diff --git a/Makefile.in b/Makefile.in index 5b17c0e426..59a62d0618 100644 --- a/Makefile.in +++ b/Makefile.in @@ -118,9 +118,14 @@ LDFLAGS.dlopen = @LDFLAGS_DLOPEN@ LDFLAGS.readline = @LDFLAGS_READLINE@ CFLAGS.readline = @CFLAGS_READLINE@ LDFLAGS.icu = @LDFLAGS_ICU@ +LDFLAGS.rt = @LDFLAGS_RT@ CFLAGS.icu = @CFLAGS_ICU@ LDFLAGS.libsqlite3.soname = @LDFLAGS_LIBSQLITE3_SONAME@ # soname: see https://sqlite.org/src/forumpost/5a3b44f510df8ded +LDFLAGS.libsqlite3.os-specific = @LDFLAGS_MAC_CVERSION@ @LDFLAGS_OUT_IMPLIB@ +# os-specific: see +# - https://sqlite.org/forum/forumpost/9dfd5b8fd525a5d7 +# - https://sqlite.org/forum/forumpost/0c7fc097b2 ENABLE_SHARED = @ENABLE_SHARED@ ENABLE_STATIC = @ENABLE_STATIC@ HAVE_WASI_SDK = @HAVE_WASI_SDK@ @@ -323,9 +328,6 @@ distclean-autosetup: clean rm -f $(TOP)/tool/emcc.sh rm -f libsqlite3*$(T.dll) rm -f jimsh0* -# -if [ -f ext/wasm/GNUmakefile ]; then \ -# gmake --no-print-directory --ignore-errors -C ext/wasm distclean; \ -# fi >/dev/null 2>&1; true distclean: distclean-autosetup # @@ -335,4 +337,5 @@ distclean: distclean-autosetup version-info$(T.exe): $(TOP)/tool/version-info.c Makefile sqlite3.h $(T.link) $(ST_OPT) -o $@ $(TOP)/tool/version-info.c +IS_CROSS_COMPILING = @IS_CROSS_COMPILING@ include $(TOP)/main.mk diff --git a/Makefile.msc b/Makefile.msc index 78b1d7ee22..09e860aaac 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -994,10 +994,14 @@ TCLLIBS = !ENDIF !IFNDEF LIBTCLSTUB -!IF EXISTS("$(TCLLIBDIR)\tclstub$(TCLSUFFIX).lib") -LIBTCLSTUB = tclstub$(TCLSUFFIX).lib -!ELSE +!IF EXISTS("$(TCLLIBDIR)\tclstub$(TCLVERSION)$(TCLSUFFIX).lib") LIBTCLSTUB = tclstub$(TCLVERSION)$(TCLSUFFIX).lib +!ELSEIF EXISTS("$(TCLLIBDIR)\tclstub$(TCLSUFFIX).lib") +LIBTCLSTUB = tclstub$(TCLSUFFIX).lib +!ELSEIF EXISTS("$(TCLLIBDIR)\tclstub$(TCLVERSION).lib") +LIBTCLSTUB = tclstub$(TCLVERSION).lib +!ELSE +LIBTCLSTUB = tclstub.lib !ENDIF !ENDIF @@ -1898,7 +1902,10 @@ tclextension-uninstall: $(TCLSH_CMD) $(TOP)\tool\buildtclext.tcl --uninstall tclextension-list: - $(TCLSH_CMD) $(TOP)\tool\buildtclext.tcl --info + @ $(TCLSH_CMD) $(TOP)\tool\buildtclext.tcl --info + +tclextension-verify: sqlite3.h + @ $(TCLSH_CMD) $(TOP)\tool\buildtclext.tcl --version-check # <> @@ -2337,7 +2344,7 @@ parse.c: $(TOP)\src\parse.y lemon.exe .\lemon.exe $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS) $(OPTS) -S parse.y $(SQLITE3H): $(TOP)\src\sqlite.h.in $(TOP)\manifest mksourceid.exe $(TOP)\VERSION $(JIM_TCLSH) - $(JIM_TCLSH) $(TOP)\tool\mksqlite3h.tcl "$(TOP:\=/)" > $(SQLITE3H) $(MKSQLITE3H_ARGS) + $(JIM_TCLSH) $(TOP)\tool\mksqlite3h.tcl "$(TOP:\=/)" -o $(SQLITE3H) $(MKSQLITE3H_ARGS) sqlite3ext.h: .target_source !IF $(USE_STDCALL)!=0 || $(FOR_WIN10)!=0 @@ -2401,7 +2408,7 @@ SHELL_DEP = $(SHELL_DEP) $(TOP)\ext\misc\zipfile.c !ENDIF shell.c: $(SHELL_DEP) $(TOP)\tool\mkshellc.tcl $(JIM_TCLSH) - $(JIM_TCLSH) $(TOP)\tool\mkshellc.tcl > shell.c + $(JIM_TCLSH) $(TOP)\tool\mkshellc.tcl shell.c zlib: pushd $(ZLIBDIR) && $(MAKE) /f win32\Makefile.msc clean $(ZLIBLIB) && popd @@ -2802,6 +2809,8 @@ tcl-env: @echo LIBTCL = $(LIBTCL) @echo LIBTCLSTUB = $(LIBTCLSTUB) @echo TCLSH_CMD = $(TCLSH_CMD) + @echo JIM_TCLSH = $(JIM_TCLSH) + @echo VISUALSTUDIOVERSION = $(VISUALSTUDIOVERSION) LSMDIR=$(TOP)\ext\lsm1 !INCLUDE $(LSMDIR)\Makefile.msc @@ -2812,7 +2821,8 @@ moreclean: clean clean: del /Q *.exp *.lo *.ilk *.lib *.obj *.ncb *.pdb *.sdf *.suo 2>NUL - del /Q *.bsc *.def *.cod *.da *.bb *.bbg *.vc gmon.out 2>NUL + del /Q *.bsc *.cod *.da *.bb *.bbg *.vc gmon.out 2>NUL + del /Q sqlite3.def tclsqlite3.def 2>NUL del /Q $(SQLITE3EXE) $(SQLITE3DLL) Replace.exe 2>NUL # <> del /Q $(SQLITE3TCLDLL) pkgIndex.tcl 2>NUL @@ -2846,4 +2856,5 @@ clean: del /Q fts5.* fts5parse.* 2>NUL del /Q lsm.h lsm1.c 2>NUL del /q src-verify.exe 2>NUL + del /q jimsh.exe jimsh0.exe 2>NUL # <> diff --git a/VERSION b/VERSION index 7dd5eda815..ca25ff637a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.48.0 +3.50.0 diff --git a/auto.def b/auto.def index c9aa0cb9d1..6274d79b68 100644 --- a/auto.def +++ b/auto.def @@ -12,18 +12,17 @@ # # JimTCL: https://jim.tcl.tk # -use cc cc-db cc-shared cc-lib proj pkg-config +use sqlite-config + +if {[string first " " $autosetup(srcdir)] != -1} { + user-error "The pathname of the source tree\ + may not contain space characters" +} +if {[string first " " $autosetup(builddir)] != -1} { + user-error "The pathname of the build directory\ + may not contain space characters" +} -# $DUMP_DEFINES_TXT is the file emitted by --dump-defines, intended -# only for build debugging and not part of the public build interface. -set DUMP_DEFINES_TXT ./config.defines.txt -# $DUMP_DEFINES_JSON is the autosetup counterpart of the historical -# "DEFS" var which was generated by the autotools in the pre-processed -# autotools builds (but not in the canonical tree). Generation of this -# file is disabled (via an empty file name) until/unless someone -# voices a specific interest in it. The original motivating use case -# is handled fine by sqlite_cfg.h. -set DUMP_DEFINES_JSON ""; #./config.defines.json ######################################################################## # Regarding flag compatibility with the historical autotool configure @@ -34,10 +33,9 @@ set DUMP_DEFINES_JSON ""; #./config.defines.json # flags compared to the historical autotools build. The differences # are documented here: # -# 1) --debug is used by autosetup itself, so we have to rename it to -# --with-debug. We cannot use --enable-debug because that is, for -# autosetup, an alias for --debug=1. Alternately, we can patch -# autosetup to use --autosetup-debug for its own purposes instead. +# 1) --debug is used by autosetup itself, but we patch it because +# decades of muscle memory expect --debug to apply to this code, +# not the configure script (details are in autosetup/README.md). # # 2) In autosetup, all flags starting with (--enable, --disable) are # forced to be booleans and receive special handling in how they're @@ -199,9 +197,12 @@ set flags { test-status => {Enable status of tests} gcov=0 => {Enable coverage testing using gcov} linemacros => {Enable #line macros in the amalgamation} - dump-defines=0 => {Dump autosetup defines to $DUMP_DEFINES_TXT (for build debugging)} dynlink-tools => {Dynamically link libsqlite3 to certain tools which normally statically embed it.} + dump-defines=0 => {Dump autosetup defines to $::sqliteConfig(dump-defines-txt) (for build debugging)} + # + # soname:=legacy => + # --soname has a long story behind it: https://sqlite.org/src/forumpost/5a3b44f510df8ded {SONAME for libsqlite3.so. "none", or not using this flag, sets no soname. "legacy" sets it to its historical value of libsqlite3.so.0. A value matching the glob "libsqlite3.*" sets @@ -209,125 +210,21 @@ set flags { suffix which gets applied to "libsqlite3.so.", e.g. --soname=9.10 equates to "libsqlite3.so.9.10". } - # --soname has a long story behind it: https://sqlite.org/src/forumpost/5a3b44f510df8ded - # + out-implib=0 => + {Enable use of --out-implib linker flag to generate an "import library" for the DLL} + # } -if {"" ne $DUMP_DEFINES_JSON} { +if {"" ne $::sqliteConfig(dump-defines-json)} { lappend flags \ defines-json-include-lowercase=0 \ - => {Include lower-case defines (primarily system paths) in $DUMP_DEFINES_JSON} + => {Include lower-case defines (primarily system paths) in $::sqliteConfig(dump-defines-json)} } options [subst -nobackslashes -nocommands $flags] unset flags +sqlite-post-options-init -# -# Carry values from hidden --flag aliases over to their canonical flag -# forms. -# -proj-xfer-options-aliases { - with-readline-inc => with-readline-cflags - with-readline-lib => with-readline-ldflags - with-debug => debug -} - -set srcdir $::autosetup(srcdir) -set PACKAGE_VERSION [proj-file-content -trim $srcdir/VERSION] -define PACKAGE_NAME "sqlite" -define PACKAGE_URL {https://sqlite.org} -define PACKAGE_VERSION $PACKAGE_VERSION -define PACKAGE_STRING "[get-define PACKAGE_NAME] $PACKAGE_VERSION" -define PACKAGE_BUGREPORT [get-define PACKAGE_URL]/forum -msg-result "Source dir = $srcdir" -msg-result "Build dir = $::autosetup(builddir)" -msg-result "Configuring SQLite version $PACKAGE_VERSION" - -apply {{} { - # - # SQLITE_AUTORECONFIG contains make target rules for re-running the - # configure script with the same arguments it was initially invoked - # with. This can be used to automatically reconfigure - # - proc squote {arg} { - # Wrap $arg in single-quotes if it looks like it might need that - # to avoid mis-handling as a shell argument. We assume that $arg - # will never contain any single-quote characters. - if {[string match {*[ &;$*"]*} $arg]} { return '$arg' } - return $arg - } - define-append SQLITE_AUTORECONFIG cd [squote $::autosetup(builddir)] && [squote $::srcdir/configure] - #{*}$::autosetup(argv) breaks with --flag='val with spaces', so... - foreach arg $::autosetup(argv) { - define-append SQLITE_AUTORECONFIG [squote $arg] - } - rename squote "" -}} - -# Are we cross-compiling? -set isCrossCompiling [proj-is-cross-compiling] - -define OPT_FEATURE_FLAGS {} ; # -DSQLITE_OMIT/ENABLE flags. -define OPT_SHELL {} ; # Feature-related CFLAGS for the sqlite3 CLI app -######################################################################## -# Adds $args, if not empty, to OPT_FEATURE_FLAGS. If the first arg is -# -shell then it strips that arg and passes the remaining args the -# sqlite-add-shell-opt in addition to adding them to -# OPT_FEATURE_FLAGS. -proc sqlite-add-feature-flag {args} { - set shell "" - if {"-shell" eq [lindex $args 0]} { - set args [lassign $args shell] - } - if {"" ne $args} { - if {"" ne $shell} { - sqlite-add-shell-opt {*}$args - } - define-append OPT_FEATURE_FLAGS {*}$args - } -} -# Appends $args, if not empty, to OPT_SHELL. -proc sqlite-add-shell-opt {args} { - if {"" ne $args} { - define-append OPT_SHELL {*}$args - } -} - -# Pass msg-debug=1 to configure to enable obnoxiously loud output from -# msg-debug. -set msgDebugEnabled [proj-val-truthy [get-env msg-debug 0]] -proc msg-debug {msg} { - if {$::msgDebugEnabled} { - puts stderr [proj-bold "** DEBUG: $msg"] - } -} - -proj-file-extensions -if {".exe" eq [get-define TARGET_EXEEXT]} { - define SQLITE_OS_UNIX 0 - define SQLITE_OS_WIN 1 -} else { - define SQLITE_OS_UNIX 1 - define SQLITE_OS_WIN 0 -} - -######### -# Programs needed -cc-check-tools ld ar ; # must come before [sqlite-check-wasi-sdk] -if {"" eq [proj-bin-define install]} { - proj-warn "Cannot find install binary, so 'make install' will not work." - define BIN_INSTALL false -} - -######################################################################## -# We differentiate between two C compilers: the one used for binaries -# which are to run on the build system (in autosetup it's called -# CC_FOR_BUILD and in Makefile.in it's $(B.cc)) and the one used for -# compiling binaries for the target system (CC a.k.a. $(T.cc)). -# Normally they're the same, but they will differ when -# cross-compiling. -define CFLAGS [proj-get-env CFLAGS {-g -O2}] -define BUILD_CFLAGS [proj-get-env BUILD_CFLAGS {-g}] - +sqlite-setup-default-cflags proj-if-opt-truthy dev { # --enable-dev needs to come early so that the downstream tests # which check for the following flags use their updated state. @@ -335,170 +232,23 @@ proj-if-opt-truthy dev { proj-opt-set debug 1 proj-opt-set amalgamation 0 define CFLAGS [get-env CFLAGS {-O0 -g}] + # -------------^^^^^^^ intentionally using [get-env] instead of + # [proj-get-env] here because [sqlite-setup-default-cflags] uses + # [proj-get-env] and we want this to supercede that. } -######################################################################## -# Handle --with-wasi-sdk=DIR -# -# This must be run relatively early on because it may change the -# toolchain and disable a number of config options. -proc sqlite-check-wasi-sdk {} { - set wasiSdkDir [opt-val with-wasi-sdk] ; # ??? [lindex [opt-val with-wasi-sdk] end] - define HAVE_WASI_SDK 0 - if {$wasiSdkDir eq ""} { - return 0 - } elseif {$::isCrossCompiling} { - proj-fatal "Cannot combine --with-wasi-sdk with cross-compilation" - } - msg-result "Checking WASI SDK directory \[$wasiSdkDir]... " - #puts "prefix = [prefix $wasiSdkDir/bin {clang ld}]" - proj-affirm-files-exist -v {*}[prefix "$wasiSdkDir/bin/" {clang wasm-ld ar}] - define HAVE_WASI_SDK 1 - define WASI_SDK_DIR $wasiSdkDir - # Disable numerous options which we know either can't work or are - # not useful in this build... - msg-result "Using wasi-sdk clang. Disabling CLI shell modifying config flags:" - # Boolean (--enable-/--disable-) flags which must be switched off: - foreach opt { - dynlink-tools - editline - gcov - icu-collations - load-extension - readline - shared - tcl - threadsafe - } { - if {[opt-bool $opt]} { - msg-result " --disable-$opt" - proj-opt-set $opt 0 - } - } - # Non-boolean flags which need to be cleared: - foreach opt { - with-emsdk - with-icu-config - with-icu-ldflags - with-icu-cflags - with-linenoise - with-tcl - } { - if {[proj-opt-was-provided $opt]} { - msg-result " removing --$opt" - proj-opt-set $opt "" - } - } - # Remember that we now have a discrepancy beteween - # $::isCrossCompiling and [proj-is-cross-compiling]. - set ::isCrossCompiling 1 - - # - # Changing --host and --target have no effect here except to - # possibly cause confusion. Autosetup has finished processing them - # by this point. - # - # host_alias=wasm32-wasi - # target=wasm32-wasi - # - # Merely changing CC, LD, and AR to the wasi-sdk's is enough to get - # sqlite3.o building in WASM format. - # - define CC "${wasiSdkDir}/bin/clang" - define LD "${wasiSdkDir}/bin/wasm-ld" - define AR "${wasiSdkDir}/bin/ar" - #define STRIP "${wasiSdkDir}/bin/strip" - return 1 -}; # sqlite-check-wasi-sdk -sqlite-check-wasi-sdk - -######################################################################## -# --dynlink-tools tells the build to dynamically link certain binaries -# to libsqlite3.so instead of embedding a copy of the amalgamation. -define LINK_TOOLS_DYNAMICALLY [proj-opt-was-provided dynlink-tools] +sqlite-check-common-bins ;# must come before [sqlite-handle-wasi-sdk] +sqlite-handle-wasi-sdk ;# must run relatively early, as it changes the environment +sqlite-check-common-system-deps # # Enable large file support (if special flags are necessary) +# define HAVE_LFS 0 if {[opt-bool largefile]} { cc-check-lfs } -# -# Check for needed/wanted data types -cc-with {-includes stdint.h} \ - {cc-check-types int8_t int16_t int32_t int64_t intptr_t \ - uint8_t uint16_t uint32_t uint64_t uintptr_t} - -# -# Check for needed/wanted functions -cc-check-functions gmtime_r isnan localtime_r localtime_s \ - malloc_usable_size strchrnul usleep utime pread pread64 pwrite pwrite64 - -proj-check-function-in-lib fdatasync rt -define LDFLAGS_FDATASYNC [get-define lib_fdatasync] -undefine lib_fdatasync - -# -# Check for needed/wanted headers -cc-check-includes \ - sys/types.h sys/stat.h dlfcn.h unistd.h \ - stdlib.h malloc.h memory.h \ - string.h strings.h \ - inttypes.h - -if {[cc-check-includes zlib.h] && [proj-check-function-in-lib deflate z]} { - # TODO? port over the more sophisticated zlib search from the fossil auto.def - define HAVE_ZLIB 1 - define LDFLAGS_ZLIB -lz - sqlite-add-shell-opt -DSQLITE_HAVE_ZLIB=1 -} else { - define HAVE_ZLIB 0 - define LDFLAGS_ZLIB "" -} - -proj-check-rpath ; # Determine proper rpath-handling flag - -######################################################################## -# "soname" for libsqlite3.so. See discussion at: -# https://sqlite.org/src/forumpost/5a3b44f510df8ded -apply {{} { - define LDFLAGS_LIBSQLITE3_SONAME "" - if {[proj-opt-was-provided soname]} { - set soname [join [opt-val soname] ""] - } else { - # Enabling soname breaks linking for the --dynlink-tools feature, - # and this project has no direct use for soname, so default to - # none. Package maintainers, on the other hand, like to have an - # soname. - set soname none - } - switch -exact -- $soname { - none - "" { return 0 } - auto { set soname libsqlite3.so.3 } - legacy { set soname libsqlite3.so.0 } - default { - if {[string match libsqlite3.* $soname]} { - # use it as-is - } else { - # Assume it's a suffix - set soname "libsqlite3.so.${soname}" - } - } - } - msg-debug "soname=$soname" - if {[proj-check-soname $soname]} { - define LDFLAGS_LIBSQLITE3_SONAME [get-define LDFLAGS_SONAME_PREFIX]$soname - msg-result "Setting SONAME using: [get-define LDFLAGS_LIBSQLITE3_SONAME]" - } elseif {[proj-opt-was-provided soname]} { - # --soname was explicitly requested but not available, so fail fatally - proj-fatal "This environment does not support SONAME." - } else { - # --soname was not explicitly requested but not available, so just warn - msg-result "This environment does not support SONAME." - } -}} - proj-define-for-opt shared ENABLE_SHARED "Build shared library?" if {![proj-define-for-opt static ENABLE_STATIC \ @@ -516,924 +266,18 @@ proj-define-for-opt test-status TSTRNNR_OPTS \ proj-define-for-opt linemacros AMALGAMATION_LINE_MACROS \ "Use #line macros in the amalgamation:" -msg-checking "SQLITE_DEBUG build? " -proj-if-opt-truthy debug { - define SQLITE_DEBUG 1 - define TARGET_DEBUG {-g -DSQLITE_DEBUG=1 -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE -O0 -Wall} - proj-opt-set memsys5 - msg-result yes -} { - define TARGET_DEBUG {-DNDEBUG} - msg-result no -} +define LINK_TOOLS_DYNAMICALLY [proj-opt-was-provided dynlink-tools] -######################################################################## -# TCL... -# -# sqlite-check-tcl performs most of the --with-tcl and --with-tclsh -# handling. Some related bits and pieces are performed before and -# after that function is called. -# -# Important [define]'d vars: -# -# - HAVE_TCL indicates whether we have a tclsh suitable for building -# the TCL SQLite extension and, by extension, the testing -# infrastructure. This must only be 1 for environments where -# tclConfig.sh can be found. -# -# - TCLSH_CMD is the path to the canonical tclsh or "". It never -# refers to jimtcl. -# -# - TCL_CONFIG_SH is the path to tclConfig.sh or "". -# -# - TCLLIBDIR is the dir to which libtclsqlite3 gets installed. -# -# - BTCLSH = the path to the tcl interpreter used for in-tree code -# generation. It may be jimtcl or the canonical tclsh but may not -# be empty - this tree requires TCL to generated numerous -# components. -# -# If --tcl or --with-tcl are provided but no TCL is found, this -# function fails fatally. If they are not explicitly provided then -# failure to find TCL is not fatal but a loud warning will be emitted. -# -proc sqlite-check-tcl {} { - rename sqlite-check-tcl "" - define TCLSH_CMD false ; # Significant is that it exits with non-0 - define HAVE_TCL 0 ; # Will be enabled via --tcl or a successful search - define TCLLIBDIR "" ; # Installation dir for TCL extension lib - define TCL_CONFIG_SH ""; # full path to tclConfig.sh - - # Clear out all vars which would be set by tclConfigToAutoDef.sh, so - # that the late-config validation of @VARS@ works even if - # --disable-tcl is used. - foreach k {TCL_INCLUDE_SPEC TCL_LIB_SPEC TCL_STUB_LIB_SPEC TCL_EXEC_PREFIX TCL_VERSION} { - define $k "" - } - - file delete -force ".tclenv.sh"; # ensure no stale state from previous configures. - if {![opt-bool tcl]} { - proj-indented-notice { - NOTE: TCL is disabled via --disable-tcl. This means that none - of the TCL-based components will be built, including tests - and sqlite3_analyzer. - } - return - } - # TODO: document the steps this is taking. - global srcdir - msg-result "Checking for a suitable tcl... " - proj-assert [proj-opt-truthy tcl] - set use_tcl 1 - set with_tclsh [opt-val with-tclsh] - set with_tcl [opt-val with-tcl] - if {"prefix" eq $with_tcl} { - set with_tcl [get-define prefix] - } - msg-debug "sqlite-check-tcl: use_tcl ${use_tcl}" - msg-debug "sqlite-check-tcl: with_tclsh=${with_tclsh}" - msg-debug "sqlite-check-tcl: with_tcl=$with_tcl" - if {"" eq $with_tclsh && "" eq $with_tcl} { - # If neither --with-tclsh nor --with-tcl are provided, try to find - # a workable tclsh. - set with_tclsh [proj-first-bin-of tclsh9.0 tclsh8.6 tclsh] - msg-debug "sqlite-check-tcl: with_tclsh=${with_tclsh}" - } - - set doConfigLookup 1 ; # set to 0 to test the tclConfig.sh-not-found cases - if {"" ne $with_tclsh} { - # --with-tclsh was provided or found above. Validate it and use it - # to trump any value passed via --with-tcl=DIR. - if {![file isfile $with_tclsh]} { - proj-fatal "TCL shell $with_tclsh is not a file" - } elseif {![file-isexec $with_tclsh]} { - proj-fatal "TCL shell $with_tclsh is not executable" - } else { - define TCLSH_CMD $with_tclsh - #msg-result "Using tclsh: $with_tclsh" - } - if {$doConfigLookup && - [catch {exec $with_tclsh $srcdir/tool/find_tclconfig.tcl} result] == 0} { - set with_tcl $result - } - if {"" ne $with_tcl && [file isdir $with_tcl]} { - msg-result "$with_tclsh recommends the tclConfig.sh from $with_tcl" - } else { - proj-warn "$with_tclsh is unable to recommend a tclConfig.sh" - set use_tcl 0 - } - } - set cfg "" - set tclSubdirs {tcl9.0 tcl8.6 lib} - while {$use_tcl} { - if {"" ne $with_tcl} { - # Ensure that we can find tclConfig.sh under ${with_tcl}/... - if {$doConfigLookup} { - if {[file readable "${with_tcl}/tclConfig.sh"]} { - set cfg "${with_tcl}/tclConfig.sh" - } else { - foreach i $tclSubdirs { - if {[file readable "${with_tcl}/$i/tclConfig.sh"]} { - set cfg "${with_tcl}/$i/tclConfig.sh" - break - } - } - } - } - if {"" eq $cfg} { - proj-fatal "No tclConfig.sh found under ${with_tcl}" - } - } else { - # If we have not yet found a tclConfig.sh file, look in - # $libdir which is set automatically by autosetup or by the - # --prefix command-line option. See - # https://sqlite.org/forum/forumpost/e04e693439a22457 - set libdir [get-define libdir] - if {[file readable "${libdir}/tclConfig.sh"]} { - set cfg "${libdir}/tclConfig.sh" - } else { - foreach i $tclSubdirs { - if {[file readable "${libdir}/$i/tclConfig.sh"]} { - set cfg "${libdir}/$i/tclConfig.sh" - break - } - } - } - if {![file readable $cfg]} { - break - } - } - msg-result "Using tclConfig.sh: $cfg" - break - } - define TCL_CONFIG_SH $cfg - # Export a subset of tclConfig.sh to the current TCL-space. If $cfg - # is an empty string, this emits empty-string entries for the - # various options we're interested in. - eval [exec "${srcdir}/tool/tclConfigShToAutoDef.sh" "$cfg"] - - if {"" eq $with_tclsh && $cfg ne ""} { - # We have tclConfig.sh but no tclsh. Attempt to locate a tclsh - # based on info from tclConfig.sh. - proj-assert {"" ne [get-define TCL_EXEC_PREFIX]} - set with_tclsh [get-define TCL_EXEC_PREFIX]/bin/tclsh[get-define TCL_VERSION] - if {![file-isexec $with_tclsh]} { - set with_tclsh2 [get-define TCL_EXEC_PREFIX]/bin/tclsh - if {![file-isexec $with_tclsh2]} { - proj-warn "Cannot find a usable tclsh (tried: $with_tclsh $with_tclsh2)" - } else { - set with_tclsh $with_tclsh2 - } - } - } - define TCLSH_CMD $with_tclsh - if {$use_tcl} { - # Set up the TCLLIBDIR - # - # 2024-10-28: calculation of TCLLIBDIR is now done via the shell - # in main.mk (search it for T.tcl.env.sh) so that - # static/hand-written makefiles which import main.mk do not have - # to define that before importing main.mk. Even so, we export - # TCLLIBDIR from here, which will cause the canonical makefile to - # use this one rather than to re-calculate it at make-time. - set tcllibdir [get-env TCLLIBDIR ""] - if {"" eq $tcllibdir} { - # Attempt to extract TCLLIBDIR from TCL's $auto_path - if {"" ne $with_tclsh && - [catch {exec echo "puts stdout \$auto_path" | "$with_tclsh"} result] == 0} { - foreach i $result { - if {[file isdir $i]} { - set tcllibdir $i/sqlite3 - break - } - } - } else { - proj-warn "Cannot determine TCLLIBDIR." - # The makefile will fail fatally in this case if a target is - # invoked which requires TCLLIBDIR. - } - } - #if {"" ne $tcllibdir} { msg-result "TCLLIBDIR = ${tcllibdir}"; } - define TCLLIBDIR $tcllibdir - }; # find TCLLIBDIR - - if {[file-isexec $with_tclsh]} { - msg-result "Using tclsh: $with_tclsh" - if {$cfg ne ""} { - define HAVE_TCL 1 - } else { - proj-warn "Found tclsh but no tclConfig.sh." - } - } - show-notices - # If TCL is not found: if it was explicitly requested then fail - # fatally, else just emit a warning. If we can find the APIs needed - # to generate a working JimTCL then that will suffice for build-time - # TCL purposes (see: proc sqlite-determine-codegen-tcl). - if {![get-define HAVE_TCL] && - ([proj-opt-was-provided tcl] || [proj-opt-was-provided with-tcl])} { - proj-fatal "TCL support was requested but no tclConfig.sh could be found." - } - if {"" eq $cfg} { - proj-assert {0 == [get-define HAVE_TCL]} - proj-indented-notice { - WARNING: Cannot find a usable tclConfig.sh file. Use - --with-tcl=DIR to specify a directory where tclConfig.sh can be - found. SQLite does not use TCL internally, but some optional - components require TCL, including tests and sqlite3_analyzer. - } - } -}; # sqlite-check-tcl -sqlite-check-tcl - -######################################################################## -# sqlite-determine-codegen-tcl checks which TCL to use as a code -# generator. By default, prefer jimsh simply because we have it -# in-tree (it's part of autosetup) unless --with-tclsh=X is used, in -# which case prefer X. -# -# Returns the human-readable name of the TCL it selects. Fails fatally -# if it cannot detect a TCL appropriate for code generation. -# -# Defines: -# -# - BTCLSH = the TCL shell used for code generation. It may set this -# to an unexpanded makefile var name. -# -# - CFLAGS_JIMSH = any flags needed for buildng a BTCLSH-compatible -# jimsh. The defaults may be passed on to configure as -# CFLAGS_JIMSH=... -set useJimForCodeGen 0 ; # Set to 1 when using jimsh for code generation. - # May affect later decisions. -proc sqlite-determine-codegen-tcl {} { - rename sqlite-determine-codegen-tcl "" - msg-result "Checking for TCL to use for code generation... " - define CFLAGS_JIMSH [proj-get-env CFLAGS_JIMSH {-O1}] - set cgtcl [opt-val with-tclsh jimsh] - if {"jimsh" ne $cgtcl} { - # When --with-tclsh=X is used, use that for all TCL purposes, - # including in-tree code generation, per developer request. - define BTCLSH "\$(TCLSH_CMD)" - return $cgtcl - } - set flagsToRestore {CC CFLAGS AS_CFLAGS CPPFLAGS AS_CPPFLAGS LDFLAGS LINKFLAGS LIBS CROSS} - define-push $flagsToRestore { - # We have to swap CC to CC_FOR_BUILD for purposes of the various - # [cc-...] tests below. Recall that --with-wasi-sdk may have - # swapped out CC with one which is not appropriate for this block. - # Per consulation with autosetup's creator, doing this properly - # requires us to [define-push] the whole $flagsToRestore list - # (plus a few others which are not relevant in this tree). - # - # These will get set to their previous values at the end of this - # block. - foreach flag $flagsToRestore {define $flag ""} - define CC [get-define CC_FOR_BUILD] - # These headers are technically optional for JimTCL but necessary if - # we want to use it for code generation: - set sysh [cc-check-includes dirent.h sys/time.h] - # jimsh0.c hard-codes #define's for HAVE_DIRENT_H and - # HAVE_SYS_TIME_H on the platforms it supports, so we do not - # need to add -D... flags for those. We check for them here only - # so that we can avoid the situation that we later, at - # make-time, try to compile jimsh but it then fails due to - # missing headers (i.e. fail earlier rather than later). - if {$sysh && [cc-check-functions realpath]} { - define-append CFLAGS_JIMSH -DHAVE_REALPATH - define BTCLSH "\$(JIMSH)" - set ::useJimForCodeGen 1 - } elseif {$sysh && [cc-check-functions _fullpath]} { - # _fullpath() is a Windows API. It's not entirely clear - # whether we need to add {-DHAVE_SYS_TIME_H -DHAVE_DIRENT_H} - # to CFLAGS_JIMSH in this case. On MinGW32 we definitely do - # not want to because it already hard-codes them. On _MSC_VER - # builds it does not. - define-append CFLAGS_JIMSH -DHAVE__FULLPATH - define BTCLSH "\$(JIMSH)" - set ::useJimForCodeGen 1 - } elseif {[file-isexec [get-define TCLSH_CMD]]} { - set cgtcl [get-define TCLSH_CMD] - define BTCLSH "\$(TCLSH_CMD)" - } else { - # One last-ditch effort to find TCLSH_CMD: use info from - # tclConfig.sh to try to find a tclsh - if {"" eq [get-define TCLSH_CMD]} { - set tpre [get-define TCL_EXEC_PREFIX] - if {"" ne $tpre} { - set tv [get-define TCL_VERSION] - if {[file-isexec "${tpre}/bin/tclsh${tv}"]} { - define TCLSH_CMD "${tpre}/bin/tclsh${tv}" - } elseif {[file-isexec "${tpre}/bin/tclsh"]} { - define TCLSH_CMD "${tpre}/bin/tclsh" - } - } - } - set cgtcl [get-define TCLSH_CMD] - if {![file-isexec $cgtcl]} { - proj-fatal "Cannot find a tclsh to use for code generation." - } - define BTCLSH "\$(TCLSH_CMD)" - } - }; # CC swap-out - return $cgtcl -}; # sqlite-determine-codegen-tcl -msg-result "TCL for code generation: [sqlite-determine-codegen-tcl]" -# /TCL -######################################################################## - -######################################################################## -# Thread safety? -msg-checking "Support threadsafe operation? " -proj-if-opt-truthy threadsafe { - msg-result yes - sqlite-add-feature-flag -DSQLITE_THREADSAFE=1 - if {![proj-check-function-in-lib pthread_create pthread] - || ![proj-check-function-in-lib pthread_mutexattr_init pthread]} { - user-error "Missing required pthread bits" - } - define LDFLAGS_PTHREAD [get-define lib_pthread_create] - undefine lib_pthread_create - # Recall that LDFLAGS_PTHREAD might be empty even if pthreads if - # found because it's in -lc on some platforms. -} { - msg-result no - sqlite-add-feature-flag -DSQLITE_THREADSAFE=0 - define LDFLAGS_PTHREAD "" -} - -######################################################################## -# Do we want temporary databases in memory? -# -# The test fixture likes to set SQLITE_TEMP_STORE on its own, so do -# not set that feature flag unless it was explicitly provided to the -# configure script. -if {[proj-opt-was-provided with-tempstore]} { - apply {{} { - set ts [opt-val with-tempstore no] - set tsn 1 - msg-checking "Use an in-RAM database for temporary tables? " - switch -exact -- $ts { - never { set tsn 0 } - no { set tsn 1 } - yes { set tsn 2 } - always { set tsn 3 } - default { - user-error "Invalid --with-tempstore value '$ts'. Use one of: never, no, yes, always" - } - } - msg-result $ts - sqlite-add-feature-flag -DSQLITE_TEMP_STORE=$tsn - }} -} - -######################################################################## -# sqlite-check-line-editing jumps through proverbial hoops to try to -# find a working line-editing library, setting: -# -# - HAVE_READLINE to 0 or 1 -# - HAVE_LINENOISE to 0, 1, or 2 -# - HAVE_EDITLINE to 0 or 1 -# -# Only one of ^^^ those will be set to non-0. -# -# - LDFLAGS_READLINE = linker flags or empty string -# -# - CFLAGS_READLINE = compilation flags for clients or empty string. -# -# Note that LDFLAGS_READLINE and CFLAGS_READLINE may refer to -# linenoise or editline, not necessarily libreadline. In some cases -# it will set HAVE_READLINE=1 when it's really using editline, for -# reasons described in this function's comments. -# -# Returns a string describing which line-editing approach to use, or -# "none" if no option is available. -# -# Order of checks: -# -# 1) --with-linenoise trumps all others and skips all of the -# complexities involved with the remaining options. -# -# 2) --editline trumps --readline -# -# 3) --disable-readline trumps --readline -# -# 4) Default to automatic search for optional readline -# -# 5) Try to find readline or editline. If it's not found AND the -# corresponding --FEATURE flag was explicitly given, fail fatally, -# else fail silently. -proc sqlite-check-line-editing {} { - rename sqlite-check-line-editing "" - msg-result "Checking for line-editing capability..." - define HAVE_READLINE 0 - define HAVE_LINENOISE 0 - define HAVE_EDITLINE 0 - define LDFLAGS_READLINE "" - define CFLAGS_READLINE "" - set failIfNotFound 0 ; # Gets set to 1 for explicit --FEATURE requests - # so that we know whether to fail fatally or not - # if the library is not found. - set libsForReadline {readline edit} ; # -l names to check for readline(). - # The libedit check changes this. - set editLibName "readline" ; # "readline" or "editline" - set editLibDef "HAVE_READLINE" ; # "HAVE_READLINE" or "HAVE_EDITLINE" - set dirLn [opt-val with-linenoise] - if {"" ne $dirLn} { - # Use linenoise from a copy of its sources (not a library)... - if {![file isdir $dirLn]} { - proj-fatal "--with-linenoise value is not a directory" - } - set lnH $dirLn/linenoise.h - if {![file exists $lnH] } { - proj-fatal "Cannot find linenoise.h in $dirLn" - } - set lnC "" - set lnCOpts {linenoise-ship.c linenoise.c} - foreach f $lnCOpts { - if {[file exists $dirLn/$f]} { - set lnC $dirLn/$f - break; - } - } - if {"" eq $lnC} { - proj-fatal "Cannot find any of $lnCOpts in $dirLn" - } - set flavor "" - set lnVal [proj-which-linenoise $lnH] - switch -- $lnVal { - 1 { set flavor "antirez" } - 2 { set flavor "msteveb" } - default { - proj-fatal "Cannot determine the flavor of linenoise from $lnH" - } - } - define CFLAGS_READLINE "-I$dirLn $lnC" - define HAVE_LINENOISE $lnVal - sqlite-add-shell-opt -DHAVE_LINENOISE=$lnVal - if {$::useJimForCodeGen && 2 == $lnVal} { - define-append CFLAGS_JIMSH -DUSE_LINENOISE [get-define CFLAGS_READLINE] - user-notice "Adding linenoise support to jimsh." - } - return "linenoise ($flavor)" - } elseif {[opt-bool editline]} { - # libedit mimics libreadline and on some systems does not have its - # own header installed (instead, that of libreadline is used). - # - # shell.c historically expects HAVE_EDITLINE to be set for - # libedit, but it then expects to see , which - # some system's don't actually have despite having libedit. If we - # end up finding below, we will use - # -DHAVE_EDITLINE=1, else we will use -DHAVE_READLINE=1. In either - # case, we will link against libedit. - set failIfNotFound 1 - set libsForReadline {edit} - set editLibName editline - } elseif {![opt-bool readline]} { - msg-result "Readline support explicitly disabled with --disable-readline" - return "none" - } elseif {[proj-opt-was-provided readline]} { - # If an explicit --[enable-]readline was used, fail if it's not - # found, else treat the feature as optional. - set failIfNotFound 1 - } - - # Transform with-readline-header=X to with-readline-cflags=-I... - set v [opt-val with-readline-header] - proj-opt-set with-readline-header "" - if {"" ne $v} { - if {"auto" eq $v} { - proj-opt-set with-readline-cflags auto - } else { - set v [file dirname $v] - if {[string match */readline $v]} { - # Special case: if the path includes .../readline/readline.h, - # set the -I to one dir up from that because our sources - # #include or . - set v [file dirname $v] - } - proj-opt-set with-readline-cflags "-I$v" - } - } - - # Look for readline.h - set rlInc [opt-val with-readline-cflags auto] - if {"auto" eq $rlInc} { - set rlInc "" - if {$::isCrossCompiling} { - # ^^^ this check is derived from the legacy configure script. - proj-warn "Skipping check for readline.h because we're cross-compiling." - } else { - set dirs "[get-define prefix] /usr /usr/local /usr/local/readline /usr/contrib /mingw" - set subdirs "include/$editLibName" - if {"editline" eq $editLibName} { - lappend subdirs include/readline - # ^^^ editline, on some systems, does not have its own header, - # and uses libreadline's header. - } - lappend subdirs include - # ^^^ The dirs and subdirs lists are, except for the inclusion - # of $prefix and editline, from the legacy configure script - set rlInc [proj-search-for-header-dir readline.h \ - -dirs $dirs -subdirs $subdirs] - if {"" ne $rlInc} { - if {[string match */readline $rlInc]} { - set rlInc [file dirname $rlInc]; # shell #include's - } elseif {[string match */editline $rlInc]} { - set editLibDef HAVE_EDITLINE - set rlInc [file dirname $rlInc]; # shell #include's - } - set rlInc "-I${rlInc}" - } - } - } elseif {"" ne $rlInc && ![string match *-I* $rlInc]} { - proj-fatal "Argument to --with-readline-cflags is intended to be CFLAGS and contain -I..." - } - - # If readline.h was found/specified, look for lib(readline|edit)... - # - # This is not quite straightforward because both libreadline and - # libedit typically require some other library which (according to - # legacy autotools-generated tests) provides tgetent(3). On some - # systems that's built into libreadline/edit, on some (most?) its in - # lib[n]curses, and on some it's in libtermcap. - set rlLib "" - if {"" ne $rlInc} { - set rlLib [opt-val with-readline-ldflags] - if {"" eq $rlLib || "auto" eq $rlLib} { - set rlLib "" - set libTerm "" - if {[proj-check-function-in-lib tgetent "$editLibName ncurses curses termcap"]} { - # ^^^ that libs list comes from the legacy configure script ^^^ - set libTerm [get-define lib_tgetent] - undefine lib_tgetent - } - if {$editLibName eq $libTerm} { - set rlLib $libTerm - } elseif {[proj-check-function-in-lib readline $libsForReadline $libTerm]} { - set rlLib [get-define lib_readline] - lappend rlLib $libTerm - undefine lib_readline - } - } - } - - # If we found a library, configure the build to use it... - if {"" ne $rlLib} { - if {"editline" eq $editLibName && "HAVE_READLINE" eq $editLibDef} { - # Alert the user that, despite outward appearances, we won't be - # linking to the GPL'd libreadline. Presumably that distinction is - # significant for those using --editline. - proj-indented-notice { - NOTE: the local libedit but uses so we - will compile with -DHAVE_READLINE=1 but will link with - libedit. - } - } - set rlLib [join $rlLib] - set rlInc [join $rlInc] - define LDFLAGS_READLINE $rlLib - define CFLAGS_READLINE $rlInc - proj-assert {$editLibDef in {HAVE_READLINE HAVE_EDITLINE}} - proj-assert {$editLibName in {readline editline}} - sqlite-add-shell-opt -D${editLibDef}=1 - msg-result "Using $editLibName flags: $rlInc $rlLib" - # Check whether rl_completion_matches() has a signature we can use - # and disable that sub-feature if it doesn't. - if {![cctest \ - -cflags "$rlInc -D${editLibDef}" -libs $rlLib -nooutput 1 -source { - #include - #ifdef HAVE_EDITLINE - #include - #else - #include - #endif - static char * rcg(const char *z, int i){(void)z; (void)i; return 0;} - int main(void) { - char ** x = rl_completion_matches("one", rcg); - (void)x; - return 0; - } - }]} { - proj-warn "readline-style completion disabled due to rl_completion_matches() signature mismatch" - sqlite-add-shell-opt -DSQLITE_OMIT_READLINE_COMPLETION - } - return $editLibName - } - - if {$failIfNotFound} { - proj-fatal "Explicit --$editLibName failed to find a matching library." - } - return "none" -}; # sqlite-check-line-editing -msg-result "Line-editing support for the sqlite3 shell: [sqlite-check-line-editing]" - -proj-if-opt-truthy load-extension { - if {[proj-check-function-in-lib dlopen dl]} { - define LDFLAGS_DLOPEN [get-define lib_dlopen] - undefine lib_dlopen - } else { - user-error "dlopen() not found. Use --disable-load-extension to bypass this check." - } -} { - define LDFLAGS_DLOPEN "" - sqlite-add-feature-flag {-DSQLITE_OMIT_LOAD_EXTENSION=1} - msg-result "Disabling loadable extensions." -} - -proj-if-opt-truthy math { - if {![proj-check-function-in-lib ceil m]} { - user-error "Cannot find libm functions. Use --disable-math to bypass this." - } - define LDFLAGS_MATH [get-define lib_ceil] - undefine lib_ceil - sqlite-add-feature-flag {-DSQLITE_ENABLE_MATH_FUNCTIONS} - msg-result "Enabling math SQL functions [get-define LDFLAGS_MATH]" -} { - define LDFLAGS_MATH "" - msg-result "Disabling math SQL functions" -} - -######################################################################## -# ICU - International Components for Unicode -# -# Handles these flags: -# -# --with-icu-ldflags=LDFLAGS -# --with-icu-cflags=CFLAGS -# --with-icu-config[=auto | pkg-config | /path/to/icu-config] -# --enable-icu-collations -# -# --with-icu-config values: -# -# - auto: use the first one of (pkg-config, icu-config) found on the -# system. -# - pkg-config: use only pkg-config to determine flags -# - /path/to/icu-config: use that to determine flags -# -# If --with-icu-config is used as neither pkg-config nor icu-config -# are found, fail fatally. -# -# If both --with-icu-ldflags and --with-icu-config are provided, they -# are cumulative. If neither are provided, icu-collations is not -# honored and a warning is emitted if it is provided. -# -# Design note: though we could automatically enable ICU if the -# icu-config binary or (pkg-config icu-io) are found, we specifically -# do not. ICU is always an opt-in feature. -proc sqlite-check-icu {} { - rename sqlite-check-icu "" - define LDFLAGS_ICU [join [opt-val with-icu-ldflags ""]] - define CFLAGS_ICU [join [opt-val with-icu-cflags ""]] - if {[proj-opt-was-provided with-icu-config]} { - set icuConfigBin [opt-val with-icu-config] - set tryIcuConfigBin 1; # set to 0 if we end up using pkg-config - if {"auto" eq $icuConfigBin || "pkg-config" eq $icuConfigBin} { - if {[pkg-config-init 0] && [pkg-config icu-io]} { - # Maintenance reminder: historical docs say to use both of - # (icu-io, icu-uc). icu-uc lacks a required lib and icu-io has - # all of them on tested OSes. - set tryIcuConfigBin 0 - define LDFLAGS_ICU [get-define PKG_ICU_IO_LDFLAGS] - define-append LDFLAGS_ICU [get-define PKG_ICU_IO_LIBS] - define CFLAGS_ICU [get-define PKG_ICU_IO_CFLAGS] - } elseif {"pkg-config" eq $icuConfigBin} { - proj-fatal "pkg-config cannot find package icu-io" - } else { - proj-assert {"auto" eq $icuConfigBin} - } - } - if {$tryIcuConfigBin} { - if {"auto" eq $icuConfigBin} { - set icuConfigBin [proj-first-bin-of \ - /usr/local/bin/icu-config \ - /usr/bin/icu-config] - if {"" eq $icuConfigBin} { - proj-fatal "--with-icu-config=auto cannot find (pkg-config icu-io) or icu-config binary" - } - } - if {[file-isexec $icuConfigBin]} { - set x [exec $icuConfigBin --ldflags] - if {"" eq $x} { - proj-fatal "$icuConfigBin --ldflags returned no data" - } - define-append LDFLAGS_ICU $x - set x [exec $icuConfigBin --cppflags] - define-append CFLAGS_ICU $x - } else { - proj-fatal "--with-icu-config=$bin does not refer to an executable" - } - } - } - set ldflags [define LDFLAGS_ICU [string trim [get-define LDFLAGS_ICU]]] - set cflags [define CFLAGS_ICU [string trim [get-define CFLAGS_ICU]]] - if {"" ne $ldflags} { - sqlite-add-feature-flag -shell -DSQLITE_ENABLE_ICU - msg-result "Enabling ICU support with flags: $ldflags $cflags" - if {[opt-bool icu-collations]} { - msg-result "Enabling ICU collations." - sqlite-add-feature-flag -shell -DSQLITE_ENABLE_ICU_COLLATIONS - # Recall that shell.c builds with sqlite3.c - } - } elseif {[opt-bool icu-collations]} { - proj-warn "ignoring --enable-icu-collations because neither --with-icu-ldflags nor --with-icu-config provided any linker flags" - } else { - msg-result "ICU support is disabled." - } -}; # sqlite-check-icu -sqlite-check-icu - -######################################################################## -# Check for the Emscripten SDK for building the web-based wasm -# components. The core lib and tools do not require this but ext/wasm -# does. -apply {{} { - if {$::autosetup(srcdir) ne $::autosetup(builddir)} { - # The EMSDK pieces require writing to the original source tree - # even when doing an out-of-tree build. The ext/wasm pieces do not - # support an out-of-tree build so we catch that case and treat it - # as if EMSDK were not found. - msg-result "Out-of tree build: not checking for EMSDK." - define EMCC_WRAPPER "" - return - } - set emccsh $::srcdir/tool/emcc.sh - if {![get-define HAVE_WASI_SDK] && [proj-check-emsdk]} { - define EMCC_WRAPPER $emccsh - proj-make-from-dot-in $emccsh - catch {exec chmod u+x $emccsh} - } else { - define EMCC_WRAPPER "" - file delete -force $emccsh - } -}} - -######################################################################## -# Check for log(3) in libm and die with an error if it is not -# found. $featureName should be the feature name which requires that -# function (it's used only in error messages). defines LDFLAGS_MATH to -# the required linker flags (which may be empty even if the math APIs -# are found, depending on the OS). -proc affirm-have-math {featureName} { - if {"" eq [get-define LDFLAGS_MATH ""]} { - if {![msg-quiet proj-check-function-in-lib log m]} { - user-error "Missing math APIs for $featureName" - } - define LDFLAGS_MATH [get-define lib_log ""] - undefine lib_log - } -} - -######################################################################## -# Handle various SQLITE_ENABLE_... feature flags. -msg-result "Feature flags..." -foreach {boolFlag featureFlag ifSetEvalThis} { - all {} { - # The 'all' option must be first in this list. - proj-opt-set fts4 - proj-opt-set fts5 - proj-opt-set geopoly - proj-opt-set rtree - proj-opt-set session - } - fts4 -DSQLITE_ENABLE_FTS4 {affirm-have-math fts4} - fts5 -DSQLITE_ENABLE_FTS5 {affirm-have-math fts5} - geopoly -DSQLITE_ENABLE_GEOPOLY {proj-opt-set rtree} - rtree -DSQLITE_ENABLE_RTREE {} - session {-DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_PREUPDATE_HOOK} {} - update-limit -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT {} - memsys5 -DSQLITE_ENABLE_MEMSYS5 {} - memsys3 {} { - if {[opt-bool memsys5]} { - proj-warn "not enabling memsys3 because memsys5 is enabled." - expr 0 - } else { - sqlite-add-feature-flag -DSQLITE_ENABLE_MEMSYS3 - } - } - scanstatus -DSQLITE_ENABLE_STMT_SCANSTATUS {} -} { - proj-if-opt-truthy $boolFlag { - sqlite-add-feature-flag $featureFlag - if {0 != [eval $ifSetEvalThis] && "all" ne $boolFlag} { - msg-result " + $boolFlag" - } - } { - if {"all" ne $boolFlag} { - msg-result " - $boolFlag" - } - } -} - -######################################################################## -# Invert the above loop's logic for some SQLITE_OMIT_... cases. If -# config option $boolFlag is false, [sqlite-add-feature-flag -# $featureFlag], where $featureFlag is intended to be -# -DSQLITE_OMIT_... -foreach {boolFlag featureFlag} { - json -DSQLITE_OMIT_JSON -} { - if {[proj-opt-truthy $boolFlag]} { - msg-result " + $boolFlag" - } else { - sqlite-add-feature-flag $featureFlag - msg-result " - $boolFlag" - } -} - -######################################################################### -# Show the final feature flag sets: -apply {{} { - set oFF [get-define OPT_FEATURE_FLAGS] - if {"" ne $oFF} { - define OPT_FEATURE_FLAGS [lsort -unique $oFF] - msg-result "Library feature flags: [get-define OPT_FEATURE_FLAGS]" - } - set oFF [get-define OPT_SHELL] - if {"" ne $oFF} { - define OPT_SHELL [lsort -unique $oFF] - msg-result "Shell options: [get-define OPT_SHELL]" - } - unset oFF -}} - -######################################################################## -# "Re-export" the autoconf-conventional --XYZdir flags into something -# which is more easily overridable from a make invocation. See the docs -# for [proj-remap-autoconf-dir-vars] for the explanation of why. -# -# We do this late in the config process, immediately before we export -# the Makefile and other generated files, so that configure tests -# which make make use of the autotools-conventional flags -# (e.g. [proj-check-rpath]) may do so before we "mangle" them here. -proj-remap-autoconf-dir-vars - -######################################################################## -# Generate the output files. -# -# Potential TODO (unclear): in sqlite3.pc.in, do we need to include -# any CFLAGS_READLINE, CFLAGS_ZLIB, etc in its "Cflags:" section? -proj-make-from-dot-in -touch Makefile sqlite3.pc -make-config-header sqlite_cfg.h \ - -bare {SIZEOF_* HAVE_DECL_*} \ - -none {HAVE_CFLAG_* LDFLAGS_* SH_* SQLITE_AUTORECONFIG - TARGET_* USE_GCOV TCL_*} \ - -auto {HAVE_* PACKAGE_*} \ - -none * -proj-touch sqlite_cfg.h ; # help avoid frequent unnecessary @SQLITE_AUTORECONFIG@ - -######################################################################## -# Some build-dev/debug-only output -proj-if-opt-truthy dump-defines { - make-config-header $::DUMP_DEFINES_TXT \ - -bare {SQLITE_OS* SQLITE_DEBUG USE_*} \ - -str {BIN_* CC LD AR LDFLAG* OPT_*} \ - -auto {*} - # achtung: ^^^^ whichever SQLITE_OS_foo flag which is set to 0 will - # get _undefined_ here unless it's part of the -bare set. - if {"" ne $DUMP_DEFINES_JSON} { - msg-result "--dump-defines is creating $::DUMP_DEFINES_JSON" - ######################################################################## - # Dump config-defines.json... - # Demonstrate (mis?)handling of spaces in JSON-export array values: - # define-append OPT_FOO.list {"-DFOO=bar baz" -DBAR="baz barre"} - define OPT_FEATURE_FLAGS.list [get-define OPT_FEATURE_FLAGS] - define OPT_SHELL.list [get-define OPT_SHELL] - set dumpDefsOpt { - -bare {SIZEOF_* HAVE_DECL_*} - -none {HAVE_CFLAG_* LDFLAGS_* SH_* SQLITE_AUTORECONFIG TARGET_* USE_GCOV TCL_*} - -array {*.list} - -auto {OPT_* PACKAGE_* HAVE_*} - } - if {[opt-bool defines-json-include-lowercase]} { - lappend dumpDefsOpt -none {lib_*} ; # remnants from proj-check-function-in-lib and friends - lappend dumpDefsOpt -auto {[a-z]*} - } - lappend dumpDefsOpt -none * - proj-dump-defs-json $DUMP_DEFINES_JSON {*}$dumpDefsOpt - undefine OPT_FEATURE_FLAGS.list - undefine OPT_SHELL.list - } -} - -######################################################################## -# Perform some high-level validation on the generated files... -# -# 1) Ensure that no unresolved @VAR@ placeholders are in files which -# use those. -# -# 2) TBD -apply {{} { - # Check #1: ensure that files which get filtered for @VAR@ do not - # contain any unresolved @VAR@ refs. That may indicate an - # unexported/unused var or a typo. - foreach f "Makefile sqlite3.pc $::srcdir/tool/emcc.sh" { - if {![file exists $f]} continue - set lnno 1 - foreach line [proj-file-content-list $f] { - if {[regexp {(@[A-Za-z_]+@)} $line match]} { - error "Unresolved reference to $match at line $lnno of $f" - } - incr lnno - } - } -}} +proj-check-rpath +sqlite-handle-soname +sqlite-handle-debug +sqlite-handle-tcl +sqlite-handle-threadsafe +sqlite-handle-tempstore +sqlite-handle-line-editing +sqlite-handle-load-extension +sqlite-handle-math +sqlite-handle-icu +sqlite-handle-emsdk +sqlite-common-late-stage-config +sqlite-dump-defines diff --git a/autoconf/INSTALL b/autoconf/INSTALL deleted file mode 100644 index a1e89e18ad..0000000000 --- a/autoconf/INSTALL +++ /dev/null @@ -1,370 +0,0 @@ -Installation Instructions -************************* - -Copyright (C) 1994-1996, 1999-2002, 2004-2011 Free Software Foundation, -Inc. - - Copying and distribution of this file, with or without modification, -are permitted in any medium without royalty provided the copyright -notice and this notice are preserved. This file is offered as-is, -without warranty of any kind. - -Basic Installation -================== - - Briefly, the shell commands `./configure; make; make install' should -configure, build, and install this package. The following -more-detailed instructions are generic; see the `README' file for -instructions specific to this package. Some packages provide this -`INSTALL' file but do not implement all of the features documented -below. The lack of an optional feature in a given package is not -necessarily a bug. More recommendations for GNU packages can be found -in *note Makefile Conventions: (standards)Makefile Conventions. - - The `configure' shell script attempts to guess correct values for -various system-dependent variables used during compilation. It uses -those values to create a `Makefile' in each directory of the package. -It may also create one or more `.h' files containing system-dependent -definitions. Finally, it creates a shell script `config.status' that -you can run in the future to recreate the current configuration, and a -file `config.log' containing compiler output (useful mainly for -debugging `configure'). - - It can also use an optional file (typically called `config.cache' -and enabled with `--cache-file=config.cache' or simply `-C') that saves -the results of its tests to speed up reconfiguring. Caching is -disabled by default to prevent problems with accidental use of stale -cache files. - - If you need to do unusual things to compile the package, please try -to figure out how `configure' could check whether to do them, and mail -diffs or instructions to the address given in the `README' so they can -be considered for the next release. If you are using the cache, and at -some point `config.cache' contains results you don't want to keep, you -may remove or edit it. - - The file `configure.ac' (or `configure.in') is used to create -`configure' by a program called `autoconf'. You need `configure.ac' if -you want to change it or regenerate `configure' using a newer version -of `autoconf'. - - The simplest way to compile this package is: - - 1. `cd' to the directory containing the package's source code and type - `./configure' to configure the package for your system. - - Running `configure' might take a while. While running, it prints - some messages telling which features it is checking for. - - 2. Type `make' to compile the package. - - 3. Optionally, type `make check' to run any self-tests that come with - the package, generally using the just-built uninstalled binaries. - - 4. Type `make install' to install the programs and any data files and - documentation. When installing into a prefix owned by root, it is - recommended that the package be configured and built as a regular - user, and only the `make install' phase executed with root - privileges. - - 5. Optionally, type `make installcheck' to repeat any self-tests, but - this time using the binaries in their final installed location. - This target does not install anything. Running this target as a - regular user, particularly if the prior `make install' required - root privileges, verifies that the installation completed - correctly. - - 6. You can remove the program binaries and object files from the - source code directory by typing `make clean'. To also remove the - files that `configure' created (so you can compile the package for - a different kind of computer), type `make distclean'. There is - also a `make maintainer-clean' target, but that is intended mainly - for the package's developers. If you use it, you may have to get - all sorts of other programs in order to regenerate files that came - with the distribution. - - 7. Often, you can also type `make uninstall' to remove the installed - files again. In practice, not all packages have tested that - uninstallation works correctly, even though it is required by the - GNU Coding Standards. - - 8. Some packages, particularly those that use Automake, provide `make - distcheck', which can by used by developers to test that all other - targets like `make install' and `make uninstall' work correctly. - This target is generally not run by end users. - -Compilers and Options -===================== - - Some systems require unusual options for compilation or linking that -the `configure' script does not know about. Run `./configure --help' -for details on some of the pertinent environment variables. - - You can give `configure' initial values for configuration parameters -by setting variables in the command line or in the environment. Here -is an example: - - ./configure CC=c99 CFLAGS=-g LIBS=-lposix - - *Note Defining Variables::, for more details. - -Compiling For Multiple Architectures -==================================== - - You can compile the package for more than one kind of computer at the -same time, by placing the object files for each architecture in their -own directory. To do this, you can use GNU `make'. `cd' to the -directory where you want the object files and executables to go and run -the `configure' script. `configure' automatically checks for the -source code in the directory that `configure' is in and in `..'. This -is known as a "VPATH" build. - - With a non-GNU `make', it is safer to compile the package for one -architecture at a time in the source code directory. After you have -installed the package for one architecture, use `make distclean' before -reconfiguring for another architecture. - - On MacOS X 10.5 and later systems, you can create libraries and -executables that work on multiple system types--known as "fat" or -"universal" binaries--by specifying multiple `-arch' options to the -compiler but only a single `-arch' option to the preprocessor. Like -this: - - ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ - CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ - CPP="gcc -E" CXXCPP="g++ -E" - - This is not guaranteed to produce working output in all cases, you -may have to build one architecture at a time and combine the results -using the `lipo' tool if you have problems. - -Installation Names -================== - - By default, `make install' installs the package's commands under -`/usr/local/bin', include files under `/usr/local/include', etc. You -can specify an installation prefix other than `/usr/local' by giving -`configure' the option `--prefix=PREFIX', where PREFIX must be an -absolute file name. - - You can specify separate installation prefixes for -architecture-specific files and architecture-independent files. If you -pass the option `--exec-prefix=PREFIX' to `configure', the package uses -PREFIX as the prefix for installing programs and libraries. -Documentation and other data files still use the regular prefix. - - In addition, if you use an unusual directory layout you can give -options like `--bindir=DIR' to specify different values for particular -kinds of files. Run `configure --help' for a list of the directories -you can set and what kinds of files go in them. In general, the -default for these options is expressed in terms of `${prefix}', so that -specifying just `--prefix' will affect all of the other directory -specifications that were not explicitly provided. - - The most portable way to affect installation locations is to pass the -correct locations to `configure'; however, many packages provide one or -both of the following shortcuts of passing variable assignments to the -`make install' command line to change installation locations without -having to reconfigure or recompile. - - The first method involves providing an override variable for each -affected directory. For example, `make install -prefix=/alternate/directory' will choose an alternate location for all -directory configuration variables that were expressed in terms of -`${prefix}'. Any directories that were specified during `configure', -but not in terms of `${prefix}', must each be overridden at install -time for the entire installation to be relocated. The approach of -makefile variable overrides for each directory variable is required by -the GNU Coding Standards, and ideally causes no recompilation. -However, some platforms have known limitations with the semantics of -shared libraries that end up requiring recompilation when using this -method, particularly noticeable in packages that use GNU Libtool. - - The second method involves providing the `DESTDIR' variable. For -example, `make install DESTDIR=/alternate/directory' will prepend -`/alternate/directory' before all installation names. The approach of -`DESTDIR' overrides is not required by the GNU Coding Standards, and -does not work on platforms that have drive letters. On the other hand, -it does better at avoiding recompilation issues, and works well even -when some directory options were not specified in terms of `${prefix}' -at `configure' time. - -Optional Features -================= - - If the package supports it, you can cause programs to be installed -with an extra prefix or suffix on their names by giving `configure' the -option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. - - Some packages pay attention to `--enable-FEATURE' options to -`configure', where FEATURE indicates an optional part of the package. -They may also pay attention to `--with-PACKAGE' options, where PACKAGE -is something like `gnu-as' or `x' (for the X Window System). The -`README' should mention any `--enable-' and `--with-' options that the -package recognizes. - - For packages that use the X Window System, `configure' can usually -find the X include and library files automatically, but if it doesn't, -you can use the `configure' options `--x-includes=DIR' and -`--x-libraries=DIR' to specify their locations. - - Some packages offer the ability to configure how verbose the -execution of `make' will be. For these packages, running `./configure ---enable-silent-rules' sets the default to minimal output, which can be -overridden with `make V=1'; while running `./configure ---disable-silent-rules' sets the default to verbose, which can be -overridden with `make V=0'. - -Particular systems -================== - - On HP-UX, the default C compiler is not ANSI C compatible. If GNU -CC is not installed, it is recommended to use the following options in -order to use an ANSI C compiler: - - ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" - -and if that doesn't work, install pre-built binaries of GCC for HP-UX. - - HP-UX `make' updates targets which have the same time stamps as -their prerequisites, which makes it generally unusable when shipped -generated files such as `configure' are involved. Use GNU `make' -instead. - - On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot -parse its `' header file. The option `-nodtk' can be used as -a workaround. If GNU CC is not installed, it is therefore recommended -to try - - ./configure CC="cc" - -and if that doesn't work, try - - ./configure CC="cc -nodtk" - - On Solaris, don't put `/usr/ucb' early in your `PATH'. This -directory contains several dysfunctional programs; working variants of -these programs are available in `/usr/bin'. So, if you need `/usr/ucb' -in your `PATH', put it _after_ `/usr/bin'. - - On Haiku, software installed for all users goes in `/boot/common', -not `/usr/local'. It is recommended to use the following options: - - ./configure --prefix=/boot/common - -Specifying the System Type -========================== - - There may be some features `configure' cannot figure out -automatically, but needs to determine by the type of machine the package -will run on. Usually, assuming the package is built to be run on the -_same_ architectures, `configure' can figure that out, but if it prints -a message saying it cannot guess the machine type, give it the -`--build=TYPE' option. TYPE can either be a short name for the system -type, such as `sun4', or a canonical name which has the form: - - CPU-COMPANY-SYSTEM - -where SYSTEM can have one of these forms: - - OS - KERNEL-OS - - See the file `config.sub' for the possible values of each field. If -`config.sub' isn't included in this package, then this package doesn't -need to know the machine type. - - If you are _building_ compiler tools for cross-compiling, you should -use the option `--target=TYPE' to select the type of system they will -produce code for. - - If you want to _use_ a cross compiler, that generates code for a -platform different from the build platform, you should specify the -"host" platform (i.e., that on which the generated programs will -eventually be run) with `--host=TYPE'. - -Sharing Defaults -================ - - If you want to set default values for `configure' scripts to share, -you can create a site shell script called `config.site' that gives -default values for variables like `CC', `cache_file', and `prefix'. -`configure' looks for `PREFIX/share/config.site' if it exists, then -`PREFIX/etc/config.site' if it exists. Or, you can set the -`CONFIG_SITE' environment variable to the location of the site script. -A warning: not all `configure' scripts look for a site script. - -Defining Variables -================== - - Variables not defined in a site shell script can be set in the -environment passed to `configure'. However, some packages may run -configure again during the build, and the customized values of these -variables may be lost. In order to avoid this problem, you should set -them in the `configure' command line, using `VAR=value'. For example: - - ./configure CC=/usr/local2/bin/gcc - -causes the specified `gcc' to be used as the C compiler (unless it is -overridden in the site shell script). - -Unfortunately, this technique does not work for `CONFIG_SHELL' due to -an Autoconf bug. Until the bug is fixed you can use this workaround: - - CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash - -`configure' Invocation -====================== - - `configure' recognizes the following options to control how it -operates. - -`--help' -`-h' - Print a summary of all of the options to `configure', and exit. - -`--help=short' -`--help=recursive' - Print a summary of the options unique to this package's - `configure', and exit. The `short' variant lists options used - only in the top level, while the `recursive' variant lists options - also present in any nested packages. - -`--version' -`-V' - Print the version of Autoconf used to generate the `configure' - script, and exit. - -`--cache-file=FILE' - Enable the cache: use and save the results of the tests in FILE, - traditionally `config.cache'. FILE defaults to `/dev/null' to - disable caching. - -`--config-cache' -`-C' - Alias for `--cache-file=config.cache'. - -`--quiet' -`--silent' -`-q' - Do not print messages saying which checks are being made. To - suppress all normal output, redirect it to `/dev/null' (any error - messages will still be shown). - -`--srcdir=DIR' - Look for the package's source code in directory DIR. Usually - `configure' can determine that directory automatically. - -`--prefix=DIR' - Use DIR as the installation prefix. *note Installation Names:: - for more details, including other options available for fine-tuning - the installation locations. - -`--no-create' -`-n' - Run the configure checks, but stop before creating any output - files. - -`configure' also accepts some other, not widely useful, options. Run -`configure --help' for more details. - diff --git a/autoconf/Makefile.am b/autoconf/Makefile.am deleted file mode 100644 index 1eaa560ff8..0000000000 --- a/autoconf/Makefile.am +++ /dev/null @@ -1,20 +0,0 @@ - -AM_CFLAGS = @BUILD_CFLAGS@ -lib_LTLIBRARIES = libsqlite3.la -libsqlite3_la_SOURCES = sqlite3.c -libsqlite3_la_LDFLAGS = -no-undefined -version-info 8:6:8 - -bin_PROGRAMS = sqlite3 -sqlite3_SOURCES = shell.c sqlite3.h -EXTRA_sqlite3_SOURCES = sqlite3.c -sqlite3_LDADD = @EXTRA_SHELL_OBJ@ @READLINE_LIBS@ -sqlite3_DEPENDENCIES = @EXTRA_SHELL_OBJ@ -sqlite3_CFLAGS = $(AM_CFLAGS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_DQS=0 -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_ENABLE_STMTVTAB -DSQLITE_ENABLE_DBSTAT_VTAB $(SHELL_CFLAGS) - -include_HEADERS = sqlite3.h sqlite3ext.h - -EXTRA_DIST = sqlite3.1 tea Makefile.msc sqlite3.rc sqlite3rc.h README.txt Replace.cs Makefile.fallback -pkgconfigdir = ${libdir}/pkgconfig -pkgconfig_DATA = sqlite3.pc - -man_MANS = sqlite3.1 diff --git a/autoconf/Makefile.in b/autoconf/Makefile.in new file mode 100644 index 0000000000..8e09b9c610 --- /dev/null +++ b/autoconf/Makefile.in @@ -0,0 +1,248 @@ +######################################################################## +# This is a main makefile for the "autoconf" bundle of SQLite. This is +# a trimmed-down version of the canonical makefile, devoid of most +# documentation. For the full docs, see /main.mk in the canonical +# source tree. +# +# Maintenance reminders: +# +# - To keep this working with an out-of-tree build, be sure to prefix +# input file names with $(TOP)/ where appropriate (which is most +# places). +# +# - The original/canonical recipes can be found in /main.mk in the +# canonical source tree. +all: + +TOP = @abs_top_srcdir@ + +PACKAGE_VERSION = @PACKAGE_VERSION@ + +# +# Filename extensions for binaries and libraries +# +B.exe = @BUILD_EXEEXT@ +T.exe = @TARGET_EXEEXT@ +B.dll = @BUILD_DLLEXT@ +T.dll = @TARGET_DLLEXT@ +B.lib = @BUILD_LIBEXT@ +T.lib = @TARGET_LIBEXT@ + +# +# Autotools-compatibility dirs +# +prefix = @prefix@ +datadir = @datadir@ +mandir = @mandir@ +includedir = @includedir@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +libdir = @libdir@ + +# +# Required binaries +# +INSTALL = @BIN_INSTALL@ +AR = @AR@ +AR.flags = cr +CC = @CC@ + + +ENABLE_LIB_SHARED = @ENABLE_LIB_SHARED@ +ENABLE_LIB_STATIC = @ENABLE_LIB_STATIC@ + +CFLAGS = @CFLAGS@ @CPPFLAGS@ +# +# $(LDFLAGS.configure) represents any LDFLAGS=... the client passes to +# configure. See main.mk. +# +LDFLAGS.configure = @LDFLAGS@ + +CFLAGS.core = @SH_CFLAGS@ +LDFLAGS.shlib = @SH_LDFLAGS@ +LDFLAGS.zlib = @LDFLAGS_ZLIB@ +LDFLAGS.math = @LDFLAGS_MATH@ +LDFLAGS.rpath = @LDFLAGS_RPATH@ +LDFLAGS.pthread = @LDFLAGS_PTHREAD@ +LDFLAGS.dlopen = @LDFLAGS_DLOPEN@ +LDFLAGS.readline = @LDFLAGS_READLINE@ +CFLAGS.readline = @CFLAGS_READLINE@ +LDFLAGS.rt = @LDFLAGS_RT@ +LDFLAGS.icu = @LDFLAGS_ICU@ +CFLAGS.icu = @CFLAGS_ICU@ + +# When cross-compiling, we need to avoid the -s flag because it only +# works on the build host's platform. +INSTALL.strip.1 = $(INSTALL) +INSTALL.strip.0 = $(INSTALL) -s +INSTALL.strip = $(INSTALL.strip.@IS_CROSS_COMPILING@) +INSTALL.noexec = $(INSTALL) -m 0644 + +install-dir.bin = $(DESTDIR)$(bindir) +install-dir.lib = $(DESTDIR)$(libdir) +install-dir.include = $(DESTDIR)$(includedir) +install-dir.pkgconfig = $(DESTDIR)$(libdir)/pkgconfig +install-dir.man1 = $(DESTDIR)$(mandir)/man1 +install-dir.all = $(install-dir.bin) $(install-dir.include) \ + $(install-dir.lib) $(install-dir.man1) \ + $(install-dir.pkgconfig) +$(install-dir.all): + if [ ! -d "$@" ]; then $(INSTALL) -d "$@"; fi +# ^^^^ on some platforms, install -d fails if the target already exists. + + +# +# Vars with the AS_ prefix are specifically related to AutoSetup. +# +# AS_AUTO_DEF is the main configure script. +# +AS_AUTO_DEF = $(TOP)/auto.def + +# +# Shell commands to re-run $(TOP)/configure with the same args it was +# invoked with to produce this makefile. +# +AS_AUTORECONFIG = @SQLITE_AUTORECONFIG@ +Makefile: $(TOP)/Makefile.in $(AS_AUTO_DEF) + $(AS_AUTORECONFIG) + @touch $@ + +sqlite3.pc: $(TOP)/sqlite3.pc.in $(AS_AUTO_DEF) + $(AS_AUTORECONFIG) + @touch $@ + +sqlite_cfg.h: $(AS_AUTO_DEF) + $(AS_AUTORECONFIG) + @touch $@ + +# +# CFLAGS for sqlite3$(T.exe) +# +SHELL_OPT ?= @OPT_SHELL@ + +# +# Library-level feature flags +# +OPT_FEATURE_FLAGS = @OPT_FEATURE_FLAGS@ + +LDFLAGS.libsqlite3.soname = @LDFLAGS_LIBSQLITE3_SONAME@ +# soname: see https://sqlite.org/src/forumpost/5a3b44f510df8ded +LDFLAGS.libsqlite3.os-specific = @LDFLAGS_MAC_CVERSION@ @LDFLAGS_OUT_IMPLIB@ +# os-specific: see +# - https://sqlite.org/forum/forumpost/9dfd5b8fd525a5d7 +# - https://sqlite.org/forum/forumpost/0c7fc097b2 + +LDFLAGS.libsqlite3 = \ + $(LDFLAGS.rpath) $(LDFLAGS.pthread) \ + $(LDFLAGS.math) $(LDFLAGS.dlopen) \ + $(LDFLAGS.zlib) $(LDFLAGS.icu) \ + $(LDFLAGS.rt) $(LDFLAGS.configure) +CFLAGS.libsqlite3 = -I. $(CFLAGS.core) $(CFLAGS.icu) $(OPT_FEATURE_FLAGS) + +sqlite3.o: $(TOP)/sqlite3.h $(TOP)/sqlite3.c + $(CC) -c $(TOP)/sqlite3.c -o $@ $(CFLAGS) $(CFLAGS.libsqlite3) + +libsqlite3.LIB = libsqlite3$(T.lib) +libsqlite3.SO = libsqlite3$(T.dll) + +$(libsqlite3.SO): sqlite3.o + $(CC) -o $@ sqlite3.o $(LDFLAGS.shlib) \ + $(LDFLAGS) $(LDFLAGS.libsqlite3) \ + $(LDFLAGS.libsqlite3.os-specific) $(LDFLAGS.libsqlite3.soname) +all: $(libsqlite3.SO) + +$(libsqlite3.LIB): sqlite3.o + $(AR) $(AR.flags) $@ sqlite3.o +all: $(libsqlite3.LIB) + +install-so-1: $(install-dir.lib) $(libsqlite3.SO) + $(INSTALL) $(libsqlite3.SO) "$(install-dir.lib)" + @echo "Setting up $(libsqlite3.SO) version symlinks..."; \ + cd "$(install-dir.lib)" || exit $$?; \ + if [ x.dylib = x$(T.dll) ]; then \ + rm -f libsqlite3.0$(T.dll) libsqlite3.$(PACKAGE_VERSION)$(T.dll) || exit $$?; \ + dllname=libsqlite3.$(PACKAGE_VERSION)$(T.dll); \ + mv $(libsqlite3.SO) $$dllname || exit $$?; \ + ln -s $$dllname $(libsqlite3.SO) || exit $$?; \ + ln -s $$dllname libsqlite3.0$(T.dll) || exit $$?; \ + ls -la $$dllname $(libsqlite3.SO) libsqlite3.0$(T.dll); \ + else \ + rm -f $(libsqlite3.SO).0 $(libsqlite3.SO).$(PACKAGE_VERSION) || exit $$?; \ + mv $(libsqlite3.SO) $(libsqlite3.SO).$(PACKAGE_VERSION) || exit $$?; \ + ln -s $(libsqlite3.SO).$(PACKAGE_VERSION) $(libsqlite3.SO) || exit $$?; \ + ln -s $(libsqlite3.SO).$(PACKAGE_VERSION) $(libsqlite3.SO).0 || exit $$?; \ + ls -la $(libsqlite3.SO) $(libsqlite3.SO).[03]*; \ + if [ -e $(libsqlite3.SO).0.8.6 ]; then \ + echo "ACHTUNG: legacy libtool-compatible install found. Re-linking it..."; \ + rm -f libsqlite3.la $(libsqlite3.SO).0.8.6 || exit $$?; \ + ln -s $(libsqlite3.SO).$(PACKAGE_VERSION) $(libsqlite3.SO).0.8.6 || exit $$?; \ + ls -la $(libsqlite3.SO).0.8.6; \ + elif [ x1 = "x$(INSTALL_SO_086_LINK)" ]; then \ + echo "ACHTUNG: installing legacy libtool-style links because INSTALL_SO_086_LINK=1"; \ + rm -f libsqlite3.la $(libsqlite3.SO).0.8.6 || exit $$?; \ + ln -s $(libsqlite3.SO).$(PACKAGE_VERSION) $(libsqlite3.SO).0.8.6 || exit $$?; \ + ls -la $(libsqlite3.SO).0.8.6; \ + fi; \ + fi + +install-so-0 install-so-: +install-so: install-so-$(ENABLE_LIB_SHARED) +install: install-so + +install-lib-1: $(install-dir.lib) $(libsqlite3.LIB) + $(INSTALL.noexec) $(libsqlite3.LIB) "$(install-dir.lib)" +install-lib-0 install-lib-: +install-lib: install-lib-$(ENABLE_LIB_STATIC) +install: install-lib + + +sqlite3$(T.exe): $(TOP)/shell.c $(TOP)/sqlite3.c + $(CC) -o $@ \ + $(TOP)/shell.c $(TOP)/sqlite3.c \ + -I. $(OPT_FEATURE_FLAGS) $(SHELL_OPT) \ + $(CFLAGS) $(CFLAGS.readline) $(CFLAGS.icu) \ + $(LDFLAGS) $(LDFLAGS.libsqlite3) $(LDFLAGS.readline) +all: sqlite3$(T.exe) + +install-shell: sqlite3$(T.exe) $(install-dir.bin) + $(INSTALL.strip) sqlite3$(T.exe) "$(install-dir.bin)" +install: install-shell + +install-headers: $(TOP)/sqlite3.h $(install-dir.include) + $(INSTALL.noexec) $(TOP)/sqlite3.h $(TOP)/sqlite3ext.h "$(install-dir.include)" +install: install-headers + +install-pc: sqlite3.pc $(install-dir.pkgconfig) + $(INSTALL.noexec) sqlite3.pc "$(install-dir.pkgconfig)" +install: install-pc + +install-man1: $(TOP)/sqlite3.1 $(install-dir.man1) + $(INSTALL.noexec) $(TOP)/sqlite3.1 "$(install-dir.man1)" +install: install-man1 + +clean: + rm -f *.o sqlite3$(T.exe) + rm -f $(libsqlite3.LIB) $(libsqlite3.SO) $(libsqlite3.SO).a + +distclean: clean + rm -f jimsh0$(T.exe) config.* sqlite3.pc + +DIST_FILES := \ + README.txt VERSION \ + auto.def autosetup configure tea \ + sqlite3.h sqlite3.c shell.c sqlite3ext.h \ + Makefile.in Makefile.msc Makefile.fallback \ + sqlite3.rc sqlite3rc.h Replace.cs \ + sqlite3.pc.in sqlite3.1 + +# Maintenance note: dist_name must be sqlite-$(PACKAGE_VERSION) so +# that tool/mkautoconfamal.sh knows how to find it. +dist_name = sqlite-$(PACKAGE_VERSION) +dist_tarball = $(dist_name).tar.gz +dist: + rm -fr $(dist_name) + mkdir -p $(dist_name) + cp -rp $(DIST_FILES) $(dist_name)/. + tar czf $(dist_tarball) $(dist_name) + rm -fr $(dist_name) + ls -l $(dist_tarball) diff --git a/autoconf/Makefile.msc b/autoconf/Makefile.msc index 1f0e42db4b..47e0a83af8 100644 --- a/autoconf/Makefile.msc +++ b/autoconf/Makefile.msc @@ -1087,5 +1087,6 @@ $(LIBRESOBJS): $(TOP)\sqlite3.rc rcver.vc $(SQLITE3H) clean: del /Q *.exp *.lo *.ilk *.lib *.obj *.ncb *.pdb *.sdf *.suo 2>NUL - del /Q *.bsc *.def *.cod *.da *.bb *.bbg *.vc gmon.out 2>NUL + del /Q *.bsc *.cod *.da *.bb *.bbg *.vc gmon.out 2>NUL + del /Q sqlite3.def tclsqlite3.def 2>NUL del /Q $(SQLITE3EXE) $(SQLITE3DLL) Replace.exe 2>NUL diff --git a/autoconf/README.first b/autoconf/README.first index 5c2ea0a70f..75c4a76d61 100644 --- a/autoconf/README.first +++ b/autoconf/README.first @@ -1,11 +1,12 @@ -This directory contains components use to build an autoconf-ready package -of the SQLite amalgamation: sqlite-autoconf-30XXXXXX.tar.gz +This directory contains components used to build an autoconf-like +package of the SQLite amalgamation: sqlite-autoconf-30XXXXXX.tar.gz -To build the autoconf amalgamation, run from the top-level: +To build the autoconf amalgamation, run from the top of the canonical +source tree: ./configure make amalgamation-tarball -The amalgamation-tarball target (also available in "main.mk") runs the -script tool/mkautoconfamal.sh which does the work. Refer to that script -for details. +The amalgamation-tarball target (available in "main.mk") runs the +script tool/mkautoconfamal.sh which does the work. Refer to that +script for details. diff --git a/autoconf/README.txt b/autoconf/README.txt index b3d3510746..646c0a1215 100644 --- a/autoconf/README.txt +++ b/autoconf/README.txt @@ -4,7 +4,7 @@ This package contains: * the sqlite3.h and sqlite3ext.h header files that define the C-language interface to the sqlite3.c library file * the shell.c file used to build the sqlite3 command-line shell program - * autoconf/automake installation infrastucture for building on POSIX + * autoconf-like installation infrastucture for building on POSIX compliant systems * a Makefile.msc, sqlite3.rc, and Replace.cs for building with Microsoft Visual C++ on Windows @@ -19,8 +19,10 @@ using only generic tools and without having to install TCL. The purpose of this package is to provide that capability. This package contains a pre-build SQLite amalgamation file "sqlite3.c" -(and its associated header file "sqlite3.h"). Because the amalgamation -has been pre-built, no TCL is required. +(and its associated header file "sqlite3.h"). Because the +amalgamation has been pre-built, no TCL is required for the code +generate (the configure script itself is written in TCL but it can use +the embedded copy of JimTCL). REASONS TO USE THE CANONICAL BUILD SYSTEM RATHER THAN THIS PACKAGE ================================================================== @@ -47,14 +49,12 @@ SUMMARY OF HOW TO BUILD USING THIS PACKAGE BUILDING ON POSIX ================= -The generic installation instructions for autoconf/automake are found -in the INSTALL file. +The configure script follows common conventions, making it easy +to use for anyone who has configured a software tree before. +It supports a number of build-time flags, the full list of which +can be seen by running: -The following SQLite specific boolean options are supported: - - --enable-readline use readline in shell tool [default=yes] - --enable-threadsafe build a thread-safe library [default=yes] - --enable-dynamic-extensions support loadable extensions [default=yes] + ./configure --help The default value for the CFLAGS variable (options passed to the C compiler) includes debugging symbols in the build, resulting in larger @@ -65,10 +65,11 @@ line like this: to produce a smaller installation footprint. -Other SQLite compilation parameters can also be set using CFLAGS. For +Many SQLite compilation parameters can be defined by passing flags +to the configure script. Others may be passed on in the CFLAGS. For example: - $ CFLAGS="-Os -DSQLITE_THREADSAFE=0" ./configure + $ CFLAGS="-Os -DSQLITE_OMIT_DEPRECATED" ./configure BUILDING WITH MICROSOFT VISUAL C++ diff --git a/autoconf/auto.def b/autoconf/auto.def new file mode 100644 index 0000000000..42af1b459b --- /dev/null +++ b/autoconf/auto.def @@ -0,0 +1,101 @@ +#/do/not/tclsh +# ^^^ help out editors which guess this file's content type. +# +# This is the main autosetup-compatible configure script for the +# "autoconf" bundle of the SQLite project. +# +# This script must be kept compatible with JimTCL, a copy of which is +# included in this source tree as ./autosetup/jimsh0.c. +# +use sqlite-config + +options { + # + static=1 => {Disable build of static library} + shared=1 => {Disable build of shared library} + # + # + threadsafe=1 => {Disable mutexing} + with-tempstore:=no => {Use an in-RAM database for temporary tables: never,no,yes,always} + load-extension=1 => {Disable loading of external extensions} + math=1 => {Disable math functions} + json=1 => {Disable JSON functions} + memsys5 => {Enable MEMSYS5} + memsys3 => {Enable MEMSYS3} + fts3 => {Enable the FTS3 extension} + fts4 => {Enable the FTS4 extension} + fts5 => {Enable the FTS5 extension} + update-limit => {Enable the UPDATE/DELETE LIMIT clause} + geopoly => {Enable the GEOPOLY extension} + rtree => {Enable the RTREE extension} + session => {Enable the SESSION extension} + all => {Enable FTS4, FTS5, Geopoly, RTree, Sessions} + # + # + readline=1 => {Disable readline support} + # --with-readline-lib is a backwards-compatible alias for + # --with-readline-ldflags + with-readline-lib: + with-readline-ldflags:=auto + => {Readline LDFLAGS, e.g. -lreadline -lncurses} + # --with-readline-inc is a backwards-compatible alias for + # --with-readline-cflags. + with-readline-inc: + with-readline-cflags:=auto + => {Readline CFLAGS, e.g. -I/path/to/includes} + with-readline-header:PATH + => {Full path to readline.h, from which --with-readline-cflags will be derived} + with-linenoise:DIR => {Source directory for linenoise.c and linenoise.h} + editline=0 => {Enable BSD editline support} + # + # + with-icu-ldflags:LDFLAGS + => {Enable SQLITE_ENABLE_ICU and add the given linker flags for the ICU libraries} + with-icu-cflags:CFLAGS + => {Apply extra CFLAGS/CPPFLAGS necessary for building with ICU. e.g. -I/usr/local/include} + with-icu-config:=auto => {Enable SQLITE_ENABLE_ICU. Value must be one of: auto, pkg-config, /path/to/icu-config} + icu-collations=0 => {Enable SQLITE_ENABLE_ICU_COLLATIONS. Requires --with-icu-ldflags=... or --with-icu-config} + # + # + # Note that using the --debug/--enable-debug flag here requires patching + # autosetup/autosetup to rename the --debug to --autosetup-debug. + with-debug=0 + debug=0 => + {Enable debug build flags. This option will impact performance by + as much as 4x, as it includes large numbers of assert()s in + performance-critical loops. Never use --debug for production + builds.} + # + # + soname:=legacy => + # --soname has a long story behind it: https://sqlite.org/src/forumpost/5a3b44f510df8ded + {SONAME for libsqlite3.so. "none", or not using this flag, sets no + soname. "legacy" sets it to its historical value of + libsqlite3.so.0. A value matching the glob "libsqlite3.*" sets + it to that literal value. Any other value is assumed to be a + suffix which gets applied to "libsqlite3.so.", + e.g. --soname=9.10 equates to "libsqlite3.so.9.10". + } + out-implib=0 => + {Enable use of --out-implib linker flag to generate an "import library" for the DLL} + # +} + +sqlite-post-options-init +sqlite-check-common-bins +sqlite-check-common-system-deps +proj-check-rpath +sqlite-handle-soname +sqlite-setup-default-cflags +sqlite-handle-debug +sqlite-handle-threadsafe +sqlite-handle-tempstore +sqlite-handle-line-editing +sqlite-handle-load-extension +sqlite-handle-math +sqlite-handle-icu + +define ENABLE_LIB_SHARED [opt-bool shared] +define ENABLE_LIB_STATIC [opt-bool static] + +sqlite-common-late-stage-config diff --git a/autoconf/configure.ac b/autoconf/configure.ac deleted file mode 100644 index 0c7a32db1f..0000000000 --- a/autoconf/configure.ac +++ /dev/null @@ -1,270 +0,0 @@ - -#----------------------------------------------------------------------- -# Supports the following non-standard switches. -# -# --enable-threadsafe -# --enable-readline -# --enable-editline -# --enable-static-shell -# --enable-dynamic-extensions -# - -AC_PREREQ(2.61) -AC_INIT(sqlite, --SQLITE-VERSION--, http://www.sqlite.org) -AC_CONFIG_SRCDIR([sqlite3.c]) -AC_CONFIG_AUX_DIR([.]) - -# Use automake. -AM_INIT_AUTOMAKE([foreign]) - -AC_SYS_LARGEFILE - -# Check for required programs. -AC_PROG_CC -AC_PROG_LIBTOOL -AC_PROG_MKDIR_P - -# Check for library functions that SQLite can optionally use. -AC_CHECK_FUNCS([fdatasync usleep fullfsync localtime_r gmtime_r]) -AC_FUNC_STRERROR_R - -AC_CONFIG_FILES([Makefile sqlite3.pc]) -BUILD_CFLAGS= -AC_SUBST(BUILD_CFLAGS) - -#------------------------------------------------------------------------- -# Two options to enable readline compatible libraries: -# -# --enable-editline -# --enable-readline -# -# Both are enabled by default. If, after command line processing both are -# still enabled, the script searches for editline first and automatically -# disables readline if it is found. So, to use readline explicitly, the -# user must pass "--disable-editline". To disable command line editing -# support altogether, "--disable-editline --disable-readline". -# -# When searching for either library, check for headers before libraries -# as some distros supply packages that contain libraries but not header -# files, which come as a separate development package. -# -AC_ARG_ENABLE(editline, [AS_HELP_STRING([--enable-editline],[use BSD libedit])]) -AC_ARG_ENABLE(readline, [AS_HELP_STRING([--enable-readline],[use readline])]) - -AS_IF([ test x"$enable_editline" != xno ],[ - AC_CHECK_HEADERS([editline/readline.h],[ - sLIBS=$LIBS - LIBS="" - AC_SEARCH_LIBS([readline],[edit],[ - AC_DEFINE([HAVE_EDITLINE],1,Define to use BSD editline) - READLINE_LIBS="$LIBS -ltinfo" - enable_readline=no - ],[],[-ltinfo]) - AS_UNSET(ac_cv_search_readline) - LIBS=$sLIBS - ]) -]) - -AS_IF([ test x"$enable_readline" != xno ],[ - AC_CHECK_HEADERS([readline/readline.h],[ - sLIBS=$LIBS - LIBS="" - AC_SEARCH_LIBS(tgetent, termcap curses ncurses ncursesw, [], []) - AC_SEARCH_LIBS(readline,[readline edit], [ - AC_DEFINE([HAVE_READLINE],1,Define to use readline or wrapper) - READLINE_LIBS=$LIBS - ]) - LIBS=$sLIBS - ]) -]) - -AC_SUBST(READLINE_LIBS) -#----------------------------------------------------------------------- - -#----------------------------------------------------------------------- -# --enable-threadsafe -# -AC_ARG_ENABLE(threadsafe, [AS_HELP_STRING( - [--enable-threadsafe], [build a thread-safe library [default=yes]])], - [], [enable_threadsafe=yes]) -if test x"$enable_threadsafe" == "xno"; then - BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_THREADSAFE=0" -else - BUILD_CFLAGS="$BUILD_CFLAGS -D_REENTRANT=1 -DSQLITE_THREADSAFE=1" - AC_SEARCH_LIBS(pthread_create, pthread) - AC_SEARCH_LIBS(pthread_mutexattr_init, pthread) -fi -#----------------------------------------------------------------------- - -#----------------------------------------------------------------------- -# --enable-dynamic-extensions -# -AC_ARG_ENABLE(dynamic-extensions, [AS_HELP_STRING( - [--enable-dynamic-extensions], [support loadable extensions [default=yes]])], - [], [enable_dynamic_extensions=yes]) -if test x"$enable_dynamic_extensions" != "xno"; then - AC_SEARCH_LIBS(dlopen, dl) -else - BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_OMIT_LOAD_EXTENSION=1" -fi -AC_MSG_CHECKING([for whether to support dynamic extensions]) -AC_MSG_RESULT($enable_dynamic_extensions) -#----------------------------------------------------------------------- - -#----------------------------------------------------------------------- -# --enable-math -# -AC_ARG_ENABLE(math, [AS_HELP_STRING( - [--enable-math], [SQL math functions [default=yes]])], - [], [enable_math=yes]) -AC_MSG_CHECKING([SQL math functions]) -if test x"$enable_math" = "xyes"; then - BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_MATH_FUNCTIONS" - AC_MSG_RESULT([enabled]) - AC_SEARCH_LIBS(ceil, m) -else - AC_MSG_RESULT([disabled]) -fi -#----------------------------------------------------------------------- - -#----------------------------------------------------------------------- -# --enable-fts4 -# -AC_ARG_ENABLE(fts4, [AS_HELP_STRING( - [--enable-fts4], [include fts4 support [default=yes]])], - [], [enable_fts4=yes]) -AC_MSG_CHECKING([FTS4 extension]) -if test x"$enable_fts4" = "xyes"; then - BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_FTS4" - AC_MSG_RESULT([enabled]) -else - AC_MSG_RESULT([disabled]) -fi -#----------------------------------------------------------------------- - -#----------------------------------------------------------------------- -# --enable-fts3 -# -AC_ARG_ENABLE(fts3, [AS_HELP_STRING( - [--enable-fts3], [include fts3 support [default=no]])], - [], []) -AC_MSG_CHECKING([FTS3 extension]) -if test x"$enable_fts3" = "xyes" -a x"$enable_fts4" = "xno"; then - BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_FTS3" - AC_MSG_RESULT([enabled]) -else - AC_MSG_RESULT([disabled]) -fi -#----------------------------------------------------------------------- - -#----------------------------------------------------------------------- -# --enable-fts5 -# -AC_ARG_ENABLE(fts5, [AS_HELP_STRING( - [--enable-fts5], [include fts5 support [default=yes]])], - [], [enable_fts5=yes]) -AC_MSG_CHECKING([FTS5 extension]) -if test x"$enable_fts5" = "xyes"; then - AC_MSG_RESULT([enabled]) - AC_SEARCH_LIBS(log, m) - BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_FTS5" -else - AC_MSG_RESULT([disabled]) -fi -#----------------------------------------------------------------------- - -#----------------------------------------------------------------------- -# --enable-rtree -# -AC_ARG_ENABLE(rtree, [AS_HELP_STRING( - [--enable-rtree], [include rtree support [default=yes]])], - [], [enable_rtree=yes]) -AC_MSG_CHECKING([RTREE extension]) -if test x"$enable_rtree" = "xyes"; then - BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_GEOPOLY" - AC_MSG_RESULT([enabled]) -else - AC_MSG_RESULT([disabled]) -fi -#----------------------------------------------------------------------- - -#----------------------------------------------------------------------- -# --enable-session -# -AC_ARG_ENABLE(session, [AS_HELP_STRING( - [--enable-session], [enable the session extension [default=no]])], - [], []) -AC_MSG_CHECKING([Session extension]) -if test x"$enable_session" = "xyes"; then - BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_PREUPDATE_HOOK" - AC_MSG_RESULT([enabled]) -else - AC_MSG_RESULT([disabled]) -fi -#----------------------------------------------------------------------- - -#----------------------------------------------------------------------- -# --enable-debug -# -AC_ARG_ENABLE(debug, [AS_HELP_STRING( - [--enable-debug], [build with debugging features enabled [default=no]])], - [], []) -AC_MSG_CHECKING([Build type]) -if test x"$enable_debug" = "xyes"; then - BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_DEBUG -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE" - CFLAGS="-g -O0" - AC_MSG_RESULT([debug]) -else - AC_MSG_RESULT([release]) -fi -#----------------------------------------------------------------------- - -#----------------------------------------------------------------------- -# --enable-static-shell -# -AC_ARG_ENABLE(static-shell, [AS_HELP_STRING( - [--enable-static-shell], - [statically link libsqlite3 into shell tool [default=yes]])], - [], [enable_static_shell=yes]) -if test x"$enable_static_shell" = "xyes"; then - EXTRA_SHELL_OBJ=sqlite3-sqlite3.$OBJEXT -else - EXTRA_SHELL_OBJ=libsqlite3.la -fi -AC_SUBST(EXTRA_SHELL_OBJ) -#----------------------------------------------------------------------- - -AC_CHECK_FUNCS(posix_fallocate) -AC_CHECK_HEADERS(zlib.h,[ - AC_SEARCH_LIBS(deflate,z,[BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_HAVE_ZLIB"]) -]) - -AC_SEARCH_LIBS(system,,,[SHELL_CFLAGS="-DSQLITE_NOHAVE_SYSTEM"]) -AC_SUBST(SHELL_CFLAGS) - -#----------------------------------------------------------------------- -# UPDATE: Maybe it's better if users just set CFLAGS before invoking -# configure. This option doesn't really add much... -# -# --enable-tempstore -# -# AC_ARG_ENABLE(tempstore, [AS_HELP_STRING( -# [--enable-tempstore], -# [in-memory temporary tables (never, no, yes, always) [default=no]])], -# [], [enable_tempstore=no]) -# AC_MSG_CHECKING([for whether or not to store temp tables in-memory]) -# case "$enable_tempstore" in -# never ) TEMP_STORE=0 ;; -# no ) TEMP_STORE=1 ;; -# always ) TEMP_STORE=3 ;; -# yes ) TEMP_STORE=3 ;; -# * ) -# TEMP_STORE=1 -# enable_tempstore=yes -# ;; -# esac -# AC_MSG_RESULT($enable_tempstore) -# AC_SUBST(TEMP_STORE) -#----------------------------------------------------------------------- - -AC_OUTPUT diff --git a/autoconf/tea/configure.ac b/autoconf/tea/configure.ac index 8abf8ad02f..d3952707a2 100644 --- a/autoconf/tea/configure.ac +++ b/autoconf/tea/configure.ac @@ -19,7 +19,7 @@ dnl to configure the system for the local environment. # so that we create the export library with the dll. #----------------------------------------------------------------------- -AC_INIT([sqlite],[3.48.0]) +AC_INIT([sqlite],[3.50.0]) #-------------------------------------------------------------------- # Call TEA_INIT as the first TEA_ macro to set up initial vars. diff --git a/autosetup/README.md b/autosetup/README.md index fd426d56d9..2d6cf723c0 100644 --- a/autosetup/README.md +++ b/autosetup/README.md @@ -33,12 +33,26 @@ $ ./configure --reference | less That will include any docs from any TCL files in the `./autosetup` dir which contain certain (simple) markup defined by autosetup. -This project's own autosetup-related APIs are in [proj.tcl][] or -[auto.def][]. The former contains helper APIs which are, more or -less, portable across projects (that file is re-used as-is in other -projects) and all have a `proj-` name prefix. The latter is the main -configure script driver and contains related functions which are -specific to this tree. +This project's own configuration-related TCL code is spread across the +following files: + +- [proj.tcl][]: project-agnostic utility code for autosetup-driven + projects. This file is designed to be shared between this project, + other projects managed under the SQLite/Hwaci umbrella + (e.g. Fossil), and personal projects of SQLite's developers. It is + essentially an amalgamation of a decade's worth of autosetup-related + utility code. +- [auto.def][]: the primary driver for the `./configure` process. + When we talk about "the configure script," we're referring to + this file. +- [sqlite-config.tcl][]: utility code which is too project-specific + for `proj.tcl`. We split this out of `auto.def` so that it can be + used by both `auto.def` and... +- [autoconf/auto.def][]: the main driver script for the "autoconf" + bundle's configure script. It is essentially a slightly trimmed-down + version of the main `auto.def` file. The `autoconf` dir was ported + from the Autotools to Autosetup in the 3.49.0 dev cycle but retains + the "autoconf" name to minimize downstream disruption. @@ -47,9 +61,11 @@ Autosetup API Tips This section briefly covers only APIs which are frequently useful in day-to-day maintenance and might not be immediately recognized as such -obvious from a casual perusal of [auto.def][]. The complete docs of -those with `proj-` prefix can be found in [proj.tcl][]. The others are -scattered around [the TCL files in ./autosetup](/dir/autosetup). +obvious from a casual perusal of the relevant TCL files. The complete +docs of those with `proj-` prefix can be found in [proj.tcl][] and +those with an `sqlite-` prefix are in [sqlite-config.tcl][]. The +others are scattered around [the TCL files in +./autosetup](/dir/autosetup). In (mostly) alphabetical order: @@ -180,6 +196,30 @@ APIs must not use `[file normalize]`, but autosetup provides a TCL-only implementation of `[file-normalize]` (note the dash) for portable use in the configure script. +Known TCL Incompatibilities +------------------------------------------------------------------------ + +A summary of known incompatibilities in JimTCL + +- **CRNL line endings**: prior to 2025-02-05 `fconfigure -translation ...` + was a no-op in JimTCL, and it emits CRNL line endings by default on + Windows. Since then, it supports `-translation binary`, which is + close enough to `-translation lf` for our purposes. When working + with files using the `open` command, it is important to use mode + `"rb"` or `"wb"`, as appropriate, so that the output does not get + CRNL-mangled on Windows. + +- **`file copy`** does not support multiple source files. See + [](/info/61f18c96183867fe) for a workaround. + +- **Regular expressions**: + + - Patterns treat `\nnn` octal values as back-references (which it + does not support). Those can be reformulated as demonstrated in + [](/info/aeac23359bb681c0). + + - `regsub` does not support the `\y` flag. A workaround is demonstrated + in [](/info/c2e5dd791cce3ec4). Design Conventions @@ -331,8 +371,10 @@ If autosetup is upgraded and this patch is _not_ applied the invoking [Autosetup]: https://msteveb.github.io/autosetup/ [auto.def]: /file/auto.def +[autoconf/auto.def]: /file/autoconf/auto.def [autosetup-git]: https://github.com/msteveb/autosetup [proj.tcl]: /file/autosetup/proj.tcl +[sqlite-config.tcl]: /file/autosetup/sqlite-config.tcl [Makefile.in]: /file/Makefile.in [main.mk]: /file/main.mk [JimTCL]: https://jim.tcl.tk diff --git a/autosetup/jimsh0.c b/autosetup/jimsh0.c index 84db85a207..0526b9a446 100644 --- a/autosetup/jimsh0.c +++ b/autosetup/jimsh0.c @@ -1198,6 +1198,11 @@ int Jim_OpenForRead(const char *filename); #define Jim_FileStat _fstat64 #define Jim_Lseek _lseeki64 #define O_TEXT _O_TEXT + #define O_BINARY _O_BINARY + #define Jim_SetMode _setmode + #ifndef STDIN_FILENO + #define STDIN_FILENO 0 + #endif #else #if defined(HAVE_STAT64) @@ -1864,7 +1869,7 @@ int Jim_tclcompatInit(Jim_Interp *interp) " $f buffering $v\n" " }\n" " -tr* {\n" -"\n" +" $f translation $v\n" " }\n" " default {\n" " return -code error \"fconfigure: unknown option $n\"\n" @@ -2936,6 +2941,28 @@ static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv) return JIM_OK; } +static int aio_cmd_translation(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + enum {OPT_BINARY, OPT_TEXT}; + static const char * const options[] = { + "binary", + "text", + NULL + }; + int opt; + + if (Jim_GetEnum(interp, argv[0], options, &opt, NULL, JIM_ERRMSG) != JIM_OK) { + return JIM_ERR; + } +#if defined(Jim_SetMode) + else { + AioFile *af = Jim_CmdPrivData(interp); + Jim_SetMode(af->fd, opt == OPT_BINARY ? O_BINARY : O_TEXT); + } +#endif + return JIM_OK; +} + static int aio_cmd_readsize(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { AioFile *af = Jim_CmdPrivData(interp); @@ -3145,6 +3172,13 @@ static const jim_subcmd_type aio_command_table[] = { 0, 2, + }, + { "translation", + "binary|text", + aio_cmd_translation, + 1, + 1, + }, { "readsize", "?size?", @@ -24425,6 +24459,10 @@ int main(int argc, char *const argv[]) } if (retcode != JIM_EXIT) { JimSetArgv(interp, 0, NULL); + if (!isatty(STDIN_FILENO)) { + + goto eval_stdin; + } retcode = Jim_InteractivePrompt(interp); } } @@ -24447,6 +24485,7 @@ int main(int argc, char *const argv[]) Jim_SetVariableStr(interp, "argv0", Jim_NewStringObj(interp, argv[1], -1)); JimSetArgv(interp, argc - 2, argv + 2); if (strcmp(argv[1], "-") == 0) { +eval_stdin: retcode = Jim_Eval(interp, "eval [info source [stdin read] stdin 1]"); } else { retcode = Jim_EvalFile(interp, argv[1]); diff --git a/autosetup/proj.tcl b/autosetup/proj.tcl index a469c898de..fdfbf3a5ed 100644 --- a/autosetup/proj.tcl +++ b/autosetup/proj.tcl @@ -114,11 +114,13 @@ proc proj-bold {str} { # # If the -notice flag it used then it emits using [user-notice], which # means its rendering will (A) go to stderr and (B) be delayed until -# the next time autosetup goes to output a message. If -notice -# is not used, it will send the message to stdout without delay. +# the next time autosetup goes to output a message. # # If the -error flag is provided then it renders the message # immediately to stderr and then exits. +# +# If neither -notice nor -error are used, the message will be sent to +# stdout without delay. proc proj-indented-notice {args} { set fErr "" set outFunc "puts" @@ -126,6 +128,7 @@ proc proj-indented-notice {args} { switch -exact -- [lindex $args 0] { -error { set args [lassign $args fErr] + set outFunc "user-notice" } -notice { set args [lassign $args -] @@ -181,10 +184,10 @@ proc proj-lshift_ {listVar {count 1}} { ######################################################################## # Expects to receive string input, which it splits on newlines, strips -# out any lines which begin with an number of whitespace followed by a -# '#', and returns a value containing the [append]ed results of each +# out any lines which begin with any number of whitespace followed by +# a '#', and returns a value containing the [append]ed results of each # remaining line with a \n between each. -proc proj-strip-hash-comments_ {val} { +proc proj-strip-hash-comments {val} { set x {} foreach line [split $val \n] { if {![string match "#*" [string trimleft $line]]} { @@ -200,7 +203,7 @@ proc proj-strip-hash-comments_ {val} { # A proxy for cc-check-function-in-lib which does not make any global # changes to the LIBS define. Returns the result of # cc-check-function-in-lib (i.e. true or false). The resulting linker -# flags are stored in ${lib_${function}}. +# flags are stored in the [define] named lib_${function}. proc proj-check-function-in-lib {function libs {otherlibs {}}} { set found 0 define-push {LIBS} { @@ -276,9 +279,9 @@ proc proj-find-executable-path {args} { # a binary, sets a define (see below) to the result, and returns the # result (an empty string if not found). # -# The define'd name is: if defName is empty then "BIN_X" is used, -# where X is the upper-case form of $binName with any '-' characters -# replaced with '_'. +# The define'd name is: If $defName is not empty, it is used as-is. If +# $defName is empty then "BIN_X" is used, where X is the upper-case +# form of $binName with any '-' characters replaced with '_'. proc proj-bin-define {binName {defName {}}} { set check [proj-find-executable-path -v $binName] if {"" eq $defName} { @@ -760,7 +763,7 @@ proc proj-exe-extension {} { # Trivia: for .dylib files, the linker needs the -dynamiclib flag # instead of -shared. proc proj-dll-extension {} { - proc inner {key} { + set inner {{key} { switch -glob -- [get-define $key] { *apple* { return ".dylib" @@ -772,9 +775,9 @@ proc proj-dll-extension {} { return ".so" } } - } - define BUILD_DLLEXT [inner build] - define TARGET_DLLEXT [inner host] + }} + define BUILD_DLLEXT [apply $inner build] + define TARGET_DLLEXT [apply $inner host] } ######################################################################## @@ -784,18 +787,20 @@ proc proj-dll-extension {} { # BUILD_LIBEXT and TARGET_LIBEXT to the conventional static library # extension for the being-built-on resp. the target platform. proc proj-lib-extension {} { - proc inner {key} { + set inner {{key} { switch -glob -- [get-define $key] { *-*-ming* - *-*-cygwin - *-*-msys { - return ".lib" + return ".a" + # ^^^ this was ".lib" until 2025-02-07. See + # https://sqlite.org/forum/forumpost/02db2d4240 } default { return ".a" } } - } - define BUILD_LIBEXT [inner build] - define TARGET_LIBEXT [inner host] + }} + define BUILD_LIBEXT [apply $inner build] + define TARGET_LIBEXT [apply $inner host] } ######################################################################## @@ -850,16 +855,16 @@ proc proj-affirm-files-exist {args} { # If the given directory is found, it expects to find emsdk_env.sh in # that directory, as well as the emcc compiler somewhere under there. # -# If the --with-emsdk flag is explicitly provided and the SDK is not -# found then a fatal error is generated, otherwise failure to find the -# SDK is not fatal. +# If the --with-emsdk[=DIR] flag is explicitly provided and the SDK is +# not found then a fatal error is generated, otherwise failure to find +# the SDK is not fatal. # # Defines the following: # -# - EMSDK_HOME = top dir of the emsdk or "". -# - EMSDK_ENV_SH = path to EMSDK_HOME/emsdk_env.sh or "" -# - BIN_EMCC = $EMSDK_HOME/upstream/emscripten/emcc or "" # - HAVE_EMSDK = 0 or 1 (this function's return value) +# - EMSDK_HOME = "" or top dir of the emsdk +# - EMSDK_ENV_SH = "" or $EMSDK_HOME/emsdk_env.sh +# - BIN_EMCC = "" or $EMSDK_HOME/upstream/emscripten/emcc # # Returns 1 if EMSDK_ENV_SH is found, else 0. If EMSDK_HOME is not empty # but BIN_EMCC is then emcc was not found in the EMSDK_HOME, in which @@ -1119,7 +1124,7 @@ proc proj-dump-defs-json {file args} { # that [opt-value canonical] will return X if --alias=X is passed to # configure. proc proj-xfer-options-aliases {mapping} { - foreach {hidden - canonical} [proj-strip-hash-comments_ $mapping] { + foreach {hidden - canonical} [proj-strip-hash-comments $mapping] { if {[proj-opt-was-provided $hidden]} { if {[proj-opt-was-provided $canonical]} { proj-fatal "both --$canonical and its alias --$hidden were used. Use only one or the other." diff --git a/autosetup/sqlite-config.tcl b/autosetup/sqlite-config.tcl new file mode 100644 index 0000000000..1aaa8af374 --- /dev/null +++ b/autosetup/sqlite-config.tcl @@ -0,0 +1,1485 @@ +# This file holds functions for autosetup which are specific to the +# sqlite build tree. They are in this file, instead of auto.def, so +# that they can be reused in the TEA sub-tree. This file requires +# functions from proj.tcl. + +use cc cc-db cc-shared cc-lib pkg-config proj + +# +# Object for communicating config-time state across various +# auto.def-related pieces. +# +array set sqliteConfig [proj-strip-hash-comments { + # + # Gets set to 1 when using jimsh for code generation. May affect + # later decisions. + use-jim-for-codegen 0 + # + # Pass msg-debug=1 to configure to enable obnoxiously loud output + # from [msg-debug]. + msg-debug-enabled 0 + # + # Output file for --dump-defines. Intended only for build debugging + # and not part of the public build interface. + dump-defines-txt ./config.defines.txt + # + # Output file for --dump-defines-json. This is the autosetup + # counterpart of the historical "DEFS" var which was generated by + # the autotools in the pre-processed autotools builds (but not in + # the canonical tree). Generation of this file is disabled (via an + # empty file name) until/unless someone voices a specific interest + # in it. The original motivating use case is handled fine by + # sqlite_cfg.h. + dump-defines-json "" +}] + +# +# Set to 1 when cross-compiling This value may be changed by certain +# build options, so it's important that config code which checks for +# cross-compilation uses this var instead of +# [proj-is-cross-compiling]. +# +set sqliteConfig(is-cross-compiling) [proj-is-cross-compiling] + +######################################################################## +# Runs some common initialization which must happen immediately after +# autosetup's [options] function is called. This is also a convenient +# place to put some generic pieces common to both the canonical +# top-level build and the "autoconf" build, but it's not intended to +# be a catch-all dumping ground for such. +proc sqlite-post-options-init {} { + # + # Carry values from hidden --flag aliases over to their canonical + # flag forms. This list must include only options which are common + # to both the top-level auto.def and autoconf/auto.def. + # + proj-xfer-options-aliases { + with-readline-inc => with-readline-cflags + with-readline-lib => with-readline-ldflags + with-debug => debug + } + sqlite-autoreconfig + proj-file-extensions + if {".exe" eq [get-define TARGET_EXEEXT]} { + define SQLITE_OS_UNIX 0 + define SQLITE_OS_WIN 1 + } else { + define SQLITE_OS_UNIX 1 + define SQLITE_OS_WIN 0 + } + set ::sqliteConfig(msg-debug-enabled) [proj-val-truthy [get-env msg-debug 0]] + sqlite-setup-package-info +} + +######################################################################## +# Called by [sqlite-post-options-init] to set up PACKAGE_NAME and +# related defines. +proc sqlite-setup-package-info {} { + set srcdir $::autosetup(srcdir) + set PACKAGE_VERSION [proj-file-content -trim $srcdir/VERSION] + define PACKAGE_NAME "sqlite" + define PACKAGE_URL {https://sqlite.org} + define PACKAGE_VERSION $PACKAGE_VERSION + define PACKAGE_STRING "[get-define PACKAGE_NAME] $PACKAGE_VERSION" + define PACKAGE_BUGREPORT [get-define PACKAGE_URL]/forum + msg-result "Source dir = $srcdir" + msg-result "Build dir = $::autosetup(builddir)" + msg-result "Configuring SQLite version $PACKAGE_VERSION" +} + +######################################################################## +# Internal config-time debugging output routine. It generates no +# output unless msg-debug=1 is passed to the configure script. +proc msg-debug {msg} { + if {$::sqliteConfig(msg-debug-enabled)} { + puts stderr [proj-bold "** DEBUG: $msg"] + } +} + +######################################################################## +# Sets up the SQLITE_AUTORECONFIG define. +proc sqlite-autoreconfig {} { + # + # SQLITE_AUTORECONFIG contains make target rules for re-running the + # configure script with the same arguments it was initially invoked + # with. This can be used to automatically reconfigure + # + proc squote {arg} { + # Wrap $arg in single-quotes if it looks like it might need that + # to avoid mis-handling as a shell argument. We assume that $arg + # will never contain any single-quote characters. + if {[string match {*[ &;$*"]*} $arg]} { return '$arg' } + return $arg + } + define-append SQLITE_AUTORECONFIG cd [squote $::autosetup(builddir)] && [squote $::autosetup(srcdir)/configure] + #{*}$::autosetup(argv) breaks with --flag='val with spaces', so... + foreach arg $::autosetup(argv) { + define-append SQLITE_AUTORECONFIG [squote $arg] + } + rename squote "" +} + +define OPT_FEATURE_FLAGS {} ; # -DSQLITE_OMIT/ENABLE flags. +define OPT_SHELL {} ; # Feature-related CFLAGS for the sqlite3 CLI app +######################################################################## +# Adds $args, if not empty, to OPT_FEATURE_FLAGS. If the first arg is +# -shell then it strips that arg and passes the remaining args the +# sqlite-add-shell-opt in addition to adding them to +# OPT_FEATURE_FLAGS. +proc sqlite-add-feature-flag {args} { + set shell "" + if {"-shell" eq [lindex $args 0]} { + set args [lassign $args shell] + } + if {"" ne $args} { + if {"" ne $shell} { + sqlite-add-shell-opt {*}$args + } + define-append OPT_FEATURE_FLAGS {*}$args + } +} +# Appends $args, if not empty, to OPT_SHELL. +proc sqlite-add-shell-opt {args} { + if {"" ne $args} { + define-append OPT_SHELL {*}$args + } +} + +######################################################################## +# Check for log(3) in libm and die with an error if it is not +# found. $featureName should be the feature name which requires that +# function (it's used only in error messages). defines LDFLAGS_MATH to +# the required linker flags (which may be empty even if the math APIs +# are found, depending on the OS). +proc sqlite-affirm-have-math {featureName} { + if {"" eq [get-define LDFLAGS_MATH ""]} { + if {![msg-quiet proj-check-function-in-lib log m]} { + user-error "Missing math APIs for $featureName" + } + define LDFLAGS_MATH [get-define lib_log ""] + undefine lib_log + } +} + +######################################################################## +# Run checks for required binaries, like ld and ar. In the canonical +# build this must come before [sqlite-handle-wasi-sdk]. +proc sqlite-check-common-bins {} { + cc-check-tools ld ar ; # must come before [sqlite-handle-wasi-sdk] + if {"" eq [proj-bin-define install]} { + proj-warn "Cannot find install binary, so 'make install' will not work." + define BIN_INSTALL false + } +} + +######################################################################## +# Run checks for system-level includes and libs which are common to +# both the canonical build and the "autoconf" bundle. +proc sqlite-check-common-system-deps {} { + # + # Check for needed/wanted data types + cc-with {-includes stdint.h} \ + {cc-check-types int8_t int16_t int32_t int64_t intptr_t \ + uint8_t uint16_t uint32_t uint64_t uintptr_t} + + # + # Check for needed/wanted functions + cc-check-functions gmtime_r isnan localtime_r localtime_s \ + malloc_usable_size strchrnul usleep utime pread pread64 pwrite pwrite64 + + set ldrt "" + # Collapse funcs from librt into LDFLAGS_RT. + # Some systems (ex: SunOS) require -lrt in order to use nanosleep + foreach func {fdatasync nanosleep} { + if {[proj-check-function-in-lib $func rt]} { + lappend ldrt [get-define lib_${func}] + } + } + define LDFLAGS_RT [join [lsort -unique $ldrt] ""] + + # + # Check for needed/wanted headers + cc-check-includes \ + sys/types.h sys/stat.h dlfcn.h unistd.h \ + stdlib.h malloc.h memory.h \ + string.h strings.h \ + inttypes.h + + if {[cc-check-includes zlib.h] && [proj-check-function-in-lib deflate z]} { + # TODO? port over the more sophisticated zlib search from the fossil auto.def + define HAVE_ZLIB 1 + define LDFLAGS_ZLIB -lz + sqlite-add-shell-opt -DSQLITE_HAVE_ZLIB=1 + } else { + define HAVE_ZLIB 0 + define LDFLAGS_ZLIB "" + } +} + +proc sqlite-setup-default-cflags {} { + ######################################################################## + # We differentiate between two C compilers: the one used for binaries + # which are to run on the build system (in autosetup it's called + # CC_FOR_BUILD and in Makefile.in it's $(B.cc)) and the one used for + # compiling binaries for the target system (CC a.k.a. $(T.cc)). + # Normally they're the same, but they will differ when + # cross-compiling. + # + # When cross-compiling we default to not using the -g flag, based on a + # /chat discussion prompted by + # https://sqlite.org/forum/forumpost/9a67df63eda9925c + set defaultCFlags {-O2} + if {!$::sqliteConfig(is-cross-compiling)} { + lappend defaultCFlags -g + } + define CFLAGS [proj-get-env CFLAGS $defaultCFlags] + # BUILD_CFLAGS is the CFLAGS for CC_FOR_BUILD. + define BUILD_CFLAGS [proj-get-env BUILD_CFLAGS {-g}] + + # Copy all CFLAGS and CPPFLAGS entries matching -DSQLITE_OMIT* and + # -DSQLITE_ENABLE* to OPT_FEATURE_FLAGS. This behavior is derived + # from the legacy build and was missing the 3.48.0 release (the + # initial Autosetup port). + # https://sqlite.org/forum/forumpost/9801e54665afd728 + # + # Handling of CPPFLAGS, as well as removing ENABLE/OMIT from + # CFLAGS/CPPFLAGS, was missing in the 3.49.0 release as well. + # + # If any configure flags for features are in conflict with + # CFLAGS/CPPFLAGS-specified feature flags, all bets are off. There + # are no guarantees about which one will take precedence. + foreach flagDef {CFLAGS CPPFLAGS} { + set tmp "" + foreach cf [get-define $flagDef ""] { + switch -glob -- $cf { + -DSQLITE_OMIT* - + -DSQLITE_ENABLE* { + sqlite-add-feature-flag $cf + } + default { + lappend tmp $cf + } + } + } + define $flagDef $tmp + } + + # Strip all SQLITE_ENABLE/OMIT flags from BUILD_CFLAGS, + # for compatibility with the legacy build. + set tmp "" + foreach cf [get-define BUILD_CFLAGS ""] { + switch -glob -- $cf { + -DSQLITE_OMIT* - + -DSQLITE_ENABLE* {} + default { + lappend tmp $cf + } + } + } + define BUILD_CFLAGS $tmp +} + +######################################################################## +# Handle various SQLITE_ENABLE_... feature flags. +proc sqlite-handle-common-feature-flags {} { + msg-result "Feature flags..." + foreach {boolFlag featureFlag ifSetEvalThis} { + all {} { + # The 'all' option must be first in this list. + proj-opt-set fts4 + proj-opt-set fts5 + proj-opt-set geopoly + proj-opt-set rtree + proj-opt-set session + } + fts4 -DSQLITE_ENABLE_FTS4 {sqlite-affirm-have-math fts4} + fts5 -DSQLITE_ENABLE_FTS5 {sqlite-affirm-have-math fts5} + geopoly -DSQLITE_ENABLE_GEOPOLY {proj-opt-set rtree} + rtree -DSQLITE_ENABLE_RTREE {} + session {-DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_PREUPDATE_HOOK} {} + update-limit -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT {} + memsys5 -DSQLITE_ENABLE_MEMSYS5 {} + memsys3 {} { + if {[opt-bool memsys5]} { + proj-warn "not enabling memsys3 because memsys5 is enabled." + expr 0 + } else { + sqlite-add-feature-flag -DSQLITE_ENABLE_MEMSYS3 + } + } + scanstatus -DSQLITE_ENABLE_STMT_SCANSTATUS {} + } { + if {$boolFlag ni $::autosetup(options)} { + # Skip flags which are in the canonical build but not + # the autoconf bundle. + continue + } + proj-if-opt-truthy $boolFlag { + sqlite-add-feature-flag $featureFlag + if {0 != [eval $ifSetEvalThis] && "all" ne $boolFlag} { + msg-result " + $boolFlag" + } + } { + if {"all" ne $boolFlag} { + msg-result " - $boolFlag" + } + } + } + ######################################################################## + # Invert the above loop's logic for some SQLITE_OMIT_... cases. If + # config option $boolFlag is false, [sqlite-add-feature-flag + # $featureFlag], where $featureFlag is intended to be + # -DSQLITE_OMIT_... + foreach {boolFlag featureFlag} { + json -DSQLITE_OMIT_JSON + } { + if {[proj-opt-truthy $boolFlag]} { + msg-result " + $boolFlag" + } else { + sqlite-add-feature-flag $featureFlag + msg-result " - $boolFlag" + } + } + +} + +######################################################################### +# Remove duplicates from the final feature flag sets and show them to +# the user. +proc sqlite-finalize-feature-flags {} { + set oFF [get-define OPT_FEATURE_FLAGS] + if {"" ne $oFF} { + define OPT_FEATURE_FLAGS [lsort -unique $oFF] + msg-result "Library feature flags: [get-define OPT_FEATURE_FLAGS]" + } + set oFF [get-define OPT_SHELL] + if {"" ne $oFF} { + define OPT_SHELL [lsort -unique $oFF] + msg-result "Shell options: [get-define OPT_SHELL]" + } +} + +######################################################################## +# Checks for the --debug flag, defining SQLITE_DEBUG to 1 if it is +# true. TARGET_DEBUG gets defined either way, with content depending +# on whether --debug is true or false. +proc sqlite-handle-debug {} { + msg-checking "SQLITE_DEBUG build? " + proj-if-opt-truthy debug { + define SQLITE_DEBUG 1 + define TARGET_DEBUG {-g -DSQLITE_DEBUG=1 -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE -O0 -Wall} + proj-opt-set memsys5 + msg-result yes + } { + define TARGET_DEBUG {-DNDEBUG} + msg-result no + } +} + +######################################################################## +# "soname" for libsqlite3.so. See discussion at: +# https://sqlite.org/src/forumpost/5a3b44f510df8ded +proc sqlite-handle-soname {} { + define LDFLAGS_LIBSQLITE3_SONAME "" + if {[proj-opt-was-provided soname]} { + set soname [join [opt-val soname] ""] + } else { + # Enabling soname breaks linking for the --dynlink-tools feature, + # and this project has no direct use for soname, so default to + # none. Package maintainers, on the other hand, like to have an + # soname. + set soname none + } + switch -exact -- $soname { + none - "" { return 0 } + legacy { set soname libsqlite3.so.0 } + default { + if {[string match libsqlite3.* $soname]} { + # use it as-is + } else { + # Assume it's a suffix + set soname "libsqlite3.so.${soname}" + } + } + } + msg-debug "soname=$soname" + if {[proj-check-soname $soname]} { + define LDFLAGS_LIBSQLITE3_SONAME [get-define LDFLAGS_SONAME_PREFIX]$soname + msg-result "Setting SONAME using: [get-define LDFLAGS_LIBSQLITE3_SONAME]" + } elseif {[proj-opt-was-provided soname]} { + # --soname was explicitly requested but not available, so fail fatally + proj-fatal "This environment does not support SONAME." + } else { + # --soname was not explicitly requested but not available, so just warn + msg-result "This environment does not support SONAME." + } +} + +######################################################################## +# If --enable-thresafe is set, this adds -DSQLITE_THREADSAFE=1 to +# OPT_FEATURE_FLAGS and sets LDFLAGS_PTHREAD to the linker flags +# needed for linking pthread. If --enable-threadsafe is not set, adds +# -DSQLITE_THREADSAFE=0 to OPT_FEATURE_FLAGS and sets LDFLAGS_PTHREAD +# to an empty string. +proc sqlite-handle-threadsafe {} { + msg-checking "Support threadsafe operation? " + proj-if-opt-truthy threadsafe { + msg-result yes + sqlite-add-feature-flag -DSQLITE_THREADSAFE=1 + if {![proj-check-function-in-lib pthread_create pthread] + || ![proj-check-function-in-lib pthread_mutexattr_init pthread]} { + user-error "Missing required pthread bits" + } + define LDFLAGS_PTHREAD [get-define lib_pthread_create] + undefine lib_pthread_create + # Recall that LDFLAGS_PTHREAD might be empty even if pthreads if + # found because it's in -lc on some platforms. + } { + msg-result no + sqlite-add-feature-flag -DSQLITE_THREADSAFE=0 + define LDFLAGS_PTHREAD "" + } +} + +######################################################################## +# Handles the --with-tempstore flag. +# +# The test fixture likes to set SQLITE_TEMP_STORE on its own, so do +# not set that feature flag unless it was explicitly provided to the +# configure script. +proc sqlite-handle-tempstore {} { + if {[proj-opt-was-provided with-tempstore]} { + set ts [opt-val with-tempstore no] + set tsn 1 + msg-checking "Use an in-RAM database for temporary tables? " + switch -exact -- $ts { + never { set tsn 0 } + no { set tsn 1 } + yes { set tsn 2 } + always { set tsn 3 } + default { + user-error "Invalid --with-tempstore value '$ts'. Use one of: never, no, yes, always" + } + } + msg-result $ts + sqlite-add-feature-flag -DSQLITE_TEMP_STORE=$tsn + } +} + +######################################################################## +# Check for the Emscripten SDK for building the web-based wasm +# components. The core lib and tools do not require this but ext/wasm +# does. Most of the work is done via [proj-check-emsdk], then this +# function adds the following defines: +# +# - EMCC_WRAPPER = "" or top-srcdir/tool/emcc.sh +# - BIN_WASM_OPT = "" or path to wasm-opt +# - BIN_WASM_STRIP = "" or path to wasm-strip +# +# Noting that: +# +# 1) Not finding the SDK is not fatal at this level, nor is failure to +# find one of the related binaries. +# +# 2) wasm-strip is part of the wabt package: +# +# https://github.com/WebAssembly/wabt +# +# and this project requires it for production-mode builds but not dev +# builds. +# +proc sqlite-handle-emsdk {} { + define EMCC_WRAPPER "" + define BIN_WASM_STRIP "" + define BIN_WASM_OPT "" + set srcdir $::autosetup(srcdir) + if {$srcdir ne $::autosetup(builddir)} { + # The EMSDK pieces require writing to the original source tree + # even when doing an out-of-tree build. The ext/wasm pieces do not + # support an out-of-tree build so we treat that case as if EMSDK + # were not found. + msg-result "Out-of tree build: not checking for EMSDK." + return + } + set emccSh $srcdir/tool/emcc.sh + set extWasmConfig $srcdir/ext/wasm/config.make + if {![get-define HAVE_WASI_SDK] && [proj-check-emsdk]} { + define EMCC_WRAPPER $emccSh + set emsdkHome [get-define EMSDK_HOME ""] + proj-assert {"" ne $emsdkHome} + #define EMCC_WRAPPER ""; # just for testing + proj-bin-define wasm-strip + proj-bin-define bash; # ext/wasm/GNUmakefile requires bash + if {[file-isexec $emsdkHome/upstream/bin/wasm-opt]} { + define BIN_WASM_OPT $emsdkHome/upstream/bin/wasm-opt + } else { + # Maybe there's a copy in the path? + proj-bin-define wasm-opt BIN_WASM_OPT + } + proj-make-from-dot-in $emccSh $extWasmConfig + catch {exec chmod u+x $emccSh} + } else { + define EMCC_WRAPPER "" + file delete -force -- $emccSh $extWasmConfig + } +} + +######################################################################## +# sqlite-check-line-editing jumps through proverbial hoops to try to +# find a working line-editing library, setting: +# +# - HAVE_READLINE to 0 or 1 +# - HAVE_LINENOISE to 0, 1, or 2 +# - HAVE_EDITLINE to 0 or 1 +# +# Only one of ^^^ those will be set to non-0. +# +# - LDFLAGS_READLINE = linker flags or empty string +# +# - CFLAGS_READLINE = compilation flags for clients or empty string. +# +# Note that LDFLAGS_READLINE and CFLAGS_READLINE may refer to +# linenoise or editline, not necessarily libreadline. In some cases +# it will set HAVE_READLINE=1 when it's really using editline, for +# reasons described in this function's comments. +# +# Returns a string describing which line-editing approach to use, or +# "none" if no option is available. +# +# Order of checks: +# +# 1) --with-linenoise trumps all others and skips all of the +# complexities involved with the remaining options. +# +# 2) --editline trumps --readline +# +# 3) --disable-readline trumps --readline +# +# 4) Default to automatic search for optional readline +# +# 5) Try to find readline or editline. If it's not found AND the +# corresponding --FEATURE flag was explicitly given, fail fatally, +# else fail silently. +proc sqlite-check-line-editing {} { + msg-result "Checking for line-editing capability..." + define HAVE_READLINE 0 + define HAVE_LINENOISE 0 + define HAVE_EDITLINE 0 + define LDFLAGS_READLINE "" + define CFLAGS_READLINE "" + set failIfNotFound 0 ; # Gets set to 1 for explicit --FEATURE requests + # so that we know whether to fail fatally or not + # if the library is not found. + set libsForReadline {readline edit} ; # -l names to check for readline(). + # The libedit check changes this. + set editLibName "readline" ; # "readline" or "editline" + set editLibDef "HAVE_READLINE" ; # "HAVE_READLINE" or "HAVE_EDITLINE" + set dirLn [opt-val with-linenoise] + if {"" ne $dirLn} { + # Use linenoise from a copy of its sources (not a library)... + if {![file isdir $dirLn]} { + proj-fatal "--with-linenoise value is not a directory" + } + set lnH $dirLn/linenoise.h + if {![file exists $lnH] } { + proj-fatal "Cannot find linenoise.h in $dirLn" + } + set lnC "" + set lnCOpts {linenoise-ship.c linenoise.c} + foreach f $lnCOpts { + if {[file exists $dirLn/$f]} { + set lnC $dirLn/$f + break; + } + } + if {"" eq $lnC} { + proj-fatal "Cannot find any of $lnCOpts in $dirLn" + } + set flavor "" + set lnVal [proj-which-linenoise $lnH] + switch -- $lnVal { + 1 { set flavor "antirez" } + 2 { set flavor "msteveb" } + default { + proj-fatal "Cannot determine the flavor of linenoise from $lnH" + } + } + define CFLAGS_READLINE "-I$dirLn $lnC" + define HAVE_LINENOISE $lnVal + sqlite-add-shell-opt -DHAVE_LINENOISE=$lnVal + if {$::sqliteConfig(use-jim-for-codegen) && 2 == $lnVal} { + define-append CFLAGS_JIMSH -DUSE_LINENOISE [get-define CFLAGS_READLINE] + user-notice "Adding linenoise support to jimsh." + } + return "linenoise ($flavor)" + } elseif {[opt-bool editline]} { + # libedit mimics libreadline and on some systems does not have its + # own header installed (instead, that of libreadline is used). + # + # shell.c historically expects HAVE_EDITLINE to be set for + # libedit, but it then expects to see , which + # some system's don't actually have despite having libedit. If we + # end up finding below, we will use + # -DHAVE_EDITLINE=1, else we will use -DHAVE_READLINE=1. In either + # case, we will link against libedit. + set failIfNotFound 1 + set libsForReadline {edit} + set editLibName editline + } elseif {![opt-bool readline]} { + msg-result "Readline support explicitly disabled with --disable-readline" + return "none" + } elseif {[proj-opt-was-provided readline]} { + # If an explicit --[enable-]readline was used, fail if it's not + # found, else treat the feature as optional. + set failIfNotFound 1 + } + + # Transform with-readline-header=X to with-readline-cflags=-I... + set v [opt-val with-readline-header] + proj-opt-set with-readline-header "" + if {"" ne $v} { + if {"auto" eq $v} { + proj-opt-set with-readline-cflags auto + } else { + set v [file dirname $v] + if {[string match */readline $v]} { + # Special case: if the path includes .../readline/readline.h, + # set the -I to one dir up from that because our sources + # #include or . + set v [file dirname $v] + } + proj-opt-set with-readline-cflags "-I$v" + } + } + + # Look for readline.h + set rlInc [opt-val with-readline-cflags auto] + if {"auto" eq $rlInc} { + set rlInc "" + if {$::sqliteConfig(is-cross-compiling)} { + # ^^^ this check is derived from the legacy configure script. + proj-warn "Skipping check for readline.h because we're cross-compiling." + } else { + set dirs "[get-define prefix] /usr /usr/local /usr/local/readline /usr/contrib /mingw" + set subdirs "include/$editLibName" + if {"editline" eq $editLibName} { + lappend subdirs include/readline + # ^^^ editline, on some systems, does not have its own header, + # and uses libreadline's header. + } + lappend subdirs include + # ^^^ The dirs and subdirs lists are, except for the inclusion + # of $prefix and editline, from the legacy configure script + set rlInc [proj-search-for-header-dir readline.h \ + -dirs $dirs -subdirs $subdirs] + if {"" ne $rlInc} { + if {[string match */readline $rlInc]} { + set rlInc [file dirname $rlInc]; # shell #include's + } elseif {[string match */editline $rlInc]} { + set editLibDef HAVE_EDITLINE + set rlInc [file dirname $rlInc]; # shell #include's + } + set rlInc "-I${rlInc}" + } + } + } elseif {"" ne $rlInc && ![string match *-I* $rlInc]} { + proj-fatal "Argument to --with-readline-cflags is intended to be CFLAGS and contain -I..." + } + + # If readline.h was found/specified, look for lib(readline|edit)... + # + # This is not quite straightforward because both libreadline and + # libedit typically require some other library which (according to + # legacy autotools-generated tests) provides tgetent(3). On some + # systems that's built into libreadline/edit, on some (most?) its in + # lib[n]curses, and on some it's in libtermcap. + set rlLib "" + if {"" ne $rlInc} { + set rlLib [opt-val with-readline-ldflags] + if {"" eq $rlLib || "auto" eq $rlLib} { + set rlLib "" + set libTerm "" + if {[proj-check-function-in-lib tgetent "$editLibName ncurses curses termcap"]} { + # ^^^ that libs list comes from the legacy configure script ^^^ + set libTerm [get-define lib_tgetent] + undefine lib_tgetent + } + if {$editLibName eq $libTerm} { + set rlLib $libTerm + } elseif {[proj-check-function-in-lib readline $libsForReadline $libTerm]} { + set rlLib [get-define lib_readline] + lappend rlLib $libTerm + undefine lib_readline + } + } + } + + # If we found a library, configure the build to use it... + if {"" ne $rlLib} { + if {"editline" eq $editLibName && "HAVE_READLINE" eq $editLibDef} { + # Alert the user that, despite outward appearances, we won't be + # linking to the GPL'd libreadline. Presumably that distinction is + # significant for those using --editline. + proj-indented-notice { + NOTE: the local libedit but uses so we + will compile with -DHAVE_READLINE=1 but will link with + libedit. + } + } + set rlLib [join $rlLib] + set rlInc [join $rlInc] + define LDFLAGS_READLINE $rlLib + define CFLAGS_READLINE $rlInc + proj-assert {$editLibDef in {HAVE_READLINE HAVE_EDITLINE}} + proj-assert {$editLibName in {readline editline}} + sqlite-add-shell-opt -D${editLibDef}=1 + msg-result "Using $editLibName flags: $rlInc $rlLib" + # Check whether rl_completion_matches() has a signature we can use + # and disable that sub-feature if it doesn't. + if {![cctest \ + -cflags "$rlInc -D${editLibDef}" -libs $rlLib -nooutput 1 -source { + #include + #ifdef HAVE_EDITLINE + #include + #else + #include + #endif + static char * rcg(const char *z, int i){(void)z; (void)i; return 0;} + int main(void) { + char ** x = rl_completion_matches("one", rcg); + (void)x; + return 0; + } + }]} { + proj-warn "readline-style completion disabled due to rl_completion_matches() signature mismatch" + sqlite-add-shell-opt -DSQLITE_OMIT_READLINE_COMPLETION + } + return $editLibName + } + + if {$failIfNotFound} { + proj-fatal "Explicit --$editLibName failed to find a matching library." + } + return "none" +}; # sqlite-check-line-editing + +######################################################################## +# Runs sqlite-check-line-editing and adds a message around it In the +# canonical build this must not be called before +# sqlite-determine-codegen-tcl. +proc sqlite-handle-line-editing {} { + msg-result "Line-editing support for the sqlite3 shell: [sqlite-check-line-editing]" +} + + +######################################################################## +# ICU - International Components for Unicode +# +# Handles these flags: +# +# --with-icu-ldflags=LDFLAGS +# --with-icu-cflags=CFLAGS +# --with-icu-config[=auto | pkg-config | /path/to/icu-config] +# --enable-icu-collations +# +# --with-icu-config values: +# +# - auto: use the first one of (pkg-config, icu-config) found on the +# system. +# - pkg-config: use only pkg-config to determine flags +# - /path/to/icu-config: use that to determine flags +# +# If --with-icu-config is used as neither pkg-config nor icu-config +# are found, fail fatally. +# +# If both --with-icu-ldflags and --with-icu-config are provided, they +# are cumulative. If neither are provided, icu-collations is not +# honored and a warning is emitted if it is provided. +# +# Design note: though we could automatically enable ICU if the +# icu-config binary or (pkg-config icu-io) are found, we specifically +# do not. ICU is always an opt-in feature. +proc sqlite-handle-icu {} { + define LDFLAGS_ICU [join [opt-val with-icu-ldflags ""]] + define CFLAGS_ICU [join [opt-val with-icu-cflags ""]] + if {[proj-opt-was-provided with-icu-config]} { + set icuConfigBin [opt-val with-icu-config] + set tryIcuConfigBin 1; # set to 0 if we end up using pkg-config + if {"auto" eq $icuConfigBin || "pkg-config" eq $icuConfigBin} { + if {[pkg-config-init 0] && [pkg-config icu-io]} { + # Maintenance reminder: historical docs say to use both of + # (icu-io, icu-uc). icu-uc lacks a required lib and icu-io has + # all of them on tested OSes. + set tryIcuConfigBin 0 + define LDFLAGS_ICU [get-define PKG_ICU_IO_LDFLAGS] + define-append LDFLAGS_ICU [get-define PKG_ICU_IO_LIBS] + define CFLAGS_ICU [get-define PKG_ICU_IO_CFLAGS] + } elseif {"pkg-config" eq $icuConfigBin} { + proj-fatal "pkg-config cannot find package icu-io" + } else { + proj-assert {"auto" eq $icuConfigBin} + } + } + if {$tryIcuConfigBin} { + if {"auto" eq $icuConfigBin} { + set icuConfigBin [proj-first-bin-of \ + /usr/local/bin/icu-config \ + /usr/bin/icu-config] + if {"" eq $icuConfigBin} { + proj-fatal "--with-icu-config=auto cannot find (pkg-config icu-io) or icu-config binary" + } + } + if {[file-isexec $icuConfigBin]} { + set x [exec $icuConfigBin --ldflags] + if {"" eq $x} { + proj-fatal "$icuConfigBin --ldflags returned no data" + } + define-append LDFLAGS_ICU $x + set x [exec $icuConfigBin --cppflags] + define-append CFLAGS_ICU $x + } else { + proj-fatal "--with-icu-config=$bin does not refer to an executable" + } + } + } + set ldflags [define LDFLAGS_ICU [string trim [get-define LDFLAGS_ICU]]] + set cflags [define CFLAGS_ICU [string trim [get-define CFLAGS_ICU]]] + if {"" ne $ldflags} { + sqlite-add-feature-flag -shell -DSQLITE_ENABLE_ICU + msg-result "Enabling ICU support with flags: $ldflags $cflags" + if {[opt-bool icu-collations]} { + msg-result "Enabling ICU collations." + sqlite-add-feature-flag -shell -DSQLITE_ENABLE_ICU_COLLATIONS + # Recall that shell.c builds with sqlite3.c + } + } elseif {[opt-bool icu-collations]} { + proj-warn "ignoring --enable-icu-collations because neither --with-icu-ldflags nor --with-icu-config provided any linker flags" + } else { + msg-result "ICU support is disabled." + } +}; # sqlite-handle-icu + + +######################################################################## +# Handles the --enable-load-extension flag. Returns 1 if the support +# is enabled, else 0. If support for that feature is not found, a +# fatal error is triggered if --enable-load-extension is explicitly +# provided, else a loud warning is instead emited. If +# --disable-load-extension is used, no check is performed. +# +# Makes the following environment changes: +# +# - defines LDFLAGS_DLOPEN to any linker flags needed for this +# feature. It may legally be empty on some systems where dlopen() +# is in libc. +# +# - If the feature is not available, adds +# -DSQLITE_OMIT_LOAD_EXTENSION=1 to the feature flags list. +proc sqlite-handle-load-extension {} { + define LDFLAGS_DLOPEN "" + set found 0 + proj-if-opt-truthy load-extension { + set found [proj-check-function-in-lib dlopen dl] + if {$found} { + define LDFLAGS_DLOPEN [get-define lib_dlopen] + undefine lib_dlopen + } else { + if {[proj-opt-was-provided load-extension]} { + # Explicit --enable-load-extension: fail if not found + proj-indented-notice -error { + --enable-load-extension was provided but dlopen() + not found. Use --disable-load-extension to bypass this + check. + } + } else { + # It was implicitly enabled: warn if not found + proj-indented-notice { + WARNING: dlopen() not found, so loadable module support will + be disabled. Use --disable-load-extension to bypass this + check. + } + } + } + } + if {$found} { + msg-result "Loadable extension support enabled." + } else { + msg-result "Disabling loadable extension support. Use --enable-load-extensions to enable them." + sqlite-add-feature-flag {-DSQLITE_OMIT_LOAD_EXTENSION=1} + } + return $found +} + +######################################################################## +# Handles the --enable-math flag. +proc sqlite-handle-math {} { + proj-if-opt-truthy math { + if {![proj-check-function-in-lib ceil m]} { + user-error "Cannot find libm functions. Use --disable-math to bypass this." + } + define LDFLAGS_MATH [get-define lib_ceil] + undefine lib_ceil + sqlite-add-feature-flag {-DSQLITE_ENABLE_MATH_FUNCTIONS} + msg-result "Enabling math SQL functions [get-define LDFLAGS_MATH]" + } { + define LDFLAGS_MATH "" + msg-result "Disabling math SQL functions" + } +} + +######################################################################## +# If this OS looks like a Mac, checks for the Mac-specific +# -current_version and -compatibility_version linker flags. Defines +# LDFLAGS_MAC_CVERSION to an empty string and returns 0 if they're not +# supported, else defines that to the linker flags and returns 1. +# +# We don't check this on non-Macs because this whole thing is a +# libtool compatibility kludge to account for a version stamp which +# libtool applied only on Mac platforms. +# +# Based on https://sqlite.org/forum/forumpost/9dfd5b8fd525a5d7. +proc sqlite-check-mac-cversion {} { + define LDFLAGS_MAC_CVERSION "" + set rc 0 + if {[proj-looks-like-mac]} { + cc-with {} { + # These version numbers are historical libtool-defined values, not + # library-defined ones + if {[cc-check-flags "-Wl,-current_version,9.6.0"] + && [cc-check-flags "-Wl,-compatibility_version,9.0.0"]} { + define LDFLAGS_MAC_CVERSION "-Wl,-compatibility_version,9.0.0 -Wl,-current_version,9.6.0" + set rc 1 + } elseif {[cc-check-flags "-compatibility_version 9.0.0"] + && [cc-check-flags "-current_version 9.6.0"]} { + define LDFLAGS_MAC_CVERSION "-compatibility_version 9.0.0 -current_version 9.6.0" + set rc 1 + } + } + } + return $rc +} + +######################################################################## +# Define LDFLAGS_OUT_IMPLIB to either an empty string or to a +# -Wl,... flag for the platform-specific --out-implib flag, which is +# used for building an "import library .dll.a" file on some platforms +# (e.g. mingw). Returns 1 if supported, else 0. +# +# If the configure flag --out-implib is not used then this is a no-op. +# The feature is specifically opt-in because on some platforms the +# feature test will pass but using that flag will fail at link-time +# (e.g. OpenBSD). +# +# Added in response to: https://sqlite.org/forum/forumpost/0c7fc097b2 +proc sqlite-check-out-implib {} { + define LDFLAGS_OUT_IMPLIB "" + set rc 0 + if {[proj-opt-was-provided out-implib]} { + cc-with {} { + set dll "libsqlite3[get-define TARGET_DLLEXT]" + set flags "-Wl,--out-implib,${dll}.a" + if {[cc-check-flags $flags]} { + define LDFLAGS_OUT_IMPLIB $flags + set rc 1 + } + } + } + return $rc +} + +######################################################################## +# Performs late-stage config steps common to both the canonical and +# autoconf bundle builds. +proc sqlite-common-late-stage-config {} { + sqlite-check-mac-cversion + sqlite-check-out-implib + sqlite-process-dot-in-files + sqlite-post-config-validation +} + +######################################################################## +# Perform some late-stage work and generate the configure-process +# output file(s). +proc sqlite-process-dot-in-files {} { + ######################################################################## + # When cross-compiling, we have to avoid using the -s flag to + # /usr/bin/install: + # https://sqlite.org/forum/forumpost/9a67df63eda9925c + define IS_CROSS_COMPILING $::sqliteConfig(is-cross-compiling) + + # Finish up handling of the various feature flags here because it's + # convenient for both the canonical build and autoconf bundles that + # it be done here. + sqlite-handle-common-feature-flags + sqlite-finalize-feature-flags + + ######################################################################## + # "Re-export" the autoconf-conventional --XYZdir flags into something + # which is more easily overridable from a make invocation. See the docs + # for [proj-remap-autoconf-dir-vars] for the explanation of why. + # + # We do this late in the config process, immediately before we export + # the Makefile and other generated files, so that configure tests + # which make make use of the autotools-conventional flags + # (e.g. [proj-check-rpath]) may do so before we "mangle" them here. + proj-remap-autoconf-dir-vars + + proj-make-from-dot-in -touch Makefile sqlite3.pc + make-config-header sqlite_cfg.h \ + -bare {SIZEOF_* HAVE_DECL_*} \ + -none {HAVE_CFLAG_* LDFLAGS_* SH_* SQLITE_AUTORECONFIG + TARGET_* USE_GCOV TCL_*} \ + -auto {HAVE_* PACKAGE_*} \ + -none * + proj-touch sqlite_cfg.h ; # help avoid frequent unnecessary @SQLITE_AUTORECONFIG@ +} + +######################################################################## +# Perform some high-level validation on the generated files... +# +# 1) Ensure that no unresolved @VAR@ placeholders are in files which +# use those. +# +# 2) TBD +proc sqlite-post-config-validation {} { + # Check #1: ensure that files which get filtered for @VAR@ do not + # contain any unresolved @VAR@ refs. That may indicate an + # unexported/unused var or a typo. + set srcdir $::autosetup(srcdir) + foreach f [list Makefile sqlite3.pc \ + $srcdir/tool/emcc.sh \ + $srcdir/ext/wasm/config.make] { + if {![file exists $f]} continue + set lnno 1 + foreach line [proj-file-content-list $f] { + if {[regexp {(@[A-Za-z0-9_]+@)} $line match]} { + error "Unresolved reference to $match at line $lnno of $f" + } + incr lnno + } + } +} + +######################################################################## +# Handle --with-wasi-sdk[=DIR] +# +# This must be run relatively early on because it may change the +# toolchain and disable a number of config options. However, in the +# canonical build this must come after [sqlite-check-common-bins]. +proc sqlite-handle-wasi-sdk {} { + set wasiSdkDir [opt-val with-wasi-sdk] ; # ??? [lindex [opt-val with-wasi-sdk] end] + define HAVE_WASI_SDK 0 + if {$wasiSdkDir eq ""} { + return 0 + } elseif {$::sqliteConfig(is-cross-compiling)} { + proj-fatal "Cannot combine --with-wasi-sdk with cross-compilation" + } + msg-result "Checking WASI SDK directory \[$wasiSdkDir]... " + proj-affirm-files-exist -v {*}[prefix "$wasiSdkDir/bin/" {clang wasm-ld ar}] + define HAVE_WASI_SDK 1 + define WASI_SDK_DIR $wasiSdkDir + # Disable numerous options which we know either can't work or are + # not useful in this build... + msg-result "Using wasi-sdk clang. Disabling CLI shell and modifying config flags:" + # Boolean (--enable-/--disable-) flags which must be switched off: + foreach opt { + dynlink-tools + editline + gcov + icu-collations + load-extension + readline + shared + tcl + threadsafe + } { + if {[opt-bool $opt]} { + msg-result " --disable-$opt" + proj-opt-set $opt 0 + } + } + # Non-boolean flags which need to be cleared: + foreach opt { + with-emsdk + with-icu-config + with-icu-ldflags + with-icu-cflags + with-linenoise + with-tcl + } { + if {[proj-opt-was-provided $opt]} { + msg-result " removing --$opt" + proj-opt-set $opt "" + } + } + # Remember that we now have a discrepancy beteween + # $::sqliteConfig(is-cross-compiling) and [proj-is-cross-compiling]. + set ::sqliteConfig(is-cross-compiling) 1 + + # + # Changing --host and --target have no effect here except to + # possibly cause confusion. Autosetup has finished processing them + # by this point. + # + # host_alias=wasm32-wasi + # target=wasm32-wasi + # + # Merely changing CC, LD, and AR to the wasi-sdk's is enough to get + # sqlite3.o building in WASM format. + # + define CC "${wasiSdkDir}/bin/clang" + define LD "${wasiSdkDir}/bin/wasm-ld" + define AR "${wasiSdkDir}/bin/ar" + #define STRIP "${wasiSdkDir}/bin/strip" + return 1 +}; # sqlite-handle-wasi-sdk + +######################################################################## +# TCL... +# +# sqlite-check-tcl performs most of the --with-tcl and --with-tclsh +# handling. Some related bits and pieces are performed before and +# after that function is called. +# +# Important [define]'d vars: +# +# - HAVE_TCL indicates whether we have a tclsh suitable for building +# the TCL SQLite extension and, by extension, the testing +# infrastructure. This must only be 1 for environments where +# tclConfig.sh can be found. +# +# - TCLSH_CMD is the path to the canonical tclsh or "". It never +# refers to jimtcl. +# +# - TCL_CONFIG_SH is the path to tclConfig.sh or "". +# +# - TCLLIBDIR is the dir to which libtclsqlite3 gets installed. +# +# - BTCLSH = the path to the tcl interpreter used for in-tree code +# generation. It may be jimtcl or the canonical tclsh but may not +# be empty - this tree requires TCL to generated numerous +# components. +# +# If --tcl or --with-tcl are provided but no TCL is found, this +# function fails fatally. If they are not explicitly provided then +# failure to find TCL is not fatal but a loud warning will be emitted. +# +proc sqlite-check-tcl {} { + define TCLSH_CMD false ; # Significant is that it exits with non-0 + define HAVE_TCL 0 ; # Will be enabled via --tcl or a successful search + define TCLLIBDIR "" ; # Installation dir for TCL extension lib + define TCL_CONFIG_SH ""; # full path to tclConfig.sh + + # Clear out all vars which would be set by tclConfigToAutoDef.sh, so + # that the late-config validation of @VARS@ works even if + # --disable-tcl is used. + foreach k {TCL_INCLUDE_SPEC TCL_LIB_SPEC TCL_STUB_LIB_SPEC TCL_EXEC_PREFIX TCL_VERSION} { + define $k "" + } + + file delete -force ".tclenv.sh"; # ensure no stale state from previous configures. + if {![opt-bool tcl]} { + proj-indented-notice { + NOTE: TCL is disabled via --disable-tcl. This means that none + of the TCL-based components will be built, including tests + and sqlite3_analyzer. + } + return + } + # TODO: document the steps this is taking. + set srcdir $::autosetup(srcdir) + msg-result "Checking for a suitable tcl... " + proj-assert [proj-opt-truthy tcl] + set use_tcl 1 + set with_tclsh [opt-val with-tclsh] + set with_tcl [opt-val with-tcl] + if {"prefix" eq $with_tcl} { + set with_tcl [get-define prefix] + } + msg-debug "sqlite-check-tcl: use_tcl ${use_tcl}" + msg-debug "sqlite-check-tcl: with_tclsh=${with_tclsh}" + msg-debug "sqlite-check-tcl: with_tcl=$with_tcl" + if {"" eq $with_tclsh && "" eq $with_tcl} { + # If neither --with-tclsh nor --with-tcl are provided, try to find + # a workable tclsh. + set with_tclsh [proj-first-bin-of tclsh9.0 tclsh8.6 tclsh] + msg-debug "sqlite-check-tcl: with_tclsh=${with_tclsh}" + } + + set doConfigLookup 1 ; # set to 0 to test the tclConfig.sh-not-found cases + if {"" ne $with_tclsh} { + # --with-tclsh was provided or found above. Validate it and use it + # to trump any value passed via --with-tcl=DIR. + if {![file-isexec $with_tclsh]} { + proj-fatal "TCL shell $with_tclsh is not executable" + } else { + define TCLSH_CMD $with_tclsh + #msg-result "Using tclsh: $with_tclsh" + } + if {$doConfigLookup && + [catch {exec $with_tclsh $srcdir/tool/find_tclconfig.tcl} result] == 0} { + set with_tcl $result + } + if {"" ne $with_tcl && [file isdir $with_tcl]} { + msg-result "$with_tclsh recommends the tclConfig.sh from $with_tcl" + } else { + proj-warn "$with_tclsh is unable to recommend a tclConfig.sh" + set use_tcl 0 + } + } + set cfg "" + set tclSubdirs {tcl9.0 tcl8.6 lib} + while {$use_tcl} { + if {"" ne $with_tcl} { + # Ensure that we can find tclConfig.sh under ${with_tcl}/... + if {$doConfigLookup} { + if {[file readable "${with_tcl}/tclConfig.sh"]} { + set cfg "${with_tcl}/tclConfig.sh" + } else { + foreach i $tclSubdirs { + if {[file readable "${with_tcl}/$i/tclConfig.sh"]} { + set cfg "${with_tcl}/$i/tclConfig.sh" + break + } + } + } + } + if {"" eq $cfg} { + proj-fatal "No tclConfig.sh found under ${with_tcl}" + } + } else { + # If we have not yet found a tclConfig.sh file, look in + # $libdir which is set automatically by autosetup or by the + # --prefix command-line option. See + # https://sqlite.org/forum/forumpost/e04e693439a22457 + set libdir [get-define libdir] + if {[file readable "${libdir}/tclConfig.sh"]} { + set cfg "${libdir}/tclConfig.sh" + } else { + foreach i $tclSubdirs { + if {[file readable "${libdir}/$i/tclConfig.sh"]} { + set cfg "${libdir}/$i/tclConfig.sh" + break + } + } + } + if {![file readable $cfg]} { + break + } + } + msg-result "Using tclConfig.sh: $cfg" + break + } + define TCL_CONFIG_SH $cfg + # Export a subset of tclConfig.sh to the current TCL-space. If $cfg + # is an empty string, this emits empty-string entries for the + # various options we're interested in. + eval [exec sh "$srcdir/tool/tclConfigShToAutoDef.sh" "$cfg"] + # ---------^^ a Windows/msys workaround, without which it cannot + # exec a .sh file: https://sqlite.org/forum/forumpost/befb352a42a7cd6d + + if {"" eq $with_tclsh && $cfg ne ""} { + # We have tclConfig.sh but no tclsh. Attempt to locate a tclsh + # based on info from tclConfig.sh. + proj-assert {"" ne [get-define TCL_EXEC_PREFIX]} + set with_tclsh [get-define TCL_EXEC_PREFIX]/bin/tclsh[get-define TCL_VERSION] + if {![file-isexec $with_tclsh]} { + set with_tclsh2 [get-define TCL_EXEC_PREFIX]/bin/tclsh + if {![file-isexec $with_tclsh2]} { + proj-warn "Cannot find a usable tclsh (tried: $with_tclsh $with_tclsh2)" + } else { + set with_tclsh $with_tclsh2 + } + } + } + define TCLSH_CMD $with_tclsh + if {$use_tcl} { + # Set up the TCLLIBDIR + # + # 2024-10-28: calculation of TCLLIBDIR is now done via the shell + # in main.mk (search it for T.tcl.env.sh) so that + # static/hand-written makefiles which import main.mk do not have + # to define that before importing main.mk. Even so, we export + # TCLLIBDIR from here, which will cause the canonical makefile to + # use this one rather than to re-calculate it at make-time. + set tcllibdir [get-env TCLLIBDIR ""] + if {"" eq $tcllibdir} { + # Attempt to extract TCLLIBDIR from TCL's $auto_path + if {"" ne $with_tclsh && + [catch {exec echo "puts stdout \$auto_path" | "$with_tclsh"} result] == 0} { + foreach i $result { + if {[file isdir $i]} { + set tcllibdir $i/sqlite3 + break + } + } + } else { + proj-warn "Cannot determine TCLLIBDIR." + # The makefile will fail fatally in this case if a target is + # invoked which requires TCLLIBDIR. + } + } + #if {"" ne $tcllibdir} { msg-result "TCLLIBDIR = ${tcllibdir}"; } + define TCLLIBDIR $tcllibdir + }; # find TCLLIBDIR + + if {[file-isexec $with_tclsh]} { + msg-result "Using tclsh: $with_tclsh" + if {$cfg ne ""} { + define HAVE_TCL 1 + } else { + proj-warn "Found tclsh but no tclConfig.sh." + } + } + show-notices + # If TCL is not found: if it was explicitly requested then fail + # fatally, else just emit a warning. If we can find the APIs needed + # to generate a working JimTCL then that will suffice for build-time + # TCL purposes (see: proc sqlite-determine-codegen-tcl). + if {![get-define HAVE_TCL] && + ([proj-opt-was-provided tcl] || [proj-opt-was-provided with-tcl])} { + proj-fatal "TCL support was requested but no tclConfig.sh could be found." + } + if {"" eq $cfg} { + proj-assert {0 == [get-define HAVE_TCL]} + proj-indented-notice { + WARNING: Cannot find a usable tclConfig.sh file. Use + --with-tcl=DIR to specify a directory where tclConfig.sh can be + found. SQLite does not use TCL internally, but some optional + components require TCL, including tests and sqlite3_analyzer. + } + } +}; # sqlite-check-tcl + +######################################################################## +# sqlite-determine-codegen-tcl checks which TCL to use as a code +# generator. By default, prefer jimsh simply because we have it +# in-tree (it's part of autosetup) unless --with-tclsh=X is used, in +# which case prefer X. +# +# Returns the human-readable name of the TCL it selects. Fails fatally +# if it cannot detect a TCL appropriate for code generation. +# +# Defines: +# +# - BTCLSH = the TCL shell used for code generation. It may set this +# to an unexpanded makefile var name. +# +# - CFLAGS_JIMSH = any flags needed for buildng a BTCLSH-compatible +# jimsh. The defaults may be passed on to configure as +# CFLAGS_JIMSH=... +proc sqlite-determine-codegen-tcl {} { + msg-result "Checking for TCL to use for code generation... " + define CFLAGS_JIMSH [proj-get-env CFLAGS_JIMSH {-O1}] + set cgtcl [opt-val with-tclsh jimsh] + if {"jimsh" ne $cgtcl} { + # When --with-tclsh=X is used, use that for all TCL purposes, + # including in-tree code generation, per developer request. + define BTCLSH "\$(TCLSH_CMD)" + return $cgtcl + } + set flagsToRestore {CC CFLAGS AS_CFLAGS CPPFLAGS AS_CPPFLAGS LDFLAGS LINKFLAGS LIBS CROSS} + define-push $flagsToRestore { + # We have to swap CC to CC_FOR_BUILD for purposes of the various + # [cc-...] tests below. Recall that --with-wasi-sdk may have + # swapped out CC with one which is not appropriate for this block. + # Per consulation with autosetup's creator, doing this properly + # requires us to [define-push] the whole $flagsToRestore list + # (plus a few others which are not relevant in this tree). + # + # These will get set to their previous values at the end of this + # block. + foreach flag $flagsToRestore {define $flag ""} + define CC [get-define CC_FOR_BUILD] + # These headers are technically optional for JimTCL but necessary if + # we want to use it for code generation: + set sysh [cc-check-includes dirent.h sys/time.h] + # jimsh0.c hard-codes #define's for HAVE_DIRENT_H and + # HAVE_SYS_TIME_H on the platforms it supports, so we do not + # need to add -D... flags for those. We check for them here only + # so that we can avoid the situation that we later, at + # make-time, try to compile jimsh but it then fails due to + # missing headers (i.e. fail earlier rather than later). + if {$sysh && [cc-check-functions realpath]} { + define-append CFLAGS_JIMSH -DHAVE_REALPATH + define BTCLSH "\$(JIMSH)" + set ::sqliteConfig(use-jim-for-codegen) 1 + } elseif {$sysh && [cc-check-functions _fullpath]} { + # _fullpath() is a Windows API. It's not entirely clear + # whether we need to add {-DHAVE_SYS_TIME_H -DHAVE_DIRENT_H} + # to CFLAGS_JIMSH in this case. On MinGW32 we definitely do + # not want to because it already hard-codes them. On _MSC_VER + # builds it does not. + define-append CFLAGS_JIMSH -DHAVE__FULLPATH + define BTCLSH "\$(JIMSH)" + set ::sqliteConfig(use-jim-for-codegen) 1 + } elseif {[file-isexec [get-define TCLSH_CMD]]} { + set cgtcl [get-define TCLSH_CMD] + define BTCLSH "\$(TCLSH_CMD)" + } else { + # One last-ditch effort to find TCLSH_CMD: use info from + # tclConfig.sh to try to find a tclsh + if {"" eq [get-define TCLSH_CMD]} { + set tpre [get-define TCL_EXEC_PREFIX] + if {"" ne $tpre} { + set tv [get-define TCL_VERSION] + if {[file-isexec "${tpre}/bin/tclsh${tv}"]} { + define TCLSH_CMD "${tpre}/bin/tclsh${tv}" + } elseif {[file-isexec "${tpre}/bin/tclsh"]} { + define TCLSH_CMD "${tpre}/bin/tclsh" + } + } + } + set cgtcl [get-define TCLSH_CMD] + if {![file-isexec $cgtcl]} { + proj-fatal "Cannot find a tclsh to use for code generation." + } + define BTCLSH "\$(TCLSH_CMD)" + } + }; # CC swap-out + return $cgtcl +}; # sqlite-determine-codegen-tcl + +######################################################################## +# Runs sqlite-check-tcl and sqlite-determine-codegen-tcl. +proc sqlite-handle-tcl {} { + sqlite-check-tcl + msg-result "TCL for code generation: [sqlite-determine-codegen-tcl]" +} + +######################################################################## +# If the --dump-defines configure flag is provided then emit a list of +# all [define] values to config.defines.txt, else do nothing. +proc sqlite-dump-defines {} { + proj-if-opt-truthy dump-defines { + make-config-header $::sqliteConfig(dump-defines-txt) \ + -bare {SQLITE_OS* SQLITE_DEBUG USE_*} \ + -str {BIN_* CC LD AR LDFLAG* OPT_*} \ + -auto {*} + # achtung: ^^^^ whichever SQLITE_OS_foo flag which is set to 0 will + # get _undefined_ here unless it's part of the -bare set. + if {"" ne $::sqliteConfig(dump-defines-json)} { + msg-result "--dump-defines is creating $::sqliteConfig(dump-defines-json)" + ######################################################################## + # Dump config-defines.json... + # Demonstrate (mis?)handling of spaces in JSON-export array values: + # define-append OPT_FOO.list {"-DFOO=bar baz" -DBAR="baz barre"} + define OPT_FEATURE_FLAGS.list [get-define OPT_FEATURE_FLAGS] + define OPT_SHELL.list [get-define OPT_SHELL] + set dumpDefsOpt { + -bare {SIZEOF_* HAVE_DECL_*} + -none {HAVE_CFLAG_* LDFLAGS_* SH_* SQLITE_AUTORECONFIG TARGET_* USE_GCOV TCL_*} + -array {*.list} + -auto {OPT_* PACKAGE_* HAVE_*} + } + if {[opt-bool defines-json-include-lowercase]} { + lappend dumpDefsOpt -none {lib_*} ; # remnants from proj-check-function-in-lib and friends + lappend dumpDefsOpt -auto {[a-z]*} + } + lappend dumpDefsOpt -none * + proj-dump-defs-json $::sqliteConfig(dump-defines-json) {*}$dumpDefsOpt + undefine OPT_FEATURE_FLAGS.list + undefine OPT_SHELL.list + } + } +} diff --git a/doc/compile-for-unix.md b/doc/compile-for-unix.md index 7e3784d695..ce76b97bae 100644 --- a/doc/compile-for-unix.md +++ b/doc/compile-for-unix.md @@ -4,6 +4,8 @@ Here are step-by-step instructions on how to build SQLite from canonical source on any modern machine that isn't Windows. These notes are tested (on 2024-10-11) on Ubuntu and on MacOS, but they are general and should work on most any modern unix platform. +See the companion document ([](./compile-for-windows.md>)) for +guidance on building for Windows. 1. Install a C-compiler. GCC or Clang both work fine. If you are reading this document, you've probably already done that. @@ -12,6 +14,8 @@ are general and should work on most any modern unix platform. we'll do a private install in the $HOME/local directory, but you can make adjustments to install TCL wherever you like. This document assumes you are working with TCL version 9.0. + See also the [](./tcl-extension-testing.md) document that contains + more details on compiling Tcl for use with SQLite.
  1. Get the TCL source archive, perhaps from @@ -45,7 +49,7 @@ are general and should work on most any modern unix platform.
  2. `make sqldiff`
  3. `make sqlite3_rsync` -

    None of the targets above require TCL. TCL is only needed +

    None of the targets above require TCL. TCL is needed for the following targets:

    • `make tclextension-install` diff --git a/doc/compile-for-windows.md b/doc/compile-for-windows.md index b3a48549a6..2e62286339 100644 --- a/doc/compile-for-windows.md +++ b/doc/compile-for-windows.md @@ -1,12 +1,23 @@ # Notes On Compiling SQLite On Windows 11 -Here are step-by-step instructions on how to build SQLite from -canonical source on a new Windows 11 PC, as of 2024-10-09: +Below are step-by-step instructions on how to build SQLite from +canonical source on a new Windows 11 PC, as of 2024-10-09. +See [](./compile-for-unix.md) for a similar guide for unix-like +systems, including MacOS. 1. Install Microsoft Visual Studio. The free "community edition" will work fine. Do a standard install for C++ development. SQLite only needs the "cl" compiler and the "nmake" build tool. +
      • Note: + VS2015 or later is required for the procedures below to + all work. You *might* be able to get the build to work with + earlier versions of MSVC, but in that case the TCL installation + of step 3 will be required, since the "jimsh0.c" program of + Autosetup that is used as a substitute for "tclsh.exe" won't + compile with versions of Visual Studio prior to VS2015. In any + event, building SQLite from canonical source code on Windows + is not supported for earlier versions of Visual Studio.
      2. Under the "Start" menu, find "All Apps" then go to "Visual Studio 20XX" and find "x64 Native Tools Command Prompt for VS 20XX". Pin that @@ -24,16 +35,21 @@ canonical source on a new Windows 11 PC, as of 2024-10-09: "tclsh90.exe" command-line tool as part of the build process, and the "tcl90.lib" and "tclstub.lib" libraries in order to run tests. This document assumes you are working with TCL version 9.0. - See versions of this document from prior to 2024-10-10 for - instructions on how to build using TCL version 8.6. + See [](./tcl-extension-testing.md#windows) for guidance on how + to compile TCL version 8.6 for use with SQLite.
      1. Get the TCL source archive, perhaps from or .
      2. Untar or unzip the source archive. CD into the "win/" subfolder of the source tree. -
      3. Run: `nmake /f makefile.vc release` -
      4. Run: `nmake /f makefile.vc INSTALLDIR=c:\Tcl install` +
      5. Run: `nmake /f makefile.vc INSTALLDIR=c:\Tcl release` +
      6. Run: `nmake /f makefile.vc INSTALLDIR=c:\Tcl install`
        + Notes: +
          +
        1. The previous two `nmake` commands must be run separately. +
        2. Also, the INSTALLDIR=... argument is required on both. +
      7. Optional: CD to `c:\Tcl\bin` and make a copy of `tclsh90.exe` over into just `tclsh.exe`.
      8. Optional: diff --git a/doc/tcl-extension-testing.md b/doc/tcl-extension-testing.md new file mode 100644 index 0000000000..df5f6537ba --- /dev/null +++ b/doc/tcl-extension-testing.md @@ -0,0 +1,208 @@ +# Test Procedures For The SQLite TCL Extension + +## 1.0 Background + +The SQLite TCL extension logic (in the +"[tclsqlite.c](/file/src/tclsqlite.c)" source +file) is statically linked into "textfixture" executable +which is the program used to do most of the testing +associated with "make test", "make devtest", and/or +"make releasetest". So the functionality of the SQLite +TCL extension is thoroughly vetted during normal testing. The +procedures below are designed to test the loadable extension +aspect of the SQLite TCL extension, and in particular to verify +that the "make tclextension-install" build target works and that +an ordinary tclsh can subsequently run "package require sqlite3". + +This procedure can also be used as a template for how to set up +a local TCL+SQLite development environment. In other words, it +can be be used as a guide on how to compile per-user copies of +Tcl that are used to develop, test, and debug SQLite. In that +case, perhaps make minor changes to the procedure such as: + + * Make TCLBUILD directory is permanent. + * Enable debugging symbols on the Tcl library build. + * Reduce the optimization level to -O0 for easier debugging. + * Also compile "wish" to go with each "tclsh". + + + +## 2.0 Testing On Unix-like Systems (Including Mac) + +See also the [](./compile-for-unix.md) document which provides another +perspective on how to compile SQLite on unix-like systems. + +### 2.1 Setup + +
          +
        1. + [Fossil](https://fossil-scm.org/) installed. +
        2. Check out source code and set environment variables: +
            +
          1. **TCLSOURCE** → + The top-level directory of a Fossil check-out of the TCL source tree. +
          2. **SQLITESOURCE** → + A Fossil check-out of the SQLite source tree. +
          3. **TCLBUILD** → + A directory that does not exist at the start of the test and which + will be deleted at the end of the test, and that will contain the + test builds of the TCL libraries and the SQLite TCL Extensions. +
          +
        + +### 2.2 Testing TCL 8.6 on unix + +
          +
        1. `mkdir -p $TCLBUILD/tcl86` +
        2. `cd $TCLSOURCE/unix` +
        3. `fossil up core-8-6-16`
          + ↑ Or some other version of Tcl8.6. +
        4. `fossil clean -x` +
        5. `./configure --prefix=$TCLBUILD/tcl86 --disable-shared`
          + ↑ The --disable-shared is to avoid the need to set LD_LIBRARY_PATH + when using this Tcl build. +
        6. `make install` +
        7. `cd $SQLITESOURCE` +
        8. `fossil clean -x` +
        9. `./configure --with-tclsh=$TCLBUILD/tcl86/bin/tclsh8.6 --all` +
        10. `make tclextension-install`
          + ↑ Verify extension installed at $TCLBUILD/tcl86/lib/tcl8.6/sqlite3.* +
        11. `make tclextension-list`
          + ↑ Verify TCL extension correctly installed. +
        12. `make tclextension-verify`
          + ↑ Verify that the correct version is installed. +
        13. `$TCLBUILD/tcl86/bin/tclsh8.6 test/testrunner.tcl release --explain`
          + ↑ Verify thousands of lines of output with no errors. Or + consider running "devtest" without --explain instead of "release". +
        + +### 2.3 Testing TCL 9.0 on unix + +
          +
        1. `mkdir -p $TCLBUILD/tcl90` +
        2. `fossil up core-9-0-0`
          + ↑ Or some other version of Tcl9 +
        3. `fossil clean -x` +
        4. `./configure --prefix=$TCLBUILD/tcl90 --disable-shared`
          + ↑ The --disable-shared is to avoid the need to set LD_LIBRARY_PATH + when using this Tcl build. +
        5. `make install` +
        6. `cp -r ../library $TCLBUILD/tcl90/lib/tcl9.0`
          + ↑ The Tcl library is not installed by "make install" for Tcl9.0 unless + you also include the --disable-zipfs to ./configure. But if you do that + then the generated tclsh9.0 is no longer stand-alone. On the other hand, + if you don't install the Tcl library, other programs like testfixture + won't be able to find the Tcl library and hence won't work. This + extra installation step resolves the dilemma. + This step is not required when building Tcl8.6, which lacks support for + zipfs and hence always installs its Tcl library. +
        7. `cd $SQLITESOURCE` +
        8. `fossil clean -x` +
        9. `./configure --with-tclsh=$TCLBUILD/tcl90/bin/tclsh9.0 --all` +
        10. `make tclextension-install`
          + ↑ Verify extension installed at $TCLBUILD/tcl90/lib/sqlite3.* +
        11. `make tclextension-list`
          + ↑ Verify TCL extension correctly installed. +
        12. `make tclextension-verify` +
        13. `$TCLBUILD/tcl90/bin/tclsh9.0 test/testrunner.tcl release --explain`
          + ↑ Verify thousands of lines of output with no errors. Or + consider running "devtest" without --explain instead of "release". +
        + +### 2.4 Cleanup + +
          +
        1. `rm -rf $TCLBUILD` +
        + + +## 3.0 Testing On Windows + +See also the [](./compile-for-windows.md) document which provides another +perspective on how to compile SQLite on Windows. + +### 3.1 Setup for Windows + +
          +
        1. + [Fossil](https://fossil-scm.org/) installed. +
        2. + Unix-like command-line tools installed. Example: + [unxutils](https://unxutils.sourceforge.net/) +
        3. [Visual Studio](https://visualstudio.microsoft.com/vs/community/) + installed. VS2015 or later required. +
        4. Check out source code and set environment variables. +
            +
          1. **TCLSOURCE** → + The top-level directory of a Fossil check-out of the TCL source tree. +
          2. **SQLITESOURCE** → + A Fossil check-out of the SQLite source tree. +
          3. **TCLBUILD** → + A directory that does not exist at the start of the test and which + will be deleted at the end of the test, and that will contain the + test builds of the TCL libraries and the SQLite TCL Extensions. +
          4. **ORIGINALPATH** → + The original value of %PATH%. In other words, set as follows: + `set ORIGINALPATH %PATH%` +
          +
        + +### 3.2 Testing TCL 8.6 on Windows + +
          +
        1. `mkdir %TCLBUILD%\tcl86` +
        2. `cd %TCLSOURCE%\win` +
        3. `fossil up core-8-6-16`
          + ↑ Or some other version of Tcl8.6. +
        4. `fossil clean -x` +
        5. `set INSTALLDIR=%TCLBUILD%\tcl86` +
        6. `nmake /f makefile.vc release`
          + ⇅ You *must* invoke the "release" and "install" targets + using separate "nmake" commands or tclsh86t.exe won't be + installed. +
        7. `nmake /f makefile.vc install` +
        8. `cd %SQLITESOURCE%` +
        9. `fossil clean -x` +
        10. `set TCLDIR=%TCLBUILD%\tcl86` +
        11. `set PATH=%TCLBUILD%\tcl86\bin;%ORIGINALPATH%` +
        12. `set TCLSH_CMD=%TCLBUILD%\tcl86\bin\tclsh86t.exe` +
        13. `nmake /f Makefile.msc tclextension-install`
          + ↑ Verify extension installed at %TCLBUILD%\\tcl86\\lib\\tcl8.6\\sqlite3.* +
        14. `nmake /f Makefile.msc tclextension-verify` +
        15. `tclsh86t test/testrunner.tcl release --explain`
          + ↑ Verify thousands of lines of output with no errors. Or + consider running "devtest" without --explain instead of "release". +
        + +### 3.3 Testing TCL 9.0 on Windows + +
          +
        1. `mkdir %TCLBUILD%\tcl90` +
        2. `cd %TCLSOURCE%\win` +
        3. `fossil up core-9-0-0`
          + ↑ Or some other version of Tcl9 +
        4. `fossil clean -x` +
        5. `set INSTALLDIR=%TCLBUILD%\tcl90` +
        6. `nmake /f makefile.vc release`
          + ⇅ You *must* invoke the "release" and "install" targets + using separate "nmake" commands or tclsh90.exe won't be + installed. +
        7. `nmake /f makefile.vc install` +
        8. `cd %SQLITESOURCE%` +
        9. `fossil clean -x` +
        10. `set TCLDIR=%TCLBUILD%\tcl90` +
        11. `set PATH=%TCLBUILD%\tcl90\bin;%ORIGINALPATH%` +
        12. `set TCLSH_CMD=%TCLBUILD%\tcl90\bin\tclsh90.exe` +
        13. `nmake /f Makefile.msc tclextension-install`
          + ↑ Verify extension installed at %TCLBUILD%\\tcl90\\lib\\sqlite3.* +
        14. `nmake /f Makefile.msc tclextension-verify` +
        15. `tclsh90 test/testrunner.tcl release --explain`
          + ↑ Verify thousands of lines of output with no errors. Or + consider running "devtest" without --explain instead of "release". +
        + +### 3.4 Cleanup + +
          +
        1. `rm -rf %TCLBUILD%` +
        diff --git a/ext/fts3/fts3.c b/ext/fts3/fts3.c index e58f256a48..2b2c3b8d26 100644 --- a/ext/fts3/fts3.c +++ b/ext/fts3/fts3.c @@ -5787,6 +5787,24 @@ static void fts3EvalRestart( } } +/* +** Expression node pExpr is an MSR phrase. This function restarts pExpr +** so that it is a regular phrase query, not an MSR. SQLITE_OK is returned +** if successful, or an SQLite error code otherwise. +*/ +int sqlite3Fts3MsrCancel(Fts3Cursor *pCsr, Fts3Expr *pExpr){ + int rc = SQLITE_OK; + if( pExpr->bEof==0 ){ + i64 iDocid = pExpr->iDocid; + fts3EvalRestart(pCsr, pExpr, &rc); + while( rc==SQLITE_OK && pExpr->iDocid!=iDocid ){ + fts3EvalNextRow(pCsr, pExpr, &rc); + if( pExpr->bEof ) rc = FTS_CORRUPT_VTAB; + } + } + return rc; +} + /* ** After allocating the Fts3Expr.aMI[] array for each phrase in the ** expression rooted at pExpr, the cursor iterates through all rows matched diff --git a/ext/fts3/fts3Int.h b/ext/fts3/fts3Int.h index 3b236faf49..77e6737af4 100644 --- a/ext/fts3/fts3Int.h +++ b/ext/fts3/fts3Int.h @@ -640,6 +640,7 @@ int sqlite3Fts3MsrIncrNext( int sqlite3Fts3EvalPhrasePoslist(Fts3Cursor *, Fts3Expr *, int iCol, char **); int sqlite3Fts3MsrOvfl(Fts3Cursor *, Fts3MultiSegReader *, int *); int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr); +int sqlite3Fts3MsrCancel(Fts3Cursor*, Fts3Expr*); /* fts3_tokenize_vtab.c */ int sqlite3Fts3InitTok(sqlite3*, Fts3Hash *, void(*xDestroy)(void*)); diff --git a/ext/fts3/fts3_snippet.c b/ext/fts3/fts3_snippet.c index 80f62eb3bb..8a6ab8ea62 100644 --- a/ext/fts3/fts3_snippet.c +++ b/ext/fts3/fts3_snippet.c @@ -1586,6 +1586,22 @@ static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){ return rc; } +/* +** If expression pExpr is a phrase expression that uses an MSR query, +** restart it as a regular, non-incremental query. Return SQLITE_OK +** if successful, or an SQLite error code otherwise. +*/ +static int fts3ExprRestartIfCb(Fts3Expr *pExpr, int iPhrase, void *ctx){ + TermOffsetCtx *p = (TermOffsetCtx*)ctx; + int rc = SQLITE_OK; + UNUSED_PARAMETER(iPhrase); + if( pExpr->pPhrase && pExpr->pPhrase->bIncr ){ + rc = sqlite3Fts3MsrCancel(p->pCsr, pExpr); + pExpr->pPhrase->bIncr = 0; + } + return rc; +} + /* ** Implementation of offsets() function. */ @@ -1622,6 +1638,12 @@ void sqlite3Fts3Offsets( sCtx.iDocid = pCsr->iPrevId; sCtx.pCsr = pCsr; + /* If a query restart will be required, do it here, rather than later of + ** after pointers to poslist buffers that may be invalidated by a restart + ** have been saved. */ + rc = sqlite3Fts3ExprIterate(pCsr->pExpr, fts3ExprRestartIfCb, (void*)&sCtx); + if( rc!=SQLITE_OK ) goto offsets_out; + /* Loop through the table columns, appending offset information to ** string-buffer res for each column. */ diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index 8730b69509..f56aa82e8c 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -778,11 +778,13 @@ static int fts5LeafFirstTermOff(Fts5Data *pLeaf){ /* ** Close the read-only blob handle, if it is open. */ -void sqlite3Fts5IndexCloseReader(Fts5Index *p){ +static void fts5IndexCloseReader(Fts5Index *p){ if( p->pReader ){ + int rc; sqlite3_blob *pReader = p->pReader; p->pReader = 0; - sqlite3_blob_close(pReader); + rc = sqlite3_blob_close(pReader); + if( p->rc==SQLITE_OK ) p->rc = rc; } } @@ -807,7 +809,7 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){ assert( p->pReader==0 ); p->pReader = pBlob; if( rc!=SQLITE_OK ){ - sqlite3Fts5IndexCloseReader(p); + fts5IndexCloseReader(p); } if( rc==SQLITE_ABORT ) rc = SQLITE_OK; } @@ -891,9 +893,13 @@ static int fts5IndexPrepareStmt( ){ if( p->rc==SQLITE_OK ){ if( zSql ){ - p->rc = sqlite3_prepare_v3(p->pConfig->db, zSql, -1, + int rc = sqlite3_prepare_v3(p->pConfig->db, zSql, -1, SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_NO_VTAB, ppStmt, 0); + /* If this prepare() call fails with SQLITE_ERROR, then one of the + ** %_idx or %_data tables has been removed or modified. Call this + ** corruption. */ + p->rc = (rc==SQLITE_ERROR ? SQLITE_CORRUPT : rc); }else{ p->rc = SQLITE_NOMEM; } @@ -5005,6 +5011,14 @@ static int fts5IndexReturn(Fts5Index *p){ return rc; } +/* +** Close the read-only blob handle, if it is open. +*/ +void sqlite3Fts5IndexCloseReader(Fts5Index *p){ + fts5IndexCloseReader(p); + fts5IndexReturn(p); +} + typedef struct Fts5FlushCtx Fts5FlushCtx; struct Fts5FlushCtx { Fts5Index *pIdx; @@ -5462,8 +5476,11 @@ static void fts5DoSecureDelete( ** This is called as part of flushing a delete to disk in 'secure-delete' ** mode. It edits the segments within the database described by argument ** pStruct to remove the entries for term zTerm, rowid iRowid. +** +** Return SQLITE_OK if successful, or an SQLite error code if an error +** has occurred. Any error code is also stored in the Fts5Index handle. */ -static void fts5FlushSecureDelete( +static int fts5FlushSecureDelete( Fts5Index *p, Fts5Structure *pStruct, const char *zTerm, @@ -5473,6 +5490,24 @@ static void fts5FlushSecureDelete( const int f = FTS5INDEX_QUERY_SKIPHASH; Fts5Iter *pIter = 0; /* Used to find term instance */ + /* If the version number has not been set to SECUREDELETE, do so now. */ + if( p->pConfig->iVersion!=FTS5_CURRENT_VERSION_SECUREDELETE ){ + Fts5Config *pConfig = p->pConfig; + sqlite3_stmt *pStmt = 0; + fts5IndexPrepareStmt(p, &pStmt, sqlite3_mprintf( + "REPLACE INTO %Q.'%q_config' VALUES ('version', %d)", + pConfig->zDb, pConfig->zName, FTS5_CURRENT_VERSION_SECUREDELETE + )); + if( p->rc==SQLITE_OK ){ + int rc; + sqlite3_step(pStmt); + rc = sqlite3_finalize(pStmt); + if( p->rc==SQLITE_OK ) p->rc = rc; + pConfig->iCookie++; + pConfig->iVersion = FTS5_CURRENT_VERSION_SECUREDELETE; + } + } + fts5MultiIterNew(p, pStruct, f, 0, (const u8*)zTerm, nTerm, -1, 0, &pIter); if( fts5MultiIterEof(p, pIter)==0 ){ i64 iThis = fts5MultiIterRowid(pIter); @@ -5490,6 +5525,7 @@ static void fts5FlushSecureDelete( } fts5MultiIterFree(pIter); + return p->rc; } @@ -5573,8 +5609,9 @@ static void fts5FlushOneHash(Fts5Index *p){ ** using fts5FlushSecureDelete(). */ if( bSecureDelete ){ if( eDetail==FTS5_DETAIL_NONE ){ - if( iOffrc!=SQLITE_OK || pDoclist[iOff]==0x01 ){ iOff++; continue; @@ -6651,6 +6689,7 @@ static void fts5SetupPrefixIter( } pData = fts5IdxMalloc(p, sizeof(*pData)+s.doclist.n+FTS5_DATA_ZERO_PADDING); + assert( pData!=0 || p->rc!=SQLITE_OK ); if( pData ){ pData->p = (u8*)&pData[1]; pData->nn = pData->szLeaf = s.doclist.n; @@ -6658,6 +6697,7 @@ static void fts5SetupPrefixIter( fts5MultiIterNew2(p, pData, bDesc, ppIter); } + assert( (*ppIter)!=0 || p->rc!=SQLITE_OK ); if( p->rc==SQLITE_OK && s.pTokendata ){ fts5TokendataIterSortMap(p, s2.pT); (*ppIter)->pTokenDataIter = s2.pT; @@ -6706,7 +6746,7 @@ int sqlite3Fts5IndexBeginWrite(Fts5Index *p, int bDelete, i64 iRowid){ int sqlite3Fts5IndexSync(Fts5Index *p){ assert( p->rc==SQLITE_OK ); fts5IndexFlush(p); - sqlite3Fts5IndexCloseReader(p); + fts5IndexCloseReader(p); return fts5IndexReturn(p); } @@ -6717,11 +6757,10 @@ int sqlite3Fts5IndexSync(Fts5Index *p){ ** records must be invalidated. */ int sqlite3Fts5IndexRollback(Fts5Index *p){ - sqlite3Fts5IndexCloseReader(p); + fts5IndexCloseReader(p); fts5IndexDiscardData(p); fts5StructureInvalidate(p); - /* assert( p->rc==SQLITE_OK ); */ - return SQLITE_OK; + return fts5IndexReturn(p); } /* @@ -6922,6 +6961,16 @@ static void fts5SegIterSetEOF(Fts5SegIter *pSeg){ pSeg->pLeaf = 0; } +static void fts5IterClose(Fts5IndexIter *pIndexIter){ + if( pIndexIter ){ + Fts5Iter *pIter = (Fts5Iter*)pIndexIter; + Fts5Index *pIndex = pIter->pIndex; + fts5TokendataIterDelete(pIter->pTokenDataIter); + fts5MultiIterFree(pIter); + fts5IndexCloseReader(pIndex); + } +} + /* ** This function appends iterator pAppend to Fts5TokenDataIter pIn and ** returns the result. @@ -6949,7 +6998,7 @@ static Fts5TokenDataIter *fts5AppendTokendataIter( } } if( p->rc ){ - sqlite3Fts5IterClose((Fts5IndexIter*)pAppend); + fts5IterClose((Fts5IndexIter*)pAppend); }else{ pRet->apIter[pRet->nIter++] = pAppend; } @@ -7162,7 +7211,7 @@ static Fts5Iter *fts5SetupTokendataIter( fts5BufferSet(&p->rc, &bSeek, nToken, pToken); } if( p->rc ){ - sqlite3Fts5IterClose((Fts5IndexIter*)pNew); + fts5IterClose((Fts5IndexIter*)pNew); break; } @@ -7227,7 +7276,7 @@ static Fts5Iter *fts5SetupTokendataIter( ** not point to any terms that match the query. So delete it and break ** out of the loop - all required iterators have been collected. */ if( pSmall==0 ){ - sqlite3Fts5IterClose((Fts5IndexIter*)pNew); + fts5IterClose((Fts5IndexIter*)pNew); break; } @@ -7289,6 +7338,7 @@ int sqlite3Fts5IndexQuery( int iIdx = 0; /* Index to search */ int iPrefixIdx = 0; /* +1 prefix index */ int bTokendata = pConfig->bTokendata; + assert( buf.p!=0 ); if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken); /* The NOTOKENDATA flag is set when each token in a tokendata=1 table @@ -7355,9 +7405,9 @@ int sqlite3Fts5IndexQuery( } if( p->rc ){ - sqlite3Fts5IterClose((Fts5IndexIter*)pRet); + fts5IterClose((Fts5IndexIter*)pRet); pRet = 0; - sqlite3Fts5IndexCloseReader(p); + fts5IndexCloseReader(p); } *ppIter = (Fts5IndexIter*)pRet; @@ -7450,6 +7500,7 @@ static int fts5SetupPrefixIterTokendata( memset(&ctx, 0, sizeof(ctx)); fts5BufferGrow(&p->rc, &token, nToken+1); + assert( token.p!=0 || p->rc!=SQLITE_OK ); ctx.pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*ctx.pT)); if( p->rc==SQLITE_OK ){ @@ -7606,11 +7657,9 @@ int sqlite3Fts5IndexIterWriteTokendata( */ void sqlite3Fts5IterClose(Fts5IndexIter *pIndexIter){ if( pIndexIter ){ - Fts5Iter *pIter = (Fts5Iter*)pIndexIter; - Fts5Index *pIndex = pIter->pIndex; - fts5TokendataIterDelete(pIter->pTokenDataIter); - fts5MultiIterFree(pIter); - sqlite3Fts5IndexCloseReader(pIndex); + Fts5Index *pIndex = ((Fts5Iter*)pIndexIter)->pIndex; + fts5IterClose(pIndexIter); + fts5IndexReturn(pIndex); } } @@ -8140,7 +8189,7 @@ static int fts5QueryCksum( rc = sqlite3Fts5IterNext(pIter); } } - sqlite3Fts5IterClose(pIter); + fts5IterClose(pIter); *pCksum = cksum; return rc; diff --git a/ext/fts5/fts5_main.c b/ext/fts5/fts5_main.c index 876420f24d..9f504bb3a3 100644 --- a/ext/fts5/fts5_main.c +++ b/ext/fts5/fts5_main.c @@ -1901,7 +1901,6 @@ static int fts5UpdateMethod( Fts5Config *pConfig = pTab->p.pConfig; int eType0; /* value_type() of apVal[0] */ int rc = SQLITE_OK; /* Return code */ - int bUpdateOrDelete = 0; /* A transaction must be open when this is called. */ assert( pTab->ts.eState==1 || pTab->ts.eState==2 ); @@ -1913,7 +1912,7 @@ static int fts5UpdateMethod( ); assert( pTab->p.pConfig->pzErrmsg==0 ); if( pConfig->pgsz==0 ){ - rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex); + rc = sqlite3Fts5ConfigLoad(pTab->p.pConfig, pTab->p.pConfig->iCookie); if( rc!=SQLITE_OK ) return rc; } @@ -1938,7 +1937,6 @@ static int fts5UpdateMethod( rc = SQLITE_ERROR; }else{ rc = fts5SpecialDelete(pTab, apVal); - bUpdateOrDelete = 1; } }else{ rc = fts5SpecialInsert(pTab, z, apVal[2 + pConfig->nCol + 1]); @@ -1975,7 +1973,6 @@ static int fts5UpdateMethod( }else{ i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0, 0); - bUpdateOrDelete = 1; } } @@ -2003,7 +2000,6 @@ static int fts5UpdateMethod( if( eConflict==SQLITE_REPLACE && eType1==SQLITE_INTEGER ){ i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0, 0); - bUpdateOrDelete = 1; } fts5StorageInsert(&rc, pTab, apVal, pRowid); } @@ -2057,23 +2053,8 @@ static int fts5UpdateMethod( rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 1); fts5StorageInsert(&rc, pTab, apVal, pRowid); } - bUpdateOrDelete = 1; sqlite3Fts5StorageReleaseDeleteRow(pStorage); } - - } - } - - if( rc==SQLITE_OK - && bUpdateOrDelete - && pConfig->bSecureDelete - && pConfig->iVersion==FTS5_CURRENT_VERSION - ){ - rc = sqlite3Fts5StorageConfigValue( - pTab->pStorage, "version", 0, FTS5_CURRENT_VERSION_SECUREDELETE - ); - if( rc==SQLITE_OK ){ - pConfig->iVersion = FTS5_CURRENT_VERSION_SECUREDELETE; } } @@ -2126,6 +2107,7 @@ static int fts5RollbackMethod(sqlite3_vtab *pVtab){ Fts5FullTable *pTab = (Fts5FullTable*)pVtab; fts5CheckTransactionState(pTab, FTS5_ROLLBACK, 0); rc = sqlite3Fts5StorageRollback(pTab->pStorage); + pTab->p.pConfig->pgsz = 0; return rc; } diff --git a/ext/fts5/fts5_storage.c b/ext/fts5/fts5_storage.c index 31f5fc5dc3..2b43016bef 100644 --- a/ext/fts5/fts5_storage.c +++ b/ext/fts5/fts5_storage.c @@ -205,6 +205,11 @@ static int fts5StorageGetStmt( if( rc!=SQLITE_OK && pzErrMsg ){ *pzErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pC->db)); } + if( rc==SQLITE_ERROR && eStmt>FTS5_STMT_LOOKUP2 && eStmt>8) & 0xff; + deliberate_fall_through; /* FALLTHRU */ case 1: pOut[0] = (qv>>16) & 0xff; + break; } pOut += nbo; } diff --git a/ext/misc/base85.c b/ext/misc/base85.c index e7ef0a04c9..eaf1732c46 100644 --- a/ext/misc/base85.c +++ b/ext/misc/base85.c @@ -232,12 +232,16 @@ static u8* fromBase85( char *pIn, int ncIn, u8 *pOut ){ switch( nbo ){ case 4: *pOut++ = (qv >> 24)&0xff; + /* FALLTHRU */ case 3: *pOut++ = (qv >> 16)&0xff; + /* FALLTHRU */ case 2: *pOut++ = (qv >> 8)&0xff; + /* FALLTHRU */ case 1: *pOut++ = qv&0xff; + /* FALLTHRU */ case 0: break; } diff --git a/ext/misc/closure.c b/ext/misc/closure.c index 79a5a21d1e..267ae1c424 100644 --- a/ext/misc/closure.c +++ b/ext/misc/closure.c @@ -588,12 +588,17 @@ static int closureOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ return SQLITE_OK; } +/* +** Wrapper around sqlite3_free +*/ +static void closureMemFree(closure_avl *p){ sqlite3_free(p); } + /* ** Free up all the memory allocated by a cursor. Set it rLimit to 0 ** to indicate that it is at EOF. */ static void closureClearCursor(closure_cursor *pCur){ - closureAvlDestroy(pCur->pClosure, (void(*)(closure_avl*))sqlite3_free); + closureAvlDestroy(pCur->pClosure, closureMemFree); sqlite3_free(pCur->zTableName); sqlite3_free(pCur->zIdColumn); sqlite3_free(pCur->zParentColumn); diff --git a/ext/misc/fileio.c b/ext/misc/fileio.c index 483ef0187f..80ac3b3e7c 100644 --- a/ext/misc/fileio.c +++ b/ext/misc/fileio.c @@ -437,7 +437,7 @@ static int writeFile( GetSystemTime(¤tTime); SystemTimeToFileTime(¤tTime, &lastAccess); - intervals = Int32x32To64(mtime, 10000000) + 116444736000000000; + intervals = (mtime*10000000) + 116444736000000000; lastWrite.dwLowDateTime = (DWORD)intervals; lastWrite.dwHighDateTime = intervals >> 32; zUnicodeName = sqlite3_win32_utf8_to_unicode(zFile); diff --git a/ext/misc/regexp.c b/ext/misc/regexp.c index a50008ca35..71c7ea4c22 100644 --- a/ext/misc/regexp.c +++ b/ext/misc/regexp.c @@ -656,7 +656,8 @@ static const char *re_subcompile_string(ReCompiled *p){ ** regular expression. Applications should invoke this routine once ** for every call to re_compile() to avoid memory leaks. */ -static void re_free(ReCompiled *pRe){ +static void re_free(void *p){ + ReCompiled *pRe = (ReCompiled*)p; if( pRe ){ sqlite3_free(pRe->aOp); sqlite3_free(pRe->aArg); diff --git a/ext/misc/sqlite3_stdio.c b/ext/misc/sqlite3_stdio.c index 97c3551da2..c9bceb1942 100644 --- a/ext/misc/sqlite3_stdio.c +++ b/ext/misc/sqlite3_stdio.c @@ -46,6 +46,11 @@ ** use O_U8TEXT when writing to the Windows console (or anything ** else for which _isatty() returns true) and to use O_BINARY or O_TEXT ** for all other output channels. +** +** The SQLITE_USE_W32_FOR_CONSOLE_IO macro is also available. If +** defined, it forces the use of Win32 APIs for all console I/O, both +** input and output. This is necessary for some non-Microsoft run-times +** that implement stdio differently from Microsoft/Visual-Studio. */ #if defined(SQLITE_U8TEXT_ONLY) # define UseWtextForOutput(fd) 1 @@ -148,10 +153,10 @@ char *sqlite3_fgets(char *buf, int sz, FILE *in){ */ wchar_t *b1 = sqlite3_malloc( sz*sizeof(wchar_t) ); if( b1==0 ) return 0; -#ifndef SQLITE_USE_STDIO_FOR_CONSOLE +#ifdef SQLITE_USE_W32_FOR_CONSOLE_IO DWORD nRead = 0; if( IsConsole(in) - && ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), b1, sz, &nRead, 0) + && ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), b1, sz-1, &nRead, 0) ){ b1[nRead] = 0; }else @@ -226,7 +231,7 @@ int sqlite3_fputs(const char *z, FILE *out){ sz = MultiByteToWideChar(CP_UTF8, 0, z, sz, b1, sz); b1[sz] = 0; -#ifndef SQLITE_STDIO_FOR_CONSOLE +#ifdef SQLITE_USE_W32_FOR_CONSOLE_IO DWORD nWr = 0; if( IsConsole(out) && WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),b1,sz,&nWr,0) @@ -236,8 +241,9 @@ int sqlite3_fputs(const char *z, FILE *out){ }else #endif { - /* For non-console I/O, or if SQLITE_USE_STDIO_FOR_CONSOLE is defined - ** then write using the standard library. */ + /* As long as SQLITE_USE_W32_FOR_CONSOLE_IO is not defined, or for + ** non-console I/O even if that macro is defined, write using the + ** standard library. */ _setmode(_fileno(out), _O_U8TEXT); if( UseBinaryWText(out) ){ piecemealOutput(b1, sz, out); diff --git a/ext/misc/vfstrace.c b/ext/misc/vfstrace.c index e8b51cdd03..c274558d17 100644 --- a/ext/misc/vfstrace.c +++ b/ext/misc/vfstrace.c @@ -188,6 +188,7 @@ struct vfstrace_file { #define VTR_SLEEP 0x02000000 #define VTR_CURTIME 0x04000000 #define VTR_LASTERR 0x08000000 +#define VTR_FETCH 0x10000000 /* Also coverse xUnfetch */ /* ** Method declarations for vfstrace_file. @@ -617,6 +618,7 @@ static int vfstraceFileControl(sqlite3_file *pFile, int op, void *pArg){ { "currenttime", VTR_CURTIME }, { "currenttimeint64", VTR_CURTIME }, { "getlasterror", VTR_LASTERR }, + { "fetch", VTR_FETCH }, }; int onOff = 1; while( zArg[0] ){ @@ -844,7 +846,28 @@ static int vfstraceShmUnmap(sqlite3_file *pFile, int delFlag){ vfstrace_print_errcode(pInfo, " -> %s\n", rc); return rc; } - +static int vfstraceFetch(sqlite3_file *pFile, i64 iOff, int nAmt, void **pptr){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + vfstraceOnOff(pInfo, VTR_FETCH); + vfstrace_printf(pInfo, "%s.xFetch(%s,iOff=%lld,nAmt=%d,p=%p)", + pInfo->zVfsName, p->zFName, iOff, nAmt, *pptr); + rc = p->pReal->pMethods->xFetch(p->pReal, iOff, nAmt, pptr); + vfstrace_print_errcode(pInfo, " -> %s\n", rc); + return rc; +} +static int vfstraceUnfetch(sqlite3_file *pFile, i64 iOff, void *ptr){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + vfstraceOnOff(pInfo, VTR_FETCH); + vfstrace_printf(pInfo, "%s.xUnfetch(%s,iOff=%lld,p=%p)", + pInfo->zVfsName, p->zFName, iOff, ptr); + rc = p->pReal->pMethods->xUnfetch(p->pReal, iOff, ptr); + vfstrace_print_errcode(pInfo, " -> %s\n", rc); + return rc; +} /* @@ -891,6 +914,10 @@ static int vfstraceOpen( pNew->xShmBarrier = pSub->xShmBarrier ? vfstraceShmBarrier : 0; pNew->xShmUnmap = pSub->xShmUnmap ? vfstraceShmUnmap : 0; } + if( pNew->iVersion>=3 ){ + pNew->xFetch = pSub->xFetch ? vfstraceFetch : 0; + pNew->xUnfetch = pSub->xUnfetch ? vfstraceUnfetch : 0; + } pFile->pMethods = pNew; } vfstrace_print_errcode(pInfo, " -> %s", rc); @@ -1006,7 +1033,7 @@ static void vfstraceDlClose(sqlite3_vfs *pVfs, void *pHandle){ vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; sqlite3_vfs *pRoot = pInfo->pRootVfs; vfstraceOnOff(pInfo, VTR_DLCLOSE); - vfstrace_printf(pInfo, "%s.xDlOpen()\n", pInfo->zVfsName); + vfstrace_printf(pInfo, "%s.xDlClose()\n", pInfo->zVfsName); pRoot->xDlClose(pRoot, pHandle); } diff --git a/ext/session/session1.test b/ext/session/session1.test index bcd7b03d5c..6da9051791 100644 --- a/ext/session/session1.test +++ b/ext/session/session1.test @@ -204,7 +204,9 @@ proc do_conflict_test {tn args} { foreach t $O(-tables) { S attach $t } execsql $O(-sql) set ::xConflict [list] - sqlite3changeset_apply db2 [S changeset] xConflict + catch { + sqlite3changeset_apply db2 [S changeset] xConflict + } set conflicts [list] foreach c $O(-conflicts) { diff --git a/ext/session/session9.test b/ext/session/session9.test index ebb88ffade..5c406c344e 100644 --- a/ext/session/session9.test +++ b/ext/session/session9.test @@ -80,8 +80,10 @@ foreach {tn delrow trans conflictargs conflictret} { 8 3 1 {FOREIGN_KEY 1} ABORT } { - set A(OMIT) {0 {}} - set A(ABORT) {1 SQLITE_CONSTRAINT} + set A(OMIT,0) {0 {}} + set A(OMIT,1) {0 {}} + set A(ABORT,0) {1 SQLITE_CONSTRAINT} + set A(ABORT,1) {1 SQLITE_CONSTRAINT} do_test 1.2.$tn.1 { populate_db execsql { DELETE FROM p1 WHERE a=($delrow+0) } @@ -89,20 +91,24 @@ foreach {tn delrow trans conflictargs conflictret} { set ::xConflict [list] list [catch {sqlite3changeset_apply db $::cc xConflict} msg] $msg - } $A($conflictret) + } $A($conflictret,$trans) do_test 1.2.$tn.2 { set ::xConflict } $conflictargs - set A(OMIT) {1 1} - set A(ABORT) {0 0} + set A(OMIT,0) {1 1} + set A(OMIT,1) {1 1} + set A(ABORT,0) {0 0} + set A(ABORT,1) {0 0} + do_test 1.2.$tn.3 { execsql { SELECT count(*) FROM c1 UNION ALL SELECT count(*) FROM c2 } - } $A($conflictret) + } $A($conflictret,$trans) do_test 1.2.$tn.4 { expr ![sqlite3_get_autocommit db] } $trans do_test 1.2.$tn.5 { - if { $trans } { execsql COMMIT } + if { $trans && $conflictret=="ABORT" } { execsql COMMIT } } {} + catchsql ROLLBACK } #-------------------------------------------------------------------- diff --git a/ext/session/session_common.tcl b/ext/session/session_common.tcl index 3ff84f1c5e..7c1273bb1a 100644 --- a/ext/session/session_common.tcl +++ b/ext/session/session_common.tcl @@ -201,12 +201,16 @@ proc compare_db {db1 db2} { foreach tbl $lot1 { set col1 [list] set col2 [list] + set quoted [list] $db1 eval "PRAGMA table_info = $tbl" { lappend col1 $name } - $db2 eval "PRAGMA table_info = $tbl" { lappend col2 $name } + $db2 eval "PRAGMA table_info = $tbl" { + lappend col2 $name + lappend quoted "\"[string map {\" \"\"} $name]\"" + } if {$col1 != $col2} { error "table $tbl schema mismatch" } - set sql "SELECT * FROM $tbl ORDER BY [join $col1 ,]" + set sql "SELECT * FROM $tbl ORDER BY [join $quoted ,]" set data1 [$db1 eval $sql] set data2 [$db2 eval $sql] if {$data1 != $data2} { diff --git a/ext/session/session_gen.test b/ext/session/session_gen.test new file mode 100644 index 0000000000..e9de4beaba --- /dev/null +++ b/ext/session/session_gen.test @@ -0,0 +1,191 @@ +# 2025 Jan 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. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# + +if {![info exists testdir]} { + set testdir [file join [file dirname [info script]] .. .. test] +} +source [file join [file dirname [info script]] session_common.tcl] +source $testdir/tester.tcl +ifcapable !session {finish_test; return} + +set testprefix session_gen + + +foreach {otn sct} { + 1 VIRTUAL + 2 STORED +} { +eval [string map [list %TYPE% $sct] { + reset_db + set testprefix $testprefix-$otn + +do_execsql_test 1.0 { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + + CREATE TABLE t2(a INTEGER PRIMARY KEY, b AS (c+1) %TYPE%, c); + + CREATE TABLE t3( + a, + b AS (a+10) %TYPE%, + c, + d AS (c+1) %TYPE%, + e, + PRIMARY KEY(c, e) + ) WITHOUT ROWID; + + CREATE TABLE t4(a AS (c*100) %TYPE%, b INTEGER PRIMARY KEY, c); + + CREATE TABLE t5(x, y); +} + +foreach {tn sql changeset} { + + 0.1 { + INSERT INTO t5 VALUES('abc', 'def'); + } { + {INSERT t5 0 X.. {} {i 1 t abc t def}} + } + 0.2 { + UPDATE t5 SET y='xyz' WHERE rowid=1; + } { + {UPDATE t5 0 X.. {i 1 {} {} t def} {{} {} {} {} t xyz}} + } + 0.3 { + DELETE FROM t5; + } { + {DELETE t5 0 X.. {i 1 t abc t xyz} {}} + } + + 1.1 { + INSERT INTO t2 VALUES(1, 2); + INSERT INTO t2 VALUES(2, 123); + } { + {INSERT t2 0 X. {} {i 1 i 2}} + {INSERT t2 0 X. {} {i 2 i 123}} + } + 1.2 { + UPDATE t2 SET c=456 WHERE a=1 + } { + {UPDATE t2 0 X. {i 1 i 2} {{} {} i 456}} + } + + 1.3 { + DELETE FROM t2 WHERE a=2 + } { + {DELETE t2 0 X. {i 2 i 123} {}} + } + + 1.4 { + UPDATE t2 SET a=15 + } { + {INSERT t2 0 X. {} {i 15 i 456}} + {DELETE t2 0 X. {i 1 i 456} {}} + } + + 2.1 { + INSERT INTO t3 VALUES(5, 6, 7); + INSERT INTO t3 VALUES(8, 9, 10); + } { + {INSERT t3 0 .XX {} {i 8 i 9 i 10}} + {INSERT t3 0 .XX {} {i 5 i 6 i 7}} + } + + 2.2 { + UPDATE t3 SET a = 505 WHERE (c, e) = (6, 7); + } { + {UPDATE t3 0 .XX {i 5 i 6 i 7} {i 505 {} {} {} {}}} + } + + 2.3 { + DELETE FROM t3 WHERE (c, e) = (9, 10); + } { + {DELETE t3 0 .XX {i 8 i 9 i 10} {}} + } + + 2.4 { + UPDATE t3 SET c=1000 + } { + {DELETE t3 0 .XX {i 505 i 6 i 7} {}} + {INSERT t3 0 .XX {} {i 505 i 1000 i 7}} + } + + 3.1 { + INSERT INTO t4 VALUES(100, 100); + } { + {INSERT t4 0 X. {} {i 100 i 100}} + } + +} { + do_test 1.$tn.1 { + sqlite3session S db main + S object_config rowid 1 + S attach * + execsql $sql + } {} + + do_changeset_test 1.$tn.2 S $changeset + + S delete +} +#------------------------------------------------------------------------- +reset_db + +forcedelete test.db2 +sqlite3 db2 test.db2 + +do_common_sql { + CREATE TABLE t0(x INTEGER PRIMARY KEY, y); + INSERT INTO t0 VALUES(1, 'one'); + INSERT INTO t0 VALUES(2, 'two'); + + CREATE TABLE t1(a AS (c*10) %TYPE%, b INTEGER PRIMARY KEY, c); + INSERT INTO t1 VALUES(1, 5); + INSERT INTO t1 VALUES(2, 10); + INSERT INTO t1 VALUES(3, 5); + + CREATE TABLE t2( + a, b, c AS (a*b) %TYPE%, + 'k 1', 'k 2', PRIMARY KEY('k 1', 'k 2') + ) WITHOUT ROWID; + INSERT INTO t2 VALUES('a', 'b', 1, 11); + INSERT INTO t2 VALUES('A', 'B', 2, 22); + INSERT INTO t2 VALUES('Aa', 'Bb', 3, 33); +} + +foreach {tn sql} { + 1.1 { INSERT INTO t0 VALUES(4, 15) } + 1.2 { INSERT INTO t1 VALUES(4, 15) } + 1.3 { INSERT INTO t2 VALUES(1, 2, 3, 4) } + + 2.1 { UPDATE t1 SET c=100 WHERE b=2 } + 2.2 { UPDATE t2 SET a=11 } + + 3.1 { DELETE FROM t2 WHERE (t2.'k 1') = 2 } + 3.2 { DELETE FROM t1 } +} { + do_test 2.$tn.1 { + # execsql { PRAGMA vdbe_listing = 1 } db2 + do_then_apply_sql $sql + } {} + do_test 2.$tn.2 { + compare_db db db2 + } {} +} +db2 close + +}]} + + + + +finish_test diff --git a/ext/session/sessionnoact.test b/ext/session/sessionnoact.test index aa1cde4749..54e9a62151 100644 --- a/ext/session/sessionnoact.test +++ b/ext/session/sessionnoact.test @@ -59,7 +59,7 @@ do_execsql_test 1.2 { set ::nConflict 0 proc conflict {args} { incr ::nConflict - return "OMIT" + return "ABORT" } sqlite3changeset_apply_v2 db $C conflict @@ -82,7 +82,6 @@ do_execsql_test 1.5 { UPDATE p1 SET c=12345 WHERE a = 45; } -breakpoint sqlite3changeset_apply_v2 -noaction db $C conflict do_execsql_test 1.6 { SELECT * FROM c1 @@ -108,4 +107,126 @@ do_execsql_test 1.8 { PRAGMA foreign_key_check } +#------------------------------------------------------------------------- +# Check that a changeset that causes an FK violation may not be applied, +# even if SQLITE_CHANGESETAPPLY_FKNOACTION is specified. +# +# UPDATE: Unless the conflict-handler returns OMIT. In that case it can +# be committed. See test cases 3.* in this file. +# +reset_db +do_execsql_test 2.0 { + CREATE TABLE p1(a INTEGER PRIMARY KEY, b, c UNIQUE); + INSERT INTO p1 VALUES(1, 1, 'one'); + INSERT INTO p1 VALUES(2, 2, 'two'); + + CREATE TABLE c1(x REFERENCES p1(c) ON DELETE CASCADE); + INSERT INTO c1 VALUES('two'); +} + +db_save + +set C [changeset_from_sql { + DELETE FROM p1 WHERE a=2; +}] + +db_restore_and_reopen + +do_test 2.1 { + sqlite3changeset_apply_v2 -noaction db $C conflict +} {} +do_execsql_test 2.2 { + SELECT * FROM p1 +} {1 1 one} + +db_restore_and_reopen +db eval { PRAGMA foreign_keys = 1 } + +do_test 2.3 { + list [catch { sqlite3changeset_apply_v2 -noaction db $C conflict } msg] $msg +} {1 SQLITE_CONSTRAINT} +do_execsql_test 2.4 { + SELECT * FROM p1; +} {1 1 one 2 2 two} +do_execsql_test 2.5 { + SELECT * FROM c1; +} {two} + +db_restore_and_reopen +db eval { PRAGMA foreign_keys = 1 } + +do_test 2.6 { + list [catch { + sqlite3changeset_apply_v2 -ignorenoop -noaction db $C conflict + } msg] $msg +} {1 SQLITE_CONSTRAINT} +do_execsql_test 2.7 { + SELECT * FROM p1; +} {1 1 one 2 2 two} +do_execsql_test 2.8 { + SELECT * FROM c1; +} {two} + +#------------------------------------------------------------------------- +# +reset_db +do_execsql_test 3.0 { + CREATE TABLE p1(a INTEGER PRIMARY KEY, b, c UNIQUE); + INSERT INTO p1 VALUES(1, 1, 'one'); + INSERT INTO p1 VALUES(2, 2, 'two'); + + CREATE TABLE c1(x REFERENCES p1(c) ON DELETE CASCADE); + INSERT INTO c1 VALUES('two'); +} + +set ::nConflict 0 +proc conflict {args} { + incr ::nConflict + return "OMIT" +} + +db_save + +set C [changeset_from_sql { + DELETE FROM p1 WHERE a=2; +}] + +db_restore_and_reopen + +do_test 3.1 { + sqlite3changeset_apply_v2 -noaction db $C conflict +} {} +do_execsql_test 3.2 { + SELECT * FROM p1 +} {1 1 one} + +db_restore_and_reopen +db eval { PRAGMA foreign_keys = 1 } + +do_test 3.3 { + list [catch { sqlite3changeset_apply_v2 -noaction db $C conflict } msg] $msg +} {0 {}} +do_execsql_test 3.4 { + SELECT * FROM p1; +} {1 1 one} +do_execsql_test 3.5 { + SELECT * FROM c1; +} {two} + +db_restore_and_reopen +db eval { PRAGMA foreign_keys = 1 } + +do_test 3.6 { + list [catch { + sqlite3changeset_apply_v2 -ignorenoop -noaction db $C conflict + } msg] $msg +} {0 {}} +do_execsql_test 3.7 { + SELECT * FROM p1; +} {1 1 one} +do_execsql_test 3.8 { + SELECT * FROM c1; +} {two} + finish_test + diff --git a/ext/session/sqlite3session.c b/ext/session/sqlite3session.c index f2eb942e68..1cf1bf4669 100644 --- a/ext/session/sqlite3session.c +++ b/ext/session/sqlite3session.c @@ -139,11 +139,13 @@ struct sqlite3_changeset_iter { struct SessionTable { SessionTable *pNext; char *zName; /* Local name of table */ - int nCol; /* Number of columns in table zName */ + int nCol; /* Number of non-hidden columns */ + int nTotalCol; /* Number of columns including hidden */ int bStat1; /* True if this is sqlite_stat1 */ int bRowid; /* True if this table uses rowid for PK */ const char **azCol; /* Column names */ const char **azDflt; /* Default value expressions */ + int *aiIdx; /* Index to pass to xNew/xOld */ u8 *abPK; /* Array of primary key flags */ int nEntry; /* Total number of entries in hash table */ int nChange; /* Size of apChange[] array */ @@ -546,22 +548,22 @@ static int sessionPreupdateHash( unsigned int h = 0; /* Hash value to return */ int i; /* Used to iterate through columns */ + assert( pTab->nTotalCol==pSession->hook.xCount(pSession->hook.pCtx) ); if( pTab->bRowid ){ - assert( pTab->nCol-1==pSession->hook.xCount(pSession->hook.pCtx) ); h = sessionHashAppendI64(h, iRowid); }else{ assert( *pbNullPK==0 ); - assert( pTab->nCol==pSession->hook.xCount(pSession->hook.pCtx) ); for(i=0; inCol; i++){ if( pTab->abPK[i] ){ int rc; int eType; sqlite3_value *pVal; + int iIdx = pTab->aiIdx[i]; if( bNew ){ - rc = pSession->hook.xNew(pSession->hook.pCtx, i, &pVal); + rc = pSession->hook.xNew(pSession->hook.pCtx, iIdx, &pVal); }else{ - rc = pSession->hook.xOld(pSession->hook.pCtx, i, &pVal); + rc = pSession->hook.xOld(pSession->hook.pCtx, iIdx, &pVal); } if( rc!=SQLITE_OK ) return rc; @@ -898,6 +900,7 @@ static int sessionPreupdateEqual( sqlite3_value *pVal; /* Value returned by preupdate_new/old */ int rc; /* Error code from preupdate_new/old */ int eType = *a++; /* Type of value from change record */ + int iIdx = pTab->aiIdx[iCol]; /* The following calls to preupdate_new() and preupdate_old() can not ** fail. This is because they cache their return values, and by the @@ -906,10 +909,10 @@ static int sessionPreupdateEqual( ** this (that the method has already been called). */ if( op==SQLITE_INSERT ){ /* assert( db->pPreUpdate->pNewUnpacked || db->pPreUpdate->aNew ); */ - rc = pSession->hook.xNew(pSession->hook.pCtx, iCol, &pVal); + rc = pSession->hook.xNew(pSession->hook.pCtx, iIdx, &pVal); }else{ /* assert( db->pPreUpdate->pUnpacked ); */ - rc = pSession->hook.xOld(pSession->hook.pCtx, iCol, &pVal); + rc = pSession->hook.xOld(pSession->hook.pCtx, iIdx, &pVal); } assert( rc==SQLITE_OK ); (void)rc; /* Suppress warning about unused variable */ @@ -1034,9 +1037,11 @@ static int sessionTableInfo( const char *zDb, /* Name of attached database (e.g. "main") */ const char *zThis, /* Table name */ int *pnCol, /* OUT: number of columns */ + int *pnTotalCol, /* OUT: number of hidden columns */ const char **pzTab, /* OUT: Copy of zThis */ const char ***pazCol, /* OUT: Array of column names for table */ const char ***pazDflt, /* OUT: Array of default value expressions */ + int **paiIdx, /* OUT: Array of xNew/xOld indexes */ u8 **pabPK, /* OUT: Array of booleans - true for PK col */ int *pbRowid /* OUT: True if only PK is a rowid */ ){ @@ -1051,6 +1056,7 @@ static int sessionTableInfo( char **azCol = 0; char **azDflt = 0; u8 *abPK = 0; + int *aiIdx = 0; int bRowid = 0; /* Set to true to use rowid as PK */ assert( pazCol && pabPK ); @@ -1058,6 +1064,8 @@ static int sessionTableInfo( *pazCol = 0; *pabPK = 0; *pnCol = 0; + if( pnTotalCol ) *pnTotalCol = 0; + if( paiIdx ) *paiIdx = 0; if( pzTab ) *pzTab = 0; if( pazDflt ) *pazDflt = 0; @@ -1067,9 +1075,9 @@ static int sessionTableInfo( if( rc==SQLITE_OK ){ /* For sqlite_stat1, pretend that (tbl,idx) is the PRIMARY KEY. */ zPragma = sqlite3_mprintf( - "SELECT 0, 'tbl', '', 0, '', 1 UNION ALL " - "SELECT 1, 'idx', '', 0, '', 2 UNION ALL " - "SELECT 2, 'stat', '', 0, '', 0" + "SELECT 0, 'tbl', '', 0, '', 1, 0 UNION ALL " + "SELECT 1, 'idx', '', 0, '', 2, 0 UNION ALL " + "SELECT 2, 'stat', '', 0, '', 0, 0" ); }else if( rc==SQLITE_ERROR ){ zPragma = sqlite3_mprintf(""); @@ -1077,7 +1085,7 @@ static int sessionTableInfo( return rc; } }else{ - zPragma = sqlite3_mprintf("PRAGMA '%q'.table_info('%q')", zDb, zThis); + zPragma = sqlite3_mprintf("PRAGMA '%q'.table_xinfo('%q')", zDb, zThis); } if( !zPragma ){ return SQLITE_NOMEM; @@ -1094,7 +1102,9 @@ static int sessionTableInfo( while( SQLITE_ROW==sqlite3_step(pStmt) ){ nByte += sqlite3_column_bytes(pStmt, 1); /* name */ nByte += sqlite3_column_bytes(pStmt, 4); /* dflt_value */ - nDbCol++; + if( sqlite3_column_int(pStmt, 6)==0 ){ /* !hidden */ + nDbCol++; + } if( sqlite3_column_int(pStmt, 5) ) bRowid = 0; /* pk */ } if( nDbCol==0 ) bRowid = 0; @@ -1103,7 +1113,7 @@ static int sessionTableInfo( rc = sqlite3_reset(pStmt); if( rc==SQLITE_OK ){ - nByte += nDbCol * (sizeof(const char *)*2 + sizeof(u8) + 1 + 1); + nByte += nDbCol * (sizeof(const char *)*2 +sizeof(int)+sizeof(u8) + 1 + 1); pAlloc = sessionMalloc64(pSession, nByte); if( pAlloc==0 ){ rc = SQLITE_NOMEM; @@ -1114,8 +1124,8 @@ static int sessionTableInfo( if( rc==SQLITE_OK ){ azCol = (char **)pAlloc; azDflt = (char**)&azCol[nDbCol]; - pAlloc = (u8 *)&azDflt[nDbCol]; - abPK = (u8 *)pAlloc; + aiIdx = (int*)&azDflt[nDbCol]; + abPK = (u8 *)&aiIdx[nDbCol]; pAlloc = &abPK[nDbCol]; if( pzTab ){ memcpy(pAlloc, zThis, nThis+1); @@ -1130,27 +1140,32 @@ static int sessionTableInfo( azCol[i] = (char*)pAlloc; pAlloc += nName+1; abPK[i] = 1; + aiIdx[i] = -1; i++; } while( SQLITE_ROW==sqlite3_step(pStmt) ){ - int nName = sqlite3_column_bytes(pStmt, 1); - int nDflt = sqlite3_column_bytes(pStmt, 4); - const unsigned char *zName = sqlite3_column_text(pStmt, 1); - const unsigned char *zDflt = sqlite3_column_text(pStmt, 4); + if( sqlite3_column_int(pStmt, 6)==0 ){ /* !hidden */ + int nName = sqlite3_column_bytes(pStmt, 1); + int nDflt = sqlite3_column_bytes(pStmt, 4); + const unsigned char *zName = sqlite3_column_text(pStmt, 1); + const unsigned char *zDflt = sqlite3_column_text(pStmt, 4); - if( zName==0 ) break; - memcpy(pAlloc, zName, nName+1); - azCol[i] = (char *)pAlloc; - pAlloc += nName+1; - if( zDflt ){ - memcpy(pAlloc, zDflt, nDflt+1); - azDflt[i] = (char *)pAlloc; - pAlloc += nDflt+1; - }else{ - azDflt[i] = 0; + if( zName==0 ) break; + memcpy(pAlloc, zName, nName+1); + azCol[i] = (char *)pAlloc; + pAlloc += nName+1; + if( zDflt ){ + memcpy(pAlloc, zDflt, nDflt+1); + azDflt[i] = (char *)pAlloc; + pAlloc += nDflt+1; + }else{ + azDflt[i] = 0; + } + abPK[i] = sqlite3_column_int(pStmt, 5); + aiIdx[i] = sqlite3_column_int(pStmt, 0); + i++; } - abPK[i] = sqlite3_column_int(pStmt, 5); - i++; + if( pnTotalCol ) (*pnTotalCol)++; } rc = sqlite3_reset(pStmt); } @@ -1163,6 +1178,7 @@ static int sessionTableInfo( if( pazDflt ) *pazDflt = (const char**)azDflt; *pabPK = abPK; *pnCol = nDbCol; + if( paiIdx ) *paiIdx = aiIdx; }else{ sessionFree(pSession, azCol); } @@ -1194,7 +1210,8 @@ static int sessionInitTable( u8 *abPK; assert( pTab->azCol==0 || pTab->abPK==0 ); rc = sessionTableInfo(pSession, db, zDb, - pTab->zName, &pTab->nCol, 0, &pTab->azCol, &pTab->azDflt, &abPK, + pTab->zName, &pTab->nCol, &pTab->nTotalCol, 0, &pTab->azCol, + &pTab->azDflt, &pTab->aiIdx, &abPK, ((pSession==0 || pSession->bImplicitPK) ? &pTab->bRowid : 0) ); if( rc==SQLITE_OK ){ @@ -1229,15 +1246,17 @@ static int sessionInitTable( */ static int sessionReinitTable(sqlite3_session *pSession, SessionTable *pTab){ int nCol = 0; + int nTotalCol = 0; const char **azCol = 0; const char **azDflt = 0; + int *aiIdx = 0; u8 *abPK = 0; int bRowid = 0; assert( pSession->rc==SQLITE_OK ); pSession->rc = sessionTableInfo(pSession, pSession->db, pSession->zDb, - pTab->zName, &nCol, 0, &azCol, &azDflt, &abPK, + pTab->zName, &nCol, &nTotalCol, 0, &azCol, &azDflt, &aiIdx, &abPK, (pSession->bImplicitPK ? &bRowid : 0) ); if( pSession->rc==SQLITE_OK ){ @@ -1260,8 +1279,10 @@ static int sessionReinitTable(sqlite3_session *pSession, SessionTable *pTab){ const char **a = pTab->azCol; pTab->azCol = azCol; pTab->nCol = nCol; + pTab->nTotalCol = nTotalCol; pTab->azDflt = azDflt; pTab->abPK = abPK; + pTab->aiIdx = aiIdx; azCol = a; } if( pSession->bEnableSize ){ @@ -1579,7 +1600,7 @@ static int sessionUpdateMaxSize( int ii; for(ii=0; iinCol; ii++){ sqlite3_value *p = 0; - pSession->hook.xNew(pSession->hook.pCtx, ii, &p); + pSession->hook.xNew(pSession->hook.pCtx, pTab->aiIdx[ii], &p); sessionSerializeValue(0, p, &nNew); } } @@ -1599,8 +1620,9 @@ static int sessionUpdateMaxSize( int bChanged = 1; int nOld = 0; int eType; + int iIdx = pTab->aiIdx[ii]; sqlite3_value *p = 0; - pSession->hook.xNew(pSession->hook.pCtx, ii-pTab->bRowid, &p); + pSession->hook.xNew(pSession->hook.pCtx, iIdx, &p); if( p==0 ){ return SQLITE_NOMEM; } @@ -1697,11 +1719,11 @@ static void sessionPreupdateOneChange( /* Check the number of columns in this xPreUpdate call matches the ** number of columns in the table. */ nExpect = pSession->hook.xCount(pSession->hook.pCtx); - if( (pTab->nCol-pTab->bRowid)nTotalColnCol-pTab->bRowid)!=nExpect ){ + if( pTab->nTotalCol!=nExpect ){ pSession->rc = SQLITE_SCHEMA; return; } @@ -1758,14 +1780,15 @@ static void sessionPreupdateOneChange( /* Figure out how large an allocation is required */ nByte = sizeof(SessionChange); - for(i=0; i<(pTab->nCol-pTab->bRowid); i++){ + for(i=pTab->bRowid; inCol; i++){ + int iIdx = pTab->aiIdx[i]; sqlite3_value *p = 0; if( op!=SQLITE_INSERT ){ /* This may fail if the column has a non-NULL default and was added ** using ALTER TABLE ADD COLUMN after this record was created. */ - rc = pSession->hook.xOld(pSession->hook.pCtx, i, &p); + rc = pSession->hook.xOld(pSession->hook.pCtx, iIdx, &p); }else if( pTab->abPK[i] ){ - TESTONLY(int trc = ) pSession->hook.xNew(pSession->hook.pCtx, i, &p); + TESTONLY(int trc = ) pSession->hook.xNew(pSession->hook.pCtx,iIdx,&p); assert( trc==SQLITE_OK ); } @@ -1800,12 +1823,13 @@ static void sessionPreupdateOneChange( sessionPutI64(&pC->aRecord[1], iRowid); nByte = 9; } - for(i=0; i<(pTab->nCol-pTab->bRowid); i++){ + for(i=pTab->bRowid; inCol; i++){ sqlite3_value *p = 0; + int iIdx = pTab->aiIdx[i]; if( op!=SQLITE_INSERT ){ - pSession->hook.xOld(pSession->hook.pCtx, i, &p); + pSession->hook.xOld(pSession->hook.pCtx, iIdx, &p); }else if( pTab->abPK[i] ){ - pSession->hook.xNew(pSession->hook.pCtx, i, &p); + pSession->hook.xNew(pSession->hook.pCtx, iIdx, &p); } sessionSerializeValue(&pC->aRecord[nByte], p, &nByte); } @@ -2207,7 +2231,8 @@ int sqlite3session_diff( int bRowid = 0; u8 *abPK; const char **azCol = 0; - rc = sessionTableInfo(0, db, zFrom, zTbl, &nCol, 0, &azCol, 0, &abPK, + rc = sessionTableInfo(0, db, zFrom, zTbl, + &nCol, 0, 0, &azCol, 0, 0, &abPK, pSession->bImplicitPK ? &bRowid : 0 ); if( rc==SQLITE_OK ){ @@ -2531,9 +2556,11 @@ static void sessionAppendIdent( char *zOut = (char *)&p->aBuf[p->nBuf]; const char *zIn = zStr; *zOut++ = '"'; - while( *zIn ){ - if( *zIn=='"' ) *zOut++ = '"'; - *zOut++ = *(zIn++); + if( zIn!=0 ){ + while( *zIn ){ + if( *zIn=='"' ) *zOut++ = '"'; + *zOut++ = *(zIn++); + } } *zOut++ = '"'; p->nBuf = (int)((u8 *)zOut - p->aBuf); @@ -2784,10 +2811,10 @@ static int sessionSelectStmt( int rc = SQLITE_OK; char *zSql = 0; const char *zSep = ""; - const char *zCols = bRowid ? SESSIONS_ROWID ", *" : "*"; int nSql = -1; int i; + SessionBuffer cols = {0, 0, 0}; SessionBuffer nooptest = {0, 0, 0}; SessionBuffer pkfield = {0, 0, 0}; SessionBuffer pkvar = {0, 0, 0}; @@ -2800,9 +2827,16 @@ static int sessionSelectStmt( sessionAppendStr(&pkvar, "?1, (CASE WHEN ?2=X'' THEN NULL ELSE ?2 END)", &rc ); - zCols = "tbl, ?2, stat"; + sessionAppendStr(&cols, "tbl, ?2, stat", &rc); }else{ + #if 0 + if( bRowid ){ + sessionAppendStr(&cols, SESSIONS_ROWID, &rc); + } + #endif for(i=0; i/dev/null) - ifeq (,$(wasm-strip.bin)) - $(info WARNING: *******************************************************************) - $(info WARNING: Builds using -O2/-O3/-Os/-Oz will minify WASM-exported names,) - $(info WARNING: breaking _All The Things_. The workaround for that is to build) - $(info WARNING: with -g3 (which explodes the file size) and then strip the debug) - $(info WARNING: info after compilation, using wasm-strip, to shrink the wasm file.) - $(info WARNING: wasm-strip was not found in the PATH so we cannot strip those.) - $(info WARNING: If this build uses any optimization level higher than -O1 then) - $(info WARNING: the ***resulting JS code WILL NOT BE USABLE***.) - $(info WARNING: wasm-strip is part of the wabt package:) - $(info WARNING: https://github.com/WebAssembly/wabt) - $(info WARNING: on Ubuntu-like systems it can be installed with:) - $(info WARNING: sudo apt install wabt) - $(info WARNING: *******************************************************************) - ifneq (,$(filter $(OPTIMIZED_TARGETS),$(MAKECMDGOALS))) - $(error Cannot make release-quality binary because wasm-strip is not available.) - endif - wasm-strip.bin := echo "not wasm-stripping" - endif -endif -maybe-wasm-strip := $(wasm-strip.bin) - ######################################################################## # barebones=1 disables all "extraneous" stuff from sqlite3-wasm.c, the # goal being to create a WASM file with only the core APIs. @@ -376,20 +377,7 @@ $(bin.stripccomments): $(bin.stripccomments).c $(MAKEFILE) DISTCLEAN_FILES += $(bin.stripccomments) ######################################################################## -# bin.mkwb is used for generating some of the makefile code for the -# various wasm builds. It used to be generated in this makefile via a -# difficult-to-read/maintain block of $(eval)'d code. Attempts were -# made to generate it from tcl and bash (shell) but having to escape -# the $ references in those languages made it just as illegible as the -# native makefile code. Somewhat surprisingly, moving that code generation -# to C makes it slightly less illegible than the previous 3 options. -bin.mkwb := ./mkwasmbuilds -$(bin.mkwb): $(bin.mkwb).c $(MAKEFILE) - $(CC) -o $@ $< -DISTCLEAN_FILES += $(bin.mkwb) - -######################################################################## -# C-PP.FILTER: a $(call)able to transform $(1) to $(2) via: +# SQLITE.CALL.C-PP.FILTER: a $(call)able to transform $(1) to $(2) via: # # ./c-pp -f $(1) -o $(2) $(3) # @@ -419,7 +407,7 @@ DISTCLEAN_FILES += $(bin.mkwb) # JS/WASM build. They are solely for use with $(bin.c-pp) itself. # # -D... flags which should be included in all invocations should be -# appended to $(C-PP.FILTER.global). +# appended to $(SQLITE.CALL.C-PP.FILTER.global). bin.c-pp := ./c-pp $(bin.c-pp): c-pp.c $(sqlite3.c) $(MAKEFILE) $(CC) -O0 -o $@ c-pp.c $(sqlite3.c) '-DCMPP_DEFAULT_DELIM="//#"' -I$(dir.top) \ @@ -427,20 +415,21 @@ $(bin.c-pp): c-pp.c $(sqlite3.c) $(MAKEFILE) -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_WAL -DSQLITE_THREADSAFE=0 \ -DSQLITE_TEMP_STORE=3 DISTCLEAN_FILES += $(bin.c-pp) -C-PP.FILTER.global ?= +SQLITE.CALL.C-PP.FILTER.global ?= ifeq (1,$(SQLITE_C_IS_SEE)) - C-PP.FILTER.global += -Denable-see + SQLITE.CALL.C-PP.FILTER.global += -Denable-see endif -define C-PP.FILTER +define SQLITE.CALL.C-PP.FILTER # Create $2 from $1 using $(bin.c-pp) # $1 = Input file: c-pp -f $(1).js # $2 = Output file: c-pp -o $(2).js # $3 = optional c-pp -D... flags -$(2): $(1) $$(MAKEFILE) $$(bin.c-pp) - $$(bin.c-pp) -f $(1) -o $$@ $(3) $(C-PP.FILTER.global) +$(2): $(1) $$(MAKEFILE_LIST) $$(bin.c-pp) + @mkdir -p $$(dir $$@) + $$(bin.c-pp) -f $(1) -o $$@ $(3) $(SQLITE.CALL.C-PP.FILTER.global) #CLEAN_FILES += $(2) endef -# /end C-PP.FILTER +# /end SQLITE.CALL.C-PP.FILTER ######################################################################## # cflags.common = C compiler flags for all builds @@ -618,7 +607,6 @@ emcc.cflags += -I. -I$(dir.top) ######################################################################## # emcc flags specific to building .js/.wasm files... emcc.jsflags := -fPIC -emcc.jsflags += --minify 0 emcc.jsflags += --no-entry emcc.jsflags += -sWASM_BIGINT=$(emcc.WASM_BIGINT) emcc.jsflags += -sMODULARIZE @@ -631,6 +619,12 @@ emcc.exportedRuntimeMethods := \ emcc.jsflags += $(emcc.exportedRuntimeMethods) emcc.jsflags += -sUSE_CLOSURE_COMPILER=0 emcc.jsflags += -sIMPORTED_MEMORY +ifeq (,$(filter -O0,$(emcc_opt))) +emcc.assert ?= 0 +else +emcc.assert ?= 2 +endif +emcc.jsflags += -sASSERTIONS=$(emcc.assert) emcc.jsflags += -sSTRICT_JS=0 # STRICT_JS disabled due to: # https://github.com/emscripten-core/emscripten/issues/18610 @@ -826,7 +820,7 @@ sqlite3-wasmfs.cfiles := $(sqlite3-wasm.cfiles) ######################################################################## ######################################################################## -# SQLITE3.xJS.ESM-EXPORT-DEFAULT is used by mkwasmbuilds.c and the +# SQLITE.CALL.xJS.ESM-EXPORT-DEFAULT is used by mkwasmbuilds.c and the # wasmfs build. $1 is 1 if the build mode needs this workaround # (modes: esm, bundler-friendly, node) and 0 if not (vanilla). $2 must # be 0 for all builds except sqlite3-wasmfs.mjs, in which case it must @@ -847,9 +841,9 @@ sqlite3-wasmfs.cfiles := $(sqlite3-wasm.cfiles) # # Maintenance reminder: Mac sed works differently than GNU sed, so we # use awk instead of sed for this. -define SQLITE3.xJS.ESM-EXPORT-DEFAULT +define SQLITE.CALL.xJS.ESM-EXPORT-DEFAULT if [ x1 = x$(1) ]; then \ - echo "Fragile workaround for emscripten/issues/18237. See SQLITE3.xJS.ESM-EXPORT-DEFAULT."; \ + echo "Fragile workaround for emscripten/issues/18237. See SQLITE.CALL.xJS.ESM-EXPORT-DEFAULT."; \ {\ awk '/^export default/ && !f{f=1; next} 1' $@ > $@.tmp && mv $@.tmp $@; \ } || exit $$?; \ @@ -873,14 +867,6 @@ sqlite3-node.mjs := $(dir.dout)/sqlite3-node.mjs sqlite3-api-wasmfs.mjs := $(dir.tmp)/sqlite3-api-wasmfs.mjs sqlite3-wasmfs.mjs := $(dir.wasmfs)/sqlite3-wasmfs.mjs EXPORTED_FUNCTIONS.fiddle := $(dir.tmp)/EXPORTED_FUNCTIONS.fiddle -ifneq (1,$(MAKING_CLEAN)) -.wasmbuilds.make: $(bin.mkwb) - @rm -f $@ - $(bin.mkwb) > $@ - @chmod -w $@ --include .wasmbuilds.make -endif -DISTCLEAN_FILES += .wasmbuilds.make # The various -D... values used by *.c-pp.js include: # @@ -930,22 +916,47 @@ sqlite3-worker1-promiser.js := $(dir.dout)/sqlite3-worker1-promiser.js sqlite3-worker1-promiser.mjs := $(dir.dout)/sqlite3-worker1-promiser.mjs sqlite3-worker1-bundler-friendly.mjs := $(dir.dout)/sqlite3-worker1-bundler-friendly.mjs sqlite3-worker1-promiser-bundler-friendly.js := $(dir.dout)/sqlite3-worker1-promiser-bundler-friendly.js -$(eval $(call C-PP.FILTER,$(sqlite3-worker1.js.in),$(sqlite3-worker1.js))) -$(eval $(call C-PP.FILTER,$(sqlite3-worker1.js.in),$(sqlite3-worker1-bundler-friendly.mjs),\ + +ifneq (1,$(MAKING_CLEAN)) +# This block MUST come between the above definitions of +# sqlite3-...js/mjs and the $(eval) calls below this block which use +# SQLITE.CALL.C-PP.FILTER. +######################################################################## +# bin.mkwb is used for generating some of the makefile code for the +# various wasm builds. It used to be generated in this makefile via a +# difficult-to-read/maintain block of $(eval)'d code. Attempts were +# made to generate it from tcl and bash (shell) but having to escape +# the $ references in those languages made it just as illegible as the +# native makefile code. Somewhat surprisingly, moving that code generation +# to C makes it slightly less illegible than the previous 3 options. +bin.mkwb := ./mkwasmbuilds +$(bin.mkwb): $(bin.mkwb).c $(MAKEFILE) + $(CC) -o $@ $< +DISTCLEAN_FILES += $(bin.mkwb) +.wasmbuilds.make: $(bin.mkwb) + @rm -f $@ + $(bin.mkwb) > $@ + @chmod -w $@ +-include .wasmbuilds.make +endif +DISTCLEAN_FILES += .wasmbuilds.make + +$(eval $(call SQLITE.CALL.C-PP.FILTER,$(sqlite3-worker1.js.in),$(sqlite3-worker1.js))) +$(eval $(call SQLITE.CALL.C-PP.FILTER,$(sqlite3-worker1.js.in),$(sqlite3-worker1-bundler-friendly.mjs),\ $(c-pp.D.sqlite3-bundler-friendly))) -$(eval $(call C-PP.FILTER,$(sqlite3-worker1-promiser.js.in),$(sqlite3-worker1-promiser.js))) -$(eval $(call C-PP.FILTER,$(sqlite3-worker1-promiser.js.in),\ +$(eval $(call SQLITE.CALL.C-PP.FILTER,$(sqlite3-worker1-promiser.js.in),$(sqlite3-worker1-promiser.js))) +$(eval $(call SQLITE.CALL.C-PP.FILTER,$(sqlite3-worker1-promiser.js.in),\ $(sqlite3-worker1-promiser-bundler-friendly.js),\ $(c-pp.D.sqlite3-bundler-friendly))) -$(eval $(call C-PP.FILTER,$(sqlite3-worker1-promiser.js.in),$(sqlite3-worker1-promiser.mjs),\ +$(eval $(call SQLITE.CALL.C-PP.FILTER,$(sqlite3-worker1-promiser.js.in),$(sqlite3-worker1-promiser.mjs),\ -Dtarget=es6-module -Dtarget=es6-bundler-friendly)) $(sqlite3-bundler-friendly.mjs): $(sqlite3-worker1-bundler-friendly.mjs) \ $(sqlite3-worker1-promiser-bundler-friendly.js) -$(eval $(call C-PP.FILTER,demo-worker1-promiser.c-pp.js,demo-worker1-promiser.js)) -$(eval $(call C-PP.FILTER,demo-worker1-promiser.c-pp.js,demo-worker1-promiser.mjs,\ +$(eval $(call SQLITE.CALL.C-PP.FILTER,demo-worker1-promiser.c-pp.js,demo-worker1-promiser.js)) +$(eval $(call SQLITE.CALL.C-PP.FILTER,demo-worker1-promiser.c-pp.js,demo-worker1-promiser.mjs,\ -Dtarget=es6-module)) -$(eval $(call C-PP.FILTER,demo-worker1-promiser.c-pp.html,demo-worker1-promiser.html)) -$(eval $(call C-PP.FILTER,demo-worker1-promiser.c-pp.html,demo-worker1-promiser-esm.html,\ +$(eval $(call SQLITE.CALL.C-PP.FILTER,demo-worker1-promiser.c-pp.html,demo-worker1-promiser.html)) +$(eval $(call SQLITE.CALL.C-PP.FILTER,demo-worker1-promiser.c-pp.html,demo-worker1-promiser-esm.html,\ -Dtarget=es6-module)) all: $(sqlite3-worker1.js) \ $(sqlite3-worker1-promiser.js) $(sqlite3-worker1-promiser.mjs) @@ -1042,7 +1053,7 @@ $(speedtest1.js): $(MAKEFILE) $(speedtest1.cfiles) \ $(pre-post-speedtest1-vanilla.deps) \ $(EXPORTED_FUNCTIONS.speedtest1) @echo "Building $@ ..." - $(emcc.bin) \ + $(bin.emcc) \ $(emcc.speedtest1) \ $(emcc.speedtest1.common) \ $(emcc.flags.speedtest1-vanilla) $(pre-post-speedtest1-vanilla.flags) \ @@ -1052,6 +1063,7 @@ $(speedtest1.js): $(MAKEFILE) $(speedtest1.cfiles) \ $(speedtest1.exit-runtime0) \ -o $@ $(speedtest1.cfiles) -lm $(maybe-wasm-strip) $(speedtest1.wasm) + sed -i -e '/^var _sqlite3.*createExportWrapper/d' $@ chmod -x $(speedtest1.wasm) ls -la $@ $(speedtest1.wasm) @@ -1077,10 +1089,10 @@ all: speedtest1 # tester1.mjs: cases 3 and 4 # # To create those, we filter tester1.c-pp.js with $(bin.c-pp)... -$(eval $(call C-PP.FILTER,tester1.c-pp.js,tester1.js)) -$(eval $(call C-PP.FILTER,tester1.c-pp.js,tester1.mjs,$(c-pp.D.sqlite3-esm))) -$(eval $(call C-PP.FILTER,tester1.c-pp.html,tester1.html)) -$(eval $(call C-PP.FILTER,tester1.c-pp.html,tester1-esm.html,$(c-pp.D.sqlite3-esm))) +$(eval $(call SQLITE.CALL.C-PP.FILTER,tester1.c-pp.js,tester1.js)) +$(eval $(call SQLITE.CALL.C-PP.FILTER,tester1.c-pp.js,tester1.mjs,$(c-pp.D.sqlite3-esm))) +$(eval $(call SQLITE.CALL.C-PP.FILTER,tester1.c-pp.html,tester1.html)) +$(eval $(call SQLITE.CALL.C-PP.FILTER,tester1.c-pp.html,tester1-esm.html,$(c-pp.D.sqlite3-esm))) tester1: tester1.js tester1.mjs tester1.html tester1-esm.html # Note that we do not include $(sqlite3-bundler-friendly.mjs) in this # because bundlers are client-specific. diff --git a/ext/wasm/README-dist.txt b/ext/wasm/README-dist.txt index 6656a2072a..4a527fc5ef 100644 --- a/ext/wasm/README-dist.txt +++ b/ext/wasm/README-dist.txt @@ -31,16 +31,20 @@ build: use of and is not demonstrated here. Browsers will not serve WASM files from file:// URLs, so the test and -demonstration apps require a web server and that server must include -the following headers in its response when serving the files: +demonstration apps require a web server and that server must, for the +OPFS[^1]-related features, include the following headers in its response +when serving the files: Cross-Origin-Opener-Policy: same-origin Cross-Origin-Embedder-Policy: require-corp -The core library will function without those headers but certain -features, most notably OPFS storage, will not be available. +Most functionality will work without those headers but the OPFS[^1] +storage capability will not be available without them. One simple way to get the demo apps up and running on Unix-style systems is to install althttpd (https://sqlite.org/althttpd) and run: althttpd --enable-sab --page index.html + + +[^1]: https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system diff --git a/ext/wasm/README.md b/ext/wasm/README.md index 0c328310d9..c41e54bd2a 100644 --- a/ext/wasm/README.md +++ b/ext/wasm/README.md @@ -29,8 +29,16 @@ $ ./emsdk install latest $ ./emsdk activate latest ``` -The following needs to be run for each shell instance which needs the -`emcc` compiler: +(Sidebar: Emscripten updates can and do _change things_, i.e. _break +things_, so it's considered _required practice_ to test thoroughly +after upgrading it! Our build process makes no guarantees about which +Emscripten version(s) will or won't work, but it's important that +production builds are built using a compatible version. During active +development, the EMSDK is frequently updated, the goal being to keep +`sqlite3.wasm` working with "the latest" EMSDK.) + +The SQLite configure script will search for the EMSDK. One way +to ensure that it finds it is: ``` # Activate PATH and other environment variables in the current terminal: @@ -38,15 +46,27 @@ $ source ./emsdk_env.sh $ which emcc /path/to/emsdk/upstream/emscripten/emcc + +$ ./configure ... ``` -Optionally, add that to your login shell's resource file (`~/.bashrc` -or equivalent). +Optionally, add that `source` part to your login shell's resource file +(`~/.bashrc` or equivalent). -That `env` script needs to be sourced for building this application -from the top of the sqlite3 build tree: +Another way is to pass the EMSDK dir to configure: ``` +$ ./configure --with-emsdk=/path/to/emsdk +``` + +The build tree uses a small wrapper for invoking the `emcc` (the +Emscripten compiler): `tool/emcc.sh` is generated from +`tool/emcc.sh.in` using the EMSDK path found by the configure process. + +With that in place, the most common build approaches are: + +``` +# From the top of the tree: $ make fiddle ``` @@ -57,7 +77,7 @@ $ cd ext/wasm $ make ``` -That will generate the a number of files required for a handful of +Those will generate the a number of files required for a handful of test and demo applications which can be accessed via `index.html`. WASM content cannot, due to XMLHttpRequest security limitations, be loaded if the containing HTML file is opened directly @@ -68,16 +88,21 @@ needs to be served via an HTTP server. For example, using ``` $ cd ext/wasm $ althttpd --enable-sab --max-age 1 --page index.html +# Or, more simply, from the ext/wasm dir: +$ make httpd ``` -That will open the system's browser and run the index page, from which -all of the test and demo applications can be accessed. +That will open the system's browser and visit the index page, from +which (almost) all of the test and demo applications can be accessed. +(`ext/wasm/SQLTester` is not listed in that page because it's only of +real utility when it's used in conjunction with the project's +proprietary test suite, which most users don't have access to.) Note that when serving this app via [althttpd][], it must be a version from 2022-09-26 or newer so that it recognizes the `--enable-sab` flag, which causes althttpd to emit two HTTP response headers which are required to enable JavaScript's `SharedArrayBuffer` and `Atomics` -APIs. Those APIs are required in order to enable the OPFS-related +APIs. Those APIs are required in order to enable the [OPFS][]-related features in the apps which use them. # Testing on a remote machine that is accessed via SSH @@ -104,3 +129,4 @@ be tunneled using SSH. [emscripten]: https://emscripten.org [althttpd]: https://sqlite.org/althttpd [SharedArrayBuffer]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer +[OPFS]: https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system diff --git a/ext/wasm/api/README.md b/ext/wasm/api/README.md index ebd4aaacb1..ce7e58ebed 100644 --- a/ext/wasm/api/README.md +++ b/ext/wasm/api/README.md @@ -85,17 +85,18 @@ browser client: Installs the `sqlite3.vtab` namespace, which contain helpers for use by downstream code which creates `sqlite3_module` implementations. - **`sqlite3-vfs-opfs.c-pp.js`**\ - is an sqlite3 VFS implementation which supports the Origin-Private - FileSystem (OPFS) as a storage layer to provide persistent storage - for database files in a browser. It requires... + is an sqlite3 VFS implementation which supports the [Origin-Private + FileSystem (OPFS)][OPFS] as a storage layer to provide persistent + storage for database files in a browser. It requires... - **`sqlite3-opfs-async-proxy.js`**\ - is the asynchronous backend part of the OPFS proxy. It speaks - directly to the (async) OPFS API and channels those results back - to its synchronous counterpart. This file, because it must be - started in its own Worker, is not part of the amalgamation. + is the asynchronous backend part of the [OPFS][] proxy. It + speaks directly to the (async) OPFS API and channels those + results back to its synchronous counterpart. This file, because + it must be started in its own Worker, is not part of the + amalgamation. - **`sqlite3-vfs-opfs-sahpool.c-pp.js`**\ - is another sqlite3 VFS supporting the OPFS, but uses a completely - different approach that the above-listed one. + is another sqlite3 VFS supporting the [OPFS][], but uses a + completely different approach that the above-listed one. - **`sqlite3-api-cleanup.js`**\ The previous files do not immediately extend the library. Instead they add callback functions to be called during its @@ -152,7 +153,7 @@ into the build-generated `sqlite3.js` along with `sqlite3-api.js`. flag. This file overwrites the Emscripten-installed `sqlite3InitModule()` function with one which, after the module is loaded, also initializes the asynchronous parts of the sqlite3 - module. For example, the OPFS VFS support. + module. For example, the [OPFS][] VFS support. Preprocessing of Source Files @@ -164,3 +165,6 @@ builds. The preprocessor application itself is in [`c-pp.c`](/file/ext/wasm/c-pp.c) and the complete technical details of such preprocessing are maintained in [`GNUMakefile`](/file/ext/wasm/GNUmakefile). + + +[OPFS]: https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system diff --git a/ext/wasm/api/extern-post-js.c-pp.js b/ext/wasm/api/extern-post-js.c-pp.js index 63e55051c6..3598c0d6fe 100644 --- a/ext/wasm/api/extern-post-js.c-pp.js +++ b/ext/wasm/api/extern-post-js.c-pp.js @@ -12,6 +12,7 @@ const toExportForESM = //#endif (function(){ + //console.warn("this is extern-post-js"); /** In order to hide the sqlite3InitModule()'s resulting Emscripten module from downstream clients (and simplify our @@ -62,6 +63,16 @@ const toExportForESM = globalThis.sqlite3InitModule = function ff(...args){ //console.warn("Using replaced sqlite3InitModule()",globalThis.location); return originalInit(...args).then((EmscriptenModule)=>{ + //console.warn("originalInit() then() arg =",EmscriptenModule); + //console.warn("initModuleState =",initModuleState); + EmscriptenModule.runSQLite3PostLoadInit(EmscriptenModule); + const s = EmscriptenModule.sqlite3; + s.scriptInfo = initModuleState; + //console.warn("sqlite3.scriptInfo =",s.scriptInfo); + if(ff.__isUnderTest) s.__isUnderTest = true; + const f = s.asyncPostInit; + delete s.asyncPostInit; + const rv = f(); //#if wasmfs if('undefined'!==typeof WorkerGlobalScope && EmscriptenModule['ENVIRONMENT_IS_PTHREAD']){ @@ -74,14 +85,7 @@ const toExportForESM = return EmscriptenModule; } //#endif - //console.warn("sqlite3InitModule() returning sqlite3 object."); - const s = EmscriptenModule.sqlite3; - s.scriptInfo = initModuleState; - //console.warn("sqlite3.scriptInfo =",s.scriptInfo); - if(ff.__isUnderTest) s.__isUnderTest = true; - const f = s.asyncPostInit; - delete s.asyncPostInit; - return f(); + return rv; }).catch((e)=>{ console.error("Exception loading sqlite3 module:",e); throw e; diff --git a/ext/wasm/api/post-js-footer.js b/ext/wasm/api/post-js-footer.js index 58882cbd9c..7af12cbe34 100644 --- a/ext/wasm/api/post-js-footer.js +++ b/ext/wasm/api/post-js-footer.js @@ -1,4 +1,6 @@ /* The current function scope was opened via post-js-header.js, which gets prepended to this at build-time. This file closes that scope. */ -})/*postRun.push(...)*/; +//console.warn("This is the end of the Module.runSQLite3PostLoadInit handler."); +}/*Module.runSQLite3PostLoadInit()*/; +//console.warn("This is the end of the setup of the (pending) Module.runSQLite3PostLoadInit"); diff --git a/ext/wasm/api/post-js-header.js b/ext/wasm/api/post-js-header.js index 7fd82a7d6c..77e3cd227c 100644 --- a/ext/wasm/api/post-js-header.js +++ b/ext/wasm/api/post-js-header.js @@ -7,22 +7,27 @@ installs will be run after the WASM module is loaded, at which point the sqlite3 JS API bits will get set up. */ -if(!Module.postRun) Module.postRun = []; -Module.postRun.push(function(Module/*the Emscripten-style module object*/){ +Module.runSQLite3PostLoadInit = function(EmscriptenModule/*the Emscripten-style module object*/){ + /** ^^^ Don't use Module.postRun, as that runs a different time + depending on whether this file is built with emcc 3.1.x or + 4.0.x. This function name is intentionally obnoxiously verbose to + ensure that we don't collide with current and future Emscripten + symbol names. */ 'use strict'; + //console.warn("This is the start of Module.runSQLite3PostLoadInit()"); /* This function will contain at least the following: - - post-js-header.js (this file) + - post-js-header.js => this file - sqlite3-api-prologue.js => Bootstrapping bits to attach the rest to - common/whwasmutil.js => Replacements for much of Emscripten's glue - - jaccwaby/jaccwabyt.js => Jaccwabyt (C/JS struct binding) + - jaccwabyt/jaccwabyt.js => Jaccwabyt (C/JS struct binding) - sqlite3-api-glue.js => glues previous parts together - - sqlite3-api-oo.js => SQLite3 OO API #1 + - sqlite3-api-oo1.js => SQLite3 OO API #1 - sqlite3-api-worker1.js => Worker-based API - sqlite3-vfs-helper.c-pp.js => Utilities for VFS impls - sqlite3-vtab-helper.c-pp.js => Utilities for virtual table impls - - sqlite3-vfs-opfs.c-pp.js => OPFS VFS + - sqlite3-vfs-opfs.c-pp.js => OPFS VFS - sqlite3-vfs-opfs-sahpool.c-pp.js => OPFS SAHPool VFS - sqlite3-api-cleanup.js => final API cleanup - - post-js-footer.js => closes this postRun() function + - post-js-footer.js => closes this function */ diff --git a/ext/wasm/api/sqlite3-api-cleanup.js b/ext/wasm/api/sqlite3-api-cleanup.js index 65dbb4eb64..7fb29e6487 100644 --- a/ext/wasm/api/sqlite3-api-cleanup.js +++ b/ext/wasm/api/sqlite3-api-cleanup.js @@ -14,6 +14,9 @@ intended to be appended after all other sqlite3-api-*.js files so that it can finalize any setup and clean up any global symbols temporarily used for setting up the API's various subsystems. + + In Emscripten builds it's run in the context of a Module.postRun + handler. */ 'use strict'; if('undefined' !== typeof Module){ // presumably an Emscripten build diff --git a/ext/wasm/api/sqlite3-api-glue.c-pp.js b/ext/wasm/api/sqlite3-api-glue.c-pp.js index 680218370a..a40b832824 100644 --- a/ext/wasm/api/sqlite3-api-glue.c-pp.js +++ b/ext/wasm/api/sqlite3-api-glue.c-pp.js @@ -228,13 +228,49 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }), '*' ]], + /** + We do not have a way to automatically clean up destructors + which are automatically converted from JS functions via the + final argument to sqlite3_set_auxdata(). Because of that, + automatic function conversion is not supported for this + function. Clients should use wasm.installFunction() to create + such callbacks, then pass that pointer to + sqlite3_set_auxdata(). Relying on automated conversions here + would lead to leaks of JS/WASM proxy functions because + sqlite3_set_auxdata() is frequently called in UDFs. + + The sqlite3.oo1.DB class's onclose handlers can be used for this + purpose. For example: + + const pAuxDtor = wasm.installFunction('v(p)', function(ptr){ + //free ptr + }); + myDb.onclose = { + after: ()=>{ + wasm.uninstallFunction(pAuxDtor); + } + }; + + Then pass pAuxDtor as the final argument to appropriate + sqlite3_set_auxdata() calls. + + Note that versions prior to 3.49.0 ostensibly had automatic + function conversion here but a typo prevented it from + working. Rather than fix it, it was removed because testing the + fix brought the huge potential for memory leaks to the + forefront. + */ ["sqlite3_set_auxdata", undefined, [ "sqlite3_context*", "int", "*", - new wasm.xWrap.FuncPtrAdapter({ - name: 'xDestroyAuxData', - signature: 'v(*)', - contextKey: (argv, argIndex)=>argv[0/* sqlite3_context* */] - }) + true + ? "*" + : new wasm.xWrap.FuncPtrAdapter({ + /* If we can find a way to automate their cleanup, JS functions can + be auto-converted with this. */ + name: 'xDestroyAuxData', + signature: 'v(p)', + contextKey: (argv, argIndex)=>argv[0/* sqlite3_context* */] + }) ]], ["sqlite3_shutdown", undefined], ["sqlite3_sourceid", "string"], @@ -1047,6 +1083,10 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ 'sqlite3_set_authorizer', 'sqlite3_trace_v2', 'sqlite3_update_hook' + /* + We do not yet have a way to clean up automatically-converted + sqlite3_set_auxdata() finalizers. + */ ]) { const x = wasm.exports[name]; if( !x ){ diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index c8db3698c1..6b032be84d 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -12,12 +12,12 @@ This file is intended to be combined at build-time with other related code, most notably a header and footer which wraps this - whole file into an Emscripten Module.postRun() handler. The sqlite3 - JS API has no hard requirements on Emscripten and does not expose - any Emscripten APIs to clients. It is structured such that its build - can be tweaked to include it in arbitrary WASM environments which - can supply the necessary underlying features (e.g. a POSIX file I/O - layer). + whole file into a single callback which can be run after Emscripten + loads the corresponding WASM module. The sqlite3 JS API has no hard + requirements on Emscripten and does not expose any Emscripten APIs + to clients. It is structured such that its build can be tweaked to + include it in arbitrary WASM environments which can supply the + necessary underlying features (e.g. a POSIX file I/O layer). Main project home page: https://sqlite.org @@ -1712,41 +1712,48 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( missing or falsy pointer argument as 0. */ capi.sqlite3_db_config = function(pDb, op, ...args){ - if(!this.s){ - this.s = wasm.xWrap('sqlite3__wasm_db_config_s','int', - ['sqlite3*', 'int', 'string:static'] - /* MAINDBNAME requires a static string */); - this.pii = wasm.xWrap('sqlite3__wasm_db_config_pii', 'int', - ['sqlite3*', 'int', '*','int', 'int']); - this.ip = wasm.xWrap('sqlite3__wasm_db_config_ip','int', - ['sqlite3*', 'int', 'int','*']); - } switch(op){ - case capi.SQLITE_DBCONFIG_ENABLE_FKEY: - case capi.SQLITE_DBCONFIG_ENABLE_TRIGGER: - case capi.SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER: - case capi.SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION: - case capi.SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE: - case capi.SQLITE_DBCONFIG_ENABLE_QPSG: - case capi.SQLITE_DBCONFIG_TRIGGER_EQP: - case capi.SQLITE_DBCONFIG_RESET_DATABASE: - case capi.SQLITE_DBCONFIG_DEFENSIVE: - case capi.SQLITE_DBCONFIG_WRITABLE_SCHEMA: - case capi.SQLITE_DBCONFIG_LEGACY_ALTER_TABLE: - case capi.SQLITE_DBCONFIG_DQS_DML: - case capi.SQLITE_DBCONFIG_DQS_DDL: - case capi.SQLITE_DBCONFIG_ENABLE_VIEW: - case capi.SQLITE_DBCONFIG_LEGACY_FILE_FORMAT: - case capi.SQLITE_DBCONFIG_TRUSTED_SCHEMA: - case capi.SQLITE_DBCONFIG_STMT_SCANSTATUS: - case capi.SQLITE_DBCONFIG_REVERSE_SCANORDER: - return this.ip(pDb, op, args[0], args[1] || 0); - case capi.SQLITE_DBCONFIG_LOOKASIDE: - return this.pii(pDb, op, args[0], args[1], args[2]); - case capi.SQLITE_DBCONFIG_MAINDBNAME: - return this.s(pDb, op, args[0]); - default: - return capi.SQLITE_MISUSE; + case capi.SQLITE_DBCONFIG_ENABLE_FKEY: + case capi.SQLITE_DBCONFIG_ENABLE_TRIGGER: + case capi.SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER: + case capi.SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION: + case capi.SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE: + case capi.SQLITE_DBCONFIG_ENABLE_QPSG: + case capi.SQLITE_DBCONFIG_TRIGGER_EQP: + case capi.SQLITE_DBCONFIG_RESET_DATABASE: + case capi.SQLITE_DBCONFIG_DEFENSIVE: + case capi.SQLITE_DBCONFIG_WRITABLE_SCHEMA: + case capi.SQLITE_DBCONFIG_LEGACY_ALTER_TABLE: + case capi.SQLITE_DBCONFIG_DQS_DML: + case capi.SQLITE_DBCONFIG_DQS_DDL: + case capi.SQLITE_DBCONFIG_ENABLE_VIEW: + case capi.SQLITE_DBCONFIG_LEGACY_FILE_FORMAT: + case capi.SQLITE_DBCONFIG_TRUSTED_SCHEMA: + case capi.SQLITE_DBCONFIG_STMT_SCANSTATUS: + case capi.SQLITE_DBCONFIG_REVERSE_SCANORDER: + case capi.SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE: + case capi.SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE: + case capi.SQLITE_DBCONFIG_ENABLE_COMMENTS: + if( !this.ip ){ + this.ip = wasm.xWrap('sqlite3__wasm_db_config_ip','int', + ['sqlite3*', 'int', 'int', '*']); + } + return this.ip(pDb, op, args[0], args[1] || 0); + case capi.SQLITE_DBCONFIG_LOOKASIDE: + if( !this.pii ){ + this.pii = wasm.xWrap('sqlite3__wasm_db_config_pii', 'int', + ['sqlite3*', 'int', '*', 'int', 'int']); + } + return this.pii(pDb, op, args[0], args[1], args[2]); + case capi.SQLITE_DBCONFIG_MAINDBNAME: + if(!this.s){ + this.s = wasm.xWrap('sqlite3__wasm_db_config_s','int', + ['sqlite3*', 'int', 'string:static'] + /* MAINDBNAME requires a static string */); + } + return this.s(pDb, op, args[0]); + default: + return capi.SQLITE_MISUSE; } }.bind(Object.create(null)); diff --git a/ext/wasm/api/sqlite3-api-worker1.c-pp.js b/ext/wasm/api/sqlite3-api-worker1.c-pp.js index 9918625459..5e088f4384 100644 --- a/ext/wasm/api/sqlite3-api-worker1.c-pp.js +++ b/ext/wasm/api/sqlite3-api-worker1.c-pp.js @@ -279,11 +279,11 @@ The arguments are in the same form accepted by oo1.DB.exec(), with the exceptions noted below. - If the `countChanges` arguments property (added in version 3.43) is - truthy then the `result` property contained by the returned object - will have a `changeCount` property which holds the number of changes - made by the provided SQL. Because the SQL may contain an arbitrary - number of statements, the `changeCount` is calculated by calling + If `args.countChanges` (added in version 3.43) is truthy then the + `result` property contained by the returned object will have a + `changeCount` property which holds the number of changes made by the + provided SQL. Because the SQL may contain an arbitrary number of + statements, the `changeCount` is calculated by calling `sqlite3_total_changes()` before and after the SQL is evaluated. If the value of `countChanges` is 64 then the `changeCount` property will be returned as a 64-bit integer in the form of a BigInt (noting @@ -292,6 +292,15 @@ calling `sqlite3_total_changes64()` before and after the SQL is evaluated. + If the `args.lastInsertRowId` (added in version 3.50.0) is truthy + then the `result` property contained by the returned object will + have a `lastInsertRowId` will hold a BigInt-type value corresponding + to the result of sqlite3_last_insert_rowid(). This value is only + fetched once, after the SQL is run, regardless of how many + statements the SQL contains. This API has no idea whether the SQL + contains any INSERTs, so it is up to the client to apply/rely on + this property only when it makes sense to do so. + A function-type args.callback property cannot cross the window/Worker boundary, so is not useful here. If args.callback is a string then it is assumed to be a @@ -542,6 +551,12 @@ sqlite3.initWorker1API = function(){ if(undefined !== changeCount){ rc.changeCount = db.changes(true,64===rc.countChanges) - changeCount; } + const lastInsertRowId = !!rc.lastInsertRowId + ? sqlite3.capi.sqlite3_last_insert_rowid(db) + : undefined; + if( undefined!==lastInsertRowId ){ + rc.lastInsertRowId = lastInsertRowId; + } if(rc.callback instanceof Function){ rc.callback = theCallback; /* Post a sentinel message to tell the client that the end diff --git a/ext/wasm/api/sqlite3-wasm.c b/ext/wasm/api/sqlite3-wasm.c index c5dd495e54..461afe0663 100644 --- a/ext/wasm/api/sqlite3-wasm.c +++ b/ext/wasm/api/sqlite3-wasm.c @@ -331,7 +331,6 @@ SQLITE_WASM_EXPORT void sqlite3__wasm_pstack_restore(unsigned char * p){ */ SQLITE_WASM_EXPORT void * sqlite3__wasm_pstack_alloc(int n){ if( n<=0 ) return 0; - //if( n & 0x7 ) n += 8 - (n & 0x7) /* align to 8-byte boundary */; n = (n + 7) & ~7 /* align to 8-byte boundary */; if( PStack.pBegin + n > PStack.pPos /*not enough space left*/ || PStack.pBegin + n <= PStack.pBegin /*overflow*/ ) return 0; @@ -597,6 +596,9 @@ const char * sqlite3__wasm_enum_json(void){ DefInt(SQLITE_DBCONFIG_TRUSTED_SCHEMA); DefInt(SQLITE_DBCONFIG_STMT_SCANSTATUS); DefInt(SQLITE_DBCONFIG_REVERSE_SCANORDER); + DefInt(SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE); + DefInt(SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE); + DefInt(SQLITE_DBCONFIG_ENABLE_COMMENTS); DefInt(SQLITE_DBCONFIG_MAX); } _DefGroup; @@ -1630,6 +1632,9 @@ int sqlite3__wasm_db_config_ip(sqlite3 *pDb, int op, int arg1, int* pArg2){ case SQLITE_DBCONFIG_TRUSTED_SCHEMA: case SQLITE_DBCONFIG_STMT_SCANSTATUS: case SQLITE_DBCONFIG_REVERSE_SCANORDER: + case SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE: + case SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE: + case SQLITE_DBCONFIG_ENABLE_COMMENTS: return sqlite3_db_config(pDb, op, arg1, pArg2); default: return SQLITE_MISUSE; } diff --git a/ext/wasm/common/whwasmutil.js b/ext/wasm/common/whwasmutil.js index 25400d48e3..509d33b371 100644 --- a/ext/wasm/common/whwasmutil.js +++ b/ext/wasm/common/whwasmutil.js @@ -51,7 +51,7 @@ Its global-scope symbol is intended only to provide an easy way to make it available to 3rd-party scripts and "should" be deleted - after calling it. That symbols is _not_ used within the library. + after calling it. That symbol is _not_ used within the library. Forewarning: this API explicitly targets only browser environments. If a given non-browser environment has the @@ -69,7 +69,8 @@ - WASM-exported "indirect function table" access and manipulation. e.g. creating new WASM-side functions using JS functions, analog to Emscripten's addFunction() and - uninstallFunction() but slightly different. + uninstallFunction() but slightly different and with more useful + lifetime semantics. - Get/set specific heap memory values, analog to Emscripten's getValue() and setValue(). @@ -165,11 +166,11 @@ This code is developed and maintained in conjunction with the Jaccwabyt project: - https://fossil.wanderinghorse.net/r/jaccwabbyt + https://fossil.wanderinghorse.net/r/jaccwabyt More specifically: - https://fossil.wanderinghorse.net/r/jaccwabbyt/file/common/whwasmutil.js + https://fossil.wanderinghorse.net/r/jaccwabyt/file/common/whwasmutil.js */ globalThis.WhWasmUtilInstaller = function(target){ 'use strict'; @@ -1627,7 +1628,7 @@ globalThis.WhWasmUtilInstaller = function(target){ need a level of hand-written wrappers around them, depending on how they're used, in order to provide the client with JS strings. Alternately, clients will need to perform such conversions - on their own, e.g. using cstrtojs(). Or maybe we can find a way + on their own, e.g. using cstrToJs(). Or maybe we can find a way to perform such conversions here, via addition of an xWrap()-style function signature to the options argument. */ @@ -2055,7 +2056,7 @@ globalThis.WhWasmUtilInstaller = function(target){ if(1===argc) return xcvPart.get(typeName); else if(2===argc){ if(!adapter){ - delete xcvPart.get(typeName); + xcvPart.delete(typeName); return func; }else if(!(adapter instanceof Function)){ toss(modeName,"requires a function argument."); diff --git a/ext/wasm/config.make.in b/ext/wasm/config.make.in new file mode 100644 index 0000000000..f30baac3f1 --- /dev/null +++ b/ext/wasm/config.make.in @@ -0,0 +1,15 @@ +# Gets filtered by the configure script +bin.bash = @BIN_BASH@ +bin.emcc = @EMCC_WRAPPER@ +bin.wasm-strip = @BIN_WASM_STRIP@ +bin.wasm-opt = @BIN_WASM_OPT@ + +SHELL := $(bin.bash) + +# The following overrides can be uncommented to test various +# validation and if/else branches the makefile code: +# +#bin.bash := +#bin.emcc := +#bin.wasm-strip := +#bin.wasm-opt := diff --git a/ext/wasm/demo-worker1-promiser.c-pp.js b/ext/wasm/demo-worker1-promiser.c-pp.js index f6fc9568ae..0b8557b826 100644 --- a/ext/wasm/demo-worker1-promiser.c-pp.js +++ b/ext/wasm/demo-worker1-promiser.c-pp.js @@ -115,6 +115,7 @@ delete globalThis.sqlite3Worker1Promiser; "insert into t(a,b) values(1,2),(3,4),(5,6)" ].join(';'), resultRows: [], columnNames: [], + lastInsertRowId: true, countChanges: sqConfig.bigIntEnabled ? 64 : true }, function(ev){ ev = ev.result; @@ -122,7 +123,9 @@ delete globalThis.sqlite3Worker1Promiser; .assert(0===ev.columnNames.length) .assert(sqConfig.bigIntEnabled ? (3n===ev.changeCount) - : (3===ev.changeCount)); + : (3===ev.changeCount)) + .assert('bigint'===typeof ev.lastInsertRowId) + .assert(ev.lastInsertRowId>=3); }); await wtest('exec',{ diff --git a/ext/wasm/demo-worker1.js b/ext/wasm/demo-worker1.js index 60f5e8dec0..1a05cc7ac2 100644 --- a/ext/wasm/demo-worker1.js +++ b/ext/wasm/demo-worker1.js @@ -156,11 +156,14 @@ sql: ["create table t(a,b);", "insert into t(a,b) values(1,2),(3,4),(5,6)" ], + lastInsertRowId: true, resultRows: [], columnNames: [] }, function(ev){ ev = ev.result; T.assert(0===ev.resultRows.length) - .assert(0===ev.columnNames.length); + .assert(0===ev.columnNames.length) + .assert('bigint'===typeof ev.lastInsertRowId) + .assert(ev.lastInsertRowId>=3); }); runOneTest('exec',{ sql: 'select a a, b b from t order by a', diff --git a/ext/wasm/dist.make b/ext/wasm/dist.make index e820e066d4..60699ff5c0 100644 --- a/ext/wasm/dist.make +++ b/ext/wasm/dist.make @@ -97,6 +97,16 @@ STRIP_K2.js := $(sqlite3.js) $(sqlite3.mjs) \ # Note that we require $(bin.version-info) in order to figure out the # dist file's name, so cannot (without a recursive make) have the # target name equal to the archive name. +# +# 2025-01-15: Emsdk 4.0.0 introduces, in its generated code, a regex +# which contains the pattern /*. That, of course, confuses any C-style +# comment-stripper which is not specifically JS-aware and smart enough +# to know that it's in a regex or string literal. Because of that, +# comment-stripping is currently disabled, which means the builds will +# be significantly larger than before. +#apply_comment_stripper := false +apply_comment_stripper := true +# ^^^ shell command true or false dist: \ $(bin.stripccomments) $(bin.version-info) \ $(dist.build) $(STRIP_K1.js) $(STRIP_K2.js) \ @@ -109,8 +119,8 @@ dist: \ @cp -p README-dist.txt $(dist-dir.top)/README.txt @cp -p index-dist.html $(dist-dir.top)/index.html @cp -p $(dist.jswasm.extras) $(dist-dir.jswasm) - @$(foreach JS,$(STRIP_K1.js),$(call DIST_STRIP_COMMENTS,$(JS),-k)) - @$(foreach JS,$(STRIP_K2.js),$(call DIST_STRIP_COMMENTS,$(JS),-k -k)) + @if $(apply_comment_stripper); then $(foreach JS,$(STRIP_K1.js),$(call DIST_STRIP_COMMENTS,$(JS),-k)) fi + @if $(apply_comment_stripper); then $(foreach JS,$(STRIP_K2.js),$(call DIST_STRIP_COMMENTS,$(JS),-k -k)) fi @cp -p $(dist.common.extras) $(dist-dir.common) @set -e; \ vnum=$$($(bin.version-info) --download-version); \ diff --git a/ext/wasm/index-dist.html b/ext/wasm/index-dist.html index 7b778b0205..47bae3f7a9 100644 --- a/ext/wasm/index-dist.html +++ b/ext/wasm/index-dist.html @@ -97,8 +97,8 @@ wrapper is significantly easier to use, however.
      9. demo-worker1-promiser: a demo of the Promise-based wrapper of the Worker1 API.
      10. -
      11. demo-worker1-promiser-esm: - same as the previous demo except loads the promiser from an ESM module.
      12. +
  4. diff --git a/ext/wasm/mkwasmbuilds.c b/ext/wasm/mkwasmbuilds.c index 29b6cafae9..91c03b6d42 100644 --- a/ext/wasm/mkwasmbuilds.c +++ b/ext/wasm/mkwasmbuilds.c @@ -64,6 +64,75 @@ static void mk_prologue(void){ ps("# pre-post-jses.deps.* = a list of dependencies for the"); ps("# --[extern-][pre/post]-js files."); ps("pre-post-jses.deps.common := $(extern-pre-js.js) $(sqlite3-license-version.js)"); + + { + /* SQLITE.CALL.WASM-OPT = shell code to run $(1) (source wasm file + ** name) through $(bin.wasm-opt) */ + const char * zOptFlags = + /* + ** Flags for wasm-opt. It has many, many, MANY "passes" options + ** and the ones which appear here were selected solely on the + ** basis of trial and error. + ** + ** All wasm file size savings/costs mentioned below are based on + ** the vanilla build of sqlite3.wasm with -Oz (our shipping + ** configuration). Comments like "saves nothing" may not be + ** technically correct: "nothing" means "some neglible amount." + ** + ** Note that performance gains/losses are _not_ taken into + ** account here: only wasm file size. + */ + "--enable-bulk-memory-opt " /* required */ + "--all-features " /* required */ + "--post-emscripten " /* Saves roughly 12kb */ + "--strip-debug " /* We already wasm-strip, but in + ** case this environment has no + ** wasm-strip... */ + /* + ** The rest are trial-and-error. See wasm-opt --help and search + ** for "Optimization passes" to find the full list. + ** + ** With many flags this gets unusuably slow. + */ + /*"--converge " saves nothing for the options we're using */ + /*"--dce " saves nothing */ + /*"--directize " saves nothing */ + /*"--gsi " no: requires --closed-world flag, which does not + ** sound like something we want. */ + /*"--gufa --gufa-cast-all --gufa-optimizing " costs roughly 2kb */ + /*"--heap-store-optimization " saves nothing */ + /*"--heap2local " saves nothing */ + //"--inlining --inlining-optimizing " costs roughly 3kb */ + "--local-cse " /* saves roughly 1kb */ + /*"--once-reduction " saves nothing */ + /*"--remove-memory-init " presumably a performance tweak */ + /*"--remove-unused-names " saves nothing */ + /*"--safe-heap "*/ + /*"--vacuum " saves nothing */ + ; + ps("ifeq (,$(bin.wasm-opt))"); + ps("define SQLITE.CALL.WASM-OPT"); + ps("echo 'wasm-opt not available for $(1)'"); + ps("endef"); + ps("else"); + ps("define SQLITE.CALL.WASM-OPT"); + pf("echo -n 'Before wasm-opt:'; ls -l $(1);\\\n" + "\trm -f wasm-opt-tmp.wasm;\\\n" + /* It's very likely that the set of wasm-opt flags varies from + ** version to version, so we'll ignore any errors here. */ + "\tif $(bin.wasm-opt) $(1) -o wasm-opt-tmp.wasm \\\n" + "\t\t%s; then \\\n" + "\t\tmv wasm-opt-tmp.wasm $(1); \\\n" + "\t\techo -n 'After wasm-opt: '; \\\n" + "\t\tls -l $(1); \\\n" + "\telse \\\n" + "\t\techo 'WARNING: ignoring wasm-opt failure'; \\\n" + "\tfi\n", + zOptFlags + ); + ps("endef"); + ps("endif"); + } } /* @@ -75,23 +144,23 @@ static void mk_pre_post(const char *zName /* build name */, const char *zMode /* build mode */, const char *zCmppD /* optional -D flags for c-pp for the ** --pre/--post-js files. */){ - pf("%s# Begin --pre/--post flags for %s-%s\n", zBanner, zName, zMode); + pf("%s# Begin --pre/--post flags for %s-%s\n", zBanner, zNM); pf("c-pp.D.%s-%s := %s\n", zNM, zCmppD ? zCmppD : ""); pf("pre-post-%s-%s.flags ?=\n", zNM); /* --pre-js=... */ pf("pre-js.js.%s-%s := $(dir.tmp)/pre-js.%s-%s.js\n", zNM, zNM); - pf("$(pre-js.js.%s-%s): $(MAKEFILE)\n", zNM); + pf("$(pre-js.js.%s-%s): $(MAKEFILE_LIST)\n", zNM); #if 1 - pf("$(eval $(call C-PP.FILTER,$(pre-js.js.in),$(pre-js.js.%s-%s)," + pf("$(eval $(call SQLITE.CALL.C-PP.FILTER,$(pre-js.js.in),$(pre-js.js.%s-%s)," "$(c-pp.D.%s-%s)))\n", zNM, zNM); #else /* This part is needed if/when we re-enable the custom ** Module.instantiateModule() impl in api/pre-js.c-pp.js. */ pf("pre-js.js.%s-%s.intermediary := $(dir.tmp)/pre-js.%s-%s.intermediary.js\n", zNM, zNM); - pf("$(eval $(call C-PP.FILTER,$(pre-js.js.in),$(pre-js.js.%s-%s.intermediary)," + pf("$(eval $(call SQLITE.CALL.C-PP.FILTER,$(pre-js.js.in),$(pre-js.js.%s-%s.intermediary)," "$(c-pp.D.%s-%s) -Dcustom-Module.instantiateModule))\n", zNM, zNM); pf("$(pre-js.js.%s-%s): $(pre-js.js.%s-%s.intermediary)\n", zNM, zNM); pf("\tcp $(pre-js.js.%s-%s.intermediary) $@\n", zNM); @@ -107,12 +176,12 @@ static void mk_pre_post(const char *zName /* build name */, /* --post-js=... */ pf("post-js.js.%s-%s := $(dir.tmp)/post-js.%s-%s.js\n", zNM, zNM); - pf("$(eval $(call C-PP.FILTER,$(post-js.js.in)," + pf("$(eval $(call SQLITE.CALL.C-PP.FILTER,$(post-js.js.in)," "$(post-js.js.%s-%s),$(c-pp.D.%s-%s)))\n", zNM, zNM); /* --extern-post-js=... */ pf("extern-post-js.js.%s-%s := $(dir.tmp)/extern-post-js.%s-%s.js\n", zNM, zNM); - pf("$(eval $(call C-PP.FILTER,$(extern-post-js.js.in),$(extern-post-js.js.%s-%s)," + pf("$(eval $(call SQLITE.CALL.C-PP.FILTER,$(extern-post-js.js.in),$(extern-post-js.js.%s-%s)," "$(c-pp.D.%s-%s)))\n", zNM, zNM); /* Combine flags for use with emcc... */ @@ -130,7 +199,7 @@ static void mk_pre_post(const char *zName /* build name */, zNM, zNM, zNM); pf("pre-post-%s-%s.deps := $(pre-post-jses.%s-%s.deps) $(dir.tmp)/pre-js.%s-%s.js\n", zNM, zNM, zNM); - pf("# End --pre/--post flags for %s-%s%s", zName, zMode, zBanner); + pf("# End --pre/--post flags for %s-%s%s", zNM, zBanner); } /* @@ -149,29 +218,29 @@ static void mk_fiddle(){ pf("fiddle-module.js%s := %s/fiddle-module.js\n", zTail, zDir); pf("fiddle-module.wasm%s := " "$(subst .js,.wasm,$(fiddle-module.js%s))\n", zTail, zTail); - pf("$(fiddle-module.js%s):%s $(MAKEFILE) $(MAKEFILE.fiddle) " + pf("$(fiddle-module.js%s):%s $(MAKEFILE_LIST) $(MAKEFILE.fiddle) " "$(EXPORTED_FUNCTIONS.fiddle) " "$(fiddle.cses) $(pre-post-fiddle-module-vanilla.deps) " "$(SOAP.js)\n", zTail, (i ? " $(fiddle-module.js)" : "")); if( 1==i ){/*fiddle.debug*/ - pf(" @test -d \"$(dir $@)\" || mkdir -p \"$(dir $@)\"\n"); + pf("\t@test -d \"$(dir $@)\" || mkdir -p \"$(dir $@)\"\n"); } - pf(" $(emcc.bin) -o $@ $(fiddle.emcc-flags%s) " + pf("\t$(bin.emcc) -o $@ $(fiddle.emcc-flags%s) " "$(pre-post-fiddle-module-vanilla.flags) $(fiddle.cses)\n", zTail); - pf(" $(maybe-wasm-strip) $(fiddle-module.wasm%s)\n", zTail); - pf(" @cp -p $(SOAP.js) $(dir $@)\n"); + pf("\t$(maybe-wasm-strip) $(fiddle-module.wasm%s)\n", zTail); + pf("\t@cp -p $(SOAP.js) $(dir $@)\n"); if( 1==i ){/*fiddle.debug*/ - pf(" cp -p $(dir.fiddle)/index.html " + pf("\tcp -p $(dir.fiddle)/index.html " "$(dir.fiddle)/fiddle.js " "$(dir.fiddle)/fiddle-worker.js " "$(dir $@)\n"); } - pf(" @for i in %s/*.*js %s/*.html %s/*.wasm; do \\\n" - " test -f $${i} || continue; \\\n" - " gzip < $${i} > $${i}.gz; \\\n" - " done\n", zDir, zDir, zDir); + pf("\t@for i in %s/*.*js %s/*.html %s/*.wasm; do \\\n" + "\t\ttest -f $${i} || continue; \\\n" + "\t\tgzip < $${i} > $${i}.gz; \\\n" + "\tdone\n", zDir, zDir, zDir); if( 0==i ){ ps("fiddle: $(fiddle-module.js)"); }else{ @@ -193,6 +262,10 @@ static void mk_lib_mode(const char *zName /* build name */, const char *zJsOut /* name of generated sqlite3.js/.mjs */, const char *zCmppD /* extra -D flags for c-pp */, const char *zEmcc /* extra flags for emcc */){ + const char * zWasmOut = "$(basename $@).wasm" + /* The various targets named X.js or X.mjs (zJsOut) also generate + ** X.wasm, and we need that part of the name to perform some + ** post-processing after Emscripten generates X.wasm. */; assert( zName ); assert( zMode ); assert( zApiJsOut ); @@ -201,22 +274,29 @@ static void mk_lib_mode(const char *zName /* build name */, if( !zEmcc ) zEmcc = ""; pf("%s# Begin build [%s-%s]\n", zBanner, zNM); - pf("ifneq (1,$(MAKING_CLEAN))\n"); + pf("# zApiJsOut=%s\n# zJsOut=%s\n# zCmppD=%s\n", zApiJsOut, zJsOut, zCmppD); pf("$(info Setting up build [%s-%s]: %s)\n", zNM, zJsOut); mk_pre_post(zNM, zCmppD); pf("\nemcc.flags.%s.%s ?=\n", zNM); if( zEmcc[0] ){ pf("emcc.flags.%s.%s += %s\n", zNM, zEmcc); } - pf("$(eval $(call C-PP.FILTER, $(sqlite3-api.js.in), %s, %s))\n", + pf("$(eval $(call SQLITE.CALL.C-PP.FILTER, $(sqlite3-api.js.in), %s, %s))\n", zApiJsOut, zCmppD); /* target zJsOut */ - pf("%s: %s $(MAKEFILE) $(sqlite3-wasm.cfiles) $(EXPORTED_FUNCTIONS.api) " - "$(pre-post-%s-%s.deps)\n", + pf("%s: %s $(MAKEFILE_LIST) $(sqlite3-wasm.cfiles) $(EXPORTED_FUNCTIONS.api) " + "$(pre-post-%s-%s.deps) " + "$(sqlite3-api.ext.jses)" + /* ^^^ maintenance reminder: we set these as deps so that they + get copied into place early. That allows the developer to + reload the base-most test pages while the later-stage builds + are still compiling, which is especially helpful when running + builds with long build times (like -Oz). */ + "\n", zJsOut, zApiJsOut, zNM); pf("\t@echo \"Building $@ ...\"\n"); - pf("\t$(emcc.bin) -o $@ $(emcc_opt_full) $(emcc.flags) \\\n"); + pf("\t$(bin.emcc) -o $@ $(emcc_opt_full) $(emcc.flags) \\\n"); pf("\t\t$(emcc.jsflags) -sENVIRONMENT=$(emcc.environment.%s) \\\n", zMode); pf("\t\t$(pre-post-%s-%s.flags) \\\n", zNM); pf("\t\t$(emcc.flags.%s) $(emcc.flags.%s.%s) \\\n", zName, zNM); @@ -224,36 +304,59 @@ static void mk_lib_mode(const char *zName /* build name */, "\t\t$(cflags.%s) $(cflags.%s.%s) \\\n" "\t\t$(cflags.wasm_extra_init) $(sqlite3-wasm.cfiles)\n", zName, zNM); if( bIsEsm ){ - /* TODO? Replace this CALL with the corresponding makefile code. - ** OTOH, we also use this $(call) in the speedtest1-wasmfs build, - ** which is not part of the rules emitted by this program. */ - pf("\t@$(call SQLITE3.xJS.ESM-EXPORT-DEFAULT,1,%d)\n", + /* TODO? Replace this $(call) with the corresponding makefile + ** code. OTOH, we also use this $(call) in the speedtest1-wasmfs + ** build, which is not part of the rules emitted by this + ** program. */ + pf("\t@$(call SQLITE.CALL.xJS.ESM-EXPORT-DEFAULT,1,%d)\n", 0==strcmp("sqlite3-wasmfs", zName) ? 1 : 0); } - pf("\t@dotwasm=$(basename $@).wasm; \\\n" - "\tchmod -x $$dotwasm; \\\n" - "\t$(maybe-wasm-strip) $$dotwasm; \\\n"); + pf("\t@chmod -x %s; \\\n" + "\t\t$(maybe-wasm-strip) %s;\n", + zWasmOut, zWasmOut); + pf("\t@$(call SQLITE.CALL.WASM-OPT,%s)\n", zWasmOut); + pf("\t@sed -i -e '/^var _sqlite3.*createExportWrapper/d' %s || exit; \\\n" + /* ^^^^^^ reminder: Mac/BSD sed has no -i flag */ + "\t\techo 'Stripped out createExportWrapper() parts.'\n", + zJsOut) /* Our JS code installs bindings of each WASM export. The + generated Emscripten JS file does the same using its + own framework, but we don't use those results and can + speed up lib init, and reduce memory cost + considerably, by stripping them out. */; /* - ** The above $(emcc.bin) call will write zJsOut and will create a - ** like-named .wasm file. That .wasm file name gets hard-coded into - ** zJsOut so we need to, for some cases, patch zJsOut to use the - ** name sqlite3.wasm instead. Note that the resulting .wasm file is - ** identical for all builds for which zEmcc is empty. + ** The above $(bin.emcc) call will write zJsOut and will create a + ** like-named .wasm file (zWasmOut). That .wasm file name gets + ** hard-coded into zJsOut so we need to, for some cases, patch + ** zJsOut to use the name sqlite3.wasm instead. Note that the + ** resulting .wasm file is identical for all builds for which zEmcc + ** is empty. */ if( 0==strcmp("bundler-friendly", zMode) - || 0==strcmp("node", zMode) ) { - pf("\techo 'Patching $@ for %s.wasm...' \\\n", zName); - pf("\trm -f $$dotwasm; dotwasm=; \\\n" - "\tsed -i -e 's/%s-%s.wasm/%s.wasm/g' $@ || exit $$?; \\\n", + || 0==strcmp("node", zMode) ){ + pf("\t@echo 'Patching $@ for %s.wasm...'; \\\n", zName); + pf("\t\trm -f %s; \\\n", zWasmOut); + pf("\t\tsed -i -e 's/%s-%s.wasm/%s.wasm/g' $@ || exit;\n", + /* ^^^^^^ reminder: Mac/BSD sed has no -i flag */ zNM, zName); + pf("\t@ls -la $@\n"); + if( 0==strcmp("bundler-friendly", zMode) ){ + /* Avoid a 3rd occurance of the bug fixed by 65798c09a00662a3, + ** which was (in two cases) caused by makefile refactoring and + ** not recognized until after a release was made with the broken + ** sqlite3-bundler-friendly.mjs: */ + pf("\t@if grep -e '^ *importScripts(' $@; " + "then echo 'ERROR: bug fixed in 65798c09a00662a3 has re-appeared'; " + "exit 1; fi;\n"); + } + + }else{ + pf("\t@ls -la %s $@\n", zWasmOut); } - pf("\tls -la $$dotwasm $@\n"); if( 0!=strcmp("sqlite3-wasmfs", zName) ){ /* The sqlite3-wasmfs build is optional and needs to be invoked ** conditionally using info we don't have here. */ pf("all: %s\n", zJsOut); } - ps("endif\n# ^^^ !$(MAKING_CLEAN)"); pf("# End build [%s-%s]%s", zNM, zBanner); } diff --git a/ext/wasm/tester1.c-pp.js b/ext/wasm/tester1.c-pp.js index a21a1c330e..28d61de071 100644 --- a/ext/wasm/tester1.c-pp.js +++ b/ext/wasm/tester1.c-pp.js @@ -1241,6 +1241,12 @@ globalThis.sqlite3InitModule = sqlite3InitModule; }finally{ wasm.pstack.restore(stack); } + + capi.sqlite3_db_config(this.db, capi.SQLITE_DBCONFIG_ENABLE_COMMENTS, 0, null); + T.mustThrow(()=>this.db.exec("select 1 /* with comments */"), "SQL comments are disallowed"); + capi.sqlite3_db_config(this.db, capi.SQLITE_DBCONFIG_ENABLE_COMMENTS, 1, null); + this.db.exec("select 1 /* with comments */"); + /* SQLITE_DBCONFIG_ENABLE_ATTACH_... are in the ATTACH-specific tests */ }) //////////////////////////////////////////////////////////////////// @@ -1999,7 +2005,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule; }/*window UDFs*/) //////////////////////////////////////////////////////////////////// - .t("ATTACH", function(){ + .t("ATTACH", function(sqlite3){ const db = this.db; const resultRows = []; db.exec({ @@ -2078,7 +2084,36 @@ globalThis.sqlite3InitModule = sqlite3InitModule; db.exec("detach foo"); T.mustThrow(()=>db.exec("select * from foo.bar"), "Because foo is no longer attached."); - }) + + /* SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE/WRITE... */ + const db2 = new sqlite3.oo1.DB(); + try{ + capi.sqlite3_db_config(db2, capi.SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE, 0, null); + T.mustThrow(()=>db2.exec("attach 'attached.db' as foo"), + "Cannot create a new db via ATTACH"); + capi.sqlite3_db_config(db2, capi.SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE, 1, null); + db2.exec([ + "attach 'attached.db' as foo;", + "create table foo.t(a);", + "insert into foo.t(a) values(1);", + "detach foo;" + ]); + capi.sqlite3_db_config(db2, capi.SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE, 0, null); + db2.exec("attach 'attached.db' as foo"); + T.mustThrow(()=>db2.exec("insert into foo.t(a) values(2)"), + "ATTACH_WRITE is false"); + capi.sqlite3_db_config(db2, capi.SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE, 1, null); + db2.exec([ + "detach foo;", + "attach 'attached.db' as foo;", + "insert into foo.t(a) values(2);", + "drop table foo.t;", + "detach foo" + ]); + }finally{ + db2.close(); + } + })/*ATTACH tests*/ //////////////////////////////////////////////////////////////////// .t("Read-only", function(sqlite3){ @@ -3402,6 +3437,71 @@ globalThis.sqlite3InitModule = sqlite3InitModule; } } }) + .t({ + /* https://github.com/sqlite/sqlite-wasm/issues/92 */ + name: 'sqlite3_set_auxdata() binding signature', + test: function(sqlite3){ + const db = new sqlite3.oo1.DB(); + const stack = wasm.pstack.pointer; + const pAux = wasm.pstack.alloc(4); + let pAuxDestructed = 0; + const pAuxDtor = wasm.installFunction('v(p)', function(ptr){ + //log("freeing auxdata"); + ++pAuxDestructed; + }); + let pAuxDtorDestructed = false; + db.onclose = { + after: ()=>{ + pAuxDtorDestructed = true; + wasm.uninstallFunction(pAuxDtor); + } + }; + let nAuxSet = 0 /* how many times we set aux data */; + let nAuxReused = 0 /* how many times we reused aux data */; + try{ + db.createFunction("auxtest",{ + xFunc: function(pCx, x, y){ + T.assert(wasm.isPtr(pCx)); + const localAux = capi.sqlite3_get_auxdata(pCx, 0); + if( !localAux ){ + //log("setting auxdata"); + /** + We do not currently an automated way to clean up + auxdata finalizer functions (the 4th argument to + sqlite3_set_auxdata()) which get automatically + converted from JS to WASM. Because of that, enabling + automated conversions here would lead to leaks more + often than not. Instead, follow the pattern show in + this function: use wasm.installFunction() to create + the function, then pass the resulting function + pointer this function, and cleanup (at some point) + using wasm.uninstallFunction(). + */ + ++nAuxSet; + capi.sqlite3_set_auxdata(pCx, 0, pAux, pAuxDtor); + }else{ + //log("reusing auxdata",localAux); + T.assert(pAux===localAux); + ++nAuxReused; + } + return x; + } + }); + db.exec([ + "create table t(a);", + "insert into t(a) values(1),(2),(3);", + "select auxtest(1,a), auxtest(1,a) from t order by a" + ]); + }finally{ + db.close(); + wasm.pstack.restore(stack); + } + T.assert(nAuxSet>0).assert(nAuxReused>0) + .assert(6===nAuxReused+nAuxSet); + T.assert(pAuxDestructed>0); + T.assert(pAuxDtorDestructed); + } + }) ;/*end of Bug Reports group*/; //////////////////////////////////////////////////////////////////////// diff --git a/ext/wasm/wasmfs.make b/ext/wasm/wasmfs.make index 9de5574f26..2c6fa35bdd 100644 --- a/ext/wasm/wasmfs.make +++ b/ext/wasm/wasmfs.make @@ -101,7 +101,7 @@ $(speedtest1-wasmfs.mjs): $(speedtest1.cfiles) $(sqlite3-wasmfs.js) \ $(emcc.flags.sqlite3-wasmfs) \ $(emcc.flags.speedtest1-wasmfs) \ -o $@ $(speedtest1.cfiles) -lm - @$(call SQLITE3.xJS.ESM-EXPORT-DEFAULT,1,1) + @$(call SQLITE.CALL.xJS.ESM-EXPORT-DEFAULT,1,1) $(maybe-wasm-strip) $(speedtest1-wasmfs.wasm) chmod -x $(speedtest1-wasmfs.wasm) ls -la $@ $(speedtest1-wasmfs.wasm) diff --git a/main.mk b/main.mk index f98850731e..9eec5495ee 100644 --- a/main.mk +++ b/main.mk @@ -163,9 +163,11 @@ LDFLAGS.rpath ?= -Wl,-rpath -Wl,$(prefix)/lib LDFLAGS.pthread ?= -lpthread LDFLAGS.dlopen ?= -ldl LDFLAGS.shlib ?= -shared +LDFLAGS.rt ?= # nanosleep on some platforms LDFLAGS.icu ?= # -licui18n -licuuc -licudata CFLAGS.icu ?= LDFLAGS.libsqlite3.soname ?= # see https://sqlite.org/src/forumpost/5a3b44f510df8ded +LDFLAGS.libsqlite3.os-specific ?= # see https://sqlite.org/forum/forumpost/9dfd5b8fd525a5d7 # libreadline (or a workalike): # To activate readline in the shell: SHELL_OPT = -DHAVE_READLINE=1 LDFLAGS.readline ?= -lreadline # these vary across platforms @@ -412,7 +414,7 @@ LDFLAGS.libsqlite3 = \ $(LDFLAGS.rpath) $(LDFLAGS.pthread) \ $(LDFLAGS.math) $(LDFLAGS.dlopen) \ $(LDFLAGS.zlib) $(LDFLAGS.icu) \ - $(LDFLAGS.configure) + $(LDFLAGS.rt) $(LDFLAGS.configure) # # $(install-dir.XYZ) = dirs for installation. @@ -434,7 +436,8 @@ install-dir.all = $(install-dir.bin) $(install-dir.include) \ $(install-dir.lib) $(install-dir.man1) \ $(install-dir.pkgconfig) $(install-dir.all): - $(INSTALL) -d "$@" + if [ ! -d "$@" ]; then $(INSTALL) -d "$@"; fi +# ^^^^ on some platforms, install -d fails if the target already exists. # # After jimsh is compiled, we run some sanity checks to ensure that @@ -1043,9 +1046,9 @@ T.link.tcl = $(T.tcl.env.source); $(T.link) rm -rf tsrc mkdir tsrc cp -f $(SRC) tsrc - rm tsrc/sqlite.h.in tsrc/parse.y + rm -f tsrc/sqlite.h.in tsrc/parse.y $(B.tclsh) $(TOP)/tool/vdbe-compress.tcl $(OPTS) vdbe.new - mv vdbe.new tsrc/vdbe.c + mv -f vdbe.new tsrc/vdbe.c cp fts5.c fts5.h tsrc touch .target_source @@ -1066,7 +1069,7 @@ mksourceid$(B.exe): $(MAKE_SANITY_CHECK) $(TOP)/tool/mksourceid.c sqlite3.h: $(MAKE_SANITY_CHECK) $(TOP)/src/sqlite.h.in \ $(TOP)/manifest mksourceid$(B.exe) \ $(TOP)/VERSION $(B.tclsh) - $(B.tclsh) $(TOP)/tool/mksqlite3h.tcl $(TOP) >sqlite3.h + $(B.tclsh) $(TOP)/tool/mksqlite3h.tcl $(TOP) -o sqlite3.h sqlite3.c: .target_source sqlite3.h $(TOP)/tool/mksqlite3c.tcl src-verify$(B.exe) \ $(B.tclsh) @@ -1075,7 +1078,7 @@ sqlite3.c: .target_source sqlite3.h $(TOP)/tool/mksqlite3c.tcl src-verify$(B.exe cp $(TOP)/ext/session/sqlite3session.h . sqlite3r.h: sqlite3.h $(B.tclsh) - $(B.tclsh) $(TOP)/tool/mksqlite3h.tcl $(TOP) --enable-recover >sqlite3r.h + $(B.tclsh) $(TOP)/tool/mksqlite3h.tcl $(TOP) --enable-recover -o sqlite3r.h sqlite3r.c: sqlite3.c sqlite3r.h $(B.tclsh) cp $(TOP)/ext/recover/sqlite3recover.c tsrc/ @@ -1411,7 +1414,7 @@ all: lib # $(libsqlite3.SO): $(LIBOBJ) $(T.link.shared) -o $@ $(LIBOBJ) $(LDFLAGS.libsqlite3) \ - $(LDFLAGS.libsqlite3.soname) + $(LDFLAGS.libsqlite3.os-specific) $(LDFLAGS.libsqlite3.soname) $(libsqlite3.SO)-1: $(libsqlite3.SO) $(libsqlite3.SO)-0 $(libsqlite3.SO)-: so: $(libsqlite3.SO)-$(ENABLE_SHARED) @@ -1429,6 +1432,9 @@ all: so # unnecessary unless we want to set SONAME to libsqlite3.so.3, which # is also unnecessary. # +# N.B. different transformations are applied on systems where $(T.dll) +# is ".dylib" and none of the following docs apply on such systems. +# # The link named libsqlite3.so.0 is provided in an attempt to reduce # downstream disruption when performing upgrades from pre-3.48 to a # version 3.48 or higher. That name is considered a legacy remnant @@ -1465,8 +1471,16 @@ all: so # install-so-1: $(install-dir.lib) $(libsqlite3.SO) $(INSTALL) $(libsqlite3.SO) "$(install-dir.lib)" - @echo "Setting up $(libsqlite3.SO) symlinks..."; \ - cd "$(install-dir.lib)" || exit $$?; \ + @echo "Setting up $(libsqlite3.SO) version symlinks..."; \ + cd "$(install-dir.lib)" || exit $$?; \ + if [ x.dylib = x$(T.dll) ]; then \ + rm -f libsqlite3.0$(T.dll) libsqlite3.$(PACKAGE_VERSION)$(T.dll) || exit $$?; \ + dllname=libsqlite3.$(PACKAGE_VERSION)$(T.dll); \ + mv $(libsqlite3.SO) $$dllname || exit $$?; \ + ln -s $$dllname $(libsqlite3.SO) || exit $$?; \ + ln -s $$dllname libsqlite3.0$(T.dll) || exit $$?; \ + ls -la $$dllname $(libsqlite3.SO) libsqlite3.0$(T.dll); \ + else \ rm -f $(libsqlite3.SO).0 $(libsqlite3.SO).$(PACKAGE_VERSION) || exit $$?; \ mv $(libsqlite3.SO) $(libsqlite3.SO).$(PACKAGE_VERSION) || exit $$?; \ ln -s $(libsqlite3.SO).$(PACKAGE_VERSION) $(libsqlite3.SO) || exit $$?; \ @@ -1482,7 +1496,8 @@ install-so-1: $(install-dir.lib) $(libsqlite3.SO) rm -f libsqlite3.la $(libsqlite3.SO).0.8.6 || exit $$?; \ ln -s $(libsqlite3.SO).$(PACKAGE_VERSION) $(libsqlite3.SO).0.8.6 || exit $$?; \ ls -la $(libsqlite3.SO).0.8.6; \ - fi + fi; \ + fi install-so-0 install-so-: install-so: install-so-$(ENABLE_SHARED) install: install-so @@ -1507,7 +1522,7 @@ install: install-headers # libtclsqlite3... # pkgIndex.tcl: - echo 'package ifneeded sqlite3 $(PACKAGE_VERSION) [list load [file join $$dir libtclsqlite3[info sharedlibextension]] sqlite3]' > $@ + echo 'package ifneeded sqlite3 $(PACKAGE_VERSION) [list load [file join $$dir libtclsqlite3[info sharedlibextension]] Sqlite3]' > $@ pkgIndex.tcl-1: pkgIndex.tcl pkgIndex.tcl-0 pkgIndex.tcl-: tcl: pkgIndex.tcl-$(HAVE_TCL) @@ -1572,7 +1587,14 @@ tclextension-uninstall: # by $TCLSH_CMD, including prior versions. # tclextension-list: - $(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --info + @ $(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --info + +# Verify that the SQLite TCL extension that is loaded by default +# in $(TCLSH_CMD) is the same as the version of SQLite for the +# current source tree +# +tclextension-verify: sqlite3.h + @ $(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --version-check # # FTS5 things @@ -1929,6 +1951,21 @@ amalgamation-tarball: sqlite3.c sqlite3rc.h snapshot-tarball: sqlite3.c sqlite3rc.h TOP=$(TOP) sh $(TOP)/tool/mkautoconfamal.sh --snapshot +# Build a ZIP archive snapshot of the latest check-in. +# +sqlite-src.zip: $(TOP)/tool/mksrczip.tcl + $(TCLSH_CMD) $(TOP)/tool/mksrczip.tcl + +# Build a ZIP archive of the amaglamation +# +sqlite-amalgamation.zip: $(TOP)/tool/mkamalzip.tcl sqlite3.c sqlite3.h shell.c sqlite3ext.h + $(TCLSH_CMD) $(TOP)/tool/mkamalzip.tcl + +# Build all the source code deliverables +# +src-archives: sqlite-amalgamation.zip amalgamation-tarball sqlite-src.zip + ls -ltr *.zip *.tar.gz | tail -3 + # Build a ZIP archive containing various command-line tools. # tool-zip: testfixture$(T.exe) sqlite3$(T.exe) sqldiff$(T.exe) \ @@ -1995,7 +2032,7 @@ sqlite3d$(T.exe): shell.c $(LIBOBJS0) $(LDFLAGS.libsqlite3) $(LDFLAGS.readline) install-shell-0: sqlite3$(T.exe) $(install-dir.bin) - $(INSTALL) -s sqlite3$(T.exe) "$(install-dir.bin)" + $(INSTALL) sqlite3$(T.exe) "$(install-dir.bin)" install-shell-1: install: install-shell-$(HAVE_WASI_SDK) @@ -2009,7 +2046,7 @@ sqldiff$(T.exe): $(sqldiff.$(LINK_TOOLS_DYNAMICALLY).deps) $(sqldiff.$(LINK_TOOLS_DYNAMICALLY).rules) install-diff: sqldiff$(T.exe) $(install-dir.bin) - $(INSTALL) -s sqldiff$(T.exe) "$(install-dir.bin)" + $(INSTALL) sqldiff$(T.exe) "$(install-dir.bin)" #install: install-diff dbhash$(T.exe): $(TOP)/tool/dbhash.c sqlite3.o sqlite3.h @@ -2188,7 +2225,7 @@ SHELL_DEP = \ $(TOP)/src/test_windirent.h shell.c: $(SHELL_DEP) $(TOP)/tool/mkshellc.tcl $(B.tclsh) - $(B.tclsh) $(TOP)/tool/mkshellc.tcl >shell.c + $(B.tclsh) $(TOP)/tool/mkshellc.tcl shell.c # # Rules to build the extension objects. @@ -2296,7 +2333,7 @@ tidy: tidy-. rm -f lemon$(B.exe) sqlite*.tar.gz rm -f mkkeywordhash$(B.exe) mksourceid$(B.exe) rm -f parse.* fts5parse.* - rm -f $(libsqlite3.SO) $(libsqlite3.LIB) $(libtclsqlite3.SO) + rm -f $(libsqlite3.SO) $(libsqlite3.LIB) $(libtclsqlite3.SO) libsqlite3$(T.dll).a rm -f tclsqlite3$(T.exe) $(TESTPROGS) rm -f LogEst$(T.exe) fts3view$(T.exe) rollback-test$(T.exe) showdb$(T.exe) rm -f showjournal$(T.exe) showstat4$(T.exe) showwal$(T.exe) speedtest1$(T.exe) diff --git a/manifest b/manifest index 32d9f7e9f4..75514bd2c6 100644 --- a/manifest +++ b/manifest @@ -1,31 +1,30 @@ -C Experimental\schange\sto\sallow\sclients\sto\sblock\swhen\staking\sa\sSHARED\slock\sto\sconnect\sto\sa\swal\smode\sdatabase. -D 2025-02-10T20:46:14.679 +C Merge\slatest\schanges\sfrom\strunk\sinto\sthis\sbranch. +D 2025-02-11T17:10:46.191 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md e108e1e69ae8e8a59e93c455654b8ac9356a11720d3345df2a4743e9590fb20d -F Makefile.in ad349acf91b3569033439fe498fa197aa530cafaa01362eb7daad2f84e43d265 +F Makefile.in aa869faf7ca086f35c9b3e974fddc7fd65ed2dc45a246b1a94e6f9fdc99b0ed5 F Makefile.linux-generic bd3e3cacd369821a6241d4ea1967395c962dfe3057e38cb0a435cee0e8b789d0 -F Makefile.msc 1fc95c6cb5677efe36b804a4b9afcb19e2c42caa3deef6e7455bb317a140e132 +F Makefile.msc b12d09789c2cd6e342ca13e9426e443400e31a766b2b93e1030b5955386cb9ed F README.md c3c0f19532ce28f6297a71870f3c7b424729f0e6d9ab889616d3587dd2332159 -F VERSION 8dc0c3df15fd5ff0622f88fc483533fce990b1cbb2f5fb9fdfb4dbd71eef2889 +F VERSION 001dea55eb8304ec9130b6b44a32d3fc349f279d45a7e224fc0730c3cb8e2372 F art/icon-243x273.gif 9750b734f82fdb3dc43127753d5e6fbf3b62c9f4e136c2fbf573b2f57ea87af5 F art/icon-80x90.gif 65509ce3e5f86a9cd64fe7fca2d23954199f31fe44c1e09e208c80fb83d87031 F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2 F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90 F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2 F art/sqlite370.svg 40b7e2fe8aac3add5d56dd86ab8d427a4eca5bcb3fe4f8946cb3794e1821d531 -F auto.def 63dfbbc58b041d1c5c516f31a02679cce8d79123c89ad87fd2783f4ef26dedbb -F autoconf/INSTALL 83e4a25da9fd053c7b3665eaaaf7919707915903 -F autoconf/Makefile.am adedc1324b6a87fdd1265ddd336d2fb7d4f36a0e77b86ea553ae7cc4ea239347 +F auto.def ccf74471ec89edfd5bf942fd6d60fc8ce09f7aa7527d668a1ac44d1bbbcb412d F autoconf/Makefile.fallback 22fe523eb36dfce31e0f6349f782eb084e86a5620b2b0b4f84a2d6133f53f5ac -F autoconf/Makefile.msc ffff61fe851443015ddb6600ab69a9df503cfec25459b336be7ba8c9a9e473f8 -F autoconf/README.first 6c4f34fe115ff55d4e8dbfa3cecf04a0188292f7 -F autoconf/README.txt 5e946ffb6fbdbb114c81e1bdc862df27fce8beab557d7b0421820b0fe8fc048f -F autoconf/configure.ac ec7fa914c5e74ff212fe879f9bb6918e1234497e05facfb641f30c4d5893b277 +F autoconf/Makefile.in 7bd73a4c8cd89025cbc92b4f887c6fd1b8cd8ecbe62c4ac1f36ac84d04043479 w autoconf/Makefile.am +F autoconf/Makefile.msc 0a071367537dc395285a5d624ac4f99f3a387b27cc5e89752423c0499e15aec4 +F autoconf/README.first f1d3876e9a7852c22f275a6f06814e64934cecbc0b5b9617d64849094c1fd136 +F autoconf/README.txt 7f01dc3915e2d68f329011073662369e62a0938a2c69398807823c57591cb288 +F autoconf/auto.def f468a32e6f57c52390e0fe2466974d0afaa1b0fc1d51cbacb4cb3950bd089f67 F autoconf/tea/Makefile.in ba0556fee8da09c066bad85a4457904e46ee2c2eabaa309c0e83a78f2f151a8e F autoconf/tea/README.txt 61e62e519579e4a112791354d6d440f8b51ea6db3b0bab58d59f29df42d2dfe3 F autoconf/tea/aclocal.m4 52c47aac44ce0ddb1f918b6993e8beb8eee88f43 -F autoconf/tea/configure.ac ff2d745f88e493080810b67958d88b4f7a7d79f19e2ee8e7f72ffd6fc04eabc7 +F autoconf/tea/configure.ac 830cb2af5a3888312d0fd88402c2509d551d60e4c03f0481006a07c78313ef12 F autoconf/tea/doc/sqlite3.n e1fe45d4f5286ee3d0ccc877aca2a0def488e9bb F autoconf/tea/license.terms 13bd403c9610fd2b76ece0ab50c4c5eda933d523 F autoconf/tea/pkgIndex.tcl.in 55aec3c6d7e9a1de9b8d2fdc9c27fd055da3ac3a51b572195e2ae7300bcfd3a2 @@ -38,7 +37,7 @@ F autoconf/tea/win/rules.vc 94a18c3e453535459b4a643983acca52fb8756e79055bd2ad4b0 F autoconf/tea/win/targets.vc 96a25a1fa6e9e9cfb348fd3760a5395b4ce8acafc8ed10f0412937ec200d5dbd F autosetup/LICENSE 41a26aebdd2cd185d1e2b210f71b7ce234496979f6b35aef2cbf6b80cbed4ce4 F autosetup/README.autosetup a78ff8c4a3d2636a4268736672a74bf14a82f42687fcf0631a70c516075c031e -F autosetup/README.md 1a02f5a94fd460eb7ffc8dea5d6f1657e38ddf8ffa2d6c5dce9a630b97021a69 +F autosetup/README.md b306314e8a87ccf873cb5b2a360c4a27bbf841df5b76f3acbd65322cff165476 F autosetup/autosetup df8b53928b1fe3c67db5bc77c8e1eb8160c1b6a26c370e9a06c68748f803b7e4 x F autosetup/autosetup-config.guess dfa101c5e8220e864d5e9c72a85e87110df60260d36cb951ad0a85d6d9eaa463 x F autosetup/autosetup-config.sub a38fb074d0dece01cf919e9fb534a26011608aa8fa606490864295328526cd73 x @@ -48,19 +47,21 @@ F autosetup/cc-db.tcl 6e0ed90146197a5a05b245e649975c07c548e30926b218ca3e1d4dc034 F autosetup/cc-lib.tcl 493c5935b5dd3bf9bd4eca89b07c8b1b1a9356d61783035144e21795facf7360 F autosetup/cc-shared.tcl 4f024e94a47f427ba61de1739f6381ef0080210f9fae89112d5c1de1e5460d78 F autosetup/cc.tcl c0fcc50ca91deff8741e449ddad05bcd08268bc31177e613a6343bbd1fd3e45f -F autosetup/jimsh0.c d40e381ea4526a067590e7b91bd4b2efa6d4980d286f908054c647b3df4aee14 +F autosetup/jimsh0.c 6573f6bc6ff204de0139692648d7037ca0b6c067bac83a7b4e087f20a86866a4 F autosetup/pkg-config.tcl 4e635bf39022ff65e0d5434339dd41503ea48fc53822c9c5bde88b02d3d952ba -F autosetup/proj.tcl 2e817159b997077cb79bd871f6255276b787558f386dfc0830b0f825f6a53767 +F autosetup/proj.tcl cef1e0aa0f2dee2042af66f28c97a9445f84d55d858ba9db4f6116846a1a325f +F autosetup/sqlite-config.tcl 62fd6e8782ecfb9f8e54cf2860e590ce9d8692218f03cfa8b25e87ff6b1d0b9b F autosetup/system.tcl 51d4be76cd9a9074704b584e5c9cbba616202c8468cf9ba8a4f8294a7ab1dba9 F configure 9a00b21dfd13757bbfb8d89b30660a89ec1f8f3a79402b8f9f9b6fc475c3303a x F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad F doc/F2FS.txt c1d4a0ae9711cfe0e1d8b019d154f1c29e0d3abfe820787ba1e9ed7691160fcd -F doc/compile-for-unix.md 7d6a5770611ea0643de456b385581923dac7c0a7c3758825dda810d12fc3e5b2 -F doc/compile-for-windows.md 17e1491897a117ff0247531a61671b26d487bc1dad25c3894c04ad4fca936a7f +F doc/compile-for-unix.md c9dce1ddd4bf0d25efccc5c63eb047e78c01ce06a6ff29c73e0a8af4a0f4adbc +F doc/compile-for-windows.md 5141661e783c9ca9e3fd30e813345898712f5c311d71316f183db87038fa28a6 F doc/json-enhancements.md e356fc834781f1f1aa22ee300027a270b2c960122468499bf347bb123ce1ea4f F doc/jsonb.md 5fab4b8613aa9153fbeb6259297bd4697988af8b3d23900deba588fa7841456b F doc/lemon.html 8b266ff711d2ec7f867c3dca37634963f48a630329908cc282beebfa8c708706 F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710 +F doc/tcl-extension-testing.md 864875c3b672db79e7d42348dd726f9a4fbd852b1d8e5efcf09fe3d1ff6bf2a2 F doc/testrunner.md 15583cf8c7d8a1c3378fd5d4319ca769a14c4d950a5df9b015d01d5be290dc69 F doc/trusted-schema.md 33625008620e879c7bcfbbfa079587612c434fa094d338b08242288d358c3e8a F doc/vdbesort-memory.md 4da2639c14cd24a31e0af694b1a8dd37eaf277aff3867e9a8cc14046bc49df56 @@ -77,16 +78,16 @@ F ext/fts3/README.content b9078d0843a094d86af0d48dffbff13c906702b4c3558012e67b9c F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a F ext/fts3/README.tokenizers b92bdeb8b46503f0dd301d364efc5ef59ef9fa8e2758b8e742f39fa93a2e422d F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d -F ext/fts3/fts3.c 9f8ce82bbf4ec0636e6170e58f17b04817fa4c39b2d5126ac06f005d485f6d5e +F ext/fts3/fts3.c b840ee915a6fb36571e3fe3c096e8a481a4a9cd8a35199a1b976b132b9f84ad3 F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe -F ext/fts3/fts3Int.h 968f7d7cae541a6926146e9fd3fb2b2ccbd3845b7890a8ed03de0c06ac776682 +F ext/fts3/fts3Int.h 2fe7c76dfd7d46dff964d17d3f4c53bca2116cf5d6252552ebbc22e38afdf4e0 F ext/fts3/fts3_aux.c 7eab82a9cf0830f6551ba3abfdbe73ed39e322a4d3940ee82fbf723674ecd9f3 F ext/fts3/fts3_expr.c 365849a2a1185e19028a9db2d9f1ea63efe909a3a6aca7ec86fc26a13a60bd58 F ext/fts3/fts3_hash.c 8b6e31bfb0844c27dc6092c2620bdb1fca17ed613072db057d96952c6bdb48b7 F ext/fts3/fts3_hash.h 39cf6874dc239d6b4e30479b1975fe5b22a3caaf F ext/fts3/fts3_icu.c 305ce7fb6036484085b5556a9c8e62acdc7763f0f4cdf5fd538212a9f3720116 F ext/fts3/fts3_porter.c e19807ce0ae31c1c6e9898e89ecc93183d7ec224ea101af039722a4f49e5f2b8 -F ext/fts3/fts3_snippet.c c38117a2e4dcc9485a170a57a6134423955247b230fef7073c46fa9c51239540 +F ext/fts3/fts3_snippet.c 7a3d5e2cefbb1cb51fb9c65458670cc269647ede18e1ffd57b513f9b4ec10c3e F ext/fts3/fts3_term.c 6a96027ad364001432545fe43322b6af04ed28bb5619ec51af1f59d0710d6d69 F ext/fts3/fts3_test.c 7a9cb3d61774134211bf4bfdf1adcb581a1a0377f2d050a121ae7ab44baef0e3 F ext/fts3/fts3_tokenize_vtab.c 7fd9ef364f257b97218b9c331f2378e307375c592f70fd541f714e747d944962 @@ -111,9 +112,9 @@ F ext/fts5/fts5_buffer.c 0eec58bff585f1a44ea9147eae5da2447292080ea435957f7488c70 F ext/fts5/fts5_config.c e7d8dd062b44a66cd77e5a0f74f23a2354cd1f3f8575afb967b2773c3384f7f8 F ext/fts5/fts5_expr.c 69b8d976058512c07dfe86e229521b7a871768157bd1607cedf1a5038dfd72c9 F ext/fts5/fts5_hash.c adda4272be401566a6e0ba1acbe70ee5cb97fce944bc2e04dc707152a0ec91b1 -F ext/fts5/fts5_index.c cef6791bd9f9db4305494292d6dd5d24a7379aabf370a4d6b559e16b740fa88e -F ext/fts5/fts5_main.c 72527efa1d634054b93a21eafe854763cbc5c270e8a4ab99bbb589557b818482 -F ext/fts5/fts5_storage.c 337b05e4c66fc822d031e264d65bde807ec2fab08665ca2cc8aaf9c5fa06792c +F ext/fts5/fts5_index.c f1eec0931548b529ddd7ebd274eaef37de7461fe2b0ebdc9818f37324bdf9494 +F ext/fts5/fts5_main.c 9a1daef7247f9b8a50b4159323e340efa6b0e4bea4fcd83580480f94d4f2c888 +F ext/fts5/fts5_storage.c 1ad05dab4830a4e2eaf2900bb143477f93bc17437093582f36f4b818809e88d8 F ext/fts5/fts5_tcl.c 7fb5a3d3404099075aaa2457307cb459bbc257c0de3dbd52b1e80a5b503e0329 F ext/fts5/fts5_test_mi.c 08c11ec968148d4cb4119d96d819f8c1f329812c568bac3684f5464be177d3ee F ext/fts5/fts5_test_tok.c 3cb0a9b508b30d17ef025ccddd26ae3dc8ddffbe76c057616e59a9aa85d36f3b @@ -146,7 +147,7 @@ F ext/fts5/test/fts5bigpl.test 8f09858aab866c33593560e6480b2b6975ae7ff29ca32ad7b F ext/fts5/test/fts5bigtok.test 541119e616c637caea925a8c028c37c2c29e94383e00aa2f9198d530724b6e36 F ext/fts5/test/fts5blob.test 9644a5f917306690e08c5f89a470a3f2489376eaa52026eeca3209d149d6af74 F ext/fts5/test/fts5cat.test bf67dd335f964482ee658287521b81e2b88697b45eb7f73933e15f198ed447cb -F ext/fts5/test/fts5circref.test f880dfd0d99f6fb73b88ccacb0927d18e833672fd906cc47d6b4e529419eaa62 +F ext/fts5/test/fts5circref.test 0918c69440a73fff429bc9797b07086fc74d018eb3abb1cf9738980390bb2713 F ext/fts5/test/fts5colset.test 544f4998cdbfe06a3123887fc0221612e8aa8192cdaff152872f1aadb10e6897 F ext/fts5/test/fts5columnsize.test 0af91d63985afdf663455d4b572b935238380140d74079eac362760866d3297b F ext/fts5/test/fts5config.test 017daf10d2642496e97402baa0134de8b5b46b9c37e53c229cd9ab711d21522c @@ -160,9 +161,9 @@ F ext/fts5/test/fts5contentless4.test ec34dc69ef474ca9997dae6d91e072906e0e9a5a4b F ext/fts5/test/fts5contentless5.test 38cd0392c730dc7090c550321ce3c24ba4c392bc97308b51a4180e9959dca7b5 F ext/fts5/test/fts5corrupt.test 6485f721b88ba355ca5d701e7ee87a4efa3ea578d8e6adb26f51ef956c8328bd F ext/fts5/test/fts5corrupt2.test 335911e3f68b9625d850325f9e29a128db3f4276a8c9d4e32134580da8f924c4 -F ext/fts5/test/fts5corrupt3.test 4fc3bf129f1616bea00884a23fd9d7b0e46d01791d2b57fe8d68ac36e8d3ff7c +F ext/fts5/test/fts5corrupt3.test 3420ad30bf9e9bbdbd43b3224c582431744899530a65b11b60ddacdf14200e19 F ext/fts5/test/fts5corrupt4.test dc08d19f5b8943e95a7778a7d8da592042504faf18dd93f68f7d7a0d7d7dd733 -F ext/fts5/test/fts5corrupt5.test 11b47126f5772cc37b67e3e8b2ed05895c4d07c05338bc07e4eea225bfe32c76 +F ext/fts5/test/fts5corrupt5.test bcf0801b0c991eadae3cb8e978e82b4bf01412cb4df41874a90d5aa279c7cc96 F ext/fts5/test/fts5corrupt6.test 2d72db743db7b5d9c9a6d0cfef24d799ed1aa5e8192b66c40e871a37ed9eed06 F ext/fts5/test/fts5corrupt7.test 4e830875c33b9ea3c4cf1ba71e692b63893cbb4faae8c69b1071889dc26e211c F ext/fts5/test/fts5corrupt8.test b81d802e41631e98100f49a1aadeeffef860e30a62d6ed7d743c2797c477239e @@ -190,7 +191,7 @@ F ext/fts5/test/fts5faultE.test 844586ce71dab4be85bb86880e87b624d089f851654cd22e F ext/fts5/test/fts5faultF.test 4abef99f86e99d9f0c6460dd68c586a766b6b9f1f660ada55bf2e8266bd1bbc1 F ext/fts5/test/fts5faultG.test 0544411ffcb3e19b42866f757a8a5e0fb8fef3a62c06f61d14deebc571bb7ea9 F ext/fts5/test/fts5faultH.test 2b2b5b8cb1b3fd7679f488c06e22af44107fbc6137eaf45b3e771dc7b149312d -F ext/fts5/test/fts5faultI.test 0706b307b208638554c9e65b4091e1c0dd8c92941535089a301df454ff2c56f4 +F ext/fts5/test/fts5faultI.test 9b33d664bccee4bbde0f275a48b2df3ea2f05d41f6d1d171aa2e844382cba621 F ext/fts5/test/fts5first.test bfd685b96905bf541d99d8644e0a7219d1d833455a08ab64e344071a613b6ba9 F ext/fts5/test/fts5full.test 97d263c1072f4a560929cca31e70f65d2ae232610e17e6affcf7e979df59547b F ext/fts5/test/fts5fuzz1.test 238d8c45f3b81342aa384de3e581ff2fa330bf922a7b69e484bbc06051a1080e @@ -205,7 +206,7 @@ F ext/fts5/test/fts5locale.test 83ba7ee12628b540d3098f39c39c1de0c0440eddff8f7512 F ext/fts5/test/fts5matchinfo.test 877520582feb86bbfd95ab780099bcba4526f18ac75ee34979144cf86ba3a5a3 F ext/fts5/test/fts5merge.test 2654df0bcdb2d117c2d38b6aeb0168061be01c643f9e9194b36c43a2970e8082 F ext/fts5/test/fts5merge2.test 3ebad1a59d6ad3fb66eff6523a09e95dc6367cbefb3cd73196801dea0425c8e2 -F ext/fts5/test/fts5misc.test 8c3cc771f773dc4bb4973620c51e7729e324ca2cc80eb8894f1c2c605e361f0b +F ext/fts5/test/fts5misc.test f4dee7da898d605a6488c5b7afaace3158ed6bb9addff78faa1b37b402b77fb9 F ext/fts5/test/fts5multi.test a15bc91cdb717492e6e1b66fec1c356cb57386b980c7ba5af1915f97fe878581 F ext/fts5/test/fts5multiclient.test 5ff811c028d6108045ffef737f1e9f05028af2458e456c0937c1d1b8dea56d45 F ext/fts5/test/fts5near.test 33d60867581066e5db7016deb5d651628125d7ff4e0233a88175aa5b65874c74 @@ -230,7 +231,7 @@ F ext/fts5/test/fts5rank.test 47c1e8e5d84754ff18e012fdd629776088b5a15de41bdd2495 F ext/fts5/test/fts5rebuild.test 83e72d77636378833233fadc7cb7517a2fa446ea7d1f94dd526ba3e7e104b9f5 F ext/fts5/test/fts5restart.test 9af2084b8e065130037b95f05f3f220bb7973903a7701e2c5fb916dff7cf80c5 F ext/fts5/test/fts5rowid.test 8632829fec04996832a4cfb4f0bd89721ba65b7e398c1731741bdb63f070e1a3 -F ext/fts5/test/fts5savepoint.test 7f373184cf2d6c1c472d2bc732e1fce62211ffe023f13e381db0f5e4fd06e41d +F ext/fts5/test/fts5savepoint.test 1447758d7900afe903cef08b4524c5331fb60c1126ae6fba7f4d8704268013c5 F ext/fts5/test/fts5secure.test a02f771742fb2b1b9bdcb4bf523bcf2d0aa1ff597831d40fe3e72aaa6d0ec40f F ext/fts5/test/fts5secure2.test 2e961d7eef939f294c56b5d895cac7f1c3a60b934ee2cfd5e5e620bdf1ba6bbc F ext/fts5/test/fts5secure3.test 6d066828d225b0dbe5db818d4d6165df7bb70210e68a577e858e8762400d5a23 @@ -263,7 +264,7 @@ F ext/fts5/test/fts5unindexed.test 168838d2c385e131120bbf5b516d2432a5fabc4caa225 F ext/fts5/test/fts5unindexed2.test 516236eceaac05ace322290a0d3705b4c4ffe4760d8eb9d014d9d27d56dfcc02 F ext/fts5/test/fts5update.test b8affd796e45c94a4d19ad5c26606ea06065a0f162a9562d9f005b5a80ccf0bc F ext/fts5/test/fts5update2.test c5baa76799ac605ebb8e5e21035db2014b396cef25c903eb96ba39b1d6f9f046 -F ext/fts5/test/fts5version.test c22d163c17e60a99f022cbc52de5a48bb7f84deaa00fe15e9bc4c3aa1996204e +F ext/fts5/test/fts5version.test 44ab35566267b7618c090443de2d9ad84f633df5d20bf72e9bad199ae5fced84 F ext/fts5/test/fts5vocab.test 2a2bdb60d0998fa3124d541b6d30b019504918dc43a6584645b63a24be72f992 F ext/fts5/test/fts5vocab2.test bbba149c254375d00055930c1a501c9a51e80b0d20bf7b98f3e9fa3b03786373 F ext/fts5/tool/fts5speed.tcl b0056f91a55b2d1a3684ec05729de92b042e2f85 @@ -399,15 +400,15 @@ F ext/misc/README.md d6dd0fe1d8af77040216798a6a2b0c46c73054d2f0ea544fbbcdccf6f23 F ext/misc/amatch.c 5001711cbecdd57b288cb613386789f3034e5beb58fbe0c79f2b3d643ffd4e03 F ext/misc/anycollseq.c 5ffdfde9829eeac52219136ad6aa7cd9a4edb3b15f4f2532de52f4a22525eddb F ext/misc/appendvfs.c 9642c7a194a2a25dca7ad3e36af24a0a46d7702168c4ad7e59c9f9b0e16a3824 -F ext/misc/base64.c a71b131e50300c654a66c469a25b62874481f3d1cb3beb56aca9a68edd812e0d -F ext/misc/base85.c 073054111988db593ef5fdb87ab8c459df1ea0c3aaaddf0f5bfa3d72b7e6280a +F ext/misc/base64.c 73c31eb325c71bae2e27276565e3f674fc095d8b0d7a651becb3b241a4d2fa57 +F ext/misc/base85.c a70c885c5c9350261ea6e7b166038eab21a09cf4fceae856ce41fae9c2213b60 F ext/misc/basexx.c 89ad6b76558efbceb627afd5e2ef1d84b2e96d9aaf9b7ecb20e3d00b51be6fcf F ext/misc/blobio.c a867c4c4617f6ec223a307ebfe0eabb45e0992f74dd47722b96f3e631c0edb2a F ext/misc/btreeinfo.c cb952620eedf5c0b7625b678f0f08e54d2ec0011d4e50efda5ebdc97f3df7d04 F ext/misc/carray.c 34fac63770971611c5285de0a9f0ac67d504eaf66be891f637add9290f1c76a5 F ext/misc/carray.h 503209952ccf2431c7fd899ebb92bf46bf7635b38aace42ec8aa1b8d7b6e98a5 F ext/misc/cksumvfs.c 3a7931dd30667be6348af919f3f9e6188dfd7646b42af8e399a499b327f5bd63 -F ext/misc/closure.c 0e04f52d93e678dd6f950f195f365992edf3c380df246f3d80425cba4c13891e +F ext/misc/closure.c 87e0967772e0087e709887ce7ca9cf13aa32d2096e33b5d3382c8b8d477c6cb1 F ext/misc/completion.c cb978c88d5577821323617a8ea775ce1b920e02dcdb593858f02044a4d008eea F ext/misc/compress.c 2c79a74330e0e0ba6cb3f7397f8ba5af12d46377ef5d3ee075e12dd8a6ed57f0 F ext/misc/csv.c 575c2c05fba0a451586a4d42c2c81e711780c41e797126f198d8d9e0a308dcdb @@ -415,7 +416,7 @@ F ext/misc/dbdump.c b8592f6f2da292c62991a13864a60d6c573c47a9cc58362131b9e6a64f82 F ext/misc/decimal.c 172cf81a8634e6a0f0bedaf71a8372fee63348cf5a3c4e1b78bb233c35889fdc F ext/misc/eval.c 04bc9aada78c888394204b4ed996ab834b99726fb59603b0ee3ed6e049755dc1 F ext/misc/explain.c 606100185fb90d6a1eade1ed0414d53503c86820d8956a06e3b0a56291894f2b -F ext/misc/fileio.c e6b34db4df4b55b96265086c0010264e257b6eab1644e665697a6da587659403 +F ext/misc/fileio.c 07cf3109ec6452789e3a989a010234e2a17b599ce82ea29212c948572456abac F ext/misc/fossildelta.c 8c026e086e406e2b69947f1856fa3b848fff5379962276430d10085b8756b05a F ext/misc/fuzzer.c 8b28acf1a7e95d50e332bdd47e792ff27054ad99d3f9bc2e91273814d4b31a5a F ext/misc/ieee754.c 62a90978204d2c956d5036eb89e548e736ca5fac0e965912867ddd7bb833256d @@ -431,7 +432,7 @@ F ext/misc/percentile.c 82531c62cd015b9cdca95ad6bb10c3a907ceb570d21ebd4fb7d634c8 F ext/misc/prefixes.c 82645f79229877afab08c8b08ca1e7fa31921280906b90a61c294e4f540cd2a6 F ext/misc/qpvtab.c fc189e127f68f791af90a487f4460ec91539a716daf45a0c357e963fd47cc06c F ext/misc/randomjson.c ef835fc64289e76ac4873b85fe12f9463a036168d7683cf2b773e36e6262c4ed -F ext/misc/regexp.c 4bdd0045912f81c84908bd535ec5ad3b1c8540b4287c70ab84070963624047db +F ext/misc/regexp.c 388e7f237307c7dfbfb8dde44e097946f6c437801d63f0d7ad63f3320d4e61cc F ext/misc/remember.c add730f0f7e7436cd15ea3fd6a90fd83c3f706ab44169f7f048438b7d6baa69c F ext/misc/rot13.c 51ac5f51e9d5fd811db58a9c23c628ad5f333c173f1fc53c8491a3603d38556c F ext/misc/scrub.c 2a44b0d44c69584c0580ad2553f6290a307a49df4668941d2812135bfb96a946 @@ -441,7 +442,7 @@ F ext/misc/shathree.c f3a778f27bf3e71b666a77f28e463a3b931c4dbe4219447e61bb678b4b F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52 F ext/misc/spellfix.c bcc42ef3fd29429bc01a83e751332b8d4690e65d45008449bdffe7656371487f F ext/misc/sqlar.c a6175790482328171da47095f87608b48a476d4fac78d8a9ff18b03a2454f634 -F ext/misc/sqlite3_stdio.c 5657afb6ec81bef31790973528980af778e0e1388a93db780d33007336efe6e6 +F ext/misc/sqlite3_stdio.c 0fe5a45bd332b30aef2b68c64edbe69e31e9c42365b0fa79ce95a034bca6fbb0 F ext/misc/sqlite3_stdio.h f05eaf5e0258f0573910324a789a9586fc360a57678c57a6d63cfaa2245b6176 F ext/misc/stmt.c b090086cd6bd6281c21271d38d576eeffe662f0e6b67536352ce32bbaa438321 F ext/misc/stmtrand.c 59cffa5d8e158943ff1ce078956d8e208e8c04e67307e8f249dece2436dcb7fc @@ -453,7 +454,7 @@ F ext/misc/urifuncs.c f71360d14fa9e7626b563f1f781c6148109462741c5235ac63ae0f8917 F ext/misc/uuid.c 5bb2264c1b64d163efa46509544fd7500cb8769cb7c16dd52052da8d961505cf F ext/misc/vfslog.c 3932ab932eeb2601dbc4447cb14d445aaa9fbe43b863ef5f014401c3420afd20 F ext/misc/vfsstat.c a85df08654743922a19410d7b1e3111de41bb7cd07d20dd16eda4e2b808d269d -F ext/misc/vfstrace.c 4d8b39570cbede1a05928c77e2142f8a744468443bf649cf86da3924e5e60fca +F ext/misc/vfstrace.c a73386403c350b210dc788a2d23a0f5cc89c49b176109a66af11b5078c116331 F ext/misc/vtablog.c 1100250ce8782db37c833e3a9a5c9a3ecf1af5e15b8325572b82e6e0a138ffb5 F ext/misc/vtshim.c 1976e6dd68dd0d64508c91a6dfab8e75f8aaf6cd F ext/misc/wholenumber.c 0fa0c082676b7868bf2fa918e911133f2b349bcdceabd1198bba5f65b4fc0668 @@ -575,14 +576,14 @@ F ext/rtree/visual01.txt e9c2564083bcd30ec51b07f881bffbf0e12b50a3f6fced0c222c5c1 F ext/session/changeset.c 7a1e6a14c7e92d36ca177e92e88b5281acd709f3b726298dc34ec0fb58869cb5 F ext/session/changesetfuzz.c 227076ab0ae4447d742c01ee88a564da6478bbf26b65108bf8fac9cd8b0b24aa F ext/session/changesetfuzz1.test 15b629004e58d5ffcc852e6842a603775bb64b1ce51254831f3d12b113b616cd -F ext/session/session1.test e94f764fbfb672147c0ef7026b195988133b371dc8cf9e52423eba6cad69717e +F ext/session/session1.test cc7e58976c2cc6263fb7ef0c5125a98eafc2f213c75929f986768d2dbc224725 F ext/session/session2.test ee83bb973b9ce17ccce4db931cdcdae65eb40bbb22089b2fe6aa4f6be3b9303f F ext/session/session3.test 2cc1629cfb880243aec1a7251145e07b78411d851b39b2aa1390704550db8e6a F ext/session/session4.test 823f6f018fcbb8dacf61e2960f8b3b848d492b094f8b495eae1d9407d9ab7219 F ext/session/session5.test 716bc6fafd625ce60dfa62ae128971628c1a1169 F ext/session/session6.test 35279f2ec45448cd2e24a61688219dc6cf7871757716063acf4a8b5455e1e926 F ext/session/session8.test 326f3273abf9d5d2d7d559eee8f5994c4ea74a5d935562454605e6607ee29904 -F ext/session/session9.test 5409d90d8141881d08285ed1c2c0d8d10fb92069 +F ext/session/session9.test 4e3aff62d6b4294498ddbe309076de06f4fddffad4fe5f5a6c033358b01df083 F ext/session/sessionA.test 1feeab0b8e03527f08f2f1defb442da25480138f F ext/session/sessionB.test c4fb7f8a688787111606e123a555f18ee04f65bb9f2a4bb2aa71d55ce4e6d02c F ext/session/sessionC.test f8a5508bc059ae646e5ec9bdbca66ad24bc92fe99fda5790ac57e1f59fce2fdf @@ -591,7 +592,8 @@ F ext/session/sessionE.test b2010949c9d7415306f64e3c2072ddabc4b8250c98478d3c0c4d F ext/session/sessionF.test d37ed800881e742c208df443537bf29aa49fd56eac520d0f0c6df3e6320f3401 F ext/session/sessionG.test 3efe388282d641b65485b5462e67851002cd91a282dc95b685d085eb8efdad0a F ext/session/sessionH.test 71bbff6b1abb2c4ac62b84dee53273c37e0b21e5fde3aed80929403e091ef859 -F ext/session/session_common.tcl e5598096425486b363718e2cda48ee85d660c96b4f8ea9d9d7a4c3ef514769da +F ext/session/session_common.tcl a31f537a929a695a852d241c9434f2847cadf329856401921139fbb03a5a7697 +F ext/session/session_gen.test 942a0002df10da53c45b40b581cc3ed25e7ff42bda1e7ba497273dc2887aa8e6 F ext/session/session_speed_test.c dcf0ef58d76b70c8fbd9eab3be77cf9deb8bc1638fed8be518b62d6cbdef88b3 F ext/session/sessionalter.test e852acb3d2357aac7d0b920a2109da758c4331bfdf85b41d39aa3a8c18914f65 F ext/session/sessionat.test 00c8badb35e43a2f12a716d2734a44d614ff62361979b6b85419035bc04b45ee @@ -605,7 +607,7 @@ F ext/session/sessionfault2.test b0d6a7c1d7398a7e800d84657404909c7d385965ea8576d F ext/session/sessionfault3.test ce0b5d182133935c224d72507dbf1c5be1a1febf7e85d0b0fbd6d2f724b32b96 F ext/session/sessioninvert.test 04075517a9497a80d39c495ba6b44f3982c7371129b89e2c52219819bc105a25 F ext/session/sessionmem.test f2a735db84a3e9e19f571033b725b0b2daf847f3f28b1da55a0c1a4e74f1de09 -F ext/session/sessionnoact.test 2563dff62a2a80dc7c88002241b2fd1578c3e5438735e180fb7e941ebbc66214 +F ext/session/sessionnoact.test 4c7ae5c7d351cb5323bca62b6b095592ad24bd90a6713c178b62ab0063d23e19 F ext/session/sessionnoop.test a9366a36a95ef85f8a3687856ebef46983df399541174cb1ede2ee53b8011bc7 F ext/session/sessionnoop2.test de4672dce88464396ec9f30ed08c6c01643a69c53ae540fadbbf6d30642d64e8 F ext/session/sessionrebase.test 702378bdcb5062f1106e74457beca8797d09c113a81768734a58b197b5b334e2 @@ -613,13 +615,13 @@ F ext/session/sessionrowid.test 85187c2f1b38861a5844868126f69f9ec62223a03449a98a F ext/session/sessionsize.test 8fcf4685993c3dbaa46a24183940ab9f5aa9ed0d23e5fb63bfffbdb56134b795 F ext/session/sessionstat1.test 5e718d5888c0c49bbb33a7a4f816366db85f59f6a4f97544a806421b85dc2dec F ext/session/sessionwor.test 6fd9a2256442cebde5b2284936ae9e0d54bde692d0f5fd009ecef8511f4cf3fc -F ext/session/sqlite3session.c 3d0a7f0f7a1c946e01818c716a55a40ae30542a29a9045cb05daf7fb658cdafa +F ext/session/sqlite3session.c 52a680dbb03c4734748b215d95987fb4d95ab23baaf053a01ac2626610963b58 F ext/session/sqlite3session.h 683ccbf16e2c2521661fc4c1cf918ce57002039efbcabcd8097fa4bca569104b -F ext/session/test_session.c aa29abdcc9011ac02f4fa38e8ede226106eaeee7c3ea7d8b2b999a124e0c368c +F ext/session/test_session.c 12e0a2c15fd60f92da4bb29c697c9177ff0c0dbcdc5129a54c47e999f147937a F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c -F ext/wasm/GNUmakefile 311aa0d5edc7006409962cc77cc26560d92f9be69c2c4302e8bbc68189fd02db -F ext/wasm/README-dist.txt 6382cb9548076fca472fb3330bbdba3a55c1ea0b180ff9253f084f07ff383576 -F ext/wasm/README.md a8a2962c3aebdf8d2104a9102e336c5554e78fc6072746e5daf9c61514e7d193 +F ext/wasm/GNUmakefile 06e0556e9840fd3d8870997025c2507ace9ba7bf11195f770295fc7947dfc1b5 +F ext/wasm/README-dist.txt f01081a850ce38a56706af6b481e3a7878e24e42b314cfcd4b129f0f8427066a +F ext/wasm/README.md b89605f65661cf35bf034ff6d43e448cc169b8017fc105d498e33b81218b482c F ext/wasm/SQLTester/GNUmakefile e0794f676d55819951bbfae45cc5e8d7818dc460492dc317ce7f0d2eca15caff F ext/wasm/SQLTester/SQLTester.mjs 66e1adc3d79467b68e3e40614fd42c1a577c7e219ec0985db966eded52a941e5 F ext/wasm/SQLTester/SQLTester.run.mjs 57f2adb33f43f2784abbf8026c1bfd049d8013af1998e7dcb8b50c89ffc332e0 @@ -629,24 +631,24 @@ F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-core 2bcbbfe3b95c043ed6037e2708a2ee078 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-extras fe40d6d758646e38f8b15f709044951e10884214f5453d35502100179c388c13 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-see fb29e62082a658f0d81102488414d422c393c4b20cc2f685b216bc566237957b F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 -F ext/wasm/api/README.md 34fe11466f9c1d81b10a0469e1114e5f1c5a6365c73d80a1a6ca639a1a358b73 -F ext/wasm/api/extern-post-js.c-pp.js c4154a7f90c2d7e51fd6738273908152036c3457fdc0b6523f1be3ef51105aac +F ext/wasm/api/README.md c64ec8e84449c069e0217706d9d7d31b3bd53627228b2ba0c3cddbdc2350ca66 +F ext/wasm/api/extern-post-js.c-pp.js 3fcd904f1204685dea84e5ae90d8b7e65a1dcebab1e838386d8328b74cce46c9 F ext/wasm/api/extern-pre-js.js cc61c09c7a24a07dbecb4c352453c3985170cec12b4e7e7e7a4d11d43c5c8f41 -F ext/wasm/api/post-js-footer.js cd0a8ec768501d9bd45d325ab0442037fb0e33d1f3b4f08902f15c34720ee4a1 -F ext/wasm/api/post-js-header.js 04dc12c3edd666b64a1b4ef3b6690c88dcc653f26451fd4734472d8e29c1c122 +F ext/wasm/api/post-js-footer.js 365405929f41ca0e6d389ed8a8da3f3c93e11d3ef43a90ae151e37fa9f75bf41 +F ext/wasm/api/post-js-header.js 53740d824e5d9027eb1e6fd59e216abbd2136740ce260ea5f0699ff2acb0a701 F ext/wasm/api/pre-js.c-pp.js a614a2c82b12c4d96d8e3ba77330329efc53c4d56a8a7e60ade900f341866cfb -F ext/wasm/api/sqlite3-api-cleanup.js d235ad237df6954145404305040991c72ef8b1881715d2a650dda7b3c2576d0e -F ext/wasm/api/sqlite3-api-glue.c-pp.js fb6dbfe692cc23000a65a4cd95a1a47ed5eb592dc9d8b55363b3c2952a787244 +F ext/wasm/api/sqlite3-api-cleanup.js 3ac1786e461ada63033143be8c3b00b26b939540661f3e839515bb92f2e35359 +F ext/wasm/api/sqlite3-api-glue.c-pp.js 5c0209e6a28164b4c2c1a34b0bb4aee3b7b1a264988d7e71fac08b8ede5b7ae3 F ext/wasm/api/sqlite3-api-oo1.c-pp.js f3a8e2004c6625d17946c11f2fb32008be78bc5207bf746fc77d59848813225f -F ext/wasm/api/sqlite3-api-prologue.js 6f1257e04885632ed9f44d43aba200b86e0bc16709ffdba29abbbeb1bc8e8b76 -F ext/wasm/api/sqlite3-api-worker1.c-pp.js 5cc22a3c0d52828cb32aad8691488719f47d27567e63e8bc8b832d74371c352d +F ext/wasm/api/sqlite3-api-prologue.js 9e7d89a2c0d02b8b2052a62757a89f1e7e4dbcc0d9cd3f2dafa896786954dad2 +F ext/wasm/api/sqlite3-api-worker1.c-pp.js f646a65257973b8c4481f8a6a216370b85644f23e64b126e7ae113570587c0ab F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89 F ext/wasm/api/sqlite3-opfs-async-proxy.js 3774befd97cd1a5e2895c8225a894aad946848c6d9b4028acc988b5d123475af F ext/wasm/api/sqlite3-vfs-helper.c-pp.js 3f828cc66758acb40e9c5b4dcfd87fd478a14c8fb7f0630264e6c7fa0e57515d F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js bb5e96cd0fd6e1e54538256433f1c60a4e3095063c4d1a79a8a022fc59be9571 F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 9b86ca2d8276cf919fbc9ba2a10e9786033b64f92c2db844d951804dee6c4b4e F ext/wasm/api/sqlite3-vtab-helper.c-pp.js e809739d71e8b35dfe1b55d24d91f02d04239e6aef7ca1ea92a15a29e704f616 -F ext/wasm/api/sqlite3-wasm.c 83f5e9f998e9fa4261eb84e9f092210e3ffe03895119f5ded0429eb34ab9d2be +F ext/wasm/api/sqlite3-wasm.c 6f9d8529072d072359cd22dc5dfb0572c524684686569cfbd0f9640d7619fc10 F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js 46f303ba8ddd1b2f0a391798837beddfa72e8c897038c8047eda49ce7d5ed46b F ext/wasm/api/sqlite3-worker1.c-pp.js 5e8706c2c4af2a57fbcdc02f4e7ef79869971bc21bb8ede777687786ce1c92d5 F ext/wasm/batch-runner-sahpool.html e9a38fdeb36a13eac7b50241dfe7ae066fe3f51f5c0b0151e7baee5fce0d07a7 @@ -657,27 +659,28 @@ F ext/wasm/c-pp.c 6d131069644964223305582a80973477fa8b06b57306781690d7874ebd3a4f F ext/wasm/common/SqliteTestUtil.js 7adaeffef757d8708418dc9190f72df22367b531831775804b31598b44f6aa51 F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15 F ext/wasm/common/testing.css e97549bab24126c24e0daabfe2de9bb478fb0a69fdb2ddd0a73a992c091aad6f -F ext/wasm/common/whwasmutil.js 6181f8cd958700f3723350bd4d76c7cc797db331a9aa14b25b42d121f12d6fee +F ext/wasm/common/whwasmutil.js c2e459286c1ada789cda6b17761bb1eea6034be572468eed78c049354f1051ba +F ext/wasm/config.make.in 4bc43443f768a61efd43cf995a5e618f58ac9afc0936706014193537d82c41cb F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508 F ext/wasm/demo-123.js c7b3cca50c55841c381a9ca4f9396e5bbdc6114273d0b10a43e378e32e7be5bf F ext/wasm/demo-jsstorage.html 409c4be4af5f207fb2877160724b91b33ea36a3cd8c204e8da1acb828ffe588e F ext/wasm/demo-jsstorage.js 44e3ae7ec2483b6c511384c3c290beb6f305c721186bcf5398ca4e00004a06b8 F ext/wasm/demo-worker1-promiser.c-pp.html 635cf90685805e21772a5f7a35d1ace80f98a9ef7c42ff04d7a125ddca7e5db8 -F ext/wasm/demo-worker1-promiser.c-pp.js fcc628cb42fcfaf07d250477801de1e6deb1e319d003976612a0db8d76b9fccc +F ext/wasm/demo-worker1-promiser.c-pp.js af168699d3cab1c27ad2364ebe06cd49db300bdbf404e23b00d5742ed52816ba F ext/wasm/demo-worker1.html 2c178c1890a2beb5a5fecb1453e796d067a4b8d3d2a04d65ca2eb1ab2c68ef5d -F ext/wasm/demo-worker1.js 836bece8615b17b1b572584f7b15912236a5947fe8c68b98d2737d7e287447ef -F ext/wasm/dist.make 653e212c1e84aa3be168d62a10616ccea45ee9585b0192745d2706707a5248ce +F ext/wasm/demo-worker1.js 08720227e98fa5b44761cf6e219269cee3e9dd0421d8d91459535da776950314 +F ext/wasm/dist.make 92ef4ffe33022a50f92d602acabad10bd8dd91759f3eb7df27fc6d7d37072b96 F ext/wasm/example_extra_init.c 2347cd69d19d839ef4e5e77b7855103a7fe3ef2af86f2e8c95839afd8b05862f F ext/wasm/fiddle.make d4969f0322a582c57a22ce3541f10a5b09a609d14eab32891f613f43b3c14d8b F ext/wasm/fiddle/fiddle-worker.js 850e66fce39b89d59e161d1abac43a181a4caa89ddeea162765d660277cd84ce F ext/wasm/fiddle/fiddle.js b444a5646a9aac9f3fc06c53d78af5e1912eb235d69a8e6010723e4eb0e9d4a1 F ext/wasm/fiddle/index.html c79b1741cbeba78f88af0a84cf5ec7de87a909a6a8d10a369b1f4824c66c2088 -F ext/wasm/index-dist.html 564b5ec5669676482c5a25dea9e721d8eafed426ecb155f93d29aeff8507511f +F ext/wasm/index-dist.html 56132399702b15d70c474c3f1952541e25cb0922942868f70daf188f024b3730 F ext/wasm/index.html e4bbffdb3d40eff12b3f9c7abedef91787e2935620b7f8d40f2c774b80ad8fa9 F ext/wasm/jaccwabyt/jaccwabyt.js 1264710db3cfbcb6887d95665b7aeba60c1126eaef789ca4cf1a4a17d5bc7f54 F ext/wasm/jaccwabyt/jaccwabyt.md 59a20df389abcc3606eb4eaea7fb7ba14504beb3e345dbea9b99a0618ba3bec8 -F ext/wasm/mkwasmbuilds.c e3580b26bc393e4e4beb25f6349b999878782f3319b740469f64c2e772632e03 +F ext/wasm/mkwasmbuilds.c baf6636e139e2c1e3b56e8dc26073ec80f6d14ae1876b023985315f43ccf312b F ext/wasm/module-symbols.html dc476b403369b26a1a23773e13b80f41b9a49f0825e81435fe3600a7cfbbe337 F ext/wasm/scratchpad-wasmfs.html a3d7388f3c4b263676b58b526846e9d02dfcb4014ff29d3a5040935286af5b96 F ext/wasm/scratchpad-wasmfs.mjs 66034b9256b218de59248aad796760a1584c1dd842231505895eff00dbd57c63 @@ -693,13 +696,13 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555 F ext/wasm/test-opfs-vfs.js 1618670e466f424aa289859fe0ec8ded223e42e9e69b5c851f809baaaca1a00c F ext/wasm/tester1-worker.html ebc4b820a128963afce328ecf63ab200bd923309eb939f4110510ab449e9814c F ext/wasm/tester1.c-pp.html 1c1bc78b858af2019e663b1a31e76657b73dc24bede28ca92fbe917c3a972af2 -F ext/wasm/tester1.c-pp.js 228101c290003423f0bfb66a6ebbfc6904fa7b1b69466e700c135f74ee83d62a +F ext/wasm/tester1.c-pp.js 05a0143c44a4114aad0ed40ce73c528febc3e0d6b69f48a51c895d7030015b74 F ext/wasm/tests/opfs/concurrency/index.html 657578a6e9ce1e9b8be951549ed93a6a471f4520a99e5b545928668f4285fb5e F ext/wasm/tests/opfs/concurrency/test.js d08889a5bb6e61937d0b8cbb78c9efbefbf65ad09f510589c779b7cc6a803a88 F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2 -F ext/wasm/wasmfs.make bc8bb227f35d5bd3863a7bd2233437c37472a0d81585979f058f9b9b503bef35 +F ext/wasm/wasmfs.make 68999f5bd8c489239592d59a420f8c627c99169bbd6fa16a404751f757b9f702 F magic.txt 5ade0bc977aa135e79e3faaea894d5671b26107cc91e70783aa7dc83f22f3ba0 -F main.mk 1ec3c7a81198397b14bf75c9304f01e891a5d464797cd402d4f3781feadd3331 +F main.mk c85055d36c36c188cee94cabe2e252cd912d10d1a9d62af477e855533b9531b2 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504 F mptest/crash01.test 61e61469e257df0850df4293d7d4d6c2af301421 @@ -709,9 +712,9 @@ F mptest/multiwrite01.test dab5c5f8f9534971efce679152c5146da265222d F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 acdff36db796e2d00225b911d3047d580cd136547298435426ce9d40347973cc F sqlite3.pc.in 0977c03a4da7c4204bd60e784a0efb8d51a190448aba78a4e973fe7192bdaf03 -F src/alter.c aa93e37e4a36a0525bbb2a2aeda20d2018f0aa995542c7dc658e031375e3f532 -F src/analyze.c 9a8b67239d899ac12289db5db3f5bfe7f7a0ad1277f80f87ead1d048085876eb -F src/attach.c f541c1fb59e56a1c8ba6b023d5e1844448b499fac669d2cbca18be7b6e7d4998 +F src/alter.c 1751e231d8385067fa0d0145f0d461a092db6bd3d7edbfc3172db625aceccd9a +F src/analyze.c 0823d2edb3ce564157e9d62714cc729027933209b712e95fbabb23e47fff0aec +F src/attach.c 28fe81ea9eddae3188a7454e47994328179cfa9b0b197bed8c7e35ec14b941c9 F src/auth.c 54ab9c6c5803b47c0d45b76ce27eff22a03b4b1f767c5945a3a4eb13aa4c78dc F src/backup.c 5c97e8023aab1ce14a42387eb3ae00ba5a0644569e3476f38661fa6f824c3523 F src/bitvec.c 9eac5f42c11914d5ef00a75605bb205e934f435c579687f985f1f8b0995c8645 @@ -719,28 +722,28 @@ F src/btmutex.c 79a43670447eacc651519a429f6ece9fd638563cf95b469d6891185ddae2b522 F src/btree.c 63ca6b647342e8cef643863cd0962a542f133e1069460725ba4461dcda92b03c F src/btree.h 18e5e7b2124c23426a283523e5f31a4bff029131b795bb82391f9d2f3136fc50 F src/btreeInt.h 98aadb6dcb77b012cab2574d6a728fad56b337fc946839b9898c4b4c969e30b6 -F src/build.c c6b09342d870a509529244ed8e19b4175a261f2e3163c199241d69e1d8a57607 -F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490 +F src/build.c 602fc45ea6301a3dc03ec20a9f9b294c492b7e1766ae96651f2ba8044dc445a6 +F src/callback.c acae8c8dddda41ee85cfdf19b926eefe830f371069f8aadca3aa39adf5b1c859 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e F src/ctime.c d35723024b963edce9c0fad5b3303e8bb9266083784844baed10a6dedfe26f3b -F src/date.c 89ce1ff20512a7fa5070ba6e7dd5c171148ca7d580955795bf97c79c2456144a -F src/dbpage.c b1aeb47c1004f26c39c6800f0045b8d729d232aca24f6aa430c491b83003d033 +F src/date.c 842c08ac143a56a627b05ac51d68624f2b7b03e3b4cba596205e735eed64ee57 +F src/dbpage.c 2e677acb658a29965e55398bbc61161cb7819da538057c8032adac7ab8e4a8c0 F src/dbstat.c 73362c0df0f40ad5523a6f5501224959d0976757b511299bf892313e79d14f5c F src/delete.c 03a77ba20e54f0f42ebd8eddf15411ed6bdb06a2c472ac4b6b336521bf7cea42 -F src/expr.c 3329173aacc6c37da3971b6253827799b32e301673be00126df8271bf018e15f +F src/expr.c ca943270395374afc65256ce86cdb152a22fa6ff146895175833b89ba870e117 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c 928ed2517e8732113d2b9821aa37af639688d752f4ea9ac6e0e393d713eeb76f -F src/func.c e6e997efb9ffaf8b07842e745159695669fdfa020f03635a2f774adab8b0f4af +F src/func.c b2fb33139972d7d65640b27ea962a49f1616265428001090cab39fcf270228e1 F src/global.c a19e4b1ca1335f560e9560e590fc13081e21f670643367f99cb9e8f9dc7d615b -F src/hash.c 9ee4269fb1d6632a6fecfb9479c93a1f29271bddbbaf215dd60420bcb80c7220 -F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 +F src/hash.c 73934a7f7ab1cb110614a9388cb516893b0cf5b7b69e4fd1a0780ac4ce166be7 +F src/hash.h 46b92795a95bfefb210f52f0c316e9d7cdbcdd7e7fcfb0d8be796d3a5767cddf F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 -F src/insert.c f8d1a0f8ee258411009c6b7f2d93170e351bd19f5ad89d57e1180644297cbe70 -F src/json.c 68a98c020c22127f2d65f08855f7fc7460ff352a6ce0b543d8931dde83319c22 +F src/insert.c 05e04ef637cbc0dccb9a5c5d188a5a2608891e554c8ec17c7a71afe2cf896a06 +F src/json.c 2663a0c7e574cb928de944720dcdcc11c931877d877549b8f1258a4002efd6f7 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36 -F src/main.c ad702f1ad48cd82f700b3e1b28b523154488874655cbd39bc9336d0bb2e8f728 +F src/main.c 7ace1b61344be773705132dc1fb607940ddd886170fd6613c004631d8c2dc470 F src/malloc.c 410e570b30c26cc36e3372577df50f7a96ee3eed5b2b161c6b6b48773c650c5e F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c 3bb59158c38e05f6270e761a9f435bf19827a264c13d1631c58b84bdc96d73b2 @@ -761,12 +764,12 @@ F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63 F src/os_common.h 6c0eb8dd40ef3e12fe585a13e709710267a258e2c8dd1c40b1948a1d14582e06 F src/os_kv.c 4d39e1f1c180b11162c6dc4aa8ad34053873a639bac6baae23272fc03349986a F src/os_setup.h 6011ad7af5db4e05155f385eb3a9b4470688de6f65d6166b8956e58a3d872107 -F src/os_unix.c e15305b49518d2e2cd405d14ede027a7ca52679d4d31b25635fb88d545211feb +F src/os_unix.c 49ee8571f42e1bfddc7d56829f68bce86f00c3000240ffbed0f49170631136ae F src/os_win.c 48c536ebd662a4b39623fffba41d95234854ddfbea4865d03fef1f51c29da187 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a -F src/pager.c 2fdd489447aa6bb0f672973bacb801ced92225ca9a1c874ed9b856d2741dc54e +F src/pager.c 3a1c4e7f69af482e33c8cba8a75afe0dda0ea6391240adac22b040ce1bdeef44 F src/pager.h 6137149346e6c8a3ddc1eeb40aee46381e9bc8b0fcc6dda8a1efde993c2275b8 -F src/parse.y dcf45a81b61223ac93e61fdfe9b22d635dd371c446e8222634d90aa37e25e5f6 +F src/parse.y f84673f1454e2bcf517623d4346e67fb2d73e57826ea103681ad5848238f6029 F src/pcache.c 588cc3c5ccaaadde689ed35ce5c5c891a1f7b1f4d1f56f6cf0143b74d8ee6484 F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5 F src/pcache1.c 49516ad7718a3626f28f710fa7448ef1fce3c07fd169acbb4817341950264319 @@ -775,20 +778,20 @@ F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7 F src/prepare.c 1832be043fce7d489959aae6f994c452d023914714c4d5457beaed51c0f3d126 F src/printf.c 96f7f8baeedc7639da94e4e7a4a2c200e2537c4eec9e5e1c2ffc821f40eb3105 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c -F src/resolve.c c8a5372b97b2a2e972a280676f06ddb5b74e885d3b1f5ce383f839907b57ef68 +F src/resolve.c 626c24b258b111f75c22107aa5614ad89810df3026f5ca071116d3fe75925c75 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 -F src/select.c 83e88fbb36f89b6703b348777491619554f0fd6f917c9fdf51e4c2e9cda6c04e -F src/shell.c.in ee54de10e9bd5572f689a6bc0c8e6fa58a8870e1670978ded44412d2715fd908 -F src/sqlite.h.in 78a34c8c6564ae43b16322bac5e843167ef97f5084c9c663684f67576ddcbcb2 +F src/select.c 57893cc8b099f231f7ed5b84faff14841f2aabb4776e32e17fae00aeae0a8993 +F src/shell.c.in b377a59822f207106424f08aead37e78b609222e98f86f04cc8a03563ccf3237 +F src/sqlite.h.in 5ad5afb4262e139f6214bce5af6d08cd3fd604137c0947b6018dcbc6fbfd3f06 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 -F src/sqliteInt.h b6e49765628a6b74e2c47a4e19f5245fa226ef32c6abc1b82849af8107a6b042 +F src/sqliteInt.h 6fa99f64ab558dd8e3ae44a8e67f78f5058bd452b5df959c44b00c9b74140675 F src/sqliteLimit.h 1bbdbf72bd0411d003267ffebc59a262f061df5653027a75627d03f48ca30523 F src/status.c cb11f8589a6912af2da3bb1ec509a94dd8ef27df4d4c1a97e0bcf2309ece972b F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 -F src/tclsqlite.c 90441d3cc16f966a23499d9096a3d2d971e5e8fddb4d1413b096b79c2b2cff07 +F src/tclsqlite.c 5c1e367e26711044730c93d4b81312170918a8d1fe811f45be740ab48f7de8c1 F src/tclsqlite.h 65e2c761446e1c9fa0342b7d2612a703483643c8b6a316d12a65b745a4727395 -F src/test1.c 1be101915405d022cef33bf7a13bdc0753015785de409a028b393fd4b8c7fc9b +F src/test1.c ba7b93478a6a7a3f48ec5507f28bc662636ac5d9f9791700d3648a8e788f0bb2 F src/test2.c 7ebc518e6735939d8979273a6f7b1d9b5702babf059f6ad62499f7f60a9eb9a3 F src/test3.c e7573aa0f78ee4e070a4bc8c3493941c1aa64d5c66d4825c74c0f055451f432b F src/test4.c 13e57ae7ec7a959ee180970aef09deed141252fe9bb07c61054f0dfa4f1dfd5d @@ -809,11 +812,11 @@ F src/test_fs.c c411c40baba679536fc34e2679349f59d8225570aed3488b5b3ef1908525a3d5 F src/test_func.c 858d4dddb7acf88222ebcba7cffb585f6dde83e4a15b838c0d05ccdf8d5219b9 F src/test_hexio.c 7449504e4bde876ba91b202617a9228c7c8c2e7bd8b957302f3803ac0e9e353c F src/test_init.c 17313332d58e90defc527129d5eda4a08bd6b6e8de7207a231523c8d98fb445e -F src/test_intarray.c e4216aadee9df2de7d1aee7e70f6b22c80ee79ece72a63d57105db74217639e5 +F src/test_intarray.c 3fcf8ca7bb5c8776ea83f6aa9b66f8df0d1f37a99207b0097c8486f9c15cedbf F src/test_intarray.h 6c3534641108cd1bea517a8e117dcba237081310a29a4c35bd2190caa8972293 F src/test_journal.c a0b9709b2f12b1ec819eea8a1176f283bca6d688a6d4a502bd6fd79786f4e287 F src/test_loadext.c 337056bae59f80b9eb00ba82088b39d0f4fe6dfd -F src/test_malloc.c a0295e022103b14a1bc5e0660cc2af7fbec05e0d029098782e326e50612e69d9 +F src/test_malloc.c 4954125ee89aa51d9f641d5cb272cc93ca4cb03dcc7c9c941d70210354c69567 F src/test_md5.c 811a45330c9391933360f998156a8907ee29909c828ab83ac05d329942cbea8f F src/test_multiplex.c b99d7f43ec859e6b93a40aaa5455420b3ad133053cce3db739498d29ea30735f F src/test_multiplex.h f0ff5b6f4462bfd46dac165d6375b9530d08089b7bcbe75e88e0926110db5363 @@ -838,33 +841,33 @@ F src/test_windirent.h da2e5b73c32d09905fbdd00f27cd802212a32a58ead882736fe4f5eb7 F src/test_window.c 6d80e11fba89a1796525e6f0048ff0c7789aa2c6b0b11c80827dc1437bd8ea72 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c -F src/tokenize.c fe17e03175cae35b6694d0f879e7bc3d1ddea2fd4ab148cba9bbd025b7a7bb12 -F src/treeview.c 921392561385e05ef5703f20a7a72f0a0a45c1fb749558d7467fae2c3f525006 -F src/trigger.c 247e2d712d5edc6021d52a169f6ac9a9c10d7144bc4ac7ea06c1ed2aa414659f -F src/update.c 0e01aa6a3edf9ec112b33eb714b9016a81241497b1fb7c3e74332f4f71756508 +F src/tokenize.c 375a772e2342274f4bf73605a70633237da09deed00a9bf4c4816a56777ea7c9 +F src/treeview.c d85ce76e6d1498d781957c07cb234da6d77ce0ed2d196480d516f54dabc62279 +F src/trigger.c da3c25786870d8bf97cd46b493374c2375d1abaf20a9b0f5f8629a3f2f2ce383 +F src/update.c 3e5e7ff66fa19ebe4d1b113d480639a24cc1175adbefabbd1a948a07f28e37cf F src/upsert.c 215328c3f91623c520ec8672c44323553f12caeb4f01b1090ebdca99fdf7b4f1 F src/utf.c 8b29d9a5956569ea2700f869669b8ef67a9662ee5e724ff77ab3c387e27094ba -F src/util.c e5f6a5eeaa26b69054a43bbd0048cfe3d2851f6961052b35aed8f695df922850 +F src/util.c 9ff6470dabcf943fd796d2da766c98bd328c8f6fe036a31e5b338e628603f989 F src/vacuum.c b763b6457bd058d2072ef9364832351fd8d11e8abf70cbb349657360f7d55c40 -F src/vdbe.c 8a6eb02823b424b273614bae41579392a5c495424592b60423dd2c443a583df0 -F src/vdbe.h 9676348d342bd04e21e384c63b57224171ce84fac77853357334ef94c4d33cf4 -F src/vdbeInt.h bf294a0c8fc4cc80779e74b04b8bd82c6e1197b3137cefe0b16cdf002fc7dfd6 -F src/vdbeapi.c 38c252a202d70b56cfb734460bc888ddbd581afec1a10cd4d6c894c9e0b5baea -F src/vdbeaux.c f0706ad786b8a6c5bc7ea622f3916c2ba2b883abc872d0b4911c4f021945c0e5 -F src/vdbeblob.c 255be187436da38b01f276c02e6a08103489bbe2a7c6c21537b7aecbe0e1f797 -F src/vdbemem.c df568ef0187e4be2788c35174f6d9b8566ab9475f9aff2d73907ed05aa5684b2 +F src/vdbe.c b98d86de7f0e6c02fb14e0e1ae8feab6aa84669d389771a848e23f59eb70dcad +F src/vdbe.h 3d26d5c7660c5c7bd33ffb0d8784615072d8b23c81f8110870efe2631136bc89 +F src/vdbeInt.h 078b1c15b26587b54c1c1879d0d2f4dec812b9de4c337fed9faf73fbcc3bf091 +F src/vdbeapi.c 82fe278a7c71b653235c6f9fb5de0b5de589908dfcb011ba2a782e8becf06f86 +F src/vdbeaux.c 541d3d232714455960eab4ed10b34cb48b4bcd565d7539ef31092f5e73648e6b +F src/vdbeblob.c 9166b6eb7054e5da82e35255892fb1ed551355a4716452539e8e3ac14f25fbe3 +F src/vdbemem.c 4af21a948820259ced96e3d46d70f9af347afa2deb7cb60a8b3981d5748e4279 F src/vdbesort.c d0a3c7056c081703c8b6d91ad60f17da5e062a5c64bf568ed0fa1b5f4cae311f F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf823 F src/vdbevtab.c fc46b9cbd759dc013f0b3724549cc0d71379183c667df3a5988f7e2f1bd485f3 -F src/vtab.c 316cd48e9320660db3047cd306cd056e4361180cebb4d0f10a39244e10c11422 +F src/vtab.c 828221bdbeaaa6d62126ee6d07fd4ec0d09dcaea846f87ad01944d8b7e548859 F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c 6da5a05c124bad1e530cf83bafa07d28afdda20c28b276de49948ef8295d9e84 F src/wal.h ba252daaa94f889f4b2c17c027e823d9be47ce39da1d3799886bbd51f0490452 F src/walker.c d5006d6b005e4ea7302ad390957a8d41ed83faa177e412f89bc5600a7462a014 -F src/where.c 9ad3dea8003a8913da6a4ca8322e2fe30773f46e88a0d4fbf9db13bdb999efa2 -F src/whereInt.h 1e36ec50392f7cc3d93d1152d4338064cd522b87156a0739388b7e273735f0ca -F src/wherecode.c 0c3d3199a2b769a5e2bb70feb5003dc85b3d86842ecaf903a47f2b4205ca5dab -F src/whereexpr.c 0f93a29cabd3a338d09a1f5c6770620a1ac51ec1157f3229502a7e7767c60b6f +F src/where.c 09dc313e7223ca1217c39c7026b00f16ff449a8323511a762fcba7863a00f4cd +F src/whereInt.h d20cddddb1d61b18d5cb1fcfa9b77fbeebbc4afe44d996e603452a23b3009ee1 +F src/wherecode.c 5baa06f0daae7d38aca1d4814030b82ad4f127fe6bad18f0644776a474f6088b +F src/whereexpr.c 2415c8eee5ff89a8b709d7d83d71c1ff986cd720d0520057e1d8a5371339012a F src/window.c 2bf01f9941a64fbcead61a0e3cb5db3fca5094b30d2ff0d23274c2a81d2e2385 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627 @@ -924,7 +927,7 @@ F test/auth2.test 9eb7fce9f34bf1f50d3f366fb3e606be5a2000a1 F test/auth3.test 76d20a7fa136d63bcfcf8bcb65c0b1455ed71078d81f22bcd0550d3eb18594ab F test/autoanalyze1.test b9cc3f32a990fa56669b668d237c6d53e983554ae80c0604992e18869a0b2dec F test/autoinc.test 997d6f185f138229dc4251583a1d04816423dddc2fc034871a01aeb1d728cb39 -F test/autoindex1.test 714cac6e60beeb5a26ed346dd46505ba60b5a5597e9122c9ed3a55f89a922aa4 +F test/autoindex1.test 65931519206bbec71948b11e125af0656435a0937973fe5fed70d776a712911f F test/autoindex2.test 12ef578928102baaa0dc23ad397601a2f4ecb0df F test/autoindex3.test ca502c8050166ac6107a7b4fe4e951f4d3270a23a958af02b14f1b962b83c4b6 F test/autoindex4.test 3c2105e9172920e26f950ba3c5823e4972190e022c1e6f260ba476b0af24c593 @@ -987,7 +990,7 @@ F test/cache.test 13bc046b26210471ca6f2889aceb1ea52dc717de F test/cacheflush.test af25bb1509df04c1da10e38d8f322d66eceedf61 F test/cachespill.test 895997f84a25b323b166aecb69baab2d6380ea98f9e0bcc688c4493c535cfab9 F test/capi2.test 4ee545824adc3eb33bf57ef89f77440b28188ec3da72e5425ff0fcdba32e8d5a -F test/capi3.test 4892b5e53d2a6941edc9d204a0ab174dd66e8689282d9a15e4384561c3965945 +F test/capi3.test ab90c548969613315605c555a8623f6b56e00e28d451c46a17ef73683c422c70 F test/capi3b.test efb2b9cfd127efa84433cd7a2d72ce0454ae0dc4 F test/capi3c.test 31d3a6778f2d06f2d9222bd7660c41a516d1518a059b069e96ebbeadb5a490f7 F test/capi3d.test 8b778794af891b0dca3d900bd345fbc8ebd2aa2aae425a9dccdd10d5233dfbde @@ -1066,7 +1069,7 @@ F test/ctime.test 340f362f41f92972bbd71f44e10569a5cc694062b692231bd08aa6fe6c1c47 F test/cursorhint.test 05cf0febe5c5f8a31f199401fd1c9322249e753950d55f26f9d5aca61408a270 F test/cursorhint2.test 6f3aa9cb19e7418967a10ec6905209bcbb5968054da855fc36c8beee9ae9c42f F test/dataversion1.test 6e5e86ac681f0782e766ebcb56c019ae001522d114e0e111e5ebf68ccf2a7bb8 -F test/date.test c8ff835023f2107b57ce7a45c92265d51c98a23fc93231e998f12d850831aad6 +F test/date.test 8911c3d9fb0e496e92e0259697f431a00707222d2b3438ce1105d1790a3c0d51 F test/date2.test 7e12ec14aaf4d5e6294b4ba140445b0eca06ea50062a9c3a69c4ee13d0b6f8b1 F test/date3.test a1b77abf05c6772fe5ca2337cac1398892f2a41e62bce7e6be0f4a08a0e64ae5 F test/date4.test 75dc8401e8c0639a228cd26a6eaa4ff5ea8ccda912b9853d1c9462c476670e17 @@ -1076,8 +1079,8 @@ F test/dbfuzz.c 73047c920d6210e5912c87cdffd9a1c281d4252e F test/dbfuzz001.test 6c9a4622029d69dc38926f115864b055cb2f39badd25ec22cbfb130c8ba8e9c3 F test/dbfuzz2-seed1.db e6225c6f3d7b63f9c5b6867146a5f329d997ab105bee64644dc2b3a2f2aebaee F test/dbfuzz2.c 4b3c12de4d98b1b2d908ab03d217d4619e47c8b23d5e67f8a6f2b1bdee7cae23 -F test/dbpage.test fce29035c7566fd7835ec0f19422cb4b9c6944ce0e1b936ff8452443f92e887d -F test/dbpagefault.test 35f06cfb2ef100a9b19d25754e8141b9cba9b7daabd4c60fa5af93fcce884435 +F test/dbpage.test 63fab1eb026bada121107e53436fa749bbf83281dc9dea17af422f7a5c0f289f +F test/dbpagefault.test ea39de2ca86041a9c6df1135645180a76d0a8da93ac159e2fafe38e39636530b F test/dbstatus.test 4a4221a883025ffd39696b3d1b3910b928fb097d77e671351acb35f3aed42759 F test/dbstatus2.test f5fe0afed3fa45e57cfa70d1147606c20d2ba23feac78e9a172f2fe8ab5b78ef F test/decimal.test ef731887b43ee32ef86e1c8fddb61a40789f988332c029c601dcf2c319277e9e @@ -1152,7 +1155,7 @@ F test/fkey2.test 1063d65e5923c054cfb8f0555a92a3ae0fa8c067275a33ee1715bd856cdb30 F test/fkey3.test 76d475c80b84ee7a5d062e56ccb6ea68882e2b49 F test/fkey4.test 86446017011273aad8f9a99c1a65019e7bd9ca9d F test/fkey5.test 6727452e163a427147e84e739da18713da553d79f9783559b04fdcd36d5c7421 -F test/fkey6.test d078a1e323a740062bed38df32b8a736fd320dc0 +F test/fkey6.test ebd11efb00b9c70b57f4c6b6184445145c96e320329bd90a175036570c5b25ca F test/fkey7.test 64fb28da03da5dfe3cdef5967aa7e832c2507bf7fb8f0780cacbca1f2338d031 F test/fkey8.test 51deda7f1a1448bca95875e4a6e1a3a75b4bd7215e924e845bd60de60e4d84bf F test/fkey_malloc.test 594a7ea1fbab553c036c70813cd8bd9407d63749 @@ -1274,7 +1277,7 @@ F test/fuzz3.test 70ba57260364b83e964707b9d4b5625284239768ab907dd387c740c0370ce3 F test/fuzz4.test c229bcdb45518a89e1d208a21343e061503460ac69fae1539320a89f572eb634 F test/fuzz_common.tcl b7197de6ed1ee8250a4f82d67876f4561b42ee8cbbfc6160dcb66331bad3f830 F test/fuzz_malloc.test f348276e732e814802e39f042b1f6da6362a610af73a528d8f76898fde6b22f2 -F test/fuzzcheck.c 89b71d92b150a532e945e489d6e0721a4b15353c9255e079c198ed2a1958018b +F test/fuzzcheck.c 6fc952750a69168dd5fea38b9d35cb38475bfda15c8acfd156ac09cd03ddbd3e F test/fuzzdata1.db 3e86d9cf5aea68ddb8e27c02d7dfdaa226347426c7eb814918e4d95475bf8517 F test/fuzzdata2.db 128b3feeb78918d075c9b14b48610145a0dd4c8d6f1ca7c2870c7e425f5bf31f F test/fuzzdata3.db c6586d3e3cef0fbc18108f9bb649aa77bfc38aba @@ -1282,7 +1285,7 @@ F test/fuzzdata4.db b502c7d5498261715812dd8b3c2005bad08b3a26e6489414bd13926cd3e4 F test/fuzzdata5.db e35f64af17ec48926481cfaf3b3855e436bd40d1cfe2d59a9474cb4b748a52a5 F test/fuzzdata6.db b8725a5f5cf7a3b7241a9038e57ca7e7cc8c3f4d86b44bd770617bda245ab2b0 F test/fuzzdata7.db 0166b56fd7a6b9636a1d60ef0a060f86ddaecf99400a666bb6e5bbd7199ad1f2 -F test/fuzzdata8.db 4a53b6d077c6a5c23b609d8d3ac66996fa55ba3f8d02f9b6efdd0214a767a35a +F test/fuzzdata8.db c6f9cb7d2b808fb10894afe53ef00f51e73e43baa7aabdba7e9af4713fc5b186 F test/fuzzer1.test 3d4c4b7e547aba5e5511a2991e3e3d07166cfbb8 F test/fuzzer2.test a85ef814ce071293bce1ad8dffa217cbbaad4c14 F test/fuzzerfault.test f64c4aef4c9e9edf1d6dc0d3f1e65dcc81e67c996403c88d14f09b74807a42bc @@ -1304,7 +1307,7 @@ F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0 F test/in4.test bb767ec1cfd1730256f0a83219f0acda36bc251b63f8b8bb7d8c7cff17875a4f F test/in5.test 4fd79c70dfa0681313e8cdca07f5ff0400bdc0e20f808a5c59eaef1e4b48082a F test/in6.test f5f40d6816a8bb7c784424b58a10ac38efb76ab29127a2c17399e0cbeeda0e4b -F test/in7.test 5050b648510d88bd27ff6b40991a45e1cc277c20e258162e81650e01069a56bb +F test/in7.test d9efdee00b074a60c6343993b2eda78bc369ab080dad864513c73f8aca89d566 F test/incrblob.test c9b96afc292aeff43d6687bcb09b0280aa599822 F test/incrblob2.test a494c9e848560039a23974b9119cfc2cf3ad3bd15cc2694ee6367ae537ef8f1f F test/incrblob3.test 67621a04b3084113bf38ce03797d70eca012d9d8f948193b8f655df577b0da6f @@ -1398,7 +1401,7 @@ F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200 F test/lemon-test01.y 70110eff607ab137ccc851edb2bc7e14a6d4f246b5d2d25f82a60b69d87a9ff2 F test/like.test b3ea2ba3558199aa8f25a42ddeb54772e234fab50868c9f066047acdbda8fc58 F test/like2.test d3be15fefee3e02fc88942a9b98f26c5339bbdef7783c90023c092c4955fe3d3 -F test/like3.test a76e5938fadbe6d32807284c796bafd869974a961057bc5fc5a28e06de98745c +F test/like3.test b21284df226d6028feeb4dcc56ad9d32673d82c14a63f15f25471292c36491e7 F test/limit.test 350f5d03c29e7dff9a2cde016f84f8d368d40bcd02fa2b2a52fa10c4bf3cbfaf F test/limit2.test 9409b033284642a859fafc95f29a5a6a557bd57c1f0d7c3f554bd64ed69df77e F test/literal.test a65dca9fef86e51b8e45544268e37abbd4bb94ba35fd65f6fdcab2f288cd8f79 @@ -1532,7 +1535,7 @@ F test/pg_common.tcl 3b27542224db1e713ae387459b5d117c836a5f6e328846922993b6d2b76 F test/pragma.test 11cb9310c42f921918f7f563e3c0b6e70f9f9c3a6a1cf12af8fccb6c574f3882 F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f F test/pragma3.test 92a46bbea12322dd94a404f49edcfbfc913a2c98115f0d030a7459bb4712ef31 -F test/pragma4.test 336b99c2a9fd35af3cc6da94f794b4cba09bbdb18f0ecbd3f734bb6bb8e1c15c +F test/pragma4.test 396ef9bff1fb966d41721545ad4b12bfc26aae315f5fe51d9b917828d49e6f8e F test/pragma5.test 7b33fc43e2e41abf17f35fb73f71b49671a380ea92a6c94b6ce530a25f8d9102 F test/pragma6.test c5ec577ba087954b4dfa619a3cbe97b155b60a0af487527abe89b10fc17e6512 F test/pragmafault.test 275edaf3161771d37de60e5c2b412627ac94cef11739236bec12ed1258b240f8 @@ -1676,7 +1679,9 @@ F test/speed3.test 694affeb9100526007436334cf7d08f3d74b85ef F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715 F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa F test/speed4p.test 377a0c48e5a92e0b11c1c5ebb1bc9d83a7312c922bc0cb05970ef5d6a96d1f0c -F test/speedtest1.c cc503febbb8559d541a67d7a33d3d7bb8a2c8cbbfc89eb336e2e2bd6ad6a63ee +F test/speedtest.md ee958457ae1b729d9715ae33c0320600000bf1d9ddea1a88dcf79f56729d6fad +F test/speedtest.tcl 926d1e168f4a14e6fb68c5dc174de743536b547f365264bd5bac533b3621a4a0 x +F test/speedtest1.c 132cd5ba064f48910bb4b68337442b0ef419218c8de9e9855f66d98015286ddb F test/spellfix.test 951a6405d49d1a23d6b78027d3877b4a33eeb8221dcab5704b499755bb4f552e F test/spellfix2.test dfc8f519a3fc204cb2dfa8b4f29821ae90f6f8c3 F test/spellfix3.test 0f9efaaa502a0e0a09848028518a6fb096c8ad33 @@ -1684,7 +1689,7 @@ F test/spellfix4.test 51c7c26514ade169855c66bcf130bd5acfb4d7fd090cc624645ab275ae F test/sqldiff1.test 1b7ab4f312442c5cc6b3a5f299fa8ca051416d1dd173cb1126fd51bf64f2c3fb F test/sqllimits1.test 408131e4975d61868711c83f101a56d4602313cc5cae88d3eee81c1da364fd89 F test/sqllog.test 6af6cb0b09f4e44e1917e06ce85be7670302517a -F test/starschema1.test a84205f97fe278a015ac39546c86b97228d22043af28f3a2ef809e8d5637ce1d +F test/starschema1.test f5388cd32527ab18d3f98f9e3402ec780f6a186e04e0d9c8531d7568ee734e11 F test/startup.c 1beb5ca66fcc0fce95c3444db9d1674f90fc605499a574ae2434dcfc10d22805 F test/stat.test 123212a20ceb496893d5254a5f6c76442ce549fdc08d1702d8288a2bbaac8408 F test/statfault.test 064f43379e4992b5221b7d9ac887c313b3191f85cce605d78e416fc4045da64e @@ -1724,7 +1729,7 @@ F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637 F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc F test/tester.tcl 2f900e8c912fbbaf381e69a92258fa6023ad0b4c8907e426bebbb4585da23c61 F test/testrunner.tcl 90ed8b6c2b26dc1f6af08aeb04670a5df86172f3d9828d8af000f972afa50061 x -F test/testrunner_data.tcl 1497914126bcf5749aa9b86268bd7636b027f7dcfac8746e7bcb89b97267a81c +F test/testrunner_data.tcl f64589ddd05abc2be4d6ab7573fb1cebb27a1034a092d95da684187dd455cd41 F test/thread001.test a0985c117eab62c0c65526e9fa5d1360dd1cac5b03bde223902763274ce21899 F test/thread002.test c24c83408e35ba5a952a3638b7ac03ccdf1ce4409289c54a050ac4c5f1de7502 F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7 @@ -1888,7 +1893,7 @@ F test/tokenize.test ce430a7aed48fc98301611429595883fdfcab5d7 F test/tpch01.test 4479008f85f6f8f25f7ab2cb305d665752b4727fa28a8df3d8e0ad46520c62ff F test/trace.test a659a9862957f4789e37a92b3bf6d2caf5c86b02cdeefc41e850ae53acf6992a F test/trace2.test f5cb67ad3bc09e0c58e8cca78dfd0b5639259983 -F test/trace3.test 4f418ed30d15d9d17dcf13a17f0bd99a92e3038e038798e35db7525f82f4c281 +F test/trace3.test 2deeac66359c9f007f0fc9fb6336994a5d68fc1a65129f322a9e9546fd537d0a F test/trans.test 45f6f9ab6f66a7b5744f1caac06b558f95da62501916906cf55586a896f9f439 F test/trans2.test 62bd045bfc7a1c14c5ba83ba64d21ade31583f76 F test/trans3.test 91a100e5412b488e22a655fe423a14c26403ab94 @@ -2023,7 +2028,7 @@ F test/walrofault.test c70cb6e308c443867701856cce92ad8288cd99488fa52afab77cca6cf F test/walseh1.test bae700eb99519b6d5cd3f893c04759accc5a59c391d4189fe4dd6995a533442b F test/walsetlk.test 9c5b92f9a20252540fedf9ffa6ee3d1b8af08ea4b80d0144d9b88e6c0c1de80d F test/walsetlk2.test 5ae8662a28c013e8df2ce975f9e3577a7f239aeb4622bb8d4d0ca8e16c0c132e -F test/walsetlk3.test 520633cb23561e960d3100b23f9da692f0cce2ffd650d72e2d06163e6c2d228b +F test/walsetlk3.test 7ac01cb6ca9640081fbb5c59170172b85fbaf9e7a8a1e678a38e21df5d55721e F test/walshared.test 42e3808582504878af237ea02c42ca793e8a0efaa19df7df26ac573370dbc7a3 F test/walslow.test 0c51843836c9dcf40a5ac05aa781bfb977b396ee2c872d92bd48b79d5dd9aa23 F test/walthread.test 14b20fcfa6ae152f5d8e12f5dc8a8a724b7ef189f5d8ef1e2ceab79f2af51747 @@ -2089,7 +2094,7 @@ F test/with2.test 181674a6cc86a601ca2ac052741cdfad5b529e07e870435d2f6cdb92d589ff F test/with3.test e30369ea27aa27eb1bda4c5e510c8a9f782c8afd2ab99d1a02b8a7f25a5d3e65 F test/with4.test 257be66c0c67fee1defbbac0f685c3465e2cad037f21ce65f23f86084f198205 F test/with5.test 6248213c41fab36290b5b73aa3f937309dfba337004d9d8434c3fabc8c7d4be8 -F test/with6.test 9ff3503c3ff7cd459dc4852a02aaefa998dccace53f4142a0eb726174ad5984a +F test/with6.test 281e4861b5e517f6c3c2f08517a520c1e2ee7c11966545d3901f258a4fe8ef76 F test/withM.test 693b61765f2b387b5e3e24a4536e2e82de15ff64 F test/without_rowid1.test a5210b8770dc4736bca4e74bc96588f43025ad03ad6a80f885afd36d9890e217 F test/without_rowid2.test af260339f79d13cb220288b67cd287fbcf81ad99 @@ -2111,7 +2116,7 @@ F tool/GetTclKit.bat d84033c6a93dfe735d247f48ba00292a1cc284dcf69963e5e672444e045 F tool/Replace.cs 02c67258801c2fb5f63231e0ac0f220b4b36ba91 F tool/build-all-msvc.bat c817b716e0edeecaf265a6775b63e5f45c34a6544f1d4114a222701ed5ac79ab x F tool/build-shell.sh 369c4b171cc877ad974fef691e4da782b4c1e99fe8f4361316c735f64d49280f -F tool/buildtclext.tcl 5e1f1aa843e635c8b7480c7d1ec1f149a5e52136ae2fca1226304053a1a60587 +F tool/buildtclext.tcl 20726b6b73c7911baa8519a9467b4062104339a5ce57947819884525c56d79e3 F tool/cg_anno.tcl c1f875f5a4c9caca3d59937b16aff716f8b1883935f1b4c9ae23124705bc8099 x F tool/checkSpacing.c 810e51703529a204fc4e1eb060e9ab663e3c06d2 F tool/cktclsh.sh 6075eef9c6b9ba4b38fef2ca2a66d25f2311bd3c610498d18a9b01f861629cca @@ -2120,7 +2125,7 @@ F tool/custom.txt 24ed55e71c5edae0067ba159bbf09240d58b160331f7716e95816cd3aa0ba5 F tool/dbhash.c 5da0c61032d23d74f2ab84ffc5740f0e8abec94f2c45c0b4306be7eb3ae96df0 F tool/dbtotxt.c ca48d34eaca6d6b6e4bd6a7be2b72caf34475869054240244c60fa7e69a518d6 F tool/dbtotxt.md c9a57af8739957ef36d2cfad5c4b1443ff3688ed33e4901ee200c8b651f43f3c -F tool/emcc.sh.in 1f3226166bad1765c0bf42fac3d29037704c2078eb22562f9ddfbe73bff023b0 +F tool/emcc.sh.in 41a049468c8155433e37e656ba5bae063a000768b1d627025f277732c4e7c4a4 F tool/enlargedb.c 3e8b2612b985cfa7e3e8800031ee191b43ae80de96abb5abbd5eada62651ee21 F tool/extract-sqlite3h.tcl 069ceab0cee26cba99952bfa08c0b23e35941c837acabe143f0c355d96c9e2eb x F tool/extract.c 054069d81b095fbdc189a6f5d4466e40380505e2 @@ -2140,7 +2145,8 @@ F tool/loadfts.c c3c64e4d5e90e8ba41159232c2189dba4be7b862 F tool/logest.c c34e5944318415de513d29a6098df247a9618c96d83c38d4abd88641fe46e669 F tool/max-limits.c cbb635fbb37ae4d05f240bfb5b5270bb63c54439 F tool/merge-test.tcl de76b62f2de2a92d4c1ca4f976bce0aea6899e0229e250479b229b2a1914b176 -F tool/mkautoconfamal.sh cbdcf993fa83dccbef7fb77b39cdeb31ef9f77d9d88c9e343b58d35ca3898a6a +F tool/mkamalzip.tcl 8aa5ebe7973c8b8774062d34e15fea9815c4cc2ceea3a9b184695f005910876a +F tool/mkautoconfamal.sh 14d2144043c6455958012f92324f4ce7c90a261b5daa2f2c7509498468475f8d F tool/mkccode.tcl 210159febe0ef0ecbc53c79833500663ceaba0115b2b374405818dc835b5f84b x F tool/mkctimec.tcl ef6a67ec82e5b6fc19152a4c79f237227b18bf67ff16d155bac7adb94355d9cf x F tool/mkkeywordhash.c 6b0be901c47f9ad42215fc995eb2f4384ac49213b1fba395102ec3e999acf559 @@ -2149,18 +2155,19 @@ F tool/mkopcodec.tcl 33d20791e191df43209b77d37f0ff0904620b28465cca6990cf8d60da61 F tool/mkopcodeh.tcl 2b4e6967a670ef21bf53a164964c35c6163277d002a4c6f56fa231d68c88d023 F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa F tool/mkpragmatab.tcl 32e359ccb21011958a821955254bd7a5fa7915d01a8c16fed91ffc8b40cb4adf -F tool/mkshellc.tcl 2bc29c201933ae72a16a79070fe80aded80c24ea487ecd2f8df20c2973c87bfc +F tool/mkshellc.tcl 9ce74de0fa904a2c56a96f8d8b5261246bacb0eaa8d7e184f9e18ff94145ebbc F tool/mksourceid.c 36aa8020014aed0836fd13c51d6dc9219b0df1761d6b5f58ff5b616211b079b9 F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97 -F tool/mksqlite3c-noext.tcl 4f7cfef5152b0c91920355cbfc1d608a4ad242cb819f1aea07f6d0274f584a7f -F tool/mksqlite3c.tcl 9e88a30981280e33489fe4782f4ab1e5349ba1866603fba7f1a948d5599b9124 -F tool/mksqlite3h.tcl 7a4648fef5efb33308d575c7775eb242855d71d5bf89065df3f006b9a634a0a1 +F tool/mksqlite3c-noext.tcl 351c55256213154cabb051a3c870ef9f4487de905015141ae50dc7578a901b84 +F tool/mksqlite3c.tcl ba13086555b3cb835eba5e47a9250300ab85304d23fd1081abd3f29d8ab71a2b +F tool/mksqlite3h.tcl b05b85c32295bad3fe64807729693d1f19faed3c464c5faac6c53bb6b972ac2f F tool/mksqlite3internalh.tcl eb994013e833359137eb53a55acdad0b5ae1049b +F tool/mksrczip.tcl 81efd9974dbb36005383f2cd655520057a2ae5aa85ac2441a80c7c28f803ac52 F tool/mktoolzip.tcl 34b4e92be544f820e2cc26f143f7d5aec511e826ec394cc82969a5dcf7c7a27c F tool/mkvsix.tcl 67b40996a50f985a573278eea32fc5a5eb6110bdf14d33f1d8086e48c69e540a F tool/offsets.c 8ed2b344d33f06e71366a9b93ccedaa38c096cc1dbd4c3c26ad08c6115285845 F tool/omittest-msvc.tcl d6b8f501ac1d7798c4126065030f89812379012cad98a1735d6d7221492abc08 -F tool/omittest.tcl 5ca5e4e01716d5f35b48b00fd351d929f01fbb98169a5a3cd00baf3d2e2019a9 +F tool/omittest.tcl bec70ef0e16255c8d9eb06ecd7edf823c07a60a836186cdbce3528fb34b67995 F tool/opcodesum.tcl 740ed206ba8c5040018988129abbf3089a0ccf4a F tool/pagesig.c f98909b4168d9cac11a2de7f031adea0e2f3131faa7515a72807c03ec58eafeb F tool/replace.tcl 511c61acfe563dfb58675efb4628bb158a13d48ff8322123ac447e9d25a82d9a @@ -2182,7 +2189,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 7ce07da76b5e745783e703a834417d725b7d45fd F tool/spellsift.tcl 52b4b04dc4333c7ab024f09d9d66ed6b6f7c6eb00b38497a09f338fa55d40618 x -F tool/split-sqlite3c.tcl 5aa60643afca558bc732b1444ae81a522326f91e1dc5665b369c54f09e20de60 +F tool/split-sqlite3c.tcl 07e18a1d8cc3f6b3a4a1f3528e63c9b29a5c8a7bca0b8d394b231da464ce1247 F tool/sqldiff.c 2a0987d183027c795ced13d6749061c1d2f38e24eddb428f56fa64c3a8f51e4b F tool/sqlite3_analyzer.c.in fc7735c499d226a49d843d8209b2543e4e5229eeb71a674c331323a2217b65b4 F tool/sqlite3_rsync.c 9a1cca2ab1271c59b37a6493c15dc1bcd0ab9149197a9125926bc08dd26b83fb @@ -2192,7 +2199,7 @@ F tool/src-verify.c d00f93263aa2fa6ba0cba0106d95458e6effb94fdb5fc634f56834f90c05 F tool/srcck1.c 371de5363b70154012955544f86fdee8f6e5326f F tool/srctree-check.tcl 1f1f505835a4beca64c1751a7ebec5c41a1ddf22b1e80481345b95059eef6583 F tool/stack_usage.tcl f8e71b92cdb099a147dad572375595eae55eca43 -F tool/stripccomments.c 20b8aabc4694d0d4af5566e42da1f1a03aff057689370326e9269a9ddcffdc37 +F tool/stripccomments.c dfe9cc03cf87728ac9836be30763f8aa52b82caca0780b3d3f3572e4643b01d3 F tool/symbols-mingw.sh 4dbcea7e74768305384c9fd2ed2b41bbf9f0414d F tool/symbols.sh 1612bd947750e21e7b47befad5f6b3825b06cce0705441f903bf35ced65ae9b9 F tool/tclConfigShToAutoDef.sh 44ec55046d86a3febb2cb3e099399b41794e80e9cd138eee7b9b016f819e882b x @@ -2204,8 +2211,8 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350 F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7 F tool/warnings.sh 49a486c5069de041aedcbde4de178293e0463ae9918ecad7539eedf0ec77a139 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 7eb5accb7cf937fc967dcd86da0af813fb18a2697348bd231fbefd3c09b930ab -R a7261f9e35c03d513b32b9d4fcb3d611 +P d2d6a000fb9bf8097e0ce9979685408d183be3ab785ceeb11ec1f97a81a83e41 75535f2355b3b2e83dd57f4c30340af98c8dbcfe6ff1e9be17d23bd30d7d766c +R 4f8d4a49a69f37717318dd9062d1384e U dan -Z c73a12f9f64486e78d8e69c08a89db7b +Z 6f5c2c68e6ed575542ad7a68cf45ae3a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 27fe52162f..315c9000b9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d2d6a000fb9bf8097e0ce9979685408d183be3ab785ceeb11ec1f97a81a83e41 +e5ec5bb9f4dc3e02db7ab0e49686f47617af75d3f7d4ab23288a1aea4a693e59 diff --git a/src/alter.c b/src/alter.c index ff20757589..5f706b513f 100644 --- a/src/alter.c +++ b/src/alter.c @@ -632,10 +632,8 @@ void sqlite3AlterRenameColumn( ** altered. Set iCol to be the index of the column being renamed */ zOld = sqlite3NameFromToken(db, pOld); if( !zOld ) goto exit_rename_column; - for(iCol=0; iColnCol; iCol++){ - if( 0==sqlite3StrICmp(pTab->aCol[iCol].zCnName, zOld) ) break; - } - if( iCol==pTab->nCol ){ + iCol = sqlite3ColumnIndex(pTab, zOld); + if( iCol<0 ){ sqlite3ErrorMsg(pParse, "no such column: \"%T\"", pOld); goto exit_rename_column; } diff --git a/src/analyze.c b/src/analyze.c index 9213c202b7..799d43924c 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -215,7 +215,8 @@ static void openStatTable( sqlite3NestedParse(pParse, "CREATE TABLE %Q.%s(%s)", pDb->zDbSName, zTab, aTable[i].zCols ); - aRoot[i] = (u32)pParse->regRoot; + assert( pParse->isCreate || pParse->nErr ); + aRoot[i] = (u32)pParse->u1.cr.regRoot; aCreateTbl[i] = OPFLAG_P2ISREG; } }else{ diff --git a/src/attach.c b/src/attach.c index 3ade237b5c..0812040def 100644 --- a/src/attach.c +++ b/src/attach.c @@ -175,6 +175,12 @@ static void attachFunc( sqlite3_free(zErr); return; } + if( (db->flags & SQLITE_AttachWrite)==0 ){ + flags &= ~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE); + flags |= SQLITE_OPEN_READONLY; + }else if( (db->flags & SQLITE_AttachCreate)==0 ){ + flags &= ~SQLITE_OPEN_CREATE; + } assert( pVfs ); flags |= SQLITE_OPEN_MAIN_DB; rc = sqlite3BtreeOpen(pVfs, zPath, db, &pNew->pBt, 0, flags); diff --git a/src/build.c b/src/build.c index a5deb54fc6..8f64d5ec30 100644 --- a/src/build.c +++ b/src/build.c @@ -168,10 +168,12 @@ void sqlite3FinishCoding(Parse *pParse){ || sqlite3VdbeAssertMayAbort(v, pParse->mayAbort)); if( v ){ if( pParse->bReturning ){ - Returning *pReturning = pParse->u1.pReturning; + Returning *pReturning; int addrRewind; int reg; + assert( !pParse->isCreate ); + pReturning = pParse->u1.d.pReturning; if( pReturning->nRetCol ){ sqlite3VdbeAddOp0(v, OP_FkCheck); addrRewind = @@ -247,7 +249,9 @@ void sqlite3FinishCoding(Parse *pParse){ } if( pParse->bReturning ){ - Returning *pRet = pParse->u1.pReturning; + Returning *pRet; + assert( !pParse->isCreate ); + pRet = pParse->u1.d.pReturning; if( pRet->nRetCol ){ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRet->iRetCur, pRet->nRetCol); } @@ -1319,8 +1323,9 @@ void sqlite3StartTable( /* If the file format and encoding in the database have not been set, ** set them now. */ - reg1 = pParse->regRowid = ++pParse->nMem; - reg2 = pParse->regRoot = ++pParse->nMem; + assert( pParse->isCreate ); + reg1 = pParse->u1.cr.regRowid = ++pParse->nMem; + reg2 = pParse->u1.cr.regRoot = ++pParse->nMem; reg3 = ++pParse->nMem; sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, reg3, BTREE_FILE_FORMAT); sqlite3VdbeUsesBtree(v, iDb); @@ -1335,8 +1340,8 @@ 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 in register pParse->regRowid. - ** The root page number of the new table is left in reg pParse->regRoot. + ** The rowid for the new entry is left in register pParse->u1.cr.regRowid. + ** The root page of the new table is left in reg pParse->u1.cr.regRoot. ** The rowid and root page number values are needed by the code that ** sqlite3EndTable will generate. */ @@ -1347,7 +1352,7 @@ void sqlite3StartTable( #endif { assert( !pParse->bReturning ); - pParse->u1.addrCrTab = + pParse->u1.cr.addrCrTab = sqlite3VdbeAddOp3(v, OP_CreateBtree, iDb, reg2, BTREE_INTKEY); } sqlite3OpenSchemaTable(pParse, iDb); @@ -1425,7 +1430,8 @@ void sqlite3AddReturning(Parse *pParse, ExprList *pList){ sqlite3ExprListDelete(db, pList); return; } - pParse->u1.pReturning = pRet; + assert( !pParse->isCreate ); + pParse->u1.d.pReturning = pRet; pRet->pParse = pParse; pRet->pReturnEL = pList; sqlite3ParserAddCleanup(pParse, sqlite3DeleteReturning, pRet); @@ -1467,7 +1473,6 @@ void sqlite3AddColumn(Parse *pParse, Token sName, Token sType){ char *zType; Column *pCol; sqlite3 *db = pParse->db; - u8 hName; Column *aNew; u8 eType = COLTYPE_CUSTOM; u8 szEst = 1; @@ -1521,13 +1526,10 @@ void sqlite3AddColumn(Parse *pParse, Token sName, Token sType){ memcpy(z, sName.z, sName.n); z[sName.n] = 0; sqlite3Dequote(z); - hName = sqlite3StrIHash(z); - for(i=0; inCol; i++){ - if( p->aCol[i].hName==hName && sqlite3StrICmp(z, p->aCol[i].zCnName)==0 ){ - sqlite3ErrorMsg(pParse, "duplicate column name: %s", z); - sqlite3DbFree(db, z); - return; - } + if( p->nCol && sqlite3ColumnIndex(p, z)>=0 ){ + sqlite3ErrorMsg(pParse, "duplicate column name: %s", z); + sqlite3DbFree(db, z); + return; } aNew = sqlite3DbRealloc(db,p->aCol,((i64)p->nCol+1)*sizeof(p->aCol[0])); if( aNew==0 ){ @@ -1538,7 +1540,7 @@ void sqlite3AddColumn(Parse *pParse, Token sName, Token sType){ pCol = &p->aCol[p->nCol]; memset(pCol, 0, sizeof(p->aCol[0])); pCol->zCnName = z; - pCol->hName = hName; + pCol->hName = sqlite3StrIHash(z); sqlite3ColumnPropertiesFromName(p, pCol); if( sType.n==0 ){ @@ -1562,9 +1564,14 @@ void sqlite3AddColumn(Parse *pParse, Token sName, Token sType){ pCol->affinity = sqlite3AffinityType(zType, pCol); pCol->colFlags |= COLFLAG_HASTYPE; } + if( p->nCol<=0xff ){ + u8 h = pCol->hName % sizeof(p->aHx); + p->aHx[h] = p->nCol; + } p->nCol++; p->nNVCol++; - pParse->constraintName.n = 0; + assert( pParse->isCreate ); + pParse->u1.cr.constraintName.n = 0; } /* @@ -1828,15 +1835,11 @@ void sqlite3AddPrimaryKey( assert( pCExpr!=0 ); sqlite3StringToId(pCExpr); if( pCExpr->op==TK_ID ){ - const char *zCName; assert( !ExprHasProperty(pCExpr, EP_IntValue) ); - zCName = pCExpr->u.zToken; - for(iCol=0; iColnCol; iCol++){ - if( sqlite3StrICmp(zCName, pTab->aCol[iCol].zCnName)==0 ){ - pCol = &pTab->aCol[iCol]; - makeColumnPartOfPrimaryKey(pParse, pCol); - break; - } + iCol = sqlite3ColumnIndex(pTab, pCExpr->u.zToken); + if( iCol>=0 ){ + pCol = &pTab->aCol[iCol]; + makeColumnPartOfPrimaryKey(pParse, pCol); } } } @@ -1888,8 +1891,10 @@ void sqlite3AddCheckConstraint( && !sqlite3BtreeIsReadonly(db->aDb[db->init.iDb].pBt) ){ pTab->pCheck = sqlite3ExprListAppend(pParse, pTab->pCheck, pCheckExpr); - if( pParse->constraintName.n ){ - sqlite3ExprListSetName(pParse, pTab->pCheck, &pParse->constraintName, 1); + assert( pParse->isCreate ); + if( pParse->u1.cr.constraintName.n ){ + sqlite3ExprListSetName(pParse, pTab->pCheck, + &pParse->u1.cr.constraintName, 1); }else{ Token t; for(zStart++; sqlite3Isspace(zStart[0]); zStart++){} @@ -2339,9 +2344,9 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ ** into BTREE_BLOBKEY. */ assert( !pParse->bReturning ); - if( pParse->u1.addrCrTab ){ + if( pParse->u1.cr.addrCrTab ){ assert( v ); - sqlite3VdbeChangeP3(v, pParse->u1.addrCrTab, BTREE_BLOBKEY); + sqlite3VdbeChangeP3(v, pParse->u1.cr.addrCrTab, BTREE_BLOBKEY); } /* Locate the PRIMARY KEY index. Or, if this table was originally @@ -2781,7 +2786,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 in register pParse->regRoot. + ** new table is in register pParse->u1.cr.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 @@ -2812,7 +2817,8 @@ void sqlite3EndTable( regRec = ++pParse->nMem; regRowid = ++pParse->nMem; sqlite3MayAbort(pParse); - sqlite3VdbeAddOp3(v, OP_OpenWrite, iCsr, pParse->regRoot, iDb); + assert( pParse->isCreate ); + sqlite3VdbeAddOp3(v, OP_OpenWrite, iCsr, pParse->u1.cr.regRoot, iDb); sqlite3VdbeChangeP5(v, OPFLAG_P2ISREG); addrTop = sqlite3VdbeCurrentAddr(v) + 1; sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop); @@ -2857,6 +2863,7 @@ void sqlite3EndTable( ** schema table. We just need to update that slot with all ** the information we've collected. */ + assert( pParse->isCreate ); sqlite3NestedParse(pParse, "UPDATE %Q." LEGACY_SCHEMA_TABLE " SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q" @@ -2865,9 +2872,9 @@ void sqlite3EndTable( zType, p->zName, p->zName, - pParse->regRoot, + pParse->u1.cr.regRoot, zStmt, - pParse->regRowid + pParse->u1.cr.regRowid ); sqlite3DbFree(db, zStmt); sqlite3ChangeCookie(pParse, iDb); @@ -4691,7 +4698,6 @@ void sqlite3IdListDelete(sqlite3 *db, IdList *pList){ int i; assert( db!=0 ); if( pList==0 ) return; - assert( pList->eU4!=EU4_EXPR ); /* EU4_EXPR mode is not currently used */ for(i=0; inId; i++){ sqlite3DbFree(db, pList->a[i].zName); } diff --git a/src/callback.c b/src/callback.c index c36d51a4ec..6fe21a2956 100644 --- a/src/callback.c +++ b/src/callback.c @@ -302,12 +302,18 @@ static int matchQuality( u8 enc /* Desired text encoding */ ){ int match; - assert( p->nArg>=-1 ); + assert( p->nArg>=(-4) && p->nArg!=(-2) ); + assert( nArg>=(-2) ); /* Wrong number of arguments means "no match" */ if( p->nArg!=nArg ){ - if( nArg==(-2) ) return (p->xSFunc==0) ? 0 : FUNC_PERFECT_MATCH; + if( nArg==(-2) ) return p->xSFunc==0 ? 0 : FUNC_PERFECT_MATCH; if( p->nArg>=0 ) return 0; + /* Special p->nArg values available to built-in functions only: + ** -3 1 or more arguments required + ** -4 2 or more arguments required + */ + if( p->nArg<(-2) && nArg<(-2-p->nArg) ) return 0; } /* Give a better score to a function with a specific number of arguments diff --git a/src/date.c b/src/date.c index 8c48a81fa5..de27366370 100644 --- a/src/date.c +++ b/src/date.c @@ -222,6 +222,9 @@ static int parseHhMmSs(const char *zDate, DateTime *p){ zDate++; } ms /= rScale; + /* Truncate to avoid problems with sub-milliseconds + ** rounding. https://sqlite.org/forum/forumpost/766a2c9231 */ + if( ms>0.999 ) ms = 0.999; } }else{ s = 0; @@ -1429,7 +1432,7 @@ static void strftimeFunc( } case 'f': { /* Fractional seconds. (Non-standard) */ double s = x.s; - if( s>59.999 ) s = 59.999; + if( NEVER(s>59.999) ) s = 59.999; sqlite3_str_appendf(&sRes, "%06.3f", s); break; } diff --git a/src/dbpage.c b/src/dbpage.c index 74f345570e..eb5ab33fe1 100644 --- a/src/dbpage.c +++ b/src/dbpage.c @@ -317,6 +317,24 @@ static int dbpageRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ return SQLITE_OK; } +/* +** Open write transactions. Since we do not know in advance which database +** files will be written by the sqlite_dbpage virtual table, start a write +** transaction on them all. +** +** Return SQLITE_OK if successful, or an SQLite error code otherwise. +*/ +static int dbpageBeginTrans(DbpageTable *pTab){ + sqlite3 *db = pTab->db; + int rc = SQLITE_OK; + int i; + for(i=0; rc==SQLITE_OK && inDb; i++){ + Btree *pBt = db->aDb[i].pBt; + if( pBt ) rc = sqlite3BtreeBeginTrans(pBt, 1, 0); + } + return rc; +} + static int dbpageUpdate( sqlite3_vtab *pVtab, int argc, @@ -384,6 +402,12 @@ static int dbpageUpdate( goto update_fail; } } + + if( dbpageBeginTrans(pTab)!=SQLITE_OK ){ + zErr = "failed to open transaction"; + goto update_fail; + } + pPager = sqlite3BtreePager(pBt); rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pDbPage, 0); if( rc==SQLITE_OK ){ @@ -400,23 +424,14 @@ static int dbpageUpdate( return rc; update_fail: + pTab->pgnoTrunc = 0; sqlite3_free(pVtab->zErrMsg); pVtab->zErrMsg = sqlite3_mprintf("%s", zErr); return SQLITE_ERROR; } -/* Since we do not know in advance which database files will be -** written by the sqlite_dbpage virtual table, start a write transaction -** on them all. -*/ static int dbpageBegin(sqlite3_vtab *pVtab){ DbpageTable *pTab = (DbpageTable *)pVtab; - sqlite3 *db = pTab->db; - int i; - for(i=0; inDb; i++){ - Btree *pBt = db->aDb[i].pBt; - if( pBt ) (void)sqlite3BtreeBeginTrans(pBt, 1, 0); - } pTab->pgnoTrunc = 0; return SQLITE_OK; } @@ -452,7 +467,7 @@ static int dbpageRollbackTo(sqlite3_vtab *pVtab, int notUsed1){ */ int sqlite3DbpageRegister(sqlite3 *db){ static sqlite3_module dbpage_module = { - 0, /* iVersion */ + 2, /* iVersion */ dbpageConnect, /* xCreate */ dbpageConnect, /* xConnect */ dbpageBestIndex, /* xBestIndex */ diff --git a/src/expr.c b/src/expr.c index 86c966683c..3011fcd9ad 100644 --- a/src/expr.c +++ b/src/expr.c @@ -1849,7 +1849,6 @@ ExprList *sqlite3ExprListDup(sqlite3 *db, const ExprList *p, int flags){ } pItem->zEName = sqlite3DbStrDup(db, pOldItem->zEName); pItem->fg = pOldItem->fg; - pItem->fg.done = 0; pItem->u = pOldItem->u; } return pNew; @@ -1932,16 +1931,13 @@ IdList *sqlite3IdListDup(sqlite3 *db, const IdList *p){ int i; assert( db!=0 ); if( p==0 ) return 0; - assert( p->eU4!=EU4_EXPR ); pNew = sqlite3DbMallocRawNN(db, sizeof(*pNew)+(p->nId-1)*sizeof(p->a[0]) ); if( pNew==0 ) return 0; pNew->nId = p->nId; - pNew->eU4 = p->eU4; for(i=0; inId; i++){ struct IdList_item *pNewItem = &pNew->a[i]; const struct IdList_item *pOldItem = &p->a[i]; pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName); - pNewItem->u4 = pOldItem->u4; } return pNew; } @@ -2969,13 +2965,7 @@ const char *sqlite3RowidAlias(Table *pTab){ int ii; assert( VisibleRowid(pTab) ); for(ii=0; iinCol; iCol++){ - if( sqlite3_stricmp(azOpt[ii], pTab->aCol[iCol].zCnName)==0 ) break; - } - if( iCol==pTab->nCol ){ - return azOpt[ii]; - } + if( sqlite3ColumnIndex(pTab, azOpt[ii])<0 ) return azOpt[ii]; } return 0; } @@ -3285,6 +3275,7 @@ int sqlite3FindInIndex( if( aiMap ) aiMap[i] = j; } + assert( nExpr>0 && nExpropcode==OP_BeginSubrtn ); pSig = pOp->p4.pSubrtnSig; assert( pSig!=0 ); + if( !pSig->bComplete ) continue; if( pNewSig->selId!=pSig->selId ) continue; if( strcmp(pNewSig->zAff,pSig->zAff)!=0 ) continue; pExpr->y.sub.iAddr = pSig->iAddr; @@ -3510,6 +3502,7 @@ void sqlite3CodeRhsOfIN( KeyInfo *pKeyInfo = 0; /* Key information */ int nVal; /* Size of vector pLeft */ Vdbe *v; /* The prepared statement under construction */ + SubrtnSig *pSig = 0; /* Signature for this subroutine */ v = pParse->pVdbe; assert( v!=0 ); @@ -3530,7 +3523,6 @@ void sqlite3CodeRhsOfIN( ** Compute a signature for the RHS of the IN operator to facility ** finding and reusing prior instances of the same IN operator. */ - SubrtnSig *pSig = 0; assert( !ExprUseXSelect(pExpr) || pExpr->x.pSelect!=0 ); if( ExprUseXSelect(pExpr) && (pExpr->x.pSelect->selFlags & SF_All)==0 ){ pSig = sqlite3DbMallocRawNN(pParse->db, sizeof(pSig[0])); @@ -3573,6 +3565,7 @@ void sqlite3CodeRhsOfIN( pExpr->y.sub.iAddr = sqlite3VdbeAddOp2(v, OP_BeginSubrtn, 0, pExpr->y.sub.regReturn) + 1; if( pSig ){ + pSig->bComplete = 0; pSig->iAddr = pExpr->y.sub.iAddr; pSig->regReturn = pExpr->y.sub.regReturn; pSig->iTable = iTab; @@ -3708,6 +3701,7 @@ void sqlite3CodeRhsOfIN( sqlite3ReleaseTempReg(pParse, r1); sqlite3ReleaseTempReg(pParse, r2); } + if( pSig ) pSig->bComplete = 1; if( pKeyInfo ){ sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO); } diff --git a/src/func.c b/src/func.c index a4b72ecc4d..52462b4682 100644 --- a/src/func.c +++ b/src/func.c @@ -354,14 +354,8 @@ static void substrFunc( int len; int p0type; i64 p1, p2; - int negP2 = 0; assert( argc==3 || argc==2 ); - if( sqlite3_value_type(argv[1])==SQLITE_NULL - || (argc==3 && sqlite3_value_type(argv[2])==SQLITE_NULL) - ){ - return; - } p0type = sqlite3_value_type(argv[0]); p1 = sqlite3_value_int64(argv[1]); if( p0type==SQLITE_BLOB ){ @@ -379,28 +373,31 @@ static void substrFunc( } } } -#ifdef SQLITE_SUBSTR_COMPATIBILITY - /* If SUBSTR_COMPATIBILITY is defined then substr(X,0,N) work the same as - ** as substr(X,1,N) - it returns the first N characters of X. This - ** is essentially a back-out of the bug-fix in check-in [5fc125d362df4b8] - ** from 2009-02-02 for compatibility of applications that exploited the - ** old buggy behavior. */ - if( p1==0 ) p1 = 1; /* */ -#endif if( argc==3 ){ p2 = sqlite3_value_int64(argv[2]); - if( p2<0 ){ - p2 = -p2; - negP2 = 1; - } + if( p2==0 && sqlite3_value_type(argv[2])==SQLITE_NULL ) return; }else{ p2 = sqlite3_context_db_handle(context)->aLimit[SQLITE_LIMIT_LENGTH]; } + if( p1==0 ){ +#ifdef SQLITE_SUBSTR_COMPATIBILITY + /* If SUBSTR_COMPATIBILITY is defined then substr(X,0,N) work the same as + ** as substr(X,1,N) - it returns the first N characters of X. This + ** is essentially a back-out of the bug-fix in check-in [5fc125d362df4b8] + ** from 2009-02-02 for compatibility of applications that exploited the + ** old buggy behavior. */ + p1 = 1; /* */ +#endif + if( sqlite3_value_type(argv[1])==SQLITE_NULL ) return; + } if( p1<0 ){ p1 += len; if( p1<0 ){ - p2 += p1; - if( p2<0 ) p2 = 0; + if( p2<0 ){ + p2 = 0; + }else{ + p2 += p1; + } p1 = 0; } }else if( p1>0 ){ @@ -408,12 +405,13 @@ static void substrFunc( }else if( p2>0 ){ p2--; } - if( negP2 ){ - p1 -= p2; - if( p1<0 ){ - p2 += p1; - p1 = 0; + if( p2<0 ){ + if( p2<-p1 ){ + p2 = p1; + }else{ + p2 = -p2; } + p1 -= p2; } assert( p1>=0 && p2>=0 ); if( p0type!=SQLITE_BLOB ){ @@ -1870,7 +1868,10 @@ static void sumInverse(sqlite3_context *context, int argc, sqlite3_value**argv){ assert( p->cnt>0 ); p->cnt--; if( !p->approx ){ - p->iSum -= sqlite3_value_int64(argv[0]); + if( sqlite3SubInt64(&p->iSum, sqlite3_value_int64(argv[0])) ){ + p->ovrfl = 1; + p->approx = 1; + } }else if( type==SQLITE_INTEGER ){ i64 iVal = sqlite3_value_int64(argv[0]); if( iVal!=SMALLEST_INT64 ){ @@ -2696,12 +2697,10 @@ void sqlite3RegisterBuiltinFunctions(void){ FUNCTION(rtrim, 2, 2, 0, trimFunc ), FUNCTION(trim, 1, 3, 0, trimFunc ), FUNCTION(trim, 2, 3, 0, trimFunc ), - FUNCTION(min, -1, 0, 1, minmaxFunc ), - FUNCTION(min, 0, 0, 1, 0 ), + FUNCTION(min, -3, 0, 1, minmaxFunc ), WAGGREGATE(min, 1, 0, 1, minmaxStep, minMaxFinalize, minMaxValue, 0, SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER ), - FUNCTION(max, -1, 1, 1, minmaxFunc ), - FUNCTION(max, 0, 1, 1, 0 ), + FUNCTION(max, -3, 1, 1, minmaxFunc ), WAGGREGATE(max, 1, 1, 1, minmaxStep, minMaxFinalize, minMaxValue, 0, SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER ), FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF), @@ -2728,11 +2727,8 @@ void sqlite3RegisterBuiltinFunctions(void){ FUNCTION(hex, 1, 0, 0, hexFunc ), FUNCTION(unhex, 1, 0, 0, unhexFunc ), FUNCTION(unhex, 2, 0, 0, unhexFunc ), - FUNCTION(concat, -1, 0, 0, concatFunc ), - FUNCTION(concat, 0, 0, 0, 0 ), - FUNCTION(concat_ws, -1, 0, 0, concatwsFunc ), - FUNCTION(concat_ws, 0, 0, 0, 0 ), - FUNCTION(concat_ws, 1, 0, 0, 0 ), + FUNCTION(concat, -3, 0, 0, concatFunc ), + FUNCTION(concat_ws, -4, 0, 0, concatwsFunc ), INLINE_FUNC(ifnull, 2, INLINEFUNC_coalesce, 0 ), VFUNCTION(random, 0, 0, 0, randomFunc ), VFUNCTION(randomblob, 1, 0, 0, randomBlob ), @@ -2776,8 +2772,6 @@ void sqlite3RegisterBuiltinFunctions(void){ #ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION FUNCTION(unknown, -1, 0, 0, unknownFunc ), #endif - FUNCTION(coalesce, 1, 0, 0, 0 ), - FUNCTION(coalesce, 0, 0, 0, 0 ), #ifdef SQLITE_ENABLE_MATH_FUNCTIONS MFUNCTION(ceil, 1, xCeil, ceilingFunc ), MFUNCTION(ceiling, 1, xCeil, ceilingFunc ), @@ -2815,11 +2809,9 @@ void sqlite3RegisterBuiltinFunctions(void){ MFUNCTION(pi, 0, 0, piFunc ), #endif /* SQLITE_ENABLE_MATH_FUNCTIONS */ FUNCTION(sign, 1, 0, 0, signFunc ), - INLINE_FUNC(coalesce, -1, INLINEFUNC_coalesce, 0 ), - INLINE_FUNC(iif, 2, INLINEFUNC_iif, 0 ), - INLINE_FUNC(iif, 3, INLINEFUNC_iif, 0 ), - INLINE_FUNC(if, 2, INLINEFUNC_iif, 0 ), - INLINE_FUNC(if, 3, INLINEFUNC_iif, 0 ), + INLINE_FUNC(coalesce, -4, INLINEFUNC_coalesce, 0 ), + INLINE_FUNC(iif, -4, INLINEFUNC_iif, 0 ), + INLINE_FUNC(if, -4, INLINEFUNC_iif, 0 ), }; #ifndef SQLITE_OMIT_ALTERTABLE sqlite3AlterFunctions(); diff --git a/src/hash.c b/src/hash.c index 8ec043f119..8cc6c09663 100644 --- a/src/hash.c +++ b/src/hash.c @@ -54,12 +54,19 @@ void sqlite3HashClear(Hash *pH){ */ static unsigned int strHash(const char *z){ unsigned int h = 0; - unsigned char c; - while( (c = (unsigned char)*z++)!=0 ){ /*OPTIMIZATION-IF-TRUE*/ + while( z[0] ){ /*OPTIMIZATION-IF-TRUE*/ /* Knuth multiplicative hashing. (Sorting & Searching, p. 510). ** 0x9e3779b1 is 2654435761 which is the closest prime number to - ** (2**32)*golden_ratio, where golden_ratio = (sqrt(5) - 1)/2. */ - h += sqlite3UpperToLower[c]; + ** (2**32)*golden_ratio, where golden_ratio = (sqrt(5) - 1)/2. + ** + ** Only bits 0xdf for ASCII and bits 0xbf for EBCDIC each octet are + ** hashed since the omitted bits determine the upper/lower case difference. + */ +#ifdef SQLITE_EBCDIC + h += 0xbf & (unsigned char)*(z++); +#else + h += 0xdf & (unsigned char)*(z++); +#endif h *= 0x9e3779b1; } return h; @@ -132,9 +139,8 @@ static int rehash(Hash *pH, unsigned int new_size){ pH->htsize = new_size = sqlite3MallocSize(new_ht)/sizeof(struct _ht); memset(new_ht, 0, new_size*sizeof(struct _ht)); for(elem=pH->first, pH->first=0; elem; elem = next_elem){ - unsigned int h = strHash(elem->pKey) % new_size; next_elem = elem->next; - insertElement(pH, &new_ht[h], elem); + insertElement(pH, &new_ht[elem->h % new_size], elem); } return 1; } @@ -152,23 +158,22 @@ static HashElem *findElementWithHash( HashElem *elem; /* Used to loop thru the element list */ unsigned int count; /* Number of elements left to test */ unsigned int h; /* The computed hash */ - static HashElem nullElement = { 0, 0, 0, 0 }; + static HashElem nullElement = { 0, 0, 0, 0, 0 }; + h = strHash(pKey); if( pH->ht ){ /*OPTIMIZATION-IF-TRUE*/ struct _ht *pEntry; - h = strHash(pKey) % pH->htsize; - pEntry = &pH->ht[h]; + pEntry = &pH->ht[h % pH->htsize]; elem = pEntry->chain; count = pEntry->count; }else{ - h = 0; elem = pH->first; count = pH->count; } if( pHash ) *pHash = h; while( count ){ assert( elem!=0 ); - if( sqlite3StrICmp(elem->pKey,pKey)==0 ){ + if( h==elem->h && sqlite3StrICmp(elem->pKey,pKey)==0 ){ return elem; } elem = elem->next; @@ -180,10 +185,9 @@ static HashElem *findElementWithHash( /* Remove a single entry from the hash table given a pointer to that ** element and a hash on the element's key. */ -static void removeElementGivenHash( +static void removeElement( Hash *pH, /* The pH containing "elem" */ - HashElem* elem, /* The element to be removed from the pH */ - unsigned int h /* Hash value for the element */ + HashElem *elem /* The element to be removed from the pH */ ){ struct _ht *pEntry; if( elem->prev ){ @@ -195,7 +199,7 @@ static void removeElementGivenHash( elem->next->prev = elem->prev; } if( pH->ht ){ - pEntry = &pH->ht[h]; + pEntry = &pH->ht[elem->h % pH->htsize]; if( pEntry->chain==elem ){ pEntry->chain = elem->next; } @@ -246,7 +250,7 @@ void *sqlite3HashInsert(Hash *pH, const char *pKey, void *data){ if( elem->data ){ void *old_data = elem->data; if( data==0 ){ - removeElementGivenHash(pH,elem,h); + removeElement(pH,elem); }else{ elem->data = data; elem->pKey = pKey; @@ -257,14 +261,12 @@ void *sqlite3HashInsert(Hash *pH, const char *pKey, void *data){ new_elem = (HashElem*)sqlite3Malloc( sizeof(HashElem) ); if( new_elem==0 ) return data; new_elem->pKey = pKey; + new_elem->h = h; new_elem->data = data; pH->count++; - if( pH->count>=10 && pH->count > 2*pH->htsize ){ - if( rehash(pH, pH->count*2) ){ - assert( pH->htsize>0 ); - h = strHash(pKey) % pH->htsize; - } + if( pH->count>=5 && pH->count > 2*pH->htsize ){ + rehash(pH, pH->count*3); } - insertElement(pH, pH->ht ? &pH->ht[h] : 0, new_elem); + insertElement(pH, pH->ht ? &pH->ht[new_elem->h % pH->htsize] : 0, new_elem); return 0; } diff --git a/src/hash.h b/src/hash.h index 3f491e45c0..cff65d6e50 100644 --- a/src/hash.h +++ b/src/hash.h @@ -60,6 +60,7 @@ struct HashElem { HashElem *next, *prev; /* Next and previous elements in the table */ void *data; /* Data associated with this element */ const char *pKey; /* Key associated with this element */ + unsigned int h; /* hash for pKey */ }; /* diff --git a/src/insert.c b/src/insert.c index d380281bed..c1ca1897ed 100644 --- a/src/insert.c +++ b/src/insert.c @@ -927,6 +927,7 @@ void sqlite3Insert( int regRowid; /* registers holding insert rowid */ int regData; /* register holding first column to insert */ int *aRegIdx = 0; /* One register allocated to each index */ + int *aTabColMap = 0; /* Mapping from pTab columns to pCol entries */ #ifndef SQLITE_OMIT_TRIGGER int isView; /* True if attempting to insert into a view */ @@ -1071,31 +1072,25 @@ void sqlite3Insert( */ bIdListInOrder = (pTab->tabFlags & (TF_OOOHidden|TF_HasStored))==0; if( pColumn ){ - assert( pColumn->eU4!=EU4_EXPR ); - pColumn->eU4 = EU4_IDX; + aTabColMap = sqlite3DbMallocZero(db, pTab->nCol*sizeof(int)); + if( aTabColMap==0 ) goto insert_cleanup; for(i=0; inId; i++){ - pColumn->a[i].u4.idx = -1; - } - for(i=0; inId; i++){ - for(j=0; jnCol; j++){ - if( sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zCnName)==0 ){ - pColumn->a[i].u4.idx = j; - if( i!=j ) bIdListInOrder = 0; - if( j==pTab->iPKey ){ - ipkColumn = i; assert( !withoutRowid ); - } -#ifndef SQLITE_OMIT_GENERATED_COLUMNS - if( pTab->aCol[j].colFlags & (COLFLAG_STORED|COLFLAG_VIRTUAL) ){ - sqlite3ErrorMsg(pParse, - "cannot INSERT into generated column \"%s\"", - pTab->aCol[j].zCnName); - goto insert_cleanup; - } -#endif - break; + j = sqlite3ColumnIndex(pTab, pColumn->a[i].zName); + if( j>=0 ){ + if( aTabColMap[j]==0 ) aTabColMap[j] = i+1; + if( i!=j ) bIdListInOrder = 0; + if( j==pTab->iPKey ){ + ipkColumn = i; assert( !withoutRowid ); } - } - if( j>=pTab->nCol ){ +#ifndef SQLITE_OMIT_GENERATED_COLUMNS + if( pTab->aCol[j].colFlags & (COLFLAG_STORED|COLFLAG_VIRTUAL) ){ + sqlite3ErrorMsg(pParse, + "cannot INSERT into generated column \"%s\"", + pTab->aCol[j].zCnName); + goto insert_cleanup; + } +#endif + }else{ if( sqlite3IsRowid(pColumn->a[i].zName) && !withoutRowid ){ ipkColumn = i; bIdListInOrder = 0; @@ -1393,7 +1388,7 @@ void sqlite3Insert( continue; }else if( pColumn==0 ){ /* Hidden columns that are not explicitly named in the INSERT - ** get there default value */ + ** get their default value */ sqlite3ExprCodeFactorable(pParse, sqlite3ColumnExpr(pTab, &pTab->aCol[i]), iRegStore); @@ -1401,9 +1396,9 @@ void sqlite3Insert( } } if( pColumn ){ - assert( pColumn->eU4==EU4_IDX ); - for(j=0; jnId && pColumn->a[j].u4.idx!=i; j++){} - if( j>=pColumn->nId ){ + j = aTabColMap[i]; + assert( j>=0 && j<=pColumn->nId ); + if( j==0 ){ /* A column not named in the insert column list gets its ** default value */ sqlite3ExprCodeFactorable(pParse, @@ -1411,7 +1406,7 @@ void sqlite3Insert( iRegStore); continue; } - k = j; + k = j - 1; }else if( nColumn==0 ){ /* This is INSERT INTO ... DEFAULT VALUES. Load the default value. */ sqlite3ExprCodeFactorable(pParse, @@ -1656,7 +1651,10 @@ insert_cleanup: sqlite3ExprListDelete(db, pList); sqlite3UpsertDelete(db, pUpsert); sqlite3SelectDelete(db, pSelect); - sqlite3IdListDelete(db, pColumn); + if( pColumn ){ + sqlite3IdListDelete(db, pColumn); + sqlite3DbFree(db, aTabColMap); + } if( aRegIdx ) sqlite3DbNNFreeNN(db, aRegIdx); } diff --git a/src/json.c b/src/json.c index a0a075e66c..47a9c875e7 100644 --- a/src/json.c +++ b/src/json.c @@ -2054,10 +2054,7 @@ static u32 jsonbPayloadSize(const JsonParse *pParse, u32 i, u32 *pSz){ u8 x; u32 sz; u32 n; - if( NEVER(i>pParse->nBlob) ){ - *pSz = 0; - return 0; - } + assert( i<=pParse->nBlob ); x = pParse->aBlob[i]>>4; if( x<=11 ){ sz = x; @@ -2101,8 +2098,8 @@ static u32 jsonbPayloadSize(const JsonParse *pParse, u32 i, u32 *pSz){ if( (i64)i+sz+n > pParse->nBlob && (i64)i+sz+n > pParse->nBlob-pParse->delta ){ - sz = 0; - n = 0; + *pSz = 0; + return 0; } *pSz = sz; return n; @@ -2199,9 +2196,12 @@ static u32 jsonTranslateBlobToText( } case JSONB_TEXT: case JSONB_TEXTJ: { - jsonAppendChar(pOut, '"'); - jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz); - jsonAppendChar(pOut, '"'); + if( pOut->nUsed+sz+2<=pOut->nAlloc || jsonStringGrow(pOut, sz+2)==0 ){ + pOut->zBuf[pOut->nUsed] = '"'; + memcpy(pOut->zBuf+pOut->nUsed+1,(const char*)&pParse->aBlob[i+n],sz); + pOut->zBuf[pOut->nUsed+sz+1] = '"'; + pOut->nUsed += sz+2; + } break; } case JSONB_TEXT5: { diff --git a/src/main.c b/src/main.c index 8cd0303c4d..e2d272157f 100644 --- a/src/main.c +++ b/src/main.c @@ -959,7 +959,7 @@ int sqlite3_db_config(sqlite3 *db, int op, ...){ default: { static const struct { int op; /* The opcode */ - u32 mask; /* Mask of the bit in sqlite3.flags to set/clear */ + u64 mask; /* Mask of the bit in sqlite3.flags to set/clear */ } aFlagOp[] = { { SQLITE_DBCONFIG_ENABLE_FKEY, SQLITE_ForeignKeys }, { SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger }, @@ -980,6 +980,9 @@ int sqlite3_db_config(sqlite3 *db, int op, ...){ { SQLITE_DBCONFIG_TRUSTED_SCHEMA, SQLITE_TrustedSchema }, { SQLITE_DBCONFIG_STMT_SCANSTATUS, SQLITE_StmtScanStatus }, { SQLITE_DBCONFIG_REVERSE_SCANORDER, SQLITE_ReverseOrder }, + { SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE, SQLITE_AttachCreate }, + { SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE, SQLITE_AttachWrite }, + { SQLITE_DBCONFIG_ENABLE_COMMENTS, SQLITE_Comments }, }; unsigned int i; rc = SQLITE_ERROR; /* IMP: R-42790-23372 */ @@ -1831,8 +1834,10 @@ int sqlite3_busy_timeout(sqlite3 *db, int ms){ ** Set the setlk timeout value. */ int sqlite3_setlk_timeout(sqlite3 *db, int ms, int flags){ +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT int iDb; int bBOC = ((flags & SQLITE_SETLK_BLOCK_ON_CONNECT) ? 1 : 0); +#endif #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; #endif @@ -3353,6 +3358,9 @@ static int openDatabase( | SQLITE_EnableTrigger | SQLITE_EnableView | SQLITE_CacheSpill + | SQLITE_AttachCreate + | SQLITE_AttachWrite + | SQLITE_Comments #if !defined(SQLITE_TRUSTED_SCHEMA) || SQLITE_TRUSTED_SCHEMA+0!=0 | SQLITE_TrustedSchema #endif @@ -3969,13 +3977,10 @@ int sqlite3_table_column_metadata( if( zColumnName==0 ){ /* Query for existence of table only */ }else{ - for(iCol=0; iColnCol; iCol++){ + iCol = sqlite3ColumnIndex(pTab, zColumnName); + if( iCol>=0 ){ pCol = &pTab->aCol[iCol]; - if( 0==sqlite3StrICmp(pCol->zCnName, zColumnName) ){ - break; - } - } - if( iCol==pTab->nCol ){ + }else{ if( HasRowid(pTab) && sqlite3IsRowid(zColumnName) ){ iCol = pTab->iPKey; pCol = iCol>=0 ? &pTab->aCol[iCol] : 0; diff --git a/src/os_unix.c b/src/os_unix.c index 7f92c5234d..02fe7c819c 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -1665,7 +1665,7 @@ static int unixFileLock(unixFile *pFile, struct flock *pLock){ if( (pFile->ctrlFlags & (UNIXFILE_EXCL|UNIXFILE_RDONLY))==UNIXFILE_EXCL ){ if( pInode->bProcessLock==0 ){ struct flock lock; - assert( pInode->nLock==0 ); + /* assert( pInode->nLock==0 ); <-- Not true if unix-excl READONLY used */ lock.l_whence = SEEK_SET; lock.l_start = SHARED_FIRST; lock.l_len = SHARED_SIZE; diff --git a/src/pager.c b/src/pager.c index e2dbbeae3f..ecec892b45 100644 --- a/src/pager.c +++ b/src/pager.c @@ -808,7 +808,7 @@ int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){ if( pPager->pWal ){ u32 iRead = 0; (void)sqlite3WalFindFrame(pPager->pWal, pgno, &iRead); - return iRead==0; /* Condition (4) */ + if( iRead ) return 0; /* Case (4) */ } #endif assert( pPager->fd->pMethods->xDeviceCharacteristics!=0 ); diff --git a/src/parse.y b/src/parse.y index b8d904d12c..76c9a8e4ed 100644 --- a/src/parse.y +++ b/src/parse.y @@ -62,6 +62,11 @@ %include { #include "sqliteInt.h" +/* +** Verify that the pParse->isCreate field is set +*/ +#define ASSERT_IS_CREATE assert(pParse->isCreate) + /* ** Disable all error recovery processing in the parser push-down ** automaton. @@ -125,6 +130,10 @@ static void parserSyntaxError(Parse *pParse, Token *p){ static void disableLookaside(Parse *pParse){ sqlite3 *db = pParse->db; pParse->disableLookaside++; +#ifdef SQLITE_DEBUG + pParse->isCreate = 1; +#endif + memset(&pParse->u1.cr, 0, sizeof(pParse->u1.cr)); DisableLookaside; } @@ -197,7 +206,9 @@ cmd ::= create_table create_table_args. create_table ::= createkw temp(T) TABLE ifnotexists(E) nm(Y) dbnm(Z). { sqlite3StartTable(pParse,&Y,&Z,T,0,0,E); } -createkw(A) ::= CREATE(A). {disableLookaside(pParse);} +createkw(A) ::= CREATE(A). { + disableLookaside(pParse); +} %type ifnotexists {int} ifnotexists(A) ::= . {A = 0;} @@ -373,7 +384,7 @@ scantok(A) ::= . { // carglist ::= carglist ccons. carglist ::= . -ccons ::= CONSTRAINT nm(X). {pParse->constraintName = X;} +ccons ::= CONSTRAINT nm(X). {ASSERT_IS_CREATE; pParse->u1.cr.constraintName = X;} ccons ::= DEFAULT scantok(A) term(X). {sqlite3AddDefaultValue(pParse,X,A.z,&A.z[A.n]);} ccons ::= DEFAULT LP(A) expr(X) RP(Z). @@ -448,9 +459,9 @@ conslist_opt(A) ::= . {A.n = 0; A.z = 0;} conslist_opt(A) ::= COMMA(A) conslist. conslist ::= conslist tconscomma tcons. conslist ::= tcons. -tconscomma ::= COMMA. {pParse->constraintName.n = 0;} +tconscomma ::= COMMA. {ASSERT_IS_CREATE; pParse->u1.cr.constraintName.n = 0;} tconscomma ::= . -tcons ::= CONSTRAINT nm(X). {pParse->constraintName = X;} +tcons ::= CONSTRAINT nm(X). {ASSERT_IS_CREATE; pParse->u1.cr.constraintName = X;} tcons ::= PRIMARY KEY LP sortlist(X) autoinc(I) RP onconf(R). {sqlite3AddPrimaryKey(pParse,X,R,I,0);} tcons ::= UNIQUE LP sortlist(X) RP onconf(R). @@ -1659,6 +1670,10 @@ trigger_decl(A) ::= temp(T) TRIGGER ifnotexists(NOERR) nm(B) dbnm(Z) ON fullname(E) foreach_clause when_clause(G). { sqlite3BeginTrigger(pParse, &B, &Z, C, D.a, D.b, E, G, T, NOERR); A = (Z.n==0?B:Z); /*A-overwrites-T*/ +#ifdef SQLITE_DEBUG + assert( pParse->isCreate ); /* Set by createkw reduce action */ + pParse->isCreate = 0; /* But, should not be set for CREATE TRIGGER */ +#endif } %type trigger_time {int} @@ -1882,7 +1897,8 @@ wqlist(A) ::= wqlist(A) COMMA wqitem(X). { // These must be at the end of this file. Specifically, the rules that // introduce tokens WINDOW, OVER and FILTER must appear last. This causes // the integer values assigned to these tokens to be larger than all other -// tokens that may be output by the tokenizer except TK_SPACE and TK_ILLEGAL. +// tokens that may be output by the tokenizer except TK_SPACE, TK_COMMENT, +// and TK_ILLEGAL. // %ifndef SQLITE_OMIT_WINDOWFUNC %type windowdefn_list {Window*} @@ -2059,9 +2075,9 @@ term(A) ::= QNUMBER(X). { } /* -** The TK_SPACE and TK_ILLEGAL tokens must be the last two tokens. The -** parser depends on this. Those tokens are not used in any grammar rule. -** They are only used by the tokenizer. Declare them last so that they -** are guaranteed to be the last two tokens +** The TK_SPACE, TK_COMMENT, and TK_ILLEGAL tokens must be the last three +** tokens. The parser depends on this. Those tokens are not used in any +** grammar rule. They are only used by the tokenizer. Declare them last +** so that they are guaranteed to be the last three. */ -%token SPACE ILLEGAL. +%token SPACE COMMENT ILLEGAL. diff --git a/src/resolve.c b/src/resolve.c index d6a5144af8..54ce4fb1ea 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -294,7 +294,6 @@ static int lookupName( Schema *pSchema = 0; /* Schema of the expression */ int eNewExprOp = TK_COLUMN; /* New value for pExpr->op on success */ Table *pTab = 0; /* Table holding the row */ - Column *pCol; /* A column of pTab */ ExprList *pFJMatch = 0; /* Matches for FULL JOIN .. USING */ const char *zCol = pRight->u.zToken; @@ -345,7 +344,6 @@ static int lookupName( if( pSrcList ){ for(i=0, pItem=pSrcList->a; inSrc; i++, pItem++){ - u8 hCol; pTab = pItem->pSTab; assert( pTab!=0 && pTab->zName!=0 ); assert( pTab->nCol>0 || pParse->nErr ); @@ -433,43 +431,38 @@ static int lookupName( sqlite3RenameTokenRemap(pParse, 0, (void*)&pExpr->y.pTab); } } - hCol = sqlite3StrIHash(zCol); - for(j=0, pCol=pTab->aCol; jnCol; j++, pCol++){ - if( pCol->hName==hCol - && sqlite3StrICmp(pCol->zCnName, zCol)==0 - ){ - if( cnt>0 ){ - if( pItem->fg.isUsing==0 - || sqlite3IdListIndex(pItem->u3.pUsing, zCol)<0 - ){ - /* Two or more tables have the same column name which is - ** not joined by USING. This is an error. Signal as much - ** by clearing pFJMatch and letting cnt go above 1. */ - sqlite3ExprListDelete(db, pFJMatch); - pFJMatch = 0; - }else - if( (pItem->fg.jointype & JT_RIGHT)==0 ){ - /* An INNER or LEFT JOIN. Use the left-most table */ - continue; - }else - if( (pItem->fg.jointype & JT_LEFT)==0 ){ - /* A RIGHT JOIN. Use the right-most table */ - cnt = 0; - sqlite3ExprListDelete(db, pFJMatch); - pFJMatch = 0; - }else{ - /* For a FULL JOIN, we must construct a coalesce() func */ - extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn); - } + j = sqlite3ColumnIndex(pTab, zCol); + if( j>=0 ){ + if( cnt>0 ){ + if( pItem->fg.isUsing==0 + || sqlite3IdListIndex(pItem->u3.pUsing, zCol)<0 + ){ + /* Two or more tables have the same column name which is + ** not joined by USING. This is an error. Signal as much + ** by clearing pFJMatch and letting cnt go above 1. */ + sqlite3ExprListDelete(db, pFJMatch); + pFJMatch = 0; + }else + if( (pItem->fg.jointype & JT_RIGHT)==0 ){ + /* An INNER or LEFT JOIN. Use the left-most table */ + continue; + }else + if( (pItem->fg.jointype & JT_LEFT)==0 ){ + /* A RIGHT JOIN. Use the right-most table */ + cnt = 0; + sqlite3ExprListDelete(db, pFJMatch); + pFJMatch = 0; + }else{ + /* For a FULL JOIN, we must construct a coalesce() func */ + extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn); } - cnt++; - pMatch = pItem; - /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ - pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j; - if( pItem->fg.isNestedFrom ){ - sqlite3SrcItemColumnUsed(pItem, j); - } - break; + } + cnt++; + pMatch = pItem; + /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ + pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j; + if( pItem->fg.isNestedFrom ){ + sqlite3SrcItemColumnUsed(pItem, j); } } if( 0==cnt && VisibleRowid(pTab) ){ @@ -559,23 +552,18 @@ static int lookupName( if( pTab ){ int iCol; - u8 hCol = sqlite3StrIHash(zCol); pSchema = pTab->pSchema; cntTab++; - for(iCol=0, pCol=pTab->aCol; iColnCol; iCol++, pCol++){ - if( pCol->hName==hCol - && sqlite3StrICmp(pCol->zCnName, zCol)==0 - ){ - if( iCol==pTab->iPKey ){ - iCol = -1; - } - break; + iCol = sqlite3ColumnIndex(pTab, zCol); + if( iCol>=0 ){ + if( pTab->iPKey==iCol ) iCol = -1; + }else{ + if( sqlite3IsRowid(zCol) && VisibleRowid(pTab) ){ + iCol = -1; + }else{ + iCol = pTab->nCol; } } - if( iCol>=pTab->nCol && sqlite3IsRowid(zCol) && VisibleRowid(pTab) ){ - /* IMP: R-51414-32910 */ - iCol = -1; - } if( iColnCol ){ cnt++; pMatch = 0; diff --git a/src/select.c b/src/select.c index cf25c8e678..e47a9b6be2 100644 --- a/src/select.c +++ b/src/select.c @@ -319,10 +319,33 @@ int sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token *pC){ */ int sqlite3ColumnIndex(Table *pTab, const char *zCol){ int i; - u8 h = sqlite3StrIHash(zCol); - Column *pCol; - for(pCol=pTab->aCol, i=0; inCol; pCol++, i++){ - if( pCol->hName==h && sqlite3StrICmp(pCol->zCnName, zCol)==0 ) return i; + u8 h; + const Column *aCol; + int nCol; + + h = sqlite3StrIHash(zCol); + aCol = pTab->aCol; + nCol = pTab->nCol; + + /* See if the aHx gives us a lucky match */ + i = pTab->aHx[h % sizeof(pTab->aHx)]; + assert( i=nCol ) break; } return -1; } diff --git a/src/shell.c.in b/src/shell.c.in index cad8c92c57..fcc9316b00 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -7337,7 +7337,7 @@ static int arProcessSwitch(ArCommand *pAr, int eSwitch, const char *zArg){ break; case AR_SWITCH_APPEND: pAr->bAppend = 1; - deliberate_fall_through; + deliberate_fall_through; /* FALLTHRU */ case AR_SWITCH_FILE: pAr->zFile = zArg; break; @@ -8725,6 +8725,9 @@ static int do_meta_command(char *zLine, ShellState *p){ const char *zName; int op; } aDbConfig[] = { + { "attach_create", SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE }, + { "attach_write", SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE }, + { "comments", SQLITE_DBCONFIG_ENABLE_COMMENTS }, { "defensive", SQLITE_DBCONFIG_DEFENSIVE }, { "dqs_ddl", SQLITE_DBCONFIG_DQS_DDL }, { "dqs_dml", SQLITE_DBCONFIG_DQS_DML }, @@ -10079,6 +10082,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( zFile==0 ){ zFile = sqlite3_mprintf("stdout"); } + shell_check_oom(zFile); if( bOnce ){ p->outCount = 2; }else{ @@ -10121,6 +10125,7 @@ static int do_meta_command(char *zLine, ShellState *p){ #else FILE *pfPipe = sqlite3_popen(zFile + 1, "w"); if( pfPipe==0 ){ + assert( stderr!=NULL ); sqlite3_fprintf(stderr,"Error: cannot open pipe \"%s\"\n", zFile + 1); rc = 1; }else{ @@ -10133,7 +10138,8 @@ static int do_meta_command(char *zLine, ShellState *p){ FILE *pfFile = output_file_open(zFile); if( pfFile==0 ){ if( cli_strcmp(zFile,"off")!=0 ){ - sqlite3_fprintf(stderr,"Error: cannot write to \"%s\"\n", zFile); + assert( stderr!=NULL ); + sqlite3_fprintf(stderr,"Error: cannot write to \"%s\"\n", zFile); } rc = 1; } else { @@ -10237,6 +10243,7 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = 1; } } + bind_prepared_stmt(p, pStmt); sqlite3_step(pStmt); sqlite3_finalize(pStmt); }else @@ -11471,6 +11478,7 @@ static int do_meta_command(char *zLine, ShellState *p){ { 0x04000000, 1, "NullUnusedCols" }, { 0x08000000, 1, "OnePass" }, { 0x10000000, 1, "OrderBySubq" }, + { 0x20000000, 1, "StarQuery" }, { 0xffffffff, 0, "All" }, }; unsigned int curOpt; @@ -12025,7 +12033,7 @@ static QuickScanState quickscan(char *zLine, QuickScanState qss, break; case '[': cin = ']'; - deliberate_fall_through; + deliberate_fall_through; /* FALLTHRU */ case '`': case '\'': case '"': cWait = cin; qss = QSS_HasDark | cWait; @@ -12060,7 +12068,7 @@ static QuickScanState quickscan(char *zLine, QuickScanState qss, ++zLine; continue; } - deliberate_fall_through; + deliberate_fall_through; /* FALLTHRU */ case ']': CONTINUE_PROMPT_AWAITC(pst, 0); qss = QSS_SETV(qss, 0); diff --git a/src/sqlite.h.in b/src/sqlite.h.in index d053eb7d7e..bf1974a862 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -2217,7 +2217,15 @@ struct sqlite3_mem_methods { ** CAPI3REF: Database Connection Configuration Options ** ** These constants are the available integer configuration options that -** can be passed as the second argument to the [sqlite3_db_config()] interface. +** can be passed as the second parameter to the [sqlite3_db_config()] interface. +** +** The [sqlite3_db_config()] interface is a var-args functions. It takes a +** variable number of parameters, though always at least two. The number of +** parameters passed into sqlite3_db_config() depends on which of these +** constants is given as the second parameter. This documentation page +** refers to parameters beyond the second as "arguments". Thus, when this +** page says "the N-th argument" it means "the N-th parameter past the +** configuration option" or "the (N+2)-th parameter to sqlite3_db_config()". ** ** New configuration options may be added in future releases of SQLite. ** Existing configuration options might be discontinued. Applications @@ -2229,8 +2237,14 @@ struct sqlite3_mem_methods { **
    ** [[SQLITE_DBCONFIG_LOOKASIDE]] **
    SQLITE_DBCONFIG_LOOKASIDE
    -**
    ^This option takes three additional arguments that determine the -** [lookaside memory allocator] configuration for the [database connection]. +**
    The SQLITE_DBCONFIG_LOOKASIDE option is used to adjust the +** configuration of the lookaside memory allocator within a database +** connection. +** The arguments to the SQLITE_DBCONFIG_LOOKASIDE option are not +** in the [DBCONFIG arguments|usual format]. +** The SQLITE_DBCONFIG_LOOKASIDE option takes three arguments, not two, +** so that a call to [sqlite3_db_config()] that uses SQLITE_DBCONFIG_LOOKASIDE +** should have a total of five parameters. ** ^The first argument (the third parameter to [sqlite3_db_config()] is a ** pointer to a memory buffer to use for lookaside memory. ** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb @@ -2253,7 +2267,8 @@ struct sqlite3_mem_methods { ** [[SQLITE_DBCONFIG_ENABLE_FKEY]] **
    SQLITE_DBCONFIG_ENABLE_FKEY
    **
    ^This option is used to enable or disable the enforcement of -** [foreign key constraints]. There should be two additional arguments. +** [foreign key constraints]. This is the same setting that is +** enabled or disabled by the [PRAGMA foreign_keys] statement. ** The first argument is an integer which is 0 to disable FK enforcement, ** positive to enable FK enforcement or negative to leave FK enforcement ** unchanged. The second parameter is a pointer to an integer into which @@ -2275,13 +2290,13 @@ struct sqlite3_mem_methods { **

    Originally this option disabled all triggers. ^(However, since ** SQLite version 3.35.0, TEMP triggers are still allowed even if ** this option is off. So, in other words, this option now only disables -** triggers in the main database schema or in the schemas of ATTACH-ed +** triggers in the main database schema or in the schemas of [ATTACH]-ed ** databases.)^

    ** ** [[SQLITE_DBCONFIG_ENABLE_VIEW]] **
    SQLITE_DBCONFIG_ENABLE_VIEW
    **
    ^This option is used to enable or disable [CREATE VIEW | views]. -** There should be two additional arguments. +** There must be two additional arguments. ** The first argument is an integer which is 0 to disable views, ** positive to enable views or negative to leave the setting unchanged. ** The second parameter is a pointer to an integer into which @@ -2300,7 +2315,7 @@ struct sqlite3_mem_methods { **
    ^This option is used to enable or disable the ** [fts3_tokenizer()] function which is part of the ** [FTS3] full-text search engine extension. -** There should be two additional arguments. +** There must be two additional arguments. ** The first argument is an integer which is 0 to disable fts3_tokenizer() or ** positive to enable fts3_tokenizer() or negative to leave the setting ** unchanged. @@ -2315,7 +2330,7 @@ struct sqlite3_mem_methods { ** interface independently of the [load_extension()] SQL function. ** The [sqlite3_enable_load_extension()] API enables or disables both the ** C-API [sqlite3_load_extension()] and the SQL function [load_extension()]. -** There should be two additional arguments. +** There must be two additional arguments. ** When the first argument to this interface is 1, then only the C-API is ** enabled and the SQL function remains disabled. If the first argument to ** this interface is 0, then both the C-API and the SQL function are disabled. @@ -2329,23 +2344,30 @@ struct sqlite3_mem_methods { ** ** [[SQLITE_DBCONFIG_MAINDBNAME]]
    SQLITE_DBCONFIG_MAINDBNAME
    **
    ^This option is used to change the name of the "main" database -** schema. ^The sole argument is a pointer to a constant UTF8 string -** which will become the new schema name in place of "main". ^SQLite -** does not make a copy of the new main schema name string, so the application -** must ensure that the argument passed into this DBCONFIG option is unchanged -** until after the database connection closes. +** schema. This option does not follow the +** [DBCONFIG arguments|usual SQLITE_DBCONFIG argument format]. +** This option takes exactly one additional argument so that the +** [sqlite3_db_config()] call has a total of three parameters. The +** extra argument must be a pointer to a constant UTF8 string which +** will become the new schema name in place of "main". ^SQLite does +** not make a copy of the new main schema name string, so the application +** must ensure that the argument passed into SQLITE_DBCONFIG MAINDBNAME +** is unchanged until after the database connection closes. **
    ** ** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]] **
    SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE
    -**
    Usually, when a database in wal mode is closed or detached from a -** database handle, SQLite checks if this will mean that there are now no -** connections at all to the database. If so, it performs a checkpoint -** operation before closing the connection. This option may be used to -** override this behavior. The first parameter passed to this operation -** is an integer - positive to disable checkpoints-on-close, or zero (the -** default) to enable them, and negative to leave the setting unchanged. -** The second parameter is a pointer to an integer +**
    Usually, when a database in [WAL mode] is closed or detached from a +** database handle, SQLite checks if if there are other connections to the +** same database, and if there are no other database connection (if the +** connection being closed is the last open connection to the database), +** then SQLite performs a [checkpoint] before closing the connection and +** deletes the WAL file. The SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE option can +** be used to override that behavior. The first argument passed to this +** operation (the third parameter to [sqlite3_db_config()]) is an integer +** which is positive to disable checkpoints-on-close, or zero (the default) +** to enable them, and negative to leave the setting unchanged. +** The second argument (the fourth parameter) is a pointer to an integer ** into which is written 0 or 1 to indicate whether checkpoints-on-close ** have been disabled - 0 if they are not disabled, 1 if they are. **
    @@ -2506,7 +2528,7 @@ struct sqlite3_mem_methods { ** statistics. For statistics to be collected, the flag must be set on ** the database handle both when the SQL statement is prepared and when it ** is stepped. The flag is set (collection of statistics is enabled) -** by default. This option takes two arguments: an integer and a pointer to +** by default.

    This option takes two arguments: an integer and a pointer to ** an integer.. The first argument is 1, 0, or -1 to enable, disable, or ** leave unchanged the statement scanstatus option. If the second argument ** is not NULL, then the value of the statement scanstatus setting after @@ -2520,7 +2542,7 @@ struct sqlite3_mem_methods { ** in which tables and indexes are scanned so that the scans start at the end ** and work toward the beginning rather than starting at the beginning and ** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the -** same as setting [PRAGMA reverse_unordered_selects]. This option takes +** same as setting [PRAGMA reverse_unordered_selects].

    This option takes ** two arguments which are an integer and a pointer to an integer. The first ** argument is 1, 0, or -1 to enable, disable, or leave unchanged the ** reverse scan order flag, respectively. If the second argument is not NULL, @@ -2529,7 +2551,76 @@ struct sqlite3_mem_methods { ** first argument. ** ** +** [[SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE]] +**

    SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE
    +**
    The SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE option enables or disables +** the ability of the [ATTACH DATABASE] SQL command to create a new database +** file if the database filed named in the ATTACH command does not already +** exist. This ability of ATTACH to create a new database is enabled by +** default. Applications can disable or reenable the ability for ATTACH to +** create new database files using this DBCONFIG option.

    +** This option takes two arguments which are an integer and a pointer +** to an integer. The first argument is 1, 0, or -1 to enable, disable, or +** leave unchanged the attach-create flag, respectively. If the second +** argument is not NULL, then 0 or 1 is written into the integer that the +** second argument points to depending on if the attach-create flag is set +** after processing the first argument. +**

    +** +** [[SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE]] +**
    SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE
    +**
    The SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE option enables or disables the +** ability of the [ATTACH DATABASE] SQL command to open a database for writing. +** This capability is enabled by default. Applications can disable or +** reenable this capability using the current DBCONFIG option. If the +** the this capability is disabled, the [ATTACH] command will still work, +** but the database will be opened read-only. If this option is disabled, +** then the ability to create a new database using [ATTACH] is also disabled, +** regardless of the value of the [SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE] +** option.

    +** This option takes two arguments which are an integer and a pointer +** to an integer. The first argument is 1, 0, or -1 to enable, disable, or +** leave unchanged the ability to ATTACH another database for writing, +** respectively. If the second argument is not NULL, then 0 or 1 is written +** into the integer to which the second argument points, depending on whether +** the ability to ATTACH a read/write database is enabled or disabled +** after processing the first argument. +**

    +** +** [[SQLITE_DBCONFIG_ENABLE_COMMENTS]] +**
    SQLITE_DBCONFIG_ENABLE_COMMENTS
    +**
    The SQLITE_DBCONFIG_ENABLE_COMMENTS option enables or disables the +** ability to include comments in SQL text. Comments are enabled by default. +** An application can disable or reenable comments in SQL text using this +** DBCONFIG option.

    +** This option takes two arguments which are an integer and a pointer +** to an integer. The first argument is 1, 0, or -1 to enable, disable, or +** leave unchanged the ability to use comments in SQL text, +** respectively. If the second argument is not NULL, then 0 or 1 is written +** into the integer that the second argument points to depending on if +** comments are allowed in SQL text after processing the first argument. +**

    +** **
    +** +** [[DBCONFIG arguments]]

    Arguments To SQLITE_DBCONFIG Options

    +** +**

    Most of the SQLITE_DBCONFIG options take two arguments, so that the +** overall call to [sqlite3_db_config()] has a total of four parameters. +** The first argument (the third parameter to sqlite3_db_config()) is a integer. +** The second argument is a pointer to an integer. If the first argument is 1, +** then the option becomes enabled. If the first integer argument is 0, then the +** option is disabled. If the first argument is -1, then the option setting +** is unchanged. The second argument, the pointer to an integer, may be NULL. +** If the second argument is not NULL, then a value of 0 or 1 is written into +** the integer to which the second argument points, depending on whether the +** setting is disabled or enabled after applying any changes specified by +** the first argument. +** +**

    While most SQLITE_DBCONFIG options use the argument format +** described in the previous paragraph, the [SQLITE_DBCONFIG_MAINDBNAME] +** and [SQLITE_DBCONFIG_LOOKASIDE] options are different. See the +** documentation of those exceptional options for details. */ #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ @@ -2551,7 +2642,10 @@ struct sqlite3_mem_methods { #define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */ #define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */ #define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */ -#define SQLITE_DBCONFIG_MAX 1019 /* Largest DBCONFIG */ +#define SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE 1020 /* int int* */ +#define SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE 1021 /* int int* */ +#define SQLITE_DBCONFIG_ENABLE_COMMENTS 1022 /* int int* */ +#define SQLITE_DBCONFIG_MAX 1022 /* Largest DBCONFIG */ /* ** CAPI3REF: Enable Or Disable Extended Result Codes @@ -10792,8 +10886,9 @@ SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb); /* ** CAPI3REF: Serialize a database ** -** The sqlite3_serialize(D,S,P,F) interface returns a pointer to memory -** that is a serialization of the S database on [database connection] D. +** The sqlite3_serialize(D,S,P,F) interface returns a pointer to +** memory that is a serialization of the S database on +** [database connection] D. If S is a NULL pointer, the main database is used. ** If P is not a NULL pointer, then the size of the database in bytes ** is written into *P. ** diff --git a/src/sqliteInt.h b/src/sqliteInt.h index f56625cef0..336c3a583e 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -843,6 +843,11 @@ typedef INT16_TYPE i16; /* 2-byte signed integer */ typedef UINT8_TYPE u8; /* 1-byte unsigned integer */ typedef INT8_TYPE i8; /* 1-byte signed integer */ +/* A bitfield type for use inside of structures. Always follow with :N where +** N is the number of bits. +*/ +typedef unsigned bft; /* Bit Field Type */ + /* ** SQLITE_MAX_U32 is a u64 constant that is the maximum u64 value ** that can be stored in a u32 without loss of data. The value @@ -881,6 +886,8 @@ typedef u64 tRowcnt; ** 0.5 -> -10 0.1 -> -33 0.0625 -> -40 */ typedef INT16_TYPE LogEst; +#define LOGEST_MIN (-32768) +#define LOGEST_MAX (32767) /* ** Set the SQLITE_PTRSIZE macro to the number of bytes in a pointer @@ -1151,7 +1158,7 @@ extern u32 sqlite3WhereTrace; ** 0xFFFF---- Low-level debug messages ** ** 0x00000001 Code generation -** 0x00000002 Solver +** 0x00000002 Solver (Use 0x40000 for less detail) ** 0x00000004 Solver costs ** 0x00000008 WhereLoop inserts ** @@ -1170,6 +1177,8 @@ extern u32 sqlite3WhereTrace; ** ** 0x00010000 Show more detail when printing WHERE terms ** 0x00020000 Show WHERE terms returned from whereScanNext() +** 0x00040000 Solver overview messages +** 0x00080000 Star-query heuristic */ @@ -1834,6 +1843,9 @@ struct sqlite3 { #define SQLITE_CorruptRdOnly HI(0x00002) /* Prohibit writes due to error */ #define SQLITE_ReadUncommit HI(0x00004) /* READ UNCOMMITTED in shared-cache */ #define SQLITE_FkNoAction HI(0x00008) /* Treat all FK as NO ACTION */ +#define SQLITE_AttachCreate HI(0x00010) /* ATTACH allowed to create new dbs */ +#define SQLITE_AttachWrite HI(0x00020) /* ATTACH allowed to open for write */ +#define SQLITE_Comments HI(0x00040) /* Enable SQL comments */ /* Flags used only if debugging */ #ifdef SQLITE_DEBUG @@ -1893,6 +1905,7 @@ struct sqlite3 { #define SQLITE_NullUnusedCols 0x04000000 /* NULL unused columns in subqueries */ #define SQLITE_OnePass 0x08000000 /* Single-pass DELETE and UPDATE */ #define SQLITE_OrderBySubq 0x10000000 /* ORDER BY in subquery helps outer */ +#define SQLITE_StarQuery 0x20000000 /* Heurists for star queries */ #define SQLITE_AllOpts 0xffffffff /* All optimizations */ /* @@ -2422,6 +2435,7 @@ struct Table { } u; Trigger *pTrigger; /* List of triggers on this object */ Schema *pSchema; /* Schema that contains this table */ + u8 aHx[16]; /* Column aHt[K%sizeof(aHt)] might have hash K */ }; /* @@ -3222,13 +3236,8 @@ struct ExprList { */ struct IdList { int nId; /* Number of identifiers on the list */ - u8 eU4; /* Which element of a.u4 is valid */ struct IdList_item { char *zName; /* Name of the identifier */ - union { - int idx; /* Index in some Table.aCol[] of a column named zName */ - Expr *pExpr; /* Expr to implement a USING variable -- NOT USED */ - } u4; } a[1]; }; @@ -3824,25 +3833,32 @@ struct Parse { char *zErrMsg; /* An error message */ Vdbe *pVdbe; /* An engine for executing database bytecode */ int rc; /* Return code from execution */ - u8 colNamesSet; /* TRUE after OP_ColumnName has been issued to pVdbe */ - u8 checkSchema; /* Causes schema cookie check after an error */ + LogEst nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */ u8 nested; /* Number of nested calls to the parser/code generator */ u8 nTempReg; /* Number of temporary registers in aTempReg[] */ u8 isMultiWrite; /* True if statement may modify/insert multiple rows */ u8 mayAbort; /* True if statement may throw an ABORT exception */ u8 hasCompound; /* Need to invoke convertCompoundSelectToSubquery() */ - u8 okConstFactor; /* OK to factor out constants */ u8 disableLookaside; /* Number of times lookaside has been disabled */ u8 prepFlags; /* SQLITE_PREPARE_* flags */ u8 withinRJSubrtn; /* Nesting level for RIGHT JOIN body subroutines */ - u8 bHasWith; /* True if statement contains WITH */ u8 mSubrtnSig; /* mini Bloom filter on available SubrtnSig.selId */ + u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */ + u8 bReturning; /* Coding a RETURNING trigger */ + u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */ + u8 disableTriggers; /* True to disable triggers */ #if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) u8 earlyCleanup; /* OOM inside sqlite3ParserAddCleanup() */ #endif #ifdef SQLITE_DEBUG u8 ifNotExists; /* Might be true if IF NOT EXISTS. Assert()s only */ + u8 isCreate; /* CREATE TABLE, INDEX, or VIEW (but not TRIGGER) + ** and ALTER TABLE ADD COLUMN. */ #endif + bft colNamesSet :1; /* TRUE after OP_ColumnName has been issued to pVdbe */ + bft bHasWith :1; /* True if statement contains WITH */ + bft okConstFactor :1; /* OK to factor out constants */ + bft checkSchema :1; /* Causes schema cookie check after an error */ int nRangeReg; /* Size of the temporary register block */ int iRangeReg; /* First register in temporary register block */ int nErr; /* Number of errors seen */ @@ -3857,12 +3873,9 @@ struct Parse { ExprList *pConstExpr;/* Constant expressions */ IndexedExpr *pIdxEpr;/* List of expressions used by active indexes */ IndexedExpr *pIdxPartExpr; /* Exprs constrained by index WHERE clauses */ - Token constraintName;/* Name of the constraint currently being parsed */ yDbMask writeMask; /* Start a write transaction on these databases */ yDbMask cookieMask; /* Bitmask of schema verified databases */ - int regRowid; /* Register holding rowid of CREATE TABLE entry */ - int regRoot; /* Register holding root page number for new objects */ - int nMaxArg; /* Max args passed to user function by sub-program */ + int nMaxArg; /* Max args to xUpdate and xFilter vtab methods */ int nSelect; /* Number of SELECT stmts. Counter for Select.selId */ #ifndef SQLITE_OMIT_PROGRESS_CALLBACK u32 nProgressSteps; /* xProgress steps taken during sqlite3_prepare() */ @@ -3876,17 +3889,6 @@ struct Parse { Table *pTriggerTab; /* Table triggers are being coded for */ TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */ ParseCleanup *pCleanup; /* List of cleanup operations to run after parse */ - union { - int addrCrTab; /* Address of OP_CreateBtree on CREATE TABLE */ - Returning *pReturning; /* The RETURNING clause */ - } u1; - u32 oldmask; /* Mask of old.* columns referenced */ - u32 newmask; /* Mask of new.* columns referenced */ - LogEst nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */ - u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */ - u8 bReturning; /* Coding a RETURNING trigger */ - u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */ - u8 disableTriggers; /* True to disable triggers */ /************************************************************************** ** Fields above must be initialized to zero. The fields that follow, @@ -3898,6 +3900,19 @@ struct Parse { int aTempReg[8]; /* Holding area for temporary registers */ Parse *pOuterParse; /* Outer Parse object when nested */ Token sNameToken; /* Token with unqualified schema object name */ + u32 oldmask; /* Mask of old.* columns referenced */ + u32 newmask; /* Mask of new.* columns referenced */ + union { + struct { /* These fields available when isCreate is true */ + int addrCrTab; /* Address of OP_CreateBtree on CREATE TABLE */ + int regRowid; /* Register holding rowid of CREATE TABLE entry */ + int regRoot; /* Register holding root page for new objects */ + Token constraintName; /* Name of the constraint currently being parsed */ + } cr; + struct { /* These fields available to all other statements */ + Returning *pReturning; /* The RETURNING clause */ + } d; + } u1; /************************************************************************ ** Above is constant between recursions. Below is reset before and after @@ -3915,9 +3930,7 @@ struct Parse { int nVtabLock; /* Number of virtual tables to lock */ #endif int nHeight; /* Expression tree height of current sub-select */ -#ifndef SQLITE_OMIT_EXPLAIN int addrExplain; /* Address of current OP_Explain opcode */ -#endif VList *pVList; /* Mapping between variable names and numbers */ Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */ const char *zTail; /* All SQL text past the last semicolon parsed */ diff --git a/src/tclsqlite.c b/src/tclsqlite.c index f0b5c3e814..824e8c4d3c 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -341,7 +341,7 @@ static int SQLITE_TCLAPI incrblobInput( */ static int SQLITE_TCLAPI incrblobOutput( ClientData instanceData, - CONST char *buf, + const char *buf, int toWrite, int *errorCodePtr ){ @@ -510,7 +510,7 @@ static int createIncrblobChannel( ** or {...} or ; to be seen anywhere. Most callback scripts consist ** of just a single procedure name and they meet this requirement. */ -static int safeToUseEvalObjv(Tcl_Interp *interp, Tcl_Obj *pCmd){ +static int safeToUseEvalObjv(Tcl_Obj *pCmd){ /* We could try to do something with Tcl_Parse(). But we will instead ** just do a search for forbidden characters. If any of the forbidden ** characters appear in pCmd, we will report the string as unsafe. @@ -1097,7 +1097,8 @@ static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){ /* Only return a BLOB type if the Tcl variable is a bytearray and ** has no string representation. */ eType = SQLITE_BLOB; - }else if( (c=='b' && strcmp(zType,"boolean")==0) + }else if( (c=='b' && pVar->bytes==0 && strcmp(zType,"boolean")==0 ) + || (c=='b' && pVar->bytes==0 && strcmp(zType,"booleanString")==0 ) || (c=='w' && strcmp(zType,"wideInt")==0) || (c=='i' && strcmp(zType,"int")==0) ){ @@ -1505,9 +1506,12 @@ static int dbPrepareAndBind( sqlite3_bind_blob(pStmt, i, data, n, SQLITE_STATIC); Tcl_IncrRefCount(pVar); pPreStmt->apParm[iParm++] = pVar; - }else if( c=='b' && strcmp(zType,"boolean")==0 ){ + }else if( c=='b' && pVar->bytes==0 + && (strcmp(zType,"booleanString")==0 + || strcmp(zType,"boolean")==0) + ){ int nn; - Tcl_GetIntFromObj(interp, pVar, &nn); + Tcl_GetBooleanFromObj(interp, pVar, &nn); sqlite3_bind_int(pStmt, i, nn); }else if( c=='d' && strcmp(zType,"double")==0 ){ double r; @@ -1843,7 +1847,8 @@ static Tcl_Obj *dbEvalColumnValue(DbEvalContext *p, int iCol){ ** are 8.6 or newer, the code still tests the Tcl version at runtime. ** This allows stubs-enabled builds to be used with older Tcl libraries. */ -#if TCL_MAJOR_VERSION>8 || (TCL_MAJOR_VERSION==8 && TCL_MINOR_VERSION>=6) +#if TCL_MAJOR_VERSION>8 || !defined(TCL_MINOR_VERSION) \ + || TCL_MINOR_VERSION>=6 # define SQLITE_TCL_NRE 1 static int DbUseNre(void){ int major, minor; @@ -1959,7 +1964,7 @@ static void DbHookCmd( } if( pArg ){ assert( !(*ppHook) ); - if( Tcl_GetCharLength(pArg)>0 ){ + if( Tcl_GetString(pArg)[0] ){ *ppHook = pArg; Tcl_IncrRefCount(*ppHook); } @@ -2988,7 +2993,7 @@ deserialize_error: } pFunc->pScript = pScript; Tcl_IncrRefCount(pScript); - pFunc->useEvalObjv = safeToUseEvalObjv(interp, pScript); + pFunc->useEvalObjv = safeToUseEvalObjv(pScript); pFunc->eType = eType; rc = sqlite3_create_function(pDb->db, zName, nArg, flags, pFunc, tclSqlFunc, 0, 0); @@ -4016,7 +4021,9 @@ EXTERN int Tclsqlite_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; } EXTERN int Sqlite_SafeInit(Tcl_Interp *interp){ return TCL_ERROR; } EXTERN int Sqlite_SafeUnload(Tcl_Interp *interp, int flags){return TCL_ERROR;} -/* Also variants with a lowercase "s" */ +/* Also variants with a lowercase "s". I'm told that these are +** deprecated in Tcl9, but they continue to be included for backwards +** compatibility. */ EXTERN int sqlite3_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp);} EXTERN int sqlite_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp);} diff --git a/src/test1.c b/src/test1.c index a0ca93d10c..e45a05fe47 100644 --- a/src/test1.c +++ b/src/test1.c @@ -600,6 +600,7 @@ static int SQLITE_TCLAPI test_get_table_printf( } sqlite3_free(zSql); sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", rc); + Tcl_ResetResult(interp); Tcl_AppendElement(interp, zBuf); if( rc==SQLITE_OK ){ if( argc==4 ){ @@ -5680,9 +5681,11 @@ static int SQLITE_TCLAPI test_stmt_utf8( sqlite3_stmt *pStmt; int col; const char *(*xFunc)(sqlite3_stmt*, int); + const unsigned char *(*xFuncU)(sqlite3_stmt*, int); const char *zRet; xFunc = (const char *(*)(sqlite3_stmt*, int))clientData; + xFuncU = (const unsigned char*(*)(sqlite3_stmt*,int))xFunc; if( objc!=3 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " STMT column", 0); @@ -5691,7 +5694,11 @@ static int SQLITE_TCLAPI test_stmt_utf8( if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; if( Tcl_GetIntFromObj(interp, objv[2], &col) ) return TCL_ERROR; - zRet = xFunc(pStmt, col); + if( xFunc==sqlite3_column_name || xFunc==sqlite3_column_decltype ){ + zRet = xFunc(pStmt, col); + }else{ + zRet = (const char*)xFuncU(pStmt, col); + } if( zRet ){ Tcl_SetResult(interp, (char *)zRet, 0); } @@ -7588,12 +7595,16 @@ static int SQLITE_TCLAPI test_wal_autocheckpoint( /* ** tclcmd: test_sqlite3_log ?SCRIPT? +** +** Caution: If you register a log callback, you must deregister it (by +** invoking test_sqlite3_log with no arguments) prior to closing the +** Tcl interpreter or else a memory error will occur. */ static struct LogCallback { Tcl_Interp *pInterp; Tcl_Obj *pObj; } logcallback = {0, 0}; -static void xLogcallback(void *unused, int err, char *zMsg){ +static void xLogcallback(void *unused, int err, const char *zMsg){ Tcl_Obj *pNew = Tcl_DuplicateObj(logcallback.pObj); Tcl_IncrRefCount(pNew); Tcl_ListObjAppendElement( @@ -7619,7 +7630,7 @@ static int SQLITE_TCLAPI test_sqlite3_log( logcallback.pInterp = 0; sqlite3_config(SQLITE_CONFIG_LOG, (void*)0, (void*)0); } - if( objc>1 ){ + if( objc>1 && Tcl_GetString(objv[1])[0]!=0 ){ logcallback.pObj = objv[1]; Tcl_IncrRefCount(logcallback.pObj); logcallback.pInterp = interp; @@ -8655,7 +8666,6 @@ static int SQLITE_TCLAPI test_decode_hexdb( const char *zIn = 0; unsigned char *a = 0; int n = 0; - int lineno = 0; int i, iNext; int iOffset = 0; int j, k; @@ -8667,7 +8677,6 @@ static int SQLITE_TCLAPI test_decode_hexdb( } zIn = Tcl_GetString(objv[1]); for(i=0; zIn[i]; i=iNext){ - lineno++; for(iNext=i; zIn[iNext] && zIn[iNext]!='\n'; iNext++){} if( zIn[iNext]=='\n' ) iNext++; while( zIn[i]==' ' || zIn[i]=='\t' ){ i++; } diff --git a/src/test_intarray.c b/src/test_intarray.c index 16c1df2e9c..9e4629467e 100644 --- a/src/test_intarray.c +++ b/src/test_intarray.c @@ -61,7 +61,8 @@ struct intarray_cursor { /* ** Free an sqlite3_intarray object. */ -static void intarrayFree(sqlite3_intarray *p){ +static void intarrayFree(void *pX){ + sqlite3_intarray *p = (sqlite3_intarray*)pX; if( p->xFree ){ p->xFree(p->a); } diff --git a/src/test_malloc.c b/src/test_malloc.c index 21faa0d291..8d6c4fa505 100644 --- a/src/test_malloc.c +++ b/src/test_malloc.c @@ -41,8 +41,9 @@ static struct MemFault { ** fire on any simulated malloc() failure. */ static void sqlite3Fault(void){ - static int cnt = 0; + static u64 cnt = 0; cnt++; + if( cnt>((u64)1<<63) ) abort(); } /* @@ -52,8 +53,9 @@ static void sqlite3Fault(void){ ** This routine only runs on the first such failure. */ static void sqlite3FirstFault(void){ - static int cnt2 = 0; + static u64 cnt2 = 0; cnt2++; + if( cnt2>((u64)1<<63) ) abort(); } /* diff --git a/src/tokenize.c b/src/tokenize.c index b49b2aa16e..fe300ca529 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -288,7 +288,7 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){ case CC_MINUS: { if( z[1]=='-' ){ for(i=2; (c=z[i])!=0 && c!='\n'; i++){} - *tokenType = TK_SPACE; /* IMP: R-22934-25134 */ + *tokenType = TK_COMMENT; return i; }else if( z[1]=='>' ){ *tokenType = TK_PTR; @@ -324,7 +324,7 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){ } for(i=3, c=z[2]; (c!='*' || z[i]!='/') && (c=z[i])!=0; i++){} if( c ) i++; - *tokenType = TK_SPACE; /* IMP: R-22934-25134 */ + *tokenType = TK_COMMENT; return i; } case CC_PERCENT: { @@ -653,12 +653,12 @@ int sqlite3RunParser(Parse *pParse, const char *zSql){ if( tokenType>=TK_WINDOW ){ assert( tokenType==TK_SPACE || tokenType==TK_OVER || tokenType==TK_FILTER || tokenType==TK_ILLEGAL || tokenType==TK_WINDOW - || tokenType==TK_QNUMBER + || tokenType==TK_QNUMBER || tokenType==TK_COMMENT ); #else if( tokenType>=TK_SPACE ){ assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL - || tokenType==TK_QNUMBER + || tokenType==TK_QNUMBER || tokenType==TK_COMMENT ); #endif /* SQLITE_OMIT_WINDOWFUNC */ if( AtomicLoad(&db->u1.isInterrupted) ){ @@ -692,6 +692,9 @@ int sqlite3RunParser(Parse *pParse, const char *zSql){ assert( n==6 ); tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed); #endif /* SQLITE_OMIT_WINDOWFUNC */ + }else if( tokenType==TK_COMMENT && (db->flags & SQLITE_Comments)!=0 ){ + zSql += n; + continue; }else if( tokenType!=TK_QNUMBER ){ Token x; x.z = zSql; @@ -798,6 +801,7 @@ char *sqlite3Normalize( n = sqlite3GetToken((unsigned char*)zSql+i, &tokenType); if( NEVER(n<=0) ) break; switch( tokenType ){ + case TK_COMMENT: case TK_SPACE: { break; } diff --git a/src/treeview.c b/src/treeview.c index 30592d35b2..8329659249 100644 --- a/src/treeview.c +++ b/src/treeview.c @@ -215,7 +215,10 @@ void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){ sqlite3_str_appendf(&x, " DDL"); } if( pItem->fg.isCte ){ - sqlite3_str_appendf(&x, " CteUse=0x%p", pItem->u2.pCteUse); + static const char *aMat[] = {",MAT", "", ",NO-MAT"}; + sqlite3_str_appendf(&x, " CteUse=%d%s", + pItem->u2.pCteUse->nUse, + aMat[pItem->u2.pCteUse->eM10d]); } if( pItem->fg.isOn || (pItem->fg.isUsing==0 && pItem->u3.pOn!=0) ){ sqlite3_str_appendf(&x, " isOn"); @@ -246,9 +249,6 @@ void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){ sqlite3TreeViewColumnList(pView, pTab->aCol, pTab->nCol, 1); } assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem) ); - sqlite3TreeViewPush(&pView, 0); - sqlite3TreeViewLine(pView, "SUBQUERY"); - sqlite3TreeViewPop(&pView); sqlite3TreeViewSelect(pView, pItem->u4.pSubq->pSelect, 0); } if( pItem->fg.isTabFunc ){ @@ -978,21 +978,7 @@ void sqlite3TreeViewBareIdList( if( zName==0 ) zName = "(null)"; sqlite3TreeViewPush(&pView, moreToFollow); sqlite3TreeViewLine(pView, 0); - if( pList->eU4==EU4_NONE ){ - fprintf(stdout, "%s\n", zName); - }else if( pList->eU4==EU4_IDX ){ - fprintf(stdout, "%s (%d)\n", zName, pList->a[i].u4.idx); - }else{ - assert( pList->eU4==EU4_EXPR ); - if( pList->a[i].u4.pExpr==0 ){ - fprintf(stdout, "%s (pExpr=NULL)\n", zName); - }else{ - fprintf(stdout, "%s\n", zName); - sqlite3TreeViewPush(&pView, inId-1); - sqlite3TreeViewExpr(pView, pList->a[i].u4.pExpr, 0); - sqlite3TreeViewPop(&pView); - } - } + fprintf(stdout, "%s\n", zName); sqlite3TreeViewPop(&pView); } } diff --git a/src/trigger.c b/src/trigger.c index e306a2e664..604c3ab42f 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -70,7 +70,8 @@ Trigger *sqlite3TriggerList(Parse *pParse, Table *pTab){ assert( pParse->db->pVtabCtx==0 ); #endif assert( pParse->bReturning ); - assert( &(pParse->u1.pReturning->retTrig) == pTrig ); + assert( !pParse->isCreate ); + assert( &(pParse->u1.d.pReturning->retTrig) == pTrig ); pTrig->table = pTab->zName; pTrig->pTabSchema = pTab->pSchema; pTrig->pNext = pList; @@ -1047,7 +1048,8 @@ static void codeReturningTrigger( return; } assert( db->pParse==pParse ); - pReturning = pParse->u1.pReturning; + assert( !pParse->isCreate ); + pReturning = pParse->u1.d.pReturning; if( pTrigger != &(pReturning->retTrig) ){ /* This RETURNING trigger is for a different statement */ return; @@ -1277,6 +1279,8 @@ static TriggerPrg *codeRowTrigger( sSubParse.eTriggerOp = pTrigger->op; sSubParse.nQueryLoop = pParse->nQueryLoop; sSubParse.prepFlags = pParse->prepFlags; + sSubParse.oldmask = 0; + sSubParse.newmask = 0; v = sqlite3GetVdbe(&sSubParse); if( v ){ diff --git a/src/update.c b/src/update.c index a8e7f77803..979afea1f5 100644 --- a/src/update.c +++ b/src/update.c @@ -465,38 +465,32 @@ void sqlite3Update( */ chngRowid = chngPk = 0; for(i=0; inExpr; i++){ - u8 hCol = sqlite3StrIHash(pChanges->a[i].zEName); /* If this is an UPDATE with a FROM clause, do not resolve expressions ** here. The call to sqlite3Select() below will do that. */ if( nChangeFrom==0 && sqlite3ResolveExprNames(&sNC, pChanges->a[i].pExpr) ){ goto update_cleanup; } - for(j=0; jnCol; j++){ - if( pTab->aCol[j].hName==hCol - && sqlite3StrICmp(pTab->aCol[j].zCnName, pChanges->a[i].zEName)==0 - ){ - if( j==pTab->iPKey ){ - chngRowid = 1; - pRowidExpr = pChanges->a[i].pExpr; - iRowidExpr = i; - }else if( pPk && (pTab->aCol[j].colFlags & COLFLAG_PRIMKEY)!=0 ){ - chngPk = 1; - } -#ifndef SQLITE_OMIT_GENERATED_COLUMNS - else if( pTab->aCol[j].colFlags & COLFLAG_GENERATED ){ - testcase( pTab->aCol[j].colFlags & COLFLAG_VIRTUAL ); - testcase( pTab->aCol[j].colFlags & COLFLAG_STORED ); - sqlite3ErrorMsg(pParse, - "cannot UPDATE generated column \"%s\"", - pTab->aCol[j].zCnName); - goto update_cleanup; - } -#endif - aXRef[j] = i; - break; + j = sqlite3ColumnIndex(pTab, pChanges->a[i].zEName); + if( j>=0 ){ + if( j==pTab->iPKey ){ + chngRowid = 1; + pRowidExpr = pChanges->a[i].pExpr; + iRowidExpr = i; + }else if( pPk && (pTab->aCol[j].colFlags & COLFLAG_PRIMKEY)!=0 ){ + chngPk = 1; } - } - if( j>=pTab->nCol ){ +#ifndef SQLITE_OMIT_GENERATED_COLUMNS + else if( pTab->aCol[j].colFlags & COLFLAG_GENERATED ){ + testcase( pTab->aCol[j].colFlags & COLFLAG_VIRTUAL ); + testcase( pTab->aCol[j].colFlags & COLFLAG_STORED ); + sqlite3ErrorMsg(pParse, + "cannot UPDATE generated column \"%s\"", + pTab->aCol[j].zCnName); + goto update_cleanup; + } +#endif + aXRef[j] = i; + }else{ if( pPk==0 && sqlite3IsRowid(pChanges->a[i].zEName) ){ j = -1; chngRowid = 1; diff --git a/src/util.c b/src/util.c index ecce460e01..703ef0a23a 100644 --- a/src/util.c +++ b/src/util.c @@ -1130,7 +1130,11 @@ void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ } p->z = &p->zBuf[i+1]; assert( i+p->n < sizeof(p->zBuf) ); - while( ALWAYS(p->n>0) && p->z[p->n-1]=='0' ){ p->n--; } + assert( p->n>0 ); + while( p->z[p->n-1]=='0' ){ + p->n--; + assert( p->n>0 ); + } } /* diff --git a/src/vdbe.c b/src/vdbe.c index 558970ed95..ec871c5a6e 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -607,6 +607,7 @@ static void registerTrace(int iReg, Mem *p){ printf("R[%d] = ", iReg); memTracePrint(p); if( p->pScopyFrom ){ + assert( p->pScopyFrom->bScopy ); printf(" <== R[%d]", (int)(p->pScopyFrom - &p[-iReg])); } printf("\n"); @@ -1590,6 +1591,7 @@ case OP_Move: { { int i; for(i=1; inMem; i++){ if( aMem[i].pScopyFrom==pIn1 ){ + assert( aMem[i].bScopy ); aMem[i].pScopyFrom = pOut; } } @@ -1662,6 +1664,7 @@ case OP_SCopy: { /* out2 */ #ifdef SQLITE_DEBUG pOut->pScopyFrom = pIn1; pOut->mScopyFlags = pIn1->flags; + pIn1->bScopy = 1; #endif break; } @@ -8360,6 +8363,7 @@ case OP_VFilter: { /* jump, ncycle */ /* Invoke the xFilter method */ apArg = p->apArg; + assert( nArg<=p->napArg ); for(i = 0; ivtabOnConflict; apArg = p->apArg; pX = &aMem[pOp->p3]; + assert( nArg<=p->napArg ); for(i=0; istartTime>0 ); - assert( (db->mTrace & (SQLITE_TRACE_PROFILE|SQLITE_TRACE_XPROFILE))!=0 ); assert( db->init.busy==0 ); assert( p->zSql!=0 ); sqlite3OsCurrentTimeInt64(db->pVfs, &iNow); @@ -783,7 +782,7 @@ static int sqlite3Step(Vdbe *p){ } assert( db->nVdbeWrite>0 || db->autoCommit==0 - || (db->nDeferredCons==0 && db->nDeferredImmCons==0) + || ((db->nDeferredCons + db->nDeferredImmCons)==0) ); #ifndef SQLITE_OMIT_TRACE @@ -1294,6 +1293,7 @@ static const Mem *columnNullValue(void){ #ifdef SQLITE_DEBUG /* .pScopyFrom = */ (Mem*)0, /* .mScopyFlags= */ 0, + /* .bScopy = */ 0, #endif }; return &nullMem; @@ -2176,6 +2176,7 @@ int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ PreUpdate *p; Mem *pMem; int rc = SQLITE_OK; + int iStore = 0; #ifdef SQLITE_ENABLE_API_ARMOR if( db==0 || ppValue==0 ){ @@ -2190,9 +2191,11 @@ int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ goto preupdate_old_out; } if( p->pPk ){ - iIdx = sqlite3TableColumnToIndex(p->pPk, iIdx); + iStore = sqlite3TableColumnToIndex(p->pPk, iIdx); + }else{ + iStore = sqlite3TableColumnToStorage(p->pTab, iIdx); } - if( iIdx>=p->pCsr->nField || iIdx<0 ){ + if( iStore>=p->pCsr->nField || iStore<0 ){ rc = SQLITE_RANGE; goto preupdate_old_out; } @@ -2223,8 +2226,8 @@ int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ p->aRecord = aRec; } - pMem = *ppValue = &p->pUnpacked->aMem[iIdx]; - if( iIdx>=p->pUnpacked->nField ){ + pMem = *ppValue = &p->pUnpacked->aMem[iStore]; + if( iStore>=p->pUnpacked->nField ){ /* This occurs when the table has been extended using ALTER TABLE ** ADD COLUMN. The value to return is the default value of the column. */ Column *pCol = &p->pTab->aCol[iIdx]; @@ -2328,6 +2331,7 @@ int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ PreUpdate *p; int rc = SQLITE_OK; Mem *pMem; + int iStore = 0; #ifdef SQLITE_ENABLE_API_ARMOR if( db==0 || ppValue==0 ){ @@ -2340,9 +2344,12 @@ int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ goto preupdate_new_out; } if( p->pPk && p->op!=SQLITE_UPDATE ){ - iIdx = sqlite3TableColumnToIndex(p->pPk, iIdx); + iStore = sqlite3TableColumnToIndex(p->pPk, iIdx); + }else{ + iStore = sqlite3TableColumnToStorage(p->pTab, iIdx); } - if( iIdx>=p->pCsr->nField || iIdx<0 ){ + + if( iStore>=p->pCsr->nField || iStore<0 ){ rc = SQLITE_RANGE; goto preupdate_new_out; } @@ -2362,14 +2369,14 @@ int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ } p->pNewUnpacked = pUnpack; } - pMem = &pUnpack->aMem[iIdx]; + pMem = &pUnpack->aMem[iStore]; if( iIdx==p->pTab->iPKey ){ sqlite3VdbeMemSetInt64(pMem, p->iKey2); - }else if( iIdx>=pUnpack->nField ){ + }else if( iStore>=pUnpack->nField ){ pMem = (sqlite3_value *)columnNullValue(); } }else{ - /* For an UPDATE, memory cell (p->iNewReg+1+iIdx) contains the required + /* For an UPDATE, memory cell (p->iNewReg+1+iStore) contains the required ** value. Make a copy of the cell contents and return a pointer to it. ** It is not safe to return a pointer to the memory cell itself as the ** caller may modify the value text encoding. @@ -2382,13 +2389,13 @@ int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ goto preupdate_new_out; } } - assert( iIdx>=0 && iIdxpCsr->nField ); - pMem = &p->aNew[iIdx]; + assert( iStore>=0 && iStorepCsr->nField ); + pMem = &p->aNew[iStore]; if( pMem->flags==0 ){ if( iIdx==p->pTab->iPKey ){ sqlite3VdbeMemSetInt64(pMem, p->iKey2); }else{ - rc = sqlite3VdbeMemCopy(pMem, &p->v->aMem[p->iNewReg+1+iIdx]); + rc = sqlite3VdbeMemCopy(pMem, &p->v->aMem[p->iNewReg+1+iStore]); if( rc!=SQLITE_OK ) goto preupdate_new_out; } } diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 4414f7a2ec..cf661eb9cb 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -856,8 +856,8 @@ void sqlite3VdbeAssertAbortable(Vdbe *p){ ** (1) For each jump instruction with a negative P2 value (a label) ** resolve the P2 value to an actual address. ** -** (2) Compute the maximum number of arguments used by any SQL function -** and store that value in *pMaxFuncArgs. +** (2) Compute the maximum number of arguments used by the xUpdate/xFilter +** methods of any virtual table and store that value in *pMaxVtabArgs. ** ** (3) Update the Vdbe.readOnly and Vdbe.bIsReader flags to accurately ** indicate what the prepared statement actually does. @@ -870,8 +870,8 @@ void sqlite3VdbeAssertAbortable(Vdbe *p){ ** script numbers the opcodes correctly. Changes to this routine must be ** coordinated with changes to mkopcodeh.tcl. */ -static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ - int nMaxArgs = *pMaxFuncArgs; +static void resolveP2Values(Vdbe *p, int *pMaxVtabArgs){ + int nMaxVtabArgs = *pMaxVtabArgs; Op *pOp; Parse *pParse = p->pParse; int *aLabel = pParse->aLabel; @@ -916,15 +916,19 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ } #ifndef SQLITE_OMIT_VIRTUALTABLE case OP_VUpdate: { - if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2; + if( pOp->p2>nMaxVtabArgs ) nMaxVtabArgs = pOp->p2; break; } case OP_VFilter: { int n; + /* The instruction immediately prior to VFilter will be an + ** OP_Integer that sets the "argc" value for the VFilter. See + ** the code where OP_VFilter is generated at tag-20250207a. */ assert( (pOp - p->aOp) >= 3 ); assert( pOp[-1].opcode==OP_Integer ); + assert( pOp[-1].p2==pOp->p3+1 ); n = pOp[-1].p1; - if( n>nMaxArgs ) nMaxArgs = n; + if( n>nMaxVtabArgs ) nMaxVtabArgs = n; /* Fall through into the default case */ /* no break */ deliberate_fall_through } @@ -965,7 +969,7 @@ resolve_p2_values_loop_exit: pParse->aLabel = 0; } pParse->nLabel = 0; - *pMaxFuncArgs = nMaxArgs; + *pMaxVtabArgs = nMaxVtabArgs; assert( p->bIsReader!=0 || DbMaskAllZero(p->btreeMask) ); } @@ -2144,6 +2148,7 @@ void sqlite3VdbePrintOp(FILE *pOut, int pc, VdbeOp *pOp){ ** will be initialized before use. */ static void initMemArray(Mem *p, int N, sqlite3 *db, u16 flags){ + assert( db!=0 ); if( N>0 ){ do{ p->flags = flags; @@ -2151,6 +2156,7 @@ static void initMemArray(Mem *p, int N, sqlite3 *db, u16 flags){ p->szMalloc = 0; #ifdef SQLITE_DEBUG p->pScopyFrom = 0; + p->bScopy = 0; #endif p++; }while( (--N)>0 ); @@ -2169,6 +2175,7 @@ static void releaseMemArray(Mem *p, int N){ if( p && N ){ Mem *pEnd = &p[N]; sqlite3 *db = p->db; + assert( db!=0 ); if( db->pnBytesFreed ){ do{ if( p->szMalloc ) sqlite3DbFree(db, p->zMalloc); @@ -2640,7 +2647,7 @@ void sqlite3VdbeMakeReady( int nVar; /* Number of parameters */ int nMem; /* Number of VM memory registers */ int nCursor; /* Number of cursors required */ - int nArg; /* Number of arguments in subprograms */ + int nArg; /* Max number args to xFilter or xUpdate */ int n; /* Loop counter */ struct ReusableSpace x; /* Reusable bulk memory */ @@ -2649,6 +2656,7 @@ void sqlite3VdbeMakeReady( assert( pParse!=0 ); assert( p->eVdbeState==VDBE_INIT_STATE ); assert( pParse==p->pParse ); + assert( pParse->db==p->db ); p->pVList = pParse->pVList; pParse->pVList = 0; db = p->db; @@ -2711,6 +2719,9 @@ void sqlite3VdbeMakeReady( p->apCsr = allocSpace(&x, p->apCsr, nCursor*sizeof(VdbeCursor*)); } } +#ifdef SQLITE_DEBUG + p->napArg = nArg; +#endif if( db->mallocFailed ){ p->nVar = 0; diff --git a/src/vdbeblob.c b/src/vdbeblob.c index 6cb36da37a..79698d0af4 100644 --- a/src/vdbeblob.c +++ b/src/vdbeblob.c @@ -192,12 +192,8 @@ int sqlite3_blob_open( pBlob->zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName; /* Now search pTab for the exact column. */ - for(iCol=0; iColnCol; iCol++) { - if( sqlite3StrICmp(pTab->aCol[iCol].zCnName, zColumn)==0 ){ - break; - } - } - if( iCol==pTab->nCol ){ + iCol = sqlite3ColumnIndex(pTab, zColumn); + if( iCol<0 ){ sqlite3DbFree(db, zErr); zErr = sqlite3MPrintf(db, "no such column: \"%s\"", zColumn); rc = SQLITE_ERROR; diff --git a/src/vdbemem.c b/src/vdbemem.c index 0fc6b68f5e..61298d10ff 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -327,7 +327,7 @@ void sqlite3VdbeMemZeroTerminateIfAble(Mem *pMem){ return; } if( pMem->enc!=SQLITE_UTF8 ) return; - if( NEVER(pMem->z==0) ) return; + assert( pMem->z!=0 ); if( pMem->flags & MEM_Dyn ){ if( pMem->xDel==sqlite3_free && sqlite3_msize(pMem->z) >= (u64)(pMem->n+1) @@ -1046,27 +1046,30 @@ int sqlite3VdbeMemTooBig(Mem *p){ void sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){ int i; Mem *pX; - for(i=1, pX=pVdbe->aMem+1; inMem; i++, pX++){ - if( pX->pScopyFrom==pMem ){ - u16 mFlags; - if( pVdbe->db->flags & SQLITE_VdbeTrace ){ - sqlite3DebugPrintf("Invalidate R[%d] due to change in R[%d]\n", - (int)(pX - pVdbe->aMem), (int)(pMem - pVdbe->aMem)); + if( pMem->bScopy ){ + for(i=1, pX=pVdbe->aMem+1; inMem; i++, pX++){ + if( pX->pScopyFrom==pMem ){ + u16 mFlags; + if( pVdbe->db->flags & SQLITE_VdbeTrace ){ + sqlite3DebugPrintf("Invalidate R[%d] due to change in R[%d]\n", + (int)(pX - pVdbe->aMem), (int)(pMem - pVdbe->aMem)); + } + /* If pX is marked as a shallow copy of pMem, then try to verify that + ** no significant changes have been made to pX since the OP_SCopy. + ** A significant change would indicated a missed call to this + ** function for pX. Minor changes, such as adding or removing a + ** dual type, are allowed, as long as the underlying value is the + ** same. */ + mFlags = pMem->flags & pX->flags & pX->mScopyFlags; + assert( (mFlags&(MEM_Int|MEM_IntReal))==0 || pMem->u.i==pX->u.i ); + + /* pMem is the register that is changing. But also mark pX as + ** undefined so that we can quickly detect the shallow-copy error */ + pX->flags = MEM_Undefined; + pX->pScopyFrom = 0; } - /* If pX is marked as a shallow copy of pMem, then try to verify that - ** no significant changes have been made to pX since the OP_SCopy. - ** A significant change would indicated a missed call to this - ** function for pX. Minor changes, such as adding or removing a - ** dual type, are allowed, as long as the underlying value is the - ** same. */ - mFlags = pMem->flags & pX->flags & pX->mScopyFlags; - assert( (mFlags&(MEM_Int|MEM_IntReal))==0 || pMem->u.i==pX->u.i ); - - /* pMem is the register that is changing. But also mark pX as - ** undefined so that we can quickly detect the shallow-copy error */ - pX->flags = MEM_Undefined; - pX->pScopyFrom = 0; } + pMem->bScopy = 0; } pMem->pScopyFrom = 0; } diff --git a/src/vtab.c b/src/vtab.c index 76ad3613e8..e40f60873a 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -479,11 +479,12 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ ** schema table. We just need to update that slot with all ** the information we've collected. ** - ** The VM register number pParse->regRowid holds the rowid of an + ** The VM register number pParse->u1.cr.regRowid holds the rowid of an ** entry in the sqlite_schema table that was created for this vtab ** by sqlite3StartTable(). */ iDb = sqlite3SchemaToIndex(db, pTab->pSchema); + assert( pParse->isCreate ); sqlite3NestedParse(pParse, "UPDATE %Q." LEGACY_SCHEMA_TABLE " " "SET type='table', name=%Q, tbl_name=%Q, rootpage=0, sql=%Q " @@ -492,7 +493,7 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ pTab->zName, pTab->zName, zStmt, - pParse->regRowid + pParse->u1.cr.regRowid ); v = sqlite3GetVdbe(pParse); sqlite3ChangeCookie(pParse, iDb); @@ -830,7 +831,9 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ z = (const unsigned char*)zCreateTable; for(i=0; aKeyword[i]; i++){ int tokenType = 0; - do{ z += sqlite3GetToken(z, &tokenType); }while( tokenType==TK_SPACE ); + do{ + z += sqlite3GetToken(z, &tokenType); + }while( tokenType==TK_SPACE || tokenType==TK_COMMENT ); if( tokenType!=aKeyword[i] ){ sqlite3ErrorWithMsg(db, SQLITE_ERROR, "syntax error"); return SQLITE_ERROR; diff --git a/src/where.c b/src/where.c index c9698699b3..5cb52b8adb 100644 --- a/src/where.c +++ b/src/where.c @@ -860,6 +860,11 @@ static int constraintCompatibleWithOuterJoin( ** more than 20, then return false. ** ** 3. If no disqualifying conditions above are found, return true. +** +** 2025-01-03: I experimented with a new rule that returns false if the +** the datatype of the column is "BOOLEAN". This did not improve +** performance on any queries at hand, but it did burn CPU cycles, so the +** idea was not committed. */ static SQLITE_NOINLINE int columnIsGoodIndexCandidate( const Table *pTab, @@ -944,7 +949,7 @@ static void explainAutomaticIndex( sqlite3_str *pStr = sqlite3_str_new(pParse->db); sqlite3_str_appendf(pStr,"CREATE AUTOMATIC INDEX ON %s(", pTab->zName); assert( pIdx->nColumn>1 ); - assert( pIdx->aiColumn[pIdx->nColumn-1]==XN_ROWID ); + assert( pIdx->aiColumn[pIdx->nColumn-1]==XN_ROWID || !HasRowid(pTab) ); for(ii=0; ii<(pIdx->nColumn-1); ii++){ const char *zName = 0; int iCol = pIdx->aiColumn[ii]; @@ -1075,6 +1080,19 @@ static SQLITE_NOINLINE void constructAutomaticIndex( }else{ extraCols = pSrc->colUsed & (~idxCols | MASKBIT(BMS-1)); } + if( !HasRowid(pTable) ){ + /* For WITHOUT ROWID tables, ensure that all PRIMARY KEY columns are + ** either in the idxCols mask or in the extraCols mask */ + for(i=0; inCol; i++){ + if( (pTable->aCol[i].colFlags & COLFLAG_PRIMKEY)==0 ) continue; + if( i>=BMS-1 ){ + extraCols |= MASKBIT(BMS-1); + break; + } + if( idxCols & MASKBIT(i) ) continue; + extraCols |= MASKBIT(i); + } + } mxBitCol = MIN(BMS-1,pTable->nCol); testcase( pTable->nCol==BMS-1 ); testcase( pTable->nCol==BMS-2 ); @@ -1086,7 +1104,8 @@ static SQLITE_NOINLINE void constructAutomaticIndex( } /* Construct the Index object to describe this index */ - pIdx = sqlite3AllocateIndexObject(pParse->db, nKeyCol+1, 0, &zNotUsed); + pIdx = sqlite3AllocateIndexObject(pParse->db, nKeyCol+HasRowid(pTable), + 0, &zNotUsed); if( pIdx==0 ) goto end_auto_index_create; pLoop->u.btree.pIndex = pIdx; pIdx->zName = "auto-index"; @@ -1142,8 +1161,10 @@ static SQLITE_NOINLINE void constructAutomaticIndex( } } assert( n==nKeyCol ); - pIdx->aiColumn[n] = XN_ROWID; - pIdx->azColl[n] = sqlite3StrBINARY; + if( HasRowid(pTable) ){ + pIdx->aiColumn[n] = XN_ROWID; + pIdx->azColl[n] = sqlite3StrBINARY; + } /* Create the automatic index */ explainAutomaticIndex(pParse, pIdx, pPartial!=0, &addrExp); @@ -2410,8 +2431,9 @@ void sqlite3WhereClausePrint(WhereClause *pWC){ ** 1.002.001 t2.t2xy 2 f 010241 N 2 cost 0,56,31 */ void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC){ + WhereInfo *pWInfo; if( pWC ){ - WhereInfo *pWInfo = pWC->pWInfo; + pWInfo = pWC->pWInfo; int nb = 1+(pWInfo->pTabList->nSrc+3)/4; SrcItem *pItem = pWInfo->pTabList->a + p->iTab; Table *pTab = pItem->pSTab; @@ -2421,6 +2443,7 @@ void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC){ sqlite3DebugPrintf(" %12s", pItem->zAlias ? pItem->zAlias : pTab->zName); }else{ + pWInfo = 0; sqlite3DebugPrintf("%c%2d.%03llx.%03llx %c%d", p->cId, p->iTab, p->maskSelf, p->prereq & 0xfff, p->cId, p->iTab); } @@ -2452,7 +2475,12 @@ void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC){ }else{ sqlite3DebugPrintf(" f %06x N %d", p->wsFlags, p->nLTerm); } - sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut); + if( pWInfo && pWInfo->bStarUsed && p->rStarDelta!=0 ){ + sqlite3DebugPrintf(" cost %d,%d,%d delta=%d\n", + p->rSetup, p->rRun, p->nOut, p->rStarDelta); + }else{ + sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut); + } if( p->nLTerm && (sqlite3WhereTrace & 0x4000)!=0 ){ int i; for(i=0; inLTerm; i++){ @@ -3918,7 +3946,6 @@ static int whereLoopAddBtree( && (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0 && !pSrc->fg.isIndexedBy /* Has no INDEXED BY clause */ && !pSrc->fg.notIndexed /* Has no NOT INDEXED clause */ - && HasRowid(pTab) /* Not WITHOUT ROWID table. (FIXME: Why not?) */ && !pSrc->fg.isCorrelated /* Not a correlated subquery */ && !pSrc->fg.isRecursive /* Not a recursive common table expression. */ && (pSrc->fg.jointype & JT_RIGHT)==0 /* Not the right tab of a RIGHT JOIN */ @@ -5421,68 +5448,201 @@ static LogEst whereSortingCost( ** 18 for star queries ** 12 otherwise ** -** For the purposes of SQLite, a star-query is defined as a query -** with a large central table that is joined against four or more -** smaller tables. The central table is called the "fact" table. -** The smaller tables that get joined are "dimension tables". +** For the purposes of this heuristic, a star-query is defined as a query +** with a large central table that is joined using an INNER JOIN, +** not CROSS or OUTER JOINs, against four or more smaller tables. +** The central table is called the "fact" table. The smaller tables +** that get joined are "dimension tables". Also, any table that is +** self-joined cannot be a dimension table; we assume that dimension +** tables may only be joined against fact tables. ** ** SIDE EFFECT: (and really the whole point of this subroutine) ** -** If pWInfo describes a star-query, then the cost on WhereLoops for the -** fact table is reduced. This heuristic helps keep fact tables in -** outer loops. Without this heuristic, paths with fact tables in outer -** loops tend to get pruned by the mxChoice limit on the number of paths, -** resulting in poor query plans. The total amount of heuristic cost -** adjustment is stored in pWInfo->nOutStarDelta and the cost adjustment -** for each WhereLoop is stored in its rStarDelta field. +** If pWInfo describes a star-query, then the cost for SCANs of dimension +** WhereLoops is increased to be slightly larger than the cost of a SCAN +** in the fact table. Only SCAN costs are increased. SEARCH costs are +** unchanged. This heuristic helps keep fact tables in outer loops. Without +** this heuristic, paths with fact tables in outer loops tend to get pruned +** by the mxChoice limit on the number of paths, resulting in poor query +** plans. See the starschema1.test test module for examples of queries +** that need this heuristic to find good query plans. +** +** This heuristic can be completely disabled, so that no query is +** considered a star-query, using SQLITE_TESTCTRL_OPTIMIZATION to +** disable the SQLITE_StarQuery optimization. In the CLI, the command +** to do that is: ".testctrl opt -starquery". +** +** HISTORICAL NOTES: +** +** This optimization was first added on 2024-05-09 by check-in 38db9b5c83d. +** The original optimization reduced the cost and output size estimate for +** fact tables to help them move to outer loops. But months later (as people +** started upgrading) performance regression reports started caming in, +** including: +** +** forum post b18ef983e68d06d1 (2024-12-21) +** forum post 0025389d0860af82 (2025-01-14) +** forum post d87570a145599033 (2025-01-17) +** +** To address these, the criteria for a star-query was tightened to exclude +** cases where the fact and dimensions are separated by an outer join, and +** the affect of star-schema detection was changed to increase the rRun cost +** on just full table scans of dimension tables, rather than reducing costs +** in the all access methods of the fact table. */ -static int computeMxChoice(WhereInfo *pWInfo, LogEst nRowEst){ +static int computeMxChoice(WhereInfo *pWInfo){ int nLoop = pWInfo->nLevel; /* Number of terms in the join */ - if( nRowEst==0 && nLoop>=5 ){ - /* Check to see if we are dealing with a star schema and if so, reduce - ** the cost of fact tables relative to dimension tables, as a heuristic - ** to help keep the fact tables in outer loops. + WhereLoop *pWLoop; /* For looping over WhereLoops */ + +#ifdef SQLITE_DEBUG + /* The star-query detection code below makes use of the following + ** properties of the WhereLoop list, so verify them before + ** continuing: + ** (1) .maskSelf is the bitmask corresponding to .iTab + ** (2) The WhereLoop list is in ascending .iTab order + */ + for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){ + assert( pWLoop->maskSelf==MASKBIT(pWLoop->iTab) ); + assert( pWLoop->pNextLoop==0 || pWLoop->iTab<=pWLoop->pNextLoop->iTab ); + } +#endif /* SQLITE_DEBUG */ + + if( nLoop>=5 + && !pWInfo->bStarDone + && OptimizationEnabled(pWInfo->pParse->db, SQLITE_StarQuery) + ){ + SrcItem *aFromTabs; /* All terms of the FROM clause */ + int iFromIdx; /* Term of FROM clause is the candidate fact-table */ + Bitmask m; /* Bitmask for candidate fact-table */ + Bitmask mSelfJoin = 0; /* Tables that cannot be dimension tables */ + WhereLoop *pStart; /* Where to start searching for dimension-tables */ + + pWInfo->bStarDone = 1; /* Only do this computation once */ + + /* Look for fact tables with four or more dimensions where the + ** dimension tables are not separately from the fact tables by an outer + ** or cross join. Adjust cost weights if found. */ - int iLoop; /* Counter over join terms */ - Bitmask m; /* Bitmask for current loop */ - assert( pWInfo->nOutStarDelta==0 ); - for(iLoop=0, m=1; iLoopbStarUsed ); + aFromTabs = pWInfo->pTabList->a; + pStart = pWInfo->pLoops; + for(iFromIdx=0, m=1; iFromIdxpLoops; pWLoop; pWLoop=pWLoop->pNextLoop){ - if( (pWLoop->prereq & m)!=0 && (pWLoop->maskSelf & mSeen)==0 ){ - nDep++; - mSeen |= pWLoop->maskSelf; + SrcItem *pFactTab; /* The candidate fact table */ + + pFactTab = aFromTabs + iFromIdx; + if( (pFactTab->fg.jointype & (JT_OUTER|JT_CROSS))!=0 ){ + /* If the candidate fact-table is the right table of an outer join + ** restrict the search for dimension-tables to be tables to the right + ** of the fact-table. */ + if( iFromIdx+4 > nLoop ) break; /* Impossible to reach nDep>=4 */ + while( pStart && pStart->iTab<=iFromIdx ){ + pStart = pStart->pNextLoop; + } + } + for(pWLoop=pStart; pWLoop; pWLoop=pWLoop->pNextLoop){ + if( (aFromTabs[pWLoop->iTab].fg.jointype & (JT_OUTER|JT_CROSS))!=0 ){ + /* Fact-tables and dimension-tables cannot be separated by an + ** outer join (at least for the definition of fact- and dimension- + ** used by this heuristic). */ + break; + } + if( (pWLoop->prereq & m)!=0 /* pWInfo depends on iFromIdx */ + && (pWLoop->maskSelf & mSeen)==0 /* pWInfo not already a dependency */ + && (pWLoop->maskSelf & mSelfJoin)==0 /* Not a self-join */ + ){ + if( aFromTabs[pWLoop->iTab].pSTab==pFactTab->pSTab ){ + mSelfJoin |= m; + }else{ + nDep++; + mSeen |= pWLoop->maskSelf; + } } } if( nDep<=3 ) continue; - rDelta = 15*(nDep-3); -#ifdef WHERETRACE_ENABLED /* 0x4 */ - if( sqlite3WhereTrace&0x4 ){ - SrcItem *pItem = pWInfo->pTabList->a + iLoop; - sqlite3DebugPrintf("Fact-table %s: %d dimensions, cost reduced %d\n", - pItem->zAlias ? pItem->zAlias : pItem->pSTab->zName, - nDep, rDelta); - } -#endif - if( pWInfo->nOutStarDelta==0 ){ + + /* If we reach this point, it means that pFactTab is a fact table + ** with four or more dimensions connected by inner joins. Proceed + ** to make cost adjustments. */ + +#ifdef WHERETRACE_ENABLED + /* Make sure rStarDelta values are initialized */ + if( !pWInfo->bStarUsed ){ for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){ pWLoop->rStarDelta = 0; } } - pWInfo->nOutStarDelta += rDelta; - for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){ - if( pWLoop->maskSelf==m ){ - pWLoop->rRun -= rDelta; - pWLoop->nOut -= rDelta; - pWLoop->rStarDelta = rDelta; +#endif + pWInfo->bStarUsed = 1; + + /* Compute the maximum cost of any WhereLoop for the + ** fact table plus one epsilon */ + mxRun = LOGEST_MIN; + for(pWLoop=pStart; pWLoop; pWLoop=pWLoop->pNextLoop){ + if( pWLoop->iTabiTab>iFromIdx ) break; + if( pWLoop->rRun>mxRun ) mxRun = pWLoop->rRun; + } + if( ALWAYS(mxRunpNextLoop){ + if( (pWLoop->maskSelf & mSeen)==0 ) continue; + if( pWLoop->nLTerm ) continue; + if( pWLoop->rRuniTab; + sqlite3DebugPrintf( + "Increase SCAN cost of dimension %s(%d) of fact %s(%d) to %d\n", + pDim->zAlias ? pDim->zAlias: pDim->pSTab->zName, pWLoop->iTab, + pFactTab->zAlias ? pFactTab->zAlias : pFactTab->pSTab->zName, + iFromIdx, mxRun + ); + } + pWLoop->rStarDelta = mxRun - pWLoop->rRun; +#endif /* WHERETRACE_ENABLED */ + pWLoop->rRun = mxRun; } } - } + } +#ifdef WHERETRACE_ENABLED /* 0x80000 */ + if( (sqlite3WhereTrace & 0x80000)!=0 && pWInfo->bStarUsed ){ + sqlite3DebugPrintf("WhereLoops changed by star-query heuristic:\n"); + for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){ + if( pWLoop->rStarDelta ){ + sqlite3WhereLoopPrint(pWLoop, &pWInfo->sWC); + } + } + } +#endif } - return pWInfo->nOutStarDelta>0 ? 18 : 12; + return pWInfo->bStarUsed ? 18 : 12; +} + +/* +** Two WhereLoop objects, pCandidate and pBaseline, are known to have the +** same cost. Look deep into each to see if pCandidate is even slightly +** better than pBaseline. Return false if it is, if pCandidate is is preferred. +** Return true if pBaseline is preferred or if we cannot tell the difference. +** +** Result Meaning +** -------- ---------------------------------------------------------- +** true We cannot tell the difference in pCandidate and pBaseline +** false pCandidate seems like a better choice than pBaseline +*/ +static SQLITE_NOINLINE int whereLoopIsNoBetter( + const WhereLoop *pCandidate, + const WhereLoop *pBaseline +){ + if( (pCandidate->wsFlags & WHERE_INDEXED)==0 ) return 1; + if( (pBaseline->wsFlags & WHERE_INDEXED)==0 ) return 1; + if( pCandidate->u.btree.pIndex->szIdxRow < + pBaseline->u.btree.pIndex->szIdxRow ) return 0; + return 1; } /* @@ -5506,7 +5666,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ int mxI = 0; /* Index of next entry to replace */ int nOrderBy; /* Number of ORDER BY clause terms */ LogEst mxCost = 0; /* Maximum cost of a set of paths */ - LogEst mxUnsorted = 0; /* Maximum unsorted cost of a set of path */ + LogEst mxUnsort = 0; /* Maximum unsorted cost of a set of path */ int nTo, nFrom; /* Number of valid entries in aTo[] and aFrom[] */ WherePath *aFrom; /* All nFrom paths at the previous level */ WherePath *aTo; /* The nTo best paths at the current level */ @@ -5535,8 +5695,10 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ mxChoice = 1; }else if( nLoop==2 ){ mxChoice = 5; + }else if( pParse->nErr ){ + mxChoice = 1; }else{ - mxChoice = computeMxChoice(pWInfo, nRowEst); + mxChoice = computeMxChoice(pWInfo); } assert( nLoop<=pWInfo->pTabList->nSrc ); @@ -5603,7 +5765,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){ LogEst nOut; /* Rows visited by (pFrom+pWLoop) */ LogEst rCost; /* Cost of path (pFrom+pWLoop) */ - LogEst rUnsorted; /* Unsorted cost of (pFrom+pWLoop) */ + LogEst rUnsort; /* Unsorted cost of (pFrom+pWLoop) */ i8 isOrdered; /* isOrdered for (pFrom+pWLoop) */ Bitmask maskNew; /* Mask of src visited by (..) */ Bitmask revMask; /* Mask of rev-order loops for (..) */ @@ -5621,11 +5783,11 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ /* At this point, pWLoop is a candidate to be the next loop. ** Compute its cost */ - rUnsorted = pWLoop->rRun + pFrom->nRow; + rUnsort = pWLoop->rRun + pFrom->nRow; if( pWLoop->rSetup ){ - rUnsorted = sqlite3LogEstAdd(pWLoop->rSetup, rUnsorted); + rUnsort = sqlite3LogEstAdd(pWLoop->rSetup, rUnsort); } - rUnsorted = sqlite3LogEstAdd(rUnsorted, pFrom->rUnsorted); + rUnsort = sqlite3LogEstAdd(rUnsort, pFrom->rUnsort); nOut = pFrom->nRow + pWLoop->nOut; maskNew = pFrom->maskLoop | pWLoop->maskSelf; isOrdered = pFrom->isOrdered; @@ -5647,15 +5809,15 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ ** extra encouragement to the query planner to select a plan ** where the rows emerge in the correct order without any sorting ** required. */ - rCost = sqlite3LogEstAdd(rUnsorted, aSortCost[isOrdered]) + 3; + rCost = sqlite3LogEstAdd(rUnsort, aSortCost[isOrdered]) + 3; WHERETRACE(0x002, ("---- sort cost=%-3d (%d/%d) increases cost %3d to %-3d\n", aSortCost[isOrdered], (nOrderBy-isOrdered), nOrderBy, - rUnsorted, rCost)); + rUnsort, rCost)); }else{ - rCost = rUnsorted; - rUnsorted -= 2; /* TUNING: Slight bias in favor of no-sort plans */ + rCost = rUnsort; + rUnsort -= 2; /* TUNING: Slight bias in favor of no-sort plans */ } /* Check to see if pWLoop should be added to the set of @@ -5681,7 +5843,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ if( jj>=nTo ){ /* None of the existing best-so-far paths match the candidate. */ if( nTo>=mxChoice - && (rCost>mxCost || (rCost==mxCost && rUnsorted>=mxUnsorted)) + && (rCost>mxCost || (rCost==mxCost && rUnsort>=mxUnsort)) ){ /* The current candidate is no better than any of the mxChoice ** paths currently in the best-so-far buffer. So discard @@ -5689,7 +5851,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ #ifdef WHERETRACE_ENABLED /* 0x4 */ if( sqlite3WhereTrace&0x4 ){ sqlite3DebugPrintf("Skip %s cost=%-3d,%3d,%3d order=%c\n", - wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted, + wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsort, isOrdered>=0 ? isOrdered+'0' : '?'); } #endif @@ -5708,7 +5870,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ #ifdef WHERETRACE_ENABLED /* 0x4 */ if( sqlite3WhereTrace&0x4 ){ sqlite3DebugPrintf("New %s cost=%-3d,%3d,%3d order=%c\n", - wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted, + wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsort, isOrdered>=0 ? isOrdered+'0' : '?'); } #endif @@ -5719,24 +5881,23 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ ** pTo or if the candidate should be skipped. ** ** The conditional is an expanded vector comparison equivalent to: - ** (pTo->rCost,pTo->nRow,pTo->rUnsorted) <= (rCost,nOut,rUnsorted) + ** (pTo->rCost,pTo->nRow,pTo->rUnsort) <= (rCost,nOut,rUnsort) */ - if( pTo->rCostrCost==rCost - && (pTo->nRownRow==nOut && pTo->rUnsorted<=rUnsorted) - ) - ) + if( (pTo->rCostrCost==rCost && pTo->nRowrCost==rCost && pTo->nRow==nOut && pTo->rUnsortrCost==rCost && pTo->nRow==nOut && pTo->rUnsort==rUnsort + && whereLoopIsNoBetter(pWLoop, pTo->aLoop[iLoop]) ) ){ #ifdef WHERETRACE_ENABLED /* 0x4 */ if( sqlite3WhereTrace&0x4 ){ sqlite3DebugPrintf( "Skip %s cost=%-3d,%3d,%3d order=%c", - wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted, + wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsort, isOrdered>=0 ? isOrdered+'0' : '?'); sqlite3DebugPrintf(" vs %s cost=%-3d,%3d,%3d order=%c\n", wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow, - pTo->rUnsorted, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?'); + pTo->rUnsort, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?'); } #endif /* Discard the candidate path from further consideration */ @@ -5750,11 +5911,11 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ if( sqlite3WhereTrace&0x4 ){ sqlite3DebugPrintf( "Update %s cost=%-3d,%3d,%3d order=%c", - wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted, + wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsort, isOrdered>=0 ? isOrdered+'0' : '?'); sqlite3DebugPrintf(" was %s cost=%-3d,%3d,%3d order=%c\n", wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow, - pTo->rUnsorted, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?'); + pTo->rUnsort, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?'); } #endif } @@ -5763,20 +5924,20 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ pTo->revLoop = revMask; pTo->nRow = nOut; pTo->rCost = rCost; - pTo->rUnsorted = rUnsorted; + pTo->rUnsort = rUnsort; pTo->isOrdered = isOrdered; memcpy(pTo->aLoop, pFrom->aLoop, sizeof(WhereLoop*)*iLoop); pTo->aLoop[iLoop] = pWLoop; if( nTo>=mxChoice ){ mxI = 0; mxCost = aTo[0].rCost; - mxUnsorted = aTo[0].nRow; + mxUnsort = aTo[0].nRow; for(jj=1, pTo=&aTo[1]; jjrCost>mxCost - || (pTo->rCost==mxCost && pTo->rUnsorted>mxUnsorted) + || (pTo->rCost==mxCost && pTo->rUnsort>mxUnsort) ){ mxCost = pTo->rCost; - mxUnsorted = pTo->rUnsorted; + mxUnsort = pTo->rUnsort; mxI = jj; } } @@ -5788,8 +5949,10 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ if( sqlite3WhereTrace & 0x02 ){ LogEst rMin, rFloor = 0; int nDone = 0; + int nProgress; sqlite3DebugPrintf("---- after round %d ----\n", iLoop); - while( nDonerCost>rFloor && pTo->rCostrCost; @@ -5805,10 +5968,11 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ sqlite3DebugPrintf("\n"); } nDone++; + nProgress++; } } rFloor = rMin; - } + }while( nDone0 ); } #endif @@ -5902,7 +6066,10 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ } } - pWInfo->nRowOut = pFrom->nRow + pWInfo->nOutStarDelta; + pWInfo->nRowOut = pFrom->nRow; +#ifdef WHERETRACE_ENABLED + pWInfo->rTotalCost = pFrom->rCost; +#endif /* Free temporary memory and return success */ sqlite3StackFreeNN(pParse->db, pSpace); @@ -6300,7 +6467,6 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful( } } nSearch += pLoop->nOut; - if( pWInfo->nOutStarDelta ) nSearch += pLoop->rStarDelta; } } @@ -6783,7 +6949,8 @@ WhereInfo *sqlite3WhereBegin( assert( db->mallocFailed==0 ); #ifdef WHERETRACE_ENABLED if( sqlite3WhereTrace ){ - sqlite3DebugPrintf("---- Solution nRow=%d", pWInfo->nRowOut); + sqlite3DebugPrintf("---- Solution cost=%d, nRow=%d", + pWInfo->rTotalCost, pWInfo->nRowOut); if( pWInfo->nOBSat>0 ){ sqlite3DebugPrintf(" ORDERBY=%d,0x%llx", pWInfo->nOBSat, pWInfo->revMask); } diff --git a/src/whereInt.h b/src/whereInt.h index f262b0eebc..8ba8a7072d 100644 --- a/src/whereInt.h +++ b/src/whereInt.h @@ -162,8 +162,10 @@ struct WhereLoop { /**** whereLoopXfer() copies fields above ***********************/ # define WHERE_LOOP_XFER_SZ offsetof(WhereLoop,nLSlot) u16 nLSlot; /* Number of slots allocated for aLTerm[] */ +#ifdef WHERETRACE_ENABLED LogEst rStarDelta; /* Cost delta due to star-schema heuristic. Not - ** initialized unless pWInfo->nOutStarDelta>0 */ + ** initialized unless pWInfo->bStarUsed */ +#endif WhereTerm **aLTerm; /* WhereTerms used */ WhereLoop *pNextLoop; /* Next WhereLoop object in the WhereClause */ WhereTerm *aLTermSpace[3]; /* Initial aLTerm[] space */ @@ -212,7 +214,7 @@ struct WherePath { Bitmask revLoop; /* aLoop[]s that should be reversed for ORDER BY */ LogEst nRow; /* Estimated number of rows generated by this path */ LogEst rCost; /* Total cost of this path */ - LogEst rUnsorted; /* Total cost of this path ignoring sorting costs */ + LogEst rUnsort; /* Total cost of this path ignoring sorting costs */ i8 isOrdered; /* No. of ORDER BY terms satisfied. -1 for unknown */ WhereLoop **aLoop; /* Array of WhereLoop objects implementing this path */ }; @@ -485,9 +487,13 @@ struct WhereInfo { unsigned bDeferredSeek :1; /* Uses OP_DeferredSeek */ unsigned untestedTerms :1; /* Not all WHERE terms resolved by outer loop */ unsigned bOrderedInnerLoop:1;/* True if only the inner-most loop is ordered */ - unsigned sorted :1; /* True if really sorted (not just grouped) */ - LogEst nOutStarDelta; /* Artifical nOut reduction for star-query */ + unsigned sorted :1; /* True if really sorted (not just grouped) */ + unsigned bStarDone :1; /* True if check for star-query is complete */ + unsigned bStarUsed :1; /* True if star-query heuristic is used */ LogEst nRowOut; /* Estimated number of output rows */ +#ifdef WHERETRACE_ENABLED + LogEst rTotalCost; /* Total cost of the solution */ +#endif int iTop; /* The very beginning of the WHERE loop */ int iEndWhere; /* End of the WHERE clause itself */ WhereLoop *pLoops; /* List of all WhereLoop objects */ diff --git a/src/wherecode.c b/src/wherecode.c index 045653aac8..1a0cdc6d71 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -1608,6 +1608,9 @@ Bitmask sqlite3WhereCodeOneLoopStart( } sqlite3VdbeAddOp2(v, OP_Integer, pLoop->u.vtab.idxNum, iReg); sqlite3VdbeAddOp2(v, OP_Integer, nConstraint, iReg+1); + /* The instruction immediately prior to OP_VFilter must be an OP_Integer + ** that sets the "argc" value for xVFilter. This is necessary for + ** resolveP2() to work correctly. See tag-20250207a. */ sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrNotFound, iReg, pLoop->u.vtab.idxStr, pLoop->u.vtab.needFree ? P4_DYNAMIC : P4_STATIC); diff --git a/src/whereexpr.c b/src/whereexpr.c index 2b6eb6a78d..4a24dadd23 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -219,12 +219,12 @@ static int isLikeOrGlob( z = (u8*)pRight->u.zToken; } if( z ){ - /* Count the number of prefix bytes prior to the first wildcard. - ** or U+fffd character. If the underlying database has a UTF16LE - ** encoding, then only consider ASCII characters. Note that the - ** encoding of z[] is UTF8 - we are dealing with only UTF8 here in - ** this code, but the database engine itself might be processing - ** content using a different encoding. */ + /* Count the number of prefix bytes prior to the first wildcard, + ** U+fffd character, or malformed utf-8. If the underlying database + ** has a UTF16LE encoding, then only consider ASCII characters. Note that + ** the encoding of z[] is UTF8 - we are dealing with only UTF8 here in this + ** code, but the database engine itself might be processing content using a + ** different encoding. */ cnt = 0; while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){ cnt++; @@ -232,7 +232,9 @@ static int isLikeOrGlob( cnt++; }else if( c>=0x80 ){ const u8 *z2 = z+cnt-1; - if( sqlite3Utf8Read(&z2)==0xfffd || ENC(db)==SQLITE_UTF16LE ){ + if( c==0xff || sqlite3Utf8Read(&z2)==0xfffd /* bad utf-8 */ + || ENC(db)==SQLITE_UTF16LE + ){ cnt--; break; }else{ @@ -1384,9 +1386,8 @@ static void exprAnalyze( } if( !db->mallocFailed ){ - u8 c, *pC; /* Last character before the first wildcard */ + u8 *pC; /* Last character before the first wildcard */ pC = (u8*)&pStr2->u.zToken[sqlite3Strlen30(pStr2->u.zToken)-1]; - c = *pC; if( noCase ){ /* The point is to increment the last character before the first ** wildcard. But if we increment '@', that will push it into the @@ -1394,10 +1395,17 @@ static void exprAnalyze( ** inequality. To avoid this, make sure to also run the full ** LIKE on all candidate expressions by clearing the isComplete flag */ - if( c=='A'-1 ) isComplete = 0; - c = sqlite3UpperToLower[c]; + if( *pC=='A'-1 ) isComplete = 0; + *pC = sqlite3UpperToLower[*pC]; } - *pC = c + 1; + + /* Increment the value of the last utf8 character in the prefix. */ + while( *pC==0xBF && pC>(u8*)pStr2->u.zToken ){ + *pC = 0x80; + pC--; + } + assert( *pC!=0xFF ); /* isLikeOrGlob() guarantees this */ + (*pC)++; } zCollSeqName = noCase ? "NOCASE" : sqlite3StrBINARY; pNewExpr1 = sqlite3ExprDup(db, pLeft, 0); diff --git a/test/autoindex1.test b/test/autoindex1.test index b294a2721f..1c8ce007f0 100644 --- a/test/autoindex1.test +++ b/test/autoindex1.test @@ -563,4 +563,32 @@ do_execsql_test autoindex-1120 { SELECT * FROM t1 LEFT JOIN t2 ON (t2.c=+t1.a) LEFT JOIN t3 ON (t2.d IS NULL); } {1 1 1 2 {} {}} +# 2025-01-18 +# Added support for automatic indexes on WITHOUT ROWID tables. +# +reset_db +do_execsql_test autoindex-1200 { + CREATE TABLE t1(a INT, b INT, x INT, PRIMARY KEY(a,b)) WITHOUT ROWID; + INSERT INTO t1 VALUES(1,2,90),(1,3,91),(1,4,92); + CREATE TABLE t2a(c INTEGER PRIMARY KEY, i1 INT); + CREATE TABLE t2b(i1 INTEGER PRIMARY KEY, d INT); + CREATE VIEW t2(c,d) AS SELECT c, d FROM t2a NATURAL JOIN t2b; + INSERT INTO t2a VALUES(3,93),(4,94),(5,95),(6,96),(7,97); + INSERT INTO t2b VALUES(91,11),(92,22),(93,33),(94,44),(95,55); + CREATE TABLE dual(dummy TEXT); + INSERT INTO dual(dummy) VALUES('x'); +} +db null NULL +do_execsql_test autoindex-1210 { + SELECT t1.*, t2.* FROM t2 LEFT OUTER JOIN t1 ON b=c ORDER BY +b; +} { + NULL NULL NULL 5 55 + 1 3 91 3 33 + 1 4 92 4 44 +} +do_execsql_test autoindex-1211 { + EXPLAIN QUERY PLAN + SELECT t1.*, t2.* FROM t2 LEFT OUTER JOIN t1 ON b=c ORDER BY +b; +} {/SEARCH t1 USING AUTOMATIC COVERING INDEX/} + finish_test diff --git a/test/capi3.test b/test/capi3.test index e65f90e3aa..6319d8284d 100644 --- a/test/capi3.test +++ b/test/capi3.test @@ -689,7 +689,9 @@ do_test capi3-6.3 { sqlite3_finalize $STMT } {SQLITE_OK} -if {[clang_sanitize_address]==0} { +if {0 && [clang_sanitize_address]==0} { + # This use-after-free occasionally causes segfaults during ordinary + # builds. Let's just disable it completely. do_test capi3-6.4-misuse { db cache flush sqlite3_close $DB diff --git a/test/date.test b/test/date.test index d22b652b47..2042880a92 100644 --- a/test/date.test +++ b/test/date.test @@ -651,5 +651,13 @@ datetest 19.51 {date('2000-08-31','+0022-06-00','floor')} {2023-02-28} datetest 19.52 {date('2000-08-31','+0023-06-00','ceiling')} {2024-03-02} datetest 19.53 {date('2000-08-31','+0022-06-00','ceiling')} {2023-03-03} +# 2025-01-21 +# https://sqlite.org/forum/forumpost/766a2c9231 +# +datetest 20.1 {datetime('2024-12-31 23:59:59.9990')} {2024-12-31 23:59:59} +datetest 20.2 {datetime('2024-12-31 23:59:59.9999999999999')} \ + {2024-12-31 23:59:59} +datetest 20.3 {datetime('2024-12-31 23:59:59.9995')} {2024-12-31 23:59:59} +datetest 20.4 {datetime('2024-12-31 23:59:58.9995')} {2024-12-31 23:59:58} finish_test diff --git a/test/dbpage.test b/test/dbpage.test index 0646a70b02..8039e0e1be 100644 --- a/test/dbpage.test +++ b/test/dbpage.test @@ -108,4 +108,148 @@ do_execsql_test 300 { SELECT * FROM sqlite_temp_schema, sqlite_dbpage; } {} +#------------------------------------------------------------------------- +reset_db +do_execsql_test 400 { + ATTACH ':memory:' AS aux1; + BEGIN; + CREATE VIRTUAL TABLE aux1.t1 USING sqlite_dbpage; + INSERT INTO t1 VALUES(17, NULL); + COMMIT; +} + +#------------------------------------------------------------------------- +reset_db +forcedelete test.db2 +sqlite3 db2 test.db2 +db2 eval { + PRAGMA auto_vacuum=NONE; + CREATE TABLE t1(x, y); +} + +do_execsql_test 500 { + PRAGMA auto_vacuum=NONE; + CREATE TABLE x1(a); + INSERT INTO x1 VALUES( hex(randomblob(2000)) ); + INSERT INTO x1 VALUES( hex(randomblob(2000)) ); + INSERT INTO x1 VALUES( hex(randomblob(2000)) ); + INSERT INTO x1 VALUES( hex(randomblob(2000)) ); + PRAGMA page_count; +} {18} + +do_test 510 { + db eval BEGIN + db2 eval { PRAGMA page_count } { + db eval { + INSERT INTO sqlite_dbpage values($page_count, NULL); + } + } + db2 eval { SELECT pgno, data FROM sqlite_dbpage } { + db eval { + INSERT INTO sqlite_dbpage values($pgno, $data); + } + } + + db eval COMMIT +} {} + +db close +sqlite3 db test.db + +do_execsql_test 520 { + PRAGMA page_count; + SELECT * FROM t1; +} {2} + +db2 close + +#------------------------------------------------------------------------- +reset_db +forcedelete test.db2 +do_execsql_test 610 { + ATTACH 'test.db2' AS aux; + CREATE TABLE t1(x); + CREATE TABLE t2(y); + INSERT INTO t1 VALUES(1234); + CREATE TABLE aux.x1(z); +} + +set pgno [db one {SELECT max(rootpage) FROM sqlite_schema}] +sqlite3 db2 test.db2 +db2 eval { + BEGIN; + SELECT * FROM x1; +} + +do_catchsql_test 620 { + UPDATE sqlite_dbpage SET data = ( + SELECT data FROM sqlite_dbpage WHERE pgno=$pgno-1 + ) WHERE pgno = $pgno; +} {1 {database is locked}} + +db2 eval { + COMMIT; +} + +do_catchsql_test 630 { + UPDATE sqlite_dbpage SET data = ( + SELECT data FROM sqlite_dbpage WHERE pgno=$pgno-1 + ) WHERE pgno = $pgno; +} {0 {}} + +db close +sqlite3 db test.db + +do_execsql_test 640 { + SELECT * FROM t2; +} {1234} + +db2 close + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 700 { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES( hex(randomblob(1000)) ); + INSERT INTO t1 VALUES( hex(randomblob(1000)) ); + INSERT INTO t1 VALUES( hex(randomblob(1000)) ); +} + +forcedelete test.db2 +sqlite3 db2 test.db2 +db2 eval { + CREATE TABLE y1(y); + INSERT INTO y1 VALUES( hex(randomblob(1000)) ); +} + +set max [db2 one {PRAGMA page_count}] + +do_test 710 { + execsql { + BEGIN; + } + + for {set ii 1} {$ii <= $max} {incr ii} { + set data [db2 one {SELECT data FROM sqlite_dbpage WHERE pgno=$ii}] + execsql { + UPDATE sqlite_dbpage SET data=$data WHERE pgno=$ii + } + } + + execsql { + SAVEPOINT abc; + INSERT INTO sqlite_dbpage VALUES(2, NULL); + ROLLBACK TO abc; + COMMIT; + } +} {} + +db close +sqlite3 db test.db + +do_execsql_test 720 { + PRAGMA integrity_check +} {ok} + + finish_test diff --git a/test/dbpagefault.test b/test/dbpagefault.test index f27741cba1..e5b246fc94 100644 --- a/test/dbpagefault.test +++ b/test/dbpagefault.test @@ -82,5 +82,31 @@ do_catchsql_test 3.2 { # faultsim_test_result {0 {}} #} +reset_db +forcedelete test.db2 +do_execsql_test 4.0 { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES('one'); + CREATE TABLE t2(x); + INSERT INTO t2 VALUES('two'); + ATTACH 'test.db2' AS aux; + CREATE TABLE aux.x1(x); +} + +set pgno [db one {SELECT max(rootpage) FROM sqlite_schema}] + +faultsim_save_and_close +do_faultsim_test 4 -prep { + faultsim_restore_and_reopen + execsql { ATTACH 'test.db2' AS aux; } +} -body { + execsql { + UPDATE sqlite_dbpage SET data = ( + SELECT data FROM sqlite_dbpage WHERE pgno=($pgno-1) + ) WHERE pgno = $pgno; + } +} -test { + faultsim_test_result {0 {}} {1 {unable to open a temporary database file for storing temporary tables}} +} finish_test diff --git a/test/fkey6.test b/test/fkey6.test index b658f20fea..72de926b52 100644 --- a/test/fkey6.test +++ b/test/fkey6.test @@ -225,5 +225,47 @@ do_execsql_test 3.3.4 { SELECT * FROM p2; } {0 one 1 deleted!} +#------------------------------------------------------------------------- +# Verify that, even with "PRAGMA defer_foreign_keys", a transaction cannot +# be committed if there are outstanding foreign key violations. +# +reset_db +do_execsql_test 4.0 { + CREATE TABLE p1(a INTEGER PRIMARY KEY, b UNIQUE); + CREATE TABLE c1(x REFERENCES p1(b)); + + INSERT INTO p1 VALUES(1, 'one'), (2, 'two'), (3, 'three'); + INSERT INTO c1 VALUES('two'); + + PRAGMA foreign_keys = 1; + PRAGMA defer_foreign_keys = 1; +} + +do_execsql_test 4.1 { + BEGIN; + DELETE FROM p1 WHERE a=2; +} + +do_catchsql_test 4.2 { + COMMIT; +} {1 {FOREIGN KEY constraint failed}} + +#------------------------------------------------------------------------- +# +reset_db +do_execsql_test 5.0 { + PRAGMA foreign_keys = 1; + CREATE TABLE p1(a INTEGER PRIMARY KEY, b); + CREATE TABLE c1(x REFERENCES p1 DEFERRABLE INITIALLY DEFERRED); +} + +do_execsql_test 5.1 { + BEGIN; + INSERT INTO c1 VALUES(123); + PRAGMA defer_foreign_keys = 1; + INSERT INTO p1 VALUES(123, 'one two three'); + COMMIT; +} + finish_test diff --git a/test/fuzzcheck.c b/test/fuzzcheck.c index 9f339096bc..84e3f32895 100644 --- a/test/fuzzcheck.c +++ b/test/fuzzcheck.c @@ -507,7 +507,8 @@ static void writefileFunc( static void blobListLoadFromDb( sqlite3 *db, /* Read from this database */ const char *zSql, /* Query used to extract the blobs */ - int onlyId, /* Only load where id is this value */ + int firstId, /* First sqlid to load */ + int lastId, /* Last sqlid to load */ int *pN, /* OUT: Write number of blobs loaded here */ Blob **ppList /* OUT: Write the head of the blob list here */ ){ @@ -518,8 +519,9 @@ static void blobListLoadFromDb( int rc; char *z2; - if( onlyId>0 ){ - z2 = sqlite3_mprintf("%s WHERE rowid=%d", zSql, onlyId); + if( firstId>0 ){ + z2 = sqlite3_mprintf("%s WHERE rowid BETWEEN %d AND %d", zSql, + firstId, lastId); }else{ z2 = sqlite3_mprintf("%s", zSql); } @@ -1836,7 +1838,8 @@ static void showHelp(void){ "each database, checking for crashes and memory leaks.\n" "Options:\n" " --cell-size-check Set the PRAGMA cell_size_check=ON\n" -" --dbid N Use only the database where dbid=N\n" +" --dbid M..N Use only the databases where dbid between M and N\n" +" \"M..\" for M and afterwards. Just \"M\" for M only\n" " --export-db DIR Write databases to files(s) in DIR. Works with --dbid\n" " --export-sql DIR Write SQL to file(s) in DIR. Also works with --sqlid\n" " --help Show this help text\n" @@ -1861,7 +1864,8 @@ static void showHelp(void){ " --script Output CLI script instead of running tests\n" " --skip N Skip the first N test cases\n" " --spinner Use a spinner to show progress\n" -" --sqlid N Use only SQL where sqlid=N\n" +" --sqlid M..N Use only SQL where sqlid between M..N\n" +" \"M..\" for M and afterwards. Just \"M\" for M only\n" " --timeout N Maximum time for any one test in N millseconds\n" " -v|--verbose Increased output. Repeat for more output.\n" " --vdbe-debug Activate VDBE debugging.\n" @@ -1883,8 +1887,10 @@ int main(int argc, char **argv){ Blob *pDb; /* For looping over template databases */ int i; /* Loop index for the argv[] loop */ int dbSqlOnly = 0; /* Only use scripts that are dbsqlfuzz */ - int onlySqlid = -1; /* --sqlid */ - int onlyDbid = -1; /* --dbid */ + int firstSqlid = -1; /* First --sqlid range */ + int lastSqlid = 0x7fffffff; /* Last --sqlid range */ + int firstDbid = -1; /* --dbid */ + int lastDbid = 0x7fffffff; /* --dbid end */ int nativeFlag = 0; /* --native-vfs */ int rebuildFlag = 0; /* --rebuild */ int vdbeLimitFlag = 0; /* --limit-vdbe */ @@ -1917,6 +1923,7 @@ int main(int argc, char **argv){ int bTimer = 0; /* Show elapse time for each test */ int nV; /* How much to increase verbosity with -vvvv */ sqlite3_int64 tmStart; /* Start of each test */ + int iEstTime = 0; /* LPF for the time-to-go */ sqlite3_config(SQLITE_CONFIG_URI,1); registerOomSimulator(); @@ -1941,8 +1948,18 @@ int main(int argc, char **argv){ cellSzCkFlag = 1; }else if( strcmp(z,"dbid")==0 ){ + const char *zDotDot; if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]); - onlyDbid = integerValue(argv[++i]); + i++; + zDotDot = strstr(argv[i], ".."); + if( zDotDot ){ + firstDbid = atoi(argv[i]); + if( zDotDot[2] ){ + lastDbid = atoi(&zDotDot[2]); + } + }else{ + lastDbid = firstDbid = integerValue(argv[i]); + } }else if( strcmp(z,"export-db")==0 ){ if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]); @@ -2042,8 +2059,19 @@ int main(int argc, char **argv){ bTimer = 1; }else if( strcmp(z,"sqlid")==0 ){ + const char *zDotDot; if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]); - onlySqlid = integerValue(argv[++i]); + i++; + zDotDot = strstr(argv[i], ".."); + if( zDotDot ){ + firstSqlid = atoi(argv[i]); + if( zDotDot[2] ){ + lastSqlid = atoi(&zDotDot[2]); + } + }else{ + firstSqlid = integerValue(argv[i]); + lastSqlid = firstSqlid; + } }else if( strcmp(z,"timeout")==0 ){ if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]); @@ -2291,13 +2319,14 @@ int main(int argc, char **argv){ const char *zExDb = "SELECT writefile(printf('%s/db%06d.db',?1,dbid),dbcontent)," " dbid, printf('%s/db%06d.db',?1,dbid), length(dbcontent)" - " FROM db WHERE ?2<0 OR dbid=?2;"; + " FROM db WHERE dbid BETWEEN ?2 AND ?3;"; rc = sqlite3_prepare_v2(db, zExDb, -1, &pStmt, 0); if( rc ) fatalError("cannot prepare statement [%s]: %s", zExDb, sqlite3_errmsg(db)); sqlite3_bind_text64(pStmt, 1, zExpDb, strlen(zExpDb), SQLITE_STATIC, SQLITE_UTF8); - sqlite3_bind_int(pStmt, 2, onlyDbid); + sqlite3_bind_int(pStmt, 2, firstDbid); + sqlite3_bind_int(pStmt, 3, lastDbid); while( sqlite3_step(pStmt)==SQLITE_ROW ){ printf("write db-%d (%d bytes) into %s\n", sqlite3_column_int(pStmt,1), @@ -2310,13 +2339,14 @@ int main(int argc, char **argv){ const char *zExSql = "SELECT writefile(printf('%s/sql%06d.txt',?1,sqlid),sqltext)," " sqlid, printf('%s/sql%06d.txt',?1,sqlid), length(sqltext)" - " FROM xsql WHERE ?2<0 OR sqlid=?2;"; + " FROM xsql WHERE sqlid BETWEEN ?2 AND ?3;"; rc = sqlite3_prepare_v2(db, zExSql, -1, &pStmt, 0); if( rc ) fatalError("cannot prepare statement [%s]: %s", zExSql, sqlite3_errmsg(db)); sqlite3_bind_text64(pStmt, 1, zExpSql, strlen(zExpSql), SQLITE_STATIC, SQLITE_UTF8); - sqlite3_bind_int(pStmt, 2, onlySqlid); + sqlite3_bind_int(pStmt, 2, firstSqlid); + sqlite3_bind_int(pStmt, 3, lastSqlid); while( sqlite3_step(pStmt)==SQLITE_ROW ){ printf("write sql-%d (%d bytes) into %s\n", sqlite3_column_int(pStmt,1), @@ -2332,11 +2362,11 @@ int main(int argc, char **argv){ /* Load all SQL script content and all initial database images from the ** source db */ - blobListLoadFromDb(db, "SELECT sqlid, sqltext FROM xsql", onlySqlid, - &g.nSql, &g.pFirstSql); + blobListLoadFromDb(db, "SELECT sqlid, sqltext FROM xsql", firstSqlid, + lastSqlid, &g.nSql, &g.pFirstSql); if( g.nSql==0 ) fatalError("need at least one SQL script"); - blobListLoadFromDb(db, "SELECT dbid, dbcontent FROM db", onlyDbid, - &g.nDb, &g.pFirstDb); + blobListLoadFromDb(db, "SELECT dbid, dbcontent FROM db", firstDbid, + lastDbid, &g.nDb, &g.pFirstDb); if( g.nDb==0 ){ g.pFirstDb = safe_realloc(0, sizeof(Blob)); memset(g.pFirstDb, 0, sizeof(Blob)); @@ -2416,9 +2446,29 @@ int main(int argc, char **argv){ if( bScript ){ /* No progress output */ }else if( bSpinner ){ - int nTotal =g.nSql; + int nTotal = g.nSql; int idx = pSql->seq; - printf("\r%s: %d/%d ", zDbName, idx, nTotal); + if( nSrcDb==1 && nTotal>idx && idx>=20 ){ + int iToGo = (timeOfDay() - iBegin)*(nTotal-idx)/(idx*1000); + int hr, min, sec; + if( idx==20 ){ + iEstTime = iToGo; + }else{ + iEstTime = (iToGo + 7*iEstTime)/8; + } + hr = iEstTime/3600; + min = (iEstTime/60)%60; + sec = iEstTime%60; + if( hr>0 ){ + printf("\r%s: %d/%d ETC %d:%02d:%02d ", + zDbName, idx, nTotal, hr, min, sec); + }else{ + printf("\r%s: %d/%d ETC %02d:%02d ", + zDbName, idx, nTotal, min, sec); + } + }else{ + printf("\r%s: %d/%d ", zDbName, idx, nTotal); + } fflush(stdout); }else if( verboseFlag>1 ){ printf("%s\n", g.zTestName); @@ -2457,7 +2507,7 @@ int main(int argc, char **argv){ }else if( bSpinner ){ int nTotal = g.nDb*g.nSql; int idx = pSql->seq*g.nDb + pDb->id - 1; - printf("\r%s: %d/%d ", zDbName, idx, nTotal); + printf("\r%s: %d/%d ", zDbName, idx, nTotal); fflush(stdout); }else if( verboseFlag>1 ){ printf("%s\n", g.zTestName); @@ -2560,7 +2610,7 @@ int main(int argc, char **argv){ /* No progress output */ }else if( bSpinner ){ int nTotal = g.nDb*g.nSql; - printf("\r%s: %d/%d \n", zDbName, nTotal, nTotal); + printf("\r%s: %d/%d \n", zDbName, nTotal, nTotal); }else if( !quietFlag && verboseFlag<2 ){ printf(" 100%% - %d tests\n", g.nDb*g.nSql); } diff --git a/test/fuzzdata8.db b/test/fuzzdata8.db index 3e34180071..469df2c681 100644 Binary files a/test/fuzzdata8.db and b/test/fuzzdata8.db differ diff --git a/test/in7.test b/test/in7.test index 4dc0821d18..763396140a 100644 --- a/test/in7.test +++ b/test/in7.test @@ -219,4 +219,33 @@ do_execsql_test 3.8 { SELECT t1.a, t2.b FROM t1, t2 WHERE (t1.a, t2.b) IN ((1, 2)); } {1 2} +# 2025-01-30 Inifinite loop in byte-code discovered by dbsqlfuzz +# having to do with SubrtnSig logic. The code was using a Subroutine +# from within itself resulting in infinite recursion. +# +# This test will spin forever if the bug has not been fixed, or if +# it reappears. +# +reset_db +do_execsql_test 4.0 { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + INSERT INTO t1 VALUES(1,x'1111'); + CREATE TABLE t2(c); + CREATE TABLE t3(d); + CREATE TRIGGER t1tr UPDATE ON t1 BEGIN + UPDATE t1 SET b=x'2222' FROM t2; + UPDATE t1 + SET b = (SELECT a IN (SELECT a + FROM t1 + WHERE (b,a) IN (SELECT rowid, d + FROM t3 + ) + ) + FROM t1 NATURAL RIGHT JOIN t1 + ); + END; + UPDATE t1 SET b=x'3333'; + SELECT quote(b) FROM t1; +} {X'3333'} + finish_test diff --git a/test/like3.test b/test/like3.test index a93e113d62..0b28574376 100644 --- a/test/like3.test +++ b/test/like3.test @@ -275,4 +275,84 @@ do_eqp_test like3-6.240 { } } +#------------------------------------------------------------------------- + +ifcapable utf16 { + reset_db + do_execsql_test like3-7.0 { + PRAGMA encoding = 'UTF-16be'; + + CREATE TABLE Example(word TEXT NOT NULL); + CREATE INDEX Example_word on Example(word); + + INSERT INTO Example VALUES(char(0x307F)); + } + + do_execsql_test like3-7.1 { + SELECT char(0x307F)=='み'; + } {1} + + do_execsql_test like3-7.1 { + SELECT * FROM Example WHERE word GLOB 'み*' + } {み} + + do_execsql_test like3-7.2 { + SELECT * FROM Example WHERE word >= char(0x307F) AND word < char(0x3080); + } {み} +} + +#------------------------------------------------------------------------- +reset_db + +foreach enc { + UTF-8 + UTF-16le + UTF-16be +} { + foreach {tn expr} { + 1 "CAST (X'FF' AS TEXT)" + 2 "CAST (X'FFBF' AS TEXT)" + 3 "CAST (X'FFBFBF' AS TEXT)" + 4 "CAST (X'FFBFBFBF' AS TEXT)" + + 5 "'abc' || CAST (X'FF' AS TEXT)" + 6 "'def' || CAST (X'FFBF' AS TEXT)" + 7 "'ghi' || CAST (X'FFBFBF' AS TEXT)" + 8 "'jkl' || CAST (X'FFBFBFBF' AS TEXT)" + } { + reset_db + execsql "PRAGMA encoding = '$enc'" + set tn utf[string range $enc 4 end].$tn + do_execsql_test like3-8.$tn.1 { + CREATE TABLE t1(x); + } + + do_execsql_test like3-8.$tn.2 { + PRAGMA encoding + } $enc + + do_execsql_test like3-8.$tn.3 " + INSERT INTO t1 VALUES( $expr ) + " + + do_execsql_test like3-8.$tn.4 { + SELECT typeof(x) FROM t1 + } {text} + + set x [db one {SELECT x || '%' FROM t1}] + + do_execsql_test like3-8.$tn.5 { + SELECT rowid FROM t1 WHERE x LIKE $x + } 1 + + do_execsql_test like3-8.$tn.6 { + CREATE INDEX i1 ON t1(x); + } + + do_execsql_test like3-8.$tn.7 { + SELECT rowid FROM t1 WHERE x LIKE $x + } 1 + } +} + finish_test diff --git a/test/pragma4.test b/test/pragma4.test index 0466960cab..2ba87c0c60 100644 --- a/test/pragma4.test +++ b/test/pragma4.test @@ -301,7 +301,7 @@ ifcapable vtab { do_test 6.3 { set ::log } {} - test_sqlite3_log {} + test_sqlite3_log } # 2024-05-08 https://sqlite.org/forum/forumpost/cf29a33e94 diff --git a/test/speedtest.md b/test/speedtest.md new file mode 100644 index 0000000000..135e562aed --- /dev/null +++ b/test/speedtest.md @@ -0,0 +1,53 @@ +# Performance And Size Measurements + +This document shows a procedure for making performance and size +comparisons between two versions of the SQLite Amalgamation "sqlite3.c". +You will need: + + * fossil + * valgrind + * tclsh + * A script or program named "open" that brings up *.txt files in an + editor for viewing. (Macs provide this by default. You'll need to + come up with your own on Linux and Windows.) + * An SQLite source tree + +The procedure described in this document is not the only way to make +performance and size measurements. Use this as a guide and make +adjustments as needed. + +## Establish the baseline measurement + + * Begin at the root the SQLite source tree + * mkdir -p ../speed
    + ↑ Speed measurement output files will go into this directory. + You can actually put those files wherever you want. This is just a + suggestion. It might be good to keep these files outside of the + source tree so that "fossil clean" does not delete them. + * Obtain the baseline SQLite amalgamation. For the purpose of this + technical note, assume the baseline SQLite sources are in files + "../baseline/sqlite3.c" and "../baseline/sqlite3.h". + * test/speedtest.tcl ../baseline/sqlite3.c ../speed/baseline.txt
    + ↑ The performance measure will be written into ../speed/baseline.txt + and that file will be brought up in an editor for easy viewing.
    + ↑ The "sqlite3.h" will be taken from the directory that contains + the "sqlite3.c" amalgamation file. + +## Comparing the current checkout against the baseline + + * make sqlite3.c + * test/speedtest.tcl sqlite3.c ../speed/test.txt ../speed/baseline.txt
    + ↑ Test results written into ../speed/test.txt and then + "fossil xdiff" is run to compare ../speed/baseline.txt against + the new test results. + +## When to do this + +Performance and size checks should be done prior to trunk check-ins. +Sometimes a seemingly innocuous change can have large performance +impacts. A large impact does not mean that the change cannot continue, +but it is important to be aware of the impact. + +## Additional hints + +Use the --help option to test/speedtest.tcl to see other available options. diff --git a/test/speedtest.tcl b/test/speedtest.tcl new file mode 100755 index 0000000000..1ad92d9ab0 --- /dev/null +++ b/test/speedtest.tcl @@ -0,0 +1,304 @@ +#!/bin/sh +# the next line restarts using tclsh \ +exec tclsh "$0" ${1+"$@"} +# +# This program runs performance testing on sqlite3.c. Usage: +set usage {USAGE: + + speedtest.tcl sqlite3.c x1.txt trunk.txt -Os -DSQLITE_ENABLE_STAT4 + | | | `-----------------------' + File to test ----' | | | + | | `- options + Output filename --------' | + `--- optional prior output to diff + +Do a cache-grind performance analysis of the sqlite3.c file named and +write the results into the output file. The ".txt" is appended to the +output file (and diff-file) name if it is not already present. If the +diff-file is specified then show a diff from the diff-file to the new +output. + +Other options include: + CC=... Specify an alternative C compiler. Default is "gcc". + -D... -D and -O options are passed through to the C compiler. + --dryrun Show what would happen but don't do anything. + --help Show this help screen. + --lean "Lean" mode. + --lookaside N SZ Lookahead uses N slots of SZ bytes each. + --pagesize N Use N as the page size. + --quiet | -q "Quite". Put results in file but don't pop up editor + --size N Change the test size. 100 means 100%. Default: 5. + --testset TEST Specify the specific testset to use. The default + is "mix1". Other options include: "main", "json", + "cte", "orm", "fp", "rtree". +} +set srcfile {} +set outfile {} +set difffile {} +set cflags {-DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_THREADSAFE=0} +set cc gcc +set testset mix1 +set dryrun 0 +set quiet 0 +set speedtestflags {--shrink-memory --reprepare --stats --heap 40000000 64} +lappend speedtestflags --journal wal --size 5 + +for {set i 0} {$i<[llength $argv]} {incr i} { + set arg [lindex $argv $i] + if {[string index $arg 0]=="-"} { + switch -- $arg { + -pagesize - + --pagesize { + lappend speedtestflags --pagesize + incr i + lappend speedtestflags [lindex $argv $i] + } + -lookaside - + --lookaside { + lappend speedtestflags --lookaside + incr i + lappend speedtestflags [lindex $argv $i] + incr i + lappend speedtestflags [lindex $argv $i] + } + -lean - + --lean { + lappend cflags \ + -DSQLITE_DEFAULT_MEMSTATUS=0 \ + -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \ + -DSQLITE_LIKE_DOESNT_MATCH_BLOBS=1 \ + -DSQLITE_MAX_EXPR_DEPTH=1 \ + -DSQLITE_OMIT_DECLTYPE \ + -DSQLITE_OMIT_DEPRECATED \ + -DSQLITE_OMIT_PROGRESS_CALLBACK \ + -DSQLITE_OMIT_SHARED_CACHE \ + -DSQLITE_USE_ALLOCA + } + -testset - + --testset { + incr i + set testset [lindex $argv $i] + } + -size - + --size { + incr i + set newsize [lindex $argv $i] + if {$newsize<1} {set newsize 1} + set speedtestflags \ + [regsub {.-size \d+} $speedtestflags "-size $newsize"] + } + -n - + -dryrun - + --dryrun { + set dryrun 1 + } + -? - + -help - + --help { + puts $usage + exit 0 + } + -q - + -quiet - + --quiet { + set quiet 1 + } + default { + lappend cflags $arg + } + } + continue + } + if {[string match CC=* $arg]} { + set cc [lrange $arg 3 end] + continue + } + if {[string match *.c $arg]} { + if {$srcfile!=""} { + puts stderr "multiple source files: $srcfile $arg" + exit 1 + } + set srcfile $arg + continue + } + if {[lsearch {main cte rtree orm fp json parsenumber mix1} $arg]>=0} { + set testset $arg + continue + } + if {$outfile==""} { + set outfile $arg + continue + } + if {$difffile==""} { + set difffile $arg + continue + } + puts stderr "unknown option: \"$arg\". Use --help for more info." + exit 1 +} +if {[lsearch -glob $cflags -O*]<0} { + lappend cflags -Os +} +if {[lsearch -glob $cflags -DSQLITE_ENABLE_MEMSYS*]<0} { + lappend cflags -DSQLITE_ENABLE_MEMSYS5 +} +if {[lsearch -glob $cflags -DSQLITE_ENABLE_RTREE*]<0} { + lappend cflags -DSQLITE_ENABLE_RTREE +} +if {$srcfile==""} { + puts stderr "no sqlite3.c source file specified" + exit 1 +} +if {![file readable $srcfile]} { + puts stderr "source file \"$srcfile\" does not exist" + exit 1 +} +if {$outfile==""} { + puts stderr "no output file specified" + exit 1 +} +if {![string match *.* [file tail $outfile]]} { + append outfile .txt +} +if {$difffile!=""} { + if {![file exists $difffile]} { + if {[file exists $difffile.txt]} { + append difffile .txt + } else { + puts stderr "No such file: \"$difffile\"" + exit 1 + } + } +} + +set cccmd [list $cc -g] +lappend cccmd -I[file dir $srcfile] +lappend cccmd {*}[lsort $cflags] +lappend cccmd [file dir $argv0]/speedtest1.c +lappend cccmd $srcfile +lappend cccmd -o speedtest1 +puts $cccmd +if {!$dryrun} { + exec {*}$cccmd +} +lappend speedtestflags --testset $testset +set stcmd [list valgrind --tool=cachegrind ./speedtest1 {*}$speedtestflags] +lappend stcmd speedtest1.db +lappend stcmd >valgrind-out.txt 2>valgrind-err.txt +puts $stcmd +if {!$dryrun} { + foreach file {speedtest1.db speedtest1.db-journal speedtest1.db-wal + speedtest1.db-shm} { + if {[file exists $file]} {file delete $file} + } + exec {*}$stcmd +} + +set maxmtime 0 +set cgfile {} +foreach cgout [glob -nocomplain cachegrind.out.*] { + if {[file mtime $cgout]>$maxmtime} { + set cgfile $cgout + set maxmtime [file mtime $cgfile] + } +} +if {$cgfile==""} { + puts "no cachegrind output" + exit 1 +} + +############# Process the cachegrind.out.# file ########################## +set fd [open $outfile wb] +set in [open "|cg_annotate --show=Ir --auto=yes --context=40 $cgfile" r] +set dest ! +set out(!) {} +set linenum 0 +set cntlines 0 ;# true to remember cycle counts on each line +set seenSqlite3 0 ;# true if we have seen the sqlite3.c file +while {![eof $in]} { + set line [string map {\t { }} [gets $in]] + if {[regexp {^-- Auto-annotated source: (.*)} $line all name]} { + set dest $name + if {[string match */sqlite3.c $dest]} { + set cntlines 1 + set seenSqlite3 1 + } else { + set cntlines 0 + } + } elseif {[regexp {^-- line (\d+) ------} $line all ln]} { + set line [lreplace $line 2 2 {#}] + set linenum [expr {$ln-1}] + } elseif {[regexp {^The following files chosen for } $line]} { + set dest ! + } + append out($dest) $line\n + if {$cntlines} { + incr linenum + if {[regexp {^ *([0-9,]+) } $line all x]} { + set x [string map {, {}} $x] + set cycles($linenum) $x + } + } +} +foreach x [lsort [array names out]] { + puts $fd $out($x) +} +# If the sqlite3.c file has been seen, then output a summary of the +# cycle counts for each file that went into making up sqlite3.c +# +if {$seenSqlite3} { + close $in + set in [open sqlite3.c] + set linenum 0 + set fn sqlite3.c + set pattern1 {^/\*+ Begin file ([^ ]+) \*} + set pattern2 {^/\*+ Continuing where we left off in ([^ ]+) \*} + while {![eof $in]} { + set line [gets $in] + incr linenum + if {[regexp $pattern1 $line all newfn]} { + set fn $newfn + } elseif {[regexp $pattern2 $line all newfn]} { + set fn $newfn + } elseif {[info exists cycles($linenum)]} { + incr fcycles($fn) $cycles($linenum) + } + } + close $in + puts $fd \ + {**********************************************************************} + set lx {} + set sum 0 + foreach {fn cnt} [array get fcycles] { + lappend lx [list $cnt $fn] + incr sum $cnt + } + puts $fd [format {%20s %14d %8.3f%%} TOTAL $sum 100] + foreach entry [lsort -index 0 -integer -decreasing $lx] { + foreach {cnt fn} $entry break + puts $fd [format {%20s %14d %8.3f%%} $fn $cnt [expr {$cnt*100.0/$sum}]] + } +} +puts $fd "Executable size:" +close $fd +exec size speedtest1 >>$outfile +# +# Processed cachegrind output should now be in the $outfile +############################################################################# + +if {$quiet} { + # Skip this last part of popping up a GUI viewer +} elseif {$difffile!=""} { + set fossilcmd {fossil xdiff --tk -c 20} + lappend fossilcmd $difffile + lappend fossilcmd $outfile + lappend fossilcmd & + puts $fossilcmd + if {!$dryrun} { + exec {*}$fossilcmd + } +} else { + if {!$dryrun} { + exec open $outfile + } +} diff --git a/test/speedtest1.c b/test/speedtest1.c index b0817858ae..b49c70098f 100644 --- a/test/speedtest1.c +++ b/test/speedtest1.c @@ -1,6 +1,28 @@ /* ** A program for performance testing. ** +** To build this program against an historical version of SQLite for comparison +** testing: +** +** Unix: +** +** ./configure --all +** make clean speedtest1 +** mv speedtest1 speedtest1-current +** cp $HISTORICAL_SQLITE3_C_H . +** touch sqlite3.c sqlite3.h .target_source +** make speedtest1 +** mv speedtest1 speedtest1-baseline +** +** Windows: +** +** nmake /f Makefile.msc clean speedtest1.exe +** mv speedtest1.exe speedtest1-current.exe +** cp $HISTORICAL_SQLITE_C_H . +** touch sqlite3.c sqlite3.h .target_source +** nmake /f Makefile.msc speedtest1.exe +** mv speedtest1.exe speedtest1-baseline.exe +** ** The available command-line options are described below: */ static const char zHelp[] = @@ -42,7 +64,9 @@ static const char zHelp[] = " --stats Show statistics at the end\n" " --stmtscanstatus Activate SQLITE_DBCONFIG_STMT_SCANSTATUS\n" " --temp N N from 0 to 9. 0: no temp table. 9: all temp tables\n" - " --testset T Run test-set T (main, cte, rtree, orm, fp, debug)\n" + " --testset T Run test-set T (main, cte, rtree, orm, fp, json,\n" + " star, app, debug). Can be a comma-separated list\n" + " of values, with /SCALE suffixes or macro \"mix1\"\n" " --trace Turn on SQL tracing\n" " --threads N Use up to N threads for sorting\n" " --utf16be Set text encoding to UTF-16BE\n" @@ -88,6 +112,8 @@ struct HashContext { /* All global state is held in this structure */ static struct Global { sqlite3 *db; /* The open database connection */ + const char *zDbName; /* Name of the database file */ + const char *zVfs; /* --vfs NAME */ sqlite3_stmt *pStmt; /* Current SQL statement */ sqlite3_int64 iStart; /* Start-time for the current test */ sqlite3_int64 iTotal; /* Total time */ @@ -99,6 +125,7 @@ static struct Global { int bMemShrink; /* Call sqlite3_db_release_memory() often */ int eTemp; /* 0: no TEMP. 9: always TEMP. */ int szTest; /* Scale factor for test iterations */ + int szBase; /* Base size prior to testset scaling */ int nRepeat; /* Repeat selects this many times */ int doCheckpoint; /* Run PRAGMA wal_checkpoint after each trans */ int nReserve; /* Reserve bytes */ @@ -1432,6 +1459,561 @@ void testset_fp(void){ speedtest1_end_test(); } +/* +** A testset for star-schema queries. +*/ +void testset_star(void){ + int n; + int i; + n = g.szTest*50; + speedtest1_begin_test(100, "Create a fact table with %d entries", n); + speedtest1_exec( + "CREATE TABLE facttab(" + " attr01 INT," + " attr02 INT," + " attr03 INT," + " data01 TEXT," + " attr04 INT," + " attr05 INT," + " attr06 INT," + " attr07 INT," + " attr08 INT," + " factid INTEGER PRIMARY KEY," + " data02 TEXT" + ");" + ); + speedtest1_exec( + "WITH RECURSIVE counter(nnn) AS" + "(VALUES(1) UNION ALL SELECT nnn+1 FROM counter WHERE nnn<%d)" + "INSERT INTO facttab(attr01,attr02,attr03,attr04,attr05," + "attr06,attr07,attr08,data01,data02)" + "SELECT random()%%12, random()%%13, random()%%14, random()%%15," + "random()%%16, random()%%17, random()%%18, random()%%19," + "concat('data-',nnn), format('%%x',random()) FROM counter;", + n + ); + speedtest1_end_test(); + + speedtest1_begin_test(110, "Create indexes on all attributes columns"); + for(i=1; i<=8; i++){ + speedtest1_exec( + "CREATE INDEX fact_attr%02d ON facttab(attr%02d)", i, i + ); + } + speedtest1_end_test(); + + speedtest1_begin_test(120, "Create dimension tables"); + for(i=1; i<=8; i++){ + speedtest1_exec( + "CREATE TABLE dimension%02d(" + "beta%02d INT, " + "content%02d TEXT, " + "rate%02d REAL)", + i, i, i, i + ); + speedtest1_exec( + "WITH RECURSIVE ctr(nn) AS" + " (VALUES(1) UNION ALL SELECT nn+1 FROM ctr WHERE nn<%d)" + " INSERT INTO dimension%02d" + " SELECT nn%%(%d), concat('content-%02d-',nn)," + " (random()%%10000)*0.125 FROM ctr;", + 4*(i+1), i, 2*(i+1), i + ); + if( i&2 ){ + speedtest1_exec( + "CREATE INDEX dim%02d ON dimension%02d(beta%02d);", + i, i, i + ); + }else{ + speedtest1_exec( + "CREATE INDEX dim%02d ON dimension%02d(beta%02d,content%02d);", + i, i, i, i + ); + } + } + speedtest1_end_test(); + + speedtest1_begin_test(130, "Star query over the entire fact table"); + speedtest1_exec( + "SELECT count(*), max(content04), min(content03), sum(rate04), avg(rate05)" + " FROM facttab, dimension01, dimension02, dimension03, dimension04," + " dimension05, dimension06, dimension07, dimension08" + " WHERE attr01=beta01" + " AND attr02=beta02" + " AND attr03=beta03" + " AND attr04=beta04" + " AND attr05=beta05" + " AND attr06=beta06" + " AND attr07=beta07" + " AND attr08=beta08" + ";" + ); + speedtest1_end_test(); + + speedtest1_begin_test(130, "Star query with LEFT JOINs"); + speedtest1_exec( + "SELECT count(*), max(content04), min(content03), sum(rate04), avg(rate05)" + " FROM facttab LEFT JOIN dimension01 ON attr01=beta01" + " LEFT JOIN dimension02 ON attr02=beta02" + " JOIN dimension03 ON attr03=beta03" + " JOIN dimension04 ON attr04=beta04" + " JOIN dimension05 ON attr05=beta05" + " LEFT JOIN dimension06 ON attr06=beta06" + " JOIN dimension07 ON attr07=beta07" + " JOIN dimension08 ON attr08=beta08" + " WHERE facttab.data01 LIKE 'data-9%%'" + ";" + ); + speedtest1_end_test(); +} + +/* +** Tests that simulate an application opening and closing an SQLite database +** frequently. Fossil is used as the model. The focus here is on rapidly +** parsing the database schema and rapidly generating prepared statements, +** in other words, rapid start-up of Fossil-like applications. +** +** The same database has no data, so the performance of sqlite3_step() is +** not significant to this testset. +*/ +static void testset_app(void){ + int i, n; + speedtest1_begin_test(100, "Generate a Fossil-like database schema"); + speedtest1_exec( + "BEGIN;" + "CREATE TABLE blob(\n" + " rid INTEGER PRIMARY KEY,\n" + " rcvid INTEGER,\n" + " size INTEGER,\n" + " uuid TEXT UNIQUE NOT NULL,\n" + " content BLOB,\n" + " CHECK( length(uuid)>=40 AND rid>0 )\n" + ");\n" + "CREATE TABLE delta(\n" + " rid INTEGER PRIMARY KEY,\n" + " srcid INTEGER NOT NULL REFERENCES blob\n" + ");\n" + "CREATE TABLE rcvfrom(\n" + " rcvid INTEGER PRIMARY KEY,\n" + " uid INTEGER REFERENCES user,\n" + " mtime DATETIME,\n" + " nonce TEXT UNIQUE,\n" + " ipaddr TEXT\n" + ");\n" + "CREATE TABLE private(rid INTEGER PRIMARY KEY);\n" + "CREATE TABLE accesslog(\n" + " uname TEXT,\n" + " ipaddr TEXT,\n" + " success BOOLEAN,\n" + " mtime TIMESTAMP\n" + ");\n" + "CREATE TABLE user(\n" + " uid INTEGER PRIMARY KEY,\n" + " login TEXT UNIQUE,\n" + " pw TEXT,\n" + " cap TEXT,\n" + " cookie TEXT,\n" + " ipaddr TEXT,\n" + " cexpire DATETIME,\n" + " info TEXT,\n" + " mtime DATE,\n" + " photo BLOB\n" + ", jx TEXT DEFAULT '{}');\n" + "CREATE TABLE reportfmt(\n" + " rn INTEGER PRIMARY KEY,\n" + " owner TEXT,\n" + " title TEXT UNIQUE,\n" + " mtime INTEGER,\n" + " cols TEXT,\n" + " sqlcode TEXT\n" + ", jx TEXT DEFAULT '{}');\n" + "CREATE TABLE config(\n" + " name TEXT PRIMARY KEY NOT NULL,\n" + " value CLOB, mtime INTEGER,\n" + " CHECK( typeof(name)='text' AND length(name)>=1 )\n" + ") WITHOUT ROWID;\n" + "CREATE TABLE shun(uuid PRIMARY KEY, mtime INTEGER, scom TEXT)\n" + " WITHOUT ROWID;\n" + "CREATE TABLE concealed(\n" + " hash TEXT PRIMARY KEY,\n" + " content TEXT\n" + ", mtime INTEGER) WITHOUT ROWID;\n" + "CREATE TABLE admin_log(\n" + " id INTEGER PRIMARY KEY,\n" + " time INTEGER, -- Seconds since 1970\n" + " page TEXT, -- path of page\n" + " who TEXT, -- User who made the change\n" + " what TEXT -- What changed\n" + ");\n" + "CREATE TABLE unversioned(\n" + " name TEXT PRIMARY KEY,\n" + " rcvid INTEGER,\n" + " mtime DATETIME,\n" + " hash TEXT,\n" + " sz INTEGER,\n" + " encoding INT,\n" + " content BLOB\n" + ") WITHOUT ROWID;\n" + "CREATE TABLE subscriber(\n" + " subscriberId INTEGER PRIMARY KEY,\n" + " subscriberCode BLOB DEFAULT (randomblob(32)) UNIQUE,\n" + " semail TEXT UNIQUE COLLATE nocase,\n" + " suname TEXT,\n" + " sverified BOOLEAN DEFAULT true,\n" + " sdonotcall BOOLEAN,\n" + " sdigest BOOLEAN,\n" + " ssub TEXT,\n" + " sctime INTDATE,\n" + " mtime INTDATE,\n" + " smip TEXT\n" + ", lastContact INT);\n" + "CREATE TABLE pending_alert(\n" + " eventid TEXT PRIMARY KEY,\n" + " sentSep BOOLEAN DEFAULT false,\n" + " sentDigest BOOLEAN DEFAULT false\n" + ", sentMod BOOLEAN DEFAULT false) WITHOUT ROWID;\n" + "CREATE TABLE filename(\n" + " fnid INTEGER PRIMARY KEY,\n" + " name TEXT UNIQUE\n" + ") STRICT;\n" + "CREATE TABLE mlink(\n" + " mid INTEGER,\n" + " fid INTEGER,\n" + " pmid INTEGER,\n" + " pid INTEGER,\n" + " fnid INTEGER REFERENCES filename,\n" + " pfnid INTEGER,\n" + " mperm INTEGER,\n" + " isaux INT DEFAULT 0\n" + ") STRICT;\n" + "CREATE TABLE plink(\n" + " pid INTEGER REFERENCES blob,\n" + " cid INTEGER REFERENCES blob,\n" + " isprim INT,\n" + " mtime REAL,\n" + " baseid INTEGER REFERENCES blob,\n" + " UNIQUE(pid, cid)\n" + ") STRICT;\n" + "CREATE TABLE leaf(rid INTEGER PRIMARY KEY);\n" + "CREATE TABLE event(\n" + " type TEXT,\n" + " mtime REAL,\n" + " objid INTEGER PRIMARY KEY,\n" + " tagid INTEGER,\n" + " uid INTEGER REFERENCES user,\n" + " bgcolor TEXT,\n" + " euser TEXT,\n" + " user TEXT,\n" + " ecomment TEXT,\n" + " comment TEXT,\n" + " brief TEXT,\n" + " omtime REAL\n" + ") STRICT;\n" + "CREATE TABLE phantom(\n" + " rid INTEGER PRIMARY KEY\n" + ");\n" + "CREATE TABLE orphan(\n" + " rid INTEGER PRIMARY KEY,\n" + " baseline INTEGER\n" + ") STRICT;\n" + "CREATE TABLE unclustered(\n" + " rid INTEGER PRIMARY KEY\n" + ");\n" + "CREATE TABLE unsent(\n" + " rid INTEGER PRIMARY KEY\n" + ");\n" + "CREATE TABLE tag(\n" + " tagid INTEGER PRIMARY KEY,\n" + " tagname TEXT UNIQUE\n" + ") STRICT;\n" + "CREATE TABLE tagxref(\n" + " tagid INTEGER REFERENCES tag,\n" + " tagtype INTEGER,\n" + " srcid INTEGER REFERENCES blob,\n" + " origid INTEGER REFERENCES blob,\n" + " value TEXT,\n" + " mtime REAL,\n" + " rid INTEGER REFERENCES blob,\n" + " UNIQUE(rid, tagid)\n" + ") STRICT;\n" + "CREATE TABLE backlink(\n" + " target TEXT,\n" + " srctype INT,\n" + " srcid INT,\n" + " mtime REAL,\n" + " UNIQUE(target, srctype, srcid)\n" + ") STRICT;\n" + "CREATE TABLE attachment(\n" + " attachid INTEGER PRIMARY KEY,\n" + " isLatest INT DEFAULT 0,\n" + " mtime REAL,\n" + " src TEXT,\n" + " target TEXT,\n" + " filename TEXT,\n" + " comment TEXT,\n" + " user TEXT\n" + ") STRICT;\n" + "CREATE TABLE cherrypick(\n" + " parentid INT,\n" + " childid INT,\n" + " isExclude INT DEFAULT false,\n" + " PRIMARY KEY(parentid, childid)\n" + ") WITHOUT ROWID, STRICT;\n" + "CREATE TABLE vcache(\n" + " vid INTEGER, -- check-in ID\n" + " fname TEXT, -- filename\n" + " rid INTEGER, -- artifact ID\n" + " PRIMARY KEY(vid,fname)\n" + ") WITHOUT ROWID;\n" + "CREATE TABLE synclog(\n" + " sfrom TEXT,\n" + " sto TEXT,\n" + " stime INT NOT NULL,\n" + " stype TEXT,\n" + " PRIMARY KEY(sfrom,sto)\n" + ") WITHOUT ROWID;\n" + "CREATE TABLE chat(\n" + " msgid INTEGER PRIMARY KEY AUTOINCREMENT,\n" + " mtime JULIANDAY,\n" + " lmtime TEXT,\n" + " xfrom TEXT,\n" + " xmsg TEXT,\n" + " fname TEXT,\n" + " fmime TEXT,\n" + " mdel INT,\n" + " file BLOB\n" + ");\n" + "CREATE TABLE ftsdocs(\n" + " rowid INTEGER PRIMARY KEY,\n" + " type CHAR(1),\n" + " rid INTEGER,\n" + " name TEXT,\n" + " idxed BOOLEAN,\n" + " label TEXT,\n" + " url TEXT,\n" + " mtime DATE,\n" + " bx TEXT,\n" + " UNIQUE(type,rid)\n" + ");\n" + "CREATE TABLE ticket(\n" + " -- Do not change any column that begins with tkt_\n" + " tkt_id INTEGER PRIMARY KEY,\n" + " tkt_uuid TEXT UNIQUE,\n" + " tkt_mtime DATE,\n" + " tkt_ctime DATE,\n" + " -- Add as many fields as required below this line\n" + " type TEXT,\n" + " status TEXT,\n" + " subsystem TEXT,\n" + " priority TEXT,\n" + " severity TEXT,\n" + " foundin TEXT,\n" + " private_contact TEXT,\n" + " resolution TEXT,\n" + " title TEXT,\n" + " comment TEXT\n" + ");\n" + "CREATE TABLE ticketchng(\n" + " -- Do not change any column that begins with tkt_\n" + " tkt_id INTEGER REFERENCES ticket,\n" + " tkt_rid INTEGER REFERENCES blob,\n" + " tkt_mtime DATE,\n" + " tkt_user TEXT,\n" + " -- Add as many fields as required below this line\n" + " login TEXT,\n" + " username TEXT,\n" + " mimetype TEXT,\n" + " icomment TEXT\n" + ");\n" + "CREATE TABLE forumpost(\n" + " fpid INTEGER PRIMARY KEY,\n" + " froot INT,\n" + " fprev INT,\n" + " firt INT,\n" + " fmtime REAL\n" + ");\n" + "CREATE INDEX delta_i1 ON delta(srcid);\n" + "CREATE INDEX blob_rcvid ON blob(rcvid);\n" + "CREATE INDEX subscriberUname\n" + " ON subscriber(suname) WHERE suname IS NOT NULL;\n" + "CREATE INDEX mlink_i1 ON mlink(mid);\n" + "CREATE INDEX mlink_i2 ON mlink(fnid);\n" + "CREATE INDEX mlink_i3 ON mlink(fid);\n" + "CREATE INDEX mlink_i4 ON mlink(pid);\n" + "CREATE INDEX plink_i2 ON plink(cid,pid);\n" + "CREATE INDEX event_i1 ON event(mtime);\n" + "CREATE INDEX orphan_baseline ON orphan(baseline);\n" + "CREATE INDEX tagxref_i1 ON tagxref(tagid, mtime);\n" + "CREATE INDEX backlink_src ON backlink(srcid, srctype);\n" + "CREATE INDEX attachment_idx1 ON attachment(target, filename, mtime);\n" + "CREATE INDEX attachment_idx2 ON attachment(src);\n" + "CREATE INDEX cherrypick_cid ON cherrypick(childid);\n" + "CREATE INDEX ftsdocIdxed ON ftsdocs(type,rid,name) WHERE idxed==0;\n" + "CREATE INDEX ftsdocName ON ftsdocs(name) WHERE type='w';\n" + "CREATE INDEX ticketchng_idx1 ON ticketchng(tkt_id, tkt_mtime);\n" + "CREATE INDEX forumthread ON forumpost(froot,fmtime);\n" + "CREATE VIEW artifact(rid,rcvid,size,atype,srcid,hash,content) AS\n" + " SELECT blob.rid,rcvid,size,1,srcid,uuid,content\n" + " FROM blob LEFT JOIN delta ON (blob.rid=delta.rid);\n" + "CREATE VIEW ftscontent AS\n" + " SELECT rowid, type, rid, name, idxed, label, url, mtime,\n" + " title(type,rid,name) AS 'title', body(type,rid,name) AS 'body'\n" + " FROM ftsdocs;\n" + ); + if( sqlite3_compileoption_used("ENABLE_FTS5") ){ + speedtest1_exec( + "CREATE VIRTUAL TABLE ftsidx\n" + " USING fts5(content=\"ftscontent\", title, body);\n" + "CREATE VIRTUAL TABLE chatfts1 USING fts5(\n" + " xmsg, content=chat, content_rowid=msgid,tokenize=porter);\n" + ); + }else{ + speedtest1_exec( + "CREATE TABLE ftsidx_data(id INTEGER PRIMARY KEY, block BLOB);\n" + "CREATE TABLE ftsidx_idx(segid, term, pgno, PRIMARY KEY(segid, term))\n" + " WITHOUT ROWID;\n" + "CREATE TABLE ftsidx_docsize(id INTEGER PRIMARY KEY, sz BLOB);\n" + "CREATE TABLE ftsidx_config(k PRIMARY KEY, v) WITHOUT ROWID;\n" + "CREATE TABLE chatfts1_data(id INTEGER PRIMARY KEY, block BLOB);\n" + "CREATE TABLE chatfts1_idx(segid, term, pgno, PRIMARY KEY(segid, term))\n" + " WITHOUT ROWID;\n" + "CREATE TABLE chatfts1_docsize(id INTEGER PRIMARY KEY, sz BLOB);\n" + "CREATE TABLE chatfts1_config(k PRIMARY KEY, v) WITHOUT ROWID;\n" + ); + } + speedtest1_exec( + "ANALYZE sqlite_schema;\n" + "INSERT INTO sqlite_stat1(tbl,idx,stat) VALUES\n" + " ('ftsidx_config','ftsidx_config','1 1'),\n" + " ('ftsidx_idx','ftsidx_idx','4215 401 1'),\n" + " ('user','sqlite_autoindex_user_1','25 1'),\n" + " ('phantom',NULL,'26'),\n" + " ('reportfmt','sqlite_autoindex_reportfmt_1','9 1'),\n" + " ('rcvfrom','sqlite_autoindex_rcvfrom_1','18445 401'),\n" + " ('private',NULL,'99'),\n" + " ('mlink','mlink_i4','116678 401'),\n" + " ('mlink','mlink_i3','121212 2'),\n" + " ('mlink','mlink_i2','106372 401'),\n" + " ('mlink','mlink_i1','99298 5'),\n" + " ('ftsidx_data',NULL,'3795'),\n" + " ('leaf',NULL,'1559'),\n" + " ('delta','delta_i1','66340 1'),\n" + " ('unversioned','unversioned','3 1'),\n" + " ('pending_alert','pending_alert','3 1'),\n" + " ('cherrypick','cherrypick_cid','680 2'),\n" + " ('cherrypick','cherrypick','628 1 1'),\n" + " ('config','config','128 1'),\n" + " ('ftsidx_docsize',NULL,'33848'),\n" + " ('event','event_i1','36096 1'),\n" + " ('plink','plink_i2','38236 1 1'),\n" + " ('plink','sqlite_autoindex_plink_1','38357 1 1'),\n" + " ('shun','shun','10 1'),\n" + " ('concealed','concealed','110 1'),\n" + " ('vcache','vcache','1888 401 1'),\n" + " ('ftsdocs','ftsdocName','19 1'),\n" + " ('ftsdocs','ftsdocIdxed','168 84 1 1'),\n" + " ('ftsdocs','sqlite_autoindex_ftsdocs_1','37312 401 1'),\n" + " ('subscriber','subscriberUname','5 1'),\n" + " ('subscriber','sqlite_autoindex_subscriber_2','37 1'),\n" + " ('subscriber','sqlite_autoindex_subscriber_1','37 1'),\n" + " ('tag','sqlite_autoindex_tag_1','2990 1'),\n" + " ('filename','sqlite_autoindex_filename_1','3168 1'),\n" + " ('chat',NULL,'56124'),\n" + " ('tagxref','tagxref_i1','40992 401 2'),\n" + " ('tagxref','sqlite_autoindex_tagxref_1','79233 3 1'),\n" + " ('attachment','attachment_idx2','11 1'),\n" + " ('attachment','attachment_idx1','11 2 2 1'),\n" + " ('blob','blob_rcvid','128240 201'),\n" + " ('blob','sqlite_autoindex_blob_1','126480 1'),\n" + " ('synclog','synclog','12 3 1'),\n" + " ('backlink','backlink_src','2160 2 2'),\n" + " ('backlink','sqlite_autoindex_backlink_1','2340 2 2 1'),\n" + " ('accesslog',NULL,'38'),\n" + " ('chatfts1_config','chatfts1_config','1 1'),\n" + " ('chatfts1_idx','chatfts1_idx','688 230 1'),\n" + " ('ticket','sqlite_autoindex_ticket_1','794 1'),\n" + " ('ticketchng','ticketchng_idx1','2089 3 1'),\n" + " ('forumpost','forumthread','4 4 1'),\n" + " ('unclustered',NULL,'12');\n" + "COMMIT;" + ); + speedtest1_end_test(); + + n = g.szTest*3; + speedtest1_begin_test(110, "Open and use the database %d times", n); + for(i=0; i=$date OR parent.pid=$pid)\n" + " ORDER BY mtime DESC LIMIT 10\n" + " )\n" + " INSERT OR IGNORE INTO ok SELECT rid FROM ancestor;" + ); + sqlite3_close(dbAux); + g.db = dbMain; + } + speedtest1_end_test(); +} + #ifdef SQLITE_ENABLE_RTREE /* Generate two numbers between 1 and mx. The first number is less than ** the second. Usually the numbers are near each other but can sometimes @@ -2149,6 +2731,120 @@ void testset_debug1(void){ } } +/* +** Performance tests for JSON. +*/ +void testset_json(void){ + unsigned int r = 0x12345678; + sqlite3_test_control(SQLITE_TESTCTRL_PRNG_SEED, r, g.db); + speedtest1_begin_test(100, "table J1 is %d rows of JSONB", + g.szTest*5); + speedtest1_exec( + "CREATE TABLE j1(x JSONB);\n" + "WITH RECURSIVE\n" + " jval(n,j) AS (\n" + " VALUES(0,'{}'),(1,'[]'),(2,'true'),(3,'false'),(4,'null'),\n" + " (5,'{x:1,y:2}'),(6,'0.0'),(7,'3.14159'),(8,'-99.9'),\n" + " (9,'[1,2,\"\\n\\u2192\\\"\\u2190\",4]')\n" + " ),\n" + " c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<26*26-1),\n" + " array1(y) AS MATERIALIZED (\n" + " SELECT jsonb_group_array(\n" + " jsonb_object('x',x,\n" + " 'y',jsonb(coalesce(j,random()%%10000)),\n" + " 'z',hex(randomblob(50)))\n" + " )\n" + " FROM c LEFT JOIN jval ON (x%%20)=n\n" + " ),\n" + " object1(z) AS MATERIALIZED (\n" + " SELECT jsonb_group_object(char(0x61+x%%26,0x61+(x/26)%%26),\n" + " jsonb( coalesce(j,random()%%10000)))\n" + " FROM c LEFT JOIN jval ON (x%%20)=n\n" + " ),\n" + " c2(n) AS (VALUES(1) UNION ALL SELECT n+1 FROM c2 WHERE n<%d)\n" + "INSERT INTO j1(x)\n" + " SELECT jsonb_object('a',n,'b',n+10000,'c',jsonb(y),'d',jsonb(z),\n" + " 'e',n+20000,'f',n+30000)\n" + " FROM array1, object1, c2;", + g.szTest*5 + ); + speedtest1_end_test(); + + speedtest1_begin_test(110, "table J2 is %d rows from J1 converted to text", g.szTest); + speedtest1_exec( + "CREATE TABLE j2(x JSON TEXT);\n" + "INSERT INTO j2(x) SELECT json(x) FROM j1 LIMIT %d", g.szTest + ); + speedtest1_end_test(); + + speedtest1_begin_test(120, "create indexes on JSON expressions on J1"); + speedtest1_exec( + "BEGIN;\n" + "CREATE INDEX j1x1 ON j1(x->>'a');\n" + "CREATE INDEX j1x2 ON j1(x->>'b');\n" + "CREATE INDEX j1x3 ON j1(x->>'f');\n" + "COMMIT;\n" + ); + speedtest1_end_test(); + + speedtest1_begin_test(130, "create indexes on JSON expressions on J2"); + speedtest1_exec( + "BEGIN;\n" + "CREATE INDEX j2x1 ON j2(x->>'a');\n" + "CREATE INDEX j2x2 ON j2(x->>'b');\n" + "CREATE INDEX j2x3 ON j2(x->>'f');\n" + "COMMIT;\n" + ); + speedtest1_end_test(); + + speedtest1_begin_test(140, "queries against J1"); + speedtest1_exec( + "WITH c(n) AS (VALUES(0) UNION ALL SELECT n+1 FROM c WHERE n<7)\n" + " SELECT sum(x->>format('$.c[%%d].x',n)) FROM c, j1;\n" + + "WITH c(n) AS (VALUES(1) UNION ALL SELECT n+1 FROM c WHERE n<5)\n" + " SELECT sum(x->>format('$.\"c\"[#-%%d].y',n)) FROM c, j1;\n" + + "SELECT sum(x->>'$.d.ez' + x->>'$.d.\"xz\"' + x->>'a' + x->>'$.c[10].y') FROM j1;\n" + + "SELECT x->>'$.d.tz[2]', x->'$.d.tz' FROM j1;\n" + ); + speedtest1_end_test(); + + speedtest1_begin_test(141, "queries involving json_type()"); + speedtest1_exec( + "WITH c(n) AS (VALUES(1) UNION ALL SELECT n+1 FROM c WHERE n<20)\n" + " SELECT json_type(x,format('$.c[#-%%d].y',n)), count(*)\n" + " FROM c, j1\n" + " WHERE j1.rowid=1\n" + " GROUP BY 1 ORDER BY 2;" + ); + speedtest1_end_test(); + + + speedtest1_begin_test(150, "json_insert()/set()/remove() on every row of J1"); + speedtest1_exec( + "BEGIN;\n" + "UPDATE j1 SET x=jsonb_insert(x,'$.g',(x->>'f')+1,'$.h',3.14159,'$.i','hello',\n" + " '$.j',json('{x:99}'),'$.k','{y:98}');\n" + "UPDATE j1 SET x=jsonb_set(x,'$.e',(x->>'f')-1);\n" + "UPDATE j1 SET x=jsonb_remove(x,'$.d');\n" + "COMMIT;\n" + ); + speedtest1_end_test(); + + speedtest1_begin_test(160, "json_insert()/set()/remove() on every row of J2"); + speedtest1_exec( + "BEGIN;\n" + "UPDATE j2 SET x=json_insert(x,'$.g',(x->>'f')+1);\n" + "UPDATE j2 SET x=json_set(x,'$.e',(x->>'f')-1);\n" + "UPDATE j2 SET x=json_remove(x,'$.d');\n" + "COMMIT;\n" + ); + speedtest1_end_test(); + +} + /* ** This testset focuses on the speed of parsing numeric literals (integers ** and real numbers). This was added to test the impact of allowing "_" @@ -2168,25 +2864,25 @@ void testset_parsenumber(void){ const int NROW = 100*g.szTest; int ii; - speedtest1_begin_test(100, "parsing small integers"); + speedtest1_begin_test(100, "parsing %d small integers", NROW); for(ii=0; iixDelete(pVfs, zDbName, 1); + pVfs->xDelete(pVfs, g.zDbName, 1); } - unlink(zDbName); + unlink(g.zDbName); } /* Open the database and the input file */ - if( sqlite3_open_v2(memDb ? ":memory:" : zDbName, &g.db, - openFlags, zVfs) ){ - fatal_error("Cannot open database file: %s\n", zDbName); + if( sqlite3_open_v2(memDb ? ":memory:" : g.zDbName, &g.db, + openFlags, g.zVfs) ){ + fatal_error("Cannot open database file: %s\n", g.zDbName); } #if SQLITE_VERSION_NUMBER>=3006001 if( nLook>0 && szLook>0 ){ @@ -2572,8 +3273,10 @@ int main(int argc, char **argv){ } if( g.bExplain ) printf(".explain\n.echo on\n"); + if( strcmp(zTSet,"mix1")==0 ) zTSet = zMix1Tests; do{ char *zThisTest = zTSet; + char *zSep; char *zComma = strchr(zThisTest,','); if( zComma ){ *zComma = 0; @@ -2581,7 +3284,20 @@ int main(int argc, char **argv){ }else{ zTSet = ""; } - if( g.iTotal>0 || zComma!=0 ){ + zSep = strchr(zThisTest, '/'); + if( zSep ){ + int kk; + for(kk=1; zSep[kk] && ISDIGIT(zSep[kk]); kk++){} + if( kk==1 || zSep[kk]!=0 ){ + fatal_error("bad modifier on testset name: \"%s\"", zThisTest); + } + g.szTest = g.szBase*integerValue(zSep+1)/100; + if( g.szTest<=0 ) g.szTest = 1; + zSep[0] = 0; + }else{ + g.szTest = g.szBase; + } + if( g.iTotal>0 || zComma==0 ){ printf(" Begin testset \"%s\"\n", zThisTest); } if( strcmp(zThisTest,"main")==0 ){ @@ -2592,8 +3308,14 @@ int main(int argc, char **argv){ testset_orm(); }else if( strcmp(zThisTest,"cte")==0 ){ testset_cte(); + }else if( strcmp(zThisTest,"star")==0 ){ + testset_star(); + }else if( strcmp(zThisTest,"app")==0 ){ + testset_app(); }else if( strcmp(zThisTest,"fp")==0 ){ testset_fp(); + }else if( strcmp(zThisTest,"json")==0 ){ + testset_json(); }else if( strcmp(zThisTest,"trigger")==0 ){ testset_trigger(); }else if( strcmp(zThisTest,"parsenumber")==0 ){ diff --git a/test/starschema1.test b/test/starschema1.test index af8168b510..bb7d8aa79b 100644 --- a/test/starschema1.test +++ b/test/starschema1.test @@ -10,7 +10,7 @@ #*********************************************************************** # # Test cases for the ability of the query planner to cope with -# star-schema queries on databases with goofy indexes. +# star-schema queries. # set testdir [file dirname $argv0] diff --git a/test/testrunner_data.tcl b/test/testrunner_data.tcl index c749481f51..4685dabf5f 100644 --- a/test/testrunner_data.tcl +++ b/test/testrunner_data.tcl @@ -98,6 +98,7 @@ namespace eval trd { set build(All-Debug) { --with-debug --enable-all -DSQLITE_ENABLE_ORDERED_SET_AGGREGATES + -DSQLITE_ENABLE_NORMALIZE } set build(All-O0) { -O0 --enable-all @@ -111,6 +112,7 @@ namespace eval trd { CC=clang -fsanitize=address,undefined -fno-sanitize-recover=undefined -DSQLITE_ENABLE_STAT4 -DSQLITE_OMIT_LOOKASIDE=1 + -DSQLITE_ENABLE_NORMALIZE -DCONFIG_SLOWDOWN_FACTOR=5.0 -DSQLITE_ENABLE_RBU --with-debug @@ -169,6 +171,7 @@ namespace eval trd { -DSQLITE_SOUNDEX=1 -DSQLITE_ENABLE_ATOMIC_WRITE=1 -DSQLITE_ENABLE_MEMORY_MANAGEMENT=1 + -DSQLITE_ENABLE_NORMALIZE -DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1 -DSQLITE_ENABLE_STAT4 -DSQLITE_ENABLE_STMT_SCANSTATUS @@ -184,6 +187,7 @@ namespace eval trd { -DSQLITE_ENABLE_FTS3=1 -DSQLITE_ENABLE_RTREE=1 -DSQLITE_ENABLE_MEMSYS5=1 + -DSQLITE_ENABLE_NORMALIZE -DSQLITE_ENABLE_COLUMN_METADATA=1 -DSQLITE_ENABLE_STAT4 -DSQLITE_ENABLE_HIDDEN_COLUMNS @@ -300,6 +304,7 @@ namespace eval trd { -DSQLITE_ENABLE_FTS3=1 -DSQLITE_ENABLE_FTS3_PARENTHESIS=1 -DSQLITE_ENABLE_FTS3_TOKENIZER=1 + -DSQLITE_ENABLE_NORMALIZE=1 -DSQLITE_ENABLE_PERSIST_WAL=1 -DSQLITE_ENABLE_PURGEABLE_PCACHE=1 -DSQLITE_ENABLE_RTREE=1 diff --git a/test/trace3.test b/test/trace3.test index 496cc2360a..639aefafa6 100644 --- a/test/trace3.test +++ b/test/trace3.test @@ -342,5 +342,21 @@ do_test 12.1.2 { sqlite3_finalize $STMT } {SQLITE_OK} +#------------------------------------------------------------------------- +reset_db +do_execsql_test 13.0 { + CREATE TABLE T1(a, b); + INSERT INTO t1 VALUES(1, 2), (3, 4); +} + +proc trace_callback {args} {} +db trace_v2 trace_callback profile + +do_test 13.1 { + db eval { SELECT * FROM t1 } { + db trace_v2 "" "" + } + set {} {} +} {} finish_test diff --git a/test/walsetlk3.test b/test/walsetlk3.test index cbd2f7247e..efd5cdf92d 100644 --- a/test/walsetlk3.test +++ b/test/walsetlk3.test @@ -18,6 +18,7 @@ source $testdir/lock_common.tcl set testprefix walsetlk3 ifcapable !wal {finish_test ; return } +ifcapable !setlk_timeout {finish_test ; return } do_execsql_test 1.0 { CREATE TABLE t1(x, y); diff --git a/test/with6.test b/test/with6.test index 95e6305474..b95ec0b763 100644 --- a/test/with6.test +++ b/test/with6.test @@ -325,6 +325,12 @@ do_eqp_test 331 { # marked with M10d_Yes and hence prohibited from participating in the # query flattening optimization. # +# Updated 2025-01-02. +# https://sqlite.org/forum/forumpost/8f38fc9878a92aa9 +# +# The same optimization that made Grunthos's query fast made +# Jean-Noël Mayor's query slow. Bummer. +# reset_db db eval { CREATE TABLE raw(country,date,total,delta, UNIQUE(country,date)); diff --git a/tool/buildtclext.tcl b/tool/buildtclext.tcl index c74540c9ea..905087d1da 100644 --- a/tool/buildtclext.tcl +++ b/tool/buildtclext.tcl @@ -15,6 +15,7 @@ Options: --info Show info on existing SQLite TCL extension installs --install-only Install an extension previously build --uninstall Uninstall the extension + --version-check Check extension version against this source tree --destdir DIR Installation root (used by "make install DESTDIR=...") Other options are retained and passed through into the compiler.} @@ -24,6 +25,7 @@ set build 1 set install 1 set uninstall 0 set infoonly 0 +set versioncheck 0 set CC {} set OPTS {} set DESTDIR ""; # --destdir "$(DESTDIR)" @@ -36,11 +38,18 @@ for {set ii 0} {$ii<[llength $argv]} {incr ii} { } elseif {$a0=="--uninstall"} { set build 0 set install 0 + set versioncheck 0 set uninstall 1 } elseif {$a0=="--info"} { set build 0 set install 0 + set versioncheck 0 set infoonly 1 + } elseif {$a0=="--version-check"} { + set build 0 + set install 0 + set infoonly 0 + set versioncheck 1 } elseif {$a0=="--cc" && $ii+1<[llength $argv]} { incr ii set CC [lindex $argv $ii] @@ -156,6 +165,33 @@ if {$tcl_platform(platform)=="windows"} { set CMD [subst $cmd] } +# Check the SQLite TCL extension that is loaded by default by this running +# TCL interpreter to see if it has the same SQLITE_SOURCE_ID as the source +# code in the directory holding this script. +# +if {$versioncheck} { + if {[catch {package require sqlite3} msg]} { + puts stderr "No SQLite TCL extension available: $msg" + exit 1 + } + sqlite3 db :memory: + set extvers [db one {SELECT sqlite_source_id()}] + db close + set fd [open sqlite3.h rb] + set sqlite3h [read $fd] + close $fd + regexp {#define SQLITE_SOURCE_ID +"([^"]+)"} $sqlite3h all srcvers + set srcvers [string range $srcvers 0 78] + set extvers [string range $extvers 0 78] + if {$srcvers==$extvers} { + puts "source code and extension versions aligned:\n$extvers" + exit 0 + } + puts stderr "source code and extension versions differ" + puts stderr "source: $srcvers\nextension: $extvers" + exit 1 +} + # Show information about prior installs # if {$infoonly} { @@ -253,7 +289,7 @@ if {$build} { # Tcl package index file, version ??? # package ifneeded sqlite3 $VERSION \\ - [list load [file join \$dir $OUT] sqlite3] + [list load [file join \$dir $OUT] Sqlite3] }] close $fd diff --git a/tool/emcc.sh.in b/tool/emcc.sh.in index 1263e1b0ea..1264df5376 100644 --- a/tool/emcc.sh.in +++ b/tool/emcc.sh.in @@ -63,4 +63,4 @@ if [ x = "x${emcc}" ]; then fi fi -exec emcc "$@" +exec $emcc "$@" diff --git a/tool/mkamalzip.tcl b/tool/mkamalzip.tcl new file mode 100644 index 0000000000..92feb4122e --- /dev/null +++ b/tool/mkamalzip.tcl @@ -0,0 +1,23 @@ +#!/usr/bin/tclsh +# +# Build a ZIP archive for the amalgamation source code found in the current +# directory. +# +set VERSION-file [file dirname [file dirname [file normalize $argv0]]]/VERSION +set fd [open ${VERSION-file} rb] +set vers [read $fd] +close $fd +scan $vers %d.%d.%d major minor patch +set numvers [format {3%02d%02d00} $minor $patch] +set dir sqlite-amalgamation-$numvers +file delete -force $dir +file mkdir $dir +set filelist {sqlite3.c sqlite3.h shell.c sqlite3ext.h} +foreach f $filelist { + file copy $f $dir/$f +} +set cmd "zip -r $dir.zip $dir" +puts $cmd +file delete -force $dir.zip +exec {*}$cmd +file delete -force $dir diff --git a/tool/mkautoconfamal.sh b/tool/mkautoconfamal.sh index 35dbfb41e0..c26ca47bf1 100644 --- a/tool/mkautoconfamal.sh +++ b/tool/mkautoconfamal.sh @@ -13,7 +13,7 @@ # -# Bail out of the script if any command returns a non-zero exit +# Bail out of the script if any command returns a non-zero exit # status. Or if the script tries to use an unset variable. These # may fail for old /bin/sh interpreters. # @@ -22,8 +22,8 @@ set -u TMPSPACE=./mkpkg_tmp_dir VERSION=`cat $TOP/VERSION` -HASH=`sed 's/^\(..........\).*/\1/' $TOP/manifest.uuid` -DATETIME=`grep '^D' $TOP/manifest | sed -e 's/[^0-9]//g' -e 's/\(............\).*/\1/'` +HASH=`cut -c1-10 $TOP/manifest.uuid` +DATETIME=`grep '^D' $TOP/manifest | tr -c -d '[0-9]' | cut -c1-12` # Verify that the version number in the TEA autoconf file is correct. # Fail with an error if not. @@ -34,12 +34,12 @@ else echo "TEA version number mismatch. Should be $VERSION"; exit 1 fi # If this script is given an argument of --snapshot, then generate a -# snapshot tarball named for the current checkout SHA1 hash, rather than +# snapshot tarball named for the current checkout SHA hash, rather than # the version number. # if test "$#" -ge 1 -a x$1 != x--snapshot then - # Set global variable $ARTIFACT to the "3xxyyzz" string incorporated + # Set global variable $ARTIFACT to the "3xxyyzz" string incorporated # into artifact filenames. And $VERSION2 to the "3.x.y[.z]" form. xx=`echo $VERSION|sed 's/3\.\([0-9]*\)\..*/\1/'` yy=`echo $VERSION|sed 's/3\.[^.]*\.\([0-9]*\).*/\1/'` @@ -54,6 +54,8 @@ fi rm -rf $TMPSPACE cp -R $TOP/autoconf $TMPSPACE +cp -R $TOP/autosetup $TMPSPACE +cp -p $TOP/configure $TMPSPACE cp sqlite3.c $TMPSPACE cp sqlite3.h $TMPSPACE cp sqlite3ext.h $TMPSPACE @@ -63,28 +65,33 @@ cp $TOP/sqlite3.pc.in $TMPSPACE cp shell.c $TMPSPACE cp $TOP/src/sqlite3.rc $TMPSPACE cp $TOP/tool/Replace.cs $TMPSPACE - -cat $TMPSPACE/configure.ac | -sed "s/--SQLITE-VERSION--/$VERSION/" > $TMPSPACE/tmp -mv $TMPSPACE/tmp $TMPSPACE/configure.ac +cp $TOP/VERSION $TMPSPACE +cp $TOP/main.mk $TMPSPACE cd $TMPSPACE -autoreconf -i -#libtoolize -#aclocal -#autoconf -#automake --add-missing + +# Clean up emacs-generated backup files from the target +rm -f ./autosetup/*~ +rm -f ./*~ + +#if true; then + # Clean up *~ files (emacs-generated backups). + # This bit is only for use during development of + # the autoconf bundle. +# find . -name '*~' -exec rm \{} \; +#fi mkdir -p tea/generic -echo "#ifdef USE_SYSTEM_SQLITE" > tea/generic/tclsqlite3.c -echo "# include " >> tea/generic/tclsqlite3.c -echo "#else" >> tea/generic/tclsqlite3.c -echo "#include \"sqlite3.c\"" >> tea/generic/tclsqlite3.c -echo "#endif" >> tea/generic/tclsqlite3.c +cat < tea/generic/tclsqlite3.c +#ifdef USE_SYSTEM_SQLITE +# include +#else +# include "sqlite3.c" +#endif +EOF cat $TOP/src/tclsqlite.c >> tea/generic/tclsqlite3.c -cat tea/configure.ac | - sed "s/AC_INIT(\[sqlite\], .*)/AC_INIT([sqlite], [$VERSION])/" > tmp +sed "s/AC_INIT(\[sqlite\], .*)/AC_INIT([sqlite], [$VERSION])/" tea/configure.ac > tmp mv tmp tea/configure.ac cd tea @@ -93,9 +100,9 @@ rm -rf autom4te.cache cd ../ ./configure && make dist -tar -xzf sqlite-$VERSION.tar.gz +tar xzf sqlite-$VERSION.tar.gz mv sqlite-$VERSION $TARBALLNAME -tar -czf $TARBALLNAME.tar.gz $TARBALLNAME +tar czf $TARBALLNAME.tar.gz $TARBALLNAME mv $TARBALLNAME.tar.gz .. cd .. ls -l $TARBALLNAME.tar.gz diff --git a/tool/mkshellc.tcl b/tool/mkshellc.tcl index af9804e4fa..85e14f8498 100644 --- a/tool/mkshellc.tcl +++ b/tool/mkshellc.tcl @@ -12,6 +12,9 @@ set topdir [file dir [file dir [file normal $argv0]]] set out stdout fconfigure stdout -translation binary +if {[lindex $argv 0]!=""} { + set out [open [lindex $argv 0] wb] +} puts $out {/* DO NOT EDIT! ** This file is automatically generated by the script in the canonical ** SQLite source tree at tool/mkshellc.tcl. That script combines source diff --git a/tool/mksqlite3c-noext.tcl b/tool/mksqlite3c-noext.tcl index 8452072564..1148b1c0d5 100644 --- a/tool/mksqlite3c-noext.tcl +++ b/tool/mksqlite3c-noext.tcl @@ -57,7 +57,7 @@ close $in # set out [open sqlite3.c w] # Force the output to use unix line endings, even on Windows. -fconfigure $out -translation lf +fconfigure $out -translation binary set today [clock format [clock seconds] -format "%Y-%m-%d %H:%M:%S UTC" -gmt 1] puts $out [subst \ {/****************************************************************************** diff --git a/tool/mksqlite3c.tcl b/tool/mksqlite3c.tcl index 1b3958f460..1d0f892363 100644 --- a/tool/mksqlite3c.tcl +++ b/tool/mksqlite3c.tcl @@ -88,7 +88,7 @@ set fname sqlite3.c if {$enable_recover} { set fname sqlite3r.c } set out [open $fname wb] # Force the output to use unix line endings, even on Windows. -fconfigure $out -translation lf +fconfigure $out -translation binary set today [clock format [clock seconds] -format "%Y-%m-%d %H:%M:%S UTC" -gmt 1] puts $out [subst \ {/****************************************************************************** @@ -130,7 +130,7 @@ if {[file executable $vsrcprog] && [file readable $srcroot/manifest]} { } else { puts $out " with changes in files:\n**" foreach f [lrange $res 1 end] { - puts $out "** $f" + puts $out "** [string trim $f]" } } } else { diff --git a/tool/mksqlite3h.tcl b/tool/mksqlite3h.tcl index c242005a07..b1d5ecdcd3 100644 --- a/tool/mksqlite3h.tcl +++ b/tool/mksqlite3h.tcl @@ -24,18 +24,36 @@ # 6) Adds the SQLITE_CALLBACK calling convention macro in front of all # callback declarations. # -# This script outputs to stdout. +# This script outputs to stdout unless the -o FILENAME option is used. # # Example usage: # -# tclsh mksqlite3h.tcl ../sqlite >sqlite3.h +# tclsh mksqlite3h.tcl ../sqlite [OPTIONS] +# ^^^^^^^^^ +# Root of source tree +# +# Where options are: +# +# --enable-recover Include the sqlite3recover extension +# -o FILENAME Write results to FILENAME instead of stdout +# --useapicall SQLITE_APICALL instead of SQLITE_CDECL # +# Default output stream +set out stdout # Get the source tree root directory from the command-line # set TOP [lindex $argv 0] +# If the -o FILENAME option is present, use FILENAME for output. +# +set x [lsearch $argv -o] +if {$x>0} { + incr x + set out [open [lindex $argv $x] wb] +} + # Enable use of SQLITE_APICALL macros at the right points? # set useapicall 0 @@ -44,6 +62,7 @@ set useapicall 0 # set enable_recover 0 +# Process command-line arguments if {[lsearch -regexp [lrange $argv 1 end] {^-+useapicall}] != -1} { set useapicall 1 } @@ -62,7 +81,7 @@ set nVersion [eval format "%d%03d%03d" [split $zVersion .]] # set PWD [pwd] cd $TOP -set tmpfile tmp-[clock millisec]-[expr {int(rand()*100000000000)}].txt +set tmpfile $PWD/tmp-[clock millisec]-[expr {int(rand()*100000000000)}].txt exec $PWD/mksourceid manifest > $tmpfile set fd [open $tmpfile rb] set zSourceId [string trim [read $fd]] @@ -88,7 +107,7 @@ set declpattern5 \ {^ *([a-zA-Z][a-zA-Z_0-9 ]+ \**)(sqlite3rebaser_[_a-zA-Z0-9]+)(\(.*)$} # Force the output to use unix line endings, even on Windows. -fconfigure stdout -translation lf +fconfigure stdout -translation binary set filelist [subst { $TOP/src/sqlite.h.in @@ -118,7 +137,7 @@ set cdecllist { foreach file $filelist { set in [open $file rb] if {![regexp {sqlite\.h\.in} $file]} { - puts "/******** Begin file [file tail $file] *********/" + puts $out "/******** Begin file [file tail $file] *********/" } while {![eof $in]} { @@ -161,11 +180,11 @@ foreach file $filelist { "(SQLITE_SYSAPI *sqlite3_syscall_ptr)"] $line] regsub {\(\*} $line {(SQLITE_CALLBACK *} line } - puts $line + puts $out $line } close $in if {![regexp {sqlite\.h\.in} $file]} { - puts "/******** End of [file tail $file] *********/" + puts $out "/******** End of [file tail $file] *********/" } } -puts "#endif /* SQLITE3_H */" +puts $out "#endif /* SQLITE3_H */" diff --git a/tool/mksrczip.tcl b/tool/mksrczip.tcl new file mode 100644 index 0000000000..4431c3d666 --- /dev/null +++ b/tool/mksrczip.tcl @@ -0,0 +1,14 @@ +#!/usr/bin/tclsh +# +# Build a ZIP archive for the complete, unedited source code that +# corresponds to the current check-out. +# +set VERSION-file [file dirname [file dirname [file normalize $argv0]]]/VERSION +set fd [open ${VERSION-file} rb] +set vers [read $fd] +close $fd +scan $vers %d.%d.%d major minor patch +set numvers [format {3%02d%02d00} $minor $patch] +set cmd "fossil zip current sqlite-src-$numvers.zip --name sqlite-src-$numvers" +puts $cmd +exec {*}$cmd diff --git a/tool/omittest.tcl b/tool/omittest.tcl index e9033c0bdd..0452a4c6f6 100644 --- a/tool/omittest.tcl +++ b/tool/omittest.tcl @@ -200,8 +200,8 @@ foreach sym $CompileOptionsToTest { } else { set opts OPT_FEATURE_FLAGS=-D$sym } - puts "make tidy sqlite3.lo $opts" - if {[catch {exec make tidy sqlite3.lo $opts >& $logfile}]} { + puts "make tidy sqlite3.o $opts" + if {[catch {exec make tidy sqlite3.o $opts >& $logfile}]} { puts "BUILD FAILED: see $logfile for details" if {[info exists FailIsOk($sym)]} { set Failure($sym) 1 diff --git a/tool/split-sqlite3c.tcl b/tool/split-sqlite3c.tcl index 0308431dab..de4db55a1b 100644 --- a/tool/split-sqlite3c.tcl +++ b/tool/split-sqlite3c.tcl @@ -15,7 +15,7 @@ set END {^/\*+ End of %s \*+/} set in [open sqlite3.c] set out1 [open sqlite3-all.c w] -fconfigure $out1 -translation lf +fconfigure $out1 -translation binary # Copy the header from sqlite3.c into sqlite3-all.c # diff --git a/tool/stripccomments.c b/tool/stripccomments.c index 53933c0138..1bdb5c6b82 100644 --- a/tool/stripccomments.c +++ b/tool/stripccomments.c @@ -111,7 +111,24 @@ void do_it_all(void){ } else if(slash == ch){ /* MARKER(("state 0 ==> 1 @ %d:%d\n", line, col)); */ - state = S_SLASH1; + if( '\\'==prev ){ + /** + JS regexes may contain slash-asterisks, as happened at: + + https://github.com/emscripten-core/emscripten/issues/23412 + + Such regexes will always necessarily be preceeded by a + backslash, though. + + It is hypothetically possible for a legitimate comment + slash-asterisk to appear immediately before a + backslash, but that seems like an even rarer corner + case than the JS regex case. + */ + fputc(ch, out); + }else{ + state = S_SLASH1; + } break; } fputc(ch, out);