diff --git a/Makefile.in b/Makefile.in index 5461633..debe2f1 100644 --- a/Makefile.in +++ b/Makefile.in @@ -163,14 +163,14 @@ NAWK = @AWK@ # Object files for the SQLite library (non-amalgamation). # -OBJS0 = alter.lo analyze.lo attach.lo auth.lo bitvec.lo btmutex.lo \ +OBJS0 = alter.lo analyze.lo attach.lo auth.lo backup.lo bitvec.lo btmutex.lo \ btree.lo build.lo callback.lo complete.lo date.lo \ delete.lo expr.lo fault.lo func.lo global.lo \ hash.lo journal.lo insert.lo legacy.lo loadext.lo \ main.lo malloc.lo mem0.lo mem1.lo mem2.lo mem3.lo mem5.lo \ memjournal.lo \ mutex.lo mutex_noop.lo mutex_os2.lo mutex_unix.lo mutex_w32.lo \ - opcodes.lo os.lo os_unix.lo os_win.lo os_os2.lo \ + notify.lo opcodes.lo os.lo os_unix.lo os_win.lo os_os2.lo \ pager.lo parse.lo pcache.lo pcache1.lo pragma.lo prepare.lo printf.lo \ random.lo resolve.lo rowset.lo select.lo status.lo \ table.lo tokenize.lo trigger.lo update.lo \ @@ -231,6 +231,7 @@ SRC = \ $(TOP)/src/mutex_os2.c \ $(TOP)/src/mutex_unix.c \ $(TOP)/src/mutex_w32.c \ + $(TOP)/src/notify.c \ $(TOP)/src/os.c \ $(TOP)/src/os.h \ $(TOP)/src/os_common.h \ @@ -518,6 +519,9 @@ attach.lo: $(TOP)/src/attach.c $(HDR) auth.lo: $(TOP)/src/auth.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/auth.c +backup.lo: $(TOP)/src/backup.c $(HDR) + $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/backup.c + bitvec.lo: $(TOP)/src/bitvec.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/bitvec.c @@ -608,6 +612,9 @@ mutex_unix.lo: $(TOP)/src/mutex_unix.c $(HDR) mutex_w32.lo: $(TOP)/src/mutex_w32.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/mutex_w32.c +notify.lo: $(TOP)/src/notify.c $(HDR) + $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/notify.c + pager.lo: $(TOP)/src/pager.c $(HDR) $(TOP)/src/pager.h $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/pager.c @@ -765,9 +772,11 @@ sqlite3_analyzer$(TEXE): $(TESTFIXTURE_SRC) $(TOP)/tool/spaceanal.tcl $(TEMP_STORE) -o $@ $(TESTFIXTURE_SRC) $(LIBTCL) -install: sqlite3$(BEXE) libsqlite3.la sqlite3.h sqlite3.pc ${HAVE_TCL:1=tcl_install} +lib_install: libsqlite3.la $(INSTALL) -d $(DESTDIR)$(libdir) $(LTINSTALL) libsqlite3.la $(DESTDIR)$(libdir) + +install: sqlite3$(BEXE) lib_install sqlite3.h sqlite3.pc ${HAVE_TCL:1=tcl_install} $(INSTALL) -d $(DESTDIR)$(bindir) $(LTINSTALL) sqlite3$(BEXE) $(DESTDIR)$(bindir) $(INSTALL) -d $(DESTDIR)$(includedir) @@ -778,7 +787,7 @@ install: sqlite3$(BEXE) libsqlite3.la sqlite3.h sqlite3.pc ${HAVE_TCL:1=tcl_inst pkgIndex.tcl: echo 'package ifneeded sqlite3 $(RELEASE) [list load $(TCLLIBDIR)/libtclsqlite3.so sqlite3]' > $@ -tcl_install: libtclsqlite3.la pkgIndex.tcl +tcl_install: lib_install libtclsqlite3.la pkgIndex.tcl $(INSTALL) -d $(DESTDIR)$(TCLLIBDIR) $(LTINSTALL) libtclsqlite3.la $(DESTDIR)$(TCLLIBDIR) rm -f $(DESTDIR)$(TCLLIBDIR)/libtclsqlite3.la $(DESTDIR)$(TCLLIBDIR)/libtclsqlite3.a diff --git a/Makefile.vxwSH4 b/Makefile.vxworks similarity index 96% rename from Makefile.vxwSH4 rename to Makefile.vxworks index 77ba884..8dde391 100644 --- a/Makefile.vxwSH4 +++ b/Makefile.vxworks @@ -13,7 +13,7 @@ include $(WIND_USR)/tool/gnu/make.$(FORCPU) #### The toplevel directory of the source tree. This is the directory # that contains this "Makefile.in" and the "configure.in" script. # -TOP = ../sqlite-3.6.5 +TOP = . #### C Compiler and options for use in building executables that # will run on the platform that is doing the build. @@ -42,13 +42,14 @@ THREADLIB = #### Specify any extra libraries needed to access required functions. # -#TLIBS = -lrt # fdatasync on Solaris 8 -#for x86 vxWorks -#TLIBS += TLIBS = $(LD_LINK_PATH_ATEND) $(LD_PARTIAL_LAST_FLAGS) -#for SH4 shared library -TLIBS_SHARED += -L$(WIND_USR)/lib/sh/SH32/commonle/PIC -#TLIBS_SHARED += $(LD_LINK_PATH_ATEND) $(LD_PARTIAL_LAST_FLAGS) -#for SH4 static +ifeq ($(CPU),SH32) + # for SH4 shared library + TLIBS_SHARED += -L$(WIND_USR)/lib/sh/SH32/commonle/PIC +else + # for all other CPUs shared library + TLIBS_SHARED += $(LD_LINK_PATH_ATEND) $(LD_PARTIAL_LAST_FLAGS) +endif +# for static library TLIBS += $(LD_LINK_PATH_ATEND) $(LD_PARTIAL_LAST_FLAGS) #### Leave SQLITE_DEBUG undefined for maximum speed. Use SQLITE_DEBUG=1 @@ -193,18 +194,18 @@ TCCX_SHARED = $(TCC_SHARED) $(OPTS) -I. -I$(TOP)/src -I$(TOP) -I$(TOP)/ext/rtree # Object files for the SQLite library. # LIBOBJ+= alter.o analyze.o attach.o auth.o \ - bitvec.o btmutex.o btree.o build.o \ + backup.o bitvec.o btmutex.o btree.o build.o \ callback.o complete.o date.o delete.o \ expr.o fault.o func.o global.o hash.o \ icu.o insert.o journal.o legacy.o loadext.o \ main.o malloc.o mem0.o mem1.o mem2.o mem3.o mem5.o memjournal.o \ mutex.o mutex_os2.o mutex_unix.o mutex_w32.o mutex_noop.o \ opcodes.o os.o os_os2.o os_unix.o os_win.o \ - pager.o parse.o pcache.o pragma.o prepare.o printf.o \ - random.o resolve.o rtree.o select.o status.o \ + pager.o parse.o pcache.o pcache1.o pragma.o prepare.o printf.o \ + random.o resolve.o rowset.o rtree.o select.o status.o \ table.o tokenize.o trigger.o \ update.o util.o vacuum.o \ - vdbe.o vdbeapi.o vdbeaux.o vdbeblob.o vdbefifo.o vdbemem.o \ + vdbe.o vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o \ walker.o where.o utf.o vtab.o @@ -215,6 +216,7 @@ SRC = \ $(TOP)/src/analyze.c \ $(TOP)/src/attach.c \ $(TOP)/src/auth.c \ + $(TOP)/src/backup.c \ $(TOP)/src/bitvec.c \ $(TOP)/src/btmutex.c \ $(TOP)/src/btree.c \ @@ -260,12 +262,14 @@ SRC = \ $(TOP)/src/pager.h \ $(TOP)/src/parse.y \ $(TOP)/src/pcache.c \ + $(TOP)/src/pcache1.c \ $(TOP)/src/pcache.h \ $(TOP)/src/pragma.c \ $(TOP)/src/prepare.c \ $(TOP)/src/printf.c \ $(TOP)/src/random.c \ $(TOP)/src/resolve.c \ + $(TOP)/src/rowset.c \ $(TOP)/src/select.c \ $(TOP)/src/status.c \ $(TOP)/src/shell.c \ @@ -286,7 +290,6 @@ SRC = \ $(TOP)/src/vdbeapi.c \ $(TOP)/src/vdbeaux.c \ $(TOP)/src/vdbeblob.c \ - $(TOP)/src/vdbefifo.c \ $(TOP)/src/vdbemem.c \ $(TOP)/src/vdbeInt.h \ $(TOP)/src/vtab.c \ @@ -316,6 +319,8 @@ SRC += \ SRC += \ $(TOP)/ext/fts3/fts3.c \ $(TOP)/ext/fts3/fts3.h \ + $(TOP)/ext/fts3/fts3_expr.c \ + $(TOP)/ext/fts3/fts3_expr.h \ $(TOP)/ext/fts3/fts3_hash.c \ $(TOP)/ext/fts3/fts3_hash.h \ $(TOP)/ext/fts3/fts3_icu.c \ diff --git a/VERSION b/VERSION index e8b6c77..3609cf7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.6.11 +3.6.13 diff --git a/configure b/configure index 0fda01e..c6e90f3 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.63 for sqlite 3.6.10. +# Generated by GNU Autoconf 2.62 for sqlite 3.6.13. # # 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.6.10' -PACKAGE_STRING='sqlite 3.6.10' +PACKAGE_VERSION='3.6.13' +PACKAGE_STRING='sqlite 3.6.13' PACKAGE_BUGREPORT='' # Factoring default headers for most tests. @@ -783,122 +783,122 @@ ac_includes_default="\ # include #endif" -ac_subst_vars='LTLIBOBJS -LIBOBJS -BUILD_CFLAGS -USE_GCOV -OPT_FEATURE_FLAGS -USE_AMALGAMATION -TARGET_DEBUG -TARGET_HAVE_READLINE -TARGET_READLINE_INC -TARGET_READLINE_LIBS -HAVE_TCL -TCL_STUB_LIB_SPEC -TCL_STUB_LIB_FLAG -TCL_STUB_LIB_FILE -TCL_LIB_SPEC -TCL_LIB_FLAG -TCL_LIB_FILE -TCL_INCLUDE_SPEC -TCL_LIBS -TCL_SRC_DIR -TCL_BIN_DIR -TCL_VERSION -TARGET_EXEEXT -SQLITE_OS_OS2 -SQLITE_OS_WIN -SQLITE_OS_UNIX -BUILD_EXEEXT -TEMP_STORE -ALLOWRELEASE -THREADSOVERRIDELOCKS -XTHREADCONNECT -SQLITE_THREADSAFE -BUILD_CC -VERSION_NUMBER -RELEASE -VERSION -program_prefix -TCLLIBDIR -TCLSH_CMD -AWK -INSTALL_DATA -INSTALL_SCRIPT -INSTALL_PROGRAM -CPP -OTOOL64 -OTOOL -LIPO -NMEDIT -DSYMUTIL -lt_ECHO -RANLIB -STRIP -AR -OBJDUMP -LN_S -NM -ac_ct_DUMPBIN -DUMPBIN -LD -FGREP -EGREP -GREP -SED -OBJEXT -EXEEXT -ac_ct_CC -CPPFLAGS -LDFLAGS -CFLAGS -CC -host_os -host_vendor -host_cpu -host -build_os -build_vendor -build_cpu -build -LIBTOOL -target_alias -host_alias -build_alias -LIBS -ECHO_T -ECHO_N -ECHO_C -DEFS -mandir -localedir -libdir -psdir -pdfdir -dvidir -htmldir -infodir -docdir -oldincludedir -includedir -localstatedir -sharedstatedir -sysconfdir -datadir -datarootdir -libexecdir -sbindir -bindir -program_transform_name -prefix -exec_prefix -PACKAGE_BUGREPORT -PACKAGE_STRING -PACKAGE_VERSION -PACKAGE_TARNAME -PACKAGE_NAME +ac_subst_vars='SHELL PATH_SEPARATOR -SHELL' +PACKAGE_NAME +PACKAGE_TARNAME +PACKAGE_VERSION +PACKAGE_STRING +PACKAGE_BUGREPORT +exec_prefix +prefix +program_transform_name +bindir +sbindir +libexecdir +datarootdir +datadir +sysconfdir +sharedstatedir +localstatedir +includedir +oldincludedir +docdir +infodir +htmldir +dvidir +pdfdir +psdir +libdir +localedir +mandir +DEFS +ECHO_C +ECHO_N +ECHO_T +LIBS +build_alias +host_alias +target_alias +LIBTOOL +build +build_cpu +build_vendor +build_os +host +host_cpu +host_vendor +host_os +CC +CFLAGS +LDFLAGS +CPPFLAGS +ac_ct_CC +EXEEXT +OBJEXT +SED +GREP +EGREP +FGREP +LD +DUMPBIN +ac_ct_DUMPBIN +NM +LN_S +OBJDUMP +AR +STRIP +RANLIB +lt_ECHO +DSYMUTIL +NMEDIT +LIPO +OTOOL +OTOOL64 +CPP +INSTALL_PROGRAM +INSTALL_SCRIPT +INSTALL_DATA +AWK +TCLSH_CMD +TCLLIBDIR +program_prefix +VERSION +RELEASE +VERSION_NUMBER +BUILD_CC +SQLITE_THREADSAFE +XTHREADCONNECT +THREADSOVERRIDELOCKS +ALLOWRELEASE +TEMP_STORE +BUILD_EXEEXT +SQLITE_OS_UNIX +SQLITE_OS_WIN +SQLITE_OS_OS2 +TARGET_EXEEXT +TCL_VERSION +TCL_BIN_DIR +TCL_SRC_DIR +TCL_LIBS +TCL_INCLUDE_SPEC +TCL_LIB_FILE +TCL_LIB_FLAG +TCL_LIB_SPEC +TCL_STUB_LIB_FILE +TCL_STUB_LIB_FLAG +TCL_STUB_LIB_SPEC +HAVE_TCL +TARGET_READLINE_LIBS +TARGET_READLINE_INC +TARGET_HAVE_READLINE +TARGET_DEBUG +USE_AMALGAMATION +OPT_FEATURE_FLAGS +USE_GCOV +BUILD_CFLAGS +LIBOBJS +LTLIBOBJS' ac_subst_files='' ac_user_opts=' enable_option_checking @@ -1357,9 +1357,9 @@ fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; - fatal) { $as_echo "$as_me: error: unrecognized options: $ac_unrecognized_opts" >&2 + fatal) { $as_echo "$as_me: error: Unrecognized options: $ac_unrecognized_opts" >&2 { (exit 1); exit 1; }; } ;; - *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + *) $as_echo "$as_me: WARNING: Unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi @@ -1412,7 +1412,7 @@ test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || - { $as_echo "$as_me: error: working directory cannot be determined" >&2 + { $as_echo "$as_me: error: Working directory cannot be determined" >&2 { (exit 1); exit 1; }; } test "X$ac_ls_di" = "X$ac_pwd_ls_di" || { $as_echo "$as_me: error: pwd does not report name of working directory" >&2 @@ -1487,7 +1487,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.6.10 to adapt to many kinds of systems. +\`configure' configures sqlite 3.6.13 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1552,7 +1552,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of sqlite 3.6.10:";; + short | recursive ) echo "Configuration of sqlite 3.6.13:";; esac cat <<\_ACEOF @@ -1670,8 +1670,8 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -sqlite configure 3.6.10 -generated by GNU Autoconf 2.63 +sqlite configure 3.6.13 +generated by GNU Autoconf 2.62 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. @@ -1684,8 +1684,8 @@ 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.6.10, which was -generated by GNU Autoconf 2.63. Invocation command line was +It was created by sqlite $as_me 3.6.13, which was +generated by GNU Autoconf 2.62. Invocation command line was $ $0 $@ @@ -1808,8 +1808,8 @@ _ASBOX case $ac_val in #( *${as_nl}*) case $ac_var in #( - *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5 -$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5 +$as_echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( @@ -2012,8 +2012,6 @@ $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi done if $ac_cache_corrupted; then - { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} { { $as_echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 @@ -2053,8 +2051,21 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu +sqlite_version_sanity_check=`cat VERSION | tr -d '\n'` +if test "$PACKAGE_VERSION" != "$sqlite_version_sanity_check" ; then +{ { $as_echo "$as_me:$LINENO: error: configure script is out of date: + configure \$PACKAGE_VERSION = $PACKAGE_VERSION + top level VERSION file = $sqlite_version_sanity_check +please regen with autoconf" >&5 +$as_echo "$as_me: error: configure script is out of date: + configure \$PACKAGE_VERSION = $PACKAGE_VERSION + top level VERSION file = $sqlite_version_sanity_check +please regen with autoconf" >&2;} + { (exit 1); exit 1; }; } +fi + # The following RCS revision string applies to configure.in -# $Revision: 1.67 $ +# $Revision: 1.71 $ ######### # Programs needed @@ -2285,8 +2296,12 @@ fi else case $cross_compiling:$ac_tool_warned in yes:) -{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +{ $as_echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +$as_echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC @@ -2485,8 +2500,12 @@ done else case $cross_compiling:$ac_tool_warned in yes:) -{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +{ $as_echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +$as_echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC @@ -2496,13 +2515,11 @@ fi fi -test -z "$CC" && { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -{ { $as_echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +test -z "$CC" && { { $as_echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH See \`config.log' for more details." >&5 $as_echo "$as_me: error: no acceptable C compiler found in \$PATH See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; }; } + { (exit 1); exit 1; }; } # Provide some information about the compiler. $as_echo "$as_me:$LINENO: checking for C compiler version" >&5 @@ -2632,13 +2649,11 @@ if test -z "$ac_file"; then $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 -{ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { { $as_echo "$as_me:$LINENO: error: C compiler cannot create executables See \`config.log' for more details." >&5 $as_echo "$as_me: error: C compiler cannot create executables See \`config.log' for more details." >&2;} - { (exit 77); exit 77; }; }; } + { (exit 77); exit 77; }; } fi ac_exeext=$ac_cv_exeext @@ -2666,15 +2681,13 @@ $as_echo "$ac_try_echo") >&5 if test "$cross_compiling" = maybe; then cross_compiling=yes else - { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -{ { $as_echo "$as_me:$LINENO: error: cannot run C compiled programs. + { { $as_echo "$as_me:$LINENO: error: cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details." >&5 $as_echo "$as_me: error: cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; }; } + { (exit 1); exit 1; }; } fi fi fi @@ -2717,13 +2730,11 @@ for ac_file in conftest.exe conftest conftest.*; do esac done else - { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -{ { $as_echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link + { { $as_echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link See \`config.log' for more details." >&5 $as_echo "$as_me: error: cannot compute suffix of executables: cannot compile and link See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; }; } + { (exit 1); exit 1; }; } fi rm -f conftest$ac_cv_exeext @@ -2777,13 +2788,11 @@ else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 -{ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { { $as_echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile See \`config.log' for more details." >&5 $as_echo "$as_me: error: cannot compute suffix of object files: cannot compile See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; }; } + { (exit 1); exit 1; }; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext @@ -3698,8 +3707,12 @@ done else case $cross_compiling:$ac_tool_warned in yes:) -{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +{ $as_echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +$as_echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} ac_tool_warned=yes ;; esac DUMPBIN=$ac_ct_DUMPBIN @@ -3725,13 +3738,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:3728: $ac_compile\"" >&5) + (eval echo "\"\$as_me:3741: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 - (eval echo "\"\$as_me:3731: $NM \\\"conftest.$ac_objext\\\"\"" >&5) + (eval echo "\"\$as_me:3744: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 - (eval echo "\"\$as_me:3734: output\"" >&5) + (eval echo "\"\$as_me:3747: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" @@ -4060,8 +4073,12 @@ fi else case $cross_compiling:$ac_tool_warned in yes:) -{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +{ $as_echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +$as_echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} ac_tool_warned=yes ;; esac OBJDUMP=$ac_ct_OBJDUMP @@ -4375,8 +4392,12 @@ fi else case $cross_compiling:$ac_tool_warned in yes:) -{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +{ $as_echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +$as_echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} ac_tool_warned=yes ;; esac AR=$ac_ct_AR @@ -4480,8 +4501,12 @@ fi else case $cross_compiling:$ac_tool_warned in yes:) -{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +{ $as_echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +$as_echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP @@ -4579,8 +4604,12 @@ fi else case $cross_compiling:$ac_tool_warned in yes:) -{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +{ $as_echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +$as_echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB @@ -4937,7 +4966,7 @@ ia64-*-hpux*) ;; *-*-irix6*) # Find out which ABI we are using. - echo '#line 4940 "configure"' > conftest.$ac_ext + echo '#line 4969 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -5213,8 +5242,12 @@ fi else case $cross_compiling:$ac_tool_warned in yes:) -{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +{ $as_echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +$as_echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} ac_tool_warned=yes ;; esac DSYMUTIL=$ac_ct_DSYMUTIL @@ -5305,8 +5338,12 @@ fi else case $cross_compiling:$ac_tool_warned in yes:) -{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +{ $as_echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +$as_echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} ac_tool_warned=yes ;; esac NMEDIT=$ac_ct_NMEDIT @@ -5397,8 +5434,12 @@ fi else case $cross_compiling:$ac_tool_warned in yes:) -{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +{ $as_echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +$as_echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} ac_tool_warned=yes ;; esac LIPO=$ac_ct_LIPO @@ -5489,8 +5530,12 @@ fi else case $cross_compiling:$ac_tool_warned in yes:) -{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +{ $as_echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +$as_echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} ac_tool_warned=yes ;; esac OTOOL=$ac_ct_OTOOL @@ -5581,8 +5626,12 @@ fi else case $cross_compiling:$ac_tool_warned in yes:) -{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +{ $as_echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +$as_echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} ac_tool_warned=yes ;; esac OTOOL64=$ac_ct_OTOOL64 @@ -5963,13 +6012,11 @@ rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else - { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -{ { $as_echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check + { { $as_echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details." >&5 $as_echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; }; } + { (exit 1); exit 1; }; } fi ac_ext=c @@ -6218,9 +6265,8 @@ ac_res=`eval 'as_val=${'$as_ac_Header'} $as_echo "$as_val"'` { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -as_val=`eval 'as_val=${'$as_ac_Header'} - $as_echo "$as_val"'` - if test "x$as_val" = x""yes; then +if test `eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` = yes; then cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF @@ -6281,9 +6327,8 @@ ac_res=`eval 'as_val=${'$as_ac_Header'} $as_echo "$as_val"'` { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -as_val=`eval 'as_val=${'$as_ac_Header'} - $as_echo "$as_val"'` - if test "x$as_val" = x""yes; then +if test `eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` = yes; then cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF @@ -6790,11 +6835,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:6793: $lt_compile\"" >&5) + (eval echo "\"\$as_me:6838: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:6797: \$? = $ac_status" >&5 + echo "$as_me:6842: \$? = $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. @@ -7129,11 +7174,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:7132: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7177: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:7136: \$? = $ac_status" >&5 + echo "$as_me:7181: \$? = $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. @@ -7234,11 +7279,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:7237: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7282: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:7241: \$? = $ac_status" >&5 + echo "$as_me:7286: \$? = $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 @@ -7289,11 +7334,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:7292: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7337: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:7296: \$? = $ac_status" >&5 + echo "$as_me:7341: \$? = $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 @@ -9594,7 +9639,7 @@ LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5 $as_echo "$ac_cv_lib_dl_dlopen" >&6; } -if test "x$ac_cv_lib_dl_dlopen" = x""yes; then +if test $ac_cv_lib_dl_dlopen = yes; then lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" else @@ -9692,7 +9737,7 @@ rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ fi { $as_echo "$as_me:$LINENO: result: $ac_cv_func_shl_load" >&5 $as_echo "$ac_cv_func_shl_load" >&6; } -if test "x$ac_cv_func_shl_load" = x""yes; then +if test $ac_cv_func_shl_load = yes; then lt_cv_dlopen="shl_load" else { $as_echo "$as_me:$LINENO: checking for shl_load in -ldld" >&5 @@ -9760,7 +9805,7 @@ LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:$LINENO: result: $ac_cv_lib_dld_shl_load" >&5 $as_echo "$ac_cv_lib_dld_shl_load" >&6; } -if test "x$ac_cv_lib_dld_shl_load" = x""yes; then +if test $ac_cv_lib_dld_shl_load = yes; then lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld" else { $as_echo "$as_me:$LINENO: checking for dlopen" >&5 @@ -9848,7 +9893,7 @@ rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ fi { $as_echo "$as_me:$LINENO: result: $ac_cv_func_dlopen" >&5 $as_echo "$ac_cv_func_dlopen" >&6; } -if test "x$ac_cv_func_dlopen" = x""yes; then +if test $ac_cv_func_dlopen = yes; then lt_cv_dlopen="dlopen" else { $as_echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5 @@ -9916,7 +9961,7 @@ LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5 $as_echo "$ac_cv_lib_dl_dlopen" >&6; } -if test "x$ac_cv_lib_dl_dlopen" = x""yes; then +if test $ac_cv_lib_dl_dlopen = yes; then lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" else { $as_echo "$as_me:$LINENO: checking for dlopen in -lsvld" >&5 @@ -9984,7 +10029,7 @@ LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:$LINENO: result: $ac_cv_lib_svld_dlopen" >&5 $as_echo "$ac_cv_lib_svld_dlopen" >&6; } -if test "x$ac_cv_lib_svld_dlopen" = x""yes; then +if test $ac_cv_lib_svld_dlopen = yes; then lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld" else { $as_echo "$as_me:$LINENO: checking for dld_link in -ldld" >&5 @@ -10052,7 +10097,7 @@ LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:$LINENO: result: $ac_cv_lib_dld_dld_link" >&5 $as_echo "$ac_cv_lib_dld_dld_link" >&6; } -if test "x$ac_cv_lib_dld_dld_link" = x""yes; then +if test $ac_cv_lib_dld_dld_link = yes; then lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld" fi @@ -10102,7 +10147,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 10105 "configure" +#line 10150 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -10198,7 +10243,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 10201 "configure" +#line 10246 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -11017,7 +11062,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:$LINENO: result: $ac_cv_type_int8_t" >&5 $as_echo "$ac_cv_type_int8_t" >&6; } -if test "x$ac_cv_type_int8_t" = x""yes; then +if test $ac_cv_type_int8_t = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_INT8_T 1 @@ -11119,7 +11164,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:$LINENO: result: $ac_cv_type_int16_t" >&5 $as_echo "$ac_cv_type_int16_t" >&6; } -if test "x$ac_cv_type_int16_t" = x""yes; then +if test $ac_cv_type_int16_t = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_INT16_T 1 @@ -11221,7 +11266,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:$LINENO: result: $ac_cv_type_int32_t" >&5 $as_echo "$ac_cv_type_int32_t" >&6; } -if test "x$ac_cv_type_int32_t" = x""yes; then +if test $ac_cv_type_int32_t = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_INT32_T 1 @@ -11323,7 +11368,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:$LINENO: result: $ac_cv_type_int64_t" >&5 $as_echo "$ac_cv_type_int64_t" >&6; } -if test "x$ac_cv_type_int64_t" = x""yes; then +if test $ac_cv_type_int64_t = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_INT64_T 1 @@ -11425,7 +11470,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:$LINENO: result: $ac_cv_type_intptr_t" >&5 $as_echo "$ac_cv_type_intptr_t" >&6; } -if test "x$ac_cv_type_intptr_t" = x""yes; then +if test $ac_cv_type_intptr_t = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_INTPTR_T 1 @@ -11527,7 +11572,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:$LINENO: result: $ac_cv_type_uint8_t" >&5 $as_echo "$ac_cv_type_uint8_t" >&6; } -if test "x$ac_cv_type_uint8_t" = x""yes; then +if test $ac_cv_type_uint8_t = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_UINT8_T 1 @@ -11629,7 +11674,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:$LINENO: result: $ac_cv_type_uint16_t" >&5 $as_echo "$ac_cv_type_uint16_t" >&6; } -if test "x$ac_cv_type_uint16_t" = x""yes; then +if test $ac_cv_type_uint16_t = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_UINT16_T 1 @@ -11731,7 +11776,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:$LINENO: result: $ac_cv_type_uint32_t" >&5 $as_echo "$ac_cv_type_uint32_t" >&6; } -if test "x$ac_cv_type_uint32_t" = x""yes; then +if test $ac_cv_type_uint32_t = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_UINT32_T 1 @@ -11833,7 +11878,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:$LINENO: result: $ac_cv_type_uint64_t" >&5 $as_echo "$ac_cv_type_uint64_t" >&6; } -if test "x$ac_cv_type_uint64_t" = x""yes; then +if test $ac_cv_type_uint64_t = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_UINT64_T 1 @@ -11935,7 +11980,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:$LINENO: result: $ac_cv_type_uintptr_t" >&5 $as_echo "$ac_cv_type_uintptr_t" >&6; } -if test "x$ac_cv_type_uintptr_t" = x""yes; then +if test $ac_cv_type_uintptr_t = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_UINTPTR_T 1 @@ -12085,9 +12130,8 @@ ac_res=`eval 'as_val=${'$as_ac_Header'} $as_echo "$ac_res" >&6; } fi -as_val=`eval 'as_val=${'$as_ac_Header'} - $as_echo "$as_val"'` - if test "x$as_val" = x""yes; then +if test `eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` = yes; then cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF @@ -12195,9 +12239,8 @@ ac_res=`eval 'as_val=${'$as_ac_var'} $as_echo "$as_val"'` { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -as_val=`eval 'as_val=${'$as_ac_var'} - $as_echo "$as_val"'` - if test "x$as_val" = x""yes; then +if test `eval 'as_val=${'$as_ac_var'} + $as_echo "$as_val"'` = yes; then cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF @@ -13084,7 +13127,7 @@ LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:$LINENO: result: $ac_cv_lib_readline_readline" >&5 $as_echo "$ac_cv_lib_readline_readline" >&6; } -if test "x$ac_cv_lib_readline_readline" = x""yes; then +if test $ac_cv_lib_readline_readline = yes; then TARGET_READLINE_LIBS="-lreadline" else found="no" @@ -13232,7 +13275,7 @@ fi $as_echo "$ac_cv_header_readline_h" >&6; } fi -if test "x$ac_cv_header_readline_h" = x""yes; then +if test $ac_cv_header_readline_h = yes; then found="yes" else @@ -13260,9 +13303,8 @@ ac_res=`eval 'as_val=${'$as_ac_File'} $as_echo "$as_val"'` { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -as_val=`eval 'as_val=${'$as_ac_File'} - $as_echo "$as_val"'` - if test "x$as_val" = x""yes; then +if test `eval 'as_val=${'$as_ac_File'} + $as_echo "$as_val"'` = yes; then found=yes fi @@ -13538,8 +13580,8 @@ _ACEOF case $ac_val in #( *${as_nl}*) case $ac_var in #( - *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5 -$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5 +$as_echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( @@ -13930,8 +13972,8 @@ 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.6.10, which was -generated by GNU Autoconf 2.63. Invocation command line was +This file was extended by sqlite $as_me 3.6.13, which was +generated by GNU Autoconf 2.62. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS @@ -13944,15 +13986,6 @@ on `(hostname || uname -n) 2>/dev/null | sed 1q` _ACEOF -case $ac_config_files in *" -"*) set x $ac_config_files; shift; ac_config_files=$*;; -esac - -case $ac_config_headers in *" -"*) set x $ac_config_headers; shift; ac_config_headers=$*;; -esac - - cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" @@ -13966,17 +13999,16 @@ ac_cs_usage="\ \`$as_me' instantiates files from templates according to the current configuration. -Usage: $0 [OPTION]... [FILE]... +Usage: $0 [OPTIONS] [FILE]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit - -q, --quiet, --silent - do not print progress messages + -q, --quiet do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions - --file=FILE[:TEMPLATE] + --file=FILE[:TEMPLATE] instantiate the configuration file FILE - --header=FILE[:TEMPLATE] + --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: @@ -13993,8 +14025,8 @@ Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_version="\\ -sqlite config.status 3.6.10 -configured by $0, generated by GNU Autoconf 2.63, +sqlite config.status 3.6.13 +configured by $0, generated by GNU Autoconf 2.62, with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" Copyright (C) 2008 Free Software Foundation, Inc. @@ -14454,8 +14486,7 @@ for ac_last_try in false false false false false :; do $as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} { (exit 1); exit 1; }; } - ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` - if test $ac_delim_n = $ac_delim_num; then + if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` = $ac_delim_num; then break elif $ac_last_try; then { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 @@ -14660,9 +14691,9 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 } split(mac1, mac2, "(") #) macro = mac2[1] - prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". + prefix = substr(line, 1, index(line, defundef) - 1) print prefix "define", macro P[macro] D[macro] next } else { @@ -14670,7 +14701,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { - print "/*", prefix defundef, macro, "*/" + print "/*", line, "*/" next } } @@ -14694,8 +14725,8 @@ do esac case $ac_mode$ac_tag in :[FHL]*:*);; - :L* | :C*:*) { { $as_echo "$as_me:$LINENO: error: invalid tag $ac_tag" >&5 -$as_echo "$as_me: error: invalid tag $ac_tag" >&2;} + :L* | :C*:*) { { $as_echo "$as_me:$LINENO: error: Invalid tag $ac_tag." >&5 +$as_echo "$as_me: error: Invalid tag $ac_tag." >&2;} { (exit 1); exit 1; }; };; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; @@ -15659,7 +15690,7 @@ if test "$no_create" != yes; then $ac_cs_success || { (exit 1); exit 1; } fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then - { $as_echo "$as_me:$LINENO: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 -$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: Unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: Unrecognized options: $ac_unrecognized_opts" >&2;} fi diff --git a/configure.ac b/configure.ac index 3a437c9..3a227f6 100644 --- a/configure.ac +++ b/configure.ac @@ -89,10 +89,19 @@ # AC_INIT(sqlite, m4_esyscmd([cat VERSION | tr -d '\n'])) +dnl Make sure the local VERSION file matches this configure script +sqlite_version_sanity_check=`cat VERSION | tr -d '\n'` +if test "$PACKAGE_VERSION" != "$sqlite_version_sanity_check" ; then +AC_MSG_ERROR([configure script is out of date: + configure \$PACKAGE_VERSION = $PACKAGE_VERSION + top level VERSION file = $sqlite_version_sanity_check +please regen with autoconf]) +fi + dnl Put the RCS revision string after AC_INIT so that it will also dnl show in in configure. # The following RCS revision string applies to configure.in -# $Revision: 1.54 $ +# $Revision: 1.55 $ ######### # Programs needed diff --git a/ext/fts1/fts1.c b/ext/fts1/fts1.c index 898e94f..d5429ff 100644 --- a/ext/fts1/fts1.c +++ b/ext/fts1/fts1.c @@ -1214,7 +1214,7 @@ static int sql_step_statement(fulltext_vtab *v, fulltext_statement iStmt, if( rc==SQLITE_BUSY ) continue; if( rc!=SQLITE_ERROR ) return rc; - /* If an SQLITE_SCHEMA error has occured, then finalizing this + /* If an SQLITE_SCHEMA error has occurred, then finalizing this * statement is going to delete the fulltext_vtab structure. If * the statement just executed is in the pFulltextStatements[] * array, it will be finalized twice. So remove it before diff --git a/ext/fts2/fts2.c b/ext/fts2/fts2.c index 226b213..bc776aa 100644 --- a/ext/fts2/fts2.c +++ b/ext/fts2/fts2.c @@ -6249,7 +6249,7 @@ static void optimizeFunc(sqlite3_context *pContext, i++; } - /* If we managed to succesfully read them all, optimize them. */ + /* If we managed to successfully read them all, optimize them. */ if( rc==SQLITE_DONE ){ assert( i==nReaders ); rc = optimizeInternal(v, readers, nReaders, &writer); @@ -6836,7 +6836,7 @@ int sqlite3Fts2Init(sqlite3 *db){ ); } - /* An error has occured. Delete the hash table and return the error code. */ + /* An error has occurred. Delete the hash table and return the error code. */ assert( rc!=SQLITE_OK ); if( pHash ){ sqlite3Fts2HashClear(pHash); diff --git a/ext/fts3/.#fts3.c.1.21 b/ext/fts3/.#fts3.c.1.21 deleted file mode 100644 index 260b17c..0000000 --- a/ext/fts3/.#fts3.c.1.21 +++ /dev/null @@ -1,7024 +0,0 @@ -/* -** 2006 Oct 10 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This is an SQLite module implementing full-text search. -*/ - -/* -** The code in this file is only compiled if: -** -** * The FTS3 module is being built as an extension -** (in which case SQLITE_CORE is not defined), or -** -** * The FTS3 module is being built into the core of -** SQLite (in which case SQLITE_ENABLE_FTS3 is defined). -*/ - -/* TODO(shess) Consider exporting this comment to an HTML file or the -** wiki. -*/ -/* The full-text index is stored in a series of b+tree (-like) -** structures called segments which map terms to doclists. The -** structures are like b+trees in layout, but are constructed from the -** bottom up in optimal fashion and are not updatable. Since trees -** are built from the bottom up, things will be described from the -** bottom up. -** -** -**** Varints **** -** The basic unit of encoding is a variable-length integer called a -** varint. We encode variable-length integers in little-endian order -** using seven bits * per byte as follows: -** -** KEY: -** A = 0xxxxxxx 7 bits of data and one flag bit -** B = 1xxxxxxx 7 bits of data and one flag bit -** -** 7 bits - A -** 14 bits - BA -** 21 bits - BBA -** and so on. -** -** This is identical to how sqlite encodes varints (see util.c). -** -** -**** Document lists **** -** A doclist (document list) holds a docid-sorted list of hits for a -** given term. Doclists hold docids, and can optionally associate -** token positions and offsets with docids. -** -** A DL_POSITIONS_OFFSETS doclist is stored like this: -** -** array { -** varint docid; -** array { (position list for column 0) -** varint position; (delta from previous position plus POS_BASE) -** varint startOffset; (delta from previous startOffset) -** varint endOffset; (delta from startOffset) -** } -** array { -** varint POS_COLUMN; (marks start of position list for new column) -** varint column; (index of new column) -** array { -** varint position; (delta from previous position plus POS_BASE) -** varint startOffset;(delta from previous startOffset) -** varint endOffset; (delta from startOffset) -** } -** } -** varint POS_END; (marks end of positions for this document. -** } -** -** Here, array { X } means zero or more occurrences of X, adjacent in -** memory. A "position" is an index of a token in the token stream -** generated by the tokenizer, while an "offset" is a byte offset, -** both based at 0. Note that POS_END and POS_COLUMN occur in the -** same logical place as the position element, and act as sentinals -** ending a position list array. -** -** A DL_POSITIONS doclist omits the startOffset and endOffset -** information. A DL_DOCIDS doclist omits both the position and -** offset information, becoming an array of varint-encoded docids. -** -** On-disk data is stored as type DL_DEFAULT, so we don't serialize -** the type. Due to how deletion is implemented in the segmentation -** system, on-disk doclists MUST store at least positions. -** -** -**** Segment leaf nodes **** -** Segment leaf nodes store terms and doclists, ordered by term. Leaf -** nodes are written using LeafWriter, and read using LeafReader (to -** iterate through a single leaf node's data) and LeavesReader (to -** iterate through a segment's entire leaf layer). Leaf nodes have -** the format: -** -** varint iHeight; (height from leaf level, always 0) -** varint nTerm; (length of first term) -** char pTerm[nTerm]; (content of first term) -** varint nDoclist; (length of term's associated doclist) -** char pDoclist[nDoclist]; (content of doclist) -** array { -** (further terms are delta-encoded) -** varint nPrefix; (length of prefix shared with previous term) -** varint nSuffix; (length of unshared suffix) -** char pTermSuffix[nSuffix];(unshared suffix of next term) -** varint nDoclist; (length of term's associated doclist) -** char pDoclist[nDoclist]; (content of doclist) -** } -** -** Here, array { X } means zero or more occurrences of X, adjacent in -** memory. -** -** Leaf nodes are broken into blocks which are stored contiguously in -** the %_segments table in sorted order. This means that when the end -** of a node is reached, the next term is in the node with the next -** greater node id. -** -** New data is spilled to a new leaf node when the current node -** exceeds LEAF_MAX bytes (default 2048). New data which itself is -** larger than STANDALONE_MIN (default 1024) is placed in a standalone -** node (a leaf node with a single term and doclist). The goal of -** these settings is to pack together groups of small doclists while -** making it efficient to directly access large doclists. The -** assumption is that large doclists represent terms which are more -** likely to be query targets. -** -** TODO(shess) It may be useful for blocking decisions to be more -** dynamic. For instance, it may make more sense to have a 2.5k leaf -** node rather than splitting into 2k and .5k nodes. My intuition is -** that this might extend through 2x or 4x the pagesize. -** -** -**** Segment interior nodes **** -** Segment interior nodes store blockids for subtree nodes and terms -** to describe what data is stored by the each subtree. Interior -** nodes are written using InteriorWriter, and read using -** InteriorReader. InteriorWriters are created as needed when -** SegmentWriter creates new leaf nodes, or when an interior node -** itself grows too big and must be split. The format of interior -** nodes: -** -** varint iHeight; (height from leaf level, always >0) -** varint iBlockid; (block id of node's leftmost subtree) -** optional { -** varint nTerm; (length of first term) -** char pTerm[nTerm]; (content of first term) -** array { -** (further terms are delta-encoded) -** varint nPrefix; (length of shared prefix with previous term) -** varint nSuffix; (length of unshared suffix) -** char pTermSuffix[nSuffix]; (unshared suffix of next term) -** } -** } -** -** Here, optional { X } means an optional element, while array { X } -** means zero or more occurrences of X, adjacent in memory. -** -** An interior node encodes n terms separating n+1 subtrees. The -** subtree blocks are contiguous, so only the first subtree's blockid -** is encoded. The subtree at iBlockid will contain all terms less -** than the first term encoded (or all terms if no term is encoded). -** Otherwise, for terms greater than or equal to pTerm[i] but less -** than pTerm[i+1], the subtree for that term will be rooted at -** iBlockid+i. Interior nodes only store enough term data to -** distinguish adjacent children (if the rightmost term of the left -** child is "something", and the leftmost term of the right child is -** "wicked", only "w" is stored). -** -** New data is spilled to a new interior node at the same height when -** the current node exceeds INTERIOR_MAX bytes (default 2048). -** INTERIOR_MIN_TERMS (default 7) keeps large terms from monopolizing -** interior nodes and making the tree too skinny. The interior nodes -** at a given height are naturally tracked by interior nodes at -** height+1, and so on. -** -** -**** Segment directory **** -** The segment directory in table %_segdir stores meta-information for -** merging and deleting segments, and also the root node of the -** segment's tree. -** -** The root node is the top node of the segment's tree after encoding -** the entire segment, restricted to ROOT_MAX bytes (default 1024). -** This could be either a leaf node or an interior node. If the top -** node requires more than ROOT_MAX bytes, it is flushed to %_segments -** and a new root interior node is generated (which should always fit -** within ROOT_MAX because it only needs space for 2 varints, the -** height and the blockid of the previous root). -** -** The meta-information in the segment directory is: -** level - segment level (see below) -** idx - index within level -** - (level,idx uniquely identify a segment) -** start_block - first leaf node -** leaves_end_block - last leaf node -** end_block - last block (including interior nodes) -** root - contents of root node -** -** If the root node is a leaf node, then start_block, -** leaves_end_block, and end_block are all 0. -** -** -**** Segment merging **** -** To amortize update costs, segments are grouped into levels and -** merged in batches. Each increase in level represents exponentially -** more documents. -** -** New documents (actually, document updates) are tokenized and -** written individually (using LeafWriter) to a level 0 segment, with -** incrementing idx. When idx reaches MERGE_COUNT (default 16), all -** level 0 segments are merged into a single level 1 segment. Level 1 -** is populated like level 0, and eventually MERGE_COUNT level 1 -** segments are merged to a single level 2 segment (representing -** MERGE_COUNT^2 updates), and so on. -** -** A segment merge traverses all segments at a given level in -** parallel, performing a straightforward sorted merge. Since segment -** leaf nodes are written in to the %_segments table in order, this -** merge traverses the underlying sqlite disk structures efficiently. -** After the merge, all segment blocks from the merged level are -** deleted. -** -** MERGE_COUNT controls how often we merge segments. 16 seems to be -** somewhat of a sweet spot for insertion performance. 32 and 64 show -** very similar performance numbers to 16 on insertion, though they're -** a tiny bit slower (perhaps due to more overhead in merge-time -** sorting). 8 is about 20% slower than 16, 4 about 50% slower than -** 16, 2 about 66% slower than 16. -** -** At query time, high MERGE_COUNT increases the number of segments -** which need to be scanned and merged. For instance, with 100k docs -** inserted: -** -** MERGE_COUNT segments -** 16 25 -** 8 12 -** 4 10 -** 2 6 -** -** This appears to have only a moderate impact on queries for very -** frequent terms (which are somewhat dominated by segment merge -** costs), and infrequent and non-existent terms still seem to be fast -** even with many segments. -** -** TODO(shess) That said, it would be nice to have a better query-side -** argument for MERGE_COUNT of 16. Also, it is possible/likely that -** optimizations to things like doclist merging will swing the sweet -** spot around. -** -** -** -**** Handling of deletions and updates **** -** Since we're using a segmented structure, with no docid-oriented -** index into the term index, we clearly cannot simply update the term -** index when a document is deleted or updated. For deletions, we -** write an empty doclist (varint(docid) varint(POS_END)), for updates -** we simply write the new doclist. Segment merges overwrite older -** data for a particular docid with newer data, so deletes or updates -** will eventually overtake the earlier data and knock it out. The -** query logic likewise merges doclists so that newer data knocks out -** older data. -** -** TODO(shess) Provide a VACUUM type operation to clear out all -** deletions and duplications. This would basically be a forced merge -** into a single segment. -*/ - -#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) - -#if defined(SQLITE_ENABLE_FTS3) && !defined(SQLITE_CORE) -# define SQLITE_CORE 1 -#endif - -#include -#include -#include -#include -#include - -#include "fts3.h" -#include "fts3_expr.h" -#include "fts3_hash.h" -#include "fts3_tokenizer.h" -#ifndef SQLITE_CORE -# include "sqlite3ext.h" - SQLITE_EXTENSION_INIT1 -#endif - - -/* TODO(shess) MAN, this thing needs some refactoring. At minimum, it -** would be nice to order the file better, perhaps something along the -** lines of: -** -** - utility functions -** - table setup functions -** - table update functions -** - table query functions -** -** Put the query functions last because they're likely to reference -** typedefs or functions from the table update section. -*/ - -#if 0 -# define FTSTRACE(A) printf A; fflush(stdout) -#else -# define FTSTRACE(A) -#endif - -/* It is not safe to call isspace(), tolower(), or isalnum() on -** hi-bit-set characters. This is the same solution used in the -** tokenizer. -*/ -/* TODO(shess) The snippet-generation code should be using the -** tokenizer-generated tokens rather than doing its own local -** tokenization. -*/ -/* TODO(shess) Is __isascii() a portable version of (c&0x80)==0? */ -static int safe_isspace(char c){ - return (c&0x80)==0 ? isspace(c) : 0; -} -static int safe_tolower(char c){ - return (c&0x80)==0 ? tolower(c) : c; -} -static int safe_isalnum(char c){ - return (c&0x80)==0 ? isalnum(c) : 0; -} - -typedef enum DocListType { - DL_DOCIDS, /* docids only */ - DL_POSITIONS, /* docids + positions */ - DL_POSITIONS_OFFSETS /* docids + positions + offsets */ -} DocListType; - -/* -** By default, only positions and not offsets are stored in the doclists. -** To change this so that offsets are stored too, compile with -** -** -DDL_DEFAULT=DL_POSITIONS_OFFSETS -** -** If DL_DEFAULT is set to DL_DOCIDS, your table can only be inserted -** into (no deletes or updates). -*/ -#ifndef DL_DEFAULT -# define DL_DEFAULT DL_POSITIONS -#endif - -enum { - POS_END = 0, /* end of this position list */ - POS_COLUMN, /* followed by new column number */ - POS_BASE -}; - -/* MERGE_COUNT controls how often we merge segments (see comment at -** top of file). -*/ -#define MERGE_COUNT 16 - -/* utility functions */ - -/* CLEAR() and SCRAMBLE() abstract memset() on a pointer to a single -** record to prevent errors of the form: -** -** my_function(SomeType *b){ -** memset(b, '\0', sizeof(b)); // sizeof(b)!=sizeof(*b) -** } -*/ -/* TODO(shess) Obvious candidates for a header file. */ -#define CLEAR(b) memset(b, '\0', sizeof(*(b))) - -#ifndef NDEBUG -# define SCRAMBLE(b) memset(b, 0x55, sizeof(*(b))) -#else -# define SCRAMBLE(b) -#endif - -/* We may need up to VARINT_MAX bytes to store an encoded 64-bit integer. */ -#define VARINT_MAX 10 - -/* Write a 64-bit variable-length integer to memory starting at p[0]. - * The length of data written will be between 1 and VARINT_MAX bytes. - * The number of bytes written is returned. */ -static int fts3PutVarint(char *p, sqlite_int64 v){ - unsigned char *q = (unsigned char *) p; - sqlite_uint64 vu = v; - do{ - *q++ = (unsigned char) ((vu & 0x7f) | 0x80); - vu >>= 7; - }while( vu!=0 ); - q[-1] &= 0x7f; /* turn off high bit in final byte */ - assert( q - (unsigned char *)p <= VARINT_MAX ); - return (int) (q - (unsigned char *)p); -} - -/* Read a 64-bit variable-length integer from memory starting at p[0]. - * Return the number of bytes read, or 0 on error. - * The value is stored in *v. */ -static int fts3GetVarint(const char *p, sqlite_int64 *v){ - const unsigned char *q = (const unsigned char *) p; - sqlite_uint64 x = 0, y = 1; - while( (*q & 0x80) == 0x80 ){ - x += y * (*q++ & 0x7f); - y <<= 7; - if( q - (unsigned char *)p >= VARINT_MAX ){ /* bad data */ - assert( 0 ); - return 0; - } - } - x += y * (*q++); - *v = (sqlite_int64) x; - return (int) (q - (unsigned char *)p); -} - -static int fts3GetVarint32(const char *p, int *pi){ - sqlite_int64 i; - int ret = fts3GetVarint(p, &i); - *pi = (int) i; - assert( *pi==i ); - return ret; -} - -/*******************************************************************/ -/* DataBuffer is used to collect data into a buffer in piecemeal -** fashion. It implements the usual distinction between amount of -** data currently stored (nData) and buffer capacity (nCapacity). -** -** dataBufferInit - create a buffer with given initial capacity. -** dataBufferReset - forget buffer's data, retaining capacity. -** dataBufferDestroy - free buffer's data. -** dataBufferSwap - swap contents of two buffers. -** dataBufferExpand - expand capacity without adding data. -** dataBufferAppend - append data. -** dataBufferAppend2 - append two pieces of data at once. -** dataBufferReplace - replace buffer's data. -*/ -typedef struct DataBuffer { - char *pData; /* Pointer to malloc'ed buffer. */ - int nCapacity; /* Size of pData buffer. */ - int nData; /* End of data loaded into pData. */ -} DataBuffer; - -static void dataBufferInit(DataBuffer *pBuffer, int nCapacity){ - assert( nCapacity>=0 ); - pBuffer->nData = 0; - pBuffer->nCapacity = nCapacity; - pBuffer->pData = nCapacity==0 ? NULL : sqlite3_malloc(nCapacity); -} -static void dataBufferReset(DataBuffer *pBuffer){ - pBuffer->nData = 0; -} -static void dataBufferDestroy(DataBuffer *pBuffer){ - if( pBuffer->pData!=NULL ) sqlite3_free(pBuffer->pData); - SCRAMBLE(pBuffer); -} -static void dataBufferSwap(DataBuffer *pBuffer1, DataBuffer *pBuffer2){ - DataBuffer tmp = *pBuffer1; - *pBuffer1 = *pBuffer2; - *pBuffer2 = tmp; -} -static void dataBufferExpand(DataBuffer *pBuffer, int nAddCapacity){ - assert( nAddCapacity>0 ); - /* TODO(shess) Consider expanding more aggressively. Note that the - ** underlying malloc implementation may take care of such things for - ** us already. - */ - if( pBuffer->nData+nAddCapacity>pBuffer->nCapacity ){ - pBuffer->nCapacity = pBuffer->nData+nAddCapacity; - pBuffer->pData = sqlite3_realloc(pBuffer->pData, pBuffer->nCapacity); - } -} -static void dataBufferAppend(DataBuffer *pBuffer, - const char *pSource, int nSource){ - assert( nSource>0 && pSource!=NULL ); - dataBufferExpand(pBuffer, nSource); - memcpy(pBuffer->pData+pBuffer->nData, pSource, nSource); - pBuffer->nData += nSource; -} -static void dataBufferAppend2(DataBuffer *pBuffer, - const char *pSource1, int nSource1, - const char *pSource2, int nSource2){ - assert( nSource1>0 && pSource1!=NULL ); - assert( nSource2>0 && pSource2!=NULL ); - dataBufferExpand(pBuffer, nSource1+nSource2); - memcpy(pBuffer->pData+pBuffer->nData, pSource1, nSource1); - memcpy(pBuffer->pData+pBuffer->nData+nSource1, pSource2, nSource2); - pBuffer->nData += nSource1+nSource2; -} -static void dataBufferReplace(DataBuffer *pBuffer, - const char *pSource, int nSource){ - dataBufferReset(pBuffer); - dataBufferAppend(pBuffer, pSource, nSource); -} - -/* StringBuffer is a null-terminated version of DataBuffer. */ -typedef struct StringBuffer { - DataBuffer b; /* Includes null terminator. */ -} StringBuffer; - -static void initStringBuffer(StringBuffer *sb){ - dataBufferInit(&sb->b, 100); - dataBufferReplace(&sb->b, "", 1); -} -static int stringBufferLength(StringBuffer *sb){ - return sb->b.nData-1; -} -static char *stringBufferData(StringBuffer *sb){ - return sb->b.pData; -} -static void stringBufferDestroy(StringBuffer *sb){ - dataBufferDestroy(&sb->b); -} - -static void nappend(StringBuffer *sb, const char *zFrom, int nFrom){ - assert( sb->b.nData>0 ); - if( nFrom>0 ){ - sb->b.nData--; - dataBufferAppend2(&sb->b, zFrom, nFrom, "", 1); - } -} -static void append(StringBuffer *sb, const char *zFrom){ - nappend(sb, zFrom, strlen(zFrom)); -} - -/* Append a list of strings separated by commas. */ -static void appendList(StringBuffer *sb, int nString, char **azString){ - int i; - for(i=0; i0 ) append(sb, ", "); - append(sb, azString[i]); - } -} - -static int endsInWhiteSpace(StringBuffer *p){ - return stringBufferLength(p)>0 && - safe_isspace(stringBufferData(p)[stringBufferLength(p)-1]); -} - -/* If the StringBuffer ends in something other than white space, add a -** single space character to the end. -*/ -static void appendWhiteSpace(StringBuffer *p){ - if( stringBufferLength(p)==0 ) return; - if( !endsInWhiteSpace(p) ) append(p, " "); -} - -/* Remove white space from the end of the StringBuffer */ -static void trimWhiteSpace(StringBuffer *p){ - while( endsInWhiteSpace(p) ){ - p->b.pData[--p->b.nData-1] = '\0'; - } -} - -/*******************************************************************/ -/* DLReader is used to read document elements from a doclist. The -** current docid is cached, so dlrDocid() is fast. DLReader does not -** own the doclist buffer. -** -** dlrAtEnd - true if there's no more data to read. -** dlrDocid - docid of current document. -** dlrDocData - doclist data for current document (including docid). -** dlrDocDataBytes - length of same. -** dlrAllDataBytes - length of all remaining data. -** dlrPosData - position data for current document. -** dlrPosDataLen - length of pos data for current document (incl POS_END). -** dlrStep - step to current document. -** dlrInit - initial for doclist of given type against given data. -** dlrDestroy - clean up. -** -** Expected usage is something like: -** -** DLReader reader; -** dlrInit(&reader, pData, nData); -** while( !dlrAtEnd(&reader) ){ -** // calls to dlrDocid() and kin. -** dlrStep(&reader); -** } -** dlrDestroy(&reader); -*/ -typedef struct DLReader { - DocListType iType; - const char *pData; - int nData; - - sqlite_int64 iDocid; - int nElement; -} DLReader; - -static int dlrAtEnd(DLReader *pReader){ - assert( pReader->nData>=0 ); - return pReader->nData==0; -} -static sqlite_int64 dlrDocid(DLReader *pReader){ - assert( !dlrAtEnd(pReader) ); - return pReader->iDocid; -} -static const char *dlrDocData(DLReader *pReader){ - assert( !dlrAtEnd(pReader) ); - return pReader->pData; -} -static int dlrDocDataBytes(DLReader *pReader){ - assert( !dlrAtEnd(pReader) ); - return pReader->nElement; -} -static int dlrAllDataBytes(DLReader *pReader){ - assert( !dlrAtEnd(pReader) ); - return pReader->nData; -} -/* TODO(shess) Consider adding a field to track iDocid varint length -** to make these two functions faster. This might matter (a tiny bit) -** for queries. -*/ -static const char *dlrPosData(DLReader *pReader){ - sqlite_int64 iDummy; - int n = fts3GetVarint(pReader->pData, &iDummy); - assert( !dlrAtEnd(pReader) ); - return pReader->pData+n; -} -static int dlrPosDataLen(DLReader *pReader){ - sqlite_int64 iDummy; - int n = fts3GetVarint(pReader->pData, &iDummy); - assert( !dlrAtEnd(pReader) ); - return pReader->nElement-n; -} -static void dlrStep(DLReader *pReader){ - assert( !dlrAtEnd(pReader) ); - - /* Skip past current doclist element. */ - assert( pReader->nElement<=pReader->nData ); - pReader->pData += pReader->nElement; - pReader->nData -= pReader->nElement; - - /* If there is more data, read the next doclist element. */ - if( pReader->nData!=0 ){ - sqlite_int64 iDocidDelta; - int iDummy, n = fts3GetVarint(pReader->pData, &iDocidDelta); - pReader->iDocid += iDocidDelta; - if( pReader->iType>=DL_POSITIONS ){ - assert( nnData ); - while( 1 ){ - n += fts3GetVarint32(pReader->pData+n, &iDummy); - assert( n<=pReader->nData ); - if( iDummy==POS_END ) break; - if( iDummy==POS_COLUMN ){ - n += fts3GetVarint32(pReader->pData+n, &iDummy); - assert( nnData ); - }else if( pReader->iType==DL_POSITIONS_OFFSETS ){ - n += fts3GetVarint32(pReader->pData+n, &iDummy); - n += fts3GetVarint32(pReader->pData+n, &iDummy); - assert( nnData ); - } - } - } - pReader->nElement = n; - assert( pReader->nElement<=pReader->nData ); - } -} -static void dlrInit(DLReader *pReader, DocListType iType, - const char *pData, int nData){ - assert( pData!=NULL && nData!=0 ); - pReader->iType = iType; - pReader->pData = pData; - pReader->nData = nData; - pReader->nElement = 0; - pReader->iDocid = 0; - - /* Load the first element's data. There must be a first element. */ - dlrStep(pReader); -} -static void dlrDestroy(DLReader *pReader){ - SCRAMBLE(pReader); -} - -#ifndef NDEBUG -/* Verify that the doclist can be validly decoded. Also returns the -** last docid found because it is convenient in other assertions for -** DLWriter. -*/ -static void docListValidate(DocListType iType, const char *pData, int nData, - sqlite_int64 *pLastDocid){ - sqlite_int64 iPrevDocid = 0; - assert( nData>0 ); - assert( pData!=0 ); - assert( pData+nData>pData ); - while( nData!=0 ){ - sqlite_int64 iDocidDelta; - int n = fts3GetVarint(pData, &iDocidDelta); - iPrevDocid += iDocidDelta; - if( iType>DL_DOCIDS ){ - int iDummy; - while( 1 ){ - n += fts3GetVarint32(pData+n, &iDummy); - if( iDummy==POS_END ) break; - if( iDummy==POS_COLUMN ){ - n += fts3GetVarint32(pData+n, &iDummy); - }else if( iType>DL_POSITIONS ){ - n += fts3GetVarint32(pData+n, &iDummy); - n += fts3GetVarint32(pData+n, &iDummy); - } - assert( n<=nData ); - } - } - assert( n<=nData ); - pData += n; - nData -= n; - } - if( pLastDocid ) *pLastDocid = iPrevDocid; -} -#define ASSERT_VALID_DOCLIST(i, p, n, o) docListValidate(i, p, n, o) -#else -#define ASSERT_VALID_DOCLIST(i, p, n, o) assert( 1 ) -#endif - -/*******************************************************************/ -/* DLWriter is used to write doclist data to a DataBuffer. DLWriter -** always appends to the buffer and does not own it. -** -** dlwInit - initialize to write a given type doclistto a buffer. -** dlwDestroy - clear the writer's memory. Does not free buffer. -** dlwAppend - append raw doclist data to buffer. -** dlwCopy - copy next doclist from reader to writer. -** dlwAdd - construct doclist element and append to buffer. -** Only apply dlwAdd() to DL_DOCIDS doclists (else use PLWriter). -*/ -typedef struct DLWriter { - DocListType iType; - DataBuffer *b; - sqlite_int64 iPrevDocid; -#ifndef NDEBUG - int has_iPrevDocid; -#endif -} DLWriter; - -static void dlwInit(DLWriter *pWriter, DocListType iType, DataBuffer *b){ - pWriter->b = b; - pWriter->iType = iType; - pWriter->iPrevDocid = 0; -#ifndef NDEBUG - pWriter->has_iPrevDocid = 0; -#endif -} -static void dlwDestroy(DLWriter *pWriter){ - SCRAMBLE(pWriter); -} -/* iFirstDocid is the first docid in the doclist in pData. It is -** needed because pData may point within a larger doclist, in which -** case the first item would be delta-encoded. -** -** iLastDocid is the final docid in the doclist in pData. It is -** needed to create the new iPrevDocid for future delta-encoding. The -** code could decode the passed doclist to recreate iLastDocid, but -** the only current user (docListMerge) already has decoded this -** information. -*/ -/* TODO(shess) This has become just a helper for docListMerge. -** Consider a refactor to make this cleaner. -*/ -static void dlwAppend(DLWriter *pWriter, - const char *pData, int nData, - sqlite_int64 iFirstDocid, sqlite_int64 iLastDocid){ - sqlite_int64 iDocid = 0; - char c[VARINT_MAX]; - int nFirstOld, nFirstNew; /* Old and new varint len of first docid. */ -#ifndef NDEBUG - sqlite_int64 iLastDocidDelta; -#endif - - /* Recode the initial docid as delta from iPrevDocid. */ - nFirstOld = fts3GetVarint(pData, &iDocid); - assert( nFirstOldiType==DL_DOCIDS) ); - nFirstNew = fts3PutVarint(c, iFirstDocid-pWriter->iPrevDocid); - - /* Verify that the incoming doclist is valid AND that it ends with - ** the expected docid. This is essential because we'll trust this - ** docid in future delta-encoding. - */ - ASSERT_VALID_DOCLIST(pWriter->iType, pData, nData, &iLastDocidDelta); - assert( iLastDocid==iFirstDocid-iDocid+iLastDocidDelta ); - - /* Append recoded initial docid and everything else. Rest of docids - ** should have been delta-encoded from previous initial docid. - */ - if( nFirstOldb, c, nFirstNew, - pData+nFirstOld, nData-nFirstOld); - }else{ - dataBufferAppend(pWriter->b, c, nFirstNew); - } - pWriter->iPrevDocid = iLastDocid; -} -static void dlwCopy(DLWriter *pWriter, DLReader *pReader){ - dlwAppend(pWriter, dlrDocData(pReader), dlrDocDataBytes(pReader), - dlrDocid(pReader), dlrDocid(pReader)); -} -static void dlwAdd(DLWriter *pWriter, sqlite_int64 iDocid){ - char c[VARINT_MAX]; - int n = fts3PutVarint(c, iDocid-pWriter->iPrevDocid); - - /* Docids must ascend. */ - assert( !pWriter->has_iPrevDocid || iDocid>pWriter->iPrevDocid ); - assert( pWriter->iType==DL_DOCIDS ); - - dataBufferAppend(pWriter->b, c, n); - pWriter->iPrevDocid = iDocid; -#ifndef NDEBUG - pWriter->has_iPrevDocid = 1; -#endif -} - -/*******************************************************************/ -/* PLReader is used to read data from a document's position list. As -** the caller steps through the list, data is cached so that varints -** only need to be decoded once. -** -** plrInit, plrDestroy - create/destroy a reader. -** plrColumn, plrPosition, plrStartOffset, plrEndOffset - accessors -** plrAtEnd - at end of stream, only call plrDestroy once true. -** plrStep - step to the next element. -*/ -typedef struct PLReader { - /* These refer to the next position's data. nData will reach 0 when - ** reading the last position, so plrStep() signals EOF by setting - ** pData to NULL. - */ - const char *pData; - int nData; - - DocListType iType; - int iColumn; /* the last column read */ - int iPosition; /* the last position read */ - int iStartOffset; /* the last start offset read */ - int iEndOffset; /* the last end offset read */ -} PLReader; - -static int plrAtEnd(PLReader *pReader){ - return pReader->pData==NULL; -} -static int plrColumn(PLReader *pReader){ - assert( !plrAtEnd(pReader) ); - return pReader->iColumn; -} -static int plrPosition(PLReader *pReader){ - assert( !plrAtEnd(pReader) ); - return pReader->iPosition; -} -static int plrStartOffset(PLReader *pReader){ - assert( !plrAtEnd(pReader) ); - return pReader->iStartOffset; -} -static int plrEndOffset(PLReader *pReader){ - assert( !plrAtEnd(pReader) ); - return pReader->iEndOffset; -} -static void plrStep(PLReader *pReader){ - int i, n; - - assert( !plrAtEnd(pReader) ); - - if( pReader->nData==0 ){ - pReader->pData = NULL; - return; - } - - n = fts3GetVarint32(pReader->pData, &i); - if( i==POS_COLUMN ){ - n += fts3GetVarint32(pReader->pData+n, &pReader->iColumn); - pReader->iPosition = 0; - pReader->iStartOffset = 0; - n += fts3GetVarint32(pReader->pData+n, &i); - } - /* Should never see adjacent column changes. */ - assert( i!=POS_COLUMN ); - - if( i==POS_END ){ - pReader->nData = 0; - pReader->pData = NULL; - return; - } - - pReader->iPosition += i-POS_BASE; - if( pReader->iType==DL_POSITIONS_OFFSETS ){ - n += fts3GetVarint32(pReader->pData+n, &i); - pReader->iStartOffset += i; - n += fts3GetVarint32(pReader->pData+n, &i); - pReader->iEndOffset = pReader->iStartOffset+i; - } - assert( n<=pReader->nData ); - pReader->pData += n; - pReader->nData -= n; -} - -static void plrInit(PLReader *pReader, DLReader *pDLReader){ - pReader->pData = dlrPosData(pDLReader); - pReader->nData = dlrPosDataLen(pDLReader); - pReader->iType = pDLReader->iType; - pReader->iColumn = 0; - pReader->iPosition = 0; - pReader->iStartOffset = 0; - pReader->iEndOffset = 0; - plrStep(pReader); -} -static void plrDestroy(PLReader *pReader){ - SCRAMBLE(pReader); -} - -/*******************************************************************/ -/* PLWriter is used in constructing a document's position list. As a -** convenience, if iType is DL_DOCIDS, PLWriter becomes a no-op. -** PLWriter writes to the associated DLWriter's buffer. -** -** plwInit - init for writing a document's poslist. -** plwDestroy - clear a writer. -** plwAdd - append position and offset information. -** plwCopy - copy next position's data from reader to writer. -** plwTerminate - add any necessary doclist terminator. -** -** Calling plwAdd() after plwTerminate() may result in a corrupt -** doclist. -*/ -/* TODO(shess) Until we've written the second item, we can cache the -** first item's information. Then we'd have three states: -** -** - initialized with docid, no positions. -** - docid and one position. -** - docid and multiple positions. -** -** Only the last state needs to actually write to dlw->b, which would -** be an improvement in the DLCollector case. -*/ -typedef struct PLWriter { - DLWriter *dlw; - - int iColumn; /* the last column written */ - int iPos; /* the last position written */ - int iOffset; /* the last start offset written */ -} PLWriter; - -/* TODO(shess) In the case where the parent is reading these values -** from a PLReader, we could optimize to a copy if that PLReader has -** the same type as pWriter. -*/ -static void plwAdd(PLWriter *pWriter, int iColumn, int iPos, - int iStartOffset, int iEndOffset){ - /* Worst-case space for POS_COLUMN, iColumn, iPosDelta, - ** iStartOffsetDelta, and iEndOffsetDelta. - */ - char c[5*VARINT_MAX]; - int n = 0; - - /* Ban plwAdd() after plwTerminate(). */ - assert( pWriter->iPos!=-1 ); - - if( pWriter->dlw->iType==DL_DOCIDS ) return; - - if( iColumn!=pWriter->iColumn ){ - n += fts3PutVarint(c+n, POS_COLUMN); - n += fts3PutVarint(c+n, iColumn); - pWriter->iColumn = iColumn; - pWriter->iPos = 0; - pWriter->iOffset = 0; - } - assert( iPos>=pWriter->iPos ); - n += fts3PutVarint(c+n, POS_BASE+(iPos-pWriter->iPos)); - pWriter->iPos = iPos; - if( pWriter->dlw->iType==DL_POSITIONS_OFFSETS ){ - assert( iStartOffset>=pWriter->iOffset ); - n += fts3PutVarint(c+n, iStartOffset-pWriter->iOffset); - pWriter->iOffset = iStartOffset; - assert( iEndOffset>=iStartOffset ); - n += fts3PutVarint(c+n, iEndOffset-iStartOffset); - } - dataBufferAppend(pWriter->dlw->b, c, n); -} -static void plwCopy(PLWriter *pWriter, PLReader *pReader){ - plwAdd(pWriter, plrColumn(pReader), plrPosition(pReader), - plrStartOffset(pReader), plrEndOffset(pReader)); -} -static void plwInit(PLWriter *pWriter, DLWriter *dlw, sqlite_int64 iDocid){ - char c[VARINT_MAX]; - int n; - - pWriter->dlw = dlw; - - /* Docids must ascend. */ - assert( !pWriter->dlw->has_iPrevDocid || iDocid>pWriter->dlw->iPrevDocid ); - n = fts3PutVarint(c, iDocid-pWriter->dlw->iPrevDocid); - dataBufferAppend(pWriter->dlw->b, c, n); - pWriter->dlw->iPrevDocid = iDocid; -#ifndef NDEBUG - pWriter->dlw->has_iPrevDocid = 1; -#endif - - pWriter->iColumn = 0; - pWriter->iPos = 0; - pWriter->iOffset = 0; -} -/* TODO(shess) Should plwDestroy() also terminate the doclist? But -** then plwDestroy() would no longer be just a destructor, it would -** also be doing work, which isn't consistent with the overall idiom. -** Another option would be for plwAdd() to always append any necessary -** terminator, so that the output is always correct. But that would -** add incremental work to the common case with the only benefit being -** API elegance. Punt for now. -*/ -static void plwTerminate(PLWriter *pWriter){ - if( pWriter->dlw->iType>DL_DOCIDS ){ - char c[VARINT_MAX]; - int n = fts3PutVarint(c, POS_END); - dataBufferAppend(pWriter->dlw->b, c, n); - } -#ifndef NDEBUG - /* Mark as terminated for assert in plwAdd(). */ - pWriter->iPos = -1; -#endif -} -static void plwDestroy(PLWriter *pWriter){ - SCRAMBLE(pWriter); -} - -/*******************************************************************/ -/* DLCollector wraps PLWriter and DLWriter to provide a -** dynamically-allocated doclist area to use during tokenization. -** -** dlcNew - malloc up and initialize a collector. -** dlcDelete - destroy a collector and all contained items. -** dlcAddPos - append position and offset information. -** dlcAddDoclist - add the collected doclist to the given buffer. -** dlcNext - terminate the current document and open another. -*/ -typedef struct DLCollector { - DataBuffer b; - DLWriter dlw; - PLWriter plw; -} DLCollector; - -/* TODO(shess) This could also be done by calling plwTerminate() and -** dataBufferAppend(). I tried that, expecting nominal performance -** differences, but it seemed to pretty reliably be worth 1% to code -** it this way. I suspect it is the incremental malloc overhead (some -** percentage of the plwTerminate() calls will cause a realloc), so -** this might be worth revisiting if the DataBuffer implementation -** changes. -*/ -static void dlcAddDoclist(DLCollector *pCollector, DataBuffer *b){ - if( pCollector->dlw.iType>DL_DOCIDS ){ - char c[VARINT_MAX]; - int n = fts3PutVarint(c, POS_END); - dataBufferAppend2(b, pCollector->b.pData, pCollector->b.nData, c, n); - }else{ - dataBufferAppend(b, pCollector->b.pData, pCollector->b.nData); - } -} -static void dlcNext(DLCollector *pCollector, sqlite_int64 iDocid){ - plwTerminate(&pCollector->plw); - plwDestroy(&pCollector->plw); - plwInit(&pCollector->plw, &pCollector->dlw, iDocid); -} -static void dlcAddPos(DLCollector *pCollector, int iColumn, int iPos, - int iStartOffset, int iEndOffset){ - plwAdd(&pCollector->plw, iColumn, iPos, iStartOffset, iEndOffset); -} - -static DLCollector *dlcNew(sqlite_int64 iDocid, DocListType iType){ - DLCollector *pCollector = sqlite3_malloc(sizeof(DLCollector)); - dataBufferInit(&pCollector->b, 0); - dlwInit(&pCollector->dlw, iType, &pCollector->b); - plwInit(&pCollector->plw, &pCollector->dlw, iDocid); - return pCollector; -} -static void dlcDelete(DLCollector *pCollector){ - plwDestroy(&pCollector->plw); - dlwDestroy(&pCollector->dlw); - dataBufferDestroy(&pCollector->b); - SCRAMBLE(pCollector); - sqlite3_free(pCollector); -} - - -/* Copy the doclist data of iType in pData/nData into *out, trimming -** unnecessary data as we go. Only columns matching iColumn are -** copied, all columns copied if iColumn is -1. Elements with no -** matching columns are dropped. The output is an iOutType doclist. -*/ -/* NOTE(shess) This code is only valid after all doclists are merged. -** If this is run before merges, then doclist items which represent -** deletion will be trimmed, and will thus not effect a deletion -** during the merge. -*/ -static void docListTrim(DocListType iType, const char *pData, int nData, - int iColumn, DocListType iOutType, DataBuffer *out){ - DLReader dlReader; - DLWriter dlWriter; - - assert( iOutType<=iType ); - - dlrInit(&dlReader, iType, pData, nData); - dlwInit(&dlWriter, iOutType, out); - - while( !dlrAtEnd(&dlReader) ){ - PLReader plReader; - PLWriter plWriter; - int match = 0; - - plrInit(&plReader, &dlReader); - - while( !plrAtEnd(&plReader) ){ - if( iColumn==-1 || plrColumn(&plReader)==iColumn ){ - if( !match ){ - plwInit(&plWriter, &dlWriter, dlrDocid(&dlReader)); - match = 1; - } - plwAdd(&plWriter, plrColumn(&plReader), plrPosition(&plReader), - plrStartOffset(&plReader), plrEndOffset(&plReader)); - } - plrStep(&plReader); - } - if( match ){ - plwTerminate(&plWriter); - plwDestroy(&plWriter); - } - - plrDestroy(&plReader); - dlrStep(&dlReader); - } - dlwDestroy(&dlWriter); - dlrDestroy(&dlReader); -} - -/* Used by docListMerge() to keep doclists in the ascending order by -** docid, then ascending order by age (so the newest comes first). -*/ -typedef struct OrderedDLReader { - DLReader *pReader; - - /* TODO(shess) If we assume that docListMerge pReaders is ordered by - ** age (which we do), then we could use pReader comparisons to break - ** ties. - */ - int idx; -} OrderedDLReader; - -/* Order eof to end, then by docid asc, idx desc. */ -static int orderedDLReaderCmp(OrderedDLReader *r1, OrderedDLReader *r2){ - if( dlrAtEnd(r1->pReader) ){ - if( dlrAtEnd(r2->pReader) ) return 0; /* Both atEnd(). */ - return 1; /* Only r1 atEnd(). */ - } - if( dlrAtEnd(r2->pReader) ) return -1; /* Only r2 atEnd(). */ - - if( dlrDocid(r1->pReader)pReader) ) return -1; - if( dlrDocid(r1->pReader)>dlrDocid(r2->pReader) ) return 1; - - /* Descending on idx. */ - return r2->idx-r1->idx; -} - -/* Bubble p[0] to appropriate place in p[1..n-1]. Assumes that -** p[1..n-1] is already sorted. -*/ -/* TODO(shess) Is this frequent enough to warrant a binary search? -** Before implementing that, instrument the code to check. In most -** current usage, I expect that p[0] will be less than p[1] a very -** high proportion of the time. -*/ -static void orderedDLReaderReorder(OrderedDLReader *p, int n){ - while( n>1 && orderedDLReaderCmp(p, p+1)>0 ){ - OrderedDLReader tmp = p[0]; - p[0] = p[1]; - p[1] = tmp; - n--; - p++; - } -} - -/* Given an array of doclist readers, merge their doclist elements -** into out in sorted order (by docid), dropping elements from older -** readers when there is a duplicate docid. pReaders is assumed to be -** ordered by age, oldest first. -*/ -/* TODO(shess) nReaders must be <= MERGE_COUNT. This should probably -** be fixed. -*/ -static void docListMerge(DataBuffer *out, - DLReader *pReaders, int nReaders){ - OrderedDLReader readers[MERGE_COUNT]; - DLWriter writer; - int i, n; - const char *pStart = 0; - int nStart = 0; - sqlite_int64 iFirstDocid = 0, iLastDocid = 0; - - assert( nReaders>0 ); - if( nReaders==1 ){ - dataBufferAppend(out, dlrDocData(pReaders), dlrAllDataBytes(pReaders)); - return; - } - - assert( nReaders<=MERGE_COUNT ); - n = 0; - for(i=0; i0 ){ - orderedDLReaderReorder(readers+i, nReaders-i); - } - - dlwInit(&writer, pReaders[0].iType, out); - while( !dlrAtEnd(readers[0].pReader) ){ - sqlite_int64 iDocid = dlrDocid(readers[0].pReader); - - /* If this is a continuation of the current buffer to copy, extend - ** that buffer. memcpy() seems to be more efficient if it has a - ** lots of data to copy. - */ - if( dlrDocData(readers[0].pReader)==pStart+nStart ){ - nStart += dlrDocDataBytes(readers[0].pReader); - }else{ - if( pStart!=0 ){ - dlwAppend(&writer, pStart, nStart, iFirstDocid, iLastDocid); - } - pStart = dlrDocData(readers[0].pReader); - nStart = dlrDocDataBytes(readers[0].pReader); - iFirstDocid = iDocid; - } - iLastDocid = iDocid; - dlrStep(readers[0].pReader); - - /* Drop all of the older elements with the same docid. */ - for(i=1; i0 ){ - orderedDLReaderReorder(readers+i, nReaders-i); - } - } - - /* Copy over any remaining elements. */ - if( nStart>0 ) dlwAppend(&writer, pStart, nStart, iFirstDocid, iLastDocid); - dlwDestroy(&writer); -} - -/* Helper function for posListUnion(). Compares the current position -** between left and right, returning as standard C idiom of <0 if -** left0 if left>right, and 0 if left==right. "End" always -** compares greater. -*/ -static int posListCmp(PLReader *pLeft, PLReader *pRight){ - assert( pLeft->iType==pRight->iType ); - if( pLeft->iType==DL_DOCIDS ) return 0; - - if( plrAtEnd(pLeft) ) return plrAtEnd(pRight) ? 0 : 1; - if( plrAtEnd(pRight) ) return -1; - - if( plrColumn(pLeft)plrColumn(pRight) ) return 1; - - if( plrPosition(pLeft)plrPosition(pRight) ) return 1; - if( pLeft->iType==DL_POSITIONS ) return 0; - - if( plrStartOffset(pLeft)plrStartOffset(pRight) ) return 1; - - if( plrEndOffset(pLeft)plrEndOffset(pRight) ) return 1; - - return 0; -} - -/* Write the union of position lists in pLeft and pRight to pOut. -** "Union" in this case meaning "All unique position tuples". Should -** work with any doclist type, though both inputs and the output -** should be the same type. -*/ -static void posListUnion(DLReader *pLeft, DLReader *pRight, DLWriter *pOut){ - PLReader left, right; - PLWriter writer; - - assert( dlrDocid(pLeft)==dlrDocid(pRight) ); - assert( pLeft->iType==pRight->iType ); - assert( pLeft->iType==pOut->iType ); - - plrInit(&left, pLeft); - plrInit(&right, pRight); - plwInit(&writer, pOut, dlrDocid(pLeft)); - - while( !plrAtEnd(&left) || !plrAtEnd(&right) ){ - int c = posListCmp(&left, &right); - if( c<0 ){ - plwCopy(&writer, &left); - plrStep(&left); - }else if( c>0 ){ - plwCopy(&writer, &right); - plrStep(&right); - }else{ - plwCopy(&writer, &left); - plrStep(&left); - plrStep(&right); - } - } - - plwTerminate(&writer); - plwDestroy(&writer); - plrDestroy(&left); - plrDestroy(&right); -} - -/* Write the union of doclists in pLeft and pRight to pOut. For -** docids in common between the inputs, the union of the position -** lists is written. Inputs and outputs are always type DL_DEFAULT. -*/ -static void docListUnion( - const char *pLeft, int nLeft, - const char *pRight, int nRight, - DataBuffer *pOut /* Write the combined doclist here */ -){ - DLReader left, right; - DLWriter writer; - - if( nLeft==0 ){ - if( nRight!=0) dataBufferAppend(pOut, pRight, nRight); - return; - } - if( nRight==0 ){ - dataBufferAppend(pOut, pLeft, nLeft); - return; - } - - dlrInit(&left, DL_DEFAULT, pLeft, nLeft); - dlrInit(&right, DL_DEFAULT, pRight, nRight); - dlwInit(&writer, DL_DEFAULT, pOut); - - while( !dlrAtEnd(&left) || !dlrAtEnd(&right) ){ - if( dlrAtEnd(&right) ){ - dlwCopy(&writer, &left); - dlrStep(&left); - }else if( dlrAtEnd(&left) ){ - dlwCopy(&writer, &right); - dlrStep(&right); - }else if( dlrDocid(&left)dlrDocid(&right) ){ - dlwCopy(&writer, &right); - dlrStep(&right); - }else{ - posListUnion(&left, &right, &writer); - dlrStep(&left); - dlrStep(&right); - } - } - - dlrDestroy(&left); - dlrDestroy(&right); - dlwDestroy(&writer); -} - -/* -** This function is used as part of the implementation of phrase and -** NEAR matching. -** -** pLeft and pRight are DLReaders positioned to the same docid in -** lists of type DL_POSITION. This function writes an entry to the -** DLWriter pOut for each position in pRight that is less than -** (nNear+1) greater (but not equal to or smaller) than a position -** in pLeft. For example, if nNear is 0, and the positions contained -** by pLeft and pRight are: -** -** pLeft: 5 10 15 20 -** pRight: 6 9 17 21 -** -** then the docid is added to pOut. If pOut is of type DL_POSITIONS, -** then a positionids "6" and "21" are also added to pOut. -** -** If boolean argument isSaveLeft is true, then positionids are copied -** from pLeft instead of pRight. In the example above, the positions "5" -** and "20" would be added instead of "6" and "21". -*/ -static void posListPhraseMerge( - DLReader *pLeft, - DLReader *pRight, - int nNear, - int isSaveLeft, - DLWriter *pOut -){ - PLReader left, right; - PLWriter writer; - int match = 0; - - assert( dlrDocid(pLeft)==dlrDocid(pRight) ); - assert( pOut->iType!=DL_POSITIONS_OFFSETS ); - - plrInit(&left, pLeft); - plrInit(&right, pRight); - - while( !plrAtEnd(&left) && !plrAtEnd(&right) ){ - if( plrColumn(&left)plrColumn(&right) ){ - plrStep(&right); - }else if( plrPosition(&left)>=plrPosition(&right) ){ - plrStep(&right); - }else{ - if( (plrPosition(&right)-plrPosition(&left))<=(nNear+1) ){ - if( !match ){ - plwInit(&writer, pOut, dlrDocid(pLeft)); - match = 1; - } - if( !isSaveLeft ){ - plwAdd(&writer, plrColumn(&right), plrPosition(&right), 0, 0); - }else{ - plwAdd(&writer, plrColumn(&left), plrPosition(&left), 0, 0); - } - plrStep(&right); - }else{ - plrStep(&left); - } - } - } - - if( match ){ - plwTerminate(&writer); - plwDestroy(&writer); - } - - plrDestroy(&left); - plrDestroy(&right); -} - -/* -** Compare the values pointed to by the PLReaders passed as arguments. -** Return -1 if the value pointed to by pLeft is considered less than -** the value pointed to by pRight, +1 if it is considered greater -** than it, or 0 if it is equal. i.e. -** -** (*pLeft - *pRight) -** -** A PLReader that is in the EOF condition is considered greater than -** any other. If neither argument is in EOF state, the return value of -** plrColumn() is used. If the plrColumn() values are equal, the -** comparison is on the basis of plrPosition(). -*/ -static int plrCompare(PLReader *pLeft, PLReader *pRight){ - assert(!plrAtEnd(pLeft) || !plrAtEnd(pRight)); - - if( plrAtEnd(pRight) || plrAtEnd(pLeft) ){ - return (plrAtEnd(pRight) ? -1 : 1); - } - if( plrColumn(pLeft)!=plrColumn(pRight) ){ - return ((plrColumn(pLeft)0) -** and write the results into pOut. -** -** A phrase intersection means that two documents only match -** if pLeft.iPos+1==pRight.iPos. -** -** A NEAR intersection means that two documents only match if -** (abs(pLeft.iPos-pRight.iPos) 0", - /* SEGDIR_DELETE */ "delete from %_segdir where level = ?", - - /* NOTE(shess): The first three results of the following two - ** statements must match. - */ - /* SEGDIR_SELECT_SEGMENT */ - "select start_block, leaves_end_block, root from %_segdir " - " where level = ? and idx = ?", - /* SEGDIR_SELECT_ALL */ - "select start_block, leaves_end_block, root from %_segdir " - " order by level desc, idx asc", - /* SEGDIR_DELETE_ALL */ "delete from %_segdir", - /* SEGDIR_COUNT */ "select count(*), ifnull(max(level),0) from %_segdir", -}; - -/* -** A connection to a fulltext index is an instance of the following -** structure. The xCreate and xConnect methods create an instance -** of this structure and xDestroy and xDisconnect free that instance. -** All other methods receive a pointer to the structure as one of their -** arguments. -*/ -struct fulltext_vtab { - sqlite3_vtab base; /* Base class used by SQLite core */ - sqlite3 *db; /* The database connection */ - const char *zDb; /* logical database name */ - const char *zName; /* virtual table name */ - int nColumn; /* number of columns in virtual table */ - char **azColumn; /* column names. malloced */ - char **azContentColumn; /* column names in content table; malloced */ - sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */ - - /* Precompiled statements which we keep as long as the table is - ** open. - */ - sqlite3_stmt *pFulltextStatements[MAX_STMT]; - - /* Precompiled statements used for segment merges. We run a - ** separate select across the leaf level of each tree being merged. - */ - sqlite3_stmt *pLeafSelectStmts[MERGE_COUNT]; - /* The statement used to prepare pLeafSelectStmts. */ -#define LEAF_SELECT \ - "select block from %_segments where blockid between ? and ? order by blockid" - - /* These buffer pending index updates during transactions. - ** nPendingData estimates the memory size of the pending data. It - ** doesn't include the hash-bucket overhead, nor any malloc - ** overhead. When nPendingData exceeds kPendingThreshold, the - ** buffer is flushed even before the transaction closes. - ** pendingTerms stores the data, and is only valid when nPendingData - ** is >=0 (nPendingData<0 means pendingTerms has not been - ** initialized). iPrevDocid is the last docid written, used to make - ** certain we're inserting in sorted order. - */ - int nPendingData; -#define kPendingThreshold (1*1024*1024) - sqlite_int64 iPrevDocid; - fts3Hash pendingTerms; -}; - -/* -** When the core wants to do a query, it create a cursor using a -** call to xOpen. This structure is an instance of a cursor. It -** is destroyed by xClose. -*/ -typedef struct fulltext_cursor { - sqlite3_vtab_cursor base; /* Base class used by SQLite core */ - QueryType iCursorType; /* Copy of sqlite3_index_info.idxNum */ - sqlite3_stmt *pStmt; /* Prepared statement in use by the cursor */ - int eof; /* True if at End Of Results */ - Fts3Expr *pExpr; /* Parsed MATCH query string */ - Snippet snippet; /* Cached snippet for the current row */ - int iColumn; /* Column being searched */ - DataBuffer result; /* Doclist results from fulltextQuery */ - DLReader reader; /* Result reader if result not empty */ -} fulltext_cursor; - -static fulltext_vtab *cursor_vtab(fulltext_cursor *c){ - return (fulltext_vtab *) c->base.pVtab; -} - -static const sqlite3_module fts3Module; /* forward declaration */ - -/* Return a dynamically generated statement of the form - * insert into %_content (docid, ...) values (?, ...) - */ -static const char *contentInsertStatement(fulltext_vtab *v){ - StringBuffer sb; - int i; - - initStringBuffer(&sb); - append(&sb, "insert into %_content (docid, "); - appendList(&sb, v->nColumn, v->azContentColumn); - append(&sb, ") values (?"); - for(i=0; inColumn; ++i) - append(&sb, ", ?"); - append(&sb, ")"); - return stringBufferData(&sb); -} - -/* Return a dynamically generated statement of the form - * select from %_content where docid = ? - */ -static const char *contentSelectStatement(fulltext_vtab *v){ - StringBuffer sb; - initStringBuffer(&sb); - append(&sb, "SELECT "); - appendList(&sb, v->nColumn, v->azContentColumn); - append(&sb, " FROM %_content WHERE docid = ?"); - return stringBufferData(&sb); -} - -/* Return a dynamically generated statement of the form - * update %_content set [col_0] = ?, [col_1] = ?, ... - * where docid = ? - */ -static const char *contentUpdateStatement(fulltext_vtab *v){ - StringBuffer sb; - int i; - - initStringBuffer(&sb); - append(&sb, "update %_content set "); - for(i=0; inColumn; ++i) { - if( i>0 ){ - append(&sb, ", "); - } - append(&sb, v->azContentColumn[i]); - append(&sb, " = ?"); - } - append(&sb, " where docid = ?"); - return stringBufferData(&sb); -} - -/* Puts a freshly-prepared statement determined by iStmt in *ppStmt. -** If the indicated statement has never been prepared, it is prepared -** and cached, otherwise the cached version is reset. -*/ -static int sql_get_statement(fulltext_vtab *v, fulltext_statement iStmt, - sqlite3_stmt **ppStmt){ - assert( iStmtpFulltextStatements[iStmt]==NULL ){ - const char *zStmt; - int rc; - switch( iStmt ){ - case CONTENT_INSERT_STMT: - zStmt = contentInsertStatement(v); break; - case CONTENT_SELECT_STMT: - zStmt = contentSelectStatement(v); break; - case CONTENT_UPDATE_STMT: - zStmt = contentUpdateStatement(v); break; - default: - zStmt = fulltext_zStatement[iStmt]; - } - rc = sql_prepare(v->db, v->zDb, v->zName, &v->pFulltextStatements[iStmt], - zStmt); - if( zStmt != fulltext_zStatement[iStmt]) sqlite3_free((void *) zStmt); - if( rc!=SQLITE_OK ) return rc; - } else { - int rc = sqlite3_reset(v->pFulltextStatements[iStmt]); - if( rc!=SQLITE_OK ) return rc; - } - - *ppStmt = v->pFulltextStatements[iStmt]; - return SQLITE_OK; -} - -/* Like sqlite3_step(), but convert SQLITE_DONE to SQLITE_OK and -** SQLITE_ROW to SQLITE_ERROR. Useful for statements like UPDATE, -** where we expect no results. -*/ -static int sql_single_step(sqlite3_stmt *s){ - int rc = sqlite3_step(s); - return (rc==SQLITE_DONE) ? SQLITE_OK : rc; -} - -/* Like sql_get_statement(), but for special replicated LEAF_SELECT -** statements. idx -1 is a special case for an uncached version of -** the statement (used in the optimize implementation). -*/ -/* TODO(shess) Write version for generic statements and then share -** that between the cached-statement functions. -*/ -static int sql_get_leaf_statement(fulltext_vtab *v, int idx, - sqlite3_stmt **ppStmt){ - assert( idx>=-1 && idxdb, v->zDb, v->zName, ppStmt, LEAF_SELECT); - }else if( v->pLeafSelectStmts[idx]==NULL ){ - int rc = sql_prepare(v->db, v->zDb, v->zName, &v->pLeafSelectStmts[idx], - LEAF_SELECT); - if( rc!=SQLITE_OK ) return rc; - }else{ - int rc = sqlite3_reset(v->pLeafSelectStmts[idx]); - if( rc!=SQLITE_OK ) return rc; - } - - *ppStmt = v->pLeafSelectStmts[idx]; - return SQLITE_OK; -} - -/* insert into %_content (docid, ...) values ([docid], [pValues]) -** If the docid contains SQL NULL, then a unique docid will be -** generated. -*/ -static int content_insert(fulltext_vtab *v, sqlite3_value *docid, - sqlite3_value **pValues){ - sqlite3_stmt *s; - int i; - int rc = sql_get_statement(v, CONTENT_INSERT_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_value(s, 1, docid); - if( rc!=SQLITE_OK ) return rc; - - for(i=0; inColumn; ++i){ - rc = sqlite3_bind_value(s, 2+i, pValues[i]); - if( rc!=SQLITE_OK ) return rc; - } - - return sql_single_step(s); -} - -/* update %_content set col0 = pValues[0], col1 = pValues[1], ... - * where docid = [iDocid] */ -static int content_update(fulltext_vtab *v, sqlite3_value **pValues, - sqlite_int64 iDocid){ - sqlite3_stmt *s; - int i; - int rc = sql_get_statement(v, CONTENT_UPDATE_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - for(i=0; inColumn; ++i){ - rc = sqlite3_bind_value(s, 1+i, pValues[i]); - if( rc!=SQLITE_OK ) return rc; - } - - rc = sqlite3_bind_int64(s, 1+v->nColumn, iDocid); - if( rc!=SQLITE_OK ) return rc; - - return sql_single_step(s); -} - -static void freeStringArray(int nString, const char **pString){ - int i; - - for (i=0 ; i < nString ; ++i) { - if( pString[i]!=NULL ) sqlite3_free((void *) pString[i]); - } - sqlite3_free((void *) pString); -} - -/* select * from %_content where docid = [iDocid] - * The caller must delete the returned array and all strings in it. - * null fields will be NULL in the returned array. - * - * TODO: Perhaps we should return pointer/length strings here for consistency - * with other code which uses pointer/length. */ -static int content_select(fulltext_vtab *v, sqlite_int64 iDocid, - const char ***pValues){ - sqlite3_stmt *s; - const char **values; - int i; - int rc; - - *pValues = NULL; - - rc = sql_get_statement(v, CONTENT_SELECT_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int64(s, 1, iDocid); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_step(s); - if( rc!=SQLITE_ROW ) return rc; - - values = (const char **) sqlite3_malloc(v->nColumn * sizeof(const char *)); - for(i=0; inColumn; ++i){ - if( sqlite3_column_type(s, i)==SQLITE_NULL ){ - values[i] = NULL; - }else{ - values[i] = string_dup((char*)sqlite3_column_text(s, i)); - } - } - - /* We expect only one row. We must execute another sqlite3_step() - * to complete the iteration; otherwise the table will remain locked. */ - rc = sqlite3_step(s); - if( rc==SQLITE_DONE ){ - *pValues = values; - return SQLITE_OK; - } - - freeStringArray(v->nColumn, values); - return rc; -} - -/* delete from %_content where docid = [iDocid ] */ -static int content_delete(fulltext_vtab *v, sqlite_int64 iDocid){ - sqlite3_stmt *s; - int rc = sql_get_statement(v, CONTENT_DELETE_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int64(s, 1, iDocid); - if( rc!=SQLITE_OK ) return rc; - - return sql_single_step(s); -} - -/* Returns SQLITE_ROW if any rows exist in %_content, SQLITE_DONE if -** no rows exist, and any error in case of failure. -*/ -static int content_exists(fulltext_vtab *v){ - sqlite3_stmt *s; - int rc = sql_get_statement(v, CONTENT_EXISTS_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_step(s); - if( rc!=SQLITE_ROW ) return rc; - - /* We expect only one row. We must execute another sqlite3_step() - * to complete the iteration; otherwise the table will remain locked. */ - rc = sqlite3_step(s); - if( rc==SQLITE_DONE ) return SQLITE_ROW; - if( rc==SQLITE_ROW ) return SQLITE_ERROR; - return rc; -} - -/* insert into %_segments values ([pData]) -** returns assigned blockid in *piBlockid -*/ -static int block_insert(fulltext_vtab *v, const char *pData, int nData, - sqlite_int64 *piBlockid){ - sqlite3_stmt *s; - int rc = sql_get_statement(v, BLOCK_INSERT_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_blob(s, 1, pData, nData, SQLITE_STATIC); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_step(s); - if( rc==SQLITE_ROW ) return SQLITE_ERROR; - if( rc!=SQLITE_DONE ) return rc; - - /* blockid column is an alias for rowid. */ - *piBlockid = sqlite3_last_insert_rowid(v->db); - return SQLITE_OK; -} - -/* delete from %_segments -** where blockid between [iStartBlockid] and [iEndBlockid] -** -** Deletes the range of blocks, inclusive, used to delete the blocks -** which form a segment. -*/ -static int block_delete(fulltext_vtab *v, - sqlite_int64 iStartBlockid, sqlite_int64 iEndBlockid){ - sqlite3_stmt *s; - int rc = sql_get_statement(v, BLOCK_DELETE_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int64(s, 1, iStartBlockid); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int64(s, 2, iEndBlockid); - if( rc!=SQLITE_OK ) return rc; - - return sql_single_step(s); -} - -/* Returns SQLITE_ROW with *pidx set to the maximum segment idx found -** at iLevel. Returns SQLITE_DONE if there are no segments at -** iLevel. Otherwise returns an error. -*/ -static int segdir_max_index(fulltext_vtab *v, int iLevel, int *pidx){ - sqlite3_stmt *s; - int rc = sql_get_statement(v, SEGDIR_MAX_INDEX_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int(s, 1, iLevel); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_step(s); - /* Should always get at least one row due to how max() works. */ - if( rc==SQLITE_DONE ) return SQLITE_DONE; - if( rc!=SQLITE_ROW ) return rc; - - /* NULL means that there were no inputs to max(). */ - if( SQLITE_NULL==sqlite3_column_type(s, 0) ){ - rc = sqlite3_step(s); - if( rc==SQLITE_ROW ) return SQLITE_ERROR; - return rc; - } - - *pidx = sqlite3_column_int(s, 0); - - /* We expect only one row. We must execute another sqlite3_step() - * to complete the iteration; otherwise the table will remain locked. */ - rc = sqlite3_step(s); - if( rc==SQLITE_ROW ) return SQLITE_ERROR; - if( rc!=SQLITE_DONE ) return rc; - return SQLITE_ROW; -} - -/* insert into %_segdir values ( -** [iLevel], [idx], -** [iStartBlockid], [iLeavesEndBlockid], [iEndBlockid], -** [pRootData] -** ) -*/ -static int segdir_set(fulltext_vtab *v, int iLevel, int idx, - sqlite_int64 iStartBlockid, - sqlite_int64 iLeavesEndBlockid, - sqlite_int64 iEndBlockid, - const char *pRootData, int nRootData){ - sqlite3_stmt *s; - int rc = sql_get_statement(v, SEGDIR_SET_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int(s, 1, iLevel); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int(s, 2, idx); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int64(s, 3, iStartBlockid); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int64(s, 4, iLeavesEndBlockid); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int64(s, 5, iEndBlockid); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_blob(s, 6, pRootData, nRootData, SQLITE_STATIC); - if( rc!=SQLITE_OK ) return rc; - - return sql_single_step(s); -} - -/* Queries %_segdir for the block span of the segments in level -** iLevel. Returns SQLITE_DONE if there are no blocks for iLevel, -** SQLITE_ROW if there are blocks, else an error. -*/ -static int segdir_span(fulltext_vtab *v, int iLevel, - sqlite_int64 *piStartBlockid, - sqlite_int64 *piEndBlockid){ - sqlite3_stmt *s; - int rc = sql_get_statement(v, SEGDIR_SPAN_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int(s, 1, iLevel); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_step(s); - if( rc==SQLITE_DONE ) return SQLITE_DONE; /* Should never happen */ - if( rc!=SQLITE_ROW ) return rc; - - /* This happens if all segments at this level are entirely inline. */ - if( SQLITE_NULL==sqlite3_column_type(s, 0) ){ - /* We expect only one row. We must execute another sqlite3_step() - * to complete the iteration; otherwise the table will remain locked. */ - int rc2 = sqlite3_step(s); - if( rc2==SQLITE_ROW ) return SQLITE_ERROR; - return rc2; - } - - *piStartBlockid = sqlite3_column_int64(s, 0); - *piEndBlockid = sqlite3_column_int64(s, 1); - - /* We expect only one row. We must execute another sqlite3_step() - * to complete the iteration; otherwise the table will remain locked. */ - rc = sqlite3_step(s); - if( rc==SQLITE_ROW ) return SQLITE_ERROR; - if( rc!=SQLITE_DONE ) return rc; - return SQLITE_ROW; -} - -/* Delete the segment blocks and segment directory records for all -** segments at iLevel. -*/ -static int segdir_delete(fulltext_vtab *v, int iLevel){ - sqlite3_stmt *s; - sqlite_int64 iStartBlockid, iEndBlockid; - int rc = segdir_span(v, iLevel, &iStartBlockid, &iEndBlockid); - if( rc!=SQLITE_ROW && rc!=SQLITE_DONE ) return rc; - - if( rc==SQLITE_ROW ){ - rc = block_delete(v, iStartBlockid, iEndBlockid); - if( rc!=SQLITE_OK ) return rc; - } - - /* Delete the segment directory itself. */ - rc = sql_get_statement(v, SEGDIR_DELETE_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int64(s, 1, iLevel); - if( rc!=SQLITE_OK ) return rc; - - return sql_single_step(s); -} - -/* Delete entire fts index, SQLITE_OK on success, relevant error on -** failure. -*/ -static int segdir_delete_all(fulltext_vtab *v){ - sqlite3_stmt *s; - int rc = sql_get_statement(v, SEGDIR_DELETE_ALL_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - rc = sql_single_step(s); - if( rc!=SQLITE_OK ) return rc; - - rc = sql_get_statement(v, BLOCK_DELETE_ALL_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - return sql_single_step(s); -} - -/* Returns SQLITE_OK with *pnSegments set to the number of entries in -** %_segdir and *piMaxLevel set to the highest level which has a -** segment. Otherwise returns the SQLite error which caused failure. -*/ -static int segdir_count(fulltext_vtab *v, int *pnSegments, int *piMaxLevel){ - sqlite3_stmt *s; - int rc = sql_get_statement(v, SEGDIR_COUNT_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_step(s); - /* TODO(shess): This case should not be possible? Should stronger - ** measures be taken if it happens? - */ - if( rc==SQLITE_DONE ){ - *pnSegments = 0; - *piMaxLevel = 0; - return SQLITE_OK; - } - if( rc!=SQLITE_ROW ) return rc; - - *pnSegments = sqlite3_column_int(s, 0); - *piMaxLevel = sqlite3_column_int(s, 1); - - /* We expect only one row. We must execute another sqlite3_step() - * to complete the iteration; otherwise the table will remain locked. */ - rc = sqlite3_step(s); - if( rc==SQLITE_DONE ) return SQLITE_OK; - if( rc==SQLITE_ROW ) return SQLITE_ERROR; - return rc; -} - -/* TODO(shess) clearPendingTerms() is far down the file because -** writeZeroSegment() is far down the file because LeafWriter is far -** down the file. Consider refactoring the code to move the non-vtab -** code above the vtab code so that we don't need this forward -** reference. -*/ -static int clearPendingTerms(fulltext_vtab *v); - -/* -** Free the memory used to contain a fulltext_vtab structure. -*/ -static void fulltext_vtab_destroy(fulltext_vtab *v){ - int iStmt, i; - - FTSTRACE(("FTS3 Destroy %p\n", v)); - for( iStmt=0; iStmtpFulltextStatements[iStmt]!=NULL ){ - sqlite3_finalize(v->pFulltextStatements[iStmt]); - v->pFulltextStatements[iStmt] = NULL; - } - } - - for( i=0; ipLeafSelectStmts[i]!=NULL ){ - sqlite3_finalize(v->pLeafSelectStmts[i]); - v->pLeafSelectStmts[i] = NULL; - } - } - - if( v->pTokenizer!=NULL ){ - v->pTokenizer->pModule->xDestroy(v->pTokenizer); - v->pTokenizer = NULL; - } - - clearPendingTerms(v); - - sqlite3_free(v->azColumn); - for(i = 0; i < v->nColumn; ++i) { - sqlite3_free(v->azContentColumn[i]); - } - sqlite3_free(v->azContentColumn); - sqlite3_free(v); -} - -/* -** Token types for parsing the arguments to xConnect or xCreate. -*/ -#define TOKEN_EOF 0 /* End of file */ -#define TOKEN_SPACE 1 /* Any kind of whitespace */ -#define TOKEN_ID 2 /* An identifier */ -#define TOKEN_STRING 3 /* A string literal */ -#define TOKEN_PUNCT 4 /* A single punctuation character */ - -/* -** If X is a character that can be used in an identifier then -** ftsIdChar(X) will be true. Otherwise it is false. -** -** For ASCII, any character with the high-order bit set is -** allowed in an identifier. For 7-bit characters, -** isFtsIdChar[X] must be 1. -** -** Ticket #1066. the SQL standard does not allow '$' in the -** middle of identfiers. But many SQL implementations do. -** SQLite will allow '$' in identifiers for compatibility. -** But the feature is undocumented. -*/ -static const char isFtsIdChar[] = { -/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */ - 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2x */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */ - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */ - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */ -}; -#define ftsIdChar(C) (((c=C)&0x80)!=0 || (c>0x1f && isFtsIdChar[c-0x20])) - - -/* -** Return the length of the token that begins at z[0]. -** Store the token type in *tokenType before returning. -*/ -static int ftsGetToken(const char *z, int *tokenType){ - int i, c; - switch( *z ){ - case 0: { - *tokenType = TOKEN_EOF; - return 0; - } - case ' ': case '\t': case '\n': case '\f': case '\r': { - for(i=1; safe_isspace(z[i]); i++){} - *tokenType = TOKEN_SPACE; - return i; - } - case '`': - case '\'': - case '"': { - int delim = z[0]; - for(i=1; (c=z[i])!=0; i++){ - if( c==delim ){ - if( z[i+1]==delim ){ - i++; - }else{ - break; - } - } - } - *tokenType = TOKEN_STRING; - return i + (c!=0); - } - case '[': { - for(i=1, c=z[0]; c!=']' && (c=z[i])!=0; i++){} - *tokenType = TOKEN_ID; - return i; - } - default: { - if( !ftsIdChar(*z) ){ - break; - } - for(i=1; ftsIdChar(z[i]); i++){} - *tokenType = TOKEN_ID; - return i; - } - } - *tokenType = TOKEN_PUNCT; - return 1; -} - -/* -** A token extracted from a string is an instance of the following -** structure. -*/ -typedef struct FtsToken { - const char *z; /* Pointer to token text. Not '\000' terminated */ - short int n; /* Length of the token text in bytes. */ -} FtsToken; - -/* -** Given a input string (which is really one of the argv[] parameters -** passed into xConnect or xCreate) split the string up into tokens. -** Return an array of pointers to '\000' terminated strings, one string -** for each non-whitespace token. -** -** The returned array is terminated by a single NULL pointer. -** -** Space to hold the returned array is obtained from a single -** malloc and should be freed by passing the return value to free(). -** The individual strings within the token list are all a part of -** the single memory allocation and will all be freed at once. -*/ -static char **tokenizeString(const char *z, int *pnToken){ - int nToken = 0; - FtsToken *aToken = sqlite3_malloc( strlen(z) * sizeof(aToken[0]) ); - int n = 1; - int e, i; - int totalSize = 0; - char **azToken; - char *zCopy; - while( n>0 ){ - n = ftsGetToken(z, &e); - if( e!=TOKEN_SPACE ){ - aToken[nToken].z = z; - aToken[nToken].n = n; - nToken++; - totalSize += n+1; - } - z += n; - } - azToken = (char**)sqlite3_malloc( nToken*sizeof(char*) + totalSize ); - zCopy = (char*)&azToken[nToken]; - nToken--; - for(i=0; i=0 ){ - azIn[j] = azIn[i]; - } - j++; - } - } - azIn[j] = 0; - } -} - - -/* -** Find the first alphanumeric token in the string zIn. Null-terminate -** this token. Remove any quotation marks. And return a pointer to -** the result. -*/ -static char *firstToken(char *zIn, char **pzTail){ - int n, ttype; - while(1){ - n = ftsGetToken(zIn, &ttype); - if( ttype==TOKEN_SPACE ){ - zIn += n; - }else if( ttype==TOKEN_EOF ){ - *pzTail = zIn; - return 0; - }else{ - zIn[n] = 0; - *pzTail = &zIn[1]; - dequoteString(zIn); - return zIn; - } - } - /*NOTREACHED*/ -} - -/* Return true if... -** -** * s begins with the string t, ignoring case -** * s is longer than t -** * The first character of s beyond t is not a alphanumeric -** -** Ignore leading space in *s. -** -** To put it another way, return true if the first token of -** s[] is t[]. -*/ -static int startsWith(const char *s, const char *t){ - while( safe_isspace(*s) ){ s++; } - while( *t ){ - if( safe_tolower(*s++)!=safe_tolower(*t++) ) return 0; - } - return *s!='_' && !safe_isalnum(*s); -} - -/* -** An instance of this structure defines the "spec" of a -** full text index. This structure is populated by parseSpec -** and use by fulltextConnect and fulltextCreate. -*/ -typedef struct TableSpec { - const char *zDb; /* Logical database name */ - const char *zName; /* Name of the full-text index */ - int nColumn; /* Number of columns to be indexed */ - char **azColumn; /* Original names of columns to be indexed */ - char **azContentColumn; /* Column names for %_content */ - char **azTokenizer; /* Name of tokenizer and its arguments */ -} TableSpec; - -/* -** Reclaim all of the memory used by a TableSpec -*/ -static void clearTableSpec(TableSpec *p) { - sqlite3_free(p->azColumn); - sqlite3_free(p->azContentColumn); - sqlite3_free(p->azTokenizer); -} - -/* Parse a CREATE VIRTUAL TABLE statement, which looks like this: - * - * CREATE VIRTUAL TABLE email - * USING fts3(subject, body, tokenize mytokenizer(myarg)) - * - * We return parsed information in a TableSpec structure. - * - */ -static int parseSpec(TableSpec *pSpec, int argc, const char *const*argv, - char**pzErr){ - int i, n; - char *z, *zDummy; - char **azArg; - const char *zTokenizer = 0; /* argv[] entry describing the tokenizer */ - - assert( argc>=3 ); - /* Current interface: - ** argv[0] - module name - ** argv[1] - database name - ** argv[2] - table name - ** argv[3..] - columns, optionally followed by tokenizer specification - ** and snippet delimiters specification. - */ - - /* Make a copy of the complete argv[][] array in a single allocation. - ** The argv[][] array is read-only and transient. We can write to the - ** copy in order to modify things and the copy is persistent. - */ - CLEAR(pSpec); - for(i=n=0; izDb = azArg[1]; - pSpec->zName = azArg[2]; - pSpec->nColumn = 0; - pSpec->azColumn = azArg; - zTokenizer = "tokenize simple"; - for(i=3; inColumn] = firstToken(azArg[i], &zDummy); - pSpec->nColumn++; - } - } - if( pSpec->nColumn==0 ){ - azArg[0] = "content"; - pSpec->nColumn = 1; - } - - /* - ** Construct the list of content column names. - ** - ** Each content column name will be of the form cNNAAAA - ** where NN is the column number and AAAA is the sanitized - ** column name. "sanitized" means that special characters are - ** converted to "_". The cNN prefix guarantees that all column - ** names are unique. - ** - ** The AAAA suffix is not strictly necessary. It is included - ** for the convenience of people who might examine the generated - ** %_content table and wonder what the columns are used for. - */ - pSpec->azContentColumn = sqlite3_malloc( pSpec->nColumn * sizeof(char *) ); - if( pSpec->azContentColumn==0 ){ - clearTableSpec(pSpec); - return SQLITE_NOMEM; - } - for(i=0; inColumn; i++){ - char *p; - pSpec->azContentColumn[i] = sqlite3_mprintf("c%d%s", i, azArg[i]); - for (p = pSpec->azContentColumn[i]; *p ; ++p) { - if( !safe_isalnum(*p) ) *p = '_'; - } - } - - /* - ** Parse the tokenizer specification string. - */ - pSpec->azTokenizer = tokenizeString(zTokenizer, &n); - tokenListToIdList(pSpec->azTokenizer); - - return SQLITE_OK; -} - -/* -** Generate a CREATE TABLE statement that describes the schema of -** the virtual table. Return a pointer to this schema string. -** -** Space is obtained from sqlite3_mprintf() and should be freed -** using sqlite3_free(). -*/ -static char *fulltextSchema( - int nColumn, /* Number of columns */ - const char *const* azColumn, /* List of columns */ - const char *zTableName /* Name of the table */ -){ - int i; - char *zSchema, *zNext; - const char *zSep = "("; - zSchema = sqlite3_mprintf("CREATE TABLE x"); - for(i=0; ibase */ - v->db = db; - v->zDb = spec->zDb; /* Freed when azColumn is freed */ - v->zName = spec->zName; /* Freed when azColumn is freed */ - v->nColumn = spec->nColumn; - v->azContentColumn = spec->azContentColumn; - spec->azContentColumn = 0; - v->azColumn = spec->azColumn; - spec->azColumn = 0; - - if( spec->azTokenizer==0 ){ - return SQLITE_NOMEM; - } - - zTok = spec->azTokenizer[0]; - if( !zTok ){ - zTok = "simple"; - } - nTok = strlen(zTok)+1; - - m = (sqlite3_tokenizer_module *)sqlite3Fts3HashFind(pHash, zTok, nTok); - if( !m ){ - *pzErr = sqlite3_mprintf("unknown tokenizer: %s", spec->azTokenizer[0]); - rc = SQLITE_ERROR; - goto err; - } - - for(n=0; spec->azTokenizer[n]; n++){} - if( n ){ - rc = m->xCreate(n-1, (const char*const*)&spec->azTokenizer[1], - &v->pTokenizer); - }else{ - rc = m->xCreate(0, 0, &v->pTokenizer); - } - if( rc!=SQLITE_OK ) goto err; - v->pTokenizer->pModule = m; - - /* TODO: verify the existence of backing tables foo_content, foo_term */ - - schema = fulltextSchema(v->nColumn, (const char*const*)v->azColumn, - spec->zName); - rc = sqlite3_declare_vtab(db, schema); - sqlite3_free(schema); - if( rc!=SQLITE_OK ) goto err; - - memset(v->pFulltextStatements, 0, sizeof(v->pFulltextStatements)); - - /* Indicate that the buffer is not live. */ - v->nPendingData = -1; - - *ppVTab = &v->base; - FTSTRACE(("FTS3 Connect %p\n", v)); - - return rc; - -err: - fulltext_vtab_destroy(v); - return rc; -} - -static int fulltextConnect( - sqlite3 *db, - void *pAux, - int argc, const char *const*argv, - sqlite3_vtab **ppVTab, - char **pzErr -){ - TableSpec spec; - int rc = parseSpec(&spec, argc, argv, pzErr); - if( rc!=SQLITE_OK ) return rc; - - rc = constructVtab(db, (fts3Hash *)pAux, &spec, ppVTab, pzErr); - clearTableSpec(&spec); - return rc; -} - -/* The %_content table holds the text of each document, with -** the docid column exposed as the SQLite rowid for the table. -*/ -/* TODO(shess) This comment needs elaboration to match the updated -** code. Work it into the top-of-file comment at that time. -*/ -static int fulltextCreate(sqlite3 *db, void *pAux, - int argc, const char * const *argv, - sqlite3_vtab **ppVTab, char **pzErr){ - int rc; - TableSpec spec; - StringBuffer schema; - FTSTRACE(("FTS3 Create\n")); - - rc = parseSpec(&spec, argc, argv, pzErr); - if( rc!=SQLITE_OK ) return rc; - - initStringBuffer(&schema); - append(&schema, "CREATE TABLE %_content("); - append(&schema, " docid INTEGER PRIMARY KEY,"); - appendList(&schema, spec.nColumn, spec.azContentColumn); - append(&schema, ")"); - rc = sql_exec(db, spec.zDb, spec.zName, stringBufferData(&schema)); - stringBufferDestroy(&schema); - if( rc!=SQLITE_OK ) goto out; - - rc = sql_exec(db, spec.zDb, spec.zName, - "create table %_segments(" - " blockid INTEGER PRIMARY KEY," - " block blob" - ");" - ); - if( rc!=SQLITE_OK ) goto out; - - rc = sql_exec(db, spec.zDb, spec.zName, - "create table %_segdir(" - " level integer," - " idx integer," - " start_block integer," - " leaves_end_block integer," - " end_block integer," - " root blob," - " primary key(level, idx)" - ");"); - if( rc!=SQLITE_OK ) goto out; - - rc = constructVtab(db, (fts3Hash *)pAux, &spec, ppVTab, pzErr); - -out: - clearTableSpec(&spec); - return rc; -} - -/* Decide how to handle an SQL query. */ -static int fulltextBestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ - fulltext_vtab *v = (fulltext_vtab *)pVTab; - int i; - FTSTRACE(("FTS3 BestIndex\n")); - - for(i=0; inConstraint; ++i){ - const struct sqlite3_index_constraint *pConstraint; - pConstraint = &pInfo->aConstraint[i]; - if( pConstraint->usable ) { - if( (pConstraint->iColumn==-1 || pConstraint->iColumn==v->nColumn+1) && - pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){ - pInfo->idxNum = QUERY_DOCID; /* lookup by docid */ - FTSTRACE(("FTS3 QUERY_DOCID\n")); - } else if( pConstraint->iColumn>=0 && pConstraint->iColumn<=v->nColumn && - pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH ){ - /* full-text search */ - pInfo->idxNum = QUERY_FULLTEXT + pConstraint->iColumn; - FTSTRACE(("FTS3 QUERY_FULLTEXT %d\n", pConstraint->iColumn)); - } else continue; - - pInfo->aConstraintUsage[i].argvIndex = 1; - pInfo->aConstraintUsage[i].omit = 1; - - /* An arbitrary value for now. - * TODO: Perhaps docid matches should be considered cheaper than - * full-text searches. */ - pInfo->estimatedCost = 1.0; - - return SQLITE_OK; - } - } - pInfo->idxNum = QUERY_GENERIC; - return SQLITE_OK; -} - -static int fulltextDisconnect(sqlite3_vtab *pVTab){ - FTSTRACE(("FTS3 Disconnect %p\n", pVTab)); - fulltext_vtab_destroy((fulltext_vtab *)pVTab); - return SQLITE_OK; -} - -static int fulltextDestroy(sqlite3_vtab *pVTab){ - fulltext_vtab *v = (fulltext_vtab *)pVTab; - int rc; - - FTSTRACE(("FTS3 Destroy %p\n", pVTab)); - rc = sql_exec(v->db, v->zDb, v->zName, - "drop table if exists %_content;" - "drop table if exists %_segments;" - "drop table if exists %_segdir;" - ); - if( rc!=SQLITE_OK ) return rc; - - fulltext_vtab_destroy((fulltext_vtab *)pVTab); - return SQLITE_OK; -} - -static int fulltextOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ - fulltext_cursor *c; - - c = (fulltext_cursor *) sqlite3_malloc(sizeof(fulltext_cursor)); - if( c ){ - memset(c, 0, sizeof(fulltext_cursor)); - /* sqlite will initialize c->base */ - *ppCursor = &c->base; - FTSTRACE(("FTS3 Open %p: %p\n", pVTab, c)); - return SQLITE_OK; - }else{ - return SQLITE_NOMEM; - } -} - -/* Free all of the dynamically allocated memory held by the -** Snippet -*/ -static void snippetClear(Snippet *p){ - sqlite3_free(p->aMatch); - sqlite3_free(p->zOffset); - sqlite3_free(p->zSnippet); - CLEAR(p); -} - -/* -** Append a single entry to the p->aMatch[] log. -*/ -static void snippetAppendMatch( - Snippet *p, /* Append the entry to this snippet */ - int iCol, int iTerm, /* The column and query term */ - int iToken, /* Matching token in document */ - int iStart, int nByte /* Offset and size of the match */ -){ - int i; - struct snippetMatch *pMatch; - if( p->nMatch+1>=p->nAlloc ){ - p->nAlloc = p->nAlloc*2 + 10; - p->aMatch = sqlite3_realloc(p->aMatch, p->nAlloc*sizeof(p->aMatch[0]) ); - if( p->aMatch==0 ){ - p->nMatch = 0; - p->nAlloc = 0; - return; - } - } - i = p->nMatch++; - pMatch = &p->aMatch[i]; - pMatch->iCol = iCol; - pMatch->iTerm = iTerm; - pMatch->iToken = iToken; - pMatch->iStart = iStart; - pMatch->nByte = nByte; -} - -/* -** Sizing information for the circular buffer used in snippetOffsetsOfColumn() -*/ -#define FTS3_ROTOR_SZ (32) -#define FTS3_ROTOR_MASK (FTS3_ROTOR_SZ-1) - -/* -** Function to iterate through the tokens of a compiled expression. -** -** Except, skip all tokens on the right-hand side of a NOT operator. -** This function is used to find tokens as part of snippet and offset -** generation and we do nt want snippets and offsets to report matches -** for tokens on the RHS of a NOT. -*/ -static int fts3NextExprToken(Fts3Expr **ppExpr, int *piToken){ - Fts3Expr *p = *ppExpr; - int iToken = *piToken; - if( iToken<0 ){ - /* In this case the expression p is the root of an expression tree. - ** Move to the first token in the expression tree. - */ - while( p->pLeft ){ - p = p->pLeft; - } - iToken = 0; - }else{ - assert(p && p->eType==FTSQUERY_PHRASE ); - if( iToken<(p->pPhrase->nToken-1) ){ - iToken++; - }else{ - iToken = 0; - while( p->pParent && p->pParent->pLeft!=p ){ - assert( p->pParent->pRight==p ); - p = p->pParent; - } - p = p->pParent; - if( p ){ - assert( p->pRight!=0 ); - p = p->pRight; - while( p->pLeft ){ - p = p->pLeft; - } - } - } - } - - *ppExpr = p; - *piToken = iToken; - return p?1:0; -} - -/* -** Return TRUE if the expression node pExpr is located beneath the -** RHS of a NOT operator. -*/ -static int fts3ExprBeneathNot(Fts3Expr *p){ - Fts3Expr *pParent; - while( p ){ - pParent = p->pParent; - if( pParent && pParent->eType==FTSQUERY_NOT && pParent->pRight==p ){ - return 1; - } - p = pParent; - } - return 0; -} - -/* -** Add entries to pSnippet->aMatch[] for every match that occurs against -** document zDoc[0..nDoc-1] which is stored in column iColumn. -*/ -static void snippetOffsetsOfColumn( - fulltext_cursor *pCur, /* The fulltest search cursor */ - Snippet *pSnippet, /* The Snippet object to be filled in */ - int iColumn, /* Index of fulltext table column */ - const char *zDoc, /* Text of the fulltext table column */ - int nDoc /* Length of zDoc in bytes */ -){ - const sqlite3_tokenizer_module *pTModule; /* The tokenizer module */ - sqlite3_tokenizer *pTokenizer; /* The specific tokenizer */ - sqlite3_tokenizer_cursor *pTCursor; /* Tokenizer cursor */ - fulltext_vtab *pVtab; /* The full text index */ - int nColumn; /* Number of columns in the index */ - int i, j; /* Loop counters */ - int rc; /* Return code */ - unsigned int match, prevMatch; /* Phrase search bitmasks */ - const char *zToken; /* Next token from the tokenizer */ - int nToken; /* Size of zToken */ - int iBegin, iEnd, iPos; /* Offsets of beginning and end */ - - /* The following variables keep a circular buffer of the last - ** few tokens */ - unsigned int iRotor = 0; /* Index of current token */ - int iRotorBegin[FTS3_ROTOR_SZ]; /* Beginning offset of token */ - int iRotorLen[FTS3_ROTOR_SZ]; /* Length of token */ - - pVtab = cursor_vtab(pCur); - nColumn = pVtab->nColumn; - pTokenizer = pVtab->pTokenizer; - pTModule = pTokenizer->pModule; - rc = pTModule->xOpen(pTokenizer, zDoc, nDoc, &pTCursor); - if( rc ) return; - pTCursor->pTokenizer = pTokenizer; - - prevMatch = 0; - while( !pTModule->xNext(pTCursor, &zToken, &nToken, &iBegin, &iEnd, &iPos) ){ - Fts3Expr *pIter = pCur->pExpr; - int iIter = -1; - iRotorBegin[iRotor&FTS3_ROTOR_MASK] = iBegin; - iRotorLen[iRotor&FTS3_ROTOR_MASK] = iEnd-iBegin; - match = 0; - for(i=0; i<(FTS3_ROTOR_SZ-1) && fts3NextExprToken(&pIter, &iIter); i++){ - int nPhrase; /* Number of tokens in current phrase */ - struct PhraseToken *pToken; /* Current token */ - int iCol; /* Column index */ - - if( fts3ExprBeneathNot(pIter) ) continue; - nPhrase = pIter->pPhrase->nToken; - pToken = &pIter->pPhrase->aToken[iIter]; - iCol = pIter->pPhrase->iColumn; - if( iCol>=0 && iColn>nToken ) continue; - if( !pToken->isPrefix && pToken->nn<=nToken ); - if( memcmp(pToken->z, zToken, pToken->n) ) continue; - if( iIter>0 && (prevMatch & (1<=0; j--){ - int k = (iRotor-j) & FTS3_ROTOR_MASK; - snippetAppendMatch(pSnippet, iColumn, i-j, iPos-j, - iRotorBegin[k], iRotorLen[k]); - } - } - } - prevMatch = match<<1; - iRotor++; - } - pTModule->xClose(pTCursor); -} - -/* -** Remove entries from the pSnippet structure to account for the NEAR -** operator. When this is called, pSnippet contains the list of token -** offsets produced by treating all NEAR operators as AND operators. -** This function removes any entries that should not be present after -** accounting for the NEAR restriction. For example, if the queried -** document is: -** -** "A B C D E A" -** -** and the query is: -** -** A NEAR/0 E -** -** then when this function is called the Snippet contains token offsets -** 0, 4 and 5. This function removes the "0" entry (because the first A -** is not near enough to an E). -** -** When this function is called, the value pointed to by parameter piLeft is -** the integer id of the left-most token in the expression tree headed by -** pExpr. This function increments *piLeft by the total number of tokens -** in the expression tree headed by pExpr. -** -** Return 1 if any trimming occurs. Return 0 if no trimming is required. -*/ -static int trimSnippetOffsets( - Fts3Expr *pExpr, /* The search expression */ - Snippet *pSnippet, /* The set of snippet offsets to be trimmed */ - int *piLeft /* Index of left-most token in pExpr */ -){ - if( pExpr ){ - if( trimSnippetOffsets(pExpr->pLeft, pSnippet, piLeft) ){ - return 1; - } - - switch( pExpr->eType ){ - case FTSQUERY_PHRASE: - *piLeft += pExpr->pPhrase->nToken; - break; - case FTSQUERY_NEAR: { - /* The right-hand-side of a NEAR operator is always a phrase. The - ** left-hand-side is either a phrase or an expression tree that is - ** itself headed by a NEAR operator. The following initializations - ** set local variable iLeft to the token number of the left-most - ** token in the right-hand phrase, and iRight to the right most - ** token in the same phrase. For example, if we had: - ** - ** MATCH '"abc def" NEAR/2 "ghi jkl"' - ** - ** then iLeft will be set to 2 (token number of ghi) and nToken will - ** be set to 4. - */ - Fts3Expr *pLeft = pExpr->pLeft; - Fts3Expr *pRight = pExpr->pRight; - int iLeft = *piLeft; - int nNear = pExpr->nNear; - int nToken = pRight->pPhrase->nToken; - int jj, ii; - if( pLeft->eType==FTSQUERY_NEAR ){ - pLeft = pLeft->pRight; - } - assert( pRight->eType==FTSQUERY_PHRASE ); - assert( pLeft->eType==FTSQUERY_PHRASE ); - nToken += pLeft->pPhrase->nToken; - - for(ii=0; iinMatch; ii++){ - struct snippetMatch *p = &pSnippet->aMatch[ii]; - if( p->iTerm==iLeft ){ - int isOk = 0; - /* Snippet ii is an occurence of query term iLeft in the document. - ** It occurs at position (p->iToken) of the document. We now - ** search for an instance of token (iLeft-1) somewhere in the - ** range (p->iToken - nNear)...(p->iToken + nNear + nToken) within - ** the set of snippetMatch structures. If one is found, proceed. - ** If one cannot be found, then remove snippets ii..(ii+N-1) - ** from the matching snippets, where N is the number of tokens - ** in phrase pRight->pPhrase. - */ - for(jj=0; isOk==0 && jjnMatch; jj++){ - struct snippetMatch *p2 = &pSnippet->aMatch[jj]; - if( p2->iTerm==(iLeft-1) ){ - if( p2->iToken>=(p->iToken-nNear-1) - && p2->iToken<(p->iToken+nNear+nToken) - ){ - isOk = 1; - } - } - } - if( !isOk ){ - int kk; - for(kk=0; kkpPhrase->nToken; kk++){ - pSnippet->aMatch[kk+ii].iTerm = -2; - } - return 1; - } - } - if( p->iTerm==(iLeft-1) ){ - int isOk = 0; - for(jj=0; isOk==0 && jjnMatch; jj++){ - struct snippetMatch *p2 = &pSnippet->aMatch[jj]; - if( p2->iTerm==iLeft ){ - if( p2->iToken<=(p->iToken+nNear+1) - && p2->iToken>(p->iToken-nNear-nToken) - ){ - isOk = 1; - } - } - } - if( !isOk ){ - int kk; - for(kk=0; kkpPhrase->nToken; kk++){ - pSnippet->aMatch[ii-kk].iTerm = -2; - } - return 1; - } - } - } - break; - } - } - - if( trimSnippetOffsets(pExpr->pRight, pSnippet, piLeft) ){ - return 1; - } - } - return 0; -} - -/* -** Compute all offsets for the current row of the query. -** If the offsets have already been computed, this routine is a no-op. -*/ -static void snippetAllOffsets(fulltext_cursor *p){ - int nColumn; - int iColumn, i; - int iFirst, iLast; - int iTerm = 0; - fulltext_vtab *pFts = cursor_vtab(p); - - if( p->snippet.nMatch || p->pExpr==0 ){ - return; - } - nColumn = pFts->nColumn; - iColumn = (p->iCursorType - QUERY_FULLTEXT); - if( iColumn<0 || iColumn>=nColumn ){ - /* Look for matches over all columns of the full-text index */ - iFirst = 0; - iLast = nColumn-1; - }else{ - /* Look for matches in the iColumn-th column of the index only */ - iFirst = iColumn; - iLast = iColumn; - } - for(i=iFirst; i<=iLast; i++){ - const char *zDoc; - int nDoc; - zDoc = (const char*)sqlite3_column_text(p->pStmt, i+1); - nDoc = sqlite3_column_bytes(p->pStmt, i+1); - snippetOffsetsOfColumn(p, &p->snippet, i, zDoc, nDoc); - } - - while( trimSnippetOffsets(p->pExpr, &p->snippet, &iTerm) ){ - iTerm = 0; - } -} - -/* -** Convert the information in the aMatch[] array of the snippet -** into the string zOffset[0..nOffset-1]. This string is used as -** the return of the SQL offsets() function. -*/ -static void snippetOffsetText(Snippet *p){ - int i; - int cnt = 0; - StringBuffer sb; - char zBuf[200]; - if( p->zOffset ) return; - initStringBuffer(&sb); - for(i=0; inMatch; i++){ - struct snippetMatch *pMatch = &p->aMatch[i]; - if( pMatch->iTerm>=0 ){ - /* If snippetMatch.iTerm is less than 0, then the match was - ** discarded as part of processing the NEAR operator (see the - ** trimSnippetOffsetsForNear() function for details). Ignore - ** it in this case - */ - zBuf[0] = ' '; - sqlite3_snprintf(sizeof(zBuf)-1, &zBuf[cnt>0], "%d %d %d %d", - pMatch->iCol, pMatch->iTerm, pMatch->iStart, pMatch->nByte); - append(&sb, zBuf); - cnt++; - } - } - p->zOffset = stringBufferData(&sb); - p->nOffset = stringBufferLength(&sb); -} - -/* -** zDoc[0..nDoc-1] is phrase of text. aMatch[0..nMatch-1] are a set -** of matching words some of which might be in zDoc. zDoc is column -** number iCol. -** -** iBreak is suggested spot in zDoc where we could begin or end an -** excerpt. Return a value similar to iBreak but possibly adjusted -** to be a little left or right so that the break point is better. -*/ -static int wordBoundary( - int iBreak, /* The suggested break point */ - const char *zDoc, /* Document text */ - int nDoc, /* Number of bytes in zDoc[] */ - struct snippetMatch *aMatch, /* Matching words */ - int nMatch, /* Number of entries in aMatch[] */ - int iCol /* The column number for zDoc[] */ -){ - int i; - if( iBreak<=10 ){ - return 0; - } - if( iBreak>=nDoc-10 ){ - return nDoc; - } - for(i=0; i0 && aMatch[i-1].iStart+aMatch[i-1].nByte>=iBreak ){ - return aMatch[i-1].iStart; - } - } - for(i=1; i<=10; i++){ - if( safe_isspace(zDoc[iBreak-i]) ){ - return iBreak - i + 1; - } - if( safe_isspace(zDoc[iBreak+i]) ){ - return iBreak + i + 1; - } - } - return iBreak; -} - - - -/* -** Allowed values for Snippet.aMatch[].snStatus -*/ -#define SNIPPET_IGNORE 0 /* It is ok to omit this match from the snippet */ -#define SNIPPET_DESIRED 1 /* We want to include this match in the snippet */ - -/* -** Generate the text of a snippet. -*/ -static void snippetText( - fulltext_cursor *pCursor, /* The cursor we need the snippet for */ - const char *zStartMark, /* Markup to appear before each match */ - const char *zEndMark, /* Markup to appear after each match */ - const char *zEllipsis /* Ellipsis mark */ -){ - int i, j; - struct snippetMatch *aMatch; - int nMatch; - int nDesired; - StringBuffer sb; - int tailCol; - int tailOffset; - int iCol; - int nDoc; - const char *zDoc; - int iStart, iEnd; - int tailEllipsis = 0; - int iMatch; - - - sqlite3_free(pCursor->snippet.zSnippet); - pCursor->snippet.zSnippet = 0; - aMatch = pCursor->snippet.aMatch; - nMatch = pCursor->snippet.nMatch; - initStringBuffer(&sb); - - for(i=0; i0; i++){ - if( aMatch[i].snStatus!=SNIPPET_DESIRED ) continue; - nDesired--; - iCol = aMatch[i].iCol; - zDoc = (const char*)sqlite3_column_text(pCursor->pStmt, iCol+1); - nDoc = sqlite3_column_bytes(pCursor->pStmt, iCol+1); - iStart = aMatch[i].iStart - 40; - iStart = wordBoundary(iStart, zDoc, nDoc, aMatch, nMatch, iCol); - if( iStart<=10 ){ - iStart = 0; - } - if( iCol==tailCol && iStart<=tailOffset+20 ){ - iStart = tailOffset; - } - if( (iCol!=tailCol && tailCol>=0) || iStart!=tailOffset ){ - trimWhiteSpace(&sb); - appendWhiteSpace(&sb); - append(&sb, zEllipsis); - appendWhiteSpace(&sb); - } - iEnd = aMatch[i].iStart + aMatch[i].nByte + 40; - iEnd = wordBoundary(iEnd, zDoc, nDoc, aMatch, nMatch, iCol); - if( iEnd>=nDoc-10 ){ - iEnd = nDoc; - tailEllipsis = 0; - }else{ - tailEllipsis = 1; - } - while( iMatchsnippet.zSnippet = stringBufferData(&sb); - pCursor->snippet.nSnippet = stringBufferLength(&sb); -} - - -/* -** Close the cursor. For additional information see the documentation -** on the xClose method of the virtual table interface. -*/ -static int fulltextClose(sqlite3_vtab_cursor *pCursor){ - fulltext_cursor *c = (fulltext_cursor *) pCursor; - FTSTRACE(("FTS3 Close %p\n", c)); - sqlite3_finalize(c->pStmt); - sqlite3Fts3ExprFree(c->pExpr); - snippetClear(&c->snippet); - if( c->result.nData!=0 ){ - dlrDestroy(&c->reader); - } - dataBufferDestroy(&c->result); - sqlite3_free(c); - return SQLITE_OK; -} - -static int fulltextNext(sqlite3_vtab_cursor *pCursor){ - fulltext_cursor *c = (fulltext_cursor *) pCursor; - int rc; - - FTSTRACE(("FTS3 Next %p\n", pCursor)); - snippetClear(&c->snippet); - if( c->iCursorType < QUERY_FULLTEXT ){ - /* TODO(shess) Handle SQLITE_SCHEMA AND SQLITE_BUSY. */ - rc = sqlite3_step(c->pStmt); - switch( rc ){ - case SQLITE_ROW: - c->eof = 0; - return SQLITE_OK; - case SQLITE_DONE: - c->eof = 1; - return SQLITE_OK; - default: - c->eof = 1; - return rc; - } - } else { /* full-text query */ - rc = sqlite3_reset(c->pStmt); - if( rc!=SQLITE_OK ) return rc; - - if( c->result.nData==0 || dlrAtEnd(&c->reader) ){ - c->eof = 1; - return SQLITE_OK; - } - rc = sqlite3_bind_int64(c->pStmt, 1, dlrDocid(&c->reader)); - dlrStep(&c->reader); - if( rc!=SQLITE_OK ) return rc; - /* TODO(shess) Handle SQLITE_SCHEMA AND SQLITE_BUSY. */ - rc = sqlite3_step(c->pStmt); - if( rc==SQLITE_ROW ){ /* the case we expect */ - c->eof = 0; - return SQLITE_OK; - } - /* an error occurred; abort */ - return rc==SQLITE_DONE ? SQLITE_ERROR : rc; - } -} - - -/* TODO(shess) If we pushed LeafReader to the top of the file, or to -** another file, term_select() could be pushed above -** docListOfTerm(). -*/ -static int termSelect(fulltext_vtab *v, int iColumn, - const char *pTerm, int nTerm, int isPrefix, - DocListType iType, DataBuffer *out); - -/* -** Return a DocList corresponding to the phrase *pPhrase. -** -** The resulting DL_DOCIDS doclist is stored in pResult, which is -** overwritten. -*/ -static int docListOfPhrase( - fulltext_vtab *pTab, /* The full text index */ - Fts3Phrase *pPhrase, /* Phrase to return a doclist corresponding to */ - DocListType eListType, /* Either DL_DOCIDS or DL_POSITIONS */ - DataBuffer *pResult /* Write the result here */ -){ - int ii; - int rc = SQLITE_OK; - int iCol = pPhrase->iColumn; - DocListType eType = eListType; - assert( eType==DL_POSITIONS || eType==DL_DOCIDS ); - if( pPhrase->nToken>1 ){ - eType = DL_POSITIONS; - } - - /* This code should never be called with buffered updates. */ - assert( pTab->nPendingData<0 ); - - for(ii=0; rc==SQLITE_OK && iinToken; ii++){ - DataBuffer tmp; - struct PhraseToken *p = &pPhrase->aToken[ii]; - rc = termSelect(pTab, iCol, p->z, p->n, p->isPrefix, eType, &tmp); - if( rc==SQLITE_OK ){ - if( ii==0 ){ - *pResult = tmp; - }else{ - DataBuffer res = *pResult; - dataBufferInit(pResult, 0); - if( ii==(pPhrase->nToken-1) ){ - eType = eListType; - } - docListPhraseMerge( - res.pData, res.nData, tmp.pData, tmp.nData, 0, 0, eType, pResult - ); - dataBufferDestroy(&res); - dataBufferDestroy(&tmp); - } - } - } - - return rc; -} - -/* -** Evaluate the full-text expression pExpr against fts3 table pTab. Write -** the results into pRes. -*/ -static int evalFts3Expr( - fulltext_vtab *pTab, /* Fts3 Virtual table object */ - Fts3Expr *pExpr, /* Parsed fts3 expression */ - DataBuffer *pRes /* OUT: Write results of the expression here */ -){ - int rc = SQLITE_OK; - - /* Initialize the output buffer. If this is an empty query (pExpr==0), - ** this is all that needs to be done. Empty queries produce empty - ** result sets. - */ - dataBufferInit(pRes, 0); - - if( pExpr ){ - if( pExpr->eType==FTSQUERY_PHRASE ){ - DocListType eType = DL_DOCIDS; - if( pExpr->pParent && pExpr->pParent->eType==FTSQUERY_NEAR ){ - eType = DL_POSITIONS; - } - rc = docListOfPhrase(pTab, pExpr->pPhrase, eType, pRes); - }else{ - DataBuffer lhs; - DataBuffer rhs; - - dataBufferInit(&rhs, 0); - if( SQLITE_OK==(rc = evalFts3Expr(pTab, pExpr->pLeft, &lhs)) - && SQLITE_OK==(rc = evalFts3Expr(pTab, pExpr->pRight, &rhs)) - ){ - switch( pExpr->eType ){ - case FTSQUERY_NEAR: { - int nToken; - Fts3Expr *pLeft; - DocListType eType = DL_DOCIDS; - if( pExpr->pParent && pExpr->pParent->eType==FTSQUERY_NEAR ){ - eType = DL_POSITIONS; - } - pLeft = pExpr->pLeft; - while( pLeft->eType==FTSQUERY_NEAR ){ - pLeft=pLeft->pRight; - } - assert( pExpr->pRight->eType==FTSQUERY_PHRASE ); - assert( pLeft->eType==FTSQUERY_PHRASE ); - nToken = pLeft->pPhrase->nToken + pExpr->pRight->pPhrase->nToken; - docListPhraseMerge(lhs.pData, lhs.nData, rhs.pData, rhs.nData, - pExpr->nNear+1, nToken, eType, pRes - ); - break; - } - case FTSQUERY_NOT: { - docListExceptMerge(lhs.pData, lhs.nData, rhs.pData, rhs.nData,pRes); - break; - } - case FTSQUERY_AND: { - docListAndMerge(lhs.pData, lhs.nData, rhs.pData, rhs.nData, pRes); - break; - } - case FTSQUERY_OR: { - docListOrMerge(lhs.pData, lhs.nData, rhs.pData, rhs.nData, pRes); - break; - } - } - } - dataBufferDestroy(&lhs); - dataBufferDestroy(&rhs); - } - } - - return rc; -} - -/* TODO(shess) Refactor the code to remove this forward decl. */ -static int flushPendingTerms(fulltext_vtab *v); - -/* Perform a full-text query using the search expression in -** zInput[0..nInput-1]. Return a list of matching documents -** in pResult. -** -** Queries must match column iColumn. Or if iColumn>=nColumn -** they are allowed to match against any column. -*/ -static int fulltextQuery( - fulltext_vtab *v, /* The full text index */ - int iColumn, /* Match against this column by default */ - const char *zInput, /* The query string */ - int nInput, /* Number of bytes in zInput[] */ - DataBuffer *pResult, /* Write the result doclist here */ - Fts3Expr **ppExpr /* Put parsed query string here */ -){ - int rc; - - /* TODO(shess) Instead of flushing pendingTerms, we could query for - ** the relevant term and merge the doclist into what we receive from - ** the database. Wait and see if this is a common issue, first. - ** - ** A good reason not to flush is to not generate update-related - ** error codes from here. - */ - - /* Flush any buffered updates before executing the query. */ - rc = flushPendingTerms(v); - if( rc!=SQLITE_OK ){ - return rc; - } - - /* Parse the query passed to the MATCH operator. */ - rc = sqlite3Fts3ExprParse(v->pTokenizer, - v->azColumn, v->nColumn, iColumn, zInput, nInput, ppExpr - ); - if( rc!=SQLITE_OK ){ - assert( 0==(*ppExpr) ); - return rc; - } - - return evalFts3Expr(v, *ppExpr, pResult); -} - -/* -** This is the xFilter interface for the virtual table. See -** the virtual table xFilter method documentation for additional -** information. -** -** If idxNum==QUERY_GENERIC then do a full table scan against -** the %_content table. -** -** If idxNum==QUERY_DOCID then do a docid lookup for a single entry -** in the %_content table. -** -** If idxNum>=QUERY_FULLTEXT then use the full text index. The -** column on the left-hand side of the MATCH operator is column -** number idxNum-QUERY_FULLTEXT, 0 indexed. argv[0] is the right-hand -** side of the MATCH operator. -*/ -/* TODO(shess) Upgrade the cursor initialization and destruction to -** account for fulltextFilter() being called multiple times on the -** same cursor. The current solution is very fragile. Apply fix to -** fts3 as appropriate. -*/ -static int fulltextFilter( - sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */ - int idxNum, const char *idxStr, /* Which indexing scheme to use */ - int argc, sqlite3_value **argv /* Arguments for the indexing scheme */ -){ - fulltext_cursor *c = (fulltext_cursor *) pCursor; - fulltext_vtab *v = cursor_vtab(c); - int rc; - - FTSTRACE(("FTS3 Filter %p\n",pCursor)); - - /* If the cursor has a statement that was not prepared according to - ** idxNum, clear it. I believe all calls to fulltextFilter with a - ** given cursor will have the same idxNum , but in this case it's - ** easy to be safe. - */ - if( c->pStmt && c->iCursorType!=idxNum ){ - sqlite3_finalize(c->pStmt); - c->pStmt = NULL; - } - - /* Get a fresh statement appropriate to idxNum. */ - /* TODO(shess): Add a prepared-statement cache in the vt structure. - ** The cache must handle multiple open cursors. Easier to cache the - ** statement variants at the vt to reduce malloc/realloc/free here. - ** Or we could have a StringBuffer variant which allowed stack - ** construction for small values. - */ - if( !c->pStmt ){ - StringBuffer sb; - initStringBuffer(&sb); - append(&sb, "SELECT docid, "); - appendList(&sb, v->nColumn, v->azContentColumn); - append(&sb, " FROM %_content"); - if( idxNum!=QUERY_GENERIC ) append(&sb, " WHERE docid = ?"); - rc = sql_prepare(v->db, v->zDb, v->zName, &c->pStmt, - stringBufferData(&sb)); - stringBufferDestroy(&sb); - if( rc!=SQLITE_OK ) return rc; - c->iCursorType = idxNum; - }else{ - sqlite3_reset(c->pStmt); - assert( c->iCursorType==idxNum ); - } - - switch( idxNum ){ - case QUERY_GENERIC: - break; - - case QUERY_DOCID: - rc = sqlite3_bind_int64(c->pStmt, 1, sqlite3_value_int64(argv[0])); - if( rc!=SQLITE_OK ) return rc; - break; - - default: /* full-text search */ - { - int iCol = idxNum-QUERY_FULLTEXT; - const char *zQuery = (const char *)sqlite3_value_text(argv[0]); - assert( idxNum<=QUERY_FULLTEXT+v->nColumn); - assert( argc==1 ); - if( c->result.nData!=0 ){ - /* This case happens if the same cursor is used repeatedly. */ - dlrDestroy(&c->reader); - dataBufferReset(&c->result); - }else{ - dataBufferInit(&c->result, 0); - } - rc = fulltextQuery(v, iCol, zQuery, -1, &c->result, &c->pExpr); - if( rc!=SQLITE_OK ) return rc; - if( c->result.nData!=0 ){ - dlrInit(&c->reader, DL_DOCIDS, c->result.pData, c->result.nData); - } - break; - } - } - - return fulltextNext(pCursor); -} - -/* This is the xEof method of the virtual table. The SQLite core -** calls this routine to find out if it has reached the end of -** a query's results set. -*/ -static int fulltextEof(sqlite3_vtab_cursor *pCursor){ - fulltext_cursor *c = (fulltext_cursor *) pCursor; - return c->eof; -} - -/* This is the xColumn method of the virtual table. The SQLite -** core calls this method during a query when it needs the value -** of a column from the virtual table. This method needs to use -** one of the sqlite3_result_*() routines to store the requested -** value back in the pContext. -*/ -static int fulltextColumn(sqlite3_vtab_cursor *pCursor, - sqlite3_context *pContext, int idxCol){ - fulltext_cursor *c = (fulltext_cursor *) pCursor; - fulltext_vtab *v = cursor_vtab(c); - - if( idxColnColumn ){ - sqlite3_value *pVal = sqlite3_column_value(c->pStmt, idxCol+1); - sqlite3_result_value(pContext, pVal); - }else if( idxCol==v->nColumn ){ - /* The extra column whose name is the same as the table. - ** Return a blob which is a pointer to the cursor - */ - sqlite3_result_blob(pContext, &c, sizeof(c), SQLITE_TRANSIENT); - }else if( idxCol==v->nColumn+1 ){ - /* The docid column, which is an alias for rowid. */ - sqlite3_value *pVal = sqlite3_column_value(c->pStmt, 0); - sqlite3_result_value(pContext, pVal); - } - return SQLITE_OK; -} - -/* This is the xRowid method. The SQLite core calls this routine to -** retrieve the rowid for the current row of the result set. fts3 -** exposes %_content.docid as the rowid for the virtual table. The -** rowid should be written to *pRowid. -*/ -static int fulltextRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ - fulltext_cursor *c = (fulltext_cursor *) pCursor; - - *pRowid = sqlite3_column_int64(c->pStmt, 0); - return SQLITE_OK; -} - -/* Add all terms in [zText] to pendingTerms table. If [iColumn] > 0, -** we also store positions and offsets in the hash table using that -** column number. -*/ -static int buildTerms(fulltext_vtab *v, sqlite_int64 iDocid, - const char *zText, int iColumn){ - sqlite3_tokenizer *pTokenizer = v->pTokenizer; - sqlite3_tokenizer_cursor *pCursor; - const char *pToken; - int nTokenBytes; - int iStartOffset, iEndOffset, iPosition; - int rc; - - rc = pTokenizer->pModule->xOpen(pTokenizer, zText, -1, &pCursor); - if( rc!=SQLITE_OK ) return rc; - - pCursor->pTokenizer = pTokenizer; - while( SQLITE_OK==(rc=pTokenizer->pModule->xNext(pCursor, - &pToken, &nTokenBytes, - &iStartOffset, &iEndOffset, - &iPosition)) ){ - DLCollector *p; - int nData; /* Size of doclist before our update. */ - - /* Positions can't be negative; we use -1 as a terminator - * internally. Token can't be NULL or empty. */ - if( iPosition<0 || pToken == NULL || nTokenBytes == 0 ){ - rc = SQLITE_ERROR; - break; - } - - p = fts3HashFind(&v->pendingTerms, pToken, nTokenBytes); - if( p==NULL ){ - nData = 0; - p = dlcNew(iDocid, DL_DEFAULT); - fts3HashInsert(&v->pendingTerms, pToken, nTokenBytes, p); - - /* Overhead for our hash table entry, the key, and the value. */ - v->nPendingData += sizeof(struct fts3HashElem)+sizeof(*p)+nTokenBytes; - }else{ - nData = p->b.nData; - if( p->dlw.iPrevDocid!=iDocid ) dlcNext(p, iDocid); - } - if( iColumn>=0 ){ - dlcAddPos(p, iColumn, iPosition, iStartOffset, iEndOffset); - } - - /* Accumulate data added by dlcNew or dlcNext, and dlcAddPos. */ - v->nPendingData += p->b.nData-nData; - } - - /* TODO(shess) Check return? Should this be able to cause errors at - ** this point? Actually, same question about sqlite3_finalize(), - ** though one could argue that failure there means that the data is - ** not durable. *ponder* - */ - pTokenizer->pModule->xClose(pCursor); - if( SQLITE_DONE == rc ) return SQLITE_OK; - return rc; -} - -/* Add doclists for all terms in [pValues] to pendingTerms table. */ -static int insertTerms(fulltext_vtab *v, sqlite_int64 iDocid, - sqlite3_value **pValues){ - int i; - for(i = 0; i < v->nColumn ; ++i){ - char *zText = (char*)sqlite3_value_text(pValues[i]); - int rc = buildTerms(v, iDocid, zText, i); - if( rc!=SQLITE_OK ) return rc; - } - return SQLITE_OK; -} - -/* Add empty doclists for all terms in the given row's content to -** pendingTerms. -*/ -static int deleteTerms(fulltext_vtab *v, sqlite_int64 iDocid){ - const char **pValues; - int i, rc; - - /* TODO(shess) Should we allow such tables at all? */ - if( DL_DEFAULT==DL_DOCIDS ) return SQLITE_ERROR; - - rc = content_select(v, iDocid, &pValues); - if( rc!=SQLITE_OK ) return rc; - - for(i = 0 ; i < v->nColumn; ++i) { - rc = buildTerms(v, iDocid, pValues[i], -1); - if( rc!=SQLITE_OK ) break; - } - - freeStringArray(v->nColumn, pValues); - return SQLITE_OK; -} - -/* TODO(shess) Refactor the code to remove this forward decl. */ -static int initPendingTerms(fulltext_vtab *v, sqlite_int64 iDocid); - -/* Insert a row into the %_content table; set *piDocid to be the ID of the -** new row. Add doclists for terms to pendingTerms. -*/ -static int index_insert(fulltext_vtab *v, sqlite3_value *pRequestDocid, - sqlite3_value **pValues, sqlite_int64 *piDocid){ - int rc; - - rc = content_insert(v, pRequestDocid, pValues); /* execute an SQL INSERT */ - if( rc!=SQLITE_OK ) return rc; - - /* docid column is an alias for rowid. */ - *piDocid = sqlite3_last_insert_rowid(v->db); - rc = initPendingTerms(v, *piDocid); - if( rc!=SQLITE_OK ) return rc; - - return insertTerms(v, *piDocid, pValues); -} - -/* Delete a row from the %_content table; add empty doclists for terms -** to pendingTerms. -*/ -static int index_delete(fulltext_vtab *v, sqlite_int64 iRow){ - int rc = initPendingTerms(v, iRow); - if( rc!=SQLITE_OK ) return rc; - - rc = deleteTerms(v, iRow); - if( rc!=SQLITE_OK ) return rc; - - return content_delete(v, iRow); /* execute an SQL DELETE */ -} - -/* Update a row in the %_content table; add delete doclists to -** pendingTerms for old terms not in the new data, add insert doclists -** to pendingTerms for terms in the new data. -*/ -static int index_update(fulltext_vtab *v, sqlite_int64 iRow, - sqlite3_value **pValues){ - int rc = initPendingTerms(v, iRow); - if( rc!=SQLITE_OK ) return rc; - - /* Generate an empty doclist for each term that previously appeared in this - * row. */ - rc = deleteTerms(v, iRow); - if( rc!=SQLITE_OK ) return rc; - - rc = content_update(v, pValues, iRow); /* execute an SQL UPDATE */ - if( rc!=SQLITE_OK ) return rc; - - /* Now add positions for terms which appear in the updated row. */ - return insertTerms(v, iRow, pValues); -} - -/*******************************************************************/ -/* InteriorWriter is used to collect terms and block references into -** interior nodes in %_segments. See commentary at top of file for -** format. -*/ - -/* How large interior nodes can grow. */ -#define INTERIOR_MAX 2048 - -/* Minimum number of terms per interior node (except the root). This -** prevents large terms from making the tree too skinny - must be >0 -** so that the tree always makes progress. Note that the min tree -** fanout will be INTERIOR_MIN_TERMS+1. -*/ -#define INTERIOR_MIN_TERMS 7 -#if INTERIOR_MIN_TERMS<1 -# error INTERIOR_MIN_TERMS must be greater than 0. -#endif - -/* ROOT_MAX controls how much data is stored inline in the segment -** directory. -*/ -/* TODO(shess) Push ROOT_MAX down to whoever is writing things. It's -** only here so that interiorWriterRootInfo() and leafWriterRootInfo() -** can both see it, but if the caller passed it in, we wouldn't even -** need a define. -*/ -#define ROOT_MAX 1024 -#if ROOT_MAXterm, 0); - dataBufferReplace(&block->term, pTerm, nTerm); - - n = fts3PutVarint(c, iHeight); - n += fts3PutVarint(c+n, iChildBlock); - dataBufferInit(&block->data, INTERIOR_MAX); - dataBufferReplace(&block->data, c, n); - } - return block; -} - -#ifndef NDEBUG -/* Verify that the data is readable as an interior node. */ -static void interiorBlockValidate(InteriorBlock *pBlock){ - const char *pData = pBlock->data.pData; - int nData = pBlock->data.nData; - int n, iDummy; - sqlite_int64 iBlockid; - - assert( nData>0 ); - assert( pData!=0 ); - assert( pData+nData>pData ); - - /* Must lead with height of node as a varint(n), n>0 */ - n = fts3GetVarint32(pData, &iDummy); - assert( n>0 ); - assert( iDummy>0 ); - assert( n0 ); - assert( n<=nData ); - pData += n; - nData -= n; - - /* Zero or more terms of positive length */ - if( nData!=0 ){ - /* First term is not delta-encoded. */ - n = fts3GetVarint32(pData, &iDummy); - assert( n>0 ); - assert( iDummy>0 ); - assert( n+iDummy>0); - assert( n+iDummy<=nData ); - pData += n+iDummy; - nData -= n+iDummy; - - /* Following terms delta-encoded. */ - while( nData!=0 ){ - /* Length of shared prefix. */ - n = fts3GetVarint32(pData, &iDummy); - assert( n>0 ); - assert( iDummy>=0 ); - assert( n0 ); - assert( iDummy>0 ); - assert( n+iDummy>0); - assert( n+iDummy<=nData ); - pData += n+iDummy; - nData -= n+iDummy; - } - } -} -#define ASSERT_VALID_INTERIOR_BLOCK(x) interiorBlockValidate(x) -#else -#define ASSERT_VALID_INTERIOR_BLOCK(x) assert( 1 ) -#endif - -typedef struct InteriorWriter { - int iHeight; /* from 0 at leaves. */ - InteriorBlock *first, *last; - struct InteriorWriter *parentWriter; - - DataBuffer term; /* Last term written to block "last". */ - sqlite_int64 iOpeningChildBlock; /* First child block in block "last". */ -#ifndef NDEBUG - sqlite_int64 iLastChildBlock; /* for consistency checks. */ -#endif -} InteriorWriter; - -/* Initialize an interior node where pTerm[nTerm] marks the leftmost -** term in the tree. iChildBlock is the leftmost child block at the -** next level down the tree. -*/ -static void interiorWriterInit(int iHeight, const char *pTerm, int nTerm, - sqlite_int64 iChildBlock, - InteriorWriter *pWriter){ - InteriorBlock *block; - assert( iHeight>0 ); - CLEAR(pWriter); - - pWriter->iHeight = iHeight; - pWriter->iOpeningChildBlock = iChildBlock; -#ifndef NDEBUG - pWriter->iLastChildBlock = iChildBlock; -#endif - block = interiorBlockNew(iHeight, iChildBlock, pTerm, nTerm); - pWriter->last = pWriter->first = block; - ASSERT_VALID_INTERIOR_BLOCK(pWriter->last); - dataBufferInit(&pWriter->term, 0); -} - -/* Append the child node rooted at iChildBlock to the interior node, -** with pTerm[nTerm] as the leftmost term in iChildBlock's subtree. -*/ -static void interiorWriterAppend(InteriorWriter *pWriter, - const char *pTerm, int nTerm, - sqlite_int64 iChildBlock){ - char c[VARINT_MAX+VARINT_MAX]; - int n, nPrefix = 0; - - ASSERT_VALID_INTERIOR_BLOCK(pWriter->last); - - /* The first term written into an interior node is actually - ** associated with the second child added (the first child was added - ** in interiorWriterInit, or in the if clause at the bottom of this - ** function). That term gets encoded straight up, with nPrefix left - ** at 0. - */ - if( pWriter->term.nData==0 ){ - n = fts3PutVarint(c, nTerm); - }else{ - while( nPrefixterm.nData && - pTerm[nPrefix]==pWriter->term.pData[nPrefix] ){ - nPrefix++; - } - - n = fts3PutVarint(c, nPrefix); - n += fts3PutVarint(c+n, nTerm-nPrefix); - } - -#ifndef NDEBUG - pWriter->iLastChildBlock++; -#endif - assert( pWriter->iLastChildBlock==iChildBlock ); - - /* Overflow to a new block if the new term makes the current block - ** too big, and the current block already has enough terms. - */ - if( pWriter->last->data.nData+n+nTerm-nPrefix>INTERIOR_MAX && - iChildBlock-pWriter->iOpeningChildBlock>INTERIOR_MIN_TERMS ){ - pWriter->last->next = interiorBlockNew(pWriter->iHeight, iChildBlock, - pTerm, nTerm); - pWriter->last = pWriter->last->next; - pWriter->iOpeningChildBlock = iChildBlock; - dataBufferReset(&pWriter->term); - }else{ - dataBufferAppend2(&pWriter->last->data, c, n, - pTerm+nPrefix, nTerm-nPrefix); - dataBufferReplace(&pWriter->term, pTerm, nTerm); - } - ASSERT_VALID_INTERIOR_BLOCK(pWriter->last); -} - -/* Free the space used by pWriter, including the linked-list of -** InteriorBlocks, and parentWriter, if present. -*/ -static int interiorWriterDestroy(InteriorWriter *pWriter){ - InteriorBlock *block = pWriter->first; - - while( block!=NULL ){ - InteriorBlock *b = block; - block = block->next; - dataBufferDestroy(&b->term); - dataBufferDestroy(&b->data); - sqlite3_free(b); - } - if( pWriter->parentWriter!=NULL ){ - interiorWriterDestroy(pWriter->parentWriter); - sqlite3_free(pWriter->parentWriter); - } - dataBufferDestroy(&pWriter->term); - SCRAMBLE(pWriter); - return SQLITE_OK; -} - -/* If pWriter can fit entirely in ROOT_MAX, return it as the root info -** directly, leaving *piEndBlockid unchanged. Otherwise, flush -** pWriter to %_segments, building a new layer of interior nodes, and -** recursively ask for their root into. -*/ -static int interiorWriterRootInfo(fulltext_vtab *v, InteriorWriter *pWriter, - char **ppRootInfo, int *pnRootInfo, - sqlite_int64 *piEndBlockid){ - InteriorBlock *block = pWriter->first; - sqlite_int64 iBlockid = 0; - int rc; - - /* If we can fit the segment inline */ - if( block==pWriter->last && block->data.nDatadata.pData; - *pnRootInfo = block->data.nData; - return SQLITE_OK; - } - - /* Flush the first block to %_segments, and create a new level of - ** interior node. - */ - ASSERT_VALID_INTERIOR_BLOCK(block); - rc = block_insert(v, block->data.pData, block->data.nData, &iBlockid); - if( rc!=SQLITE_OK ) return rc; - *piEndBlockid = iBlockid; - - pWriter->parentWriter = sqlite3_malloc(sizeof(*pWriter->parentWriter)); - interiorWriterInit(pWriter->iHeight+1, - block->term.pData, block->term.nData, - iBlockid, pWriter->parentWriter); - - /* Flush additional blocks and append to the higher interior - ** node. - */ - for(block=block->next; block!=NULL; block=block->next){ - ASSERT_VALID_INTERIOR_BLOCK(block); - rc = block_insert(v, block->data.pData, block->data.nData, &iBlockid); - if( rc!=SQLITE_OK ) return rc; - *piEndBlockid = iBlockid; - - interiorWriterAppend(pWriter->parentWriter, - block->term.pData, block->term.nData, iBlockid); - } - - /* Parent node gets the chance to be the root. */ - return interiorWriterRootInfo(v, pWriter->parentWriter, - ppRootInfo, pnRootInfo, piEndBlockid); -} - -/****************************************************************/ -/* InteriorReader is used to read off the data from an interior node -** (see comment at top of file for the format). -*/ -typedef struct InteriorReader { - const char *pData; - int nData; - - DataBuffer term; /* previous term, for decoding term delta. */ - - sqlite_int64 iBlockid; -} InteriorReader; - -static void interiorReaderDestroy(InteriorReader *pReader){ - dataBufferDestroy(&pReader->term); - SCRAMBLE(pReader); -} - -/* TODO(shess) The assertions are great, but what if we're in NDEBUG -** and the blob is empty or otherwise contains suspect data? -*/ -static void interiorReaderInit(const char *pData, int nData, - InteriorReader *pReader){ - int n, nTerm; - - /* Require at least the leading flag byte */ - assert( nData>0 ); - assert( pData[0]!='\0' ); - - CLEAR(pReader); - - /* Decode the base blockid, and set the cursor to the first term. */ - n = fts3GetVarint(pData+1, &pReader->iBlockid); - assert( 1+n<=nData ); - pReader->pData = pData+1+n; - pReader->nData = nData-(1+n); - - /* A single-child interior node (such as when a leaf node was too - ** large for the segment directory) won't have any terms. - ** Otherwise, decode the first term. - */ - if( pReader->nData==0 ){ - dataBufferInit(&pReader->term, 0); - }else{ - n = fts3GetVarint32(pReader->pData, &nTerm); - dataBufferInit(&pReader->term, nTerm); - dataBufferReplace(&pReader->term, pReader->pData+n, nTerm); - assert( n+nTerm<=pReader->nData ); - pReader->pData += n+nTerm; - pReader->nData -= n+nTerm; - } -} - -static int interiorReaderAtEnd(InteriorReader *pReader){ - return pReader->term.nData==0; -} - -static sqlite_int64 interiorReaderCurrentBlockid(InteriorReader *pReader){ - return pReader->iBlockid; -} - -static int interiorReaderTermBytes(InteriorReader *pReader){ - assert( !interiorReaderAtEnd(pReader) ); - return pReader->term.nData; -} -static const char *interiorReaderTerm(InteriorReader *pReader){ - assert( !interiorReaderAtEnd(pReader) ); - return pReader->term.pData; -} - -/* Step forward to the next term in the node. */ -static void interiorReaderStep(InteriorReader *pReader){ - assert( !interiorReaderAtEnd(pReader) ); - - /* If the last term has been read, signal eof, else construct the - ** next term. - */ - if( pReader->nData==0 ){ - dataBufferReset(&pReader->term); - }else{ - int n, nPrefix, nSuffix; - - n = fts3GetVarint32(pReader->pData, &nPrefix); - n += fts3GetVarint32(pReader->pData+n, &nSuffix); - - /* Truncate the current term and append suffix data. */ - pReader->term.nData = nPrefix; - dataBufferAppend(&pReader->term, pReader->pData+n, nSuffix); - - assert( n+nSuffix<=pReader->nData ); - pReader->pData += n+nSuffix; - pReader->nData -= n+nSuffix; - } - pReader->iBlockid++; -} - -/* Compare the current term to pTerm[nTerm], returning strcmp-style -** results. If isPrefix, equality means equal through nTerm bytes. -*/ -static int interiorReaderTermCmp(InteriorReader *pReader, - const char *pTerm, int nTerm, int isPrefix){ - const char *pReaderTerm = interiorReaderTerm(pReader); - int nReaderTerm = interiorReaderTermBytes(pReader); - int c, n = nReaderTerm0 ) return -1; - if( nTerm>0 ) return 1; - return 0; - } - - c = memcmp(pReaderTerm, pTerm, n); - if( c!=0 ) return c; - if( isPrefix && n==nTerm ) return 0; - return nReaderTerm - nTerm; -} - -/****************************************************************/ -/* LeafWriter is used to collect terms and associated doclist data -** into leaf blocks in %_segments (see top of file for format info). -** Expected usage is: -** -** LeafWriter writer; -** leafWriterInit(0, 0, &writer); -** while( sorted_terms_left_to_process ){ -** // data is doclist data for that term. -** rc = leafWriterStep(v, &writer, pTerm, nTerm, pData, nData); -** if( rc!=SQLITE_OK ) goto err; -** } -** rc = leafWriterFinalize(v, &writer); -**err: -** leafWriterDestroy(&writer); -** return rc; -** -** leafWriterStep() may write a collected leaf out to %_segments. -** leafWriterFinalize() finishes writing any buffered data and stores -** a root node in %_segdir. leafWriterDestroy() frees all buffers and -** InteriorWriters allocated as part of writing this segment. -** -** TODO(shess) Document leafWriterStepMerge(). -*/ - -/* Put terms with data this big in their own block. */ -#define STANDALONE_MIN 1024 - -/* Keep leaf blocks below this size. */ -#define LEAF_MAX 2048 - -typedef struct LeafWriter { - int iLevel; - int idx; - sqlite_int64 iStartBlockid; /* needed to create the root info */ - sqlite_int64 iEndBlockid; /* when we're done writing. */ - - DataBuffer term; /* previous encoded term */ - DataBuffer data; /* encoding buffer */ - - /* bytes of first term in the current node which distinguishes that - ** term from the last term of the previous node. - */ - int nTermDistinct; - - InteriorWriter parentWriter; /* if we overflow */ - int has_parent; -} LeafWriter; - -static void leafWriterInit(int iLevel, int idx, LeafWriter *pWriter){ - CLEAR(pWriter); - pWriter->iLevel = iLevel; - pWriter->idx = idx; - - dataBufferInit(&pWriter->term, 32); - - /* Start out with a reasonably sized block, though it can grow. */ - dataBufferInit(&pWriter->data, LEAF_MAX); -} - -#ifndef NDEBUG -/* Verify that the data is readable as a leaf node. */ -static void leafNodeValidate(const char *pData, int nData){ - int n, iDummy; - - if( nData==0 ) return; - assert( nData>0 ); - assert( pData!=0 ); - assert( pData+nData>pData ); - - /* Must lead with a varint(0) */ - n = fts3GetVarint32(pData, &iDummy); - assert( iDummy==0 ); - assert( n>0 ); - assert( n0 ); - assert( iDummy>0 ); - assert( n+iDummy>0 ); - assert( n+iDummy0 ); - assert( iDummy>0 ); - assert( n+iDummy>0 ); - assert( n+iDummy<=nData ); - ASSERT_VALID_DOCLIST(DL_DEFAULT, pData+n, iDummy, NULL); - pData += n+iDummy; - nData -= n+iDummy; - - /* Verify that trailing terms and doclists also are readable. */ - while( nData!=0 ){ - n = fts3GetVarint32(pData, &iDummy); - assert( n>0 ); - assert( iDummy>=0 ); - assert( n0 ); - assert( iDummy>0 ); - assert( n+iDummy>0 ); - assert( n+iDummy0 ); - assert( iDummy>0 ); - assert( n+iDummy>0 ); - assert( n+iDummy<=nData ); - ASSERT_VALID_DOCLIST(DL_DEFAULT, pData+n, iDummy, NULL); - pData += n+iDummy; - nData -= n+iDummy; - } -} -#define ASSERT_VALID_LEAF_NODE(p, n) leafNodeValidate(p, n) -#else -#define ASSERT_VALID_LEAF_NODE(p, n) assert( 1 ) -#endif - -/* Flush the current leaf node to %_segments, and adding the resulting -** blockid and the starting term to the interior node which will -** contain it. -*/ -static int leafWriterInternalFlush(fulltext_vtab *v, LeafWriter *pWriter, - int iData, int nData){ - sqlite_int64 iBlockid = 0; - const char *pStartingTerm; - int nStartingTerm, rc, n; - - /* Must have the leading varint(0) flag, plus at least some - ** valid-looking data. - */ - assert( nData>2 ); - assert( iData>=0 ); - assert( iData+nData<=pWriter->data.nData ); - ASSERT_VALID_LEAF_NODE(pWriter->data.pData+iData, nData); - - rc = block_insert(v, pWriter->data.pData+iData, nData, &iBlockid); - if( rc!=SQLITE_OK ) return rc; - assert( iBlockid!=0 ); - - /* Reconstruct the first term in the leaf for purposes of building - ** the interior node. - */ - n = fts3GetVarint32(pWriter->data.pData+iData+1, &nStartingTerm); - pStartingTerm = pWriter->data.pData+iData+1+n; - assert( pWriter->data.nData>iData+1+n+nStartingTerm ); - assert( pWriter->nTermDistinct>0 ); - assert( pWriter->nTermDistinct<=nStartingTerm ); - nStartingTerm = pWriter->nTermDistinct; - - if( pWriter->has_parent ){ - interiorWriterAppend(&pWriter->parentWriter, - pStartingTerm, nStartingTerm, iBlockid); - }else{ - interiorWriterInit(1, pStartingTerm, nStartingTerm, iBlockid, - &pWriter->parentWriter); - pWriter->has_parent = 1; - } - - /* Track the span of this segment's leaf nodes. */ - if( pWriter->iEndBlockid==0 ){ - pWriter->iEndBlockid = pWriter->iStartBlockid = iBlockid; - }else{ - pWriter->iEndBlockid++; - assert( iBlockid==pWriter->iEndBlockid ); - } - - return SQLITE_OK; -} -static int leafWriterFlush(fulltext_vtab *v, LeafWriter *pWriter){ - int rc = leafWriterInternalFlush(v, pWriter, 0, pWriter->data.nData); - if( rc!=SQLITE_OK ) return rc; - - /* Re-initialize the output buffer. */ - dataBufferReset(&pWriter->data); - - return SQLITE_OK; -} - -/* Fetch the root info for the segment. If the entire leaf fits -** within ROOT_MAX, then it will be returned directly, otherwise it -** will be flushed and the root info will be returned from the -** interior node. *piEndBlockid is set to the blockid of the last -** interior or leaf node written to disk (0 if none are written at -** all). -*/ -static int leafWriterRootInfo(fulltext_vtab *v, LeafWriter *pWriter, - char **ppRootInfo, int *pnRootInfo, - sqlite_int64 *piEndBlockid){ - /* we can fit the segment entirely inline */ - if( !pWriter->has_parent && pWriter->data.nDatadata.pData; - *pnRootInfo = pWriter->data.nData; - *piEndBlockid = 0; - return SQLITE_OK; - } - - /* Flush remaining leaf data. */ - if( pWriter->data.nData>0 ){ - int rc = leafWriterFlush(v, pWriter); - if( rc!=SQLITE_OK ) return rc; - } - - /* We must have flushed a leaf at some point. */ - assert( pWriter->has_parent ); - - /* Tenatively set the end leaf blockid as the end blockid. If the - ** interior node can be returned inline, this will be the final - ** blockid, otherwise it will be overwritten by - ** interiorWriterRootInfo(). - */ - *piEndBlockid = pWriter->iEndBlockid; - - return interiorWriterRootInfo(v, &pWriter->parentWriter, - ppRootInfo, pnRootInfo, piEndBlockid); -} - -/* Collect the rootInfo data and store it into the segment directory. -** This has the effect of flushing the segment's leaf data to -** %_segments, and also flushing any interior nodes to %_segments. -*/ -static int leafWriterFinalize(fulltext_vtab *v, LeafWriter *pWriter){ - sqlite_int64 iEndBlockid; - char *pRootInfo; - int rc, nRootInfo; - - rc = leafWriterRootInfo(v, pWriter, &pRootInfo, &nRootInfo, &iEndBlockid); - if( rc!=SQLITE_OK ) return rc; - - /* Don't bother storing an entirely empty segment. */ - if( iEndBlockid==0 && nRootInfo==0 ) return SQLITE_OK; - - return segdir_set(v, pWriter->iLevel, pWriter->idx, - pWriter->iStartBlockid, pWriter->iEndBlockid, - iEndBlockid, pRootInfo, nRootInfo); -} - -static void leafWriterDestroy(LeafWriter *pWriter){ - if( pWriter->has_parent ) interiorWriterDestroy(&pWriter->parentWriter); - dataBufferDestroy(&pWriter->term); - dataBufferDestroy(&pWriter->data); -} - -/* Encode a term into the leafWriter, delta-encoding as appropriate. -** Returns the length of the new term which distinguishes it from the -** previous term, which can be used to set nTermDistinct when a node -** boundary is crossed. -*/ -static int leafWriterEncodeTerm(LeafWriter *pWriter, - const char *pTerm, int nTerm){ - char c[VARINT_MAX+VARINT_MAX]; - int n, nPrefix = 0; - - assert( nTerm>0 ); - while( nPrefixterm.nData && - pTerm[nPrefix]==pWriter->term.pData[nPrefix] ){ - nPrefix++; - /* Failing this implies that the terms weren't in order. */ - assert( nPrefixdata.nData==0 ){ - /* Encode the node header and leading term as: - ** varint(0) - ** varint(nTerm) - ** char pTerm[nTerm] - */ - n = fts3PutVarint(c, '\0'); - n += fts3PutVarint(c+n, nTerm); - dataBufferAppend2(&pWriter->data, c, n, pTerm, nTerm); - }else{ - /* Delta-encode the term as: - ** varint(nPrefix) - ** varint(nSuffix) - ** char pTermSuffix[nSuffix] - */ - n = fts3PutVarint(c, nPrefix); - n += fts3PutVarint(c+n, nTerm-nPrefix); - dataBufferAppend2(&pWriter->data, c, n, pTerm+nPrefix, nTerm-nPrefix); - } - dataBufferReplace(&pWriter->term, pTerm, nTerm); - - return nPrefix+1; -} - -/* Used to avoid a memmove when a large amount of doclist data is in -** the buffer. This constructs a node and term header before -** iDoclistData and flushes the resulting complete node using -** leafWriterInternalFlush(). -*/ -static int leafWriterInlineFlush(fulltext_vtab *v, LeafWriter *pWriter, - const char *pTerm, int nTerm, - int iDoclistData){ - char c[VARINT_MAX+VARINT_MAX]; - int iData, n = fts3PutVarint(c, 0); - n += fts3PutVarint(c+n, nTerm); - - /* There should always be room for the header. Even if pTerm shared - ** a substantial prefix with the previous term, the entire prefix - ** could be constructed from earlier data in the doclist, so there - ** should be room. - */ - assert( iDoclistData>=n+nTerm ); - - iData = iDoclistData-(n+nTerm); - memcpy(pWriter->data.pData+iData, c, n); - memcpy(pWriter->data.pData+iData+n, pTerm, nTerm); - - return leafWriterInternalFlush(v, pWriter, iData, pWriter->data.nData-iData); -} - -/* Push pTerm[nTerm] along with the doclist data to the leaf layer of -** %_segments. -*/ -static int leafWriterStepMerge(fulltext_vtab *v, LeafWriter *pWriter, - const char *pTerm, int nTerm, - DLReader *pReaders, int nReaders){ - char c[VARINT_MAX+VARINT_MAX]; - int iTermData = pWriter->data.nData, iDoclistData; - int i, nData, n, nActualData, nActual, rc, nTermDistinct; - - ASSERT_VALID_LEAF_NODE(pWriter->data.pData, pWriter->data.nData); - nTermDistinct = leafWriterEncodeTerm(pWriter, pTerm, nTerm); - - /* Remember nTermDistinct if opening a new node. */ - if( iTermData==0 ) pWriter->nTermDistinct = nTermDistinct; - - iDoclistData = pWriter->data.nData; - - /* Estimate the length of the merged doclist so we can leave space - ** to encode it. - */ - for(i=0, nData=0; idata, c, n); - - docListMerge(&pWriter->data, pReaders, nReaders); - ASSERT_VALID_DOCLIST(DL_DEFAULT, - pWriter->data.pData+iDoclistData+n, - pWriter->data.nData-iDoclistData-n, NULL); - - /* The actual amount of doclist data at this point could be smaller - ** than the length we encoded. Additionally, the space required to - ** encode this length could be smaller. For small doclists, this is - ** not a big deal, we can just use memmove() to adjust things. - */ - nActualData = pWriter->data.nData-(iDoclistData+n); - nActual = fts3PutVarint(c, nActualData); - assert( nActualData<=nData ); - assert( nActual<=n ); - - /* If the new doclist is big enough for force a standalone leaf - ** node, we can immediately flush it inline without doing the - ** memmove(). - */ - /* TODO(shess) This test matches leafWriterStep(), which does this - ** test before it knows the cost to varint-encode the term and - ** doclist lengths. At some point, change to - ** pWriter->data.nData-iTermData>STANDALONE_MIN. - */ - if( nTerm+nActualData>STANDALONE_MIN ){ - /* Push leaf node from before this term. */ - if( iTermData>0 ){ - rc = leafWriterInternalFlush(v, pWriter, 0, iTermData); - if( rc!=SQLITE_OK ) return rc; - - pWriter->nTermDistinct = nTermDistinct; - } - - /* Fix the encoded doclist length. */ - iDoclistData += n - nActual; - memcpy(pWriter->data.pData+iDoclistData, c, nActual); - - /* Push the standalone leaf node. */ - rc = leafWriterInlineFlush(v, pWriter, pTerm, nTerm, iDoclistData); - if( rc!=SQLITE_OK ) return rc; - - /* Leave the node empty. */ - dataBufferReset(&pWriter->data); - - return rc; - } - - /* At this point, we know that the doclist was small, so do the - ** memmove if indicated. - */ - if( nActualdata.pData+iDoclistData+nActual, - pWriter->data.pData+iDoclistData+n, - pWriter->data.nData-(iDoclistData+n)); - pWriter->data.nData -= n-nActual; - } - - /* Replace written length with actual length. */ - memcpy(pWriter->data.pData+iDoclistData, c, nActual); - - /* If the node is too large, break things up. */ - /* TODO(shess) This test matches leafWriterStep(), which does this - ** test before it knows the cost to varint-encode the term and - ** doclist lengths. At some point, change to - ** pWriter->data.nData>LEAF_MAX. - */ - if( iTermData+nTerm+nActualData>LEAF_MAX ){ - /* Flush out the leading data as a node */ - rc = leafWriterInternalFlush(v, pWriter, 0, iTermData); - if( rc!=SQLITE_OK ) return rc; - - pWriter->nTermDistinct = nTermDistinct; - - /* Rebuild header using the current term */ - n = fts3PutVarint(pWriter->data.pData, 0); - n += fts3PutVarint(pWriter->data.pData+n, nTerm); - memcpy(pWriter->data.pData+n, pTerm, nTerm); - n += nTerm; - - /* There should always be room, because the previous encoding - ** included all data necessary to construct the term. - */ - assert( ndata.nData-iDoclistDatadata.pData+n, - pWriter->data.pData+iDoclistData, - pWriter->data.nData-iDoclistData); - pWriter->data.nData -= iDoclistData-n; - } - ASSERT_VALID_LEAF_NODE(pWriter->data.pData, pWriter->data.nData); - - return SQLITE_OK; -} - -/* Push pTerm[nTerm] along with the doclist data to the leaf layer of -** %_segments. -*/ -/* TODO(shess) Revise writeZeroSegment() so that doclists are -** constructed directly in pWriter->data. -*/ -static int leafWriterStep(fulltext_vtab *v, LeafWriter *pWriter, - const char *pTerm, int nTerm, - const char *pData, int nData){ - int rc; - DLReader reader; - - dlrInit(&reader, DL_DEFAULT, pData, nData); - rc = leafWriterStepMerge(v, pWriter, pTerm, nTerm, &reader, 1); - dlrDestroy(&reader); - - return rc; -} - - -/****************************************************************/ -/* LeafReader is used to iterate over an individual leaf node. */ -typedef struct LeafReader { - DataBuffer term; /* copy of current term. */ - - const char *pData; /* data for current term. */ - int nData; -} LeafReader; - -static void leafReaderDestroy(LeafReader *pReader){ - dataBufferDestroy(&pReader->term); - SCRAMBLE(pReader); -} - -static int leafReaderAtEnd(LeafReader *pReader){ - return pReader->nData<=0; -} - -/* Access the current term. */ -static int leafReaderTermBytes(LeafReader *pReader){ - return pReader->term.nData; -} -static const char *leafReaderTerm(LeafReader *pReader){ - assert( pReader->term.nData>0 ); - return pReader->term.pData; -} - -/* Access the doclist data for the current term. */ -static int leafReaderDataBytes(LeafReader *pReader){ - int nData; - assert( pReader->term.nData>0 ); - fts3GetVarint32(pReader->pData, &nData); - return nData; -} -static const char *leafReaderData(LeafReader *pReader){ - int n, nData; - assert( pReader->term.nData>0 ); - n = fts3GetVarint32(pReader->pData, &nData); - return pReader->pData+n; -} - -static void leafReaderInit(const char *pData, int nData, - LeafReader *pReader){ - int nTerm, n; - - assert( nData>0 ); - assert( pData[0]=='\0' ); - - CLEAR(pReader); - - /* Read the first term, skipping the header byte. */ - n = fts3GetVarint32(pData+1, &nTerm); - dataBufferInit(&pReader->term, nTerm); - dataBufferReplace(&pReader->term, pData+1+n, nTerm); - - /* Position after the first term. */ - assert( 1+n+nTermpData = pData+1+n+nTerm; - pReader->nData = nData-1-n-nTerm; -} - -/* Step the reader forward to the next term. */ -static void leafReaderStep(LeafReader *pReader){ - int n, nData, nPrefix, nSuffix; - assert( !leafReaderAtEnd(pReader) ); - - /* Skip previous entry's data block. */ - n = fts3GetVarint32(pReader->pData, &nData); - assert( n+nData<=pReader->nData ); - pReader->pData += n+nData; - pReader->nData -= n+nData; - - if( !leafReaderAtEnd(pReader) ){ - /* Construct the new term using a prefix from the old term plus a - ** suffix from the leaf data. - */ - n = fts3GetVarint32(pReader->pData, &nPrefix); - n += fts3GetVarint32(pReader->pData+n, &nSuffix); - assert( n+nSuffixnData ); - pReader->term.nData = nPrefix; - dataBufferAppend(&pReader->term, pReader->pData+n, nSuffix); - - pReader->pData += n+nSuffix; - pReader->nData -= n+nSuffix; - } -} - -/* strcmp-style comparison of pReader's current term against pTerm. -** If isPrefix, equality means equal through nTerm bytes. -*/ -static int leafReaderTermCmp(LeafReader *pReader, - const char *pTerm, int nTerm, int isPrefix){ - int c, n = pReader->term.nDataterm.nData : nTerm; - if( n==0 ){ - if( pReader->term.nData>0 ) return -1; - if(nTerm>0 ) return 1; - return 0; - } - - c = memcmp(pReader->term.pData, pTerm, n); - if( c!=0 ) return c; - if( isPrefix && n==nTerm ) return 0; - return pReader->term.nData - nTerm; -} - - -/****************************************************************/ -/* LeavesReader wraps LeafReader to allow iterating over the entire -** leaf layer of the tree. -*/ -typedef struct LeavesReader { - int idx; /* Index within the segment. */ - - sqlite3_stmt *pStmt; /* Statement we're streaming leaves from. */ - int eof; /* we've seen SQLITE_DONE from pStmt. */ - - LeafReader leafReader; /* reader for the current leaf. */ - DataBuffer rootData; /* root data for inline. */ -} LeavesReader; - -/* Access the current term. */ -static int leavesReaderTermBytes(LeavesReader *pReader){ - assert( !pReader->eof ); - return leafReaderTermBytes(&pReader->leafReader); -} -static const char *leavesReaderTerm(LeavesReader *pReader){ - assert( !pReader->eof ); - return leafReaderTerm(&pReader->leafReader); -} - -/* Access the doclist data for the current term. */ -static int leavesReaderDataBytes(LeavesReader *pReader){ - assert( !pReader->eof ); - return leafReaderDataBytes(&pReader->leafReader); -} -static const char *leavesReaderData(LeavesReader *pReader){ - assert( !pReader->eof ); - return leafReaderData(&pReader->leafReader); -} - -static int leavesReaderAtEnd(LeavesReader *pReader){ - return pReader->eof; -} - -/* loadSegmentLeaves() may not read all the way to SQLITE_DONE, thus -** leaving the statement handle open, which locks the table. -*/ -/* TODO(shess) This "solution" is not satisfactory. Really, there -** should be check-in function for all statement handles which -** arranges to call sqlite3_reset(). This most likely will require -** modification to control flow all over the place, though, so for now -** just punt. -** -** Note the the current system assumes that segment merges will run to -** completion, which is why this particular probably hasn't arisen in -** this case. Probably a brittle assumption. -*/ -static int leavesReaderReset(LeavesReader *pReader){ - return sqlite3_reset(pReader->pStmt); -} - -static void leavesReaderDestroy(LeavesReader *pReader){ - /* If idx is -1, that means we're using a non-cached statement - ** handle in the optimize() case, so we need to release it. - */ - if( pReader->pStmt!=NULL && pReader->idx==-1 ){ - sqlite3_finalize(pReader->pStmt); - } - leafReaderDestroy(&pReader->leafReader); - dataBufferDestroy(&pReader->rootData); - SCRAMBLE(pReader); -} - -/* Initialize pReader with the given root data (if iStartBlockid==0 -** the leaf data was entirely contained in the root), or from the -** stream of blocks between iStartBlockid and iEndBlockid, inclusive. -*/ -static int leavesReaderInit(fulltext_vtab *v, - int idx, - sqlite_int64 iStartBlockid, - sqlite_int64 iEndBlockid, - const char *pRootData, int nRootData, - LeavesReader *pReader){ - CLEAR(pReader); - pReader->idx = idx; - - dataBufferInit(&pReader->rootData, 0); - if( iStartBlockid==0 ){ - /* Entire leaf level fit in root data. */ - dataBufferReplace(&pReader->rootData, pRootData, nRootData); - leafReaderInit(pReader->rootData.pData, pReader->rootData.nData, - &pReader->leafReader); - }else{ - sqlite3_stmt *s; - int rc = sql_get_leaf_statement(v, idx, &s); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int64(s, 1, iStartBlockid); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int64(s, 2, iEndBlockid); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_step(s); - if( rc==SQLITE_DONE ){ - pReader->eof = 1; - return SQLITE_OK; - } - if( rc!=SQLITE_ROW ) return rc; - - pReader->pStmt = s; - leafReaderInit(sqlite3_column_blob(pReader->pStmt, 0), - sqlite3_column_bytes(pReader->pStmt, 0), - &pReader->leafReader); - } - return SQLITE_OK; -} - -/* Step the current leaf forward to the next term. If we reach the -** end of the current leaf, step forward to the next leaf block. -*/ -static int leavesReaderStep(fulltext_vtab *v, LeavesReader *pReader){ - assert( !leavesReaderAtEnd(pReader) ); - leafReaderStep(&pReader->leafReader); - - if( leafReaderAtEnd(&pReader->leafReader) ){ - int rc; - if( pReader->rootData.pData ){ - pReader->eof = 1; - return SQLITE_OK; - } - rc = sqlite3_step(pReader->pStmt); - if( rc!=SQLITE_ROW ){ - pReader->eof = 1; - return rc==SQLITE_DONE ? SQLITE_OK : rc; - } - leafReaderDestroy(&pReader->leafReader); - leafReaderInit(sqlite3_column_blob(pReader->pStmt, 0), - sqlite3_column_bytes(pReader->pStmt, 0), - &pReader->leafReader); - } - return SQLITE_OK; -} - -/* Order LeavesReaders by their term, ignoring idx. Readers at eof -** always sort to the end. -*/ -static int leavesReaderTermCmp(LeavesReader *lr1, LeavesReader *lr2){ - if( leavesReaderAtEnd(lr1) ){ - if( leavesReaderAtEnd(lr2) ) return 0; - return 1; - } - if( leavesReaderAtEnd(lr2) ) return -1; - - return leafReaderTermCmp(&lr1->leafReader, - leavesReaderTerm(lr2), leavesReaderTermBytes(lr2), - 0); -} - -/* Similar to leavesReaderTermCmp(), with additional ordering by idx -** so that older segments sort before newer segments. -*/ -static int leavesReaderCmp(LeavesReader *lr1, LeavesReader *lr2){ - int c = leavesReaderTermCmp(lr1, lr2); - if( c!=0 ) return c; - return lr1->idx-lr2->idx; -} - -/* Assume that pLr[1]..pLr[nLr] are sorted. Bubble pLr[0] into its -** sorted position. -*/ -static void leavesReaderReorder(LeavesReader *pLr, int nLr){ - while( nLr>1 && leavesReaderCmp(pLr, pLr+1)>0 ){ - LeavesReader tmp = pLr[0]; - pLr[0] = pLr[1]; - pLr[1] = tmp; - nLr--; - pLr++; - } -} - -/* Initializes pReaders with the segments from level iLevel, returning -** the number of segments in *piReaders. Leaves pReaders in sorted -** order. -*/ -static int leavesReadersInit(fulltext_vtab *v, int iLevel, - LeavesReader *pReaders, int *piReaders){ - sqlite3_stmt *s; - int i, rc = sql_get_statement(v, SEGDIR_SELECT_LEVEL_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int(s, 1, iLevel); - if( rc!=SQLITE_OK ) return rc; - - i = 0; - while( (rc = sqlite3_step(s))==SQLITE_ROW ){ - sqlite_int64 iStart = sqlite3_column_int64(s, 0); - sqlite_int64 iEnd = sqlite3_column_int64(s, 1); - const char *pRootData = sqlite3_column_blob(s, 2); - int nRootData = sqlite3_column_bytes(s, 2); - - assert( i0 ){ - leavesReaderDestroy(&pReaders[i]); - } - return rc; - } - - *piReaders = i; - - /* Leave our results sorted by term, then age. */ - while( i-- ){ - leavesReaderReorder(pReaders+i, *piReaders-i); - } - return SQLITE_OK; -} - -/* Merge doclists from pReaders[nReaders] into a single doclist, which -** is written to pWriter. Assumes pReaders is ordered oldest to -** newest. -*/ -/* TODO(shess) Consider putting this inline in segmentMerge(). */ -static int leavesReadersMerge(fulltext_vtab *v, - LeavesReader *pReaders, int nReaders, - LeafWriter *pWriter){ - DLReader dlReaders[MERGE_COUNT]; - const char *pTerm = leavesReaderTerm(pReaders); - int i, nTerm = leavesReaderTermBytes(pReaders); - - assert( nReaders<=MERGE_COUNT ); - - for(i=0; i0 ){ - rc = leavesReaderStep(v, lrs+i); - if( rc!=SQLITE_OK ) goto err; - - /* Reorder by term, then by age. */ - leavesReaderReorder(lrs+i, MERGE_COUNT-i); - } - } - - for(i=0; i0 ); - - for(rc=SQLITE_OK; rc==SQLITE_OK && !leavesReaderAtEnd(pReader); - rc=leavesReaderStep(v, pReader)){ - /* TODO(shess) Really want leavesReaderTermCmp(), but that name is - ** already taken to compare the terms of two LeavesReaders. Think - ** on a better name. [Meanwhile, break encapsulation rather than - ** use a confusing name.] - */ - int c = leafReaderTermCmp(&pReader->leafReader, pTerm, nTerm, isPrefix); - if( c>0 ) break; /* Past any possible matches. */ - if( c==0 ){ - const char *pData = leavesReaderData(pReader); - int iBuffer, nData = leavesReaderDataBytes(pReader); - - /* Find the first empty buffer. */ - for(iBuffer=0; iBuffer0 ){ - assert(pBuffers!=NULL); - memcpy(p, pBuffers, nBuffers*sizeof(*pBuffers)); - sqlite3_free(pBuffers); - } - pBuffers = p; - } - dataBufferInit(&(pBuffers[nBuffers]), 0); - nBuffers++; - } - - /* At this point, must have an empty at iBuffer. */ - assert(iBufferpData, p->nData); - - /* dataBufferReset() could allow a large doclist to blow up - ** our memory requirements. - */ - if( p->nCapacity<1024 ){ - dataBufferReset(p); - }else{ - dataBufferDestroy(p); - dataBufferInit(p, 0); - } - } - } - } - } - - /* Union all the doclists together into *out. */ - /* TODO(shess) What if *out is big? Sigh. */ - if( rc==SQLITE_OK && nBuffers>0 ){ - int iBuffer; - for(iBuffer=0; iBuffer0 ){ - if( out->nData==0 ){ - dataBufferSwap(out, &(pBuffers[iBuffer])); - }else{ - docListAccumulateUnion(out, pBuffers[iBuffer].pData, - pBuffers[iBuffer].nData); - } - } - } - } - - while( nBuffers-- ){ - dataBufferDestroy(&(pBuffers[nBuffers])); - } - if( pBuffers!=NULL ) sqlite3_free(pBuffers); - - return rc; -} - -/* Call loadSegmentLeavesInt() with pData/nData as input. */ -static int loadSegmentLeaf(fulltext_vtab *v, const char *pData, int nData, - const char *pTerm, int nTerm, int isPrefix, - DataBuffer *out){ - LeavesReader reader; - int rc; - - assert( nData>1 ); - assert( *pData=='\0' ); - rc = leavesReaderInit(v, 0, 0, 0, pData, nData, &reader); - if( rc!=SQLITE_OK ) return rc; - - rc = loadSegmentLeavesInt(v, &reader, pTerm, nTerm, isPrefix, out); - leavesReaderReset(&reader); - leavesReaderDestroy(&reader); - return rc; -} - -/* Call loadSegmentLeavesInt() with the leaf nodes from iStartLeaf to -** iEndLeaf (inclusive) as input, and merge the resulting doclist into -** out. -*/ -static int loadSegmentLeaves(fulltext_vtab *v, - sqlite_int64 iStartLeaf, sqlite_int64 iEndLeaf, - const char *pTerm, int nTerm, int isPrefix, - DataBuffer *out){ - int rc; - LeavesReader reader; - - assert( iStartLeaf<=iEndLeaf ); - rc = leavesReaderInit(v, 0, iStartLeaf, iEndLeaf, NULL, 0, &reader); - if( rc!=SQLITE_OK ) return rc; - - rc = loadSegmentLeavesInt(v, &reader, pTerm, nTerm, isPrefix, out); - leavesReaderReset(&reader); - leavesReaderDestroy(&reader); - return rc; -} - -/* Taking pData/nData as an interior node, find the sequence of child -** nodes which could include pTerm/nTerm/isPrefix. Note that the -** interior node terms logically come between the blocks, so there is -** one more blockid than there are terms (that block contains terms >= -** the last interior-node term). -*/ -/* TODO(shess) The calling code may already know that the end child is -** not worth calculating, because the end may be in a later sibling -** node. Consider whether breaking symmetry is worthwhile. I suspect -** it is not worthwhile. -*/ -static void getChildrenContaining(const char *pData, int nData, - const char *pTerm, int nTerm, int isPrefix, - sqlite_int64 *piStartChild, - sqlite_int64 *piEndChild){ - InteriorReader reader; - - assert( nData>1 ); - assert( *pData!='\0' ); - interiorReaderInit(pData, nData, &reader); - - /* Scan for the first child which could contain pTerm/nTerm. */ - while( !interiorReaderAtEnd(&reader) ){ - if( interiorReaderTermCmp(&reader, pTerm, nTerm, 0)>0 ) break; - interiorReaderStep(&reader); - } - *piStartChild = interiorReaderCurrentBlockid(&reader); - - /* Keep scanning to find a term greater than our term, using prefix - ** comparison if indicated. If isPrefix is false, this will be the - ** same blockid as the starting block. - */ - while( !interiorReaderAtEnd(&reader) ){ - if( interiorReaderTermCmp(&reader, pTerm, nTerm, isPrefix)>0 ) break; - interiorReaderStep(&reader); - } - *piEndChild = interiorReaderCurrentBlockid(&reader); - - interiorReaderDestroy(&reader); - - /* Children must ascend, and if !prefix, both must be the same. */ - assert( *piEndChild>=*piStartChild ); - assert( isPrefix || *piStartChild==*piEndChild ); -} - -/* Read block at iBlockid and pass it with other params to -** getChildrenContaining(). -*/ -static int loadAndGetChildrenContaining( - fulltext_vtab *v, - sqlite_int64 iBlockid, - const char *pTerm, int nTerm, int isPrefix, - sqlite_int64 *piStartChild, sqlite_int64 *piEndChild -){ - sqlite3_stmt *s = NULL; - int rc; - - assert( iBlockid!=0 ); - assert( pTerm!=NULL ); - assert( nTerm!=0 ); /* TODO(shess) Why not allow this? */ - assert( piStartChild!=NULL ); - assert( piEndChild!=NULL ); - - rc = sql_get_statement(v, BLOCK_SELECT_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int64(s, 1, iBlockid); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_step(s); - if( rc==SQLITE_DONE ) return SQLITE_ERROR; - if( rc!=SQLITE_ROW ) return rc; - - getChildrenContaining(sqlite3_column_blob(s, 0), sqlite3_column_bytes(s, 0), - pTerm, nTerm, isPrefix, piStartChild, piEndChild); - - /* We expect only one row. We must execute another sqlite3_step() - * to complete the iteration; otherwise the table will remain - * locked. */ - rc = sqlite3_step(s); - if( rc==SQLITE_ROW ) return SQLITE_ERROR; - if( rc!=SQLITE_DONE ) return rc; - - return SQLITE_OK; -} - -/* Traverse the tree represented by pData[nData] looking for -** pTerm[nTerm], placing its doclist into *out. This is internal to -** loadSegment() to make error-handling cleaner. -*/ -static int loadSegmentInt(fulltext_vtab *v, const char *pData, int nData, - sqlite_int64 iLeavesEnd, - const char *pTerm, int nTerm, int isPrefix, - DataBuffer *out){ - /* Special case where root is a leaf. */ - if( *pData=='\0' ){ - return loadSegmentLeaf(v, pData, nData, pTerm, nTerm, isPrefix, out); - }else{ - int rc; - sqlite_int64 iStartChild, iEndChild; - - /* Process pData as an interior node, then loop down the tree - ** until we find the set of leaf nodes to scan for the term. - */ - getChildrenContaining(pData, nData, pTerm, nTerm, isPrefix, - &iStartChild, &iEndChild); - while( iStartChild>iLeavesEnd ){ - sqlite_int64 iNextStart, iNextEnd; - rc = loadAndGetChildrenContaining(v, iStartChild, pTerm, nTerm, isPrefix, - &iNextStart, &iNextEnd); - if( rc!=SQLITE_OK ) return rc; - - /* If we've branched, follow the end branch, too. */ - if( iStartChild!=iEndChild ){ - sqlite_int64 iDummy; - rc = loadAndGetChildrenContaining(v, iEndChild, pTerm, nTerm, isPrefix, - &iDummy, &iNextEnd); - if( rc!=SQLITE_OK ) return rc; - } - - assert( iNextStart<=iNextEnd ); - iStartChild = iNextStart; - iEndChild = iNextEnd; - } - assert( iStartChild<=iLeavesEnd ); - assert( iEndChild<=iLeavesEnd ); - - /* Scan through the leaf segments for doclists. */ - return loadSegmentLeaves(v, iStartChild, iEndChild, - pTerm, nTerm, isPrefix, out); - } -} - -/* Call loadSegmentInt() to collect the doclist for pTerm/nTerm, then -** merge its doclist over *out (any duplicate doclists read from the -** segment rooted at pData will overwrite those in *out). -*/ -/* TODO(shess) Consider changing this to determine the depth of the -** leaves using either the first characters of interior nodes (when -** ==1, we're one level above the leaves), or the first character of -** the root (which will describe the height of the tree directly). -** Either feels somewhat tricky to me. -*/ -/* TODO(shess) The current merge is likely to be slow for large -** doclists (though it should process from newest/smallest to -** oldest/largest, so it may not be that bad). It might be useful to -** modify things to allow for N-way merging. This could either be -** within a segment, with pairwise merges across segments, or across -** all segments at once. -*/ -static int loadSegment(fulltext_vtab *v, const char *pData, int nData, - sqlite_int64 iLeavesEnd, - const char *pTerm, int nTerm, int isPrefix, - DataBuffer *out){ - DataBuffer result; - int rc; - - assert( nData>1 ); - - /* This code should never be called with buffered updates. */ - assert( v->nPendingData<0 ); - - dataBufferInit(&result, 0); - rc = loadSegmentInt(v, pData, nData, iLeavesEnd, - pTerm, nTerm, isPrefix, &result); - if( rc==SQLITE_OK && result.nData>0 ){ - if( out->nData==0 ){ - DataBuffer tmp = *out; - *out = result; - result = tmp; - }else{ - DataBuffer merged; - DLReader readers[2]; - - dlrInit(&readers[0], DL_DEFAULT, out->pData, out->nData); - dlrInit(&readers[1], DL_DEFAULT, result.pData, result.nData); - dataBufferInit(&merged, out->nData+result.nData); - docListMerge(&merged, readers, 2); - dataBufferDestroy(out); - *out = merged; - dlrDestroy(&readers[0]); - dlrDestroy(&readers[1]); - } - } - dataBufferDestroy(&result); - return rc; -} - -/* Scan the database and merge together the posting lists for the term -** into *out. -*/ -static int termSelect( - fulltext_vtab *v, - int iColumn, - const char *pTerm, int nTerm, /* Term to query for */ - int isPrefix, /* True for a prefix search */ - DocListType iType, - DataBuffer *out /* Write results here */ -){ - DataBuffer doclist; - sqlite3_stmt *s; - int rc = sql_get_statement(v, SEGDIR_SELECT_ALL_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - /* This code should never be called with buffered updates. */ - assert( v->nPendingData<0 ); - - dataBufferInit(&doclist, 0); - dataBufferInit(out, 0); - - /* Traverse the segments from oldest to newest so that newer doclist - ** elements for given docids overwrite older elements. - */ - while( (rc = sqlite3_step(s))==SQLITE_ROW ){ - const char *pData = sqlite3_column_blob(s, 2); - const int nData = sqlite3_column_bytes(s, 2); - const sqlite_int64 iLeavesEnd = sqlite3_column_int64(s, 1); - rc = loadSegment(v, pData, nData, iLeavesEnd, pTerm, nTerm, isPrefix, - &doclist); - if( rc!=SQLITE_OK ) goto err; - } - if( rc==SQLITE_DONE ){ - if( doclist.nData!=0 ){ - /* TODO(shess) The old term_select_all() code applied the column - ** restrict as we merged segments, leading to smaller buffers. - ** This is probably worthwhile to bring back, once the new storage - ** system is checked in. - */ - if( iColumn==v->nColumn) iColumn = -1; - docListTrim(DL_DEFAULT, doclist.pData, doclist.nData, - iColumn, iType, out); - } - rc = SQLITE_OK; - } - - err: - dataBufferDestroy(&doclist); - return rc; -} - -/****************************************************************/ -/* Used to hold hashtable data for sorting. */ -typedef struct TermData { - const char *pTerm; - int nTerm; - DLCollector *pCollector; -} TermData; - -/* Orders TermData elements in strcmp fashion ( <0 for less-than, 0 -** for equal, >0 for greater-than). -*/ -static int termDataCmp(const void *av, const void *bv){ - const TermData *a = (const TermData *)av; - const TermData *b = (const TermData *)bv; - int n = a->nTermnTerm ? a->nTerm : b->nTerm; - int c = memcmp(a->pTerm, b->pTerm, n); - if( c!=0 ) return c; - return a->nTerm-b->nTerm; -} - -/* Order pTerms data by term, then write a new level 0 segment using -** LeafWriter. -*/ -static int writeZeroSegment(fulltext_vtab *v, fts3Hash *pTerms){ - fts3HashElem *e; - int idx, rc, i, n; - TermData *pData; - LeafWriter writer; - DataBuffer dl; - - /* Determine the next index at level 0, merging as necessary. */ - rc = segdirNextIndex(v, 0, &idx); - if( rc!=SQLITE_OK ) return rc; - - n = fts3HashCount(pTerms); - pData = sqlite3_malloc(n*sizeof(TermData)); - - for(i = 0, e = fts3HashFirst(pTerms); e; i++, e = fts3HashNext(e)){ - assert( i1 ) qsort(pData, n, sizeof(*pData), termDataCmp); - - /* TODO(shess) Refactor so that we can write directly to the segment - ** DataBuffer, as happens for segment merges. - */ - leafWriterInit(0, idx, &writer); - dataBufferInit(&dl, 0); - for(i=0; inPendingData>=0 ){ - fts3HashElem *e; - for(e=fts3HashFirst(&v->pendingTerms); e; e=fts3HashNext(e)){ - dlcDelete(fts3HashData(e)); - } - fts3HashClear(&v->pendingTerms); - v->nPendingData = -1; - } - return SQLITE_OK; -} - -/* If pendingTerms has data, flush it to a level-zero segment, and -** free it. -*/ -static int flushPendingTerms(fulltext_vtab *v){ - if( v->nPendingData>=0 ){ - int rc = writeZeroSegment(v, &v->pendingTerms); - if( rc==SQLITE_OK ) clearPendingTerms(v); - return rc; - } - return SQLITE_OK; -} - -/* If pendingTerms is "too big", or docid is out of order, flush it. -** Regardless, be certain that pendingTerms is initialized for use. -*/ -static int initPendingTerms(fulltext_vtab *v, sqlite_int64 iDocid){ - /* TODO(shess) Explore whether partially flushing the buffer on - ** forced-flush would provide better performance. I suspect that if - ** we ordered the doclists by size and flushed the largest until the - ** buffer was half empty, that would let the less frequent terms - ** generate longer doclists. - */ - if( iDocid<=v->iPrevDocid || v->nPendingData>kPendingThreshold ){ - int rc = flushPendingTerms(v); - if( rc!=SQLITE_OK ) return rc; - } - if( v->nPendingData<0 ){ - fts3HashInit(&v->pendingTerms, FTS3_HASH_STRING, 1); - v->nPendingData = 0; - } - v->iPrevDocid = iDocid; - return SQLITE_OK; -} - -/* This function implements the xUpdate callback; it is the top-level entry - * point for inserting, deleting or updating a row in a full-text table. */ -static int fulltextUpdate(sqlite3_vtab *pVtab, int nArg, sqlite3_value **ppArg, - sqlite_int64 *pRowid){ - fulltext_vtab *v = (fulltext_vtab *) pVtab; - int rc; - - FTSTRACE(("FTS3 Update %p\n", pVtab)); - - if( nArg<2 ){ - rc = index_delete(v, sqlite3_value_int64(ppArg[0])); - if( rc==SQLITE_OK ){ - /* If we just deleted the last row in the table, clear out the - ** index data. - */ - rc = content_exists(v); - if( rc==SQLITE_ROW ){ - rc = SQLITE_OK; - }else if( rc==SQLITE_DONE ){ - /* Clear the pending terms so we don't flush a useless level-0 - ** segment when the transaction closes. - */ - rc = clearPendingTerms(v); - if( rc==SQLITE_OK ){ - rc = segdir_delete_all(v); - } - } - } - } else if( sqlite3_value_type(ppArg[0]) != SQLITE_NULL ){ - /* An update: - * ppArg[0] = old rowid - * ppArg[1] = new rowid - * ppArg[2..2+v->nColumn-1] = values - * ppArg[2+v->nColumn] = value for magic column (we ignore this) - * ppArg[2+v->nColumn+1] = value for docid - */ - sqlite_int64 rowid = sqlite3_value_int64(ppArg[0]); - if( sqlite3_value_type(ppArg[1]) != SQLITE_INTEGER || - sqlite3_value_int64(ppArg[1]) != rowid ){ - rc = SQLITE_ERROR; /* we don't allow changing the rowid */ - }else if( sqlite3_value_type(ppArg[2+v->nColumn+1]) != SQLITE_INTEGER || - sqlite3_value_int64(ppArg[2+v->nColumn+1]) != rowid ){ - rc = SQLITE_ERROR; /* we don't allow changing the docid */ - }else{ - assert( nArg==2+v->nColumn+2); - rc = index_update(v, rowid, &ppArg[2]); - } - } else { - /* An insert: - * ppArg[1] = requested rowid - * ppArg[2..2+v->nColumn-1] = values - * ppArg[2+v->nColumn] = value for magic column (we ignore this) - * ppArg[2+v->nColumn+1] = value for docid - */ - sqlite3_value *pRequestDocid = ppArg[2+v->nColumn+1]; - assert( nArg==2+v->nColumn+2); - if( SQLITE_NULL != sqlite3_value_type(pRequestDocid) && - SQLITE_NULL != sqlite3_value_type(ppArg[1]) ){ - /* TODO(shess) Consider allowing this to work if the values are - ** identical. I'm inclined to discourage that usage, though, - ** given that both rowid and docid are special columns. Better - ** would be to define one or the other as the default winner, - ** but should it be fts3-centric (docid) or SQLite-centric - ** (rowid)? - */ - rc = SQLITE_ERROR; - }else{ - if( SQLITE_NULL == sqlite3_value_type(pRequestDocid) ){ - pRequestDocid = ppArg[1]; - } - rc = index_insert(v, pRequestDocid, &ppArg[2], pRowid); - } - } - - return rc; -} - -static int fulltextSync(sqlite3_vtab *pVtab){ - FTSTRACE(("FTS3 xSync()\n")); - return flushPendingTerms((fulltext_vtab *)pVtab); -} - -static int fulltextBegin(sqlite3_vtab *pVtab){ - fulltext_vtab *v = (fulltext_vtab *) pVtab; - FTSTRACE(("FTS3 xBegin()\n")); - - /* Any buffered updates should have been cleared by the previous - ** transaction. - */ - assert( v->nPendingData<0 ); - return clearPendingTerms(v); -} - -static int fulltextCommit(sqlite3_vtab *pVtab){ - fulltext_vtab *v = (fulltext_vtab *) pVtab; - FTSTRACE(("FTS3 xCommit()\n")); - - /* Buffered updates should have been cleared by fulltextSync(). */ - assert( v->nPendingData<0 ); - return clearPendingTerms(v); -} - -static int fulltextRollback(sqlite3_vtab *pVtab){ - FTSTRACE(("FTS3 xRollback()\n")); - return clearPendingTerms((fulltext_vtab *)pVtab); -} - -/* -** Implementation of the snippet() function for FTS3 -*/ -static void snippetFunc( - sqlite3_context *pContext, - int argc, - sqlite3_value **argv -){ - fulltext_cursor *pCursor; - if( argc<1 ) return; - if( sqlite3_value_type(argv[0])!=SQLITE_BLOB || - sqlite3_value_bytes(argv[0])!=sizeof(pCursor) ){ - sqlite3_result_error(pContext, "illegal first argument to html_snippet",-1); - }else{ - const char *zStart = ""; - const char *zEnd = ""; - const char *zEllipsis = "..."; - memcpy(&pCursor, sqlite3_value_blob(argv[0]), sizeof(pCursor)); - if( argc>=2 ){ - zStart = (const char*)sqlite3_value_text(argv[1]); - if( argc>=3 ){ - zEnd = (const char*)sqlite3_value_text(argv[2]); - if( argc>=4 ){ - zEllipsis = (const char*)sqlite3_value_text(argv[3]); - } - } - } - snippetAllOffsets(pCursor); - snippetText(pCursor, zStart, zEnd, zEllipsis); - sqlite3_result_text(pContext, pCursor->snippet.zSnippet, - pCursor->snippet.nSnippet, SQLITE_STATIC); - } -} - -/* -** Implementation of the offsets() function for FTS3 -*/ -static void snippetOffsetsFunc( - sqlite3_context *pContext, - int argc, - sqlite3_value **argv -){ - fulltext_cursor *pCursor; - if( argc<1 ) return; - if( sqlite3_value_type(argv[0])!=SQLITE_BLOB || - sqlite3_value_bytes(argv[0])!=sizeof(pCursor) ){ - sqlite3_result_error(pContext, "illegal first argument to offsets",-1); - }else{ - memcpy(&pCursor, sqlite3_value_blob(argv[0]), sizeof(pCursor)); - snippetAllOffsets(pCursor); - snippetOffsetText(&pCursor->snippet); - sqlite3_result_text(pContext, - pCursor->snippet.zOffset, pCursor->snippet.nOffset, - SQLITE_STATIC); - } -} - -/* OptLeavesReader is nearly identical to LeavesReader, except that -** where LeavesReader is geared towards the merging of complete -** segment levels (with exactly MERGE_COUNT segments), OptLeavesReader -** is geared towards implementation of the optimize() function, and -** can merge all segments simultaneously. This version may be -** somewhat less efficient than LeavesReader because it merges into an -** accumulator rather than doing an N-way merge, but since segment -** size grows exponentially (so segment count logrithmically) this is -** probably not an immediate problem. -*/ -/* TODO(shess): Prove that assertion, or extend the merge code to -** merge tree fashion (like the prefix-searching code does). -*/ -/* TODO(shess): OptLeavesReader and LeavesReader could probably be -** merged with little or no loss of performance for LeavesReader. The -** merged code would need to handle >MERGE_COUNT segments, and would -** also need to be able to optionally optimize away deletes. -*/ -typedef struct OptLeavesReader { - /* Segment number, to order readers by age. */ - int segment; - LeavesReader reader; -} OptLeavesReader; - -static int optLeavesReaderAtEnd(OptLeavesReader *pReader){ - return leavesReaderAtEnd(&pReader->reader); -} -static int optLeavesReaderTermBytes(OptLeavesReader *pReader){ - return leavesReaderTermBytes(&pReader->reader); -} -static const char *optLeavesReaderData(OptLeavesReader *pReader){ - return leavesReaderData(&pReader->reader); -} -static int optLeavesReaderDataBytes(OptLeavesReader *pReader){ - return leavesReaderDataBytes(&pReader->reader); -} -static const char *optLeavesReaderTerm(OptLeavesReader *pReader){ - return leavesReaderTerm(&pReader->reader); -} -static int optLeavesReaderStep(fulltext_vtab *v, OptLeavesReader *pReader){ - return leavesReaderStep(v, &pReader->reader); -} -static int optLeavesReaderTermCmp(OptLeavesReader *lr1, OptLeavesReader *lr2){ - return leavesReaderTermCmp(&lr1->reader, &lr2->reader); -} -/* Order by term ascending, segment ascending (oldest to newest), with -** exhausted readers to the end. -*/ -static int optLeavesReaderCmp(OptLeavesReader *lr1, OptLeavesReader *lr2){ - int c = optLeavesReaderTermCmp(lr1, lr2); - if( c!=0 ) return c; - return lr1->segment-lr2->segment; -} -/* Bubble pLr[0] to appropriate place in pLr[1..nLr-1]. Assumes that -** pLr[1..nLr-1] is already sorted. -*/ -static void optLeavesReaderReorder(OptLeavesReader *pLr, int nLr){ - while( nLr>1 && optLeavesReaderCmp(pLr, pLr+1)>0 ){ - OptLeavesReader tmp = pLr[0]; - pLr[0] = pLr[1]; - pLr[1] = tmp; - nLr--; - pLr++; - } -} - -/* optimize() helper function. Put the readers in order and iterate -** through them, merging doclists for matching terms into pWriter. -** Returns SQLITE_OK on success, or the SQLite error code which -** prevented success. -*/ -static int optimizeInternal(fulltext_vtab *v, - OptLeavesReader *readers, int nReaders, - LeafWriter *pWriter){ - int i, rc = SQLITE_OK; - DataBuffer doclist, merged, tmp; - - /* Order the readers. */ - i = nReaders; - while( i-- > 0 ){ - optLeavesReaderReorder(&readers[i], nReaders-i); - } - - dataBufferInit(&doclist, LEAF_MAX); - dataBufferInit(&merged, LEAF_MAX); - - /* Exhausted readers bubble to the end, so when the first reader is - ** at eof, all are at eof. - */ - while( !optLeavesReaderAtEnd(&readers[0]) ){ - - /* Figure out how many readers share the next term. */ - for(i=1; i 0 ){ - dlrDestroy(&dlReaders[nReaders]); - } - - /* Accumulated doclist to reader 0 for next pass. */ - dlrInit(&dlReaders[0], DL_DEFAULT, doclist.pData, doclist.nData); - } - - /* Destroy reader that was left in the pipeline. */ - dlrDestroy(&dlReaders[0]); - - /* Trim deletions from the doclist. */ - dataBufferReset(&merged); - docListTrim(DL_DEFAULT, doclist.pData, doclist.nData, - -1, DL_DEFAULT, &merged); - } - - /* Only pass doclists with hits (skip if all hits deleted). */ - if( merged.nData>0 ){ - rc = leafWriterStep(v, pWriter, - optLeavesReaderTerm(&readers[0]), - optLeavesReaderTermBytes(&readers[0]), - merged.pData, merged.nData); - if( rc!=SQLITE_OK ) goto err; - } - - /* Step merged readers to next term and reorder. */ - while( i-- > 0 ){ - rc = optLeavesReaderStep(v, &readers[i]); - if( rc!=SQLITE_OK ) goto err; - - optLeavesReaderReorder(&readers[i], nReaders-i); - } - } - - err: - dataBufferDestroy(&doclist); - dataBufferDestroy(&merged); - return rc; -} - -/* Implement optimize() function for FTS3. optimize(t) merges all -** segments in the fts index into a single segment. 't' is the magic -** table-named column. -*/ -static void optimizeFunc(sqlite3_context *pContext, - int argc, sqlite3_value **argv){ - fulltext_cursor *pCursor; - if( argc>1 ){ - sqlite3_result_error(pContext, "excess arguments to optimize()",-1); - }else if( sqlite3_value_type(argv[0])!=SQLITE_BLOB || - sqlite3_value_bytes(argv[0])!=sizeof(pCursor) ){ - sqlite3_result_error(pContext, "illegal first argument to optimize",-1); - }else{ - fulltext_vtab *v; - int i, rc, iMaxLevel; - OptLeavesReader *readers; - int nReaders; - LeafWriter writer; - sqlite3_stmt *s; - - memcpy(&pCursor, sqlite3_value_blob(argv[0]), sizeof(pCursor)); - v = cursor_vtab(pCursor); - - /* Flush any buffered updates before optimizing. */ - rc = flushPendingTerms(v); - if( rc!=SQLITE_OK ) goto err; - - rc = segdir_count(v, &nReaders, &iMaxLevel); - if( rc!=SQLITE_OK ) goto err; - if( nReaders==0 || nReaders==1 ){ - sqlite3_result_text(pContext, "Index already optimal", -1, - SQLITE_STATIC); - return; - } - - rc = sql_get_statement(v, SEGDIR_SELECT_ALL_STMT, &s); - if( rc!=SQLITE_OK ) goto err; - - readers = sqlite3_malloc(nReaders*sizeof(readers[0])); - if( readers==NULL ) goto err; - - /* Note that there will already be a segment at this position - ** until we call segdir_delete() on iMaxLevel. - */ - leafWriterInit(iMaxLevel, 0, &writer); - - i = 0; - while( (rc = sqlite3_step(s))==SQLITE_ROW ){ - sqlite_int64 iStart = sqlite3_column_int64(s, 0); - sqlite_int64 iEnd = sqlite3_column_int64(s, 1); - const char *pRootData = sqlite3_column_blob(s, 2); - int nRootData = sqlite3_column_bytes(s, 2); - - assert( i 0 ){ - leavesReaderDestroy(&readers[i].reader); - } - sqlite3_free(readers); - - /* If we've successfully gotten to here, delete the old segments - ** and flush the interior structure of the new segment. - */ - if( rc==SQLITE_OK ){ - for( i=0; i<=iMaxLevel; i++ ){ - rc = segdir_delete(v, i); - if( rc!=SQLITE_OK ) break; - } - - if( rc==SQLITE_OK ) rc = leafWriterFinalize(v, &writer); - } - - leafWriterDestroy(&writer); - - if( rc!=SQLITE_OK ) goto err; - - sqlite3_result_text(pContext, "Index optimized", -1, SQLITE_STATIC); - return; - - /* TODO(shess): Error-handling needs to be improved along the - ** lines of the dump_ functions. - */ - err: - { - char buf[512]; - sqlite3_snprintf(sizeof(buf), buf, "Error in optimize: %s", - sqlite3_errmsg(sqlite3_context_db_handle(pContext))); - sqlite3_result_error(pContext, buf, -1); - } - } -} - -#ifdef SQLITE_TEST -/* Generate an error of the form ": ". If msg is NULL, -** pull the error from the context's db handle. -*/ -static void generateError(sqlite3_context *pContext, - const char *prefix, const char *msg){ - char buf[512]; - if( msg==NULL ) msg = sqlite3_errmsg(sqlite3_context_db_handle(pContext)); - sqlite3_snprintf(sizeof(buf), buf, "%s: %s", prefix, msg); - sqlite3_result_error(pContext, buf, -1); -} - -/* Helper function to collect the set of terms in the segment into -** pTerms. The segment is defined by the leaf nodes between -** iStartBlockid and iEndBlockid, inclusive, or by the contents of -** pRootData if iStartBlockid is 0 (in which case the entire segment -** fit in a leaf). -*/ -static int collectSegmentTerms(fulltext_vtab *v, sqlite3_stmt *s, - fts3Hash *pTerms){ - const sqlite_int64 iStartBlockid = sqlite3_column_int64(s, 0); - const sqlite_int64 iEndBlockid = sqlite3_column_int64(s, 1); - const char *pRootData = sqlite3_column_blob(s, 2); - const int nRootData = sqlite3_column_bytes(s, 2); - LeavesReader reader; - int rc = leavesReaderInit(v, 0, iStartBlockid, iEndBlockid, - pRootData, nRootData, &reader); - if( rc!=SQLITE_OK ) return rc; - - while( rc==SQLITE_OK && !leavesReaderAtEnd(&reader) ){ - const char *pTerm = leavesReaderTerm(&reader); - const int nTerm = leavesReaderTermBytes(&reader); - void *oldValue = sqlite3Fts3HashFind(pTerms, pTerm, nTerm); - void *newValue = (void *)((char *)oldValue+1); - - /* From the comment before sqlite3Fts3HashInsert in fts3_hash.c, - ** the data value passed is returned in case of malloc failure. - */ - if( newValue==sqlite3Fts3HashInsert(pTerms, pTerm, nTerm, newValue) ){ - rc = SQLITE_NOMEM; - }else{ - rc = leavesReaderStep(v, &reader); - } - } - - leavesReaderDestroy(&reader); - return rc; -} - -/* Helper function to build the result string for dump_terms(). */ -static int generateTermsResult(sqlite3_context *pContext, fts3Hash *pTerms){ - int iTerm, nTerms, nResultBytes, iByte; - char *result; - TermData *pData; - fts3HashElem *e; - - /* Iterate pTerms to generate an array of terms in pData for - ** sorting. - */ - nTerms = fts3HashCount(pTerms); - assert( nTerms>0 ); - pData = sqlite3_malloc(nTerms*sizeof(TermData)); - if( pData==NULL ) return SQLITE_NOMEM; - - nResultBytes = 0; - for(iTerm = 0, e = fts3HashFirst(pTerms); e; iTerm++, e = fts3HashNext(e)){ - nResultBytes += fts3HashKeysize(e)+1; /* Term plus trailing space */ - assert( iTerm0 ); /* nTerms>0, nResultsBytes must be, too. */ - result = sqlite3_malloc(nResultBytes); - if( result==NULL ){ - sqlite3_free(pData); - return SQLITE_NOMEM; - } - - if( nTerms>1 ) qsort(pData, nTerms, sizeof(*pData), termDataCmp); - - /* Read the terms in order to build the result. */ - iByte = 0; - for(iTerm=0; iTerm0 ){ - rc = generateTermsResult(pContext, &terms); - if( rc==SQLITE_NOMEM ){ - generateError(pContext, "dump_terms", "out of memory"); - }else{ - assert( rc==SQLITE_OK ); - } - }else if( argc==3 ){ - /* The specific segment asked for could not be found. */ - generateError(pContext, "dump_terms", "segment not found"); - }else{ - /* No segments found. */ - /* TODO(shess): It should be impossible to reach this. This - ** case can only happen for an empty table, in which case - ** SQLite has no rows to call this function on. - */ - sqlite3_result_null(pContext); - } - } - sqlite3Fts3HashClear(&terms); - } -} - -/* Expand the DL_DEFAULT doclist in pData into a text result in -** pContext. -*/ -static void createDoclistResult(sqlite3_context *pContext, - const char *pData, int nData){ - DataBuffer dump; - DLReader dlReader; - - assert( pData!=NULL && nData>0 ); - - dataBufferInit(&dump, 0); - dlrInit(&dlReader, DL_DEFAULT, pData, nData); - for( ; !dlrAtEnd(&dlReader); dlrStep(&dlReader) ){ - char buf[256]; - PLReader plReader; - - plrInit(&plReader, &dlReader); - if( DL_DEFAULT==DL_DOCIDS || plrAtEnd(&plReader) ){ - sqlite3_snprintf(sizeof(buf), buf, "[%lld] ", dlrDocid(&dlReader)); - dataBufferAppend(&dump, buf, strlen(buf)); - }else{ - int iColumn = plrColumn(&plReader); - - sqlite3_snprintf(sizeof(buf), buf, "[%lld %d[", - dlrDocid(&dlReader), iColumn); - dataBufferAppend(&dump, buf, strlen(buf)); - - for( ; !plrAtEnd(&plReader); plrStep(&plReader) ){ - if( plrColumn(&plReader)!=iColumn ){ - iColumn = plrColumn(&plReader); - sqlite3_snprintf(sizeof(buf), buf, "] %d[", iColumn); - assert( dump.nData>0 ); - dump.nData--; /* Overwrite trailing space. */ - assert( dump.pData[dump.nData]==' '); - dataBufferAppend(&dump, buf, strlen(buf)); - } - if( DL_DEFAULT==DL_POSITIONS_OFFSETS ){ - sqlite3_snprintf(sizeof(buf), buf, "%d,%d,%d ", - plrPosition(&plReader), - plrStartOffset(&plReader), plrEndOffset(&plReader)); - }else if( DL_DEFAULT==DL_POSITIONS ){ - sqlite3_snprintf(sizeof(buf), buf, "%d ", plrPosition(&plReader)); - }else{ - assert( NULL=="Unhandled DL_DEFAULT value"); - } - dataBufferAppend(&dump, buf, strlen(buf)); - } - plrDestroy(&plReader); - - assert( dump.nData>0 ); - dump.nData--; /* Overwrite trailing space. */ - assert( dump.pData[dump.nData]==' '); - dataBufferAppend(&dump, "]] ", 3); - } - } - dlrDestroy(&dlReader); - - assert( dump.nData>0 ); - dump.nData--; /* Overwrite trailing space. */ - assert( dump.pData[dump.nData]==' '); - dump.pData[dump.nData] = '\0'; - assert( dump.nData>0 ); - - /* Passes ownership of dump's buffer to pContext. */ - sqlite3_result_text(pContext, dump.pData, dump.nData, sqlite3_free); - dump.pData = NULL; - dump.nData = dump.nCapacity = 0; -} - -/* Implements dump_doclist() for use in inspecting the fts3 index from -** tests. TEXT result containing a string representation of the -** doclist for the indicated term. dump_doclist(t, term, level, idx) -** dumps the doclist for term from the segment specified by level, idx -** (in %_segdir), while dump_doclist(t, term) dumps the logical -** doclist for the term across all segments. The per-segment doclist -** can contain deletions, while the full-index doclist will not -** (deletions are omitted). -** -** Result formats differ with the setting of DL_DEFAULTS. Examples: -** -** DL_DOCIDS: [1] [3] [7] -** DL_POSITIONS: [1 0[0 4] 1[17]] [3 1[5]] -** DL_POSITIONS_OFFSETS: [1 0[0,0,3 4,23,26] 1[17,102,105]] [3 1[5,20,23]] -** -** In each case the number after the outer '[' is the docid. In the -** latter two cases, the number before the inner '[' is the column -** associated with the values within. For DL_POSITIONS the numbers -** within are the positions, for DL_POSITIONS_OFFSETS they are the -** position, the start offset, and the end offset. -*/ -static void dumpDoclistFunc( - sqlite3_context *pContext, - int argc, sqlite3_value **argv -){ - fulltext_cursor *pCursor; - if( argc!=2 && argc!=4 ){ - generateError(pContext, "dump_doclist", "incorrect arguments"); - }else if( sqlite3_value_type(argv[0])!=SQLITE_BLOB || - sqlite3_value_bytes(argv[0])!=sizeof(pCursor) ){ - generateError(pContext, "dump_doclist", "illegal first argument"); - }else if( sqlite3_value_text(argv[1])==NULL || - sqlite3_value_text(argv[1])[0]=='\0' ){ - generateError(pContext, "dump_doclist", "empty second argument"); - }else{ - const char *pTerm = (const char *)sqlite3_value_text(argv[1]); - const int nTerm = strlen(pTerm); - fulltext_vtab *v; - int rc; - DataBuffer doclist; - - memcpy(&pCursor, sqlite3_value_blob(argv[0]), sizeof(pCursor)); - v = cursor_vtab(pCursor); - - dataBufferInit(&doclist, 0); - - /* termSelect() yields the same logical doclist that queries are - ** run against. - */ - if( argc==2 ){ - rc = termSelect(v, v->nColumn, pTerm, nTerm, 0, DL_DEFAULT, &doclist); - }else{ - sqlite3_stmt *s = NULL; - - /* Get our specific segment's information. */ - rc = sql_get_statement(v, SEGDIR_SELECT_SEGMENT_STMT, &s); - if( rc==SQLITE_OK ){ - rc = sqlite3_bind_int(s, 1, sqlite3_value_int(argv[2])); - if( rc==SQLITE_OK ){ - rc = sqlite3_bind_int(s, 2, sqlite3_value_int(argv[3])); - } - } - - if( rc==SQLITE_OK ){ - rc = sqlite3_step(s); - - if( rc==SQLITE_DONE ){ - dataBufferDestroy(&doclist); - generateError(pContext, "dump_doclist", "segment not found"); - return; - } - - /* Found a segment, load it into doclist. */ - if( rc==SQLITE_ROW ){ - const sqlite_int64 iLeavesEnd = sqlite3_column_int64(s, 1); - const char *pData = sqlite3_column_blob(s, 2); - const int nData = sqlite3_column_bytes(s, 2); - - /* loadSegment() is used by termSelect() to load each - ** segment's data. - */ - rc = loadSegment(v, pData, nData, iLeavesEnd, pTerm, nTerm, 0, - &doclist); - if( rc==SQLITE_OK ){ - rc = sqlite3_step(s); - - /* Should not have more than one matching segment. */ - if( rc!=SQLITE_DONE ){ - sqlite3_reset(s); - dataBufferDestroy(&doclist); - generateError(pContext, "dump_doclist", "invalid segdir"); - return; - } - rc = SQLITE_OK; - } - } - } - - sqlite3_reset(s); - } - - if( rc==SQLITE_OK ){ - if( doclist.nData>0 ){ - createDoclistResult(pContext, doclist.pData, doclist.nData); - }else{ - /* TODO(shess): This can happen if the term is not present, or - ** if all instances of the term have been deleted and this is - ** an all-index dump. It may be interesting to distinguish - ** these cases. - */ - sqlite3_result_text(pContext, "", 0, SQLITE_STATIC); - } - }else if( rc==SQLITE_NOMEM ){ - /* Handle out-of-memory cases specially because if they are - ** generated in fts3 code they may not be reflected in the db - ** handle. - */ - /* TODO(shess): Handle this more comprehensively. - ** sqlite3ErrStr() has what I need, but is internal. - */ - generateError(pContext, "dump_doclist", "out of memory"); - }else{ - generateError(pContext, "dump_doclist", NULL); - } - - dataBufferDestroy(&doclist); - } -} -#endif - -/* -** This routine implements the xFindFunction method for the FTS3 -** virtual table. -*/ -static int fulltextFindFunction( - sqlite3_vtab *pVtab, - int nArg, - const char *zName, - void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), - void **ppArg -){ - if( strcmp(zName,"snippet")==0 ){ - *pxFunc = snippetFunc; - return 1; - }else if( strcmp(zName,"offsets")==0 ){ - *pxFunc = snippetOffsetsFunc; - return 1; - }else if( strcmp(zName,"optimize")==0 ){ - *pxFunc = optimizeFunc; - return 1; -#ifdef SQLITE_TEST - /* NOTE(shess): These functions are present only for testing - ** purposes. No particular effort is made to optimize their - ** execution or how they build their results. - */ - }else if( strcmp(zName,"dump_terms")==0 ){ - /* fprintf(stderr, "Found dump_terms\n"); */ - *pxFunc = dumpTermsFunc; - return 1; - }else if( strcmp(zName,"dump_doclist")==0 ){ - /* fprintf(stderr, "Found dump_doclist\n"); */ - *pxFunc = dumpDoclistFunc; - return 1; -#endif - } - return 0; -} - -/* -** Rename an fts3 table. -*/ -static int fulltextRename( - sqlite3_vtab *pVtab, - const char *zName -){ - fulltext_vtab *p = (fulltext_vtab *)pVtab; - int rc = SQLITE_NOMEM; - char *zSql = sqlite3_mprintf( - "ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';" - "ALTER TABLE %Q.'%q_segments' RENAME TO '%q_segments';" - "ALTER TABLE %Q.'%q_segdir' RENAME TO '%q_segdir';" - , p->zDb, p->zName, zName - , p->zDb, p->zName, zName - , p->zDb, p->zName, zName - ); - if( zSql ){ - rc = sqlite3_exec(p->db, zSql, 0, 0, 0); - sqlite3_free(zSql); - } - return rc; -} - -static const sqlite3_module fts3Module = { - /* iVersion */ 0, - /* xCreate */ fulltextCreate, - /* xConnect */ fulltextConnect, - /* xBestIndex */ fulltextBestIndex, - /* xDisconnect */ fulltextDisconnect, - /* xDestroy */ fulltextDestroy, - /* xOpen */ fulltextOpen, - /* xClose */ fulltextClose, - /* xFilter */ fulltextFilter, - /* xNext */ fulltextNext, - /* xEof */ fulltextEof, - /* xColumn */ fulltextColumn, - /* xRowid */ fulltextRowid, - /* xUpdate */ fulltextUpdate, - /* xBegin */ fulltextBegin, - /* xSync */ fulltextSync, - /* xCommit */ fulltextCommit, - /* xRollback */ fulltextRollback, - /* xFindFunction */ fulltextFindFunction, - /* xRename */ fulltextRename, -}; - -static void hashDestroy(void *p){ - fts3Hash *pHash = (fts3Hash *)p; - sqlite3Fts3HashClear(pHash); - sqlite3_free(pHash); -} - -/* -** The fts3 built-in tokenizers - "simple" and "porter" - are implemented -** in files fts3_tokenizer1.c and fts3_porter.c respectively. The following -** two forward declarations are for functions declared in these files -** used to retrieve the respective implementations. -** -** Calling sqlite3Fts3SimpleTokenizerModule() sets the value pointed -** to by the argument to point a the "simple" tokenizer implementation. -** Function ...PorterTokenizerModule() sets *pModule to point to the -** porter tokenizer/stemmer implementation. -*/ -void sqlite3Fts3SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule); -void sqlite3Fts3PorterTokenizerModule(sqlite3_tokenizer_module const**ppModule); -void sqlite3Fts3IcuTokenizerModule(sqlite3_tokenizer_module const**ppModule); - -int sqlite3Fts3InitHashTable(sqlite3 *, fts3Hash *, const char *); - -/* -** Initialise the fts3 extension. If this extension is built as part -** of the sqlite library, then this function is called directly by -** SQLite. If fts3 is built as a dynamically loadable extension, this -** function is called by the sqlite3_extension_init() entry point. -*/ -int sqlite3Fts3Init(sqlite3 *db){ - int rc = SQLITE_OK; - fts3Hash *pHash = 0; - const sqlite3_tokenizer_module *pSimple = 0; - const sqlite3_tokenizer_module *pPorter = 0; - const sqlite3_tokenizer_module *pIcu = 0; - - sqlite3Fts3SimpleTokenizerModule(&pSimple); - sqlite3Fts3PorterTokenizerModule(&pPorter); -#ifdef SQLITE_ENABLE_ICU - sqlite3Fts3IcuTokenizerModule(&pIcu); -#endif - - /* Allocate and initialise the hash-table used to store tokenizers. */ - pHash = sqlite3_malloc(sizeof(fts3Hash)); - if( !pHash ){ - rc = SQLITE_NOMEM; - }else{ - sqlite3Fts3HashInit(pHash, FTS3_HASH_STRING, 1); - } - - /* Load the built-in tokenizers into the hash table */ - if( rc==SQLITE_OK ){ - if( sqlite3Fts3HashInsert(pHash, "simple", 7, (void *)pSimple) - || sqlite3Fts3HashInsert(pHash, "porter", 7, (void *)pPorter) - || (pIcu && sqlite3Fts3HashInsert(pHash, "icu", 4, (void *)pIcu)) - ){ - rc = SQLITE_NOMEM; - } - } - -#ifdef SQLITE_TEST - sqlite3Fts3ExprInitTestInterface(db); -#endif - - /* Create the virtual table wrapper around the hash-table and overload - ** the two scalar functions. If this is successful, register the - ** module with sqlite. - */ - if( SQLITE_OK==rc - && SQLITE_OK==(rc = sqlite3Fts3InitHashTable(db, pHash, "fts3_tokenizer")) - && SQLITE_OK==(rc = sqlite3_overload_function(db, "snippet", -1)) - && SQLITE_OK==(rc = sqlite3_overload_function(db, "offsets", -1)) - && SQLITE_OK==(rc = sqlite3_overload_function(db, "optimize", -1)) -#ifdef SQLITE_TEST - && SQLITE_OK==(rc = sqlite3_overload_function(db, "dump_terms", -1)) - && SQLITE_OK==(rc = sqlite3_overload_function(db, "dump_doclist", -1)) -#endif - ){ - return sqlite3_create_module_v2( - db, "fts3", &fts3Module, (void *)pHash, hashDestroy - ); - } - - /* An error has occured. Delete the hash table and return the error code. */ - assert( rc!=SQLITE_OK ); - if( pHash ){ - sqlite3Fts3HashClear(pHash); - sqlite3_free(pHash); - } - return rc; -} - -#if !SQLITE_CORE -int sqlite3_extension_init( - sqlite3 *db, - char **pzErrMsg, - const sqlite3_api_routines *pApi -){ - SQLITE_EXTENSION_INIT2(pApi) - return sqlite3Fts3Init(db); -} -#endif - -#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ diff --git a/ext/fts3/fts3.c b/ext/fts3/fts3.c index 260b17c..19e6495 100644 --- a/ext/fts3/fts3.c +++ b/ext/fts3/fts3.c @@ -6410,7 +6410,7 @@ static void optimizeFunc(sqlite3_context *pContext, i++; } - /* If we managed to succesfully read them all, optimize them. */ + /* If we managed to successfully read them all, optimize them. */ if( rc==SQLITE_DONE ){ assert( i==nReaders ); rc = optimizeInternal(v, readers, nReaders, &writer); @@ -7001,7 +7001,7 @@ int sqlite3Fts3Init(sqlite3 *db){ ); } - /* An error has occured. Delete the hash table and return the error code. */ + /* An error has occurred. Delete the hash table and return the error code. */ assert( rc!=SQLITE_OK ); if( pHash ){ sqlite3Fts3HashClear(pHash); diff --git a/ext/fts3/fts3_expr.c b/ext/fts3/fts3_expr.c index a8475bd..ae11e2d 100644 --- a/ext/fts3/fts3_expr.c +++ b/ext/fts3/fts3_expr.c @@ -212,8 +212,6 @@ static int getNextString( if( ii==0 ){ memset(p, 0, nByte); p->pPhrase = (Fts3Phrase *)&p[1]; - p->eType = FTSQUERY_PHRASE; - p->pPhrase->iColumn = pParse->iDefaultCol; } p->pPhrase = (Fts3Phrase *)&p[1]; p->pPhrase->nToken = ii+1; @@ -237,19 +235,25 @@ static int getNextString( char *zNew; int nNew = 0; int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase); - nByte += (p->pPhrase->nToken-1) * sizeof(struct PhraseToken); + nByte += (p?(p->pPhrase->nToken-1):0) * sizeof(struct PhraseToken); p = fts3ReallocOrFree(p, nByte + nTemp); if( !p ){ goto no_mem; } + if( zTemp ){ + zNew = &(((char *)p)[nByte]); + memcpy(zNew, zTemp, nTemp); + }else{ + memset(p, 0, nByte+nTemp); + } p->pPhrase = (Fts3Phrase *)&p[1]; - zNew = &(((char *)p)[nByte]); - memcpy(zNew, zTemp, nTemp); for(jj=0; jjpPhrase->nToken; jj++){ p->pPhrase->aToken[jj].z = &zNew[nNew]; nNew += p->pPhrase->aToken[jj].n; } sqlite3_free(zTemp); + p->eType = FTSQUERY_PHRASE; + p->pPhrase->iColumn = pParse->iDefaultCol; rc = SQLITE_OK; } diff --git a/main.mk b/main.mk index f5b6231..718ba49 100644 --- a/main.mk +++ b/main.mk @@ -59,7 +59,7 @@ LIBOBJ+= alter.o analyze.o attach.o auth.o \ main.o malloc.o mem0.o mem1.o mem2.o mem3.o mem5.o \ memjournal.o \ mutex.o mutex_noop.o mutex_os2.o mutex_unix.o mutex_w32.o \ - opcodes.o os.o os_os2.o os_unix.o os_win.o \ + notify.o opcodes.o os.o os_os2.o os_unix.o os_win.o \ pager.o parse.o pcache.o pcache1.o pragma.o prepare.o printf.o \ random.o resolve.o rowset.o rtree.o select.o status.o \ table.o tokenize.o trigger.o \ @@ -112,6 +112,7 @@ SRC = \ $(TOP)/src/mutex_os2.c \ $(TOP)/src/mutex_unix.c \ $(TOP)/src/mutex_w32.c \ + $(TOP)/src/notify.c \ $(TOP)/src/os.c \ $(TOP)/src/os.h \ $(TOP)/src/os_common.h \ diff --git a/sqlite3.def b/sqlite3.def deleted file mode 100644 index 8a826ca..0000000 --- a/sqlite3.def +++ /dev/null @@ -1,150 +0,0 @@ -EXPORTS -sqlite3_aggregate_context -sqlite3_aggregate_count -sqlite3_auto_extension -sqlite3_bind_blob -sqlite3_bind_double -sqlite3_bind_int -sqlite3_bind_int64 -sqlite3_bind_null -sqlite3_bind_parameter_count -sqlite3_bind_parameter_index -sqlite3_bind_parameter_name -sqlite3_bind_text -sqlite3_bind_text16 -sqlite3_bind_value -sqlite3_bind_zeroblob -sqlite3_blob_bytes -sqlite3_blob_close -sqlite3_blob_open -sqlite3_blob_read -sqlite3_blob_write -sqlite3_busy_handler -sqlite3_busy_timeout -sqlite3_changes -sqlite3_clear_bindings -sqlite3_close -sqlite3_collation_needed -sqlite3_collation_needed16 -sqlite3_column_blob -sqlite3_column_bytes -sqlite3_column_bytes16 -sqlite3_column_count -sqlite3_column_decltype -sqlite3_column_decltype16 -sqlite3_column_double -sqlite3_column_int -sqlite3_column_int64 -sqlite3_column_name -sqlite3_column_name16 -sqlite3_column_text -sqlite3_column_text16 -sqlite3_column_type -sqlite3_column_value -sqlite3_commit_hook -sqlite3_complete -sqlite3_complete16 -sqlite3_create_collation -sqlite3_create_collation16 -sqlite3_create_collation_v2 -sqlite3_create_function -sqlite3_create_function16 -sqlite3_create_module -sqlite3_create_module_v2 -sqlite3_data_count -sqlite3_db_handle -sqlite3_declare_vtab -sqlite3_enable_load_extension -sqlite3_enable_shared_cache -sqlite3_errcode -sqlite3_errmsg -sqlite3_errmsg16 -sqlite3_exec -sqlite3_expired -sqlite3_extended_result_codes -sqlite3_file_control -sqlite3_finalize -sqlite3_free -sqlite3_free_table -sqlite3_get_autocommit -sqlite3_get_auxdata -sqlite3_get_table -sqlite3_global_recover -sqlite3_interrupt -sqlite3_last_insert_rowid -sqlite3_libversion -sqlite3_libversion_number -sqlite3_load_extension -sqlite3_malloc -sqlite3_memory_alarm -sqlite3_memory_highwater -sqlite3_memory_used -sqlite3_mprintf -sqlite3_mutex_alloc -sqlite3_mutex_enter -sqlite3_mutex_free -sqlite3_mutex_leave -sqlite3_mutex_try -sqlite3_open -sqlite3_open16 -sqlite3_open_v2 -sqlite3_overload_function -sqlite3_prepare -sqlite3_prepare16 -sqlite3_prepare16_v2 -sqlite3_prepare_v2 -sqlite3_profile -sqlite3_progress_handler -sqlite3_randomness -sqlite3_realloc -sqlite3_release_memory -sqlite3_reset -sqlite3_reset_auto_extension -sqlite3_result_blob -sqlite3_result_double -sqlite3_result_error -sqlite3_result_error16 -sqlite3_result_error_code -sqlite3_result_error_nomem -sqlite3_result_error_toobig -sqlite3_result_int -sqlite3_result_int64 -sqlite3_result_null -sqlite3_result_text -sqlite3_result_text16 -sqlite3_result_text16be -sqlite3_result_text16le -sqlite3_result_value -sqlite3_result_zeroblob -sqlite3_rollback_hook -sqlite3_set_authorizer -sqlite3_set_auxdata -sqlite3_sleep -sqlite3_snprintf -sqlite3_soft_heap_limit -sqlite3_sql -sqlite3_step -sqlite3_test_control -sqlite3_thread_cleanup -sqlite3_threadsafe -sqlite3_total_changes -sqlite3_trace -sqlite3_transfer_bindings -sqlite3_update_hook -sqlite3_user_data -sqlite3_value_blob -sqlite3_value_bytes -sqlite3_value_bytes16 -sqlite3_value_double -sqlite3_value_int -sqlite3_value_int64 -sqlite3_value_numeric_type -sqlite3_value_text -sqlite3_value_text16 -sqlite3_value_text16be -sqlite3_value_text16le -sqlite3_value_type -sqlite3_vfs_find -sqlite3_vfs_register -sqlite3_vfs_unregister -sqlite3_vmprintf diff --git a/src/alter.c b/src/alter.c index 4974158..d89b433 100644 --- a/src/alter.c +++ b/src/alter.c @@ -12,7 +12,7 @@ ** This file contains C code routines that used to generate VDBE code ** that implements the ALTER TABLE command. ** -** $Id: alter.c,v 1.53 2009/02/13 03:43:32 drh Exp $ +** $Id: alter.c,v 1.55 2009/03/24 15:08:10 drh Exp $ */ #include "sqliteInt.h" @@ -193,7 +193,7 @@ static char *whereTempTriggers(Parse *pParse, Table *pTab){ */ if( pTab->pSchema!=pTempSchema ){ sqlite3 *db = pParse->db; - for( pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext ){ + for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){ if( pTrig->pSchema==pTempSchema ){ if( !zWhere ){ zWhere = sqlite3MPrintf(db, "name=%Q", pTrig->name); @@ -232,7 +232,7 @@ static void reloadTableSchema(Parse *pParse, Table *pTab, const char *zName){ #ifndef SQLITE_OMIT_TRIGGER /* Drop any table triggers from the internal schema. */ - for(pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext){ + for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){ int iTrigDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema); assert( iTrigDb==iDb || iTrigDb==1 ); sqlite3VdbeAddOp4(v, OP_DropTrigger, iTrigDb, 0, 0, pTrig->name, 0); @@ -593,7 +593,7 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){ if( !pNew ) goto exit_begin_add_column; pParse->pNewTable = pNew; pNew->nRef = 1; - pNew->db = db; + pNew->dbMem = pTab->dbMem; pNew->nCol = pTab->nCol; assert( pNew->nCol>0 ); nAlloc = (((pNew->nCol-1)/8)*8)+8; diff --git a/src/analyze.c b/src/analyze.c index 3862a29..d002805 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -11,7 +11,7 @@ ************************************************************************* ** This file contains code associated with the ANALYZE command. ** -** @(#) $Id: analyze.c,v 1.48 2009/02/13 16:59:53 drh Exp $ +** @(#) $Id: analyze.c,v 1.51 2009/02/28 10:47:42 danielk1977 Exp $ */ #ifndef SQLITE_OMIT_ANALYZE #include "sqliteInt.h" @@ -74,8 +74,8 @@ static void openStatTable( if( !createStat1 ){ sqlite3TableLock(pParse, iDb, iRootPage, 1, "sqlite_stat1"); } - sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, 3); sqlite3VdbeAddOp3(v, OP_OpenWrite, iStatCur, iRootPage, iDb); + sqlite3VdbeChangeP4(v, -1, (char *)3, P4_INT32); sqlite3VdbeChangeP5(v, createStat1); } @@ -117,7 +117,7 @@ static void analyzeOneTable( /* Establish a read-lock on the table at the shared-cache level. */ sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); - iIdxCur = pParse->nTab; + iIdxCur = pParse->nTab++; for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); int regFields; /* Register block for building records */ @@ -131,7 +131,6 @@ static void analyzeOneTable( */ assert( iDb==sqlite3SchemaToIndex(pParse->db, pIdx->pSchema) ); nCol = pIdx->nColumn; - sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, nCol+1); sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb, (char *)pKey, P4_KEYINFO_HANDOFF); VdbeComment((v, "%s", pIdx->zName)); @@ -189,7 +188,7 @@ static void analyzeOneTable( ** 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 entires + ** 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 diff --git a/src/attach.c b/src/attach.c index bf2683f..0f54bcc 100644 --- a/src/attach.c +++ b/src/attach.c @@ -11,7 +11,7 @@ ************************************************************************* ** This file contains code used to implement the ATTACH and DETACH commands. ** -** $Id: attach.c,v 1.82 2009/02/03 16:51:25 danielk1977 Exp $ +** $Id: attach.c,v 1.84 2009/04/08 13:51:51 drh Exp $ */ #include "sqliteInt.h" @@ -488,11 +488,11 @@ int sqlite3FixExpr( Expr *pExpr /* The expression to be fixed to one database */ ){ while( pExpr ){ - if( sqlite3FixSelect(pFix, pExpr->pSelect) ){ - return 1; - } - if( sqlite3FixExprList(pFix, pExpr->pList) ){ - return 1; + if( ExprHasAnyProperty(pExpr, EP_TokenOnly|EP_SpanToken) ) break; + if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + if( sqlite3FixSelect(pFix, pExpr->x.pSelect) ) return 1; + }else{ + if( sqlite3FixExprList(pFix, pExpr->x.pList) ) return 1; } if( sqlite3FixExpr(pFix, pExpr->pRight) ){ return 1; diff --git a/src/backup.c b/src/backup.c index 9c55804..781b4b0 100644 --- a/src/backup.c +++ b/src/backup.c @@ -12,7 +12,7 @@ ** This file contains the implementation of the sqlite3_backup_XXX() ** API functions and the related features. ** -** $Id: backup.c,v 1.12 2009/02/16 17:55:47 shane Exp $ +** $Id: backup.c,v 1.13 2009/03/16 13:19:36 danielk1977 Exp $ */ #include "sqliteInt.h" #include "btreeInt.h" @@ -292,10 +292,10 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){ int bCloseTrans = 0; /* True if src db requires unlocking */ /* If the source pager is currently in a write-transaction, return - ** SQLITE_LOCKED immediately. + ** SQLITE_BUSY immediately. */ if( p->pDestDb && p->pSrc->pBt->inTransaction==TRANS_WRITE ){ - rc = SQLITE_LOCKED; + rc = SQLITE_BUSY; }else{ rc = SQLITE_OK; } diff --git a/src/bitvec.c b/src/bitvec.c index 39513ff..b6420a7 100644 --- a/src/bitvec.c +++ b/src/bitvec.c @@ -34,7 +34,7 @@ ** start of a transaction, and is thus usually less than a few thousand, ** but can be as large as 2 billion for a really big database. ** -** @(#) $Id: bitvec.c,v 1.13 2009/01/20 17:06:27 danielk1977 Exp $ +** @(#) $Id: bitvec.c,v 1.14 2009/04/01 23:49:04 drh Exp $ */ #include "sqliteInt.h" @@ -94,8 +94,9 @@ */ struct Bitvec { u32 iSize; /* Maximum bit index. Max iSize is 4,294,967,296. */ - u32 nSet; /* Number of bits that are set - only valid for aHash element */ - /* Max nSet is BITVEC_NINT. For BITVEC_SZ of 512, this would be 125. */ + u32 nSet; /* Number of bits that are set - only valid for aHash + ** element. Max is BITVEC_NINT. For BITVEC_SZ of 512, + ** this would be 125. */ u32 iDivisor; /* Number of bits handled by each apSub[] entry. */ /* Should >=0 for apSub element. */ /* Max iDivisor is max(u32) / BITVEC_NPTR + 1. */ @@ -377,7 +378,8 @@ int sqlite3BitvecBuiltinTest(int sz, int *aOp){ ** is found. */ rc = sqlite3BitvecTest(0,0) + sqlite3BitvecTest(pBitvec, sz+1) - + sqlite3BitvecTest(pBitvec, 0); + + sqlite3BitvecTest(pBitvec, 0) + + (sqlite3BitvecSize(pBitvec) - sz); for(i=1; i<=sz; i++){ if( (TESTBIT(pV,i))!=sqlite3BitvecTest(pBitvec,i) ){ rc = i; diff --git a/src/btmutex.c b/src/btmutex.c index b1ab517..34399bb 100644 --- a/src/btmutex.c +++ b/src/btmutex.c @@ -10,7 +10,7 @@ ** ************************************************************************* ** -** $Id: btmutex.c,v 1.12 2008/11/17 19:18:55 danielk1977 Exp $ +** $Id: btmutex.c,v 1.15 2009/04/10 12:55:17 danielk1977 Exp $ ** ** This file contains code used to implement mutexes on Btree objects. ** This code really belongs in btree.c. But btree.c is getting too @@ -18,8 +18,37 @@ ** a good breakout. */ #include "btreeInt.h" -#if SQLITE_THREADSAFE && !defined(SQLITE_OMIT_SHARED_CACHE) +#ifndef SQLITE_OMIT_SHARED_CACHE +#if SQLITE_THREADSAFE +/* +** Obtain the BtShared mutex associated with B-Tree handle p. Also, +** set BtShared.db to the database handle associated with p and the +** p->locked boolean to true. +*/ +static void lockBtreeMutex(Btree *p){ + assert( p->locked==0 ); + assert( sqlite3_mutex_notheld(p->pBt->mutex) ); + assert( sqlite3_mutex_held(p->db->mutex) ); + + sqlite3_mutex_enter(p->pBt->mutex); + p->pBt->db = p->db; + p->locked = 1; +} + +/* +** Release the BtShared mutex associated with B-Tree handle p and +** clear the p->locked boolean. +*/ +static void unlockBtreeMutex(Btree *p){ + assert( p->locked==1 ); + assert( sqlite3_mutex_held(p->pBt->mutex) ); + assert( sqlite3_mutex_held(p->db->mutex) ); + assert( p->db==p->pBt->db ); + + sqlite3_mutex_leave(p->pBt->mutex); + p->locked = 0; +} /* ** Enter a mutex on the given BTree object. @@ -57,6 +86,10 @@ void sqlite3BtreeEnter(Btree *p){ /* We should already hold a lock on the database connection */ assert( sqlite3_mutex_held(p->db->mutex) ); + /* Unless the database is sharable and unlocked, then BtShared.db + ** should already be set correctly. */ + assert( (p->locked==0 && p->sharable) || p->pBt->db==p->db ); + if( !p->sharable ) return; p->wantToLock++; if( p->locked ) return; @@ -66,6 +99,7 @@ void sqlite3BtreeEnter(Btree *p){ ** procedure that follows. Just be sure not to block. */ if( sqlite3_mutex_try(p->pBt->mutex)==SQLITE_OK ){ + p->pBt->db = p->db; p->locked = 1; return; } @@ -80,16 +114,13 @@ void sqlite3BtreeEnter(Btree *p){ assert( pLater->pNext==0 || pLater->pNext->pBt>pLater->pBt ); assert( !pLater->locked || pLater->wantToLock>0 ); if( pLater->locked ){ - sqlite3_mutex_leave(pLater->pBt->mutex); - pLater->locked = 0; + unlockBtreeMutex(pLater); } } - sqlite3_mutex_enter(p->pBt->mutex); - p->locked = 1; + lockBtreeMutex(p); for(pLater=p->pNext; pLater; pLater=pLater->pNext){ if( pLater->wantToLock ){ - sqlite3_mutex_enter(pLater->pBt->mutex); - pLater->locked = 1; + lockBtreeMutex(pLater); } } } @@ -102,25 +133,25 @@ void sqlite3BtreeLeave(Btree *p){ assert( p->wantToLock>0 ); p->wantToLock--; if( p->wantToLock==0 ){ - assert( p->locked ); - sqlite3_mutex_leave(p->pBt->mutex); - p->locked = 0; + unlockBtreeMutex(p); } } } #ifndef NDEBUG /* -** Return true if the BtShared mutex is held on the btree. -** -** This routine makes no determination one why or another if the -** database connection mutex is held. +** Return true if the BtShared mutex is held on the btree, or if the +** B-Tree is not marked as sharable. ** ** This routine is used only from within assert() statements. */ int sqlite3BtreeHoldsMutex(Btree *p){ - return (p->sharable==0 || - (p->locked && p->wantToLock && sqlite3_mutex_held(p->pBt->mutex))); + assert( p->sharable==0 || p->locked==0 || p->wantToLock>0 ); + assert( p->sharable==0 || p->locked==0 || p->db==p->pBt->db ); + assert( p->sharable==0 || p->locked==0 || sqlite3_mutex_held(p->pBt->mutex) ); + assert( p->sharable==0 || p->locked==0 || sqlite3_mutex_held(p->db->mutex) ); + + return (p->sharable==0 || p->locked); } #endif @@ -160,6 +191,7 @@ void sqlite3BtreeEnterAll(sqlite3 *db){ assert( sqlite3_mutex_held(db->mutex) ); for(i=0; inDb; i++){ p = db->aDb[i].pBt; + assert( !p || (p->locked==0 && p->sharable) || p->pBt->db==p->db ); if( p && p->sharable ){ p->wantToLock++; if( !p->locked ){ @@ -168,13 +200,11 @@ void sqlite3BtreeEnterAll(sqlite3 *db){ while( p->locked && p->pNext ) p = p->pNext; for(pLater = p->pNext; pLater; pLater=pLater->pNext){ if( pLater->locked ){ - sqlite3_mutex_leave(pLater->pBt->mutex); - pLater->locked = 0; + unlockBtreeMutex(pLater); } } while( p ){ - sqlite3_mutex_enter(p->pBt->mutex); - p->locked++; + lockBtreeMutex(p); p = p->pNext; } } @@ -191,9 +221,7 @@ void sqlite3BtreeLeaveAll(sqlite3 *db){ assert( p->wantToLock>0 ); p->wantToLock--; if( p->wantToLock==0 ){ - assert( p->locked ); - sqlite3_mutex_leave(p->pBt->mutex); - p->locked = 0; + unlockBtreeMutex(p); } } } @@ -282,8 +310,7 @@ void sqlite3BtreeMutexArrayEnter(BtreeMutexArray *pArray){ p->wantToLock++; if( !p->locked && p->sharable ){ - sqlite3_mutex_enter(p->pBt->mutex); - p->locked = 1; + lockBtreeMutex(p); } } } @@ -305,11 +332,23 @@ void sqlite3BtreeMutexArrayLeave(BtreeMutexArray *pArray){ p->wantToLock--; if( p->wantToLock==0 && p->locked ){ - sqlite3_mutex_leave(p->pBt->mutex); - p->locked = 0; + unlockBtreeMutex(p); } } } - -#endif /* SQLITE_THREADSAFE && !SQLITE_OMIT_SHARED_CACHE */ +#else +void sqlite3BtreeEnter(Btree *p){ + p->pBt->db = p->db; +} +void sqlite3BtreeEnterAll(sqlite3 *db){ + int i; + for(i=0; inDb; i++){ + Btree *p = db->aDb[i].pBt; + if( p ){ + p->pBt->db = p->db; + } + } +} +#endif /* if SQLITE_THREADSAFE */ +#endif /* ifndef SQLITE_OMIT_SHARED_CACHE */ diff --git a/src/btree.c b/src/btree.c index c4bb534..d928559 100644 --- a/src/btree.c +++ b/src/btree.c @@ -9,7 +9,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** $Id: btree.c,v 1.565 2009/02/04 01:49:30 shane Exp $ +** $Id: btree.c,v 1.595 2009/04/11 16:06:15 danielk1977 Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** See the header comment on "btreeInt.h" for additional information. @@ -42,6 +42,8 @@ int sqlite3BtreeTrace=0; /* True to enable tracing */ ** in shared cache. This variable has file scope during normal builds, ** but the test harness needs to access it so we make it global for ** test builds. +** +** Access to this variable is protected by SQLITE_MUTEX_STATIC_MASTER. */ #ifdef SQLITE_TEST BtShared *SQLITE_WSD sqlite3SharedCacheList = 0; @@ -68,31 +70,32 @@ int sqlite3_enable_shared_cache(int enable){ /* ** Forward declaration */ -static int checkReadLocks(Btree*, Pgno, BtCursor*, i64); +static int checkForReadConflicts(Btree*, Pgno, BtCursor*, i64); #ifdef SQLITE_OMIT_SHARED_CACHE /* - ** The functions queryTableLock(), lockTable() and unlockAllTables() + ** The functions querySharedCacheTableLock(), setSharedCacheTableLock(), + ** and clearAllSharedCacheTableLocks() ** manipulate entries in the BtShared.pLock linked list used to store ** shared-cache table level locks. If the library is compiled with the ** shared-cache feature disabled, then there is only ever one user ** of each BtShared structure and so this locking is not necessary. ** So define the lock related functions as no-ops. */ - #define queryTableLock(a,b,c) SQLITE_OK - #define lockTable(a,b,c) SQLITE_OK - #define unlockAllTables(a) + #define querySharedCacheTableLock(a,b,c) SQLITE_OK + #define setSharedCacheTableLock(a,b,c) SQLITE_OK + #define clearAllSharedCacheTableLocks(a) #endif #ifndef SQLITE_OMIT_SHARED_CACHE /* ** Query to see if btree handle p may obtain a lock of type eLock ** (READ_LOCK or WRITE_LOCK) on the table with root-page iTab. Return -** SQLITE_OK if the lock may be obtained (by calling lockTable()), or -** SQLITE_LOCKED if not. +** SQLITE_OK if the lock may be obtained (by calling +** setSharedCacheTableLock()), or SQLITE_LOCKED if not. */ -static int queryTableLock(Btree *p, Pgno iTab, u8 eLock){ +static int querySharedCacheTableLock(Btree *p, Pgno iTab, u8 eLock){ BtShared *pBt = p->pBt; BtLock *pIter; @@ -100,6 +103,13 @@ static int queryTableLock(Btree *p, Pgno iTab, u8 eLock){ assert( eLock==READ_LOCK || eLock==WRITE_LOCK ); assert( p->db!=0 ); + /* If requesting a write-lock, then the Btree must have an open write + ** transaction on this file. And, obviously, for this to be so there + ** must be an open write transaction on the file itself. + */ + assert( eLock==READ_LOCK || (p==pBt->pWriter && p->inTrans==TRANS_WRITE) ); + assert( eLock==READ_LOCK || pBt->inTransaction==TRANS_WRITE ); + /* This is a no-op if the shared-cache is not enabled */ if( !p->sharable ){ return SQLITE_OK; @@ -108,23 +118,26 @@ static int queryTableLock(Btree *p, Pgno iTab, u8 eLock){ /* If some other connection is holding an exclusive lock, the ** requested lock may not be obtained. */ - if( pBt->pExclusive && pBt->pExclusive!=p ){ - return SQLITE_LOCKED; + if( pBt->pWriter!=p && pBt->isExclusive ){ + sqlite3ConnectionBlocked(p->db, pBt->pWriter->db); + return SQLITE_LOCKED_SHAREDCACHE; } - /* This (along with lockTable()) is where the ReadUncommitted flag is - ** dealt with. If the caller is querying for a read-lock and the flag is - ** set, it is unconditionally granted - even if there are write-locks + /* This (along with setSharedCacheTableLock()) is where + ** the ReadUncommitted flag is dealt with. + ** If the caller is querying for a read-lock on any table + ** other than the sqlite_master table (table 1) and if the ReadUncommitted + ** flag is set, then the lock granted even if there are write-locks ** on the table. If a write-lock is requested, the ReadUncommitted flag ** is not considered. ** - ** In function lockTable(), if a read-lock is demanded and the + ** In function setSharedCacheTableLock(), if a read-lock is demanded and the ** ReadUncommitted flag is set, no entry is added to the locks list ** (BtShared.pLock). ** - ** To summarize: If the ReadUncommitted flag is set, then read cursors do - ** not create or respect table locks. The locking procedure for a - ** write-cursor does not change. + ** To summarize: If the ReadUncommitted flag is set, then read cursors + ** on non-schema tables do not create or respect table locks. The locking + ** procedure for a write-cursor does not change. */ if( 0==(p->db->flags&SQLITE_ReadUncommitted) || @@ -132,9 +145,24 @@ static int queryTableLock(Btree *p, Pgno iTab, u8 eLock){ iTab==MASTER_ROOT ){ for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ - if( pIter->pBtree!=p && pIter->iTable==iTab && - (pIter->eLock!=eLock || eLock!=READ_LOCK) ){ - return SQLITE_LOCKED; + /* The condition (pIter->eLock!=eLock) in the following if(...) + ** statement is a simplification of: + ** + ** (eLock==WRITE_LOCK || pIter->eLock==WRITE_LOCK) + ** + ** since we know that if eLock==WRITE_LOCK, then no other connection + ** may hold a WRITE_LOCK on any table in this file (since there can + ** only be a single writer). + */ + assert( pIter->eLock==READ_LOCK || pIter->eLock==WRITE_LOCK ); + assert( eLock==READ_LOCK || pIter->pBtree==p || pIter->eLock==READ_LOCK); + if( pIter->pBtree!=p && pIter->iTable==iTab && pIter->eLock!=eLock ){ + sqlite3ConnectionBlocked(p->db, pIter->pBtree->db); + if( eLock==WRITE_LOCK ){ + assert( p==pBt->pWriter ); + pBt->isPending = 1; + } + return SQLITE_LOCKED_SHAREDCACHE; } } } @@ -151,7 +179,7 @@ static int queryTableLock(Btree *p, Pgno iTab, u8 eLock){ ** SQLITE_OK is returned if the lock is added successfully. SQLITE_BUSY and ** SQLITE_NOMEM may also be returned. */ -static int lockTable(Btree *p, Pgno iTable, u8 eLock){ +static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){ BtShared *pBt = p->pBt; BtLock *pLock = 0; BtLock *pIter; @@ -165,12 +193,13 @@ static int lockTable(Btree *p, Pgno iTable, u8 eLock){ return SQLITE_OK; } - assert( SQLITE_OK==queryTableLock(p, iTable, eLock) ); + assert( SQLITE_OK==querySharedCacheTableLock(p, iTable, eLock) ); - /* If the read-uncommitted flag is set and a read-lock is requested, - ** return early without adding an entry to the BtShared.pLock list. See - ** comment in function queryTableLock() for more info on handling - ** the ReadUncommitted flag. + /* If the read-uncommitted flag is set and a read-lock is requested on + ** a non-schema table, then the lock is always granted. Return early + ** without adding an entry to the BtShared.pLock list. See + ** comment in function querySharedCacheTableLock() for more info + ** on handling the ReadUncommitted flag. */ if( (p->db->flags&SQLITE_ReadUncommitted) && @@ -217,19 +246,25 @@ static int lockTable(Btree *p, Pgno iTable, u8 eLock){ #ifndef SQLITE_OMIT_SHARED_CACHE /* -** Release all the table locks (locks obtained via calls to the lockTable() -** procedure) held by Btree handle p. +** Release all the table locks (locks obtained via calls to +** the setSharedCacheTableLock() procedure) held by Btree handle p. +** +** This function assumes that handle p has an open read or write +** transaction. If it does not, then the BtShared.isPending variable +** may be incorrectly cleared. */ -static void unlockAllTables(Btree *p){ +static void clearAllSharedCacheTableLocks(Btree *p){ BtShared *pBt = p->pBt; BtLock **ppIter = &pBt->pLock; assert( sqlite3BtreeHoldsMutex(p) ); assert( p->sharable || 0==*ppIter ); + assert( p->inTrans>0 ); while( *ppIter ){ BtLock *pLock = *ppIter; - assert( pBt->pExclusive==0 || pBt->pExclusive==pLock->pBtree ); + assert( pBt->isExclusive==0 || pBt->pWriter==pLock->pBtree ); + assert( pLock->pBtree->inTrans>=pLock->eLock ); if( pLock->pBtree==p ){ *ppIter = pLock->pNext; sqlite3_free(pLock); @@ -238,8 +273,22 @@ static void unlockAllTables(Btree *p){ } } - if( pBt->pExclusive==p ){ - pBt->pExclusive = 0; + assert( pBt->isPending==0 || pBt->pWriter ); + if( pBt->pWriter==p ){ + pBt->pWriter = 0; + pBt->isExclusive = 0; + pBt->isPending = 0; + }else if( pBt->nTransaction==2 ){ + /* This function is called when connection p is concluding its + ** transaction. If there currently exists a writer, and p is not + ** that writer, then the number of locks held by connections other + ** than the writer must be about to drop to zero. In this case + ** set the isPending flag to 0. + ** + ** If there is not currently a writer, then BtShared.isPending must + ** be zero already. So this next line is harmless in that case. + */ + pBt->isPending = 0; } } #endif /* SQLITE_OMIT_SHARED_CACHE */ @@ -822,72 +871,74 @@ static int defragmentPage(MemPage *pPage){ } /* -** Allocate nByte bytes of space on a page. +** Allocate nByte bytes of space from within the B-Tree page passed +** as the first argument. Return the index into pPage->aData[] of the +** first byte of allocated space. ** -** Return the index into pPage->aData[] of the first byte of -** the new allocation. The caller guarantees that there is enough -** space. This routine will never fail. +** The caller guarantees that the space between the end of the cell-offset +** array and the start of the cell-content area is at least nByte bytes +** in size. So this routine can never fail. ** -** If the page contains nBytes of free space but does not contain -** nBytes of contiguous free space, then this routine automatically -** calls defragementPage() to consolidate all free space before -** allocating the new chunk. +** If there are already 60 or more bytes of fragments within the page, +** the page is defragmented before returning. If this were not done there +** is a chance that the number of fragmented bytes could eventually +** overflow the single-byte field of the page-header in which this value +** is stored. */ static int allocateSpace(MemPage *pPage, int nByte){ - int addr, pc, hdr; - int size; - int nFrag; + const int hdr = pPage->hdrOffset; /* Local cache of pPage->hdrOffset */ + u8 * const data = pPage->aData; /* Local cache of pPage->aData */ + int nFrag; /* Number of fragmented bytes on pPage */ int top; - int nCell; - int cellOffset; - unsigned char *data; - data = pPage->aData; assert( sqlite3PagerIswriteable(pPage->pDbPage) ); assert( pPage->pBt ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( nByte>=0 ); /* Minimum cell size is 4 */ assert( pPage->nFree>=nByte ); assert( pPage->nOverflow==0 ); - pPage->nFree -= (u16)nByte; - hdr = pPage->hdrOffset; + /* Assert that the space between the cell-offset array and the + ** cell-content area is greater than nByte bytes. + */ + assert( nByte <= ( + get2byte(&data[hdr+5])-(hdr+8+(pPage->leaf?0:4)+2*get2byte(&data[hdr+3])) + )); + + pPage->nFree -= (u16)nByte; nFrag = data[hdr+7]; - if( nFrag<60 ){ - /* Search the freelist looking for a slot big enough to satisfy the - ** space request. */ - addr = hdr+1; - while( (pc = get2byte(&data[addr]))>0 ){ - size = get2byte(&data[pc+2]); + if( nFrag>=60 ){ + defragmentPage(pPage); + }else{ + /* Search the freelist looking for a free slot big enough to satisfy + ** the request. The allocation is made from the first free slot in + ** the list that is large enough to accomadate it. + */ + int pc, addr; + for(addr=hdr+1; (pc = get2byte(&data[addr]))>0; addr=pc){ + int size = get2byte(&data[pc+2]); /* Size of free slot */ if( size>=nByte ){ int x = size - nByte; - if( sizecellOffset; - if( nFrag>=60 || cellOffset + 2*nCell > top - nByte ){ - defragmentPage(pPage); - top = get2byte(&data[hdr+5]); - } - top -= nByte; - assert( cellOffset + 2*nCell <= top ); + top = get2byte(&data[hdr+5]) - nByte; put2byte(&data[hdr+5], top); - assert( sqlite3PagerIswriteable(pPage->pDbPage) ); return top; } @@ -1275,10 +1326,17 @@ static void releasePage(MemPage *pPage){ static void pageReinit(DbPage *pData){ MemPage *pPage; pPage = (MemPage *)sqlite3PagerGetExtra(pData); + assert( sqlite3PagerPageRefcount(pData)>0 ); if( pPage->isInit ){ assert( sqlite3_mutex_held(pPage->pBt->mutex) ); pPage->isInit = 0; - if( sqlite3PagerPageRefcount(pData)>0 ){ + if( sqlite3PagerPageRefcount(pData)>1 ){ + /* pPage might not be a btree page; it might be an overflow page + ** or ptrmap page or a free page. In those cases, the following + ** call to sqlite3BtreeInitPage() will likely return SQLITE_CORRUPT. + ** But no harm is done by this. And it is very important that + ** sqlite3BtreeInitPage() be called on every btree page so we make + ** the call for every page that comes in for re-initing. */ sqlite3BtreeInitPage(pPage); } } @@ -1310,12 +1368,13 @@ int sqlite3BtreeOpen( int flags, /* Options */ int vfsFlags /* Flags passed through to sqlite3_vfs.xOpen() */ ){ - sqlite3_vfs *pVfs; /* The VFS to use for this btree */ - BtShared *pBt = 0; /* Shared part of btree structure */ - Btree *p; /* Handle to return */ - int rc = SQLITE_OK; - u8 nReserve; - unsigned char zDbHeader[100]; + sqlite3_vfs *pVfs; /* The VFS to use for this btree */ + BtShared *pBt = 0; /* Shared part of btree structure */ + Btree *p; /* Handle to return */ + sqlite3_mutex *mutexOpen = 0; /* Prevents a race condition. Ticket #3537 */ + int rc = SQLITE_OK; /* Result code from this function */ + u8 nReserve; /* Byte of unused space on each page */ + unsigned char zDbHeader[100]; /* Database header content */ /* Set the variable isMemdb to true for an in-memory database, or ** false for a file-based database. This symbol is only required if @@ -1346,10 +1405,7 @@ int sqlite3BtreeOpen( ** If this Btree is a candidate for shared cache, try to find an ** existing BtShared object that we can share with */ - if( isMemdb==0 - && (db->flags & SQLITE_Vtab)==0 - && zFilename && zFilename[0] - ){ + if( isMemdb==0 && zFilename && zFilename[0] ){ if( sqlite3GlobalConfig.sharedCacheEnabled ){ int nFullPathname = pVfs->mxPathname+1; char *zFullPathname = sqlite3Malloc(nFullPathname); @@ -1361,6 +1417,8 @@ int sqlite3BtreeOpen( return SQLITE_NOMEM; } sqlite3OsFullPathname(pVfs, zFilename, nFullPathname, zFullPathname); + mutexOpen = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_OPEN); + sqlite3_mutex_enter(mutexOpen); mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); sqlite3_mutex_enter(mutexShared); for(pBt=GLOBAL(BtShared*,sqlite3SharedCacheList); pBt; pBt=pBt->pNext){ @@ -1412,6 +1470,7 @@ int sqlite3BtreeOpen( if( rc!=SQLITE_OK ){ goto btree_open_out; } + pBt->db = db; sqlite3PagerSetBusyhandler(pBt->pPager, btreeInvokeBusyHandler, pBt); p->pBt = pBt; @@ -1514,6 +1573,10 @@ btree_open_out: sqlite3_free(p); *ppBtree = 0; } + if( mutexOpen ){ + assert( sqlite3_mutex_held(mutexOpen) ); + sqlite3_mutex_leave(mutexOpen); + } return rc; } @@ -1585,7 +1648,6 @@ int sqlite3BtreeClose(Btree *p){ /* Close all cursors opened via this handle. */ assert( sqlite3_mutex_held(p->db->mutex) ); sqlite3BtreeEnter(p); - pBt->db = p->db; pCur = pBt->pCursor; while( pCur ){ BtCursor *pTmp = pCur; @@ -1695,6 +1757,8 @@ int sqlite3BtreeSyncDisabled(Btree *p){ #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM) /* ** Change the default pages size and the number of reserved bytes per page. +** Or, if the page size has already been fixed, return SQLITE_READONLY +** without changing anything. ** ** The page size must be a power of 2 between 512 and 65536. If the page ** size supplied does not meet this constraint then the page size is not @@ -1707,8 +1771,11 @@ int sqlite3BtreeSyncDisabled(Btree *p){ ** ** If parameter nReserve is less than zero, then the number of reserved ** bytes per page is left unchanged. +** +** If the iFix!=0 then the pageSizeFixed flag is set so that the page size +** and autovacuum mode can no longer be changed. */ -int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve){ +int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){ int rc = SQLITE_OK; BtShared *pBt = p->pBt; assert( nReserve>=-1 && nReserve<=255 ); @@ -1730,6 +1797,7 @@ int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve){ rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize); } pBt->usableSize = pBt->pageSize - (u16)nReserve; + if( iFix ) pBt->pageSizeFixed = 1; sqlite3BtreeLeave(p); return rc; } @@ -1740,6 +1808,12 @@ int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve){ int sqlite3BtreeGetPageSize(Btree *p){ return p->pBt->pageSize; } + +/* +** Return the number of bytes of space at the end of every page that +** are intentually left unused. This is the "reserved" space that is +** sometimes used by extensions. +*/ int sqlite3BtreeGetReserve(Btree *p){ int n; sqlite3BtreeEnter(p); @@ -1774,13 +1848,14 @@ int sqlite3BtreeSetAutoVacuum(Btree *p, int autoVacuum){ #else BtShared *pBt = p->pBt; int rc = SQLITE_OK; - u8 av = autoVacuum ?1:0; + u8 av = (u8)autoVacuum; sqlite3BtreeEnter(p); - if( pBt->pageSizeFixed && av!=pBt->autoVacuum ){ + if( pBt->pageSizeFixed && (av ?1:0)!=pBt->autoVacuum ){ rc = SQLITE_READONLY; }else{ - pBt->autoVacuum = av; + pBt->autoVacuum = av ?1:0; + pBt->incrVacuum = av==2 ?1:0; } sqlite3BtreeLeave(p); return rc; @@ -1823,7 +1898,7 @@ static int lockBtree(BtShared *pBt){ int nPage; assert( sqlite3_mutex_held(pBt->mutex) ); - if( pBt->pPage1 ) return SQLITE_OK; + assert( pBt->pPage1==0 ); rc = sqlite3BtreeGetPage(pBt, 1, &pPage1, 0); if( rc!=SQLITE_OK ) return rc; @@ -1965,7 +2040,6 @@ static void unlockBtreeIfUnused(BtShared *pBt){ releasePage(pBt->pPage1); } pBt->pPage1 = 0; - pBt->inStmt = 0; } } @@ -2047,11 +2121,11 @@ static int newDatabase(BtShared *pBt){ ** proceed. */ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){ + sqlite3 *pBlock = 0; BtShared *pBt = p->pBt; int rc = SQLITE_OK; sqlite3BtreeEnter(p); - pBt->db = p->db; btreeIntegrity(p); /* If the btree is already in a write-transaction, or it @@ -2068,33 +2142,38 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){ goto trans_begun; } +#ifndef SQLITE_OMIT_SHARED_CACHE /* If another database handle has already opened a write transaction ** on this shared-btree structure and a second write transaction is - ** requested, return SQLITE_BUSY. + ** requested, return SQLITE_LOCKED. */ - if( pBt->inTransaction==TRANS_WRITE && wrflag ){ - rc = SQLITE_BUSY; - goto trans_begun; - } - -#ifndef SQLITE_OMIT_SHARED_CACHE - if( wrflag>1 ){ + if( (wrflag && pBt->inTransaction==TRANS_WRITE) || pBt->isPending ){ + pBlock = pBt->pWriter->db; + }else if( wrflag>1 ){ BtLock *pIter; for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ if( pIter->pBtree!=p ){ - rc = SQLITE_BUSY; - goto trans_begun; + pBlock = pIter->pBtree->db; + break; } } } + if( pBlock ){ + sqlite3ConnectionBlocked(p->db, pBlock); + rc = SQLITE_LOCKED_SHAREDCACHE; + goto trans_begun; + } #endif do { - if( pBt->pPage1==0 ){ - do{ - rc = lockBtree(pBt); - }while( pBt->pPage1==0 && rc==SQLITE_OK ); - } + /* Call lockBtree() until either pBt->pPage1 is populated or + ** lockBtree() returns something other than SQLITE_OK. lockBtree() + ** may return SQLITE_OK but leave pBt->pPage1 set to 0 if after + ** reading page 1 it discovers that the page-size of the database + ** file is not pBt->pageSize. In this case lockBtree() will update + ** pBt->pageSize to the page-size of the file on disk. + */ + while( pBt->pPage1==0 && SQLITE_OK==(rc = lockBtree(pBt)) ); if( rc==SQLITE_OK && wrflag ){ if( pBt->readOnly ){ @@ -2107,9 +2186,7 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){ } } - if( rc==SQLITE_OK ){ - if( wrflag ) pBt->inStmt = 0; - }else{ + if( rc!=SQLITE_OK ){ unlockBtreeIfUnused(pBt); } }while( rc==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE && @@ -2124,9 +2201,10 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){ pBt->inTransaction = p->inTrans; } #ifndef SQLITE_OMIT_SHARED_CACHE - if( wrflag>1 ){ - assert( !pBt->pExclusive ); - pBt->pExclusive = p; + if( wrflag ){ + assert( !pBt->pWriter ); + pBt->pWriter = p; + pBt->isExclusive = (u8)(wrflag>1); } #endif } @@ -2194,7 +2272,7 @@ set_child_ptrmaps_out: } /* -** Somewhere on pPage, which is guarenteed to be a btree page, not an overflow +** Somewhere on pPage, which is guaranteed to be a btree page, not an overflow ** page, is a pointer to page iFrom. Modify this pointer so that it points to ** iTo. Parameter eType describes the type of pointer to be modified, as ** follows: @@ -2357,6 +2435,7 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){ Pgno nFreeList; /* Number of pages still on the free-list */ assert( sqlite3_mutex_held(pBt->mutex) ); + assert( iLastPg>nFin ); if( !PTRMAP_ISPAGE(pBt, iLastPg) && iLastPg!=PENDING_BYTE_PAGE(pBt) ){ int rc; @@ -2364,7 +2443,7 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){ Pgno iPtrPage; nFreeList = get4byte(&pBt->pPage1->aData[36]); - if( nFreeList==0 || nFin==iLastPg ){ + if( nFreeList==0 ){ return SQLITE_DONE; } @@ -2433,6 +2512,18 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){ if( nFin==0 ){ iLastPg--; while( iLastPg==PENDING_BYTE_PAGE(pBt)||PTRMAP_ISPAGE(pBt, iLastPg) ){ + if( PTRMAP_ISPAGE(pBt, iLastPg) ){ + MemPage *pPg; + int rc = sqlite3BtreeGetPage(pBt, iLastPg, &pPg, 0); + if( rc!=SQLITE_OK ){ + return rc; + } + rc = sqlite3PagerWrite(pPg->pDbPage); + releasePage(pPg); + if( rc!=SQLITE_OK ){ + return rc; + } + } iLastPg--; } sqlite3PagerTruncateImage(pBt->pPager, iLastPg); @@ -2445,7 +2536,7 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){ ** It performs a single unit of work towards an incremental vacuum. ** ** If the incremental vacuum is finished after this function has run, -** SQLITE_DONE is returned. If it is not finished, but no error occured, +** SQLITE_DONE is returned. If it is not finished, but no error occurred, ** SQLITE_OK is returned. Otherwise an SQLite error code. */ int sqlite3BtreeIncrVacuum(Btree *p){ @@ -2453,7 +2544,6 @@ int sqlite3BtreeIncrVacuum(Btree *p){ BtShared *pBt = p->pBt; sqlite3BtreeEnter(p); - pBt->db = p->db; assert( pBt->inTransaction==TRANS_WRITE && p->inTrans==TRANS_WRITE ); if( !pBt->autoVacuum ){ rc = SQLITE_DONE; @@ -2490,16 +2580,18 @@ static int autoVacuumCommit(BtShared *pBt){ const int pgsz = pBt->pageSize; Pgno nOrig = pagerPagecount(pBt); - if( PTRMAP_ISPAGE(pBt, nOrig) ){ + if( PTRMAP_ISPAGE(pBt, nOrig) || nOrig==PENDING_BYTE_PAGE(pBt) ){ + /* It is not possible to create a database for which the final page + ** is either a pointer-map page or the pending-byte page. If one + ** is encountered, this indicates corruption. + */ return SQLITE_CORRUPT_BKPT; } - if( nOrig==PENDING_BYTE_PAGE(pBt) ){ - nOrig--; - } + nFree = get4byte(&pBt->pPage1->aData[36]); nPtrmap = (nFree-nOrig+PTRMAP_PAGENO(pBt, nOrig)+pgsz/5)/(pgsz/5); nFin = nOrig - nFree - nPtrmap; - if( nOrig>PENDING_BYTE_PAGE(pBt) && nFin<=PENDING_BYTE_PAGE(pBt) ){ + if( nOrig>PENDING_BYTE_PAGE(pBt) && nFininTrans==TRANS_WRITE ){ BtShared *pBt = p->pBt; sqlite3BtreeEnter(p); - pBt->db = p->db; #ifndef SQLITE_OMIT_AUTOVACUUM if( pBt->autoVacuum ){ rc = autoVacuumCommit(pBt); @@ -2592,7 +2683,6 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p){ BtShared *pBt = p->pBt; sqlite3BtreeEnter(p); - pBt->db = p->db; btreeIntegrity(p); /* If the handle has a write-transaction open, commit the shared-btrees @@ -2608,9 +2698,7 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p){ return rc; } pBt->inTransaction = TRANS_READ; - pBt->inStmt = 0; } - unlockAllTables(p); /* If the handle has any kind of transaction open, decrement the transaction ** count of the shared btree. If the transaction count reaches 0, set @@ -2618,6 +2706,7 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p){ ** will unlock the pager. */ if( p->inTrans!=TRANS_NONE ){ + clearAllSharedCacheTableLocks(p); pBt->nTransaction--; if( 0==pBt->nTransaction ){ pBt->inTransaction = TRANS_NONE; @@ -2719,11 +2808,10 @@ int sqlite3BtreeRollback(Btree *p){ MemPage *pPage1; sqlite3BtreeEnter(p); - pBt->db = p->db; rc = saveAllCursors(pBt, 0, 0); #ifndef SQLITE_OMIT_SHARED_CACHE if( rc!=SQLITE_OK ){ - /* This is a horrible situation. An IO or malloc() error occured whilst + /* This is a horrible situation. An IO or malloc() error occurred whilst ** trying to save cursor positions. If this is an automatic rollback (as ** the result of a constraint, malloc() failure or IO error) then ** the cache may be internally inconsistent (not contain valid trees) so @@ -2734,7 +2822,6 @@ int sqlite3BtreeRollback(Btree *p){ } #endif btreeIntegrity(p); - unlockAllTables(p); if( p->inTrans==TRANS_WRITE ){ int rc2; @@ -2756,6 +2843,7 @@ int sqlite3BtreeRollback(Btree *p){ } if( p->inTrans!=TRANS_NONE ){ + clearAllSharedCacheTableLocks(p); assert( pBt->nTransaction>0 ); pBt->nTransaction--; if( 0==pBt->nTransaction ){ @@ -2765,7 +2853,6 @@ int sqlite3BtreeRollback(Btree *p){ btreeClearHasContent(pBt); p->inTrans = TRANS_NONE; - pBt->inStmt = 0; unlockBtreeIfUnused(pBt); btreeIntegrity(p); @@ -2774,29 +2861,32 @@ int sqlite3BtreeRollback(Btree *p){ } /* -** Start a statement subtransaction. The subtransaction can -** can be rolled back independently of the main transaction. -** You must start a transaction before starting a subtransaction. -** The subtransaction is ended automatically if the main transaction -** commits or rolls back. -** -** Only one subtransaction may be active at a time. It is an error to try -** to start a new subtransaction if another subtransaction is already active. +** Start a statement subtransaction. The subtransaction can can be rolled +** back independently of the main transaction. You must start a transaction +** before starting a subtransaction. The subtransaction is ended automatically +** if the main transaction commits or rolls back. ** ** Statement subtransactions are used around individual SQL statements ** that are contained within a BEGIN...COMMIT block. If a constraint ** error occurs within the statement, the effect of that one statement ** can be rolled back without having to rollback the entire transaction. +** +** A statement sub-transaction is implemented as an anonymous savepoint. The +** value passed as the second parameter is the total number of savepoints, +** including the new anonymous savepoint, open on the B-Tree. i.e. if there +** are no active savepoints and no other statement-transactions open, +** iStatement is 1. This anonymous savepoint can be released or rolled back +** using the sqlite3BtreeSavepoint() function. */ -int sqlite3BtreeBeginStmt(Btree *p){ +int sqlite3BtreeBeginStmt(Btree *p, int iStatement){ int rc; BtShared *pBt = p->pBt; sqlite3BtreeEnter(p); - pBt->db = p->db; assert( p->inTrans==TRANS_WRITE ); - assert( !pBt->inStmt ); assert( pBt->readOnly==0 ); - if( NEVER(p->inTrans!=TRANS_WRITE || pBt->inStmt || pBt->readOnly) ){ + assert( iStatement>0 ); + assert( iStatement>p->db->nSavepoint ); + if( NEVER(p->inTrans!=TRANS_WRITE || pBt->readOnly) ){ rc = SQLITE_INTERNAL; }else{ assert( pBt->inTransaction==TRANS_WRITE ); @@ -2805,55 +2895,7 @@ int sqlite3BtreeBeginStmt(Btree *p){ ** SQL statements. It is illegal to open, release or rollback any ** such savepoints while the statement transaction savepoint is active. */ - rc = sqlite3PagerOpenSavepoint(pBt->pPager, p->db->nSavepoint+1); - pBt->inStmt = 1; - } - sqlite3BtreeLeave(p); - return rc; -} - -/* -** Commit the statment subtransaction currently in progress. If no -** subtransaction is active, this is a no-op. -*/ -int sqlite3BtreeCommitStmt(Btree *p){ - int rc; - BtShared *pBt = p->pBt; - sqlite3BtreeEnter(p); - pBt->db = p->db; - assert( pBt->readOnly==0 ); - if( pBt->inStmt ){ - int iStmtpoint = p->db->nSavepoint; - rc = sqlite3PagerSavepoint(pBt->pPager, SAVEPOINT_RELEASE, iStmtpoint); - }else{ - rc = SQLITE_OK; - } - pBt->inStmt = 0; - sqlite3BtreeLeave(p); - return rc; -} - -/* -** Rollback the active statement subtransaction. If no subtransaction -** is active this routine is a no-op. -** -** All cursors will be invalidated by this operation. Any attempt -** to use a cursor that was open at the beginning of this operation -** will result in an error. -*/ -int sqlite3BtreeRollbackStmt(Btree *p){ - int rc = SQLITE_OK; - BtShared *pBt = p->pBt; - sqlite3BtreeEnter(p); - pBt->db = p->db; - assert( pBt->readOnly==0 ); - if( pBt->inStmt ){ - int iStmtpoint = p->db->nSavepoint; - rc = sqlite3PagerSavepoint(pBt->pPager, SAVEPOINT_ROLLBACK, iStmtpoint); - if( rc==SQLITE_OK ){ - rc = sqlite3PagerSavepoint(pBt->pPager, SAVEPOINT_RELEASE, iStmtpoint); - } - pBt->inStmt = 0; + rc = sqlite3PagerOpenSavepoint(pBt->pPager, iStatement); } sqlite3BtreeLeave(p); return rc; @@ -2875,11 +2917,9 @@ int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){ int rc = SQLITE_OK; if( p && p->inTrans==TRANS_WRITE ){ BtShared *pBt = p->pBt; - assert( pBt->inStmt==0 ); assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK ); assert( iSavepoint>=0 || (iSavepoint==-1 && op==SAVEPOINT_ROLLBACK) ); sqlite3BtreeEnter(p); - pBt->db = p->db; rc = sqlite3PagerSavepoint(pBt->pPager, op, iSavepoint); if( rc==SQLITE_OK ){ rc = newDatabase(pBt); @@ -2937,8 +2977,10 @@ static int btreeCursor( if( NEVER(pBt->readOnly) ){ return SQLITE_READONLY; } - if( checkReadLocks(p, iTable, 0, 0) ){ - return SQLITE_LOCKED; + rc = checkForReadConflicts(p, iTable, 0, 0); + if( rc!=SQLITE_OK ){ + assert( rc==SQLITE_LOCKED_SHAREDCACHE ); + return rc; } } @@ -2976,6 +3018,7 @@ static int btreeCursor( } pBt->pCursor = pCur; pCur->eState = CURSOR_INVALID; + pCur->cachedRowid = 0; return SQLITE_OK; @@ -2993,16 +3036,52 @@ int sqlite3BtreeCursor( ){ int rc; sqlite3BtreeEnter(p); - p->pBt->db = p->db; rc = btreeCursor(p, iTable, wrFlag, pKeyInfo, pCur); sqlite3BtreeLeave(p); return rc; } -int sqlite3BtreeCursorSize(){ + +/* +** Return the size of a BtCursor object in bytes. +** +** This interfaces is needed so that users of cursors can preallocate +** sufficient storage to hold a cursor. The BtCursor object is opaque +** to users so they cannot do the sizeof() themselves - they must call +** this routine. +*/ +int sqlite3BtreeCursorSize(void){ return sizeof(BtCursor); } +/* +** Set the cached rowid value of every cursor in the same database file +** as pCur and having the same root page number as pCur. The value is +** set to iRowid. +** +** Only positive rowid values are considered valid for this cache. +** The cache is initialized to zero, indicating an invalid cache. +** A btree will work fine with zero or negative rowids. We just cannot +** cache zero or negative rowids, which means tables that use zero or +** negative rowids might run a little slower. But in practice, zero +** or negative rowids are very uncommon so this should not be a problem. +*/ +void sqlite3BtreeSetCachedRowid(BtCursor *pCur, sqlite3_int64 iRowid){ + BtCursor *p; + for(p=pCur->pBt->pCursor; p; p=p->pNext){ + if( p->pgnoRoot==pCur->pgnoRoot ) p->cachedRowid = iRowid; + } + assert( pCur->cachedRowid==iRowid ); +} +/* +** Return the cached rowid for the given cursor. A negative or zero +** return value indicates that the rowid cache is invalid and should be +** ignored. If the rowid cache has never before been set, then a +** zero is returned. +*/ +sqlite3_int64 sqlite3BtreeGetCachedRowid(BtCursor *pCur){ + return pCur->cachedRowid; +} /* ** Close a cursor. The read lock on the database file is released @@ -3014,7 +3093,6 @@ int sqlite3BtreeCloseCursor(BtCursor *pCur){ int i; BtShared *pBt = pCur->pBt; sqlite3BtreeEnter(pBtree); - pBt->db = pBtree->db; sqlite3BtreeClearCursor(pCur); if( pCur->pPrev ){ pCur->pPrev->pNext = pCur->pNext; @@ -3064,6 +3142,8 @@ void sqlite3BtreeReleaseTempCursor(BtCursor *pCur){ sqlite3_free(pCur->pKey); } + + /* ** Make sure the BtCursor* given in the argument has a valid ** BtCursor.info structure. If it is not already valid, call @@ -3501,7 +3581,7 @@ int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ ** and data to fit on the local page and for there to be no overflow ** pages. When that is so, this routine can be used to access the ** key and data without making a copy. If the key and/or data spills -** onto overflow pages, then accessPayload() must be used to reassembly +** onto overflow pages, then accessPayload() must be used to reassemble ** the key/data and copy it into a preallocated buffer. ** ** The pointer returned by this routine looks directly into the cached @@ -3990,7 +4070,8 @@ int sqlite3BtreeMoveto( ){ int rc; /* Status code */ UnpackedRecord *pIdxKey; /* Unpacked index key */ - UnpackedRecord aSpace[16]; /* Temp space for pIdxKey - to avoid a malloc */ + char aSpace[150]; /* Temp space for pIdxKey - to avoid a malloc */ + if( pKey ){ assert( nKey==(i64)(int)nKey ); @@ -4391,8 +4472,15 @@ static int allocateBtreePage( ** at the end of the file instead of one. The first allocated page ** becomes a new pointer-map page, the second is used by the caller. */ + MemPage *pPg = 0; TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", *pPgno)); assert( *pPgno!=PENDING_BYTE_PAGE(pBt) ); + rc = sqlite3BtreeGetPage(pBt, *pPgno, &pPg, 0); + if( rc==SQLITE_OK ){ + rc = sqlite3PagerWrite(pPg->pDbPage); + releasePage(pPg); + } + if( rc ) return rc; (*pPgno)++; if( *pPgno==PENDING_BYTE_PAGE(pBt) ){ (*pPgno)++; } } @@ -4584,7 +4672,10 @@ static int clearCell(MemPage *pPage, unsigned char *pCell){ while( nOvfl-- ){ Pgno iNext = 0; MemPage *pOvfl = 0; - if( ovflPgno==0 || ovflPgno>pagerPagecount(pBt) ){ + if( ovflPgno<2 || ovflPgno>pagerPagecount(pBt) ){ + /* 0 is not a legal page number and page 1 cannot be an + ** overflow page. Therefore if ovflPgno<2 or past the end of the + ** file the database must be corrupt. */ return SQLITE_CORRUPT_BKPT; } if( nOvfl ){ @@ -5263,13 +5354,13 @@ static int balance_nonroot(BtCursor *pCur){ } szCell = (u16*)&apCell[nMaxCells]; aCopy[0] = (u8*)&szCell[nMaxCells]; - assert( ((aCopy[0] - (u8*)0) & 7)==0 ); /* 8-byte alignment required */ + assert( EIGHT_BYTE_ALIGNMENT(aCopy[0]) ); for(i=1; ipageSize+ROUND8(sizeof(MemPage))]; assert( ((aCopy[i] - (u8*)0) & 7)==0 ); /* 8-byte alignment required */ } aSpace1 = &aCopy[NB-1][pBt->pageSize+ROUND8(sizeof(MemPage))]; - assert( ((aSpace1 - (u8*)0) & 7)==0 ); /* 8-byte alignment required */ + assert( EIGHT_BYTE_ALIGNMENT(aSpace1) ); if( ISAUTOVACUUM ){ aFrom = &aSpace1[pBt->pageSize]; } @@ -5913,9 +6004,9 @@ static int balance(BtCursor *pCur, int isInsert){ ** is not in the ReadUncommmitted state, then this routine returns ** SQLITE_LOCKED. ** -** As well as cursors with wrFlag==0, cursors with wrFlag==1 and -** isIncrblobHandle==1 are also considered 'read' cursors. Incremental -** blob cursors are used for both reading and writing. +** As well as cursors with wrFlag==0, cursors with +** isIncrblobHandle==1 are also considered 'read' cursors because +** incremental blob cursors are used for both reading and writing. ** ** When pgnoRoot is the root page of an intkey table, this function is also ** responsible for invalidating incremental blob cursors when the table row @@ -5937,11 +6028,11 @@ static int balance(BtCursor *pCur, int isInsert){ ** 3) If both pExclude and iRow are set to zero, no incremental blob ** cursors are invalidated. */ -static int checkReadLocks( - Btree *pBtree, - Pgno pgnoRoot, - BtCursor *pExclude, - i64 iRow +static int checkForReadConflicts( + Btree *pBtree, /* The database file to check */ + Pgno pgnoRoot, /* Look for read cursors on this btree */ + BtCursor *pExclude, /* Ignore this cursor */ + i64 iRow /* The rowid that might be changing */ ){ BtCursor *p; BtShared *pBt = pBtree->pBt; @@ -5965,9 +6056,10 @@ static int checkReadLocks( #endif ){ sqlite3 *dbOther = p->pBtree->db; - if( dbOther==0 || - (dbOther!=db && (dbOther->flags & SQLITE_ReadUncommitted)==0) ){ - return SQLITE_LOCKED; + assert(dbOther); + if( dbOther!=db && (dbOther->flags & SQLITE_ReadUncommitted)==0 ){ + sqlite3ConnectionBlocked(db, dbOther); + return SQLITE_LOCKED_SHAREDCACHE; } } } @@ -6004,8 +6096,11 @@ int sqlite3BtreeInsert( assert( pBt->inTransaction==TRANS_WRITE ); assert( !pBt->readOnly ); assert( pCur->wrFlag ); - if( checkReadLocks(pCur->pBtree, pCur->pgnoRoot, pCur, nKey) ){ - return SQLITE_LOCKED; /* The table pCur points to has a read lock */ + rc = checkForReadConflicts(pCur->pBtree, pCur->pgnoRoot, pCur, nKey); + if( rc ){ + /* The table pCur points to has a read lock */ + assert( rc==SQLITE_LOCKED_SHAREDCACHE ); + return rc; } if( pCur->eState==CURSOR_FAULT ){ return pCur->skip; @@ -6101,8 +6196,11 @@ int sqlite3BtreeDelete(BtCursor *pCur){ return SQLITE_ERROR; /* The cursor is not pointing to anything */ } assert( pCur->wrFlag ); - if( checkReadLocks(pCur->pBtree, pCur->pgnoRoot, pCur, pCur->info.nKey) ){ - return SQLITE_LOCKED; /* The table pCur points to has a read lock */ + rc = checkForReadConflicts(p, pCur->pgnoRoot, pCur, pCur->info.nKey); + if( rc!=SQLITE_OK ){ + /* The table pCur points to has a read lock */ + assert( rc==SQLITE_LOCKED_SHAREDCACHE ); + return rc; } /* Restore the current cursor position (a no-op if the cursor is not in @@ -6248,8 +6346,8 @@ int sqlite3BtreeDelete(BtCursor *pCur){ rc = sqlite3BtreeNext(&leafCur, ¬Used); } pLeafPage = leafCur.apPage[leafCur.iPage]; - assert( pLeafPage->pgno==leafPgno ); - assert( leafCur.aiIdx[leafCur.iPage]==0 ); + assert( rc!=SQLITE_OK || pLeafPage->pgno==leafPgno ); + assert( rc!=SQLITE_OK || leafCur.aiIdx[leafCur.iPage]==0 ); } if( SQLITE_OK==rc @@ -6413,7 +6511,6 @@ static int btreeCreateTable(Btree *p, int *piTable, int flags){ int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){ int rc; sqlite3BtreeEnter(p); - p->pBt->db = p->db; rc = btreeCreateTable(p, piTable, flags); sqlite3BtreeLeave(p); return rc; @@ -6485,9 +6582,8 @@ int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){ int rc; BtShared *pBt = p->pBt; sqlite3BtreeEnter(p); - pBt->db = p->db; assert( p->inTrans==TRANS_WRITE ); - if( (rc = checkReadLocks(p, iTable, 0, 1))!=SQLITE_OK ){ + if( (rc = checkForReadConflicts(p, iTable, 0, 1))!=SQLITE_OK ){ /* nothing to do */ }else if( SQLITE_OK!=(rc = saveAllCursors(pBt, iTable, 0)) ){ /* nothing to do */ @@ -6533,7 +6629,8 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){ ** occur. */ if( pBt->pCursor ){ - return SQLITE_LOCKED; + sqlite3ConnectionBlocked(p->db, pBt->pCursor->pBtree->db); + return SQLITE_LOCKED_SHAREDCACHE; } rc = sqlite3BtreeGetPage(pBt, (Pgno)iTable, &pPage, 0); @@ -6626,7 +6723,6 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){ int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){ int rc; sqlite3BtreeEnter(p); - p->pBt->db = p->db; rc = btreeDropTable(p, iTable, piMoved); sqlite3BtreeLeave(p); return rc; @@ -6650,14 +6746,14 @@ int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){ BtShared *pBt = p->pBt; sqlite3BtreeEnter(p); - pBt->db = p->db; /* Reading a meta-data value requires a read-lock on page 1 (and hence ** the sqlite_master table. We grab this lock regardless of whether or ** not the SQLITE_ReadUncommitted flag is set (the table rooted at page - ** 1 is treated as a special case by queryTableLock() and lockTable()). + ** 1 is treated as a special case by querySharedCacheTableLock() + ** and setSharedCacheTableLock()). */ - rc = queryTableLock(p, 1, READ_LOCK); + rc = querySharedCacheTableLock(p, 1, READ_LOCK); if( rc!=SQLITE_OK ){ sqlite3BtreeLeave(p); return rc; @@ -6698,8 +6794,14 @@ int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){ if( idx==4 && *pMeta>0 ) pBt->readOnly = 1; #endif - /* Grab the read-lock on page 1. */ - rc = lockTable(p, 1, READ_LOCK); + /* If there is currently an open transaction, grab a read-lock + ** on page 1 of the database file. This is done to make sure that + ** no other connection can modify the meta value just read from + ** the database until the transaction is concluded. + */ + if( p->inTrans>0 ){ + rc = setSharedCacheTableLock(p, 1, READ_LOCK); + } sqlite3BtreeLeave(p); return rc; } @@ -6714,7 +6816,6 @@ int sqlite3BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){ int rc; assert( idx>=1 && idx<=15 ); sqlite3BtreeEnter(p); - pBt->db = p->db; assert( p->inTrans==TRANS_WRITE ); assert( pBt->pPage1!=0 ); pP1 = pBt->pPage1->aData; @@ -6750,6 +6851,75 @@ int sqlite3BtreeFlags(BtCursor *pCur){ return pPage->aData[pPage->hdrOffset]; } +#ifndef SQLITE_OMIT_BTREECOUNT +/* +** The first argument, pCur, is a cursor opened on some b-tree. Count the +** number of entries in the b-tree and write the result to *pnEntry. +** +** SQLITE_OK is returned if the operation is successfully executed. +** Otherwise, if an error is encountered (i.e. an IO error or database +** corruption) an SQLite error code is returned. +*/ +int sqlite3BtreeCount(BtCursor *pCur, i64 *pnEntry){ + i64 nEntry = 0; /* Value to return in *pnEntry */ + int rc; /* Return code */ + rc = moveToRoot(pCur); + + /* Unless an error occurs, the following loop runs one iteration for each + ** page in the B-Tree structure (not including overflow pages). + */ + while( rc==SQLITE_OK ){ + int iIdx; /* Index of child node in parent */ + MemPage *pPage; /* Current page of the b-tree */ + + /* If this is a leaf page or the tree is not an int-key tree, then + ** this page contains countable entries. Increment the entry counter + ** accordingly. + */ + pPage = pCur->apPage[pCur->iPage]; + if( pPage->leaf || !pPage->intKey ){ + nEntry += pPage->nCell; + } + + /* pPage is a leaf node. This loop navigates the cursor so that it + ** points to the first interior cell that it points to the parent of + ** the next page in the tree that has not yet been visited. The + ** pCur->aiIdx[pCur->iPage] value is set to the index of the parent cell + ** of the page, or to the number of cells in the page if the next page + ** to visit is the right-child of its parent. + ** + ** If all pages in the tree have been visited, return SQLITE_OK to the + ** caller. + */ + if( pPage->leaf ){ + do { + if( pCur->iPage==0 ){ + /* All pages of the b-tree have been visited. Return successfully. */ + *pnEntry = nEntry; + return SQLITE_OK; + } + sqlite3BtreeMoveToParent(pCur); + }while ( pCur->aiIdx[pCur->iPage]>=pCur->apPage[pCur->iPage]->nCell ); + + pCur->aiIdx[pCur->iPage]++; + pPage = pCur->apPage[pCur->iPage]; + } + + /* Descend to the child node of the cell that the cursor currently + ** points at. This is the right-child if (iIdx==pPage->nCell). + */ + iIdx = pCur->aiIdx[pCur->iPage]; + if( iIdx==pPage->nCell ){ + rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8])); + }else{ + rc = moveToChild(pCur, get4byte(findCell(pPage, iIdx))); + } + } + + /* An error has occurred. Return an error code. */ + return rc; +} +#endif /* ** Return the pager associated with a BTree. This routine is used for @@ -6986,7 +7156,9 @@ static int checkTreePage( sz = info.nData; if( !pPage->intKey ) sz += (int)info.nKey; assert( sz==info.nPayload ); - if( sz>info.nLocal ){ + if( (sz>info.nLocal) + && (&pCell[info.iOverflow]<=&pPage->aData[pBt->usableSize]) + ){ int nPage = (sz - info.nLocal + usableSize - 5)/(usableSize - 4); Pgno pgnoOvfl = get4byte(&pCell[info.iOverflow]); #ifndef SQLITE_OMIT_AUTOVACUUM @@ -7117,7 +7289,6 @@ char *sqlite3BtreeIntegrityCheck( char zErr[100]; sqlite3BtreeEnter(p); - pBt->db = p->db; nRef = sqlite3PagerRefcount(pBt->pPager); if( lockBtreeWithRetry(p)!=SQLITE_OK ){ *pnErr = 1; @@ -7248,14 +7419,6 @@ int sqlite3BtreeIsInTrans(Btree *p){ return (p && (p->inTrans==TRANS_WRITE)); } -/* -** Return non-zero if a statement transaction is active. -*/ -int sqlite3BtreeIsInStmt(Btree *p){ - assert( sqlite3BtreeHoldsMutex(p) ); - return ALWAYS(p->pBt) && p->pBt->inStmt; -} - /* ** Return non-zero if a read (or write) transaction is active. */ @@ -7303,14 +7466,16 @@ void *sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){ } /* -** Return true if another user of the same shared btree as the argument -** handle holds an exclusive lock on the sqlite_master table. +** Return SQLITE_LOCKED_SHAREDCACHE if another user of the same shared +** btree as the argument handle holds an exclusive lock on the +** sqlite_master table. Otherwise SQLITE_OK. */ int sqlite3BtreeSchemaLocked(Btree *p){ int rc; assert( sqlite3_mutex_held(p->db->mutex) ); sqlite3BtreeEnter(p); - rc = (queryTableLock(p, MASTER_ROOT, READ_LOCK)!=SQLITE_OK); + rc = querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK); + assert( rc==SQLITE_OK || rc==SQLITE_LOCKED_SHAREDCACHE ); sqlite3BtreeLeave(p); return rc; } @@ -7329,9 +7494,9 @@ int sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){ assert( READ_LOCK+1==WRITE_LOCK ); assert( isWriteLock==0 || isWriteLock==1 ); sqlite3BtreeEnter(p); - rc = queryTableLock(p, iTab, lockType); + rc = querySharedCacheTableLock(p, iTab, lockType); if( rc==SQLITE_OK ){ - rc = lockTable(p, iTab, lockType); + rc = setSharedCacheTableLock(p, iTab, lockType); } sqlite3BtreeLeave(p); } @@ -7348,6 +7513,8 @@ int sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){ ** to change the length of the data stored. */ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){ + int rc; + assert( cursorHoldsMutex(pCsr) ); assert( sqlite3_mutex_held(pCsr->pBtree->db->mutex) ); assert(pCsr->isIncrblobHandle); @@ -7368,8 +7535,11 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){ } assert( !pCsr->pBt->readOnly && pCsr->pBt->inTransaction==TRANS_WRITE ); - if( checkReadLocks(pCsr->pBtree, pCsr->pgnoRoot, pCsr, 0) ){ - return SQLITE_LOCKED; /* The table pCur points to has a read lock */ + rc = checkForReadConflicts(pCsr->pBtree, pCsr->pgnoRoot, pCsr, 0); + if( rc!=SQLITE_OK ){ + /* The table pCur points to has a read lock */ + assert( rc==SQLITE_LOCKED_SHAREDCACHE ); + return rc; } if( pCsr->eState==CURSOR_INVALID || !pCsr->apPage[pCsr->iPage]->intKey ){ return SQLITE_ERROR; diff --git a/src/btree.h b/src/btree.h index dff9fe1..378c989 100644 --- a/src/btree.h +++ b/src/btree.h @@ -13,7 +13,7 @@ ** subsystem. See comments in the source code for a detailed description ** of what each interface routine does. ** -** @(#) $Id: btree.h,v 1.108 2009/02/03 16:51:25 danielk1977 Exp $ +** @(#) $Id: btree.h,v 1.113 2009/04/10 12:55:17 danielk1977 Exp $ */ #ifndef _BTREE_H_ #define _BTREE_H_ @@ -80,7 +80,7 @@ int sqlite3BtreeClose(Btree*); int sqlite3BtreeSetCacheSize(Btree*,int); int sqlite3BtreeSetSafetyLevel(Btree*,int,int); int sqlite3BtreeSyncDisabled(Btree*); -int sqlite3BtreeSetPageSize(Btree*,int,int); +int sqlite3BtreeSetPageSize(Btree*,int,int,int); int sqlite3BtreeGetPageSize(Btree*); int sqlite3BtreeMaxPageCount(Btree*,int); int sqlite3BtreeGetReserve(Btree*); @@ -91,12 +91,9 @@ int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster); int sqlite3BtreeCommitPhaseTwo(Btree*); int sqlite3BtreeCommit(Btree*); int sqlite3BtreeRollback(Btree*); -int sqlite3BtreeBeginStmt(Btree*); -int sqlite3BtreeCommitStmt(Btree*); -int sqlite3BtreeRollbackStmt(Btree*); +int sqlite3BtreeBeginStmt(Btree*,int); int sqlite3BtreeCreateTable(Btree*, int*, int flags); int sqlite3BtreeIsInTrans(Btree*); -int sqlite3BtreeIsInStmt(Btree*); int sqlite3BtreeIsInReadTrans(Btree*); int sqlite3BtreeIsInBackup(Btree*); void *sqlite3BtreeSchema(Btree *, int, void(*)(void *)); @@ -165,6 +162,8 @@ const void *sqlite3BtreeKeyFetch(BtCursor*, int *pAmt); const void *sqlite3BtreeDataFetch(BtCursor*, int *pAmt); int sqlite3BtreeDataSize(BtCursor*, u32 *pSize); int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*); +void sqlite3BtreeSetCachedRowid(BtCursor*, sqlite3_int64); +sqlite3_int64 sqlite3BtreeGetCachedRowid(BtCursor*); char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*); struct Pager *sqlite3BtreePager(Btree*); @@ -173,6 +172,10 @@ int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*); void sqlite3BtreeCacheOverflow(BtCursor *); void sqlite3BtreeClearCursor(BtCursor *); +#ifndef SQLITE_OMIT_BTREECOUNT +int sqlite3BtreeCount(BtCursor *, i64 *); +#endif + #ifdef SQLITE_TEST int sqlite3BtreeCursorInfo(BtCursor*, int*, int); void sqlite3BtreeCursorList(Btree*); @@ -183,42 +186,39 @@ void sqlite3BtreeCursorList(Btree*); ** use mutexes to access the BtShared structures. So make the ** Enter and Leave procedures no-ops. */ -#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE +#ifndef SQLITE_OMIT_SHARED_CACHE void sqlite3BtreeEnter(Btree*); - void sqlite3BtreeLeave(Btree*); -#ifndef NDEBUG - /* This routine is used inside assert() statements only. */ - int sqlite3BtreeHoldsMutex(Btree*); + void sqlite3BtreeEnterAll(sqlite3*); +#else +# define sqlite3BtreeEnter(X) +# define sqlite3BtreeEnterAll(X) #endif + +#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE + void sqlite3BtreeLeave(Btree*); void sqlite3BtreeEnterCursor(BtCursor*); void sqlite3BtreeLeaveCursor(BtCursor*); - void sqlite3BtreeEnterAll(sqlite3*); void sqlite3BtreeLeaveAll(sqlite3*); -#ifndef NDEBUG - /* This routine is used inside assert() statements only. */ - int sqlite3BtreeHoldsAllMutexes(sqlite3*); -#endif void sqlite3BtreeMutexArrayEnter(BtreeMutexArray*); void sqlite3BtreeMutexArrayLeave(BtreeMutexArray*); void sqlite3BtreeMutexArrayInsert(BtreeMutexArray*, Btree*); -#else -# define sqlite3BtreeEnter(X) -# define sqlite3BtreeLeave(X) #ifndef NDEBUG - /* This routine is used inside assert() statements only. */ -# define sqlite3BtreeHoldsMutex(X) 1 + /* These routines are used inside assert() statements only. */ + int sqlite3BtreeHoldsMutex(Btree*); + int sqlite3BtreeHoldsAllMutexes(sqlite3*); #endif +#else + +# define sqlite3BtreeLeave(X) # define sqlite3BtreeEnterCursor(X) # define sqlite3BtreeLeaveCursor(X) -# define sqlite3BtreeEnterAll(X) # define sqlite3BtreeLeaveAll(X) -#ifndef NDEBUG - /* This routine is used inside assert() statements only. */ -# define sqlite3BtreeHoldsAllMutexes(X) 1 -#endif # define sqlite3BtreeMutexArrayEnter(X) # define sqlite3BtreeMutexArrayLeave(X) # define sqlite3BtreeMutexArrayInsert(X,Y) + +# define sqlite3BtreeHoldsMutex(X) 1 +# define sqlite3BtreeHoldsAllMutexes(X) 1 #endif diff --git a/src/btreeInt.h b/src/btreeInt.h index 32aec96..0c196d3 100644 --- a/src/btreeInt.h +++ b/src/btreeInt.h @@ -9,7 +9,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** $Id: btreeInt.h,v 1.42 2009/02/03 16:51:25 danielk1977 Exp $ +** $Id: btreeInt.h,v 1.46 2009/03/20 14:18:52 danielk1977 Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to @@ -205,11 +205,6 @@ */ #include "sqliteInt.h" -/* Round up a number to the next larger multiple of 8. This is used -** to force 8-byte alignment on 64-bit architectures. -*/ -#define ROUND8(x) ((x+7)&~7) - /* The following value is the maximum cell size assuming a maximum page ** size give above. @@ -356,13 +351,30 @@ struct Btree { ** may not be modified once it is initially set as long as nRef>0. ** The pSchema field may be set once under BtShared.mutex and ** thereafter is unchanged as long as nRef>0. +** +** isPending: +** +** If a BtShared client fails to obtain a write-lock on a database +** table (because there exists one or more read-locks on the table), +** the shared-cache enters 'pending-lock' state and isPending is +** set to true. +** +** The shared-cache leaves the 'pending lock' state when either of +** the following occur: +** +** 1) The current writer (BtShared.pWriter) concludes its transaction, OR +** 2) The number of locks held by other connections drops to zero. +** +** while in the 'pending-lock' state, no connection may start a new +** transaction. +** +** This feature is included to help prevent writer-starvation. */ struct BtShared { Pager *pPager; /* The page cache */ sqlite3 *db; /* Database connection currently using this Btree */ BtCursor *pCursor; /* A list of all open cursors */ MemPage *pPage1; /* First page of the database */ - u8 inStmt; /* True if we are in a statement subtransaction */ u8 readOnly; /* True if the underlying file is readonly */ u8 pageSizeFixed; /* True if the page size can no longer be changed */ #ifndef SQLITE_OMIT_AUTOVACUUM @@ -385,7 +397,9 @@ struct BtShared { int nRef; /* Number of references to this structure */ BtShared *pNext; /* Next on a list of sharable BtShared structs */ BtLock *pLock; /* List of locks held on this shared-btree struct */ - Btree *pExclusive; /* Btree with an EXCLUSIVE lock on the whole db */ + Btree *pWriter; /* Btree with currently open write transaction */ + u8 isExclusive; /* True if pWriter has an EXCLUSIVE lock on the db */ + u8 isPending; /* If waiting for read-locks to clear */ #endif u8 *pTmpSpace; /* BtShared.pageSize bytes of space for tmp use */ }; @@ -438,6 +452,7 @@ struct BtCursor { BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */ struct KeyInfo *pKeyInfo; /* Argument passed to comparison function */ Pgno pgnoRoot; /* The root page of this tree */ + sqlite3_int64 cachedRowid; /* Next rowid cache. 0 means not valid */ CellInfo info; /* A parse of the cell we are pointing at */ u8 wrFlag; /* True if writable */ u8 atLast; /* Cursor pointing to the last entry */ diff --git a/src/build.c b/src/build.c index 8510713..a54efcf 100644 --- a/src/build.c +++ b/src/build.c @@ -22,7 +22,7 @@ ** COMMIT ** ROLLBACK ** -** $Id: build.c,v 1.518 2009/02/13 03:43:32 drh Exp $ +** $Id: build.c,v 1.528 2009/04/08 13:51:51 drh Exp $ */ #include "sqliteInt.h" @@ -178,19 +178,6 @@ void sqlite3FinishCoding(Parse *pParse){ codeTableLocks(pParse); sqlite3VdbeAddOp2(v, OP_Goto, 0, pParse->cookieGoto); } - -#ifndef SQLITE_OMIT_TRACE - if( !db->init.busy ){ - /* Change the P4 argument of the first opcode (which will always be - ** an OP_Trace) to be the complete text of the current SQL statement. - */ - VdbeOp *pOp = sqlite3VdbeGetOp(v, 0); - if( pOp && pOp->opcode==OP_Trace ){ - sqlite3VdbeChangeP4(v, 0, pParse->zSql, - (int)(pParse->zTail - pParse->zSql)); - } - } -#endif /* SQLITE_OMIT_TRACE */ } @@ -202,8 +189,8 @@ void sqlite3FinishCoding(Parse *pParse){ sqlite3VdbeTrace(v, trace); #endif assert( pParse->disableColCache==0 ); /* Disables and re-enables match */ - sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem+3, - pParse->nTab+3, pParse->explain); + sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem, + pParse->nTab, pParse->explain); pParse->rc = SQLITE_DONE; pParse->colNamesSet = 0; }else if( pParse->rc==SQLITE_OK ){ @@ -352,7 +339,7 @@ Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const char *zDb){ ** Reclaim the memory used by an index */ static void freeIndex(Index *p){ - sqlite3 *db = p->pTable->db; + sqlite3 *db = p->pTable->dbMem; sqlite3DbFree(db, p->zColAff); sqlite3DbFree(db, p); } @@ -480,7 +467,7 @@ void sqlite3CommitInternalChanges(sqlite3 *db){ static void sqliteResetColumnNames(Table *pTable){ int i; Column *pCol; - sqlite3 *db = pTable->db; + sqlite3 *db = pTable->dbMem; assert( pTable!=0 ); if( (pCol = pTable->aCol)!=0 ){ for(i=0; inCol; i++, pCol++){ @@ -511,7 +498,7 @@ void sqlite3DeleteTable(Table *pTable){ sqlite3 *db; if( pTable==0 ) return; - db = pTable->db; + db = pTable->dbMem; /* Do not delete the table until the reference count reaches zero. */ pTable->nRef--; @@ -616,8 +603,11 @@ char *sqlite3NameFromToken(sqlite3 *db, Token *pName){ void sqlite3OpenMasterTable(Parse *p, int iDb){ Vdbe *v = sqlite3GetVdbe(p); sqlite3TableLock(p, iDb, MASTER_ROOT, 1, SCHEMA_TABLE(iDb)); - sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, 5);/* sqlite_master has 5 columns */ sqlite3VdbeAddOp3(v, OP_OpenWrite, 0, MASTER_ROOT, iDb); + sqlite3VdbeChangeP4(v, -1, (char *)5, P4_INT32); /* 5 column table */ + if( p->nTab==0 ){ + p->nTab = 1; + } } /* @@ -846,7 +836,7 @@ void sqlite3StartTable( pTable->iPKey = -1; pTable->pSchema = db->aDb[iDb].pSchema; pTable->nRef = 1; - pTable->db = db; + pTable->dbMem = db->lookaside.bEnabled ? db : 0; if( pParse->pNewTable ) sqlite3DeleteTable(pParse->pNewTable); pParse->pNewTable = pTable; @@ -901,9 +891,10 @@ void sqlite3StartTable( ** The record created does not contain anything yet. It will be replaced ** by the real entry in code generated at sqlite3EndTable(). ** - ** The rowid for the new entry is left on the top of the stack. - ** The rowid value is needed by the code that sqlite3EndTable will - ** generate. + ** The rowid for the new entry is left in register pParse->regRowid. + ** The root page number of the new table is left in reg pParse->regRoot. + ** The rowid and root page number values are needed by the code that + ** sqlite3EndTable will generate. */ #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) if( isView || isVirtual ){ @@ -1116,12 +1107,12 @@ void sqlite3AddDefaultValue(Parse *pParse, Expr *pExpr){ sqlite3ErrorMsg(pParse, "default value of column [%s] is not constant", pCol->zName); }else{ - Expr *pCopy; + /* A copy of pExpr is used instead of the original, as pExpr contains + ** tokens that point to volatile memory. The 'span' of the expression + ** is required by pragma table_info. + */ sqlite3ExprDelete(db, pCol->pDflt); - pCol->pDflt = pCopy = sqlite3ExprDup(db, pExpr); - if( pCopy ){ - sqlite3TokenCopy(db, &pCopy->span, &pExpr->span); - } + pCol->pDflt = sqlite3ExprDup(db, pExpr, EXPRDUP_REDUCE|EXPRDUP_SPAN); } } sqlite3ExprDelete(db, pExpr); @@ -1217,7 +1208,7 @@ void sqlite3AddCheckConstraint( ** to malloced space and not the (ephemeral) text of the CREATE TABLE ** statement */ pTab->pCheck = sqlite3ExprAnd(db, pTab->pCheck, - sqlite3ExprDup(db, pCheckExpr)); + sqlite3ExprDup(db, pCheckExpr, 0)); } #endif sqlite3ExprDelete(db, pCheckExpr); @@ -1340,18 +1331,100 @@ static int identLength(const char *z){ } /* -** Write an identifier onto the end of the given string. Add -** quote characters as needed. +** This function is a wrapper around sqlite3GetToken() used by +** isValidDimension(). This function differs from sqlite3GetToken() in +** that: +** +** * Whitespace is ignored, and +** * The output variable *peToken is set to 0 if the end of the +** nul-terminated input string is reached. */ -static void identPut(char *z, int *pIdx, char *zSignedIdent){ +static int getTokenNoSpace(unsigned char *z, int *peToken){ + int n = 0; + while( sqlite3Isspace(z[n]) ) n++; + if( !z[n] ){ + *peToken = 0; + return 0; + } + return n + sqlite3GetToken(&z[n], peToken); +} + +/* +** Parameter z points to a nul-terminated string. Return true if, when +** whitespace is ignored, the contents of this string matches one of +** the following patterns: +** +** "" +** "(number)" +** "(number,number)" +*/ +static int isValidDimension(unsigned char *z){ + int eToken; + int n = 0; + n += getTokenNoSpace(&z[n], &eToken); + if( eToken ){ + if( eToken!=TK_LP ) return 0; + n += getTokenNoSpace(&z[n], &eToken); + if( eToken==TK_PLUS || eToken==TK_MINUS ){ + n += getTokenNoSpace(&z[n], &eToken); + } + if( eToken!=TK_INTEGER && eToken!=TK_FLOAT ) return 0; + n += getTokenNoSpace(&z[n], &eToken); + if( eToken==TK_COMMA ){ + n += getTokenNoSpace(&z[n], &eToken); + if( eToken==TK_PLUS || eToken==TK_MINUS ){ + n += getTokenNoSpace(&z[n], &eToken); + } + if( eToken!=TK_INTEGER && eToken!=TK_FLOAT ) return 0; + n += getTokenNoSpace(&z[n], &eToken); + } + if( eToken!=TK_RP ) return 0; + getTokenNoSpace(&z[n], &eToken); + } + if( eToken ) return 0; + return 1; +} + +/* +** The first parameter is a pointer to an output buffer. The second +** parameter is a pointer to an integer that contains the offset at +** which to write into the output buffer. This function copies the +** nul-terminated string pointed to by the third parameter, zSignedIdent, +** to the specified offset in the buffer and updates *pIdx to refer +** to the first byte after the last byte written before returning. +** +** If the string zSignedIdent consists entirely of alpha-numeric +** characters, does not begin with a digit and is not an SQL keyword, +** then it is copied to the output buffer exactly as it is. Otherwise, +** it is quoted using double-quotes. +*/ +static void identPut(char *z, int *pIdx, char *zSignedIdent, int isTypename){ unsigned char *zIdent = (unsigned char*)zSignedIdent; int i, j, needQuote; i = *pIdx; + for(j=0; zIdent[j]; j++){ if( !sqlite3Isalnum(zIdent[j]) && zIdent[j]!='_' ) break; } - needQuote = zIdent[j]!=0 || sqlite3Isdigit(zIdent[0]) - || sqlite3KeywordCode(zIdent, j)!=TK_ID; + needQuote = sqlite3Isdigit(zIdent[0]) || sqlite3KeywordCode(zIdent, j)!=TK_ID; + if( !needQuote ){ + if( isTypename ){ + /* If this is a type-name, allow a little more flexibility. In SQLite, + ** a type-name is specified as: + ** + ** ids [ids] [(number [, number])] + ** + ** where "ids" is either a quoted string or a simple identifier (in the + ** above notation, [] means optional). It is a bit tricky to check + ** for all cases, but it is good to avoid unnecessarily quoting common + ** typenames like VARCHAR(10). + */ + needQuote = !isValidDimension(&zIdent[j]); + }else{ + needQuote = zIdent[j]; + } + } + if( needQuote ) z[i++] = '"'; for(j=0; zIdent[j]; j++){ z[i++] = zIdent[j]; @@ -1377,7 +1450,7 @@ static char *createTableStmt(sqlite3 *db, Table *p){ n += identLength(pCol->zName); z = pCol->zType; if( z ){ - n += (sqlite3Strlen30(z) + 1); + n += identLength(z); } } n += identLength(p->zName); @@ -1398,18 +1471,17 @@ static char *createTableStmt(sqlite3 *db, Table *p){ } sqlite3_snprintf(n, zStmt, "CREATE TABLE "); k = sqlite3Strlen30(zStmt); - identPut(zStmt, &k, p->zName); + identPut(zStmt, &k, p->zName, 0); zStmt[k++] = '('; for(pCol=p->aCol, i=0; inCol; i++, pCol++){ sqlite3_snprintf(n-k, &zStmt[k], zSep); k += sqlite3Strlen30(&zStmt[k]); zSep = zSep2; - identPut(zStmt, &k, pCol->zName); + identPut(zStmt, &k, pCol->zName, 0); if( (z = pCol->zType)!=0 ){ zStmt[k++] = ' '; assert( (int)(sqlite3Strlen30(z)+k+1)<=n ); - sqlite3_snprintf(n-k, &zStmt[k], "%s", z); - k += sqlite3Strlen30(z); + identPut(zStmt, &k, z, 1); } } sqlite3_snprintf(n-k, &zStmt[k], "%s", zEnd); @@ -1489,8 +1561,7 @@ void sqlite3EndTable( } /* If not initializing, then create a record for the new table - ** in the SQLITE_MASTER table of the database. The record number - ** for the new table entry should already be on the stack. + ** in the SQLITE_MASTER table of the database. ** ** If this is a TEMPORARY table, write the entry into the auxiliary ** file instead of into the main database file. @@ -1507,9 +1578,8 @@ void sqlite3EndTable( sqlite3VdbeAddOp1(v, OP_Close, 0); - /* Create the rootpage for the new table and push it onto the stack. - ** A view has no rootpage, so just push a zero onto the stack for - ** views. Initialize zType at the same time. + /* + ** Initialize zType for the new view or table. */ if( p->pSelect==0 ){ /* A regular table */ @@ -1525,7 +1595,7 @@ void sqlite3EndTable( /* If this is a CREATE TABLE xx AS SELECT ..., execute the SELECT ** statement to populate the new table. The root-page number for the - ** new table is on the top of the vdbe stack. + ** new table is in register pParse->regRoot. ** ** Once the SELECT has been coded by sqlite3Select(), it is in a ** suitable state to query for the column names and types to be used @@ -1540,7 +1610,7 @@ void sqlite3EndTable( SelectDest dest; Table *pSelTab; - assert(pParse->nTab==0); + assert(pParse->nTab==1); sqlite3VdbeAddOp3(v, OP_OpenWrite, 1, pParse->regRoot, iDb); sqlite3VdbeChangeP5(v, 1); pParse->nTab = 2; @@ -1571,9 +1641,7 @@ void sqlite3EndTable( /* A slot for the record has already been allocated in the ** SQLITE_MASTER table. We just need to update that slot with all - ** the information we've collected. The rowid for the preallocated - ** slot is the 2nd item on the stack. The top of the stack is the - ** root page for the new table (or a 0 if this is a view). + ** the information we've collected. */ sqlite3NestedParse(pParse, "UPDATE %Q.%s " @@ -1701,7 +1769,7 @@ void sqlite3CreateView( ** allocated rather than point to the input string - which means that ** they will persist after the current sqlite3_exec() call returns. */ - p->pSelect = sqlite3SelectDup(db, pSelect); + p->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); sqlite3SelectDelete(db, pSelect); if( db->mallocFailed ){ return; @@ -1783,11 +1851,13 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ ** statement that defines the view. */ assert( pTable->pSelect ); - pSel = sqlite3SelectDup(db, pTable->pSelect); + pSel = sqlite3SelectDup(db, pTable->pSelect, 0); if( pSel ){ + u8 enableLookaside = db->lookaside.bEnabled; n = pParse->nTab; sqlite3SrcListAssignCursors(pParse, pSel->pSrc); pTable->nCol = -1; + db->lookaside.bEnabled = 0; #ifndef SQLITE_OMIT_AUTHORIZATION xAuth = db->xAuth; db->xAuth = 0; @@ -1796,6 +1866,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ #else pSelTab = sqlite3ResultSetOfSelect(pParse, pSel); #endif + db->lookaside.bEnabled = enableLookaside; pParse->nTab = n; if( pSelTab ){ assert( pTable->aCol==0 ); @@ -1892,8 +1963,8 @@ static void destroyRootPage(Parse *pParse, int iTable, int iDb){ ** location iTable. The following code modifies the sqlite_master table to ** reflect this. ** - ** The "#%d" in the SQL is a special constant that means whatever value - ** is on the top of the stack. See sqlite3RegisterExpr(). + ** The "#NNN" in the SQL is a special constant that means whatever value + ** is in register NNN. See sqlite3RegisterExpr(). */ sqlite3NestedParse(pParse, "UPDATE %Q.%s SET rootpage=%d WHERE #%d AND rootpage=#%d", @@ -2068,7 +2139,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){ ** is generated to remove entries from sqlite_master and/or ** sqlite_temp_master if required. */ - pTrigger = pTab->pTrigger; + pTrigger = sqlite3TriggerList(pParse, pTab); while( pTrigger ){ assert( pTrigger->pSchema==pTab->pSchema || pTrigger->pSchema==db->aDb[1].pSchema ); @@ -2277,8 +2348,8 @@ void sqlite3DeferForeignKey(Parse *pParse, int isDeferred){ */ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ Table *pTab = pIndex->pTable; /* The table that is indexed */ - int iTab = pParse->nTab; /* Btree cursor used for pTab */ - int iIdx = pParse->nTab+1; /* Btree cursor used for pIndex */ + int iTab = pParse->nTab++; /* Btree cursor used for pTab */ + int iIdx = pParse->nTab++; /* Btree cursor used for pIndex */ int addr1; /* Address of top of loop */ int tnum; /* Root page of index */ Vdbe *v; /* Generate code into this virtual machine */ @@ -2773,7 +2844,8 @@ void sqlite3CreateIndex( /* Clean up before exiting */ exit_create_index: if( pIndex ){ - freeIndex(pIndex); + sqlite3_free(pIndex->zColAff); + sqlite3DbFree(db, pIndex); } sqlite3ExprListDelete(db, pList); sqlite3SrcListDelete(db, pTblName); diff --git a/src/callback.c b/src/callback.c index 0c1e657..c4a5280 100644 --- a/src/callback.c +++ b/src/callback.c @@ -13,7 +13,7 @@ ** This file contains functions used to access the internal hash tables ** of user defined functions and collation sequences. ** -** $Id: callback.c,v 1.35 2009/01/31 22:28:49 drh Exp $ +** $Id: callback.c,v 1.37 2009/03/24 15:08:10 drh Exp $ */ #include "sqliteInt.h" @@ -175,7 +175,7 @@ static CollSeq *findCollSeqEntry( pColl[0].zName[nName] = 0; pDel = sqlite3HashInsert(&db->aCollSeq, pColl[0].zName, nName, pColl); - /* If a malloc() failure occured in sqlite3HashInsert(), it will + /* If a malloc() failure occurred in sqlite3HashInsert(), it will ** return the pColl pointer to be deleted (because it wasn't added ** to the hash table). */ @@ -423,6 +423,7 @@ void sqlite3SchemaFree(void *p){ sqlite3HashInit(&pSchema->tblHash, 0); for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){ Table *pTab = sqliteHashData(pElem); + assert( pTab->dbMem==0 ); sqlite3DeleteTable(pTab); } sqlite3HashClear(&temp1); diff --git a/src/date.c b/src/date.c index 0fa1ee9..5b1de72 100644 --- a/src/date.c +++ b/src/date.c @@ -16,7 +16,7 @@ ** sqlite3RegisterDateTimeFunctions() found at the bottom of the file. ** All other code has file scope. ** -** $Id: date.c,v 1.103 2009/02/04 03:59:25 shane Exp $ +** $Id: date.c,v 1.105 2009/04/03 12:04:37 drh Exp $ ** ** SQLite processes all times and dates as Julian Day numbers. The ** dates and times are stored as the number of days since noon @@ -953,8 +953,8 @@ static void strftimeFunc( case 'm': sqlite3_snprintf(3, &z[j],"%02d",x.M); j+=2; break; case 'M': sqlite3_snprintf(3, &z[j],"%02d",x.m); j+=2; break; case 's': { - sqlite3_snprintf(30,&z[j],"%d", - (int)(x.iJD/1000.0 - 210866760000.0)); + sqlite3_snprintf(30,&z[j],"%lld", + (i64)(x.iJD/1000 - 21086676*(i64)10000)); j += sqlite3Strlen30(&z[j]); break; } diff --git a/src/delete.c b/src/delete.c index eb683f5..1850ac4 100644 --- a/src/delete.c +++ b/src/delete.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** in order to generate code for DELETE FROM statements. ** -** $Id: delete.c,v 1.191 2008/12/23 23:56:22 drh Exp $ +** $Id: delete.c,v 1.198 2009/03/05 03:48:07 shane Exp $ */ #include "sqliteInt.h" @@ -77,8 +77,8 @@ void sqlite3OpenTable( v = sqlite3GetVdbe(p); assert( opcode==OP_OpenWrite || opcode==OP_OpenRead ); sqlite3TableLock(p, iDb, pTab->tnum, (opcode==OP_OpenWrite)?1:0, pTab->zName); - sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol); sqlite3VdbeAddOp3(v, opcode, iCur, pTab->tnum, iDb); + sqlite3VdbeChangeP4(v, -1, SQLITE_INT_TO_PTR(pTab->nCol), P4_INT32); VdbeComment((v, "%s", pTab->zName)); } @@ -99,12 +99,12 @@ void sqlite3MaterializeView( Select *pDup; sqlite3 *db = pParse->db; - pDup = sqlite3SelectDup(db, pView->pSelect); + pDup = sqlite3SelectDup(db, pView->pSelect, 0); if( pWhere ){ SrcList *pFrom; Token viewName; - pWhere = sqlite3ExprDup(db, pWhere); + pWhere = sqlite3ExprDup(db, pWhere, 0); viewName.z = (u8*)pView->zName; viewName.n = (unsigned int)sqlite3Strlen30((const char*)viewName.z); pFrom = sqlite3SrcListAppendFromTerm(pParse, 0, 0, 0, &viewName, pDup, 0,0); @@ -174,14 +174,15 @@ Expr *sqlite3LimitWhere( /* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree ** and the SELECT subtree. */ - pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc); + pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc, 0); if( pSelectSrc == 0 ) { sqlite3ExprListDelete(pParse->db, pEList); goto limit_where_cleanup_2; } /* generate the SELECT expression tree. */ - pSelect = sqlite3SelectNew(pParse,pEList,pSelectSrc,pWhere,0,0,pOrderBy,0,pLimit,pOffset); + pSelect = sqlite3SelectNew(pParse,pEList,pSelectSrc,pWhere,0,0, + pOrderBy,0,pLimit,pOffset); if( pSelect == 0 ) return 0; /* now generate the new WHERE rowid IN clause for the DELETE/UDPATE */ @@ -190,7 +191,8 @@ Expr *sqlite3LimitWhere( pInClause = sqlite3PExpr(pParse, TK_IN, pWhereRowid, 0, 0); if( pInClause == 0 ) goto limit_where_cleanup_1; - pInClause->pSelect = pSelect; + pInClause->x.pSelect = pSelect; + pInClause->flags |= EP_xIsSelect; sqlite3ExprSetHeight(pParse, pInClause); return pInClause; @@ -238,7 +240,7 @@ void sqlite3DeleteFrom( #ifndef SQLITE_OMIT_TRIGGER int isView; /* True if attempting to delete from a view */ - int triggers_exist = 0; /* True if any triggers exist */ + Trigger *pTrigger; /* List of table triggers, if required */ #endif int iBeginAfterTrigger = 0; /* Address of after trigger program */ int iEndAfterTrigger = 0; /* Exit of after trigger program */ @@ -265,10 +267,10 @@ void sqlite3DeleteFrom( ** deleted from is a view */ #ifndef SQLITE_OMIT_TRIGGER - triggers_exist = sqlite3TriggersExist(pTab, TK_DELETE, 0); + pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0); isView = pTab->pSelect!=0; #else -# define triggers_exist 0 +# define pTrigger 0 # define isView 0 #endif #ifdef SQLITE_OMIT_VIEW @@ -276,7 +278,7 @@ void sqlite3DeleteFrom( # define isView 0 #endif - if( sqlite3IsReadOnly(pParse, pTab, triggers_exist) ){ + if( sqlite3IsReadOnly(pParse, pTab, (pTrigger?1:0)) ){ goto delete_from_cleanup; } iDb = sqlite3SchemaToIndex(db, pTab->pSchema); @@ -287,7 +289,7 @@ void sqlite3DeleteFrom( if( rcauth==SQLITE_DENY ){ goto delete_from_cleanup; } - assert(!isView || triggers_exist); + assert(!isView || pTrigger); /* If pTab is really a view, make sure it has been initialized. */ @@ -297,7 +299,7 @@ void sqlite3DeleteFrom( /* Allocate a cursor used to store the old.* data for a trigger. */ - if( triggers_exist ){ + if( pTrigger ){ oldIdx = pParse->nTab++; } @@ -322,21 +324,21 @@ void sqlite3DeleteFrom( goto delete_from_cleanup; } if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); - sqlite3BeginWriteOperation(pParse, triggers_exist, iDb); + sqlite3BeginWriteOperation(pParse, (pTrigger?1:0), iDb); - if( triggers_exist ){ + if( pTrigger ){ int orconf = ((pParse->trigStack)?pParse->trigStack->orconf:OE_Default); int iGoto = sqlite3VdbeAddOp0(v, OP_Goto); addr = sqlite3VdbeMakeLabel(v); iBeginBeforeTrigger = sqlite3VdbeCurrentAddr(v); - (void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TRIGGER_BEFORE, pTab, - -1, oldIdx, orconf, addr, &old_col_mask, 0); + (void)sqlite3CodeRowTrigger(pParse, pTrigger, TK_DELETE, 0, + TRIGGER_BEFORE, pTab, -1, oldIdx, orconf, addr, &old_col_mask, 0); iEndBeforeTrigger = sqlite3VdbeAddOp0(v, OP_Goto); iBeginAfterTrigger = sqlite3VdbeCurrentAddr(v); - (void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TRIGGER_AFTER, pTab, -1, - oldIdx, orconf, addr, &old_col_mask, 0); + (void)sqlite3CodeRowTrigger(pParse, pTrigger, TK_DELETE, 0, + TRIGGER_AFTER, pTab, -1, oldIdx, orconf, addr, &old_col_mask, 0); iEndAfterTrigger = sqlite3VdbeAddOp0(v, OP_Goto); sqlite3VdbeJumpHere(v, iGoto); @@ -373,7 +375,7 @@ void sqlite3DeleteFrom( ** It is easier just to erase the whole table. Note, however, that ** this means that the row change count will be incorrect. */ - if( rcauth==SQLITE_OK && pWhere==0 && !triggers_exist && !IsVirtual(pTab) ){ + if( rcauth==SQLITE_OK && pWhere==0 && !pTrigger && !IsVirtual(pTab) ){ assert( !isView ); sqlite3VdbeAddOp3(v, OP_Clear, pTab->tnum, iDb, memCnt); if( !pParse->nested ){ @@ -405,9 +407,8 @@ void sqlite3DeleteFrom( /* Open the pseudo-table used to store OLD if there are triggers. */ - if( triggers_exist ){ - sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol); - sqlite3VdbeAddOp1(v, OP_OpenPseudo, oldIdx); + if( pTrigger ){ + sqlite3VdbeAddOp3(v, OP_OpenPseudo, oldIdx, 0, pTab->nCol); } /* Delete every item whose key was written to the list during the @@ -426,12 +427,12 @@ void sqlite3DeleteFrom( /* This is the beginning of the delete loop. If a trigger encounters ** an IGNORE constraint, it jumps back to here. */ - if( triggers_exist ){ + if( pTrigger ){ sqlite3VdbeResolveLabel(v, addr); } addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, end, iRowid); - if( triggers_exist ){ + if( pTrigger ){ int iData = ++pParse->nMem; /* For storing row data of OLD table */ /* If the record is no longer present in the table, jump to the @@ -469,7 +470,7 @@ void sqlite3DeleteFrom( /* If there are row triggers, close all cursors then invoke ** the AFTER triggers */ - if( triggers_exist ){ + if( pTrigger ){ /* Jump back and run the AFTER triggers */ sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginAfterTrigger); sqlite3VdbeJumpHere(v, iEndAfterTrigger); diff --git a/src/expr.c b/src/expr.c index 8960a7a..35a5853 100644 --- a/src/expr.c +++ b/src/expr.c @@ -12,7 +12,7 @@ ** This file contains routines used for analyzing expressions and ** for generating VDBE code that evaluates expressions in SQLite. ** -** $Id: expr.c,v 1.411 2009/02/04 03:59:25 shane Exp $ +** $Id: expr.c,v 1.426 2009/04/08 13:51:51 drh Exp $ */ #include "sqliteInt.h" @@ -35,7 +35,8 @@ char sqlite3ExprAffinity(Expr *pExpr){ int op = pExpr->op; if( op==TK_SELECT ){ - return sqlite3ExprAffinity(pExpr->pSelect->pEList->a[0].pExpr); + assert( pExpr->flags&EP_xIsSelect ); + return sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr); } #ifndef SQLITE_OMIT_CAST if( op==TK_CAST ){ @@ -155,11 +156,9 @@ static char comparisonAffinity(Expr *pExpr){ aff = sqlite3ExprAffinity(pExpr->pLeft); if( pExpr->pRight ){ aff = sqlite3CompareAffinity(pExpr->pRight, aff); - } - else if( pExpr->pSelect ){ - aff = sqlite3CompareAffinity(pExpr->pSelect->pEList->a[0].pExpr, aff); - } - else if( !aff ){ + }else if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + aff = sqlite3CompareAffinity(pExpr->x.pSelect->pEList->a[0].pExpr, aff); + }else if( !aff ){ aff = SQLITE_AFF_NONE; } return aff; @@ -345,8 +344,11 @@ static void exprSetHeight(Expr *p){ int nHeight = 0; heightOfExpr(p->pLeft, &nHeight); heightOfExpr(p->pRight, &nHeight); - heightOfExprList(p->pList, &nHeight); - heightOfSelect(p->pSelect, &nHeight); + if( ExprHasProperty(p, EP_xIsSelect) ){ + heightOfSelect(p->x.pSelect, &nHeight); + }else{ + heightOfExprList(p->x.pList, &nHeight); + } p->nHeight = nHeight + 1; } @@ -402,8 +404,24 @@ Expr *sqlite3Expr( pNew->iAgg = -1; pNew->span.z = (u8*)""; if( pToken ){ + int c; assert( pToken->dyn==0 ); - pNew->span = pNew->token = *pToken; + pNew->span = *pToken; + + /* The pToken->z value is read-only. But the new expression + ** node created here might be passed to sqlite3DequoteExpr() which + ** will attempt to modify pNew->token.z. Hence, if the token + ** is quoted, make a copy now so that DequoteExpr() will change + ** the copy rather than the original text. + */ + if( pToken->n>=2 + && ((c = pToken->z[0])=='\'' || c=='"' || c=='[' || c=='`') ){ + sqlite3TokenCopy(db, &pNew->token, pToken); + }else{ + pNew->token = *pToken; + pNew->flags |= EP_Dequoted; + VVA_ONLY( pNew->vvaFlags |= EVVA_ReadOnlyToken; ) + } }else if( pLeft ){ if( pRight ){ if( pRight->span.dyn==0 && pLeft->span.dyn==0 ){ @@ -492,7 +510,10 @@ void sqlite3ExprSpan(Expr *pExpr, Token *pLeft, Token *pRight){ assert( pLeft!=0 ); if( pExpr ){ pExpr->span.z = pLeft->z; - pExpr->span.n = pRight->n + (pRight->z - pLeft->z); + /* The following assert() may fail when this is called + ** via sqlite3PExpr()/sqlite3Expr() from addWhereTerm(). */ + /* assert(pRight->z >= pLeft->z); */ + pExpr->span.n = pRight->n + (unsigned)(pRight->z - pLeft->z); } } @@ -506,15 +527,15 @@ Expr *sqlite3ExprFunction(Parse *pParse, ExprList *pList, Token *pToken){ assert( pToken ); pNew = sqlite3DbMallocZero(db, sizeof(Expr) ); if( pNew==0 ){ - sqlite3ExprListDelete(db, pList); /* Avoid leaking memory when malloc fails */ + sqlite3ExprListDelete(db, pList); /* Avoid memory leak when malloc fails */ return 0; } pNew->op = TK_FUNCTION; - pNew->pList = pList; + pNew->x.pList = pList; + assert( !ExprHasProperty(pNew, EP_xIsSelect) ); assert( pToken->dyn==0 ); - pNew->token = *pToken; - pNew->span = pNew->token; - + pNew->span = *pToken; + sqlite3TokenCopy(db, &pNew->token, pToken); sqlite3ExprSetHeight(pParse, pNew); return pNew; } @@ -607,12 +628,25 @@ void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){ ** Substructure is deleted. */ void sqlite3ExprClear(sqlite3 *db, Expr *p){ - if( p->span.dyn ) sqlite3DbFree(db, (char*)p->span.z); if( p->token.dyn ) sqlite3DbFree(db, (char*)p->token.z); - sqlite3ExprDelete(db, p->pLeft); - sqlite3ExprDelete(db, p->pRight); - sqlite3ExprListDelete(db, p->pList); - sqlite3SelectDelete(db, p->pSelect); + if( !ExprHasAnyProperty(p, EP_TokenOnly|EP_SpanToken) ){ + if( p->span.dyn ) sqlite3DbFree(db, (char*)p->span.z); + if( ExprHasProperty(p, EP_Reduced) ){ + /* Subtrees are part of the same memory allocation when EP_Reduced set */ + if( p->pLeft ) sqlite3ExprClear(db, p->pLeft); + if( p->pRight ) sqlite3ExprClear(db, p->pRight); + }else{ + /* Subtrees are separate allocations when EP_Reduced is clear */ + sqlite3ExprDelete(db, p->pLeft); + sqlite3ExprDelete(db, p->pRight); + } + /* x.pSelect and x.pList are always separately allocated */ + if( ExprHasProperty(p, EP_xIsSelect) ){ + sqlite3SelectDelete(db, p->x.pSelect); + }else{ + sqlite3ExprListDelete(db, p->x.pList); + } + } } /* @@ -628,15 +662,192 @@ void sqlite3ExprDelete(sqlite3 *db, Expr *p){ ** The Expr.token field might be a string literal that is quoted. ** If so, remove the quotation marks. */ -void sqlite3DequoteExpr(sqlite3 *db, Expr *p){ - if( ExprHasAnyProperty(p, EP_Dequoted) ){ - return; +void sqlite3DequoteExpr(Expr *p){ + if( !ExprHasAnyProperty(p, EP_Dequoted) ){ + ExprSetProperty(p, EP_Dequoted); + assert( (p->vvaFlags & EVVA_ReadOnlyToken)==0 ); + sqlite3Dequote((char*)p->token.z); } - ExprSetProperty(p, EP_Dequoted); - if( p->token.dyn==0 ){ - sqlite3TokenCopy(db, &p->token, &p->token); +} + +/* +** Return the number of bytes allocated for the expression structure +** passed as the first argument. This is always one of EXPR_FULLSIZE, +** EXPR_REDUCEDSIZE or EXPR_TOKENONLYSIZE. +*/ +static int exprStructSize(Expr *p){ + if( ExprHasProperty(p, EP_TokenOnly) ) return EXPR_TOKENONLYSIZE; + if( ExprHasProperty(p, EP_SpanToken) ) return EXPR_SPANTOKENSIZE; + if( ExprHasProperty(p, EP_Reduced) ) return EXPR_REDUCEDSIZE; + return EXPR_FULLSIZE; +} + +/* +** sqlite3ExprDup() has been called to create a copy of expression p with +** the EXPRDUP_XXX flags passed as the second argument. This function +** returns the space required for the copy of the Expr structure only. +** This is always one of EXPR_FULLSIZE, EXPR_REDUCEDSIZE or EXPR_TOKENONLYSIZE. +*/ +static int dupedExprStructSize(Expr *p, int flags){ + int nSize; + if( 0==(flags&EXPRDUP_REDUCE) ){ + nSize = EXPR_FULLSIZE; + }else if( p->pLeft || p->pRight || p->pColl || p->x.pList ){ + nSize = EXPR_REDUCEDSIZE; + }else if( flags&EXPRDUP_SPAN ){ + nSize = EXPR_SPANTOKENSIZE; + }else{ + nSize = EXPR_TOKENONLYSIZE; } - sqlite3Dequote((char*)p->token.z); + return nSize; +} + +/* +** sqlite3ExprDup() has been called to create a copy of expression p with +** the EXPRDUP_XXX passed as the second argument. This function returns +** the space in bytes required to store the copy of the Expr structure +** and the copies of the Expr.token.z and Expr.span.z (if applicable) +** string buffers. +*/ +static int dupedExprNodeSize(Expr *p, int flags){ + int nByte = dupedExprStructSize(p, flags) + (p->token.z ? p->token.n + 1 : 0); + if( (flags&EXPRDUP_SPAN)!=0 + && (p->token.z!=p->span.z || p->token.n!=p->span.n) + ){ + nByte += p->span.n; + } + return ROUND8(nByte); +} + +/* +** Return the number of bytes required to create a duplicate of the +** expression passed as the first argument. The second argument is a +** mask containing EXPRDUP_XXX flags. +** +** The value returned includes space to create a copy of the Expr struct +** itself and the buffer referred to by Expr.token, if any. If the +** EXPRDUP_SPAN flag is set, then space to create a copy of the buffer +** refered to by Expr.span is also included. +** +** If the EXPRDUP_REDUCE flag is set, then the return value includes +** space to duplicate all Expr nodes in the tree formed by Expr.pLeft +** and Expr.pRight variables (but not for any structures pointed to or +** descended from the Expr.x.pList or Expr.x.pSelect variables). +*/ +static int dupedExprSize(Expr *p, int flags){ + int nByte = 0; + if( p ){ + nByte = dupedExprNodeSize(p, flags); + if( flags&EXPRDUP_REDUCE ){ + int f = flags&(~EXPRDUP_SPAN); + nByte += dupedExprSize(p->pLeft, f) + dupedExprSize(p->pRight, f); + } + } + return nByte; +} + +/* +** This function is similar to sqlite3ExprDup(), except that if pzBuffer +** is not NULL then *pzBuffer is assumed to point to a buffer large enough +** to store the copy of expression p, the copies of p->token and p->span +** (if applicable), and the copies of the p->pLeft and p->pRight expressions, +** if any. Before returning, *pzBuffer is set to the first byte passed the +** portion of the buffer copied into by this function. +*/ +static Expr *exprDup(sqlite3 *db, Expr *p, int flags, u8 **pzBuffer){ + Expr *pNew = 0; /* Value to return */ + if( p ){ + const int isRequireSpan = (flags&EXPRDUP_SPAN); + const int isReduced = (flags&EXPRDUP_REDUCE); + u8 *zAlloc; + + assert( pzBuffer==0 || isReduced ); + + /* Figure out where to write the new Expr structure. */ + if( pzBuffer ){ + zAlloc = *pzBuffer; + }else{ + zAlloc = sqlite3DbMallocRaw(db, dupedExprSize(p, flags)); + } + pNew = (Expr *)zAlloc; + + if( pNew ){ + /* Set nNewSize to the size allocated for the structure pointed to + ** by pNew. This is either EXPR_FULLSIZE, EXPR_REDUCEDSIZE or + ** EXPR_TOKENONLYSIZE. nToken is set to the number of bytes consumed + ** by the copy of the p->token.z string (if any). + */ + const int nNewSize = dupedExprStructSize(p, flags); + const int nToken = (p->token.z ? p->token.n + 1 : 0); + if( isReduced ){ + assert( ExprHasProperty(p, EP_Reduced)==0 ); + memcpy(zAlloc, p, nNewSize); + }else{ + int nSize = exprStructSize(p); + memcpy(zAlloc, p, nSize); + memset(&zAlloc[nSize], 0, EXPR_FULLSIZE-nSize); + } + + /* Set the EP_Reduced and EP_TokenOnly flags appropriately. */ + pNew->flags &= ~(EP_Reduced|EP_TokenOnly|EP_SpanToken); + switch( nNewSize ){ + case EXPR_REDUCEDSIZE: pNew->flags |= EP_Reduced; break; + case EXPR_TOKENONLYSIZE: pNew->flags |= EP_TokenOnly; break; + case EXPR_SPANTOKENSIZE: pNew->flags |= EP_SpanToken; break; + } + + /* Copy the p->token string, if any. */ + if( nToken ){ + unsigned char *zToken = &zAlloc[nNewSize]; + memcpy(zToken, p->token.z, nToken-1); + zToken[nToken-1] = '\0'; + pNew->token.dyn = 0; + pNew->token.z = zToken; + } + + if( 0==((p->flags|pNew->flags) & EP_TokenOnly) ){ + /* Fill in the pNew->span token, if required. */ + if( isRequireSpan ){ + if( p->token.z!=p->span.z || p->token.n!=p->span.n ){ + pNew->span.z = &zAlloc[nNewSize+nToken]; + memcpy((char *)pNew->span.z, p->span.z, p->span.n); + pNew->span.dyn = 0; + }else{ + pNew->span.z = pNew->token.z; + pNew->span.n = pNew->token.n; + } + }else{ + pNew->span.z = 0; + pNew->span.n = 0; + } + } + + if( 0==((p->flags|pNew->flags) & (EP_TokenOnly|EP_SpanToken)) ){ + /* Fill in the pNew->x.pSelect or pNew->x.pList member. */ + if( ExprHasProperty(p, EP_xIsSelect) ){ + pNew->x.pSelect = sqlite3SelectDup(db, p->x.pSelect, isReduced); + }else{ + pNew->x.pList = sqlite3ExprListDup(db, p->x.pList, isReduced); + } + } + + /* Fill in pNew->pLeft and pNew->pRight. */ + if( ExprHasAnyProperty(pNew, EP_Reduced|EP_TokenOnly|EP_SpanToken) ){ + zAlloc += dupedExprNodeSize(p, flags); + if( ExprHasProperty(pNew, EP_Reduced) ){ + pNew->pLeft = exprDup(db, p->pLeft, EXPRDUP_REDUCE, &zAlloc); + pNew->pRight = exprDup(db, p->pRight, EXPRDUP_REDUCE, &zAlloc); + } + if( pzBuffer ){ + *pzBuffer = zAlloc; + } + }else if( !ExprHasAnyProperty(p, EP_TokenOnly|EP_SpanToken) ){ + pNew->pLeft = sqlite3ExprDup(db, p->pLeft, 0); + pNew->pRight = sqlite3ExprDup(db, p->pRight, 0); + } + } + } + return pNew; } /* @@ -650,27 +861,21 @@ void sqlite3DequoteExpr(sqlite3 *db, Expr *p){ ** by subsequent calls to sqlite*ListAppend() routines. ** ** Any tables that the SrcList might point to are not duplicated. +** +** The flags parameter contains a combination of the EXPRDUP_XXX flags. If +** the EXPRDUP_SPAN flag is set in the argument parameter, then the +** Expr.span field of the input expression is copied. If EXPRDUP_SPAN is +** clear, then the Expr.span field of the returned expression structure +** is zeroed. +** +** If the EXPRDUP_REDUCE flag is set, then the structure returned is a +** truncated version of the usual Expr structure that will be stored as +** part of the in-memory representation of the database schema. */ -Expr *sqlite3ExprDup(sqlite3 *db, Expr *p){ - Expr *pNew; - if( p==0 ) return 0; - pNew = sqlite3DbMallocRaw(db, sizeof(*p) ); - if( pNew==0 ) return 0; - memcpy(pNew, p, sizeof(*pNew)); - if( p->token.z!=0 ){ - pNew->token.z = (u8*)sqlite3DbStrNDup(db, (char*)p->token.z, p->token.n); - pNew->token.dyn = 1; - }else{ - assert( pNew->token.z==0 ); - } - pNew->span.z = 0; - pNew->pLeft = sqlite3ExprDup(db, p->pLeft); - pNew->pRight = sqlite3ExprDup(db, p->pRight); - pNew->pList = sqlite3ExprListDup(db, p->pList); - pNew->pSelect = sqlite3SelectDup(db, p->pSelect); - return pNew; +Expr *sqlite3ExprDup(sqlite3 *db, Expr *p, int flags){ + return exprDup(db, p, flags, 0); } -void sqlite3TokenCopy(sqlite3 *db, Token *pTo, Token *pFrom){ +void sqlite3TokenCopy(sqlite3 *db, Token *pTo, const Token *pFrom){ if( pTo->dyn ) sqlite3DbFree(db, (char*)pTo->z); if( pFrom->z ){ pTo->n = pFrom->n; @@ -680,7 +885,7 @@ void sqlite3TokenCopy(sqlite3 *db, Token *pTo, Token *pFrom){ pTo->z = 0; } } -ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p){ +ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){ ExprList *pNew; struct ExprList_item *pItem, *pOldItem; int i; @@ -696,17 +901,9 @@ ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p){ } pOldItem = p->a; for(i=0; inExpr; i++, pItem++, pOldItem++){ - Expr *pNewExpr, *pOldExpr; - pItem->pExpr = pNewExpr = sqlite3ExprDup(db, pOldExpr = pOldItem->pExpr); - if( pOldExpr->span.z!=0 && pNewExpr ){ - /* Always make a copy of the span for top-level expressions in the - ** expression list. The logic in SELECT processing that determines - ** the names of columns in the result set needs this information */ - sqlite3TokenCopy(db, &pNewExpr->span, &pOldExpr->span); - } - assert( pNewExpr==0 || pNewExpr->span.z!=0 - || pOldExpr->span.z==0 - || db->mallocFailed ); + Expr *pNewExpr; + Expr *pOldExpr = pOldItem->pExpr; + pItem->pExpr = pNewExpr = sqlite3ExprDup(db, pOldExpr, flags); pItem->zName = sqlite3DbStrDup(db, pOldItem->zName); pItem->sortOrder = pOldItem->sortOrder; pItem->done = 0; @@ -724,7 +921,7 @@ ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p){ */ #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \ || !defined(SQLITE_OMIT_SUBQUERY) -SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p){ +SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){ SrcList *pNew; int i; int nByte; @@ -750,8 +947,8 @@ SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p){ if( pTab ){ pTab->nRef++; } - pNewItem->pSelect = sqlite3SelectDup(db, pOldItem->pSelect); - pNewItem->pOn = sqlite3ExprDup(db, pOldItem->pOn); + pNewItem->pSelect = sqlite3SelectDup(db, pOldItem->pSelect, flags); + pNewItem->pOn = sqlite3ExprDup(db, pOldItem->pOn, flags); pNewItem->pUsing = sqlite3IdListDup(db, pOldItem->pUsing); pNewItem->colUsed = pOldItem->colUsed; } @@ -777,21 +974,24 @@ IdList *sqlite3IdListDup(sqlite3 *db, IdList *p){ } return pNew; } -Select *sqlite3SelectDup(sqlite3 *db, Select *p){ +Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){ Select *pNew; if( p==0 ) return 0; pNew = sqlite3DbMallocRaw(db, sizeof(*p) ); if( pNew==0 ) return 0; - pNew->pEList = sqlite3ExprListDup(db, p->pEList); - pNew->pSrc = sqlite3SrcListDup(db, p->pSrc); - pNew->pWhere = sqlite3ExprDup(db, p->pWhere); - pNew->pGroupBy = sqlite3ExprListDup(db, p->pGroupBy); - pNew->pHaving = sqlite3ExprDup(db, p->pHaving); - pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy); + /* Always make a copy of the span for top-level expressions in the + ** expression list. The logic in SELECT processing that determines + ** the names of columns in the result set needs this information */ + pNew->pEList = sqlite3ExprListDup(db, p->pEList, flags|EXPRDUP_SPAN); + pNew->pSrc = sqlite3SrcListDup(db, p->pSrc, flags); + pNew->pWhere = sqlite3ExprDup(db, p->pWhere, flags); + pNew->pGroupBy = sqlite3ExprListDup(db, p->pGroupBy, flags); + pNew->pHaving = sqlite3ExprDup(db, p->pHaving, flags); + pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy, flags); pNew->op = p->op; - pNew->pPrior = sqlite3SelectDup(db, p->pPrior); - pNew->pLimit = sqlite3ExprDup(db, p->pLimit); - pNew->pOffset = sqlite3ExprDup(db, p->pOffset); + pNew->pPrior = sqlite3SelectDup(db, p->pPrior, flags); + pNew->pLimit = sqlite3ExprDup(db, p->pLimit, flags); + pNew->pOffset = sqlite3ExprDup(db, p->pOffset, flags); pNew->iLimit = 0; pNew->iOffset = 0; pNew->selFlags = p->selFlags & ~SF_UsesEphemeral; @@ -802,7 +1002,7 @@ Select *sqlite3SelectDup(sqlite3 *db, Select *p){ return pNew; } #else -Select *sqlite3SelectDup(sqlite3 *db, Select *p){ +Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){ assert( p==0 ); return 0; } @@ -1143,13 +1343,19 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){ ** If this is the case, it may be possible to use an existing table ** or index instead of generating an epheremal table. */ - p = pX->pSelect; + p = (ExprHasProperty(pX, EP_xIsSelect) ? pX->x.pSelect : 0); if( isCandidateForInOpt(p) ){ - sqlite3 *db = pParse->db; - Index *pIdx; - Expr *pExpr = p->pEList->a[0].pExpr; - int iCol = pExpr->iColumn; - Vdbe *v = sqlite3GetVdbe(pParse); + sqlite3 *db = pParse->db; /* Database connection */ + Expr *pExpr = p->pEList->a[0].pExpr; /* Expression */ + int iCol = pExpr->iColumn; /* Index of column */ + Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */ + Table *pTab = p->pSrc->a[0].pTab; /* Table . */ + int iDb; /* Database idx for pTab */ + + /* Code an OP_VerifyCookie and OP_TableLock for
. */ + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); + sqlite3CodeVerifySchema(pParse, iDb); + sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); /* This function is only called from two places. In both cases the vdbe ** has already been allocated. So assume sqlite3GetVdbe() is always @@ -1159,8 +1365,6 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){ if( iCol<0 ){ int iMem = ++pParse->nMem; int iAddr; - Table *pTab = p->pSrc->a[0].pTab; - int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); sqlite3VdbeUsesBtree(v, iDb); iAddr = sqlite3VdbeAddOp1(v, OP_If, iMem); @@ -1171,17 +1375,17 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){ sqlite3VdbeJumpHere(v, iAddr); }else{ + Index *pIdx; /* Iterator variable */ + /* The collation sequence used by the comparison. If an index is to ** be used in place of a temp-table, it must be ordered according - ** to this collation sequence. - */ + ** to this collation sequence. */ CollSeq *pReq = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pExpr); /* Check that the affinity that will be used to perform the ** comparison is the same as the affinity of the column. If ** it is not, it is not possible to use any index. */ - Table *pTab = p->pSrc->a[0].pTab; char aff = comparisonAffinity(pX); int affinity_ok = (pTab->aCol[iCol].affinity==aff||aff==SQLITE_AFF_NONE); @@ -1190,7 +1394,6 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){ && (pReq==sqlite3FindCollSeq(db, ENC(db), pIdx->azColl[0], -1, 0)) && (!mustBeUnique || (pIdx->nColumn==1 && pIdx->onError!=OE_None)) ){ - int iDb; int iMem = ++pParse->nMem; int iAddr; char *pKey; @@ -1202,7 +1405,6 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){ iAddr = sqlite3VdbeAddOp1(v, OP_If, iMem); sqlite3VdbeAddOp2(v, OP_Integer, 1, iMem); - sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pIdx->nColumn); sqlite3VdbeAddOp4(v, OP_OpenRead, iTab, pIdx->tnum, iDb, pKey,P4_KEYINFO_HANDOFF); VdbeComment((v, "%s", pIdx->zName)); @@ -1222,7 +1424,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){ eType = IN_INDEX_EPH; if( prNotFound ){ *prNotFound = rMayHaveNull = ++pParse->nMem; - }else if( pX->pLeft->iColumn<0 && pX->pSelect==0 ){ + }else if( pX->pLeft->iColumn<0 && !ExprHasAnyProperty(pX, EP_xIsSelect) ){ eType = IN_INDEX_ROWID; } sqlite3CodeSubselect(pParse, pX, rMayHaveNull, eType==IN_INDEX_ROWID); @@ -1311,7 +1513,7 @@ void sqlite3CodeSubselect( memset(&keyInfo, 0, sizeof(keyInfo)); keyInfo.nField = 1; - if( pExpr->pSelect ){ + if( ExprHasProperty(pExpr, EP_xIsSelect) ){ /* Case 1: expr IN (SELECT ...) ** ** Generate code to write the results of the select into the temporary @@ -1324,15 +1526,15 @@ void sqlite3CodeSubselect( sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable); dest.affinity = (u8)affinity; assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable ); - if( sqlite3Select(pParse, pExpr->pSelect, &dest) ){ + if( sqlite3Select(pParse, pExpr->x.pSelect, &dest) ){ return; } - pEList = pExpr->pSelect->pEList; + pEList = pExpr->x.pSelect->pEList; if( pEList && pEList->nExpr>0 ){ keyInfo.aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft, pEList->a[0].pExpr); } - }else if( pExpr->pList ){ + }else if( pExpr->x.pList ){ /* Case 2: expr IN (exprlist) ** ** For each expression, build an index key from the evaluation and @@ -1341,7 +1543,7 @@ void sqlite3CodeSubselect( ** a column, use numeric affinity. */ int i; - ExprList *pList = pExpr->pList; + ExprList *pList = pExpr->x.pList; struct ExprList_item *pItem; int r1, r2, r3; @@ -1401,7 +1603,8 @@ void sqlite3CodeSubselect( Select *pSel; SelectDest dest; - pSel = pExpr->pSelect; + assert( ExprHasProperty(pExpr, EP_xIsSelect) ); + pSel = pExpr->x.pSelect; sqlite3SelectDestInit(&dest, 0, ++pParse->nMem); if( pExpr->op==TK_SELECT ){ dest.eDest = SRT_Mem; @@ -1795,7 +1998,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ break; } case TK_STRING: { - sqlite3DequoteExpr(db, pExpr); + sqlite3DequoteExpr(pExpr); sqlite3VdbeAddOp4(v,OP_String8, 0, target, 0, (char*)pExpr->token.z, pExpr->token.n); break; @@ -1821,9 +2024,26 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ } #endif case TK_VARIABLE: { - sqlite3VdbeAddOp2(v, OP_Variable, pExpr->iTable, target); - if( pExpr->token.n>1 ){ - sqlite3VdbeChangeP4(v, -1, (char*)pExpr->token.z, pExpr->token.n); + int iPrior; + VdbeOp *pOp; + if( pExpr->token.n<=1 + && (iPrior = sqlite3VdbeCurrentAddr(v)-1)>=0 + && (pOp = sqlite3VdbeGetOp(v, iPrior))->opcode==OP_Variable + && pOp->p1+pOp->p3==pExpr->iTable + && pOp->p2+pOp->p3==target + && pOp->p4.z==0 + ){ + /* If the previous instruction was a copy of the previous unnamed + ** parameter into the previous register, then simply increment the + ** repeat count on the prior instruction rather than making a new + ** instruction. + */ + pOp->p3++; + }else{ + sqlite3VdbeAddOp3(v, OP_Variable, pExpr->iTable, target, 1); + if( pExpr->token.n>1 ){ + sqlite3VdbeChangeP4(v, -1, (char*)pExpr->token.z, pExpr->token.n); + } } break; } @@ -1985,28 +2205,34 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ } case TK_CONST_FUNC: case TK_FUNCTION: { - ExprList *pList = pExpr->pList; - int nExpr = pList ? pList->nExpr : 0; - FuncDef *pDef; - int nId; - const char *zId; - int constMask = 0; - int i; - u8 enc = ENC(db); - CollSeq *pColl = 0; + ExprList *pFarg; /* List of function arguments */ + int nFarg; /* Number of function arguments */ + FuncDef *pDef; /* The function definition object */ + int nId; /* Length of the function name in bytes */ + const char *zId; /* The function name */ + int constMask = 0; /* Mask of function arguments that are constant */ + int i; /* Loop counter */ + u8 enc = ENC(db); /* The text encoding used by this database */ + CollSeq *pColl = 0; /* A collating sequence */ + assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); testcase( op==TK_CONST_FUNC ); testcase( op==TK_FUNCTION ); + if( ExprHasAnyProperty(pExpr, EP_TokenOnly|EP_SpanToken) ){ + pFarg = 0; + }else{ + pFarg = pExpr->x.pList; + } + nFarg = pFarg ? pFarg->nExpr : 0; zId = (char*)pExpr->token.z; nId = pExpr->token.n; - pDef = sqlite3FindFunction(db, zId, nId, nExpr, enc, 0); + pDef = sqlite3FindFunction(db, zId, nId, nFarg, enc, 0); assert( pDef!=0 ); - if( pList ){ - nExpr = pList->nExpr; - r1 = sqlite3GetTempRange(pParse, nExpr); - sqlite3ExprCodeExprList(pParse, pList, r1, 1); + if( pFarg ){ + r1 = sqlite3GetTempRange(pParse, nFarg); + sqlite3ExprCodeExprList(pParse, pFarg, r1, 1); }else{ - nExpr = r1 = 0; + r1 = 0; } #ifndef SQLITE_OMIT_VIRTUALTABLE /* Possibly overload the function if the first argument is @@ -2021,18 +2247,18 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ ** "glob(B,A). We want to use the A in "A glob B" to test ** for function overloading. But we use the B term in "glob(B,A)". */ - if( nExpr>=2 && (pExpr->flags & EP_InfixFunc) ){ - pDef = sqlite3VtabOverloadFunction(db, pDef, nExpr, pList->a[1].pExpr); - }else if( nExpr>0 ){ - pDef = sqlite3VtabOverloadFunction(db, pDef, nExpr, pList->a[0].pExpr); + if( nFarg>=2 && (pExpr->flags & EP_InfixFunc) ){ + pDef = sqlite3VtabOverloadFunction(db, pDef, nFarg, pFarg->a[1].pExpr); + }else if( nFarg>0 ){ + pDef = sqlite3VtabOverloadFunction(db, pDef, nFarg, pFarg->a[0].pExpr); } #endif - for(i=0; ia[i].pExpr) ){ + for(i=0; ia[i].pExpr) ){ constMask |= (1<flags & SQLITE_FUNC_NEEDCOLL)!=0 && !pColl ){ - pColl = sqlite3ExprCollSeq(pParse, pList->a[i].pExpr); + pColl = sqlite3ExprCollSeq(pParse, pFarg->a[i].pExpr); } } if( pDef->flags & SQLITE_FUNC_NEEDCOLL ){ @@ -2041,11 +2267,11 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ } sqlite3VdbeAddOp4(v, OP_Function, constMask, r1, target, (char*)pDef, P4_FUNCDEF); - sqlite3VdbeChangeP5(v, (u8)nExpr); - if( nExpr ){ - sqlite3ReleaseTempRange(pParse, r1, nExpr); + sqlite3VdbeChangeP5(v, (u8)nFarg); + if( nFarg ){ + sqlite3ReleaseTempRange(pParse, r1, nFarg); } - sqlite3ExprCacheAffinityChange(pParse, r1, nExpr); + sqlite3ExprCacheAffinityChange(pParse, r1, nFarg); break; } #ifndef SQLITE_OMIT_SUBQUERY @@ -2162,7 +2388,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ */ case TK_BETWEEN: { Expr *pLeft = pExpr->pLeft; - struct ExprList_item *pLItem = pExpr->pList->a; + struct ExprList_item *pLItem = pExpr->x.pList->a; Expr *pRight = pLItem->pExpr; codeCompareOperands(pParse, pLeft, &r1, ®Free1, @@ -2222,10 +2448,10 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ Expr *pX; /* The X expression */ Expr *pTest = 0; /* X==Ei (form A) or just Ei (form B) */ - assert(pExpr->pList); - assert((pExpr->pList->nExpr % 2) == 0); - assert(pExpr->pList->nExpr > 0); - pEList = pExpr->pList; + assert( !ExprHasProperty(pExpr, EP_xIsSelect) && pExpr->x.pList ); + assert((pExpr->x.pList->nExpr % 2) == 0); + assert(pExpr->x.pList->nExpr > 0); + pEList = pExpr->x.pList; aListelem = pEList->a; nExpr = pEList->nExpr; endLabel = sqlite3VdbeMakeLabel(v); @@ -2273,15 +2499,15 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ "RAISE() may only be used within a trigger-program"); return 0; } - if( pExpr->iColumn!=OE_Ignore ){ - assert( pExpr->iColumn==OE_Rollback || - pExpr->iColumn == OE_Abort || - pExpr->iColumn == OE_Fail ); - sqlite3DequoteExpr(db, pExpr); - sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, pExpr->iColumn, 0, + if( pExpr->affinity!=OE_Ignore ){ + assert( pExpr->affinity==OE_Rollback || + pExpr->affinity == OE_Abort || + pExpr->affinity == OE_Fail ); + sqlite3DequoteExpr(pExpr); + sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, pExpr->affinity, 0, (char*)pExpr->token.z, pExpr->token.n); } else { - assert( pExpr->iColumn == OE_Ignore ); + assert( pExpr->affinity == OE_Ignore ); sqlite3VdbeAddOp2(v, OP_ContextPop, 0, 0); sqlite3VdbeAddOp2(v, OP_Goto, 0, pParse->trigStack->ignoreJump); VdbeComment((v, "raise(IGNORE)")); @@ -2438,7 +2664,8 @@ static int evalConstExpr(Walker *pWalker, Expr *pExpr){ ** Mark them this way to avoid generated unneeded OP_SCopy ** instructions. */ - ExprList *pList = pExpr->pList; + ExprList *pList = pExpr->x.pList; + assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); if( pList ){ int i = pList->nExpr; struct ExprList_item *pItem = pList->a; @@ -2614,16 +2841,17 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ Expr compRight; Expr exprX; + assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); exprX = *pExpr->pLeft; exprAnd.op = TK_AND; exprAnd.pLeft = &compLeft; exprAnd.pRight = &compRight; compLeft.op = TK_GE; compLeft.pLeft = &exprX; - compLeft.pRight = pExpr->pList->a[0].pExpr; + compLeft.pRight = pExpr->x.pList->a[0].pExpr; compRight.op = TK_LE; compRight.pLeft = &exprX; - compRight.pRight = pExpr->pList->a[1].pExpr; + compRight.pRight = pExpr->x.pList->a[1].pExpr; exprX.iTable = sqlite3ExprCodeTemp(pParse, &exprX, ®Free1); testcase( regFree1==0 ); exprX.op = TK_REGISTER; @@ -2765,16 +2993,17 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ Expr compRight; Expr exprX; + assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); exprX = *pExpr->pLeft; exprAnd.op = TK_AND; exprAnd.pLeft = &compLeft; exprAnd.pRight = &compRight; compLeft.op = TK_GE; compLeft.pLeft = &exprX; - compLeft.pRight = pExpr->pList->a[0].pExpr; + compLeft.pRight = pExpr->x.pList->a[0].pExpr; compRight.op = TK_LE; compRight.pLeft = &exprX; - compRight.pRight = pExpr->pList->a[1].pExpr; + compRight.pRight = pExpr->x.pList->a[1].pExpr; exprX.iTable = sqlite3ExprCodeTemp(pParse, &exprX, ®Free1); testcase( regFree1==0 ); exprX.op = TK_REGISTER; @@ -2813,22 +3042,25 @@ int sqlite3ExprCompare(Expr *pA, Expr *pB){ if( pA==0||pB==0 ){ return pB==pA; } - if( pA->op!=pB->op ) return 0; - if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 0; - if( !sqlite3ExprCompare(pA->pLeft, pB->pLeft) ) return 0; - if( !sqlite3ExprCompare(pA->pRight, pB->pRight) ) return 0; - if( pA->pList ){ - if( pB->pList==0 ) return 0; - if( pA->pList->nExpr!=pB->pList->nExpr ) return 0; - for(i=0; ipList->nExpr; i++){ - if( !sqlite3ExprCompare(pA->pList->a[i].pExpr, pB->pList->a[i].pExpr) ){ - return 0; - } - } - }else if( pB->pList ){ + if( ExprHasProperty(pA, EP_xIsSelect) || ExprHasProperty(pB, EP_xIsSelect) ){ return 0; } - if( pA->pSelect || pB->pSelect ) return 0; + if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 0; + if( pA->op!=pB->op ) return 0; + if( !sqlite3ExprCompare(pA->pLeft, pB->pLeft) ) return 0; + if( !sqlite3ExprCompare(pA->pRight, pB->pRight) ) return 0; + + if( pA->x.pList && pB->x.pList ){ + if( pA->x.pList->nExpr!=pB->x.pList->nExpr ) return 0; + for(i=0; ix.pList->nExpr; i++){ + Expr *pExprA = pA->x.pList->a[i].pExpr; + Expr *pExprB = pB->x.pList->a[i].pExpr; + if( !sqlite3ExprCompare(pExprA, pExprB) ) return 0; + } + }else if( pA->x.pList || pB->x.pList ){ + return 0; + } + if( pA->iTable!=pB->iTable || pA->iColumn!=pB->iColumn ) return 0; if( pA->op!=TK_COLUMN && pA->token.z ){ if( pB->token.z==0 ) return 0; @@ -2976,12 +3208,13 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ u8 enc = ENC(pParse->db); i = addAggInfoFunc(pParse->db, pAggInfo); if( i>=0 ){ + assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); pItem = &pAggInfo->aFunc[i]; pItem->pExpr = pExpr; pItem->iMem = ++pParse->nMem; pItem->pFunc = sqlite3FindFunction(pParse->db, (char*)pExpr->token.z, pExpr->token.n, - pExpr->pList ? pExpr->pList->nExpr : 0, enc, 0); + pExpr->x.pList ? pExpr->x.pList->nExpr : 0, enc, 0); if( pExpr->flags & EP_Distinct ){ pItem->iDistinct = pParse->nTab++; }else{ diff --git a/src/func.c b/src/func.c index 07671ff..058b158 100644 --- a/src/func.c +++ b/src/func.c @@ -16,7 +16,7 @@ ** sqliteRegisterBuildinFunctions() found at the bottom of the file. ** All other code has file scope. ** -** $Id: func.c,v 1.222 2009/02/04 03:59:25 shane Exp $ +** $Id: func.c,v 1.231 2009/04/08 23:04:14 drh Exp $ */ #include "sqliteInt.h" #include @@ -266,16 +266,22 @@ static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ /* ** Allocate nByte bytes of space using sqlite3_malloc(). If the ** allocation fails, call sqlite3_result_error_nomem() to notify -** the database handle that malloc() has failed. +** the database handle that malloc() has failed and return NULL. +** If nByte is larger than the maximum string or blob length, then +** raise an SQLITE_TOOBIG exception and return NULL. */ static void *contextMalloc(sqlite3_context *context, i64 nByte){ char *z; - if( nByte>sqlite3_context_db_handle(context)->aLimit[SQLITE_LIMIT_LENGTH] ){ + sqlite3 *db = sqlite3_context_db_handle(context); + assert( nByte>0 ); + testcase( nByte==db->aLimit[SQLITE_LIMIT_LENGTH] ); + testcase( nByte==db->aLimit[SQLITE_LIMIT_LENGTH]+1 ); + if( nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){ sqlite3_result_error_toobig(context); z = 0; }else{ z = sqlite3Malloc((int)nByte); - if( !z && nByte>0 ){ + if( !z ){ sqlite3_result_error_nomem(context); } } @@ -356,8 +362,17 @@ static void randomFunc( sqlite_int64 r; UNUSED_PARAMETER2(NotUsed, NotUsed2); sqlite3_randomness(sizeof(r), &r); - if( (r<<1)==0 ) r = 0; /* Prevent 0x8000.... as the result so that we */ - /* can always do abs() of the result */ + if( r<0 ){ + /* We need to prevent a random number of 0x8000000000000000 + ** (or -9223372036854775808) since when you do abs() of that + ** number of you get the same value back again. To do this + ** in a way that is testable, mask the sign bit off of negative + ** values, resulting in a positive value. Then take the + ** 2s complement of that positive value. The end result can + ** therefore be no less than -9223372036854775807. + */ + r = -(r ^ (((sqlite3_int64)1)<<63)); + } sqlite3_result_int64(context, r); } @@ -444,7 +459,7 @@ struct compareInfo { ** whereas only characters less than 0x80 do in ASCII. */ #if defined(SQLITE_EBCDIC) -# define sqlite3Utf8Read(A,B,C) (*(A++)) +# define sqlite3Utf8Read(A,C) (*(A++)) # define GlogUpperToLower(A) A = sqlite3UpperToLower[A] #else # define GlogUpperToLower(A) if( A<0x80 ){ A = sqlite3UpperToLower[A]; } @@ -501,18 +516,18 @@ static int patternCompare( u8 noCase = pInfo->noCase; int prevEscape = 0; /* True if the previous character was 'escape' */ - while( (c = sqlite3Utf8Read(zPattern,0,&zPattern))!=0 ){ + while( (c = sqlite3Utf8Read(zPattern,&zPattern))!=0 ){ if( !prevEscape && c==matchAll ){ - while( (c=sqlite3Utf8Read(zPattern,0,&zPattern)) == matchAll + while( (c=sqlite3Utf8Read(zPattern,&zPattern)) == matchAll || c == matchOne ){ - if( c==matchOne && sqlite3Utf8Read(zString, 0, &zString)==0 ){ + if( c==matchOne && sqlite3Utf8Read(zString, &zString)==0 ){ return 0; } } if( c==0 ){ return 1; }else if( c==esc ){ - c = sqlite3Utf8Read(zPattern, 0, &zPattern); + c = sqlite3Utf8Read(zPattern, &zPattern); if( c==0 ){ return 0; } @@ -524,17 +539,17 @@ static int patternCompare( } return *zString!=0; } - while( (c2 = sqlite3Utf8Read(zString,0,&zString))!=0 ){ + while( (c2 = sqlite3Utf8Read(zString,&zString))!=0 ){ if( noCase ){ GlogUpperToLower(c2); GlogUpperToLower(c); while( c2 != 0 && c2 != c ){ - c2 = sqlite3Utf8Read(zString, 0, &zString); + c2 = sqlite3Utf8Read(zString, &zString); GlogUpperToLower(c2); } }else{ while( c2 != 0 && c2 != c ){ - c2 = sqlite3Utf8Read(zString, 0, &zString); + c2 = sqlite3Utf8Read(zString, &zString); } } if( c2==0 ) return 0; @@ -542,7 +557,7 @@ static int patternCompare( } return 0; }else if( !prevEscape && c==matchOne ){ - if( sqlite3Utf8Read(zString, 0, &zString)==0 ){ + if( sqlite3Utf8Read(zString, &zString)==0 ){ return 0; } }else if( c==matchSet ){ @@ -550,20 +565,20 @@ static int patternCompare( assert( esc==0 ); /* This only occurs for GLOB, not LIKE */ seen = 0; invert = 0; - c = sqlite3Utf8Read(zString, 0, &zString); + c = sqlite3Utf8Read(zString, &zString); if( c==0 ) return 0; - c2 = sqlite3Utf8Read(zPattern, 0, &zPattern); + c2 = sqlite3Utf8Read(zPattern, &zPattern); if( c2=='^' ){ invert = 1; - c2 = sqlite3Utf8Read(zPattern, 0, &zPattern); + c2 = sqlite3Utf8Read(zPattern, &zPattern); } if( c2==']' ){ if( c==']' ) seen = 1; - c2 = sqlite3Utf8Read(zPattern, 0, &zPattern); + c2 = sqlite3Utf8Read(zPattern, &zPattern); } while( c2 && c2!=']' ){ if( c2=='-' && zPattern[0]!=']' && zPattern[0]!=0 && prior_c>0 ){ - c2 = sqlite3Utf8Read(zPattern, 0, &zPattern); + c2 = sqlite3Utf8Read(zPattern, &zPattern); if( c>=prior_c && c<=c2 ) seen = 1; prior_c = 0; }else{ @@ -572,7 +587,7 @@ static int patternCompare( } prior_c = c2; } - c2 = sqlite3Utf8Read(zPattern, 0, &zPattern); + c2 = sqlite3Utf8Read(zPattern, &zPattern); } if( c2==0 || (seen ^ invert)==0 ){ return 0; @@ -580,7 +595,7 @@ static int patternCompare( }else if( esc==c && !prevEscape ){ prevEscape = 1; }else{ - c2 = sqlite3Utf8Read(zString, 0, &zString); + c2 = sqlite3Utf8Read(zString, &zString); if( noCase ){ GlogUpperToLower(c); GlogUpperToLower(c2); @@ -623,6 +638,7 @@ static void likeFunc( ){ const unsigned char *zA, *zB; int escape = 0; + int nPat; sqlite3 *db = sqlite3_context_db_handle(context); zB = sqlite3_value_text(argv[0]); @@ -631,8 +647,10 @@ static void likeFunc( /* Limit the length of the LIKE or GLOB pattern to avoid problems ** of deep recursion and N*N behavior in patternCompare(). */ - if( sqlite3_value_bytes(argv[0]) > - db->aLimit[SQLITE_LIMIT_LIKE_PATTERN_LENGTH] ){ + nPat = sqlite3_value_bytes(argv[0]); + testcase( nPat==db->aLimit[SQLITE_LIMIT_LIKE_PATTERN_LENGTH] ); + testcase( nPat==db->aLimit[SQLITE_LIMIT_LIKE_PATTERN_LENGTH]+1 ); + if( nPat > db->aLimit[SQLITE_LIMIT_LIKE_PATTERN_LENGTH] ){ sqlite3_result_error(context, "LIKE or GLOB pattern too complex", -1); return; } @@ -649,7 +667,7 @@ static void likeFunc( "ESCAPE expression must be a single character", -1); return; } - escape = sqlite3Utf8Read(zEsc, 0, &zEsc); + escape = sqlite3Utf8Read(zEsc, &zEsc); } if( zA && zB ){ struct compareInfo *pInfo = sqlite3_user_data(context); @@ -808,10 +826,13 @@ static void zeroblobFunc( sqlite3_value **argv ){ i64 n; + sqlite3 *db = sqlite3_context_db_handle(context); assert( argc==1 ); UNUSED_PARAMETER(argc); n = sqlite3_value_int64(argv[0]); - if( n>SQLITE_MAX_LENGTH ){ + testcase( n==db->aLimit[SQLITE_LIMIT_LENGTH] ); + testcase( n==db->aLimit[SQLITE_LIMIT_LENGTH]+1 ); + if( n>db->aLimit[SQLITE_LIMIT_LENGTH] ){ sqlite3_result_error_toobig(context); }else{ sqlite3_result_zeroblob(context, (int)n); @@ -877,7 +898,9 @@ static void replaceFunc( u8 *zOld; sqlite3 *db = sqlite3_context_db_handle(context); nOut += nRep - nPattern; - if( nOut>=db->aLimit[SQLITE_LIMIT_LENGTH] ){ + testcase( nOut-1==db->aLimit[SQLITE_LIMIT_LENGTH] ); + testcase( nOut-2==db->aLimit[SQLITE_LIMIT_LENGTH] ); + if( nOut-1>db->aLimit[SQLITE_LIMIT_LENGTH] ){ sqlite3_result_error_toobig(context); sqlite3DbFree(db, zOut); return; @@ -961,7 +984,7 @@ static void trimFunc( int len = 0; for(i=0; i=nChar ) break; zIn += len; @@ -1155,6 +1178,13 @@ static void countStep(sqlite3_context *context, int argc, sqlite3_value **argv){ if( (argc==0 || SQLITE_NULL!=sqlite3_value_type(argv[0])) && p ){ p->n++; } + + /* The sqlite3_aggregate_count() function is deprecated. But just to make + ** sure it still operates correctly, verify that its count agrees with our + ** internal count when using count(*) and when the total count can be + ** expressed as a 32-bit integer. */ + assert( argc==1 || p==0 || p->n>0x7fffffff + || p->n==sqlite3_aggregate_count(context) ); } static void countFinalize(sqlite3_context *context){ CountCtx *p; @@ -1203,7 +1233,7 @@ static void minMaxFinalize(sqlite3_context *context){ sqlite3_value *pRes; pRes = (sqlite3_value *)sqlite3_aggregate_context(context, 0); if( pRes ){ - if( pRes->flags ){ + if( ALWAYS(pRes->flags) ){ sqlite3_result_value(context, pRes); } sqlite3VdbeMemRelease(pRes); @@ -1288,7 +1318,7 @@ static void setLikeOptFlag(sqlite3 *db, const char *zName, u8 flagVal){ FuncDef *pDef; pDef = sqlite3FindFunction(db, zName, sqlite3Strlen30(zName), 2, SQLITE_UTF8, 0); - if( pDef ){ + if( ALWAYS(pDef) ){ pDef->flags = flagVal; } } @@ -1305,9 +1335,9 @@ void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){ }else{ pInfo = (struct compareInfo*)&likeInfoNorm; } - sqlite3CreateFunc(db, "like", 2, SQLITE_UTF8, pInfo, likeFunc, 0, 0); - sqlite3CreateFunc(db, "like", 3, SQLITE_UTF8, pInfo, likeFunc, 0, 0); - sqlite3CreateFunc(db, "glob", 2, SQLITE_UTF8, + sqlite3CreateFunc(db, "like", 2, SQLITE_ANY, pInfo, likeFunc, 0, 0); + sqlite3CreateFunc(db, "like", 3, SQLITE_ANY, pInfo, likeFunc, 0, 0); + sqlite3CreateFunc(db, "glob", 2, SQLITE_ANY, (struct compareInfo*)&globInfo, likeFunc, 0,0); setLikeOptFlag(db, "glob", SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE); setLikeOptFlag(db, "like", @@ -1323,15 +1353,16 @@ void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){ */ int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){ FuncDef *pDef; - if( pExpr->op!=TK_FUNCTION || !pExpr->pList ){ - return 0; - } - if( pExpr->pList->nExpr!=2 ){ + if( pExpr->op!=TK_FUNCTION + || !pExpr->x.pList + || pExpr->x.pList->nExpr!=2 + ){ return 0; } + assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); pDef = sqlite3FindFunction(db, (char*)pExpr->token.z, pExpr->token.n, 2, SQLITE_UTF8, 0); - if( pDef==0 || (pDef->flags & SQLITE_FUNC_LIKE)==0 ){ + if( NEVER(pDef==0) || (pDef->flags & SQLITE_FUNC_LIKE)==0 ){ return 0; } @@ -1412,7 +1443,8 @@ void sqlite3RegisterGlobalFunctions(void){ AGGREGATE(sum, 1, 0, 0, sumStep, sumFinalize ), AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize ), AGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize ), - AGGREGATE(count, 0, 0, 0, countStep, countFinalize ), + /* AGGREGATE(count, 0, 0, 0, countStep, countFinalize ), */ + {0,SQLITE_UTF8,SQLITE_FUNC_COUNT,0,0,0,countStep,countFinalize,"count",0}, AGGREGATE(count, 1, 0, 0, countStep, countFinalize ), AGGREGATE(group_concat, 1, 0, 0, groupConcatStep, groupConcatFinalize), AGGREGATE(group_concat, 2, 0, 0, groupConcatStep, groupConcatFinalize), diff --git a/src/insert.c b/src/insert.c index 0ab647e..9b23c86 100644 --- a/src/insert.c +++ b/src/insert.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle INSERT statements in SQLite. ** -** $Id: insert.c,v 1.256 2008/12/10 21:19:57 drh Exp $ +** $Id: insert.c,v 1.260 2009/02/28 10:47:42 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -168,7 +168,7 @@ static int autoIncBegin( if( pTab->tabFlags & TF_Autoincrement ){ Vdbe *v = pParse->pVdbe; Db *pDb = &pParse->db->aDb[iDb]; - int iCur = pParse->nTab; + int iCur = pParse->nTab++; int addr; /* Address of the top of the loop */ assert( v ); pParse->nMem++; /* Holds name of table */ @@ -217,7 +217,7 @@ static void autoIncEnd( int memId /* Memory cell holding the maximum rowid */ ){ if( pTab->tabFlags & TF_Autoincrement ){ - int iCur = pParse->nTab; + int iCur = pParse->nTab++; Vdbe *v = pParse->pVdbe; Db *pDb = &pParse->db->aDb[iDb]; int j1; @@ -401,7 +401,8 @@ void sqlite3Insert( #ifndef SQLITE_OMIT_TRIGGER int isView; /* True if attempting to insert into a view */ - int triggers_exist = 0; /* True if there are FOR EACH ROW triggers */ + Trigger *pTrigger; /* List of triggers on pTab, if required */ + int tmask; /* Mask of trigger times */ #endif db = pParse->db; @@ -431,22 +432,24 @@ void sqlite3Insert( ** inserted into is a view */ #ifndef SQLITE_OMIT_TRIGGER - triggers_exist = sqlite3TriggersExist(pTab, TK_INSERT, 0); + pTrigger = sqlite3TriggersExist(pParse, pTab, TK_INSERT, 0, &tmask); isView = pTab->pSelect!=0; #else -# define triggers_exist 0 +# define pTrigger 0 +# define tmask 0 # define isView 0 #endif #ifdef SQLITE_OMIT_VIEW # undef isView # define isView 0 #endif + assert( (pTrigger && tmask) || (pTrigger==0 && tmask==0) ); /* Ensure that: * (a) the table is not read-only, * (b) that if it is a view then ON INSERT triggers exist */ - if( sqlite3IsReadOnly(pParse, pTab, triggers_exist) ){ + if( sqlite3IsReadOnly(pParse, pTab, tmask) ){ goto insert_cleanup; } assert( pTab!=0 ); @@ -464,10 +467,10 @@ void sqlite3Insert( v = sqlite3GetVdbe(pParse); if( v==0 ) goto insert_cleanup; if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); - sqlite3BeginWriteOperation(pParse, pSelect || triggers_exist, iDb); + sqlite3BeginWriteOperation(pParse, pSelect || pTrigger, iDb); /* if there are row triggers, allocate a temp table for new.* references. */ - if( triggers_exist ){ + if( pTrigger ){ newIdx = pParse->nTab++; } @@ -482,7 +485,7 @@ void sqlite3Insert( ** This is the 2nd template. */ if( pColumn==0 && xferOptimization(pParse, pTab, pSelect, onError, iDb) ){ - assert( !triggers_exist ); + assert( !pTrigger ); assert( pList==0 ); goto insert_cleanup; } @@ -557,7 +560,7 @@ void sqlite3Insert( ** of the tables being read by the SELECT statement. Also use a ** temp table in the case of row triggers. */ - if( triggers_exist || readsTable(v, addrSelect, iDb, pTab) ){ + if( pTrigger || readsTable(v, addrSelect, iDb, pTab) ){ useTempTable = 1; } @@ -676,9 +679,8 @@ void sqlite3Insert( /* Open the temp table for FOR EACH ROW triggers */ - if( triggers_exist ){ - sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol); - sqlite3VdbeAddOp2(v, OP_OpenPseudo, newIdx, 0); + if( pTrigger ){ + sqlite3VdbeAddOp3(v, OP_OpenPseudo, newIdx, 0, pTab->nCol); } /* Initialize the count of rows to be inserted @@ -745,7 +747,7 @@ void sqlite3Insert( /* Run the BEFORE and INSTEAD OF triggers, if there are any */ endOfLoop = sqlite3VdbeMakeLabel(v); - if( triggers_exist & TRIGGER_BEFORE ){ + if( tmask & TRIGGER_BEFORE ){ int regTrigRowid; int regCols; int regRec; @@ -813,8 +815,8 @@ void sqlite3Insert( sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol); /* Fire BEFORE or INSTEAD OF triggers */ - if( sqlite3CodeRowTrigger(pParse, TK_INSERT, 0, TRIGGER_BEFORE, pTab, - newIdx, -1, onError, endOfLoop, 0, 0) ){ + if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_BEFORE, + pTab, newIdx, -1, onError, endOfLoop, 0, 0) ){ goto insert_cleanup; } } @@ -936,7 +938,7 @@ void sqlite3Insert( regIns, aRegIdx, 0, - (triggers_exist & TRIGGER_AFTER)!=0 ? newIdx : -1, + (tmask&TRIGGER_AFTER) ? newIdx : -1, appendFlag ); } @@ -948,10 +950,10 @@ void sqlite3Insert( sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1); } - if( triggers_exist ){ + if( pTrigger ){ /* Code AFTER triggers */ - if( sqlite3CodeRowTrigger(pParse, TK_INSERT, 0, TRIGGER_AFTER, pTab, - newIdx, -1, onError, endOfLoop, 0, 0) ){ + if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_AFTER, + pTab, newIdx, -1, onError, endOfLoop, 0, 0) ){ goto insert_cleanup; } } @@ -1125,7 +1127,6 @@ void sqlite3GenerateConstraintChecks( if( onError==OE_Replace && pTab->aCol[i].pDflt==0 ){ onError = OE_Abort; } - j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regData+i); assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail || onError==OE_Ignore || onError==OE_Replace ); switch( onError ){ @@ -1133,22 +1134,24 @@ void sqlite3GenerateConstraintChecks( case OE_Abort: case OE_Fail: { char *zMsg; - sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_CONSTRAINT, onError); + j1 = sqlite3VdbeAddOp3(v, OP_HaltIfNull, + SQLITE_CONSTRAINT, onError, regData+i); zMsg = sqlite3MPrintf(pParse->db, "%s.%s may not be NULL", pTab->zName, pTab->aCol[i].zName); sqlite3VdbeChangeP4(v, -1, zMsg, P4_DYNAMIC); break; } case OE_Ignore: { - sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest); + sqlite3VdbeAddOp2(v, OP_IsNull, regData+i, ignoreDest); break; } case OE_Replace: { + j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regData+i); sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regData+i); + sqlite3VdbeJumpHere(v, j1); break; } } - sqlite3VdbeJumpHere(v, j1); } /* Test all CHECK constraints @@ -1530,7 +1533,7 @@ static int xferOptimization( if( pSelect==0 ){ return 0; /* Must be of the form INSERT INTO ... SELECT ... */ } - if( pDest->pTrigger ){ + if( sqlite3TriggerList(pParse, pDest) ){ return 0; /* tab1 must not have triggers */ } #ifndef SQLITE_OMIT_VIRTUALTABLE diff --git a/src/legacy.c b/src/legacy.c index e83817d..75de5dc 100644 --- a/src/legacy.c +++ b/src/legacy.c @@ -14,7 +14,7 @@ ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** -** $Id: legacy.c,v 1.31 2009/01/20 16:53:40 danielk1977 Exp $ +** $Id: legacy.c,v 1.32 2009/03/19 18:51:07 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -101,7 +101,7 @@ int sqlite3_exec( } if( xCallback(pArg, nCol, azVals, azCols) ){ rc = SQLITE_ABORT; - sqlite3_finalize(pStmt); + sqlite3VdbeFinalize((Vdbe *)pStmt); pStmt = 0; sqlite3Error(db, SQLITE_ABORT, 0); goto exec_out; @@ -109,7 +109,7 @@ int sqlite3_exec( } if( rc!=SQLITE_ROW ){ - rc = sqlite3_finalize(pStmt); + rc = sqlite3VdbeFinalize((Vdbe *)pStmt); pStmt = 0; if( rc!=SQLITE_SCHEMA ){ nRetry = 0; @@ -125,7 +125,7 @@ int sqlite3_exec( } exec_out: - if( pStmt ) sqlite3_finalize(pStmt); + if( pStmt ) sqlite3VdbeFinalize((Vdbe *)pStmt); sqlite3DbFree(db, azCols); rc = sqlite3ApiExit(db, rc); diff --git a/src/main.c b/src/main.c index 0339379..e9447e3 100644 --- a/src/main.c +++ b/src/main.c @@ -14,7 +14,7 @@ ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** -** $Id: main.c,v 1.528 2009/02/05 16:31:46 drh Exp $ +** $Id: main.c,v 1.536 2009/04/09 01:23:49 drh Exp $ */ #include "sqliteInt.h" @@ -219,6 +219,7 @@ int sqlite3_shutdown(void){ if( sqlite3GlobalConfig.isInit ){ sqlite3_os_end(); } + sqlite3_reset_auto_extension(); sqlite3MallocEnd(); sqlite3MutexEnd(); sqlite3GlobalConfig.isInit = 0; @@ -404,12 +405,12 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){ sz = 0; pStart = 0; }else if( pBuf==0 ){ - sz = (sz + 7)&~7; + sz = ROUND8(sz); sqlite3BeginBenignMalloc(); pStart = sqlite3Malloc( sz*cnt ); sqlite3EndBenignMalloc(); }else{ - sz = sz&~7; + sz = ROUNDDOWN8(sz); pStart = pBuf; } db->lookaside.pStart = pStart; @@ -418,7 +419,7 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){ if( pStart ){ int i; LookasideSlot *p; - assert( sz > sizeof(LookasideSlot*) ); + assert( sz > (int)sizeof(LookasideSlot*) ); p = (LookasideSlot*)pStart; for(i=cnt-1; i>=0; i--){ p->pNext = db->lookaside.pFree; @@ -560,6 +561,7 @@ void sqlite3CloseSavepoints(sqlite3 *db){ sqlite3DbFree(db, pTmp); } db->nSavepoint = 0; + db->nStatement = 0; db->isTransactionSavepoint = 0; } @@ -629,6 +631,12 @@ int sqlite3_close(sqlite3 *db){ } } sqlite3ResetInternalSchema(db, 0); + + /* Tell the code in notify.c that the connection no longer holds any + ** locks and does not require any further unlock-notify callbacks. + */ + sqlite3ConnectionClosed(db); + assert( db->nDb<=2 ); assert( db->aDb==db->aDbStatic ); for(j=0; jaFunc.a); j++){ @@ -1238,15 +1246,15 @@ const char *sqlite3_errmsg(sqlite3 *db){ if( !sqlite3SafetyCheckSickOrOk(db) ){ return sqlite3ErrStr(SQLITE_MISUSE); } - if( db->mallocFailed ){ - return sqlite3ErrStr(SQLITE_NOMEM); - } sqlite3_mutex_enter(db->mutex); - assert( !db->mallocFailed ); - z = (char*)sqlite3_value_text(db->pErr); - assert( !db->mallocFailed ); - if( z==0 ){ - z = sqlite3ErrStr(db->errCode); + if( db->mallocFailed ){ + z = sqlite3ErrStr(SQLITE_NOMEM); + }else{ + z = (char*)sqlite3_value_text(db->pErr); + assert( !db->mallocFailed ); + if( z==0 ){ + z = sqlite3ErrStr(db->errCode); + } } sqlite3_mutex_leave(db->mutex); return z; @@ -1258,46 +1266,42 @@ const char *sqlite3_errmsg(sqlite3 *db){ ** error. */ const void *sqlite3_errmsg16(sqlite3 *db){ - /* Because all the characters in the string are in the unicode - ** range 0x00-0xFF, if we pad the big-endian string with a - ** zero byte, we can obtain the little-endian string with - ** &big_endian[1]. - */ - static const char outOfMemBe[] = { - 0, 'o', 0, 'u', 0, 't', 0, ' ', - 0, 'o', 0, 'f', 0, ' ', - 0, 'm', 0, 'e', 0, 'm', 0, 'o', 0, 'r', 0, 'y', 0, 0, 0 + static const u16 outOfMem[] = { + 'o', 'u', 't', ' ', 'o', 'f', ' ', 'm', 'e', 'm', 'o', 'r', 'y', 0 }; - static const char misuseBe [] = { - 0, 'l', 0, 'i', 0, 'b', 0, 'r', 0, 'a', 0, 'r', 0, 'y', 0, ' ', - 0, 'r', 0, 'o', 0, 'u', 0, 't', 0, 'i', 0, 'n', 0, 'e', 0, ' ', - 0, 'c', 0, 'a', 0, 'l', 0, 'l', 0, 'e', 0, 'd', 0, ' ', - 0, 'o', 0, 'u', 0, 't', 0, ' ', - 0, 'o', 0, 'f', 0, ' ', - 0, 's', 0, 'e', 0, 'q', 0, 'u', 0, 'e', 0, 'n', 0, 'c', 0, 'e', 0, 0, 0 + static const u16 misuse[] = { + 'l', 'i', 'b', 'r', 'a', 'r', 'y', ' ', + 'r', 'o', 'u', 't', 'i', 'n', 'e', ' ', + 'c', 'a', 'l', 'l', 'e', 'd', ' ', + 'o', 'u', 't', ' ', + 'o', 'f', ' ', + 's', 'e', 'q', 'u', 'e', 'n', 'c', 'e', 0 }; const void *z; if( !db ){ - return (void *)(&outOfMemBe[SQLITE_UTF16NATIVE==SQLITE_UTF16LE?1:0]); + return (void *)outOfMem; } if( !sqlite3SafetyCheckSickOrOk(db) ){ - return (void *)(&misuseBe[SQLITE_UTF16NATIVE==SQLITE_UTF16LE?1:0]); + return (void *)misuse; } sqlite3_mutex_enter(db->mutex); - assert( !db->mallocFailed ); - z = sqlite3_value_text16(db->pErr); - if( z==0 ){ - sqlite3ValueSetStr(db->pErr, -1, sqlite3ErrStr(db->errCode), - SQLITE_UTF8, SQLITE_STATIC); + if( db->mallocFailed ){ + z = (void *)outOfMem; + }else{ z = sqlite3_value_text16(db->pErr); + if( z==0 ){ + sqlite3ValueSetStr(db->pErr, -1, sqlite3ErrStr(db->errCode), + SQLITE_UTF8, SQLITE_STATIC); + z = sqlite3_value_text16(db->pErr); + } + /* A malloc() may have failed within the call to sqlite3_value_text16() + ** above. If this is the case, then the db->mallocFailed flag needs to + ** be cleared before returning. Do this directly, instead of via + ** sqlite3ApiExit(), to avoid setting the database handle error message. + */ + db->mallocFailed = 0; } - /* A malloc() may have failed within the call to sqlite3_value_text16() - ** above. If this is the case, then the db->mallocFailed flag needs to - ** be cleared before returning. Do this directly, instead of via - ** sqlite3ApiExit(), to avoid setting the database handle error message. - */ - db->mallocFailed = 0; sqlite3_mutex_leave(db->mutex); return z; } @@ -1940,7 +1944,6 @@ int sqlite3_table_column_metadata( (void)sqlite3SafetyOn(db); sqlite3BtreeEnterAll(db); rc = sqlite3Init(db, &zErrMsg); - sqlite3BtreeLeaveAll(db); if( SQLITE_OK!=rc ){ goto error_out; } @@ -1996,6 +1999,7 @@ int sqlite3_table_column_metadata( } error_out: + sqlite3BtreeLeaveAll(db); (void)sqlite3SafetyOff(db); /* Whether the function call succeeded or failed, set the output parameters diff --git a/src/malloc.c b/src/malloc.c index 468e057..92e5639 100644 --- a/src/malloc.c +++ b/src/malloc.c @@ -12,7 +12,7 @@ ** ** Memory allocation functions used throughout sqlite. ** -** $Id: malloc.c,v 1.56 2009/02/17 18:37:29 drh Exp $ +** $Id: malloc.c,v 1.61 2009/03/24 15:08:10 drh Exp $ */ #include "sqliteInt.h" #include @@ -121,7 +121,7 @@ int sqlite3MallocInit(void){ if( sqlite3GlobalConfig.pScratch && sqlite3GlobalConfig.szScratch>=100 && sqlite3GlobalConfig.nScratch>=0 ){ int i; - sqlite3GlobalConfig.szScratch = (sqlite3GlobalConfig.szScratch - 4) & ~7; + sqlite3GlobalConfig.szScratch = ROUNDDOWN8(sqlite3GlobalConfig.szScratch-4); mem0.aScratchFree = (u32*)&((char*)sqlite3GlobalConfig.pScratch) [sqlite3GlobalConfig.szScratch*sqlite3GlobalConfig.nScratch]; for(i=0; i=1 ){ int i; int overhead; - int sz = sqlite3GlobalConfig.szPage & ~7; + int sz = ROUNDDOWN8(sqlite3GlobalConfig.szPage); int n = sqlite3GlobalConfig.nPage; overhead = (4*n + sz - 1)/sz; sqlite3GlobalConfig.nPage -= overhead; @@ -407,95 +407,6 @@ void sqlite3ScratchFree(void *p){ } } -/* -** Allocate memory to be used by the page cache. Make use of the -** memory buffer provided by SQLITE_CONFIG_PAGECACHE if there is one -** and that memory is of the right size and is not completely -** consumed. Otherwise, failover to sqlite3Malloc(). -*/ -#if 0 -void *sqlite3PageMalloc(int n){ - void *p; - assert( n>0 ); - assert( (n & (n-1))==0 ); - assert( n>=512 && n<=32768 ); - - if( sqlite3GlobalConfig.szPage=(void*)mem0.aPageFree ){ - /* In this case, the page allocation was obtained from a regular - ** call to sqlite3_mem_methods.xMalloc() (a page-cache-memory - ** "overflow"). Free the block with sqlite3_mem_methods.xFree(). - */ - if( sqlite3GlobalConfig.bMemstat ){ - int iSize = sqlite3MallocSize(p); - sqlite3_mutex_enter(mem0.mutex); - sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize); - sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -iSize); - sqlite3GlobalConfig.m.xFree(p); - sqlite3_mutex_leave(mem0.mutex); - }else{ - sqlite3GlobalConfig.m.xFree(p); - } - }else{ - /* The page allocation was allocated from the sqlite3GlobalConfig.pPage - ** buffer. In this case all that is add the index of the page in - ** the sqlite3GlobalConfig.pPage array to the set of free indexes stored - ** in the mem0.aPageFree[] array. - */ - int i; - i = (u8 *)p - (u8 *)sqlite3GlobalConfig.pPage; - i /= sqlite3GlobalConfig.szPage; - assert( i>=0 && imutex) ); if( p==0 ){ return 0; }else if( isLookaside(db, p) ){ @@ -544,6 +456,7 @@ void sqlite3_free(void *p){ ** connection. */ void sqlite3DbFree(sqlite3 *db, void *p){ + assert( db==0 || sqlite3_mutex_held(db->mutex) ); if( isLookaside(db, p) ){ LookasideSlot *pBuf = (LookasideSlot*)p; pBuf->pNext = db->lookaside.pFree; @@ -652,6 +565,7 @@ void *sqlite3DbMallocZero(sqlite3 *db, int n){ */ void *sqlite3DbMallocRaw(sqlite3 *db, int n){ void *p; + assert( db==0 || sqlite3_mutex_held(db->mutex) ); #ifndef SQLITE_OMIT_LOOKASIDE if( db ){ LookasideSlot *pBuf; @@ -686,6 +600,8 @@ void *sqlite3DbMallocRaw(sqlite3 *db, int n){ */ void *sqlite3DbRealloc(sqlite3 *db, void *p, int n){ void *pNew = 0; + assert( db!=0 ); + assert( sqlite3_mutex_held(db->mutex) ); if( db->mallocFailed==0 ){ if( p==0 ){ return sqlite3DbMallocRaw(db, n); @@ -780,10 +696,10 @@ void sqlite3SetString(char **pz, sqlite3 *db, const char *zFormat, ...){ ** sqlite3_realloc. ** ** The returned value is normally a copy of the second argument to this -** function. However, if a malloc() failure has occured since the previous +** function. However, if a malloc() failure has occurred since the previous ** invocation SQLITE_NOMEM is returned instead. ** -** If the first argument, db, is not NULL and a malloc() error has occured, +** If the first argument, db, is not NULL and a malloc() error has occurred, ** then the connection error-code (the value returned by sqlite3_errcode()) ** is set to SQLITE_NOMEM. */ diff --git a/src/mem1.c b/src/mem1.c index ca1e6ae..e3b8863 100644 --- a/src/mem1.c +++ b/src/mem1.c @@ -17,7 +17,7 @@ ** This file contains implementations of the low-level memory allocation ** routines specified in the sqlite3_mem_methods object. ** -** $Id: mem1.c,v 1.29 2008/12/10 21:19:57 drh Exp $ +** $Id: mem1.c,v 1.30 2009/03/23 04:33:33 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -39,7 +39,7 @@ static void *sqlite3MemMalloc(int nByte){ sqlite3_int64 *p; assert( nByte>0 ); - nByte = (nByte+7)&~7; + nByte = ROUND8(nByte); p = malloc( nByte+8 ); if( p ){ p[0] = nByte; @@ -76,7 +76,7 @@ static void sqlite3MemFree(void *pPrior){ static void *sqlite3MemRealloc(void *pPrior, int nByte){ sqlite3_int64 *p = (sqlite3_int64*)pPrior; assert( pPrior!=0 && nByte>0 ); - nByte = (nByte+7)&~7; + nByte = ROUND8(nByte); p = (sqlite3_int64*)pPrior; p--; p = realloc(p, nByte+8 ); @@ -103,7 +103,7 @@ static int sqlite3MemSize(void *pPrior){ ** Round up a request size to the next valid allocation size. */ static int sqlite3MemRoundup(int n){ - return (n+7) & ~7; + return ROUND8(n); } /* diff --git a/src/mem2.c b/src/mem2.c index 156237f..8c498a2 100644 --- a/src/mem2.c +++ b/src/mem2.c @@ -19,7 +19,7 @@ ** This file contains implementations of the low-level memory allocation ** routines specified in the sqlite3_mem_methods object. ** -** $Id: mem2.c,v 1.43 2009/02/05 03:00:06 shane Exp $ +** $Id: mem2.c,v 1.45 2009/03/23 04:33:33 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -128,7 +128,7 @@ static struct { ** Adjust memory usage statistics */ static void adjustStats(int iSize, int increment){ - int i = ((iSize+7)&~7)/8; + int i = ROUND8(iSize)/8; if( i>NCSIZE-1 ){ i = NCSIZE - 1; } @@ -159,7 +159,7 @@ static struct MemBlockHdr *sqlite3MemsysGetHeader(void *pAllocation){ p = (struct MemBlockHdr*)pAllocation; p--; assert( p->iForeGuard==(int)FOREGUARD ); - nReserve = (p->iSize+7)&~7; + nReserve = ROUND8(p->iSize); pInt = (int*)pAllocation; pU8 = (u8*)pAllocation; assert( pInt[nReserve/sizeof(int)]==(int)REARGUARD ); @@ -209,7 +209,7 @@ static void sqlite3MemShutdown(void *NotUsed){ ** Round up a request size to the next valid allocation size. */ static int sqlite3MemRoundup(int n){ - return (n+7) & ~7; + return ROUND8(n); } /* @@ -225,7 +225,7 @@ static void *sqlite3MemMalloc(int nByte){ int nReserve; sqlite3_mutex_enter(mem.mutex); assert( mem.disallow==0 ); - nReserve = (nByte+7)&~7; + nReserve = ROUND8(nByte); totalSize = nReserve + sizeof(*pHdr) + sizeof(int) + mem.nBacktrace*sizeof(void*) + mem.nTitle; p = malloc(totalSize); @@ -248,6 +248,7 @@ static void *sqlite3MemMalloc(int nByte){ void *aAddr[40]; pHdr->nBacktrace = backtrace(aAddr, mem.nBacktrace+1)-1; memcpy(pBt, &aAddr[1], pHdr->nBacktrace*sizeof(void*)); + assert(pBt[0]); if( mem.xBacktrace ){ mem.xBacktrace(nByte, pHdr->nBacktrace-1, &aAddr[1]); } @@ -371,7 +372,7 @@ void sqlite3MemdebugSettitle(const char *zTitle){ if( n>=sizeof(mem.zTitle) ) n = sizeof(mem.zTitle)-1; memcpy(mem.zTitle, zTitle, n); mem.zTitle[n] = 0; - mem.nTitle = (n+7)&~7; + mem.nTitle = ROUND8(n); sqlite3_mutex_leave(mem.mutex); } diff --git a/src/memjournal.c b/src/memjournal.c index 945b5da..15665a0 100644 --- a/src/memjournal.c +++ b/src/memjournal.c @@ -14,7 +14,7 @@ ** The in-memory rollback journal is used to journal transactions for ** ":memory:" databases and when the journal_mode=MEMORY pragma is used. ** -** @(#) $Id: memjournal.c,v 1.8 2008/12/20 02:14:40 drh Exp $ +** @(#) $Id: memjournal.c,v 1.11 2009/04/05 12:22:09 drh Exp $ */ #include "sqliteInt.h" @@ -25,8 +25,13 @@ typedef struct FileChunk FileChunk; /* Space to hold the rollback journal is allocated in increments of ** this many bytes. +** +** The size chosen is a little less than a power of two. That way, +** the FileChunk object will have a size that almost exactly fills +** a power-of-two allocation. This mimimizes wasted space in power-of-two +** memory allocators. */ -#define JOURNAL_CHUNKSIZE 1024 +#define JOURNAL_CHUNKSIZE ((int)(1024-sizeof(FileChunk*))) /* Macro to find the minimum of two numeric values. */ @@ -63,7 +68,8 @@ struct MemJournal { }; /* -** Read data from the file. +** Read data from the in-memory journal file. This is the implementation +** of the sqlite3_vfs.xRead method. */ static int memjrnlRead( sqlite3_file *pJfd, /* The journal file from which to read */ @@ -77,12 +83,13 @@ static int memjrnlRead( int iChunkOffset; FileChunk *pChunk; + /* SQLite never tries to read past the end of a rollback journal file */ assert( iOfst+iAmt<=p->endpoint.iOffset ); if( p->readpoint.iOffset!=iOfst || iOfst==0 ){ sqlite3_int64 iOff = 0; for(pChunk=p->pFirst; - pChunk && (iOff+JOURNAL_CHUNKSIZE)<=iOfst; + ALWAYS(pChunk) && (iOff+JOURNAL_CHUNKSIZE)<=iOfst; pChunk=pChunk->pNext ){ iOff += JOURNAL_CHUNKSIZE; @@ -185,11 +192,17 @@ static int memjrnlClose(sqlite3_file *pJfd){ /* ** Sync the file. +** +** Syncing an in-memory journal is a no-op. And, in fact, this routine +** is never called in a working implementation. This implementation +** exists purely as a contingency, in case some malfunction in some other +** part of SQLite causes Sync to be called by mistake. */ -static int memjrnlSync(sqlite3_file *NotUsed, int NotUsed2){ - UNUSED_PARAMETER2(NotUsed, NotUsed2); - return SQLITE_OK; -} +static int memjrnlSync(sqlite3_file *NotUsed, int NotUsed2){ /*NO_TEST*/ + UNUSED_PARAMETER2(NotUsed, NotUsed2); /*NO_TEST*/ + assert( 0 ); /*NO_TEST*/ + return SQLITE_OK; /*NO_TEST*/ +} /*NO_TEST*/ /* ** Query the size of the file in bytes. @@ -224,6 +237,7 @@ static struct sqlite3_io_methods MemJournalMethods = { */ void sqlite3MemJournalOpen(sqlite3_file *pJfd){ MemJournal *p = (MemJournal *)pJfd; + assert( EIGHT_BYTE_ALIGNMENT(p) ); memset(p, 0, sqlite3MemJournalSize()); p->pMethod = &MemJournalMethods; } diff --git a/src/notify.c b/src/notify.c new file mode 100644 index 0000000..afa7626 --- /dev/null +++ b/src/notify.c @@ -0,0 +1,333 @@ +/* +** 2009 March 3 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This file contains the implementation of the sqlite3_unlock_notify() +** API method and its associated functionality. +** +** $Id: notify.c,v 1.4 2009/04/07 22:06:57 drh Exp $ +*/ +#include "sqliteInt.h" +#include "btreeInt.h" + +/* Omit this entire file if SQLITE_ENABLE_UNLOCK_NOTIFY is not defined. */ +#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY + +/* +** Public interfaces: +** +** sqlite3ConnectionBlocked() +** sqlite3ConnectionUnlocked() +** sqlite3ConnectionClosed() +** sqlite3_unlock_notify() +*/ + +#define assertMutexHeld() \ + assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)) ) + +/* +** Head of a linked list of all sqlite3 objects created by this process +** for which either sqlite3.pBlockingConnection or sqlite3.pUnlockConnection +** is not NULL. This variable may only accessed while the STATIC_MASTER +** mutex is held. +*/ +static sqlite3 *SQLITE_WSD sqlite3BlockedList = 0; + +#ifndef NDEBUG +/* +** This function is a complex assert() that verifies the following +** properties of the blocked connections list: +** +** 1) Each entry in the list has a non-NULL value for either +** pUnlockConnection or pBlockingConnection, or both. +** +** 2) All entries in the list that share a common value for +** xUnlockNotify are grouped together. +** +** 3) If the argument db is not NULL, then none of the entries in the +** blocked connections list have pUnlockConnection or pBlockingConnection +** set to db. This is used when closing connection db. +*/ +static void checkListProperties(sqlite3 *db){ + sqlite3 *p; + for(p=sqlite3BlockedList; p; p=p->pNextBlocked){ + int seen = 0; + sqlite3 *p2; + + /* Verify property (1) */ + assert( p->pUnlockConnection || p->pBlockingConnection ); + + /* Verify property (2) */ + for(p2=sqlite3BlockedList; p2!=p; p2=p2->pNextBlocked){ + if( p2->xUnlockNotify==p->xUnlockNotify ) seen = 1; + assert( p2->xUnlockNotify==p->xUnlockNotify || !seen ); + assert( db==0 || p->pUnlockConnection!=db ); + assert( db==0 || p->pBlockingConnection!=db ); + } + } +} +#else +# define checkListProperties(x) +#endif + +/* +** Remove connection db from the blocked connections list. If connection +** db is not currently a part of the list, this function is a no-op. +*/ +static void removeFromBlockedList(sqlite3 *db){ + sqlite3 **pp; + assertMutexHeld(); + for(pp=&sqlite3BlockedList; *pp; pp = &(*pp)->pNextBlocked){ + if( *pp==db ){ + *pp = (*pp)->pNextBlocked; + break; + } + } +} + +/* +** Add connection db to the blocked connections list. It is assumed +** that it is not already a part of the list. +*/ +static void addToBlockedList(sqlite3 *db){ + sqlite3 **pp; + assertMutexHeld(); + for( + pp=&sqlite3BlockedList; + *pp && (*pp)->xUnlockNotify!=db->xUnlockNotify; + pp=&(*pp)->pNextBlocked + ); + db->pNextBlocked = *pp; + *pp = db; +} + +/* +** Obtain the STATIC_MASTER mutex. +*/ +static void enterMutex(void){ + sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); + checkListProperties(0); +} + +/* +** Release the STATIC_MASTER mutex. +*/ +static void leaveMutex(void){ + assertMutexHeld(); + checkListProperties(0); + sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); +} + +/* +** Register an unlock-notify callback. +** +** This is called after connection "db" has attempted some operation +** but has received an SQLITE_LOCKED error because another connection +** (call it pOther) in the same process was busy using the same shared +** cache. pOther is found by looking at db->pBlockingConnection. +** +** If there is no blocking connection, the callback is invoked immediately, +** before this routine returns. +** +** If pOther is already blocked on db, then report SQLITE_LOCKED, to indicate +** a deadlock. +** +** Otherwise, make arrangements to invoke xNotify when pOther drops +** its locks. +** +** Each call to this routine overrides any prior callbacks registered +** on the same "db". If xNotify==0 then any prior callbacks are immediately +** cancelled. +*/ +int sqlite3_unlock_notify( + sqlite3 *db, + void (*xNotify)(void **, int), + void *pArg +){ + int rc = SQLITE_OK; + + sqlite3_mutex_enter(db->mutex); + enterMutex(); + + if( xNotify==0 ){ + removeFromBlockedList(db); + db->pUnlockConnection = 0; + db->xUnlockNotify = 0; + db->pUnlockArg = 0; + }else if( 0==db->pBlockingConnection ){ + /* The blocking transaction has been concluded. Or there never was a + ** blocking transaction. In either case, invoke the notify callback + ** immediately. + */ + xNotify(&pArg, 1); + }else{ + sqlite3 *p; + + for(p=db->pBlockingConnection; p && p!=db; p=p->pUnlockConnection){} + if( p ){ + rc = SQLITE_LOCKED; /* Deadlock detected. */ + }else{ + db->pUnlockConnection = db->pBlockingConnection; + db->xUnlockNotify = xNotify; + db->pUnlockArg = pArg; + removeFromBlockedList(db); + addToBlockedList(db); + } + } + + leaveMutex(); + assert( !db->mallocFailed ); + sqlite3Error(db, rc, (rc?"database is deadlocked":0)); + sqlite3_mutex_leave(db->mutex); + return rc; +} + +/* +** This function is called while stepping or preparing a statement +** associated with connection db. The operation will return SQLITE_LOCKED +** to the user because it requires a lock that will not be available +** until connection pBlocker concludes its current transaction. +*/ +void sqlite3ConnectionBlocked(sqlite3 *db, sqlite3 *pBlocker){ + enterMutex(); + if( db->pBlockingConnection==0 && db->pUnlockConnection==0 ){ + addToBlockedList(db); + } + db->pBlockingConnection = pBlocker; + leaveMutex(); +} + +/* +** This function is called when +** the transaction opened by database db has just finished. Locks held +** by database connection db have been released. +** +** This function loops through each entry in the blocked connections +** list and does the following: +** +** 1) If the sqlite3.pBlockingConnection member of a list entry is +** set to db, then set pBlockingConnection=0. +** +** 2) If the sqlite3.pUnlockConnection member of a list entry is +** set to db, then invoke the configured unlock-notify callback and +** set pUnlockConnection=0. +** +** 3) If the two steps above mean that pBlockingConnection==0 and +** pUnlockConnection==0, remove the entry from the blocked connections +** list. +*/ +void sqlite3ConnectionUnlocked(sqlite3 *db){ + void (*xUnlockNotify)(void **, int) = 0; /* Unlock-notify cb to invoke */ + int nArg = 0; /* Number of entries in aArg[] */ + sqlite3 **pp; /* Iterator variable */ + void **aArg; /* Arguments to the unlock callback */ + void **aDyn = 0; /* Dynamically allocated space for aArg[] */ + void *aStatic[16]; /* Starter space for aArg[]. No malloc required */ + + aArg = aStatic; + enterMutex(); /* Enter STATIC_MASTER mutex */ + + /* This loop runs once for each entry in the blocked-connections list. */ + for(pp=&sqlite3BlockedList; *pp; /* no-op */ ){ + sqlite3 *p = *pp; + + /* Step 1. */ + if( p->pBlockingConnection==db ){ + p->pBlockingConnection = 0; + } + + /* Step 2. */ + if( p->pUnlockConnection==db ){ + assert( p->xUnlockNotify ); + if( p->xUnlockNotify!=xUnlockNotify && nArg!=0 ){ + xUnlockNotify(aArg, nArg); + nArg = 0; + } + + sqlite3BeginBenignMalloc(); + assert( aArg==aDyn || (aDyn==0 && aArg==aStatic) ); + assert( nArg<=(int)ArraySize(aStatic) || aArg==aDyn ); + if( (!aDyn && nArg==(int)ArraySize(aStatic)) + || (aDyn && nArg==(int)(sqlite3DbMallocSize(db, aDyn)/sizeof(void*))) + ){ + /* The aArg[] array needs to grow. */ + void **pNew = (void **)sqlite3Malloc(nArg*sizeof(void *)*2); + if( pNew ){ + memcpy(pNew, aArg, nArg*sizeof(void *)); + sqlite3_free(aDyn); + aDyn = aArg = pNew; + }else{ + /* This occurs when the array of context pointers that need to + ** be passed to the unlock-notify callback is larger than the + ** aStatic[] array allocated on the stack and the attempt to + ** allocate a larger array from the heap has failed. + ** + ** This is a difficult situation to handle. Returning an error + ** code to the caller is insufficient, as even if an error code + ** is returned the transaction on connection db will still be + ** closed and the unlock-notify callbacks on blocked connections + ** will go unissued. This might cause the application to wait + ** indefinitely for an unlock-notify callback that will never + ** arrive. + ** + ** Instead, invoke the unlock-notify callback with the context + ** array already accumulated. We can then clear the array and + ** begin accumulating any further context pointers without + ** requiring any dynamic allocation. This is sub-optimal because + ** it means that instead of one callback with a large array of + ** context pointers the application will receive two or more + ** callbacks with smaller arrays of context pointers, which will + ** reduce the applications ability to prioritize multiple + ** connections. But it is the best that can be done under the + ** circumstances. + */ + xUnlockNotify(aArg, nArg); + nArg = 0; + } + } + sqlite3EndBenignMalloc(); + + aArg[nArg++] = p->pUnlockArg; + xUnlockNotify = p->xUnlockNotify; + p->pUnlockConnection = 0; + p->xUnlockNotify = 0; + p->pUnlockArg = 0; + } + + /* Step 3. */ + if( p->pBlockingConnection==0 && p->pUnlockConnection==0 ){ + /* Remove connection p from the blocked connections list. */ + *pp = p->pNextBlocked; + p->pNextBlocked = 0; + }else{ + pp = &p->pNextBlocked; + } + } + + if( nArg!=0 ){ + xUnlockNotify(aArg, nArg); + } + sqlite3_free(aDyn); + leaveMutex(); /* Leave STATIC_MASTER mutex */ +} + +/* +** This is called when the database connection passed as an argument is +** being closed. The connection is removed from the blocked list. +*/ +void sqlite3ConnectionClosed(sqlite3 *db){ + sqlite3ConnectionUnlocked(db); + enterMutex(); + removeFromBlockedList(db); + checkListProperties(db); + leaveMutex(); +} +#endif diff --git a/src/os.c b/src/os.c index c353388..d879b8a 100644 --- a/src/os.c +++ b/src/os.c @@ -13,7 +13,7 @@ ** This file contains OS interface code that is common to all ** architectures. ** -** $Id: os.c,v 1.125 2008/12/08 18:19:18 drh Exp $ +** $Id: os.c,v 1.126 2009/03/25 14:24:42 drh Exp $ */ #define _SQLITE_OS_C_ 1 #include "sqliteInt.h" @@ -112,8 +112,11 @@ int sqlite3OsOpen( int flags, int *pFlagsOut ){ + int rc; DO_OS_MALLOC_TEST; - return pVfs->xOpen(pVfs, zPath, pFile, flags, pFlagsOut); + rc = pVfs->xOpen(pVfs, zPath, pFile, flags, pFlagsOut); + assert( rc==SQLITE_OK || pFile->pMethods==0 ); + return rc; } int sqlite3OsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ return pVfs->xDelete(pVfs, zPath, dirSync); diff --git a/src/os_common.h b/src/os_common.h index 5fab7a4..03d1d74 100644 --- a/src/os_common.h +++ b/src/os_common.h @@ -17,7 +17,7 @@ ** This file should be #included by the os_*.c files only. It is not a ** general purpose header file. ** -** $Id: os_common.h,v 1.37 2008/05/29 20:22:37 shane Exp $ +** $Id: os_common.h,v 1.38 2009/02/24 18:40:50 danielk1977 Exp $ */ #ifndef _OS_COMMON_H_ #define _OS_COMMON_H_ @@ -31,15 +31,6 @@ # error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." #endif - -/* - * When testing, this global variable stores the location of the - * pending-byte in the database file. - */ -#ifdef SQLITE_TEST -unsigned int sqlite3_pending_byte = 0x40000000; -#endif - #ifdef SQLITE_DEBUG int sqlite3OSTrace = 0; #define OSTRACE1(X) if( sqlite3OSTrace ) sqlite3DebugPrintf(X) diff --git a/src/os_unix.c b/src/os_unix.c index 5696b14..8776aca 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -43,7 +43,7 @@ ** * Definitions of sqlite3_vfs objects for all locking methods ** plus implementations of sqlite3_os_init() and sqlite3_os_end(). ** -** $Id: os_unix.c,v 1.241 2009/02/09 17:34:07 drh Exp $ +** $Id: os_unix.c,v 1.250 2009/04/07 05:35:04 chw Exp $ */ #include "sqliteInt.h" #if SQLITE_OS_UNIX /* This file is used on unix only */ @@ -1454,11 +1454,12 @@ static int unixUnlock(sqlite3_file *id, int locktype){ if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } - goto end_unlock; + goto end_unlock; } } if( locktype==NO_LOCK ){ struct unixOpenCnt *pOpen; + int rc2 = SQLITE_OK; /* Decrement the shared lock counter. Release the lock using an ** OS call only when all threads in this same process have released @@ -1480,8 +1481,8 @@ static int unixUnlock(sqlite3_file *id, int locktype){ if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } - pLock->cnt = 1; - goto end_unlock; + pLock->locktype = NO_LOCK; + pFile->locktype = NO_LOCK; } } @@ -1489,30 +1490,31 @@ static int unixUnlock(sqlite3_file *id, int locktype){ ** count reaches zero, close any other file descriptors whose close ** was deferred because of outstanding locks. */ - if( rc==SQLITE_OK ){ - pOpen = pFile->pOpen; - pOpen->nLock--; - assert( pOpen->nLock>=0 ); - if( pOpen->nLock==0 && pOpen->nPending>0 ){ - int i; - for(i=0; inPending; i++){ - /* close pending fds, but if closing fails don't free the array - ** assign -1 to the successfully closed descriptors and record the - ** error. The next attempt to unlock will try again. */ - if( pOpen->aPending[i] < 0 ) continue; - if( close(pOpen->aPending[i]) ){ - pFile->lastErrno = errno; - rc = SQLITE_IOERR_CLOSE; - }else{ - pOpen->aPending[i] = -1; - } - } - if( rc==SQLITE_OK ){ - sqlite3_free(pOpen->aPending); - pOpen->nPending = 0; - pOpen->aPending = 0; + pOpen = pFile->pOpen; + pOpen->nLock--; + assert( pOpen->nLock>=0 ); + if( pOpen->nLock==0 && pOpen->nPending>0 ){ + int i; + for(i=0; inPending; i++){ + /* close pending fds, but if closing fails don't free the array + ** assign -1 to the successfully closed descriptors and record the + ** error. The next attempt to unlock will try again. */ + if( pOpen->aPending[i] < 0 ) continue; + if( close(pOpen->aPending[i]) ){ + pFile->lastErrno = errno; + rc2 = SQLITE_IOERR_CLOSE; + }else{ + pOpen->aPending[i] = -1; } } + if( rc2==SQLITE_OK ){ + sqlite3_free(pOpen->aPending); + pOpen->nPending = 0; + pOpen->aPending = 0; + } + } + if( rc==SQLITE_OK ){ + rc = rc2; } } @@ -2211,8 +2213,8 @@ static int semClose(sqlite3_file *id) { unixEnterMutex(); releaseLockInfo(pFile->pLock); releaseOpenCnt(pFile->pOpen); - closeUnixFile(id); unixLeaveMutex(); + closeUnixFile(id); } return SQLITE_OK; } @@ -2824,10 +2826,12 @@ int sqlite3_fullsync_count = 0; #endif /* -** Use the fdatasync() API only if the HAVE_FDATASYNC macro is defined. -** Otherwise use fsync() in its place. +** We do not trust systems to provide a working fdatasync(). Some do. +** Others do no. To be safe, we will stick with the (slower) fsync(). +** If you know that your system does support fdatasync() correctly, +** then simply compile with -Dfdatasync=fdatasync */ -#ifndef HAVE_FDATASYNC +#if !defined(fdatasync) && !defined(__linux__) # define fdatasync fsync #endif @@ -2853,6 +2857,19 @@ int sqlite3_fullsync_count = 0; ** You are strongly advised *not* to deploy with SQLITE_NO_SYNC ** enabled, however, since with SQLITE_NO_SYNC enabled, an OS crash ** or power failure will likely corrupt the database file. +** +** SQLite sets the dataOnly flag if the size of the file is unchanged. +** The idea behind dataOnly is that it should only write the file content +** to disk, not the inode. We only set dataOnly if the file size is +** unchanged since the file size is part of the inode. However, +** Ted Ts'o tells us that fdatasync() will also write the inode if the +** file size has changed. The only real difference between fdatasync() +** and fsync(), Ted tells us, is that fdatasync() will not flush the +** inode if the mtime or owner or other inode attributes have changed. +** We only care about the file size, not the other file attributes, so +** as far as SQLite is concerned, an fdatasync() is always adequate. +** So, we always use fdatasync() if it is available, regardless of +** the value of the dataOnly flag. */ static int full_fsync(int fd, int fullSync, int dataOnly){ int rc; @@ -2869,6 +2886,7 @@ static int full_fsync(int fd, int fullSync, int dataOnly){ UNUSED_PARAMETER(dataOnly); #else UNUSED_PARAMETER(fullSync); + UNUSED_PARAMETER(dataOnly); #endif /* Record the number of times that we do a normal fsync() and @@ -2902,16 +2920,12 @@ static int full_fsync(int fd, int fullSync, int dataOnly){ if( rc ) rc = fsync(fd); #else - if( dataOnly ){ - rc = fdatasync(fd); + rc = fdatasync(fd); #if OS_VXWORKS - if( rc==-1 && errno==ENOTSUP ){ - rc = fsync(fd); - } -#endif - }else{ + if( rc==-1 && errno==ENOTSUP ){ rc = fsync(fd); } +#endif /* OS_VXWORKS */ #endif /* ifdef SQLITE_NO_SYNC elif HAVE_FULLFSYNC */ if( OS_VXWORKS && rc!= -1 ){ @@ -3193,7 +3207,7 @@ IOMETHODS( dotlockCheckReservedLock /* xCheckReservedLock method */ ) -#if SQLITE_ENABLE_LOCKING_STYLE +#if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS IOMETHODS( flockIoFinder, /* Finder function name */ flockIoMethods, /* sqlite3_io_methods object name */ @@ -3317,6 +3331,44 @@ static const sqlite3_io_methods *(*const autolockIoFinder)(const char*,int) #endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ +#if OS_VXWORKS && SQLITE_ENABLE_LOCKING_STYLE +/* +** This "finder" function attempts to determine the best locking strategy +** for the database file "filePath". It then returns the sqlite3_io_methods +** object that implements that strategy. +** +** This is for VXWorks only. +*/ +static const sqlite3_io_methods *autolockIoFinderImpl( + const char *filePath, /* name of the database file */ + int fd /* file descriptor open on the database file */ +){ + struct flock lockInfo; + + if( !filePath ){ + /* If filePath==NULL that means we are dealing with a transient file + ** that does not need to be locked. */ + return &nolockIoMethods; + } + + /* Test if fcntl() is supported and use POSIX style locks. + ** Otherwise fall back to the named semaphore method. + */ + lockInfo.l_len = 1; + lockInfo.l_start = 0; + lockInfo.l_whence = SEEK_SET; + lockInfo.l_type = F_RDLCK; + if( fcntl(fd, F_GETLK, &lockInfo)!=-1 ) { + return &posixIoMethods; + }else{ + return &semIoMethods; + } +} +static const sqlite3_io_methods *(*const autolockIoFinder)(const char*,int) + = autolockIoFinderImpl; + +#endif /* OS_VXWORKS && SQLITE_ENABLE_LOCKING_STYLE */ + /* ** An abstract type for a pointer to a IO method finder function: */ @@ -3599,7 +3651,7 @@ static int unixOpen( int flags, /* Input flags to control the opening */ int *pOutFlags /* Output flags returned to SQLite core */ ){ - int fd = 0; /* File descriptor returned by open() */ + int fd = -1; /* File descriptor returned by open() */ int dirfd = -1; /* Directory file descriptor */ int openFlags = 0; /* Flags to pass to open() */ int eType = flags&0xFFFFFF00; /* Type of file to open */ @@ -3702,7 +3754,7 @@ static int unixOpen( } #endif - assert(fd!=0); + assert( fd>=0 ); if( isOpenDirectory ){ rc = openDirectory(zPath, &dirfd); if( rc!=SQLITE_OK ){ @@ -3984,16 +4036,18 @@ static int unixSleep(sqlite3_vfs *NotUsed, int microseconds){ sp.tv_sec = microseconds / 1000000; sp.tv_nsec = (microseconds % 1000000) * 1000; nanosleep(&sp, NULL); + UNUSED_PARAMETER(NotUsed); return microseconds; #elif defined(HAVE_USLEEP) && HAVE_USLEEP usleep(microseconds); + UNUSED_PARAMETER(NotUsed); return microseconds; #else int seconds = (microseconds+999999)/1000000; sleep(seconds); + UNUSED_PARAMETER(NotUsed); return seconds*1000000; #endif - UNUSED_PARAMETER(NotUsed); } /* @@ -5045,7 +5099,7 @@ int sqlite3_os_init(void){ ** array cannot be const. */ static sqlite3_vfs aVfs[] = { -#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) +#if SQLITE_ENABLE_LOCKING_STYLE && (OS_VXWORKS || defined(__APPLE__)) UNIXVFS("unix", autolockIoFinder ), #else UNIXVFS("unix", posixIoFinder ), @@ -5057,8 +5111,10 @@ int sqlite3_os_init(void){ #endif #if SQLITE_ENABLE_LOCKING_STYLE UNIXVFS("unix-posix", posixIoFinder ), +#if !OS_VXWORKS UNIXVFS("unix-flock", flockIoFinder ), #endif +#endif #if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) UNIXVFS("unix-afp", afpIoFinder ), UNIXVFS("unix-proxy", proxyIoFinder ), diff --git a/src/os_win.c b/src/os_win.c index 2f98ee7..8f705cf 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -12,7 +12,7 @@ ** ** This file contains code that is specific to windows. ** -** $Id: os_win.c,v 1.148 2009/02/05 03:16:21 shane Exp $ +** $Id: os_win.c,v 1.154 2009/04/09 14:27:07 chw Exp $ */ #include "sqliteInt.h" #if SQLITE_OS_WIN /* This file is used for windows only */ @@ -75,6 +75,7 @@ */ #if SQLITE_OS_WINCE # define AreFileApisANSI() 1 +# define GetDiskFreeSpaceW() 0 #endif /* @@ -101,6 +102,7 @@ struct winFile { unsigned char locktype; /* Type of lock currently held on this file */ short sharedLockByte; /* Randomly chosen byte used as a shared lock */ DWORD lastErrno; /* The Windows errno from the last I/O error */ + DWORD sectorSize; /* Sector size of the device file is on */ #if SQLITE_OS_WINCE WCHAR *zDeleteOnClose; /* Name of file to delete when closing */ HANDLE hMutex; /* Mutex used to control access to shared lock */ @@ -110,6 +112,13 @@ struct winFile { #endif }; +/* +** Forward prototypes. +*/ +static int getSectorSize( + sqlite3_vfs *pVfs, + const char *zRelative /* UTF-8 file name */ +); /* ** The following variable is (normally) set once and never changes @@ -135,7 +144,7 @@ static int sqlite3_os_type = 0; ** ** Here is an interesting observation: Win95, Win98, and WinME lack ** the LockFileEx() API. But we can still statically link against that -** API as long as we don't call it win running Win95/98/ME. A call to +** API as long as we don't call it when running Win95/98/ME. A call to ** this routine is used to determine if the host is Win95/98/ME or ** WinNT/2K/XP so that we will know whether or not we can safely call ** the LockFileEx() API. @@ -610,6 +619,8 @@ static BOOL winceLockFileEx( static int winClose(sqlite3_file *id){ int rc, cnt = 0; winFile *pFile = (winFile*)id; + + assert( id!=0 ); OSTRACE2("CLOSE %d\n", pFile->h); do{ rc = CloseHandle(pFile->h); @@ -654,9 +665,10 @@ static int winRead( LONG upperBits = (LONG)((offset>>32) & 0x7fffffff); LONG lowerBits = (LONG)(offset & 0xffffffff); DWORD rc; - DWORD got; winFile *pFile = (winFile*)id; DWORD error; + DWORD got; + assert( id!=0 ); SimulateIOError(return SQLITE_IOERR_READ); OSTRACE3("READ %d lock=%d\n", pFile->h, pFile->locktype); @@ -691,9 +703,10 @@ static int winWrite( LONG upperBits = (LONG)((offset>>32) & 0x7fffffff); LONG lowerBits = (LONG)(offset & 0xffffffff); DWORD rc; - DWORD wrote = 0; winFile *pFile = (winFile*)id; DWORD error; + DWORD wrote = 0; + assert( id!=0 ); SimulateIOError(return SQLITE_IOERR_WRITE); SimulateDiskfullError(return SQLITE_FULL); @@ -723,26 +736,26 @@ static int winWrite( ** Truncate an open file to a specified size */ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ - DWORD rc; LONG upperBits = (LONG)((nByte>>32) & 0x7fffffff); LONG lowerBits = (LONG)(nByte & 0xffffffff); + DWORD rc; winFile *pFile = (winFile*)id; - DWORD error = NO_ERROR; + DWORD error; + + assert( id!=0 ); OSTRACE3("TRUNCATE %d %lld\n", pFile->h, nByte); SimulateIOError(return SQLITE_IOERR_TRUNCATE); rc = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); - if( INVALID_SET_FILE_POINTER == rc ){ - error = GetLastError(); + if( rc==INVALID_SET_FILE_POINTER && (error=GetLastError())!=NO_ERROR ){ + pFile->lastErrno = error; + return SQLITE_IOERR_TRUNCATE; } - if( error == NO_ERROR ){ - /* SetEndOfFile will fail if nByte is negative */ - if( SetEndOfFile(pFile->h) ){ - return SQLITE_OK; - } - error = GetLastError(); + /* SetEndOfFile will fail if nByte is negative */ + if( !SetEndOfFile(pFile->h) ){ + pFile->lastErrno = GetLastError(); + return SQLITE_IOERR_TRUNCATE; } - pFile->lastErrno = error; - return SQLITE_IOERR_TRUNCATE; + return SQLITE_OK; } #ifdef SQLITE_TEST @@ -760,6 +773,8 @@ int sqlite3_fullsync_count = 0; static int winSync(sqlite3_file *id, int flags){ #ifndef SQLITE_NO_SYNC winFile *pFile = (winFile*)id; + + assert( id!=0 ); OSTRACE3("SYNC %d lock=%d\n", pFile->h, pFile->locktype); #else UNUSED_PARAMETER(id); @@ -791,9 +806,12 @@ static int winSync(sqlite3_file *id, int flags){ ** Determine the current size of a file in bytes */ static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){ + DWORD upperBits; + DWORD lowerBits; winFile *pFile = (winFile*)id; - DWORD upperBits, lowerBits; DWORD error; + + assert( id!=0 ); SimulateIOError(return SQLITE_IOERR_FSTAT); lowerBits = GetFileSize(pFile->h, &upperBits); if( (lowerBits == INVALID_FILE_SIZE) @@ -897,7 +915,7 @@ static int winLock(sqlite3_file *id, int locktype){ winFile *pFile = (winFile*)id; DWORD error = NO_ERROR; - assert( pFile!=0 ); + assert( id!=0 ); OSTRACE5("LOCK %d %d was %d(%d)\n", pFile->h, locktype, pFile->locktype, pFile->sharedLockByte); @@ -1015,7 +1033,8 @@ static int winLock(sqlite3_file *id, int locktype){ static int winCheckReservedLock(sqlite3_file *id, int *pResOut){ int rc; winFile *pFile = (winFile*)id; - assert( pFile!=0 ); + + assert( id!=0 ); if( pFile->locktype>=RESERVED_LOCK ){ rc = 1; OSTRACE3("TEST WR-LOCK %d %d (local)\n", pFile->h, rc); @@ -1100,8 +1119,8 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){ ** same for both. */ static int winSectorSize(sqlite3_file *id){ - UNUSED_PARAMETER(id); - return SQLITE_DEFAULT_SECTOR_SIZE; + assert( id!=0 ); + return (int)(((winFile*)id)->sectorSize); } /* @@ -1245,7 +1264,6 @@ static int getLastErrorMsg(int nBuf, char *zBuf){ return 0; } - /* ** Open a file. */ @@ -1269,6 +1287,7 @@ static int winOpen( const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */ char zTmpname[MAX_PATH+1]; /* Buffer used to create temp filename */ + assert( id!=0 ); UNUSED_PARAMETER(pVfs); /* If the second argument to this function is NULL, generate a @@ -1348,7 +1367,7 @@ static int winOpen( if( h==INVALID_HANDLE_VALUE ){ free(zConverted); if( flags & SQLITE_OPEN_READWRITE ){ - return winOpen(0, zName, id, + return winOpen(pVfs, zName, id, ((flags|SQLITE_OPEN_READONLY)&~SQLITE_OPEN_READWRITE), pOutFlags); }else{ return SQLITE_CANTOPEN; @@ -1365,6 +1384,7 @@ static int winOpen( pFile->pMethod = &winIoMethod; pFile->h = h; pFile->lastErrno = NO_ERROR; + pFile->sectorSize = getSectorSize(pVfs, zUtf8Name); #if SQLITE_OS_WINCE if( (flags & (SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB)) == (SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB) @@ -1556,6 +1576,73 @@ static int winFullPathname( #endif } +/* +** Get the sector size of the device used to store +** file. +*/ +static int getSectorSize( + sqlite3_vfs *pVfs, + const char *zRelative /* UTF-8 file name */ +){ + DWORD bytesPerSector = SQLITE_DEFAULT_SECTOR_SIZE; + char zFullpath[MAX_PATH+1]; + int rc; + DWORD dwRet = 0, dwDummy; + + /* + ** We need to get the full path name of the file + ** to get the drive letter to look up the sector + ** size. + */ + rc = winFullPathname(pVfs, zRelative, MAX_PATH, zFullpath); + if( rc == SQLITE_OK ) + { + void *zConverted = convertUtf8Filename(zFullpath); + if( zConverted ){ + if( isNT() ){ + int i; + /* trim path to just drive reference */ + WCHAR *p = zConverted; + for(i=0;ipageSize, aData), (isMainJrnl?"main-journal":"sub-journal") @@ -2029,11 +2024,11 @@ static int pager_playback(Pager *pPager, int isHot){ pPager->journalOff = szJ; break; }else{ - /* If we are unable to rollback, then the database is probably - ** going to end up being corrupt. It is corrupt to us, anyhow. - ** Perhaps the next process to come along can fix it.... + /* If we are unable to rollback, quit and return the error + ** code. This will cause the pager to enter the error state + ** so that no further harm will be done. Perhaps the next + ** process to come along will be able to rollback the database. */ - rc = SQLITE_CORRUPT_BKPT; goto end_playback; } } @@ -2054,7 +2049,7 @@ end_playback: ); /* If this playback is happening automatically as a result of an IO or - ** malloc error that occured after the change-counter was updated but + ** malloc error that occurred after the change-counter was updated but ** before the transaction was committed, then the change-counter ** modification may just have been reverted. If this happens in exclusive ** mode, then subsequent transactions performed by the connection will not @@ -3119,9 +3114,9 @@ int sqlite3PagerOpen( ** source file journal.c). */ if( sqlite3JournalSize(pVfs)>sqlite3MemJournalSize() ){ - journalFileSize = sqlite3JournalSize(pVfs); + journalFileSize = ROUND8(sqlite3JournalSize(pVfs)); }else{ - journalFileSize = sqlite3MemJournalSize(); + journalFileSize = ROUND8(sqlite3MemJournalSize()); } /* Set the output variable to NULL in case an error occurs. */ @@ -3177,23 +3172,25 @@ int sqlite3PagerOpen( ** Journal file name (nPathname+8+1 bytes) */ pPtr = (u8 *)sqlite3MallocZero( - sizeof(*pPager) + /* Pager structure */ - pcacheSize + /* PCache object */ - pVfs->szOsFile + /* The main db file */ - journalFileSize * 2 + /* The two journal files */ - nPathname + 1 + /* zFilename */ - nPathname + 8 + 1 /* zJournal */ + ROUND8(sizeof(*pPager)) + /* Pager structure */ + ROUND8(pcacheSize) + /* PCache object */ + ROUND8(pVfs->szOsFile) + /* The main db file */ + journalFileSize * 2 + /* The two journal files */ + nPathname + 1 + /* zFilename */ + nPathname + 8 + 1 /* zJournal */ ); + assert( EIGHT_BYTE_ALIGNMENT(journalFileSize) ); if( !pPtr ){ sqlite3_free(zPathname); return SQLITE_NOMEM; } pPager = (Pager*)(pPtr); - pPager->pPCache = (PCache*)(pPtr += sizeof(*pPager)); - pPager->fd = (sqlite3_file*)(pPtr += pcacheSize); - pPager->sjfd = (sqlite3_file*)(pPtr += pVfs->szOsFile); + pPager->pPCache = (PCache*)(pPtr += ROUND8(sizeof(*pPager))); + pPager->fd = (sqlite3_file*)(pPtr += ROUND8(pcacheSize)); + pPager->sjfd = (sqlite3_file*)(pPtr += ROUND8(pVfs->szOsFile)); pPager->jfd = (sqlite3_file*)(pPtr += journalFileSize); pPager->zFilename = (char*)(pPtr += journalFileSize); + assert( EIGHT_BYTE_ALIGNMENT(pPager->jfd) ); /* Fill in the Pager.zFilename and Pager.zJournal buffers, if required. */ if( zPathname ){ @@ -3268,7 +3265,7 @@ int sqlite3PagerOpen( testcase( rc!=SQLITE_OK ); } - /* If an error occured in either of the blocks above, free the + /* If an error occurred in either of the blocks above, free the ** Pager structure and close the file. */ if( rc!=SQLITE_OK ){ @@ -3279,7 +3276,7 @@ int sqlite3PagerOpen( } /* Initialize the PCache object. */ - nExtra = FORCE_ALIGNMENT(nExtra); + nExtra = ROUND8(nExtra); sqlite3PcacheOpen(szPageDflt, nExtra, !memDb, !memDb?pagerStress:0, (void *)pPager, pPager->pPCache); @@ -3335,11 +3332,12 @@ int sqlite3PagerOpen( ** PAGER_SHARED state. It tests if there is a hot journal present in ** the file-system for the given pager. A hot journal is one that ** needs to be played back. According to this function, a hot-journal -** file exists if the following three criteria are met: +** file exists if the following criteria are met: ** ** * The journal file exists in the file system, and ** * No process holds a RESERVED or greater lock on the database file, and -** * The database file itself is greater than 0 bytes in size. +** * The database file itself is greater than 0 bytes in size, and +** * The first byte of the journal file exists and is not 0x00. ** ** If the current size of the database file is 0 but a journal file ** exists, that is probably an old journal left over from a prior @@ -3347,14 +3345,12 @@ int sqlite3PagerOpen( ** just deleted using OsDelete, *pExists is set to 0 and SQLITE_OK ** is returned. ** -** This routine does not open the journal file to examine its -** content. Hence, the journal might contain the name of a master -** journal file that has been deleted, and hence not be hot. Or -** the header of the journal might be zeroed out. This routine -** does not discover these cases of a non-hot journal - if the -** journal file exists and is not empty this routine assumes it -** is hot. The pager_playback() routine will discover that the -** journal file is not really hot and will no-op. +** This routine does not check if there is a master journal filename +** at the end of the file. If there is, and that master journal file +** does not exist, then the journal file is not really hot. In this +** case this routine will return a false-positive. The pager_playback() +** routine will discover that the journal file is not really hot and +** will not roll it back. ** ** If a hot-journal file is found to exist, *pExists is set to 1 and ** SQLITE_OK returned. If no hot-journal file is present, *pExists is @@ -3365,29 +3361,52 @@ int sqlite3PagerOpen( static int hasHotJournal(Pager *pPager, int *pExists){ sqlite3_vfs * const pVfs = pPager->pVfs; int rc; /* Return code */ - int exists = 0; /* True if a journal file is present */ - int locked = 0; /* True if some process holds a RESERVED lock */ + int exists; /* True if a journal file is present */ assert( pPager!=0 ); assert( pPager->useJournal ); assert( isOpen(pPager->fd) ); + assert( !isOpen(pPager->jfd) ); *pExists = 0; rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists); if( rc==SQLITE_OK && exists ){ + int locked; /* True if some process holds a RESERVED lock */ rc = sqlite3OsCheckReservedLock(pPager->fd, &locked); if( rc==SQLITE_OK && !locked ){ int nPage; + + /* Check the size of the database file. If it consists of 0 pages, + ** then delete the journal file. See the header comment above for + ** the reasoning here. + */ rc = sqlite3PagerPagecount(pPager, &nPage); if( rc==SQLITE_OK ){ - if( nPage==0 ){ - sqlite3OsDelete(pVfs, pPager->zJournal, 0); + if( nPage==0 ){ + rc = sqlite3OsDelete(pVfs, pPager->zJournal, 0); }else{ - *pExists = 1; + /* The journal file exists and no other connection has a reserved + ** or greater lock on the database file. Now check that there is + ** at least one non-zero bytes at the start of the journal file. + ** If there is, then we consider this journal to be hot. If not, + ** it can be ignored. + */ + int f = SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL; + rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &f); + if( rc==SQLITE_OK ){ + u8 first = 0; + rc = sqlite3OsRead(pPager->jfd, (void *)&first, 1, 0); + if( rc==SQLITE_IOERR_SHORT_READ ){ + rc = SQLITE_OK; + } + sqlite3OsClose(pPager->jfd); + *pExists = (first!=0); + } } } } } + return rc; } @@ -3413,10 +3432,13 @@ static int readDbPage(PgHdr *pPg){ if( !isOpen(pPager->fd) ){ assert( pPager->tempFile ); memset(pPg->pData, 0, pPager->pageSize); - return SQLITE_IOERR_SHORT_READ; + return SQLITE_OK; } iOffset = (pgno-1)*(i64)pPager->pageSize; rc = sqlite3OsRead(pPager->fd, pPg->pData, pPager->pageSize, iOffset); + if( rc==SQLITE_IOERR_SHORT_READ ){ + rc = SQLITE_OK; + } if( pgno==1 ){ u8 *dbFileVers = &((u8*)pPg->pData)[24]; memcpy(&pPager->dbFileVers, dbFileVers, sizeof(pPager->dbFileVers)); @@ -3800,7 +3822,7 @@ int sqlite3PagerAcquire( }else{ assert( pPg->pPager==pPager ); rc = readDbPage(pPg); - if( rc!=SQLITE_OK && rc!=SQLITE_IOERR_SHORT_READ ){ + if( rc!=SQLITE_OK ){ pagerDropPage(pPg); return rc; } @@ -3933,7 +3955,7 @@ static int pager_open_journal(Pager *pPager){ sqlite3MemJournalOpen(pPager->jfd); }else{ const int flags = /* VFS flags to open journal file */ - SQLITE_OPEN_READWRITE|SQLITE_OPEN_EXCLUSIVE|SQLITE_OPEN_CREATE| + SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE| (pPager->tempFile ? (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL): (SQLITE_OPEN_MAIN_JOURNAL) @@ -4135,7 +4157,7 @@ static int pager_write(PgHdr *pPg){ pPager->needSync = 1; } - /* An error has occured writing to the journal file. The + /* An error has occurred writing to the journal file. The ** transaction will be rolled back by the layer above. */ if( rc!=SQLITE_OK ){ @@ -4851,7 +4873,7 @@ int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){ ** Parameter op is always either SAVEPOINT_ROLLBACK or SAVEPOINT_RELEASE. ** If it is SAVEPOINT_RELEASE, then release and destroy the savepoint with ** index iSavepoint. If it is SAVEPOINT_ROLLBACK, then rollback all changes -** that have occured since the specified savepoint was created. +** that have occurred since the specified savepoint was created. ** ** The savepoint to rollback or release is identified by parameter ** iSavepoint. A value of 0 means to operate on the outermost savepoint @@ -4997,6 +5019,7 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){ PgHdr *pPgOld; /* The page being overwritten. */ Pgno needSyncPgno = 0; /* Old value of pPg->pgno, if sync is required */ int rc; /* Return code */ + Pgno origPgno; /* The original page number */ assert( pPg->nRef>0 ); @@ -5053,13 +5076,11 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){ assert( !pPgOld || pPgOld->nRef==1 ); if( pPgOld ){ pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC); - } - - sqlite3PcacheMove(pPg, pgno); - if( pPgOld ){ sqlite3PcacheDrop(pPgOld); } + origPgno = pPg->pgno; + sqlite3PcacheMove(pPg, pgno); sqlite3PcacheMakeDirty(pPg); pPager->dbModified = 1; @@ -5097,6 +5118,19 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){ sqlite3PagerUnref(pPgHdr); } + /* + ** For an in-memory database, make sure the original page continues + ** to exist, in case the transaction needs to roll back. We allocate + ** the page now, instead of at rollback, because we can better deal + ** with an out-of-memory error now. Ticket #3761. + */ + if( MEMDB ){ + DbPage *pNew; + rc = sqlite3PagerAcquire(pPager, origPgno, &pNew, 1); + if( rc!=SQLITE_OK ) return rc; + sqlite3PagerUnref(pNew); + } + return SQLITE_OK; } #endif @@ -5151,30 +5185,33 @@ int sqlite3PagerLockingMode(Pager *pPager, int eMode){ ** PAGER_JOURNALMODE_MEMORY ** ** If the parameter is not _QUERY, then the journal-mode is set to the -** value specified. +** value specified. Except, an in-memory database can only have its +** journal mode set to _OFF or _MEMORY. Attempts to change the journal +** mode of an in-memory database to something other than _OFF or _MEMORY +** are silently ignored. ** ** The returned indicate the current (possibly updated) journal-mode. */ int sqlite3PagerJournalMode(Pager *pPager, int eMode){ - if( !MEMDB ){ - assert( eMode==PAGER_JOURNALMODE_QUERY - || eMode==PAGER_JOURNALMODE_DELETE - || eMode==PAGER_JOURNALMODE_TRUNCATE - || eMode==PAGER_JOURNALMODE_PERSIST - || eMode==PAGER_JOURNALMODE_OFF - || eMode==PAGER_JOURNALMODE_MEMORY ); - assert( PAGER_JOURNALMODE_QUERY<0 ); - if( eMode>=0 ){ - pPager->journalMode = (u8)eMode; - }else{ - assert( eMode==PAGER_JOURNALMODE_QUERY ); - } + assert( eMode==PAGER_JOURNALMODE_QUERY + || eMode==PAGER_JOURNALMODE_DELETE + || eMode==PAGER_JOURNALMODE_TRUNCATE + || eMode==PAGER_JOURNALMODE_PERSIST + || eMode==PAGER_JOURNALMODE_OFF + || eMode==PAGER_JOURNALMODE_MEMORY ); + assert( PAGER_JOURNALMODE_QUERY<0 ); + if( eMode>=0 && (!MEMDB || eMode==PAGER_JOURNALMODE_MEMORY + || eMode==PAGER_JOURNALMODE_OFF) ){ + pPager->journalMode = (u8)eMode; } return (int)pPager->journalMode; } /* ** Get/set the size-limit used for persistent journal files. +** +** Setting the size limit to -1 means no limit is enforced. +** An attempt to set a limit smaller than -1 is a no-op. */ i64 sqlite3PagerJournalSizeLimit(Pager *pPager, i64 iLimit){ if( iLimit>=-1 ){ diff --git a/src/parse.y b/src/parse.y index 4b5c312..7024814 100644 --- a/src/parse.y +++ b/src/parse.y @@ -14,7 +14,7 @@ ** the parser. Lemon will also generate a header file containing ** numeric codes for all of the tokens. ** -** @(#) $Id: parse.y,v 1.268 2009/01/29 19:27:47 drh Exp $ +** @(#) $Id: parse.y,v 1.274 2009/04/06 14:16:43 drh Exp $ */ // All token codes are small integers with #defines that begin with "TK_" @@ -133,9 +133,13 @@ cmd ::= ROLLBACK trans_opt TO savepoint_opt nm(X). { ///////////////////// The CREATE TABLE statement //////////////////////////// // cmd ::= create_table create_table_args. -create_table ::= CREATE temp(T) TABLE ifnotexists(E) nm(Y) dbnm(Z). { +create_table ::= createkw temp(T) TABLE ifnotexists(E) nm(Y) dbnm(Z). { sqlite3StartTable(pParse,&Y,&Z,T,0,0,E); } +createkw(A) ::= CREATE(X). { + pParse->db->lookaside.bEnabled = 0; + A = X; +} %type ifnotexists {int} ifnotexists(A) ::= . {A = 0;} ifnotexists(A) ::= IF NOT EXISTS. {A = 1;} @@ -174,6 +178,7 @@ columnid(A) ::= nm(X). { // %type id {Token} id(A) ::= ID(X). {A = X;} +id(A) ::= INDEXED(X). {A = X;} // The following directive causes tokens ABORT, AFTER, ASC, etc. to // fallback to ID if they will not parse as their original value. @@ -224,7 +229,7 @@ ids(A) ::= ID|STRING(X). {A = X;} // The name of a column or table can be any of the following: // %type nm {Token} -nm(A) ::= ID(X). {A = X;} +nm(A) ::= id(X). {A = X;} nm(A) ::= STRING(X). {A = X;} nm(A) ::= JOIN_KW(X). {A = X;} @@ -364,7 +369,7 @@ ifexists(A) ::= . {A = 0;} ///////////////////// The CREATE VIEW statement ///////////////////////////// // %ifndef SQLITE_OMIT_VIEW -cmd ::= CREATE(X) temp(T) VIEW ifnotexists(E) nm(Y) dbnm(Z) AS select(S). { +cmd ::= createkw(X) temp(T) VIEW ifnotexists(E) nm(Y) dbnm(Z) AS select(S). { sqlite3CreateView(pParse, &X, &Y, &Z, S, T, E); } cmd ::= DROP VIEW ifexists(E) fullname(X). { @@ -697,7 +702,7 @@ inscollist(A) ::= nm(Y). expr(A) ::= term(X). {A = X;} expr(A) ::= LP(B) expr(X) RP(E). {A = X; sqlite3ExprSpan(A,&B,&E); } term(A) ::= NULL(X). {A = sqlite3PExpr(pParse, @X, 0, 0, &X);} -expr(A) ::= ID(X). {A = sqlite3PExpr(pParse, TK_ID, 0, 0, &X);} +expr(A) ::= id(X). {A = sqlite3PExpr(pParse, TK_ID, 0, 0, &X);} expr(A) ::= JOIN_KW(X). {A = sqlite3PExpr(pParse, TK_ID, 0, 0, &X);} expr(A) ::= nm(X) DOT nm(Y). { Expr *temp1 = sqlite3PExpr(pParse, TK_ID, 0, 0, &X); @@ -824,7 +829,7 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] { pList = sqlite3ExprListAppend(pParse,pList, Y, 0); A = sqlite3PExpr(pParse, TK_BETWEEN, W, 0, 0); if( A ){ - A->pList = pList; + A->x.pList = pList; }else{ sqlite3ExprListDelete(pParse->db, pList); } @@ -838,7 +843,7 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] { expr(A) ::= expr(X) in_op(N) LP exprlist(Y) RP(E). [IN] { A = sqlite3PExpr(pParse, TK_IN, X, 0, 0); if( A ){ - A->pList = Y; + A->x.pList = Y; sqlite3ExprSetHeight(pParse, A); }else{ sqlite3ExprListDelete(pParse->db, Y); @@ -849,7 +854,8 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] { expr(A) ::= LP(B) select(X) RP(E). { A = sqlite3PExpr(pParse, TK_SELECT, 0, 0, 0); if( A ){ - A->pSelect = X; + A->x.pSelect = X; + ExprSetProperty(A, EP_xIsSelect); sqlite3ExprSetHeight(pParse, A); }else{ sqlite3SelectDelete(pParse->db, X); @@ -859,7 +865,8 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] { expr(A) ::= expr(X) in_op(N) LP select(Y) RP(E). [IN] { A = sqlite3PExpr(pParse, TK_IN, X, 0, 0); if( A ){ - A->pSelect = Y; + A->x.pSelect = Y; + ExprSetProperty(A, EP_xIsSelect); sqlite3ExprSetHeight(pParse, A); }else{ sqlite3SelectDelete(pParse->db, Y); @@ -871,7 +878,8 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] { SrcList *pSrc = sqlite3SrcListAppend(pParse->db, 0,&Y,&Z); A = sqlite3PExpr(pParse, TK_IN, X, 0, 0); if( A ){ - A->pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0); + A->x.pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0); + ExprSetProperty(A, EP_xIsSelect); sqlite3ExprSetHeight(pParse, A); }else{ sqlite3SrcListDelete(pParse->db, pSrc); @@ -882,7 +890,8 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] { expr(A) ::= EXISTS(B) LP select(Y) RP(E). { Expr *p = A = sqlite3PExpr(pParse, TK_EXISTS, 0, 0, 0); if( p ){ - p->pSelect = Y; + p->x.pSelect = Y; + ExprSetProperty(A, EP_xIsSelect); sqlite3ExprSpan(p,&B,&E); sqlite3ExprSetHeight(pParse, A); }else{ @@ -895,7 +904,7 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] { expr(A) ::= CASE(C) case_operand(X) case_exprlist(Y) case_else(Z) END(E). { A = sqlite3PExpr(pParse, TK_CASE, X, Z, 0); if( A ){ - A->pList = Y; + A->x.pList = Y; sqlite3ExprSetHeight(pParse, A); }else{ sqlite3ExprListDelete(pParse->db, Y); @@ -936,7 +945,7 @@ nexprlist(A) ::= expr(Y). ///////////////////////////// The CREATE INDEX command /////////////////////// // -cmd ::= CREATE(S) uniqueflag(U) INDEX ifnotexists(NE) nm(X) dbnm(D) +cmd ::= createkw(S) uniqueflag(U) INDEX ifnotexists(NE) nm(X) dbnm(D) ON nm(Y) LP idxlist(Z) RP(E). { sqlite3CreateIndex(pParse, &X, &D, sqlite3SrcListAppend(pParse->db,0,&Y,0), Z, U, @@ -997,16 +1006,19 @@ cmd ::= VACUUM nm. {sqlite3Vacuum(pParse);} // %ifndef SQLITE_OMIT_PARSER %ifndef SQLITE_OMIT_PRAGMA -cmd ::= PRAGMA nm(X) dbnm(Z) EQ nmnum(Y). {sqlite3Pragma(pParse,&X,&Z,&Y,0);} -cmd ::= PRAGMA nm(X) dbnm(Z) EQ ON(Y). {sqlite3Pragma(pParse,&X,&Z,&Y,0);} -cmd ::= PRAGMA nm(X) dbnm(Z) EQ DELETE(Y). {sqlite3Pragma(pParse,&X,&Z,&Y,0);} -cmd ::= PRAGMA nm(X) dbnm(Z) EQ minus_num(Y). { - sqlite3Pragma(pParse,&X,&Z,&Y,1); -} +cmd ::= PRAGMA nm(X) dbnm(Z). {sqlite3Pragma(pParse,&X,&Z,0,0);} +cmd ::= PRAGMA nm(X) dbnm(Z) EQ nmnum(Y). {sqlite3Pragma(pParse,&X,&Z,&Y,0);} cmd ::= PRAGMA nm(X) dbnm(Z) LP nmnum(Y) RP. {sqlite3Pragma(pParse,&X,&Z,&Y,0);} -cmd ::= PRAGMA nm(X) dbnm(Z). {sqlite3Pragma(pParse,&X,&Z,0,0);} +cmd ::= PRAGMA nm(X) dbnm(Z) EQ minus_num(Y). + {sqlite3Pragma(pParse,&X,&Z,&Y,1);} +cmd ::= PRAGMA nm(X) dbnm(Z) LP minus_num(Y) RP. + {sqlite3Pragma(pParse,&X,&Z,&Y,1);} + nmnum(A) ::= plus_num(X). {A = X;} nmnum(A) ::= nm(X). {A = X;} +nmnum(A) ::= ON(X). {A = X;} +nmnum(A) ::= DELETE(X). {A = X;} +nmnum(A) ::= DEFAULT(X). {A = X;} %endif SQLITE_OMIT_PRAGMA %endif SQLITE_OMIT_PARSER plus_num(A) ::= plus_opt number(X). {A = X;} @@ -1019,7 +1031,7 @@ plus_opt ::= . %ifndef SQLITE_OMIT_TRIGGER -cmd ::= CREATE trigger_decl(A) BEGIN trigger_cmd_list(S) END(Z). { +cmd ::= createkw trigger_decl(A) BEGIN trigger_cmd_list(S) END(Z). { Token all; all.z = A.z; all.n = (int)(Z.z - A.z) + Z.n; @@ -1100,14 +1112,14 @@ trigger_cmd(A) ::= select(X). {A = sqlite3TriggerSelectStep(pParse->db, X); } expr(A) ::= RAISE(X) LP IGNORE RP(Y). { A = sqlite3PExpr(pParse, TK_RAISE, 0, 0, 0); if( A ){ - A->iColumn = OE_Ignore; + A->affinity = OE_Ignore; sqlite3ExprSpan(A, &X, &Y); } } expr(A) ::= RAISE(X) LP raisetype(T) COMMA nm(Z) RP(Y). { A = sqlite3PExpr(pParse, TK_RAISE, 0, 0, &Z); if( A ) { - A->iColumn = T; + A->affinity = (char)T; sqlite3ExprSpan(A, &X, &Y); } } @@ -1165,6 +1177,7 @@ cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column(Y). { sqlite3AlterFinishAddColumn(pParse, &Y); } add_column_fullname ::= fullname(X). { + pParse->db->lookaside.bEnabled = 0; sqlite3AlterBeginAddColumn(pParse, X); } kwcolumn_opt ::= . @@ -1175,7 +1188,7 @@ kwcolumn_opt ::= COLUMNKW. %ifndef SQLITE_OMIT_VIRTUALTABLE cmd ::= create_vtab. {sqlite3VtabFinishParse(pParse,0);} cmd ::= create_vtab LP vtabarglist RP(X). {sqlite3VtabFinishParse(pParse,&X);} -create_vtab ::= CREATE VIRTUAL TABLE nm(X) dbnm(Y) USING nm(Z). { +create_vtab ::= createkw VIRTUAL TABLE nm(X) dbnm(Y) USING nm(Z). { sqlite3VtabBeginParse(pParse, &X, &Y, &Z); } vtabarglist ::= vtabarg. diff --git a/src/pcache.c b/src/pcache.c index 26d9c82..5715819 100644 --- a/src/pcache.c +++ b/src/pcache.c @@ -11,7 +11,7 @@ ************************************************************************* ** This file implements that page cache. ** -** @(#) $Id: pcache.c,v 1.43 2009/01/23 16:45:01 danielk1977 Exp $ +** @(#) $Id: pcache.c,v 1.44 2009/03/31 01:32:18 drh Exp $ */ #include "sqliteInt.h" @@ -336,11 +336,9 @@ void sqlite3PcacheDrop(PgHdr *p){ ** make it so. */ void sqlite3PcacheMakeDirty(PgHdr *p){ - PCache *pCache; p->flags &= ~PGHDR_DONT_WRITE; assert( p->nRef>0 ); if( 0==(p->flags & PGHDR_DIRTY) ){ - pCache = p->pCache; p->flags |= PGHDR_DIRTY; pcacheAddToDirtyList( p); } diff --git a/src/pcache1.c b/src/pcache1.c index a80d337..0f2c36d 100644 --- a/src/pcache1.c +++ b/src/pcache1.c @@ -16,7 +16,7 @@ ** If the default page cache implementation is overriden, then neither of ** these two features are available. ** -** @(#) $Id: pcache1.c,v 1.8 2009/01/23 16:45:01 danielk1977 Exp $ +** @(#) $Id: pcache1.c,v 1.10 2009/03/23 04:33:33 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -129,7 +129,7 @@ static SQLITE_WSD struct PCacheGlobal { */ void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){ PgFreeslot *p; - sz &= ~7; + sz = ROUNDDOWN8(sz); pcache1.szSlot = sz; pcache1.pStart = pBuf; pcache1.pFree = 0; @@ -516,7 +516,7 @@ static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){ nPinned = pCache->nPage - pCache->nRecyclable; if( createFlag==1 && pCache->bPurgeable && ( nPinned>=(pcache1.nMaxPage+pCache->nMin-pcache1.nMinPage) - || nPinned>=(pCache->nMax) + || nPinned>=(pCache->nMax * 9 / 10) )){ goto fetch_out; } diff --git a/src/pragma.c b/src/pragma.c index ad35d54..f3761b1 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -11,7 +11,7 @@ ************************************************************************* ** This file contains code used to implement the PRAGMA command. ** -** $Id: pragma.c,v 1.202 2009/01/20 16:53:41 danielk1977 Exp $ +** $Id: pragma.c,v 1.209 2009/04/07 22:05:43 drh Exp $ */ #include "sqliteInt.h" @@ -144,14 +144,16 @@ static int changeTempStorage(Parse *pParse, const char *zStorageType){ /* ** Generate code to return a single integer value. */ -static void returnSingleInt(Parse *pParse, const char *zLabel, int value){ +static void returnSingleInt(Parse *pParse, const char *zLabel, i64 value){ Vdbe *v = sqlite3GetVdbe(pParse); int mem = ++pParse->nMem; - sqlite3VdbeAddOp2(v, OP_Integer, value, mem); - if( pParse->explain==0 ){ - sqlite3VdbeSetNumCols(v, 1); - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLabel, SQLITE_STATIC); + i64 *pI64 = sqlite3DbMallocRaw(pParse->db, sizeof(value)); + if( pI64 ){ + memcpy(pI64, &value, sizeof(value)); } + sqlite3VdbeAddOp4(v, OP_Int64, 0, mem, 0, (char*)pI64, P4_INT64); + sqlite3VdbeSetNumCols(v, 1); + sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLabel, SQLITE_STATIC); sqlite3VdbeAddOp2(v, OP_ResultRow, mem, 1); } @@ -172,6 +174,7 @@ static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){ { "empty_result_callbacks", SQLITE_NullCallback }, { "legacy_file_format", SQLITE_LegacyFileFmt }, { "fullfsync", SQLITE_FullFSync }, + { "reverse_unordered_selects", SQLITE_ReverseOrder }, #ifdef SQLITE_DEBUG { "sql_trace", SQLITE_SqlTrace }, { "vdbe_listing", SQLITE_VdbeListing }, @@ -368,7 +371,7 @@ void sqlite3Pragma( ** buffer that the pager module resizes using sqlite3_realloc(). */ db->nextPagesize = atoi(zRight); - if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, -1) ){ + if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, -1, 0) ){ db->mallocFailed = 1; } } @@ -530,14 +533,11 @@ void sqlite3Pragma( Pager *pPager = sqlite3BtreePager(pDb->pBt); i64 iLimit = -2; if( zRight ){ - int iLimit32 = atoi(zRight); - if( iLimit32<-1 ){ - iLimit32 = -1; - } - iLimit = iLimit32; + sqlite3Atoi64(zRight, &iLimit); + if( iLimit<-1 ) iLimit = -1; } iLimit = sqlite3PagerJournalSizeLimit(pPager, iLimit); - returnSingleInt(pParse, "journal_size_limit", (int)iLimit); + returnSingleInt(pParse, "journal_size_limit", iLimit); }else #endif /* SQLITE_OMIT_PAGER_PRAGMAS */ @@ -831,7 +831,6 @@ void sqlite3Pragma( sqlite3VdbeSetColName(v, 5, COLNAME_NAME, "pk", SQLITE_STATIC); sqlite3ViewGetColumnNames(pParse, pTab); for(i=0, pCol=pTab->aCol; inCol; i++, pCol++){ - const Token *pDflt; if( IsHiddenColumn(pCol) ){ nHidden++; continue; @@ -842,9 +841,9 @@ void sqlite3Pragma( pCol->zType ? pCol->zType : "", 0); sqlite3VdbeAddOp2(v, OP_Integer, (pCol->notNull ? 1 : 0), 4); if( pCol->pDflt ){ - pDflt = &pCol->pDflt->span; - assert( pDflt->z ); - sqlite3VdbeAddOp4(v, OP_String8, 0, 5, 0, (char*)pDflt->z, pDflt->n); + const Token *p = &pCol->pDflt->span; + assert( p->z ); + sqlite3VdbeAddOp4(v, OP_String8, 0, 5, 0, (char*)p->z, p->n); }else{ sqlite3VdbeAddOp2(v, OP_Null, 0, 5); } @@ -1079,7 +1078,6 @@ void sqlite3Pragma( cnt++; } } - if( cnt==0 ) continue; /* Make sure sufficient number of registers have been allocated */ if( pParse->nMem < cnt+4 ){ @@ -1152,7 +1150,6 @@ void sqlite3Pragma( { OP_Concat, 3, 2, 2}, { OP_ResultRow, 2, 1, 0}, }; - if( pIdx->tnum==0 ) continue; addr = sqlite3VdbeAddOp1(v, OP_IfPos, 1); sqlite3VdbeAddOp2(v, OP_Halt, 0, 0); sqlite3VdbeJumpHere(v, addr); diff --git a/src/prepare.c b/src/prepare.c index a4448d9..e3479f3 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -13,7 +13,7 @@ ** interface, and routines that contribute to loading the database schema ** from disk. ** -** $Id: prepare.c,v 1.105 2009/01/20 16:53:41 danielk1977 Exp $ +** $Id: prepare.c,v 1.116 2009/04/02 18:32:27 drh Exp $ */ #include "sqliteInt.h" @@ -77,21 +77,17 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){ */ char *zErr; int rc; - u8 lookasideEnabled; assert( db->init.busy ); db->init.iDb = iDb; db->init.newTnum = atoi(argv[1]); - lookasideEnabled = db->lookaside.bEnabled; - db->lookaside.bEnabled = 0; rc = sqlite3_exec(db, argv[2], 0, 0, &zErr); db->init.iDb = 0; - db->lookaside.bEnabled = lookasideEnabled; assert( rc!=SQLITE_OK || zErr==0 ); if( SQLITE_OK!=rc ){ pData->rc = rc; if( rc==SQLITE_NOMEM ){ db->mallocFailed = 1; - }else if( rc!=SQLITE_INTERRUPT ){ + }else if( rc!=SQLITE_INTERRUPT && (rc&0xff)!=SQLITE_LOCKED ){ corruptSchema(pData, argv[0], zErr); } sqlite3DbFree(db, zErr); @@ -351,10 +347,10 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ } if( rc==SQLITE_OK || (db->flags&SQLITE_RecoveryMode)){ /* Black magic: If the SQLITE_RecoveryMode flag is set, then consider - ** the schema loaded, even if errors occured. In this situation the + ** the schema loaded, even if errors occurred. In this situation the ** current sqlite3_prepare() operation will fail, but the following one ** will attempt to compile the supplied statement against whatever subset - ** of the schema was loaded before the error occured. The primary + ** of the schema was loaded before the error occurred. The primary ** purpose of this is to allow access to the sqlite_master table ** even when its contents have been corrupted. */ @@ -532,26 +528,45 @@ static int sqlite3Prepare( int rc = SQLITE_OK; int i; - assert( ppStmt ); - *ppStmt = 0; - if( sqlite3SafetyOn(db) ){ - return SQLITE_MISUSE; - } + if( sqlite3SafetyOn(db) ) return SQLITE_MISUSE; + assert( ppStmt && *ppStmt==0 ); assert( !db->mallocFailed ); assert( sqlite3_mutex_held(db->mutex) ); - /* If any attached database schemas are locked, do not proceed with - ** compilation. Instead return SQLITE_LOCKED immediately. + /* Check to verify that it is possible to get a read lock on all + ** database schemas. The inability to get a read lock indicates that + ** some other database connection is holding a write-lock, which in + ** turn means that the other connection has made uncommitted changes + ** to the schema. + ** + ** Were we to proceed and prepare the statement against the uncommitted + ** schema changes and if those schema changes are subsequently rolled + ** back and different changes are made in their place, then when this + ** prepared statement goes to run the schema cookie would fail to detect + ** the schema change. Disaster would follow. + ** + ** This thread is currently holding mutexes on all Btrees (because + ** of the sqlite3BtreeEnterAll() in sqlite3LockAndPrepare()) so it + ** is not possible for another thread to start a new schema change + ** while this routine is running. Hence, we do not need to hold + ** locks on the schema, we just need to make sure nobody else is + ** holding them. + ** + ** Note that setting READ_UNCOMMITTED overrides most lock detection, + ** but it does *not* override schema lock detection, so this all still + ** works even if READ_UNCOMMITTED is set. */ for(i=0; inDb; i++) { Btree *pBt = db->aDb[i].pBt; if( pBt ){ + assert( sqlite3BtreeHoldsMutex(pBt) ); rc = sqlite3BtreeSchemaLocked(pBt); if( rc ){ const char *zDb = db->aDb[i].zName; - sqlite3Error(db, SQLITE_LOCKED, "database schema is locked: %s", zDb); + sqlite3Error(db, rc, "database schema is locked: %s", zDb); (void)sqlite3SafetyOff(db); - return sqlite3ApiExit(db, SQLITE_LOCKED); + testcase( db->flags & SQLITE_ReadUncommitted ); + return sqlite3ApiExit(db, rc); } } } @@ -621,11 +636,13 @@ static int sqlite3Prepare( rc = SQLITE_MISUSE; } - if( saveSqlFlag ){ - sqlite3VdbeSetSql(sParse.pVdbe, zSql, (int)(sParse.zTail - zSql)); + assert( db->init.busy==0 || saveSqlFlag==0 ); + if( db->init.busy==0 ){ + Vdbe *pVdbe = sParse.pVdbe; + sqlite3VdbeSetSql(pVdbe, zSql, (int)(sParse.zTail-zSql), saveSqlFlag); } - if( rc!=SQLITE_OK || db->mallocFailed ){ - sqlite3_finalize((sqlite3_stmt*)sParse.pVdbe); + if( sParse.pVdbe && (rc!=SQLITE_OK || db->mallocFailed) ){ + sqlite3VdbeFinalize(sParse.pVdbe); assert(!(*ppStmt)); }else{ *ppStmt = (sqlite3_stmt*)sParse.pVdbe; @@ -651,6 +668,8 @@ static int sqlite3LockAndPrepare( const char **pzTail /* OUT: End of parsed string */ ){ int rc; + assert( ppStmt!=0 ); + *ppStmt = 0; if( !sqlite3SafetyCheckOk(db) ){ return SQLITE_MISUSE; } @@ -664,8 +683,11 @@ static int sqlite3LockAndPrepare( /* ** Rerun the compilation of a statement after a schema change. -** Return true if the statement was recompiled successfully. -** Return false if there is an error of some kind. +** +** If the statement is successfully recompiled, return SQLITE_OK. Otherwise, +** if the statement cannot be recompiled because another connection has +** locked the sqlite3_master table, return SQLITE_LOCKED. If any other error +** occurs, return SQLITE_SCHEMA. */ int sqlite3Reprepare(Vdbe *p){ int rc; @@ -684,7 +706,7 @@ int sqlite3Reprepare(Vdbe *p){ db->mallocFailed = 1; } assert( pNew==0 ); - return 0; + return (rc==SQLITE_LOCKED) ? SQLITE_LOCKED : SQLITE_SCHEMA; }else{ assert( pNew!=0 ); } @@ -692,7 +714,7 @@ int sqlite3Reprepare(Vdbe *p){ sqlite3TransferBindings(pNew, (sqlite3_stmt*)p); sqlite3VdbeResetStepResult((Vdbe*)pNew); sqlite3VdbeFinalize((Vdbe*)pNew); - return 1; + return SQLITE_OK; } @@ -750,6 +772,8 @@ static int sqlite3Prepare16( const char *zTail8 = 0; int rc = SQLITE_OK; + assert( ppStmt ); + *ppStmt = 0; if( !sqlite3SafetyCheckOk(db) ){ return SQLITE_MISUSE; } diff --git a/src/printf.c b/src/printf.c index 58e575b..bc8d022 100644 --- a/src/printf.c +++ b/src/printf.c @@ -5,7 +5,7 @@ ** an historical reference. Most of the "enhancements" have been backed ** out so that the functionality is now the same as standard printf(). ** -** $Id: printf.c,v 1.99 2008/12/10 19:26:24 drh Exp $ +** $Id: printf.c,v 1.102 2009/04/08 16:10:04 drh Exp $ ** ************************************************************************** ** @@ -77,6 +77,8 @@ #define etSQLESCAPE3 15 /* %w -> Strings with '\"' doubled */ #define etORDINAL 16 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */ +#define etINVALID 0 /* Any unrecognized conversion type */ + /* ** An "etByte" is an 8-bit unsigned value. @@ -133,6 +135,9 @@ static const et_info fmtinfo[] = { { 'n', 0, 0, etSIZE, 0, 0 }, { '%', 0, 0, etPERCENT, 0, 0 }, { 'p', 16, 0, etPOINTER, 0, 1 }, + +/* All the rest have the FLAG_INTERN bit set and are thus for internal +** use only */ { 'T', 0, 2, etTOKEN, 0, 0 }, { 'S', 0, 2, etSRCLIST, 0, 0 }, { 'r', 10, 3, etORDINAL, 0, 0 }, @@ -335,7 +340,8 @@ void sqlite3VXPrintf( flag_long = flag_longlong = 0; } /* Fetch the info entry for the field */ - infop = 0; + infop = &fmtinfo[0]; + xtype = etINVALID; for(idx=0; idxtooBig | p->mallocFailed ){ + testcase(p->tooBig); + testcase(p->mallocFailed); return; } if( N<0 ){ N = sqlite3Strlen30(z); } - if( N==0 || z==0 ){ + if( N==0 || NEVER(z==0) ){ return; } if( p->nChar+N >= p->nAlloc ){ @@ -816,12 +826,13 @@ char *sqlite3VMPrintf(sqlite3 *db, const char *zFormat, va_list ap){ char *z; char zBase[SQLITE_PRINT_BUF_SIZE]; StrAccum acc; + assert( db!=0 ); sqlite3StrAccumInit(&acc, zBase, sizeof(zBase), - db ? db->aLimit[SQLITE_LIMIT_LENGTH] : SQLITE_MAX_LENGTH); + db->aLimit[SQLITE_LIMIT_LENGTH]); acc.db = db; sqlite3VXPrintf(&acc, 1, zFormat, ap); z = sqlite3StrAccumFinish(&acc); - if( acc.mallocFailed && db ){ + if( acc.mallocFailed ){ db->mallocFailed = 1; } return z; diff --git a/src/resolve.c b/src/resolve.c index d6f58fc..1f2641d 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -14,7 +14,7 @@ ** resolve all identifiers by associating them with a particular ** table and column. ** -** $Id: resolve.c,v 1.15 2008/12/10 19:26:24 drh Exp $ +** $Id: resolve.c,v 1.20 2009/03/05 04:23:47 shane Exp $ */ #include "sqliteInt.h" #include @@ -63,8 +63,9 @@ static void resolveAlias( assert( pOrig!=0 ); assert( pOrig->flags & EP_Resolved ); db = pParse->db; - pDup = sqlite3ExprDup(db, pOrig); + pDup = sqlite3ExprDup(db, pOrig, 0); if( pDup==0 ) return; + sqlite3TokenCopy(db, &pDup->token, &pOrig->token); if( pDup->op!=TK_COLUMN && zType[0]!='G' ){ pDup = sqlite3PExpr(pParse, TK_AS, pDup, 0, 0); if( pDup==0 ) return; @@ -129,6 +130,7 @@ static int lookupName( NameContext *pTopNC = pNC; /* First namecontext in the list */ Schema *pSchema = 0; /* Schema of the expression */ + assert( pNC ); /* the name context cannot be NULL. */ assert( pColumnToken && pColumnToken->z ); /* The Z in X.Y.Z cannot be NULL */ /* Dequote and zero-terminate the names */ @@ -282,8 +284,8 @@ static int lookupName( if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){ Expr *pOrig; assert( pExpr->pLeft==0 && pExpr->pRight==0 ); - assert( pExpr->pList==0 ); - assert( pExpr->pSelect==0 ); + assert( pExpr->x.pList==0 ); + assert( pExpr->x.pSelect==0 ); pOrig = pEList->a[j].pExpr; if( !pNC->allowAgg && ExprHasProperty(pOrig, EP_Agg) ){ sqlite3ErrorMsg(pParse, "misuse of aliased aggregate %s", zAs); @@ -474,8 +476,8 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ */ case TK_CONST_FUNC: case TK_FUNCTION: { - ExprList *pList = pExpr->pList; /* The argument list */ - int n = pList ? pList->nExpr : 0; /* Number of arguments */ + ExprList *pList = pExpr->x.pList; /* The argument list */ + int n = pList ? pList->nExpr : 0; /* Number of arguments */ int no_such_func = 0; /* True if no such function exists */ int wrong_num_args = 0; /* True if wrong number of arguments */ int is_agg = 0; /* True if is an aggregate function */ @@ -485,6 +487,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ FuncDef *pDef; /* Information about the function */ u8 enc = ENC(pParse->db); /* The database encoding */ + assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); zId = (char*)pExpr->token.z; nId = pExpr->token.n; pDef = sqlite3FindFunction(pParse->db, zId, nId, n, enc, 0); @@ -541,14 +544,14 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ case TK_EXISTS: #endif case TK_IN: { - if( pExpr->pSelect ){ + if( ExprHasProperty(pExpr, EP_xIsSelect) ){ int nRef = pNC->nRef; #ifndef SQLITE_OMIT_CHECK if( pNC->isCheck ){ sqlite3ErrorMsg(pParse,"subqueries prohibited in CHECK constraints"); } #endif - sqlite3WalkSelect(pWalker, pExpr->pSelect); + sqlite3WalkSelect(pWalker, pExpr->x.pSelect); assert( pNC->nRef>=nRef ); if( nRef!=pNC->nRef ){ ExprSetProperty(pExpr, EP_VarSelect); @@ -736,7 +739,7 @@ static int resolveCompoundOrderBy( }else{ iCol = resolveAsName(pParse, pEList, pE); if( iCol==0 ){ - pDup = sqlite3ExprDup(db, pE); + pDup = sqlite3ExprDup(db, pE, 0); if( !db->mallocFailed ){ assert(pDup); iCol = resolveOrderByTermToExprList(pParse, pSelect, pDup); diff --git a/src/rowset.c b/src/rowset.c index 08ac492..e8f7a52 100644 --- a/src/rowset.c +++ b/src/rowset.c @@ -25,7 +25,7 @@ ** Big chunks of rowid/next-ptr pairs are allocated at a time, to ** reduce the malloc overhead. ** -** $Id: rowset.c,v 1.3 2009/01/13 20:14:16 drh Exp $ +** $Id: rowset.c,v 1.4 2009/04/01 19:35:55 drh Exp $ */ #include "sqliteInt.h" @@ -120,7 +120,7 @@ void sqlite3RowSetClear(RowSet *p){ void sqlite3RowSetInsert(RowSet *p, i64 rowid){ struct RowSetEntry *pEntry; struct RowSetEntry *pLast; - if( p==0 ) return; /* Must have been a malloc failure */ + assert( p!=0 ); if( p->nFresh==0 ){ struct RowSetChunk *pNew; pNew = sqlite3DbMallocRaw(p->db, sizeof(*pNew)); diff --git a/src/select.c b/src/select.c index 943f0ef..08d9e41 100644 --- a/src/select.c +++ b/src/select.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle SELECT statements in SQLite. ** -** $Id: select.c,v 1.499 2009/02/09 13:19:28 drh Exp $ +** $Id: select.c,v 1.507 2009/04/02 16:59:47 drh Exp $ */ #include "sqliteInt.h" @@ -800,8 +800,7 @@ static void generateSortTail( iTab = pOrderBy->iECursor; if( eDest==SRT_Output || eDest==SRT_Coroutine ){ pseudoTab = pParse->nTab++; - sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, nColumn); - sqlite3VdbeAddOp2(v, OP_OpenPseudo, pseudoTab, eDest==SRT_Output); + sqlite3VdbeAddOp3(v, OP_OpenPseudo, pseudoTab, eDest==SRT_Output, nColumn); } addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak); codeOffset(v, p, addrContinue); @@ -987,8 +986,9 @@ static const char *columnType( ** statement. */ NameContext sNC; - Select *pS = pExpr->pSelect; + Select *pS = pExpr->x.pSelect; Expr *p = pS->pEList->a[0].pExpr; + assert( ExprHasProperty(pExpr, EP_xIsSelect) ); sNC.pSrcList = pS->pSrc; sNC.pNext = pNC; sNC.pParse = pNC->pParse; @@ -1229,7 +1229,7 @@ static int selectColumnsFromExprList( ** The column list has only names, not types or collations. This ** routine goes through and adds the types and collations. ** -** This routine requires that all indentifiers in the SELECT +** This routine requires that all identifiers in the SELECT ** statement be resolved. */ static void selectAddColumnTypeAndCollation( @@ -1284,7 +1284,7 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){ if( pTab==0 ){ return 0; } - pTab->db = db; + pTab->dbMem = db->lookaside.bEnabled ? db : 0; pTab->nRef = 1; pTab->zName = 0; selectColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol); @@ -2141,7 +2141,7 @@ static int multiSelectOrderBy( /* Reattach the ORDER BY clause to the query. */ p->pOrderBy = pOrderBy; - pPrior->pOrderBy = sqlite3ExprListDup(pParse->db, pOrderBy); + pPrior->pOrderBy = sqlite3ExprListDup(pParse->db, pOrderBy, 0); /* Allocate a range of temporary registers and the KeyInfo needed ** for the logic that removes duplicate result rows when the @@ -2392,23 +2392,26 @@ static void substExpr( }else{ Expr *pNew; assert( pEList!=0 && pExpr->iColumnnExpr ); - assert( pExpr->pLeft==0 && pExpr->pRight==0 && pExpr->pList==0 ); + assert( pExpr->pLeft==0 && pExpr->pRight==0 ); pNew = pEList->a[pExpr->iColumn].pExpr; assert( pNew!=0 ); pExpr->op = pNew->op; assert( pExpr->pLeft==0 ); - pExpr->pLeft = sqlite3ExprDup(db, pNew->pLeft); + pExpr->pLeft = sqlite3ExprDup(db, pNew->pLeft, 0); assert( pExpr->pRight==0 ); - pExpr->pRight = sqlite3ExprDup(db, pNew->pRight); - assert( pExpr->pList==0 ); - pExpr->pList = sqlite3ExprListDup(db, pNew->pList); + pExpr->pRight = sqlite3ExprDup(db, pNew->pRight, 0); pExpr->iTable = pNew->iTable; pExpr->pTab = pNew->pTab; pExpr->iColumn = pNew->iColumn; pExpr->iAgg = pNew->iAgg; sqlite3TokenCopy(db, &pExpr->token, &pNew->token); sqlite3TokenCopy(db, &pExpr->span, &pNew->span); - pExpr->pSelect = sqlite3SelectDup(db, pNew->pSelect); + assert( pExpr->x.pList==0 && pExpr->x.pSelect==0 ); + if( ExprHasProperty(pNew, EP_xIsSelect) ){ + pExpr->x.pSelect = sqlite3SelectDup(db, pNew->x.pSelect, 0); + }else{ + pExpr->x.pList = sqlite3ExprListDup(db, pNew->x.pList, 0); + } pExpr->flags = pNew->flags; pExpr->pAggInfo = pNew->pAggInfo; pNew->pAggInfo = 0; @@ -2416,8 +2419,11 @@ static void substExpr( }else{ substExpr(db, pExpr->pLeft, iTable, pEList); substExpr(db, pExpr->pRight, iTable, pEList); - substSelect(db, pExpr->pSelect, iTable, pEList); - substExprList(db, pExpr->pList, iTable, pEList); + if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + substSelect(db, pExpr->x.pSelect, iTable, pEList); + }else{ + substExprList(db, pExpr->x.pList, iTable, pEList); + } } } static void substExprList( @@ -2549,6 +2555,12 @@ static void substSelect( ** (19) The subquery does not use LIMIT or the outer query does not ** have a WHERE clause. ** +** (20) If the sub-query is a compound select, then it must not use +** an ORDER BY clause. Ticket #3773. We could relax this constraint +** somewhat by saying that the terms of the ORDER BY clause must +** appear as unmodified result columns in the outer query. But +** have other optimizations in mind to deal with that case. +** ** In this routine, the "p" parameter is a pointer to the outer query. ** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query ** uses aggregates and subqueryIsAgg is true if the subquery uses aggregates. @@ -2659,6 +2671,9 @@ static int flattenSubquery( ** queries. */ if( pSub->pPrior ){ + if( pSub->pOrderBy ){ + return 0; /* Restriction 20 */ + } if( isAgg || (p->selFlags & SF_Distinct)!=0 || pSrc->nSrc!=1 ){ return 0; } @@ -2729,7 +2744,7 @@ static int flattenSubquery( p->pSrc = 0; p->pPrior = 0; p->pLimit = 0; - pNew = sqlite3SelectDup(db, p); + pNew = sqlite3SelectDup(db, p, 0); p->pLimit = pLimit; p->pOrderBy = pOrderBy; p->pSrc = pSrc; @@ -2873,7 +2888,7 @@ static int flattenSubquery( substExprList(db, pParent->pOrderBy, iParent, pSub->pEList); } if( pSub->pWhere ){ - pWhere = sqlite3ExprDup(db, pSub->pWhere); + pWhere = sqlite3ExprDup(db, pSub->pWhere, 0); }else{ pWhere = 0; } @@ -2883,9 +2898,9 @@ static int flattenSubquery( pParent->pWhere = pWhere; substExpr(db, pParent->pHaving, iParent, pSub->pEList); pParent->pHaving = sqlite3ExprAnd(db, pParent->pHaving, - sqlite3ExprDup(db, pSub->pHaving)); + sqlite3ExprDup(db, pSub->pHaving, 0)); assert( pParent->pGroupBy==0 ); - pParent->pGroupBy = sqlite3ExprListDup(db, pSub->pGroupBy); + pParent->pGroupBy = sqlite3ExprListDup(db, pSub->pGroupBy, 0); }else{ substExpr(db, pParent->pWhere, iParent, pSub->pEList); pParent->pWhere = sqlite3ExprAnd(db, pParent->pWhere, pWhere); @@ -2934,7 +2949,8 @@ static u8 minMaxQuery(Select *p){ if( pEList->nExpr!=1 ) return WHERE_ORDERBY_NORMAL; pExpr = pEList->a[0].pExpr; - pEList = pExpr->pList; + if( ExprHasProperty(pExpr, EP_xIsSelect) ) return 0; + pEList = pExpr->x.pList; if( pExpr->op!=TK_AGG_FUNCTION || pEList==0 || pEList->nExpr!=1 ) return 0; if( pEList->a[0].pExpr->op!=TK_AGG_COLUMN ) return WHERE_ORDERBY_NORMAL; if( pExpr->token.n!=3 ) return WHERE_ORDERBY_NORMAL; @@ -2946,6 +2962,40 @@ static u8 minMaxQuery(Select *p){ return WHERE_ORDERBY_NORMAL; } +/* +** The select statement passed as the first argument is an aggregate query. +** The second argment is the associated aggregate-info object. This +** function tests if the SELECT is of the form: +** +** SELECT count(*) FROM +** +** where table is a database table, not a sub-select or view. If the query +** does match this pattern, then a pointer to the Table object representing +** is returned. Otherwise, 0 is returned. +*/ +static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){ + Table *pTab; + Expr *pExpr; + + assert( !p->pGroupBy ); + + if( p->pWhere || p->pEList->nExpr!=1 + || p->pSrc->nSrc!=1 || p->pSrc->a[0].pSelect + ){ + return 0; + } + pTab = p->pSrc->a[0].pTab; + pExpr = p->pEList->a[0].pExpr; + assert( pTab && !pTab->pSelect && pExpr ); + + if( IsVirtual(pTab) ) return 0; + if( pExpr->op!=TK_AGG_FUNCTION ) return 0; + if( (pAggInfo->aFunc[0].pFunc->flags&SQLITE_FUNC_COUNT)==0 ) return 0; + if( pExpr->flags&EP_Distinct ) return 0; + + return pTab; +} + /* ** If the source-list item passed as an argument was augmented with an ** INDEXED BY clause, then try to locate the specified index. If there @@ -3039,7 +3089,7 @@ static int selectExpander(Walker *pWalker, Select *p){ sqlite3WalkSelect(pWalker, pSel); pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table)); if( pTab==0 ) return WRC_Abort; - pTab->db = db; + pTab->dbMem = db->lookaside.bEnabled ? db : 0; pTab->nRef = 1; pTab->zName = sqlite3MPrintf(db, "sqlite_subquery_%p_", (void*)pTab); while( pSel->pPrior ){ pSel = pSel->pPrior; } @@ -3065,7 +3115,7 @@ static int selectExpander(Walker *pWalker, Select *p){ ** in the inner view. */ if( pFrom->pSelect==0 ){ - pFrom->pSelect = sqlite3SelectDup(db, pTab->pSelect); + pFrom->pSelect = sqlite3SelectDup(db, pTab->pSelect, 0); sqlite3WalkSelect(pWalker, pFrom->pSelect); } } @@ -3364,12 +3414,13 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){ sqlite3VdbeAddOp2(v, OP_Null, 0, pFunc->iMem); if( pFunc->iDistinct>=0 ){ Expr *pE = pFunc->pExpr; - if( pE->pList==0 || pE->pList->nExpr!=1 ){ + assert( !ExprHasProperty(pE, EP_xIsSelect) ); + if( pE->x.pList==0 || pE->x.pList->nExpr!=1 ){ sqlite3ErrorMsg(pParse, "DISTINCT aggregates must have exactly one " "argument"); pFunc->iDistinct = -1; }else{ - KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->pList); + KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->x.pList); sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0, (char*)pKeyInfo, P4_KEYINFO_HANDOFF); } @@ -3386,7 +3437,8 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){ int i; struct AggInfo_func *pF; for(i=0, pF=pAggInfo->aFunc; inFunc; i++, pF++){ - ExprList *pList = pF->pExpr->pList; + ExprList *pList = pF->pExpr->x.pList; + assert( !ExprHasProperty(pF->pExpr, EP_xIsSelect) ); sqlite3VdbeAddOp4(v, OP_AggFinal, pF->iMem, pList ? pList->nExpr : 0, 0, (void*)pF->pFunc, P4_FUNCDEF); } @@ -3407,7 +3459,8 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){ int nArg; int addrNext = 0; int regAgg; - ExprList *pList = pF->pExpr->pList; + ExprList *pList = pF->pExpr->x.pList; + assert( !ExprHasProperty(pF->pExpr, EP_xIsSelect) ); if( pList ){ nArg = pList->nExpr; regAgg = sqlite3GetTempRange(pParse, nArg); @@ -3657,7 +3710,7 @@ int sqlite3Select( ** GROUP BY might use an index, DISTINCT never does. */ if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct && !p->pGroupBy ){ - p->pGroupBy = sqlite3ExprListDup(db, p->pEList); + p->pGroupBy = sqlite3ExprListDup(db, p->pEList, 0); pGroupBy = p->pGroupBy; p->selFlags &= ~SF_Distinct; isDistinct = 0; @@ -3780,7 +3833,8 @@ int sqlite3Select( } sAggInfo.nAccumulator = sAggInfo.nColumn; for(i=0; ipList); + assert( !ExprHasProperty(sAggInfo.aFunc[i].pExpr, EP_xIsSelect) ); + sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->x.pList); } if( db->mallocFailed ) goto select_end; @@ -3987,68 +4041,127 @@ int sqlite3Select( } /* endif pGroupBy */ else { - ExprList *pMinMax = 0; ExprList *pDel = 0; - u8 flag; +#ifndef SQLITE_OMIT_BTREECOUNT + Table *pTab; + if( (pTab = isSimpleCount(p, &sAggInfo))!=0 ){ + /* If isSimpleCount() returns a pointer to a Table structure, then + ** the SQL statement is of the form: + ** + ** SELECT count(*) FROM + ** + ** where the Table structure returned represents table . + ** + ** This statement is so common that it is optimized specially. The + ** OP_Count instruction is executed either on the intkey table that + ** contains the data for table or on one of its indexes. It + ** is better to execute the op on an index, as indexes are almost + ** always spread across less pages than their corresponding tables. + */ + const int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); + const int iCsr = pParse->nTab++; /* Cursor to scan b-tree */ + Index *pIdx; /* Iterator variable */ + KeyInfo *pKeyInfo = 0; /* Keyinfo for scanned index */ + Index *pBest = 0; /* Best index found so far */ + int iRoot = pTab->tnum; /* Root page of scanned b-tree */ - /* Check if the query is of one of the following forms: - ** - ** SELECT min(x) FROM ... - ** SELECT max(x) FROM ... - ** - ** If it is, then ask the code in where.c to attempt to sort results - ** as if there was an "ORDER ON x" or "ORDER ON x DESC" clause. - ** If where.c is able to produce results sorted in this order, then - ** add vdbe code to break out of the processing loop after the - ** first iteration (since the first iteration of the loop is - ** guaranteed to operate on the row with the minimum or maximum - ** value of x, the only row required). - ** - ** A special flag must be passed to sqlite3WhereBegin() to slightly - ** modify behaviour as follows: - ** - ** + If the query is a "SELECT min(x)", then the loop coded by - ** where.c should not iterate over any values with a NULL value - ** for x. - ** - ** + The optimizer code in where.c (the thing that decides which - ** index or indices to use) should place a different priority on - ** satisfying the 'ORDER BY' clause than it does in other cases. - ** Refer to code and comments in where.c for details. - */ - flag = minMaxQuery(p); - if( flag ){ - pDel = pMinMax = sqlite3ExprListDup(db, p->pEList->a[0].pExpr->pList); - if( pMinMax && !db->mallocFailed ){ - pMinMax->a[0].sortOrder = flag!=WHERE_ORDERBY_MIN ?1:0; - pMinMax->a[0].pExpr->op = TK_COLUMN; + sqlite3CodeVerifySchema(pParse, iDb); + sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); + + /* Search for the index that has the least amount of columns. If + ** there is such an index, and it has less columns than the table + ** does, then we can assume that it consumes less space on disk and + ** will therefore be cheaper to scan to determine the query result. + ** In this case set iRoot to the root page number of the index b-tree + ** and pKeyInfo to the KeyInfo structure required to navigate the + ** index. + ** + ** In practice the KeyInfo structure will not be used. It is only + ** passed to keep OP_OpenRead happy. + */ + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + if( !pBest || pIdx->nColumnnColumn ){ + pBest = pIdx; + } } + if( pBest && pBest->nColumnnCol ){ + iRoot = pBest->tnum; + pKeyInfo = sqlite3IndexKeyinfo(pParse, pBest); + } + + /* Open a read-only cursor, execute the OP_Count, close the cursor. */ + sqlite3VdbeAddOp3(v, OP_OpenRead, iCsr, iRoot, iDb); + if( pKeyInfo ){ + sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO_HANDOFF); + } + sqlite3VdbeAddOp2(v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem); + sqlite3VdbeAddOp1(v, OP_Close, iCsr); + }else +#endif /* SQLITE_OMIT_BTREECOUNT */ + { + /* Check if the query is of one of the following forms: + ** + ** SELECT min(x) FROM ... + ** SELECT max(x) FROM ... + ** + ** If it is, then ask the code in where.c to attempt to sort results + ** as if there was an "ORDER ON x" or "ORDER ON x DESC" clause. + ** If where.c is able to produce results sorted in this order, then + ** add vdbe code to break out of the processing loop after the + ** first iteration (since the first iteration of the loop is + ** guaranteed to operate on the row with the minimum or maximum + ** value of x, the only row required). + ** + ** A special flag must be passed to sqlite3WhereBegin() to slightly + ** modify behaviour as follows: + ** + ** + If the query is a "SELECT min(x)", then the loop coded by + ** where.c should not iterate over any values with a NULL value + ** for x. + ** + ** + The optimizer code in where.c (the thing that decides which + ** index or indices to use) should place a different priority on + ** satisfying the 'ORDER BY' clause than it does in other cases. + ** Refer to code and comments in where.c for details. + */ + ExprList *pMinMax = 0; + u8 flag = minMaxQuery(p); + if( flag ){ + assert( !ExprHasProperty(p->pEList->a[0].pExpr, EP_xIsSelect) ); + pMinMax = sqlite3ExprListDup(db, p->pEList->a[0].pExpr->x.pList,0); + pDel = pMinMax; + if( pMinMax && !db->mallocFailed ){ + pMinMax->a[0].sortOrder = flag!=WHERE_ORDERBY_MIN ?1:0; + pMinMax->a[0].pExpr->op = TK_COLUMN; + } + } + + /* This case runs if the aggregate has no GROUP BY clause. The + ** processing is much simpler since there is only a single row + ** of output. + */ + resetAccumulator(pParse, &sAggInfo); + pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax, flag, 0); + if( pWInfo==0 ){ + sqlite3ExprListDelete(db, pDel); + goto select_end; + } + updateAccumulator(pParse, &sAggInfo); + if( !pMinMax && flag ){ + sqlite3VdbeAddOp2(v, OP_Goto, 0, pWInfo->iBreak); + VdbeComment((v, "%s() by index", + (flag==WHERE_ORDERBY_MIN?"min":"max"))); + } + sqlite3WhereEnd(pWInfo); + finalizeAggFunctions(pParse, &sAggInfo); } - /* This case runs if the aggregate has no GROUP BY clause. The - ** processing is much simpler since there is only a single row - ** of output. - */ - resetAccumulator(pParse, &sAggInfo); - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax, flag, 0); - if( pWInfo==0 ){ - sqlite3ExprListDelete(db, pDel); - goto select_end; - } - updateAccumulator(pParse, &sAggInfo); - if( !pMinMax && flag ){ - sqlite3VdbeAddOp2(v, OP_Goto, 0, pWInfo->iBreak); - VdbeComment((v, "%s() by index",(flag==WHERE_ORDERBY_MIN?"min":"max"))); - } - sqlite3WhereEnd(pWInfo); - finalizeAggFunctions(pParse, &sAggInfo); pOrderBy = 0; if( pHaving ){ sqlite3ExprIfFalse(pParse, pHaving, addrEnd, SQLITE_JUMPIFNULL); } selectInnerLoop(pParse, p, p->pEList, 0, 0, 0, -1, pDest, addrEnd, addrEnd); - sqlite3ExprListDelete(db, pDel); } sqlite3VdbeResolveLabel(v, addrEnd); diff --git a/src/shell.c b/src/shell.c index 93e4816..d58f151 100644 --- a/src/shell.c +++ b/src/shell.c @@ -12,7 +12,7 @@ ** This file contains code to implement the "sqlite" command line ** utility for accessing SQLite databases. ** -** $Id: shell.c,v 1.201 2009/02/04 22:46:47 drh Exp $ +** $Id: shell.c,v 1.207 2009/03/16 10:59:44 drh Exp $ */ #if defined(_WIN32) || defined(WIN32) /* This needs to come before any includes for MSVC compiler */ @@ -120,6 +120,861 @@ static void endTimer(void){ */ #define UNUSED_PARAMETER(x) (void)(x) + +/************************************************************************** +*************************************************************************** +** Begin genfkey logic. +*/ +#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined SQLITE_OMIT_SUBQUERY + +#define GENFKEY_ERROR 1 +#define GENFKEY_DROPTRIGGER 2 +#define GENFKEY_CREATETRIGGER 3 +static int genfkey_create_triggers(sqlite3 *, const char *, void *, + int (*)(void *, int, const char *) +); + +struct GenfkeyCb { + void *pCtx; + int eType; + int (*xData)(void *, int, const char *); +}; +typedef struct GenfkeyCb GenfkeyCb; + +/* The code in this file defines a sqlite3 virtual-table module that +** provides a read-only view of the current database schema. There is one +** row in the schema table for each column in the database schema. +*/ +#define SCHEMA \ +"CREATE TABLE x(" \ + "database," /* Name of database (i.e. main, temp etc.) */ \ + "tablename," /* Name of table */ \ + "cid," /* Column number (from left-to-right, 0 upward) */ \ + "name," /* Column name */ \ + "type," /* Specified type (i.e. VARCHAR(32)) */ \ + "not_null," /* Boolean. True if NOT NULL was specified */ \ + "dflt_value," /* Default value for this column */ \ + "pk" /* True if this column is part of the primary key */ \ +")" + +#define SCHEMA2 \ +"CREATE TABLE x(" \ + "database," /* Name of database (i.e. main, temp etc.) */ \ + "from_tbl," /* Name of table */ \ + "fkid," \ + "seq," \ + "to_tbl," \ + "from_col," \ + "to_col," \ + "on_update," \ + "on_delete," \ + "match" \ +")" + +#define SCHEMA3 \ +"CREATE TABLE x(" \ + "database," /* Name of database (i.e. main, temp etc.) */ \ + "tablename," /* Name of table */ \ + "seq," \ + "name," \ + "isunique" \ +")" + +#define SCHEMA4 \ +"CREATE TABLE x(" \ + "database," /* Name of database (i.e. main, temp etc.) */ \ + "indexname," /* Name of table */ \ + "seqno," \ + "cid," \ + "name" \ +")" + +#define SCHEMA5 \ +"CREATE TABLE x(" \ + "database," /* Name of database (i.e. main, temp etc.) */ \ + "triggername," /* Name of trigger */ \ + "dummy" /* Unused */ \ +")" + +typedef struct SchemaTable SchemaTable; +struct SchemaTable { + const char *zName; + const char *zObject; + const char *zPragma; + const char *zSchema; +} aSchemaTable[] = { + { "table_info", "table", "PRAGMA %Q.table_info(%Q)", SCHEMA }, + { "foreign_key_list", "table", "PRAGMA %Q.foreign_key_list(%Q)", SCHEMA2 }, + { "index_list", "table", "PRAGMA %Q.index_list(%Q)", SCHEMA3 }, + { "index_info", "index", "PRAGMA %Q.index_info(%Q)", SCHEMA4 }, + { "trigger_list", "trigger", "SELECT 1", SCHEMA5 }, + { 0, 0, 0, 0 } +}; + +typedef struct schema_vtab schema_vtab; +typedef struct schema_cursor schema_cursor; + +/* A schema table object */ +struct schema_vtab { + sqlite3_vtab base; + sqlite3 *db; + SchemaTable *pType; +}; + +/* A schema table cursor object */ +struct schema_cursor { + sqlite3_vtab_cursor base; + sqlite3_stmt *pDbList; + sqlite3_stmt *pTableList; + sqlite3_stmt *pColumnList; + int rowid; +}; + +/* +** Table destructor for the schema module. +*/ +static int schemaDestroy(sqlite3_vtab *pVtab){ + sqlite3_free(pVtab); + return 0; +} + +/* +** Table constructor for the schema module. +*/ +static int schemaCreate( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr +){ + int rc = SQLITE_NOMEM; + schema_vtab *pVtab; + SchemaTable *pType = &aSchemaTable[0]; + + UNUSED_PARAMETER(pzErr); + if( argc>3 ){ + int i; + pType = 0; + for(i=0; aSchemaTable[i].zName; i++){ + if( 0==strcmp(argv[3], aSchemaTable[i].zName) ){ + pType = &aSchemaTable[i]; + } + } + if( !pType ){ + return SQLITE_ERROR; + } + } + + pVtab = sqlite3_malloc(sizeof(schema_vtab)); + if( pVtab ){ + memset(pVtab, 0, sizeof(schema_vtab)); + pVtab->db = (sqlite3 *)pAux; + pVtab->pType = pType; + rc = sqlite3_declare_vtab(db, pType->zSchema); + } + *ppVtab = (sqlite3_vtab *)pVtab; + return rc; +} + +/* +** Open a new cursor on the schema table. +*/ +static int schemaOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ + int rc = SQLITE_NOMEM; + schema_cursor *pCur; + UNUSED_PARAMETER(pVTab); + pCur = sqlite3_malloc(sizeof(schema_cursor)); + if( pCur ){ + memset(pCur, 0, sizeof(schema_cursor)); + *ppCursor = (sqlite3_vtab_cursor *)pCur; + rc = SQLITE_OK; + } + return rc; +} + +/* +** Close a schema table cursor. +*/ +static int schemaClose(sqlite3_vtab_cursor *cur){ + schema_cursor *pCur = (schema_cursor *)cur; + sqlite3_finalize(pCur->pDbList); + sqlite3_finalize(pCur->pTableList); + sqlite3_finalize(pCur->pColumnList); + sqlite3_free(pCur); + return SQLITE_OK; +} + +static void columnToResult(sqlite3_context *ctx, sqlite3_stmt *pStmt, int iCol){ + switch( sqlite3_column_type(pStmt, iCol) ){ + case SQLITE_NULL: + sqlite3_result_null(ctx); + break; + case SQLITE_INTEGER: + sqlite3_result_int64(ctx, sqlite3_column_int64(pStmt, iCol)); + break; + case SQLITE_FLOAT: + sqlite3_result_double(ctx, sqlite3_column_double(pStmt, iCol)); + break; + case SQLITE_TEXT: { + const char *z = (const char *)sqlite3_column_text(pStmt, iCol); + sqlite3_result_text(ctx, z, -1, SQLITE_TRANSIENT); + break; + } + } +} + +/* +** Retrieve a column of data. +*/ +static int schemaColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ + schema_cursor *pCur = (schema_cursor *)cur; + switch( i ){ + case 0: + columnToResult(ctx, pCur->pDbList, 1); + break; + case 1: + columnToResult(ctx, pCur->pTableList, 0); + break; + default: + columnToResult(ctx, pCur->pColumnList, i-2); + break; + } + return SQLITE_OK; +} + +/* +** Retrieve the current rowid. +*/ +static int schemaRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ + schema_cursor *pCur = (schema_cursor *)cur; + *pRowid = pCur->rowid; + return SQLITE_OK; +} + +static int finalize(sqlite3_stmt **ppStmt){ + int rc = sqlite3_finalize(*ppStmt); + *ppStmt = 0; + return rc; +} + +static int schemaEof(sqlite3_vtab_cursor *cur){ + schema_cursor *pCur = (schema_cursor *)cur; + return (pCur->pDbList ? 0 : 1); +} + +/* +** Advance the cursor to the next row. +*/ +static int schemaNext(sqlite3_vtab_cursor *cur){ + int rc = SQLITE_OK; + schema_cursor *pCur = (schema_cursor *)cur; + schema_vtab *pVtab = (schema_vtab *)(cur->pVtab); + char *zSql = 0; + + while( !pCur->pColumnList || SQLITE_ROW!=sqlite3_step(pCur->pColumnList) ){ + if( SQLITE_OK!=(rc = finalize(&pCur->pColumnList)) ) goto next_exit; + + while( !pCur->pTableList || SQLITE_ROW!=sqlite3_step(pCur->pTableList) ){ + if( SQLITE_OK!=(rc = finalize(&pCur->pTableList)) ) goto next_exit; + + assert(pCur->pDbList); + while( SQLITE_ROW!=sqlite3_step(pCur->pDbList) ){ + rc = finalize(&pCur->pDbList); + goto next_exit; + } + + /* Set zSql to the SQL to pull the list of tables from the + ** sqlite_master (or sqlite_temp_master) table of the database + ** identfied by the row pointed to by the SQL statement pCur->pDbList + ** (iterating through a "PRAGMA database_list;" statement). + */ + if( sqlite3_column_int(pCur->pDbList, 0)==1 ){ + zSql = sqlite3_mprintf( + "SELECT name FROM sqlite_temp_master WHERE type=%Q", + pVtab->pType->zObject + ); + }else{ + sqlite3_stmt *pDbList = pCur->pDbList; + zSql = sqlite3_mprintf( + "SELECT name FROM %Q.sqlite_master WHERE type=%Q", + sqlite3_column_text(pDbList, 1), pVtab->pType->zObject + ); + } + if( !zSql ){ + rc = SQLITE_NOMEM; + goto next_exit; + } + + rc = sqlite3_prepare(pVtab->db, zSql, -1, &pCur->pTableList, 0); + sqlite3_free(zSql); + if( rc!=SQLITE_OK ) goto next_exit; + } + + /* Set zSql to the SQL to the table_info pragma for the table currently + ** identified by the rows pointed to by statements pCur->pDbList and + ** pCur->pTableList. + */ + zSql = sqlite3_mprintf(pVtab->pType->zPragma, + sqlite3_column_text(pCur->pDbList, 1), + sqlite3_column_text(pCur->pTableList, 0) + ); + + if( !zSql ){ + rc = SQLITE_NOMEM; + goto next_exit; + } + rc = sqlite3_prepare(pVtab->db, zSql, -1, &pCur->pColumnList, 0); + sqlite3_free(zSql); + if( rc!=SQLITE_OK ) goto next_exit; + } + pCur->rowid++; + +next_exit: + /* TODO: Handle rc */ + return rc; +} + +/* +** Reset a schema table cursor. +*/ +static int schemaFilter( + sqlite3_vtab_cursor *pVtabCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv +){ + int rc; + schema_vtab *pVtab = (schema_vtab *)(pVtabCursor->pVtab); + schema_cursor *pCur = (schema_cursor *)pVtabCursor; + UNUSED_PARAMETER(idxNum); + UNUSED_PARAMETER(idxStr); + UNUSED_PARAMETER(argc); + UNUSED_PARAMETER(argv); + pCur->rowid = 0; + finalize(&pCur->pTableList); + finalize(&pCur->pColumnList); + finalize(&pCur->pDbList); + rc = sqlite3_prepare(pVtab->db,"SELECT 0, 'main'", -1, &pCur->pDbList, 0); + return (rc==SQLITE_OK ? schemaNext(pVtabCursor) : rc); +} + +/* +** Analyse the WHERE condition. +*/ +static int schemaBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + UNUSED_PARAMETER(tab); + UNUSED_PARAMETER(pIdxInfo); + return SQLITE_OK; +} + +/* +** A virtual table module that merely echos method calls into TCL +** variables. +*/ +static sqlite3_module schemaModule = { + 0, /* iVersion */ + schemaCreate, + schemaCreate, + schemaBestIndex, + schemaDestroy, + schemaDestroy, + schemaOpen, /* xOpen - open a cursor */ + schemaClose, /* xClose - close a cursor */ + schemaFilter, /* xFilter - configure scan constraints */ + schemaNext, /* xNext - advance a cursor */ + schemaEof, /* xEof */ + schemaColumn, /* xColumn - read data */ + schemaRowid, /* xRowid - read data */ + 0, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindMethod */ + 0, /* xRename */ +}; + +/* +** Extension load function. +*/ +static int installSchemaModule(sqlite3 *db, sqlite3 *sdb){ + sqlite3_create_module(db, "schema", &schemaModule, (void *)sdb); + return 0; +} + +/* +** sj(zValue, zJoin) +** +** The following block contains the implementation of an aggregate +** function that returns a string. Each time the function is stepped, +** it appends data to an internal buffer. When the aggregate is finalized, +** the contents of the buffer are returned. +** +** The first time the aggregate is stepped the buffer is set to a copy +** of the first argument. The second time and subsequent times it is +** stepped a copy of the second argument is appended to the buffer, then +** a copy of the first. +** +** Example: +** +** INSERT INTO t1(a) VALUES('1'); +** INSERT INTO t1(a) VALUES('2'); +** INSERT INTO t1(a) VALUES('3'); +** SELECT sj(a, ', ') FROM t1; +** +** => "1, 2, 3" +** +*/ +struct StrBuffer { + char *zBuf; +}; +typedef struct StrBuffer StrBuffer; +static void joinFinalize(sqlite3_context *context){ + StrBuffer *p; + p = (StrBuffer *)sqlite3_aggregate_context(context, sizeof(StrBuffer)); + sqlite3_result_text(context, p->zBuf, -1, SQLITE_TRANSIENT); + sqlite3_free(p->zBuf); +} +static void joinStep( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + StrBuffer *p; + UNUSED_PARAMETER(argc); + p = (StrBuffer *)sqlite3_aggregate_context(context, sizeof(StrBuffer)); + if( p->zBuf==0 ){ + p->zBuf = sqlite3_mprintf("%s", sqlite3_value_text(argv[0])); + }else{ + char *zTmp = p->zBuf; + p->zBuf = sqlite3_mprintf("%s%s%s", + zTmp, sqlite3_value_text(argv[1]), sqlite3_value_text(argv[0]) + ); + sqlite3_free(zTmp); + } +} + +/* +** dq(zString) +** +** This scalar function accepts a single argument and interprets it as +** a text value. The return value is the argument enclosed in double +** quotes. If any double quote characters are present in the argument, +** these are escaped. +** +** dq('the raven "Nevermore."') == '"the raven ""Nevermore."""' +*/ +static void doublequote( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + int ii; + char *zOut; + char *zCsr; + const char *zIn = (const char *)sqlite3_value_text(argv[0]); + int nIn = sqlite3_value_bytes(argv[0]); + + UNUSED_PARAMETER(argc); + zOut = sqlite3_malloc(nIn*2+3); + zCsr = zOut; + *zCsr++ = '"'; + for(ii=0; iinMalloc ){ + nMalloc = 16 + (nOut+nCopy)*2; + zOut = (char *)sqlite3_realloc(zOut, nMalloc); + } + assert( nMalloc>=(nOut+nCopy) ); + memcpy(&zOut[nOut], zCopy, nCopy); + i += nReplace; + nOut += nCopy; + } + + sqlite3_result_text(context, zOut, nOut, SQLITE_TRANSIENT); + sqlite3_free(zOut); +} + +/* +** A callback for sqlite3_exec() invokes the callback specified by the +** GenfkeyCb structure pointed to by the void* passed as the first argument. +*/ +static int invokeCallback(void *p, int nArg, char **azArg, char **azCol){ + GenfkeyCb *pCb = (GenfkeyCb *)p; + UNUSED_PARAMETER(nArg); + UNUSED_PARAMETER(azCol); + return pCb->xData(pCb->pCtx, pCb->eType, azArg[0]); +} + +int detectSchemaProblem( + sqlite3 *db, /* Database connection */ + const char *zMessage, /* English language error message */ + const char *zSql, /* SQL statement to run */ + GenfkeyCb *pCb +){ + sqlite3_stmt *pStmt; + int rc; + rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); + if( rc!=SQLITE_OK ){ + return rc; + } + while( SQLITE_ROW==sqlite3_step(pStmt) ){ + char *zDel; + int iFk = sqlite3_column_int(pStmt, 0); + const char *zTab = (const char *)sqlite3_column_text(pStmt, 1); + zDel = sqlite3_mprintf("Error in table %s: %s", zTab, zMessage); + rc = pCb->xData(pCb->pCtx, pCb->eType, zDel); + sqlite3_free(zDel); + if( rc!=SQLITE_OK ) return rc; + zDel = sqlite3_mprintf( + "DELETE FROM temp.fkey WHERE from_tbl = %Q AND fkid = %d" + , zTab, iFk + ); + sqlite3_exec(db, zDel, 0, 0, 0); + sqlite3_free(zDel); + } + sqlite3_finalize(pStmt); + return SQLITE_OK; +} + +/* +** Create and populate temporary table "fkey". +*/ +static int populateTempTable(sqlite3 *db, GenfkeyCb *pCallback){ + int rc; + + rc = sqlite3_exec(db, + "CREATE VIRTUAL TABLE temp.v_fkey USING schema(foreign_key_list);" + "CREATE VIRTUAL TABLE temp.v_col USING schema(table_info);" + "CREATE VIRTUAL TABLE temp.v_idxlist USING schema(index_list);" + "CREATE VIRTUAL TABLE temp.v_idxinfo USING schema(index_info);" + "CREATE VIRTUAL TABLE temp.v_triggers USING schema(trigger_list);" + "CREATE TABLE temp.fkey AS " + "SELECT from_tbl, to_tbl, fkid, from_col, to_col, on_update, on_delete " + "FROM temp.v_fkey WHERE database = 'main';" + , 0, 0, 0 + ); + if( rc!=SQLITE_OK ) return rc; + + rc = detectSchemaProblem(db, "foreign key columns do not exist", + "SELECT fkid, from_tbl " + "FROM temp.fkey " + "WHERE to_col IS NOT NULL AND NOT EXISTS (SELECT 1 " + "FROM temp.v_col WHERE tablename=to_tbl AND name==to_col" + ")", pCallback + ); + if( rc!=SQLITE_OK ) return rc; + + /* At this point the temp.fkey table is mostly populated. If any foreign + ** keys were specified so that they implicitly refer to they primary + ** key of the parent table, the "to_col" values of the temp.fkey rows + ** are still set to NULL. + ** + ** This is easily fixed for single column primary keys, but not for + ** composites. With a composite primary key, there is no way to reliably + ** query sqlite for the order in which the columns that make up the + ** composite key were declared i.e. there is no way to tell if the + ** schema actually contains "PRIMARY KEY(a, b)" or "PRIMARY KEY(b, a)". + ** Therefore, this case is not handled. The following function call + ** detects instances of this case. + */ + rc = detectSchemaProblem(db, "implicit mapping to composite primary key", + "SELECT fkid, from_tbl " + "FROM temp.fkey " + "WHERE to_col IS NULL " + "GROUP BY fkid, from_tbl HAVING count(*) > 1", pCallback + ); + if( rc!=SQLITE_OK ) return rc; + + /* Detect attempts to implicitly map to the primary key of a table + ** that has no primary key column. + */ + rc = detectSchemaProblem(db, "implicit mapping to non-existant primary key", + "SELECT fkid, from_tbl " + "FROM temp.fkey " + "WHERE to_col IS NULL AND NOT EXISTS " + "(SELECT 1 FROM temp.v_col WHERE pk AND tablename = temp.fkey.to_tbl)" + , pCallback + ); + if( rc!=SQLITE_OK ) return rc; + + /* Fix all the implicit primary key mappings in the temp.fkey table. */ + rc = sqlite3_exec(db, + "UPDATE temp.fkey SET to_col = " + "(SELECT name FROM temp.v_col WHERE pk AND tablename=temp.fkey.to_tbl)" + " WHERE to_col IS NULL;" + , 0, 0, 0 + ); + if( rc!=SQLITE_OK ) return rc; + + /* Now check that all all parent keys are either primary keys or + ** subject to a unique constraint. + */ + rc = sqlite3_exec(db, + "CREATE TABLE temp.idx2 AS SELECT " + "il.tablename AS tablename," + "ii.indexname AS indexname," + "ii.name AS col " + "FROM temp.v_idxlist AS il, temp.v_idxinfo AS ii " + "WHERE il.isunique AND il.database='main' AND ii.indexname = il.name;" + "INSERT INTO temp.idx2 " + "SELECT tablename, 'pk', name FROM temp.v_col WHERE pk;" + + "CREATE TABLE temp.idx AS SELECT " + "tablename, indexname, sj(dq(col),',') AS cols " + "FROM (SELECT * FROM temp.idx2 ORDER BY col) " + "GROUP BY tablename, indexname;" + + "CREATE TABLE temp.fkey2 AS SELECT " + "fkid, from_tbl, to_tbl, sj(dq(to_col),',') AS cols " + "FROM (SELECT * FROM temp.fkey ORDER BY to_col) " + "GROUP BY fkid, from_tbl;" + + "CREATE TABLE temp.triggers AS SELECT " + "triggername FROM temp.v_triggers WHERE database='main' AND " + "triggername LIKE 'genfkey%';" + , 0, 0, 0 + ); + if( rc!=SQLITE_OK ) return rc; + rc = detectSchemaProblem(db, "foreign key is not unique", + "SELECT fkid, from_tbl " + "FROM temp.fkey2 " + "WHERE NOT EXISTS (SELECT 1 " + "FROM temp.idx WHERE tablename=to_tbl AND fkey2.cols==idx.cols" + ")", pCallback + ); + if( rc!=SQLITE_OK ) return rc; + + return rc; +} + +#define GENFKEY_ERROR 1 +#define GENFKEY_DROPTRIGGER 2 +#define GENFKEY_CREATETRIGGER 3 +static int genfkey_create_triggers( + sqlite3 *sdb, /* Connection to read schema from */ + const char *zDb, /* Name of db to read ("main", "temp") */ + void *pCtx, /* Context pointer to pass to xData */ + int (*xData)(void *, int, const char *) +){ + const char *zSql = + "SELECT multireplace('" + + "-- Triggers for foreign key mapping:\n" + "--\n" + "-- /from_readable/ REFERENCES /to_readable/\n" + "-- on delete /on_delete/\n" + "-- on update /on_update/\n" + "--\n" + + /* The "BEFORE INSERT ON " trigger. This trigger's job is to + ** throw an exception if the user tries to insert a row into the + ** referencing table for which there is no corresponding row in + ** the referenced table. + */ + "CREATE TRIGGER /name/_insert_referencing BEFORE INSERT ON /tbl/ WHEN \n" + " /key_notnull/ AND NOT EXISTS (SELECT 1 FROM /ref/ WHERE /cond1/)\n" + "BEGIN\n" + " SELECT RAISE(ABORT, ''constraint failed'');\n" + "END;\n" + + /* The "BEFORE UPDATE ON " trigger. This trigger's job + ** is to throw an exception if the user tries to update a row in the + ** referencing table causing it to correspond to no row in the + ** referenced table. + */ + "CREATE TRIGGER /name/_update_referencing BEFORE\n" + " UPDATE OF /rkey_list/ ON /tbl/ WHEN \n" + " /key_notnull/ AND \n" + " NOT EXISTS (SELECT 1 FROM /ref/ WHERE /cond1/)\n" + "BEGIN\n" + " SELECT RAISE(ABORT, ''constraint failed'');\n" + "END;\n" + + + /* The "BEFORE DELETE ON " trigger. This trigger's job + ** is to detect when a row is deleted from the referenced table to + ** which rows in the referencing table correspond. The action taken + ** depends on the value of the 'ON DELETE' clause. + */ + "CREATE TRIGGER /name/_delete_referenced BEFORE DELETE ON /ref/ WHEN\n" + " EXISTS (SELECT 1 FROM /tbl/ WHERE /cond2/)\n" + "BEGIN\n" + " /delete_action/\n" + "END;\n" + + /* The "BEFORE DELETE ON " trigger. This trigger's job + ** is to detect when the key columns of a row in the referenced table + ** to which one or more rows in the referencing table correspond are + ** updated. The action taken depends on the value of the 'ON UPDATE' + ** clause. + */ + "CREATE TRIGGER /name/_update_referenced AFTER\n" + " UPDATE OF /fkey_list/ ON /ref/ WHEN \n" + " EXISTS (SELECT 1 FROM /tbl/ WHERE /cond2/)\n" + "BEGIN\n" + " /update_action/\n" + "END;\n" + "'" + + /* These are used in the SQL comment written above each set of triggers */ + ", '/from_readable/', from_tbl || '(' || sj(from_col, ', ') || ')'" + ", '/to_readable/', to_tbl || '(' || sj(to_col, ', ') || ')'" + ", '/on_delete/', on_delete" + ", '/on_update/', on_update" + + ", '/name/', 'genfkey' || min(rowid)" + ", '/tbl/', dq(from_tbl)" + ", '/ref/', dq(to_tbl)" + ", '/key_notnull/', sj('new.' || dq(from_col) || ' IS NOT NULL', ' AND ')" + + ", '/fkey_list/', sj(to_col, ', ')" + ", '/rkey_list/', sj(from_col, ', ')" + + ", '/cond1/', sj(multireplace('new./from/ == /to/'" + ", '/from/', dq(from_col)" + ", '/to/', dq(to_col)" + "), ' AND ')" + ", '/cond2/', sj(multireplace('old./to/ == /from/'" + ", '/from/', dq(from_col)" + ", '/to/', dq(to_col)" + "), ' AND ')" + + ", '/update_action/', CASE on_update " + "WHEN 'SET NULL' THEN " + "multireplace('UPDATE /tbl/ SET /setlist/ WHERE /where/;' " + ", '/setlist/', sj(from_col||' = NULL',', ')" + ", '/tbl/', dq(from_tbl)" + ", '/where/', sj(from_col||' = old.'||dq(to_col),' AND ')" + ")" + "WHEN 'CASCADE' THEN " + "multireplace('UPDATE /tbl/ SET /setlist/ WHERE /where/;' " + ", '/setlist/', sj(dq(from_col)||' = new.'||dq(to_col),', ')" + ", '/tbl/', dq(from_tbl)" + ", '/where/', sj(dq(from_col)||' = old.'||dq(to_col),' AND ')" + ")" + "ELSE " + " 'SELECT RAISE(ABORT, ''constraint failed'');'" + "END " + + ", '/delete_action/', CASE on_delete " + "WHEN 'SET NULL' THEN " + "multireplace('UPDATE /tbl/ SET /setlist/ WHERE /where/;' " + ", '/setlist/', sj(from_col||' = NULL',', ')" + ", '/tbl/', dq(from_tbl)" + ", '/where/', sj(from_col||' = old.'||dq(to_col),' AND ')" + ")" + "WHEN 'CASCADE' THEN " + "multireplace('DELETE FROM /tbl/ WHERE /where/;' " + ", '/tbl/', dq(from_tbl)" + ", '/where/', sj(dq(from_col)||' = old.'||dq(to_col),' AND ')" + ")" + "ELSE " + " 'SELECT RAISE(ABORT, ''constraint failed'');'" + "END " + + ") FROM temp.fkey " + "GROUP BY from_tbl, fkid" + ; + + int rc; + const int enc = SQLITE_UTF8; + sqlite3 *db = 0; + + GenfkeyCb cb; + cb.xData = xData; + cb.pCtx = pCtx; + + UNUSED_PARAMETER(zDb); + + /* Open the working database handle. */ + rc = sqlite3_open(":memory:", &db); + if( rc!=SQLITE_OK ) goto genfkey_exit; + + /* Create the special scalar and aggregate functions used by this program. */ + sqlite3_create_function(db, "dq", 1, enc, 0, doublequote, 0, 0); + sqlite3_create_function(db, "multireplace", -1, enc, db, multireplace, 0, 0); + sqlite3_create_function(db, "sj", 2, enc, 0, 0, joinStep, joinFinalize); + + /* Install the "schema" virtual table module */ + installSchemaModule(db, sdb); + + /* Create and populate a temp table with the information required to + ** build the foreign key triggers. See function populateTempTable() + ** for details. + */ + cb.eType = GENFKEY_ERROR; + rc = populateTempTable(db, &cb); + if( rc!=SQLITE_OK ) goto genfkey_exit; + + /* Unless the --no-drop option was specified, generate DROP TRIGGER + ** statements to drop any triggers in the database generated by a + ** previous run of this program. + */ + cb.eType = GENFKEY_DROPTRIGGER; + rc = sqlite3_exec(db, + "SELECT 'DROP TRIGGER main.' || dq(triggername) || ';' FROM triggers" + ,invokeCallback, (void *)&cb, 0 + ); + if( rc!=SQLITE_OK ) goto genfkey_exit; + + /* Run the main query to create the trigger definitions. */ + cb.eType = GENFKEY_CREATETRIGGER; + rc = sqlite3_exec(db, zSql, invokeCallback, (void *)&cb, 0); + if( rc!=SQLITE_OK ) goto genfkey_exit; + +genfkey_exit: + sqlite3_close(db); + return rc; +} + + +#endif +/* End genfkey logic. */ +/*************************************************************************/ +/*************************************************************************/ + /* ** If the following flag is set, then command execution stops ** at an error if we are not interactive. @@ -926,6 +1781,62 @@ static int run_schema_dump_query( return rc; } +#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_SUBQUERY) +struct GenfkeyCmd { + sqlite3 *db; /* Database handle */ + struct callback_data *pCb; /* Callback data */ + int isIgnoreErrors; /* True for --ignore-errors */ + int isExec; /* True for --exec */ + int isNoDrop; /* True for --no-drop */ + int nErr; /* Number of errors seen so far */ +}; +typedef struct GenfkeyCmd GenfkeyCmd; + +static int genfkeyParseArgs(GenfkeyCmd *p, char **azArg, int nArg){ + int ii; + memset(p, 0, sizeof(GenfkeyCmd)); + + for(ii=0; ii2 && n<10 && 0==strncmp(azArg[ii], "--no-drop", n) ){ + p->isNoDrop = 1; + }else if( n>2 && n<16 && 0==strncmp(azArg[ii], "--ignore-errors", n) ){ + p->isIgnoreErrors = 1; + }else if( n>2 && n<7 && 0==strncmp(azArg[ii], "--exec", n) ){ + p->isExec = 1; + }else{ + fprintf(stderr, "unknown option: %s\n", azArg[ii]); + return -1; + } + } + + return SQLITE_OK; +} + +static int genfkeyCmdCb(void *pCtx, int eType, const char *z){ + GenfkeyCmd *p = (GenfkeyCmd *)pCtx; + if( eType==GENFKEY_ERROR && !p->isIgnoreErrors ){ + p->nErr++; + fprintf(stderr, "%s\n", z); + } + + if( p->nErr==0 && ( + (eType==GENFKEY_CREATETRIGGER) + || (eType==GENFKEY_DROPTRIGGER && !p->isNoDrop) + )){ + if( p->isExec ){ + sqlite3_exec(p->db, z, 0, 0, 0); + }else{ + char *zCol = "sql"; + callback((void *)p->pCb, 1, (char **)&z, (char **)&zCol); + } + } + + return SQLITE_OK; +} +#endif + /* ** Text of a help message */ @@ -937,6 +1848,14 @@ static char zHelp[] = ".echo ON|OFF Turn command echo on or off\n" ".exit Exit this program\n" ".explain ON|OFF Turn output mode suitable for EXPLAIN on or off.\n" +#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_SUBQUERY) + ".genfkey ?OPTIONS? Options are:\n" + " --no-drop: Do not drop old fkey triggers.\n" + " --ignore-errors: Ignore tables with fkey errors\n" + " --exec: Execute generated SQL immediately\n" + " See file tool/genfkey.README in the source \n" + " distribution for further information.\n" +#endif ".header(s) ON|OFF Turn display of headers on or off\n" ".help Show this message\n" ".import FILE TABLE Import data from FILE into TABLE\n" @@ -1240,6 +2159,17 @@ static int do_meta_command(char *zLine, struct callback_data *p){ } }else +#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_SUBQUERY) + if( c=='g' && strncmp(azArg[0], "genfkey", n)==0 ){ + GenfkeyCmd cmd; + if( 0==genfkeyParseArgs(&cmd, &azArg[1], nArg-1) ){ + cmd.db = p->db; + cmd.pCb = p; + genfkey_create_triggers(p->db, "main", (void *)&cmd, genfkeyCmdCb); + } + }else +#endif + if( c=='h' && (strncmp(azArg[0], "header", n)==0 || strncmp(azArg[0], "headers", n)==0 )&& nArg>1 ){ p->showHeader = booleanValue(azArg[1]); diff --git a/src/sqlite.h.in b/src/sqlite.h.in index a9447a2..967e785 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -30,7 +30,7 @@ ** the version number) and changes its name to "sqlite3.h" as ** part of the build process. ** -** @(#) $Id: sqlite.h.in,v 1.432 2009/02/12 17:07:35 drh Exp $ +** @(#) $Id: sqlite.h.in,v 1.440 2009/04/06 15:55:04 drh Exp $ */ #ifndef _SQLITE3_H_ #define _SQLITE3_H_ @@ -97,15 +97,7 @@ extern "C" { ** ** See also: [sqlite3_libversion()] and [sqlite3_libversion_number()]. ** -** INVARIANTS: -** -** {H10011} The SQLITE_VERSION #define in the sqlite3.h header file shall -** evaluate to a string literal that is the SQLite version -** with which the header file is associated. -** -** {H10014} The SQLITE_VERSION_NUMBER #define shall resolve to an integer -** with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z -** are the major version, minor version, and release number. +** Requirements: [H10011] [H10014] */ #define SQLITE_VERSION "--VERS--" #define SQLITE_VERSION_NUMBER --VERSION-NUMBER-- @@ -126,16 +118,7 @@ extern "C" { ** for use in DLLs since DLL users usually do not have direct access to string ** constants within the DLL. ** -** INVARIANTS: -** -** {H10021} The [sqlite3_libversion_number()] interface shall return -** an integer equal to [SQLITE_VERSION_NUMBER]. -** -** {H10022} The [sqlite3_version] string constant shall contain -** the text of the [SQLITE_VERSION] string. -** -** {H10023} The [sqlite3_libversion()] function shall return -** a pointer to the [sqlite3_version] string constant. +** Requirements: [H10021] [H10022] [H10023] */ SQLITE_EXTERN const char sqlite3_version[]; const char *sqlite3_libversion(void); @@ -171,13 +154,7 @@ int sqlite3_libversion_number(void); ** ** See the [threading mode] documentation for additional information. ** -** INVARIANTS: -** -** {H10101} The [sqlite3_threadsafe()] function shall return zero if -** and only if SQLite was compiled with mutexing code omitted. -** -** {H10102} The value returned by the [sqlite3_threadsafe()] function -** shall remain the same across calls to [sqlite3_config()]. +** Requirements: [H10101] [H10102] */ int sqlite3_threadsafe(void); @@ -207,13 +184,7 @@ typedef struct sqlite3 sqlite3; ** The sqlite_int64 and sqlite_uint64 types are supported for backwards ** compatibility only. ** -** INVARIANTS: -** -** {H10201} The [sqlite_int64] and [sqlite3_int64] type shall specify -** a 64-bit signed integer. -** -** {H10202} The [sqlite_uint64] and [sqlite3_uint64] type shall specify -** a 64-bit unsigned integer. +** Requirements: [H10201] [H10202] */ #ifdef SQLITE_INT64_TYPE typedef SQLITE_INT64_TYPE sqlite_int64; @@ -258,34 +229,13 @@ typedef sqlite_uint64 sqlite3_uint64; ** If [sqlite3_close()] is invoked while a transaction is open, ** the transaction is automatically rolled back. ** -** INVARIANTS: +** The C parameter to [sqlite3_close(C)] must be either a NULL +** pointer or an [sqlite3] object pointer obtained +** from [sqlite3_open()], [sqlite3_open16()], or +** [sqlite3_open_v2()], and not previously closed. ** -** {H12011} A successful call to [sqlite3_close(C)] shall destroy the -** [database connection] object C. -** -** {H12012} A successful call to [sqlite3_close(C)] shall return SQLITE_OK. -** -** {H12013} A successful call to [sqlite3_close(C)] shall release all -** memory and system resources associated with [database connection] -** C. -** -** {H12014} A call to [sqlite3_close(C)] on a [database connection] C that -** has one or more open [prepared statements] shall fail with -** an [SQLITE_BUSY] error code. -** -** {H12015} A call to [sqlite3_close(C)] where C is a NULL pointer shall -** be a harmless no-op returning SQLITE_OK. -** -** {H12019} When [sqlite3_close(C)] is invoked on a [database connection] C -** that has a pending transaction, the transaction shall be -** rolled back. -** -** ASSUMPTIONS: -** -** {A12016} The C parameter to [sqlite3_close(C)] must be either a NULL -** pointer or an [sqlite3] object pointer obtained -** from [sqlite3_open()], [sqlite3_open16()], or -** [sqlite3_open_v2()], and not previously closed. +** Requirements: +** [H12011] [H12012] [H12013] [H12014] [H12015] [H12019] */ int sqlite3_close(sqlite3 *); @@ -323,86 +273,22 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**); ** The sqlite3_exec() routine does nothing to the database that cannot be done ** by [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()]. ** -** INVARIANTS: +** The first parameter to [sqlite3_exec()] must be an valid and open +** [database connection]. ** -** {H12101} A successful invocation of [sqlite3_exec(D,S,C,A,E)] -** shall sequentially evaluate all of the UTF-8 encoded, -** semicolon-separated SQL statements in the zero-terminated -** string S within the context of the [database connection] D. +** The database connection must not be closed while +** [sqlite3_exec()] is running. ** -** {H12102} If the S parameter to [sqlite3_exec(D,S,C,A,E)] is NULL then -** the actions of the interface shall be the same as if the -** S parameter were an empty string. +** The calling function should use [sqlite3_free()] to free +** the memory that *errmsg is left pointing at once the error +** message is no longer needed. ** -** {H12104} The return value of [sqlite3_exec()] shall be [SQLITE_OK] if all -** SQL statements run successfully and to completion. +** The SQL statement text in the 2nd parameter to [sqlite3_exec()] +** must remain unchanged while [sqlite3_exec()] is running. ** -** {H12105} The return value of [sqlite3_exec()] shall be an appropriate -** non-zero [error code] if any SQL statement fails. -** -** {H12107} If one or more of the SQL statements handed to [sqlite3_exec()] -** return results and the 3rd parameter is not NULL, then -** the callback function specified by the 3rd parameter shall be -** invoked once for each row of result. -** -** {H12110} If the callback returns a non-zero value then [sqlite3_exec()] -** shall abort the SQL statement it is currently evaluating, -** skip all subsequent SQL statements, and return [SQLITE_ABORT]. -** -** {H12113} The [sqlite3_exec()] routine shall pass its 4th parameter through -** as the 1st parameter of the callback. -** -** {H12116} The [sqlite3_exec()] routine shall set the 2nd parameter of its -** callback to be the number of columns in the current row of -** result. -** -** {H12119} The [sqlite3_exec()] routine shall set the 3rd parameter of its -** callback to be an array of pointers to strings holding the -** values for each column in the current result set row as -** obtained from [sqlite3_column_text()]. -** -** {H12122} The [sqlite3_exec()] routine shall set the 4th parameter of its -** callback to be an array of pointers to strings holding the -** names of result columns as obtained from [sqlite3_column_name()]. -** -** {H12125} If the 3rd parameter to [sqlite3_exec()] is NULL then -** [sqlite3_exec()] shall silently discard query results. -** -** {H12131} If an error occurs while parsing or evaluating any of the SQL -** statements in the S parameter of [sqlite3_exec(D,S,C,A,E)] and if -** the E parameter is not NULL, then [sqlite3_exec()] shall store -** in *E an appropriate error message written into memory obtained -** from [sqlite3_malloc()]. -** -** {H12134} The [sqlite3_exec(D,S,C,A,E)] routine shall set the value of -** *E to NULL if E is not NULL and there are no errors. -** -** {H12137} The [sqlite3_exec(D,S,C,A,E)] function shall set the [error code] -** and message accessible via [sqlite3_errcode()], -** [sqlite3_extended_errcode()], -** [sqlite3_errmsg()], and [sqlite3_errmsg16()]. -** -** {H12138} If the S parameter to [sqlite3_exec(D,S,C,A,E)] is NULL or an -** empty string or contains nothing other than whitespace, comments, -** and/or semicolons, then results of [sqlite3_errcode()], -** [sqlite3_extended_errcode()], -** [sqlite3_errmsg()], and [sqlite3_errmsg16()] -** shall reset to indicate no errors. -** -** ASSUMPTIONS: -** -** {A12141} The first parameter to [sqlite3_exec()] must be an valid and open -** [database connection]. -** -** {A12142} The database connection must not be closed while -** [sqlite3_exec()] is running. -** -** {A12143} The calling function should use [sqlite3_free()] to free -** the memory that *errmsg is left pointing at once the error -** message is no longer needed. -** -** {A12145} The SQL statement text in the 2nd parameter to [sqlite3_exec()] -** must remain unchanged while [sqlite3_exec()] is running. +** Requirements: +** [H12101] [H12102] [H12104] [H12105] [H12107] [H12110] [H12113] [H12116] +** [H12119] [H12122] [H12125] [H12131] [H12134] [H12137] [H12138] */ int sqlite3_exec( sqlite3*, /* An open database */ @@ -478,19 +364,6 @@ int sqlite3_exec( ** ** The SQLITE_OK result code will never be extended. It will always ** be exactly zero. -** -** INVARIANTS: -** -** {H10223} The symbolic name for an extended result code shall contains -** a related primary result code as a prefix. -** -** {H10224} Primary result code names shall contain a single "_" character. -** -** {H10225} Extended result code names shall contain two or more "_" characters. -** -** {H10226} The numeric value of an extended result code shall contain the -** numeric value of its corresponding primary result code in -** its least significant 8 bits. */ #define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8)) #define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8)) @@ -509,6 +382,7 @@ int sqlite3_exec( #define SQLITE_IOERR_LOCK (SQLITE_IOERR | (15<<8)) #define SQLITE_IOERR_CLOSE (SQLITE_IOERR | (16<<8)) #define SQLITE_IOERR_DIR_CLOSE (SQLITE_IOERR | (17<<8)) +#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8) ) /* ** CAPI3REF: Flags For File Open Operations {H10230} @@ -587,8 +461,9 @@ int sqlite3_exec( ** ** When the SQLITE_SYNC_DATAONLY flag is used, it means that the ** sync operation only needs to flush data to mass storage. Inode -** information need not be flushed. The SQLITE_SYNC_NORMAL flag means -** to use normal fsync() semantics. The SQLITE_SYNC_FULL flag means +** information need not be flushed. If the lower four bits of the flag +** equal SQLITE_SYNC_NORMAL, that means to use normal fsync() semantics. +** If the lower four bits equal SQLITE_SYNC_FULL, that means ** to use Mac OS X style fullsync instead of fsync(). */ #define SQLITE_SYNC_NORMAL 0x00002 @@ -995,94 +870,10 @@ int sqlite3_os_end(void); ** If the option is unknown or SQLite is unable to set the option ** then this routine returns a non-zero [error code]. ** -** INVARIANTS: -** -** {H14103} A successful invocation of [sqlite3_config()] shall return -** [SQLITE_OK]. -** -** {H14106} The [sqlite3_config()] interface shall return [SQLITE_MISUSE] -** if it is invoked in between calls to [sqlite3_initialize()] and -** [sqlite3_shutdown()]. -** -** {H14120} A successful call to [sqlite3_config]([SQLITE_CONFIG_SINGLETHREAD]) -** shall set the default [threading mode] to Single-thread. -** -** {H14123} A successful call to [sqlite3_config]([SQLITE_CONFIG_MULTITHREAD]) -** shall set the default [threading mode] to Multi-thread. -** -** {H14126} A successful call to [sqlite3_config]([SQLITE_CONFIG_SERIALIZED]) -** shall set the default [threading mode] to Serialized. -** -** {H14129} A successful call to [sqlite3_config]([SQLITE_CONFIG_MUTEX],X) -** where X is a pointer to an initialized [sqlite3_mutex_methods] -** object shall cause all subsequent mutex operations performed -** by SQLite to use the mutex methods that were present in X -** during the call to [sqlite3_config()]. -** -** {H14132} A successful call to [sqlite3_config]([SQLITE_CONFIG_GETMUTEX],X) -** where X is a pointer to an [sqlite3_mutex_methods] object -** shall overwrite the content of [sqlite3_mutex_methods] object -** with the mutex methods currently in use by SQLite. -** -** {H14135} A successful call to [sqlite3_config]([SQLITE_CONFIG_MALLOC],M) -** where M is a pointer to an initialized [sqlite3_mem_methods] -** object shall cause all subsequent memory allocation operations -** performed by SQLite to use the methods that were present in -** M during the call to [sqlite3_config()]. -** -** {H14138} A successful call to [sqlite3_config]([SQLITE_CONFIG_GETMALLOC],M) -** where M is a pointer to an [sqlite3_mem_methods] object shall -** overwrite the content of [sqlite3_mem_methods] object with -** the memory allocation methods currently in use by -** SQLite. -** -** {H14141} A successful call to [sqlite3_config]([SQLITE_CONFIG_MEMSTATUS],1) -** shall enable the memory allocation status collection logic. -** -** {H14144} A successful call to [sqlite3_config]([SQLITE_CONFIG_MEMSTATUS],0) -** shall disable the memory allocation status collection logic. -** -** {H14147} The memory allocation status collection logic shall be -** enabled by default. -** -** {H14150} A successful call to [sqlite3_config]([SQLITE_CONFIG_SCRATCH],S,Z,N) -** where Z and N are non-negative integers and -** S is a pointer to an aligned memory buffer not less than -** Z*N bytes in size shall cause S to be used by the -** [scratch memory allocator] for as many as N simulataneous -** allocations each of size (Z & ~7). -** -** {H14153} A successful call to [sqlite3_config]([SQLITE_CONFIG_SCRATCH],S,Z,N) -** where S is a NULL pointer shall disable the -** [scratch memory allocator]. -** -** {H14156} A successful call to -** [sqlite3_config]([SQLITE_CONFIG_PAGECACHE],S,Z,N) -** where Z and N are non-negative integers and -** S is a pointer to an aligned memory buffer not less than -** Z*N bytes in size shall cause S to be used by the -** [pagecache memory allocator] for as many as N simulataneous -** allocations each of size (Z & ~7). -** -** {H14159} A successful call to -** [sqlite3_config]([SQLITE_CONFIG_PAGECACHE],S,Z,N) -** where S is a NULL pointer shall disable the -** [pagecache memory allocator]. -** -** {H14162} A successful call to [sqlite3_config]([SQLITE_CONFIG_HEAP],H,Z,N) -** where Z and N are non-negative integers and -** H is a pointer to an aligned memory buffer not less than -** Z bytes in size shall enable the [memsys5] memory allocator -** and cause it to use buffer S as its memory source and to use -** a minimum allocation size of N. -** -** {H14165} A successful call to [sqlite3_config]([SQLITE_CONFIG_HEAP],H,Z,N) -** where H is a NULL pointer shall disable the -** [memsys5] memory allocator. -** -** {H14168} A successful call to [sqlite3_config]([SQLITE_CONFIG_LOOKASIDE],Z,N) -** shall cause the default [lookaside memory allocator] configuration -** for new [database connections] to be N slots of Z bytes each. +** Requirements: +** [H14103] [H14106] [H14120] [H14123] [H14126] [H14129] [H14132] [H14135] +** [H14138] [H14141] [H14144] [H14147] [H14150] [H14153] [H14156] [H14159] +** [H14162] [H14165] [H14168] */ SQLITE_EXPERIMENTAL int sqlite3_config(int, ...); @@ -1105,37 +896,8 @@ SQLITE_EXPERIMENTAL int sqlite3_config(int, ...); ** New verbs are likely to be added in future releases of SQLite. ** Additional arguments depend on the verb. ** -** INVARIANTS: -** -** {H14203} A call to [sqlite3_db_config(D,V,...)] shall return [SQLITE_OK] -** if and only if the call is successful. -** -** {H14206} If one or more slots of the [lookaside memory allocator] for -** [database connection] D are in use, then a call to -** [sqlite3_db_config](D,[SQLITE_DBCONFIG_LOOKASIDE],...) shall -** fail with an [SQLITE_BUSY] return code. -** -** {H14209} A successful call to -** [sqlite3_db_config](D,[SQLITE_DBCONFIG_LOOKASIDE],B,Z,N) where -** D is an open [database connection] and Z and N are positive -** integers and B is an aligned buffer at least Z*N bytes in size -** shall cause the [lookaside memory allocator] for D to use buffer B -** with N slots of Z bytes each. -** -** {H14212} A successful call to -** [sqlite3_db_config](D,[SQLITE_DBCONFIG_LOOKASIDE],B,Z,N) where -** D is an open [database connection] and Z and N are positive -** integers and B is NULL pointer shall cause the -** [lookaside memory allocator] for D to a obtain Z*N byte buffer -** from the primary memory allocator and use that buffer -** with N lookaside slots of Z bytes each. -** -** {H14215} A successful call to -** [sqlite3_db_config](D,[SQLITE_DBCONFIG_LOOKASIDE],B,Z,N) where -** D is an open [database connection] and Z and N are zero shall -** disable the [lookaside memory allocator] for D. -** -** +** Requirements: +** [H14203] [H14206] [H14209] [H14212] [H14215] */ SQLITE_EXPERIMENTAL int sqlite3_db_config(sqlite3*, int op, ...); @@ -1395,14 +1157,8 @@ struct sqlite3_mem_methods { ** [extended result codes] feature of SQLite. The extended result ** codes are disabled by default for historical compatibility considerations. ** -** INVARIANTS: -** -** {H12201} Each new [database connection] shall have the -** [extended result codes] feature disabled by default. -** -** {H12202} The [sqlite3_extended_result_codes(D,F)] interface shall enable -** [extended result codes] for the [database connection] D -** if the F parameter is true, or disable them if F is false. +** Requirements: +** [H12201] [H12202] */ int sqlite3_extended_result_codes(sqlite3*, int onoff); @@ -1439,27 +1195,15 @@ int sqlite3_extended_result_codes(sqlite3*, int onoff); ** For the purposes of this routine, an [INSERT] is considered to ** be successful even if it is subsequently rolled back. ** -** INVARIANTS: +** Requirements: +** [H12221] [H12223] ** -** {H12221} The [sqlite3_last_insert_rowid()] function shall return -** the [rowid] -** of the most recent successful [INSERT] performed on the same -** [database connection] and within the same or higher level -** trigger context, or zero if there have been no qualifying -** [INSERT] statements. -** -** {H12223} The [sqlite3_last_insert_rowid()] function shall return the -** same value when called from the same trigger context -** immediately before and after a [ROLLBACK]. -** -** ASSUMPTIONS: -** -** {A12232} If a separate thread performs a new [INSERT] on the same -** database connection while the [sqlite3_last_insert_rowid()] -** function is running and thus changes the last insert [rowid], -** then the value returned by [sqlite3_last_insert_rowid()] is -** unpredictable and might not equal either the old or the new -** last insert [rowid]. +** If a separate thread performs a new [INSERT] on the same +** database connection while the [sqlite3_last_insert_rowid()] +** function is running and thus changes the last insert [rowid], +** then the value returned by [sqlite3_last_insert_rowid()] is +** unpredictable and might not equal either the old or the new +** last insert [rowid]. */ sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); @@ -1514,24 +1258,12 @@ sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); ** [SQLITE_OMIT_TRUNCATE_OPTIMIZATION] compile-time option to disable the ** optimization on all queries. ** -** INVARIANTS: +** Requirements: +** [H12241] [H12243] ** -** {H12241} The [sqlite3_changes()] function shall return the number of -** row changes caused by the most recent INSERT, UPDATE, -** or DELETE statement on the same database connection and -** within the same or higher trigger context, or zero if there have -** not been any qualifying row changes. -** -** {H12243} Statements of the form "DELETE FROM tablename" with no -** WHERE clause shall cause subsequent calls to -** [sqlite3_changes()] to return zero, regardless of the -** number of rows originally in the table. -** -** ASSUMPTIONS: -** -** {A12252} If a separate thread makes changes on the same database connection -** while [sqlite3_changes()] is running then the value returned -** is unpredictable and not meaningful. +** If a separate thread makes changes on the same database connection +** while [sqlite3_changes()] is running then the value returned +** is unpredictable and not meaningful. */ int sqlite3_changes(sqlite3*); @@ -1560,22 +1292,12 @@ int sqlite3_changes(sqlite3*); ** ** See also the [sqlite3_changes()] interface. ** -** INVARIANTS: +** Requirements: +** [H12261] [H12263] ** -** {H12261} The [sqlite3_total_changes()] returns the total number -** of row changes caused by INSERT, UPDATE, and/or DELETE -** statements on the same [database connection], in any -** trigger context, since the database connection was created. -** -** {H12263} Statements of the form "DELETE FROM tablename" with no -** WHERE clause shall not change the value returned -** by [sqlite3_total_changes()]. -** -** ASSUMPTIONS: -** -** {A12264} If a separate thread makes changes on the same database connection -** while [sqlite3_total_changes()] is running then the value -** returned is unpredictable and not meaningful. +** If a separate thread makes changes on the same database connection +** while [sqlite3_total_changes()] is running then the value +** returned is unpredictable and not meaningful. */ int sqlite3_total_changes(sqlite3*); @@ -1605,19 +1327,11 @@ int sqlite3_total_changes(sqlite3*); ** A call to sqlite3_interrupt() has no effect on SQL statements ** that are started after sqlite3_interrupt() returns. ** -** INVARIANTS: +** Requirements: +** [H12271] [H12272] ** -** {H12271} The [sqlite3_interrupt()] interface will force all running -** SQL statements associated with the same database connection -** to halt after processing at most one additional row of data. -** -** {H12272} Any SQL statement that is interrupted by [sqlite3_interrupt()] -** will return [SQLITE_INTERRUPT]. -** -** ASSUMPTIONS: -** -** {A12279} If the database connection closes while [sqlite3_interrupt()] -** is running then bad things will likely happen. +** If the database connection closes while [sqlite3_interrupt()] +** is running then bad things will likely happen. */ void sqlite3_interrupt(sqlite3*); @@ -1638,25 +1352,13 @@ void sqlite3_interrupt(sqlite3*); ** These routines do not parse the SQL statements thus ** will not detect syntactically incorrect SQL. ** -** INVARIANTS: +** Requirements: [H10511] [H10512] ** -** {H10511} A successful evaluation of [sqlite3_complete()] or -** [sqlite3_complete16()] functions shall -** return a numeric 1 if and only if the last non-whitespace -** token in their input is a semicolon that is not in between -** the BEGIN and END of a CREATE TRIGGER statement. +** The input to [sqlite3_complete()] must be a zero-terminated +** UTF-8 string. ** -** {H10512} If a memory allocation error occurs during an invocation -** of [sqlite3_complete()] or [sqlite3_complete16()] then the -** routine shall return [SQLITE_NOMEM]. -** -** ASSUMPTIONS: -** -** {A10512} The input to [sqlite3_complete()] must be a zero-terminated -** UTF-8 string. -** -** {A10513} The input to [sqlite3_complete16()] must be a zero-terminated -** UTF-16 string in native byte order. +** The input to [sqlite3_complete16()] must be a zero-terminated +** UTF-16 string in native byte order. */ int sqlite3_complete(const char *sql); int sqlite3_complete16(const void *sql); @@ -1722,32 +1424,11 @@ int sqlite3_complete16(const void *sql); ** database connection that invoked the busy handler. Any such actions ** result in undefined behavior. ** -** INVARIANTS: +** Requirements: +** [H12311] [H12312] [H12314] [H12316] [H12318] ** -** {H12311} The [sqlite3_busy_handler(D,C,A)] function shall replace -** busy callback in the [database connection] D with a new -** a new busy handler C and application data pointer A. -** -** {H12312} Newly created [database connections] shall have a busy -** handler of NULL. -** -** {H12314} When two or more [database connections] share a -** [sqlite3_enable_shared_cache | common cache], -** the busy handler for the database connection currently using -** the cache shall be invoked when the cache encounters a lock. -** -** {H12316} If a busy handler callback returns zero, then the SQLite interface -** that provoked the locking event shall return [SQLITE_BUSY]. -** -** {H12318} SQLite shall invokes the busy handler with two arguments which -** are a copy of the pointer supplied by the 3rd parameter to -** [sqlite3_busy_handler()] and a count of the number of prior -** invocations of the busy handler for the same locking event. -** -** ASSUMPTIONS: -** -** {A12319} A busy handler must not close the database connection -** or [prepared statement] that invoked the busy handler. +** A busy handler must not close the database connection +** or [prepared statement] that invoked the busy handler. */ int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*); @@ -1769,21 +1450,8 @@ int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*); ** was defined (using [sqlite3_busy_handler()]) prior to calling ** this routine, that other busy handler is cleared. ** -** INVARIANTS: -** -** {H12341} The [sqlite3_busy_timeout()] function shall override any prior -** [sqlite3_busy_timeout()] or [sqlite3_busy_handler()] setting -** on the same [database connection]. -** -** {H12343} If the 2nd parameter to [sqlite3_busy_timeout()] is less than -** or equal to zero, then the busy handler shall be cleared so that -** all subsequent locking events immediately return [SQLITE_BUSY]. -** -** {H12344} If the 2nd parameter to [sqlite3_busy_timeout()] is a positive -** number N, then a busy handler shall be set that repeatedly calls -** the xSleep() method in the [sqlite3_vfs | VFS interface] until -** either the lock clears or until the cumulative sleep time -** reported back by xSleep() exceeds N milliseconds. +** Requirements: +** [H12341] [H12343] [H12344] */ int sqlite3_busy_timeout(sqlite3*, int ms); @@ -1855,38 +1523,8 @@ int sqlite3_busy_timeout(sqlite3*, int ms); ** wrapper layer outside of the internal [sqlite3_exec()] call are not ** reflected in subsequent calls to [sqlite3_errcode()] or [sqlite3_errmsg()]. ** -** INVARIANTS: -** -** {H12371} If a [sqlite3_get_table()] fails a memory allocation, then -** it shall free the result table under construction, abort the -** query in process, skip any subsequent queries, set the -** *pazResult output pointer to NULL and return [SQLITE_NOMEM]. -** -** {H12373} If the pnColumn parameter to [sqlite3_get_table()] is not NULL -** then a successful invocation of [sqlite3_get_table()] shall -** write the number of columns in the -** result set of the query into *pnColumn. -** -** {H12374} If the pnRow parameter to [sqlite3_get_table()] is not NULL -** then a successful invocation of [sqlite3_get_table()] shall -** writes the number of rows in the -** result set of the query into *pnRow. -** -** {H12376} A successful invocation of [sqlite3_get_table()] that computes -** N rows of result with C columns per row shall make *pazResult -** point to an array of pointers to (N+1)*C strings where the first -** C strings are column names as obtained from -** [sqlite3_column_name()] and the rest are column result values -** obtained from [sqlite3_column_text()]. -** -** {H12379} The values in the pazResult array returned by [sqlite3_get_table()] -** shall remain valid until cleared by [sqlite3_free_table()]. -** -** {H12382} When an error occurs during evaluation of [sqlite3_get_table()] -** the function shall set *pazResult to NULL, write an error message -** into memory obtained from [sqlite3_malloc()], make -** **pzErrmsg point to that error message, and return a -** appropriate [error code]. +** Requirements: +** [H12371] [H12373] [H12374] [H12376] [H12379] [H12382] */ int sqlite3_get_table( sqlite3 *db, /* An open database */ @@ -1990,22 +1628,8 @@ void sqlite3_free_table(char **result); ** addition that after the string has been read and copied into ** the result, [sqlite3_free()] is called on the input string. {END} ** -** INVARIANTS: -** -** {H17403} The [sqlite3_mprintf()] and [sqlite3_vmprintf()] interfaces -** return either pointers to zero-terminated UTF-8 strings held in -** memory obtained from [sqlite3_malloc()] or NULL pointers if -** a call to [sqlite3_malloc()] fails. -** -** {H17406} The [sqlite3_snprintf()] interface writes a zero-terminated -** UTF-8 string into the buffer pointed to by the second parameter -** provided that the first parameter is greater than zero. -** -** {H17407} The [sqlite3_snprintf()] interface does not write slots of -** its output buffer (the second parameter) outside the range -** of 0 through N-1 (where N is the first parameter) -** regardless of the length of the string -** requested by the format specification. +** Requirements: +** [H17403] [H17406] [H17407] */ char *sqlite3_mprintf(const char*,...); char *sqlite3_vmprintf(const char*, va_list); @@ -2079,57 +1703,18 @@ char *sqlite3_snprintf(int,char*,const char*, ...); ** they are reported back as [SQLITE_CANTOPEN] or ** [SQLITE_IOERR] rather than [SQLITE_NOMEM]. ** -** INVARIANTS: +** Requirements: +** [H17303] [H17304] [H17305] [H17306] [H17310] [H17312] [H17315] [H17318] +** [H17321] [H17322] [H17323] ** -** {H17303} The [sqlite3_malloc(N)] interface returns either a pointer to -** a newly checked-out block of at least N bytes of memory -** that is 8-byte aligned, or it returns NULL if it is unable -** to fulfill the request. +** The pointer arguments to [sqlite3_free()] and [sqlite3_realloc()] +** must be either NULL or else pointers obtained from a prior +** invocation of [sqlite3_malloc()] or [sqlite3_realloc()] that have +** not yet been released. ** -** {H17304} The [sqlite3_malloc(N)] interface returns a NULL pointer if -** N is less than or equal to zero. -** -** {H17305} The [sqlite3_free(P)] interface releases memory previously -** returned from [sqlite3_malloc()] or [sqlite3_realloc()], -** making it available for reuse. -** -** {H17306} A call to [sqlite3_free(NULL)] is a harmless no-op. -** -** {H17310} A call to [sqlite3_realloc(0,N)] is equivalent to a call -** to [sqlite3_malloc(N)]. -** -** {H17312} A call to [sqlite3_realloc(P,0)] is equivalent to a call -** to [sqlite3_free(P)]. -** -** {H17315} The SQLite core uses [sqlite3_malloc()], [sqlite3_realloc()], -** and [sqlite3_free()] for all of its memory allocation and -** deallocation needs. -** -** {H17318} The [sqlite3_realloc(P,N)] interface returns either a pointer -** to a block of checked-out memory of at least N bytes in size -** that is 8-byte aligned, or a NULL pointer. -** -** {H17321} When [sqlite3_realloc(P,N)] returns a non-NULL pointer, it first -** copies the first K bytes of content from P into the newly -** allocated block, where K is the lesser of N and the size of -** the buffer P. -** -** {H17322} When [sqlite3_realloc(P,N)] returns a non-NULL pointer, it first -** releases the buffer P. -** -** {H17323} When [sqlite3_realloc(P,N)] returns NULL, the buffer P is -** not modified or released. -** -** ASSUMPTIONS: -** -** {A17350} The pointer arguments to [sqlite3_free()] and [sqlite3_realloc()] -** must be either NULL or else pointers obtained from a prior -** invocation of [sqlite3_malloc()] or [sqlite3_realloc()] that have -** not yet been released. -** -** {A17351} The application must not read or write any part of -** a block of memory after it has been released using -** [sqlite3_free()] or [sqlite3_realloc()]. +** The application must not read or write any part of +** a block of memory after it has been released using +** [sqlite3_free()] or [sqlite3_realloc()]. */ void *sqlite3_malloc(int); void *sqlite3_realloc(void*, int); @@ -2142,26 +1727,8 @@ void sqlite3_free(void*); ** of the [sqlite3_malloc()], [sqlite3_free()], and [sqlite3_realloc()] ** routines, which form the built-in memory allocation subsystem. ** -** INVARIANTS: -** -** {H17371} The [sqlite3_memory_used()] routine returns the number of bytes -** of memory currently outstanding (malloced but not freed). -** -** {H17373} The [sqlite3_memory_highwater()] routine returns the maximum -** value of [sqlite3_memory_used()] since the high-water mark -** was last reset. -** -** {H17374} The values returned by [sqlite3_memory_used()] and -** [sqlite3_memory_highwater()] include any overhead -** added by SQLite in its implementation of [sqlite3_malloc()], -** but not overhead added by the any underlying system library -** routines that [sqlite3_malloc()] may call. -** -** {H17375} The memory high-water mark is reset to the current value of -** [sqlite3_memory_used()] if and only if the parameter to -** [sqlite3_memory_highwater()] is true. The value returned -** by [sqlite3_memory_highwater(1)] is the high-water mark -** prior to the reset. +** Requirements: +** [H17371] [H17373] [H17374] [H17375] */ sqlite3_int64 sqlite3_memory_used(void); sqlite3_int64 sqlite3_memory_highwater(int resetFlag); @@ -2184,10 +1751,8 @@ sqlite3_int64 sqlite3_memory_highwater(int resetFlag); ** internally and without recourse to the [sqlite3_vfs] xRandomness ** method. ** -** INVARIANTS: -** -** {H17392} The [sqlite3_randomness(N,P)] interface writes N bytes of -** high-quality pseudo-randomness into buffer P. +** Requirements: +** [H17392] */ void sqlite3_randomness(int N, void *P); @@ -2264,57 +1829,9 @@ void sqlite3_randomness(int N, void *P); ** [sqlite3_prepare()] or its variants. Authorization is not ** performed during statement evaluation in [sqlite3_step()]. ** -** INVARIANTS: -** -** {H12501} The [sqlite3_set_authorizer(D,...)] interface registers a -** authorizer callback with database connection D. -** -** {H12502} The authorizer callback is invoked as SQL statements are -** being parseed and compiled. -** -** {H12503} If the authorizer callback returns any value other than -** [SQLITE_IGNORE], [SQLITE_OK], or [SQLITE_DENY], then -** the application interface call that caused -** the authorizer callback to run shall fail with an -** [SQLITE_ERROR] error code and an appropriate error message. -** -** {H12504} When the authorizer callback returns [SQLITE_OK], the operation -** described is processed normally. -** -** {H12505} When the authorizer callback returns [SQLITE_DENY], the -** application interface call that caused the -** authorizer callback to run shall fail -** with an [SQLITE_ERROR] error code and an error message -** explaining that access is denied. -** -** {H12506} If the authorizer code (the 2nd parameter to the authorizer -** callback) is [SQLITE_READ] and the authorizer callback returns -** [SQLITE_IGNORE], then the prepared statement is constructed to -** insert a NULL value in place of the table column that would have -** been read if [SQLITE_OK] had been returned. -** -** {H12507} If the authorizer code (the 2nd parameter to the authorizer -** callback) is anything other than [SQLITE_READ], then -** a return of [SQLITE_IGNORE] has the same effect as [SQLITE_DENY]. -** -** {H12510} The first parameter to the authorizer callback is a copy of -** the third parameter to the [sqlite3_set_authorizer()] interface. -** -** {H12511} The second parameter to the callback is an integer -** [SQLITE_COPY | action code] that specifies the particular action -** to be authorized. -** -** {H12512} The third through sixth parameters to the callback are -** zero-terminated strings that contain -** additional details about the action to be authorized. -** -** {H12520} Each call to [sqlite3_set_authorizer()] overrides -** any previously installed authorizer. -** -** {H12521} A NULL authorizer means that no authorization -** callback is invoked. -** -** {H12522} The default authorizer is NULL. +** Requirements: +** [H12501] [H12502] [H12503] [H12504] [H12505] [H12506] [H12507] [H12510] +** [H12511] [H12512] [H12520] [H12521] [H12522] */ int sqlite3_set_authorizer( sqlite3*, @@ -2353,27 +1870,8 @@ int sqlite3_set_authorizer( ** the access attempt or NULL if this access attempt is directly from ** top-level SQL code. ** -** INVARIANTS: -** -** {H12551} The second parameter to an -** [sqlite3_set_authorizer | authorizer callback] shall be an integer -** [SQLITE_COPY | authorizer code] that specifies what action -** is being authorized. -** -** {H12552} The 3rd and 4th parameters to the -** [sqlite3_set_authorizer | authorization callback] -** shall be parameters or NULL depending on which -** [SQLITE_COPY | authorizer code] is used as the second parameter. -** -** {H12553} The 5th parameter to the -** [sqlite3_set_authorizer | authorizer callback] shall be the name -** of the database (example: "main", "temp", etc.) if applicable. -** -** {H12554} The 6th parameter to the -** [sqlite3_set_authorizer | authorizer callback] shall be the name -** of the inner-most trigger or view that is responsible for -** the access attempt or NULL if this access attempt is directly from -** top-level SQL code. +** Requirements: +** [H12551] [H12552] [H12553] [H12554] */ /******************************************* 3rd ************ 4th ***********/ #define SQLITE_CREATE_INDEX 1 /* Index Name Table Name */ @@ -2429,41 +1927,9 @@ int sqlite3_set_authorizer( ** the original statement text and an estimate of wall-clock time ** of how long that statement took to run. ** -** INVARIANTS: -** -** {H12281} The callback function registered by [sqlite3_trace()] -** shall be invoked -** whenever an SQL statement first begins to execute and -** whenever a trigger subprogram first begins to run. -** -** {H12282} Each call to [sqlite3_trace()] shall override the previously -** registered trace callback. -** -** {H12283} A NULL trace callback shall disable tracing. -** -** {H12284} The first argument to the trace callback shall be a copy of -** the pointer which was the 3rd argument to [sqlite3_trace()]. -** -** {H12285} The second argument to the trace callback is a -** zero-terminated UTF-8 string containing the original text -** of the SQL statement as it was passed into [sqlite3_prepare_v2()] -** or the equivalent, or an SQL comment indicating the beginning -** of a trigger subprogram. -** -** {H12287} The callback function registered by [sqlite3_profile()] is invoked -** as each SQL statement finishes. -** -** {H12288} The first parameter to the profile callback is a copy of -** the 3rd parameter to [sqlite3_profile()]. -** -** {H12289} The second parameter to the profile callback is a -** zero-terminated UTF-8 string that contains the complete text of -** the SQL statement as it was processed by [sqlite3_prepare_v2()] -** or the equivalent. -** -** {H12290} The third parameter to the profile callback is an estimate -** of the number of nanoseconds of wall-clock time required to -** run the SQL statement from start to finish. +** Requirements: +** [H12281] [H12282] [H12283] [H12284] [H12285] [H12287] [H12288] [H12289] +** [H12290] */ SQLITE_EXPERIMENTAL void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*, @@ -2487,37 +1953,9 @@ SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*, ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their ** database connections for the meaning of "modify" in this paragraph. ** -** INVARIANTS: +** Requirements: +** [H12911] [H12912] [H12913] [H12914] [H12915] [H12916] [H12917] [H12918] ** -** {H12911} The callback function registered by sqlite3_progress_handler() -** is invoked periodically during long running calls to -** [sqlite3_step()]. -** -** {H12912} The progress callback is invoked once for every N virtual -** machine opcodes, where N is the second argument to -** the [sqlite3_progress_handler()] call that registered -** the callback. If N is less than 1, sqlite3_progress_handler() -** acts as if a NULL progress handler had been specified. -** -** {H12913} The progress callback itself is identified by the third -** argument to sqlite3_progress_handler(). -** -** {H12914} The fourth argument to sqlite3_progress_handler() is a -** void pointer passed to the progress callback -** function each time it is invoked. -** -** {H12915} If a call to [sqlite3_step()] results in fewer than N opcodes -** being executed, then the progress callback is never invoked. -** -** {H12916} Every call to [sqlite3_progress_handler()] -** overwrites any previously registered progress handler. -** -** {H12917} If the progress handler callback is NULL then no progress -** handler is invoked. -** -** {H12918} If the progress callback returns a result other than 0, then -** the behavior is a if [sqlite3_interrupt()] had been called. -** */ void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); @@ -2601,72 +2039,9 @@ void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); ** characters must be converted to UTF-8 prior to passing them into ** sqlite3_open() or sqlite3_open_v2(). ** -** INVARIANTS: -** -** {H12701} The [sqlite3_open()], [sqlite3_open16()], and -** [sqlite3_open_v2()] interfaces create a new -** [database connection] associated with -** the database file given in their first parameter. -** -** {H12702} The filename argument is interpreted as UTF-8 -** for [sqlite3_open()] and [sqlite3_open_v2()] and as UTF-16 -** in the native byte order for [sqlite3_open16()]. -** -** {H12703} A successful invocation of [sqlite3_open()], [sqlite3_open16()], -** or [sqlite3_open_v2()] writes a pointer to a new -** [database connection] into *ppDb. -** -** {H12704} The [sqlite3_open()], [sqlite3_open16()], and -** [sqlite3_open_v2()] interfaces return [SQLITE_OK] upon success, -** or an appropriate [error code] on failure. -** -** {H12706} The default text encoding for a new database created using -** [sqlite3_open()] or [sqlite3_open_v2()] will be UTF-8. -** -** {H12707} The default text encoding for a new database created using -** [sqlite3_open16()] will be UTF-16. -** -** {H12709} The [sqlite3_open(F,D)] interface is equivalent to -** [sqlite3_open_v2(F,D,G,0)] where the G parameter is -** [SQLITE_OPEN_READWRITE]|[SQLITE_OPEN_CREATE]. -** -** {H12711} If the G parameter to [sqlite3_open_v2(F,D,G,V)] contains the -** bit value [SQLITE_OPEN_READONLY] then the database is opened -** for reading only. -** -** {H12712} If the G parameter to [sqlite3_open_v2(F,D,G,V)] contains the -** bit value [SQLITE_OPEN_READWRITE] then the database is opened -** reading and writing if possible, or for reading only if the -** file is write protected by the operating system. -** -** {H12713} If the G parameter to [sqlite3_open_v2(F,D,G,V)] omits the -** bit value [SQLITE_OPEN_CREATE] and the database does not -** previously exist, an error is returned. -** -** {H12714} If the G parameter to [sqlite3_open_v2(F,D,G,V)] contains the -** bit value [SQLITE_OPEN_CREATE] and the database does not -** previously exist, then an attempt is made to create and -** initialize the database. -** -** {H12717} If the filename argument to [sqlite3_open()], [sqlite3_open16()], -** or [sqlite3_open_v2()] is ":memory:", then an private, -** ephemeral, in-memory database is created for the connection. -** Is SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE required -** in sqlite3_open_v2()? -** -** {H12719} If the filename is NULL or an empty string, then a private, -** ephemeral on-disk database will be created. -** Is SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE required -** in sqlite3_open_v2()? -** -** {H12721} The [database connection] created by [sqlite3_open_v2(F,D,G,V)] -** will use the [sqlite3_vfs] object identified by the V parameter, -** or the default [sqlite3_vfs] object if V is a NULL pointer. -** -** {H12723} Two [database connections] will share a common cache if both were -** opened with the same VFS while [shared cache mode] was enabled and -** if both filenames compare equal using memcmp() after having been -** processed by the [sqlite3_vfs | xFullPathname] method of the VFS. +** Requirements: +** [H12701] [H12702] [H12703] [H12704] [H12706] [H12707] [H12709] [H12711] +** [H12712] [H12713] [H12714] [H12717] [H12719] [H12721] [H12723] */ int sqlite3_open( const char *filename, /* Database filename (UTF-8) */ @@ -2716,36 +2091,8 @@ int sqlite3_open_v2( ** was invoked incorrectly by the application. In that case, the ** error code and message may or may not be set. ** -** INVARIANTS: -** -** {H12801} The [sqlite3_errcode(D)] interface returns the numeric -** [result code] or [extended result code] for the most recently -** failed interface call associated with the [database connection] D. -** -** {H12802} The [sqlite3_extended_errcode(D)] interface returns the numeric -** [extended result code] for the most recently -** failed interface call associated with the [database connection] D. -** -** {H12803} The [sqlite3_errmsg(D)] and [sqlite3_errmsg16(D)] -** interfaces return English-language text that describes -** the error in the mostly recently failed interface call, -** encoded as either UTF-8 or UTF-16 respectively. -** -** {H12807} The strings returned by [sqlite3_errmsg()] and [sqlite3_errmsg16()] -** are valid until the next SQLite interface call. -** -** {H12808} Calls to API routines that do not return an error code -** (example: [sqlite3_data_count()]) do not -** change the error code or message returned by -** [sqlite3_errcode()], [sqlite3_extended_errcode()], -** [sqlite3_errmsg()], or [sqlite3_errmsg16()]. -** -** {H12809} Interfaces that are not associated with a specific -** [database connection] (examples: -** [sqlite3_mprintf()] or [sqlite3_enable_shared_cache()] -** do not change the values returned by -** [sqlite3_errcode()], [sqlite3_extended_errcode()], -** [sqlite3_errmsg()], or [sqlite3_errmsg16()]. +** Requirements: +** [H12801] [H12802] [H12803] [H12807] [H12808] [H12809] */ int sqlite3_errcode(sqlite3 *db); int sqlite3_extended_errcode(sqlite3 *db); @@ -2812,19 +2159,8 @@ typedef struct sqlite3_stmt sqlite3_stmt; ** ** New run-time limit categories may be added in future releases. ** -** INVARIANTS: -** -** {H12762} A successful call to [sqlite3_limit(D,C,V)] where V is -** positive changes the limit on the size of construct C in the -** [database connection] D to the lesser of V and the hard upper -** bound on the size of C that is set at compile-time. -** -** {H12766} A successful call to [sqlite3_limit(D,C,V)] where V is negative -** leaves the state of the [database connection] D unchanged. -** -** {H12769} A successful call to [sqlite3_limit(D,C,V)] returns the -** value of the limit on the size of construct C in the -** [database connection] D as it was prior to the call. +** Requirements: +** [H12762] [H12766] [H12769] */ int sqlite3_limit(sqlite3*, int id, int newVal); @@ -2893,7 +2229,8 @@ int sqlite3_limit(sqlite3*, int id, int newVal); ** program using one of these routines. ** ** The first argument, "db", is a [database connection] obtained from a -** prior call to [sqlite3_open()], [sqlite3_open_v2()] or [sqlite3_open16()]. +** prior successful call to [sqlite3_open()], [sqlite3_open_v2()] or +** [sqlite3_open16()]. The database connection must not have been closed. ** ** The second argument, "zSql", is the statement to be compiled, encoded ** as either UTF-8 or UTF-16. The sqlite3_prepare() and sqlite3_prepare_v2() @@ -2910,17 +2247,18 @@ int sqlite3_limit(sqlite3*, int id, int newVal); ** is equal to the number of bytes in the input string including ** the nul-terminator bytes. ** -** *pzTail is made to point to the first byte past the end of the -** first SQL statement in zSql. These routines only compile the first -** statement in zSql, so *pzTail is left pointing to what remains -** uncompiled. +** If pzTail is not NULL then *pzTail is made to point to the first byte +** past the end of the first SQL statement in zSql. These routines only +** compile the first statement in zSql, so *pzTail is left pointing to +** what remains uncompiled. ** ** *ppStmt is left pointing to a compiled [prepared statement] that can be ** executed using [sqlite3_step()]. If there is an error, *ppStmt is set ** to NULL. If the input text contains no SQL (if the input is an empty ** string or a comment) then *ppStmt is set to NULL. -** {A13018} The calling procedure is responsible for deleting the compiled +** The calling procedure is responsible for deleting the compiled ** SQL statement using [sqlite3_finalize()] after it has finished with it. +** ppStmt may not be NULL. ** ** On success, [SQLITE_OK] is returned, otherwise an [error code] is returned. ** @@ -2954,41 +2292,9 @@ int sqlite3_limit(sqlite3*, int id, int newVal); ** ** ** -** INVARIANTS: +** Requirements: +** [H13011] [H13012] [H13013] [H13014] [H13015] [H13016] [H13019] [H13021] ** -** {H13011} The [sqlite3_prepare(db,zSql,...)] and -** [sqlite3_prepare_v2(db,zSql,...)] interfaces interpret the -** text in their zSql parameter as UTF-8. -** -** {H13012} The [sqlite3_prepare16(db,zSql,...)] and -** [sqlite3_prepare16_v2(db,zSql,...)] interfaces interpret the -** text in their zSql parameter as UTF-16 in the native byte order. -** -** {H13013} If the nByte argument to [sqlite3_prepare_v2(db,zSql,nByte,...)] -** and its variants is less than zero, the SQL text is -** read from zSql is read up to the first zero terminator. -** -** {H13014} If the nByte argument to [sqlite3_prepare_v2(db,zSql,nByte,...)] -** and its variants is non-negative, then at most nBytes bytes of -** SQL text is read from zSql. -** -** {H13015} In [sqlite3_prepare_v2(db,zSql,N,P,pzTail)] and its variants -** if the zSql input text contains more than one SQL statement -** and pzTail is not NULL, then *pzTail is made to point to the -** first byte past the end of the first SQL statement in zSql. -** What does *pzTail point to if there is one statement? -** -** {H13016} A successful call to [sqlite3_prepare_v2(db,zSql,N,ppStmt,...)] -** or one of its variants writes into *ppStmt a pointer to a new -** [prepared statement] or a pointer to NULL if zSql contains -** nothing other than whitespace or comments. -** -** {H13019} The [sqlite3_prepare_v2()] interface and its variants return -** [SQLITE_OK] or an appropriate [error code] upon failure. -** -** {H13021} Before [sqlite3_prepare(db,zSql,nByte,ppStmt,pzTail)] or its -** variants returns an error (any value other than [SQLITE_OK]), -** they first set *ppStmt to NULL. */ int sqlite3_prepare( sqlite3 *db, /* Database handle */ @@ -3026,20 +2332,8 @@ int sqlite3_prepare16_v2( ** SQL text used to create a [prepared statement] if that statement was ** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()]. ** -** INVARIANTS: -** -** {H13101} If the [prepared statement] passed as the argument to -** [sqlite3_sql()] was compiled using either [sqlite3_prepare_v2()] or -** [sqlite3_prepare16_v2()], then [sqlite3_sql()] returns -** a pointer to a zero-terminated string containing a UTF-8 rendering -** of the original SQL statement. -** -** {H13102} If the [prepared statement] passed as the argument to -** [sqlite3_sql()] was compiled using either [sqlite3_prepare()] or -** [sqlite3_prepare16()], then [sqlite3_sql()] returns a NULL pointer. -** -** {H13103} The string returned by [sqlite3_sql(S)] is valid until the -** [prepared statement] S is deleted using [sqlite3_finalize(S)]. +** Requirements: +** [H13101] [H13102] [H13103] */ const char *sqlite3_sql(sqlite3_stmt *pStmt); @@ -3175,79 +2469,10 @@ typedef struct sqlite3_context sqlite3_context; ** See also: [sqlite3_bind_parameter_count()], ** [sqlite3_bind_parameter_name()], and [sqlite3_bind_parameter_index()]. ** -** INVARIANTS: +** Requirements: +** [H13506] [H13509] [H13512] [H13515] [H13518] [H13521] [H13524] [H13527] +** [H13530] [H13533] [H13536] [H13539] [H13542] [H13545] [H13548] [H13551] ** -** {H13506} The [SQL statement compiler] recognizes tokens of the forms -** "?", "?NNN", "$VVV", ":VVV", and "@VVV" as SQL parameters, -** where NNN is any sequence of one or more digits -** and where VVV is any sequence of one or more alphanumeric -** characters or "::" optionally followed by a string containing -** no spaces and contained within parentheses. -** -** {H13509} The initial value of an SQL parameter is NULL. -** -** {H13512} The index of an "?" SQL parameter is one larger than the -** largest index of SQL parameter to the left, or 1 if -** the "?" is the leftmost SQL parameter. -** -** {H13515} The index of an "?NNN" SQL parameter is the integer NNN. -** -** {H13518} The index of an ":VVV", "$VVV", or "@VVV" SQL parameter is -** the same as the index of leftmost occurrences of the same -** parameter, or one more than the largest index over all -** parameters to the left if this is the first occurrence -** of this parameter, or 1 if this is the leftmost parameter. -** -** {H13521} The [SQL statement compiler] fails with an [SQLITE_RANGE] -** error if the index of an SQL parameter is less than 1 -** or greater than the compile-time SQLITE_MAX_VARIABLE_NUMBER -** parameter. -** -** {H13524} Calls to [sqlite3_bind_text | sqlite3_bind(S,N,V,...)] -** associate the value V with all SQL parameters having an -** index of N in the [prepared statement] S. -** -** {H13527} Calls to [sqlite3_bind_text | sqlite3_bind(S,N,...)] -** override prior calls with the same values of S and N. -** -** {H13530} Bindings established by [sqlite3_bind_text | sqlite3_bind(S,...)] -** persist across calls to [sqlite3_reset(S)]. -** -** {H13533} In calls to [sqlite3_bind_blob(S,N,V,L,D)], -** [sqlite3_bind_text(S,N,V,L,D)], or -** [sqlite3_bind_text16(S,N,V,L,D)] SQLite binds the first L -** bytes of the BLOB or string pointed to by V, when L -** is non-negative. -** -** {H13536} In calls to [sqlite3_bind_text(S,N,V,L,D)] or -** [sqlite3_bind_text16(S,N,V,L,D)] SQLite binds characters -** from V through the first zero character when L is negative. -** -** {H13539} In calls to [sqlite3_bind_blob(S,N,V,L,D)], -** [sqlite3_bind_text(S,N,V,L,D)], or -** [sqlite3_bind_text16(S,N,V,L,D)] when D is the special -** constant [SQLITE_STATIC], SQLite assumes that the value V -** is held in static unmanaged space that will not change -** during the lifetime of the binding. -** -** {H13542} In calls to [sqlite3_bind_blob(S,N,V,L,D)], -** [sqlite3_bind_text(S,N,V,L,D)], or -** [sqlite3_bind_text16(S,N,V,L,D)] when D is the special -** constant [SQLITE_TRANSIENT], the routine makes a -** private copy of the value V before it returns. -** -** {H13545} In calls to [sqlite3_bind_blob(S,N,V,L,D)], -** [sqlite3_bind_text(S,N,V,L,D)], or -** [sqlite3_bind_text16(S,N,V,L,D)] when D is a pointer to -** a function, SQLite invokes that function to destroy the -** value V after it has finished using the value V. -** -** {H13548} In calls to [sqlite3_bind_zeroblob(S,N,V,L)] the value bound -** is a BLOB of L bytes, or a zero-length BLOB if L is negative. -** -** {H13551} In calls to [sqlite3_bind_value(S,N,V)] the V argument may -** be either a [protected sqlite3_value] object or an -** [unprotected sqlite3_value] object. */ int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); int sqlite3_bind_double(sqlite3_stmt*, int, double); @@ -3277,11 +2502,8 @@ int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); ** [sqlite3_bind_parameter_name()], and ** [sqlite3_bind_parameter_index()]. ** -** INVARIANTS: -** -** {H13601} The [sqlite3_bind_parameter_count(S)] interface returns -** the largest index of all SQL parameters in the -** [prepared statement] S, or 0 if S contains no SQL parameters. +** Requirements: +** [H13601] */ int sqlite3_bind_parameter_count(sqlite3_stmt*); @@ -3310,13 +2532,8 @@ int sqlite3_bind_parameter_count(sqlite3_stmt*); ** [sqlite3_bind_parameter_count()], and ** [sqlite3_bind_parameter_index()]. ** -** INVARIANTS: -** -** {H13621} The [sqlite3_bind_parameter_name(S,N)] interface returns -** a UTF-8 rendering of the name of the SQL parameter in -** the [prepared statement] S having index N, or -** NULL if there is no SQL parameter with index N or if the -** parameter with index N is an anonymous parameter "?". +** Requirements: +** [H13621] */ const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); @@ -3334,12 +2551,8 @@ const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); ** [sqlite3_bind_parameter_count()], and ** [sqlite3_bind_parameter_index()]. ** -** INVARIANTS: -** -** {H13641} The [sqlite3_bind_parameter_index(S,N)] interface returns -** the index of SQL parameter in the [prepared statement] -** S whose name matches the UTF-8 string N, or 0 if there is -** no match. +** Requirements: +** [H13641] */ int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName); @@ -3350,10 +2563,8 @@ int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName); ** the [sqlite3_bind_blob | bindings] on a [prepared statement]. ** Use this routine to reset all host parameters to NULL. ** -** INVARIANTS: -** -** {H13661} The [sqlite3_clear_bindings(S)] interface resets all SQL -** parameter bindings in the [prepared statement] S back to NULL. +** Requirements: +** [H13661] */ int sqlite3_clear_bindings(sqlite3_stmt*); @@ -3364,11 +2575,8 @@ int sqlite3_clear_bindings(sqlite3_stmt*); ** [prepared statement]. This routine returns 0 if pStmt is an SQL ** statement that does not return data (for example an [UPDATE]). ** -** INVARIANTS: -** -** {H13711} The [sqlite3_column_count(S)] interface returns the number of -** columns in the result set generated by the [prepared statement] S, -** or 0 if S does not generate a result set. +** Requirements: +** [H13711] */ int sqlite3_column_count(sqlite3_stmt *pStmt); @@ -3396,35 +2604,8 @@ int sqlite3_column_count(sqlite3_stmt *pStmt); ** then the name of the column is unspecified and may change from ** one release of SQLite to the next. ** -** INVARIANTS: -** -** {H13721} A successful invocation of the [sqlite3_column_name(S,N)] -** interface returns the name of the Nth column (where 0 is -** the leftmost column) for the result set of the -** [prepared statement] S as a zero-terminated UTF-8 string. -** -** {H13723} A successful invocation of the [sqlite3_column_name16(S,N)] -** interface returns the name of the Nth column (where 0 is -** the leftmost column) for the result set of the -** [prepared statement] S as a zero-terminated UTF-16 string -** in the native byte order. -** -** {H13724} The [sqlite3_column_name()] and [sqlite3_column_name16()] -** interfaces return a NULL pointer if they are unable to -** allocate memory to hold their normal return strings. -** -** {H13725} If the N parameter to [sqlite3_column_name(S,N)] or -** [sqlite3_column_name16(S,N)] is out of range, then the -** interfaces return a NULL pointer. -** -** {H13726} The strings returned by [sqlite3_column_name(S,N)] and -** [sqlite3_column_name16(S,N)] are valid until the next -** call to either routine with the same S and N parameters -** or until [sqlite3_finalize(S)] is called. -** -** {H13727} When a result column of a [SELECT] statement contains -** an AS clause, the name of that column is the identifier -** to the right of the AS keyword. +** Requirements: +** [H13721] [H13723] [H13724] [H13725] [H13726] [H13727] */ const char *sqlite3_column_name(sqlite3_stmt*, int N); const void *sqlite3_column_name16(sqlite3_stmt*, int N); @@ -3466,57 +2647,13 @@ const void *sqlite3_column_name16(sqlite3_stmt*, int N); ** prepared statement and column at the same time then the results are ** undefined. ** -** INVARIANTS: +** Requirements: +** [H13741] [H13742] [H13743] [H13744] [H13745] [H13746] [H13748] ** -** {H13741} The [sqlite3_column_database_name(S,N)] interface returns either -** the UTF-8 zero-terminated name of the database from which the -** Nth result column of the [prepared statement] S is extracted, -** or NULL if the Nth column of S is a general expression -** or if unable to allocate memory to store the name. -** -** {H13742} The [sqlite3_column_database_name16(S,N)] interface returns either -** the UTF-16 native byte order zero-terminated name of the database -** from which the Nth result column of the [prepared statement] S is -** extracted, or NULL if the Nth column of S is a general expression -** or if unable to allocate memory to store the name. -** -** {H13743} The [sqlite3_column_table_name(S,N)] interface returns either -** the UTF-8 zero-terminated name of the table from which the -** Nth result column of the [prepared statement] S is extracted, -** or NULL if the Nth column of S is a general expression -** or if unable to allocate memory to store the name. -** -** {H13744} The [sqlite3_column_table_name16(S,N)] interface returns either -** the UTF-16 native byte order zero-terminated name of the table -** from which the Nth result column of the [prepared statement] S is -** extracted, or NULL if the Nth column of S is a general expression -** or if unable to allocate memory to store the name. -** -** {H13745} The [sqlite3_column_origin_name(S,N)] interface returns either -** the UTF-8 zero-terminated name of the table column from which the -** Nth result column of the [prepared statement] S is extracted, -** or NULL if the Nth column of S is a general expression -** or if unable to allocate memory to store the name. -** -** {H13746} The [sqlite3_column_origin_name16(S,N)] interface returns either -** the UTF-16 native byte order zero-terminated name of the table -** column from which the Nth result column of the -** [prepared statement] S is extracted, or NULL if the Nth column -** of S is a general expression or if unable to allocate memory -** to store the name. -** -** {H13748} The return values from -** [sqlite3_column_database_name | column metadata interfaces] -** are valid for the lifetime of the [prepared statement] -** or until the encoding is changed by another metadata -** interface call for the same prepared statement and column. -** -** ASSUMPTIONS: -** -** {A13751} If two or more threads call one or more -** [sqlite3_column_database_name | column metadata interfaces] -** for the same [prepared statement] and result column -** at the same time then the results are undefined. +** If two or more threads call one or more +** [sqlite3_column_database_name | column metadata interfaces] +** for the same [prepared statement] and result column +** at the same time then the results are undefined. */ const char *sqlite3_column_database_name(sqlite3_stmt*,int); const void *sqlite3_column_database_name16(sqlite3_stmt*,int); @@ -3554,26 +2691,8 @@ const void *sqlite3_column_origin_name16(sqlite3_stmt*,int); ** is associated with individual values, not with the containers ** used to hold those values. ** -** INVARIANTS: -** -** {H13761} A successful call to [sqlite3_column_decltype(S,N)] returns a -** zero-terminated UTF-8 string containing the declared datatype -** of the table column that appears as the Nth column (numbered -** from 0) of the result set to the [prepared statement] S. -** -** {H13762} A successful call to [sqlite3_column_decltype16(S,N)] -** returns a zero-terminated UTF-16 native byte order string -** containing the declared datatype of the table column that appears -** as the Nth column (numbered from 0) of the result set to the -** [prepared statement] S. -** -** {H13763} If N is less than 0 or N is greater than or equal to -** the number of columns in the [prepared statement] S, -** or if the Nth column of S is an expression or subquery rather -** than a table column, or if a memory allocation failure -** occurs during encoding conversions, then -** calls to [sqlite3_column_decltype(S,N)] or -** [sqlite3_column_decltype16(S,N)] return NULL. +** Requirements: +** [H13761] [H13762] [H13763] */ const char *sqlite3_column_decltype(sqlite3_stmt*,int); const void *sqlite3_column_decltype16(sqlite3_stmt*,int); @@ -3643,31 +2762,8 @@ const void *sqlite3_column_decltype16(sqlite3_stmt*,int); ** then the more specific [error codes] are returned directly ** by sqlite3_step(). The use of the "v2" interface is recommended. ** -** INVARIANTS: -** -** {H13202} If the [prepared statement] S is ready to be run, then -** [sqlite3_step(S)] advances that prepared statement until -** completion or until it is ready to return another row of the -** result set, or until an [sqlite3_interrupt | interrupt] -** or a run-time error occurs. -** -** {H15304} When a call to [sqlite3_step(S)] causes the [prepared statement] -** S to run to completion, the function returns [SQLITE_DONE]. -** -** {H15306} When a call to [sqlite3_step(S)] stops because it is ready to -** return another row of the result set, it returns [SQLITE_ROW]. -** -** {H15308} If a call to [sqlite3_step(S)] encounters an -** [sqlite3_interrupt | interrupt] or a run-time error, -** it returns an appropriate error code that is not one of -** [SQLITE_OK], [SQLITE_ROW], or [SQLITE_DONE]. -** -** {H15310} If an [sqlite3_interrupt | interrupt] or a run-time error -** occurs during a call to [sqlite3_step(S)] -** for a [prepared statement] S created using -** legacy interfaces [sqlite3_prepare()] or -** [sqlite3_prepare16()], then the function returns either -** [SQLITE_ERROR], [SQLITE_BUSY], or [SQLITE_MISUSE]. +** Requirements: +** [H13202] [H15304] [H15306] [H15308] [H15310] */ int sqlite3_step(sqlite3_stmt*); @@ -3676,17 +2772,8 @@ int sqlite3_step(sqlite3_stmt*); ** ** Returns the number of values in the current row of the result set. ** -** INVARIANTS: -** -** {H13771} After a call to [sqlite3_step(S)] that returns [SQLITE_ROW], -** the [sqlite3_data_count(S)] routine will return the same value -** as the [sqlite3_column_count(S)] function. -** -** {H13772} After [sqlite3_step(S)] has returned any value other than -** [SQLITE_ROW] or before [sqlite3_step(S)] has been called on the -** [prepared statement] for the first time since it was -** [sqlite3_prepare | prepared] or [sqlite3_reset | reset], -** the [sqlite3_data_count(S)] routine returns zero. +** Requirements: +** [H13771] [H13772] */ int sqlite3_data_count(sqlite3_stmt *pStmt); @@ -3874,60 +2961,9 @@ int sqlite3_data_count(sqlite3_stmt *pStmt); ** pointer. Subsequent calls to [sqlite3_errcode()] will return ** [SQLITE_NOMEM]. ** -** INVARIANTS: -** -** {H13803} The [sqlite3_column_blob(S,N)] interface converts the -** Nth column in the current row of the result set for -** the [prepared statement] S into a BLOB and then returns a -** pointer to the converted value. -** -** {H13806} The [sqlite3_column_bytes(S,N)] interface returns the -** number of bytes in the BLOB or string (exclusive of the -** zero terminator on the string) that was returned by the -** most recent call to [sqlite3_column_blob(S,N)] or -** [sqlite3_column_text(S,N)]. -** -** {H13809} The [sqlite3_column_bytes16(S,N)] interface returns the -** number of bytes in the string (exclusive of the -** zero terminator on the string) that was returned by the -** most recent call to [sqlite3_column_text16(S,N)]. -** -** {H13812} The [sqlite3_column_double(S,N)] interface converts the -** Nth column in the current row of the result set for the -** [prepared statement] S into a floating point value and -** returns a copy of that value. -** -** {H13815} The [sqlite3_column_int(S,N)] interface converts the -** Nth column in the current row of the result set for the -** [prepared statement] S into a 64-bit signed integer and -** returns the lower 32 bits of that integer. -** -** {H13818} The [sqlite3_column_int64(S,N)] interface converts the -** Nth column in the current row of the result set for the -** [prepared statement] S into a 64-bit signed integer and -** returns a copy of that integer. -** -** {H13821} The [sqlite3_column_text(S,N)] interface converts the -** Nth column in the current row of the result set for -** the [prepared statement] S into a zero-terminated UTF-8 -** string and returns a pointer to that string. -** -** {H13824} The [sqlite3_column_text16(S,N)] interface converts the -** Nth column in the current row of the result set for the -** [prepared statement] S into a zero-terminated 2-byte -** aligned UTF-16 native byte order string and returns -** a pointer to that string. -** -** {H13827} The [sqlite3_column_type(S,N)] interface returns -** one of [SQLITE_NULL], [SQLITE_INTEGER], [SQLITE_FLOAT], -** [SQLITE_TEXT], or [SQLITE_BLOB] as appropriate for -** the Nth column in the current row of the result set for -** the [prepared statement] S. -** -** {H13830} The [sqlite3_column_value(S,N)] interface returns a -** pointer to an [unprotected sqlite3_value] object for the -** Nth column in the current row of the result set for -** the [prepared statement] S. +** Requirements: +** [H13803] [H13806] [H13809] [H13812] [H13815] [H13818] [H13821] [H13824] +** [H13827] [H13830] */ const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); int sqlite3_column_bytes(sqlite3_stmt*, int iCol); @@ -3956,15 +2992,8 @@ sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); ** depending on the circumstances, and the ** [error code] returned will be [SQLITE_ABORT]. ** -** INVARIANTS: -** -** {H11302} The [sqlite3_finalize(S)] interface destroys the -** [prepared statement] S and releases all -** memory and file resources held by that object. -** -** {H11304} If the most recent call to [sqlite3_step(S)] for the -** [prepared statement] S returned an error, -** then [sqlite3_finalize(S)] returns that same error. +** Requirements: +** [H11302] [H11304] */ int sqlite3_finalize(sqlite3_stmt *pStmt); @@ -4071,70 +3100,9 @@ int sqlite3_reset(sqlite3_stmt *pStmt); ** close the database connection nor finalize or reset the prepared ** statement in which the function is running. ** -** INVARIANTS: -** -** {H16103} The [sqlite3_create_function16(D,X,...)] interface shall behave -** as [sqlite3_create_function(D,X,...)] in every way except that it -** interprets the X argument as zero-terminated UTF-16 -** native byte order instead of as zero-terminated UTF-8. -** -** {H16106} A successful invocation of the -** [sqlite3_create_function(D,X,N,E,...)] interface shall register -** or replaces callback functions in the [database connection] D -** used to implement the SQL function named X with N parameters -** and having a preferred text encoding of E. -** -** {H16109} A successful call to [sqlite3_create_function(D,X,N,E,P,F,S,L)] -** shall replace the P, F, S, and L values from any prior calls with -** the same D, X, N, and E values. -** -** {H16112} The [sqlite3_create_function(D,X,...)] interface shall fail -** if the SQL function name X is -** longer than 255 bytes exclusive of the zero terminator. -** -** {H16118} The [sqlite3_create_function(D,X,N,E,P,F,S,L)] interface -** shall fail unless either F is NULL and S and L are non-NULL or -*** F is non-NULL and S and L are NULL. -** -** {H16121} The [sqlite3_create_function(D,...)] interface shall fails with an -** error code of [SQLITE_BUSY] if there exist [prepared statements] -** associated with the [database connection] D. -** -** {H16124} The [sqlite3_create_function(D,X,N,...)] interface shall fail with -** an error code of [SQLITE_ERROR] if parameter N is less -** than -1 or greater than 127. -** -** {H16127} When N is non-negative, the [sqlite3_create_function(D,X,N,...)] -** interface shall register callbacks to be invoked for the -** SQL function -** named X when the number of arguments to the SQL function is -** exactly N. -** -** {H16130} When N is -1, the [sqlite3_create_function(D,X,N,...)] -** interface shall register callbacks to be invoked for the SQL -** function named X with any number of arguments. -** -** {H16133} When calls to [sqlite3_create_function(D,X,N,...)] -** specify multiple implementations of the same function X -** and when one implementation has N>=0 and the other has N=(-1) -** the implementation with a non-zero N shall be preferred. -** -** {H16136} When calls to [sqlite3_create_function(D,X,N,E,...)] -** specify multiple implementations of the same function X with -** the same number of arguments N but with different -** encodings E, then the implementation where E matches the -** database encoding shall preferred. -** -** {H16139} For an aggregate SQL function created using -** [sqlite3_create_function(D,X,N,E,P,0,S,L)] the finalizer -** function L shall always be invoked exactly once if the -** step function S is called one or more times. -** -** {H16142} When SQLite invokes either the xFunc or xStep function of -** an application-defined SQL function or aggregate created -** by [sqlite3_create_function()] or [sqlite3_create_function16()], -** then the array of [sqlite3_value] objects passed as the -** third parameter shall be [protected sqlite3_value] objects. +** Requirements: +** [H16103] [H16106] [H16109] [H16112] [H16118] [H16121] [H16124] [H16127] +** [H16130] [H16133] [H16136] [H16139] [H16142] */ int sqlite3_create_function( sqlite3 *db, @@ -4234,67 +3202,9 @@ SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),void ** These routines must be called from the same thread as ** the SQL function that supplied the [sqlite3_value*] parameters. ** -** INVARIANTS: -** -** {H15103} The [sqlite3_value_blob(V)] interface converts the -** [protected sqlite3_value] object V into a BLOB and then -** returns a pointer to the converted value. -** -** {H15106} The [sqlite3_value_bytes(V)] interface returns the -** number of bytes in the BLOB or string (exclusive of the -** zero terminator on the string) that was returned by the -** most recent call to [sqlite3_value_blob(V)] or -** [sqlite3_value_text(V)]. -** -** {H15109} The [sqlite3_value_bytes16(V)] interface returns the -** number of bytes in the string (exclusive of the -** zero terminator on the string) that was returned by the -** most recent call to [sqlite3_value_text16(V)], -** [sqlite3_value_text16be(V)], or [sqlite3_value_text16le(V)]. -** -** {H15112} The [sqlite3_value_double(V)] interface converts the -** [protected sqlite3_value] object V into a floating point value and -** returns a copy of that value. -** -** {H15115} The [sqlite3_value_int(V)] interface converts the -** [protected sqlite3_value] object V into a 64-bit signed integer and -** returns the lower 32 bits of that integer. -** -** {H15118} The [sqlite3_value_int64(V)] interface converts the -** [protected sqlite3_value] object V into a 64-bit signed integer and -** returns a copy of that integer. -** -** {H15121} The [sqlite3_value_text(V)] interface converts the -** [protected sqlite3_value] object V into a zero-terminated UTF-8 -** string and returns a pointer to that string. -** -** {H15124} The [sqlite3_value_text16(V)] interface converts the -** [protected sqlite3_value] object V into a zero-terminated 2-byte -** aligned UTF-16 native byte order -** string and returns a pointer to that string. -** -** {H15127} The [sqlite3_value_text16be(V)] interface converts the -** [protected sqlite3_value] object V into a zero-terminated 2-byte -** aligned UTF-16 big-endian -** string and returns a pointer to that string. -** -** {H15130} The [sqlite3_value_text16le(V)] interface converts the -** [protected sqlite3_value] object V into a zero-terminated 2-byte -** aligned UTF-16 little-endian -** string and returns a pointer to that string. -** -** {H15133} The [sqlite3_value_type(V)] interface returns -** one of [SQLITE_NULL], [SQLITE_INTEGER], [SQLITE_FLOAT], -** [SQLITE_TEXT], or [SQLITE_BLOB] as appropriate for -** the [sqlite3_value] object V. -** -** {H15136} The [sqlite3_value_numeric_type(V)] interface converts -** the [protected sqlite3_value] object V into either an integer or -** a floating point value if it can do so without loss of -** information, and returns one of [SQLITE_NULL], -** [SQLITE_INTEGER], [SQLITE_FLOAT], [SQLITE_TEXT], or -** [SQLITE_BLOB] as appropriate for the -** [protected sqlite3_value] object V after the conversion attempt. +** Requirements: +** [H15103] [H15106] [H15109] [H15112] [H15115] [H15118] [H15121] [H15124] +** [H15127] [H15130] [H15133] [H15136] */ const void *sqlite3_value_blob(sqlite3_value*); int sqlite3_value_bytes(sqlite3_value*); @@ -4332,25 +3242,8 @@ int sqlite3_value_numeric_type(sqlite3_value*); ** This routine must be called from the same thread in which ** the aggregate SQL function is running. ** -** INVARIANTS: -** -** {H16211} The first invocation of [sqlite3_aggregate_context(C,N)] for -** a particular instance of an aggregate function (for a particular -** context C) causes SQLite to allocate N bytes of memory, -** zero that memory, and return a pointer to the allocated memory. -** -** {H16213} If a memory allocation error occurs during -** [sqlite3_aggregate_context(C,N)] then the function returns 0. -** -** {H16215} Second and subsequent invocations of -** [sqlite3_aggregate_context(C,N)] for the same context pointer C -** ignore the N parameter and return a pointer to the same -** block of memory returned by the first invocation. -** -** {H16217} The memory allocated by [sqlite3_aggregate_context(C,N)] is -** automatically freed on the next call to [sqlite3_reset()] -** or [sqlite3_finalize()] for the [prepared statement] containing -** the aggregate function associated with context C. +** Requirements: +** [H16211] [H16213] [H16215] [H16217] */ void *sqlite3_aggregate_context(sqlite3_context*, int nBytes); @@ -4366,12 +3259,8 @@ void *sqlite3_aggregate_context(sqlite3_context*, int nBytes); ** This routine must be called from the same thread in which ** the application-defined function is running. ** -** INVARIANTS: -** -** {H16243} The [sqlite3_user_data(C)] interface returns a copy of the -** P pointer from the [sqlite3_create_function(D,X,N,E,P,F,S,L)] -** or [sqlite3_create_function16(D,X,N,E,P,F,S,L)] call that -** registered the SQL function associated with [sqlite3_context] C. +** Requirements: +** [H16243] */ void *sqlite3_user_data(sqlite3_context*); @@ -4384,12 +3273,8 @@ void *sqlite3_user_data(sqlite3_context*); ** and [sqlite3_create_function16()] routines that originally ** registered the application defined function. ** -** INVARIANTS: -** -** {H16253} The [sqlite3_context_db_handle(C)] interface returns a copy of the -** D pointer from the [sqlite3_create_function(D,X,N,E,P,F,S,L)] -** or [sqlite3_create_function16(D,X,N,E,P,F,S,L)] call that -** registered the SQL function associated with [sqlite3_context] C. +** Requirements: +** [H16253] */ sqlite3 *sqlite3_context_db_handle(sqlite3_context*); @@ -4435,31 +3320,8 @@ sqlite3 *sqlite3_context_db_handle(sqlite3_context*); ** These routines must be called from the same thread in which ** the SQL function is running. ** -** INVARIANTS: -** -** {H16272} The [sqlite3_get_auxdata(C,N)] interface returns a pointer -** to metadata associated with the Nth parameter of the SQL function -** whose context is C, or NULL if there is no metadata associated -** with that parameter. -** -** {H16274} The [sqlite3_set_auxdata(C,N,P,D)] interface assigns a metadata -** pointer P to the Nth parameter of the SQL function with context C. -** -** {H16276} SQLite will invoke the destructor D with a single argument -** which is the metadata pointer P following a call to -** [sqlite3_set_auxdata(C,N,P,D)] when SQLite ceases to hold -** the metadata. -** -** {H16277} SQLite ceases to hold metadata for an SQL function parameter -** when the value of that parameter changes. -** -** {H16278} When [sqlite3_set_auxdata(C,N,P,D)] is invoked, the destructor -** is called for any prior metadata associated with the same function -** context C and parameter N. -** -** {H16279} SQLite will call destructors for any metadata it is holding -** in a particular [prepared statement] S when either -** [sqlite3_reset(S)] or [sqlite3_finalize(S)] is called. +** Requirements: +** [H16272] [H16274] [H16276] [H16277] [H16278] [H16279] */ void *sqlite3_get_auxdata(sqlite3_context*, int N); void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); @@ -4587,101 +3449,10 @@ typedef void (*sqlite3_destructor_type)(void*); ** than the one containing the application-defined function that received ** the [sqlite3_context] pointer, the results are undefined. ** -** INVARIANTS: -** -** {H16403} The default return value from any SQL function is NULL. -** -** {H16406} The [sqlite3_result_blob(C,V,N,D)] interface changes the -** return value of function C to be a BLOB that is N bytes -** in length and with content pointed to by V. -** -** {H16409} The [sqlite3_result_double(C,V)] interface changes the -** return value of function C to be the floating point value V. -** -** {H16412} The [sqlite3_result_error(C,V,N)] interface changes the return -** value of function C to be an exception with error code -** [SQLITE_ERROR] and a UTF-8 error message copied from V up to the -** first zero byte or until N bytes are read if N is positive. -** -** {H16415} The [sqlite3_result_error16(C,V,N)] interface changes the return -** value of function C to be an exception with error code -** [SQLITE_ERROR] and a UTF-16 native byte order error message -** copied from V up to the first zero terminator or until N bytes -** are read if N is positive. -** -** {H16418} The [sqlite3_result_error_toobig(C)] interface changes the return -** value of the function C to be an exception with error code -** [SQLITE_TOOBIG] and an appropriate error message. -** -** {H16421} The [sqlite3_result_error_nomem(C)] interface changes the return -** value of the function C to be an exception with error code -** [SQLITE_NOMEM] and an appropriate error message. -** -** {H16424} The [sqlite3_result_error_code(C,E)] interface changes the return -** value of the function C to be an exception with error code E. -** The error message text is unchanged. -** -** {H16427} The [sqlite3_result_int(C,V)] interface changes the -** return value of function C to be the 32-bit integer value V. -** -** {H16430} The [sqlite3_result_int64(C,V)] interface changes the -** return value of function C to be the 64-bit integer value V. -** -** {H16433} The [sqlite3_result_null(C)] interface changes the -** return value of function C to be NULL. -** -** {H16436} The [sqlite3_result_text(C,V,N,D)] interface changes the -** return value of function C to be the UTF-8 string -** V up to the first zero if N is negative -** or the first N bytes of V if N is non-negative. -** -** {H16439} The [sqlite3_result_text16(C,V,N,D)] interface changes the -** return value of function C to be the UTF-16 native byte order -** string V up to the first zero if N is negative -** or the first N bytes of V if N is non-negative. -** -** {H16442} The [sqlite3_result_text16be(C,V,N,D)] interface changes the -** return value of function C to be the UTF-16 big-endian -** string V up to the first zero if N is negative -** or the first N bytes or V if N is non-negative. -** -** {H16445} The [sqlite3_result_text16le(C,V,N,D)] interface changes the -** return value of function C to be the UTF-16 little-endian -** string V up to the first zero if N is negative -** or the first N bytes of V if N is non-negative. -** -** {H16448} The [sqlite3_result_value(C,V)] interface changes the -** return value of function C to be the [unprotected sqlite3_value] -** object V. -** -** {H16451} The [sqlite3_result_zeroblob(C,N)] interface changes the -** return value of function C to be an N-byte BLOB of all zeros. -** -** {H16454} The [sqlite3_result_error()] and [sqlite3_result_error16()] -** interfaces make a copy of their error message strings before -** returning. -** -** {H16457} If the D destructor parameter to [sqlite3_result_blob(C,V,N,D)], -** [sqlite3_result_text(C,V,N,D)], [sqlite3_result_text16(C,V,N,D)], -** [sqlite3_result_text16be(C,V,N,D)], or -** [sqlite3_result_text16le(C,V,N,D)] is the constant [SQLITE_STATIC] -** then no destructor is ever called on the pointer V and SQLite -** assumes that V is immutable. -** -** {H16460} If the D destructor parameter to [sqlite3_result_blob(C,V,N,D)], -** [sqlite3_result_text(C,V,N,D)], [sqlite3_result_text16(C,V,N,D)], -** [sqlite3_result_text16be(C,V,N,D)], or -** [sqlite3_result_text16le(C,V,N,D)] is the constant -** [SQLITE_TRANSIENT] then the interfaces makes a copy of the -** content of V and retains the copy. -** -** {H16463} If the D destructor parameter to [sqlite3_result_blob(C,V,N,D)], -** [sqlite3_result_text(C,V,N,D)], [sqlite3_result_text16(C,V,N,D)], -** [sqlite3_result_text16be(C,V,N,D)], or -** [sqlite3_result_text16le(C,V,N,D)] is some value other than -** the constants [SQLITE_STATIC] and [SQLITE_TRANSIENT] then -** SQLite will invoke the destructor D with V as its only argument -** when it has finished with the V value. +** Requirements: +** [H16403] [H16406] [H16409] [H16412] [H16415] [H16418] [H16421] [H16424] +** [H16427] [H16430] [H16433] [H16436] [H16439] [H16442] [H16445] [H16448] +** [H16451] [H16454] [H16457] [H16460] [H16463] */ void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*)); void sqlite3_result_double(sqlite3_context*, double); @@ -4742,54 +3513,9 @@ void sqlite3_result_zeroblob(sqlite3_context*, int n); ** collation creation functions or when the [database connection] is closed ** using [sqlite3_close()]. ** -** INVARIANTS: -** -** {H16603} A successful call to the -** [sqlite3_create_collation_v2(B,X,E,P,F,D)] interface -** registers function F as the comparison function used to -** implement collation X on the [database connection] B for -** databases having encoding E. -** -** {H16604} SQLite understands the X parameter to -** [sqlite3_create_collation_v2(B,X,E,P,F,D)] as a zero-terminated -** UTF-8 string in which case is ignored for ASCII characters and -** is significant for non-ASCII characters. -** -** {H16606} Successive calls to [sqlite3_create_collation_v2(B,X,E,P,F,D)] -** with the same values for B, X, and E, override prior values -** of P, F, and D. -** -** {H16609} If the destructor D in [sqlite3_create_collation_v2(B,X,E,P,F,D)] -** is not NULL then it is called with argument P when the -** collating function is dropped by SQLite. -** -** {H16612} A collating function is dropped when it is overloaded. -** -** {H16615} A collating function is dropped when the database connection -** is closed using [sqlite3_close()]. -** -** {H16618} The pointer P in [sqlite3_create_collation_v2(B,X,E,P,F,D)] -** is passed through as the first parameter to the comparison -** function F for all subsequent invocations of F. -** -** {H16621} A call to [sqlite3_create_collation(B,X,E,P,F)] is exactly -** the same as a call to [sqlite3_create_collation_v2()] with -** the same parameters and a NULL destructor. -** -** {H16624} Following a [sqlite3_create_collation_v2(B,X,E,P,F,D)], -** SQLite uses the comparison function F for all text comparison -** operations on the [database connection] B on text values that -** use the collating sequence named X. -** -** {H16627} The [sqlite3_create_collation16(B,X,E,P,F)] works the same -** as [sqlite3_create_collation(B,X,E,P,F)] except that the -** collation name X is understood as UTF-16 in native byte order -** instead of UTF-8. -** -** {H16630} When multiple comparison functions are available for the same -** collating sequence, SQLite chooses the one whose text encoding -** requires the least amount of conversion from the default -** text encoding of the database. +** Requirements: +** [H16603] [H16604] [H16606] [H16609] [H16612] [H16615] [H16618] [H16621] +** [H16624] [H16627] [H16630] */ int sqlite3_create_collation( sqlite3*, @@ -4840,24 +3566,8 @@ int sqlite3_create_collation16( ** [sqlite3_create_collation()], [sqlite3_create_collation16()], or ** [sqlite3_create_collation_v2()]. ** -** INVARIANTS: -** -** {H16702} A successful call to [sqlite3_collation_needed(D,P,F)] -** or [sqlite3_collation_needed16(D,P,F)] causes -** the [database connection] D to invoke callback F with first -** parameter P whenever it needs a comparison function for a -** collating sequence that it does not know about. -** -** {H16704} Each successful call to [sqlite3_collation_needed()] or -** [sqlite3_collation_needed16()] overrides the callback registered -** on the same [database connection] by prior calls to either -** interface. -** -** {H16706} The name of the requested collating function passed in the -** 4th parameter to the callback is in UTF-8 if the callback -** was registered using [sqlite3_collation_needed()] and -** is in UTF-16 native byte order if the callback was -** registered using [sqlite3_collation_needed16()]. +** Requirements: +** [H16702] [H16704] [H16706] */ int sqlite3_collation_needed( sqlite3*, @@ -4909,16 +3619,7 @@ int sqlite3_rekey( ** SQLite implements this interface by calling the xSleep() ** method of the default [sqlite3_vfs] object. ** -** INVARIANTS: -** -** {H10533} The [sqlite3_sleep(M)] interface invokes the xSleep -** method of the default [sqlite3_vfs|VFS] in order to -** suspend execution of the current thread for at least -** M milliseconds. -** -** {H10536} The [sqlite3_sleep(M)] interface returns the number of -** milliseconds of sleep actually requested of the operating -** system, which might be larger than the parameter M. +** Requirements: [H10533] [H10536] */ int sqlite3_sleep(int); @@ -4931,10 +3632,24 @@ int sqlite3_sleep(int); ** is a NULL pointer, then SQLite performs a search for an appropriate ** temporary file directory. ** -** It is not safe to modify this variable once a [database connection] -** has been opened. It is intended that this variable be set once +** It is not safe to read or modify this variable in more than one +** thread at a time. It is not safe to read or modify this variable +** if a [database connection] is being used at the same time in a separate +** thread. +** It is intended that this variable be set once ** as part of process initialization and before any SQLite interface -** routines have been call and remain unchanged thereafter. +** routines have been called and that this variable remain unchanged +** thereafter. +** +** The [temp_store_directory pragma] may modify this variable and cause +** it to point to memory obtained from [sqlite3_malloc]. Furthermore, +** the [temp_store_directory pragma] always assumes that any string +** that this variable points to is held in memory obtained from +** [sqlite3_malloc] and the pragma may attempt to free that memory +** using [sqlite3_free]. +** Hence, if this variable is modified directly, either it should be +** made NULL or made to point to memory obtained from [sqlite3_malloc] +** or else the use of the [temp_store_directory pragma] should be avoided. */ SQLITE_EXTERN char *sqlite3_temp_directory; @@ -4955,24 +3670,11 @@ SQLITE_EXTERN char *sqlite3_temp_directory; ** find out whether SQLite automatically rolled back the transaction after ** an error is to use this function. ** -** INVARIANTS: +** If another thread changes the autocommit status of the database +** connection while this routine is running, then the return value +** is undefined. ** -** {H12931} The [sqlite3_get_autocommit(D)] interface returns non-zero or -** zero if the [database connection] D is or is not in autocommit -** mode, respectively. -** -** {H12932} Autocommit mode is on by default. -** -** {H12933} Autocommit mode is disabled by a successful [BEGIN] statement. -** -** {H12934} Autocommit mode is enabled by a successful [COMMIT] or [ROLLBACK] -** statement. -** -** ASSUMPTIONS: -** -** {A12936} If another thread changes the autocommit status of the database -** connection while this routine is running, then the return value -** is undefined. +** Requirements: [H12931] [H12932] [H12933] [H12934] */ int sqlite3_get_autocommit(sqlite3*); @@ -4985,11 +3687,7 @@ int sqlite3_get_autocommit(sqlite3*); ** to the [sqlite3_prepare_v2()] call (or its variants) that was used to ** create the statement in the first place. ** -** INVARIANTS: -** -** {H13123} The [sqlite3_db_handle(S)] interface returns a pointer -** to the [database connection] associated with the -** [prepared statement] S. +** Requirements: [H13123] */ sqlite3 *sqlite3_db_handle(sqlite3_stmt*); @@ -5002,31 +3700,11 @@ sqlite3 *sqlite3_db_handle(sqlite3_stmt*); ** associated with the database connection pDb. If no prepared statement ** satisfies the conditions of this routine, it returns NULL. ** -** INVARIANTS: +** The [database connection] pointer D in a call to +** [sqlite3_next_stmt(D,S)] must refer to an open database +** connection and in particular must not be a NULL pointer. ** -** {H13143} If D is a [database connection] that holds one or more -** unfinalized [prepared statements] and S is a NULL pointer, -** then [sqlite3_next_stmt(D, S)] routine shall return a pointer -** to one of the prepared statements associated with D. -** -** {H13146} If D is a [database connection] that holds no unfinalized -** [prepared statements] and S is a NULL pointer, then -** [sqlite3_next_stmt(D, S)] routine shall return a NULL pointer. -** -** {H13149} If S is a [prepared statement] in the [database connection] D -** and S is not the last prepared statement in D, then -** [sqlite3_next_stmt(D, S)] routine shall return a pointer -** to the next prepared statement in D after S. -** -** {H13152} If S is the last [prepared statement] in the -** [database connection] D then the [sqlite3_next_stmt(D, S)] -** routine shall return a NULL pointer. -** -** ASSUMPTIONS: -** -** {A13154} The [database connection] pointer D in a call to -** [sqlite3_next_stmt(D,S)] must refer to an open database -** connection and in particular must not be a NULL pointer. +** Requirements: [H13143] [H13146] [H13149] [H13152] */ sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); @@ -5067,41 +3745,9 @@ sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); ** rolled back because a commit callback returned non-zero. ** Check on this ** -** INVARIANTS: -** -** {H12951} The [sqlite3_commit_hook(D,F,P)] interface registers the -** callback function F to be invoked with argument P whenever -** a transaction commits on the [database connection] D. -** -** {H12952} The [sqlite3_commit_hook(D,F,P)] interface returns the P argument -** from the previous call with the same [database connection] D, -** or NULL on the first call for a particular database connection D. -** -** {H12953} Each call to [sqlite3_commit_hook()] overwrites the callback -** registered by prior calls. -** -** {H12954} If the F argument to [sqlite3_commit_hook(D,F,P)] is NULL -** then the commit hook callback is canceled and no callback -** is invoked when a transaction commits. -** -** {H12955} If the commit callback returns non-zero then the commit is -** converted into a rollback. -** -** {H12961} The [sqlite3_rollback_hook(D,F,P)] interface registers the -** callback function F to be invoked with argument P whenever -** a transaction rolls back on the [database connection] D. -** -** {H12962} The [sqlite3_rollback_hook(D,F,P)] interface returns the P -** argument from the previous call with the same -** [database connection] D, or NULL on the first call -** for a particular database connection D. -** -** {H12963} Each call to [sqlite3_rollback_hook()] overwrites the callback -** registered by prior calls. -** -** {H12964} If the F argument to [sqlite3_rollback_hook(D,F,P)] is NULL -** then the rollback hook callback is canceled and no callback -** is invoked when a transaction rolls back. +** Requirements: +** [H12951] [H12952] [H12953] [H12954] [H12955] +** [H12961] [H12962] [H12963] [H12964] */ void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); @@ -5140,36 +3786,8 @@ void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); ** If another function was previously registered, its pArg value ** is returned. Otherwise NULL is returned. ** -** INVARIANTS: -** -** {H12971} The [sqlite3_update_hook(D,F,P)] interface causes the callback -** function F to be invoked with first parameter P whenever -** a table row is modified, inserted, or deleted on -** the [database connection] D. -** -** {H12973} The [sqlite3_update_hook(D,F,P)] interface returns the value -** of P for the previous call on the same [database connection] D, -** or NULL for the first call. -** -** {H12975} If the update hook callback F in [sqlite3_update_hook(D,F,P)] -** is NULL then the no update callbacks are made. -** -** {H12977} Each call to [sqlite3_update_hook(D,F,P)] overrides prior calls -** to the same interface on the same [database connection] D. -** -** {H12979} The update hook callback is not invoked when internal system -** tables such as sqlite_master and sqlite_sequence are modified. -** -** {H12981} The second parameter to the update callback -** is one of [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE], -** depending on the operation that caused the callback to be invoked. -** -** {H12983} The third and fourth arguments to the callback contain pointers -** to zero-terminated UTF-8 strings which are the names of the -** database and table that is being updated. - -** {H12985} The final callback parameter is the [rowid] of the row after -** the change occurs. +** Requirements: +** [H12971] [H12973] [H12975] [H12977] [H12979] [H12981] [H12983] [H12986] */ void *sqlite3_update_hook( sqlite3*, @@ -5208,19 +3826,7 @@ void *sqlite3_update_hook( ** ** See Also: [SQLite Shared-Cache Mode] ** -** INVARIANTS: -** -** {H10331} A successful invocation of [sqlite3_enable_shared_cache(B)] -** will enable or disable shared cache mode for any subsequently -** created [database connection] in the same process. -** -** {H10336} When shared cache is enabled, the [sqlite3_create_module()] -** interface will always return an error. -** -** {H10337} The [sqlite3_enable_shared_cache(B)] interface returns -** [SQLITE_OK] if shared cache was enabled or disabled successfully. -** -** {H10339} Shared cache is disabled by default. +** Requirements: [H10331] [H10336] [H10337] [H10339] */ int sqlite3_enable_shared_cache(int); @@ -5234,15 +3840,7 @@ int sqlite3_enable_shared_cache(int); ** sqlite3_release_memory() returns the number of bytes actually freed, ** which might be more or less than the amount requested. ** -** INVARIANTS: -** -** {H17341} The [sqlite3_release_memory(N)] interface attempts to -** free N bytes of heap memory by deallocating non-essential -** memory allocations held by the database library. -** -** {H16342} The [sqlite3_release_memory(N)] returns the number -** of bytes actually freed, which might be more or less -** than the amount requested. +** Requirements: [H17341] [H17342] */ int sqlite3_release_memory(int); @@ -5276,33 +3874,8 @@ int sqlite3_release_memory(int); ** version 3.5.0 there is no mechanism for limiting the heap usage for ** individual threads. ** -** INVARIANTS: -** -** {H16351} The [sqlite3_soft_heap_limit(N)] interface places a soft limit -** of N bytes on the amount of heap memory that may be allocated -** using [sqlite3_malloc()] or [sqlite3_realloc()] at any point -** in time. -** -** {H16352} If a call to [sqlite3_malloc()] or [sqlite3_realloc()] would -** cause the total amount of allocated memory to exceed the -** soft heap limit, then [sqlite3_release_memory()] is invoked -** in an attempt to reduce the memory usage prior to proceeding -** with the memory allocation attempt. -** -** {H16353} Calls to [sqlite3_malloc()] or [sqlite3_realloc()] that trigger -** attempts to reduce memory usage through the soft heap limit -** mechanism continue even if the attempt to reduce memory -** usage is unsuccessful. -** -** {H16354} A negative or zero value for N in a call to -** [sqlite3_soft_heap_limit(N)] means that there is no soft -** heap limit and [sqlite3_release_memory()] will only be -** called when memory is completely exhausted. -** -** {H16355} The default value for the soft heap limit is zero. -** -** {H16358} Each call to [sqlite3_soft_heap_limit(N)] overrides the -** values set by all prior calls. +** Requirements: +** [H16351] [H16352] [H16353] [H16354] [H16355] [H16358] */ void sqlite3_soft_heap_limit(int); @@ -5802,34 +4375,8 @@ typedef struct sqlite3_blob sqlite3_blob; ** rollback by the expiration of the BLOB. Such changes will eventually ** commit if the transaction continues to completion. ** -** INVARIANTS: -** -** {H17813} A successful invocation of the [sqlite3_blob_open(D,B,T,C,R,F,P)] -** interface shall open an [sqlite3_blob] object P on the BLOB -** in column C of the table T in the database B on -** the [database connection] D. -** -** {H17814} A successful invocation of [sqlite3_blob_open(D,...)] shall start -** a new transaction on the [database connection] D if that -** connection is not already in a transaction. -** -** {H17816} The [sqlite3_blob_open(D,B,T,C,R,F,P)] interface shall open -** the BLOB for read and write access if and only if the F -** parameter is non-zero. -** -** {H17819} The [sqlite3_blob_open()] interface shall return [SQLITE_OK] on -** success and an appropriate [error code] on failure. -** -** {H17821} If an error occurs during evaluation of [sqlite3_blob_open(D,...)] -** then subsequent calls to [sqlite3_errcode(D)], -** [sqlite3_extended_errcode()], -** [sqlite3_errmsg(D)], and [sqlite3_errmsg16(D)] shall return -** information appropriate for that error. -** -** {H17824} If any column in the row that a [sqlite3_blob] has open is -** changed by a separate [UPDATE] or [DELETE] statement or by -** an [ON CONFLICT] side effect, then the [sqlite3_blob] shall -** be marked as invalid. +** Requirements: +** [H17813] [H17814] [H17816] [H17819] [H17821] [H17824] */ int sqlite3_blob_open( sqlite3*, @@ -5860,20 +4407,8 @@ int sqlite3_blob_open( ** The BLOB is closed unconditionally. Even if this routine returns ** an error code, the BLOB is still closed. ** -** INVARIANTS: -** -** {H17833} The [sqlite3_blob_close(P)] interface closes an [sqlite3_blob] -** object P previously opened using [sqlite3_blob_open()]. -** -** {H17836} Closing an [sqlite3_blob] object using -** [sqlite3_blob_close()] shall cause the current transaction to -** commit if there are no other open [sqlite3_blob] objects -** or [prepared statements] on the same [database connection] and -** the database connection is in [autocommit mode]. -** -** {H17839} The [sqlite3_blob_close(P)] interfaces shall close the -** [sqlite3_blob] object P unconditionally, even if -** [sqlite3_blob_close(P)] returns something other than [SQLITE_OK]. +** Requirements: +** [H17833] [H17836] [H17839] */ int sqlite3_blob_close(sqlite3_blob *); @@ -5883,11 +4418,8 @@ int sqlite3_blob_close(sqlite3_blob *); ** Returns the size in bytes of the BLOB accessible via the open ** []BLOB handle] in its only argument. ** -** INVARIANTS: -** -** {H17843} The [sqlite3_blob_bytes(P)] interface returns the size -** in bytes of the BLOB that the [sqlite3_blob] object P -** refers to. +** Requirements: +** [H17843] */ int sqlite3_blob_bytes(sqlite3_blob *); @@ -5908,38 +4440,8 @@ int sqlite3_blob_bytes(sqlite3_blob *); ** On success, SQLITE_OK is returned. ** Otherwise, an [error code] or an [extended error code] is returned. ** -** INVARIANTS: -** -** {H17853} A successful invocation of [sqlite3_blob_read(P,Z,N,X)] -** shall reads N bytes of data out of the BLOB referenced by -** [BLOB handle] P beginning at offset X and store those bytes -** into buffer Z. -** -** {H17856} In [sqlite3_blob_read(P,Z,N,X)] if the size of the BLOB -** is less than N+X bytes, then the function shall leave the -** Z buffer unchanged and return [SQLITE_ERROR]. -** -** {H17859} In [sqlite3_blob_read(P,Z,N,X)] if X or N is less than zero -** then the function shall leave the Z buffer unchanged -** and return [SQLITE_ERROR]. -** -** {H17862} The [sqlite3_blob_read(P,Z,N,X)] interface shall return [SQLITE_OK] -** if N bytes are successfully read into buffer Z. -** -** {H17863} If the [BLOB handle] P is expired and X and N are within bounds -** then [sqlite3_blob_read(P,Z,N,X)] shall leave the Z buffer -** unchanged and return [SQLITE_ABORT]. -** -** {H17865} If the requested read could not be completed, -** the [sqlite3_blob_read(P,Z,N,X)] interface shall return an -** appropriate [error code] or [extended error code]. -** -** {H17868} If an error occurs during evaluation of [sqlite3_blob_read(P,...)] -** then subsequent calls to [sqlite3_errcode(D)], -** [sqlite3_extended_errcode()], -** [sqlite3_errmsg(D)], and [sqlite3_errmsg16(D)] shall return -** information appropriate for that error, where D is the -** [database connection] that was used to open the [BLOB handle] P. +** Requirements: +** [H17853] [H17856] [H17859] [H17862] [H17863] [H17865] [H17868] */ int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); @@ -5970,45 +4472,9 @@ int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); ** On success, SQLITE_OK is returned. ** Otherwise, an [error code] or an [extended error code] is returned. ** -** INVARIANTS: -** -** {H17873} A successful invocation of [sqlite3_blob_write(P,Z,N,X)] -** shall write N bytes of data from buffer Z into the BLOB -** referenced by [BLOB handle] P beginning at offset X into -** the BLOB. -** -** {H17874} In the absence of other overridding changes, the changes -** written to a BLOB by [sqlite3_blob_write()] shall -** remain in effect after the associated [BLOB handle] expires. -** -** {H17875} If the [BLOB handle] P was opened for reading only then -** an invocation of [sqlite3_blob_write(P,Z,N,X)] shall leave -** the referenced BLOB unchanged and return [SQLITE_READONLY]. -** -** {H17876} If the size of the BLOB referenced by [BLOB handle] P is -** less than N+X bytes then [sqlite3_blob_write(P,Z,N,X)] shall -** leave the BLOB unchanged and return [SQLITE_ERROR]. -** -** {H17877} If the [BLOB handle] P is expired and X and N are within bounds -** then [sqlite3_blob_read(P,Z,N,X)] shall leave the BLOB -** unchanged and return [SQLITE_ABORT]. -** -** {H17879} If X or N are less than zero then [sqlite3_blob_write(P,Z,N,X)] -** shall leave the BLOB referenced by [BLOB handle] P unchanged -** and return [SQLITE_ERROR]. -** -** {H17882} The [sqlite3_blob_write(P,Z,N,X)] interface shall return -** [SQLITE_OK] if N bytes where successfully written into the BLOB. -** -** {H17885} If the requested write could not be completed, -** the [sqlite3_blob_write(P,Z,N,X)] interface shall return an -** appropriate [error code] or [extended error code]. -** -** {H17888} If an error occurs during evaluation of [sqlite3_blob_write(D,...)] -** then subsequent calls to [sqlite3_errcode(D)], -** [sqlite3_extended_errcode()], -** [sqlite3_errmsg(D)], and [sqlite3_errmsg16(D)] shall return -** information appropriate for that error. +** Requirements: +** [H17873] [H17874] [H17875] [H17876] [H17877] [H17879] [H17882] [H17885] +** [H17888] */ int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset); @@ -6041,31 +4507,8 @@ int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset); ** If the default VFS is unregistered, another VFS is chosen as ** the default. The choice for the new VFS is arbitrary. ** -** INVARIANTS: -** -** {H11203} The [sqlite3_vfs_find(N)] interface returns a pointer to the -** registered [sqlite3_vfs] object whose name exactly matches -** the zero-terminated UTF-8 string N, or it returns NULL if -** there is no match. -** -** {H11206} If the N parameter to [sqlite3_vfs_find(N)] is NULL then -** the function returns a pointer to the default [sqlite3_vfs] -** object if there is one, or NULL if there is no default -** [sqlite3_vfs] object. -** -** {H11209} The [sqlite3_vfs_register(P,F)] interface registers the -** well-formed [sqlite3_vfs] object P using the name given -** by the zName field of the object. -** -** {H11212} Using the [sqlite3_vfs_register(P,F)] interface to register -** the same [sqlite3_vfs] object multiple times is a harmless no-op. -** -** {H11215} The [sqlite3_vfs_register(P,F)] interface makes the [sqlite3_vfs] -** object P the default [sqlite3_vfs] object if F is non-zero. -** -** {H11218} The [sqlite3_vfs_unregister(P)] interface unregisters the -** [sqlite3_vfs] object P so that it is no longer returned by -** subsequent calls to [sqlite3_vfs_find()]. +** Requirements: +** [H11203] [H11206] [H11209] [H11212] [H11215] [H11218] */ sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName); int sqlite3_vfs_register(sqlite3_vfs*, int makeDflt); @@ -6299,7 +4742,8 @@ int sqlite3_mutex_notheld(sqlite3_mutex*); #define SQLITE_MUTEX_RECURSIVE 1 #define SQLITE_MUTEX_STATIC_MASTER 2 #define SQLITE_MUTEX_STATIC_MEM 3 /* sqlite3_malloc() */ -#define SQLITE_MUTEX_STATIC_MEM2 4 /* sqlite3_release_memory() */ +#define SQLITE_MUTEX_STATIC_MEM2 4 /* NOT USED */ +#define SQLITE_MUTEX_STATIC_OPEN 4 /* sqlite3BtreeOpen() */ #define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_random() */ #define SQLITE_MUTEX_STATIC_LRU 6 /* lru page list */ #define SQLITE_MUTEX_STATIC_LRU2 7 /* lru page list */ @@ -6924,6 +5368,127 @@ int sqlite3_backup_finish(sqlite3_backup *p); int sqlite3_backup_remaining(sqlite3_backup *p); int sqlite3_backup_pagecount(sqlite3_backup *p); +/* +** CAPI3REF: Unlock Notification +** EXPERIMENTAL +** +** When running in shared-cache mode, a database operation may fail with +** an [SQLITE_LOCKED] error if the required locks on the shared-cache or +** individual tables within the shared-cache cannot be obtained. See +** [SQLite Shared-Cache Mode] for a description of shared-cache locking. +** This API may be used to register a callback that SQLite will invoke +** when the connection currently holding the required lock relinquishes it. +** This API is only available if the library was compiled with the +** [SQLITE_ENABLE_UNLOCK_NOTIFY] C-preprocessor symbol defined. +** +** See Also: [Using the SQLite Unlock Notification Feature]. +** +** Shared-cache locks are released when a database connection concludes +** its current transaction, either by committing it or rolling it back. +** +** When a connection (known as the blocked connection) fails to obtain a +** shared-cache lock and SQLITE_LOCKED is returned to the caller, the +** identity of the database connection (the blocking connection) that +** has locked the required resource is stored internally. After an +** application receives an SQLITE_LOCKED error, it may call the +** sqlite3_unlock_notify() method with the blocked connection handle as +** the first argument to register for a callback that will be invoked +** when the blocking connections current transaction is concluded. The +** callback is invoked from within the [sqlite3_step] or [sqlite3_close] +** call that concludes the blocking connections transaction. +** +** If sqlite3_unlock_notify() is called in a multi-threaded application, +** there is a chance that the blocking connection will have already +** concluded its transaction by the time sqlite3_unlock_notify() is invoked. +** If this happens, then the specified callback is invoked immediately, +** from within the call to sqlite3_unlock_notify(). +** +** If the blocked connection is attempting to obtain a write-lock on a +** shared-cache table, and more than one other connection currently holds +** a read-lock on the same table, then SQLite arbitrarily selects one of +** the other connections to use as the blocking connection. +** +** There may be at most one unlock-notify callback registered by a +** blocked connection. If sqlite3_unlock_notify() is called when the +** blocked connection already has a registered unlock-notify callback, +** then the new callback replaces the old. If sqlite3_unlock_notify() is +** called with a NULL pointer as its second argument, then any existing +** unlock-notify callback is cancelled. The blocked connections +** unlock-notify callback may also be canceled by closing the blocked +** connection using [sqlite3_close()]. +** +** The unlock-notify callback is not reentrant. If an application invokes +** any sqlite3_xxx API functions from within an unlock-notify callback, a +** crash or deadlock may be the result. +** +** Unless deadlock is detected (see below), sqlite3_unlock_notify() always +** returns SQLITE_OK. +** +** Callback Invocation Details +** +** When an unlock-notify callback is registered, the application provides a +** single void* pointer that is passed to the callback when it is invoked. +** However, the signature of the callback function allows SQLite to pass +** it an array of void* context pointers. The first argument passed to +** an unlock-notify callback is a pointer to an array of void* pointers, +** and the second is the number of entries in the array. +** +** When a blocking connections transaction is concluded, there may be +** more than one blocked connection that has registered for an unlock-notify +** callback. If two or more such blocked connections have specified the +** same callback function, then instead of invoking the callback function +** multiple times, it is invoked once with the set of void* context pointers +** specified by the blocked connections bundled together into an array. +** This gives the application an opportunity to prioritize any actions +** related to the set of unblocked database connections. +** +** Deadlock Detection +** +** Assuming that after registering for an unlock-notify callback a +** database waits for the callback to be issued before taking any further +** action (a reasonable assumption), then using this API may cause the +** application to deadlock. For example, if connection X is waiting for +** connection Y's transaction to be concluded, and similarly connection +** Y is waiting on connection X's transaction, then neither connection +** will proceed and the system may remain deadlocked indefinitely. +** +** To avoid this scenario, the sqlite3_unlock_notify() performs deadlock +** detection. If a given call to sqlite3_unlock_notify() would put the +** system in a deadlocked state, then SQLITE_LOCKED is returned and no +** unlock-notify callback is registered. The system is said to be in +** a deadlocked state if connection A has registered for an unlock-notify +** callback on the conclusion of connection B's transaction, and connection +** B has itself registered for an unlock-notify callback when connection +** A's transaction is concluded. Indirect deadlock is also detected, so +** the system is also considered to be deadlocked if connection B has +** registered for an unlock-notify callback on the conclusion of connection +** C's transaction, where connection C is waiting on connection A. Any +** number of levels of indirection are allowed. +** +** The "DROP TABLE" Exception +** +** When a call to [sqlite3_step()] returns SQLITE_LOCKED, it is almost +** always appropriate to call sqlite3_unlock_notify(). There is however, +** one exception. When executing a "DROP TABLE" or "DROP INDEX" statement, +** SQLite checks if there are any currently executing SELECT statements +** that belong to the same connection. If there are, SQLITE_LOCKED is +** returned. In this case there is no "blocking connection", so invoking +** sqlite3_unlock_notify() results in the unlock-notify callback being +** invoked immediately. If the application then re-attempts the "DROP TABLE" +** or "DROP INDEX" query, an infinite loop might be the result. +** +** One way around this problem is to check the extended error code returned +** by an sqlite3_step() call. If there is a blocking connection, then the +** extended error code is set to SQLITE_LOCKED_SHAREDCACHE. Otherwise, in +** the special "DROP TABLE/INDEX" case, the extended error code is just +** SQLITE_LOCKED. +*/ +int sqlite3_unlock_notify( + sqlite3 *pBlocked, /* Waiting connection */ + void (*xNotify)(void **apArg, int nArg), /* Callback function to invoke */ + void *pNotifyArg /* Argument to pass to xNotify */ +); + /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 9d400fe..a74b30a 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.833 2009/02/05 16:53:43 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.854 2009/04/08 13:51:51 drh Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -444,6 +444,22 @@ extern const int sqlite3one; #define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) #define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) +/* +** Round up a number to the next larger multiple of 8. This is used +** to force 8-byte alignment on 64-bit architectures. +*/ +#define ROUND8(x) (((x)+7)&~7) + +/* +** Round down to the nearest multiple of 8 +*/ +#define ROUNDDOWN8(x) ((x)&~7) + +/* +** Assert that the pointer X is aligned to an 8-byte boundary. +*/ +#define EIGHT_BYTE_ALIGNMENT(X) ((((char*)(X) - (char*)0)&7)==0) + /* ** An instance of the following structure is used to store the busy-handler ** callback for a given sqlite handle. @@ -675,10 +691,17 @@ struct Schema { ** lookaside malloc subsystem. Each available memory allocation in ** the lookaside subsystem is stored on a linked list of LookasideSlot ** objects. +** +** Lookaside allocations are only allowed for objects that are associated +** with a particular database connection. Hence, schema information cannot +** be stored in lookaside because in shared cache mode the schema information +** is shared by multiple database connections. Therefore, while parsing +** schema information, the Lookaside.bEnabled flag is cleared so that +** lookaside allocations are not used to construct the schema objects. */ struct Lookaside { u16 sz; /* Size of each buffer in bytes */ - u8 bEnabled; /* True if use lookaside. False to ignore it */ + u8 bEnabled; /* False to disable new lookaside allocations */ u8 bMalloced; /* True if pStart obtained from sqlite3_malloc() */ int nOut; /* Number of buffers currently checked out */ int mxOut; /* Highwater mark for nOut */ @@ -807,7 +830,26 @@ struct sqlite3 { #endif Savepoint *pSavepoint; /* List of active savepoints */ int nSavepoint; /* Number of non-transaction savepoints */ + int nStatement; /* Number of nested statement-transactions */ u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */ + +#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY + /* The following variables are all protected by the STATIC_MASTER + ** mutex, not by sqlite3.mutex. They are used by code in notify.c. + ** + ** When X.pUnlockConnection==Y, that means that X is waiting for Y to + ** unlock so that it can proceed. + ** + ** When X.pBlockingConnection==Y, that means that something that X tried + ** tried to do recently failed with an SQLITE_LOCKED error due to locks + ** held by Y. + */ + sqlite3 *pBlockingConnection; /* Connection that caused SQLITE_LOCKED */ + sqlite3 *pUnlockConnection; /* Connection to watch for unlock */ + void *pUnlockArg; /* Argument to xUnlockNotify */ + void (*xUnlockNotify)(void **, int); /* Unlock notify callback */ + sqlite3 *pNextBlocked; /* Next in list of all blocked connections */ +#endif }; /* @@ -845,8 +887,8 @@ struct sqlite3 { #define SQLITE_RecoveryMode 0x00040000 /* Ignore schema errors */ #define SQLITE_SharedCache 0x00080000 /* Cache sharing is enabled */ -#define SQLITE_Vtab 0x00100000 /* There exists a virtual table */ #define SQLITE_CommitBusy 0x00200000 /* In the process of committing */ +#define SQLITE_ReverseOrder 0x00400000 /* Reverse unordered SELECTs */ /* ** Possible values for the sqlite.magic field. @@ -886,6 +928,7 @@ struct FuncDef { #define SQLITE_FUNC_EPHEM 0x04 /* Ephemeral. Delete with VDBE */ #define SQLITE_FUNC_NEEDCOLL 0x08 /* sqlite3GetFuncCollSeq() might be called */ #define SQLITE_FUNC_PRIVATE 0x10 /* Allowed for internal use only */ +#define SQLITE_FUNC_COUNT 0x20 /* Built-in count(*) aggregate */ /* ** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are @@ -1080,7 +1123,7 @@ struct CollSeq { ** of a SELECT statement. */ struct Table { - sqlite3 *db; /* Associated database connection. Might be NULL. */ + sqlite3 *dbMem; /* DB connection used for lookaside allocations. */ char *zName; /* Name of the table or view */ int iPKey; /* If not negative, use aCol[iPKey] as the primary key */ int nCol; /* Number of columns in this table */ @@ -1091,7 +1134,6 @@ struct Table { u16 nRef; /* Number of pointers to this Table */ u8 tabFlags; /* Mask of TF_* values */ u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ - Trigger *pTrigger; /* List of SQL triggers on this table */ FKey *pFKey; /* Linked list of all foreign keys in this table */ char *zColAff; /* String defining the affinity of each column */ #ifndef SQLITE_OMIT_CHECK @@ -1106,6 +1148,7 @@ struct Table { int nModuleArg; /* Number of arguments to the module */ char **azModuleArg; /* Text of all module args. [0] is module name */ #endif + Trigger *pTrigger; /* List of triggers stored in pSchema */ Schema *pSchema; /* Schema that contains this table */ Table *pNextZombie; /* Next on the Parse.pZombieTab list */ }; @@ -1363,19 +1406,27 @@ struct AggInfo { ** Each node of an expression in the parse tree is an instance ** of this structure. ** -** Expr.op is the opcode. The integer parser token codes are reused -** as opcodes here. For example, the parser defines TK_GE to be an integer -** code representing the ">=" operator. This same integer code is reused +** Expr.op is the opcode. The integer parser token codes are reused +** as opcodes here. For example, the parser defines TK_GE to be an integer +** code representing the ">=" operator. This same integer code is reused ** to represent the greater-than-or-equal-to operator in the expression ** tree. ** -** Expr.pRight and Expr.pLeft are subexpressions. Expr.pList is a list -** of argument if the expression is a function. +** If the expression is an SQL literal (TK_INTEGER, TK_FLOAT, TK_BLOB, +** or TK_STRING), then Expr.token contains the text of the SQL literal. If +** the expression is a variable (TK_VARIABLE), then Expr.token contains the +** variable name. Finally, if the expression is an SQL function (TK_FUNCTION), +** then Expr.token contains the name of the function. ** -** Expr.token is the operator token for this node. For some expressions -** that have subexpressions, Expr.token can be the complete text that gave -** rise to the Expr. In the latter case, the token is marked as being -** a compound token. +** Expr.pRight and Expr.pLeft are the left and right subexpressions of a +** binary operator. Either or both may be NULL. +** +** Expr.x.pList is a list of arguments if the expression is an SQL function, +** a CASE expression or an IN expression of the form " IN (, ...)". +** Expr.x.pSelect is used if the expression is a sub-select or an expression of +** the form " IN (SELECT ...)". If the EP_xIsSelect bit is set in the +** Expr.flags mask, then Expr.x.pSelect is valid. Otherwise, Expr.x.pList is +** valid. ** ** An expression of the form ID or ID.ID refers to a column in a table. ** For such expressions, Expr.op is set to TK_COLUMN and Expr.iTable is @@ -1385,10 +1436,9 @@ struct AggInfo { ** value is also stored in the Expr.iAgg column in the aggregate so that ** it can be accessed after all aggregates are computed. ** -** If the expression is a function, the Expr.iTable is an integer code -** representing which function. If the expression is an unbound variable -** marker (a question mark character '?' in the original SQL) then the -** Expr.iTable holds the index number for that variable. +** If the expression is an unbound variable marker (a question mark +** character '?' in the original SQL) then the Expr.iTable holds the index +** number for that variable. ** ** If the expression is a subquery then Expr.iColumn holds an integer ** register number containing the result of the subquery. If the @@ -1396,32 +1446,62 @@ struct AggInfo { ** gives a different answer at different times during statement processing ** then iTable is the address of a subroutine that computes the subquery. ** -** The Expr.pSelect field points to a SELECT statement. The SELECT might -** be the right operand of an IN operator. Or, if a scalar SELECT appears -** in an expression the opcode is TK_SELECT and Expr.pSelect is the only -** operand. -** ** If the Expr is of type OP_Column, and the table it is selecting from ** is a disk table or the "old.*" pseudo-table, then pTab points to the ** corresponding table definition. +** +** ALLOCATION NOTES: +** +** Expr objects can use a lot of memory space in database schema. To +** help reduce memory requirements, sometimes an Expr object will be +** truncated. And to reduce the number of memory allocations, sometimes +** two or more Expr objects will be stored in a single memory allocation, +** together with Expr.token and/or Expr.span strings. +** +** If the EP_Reduced, EP_SpanToken, and EP_TokenOnly flags are set when +** an Expr object is truncated. When EP_Reduced is set, then all +** the child Expr objects in the Expr.pLeft and Expr.pRight subtrees +** are contained within the same memory allocation. Note, however, that +** the subtrees in Expr.x.pList or Expr.x.pSelect are always separately +** allocated, regardless of whether or not EP_Reduced is set. */ struct Expr { u8 op; /* Operation performed by this node */ char affinity; /* The affinity of the column or 0 if not a column */ - u16 flags; /* Various flags. See below */ - CollSeq *pColl; /* The collation type of the column or 0 */ - Expr *pLeft, *pRight; /* Left and right subnodes */ - ExprList *pList; /* A list of expressions used as function arguments - ** or in " IN ( IN ( IN ()" */ Table *pTab; /* Table for TK_COLUMN expressions. */ #if SQLITE_MAX_EXPR_DEPTH>0 int nHeight; /* Height of the tree headed by this node */ @@ -1443,6 +1523,21 @@ struct Expr { #define EP_AnyAff 0x0200 /* Can take a cached column of any affinity */ #define EP_FixedDest 0x0400 /* Result needed in a specific register */ #define EP_IntValue 0x0800 /* Integer value contained in iTable */ +#define EP_xIsSelect 0x1000 /* x.pSelect is valid (otherwise x.pList is) */ + +#define EP_Reduced 0x2000 /* Expr struct is EXPR_REDUCEDSIZE bytes only */ +#define EP_TokenOnly 0x4000 /* Expr struct is EXPR_TOKENONLYSIZE bytes only */ +#define EP_SpanToken 0x8000 /* Expr size is EXPR_SPANTOKENSIZE bytes */ + +/* +** The following are the meanings of bits in the Expr.vvaFlags field. +** This information is only used when SQLite is compiled with +** SQLITE_DEBUG defined. +*/ +#ifndef NDEBUG +#define EVVA_ReadOnlyToken 0x01 /* Expr.token.z is read-only */ +#endif + /* ** These macros can be used to test, set, or clear bits in the ** Expr.flags field. @@ -1452,6 +1547,23 @@ struct Expr { #define ExprSetProperty(E,P) (E)->flags|=(P) #define ExprClearProperty(E,P) (E)->flags&=~(P) +/* +** Macros to determine the number of bytes required by a normal Expr +** struct, an Expr struct with the EP_Reduced flag set in Expr.flags +** and an Expr struct with the EP_TokenOnly flag set. +*/ +#define EXPR_FULLSIZE sizeof(Expr) /* Full size */ +#define EXPR_REDUCEDSIZE offsetof(Expr,iTable) /* Common features */ +#define EXPR_SPANTOKENSIZE offsetof(Expr,pLeft) /* Fewer features */ +#define EXPR_TOKENONLYSIZE offsetof(Expr,span) /* Smallest possible */ + +/* +** Flags passed to the sqlite3ExprDup() function. See the header comment +** above sqlite3ExprDup() for details. +*/ +#define EXPRDUP_REDUCE 0x0001 /* Used reduced-size Expr nodes */ +#define EXPRDUP_SPAN 0x0002 /* Make a copy of Expr.span */ + /* ** A list of expressions. Each expression may optionally have a ** name. An expr/name combination can be used in several ways, such @@ -2237,7 +2349,7 @@ void sqlite3SetString(char **, sqlite3*, const char*, ...); void sqlite3ErrorMsg(Parse*, const char*, ...); void sqlite3ErrorClear(Parse*); void sqlite3Dequote(char*); -void sqlite3DequoteExpr(sqlite3*, Expr*); +void sqlite3DequoteExpr(Expr*); int sqlite3KeywordCode(const unsigned char*, int); int sqlite3RunParser(Parse*, const char*, char **); void sqlite3FinishCoding(Parse*); @@ -2379,12 +2491,12 @@ void sqlite3GenerateConstraintChecks(Parse*,Table*,int,int, void sqlite3CompleteInsertion(Parse*, Table*, int, int, int*, int, int, int); int sqlite3OpenTableAndIndices(Parse*, Table*, int, int); void sqlite3BeginWriteOperation(Parse*, int, int); -Expr *sqlite3ExprDup(sqlite3*,Expr*); -void sqlite3TokenCopy(sqlite3*,Token*, Token*); -ExprList *sqlite3ExprListDup(sqlite3*,ExprList*); -SrcList *sqlite3SrcListDup(sqlite3*,SrcList*); +Expr *sqlite3ExprDup(sqlite3*,Expr*,int); +void sqlite3TokenCopy(sqlite3*,Token*,const Token*); +ExprList *sqlite3ExprListDup(sqlite3*,ExprList*,int); +SrcList *sqlite3SrcListDup(sqlite3*,SrcList*,int); IdList *sqlite3IdListDup(sqlite3*,IdList*); -Select *sqlite3SelectDup(sqlite3*,Select*); +Select *sqlite3SelectDup(sqlite3*,Select*,int); void sqlite3FuncDefInsert(FuncDefHash*, FuncDef*); FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,int,u8,int); void sqlite3RegisterBuiltinFunctions(sqlite3*); @@ -2411,9 +2523,10 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, int); void sqlite3FinishTrigger(Parse*, TriggerStep*, Token*); void sqlite3DropTrigger(Parse*, SrcList*, int); void sqlite3DropTriggerPtr(Parse*, Trigger*); - int sqlite3TriggersExist(Table*, int, ExprList*); - int sqlite3CodeRowTrigger(Parse*, int, ExprList*, int, Table *, int, int, - int, int, u32*, u32*); + Trigger *sqlite3TriggersExist(Parse *, Table*, int, ExprList*, int *pMask); + Trigger *sqlite3TriggerList(Parse *, Table *); + int sqlite3CodeRowTrigger(Parse*, Trigger *, int, ExprList*, int, Table *, + int, int, int, int, u32*, u32*); void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*); void sqlite3DeleteTriggerStep(sqlite3*, TriggerStep*); TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*); @@ -2428,7 +2541,8 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, int); # define sqlite3DeleteTrigger(A,B) # define sqlite3DropTriggerPtr(A,B) # define sqlite3UnlinkAndDeleteTrigger(A,B,C) -# define sqlite3CodeRowTrigger(A,B,C,D,E,F,G,H,I,J,K) 0 +# define sqlite3CodeRowTrigger(A,B,C,D,E,F,G,H,I,J,K,L) 0 +# define sqlite3TriggerList(X, Y) 0 #endif int sqlite3JoinType(Parse*, Token*, Token*, Token*); @@ -2460,7 +2574,7 @@ int sqlite3GetInt32(const char *, int*); int sqlite3FitsIn64Bits(const char *, int); int sqlite3Utf16ByteLen(const void *pData, int nChar); int sqlite3Utf8CharLen(const char *pData, int nByte); -int sqlite3Utf8Read(const u8*, const u8*, const u8**); +int sqlite3Utf8Read(const u8*, const u8**); /* ** Routines to read and write variable-length integers. These used to @@ -2686,6 +2800,17 @@ int sqlite3IsMemJournal(sqlite3_file *); u32 sqlite3Get4byte(const u8*); void sqlite3Put4byte(u8*, u32); +#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY + void sqlite3ConnectionBlocked(sqlite3 *, sqlite3 *); + void sqlite3ConnectionUnlocked(sqlite3 *db); + void sqlite3ConnectionClosed(sqlite3 *db); +#else + #define sqlite3ConnectionBlocked(x,y) + #define sqlite3ConnectionUnlocked(x) + #define sqlite3ConnectionClosed(x) +#endif + + #ifdef SQLITE_SSE #include "sseInt.h" #endif diff --git a/src/table.c b/src/table.c index c57ac17..cf0a3ee 100644 --- a/src/table.c +++ b/src/table.c @@ -16,7 +16,7 @@ ** These routines are in a separate files so that they will not be linked ** if they are not used. ** -** $Id: table.c,v 1.39 2009/01/19 20:49:10 drh Exp $ +** $Id: table.c,v 1.40 2009/04/10 14:28:00 drh Exp $ */ #include "sqliteInt.h" #include @@ -29,14 +29,13 @@ ** to the callback function is uses to build the result. */ typedef struct TabResult { - char **azResult; - char *zErrMsg; - int nResult; - int nAlloc; - int nRow; - int nColumn; - int nData; - int rc; + char **azResult; /* Accumulated output */ + char *zErrMsg; /* Error message text, if an error occurs */ + int nAlloc; /* Slots allocated for azResult[] */ + int nRow; /* Number of rows in the result */ + int nColumn; /* Number of columns in the result */ + int nData; /* Slots used in azResult[]. (nRow+1)*nColumn */ + int rc; /* Return code from sqlite3_exec() */ } TabResult; /* @@ -45,10 +44,10 @@ typedef struct TabResult { ** memory as necessary. */ static int sqlite3_get_table_cb(void *pArg, int nCol, char **argv, char **colv){ - TabResult *p = (TabResult*)pArg; - int need; - int i; - char *z; + TabResult *p = (TabResult*)pArg; /* Result accumulator */ + int need; /* Slots needed in p->azResult[] */ + int i; /* Loop counter */ + char *z; /* A single column of result */ /* Make sure there is enough space in p->azResult to hold everything ** we need to remember from this invocation of the callback. @@ -58,9 +57,9 @@ static int sqlite3_get_table_cb(void *pArg, int nCol, char **argv, char **colv){ }else{ need = nCol; } - if( p->nData + need >= p->nAlloc ){ + if( p->nData + need > p->nAlloc ){ char **azNew; - p->nAlloc = p->nAlloc*2 + need + 1; + p->nAlloc = p->nAlloc*2 + need; azNew = sqlite3_realloc( p->azResult, sizeof(char*)*p->nAlloc ); if( azNew==0 ) goto malloc_failed; p->azResult = azNew; @@ -134,7 +133,6 @@ int sqlite3_get_table( if( pnRow ) *pnRow = 0; if( pzErrMsg ) *pzErrMsg = 0; res.zErrMsg = 0; - res.nResult = 0; res.nRow = 0; res.nColumn = 0; res.nData = 1; @@ -168,13 +166,12 @@ int sqlite3_get_table( } if( res.nAlloc>res.nData ){ char **azNew; - azNew = sqlite3_realloc( res.azResult, sizeof(char*)*(res.nData+1) ); + azNew = sqlite3_realloc( res.azResult, sizeof(char*)*res.nData ); if( azNew==0 ){ sqlite3_free_table(&res.azResult[1]); db->errCode = SQLITE_NOMEM; return SQLITE_NOMEM; } - res.nAlloc = res.nData+1; res.azResult = azNew; } *pazResult = &res.azResult[1]; diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 585360f..4c6c5fc 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -12,7 +12,7 @@ ** A TCL Interface to SQLite. Append this file to sqlite3.c and ** compile the whole thing to build a TCL-enabled version of SQLite. ** -** $Id: tclsqlite.c,v 1.237 2009/02/17 16:29:11 danielk1977 Exp $ +** $Id: tclsqlite.c,v 1.241 2009/03/27 12:44:35 drh Exp $ */ #include "tcl.h" #include @@ -109,6 +109,7 @@ struct SqliteDb { SqlFunc *pFunc; /* List of SQL functions */ Tcl_Obj *pUpdateHook; /* Update hook script (if any) */ Tcl_Obj *pRollbackHook; /* Rollback hook script (if any) */ + Tcl_Obj *pUnlockNotify; /* Unlock notify script (if any) */ SqlCollate *pCollate; /* List of SQL collation functions */ int rc; /* Return code of most recent sqlite3_exec() */ Tcl_Obj *pCollateNeeded; /* Collation needed script */ @@ -574,6 +575,33 @@ static void DbRollbackHandler(void *clientData){ } } +#if defined(SQLITE_TEST) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY) +static void setTestUnlockNotifyVars(Tcl_Interp *interp, int iArg, int nArg){ + char zBuf[64]; + sprintf(zBuf, "%d", iArg); + Tcl_SetVar(interp, "sqlite_unlock_notify_arg", zBuf, TCL_GLOBAL_ONLY); + sprintf(zBuf, "%d", nArg); + Tcl_SetVar(interp, "sqlite_unlock_notify_argcount", zBuf, TCL_GLOBAL_ONLY); +} +#else +# define setTestUnlockNotifyVars(x,y,z) +#endif + +#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY +static void DbUnlockNotify(void **apArg, int nArg){ + int i; + for(i=0; iinterp, i, nArg); + assert( pDb->pUnlockNotify); + Tcl_EvalObjEx(pDb->interp, pDb->pUnlockNotify, flags); + Tcl_DecrRefCount(pDb->pUnlockNotify); + pDb->pUnlockNotify = 0; + } +} +#endif + static void DbUpdateHandler( void *p, int op, @@ -993,8 +1021,8 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ "profile", "progress", "rekey", "restore", "rollback_hook", "status", "timeout", "total_changes", "trace", - "transaction", "update_hook", "version", - 0 + "transaction", "unlock_notify", "update_hook", + "version", 0 }; enum DB_enum { DB_AUTHORIZER, DB_BACKUP, DB_BUSY, @@ -1007,7 +1035,8 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ DB_PROFILE, DB_PROGRESS, DB_REKEY, DB_RESTORE, DB_ROLLBACK_HOOK, DB_STATUS, DB_TIMEOUT, DB_TOTAL_CHANGES, DB_TRACE, - DB_TRANSACTION, DB_UPDATE_HOOK, DB_VERSION, + DB_TRANSACTION, DB_UNLOCK_NOTIFY, DB_UPDATE_HOOK, + DB_VERSION, }; /* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */ @@ -2450,6 +2479,42 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ break; } + /* + ** $db unlock_notify ?script? + */ + case DB_UNLOCK_NOTIFY: { +#ifndef SQLITE_ENABLE_UNLOCK_NOTIFY + Tcl_AppendResult(interp, "unlock_notify not available in this build", 0); + rc = TCL_ERROR; +#else + if( objc!=2 && objc!=3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?"); + rc = TCL_ERROR; + }else{ + void (*xNotify)(void **, int) = 0; + void *pNotifyArg = 0; + + if( pDb->pUnlockNotify ){ + Tcl_DecrRefCount(pDb->pUnlockNotify); + pDb->pUnlockNotify = 0; + } + + if( objc==3 ){ + xNotify = DbUnlockNotify; + pNotifyArg = (void *)pDb; + pDb->pUnlockNotify = objv[2]; + Tcl_IncrRefCount(pDb->pUnlockNotify); + } + + if( sqlite3_unlock_notify(pDb->db, xNotify, pNotifyArg) ){ + Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), 0); + rc = TCL_ERROR; + } + } +#endif + break; + } + /* ** $db update_hook ?script? ** $db rollback_hook ?script? @@ -2530,8 +2595,21 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ int i; const char *zFile; const char *zVfs = 0; - int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX; + int flags; Tcl_DString translatedFilename; + + /* In normal use, each TCL interpreter runs in a single thread. So + ** by default, we can turn of mutexing on SQLite database connections. + ** However, for testing purposes it is useful to have mutexes turned + ** on. So, by default, mutexes default off. But if compiled with + ** SQLITE_TCL_DEFAULT_FULLMUTEX then mutexes default on. + */ +#ifdef SQLITE_TCL_DEFAULT_FULLMUTEX + flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX; +#else + flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX; +#endif + if( objc==2 ){ zArg = Tcl_GetStringFromObj(objv[1], 0); if( strcmp(zArg,"-version")==0 ){ diff --git a/src/test1.c b/src/test1.c index 326c67d..d6f7f79 100644 --- a/src/test1.c +++ b/src/test1.c @@ -13,7 +13,7 @@ ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test1.c,v 1.347 2009/02/03 16:51:25 danielk1977 Exp $ +** $Id: test1.c,v 1.351 2009/04/08 15:45:32 drh Exp $ */ #include "sqliteInt.h" #include "tcl.h" @@ -125,6 +125,7 @@ const char *sqlite3TestErrorName(int rc){ case SQLITE_ABORT: zName = "SQLITE_ABORT"; break; case SQLITE_BUSY: zName = "SQLITE_BUSY"; break; case SQLITE_LOCKED: zName = "SQLITE_LOCKED"; break; + case SQLITE_LOCKED_SHAREDCACHE: zName = "SQLITE_LOCKED_SHAREDCACHE";break; case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break; case SQLITE_READONLY: zName = "SQLITE_READONLY"; break; case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break; @@ -495,7 +496,7 @@ static int test_exec_nr( /* ** Usage: sqlite3_mprintf_z_test SEPARATOR ARG0 ARG1 ... ** -** Test the %z format of sqliteMPrintf(). Use multiple mprintf() calls to +** Test the %z format of sqlite_mprintf(). Use multiple mprintf() calls to ** concatenate arg0 through argn using separator as the separator. ** Return the result. */ @@ -509,7 +510,7 @@ static int test_mprintf_z( int i; for(i=2; idb = db; /* Allocate echo_vtab.zThis */ - pVtab->zThis = sqlite3MPrintf(0, "%s", argv[2]); + pVtab->zThis = sqlite3_mprintf("%s", argv[2]); if( !pVtab->zThis ){ echoDestructor((sqlite3_vtab *)pVtab); return SQLITE_NOMEM; @@ -421,10 +421,10 @@ static int echoConstructor( /* Allocate echo_vtab.zTableName */ if( argc>3 ){ - pVtab->zTableName = sqlite3MPrintf(0, "%s", argv[3]); + pVtab->zTableName = sqlite3_mprintf("%s", argv[3]); dequoteString(pVtab->zTableName); if( pVtab->zTableName && pVtab->zTableName[0]=='*' ){ - char *z = sqlite3MPrintf(0, "%s%s", argv[2], &(pVtab->zTableName[1])); + char *z = sqlite3_mprintf("%s%s", argv[2], &(pVtab->zTableName[1])); sqlite3_free(pVtab->zTableName); pVtab->zTableName = z; pVtab->isPattern = 1; @@ -482,8 +482,8 @@ static int echoCreate( if( rc==SQLITE_OK && argc==5 ){ char *zSql; echo_vtab *pVtab = *(echo_vtab **)ppVtab; - pVtab->zLogName = sqlite3MPrintf(0, "%s", argv[4]); - zSql = sqlite3MPrintf(0, "CREATE TABLE %Q(logmsg)", pVtab->zLogName); + pVtab->zLogName = sqlite3_mprintf("%s", argv[4]); + zSql = sqlite3_mprintf("CREATE TABLE %Q(logmsg)", pVtab->zLogName); rc = sqlite3_exec(db, zSql, 0, 0, 0); sqlite3_free(zSql); if( rc!=SQLITE_OK ){ @@ -536,7 +536,7 @@ static int echoDestroy(sqlite3_vtab *pVtab){ /* Drop the "log" table, if one exists (see echoCreate() for details) */ if( p && p->zLogName ){ char *zSql; - zSql = sqlite3MPrintf(0, "DROP TABLE %Q", p->zLogName); + zSql = sqlite3_mprintf("DROP TABLE %Q", p->zLogName); rc = sqlite3_exec(p->db, zSql, 0, 0, 0); sqlite3_free(zSql); } @@ -1224,7 +1224,7 @@ static int echoRename(sqlite3_vtab *vtab, const char *zNewName){ if( p->isPattern ){ int nThis = strlen(p->zThis); - char *zSql = sqlite3MPrintf(0, "ALTER TABLE %s RENAME TO %s%s", + char *zSql = sqlite3_mprintf("ALTER TABLE %s RENAME TO %s%s", p->zTableName, zNewName, &p->zTableName[nThis] ); rc = sqlite3_exec(p->db, zSql, 0, 0, 0); diff --git a/src/test9.c b/src/test9.c index 2043da2..222bdc3 100644 --- a/src/test9.c +++ b/src/test9.c @@ -14,7 +14,7 @@ ** for completeness. Test code is written in C for these cases ** as there is not much point in binding to Tcl. ** -** $Id: test9.c,v 1.6 2008/07/11 13:53:55 drh Exp $ +** $Id: test9.c,v 1.7 2009/04/02 18:32:27 drh Exp $ */ #include "sqliteInt.h" #include "tcl.h" @@ -114,6 +114,7 @@ static int c_misuse_test( ){ const char *zErrFunction = "N/A"; sqlite3 *db = 0; + sqlite3_stmt *pStmt; int rc; if( objc!=1 ){ @@ -138,29 +139,37 @@ static int c_misuse_test( goto error_out; } - rc = sqlite3_prepare(db, 0, 0, 0, 0); + pStmt = (sqlite3_stmt*)1234; + rc = sqlite3_prepare(db, 0, 0, &pStmt, 0); if( rc!=SQLITE_MISUSE ){ zErrFunction = "sqlite3_prepare"; goto error_out; } + assert( pStmt==0 ); /* Verify that pStmt is zeroed even on a MISUSE error */ - rc = sqlite3_prepare_v2(db, 0, 0, 0, 0); + pStmt = (sqlite3_stmt*)1234; + rc = sqlite3_prepare_v2(db, 0, 0, &pStmt, 0); if( rc!=SQLITE_MISUSE ){ zErrFunction = "sqlite3_prepare_v2"; goto error_out; } + assert( pStmt==0 ); #ifndef SQLITE_OMIT_UTF16 - rc = sqlite3_prepare16(db, 0, 0, 0, 0); + pStmt = (sqlite3_stmt*)1234; + rc = sqlite3_prepare16(db, 0, 0, &pStmt, 0); if( rc!=SQLITE_MISUSE ){ zErrFunction = "sqlite3_prepare16"; goto error_out; } - rc = sqlite3_prepare16_v2(db, 0, 0, 0, 0); + assert( pStmt==0 ); + pStmt = (sqlite3_stmt*)1234; + rc = sqlite3_prepare16_v2(db, 0, 0, &pStmt, 0); if( rc!=SQLITE_MISUSE ){ zErrFunction = "sqlite3_prepare16_v2"; goto error_out; } + assert( pStmt==0 ); #endif return TCL_OK; diff --git a/src/test_async.c b/src/test_async.c index 44e445e..40cd65d 100644 --- a/src/test_async.c +++ b/src/test_async.c @@ -10,7 +10,7 @@ ** ************************************************************************* ** -** $Id: test_async.c,v 1.48 2008/09/26 20:02:50 drh Exp $ +** $Id: test_async.c,v 1.57 2009/04/07 11:21:29 danielk1977 Exp $ ** ** This file contains an example implementation of an asynchronous IO ** backend for SQLite. @@ -109,7 +109,7 @@ #define ENABLE_FILE_LOCKING #ifndef SQLITE_AMALGAMATION -# include "sqlite3.h" +# include "sqliteInt.h" # include # include #endif @@ -280,7 +280,7 @@ static struct TestAsyncStaticData { volatile int ioDelay; /* Extra delay between write operations */ volatile int writerHaltWhenIdle; /* Writer thread halts when queue empty */ volatile int writerHaltNow; /* Writer thread halts after next op */ - int ioError; /* True if an IO error has occured */ + int ioError; /* True if an IO error has occurred */ int nFile; /* Number of open files (from sqlite pov) */ } async = { PTHREAD_MUTEX_INITIALIZER, @@ -419,7 +419,7 @@ struct AsyncFileData { sqlite3_file *pBaseWrite; /* Write handle to the underlying Os file */ AsyncFileLock lock; /* Lock state for this handle */ AsyncLock *pLock; /* AsyncLock object for this file system entry */ - AsyncWrite close; + AsyncWrite closeOp; /* Preallocated close operation */ }; /* @@ -483,7 +483,6 @@ static int async_mutex_lock(pthread_mutex_t *pMutex){ assert(&(aHolder[2])==&asyncdebug.writerMutexHolder); assert( pthread_self()!=0 ); - for(iIdx=0; iIdx<3; iIdx++){ if( pMutex==&aMutex[iIdx] ) break; @@ -610,7 +609,9 @@ static void assert_mutex_is_held(pthread_mutex_t *pMutex){ */ static void addAsyncWrite(AsyncWrite *pWrite){ /* We must hold the queue mutex in order to modify the queue pointers */ - pthread_mutex_lock(&async.queueMutex); + if( pWrite->op!=ASYNC_UNLOCK ){ + pthread_mutex_lock(&async.queueMutex); + } /* Add the record to the end of the write-op queue */ assert( !pWrite->pNext ); @@ -629,7 +630,9 @@ static void addAsyncWrite(AsyncWrite *pWrite){ } /* Drop the queue mutex */ - pthread_mutex_unlock(&async.queueMutex); + if( pWrite->op!=ASYNC_UNLOCK ){ + pthread_mutex_unlock(&async.queueMutex); + } /* The writer thread might have been idle because there was nothing ** on the write-op queue for it to do. So wake it up. */ @@ -639,7 +642,7 @@ static void addAsyncWrite(AsyncWrite *pWrite){ /* ** Increment async.nFile in a thread-safe manner. */ -static void incrOpenFileCount(){ +static void incrOpenFileCount(void){ /* We must hold the queue mutex in order to modify async.nFile */ pthread_mutex_lock(&async.queueMutex); if( async.nFile==0 ){ @@ -701,7 +704,7 @@ static int asyncClose(sqlite3_file *pFile){ p->lock.eLock = 0; pthread_mutex_unlock(&async.lockMutex); - addAsyncWrite(&p->close); + addAsyncWrite(&p->closeOp); return SQLITE_OK; } @@ -956,10 +959,12 @@ static int asyncUnlock(sqlite3_file *pFile, int eLock){ AsyncFileData *p = ((AsyncFile *)pFile)->pData; if( p->zName ){ AsyncFileLock *pLock = &p->lock; + pthread_mutex_lock(&async.queueMutex); pthread_mutex_lock(&async.lockMutex); pLock->eLock = MIN(pLock->eLock, eLock); - pthread_mutex_unlock(&async.lockMutex); rc = addNewAsyncWrite(p, ASYNC_UNLOCK, 0, eLock, 0); + pthread_mutex_unlock(&async.lockMutex); + pthread_mutex_unlock(&async.queueMutex); } return rc; } @@ -1041,6 +1046,25 @@ static int unlinkAsyncFile(AsyncFileData *pData){ return rc; } +/* +** The parameter passed to this function is a copy of a 'flags' parameter +** passed to this modules xOpen() method. This function returns true +** if the file should be opened asynchronously, or false if it should +** be opened immediately. +** +** If the file is to be opened asynchronously, then asyncOpen() will add +** an entry to the event queue and the file will not actually be opened +** until the event is processed. Otherwise, the file is opened directly +** by the caller. +*/ +static int doAsynchronousOpen(int flags){ + return (flags&SQLITE_OPEN_CREATE) && ( + (flags&SQLITE_OPEN_MAIN_JOURNAL) || + (flags&SQLITE_OPEN_TEMP_JOURNAL) || + (flags&SQLITE_OPEN_DELETEONCLOSE) + ); +} + /* ** Open a file. */ @@ -1075,7 +1099,7 @@ static int asyncOpen( AsyncFileData *pData; AsyncLock *pLock = 0; char *z; - int isExclusive = (flags&SQLITE_OPEN_EXCLUSIVE); + int isAsyncOpen = doAsynchronousOpen(flags); /* If zName is NULL, then the upper layer is requesting an anonymous file */ if( zName ){ @@ -1097,8 +1121,8 @@ static int asyncOpen( pData->pBaseRead = (sqlite3_file*)z; z += pVfs->szOsFile; pData->pBaseWrite = (sqlite3_file*)z; - pData->close.pFileData = pData; - pData->close.op = ASYNC_CLOSE; + pData->closeOp.pFileData = pData; + pData->closeOp.op = ASYNC_CLOSE; if( zName ){ z += pVfs->szOsFile; @@ -1107,10 +1131,14 @@ static int asyncOpen( memcpy(pData->zName, zName, nName); } - if( !isExclusive ){ - rc = pVfs->xOpen(pVfs, zName, pData->pBaseRead, flags, pOutFlags); - if( rc==SQLITE_OK && ((*pOutFlags)&SQLITE_OPEN_READWRITE) ){ - rc = pVfs->xOpen(pVfs, zName, pData->pBaseWrite, flags, 0); + if( !isAsyncOpen ){ + int flagsout; + rc = pVfs->xOpen(pVfs, pData->zName, pData->pBaseRead, flags, &flagsout); + if( rc==SQLITE_OK && (flagsout&SQLITE_OPEN_READWRITE) ){ + rc = pVfs->xOpen(pVfs, pData->zName, pData->pBaseWrite, flags, 0); + } + if( pOutFlags ){ + *pOutFlags = flagsout; } } @@ -1126,7 +1154,7 @@ static int asyncOpen( #ifdef ENABLE_FILE_LOCKING if( flags&SQLITE_OPEN_MAIN_DB ){ pLock->pFile = (sqlite3_file *)&pLock[1]; - rc = pVfs->xOpen(pVfs, zName, pLock->pFile, flags, 0); + rc = pVfs->xOpen(pVfs, pData->zName, pLock->pFile, flags, 0); if( rc!=SQLITE_OK ){ sqlite3_free(pLock); pLock = 0; @@ -1175,7 +1203,7 @@ static int asyncOpen( pData->pLock = pLock; } - if( rc==SQLITE_OK && isExclusive ){ + if( rc==SQLITE_OK && isAsyncOpen ){ rc = addNewAsyncWrite(pData, ASYNC_OPENEXCLUSIVE, (sqlite3_int64)flags,0,0); if( rc==SQLITE_OK ){ if( pOutFlags ) *pOutFlags = flags; @@ -1186,6 +1214,9 @@ static int asyncOpen( sqlite3_free(pData); } } + if( rc!=SQLITE_OK ){ + p->pMethod = 0; + } return rc; } @@ -1259,40 +1290,27 @@ static int asyncFullPathname( ** file-system uses unix style paths. */ if( rc==SQLITE_OK ){ - int iIn; - int iOut = 0; - int nPathOut = strlen(zPathOut); - - for(iIn=0; iIn1 && z[n-1]=='/' ){ n--; } + for(i=j=0; i0 && z[j-1]!='/' ){ j--; } + if( j>0 ){ j--; } + i += 2; + continue; + } } - - /* Replace any occurences of "/./" with "/" */ - if( iIn<=(nPathOut-3) - && zPathOut[iIn]=='/' && zPathOut[iIn+1]=='.' && zPathOut[iIn+2]=='/' - ){ - iIn++; - continue; - } - - /* Replace any occurences of "/../" with "" */ - if( iOut>0 && iIn<=(nPathOut-4) - && zPathOut[iIn]=='/' && zPathOut[iIn+1]=='.' - && zPathOut[iIn+2]=='.' && zPathOut[iIn+3]=='/' - ){ - iIn += 3; - iOut--; - for( ; iOut>0 && zPathOut[iOut-1]!='/'; iOut--); - continue; - } - - zPathOut[iOut++] = zPathOut[iIn]; + z[j++] = z[i]; } - zPathOut[iOut] = '\0'; + z[j] = 0; } return rc; @@ -1305,11 +1323,11 @@ static void asyncDlError(sqlite3_vfs *pAsyncVfs, int nByte, char *zErrMsg){ sqlite3_vfs *pVfs = (sqlite3_vfs *)pAsyncVfs->pAppData; pVfs->xDlError(pVfs, nByte, zErrMsg); } -static void *asyncDlSym( +static void (*asyncDlSym( sqlite3_vfs *pAsyncVfs, void *pHandle, const char *zSymbol -){ +))(void){ sqlite3_vfs *pVfs = (sqlite3_vfs *)pAsyncVfs->pAppData; return pVfs->xDlSym(pVfs, pHandle, zSymbol); } @@ -1513,15 +1531,54 @@ static void *asyncWriterThread(void *pIsStarted){ } case ASYNC_UNLOCK: { + AsyncWrite *pIter; AsyncFileData *pData = p->pFileData; int eLock = p->nByte; - pthread_mutex_lock(&async.lockMutex); - pData->lock.eAsyncLock = MIN( - pData->lock.eAsyncLock, MAX(pData->lock.eLock, eLock) - ); - assert(pData->lock.eAsyncLock>=pData->lock.eLock); - rc = getFileLock(pData->pLock); - pthread_mutex_unlock(&async.lockMutex); + + /* When a file is locked by SQLite using the async backend, it is + ** locked within the 'real' file-system synchronously. When it is + ** unlocked, an ASYNC_UNLOCK event is added to the write-queue to + ** unlock the file asynchronously. The design of the async backend + ** requires that the 'real' file-system file be locked from the + ** time that SQLite first locks it (and probably reads from it) + ** until all asynchronous write events that were scheduled before + ** SQLite unlocked the file have been processed. + ** + ** This is more complex if SQLite locks and unlocks the file multiple + ** times in quick succession. For example, if SQLite does: + ** + ** lock, write, unlock, lock, write, unlock + ** + ** Each "lock" operation locks the file immediately. Each "write" + ** and "unlock" operation adds an event to the event queue. If the + ** second "lock" operation is performed before the first "unlock" + ** operation has been processed asynchronously, then the first + ** "unlock" cannot be safely processed as is, since this would mean + ** the file was unlocked when the second "write" operation is + ** processed. To work around this, when processing an ASYNC_UNLOCK + ** operation, SQLite: + ** + ** 1) Unlocks the file to the minimum of the argument passed to + ** the xUnlock() call and the current lock from SQLite's point + ** of view, and + ** + ** 2) Only unlocks the file at all if this event is the last + ** ASYNC_UNLOCK event on this file in the write-queue. + */ + assert( holdingMutex==1 ); + assert( async.pQueueFirst==p ); + for(pIter=async.pQueueFirst->pNext; pIter; pIter=pIter->pNext){ + if( pIter->pFileData==pData && pIter->op==ASYNC_UNLOCK ) break; + } + if( !pIter ){ + pthread_mutex_lock(&async.lockMutex); + pData->lock.eAsyncLock = MIN( + pData->lock.eAsyncLock, MAX(pData->lock.eLock, eLock) + ); + assert(pData->lock.eAsyncLock>=pData->lock.eLock); + rc = getFileLock(pData->pLock); + pthread_mutex_unlock(&async.lockMutex); + } break; } @@ -1564,12 +1621,12 @@ static void *asyncWriterThread(void *pIsStarted){ } assert( holdingMutex ); - /* An IO error has occured. We cannot report the error back to the + /* An IO error has occurred. We cannot report the error back to the ** connection that requested the I/O since the error happened ** asynchronously. The connection has already moved on. There ** really is nobody to report the error to. ** - ** The file for which the error occured may have been a database or + ** The file for which the error occurred may have been a database or ** journal file. Regardless, none of the currently queued operations ** associated with the same database should now be performed. Nor should ** any subsequently requested IO on either a database or journal file diff --git a/src/test_backup.c b/src/test_backup.c index 1a1418d..7c561af 100644 --- a/src/test_backup.c +++ b/src/test_backup.c @@ -10,7 +10,7 @@ ** ************************************************************************* ** -** $Id: test_backup.c,v 1.1 2009/02/03 16:51:25 danielk1977 Exp $ +** $Id: test_backup.c,v 1.3 2009/03/30 12:56:52 drh Exp $ */ #include "tcl.h" @@ -61,12 +61,13 @@ static int backupTestCmd( switch( aSub[iCmd].eCmd ){ case BACKUP_FINISH: { + const char *zCmdName; Tcl_CmdInfo cmdInfo; - Tcl_Command cmd = Tcl_GetCommandFromObj(interp, objv[0]); - Tcl_GetCommandInfoFromToken(cmd, &cmdInfo); + zCmdName = Tcl_GetString(objv[0]); + Tcl_GetCommandInfo(interp, zCmdName, &cmdInfo); cmdInfo.deleteProc = 0; - Tcl_SetCommandInfoFromToken(cmd, &cmdInfo); - Tcl_DeleteCommandFromToken(interp, cmd); + Tcl_SetCommandInfo(interp, zCmdName, &cmdInfo); + Tcl_DeleteCommand(interp, zCmdName); rc = sqlite3_backup_finish(p); Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); @@ -145,4 +146,3 @@ int Sqlitetestbackup_Init(Tcl_Interp *interp){ Tcl_CreateObjCommand(interp, "sqlite3_backup", backupTestInit, 0, 0); return TCL_OK; } - diff --git a/src/test_config.c b/src/test_config.c index e3bcf0d..ce809f9 100644 --- a/src/test_config.c +++ b/src/test_config.c @@ -16,7 +16,7 @@ ** The focus of this file is providing the TCL testing layer ** access to compile-time constants. ** -** $Id: test_config.c,v 1.47 2009/01/12 14:01:45 danielk1977 Exp $ +** $Id: test_config.c,v 1.48 2009/03/16 13:19:36 danielk1977 Exp $ */ #include "sqliteLimit.h" @@ -493,6 +493,12 @@ Tcl_SetVar2(interp, "sqlite_options", "long_double", Tcl_SetVar2(interp, "sqlite_options", "update_delete_limit", "0", TCL_GLOBAL_ONLY); #endif +#if defined(SQLITE_ENABLE_UNLOCK_NOTIFY) + Tcl_SetVar2(interp, "sqlite_options", "unlock_notify", "1", TCL_GLOBAL_ONLY); +#else + Tcl_SetVar2(interp, "sqlite_options", "unlock_notify", "0", TCL_GLOBAL_ONLY); +#endif + #ifdef SQLITE_SECURE_DELETE Tcl_SetVar2(interp, "sqlite_options", "secure_delete", "1", TCL_GLOBAL_ONLY); #else diff --git a/src/test_func.c b/src/test_func.c index 20f3307..275359e 100644 --- a/src/test_func.c +++ b/src/test_func.c @@ -12,7 +12,7 @@ ** Code for testing all sorts of SQLite interfaces. This code ** implements new SQL functions used by the test scripts. ** -** $Id: test_func.c,v 1.13 2008/08/28 02:26:07 drh Exp $ +** $Id: test_func.c,v 1.14 2009/03/19 18:51:07 danielk1977 Exp $ */ #include "sqlite3.h" #include "tcl.h" @@ -146,6 +146,25 @@ static void test_destructor_count( sqlite3_result_int(pCtx, test_destructor_count_var); } +/* +** The following aggregate function, test_agg_errmsg16(), takes zero +** arguments. It returns the text value returned by the sqlite3_errmsg16() +** API function. +*/ +void sqlite3BeginBenignMalloc(void); +void sqlite3EndBenignMalloc(void); +static void test_agg_errmsg16_step(sqlite3_context *a, int b,sqlite3_value **c){ +} +static void test_agg_errmsg16_final(sqlite3_context *ctx){ + const void *z; + sqlite3 * db = sqlite3_context_db_handle(ctx); + sqlite3_aggregate_context(ctx, 2048); + sqlite3BeginBenignMalloc(); + z = sqlite3_errmsg16(db); + sqlite3EndBenignMalloc(); + sqlite3_result_text16(ctx, z, -1, SQLITE_TRANSIENT); +} + /* ** Routines for testing the sqlite3_get_auxdata() and sqlite3_set_auxdata() ** interface. @@ -318,6 +337,10 @@ static int registerTestFunctions(sqlite3 *db){ sqlite3_create_function(db, aFuncs[i].zName, aFuncs[i].nArg, aFuncs[i].eTextRep, 0, aFuncs[i].xFunc, 0, 0); } + + sqlite3_create_function(db, "test_agg_errmsg16", 0, SQLITE_ANY, 0, 0, + test_agg_errmsg16_step, test_agg_errmsg16_final); + return SQLITE_OK; } diff --git a/src/test_journal.c b/src/test_journal.c index 476f493..9b36287 100644 --- a/src/test_journal.c +++ b/src/test_journal.c @@ -15,7 +15,7 @@ ** correctly populates and syncs a journal file before writing to a ** corresponding database file. ** -** $Id: test_journal.c,v 1.11 2009/02/12 09:11:56 danielk1977 Exp $ +** $Id: test_journal.c,v 1.15 2009/04/07 11:21:29 danielk1977 Exp $ */ #if SQLITE_TEST /* This file is used for testing only */ @@ -50,7 +50,7 @@ ** c) The set of page numbers corresponding to free-list leaf pages. ** d) A check-sum for every page in the database file. ** -** The start of a write-transaction is deemed to have occured when a +** The start of a write-transaction is deemed to have occurred when a ** 28-byte journal header is written to byte offset 0 of the journal ** file. ** @@ -206,6 +206,17 @@ struct JtGlobal { }; static struct JtGlobal g = {0, 0}; +/* +** Functions to obtain and relinquish a mutex to protect g.pList. The +** STATIC_PRNG mutex is reused, purely for the sake of convenience. +*/ +static void enterJtMutex(void){ + sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_PRNG)); +} +static void leaveJtMutex(void){ + sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_PRNG)); +} + extern int sqlite3_io_error_pending; static void stop_ioerr_simulation(int *piSave){ *piSave = sqlite3_io_error_pending; @@ -236,11 +247,12 @@ static int jtClose(sqlite3_file *pFile){ jt_file *p = (jt_file *)pFile; closeTransaction(p); + enterJtMutex(); if( p->zName ){ for(pp=&g.pList; *pp!=p; pp=&(*pp)->pNext); *pp = p->pNext; } - + leaveJtMutex(); return sqlite3OsClose(p->pReal); } @@ -257,7 +269,6 @@ static int jtRead( return sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst); } - /* ** Parameter zJournal is the name of a journal file that is currently ** open. This function locates and returns the handle opened on the @@ -274,6 +285,7 @@ static int jtRead( **/ static jt_file *locateDatabaseHandle(const char *zJournal){ jt_file *pMain = 0; + enterJtMutex(); for(pMain=g.pList; pMain; pMain=pMain->pNext){ int nName = strlen(zJournal) - strlen("-journal"); if( (pMain->flags&SQLITE_OPEN_MAIN_DB) @@ -284,6 +296,7 @@ static jt_file *locateDatabaseHandle(const char *zJournal){ break; } } + leaveJtMutex(); return pMain; } @@ -656,6 +669,7 @@ static int jtOpen( ){ int rc; jt_file *p = (jt_file *)pFile; + pFile->pMethods = 0; p->pReal = (sqlite3_file *)&p[1]; p->pReal->pMethods = 0; rc = sqlite3OsOpen(g.pVfs, zName, p->pReal, flags, pOutFlags); @@ -668,10 +682,12 @@ static int jtOpen( p->pNext = 0; p->pWritable = 0; p->aCksum = 0; + enterJtMutex(); if( zName ){ p->pNext = g.pList; g.pList = p; } + leaveJtMutex(); } return rc; } @@ -798,7 +814,7 @@ int jt_register(char *zWrap, int isDefault){ /* ** Uninstall the jt VFS, if it is installed. */ -void jt_unregister(){ +void jt_unregister(void){ sqlite3_vfs_unregister(&jt_vfs); } diff --git a/src/test_malloc.c b/src/test_malloc.c index 21ab1e8..78bb281 100644 --- a/src/test_malloc.c +++ b/src/test_malloc.c @@ -13,7 +13,7 @@ ** This file contains code used to implement test interfaces to the ** memory allocation subsystem. ** -** $Id: test_malloc.c,v 1.53 2009/02/04 15:27:40 danielk1977 Exp $ +** $Id: test_malloc.c,v 1.54 2009/04/07 11:21:29 danielk1977 Exp $ */ #include "sqliteInt.h" #include "tcl.h" @@ -49,7 +49,7 @@ static void sqlite3Fault(void){ ** Check to see if a fault should be simulated. Return true to simulate ** the fault. Return false if the fault should not be simulated. */ -static int faultsimStep(){ +static int faultsimStep(void){ if( likely(!memfault.enable) ){ return 0; } @@ -771,7 +771,7 @@ static void test_memdebug_callback(int nByte, int nFrame, void **aFrame){ } #endif /* SQLITE_MEMDEBUG */ -static void test_memdebug_log_clear(){ +static void test_memdebug_log_clear(void){ Tcl_HashSearch search; Tcl_HashEntry *pEntry; for( diff --git a/src/test_mutex.c b/src/test_mutex.c index 2d0f719..69030ae 100644 --- a/src/test_mutex.c +++ b/src/test_mutex.c @@ -10,7 +10,7 @@ ** ************************************************************************* ** -** $Id: test_mutex.c,v 1.14 2009/02/11 05:18:07 danielk1977 Exp $ +** $Id: test_mutex.c,v 1.15 2009/03/20 13:15:30 drh Exp $ */ #include "tcl.h" @@ -248,7 +248,7 @@ static int test_read_mutex_counters( int ii; char *aName[8] = { "fast", "recursive", "static_master", "static_mem", - "static_mem2", "static_prng", "static_lru", "static_lru2" + "static_open", "static_prng", "static_lru", "static_lru2" }; if( objc!=1 ){ diff --git a/src/test_onefile.c b/src/test_onefile.c index 9408144..9d176be 100644 --- a/src/test_onefile.c +++ b/src/test_onefile.c @@ -10,7 +10,7 @@ ** ************************************************************************* ** -** $Id: test_onefile.c,v 1.11 2009/02/10 18:54:03 danielk1977 Exp $ +** $Id: test_onefile.c,v 1.12 2009/04/07 11:21:29 danielk1977 Exp $ ** ** OVERVIEW: ** @@ -810,7 +810,7 @@ static int fsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ ** true, the fs vfs becomes the new default vfs. It is the only publicly ** available function in this file. */ -int fs_register(){ +int fs_register(void){ if( fs_vfs.pParent ) return SQLITE_OK; fs_vfs.pParent = sqlite3_vfs_find(0); fs_vfs.base.mxPathname = fs_vfs.pParent->mxPathname; diff --git a/src/test_pcache.c b/src/test_pcache.c index 05bc8f7..dfef141 100644 --- a/src/test_pcache.c +++ b/src/test_pcache.c @@ -21,7 +21,7 @@ ** This pagecache implementation is designed for simplicity ** not speed. ** -** $Id: test_pcache.c,v 1.2 2009/01/07 03:59:47 drh Exp $ +** $Id: test_pcache.c,v 1.3 2009/04/11 11:38:54 drh Exp $ */ #include "sqlite3.h" #include @@ -75,9 +75,19 @@ static void testpcacheShutdown(void *pArg){ } /* -** Number of pages in a cache +** Number of pages in a cache. +** +** The number of pages is a hard upper bound in this test module. +** If more pages are requested, sqlite3PcacheFetch() returns NULL. +** +** If testing with in-memory temp tables, provide a larger pcache. +** Some of the test cases need this. */ -#define TESTPCACHE_NPAGE 217 +#if defined(SQLITE_TEMP_STORE) && SQLITE_TEMP_STORE>=2 +# define TESTPCACHE_NPAGE 499 +#else +# define TESTPCACHE_NPAGE 217 +#endif #define TESTPCACHE_RESERVE 17 /* diff --git a/src/test_thread.c b/src/test_thread.c index 973ca65..fc372a2 100644 --- a/src/test_thread.c +++ b/src/test_thread.c @@ -14,7 +14,7 @@ ** test that sqlite3 database handles may be concurrently accessed by ** multiple threads. Right now this only works on unix. ** -** $Id: test_thread.c,v 1.10 2009/02/03 19:55:20 shane Exp $ +** $Id: test_thread.c,v 1.15 2009/03/27 12:32:56 drh Exp $ */ #include "sqliteInt.h" @@ -55,8 +55,19 @@ struct EvalEvent { static Tcl_ObjCmdProc sqlthread_proc; static Tcl_ObjCmdProc clock_seconds_proc; +#if defined(SQLITE_OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY) +static Tcl_ObjCmdProc blocking_step_proc; +static Tcl_ObjCmdProc blocking_prepare_v2_proc; +#endif int Sqlitetest1_Init(Tcl_Interp *); +/* Functions from test1.c */ +void *sqlite3TestTextToPtr(const char *); +const char *sqlite3TestErrorName(int); +int getDbPointer(Tcl_Interp *, const char *, sqlite3 **); +int sqlite3TestMakePointerStr(Tcl_Interp *, char *, void *); +int sqlite3TestErrCode(Tcl_Interp *, sqlite3 *, int); + /* ** Handler for events of type EvalEvent. */ @@ -106,6 +117,13 @@ static Tcl_ThreadCreateType tclScriptThread(ClientData pSqlThread){ interp = Tcl_CreateInterp(); Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0); Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, pSqlThread, 0); +#if defined(SQLITE_OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY) + Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0); + Tcl_CreateObjCommand(interp, + "sqlite3_blocking_prepare_v2", blocking_prepare_v2_proc, (void *)1, 0); + Tcl_CreateObjCommand(interp, + "sqlite3_nonblocking_prepare_v2", blocking_prepare_v2_proc, 0, 0); +#endif Sqlitetest1_Init(interp); Sqlitetest_mutex_Init(interp); @@ -359,12 +377,248 @@ static int clock_seconds_proc( return TCL_OK; } +/************************************************************************* +** This block contains the implementation of the [sqlite3_blocking_step] +** command available to threads created by [sqlthread spawn] commands. It +** is only available on UNIX for now. This is because pthread condition +** variables are used. +** +** The source code for the C functions sqlite3_blocking_step(), +** blocking_step_notify() and the structure UnlockNotification is +** automatically extracted from this file and used as part of the +** documentation for the sqlite3_unlock_notify() API function. This +** should be considered if these functions are to be extended (i.e. to +** support windows) in the future. +*/ +#if defined(SQLITE_OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY) + +/* BEGIN_SQLITE_BLOCKING_STEP */ +/* This example uses the pthreads API */ +#include + +/* +** A pointer to an instance of this structure is passed as the user-context +** pointer when registering for an unlock-notify callback. +*/ +typedef struct UnlockNotification UnlockNotification; +struct UnlockNotification { + int fired; /* True after unlock event has occured */ + pthread_cond_t cond; /* Condition variable to wait on */ + pthread_mutex_t mutex; /* Mutex to protect structure */ +}; + +/* +** This function is an unlock-notify callback registered with SQLite. +*/ +static void unlock_notify_cb(void **apArg, int nArg){ + int i; + for(i=0; imutex); + p->fired = 1; + pthread_cond_signal(&p->cond); + pthread_mutex_unlock(&p->mutex); + } +} + +/* +** This function assumes that an SQLite API call (either sqlite3_prepare_v2() +** or sqlite3_step()) has just returned SQLITE_LOCKED. The argument is the +** associated database connection. +** +** This function calls sqlite3_unlock_notify() to register for an +** unlock-notify callback, then blocks until that callback is delivered +** and returns SQLITE_OK. The caller should then retry the failed operation. +** +** Or, if sqlite3_unlock_notify() indicates that to block would deadlock +** the system, then this function returns SQLITE_LOCKED immediately. In +** this case the caller should not retry the operation and should roll +** back the current transaction (if any). +*/ +static int wait_for_unlock_notify(sqlite3 *db){ + int rc; + UnlockNotification un; + + /* Initialize the UnlockNotification structure. */ + un.fired = 0; + pthread_mutex_init(&un.mutex, 0); + pthread_cond_init(&un.cond, 0); + + /* Register for an unlock-notify callback. */ + rc = sqlite3_unlock_notify(db, unlock_notify_cb, (void *)&un); + assert( rc==SQLITE_LOCKED || rc==SQLITE_OK ); + + /* The call to sqlite3_unlock_notify() always returns either SQLITE_LOCKED + ** or SQLITE_OK. + ** + ** If SQLITE_LOCKED was returned, then the system is deadlocked. In this + ** case this function needs to return SQLITE_LOCKED to the caller so + ** that the current transaction can be rolled back. Otherwise, block + ** until the unlock-notify callback is invoked, then return SQLITE_OK. + */ + if( rc==SQLITE_OK ){ + pthread_mutex_lock(&un.mutex); + if( !un.fired ){ + pthread_cond_wait(&un.cond, &un.mutex); + } + pthread_mutex_unlock(&un.mutex); + } + + /* Destroy the mutex and condition variables. */ + pthread_cond_destroy(&un.cond); + pthread_mutex_destroy(&un.mutex); + + return rc; +} + +/* +** This function is a wrapper around the SQLite function sqlite3_step(). +** It functions in the same way as step(), except that if a required +** shared-cache lock cannot be obtained, this function may block waiting for +** the lock to become available. In this scenario the normal API step() +** function always returns SQLITE_LOCKED. +** +** If this function returns SQLITE_LOCKED, the caller should rollback +** the current transaction (if any) and try again later. Otherwise, the +** system may become deadlocked. +*/ +int sqlite3_blocking_step(sqlite3_stmt *pStmt){ + int rc; + while( SQLITE_LOCKED==(rc = sqlite3_step(pStmt)) ){ + rc = wait_for_unlock_notify(sqlite3_db_handle(pStmt)); + if( rc!=SQLITE_OK ) break; + sqlite3_reset(pStmt); + } + return rc; +} + +/* +** This function is a wrapper around the SQLite function sqlite3_prepare_v2(). +** It functions in the same way as prepare_v2(), except that if a required +** shared-cache lock cannot be obtained, this function may block waiting for +** the lock to become available. In this scenario the normal API prepare_v2() +** function always returns SQLITE_LOCKED. +** +** If this function returns SQLITE_LOCKED, the caller should rollback +** the current transaction (if any) and try again later. Otherwise, the +** system may become deadlocked. +*/ +int sqlite3_blocking_prepare_v2( + sqlite3 *db, /* Database handle. */ + const char *zSql, /* UTF-8 encoded SQL statement. */ + int nSql, /* Length of zSql in bytes. */ + sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ + const char **pz /* OUT: End of parsed string */ +){ + int rc; + while( SQLITE_LOCKED==(rc = sqlite3_prepare_v2(db, zSql, nSql, ppStmt, pz)) ){ + rc = wait_for_unlock_notify(db); + if( rc!=SQLITE_OK ) break; + } + return rc; +} +/* END_SQLITE_BLOCKING_STEP */ + +/* +** Usage: sqlite3_blocking_step STMT +** +** Advance the statement to the next row. +*/ +static int blocking_step_proc( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + + sqlite3_stmt *pStmt; + int rc; + + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "STMT"); + return TCL_ERROR; + } + + pStmt = (sqlite3_stmt*)sqlite3TestTextToPtr(Tcl_GetString(objv[1])); + rc = sqlite3_blocking_step(pStmt); + + Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), 0); + return TCL_OK; +} + +/* +** Usage: sqlite3_blocking_prepare_v2 DB sql bytes ?tailvar? +** Usage: sqlite3_nonblocking_prepare_v2 DB sql bytes ?tailvar? +*/ +static int blocking_prepare_v2_proc( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3 *db; + const char *zSql; + int bytes; + const char *zTail = 0; + sqlite3_stmt *pStmt = 0; + char zBuf[50]; + int rc; + int isBlocking = !(clientData==0); + + if( objc!=5 && objc!=4 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", + Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; + zSql = Tcl_GetString(objv[2]); + if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR; + + if( isBlocking ){ + rc = sqlite3_blocking_prepare_v2(db, zSql, bytes, &pStmt, &zTail); + }else{ + rc = sqlite3_prepare_v2(db, zSql, bytes, &pStmt, &zTail); + } + + assert(rc==SQLITE_OK || pStmt==0); + if( zTail && objc>=5 ){ + if( bytes>=0 ){ + bytes = bytes - (zTail-zSql); + } + Tcl_ObjSetVar2(interp, objv[4], 0, Tcl_NewStringObj(zTail, bytes), 0); + } + if( rc!=SQLITE_OK ){ + assert( pStmt==0 ); + sprintf(zBuf, "%s ", (char *)sqlite3TestErrorName(rc)); + Tcl_AppendResult(interp, zBuf, sqlite3_errmsg(db), 0); + return TCL_ERROR; + } + + if( pStmt ){ + if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR; + Tcl_AppendResult(interp, zBuf, 0); + } + return TCL_OK; +} + +#endif /* SQLITE_OS_UNIX && SQLITE_ENABLE_UNLOCK_NOTIFY */ +/* +** End of implementation of [sqlite3_blocking_step]. +************************************************************************/ + /* ** Register commands with the TCL interpreter. */ int SqlitetestThread_Init(Tcl_Interp *interp){ Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, 0, 0); Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0); +#if defined(SQLITE_OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY) + Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0); + Tcl_CreateObjCommand(interp, + "sqlite3_blocking_prepare_v2", blocking_prepare_v2_proc, (void *)1, 0); + Tcl_CreateObjCommand(interp, + "sqlite3_nonblocking_prepare_v2", blocking_prepare_v2_proc, 0, 0); +#endif return TCL_OK; } #else diff --git a/src/test_wsd.c b/src/test_wsd.c index a7fad26..a5d10a7 100644 --- a/src/test_wsd.c +++ b/src/test_wsd.c @@ -14,7 +14,7 @@ ** sqlite3_wsd_init() and sqlite3_wsd_find() functions required if the ** SQLITE_OMIT_WSD symbol is defined at build time. ** -** $Id: test_wsd.c,v 1.3 2008/10/07 15:25:49 drh Exp $ +** $Id: test_wsd.c,v 1.4 2009/03/23 04:33:33 danielk1977 Exp $ */ #if defined(SQLITE_OMIT_WSD) && defined(SQLITE_TEST) @@ -69,7 +69,7 @@ void *sqlite3_wsd_find(void *K, int L){ /* If no entry for K was found, create and populate a new one. */ if( !pVar ){ - int nByte = (sizeof(ProcessLocalVar) + L + 7)&~7; + int nByte = ROUND8(sizeof(ProcessLocalVar) + L); assert( pGlobal->nFree>=nByte ); pVar = (ProcessLocalVar *)pGlobal->pFree; pVar->pKey = K; diff --git a/src/tokenize.c b/src/tokenize.c index afc3dd6..4677811 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -15,7 +15,7 @@ ** individual tokens and sends those tokens one-by-one over to the ** parser for analysis. ** -** $Id: tokenize.c,v 1.153 2009/01/20 16:53:41 danielk1977 Exp $ +** $Id: tokenize.c,v 1.155 2009/03/31 03:41:57 shane Exp $ */ #include "sqliteInt.h" #include @@ -381,14 +381,17 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){ ** error message. */ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){ - int nErr = 0; - int i; - void *pEngine; - int tokenType; - int lastTokenParsed = -1; - sqlite3 *db = pParse->db; - int mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; + int nErr = 0; /* Number of errors encountered */ + int i; /* Loop counter */ + void *pEngine; /* The LEMON-generated LALR(1) parser */ + int tokenType; /* type of the next token */ + int lastTokenParsed = -1; /* type of the previous token */ + u8 enableLookaside; /* Saved value of db->lookaside.bEnabled */ + sqlite3 *db = pParse->db; /* The database connection */ + int mxSqlLen; /* Max length of an SQL string */ + + mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; if( db->activeVdbeCnt==0 ){ db->u1.isInterrupted = 0; } @@ -408,6 +411,8 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){ assert( pParse->nVarExpr==0 ); assert( pParse->nVarExprAlloc==0 ); assert( pParse->apVarExpr==0 ); + enableLookaside = db->lookaside.bEnabled; + if( db->lookaside.pStart ) db->lookaside.bEnabled = 1; while( !db->mallocFailed && zSql[i]!=0 ){ assert( i>=0 ); pParse->sLastToken.z = (u8*)&zSql[i]; @@ -462,6 +467,7 @@ abort_parse: ); #endif /* YYDEBUG */ sqlite3ParserFree(pEngine, sqlite3_free); + db->lookaside.bEnabled = enableLookaside; if( db->mallocFailed ){ pParse->rc = SQLITE_NOMEM; } diff --git a/src/trigger.c b/src/trigger.c index 6563b14..34b3f09 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -10,7 +10,7 @@ ************************************************************************* ** ** -** $Id: trigger.c,v 1.133 2008/12/26 07:56:39 danielk1977 Exp $ +** $Id: trigger.c,v 1.135 2009/02/28 10:47:42 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -33,6 +33,30 @@ void sqlite3DeleteTriggerStep(sqlite3 *db, TriggerStep *pTriggerStep){ } } +/* +** Given table pTab, return a list of all the triggers attached to +** the table. The list is connected by Trigger.pNext pointers. +*/ +Trigger *sqlite3TriggerList(Parse *pParse, Table *pTab){ + Schema * const pTmpSchema = pParse->db->aDb[1].pSchema; + Trigger *pList = 0; /* List of triggers to return */ + + if( pTmpSchema!=pTab->pSchema ){ + HashElem *p; + for(p=sqliteHashFirst(&pTmpSchema->trigHash); p; p=sqliteHashNext(p)){ + Trigger *pTrig = (Trigger *)sqliteHashData(p); + if( pTrig->pTabSchema==pTab->pSchema + && 0==sqlite3StrICmp(pTrig->table, pTab->zName) + ){ + pTrig->pNext = (pList ? pList : pTab->pTrigger); + pList = pTrig; + } + } + } + + return (pList ? pList : pTab->pTrigger); +} + /* ** This is called by the parser when it sees a CREATE TRIGGER statement ** up to the point of the BEGIN before the trigger actions. A Trigger @@ -182,7 +206,7 @@ void sqlite3BeginTrigger( pTrigger->pTabSchema = pTab->pSchema; pTrigger->op = (u8)op; pTrigger->tr_tm = tr_tm==TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER; - pTrigger->pWhen = sqlite3ExprDup(db, pWhen); + pTrigger->pWhen = sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE); pTrigger->pColumns = sqlite3IdListDup(db, pColumns); sqlite3TokenCopy(db, &pTrigger->nameToken,pName); assert( pParse->pNewTrigger==0 ); @@ -209,14 +233,16 @@ void sqlite3FinishTrigger( TriggerStep *pStepList, /* The triggered program */ Token *pAll /* Token that describes the complete CREATE TRIGGER */ ){ - Trigger *pTrig = 0; /* The trigger whose construction is finishing up */ - sqlite3 *db = pParse->db; /* The database */ + Trigger *pTrig = pParse->pNewTrigger; /* Trigger being finished */ + char *zName; /* Name of trigger */ + sqlite3 *db = pParse->db; /* The database */ DbFixer sFix; - int iDb; /* Database containing the trigger */ + int iDb; /* Database containing the trigger */ pTrig = pParse->pNewTrigger; pParse->pNewTrigger = 0; if( pParse->nErr || !pTrig ) goto triggerfinish_cleanup; + zName = pTrig->name; iDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema); pTrig->step_list = pStepList; while( pStepList ){ @@ -242,32 +268,29 @@ void sqlite3FinishTrigger( z = sqlite3DbStrNDup(db, (char*)pAll->z, pAll->n); sqlite3NestedParse(pParse, "INSERT INTO %Q.%s VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')", - db->aDb[iDb].zName, SCHEMA_TABLE(iDb), pTrig->name, + db->aDb[iDb].zName, SCHEMA_TABLE(iDb), zName, pTrig->table, z); sqlite3DbFree(db, z); sqlite3ChangeCookie(pParse, iDb); sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0, sqlite3MPrintf( - db, "type='trigger' AND name='%q'", pTrig->name), P4_DYNAMIC + db, "type='trigger' AND name='%q'", zName), P4_DYNAMIC ); } if( db->init.busy ){ - int n; - Table *pTab; - Trigger *pDel; - pDel = sqlite3HashInsert(&db->aDb[iDb].pSchema->trigHash, - pTrig->name, sqlite3Strlen30(pTrig->name), pTrig); - if( pDel ){ - assert( pDel==pTrig ); + Trigger *pLink = pTrig; + Hash *pHash = &db->aDb[iDb].pSchema->trigHash; + pTrig = sqlite3HashInsert(pHash, zName, sqlite3Strlen30(zName), pTrig); + if( pTrig ){ db->mallocFailed = 1; - goto triggerfinish_cleanup; + }else if( pLink->pSchema==pLink->pTabSchema ){ + Table *pTab; + int n = sqlite3Strlen30(pLink->table) + 1; + pTab = sqlite3HashFind(&pLink->pTabSchema->tblHash, pLink->table, n); + assert( pTab!=0 ); + pLink->pNext = pTab->pTrigger; + pTab->pTrigger = pLink; } - n = sqlite3Strlen30(pTrig->table) + 1; - pTab = sqlite3HashFind(&pTrig->pTabSchema->tblHash, pTrig->table, n); - assert( pTab!=0 ); - pTrig->pNext = pTab->pTrigger; - pTab->pTrigger = pTrig; - pTrig = 0; } triggerfinish_cleanup: @@ -292,17 +315,17 @@ static void sqlitePersistTriggerStep(sqlite3 *db, TriggerStep *p){ p->target.dyn = 1; } if( p->pSelect ){ - Select *pNew = sqlite3SelectDup(db, p->pSelect); + Select *pNew = sqlite3SelectDup(db, p->pSelect, 1); sqlite3SelectDelete(db, p->pSelect); p->pSelect = pNew; } if( p->pWhere ){ - Expr *pNew = sqlite3ExprDup(db, p->pWhere); + Expr *pNew = sqlite3ExprDup(db, p->pWhere, EXPRDUP_REDUCE); sqlite3ExprDelete(db, p->pWhere); p->pWhere = pNew; } if( p->pExprList ){ - ExprList *pNew = sqlite3ExprListDup(db, p->pExprList); + ExprList *pNew = sqlite3ExprListDup(db, p->pExprList, 1); sqlite3ExprListDelete(db, p->pExprList); p->pExprList = pNew; } @@ -546,6 +569,9 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){ sqlite3ChangeCookie(pParse, iDb); sqlite3VdbeAddOp2(v, OP_Close, 0, 0); sqlite3VdbeAddOp4(v, OP_DropTrigger, iDb, 0, 0, pTrigger->name, 0); + if( pParse->nMem<3 ){ + pParse->nMem = 3; + } } } @@ -553,25 +579,15 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){ ** Remove a trigger from the hash tables of the sqlite* pointer. */ void sqlite3UnlinkAndDeleteTrigger(sqlite3 *db, int iDb, const char *zName){ + Hash *pHash = &(db->aDb[iDb].pSchema->trigHash); Trigger *pTrigger; - int nName = sqlite3Strlen30(zName); - pTrigger = sqlite3HashInsert(&(db->aDb[iDb].pSchema->trigHash), - zName, nName, 0); + pTrigger = sqlite3HashInsert(pHash, zName, sqlite3Strlen30(zName), 0); if( pTrigger ){ - Table *pTable = tableOfTrigger(pTrigger); - assert( pTable!=0 ); - if( pTable->pTrigger == pTrigger ){ - pTable->pTrigger = pTrigger->pNext; - }else{ - Trigger *cc = pTable->pTrigger; - while( cc ){ - if( cc->pNext == pTrigger ){ - cc->pNext = cc->pNext->pNext; - break; - } - cc = cc->pNext; - } - assert(cc); + if( pTrigger->pSchema==pTrigger->pTabSchema ){ + Table *pTab = tableOfTrigger(pTrigger); + Trigger **pp; + for(pp=&pTab->pTrigger; *pp!=pTrigger; pp=&((*pp)->pNext)); + *pp = (*pp)->pNext; } sqlite3DeleteTrigger(db, pTrigger); db->flags |= SQLITE_InternChanges; @@ -597,30 +613,31 @@ static int checkColumnOverLap(IdList *pIdList, ExprList *pEList){ } /* -** Return a bit vector to indicate what kind of triggers exist for operation -** "op" on table pTab. If pChanges is not NULL then it is a list of columns -** that are being updated. Triggers only match if the ON clause of the -** trigger definition overlaps the set of columns being updated. -** -** The returned bit vector is some combination of TRIGGER_BEFORE and -** TRIGGER_AFTER. +** Return a list of all triggers on table pTab if there exists at least +** one trigger that must be fired when an operation of type 'op' is +** performed on the table, and, if that operation is an UPDATE, if at +** least one of the columns in pChanges is being modified. */ -int sqlite3TriggersExist( +Trigger *sqlite3TriggersExist( + Parse *pParse, /* Parse context */ Table *pTab, /* The table the contains the triggers */ int op, /* one of TK_DELETE, TK_INSERT, TK_UPDATE */ - ExprList *pChanges /* Columns that change in an UPDATE statement */ + ExprList *pChanges, /* Columns that change in an UPDATE statement */ + int *pMask /* OUT: Mask of TRIGGER_BEFORE|TRIGGER_AFTER */ ){ - Trigger *pTrigger; int mask = 0; - - pTrigger = IsVirtual(pTab) ? 0 : pTab->pTrigger; - while( pTrigger ){ - if( pTrigger->op==op && checkColumnOverLap(pTrigger->pColumns, pChanges) ){ - mask |= pTrigger->tr_tm; + Trigger *pList = sqlite3TriggerList(pParse, pTab); + Trigger *p; + assert( pList==0 || IsVirtual(pTab)==0 ); + for(p=pList; p; p=p->pNext){ + if( p->op==op && checkColumnOverLap(p->pColumns, pChanges) ){ + mask |= p->tr_tm; } - pTrigger = pTrigger->pNext; } - return mask; + if( pMask ){ + *pMask = mask; + } + return (mask ? pList : 0); } /* @@ -677,7 +694,7 @@ static int codeTriggerProgram( pParse->trigStack->orconf = orconf; switch( pTriggerStep->op ){ case TK_SELECT: { - Select *ss = sqlite3SelectDup(db, pTriggerStep->pSelect); + Select *ss = sqlite3SelectDup(db, pTriggerStep->pSelect, 0); if( ss ){ SelectDest dest; @@ -692,8 +709,8 @@ static int codeTriggerProgram( pSrc = targetSrcList(pParse, pTriggerStep); sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0); sqlite3Update(pParse, pSrc, - sqlite3ExprListDup(db, pTriggerStep->pExprList), - sqlite3ExprDup(db, pTriggerStep->pWhere), orconf); + sqlite3ExprListDup(db, pTriggerStep->pExprList, 0), + sqlite3ExprDup(db, pTriggerStep->pWhere, 0), orconf); sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0); break; } @@ -702,8 +719,8 @@ static int codeTriggerProgram( pSrc = targetSrcList(pParse, pTriggerStep); sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0); sqlite3Insert(pParse, pSrc, - sqlite3ExprListDup(db, pTriggerStep->pExprList), - sqlite3SelectDup(db, pTriggerStep->pSelect), + sqlite3ExprListDup(db, pTriggerStep->pExprList, 0), + sqlite3SelectDup(db, pTriggerStep->pSelect, 0), sqlite3IdListDup(db, pTriggerStep->pIdList), orconf); sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0); break; @@ -713,7 +730,7 @@ static int codeTriggerProgram( sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0); pSrc = targetSrcList(pParse, pTriggerStep); sqlite3DeleteFrom(pParse, pSrc, - sqlite3ExprDup(db, pTriggerStep->pWhere)); + sqlite3ExprDup(db, pTriggerStep->pWhere, 0)); sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0); break; } @@ -757,6 +774,7 @@ static int codeTriggerProgram( */ int sqlite3CodeRowTrigger( Parse *pParse, /* Parse context */ + Trigger *pTrigger, /* List of triggers on table pTab */ int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */ ExprList *pChanges, /* Changes list for any UPDATE OF triggers */ int tr_tm, /* One of TRIGGER_BEFORE, TRIGGER_AFTER */ @@ -780,7 +798,7 @@ int sqlite3CodeRowTrigger( assert(newIdx != -1 || oldIdx != -1); - for(p=pTab->pTrigger; p; p=p->pNext){ + for(p=pTrigger; p; p=p->pNext){ int fire_this = 0; /* Determine whether we should code this trigger */ @@ -830,7 +848,7 @@ int sqlite3CodeRowTrigger( /* code the WHEN clause */ endTrigger = sqlite3VdbeMakeLabel(pParse->pVdbe); - whenExpr = sqlite3ExprDup(db, p->pWhen); + whenExpr = sqlite3ExprDup(db, p->pWhen, 0); if( db->mallocFailed || sqlite3ResolveExprNames(&sNC, whenExpr) ){ pParse->trigStack = trigStackEntry.pNext; sqlite3ExprDelete(db, whenExpr); diff --git a/src/update.c b/src/update.c index cd1fd21..8183183 100644 --- a/src/update.c +++ b/src/update.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle UPDATE statements. ** -** $Id: update.c,v 1.191 2008/12/23 23:56:22 drh Exp $ +** $Id: update.c,v 1.196 2009/02/28 10:47:42 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -107,7 +107,7 @@ void sqlite3Update( #ifndef SQLITE_OMIT_TRIGGER int isView; /* Trying to update a view */ - int triggers_exist = 0; /* True if any row triggers exist */ + Trigger *pTrigger; /* List of triggers on pTab, if required */ #endif int iBeginAfterTrigger = 0; /* Address of after trigger program */ int iEndAfterTrigger = 0; /* Exit of after trigger program */ @@ -143,10 +143,10 @@ void sqlite3Update( ** updated is a view */ #ifndef SQLITE_OMIT_TRIGGER - triggers_exist = sqlite3TriggersExist(pTab, TK_UPDATE, pChanges); + pTrigger = sqlite3TriggersExist(pParse, pTab, TK_UPDATE, pChanges, 0); isView = pTab->pSelect!=0; #else -# define triggers_exist 0 +# define pTrigger 0 # define isView 0 #endif #ifdef SQLITE_OMIT_VIEW @@ -154,7 +154,7 @@ void sqlite3Update( # define isView 0 #endif - if( sqlite3IsReadOnly(pParse, pTab, triggers_exist) ){ + if( sqlite3IsReadOnly(pParse, pTab, (pTrigger?1:0)) ){ goto update_cleanup; } if( sqlite3ViewGetColumnNames(pParse, pTab) ){ @@ -167,7 +167,7 @@ void sqlite3Update( /* If there are FOR EACH ROW triggers, allocate cursors for the ** special OLD and NEW tables */ - if( triggers_exist ){ + if( pTrigger ){ newIdx = pParse->nTab++; oldIdx = pParse->nTab++; } @@ -299,27 +299,27 @@ void sqlite3Update( /* Generate the code for triggers. */ - if( triggers_exist ){ + if( pTrigger ){ int iGoto; /* Create pseudo-tables for NEW and OLD */ - sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol); - sqlite3VdbeAddOp2(v, OP_OpenPseudo, oldIdx, 0); - sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol); - sqlite3VdbeAddOp2(v, OP_OpenPseudo, newIdx, 0); + sqlite3VdbeAddOp3(v, OP_OpenPseudo, oldIdx, 0, pTab->nCol); + sqlite3VdbeAddOp3(v, OP_OpenPseudo, newIdx, 0, pTab->nCol); iGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0); addr = sqlite3VdbeMakeLabel(v); iBeginBeforeTrigger = sqlite3VdbeCurrentAddr(v); - if( sqlite3CodeRowTrigger(pParse, TK_UPDATE, pChanges, TRIGGER_BEFORE, pTab, - newIdx, oldIdx, onError, addr, &old_col_mask, &new_col_mask) ){ + if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, + TRIGGER_BEFORE, pTab, newIdx, oldIdx, onError, addr, + &old_col_mask, &new_col_mask) ){ goto update_cleanup; } iEndBeforeTrigger = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0); iBeginAfterTrigger = sqlite3VdbeCurrentAddr(v); - if( sqlite3CodeRowTrigger(pParse, TK_UPDATE, pChanges, TRIGGER_AFTER, pTab, - newIdx, oldIdx, onError, addr, &old_col_mask, &new_col_mask) ){ + if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, + TRIGGER_AFTER, pTab, newIdx, oldIdx, onError, addr, + &old_col_mask, &new_col_mask) ){ goto update_cleanup; } iEndAfterTrigger = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0); @@ -399,7 +399,7 @@ void sqlite3Update( } /* Jump back to this point if a trigger encounters an IGNORE constraint. */ - if( triggers_exist ){ + if( pTrigger ){ sqlite3VdbeResolveLabel(v, addr); } @@ -412,7 +412,7 @@ void sqlite3Update( addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, 0, regOldRowid); } - if( triggers_exist ){ + if( pTrigger ){ int regRowid; int regRow; int regCols; @@ -541,7 +541,7 @@ void sqlite3Update( /* If there are triggers, close all the cursors after each iteration ** through the loop. The fire the after triggers. */ - if( triggers_exist ){ + if( pTrigger ){ sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginAfterTrigger); sqlite3VdbeJumpHere(v, iEndAfterTrigger); } @@ -559,7 +559,7 @@ void sqlite3Update( } } sqlite3VdbeAddOp2(v, OP_Close, iCur, 0); - if( triggers_exist ){ + if( pTrigger ){ sqlite3VdbeAddOp2(v, OP_Close, newIdx, 0); sqlite3VdbeAddOp2(v, OP_Close, oldIdx, 0); } @@ -633,12 +633,12 @@ static void updateVirtualTable( sqlite3CreateIdExpr(pParse, "_rowid_"), 0); if( pRowid ){ pEList = sqlite3ExprListAppend(pParse, pEList, - sqlite3ExprDup(db, pRowid), 0); + sqlite3ExprDup(db, pRowid, 0), 0); } assert( pTab->iPKey<0 ); for(i=0; inCol; i++){ if( aXRef[i]>=0 ){ - pExpr = sqlite3ExprDup(db, pChanges->a[aXRef[i]].pExpr); + pExpr = sqlite3ExprDup(db, pChanges->a[aXRef[i]].pExpr, 0); }else{ pExpr = sqlite3CreateIdExpr(pParse, pTab->aCol[i].zName); } diff --git a/src/utf.c b/src/utf.c index 6822490..ceb0ab6 100644 --- a/src/utf.c +++ b/src/utf.c @@ -12,7 +12,7 @@ ** This file contains routines used to translate between UTF-8, ** UTF-16, UTF-16BE, and UTF-16LE. ** -** $Id: utf.c,v 1.70 2008/12/10 22:30:25 shane Exp $ +** $Id: utf.c,v 1.73 2009/04/01 18:40:32 drh Exp $ ** ** Notes on UTF-8: ** @@ -110,22 +110,20 @@ static const unsigned char sqlite3Utf8Trans1[] = { #define READ_UTF16LE(zIn, c){ \ c = (*zIn++); \ c += ((*zIn++)<<8); \ - if( c>=0xD800 && c<0xE000 ){ \ + if( c>=0xD800 && c<0xE000 ){ \ int c2 = (*zIn++); \ c2 += ((*zIn++)<<8); \ c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10); \ - if( (c & 0xFFFF0000)==0 ) c = 0xFFFD; \ } \ } #define READ_UTF16BE(zIn, c){ \ c = ((*zIn++)<<8); \ c += (*zIn++); \ - if( c>=0xD800 && c<0xE000 ){ \ + if( c>=0xD800 && c<0xE000 ){ \ int c2 = ((*zIn++)<<8); \ c2 += (*zIn++); \ c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10); \ - if( (c & 0xFFFF0000)==0 ) c = 0xFFFD; \ } \ } @@ -168,13 +166,25 @@ static const unsigned char sqlite3Utf8Trans1[] = { || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \ } int sqlite3Utf8Read( - const unsigned char *z, /* First byte of UTF-8 character */ - const unsigned char *zTerm, /* Pretend this byte is 0x00 */ + const unsigned char *zIn, /* First byte of UTF-8 character */ const unsigned char **pzNext /* Write first byte past UTF-8 char here */ ){ int c; - READ_UTF8(z, zTerm, c); - *pzNext = z; + + /* Same as READ_UTF8() above but without the zTerm parameter. + ** For this routine, we assume the UTF8 string is always zero-terminated. + */ + c = *(zIn++); + if( c>=0xc0 ){ + c = sqlite3Utf8Trans1[c-0xc0]; + while( (*zIn & 0xc0)==0x80 ){ + c = (c<<6) + (0x3f & *(zIn++)); + } + if( c<0x80 + || (c&0xFFFFF800)==0xD800 + || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } + } + *pzNext = zIn; return c; } @@ -341,7 +351,8 @@ int sqlite3VdbeMemHandleBom(Mem *pMem){ int rc = SQLITE_OK; u8 bom = 0; - if( pMem->n<0 || pMem->n>1 ){ + assert( pMem->n>=0 ); + if( pMem->n>1 ){ u8 b1 = *(u8 *)pMem->z; u8 b2 = *(((u8 *)pMem->z) + 1); if( b1==0xFE && b2==0xFF ){ @@ -407,17 +418,16 @@ int sqlite3Utf8CharLen(const char *zIn, int nByte){ int sqlite3Utf8To8(unsigned char *zIn){ unsigned char *zOut = zIn; unsigned char *zStart = zIn; - unsigned char *zTerm = &zIn[sqlite3Strlen30((char *)zIn)]; u32 c; while( zIn[0] ){ - c = sqlite3Utf8Read(zIn, zTerm, (const u8**)&zIn); + c = sqlite3Utf8Read(zIn, (const u8**)&zIn); if( c!=0xfffd ){ WRITE_UTF8(zOut, c); } } *zOut = 0; - return zOut - zStart; + return (int)(zOut - zStart); } #endif @@ -445,15 +455,13 @@ char *sqlite3Utf16to8(sqlite3 *db, const void *z, int nByte){ } /* -** pZ is a UTF-16 encoded unicode string. If nChar is less than zero, -** return the number of bytes up to (but not including), the first pair -** of consecutive 0x00 bytes in pZ. If nChar is not less than zero, -** then return the number of bytes in the first nChar unicode characters -** in pZ (or up until the first pair of 0x00 bytes, whichever comes first). +** pZ is a UTF-16 encoded unicode string at least nChar characters long. +** Return the number of bytes in the first nChar unicode characters +** in pZ. nChar must be non-negative. */ int sqlite3Utf16ByteLen(const void *zIn, int nChar){ - unsigned int c = 1; - char const *z = zIn; + int c; + unsigned char const *z = zIn; int n = 0; if( SQLITE_UTF16NATIVE==SQLITE_UTF16BE ){ /* Using an "if (SQLITE_UTF16NATIVE==SQLITE_UTF16BE)" construct here @@ -465,17 +473,17 @@ int sqlite3Utf16ByteLen(const void *zIn, int nChar){ ** which branch will be followed. It is therefore assumed that no runtime ** penalty is paid for this "if" statement. */ - while( c && ((nChar<0) || n0 && n<=4 ); z[0] = 0; - zTerm = z; z = zBuf; - c = sqlite3Utf8Read(z, zTerm, (const u8**)&z); + c = sqlite3Utf8Read(z, (const u8**)&z); t = i; if( i>=0xD800 && i<=0xDFFF ) t = 0xFFFD; if( (i&0xFFFFFFFE)==0xFFFE ) t = 0xFFFD; diff --git a/src/util.c b/src/util.c index 829dfe8..b0bd1e1 100644 --- a/src/util.c +++ b/src/util.c @@ -14,7 +14,7 @@ ** This file contains functions for allocating memory, comparing ** strings, and stuff like that. ** -** $Id: util.c,v 1.248 2009/02/04 03:59:25 shane Exp $ +** $Id: util.c,v 1.249 2009/03/01 22:29:20 drh Exp $ */ #include "sqliteInt.h" #include @@ -694,7 +694,7 @@ u8 sqlite3GetVarint(const unsigned char *p, u64 *v){ /* a: p2<<28 | p4<<14 | p6 (unmasked) */ if (!(a&0x80)) { - a &= (0x7f<<28)|(0x7f<<14)|(0x7f); + a &= (0x1f<<28)|(0x7f<<14)|(0x7f); b &= (0x7f<<14)|(0x7f); b = b<<7; a |= b; @@ -711,7 +711,7 @@ u8 sqlite3GetVarint(const unsigned char *p, u64 *v){ /* b: p3<<28 | p5<<14 | p7 (unmasked) */ if (!(b&0x80)) { - b &= (0x7f<<28)|(0x7f<<14)|(0x7f); + b &= (0x1f<<28)|(0x7f<<14)|(0x7f); /* moved CSE2 up */ /* a &= (0x7f<<14)|(0x7f); */ a = a<<7; @@ -806,8 +806,8 @@ u8 sqlite3GetVarint32(const unsigned char *p, u32 *v){ /* a: p0<<28 | p2<<14 | p4 (unmasked) */ if (!(a&0x80)) { - a &= (0x7f<<28)|(0x7f<<14)|(0x7f); - b &= (0x7f<<28)|(0x7f<<14)|(0x7f); + a &= (0x1f<<28)|(0x7f<<14)|(0x7f); + b &= (0x1f<<28)|(0x7f<<14)|(0x7f); b = b<<7; *v = a | b; return 5; diff --git a/src/vacuum.c b/src/vacuum.c index c622730..acea861 100644 --- a/src/vacuum.c +++ b/src/vacuum.c @@ -14,7 +14,7 @@ ** Most of the code in this file may be omitted by defining the ** SQLITE_OMIT_VACUUM macro. ** -** $Id: vacuum.c,v 1.86 2009/02/03 16:51:25 danielk1977 Exp $ +** $Id: vacuum.c,v 1.87 2009/04/02 20:16:59 drh Exp $ */ #include "sqliteInt.h" #include "vdbeInt.h" @@ -140,8 +140,8 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ } #endif - if( sqlite3BtreeSetPageSize(pTemp, sqlite3BtreeGetPageSize(pMain), nRes) - || (!isMemDb && sqlite3BtreeSetPageSize(pTemp, db->nextPagesize, nRes)) + if( sqlite3BtreeSetPageSize(pTemp, sqlite3BtreeGetPageSize(pMain), nRes, 0) + || (!isMemDb && sqlite3BtreeSetPageSize(pTemp, db->nextPagesize, nRes, 0)) || db->mallocFailed ){ rc = SQLITE_NOMEM; @@ -268,7 +268,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ } if( rc==SQLITE_OK ){ - rc = sqlite3BtreeSetPageSize(pMain, sqlite3BtreeGetPageSize(pTemp), nRes); + rc = sqlite3BtreeSetPageSize(pMain, sqlite3BtreeGetPageSize(pTemp), nRes,1); } end_of_vacuum: diff --git a/src/vdbe.c b/src/vdbe.c index 0b9ad19..eee932a 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -43,7 +43,7 @@ ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. ** -** $Id: vdbe.c,v 1.817 2009/02/16 17:55:47 shane Exp $ +** $Id: vdbe.c,v 1.832 2009/04/10 12:55:17 danielk1977 Exp $ */ #include "sqliteInt.h" #include "vdbeInt.h" @@ -187,7 +187,7 @@ int sqlite3VdbeOpcodeHasProperty(int opcode, int mask){ static VdbeCursor *allocateCursor( Vdbe *p, /* The virtual machine */ int iCur, /* Index of the new VdbeCursor */ - Op *pOp, /* */ + int nField, /* Number of fields in the table or index */ int iDb, /* When database the cursor belongs to, or -1 */ int isBtreeCursor /* */ ){ @@ -213,15 +213,6 @@ static VdbeCursor *allocateCursor( int nByte; VdbeCursor *pCx = 0; - /* If the opcode of pOp is OP_SetNumColumns, then pOp->p2 contains - ** the number of fields in the records contained in the table or - ** index being opened. Use this to reserve space for the - ** VdbeCursor.aType[] array. - */ - int nField = 0; - if( pOp->opcode==OP_SetNumColumns || pOp->opcode==OP_OpenEphemeral ){ - nField = pOp->p2; - } nByte = sizeof(VdbeCursor) + (isBtreeCursor?sqlite3BtreeCursorSize():0) + @@ -563,7 +554,7 @@ int sqlite3VdbeExec( Mem *pOut = 0; /* Output operand */ u8 opProperty; int iCompare = 0; /* Result of last OP_Compare operation */ - int *aPermute = 0; /* Permuation of columns for OP_Compare */ + int *aPermute = 0; /* Permutation of columns for OP_Compare */ #ifdef VDBE_PROFILE u64 start; /* CPU clock count at start of opcode */ int origPc; /* Program counter at start of opcode */ @@ -571,11 +562,13 @@ int sqlite3VdbeExec( #ifndef SQLITE_OMIT_PROGRESS_CALLBACK int nProgressOps = 0; /* Opcodes executed since progress callback. */ #endif - UnpackedRecord aTempRec[16]; /* Space to hold a transient UnpackedRecord */ + + /* Temporary space into which to unpack a record. */ + char aTempRec[ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*3 + 7]; assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */ assert( db->magic==SQLITE_MAGIC_BUSY ); - sqlite3BtreeMutexArrayEnter(&p->aMutex); + sqlite3VdbeMutexArrayEnter(p); if( p->rc==SQLITE_NOMEM ){ /* This happens if a malloc() inside a call to sqlite3_column_text() or ** sqlite3_column_text16() failed. */ @@ -819,6 +812,16 @@ case OP_Yield: { /* in1 */ break; } +/* Opcode: HaltIfNull P1 P2 P3 P4 * +** +** Check the value in register P3. If is is NULL then Halt using +** parameter P1, P2, and P4 as if this were a Halt instruction. If the +** value in register P3 is not NULL, then this routine is a no-op. +*/ +case OP_HaltIfNull: { /* in3 */ + if( (pIn3->flags & MEM_Null)==0 ) break; + /* Fall through into OP_Halt */ +} /* Opcode: Halt P1 P2 * P4 * ** @@ -967,26 +970,34 @@ case OP_Blob: { /* out2-prerelease */ break; } -/* Opcode: Variable P1 P2 * * * +/* Opcode: Variable P1 P2 P3 P4 * ** -** The value of variable P1 is written into register P2. A variable is -** an unknown in the original SQL string as handed to sqlite3_compile(). -** Any occurrence of the '?' character in the original SQL is considered -** a variable. Variables in the SQL string are number from left to -** right beginning with 1. The values of variables are set using the -** sqlite3_bind() API. +** Transfer the values of bound parameters P1..P1+P3-1 into registers +** P2..P2+P3-1. +** +** If the parameter is named, then its name appears in P4 and P3==1. +** The P4 value is used by sqlite3_bind_parameter_name(). */ -case OP_Variable: { /* out2-prerelease */ +case OP_Variable: { int j = pOp->p1 - 1; + int k = pOp->p2; Mem *pVar; - assert( j>=0 && jnVar ); + int n = pOp->p3; + assert( j>=0 && j+n<=p->nVar ); + assert( k>=1 && k+n-1<=p->nMem ); + assert( pOp->p4.z==0 || pOp->p3==1 ); - pVar = &p->aVar[j]; - if( sqlite3VdbeMemTooBig(pVar) ){ - goto too_big; + while( n-- > 0 ){ + pVar = &p->aVar[j++]; + if( sqlite3VdbeMemTooBig(pVar) ){ + goto too_big; + } + pOut = &p->aMem[k++]; + sqlite3VdbeMemReleaseExternal(pOut); + pOut->flags = MEM_Null; + sqlite3VdbeMemShallowCopy(pOut, pVar, MEM_Static); + UPDATE_MAX_BLOBSIZE(pOut); } - sqlite3VdbeMemShallowCopy(pOut, &p->aVar[j], MEM_Static); - UPDATE_MAX_BLOBSIZE(pOut); break; } @@ -1002,15 +1013,14 @@ case OP_Move: { int n = pOp->p3; int p1 = pOp->p1; int p2 = pOp->p2; - assert( n>0 ); - assert( p1>0 ); - assert( p1+nnMem ); - pIn1 = &p->aMem[p1]; - assert( p2>0 ); - assert( p2+nnMem ); - pOut = &p->aMem[p2]; + assert( n>0 && p1>0 && p2>0 ); assert( p1+n<=p2 || p2+n<=p1 ); + + pIn1 = &p->aMem[p1]; + pOut = &p->aMem[p2]; while( n-- ){ + assert( pOut<=&p->aMem[p->nMem] ); + assert( pIn1<=&p->aMem[p->nMem] ); zMalloc = pOut->zMalloc; pOut->zMalloc = 0; sqlite3VdbeMemMove(pOut, pIn1); @@ -1076,7 +1086,24 @@ case OP_ResultRow: { int i; assert( p->nResColumn==pOp->p2 ); assert( pOp->p1>0 ); - assert( pOp->p1+pOp->p2<=p->nMem ); + assert( pOp->p1+pOp->p2<=p->nMem+1 ); + + /* If the SQLITE_CountRows flag is set in sqlite3.flags mask, then + ** DML statements invoke this opcode to return the number of rows + ** modified to the user. This is the only way that a VM that + ** opens a statement transaction may invoke this opcode. + ** + ** In case this is such a statement, close any statement transaction + ** opened by this VM before returning control to the user. This is to + ** ensure that statement-transactions are always nested, not overlapping. + ** If the open statement-transaction is not closed here, then the user + ** may step another VM that opens its own statement transaction. This + ** may lead to overlapping statement transactions. + */ + assert( p->iStatement==0 || db->flags&SQLITE_CountRows ); + if( SQLITE_OK!=(rc = sqlite3VdbeCloseStatement(p, SAVEPOINT_RELEASE)) ){ + break; + } /* Invalidate all ephemeral cursor row caches */ p->cacheCtr = (p->cacheCtr + 2)|1; @@ -1095,7 +1122,6 @@ case OP_ResultRow: { /* Return SQLITE_ROW */ - p->nCallback++; p->pc = pc + 1; rc = SQLITE_ROW; goto vdbe_return; @@ -1300,7 +1326,7 @@ case OP_Function: { apVal = p->apArg; assert( apVal || n==0 ); - assert( n==0 || (pOp->p2>0 && pOp->p2+n<=p->nMem) ); + assert( n==0 || (pOp->p2>0 && pOp->p2+n<=p->nMem+1) ); assert( pOp->p3p2 || pOp->p3>=pOp->p2+n ); pArg = &p->aMem[pOp->p2]; for(i=0; i0 ); assert( pKeyInfo!=0 ); p1 = pOp->p1; - assert( p1>0 && p1+n-1nMem ); + assert( p1>0 && p1+n<=p->nMem+1 ); p2 = pOp->p2; - assert( p2>0 && p2+n-1nMem ); + assert( p2>0 && p2+n<=p->nMem+1 ); for(i=0; ip1; zAffinity = pOp->p4.z; - assert( nField>0 && pOp->p2>0 && pOp->p2+nField<=p->nMem ); + assert( nField>0 && pOp->p2>0 && pOp->p2+nField<=p->nMem+1 ); pData0 = &p->aMem[nField]; nField = pOp->p2; pLast = &pData0[nField-1]; @@ -2330,6 +2358,26 @@ case OP_MakeRecord: { break; } +/* Opcode: Count P1 P2 * * * +** +** Store the number of entries (an integer value) in the table or index +** opened by cursor P1 in register P2 +*/ +#ifndef SQLITE_OMIT_BTREECOUNT +case OP_Count: { /* out2-prerelease */ + i64 nEntry; + BtCursor *pCrsr = p->apCsr[pOp->p1]->pCursor; + if( pCrsr ){ + rc = sqlite3BtreeCount(pCrsr, &nEntry); + }else{ + nEntry = 0; + } + pOut->flags = MEM_Int; + pOut->u.i = nEntry; + break; +} +#endif + /* Opcode: Statement P1 * * * * ** ** Begin an individual statement transaction which is part of a larger @@ -2361,10 +2409,12 @@ case OP_Statement: { pBt = db->aDb[i].pBt; assert( sqlite3BtreeIsInTrans(pBt) ); assert( (p->btreeMask & (1<openedStatement = 1; + if( p->iStatement==0 ){ + assert( db->nStatement>=0 && db->nSavepoint>=0 ); + db->nStatement++; + p->iStatement = db->nSavepoint + db->nStatement; } + rc = sqlite3BtreeBeginStmt(pBt, p->iStatement); } break; } @@ -2471,7 +2521,7 @@ case OP_Savepoint: { rc = sqlite3BtreeSavepoint(db->aDb[ii].pBt, p1, iSavepoint); if( rc!=SQLITE_OK ){ goto abort_due_to_error; - } + } } if( p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){ sqlite3ExpirePreparedStatements(db); @@ -2507,7 +2557,8 @@ case OP_Savepoint: { ** ** Set the database auto-commit flag to P1 (1 or 0). If P2 is true, roll ** back any currently active btree transactions. If there are any active -** VMs (apart from this one), then the COMMIT or ROLLBACK statement fails. +** VMs (apart from this one), then a ROLLBACK fails. A COMMIT fails if +** there are active writing VMs or active VMs that use shared cache. ** ** This instruction causes the VM to halt. */ @@ -2550,6 +2601,7 @@ case OP_AutoCommit: { goto vdbe_return; } } + assert( db->nStatement==0 ); sqlite3CloseSavepoints(db); if( p->rc==SQLITE_OK ){ rc = SQLITE_DONE; @@ -2767,9 +2819,11 @@ case OP_VerifyCookie: { ** to get a read lock but fails, the script terminates with an ** SQLITE_BUSY error code. ** -** The P4 value is a pointer to a KeyInfo structure that defines the -** content and collating sequence of indices. P4 is NULL for cursors -** that are not pointing to indices. +** The P4 value may be either an integer (P4_INT32) or a pointer to +** a KeyInfo structure (P4_KEYINFO). If it is a pointer to a KeyInfo +** structure, then said structure defines the content and collating +** sequence of the index being opened. Otherwise, if P4 is an integer +** value, it is set to the number of columns in the table. ** ** See also OpenWrite. */ @@ -2779,9 +2833,11 @@ case OP_VerifyCookie: { ** page is P2. Or if P5!=0 use the content of register P2 to find the ** root page. ** -** The P4 value is a pointer to a KeyInfo structure that defines the -** content and collating sequence of indices. P4 is NULL for cursors -** that are not pointing to indices. +** The P4 value may be either an integer (P4_INT32) or a pointer to +** a KeyInfo structure (P4_KEYINFO). If it is a pointer to a KeyInfo +** structure, then said structure defines the content and collating +** sequence of the index being opened. Otherwise, if P4 is an integer +** value, it is set to the number of columns in the table. ** ** This instruction works just like OpenRead except that it opens the cursor ** in read/write mode. For a given table, there can be one or more read-only @@ -2791,6 +2847,8 @@ case OP_VerifyCookie: { */ case OP_OpenRead: case OP_OpenWrite: { + int nField = 0; + KeyInfo *pKeyInfo = 0; int i = pOp->p1; int p2 = pOp->p2; int iDb = pOp->p3; @@ -2824,16 +2882,19 @@ case OP_OpenWrite: { } } assert( i>=0 ); - pCur = allocateCursor(p, i, &pOp[-1], iDb, 1); + if( pOp->p4type==P4_KEYINFO ){ + pKeyInfo = pOp->p4.pKeyInfo; + pKeyInfo->enc = ENC(p->db); + nField = pKeyInfo->nField+1; + }else if( pOp->p4type==P4_INT32 ){ + nField = pOp->p4.i; + } + pCur = allocateCursor(p, i, nField, iDb, 1); if( pCur==0 ) goto no_mem; pCur->nullRow = 1; - rc = sqlite3BtreeCursor(pX, p2, wrFlag, pOp->p4.p, pCur->pCursor); - if( pOp->p4type==P4_KEYINFO ){ - pCur->pKeyInfo = pOp->p4.pKeyInfo; - pCur->pKeyInfo->enc = ENC(p->db); - }else{ - pCur->pKeyInfo = 0; - } + rc = sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->pCursor); + pCur->pKeyInfo = pKeyInfo; + switch( rc ){ case SQLITE_BUSY: { p->pc = pc; @@ -2908,7 +2969,7 @@ case OP_OpenEphemeral: { SQLITE_OPEN_TRANSIENT_DB; assert( i>=0 ); - pCx = allocateCursor(p, i, pOp, -1, 1); + pCx = allocateCursor(p, i, pOp->p2, -1, 1); if( pCx==0 ) goto no_mem; pCx->nullRow = 1; rc = sqlite3BtreeFactory(db, 0, 1, SQLITE_DEFAULT_TEMP_CACHE_SIZE, openFlags, @@ -2943,7 +3004,7 @@ case OP_OpenEphemeral: { break; } -/* Opcode: OpenPseudo P1 P2 * * * +/* Opcode: OpenPseudo P1 P2 P3 * * ** ** Open a new cursor that points to a fake table that contains a single ** row of data. Any attempt to write a second row of data causes the @@ -2962,12 +3023,15 @@ case OP_OpenEphemeral: { ** is stored. In this case, the vdbe program must ensure that the ** memory cell containing the row data is not overwritten until the ** pseudo table is closed (or a new row is inserted into it). +** +** P3 is the number of fields in the records that will be stored by +** the pseudo-table. */ case OP_OpenPseudo: { int i = pOp->p1; VdbeCursor *pCx; assert( i>=0 ); - pCx = allocateCursor(p, i, &pOp[-1], -1, 0); + pCx = allocateCursor(p, i, pOp->p3, -1, 0); if( pCx==0 ) goto no_mem; pCx->nullRow = 1; pCx->pseudoTable = 1; @@ -3512,9 +3576,8 @@ case OP_NewRowid: { /* out2-prerelease */ #endif if( !pC->useRandomRowid ){ - if( pC->nextRowidValid ){ - v = pC->nextRowid; - }else{ + v = sqlite3BtreeGetCachedRowid(pC->pCursor); + if( v==0 ){ rc = sqlite3BtreeLast(pC->pCursor, &res); if( rc!=SQLITE_OK ){ goto abort_due_to_error; @@ -3551,12 +3614,7 @@ case OP_NewRowid: { /* out2-prerelease */ } #endif - if( vnextRowidValid = 1; - pC->nextRowid = v+1; - }else{ - pC->nextRowidValid = 0; - } + sqlite3BtreeSetCachedRowid(pC->pCursor, vuseRandomRowid ){ assert( pOp->p3==0 ); /* SQLITE_FULL must have occurred prior to this */ @@ -3634,9 +3692,6 @@ case OP_Insert: { iKey = intToKey(pKey->u.i); if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = pKey->u.i; - if( pC->nextRowidValid && pKey->u.i>=pC->nextRowid ){ - pC->nextRowidValid = 0; - } if( pData->flags & MEM_Null ){ pData->z = 0; pData->n = 0; @@ -3671,6 +3726,7 @@ case OP_Insert: { }else{ nZero = 0; } + sqlite3BtreeSetCachedRowid(pC->pCursor, 0); rc = sqlite3BtreeInsert(pC->pCursor, 0, iKey, pData->z, pData->n, nZero, pOp->p5 & OPFLAG_APPEND); @@ -3733,8 +3789,8 @@ case OP_Delete: { rc = sqlite3VdbeCursorMoveto(pC); if( rc ) goto abort_due_to_error; + sqlite3BtreeSetCachedRowid(pC->pCursor, 0); rc = sqlite3BtreeDelete(pC->pCursor); - pC->nextRowidValid = 0; pC->cacheStatus = CACHE_STALE; /* Invoke the update-hook if required. */ @@ -4066,7 +4122,7 @@ case OP_IdxDelete: { VdbeCursor *pC; BtCursor *pCrsr; assert( pOp->p3>0 ); - assert( pOp->p2>0 && pOp->p2+pOp->p3<=p->nMem ); + assert( pOp->p2>0 && pOp->p2+pOp->p3<=p->nMem+1 ); assert( i>=0 && inCursor ); assert( p->apCsr[i]!=0 ); if( (pCrsr = (pC = p->apCsr[i])->pCursor)!=0 ){ @@ -4099,11 +4155,13 @@ case OP_IdxRowid: { /* out2-prerelease */ BtCursor *pCrsr; VdbeCursor *pC; + assert( i>=0 && inCursor ); assert( p->apCsr[i]!=0 ); if( (pCrsr = (pC = p->apCsr[i])->pCursor)!=0 ){ i64 rowid; - + rc = sqlite3VdbeCursorMoveto(pC); + if( rc ) goto abort_due_to_error; assert( pC->deferredMoveto==0 ); assert( pC->isTable==0 ); if( !pC->nullRow ){ @@ -4242,7 +4300,7 @@ case OP_Destroy: { /* out2-prerelease */ ** P2==1 then the table to be clear is in the auxiliary database file ** that is used to store tables create using CREATE TEMPORARY TABLE. ** -** If the P3 value is non-zero, then the table refered to must be an +** If the P3 value is non-zero, then the table referred to must be an ** intkey table (an SQL table, not an index). In this case the row change ** count is incremented by the number of rows in the table being cleared. ** If P3 is greater than zero, then the value stored in register P3 is @@ -4321,33 +4379,58 @@ case OP_CreateTable: { /* out2-prerelease */ ** then runs the new virtual machine. It is thus a re-entrant opcode. */ case OP_ParseSchema: { - char *zSql; int iDb = pOp->p1; - const char *zMaster; - InitData initData; - assert( iDb>=0 && iDbnDb ); - if( !pOp->p2 && !DbHasProperty(db, iDb, DB_SchemaLoaded) ){ - break; + + /* If pOp->p2 is 0, then this opcode is being executed to read a + ** single row, for example the row corresponding to a new index + ** created by this VDBE, from the sqlite_master table. It only + ** does this if the corresponding in-memory schema is currently + ** loaded. Otherwise, the new index definition can be loaded along + ** with the rest of the schema when it is required. + ** + ** Although the mutex on the BtShared object that corresponds to + ** database iDb (the database containing the sqlite_master table + ** read by this instruction) is currently held, it is necessary to + ** obtain the mutexes on all attached databases before checking if + ** the schema of iDb is loaded. This is because, at the start of + ** the sqlite3_exec() call below, SQLite will invoke + ** sqlite3BtreeEnterAll(). If all mutexes are not already held, the + ** iDb mutex may be temporarily released to avoid deadlock. If + ** this happens, then some other thread may delete the in-memory + ** schema of database iDb before the SQL statement runs. The schema + ** will not be reloaded becuase the db->init.busy flag is set. This + ** can result in a "no such table: sqlite_master" or "malformed + ** database schema" error being returned to the user. + */ + assert( sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) ); + sqlite3BtreeEnterAll(db); + if( pOp->p2 || DbHasProperty(db, iDb, DB_SchemaLoaded) ){ + const char *zMaster = SCHEMA_TABLE(iDb); + char *zSql; + InitData initData; + initData.db = db; + initData.iDb = pOp->p1; + initData.pzErrMsg = &p->zErrMsg; + zSql = sqlite3MPrintf(db, + "SELECT name, rootpage, sql FROM '%q'.%s WHERE %s", + db->aDb[iDb].zName, zMaster, pOp->p4.z); + if( zSql==0 ){ + rc = SQLITE_NOMEM; + }else{ + (void)sqlite3SafetyOff(db); + assert( db->init.busy==0 ); + db->init.busy = 1; + initData.rc = SQLITE_OK; + assert( !db->mallocFailed ); + rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0); + if( rc==SQLITE_OK ) rc = initData.rc; + sqlite3DbFree(db, zSql); + db->init.busy = 0; + (void)sqlite3SafetyOn(db); + } } - zMaster = SCHEMA_TABLE(iDb); - initData.db = db; - initData.iDb = pOp->p1; - initData.pzErrMsg = &p->zErrMsg; - zSql = sqlite3MPrintf(db, - "SELECT name, rootpage, sql FROM '%q'.%s WHERE %s", - db->aDb[iDb].zName, zMaster, pOp->p4.z); - if( zSql==0 ) goto no_mem; - (void)sqlite3SafetyOff(db); - assert( db->init.busy==0 ); - db->init.busy = 1; - initData.rc = SQLITE_OK; - assert( !db->mallocFailed ); - rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0); - if( rc==SQLITE_OK ) rc = initData.rc; - sqlite3DbFree(db, zSql); - db->init.busy = 0; - (void)sqlite3SafetyOn(db); + sqlite3BtreeLeaveAll(db); if( rc==SQLITE_NOMEM ){ goto no_mem; } @@ -4781,7 +4864,7 @@ case OP_TableLock: { assert( (p->btreeMask & (1<aDb[p1].pBt, pOp->p2, isWriteLock); - if( rc==SQLITE_LOCKED ){ + if( (rc&0xFF)==SQLITE_LOCKED ){ const char *z = pOp->p4.z; sqlite3SetString(&p->zErrMsg, db, "database table is locked: %s", z); } @@ -4796,8 +4879,8 @@ case OP_TableLock: { ** xBegin method for that table. ** ** Also, whether or not P4 is set, check that this is not being called from -** within a callback to a virtual table xSync() method. If it is, set the -** error code to SQLITE_LOCKED. +** within a callback to a virtual table xSync() method. If it is, the error +** code will be set to SQLITE_LOCKED. */ case OP_VBegin: { sqlite3_vtab *pVtab = pOp->p4.pVtab; @@ -4863,7 +4946,7 @@ case OP_VOpen: { pVtabCursor->pVtab = pVtab; /* Initialise vdbe cursor object */ - pCur = allocateCursor(p, pOp->p1, &pOp[-1], -1, 0); + pCur = allocateCursor(p, pOp->p1, 0, -1, 0); if( pCur ){ pCur->pVtabCursor = pVtabCursor; pCur->pModule = pVtabCursor->pVtab->pModule; @@ -5023,7 +5106,7 @@ case OP_VColumn: { pVtab->zErrMsg = 0; /* Copy the result of the function to the P3 register. We - ** do this regardless of whether or not an error occured to ensure any + ** do this regardless of whether or not an error occurred to ensure any ** dynamic allocation in sContext.s (a Mem struct) is released. */ sqlite3VdbeChangeEncoding(&sContext.s, encoding); @@ -5204,13 +5287,14 @@ case OP_Pagecount: { /* out2-prerelease */ ** the UTF-8 string contained in P4 is emitted on the trace callback. */ case OP_Trace: { - if( pOp->p4.z ){ + char *zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql); + if( zTrace ){ if( db->xTrace ){ - db->xTrace(db->pTraceArg, pOp->p4.z); + db->xTrace(db->pTraceArg, zTrace); } #ifdef SQLITE_DEBUG if( (db->flags & SQLITE_SqlTrace)!=0 ){ - sqlite3DebugPrintf("SQL-trace: %s\n", pOp->p4.z); + sqlite3DebugPrintf("SQL-trace: %s\n", zTrace); } #endif /* SQLITE_DEBUG */ } diff --git a/src/vdbe.h b/src/vdbe.h index 312e389..64c440e 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -15,7 +15,7 @@ ** or VDBE. The VDBE implements an abstract machine that runs a ** simple program to access and modify the underlying database. ** -** $Id: vdbe.h,v 1.139 2008/10/31 10:53:23 danielk1977 Exp $ +** $Id: vdbe.h,v 1.141 2009/04/10 00:56:29 drh Exp $ */ #ifndef _SQLITE_VDBE_H_ #define _SQLITE_VDBE_H_ @@ -181,14 +181,13 @@ void sqlite3VdbeSetNumCols(Vdbe*,int); int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, void(*)(void*)); void sqlite3VdbeCountChanges(Vdbe*); sqlite3 *sqlite3VdbeDb(Vdbe*); -void sqlite3VdbeSetSql(Vdbe*, const char *z, int n); +void sqlite3VdbeSetSql(Vdbe*, const char *z, int n, int); void sqlite3VdbeSwap(Vdbe*,Vdbe*); #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT int sqlite3VdbeReleaseMemory(int); #endif -UnpackedRecord *sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*, - UnpackedRecord*,int); +UnpackedRecord *sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,char*,int); void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord*); int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*); diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 8ff7939..d9becb1 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -15,7 +15,7 @@ ** 6000 lines long) it was split up into several smaller files and ** this header information was factored out. ** -** $Id: vdbeInt.h,v 1.162 2009/02/03 15:39:01 drh Exp $ +** $Id: vdbeInt.h,v 1.167 2009/04/10 12:55:17 danielk1977 Exp $ */ #ifndef _VDBEINT_H_ #define _VDBEINT_H_ @@ -59,13 +59,11 @@ struct VdbeCursor { BtCursor *pCursor; /* The cursor structure of the backend */ int iDb; /* Index of cursor database in db->aDb[] (or -1) */ i64 lastRowid; /* Last rowid from a Next or NextIdx operation */ - i64 nextRowid; /* Next rowid returned by OP_NewRowid */ Bool zeroed; /* True if zeroed out and ready for reuse */ Bool rowidIsValid; /* True if lastRowid is valid */ Bool atFirst; /* True if pointing to first entry */ Bool useRandomRowid; /* Generate new record numbers semi-randomly */ Bool nullRow; /* True if pointing to a row with no data */ - Bool nextRowidValid; /* True if the nextRowid field is valid */ Bool pseudoTable; /* This is a NEW or OLD pseudo-tables of a trigger */ Bool ephemPseudoTable; Bool deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */ @@ -279,16 +277,13 @@ struct Vdbe { u32 magic; /* Magic number for sanity checking */ int nMem; /* Number of memory locations currently allocated */ Mem *aMem; /* The memory locations */ - int nCallback; /* Number of callbacks invoked so far */ int cacheCtr; /* VdbeCursor row cache generation counter */ int contextStackTop; /* Index of top element in the context stack */ int contextStackDepth; /* The size of the "context" stack */ Context *contextStack; /* Stack used by opcodes ContextPush & ContextPop*/ int pc; /* The program counter */ int rc; /* Value to return */ - unsigned uniqueCnt; /* Used by OP_MakeRecord when P2!=0 */ int errorAction; /* Recovery action to do in case of an error */ - int inTempTrans; /* True if temp database is transactioned */ int nResColumn; /* Number of columns in one row of the result set */ char **azResColumn; /* Values for one row of result */ char *zErrMsg; /* Error message written here */ @@ -300,17 +295,18 @@ struct Vdbe { u8 inVtabMethod; /* See comments above */ u8 usesStmtJournal; /* True if uses a statement journal */ u8 readOnly; /* True for read-only statements */ + u8 isPrepareV2; /* True if prepared with prepare_v2() */ int nChange; /* Number of db changes made since last reset */ i64 startTime; /* Time when query started - used for profiling */ int btreeMask; /* Bitmask of db->aDb[] entries referenced */ BtreeMutexArray aMutex; /* An array of Btree used here and needing locks */ int aCounter[2]; /* Counters used by sqlite3_stmt_status() */ - int nSql; /* Number of bytes in zSql */ char *zSql; /* Text of the SQL statement that generated this */ + void *pFree; /* Free this when deleting the vdbe */ #ifdef SQLITE_DEBUG FILE *trace; /* Write an execution trace here, if not NULL */ #endif - int openedStatement; /* True if this VM has opened a statement journal */ + int iStatement; /* Statement number (or 0 if has not opened stmt) */ #ifdef SQLITE_SSE int fetchId; /* Statement number used by sqlite3_fetch_statement */ int lru; /* Counter used for LRU cache replacement */ @@ -378,10 +374,17 @@ int sqlite3VdbeMemFinalize(Mem*, FuncDef*); const char *sqlite3OpcodeName(int); int sqlite3VdbeOpcodeHasProperty(int, int); int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve); +int sqlite3VdbeCloseStatement(Vdbe *, int); #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT int sqlite3VdbeReleaseBuffers(Vdbe *p); #endif +#ifndef SQLITE_OMIT_SHARED_CACHE +void sqlite3VdbeMutexArrayEnter(Vdbe *p); +#else +# define sqlite3VdbeMutexArrayEnter(p) +#endif + int sqlite3VdbeMemTranslate(Mem*, u8); #ifdef SQLITE_DEBUG void sqlite3VdbePrintSql(Vdbe*); diff --git a/src/vdbeapi.c b/src/vdbeapi.c index d3274b5..1ef51df 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -13,7 +13,7 @@ ** This file contains code use to implement APIs that are part of the ** VDBE. ** -** $Id: vdbeapi.c,v 1.151 2009/02/04 03:59:25 shane Exp $ +** $Id: vdbeapi.c,v 1.161 2009/04/10 23:11:31 drh Exp $ */ #include "sqliteInt.h" #include "vdbeInt.h" @@ -204,12 +204,14 @@ int sqlite3_finalize(sqlite3_stmt *pStmt){ rc = SQLITE_OK; }else{ Vdbe *v = (Vdbe*)pStmt; + sqlite3 *db = v->db; #if SQLITE_THREADSAFE sqlite3_mutex *mutex = v->db->mutex; #endif sqlite3_mutex_enter(mutex); stmtLruRemove(v); rc = sqlite3VdbeFinalize(v); + rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(mutex); } return rc; @@ -234,6 +236,7 @@ int sqlite3_reset(sqlite3_stmt *pStmt){ stmtLruAdd(v); sqlite3VdbeMakeReady(v, -1, 0, 0, 0); assert( (rc & (v->db->errMask))==rc ); + rc = sqlite3ApiExit(v->db, rc); sqlite3_mutex_leave(v->db->mutex); } return rc; @@ -440,7 +443,7 @@ static int sqlite3Step(Vdbe *p){ } if( p->pc<=0 && p->expired ){ - if( p->rc==SQLITE_OK ){ + if( ALWAYS(p->rc==SQLITE_OK) ){ p->rc = SQLITE_SCHEMA; } rc = SQLITE_ERROR; @@ -488,34 +491,41 @@ static int sqlite3Step(Vdbe *p){ #ifndef SQLITE_OMIT_TRACE /* Invoke the profile callback if there is one */ - if( rc!=SQLITE_ROW && db->xProfile && !db->init.busy && p->nOp>0 - && p->aOp[0].opcode==OP_Trace && p->aOp[0].p4.z!=0 ){ + if( rc!=SQLITE_ROW && db->xProfile && !db->init.busy && p->zSql ){ double rNow; u64 elapseTime; sqlite3OsCurrentTime(db->pVfs, &rNow); elapseTime = (u64)((rNow - (int)rNow)*3600.0*24.0*1000000000.0); elapseTime -= p->startTime; - db->xProfile(db->pProfileArg, p->aOp[0].p4.z, elapseTime); + db->xProfile(db->pProfileArg, p->zSql, elapseTime); } #endif db->errCode = rc; - /*sqlite3Error(p->db, rc, 0);*/ - p->rc = sqlite3ApiExit(p->db, p->rc); -end_of_step: - assert( (rc&0xff)==rc ); - if( p->zSql && (rc&0xff)db->errCode = p->rc; - /* sqlite3Error(p->db, p->rc, 0); */ - return p->rc; - }else{ - /* This is for legacy sqlite3_prepare() builds and when the code - ** is SQLITE_ROW or SQLITE_DONE */ - return rc; + if( SQLITE_NOMEM==sqlite3ApiExit(p->db, p->rc) ){ + p->rc = SQLITE_NOMEM; } +end_of_step: + /* At this point local variable rc holds the value that should be + ** returned if this statement was compiled using the legacy + ** sqlite3_prepare() interface. According to the docs, this can only + ** be one of the values in the first assert() below. Variable p->rc + ** contains the value that would be returned if sqlite3_finalize() + ** were called on statement p. + */ + assert( rc==SQLITE_ROW || rc==SQLITE_DONE || rc==SQLITE_ERROR + || rc==SQLITE_BUSY || rc==SQLITE_MISUSE + ); + assert( p->rc!=SQLITE_ROW && p->rc!=SQLITE_DONE ); + if( p->isPrepareV2 && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){ + /* If this statement was prepared using sqlite3_prepare_v2(), and an + ** error has occured, then return the error code in p->rc to the + ** caller. Set the error code in the database handle to the same value. + */ + rc = db->errCode = p->rc; + } + return (rc&db->errMask); } /* @@ -545,11 +555,11 @@ int sqlite3_step(sqlite3_stmt *pStmt){ sqlite3_mutex_enter(db->mutex); while( (rc = sqlite3Step(v))==SQLITE_SCHEMA && cnt++ < 5 - && vdbeReprepare(v) ){ + && (rc = vdbeReprepare(v))==SQLITE_OK ){ sqlite3_reset(pStmt); v->expired = 0; } - if( rc==SQLITE_SCHEMA && v->zSql && db->pErr ){ + if( rc==SQLITE_SCHEMA && ALWAYS(v->isPrepareV2) && ALWAYS(db->pErr) ){ /* This case occurs after failing to recompile an sql statement. ** The error message from the SQL compiler has already been loaded ** into the database handle. This block copies the error message @@ -608,7 +618,7 @@ void sqlite3InvalidFunction( const char *zName = context->pFunc->zName; char *zErr; UNUSED_PARAMETER2(NotUsed, NotUsed2); - zErr = sqlite3MPrintf(0, + zErr = sqlite3_mprintf( "unable to use function %s in the requested context", zName); sqlite3_result_error(context, zErr, -1); sqlite3_free(zErr); @@ -711,7 +721,7 @@ failed: ** context. */ int sqlite3_aggregate_count(sqlite3_context *p){ - assert( p && p->pFunc && p->pFunc->xStep ); + assert( p && p->pMem && p->pFunc && p->pFunc->xStep ); return p->pMem->n; } #endif @@ -754,7 +764,7 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){ }else{ /* ((double)0) In case of SQLITE_OMIT_FLOATING_POINT... */ static const Mem nullMem = {{0}, (double)0, 0, "", 0, MEM_Null, SQLITE_NULL, 0, 0, 0 }; - if( pVm->db ){ + if( pVm && ALWAYS(pVm->db) ){ sqlite3_mutex_enter(pVm->db->mutex); sqlite3Error(pVm->db, SQLITE_RANGE, 0); } @@ -894,24 +904,23 @@ static const void *columnName( const void *ret = 0; Vdbe *p = (Vdbe *)pStmt; int n; + sqlite3 *db = p->db; - - if( p!=0 ){ - n = sqlite3_column_count(pStmt); - if( N=0 ){ - N += useType*n; - sqlite3_mutex_enter(p->db->mutex); - ret = xFunc(&p->aColName[N]); - - /* A malloc may have failed inside of the xFunc() call. If this - ** is the case, clear the mallocFailed flag and return NULL. - */ - if( p->db && p->db->mallocFailed ){ - p->db->mallocFailed = 0; - ret = 0; - } - sqlite3_mutex_leave(p->db->mutex); + assert( db!=0 ); + n = sqlite3_column_count(pStmt); + if( N=0 ){ + N += useType*n; + sqlite3_mutex_enter(db->mutex); + assert( db->mallocFailed==0 ); + ret = xFunc(&p->aColName[N]); + /* A malloc may have failed inside of the xFunc() call. If this + ** is the case, clear the mallocFailed flag and return NULL. + */ + if( db->mallocFailed ){ + db->mallocFailed = 0; + ret = 0; } + sqlite3_mutex_leave(db->mutex); } return ret; } @@ -1151,8 +1160,8 @@ int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_value *pValue){ rc = sqlite3VdbeChangeEncoding(&p->aVar[i-1], ENC(p->db)); } sqlite3_mutex_leave(p->db->mutex); + rc = sqlite3ApiExit(p->db, rc); } - rc = sqlite3ApiExit(p->db, rc); return rc; } int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){ @@ -1182,18 +1191,21 @@ int sqlite3_bind_parameter_count(sqlite3_stmt *pStmt){ */ static void createVarMap(Vdbe *p){ if( !p->okVar ){ + int j; + Op *pOp; sqlite3_mutex_enter(p->db->mutex); - if( !p->okVar ){ - int j; - Op *pOp; - for(j=0, pOp=p->aOp; jnOp; j++, pOp++){ - if( pOp->opcode==OP_Variable ){ - assert( pOp->p1>0 && pOp->p1<=p->nVar ); - p->azVar[pOp->p1-1] = pOp->p4.z; - } + /* The race condition here is harmless. If two threads call this + ** routine on the same Vdbe at the same time, they both might end + ** up initializing the Vdbe.azVar[] array. That is a little extra + ** work but it results in the same answer. + */ + for(j=0, pOp=p->aOp; jnOp; j++, pOp++){ + if( pOp->opcode==OP_Variable ){ + assert( pOp->p1>0 && pOp->p1<=p->nVar ); + p->azVar[pOp->p1-1] = pOp->p4.z; } - p->okVar = 1; } + p->okVar = 1; sqlite3_mutex_leave(p->db->mutex); } } @@ -1238,36 +1250,40 @@ int sqlite3_bind_parameter_index(sqlite3_stmt *pStmt, const char *zName){ /* ** Transfer all bindings from the first statement over to the second. -** If the two statements contain a different number of bindings, then -** an SQLITE_ERROR is returned. */ int sqlite3TransferBindings(sqlite3_stmt *pFromStmt, sqlite3_stmt *pToStmt){ Vdbe *pFrom = (Vdbe*)pFromStmt; Vdbe *pTo = (Vdbe*)pToStmt; - int i, rc = SQLITE_OK; - if( (pFrom->magic!=VDBE_MAGIC_RUN && pFrom->magic!=VDBE_MAGIC_HALT) - || (pTo->magic!=VDBE_MAGIC_RUN && pTo->magic!=VDBE_MAGIC_HALT) - || pTo->db!=pFrom->db ){ - return SQLITE_MISUSE; - } - if( pFrom->nVar!=pTo->nVar ){ - return SQLITE_ERROR; - } + int i; + assert( pTo->db==pFrom->db ); + assert( pTo->nVar==pFrom->nVar ); sqlite3_mutex_enter(pTo->db->mutex); - for(i=0; rc==SQLITE_OK && inVar; i++){ + for(i=0; inVar; i++){ sqlite3VdbeMemMove(&pTo->aVar[i], &pFrom->aVar[i]); } sqlite3_mutex_leave(pTo->db->mutex); - assert( rc==SQLITE_OK || rc==SQLITE_NOMEM ); - return rc; + return SQLITE_OK; } #ifndef SQLITE_OMIT_DEPRECATED /* ** Deprecated external interface. Internal/core SQLite code ** should call sqlite3TransferBindings. +** +** Is is misuse to call this routine with statements from different +** database connections. But as this is a deprecated interface, we +** will not bother to check for that condition. +** +** If the two statements contain a different number of bindings, then +** an SQLITE_ERROR is returned. Nothing else can go wrong, so otherwise +** SQLITE_OK is returned. */ int sqlite3_transfer_bindings(sqlite3_stmt *pFromStmt, sqlite3_stmt *pToStmt){ + Vdbe *pFrom = (Vdbe*)pFromStmt; + Vdbe *pTo = (Vdbe*)pToStmt; + if( pFrom->nVar!=pTo->nVar ){ + return SQLITE_ERROR; + } return sqlite3TransferBindings(pFromStmt, pToStmt); } #endif diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 9bc5bef..8f67eef 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -14,7 +14,7 @@ ** to version 2.8.7, all this code was combined into the vdbe.c source file. ** But that file was getting too big so this subroutines were split out. ** -** $Id: vdbeaux.c,v 1.435 2009/02/03 16:51:25 danielk1977 Exp $ +** $Id: vdbeaux.c,v 1.451 2009/04/10 15:42:36 shane Exp $ */ #include "sqliteInt.h" #include "vdbeInt.h" @@ -52,17 +52,22 @@ Vdbe *sqlite3VdbeCreate(sqlite3 *db){ /* ** Remember the SQL string for a prepared statement. */ -void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n){ +void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n, int isPrepareV2){ if( p==0 ) return; +#ifdef SQLITE_OMIT_TRACE + if( !isPrepareV2 ) return; +#endif assert( p->zSql==0 ); p->zSql = sqlite3DbStrNDup(p->db, z, n); + p->isPrepareV2 = isPrepareV2 ? 1 : 0; } /* ** Return the SQL associated with a prepared statement */ const char *sqlite3_sql(sqlite3_stmt *pStmt){ - return ((Vdbe *)pStmt)->zSql; + Vdbe *p = (Vdbe *)pStmt; + return (p->isPrepareV2 ? p->zSql : 0); } /* @@ -71,7 +76,6 @@ const char *sqlite3_sql(sqlite3_stmt *pStmt){ void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){ Vdbe tmp, *pTmp; char *zTmp; - int nTmp; tmp = *pA; *pA = *pB; *pB = tmp; @@ -84,9 +88,7 @@ void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){ zTmp = pA->zSql; pA->zSql = pB->zSql; pB->zSql = zTmp; - nTmp = pA->nSql; - pA->nSql = pB->nSql; - pB->nSql = nTmp; + pB->isPrepareV2 = pA->isPrepareV2; } #ifdef SQLITE_DEBUG @@ -112,7 +114,7 @@ static int growOpArray(Vdbe *p){ int nNew = (p->nOpAlloc ? p->nOpAlloc*2 : (int)(1024/sizeof(Op))); pNew = sqlite3DbRealloc(p->db, p->aOp, nNew*sizeof(Op)); if( pNew ){ - p->nOpAlloc = nNew; + p->nOpAlloc = sqlite3DbMallocSize(p->db, pNew)/sizeof(Op); p->aOp = pNew; } return (pNew ? SQLITE_OK : SQLITE_NOMEM); @@ -901,8 +903,8 @@ int sqlite3VdbeList( } if( sqlite3VdbeMemGrow(pMem, 32, 0) ){ /* P4 */ - p->db->mallocFailed = 1; - return SQLITE_NOMEM; + assert( p->db->mallocFailed ); + return SQLITE_ERROR; } pMem->flags = MEM_Dyn|MEM_Str|MEM_Term; z = displayP4(pOp, pMem->z, 32); @@ -918,8 +920,8 @@ int sqlite3VdbeList( if( p->explain==1 ){ if( sqlite3VdbeMemGrow(pMem, 4, 0) ){ - p->db->mallocFailed = 1; - return SQLITE_NOMEM; + assert( p->db->mallocFailed ); + return SQLITE_ERROR; } pMem->flags = MEM_Dyn|MEM_Str|MEM_Term; pMem->n = 2; @@ -998,6 +1000,40 @@ void sqlite3VdbeIOTraceSql(Vdbe *p){ } #endif /* !SQLITE_OMIT_TRACE && SQLITE_ENABLE_IOTRACE */ +/* +** Allocate space from a fixed size buffer. Make *pp point to the +** allocated space. (Note: pp is a char* rather than a void** to +** work around the pointer aliasing rules of C.) *pp should initially +** be zero. If *pp is not zero, that means that the space has already +** been allocated and this routine is a noop. +** +** nByte is the number of bytes of space needed. +** +** *ppFrom point to available space and pEnd points to the end of the +** available space. +** +** *pnByte is a counter of the number of bytes of space that have failed +** to allocate. If there is insufficient space in *ppFrom to satisfy the +** request, then increment *pnByte by the amount of the request. +*/ +static void allocSpace( + char *pp, /* IN/OUT: Set *pp to point to allocated buffer */ + int nByte, /* Number of bytes to allocate */ + u8 **ppFrom, /* IN/OUT: Allocate from *ppFrom */ + u8 *pEnd, /* Pointer to 1 byte past the end of *ppFrom buffer */ + int *pnByte /* If allocation cannot be made, increment *pnByte */ +){ + assert( EIGHT_BYTE_ALIGNMENT(*ppFrom) ); + if( (*(void**)pp)==0 ){ + nByte = ROUND8(nByte); + if( (pEnd - *ppFrom)>=nByte ){ + *(void**)pp = (void *)*ppFrom; + *ppFrom += nByte; + }else{ + *pnByte += nByte; + } + } +} /* ** Prepare a virtual machine for execution. This involves things such @@ -1007,6 +1043,14 @@ void sqlite3VdbeIOTraceSql(Vdbe *p){ ** ** This is the only way to move a VDBE from VDBE_MAGIC_INIT to ** VDBE_MAGIC_RUN. +** +** This function may be called more than once on a single virtual machine. +** The first call is made while compiling the SQL statement. Subsequent +** calls are made as part of the process of resetting a statement to be +** re-executed (from a call to sqlite3_reset()). The nVar, nMem, nCursor +** and isExplain parameters are only passed correct values the first time +** the function is called. On subsequent calls, from sqlite3_reset(), nVar +** is passed -1 and nMem, nCursor and isExplain are all passed zero. */ void sqlite3VdbeMakeReady( Vdbe *p, /* The VDBE */ @@ -1039,37 +1083,52 @@ void sqlite3VdbeMakeReady( */ nMem += nCursor; - /* - ** Allocation space for registers. + /* Allocate space for memory registers, SQL variables, VDBE cursors and + ** an array to marshal SQL function arguments in. This is only done the + ** first time this function is called for a given VDBE, not when it is + ** being called from sqlite3_reset() to reset the virtual machine. */ - if( p->aMem==0 ){ + if( nVar>=0 && !db->mallocFailed ){ + u8 *zCsr = (u8 *)&p->aOp[p->nOp]; + u8 *zEnd = (u8 *)&p->aOp[p->nOpAlloc]; + int nByte; int nArg; /* Maximum number of args passed to a user function. */ resolveP2Values(p, &nArg); - assert( nVar>=0 ); if( isExplain && nMem<10 ){ nMem = 10; } - p->aMem = sqlite3DbMallocZero(db, - nMem*sizeof(Mem) /* aMem */ - + nVar*sizeof(Mem) /* aVar */ - + nArg*sizeof(Mem*) /* apArg */ - + nVar*sizeof(char*) /* azVar */ - + nCursor*sizeof(VdbeCursor*)+1 /* apCsr */ - ); - if( !db->mallocFailed ){ - p->aMem--; /* aMem[] goes from 1..nMem */ - p->nMem = nMem; /* not from 0..nMem-1 */ - p->aVar = &p->aMem[nMem+1]; + zCsr += (zCsr - (u8*)0)&7; + assert( EIGHT_BYTE_ALIGNMENT(zCsr) ); + if( zEndaMem, nMem*sizeof(Mem), &zCsr, zEnd, &nByte); + allocSpace((char*)&p->aVar, nVar*sizeof(Mem), &zCsr, zEnd, &nByte); + allocSpace((char*)&p->apArg, nArg*sizeof(Mem*), &zCsr, zEnd, &nByte); + allocSpace((char*)&p->azVar, nVar*sizeof(char*), &zCsr, zEnd, &nByte); + allocSpace((char*)&p->apCsr, + nCursor*sizeof(VdbeCursor*), &zCsr, zEnd, &nByte + ); + if( nByte ){ + p->pFree = sqlite3DbMallocRaw(db, nByte); + } + zCsr = p->pFree; + zEnd = &zCsr[nByte]; + }while( nByte && !db->mallocFailed ); + + p->nCursor = nCursor; + if( p->aVar ){ p->nVar = nVar; - p->okVar = 0; - p->apArg = (Mem**)&p->aVar[nVar]; - p->azVar = (char**)&p->apArg[nArg]; - p->apCsr = (VdbeCursor**)&p->azVar[nVar]; - p->nCursor = nCursor; for(n=0; naVar[n].flags = MEM_Null; p->aVar[n].db = db; } + } + if( p->aMem ){ + p->aMem--; /* aMem[] goes from 1..nMem */ + p->nMem = nMem; /* not from 0..nMem-1 */ for(n=1; n<=nMem; n++){ p->aMem[n].flags = MEM_Null; p->aMem[n].db = db; @@ -1084,14 +1143,13 @@ void sqlite3VdbeMakeReady( p->pc = -1; p->rc = SQLITE_OK; - p->uniqueCnt = 0; p->errorAction = OE_Abort; p->explain |= isExplain; p->magic = VDBE_MAGIC_RUN; p->nChange = 0; p->cacheCtr = 1; p->minWriteFileFormat = 255; - p->openedStatement = 0; + p->iStatement = 0; #ifdef VDBE_PROFILE { int i; @@ -1405,7 +1463,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ ** sqlite3BtreeCommitPhaseOne(), then there is a chance that the ** master journal file will be orphaned. But we cannot delete it, ** in case the master journal file name was written into the journal - ** file before the failure occured. + ** file before the failure occurred. */ for(i=0; rc==SQLITE_OK && inDb; i++){ Btree *pBt = db->aDb[i].pBt; @@ -1510,6 +1568,75 @@ static void invalidateCursorsOnModifiedBtrees(sqlite3 *db){ } } +/* +** If the Vdbe passed as the first argument opened a statement-transaction, +** close it now. Argument eOp must be either SAVEPOINT_ROLLBACK or +** SAVEPOINT_RELEASE. If it is SAVEPOINT_ROLLBACK, then the statement +** transaction is rolled back. If eOp is SAVEPOINT_RELEASE, then the +** statement transaction is commtted. +** +** If an IO error occurs, an SQLITE_IOERR_XXX error code is returned. +** Otherwise SQLITE_OK. +*/ +int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){ + sqlite3 *const db = p->db; + int rc = SQLITE_OK; + if( p->iStatement && db->nStatement ){ + int i; + const int iSavepoint = p->iStatement-1; + + assert( eOp==SAVEPOINT_ROLLBACK || eOp==SAVEPOINT_RELEASE); + assert( db->nStatement>0 ); + assert( p->iStatement==(db->nStatement+db->nSavepoint) ); + + for(i=0; inDb; i++){ + int rc2 = SQLITE_OK; + Btree *pBt = db->aDb[i].pBt; + if( pBt ){ + if( eOp==SAVEPOINT_ROLLBACK ){ + rc2 = sqlite3BtreeSavepoint(pBt, SAVEPOINT_ROLLBACK, iSavepoint); + } + if( rc2==SQLITE_OK ){ + rc2 = sqlite3BtreeSavepoint(pBt, SAVEPOINT_RELEASE, iSavepoint); + } + if( rc==SQLITE_OK ){ + rc = rc2; + } + } + } + db->nStatement--; + p->iStatement = 0; + } + return rc; +} + +/* +** If SQLite is compiled to support shared-cache mode and to be threadsafe, +** this routine obtains the mutex associated with each BtShared structure +** that may be accessed by the VM passed as an argument. In doing so it +** sets the BtShared.db member of each of the BtShared structures, ensuring +** that the correct busy-handler callback is invoked if required. +** +** If SQLite is not threadsafe but does support shared-cache mode, then +** sqlite3BtreeEnterAll() is invoked to set the BtShared.db variables +** of all of BtShared structures accessible via the database handle +** associated with the VM. Of course only a subset of these structures +** will be accessed by the VM, and we could use Vdbe.btreeMask to figure +** that subset out, but there is no advantage to doing so. +** +** If SQLite is not threadsafe and does not support shared-cache mode, this +** function is a no-op. +*/ +#ifndef SQLITE_OMIT_SHARED_CACHE +void sqlite3VdbeMutexArrayEnter(Vdbe *p){ +#if SQLITE_THREADSAFE + sqlite3BtreeMutexArrayEnter(&p->aMutex); +#else + sqlite3BtreeEnterAll(p->db); +#endif +} +#endif + /* ** This routine is called the when a VDBE tries to halt. If the VDBE ** has made changes and is in autocommit mode, then commit those @@ -1524,10 +1651,8 @@ static void invalidateCursorsOnModifiedBtrees(sqlite3 *db){ ** means the close did not happen and needs to be repeated. */ int sqlite3VdbeHalt(Vdbe *p){ + int rc; /* Used to store transient return codes */ sqlite3 *db = p->db; - int i; - int (*xFunc)(Btree *pBt) = 0; /* Function to call on each btree backend */ - int isSpecialError; /* Set to true if SQLITE_NOMEM or IOERR */ /* This function contains the logic that determines if a statement or ** transaction will be committed or rolled back as a result of the @@ -1557,9 +1682,11 @@ int sqlite3VdbeHalt(Vdbe *p){ /* No commit or rollback needed if the program never started */ if( p->pc>=0 ){ int mrc; /* Primary error code from p->rc */ + int eStatementOp = 0; + int isSpecialError; /* Set to true if a 'special' error */ /* Lock all btrees used by the statement */ - sqlite3BtreeMutexArrayEnter(&p->aMutex); + sqlite3VdbeMutexArrayEnter(p); /* Check for one of the special errors */ mrc = p->rc & 0xff; @@ -1571,11 +1698,11 @@ int sqlite3VdbeHalt(Vdbe *p){ */ if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){ if( p->rc==SQLITE_IOERR_BLOCKED && p->usesStmtJournal ){ - xFunc = sqlite3BtreeRollbackStmt; + eStatementOp = SAVEPOINT_ROLLBACK; p->rc = SQLITE_BUSY; }else if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL) && p->usesStmtJournal ){ - xFunc = sqlite3BtreeRollbackStmt; + eStatementOp = SAVEPOINT_ROLLBACK; }else{ /* We are forced to roll back the active transaction. Before doing ** so, abort any other statements this handle currently has active. @@ -1588,8 +1715,8 @@ int sqlite3VdbeHalt(Vdbe *p){ } } - /* If the auto-commit flag is set and this is the only active vdbe, then - ** we do either a commit or rollback of the current transaction. + /* If the auto-commit flag is set and this is the only active writer + ** VM, then we do either a commit or rollback of the current transaction. ** ** Note: This block also runs if one of the special errors handled ** above has occurred. @@ -1604,7 +1731,7 @@ int sqlite3VdbeHalt(Vdbe *p){ ** successful or hit an 'OR FAIL' constraint. This means a commit ** is required. */ - int rc = vdbeCommit(db, p); + rc = vdbeCommit(db, p); if( rc==SQLITE_BUSY ){ sqlite3BtreeMutexArrayLeave(&p->aMutex); return SQLITE_BUSY; @@ -1617,13 +1744,12 @@ int sqlite3VdbeHalt(Vdbe *p){ }else{ sqlite3RollbackAll(db); } - }else if( !xFunc ){ + db->nStatement = 0; + }else if( eStatementOp==0 ){ if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){ - if( p->openedStatement ){ - xFunc = sqlite3BtreeCommitStmt; - } + eStatementOp = SAVEPOINT_RELEASE; }else if( p->errorAction==OE_Abort ){ - xFunc = sqlite3BtreeRollbackStmt; + eStatementOp = SAVEPOINT_ROLLBACK; }else{ invalidateCursorsOnModifiedBtrees(db); sqlite3RollbackAll(db); @@ -1632,33 +1758,26 @@ int sqlite3VdbeHalt(Vdbe *p){ } } - /* If xFunc is not NULL, then it is one of sqlite3BtreeRollbackStmt or - ** sqlite3BtreeCommitStmt. Call it once on each backend. If an error occurs - ** and the return code is still SQLITE_OK, set the return code to the new - ** error value. + /* If eStatementOp is non-zero, then a statement transaction needs to + ** be committed or rolled back. Call sqlite3VdbeCloseStatement() to + ** do so. If this operation returns an error, and the current statement + ** error code is SQLITE_OK or SQLITE_CONSTRAINT, then set the error + ** code to the new value. */ - assert(!xFunc || - xFunc==sqlite3BtreeCommitStmt || - xFunc==sqlite3BtreeRollbackStmt - ); - for(i=0; xFunc && inDb; i++){ - int rc; - Btree *pBt = db->aDb[i].pBt; - if( pBt ){ - rc = xFunc(pBt); - if( rc && (p->rc==SQLITE_OK || p->rc==SQLITE_CONSTRAINT) ){ - p->rc = rc; - sqlite3DbFree(db, p->zErrMsg); - p->zErrMsg = 0; - } + if( eStatementOp ){ + rc = sqlite3VdbeCloseStatement(p, eStatementOp); + if( rc && (p->rc==SQLITE_OK || p->rc==SQLITE_CONSTRAINT) ){ + p->rc = rc; + sqlite3DbFree(db, p->zErrMsg); + p->zErrMsg = 0; } } - /* If this was an INSERT, UPDATE or DELETE and the statement was committed, - ** set the change counter. + /* If this was an INSERT, UPDATE or DELETE and no statement transaction + ** has been rolled back, update the database connection change-counter. */ if( p->changeCntOn && p->pc>=0 ){ - if( !xFunc || xFunc==sqlite3BtreeCommitStmt ){ + if( eStatementOp!=SAVEPOINT_ROLLBACK ){ sqlite3VdbeSetChanges(db, p->nChange); }else{ sqlite3VdbeSetChanges(db, 0); @@ -1690,6 +1809,15 @@ int sqlite3VdbeHalt(Vdbe *p){ p->rc = SQLITE_NOMEM; } + /* If the auto-commit flag is set to true, then any locks that were held + ** by connection db have now been released. Call sqlite3ConnectionUnlocked() + ** to invoke any required unlock-notify callbacks. + */ + if( db->autoCommit ){ + sqlite3ConnectionUnlocked(db); + } + + assert( db->activeVdbeCnt>0 || db->autoCommit==0 || db->nStatement==0 ); return SQLITE_OK; } @@ -1847,17 +1975,15 @@ void sqlite3VdbeDelete(Vdbe *p){ sqlite3DbFree(db, pOp->zComment); #endif } - sqlite3DbFree(db, p->aOp); } releaseMemArray(p->aVar, p->nVar); sqlite3DbFree(db, p->aLabel); - if( p->aMem ){ - sqlite3DbFree(db, &p->aMem[1]); - } releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); sqlite3DbFree(db, p->aColName); sqlite3DbFree(db, p->zSql); p->magic = VDBE_MAGIC_DEAD; + sqlite3DbFree(db, p->aOp); + sqlite3DbFree(db, p->pFree); sqlite3DbFree(db, p); } @@ -2221,30 +2347,40 @@ UnpackedRecord *sqlite3VdbeRecordUnpack( KeyInfo *pKeyInfo, /* Information about the record format */ int nKey, /* Size of the binary record */ const void *pKey, /* The binary record */ - UnpackedRecord *pSpace,/* Space available to hold resulting object */ + char *pSpace, /* Unaligned space available to hold the object */ int szSpace /* Size of pSpace[] in bytes */ ){ const unsigned char *aKey = (const unsigned char *)pKey; - UnpackedRecord *p; - int nByte, d; + UnpackedRecord *p; /* The unpacked record that we will return */ + int nByte; /* Memory space needed to hold p, in bytes */ + int d; u32 idx; - u16 u; /* Unsigned loop counter */ + u16 u; /* Unsigned loop counter */ u32 szHdr; Mem *pMem; + int nOff; /* Increase pSpace by this much to 8-byte align it */ - assert( sizeof(Mem)>sizeof(*p) ); - nByte = sizeof(Mem)*(pKeyInfo->nField+2); + /* + ** We want to shift the pointer pSpace up such that it is 8-byte aligned. + ** Thus, we need to calculate a value, nOff, between 0 and 7, to shift + ** it by. If pSpace is already 8-byte aligned, nOff should be zero. + */ + nOff = (8 - (SQLITE_PTR_TO_INT(pSpace) & 7)) & 7; + pSpace += nOff; + szSpace -= nOff; + nByte = ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nField+1); if( nByte>szSpace ){ p = sqlite3DbMallocRaw(pKeyInfo->db, nByte); if( p==0 ) return 0; p->flags = UNPACKED_NEED_FREE | UNPACKED_NEED_DESTROY; }else{ - p = pSpace; + p = (UnpackedRecord*)pSpace; p->flags = UNPACKED_NEED_DESTROY; } p->pKeyInfo = pKeyInfo; p->nField = pKeyInfo->nField + 1; - p->aMem = pMem = &((Mem*)p)[1]; + p->aMem = pMem = (Mem*)&((char*)p)[ROUND8(sizeof(UnpackedRecord))]; + assert( EIGHT_BYTE_ALIGNMENT(pMem) ); idx = getVarint32(aKey, szHdr); d = szHdr; u = 0; diff --git a/src/vdbeblob.c b/src/vdbeblob.c index 9afeb11..de051e1 100644 --- a/src/vdbeblob.c +++ b/src/vdbeblob.c @@ -12,7 +12,7 @@ ** ** This file contains code used to implement incremental BLOB I/O. ** -** $Id: vdbeblob.c,v 1.26 2008/10/02 14:49:02 danielk1977 Exp $ +** $Id: vdbeblob.c,v 1.31 2009/03/24 15:08:10 drh Exp $ */ #include "sqliteInt.h" @@ -70,17 +70,15 @@ int sqlite3_blob_open( /* One of the following two instructions is replaced by an ** OP_Noop before exection. */ - {OP_SetNumColumns, 0, 0, 0}, /* 2: Num cols for cursor */ - {OP_OpenRead, 0, 0, 0}, /* 3: Open cursor 0 for reading */ - {OP_SetNumColumns, 0, 0, 0}, /* 4: Num cols for cursor */ - {OP_OpenWrite, 0, 0, 0}, /* 5: Open cursor 0 for read/write */ + {OP_OpenRead, 0, 0, 0}, /* 2: Open cursor 0 for reading */ + {OP_OpenWrite, 0, 0, 0}, /* 3: Open cursor 0 for read/write */ - {OP_Variable, 1, 1, 0}, /* 6: Push the rowid to the stack */ - {OP_NotExists, 0, 10, 1}, /* 7: Seek the cursor */ - {OP_Column, 0, 0, 1}, /* 8 */ - {OP_ResultRow, 1, 0, 0}, /* 9 */ - {OP_Close, 0, 0, 0}, /* 10 */ - {OP_Halt, 0, 0, 0}, /* 11 */ + {OP_Variable, 1, 1, 1}, /* 4: Push the rowid to the stack */ + {OP_NotExists, 0, 8, 1}, /* 5: Seek the cursor */ + {OP_Column, 0, 0, 1}, /* 6 */ + {OP_ResultRow, 1, 0, 0}, /* 7 */ + {OP_Close, 0, 0, 0}, /* 8 */ + {OP_Halt, 0, 0, 0}, /* 9 */ }; Vdbe *v = 0; @@ -178,19 +176,19 @@ int sqlite3_blob_open( /* Remove either the OP_OpenWrite or OpenRead. Set the P2 ** parameter of the other to pTab->tnum. */ - sqlite3VdbeChangeToNoop(v, (flags ? 3 : 5), 1); - sqlite3VdbeChangeP2(v, (flags ? 5 : 3), pTab->tnum); - sqlite3VdbeChangeP3(v, (flags ? 5 : 3), iDb); + sqlite3VdbeChangeToNoop(v, (flags ? 2 : 3), 1); + sqlite3VdbeChangeP2(v, (flags ? 3 : 2), pTab->tnum); + sqlite3VdbeChangeP3(v, (flags ? 3 : 2), iDb); - /* Configure the OP_SetNumColumns. Configure the cursor to + /* Configure the number of columns. Configure the cursor to ** think that the table has one more column than it really ** does. An OP_Column to retrieve this imaginary column will ** always return an SQL NULL. This is useful because it means ** we can invoke OP_Column to fill in the vdbe cursors type ** and offset cache without causing any IO. */ - sqlite3VdbeChangeP2(v, flags ? 4 : 2, pTab->nCol+1); - sqlite3VdbeChangeP2(v, 8, pTab->nCol); + sqlite3VdbeChangeP4(v, flags ? 3 : 2, SQLITE_INT_TO_PTR(pTab->nCol+1), P4_INT32); + sqlite3VdbeChangeP2(v, 6, pTab->nCol); if( !db->mallocFailed ){ sqlite3VdbeMakeReady(v, 1, 1, 1, 0); } @@ -250,8 +248,8 @@ int sqlite3_blob_open( blob_open_out: zErr[sizeof(zErr)-1] = '\0'; - if( rc!=SQLITE_OK || db->mallocFailed ){ - sqlite3_finalize((sqlite3_stmt *)v); + if( v && (rc!=SQLITE_OK || db->mallocFailed) ){ + sqlite3VdbeFinalize(v); } sqlite3Error(db, rc, (rc==SQLITE_OK?0:zErr)); rc = sqlite3ApiExit(db, rc); @@ -266,9 +264,13 @@ blob_open_out: int sqlite3_blob_close(sqlite3_blob *pBlob){ Incrblob *p = (Incrblob *)pBlob; int rc; + sqlite3 *db; + db = p->db; + sqlite3_mutex_enter(db->mutex); rc = sqlite3_finalize(p->pStmt); - sqlite3DbFree(p->db, p); + sqlite3DbFree(db, p); + sqlite3_mutex_leave(db->mutex); return rc; } diff --git a/src/vdbemem.c b/src/vdbemem.c index cc1ffc2..e5a8755 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -15,7 +15,7 @@ ** only within the VDBE. Interface routines refer to a Mem using the ** name sqlite_value ** -** $Id: vdbemem.c,v 1.137 2009/02/04 03:59:25 shane Exp $ +** $Id: vdbemem.c,v 1.140 2009/04/05 12:22:09 drh Exp $ */ #include "sqliteInt.h" #include "vdbeInt.h" @@ -209,6 +209,7 @@ int sqlite3VdbeMemStringify(Mem *pMem, int enc){ assert( !(fg&(MEM_Str|MEM_Blob)) ); assert( fg&(MEM_Int|MEM_Real) ); assert( (pMem->flags&MEM_RowSet)==0 ); + assert( EIGHT_BYTE_ALIGNMENT(pMem) ); if( sqlite3VdbeMemGrow(pMem, nByte, 0) ){ @@ -321,6 +322,10 @@ static i64 doubleToInt64(double r){ if( r<(double)minInt ){ return minInt; }else if( r>(double)maxInt ){ + /* minInt is correct here - not maxInt. It turns out that assigning + ** a very large positive number to an integer results in a very large + ** negative integer. This makes no sense, but it is what x86 hardware + ** does so for compatibility we will do the same in software. */ return minInt; }else{ return (i64)r; @@ -333,13 +338,15 @@ static i64 doubleToInt64(double r){ ** If pMem is an integer, then the value is exact. If pMem is ** a floating-point then the value returned is the integer part. ** If pMem is a string or blob, then we make an attempt to convert -** it into a integer and return that. If pMem is NULL, return 0. +** it into a integer and return that. If pMem represents an +** an SQL-NULL value, return 0. ** -** If pMem is a string, its encoding might be changed. +** If pMem represents a string value, its encoding might be changed. */ i64 sqlite3VdbeIntValue(Mem *pMem){ int flags; assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); + assert( EIGHT_BYTE_ALIGNMENT(pMem) ); flags = pMem->flags; if( flags & MEM_Int ){ return pMem->u.i; @@ -368,6 +375,7 @@ i64 sqlite3VdbeIntValue(Mem *pMem){ */ double sqlite3VdbeRealValue(Mem *pMem){ assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); + assert( EIGHT_BYTE_ALIGNMENT(pMem) ); if( pMem->flags & MEM_Real ){ return pMem->r; }else if( pMem->flags & MEM_Int ){ @@ -398,6 +406,7 @@ void sqlite3VdbeIntegerAffinity(Mem *pMem){ assert( pMem->flags & MEM_Real ); assert( (pMem->flags & MEM_RowSet)==0 ); assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); + assert( EIGHT_BYTE_ALIGNMENT(pMem) ); pMem->u.i = doubleToInt64(pMem->r); if( pMem->r==(double)pMem->u.i ){ @@ -411,6 +420,8 @@ void sqlite3VdbeIntegerAffinity(Mem *pMem){ int sqlite3VdbeMemIntegerify(Mem *pMem){ assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); assert( (pMem->flags & MEM_RowSet)==0 ); + assert( EIGHT_BYTE_ALIGNMENT(pMem) ); + pMem->u.i = sqlite3VdbeIntValue(pMem); MemSetTypeFlag(pMem, MEM_Int); return SQLITE_OK; @@ -422,6 +433,8 @@ int sqlite3VdbeMemIntegerify(Mem *pMem){ */ int sqlite3VdbeMemRealify(Mem *pMem){ assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); + assert( EIGHT_BYTE_ALIGNMENT(pMem) ); + pMem->r = sqlite3VdbeRealValue(pMem); MemSetTypeFlag(pMem, MEM_Real); return SQLITE_OK; diff --git a/src/vtab.c b/src/vtab.c index 83c4a33..e2103bd 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -11,11 +11,16 @@ ************************************************************************* ** This file contains code used to help implement virtual tables. ** -** $Id: vtab.c,v 1.81 2008/12/10 19:26:24 drh Exp $ +** $Id: vtab.c,v 1.85 2009/04/11 16:27:20 drh Exp $ */ #ifndef SQLITE_OMIT_VIRTUALTABLE #include "sqliteInt.h" +/* +** The actual function that does the work of creating a new module. +** This function implements the sqlite3_create_module() and +** sqlite3_create_module_v2() interfaces. +*/ static int createModule( sqlite3 *db, /* Database in which module is registered */ const char *zName, /* Name assigned to this module */ @@ -97,6 +102,7 @@ void sqlite3VtabLock(sqlite3_vtab *pVtab){ ** disconnect the virtual table. */ void sqlite3VtabUnlock(sqlite3 *db, sqlite3_vtab *pVtab){ + assert( pVtab->nRef>0 ); pVtab->nRef--; assert(db); assert( sqlite3SafetyCheckOk(db) ); @@ -118,7 +124,8 @@ void sqlite3VtabUnlock(sqlite3 *db, sqlite3_vtab *pVtab){ */ void sqlite3VtabClear(Table *p){ sqlite3_vtab *pVtab = p->pVtab; - sqlite3 *db = p->db; + Schema *pSchema = p->pSchema; + sqlite3 *db = pSchema ? pSchema->db : 0; if( pVtab ){ assert( p->pMod && p->pMod->pModule ); sqlite3VtabUnlock(db, pVtab); @@ -571,7 +578,9 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ } sParse.declareVtab = 0; - sqlite3_finalize((sqlite3_stmt*)sParse.pVdbe); + if( sParse.pVdbe ){ + sqlite3VdbeFinalize(sParse.pVdbe); + } sqlite3DeleteTable(sParse.pNewTable); sParse.pNewTable = 0; @@ -711,7 +720,7 @@ int sqlite3VtabBegin(sqlite3 *db, sqlite3_vtab *pVtab){ /* Special case: If db->aVTrans is NULL and db->nVTrans is greater ** than zero, then this function is being called from within a ** virtual module xSync() callback. It is illegal to write to - ** virtual module tables in this case, so return SQLITE_LOCKED. + ** virtual module tables in this case, so return SQLITE_MISUSE. */ if( sqlite3VtabInSync(db) ){ return SQLITE_LOCKED; diff --git a/src/walker.c b/src/walker.c index 9a3a9c2..19fb4e0 100644 --- a/src/walker.c +++ b/src/walker.c @@ -12,7 +12,7 @@ ** This file contains routines used for walking the parser tree for ** an SQL statement. ** -** $Id: walker.c,v 1.1 2008/08/20 16:35:10 drh Exp $ +** $Id: walker.c,v 1.4 2009/04/08 13:51:52 drh Exp $ */ #include "sqliteInt.h" #include @@ -41,13 +41,18 @@ int sqlite3WalkExpr(Walker *pWalker, Expr *pExpr){ int rc; if( pExpr==0 ) return WRC_Continue; + testcase( ExprHasProperty(pExpr, EP_TokenOnly) ); + testcase( ExprHasProperty(pExpr, EP_SpanToken) ); + testcase( ExprHasProperty(pExpr, EP_Reduced) ); rc = pWalker->xExprCallback(pWalker, pExpr); - if( rc==WRC_Continue ){ + if( rc==WRC_Continue + && !ExprHasAnyProperty(pExpr,EP_TokenOnly|EP_SpanToken) ){ if( sqlite3WalkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort; if( sqlite3WalkExpr(pWalker, pExpr->pRight) ) return WRC_Abort; - if( sqlite3WalkExprList(pWalker, pExpr->pList) ) return WRC_Abort; - if( sqlite3WalkSelect(pWalker, pExpr->pSelect) ){ - return WRC_Abort; + if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + if( sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort; + }else{ + if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort; } } return rc & WRC_Abort; diff --git a/src/where.c b/src/where.c index 89e3f31..877ca05 100644 --- a/src/where.c +++ b/src/where.c @@ -16,7 +16,7 @@ ** so is applicable. Because this module is responsible for selecting ** indices, you might also think of this module as the "query optimizer". ** -** $Id: where.c,v 1.368 2009/02/04 03:59:25 shane Exp $ +** $Id: where.c,v 1.382 2009/04/07 13:48:12 drh Exp $ */ #include "sqliteInt.h" @@ -26,7 +26,7 @@ #if defined(SQLITE_TEST) || defined(SQLITE_DEBUG) int sqlite3WhereTrace = 0; #endif -#if 0 +#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) # define WHERETRACE(X) if(sqlite3WhereTrace) sqlite3DebugPrintf X #else # define WHERETRACE(X) @@ -431,8 +431,11 @@ static Bitmask exprTableUsage(WhereMaskSet *pMaskSet, Expr *p){ } mask = exprTableUsage(pMaskSet, p->pRight); mask |= exprTableUsage(pMaskSet, p->pLeft); - mask |= exprListTableUsage(pMaskSet, p->pList); - mask |= exprSelectTableUsage(pMaskSet, p->pSelect); + if( ExprHasProperty(p, EP_xIsSelect) ){ + mask |= exprSelectTableUsage(pMaskSet, p->x.pSelect); + }else{ + mask |= exprListTableUsage(pMaskSet, p->x.pList); + } return mask; } static Bitmask exprListTableUsage(WhereMaskSet *pMaskSet, ExprList *pList){ @@ -634,7 +637,7 @@ static int isLikeOrGlob( #ifdef SQLITE_EBCDIC if( *pnoCase ) return 0; #endif - pList = pExpr->pList; + pList = pExpr->x.pList; pRight = pList->a[0].pExpr; if( pRight->op!=TK_STRING ){ return 0; @@ -653,7 +656,7 @@ static int isLikeOrGlob( (pColl->type!=SQLITE_COLL_NOCASE || !*pnoCase) ){ return 0; } - sqlite3DequoteExpr(db, pRight); + sqlite3DequoteExpr(pRight); z = (char *)pRight->token.z; cnt = 0; if( z ){ @@ -689,7 +692,7 @@ static int isMatchOfColumn( sqlite3StrNICmp((const char*)pExpr->token.z,"match",5)!=0 ){ return 0; } - pList = pExpr->pList; + pList = pExpr->x.pList; if( pList->nExpr!=2 ){ return 0; } @@ -953,17 +956,18 @@ static void exprAnalyzeOrTerm( assert( pOrTerm->eOperator==WO_EQ ); assert( pOrTerm->leftCursor==iCursor ); assert( pOrTerm->u.leftColumn==iColumn ); - pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight); + pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0); pList = sqlite3ExprListAppend(pWC->pParse, pList, pDup, 0); pLeft = pOrTerm->pExpr->pLeft; } assert( pLeft!=0 ); - pDup = sqlite3ExprDup(db, pLeft); + pDup = sqlite3ExprDup(db, pLeft, 0); pNew = sqlite3Expr(db, TK_IN, pDup, 0, 0); if( pNew ){ int idxNew; transferJoinMarkings(pNew, pExpr); - pNew->pList = pList; + assert( !ExprHasProperty(pNew, EP_xIsSelect) ); + pNew->x.pList = pList; idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC); testcase( idxNew==0 ); exprAnalyze(pSrc, pWC, idxNew); @@ -1026,8 +1030,11 @@ static void exprAnalyze( op = pExpr->op; if( op==TK_IN ){ assert( pExpr->pRight==0 ); - pTerm->prereqRight = exprListTableUsage(pMaskSet, pExpr->pList) - | exprSelectTableUsage(pMaskSet, pExpr->pSelect); + if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + pTerm->prereqRight = exprSelectTableUsage(pMaskSet, pExpr->x.pSelect); + }else{ + pTerm->prereqRight = exprListTableUsage(pMaskSet, pExpr->x.pList); + } }else if( op==TK_ISNULL ){ pTerm->prereqRight = 0; }else{ @@ -1057,7 +1064,7 @@ static void exprAnalyze( Expr *pDup; if( pTerm->leftCursor>=0 ){ int idxNew; - pDup = sqlite3ExprDup(db, pExpr); + pDup = sqlite3ExprDup(db, pExpr, 0); if( db->mallocFailed ){ sqlite3ExprDelete(db, pDup); return; @@ -1100,7 +1107,7 @@ static void exprAnalyze( ** BETWEEN term is skipped. */ else if( pExpr->op==TK_BETWEEN && pWC->op==TK_AND ){ - ExprList *pList = pExpr->pList; + ExprList *pList = pExpr->x.pList; int i; static const u8 ops[] = {TK_GE, TK_LE}; assert( pList!=0 ); @@ -1108,8 +1115,8 @@ static void exprAnalyze( for(i=0; i<2; i++){ Expr *pNewExpr; int idxNew; - pNewExpr = sqlite3Expr(db, ops[i], sqlite3ExprDup(db, pExpr->pLeft), - sqlite3ExprDup(db, pList->a[i].pExpr), 0); + pNewExpr = sqlite3Expr(db, ops[i], sqlite3ExprDup(db, pExpr->pLeft, 0), + sqlite3ExprDup(db, pList->a[i].pExpr, 0), 0); idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC); testcase( idxNew==0 ); exprAnalyze(pSrc, pWC, idxNew); @@ -1148,18 +1155,18 @@ static void exprAnalyze( Expr *pNewExpr1, *pNewExpr2; int idxNew1, idxNew2; - pLeft = pExpr->pList->a[1].pExpr; - pRight = pExpr->pList->a[0].pExpr; + pLeft = pExpr->x.pList->a[1].pExpr; + pRight = pExpr->x.pList->a[0].pExpr; pStr1 = sqlite3PExpr(pParse, TK_STRING, 0, 0, 0); if( pStr1 ){ sqlite3TokenCopy(db, &pStr1->token, &pRight->token); pStr1->token.n = nPattern; pStr1->flags = EP_Dequoted; } - pStr2 = sqlite3ExprDup(db, pStr1); + pStr2 = sqlite3ExprDup(db, pStr1, 0); if( !db->mallocFailed ){ u8 c, *pC; - assert( pStr2->token.dyn ); + /* assert( pStr2->token.dyn ); */ pC = (u8*)&pStr2->token.z[nPattern-1]; c = *pC; if( noCase ){ @@ -1168,11 +1175,11 @@ static void exprAnalyze( } *pC = c + 1; } - pNewExpr1 = sqlite3PExpr(pParse, TK_GE, sqlite3ExprDup(db,pLeft), pStr1, 0); + pNewExpr1 = sqlite3PExpr(pParse, TK_GE, sqlite3ExprDup(db,pLeft,0),pStr1,0); idxNew1 = whereClauseInsert(pWC, pNewExpr1, TERM_VIRTUAL|TERM_DYNAMIC); testcase( idxNew1==0 ); exprAnalyze(pSrc, pWC, idxNew1); - pNewExpr2 = sqlite3PExpr(pParse, TK_LT, sqlite3ExprDup(db,pLeft), pStr2, 0); + pNewExpr2 = sqlite3PExpr(pParse, TK_LT, sqlite3ExprDup(db,pLeft,0),pStr2,0); idxNew2 = whereClauseInsert(pWC, pNewExpr2, TERM_VIRTUAL|TERM_DYNAMIC); testcase( idxNew2==0 ); exprAnalyze(pSrc, pWC, idxNew2); @@ -1198,13 +1205,13 @@ static void exprAnalyze( WhereTerm *pNewTerm; Bitmask prereqColumn, prereqExpr; - pRight = pExpr->pList->a[0].pExpr; - pLeft = pExpr->pList->a[1].pExpr; + pRight = pExpr->x.pList->a[0].pExpr; + pLeft = pExpr->x.pList->a[1].pExpr; prereqExpr = exprTableUsage(pMaskSet, pRight); prereqColumn = exprTableUsage(pMaskSet, pLeft); if( (prereqExpr & prereqColumn)==0 ){ Expr *pNewExpr; - pNewExpr = sqlite3Expr(db, TK_MATCH, 0, sqlite3ExprDup(db, pRight), 0); + pNewExpr = sqlite3Expr(db, TK_MATCH, 0, sqlite3ExprDup(db, pRight, 0), 0); idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC); testcase( idxNew==0 ); pNewTerm = &pWC->a[idxNew]; @@ -1708,15 +1715,15 @@ static double bestVirtualIndex( ** * Whether or not there must be separate lookups in the ** index and in the main table. ** -** If there was an INDEXED BY clause attached to the table in the SELECT -** statement, then this function only considers plans using the -** named index. If one cannot be found, then the returned cost is -** SQLITE_BIG_DBL. If a plan can be found that uses the named index, +** If there was an INDEXED BY clause (pSrc->pIndex) attached to the table in +** the SQL statement, then this function only considers plans using the +** named index. If no such plan is found, then the returned cost is +** SQLITE_BIG_DBL. If a plan is found that uses the named index, ** then the cost is calculated in the usual way. ** -** If a NOT INDEXED clause was attached to the table in the SELECT -** statement, then no indexes are considered. However, the selected -** plan may still take advantage of the tables built-in rowid +** If a NOT INDEXED clause (pSrc->notIndexed!=0) was attached to the table +** in the SELECT statement, then no indexes are considered. However, the +** selected plan may still take advantage of the tables built-in rowid ** index. */ static void bestIndex( @@ -1755,6 +1762,13 @@ static void bestIndex( if( pProbe==0 && findTerm(pWC, iCur, -1, 0, WO_EQ|WO_IN|WO_LT|WO_LE|WO_GT|WO_GE,0)==0 && (pOrderBy==0 || !sortableByRowid(iCur, pOrderBy, pWC->pMaskSet, &rev)) ){ + if( pParse->db->flags & SQLITE_ReverseOrder ){ + /* For application testing, randomly reverse the output order for + ** SELECT statements that omit the ORDER BY clause. This will help + ** to find cases where + */ + pCost->plan.wsFlags |= WHERE_REVERSE; + } return; } pCost->rCost = SQLITE_BIG_DBL; @@ -1776,10 +1790,12 @@ static void bestIndex( pCost->rCost = 0; pCost->nRow = 1; return; - }else if( (pExpr = pTerm->pExpr)->pList!=0 ){ + }else if( !ExprHasProperty((pExpr = pTerm->pExpr), EP_xIsSelect) + && pExpr->x.pList + ){ /* Rowid IN (LIST): cost is NlogN where N is the number of list ** elements. */ - pCost->rCost = pCost->nRow = pExpr->pList->nExpr; + pCost->rCost = pCost->nRow = pExpr->x.pList->nExpr; pCost->rCost *= estLog(pCost->rCost); }else{ /* Rowid IN (SELECT): cost is NlogN where N is the number of rows @@ -1828,7 +1844,15 @@ static void bestIndex( cost += cost*estLog(cost); WHERETRACE(("... sorting increases cost to %.9g\n", cost)); } + }else if( pParse->db->flags & SQLITE_ReverseOrder ){ + /* For application testing, randomly reverse the output order for + ** SELECT statements that omit the ORDER BY clause. This will help + ** to find cases where + */ + wsFlags |= WHERE_REVERSE; } + + /* Remember this case if it is the best so far */ if( costrCost ){ pCost->rCost = cost; pCost->nRow = nRow; @@ -1909,12 +1933,18 @@ static void bestIndex( pProbe = pSrc->pIndex; } for(; pProbe; pProbe=(pSrc->pIndex ? 0 : pProbe->pNext)){ - double inMultiplier = 1; + double inMultiplier = 1; /* Number of equality look-ups needed */ + int inMultIsEst = 0; /* True if inMultiplier is an estimate */ WHERETRACE(("... index %s:\n", pProbe->zName)); /* Count the number of columns in the index that are satisfied - ** by x=EXPR constraints or x IN (...) constraints. + ** by x=EXPR constraints or x IN (...) constraints. For a term + ** of the form x=EXPR we only have to do a single binary search. + ** But for x IN (...) we have to do a number of binary searched + ** equal to the number of entries on the RHS of the IN operator. + ** The inMultipler variable with try to estimate the number of + ** binary searches needed. */ wsFlags = 0; for(i=0; inColumn; i++){ @@ -1925,23 +1955,33 @@ static void bestIndex( if( pTerm->eOperator & WO_IN ){ Expr *pExpr = pTerm->pExpr; wsFlags |= WHERE_COLUMN_IN; - if( pExpr->pSelect!=0 ){ + if( ExprHasProperty(pExpr, EP_xIsSelect) ){ inMultiplier *= 25; - }else if( pExpr->pList ){ - inMultiplier *= pExpr->pList->nExpr + 1; + inMultIsEst = 1; + }else if( pExpr->x.pList ){ + inMultiplier *= pExpr->x.pList->nExpr + 1; } } } nRow = pProbe->aiRowEst[i] * inMultiplier; - cost = nRow * estLog(inMultiplier); + /* If inMultiplier is an estimate and that estimate results in an + ** nRow it that is more than half number of rows in the table, + ** then reduce inMultipler */ + if( inMultIsEst && nRow*2 > pProbe->aiRowEst[0] ){ + nRow = pProbe->aiRowEst[0]/2; + inMultiplier = nRow/pProbe->aiRowEst[i]; + } + cost = nRow + inMultiplier*estLog(pProbe->aiRowEst[0]); nEq = i; if( pProbe->onError!=OE_None && (wsFlags & WHERE_COLUMN_IN)==0 && nEq==pProbe->nColumn ){ wsFlags |= WHERE_UNIQUE; } - WHERETRACE(("...... nEq=%d inMult=%.9g cost=%.9g\n",nEq,inMultiplier,cost)); + WHERETRACE(("...... nEq=%d inMult=%.9g nRow=%.9g cost=%.9g\n", + nEq, inMultiplier, nRow, cost)); - /* Look for range constraints + /* Look for range constraints. Assume that each range constraint + ** makes the search space 1/3rd smaller. */ if( nEqnColumn ){ int j = pProbe->aiColumn[nEq]; @@ -1958,7 +1998,8 @@ static void bestIndex( cost /= 3; nRow /= 3; } - WHERETRACE(("...... range reduces cost to %.9g\n", cost)); + WHERETRACE(("...... range reduces nRow to %.9g and cost to %.9g\n", + nRow, cost)); } } @@ -1978,6 +2019,12 @@ static void bestIndex( cost += cost*estLog(cost); WHERETRACE(("...... orderby increases cost to %.9g\n", cost)); } + }else if( pParse->db->flags & SQLITE_ReverseOrder ){ + /* For application testing, randomly reverse the output order for + ** SELECT statements that omit the ORDER BY clause. This will help + ** to find cases where + */ + wsFlags |= WHERE_REVERSE; } /* Check to see if we can get away with using just the index without @@ -2738,11 +2785,13 @@ static Bitmask codeOneLoopStart( /* Case 5: There is no usable index. We must do a complete ** scan of the entire table. */ + static const u8 aStep[] = { OP_Next, OP_Prev }; + static const u8 aStart[] = { OP_Rewind, OP_Last }; + assert( bRev==0 || bRev==1 ); assert( omitTable==0 ); - assert( bRev==0 ); - pLevel->op = OP_Next; + pLevel->op = aStep[bRev]; pLevel->p1 = iCur; - pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, OP_Rewind, iCur, addrBrk); + pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk); pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; codeRowSetEarly = 0; } @@ -2836,7 +2885,7 @@ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){ assert( pInfo->needToFreeIdxStr==0 || db->mallocFailed ); if( pInfo->needToFreeIdxStr ){ sqlite3_free(pInfo->idxStr); - } + } sqlite3DbFree(db, pInfo); } } @@ -2943,6 +2992,7 @@ WhereInfo *sqlite3WhereBegin( int regRowSet /* Register hold RowSet if WHERE_FILL_ROWSET is set */ ){ int i; /* Loop counter */ + int nByteWInfo; /* Num. bytes allocated for WhereInfo struct */ WhereInfo *pWInfo; /* Will become the return value of this function */ Vdbe *v = pParse->pVdbe; /* The virtual database engine */ Bitmask notReady; /* Cursors that are not yet positioned */ @@ -2968,15 +3018,19 @@ WhereInfo *sqlite3WhereBegin( } /* Allocate and initialize the WhereInfo structure that will become the - ** return value. + ** return value. A single allocation is used to store the WhereInfo + ** struct, the contents of WhereInfo.a[], the WhereClause structure + ** and the WhereMaskSet structure. Since WhereClause contains an 8-byte + ** field (type Bitmask) it must be aligned on an 8-byte boundary on + ** some architectures. Hence the ROUND8() below. */ db = pParse->db; - pWInfo = sqlite3DbMallocZero(db, - sizeof(WhereInfo) - + (pTabList->nSrc-1)*sizeof(WhereLevel) - + sizeof(WhereClause) - + sizeof(WhereMaskSet) - ); + nByteWInfo = ROUND8(sizeof(WhereInfo)+(pTabList->nSrc-1)*sizeof(WhereLevel)); + pWInfo = sqlite3DbMallocZero(db, + nByteWInfo + + sizeof(WhereClause) + + sizeof(WhereMaskSet) + ); if( db->mallocFailed ){ goto whereBeginError; } @@ -2985,7 +3039,7 @@ WhereInfo *sqlite3WhereBegin( pWInfo->pTabList = pTabList; pWInfo->iBreak = sqlite3VdbeMakeLabel(v); pWInfo->regRowSet = (wctrlFlags & WHERE_FILL_ROWSET) ? regRowSet : -1; - pWInfo->pWC = pWC = (WhereClause*)&pWInfo->a[pWInfo->nLevel]; + pWInfo->pWC = pWC = (WhereClause *)&((u8 *)pWInfo)[nByteWInfo]; pWInfo->wctrlFlags = wctrlFlags; pMaskSet = (WhereMaskSet*)&pWC[1]; @@ -3231,7 +3285,7 @@ WhereInfo *sqlite3WhereBegin( Bitmask b = pTabItem->colUsed; int n = 0; for(; b; b=b>>1, n++){} - sqlite3VdbeChangeP2(v, sqlite3VdbeCurrentAddr(v)-2, n); + sqlite3VdbeChangeP4(v, sqlite3VdbeCurrentAddr(v)-1, SQLITE_INT_TO_PTR(n), P4_INT32); assert( n<=pTab->nCol ); } }else{ @@ -3244,7 +3298,6 @@ WhereInfo *sqlite3WhereBegin( int iIdxCur = pLevel->iIdxCur; assert( pIx->pSchema==pTab->pSchema ); assert( iIdxCur>=0 ); - sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pIx->nColumn+1); sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIx->tnum, iDb, (char*)pKey, P4_KEYINFO_HANDOFF); VdbeComment((v, "%s", pIx->zName)); diff --git a/test/alter.test b/test/alter.test index 2f66108..2db82dd 100644 --- a/test/alter.test +++ b/test/alter.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this script is testing the ALTER TABLE statement. # -# $Id: alter.test,v 1.31 2009/02/13 03:43:32 drh Exp $ +# $Id: alter.test,v 1.32 2009/03/24 15:08:10 drh Exp $ # set testdir [file dirname $argv0] @@ -677,7 +677,7 @@ do_test alter-9.2 { execsql { SELECT SQLITE_RENAME_TABLE(0,0); SELECT SQLITE_RENAME_TABLE(10,20); - SELECT SQLITE_RENAME_TABLE("foo", "foo"); + SELECT SQLITE_RENAME_TABLE('foo', 'foo'); } } {{} {} {}} diff --git a/test/alter2.test b/test/alter2.test index e56c976..0ce023b 100644 --- a/test/alter2.test +++ b/test/alter2.test @@ -13,7 +13,7 @@ # file format change that may be used in the future to implement # "ALTER TABLE ... ADD COLUMN". # -# $Id: alter2.test,v 1.13 2008/03/19 00:21:31 drh Exp $ +# $Id: alter2.test,v 1.14 2009/04/07 14:14:22 danielk1977 Exp $ # set testdir [file dirname $argv0] @@ -220,6 +220,8 @@ ifcapable trigger { SELECT * FROM clog; } } {{} 2 {} 6 {} 10} +} else { + execsql { CREATE TABLE abc3(a, b); } } #--------------------------------------------------------------------- @@ -276,9 +278,7 @@ ifcapable vacuum { get_file_format } {2} do_test alter2-5.2 { - execsql { - VACUUM; - } + execsql { VACUUM } } {} do_test alter2-5.3 { get_file_format diff --git a/test/async.test b/test/async.test index 457ec68..33f8630 100644 --- a/test/async.test +++ b/test/async.test @@ -6,7 +6,7 @@ #*********************************************************************** # This file runs all tests. # -# $Id: async.test,v 1.14 2008/09/15 14:47:21 danielk1977 Exp $ +# $Id: async.test,v 1.19 2009/04/11 10:25:04 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -17,15 +17,16 @@ if {[catch {sqlite3async_enable}]} { return } -rename finish_test really_finish_test +rename finish_test async_really_finish_test proc finish_test {} { catch {db close} catch {db2 close} catch {db3 close} } +if {[info exists ISQUICK]} { set ASYNC_SAVE_ISQUICK $ISQUICK } set ISQUICK 1 -set INCLUDE { +set ASYNC_INCLUDE { insert.test insert2.test insert3.test @@ -42,9 +43,9 @@ set INCLUDE { # Enable asynchronous IO. sqlite3async_enable 1 -rename do_test really_do_test +rename do_test async_really_do_test proc do_test {name args} { - uplevel really_do_test async_io-$name $args + uplevel async_really_do_test async_io-$name $args sqlite3async_start sqlite3async_halt idle sqlite3async_wait @@ -53,7 +54,7 @@ proc do_test {name args} { foreach testfile [lsort -dictionary [glob $testdir/*.test]] { set tail [file tail $testfile] - if {[lsearch -exact $INCLUDE $tail]<0} continue + if {[lsearch -exact $ASYNC_INCLUDE $tail]<0} continue source $testfile # Make sure everything is flushed through. This is because [source]ing @@ -77,6 +78,11 @@ sqlite3async_halt never sqlite3async_enable 0 set sqlite3async_trace 0 -really_finish_test -rename really_do_test do_test -rename really_finish_test finish_test +rename do_test {} +rename async_really_do_test do_test +rename finish_test {} +rename async_really_finish_test finish_test + +if {[info exists ASYNC_SAVE_ISQUICK]} { set ISQUICK $ASYNC_SAVE_ISQUICK } +finish_test + diff --git a/test/async2.test b/test/async2.test index a687a6c..cbe6991 100644 --- a/test/async2.test +++ b/test/async2.test @@ -5,7 +5,7 @@ # #*********************************************************************** # -# $Id: async2.test,v 1.9 2007/11/05 17:01:08 danielk1977 Exp $ +# $Id: async2.test,v 1.10 2009/03/24 17:43:57 drh Exp $ set testdir [file dirname $argv0] @@ -47,7 +47,7 @@ set sql_script { db close foreach err [list ioerr malloc-transient malloc-persistent] { - set ::go 1 + set ::go 10 for {set n 1} {$::go} {incr n} { set ::sqlite_io_error_pending 0 sqlite3_memdebug_fail -1 @@ -58,6 +58,7 @@ foreach err [list ioerr malloc-transient malloc-persistent] { sqlite3async_enable 1 sqlite3 db test.db + sqlite3_db_config_lookaside db 0 0 0 switch -- $err { ioerr { set ::sqlite_io_error_pending $n } @@ -78,7 +79,7 @@ foreach err [list ioerr malloc-transient malloc-persistent] { sqlite3_memdebug_fail -1 sqlite3 db test.db - set c [db eval {SELECT c FROM counter LIMIT 1}] + set c [db one {SELECT c FROM counter LIMIT 1}] switch -- $c { 1 { do_test async-$err-1.1.$n { @@ -112,7 +113,7 @@ foreach err [list ioerr malloc-transient malloc-persistent] { } {klmnopqrst and seven} } FIN { - set ::go 0 + incr ::go -1 } } diff --git a/test/async3.test b/test/async3.test index c45ab32..553088b 100644 --- a/test/async3.test +++ b/test/async3.test @@ -13,7 +13,7 @@ # Specifically, it tests that the xFullPathname() method of # of the asynchronous vfs works correctly. # -# $Id: async3.test,v 1.3 2007/11/05 17:01:08 danielk1977 Exp $ +# $Id: async3.test,v 1.4 2009/03/28 18:56:14 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -27,6 +27,7 @@ if { [info commands sqlite3async_enable]=="" } { db close sqlite3async_enable 1 +#set sqlite3async_trace 1 sqlite3async_start set paths { @@ -67,6 +68,7 @@ foreach p $paths { } db close + sqlite3async_halt idle sqlite3async_wait sqlite3async_halt never diff --git a/test/autovacuum.test b/test/autovacuum.test index 217b534..b843707 100644 --- a/test/autovacuum.test +++ b/test/autovacuum.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the SELECT statement. # -# $Id: autovacuum.test,v 1.28 2008/09/10 10:57:28 danielk1977 Exp $ +# $Id: autovacuum.test,v 1.29 2009/04/06 17:50:03 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -660,6 +660,37 @@ do_test autovacuum-8.2 { } {1 {database is locked}} catch {db2 close} catch {db eval {COMMIT}} - + +do_test autovacuum-9.1 { + execsql { + DROP TABLE t1; + DROP TABLE t2; + DROP TABLE t3; + DROP TABLE t4; + DROP TABLE t5; + PRAGMA page_count; + } +} {1} +do_test autovacuum-9.2 { + file size test.db +} 1024 +do_test autovacuum-9.3 { + execsql { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + INSERT INTO t1 VALUES(NULL, randstr(50,50)); + } + for {set ii 0} {$ii < 10} {incr ii} { + db eval { INSERT INTO t1 SELECT NULL, randstr(50,50) FROM t1 } + } + file size test.db +} $::sqlite_pending_byte +do_test autovacuum-9.4 { + execsql { INSERT INTO t1 SELECT NULL, randstr(50,50) FROM t1 } +} {} +do_test autovacuum-9.5 { + execsql { DELETE FROM t1 WHERE rowid > (SELECT max(a)/2 FROM t1) } + file size test.db +} $::sqlite_pending_byte + finish_test diff --git a/test/backup.test b/test/backup.test index 61e57b7..24ba699 100644 --- a/test/backup.test +++ b/test/backup.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the sqlite3_backup_XXX API. # -# $Id: backup.test,v 1.8 2009/02/12 17:01:50 danielk1977 Exp $ +# $Id: backup.test,v 1.9 2009/03/16 13:19:36 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -736,7 +736,7 @@ do_test backup-7.2.1 { } {} do_test backup-7.2.2 { B step 5000 -} {SQLITE_LOCKED} +} {SQLITE_BUSY} do_test backup-7.2.3 { execsql { ROLLBACK } B step 5000 diff --git a/test/backup2.test b/test/backup2.test index 2752bd5..17b5f6d 100644 --- a/test/backup2.test +++ b/test/backup2.test @@ -13,11 +13,13 @@ # of the TCL interface - methods which are based on the # sqlite3_backup_XXX API. # -# $Id: backup2.test,v 1.2 2009/02/11 16:06:19 shane Exp $ +# $Id: backup2.test,v 1.4 2009/04/07 14:14:23 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl +ifcapable !trigger||!view { finish_test ; return } + # Fill a database with test data. # do_test backup2-1 { @@ -111,8 +113,8 @@ do_test backup2-6 { # Try to backup to something that is not a database file. # do_test backup2-7 { - catch {file attributes bu2.db -permissions rw-------} catch {file attributes bu2.db -readonly 0} + catch {file attributes bu2.db -permissions rw-------} set out [open bu2.db w] puts $out "This is not a valid database file" close $out diff --git a/test/backup_ioerr.test b/test/backup_ioerr.test index e94cd4a..3b5e95d 100644 --- a/test/backup_ioerr.test +++ b/test/backup_ioerr.test @@ -12,7 +12,7 @@ # focus of this file is testing the handling of IO errors by the # sqlite3_backup_XXX APIs. # -# $Id: backup_ioerr.test,v 1.2 2009/02/04 22:46:47 drh Exp $ +# $Id: backup_ioerr.test,v 1.3 2009/04/10 18:41:01 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -53,7 +53,7 @@ proc populate_database {db {xtra_large 0}} { do_test backup_ioerr-1.1 { populate_database db set nPage [expr {[file size test.db] / 1024}] - expr {$nPage>140 && $nPage<150} + expr {$nPage>130 && $nPage<160} } {1} do_test backup_ioerr-1.2 { expr {[file size test.db] > $sqlite_pending_byte} diff --git a/test/bigfile.test b/test/bigfile.test index a036e6d..24a92c5 100644 --- a/test/bigfile.test +++ b/test/bigfile.test @@ -12,7 +12,7 @@ # focus of this script testing the ability of SQLite to handle database # files larger than 4GB. # -# $Id: bigfile.test,v 1.11 2008/11/21 22:21:51 drh Exp $ +# $Id: bigfile.test,v 1.12 2009/03/05 04:27:08 shane Exp $ # set testdir [file dirname $argv0] @@ -66,7 +66,6 @@ do_test bigfile-1.1 { db close if {[catch {fake_big_file 4096 [pwd]/test.db} msg]} { puts "**** Unable to create a file larger than 4096 MB. *****" - puts "$msg" finish_test return } @@ -83,7 +82,11 @@ do_test bigfile-1.2 { # tests. We will know the above test failed because the "db" command # does not exist. # -if {[llength [info command db]]>0} { +if {[llength [info command db]]<=0} { + puts "**** Large file support appears to be broken. *****" + finish_test + return +} do_test bigfile-1.3 { execsql { @@ -98,11 +101,6 @@ do_test bigfile-1.4 { SELECT md5sum(x) FROM t1; } } $::MAGIC_SUM -do_test bigfile-1.5 { - execsql { - SELECT md5sum(x) FROM t2; - } -} $::MAGIC_SUM db close if {[catch {fake_big_file 8192 [pwd]/test.db}]} { @@ -111,12 +109,18 @@ if {[catch {fake_big_file 8192 [pwd]/test.db}]} { return } -do_test bigfile-1.6 { +do_test bigfile-1.5 { sqlite3 db test.db execsql { SELECT md5sum(x) FROM t1; } } $::MAGIC_SUM +do_test bigfile-1.6 { + sqlite3 db test.db + execsql { + SELECT md5sum(x) FROM t2; + } +} $::MAGIC_SUM do_test bigfile-1.7 { execsql { CREATE TABLE t3 AS SELECT * FROM t1; @@ -135,11 +139,6 @@ do_test bigfile-1.9 { SELECT md5sum(x) FROM t2; } } $::MAGIC_SUM -do_test bigfile-1.10 { - execsql { - SELECT md5sum(x) FROM t3; - } -} $::MAGIC_SUM db close if {[catch {fake_big_file 16384 [pwd]/test.db}]} { @@ -148,33 +147,40 @@ if {[catch {fake_big_file 16384 [pwd]/test.db}]} { return } -do_test bigfile-1.11 { +do_test bigfile-1.10 { sqlite3 db test.db execsql { SELECT md5sum(x) FROM t1; } } $::MAGIC_SUM +do_test bigfile-1.11 { + sqlite3 db test.db + execsql { + SELECT md5sum(x) FROM t2; + } +} $::MAGIC_SUM do_test bigfile-1.12 { + sqlite3 db test.db + execsql { + SELECT md5sum(x) FROM t3; + } +} $::MAGIC_SUM +do_test bigfile-1.13 { execsql { CREATE TABLE t4 AS SELECT * FROM t1; SELECT md5sum(x) FROM t4; } } $::MAGIC_SUM -do_test bigfile-1.13 { +do_test bigfile-1.14 { db close sqlite3 db test.db execsql { SELECT md5sum(x) FROM t1; } } $::MAGIC_SUM -do_test bigfile-1.14 { - execsql { - SELECT md5sum(x) FROM t2; - } -} $::MAGIC_SUM do_test bigfile-1.15 { execsql { - SELECT md5sum(x) FROM t3; + SELECT md5sum(x) FROM t2; } } $::MAGIC_SUM do_test bigfile-1.16 { @@ -182,12 +188,5 @@ do_test bigfile-1.16 { SELECT md5sum(x) FROM t3; } } $::MAGIC_SUM -do_test bigfile-1.17 { - execsql { - SELECT md5sum(x) FROM t4; - } -} $::MAGIC_SUM - -} ;# End of the "if( db command exists )" finish_test diff --git a/test/bind.test b/test/bind.test index 1cd34fe..da0bf94 100644 --- a/test/bind.test +++ b/test/bind.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this script testing the sqlite_bind API. # -# $Id: bind.test,v 1.46 2009/02/09 05:18:33 danielk1977 Exp $ +# $Id: bind.test,v 1.47 2009/02/20 03:55:05 drh Exp $ # set testdir [file dirname $argv0] @@ -683,4 +683,76 @@ do_test bind-14.4 { param_names db { SELECT @a, @b FROM (SELECT NULL) } } {@a @b} +#-------------------------------------------------------------------------- +# Tests of the OP_Variable opcode where P3>1 +# +do_test bind-15.1 { + db eval {CREATE TABLE t4(a,b,c,d,e,f,g,h);} + set VM [sqlite3_prepare db { + INSERT INTO t4(a,b,c,d,f,g,h,e) VALUES(?,?,?,?,?,?,?,?) + } -1 TAIL] + sqlite3_bind_int $VM 1 1 + sqlite3_bind_int $VM 2 2 + sqlite3_bind_int $VM 3 3 + sqlite3_bind_int $VM 4 4 + sqlite3_bind_int $VM 5 5 + sqlite3_bind_int $VM 6 6 + sqlite3_bind_int $VM 7 7 + sqlite3_bind_int $VM 8 8 + sqlite3_step $VM + sqlite3_finalize $VM + db eval {SELECT * FROM t4} +} {1 2 3 4 8 5 6 7} +do_test bind-15.2 { + db eval {DELETE FROM t4} + set VM [sqlite3_prepare db { + INSERT INTO t4(a,b,c,d,e,f,g,h) VALUES(?,?,?,?,?,?,?,?) + } -1 TAIL] + sqlite3_bind_int $VM 1 1 + sqlite3_bind_int $VM 2 2 + sqlite3_bind_int $VM 3 3 + sqlite3_bind_int $VM 4 4 + sqlite3_bind_int $VM 5 5 + sqlite3_bind_int $VM 6 6 + sqlite3_bind_int $VM 7 7 + sqlite3_bind_int $VM 8 8 + sqlite3_step $VM + sqlite3_finalize $VM + db eval {SELECT * FROM t4} +} {1 2 3 4 5 6 7 8} +do_test bind-15.3 { + db eval {DELETE FROM t4} + set VM [sqlite3_prepare db { + INSERT INTO t4(h,g,f,e,d,c,b,a) VALUES(?,?,?,?,?,?,?,?) + } -1 TAIL] + sqlite3_bind_int $VM 1 1 + sqlite3_bind_int $VM 2 2 + sqlite3_bind_int $VM 3 3 + sqlite3_bind_int $VM 4 4 + sqlite3_bind_int $VM 5 5 + sqlite3_bind_int $VM 6 6 + sqlite3_bind_int $VM 7 7 + sqlite3_bind_int $VM 8 8 + sqlite3_step $VM + sqlite3_finalize $VM + db eval {SELECT * FROM t4} +} {8 7 6 5 4 3 2 1} +do_test bind-15.4 { + db eval {DELETE FROM t4} + set VM [sqlite3_prepare db { + INSERT INTO t4(a,b,c,d,e,f,g,h) VALUES(?,?,?,?4,?,?6,?,?) + } -1 TAIL] + sqlite3_bind_int $VM 1 1 + sqlite3_bind_int $VM 2 2 + sqlite3_bind_int $VM 3 3 + sqlite3_bind_int $VM 4 4 + sqlite3_bind_int $VM 5 5 + sqlite3_bind_int $VM 6 6 + sqlite3_bind_int $VM 7 7 + sqlite3_bind_int $VM 8 8 + sqlite3_step $VM + sqlite3_finalize $VM + db eval {SELECT * FROM t4} +} {1 2 3 4 5 6 7 8} + finish_test diff --git a/test/bindxfer.test b/test/bindxfer.test index 2669935..b8e71d9 100644 --- a/test/bindxfer.test +++ b/test/bindxfer.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this script testing the sqlite_transfer_bindings() API. # -# $Id: bindxfer.test,v 1.6 2008/10/12 00:27:54 shane Exp $ +# $Id: bindxfer.test,v 1.7 2009/04/09 14:02:44 drh Exp $ # set testdir [file dirname $argv0] @@ -68,11 +68,8 @@ ifcapable deprecated { do_test bindxfer-1.8 { set VALUES } {one two {}} - do_test bindxfer-1.9-misuse { - catch {sqlite3_finalize $VM1} - catch {sqlite3_finalize $VM2} - sqlite3_transfer_bindings $VM1 $VM2 - } 21 ;# SQLITE_MISUSE + catch {sqlite3_finalize $VM1} + catch {sqlite3_finalize $VM2} do_test bindxfer-1.10 { set VM1 [sqlite3_prepare $DB {SELECT ?, ?, ?} -1 TAIL] set VM2 [sqlite3_prepare $DB {SELECT ?, ?, ?, ?} -1 TAIL] diff --git a/test/bitvec.test b/test/bitvec.test index 24da959..d27f927 100644 --- a/test/bitvec.test +++ b/test/bitvec.test @@ -11,7 +11,7 @@ # # Unit testing of the Bitvec object. # -# $Id: bitvec.test,v 1.3 2008/11/19 18:30:35 shane Exp $ +# $Id: bitvec.test,v 1.4 2009/04/01 23:49:04 drh Exp $ # set testdir [file dirname $argv0] @@ -140,6 +140,14 @@ do_test bitvec-2.5 { do_test bitvec-2.6 { sqlite3BitvecBuiltinTest 50000 {3 60 2 50000 1 1 0} } 0 +do_test bitvec-2.7 { + sqlite3BitvecBuiltinTest 5000 { + 1 25 121 125 + 1 50 121 125 + 2 25 121 125 + 0 + } +} 0 # This procedure runs sqlite3BitvecBuiltinTest with argments "n" and # "program". But it also causes a malloc error to occur after the diff --git a/test/check.test b/test/check.test index cdf2b46..e899182 100644 --- a/test/check.test +++ b/test/check.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing CHECK constraints # -# $Id: check.test,v 1.11 2007/07/23 19:39:47 drh Exp $ +# $Id: check.test,v 1.12 2009/03/24 15:08:10 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -115,11 +115,12 @@ do_test check-1.17 { } {4 11.0} do_test check-2.1 { +breakpoint execsql { CREATE TABLE t2( x INTEGER CHECK( typeof(coalesce(x,0))=="integer" ), - y REAL CHECK( typeof(coalesce(y,0.1))=="real" ), - z TEXT CHECK( typeof(coalesce(z,''))=="text" ) + y REAL CHECK( typeof(coalesce(y,0.1))=='real' ), + z TEXT CHECK( typeof(coalesce(z,''))=='text' ) ); } } {} @@ -129,6 +130,8 @@ do_test check-2.2 { SELECT * FROM t2; } } {1 2.2 three} +db close +sqlite3 db test.db do_test check-2.3 { execsql { INSERT INTO t2 VALUES(NULL, NULL, NULL); diff --git a/test/corrupt2.test b/test/corrupt2.test index 652f784..acda254 100644 --- a/test/corrupt2.test +++ b/test/corrupt2.test @@ -13,7 +13,7 @@ # This file implements tests to make sure SQLite does not crash or # segfault if it sees a corrupt database file. # -# $Id: corrupt2.test,v 1.18 2008/09/29 11:49:48 danielk1977 Exp $ +# $Id: corrupt2.test,v 1.20 2009/04/06 17:50:03 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -233,6 +233,8 @@ db2 close proc corruption_test {args} { set A(-corrupt) {} + set A(-sqlprep) {} + set A(-tclprep) {} array set A $args catch {db close} @@ -240,6 +242,7 @@ proc corruption_test {args} { file delete -force corrupt.db-journal sqlite3 db corrupt.db + eval $A(-tclprep) db eval $A(-sqlprep) db close @@ -455,4 +458,78 @@ corruption_test -sqlprep { } {SQLITE_CORRUPT} } +corruption_test -sqlprep { + PRAGMA auto_vacuum = incremental; + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + CREATE TABLE t2(a INTEGER PRIMARY KEY, b); + INSERT INTO t1 VALUES(1, randstr(100,100)); + INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1; + INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1; + INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1; + INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1; + INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1; + INSERT INTO t2 SELECT * FROM t1; + DELETE FROM t1; +} -corrupt { + set offset [expr [file size corrupt.db] - 1024] + hexio_write corrupt.db $offset FF + hexio_write corrupt.db 24 12345678 +} -test { + do_test corrupt2-11.1 { + catchsql { PRAGMA incremental_vacuum } + } {1 {database disk image is malformed}} +} +corruption_test -sqlprep { + PRAGMA auto_vacuum = incremental; + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + CREATE TABLE t2(a INTEGER PRIMARY KEY, b); + INSERT INTO t1 VALUES(1, randstr(100,100)); + INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1; + INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1; + INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1; + INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1; + INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1; + INSERT INTO t2 SELECT * FROM t1; + DELETE FROM t1; +} -corrupt { + set pgno [expr [file size corrupt.db] / 1024] + hexio_write corrupt.db [expr 1024+5*($pgno-3)] 03 + hexio_write corrupt.db 24 12345678 +} -test { + do_test corrupt2-12.1 { + catchsql { PRAGMA incremental_vacuum } + } {1 {database disk image is malformed}} +} + +ifcapable autovacuum { + # It is not possible for the last page in a database file to be the + # pending-byte page (AKA the locking page). This test verifies that if + # an attempt is made to commit a transaction to such an auto-vacuum + # database SQLITE_CORRUPT is returned. + # + corruption_test -tclprep { + db eval { + PRAGMA auto_vacuum = full; + PRAGMA page_size = 1024; + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + INSERT INTO t1 VALUES(NULL, randstr(50,50)); + } + for {set ii 0} {$ii < 10} {incr ii} { + db eval { INSERT INTO t1 SELECT NULL, randstr(50,50) FROM t1 } + } + } -corrupt { + do_test corrupt2-13.1 { + file size corrupt.db + } $::sqlite_pending_byte + hexio_write corrupt.db [expr $::sqlite_pending_byte+1023] 00 + } -test { + do_test corrupt2-13.2 { + file size corrupt.db + } [expr $::sqlite_pending_byte + 1024] + do_test corrupt2-13.3 { + catchsql { DELETE FROM t1 WHERE rowid < 30; } + } {1 {database disk image is malformed}} + } +} + finish_test diff --git a/test/corruptC.test b/test/corruptC.test index 0ba860b..63fc2fb 100644 --- a/test/corruptC.test +++ b/test/corruptC.test @@ -15,7 +15,7 @@ # data base file, then tests that single byte corruptions in # increasingly larger quantities are handled gracefully. # -# $Id: corruptC.test,v 1.10 2008/11/19 18:43:07 drh Exp $ +# $Id: corruptC.test,v 1.11 2009/04/11 16:06:15 danielk1977 Exp $ catch {file delete -force test.db test.db-journal test.bu} @@ -260,6 +260,19 @@ do_test corruptC-2.13 { catchsql {BEGIN; CREATE TABLE t3 AS SELECT x,3 as y FROM t2 WHERE rowid%5!=0; ROLLBACK;} } {1 {database disk image is malformed}} +do_test corruptC-2.14 { + db close + copy_file test.bu test.db + + sqlite3 db test.db + set blob [string repeat abcdefghij 10000] + execsql { INSERT INTO t1 VALUES (1, $blob) } + + sqlite3 db test.db + set filesize [file size test.db] + hexio_write test.db [expr $filesize-2048] 00000001 + catchsql {DELETE FROM t1 WHERE rowid = (SELECT max(rowid) FROM t1)} +} {1 {database disk image is malformed}} # # now test for a series of quasi-random seeds diff --git a/test/count.test b/test/count.test new file mode 100644 index 0000000..d0adb53 --- /dev/null +++ b/test/count.test @@ -0,0 +1,192 @@ +# 2009 February 24 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing "SELECT count(*)" statements. +# +# $Id: count.test,v 1.4 2009/04/02 20:27:28 drh Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# Test plan: +# +# count-0.*: Make sure count(*) works on an empty database. (Ticket #3774) +# +# count-1.*: Test that the OP_Count instruction appears to work on both +# tables and indexes. Test both when they contain 0 entries, +# when all entries are on the root page, and when the b-tree +# forms a structure 2 and 3 levels deep. +# +# count-2.*: Test that +# +# + +do_test count-0.1 { + db eval { + SELECT count(*) FROM sqlite_master; + } +} {0} + +set iTest 0 +foreach zIndex [list { + /* no-op */ +} { + CREATE INDEX i1 ON t1(a); +}] { + incr iTest + do_test count-1.$iTest.1 { + execsql { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(a, b); + } + execsql $zIndex + execsql { SELECT count(*) FROM t1 } + } {0} + + do_test count-1.$iTest.2 { + execsql { + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t1 VALUES(3, 4); + SELECT count(*) FROM t1; + } + } {2} + + do_test count-1.$iTest.3 { + execsql { + INSERT INTO t1 SELECT * FROM t1; -- 4 + INSERT INTO t1 SELECT * FROM t1; -- 8 + INSERT INTO t1 SELECT * FROM t1; -- 16 + INSERT INTO t1 SELECT * FROM t1; -- 32 + INSERT INTO t1 SELECT * FROM t1; -- 64 + INSERT INTO t1 SELECT * FROM t1; -- 128 + INSERT INTO t1 SELECT * FROM t1; -- 256 + SELECT count(*) FROM t1; + } + } {256} + + do_test count-1.$iTest.4 { + execsql { + INSERT INTO t1 SELECT * FROM t1; -- 512 + INSERT INTO t1 SELECT * FROM t1; -- 1024 + INSERT INTO t1 SELECT * FROM t1; -- 2048 + INSERT INTO t1 SELECT * FROM t1; -- 4096 + SELECT count(*) FROM t1; + } + } {4096} + + do_test count-1.$iTest.5 { + execsql { + BEGIN; + INSERT INTO t1 SELECT * FROM t1; -- 8192 + INSERT INTO t1 SELECT * FROM t1; -- 16384 + INSERT INTO t1 SELECT * FROM t1; -- 32768 + INSERT INTO t1 SELECT * FROM t1; -- 65536 + COMMIT; + SELECT count(*) FROM t1; + } + } {65536} +} + +proc uses_op_count {sql} { + if {[lsearch [execsql "EXPLAIN $sql"] Count]>=0} { + return 1; + } + return 0 +} + +do_test count-2.1 { + execsql { + CREATE TABLE t2(a, b); + } + uses_op_count {SELECT count(*) FROM t2} +} {1} +do_test count-2.2 { + catchsql {SELECT count(DISTINCT *) FROM t2} +} {1 {near "*": syntax error}} +do_test count-2.3 { + uses_op_count {SELECT count(DISTINCT a) FROM t2} +} {0} +do_test count-2.4 { + uses_op_count {SELECT count(a) FROM t2} +} {0} +do_test count-2.5 { + uses_op_count {SELECT count() FROM t2} +} {1} +do_test count-2.6 { + catchsql {SELECT count(DISTINCT) FROM t2} +} {1 {DISTINCT aggregates must have exactly one argument}} +do_test count-2.7 { + uses_op_count {SELECT count(*)+1 FROM t2} +} {0} +do_test count-2.8 { + uses_op_count {SELECT count(*) FROM t2 WHERE a IS NOT NULL} +} {0} +do_test count-2.9 { + catchsql {SELECT count(*) FROM t2 HAVING count(*)>1} +} {1 {a GROUP BY clause is required before HAVING}} +do_test count-2.10 { + uses_op_count {SELECT count(*) FROM (SELECT 1)} +} {0} +do_test count-2.11 { + execsql { CREATE VIEW v1 AS SELECT 1 AS a } + uses_op_count {SELECT count(*) FROM v1} +} {0} +do_test count-2.12 { + uses_op_count {SELECT count(*), max(a) FROM t2} +} {0} +do_test count-2.13 { + uses_op_count {SELECT count(*) FROM t1, t2} +} {0} + +ifcapable vtab { + register_echo_module [sqlite3_connection_pointer db] + do_test count-2.14 { + execsql { CREATE VIRTUAL TABLE techo USING echo(t1); } + uses_op_count {SELECT count(*) FROM techo} + } {0} +} + +do_test count-3.1 { + execsql { + CREATE TABLE t3(a, b); + SELECT a FROM (SELECT count(*) AS a FROM t3) WHERE a==0; + } +} {0} +do_test count-3.2 { + execsql { + SELECT a FROM (SELECT count(*) AS a FROM t3) WHERE a==1; + } +} {} + +do_test count-4.1 { + execsql { + CREATE TABLE t4(a, b); + INSERT INTO t4 VALUES('a', 'b'); + CREATE INDEX t4i1 ON t4(b, a); + SELECT count(*) FROM t4; + } +} {1} +do_test count-4.2 { + execsql { + CREATE INDEX t4i2 ON t4(b); + SELECT count(*) FROM t4; + } +} {1} +do_test count-4.3 { + execsql { + DROP INDEX t4i1; + CREATE INDEX t4i1 ON t4(b, a); + SELECT count(*) FROM t4; + } +} {1} + + +finish_test diff --git a/test/date.test b/test/date.test index 1e0b748..1036993 100644 --- a/test/date.test +++ b/test/date.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing date and time functions. # -# $Id: date.test,v 1.32 2009/01/30 17:27:44 drh Exp $ +# $Id: date.test,v 1.33 2009/04/01 20:44:14 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -144,7 +144,15 @@ datetest 3.4 {strftime('%j','2003-10-31 12:34:56.432')} 304 datetest 3.5 {strftime('%J','2003-10-31 12:34:56.432')} 2452944.02426426 datetest 3.6 {strftime('%m','2003-10-31 12:34:56.432')} 10 datetest 3.7 {strftime('%M','2003-10-31 12:34:56.432')} 34 -datetest 3.8 {strftime('%s','2003-10-31 12:34:56.432')} 1067603696 +datetest 3.8.1 {strftime('%s','2003-10-31 12:34:56.432')} 1067603696 +datetest 3.8.2 {strftime('%s','2038-01-19 03:14:07')} 2147483647 +datetest 3.8.3 {strftime('%s','2038-01-19 03:14:08')} 2147483648 +datetest 3.8.4 {strftime('%s','2201-04-09 12:00:00')} 7298164800 +datetest 3.8.5 {strftime('%s','9999-12-31 23:59:59')} 253402300799 +datetest 3.8.6 {strftime('%s','1969-12-31 23:59:59')} -1 +datetest 3.8.7 {strftime('%s','1901-12-13 20:45:52')} -2147483648 +datetest 3.8.8 {strftime('%s','1901-12-13 20:45:51')} -2147483649 +datetest 3.8.9 {strftime('%s','1776-07-04 00:00:00')} -6106060800 datetest 3.9 {strftime('%S','2003-10-31 12:34:56.432')} 56 datetest 3.10 {strftime('%w','2003-10-31 12:34:56.432')} 5 datetest 3.11.1 {strftime('%W','2003-10-31 12:34:56.432')} 43 diff --git a/test/default.test b/test/default.test index 9a2ab1a..95a4ee0 100644 --- a/test/default.test +++ b/test/default.test @@ -12,7 +12,7 @@ # focus of this file is testing corner cases of the DEFAULT syntax # on table definitions. # -# $Id: default.test,v 1.2 2005/08/20 03:03:04 drh Exp $ +# $Id: default.test,v 1.3 2009/02/19 14:39:25 danielk1977 Exp $ # set testdir [file dirname $argv0] @@ -49,4 +49,19 @@ do_test default-1.3 { } } {1 {default value of column [y] is not constant}} +ifcapable pragma { + do_test default-2.1 { + execsql { + CREATE TABLE t4(c DEFAULT 'abc'); + PRAGMA table_info(t4); + } + } {0 c {} 0 'abc' 0} + do_test default-2.2 { + execsql { + INSERT INTO t4 DEFAULT VALUES; + PRAGMA table_info(t4); + } + } {0 c {} 0 'abc' 0} +} + finish_test diff --git a/test/delete.test b/test/delete.test index fc7e81d..c736466 100644 --- a/test/delete.test +++ b/test/delete.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the DELETE FROM statement. # -# $Id: delete.test,v 1.23 2008/04/19 20:53:26 drh Exp $ +# $Id: delete.test,v 1.25 2009/04/07 09:16:57 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -316,5 +316,79 @@ integrity_check delete-8.7 # (required before the next test file can be executed) will fail. # catch {file attributes test.db -readonly 0} +db close +file delete -force test.db test.db-journal + +# The following tests verify that SQLite correctly handles the case +# where an index B-Tree is being scanned, the rowid column being read +# from each index entry and another statement deletes some rows from +# the index B-Tree. At one point this (obscure) scenario was causing +# SQLite to return spurious SQLITE_CORRUPT errors and arguably incorrect +# query results. +# +do_test delete-9.1 { + sqlite3 db test.db + execsql { + CREATE TABLE t5(a, b); + CREATE TABLE t6(c, d); + INSERT INTO t5 VALUES(1, 2); + INSERT INTO t5 VALUES(3, 4); + INSERT INTO t5 VALUES(5, 6); + INSERT INTO t6 VALUES('a', 'b'); + INSERT INTO t6 VALUES('c', 'd'); + CREATE INDEX i5 ON t5(a); + CREATE INDEX i6 ON t6(c); + } +} {} +do_test delete-9.2 { + set res [list] + db eval { SELECT t5.rowid AS r, c, d FROM t5, t6 ORDER BY a } { + if {$r==2} { breakpoint ; db eval { DELETE FROM t5 } } + lappend res $r $c $d + } + set res +} {1 a b 1 c d 2 a b {} c d} +do_test delete-9.3 { + execsql { + INSERT INTO t5 VALUES(1, 2); + INSERT INTO t5 VALUES(3, 4); + INSERT INTO t5 VALUES(5, 6); + } + set res [list] + db eval { SELECT t5.rowid AS r, c, d FROM t5, t6 ORDER BY a } { + if {$r==2} { breakpoint ; db eval { DELETE FROM t5 WHERE rowid = 2 } } + lappend res $r $c $d + } + set res +} {1 a b 1 c d 2 a b {} c d 3 a b 3 c d} +do_test delete-9.4 { + execsql { + DELETE FROM t5; + INSERT INTO t5 VALUES(1, 2); + INSERT INTO t5 VALUES(3, 4); + INSERT INTO t5 VALUES(5, 6); + } + set res [list] + db eval { SELECT t5.rowid AS r, c, d FROM t5, t6 ORDER BY a } { + if {$r==2} { breakpoint ; db eval { DELETE FROM t5 WHERE rowid = 1 } } + lappend res $r $c $d + } + set res +} {1 a b 1 c d 2 a b 2 c d 3 a b 3 c d} +do_test delete-9.5 { + execsql { + DELETE FROM t5; + INSERT INTO t5 VALUES(1, 2); + INSERT INTO t5 VALUES(3, 4); + INSERT INTO t5 VALUES(5, 6); + } + set res [list] + db eval { SELECT t5.rowid AS r, c, d FROM t5, t6 ORDER BY a } { + if {$r==2} { breakpoint ; db eval { DELETE FROM t5 WHERE rowid = 3 } } + lappend res $r $c $d + } + set res +} {1 a b 1 c d 2 a b 2 c d} + finish_test diff --git a/test/fts3expr.test b/test/fts3expr.test index 3b6fef8..77caaea 100644 --- a/test/fts3expr.test +++ b/test/fts3expr.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this script is testing the FTS3 module. # -# $Id: fts3expr.test,v 1.6 2009/01/01 14:06:13 danielk1977 Exp $ +# $Id: fts3expr.test,v 1.7 2009/03/12 15:43:48 danielk1977 Exp $ # set testdir [file dirname $argv0] @@ -468,5 +468,13 @@ foreach {id expr res} { } $res } +do_test fts3expr-7.1 { + execsql { + CREATE VIRTUAL TABLE test USING fts3 (keyword); + INSERT INTO test VALUES ('abc'); + SELECT * FROM test WHERE keyword MATCH '""'; + } +} {} + set sqlite_fts3_enable_parentheses 0 finish_test diff --git a/test/hook.test b/test/hook.test index 33eeefc..b526708 100644 --- a/test/hook.test +++ b/test/hook.test @@ -17,7 +17,7 @@ # sqlite_update_hook (tests hook-4-*) # sqlite_rollback_hook (tests hook-5.*) # -# $Id: hook.test,v 1.14 2009/01/03 14:04:39 drh Exp $ +# $Id: hook.test,v 1.15 2009/04/07 14:14:23 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -158,26 +158,25 @@ do_test hook-4.1.2 { DELETE main t1 4 \ ] -# Update hook is not invoked for changes to sqlite_master -# -do_test hook-4.1.3 { - set ::update_hook {} - execsql { - CREATE TRIGGER r1 AFTER INSERT ON t1 BEGIN SELECT RAISE(IGNORE); END; - } - set ::update_hook -} {} -do_test hook-4.1.4 { - set ::update_hook {} - execsql { - DROP TRIGGER r1; - } - set ::update_hook -} {} - - -set ::update_hook {} ifcapable trigger { + # Update hook is not invoked for changes to sqlite_master + # + do_test hook-4.1.3 { + set ::update_hook {} + execsql { + CREATE TRIGGER r1 AFTER INSERT ON t1 BEGIN SELECT RAISE(IGNORE); END; + } + set ::update_hook + } {} + do_test hook-4.1.4 { + set ::update_hook {} + execsql { + DROP TRIGGER r1; + } + set ::update_hook + } {} + + set ::update_hook {} do_test hook-4.2.1 { catchsql { DROP TABLE t2; diff --git a/test/incrblob2.test b/test/incrblob2.test index 49e6750..5f7c1d4 100644 --- a/test/incrblob2.test +++ b/test/incrblob2.test @@ -12,7 +12,7 @@ # Test that it is possible to have two open blob handles on a single # blob object. # -# $Id: incrblob2.test,v 1.9 2008/10/03 08:44:54 danielk1977 Exp $ +# $Id: incrblob2.test,v 1.10 2009/03/16 13:19:36 danielk1977 Exp $ # set testdir [file dirname $argv0] @@ -265,7 +265,7 @@ ifcapable shared_cache { do_test incrblob2-5.3 { set blob [db incrblob t1 data 1] catchsql { INSERT INTO t1 VALUES(3, 'klmno') } db2 - } {1 {database is locked}} + } {1 {database table is locked}} do_test incrblob2-5.4 { close $blob diff --git a/test/incrvacuum.test b/test/incrvacuum.test index e1c9203..fe10acb 100644 --- a/test/incrvacuum.test +++ b/test/incrvacuum.test @@ -14,7 +14,7 @@ # Note: There are also some tests for incremental vacuum and IO # errors in incrvacuum_ioerr.test. # -# $Id: incrvacuum.test,v 1.22 2009/01/30 05:47:15 shane Exp $ +# $Id: incrvacuum.test,v 1.23 2009/02/18 20:31:18 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -673,9 +673,14 @@ do_test incrvacuum-12.2 { do_test incrvacuum-12.3 { execsql { ROLLBACK; } db2 execsql { PRAGMA auto_vacuum } -} {1} +} {2} ;# Still 2 because PRAGMA auto_vacuum setting held in case of vacuum +do_test incrvacuum-12.4 { + db close + sqlite3 db test.db + execsql { PRAGMA auto_vacuum } +} {1} ;# Revert to 1 because the database file did not change -do_test incrvacuum-12.3 { +do_test incrvacuum-12.5 { execsql { SELECT * FROM sqlite_master } execsql { PRAGMA auto_vacuum } } {1} diff --git a/test/indexedby.test b/test/indexedby.test index 38750e7..62a2a5f 100644 --- a/test/indexedby.test +++ b/test/indexedby.test @@ -9,7 +9,7 @@ # #*********************************************************************** # -# $Id: indexedby.test,v 1.4 2008/12/30 09:45:46 danielk1977 Exp $ +# $Id: indexedby.test,v 1.5 2009/03/22 20:36:19 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -225,5 +225,30 @@ do_test indexedby-9.3 { catchsql { select * from maintable, joinme INDEXED by joinme_id_text_idx } } {1 {cannot use index: joinme_id_text_idx}} -finish_test +# Make sure we can still create tables, indices, and columns whose name +# is "indexed". +# +do_test indexedby-10.1 { + execsql { + CREATE TABLE indexed(x,y); + INSERT INTO indexed VALUES(1,2); + SELECT * FROM indexed; + } +} {1 2} +do_test indexedby-10.2 { + execsql { + CREATE INDEX i10 ON indexed(x); + SELECT * FROM indexed indexed by i10 where x>0; + } +} {1 2} +do_test indexedby-10.3 { + execsql { + DROP TABLE indexed; + CREATE TABLE t10(indexed INTEGER); + INSERT INTO t10 VALUES(1); + CREATE INDEX indexed ON t10(indexed); + SELECT * FROM t10 indexed by indexed WHERE indexed>0 + } +} {1} +finish_test diff --git a/test/io.test b/test/io.test index e56d057..d9b7079 100644 --- a/test/io.test +++ b/test/io.test @@ -13,7 +13,7 @@ # IO traffic generated by SQLite (making sure SQLite is not writing out # more database pages than it has to, stuff like that). # -# $Id: io.test,v 1.20 2009/01/11 00:42:02 drh Exp $ +# $Id: io.test,v 1.21 2009/03/28 07:03:42 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -213,9 +213,7 @@ do_test io-2.6.2 { # opening the journal for exclusive access. As a result, the COMMIT # should fail with SQLITE_CANTOPEN and the transaction rolled back. # - set fd [open test.db-journal w] - puts $fd "This is not a journal file" - close $fd + file mkdir test.db-journal catchsql { COMMIT } } {1 {unable to open database file}} do_test io-2.6.3 { @@ -251,9 +249,7 @@ ifcapable attach { execsql { SELECT * FROM abc UNION ALL SELECT * FROM abc2 } } {1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10} do_test io-2.7.4 { - set fd [open test2.db-journal w] - puts $fd "This is not a journal file" - close $fd + file mkdir test2.db-journal catchsql { COMMIT } } {1 {unable to open database file}} do_test io-2.7.5 { diff --git a/test/ioerr.test b/test/ioerr.test index 5c5561b..21052e4 100644 --- a/test/ioerr.test +++ b/test/ioerr.test @@ -15,7 +15,7 @@ # The tests in this file use special facilities that are only # available in the SQLite test fixture. # -# $Id: ioerr.test,v 1.42 2008/11/26 07:40:30 danielk1977 Exp $ +# $Id: ioerr.test,v 1.43 2009/04/06 17:50:03 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -427,4 +427,34 @@ do_ioerr_test ioerr-15 -tclprep { COMMIT; } +# This test verifies that IO errors that occur within the obscure branch +# of code executed by tkt3762.test are correctly reported. +# +ifcapable vacuum&&autovacuum&&pragma { + do_ioerr_test ioerr-16 -erc 1 -ckrefcount 1 -sqlprep { + PRAGMA auto_vacuum=INCREMENTAL; + PRAGMA page_size=1024; + BEGIN; + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(zeroblob(900)); + INSERT INTO t1 VALUES(zeroblob(900)); + INSERT INTO t1 SELECT x FROM t1; + INSERT INTO t1 SELECT x FROM t1; + INSERT INTO t1 SELECT x FROM t1; + INSERT INTO t1 SELECT x FROM t1; + INSERT INTO t1 SELECT x FROM t1; + INSERT INTO t1 SELECT x FROM t1; + INSERT INTO t1 SELECT x FROM t1; + DELETE FROM t1 WHERE rowid>202; + COMMIT; + VACUUM; + PRAGMA cache_size = 10; + BEGIN; + DELETE FROM t1 WHERE rowid IN (10,11,12) ; + } -sqlbody { + PRAGMA incremental_vacuum(10); + COMMIT; + } +} + finish_test diff --git a/test/jrnlmode.test b/test/jrnlmode.test index 330188a..900a77f 100644 --- a/test/jrnlmode.test +++ b/test/jrnlmode.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The focus # of these tests is the journal mode pragma. # -# $Id: jrnlmode.test,v 1.12 2009/01/11 05:54:40 danielk1977 Exp $ +# $Id: jrnlmode.test,v 1.14 2009/04/10 15:02:44 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -25,7 +25,7 @@ if {[info exists TEMP_STORE] && $TEMP_STORE>=2} { set temp_persist memory set temp_delete memory set temp_truncate memory - set temp_off memory + set temp_off off } else { set temp_persist persist set temp_delete delete @@ -33,6 +33,16 @@ if {[info exists TEMP_STORE] && $TEMP_STORE>=2} { set temp_off off } +proc temp_journal_mode {newmode} { + if {[info exists ::TEMP_STORE] && $::TEMP_STORE>=2} { + if {$newmode ne "off" && $newmode ne "memory"} { + execsql {PRAGMA temp.journal_mode} + set newmode [db one {PRAGMA temp.journal_mode}] + } + } + set newmode +} + #---------------------------------------------------------------------- # Test cases jrnlmode-1.X test the PRAGMA logic. # @@ -42,7 +52,7 @@ do_test jrnlmode-1.0 { PRAGMA main.journal_mode; PRAGMA temp.journal_mode; } -} [list delete delete $temp_delete] +} [list delete delete [temp_journal_mode delete]] do_test jrnlmode-1.1 { execsql { PRAGMA journal_mode = persist; @@ -54,7 +64,7 @@ do_test jrnlmode-1.2 { PRAGMA main.journal_mode; PRAGMA temp.journal_mode; } -} [list persist persist $temp_persist] +} [list persist persist [temp_journal_mode persist]] do_test jrnlmode-1.4 { execsql { PRAGMA journal_mode = off; @@ -66,7 +76,7 @@ do_test jrnlmode-1.5 { PRAGMA main.journal_mode; PRAGMA temp.journal_mode; } -} [list off off $temp_off] +} [list off off [temp_journal_mode off]] do_test jrnlmode-1.6 { execsql { PRAGMA journal_mode = delete; @@ -76,9 +86,9 @@ do_test jrnlmode-1.7 { execsql { PRAGMA journal_mode; PRAGMA main.journal_mode; - PRAGMA temp.journal_mode; + PRAGMA Temp.journal_mode; } -} [list delete delete $temp_delete] +} [list delete delete [temp_journal_mode delete]] do_test jrnlmode-1.7.1 { execsql { PRAGMA journal_mode = truncate; @@ -90,7 +100,7 @@ do_test jrnlmode-1.7.2 { PRAGMA main.journal_mode; PRAGMA temp.journal_mode; } -} [list truncate truncate $temp_truncate] +} [list truncate truncate [temp_journal_mode truncate]] do_test jrnlmode-1.8 { execsql { PRAGMA journal_mode = off; @@ -117,7 +127,7 @@ ifcapable attach { PRAGMA temp.journal_mode; PRAGMA aux1.journal_mode; } - } [list off $temp_persist memory] + } [list off [temp_journal_mode persist] memory] do_test jrnlmode-1.11 { execsql { PRAGMA journal_mode; @@ -154,7 +164,7 @@ ifcapable attach { PRAGMA aux1.journal_mode; PRAGMA aux2.journal_mode; } - } [list delete $temp_delete memory memory] + } [list delete [temp_journal_mode delete] memory memory] do_test jrnlmode-1.15 { execsql { ATTACH ':memory:' as aux3; @@ -166,7 +176,7 @@ ifcapable attach { PRAGMA aux2.journal_mode; PRAGMA aux3.journal_mode; } - } [list delete $temp_delete memory memory memory] + } [list delete [temp_journal_mode delete] memory memory memory] do_test jrnlmode-1.16 { execsql { PRAGMA journal_mode = TRUNCATE; @@ -178,7 +188,7 @@ ifcapable attach { PRAGMA aux2.journal_mode; PRAGMA aux3.journal_mode; } - } [list truncate $temp_truncate memory memory memory] + } [list truncate [temp_journal_mode truncate] memory memory memory] do_test jrnlmode-1.99 { execsql { @@ -310,7 +320,10 @@ ifcapable pragma { PRAGMA aux.journal_size_limit; } } {-1} - do_test jrnlmode-5.4 { + do_test jrnlmode-5.4.1 { + execsql { PRAGMA aux.journal_size_limit = 999999999999 } + } {999999999999} + do_test jrnlmode-5.4.2 { execsql { PRAGMA aux.journal_size_limit = 10240 } } {10240} do_test jrnlmode-5.5 { @@ -484,4 +497,3 @@ ifcapable pragma { } finish_test - diff --git a/test/jrnlmode2.test b/test/jrnlmode2.test new file mode 100644 index 0000000..d0f3451 --- /dev/null +++ b/test/jrnlmode2.test @@ -0,0 +1,100 @@ +# 2009 March 24 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# $Id: jrnlmode2.test,v 1.5 2009/03/26 17:13:06 danielk1977 Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable {!pager_pragmas} { + finish_test + return +} + +#------------------------------------------------------------------------- +# Test overview: +# +# jrnlmode2-1.*: Demonstrate bug #3745 +# jrnlmode2-2.*: Demonstrate bug #3751 +# + +do_test jrnlmode2-1.1 { + execsql { + PRAGMA journal_mode = persist; + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + } +} {persist} + +do_test jrnlmode2-1.2 { + file exists test.db-journal +} {1} + +do_test jrnlmode2-1.3 { + sqlite3 db2 test.db + execsql { SELECT * FROM t1 } db2 +} {1 2} + +do_test jrnlmode2-1.4 { + execsql { + INSERT INTO t1 VALUES(3, 4); + BEGIN; + SELECT * FROM t1; + } + execsql { PRAGMA lock_status } +} {main shared temp closed} + +do_test jrnlmode2-1.5 { + file exists test.db-journal +} {1} + +do_test jrnlmode2-1.6 { + catchsql { SELECT * FROM t1 } db2 +} {0 {1 2 3 4}} + +do_test jrnlmode2-1.7 { + execsql { COMMIT } + catchsql { SELECT * FROM t1 } db2 +} {0 {1 2 3 4}} + + + +do_test jrnlmode2-2.1 { + db2 close + execsql { PRAGMA journal_mode = truncate } + execsql { INSERT INTO t1 VALUES(5, 6) } +} {} + +do_test jrnlmode2-2.2 { + file exists test.db-journal +} {1} + +do_test jrnlmode2-2.3 { + file size test.db-journal +} {0} + +do_test jrnlmode2-2.4 { + sqlite3 db2 test.db -readonly 1 + catchsql { SELECT * FROM t1 } db2 +} {0 {1 2 3 4 5 6}} + +do_test jrnlmode2-2.5 { + file delete test.db-journal +} {} + +do_test jrnlmode2-2.6 { + sqlite3 db2 test.db -readonly 1 + catchsql { SELECT * FROM t1 } db2 +} {0 {1 2 3 4 5 6}} + +catch { db2 close } +finish_test + diff --git a/test/loadext.test b/test/loadext.test index a35730d..db5745b 100644 --- a/test/loadext.test +++ b/test/loadext.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this script is extension loading. # -# $Id: loadext.test,v 1.16 2008/10/13 17:09:11 drh Exp $ +# $Id: loadext.test,v 1.17 2009/03/20 09:09:37 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -62,8 +62,7 @@ set dlerror_nosymbol {%s: undefined symbol: %s} if {$::tcl_platform(os) eq "Darwin"} { set dlerror_nosuchfile {dlopen(%s, 10): image not found} - set dlerror_notadll {dlopen(%1$s, 10): no suitable image found. Did find: - %1$s: file to short} + set dlerror_notadll {dlopen(%1$s, 10): no suitable image found.*} set dlerror_nosymbol {dlsym(XXX, %2$s): symbol not found} } @@ -74,9 +73,11 @@ if {$::tcl_platform(os) eq "Darwin"} { if {![file exists $testextension]} { set srcdir [file dir $testdir]/src set testextsrc $srcdir/test_loadext.c - if {[catch { - exec gcc {*}[set gcc_shared] -Wall -I$srcdir -I. -g $testextsrc -o $testextension - } msg]} { + + set cmdline [concat exec gcc $gcc_shared] + lappend cmdline -Wall -I$srcdir -I. -g $testextsrc -o $testextension + + if {[catch $cmdline msg]} { puts "Skipping loadext tests: Test extension not built..." puts $msg finish_test @@ -149,8 +150,9 @@ do_test loadext-2.2 { set rc [catch { sqlite3_load_extension db "${testextension}xx" } msg] - list $rc $msg -} [list 1 [format $dlerror_notadll ${testextension}xx]] + set expected_error_pattern [format $dlerror_notadll ${testextension}xx] + list $rc [string match $expected_error_pattern $msg] +} [list 1 1] # Try to load an extension for which the file is present but the # entry point is not. diff --git a/test/lock.test b/test/lock.test index fb634fe..03cd0e5 100644 --- a/test/lock.test +++ b/test/lock.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this script is database locks. # -# $Id: lock.test,v 1.37 2009/02/12 09:36:16 danielk1977 Exp $ +# $Id: lock.test,v 1.39 2009/03/28 15:04:24 drh Exp $ set testdir [file dirname $argv0] @@ -20,8 +20,8 @@ source $testdir/tester.tcl # Create an alternative connection to the database # do_test lock-1.0 { - # Give a complex pathnme to stress the path simplification logic in - # the vxworks driver. + # Give a complex pathname to stress the path simplification logic in + # the vxworks driver and in test_async. file mkdir tempdir/t1/t2 sqlite3 db2 ./tempdir/../tempdir/t1/.//t2/../../..//test.db set dummy {} diff --git a/test/lock3.test b/test/lock3.test index 4e9b54c..f771a19 100644 --- a/test/lock3.test +++ b/test/lock3.test @@ -13,7 +13,7 @@ # DEFERRED, IMMEDIATE, and EXCLUSIVE keywords as modifiers to the # BEGIN command. # -# $Id: lock3.test,v 1.2 2008/11/21 22:21:51 drh Exp $ +# $Id: lock3.test,v 1.4 2009/03/28 15:04:24 drh Exp $ set testdir [file dirname $argv0] diff --git a/test/lookaside.test b/test/lookaside.test index 08c402c..3c44560 100644 --- a/test/lookaside.test +++ b/test/lookaside.test @@ -11,7 +11,7 @@ # # Tests for the lookaside memory allocator. # -# $Id: lookaside.test,v 1.8 2008/10/11 17:06:04 drh Exp $ +# $Id: lookaside.test,v 1.10 2009/04/09 01:23:49 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -26,6 +26,7 @@ sqlite3_shutdown sqlite3_config_pagecache 0 0 sqlite3_config_scratch 0 0 sqlite3_initialize +autoinstall_test_functions sqlite3 db test.db # Make sure sqlite3_db_config() and sqlite3_db_status are working. @@ -43,11 +44,11 @@ do_test lookaside-1.4 { db eval {CREATE TABLE t1(w,x,y,z);} foreach {x y z} [sqlite3_db_status db SQLITE_DBSTATUS_LOOKASIDE_USED 0] break expr {$x==0 && $y<$z && $z==18} -} {1} +} {0} do_test lookaside-1.5 { foreach {x y z} [sqlite3_db_status db SQLITE_DBSTATUS_LOOKASIDE_USED 1] break expr {$x==0 && $y<$z && $z==18} -} {1} +} {0} do_test lookaside-1.6 { foreach {x y z} [sqlite3_db_status db SQLITE_DBSTATUS_LOOKASIDE_USED 0] break expr {$x==0 && $y==$z && $y<18} @@ -104,5 +105,6 @@ do_test lookaside-4.1 { catch sqlite3_config_error } {0} sqlite3_initialize +autoinstall_test_functions finish_test diff --git a/test/malloc.test b/test/malloc.test index 9a7c3ea..650b714 100644 --- a/test/malloc.test +++ b/test/malloc.test @@ -16,7 +16,7 @@ # to see what happens in the library if a malloc were to really fail # due to an out-of-memory situation. # -# $Id: malloc.test,v 1.75 2009/02/04 08:17:57 danielk1977 Exp $ +# $Id: malloc.test,v 1.78 2009/04/09 01:23:49 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -194,6 +194,7 @@ ifcapable vacuum { } } +autoinstall_test_functions do_malloc_test 7 -sqlprep { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); @@ -659,6 +660,7 @@ do_malloc_test 27 -tclprep { error "out of memory" } } +autoinstall_test_functions # Test that malloc failures that occur while processing INDEXED BY # clauses are handled correctly. @@ -791,6 +793,54 @@ do_malloc_test 32 -tclprep { db2 close } +# The following two OOM tests verify that OOM handling works in the +# code used to optimize "SELECT count(*) FROM ". +# +do_malloc_test 33 -tclprep { + db eval { PRAGMA cache_size = 10 } + db transaction { + db eval { CREATE TABLE abc(a, b) } + for {set i 0} {$i<500} {incr i} { + db eval {INSERT INTO abc VALUES(randstr(100,100), randstr(1000,1000))} + } + } +} -sqlbody { + SELECT count(*) FROM abc; +} +do_malloc_test 34 -tclprep { + db eval { PRAGMA cache_size = 10 } + db transaction { + db eval { CREATE TABLE abc(a PRIMARY KEY, b) } + for {set i 0} {$i<500} {incr i} { + db eval {INSERT INTO abc VALUES(randstr(100,100), randstr(1000,1000))} + } + } +} -sqlbody { + SELECT count(*) FROM abc; +} + +proc f {args} { error "Quite a long error!" } +do_malloc_test 35 -tclprep { + db func f f + set ::STMT [sqlite3_prepare db "SELECT f()" -1 DUMMY] + sqlite3_step $::STMT +} -tclbody { + sqlite3_finalize $::STMT +} -cleanup { + # At one point an assert( !db->mallocFailed ) could fail in the following + # call to sqlite3_errmsg(). Because sqlite3_finalize() had failed to clear + # the flag before returning. + sqlite3_errmsg16 db +} + +do_malloc_test 36 -sqlprep { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t1 VALUES(3, 4); +} -sqlbody { + SELECT test_agg_errmsg16(), group_concat(a) FROM t1 +} + # Ensure that no file descriptors were leaked. do_test malloc-99.X { catch {db close} diff --git a/test/malloc5.test b/test/malloc5.test index 10b6b55..73885b2 100644 --- a/test/malloc5.test +++ b/test/malloc5.test @@ -18,7 +18,7 @@ # no longer the case. In version 3.6.2, sqlite3_release_memory() only # reclaims clean pages. This test file has been updated accordingly. # -# $Id: malloc5.test,v 1.21 2008/12/30 17:55:00 drh Exp $ +# $Id: malloc5.test,v 1.22 2009/04/11 19:09:54 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -365,7 +365,11 @@ do_test malloc5-6.3.4 { # Now release 9900 more (about 9 pages worth). This should expunge # the rest of the db cache. But the db2 cache remains intact, because # SQLite tries to avoid calling sync(). - sqlite3_release_memory 9900 + if {$::tcl_platform(wordSize)==8} { + sqlite3_release_memory 10177 + } else { + sqlite3_release_memory 9900 + } list [nPage db] [nPage db2] } {0 3} do_test malloc5-6.3.5 { diff --git a/test/mallocC.test b/test/mallocC.test index 8bdc0d1..7316704 100644 --- a/test/mallocC.test +++ b/test/mallocC.test @@ -12,7 +12,7 @@ # This file tests aspects of the malloc failure while parsing # CREATE TABLE statements in auto_vacuum mode. # -# $Id: mallocC.test,v 1.9 2008/02/18 22:24:58 drh Exp $ +# $Id: mallocC.test,v 1.10 2009/04/11 16:27:50 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -87,6 +87,22 @@ execsql { PRAGMA auto_vacuum=1; CREATE TABLE t0(a, b, c); } + +# The number of memory allocation failures is different on 64-bit +# and 32-bit systems due to larger structures on 64-bit systems +# overflowing the lookaside more often. To debug problems, it is +# sometimes helpful to reduce the size of the lookaside allocation +# blocks. But this is normally disabled. +# +if {0} { + db close + sqlite3_shutdown + sqlite3_config_lookaside 50 500 + sqlite3_initialize + autoinstall_test_functions + sqlite3 db test.db +} + do_mallocC_test 1 -sql { BEGIN; -- Allocate 32 new root pages. This will exercise the 'extract specific diff --git a/test/memdb.test b/test/memdb.test index 7a26981..fd1a83d 100644 --- a/test/memdb.test +++ b/test/memdb.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this script is in-memory database backend. # -# $Id: memdb.test,v 1.17 2009/01/09 14:29:35 drh Exp $ +# $Id: memdb.test,v 1.18 2009/03/18 10:33:02 danielk1977 Exp $ set testdir [file dirname $argv0] @@ -244,7 +244,7 @@ foreach {i conf1 conf2 cmd t0 t1 t2} { do_test memdb-5.$i { if {$conf1!=""} {set conf1 "ON CONFLICT $conf1"} if {$conf2!=""} {set conf2 "ON CONFLICT $conf2"} - set r0 [catch {execsql [subst { + set r0 [catch {execsql " DROP TABLE t1; CREATE TABLE t1(a,b,c, UNIQUE(a) $conf1); INSERT INTO t1 SELECT * FROM t2; @@ -253,7 +253,7 @@ foreach {i conf1 conf2 cmd t0 t1 t2} { $cmd t3 SET x=1; $cmd t1 SET b=b*2; $cmd t1 SET a=c+5; - }]} r1] + "} r1] catch {execsql {COMMIT}} if {!$r0} {set r1 [execsql {SELECT a FROM t1 ORDER BY b}]} set r2 [execsql {SELECT x FROM t3}] diff --git a/test/memsubsys1.test b/test/memsubsys1.test index 849c404..57dc4e6 100644 --- a/test/memsubsys1.test +++ b/test/memsubsys1.test @@ -11,7 +11,7 @@ # # This file contains tests of the memory allocation subsystem # -# $Id: memsubsys1.test,v 1.13 2008/10/01 13:55:51 drh Exp $ +# $Id: memsubsys1.test,v 1.15 2009/04/11 14:46:43 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -200,7 +200,7 @@ do_test memsubsys1-5.6 { db close sqlite3_shutdown sqlite3_config_pagecache [expr 4096+$xtra_size] 24 -sqlite3_config_scratch 25000 1 +sqlite3_config_scratch 25300 1 sqlite3_initialize reset_highwater_marks build_test_db memsubsys1-6 {PRAGMA page_size=4096} @@ -226,7 +226,7 @@ do_test memsubsys1-6.6 { db close sqlite3_shutdown sqlite3_config_pagecache [expr 4096+$xtra_size] 24 -sqlite3_config_scratch 25000 1 +sqlite3_config_scratch 25300 1 sqlite3_initialize reset_highwater_marks build_test_db memsubsys1-7 { @@ -253,9 +253,55 @@ do_test memsubsys1-7.7 { set s_ovfl [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_OVERFLOW 0] 2] } 0 +# Test 8: Disable PAGECACHE. Make available SCRATCH zero. Verify that +# the SCRATCH overflow logic works. +# db close sqlite3_shutdown sqlite3_config_pagecache 0 0 +sqlite3_config_scratch 25000 0 +sqlite3_initialize +reset_highwater_marks +do_test memsubsys1-8.1 { + set pg_used [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_USED 0] 2] +} 0 +do_test memsubsys1-8.2 { + set s_ovfl [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_OVERFLOW 0] 2] +} 0 +do_test memsubsys1-8.3 { + sqlite3 db :memory: + db eval { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(zeroblob(400)); + INSERT INTO t1 VALUES(zeroblob(400)); + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + } + expr {[lindex [sqlite3_status SQLITE_STATUS_SCRATCH_OVERFLOW 0] 2]>0} +} 1 +db close +sqlite3_shutdown +sqlite3_config_memstatus 0 +sqlite3_initialize +do_test memsubsys1-8.4 { + sqlite3 db :memory: + db eval { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(zeroblob(400)); + INSERT INTO t1 VALUES(zeroblob(400)); + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM t1; + SELECT rowid FROM t1; + } +} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16} + + +db close +sqlite3_shutdown +sqlite3_config_memstatus 1 +sqlite3_config_pagecache 0 0 sqlite3_config_scratch 0 0 sqlite3_config_lookaside 100 500 sqlite3_initialize diff --git a/test/mutex1.test b/test/mutex1.test index 9e9da35..75580f3 100644 --- a/test/mutex1.test +++ b/test/mutex1.test @@ -9,7 +9,7 @@ # #*********************************************************************** # -# $Id: mutex1.test,v 1.17 2009/01/09 14:29:35 drh Exp $ +# $Id: mutex1.test,v 1.19 2009/03/24 15:08:10 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -101,8 +101,8 @@ set enable_shared_cache [sqlite3_enable_shared_cache 1] ifcapable threadsafe { foreach {mode mutexes} { singlethread {} - multithread {fast static_lru static_master static_mem static_prng } - serialized {fast recursive static_lru static_master static_mem static_prng} + multithread {fast static_lru static_master static_mem static_open static_prng } + serialized {fast recursive static_lru static_master static_mem static_open static_prng} } { do_test mutex1.2.$mode.1 { @@ -114,7 +114,7 @@ ifcapable threadsafe { do_test mutex1.2.$mode.2 { sqlite3_initialize clear_mutex_counters - sqlite3 db test.db -nomutex 0 + sqlite3 db test.db -nomutex 0 -fullmutex 0 catchsql { CREATE TABLE abc(a, b, c) } db eval { INSERT INTO abc VALUES(1, 2, 3); diff --git a/test/notify1.test b/test/notify1.test new file mode 100644 index 0000000..1b6c516 --- /dev/null +++ b/test/notify1.test @@ -0,0 +1,501 @@ +# 2009 March 04 +# +# 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 file is testing the sqlite3_unlock_notify() API. +# +# $Id: notify1.test,v 1.3 2009/04/07 22:06:57 drh Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable !unlock_notify||!shared_cache { + finish_test + return +} +db close +set ::enable_shared_cache [sqlite3_enable_shared_cache 1] + +#------------------------------------------------------------------------- +# Warm body test. Test that an unlock-notify callback can be registered +# and that it is invoked. +# +do_test notify1-1.1 { + sqlite3 db test.db + sqlite3 db2 test.db + execsql { CREATE TABLE t1(a, b) } +} {} +do_test notify1-1.2 { + execsql { + BEGIN; + INSERT INTO t1 VALUES(1, 2); + } + catchsql { INSERT INTO t1 VALUES(3, 4) } db2 +} {1 {database table is locked}} +do_test notify1-1.3 { + set zScript "" + db2 unlock_notify { + set zScript "db2 eval { INSERT INTO t1 VALUES(3, 4) }" + } + execsql { SELECT * FROM t1 } +} {1 2} +do_test notify1-1.4 { + set zScript +} {} +do_test notify1-1.5 { + execsql { COMMIT } + eval $zScript + execsql { SELECT * FROM t1 } +} {1 2 3 4} + +#------------------------------------------------------------------------- +# Verify that invoking the "unlock_notify" method with no arguments +# (which is the equivalent of invoking sqlite3_unlock_notify() with +# a NULL xNotify argument) cancels a pending notify callback. +# +do_test notify1-1.11 { + execsql { DROP TABLE t1; CREATE TABLE t1(a, b) } +} {} +do_test notify1-1.12 { + execsql { + BEGIN; + INSERT INTO t1 VALUES(1, 2); + } + catchsql { INSERT INTO t1 VALUES(3, 4) } db2 +} {1 {database table is locked}} +do_test notify1-1.13 { + set zScript "" + db2 unlock_notify { + set zScript "db2 eval { INSERT INTO t1 VALUES(3, 4) }" + } + execsql { SELECT * FROM t1 } +} {1 2} +do_test notify1-1.14 { + set zScript +} {} +do_test notify1-1.15 { + db2 unlock_notify + execsql { COMMIT } + eval $zScript + execsql { SELECT * FROM t1 } +} {1 2} + +#------------------------------------------------------------------------- +# The following tests, notify1-2.*, test that deadlock is detected +# correctly. +# +do_test notify1-2.1 { + execsql { + CREATE TABLE t2(a, b); + INSERT INTO t2 VALUES('I', 'II'); + } +} {} + +# +# Test for simple deadlock involving two database connections. +# +# 1. Grab a write-lock on t1 with [db]. Then grab a read-lock on t2 with [db2]. +# 2. Try to grab a read-lock on t1 with [db2] (fails). +# 3. Have [db2] wait on the read-lock it failed to obtain in step 2. +# 4. Try to grab a write-lock on t2 with [db] (fails). +# 5. Try to have [db] wait on the lock from step 4. Fails, as the system +# would be deadlocked (since [db2] is already waiting on [db], and this +# operation would have [db] wait on [db2]). +# +do_test notify1-2.2.1 { + execsql { + BEGIN; + INSERT INTO t1 VALUES(5, 6); + } + execsql { + BEGIN; + SELECT * FROM t2; + } db2 +} {I II} +do_test notify1-2.2.2 { + catchsql { SELECT * FROM t1 } db2 +} {1 {database table is locked: t1}} +do_test notify1-2.2.3 { + db2 unlock_notify {lappend unlock_notify db2} +} {} +do_test notify1-2.2.4 { + catchsql { INSERT INTO t2 VALUES('III', 'IV') } +} {1 {database table is locked: t2}} +do_test notify1-2.2.5 { + set rc [catch { db unlock_notify {lappend unlock_notify db} } msg] + list $rc $msg +} {1 {database is deadlocked}} + +# +# Test for slightly more complex deadlock involving three database +# connections: db, db2 and db3. +# +do_test notify1-2.3.1 { + db close + db2 close + file delete -force test.db test2.db test3.db + foreach con {db db2 db3} { + sqlite3 $con test.db + $con eval { ATTACH 'test2.db' AS aux2 } + $con eval { ATTACH 'test3.db' AS aux3 } + } + execsql { + CREATE TABLE main.t1(a, b); + CREATE TABLE aux2.t2(a, b); + CREATE TABLE aux3.t3(a, b); + } +} {} +do_test notify1-2.3.2 { + execsql { BEGIN ; INSERT INTO t1 VALUES(1, 2) } db + execsql { BEGIN ; INSERT INTO t2 VALUES(1, 2) } db2 + execsql { BEGIN ; INSERT INTO t3 VALUES(1, 2) } db3 +} {} +do_test notify1-2.3.3 { + catchsql { SELECT * FROM t2 } db +} {1 {database table is locked: t2}} +do_test notify1-2.3.4 { + catchsql { SELECT * FROM t3 } db2 +} {1 {database table is locked: t3}} +do_test notify1-2.3.5 { + catchsql { SELECT * FROM t1 } db3 +} {1 {database table is locked: t1}} +do_test notify1-2.3.6 { + set lUnlock [list] + db unlock_notify {lappend lUnlock db} + db2 unlock_notify {lappend lUnlock db2} +} {} +do_test notify1-2.3.7 { + set rc [catch { db3 unlock_notify {lappend lUnlock db3} } msg] + list $rc $msg +} {1 {database is deadlocked}} +do_test notify1-2.3.8 { + execsql { COMMIT } + set lUnlock +} {} +do_test notify1-2.3.9 { + db3 unlock_notify {lappend lUnlock db3} + set lUnlock +} {db3} +do_test notify1-2.3.10 { + execsql { COMMIT } db2 + set lUnlock +} {db3 db} +do_test notify1-2.3.11 { + execsql { COMMIT } db3 + set lUnlock +} {db3 db db2} +catch { db3 close } +catch { db2 close } +catch { db close } + +#------------------------------------------------------------------------- +# The following tests, notify1-3.* and notify1-4.*, test that callbacks +# can be issued when there are many (>16) connections waiting on a single +# unlock event. +# +foreach {tn nConn} {3 20 4 76} { + do_test notify1-$tn.1 { + sqlite3 db test.db + execsql { + BEGIN; + INSERT INTO t1 VALUES('a', 'b'); + } + } {} + set lUnlock [list] + set lUnlockFinal [list] + for {set ii 1} {$ii <= $nConn} {incr ii} { + do_test notify1-$tn.2.$ii.1 { + set cmd "db$ii" + sqlite3 $cmd test.db + catchsql { SELECT * FROM t1 } $cmd + } {1 {database table is locked: t1}} + do_test notify1-$tn.2.$ii.2 { + $cmd unlock_notify "lappend lUnlock $ii" + } {} + lappend lUnlockFinal $ii + } + do_test notify1-$tn.3 { + set lUnlock + } {} + do_test notify1-$tn.4 { + execsql {COMMIT} + lsort -integer $lUnlock + } $lUnlockFinal + do_test notify1-$tn.5 { + for {set ii 1} {$ii <= $nConn} {incr ii} { + "db$ii" close + } + } {} +} +db close + +#------------------------------------------------------------------------- +# These tests, notify1-5.*, test that a malloc() failure that occurs while +# allocating an array to use as an argument to an unlock-notify callback +# is handled correctly. +# +source $testdir/malloc_common.tcl +breakpoint +do_malloc_test notify1-5 -tclprep { + set ::lUnlock [list] + execsql { + CREATE TABLE t1(a, b); + BEGIN; + INSERT INTO t1 VALUES('a', 'b'); + } + for {set ii 1} {$ii <= 60} {incr ii} { + set cmd "db$ii" + sqlite3 $cmd test.db + catchsql { SELECT * FROM t1 } $cmd + $cmd unlock_notify "lappend ::lUnlock $ii" + } +} -sqlbody { + COMMIT; +} -cleanup { + # One of two things should have happened: + # + # 1) The transaction opened by [db] was not committed. No unlock-notify + # callbacks were invoked, OR + # 2) The transaction opened by [db] was committed and 60 unlock-notify + # callbacks were invoked. + # + do_test notify1-5.systemstate { + expr { ([llength $::lUnlock]==0 && [sqlite3_get_autocommit db]==0) + || ([llength $::lUnlock]==60 && [sqlite3_get_autocommit db]==1) + } + } {1} + for {set ii 1} {$ii <= 60} {incr ii} { "db$ii" close } +} + +#------------------------------------------------------------------------- +# Test cases notify1-6.* test cases where the following occur: +# +# notify1-6.1.*: Test encountering an SQLITE_LOCKED error when the +# "blocking connection" has already been set by a previous +# SQLITE_LOCKED. +# +# notify1-6.2.*: Test encountering an SQLITE_LOCKED error when already +# waiting on an unlock-notify callback. +# +# notify1-6.3.*: Test that if an SQLITE_LOCKED error is encountered while +# already waiting on an unlock-notify callback, and then +# the blocker that caused the SQLITE_LOCKED commits its +# transaction, the unlock-notify callback is not invoked. +# +# notify1-6.4.*: Like 6.3.*, except that instead of the second blocker +# committing its transaction, the first does. The +# unlock-notify callback is therefore invoked. +# +db close +do_test notify1-6.1.1 { + file delete -force test.db test2.db + foreach conn {db db2 db3} { + sqlite3 $conn test.db + execsql { ATTACH 'test2.db' AS two } $conn + } + execsql { + CREATE TABLE t1(a, b); + CREATE TABLE two.t2(a, b); + } + execsql { + BEGIN; + INSERT INTO t1 VALUES(1, 2); + } db2 + execsql { + BEGIN; + INSERT INTO t2 VALUES(1, 2); + } db3 +} {} +do_test notify1-6.1.2 { + catchsql { SELECT * FROM t2 } +} {1 {database table is locked: t2}} +do_test notify1-6.1.3 { + catchsql { SELECT * FROM t1 } +} {1 {database table is locked: t1}} + +do_test notify1-6.2.1 { + set unlocked 0 + db unlock_notify {set unlocked 1} + set unlocked +} {0} +do_test notify1-6.2.2 { + catchsql { SELECT * FROM t2 } +} {1 {database table is locked: t2}} +do_test notify1-6.2.3 { + execsql { COMMIT } db2 + set unlocked +} {1} + +do_test notify1-6.3.1 { + execsql { + BEGIN; + INSERT INTO t1 VALUES(3, 4); + } db2 +} {} +do_test notify1-6.3.2 { + catchsql { SELECT * FROM t1 } +} {1 {database table is locked: t1}} +do_test notify1-6.3.3 { + set unlocked 0 + db unlock_notify {set unlocked 1} + set unlocked +} {0} +do_test notify1-6.3.4 { + catchsql { SELECT * FROM t2 } +} {1 {database table is locked: t2}} +do_test notify1-6.3.5 { + execsql { COMMIT } db3 + set unlocked +} {0} + +do_test notify1-6.4.1 { + execsql { + BEGIN; + INSERT INTO t2 VALUES(3, 4); + } db3 + catchsql { SELECT * FROM t2 } +} {1 {database table is locked: t2}} +do_test notify1-6.4.2 { + execsql { COMMIT } db2 + set unlocked +} {1} +do_test notify1-6.4.3 { + execsql { COMMIT } db3 +} {} +db close +db2 close +db3 close + +#------------------------------------------------------------------------- +# Test cases notify1-7.* tests that when more than one distinct +# unlock-notify function is registered, all are invoked correctly. +# +proc unlock_notify {} { + incr ::unlock_notify +} +do_test notify1-7.1 { + foreach conn {db db2 db3} { + sqlite3 $conn test.db + } + execsql { + BEGIN; + INSERT INTO t1 VALUES(5, 6); + } +} {} +do_test notify1-7.2 { + catchsql { SELECT * FROM t1 } db2 +} {1 {database table is locked: t1}} +do_test notify1-7.3 { + catchsql { SELECT * FROM t1 } db3 +} {1 {database table is locked: t1}} +do_test notify1-7.4 { + set unlock_notify 0 + db2 unlock_notify unlock_notify + sqlite3_unlock_notify db3 +} {SQLITE_OK} +do_test notify1-7.5 { + set unlock_notify +} {0} +do_test notify1-7.6 { + execsql { COMMIT } + set unlock_notify +} {2} + +#------------------------------------------------------------------------- +# Test cases notify1-8.* tests that the correct SQLITE_LOCKED extended +# error code is returned in various scenarios. +# +do_test notify1-8.1 { + execsql { + BEGIN; + INSERT INTO t1 VALUES(7, 8); + } + catchsql { SELECT * FROM t1 } db2 +} {1 {database table is locked: t1}} +do_test notify1-8.2 { + sqlite3_extended_errcode db2 +} {SQLITE_LOCKED_SHAREDCACHE} + +do_test notify1-8.3 { + execsql { + COMMIT; + BEGIN EXCLUSIVE; + } + catchsql { SELECT * FROM t1 } db2 +} {1 {database schema is locked: main}} +do_test notify1-8.4 { + sqlite3_extended_errcode db2 +} {SQLITE_LOCKED_SHAREDCACHE} + +do_test notify1-8.X { + execsql { COMMIT } +} {} + +#------------------------------------------------------------------------- +# Test cases notify1-9.* test the shared-cache 'pending-lock' feature. +# +do_test notify1-9.1 { + execsql { + CREATE TABLE t2(a, b); + BEGIN; + SELECT * FROM t1; + } db2 +} {1 2 3 4 5 6 7 8} +do_test notify1-9.2 { + execsql { SELECT * FROM t1 } db3 +} {1 2 3 4 5 6 7 8} +do_test notify1-9.3 { + catchsql { + BEGIN; + INSERT INTO t1 VALUES(9, 10); + } +} {1 {database table is locked: t1}} +do_test notify1-9.4 { + catchsql { SELECT * FROM t2 } db3 +} {1 {database table is locked}} +do_test notify1-9.5 { + execsql { COMMIT } db2 + execsql { SELECT * FROM t2 } db3 +} {} +do_test notify1-9.6 { + execsql { COMMIT } +} {} + +do_test notify1-9.7 { + execsql { + BEGIN; + SELECT * FROM t1; + } db2 +} {1 2 3 4 5 6 7 8} +do_test notify1-9.8 { + execsql { SELECT * FROM t1 } db3 +} {1 2 3 4 5 6 7 8} +do_test notify1-9.9 { + catchsql { + BEGIN; + INSERT INTO t1 VALUES(9, 10); + } +} {1 {database table is locked: t1}} +do_test notify1-9.10 { + catchsql { SELECT * FROM t2 } db3 +} {1 {database table is locked}} +do_test notify1-9.11 { + execsql { COMMIT } + execsql { SELECT * FROM t2 } db3 +} {} +do_test notify1-9.12 { + execsql { COMMIT } db2 +} {} + +db close +db2 close +db3 close +sqlite3_enable_shared_cache $::enable_shared_cache +finish_test diff --git a/test/notify2.test b/test/notify2.test new file mode 100644 index 0000000..bfb9af8 --- /dev/null +++ b/test/notify2.test @@ -0,0 +1,242 @@ +# 2009 March 04 +# +# 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. +# +#*********************************************************************** +# +# $Id: notify2.test,v 1.7 2009/03/30 11:59:31 drh Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +if {[run_thread_tests]==0} { finish_test ; return } +ifcapable !unlock_notify||!shared_cache { finish_test ; return } + +# The tests in this file test the sqlite3_blocking_step() function in +# test_thread.c. sqlite3_blocking_step() is not an SQLite API function, +# it is just a demonstration of how the sqlite3_unlock_notify() function +# can be used to synchronize multi-threaded access to SQLite databases +# in shared-cache mode. +# +# Since the implementation of sqlite3_blocking_step() is included on the +# website as example code, it is important to test that it works. +# +# notify2-1.*: +# +# This test uses $nThread threads. Each thread opens the main database +# and attaches two other databases. Each database contains a single table. +# +# Each thread repeats transactions over and over for 20 seconds. Each +# transaction consists of 3 operations. Each operation is either a read +# or a write of one of the tables. The read operations verify an invariant +# to make sure that things are working as expected. If an SQLITE_LOCKED +# error is returned the current transaction is rolled back immediately. +# +# This exercise is repeated twice, once using sqlite3_step(), and the +# other using sqlite3_blocking_step(). The results are compared to ensure +# that sqlite3_blocking_step() resulted in higher transaction throughput. +# + +db close +set ::enable_shared_cache [sqlite3_enable_shared_cache 1] + +# Number of threads to run simultaneously. +# +set nThread 6 +set nSecond 5 + +# The Tcl script executed by each of the $nThread threads used by this test. +# +set ThreadProgram { + + # Proc used by threads to execute SQL. + # + proc execsql_blocking {db zSql} { + set lRes [list] + set rc SQLITE_OK + +set sql $zSql + + while {$rc=="SQLITE_OK" && $zSql ne ""} { + set STMT [$::xPrepare $db $zSql -1 zSql] + while {[set rc [$::xStep $STMT]] eq "SQLITE_ROW"} { + for {set i 0} {$i < [sqlite3_column_count $STMT]} {incr i} { + lappend lRes [sqlite3_column_text $STMT 0] + } + } + set rc [sqlite3_finalize $STMT] + } + + if {$rc != "SQLITE_OK"} { error "$rc $sql [sqlite3_errmsg $db]" } + return $lRes + } + + proc execsql_retry {db sql} { + set msg "SQLITE_LOCKED blah..." + while { [string match SQLITE_LOCKED* $msg] } { + catch { execsql_blocking $db $sql } msg + } + } + + proc select_one {args} { + set n [llength $args] + lindex $args [expr int($n*rand())] + } + + proc opendb {} { + # Open a database connection. Attach the two auxillary databases. + set ::DB [sqlite3_open test.db] + execsql_retry $::DB { ATTACH 'test2.db' AS aux2; } + execsql_retry $::DB { ATTACH 'test3.db' AS aux3; } + } + + opendb + + #after 2000 + + # This loop runs for ~20 seconds. + # + set iStart [clock_seconds] + while { ([clock_seconds]-$iStart) < $nSecond } { + + # Each transaction does 3 operations. Each operation is either a read + # or write of a randomly selected table (t1, t2 or t3). Set the variables + # $SQL(1), $SQL(2) and $SQL(3) to the SQL commands used to implement + # each operation. + # + for {set ii 1} {$ii <= 3} {incr ii} { + foreach {tbl database} [select_one {t1 main} {t2 aux2} {t3 aux3}] {} + + set SQL($ii) [string map [list xxx $tbl yyy $database] [select_one { + SELECT + (SELECT b FROM xxx WHERE a=(SELECT max(a) FROM xxx))==total(a) + FROM xxx WHERE a!=(SELECT max(a) FROM xxx); + } { + DELETE FROM xxx WHERE a<(SELECT max(a)-100 FROM xxx); + INSERT INTO xxx SELECT NULL, total(a) FROM xxx; + } { + CREATE INDEX IF NOT EXISTS yyy.xxx_i ON xxx(b); + } { + DROP INDEX IF EXISTS yyy.xxx_i; + } + ]] + } + + # Execute the SQL transaction. + # + set rc [catch { execsql_blocking $::DB " + BEGIN; + $SQL(1); + $SQL(2); + $SQL(3); + COMMIT; + " + } msg] + + if {$rc && [string match "SQLITE_LOCKED*" $msg] + || [string match "SQLITE_SCHEMA*" $msg] + } { + # Hit an SQLITE_LOCKED error. Rollback the current transaction. + set rc [catch { execsql_blocking $::DB ROLLBACK } msg] + if {$rc && [string match "SQLITE_LOCKED*" $msg]} { + sqlite3_close $::DB + opendb + } + } elseif {$rc} { + # Hit some other kind of error. This is a malfunction. + error $msg + } else { + # No error occured. Check that any SELECT statements in the transaction + # returned "1". Otherwise, the invariant was false, indicating that + # some malfunction has occured. + foreach r $msg { if {$r != 1} { puts "Invariant check failed: $msg" } } + } + } + + # Close the database connection and return 0. + # + sqlite3_close $::DB + expr 0 +} + +foreach {iTest xStep xPrepare} { + 1 sqlite3_blocking_step sqlite3_blocking_prepare_v2 + 2 sqlite3_step sqlite3_nonblocking_prepare_v2 +} { + file delete -force test.db test2.db test3.db + + set ThreadSetup "set xStep $xStep;set xPrepare $xPrepare;set nSecond $nSecond" + + # Set up the database schema used by this test. Each thread opens file + # test.db as the main database, then attaches files test2.db and test3.db + # as auxillary databases. Each file contains a single table (t1, t2 and t3, in + # files test.db, test2.db and test3.db, respectively). + # + do_test notify2-$iTest.1.1 { + sqlite3 db test.db + execsql { + ATTACH 'test2.db' AS aux2; + ATTACH 'test3.db' AS aux3; + CREATE TABLE main.t1(a INTEGER PRIMARY KEY, b); + CREATE TABLE aux2.t2(a INTEGER PRIMARY KEY, b); + CREATE TABLE aux3.t3(a INTEGER PRIMARY KEY, b); + INSERT INTO t1 SELECT NULL, 0; + INSERT INTO t2 SELECT NULL, 0; + INSERT INTO t3 SELECT NULL, 0; + } + } {} + do_test notify2-$iTest.1.2 { + db close + } {} + + + # Launch $nThread threads. Then wait for them to finish. + # + puts "Running $xStep test for $nSecond seconds" + unset -nocomplain finished + for {set ii 0} {$ii < $nThread} {incr ii} { + thread_spawn finished($ii) $ThreadSetup $ThreadProgram + } + for {set ii 0} {$ii < $nThread} {incr ii} { + do_test notify2-$iTest.2.$ii { + if {![info exists finished($ii)]} { vwait finished($ii) } + set finished($ii) + } {0} + } + + # Count the total number of succesful writes. + do_test notify2-$iTest.3.1 { + sqlite3 db test.db + execsql { + ATTACH 'test2.db' AS aux2; + ATTACH 'test3.db' AS aux3; + } + set anWrite($xStep) [execsql { + SELECT (SELECT max(a) FROM t1) + + (SELECT max(a) FROM t2) + + (SELECT max(a) FROM t3) + }] + db close + } {} +} + +# The following tests checks to make sure sqlite3_blocking_step() is +# faster than sqlite3_step(). blocking_step() is always faster on +# multi-core and is usually faster on single-core. But sometimes, by +# chance, step() will be faster on a single core, in which case the +# following test will fail. +# +puts "The following test seeks to demonstrate that the sqlite3_unlock_notify()" +puts "interface helps multi-core systems to run faster. This test sometimes" +puts "fails on single-core machines." +puts [array get anWrite] +do_test notify2-3 { + expr {$anWrite(sqlite3_blocking_step) > $anWrite(sqlite3_step)} +} {1} + +sqlite3_enable_shared_cache $::enable_shared_cache +finish_test diff --git a/test/pcache.test b/test/pcache.test index ad024fc..56e10fd 100644 --- a/test/pcache.test +++ b/test/pcache.test @@ -11,7 +11,7 @@ # # This file is focused on testing the pcache module. # -# $Id: pcache.test,v 1.3 2009/01/07 15:33:46 drh Exp $ +# $Id: pcache.test,v 1.4 2009/03/05 14:59:40 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -30,11 +30,11 @@ do_test pcache-1.1 { do_test pcache-1.2 { sqlite3 db test.db execsql { - PRAGMA cache_size=10; + PRAGMA cache_size=12; PRAGMA auto_vacuum=0; } pcache_stats -} {current 1 max 10 min 10 recyclable 1} +} {current 1 max 12 min 10 recyclable 1} do_test pcache-1.3 { execsql { @@ -46,7 +46,7 @@ do_test pcache-1.3 { CREATE TABLE t5(a, b, c); } pcache_stats -} {current 6 max 10 min 10 recyclable 0} +} {current 6 max 12 min 10 recyclable 0} do_test pcache-1.4 { execsql { @@ -56,13 +56,13 @@ do_test pcache-1.4 { CREATE TABLE t9(a, b, c); } pcache_stats -} {current 10 max 10 min 10 recyclable 0} +} {current 10 max 12 min 10 recyclable 0} do_test pcache-1.5 { sqlite3 db2 test.db execsql "PRAGMA cache_size=10" db2 pcache_stats -} {current 11 max 20 min 20 recyclable 1} +} {current 11 max 22 min 20 recyclable 1} do_test pcache-1.6.1 { execsql { @@ -70,7 +70,7 @@ do_test pcache-1.6.1 { SELECT * FROM sqlite_master; } db2 pcache_stats -} {current 11 max 20 min 20 recyclable 0} +} {current 11 max 22 min 20 recyclable 0} # At this point connection db2 has a read lock on the database file and a # single pinned page in its cache. Connection [db] is holding 10 dirty @@ -87,16 +87,18 @@ do_test pcache-1.6.2 { CREATE INDEX i7 ON t7(a, b); CREATE INDEX i8 ON t8(a, b); CREATE INDEX i9 ON t9(a, b); + CREATE INDEX i10 ON t9(a, b); + CREATE INDEX i11 ON t9(a, b); } pcache_stats -} {current 20 max 20 min 20 recyclable 0} +} {current 23 max 22 min 20 recyclable 0} do_test pcache-1.7 { execsql { CREATE TABLE t10(a, b, c); } pcache_stats -} {current 21 max 20 min 20 recyclable 0} +} {current 24 max 22 min 20 recyclable 0} # Rolling back the transaction held by db2 at this point releases a pinned # page. Because the number of allocated pages is greater than the @@ -106,22 +108,22 @@ do_test pcache-1.7 { do_test pcache-1.8 { execsql {ROLLBACK} db2 pcache_stats -} {current 20 max 20 min 20 recyclable 0} +} {current 23 max 22 min 20 recyclable 0} do_test pcache-1.9 { execsql COMMIT pcache_stats -} {current 20 max 20 min 20 recyclable 20} +} {current 22 max 22 min 20 recyclable 22} do_test pcache-1.10 { db2 close pcache_stats -} {current 10 max 10 min 10 recyclable 10} +} {current 12 max 12 min 10 recyclable 12} do_test pcache-1.11 { execsql { PRAGMA cache_size = 20 } pcache_stats -} {current 10 max 20 min 10 recyclable 10} +} {current 12 max 20 min 10 recyclable 12} do_test pcache-1.12 { execsql { diff --git a/test/permutations.test b/test/permutations.test index ec815ce..678be96 100644 --- a/test/permutations.test +++ b/test/permutations.test @@ -9,7 +9,7 @@ # #*********************************************************************** # -# $Id: permutations.test,v 1.45 2009/01/10 18:51:40 danielk1977 Exp $ +# $Id: permutations.test,v 1.49 2009/04/10 18:32:29 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -39,7 +39,8 @@ if {$::perm::testmode eq "all" || $::perm::testmode eq ""} { set ::perm::testmode { memsubsys1 memsubsys2 singlethread multithread onefile utf16 exclusive persistent_journal persistent_journal_error no_journal no_journal_error - autovacuum_ioerr no_mutex_try fullmutex journaltest + autovacuum_ioerr no_mutex_try fullmutex journaltest inmemory_journal + pcache0 pcache10 pcache50 pcache90 pcache100 } } if {$::perm::testmode eq "targets"} { @@ -179,12 +180,14 @@ run_tests "memsubsys1" -description { sqlite3_config_pagecache 4096 24 sqlite3_config_scratch 25000 1 sqlite3_initialize + autoinstall_test_functions } -shutdown { catch {db close} sqlite3_shutdown sqlite3_config_pagecache 0 0 sqlite3_config_scratch 0 0 sqlite3_initialize + autoinstall_test_functions } # Run some tests using pre-allocated page and scratch blocks. This time @@ -205,12 +208,14 @@ run_tests "memsubsys2" -description { sqlite3_config_pagecache 512 5 sqlite3_config_scratch 1000 1 sqlite3_initialize + autoinstall_test_functions } -shutdown { catch {db close} sqlite3_shutdown sqlite3_config_pagecache 0 0 sqlite3_config_scratch 0 0 sqlite3_initialize + autoinstall_test_functions } # Run all tests with the lookaside allocator disabled. @@ -222,11 +227,13 @@ run_tests "nolookaside" -description { sqlite3_shutdown sqlite3_config_lookaside 0 0 sqlite3_initialize + autoinstall_test_functions } -shutdown { catch {db close} sqlite3_shutdown sqlite3_config_lookaside 100 500 sqlite3_initialize + autoinstall_test_functions } # Run some tests in SQLITE_CONFIG_SINGLETHREAD mode. @@ -237,6 +244,8 @@ run_tests "singlethread" -description { catch {db close} sqlite3_shutdown catch {sqlite3_config singlethread} + sqlite3_initialize + autoinstall_test_functions } -include { delete.test delete2.test insert.test rollback.test select1.test select2.test trans.test update.test vacuum.test types.test @@ -245,6 +254,8 @@ run_tests "singlethread" -description { catch {db close} sqlite3_shutdown catch {sqlite3_config serialized} + sqlite3_initialize + autoinstall_test_functions } run_tests "nomutex" -description { @@ -274,6 +285,8 @@ run_tests "multithread" -description { catch {db close} sqlite3_shutdown catch {sqlite3_config multithread} + sqlite3_initialize + autoinstall_test_functions } -include { delete.test delete2.test insert.test rollback.test select1.test select2.test trans.test update.test vacuum.test types.test @@ -282,6 +295,8 @@ run_tests "multithread" -description { catch {db close} sqlite3_shutdown catch {sqlite3_config serialized} + sqlite3_initialize + autoinstall_test_functions } # Run some tests in SQLITE_OPEN_FULLMUTEX mode. @@ -451,12 +466,12 @@ run_tests "inmemory_journal" -description { # Exclude all tests that simulate IO errors. autovacuum_ioerr2.test incrvacuum_ioerr.test ioerr.test ioerr.test ioerr2.test ioerr3.test ioerr4.test ioerr5.test - vacuum3.test incrblob_err.test diskfull.test + vacuum3.test incrblob_err.test diskfull.test backup_ioerr.test # Exclude test scripts that use tcl IO to access journal files or count # the number of fsync() calls. pager.test exclusive.test jrnlmode.test sync.test misc1.test - journal1.test conflict.test + journal1.test conflict.test crash8.test tkt3457.test io.test } ifcapable mem3 { @@ -480,12 +495,12 @@ ifcapable mem3 { autoinstall_test_functions } -shutdown { catch {db close} - sqlite3_reset_auto_extension sqlite3_shutdown sqlite3_config_heap 0 0 sqlite3_config_lookaside 100 500 install_malloc_faultsim 1 sqlite3_initialize + autoinstall_test_functions } } @@ -501,7 +516,6 @@ ifcapable mem5 { collate5.test limit.test zeroblob.test } -initialize { catch {db close} - sqlite3_reset_auto_extension sqlite3_shutdown sqlite3_config_heap 25000000 64 sqlite3_config_lookaside 0 0 @@ -510,12 +524,12 @@ ifcapable mem5 { autoinstall_test_functions } -shutdown { catch {db close} - sqlite3_reset_auto_extension sqlite3_shutdown sqlite3_config_heap 0 0 sqlite3_config_lookaside 100 500 install_malloc_faultsim 1 sqlite3_initialize + autoinstall_test_functions } run_tests "memsys5-2" -description { @@ -524,7 +538,6 @@ ifcapable mem5 { select1.test } -initialize { catch {db close} - sqlite3_reset_auto_extension sqlite3_shutdown sqlite3_config_heap 40000000 16 sqlite3_config_lookaside 0 0 @@ -533,12 +546,12 @@ ifcapable mem5 { autoinstall_test_functions } -shutdown { catch {db close} - sqlite3_reset_auto_extension sqlite3_shutdown sqlite3_config_heap 0 0 sqlite3_config_lookaside 100 500 install_malloc_faultsim 1 sqlite3_initialize + autoinstall_test_functions } } @@ -551,10 +564,14 @@ ifcapable threadsafe { sqlite3_shutdown install_mutex_counters 1 set ::disable_mutex_try 1 + sqlite3_initialize + autoinstall_test_functions } -shutdown { catch {db close} sqlite3_shutdown install_mutex_counters 0 + sqlite3_initialize + autoinstall_test_functions } } @@ -615,87 +632,82 @@ run_tests "pcache0" -description { Alternative pcache implementation without random discard } -initialize { catch {db close} - sqlite3_reset_auto_extension sqlite3_shutdown sqlite3_config_alt_pcache 1 0 1 sqlite3_initialize autoinstall_test_functions } -shutdown { catch {db close} - sqlite3_reset_auto_extension sqlite3_shutdown sqlite3_config_alt_pcache 0 0 0 sqlite3_config_lookaside 100 500 install_malloc_faultsim 1 sqlite3_initialize + autoinstall_test_functions } -include ${perm-alt-pcache-testset} run_tests "pcache10" -description { Alternative pcache implementation without 10% random discard } -initialize { catch {db close} - sqlite3_reset_auto_extension sqlite3_shutdown sqlite3_config_alt_pcache 1 50 1 sqlite3_initialize autoinstall_test_functions } -shutdown { catch {db close} - sqlite3_reset_auto_extension sqlite3_shutdown sqlite3_config_alt_pcache 0 0 0 sqlite3_initialize + autoinstall_test_functions } -include ${perm-alt-pcache-testset} run_tests "pcache50" -description { Alternative pcache implementation without 50% random discard } -initialize { catch {db close} - sqlite3_reset_auto_extension sqlite3_shutdown sqlite3_config_alt_pcache 1 50 1 sqlite3_initialize autoinstall_test_functions } -shutdown { catch {db close} - sqlite3_reset_auto_extension sqlite3_shutdown sqlite3_config_alt_pcache 0 0 0 sqlite3_initialize + autoinstall_test_functions } -include ${perm-alt-pcache-testset} run_tests "pcache90" -description { Alternative pcache implementation without 90% random discard } -initialize { catch {db close} - sqlite3_reset_auto_extension sqlite3_shutdown sqlite3_config_alt_pcache 1 50 1 sqlite3_initialize autoinstall_test_functions } -shutdown { catch {db close} - sqlite3_reset_auto_extension sqlite3_shutdown sqlite3_config_alt_pcache 0 0 0 sqlite3_initialize + autoinstall_test_functions } -include ${perm-alt-pcache-testset} run_tests "pcache100" -description { Alternative pcache implementation that always discards when unpinning } -initialize { catch {db close} - sqlite3_reset_auto_extension sqlite3_shutdown sqlite3_config_alt_pcache 1 100 1 sqlite3_initialize autoinstall_test_functions } -shutdown { catch {db close} - sqlite3_reset_auto_extension sqlite3_shutdown sqlite3_config_alt_pcache 0 0 0 sqlite3_initialize + autoinstall_test_functions } -include ${perm-alt-pcache-testset} run_tests "journaltest" -description { diff --git a/test/quick.test b/test/quick.test index c27f1d2..8783a10 100644 --- a/test/quick.test +++ b/test/quick.test @@ -6,7 +6,7 @@ #*********************************************************************** # This file runs all tests. # -# $Id: quick.test,v 1.92 2009/02/03 16:51:25 danielk1977 Exp $ +# $Id: quick.test,v 1.95 2009/03/16 14:48:19 danielk1977 Exp $ proc lshift {lvar} { upvar $lvar l @@ -67,6 +67,7 @@ set EXCLUDE { misc7.test misuse.test mutex2.test + notify2.test onefile.test permutations.test quick.test @@ -85,6 +86,8 @@ set EXCLUDE { thread001.test thread002.test thread003.test + thread004.test + thread005.test trans2.test vacuum3.test @@ -109,17 +112,26 @@ if {[sqlite3 -has-codec]} { set INCLUDE { } -foreach testfile [lsort -dictionary [glob $testdir/*.test]] { - # If this is "veryquick.test", do not run any of the malloc or - # IO error simulations. - if {[info exists ISVERYQUICK] && ( - [string match *malloc* $testfile] || [string match *ioerr* $testfile] - ) } { - continue - } +# If the QUICKTEST_INCLUDE environment variable is set, then interpret +# it as a list of test files. Always run these files, even if they +# begin with "malloc*" or "ioerr*" or are part of the EXCLUDE list +# defined above. +# +set QUICKTEST_INCLUDE {} +catch { set QUICKTEST_INCLUDE $env(QUICKTEST_INCLUDE) } +foreach testfile [lsort -dictionary [glob $testdir/*.test]] { set tail [file tail $testfile] - if {[lsearch -exact $EXCLUDE $tail]>=0} continue + if { [lsearch $QUICKTEST_INCLUDE $tail]<0 } { + # If this is "veryquick.test", do not run any of the malloc or + # IO error simulations. + if {[info exists ISVERYQUICK] && ( + [string match *malloc* $testfile] || [string match *ioerr* $testfile] + ) } { + continue + } + if {[lsearch -exact $EXCLUDE $tail]>=0} continue + } if {[llength $INCLUDE]>0 && [lsearch -exact $INCLUDE $tail]<0} continue if {[info exists STARTAT] && [string match $STARTAT $tail]} {unset STARTAT} if {[info exists STARTAT]} continue diff --git a/test/select1.test b/test/select1.test index 59c3b58..2fa7bb4 100644 --- a/test/select1.test +++ b/test/select1.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the SELECT statement. # -# $Id: select1.test,v 1.65 2008/08/04 03:51:24 danielk1977 Exp $ +# $Id: select1.test,v 1.67 2009/04/10 15:38:43 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -1041,4 +1041,27 @@ do_test select1-14.2 { } } {0} +if {[db one {PRAGMA locking_mode}]=="normal"} { + # Check that ticket #3771 has been fixed. This test does not + # work with locking_mode=EXCLUSIVE so disable in that case. + # + do_test select1-15.1 { + execsql { + CREATE TABLE t1(a); + CREATE INDEX i1 ON t1(a); + INSERT INTO t1 VALUES(1); + INSERT INTO t1 VALUES(2); + INSERT INTO t1 VALUES(3); + } + } {} + do_test select1-15.2 { + sqlite3 db2 test.db + execsql { DROP INDEX i1 } db2 + db2 close + } {} + do_test select1-15.3 { + execsql { SELECT 2 IN (SELECT a FROM t1) } + } {1} +} + finish_test diff --git a/test/selectB.test b/test/selectB.test index 9f5261e..3fdf85c 100644 --- a/test/selectB.test +++ b/test/selectB.test @@ -10,7 +10,7 @@ #*********************************************************************** # This file implements regression tests for SQLite library. # -# $Id: selectB.test,v 1.9 2008/08/04 03:51:24 danielk1977 Exp $ +# $Id: selectB.test,v 1.10 2009/04/02 16:59:47 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -122,7 +122,7 @@ for {set ii 1} {$ii <= 2} {incr ii} { } { SELECT a FROM t1 UNION ALL SELECT d FROM t2 ORDER BY 1 LIMIT 2 OFFSET 3 } {12 14} - + test_transform selectB-$ii.9 { SELECT * FROM ( SELECT a FROM t1 UNION ALL SELECT d FROM t2 UNION ALL SELECT c FROM t1 @@ -156,33 +156,35 @@ for {set ii 1} {$ii <= 2} {incr ii} { SELECT a FROM t1 UNION ALL SELECT d FROM t2 LIMIT 2 } {2 8} - test_transform selectB-$ii.13 { - SELECT * FROM (SELECT a FROM t1 UNION ALL SELECT d FROM t2 ORDER BY a ASC) - } { - SELECT a FROM t1 UNION ALL SELECT d FROM t2 ORDER BY 1 ASC - } {2 3 8 12 14 21} - - test_transform selectB-$ii.14 { - SELECT * FROM (SELECT a FROM t1 UNION ALL SELECT d FROM t2 ORDER BY a DESC) - } { - SELECT a FROM t1 UNION ALL SELECT d FROM t2 ORDER BY 1 DESC - } {21 14 12 8 3 2} - - test_transform selectB-$ii.14 { - SELECT * FROM ( - SELECT a FROM t1 UNION ALL SELECT d FROM t2 ORDER BY a DESC - ) LIMIT 2 OFFSET 2 - } { - SELECT a FROM t1 UNION ALL SELECT d FROM t2 ORDER BY 1 DESC LIMIT 2 OFFSET 2 - } {12 8} - - test_transform selectB-$ii.15 { - SELECT * FROM ( - SELECT a, b FROM t1 UNION ALL SELECT d, e FROM t2 ORDER BY a ASC, e DESC - ) - } { - SELECT a, b FROM t1 UNION ALL SELECT d, e FROM t2 ORDER BY a ASC, e DESC - } {2 4 3 6 8 10 12 15 14 16 21 24} + # An ORDER BY in a compound subqueries defeats flattening. Ticket #3773 + # test_transform selectB-$ii.13 { + # SELECT * FROM (SELECT a FROM t1 UNION ALL SELECT d FROM t2 ORDER BY a ASC) + # } { + # SELECT a FROM t1 UNION ALL SELECT d FROM t2 ORDER BY 1 ASC + # } {2 3 8 12 14 21} + # + # test_transform selectB-$ii.14 { + # SELECT * FROM (SELECT a FROM t1 UNION ALL SELECT d FROM t2 ORDER BY a DESC) + # } { + # SELECT a FROM t1 UNION ALL SELECT d FROM t2 ORDER BY 1 DESC + # } {21 14 12 8 3 2} + # + # test_transform selectB-$ii.14 { + # SELECT * FROM ( + # SELECT a FROM t1 UNION ALL SELECT d FROM t2 ORDER BY a DESC + # ) LIMIT 2 OFFSET 2 + # } { + # SELECT a FROM t1 UNION ALL SELECT d FROM t2 ORDER BY 1 DESC + # LIMIT 2 OFFSET 2 + # } {12 8} + # + # test_transform selectB-$ii.15 { + # SELECT * FROM ( + # SELECT a, b FROM t1 UNION ALL SELECT d, e FROM t2 ORDER BY a ASC, e DESC + # ) + # } { + # SELECT a, b FROM t1 UNION ALL SELECT d, e FROM t2 ORDER BY a ASC, e DESC + # } {2 4 3 6 8 10 12 15 14 16 21 24} } do_test selectB-3.0 { diff --git a/test/selectC.test b/test/selectC.test index d35ca56..e92b83b 100644 --- a/test/selectC.test +++ b/test/selectC.test @@ -10,7 +10,7 @@ #*********************************************************************** # This file implements regression tests for SQLite library. # -# $Id: selectC.test,v 1.3 2008/09/17 00:13:12 drh Exp $ +# $Id: selectC.test,v 1.4 2009/03/02 01:22:40 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -102,14 +102,23 @@ do_test selectC-1.11 { GROUP BY x, y } } {1 aaabbb} -do_test selectC-1.12 { +proc longname_toupper x {return [string toupper $x]} +db function uppercaseconversionfunctionwithaverylongname longname_toupper +do_test selectC-1.12.1 { execsql { SELECT DISTINCT upper(b) AS x FROM t1 ORDER BY x } } {AAA CCC} -do_test selectC-1.13 { +do_test selectC-1.12.2 { + execsql { + SELECT DISTINCT uppercaseconversionfunctionwithaverylongname(b) AS x + FROM t1 + ORDER BY x + } +} {AAA CCC} +do_test selectC-1.13.1 { execsql { SELECT upper(b) AS x FROM t1 @@ -117,12 +126,27 @@ do_test selectC-1.13 { ORDER BY x } } {AAA CCC} -do_test selectC-1.14 { +do_test selectC-1.13.2 { + execsql { + SELECT uppercaseconversionfunctionwithaverylongname(b) AS x + FROM t1 + GROUP BY x + ORDER BY x + } +} {AAA CCC} +do_test selectC-1.14.1 { execsql { SELECT upper(b) AS x FROM t1 ORDER BY x DESC } } {CCC AAA AAA} +do_test selectC-1.14.2 { + execsql { + SELECT uppercaseconversionfunctionwithaverylongname(b) AS x + FROM t1 + ORDER BY x DESC + } +} {CCC AAA AAA} finish_test diff --git a/test/shared.test b/test/shared.test index d57f54e..9b04946 100644 --- a/test/shared.test +++ b/test/shared.test @@ -9,7 +9,7 @@ # #*********************************************************************** # -# $Id: shared.test,v 1.35 2008/11/21 00:10:35 aswift Exp $ +# $Id: shared.test,v 1.36 2009/03/16 13:19:36 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -875,7 +875,7 @@ do_test shared-$av.11.4 { } {0 {}} do_test shared-$av.11.5 { catchsql {INSERT INTO abc2 VALUES(1, 2, 3);} db2 -} {1 {database is locked}} +} {1 {database table is locked}} do_test shared-$av.11.6 { catchsql {SELECT * FROM abc2} } {0 {}} diff --git a/test/shared2.test b/test/shared2.test index 5f18f8b..9150e34 100644 --- a/test/shared2.test +++ b/test/shared2.test @@ -9,7 +9,7 @@ # #*********************************************************************** # -# $Id: shared2.test,v 1.5 2007/08/23 02:47:54 drh Exp $ +# $Id: shared2.test,v 1.7 2009/03/18 18:43:36 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -127,5 +127,45 @@ do_test shared2-3.2 { sqlite3_enable_shared_cache 1 } {1} +file delete -force test.db + +sqlite3 db test.db +do_test shared2-4.1 { + execsql { + CREATE TABLE t0(a, b); + CREATE TABLE t1(a, b DEFAULT 'hello world'); + } +} {} +db close + +sqlite3 db test.db +sqlite3 db2 test.db + +do_test shared2-4.2 { + execsql { SELECT a, b FROM t0 } db + execsql { INSERT INTO t1(a) VALUES(1) } db2 +} {} + +do_test shared2-4.3 { + db2 close + db close +} {} + +# At one point, this was causing a crash. +# +do_test shared2-5.1 { + sqlite3 db test.db + sqlite3 db2 test.db + execsql { CREATE TABLE t2(a, b, c) } + + # The following statement would crash when attempting to sqlite3_free() + # a pointer allocated from a lookaside buffer. + execsql { CREATE INDEX i1 ON t2(a) } db2 +} {} + +db close +db2 close + sqlite3_enable_shared_cache $::enable_shared_cache finish_test + diff --git a/test/shared6.test b/test/shared6.test new file mode 100644 index 0000000..ef04aac --- /dev/null +++ b/test/shared6.test @@ -0,0 +1,256 @@ +# 2009 April 01 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# $Id: shared6.test,v 1.3 2009/04/02 18:28:08 danielk1977 Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +ifcapable !shared_cache { finish_test ; return } + +do_test shared6-1.1.1 { + execsql { + CREATE TABLE t1(a, b); + CREATE TABLE t2(c, d); + CREATE TABLE t3(e, f); + } + db close +} {} +do_test shared6-1.1.2 { + set ::enable_shared_cache [sqlite3_enable_shared_cache 1] + sqlite3_enable_shared_cache +} {1} + +do_test shared6-1.1.3 { + sqlite3 db1 test.db + sqlite3 db2 test.db +} {} + +# Exclusive shared-cache locks. Test the following: +# +# 1.2.1: If [db1] has an exclusive lock, [db2] cannot read. +# 1.2.2: If [db1] has an exclusive lock, [db1] can read. +# 1.2.3: If [db1] has a non-exclusive write-lock, [db2] can read. +# +do_test shared6-1.2.1 { + execsql { SELECT * FROM t1 } db2 ;# Cache a compiled statement + execsql { BEGIN EXCLUSIVE } db1 + catchsql { SELECT * FROM t1 } db2 ;# Execute the cached compiled statement +} {1 {database table is locked}} +do_test shared6-1.2.2 { + execsql { SELECT * FROM t1 } db1 +} {} +do_test shared6-1.2.3 { + execsql { + COMMIT; + BEGIN; + INSERT INTO t2 VALUES(3, 4); + } db1 + execsql { SELECT * FROM t1 } db2 +} {} +do_test shared6-1.2.X { + execsql { COMMIT } db1 +} {} + +# Regular shared-cache locks. Verify the following: +# +# 1.3.1: If [db1] has a write-lock on t1, [db1] can read from t1. +# 1.3.2: If [db1] has a write-lock on t1, [db2] can read from t2. +# 1.3.3: If [db1] has a write-lock on t1, [db2] cannot read from t1. +# 1.3.4: If [db1] has a write-lock on t1, [db2] cannot write to t1. +# 1.3.5: If [db1] has a read-lock on t1, [db2] can read from t1. +# 1.3.6: If [db1] has a read-lock on t1, [db2] cannot write to t1. +# +do_test shared6-1.3.1 { + execsql { + BEGIN; + INSERT INTO t1 VALUES(1, 2); + } db1 + execsql { SELECT * FROM t1 } db1 +} {1 2} +do_test shared6-1.3.2 { + execsql { SELECT * FROM t2 } db2 +} {3 4} +do_test shared6-1.3.3 { + catchsql { SELECT * FROM t1 } db2 +} {1 {database table is locked: t1}} +do_test shared6-1.3.4 { + catchsql { INSERT INTO t2 VALUES(1, 2) } db2 +} {1 {database table is locked}} +do_test shared6-1.3.5 { + execsql { + COMMIT; + BEGIN; + SELECT * FROM t1; + } db1 + execsql { SELECT * FROM t1 } db2 +} {1 2} +do_test shared6-1.3.5 { + catchsql { INSERT INTO t1 VALUES(5, 6) } db2 +} {1 {database table is locked: t1}} +do_test shared6-1.3.X { + execsql { COMMIT } db1 +} {} + +# Read-uncommitted mode. +# +# For these tests, connection [db2] is in read-uncommitted mode. +# +# 1.4.1: If [db1] has a write-lock on t1, [db2] can still read from t1. +# 1.4.2: If [db1] has a write-lock on the db schema (sqlite_master table), +# [db2] cannot read from the schema. +# 1.4.3: If [db1] has a read-lock on t1, [db2] cannot write to t1. +# +do_test shared6-1.4.1 { + execsql { PRAGMA read_uncommitted = 1 } db2 + execsql { + BEGIN; + INSERT INTO t1 VALUES(5, 6); + } db1 + execsql { SELECT * FROM t1 } db2 +} {1 2 5 6} +do_test shared6-1.4.2 { + execsql { CREATE TABLE t4(a, b) } db1 + catchsql { SELECT * FROM t1 } db2 +} {1 {database table is locked}} +do_test shared6-1.4.3 { + execsql { + COMMIT; + BEGIN; + SELECT * FROM t1; + } db1 + catchsql { INSERT INTO t1 VALUES(7, 8) } db2 +} {1 {database table is locked: t1}} + +do_test shared6-1.X { + db1 close + db2 close +} {} + +#------------------------------------------------------------------------- +# The following tests - shared6-2.* - test that two database connections +# that connect to the same file using different VFS implementations do +# not share a cache. +# +if {$::tcl_platform(platform) eq "unix"} { + do_test shared6-2.1 { + sqlite3 db1 test.db -vfs unix + sqlite3 db2 test.db -vfs unix + sqlite3 db3 test.db -vfs unix-none + sqlite3 db4 test.db -vfs unix-none + } {} + + do_test shared6-2.2 { + execsql { BEGIN; INSERT INTO t1 VALUES(9, 10); } db1 + catchsql { SELECT * FROM t1 } db2 + } {1 {database table is locked: t1}} + do_test shared6-2.3 { + execsql { SELECT * FROM t1 } db3 + } {1 2 5 6} + + do_test shared6-2.3 { + execsql { COMMIT } db1 + execsql { BEGIN; INSERT INTO t1 VALUES(11, 12); } db3 + catchsql { SELECT * FROM t1 } db4 + } {1 {database table is locked: t1}} + + do_test shared6-2.4 { + execsql { SELECT * FROM t1 } db1 + } {1 2 5 6 9 10} + + do_test shared6-2.5 { + execsql { COMMIT } db3 + } {} + + do_test shared6-2.X { + db1 close + db2 close + db3 close + db4 close + } {} +} + +#------------------------------------------------------------------------- +# Test that it is possible to open an exclusive transaction while +# already holding a read-lock on the database file. And that it is +# not possible if some other connection holds such a lock. +# +do_test shared6-3.1 { + sqlite3 db1 test.db + sqlite3 db2 test.db + sqlite3 db3 test.db +} {} +db1 eval {SELECT * FROM t1} { + # Within this block [db1] is holding a read-lock on t1. Test that + # this means t1 cannot be written by [db2]. + # + do_test shared6-3.2 { + catchsql { INSERT INTO t1 VALUES(1, 2) } db2 + } {1 {database table is locked: t1}} + + do_test shared6-3.3 { + execsql { BEGIN EXCLUSIVE } db1 + } {} + break +} +do_test shared6-3.4 { + catchsql { SELECT * FROM t1 } db2 +} {1 {database schema is locked: main}} +do_test shared6-3.5 { + execsql COMMIT db1 +} {} +db2 eval {SELECT * FROM t1} { + do_test shared6-3.6 { + catchsql { BEGIN EXCLUSIVE } db1 + } {1 {database table is locked}} + break +} +do_test shared6-3.7 { + execsql { BEGIN } db1 + execsql { BEGIN } db2 +} {} +db2 eval {SELECT * FROM t1} { + do_test shared6-3.8 { + catchsql { INSERT INTO t1 VALUES(1, 2) } db1 + } {1 {database table is locked: t1}} + break +} +do_test shared6-3.9 { + execsql { BEGIN ; ROLLBACK } db3 +} {} +do_test shared6-3.10 { + catchsql { SELECT * FROM t1 } db3 +} {1 {database table is locked}} +do_test shared6-3.X { + db1 close + db2 close + db3 close +} {} + +do_test shared6-4.1 { + #file delete -force test.db test.db-journal + sqlite3 db1 test.db + sqlite3 db2 test.db + + set ::STMT [sqlite3_prepare_v2 db1 "SELECT * FROM t1" -1 DUMMY] + execsql { CREATE TABLE t5(a, b) } db2 +} {} +do_test shared6-4.2 { + sqlite3_finalize $::STMT +} {SQLITE_OK} +do_test shared6-4.X { + + db1 close + db2 close +} {} + +sqlite3_enable_shared_cache $::enable_shared_cache +finish_test + diff --git a/test/speed1.test b/test/speed1.test index 9264fd6..86d13ba 100644 --- a/test/speed1.test +++ b/test/speed1.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this script is measuring executing speed. # -# $Id: speed1.test,v 1.10 2008/10/15 11:43:55 danielk1977 Exp $ +# $Id: speed1.test,v 1.11 2009/04/09 01:23:49 drh Exp $ # sqlite3_shutdown @@ -293,4 +293,6 @@ speed_trial_summary speed1 db close sqlite3_shutdown eval sqlite3_config_lookaside $old_lookaside +sqlite3_initialize +autoinstall_test_functions finish_test diff --git a/test/speed1p.test b/test/speed1p.test index 721bfca..915f165 100644 --- a/test/speed1p.test +++ b/test/speed1p.test @@ -13,7 +13,7 @@ # # This is a copy of speed1.test modified to user prepared statements. # -# $Id: speed1p.test,v 1.6 2008/10/15 11:43:55 danielk1977 Exp $ +# $Id: speed1p.test,v 1.7 2009/04/09 01:23:49 drh Exp $ # sqlite3_shutdown @@ -341,4 +341,6 @@ speed_trial_summary speed1 db close sqlite3_shutdown eval sqlite3_config_lookaside $old_lookaside +sqlite3_initialize +autoinstall_test_functions finish_test diff --git a/test/table.test b/test/table.test index 880a6cb..55508e9 100644 --- a/test/table.test +++ b/test/table.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the CREATE TABLE statement. # -# $Id: table.test,v 1.49 2009/01/16 11:04:58 danielk1977 Exp $ +# $Id: table.test,v 1.50 2009/03/14 08:37:24 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -399,6 +399,37 @@ do_test table-8.8 { } } {1 {no such table: no_such_table}} +do_test table-8.9 { + execsql { + CREATE TABLE t10("col.1" [char.3]); + CREATE TABLE t11 AS SELECT * FROM t10; + SELECT sql FROM sqlite_master WHERE name = 't11'; + } +} {{CREATE TABLE t11("col.1" "char.3")}} +do_test table-8.10 { + execsql { + CREATE TABLE t12( + a INTEGER, + b VARCHAR(10), + c VARCHAR(1,10), + d VARCHAR(+1,-10), + e VARCHAR (+1,-10), + f "VARCHAR (+1,-10, 5)", + g BIG INTEGER + ); + CREATE TABLE t13 AS SELECT * FROM t12; + SELECT sql FROM sqlite_master WHERE name = 't13'; + } +} {{CREATE TABLE t13( + a INTEGER, + b VARCHAR(10), + c VARCHAR(1,10), + d VARCHAR(+1,-10), + e VARCHAR (+1,-10), + f "VARCHAR (+1,-10, 5)", + g "BIG INTEGER" +)}} + # Make sure we cannot have duplicate column names within a table. # do_test table-9.1 { diff --git a/test/tclsqlite.test b/test/tclsqlite.test index a8ac08d..e752aa9 100644 --- a/test/tclsqlite.test +++ b/test/tclsqlite.test @@ -15,7 +15,7 @@ # interface is pretty well tested. This file contains some addition # tests for fringe issues that the main test suite does not cover. # -# $Id: tclsqlite.test,v 1.72 2009/02/04 22:46:47 drh Exp $ +# $Id: tclsqlite.test,v 1.73 2009/03/16 13:19:36 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -35,7 +35,7 @@ do_test tcl-1.1 { do_test tcl-1.2 { set v [catch {db bogus} msg] lappend v $msg -} {1 {bad option "bogus": must be authorizer, backup, busy, cache, changes, close, collate, collation_needed, commit_hook, complete, copy, enable_load_extension, errorcode, eval, exists, function, incrblob, interrupt, last_insert_rowid, nullvalue, onecolumn, profile, progress, rekey, restore, rollback_hook, status, timeout, total_changes, trace, transaction, update_hook, or version}} +} {1 {bad option "bogus": must be authorizer, backup, busy, cache, changes, close, collate, collation_needed, commit_hook, complete, copy, enable_load_extension, errorcode, eval, exists, function, incrblob, interrupt, last_insert_rowid, nullvalue, onecolumn, profile, progress, rekey, restore, rollback_hook, status, timeout, total_changes, trace, transaction, unlock_notify, update_hook, or version}} do_test tcl-1.2.1 { set v [catch {db cache bogus} msg] lappend v $msg diff --git a/test/temptrigger.test b/test/temptrigger.test new file mode 100644 index 0000000..a8d4e0c --- /dev/null +++ b/test/temptrigger.test @@ -0,0 +1,204 @@ +# 2009 February 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. +# +#*********************************************************************** +# +# $Id: temptrigger.test,v 1.2 2009/04/07 14:14:23 danielk1977 Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable !trigger { finish_test ; return } + +# Test cases: +# +# temptrigger-1.*: Shared cache problem. +# temptrigger-2.*: A similar shared cache problem. +# temptrigger-3.*: Attached database problem. +# + +#------------------------------------------------------------------------- +# Test case temptrigger-1.* demonstrates a problem with temp triggers +# in shared-cache mode. If process 1 connections to a shared-cache and +# creates a temp trigger, the temp trigger is linked into the shared-cache +# schema. If process 2 reloads the shared-cache schema from disk, then +# it does not recreate the temp trigger belonging to process 1. From the +# point of view of process 1, the temp trigger just disappeared. +# +# temptrigger-1.1: In shared cache mode, create a table in the main +# database and add a temp trigger to it. +# +# temptrigger-1.2: Check that the temp trigger is correctly fired. Check +# that the temp trigger is not fired by statements +# executed by a second connection connected to the +# same shared cache. +# +# temptrigger-1.3: Using the second connection to the shared-cache, cause +# the shared-cache schema to be reloaded. +# +# temptrigger-1.4: Check that the temp trigger is still fired correctly. +# +# temptrigger-1.5: Check that the temp trigger can be dropped without error. +# +db close +set ::enable_shared_cache [sqlite3_enable_shared_cache] +sqlite3_enable_shared_cache 1 + +sqlite3 db test.db +sqlite3 db2 test.db + +do_test temptrigger-1.1 { + execsql { + CREATE TABLE t1(a, b); + CREATE TEMP TABLE tt1(a, b); + CREATE TEMP TRIGGER tr1 AFTER INSERT ON t1 BEGIN + INSERT INTO tt1 VALUES(new.a, new.b); + END; + } +} {} + +do_test temptrigger-1.2.1 { + execsql { INSERT INTO t1 VALUES(1, 2) } + execsql { SELECT * FROM t1 } +} {1 2} +do_test temptrigger-1.2.2 { + execsql { SELECT * FROM tt1 } +} {1 2} +do_test temptrigger-1.2.3 { + execsql { INSERT INTO t1 VALUES(3, 4) } db2 + execsql { SELECT * FROM t1 } +} {1 2 3 4} +do_test temptrigger-1.2.4 { + execsql { SELECT * FROM tt1 } +} {1 2} + +# Cause the shared-cache schema to be reloaded. +# +do_test temptrigger-1.3 { + execsql { BEGIN; CREATE TABLE t3(a, b); ROLLBACK; } db2 +} {} + +do_test temptrigger-1.4 { + execsql { INSERT INTO t1 VALUES(5, 6) } + execsql { SELECT * FROM tt1 } +} {1 2 5 6} + +do_test temptrigger-1.5 { + # Before the bug was fixed, the following 'DROP TRIGGER' hit an + # assert if executed. + #execsql { DROP TRIGGER tr1 } +} {} + +catch {db close} +catch {db2 close} + +#------------------------------------------------------------------------- +# Tests temptrigger-2.* are similar to temptrigger-1.*, except that +# temptrigger-2.3 simply opens and closes a connection to the shared-cache. +# It does not do anything special to cause the schema to be reloaded. +# +do_test temptrigger-2.1 { + sqlite3 db test.db + execsql { + DELETE FROM t1; + CREATE TEMP TABLE tt1(a, b); + CREATE TEMP TRIGGER tr1 AFTER INSERT ON t1 BEGIN + INSERT INTO tt1 VALUES(new.a, new.b); + END; + } +} {} +do_test temptrigger-2.2 { + execsql { + INSERT INTO t1 VALUES(10, 20); + SELECT * FROM tt1; + } +} {10 20} +do_test temptrigger-2.3 { + sqlite3 db2 test.db + db2 close +} {} +do_test temptrigger-2.4 { + execsql { + INSERT INTO t1 VALUES(30, 40); + SELECT * FROM tt1; + } +} {10 20 30 40} +do_test temptrigger-2.5 { + #execsql { DROP TRIGGER tr1 } +} {} + +catch {db close} +catch {db2 close} +sqlite3_enable_shared_cache $::enable_shared_cache + +#------------------------------------------------------------------------- +# Test case temptrigger-3.* demonstrates a problem with temp triggers +# on tables located in attached databases. At one point when SQLite reloaded +# the schema of an attached database (because some other connection had +# changed the schema cookie) it was not re-creating temp triggers attached +# to tables located within the attached database. +# +# temptrigger-3.1: Attach database 'test2.db' to connection [db]. Add a +# temp trigger to a table in 'test2.db'. +# +# temptrigger-3.2: Check that the temp trigger is correctly fired. +# +# temptrigger-3.3: Update the schema of 'test2.db' using an external +# connection. This forces [db] to reload the 'test2.db' +# schema. Check that the temp trigger is still fired +# correctly. +# +# temptrigger-3.4: Check that the temp trigger can be dropped without error. +# +do_test temptrigger-3.1 { + catch { file delete -force test2.db test2.db-journal } + catch { file delete -force test.db test.db-journal } + sqlite3 db test.db + sqlite3 db2 test2.db + execsql { CREATE TABLE t2(a, b) } db2 + execsql { + ATTACH 'test2.db' AS aux; + CREATE TEMP TABLE tt2(a, b); + CREATE TEMP TRIGGER tr2 AFTER INSERT ON aux.t2 BEGIN + INSERT INTO tt2 VALUES(new.a, new.b); + END; + } +} {} + +do_test temptrigger-3.2.1 { + execsql { + INSERT INTO aux.t2 VALUES(1, 2); + SELECT * FROM aux.t2; + } +} {1 2} +do_test temptrigger-3.2.2 { + execsql { SELECT * FROM tt2 } +} {1 2} + +do_test temptrigger-3.3.1 { + execsql { CREATE TABLE t3(a, b) } db2 + execsql { + INSERT INTO aux.t2 VALUES(3, 4); + SELECT * FROM aux.t2; + } +} {1 2 3 4} +do_test temptrigger-3.3.2 { + execsql { SELECT * FROM tt2 } +} {1 2 3 4} + +do_test temptrigger-3.4 { + # Before the bug was fixed, the following 'DROP TRIGGER' hit an + # assert if executed. + #execsql { DROP TRIGGER tr2 } +} {} + +catch { db close } +catch { db2 close } + +finish_test diff --git a/test/tester.tcl b/test/tester.tcl index 44b6ae2..d8047a5 100644 --- a/test/tester.tcl +++ b/test/tester.tcl @@ -11,7 +11,7 @@ # This file implements some common TCL routines used for regression # testing the SQLite library # -# $Id: tester.tcl,v 1.139 2009/02/05 16:31:46 drh Exp $ +# $Id: tester.tcl,v 1.143 2009/04/09 01:23:49 drh Exp $ # # What for user input before continuing. This gives an opportunity @@ -132,6 +132,7 @@ if {![info exists nTest]} { sqlite3_shutdown install_malloc_faultsim 1 sqlite3_initialize + autoinstall_test_functions if {[info exists tester_do_binarylog]} { sqlite3_instvfs binarylog -default binarylog ostrace.bin sqlite3_instvfs marker binarylog "$argv0 $argv" @@ -290,6 +291,7 @@ proc finalize_testing {} { if {$nErr>0} { puts "Failures on these tests: $::failList" } + run_thread_tests 1 if {[llength $omitList]>0} { puts "Omitted test cases:" set prec {} @@ -643,7 +645,7 @@ proc do_ioerr_test {testname args} { set ::go 1 #reset_prng_state save_prng_state - for {set n $::ioerropts(-start)} {$::go && $n<200} {incr n} { + for {set n $::ioerropts(-start)} {$::go} {incr n} { set ::TN $n incr ::ioerropts(-count) -1 if {$::ioerropts(-count)<0} break @@ -895,16 +897,16 @@ proc memdebug_log_sql {{filename mallocs.sql}} { set database temp - set tbl "CREATE TABLE ${database}.malloc(nCall, nByte" - for {set ii 1} {$ii <= $nFrame} {incr ii} { - append tbl ", f${ii}" - } - append tbl ");\n" + set tbl "CREATE TABLE ${database}.malloc(zTest, nCall, nByte, lStack);" set sql "" foreach e $data { - append sql "INSERT INTO ${database}.malloc VALUES([join $e ,]);\n" - foreach f [lrange $e 2 end] { + set nCall [lindex $e 0] + set nByte [lindex $e 1] + set lStack [lrange $e 2 end] + append sql "INSERT INTO ${database}.malloc VALUES" + append sql "('test', $nCall, $nByte, '$lStack');\n" + foreach f $lStack { set frames($f) 1 } } @@ -959,3 +961,5 @@ proc copy_file {from to} { # If the library is compiled with the SQLITE_DEFAULT_AUTOVACUUM macro set # to non-zero, then set the global variable $AUTOVACUUM to 1. set AUTOVACUUM $sqlite_options(default_autovacuum) + +source $testdir/thread_common.tcl diff --git a/test/thread001.test b/test/thread001.test index 5236bbe..9b788aa 100644 --- a/test/thread001.test +++ b/test/thread001.test @@ -9,18 +9,12 @@ # #*********************************************************************** # -# $Id: thread001.test,v 1.9 2009/02/12 17:06:41 drh Exp $ +# $Id: thread001.test,v 1.10 2009/03/26 14:48:07 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl -ifcapable !mutex { - return -} -source $testdir/thread_common.tcl -if {[info commands sqlthread] eq ""} { - return -} +if {[run_thread_tests]==0} { finish_test ; return } set ::enable_shared_cache [sqlite3_enable_shared_cache] diff --git a/test/thread002.test b/test/thread002.test index 7c5e6b3..b1e5f63 100644 --- a/test/thread002.test +++ b/test/thread002.test @@ -12,20 +12,12 @@ # This test attempts to deadlock SQLite in shared-cache mode. # # -# $Id: thread002.test,v 1.6 2009/02/12 17:06:41 drh Exp $ +# $Id: thread002.test,v 1.9 2009/03/26 14:48:07 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl -source $testdir/thread_common.tcl -if {[info commands sqlthread] eq ""} { - finish_test - return -} -ifcapable !attach||!mutex { - finish_test - return -} +if {[run_thread_tests]==0} { finish_test ; return } db close set ::enable_shared_cache [sqlite3_enable_shared_cache 1] @@ -63,8 +55,8 @@ set thread_program { execsql { SELECT * FROM aux3.t1 } execsql { INSERT INTO aux3.t1(v) SELECT sum(v) FROM aux1.t1 } - execsql { CREATE TABLE aux1.t2(a,b) } - execsql { DROP TABLE aux1.t2 } + execsql { CREATE TABLE IF NOT EXISTS aux1.t2(a,b) } + execsql { DROP TABLE IF EXISTS aux1.t2 } # if {($ii%10)==0} {puts -nonewline . ; flush stdout} puts -nonewline . ; flush stdout @@ -88,7 +80,7 @@ for {set i 0} {$i < $::NTHREAD} {incr i} { if {![info exists finished($i)]} { vwait finished($i) } - do_test thread001.2.$i { + do_test thread002.2.$i { set ::finished($i) } OK } diff --git a/test/thread003.test b/test/thread003.test index 916fc7e..8aa513a 100644 --- a/test/thread003.test +++ b/test/thread003.test @@ -12,20 +12,12 @@ # This file contains tests that attempt to break the pcache module # by bombarding it with simultaneous requests from multiple threads. # -# $Id: thread003.test,v 1.6 2009/02/12 17:06:41 drh Exp $ +# $Id: thread003.test,v 1.8 2009/03/26 14:48:07 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl -source $testdir/thread_common.tcl -if {[info commands sqlthread] eq ""} { - finish_test - return -} -ifcapable !mutex { - finish_test - return -} +if {[run_thread_tests]==0} { finish_test ; return } # Set up a couple of different databases full of pseudo-randomly # generated data. @@ -158,7 +150,7 @@ do_test thread003.3 { # sqlite3_release_memory() over and over again. # set nSecond 30 -puts "Starting thread003.3 (should run for ~$nSecond seconds)" +puts "Starting thread003.4 (should run for ~$nSecond seconds)" unset -nocomplain finished(1) unset -nocomplain finished(2) do_test thread003.4 { diff --git a/test/thread004.test b/test/thread004.test new file mode 100644 index 0000000..66b9607 --- /dev/null +++ b/test/thread004.test @@ -0,0 +1,80 @@ +# 2009 February 26 +# +# 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. +# +#*********************************************************************** +# +# $Id: thread004.test,v 1.2 2009/03/26 14:48:07 danielk1977 Exp $ + +set testdir [file dirname $argv0] + +source $testdir/tester.tcl +if {[run_thread_tests]==0} { finish_test ; return } +ifcapable !shared_cache { + finish_test + return +} +if { [info commands sqlite3_table_column_metadata] eq "" } { + finish_test + return +} + +# Use shared-cache mode for this test. +# +db close +set ::enable_shared_cache [sqlite3_enable_shared_cache] +sqlite3_enable_shared_cache 1 + +# Create a table in database test.db +# +sqlite3 db test.db +do_test thread004-1.1 { + execsql { CREATE TABLE t1(a, b, c) } +} {} + +do_test thread004-1.2 { + + set ThreadOne { + set iStart [clock_seconds] + while {[clock_seconds]<$iStart+20} { + set ::DB [sqlite3_open test.db] + sqlite3_close $::DB + } + } + set ThreadTwo { + set ::DB [sqlite3_open test.db] + set iStart [clock_seconds] + set nErr 0 + while {[clock_seconds] <$iStart+20} { + incr nErr [catch {sqlite3_table_column_metadata $::DB main t1 a}] + } + sqlite3_close $::DB + set nErr + } + + # Run two threads. The first thread opens and closes database test.db + # repeatedly. Each time this happens, the in-memory schema used by + # all connections to test.db is discarded. + # + # The second thread calls sqlite3_table_column_metadata() over and + # over again. Each time it is called, the database schema is loaded + # if it is not already in memory. At one point this was crashing. + # + unset -nocomplain finished + thread_spawn finished(1) $thread_procs $ThreadOne + thread_spawn finished(2) $thread_procs $ThreadTwo + + foreach t {1 2} { + if {![info exists finished($t)]} { vwait finished($t) } + } + + set finished(2) +} {0} +sqlite3_enable_shared_cache $::enable_shared_cache +finish_test + diff --git a/test/thread005.test b/test/thread005.test new file mode 100644 index 0000000..9253287 --- /dev/null +++ b/test/thread005.test @@ -0,0 +1,238 @@ +# 2009 March 11 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# Test a race-condition that shows up in shared-cache mode. +# +# $Id: thread005.test,v 1.5 2009/03/26 14:48:07 danielk1977 Exp $ + +set testdir [file dirname $argv0] + +source $testdir/tester.tcl +if {[run_thread_tests]==0} { finish_test ; return } +ifcapable !shared_cache { + finish_test + return +} + +db close + +# Use shared-cache mode for these tests. +# +set ::enable_shared_cache [sqlite3_enable_shared_cache] +sqlite3_enable_shared_cache 1 + +#------------------------------------------------------------------------- +# This test attempts to hit the race condition fixed by commit [6363]. +# +proc runsql {zSql {db {}}} { + set rc SQLITE_OK + while {$rc=="SQLITE_OK" && $zSql ne ""} { + set STMT [sqlite3_prepare_v2 $db $zSql -1 zSql] + while {[set rc [sqlite3_step $STMT]] eq "SQLITE_ROW"} { } + set rc [sqlite3_finalize $STMT] + } + return $rc +} +do_test thread005-1.1 { + sqlite3 db test.db + db eval { CREATE TABLE t1(a, b) } + db close +} {} +for {set ii 2} {$ii < 500} {incr ii} { + unset -nocomplain finished + thread_spawn finished(0) {sqlite3_open test.db} + thread_spawn finished(1) {sqlite3_open test.db} + if {![info exists finished(0)]} { vwait finished(0) } + if {![info exists finished(1)]} { vwait finished(1) } + + do_test thread005-1.$ii { + runsql { BEGIN } $finished(0) + runsql { INSERT INTO t1 VALUES(1, 2) } $finished(0) + + # If the race-condition was hit, then $finished(0 and $finished(1) + # will not use the same pager cache. In this case the next statement + # can be executed succesfully. However, if the race-condition is not + # hit, then $finished(1) will be blocked by the write-lock held by + # $finished(0) on the shared-cache table t1 and the statement will + # return SQLITE_LOCKED. + # + runsql { SELECT * FROM t1 } $finished(1) + } {SQLITE_LOCKED} + + sqlite3_close $finished(0) + sqlite3_close $finished(1) +} + + +#------------------------------------------------------------------------- +# This test tries to exercise a race-condition that existed in shared-cache +# mode at one point. The test uses two threads; each has a database connection +# open on the same shared cache. The schema of the database is: +# +# CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE); +# +# One thread is a reader and the other thread a reader and a writer. The +# writer thread repeats the following transaction as fast as possible: +# +# BEGIN; +# DELETE FROM t1 WHERE a = (SELECT max(a) FROM t1); +# INSERT INTO t1 VALUES(NULL, NULL); +# UPDATE t1 SET b = a WHERE a = (SELECT max(a) FROM t1); +# SELECT count(*) FROM t1 WHERE b IS NULL; +# COMMIT; +# +# The reader thread does the following over and over as fast as possible: +# +# BEGIN; +# SELECT count(*) FROM t1 WHERE b IS NULL; +# COMMIT; +# +# The test runs for 20 seconds or until one of the "SELECT count(*)" +# statements returns a non-zero value. If an SQLITE_LOCKED error occurs, +# the connection issues a ROLLBACK immediately to abandon the current +# transaction. +# +# If everything is working correctly, the "SELECT count(*)" statements +# should never return a value other than 0. The "INSERT" statement +# executed by the writer adds a row with "b IS NULL" to the table, but +# the subsequent UPDATE statement sets its "b" value to an integer +# immediately afterwards. +# +# However, before the race-condition was fixed, if the reader's SELECT +# statement hit an error (say an SQLITE_LOCKED) at the same time as the +# writer was executing the UPDATE statement, then it could incorrectly +# rollback the statement-transaction belonging to the UPDATE statement. +# The UPDATE statement would still be reported as successful to the user, +# but it would have no effect on the database contents. +# +# Note that it has so far only proved possible to hit this race-condition +# when using an ATTACHed database. There doesn't seem to be any reason +# for this, other than that operating on an ATTACHed database means there +# are a few more mutex grabs and releases during the window of time open +# for the race-condition. Maybe this encourages the scheduler to context +# switch or something... +# + +file delete -force test.db test2.db +unset -nocomplain finished + +do_test thread005-2.1 { + sqlite3 db test.db + execsql { ATTACH 'test2.db' AS aux } + execsql { + CREATE TABLE aux.t1(a INTEGER PRIMARY KEY, b UNIQUE); + INSERT INTO t1 VALUES(1, 1); + INSERT INTO t1 VALUES(2, 2); + } + db close +} {} + + +set ThreadProgram { + proc execsql {zSql {db {}}} { + if {$db eq ""} {set db $::DB} + + set lRes [list] + set rc SQLITE_OK + + while {$rc=="SQLITE_OK" && $zSql ne ""} { + set STMT [sqlite3_prepare_v2 $db $zSql -1 zSql] + while {[set rc [sqlite3_step $STMT]] eq "SQLITE_ROW"} { + for {set i 0} {$i < [sqlite3_column_count $STMT]} {incr i} { + lappend lRes [sqlite3_column_text $STMT 0] + } + } + set rc [sqlite3_finalize $STMT] + } + + if {$rc != "SQLITE_OK"} { error "$rc [sqlite3_errmsg $db]" } + return $lRes + } + + if {$isWriter} { + set Sql { + BEGIN; + DELETE FROM t1 WHERE a = (SELECT max(a) FROM t1); + INSERT INTO t1 VALUES(NULL, NULL); + UPDATE t1 SET b = a WHERE a = (SELECT max(a) FROM t1); + SELECT count(*) FROM t1 WHERE b IS NULL; + COMMIT; + } + } else { + set Sql { + BEGIN; + SELECT count(*) FROM t1 WHERE b IS NULL; + COMMIT; + } + } + + set ::DB [sqlite3_open test.db] + + execsql { ATTACH 'test2.db' AS aux } + + set result "ok" + set finish [expr [clock_seconds]+5] + while {$result eq "ok" && [clock_seconds] < $finish} { + set rc [catch {execsql $Sql} msg] + if {$rc} { + if {[string match "SQLITE_LOCKED*" $msg]} { + catch { execsql ROLLBACK } + } else { + sqlite3_close $::DB + error $msg + } + } elseif {$msg ne "0"} { + set result "failed" + } + } + + sqlite3_close $::DB + set result +} + +# There is a race-condition in btree.c that means that if two threads +# attempt to open the same database at roughly the same time, and there +# does not already exist a shared-cache corresponding to that database, +# then two shared-caches can be created instead of one. Things still more +# or less work, but the two database connections do not use the same +# shared-cache. +# +# If the threads run by this test hit this race-condition, the tests +# fail (because SQLITE_BUSY may be unexpectedly returned instead of +# SQLITE_LOCKED). To prevent this from happening, open a couple of +# connections to test.db and test2.db now to make sure that there are +# already shared-caches in memory for all databases opened by the +# test threads. +# +sqlite3 db test.db +sqlite3 db test2.db + +puts "Running thread-tests for ~20 seconds" +thread_spawn finished(0) {set isWriter 0} $ThreadProgram +thread_spawn finished(1) {set isWriter 1} $ThreadProgram +if {![info exists finished(0)]} { vwait finished(0) } +if {![info exists finished(1)]} { vwait finished(1) } + +catch { db close } +catch { db2 close } + +do_test thread005-2.2 { + list $finished(0) $finished(1) +} {ok ok} + +do_test thread005-2.3 { + sqlite3 db test.db + execsql { ATTACH 'test2.db' AS aux } + execsql { SELECT count(*) FROM t1 WHERE b IS NULL } +} {0} + +sqlite3_enable_shared_cache $::enable_shared_cache +finish_test diff --git a/test/thread_common.tcl b/test/thread_common.tcl index 213e653..bbd9389 100644 --- a/test/thread_common.tcl +++ b/test/thread_common.tcl @@ -9,18 +9,10 @@ # #*********************************************************************** # -# $Id: thread_common.tcl,v 1.3 2009/01/19 17:40:12 drh Exp $ +# $Id: thread_common.tcl,v 1.5 2009/03/26 14:48:07 danielk1977 Exp $ -set testdir [file dirname $argv0] -source $testdir/tester.tcl - -if {[info commands sqlthread] eq ""} { - puts -nonewline "Skipping thread-safety tests - " - puts " not running a threadsafe sqlite/tcl build" - puts -nonewline "Both SQLITE_THREADSAFE and TCL_THREADS must be defined when" - puts " building testfixture" - finish_test - return +if {[info exists ::thread_procs]} { + return 0 } # The following script is sourced by every thread spawned using @@ -50,7 +42,7 @@ set thread_procs { } set rc [sqlite3_finalize $::STMT] } else { - if {[string first (6) $msg]} { + if {[lindex $msg 0]=="(6)"} { set rc SQLITE_LOCKED } else { set rc SQLITE_ERROR @@ -91,4 +83,31 @@ proc thread_spawn {varname args} { sqlthread spawn $varname [join $args ;] } +# Return true if this build can run the multi-threaded tests. +# +proc run_thread_tests {{print_warning 0}} { + ifcapable !mutex { + set zProblem "SQLite build is not threadsafe" + } + if {[info commands sqlthread] eq ""} { + set zProblem "SQLite build is not threadsafe" + } + if {![info exists ::tcl_platform(threaded)]} { + set zProblem "Linked against a non-threadsafe Tcl build" + } + if {[info exists zProblem]} { + if {$print_warning} { + if {[info exists ::run_thread_tests_failed]} { + puts "WARNING: Multi-threaded tests skipped: $zProblem" + } + } else { + puts "Skipping thread tests: $zProblem" + set ::run_thread_tests_failed 1 + } + return 0 + } + return 1; +} + return 0 + diff --git a/test/tkt2565.test b/test/tkt2565.test index dbf67e9..8df0419 100644 --- a/test/tkt2565.test +++ b/test/tkt2565.test @@ -15,7 +15,7 @@ # that contain headers with nRec==0 that are followed by additional # valid headers. # -# $Id: tkt2565.test,v 1.1 2009/01/08 15:24:02 drh Exp $ +# $Id: tkt2565.test,v 1.2 2009/04/09 01:23:49 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -86,5 +86,7 @@ do_test tkt2565-1.X { # sqlite3_shutdown sqlite3_config_alt_pcache 0 +sqlite3_initialize +autoinstall_test_functions finish_test diff --git a/test/tkt2767.test b/test/tkt2767.test index 6d642b4..29b5201 100644 --- a/test/tkt2767.test +++ b/test/tkt2767.test @@ -13,12 +13,14 @@ # Ticket #2767 is for a VDBE stack overflow on BEFORE # triggers that run RAISE(IGNORE). # -# $Id: tkt2767.test,v 1.2 2008/07/12 14:52:21 drh Exp $ +# $Id: tkt2767.test,v 1.3 2009/04/07 14:14:23 danielk1977 Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl +ifcapable !trigger { finish_test ; return } + do_test tkt2767-1.1 { execsql { -- Construct a table with many rows of data diff --git a/test/tkt2832.test b/test/tkt2832.test index 6a98a7b..f998699 100644 --- a/test/tkt2832.test +++ b/test/tkt2832.test @@ -11,12 +11,14 @@ # # This file is to test that ticket #2832 has been fixed. # -# $Id: tkt2832.test,v 1.4 2008/07/12 14:52:21 drh Exp $ +# $Id: tkt2832.test,v 1.5 2009/04/07 14:14:23 danielk1977 Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl +ifcapable !trigger { finish_test ; return } + do_test tkt2832-1.1 { execsql { CREATE TABLE t1(a PRIMARY KEY); diff --git a/test/tkt2854.test b/test/tkt2854.test index 7bff008..862524a 100644 --- a/test/tkt2854.test +++ b/test/tkt2854.test @@ -9,7 +9,7 @@ # #*********************************************************************** # -# $Id: tkt2854.test,v 1.3 2008/07/12 14:52:21 drh Exp $ +# $Id: tkt2854.test,v 1.4 2009/03/16 13:19:36 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -56,7 +56,7 @@ do_test tkt2854-1.2 { } {} do_test tkt2854-1.3 { catchsql { BEGIN EXCLUSIVE } db -} {1 {database is locked}} +} {1 {database table is locked}} do_test tkt2854-1.4 { execsql { SELECT * FROM abc } db3 } {} @@ -99,10 +99,10 @@ do_test tkt2854-1.11 { } {SQLITE_ERROR SQLITE_LOCKED} do_test tkt2854-1.12 { list [sqlite3_step $::STMT2] [sqlite3_finalize $::STMT2] -} {SQLITE_BUSY SQLITE_BUSY} +} {SQLITE_ERROR SQLITE_LOCKED} do_test tkt2854-1.13 { list [sqlite3_step $::STMT3] [sqlite3_finalize $::STMT3] -} {SQLITE_BUSY SQLITE_BUSY} +} {SQLITE_ERROR SQLITE_LOCKED} do_test tkt2854-1.14 { # A regular "BEGIN" doesn't touch any databases. So it succeeds. list [sqlite3_step $::STMT4] [sqlite3_finalize $::STMT4] @@ -136,7 +136,7 @@ do_test tkt2854-1.19 { do_test tkt2854-1.20 { execsql {BEGIN IMMEDIATE} db4 catchsql {BEGIN EXCLUSIVE} db -} {1 {database is locked}} +} {1 {database table is locked}} do_test tkt2854-1.21 { execsql {SELECT * FROM abc} db2 } {} diff --git a/test/tkt3298.test b/test/tkt3298.test index c94c466..0e546f7 100644 --- a/test/tkt3298.test +++ b/test/tkt3298.test @@ -12,11 +12,13 @@ # This file tests changes to the name resolution logic that occurred # in august of 2008 and where associated with tickets #3298 and #3301 # -# $Id: tkt3298.test,v 1.2 2008/08/22 12:46:46 drh Exp $ +# $Id: tkt3298.test,v 1.3 2009/04/07 14:14:23 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl +ifcapable !trigger { finish_test ; return } + do_test tkt3298-1.1 { execsql { CREATE TABLE t1(a INTEGER PRIMARY KEY, b INT); diff --git a/test/tkt3718.test b/test/tkt3718.test new file mode 100644 index 0000000..df8ef06 --- /dev/null +++ b/test/tkt3718.test @@ -0,0 +1,231 @@ +# 2001 September 15 +# +# 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 file is testing the execution of SQL statements from +# within callbacks generated by VMs that themselves open statement +# transactions. +# +# $Id: tkt3718.test,v 1.1 2009/03/18 10:33:02 danielk1977 Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test tkt3718-1.1 { + execsql { + CREATE TABLE t1(a PRIMARY KEY, b); + INSERT INTO t1 VALUES(1, 'one'); + INSERT INTO t1 VALUES(2, 'two'); + INSERT INTO t1 VALUES(3, 'three'); + INSERT INTO t1 VALUES(4, 'four'); + INSERT INTO t1 VALUES(5, 'five'); + CREATE TABLE t2(a PRIMARY KEY, b); + } +} {} + +# SQL scalar function: +# +# f1() +# +# Uses database handle [db] to execute "SELECT f2()". Returns either +# the results or error message from the "SELECT f2()" query to the +# caller. +# +proc f1 {args} { + set a [lindex $args 0] + catch { db eval {SELECT f2($a)} } msg + set msg +} + +# SQL scalar function: +# +# f2() +# +# Return the value of . Unless is "three", in which case throw +# an exception. +# +proc f2 {args} { + set a [lindex $args 0] + if {$a == "three"} { error "Three!!" } + return $a +} + +db func f1 f1 +db func f2 f2 + +# The second INSERT statement below uses the f1 user function such that +# half-way through the INSERT operation f1() will run an SQL statement +# that throws an exception. At one point, before #3718 was fixed, this +# caused the statement transaction belonging to the INSERT statement to +# be rolled back. The result was that some (but not all) of the rows that +# should have been inserted went missing. +# +do_test tkt3718-1.2 { + execsql { + BEGIN; + INSERT INTO t2 SELECT a, b FROM t1; + INSERT INTO t2 SELECT a+5, f1(b) FROM t1; + COMMIT; + } + execsql { + SELECT a FROM t2; + } +} {1 2 3 4 5 6 7 8 9 10} + +# This test turns on the count_changes pragma (causing DML statements to +# return SQLITE_ROW once, with a single integer result value reporting the +# number of rows affected by the statement). It then executes an INSERT +# statement that requires a statement journal. After stepping the statement +# once, so that it returns SQLITE_ROW, a second SQL statement that throws an +# exception is run. At one point, before #3718 was fixed, this caused the +# statement transaction belonging to the INSERT statement to be rolled back. +# The result was that none of the rows were actually inserted. +# +# +do_test tkt3718-1.3 { + execsql { + DELETE FROM t2 WHERE a > 5; + PRAGMA count_changes = 1; + BEGIN; + } + db eval {INSERT INTO t2 SELECT a+5, b||'+5' FROM t1} { + catch { db eval {SELECT f2('three')} } msg + } + execsql { + COMMIT; + SELECT a FROM t2; + } +} {1 2 3 4 5 6 7 8 9 10} + +do_test tkt3718-1.4 { + execsql {pragma count_changes=0} +} {} + +# This SQL function executes the SQL specified as an argument against +# database [db]. +# +proc sql {doit zSql} { + if {$doit} { catchsql $zSql } +} +db func sql [list sql] + +# The following tests, tkt3718-2.*, test that a nested statement +# transaction can be successfully committed or reverted without +# affecting the parent statement transaction. +# +do_test tkt3718-2.1 { + execsql { SELECT sql(1, 'DELETE FROM t2 WHERE a = '||a ) FROM t2 WHERE a>5 } + execsql { SELECT a from t2 } +} {1 2 3 4 5} +do_test tkt3718-2.2 { + execsql { + DELETE FROM t2 WHERE a > 5; + BEGIN; + INSERT INTO t2 SELECT a+5, sql(a==3, + 'INSERT INTO t2 SELECT a+10, f2(b) FROM t1' + ) FROM t1; + } + execsql { + COMMIT; + SELECT a FROM t2; + } +} {1 2 3 4 5 6 7 8 9 10} +do_test tkt3718-2.3 { + execsql { + DELETE FROM t2 WHERE a > 5; + BEGIN; + INSERT INTO t2 SELECT a+5, sql(a==3, + 'INSERT INTO t2 SELECT a+10, b FROM t1' + ) FROM t1; + COMMIT; + } + execsql { SELECT a FROM t2 ORDER BY a+0} +} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15} +integrity_check tkt3718.2-4 + +# The next set of tests, tkt3718-3.*, test that a statement transaction +# that has a committed statement transaction nested inside of it can +# be committed or reverted. +# +foreach {tn io ii results} { + 1 0 10 {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20} + 2 1 10 {6 7 8 9 10 16 17 18 19 20} + 3 0 11 {1 2 3 4 5 6 7 8 9 10 16 17 18 19 20} + 4 1 11 {6 7 8 9 10 16 17 18 19 20} +} { + do_test tkt3718-3.$tn { + execsql { + DELETE FROM t2; + INSERT INTO t2 SELECT a+5, b FROM t1; + INSERT INTO t2 SELECT a+15, b FROM t1; + } + + catchsql " + BEGIN; + INSERT INTO t2 SELECT a+$io, sql(a==3, + 'INSERT INTO t2 SELECT a+$ii, b FROM t1' + ) FROM t1; + " + + execsql { COMMIT } + + execsql { SELECT a FROM t2 ORDER BY a+0} + } $results + + integrity_check tkt3718-3.$tn.integrity +} + +# This is the same test as tkt3718-3.*, but with 3 levels of nesting. +# +foreach {tn i1 i2 i3 results} { + 1 0 10 20 {5 10 15 20 25 30} + 2 0 10 21 {5 10 15 20 30} + 3 0 11 20 {5 10 20 30} + 4 0 11 21 {5 10 20 30} + 5 1 10 20 {10 20 30} + 6 1 10 21 {10 20 30} + 7 1 11 20 {10 20 30} + 8 1 11 21 {10 20 30} +} { + do_test tkt3718-4.$tn { + execsql { + DELETE FROM t2; + INSERT INTO t2 SELECT a+5, b FROM t1; + INSERT INTO t2 SELECT a+15, b FROM t1; + INSERT INTO t2 SELECT a+25, b FROM t1; + } + + catchsql " + BEGIN; + INSERT INTO t2 SELECT a+$i1, sql(a==3, + 'INSERT INTO t2 SELECT a+$i2, sql(a==3, + ''INSERT INTO t2 SELECT a+$i3, b FROM t1'' + ) FROM t1' + ) FROM t1; + " + + execsql { COMMIT } + + execsql { SELECT a FROM t2 WHERE (a%5)==0 ORDER BY a+0} + } $results + + do_test tkt3718-4.$tn.extra { + execsql { + SELECT + (SELECT sum(a) FROM t2)==(SELECT sum(a*5-10) FROM t2 WHERE (a%5)==0) + } + } {1} + + integrity_check tkt3718-4.$tn.integrity +} + + +finish_test + diff --git a/test/tkt3731.test b/test/tkt3731.test new file mode 100644 index 0000000..0b80b46 --- /dev/null +++ b/test/tkt3731.test @@ -0,0 +1,51 @@ +# 2009 March 18 +# +# 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. +# +#*********************************************************************** +# +# $Id: tkt3731.test,v 1.1 2009/03/17 22:33:01 drh Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +ifcapable {!trigger} { + finish_test + return +} + +do_test tkt3731-1.1 { + execsql { + CREATE TABLE t1(a PRIMARY KEY, b); + CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN + INSERT INTO t1 VALUES(new.a || '+', new.b || '+'); + END; + } +} {} + +do_test tkt3731-1.2 { + execsql { + INSERT INTO t1 VALUES('a', 'b'); + INSERT INTO t1 VALUES('c', 'd'); + SELECT * FROM t1; + } +} {a b a+ b+ c d c+ d+} + +do_test tkt3731-1.3 { + execsql { + DELETE FROM t1; + CREATE TABLE t2(a, b); + INSERT INTO t2 VALUES('e', 'f'); + INSERT INTO t2 VALUES('g', 'h'); + INSERT INTO t1 SELECT * FROM t2; + SELECT * FROM t1; + } +} {e f e+ f+ g h g+ h+} + +integrity_check tkt3731-1.4 + +finish_test diff --git a/test/tkt3757.test b/test/tkt3757.test new file mode 100644 index 0000000..14bfb23 --- /dev/null +++ b/test/tkt3757.test @@ -0,0 +1,60 @@ +# 2009 March 28 +# +# 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. +# +#*********************************************************************** +# +# Ticket #3757: The cost functions on the query optimizer for the +# IN operator can be improved. +# +# $Id: tkt3757.test,v 1.1 2009/03/29 00:13:04 drh Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# Evaluate SQL. Return the result set followed by the +# and the number of full-scan steps. +# +proc count_steps {sql} { + set r [db eval $sql] + lappend r scan [db status step] sort [db status sort] +} + +# Construct tables +# +do_test tkt3757-1.1 { + db eval { + CREATE TABLE t1(x INTEGER, y INTEGER, z TEXT); + CREATE INDEX t1i1 ON t1(y,z); + INSERT INTO t1 VALUES(1,2,'three'); + CREATE TABLE t2(a INTEGER, b TEXT); + INSERT INTO t2 VALUES(2, 'two'); + ANALYZE; + SELECT * FROM sqlite_stat1; + } +} {t1 t1i1 {1 1 1}} + +# Modify statistics in order to make the optimizer then that: +# +# (1) Table T1 has about 250K entries +# (2) There are only about 5 distinct values of T1. +# +# Then run a query with "t1.y IN (SELECT ..)" in the WHERE clause. +# Make sure the index is used. +# +do_test tkt3757-1.2 { + db eval { + DELETE FROM sqlite_stat1; + INSERT INTO sqlite_stat1 VALUES('t1','t1i1','250000 50000 30'); + } + count_steps { + SELECT * FROM t1 WHERE y IN (SELECT a FROM t2) + } +} {1 2 three scan 0 sort 0} + +finish_test diff --git a/test/tkt3761.test b/test/tkt3761.test new file mode 100644 index 0000000..2601b15 --- /dev/null +++ b/test/tkt3761.test @@ -0,0 +1,40 @@ +# 2009 March 30 +# +# 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. +# +#*********************************************************************** +# +# Ticket #3761: Make sure that an incremental vacuum on an in-memory +# database can be rolled back. +# +# $Id: tkt3761.test,v 1.1 2009/03/31 02:54:40 drh Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test tkt3761-1.1 { + db close + sqlite3 db :memory: + db eval { + PRAGMA auto_vacuum=INCREMENTAL; + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(zeroblob(900)); + INSERT INTO t1 VALUES(zeroblob(900)); + INSERT INTO t1 SELECT x FROM t1; + INSERT INTO t1 SELECT x FROM t1; + INSERT INTO t1 SELECT x FROM t1; + INSERT INTO t1 SELECT x FROM t1; + BEGIN; + DELETE FROM t1 WHERE rowid%2; + PRAGMA incremental_vacuum(4); + ROLLBACK; + } + db eval {PRAGMA integrity_check} +} {ok} + +finish_test diff --git a/test/tkt3762.test b/test/tkt3762.test new file mode 100644 index 0000000..c24041c --- /dev/null +++ b/test/tkt3762.test @@ -0,0 +1,54 @@ +# 2009 March 28 +# +# 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. +# +#*********************************************************************** +# +# Ticket #3762: Make sure that an incremental vacuum that reduces the +# size of the database file such that a pointer-map page is elemented +# can be correctly rolled back. +# +# That ticket #3762 has been fixed has already been verified by the +# savepoint6.test test script. But this script is simplier and a +# redundant test never hurts. +# +# $Id: tkt3762.test,v 1.1 2009/03/31 00:50:36 drh Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test tkt3762-1.1 { + db eval { + PRAGMA auto_vacuum=INCREMENTAL; + PRAGMA page_size=1024; + PRAGMA cache_size=10; + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(zeroblob(900)); + INSERT INTO t1 VALUES(zeroblob(900)); + INSERT INTO t1 SELECT x FROM t1; + INSERT INTO t1 SELECT x FROM t1; + INSERT INTO t1 SELECT x FROM t1; + INSERT INTO t1 SELECT x FROM t1; + INSERT INTO t1 SELECT x FROM t1; + INSERT INTO t1 SELECT x FROM t1; + INSERT INTO t1 SELECT x FROM t1; + DELETE FROM t1 WHERE rowid>202; + VACUUM; + + BEGIN; + DELETE FROM t1 WHERE rowid IN (10,11,12) ; + PRAGMA incremental_vacuum(10); + UPDATE t1 SET x=zeroblob(900) WHERE rowid BETWEEN 100 AND 110; + INSERT INTO t1 VALUES(zeroblob(39000)); + SELECT count(*) FROM t1; + ROLLBACK; + } + db eval {PRAGMA integrity_check} +} {ok} + +finish_test diff --git a/test/tkt3773.test b/test/tkt3773.test new file mode 100644 index 0000000..0dc414e --- /dev/null +++ b/test/tkt3773.test @@ -0,0 +1,38 @@ +# 2009 April 2 +# +# 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. +# +#*********************************************************************** +# +# Ticket #3773: Be careful not to over-optimize when a compound +# subquery contains an ORDER BY clause. +# +# +# $Id: tkt3773.test,v 1.1 2009/04/02 16:59:47 drh Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test tkt3773-1.1 { + db eval { + CREATE TABLE t1(a,b); + INSERT INTO t1 VALUES(2,1); + INSERT INTO t1 VALUES(33,3); + CREATE TABLE t2(x,y); + INSERT INTO t2 VALUES(123,2); + INSERT INTO t2 VALUES(4,4); + SELECT a FROM ( + SELECT a, b FROM t1 + UNION ALL + SELECT x, y FROM t2 + ORDER BY 2 + ); + } +} {2 123 33 4} + +finish_test diff --git a/test/tkt3791.test b/test/tkt3791.test new file mode 100644 index 0000000..5d676ca --- /dev/null +++ b/test/tkt3791.test @@ -0,0 +1,28 @@ +# 2009 April 2 +# +# 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. +# +#*********************************************************************** +# +# Ticket #3791: A segfault when inserting into a table that contains +# an arbitrary expression as its default value. +# +# $Id: tkt3791.test,v 1.1 2009/04/08 12:21:31 drh Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test tkt3791-1.1 { + db eval { + CREATE TABLE t1(x, y DEFAULT(datetime('now'))); + INSERT INTO t1(x) VALUES(1); + SELECT x, length(y) FROM t1; + } +} {1 19} + +finish_test diff --git a/test/tkt3793.test b/test/tkt3793.test new file mode 100644 index 0000000..5cba0a1 --- /dev/null +++ b/test/tkt3793.test @@ -0,0 +1,109 @@ +# 2009 April 10 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# +# This file implements tests to verify that ticket #3793 has been +# fixed. +# +# $Id: tkt3793.test,v 1.1 2009/04/10 09:47:07 danielk1977 Exp $ + + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable !shared_cache||!attach { + finish_test + return +} +set ::enable_shared_cache [sqlite3_enable_shared_cache 1] + +do_test tkt3793-1.1 { + sqlite3 db1 test.db + sqlite3 db2 test.db + execsql { + BEGIN; + CREATE TABLE t1(a, b); + CREATE TABLE t2(a PRIMARY KEY, b); + INSERT INTO t1 VALUES(randstr(50,50), randstr(50,50)); + INSERT INTO t1 SELECT randstr(50,50), randstr(50,50) FROM t1; + INSERT INTO t1 SELECT randstr(50,50), randstr(50,50) FROM t1; + INSERT INTO t1 SELECT randstr(50,50), randstr(50,50) FROM t1; + INSERT INTO t1 SELECT randstr(50,50), randstr(50,50) FROM t1; + INSERT INTO t1 SELECT randstr(50,50), randstr(50,50) FROM t1; + INSERT INTO t1 SELECT randstr(50,50), randstr(50,50) FROM t1; + INSERT INTO t1 SELECT randstr(50,50), randstr(50,50) FROM t1; + INSERT INTO t1 SELECT randstr(50,50), randstr(50,50) FROM t1; + INSERT INTO t1 SELECT randstr(50,50), randstr(50,50) FROM t1; + INSERT INTO t1 SELECT randstr(50,50), randstr(50,50) FROM t1; + INSERT INTO t2 SELECT * FROM t1; + COMMIT; + } +} {} + +proc busyhandler {db args} { set ::busyconnection $db ; return 1 } +db2 busy {busyhandler db2} +db1 busy {busyhandler db1} + +# Establish a read-lock on the database file using connection [db]. +# +do_test tkt3793-1.2 { + execsql { + BEGIN; + SELECT count(*) FROM t1; + } +} {1024} + +# Set the size of the cache shared by [db1] and [db2] to 10. Then update +# more than 10 pages of table t1. At this point the shared-cache will +# hold a RESERVED lock on the database file. Even though there are now +# more than 10 dirty pages in memory, it cannot upgrade to an EXCLUSIVE +# lock because of the read-lock held by [db]. +# +do_test tkt3793-1.3 { + execsql { + PRAGMA cache_size = 10; + BEGIN; + UPDATE t1 SET b = randstr(50,50); + } db1 +} {} + +set x 0 + +# Run one SELECT query on the shared-cache using [db1], then from within +# the callback run another via [db2]. Because of the large number of dirty +# pages within the cache, each time a new page is read from the database +# SQLite will attempt to upgrade to an EXCLUSIVE lock, and hence invoke +# the busy-handler. The tests here verify that the correct busy-handler +# function is invoked (the busy-handler associated with the database +# connection that called sqlite3_step()). When bug #3793 existed, sometimes +# the [db2] busy-handler was invoked from within the call to sqlite3_step() +# associated with [db1]. +# +# Note: Before the bug was fixed, if [db2] was opened with the "-fullmutex 1" +# option, then this test case would cause an assert() to fail. +# +set ::busyconnection db1 +db1 eval {SELECT * FROM t2 ORDER BY a LIMIT 20} { + do_test tkt3793-2.[incr x] { set ::busyconnection } db1 + set ::busyconnection db2 + + db2 eval { SELECT count(*) FROM t2 } + do_test tkt3793-2.[incr x] { set ::busyconnection } db2 + set ::busyconnection db1 +} + +do_test tkt3793-3 { + db1 close + db2 close +} {} + +sqlite3_enable_shared_cache $::enable_shared_cache +finish_test diff --git a/test/trace.test b/test/trace.test index 67ad0e3..762c1d7 100644 --- a/test/trace.test +++ b/test/trace.test @@ -12,7 +12,7 @@ # # This file implements tests for the "sqlite3_trace()" API. # -# $Id: trace.test,v 1.7 2008/01/12 21:35:57 drh Exp $ +# $Id: trace.test,v 1.8 2009/04/07 14:14:23 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -147,23 +147,25 @@ catch {sqlite3_finalize $STMT} # Trigger tracing. # -do_test trace-5.1 { - db eval { - CREATE TRIGGER r1t1 AFTER UPDATE ON t1 BEGIN - UPDATE t2 SET a=new.a WHERE rowid=new.rowid; - END; - CREATE TRIGGER r1t2 AFTER UPDATE ON t2 BEGIN - SELECT 'hello'; - END; - } - set TRACE_OUT {} - proc trace_proc cmd { - lappend ::TRACE_OUT [string trim $cmd] - } - db eval { - UPDATE t1 SET a=a+1; - } - set TRACE_OUT -} {{UPDATE t1 SET a=a+1;} {-- TRIGGER r1t1} {-- TRIGGER r1t2} {-- TRIGGER r1t1} {-- TRIGGER r1t2} {-- TRIGGER r1t1} {-- TRIGGER r1t2}} +ifcapable trigger { + do_test trace-5.1 { + db eval { + CREATE TRIGGER r1t1 AFTER UPDATE ON t1 BEGIN + UPDATE t2 SET a=new.a WHERE rowid=new.rowid; + END; + CREATE TRIGGER r1t2 AFTER UPDATE ON t2 BEGIN + SELECT 'hello'; + END; + } + set TRACE_OUT {} + proc trace_proc cmd { + lappend ::TRACE_OUT [string trim $cmd] + } + db eval { + UPDATE t1 SET a=a+1; + } + set TRACE_OUT + } {{UPDATE t1 SET a=a+1;} {-- TRIGGER r1t1} {-- TRIGGER r1t2} {-- TRIGGER r1t1} {-- TRIGGER r1t2} {-- TRIGGER r1t1} {-- TRIGGER r1t2}} +} finish_test diff --git a/test/trans.test b/test/trans.test index be31564..3048b4d 100644 --- a/test/trans.test +++ b/test/trans.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this script is database locks. # -# $Id: trans.test,v 1.39 2008/12/22 10:58:46 danielk1977 Exp $ +# $Id: trans.test,v 1.40 2009/03/27 09:10:12 danielk1977 Exp $ set testdir [file dirname $argv0] @@ -783,6 +783,7 @@ integrity_check trans-7.15 # set fd [open test.tcl w] puts $fd { + sqlite3_test_control_pending_byte 0x0010000 sqlite3 db test.db db eval { PRAGMA default_cache_size=20; @@ -803,6 +804,7 @@ do_test trans-8.2 { integrity_check trans-8.3 set fd [open test.tcl w] puts $fd { + sqlite3_test_control_pending_byte 0x0010000 sqlite3 db test.db db eval { PRAGMA journal_mode=persist; @@ -823,6 +825,7 @@ do_test trans-8.5 { } $checksum2 integrity_check trans-8.6 + # In the following sequence of tests, compute the MD5 sum of the content # of a table, make lots of modifications to that table, then do a rollback. # Verify that after the rollback, the MD5 checksum is unchanged. diff --git a/test/vacuum2.test b/test/vacuum2.test index b96fbde..3cd6165 100644 --- a/test/vacuum2.test +++ b/test/vacuum2.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the VACUUM statement. # -# $Id: vacuum2.test,v 1.9 2008/11/10 18:20:16 shane Exp $ +# $Id: vacuum2.test,v 1.10 2009/02/18 20:31:18 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -155,10 +155,31 @@ ifcapable autovacuum { } } {ok} do_test vacuum2-4.4 { + db close + sqlite3 db test.db execsql { pragma auto_vacuum; } } {1} + do_test vacuum2-4.5 { # Ticket #3663 + execsql { + pragma auto_vacuum=2; + vacuum; + pragma auto_vacuum; + } + } {2} + do_test vacuum2-4.6 { + execsql { + pragma integrity_check + } + } {ok} + do_test vacuum2-4.7 { + db close + sqlite3 db test.db + execsql { + pragma auto_vacuum; + } + } {2} } finish_test diff --git a/test/vtabC.test b/test/vtabC.test index 4839748..824d4ca 100644 --- a/test/vtabC.test +++ b/test/vtabC.test @@ -13,7 +13,7 @@ # and xRollback methods are only invoked after an xBegin or xCreate. # Ticket #3083. # -# $Id: vtabC.test,v 1.1 2008/04/28 20:27:54 drh Exp $ +# $Id: vtabC.test,v 1.2 2009/04/07 14:14:23 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -23,6 +23,8 @@ ifcapable !vtab { return } +ifcapable !trigger { finish_test ; return } + # N will be the number of virtual tables we have defined. # diff --git a/test/where9.test b/test/where9.test index c8921c7..020fb63 100644 --- a/test/where9.test +++ b/test/where9.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the multi-index OR clause optimizer. # -# $Id: where9.test,v 1.5 2009/01/08 21:00:03 drh Exp $ +# $Id: where9.test,v 1.7 2009/02/24 10:01:52 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl diff --git a/test/whereA.test b/test/whereA.test new file mode 100644 index 0000000..a6f1723 --- /dev/null +++ b/test/whereA.test @@ -0,0 +1,91 @@ +# 2009 February 23 +# +# 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 file is testing the reverse_select_order pragma. +# +# $Id: whereA.test,v 1.2 2009/04/06 12:26:58 drh Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test whereA-1.1 { + db eval { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE, c); + INSERT INTO t1 VALUES(1,2,3); + INSERT INTO t1 values(2,'hello','world'); + INSERT INTO t1 VALUES(3,4.53,NULL); + SELECT * FROM t1 + } +} {1 2 3 2 hello world 3 4.53 {}} +do_test whereA-1.2 { + db eval { + PRAGMA reverse_unordered_selects=1; + SELECT * FROM t1; + } +} {3 4.53 {} 2 hello world 1 2 3} + +do_test whereA-1.3 { + db eval { + PRAGMA reverse_unordered_selects=1; + SELECT * FROM t1 ORDER BY rowid; + } +} {1 2 3 2 hello world 3 4.53 {}} + +do_test whereA-2.1 { + db eval { + PRAGMA reverse_unordered_selects=0; + SELECT * FROM t1 WHERE a>0; + } +} {1 2 3 2 hello world 3 4.53 {}} +do_test whereA-2.2 { + db eval { + PRAGMA reverse_unordered_selects=1; + SELECT * FROM t1 WHERE a>0; + } +} {3 4.53 {} 2 hello world 1 2 3} + +do_test whereA-2.3 { + db eval { + PRAGMA reverse_unordered_selects=1; + SELECT * FROM t1 WHERE a>0 ORDER BY rowid; + } +} {1 2 3 2 hello world 3 4.53 {}} + +do_test whereA-3.1 { + db eval { + PRAGMA reverse_unordered_selects=0; + SELECT * FROM t1 WHERE b>0; + } +} {1 2 3 3 4.53 {} 2 hello world} +do_test whereA-3.2 { + db eval { + PRAGMA reverse_unordered_selects=1; + SELECT * FROM t1 WHERE b>0; + } +} {2 hello world 3 4.53 {} 1 2 3} +do_test whereA-3.3 { + db eval { + PRAGMA reverse_unordered_selects=1; + SELECT * FROM t1 WHERE b>0 ORDER BY b; + } +} {1 2 3 3 4.53 {} 2 hello world} + +do_test whereA-4.1 { + db eval { + CREATE TABLE t2(x); + INSERT INTO t2 VALUES(1); + INSERT INTO t2 VALUES(2); + SELECT x FROM t2; + } +} {2 1} + + +finish_test diff --git a/tool/genfkey.README b/tool/genfkey.README index ef4dcdb..57cdff8 100644 --- a/tool/genfkey.README +++ b/tool/genfkey.README @@ -4,9 +4,10 @@ OVERVIEW The SQLite library is capable of parsing SQL foreign key constraints supplied as part of CREATE TABLE statements, but it does not actually implement them. However, most of the features of foreign keys may be - implemented using SQL triggers, which SQLite does support. This program + implemented using SQL triggers, which SQLite does support. This text + file describes a feature of the SQLite shell tool (sqlite3) that extracts foreign key definitions from an existing SQLite database and - outputs the set of CREATE TRIGGER statements required to implement + creates the set of CREATE TRIGGER statements required to implement the foreign key constraints. CAPABILITIES @@ -90,54 +91,47 @@ LIMITATIONS Recursive CASCADE or SET NULL foreign key relationships are *not* detected as errors when the program is run. Buyer beware. - -COMPILATION - - The source code for this program consists of a single C file - genfkey.c. - The only dependency is sqlite itself. Using gcc and the sqlite amalgamation - source code, it may be compiled using the following command: - - gcc genfkey.c sqlite3.c -o genfkey - - Or, to use an installed version of sqlite (the kind linux distributions - install as part of the sqlite3-dev package): - - gcc genfkey.c -lsqlite3 -o genfkey - - If compiled/linked against an SQLite version earlier than 3.6.4, then - all foreign key constraints are assumed to be "ON UPDATE RESTRICT" and - "ON DELETE RESTRICT". If linked against 3.6.4 or newer, "CASCADE" and - "SET NULL" are supported as well as "RESTRICT". All 3.x versions of SQLite - may use the created triggers definitions. USAGE - genfkey ?--no-drop? ?--ignore-errors? + The functionality is accessed through an sqlite3 shell tool "dot-command": - When this program is run, it first checks the schema of the supplied SQLite + .genfkey ?--no-drop? ?--ignore-errors? ?--exec? + + When this command is run, it first checks the schema of the open SQLite database for foreign key related errors or inconsistencies. For example, a foreign key that refers to a parent table that does not exist, or a foreign key that refers to columns in a parent table that are not - guaranteed to be unique. If such errors are found, a message for each - one is printed to stderr. + guaranteed to be unique. If such errors are found and the --ignore-errors + option was not present, a message for each one is printed to stderr and + no further processing takes place. - If errors are found and the --ignore-errors option was not passed, the - program exits. Otherwise, a series of SQL trigger definitions (CREATE - TRIGGER statements) that implement the foreign key constraints found - in the database schema are written to stdout. If any errors were - found in the schema, no triggers for the problematic constraints are - output. The output CREATE TRIGGER statements should be run against the - database to enable enforcement of the foreign key constraints. For - example, for a database named "test.db" in the current working directory: + If errors are found and the --ignore-errors option is passed, then + no error messages are printed. No "CREATE TRIGGER" statements are generated + for foriegn-key definitions that contained errors, they are silently + ignored by subsequent processing. - $ genfkey ./test.db | sqlite3 ./test.db - - All triggers generated by this program have names that match the pattern + All triggers generated by this command have names that match the pattern "genfkey*". Unless the --no-drop option is specified, then the program - also outputs a "DROP TRIGGER" statement for each trigger that exists + also generates a "DROP TRIGGER" statement for each trigger that exists in the database with a name that matches this pattern. This allows the program to be used to upgrade a database schema for which foreign key triggers have already been installed (i.e. after new tables are created or existing tables dropped). - + + Finally, a series of SQL trigger definitions (CREATE TRIGGER statements) + that implement the foreign key constraints found in the database schema are + generated. + + If the --exec option was passed, then all generated SQL is immediately + executed on the database. Otherwise, the generated SQL strings are output + in the same way as the results of SELECT queries are. Normally, this means + they will be printed to stdout, but this can be configured using other + dot-commands (i.e. ".output"). + + The simplest way to activate the foriegn key definitions in a database + is simply to open it using the shell tool and enter the command + ".genfkey --exec": + + sqlite> .genfkey --exec diff --git a/tool/genfkey.c b/tool/genfkey.c deleted file mode 100644 index 2cc047b..0000000 --- a/tool/genfkey.c +++ /dev/null @@ -1,869 +0,0 @@ -/* -** 2008 October 10 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains C code for 'genfkey', a program to generate trigger -** definitions that emulate foreign keys. See genfkey.README for details. -** -** $Id: genfkey.c,v 1.2 2008/10/13 10:56:48 danielk1977 Exp $ -*/ - -#include "sqlite3.h" -#include -#include -#include -#include - -/************************************************************************** -*************************************************************************** -** Start of virtual table implementations. -**************************************************************************/ - -/* The code in this file defines a sqlite3 virtual-table module that -** provides a read-only view of the current database schema. There is one -** row in the schema table for each column in the database schema. -*/ -#define SCHEMA \ -"CREATE TABLE x(" \ - "database," /* Name of database (i.e. main, temp etc.) */ \ - "tablename," /* Name of table */ \ - "cid," /* Column number (from left-to-right, 0 upward) */ \ - "name," /* Column name */ \ - "type," /* Specified type (i.e. VARCHAR(32)) */ \ - "not_null," /* Boolean. True if NOT NULL was specified */ \ - "dflt_value," /* Default value for this column */ \ - "pk" /* True if this column is part of the primary key */ \ -")" - -#define SCHEMA2 \ -"CREATE TABLE x(" \ - "database," /* Name of database (i.e. main, temp etc.) */ \ - "from_tbl," /* Name of table */ \ - "fkid," \ - "seq," \ - "to_tbl," \ - "from_col," \ - "to_col," \ - "on_update," \ - "on_delete," \ - "match" \ -")" - -#define SCHEMA3 \ -"CREATE TABLE x(" \ - "database," /* Name of database (i.e. main, temp etc.) */ \ - "tablename," /* Name of table */ \ - "seq," \ - "name," \ - "isunique" \ -")" - -#define SCHEMA4 \ -"CREATE TABLE x(" \ - "database," /* Name of database (i.e. main, temp etc.) */ \ - "indexname," /* Name of table */ \ - "seqno," \ - "cid," \ - "name" \ -")" - -typedef struct SchemaTable SchemaTable; -struct SchemaTable { - const char *zName; - const char *zObject; - const char *zPragma; - const char *zSchema; -} aSchemaTable[] = { - { "table_info", "table", "PRAGMA %Q.table_info(%Q)", SCHEMA }, - { "foreign_key_list", "table", "PRAGMA %Q.foreign_key_list(%Q)", SCHEMA2 }, - { "index_list", "table", "PRAGMA %Q.index_list(%Q)", SCHEMA3 }, - { "index_info", "index", "PRAGMA %Q.index_info(%Q)", SCHEMA4 }, - { 0, 0, 0, 0 } -}; - -typedef struct schema_vtab schema_vtab; -typedef struct schema_cursor schema_cursor; - -/* A schema table object */ -struct schema_vtab { - sqlite3_vtab base; - sqlite3 *db; - SchemaTable *pType; -}; - -/* A schema table cursor object */ -struct schema_cursor { - sqlite3_vtab_cursor base; - sqlite3_stmt *pDbList; - sqlite3_stmt *pTableList; - sqlite3_stmt *pColumnList; - int rowid; -}; - -/* -** Table destructor for the schema module. -*/ -static int schemaDestroy(sqlite3_vtab *pVtab){ - sqlite3_free(pVtab); - return 0; -} - -/* -** Table constructor for the schema module. -*/ -static int schemaCreate( - sqlite3 *db, - void *pAux, - int argc, const char *const*argv, - sqlite3_vtab **ppVtab, - char **pzErr -){ - int rc = SQLITE_NOMEM; - schema_vtab *pVtab; - SchemaTable *pType = &aSchemaTable[0]; - - if( argc>3 ){ - int i; - pType = 0; - for(i=0; aSchemaTable[i].zName; i++){ - if( 0==strcmp(argv[3], aSchemaTable[i].zName) ){ - pType = &aSchemaTable[i]; - } - } - if( !pType ){ - return SQLITE_ERROR; - } - } - - pVtab = sqlite3_malloc(sizeof(schema_vtab)); - if( pVtab ){ - memset(pVtab, 0, sizeof(schema_vtab)); - pVtab->db = db; - pVtab->pType = pType; - rc = sqlite3_declare_vtab(db, pType->zSchema); - } - *ppVtab = (sqlite3_vtab *)pVtab; - return rc; -} - -/* -** Open a new cursor on the schema table. -*/ -static int schemaOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ - int rc = SQLITE_NOMEM; - schema_cursor *pCur; - pCur = sqlite3_malloc(sizeof(schema_cursor)); - if( pCur ){ - memset(pCur, 0, sizeof(schema_cursor)); - *ppCursor = (sqlite3_vtab_cursor *)pCur; - rc = SQLITE_OK; - } - return rc; -} - -/* -** Close a schema table cursor. -*/ -static int schemaClose(sqlite3_vtab_cursor *cur){ - schema_cursor *pCur = (schema_cursor *)cur; - sqlite3_finalize(pCur->pDbList); - sqlite3_finalize(pCur->pTableList); - sqlite3_finalize(pCur->pColumnList); - sqlite3_free(pCur); - return SQLITE_OK; -} - -/* -** Retrieve a column of data. -*/ -static int schemaColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ - schema_cursor *pCur = (schema_cursor *)cur; - switch( i ){ - case 0: - sqlite3_result_value(ctx, sqlite3_column_value(pCur->pDbList, 1)); - break; - case 1: - sqlite3_result_value(ctx, sqlite3_column_value(pCur->pTableList, 0)); - break; - default: - sqlite3_result_value(ctx, sqlite3_column_value(pCur->pColumnList, i-2)); - break; - } - return SQLITE_OK; -} - -/* -** Retrieve the current rowid. -*/ -static int schemaRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ - schema_cursor *pCur = (schema_cursor *)cur; - *pRowid = pCur->rowid; - return SQLITE_OK; -} - -static int finalize(sqlite3_stmt **ppStmt){ - int rc = sqlite3_finalize(*ppStmt); - *ppStmt = 0; - return rc; -} - -static int schemaEof(sqlite3_vtab_cursor *cur){ - schema_cursor *pCur = (schema_cursor *)cur; - return (pCur->pDbList ? 0 : 1); -} - -/* -** Advance the cursor to the next row. -*/ -static int schemaNext(sqlite3_vtab_cursor *cur){ - int rc = SQLITE_OK; - schema_cursor *pCur = (schema_cursor *)cur; - schema_vtab *pVtab = (schema_vtab *)(cur->pVtab); - char *zSql = 0; - - while( !pCur->pColumnList || SQLITE_ROW!=sqlite3_step(pCur->pColumnList) ){ - if( SQLITE_OK!=(rc = finalize(&pCur->pColumnList)) ) goto next_exit; - - while( !pCur->pTableList || SQLITE_ROW!=sqlite3_step(pCur->pTableList) ){ - if( SQLITE_OK!=(rc = finalize(&pCur->pTableList)) ) goto next_exit; - - assert(pCur->pDbList); - while( SQLITE_ROW!=sqlite3_step(pCur->pDbList) ){ - rc = finalize(&pCur->pDbList); - goto next_exit; - } - - /* Set zSql to the SQL to pull the list of tables from the - ** sqlite_master (or sqlite_temp_master) table of the database - ** identfied by the row pointed to by the SQL statement pCur->pDbList - ** (iterating through a "PRAGMA database_list;" statement). - */ - if( sqlite3_column_int(pCur->pDbList, 0)==1 ){ - zSql = sqlite3_mprintf( - "SELECT name FROM sqlite_temp_master WHERE type=%Q", - pVtab->pType->zObject - ); - }else{ - sqlite3_stmt *pDbList = pCur->pDbList; - zSql = sqlite3_mprintf( - "SELECT name FROM %Q.sqlite_master WHERE type=%Q", - sqlite3_column_text(pDbList, 1), pVtab->pType->zObject - ); - } - if( !zSql ){ - rc = SQLITE_NOMEM; - goto next_exit; - } - - rc = sqlite3_prepare(pVtab->db, zSql, -1, &pCur->pTableList, 0); - sqlite3_free(zSql); - if( rc!=SQLITE_OK ) goto next_exit; - } - - /* Set zSql to the SQL to the table_info pragma for the table currently - ** identified by the rows pointed to by statements pCur->pDbList and - ** pCur->pTableList. - */ - zSql = sqlite3_mprintf(pVtab->pType->zPragma, - sqlite3_column_text(pCur->pDbList, 1), - sqlite3_column_text(pCur->pTableList, 0) - ); - - if( !zSql ){ - rc = SQLITE_NOMEM; - goto next_exit; - } - rc = sqlite3_prepare(pVtab->db, zSql, -1, &pCur->pColumnList, 0); - sqlite3_free(zSql); - if( rc!=SQLITE_OK ) goto next_exit; - } - pCur->rowid++; - -next_exit: - /* TODO: Handle rc */ - return rc; -} - -/* -** Reset a schema table cursor. -*/ -static int schemaFilter( - sqlite3_vtab_cursor *pVtabCursor, - int idxNum, const char *idxStr, - int argc, sqlite3_value **argv -){ - int rc; - schema_vtab *pVtab = (schema_vtab *)(pVtabCursor->pVtab); - schema_cursor *pCur = (schema_cursor *)pVtabCursor; - pCur->rowid = 0; - finalize(&pCur->pTableList); - finalize(&pCur->pColumnList); - finalize(&pCur->pDbList); - rc = sqlite3_prepare(pVtab->db,"SELECT 0, 'main'", -1, &pCur->pDbList, 0); - return (rc==SQLITE_OK ? schemaNext(pVtabCursor) : rc); -} - -/* -** Analyse the WHERE condition. -*/ -static int schemaBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ - return SQLITE_OK; -} - -/* -** A virtual table module that merely echos method calls into TCL -** variables. -*/ -static sqlite3_module schemaModule = { - 0, /* iVersion */ - schemaCreate, - schemaCreate, - schemaBestIndex, - schemaDestroy, - schemaDestroy, - schemaOpen, /* xOpen - open a cursor */ - schemaClose, /* xClose - close a cursor */ - schemaFilter, /* xFilter - configure scan constraints */ - schemaNext, /* xNext - advance a cursor */ - schemaEof, /* xEof */ - schemaColumn, /* xColumn - read data */ - schemaRowid, /* xRowid - read data */ - 0, /* xUpdate */ - 0, /* xBegin */ - 0, /* xSync */ - 0, /* xCommit */ - 0, /* xRollback */ - 0, /* xFindMethod */ - 0, /* xRename */ -}; - -/* -** Extension load function. -*/ -static int installSchemaModule(sqlite3 *db){ - sqlite3_create_module(db, "schema", &schemaModule, 0); - return 0; -} - -/************************************************************************** -*************************************************************************** -** End of virtual table implementations. -** Start of SQL user function implementations. -*/ - -/* -** sj(zValue, zJoin) -** -** The following block contains the implementation of an aggregate -** function that returns a string. Each time the function is stepped, -** it appends data to an internal buffer. When the aggregate is finalized, -** the contents of the buffer are returned. -** -** The first time the aggregate is stepped the buffer is set to a copy -** of the first argument. The second time and subsequent times it is -** stepped a copy of the second argument is appended to the buffer, then -** a copy of the first. -** -** Example: -** -** INSERT INTO t1(a) VALUES('1'); -** INSERT INTO t1(a) VALUES('2'); -** INSERT INTO t1(a) VALUES('3'); -** SELECT sj(a, ', ') FROM t1; -** -** => "1, 2, 3" -** -*/ -struct StrBuffer { - char *zBuf; -}; -typedef struct StrBuffer StrBuffer; -static void joinFinalize(sqlite3_context *context){ - StrBuffer *p; - p = (StrBuffer *)sqlite3_aggregate_context(context, sizeof(StrBuffer)); - sqlite3_result_text(context, p->zBuf, -1, SQLITE_TRANSIENT); - sqlite3_free(p->zBuf); -} -static void joinStep( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - StrBuffer *p; - p = (StrBuffer *)sqlite3_aggregate_context(context, sizeof(StrBuffer)); - if( p->zBuf==0 ){ - p->zBuf = sqlite3_mprintf("%s", sqlite3_value_text(argv[0])); - }else{ - char *zTmp = p->zBuf; - p->zBuf = sqlite3_mprintf("%s%s%s", - zTmp, sqlite3_value_text(argv[1]), sqlite3_value_text(argv[0]) - ); - sqlite3_free(zTmp); - } -} - -/* -** dq(zString) -** -** This scalar function accepts a single argument and interprets it as -** a text value. The return value is the argument enclosed in double -** quotes. If any double quote characters are present in the argument, -** these are escaped. -** -** dq('the raven "Nevermore."') == '"the raven ""Nevermore."""' -*/ -static void doublequote( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - int ii; - char *zOut; - char *zCsr; - const char *zIn = (const char *)sqlite3_value_text(argv[0]); - int nIn = sqlite3_value_bytes(argv[0]); - - zOut = sqlite3_malloc(nIn*2+3); - zCsr = zOut; - *zCsr++ = '"'; - for(ii=0; iinMalloc ){ - nMalloc += (nMalloc + 16); - zOut = (char *)sqlite3_realloc(zOut, nMalloc); - } - memcpy(&zOut[nOut], zCopy, nCopy); - i += nReplace; - nOut += nCopy; - } - - sqlite3_result_text(context, zOut, nOut, SQLITE_TRANSIENT); - sqlite3_free(zOut); -} - -/************************************************************************** -*************************************************************************** -** End of SQL user function implementations. -** Start of application implementation. -*/ - -typedef struct Options Options; -struct Options { - char *zDb; - int ignoreErrors; - int noDrop; -}; - -/* -** Print out a usage message for the command line and exit. This is -** called from processCmdLine() if the program is invoked incorrectly. -*/ -static int usage(char *zProgram){ - fprintf(stderr, - "Usage: %s ?--ignore-errors? ?--no-drop? \n", zProgram - ); - exit(-1); -} - -static void processCmdLine(int nArg, char **azArg, Options *p){ - int i; - assert( nArg>0 ); - if( nArg<2 ){ - usage(azArg[0]); - } - for(i=1; i<(nArg-1); i++){ - char *z = azArg[i]; - if( 0==strcmp(z, "--ignore-errors") ){ - p->ignoreErrors = 1; - } - else if( 0==strcmp(z, "--no-drop") ){ - p->noDrop = 1; - } - else usage(azArg[0]); - } - p->zDb = azArg[nArg-1]; -} - -/* -** A callback for sqlite3_exec() that prints its first argument to -** stdout followed by a newline. -*/ -static int printString(void *p, int nArg, char **azArg, char **azCol){ - printf("%s\n", azArg[0]); - return SQLITE_OK; -} - -int detectSchemaProblem( - sqlite3 *db, /* Database connection */ - const char *zMessage, /* English language error message */ - const char *zSql, /* SQL statement to run */ - int *pHasErrors /* Set *pHasErrors==1 if errors found */ -){ - sqlite3_stmt *pStmt; - int rc; - rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); - if( rc!=SQLITE_OK ){ - return rc; - } - while( SQLITE_ROW==sqlite3_step(pStmt) ){ - char *zDel; - int iFk = sqlite3_column_int(pStmt, 0); - const char *zTab = (const char *)sqlite3_column_text(pStmt, 1); - fprintf(stderr, "Error in table %s: %s\n", zTab, zMessage); - zDel = sqlite3_mprintf( - "DELETE FROM temp.fkey WHERE from_tbl = %Q AND fkid = %d" - , zTab, iFk - ); - sqlite3_exec(db, zDel, 0, 0, 0); - sqlite3_free(zDel); - *pHasErrors = 1; - } - sqlite3_finalize(pStmt); - return SQLITE_OK; -} - -/* -** Create and populate temporary table "fkey". -*/ -static int populateTempTable(sqlite3 *db, char **pzErr, int *pHasErrors){ - int rc; - - rc = sqlite3_exec(db, - "CREATE VIRTUAL TABLE temp.v_fkey USING schema(foreign_key_list);" - "CREATE VIRTUAL TABLE temp.v_col USING schema(table_info);" - "CREATE VIRTUAL TABLE temp.v_idxlist USING schema(index_list);" - "CREATE VIRTUAL TABLE temp.v_idxinfo USING schema(index_info);" - - "CREATE TABLE temp.fkey AS " - "SELECT from_tbl, to_tbl, fkid, from_col, to_col, on_update, on_delete " - "FROM temp.v_fkey WHERE database = 'main';" - - , 0, 0, pzErr - ); - if( rc!=SQLITE_OK ) return rc; - - rc = detectSchemaProblem(db, "foreign key columns do not exist", - "SELECT fkid, from_tbl " - "FROM temp.fkey " - "WHERE to_col IS NOT NULL AND NOT EXISTS (SELECT 1 " - "FROM temp.v_col WHERE tablename=to_tbl AND name==to_col" - ")", pHasErrors - ); - if( rc!=SQLITE_OK ) return rc; - - /* At this point the temp.fkey table is mostly populated. If any foreign - ** keys were specified so that they implicitly refer to they primary - ** key of the parent table, the "to_col" values of the temp.fkey rows - ** are still set to NULL. - ** - ** This is easily fixed for single column primary keys, but not for - ** composites. With a composite primary key, there is no way to reliably - ** query sqlite for the order in which the columns that make up the - ** composite key were declared i.e. there is no way to tell if the - ** schema actually contains "PRIMARY KEY(a, b)" or "PRIMARY KEY(b, a)". - ** Therefore, this case is not handled. The following function call - ** detects instances of this case. - */ - rc = detectSchemaProblem(db, "implicit mapping to composite primary key", - "SELECT fkid, from_tbl " - "FROM temp.fkey " - "WHERE to_col IS NULL " - "GROUP BY fkid, from_tbl HAVING count(*) > 1", pHasErrors - ); - if( rc!=SQLITE_OK ) return rc; - - /* Detect attempts to implicitly map to the primary key of a table - ** that has no primary key column. - */ - rc = detectSchemaProblem(db, "implicit mapping to non-existant primary key", - "SELECT fkid, from_tbl " - "FROM temp.fkey " - "WHERE to_col IS NULL AND NOT EXISTS " - "(SELECT 1 FROM temp.v_col WHERE pk AND tablename = temp.fkey.to_tbl)" - , pHasErrors - ); - if( rc!=SQLITE_OK ) return rc; - - /* Fix all the implicit primary key mappings in the temp.fkey table. */ - rc = sqlite3_exec(db, - "UPDATE temp.fkey SET to_col = " - "(SELECT name FROM temp.v_col WHERE pk AND tablename=temp.fkey.to_tbl)" - " WHERE to_col IS NULL;" - , 0, 0, pzErr - ); - if( rc!=SQLITE_OK ) return rc; - - /* Now check that all all parent keys are either primary keys or - ** subject to a unique constraint. - */ - rc = sqlite3_exec(db, - "CREATE TABLE temp.idx2 AS SELECT " - "il.tablename AS tablename," - "ii.indexname AS indexname," - "ii.name AS col " - "FROM temp.v_idxlist AS il, temp.v_idxinfo AS ii " - "WHERE il.isunique AND il.database='main' AND ii.indexname = il.name;" - "INSERT INTO temp.idx2 " - "SELECT tablename, 'pk', name FROM temp.v_col WHERE pk;" - - "CREATE TABLE temp.idx AS SELECT " - "tablename, indexname, sj(dq(col),',') AS cols " - "FROM (SELECT * FROM temp.idx2 ORDER BY col) " - "GROUP BY tablename, indexname;" - - "CREATE TABLE temp.fkey2 AS SELECT " - "fkid, from_tbl, to_tbl, sj(dq(to_col),',') AS cols " - "FROM (SELECT * FROM temp.fkey ORDER BY to_col) " - "GROUP BY fkid, from_tbl;" - , 0, 0, pzErr - ); - if( rc!=SQLITE_OK ) return rc; - rc = detectSchemaProblem(db, "foreign key is not unique", - "SELECT fkid, from_tbl " - "FROM temp.fkey2 " - "WHERE NOT EXISTS (SELECT 1 " - "FROM temp.idx WHERE tablename=to_tbl AND fkey2.cols==idx.cols" - ")", pHasErrors - ); - if( rc!=SQLITE_OK ) return rc; - - return rc; -} - -int main(int argc, char **argv){ - sqlite3 *db; - Options opt = {0, 0, 0}; - int rc; - int hasErrors = 0; - char *zErr = 0; - const int enc = SQLITE_UTF8; - - const char *zSql = - "SELECT multireplace('" - - "-- Triggers for foreign key mapping:\n" - "--\n" - "-- /from_readable/ REFERENCES /to_readable/\n" - "-- on delete /on_delete/\n" - "-- on update /on_update/\n" - "--\n" - - /* The "BEFORE INSERT ON " trigger. This trigger's job is to - ** throw an exception if the user tries to insert a row into the - ** referencing table for which there is no corresponding row in - ** the referenced table. - */ - "CREATE TRIGGER /name/_insert_referencing BEFORE INSERT ON /tbl/ WHEN \n" - " /key_notnull/ AND NOT EXISTS (SELECT 1 FROM /ref/ WHERE /cond1/)\n" - "BEGIN\n" - " SELECT RAISE(ABORT, ''constraint failed'');\n" - "END;\n" - - /* The "BEFORE UPDATE ON " trigger. This trigger's job - ** is to throw an exception if the user tries to update a row in the - ** referencing table causing it to correspond to no row in the - ** referenced table. - */ - "CREATE TRIGGER /name/_update_referencing BEFORE\n" - " UPDATE OF /rkey_list/ ON /tbl/ WHEN \n" - " /key_notnull/ AND \n" - " NOT EXISTS (SELECT 1 FROM /ref/ WHERE /cond1/)\n" - "BEGIN\n" - " SELECT RAISE(ABORT, ''constraint failed'');\n" - "END;\n" - - - /* The "BEFORE DELETE ON " trigger. This trigger's job - ** is to detect when a row is deleted from the referenced table to - ** which rows in the referencing table correspond. The action taken - ** depends on the value of the 'ON DELETE' clause. - */ - "CREATE TRIGGER /name/_delete_referenced BEFORE DELETE ON /ref/ WHEN\n" - " EXISTS (SELECT 1 FROM /tbl/ WHERE /cond2/)\n" - "BEGIN\n" - " /delete_action/\n" - "END;\n" - - /* The "BEFORE DELETE ON " trigger. This trigger's job - ** is to detect when the key columns of a row in the referenced table - ** to which one or more rows in the referencing table correspond are - ** updated. The action taken depends on the value of the 'ON UPDATE' - ** clause. - */ - "CREATE TRIGGER /name/_update_referenced AFTER\n" - " UPDATE OF /fkey_list/ ON /ref/ WHEN \n" - " EXISTS (SELECT 1 FROM /tbl/ WHERE /cond2/)\n" - "BEGIN\n" - " /update_action/\n" - "END;\n" - "'" - - /* These are used in the SQL comment written above each set of triggers */ - ", '/from_readable/', from_tbl || '(' || sj(from_col, ', ') || ')'" - ", '/to_readable/', to_tbl || '(' || sj(to_col, ', ') || ')'" - ", '/on_delete/', on_delete" - ", '/on_update/', on_update" - - ", '/name/', 'genfkey' || min(rowid)" - ", '/tbl/', dq(from_tbl)" - ", '/ref/', dq(to_tbl)" - ", '/key_notnull/', sj('new.' || dq(from_col) || ' IS NOT NULL', ' AND ')" - - ", '/fkey_list/', sj(to_col, ', ')" - ", '/rkey_list/', sj(from_col, ', ')" - - ", '/cond1/', sj(multireplace('new./from/ == /to/'" - ", '/from/', dq(from_col)" - ", '/to/', dq(to_col)" - "), ' AND ')" - ", '/cond2/', sj(multireplace('old./to/ == /from/'" - ", '/from/', dq(from_col)" - ", '/to/', dq(to_col)" - "), ' AND ')" - - ", '/update_action/', CASE on_update " - "WHEN 'SET NULL' THEN " - "multireplace('UPDATE /tbl/ SET /setlist/ WHERE /where/;' " - ", '/setlist/', sj(from_col||' = NULL',', ')" - ", '/tbl/', dq(from_tbl)" - ", '/where/', sj(from_col||' = old.'||dq(to_col),' AND ')" - ")" - "WHEN 'CASCADE' THEN " - "multireplace('UPDATE /tbl/ SET /setlist/ WHERE /where/;' " - ", '/setlist/', sj(dq(from_col)||' = new.'||dq(to_col),', ')" - ", '/tbl/', dq(from_tbl)" - ", '/where/', sj(dq(from_col)||' = old.'||dq(to_col),' AND ')" - ")" - "ELSE " - " 'SELECT RAISE(ABORT, ''constraint failed'');'" - "END " - - ", '/delete_action/', CASE on_delete " - "WHEN 'SET NULL' THEN " - "multireplace('UPDATE /tbl/ SET /setlist/ WHERE /where/;' " - ", '/setlist/', sj(from_col||' = NULL',', ')" - ", '/tbl/', dq(from_tbl)" - ", '/where/', sj(from_col||' = old.'||dq(to_col),' AND ')" - ")" - "WHEN 'CASCADE' THEN " - "multireplace('DELETE FROM /tbl/ WHERE /where/;' " - ", '/tbl/', dq(from_tbl)" - ", '/where/', sj(dq(from_col)||' = old.'||dq(to_col),' AND ')" - ")" - "ELSE " - " 'SELECT RAISE(ABORT, ''constraint failed'');'" - "END " - - ") FROM temp.fkey " - "GROUP BY from_tbl, fkid" - ; - - processCmdLine(argc, argv, &opt); - - /* Open the database handle. */ - rc = sqlite3_open_v2(opt.zDb, &db, SQLITE_OPEN_READONLY, 0); - if( rc!=SQLITE_OK ){ - fprintf(stderr, "Error opening database file: %s\n", sqlite3_errmsg(db)); - return -1; - } - - /* Create the special scalar and aggregate functions used by this program. */ - sqlite3_create_function(db, "dq", 1, enc, 0, doublequote, 0, 0); - sqlite3_create_function(db, "multireplace", -1, enc, db, multireplace, 0, 0); - sqlite3_create_function(db, "sj", 2, enc, 0, 0, joinStep, joinFinalize); - - /* Install the "schema" virtual table module */ - installSchemaModule(db); - - /* Create and populate a temp table with the information required to - ** build the foreign key triggers. See function populateTempTable() - ** for details. - */ - rc = populateTempTable(db, &zErr, &hasErrors); - if( rc!=SQLITE_OK ){ - fprintf(stderr, "Error reading database: %s\n", zErr); - return -1; - } - if( hasErrors && opt.ignoreErrors==0 ){ - return -1; - } - - printf("BEGIN;\n"); - - /* Unless the --no-drop option was specified, generate DROP TRIGGER - ** statements to drop any triggers in the database generated by a - ** previous run of this program. - */ - if( opt.noDrop==0 ){ - rc = sqlite3_exec(db, - "SELECT 'DROP TRIGGER' || ' ' || dq(name) || ';'" - "FROM sqlite_master " - "WHERE type='trigger' AND substr(name, 0, 7) == 'genfkey'" - , printString, 0, 0 - ); - if( rc!=SQLITE_OK ){ - const char *zMsg = sqlite3_errmsg(db); - fprintf(stderr, "Generating drop triggers failed: %s\n", zMsg); - return -1; - } - } - - /* Run the main query to create the trigger definitions. */ - rc = sqlite3_exec(db, zSql, printString, 0, 0); - if( rc!=SQLITE_OK ){ - fprintf(stderr, "Generating triggers failed: %s\n", sqlite3_errmsg(db)); - return -1; - } - - printf("COMMIT;\n"); - return 0; -} - diff --git a/tool/genfkey.test b/tool/genfkey.test index c55511f..422de83 100644 --- a/tool/genfkey.test +++ b/tool/genfkey.test @@ -36,7 +36,7 @@ do_test genfkey-1.1 { } } {} do_test genfkey-1.2 { - execsql [exec ./genfkey test.db] + execsql [exec ./sqlite3 test.db .genfkey] } {} do_test genfkey-1.3 { catchsql { INSERT INTO t2 VALUES(1, 2) } @@ -129,7 +129,7 @@ do_test genfkey-2.1 { } } {} do_test genfkey-2.2 { - execsql [exec ./genfkey test.db] + execsql [exec ./sqlite3 test.db .genfkey] } {} do_test genfkey-2.3 { execsql { @@ -186,7 +186,7 @@ do_test genfkey-3.1 { } } {} do_test genfkey-3.2 { - execsql [exec ./genfkey test.db] + execsql [exec ./sqlite3 test.db .genfkey] } {} do_test genfkey-3.3 { execsql { @@ -249,7 +249,7 @@ do_test genfkey-4.1 { } {} do_test genfkey-4.X { - set rc [catch {exec ./genfkey test.db} msg] + set rc [catch {exec ./sqlite3 test.db .genfkey} msg] list $rc $msg } "1 {[string trim { Error in table t5: foreign key columns do not exist diff --git a/tool/lemon.c b/tool/lemon.c index f2869da..6610692 100644 --- a/tool/lemon.c +++ b/tool/lemon.c @@ -3432,7 +3432,7 @@ int mhflag; /* True if generating makeheaders output */ while( *cp ) stddt[j++] = *cp++; while( j>0 && isspace(stddt[j-1]) ) j--; stddt[j] = 0; - if( strcmp(stddt, lemp->tokentype)==0 ){ + if( lemp->tokentype && strcmp(stddt, lemp->tokentype)==0 ){ sp->dtnum = 0; continue; } diff --git a/tool/mksqlite3c.tcl b/tool/mksqlite3c.tcl index e13c86c..857ecfb 100644 --- a/tool/mksqlite3c.tcl +++ b/tool/mksqlite3c.tcl @@ -140,7 +140,7 @@ proc copy_file {filename} { section_comment "Begin file $tail" set in [open $filename r] set varpattern {^[a-zA-Z][a-zA-Z_0-9 *]+(sqlite3[_a-zA-Z0-9]+)(\[|;| =)} - set declpattern {[a-zA-Z][a-zA-Z_0-9 ]+ \*?(sqlite3[_a-zA-Z0-9]+)\(} + set declpattern {[a-zA-Z][a-zA-Z_0-9 ]+ \**(sqlite3[_a-zA-Z0-9]+)\(} if {[file extension $filename]==".h"} { set declpattern " *$declpattern" } @@ -287,6 +287,7 @@ foreach file { complete.c main.c + notify.c fts3.c fts3_expr.c