Compare commits
156 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 449b34571e | |||
| 8c8443a38a | |||
| 44a03671c4 | |||
| 0a5508aeb4 | |||
| 4fe1ac8fe1 | |||
| 24d290e7b0 | |||
| 8504d37b99 | |||
| 4aacd1ef8e | |||
| 0840460689 | |||
| c701d17366 | |||
| 8bc112e3e4 | |||
| 27408ab9c9 | |||
| d9eae6b75a | |||
| 45bc9b9f97 | |||
| e33ea17d24 | |||
| a12e92d2f7 | |||
| 478dfc7e74 | |||
| 1b62720fa8 | |||
| 1e3a862b79 | |||
| bd05edd98f | |||
| a24a397b8b | |||
| 0e6e05d4d5 | |||
| c525e6e817 | |||
| 1ff6f19d8b | |||
| bfb4993364 | |||
| debc8f7bb7 | |||
| aa54d7a0ca | |||
| eb27359e5e | |||
| 33f3273705 | |||
| 216676664d | |||
| 6245e5a46b | |||
| caf0473652 | |||
| 2427ce16d9 | |||
| 46bfcc1a21 | |||
| 5e71497404 | |||
| 526399b0fd | |||
| e1910ed1d2 | |||
| d82c6a2cf7 | |||
| 3d21dcc924 | |||
| ba56f7020d | |||
| 99f1aa03fb | |||
| 280559b446 | |||
| e2e81e6983 | |||
| b8c81790b2 | |||
| e24f20a4f5 | |||
| f0991c416b | |||
| 311d73efc2 | |||
| c52e9d97d4 | |||
| fbf1c0526e | |||
| e77e589a35 | |||
| 981022b7ff | |||
| a67c71224f | |||
| 66cd200ede | |||
| 513fff88c4 | |||
| fe182f74b2 | |||
| b1560be826 | |||
| 28789029ee | |||
| f453e8d84e | |||
| 4c1c400d64 | |||
| 87c807c6dd | |||
| b5aa9593ac | |||
| 5806a922ba | |||
| 427d2cd57d | |||
| db37e6097c | |||
| 6fcb7f22a1 | |||
| 2f7f948fa7 | |||
| 0cdde5b44f | |||
| a0c6de56ba | |||
| b68d63158a | |||
| 59de42763f | |||
| 37794b4ad0 | |||
| 397b82cf0f | |||
| 92d1bec584 | |||
| a5c484b432 | |||
| 64159096b8 | |||
| ab9c91ae82 | |||
| bcf25e7129 | |||
| 2be0a700ff | |||
| 3a26f72e20 | |||
| d961d49971 | |||
| 9a9140ba59 | |||
| 12bd8ea09a | |||
| 1e9baaf743 | |||
| df8aa3745a | |||
| 110055cd6c | |||
| 79a746b1f2 | |||
| 9ebf9d358b | |||
| e68aa0e44b | |||
| 6e1c2f0483 | |||
| 1ea6a53762 | |||
| 86d9c2d9f3 | |||
| d3a13f7d25 | |||
| 6ed5aa4b9b | |||
| 67f708298f | |||
| 792d1d1b6d | |||
| 6a5e74cbf0 | |||
| 23e59b34e9 | |||
| a09a4fbac9 | |||
| 6a23ff5a07 | |||
| 5706b316c3 | |||
| 90ba0d4995 | |||
| f091f423ad | |||
| 4c323ba4be | |||
| 368c549044 | |||
| b1929b7117 | |||
| 6facd9566d | |||
| 26370358b6 | |||
| a29b138692 | |||
| 1d151e6d25 | |||
| df724c893a | |||
| 140748f949 | |||
| 2bd9f69d40 | |||
| 333b07b18d | |||
| 9269b212bf | |||
| 69ce758efa | |||
| 342ef63e63 | |||
| cfee69fbe4 | |||
| eb9a145711 | |||
| d84bbac8be | |||
| a3fcd7c79e | |||
| 8658a8df59 | |||
| 7590bfd7fc | |||
| d4c224b833 | |||
| f18bf8997b | |||
| c0190101d1 | |||
| 977b0f8813 | |||
| 0d3e5ca28e | |||
| 51e3f855b8 | |||
| 5248693924 | |||
| 420233e375 | |||
| 1ff4233f90 | |||
| 10206572b6 | |||
| b504aab848 | |||
| d930d7ef14 | |||
| ded1959120 | |||
| dd16539e71 | |||
| 88ed38ca6d | |||
| f42ceb9075 | |||
| 7ac10ec8b5 | |||
| 8ae57fab64 | |||
| b65326f51a | |||
| 0148820fd7 | |||
| d2b230a488 | |||
| f429845a60 | |||
| b163f66de0 | |||
| a3ee3860a2 | |||
| c96e47c80e | |||
| 620a00ee32 | |||
| 62b954d545 | |||
| 0d92e66dfc | |||
| ddc62664bf | |||
| b4f41b801a | |||
| df77e56cde | |||
| dc9a262319 | |||
| b111f1cb2a | |||
| 5525ac1049 |
+10
-1
@@ -213,6 +213,13 @@ TCL_CONFIG_SH = @TCL_CONFIG_SH@
|
||||
#TCL_STUB_LIB_SPEC = @TCL_STUB_LIB_SPEC@
|
||||
#TCL_EXEC_PREFIX = @TCL_EXEC_PREFIX@
|
||||
#TCL_VERSION = @TCL_VERSION@
|
||||
TCL_MAJOR_VERSION = @TCL_MAJOR_VERSION@
|
||||
# ^^^ main.mk optionally uses this for determining the Tcl extension's
|
||||
# DLL name.
|
||||
TCL_EXT_DLL_BASENAME = @TCL_EXT_DLL_BASENAME@
|
||||
# ^^^ base name of the Tcl extension DLL. It varies by platform and
|
||||
# Tcl version.
|
||||
|
||||
#
|
||||
# $(TCLLIBDIR) = where to install the tcl plugin. If this is empty, it
|
||||
# is calculated at make-time by the targets which need it but we
|
||||
@@ -257,7 +264,9 @@ AS_AUTO_DEF = $(TOP)/auto.def
|
||||
# invoked with to produce this makefile.
|
||||
#
|
||||
AS_AUTORECONFIG = @SQLITE_AUTORECONFIG@
|
||||
|
||||
.PHONY: reconfigure
|
||||
reconfigure:
|
||||
$(AS_AUTORECONFIG)
|
||||
USE_AMALGAMATION ?= @USE_AMALGAMATION@
|
||||
LINK_TOOLS_DYNAMICALLY ?= @LINK_TOOLS_DYNAMICALLY@
|
||||
AMALGAMATION_GEN_FLAGS ?= --linemacros=@AMALGAMATION_LINE_MACROS@
|
||||
|
||||
+11
-6
@@ -1623,7 +1623,6 @@ TESTSRC = \
|
||||
$(TOP)\src\test_thread.c \
|
||||
$(TOP)\src\test_vdbecov.c \
|
||||
$(TOP)\src\test_vfs.c \
|
||||
$(TOP)\src\test_windirent.c \
|
||||
$(TOP)\src\test_window.c \
|
||||
$(TOP)\src\test_wsd.c \
|
||||
$(TOP)\ext\fts3\fts3_term.c \
|
||||
@@ -2395,12 +2394,11 @@ SHELL_DEP = \
|
||||
$(TOP)\ext\misc\sqlite3_stdio.h \
|
||||
$(TOP)\ext\misc\uint.c \
|
||||
$(TOP)\ext\misc\vfstrace.c \
|
||||
$(TOP)\ext\misc\windirent.h \
|
||||
$(TOP)\ext\misc\zipfile.c \
|
||||
$(TOP)\ext\recover\dbdata.c \
|
||||
$(TOP)\ext\recover\sqlite3recover.c \
|
||||
$(TOP)\ext\recover\sqlite3recover.h \
|
||||
$(TOP)\src\test_windirent.c \
|
||||
$(TOP)\src\test_windirent.h
|
||||
$(TOP)\ext\recover\sqlite3recover.h
|
||||
|
||||
# If use of zlib is enabled, add the "zipfile.c" source file.
|
||||
#
|
||||
@@ -2585,7 +2583,7 @@ sqlite_tcl.h:
|
||||
|
||||
testfixture.exe: $(TESTFIXTURE_SRC) $(TESTFIXTURE_DEP) $(SQLITE3H) $(LIBRESOBJS) $(HDR) $(SQLITE_TCL_DEP)
|
||||
$(LTLINK) -DSQLITE_NO_SYNC=1 $(TESTFIXTURE_FLAGS) \
|
||||
-DBUILD_sqlite -I$(TCLINCDIR) \
|
||||
-DBUILD_sqlite -I$(TCLINCDIR) -I$(TOP)\ext\misc \
|
||||
$(TESTFIXTURE_SRC) \
|
||||
/link $(LDFLAGS) $(LTLINKOPTS) $(TCLLIBPATHS) $(LTLIBPATHS) $(LIBRESOBJS) $(TCLLIBS) $(LTLIBS) $(TLIBS)
|
||||
|
||||
@@ -2674,7 +2672,14 @@ srctree-check: $(TOP)\tool\srctree-check.tcl
|
||||
|
||||
# Testing for a release
|
||||
#
|
||||
releasetest:
|
||||
releasetest: verify-source
|
||||
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl release
|
||||
|
||||
# xdevtest is like releasetest, except that it skips the
|
||||
# dependency on verify-source so that xdevtest can be run from
|
||||
# a modified source tree.
|
||||
#
|
||||
xdevtest:
|
||||
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl release
|
||||
|
||||
|
||||
|
||||
+51
-28
@@ -146,7 +146,7 @@ LDFLAGS.shlib = @SH_LDFLAGS@
|
||||
# sources passed to [teaish-src-add], but may also be appended to by
|
||||
# teaish.make.
|
||||
#
|
||||
tx.src =@TEAISH_EXT_SRC@
|
||||
tx.src = @TEAISH_EXT_SRC@
|
||||
|
||||
#
|
||||
# tx.CFLAGS is typically set by teaish.make, whereas TEAISH_CFLAGS
|
||||
@@ -167,6 +167,11 @@ tx.LDFLAGS =
|
||||
#
|
||||
tx.dist.files = @TEAISH_DIST_FILES@
|
||||
|
||||
#
|
||||
# The base name for a distribution tar/zip file.
|
||||
#
|
||||
tx.dist.basename = $(tx.name.dist)-$(tx.version)
|
||||
|
||||
# List of deps which may trigger an auto-reconfigure.
|
||||
#
|
||||
teaish__autogen.deps = \
|
||||
@@ -199,9 +204,14 @@ $(teaish.makefile): $(teaish__auto.def) $(teaish.makefile.in) \
|
||||
@AUTODEPS@
|
||||
|
||||
@if TEAISH_TESTER_TCL_IN
|
||||
@TEAISH_TESTER_TCL_IN@:
|
||||
@TEAISH_TESTER_TCL@: @TEAISH_TESTER_TCL_IN@
|
||||
config.log: @TEAISH_TESTER_TCL@
|
||||
@TEAISH_TESTER_TCL_IN@: $(teaish__autogen.deps)
|
||||
config.log: @TEAISH_TESTER_TCL_IN@
|
||||
@TEAISH_TESTER_TCL@: @TEAISH_TESTER_TCL_IN@
|
||||
@endif
|
||||
@if TEAISH_TEST_TCL_IN
|
||||
@TEAISH_TEST_TCL_IN@: $(teaish__autogen.deps)
|
||||
config.log: @TEAISH_TEST_TCL_IN@
|
||||
@TEAISH_TEST_TCL@: @TEAISH_TEST_TCL_IN@
|
||||
@endif
|
||||
|
||||
#
|
||||
@@ -217,7 +227,7 @@ CC.tcl = \
|
||||
#
|
||||
CC.dll = \
|
||||
$(CC.tcl) $(tx.src) $(LDFLAGS.shlib) \
|
||||
$(LDFLAGS.configure) $(LDFLAGS) $(tx.LDFLAGS) $(TCL_STUB_LIB_SPEC)
|
||||
$(tx.LDFLAGS) $(LDFLAGS.configure) $(LDFLAGS) $(TCL_STUB_LIB_SPEC)
|
||||
|
||||
@if TEAISH_ENABLE_DLL
|
||||
#
|
||||
@@ -248,16 +258,25 @@ test-extension: # this name is reserved for use by teaish.make[.in]
|
||||
test-prepre: $(tx.dll)
|
||||
@endif
|
||||
@if TEAISH_TESTER_TCL
|
||||
test-core.args = @TEAISH_TESTER_TCL@
|
||||
teaish.tester.tcl = @TEAISH_TESTER_TCL@
|
||||
test-core.args = $(teaish.tester.tcl)
|
||||
@if TEAISH_ENABLE_DLL
|
||||
test-core.args += '$(tx.dll)' '$(tx.loadPrefix)'
|
||||
@else
|
||||
test-core.args += '' ''
|
||||
@endif
|
||||
test-core.args += @TEAISH_TESTUTIL_TCL@
|
||||
# Clients may pass additional args via test.args=...
|
||||
# and ::argv will be rewritten before the test script loads, to
|
||||
# remove $(test-core.args)
|
||||
test.args ?=
|
||||
test-core: test-pre
|
||||
$(TCLSH) $(test-core.args)
|
||||
test-prepre: @TEAISH_TESTER_TCL@
|
||||
$(TCLSH) $(test-core.args) $(test.args)
|
||||
test-gdb: $(teaish.tester.tcl)
|
||||
gdb --args $(TCLSH) $(test-core.args) $(test.args)
|
||||
test-vg.flags ?= --leak-check=full -v --show-reachable=yes --track-origins=yes
|
||||
test-vg: $(teaish.tester.tcl)
|
||||
valgrind $(test-vg.flags) $(TCLSH) $(test-core.args) $(test.args)
|
||||
@else # !TEAISH_TESTER_TCL
|
||||
test-prepre:
|
||||
@endif # TEAISH_TESTER_TCL
|
||||
@@ -288,7 +307,7 @@ distclean-core: distclean-pre
|
||||
@endif
|
||||
@endif
|
||||
@if TEAISH_TESTER_TCL_IN
|
||||
rm -f @TEAISH_TESTER_TCL@
|
||||
rm -f $(teaish.tester.tcl)
|
||||
@endif
|
||||
@if TEAISH_PKGINDEX_TCL_IN
|
||||
rm -f @TEAISH_PKGINDEX_TCL@
|
||||
@@ -355,10 +374,15 @@ install-core: install-pre
|
||||
@endif
|
||||
install-test: install-core
|
||||
@echo "Post-install test of [package require $(tx.name.pkg) $(tx.version)]..."; \
|
||||
set xtra=""; \
|
||||
if [ x != "x$(DESTDIR)" ]; then \
|
||||
xtra='set ::auto_path [linsert $$::auto_path 0 [file normalize $(DESTDIR)$(TCLLIBDIR)/..]];'; \
|
||||
fi; \
|
||||
if echo \
|
||||
'set c 0; ' \
|
||||
'set c 0; ' $$xtra \
|
||||
'@TEAISH_POSTINST_PREREQUIRE@' \
|
||||
'if {[catch {package require $(tx.name.pkg) $(tx.version)}]} {incr c};' \
|
||||
'if {[catch {package require $(tx.name.pkg) $(tx.version)} xc]} {incr c};' \
|
||||
'if {$$c && "" ne $$xc} {puts $$xc; puts "auto_path=$$::auto_path"};' \
|
||||
'exit $$c' \
|
||||
| $(TCLSH) ; then \
|
||||
echo "passed"; \
|
||||
@@ -406,7 +430,7 @@ config.log: $(teaish.makefile.in)
|
||||
# recognized when running in --teaish-install mode, causing
|
||||
# the sub-configure to fail.
|
||||
dist.flags = --with-tclsh=$(TCLSH)
|
||||
dist.reconfig = $(teaish.dir)/configure $(dist.flags)
|
||||
dist.reconfig = $(teaish.dir)/configure $(tx.dist.reconfig-flags) $(dist.flags)
|
||||
|
||||
# Temp dir for dist.zip. Must be different than dist.tgz or else
|
||||
# parallel builds may hose the dist.
|
||||
@@ -414,24 +438,23 @@ teaish__dist.tmp.zip = teaish__dist_zip
|
||||
#
|
||||
# Make a distribution zip file...
|
||||
#
|
||||
dist.basename = $(tx.name.dist)-$(tx.version)
|
||||
dist.zip = $(dist.basename).zip
|
||||
dist.zip = $(tx.dist.basename).zip
|
||||
.PHONY: dist.zip dist.zip-core dist.zip-post
|
||||
#dist.zip-pre:
|
||||
# We apparently can't add a pre-hook here, else "make dist" rebuilds
|
||||
# the archive each time it's run.
|
||||
$(dist.zip): $(tx.dist.files)
|
||||
@rm -fr $(teaish__dist.tmp.zip)
|
||||
@mkdir -p $(teaish__dist.tmp.zip)/$(dist.basename)
|
||||
@mkdir -p $(teaish__dist.tmp.zip)/$(tx.dist.basename)
|
||||
@tar cf $(teaish__dist.tmp.zip)/tmp.tar $(tx.dist.files)
|
||||
@tar xf $(teaish__dist.tmp.zip)/tmp.tar -C $(teaish__dist.tmp.zip)/$(dist.basename)
|
||||
@tar xf $(teaish__dist.tmp.zip)/tmp.tar -C $(teaish__dist.tmp.zip)/$(tx.dist.basename)
|
||||
@if TEAISH_DIST_FULL
|
||||
@$(dist.reconfig) \
|
||||
--teaish-install=$(teaish__dist.tmp.zip)/$(dist.basename) \
|
||||
--t-e-d=$(teaish__dist.tmp.zip)/$(dist.basename) >/dev/null
|
||||
--teaish-install=$(teaish__dist.tmp.zip)/$(tx.dist.basename) \
|
||||
--t-e-d=$(teaish__dist.tmp.zip)/$(tx.dist.basename) >/dev/null
|
||||
@endif
|
||||
@rm -f $(dist.basename)/tmp.tar $(dist.zip)
|
||||
@cd $(teaish__dist.tmp.zip) && zip -q -r ../$(dist.zip) $(dist.basename)
|
||||
@rm -f $(tx.dist.basename)/tmp.tar $(dist.zip)
|
||||
@cd $(teaish__dist.tmp.zip) && zip -q -r ../$(dist.zip) $(tx.dist.basename)
|
||||
@rm -fr $(teaish__dist.tmp.zip)
|
||||
@ls -la $(dist.zip)
|
||||
dist.zip-core: $(dist.zip)
|
||||
@@ -447,23 +470,23 @@ undist: undist-zip
|
||||
# Make a distribution tarball...
|
||||
#
|
||||
teaish__dist.tmp.tgz = teaish__dist_tgz
|
||||
dist.tgz = $(dist.basename).tar.gz
|
||||
dist.tgz = $(tx.dist.basename).tar.gz
|
||||
.PHONY: dist.tgz dist.tgz-core dist.tgz-post
|
||||
# dist.tgz-pre:
|
||||
# see notes in dist.zip
|
||||
$(dist.tgz): $(tx.dist.files)
|
||||
@rm -fr $(teaish__dist.tmp.tgz)
|
||||
@mkdir -p $(teaish__dist.tmp.tgz)/$(dist.basename)
|
||||
@mkdir -p $(teaish__dist.tmp.tgz)/$(tx.dist.basename)
|
||||
@tar cf $(teaish__dist.tmp.tgz)/tmp.tar $(tx.dist.files)
|
||||
@tar xf $(teaish__dist.tmp.tgz)/tmp.tar -C $(teaish__dist.tmp.tgz)/$(dist.basename)
|
||||
@tar xf $(teaish__dist.tmp.tgz)/tmp.tar -C $(teaish__dist.tmp.tgz)/$(tx.dist.basename)
|
||||
@if TEAISH_DIST_FULL
|
||||
@rm -f $(teaish__dist.tmp.tgz)/$(dist.basename)/pkgIndex.tcl.in; # kludge
|
||||
@rm -f $(teaish__dist.tmp.tgz)/$(tx.dist.basename)/pkgIndex.tcl.in; # kludge
|
||||
@$(dist.reconfig) \
|
||||
--teaish-install=$(teaish__dist.tmp.tgz)/$(dist.basename) \
|
||||
--t-e-d=$(teaish__dist.tmp.zip)/$(dist.basename) >/dev/null
|
||||
--teaish-install=$(teaish__dist.tmp.tgz)/$(tx.dist.basename) \
|
||||
--t-e-d=$(teaish__dist.tmp.zip)/$(tx.dist.basename) >/dev/null
|
||||
@endif
|
||||
@rm -f $(dist.basename)/tmp.tar $(dist.tgz)
|
||||
@cd $(teaish__dist.tmp.tgz) && tar czf ../$(dist.tgz) $(dist.basename)
|
||||
@rm -f $(tx.dist.basename)/tmp.tar $(dist.tgz)
|
||||
@cd $(teaish__dist.tmp.tgz) && tar czf ../$(dist.tgz) $(tx.dist.basename)
|
||||
@rm -fr $(teaish__dist.tmp.tgz)
|
||||
@ls -la $(dist.tgz)
|
||||
dist.tgz-core: $(dist.tgz)
|
||||
|
||||
+4
-14
@@ -83,22 +83,12 @@ script and then run make. For example:
|
||||
|
||||
$ cd sqlite-*-tea
|
||||
$ ./configure --with-tcl=/path/to/tcl/install/root
|
||||
$ make
|
||||
$ make test
|
||||
$ make install
|
||||
|
||||
WINDOWS BUILD
|
||||
=============
|
||||
|
||||
The recommended method to build extensions under windows is to use the
|
||||
Msys + Mingw build process. This provides a Unix-style build while
|
||||
generating native Windows binaries. Using the Msys + Mingw build tools
|
||||
means that you can use the same configure script as per the Unix build
|
||||
to create a Makefile. See the tcl/win/README file for the URL of
|
||||
the Msys + Mingw download.
|
||||
If you have VC++ then you may wish to use the files in the win
|
||||
subdirectory and build the extension using just VC++. These files have
|
||||
been designed to be as generic as possible but will require some
|
||||
additional maintenance by the project developer to synchronise with
|
||||
the TEA configure.in and Makefile.in files. Instructions for using the
|
||||
VC++ makefile are written in the first part of the Makefile.vc
|
||||
file.
|
||||
On Windows this build is known to work on Cygwin and some Msys2
|
||||
environments. We do not currently support Microsoft makefiles for
|
||||
native Windows builds.
|
||||
|
||||
@@ -21,7 +21,8 @@ if {[llength [lindex $::argv 0]] > 0} {
|
||||
# ----^^^^^^^ needed on Haiku when argv 0 is just a filename, else
|
||||
# load cannot find the file.
|
||||
}
|
||||
source -encoding utf-8 [lindex $::argv 2]; # teaish/tester.tcl
|
||||
set ::argv [lassign $argv - -]
|
||||
source -encoding utf-8 [lindex $::argv 0]; # teaish/tester.tcl
|
||||
@if TEAISH_PKGINIT_TCL
|
||||
apply {{file} {
|
||||
set dir [file dirname $::argv0]
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
.TH sqlite3 n 4.1 "Tcl-Extensions"
|
||||
.HS sqlite3 tcl
|
||||
.BS
|
||||
.SH NAME
|
||||
sqlite3 \- an interface to the SQLite3 database engine
|
||||
.SH SYNOPSIS
|
||||
\fBsqlite3\fI command_name ?filename?\fR
|
||||
.br
|
||||
.SH DESCRIPTION
|
||||
SQLite3 is a self-contains, zero-configuration, transactional SQL database
|
||||
engine. This extension provides an easy to use interface for accessing
|
||||
SQLite database files from Tcl.
|
||||
.PP
|
||||
For full documentation see \fIhttps://sqlite.org/\fR and
|
||||
in particular \fIhttps://sqlite.org/tclsqlite.html\fR.
|
||||
@@ -64,12 +64,18 @@ apply {{dir} {
|
||||
-name.pkg sqlite3
|
||||
-version $version
|
||||
-name.dist $distname
|
||||
-vsatisfies 8.6-
|
||||
-libDir sqlite$version
|
||||
-pragmas $pragmas
|
||||
-src generic/tclsqlite3.c
|
||||
}
|
||||
# We should also have:
|
||||
# -vsatisfies 8.6-
|
||||
# But at least one platform is failing this vsatisfies check
|
||||
# for no apparent reason:
|
||||
# https://sqlite.org/forum/forumpost/fde857fb8101a4be
|
||||
}} [teaish-get -dir]
|
||||
|
||||
|
||||
#
|
||||
# Must return either an empty string or a list in the form accepted by
|
||||
# autosetup's [options] function.
|
||||
@@ -119,8 +125,6 @@ proc teaish-options {} {
|
||||
proc teaish-configure {} {
|
||||
use teaish/feature
|
||||
|
||||
teaish-src-add -dist -dir generic/tclsqlite3.c
|
||||
|
||||
if {[proj-opt-was-provided override-sqlite-version]} {
|
||||
teaish-pkginfo-set -version [opt-val override-sqlite-version]
|
||||
proj-warn "overriding sqlite version number:" [teaish-pkginfo-get -version]
|
||||
|
||||
+387
-123
@@ -60,10 +60,11 @@
|
||||
# $proj__Config is an internal-use-only array for storing whatever generic
|
||||
# internal stuff we need stored.
|
||||
#
|
||||
array set ::proj__Config {
|
||||
self-tests 1
|
||||
}
|
||||
|
||||
array set ::proj__Config [subst {
|
||||
self-tests [get-env proj.self-tests 0]
|
||||
verbose-assert [get-env proj.assert-verbose 0]
|
||||
isatty [isatty? stdout]
|
||||
}]
|
||||
|
||||
#
|
||||
# List of dot-in files to filter in the final stages of
|
||||
@@ -75,7 +76,6 @@ array set ::proj__Config {
|
||||
# See: proj-dot-ins-append and proj-dot-ins-process
|
||||
#
|
||||
set ::proj__Config(dot-in-files) [list]
|
||||
set ::proj__Config(isatty) [isatty? stdout]
|
||||
|
||||
#
|
||||
# @proj-warn msg
|
||||
@@ -85,28 +85,29 @@ set ::proj__Config(isatty) [isatty? stdout]
|
||||
#
|
||||
proc proj-warn {args} {
|
||||
show-notices
|
||||
puts stderr [join [list "WARNING: \[[proj-scope 1]\]: " {*}$args] " "]
|
||||
puts stderr [join [list "WARNING:" \[ [proj-scope 1] \]: {*}$args] " "]
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Internal impl of [proj-fatal] and [proj-error]. It must be called
|
||||
# using tailcall.
|
||||
proc proj__faterr {failMode argv} {
|
||||
#
|
||||
proc proj__faterr {failMode args} {
|
||||
show-notices
|
||||
set lvl 1
|
||||
while {"-up" eq [lindex $argv 0]} {
|
||||
set argv [lassign $argv -]
|
||||
while {"-up" eq [lindex $args 0]} {
|
||||
set args [lassign $args -]
|
||||
incr lvl
|
||||
}
|
||||
if {$failMode} {
|
||||
puts stderr [join [list "FATAL: \[[proj-scope $lvl]]: " {*}$argv]]
|
||||
puts stderr [join [list "FATAL:" \[ [proj-scope $lvl] \]: {*}$args]]
|
||||
exit 1
|
||||
} else {
|
||||
error [join [list "\[[proj-scope $lvl]]:" {*}$argv]]
|
||||
error [join [list in \[ [proj-scope $lvl] \]: {*}$args]]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# @proj-fatal ?-up...? msg...
|
||||
#
|
||||
@@ -118,7 +119,7 @@ proc proj__faterr {failMode argv} {
|
||||
# additional level.
|
||||
#
|
||||
proc proj-fatal {args} {
|
||||
tailcall proj__faterr 1 $args
|
||||
tailcall proj__faterr 1 {*}$args
|
||||
}
|
||||
|
||||
#
|
||||
@@ -127,10 +128,9 @@ proc proj-fatal {args} {
|
||||
# Works like proj-fatal but uses [error] intead of [exit].
|
||||
#
|
||||
proc proj-error {args} {
|
||||
tailcall proj__faterr 0 $args
|
||||
tailcall proj__faterr 0 {*}$args
|
||||
}
|
||||
|
||||
set ::proj__Config(verbose-assert) [get-env proj-assert-verbose 0]
|
||||
#
|
||||
# @proj-assert script ?message?
|
||||
#
|
||||
@@ -147,7 +147,7 @@ proc proj-assert {script {msg ""}} {
|
||||
if {"" eq $msg} {
|
||||
set msg $script
|
||||
}
|
||||
proj-fatal "Assertion failed in \[[proj-scope 1]\]: $msg"
|
||||
tailcall proj__faterr 1 "Assertion failed:" $msg
|
||||
}
|
||||
}
|
||||
|
||||
@@ -885,7 +885,9 @@ proc proj-looks-like-windows {{key host}} {
|
||||
#
|
||||
proc proj-looks-like-mac {{key host}} {
|
||||
switch -glob -- [get-define $key] {
|
||||
*apple* {
|
||||
*-*-darwin* {
|
||||
# https://sqlite.org/forum/forumpost/7b218c3c9f207646
|
||||
# There's at least one Linux out there which matches *apple*.
|
||||
return 1
|
||||
}
|
||||
default {
|
||||
@@ -927,17 +929,13 @@ proc proj-exe-extension {} {
|
||||
#
|
||||
proc proj-dll-extension {} {
|
||||
set inner {{key} {
|
||||
switch -glob -- [get-define $key] {
|
||||
*apple* {
|
||||
return ".dylib"
|
||||
}
|
||||
*-*-ming* - *-*-cygwin - *-*-msys {
|
||||
return ".dll"
|
||||
}
|
||||
default {
|
||||
return ".so"
|
||||
}
|
||||
if {[proj-looks-like-mac $key]} {
|
||||
return ".dylib"
|
||||
}
|
||||
if {[proj-looks-like-windows $key]} {
|
||||
return ".dll"
|
||||
}
|
||||
return ".so"
|
||||
}}
|
||||
define BUILD_DLLEXT [apply $inner build]
|
||||
define TARGET_DLLEXT [apply $inner host]
|
||||
@@ -1665,7 +1663,7 @@ proc proj-dot-ins-append {fileIn args} {
|
||||
proj-fatal "Too many arguments: $fileIn $args"
|
||||
}
|
||||
}
|
||||
#puts "******* [proj-scope]: adding $fileIn"
|
||||
#puts "******* [proj-scope]: adding [llength $fileIn]-length item: $fileIn"
|
||||
lappend ::proj__Config(dot-in-files) $fileIn
|
||||
}
|
||||
|
||||
@@ -1703,17 +1701,18 @@ proc proj-dot-ins-list {} {
|
||||
# makes proj-dot-ins-append available for re-use.
|
||||
#
|
||||
proc proj-dot-ins-process {args} {
|
||||
proj-parse-simple-flags args flags {
|
||||
proj-parse-flags args flags {
|
||||
-touch "" {return "-touch"}
|
||||
-clear 0 {expr 1}
|
||||
-validate 0 {expr 1}
|
||||
}
|
||||
#puts "args=$args"; parray flags
|
||||
if {[llength $args] > 0} {
|
||||
error "Invalid argument to [proj-scope]: $args"
|
||||
}
|
||||
foreach f $::proj__Config(dot-in-files) {
|
||||
proj-assert {3==[llength $f]} \
|
||||
"Expecting proj-dot-ins-list to be stored in 3-entry lists"
|
||||
"Expecting proj-dot-ins-list to be stored in 3-entry lists. Got: $f"
|
||||
lassign $f fIn fOut fScript
|
||||
#puts "DOING $fIn ==> $fOut"
|
||||
proj-make-from-dot-in {*}$flags(-touch) $fIn $fOut
|
||||
@@ -1753,7 +1752,7 @@ proc proj-validate-no-unresolved-ats {args} {
|
||||
set isMake [string match {*[Mm]ake*} $f]
|
||||
foreach line [proj-file-content-list $f] {
|
||||
if {!$isMake || ![string match "#*" [string trimleft $line]]} {
|
||||
if {[regexp {(@[A-Za-z0-9_]+@)} $line match]} {
|
||||
if {[regexp {(@[A-Za-z0-9_\.]+@)} $line match]} {
|
||||
error "Unresolved reference to $match at line $lnno of $f"
|
||||
}
|
||||
}
|
||||
@@ -1893,7 +1892,7 @@ proc proj-define-amend {args} {
|
||||
#
|
||||
proc proj-define-to-cflag {args} {
|
||||
set rv {}
|
||||
proj-parse-simple-flags args flags {
|
||||
proj-parse-flags args flags {
|
||||
-list 0 {expr 1}
|
||||
-quote 0 {expr 1}
|
||||
-zero-undef 0 {expr 1}
|
||||
@@ -2001,7 +2000,7 @@ proc proj-cache-key {arg {addLevel 0}} {
|
||||
# See proj-cache-key for -key's and -level's semantics, noting that
|
||||
# this function adds one to -level for purposes of that call.
|
||||
proc proj-cache-set {args} {
|
||||
proj-parse-simple-flags args flags {
|
||||
proj-parse-flags args flags {
|
||||
-key => 0
|
||||
-level => 0
|
||||
}
|
||||
@@ -2037,7 +2036,7 @@ proc proj-cache-remove {{key 0} {addLevel 0}} {
|
||||
# See proj-cache-key for $key's and $addLevel's semantics, noting that
|
||||
# this function adds one to $addLevel for purposes of that call.
|
||||
proc proj-cache-check {args} {
|
||||
proj-parse-simple-flags args flags {
|
||||
proj-parse-flags args flags {
|
||||
-key => 0
|
||||
-level => 0
|
||||
}
|
||||
@@ -2070,147 +2069,316 @@ proc proj-coalesce {args} {
|
||||
}
|
||||
|
||||
#
|
||||
# @proj-parse-simple-flags ...
|
||||
# @proj-parse-flags argvListName targetArrayName {prototype}
|
||||
#
|
||||
# A helper to parse flags from proc argument lists.
|
||||
#
|
||||
# Expects a list of arguments to parse, an array name to store any
|
||||
# -flag values to, and a prototype object which declares the flags.
|
||||
# The first argument is the name of a var holding the args to
|
||||
# parse. It will be overwritten, possibly with a smaller list.
|
||||
#
|
||||
# The prototype must be a list in one of the following forms:
|
||||
# The second argument is the name of an array variable to create in
|
||||
# the caller's scope.
|
||||
#
|
||||
# -flag defaultValue {script}
|
||||
# The third argument, $prototype, is a description of how to handle
|
||||
# the flags. Each entry in that list must be in one of the
|
||||
# following forms:
|
||||
#
|
||||
# -flag => defaultValue
|
||||
# -----^--^ (with spaces there!)
|
||||
# -flag defaultValue ?-literal|-call|-apply?
|
||||
# script|number|incr|proc-name|{apply $aLambda}
|
||||
#
|
||||
# Repeated for each flag.
|
||||
# -flag* ...as above...
|
||||
#
|
||||
# The first form represents a basic flag with no associated
|
||||
# following argument. The second form extracts its value
|
||||
# from the following argument in $argvName.
|
||||
# -flag => defaultValue ?-call proc-name-and-args|-apply lambdaExpr?
|
||||
#
|
||||
# The first argument to this function is the name of a var holding the
|
||||
# args to parse. It will be overwritten, possibly with a smaller list.
|
||||
# -flag* => ...as above...
|
||||
#
|
||||
# The second argument the name of an array variable to create in the
|
||||
# caller's scope. (Pneumonic: => points to the next argument.)
|
||||
# :PRAGMA
|
||||
#
|
||||
# For the first form of flag, $script is run in the caller's scope if
|
||||
# $argv contains -flag, and the result of that script is the new value
|
||||
# for $tgtArrayName(-flag). This function intercepts [return $val]
|
||||
# from $script. Any empty script will result in the flag having ""
|
||||
# assigned to it.
|
||||
# The first two forms represents a basic flag with no associated
|
||||
# following argument. The third and fourth forms, called arg-consuming
|
||||
# flags, extract the value from the following argument in $argvName
|
||||
# (pneumonic: => points to the next argument.). The :PRAGMA form
|
||||
# offers a way to configure certain aspects of this call.
|
||||
#
|
||||
# The args list is only inspected until the first argument which is
|
||||
# not described by $prototype. i.e. the first "non-flag" (not counting
|
||||
# values consumed for flags defined like --flag=>default).
|
||||
# If $argv contains any given flag from $prototype, its default value
|
||||
# is overridden depending on several factors:
|
||||
#
|
||||
# - If the -literal flag is used, or the flag's script is a number,
|
||||
# value is used verbatim.
|
||||
#
|
||||
# - Else if the -call flag is used, the argument must be a proc name
|
||||
# and any leading arguments, e.g. {apply $myLambda}. The proc is passed
|
||||
# the (flag, value) as arguments (non-consuming flags will get
|
||||
# passed the flag's current/starting value and consuming flags will
|
||||
# get the next argument). Its result becomes the result of the
|
||||
# flag.
|
||||
#
|
||||
# - Else if -apply X is used, it's effectively shorthand for -call
|
||||
# {apply X}. Its argument may either be a $lambaRef or a {{f v}
|
||||
# {body}} construct.
|
||||
#
|
||||
# - Else if $script is one of the following values, it is treated as
|
||||
# the result of...
|
||||
#
|
||||
# - incr: increments the current value of the flag.
|
||||
#
|
||||
# - Else $script is eval'd to get its result value. That result
|
||||
# becomes the new flag value for $tgtArrayName(-flag). This
|
||||
# function intercepts [return $val] from eval'ing $script. Any
|
||||
# empty script will result in the flag having "" assigned to it.
|
||||
#
|
||||
# Unless the -flag has a trailing asterisk, e.g. -flag*, this function
|
||||
# assumes that each flag is unique, and using a flag more than once
|
||||
# causes an error to be triggered. the -flag* forms works similarly
|
||||
# except that may appear in $argv any number of times:
|
||||
#
|
||||
# - For non-arg-consuming flags, each invocation of -flag causes the
|
||||
# result of $script to overwrite the previous value. e.g. so
|
||||
# {-flag* {x} {incr foo}} has a default value of x, but passing in
|
||||
# -flag twice would change it to the result of incrementing foo
|
||||
# twice. This form can be used to implement, e.g., increasing
|
||||
# verbosity levels by passing -verbose multiple times.
|
||||
#
|
||||
# - For arg-consuming flags, the given flag starts with value X, but
|
||||
# if the flag is provided in $argv, the default is cleared, then
|
||||
# each instance of -flag causes its value to be appended to the
|
||||
# result, so {-flag* => {a b c}} defaults to {a b c}, but passing
|
||||
# in -flag y -flag z would change it to {y z}, not {a b c y z}..
|
||||
#
|
||||
# By default, the args list is only inspected until the first argument
|
||||
# which is not described by $prototype. i.e. the first "non-flag" (not
|
||||
# counting values consumed for flags defined like -flag => default).
|
||||
# The :all-flags pragma (see below) can modify this behavior.
|
||||
#
|
||||
# If a "--" flag is encountered, no more arguments are inspected as
|
||||
# flags. If "--" is the first non-flag argument, the "--" flag is
|
||||
# removed from the results but all remaining arguments are passed
|
||||
# through. If "--" appears after the first non-flag, it is retained.
|
||||
# flags unless the :all-flags pragma (see below) is in effect. The
|
||||
# first instance of "--" is removed from the target result list but
|
||||
# all remaining instances of "--" are are passed through.
|
||||
#
|
||||
# This function assumes that each flag is unique, and using a flag
|
||||
# more than once behaves in a last-one-wins fashion.
|
||||
# Any argvName entries not described in $prototype are considered to
|
||||
# be "non-flags" for purposes of this function, even if they
|
||||
# ostensibly look like flags.
|
||||
#
|
||||
# Any argvName entries not described in $prototype are not treated as
|
||||
# flags.
|
||||
#
|
||||
# Returns the number of flags it processed in $argvName.
|
||||
# Returns the number of flags it processed in $argvName, not counting
|
||||
# "--".
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# set args [list -foo -bar {blah} 8 9 10 -theEnd]
|
||||
# proj-parse-simple-flags args flags {
|
||||
# -foo 0 {expr 1}
|
||||
# -bar => 0
|
||||
# -no-baz 2 {return 0}
|
||||
# }
|
||||
## set args [list -foo -bar {blah} -z 8 9 10 -theEnd]
|
||||
## proj-parse-flags args flags {
|
||||
## -foo 0 {expr 1}
|
||||
## -bar => 0
|
||||
## -no-baz 1 {return 0}
|
||||
## -z 0 2
|
||||
## }
|
||||
#
|
||||
# After that $flags would contain {-foo 1 -bar {blah} -no-baz 2}
|
||||
# After that $flags would contain {-foo 1 -bar {blah} -no-baz 1 -z 2}
|
||||
# and $args would be {8 9 10 -theEnd}.
|
||||
#
|
||||
# Potential TODOs: consider using lappend instead of set so that any
|
||||
# given flag can be used more than once. Or add a syntax to indicate
|
||||
# that multiples are allowed. Also consider searching the whole
|
||||
# argv list, rather than stopping at the first non-flag
|
||||
# Pragmas:
|
||||
#
|
||||
proc proj-parse-simple-flags {argvName tgtArrayName prototype} {
|
||||
# Passing :PRAGMAS to this function may modify how it works. The
|
||||
# following pragmas are supported (note the leading ":"):
|
||||
#
|
||||
# :all-flags indicates that the whole input list should be scanned,
|
||||
# not stopping at the first non-flag or "--".
|
||||
#
|
||||
proc proj-parse-flags {argvName tgtArrayName prototype} {
|
||||
upvar $argvName argv
|
||||
upvar $tgtArrayName tgt
|
||||
array set dflt {}
|
||||
array set scripts {}
|
||||
array set consuming {}
|
||||
upvar $tgtArrayName outFlags
|
||||
array set flags {}; # staging area
|
||||
array set blob {}; # holds markers for various per-key state and options
|
||||
set incrSkip 1; # 1 if we stop at the first non-flag, else 0
|
||||
# Parse $prototype for flag definitions...
|
||||
set n [llength $prototype]
|
||||
# Figure out what our flags are...
|
||||
set checkProtoFlag {
|
||||
#puts "**** checkProtoFlag #$i of $n k=$k fv=$fv"
|
||||
switch -exact -- $fv {
|
||||
-literal {
|
||||
proj-assert {![info exists blob(${k}.consumes)]}
|
||||
set blob(${k}.script) [list expr [lindex $prototype [incr i]]]
|
||||
}
|
||||
-apply {
|
||||
set fv [lindex $prototype [incr i]]
|
||||
if {2 == [llength $fv]} {
|
||||
# Treat this as a lambda literal
|
||||
set fv [list $fv]
|
||||
}
|
||||
lappend blob(${k}.call) "apply $fv"
|
||||
}
|
||||
-call {
|
||||
# arg is either a proc name or {apply $aLambda}
|
||||
set fv [lindex $prototype [incr i]]
|
||||
lappend blob(${k}.call) $fv
|
||||
}
|
||||
default {
|
||||
proj-assert {![info exists blob(${k}.consumes)]}
|
||||
set blob(${k}.script) $fv
|
||||
}
|
||||
}
|
||||
if {$i >= $n} {
|
||||
proj-error -up "[proj-scope]: Missing argument for $k flag"
|
||||
}
|
||||
}
|
||||
for {set i 0} {$i < $n} {incr i} {
|
||||
set k [lindex $prototype $i]
|
||||
#puts "**** #$i of $n k=$k"
|
||||
proj-assert {[string match -* $k]} \
|
||||
"Invalid flag value: $k"
|
||||
set v ""
|
||||
set s ""
|
||||
switch -exact -- [lindex $prototype [expr {$i + 1}]] {
|
||||
=> {
|
||||
incr i 2
|
||||
if {$i >= $n} {
|
||||
proj-error "Missing argument for $k => flag"
|
||||
}
|
||||
set consuming($k) 1
|
||||
set v [lindex $prototype $i]
|
||||
}
|
||||
default {
|
||||
set v [lindex $prototype [incr i]]
|
||||
set s [lindex $prototype [incr i]]
|
||||
set scripts($k) $s
|
||||
|
||||
# Check for :PRAGMA...
|
||||
switch -exact -- $k {
|
||||
:all-flags {
|
||||
set incrSkip 0
|
||||
continue
|
||||
}
|
||||
}
|
||||
#puts "**** #$i of $n k=$k v=$v s=$s"
|
||||
set dflt($k) $v
|
||||
|
||||
proj-assert {[string match -* $k]} \
|
||||
"Invalid argument: $k"
|
||||
|
||||
if {[string match {*\*} $k]} {
|
||||
# Re-map -foo* to -foo and flag -foo as a repeatable flag
|
||||
set k [string map {* ""} $k]
|
||||
incr blob(${k}.multi)
|
||||
}
|
||||
|
||||
if {[info exists flags($k)]} {
|
||||
proj-error -up "[proj-scope]: Duplicated prototype for flag $k"
|
||||
}
|
||||
|
||||
switch -exact -- [lindex $prototype [expr {$i + 1}]] {
|
||||
=> {
|
||||
# -flag => DFLT ?-subflag arg?
|
||||
incr i 2
|
||||
if {$i >= $n} {
|
||||
proj-error -up "[proj-scope]: Missing argument for $k => flag"
|
||||
}
|
||||
incr blob(${k}.consumes)
|
||||
set vi [lindex $prototype $i]
|
||||
if {$vi in {-apply -call}} {
|
||||
proj-error -up "[proj-scope]: Missing default value for $k flag"
|
||||
} else {
|
||||
set fv [lindex $prototype [expr {$i + 1}]]
|
||||
if {$fv in {-apply -call}} {
|
||||
incr i
|
||||
eval $checkProtoFlag
|
||||
}
|
||||
}
|
||||
}
|
||||
default {
|
||||
# -flag VALUE ?flag? SCRIPT
|
||||
set vi [lindex $prototype [incr i]]
|
||||
set fv [lindex $prototype [incr i]]
|
||||
eval $checkProtoFlag
|
||||
}
|
||||
}
|
||||
#puts "**** #$i of $n k=$k vi=$vi"
|
||||
set flags($k) $vi
|
||||
}
|
||||
# Now look for those flags in the source list
|
||||
array set tgt [array get dflt]
|
||||
unset dflt
|
||||
#puts "-- flags"; parray flags
|
||||
#puts "-- blob"; parray blob
|
||||
set rc 0
|
||||
set rv {}
|
||||
set rv {}; # staging area for the target argv value
|
||||
set skipMode 0
|
||||
set n [llength $argv]
|
||||
# Now look for those flags in $argv...
|
||||
for {set i 0} {$i < $n} {incr i} {
|
||||
set arg [lindex $argv $i]
|
||||
#puts "-- [proj-scope] arg=$arg"
|
||||
if {$skipMode} {
|
||||
lappend rv $arg
|
||||
} elseif {"--" eq $arg} {
|
||||
incr skipMode
|
||||
} elseif {[info exists tgt($arg)]} {
|
||||
if {[info exists consuming($arg)]} {
|
||||
if {$i + 1 >= $n} {
|
||||
proj-assert 0 {Cannot happen - bounds already checked}
|
||||
# "--" is the conventional way to end processing of args
|
||||
if {[incr blob(--)] > 1} {
|
||||
# Elide only the first one
|
||||
lappend rv $arg
|
||||
}
|
||||
incr skipMode $incrSkip
|
||||
} elseif {[info exists flags($arg)]} {
|
||||
# A known flag...
|
||||
set isMulti [info exists blob(${arg}.multi)]
|
||||
incr blob(${arg}.seen)
|
||||
if {1 < $blob(${arg}.seen) && !$isMulti} {
|
||||
proj-error -up [proj-scope] "$arg flag was used multiple times"
|
||||
}
|
||||
set vMode 0; # 0=as-is, 1=eval, 2=call
|
||||
set isConsuming [info exists blob(${arg}.consumes)]
|
||||
if {$isConsuming} {
|
||||
incr i
|
||||
if {$i >= $n} {
|
||||
proj-error -up [proj-scope] "is missing argument for $arg flag"
|
||||
}
|
||||
set tgt($arg) [lindex $argv [incr i]]
|
||||
} elseif {"" eq $scripts($arg)} {
|
||||
set tgt($arg) ""
|
||||
set vv [lindex $argv $i]
|
||||
} elseif {[info exists blob(${arg}.script)]} {
|
||||
set vMode 1
|
||||
set vv $blob(${arg}.script)
|
||||
} else {
|
||||
#puts "**** running scripts($arg) $scripts($arg)"
|
||||
set code [catch {uplevel 1 $scripts($arg)} xrc xopt]
|
||||
#puts "**** tgt($arg)=$scripts($arg) code=$code rc=$rc"
|
||||
if {$code in {0 2}} {
|
||||
set tgt($arg) $xrc
|
||||
} else {
|
||||
return {*}$xopt $xrc
|
||||
set vv $flags($arg)
|
||||
}
|
||||
|
||||
if {[info exists blob(${arg}.call)]} {
|
||||
set vMode 2
|
||||
set vv [concat {*}$blob(${arg}.call) $arg $vv]
|
||||
} elseif {$isConsuming} {
|
||||
proj-assert {!$vMode}
|
||||
# fall through
|
||||
} elseif {"" eq $vv || [string is double -strict $vv]} {
|
||||
set vMode 0
|
||||
} elseif {$vv in {incr}} {
|
||||
set vMode 0
|
||||
switch -exact $vv {
|
||||
incr {
|
||||
set xx $flags($k); incr xx; set vv $xx; unset xx
|
||||
}
|
||||
default {
|
||||
proj-error "Unhandled \$vv value $vv"
|
||||
}
|
||||
}
|
||||
} else {
|
||||
set vv [list eval $vv]
|
||||
set vMode 1
|
||||
}
|
||||
if {$vMode} {
|
||||
set code [catch [list uplevel 1 $vv] vv xopt]
|
||||
if {$code ni {0 2}} {
|
||||
return {*}$xopt $vv
|
||||
}
|
||||
}
|
||||
if {$isConsuming && $isMulti} {
|
||||
if {1 == $blob(${arg}.seen)} {
|
||||
# On the first hit, overwrite the default with a new list.
|
||||
set flags($arg) [list $vv]
|
||||
} else {
|
||||
# On subsequent hits, append to the list.
|
||||
lappend flags($arg) $vv
|
||||
}
|
||||
} else {
|
||||
set flags($arg) $vv
|
||||
}
|
||||
incr rc
|
||||
} else {
|
||||
incr skipMode
|
||||
# Non-flag
|
||||
incr skipMode $incrSkip
|
||||
lappend rv $arg
|
||||
}
|
||||
}
|
||||
set argv $rv
|
||||
array set outFlags [array get flags]
|
||||
#puts "-- rv=$rv argv=$argv flags="; parray flags
|
||||
return $rc
|
||||
}; # proj-parse-flags
|
||||
|
||||
#
|
||||
# Older (deprecated) name of proj-parse-flags.
|
||||
#
|
||||
proc proj-parse-simple-flags {args} {
|
||||
tailcall proj-parse-flags {*}$args
|
||||
}
|
||||
|
||||
if {$::proj__Config(self-tests)} {
|
||||
set __ova $::proj__Config(verbose-assert);
|
||||
set ::proj__Config(verbose-assert) 1
|
||||
puts "Running [info script] self-tests..."
|
||||
# proj-cache...
|
||||
apply {{} {
|
||||
#proj-warn "Test code for proj-cache"
|
||||
proj-assert {![proj-cache-check -key here check]}
|
||||
@@ -2233,4 +2401,100 @@ if {$::proj__Config(self-tests)} {
|
||||
proj-assert {"" eq [proj-cache-remove]}
|
||||
proj-assert {"" eq $check}
|
||||
}}
|
||||
}
|
||||
|
||||
# proj-parse-flags ...
|
||||
apply {{} {
|
||||
set foo 3
|
||||
set argv {-a "hi - world" -b -b -b -- -a {bye bye} -- -d -D c -a "" --}
|
||||
proj-parse-flags argv flags {
|
||||
:all-flags
|
||||
-a* => "gets overwritten"
|
||||
-b* 7 {incr foo}
|
||||
-d 1 0
|
||||
-D 0 1
|
||||
}
|
||||
|
||||
#puts "-- argv = $argv"; parray flags;
|
||||
proj-assert {"-- c --" eq $argv}
|
||||
proj-assert {$flags(-a) eq "{hi - world} {bye bye} {}"}
|
||||
proj-assert {$foo == 6}
|
||||
proj-assert {$flags(-b) eq $foo}
|
||||
proj-assert {$flags(-d) == 0}
|
||||
proj-assert {$flags(-D) == 1}
|
||||
set foo 0
|
||||
foreach x $flags(-a) {
|
||||
proj-assert {$x in {{hi - world} {bye bye} {}}}
|
||||
incr foo
|
||||
}
|
||||
proj-assert {3 == $foo}
|
||||
|
||||
set argv {-a {hi world} -b -maybe -- -a {bye bye} -- -b c --}
|
||||
set foo 0
|
||||
proj-parse-flags argv flags {
|
||||
-a => "aaa"
|
||||
-b 0 {incr foo}
|
||||
-maybe no -literal yes
|
||||
}
|
||||
#parray flags; puts "--- argv = $argv"
|
||||
proj-assert {"-a {bye bye} -- -b c --" eq $argv}
|
||||
proj-assert {$flags(-a) eq "hi world"}
|
||||
proj-assert {1 == $flags(-b)}
|
||||
proj-assert {"yes" eq $flags(-maybe)}
|
||||
|
||||
set argv {-f -g -a aaa -M -M -M -L -H -A AAA a b c}
|
||||
set foo 0
|
||||
set myLambda {{flag val} {
|
||||
proj-assert {$flag in {-f -g -M}}
|
||||
#puts "myLambda flag=$flag val=$val"
|
||||
incr val
|
||||
}}
|
||||
proc myNonLambda {flag val} {
|
||||
proj-assert {$flag in {-A -a}}
|
||||
#puts "myNonLambda flag=$flag val=$val"
|
||||
concat $val $val
|
||||
}
|
||||
proj-parse-flags argv flags {
|
||||
-f 0 -call {apply $myLambda}
|
||||
-g 2 -apply $myLambda
|
||||
-h 3 -apply $myLambda
|
||||
-H 30 33
|
||||
-a => aAAAa -apply {{f v} {
|
||||
set v
|
||||
}}
|
||||
-A => AaaaA -call myNonLambda
|
||||
-B => 17 -call myNonLambda
|
||||
-M* 0 -apply $myLambda
|
||||
-L "" -literal $myLambda
|
||||
}
|
||||
rename myNonLambda ""
|
||||
#puts "--- argv = $argv"; parray flags
|
||||
proj-assert {$flags(-f) == 1}
|
||||
proj-assert {$flags(-g) == 3}
|
||||
proj-assert {$flags(-h) == 3}
|
||||
proj-assert {$flags(-H) == 33}
|
||||
proj-assert {$flags(-a) == {aaa}}
|
||||
proj-assert {$flags(-A) eq "AAA AAA"}
|
||||
proj-assert {$flags(-B) == 17}
|
||||
proj-assert {$flags(-M) == 3}
|
||||
proj-assert {$flags(-L) eq $myLambda}
|
||||
|
||||
set argv {-touch -validate}
|
||||
proj-parse-flags argv flags {
|
||||
-touch "" {return "-touch"}
|
||||
-validate 0 1
|
||||
}
|
||||
#puts "----- argv = $argv"; parray flags
|
||||
proj-assert {$flags(-touch) eq "-touch"}
|
||||
proj-assert {$flags(-validate) == 1}
|
||||
proj-assert {$argv eq {}}
|
||||
|
||||
set argv {-i -i -i}
|
||||
proj-parse-flags argv flags {
|
||||
-i* 0 incr
|
||||
}
|
||||
proj-assert {3 == $flags(-i)}
|
||||
}}
|
||||
set ::proj__Config(verbose-assert) $__ova
|
||||
unset __ova
|
||||
puts "Done running [info script] self-tests."
|
||||
}; # proj- API self-tests
|
||||
|
||||
+40
-19
@@ -217,6 +217,11 @@ proc sqlite-configure {buildMode configScript} {
|
||||
=> {This legacy flag has no effect on the library but may influence
|
||||
the generated sqlite_cfg.h by adding #define HAVE_LFS}
|
||||
}
|
||||
{canonical} {
|
||||
column-metadata => {Enable the column metadata APIs}
|
||||
# ^^^ Affects how sqlite3.c is generated, so is not available in
|
||||
# the autoconf build.
|
||||
}
|
||||
}
|
||||
|
||||
# Options for TCL support
|
||||
@@ -227,8 +232,6 @@ proc sqlite-configure {buildMode configScript} {
|
||||
This tree requires TCL for code generation but can use the in-tree
|
||||
copy of autosetup/jimsh0.c for that. The SQLite TCL extension and the
|
||||
test code require a canonical tclsh.}
|
||||
}
|
||||
{canonical} {
|
||||
with-tcl:DIR
|
||||
=> {Directory containing tclConfig.sh or a directory one level up from
|
||||
that, from which we can derive a directory containing tclConfig.sh.
|
||||
@@ -236,11 +239,10 @@ proc sqlite-configure {buildMode configScript} {
|
||||
the --prefix flag.}
|
||||
with-tclsh:PATH
|
||||
=> {Full pathname of tclsh to use. It is used for (A) trying to find
|
||||
tclConfig.sh and (B) all TCL-based code generation. Warning: if
|
||||
its containing dir has multiple tclsh versions, it may select the
|
||||
tclConfig.sh and (B) all TCL-based code generation. Use --with-tcl
|
||||
unless you have a specific need for this flag. Warning: if its
|
||||
containing dir has multiple tclsh versions, it may select the
|
||||
wrong tclConfig.sh!}
|
||||
}
|
||||
{canonical} {
|
||||
static-tclsqlite3=0
|
||||
=> {Statically-link tclsqlite3. This only works if TCL support is
|
||||
enabled and all requisite libraries are available in
|
||||
@@ -334,8 +336,8 @@ proc sqlite-configure {buildMode configScript} {
|
||||
=> {Link the sqlite3 shell app against the DLL instead of embedding sqlite3.c}
|
||||
}
|
||||
{canonical autoconf} {
|
||||
# A potential TODO without a current use case:
|
||||
#rpath=1 => {Disable use of the rpath linker flag}
|
||||
rpath=1 => {Disable use of the rpath linker flag}
|
||||
|
||||
# soname: https://sqlite.org/src/forumpost/5a3b44f510df8ded
|
||||
soname:=legacy
|
||||
=> {SONAME for libsqlite3.so. "none", or not using this flag, sets no
|
||||
@@ -772,7 +774,8 @@ proc sqlite-handle-common-feature-flags {} {
|
||||
sqlite-add-feature-flag -DSQLITE_ENABLE_MEMSYS3
|
||||
}
|
||||
}
|
||||
scanstatus -DSQLITE_ENABLE_STMT_SCANSTATUS {}
|
||||
scanstatus -DSQLITE_ENABLE_STMT_SCANSTATUS {}
|
||||
column-metadata -DSQLITE_ENABLE_COLUMN_METADATA {}
|
||||
}] {
|
||||
if {$boolFlag ni $::autosetup(options)} {
|
||||
# Skip flags which are in the canonical build but not
|
||||
@@ -1969,13 +1972,14 @@ proc sqlite-check-tcl {} {
|
||||
# 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 ""]
|
||||
set sq3Ver [get-define PACKAGE_VERSION]
|
||||
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
|
||||
set tcllibdir $i/sqlite${sq3Ver}
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2111,15 +2115,31 @@ proc sqlite-determine-codegen-tcl {} {
|
||||
# sqlite-determine-codegen-tcl.
|
||||
proc sqlite-handle-tcl {} {
|
||||
sqlite-check-tcl
|
||||
if {"canonical" eq $::sqliteConfig(build-mode)} {
|
||||
msg-result "TCL for code generation: [sqlite-determine-codegen-tcl]"
|
||||
if {"canonical" ne $::sqliteConfig(build-mode)} return
|
||||
msg-result "TCL for code generation: [sqlite-determine-codegen-tcl]"
|
||||
|
||||
# Determine the base name of the Tcl extension's DLL
|
||||
#
|
||||
if {[get-define HAVE_TCL]} {
|
||||
if {[string match *-cygwin [get-define host]]} {
|
||||
set libname cyg
|
||||
} else {
|
||||
set libname lib
|
||||
}
|
||||
if {[get-define TCL_MAJOR_VERSION] > 8} {
|
||||
append libname tcl9
|
||||
}
|
||||
append libname sqlite
|
||||
} else {
|
||||
set libname ""
|
||||
}
|
||||
define TCL_EXT_DLL_BASENAME $libname
|
||||
# The extension is added in the makefile
|
||||
}
|
||||
|
||||
########################################################################
|
||||
# Handle the --enable/disable-rpath flag.
|
||||
proc sqlite-handle-rpath {} {
|
||||
proj-check-rpath
|
||||
# autosetup/cc-shared.tcl sets the rpath flag definition in
|
||||
# [get-define SH_LINKRPATH], but it does so on a per-platform basis
|
||||
# rather than as a compiler check. Though we should do a proper
|
||||
@@ -2128,12 +2148,13 @@ proc sqlite-handle-rpath {} {
|
||||
# for which sqlite-env-is-unix-on-windows returns a non-empty
|
||||
# string.
|
||||
|
||||
# if {[proj-opt-truthy rpath]} {
|
||||
# proj-check-rpath
|
||||
# } else {
|
||||
# msg-result "Disabling use of rpath."
|
||||
# define LDFLAGS_RPATH ""
|
||||
# }
|
||||
# https://sqlite.org/forum/forumpost/13cac3b56516f849
|
||||
if {[proj-opt-truthy rpath]} {
|
||||
proj-check-rpath
|
||||
} else {
|
||||
msg-result "Disabling use of rpath."
|
||||
define LDFLAGS_RPATH ""
|
||||
}
|
||||
}
|
||||
|
||||
########################################################################
|
||||
|
||||
+77
-56
@@ -92,6 +92,7 @@ array set teaish__Config [proj-strip-hash-comments {
|
||||
-tm.tcl.in TEAISH_TM_TCL_IN
|
||||
-options {}
|
||||
-pragmas {}
|
||||
-src {}
|
||||
}
|
||||
|
||||
#
|
||||
@@ -331,29 +332,33 @@ proc teaish-configure-core {} {
|
||||
-url - -v ""
|
||||
-tm.tcl - -v ""
|
||||
-tm.tcl.in - -v ""
|
||||
-src - -v ""
|
||||
} {
|
||||
#proj-assert 0 {Just testing}
|
||||
set isPIFlag [expr {"-" ne $pflag}]
|
||||
if {$isPIFlag} {
|
||||
if {[info exists ::teaish__PkgInfo($pflag)]} {
|
||||
# Was already set - skip it.
|
||||
continue;
|
||||
}
|
||||
proj-assert {{-} eq $key}
|
||||
proj-assert {{-} eq $key};# "Unexpected pflag=$pflag key=$key type=$type val=$val"
|
||||
set key $f2d($pflag)
|
||||
}
|
||||
proj-assert {"" ne $key}
|
||||
set got [get-define $key "<nope>"]
|
||||
if {"<nope>" ne $got} {
|
||||
# Was already set - skip it.
|
||||
continue
|
||||
if {"" ne $key} {
|
||||
if {"<nope>" ne [get-define $key "<nope>"]} {
|
||||
# Was already set - skip it.
|
||||
continue
|
||||
}
|
||||
}
|
||||
switch -exact -- $type {
|
||||
-v {}
|
||||
-e { set val [eval $val] }
|
||||
default { proj-error "Invalid type flag: $type" }
|
||||
}
|
||||
#puts "***** defining default $pflag $key {$val} isPIFlag=$isPIFlag got=$got"
|
||||
define $key $val
|
||||
#puts "***** defining default $pflag $key {$val} isPIFlag=$isPIFlag"
|
||||
if {$key ne ""} {
|
||||
define $key $val
|
||||
}
|
||||
if {$isPIFlag} {
|
||||
set ::teaish__PkgInfo($pflag) $val
|
||||
}
|
||||
@@ -541,10 +546,10 @@ proc teaish__configure_phase1 {} {
|
||||
define TEAISH_VSATISFIES_CODE [join $code "\n"]
|
||||
}}; # vsatisfies
|
||||
|
||||
if {[proj-looks-like-windows] || [proj-looks-like-mac]} {
|
||||
if {[proj-looks-like-windows]} {
|
||||
# Without this, linking of an extension will not work on Cygwin or
|
||||
# Msys2.
|
||||
msg-result "Using USE_TCL_STUBS for this environment"
|
||||
msg-result "Using USE_TCL_STUBS for Unix(ish)-on-Windows environment"
|
||||
teaish-cflags-add -DUSE_TCL_STUBS=1
|
||||
}
|
||||
|
||||
@@ -585,7 +590,8 @@ proc teaish__configure_phase1 {} {
|
||||
#
|
||||
if {0x0f & $::teaish__Config(pkginit-policy)} {
|
||||
file delete -force -- [get-define TEAISH_PKGINIT_TCL]
|
||||
proj-dot-ins-append [get-define TEAISH_PKGINIT_TCL_IN]
|
||||
proj-dot-ins-append [get-define TEAISH_PKGINIT_TCL_IN] \
|
||||
[get-define TEAISH_PKGINIT_TCL]
|
||||
}
|
||||
if {0x0f & $::teaish__Config(tm-policy)} {
|
||||
file delete -force -- [get-define TEAISH_TM_TCL]
|
||||
@@ -595,17 +601,20 @@ proc teaish__configure_phase1 {} {
|
||||
apply {{} {
|
||||
# Queue up any remaining dot-in files
|
||||
set dotIns [list]
|
||||
foreach d {
|
||||
TEAISH_TESTER_TCL_IN
|
||||
TEAISH_TEST_TCL_IN
|
||||
TEAISH_MAKEFILE_IN
|
||||
foreach {dIn => dOut} {
|
||||
TEAISH_TESTER_TCL_IN => TEAISH_TESTER_TCL
|
||||
TEAISH_TEST_TCL_IN => TEAISH_TEST_TCL
|
||||
TEAISH_MAKEFILE_IN => TEAISH_MAKEFILE
|
||||
} {
|
||||
lappend dotIns [get-define $d ""]
|
||||
lappend dotIns [get-define $dIn ""] [get-define $dOut ""]
|
||||
}
|
||||
lappend dotIns $::autosetup(srcdir)/Makefile.in; # must be after TEAISH_MAKEFILE_IN
|
||||
foreach f $dotIns {
|
||||
if {"" ne $f} {
|
||||
proj-dot-ins-append $f
|
||||
lappend dotIns $::autosetup(srcdir)/Makefile.in Makefile; # must be after TEAISH_MAKEFILE_IN.
|
||||
# Much later: probably because of timestamps for deps purposes :-?
|
||||
#puts "dotIns=$dotIns"
|
||||
foreach {i o} $dotIns {
|
||||
if {"" ne $i && "" ne $o} {
|
||||
#puts " pre-dot-ins-append: \[$i\] -> \[$o\]"
|
||||
proj-dot-ins-append $i $o
|
||||
}
|
||||
}
|
||||
}}
|
||||
@@ -640,10 +649,10 @@ proc teaish__configure_phase1 {} {
|
||||
#
|
||||
# NO [define]s after this point!
|
||||
#
|
||||
proj-dot-ins-process -validate
|
||||
proj-if-opt-truthy teaish-dump-defines {
|
||||
proj-file-write config.defines.txt $tdefs
|
||||
}
|
||||
proj-dot-ins-process -validate
|
||||
|
||||
}; # teaish__configure_phase1
|
||||
|
||||
@@ -1068,7 +1077,7 @@ If you are attempting an out-of-tree build, use
|
||||
]]} {
|
||||
if {[string match *.in $extM]} {
|
||||
define TEAISH_MAKEFILE_IN $extM
|
||||
define TEAISH_MAKEFILE [file rootname [file tail $extM]]
|
||||
define TEAISH_MAKEFILE _[file rootname [file tail $extM]]
|
||||
} else {
|
||||
define TEAISH_MAKEFILE_IN ""
|
||||
define TEAISH_MAKEFILE $extM
|
||||
@@ -1136,8 +1145,8 @@ If you are attempting an out-of-tree build, use
|
||||
set flist [list $dirExt/teaish.test.tcl.in $dirExt/teaish.test.tcl]
|
||||
if {[proj-first-file-found ttt $flist]} {
|
||||
if {[string match *.in $ttt]} {
|
||||
# Generate teaish.test.tcl from $ttt
|
||||
set xt [file rootname [file tail $ttt]]
|
||||
# Generate _teaish.test.tcl from $ttt
|
||||
set xt _[file rootname [file tail $ttt]]
|
||||
file delete -force -- $xt; # ensure no stale copy is used
|
||||
define TEAISH_TEST_TCL $xt
|
||||
define TEAISH_TEST_TCL_IN $ttt
|
||||
@@ -1304,7 +1313,6 @@ proc teaish-ldflags-prepend {args} {
|
||||
# object files (which are typically in the build tree)).
|
||||
#
|
||||
proc teaish-src-add {args} {
|
||||
set i 0
|
||||
proj-parse-simple-flags args flags {
|
||||
-dist 0 {expr 1}
|
||||
-dir 0 {expr 1}
|
||||
@@ -1389,7 +1397,7 @@ proc teaish__cleanup_rule {{tgt clean}} {
|
||||
return ${tgt}-_${x}_
|
||||
}
|
||||
|
||||
# @teaish-make-obj objfile srcfile ?...args?
|
||||
# @teaish-make-obj ?flags? ?...args?
|
||||
#
|
||||
# Uses teaish-make-add to inject makefile rules for $objfile from
|
||||
# $srcfile, which is assumed to be C code which uses libtcl. Unless
|
||||
@@ -1403,43 +1411,45 @@ proc teaish__cleanup_rule {{tgt clean}} {
|
||||
# Any arguments after the 2nd may be flags described below or, if no
|
||||
# -recipe is provided, flags for the compiler call.
|
||||
#
|
||||
# -obj obj-filename.o
|
||||
#
|
||||
# -src src-filename.c
|
||||
#
|
||||
# -recipe {...}
|
||||
# Uses the trimmed value of {...} as the recipe, prefixing it with
|
||||
# a single hard-tab character.
|
||||
#
|
||||
# -deps {...}
|
||||
# List of extra files to list as dependencies of $o. Good luck
|
||||
# escaping non-trivial cases properly.
|
||||
# List of extra files to list as dependencies of $o.
|
||||
#
|
||||
# -clean
|
||||
# Generate cleanup rules as well.
|
||||
proc teaish-make-obj {o src args} {
|
||||
set consume 0
|
||||
set clean 0
|
||||
set flag ""
|
||||
array set flags {}
|
||||
set xargs {}
|
||||
foreach arg $args {
|
||||
if {$consume} {
|
||||
set consume 0
|
||||
set flags($flag) $arg
|
||||
continue
|
||||
}
|
||||
switch -exact -- $arg {
|
||||
-clean {incr clean}
|
||||
-recipe -
|
||||
-deps {
|
||||
set flag $arg
|
||||
incr consume
|
||||
}
|
||||
default {
|
||||
lappend xargs $arg
|
||||
}
|
||||
proc teaish-make-obj {args} {
|
||||
proj-parse-simple-flags args flags {
|
||||
-clean 0 {expr 1}
|
||||
-recipe => {}
|
||||
-deps => {}
|
||||
-obj => {}
|
||||
-src => {}
|
||||
}
|
||||
#parray flags
|
||||
if {"" eq $flags(-obj)} {
|
||||
set args [lassign $args flags(-obj)]
|
||||
if {"" eq $flags(-obj)} {
|
||||
proj-error "Missing -obj flag."
|
||||
}
|
||||
}
|
||||
foreach f {-deps -src} {
|
||||
set flags($f) [string trim [string map {\n " "} $flags($f)]]
|
||||
}
|
||||
foreach f {-deps -src} {
|
||||
set flags($f) [string trim $flags($f)]
|
||||
}
|
||||
#parray flags
|
||||
#puts "-- args=$args"
|
||||
teaish-make-add \
|
||||
"# [proj-scope 1] -> [proj-scope] $o $src" -nl \
|
||||
"$o: $src $::teaish__Config(teaish.tcl)"
|
||||
"# [proj-scope 1] -> [proj-scope] $flags(-obj) $flags(-src)" -nl \
|
||||
"$flags(-obj): $flags(-src) $::teaish__Config(teaish.tcl)"
|
||||
if {[info exists flags(-deps)]} {
|
||||
teaish-make-add " " [join $flags(-deps)]
|
||||
}
|
||||
@@ -1447,12 +1457,12 @@ proc teaish-make-obj {o src args} {
|
||||
if {[info exists flags(-recipe)]} {
|
||||
teaish-make-add [string trim $flags(-recipe)] -nl
|
||||
} else {
|
||||
teaish-make-add [join [list \$(CC.tcl) -c $src {*}$xargs]] -nl
|
||||
teaish-make-add [join [list \$(CC.tcl) -c $flags(-src) {*}$args]] -nl
|
||||
}
|
||||
if {$clean} {
|
||||
if {$flags(-clean)} {
|
||||
set rule [teaish__cleanup_rule]
|
||||
teaish-make-add \
|
||||
"clean: $rule\n$rule:\n\trm -f \"$o\"\n"
|
||||
"clean: $rule\n$rule:\n\trm -f \"$flags(-obj)\"\n"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2080,6 +2090,17 @@ proc teaish-pkginfo-set {args} {
|
||||
set v $x
|
||||
}
|
||||
|
||||
-src {
|
||||
set d $::teaish__Config(extension-dir)
|
||||
foreach f $v {
|
||||
lappend ::teaish__Config(dist-files) $f
|
||||
lappend ::teaish__Config(extension-src) $d/$f
|
||||
lappend ::teaish__PkgInfo(-src) $f
|
||||
# ^^^ so that default-value initialization in
|
||||
# teaish-configure-core recognizes that it's been set.
|
||||
}
|
||||
}
|
||||
|
||||
-tm.tcl -
|
||||
-tm.tcl.in {
|
||||
if {0x30 & $::teaish__Config(pkgindex-policy)} {
|
||||
@@ -2517,7 +2538,7 @@ proc teaish__install {{dDest ""}} {
|
||||
] {
|
||||
teaish__verbose 1 msg-result "Copying files to $destDir..."
|
||||
file mkdir $destDir
|
||||
foreach f [glob -directory $srcDir *] {
|
||||
foreach f [glob -nocomplain -directory $srcDir *] {
|
||||
if {[string match {*~} $f] || [string match "#*#" [file tail $f]]} {
|
||||
# Editor-generated backups and emacs lock files
|
||||
continue
|
||||
|
||||
@@ -99,7 +99,7 @@ proc test__affert {failMode args} {
|
||||
lassign $args script msg
|
||||
}
|
||||
incr ::test__Counters($what)
|
||||
if {![uplevel 1 [concat expr [list $script]]]} {
|
||||
if {![uplevel 1 expr [list $script]]} {
|
||||
if {"" eq $msg} {
|
||||
set msg $script
|
||||
}
|
||||
@@ -136,6 +136,40 @@ proc assert {args} {
|
||||
tailcall test__affert 1 {*}$args
|
||||
}
|
||||
|
||||
#
|
||||
# @assert-matches ?-e? pattern ?-e? rhs ?msg?
|
||||
#
|
||||
# Equivalent to assert {[string match $pattern $rhs]} except that
|
||||
# if either of those are prefixed with an -e flag, they are eval'd
|
||||
# and their results are used.
|
||||
#
|
||||
proc assert-matches {args} {
|
||||
set evalLhs 0
|
||||
set evalRhs 0
|
||||
if {"-e" eq [lindex $args 0]} {
|
||||
incr evalLhs
|
||||
set args [lassign $args -]
|
||||
}
|
||||
set args [lassign $args pattern]
|
||||
if {"-e" eq [lindex $args 0]} {
|
||||
incr evalRhs
|
||||
set args [lassign $args -]
|
||||
}
|
||||
set args [lassign $args rhs msg]
|
||||
|
||||
if {$evalLhs} {
|
||||
set pattern [uplevel 1 $pattern]
|
||||
}
|
||||
if {$evalRhs} {
|
||||
set rhs [uplevel 1 $rhs]
|
||||
}
|
||||
#puts "***pattern=$pattern\n***rhs=$rhs"
|
||||
tailcall test__affert 1 \
|
||||
[join [list \[ string match [list $pattern] [list $rhs] \]]] $msg
|
||||
# why does this not work? [list \[ string match [list $pattern] [list $rhs] \]] $msg
|
||||
# "\[string match [list $pattern] [list $rhs]\]"
|
||||
}
|
||||
|
||||
#
|
||||
# @test-assert testId script ?msg?
|
||||
#
|
||||
@@ -157,7 +191,7 @@ proc test-expect {testId script result} {
|
||||
puts "test $testId"
|
||||
set x [string trim [uplevel 1 $script]]
|
||||
set result [string trim $result]
|
||||
tailcall test__affert 0 [list $x eq $result] \
|
||||
tailcall test__affert 0 [list "{$x}" eq "{$result}"] \
|
||||
"\nEXPECTED: <<$result>>\nGOT: <<$x>>"
|
||||
}
|
||||
|
||||
@@ -169,7 +203,7 @@ proc test-expect {testId script result} {
|
||||
#
|
||||
proc test-catch {cmd args} {
|
||||
if {[catch {
|
||||
$cmd {*}$args
|
||||
uplevel 1 $cmd {*}$args
|
||||
} rc xopts]} {
|
||||
puts "[test-current-scope] ignoring failure of: $cmd [lindex $args 0]: $rc"
|
||||
return 1
|
||||
@@ -177,6 +211,37 @@ proc test-catch {cmd args} {
|
||||
return 0
|
||||
}
|
||||
|
||||
#
|
||||
# @test-catch-matching pattern (script|cmd args...)
|
||||
#
|
||||
# Works like test-catch, but it expects its argument(s) to to throw an
|
||||
# error matching the given string (checked with [string match]). If
|
||||
# they do not throw, or the error does not match $pattern, this
|
||||
# function throws, else it returns 1.
|
||||
#
|
||||
# If there is no second argument, the $cmd is assumed to be a script,
|
||||
# and will be eval'd in the caller's scope.
|
||||
#
|
||||
# TODO: add -glob and -regex flags to control matching flavor.
|
||||
#
|
||||
proc test-catch-matching {pattern cmd args} {
|
||||
if {[catch {
|
||||
#puts "**** catch-matching cmd=$cmd args=$args"
|
||||
if {0 == [llength $args]} {
|
||||
uplevel 1 $cmd {*}$args
|
||||
} else {
|
||||
$cmd {*}$args
|
||||
}
|
||||
} rc xopts]} {
|
||||
if {[string match $pattern $rc]} {
|
||||
return 1
|
||||
} else {
|
||||
error "[test-current-scope] exception does not match {$pattern}: {$rc}"
|
||||
}
|
||||
}
|
||||
error "[test-current-scope] expecting to see an error matching {$pattern}"
|
||||
}
|
||||
|
||||
if {![array exists ::teaish__BuildFlags]} {
|
||||
array set ::teaish__BuildFlags {}
|
||||
}
|
||||
|
||||
+1
-1
@@ -213,7 +213,7 @@ typedef sqlite3_int64 i64; /* 8-byte signed integer */
|
||||
** Macros needed to provide flexible arrays in a portable way
|
||||
*/
|
||||
#ifndef offsetof
|
||||
# define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
|
||||
# define offsetof(ST,M) ((size_t)((char*)&((ST*)0)->M - (char*)0))
|
||||
#endif
|
||||
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
|
||||
# define FLEXARRAY
|
||||
|
||||
+1
-1
@@ -80,7 +80,7 @@ typedef sqlite3_uint64 u64;
|
||||
** Macros needed to provide flexible arrays in a portable way
|
||||
*/
|
||||
#ifndef offsetof
|
||||
# define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
|
||||
# define offsetof(ST,M) ((size_t)((char*)&((ST*)0)->M - (char*)0))
|
||||
#endif
|
||||
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
|
||||
# define FLEXARRAY
|
||||
|
||||
+97
-51
@@ -554,6 +554,36 @@ struct Fts5SegIter {
|
||||
u8 bDel; /* True if the delete flag is set */
|
||||
};
|
||||
|
||||
static int fts5IndexCorruptRowid(Fts5Index *pIdx, i64 iRowid){
|
||||
pIdx->rc = FTS5_CORRUPT;
|
||||
sqlite3Fts5ConfigErrmsg(pIdx->pConfig,
|
||||
"fts5: corruption found reading blob %lld from table \"%s\"",
|
||||
iRowid, pIdx->pConfig->zName
|
||||
);
|
||||
return SQLITE_CORRUPT_VTAB;
|
||||
}
|
||||
#define FTS5_CORRUPT_ROWID(pIdx, iRowid) fts5IndexCorruptRowid(pIdx, iRowid)
|
||||
|
||||
static int fts5IndexCorruptIter(Fts5Index *pIdx, Fts5SegIter *pIter){
|
||||
pIdx->rc = FTS5_CORRUPT;
|
||||
sqlite3Fts5ConfigErrmsg(pIdx->pConfig,
|
||||
"fts5: corruption on page %d, segment %d, table \"%s\"",
|
||||
pIter->iLeafPgno, pIter->pSeg->iSegid, pIdx->pConfig->zName
|
||||
);
|
||||
return SQLITE_CORRUPT_VTAB;
|
||||
}
|
||||
#define FTS5_CORRUPT_ITER(pIdx, pIter) fts5IndexCorruptIter(pIdx, pIter)
|
||||
|
||||
static int fts5IndexCorruptIdx(Fts5Index *pIdx){
|
||||
pIdx->rc = FTS5_CORRUPT;
|
||||
sqlite3Fts5ConfigErrmsg(pIdx->pConfig,
|
||||
"fts5: corruption in table \"%s\"", pIdx->pConfig->zName
|
||||
);
|
||||
return SQLITE_CORRUPT_VTAB;
|
||||
}
|
||||
#define FTS5_CORRUPT_IDX(pIdx) fts5IndexCorruptIdx(pIdx)
|
||||
|
||||
|
||||
/*
|
||||
** Array of tombstone pages. Reference counted.
|
||||
*/
|
||||
@@ -843,7 +873,7 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){
|
||||
** All the reasons those functions might return SQLITE_ERROR - missing
|
||||
** table, missing row, non-blob/text in block column - indicate
|
||||
** backing store corruption. */
|
||||
if( rc==SQLITE_ERROR ) rc = FTS5_CORRUPT;
|
||||
if( rc==SQLITE_ERROR ) rc = FTS5_CORRUPT_ROWID(p, iRowid);
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
u8 *aOut = 0; /* Read blob data into this buffer */
|
||||
@@ -893,7 +923,7 @@ static Fts5Data *fts5LeafRead(Fts5Index *p, i64 iRowid){
|
||||
Fts5Data *pRet = fts5DataRead(p, iRowid);
|
||||
if( pRet ){
|
||||
if( pRet->nn<4 || pRet->szLeaf>pRet->nn ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ROWID(p, iRowid);
|
||||
fts5DataRelease(pRet);
|
||||
pRet = 0;
|
||||
}
|
||||
@@ -1252,8 +1282,14 @@ static Fts5Structure *fts5StructureReadUncached(Fts5Index *p){
|
||||
/* TODO: Do we need this if the leaf-index is appended? Probably... */
|
||||
memset(&pData->p[pData->nn], 0, FTS5_DATA_PADDING);
|
||||
p->rc = fts5StructureDecode(pData->p, pData->nn, &iCookie, &pRet);
|
||||
if( p->rc==SQLITE_OK && (pConfig->pgsz==0 || pConfig->iCookie!=iCookie) ){
|
||||
p->rc = sqlite3Fts5ConfigLoad(pConfig, iCookie);
|
||||
if( p->rc==SQLITE_OK ){
|
||||
if( (pConfig->pgsz==0 || pConfig->iCookie!=iCookie) ){
|
||||
p->rc = sqlite3Fts5ConfigLoad(pConfig, iCookie);
|
||||
}
|
||||
}else if( p->rc==SQLITE_CORRUPT_VTAB ){
|
||||
sqlite3Fts5ConfigErrmsg(p->pConfig,
|
||||
"fts5: corrupt structure record for table \"%s\"", p->pConfig->zName
|
||||
);
|
||||
}
|
||||
fts5DataRelease(pData);
|
||||
if( p->rc!=SQLITE_OK ){
|
||||
@@ -1876,7 +1912,7 @@ static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){
|
||||
while( iOff>=pIter->pLeaf->szLeaf ){
|
||||
fts5SegIterNextPage(p, pIter);
|
||||
if( pIter->pLeaf==0 ){
|
||||
if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT;
|
||||
if( p->rc==SQLITE_OK ) FTS5_CORRUPT_ITER(p, pIter);
|
||||
return;
|
||||
}
|
||||
iOff = 4;
|
||||
@@ -1908,7 +1944,7 @@ static void fts5SegIterLoadTerm(Fts5Index *p, Fts5SegIter *pIter, int nKeep){
|
||||
|
||||
iOff += fts5GetVarint32(&a[iOff], nNew);
|
||||
if( iOff+nNew>pIter->pLeaf->szLeaf || nKeep>pIter->term.n || nNew==0 ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ITER(p, pIter);
|
||||
return;
|
||||
}
|
||||
pIter->term.n = nKeep;
|
||||
@@ -1950,9 +1986,9 @@ static void fts5SegIterSetNext(Fts5Index *p, Fts5SegIter *pIter){
|
||||
** leave an error in the Fts5Index object.
|
||||
*/
|
||||
static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){
|
||||
const i64 nTomb = (i64)pIter->pSeg->nPgTombstone;
|
||||
const int nTomb = pIter->pSeg->nPgTombstone;
|
||||
if( nTomb>0 ){
|
||||
i64 nByte = SZ_FTS5TOMBSTONEARRAY(nTomb+1);
|
||||
int nByte = SZ_FTS5TOMBSTONEARRAY(nTomb+1);
|
||||
Fts5TombstoneArray *pNew;
|
||||
pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte);
|
||||
if( pNew ){
|
||||
@@ -2103,7 +2139,7 @@ static void fts5SegIterReverseNewPage(Fts5Index *p, Fts5SegIter *pIter){
|
||||
iRowidOff = fts5LeafFirstRowidOff(pNew);
|
||||
if( iRowidOff ){
|
||||
if( iRowidOff>=pNew->szLeaf ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ITER(p, pIter);
|
||||
}else{
|
||||
pIter->pLeaf = pNew;
|
||||
pIter->iLeafOffset = iRowidOff;
|
||||
@@ -2337,7 +2373,7 @@ static void fts5SegIterNext(
|
||||
}
|
||||
assert_nc( iOff<pLeaf->szLeaf );
|
||||
if( iOff>pLeaf->szLeaf ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ITER(p, pIter);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -2445,18 +2481,20 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){
|
||||
fts5DataRelease(pIter->pLeaf);
|
||||
pIter->pLeaf = pLast;
|
||||
pIter->iLeafPgno = pgnoLast;
|
||||
iOff = fts5LeafFirstRowidOff(pLast);
|
||||
if( iOff>pLast->szLeaf ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
return;
|
||||
}
|
||||
iOff += fts5GetVarint(&pLast->p[iOff], (u64*)&pIter->iRowid);
|
||||
pIter->iLeafOffset = iOff;
|
||||
if( p->rc==SQLITE_OK ){
|
||||
iOff = fts5LeafFirstRowidOff(pLast);
|
||||
if( iOff>pLast->szLeaf ){
|
||||
FTS5_CORRUPT_ITER(p, pIter);
|
||||
return;
|
||||
}
|
||||
iOff += fts5GetVarint(&pLast->p[iOff], (u64*)&pIter->iRowid);
|
||||
pIter->iLeafOffset = iOff;
|
||||
|
||||
if( fts5LeafIsTermless(pLast) ){
|
||||
pIter->iEndofDoclist = pLast->nn+1;
|
||||
}else{
|
||||
pIter->iEndofDoclist = fts5LeafFirstTermOff(pLast);
|
||||
if( fts5LeafIsTermless(pLast) ){
|
||||
pIter->iEndofDoclist = pLast->nn+1;
|
||||
}else{
|
||||
pIter->iEndofDoclist = fts5LeafFirstTermOff(pLast);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2526,7 +2564,7 @@ static void fts5LeafSeek(
|
||||
iPgidx += fts5GetVarint32(&a[iPgidx], iTermOff);
|
||||
iOff = iTermOff;
|
||||
if( iOff>n ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ITER(p, pIter);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2569,7 +2607,7 @@ static void fts5LeafSeek(
|
||||
iOff = iTermOff;
|
||||
|
||||
if( iOff>=n ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ITER(p, pIter);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2591,7 +2629,7 @@ static void fts5LeafSeek(
|
||||
iPgidx = (u32)pIter->pLeaf->szLeaf;
|
||||
iPgidx += fts5GetVarint32(&pIter->pLeaf->p[iPgidx], iOff);
|
||||
if( iOff<4 || (i64)iOff>=pIter->pLeaf->szLeaf ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ITER(p, pIter);
|
||||
return;
|
||||
}else{
|
||||
nKeep = 0;
|
||||
@@ -2606,7 +2644,7 @@ static void fts5LeafSeek(
|
||||
|
||||
search_success:
|
||||
if( (i64)iOff+nNew>n || nNew<1 ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ITER(p, pIter);
|
||||
return;
|
||||
}
|
||||
pIter->iLeafOffset = iOff + nNew;
|
||||
@@ -3071,7 +3109,7 @@ static void fts5SegIterGotoPage(
|
||||
assert( iLeafPgno>pIter->iLeafPgno );
|
||||
|
||||
if( iLeafPgno>pIter->pSeg->pgnoLast ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_IDX(p);
|
||||
}else{
|
||||
fts5DataRelease(pIter->pNextLeaf);
|
||||
pIter->pNextLeaf = 0;
|
||||
@@ -3086,7 +3124,7 @@ static void fts5SegIterGotoPage(
|
||||
u8 *a = pIter->pLeaf->p;
|
||||
int n = pIter->pLeaf->szLeaf;
|
||||
if( iOff<4 || iOff>=n ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_IDX(p);
|
||||
}else{
|
||||
iOff += fts5GetVarint(&a[iOff], (u64*)&pIter->iRowid);
|
||||
pIter->iLeafOffset = iOff;
|
||||
@@ -3565,7 +3603,7 @@ static void fts5ChunkIterate(
|
||||
if( nRem<=0 ){
|
||||
break;
|
||||
}else if( pSeg->pSeg==0 ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_IDX(p);
|
||||
return;
|
||||
}else{
|
||||
pgno++;
|
||||
@@ -4668,7 +4706,7 @@ static void fts5TrimSegments(Fts5Index *p, Fts5Iter *pIter){
|
||||
** a single page has been assigned to more than one segment. In
|
||||
** this case a prior iteration of this loop may have corrupted the
|
||||
** segment currently being trimmed. */
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ROWID(p, iLeafRowid);
|
||||
}else{
|
||||
fts5BufferZero(&buf);
|
||||
fts5BufferGrow(&p->rc, &buf, pData->nn);
|
||||
@@ -5135,7 +5173,7 @@ static void fts5SecureDeleteOverflow(
|
||||
}else if( bDetailNone ){
|
||||
break;
|
||||
}else if( iNext>=pLeaf->szLeaf || pLeaf->nn<pLeaf->szLeaf || iNext<4 ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ROWID(p, iRowid);
|
||||
break;
|
||||
}else{
|
||||
int nShift = iNext - 4;
|
||||
@@ -5155,7 +5193,7 @@ static void fts5SecureDeleteOverflow(
|
||||
|
||||
i1 += fts5GetVarint32(&aPg[i1], iFirst);
|
||||
if( iFirst<iNext ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ROWID(p, iRowid);
|
||||
break;
|
||||
}
|
||||
aIdx = sqlite3Fts5MallocZero(&p->rc, (pLeaf->nn-pLeaf->szLeaf)+2);
|
||||
@@ -5378,14 +5416,14 @@ static void fts5DoSecureDelete(
|
||||
nSuffix = (nPrefix2 + nSuffix2) - nPrefix;
|
||||
|
||||
if( (iKeyOff+nSuffix)>iPgIdx || (iNextOff+nSuffix2)>iPgIdx ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_IDX(p);
|
||||
}else{
|
||||
if( iKey!=1 ){
|
||||
iOff += sqlite3Fts5PutVarint(&aPg[iOff], nPrefix);
|
||||
}
|
||||
iOff += sqlite3Fts5PutVarint(&aPg[iOff], nSuffix);
|
||||
if( nPrefix2>pSeg->term.n ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_IDX(p);
|
||||
}else if( nPrefix2>nPrefix ){
|
||||
memcpy(&aPg[iOff], &pSeg->term.p[nPrefix], nPrefix2-nPrefix);
|
||||
iOff += (nPrefix2-nPrefix);
|
||||
@@ -5809,7 +5847,7 @@ static Fts5Structure *fts5IndexOptimizeStruct(
|
||||
}
|
||||
|
||||
nByte += (((i64)pStruct->nLevel)+1) * sizeof(Fts5StructureLevel);
|
||||
assert( nByte==SZ_FTS5STRUCTURE(pStruct->nLevel+2) );
|
||||
assert( nByte==(i64)SZ_FTS5STRUCTURE(pStruct->nLevel+2) );
|
||||
pNew = (Fts5Structure*)sqlite3Fts5MallocZero(&p->rc, nByte);
|
||||
|
||||
if( pNew ){
|
||||
@@ -6178,7 +6216,7 @@ static void fts5MergePrefixLists(
|
||||
}
|
||||
|
||||
if( pHead==0 || pHead->pNext==0 ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_IDX(p);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -6215,7 +6253,7 @@ static void fts5MergePrefixLists(
|
||||
assert_nc( tmp.n+nTail<=nTmp );
|
||||
assert( tmp.n+nTail<=nTmp+nMerge*10 );
|
||||
if( tmp.n+nTail>nTmp-FTS5_DATA_ZERO_PADDING ){
|
||||
if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT;
|
||||
if( p->rc==SQLITE_OK ) FTS5_CORRUPT_IDX(p);
|
||||
break;
|
||||
}
|
||||
fts5BufferSafeAppendVarint(&out, (tmp.n+nTail) * 2);
|
||||
@@ -8344,14 +8382,17 @@ static void fts5IndexIntegrityCheckEmpty(
|
||||
for(i=iFirst; p->rc==SQLITE_OK && i<=iLast; i++){
|
||||
Fts5Data *pLeaf = fts5DataRead(p, FTS5_SEGMENT_ROWID(pSeg->iSegid, i));
|
||||
if( pLeaf ){
|
||||
if( !fts5LeafIsTermless(pLeaf) ) p->rc = FTS5_CORRUPT;
|
||||
if( i>=iNoRowid && 0!=fts5LeafFirstRowidOff(pLeaf) ) p->rc = FTS5_CORRUPT;
|
||||
if( !fts5LeafIsTermless(pLeaf)
|
||||
|| (i>=iNoRowid && 0!=fts5LeafFirstRowidOff(pLeaf))
|
||||
){
|
||||
FTS5_CORRUPT_ROWID(p, FTS5_SEGMENT_ROWID(pSeg->iSegid, i));
|
||||
}
|
||||
}
|
||||
fts5DataRelease(pLeaf);
|
||||
}
|
||||
}
|
||||
|
||||
static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){
|
||||
static void fts5IntegrityCheckPgidx(Fts5Index *p, i64 iRowid, Fts5Data *pLeaf){
|
||||
i64 iTermOff = 0;
|
||||
int ii;
|
||||
|
||||
@@ -8369,12 +8410,12 @@ static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){
|
||||
iOff = iTermOff;
|
||||
|
||||
if( iOff>=pLeaf->szLeaf ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ROWID(p, iRowid);
|
||||
}else if( iTermOff==nIncr ){
|
||||
int nByte;
|
||||
iOff += fts5GetVarint32(&pLeaf->p[iOff], nByte);
|
||||
if( (iOff+nByte)>pLeaf->szLeaf ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ROWID(p, iRowid);
|
||||
}else{
|
||||
fts5BufferSet(&p->rc, &buf1, nByte, &pLeaf->p[iOff]);
|
||||
}
|
||||
@@ -8383,7 +8424,7 @@ static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){
|
||||
iOff += fts5GetVarint32(&pLeaf->p[iOff], nKeep);
|
||||
iOff += fts5GetVarint32(&pLeaf->p[iOff], nByte);
|
||||
if( nKeep>buf1.n || (iOff+nByte)>pLeaf->szLeaf ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ROWID(p, iRowid);
|
||||
}else{
|
||||
buf1.n = nKeep;
|
||||
fts5BufferAppendBlob(&p->rc, &buf1, nByte, &pLeaf->p[iOff]);
|
||||
@@ -8391,7 +8432,7 @@ static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){
|
||||
|
||||
if( p->rc==SQLITE_OK ){
|
||||
res = fts5BufferCompare(&buf1, &buf2);
|
||||
if( res<=0 ) p->rc = FTS5_CORRUPT;
|
||||
if( res<=0 ) FTS5_CORRUPT_ROWID(p, iRowid);
|
||||
}
|
||||
}
|
||||
fts5BufferSet(&p->rc, &buf2, buf1.n, buf1.p);
|
||||
@@ -8452,7 +8493,7 @@ static void fts5IndexIntegrityCheckSegment(
|
||||
** entry even if all the terms are removed from it by secure-delete
|
||||
** operations. */
|
||||
}else{
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ROWID(p, iRow);
|
||||
}
|
||||
|
||||
}else{
|
||||
@@ -8464,15 +8505,15 @@ static void fts5IndexIntegrityCheckSegment(
|
||||
iOff = fts5LeafFirstTermOff(pLeaf);
|
||||
iRowidOff = fts5LeafFirstRowidOff(pLeaf);
|
||||
if( iRowidOff>=iOff || iOff>=pLeaf->szLeaf ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ROWID(p, iRow);
|
||||
}else{
|
||||
iOff += fts5GetVarint32(&pLeaf->p[iOff], nTerm);
|
||||
res = fts5Memcmp(&pLeaf->p[iOff], zIdxTerm, MIN(nTerm, nIdxTerm));
|
||||
if( res==0 ) res = nTerm - nIdxTerm;
|
||||
if( res<0 ) p->rc = FTS5_CORRUPT;
|
||||
if( res<0 ) FTS5_CORRUPT_ROWID(p, iRow);
|
||||
}
|
||||
|
||||
fts5IntegrityCheckPgidx(p, pLeaf);
|
||||
fts5IntegrityCheckPgidx(p, iRow, pLeaf);
|
||||
}
|
||||
fts5DataRelease(pLeaf);
|
||||
if( p->rc ) break;
|
||||
@@ -8502,7 +8543,7 @@ static void fts5IndexIntegrityCheckSegment(
|
||||
iKey = FTS5_SEGMENT_ROWID(iSegid, iPg);
|
||||
pLeaf = fts5DataRead(p, iKey);
|
||||
if( pLeaf ){
|
||||
if( fts5LeafFirstRowidOff(pLeaf)!=0 ) p->rc = FTS5_CORRUPT;
|
||||
if( fts5LeafFirstRowidOff(pLeaf)!=0 ) FTS5_CORRUPT_ROWID(p, iKey);
|
||||
fts5DataRelease(pLeaf);
|
||||
}
|
||||
}
|
||||
@@ -8517,12 +8558,12 @@ static void fts5IndexIntegrityCheckSegment(
|
||||
int iRowidOff = fts5LeafFirstRowidOff(pLeaf);
|
||||
ASSERT_SZLEAF_OK(pLeaf);
|
||||
if( iRowidOff>=pLeaf->szLeaf ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ROWID(p, iKey);
|
||||
}else if( bSecureDelete==0 || iRowidOff>0 ){
|
||||
i64 iDlRowid = fts5DlidxIterRowid(pDlidx);
|
||||
fts5GetVarint(&pLeaf->p[iRowidOff], (u64*)&iRowid);
|
||||
if( iRowid<iDlRowid || (bSecureDelete==0 && iRowid!=iDlRowid) ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ROWID(p, iKey);
|
||||
}
|
||||
}
|
||||
fts5DataRelease(pLeaf);
|
||||
@@ -8637,7 +8678,12 @@ int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCksum){
|
||||
fts5TestTerm(p, &term, 0, 0, cksum2, &cksum3);
|
||||
|
||||
fts5MultiIterFree(pIter);
|
||||
if( p->rc==SQLITE_OK && bUseCksum && cksum!=cksum2 ) p->rc = FTS5_CORRUPT;
|
||||
if( p->rc==SQLITE_OK && bUseCksum && cksum!=cksum2 ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
sqlite3Fts5ConfigErrmsg(p->pConfig,
|
||||
"fts5: checksum mismatch for table \"%s\"", p->pConfig->zName
|
||||
);
|
||||
}
|
||||
|
||||
fts5StructureRelease(pStruct);
|
||||
#ifdef SQLITE_DEBUG
|
||||
|
||||
@@ -3701,8 +3701,9 @@ static int fts5IntegrityMethod(
|
||||
" FTS5 table %s.%s: %s",
|
||||
zSchema, zTabname, sqlite3_errstr(rc));
|
||||
}
|
||||
}else if( (rc&0xff)==SQLITE_CORRUPT ){
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
sqlite3Fts5IndexCloseReader(pTab->p.pIndex);
|
||||
pTab->p.pConfig->pzErrmsg = 0;
|
||||
|
||||
|
||||
@@ -428,7 +428,7 @@ do_execsql_test 15.1 {
|
||||
}
|
||||
do_catchsql_test 15.2 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
} {1 {database disk image is malformed}}
|
||||
} {1 {fts5: checksum mismatch for table "t1"}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
@@ -47,11 +47,10 @@ do_test 1.3 {
|
||||
DELETE FROM t1_data WHERE rowid = fts5_rowid('segment', $segid, 4);
|
||||
}
|
||||
catchsql { INSERT INTO t1(t1) VALUES('integrity-check') }
|
||||
} {1 {database disk image is malformed}}
|
||||
} {1 {fts5: corruption found reading blob 137438953476 from table "t1"}}
|
||||
do_execsql_test 1.3b {
|
||||
PRAGMA integrity_check(t1);
|
||||
} {{malformed inverted index for FTS5 table main.t1}}
|
||||
|
||||
} {{fts5: corruption found reading blob 137438953476 from table "t1"}}
|
||||
|
||||
do_test 1.4 {
|
||||
db_restore_and_reopen
|
||||
@@ -61,7 +60,7 @@ do_test 1.4 {
|
||||
rowid = fts5_rowid('segment', $segid, 4);
|
||||
}
|
||||
catchsql { INSERT INTO t1(t1) VALUES('integrity-check') }
|
||||
} {1 {database disk image is malformed}}
|
||||
} {1 {fts5: corruption found reading blob 137438953476 from table "t1"}}
|
||||
|
||||
db_restore_and_reopen
|
||||
#db eval {SELECT rowid, fts5_decode(rowid, block) aS r FROM t1_data} {puts $r}
|
||||
|
||||
@@ -109,12 +109,12 @@ for {set i [expr $nbyte-2]} {$i>=0} {incr i -1} {
|
||||
|
||||
do_catchsql_test 2.$i.2 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
} {1 {database disk image is malformed}}
|
||||
} {/1.*fts5: corruption.*/}
|
||||
|
||||
do_test 2.$i.3 {
|
||||
set res [catchsql {SELECT rowid FROM t1 WHERE t1 MATCH 'x*'}]
|
||||
expr {
|
||||
$res=="1 {database disk image is malformed}"
|
||||
[string match {*fts5: corruption*} $res]
|
||||
|| $res=="0 {$all}"
|
||||
}
|
||||
} 1
|
||||
@@ -160,17 +160,17 @@ foreach {tn hdr} {
|
||||
close $fd
|
||||
|
||||
set res [catchsql {SELECT rowid FROM x3 WHERE x3 MATCH 'x AND a'}]
|
||||
if {$res == "1 {database disk image is malformed}"} {incr nCorrupt}
|
||||
if {[string match {*fts5: corruption*} $res]} {incr nCorrupt}
|
||||
set {} 1
|
||||
} {1}
|
||||
|
||||
if {($tn2 % 10)==0 && $existing != $hdr} {
|
||||
do_test 3.$tn.$tn2.2 {
|
||||
catchsql { INSERT INTO x3(x3) VALUES('integrity-check') }
|
||||
} {1 {database disk image is malformed}}
|
||||
} {/.*fts5: corruption.*/}
|
||||
do_execsql_test 3.$tn.$tn2.3 {
|
||||
PRAGMA integrity_check(x3);
|
||||
} {{malformed inverted index for FTS5 table main.x3}}
|
||||
} {/.*fts5: corruption.*/}
|
||||
}
|
||||
|
||||
execsql ROLLBACK
|
||||
@@ -209,7 +209,7 @@ foreach {tn nCut} {
|
||||
set res [catchsql {
|
||||
SELECT rowid FROM x4 WHERE x4 MATCH 'a' ORDER BY 1 DESC
|
||||
}]
|
||||
if {$res == "1 {database disk image is malformed}"} {incr nCorrupt}
|
||||
if {[string match {*fts5: corruption*} $res]} {incr nCorrupt}
|
||||
set {} 1
|
||||
} {1}
|
||||
|
||||
|
||||
+252
-40
File diff suppressed because one or more lines are too long
@@ -237,7 +237,7 @@ do_test 1.0 {
|
||||
|
||||
do_catchsql_test 1.1 {
|
||||
SELECT * FROM t1('R*') WHERE (a,b)<=(current_date,0) ORDER BY rowid DESC;
|
||||
} {1 {database disk image is malformed}}
|
||||
} {/.*fts5: corrupt.*/}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@@ -450,7 +450,7 @@ do_test 2.0 {
|
||||
|
||||
do_catchsql_test 2.1 {
|
||||
SELECT * FROM t1('R*R*R*R*') WHERE (a,b)<=(current_date,0) ORDER BY rowid DESC;
|
||||
} {1 {database disk image is malformed}}
|
||||
} {/.*fts5: corrupt.*/}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
@@ -569,7 +569,7 @@ do_test 3.0 {
|
||||
|
||||
do_catchsql_test 3.1 {
|
||||
UPDATE t1 SET b=quote(zeroblob(200)) WHERE a MATCH 'thra*T';
|
||||
} {1 {database disk image is malformed}}
|
||||
} {/.*fts5: corrupt.*/}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
@@ -878,7 +878,7 @@ do_execsql_test 5.1 {
|
||||
}
|
||||
do_catchsql_test 5.4 {
|
||||
UPDATE t1 SET content=randomblob(500);
|
||||
} {1 {database disk image is malformed}}
|
||||
} {/.*fts5: corrupt.*/}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
@@ -123,6 +123,6 @@ do_execsql_test 2.2 {
|
||||
|
||||
do_catchsql_test 2.3 {
|
||||
DELETE FROM t1 WHERE rowid = 1
|
||||
} {1 {database disk image is malformed}}
|
||||
} {/.*fts5: corrupt.*/}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -32,7 +32,7 @@ sqlite3 db test.db
|
||||
|
||||
do_catchsql_test 1.2 {
|
||||
SELECT * FROM t1
|
||||
} {1 {database disk image is malformed}}
|
||||
} {1 {fts5: corrupt structure record for table "t1"}}
|
||||
do_catchsql_test 1.3 {
|
||||
DROP TABLE t1
|
||||
} {0 {}}
|
||||
@@ -90,58 +90,5 @@ do_execsql_test 3.7 {
|
||||
SELECT * FROM sqlite_schema
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
proc hex_to_blob {hex} {
|
||||
binary encode hex $hex
|
||||
}
|
||||
db func hex_to_blob hex_to_blob
|
||||
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE x1 USING fts5(x, content='', contentless_delete=1);
|
||||
BEGIN;
|
||||
INSERT INTO x1(rowid, x) VALUES(1, 'a b c d e f g h');
|
||||
INSERT INTO x1(rowid, x) VALUES(2, 'a b c d e f g h');
|
||||
COMMIT;
|
||||
DELETE FROM x1 WHERE rowid=1;
|
||||
}
|
||||
|
||||
do_execsql_test 4.1 {
|
||||
SELECT hex(block) FROM x1_data WHERE id=10
|
||||
} {
|
||||
00000000FF00000101010200010101010101010102
|
||||
}
|
||||
|
||||
do_execsql_test 4.2.1 {
|
||||
UPDATE x1_data SET block=
|
||||
X'00000000FF00000101010200010101010101819C9B95A8000102'
|
||||
WHERE id=10;
|
||||
}
|
||||
|
||||
do_catchsql_test 4.2.2 {
|
||||
SELECT * FROM x1('c d e');
|
||||
} {1 {out of memory}}
|
||||
|
||||
do_execsql_test 4.3.1 {
|
||||
UPDATE x1_data SET block=
|
||||
X'00000000FF000001010102000101010101019282AFF9A0000102'
|
||||
WHERE id=10;
|
||||
}
|
||||
|
||||
do_catchsql_test 4.3.2 {
|
||||
SELECT * FROM x1('c d e');
|
||||
} {1 {out of memory}}
|
||||
|
||||
do_execsql_test 4.4.1 {
|
||||
UPDATE x1_data SET block=
|
||||
X'00000000FF000001010102000101010101018181808080130102'
|
||||
WHERE id=10;
|
||||
}
|
||||
|
||||
do_catchsql_test 4.3.2 {
|
||||
SELECT * FROM x1('c d e');
|
||||
} {1 {out of memory}}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ do_execsql_test 1.5 {
|
||||
|
||||
do_catchsql_test 1.6 {
|
||||
INSERT INTO f1(f1) VALUES('integrity-check');
|
||||
} {1 {database disk image is malformed}}
|
||||
} {/.*fts5: corrupt.*/}
|
||||
|
||||
do_execsql_test 1.7 {
|
||||
INSERT INTO f1(f1) VALUES('rebuild');
|
||||
|
||||
@@ -120,7 +120,7 @@ public abstract class AggregateFunction<T> implements SQLFunction {
|
||||
argument, the context is set to the given initial value. On all other
|
||||
calls, the 2nd argument is ignored.
|
||||
|
||||
@see SQLFunction.PerContextState#getAggregateState
|
||||
@see AggregateFunction.PerContextState#getAggregateState
|
||||
*/
|
||||
protected final ValueHolder<T> getAggregateState(sqlite3_context cx, T initialValue){
|
||||
return map.getAggregateState(cx, initialValue);
|
||||
@@ -130,7 +130,7 @@ public abstract class AggregateFunction<T> implements SQLFunction {
|
||||
To be called from the implementation's xFinal() method to fetch
|
||||
the final state of the UDF and remove its mapping.
|
||||
|
||||
see SQLFunction.PerContextState#takeAggregateState
|
||||
see AggregateFunction.PerContextState#takeAggregateState
|
||||
*/
|
||||
protected final T takeAggregateState(sqlite3_context cx){
|
||||
return map.takeAggregateState(cx);
|
||||
|
||||
+92
-40
@@ -67,6 +67,7 @@
|
||||
** data: For a regular file, a blob containing the file data. For a
|
||||
** symlink, a text value containing the text of the link. For a
|
||||
** directory, NULL.
|
||||
** level: Directory hierarchy level. Topmost is 1.
|
||||
**
|
||||
** If a non-NULL value is specified for the optional $dir parameter and
|
||||
** $path is a relative path, then $path is interpreted relative to $dir.
|
||||
@@ -94,11 +95,8 @@ SQLITE_EXTENSION_INIT1
|
||||
# include <sys/time.h>
|
||||
# define STRUCT_STAT struct stat
|
||||
#else
|
||||
# include "windows.h"
|
||||
# include <io.h>
|
||||
# include "windirent.h"
|
||||
# include <direct.h>
|
||||
# include "test_windirent.h"
|
||||
# define dirent DIRENT
|
||||
# define STRUCT_STAT struct _stat
|
||||
# define chmod(path,mode) fileio_chmod(path,mode)
|
||||
# define mkdir(path,mode) fileio_mkdir(path)
|
||||
@@ -117,14 +115,16 @@ SQLITE_EXTENSION_INIT1
|
||||
/*
|
||||
** Structure of the fsdir() table-valued function
|
||||
*/
|
||||
/* 0 1 2 3 4 5 */
|
||||
#define FSDIR_SCHEMA "(name,mode,mtime,data,path HIDDEN,dir HIDDEN)"
|
||||
/* 0 1 2 3 4 5 6 */
|
||||
#define FSDIR_SCHEMA "(name,mode,mtime,data,level,path HIDDEN,dir HIDDEN)"
|
||||
|
||||
#define FSDIR_COLUMN_NAME 0 /* Name of the file */
|
||||
#define FSDIR_COLUMN_MODE 1 /* Access mode */
|
||||
#define FSDIR_COLUMN_MTIME 2 /* Last modification time */
|
||||
#define FSDIR_COLUMN_DATA 3 /* File content */
|
||||
#define FSDIR_COLUMN_PATH 4 /* Path to top of search */
|
||||
#define FSDIR_COLUMN_DIR 5 /* Path is relative to this directory */
|
||||
#define FSDIR_COLUMN_LEVEL 4 /* Level. Topmost is 1 */
|
||||
#define FSDIR_COLUMN_PATH 5 /* Path to top of search */
|
||||
#define FSDIR_COLUMN_DIR 6 /* Path is relative to this directory */
|
||||
|
||||
/*
|
||||
** UTF8 chmod() function for Windows
|
||||
@@ -621,6 +621,7 @@ struct fsdir_cursor {
|
||||
sqlite3_vtab_cursor base; /* Base class - must be first */
|
||||
|
||||
int nLvl; /* Number of entries in aLvl[] array */
|
||||
int mxLvl; /* Maximum level */
|
||||
int iLvl; /* Index of current entry */
|
||||
FsdirLevel *aLvl; /* Hierarchy of directories being traversed */
|
||||
|
||||
@@ -739,7 +740,7 @@ static int fsdirNext(sqlite3_vtab_cursor *cur){
|
||||
mode_t m = pCur->sStat.st_mode;
|
||||
|
||||
pCur->iRowid++;
|
||||
if( S_ISDIR(m) ){
|
||||
if( S_ISDIR(m) && pCur->iLvl+3<pCur->mxLvl ){
|
||||
/* Descend into this directory */
|
||||
int iNew = pCur->iLvl + 1;
|
||||
FsdirLevel *pLvl;
|
||||
@@ -847,7 +848,11 @@ static int fsdirColumn(
|
||||
}else{
|
||||
readFileContents(ctx, pCur->zPath);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FSDIR_COLUMN_LEVEL:
|
||||
sqlite3_result_int(ctx, pCur->iLvl+2);
|
||||
break;
|
||||
case FSDIR_COLUMN_PATH:
|
||||
default: {
|
||||
/* The FSDIR_COLUMN_PATH and FSDIR_COLUMN_DIR are input parameters.
|
||||
@@ -881,8 +886,11 @@ static int fsdirEof(sqlite3_vtab_cursor *cur){
|
||||
/*
|
||||
** xFilter callback.
|
||||
**
|
||||
** idxNum==1 PATH parameter only
|
||||
** idxNum==2 Both PATH and DIR supplied
|
||||
** idxNum bit Meaning
|
||||
** 0x01 PATH=N
|
||||
** 0x02 DIR=N
|
||||
** 0x04 LEVEL<N
|
||||
** 0x08 LEVEL<=N
|
||||
*/
|
||||
static int fsdirFilter(
|
||||
sqlite3_vtab_cursor *cur,
|
||||
@@ -891,6 +899,7 @@ static int fsdirFilter(
|
||||
){
|
||||
const char *zDir = 0;
|
||||
fsdir_cursor *pCur = (fsdir_cursor*)cur;
|
||||
int i;
|
||||
(void)idxStr;
|
||||
fsdirResetCursor(pCur);
|
||||
|
||||
@@ -899,14 +908,24 @@ static int fsdirFilter(
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
assert( argc==idxNum && (argc==1 || argc==2) );
|
||||
assert( (idxNum & 0x01)!=0 && argc>0 );
|
||||
zDir = (const char*)sqlite3_value_text(argv[0]);
|
||||
if( zDir==0 ){
|
||||
fsdirSetErrmsg(pCur, "table function fsdir requires a non-NULL argument");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
if( argc==2 ){
|
||||
pCur->zBase = (const char*)sqlite3_value_text(argv[1]);
|
||||
i = 1;
|
||||
if( (idxNum & 0x02)!=0 ){
|
||||
assert( argc>i );
|
||||
pCur->zBase = (const char*)sqlite3_value_text(argv[i++]);
|
||||
}
|
||||
if( (idxNum & 0x0c)!=0 ){
|
||||
assert( argc>i );
|
||||
pCur->mxLvl = sqlite3_value_int(argv[i++]);
|
||||
if( idxNum & 0x08 ) pCur->mxLvl++;
|
||||
if( pCur->mxLvl<=0 ) pCur->mxLvl = 1000000000;
|
||||
}else{
|
||||
pCur->mxLvl = 1000000000;
|
||||
}
|
||||
if( pCur->zBase ){
|
||||
pCur->nBase = (int)strlen(pCur->zBase)+1;
|
||||
@@ -935,10 +954,11 @@ static int fsdirFilter(
|
||||
** In this implementation idxNum is used to represent the
|
||||
** query plan. idxStr is unused.
|
||||
**
|
||||
** The query plan is represented by values of idxNum:
|
||||
** The query plan is represented by bits in idxNum:
|
||||
**
|
||||
** (1) The path value is supplied by argv[0]
|
||||
** (2) Path is in argv[0] and dir is in argv[1]
|
||||
** 0x01 The path value is supplied by argv[0]
|
||||
** 0x02 dir is in argv[1]
|
||||
** 0x04 maxdepth is in argv[1] or [2]
|
||||
*/
|
||||
static int fsdirBestIndex(
|
||||
sqlite3_vtab *tab,
|
||||
@@ -947,6 +967,9 @@ static int fsdirBestIndex(
|
||||
int i; /* Loop over constraints */
|
||||
int idxPath = -1; /* Index in pIdxInfo->aConstraint of PATH= */
|
||||
int idxDir = -1; /* Index in pIdxInfo->aConstraint of DIR= */
|
||||
int idxLevel = -1; /* Index in pIdxInfo->aConstraint of LEVEL< or <= */
|
||||
int idxLevelEQ = 0; /* 0x08 for LEVEL<= or LEVEL=. 0x04 for LEVEL< */
|
||||
int omitLevel = 0; /* omit the LEVEL constraint */
|
||||
int seenPath = 0; /* True if an unusable PATH= constraint is seen */
|
||||
int seenDir = 0; /* True if an unusable DIR= constraint is seen */
|
||||
const struct sqlite3_index_constraint *pConstraint;
|
||||
@@ -954,25 +977,48 @@ static int fsdirBestIndex(
|
||||
(void)tab;
|
||||
pConstraint = pIdxInfo->aConstraint;
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
||||
if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
|
||||
switch( pConstraint->iColumn ){
|
||||
case FSDIR_COLUMN_PATH: {
|
||||
if( pConstraint->usable ){
|
||||
idxPath = i;
|
||||
seenPath = 0;
|
||||
}else if( idxPath<0 ){
|
||||
seenPath = 1;
|
||||
if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
|
||||
switch( pConstraint->iColumn ){
|
||||
case FSDIR_COLUMN_PATH: {
|
||||
if( pConstraint->usable ){
|
||||
idxPath = i;
|
||||
seenPath = 0;
|
||||
}else if( idxPath<0 ){
|
||||
seenPath = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FSDIR_COLUMN_DIR: {
|
||||
if( pConstraint->usable ){
|
||||
idxDir = i;
|
||||
seenDir = 0;
|
||||
}else if( idxDir<0 ){
|
||||
seenDir = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FSDIR_COLUMN_LEVEL: {
|
||||
if( pConstraint->usable && idxLevel<0 ){
|
||||
idxLevel = i;
|
||||
idxLevelEQ = 0x08;
|
||||
omitLevel = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FSDIR_COLUMN_DIR: {
|
||||
if( pConstraint->usable ){
|
||||
idxDir = i;
|
||||
seenDir = 0;
|
||||
}else if( idxDir<0 ){
|
||||
seenDir = 1;
|
||||
}
|
||||
break;
|
||||
}else
|
||||
if( pConstraint->iColumn==FSDIR_COLUMN_LEVEL
|
||||
&& pConstraint->usable
|
||||
&& idxLevel<0
|
||||
){
|
||||
if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE ){
|
||||
idxLevel = i;
|
||||
idxLevelEQ = 0x08;
|
||||
omitLevel = 1;
|
||||
}else if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ){
|
||||
idxLevel = i;
|
||||
idxLevelEQ = 0x04;
|
||||
omitLevel = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -989,14 +1035,20 @@ static int fsdirBestIndex(
|
||||
}else{
|
||||
pIdxInfo->aConstraintUsage[idxPath].omit = 1;
|
||||
pIdxInfo->aConstraintUsage[idxPath].argvIndex = 1;
|
||||
pIdxInfo->idxNum = 0x01;
|
||||
pIdxInfo->estimatedCost = 1.0e9;
|
||||
i = 2;
|
||||
if( idxDir>=0 ){
|
||||
pIdxInfo->aConstraintUsage[idxDir].omit = 1;
|
||||
pIdxInfo->aConstraintUsage[idxDir].argvIndex = 2;
|
||||
pIdxInfo->idxNum = 2;
|
||||
pIdxInfo->estimatedCost = 10.0;
|
||||
}else{
|
||||
pIdxInfo->idxNum = 1;
|
||||
pIdxInfo->estimatedCost = 100.0;
|
||||
pIdxInfo->aConstraintUsage[idxDir].argvIndex = i++;
|
||||
pIdxInfo->idxNum |= 0x02;
|
||||
pIdxInfo->estimatedCost /= 1.0e4;
|
||||
}
|
||||
if( idxLevel>=0 ){
|
||||
pIdxInfo->aConstraintUsage[idxLevel].omit = omitLevel;
|
||||
pIdxInfo->aConstraintUsage[idxLevel].argvIndex = i++;
|
||||
pIdxInfo->idxNum |= idxLevelEQ;
|
||||
pIdxInfo->estimatedCost /= 1.0e4;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+45
-3
@@ -435,6 +435,39 @@ static int vtablogFilter(
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return an sqlite3_index_info operator name in static space.
|
||||
** The name is possibly overwritten on subsequent calls.
|
||||
*/
|
||||
static char *vtablogOpName(unsigned char op){
|
||||
static char zUnknown[30];
|
||||
char *zOut;
|
||||
switch( op ){
|
||||
case SQLITE_INDEX_CONSTRAINT_EQ: zOut = "EQ"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_GT: zOut = "GT"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_LE: zOut = "LE"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_LT: zOut = "LT"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_GE: zOut = "GE"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_MATCH: zOut = "MATCH"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_LIKE: zOut = "LIKE"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_GLOB: zOut = "GLOB"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_REGEXP: zOut = "REGEXP"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_NE: zOut = "NE"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_ISNOT: zOut = "ISNOT"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_ISNOTNULL: zOut = "ISNOTNULL"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_ISNULL: zOut = "ISNULL"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_IS: zOut = "IS"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_LIMIT: zOut = "LIMIT"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_OFFSET: zOut = "OFFSET"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_FUNCTION: zOut = "FUNCTION"; break;
|
||||
default:
|
||||
sqlite3_snprintf(sizeof(zUnknown),zUnknown,"%d",op);
|
||||
zOut = zUnknown;
|
||||
break;
|
||||
}
|
||||
return zOut;
|
||||
}
|
||||
|
||||
/*
|
||||
** SQLite will invoke this method one or more times while planning a query
|
||||
** that uses the vtablog virtual table. This routine needs to create
|
||||
@@ -451,14 +484,23 @@ static int vtablogBestIndex(
|
||||
printf(" colUsed: 0x%016llx\n", p->colUsed);
|
||||
printf(" nConstraint: %d\n", p->nConstraint);
|
||||
for(i=0; i<p->nConstraint; i++){
|
||||
sqlite3_value *pVal = 0;
|
||||
int rc = sqlite3_vtab_rhs_value(p, i, &pVal);
|
||||
printf(
|
||||
" constraint[%d]: col=%d termid=%d op=%d usabled=%d collseq=%s\n",
|
||||
" constraint[%d]: col=%d termid=%d op=%s usabled=%d coll=%s rhs=",
|
||||
i,
|
||||
p->aConstraint[i].iColumn,
|
||||
p->aConstraint[i].iTermOffset,
|
||||
p->aConstraint[i].op,
|
||||
vtablogOpName(p->aConstraint[i].op),
|
||||
p->aConstraint[i].usable,
|
||||
sqlite3_vtab_collation(p,i));
|
||||
sqlite3_vtab_collation(p,i)
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
vtablogQuote(pVal);
|
||||
printf("\n");
|
||||
}else{
|
||||
printf("N/A\n");
|
||||
}
|
||||
}
|
||||
printf(" nOrderBy: %d\n", p->nOrderBy);
|
||||
for(i=0; i<p->nOrderBy; i++){
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
** 2025-06-05
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** An implementation of opendir(), readdir(), and closedir() for Windows,
|
||||
** based on the FindFirstFile(), FindNextFile(), and FindClose() APIs
|
||||
** of Win32.
|
||||
**
|
||||
** #include this file inside any C-code module that needs to use
|
||||
** opendir()/readdir()/closedir(). This file is a no-op on non-Windows
|
||||
** machines. On Windows, static functions are defined that implement
|
||||
** those standard interfaces.
|
||||
*/
|
||||
#if defined(_WIN32) && defined(_MSC_VER) && !defined(SQLITE_WINDIRENT_H)
|
||||
#define SQLITE_WINDIRENT_H
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
#ifndef FILENAME_MAX
|
||||
# define FILENAME_MAX (260)
|
||||
#endif
|
||||
#ifndef S_ISREG
|
||||
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
|
||||
#endif
|
||||
#ifndef S_ISDIR
|
||||
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
|
||||
#endif
|
||||
#ifndef S_ISLNK
|
||||
#define S_ISLNK(m) (0)
|
||||
#endif
|
||||
typedef unsigned short mode_t;
|
||||
|
||||
/* The dirent object for Windows is abbreviated. The only field really
|
||||
** usable by applications is d_name[].
|
||||
*/
|
||||
struct dirent {
|
||||
int d_ino; /* Inode number (synthesized) */
|
||||
unsigned d_attributes; /* File attributes */
|
||||
char d_name[FILENAME_MAX]; /* Null-terminated filename */
|
||||
};
|
||||
|
||||
/* The internals of DIR are opaque according to standards. So it
|
||||
** does not matter what we put here. */
|
||||
typedef struct DIR DIR;
|
||||
struct DIR {
|
||||
intptr_t d_handle; /* Handle for findfirst()/findnext() */
|
||||
struct dirent cur; /* Current entry */
|
||||
};
|
||||
|
||||
/* Ignore hidden and system files */
|
||||
#define WindowsFileToIgnore(a) \
|
||||
((((a).attrib)&_A_HIDDEN) || (((a).attrib)&_A_SYSTEM))
|
||||
|
||||
/*
|
||||
** Close a previously opened directory
|
||||
*/
|
||||
static int closedir(DIR *pDir){
|
||||
int rc = 0;
|
||||
if( pDir==0 ){
|
||||
return EINVAL;
|
||||
}
|
||||
if( pDir->d_handle!=0 && pDir->d_handle!=(-1) ){
|
||||
rc = _findclose(pDir->d_handle);
|
||||
}
|
||||
sqlite3_free(pDir);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a new directory. The directory name should be UTF-8 encoded.
|
||||
** appropriate translations happen automatically.
|
||||
*/
|
||||
static DIR *opendir(const char *zDirName){
|
||||
DIR *pDir;
|
||||
wchar_t *b1;
|
||||
sqlite3_int64 sz;
|
||||
struct _wfinddata_t data;
|
||||
|
||||
pDir = sqlite3_malloc64( sizeof(DIR) );
|
||||
if( pDir==0 ) return 0;
|
||||
memset(pDir, 0, sizeof(DIR));
|
||||
memset(&data, 0, sizeof(data));
|
||||
sz = strlen(zDirName);
|
||||
b1 = sqlite3_malloc64( (sz+3)*sizeof(b1[0]) );
|
||||
if( b1==0 ){
|
||||
closedir(pDir);
|
||||
return NULL;
|
||||
}
|
||||
sz = MultiByteToWideChar(CP_UTF8, 0, zDirName, sz, b1, sz);
|
||||
b1[sz++] = '\\';
|
||||
b1[sz++] = '*';
|
||||
b1[sz] = 0;
|
||||
if( sz+1>sizeof(data.name)/sizeof(data.name[0]) ){
|
||||
closedir(pDir);
|
||||
sqlite3_free(b1);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(data.name, b1, (sz+1)*sizeof(b1[0]));
|
||||
sqlite3_free(b1);
|
||||
pDir->d_handle = _wfindfirst(data.name, &data);
|
||||
if( pDir->d_handle<0 ){
|
||||
closedir(pDir);
|
||||
return NULL;
|
||||
}
|
||||
while( WindowsFileToIgnore(data) ){
|
||||
memset(&data, 0, sizeof(data));
|
||||
if( _wfindnext(pDir->d_handle, &data)==-1 ){
|
||||
closedir(pDir);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
pDir->cur.d_ino = 0;
|
||||
pDir->cur.d_attributes = data.attrib;
|
||||
WideCharToMultiByte(CP_UTF8, 0, data.name, -1,
|
||||
pDir->cur.d_name, FILENAME_MAX, 0, 0);
|
||||
return pDir;
|
||||
}
|
||||
|
||||
/*
|
||||
** Read the next entry from a directory.
|
||||
**
|
||||
** The returned struct-dirent object is managed by DIR. It is only
|
||||
** valid until the next readdir() or closedir() call. Only the
|
||||
** d_name[] field is meaningful. The d_name[] value has been
|
||||
** translated into UTF8.
|
||||
*/
|
||||
static struct dirent *readdir(DIR *pDir){
|
||||
struct _wfinddata_t data;
|
||||
if( pDir==0 ) return 0;
|
||||
if( (pDir->cur.d_ino++)==0 ){
|
||||
return &pDir->cur;
|
||||
}
|
||||
do{
|
||||
memset(&data, 0, sizeof(data));
|
||||
if( _wfindnext(pDir->d_handle, &data)==-1 ){
|
||||
return NULL;
|
||||
}
|
||||
}while( WindowsFileToIgnore(data) );
|
||||
pDir->cur.d_attributes = data.attrib;
|
||||
WideCharToMultiByte(CP_UTF8, 0, data.name, -1,
|
||||
pDir->cur.d_name, FILENAME_MAX, 0, 0);
|
||||
return &pDir->cur;
|
||||
}
|
||||
|
||||
#endif /* defined(_WIN32) && defined(_MSC_VER) */
|
||||
+1
-1
@@ -97,7 +97,7 @@ typedef unsigned int u32;
|
||||
# define NEVER(X) (X)
|
||||
#endif
|
||||
#ifndef offsetof
|
||||
#define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
|
||||
# define offsetof(ST,M) ((size_t)((char*)&((ST*)0)->M - (char*)0))
|
||||
#endif
|
||||
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
|
||||
# define FLEXARRAY
|
||||
|
||||
@@ -260,7 +260,8 @@ SQLITE_OPT.full-featured := \
|
||||
-DSQLITE_ENABLE_RTREE \
|
||||
-DSQLITE_ENABLE_SESSION \
|
||||
-DSQLITE_ENABLE_STMTVTAB \
|
||||
-DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
|
||||
-DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION \
|
||||
-DSQLITE_ENABLE_COLUMN_METADATA
|
||||
|
||||
ifeq (0,$(wasm-bare-bones))
|
||||
# The so-called canonical build is full-featured:
|
||||
@@ -598,8 +599,6 @@ emcc.flags += -v
|
||||
endif
|
||||
|
||||
|
||||
# wasmMemory ==> required by our code for use with -sIMPORTED_MEMORY
|
||||
# Emscripten 4.0.7 (2025-04-15) stops exporting HEAP* by default
|
||||
########################################################################
|
||||
# emcc flags for .c/.o.
|
||||
emcc.cflags :=
|
||||
@@ -619,6 +618,8 @@ emcc.jsflags += -sNO_POLYFILL
|
||||
emcc.jsflags += -sEXPORTED_FUNCTIONS=@$(EXPORTED_FUNCTIONS.api)
|
||||
emcc.exportedRuntimeMethods := \
|
||||
-sEXPORTED_RUNTIME_METHODS=wasmMemory,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAP64,HEAPU64
|
||||
# wasmMemory ==> required by our code for use with -sIMPORTED_MEMORY
|
||||
# Emscripten 4.0.7 (2025-04-15) stops exporting HEAP* by default
|
||||
emcc.jsflags += $(emcc.exportedRuntimeMethods)
|
||||
emcc.jsflags += -sUSE_CLOSURE_COMPILER=0
|
||||
emcc.jsflags += -sIMPORTED_MEMORY
|
||||
@@ -1193,11 +1194,9 @@ push-testing:
|
||||
########################################################################
|
||||
# If we find a copy of the sqlite.org/wasm docs checked out, copy
|
||||
# certain files over to it, noting that some need automatable edits...
|
||||
wasm.docs.home ?= $(firstword $(wildcard ../../../wasm $(HOME)/f/s/wasm))
|
||||
wasm.docs.found = $(if \
|
||||
$(wildcard $(wasm.docs.home)/api-index.md),$(wildcard $(wasm.docs.home)),)
|
||||
# ^^^ don't split between the args there, else it can introduce an extra
|
||||
# space.
|
||||
wasm.docs.home ?= ../../../wasm
|
||||
wasm.docs.found = $(if $(wildcard $(wasm.docs.home)/api-index.md),\
|
||||
$(wildcard $(wasm.docs.home)),)
|
||||
.PHONY: update-docs
|
||||
ifeq (,$(wasm.docs.found))
|
||||
update-docs:
|
||||
|
||||
@@ -1,12 +1,27 @@
|
||||
_sqlite3_column_database_name
|
||||
_sqlite3_column_origin_name
|
||||
_sqlite3_column_table_name
|
||||
_sqlite3_create_module
|
||||
_sqlite3_create_module_v2
|
||||
_sqlite3_create_window_function
|
||||
_sqlite3_progress_handler
|
||||
_sqlite3_set_authorizer
|
||||
_sqlite3_declare_vtab
|
||||
_sqlite3_drop_modules
|
||||
_sqlite3_preupdate_blobwrite
|
||||
_sqlite3_preupdate_count
|
||||
_sqlite3_preupdate_depth
|
||||
_sqlite3_preupdate_hook
|
||||
_sqlite3_preupdate_new
|
||||
_sqlite3_preupdate_old
|
||||
_sqlite3_progress_handler
|
||||
_sqlite3_set_authorizer
|
||||
_sqlite3_vtab_collation
|
||||
_sqlite3_vtab_distinct
|
||||
_sqlite3_vtab_in
|
||||
_sqlite3_vtab_in_first
|
||||
_sqlite3_vtab_in_next
|
||||
_sqlite3_vtab_nochange
|
||||
_sqlite3_vtab_on_conflict
|
||||
_sqlite3_vtab_rhs_value
|
||||
_sqlite3changegroup_add
|
||||
_sqlite3changegroup_add_strm
|
||||
_sqlite3changegroup_delete
|
||||
@@ -49,15 +64,3 @@ _sqlite3session_object_config
|
||||
_sqlite3session_patchset
|
||||
_sqlite3session_patchset_strm
|
||||
_sqlite3session_table_filter
|
||||
_sqlite3_create_module
|
||||
_sqlite3_create_module_v2
|
||||
_sqlite3_declare_vtab
|
||||
_sqlite3_drop_modules
|
||||
_sqlite3_vtab_collation
|
||||
_sqlite3_vtab_distinct
|
||||
_sqlite3_vtab_in
|
||||
_sqlite3_vtab_in_first
|
||||
_sqlite3_vtab_in_next
|
||||
_sqlite3_vtab_nochange
|
||||
_sqlite3_vtab_on_conflict
|
||||
_sqlite3_vtab_rhs_value
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
'use strict';
|
||||
const toss = (...args)=>{throw new Error(args.join(' '))};
|
||||
const toss3 = sqlite3.SQLite3Error.toss;
|
||||
const capi = sqlite3.capi, wasm = sqlite3.wasm, util = sqlite3.util;
|
||||
globalThis.WhWasmUtilInstaller(wasm);
|
||||
delete globalThis.WhWasmUtilInstaller;
|
||||
@@ -368,6 +367,14 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
);
|
||||
}/* sqlite3_set_authorizer() */
|
||||
|
||||
if( !!wasm.exports.sqlite3_column_origin_name ){
|
||||
wasm.bindingSignatures.push(
|
||||
["sqlite3_column_database_name","string", "sqlite3_stmt*", "int"],
|
||||
["sqlite3_column_origin_name","string", "sqlite3_stmt*", "int"],
|
||||
["sqlite3_column_table_name","string", "sqlite3_stmt*", "int"]
|
||||
);
|
||||
}
|
||||
|
||||
if(false && wasm.compileOptionUsed('SQLITE_ENABLE_NORMALIZE')){
|
||||
/* ^^^ "the problem" is that this is an optional feature and the
|
||||
build-time function-export list does not currently take
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
and it installs its deliverable as globalThis.sqlite3.oo1.
|
||||
*/
|
||||
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
const toss = (...args)=>{throw new Error(args.join(' '))};
|
||||
const toss3 = (...args)=>{throw new sqlite3.SQLite3Error(...args)};
|
||||
|
||||
const capi = sqlite3.capi, wasm = sqlite3.wasm, util = sqlite3.util;
|
||||
@@ -1061,18 +1060,18 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
const cbArgCache = Object.create(null)
|
||||
/* 2nd arg for arg.cbArg, used by (at least) row-to-object
|
||||
converter */;
|
||||
for(; stmt.step(); stmt._lockedByExec = false){
|
||||
for( ; stmt.step(); __execLock.delete(stmt) ){
|
||||
if(0===gotColNames++){
|
||||
stmt.getColumnNames(cbArgCache.columnNames = (opt.columnNames || []));
|
||||
}
|
||||
stmt._lockedByExec = true;
|
||||
__execLock.add(stmt);
|
||||
const row = arg.cbArg(stmt,cbArgCache);
|
||||
if(resultRows) resultRows.push(row);
|
||||
if(callback && false === callback.call(opt, row, stmt)){
|
||||
break;
|
||||
}
|
||||
}
|
||||
stmt._lockedByExec = false;
|
||||
__execLock.delete(stmt);
|
||||
}
|
||||
if(0===gotColNames){
|
||||
/* opt.columnNames was provided but we visited no result rows */
|
||||
@@ -1094,7 +1093,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
}*/finally{
|
||||
wasm.scopedAllocPop(stack);
|
||||
if(stmt){
|
||||
delete stmt._lockedByExec;
|
||||
__execLock.delete(stmt);
|
||||
stmt.finalize();
|
||||
}
|
||||
}
|
||||
@@ -1388,7 +1387,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
|
||||
/**
|
||||
Starts a transaction, calls the given callback, and then either
|
||||
rolls back or commits the savepoint, depending on whether the
|
||||
rolls back or commits the transaction, depending on whether the
|
||||
callback throws. The callback is passed this db object as its
|
||||
only argument. On success, returns the result of the
|
||||
callback. Throws on error.
|
||||
@@ -1511,7 +1510,30 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
};
|
||||
|
||||
/**
|
||||
If stmt._lockedByExec is truthy, this throws an exception
|
||||
Each Stmt object which is "locked" by DB.exec() gets an entry
|
||||
here to note that "lock".
|
||||
|
||||
The reason this is in place is because exec({callback:...})'s
|
||||
callback gets access to the Stmt objects created internally by
|
||||
exec() but it must not use certain Stmt APIs.
|
||||
*/
|
||||
const __execLock = new Set();
|
||||
/**
|
||||
This is a Stmt.get() counterpart of __execLock. Each time
|
||||
Stmt.step() returns true, the statement is added to this set,
|
||||
indicating that Stmt.get() is legal. Stmt APIs which invalidate
|
||||
that status remove the Stmt object from this set, which will
|
||||
cause Stmt.get() to throw with a descriptive error message
|
||||
instead of a more generic "API misuse" if we were to allow that
|
||||
call to reach the C API.
|
||||
*/
|
||||
const __stmtMayGet = new Set();
|
||||
|
||||
/**
|
||||
Stmt APIs which are prohibited on locked objects must call
|
||||
affirmNotLockedByExec() before doing any work.
|
||||
|
||||
If __execLock.has(stmt) is truthy, this throws an exception
|
||||
complaining that the 2nd argument (an operation name,
|
||||
e.g. "bind()") is not legal while the statement is "locked".
|
||||
Locking happens before an exec()-like callback is passed a
|
||||
@@ -1519,7 +1541,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
finalize the statement. If it does not throw, it returns stmt.
|
||||
*/
|
||||
const affirmNotLockedByExec = function(stmt,currentOpName){
|
||||
if(stmt._lockedByExec){
|
||||
if(__execLock.has(stmt)){
|
||||
toss3("Operation is illegal when statement is locked:",currentOpName);
|
||||
}
|
||||
return stmt;
|
||||
@@ -1604,7 +1626,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
toss3("Unsupported bind() argument type: "+(typeof val));
|
||||
}
|
||||
if(rc) DB.checkRc(stmt.db.pointer, rc);
|
||||
stmt._mayGet = false;
|
||||
return stmt;
|
||||
};
|
||||
|
||||
@@ -1627,9 +1648,9 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
const rc = capi.sqlite3_finalize(this.pointer);
|
||||
delete __stmtMap.get(this.db)[this.pointer];
|
||||
__ptrMap.delete(this);
|
||||
delete this._mayGet;
|
||||
__execLock.delete(this);
|
||||
__stmtMayGet.delete(this);
|
||||
delete this.parameterCount;
|
||||
delete this._lockedByExec;
|
||||
delete this.db;
|
||||
return rc;
|
||||
}
|
||||
@@ -1643,7 +1664,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
clearBindings: function(){
|
||||
affirmNotLockedByExec(affirmStmtOpen(this), 'clearBindings()')
|
||||
capi.sqlite3_clear_bindings(this.pointer);
|
||||
this._mayGet = false;
|
||||
__stmtMayGet.delete(this);
|
||||
return this;
|
||||
},
|
||||
/**
|
||||
@@ -1669,7 +1690,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
affirmNotLockedByExec(this,'reset()');
|
||||
if(alsoClearBinds) this.clearBindings();
|
||||
const rc = capi.sqlite3_reset(affirmStmtOpen(this).pointer);
|
||||
this._mayGet = false;
|
||||
__stmtMayGet.delete(this);
|
||||
checkSqlite3Rc(this.db, rc);
|
||||
return this;
|
||||
},
|
||||
@@ -1756,7 +1777,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
}else if(!this.parameterCount){
|
||||
toss3("This statement has no bindable parameters.");
|
||||
}
|
||||
this._mayGet = false;
|
||||
__stmtMayGet.delete(this);
|
||||
if(null===arg){
|
||||
/* bind NULL */
|
||||
return bindOne(this, ndx, BindTypes.null, arg);
|
||||
@@ -1821,14 +1842,18 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
affirmNotLockedByExec(this, 'step()');
|
||||
const rc = capi.sqlite3_step(affirmStmtOpen(this).pointer);
|
||||
switch(rc){
|
||||
case capi.SQLITE_DONE: return this._mayGet = false;
|
||||
case capi.SQLITE_ROW: return this._mayGet = true;
|
||||
default:
|
||||
this._mayGet = false;
|
||||
sqlite3.config.warn("sqlite3_step() rc=",rc,
|
||||
capi.sqlite3_js_rc_str(rc),
|
||||
"SQL =", capi.sqlite3_sql(this.pointer));
|
||||
DB.checkRc(this.db.pointer, rc);
|
||||
case capi.SQLITE_DONE:
|
||||
__stmtMayGet.delete(this);
|
||||
return false;
|
||||
case capi.SQLITE_ROW:
|
||||
__stmtMayGet.add(this);
|
||||
return true;
|
||||
default:
|
||||
__stmtMayGet.delete(this);
|
||||
sqlite3.config.warn("sqlite3_step() rc=",rc,
|
||||
capi.sqlite3_js_rc_str(rc),
|
||||
"SQL =", capi.sqlite3_sql(this.pointer));
|
||||
DB.checkRc(this.db.pointer, rc);
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -1913,7 +1938,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
getJSON() can be used for that.
|
||||
*/
|
||||
get: function(ndx,asType){
|
||||
if(!affirmStmtOpen(this)._mayGet){
|
||||
if(!__stmtMayGet.has(affirmStmtOpen(this))){
|
||||
toss3("Stmt.step() has not (recently) returned true.");
|
||||
}
|
||||
if(Array.isArray(ndx)){
|
||||
|
||||
@@ -135,9 +135,12 @@
|
||||
|
||||
/*
|
||||
** If SQLITE_WASM_BARE_BONES is defined, undefine most of the ENABLE
|
||||
** macros.
|
||||
** macros. This will, when using the canonical makefile, also elide
|
||||
** any C functions from the WASM exports which are listed in
|
||||
** ./EXPORT_FUNCTIONS.sqlite3-extras.
|
||||
*/
|
||||
#ifdef SQLITE_WASM_BARE_BONES
|
||||
# undef SQLITE_ENABLE_COLUMN_METADATA
|
||||
# undef SQLITE_ENABLE_DBPAGE_VTAB
|
||||
# undef SQLITE_ENABLE_DBSTAT_VTAB
|
||||
# undef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
||||
|
||||
@@ -279,7 +279,7 @@
|
||||
opt.innerHTML = lbl;
|
||||
opt.value = f;
|
||||
if(preselectedFlags.indexOf(f) >= 0) opt.selected = true;
|
||||
});
|
||||
});
|
||||
const cbReverseLog = E('#cb-reverse-log-order');
|
||||
const lblReverseLog = E('#lbl-reverse-log-order');
|
||||
if(cbReverseLog.checked){
|
||||
|
||||
@@ -118,7 +118,7 @@
|
||||
argv.push("--vfs", vfs);
|
||||
log2('',"Using VFS:",vfs);
|
||||
if('kvvfs' === vfs){
|
||||
forceSize = 4 /* 5 uses approx. 4.96mb */;
|
||||
forceSize = 2 /* 5 uses approx. 4.96mb */;
|
||||
dbFile = 'session';
|
||||
log2('warning',"kvvfs VFS: forcing --size",forceSize,
|
||||
"and filename '"+dbFile+"'.");
|
||||
|
||||
+124
-58
@@ -1263,7 +1263,6 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
capi.sqlite3_stmt_status(
|
||||
st, capi.SQLITE_STMTSTATUS_RUN, 0
|
||||
) === 0)
|
||||
.assert(!st._mayGet)
|
||||
.assert('a' === st.getColumnName(0))
|
||||
.mustThrowMatching(()=>st.columnCount=2,
|
||||
/columnCount property is read-only/)
|
||||
@@ -1287,9 +1286,9 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
.assert(1===st.get(0,capi.SQLITE_BLOB).length)
|
||||
.assert(st.getBlob(0) instanceof Uint8Array)
|
||||
.assert('3'.charCodeAt(0) === st.getBlob(0)[0])
|
||||
.assert(st._mayGet)
|
||||
.assert(false===st.step())
|
||||
.assert(!st._mayGet)
|
||||
.mustThrowMatching(()=>st.get(0),
|
||||
"Stmt.step() has not (recently) returned true.")
|
||||
.assert(
|
||||
capi.sqlite3_stmt_status(
|
||||
st, capi.SQLITE_STMTSTATUS_RUN, 0
|
||||
@@ -1297,11 +1296,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
|
||||
T.assert(this.progressHandlerCount>0
|
||||
|| wasm.compileOptionUsed('OMIT_PROGRESS_CALLBACK'),
|
||||
"Expecting progress callback.").
|
||||
assert(0===capi.sqlite3_strglob("*.txt", "foo.txt")).
|
||||
assert(0!==capi.sqlite3_strglob("*.txt", "foo.xtx")).
|
||||
assert(0===capi.sqlite3_strlike("%.txt", "foo.txt", 0)).
|
||||
assert(0!==capi.sqlite3_strlike("%.txt", "foo.xtx", 0));
|
||||
"Expecting progress callback.");
|
||||
}finally{
|
||||
rc = st.finalize();
|
||||
}
|
||||
@@ -1350,7 +1345,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
.assert(pVfsDb > 0)
|
||||
.assert(pVfsMem !== pVfsDflt
|
||||
/* memdb lives on top of the default vfs */)
|
||||
.assert(pVfsDb === pVfsDflt || pVfsdb === pVfsMem)
|
||||
.assert(pVfsDb === pVfsDflt || pVfsDb === pVfsMem)
|
||||
;
|
||||
/*const vMem = new capi.sqlite3_vfs(pVfsMem),
|
||||
vDflt = new capi.sqlite3_vfs(pVfsDflt),
|
||||
@@ -2193,7 +2188,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
"back into JS because of the lack of 64-bit integer support.");
|
||||
}
|
||||
}finally{
|
||||
const x = w.scopedAlloc(1), y = w.scopedAlloc(1), z = w.scopedAlloc(1);
|
||||
//const x = w.scopedAlloc(1), y = w.scopedAlloc(1), z = w.scopedAlloc(1);
|
||||
//log("x=",x,"y=",y,"z=",z); // just looking at the alignment
|
||||
w.scopedAllocPop(stack);
|
||||
}
|
||||
@@ -2673,50 +2668,70 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
|| "Only available in main thread."),
|
||||
test: function(sqlite3){
|
||||
this.kvvfsUnlink();
|
||||
let db;
|
||||
const encOpt1 = 1
|
||||
? {textkey: 'foo'}
|
||||
: {key: 'foo'};
|
||||
const encOpt2 = encOpt1.textkey
|
||||
? encOpt1
|
||||
: {hexkey: new Uint8Array([0x66,0x6f,0x6f]/*==>"foo"*/)}
|
||||
try{
|
||||
db = new this.JDb({
|
||||
filename: this.kvvfsDbFile,
|
||||
...encOpt1
|
||||
});
|
||||
db.exec([
|
||||
"create table t(a,b);",
|
||||
"insert into t(a,b) values(1,2),(3,4)"
|
||||
]);
|
||||
db.close();
|
||||
let err;
|
||||
try{
|
||||
db = new this.JDb({
|
||||
filename: this.kvvfsDbFile,
|
||||
flags: 'ct'
|
||||
let initDb = true;
|
||||
const tryKey = function(keyKey, key, expectCount){
|
||||
let db;
|
||||
//console.debug('tryKey()',arguments);
|
||||
const ctoropt = {
|
||||
filename: this.kvvfsDbFile
|
||||
//vfs: 'kvvfs'
|
||||
//,flags: 'ct'
|
||||
};
|
||||
try {
|
||||
if (initDb) {
|
||||
initDb = false;
|
||||
db = new this.JDb({
|
||||
...ctoropt,
|
||||
[keyKey]: key
|
||||
});
|
||||
db.exec([
|
||||
"drop table if exists t;",
|
||||
"create table t(a);"
|
||||
]);
|
||||
db.close();
|
||||
// Ensure that it's actually encrypted...
|
||||
let err;
|
||||
try {
|
||||
db = new this.JDb(ctoropt);
|
||||
T.assert(db, 'db opened') /* opening is fine, but... */;
|
||||
db.exec("select 1 from sqlite_schema");
|
||||
console.warn("(should not be reached) sessionStorage =", sessionStorage);
|
||||
} catch (e) {
|
||||
err = e;
|
||||
} finally {
|
||||
db.close()
|
||||
}
|
||||
T.assert(err, "Expecting an exception")
|
||||
.assert(sqlite3.capi.SQLITE_NOTADB == err.resultCode,
|
||||
"Expecting NOTADB");
|
||||
}/*initDb*/
|
||||
//console.debug('tryKey()',arguments);
|
||||
db = new sqlite3.oo1.DB({
|
||||
...ctoropt,
|
||||
vfs: 'kvvfs',
|
||||
[keyKey]: key
|
||||
});
|
||||
T.assert(db) /* opening is fine, but... */;
|
||||
db.exec("select 1 from sqlite_schema");
|
||||
console.warn("sessionStorage =",sessionStorage);
|
||||
}catch(e){
|
||||
err = e;
|
||||
}finally{
|
||||
db.close();
|
||||
db.exec("insert into t(a) values (1),(2)");
|
||||
T.assert(expectCount === db.selectValue('select sum(a) from t'));
|
||||
} finally {
|
||||
if (db) db.close();
|
||||
}
|
||||
T.assert(err,"Expecting an exception")
|
||||
.assert(sqlite3.capi.SQLITE_NOTADB==err.resultCode,
|
||||
"Expecting NOTADB");
|
||||
db = new sqlite3.oo1.DB({
|
||||
filename: this.kvvfsDbFile,
|
||||
vfs: 'kvvfs',
|
||||
...encOpt2
|
||||
});
|
||||
T.assert( 4===db.selectValue('select sum(a) from t') );
|
||||
}finally{
|
||||
if( db ) db.close();
|
||||
this.kvvfsUnlink();
|
||||
}
|
||||
}.bind(this);
|
||||
const hexFoo = new Uint8Array([0x66,0x6f,0x6f]/*=="foo"*/);
|
||||
tryKey('textkey', 'foo', 3);
|
||||
T.assert( !initDb );
|
||||
tryKey('textkey', 'foo', 6);
|
||||
this.kvvfsUnlink();
|
||||
initDb = true;
|
||||
tryKey('key', 'foo', 3);
|
||||
T.assert( !initDb );
|
||||
tryKey('key', hexFoo, 6);
|
||||
this.kvvfsUnlink();
|
||||
initDb = true;
|
||||
tryKey('hexkey', hexFoo, 3);
|
||||
T.assert( !initDb );
|
||||
tryKey('hexkey', hexFoo, 6);
|
||||
this.kvvfsUnlink();
|
||||
}
|
||||
})/*kvvfs with SEE*/
|
||||
//#endif enable-see
|
||||
@@ -2836,6 +2851,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
},
|
||||
9
|
||||
);
|
||||
T.assert( 0==rc );
|
||||
db.transaction((d)=>{
|
||||
d.exec([
|
||||
"create table t(a);",
|
||||
@@ -2849,8 +2865,10 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
.assert(2 === countHook[capi.SQLITE_UPDATE])
|
||||
.assert(1 === countHook[capi.SQLITE_DELETE]);
|
||||
//wasm.xWrap.FuncPtrAdapter.debugFuncInstall = true;
|
||||
db.close();
|
||||
T.assert( !!capi.sqlite3_preupdate_hook(db, 0, 0) );
|
||||
//wasm.xWrap.FuncPtrAdapter.debugFuncInstall = false;
|
||||
T.assert( !capi.sqlite3_preupdate_hook(db, 0, 0) );
|
||||
db.close();
|
||||
}
|
||||
})/*pre-update hooks*/
|
||||
;/*end hook API tests*/
|
||||
@@ -3051,7 +3069,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
T.assert(6 === db.selectValue('select count(*) from p')).
|
||||
assert( this.opfsImportSize == exp.byteLength );
|
||||
db.close();
|
||||
const unlink = this.opfsUnlink =
|
||||
this.opfsUnlink =
|
||||
(fn=filename)=>sqlite3.util.sqlite3__wasm_vfs_unlink("opfs",fn);
|
||||
this.opfsUnlink(filename);
|
||||
T.assert(!(await sqlite3.opfs.entryExists(filename)));
|
||||
@@ -3302,7 +3320,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
.assert(true === await u3.removeVfs())
|
||||
.assert(false === await P3b.removeVfs());
|
||||
}
|
||||
}/*OPFS SAH Pool sanity checks*/)
|
||||
}/*OPFS SAH Pool sanity checks*/);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
T.g('Misc. APIs')
|
||||
@@ -3323,6 +3341,44 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
db.close();
|
||||
})
|
||||
|
||||
/**
|
||||
Ensure that certain Stmt members throw when called
|
||||
via DB.exec().
|
||||
*/
|
||||
.t('locked-by-exec() APIs', function(sqlite3){
|
||||
const db = new sqlite3.oo1.DB();
|
||||
db.exec("create table t(a);insert into t(a) values(1);");
|
||||
let checkCount = 0;
|
||||
const checkOp = function(op){
|
||||
++checkCount;
|
||||
T.mustThrowMatching(() => {
|
||||
db.exec({
|
||||
sql: "select ?1",
|
||||
bind: op,
|
||||
callback: (row, stmt) => {
|
||||
switch (row[0]) {
|
||||
case 'bind': stmt.bind(1); break;
|
||||
case 'finalize':
|
||||
case 'clearBindings':
|
||||
case 'reset':
|
||||
case 'step': stmt[op](); break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}, /^Operation is illegal when statement is locked.*/)
|
||||
};
|
||||
try{
|
||||
checkOp('bind');
|
||||
checkOp('finalize');
|
||||
checkOp('clearBindings');
|
||||
checkOp('reset');
|
||||
checkOp('step');
|
||||
T.assert(5===checkCount);
|
||||
}finally{
|
||||
db.close();
|
||||
}
|
||||
})
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
.t("Misc. stmt_...", function(sqlite3){
|
||||
const db = new sqlite3.oo1.DB();
|
||||
@@ -3353,6 +3409,16 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
T.assert( 1===n )
|
||||
.assert( 0===capi.sqlite3_stmt_busy(stmt) )
|
||||
.assert( !stmt.isBusy() );
|
||||
|
||||
if( wasm.exports.sqlite3_column_origin_name ){
|
||||
log("Column metadata APIs enabled");
|
||||
T.assert( "t" === capi.sqlite3_column_table_name(stmt, 0))
|
||||
.assert("a" === capi.sqlite3_column_origin_name(stmt, 0))
|
||||
.assert("main" === capi.sqlite3_column_database_name(stmt, 0))
|
||||
}else{
|
||||
log("Column metadata APIs not enabled");
|
||||
} // column metadata APIs
|
||||
|
||||
stmt.finalize();
|
||||
db.close();
|
||||
})
|
||||
@@ -3364,7 +3430,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
capi.sqlite3_interrupt(db);
|
||||
T.assert( 0!==capi.sqlite3_is_interrupted(db) );
|
||||
db.close();
|
||||
})
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
T.g('Bug Reports')
|
||||
@@ -3384,10 +3450,10 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
sql: "SELECT * FROM f order by path",
|
||||
rowMode: 'array'
|
||||
});
|
||||
const dump = function(lbl){
|
||||
/*const dump = function(lbl){
|
||||
let rc = fetchEm();
|
||||
log((lbl ? (lbl+' results') : ''),rc);
|
||||
};
|
||||
};*/
|
||||
//dump('Full fts table');
|
||||
let rc = fetchEm();
|
||||
T.assert(3===rc.length);
|
||||
|
||||
@@ -772,7 +772,6 @@ TESTSRC = \
|
||||
$(TOP)/src/test_thread.c \
|
||||
$(TOP)/src/test_vdbecov.c \
|
||||
$(TOP)/src/test_vfs.c \
|
||||
$(TOP)/src/test_windirent.c \
|
||||
$(TOP)/src/test_window.c \
|
||||
$(TOP)/src/test_wsd.c \
|
||||
$(TOP)/ext/fts3/fts3_term.c \
|
||||
@@ -1048,13 +1047,17 @@ $(T.tcl.env.sh): $(TCLSH_CMD) $(TCL_CONFIG_SH) $(MAKEFILE_LIST)
|
||||
@if [ x = "x$(TCL_CONFIG_SH)" ]; then \
|
||||
echo 'TCL_CONFIG_SH must be set to point to a "tclConfig.sh"' 1>&2; exit 1; \
|
||||
fi; \
|
||||
if [ x != "x$(TCLLIBDIR)" ]; then echo TCLLIBDIR="$(TCLLIBDIR)"; else \
|
||||
if [ x != "x$(TCLLIBDIR)" ]; then \
|
||||
echo "# generated by main.mk"; \
|
||||
echo TCLLIBDIR="$(TCLLIBDIR)"; \
|
||||
else \
|
||||
ld= ; \
|
||||
for d in `echo "puts stdout \\$$auto_path" | $(TCLSH_CMD)`; do \
|
||||
if [ -d "$$d" ]; then ld=$$d; break; fi; \
|
||||
done; \
|
||||
if [ x = "x$$ld" ]; then echo "Cannot determine TCLLIBDIR" 1>&2; exit 1; fi; \
|
||||
echo "TCLLIBDIR=$$ld/sqlite3"; \
|
||||
echo "# generated by main.mk"; \
|
||||
echo "TCLLIBDIR=$$ld/sqlite3$(PACKAGE_VERSION)"; \
|
||||
fi > $@; \
|
||||
echo ". \"$(TCL_CONFIG_SH)\" || exit \$$?" >> $@; \
|
||||
echo "Created $@"
|
||||
@@ -1623,15 +1626,29 @@ install-headers: sqlite3.h $(install-dir.include)
|
||||
$(INSTALL.noexec) sqlite3.h "$(TOP)/src/sqlite3ext.h" "$(install-dir.include)"
|
||||
install: install-headers
|
||||
|
||||
#
|
||||
# If TCL_EXT_DLL_BASENAME is not set then guess the Tcl extension's
|
||||
# DLL name depending on the Tcl version. This does not account for
|
||||
# Cygwin's naming - the canonical build will usually set it, but
|
||||
# static makefiles importing this one will need to account for that on
|
||||
# their own. They can do that by setting libtclsqlite3.basename-[89]
|
||||
# to appropriate names (cygsqlite resp. cygtcl9sqlite).
|
||||
#
|
||||
TCL_MAJOR_VERSION ?= 0
|
||||
libtclsqlite3.basename-8 ?= libsqlite
|
||||
libtclsqlite3.basename-9 ?= libtcl9sqlite
|
||||
TCL_EXT_DLL_BASENAME ?= $(libtclsqlite3.basename-$(TCL_MAJOR_VERSION))
|
||||
libtclsqlite3.DLL ?= $(TCL_EXT_DLL_BASENAME)$(PACKAGE_VERSION)$(T.dll)
|
||||
|
||||
#
|
||||
# libtclsqlite3...
|
||||
#
|
||||
pkgIndex.tcl:
|
||||
echo 'package ifneeded sqlite3 $(PACKAGE_VERSION) [list load [file join $$dir libtclsqlite3[info sharedlibextension]] Sqlite3]' > $@
|
||||
pkgIndex.tcl: $(TOP)/main.mk
|
||||
echo 'package ifneeded sqlite3 $(PACKAGE_VERSION) [list load [file join $$dir $(libtclsqlite3.DLL)] Sqlite3]' > $@
|
||||
pkgIndex.tcl-1: pkgIndex.tcl
|
||||
pkgIndex.tcl-0 pkgIndex.tcl-:
|
||||
tcl: pkgIndex.tcl-$(HAVE_TCL)
|
||||
libtclsqlite3.DLL = libtclsqlite3$(T.dll)
|
||||
|
||||
$(libtclsqlite3.DLL): $(T.tcl.env.sh) tclsqlite.o $(LIBOBJ)
|
||||
$(T.tcl.env.source); \
|
||||
$(T.link.shared) -o $@ tclsqlite.o \
|
||||
@@ -1672,34 +1689,47 @@ CFLAGS.tclextension = $(CFLAGS.intree_includes) $(CFLAGS.env) $(OPT_FEATURE_FLAG
|
||||
# by --with-tclsh=
|
||||
#
|
||||
tclextension: tclsqlite3.c
|
||||
$(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --build-only --cc "$(T.cc)" $(CFLAGS.tclextension)
|
||||
$(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --build-only \
|
||||
--tclConfig.sh $(TCL_CONFIG_SH) --cc "$(T.cc)" $(CFLAGS.tclextension)
|
||||
|
||||
#
|
||||
# Install the SQLite TCL extension in a way that is appropriate for $TCLSH_CMD
|
||||
# to find it.
|
||||
#
|
||||
tclextension-install: tclsqlite3.c
|
||||
$(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --destdir "$(DESTDIR)" --cc "$(T.cc)" $(CFLAGS.tclextension)
|
||||
$(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --destdir "$(DESTDIR)" \
|
||||
--tclConfig.sh $(TCL_CONFIG_SH) --cc "$(T.cc)" $(CFLAGS.tclextension)
|
||||
|
||||
#
|
||||
# Uninstall the SQLite TCL extension that is used by $TCLSH_CMD.
|
||||
#
|
||||
tclextension-uninstall:
|
||||
$(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --uninstall
|
||||
$(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --uninstall \
|
||||
--tclConfig.sh $(TCL_CONFIG_SH)
|
||||
|
||||
#
|
||||
# List all installed the SQLite TCL extensions that is are accessible
|
||||
# by $TCLSH_CMD, including prior versions.
|
||||
#
|
||||
tclextension-list:
|
||||
@ $(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --info
|
||||
@ $(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --info \
|
||||
--tclConfig.sh $(TCL_CONFIG_SH)
|
||||
|
||||
# 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
|
||||
@ $(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --version-check \
|
||||
--tclConfig.sh $(TCL_CONFIG_SH)
|
||||
|
||||
# Run all of the tclextension targets in order, ending with uninstall.
|
||||
tclextension-all:
|
||||
$(MAKE) tclextension
|
||||
$(MAKE) tclextension-install
|
||||
$(MAKE) tclextension-list
|
||||
$(MAKE) tclextension-verify
|
||||
$(MAKE) tclextension-uninstall
|
||||
|
||||
#
|
||||
# FTS5 things
|
||||
@@ -2330,12 +2360,12 @@ SHELL_DEP = \
|
||||
$(TOP)/ext/misc/sqlar.c \
|
||||
$(TOP)/ext/misc/uint.c \
|
||||
$(TOP)/ext/misc/vfstrace.c \
|
||||
$(TOP)/ext/misc/windirent.h \
|
||||
$(TOP)/ext/misc/zipfile.c \
|
||||
$(TOP)/ext/recover/dbdata.c \
|
||||
$(TOP)/ext/recover/sqlite3recover.c \
|
||||
$(TOP)/ext/recover/sqlite3recover.h \
|
||||
$(TOP)/src/test_windirent.c \
|
||||
$(TOP)/src/test_windirent.h
|
||||
$(TOP)/ext/recover/sqlite3recover.h
|
||||
|
||||
|
||||
shell.c: $(SHELL_DEP) $(TOP)/tool/mkshellc.tcl $(B.tclsh)
|
||||
$(B.tclsh) $(TOP)/tool/mkshellc.tcl shell.c
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
C Version\s3.50.4
|
||||
D 2025-07-30T19:33:53.432
|
||||
C Enable\sthe\sEXISTS-to-JOIN\soptimization\sif\sthe\souter\squery\shas\sno\nFROM\sclause.
|
||||
D 2025-07-08T17:28:09.030
|
||||
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md e108e1e69ae8e8a59e93c455654b8ac9356a11720d3345df2a4743e9590fb20d
|
||||
F Makefile.in c3e414df4dc8dfb12f1f6baf129fcb6d18cd0ebd3c9109370fb3fceeeef9a37a
|
||||
F Makefile.in a6c14b6906f5322920dd5d1ff9a84808337911068f2a6e777ec7088a87cbc3a8
|
||||
F Makefile.linux-generic bd3e3cacd369821a6241d4ea1967395c962dfe3057e38cb0a435cee0e8b789d0
|
||||
F Makefile.msc 0206f28988bb6634c7e8aff05bf6cfa65d6dfe1d2b6bd95160dd99290a83dfc7
|
||||
F Makefile.msc ec2011bbdfc917d6a1c7c173dabb29c14ead0dd8e2e0b67278a00ae4ba576a77
|
||||
F README.md e28077cfbef795e99c9c75ed95aa7257a1166709b562076441a8506ac421b7c1
|
||||
F VERSION 33191da58a15d51e312248db3124d33e1dbc1797d7f8fe14d9df5ff07430b503
|
||||
F VERSION 16eddb43056a79c1977427ab7a05f3457c373fa159dcdced8754eb89ce7e06b8
|
||||
F art/icon-243x273.gif 9750b734f82fdb3dc43127753d5e6fbf3b62c9f4e136c2fbf573b2f57ea87af5
|
||||
F art/icon-80x90.gif 65509ce3e5f86a9cd64fe7fca2d23954199f31fe44c1e09e208c80fb83d87031
|
||||
F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
|
||||
@@ -22,15 +22,14 @@ F autoconf/Makefile.msc f15ad424ca2820df8e39d9157965710af0a64d87773706706a12ea4f
|
||||
F autoconf/README.first f1d3876e9a7852c22f275a6f06814e64934cecbc0b5b9617d64849094c1fd136
|
||||
F autoconf/README.txt b749816b8452b3af994dc6d607394bef3df1736d7e09359f1087de8439a52807
|
||||
F autoconf/auto.def 3d994f3a9cc9b712dbce92a5708570ddcf3b988141b6eb738f2ed16127a9f0ac
|
||||
F autoconf/tea/Makefile.in 14c6a79ce87e10d8a35398f2d0e04e1d83a88eb52ee16ebf0eeaccf005ff84b3
|
||||
F autoconf/tea/README.txt 6301adfea2e75eabcc550a1ed4812faaa4024c9584ac89a7f018e39f9bc9defb
|
||||
F autoconf/tea/_teaish.tester.tcl.in ed5445512e91c12afbbb99771efb68a23be4a046d52d61213fb5b6f010118129
|
||||
F autoconf/tea/Makefile.in bf6b43eafcd18766d81a8f0085cfc9cb051d8abae9031a8e7c3f5f1246e8f166
|
||||
F autoconf/tea/README.txt 23475876343498ef2b514cc7510e8f1559a17e8e03fbc7a41c1c8a3b89e7b7e3
|
||||
F autoconf/tea/_teaish.tester.tcl.in 8253b44be88e2e3f21de95a65d3a90c2be8e70b7bdd08a5b80e337ba7402f8f1
|
||||
F autoconf/tea/auto.def ce95b9450e2fa4ba5dc857e208fe10f4e6f2d737796ac3278aee6079db417529
|
||||
F autoconf/tea/configure d0b12b984edca6030d1976375b80157ac78b5b90a5b4f0dcee39357f63f4a80b x
|
||||
F autoconf/tea/doc/sqlite3.n 9a97f4f717ceab73004ea412af7960625c1cb24b5c25e4ae4c8b5d8fa4300f4e
|
||||
F autoconf/tea/license.terms 13bd403c9610fd2b76ece0ab50c4c5eda933d523
|
||||
F autoconf/tea/pkgIndex.tcl.in e07da6b94561f4aa382bab65b1ccceb04701b97bf59d007c1d1f20a222b22d07
|
||||
F autoconf/tea/teaish.tcl 81571a9f9ae5c70735595b05586cb2de9d2aea7e32aad10417c4982f2e2f01c8
|
||||
F autoconf/tea/teaish.tcl 81feb417e718ed75cdd7e2fdf6771f3da80dae97377a90c4d5b62b3754abbf1d
|
||||
F autoconf/tea/teaish.test.tcl cfe94e1fb79dd078f650295be59843d470125e0cc3a17a1414c1fb8d77f4aea6
|
||||
F autosetup/LICENSE 41a26aebdd2cd185d1e2b210f71b7ce234496979f6b35aef2cbf6b80cbed4ce4
|
||||
F autosetup/README.autosetup a78ff8c4a3d2636a4268736672a74bf14a82f42687fcf0631a70c516075c031e
|
||||
@@ -47,13 +46,13 @@ F autosetup/cc.tcl c0fcc50ca91deff8741e449ddad05bcd08268bc31177e613a6343bbd1fd3e
|
||||
F autosetup/find_tclconfig.tcl e64886ffe3b982d4df42cd28ed91fe0b5940c2c5785e126c1821baf61bc86a7e
|
||||
F autosetup/jimsh0.c 563b966c137a4ce3c9333e5196723b7ac0919140a9d7989eb440463cd855c367
|
||||
F autosetup/pkg-config.tcl 4e635bf39022ff65e0d5434339dd41503ea48fc53822c9c5bde88b02d3d952ba
|
||||
F autosetup/proj.tcl c4a77735b57f3c016a185bff048212a197b77723f9bea6cfafe396e4b542c666
|
||||
F autosetup/sqlite-config.tcl ccda82e43e377b832aae72a1678b1dc17dcaff36ed0ebbd8f0cfc88612ae8de3
|
||||
F autosetup/proj.tcl 6aac1eb3059fc511c8e1659f33b96eb2a216a371ed9b28be6661374061be9b15
|
||||
F autosetup/sqlite-config.tcl 3177dedd7bd49465fa06677fd743c4966eee5702a9ddf4914c2c1af5e5972a52
|
||||
F autosetup/system.tcl 51d4be76cd9a9074704b584e5c9cbba616202c8468cf9ba8a4f8294a7ab1dba9
|
||||
F autosetup/teaish/README.txt b40071e6f8506500a2f7f71d5fc69e0bf87b9d7678dd9da1e5b4d0acbf40b1ca
|
||||
F autosetup/teaish/core.tcl 42b4f76ebcacafded7c13a225ce55454bd1fcf8a11912729128e1076b12f6433
|
||||
F autosetup/teaish/core.tcl aee092fc71986d1272b835ea7492bb55ffc213a289502e4f14da80cf67b7e3c3
|
||||
F autosetup/teaish/feature.tcl 18194fb79a24d30e5bbdeab40999616f39278b53a27525349ded033af2fd73be
|
||||
F autosetup/teaish/tester.tcl 091745984473faea6985254b9986c6dfd0cce06f68bc515ba4afc1e6b3742fa8
|
||||
F autosetup/teaish/tester.tcl 1799514c2652db49561b3386c5242b94534d1663f2cfac861a955e071895fdd0
|
||||
F configure 9a00b21dfd13757bbfb8d89b30660a89ec1f8f3a79402b8f9f9b6fc475c3303a x
|
||||
F contrib/sqlitecon.tcl eb4c6578e08dd353263958da0dc620f8400b869a50d06e271ab0be85a51a08d3
|
||||
F doc/F2FS.txt c1d4a0ae9711cfe0e1d8b019d154f1c29e0d3abfe820787ba1e9ed7691160fcd
|
||||
@@ -82,7 +81,7 @@ F ext/fts3/README.tokenizers b92bdeb8b46503f0dd301d364efc5ef59ef9fa8e2758b8e742f
|
||||
F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
|
||||
F ext/fts3/fts3.c 4f02858ab845a97bedf06e6cc1fba49a81fe5e00a26df68d0ad0f00a5814fa70
|
||||
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
|
||||
F ext/fts3/fts3Int.h 53f00a0d037c81174af1089ad7776563dcbccb5ed4697405256d3d4260d9fccc
|
||||
F ext/fts3/fts3Int.h d0f70d602be987501cf6eff86a06a0b5d0389929c6abe24972d59594e6f4cb03
|
||||
F ext/fts3/fts3_aux.c 7eab82a9cf0830f6551ba3abfdbe73ed39e322a4d3940ee82fbf723674ecd9f3
|
||||
F ext/fts3/fts3_expr.c 5c13796638d8192c388777166075cdc8bc4b6712024cd5b72c31acdbefce5984
|
||||
F ext/fts3/fts3_hash.c d9dba473741445789330c7513d4f65737c92df23c3212784312931641814672a
|
||||
@@ -108,14 +107,14 @@ F ext/fts3/unicode/mkunicode.tcl cbf5f7b5c8ce8014bad731f246f2e520eece908465de477
|
||||
F ext/fts3/unicode/parseunicode.tcl a981bd6466d12dd17967515801c3ff23f74a281be1a03cf1e6f52a6959fc77eb
|
||||
F ext/fts5/extract_api_docs.tcl 009cf59c77afa86d137b0cca3e3b1a5efbe2264faa2df233f9a7aa8563926d15
|
||||
F ext/fts5/fts5.h ff5d3cc88b29e41612bfb29eb723e29e38973de62ca75ba3e8f94ccb67f5b5f2
|
||||
F ext/fts5/fts5Int.h 4c7380ce83e8f6b5b3216ebe2b33093a20fa72e832a88d14023f827b7d8b9933
|
||||
F ext/fts5/fts5Int.h cc1e6ed28e1fbde71e4427ba6d15b0c73a4f8f119adeb4f7bcc97e3336fbbeac
|
||||
F ext/fts5/fts5_aux.c da4a7a9a11ec15c6df0699d908915a209bcde48f0b04101461316b59f71abffb
|
||||
F ext/fts5/fts5_buffer.c f1e6d0324d7c55329d340673befc26681a372a4d36086caa8d1ec7d7c53066c7
|
||||
F ext/fts5/fts5_config.c e7d8dd062b44a66cd77e5a0f74f23a2354cd1f3f8575afb967b2773c3384f7f8
|
||||
F ext/fts5/fts5_expr.c be9e5f7f11d87e7bd3680832c93c13050fe351994b5052b0215c2ef40312c23a
|
||||
F ext/fts5/fts5_hash.c a6266cedd801ab7964fa9e74ebcdda6d30ec6a96107fa24148ec6b7b5b80f6e0
|
||||
F ext/fts5/fts5_index.c 5896ca188574f728426a8f03752dfbf4fad48d1a4b7450b29d8d3acae1739761
|
||||
F ext/fts5/fts5_main.c 57933c18efe1058d8871199875c7a59744dabc3904f3aefbf9ff4a4e11fc79e2
|
||||
F ext/fts5/fts5_index.c ed562b75c87efaa80c40e55f6c1102904728e35baa293c371f96b1c8e151399d
|
||||
F ext/fts5/fts5_main.c e558225168845dc708abeb2ad10415696e5a3249bcba1810ba3c7ef80764962e
|
||||
F ext/fts5/fts5_storage.c 19bc7c4cbe1e6a2dd9849ef7d84b5ca1fcbf194cefc3e386b901e00e08bf05c2
|
||||
F ext/fts5/fts5_tcl.c 7fb5a3d3404099075aaa2457307cb459bbc257c0de3dbd52b1e80a5b503e0329
|
||||
F ext/fts5/fts5_test_mi.c 4308d5658cb1f5eee5998dcbaac7d5bdf7a2ef43c8192ca6e0c843f856ccee26
|
||||
@@ -127,7 +126,7 @@ F ext/fts5/fts5_vocab.c ff0441c4ea165081e8152dec6d29056faa0cdc281a9f218a00e3d7aa
|
||||
F ext/fts5/fts5parse.y eb526940f892ade5693f22ffd6c4f2702543a9059942772526eac1fde256bb05
|
||||
F ext/fts5/mkportersteps.tcl 5acf962d2e0074f701620bb5308155fa1e4a63ba
|
||||
F ext/fts5/test/fts5_common.tcl c5aa7cf7148b6dcffb5b61520ae18212baf169936af734ab265143f59db328fe
|
||||
F ext/fts5/test/fts5aa.test 015c81b84d53bfcedd77d624202c8b02e9f0cbbb4b51688e3a9c9f90bccbb4ac
|
||||
F ext/fts5/test/fts5aa.test cf4ff6180873bbc131666ba846ddd90148fcb61c20aad089711d3511cce24300
|
||||
F ext/fts5/test/fts5ab.test 4bdb619fee409e11417e8827e320b857e42e926a01a0408fc9f143ec870a6ced
|
||||
F ext/fts5/test/fts5ac.test 4a73626de86f3d17c95738034880c4f0de8d54741fb943d819b528373657e59b
|
||||
F ext/fts5/test/fts5ad.test 058e616612964e61d19f70295f0e6eaedceb4b29b1fbf4f859615ef7e779dc22
|
||||
@@ -161,14 +160,14 @@ F ext/fts5/test/fts5contentless2.test 70ffe6c611d8f278240da56734df8a77948f04e273
|
||||
F ext/fts5/test/fts5contentless3.test 75eaae5ad6b284ee447788943974d323228f27cc35a1681da997135cff95bc6a
|
||||
F ext/fts5/test/fts5contentless4.test ec34dc69ef474ca9997dae6d91e072906e0e9a5a4b05ea89964c863833b6eff8
|
||||
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 3420ad30bf9e9bbdbd43b3224c582431744899530a65b11b60ddacdf14200e19
|
||||
F ext/fts5/test/fts5corrupt.test 237fce1c3261bb3a5bec333b0f0dbf5b105ec32627ef14cccbda3cfe13833193
|
||||
F ext/fts5/test/fts5corrupt2.test 4a03a158c2cb617c9f76d26b35c1ef2534124bc0bbddcea38dfd5b170ebea27b
|
||||
F ext/fts5/test/fts5corrupt3.test 03a6118a8fe5a7c217c28c92b6b25ba04643640a4ac0a1d2b8d10de8191dc5f4
|
||||
F ext/fts5/test/fts5corrupt4.test dc08d19f5b8943e95a7778a7d8da592042504faf18dd93f68f7d7a0d7d7dd733
|
||||
F ext/fts5/test/fts5corrupt5.test bcf0801b0c991eadae3cb8e978e82b4bf01412cb4df41874a90d5aa279c7cc96
|
||||
F ext/fts5/test/fts5corrupt5.test 73985d4fe6d8f0d5d5c7bcf79ae7c6522c376cd6ad710a0ff2f26e0c2e222abe
|
||||
F ext/fts5/test/fts5corrupt6.test 2d72db743db7b5d9c9a6d0cfef24d799ed1aa5e8192b66c40e871a37ed9eed06
|
||||
F ext/fts5/test/fts5corrupt7.test 4e830875c33b9ea3c4cf1ba71e692b63893cbb4faae8c69b1071889dc26e211c
|
||||
F ext/fts5/test/fts5corrupt8.test ffaaad3524c79621042eaf176d9206f68c5f07611c4d24943e60a3b6ea0026e8
|
||||
F ext/fts5/test/fts5corrupt7.test 814aab492d7a09abb5bfdd81cc66fc206d7f3868f9a3bae91876e02efc466fb3
|
||||
F ext/fts5/test/fts5corrupt8.test 7618b102b9b3a5a3494271f4975ab5837e2fb3f61f5adfcdeeb31772c859e6df
|
||||
F ext/fts5/test/fts5delete.test 2a5008f8b1174ef41d1974e606928c20e4f9da77d9f8347aed818994d89cced4
|
||||
F ext/fts5/test/fts5detail.test 54015e9c43ec4ba542cfb93268abdf280e0300f350efd08ee411284b03595cc4
|
||||
F ext/fts5/test/fts5determin.test 1b77879b2ae818b5b71c859e534ee334dac088b7cf3ff3bf76a2c82b1c788d11
|
||||
@@ -230,7 +229,7 @@ F ext/fts5/test/fts5prefix.test c0b7842f1a2d830c0b146cd438a95ea4c5a25635719ed0d9
|
||||
F ext/fts5/test/fts5prefix2.test a5bb43b8a2687efafa7ac4e5ccff6812015cf8cf18e3086bb0eb3126f30fbbf6
|
||||
F ext/fts5/test/fts5query.test 0320a7a4b58a6e3e50ec8910b301649da90ace675001f9e0bf6392750ad4591d
|
||||
F ext/fts5/test/fts5rank.test 47c1e8e5d84754ff18e012fdd629776088b5a15de41bdd24957581cf084d8a00
|
||||
F ext/fts5/test/fts5rebuild.test 83e72d77636378833233fadc7cb7517a2fa446ea7d1f94dd526ba3e7e104b9f5
|
||||
F ext/fts5/test/fts5rebuild.test dc09779fbbe151ab68206a0931c10a611912a7a12c7a85d71c5e48453f2375a5
|
||||
F ext/fts5/test/fts5restart.test 9af2084b8e065130037b95f05f3f220bb7973903a7701e2c5fb916dff7cf80c5
|
||||
F ext/fts5/test/fts5rowid.test 8632829fec04996832a4cfb4f0bd89721ba65b7e398c1731741bdb63f070e1a3
|
||||
F ext/fts5/test/fts5savepoint.test 1447758d7900afe903cef08b4524c5331fb60c1126ae6fba7f4d8704268013c5
|
||||
@@ -296,7 +295,7 @@ F ext/jni/src/org/sqlite/jni/annotation/NotNull.java be6cc3e8e114485822331630097
|
||||
F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 56e3dee1f3f703a545dfdeddc1c3d64d1581172b1ad01ffcae95c18547fafd90
|
||||
F ext/jni/src/org/sqlite/jni/annotation/package-info.java 977b374aed9d5853cbf3438ba3b0940abfa2ea4574f702a2448ee143b98ac3ca
|
||||
F ext/jni/src/org/sqlite/jni/capi/AbstractCollationCallback.java 1afa90d3f236f79cc7fcd2497e111992644f7596fbc8e8bcf7f1908ae00acd6c
|
||||
F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java 0b72cdff61533b564d65b63418129656daa9a9f30e7e7be982bd5ab394b1dbd0
|
||||
F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java 0e28a0df51368c7127e505f1e9acd92a7e66e035bcd6288463aa691cb300c9af
|
||||
F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java c045a5b47e02bb5f1af91973814a905f12048c428a3504fbc5266d1c1be3de5a
|
||||
F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759
|
||||
F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca
|
||||
@@ -418,7 +417,7 @@ F ext/misc/dbdump.c b8592f6f2da292c62991a13864a60d6c573c47a9cc58362131b9e6a64f82
|
||||
F ext/misc/decimal.c 228d47e9ef4de60daf5851da19e3ac9ac1eda9e94432816914469501db6a1129
|
||||
F ext/misc/eval.c 04bc9aada78c888394204b4ed996ab834b99726fb59603b0ee3ed6e049755dc1
|
||||
F ext/misc/explain.c 606100185fb90d6a1eade1ed0414d53503c86820d8956a06e3b0a56291894f2b
|
||||
F ext/misc/fileio.c 69b70008290598030592aec969bcd2cba04466a7b85ad3e906f87981690aab3b
|
||||
F ext/misc/fileio.c 88cb2e5744296de6638af02ef6349fd468c2eb5e5f41ba405f88d9b4ad500f8e
|
||||
F ext/misc/fossildelta.c 0aeb099e9627eea693cf21ae47826ecd1e0319b93143bed23090838b2ef0c162
|
||||
F ext/misc/fuzzer.c 6b231352815304ba60d8e9ec2ee73d4918e74d9b76bda8940ba2b64e8777515e
|
||||
F ext/misc/ieee754.c c9dd9d77c8e8e18e0a5706f8ffcccf4ccb6562073709f7453d4d73f5122f4362
|
||||
@@ -457,9 +456,10 @@ F ext/misc/uuid.c 5bb2264c1b64d163efa46509544fd7500cb8769cb7c16dd52052da8d961505
|
||||
F ext/misc/vfslog.c 3932ab932eeb2601dbc4447cb14d445aaa9fbe43b863ef5f014401c3420afd20
|
||||
F ext/misc/vfsstat.c 0b23c0a69a2b63dc0ef0af44f9c1fc977300c480a1f7a9814500369d8211f56e
|
||||
F ext/misc/vfstrace.c 0e4b8b17ac0675ea90f6d168d8214687e06ca3efbc0060aad4814994d82b41fb
|
||||
F ext/misc/vtablog.c a197addbbd1e267a5476274b74953e1b6f050e28516f0a5fe7d6382753165ee6
|
||||
F ext/misc/vtablog.c 62d54fb0f4ff9df72ffa44f4a226451a964e1d96ac550f2b8906feebdad00b58
|
||||
F ext/misc/vtshim.c e5bce24ab8c532f4fdc600148718fe1802cb6ed57417f1c1032d8961f72b0e8f
|
||||
F ext/misc/wholenumber.c 0fa0c082676b7868bf2fa918e911133f2b349bcdceabd1198bba5f65b4fc0668
|
||||
F ext/misc/windirent.h 02211ce51f3034c675f2dbf4d228194d51b3ee05734678bad5106fff6292e60c
|
||||
F ext/misc/zipfile.c b62147ac4985eaac4e368d529b1f4f43ad6bc9ac13d6805d907fff3afdac64d3
|
||||
F ext/misc/zorder.c b0ff58fa643afa1d846786d51ea8d5c4b6b35aa0254ab5a82617db92f3adda64
|
||||
F ext/rbu/rbu.c 801450b24eaf14440d8fd20385aacc751d5c9d6123398df41b1b5aa804bf4ce8
|
||||
@@ -539,7 +539,7 @@ F ext/repair/test/checkindex01.test b530f141413b587c9eb78ff734de6bb79bc3515c3350
|
||||
F ext/repair/test/test.tcl 686d76d888dffd021f64260abf29a55c57b2cedfa7fc69150b42b1d6119aac3c
|
||||
F ext/rtree/README 734aa36238bcd2dee91db5dba107d5fcbdb02396612811377a8ad50f1272b1c1
|
||||
F ext/rtree/geopoly.c f0573d5109fdc658a180db0db6eec86ab2a1cf5ce58ec66cbf3356167ea757eb
|
||||
F ext/rtree/rtree.c 811edc5c2f3e13dcee825a8ec6f2ebe29b34bdb0186184d75d461621173638fa
|
||||
F ext/rtree/rtree.c a1f04a2013bc8f982307760615d1cee591355ac4723c74c3761c128d5c6954ab
|
||||
F ext/rtree/rtree.h 4a690463901cb5e6127cf05eb8e642f127012fd5003830dbc974eca5802d9412
|
||||
F ext/rtree/rtree1.test e0608db762b2aadca0ecb6f97396cf66244490adc3ba88f2a292b27be3e1da3e
|
||||
F ext/rtree/rtree2.test 9d9deddbb16fd0c30c36e6b4fdc3ee3132d765567f0f9432ee71e1303d32603d
|
||||
@@ -621,7 +621,7 @@ F ext/session/sqlite3session.c 6b0877fe1ab832aa4b85eaca72606dfd1630a1363a1be7af1
|
||||
F ext/session/sqlite3session.h 9bb1a6687b467764b35178dc29bbd2c57ab8cd3acdc8a62f088c34ad17e4fe2b
|
||||
F ext/session/test_session.c 2ddff73ea368d827028c32851b291416e1008845832feb27b751d15e57e13cc3
|
||||
F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c
|
||||
F ext/wasm/GNUmakefile a9acd702e7423cd47b96b78d0ef546bb7612fa7731e1b400261def96bd90385e
|
||||
F ext/wasm/GNUmakefile a05bb1766f97c88cb42fbfe9e349c799c691f8ae0dc959e9d9469f0bcee89350
|
||||
F ext/wasm/README-dist.txt f01081a850ce38a56706af6b481e3a7878e24e42b314cfcd4b129f0f8427066a
|
||||
F ext/wasm/README.md b89605f65661cf35bf034ff6d43e448cc169b8017fc105d498e33b81218b482c
|
||||
F ext/wasm/SQLTester/GNUmakefile e0794f676d55819951bbfae45cc5e8d7818dc460492dc317ce7f0d2eca15caff
|
||||
@@ -630,7 +630,7 @@ F ext/wasm/SQLTester/SQLTester.run.mjs 57f2adb33f43f2784abbf8026c1bfd049d8013af1
|
||||
F ext/wasm/SQLTester/index.html 64f3435084c7d6139b08d1f2a713828a73f68de2ae6a3112cbb5980d991ba06f
|
||||
F ext/wasm/SQLTester/touint8array.c 2d5ece04ec1393a6a60c4bf96385bda5e1a10ad49f3038b96460fc5e5aa7e536
|
||||
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-core 2bcbbfe3b95c043ed6037e2708a2ee078d212dd1612c364f93588d8dc97300fe
|
||||
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-extras fe40d6d758646e38f8b15f709044951e10884214f5453d35502100179c388c13
|
||||
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-extras cb4fa8842c875b6ee99381523792975c5ebb7371bd27fbd1bd863a43c7f3505a
|
||||
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 c64ec8e84449c069e0217706d9d7d31b3bd53627228b2ba0c3cddbdc2350ca66
|
||||
@@ -640,8 +640,8 @@ F ext/wasm/api/post-js-footer.js 365405929f41ca0e6d389ed8a8da3f3c93e11d3ef43a90a
|
||||
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 3ac1786e461ada63033143be8c3b00b26b939540661f3e839515bb92f2e35359
|
||||
F ext/wasm/api/sqlite3-api-glue.c-pp.js bd8ae059bb3ea489666b2f167025833e55dc5941be99c129a56fc25e541d37ce
|
||||
F ext/wasm/api/sqlite3-api-oo1.c-pp.js f3a8e2004c6625d17946c11f2fb32008be78bc5207bf746fc77d59848813225f
|
||||
F ext/wasm/api/sqlite3-api-glue.c-pp.js 0b76510f3650053bac67ca8947cb6ab9d050ad2218118a2e7796dd37be832ffa
|
||||
F ext/wasm/api/sqlite3-api-oo1.c-pp.js c68d6da0088c2527156fca9163a721abe08e7bd077b15404fd8d292f4612adc1
|
||||
F ext/wasm/api/sqlite3-api-prologue.js 8708570165f5b4bce9a78ccd91bc9ddf8735970ac1c4d659e36c9a7d9a644bb4
|
||||
F ext/wasm/api/sqlite3-api-worker1.c-pp.js f646a65257973b8c4481f8a6a216370b85644f23e64b126e7ae113570587c0ab
|
||||
F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89
|
||||
@@ -650,7 +650,7 @@ F ext/wasm/api/sqlite3-vfs-helper.c-pp.js 3f828cc66758acb40e9c5b4dcfd87fd478a14c
|
||||
F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js 0f68a64e508598910e7c01214ae27d603dfc8baec6a184506fafac603a901931
|
||||
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 4ab0704ee198de7d1059eccedc7703c931510b588d10af0ee36ea5b3ebbac284
|
||||
F ext/wasm/api/sqlite3-vtab-helper.c-pp.js e809739d71e8b35dfe1b55d24d91f02d04239e6aef7ca1ea92a15a29e704f616
|
||||
F ext/wasm/api/sqlite3-wasm.c 7ea3d4a286a2241f6fcc65c9ff10fc04ee5590f80f40763a57001dd5e93aa4c4
|
||||
F ext/wasm/api/sqlite3-wasm.c d2672a83b99a89db6bab31eccf9f93fca15a01838d8b0224967db6d4c4bff560
|
||||
F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bc65debfe43b81fc39fb25c40ad0cc1946bd82580fbf644351107b544d6177ee
|
||||
F ext/wasm/api/sqlite3-worker1.c-pp.js 5e8706c2c4af2a57fbcdc02f4e7ef79869971bc21bb8ede777687786ce1c92d5
|
||||
F ext/wasm/batch-runner-sahpool.html e9a38fdeb36a13eac7b50241dfe7ae066fe3f51f5c0b0151e7baee5fce0d07a7
|
||||
@@ -688,9 +688,9 @@ F ext/wasm/scratchpad-wasmfs.html a3d7388f3c4b263676b58b526846e9d02dfcb4014ff29d
|
||||
F ext/wasm/scratchpad-wasmfs.mjs 66034b9256b218de59248aad796760a1584c1dd842231505895eff00dbd57c63
|
||||
F ext/wasm/speedtest1-wasmfs.html 0e9d335a9b5b5fafe6e1bc8dc0f0ca7e22e6eb916682a2d7c36218bb7d67379d
|
||||
F ext/wasm/speedtest1-wasmfs.mjs c77c7231338ed5c0e1ce16aa29106df8e5b5cf11a48319c49433490a8d3ded30
|
||||
F ext/wasm/speedtest1-worker.html 864b65ed78ce24847a348c180e7f267621a02ca027068a1863ec1c90187c1852
|
||||
F ext/wasm/speedtest1-worker.html d24d1e06caf3dcd83430c8c3d87761ff7555fd06eaeaf2fc02ce49cf45f0d032
|
||||
F ext/wasm/speedtest1-worker.js 95e549e13a4d35863a9a7fc66122b5f546c0130d3be7b06dfcc556eb66d24bde
|
||||
F ext/wasm/speedtest1.html ff048b4a623aa192e83e143e48f1ce2a899846dd42c023fdedc8772b6e3f07da
|
||||
F ext/wasm/speedtest1.html 217bf15d43065634a27a06dbd5c9a320f6da9932dbcdfc963d482bc2f99dbea9
|
||||
F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x
|
||||
F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0
|
||||
F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5
|
||||
@@ -698,7 +698,7 @@ 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 419717b16e12703487a7ccf3ea4e63d693bdfbf7657e55a7e6c559bbccf027d3
|
||||
F ext/wasm/tester1.c-pp.js 766a2ba51a2619d41a49be7c6a1ad014c1d23fc97b67496e4f103038203eb17d
|
||||
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
|
||||
@@ -709,7 +709,7 @@ F ext/wasm/tests/opfs/sahpool/sahpool-pausing.js f264925cfc82155de38cecb3d204c36
|
||||
F ext/wasm/tests/opfs/sahpool/sahpool-worker.js bd25a43fc2ab2d1bafd8f2854ad3943ef673f7c3be03e95ecf1612ff6e8e2a61
|
||||
F ext/wasm/wasmfs.make 68999f5bd8c489239592d59a420f8c627c99169bbd6fa16a404751f757b9f702
|
||||
F magic.txt 5ade0bc977aa135e79e3faaea894d5671b26107cc91e70783aa7dc83f22f3ba0
|
||||
F main.mk 34290a772ec671de1fa5defd4fa4074aad24b1ea7eaabebba071e30564c6498c
|
||||
F main.mk 3ced12d068b22957f4e82a0b94a2c1d83b37dcf39c087263691267f7bfb39a84
|
||||
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
|
||||
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
|
||||
F mptest/crash01.test 61e61469e257df0850df4293d7d4d6c2af301421
|
||||
@@ -724,19 +724,19 @@ F src/analyze.c 03bcfc083fc0cccaa9ded93604e1d4244ea245c17285d463ef6a60425fcb247d
|
||||
F src/attach.c 9af61b63b10ee702b1594ecd24fb8cea0839cfdb6addee52fba26fa879f5db9d
|
||||
F src/auth.c 54ab9c6c5803b47c0d45b76ce27eff22a03b4b1f767c5945a3a4eb13aa4c78dc
|
||||
F src/backup.c 5c97e8023aab1ce14a42387eb3ae00ba5a0644569e3476f38661fa6f824c3523
|
||||
F src/bitvec.c 203368f515a36e5537f1efc2a7f47903e9e44a6626e4fd0554f8feed981bba88
|
||||
F src/bitvec.c e242d4496774dfc88fa278177dd23b607dce369ccafb3f61b41638eea2c9b399
|
||||
F src/btmutex.c 30dada73a819a1ef5b7583786370dce1842e12e1ad941e4d05ac29695528daea
|
||||
F src/btree.c da98489a981c347cc3a3982ea2810bbb583511a73cc34762547f30dbb4cda7f0
|
||||
F src/btree.h 18e5e7b2124c23426a283523e5f31a4bff029131b795bb82391f9d2f3136fc50
|
||||
F src/btree.c cb5b8ceb9baa02a63a2f83dec09c4153e1cfbdf9c2adef5c62c26d2160eeb067
|
||||
F src/btree.h e823c46d87f63d904d735a24b76146d19f51f04445ea561f71cc3382fd1307f0
|
||||
F src/btreeInt.h 9c0f9ea5c9b5f4dcaea18111d43efe95f2ac276cd86d770dce10fd99ccc93886
|
||||
F src/build.c 67159d31bfc565e5f23a7be8159325bae599bddd19fc584ac2511b947cf341d3
|
||||
F src/build.c cc4f287348790bbb7219f7e8dee13b1c345c3377fcdd98eca866e7457ecd07e7
|
||||
F src/callback.c acae8c8dddda41ee85cfdf19b926eefe830f371069f8aadca3aa39adf5b1c859
|
||||
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
|
||||
F src/date.c 9db4d604e699a73e10b8e85a44db074a1f04c0591a77e2abfd77703f50dce1e9
|
||||
F src/dbpage.c fcb1aafe00872a8aff9a7aa0ef7ff1b01e5817ec7bbd521f8f3e1e674ac8d609
|
||||
F src/dbpage.c b3e218f8ed74fcbb7fa805df8ca669a3718d397617b3d8a8aac3307dc315c4d6
|
||||
F src/dbstat.c 73362c0df0f40ad5523a6f5501224959d0976757b511299bf892313e79d14f5c
|
||||
F src/delete.c 03a77ba20e54f0f42ebd8eddf15411ed6bdb06a2c472ac4b6b336521bf7cea42
|
||||
F src/expr.c 72e7cddcb2ea06de48f40ee301b81608d86a17b73781e42901ce585f53074589
|
||||
F src/expr.c 12d8a79f6a0eb9bfae2d468a6d5bd7b9a0ff00988b3f5326ec9bbaad61bbf385
|
||||
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
|
||||
F src/fkey.c 928ed2517e8732113d2b9821aa37af639688d752f4ea9ac6e0e393d713eeb76f
|
||||
F src/func.c de47a8295503aa130baae5e6d9868ecf4f7c4dbffa65d83ad1f70bdbac0ee2d6
|
||||
@@ -745,7 +745,7 @@ 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 d05934dfab2c5c0c480fc6fd2038f11215661de08ea6ff38d2563216bd555c1b
|
||||
F src/insert.c dfd311b0ac2d4f6359e62013db67799757f4d2cc56cca5c10f4888acfbbfa3fd
|
||||
F src/json.c cb87977b1ee25ee7d27505d65a9261b687395bf895342c8ba566b7c01aee2047
|
||||
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
|
||||
F src/loadext.c d7edd8e671237539d795d30daaf888908a2c82e99bade4c78f3be021e8b7d655
|
||||
@@ -781,22 +781,22 @@ F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5
|
||||
F src/pcache1.c 131ca0daf4e66b4608d2945ae76d6ed90de3f60539afbd5ef9ec65667a5f2fcd
|
||||
F src/pragma.c 30b535d0a66348df844ee36f890617b4cf45e9a22dcbc47ec3ca92909c50aaf1
|
||||
F src/prepare.c 1832be043fce7d489959aae6f994c452d023914714c4d5457beaed51c0f3d126
|
||||
F src/printf.c 3b91c334f528359145f4dde0dedd945bbb21044d0825ea064934d7222d61662c
|
||||
F src/printf.c 71b6d3a0093bf23f473e25480ca0024e8962681506c75f4ffd3d343a3f0ab113
|
||||
F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
|
||||
F src/resolve.c d40fe18d7c2fd0339f5846ffcf7d6809866e380acdf14c76fb2af87e9fe13f64
|
||||
F src/resolve.c d3ee7ed308d46f4ee6d3bb6316d8d6f87158f93a7fd616732138cc953cf364f0
|
||||
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
|
||||
F src/select.c 5c129b669317a1d57283055482b9c1e105199a7e47d69526491ca165d3376999
|
||||
F src/shell.c.in ba53a52dafb167ac6320703da741386c34fbcabe8c078a188bb9f89808e3ef8f
|
||||
F src/sqlite.h.in 9ae373d11e1b11ac9c81c508523ae37f1619e739858280078ee9fb4e1e62d3ed
|
||||
F src/select.c 244f2fba5f73c7ea937333bd54280e83e218a0b652fc4540cbd72d33b0f7b4d8
|
||||
F src/shell.c.in 73c0eeb7c265d59b99219d5aa055f412f07842088d8036b6d259927d85dd1bbf
|
||||
F src/sqlite.h.in 5c54f2461a1ea529bab8499148a2b238e2d4bb571d59e8ea5322d0c190abb693
|
||||
F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479
|
||||
F src/sqlite3ext.h 0bfd049bb2088cc44c2ad54f2079d1c6e43091a4e1ce8868779b75f6c1484f1e
|
||||
F src/sqliteInt.h 0c24996d4c02eca6fd971ecbe5a565aaaf5f024265a53ce62d77ee366fcc7c6a
|
||||
F src/sqliteInt.h 9c99d7565a839ad342cdda504c4b7921bb1a24c07227b8f50b7b131245a20693
|
||||
F src/sqliteLimit.h 6d817c28a8f19af95e6f4921933b7fbbca48a962bce0eb0ec81e8bb3ef38e68b
|
||||
F src/status.c 0e72e4f6be6ccfde2488eb63210297e75f569f3ce9920f6c3d77590ec6ce5ffd
|
||||
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
|
||||
F src/tclsqlite.c d0e63ffe7944dd223bf62066d9f982cbee1978811c7fbfd889f4ba9c5baed3d1
|
||||
F src/tclsqlite.c 3c604c49e6cf4211960a9ddb9505280fd22cde32175f40884c641c0f5a286036
|
||||
F src/tclsqlite.h 65e2c761446e1c9fa0342b7d2612a703483643c8b6a316d12a65b745a4727395
|
||||
F src/test1.c 9b54135e5f1352f06b1d23d7c183f124c1f33de6ea8997cd801f0f215c43591d
|
||||
F src/test1.c 13cc07851f989141b29f7ca3c6c90f6d18f90081ab423c66716c8cb29d277d1f
|
||||
F src/test2.c 62f0830958f9075692c29c6de51b495ae8969e1bef85f239ffcd9ba5fb44a5ff
|
||||
F src/test3.c 432646f581d8af1bb495e58fc98234380250954f5d5535e507fc785eccc3987a
|
||||
F src/test4.c 0ac87fc13cdb334ab3a71823f99b6c32a6bebe5d603cd6a71d84c823d43a25a0
|
||||
@@ -813,7 +813,7 @@ F src/test_config.c 7f412406592794636d6226268e26d413850a9f799bc5f3c01afc2820b165
|
||||
F src/test_delete.c d0e8f6dc55cfc98a7c27c057fb88d512260564bf0b611482656c68b8f7f401ed
|
||||
F src/test_demovfs.c 3efa2adf4f21e10d95521721687d5ca047aea91fa62dd8cc22ac9e5a9c942383
|
||||
F src/test_devsym.c 649434ed34d0b03fbd5a6b42df80f0f9a7e53f94dd1710aad5dd8831e91c4e86
|
||||
F src/test_fs.c 6711fd4c6c05914b613cfc99918a24978452f999ce03fc8f89c9794c03b20a5b
|
||||
F src/test_fs.c a946408c81231feff4b6a2c18068f20aefe254dc82288687128af8b7520d32cb
|
||||
F src/test_func.c 858d4dddb7acf88222ebcba7cffb585f6dde83e4a15b838c0d05ccdf8d5219b9
|
||||
F src/test_hexio.c a90baa0a8ab5e7cfe2216a61c9a31cfd1f8378353a3d23e25fa94c09aa755bb0
|
||||
F src/test_init.c 1649e02448f536e53172f6b1ff873254fe9a0c6c8a4502a2d25c0cc7b11945ea
|
||||
@@ -841,8 +841,6 @@ F src/test_tclvar.c ae873248a0188459b1c16ca7cc431265dacce524399e8b46725c2b3b7e04
|
||||
F src/test_thread.c 3edb4a5b5aeb1a6e9a275dccc848ac95acab7f496b3e9230f6d2d04953a2b862
|
||||
F src/test_vdbecov.c 5c426d9cd2b351f5f9ceb30cabf8c64a63bfcad644c507e0bd9ce2f6ae1a3bf3
|
||||
F src/test_vfs.c b4135c1308516adf0dfd494e6d6c33114e03732be899eace0502919b674586b5
|
||||
F src/test_windirent.c cbee2b9a118b56b5a26b99c895adcbc861587bc66d24b88d1ad6e4c1d09dad7b
|
||||
F src/test_windirent.h f8245d8002aa0d4322192d35b0f8bbfc757479e90d60fd0beb386d3913f72cdd
|
||||
F src/test_window.c 6d80e11fba89a1796525e6f0048ff0c7789aa2c6b0b11c80827dc1437bd8ea72
|
||||
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
|
||||
@@ -853,15 +851,15 @@ F src/update.c 3e5e7ff66fa19ebe4d1b113d480639a24cc1175adbefabbd1a948a07f28e37cf
|
||||
F src/upsert.c 215328c3f91623c520ec8672c44323553f12caeb4f01b1090ebdca99fdf7b4f1
|
||||
F src/utf.c 7267c3fb9e2467020507601af3354c2446c61f444387e094c779dccd5ca62165
|
||||
F src/util.c 36fb1150062957280777655976f3f9a75db236cb8207a0770ceae8d5ec17fcd3
|
||||
F src/vacuum.c d580ceb395c1ae3d59da41cbfea60683ff7dd2b94ddf4d0f5657620159e2eeb7
|
||||
F src/vdbe.c 0feab5781141acca67bd5de84172fff902304274ec5cfe58609f005b8d160050
|
||||
F src/vdbe.h 31eddcffc1d14c76c2a20fe4e137e1ee43d44f370896fae14a067052801a3625
|
||||
F src/vdbeInt.h 5446f60e89b2aa7cdf3ab0ec4e7b01b8732cd9d52d9092a0b8b1bf700768f784
|
||||
F src/vdbeapi.c 28fab30ed0acc981aecfdcaab0a421503609078e29850eb28494816682baf0a7
|
||||
F src/vdbeaux.c 948c379976885a073b54cc7d8ffda087dc1a1095d1f5bb8df218796f8c933ac3
|
||||
F src/vdbeblob.c b1b4032cac46b41e44b957c4d00aee9851f862dfd85ecb68116ba49884b03dfd
|
||||
F src/vacuum.c 1bacdd0a81d2b5dc1c508fbf0d938c89fa78dd8d5b46ec92686d44030d4f4789
|
||||
F src/vdbe.c e505b8b879a330e8dafbe3ed9582eae2fc671b44a64748d1b58c07e4e0f527da
|
||||
F src/vdbe.h 93761ed7c6b8bc19524912fd9b9b587d41bf4f1d0ade650a00dadc10518d8958
|
||||
F src/vdbeInt.h 0bc581a9763be385e3af715e8c0a503ba8422c2b7074922faf4bb0d6ae31b15e
|
||||
F src/vdbeapi.c f9a4881a9674fec3fa13da35044a1484d3c4b95f9ec891cc8ffb02ef2b7a41df
|
||||
F src/vdbeaux.c fd2c6b19a8892c31a2adc719f156f313560f9cc490cdbd04ff08fdae5d7aedb7
|
||||
F src/vdbeblob.c b3f0640db9642fbdc88bd6ebcc83d6009514cafc98f062f675f2c8d505d82692
|
||||
F src/vdbemem.c e67d9c6484d868c879d20c70d00bf4a9058082f1d4058607ca15d50eb3aebc21
|
||||
F src/vdbesort.c 706acdc581944cf6381f75c0ccf40f2debf71cdd51c5056592f3b74a1a0c3624
|
||||
F src/vdbesort.c cb6f472e83ca12c46aa7de0ac0a9d11458b357986f2617a1c90dfb19a542ecbe
|
||||
F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf823
|
||||
F src/vdbevtab.c fc46b9cbd759dc013f0b3724549cc0d71379183c667df3a5988f7e2f1bd485f3
|
||||
F src/vtab.c 828221bdbeaaa6d62126ee6d07fd4ec0d09dcaea846f87ad01944d8b7e548859
|
||||
@@ -869,10 +867,10 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
|
||||
F src/wal.c 20be6f0a25a80b7897cf2a5369bfd37ef198e6f0b6cdef16d83eee856056b159
|
||||
F src/wal.h ba252daaa94f889f4b2c17c027e823d9be47ce39da1d3799886bbd51f0490452
|
||||
F src/walker.c d5006d6b005e4ea7302ad390957a8d41ed83faa177e412f89bc5600a7462a014
|
||||
F src/where.c 09fea7e821556e8f8851b68edc39d14becb6ee64bea88e34b05ceeaadcdfaecd
|
||||
F src/whereInt.h ecdbfb5551cf394f04ec7f0bc7ad963146d80eee3071405ac29aa84950128b8e
|
||||
F src/wherecode.c 66684a11713667f532a69214ac552acee7e0825e2f1b71abaebe05511d190fb4
|
||||
F src/whereexpr.c 1c60db88b6e8472f4864b98014d1b2d9d16bdd42d5d181ec515acbcdeddc18db
|
||||
F src/where.c 6a9266dd1a559d48d8c7ca670a3e80143c7913153f7d1ceb0a4eca1087318951
|
||||
F src/whereInt.h 8d94cb116c9e06205c3d5ac87af065fc044f8cf08bfdccd94b6ea1c1308e65da
|
||||
F src/wherecode.c 2a2d2993fd98c46f525f71b3bfd330fde73d8613aa0ff3e20402dd1fc63470af
|
||||
F src/whereexpr.c d007dc41364de5902181739632380afd671e14f0c5cc9978e64a2c6df8f28c6c
|
||||
F src/window.c d01227141f622f24fbe36ca105fbe6ef023f9fd98f1ccd65da95f88886565db5
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/affinity2.test 4d7a34d328e58ca2a2d78fd76c27614a41ca7ddf4312ded9c68c04f430b3b47d
|
||||
@@ -964,7 +962,7 @@ F test/bestindex8.test b63a4f171a2c83d481bb14c431a8b72e85d27b2ffdaa0435a95d58ca9
|
||||
F test/bestindex9.test 1a4b93db117fd8abe74ae9be982f86aa72f01e60cd4ac541e6ede39673a451a0
|
||||
F test/bestindexA.test e1b5def6b190797cacf008e6815ffb78fb30261999030d60a728d572eef44c7f
|
||||
F test/bestindexB.test 328b97b69cd1a20928d5997f9ecb04d2e00f1d18e19ab27f9e9adb44d7bc51ce
|
||||
F test/bestindexC.test 2df6ada16d8f00d9bb6a9664d9c323560aeed0e0ebc7a32b99d85d70037fd250
|
||||
F test/bestindexC.test 95b4a527b1a5d07951d731604a6d4cf7e5a806b39cea0e7819d4c9667e11c3fc
|
||||
F test/bestindexD.test 6a8f6f84990bcf17dfa59652a1f935beddb7afd96f8302830fbc86b0a13df3c3
|
||||
F test/between.test b9a65fb065391980119e8a781a7409d3fcf059d89968279c750e190a9a1d5263
|
||||
F test/bigfile.test aa74f4e5db51c8e54a1d9de9fa65d01d1eb20b59
|
||||
@@ -1084,6 +1082,7 @@ F test/dbfuzz.c fc566102f72c8af84ae8077b4faf7f056c571e6fa7a32e98b66e42b7505f47b6
|
||||
F test/dbfuzz001.test 6c9a4622029d69dc38926f115864b055cb2f39badd25ec22cbfb130c8ba8e9c3
|
||||
F test/dbfuzz2-seed1.db e6225c6f3d7b63f9c5b6867146a5f329d997ab105bee64644dc2b3a2f2aebaee
|
||||
F test/dbfuzz2.c 4b3c12de4d98b1b2d908ab03d217d4619e47c8b23d5e67f8a6f2b1bdee7cae23
|
||||
F test/dblwidth-a.sql eb4141518610e52f931a55a984310075e98dc31eee5a28ae806b1e35377be85a
|
||||
F test/dbpage.test 63fab1eb026bada121107e53436fa749bbf83281dc9dea17af422f7a5c0f289f
|
||||
F test/dbpagefault.test ea39de2ca86041a9c6df1135645180a76d0a8da93ac159e2fafe38e39636530b
|
||||
F test/dbstatus.test 4a4221a883025ffd39696b3d1b3910b928fb097d77e671351acb35f3aed42759
|
||||
@@ -1132,7 +1131,7 @@ F test/enc.test b5503a87b31cea8a5084c6e447383f9ca08933bd2f29d97b6b6201081b2343eb
|
||||
F test/enc2.test 872afe58db772e7dfa1ad8e0759f8cc820e9efc8172d460fae83023101c2e435
|
||||
F test/enc3.test 55ef64416d72975c66167310a51dc9fc544ba3ae4858b8d5ab22f4cb6500b087
|
||||
F test/enc4.test c8f1ce3618508fd0909945beb8b8831feef2c020
|
||||
F test/eqp.test 82f221e8cd588434d7f3bba9a0f4c78cbe7a541615a41632e12f50608bfb4a99
|
||||
F test/eqp.test 746db9fe11629a0d00328e1721cc2a2e4726d574b677ab14de35fd914f54cc82
|
||||
F test/eqp2.test 6e8996148de88f0e7670491e92e712a2920a369b4406f21a27c3c9b6a46b68dd
|
||||
F test/errmsg.test eae9f091eb39ce7e20305de45d8e5d115b68fa856fba4ea6757b6ca3705ff7f9
|
||||
F test/errofst1.test 6da78363739ba8991f498396ab331b5d64e7ab5c4172c12b5884683ef523ac53
|
||||
@@ -1141,6 +1140,9 @@ F test/exclusive.test 7ff63be7503990921838d5c9f77f6e33e68e48ed1a9d48cd28745bf650
|
||||
F test/exclusive2.test cd70b1d9c6fffd336f9795b711dcc5d9ceba133ad3f7001da3fda63615bdc91e
|
||||
F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7
|
||||
F test/exists.test 79a75323c78f02bbe9c251ea502a092f9ef63dac
|
||||
F test/existsexpr.test 40ddd9500109579dd949cd15bbb4e3a88f79f905d1f31905b9493651f60aacf6
|
||||
F test/existsexpr2.test dc23e76389eff3d29f6488ff733012a3560cd67ec8cfaecbecd52cced5d5af11
|
||||
F test/existsfault.test ff41c11f3052c1bbd4f8dd557802310026253d67d7c4e3a180c16d2f0862973e
|
||||
F test/expr.test 4ada8eb822c45ef27a36851a258004d43c1e95e7c82585a1217e732084e4482c
|
||||
F test/expr2.test c27327ae9c017a7ff6280123f67aff496f912da74d78c888926d68b46ec75fd8
|
||||
F test/exprfault.test da33606d799718e2f8e34efd0e5858884a1ad87f608774c552a7f5517cc27181
|
||||
@@ -1301,7 +1303,7 @@ F test/genesis.tcl 1e2e2e8e5cc4058549a154ff1892fe5c9de19f98
|
||||
F test/having.test a89236dd8d55aa50c4805f82ac9daf64d477a44d712d8209c118978d0ca21ec9
|
||||
F test/hexlit.test 4a6a5f46e3c65c4bf1fa06f5dd5a9507a5627751
|
||||
F test/hidden.test 23c1393a79e846d68fd902d72c85d5e5dcf98711
|
||||
F test/hook.test 3481a68009fe143e3363fca922f6fc7a1e1f3776c51e42777f1a01b26ad2a9c8
|
||||
F test/hook.test 2d89bf9480646feb8093be3a58ea502d6521906779ed960de31dd9c4502c0541
|
||||
F test/hook2.test b9ff3b8c6519fb67f33192f1afe86e7782ee4ac8
|
||||
F test/icu.test 8da7d52cd9722c82f33b0466ed915460cb03c23a38f18a9a2d3ff97da9a4a8c0
|
||||
F test/ieee754.test b0945d12be7d255f3dfa18e2511b17ca37e0edd2b803231c52d05b86c04ab26e
|
||||
@@ -1316,7 +1318,7 @@ F test/in7.test d9efdee00b074a60c6343993b2eda78bc369ab080dad864513c73f8aca89d566
|
||||
F test/incrblob.test c9b96afc292aeff43d6687bcb09b0280aa599822
|
||||
F test/incrblob2.test a494c9e848560039a23974b9119cfc2cf3ad3bd15cc2694ee6367ae537ef8f1f
|
||||
F test/incrblob3.test 67621a04b3084113bf38ce03797d70eca012d9d8f948193b8f655df577b0da6f
|
||||
F test/incrblob4.test 21a52a6843a56cdcce968c6a86b72a7066d0e6ba
|
||||
F test/incrblob4.test 10f4537febe1774c02f8d490d393322b4a50898d4027868b67055e9c3adc22db
|
||||
F test/incrblob_err.test 89372a28f1d98254f03fed705f9efcd34ef61a674df16d2dbb4726944a2de5e9
|
||||
F test/incrblobfault.test de274b1e329169c2c3438f9528994807ea8201ebf38ae9f157d34bf3ec0cc549
|
||||
F test/incrcorrupt.test 6c567fbf870aa9e91866fe52ce6f200cd548939a
|
||||
@@ -1388,7 +1390,7 @@ F test/json/json-generator.tcl dc0dd0f393800c98658fc4c47eaa6af29d4e17527380cd286
|
||||
F test/json/json-q1.txt 65f9d1cdcc4cffa9823fb73ed936aae5658700cd001fde448f68bfb91c807307
|
||||
F test/json/json-speed-check.sh 7d5898808ce7542762318306ae6075a30f5e7ee115c4a409f487e123afe91d88 x
|
||||
F test/json/jsonb-q1.txt 1e180fe6491efab307e318b22879e3a736ac9a96539bbde7911a13ee5b33abc7
|
||||
F test/json101.test 8237a484c256965eab1678fd950a32ac56325bb7d0dadbd095a46b0ddd95d62b
|
||||
F test/json101.test cf53254f0f0c1399a01b21fc58fee0e63a12a556be91b9ee9faccdb8b82c083c
|
||||
F test/json102.test 9b2e5ada10845ff84853b3feaae2ce51ce7145ae458f74c6a6cecc6ef6ee3ae1
|
||||
F test/json103.test 355746a6b66aa438f214b4fae454b13068fad2444b5f693e0d538ad1c059b264
|
||||
F test/json104.test 1b844a70cddcfa2e4cd81a5db0657b2e61e7f00868310f24f56a9ba0114348c1
|
||||
@@ -1495,7 +1497,7 @@ F test/notify1.test 669b2b743618efdc18ca4b02f45423d5d2304abf
|
||||
F test/notify2.test 2ecabaa1305083856b7c39cf32816b612740c161
|
||||
F test/notify3.test 796c7b7157f55c93b4e672b724e9c923a6fc6aa72ac419379a623e2350472e22
|
||||
F test/notnull.test a37b663d5bb728d66fc182016613fb8e4a0a4bbf3d75b8876a7527f7d4ed3f18
|
||||
F test/notnull2.test 2ac7b4e04917148c7a1a9ed36df20150175ce942f07f5714375b29acbaca7106
|
||||
F test/notnull2.test 5b7dd6e82c409b2d011ad6acf19ae4bf0816a9c69ccf600b529d7405d7c49874
|
||||
F test/notnullfault.test fc4bb7845582a2b3db376001ef49118393b1b11abe0d24adb03db057ee2b73d5
|
||||
F test/null.test b7ff206a1c60fe01aa2abd33ef9ea83c93727d993ca8a613de86e925c9f2bc6f
|
||||
F test/nulls1.test 7a5e4346ee4285034100b4cd20e6784f16a9d6c927e44ecdf10034086bbee9c9
|
||||
@@ -1702,7 +1704,7 @@ F test/statfault.test 064f43379e4992b5221b7d9ac887c313b3191f85cce605d78e416fc404
|
||||
F test/stmt.test 54ed2cc0764bf3e48a058331813c3dbd19fc1d0827c3d8369914a5d8f564ec75
|
||||
F test/stmtrand.test 340e2ea4841c5cdc02d36e33739769c5d907ab529b12bb535407def0e413ca17
|
||||
F test/stmtvtab1.test 6873dfb24f8e79cbb5b799b95c2e4349060eb7a3b811982749a84b359468e2d5
|
||||
F test/strict1.test 4d2b492152b984fd7e8196d23eb88e2ccb0ef9e46ca2f96c2ce7147ceef9d168
|
||||
F test/strict1.test a7f9091603fe71cdc62baab0766684cba12a97ec69bfbb70be965532669cd77a
|
||||
F test/strict2.test b22c7a98b5000aef937f1990776497f0e979b1a23bc4f63e2d53b00e59b20070
|
||||
F test/subjournal.test 8d4e2572c0ee9a15549f0d8e40863161295107e52f07a3e8012a2e1fdd093c49
|
||||
F test/subquery.test 23087f9b1c15ab9cc5231d04946bdebc51db527c95eb9d7434a2222127e17a84
|
||||
@@ -1725,7 +1727,7 @@ F test/tabfunc01.test 8a484fe8b19fc24844f72ca1ceb7c9ae8c9a6bca000a5c6ccab5d89f5c
|
||||
F test/table.test e87294bf1c80bfd7792142b84ab32ea5beb4f3f71e535d7fb263a6b2068377bf
|
||||
F test/tableapi.test e37c33e6be2276e3a96bb54b00eea7f321277115d10e5b30fdb52a112b432750
|
||||
F test/tableopts.test dba698ba97251017b7c80d738c198d39ab747930
|
||||
F test/tclsqlite.test ad0bbd92edabe64cc91d990a0748142fe5ab962d74ac71fa3bfa94d50d2f4c87
|
||||
F test/tclsqlite.test 3f697424cfc1cdc9c076ec0cadb0e700f059400a3e3ce134b7d856fc9f880e1c
|
||||
F test/tempdb.test 4cdaa23ddd8acb4d79cbb1b68ccdfd09b0537aaba909ca69a876157c2a2cbd08
|
||||
F test/tempdb2.test 353864e96fd3ae2f70773d0ffbf8b1fe48589b02c2ec05013b540879410c3440
|
||||
F test/tempfault.test 0c0d349c9a99bf5f374655742577f8712c647900
|
||||
@@ -1959,7 +1961,7 @@ F test/uri.test 1250724af9beeed2d6c3716f5b990c483200c54f408d3c0ec9543a3c7961f8fc
|
||||
F test/uri2.test 9d3ba7a53ee167572d53a298ee4a5d38ec4a8fb7
|
||||
F test/utf16align.test 9fde0bb5d3a821594aa68c6829ab9c5453a084384137ebb9f6153e2d678039da
|
||||
F test/vacuum-into.test 5a489714feecfdabfc7b293be4111564a173dee92c0d6818dd0207f3ade65783
|
||||
F test/vacuum.test ce91c39f7f91a4273bf620efad21086b5aa6ef1d
|
||||
F test/vacuum.test f3b2257a4fcd659513c866a5d9e5f70999cc58fd5d74e979290385fa350b79ee
|
||||
F test/vacuum2.test 9fd45ce6ce29f5614c249e03938d3567c06a9e772d4f155949f8eafe2d8af520
|
||||
F test/vacuum3.test d9d9a04ee58c485b94694fd4f68cffaba49c32234fdefe1ac1a622c5e17d4ce3
|
||||
F test/vacuum4.test 7ea76b769fffeb41f925303b04cbcf5a5bbeabe55e4c60ae754ff24eeeb7c010
|
||||
@@ -2125,7 +2127,7 @@ F tool/GetTclKit.bat d84033c6a93dfe735d247f48ba00292a1cc284dcf69963e5e672444e045
|
||||
F tool/Replace.cs 02c67258801c2fb5f63231e0ac0f220b4b36ba91
|
||||
F tool/build-all-msvc.bat 1960a7a3e5d8176c4329e31476f6e3dfa9543675355fa9020a569f4452628458 x
|
||||
F tool/build-shell.sh 369c4b171cc877ad974fef691e4da782b4c1e99fe8f4361316c735f64d49280f
|
||||
F tool/buildtclext.tcl 85d1bcd5410d0a4c739e2d013106f03674d082cc4d573bcc57a68546b6db67cd
|
||||
F tool/buildtclext.tcl d09b753d7858314104eeaf5f4def85d35784470279809e47a633f142226f2b3f
|
||||
F tool/cg_anno.tcl c1f875f5a4c9caca3d59937b16aff716f8b1883935f1b4c9ae23124705bc8099 x
|
||||
F tool/checkSpacing.c 810e51703529a204fc4e1eb060e9ab663e3c06d2
|
||||
F tool/cktclsh.sh 6075eef9c6b9ba4b38fef2ca2a66d25f2311bd3c610498d18a9b01f861629cca
|
||||
@@ -2145,7 +2147,7 @@ F tool/genfkey.README e550911fa984c8255ebed2ef97824125d83806eb5232582700de949edf
|
||||
F tool/genfkey.test b6afd7b825d797a1e1274f519ab5695373552ecad5cd373530c63533638a5a4f
|
||||
F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
|
||||
F tool/index_usage.c f62a0c701b2c7ff2f3e21d206f093c123f222dbf07136a10ffd1ca15a5c706c5
|
||||
F tool/lemon.c 00535f27e61ae8200ba8402c1753483de39eef2f59b60da8b7951c458a32e017
|
||||
F tool/lemon.c 8f6c122e5727cb0e5f302b8efc91489b1947a8d98206d7a1b1cfc0ed685b6e7c
|
||||
F tool/lempar.c bdffd3b233a4e4e78056c9c01fadd2bb3fe902435abde3bce3d769fdf0d5cca2
|
||||
F tool/libvers.c caafc3b689638a1d88d44bc5f526c2278760d9b9
|
||||
F tool/loadfts.c c3c64e4d5e90e8ba41159232c2189dba4be7b862
|
||||
@@ -2162,7 +2164,7 @@ F tool/mkopcodec.tcl 33d20791e191df43209b77d37f0ff0904620b28465cca6990cf8d60da61
|
||||
F tool/mkopcodeh.tcl 2b4e6967a670ef21bf53a164964c35c6163277d002a4c6f56fa231d68c88d023
|
||||
F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa
|
||||
F tool/mkpragmatab.tcl 3801ce32f8c55fe63a3b279f231fb26c2c1a2ea9a09d2dd599239d87a609acec
|
||||
F tool/mkshellc.tcl 9ce74de0fa904a2c56a96f8d8b5261246bacb0eaa8d7e184f9e18ff94145ebbc
|
||||
F tool/mkshellc.tcl bab0a72a68384181a5706712dfdf6815f6526446d4e8aacace2de5e80cda91b2
|
||||
F tool/mksourceid.c 36aa8020014aed0836fd13c51d6dc9219b0df1761d6b5f58ff5b616211b079b9
|
||||
F tool/mksqlite3c-noext.tcl 351c55256213154cabb051a3c870ef9f4487de905015141ae50dc7578a901b84
|
||||
F tool/mksqlite3c.tcl f11b63445c4840509248bd4aa151a81aea25d5415fef71943c8d436eba4f3b3c
|
||||
@@ -2191,7 +2193,7 @@ F tool/spellsift.tcl 52b4b04dc4333c7ab024f09d9d66ed6b6f7c6eb00b38497a09f338fa55d
|
||||
F tool/split-sqlite3c.tcl 4969fd642dad0ea483e4e104163021d92baf98f6a8eac981fe48525f9b873430
|
||||
F tool/sqldiff.c 134be7866be19f8beb32043d5aea5657f01aaeae2df8d33d758ff722c78666b9
|
||||
F tool/sqlite3_analyzer.c.in 14f02cb5ec3c264cd6107d1f1dad77092b1cf440fc196c30b69ae87b56a1a43b
|
||||
F tool/sqlite3_rsync.c 4e152221a51ed2974f08100d86500c2cda549472e48e09699a8011bfb956d00c
|
||||
F tool/sqlite3_rsync.c c1e1a737ebb458a4ccbbaa21e5eaa878448fef1aaa71b879b9a685785e28987f
|
||||
F tool/sqltclsh.c.in 1bcc2e9da58fadf17b0bf6a50e68c1159e602ce057210b655d50bad5aaaef898
|
||||
F tool/sqltclsh.tcl 862f4cf1418df5e1315b5db3b5ebe88969e2a784525af5fbf9596592f14ed848
|
||||
F tool/src-verify.c 6c655d9a8d6b30f3648fc78a79bf3838ed68f8543869d380c43ea9f17b3b8501
|
||||
@@ -2209,10 +2211,8 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350
|
||||
F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
|
||||
F tool/warnings.sh 1ad0169b022b280bcaaf94a7fa231591be96b514230ab5c98fbf15cd7df842dd
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
P f39a0865d336d4a964627d4819ce7ba8dafd43a0e8e2a4431ea431ef73dd96c3
|
||||
R 5c3da0295a5c868b2c70cbb25c7978d3
|
||||
T +sym-release *
|
||||
T +sym-version-3.50.4 *
|
||||
P 840646df0696706bd4dd7a04dfc8b16ad80c24cbcbc57a22c9e65a93cc17af2b
|
||||
R e35575e41e9bd119ef19709aeaa4c684
|
||||
U drh
|
||||
Z e0e73bda1cca0dba2213fe2c15c764d6
|
||||
Z 4da98ddcb106ab62869cc3825500f67f
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
4d8adfb30e03f9cf27f800a2c1ba3c48fb4ca1b08b0f5ed59a4d5ecbf45e20a3
|
||||
1b9b124f9a35ebd1ac4ea70ef1ee08a4c82c11da690d4164f6b785a6fd9730d9
|
||||
|
||||
+96
-14
@@ -107,6 +107,7 @@ struct Bitvec {
|
||||
} u;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** Create a new bitmap object able to handle bits between 0 and iSize,
|
||||
** inclusive. Return a pointer to the new object. Return NULL if
|
||||
@@ -295,6 +296,52 @@ u32 sqlite3BitvecSize(Bitvec *p){
|
||||
return p->iSize;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/*
|
||||
** Show the content of a Bitvec option and its children. Indent
|
||||
** everything by n spaces. Add x to each bitvec value.
|
||||
**
|
||||
** From a debugger such as gdb, one can type:
|
||||
**
|
||||
** call sqlite3ShowBitvec(p)
|
||||
**
|
||||
** For some Bitvec p and see a recursive view of the Bitvec's content.
|
||||
*/
|
||||
static void showBitvec(Bitvec *p, int n, unsigned x){
|
||||
int i;
|
||||
if( p==0 ){
|
||||
printf("NULL\n");
|
||||
return;
|
||||
}
|
||||
printf("Bitvec 0x%p iSize=%u", p, p->iSize);
|
||||
if( p->iSize<=BITVEC_NBIT ){
|
||||
printf(" bitmap\n");
|
||||
printf("%*s bits:", n, "");
|
||||
for(i=1; i<=BITVEC_NBIT; i++){
|
||||
if( sqlite3BitvecTest(p,i) ) printf(" %u", x+(unsigned)i);
|
||||
}
|
||||
printf("\n");
|
||||
}else if( p->iDivisor==0 ){
|
||||
printf(" hash with %u entries\n", p->nSet);
|
||||
printf("%*s bits:", n, "");
|
||||
for(i=0; i<BITVEC_NINT; i++){
|
||||
if( p->u.aHash[i] ) printf(" %u", x+(unsigned)p->u.aHash[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}else{
|
||||
printf(" sub-bitvec with iDivisor=%u\n", p->iDivisor);
|
||||
for(i=0; i<BITVEC_NPTR; i++){
|
||||
if( p->u.apSub[i]==0 ) continue;
|
||||
printf("%*s apSub[%d]=", n, "", i);
|
||||
showBitvec(p->u.apSub[i], n+4, i*p->iDivisor);
|
||||
}
|
||||
}
|
||||
}
|
||||
void sqlite3ShowBitvec(Bitvec *p){
|
||||
showBitvec(p, 0, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_UNTESTABLE
|
||||
/*
|
||||
** Let V[] be an array of unsigned characters sufficient to hold
|
||||
@@ -306,6 +353,7 @@ u32 sqlite3BitvecSize(Bitvec *p){
|
||||
#define CLEARBIT(V,I) V[I>>3] &= ~(BITVEC_TELEM)(1<<(I&7))
|
||||
#define TESTBIT(V,I) (V[I>>3]&(1<<(I&7)))!=0
|
||||
|
||||
|
||||
/*
|
||||
** This routine runs an extensive test of the Bitvec code.
|
||||
**
|
||||
@@ -314,7 +362,7 @@ u32 sqlite3BitvecSize(Bitvec *p){
|
||||
** by 0, 1, or 3 operands, depending on the opcode. Another
|
||||
** opcode follows immediately after the last operand.
|
||||
**
|
||||
** There are 6 opcodes numbered from 0 through 5. 0 is the
|
||||
** There are opcodes numbered starting with 0. 0 is the
|
||||
** "halt" opcode and causes the test to end.
|
||||
**
|
||||
** 0 Halt and return the number of errors
|
||||
@@ -323,18 +371,25 @@ u32 sqlite3BitvecSize(Bitvec *p){
|
||||
** 3 N Set N randomly chosen bits
|
||||
** 4 N Clear N randomly chosen bits
|
||||
** 5 N S X Set N bits from S increment X in array only, not in bitvec
|
||||
** 6 Invoice sqlite3ShowBitvec() on the Bitvec object so far
|
||||
** 7 X Show compile-time parameters and the hash of X
|
||||
**
|
||||
** The opcodes 1 through 4 perform set and clear operations are performed
|
||||
** on both a Bitvec object and on a linear array of bits obtained from malloc.
|
||||
** Opcode 5 works on the linear array only, not on the Bitvec.
|
||||
** Opcode 5 is used to deliberately induce a fault in order to
|
||||
** confirm that error detection works.
|
||||
** confirm that error detection works. Opcodes 6 and greater are
|
||||
** state output opcodes. Opcodes 6 and greater are no-ops unless
|
||||
** SQLite has been compiled with SQLITE_DEBUG.
|
||||
**
|
||||
** At the conclusion of the test the linear array is compared
|
||||
** against the Bitvec object. If there are any differences,
|
||||
** an error is returned. If they are the same, zero is returned.
|
||||
**
|
||||
** If a memory allocation error occurs, return -1.
|
||||
**
|
||||
** sz is the size of the Bitvec. Or if sz is negative, make the size
|
||||
** 2*(unsigned)(-sz) and disabled the linear vector check.
|
||||
*/
|
||||
int sqlite3BitvecBuiltinTest(int sz, int *aOp){
|
||||
Bitvec *pBitvec = 0;
|
||||
@@ -345,10 +400,15 @@ int sqlite3BitvecBuiltinTest(int sz, int *aOp){
|
||||
|
||||
/* Allocate the Bitvec to be tested and a linear array of
|
||||
** bits to act as the reference */
|
||||
pBitvec = sqlite3BitvecCreate( sz );
|
||||
pV = sqlite3MallocZero( (7+(i64)sz)/8 + 1 );
|
||||
if( sz<=0 ){
|
||||
pBitvec = sqlite3BitvecCreate( 2*(unsigned)(-sz) );
|
||||
pV = 0;
|
||||
}else{
|
||||
pBitvec = sqlite3BitvecCreate( sz );
|
||||
pV = sqlite3MallocZero( (7+(i64)sz)/8 + 1 );
|
||||
}
|
||||
pTmpSpace = sqlite3_malloc64(BITVEC_SZ);
|
||||
if( pBitvec==0 || pV==0 || pTmpSpace==0 ) goto bitvec_end;
|
||||
if( pBitvec==0 || pTmpSpace==0 || (pV==0 && sz>0) ) goto bitvec_end;
|
||||
|
||||
/* NULL pBitvec tests */
|
||||
sqlite3BitvecSet(0, 1);
|
||||
@@ -357,6 +417,24 @@ int sqlite3BitvecBuiltinTest(int sz, int *aOp){
|
||||
/* Run the program */
|
||||
pc = i = 0;
|
||||
while( (op = aOp[pc])!=0 ){
|
||||
if( op>=6 ){
|
||||
#ifdef SQLITE_DEBUG
|
||||
if( op==6 ){
|
||||
sqlite3ShowBitvec(pBitvec);
|
||||
}else if( op==7 ){
|
||||
printf("BITVEC_SZ = %d (%d by sizeof)\n",
|
||||
BITVEC_SZ, (int)sizeof(Bitvec));
|
||||
printf("BITVEC_USIZE = %d\n", (int)BITVEC_USIZE);
|
||||
printf("BITVEC_NELEM = %d\n", (int)BITVEC_NELEM);
|
||||
printf("BITVEC_NBIT = %d\n", (int)BITVEC_NBIT);
|
||||
printf("BITVEC_NINT = %d\n", (int)BITVEC_NINT);
|
||||
printf("BITVEC_MXHASH = %d\n", (int)BITVEC_MXHASH);
|
||||
printf("BITVEC_NPTR = %d\n", (int)BITVEC_NPTR);
|
||||
}
|
||||
#endif
|
||||
pc++;
|
||||
continue;
|
||||
}
|
||||
switch( op ){
|
||||
case 1:
|
||||
case 2:
|
||||
@@ -378,12 +456,12 @@ int sqlite3BitvecBuiltinTest(int sz, int *aOp){
|
||||
pc += nx;
|
||||
i = (i & 0x7fffffff)%sz;
|
||||
if( (op & 1)!=0 ){
|
||||
SETBIT(pV, (i+1));
|
||||
if( pV ) SETBIT(pV, (i+1));
|
||||
if( op!=5 ){
|
||||
if( sqlite3BitvecSet(pBitvec, i+1) ) goto bitvec_end;
|
||||
}
|
||||
}else{
|
||||
CLEARBIT(pV, (i+1));
|
||||
if( pV ) CLEARBIT(pV, (i+1));
|
||||
sqlite3BitvecClear(pBitvec, i+1, pTmpSpace);
|
||||
}
|
||||
}
|
||||
@@ -393,14 +471,18 @@ int sqlite3BitvecBuiltinTest(int sz, int *aOp){
|
||||
** match (rc==0). Change rc to non-zero if a discrepancy
|
||||
** is found.
|
||||
*/
|
||||
rc = sqlite3BitvecTest(0,0) + sqlite3BitvecTest(pBitvec, sz+1)
|
||||
+ sqlite3BitvecTest(pBitvec, 0)
|
||||
+ (sqlite3BitvecSize(pBitvec) - sz);
|
||||
for(i=1; i<=sz; i++){
|
||||
if( (TESTBIT(pV,i))!=sqlite3BitvecTest(pBitvec,i) ){
|
||||
rc = i;
|
||||
break;
|
||||
if( pV ){
|
||||
rc = sqlite3BitvecTest(0,0) + sqlite3BitvecTest(pBitvec, sz+1)
|
||||
+ sqlite3BitvecTest(pBitvec, 0)
|
||||
+ (sqlite3BitvecSize(pBitvec) - sz);
|
||||
for(i=1; i<=sz; i++){
|
||||
if( (TESTBIT(pV,i))!=sqlite3BitvecTest(pBitvec,i) ){
|
||||
rc = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
rc = 0;
|
||||
}
|
||||
|
||||
/* Free allocated structure */
|
||||
|
||||
+45
-11
@@ -872,7 +872,7 @@ static int btreeMoveto(
|
||||
assert( nKey==(i64)(int)nKey );
|
||||
pIdxKey = sqlite3VdbeAllocUnpackedRecord(pKeyInfo);
|
||||
if( pIdxKey==0 ) return SQLITE_NOMEM_BKPT;
|
||||
sqlite3VdbeRecordUnpack(pKeyInfo, (int)nKey, pKey, pIdxKey);
|
||||
sqlite3VdbeRecordUnpack((int)nKey, pKey, pIdxKey);
|
||||
if( pIdxKey->nField==0 || pIdxKey->nField>pKeyInfo->nAllField ){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
}else{
|
||||
@@ -1929,10 +1929,10 @@ static int freeSpace(MemPage *pPage, int iStart, int iSize){
|
||||
assert( pPage->pBt!=0 );
|
||||
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
|
||||
assert( CORRUPT_DB || iStart>=pPage->hdrOffset+6+pPage->childPtrSize );
|
||||
assert( CORRUPT_DB || iEnd <= pPage->pBt->usableSize );
|
||||
assert( CORRUPT_DB || iEnd <= (int)pPage->pBt->usableSize );
|
||||
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
|
||||
assert( iSize>=4 ); /* Minimum cell size is 4 */
|
||||
assert( CORRUPT_DB || iStart<=pPage->pBt->usableSize-4 );
|
||||
assert( CORRUPT_DB || iStart<=(int)pPage->pBt->usableSize-4 );
|
||||
|
||||
/* The list of freeblocks must be in ascending order. Find the
|
||||
** spot on the list where iStart should be inserted.
|
||||
@@ -2856,6 +2856,7 @@ static int removeFromSharingList(BtShared *pBt){
|
||||
sqlite3_mutex_leave(pMainMtx);
|
||||
return removed;
|
||||
#else
|
||||
UNUSED_PARAMETER( pBt );
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
@@ -3073,6 +3074,10 @@ int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){
|
||||
sqlite3BtreeEnter(p);
|
||||
pBt->nReserveWanted = (u8)nReserve;
|
||||
x = pBt->pageSize - pBt->usableSize;
|
||||
if( x==nReserve && (pageSize==0 || (u32)pageSize==pBt->pageSize) ){
|
||||
sqlite3BtreeLeave(p);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
if( nReserve<x ) nReserve = x;
|
||||
if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){
|
||||
sqlite3BtreeLeave(p);
|
||||
@@ -5662,6 +5667,30 @@ int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Set *pRes to 1 (true) if the BTree pointed to by cursor pCur contains zero
|
||||
** rows of content. Set *pRes to 0 (false) if the table contains content.
|
||||
** Return SQLITE_OK on success or some error code (ex: SQLITE_NOMEM) if
|
||||
** something goes wrong.
|
||||
*/
|
||||
int sqlite3BtreeIsEmpty(BtCursor *pCur, int *pRes){
|
||||
int rc;
|
||||
|
||||
assert( cursorOwnsBtShared(pCur) );
|
||||
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
|
||||
if( pCur->eState==CURSOR_VALID ){
|
||||
*pRes = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
rc = moveToRoot(pCur);
|
||||
if( rc==SQLITE_EMPTY ){
|
||||
*pRes = 1;
|
||||
rc = SQLITE_OK;
|
||||
}else{
|
||||
*pRes = 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* The cursors is CURSOR_VALID and has BTCF_AtLast set. Verify that
|
||||
** this flags are true for a consistent database.
|
||||
@@ -5881,8 +5910,8 @@ moveto_table_finish:
|
||||
}
|
||||
|
||||
/*
|
||||
** Compare the "idx"-th cell on the page the cursor pCur is currently
|
||||
** pointing to to pIdxKey using xRecordCompare. Return negative or
|
||||
** Compare the "idx"-th cell on the page pPage against the key
|
||||
** pointing to by pIdxKey using xRecordCompare. Return negative or
|
||||
** zero if the cell is less than or equal pIdxKey. Return positive
|
||||
** if unknown.
|
||||
**
|
||||
@@ -5897,12 +5926,11 @@ moveto_table_finish:
|
||||
** a positive value as that will cause the optimization to be skipped.
|
||||
*/
|
||||
static int indexCellCompare(
|
||||
BtCursor *pCur,
|
||||
MemPage *pPage,
|
||||
int idx,
|
||||
UnpackedRecord *pIdxKey,
|
||||
RecordCompare xRecordCompare
|
||||
){
|
||||
MemPage *pPage = pCur->pPage;
|
||||
int c;
|
||||
int nCell; /* Size of the pCell cell in bytes */
|
||||
u8 *pCell = findCellPastPtr(pPage, idx);
|
||||
@@ -6011,14 +6039,14 @@ int sqlite3BtreeIndexMoveto(
|
||||
){
|
||||
int c;
|
||||
if( pCur->ix==pCur->pPage->nCell-1
|
||||
&& (c = indexCellCompare(pCur, pCur->ix, pIdxKey, xRecordCompare))<=0
|
||||
&& (c = indexCellCompare(pCur->pPage,pCur->ix,pIdxKey,xRecordCompare))<=0
|
||||
&& pIdxKey->errCode==SQLITE_OK
|
||||
){
|
||||
*pRes = c;
|
||||
return SQLITE_OK; /* Cursor already pointing at the correct spot */
|
||||
}
|
||||
if( pCur->iPage>0
|
||||
&& indexCellCompare(pCur, 0, pIdxKey, xRecordCompare)<=0
|
||||
&& indexCellCompare(pCur->pPage, 0, pIdxKey, xRecordCompare)<=0
|
||||
&& pIdxKey->errCode==SQLITE_OK
|
||||
){
|
||||
pCur->curFlags &= ~(BTCF_ValidOvfl|BTCF_AtLast);
|
||||
@@ -6235,7 +6263,7 @@ i64 sqlite3BtreeRowCountEst(BtCursor *pCur){
|
||||
|
||||
n = pCur->pPage->nCell;
|
||||
for(i=0; i<pCur->iPage; i++){
|
||||
n *= pCur->apPage[i]->nCell;
|
||||
n *= pCur->apPage[i]->nCell+1;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
@@ -8692,7 +8720,12 @@ static int balance_nonroot(
|
||||
** of the right-most new sibling page is set to the value that was
|
||||
** originally in the same field of the right-most old sibling page. */
|
||||
if( (pageFlags & PTF_LEAF)==0 && nOld!=nNew ){
|
||||
MemPage *pOld = (nNew>nOld ? apNew : apOld)[nOld-1];
|
||||
MemPage *pOld;
|
||||
if( nNew>nOld ){
|
||||
pOld = apNew[nOld-1];
|
||||
}else{
|
||||
pOld = apOld[nOld-1];
|
||||
}
|
||||
memcpy(&apNew[nNew-1]->aData[8], &pOld->aData[8], 4);
|
||||
}
|
||||
|
||||
@@ -11324,6 +11357,7 @@ void *sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){
|
||||
*/
|
||||
int sqlite3BtreeSchemaLocked(Btree *p){
|
||||
int rc;
|
||||
UNUSED_PARAMETER(p); /* only used in DEBUG builds */
|
||||
assert( sqlite3_mutex_held(p->db->mutex) );
|
||||
sqlite3BtreeEnter(p);
|
||||
rc = querySharedCacheTableLock(p, SCHEMA_ROOT, READ_LOCK);
|
||||
|
||||
@@ -317,6 +317,7 @@ struct BtreePayload {
|
||||
int sqlite3BtreeInsert(BtCursor*, const BtreePayload *pPayload,
|
||||
int flags, int seekResult);
|
||||
int sqlite3BtreeFirst(BtCursor*, int *pRes);
|
||||
int sqlite3BtreeIsEmpty(BtCursor *pCur, int *pRes);
|
||||
int sqlite3BtreeLast(BtCursor*, int *pRes);
|
||||
int sqlite3BtreeNext(BtCursor*, int flags);
|
||||
int sqlite3BtreeEof(BtCursor*);
|
||||
|
||||
+10
-4
@@ -5137,16 +5137,22 @@ void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pIndexedBy){
|
||||
** are deleted by this function.
|
||||
*/
|
||||
SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, SrcList *p2){
|
||||
assert( p1 && p1->nSrc==1 );
|
||||
assert( p1 );
|
||||
assert( p2 || pParse->nErr );
|
||||
assert( p2==0 || p2->nSrc>=1 );
|
||||
testcase( p1->nSrc==0 );
|
||||
if( p2 ){
|
||||
SrcList *pNew = sqlite3SrcListEnlarge(pParse, p1, p2->nSrc, 1);
|
||||
int nOld = p1->nSrc;
|
||||
SrcList *pNew = sqlite3SrcListEnlarge(pParse, p1, p2->nSrc, nOld);
|
||||
if( pNew==0 ){
|
||||
sqlite3SrcListDelete(pParse->db, p2);
|
||||
}else{
|
||||
p1 = pNew;
|
||||
memcpy(&p1->a[1], p2->a, p2->nSrc*sizeof(SrcItem));
|
||||
memcpy(&p1->a[nOld], p2->a, p2->nSrc*sizeof(SrcItem));
|
||||
assert( nOld==1 || (p2->a[0].fg.jointype & JT_LTORJ)==0 );
|
||||
assert( p1->nSrc>=1 );
|
||||
p1->a[0].fg.jointype |= (JT_LTORJ & p2->a[0].fg.jointype);
|
||||
sqlite3DbFree(pParse->db, p2);
|
||||
p1->a[0].fg.jointype |= (JT_LTORJ & p1->a[1].fg.jointype);
|
||||
}
|
||||
}
|
||||
return p1;
|
||||
|
||||
+7
-6
@@ -227,7 +227,7 @@ static int dbpageEof(sqlite3_vtab_cursor *pCursor){
|
||||
** idxStr is not used
|
||||
*/
|
||||
static int dbpageFilter(
|
||||
sqlite3_vtab_cursor *pCursor,
|
||||
sqlite3_vtab_cursor *pCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
@@ -237,10 +237,11 @@ static int dbpageFilter(
|
||||
sqlite3 *db = pTab->db;
|
||||
Btree *pBt;
|
||||
|
||||
(void)idxStr;
|
||||
|
||||
UNUSED_PARAMETER(idxStr);
|
||||
UNUSED_PARAMETER(argc);
|
||||
|
||||
/* Default setting is no rows of result */
|
||||
pCsr->pgno = 1;
|
||||
pCsr->pgno = 1;
|
||||
pCsr->mxPgno = 0;
|
||||
|
||||
if( idxNum & 2 ){
|
||||
@@ -275,8 +276,8 @@ static int dbpageFilter(
|
||||
}
|
||||
|
||||
static int dbpageColumn(
|
||||
sqlite3_vtab_cursor *pCursor,
|
||||
sqlite3_context *ctx,
|
||||
sqlite3_vtab_cursor *pCursor,
|
||||
sqlite3_context *ctx,
|
||||
int i
|
||||
){
|
||||
DbpageCursor *pCsr = (DbpageCursor *)pCursor;
|
||||
|
||||
+203
-66
@@ -2373,6 +2373,86 @@ Expr *sqlite3ExprSimplifiedAndOr(Expr *pExpr){
|
||||
return pExpr;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if it might be advantageous to compute the right operand
|
||||
** of expression pExpr first, before the left operand.
|
||||
**
|
||||
** Normally the left operand is computed before the right operand. But if
|
||||
** the left operand contains a subquery and the right does not, then it
|
||||
** might be more efficient to compute the right operand first.
|
||||
*/
|
||||
static int exprEvalRhsFirst(Expr *pExpr){
|
||||
if( ExprHasProperty(pExpr->pLeft, EP_Subquery)
|
||||
&& !ExprHasProperty(pExpr->pRight, EP_Subquery)
|
||||
){
|
||||
return 1;
|
||||
}else{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Compute the two operands of a binary operator.
|
||||
**
|
||||
** If either operand contains a subquery, then the code strives to
|
||||
** compute the operand containing the subquery second. If the other
|
||||
** operand evalutes to NULL, then a jump is made. The address of the
|
||||
** IsNull operand that does this jump is returned. The caller can use
|
||||
** this to optimize the computation so as to avoid doing the potentially
|
||||
** expensive subquery.
|
||||
**
|
||||
** If no optimization opportunities exist, return 0.
|
||||
*/
|
||||
static int exprComputeOperands(
|
||||
Parse *pParse, /* Parsing context */
|
||||
Expr *pExpr, /* The comparison expression */
|
||||
int *pR1, /* OUT: Register holding the left operand */
|
||||
int *pR2, /* OUT: Register holding the right operand */
|
||||
int *pFree1, /* OUT: Temp register to free if not zero */
|
||||
int *pFree2 /* OUT: Another temp register to free if not zero */
|
||||
){
|
||||
int addrIsNull;
|
||||
int r1, r2;
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
|
||||
assert( v!=0 );
|
||||
/*
|
||||
** If the left operand contains a (possibly expensive) subquery and the
|
||||
** right operand does not and the right operation might be NULL,
|
||||
** then compute the right operand first and do an IsNull jump if the
|
||||
** right operand evalutes to NULL.
|
||||
*/
|
||||
if( exprEvalRhsFirst(pExpr) && sqlite3ExprCanBeNull(pExpr->pRight) ){
|
||||
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, pFree2);
|
||||
addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, r2);
|
||||
VdbeComment((v, "skip left operand"));
|
||||
VdbeCoverage(v);
|
||||
}else{
|
||||
r2 = 0; /* Silence a false-positive uninit-var warning in MSVC */
|
||||
addrIsNull = 0;
|
||||
}
|
||||
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, pFree1);
|
||||
if( addrIsNull==0 ){
|
||||
/*
|
||||
** If the right operand contains a subquery and the left operand does not
|
||||
** and the left operand might be NULL, then check the left operand do
|
||||
** an IsNull check on the left operand before computing the right
|
||||
** operand.
|
||||
*/
|
||||
if( ExprHasProperty(pExpr->pRight, EP_Subquery)
|
||||
&& sqlite3ExprCanBeNull(pExpr->pLeft)
|
||||
){
|
||||
addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, r1);
|
||||
VdbeComment((v, "skip right operand"));
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, pFree2);
|
||||
}
|
||||
*pR1 = r1;
|
||||
*pR2 = r2;
|
||||
return addrIsNull;
|
||||
}
|
||||
|
||||
/*
|
||||
** pExpr is a TK_FUNCTION node. Try to determine whether or not the
|
||||
** function is a constant function. A function is constant if all of
|
||||
@@ -3817,17 +3897,23 @@ int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
|
||||
VdbeComment((v, "Init EXISTS result"));
|
||||
}
|
||||
if( pSel->pLimit ){
|
||||
/* The subquery already has a limit. If the pre-existing limit is X
|
||||
** then make the new limit X<>0 so that the new limit is either 1 or 0 */
|
||||
sqlite3 *db = pParse->db;
|
||||
pLimit = sqlite3Expr(db, TK_INTEGER, "0");
|
||||
if( pLimit ){
|
||||
pLimit->affExpr = SQLITE_AFF_NUMERIC;
|
||||
pLimit = sqlite3PExpr(pParse, TK_NE,
|
||||
sqlite3ExprDup(db, pSel->pLimit->pLeft, 0), pLimit);
|
||||
/* The subquery already has a limit. If the pre-existing limit X is
|
||||
** not already integer value 1 or 0, then make the new limit X<>0 so that
|
||||
** the new limit is either 1 or 0 */
|
||||
Expr *pLeft = pSel->pLimit->pLeft;
|
||||
if( ExprHasProperty(pLeft, EP_IntValue)==0
|
||||
|| (pLeft->u.iValue!=1 && pLeft->u.iValue!=0)
|
||||
){
|
||||
sqlite3 *db = pParse->db;
|
||||
pLimit = sqlite3Expr(db, TK_INTEGER, "0");
|
||||
if( pLimit ){
|
||||
pLimit->affExpr = SQLITE_AFF_NUMERIC;
|
||||
pLimit = sqlite3PExpr(pParse, TK_NE,
|
||||
sqlite3ExprDup(db, pLeft, 0), pLimit);
|
||||
}
|
||||
sqlite3ExprDeferredDelete(pParse, pLeft);
|
||||
pSel->pLimit->pLeft = pLimit;
|
||||
}
|
||||
sqlite3ExprDeferredDelete(pParse, pSel->pLimit->pLeft);
|
||||
pSel->pLimit->pLeft = pLimit;
|
||||
}else{
|
||||
/* If there is no pre-existing limit add a limit of 1 */
|
||||
pLimit = sqlite3Expr(pParse->db, TK_INTEGER, "1");
|
||||
@@ -4269,7 +4355,12 @@ void sqlite3ExprCodeGeneratedColumn(
|
||||
iAddr = 0;
|
||||
}
|
||||
sqlite3ExprCodeCopy(pParse, sqlite3ColumnExpr(pTab,pCol), regOut);
|
||||
if( pCol->affinity>=SQLITE_AFF_TEXT ){
|
||||
if( (pCol->colFlags & COLFLAG_VIRTUAL)!=0
|
||||
&& (pTab->tabFlags & TF_Strict)!=0
|
||||
){
|
||||
int p3 = 2+(int)(pCol - pTab->aCol);
|
||||
sqlite3VdbeAddOp4(v, OP_TypeCheck, regOut, 1, p3, (char*)pTab, P4_TABLE);
|
||||
}else if( pCol->affinity>=SQLITE_AFF_TEXT ){
|
||||
sqlite3VdbeAddOp4(v, OP_Affinity, regOut, 1, 0, &pCol->affinity, 1);
|
||||
}
|
||||
if( iAddr ) sqlite3VdbeJumpHere(v, iAddr);
|
||||
@@ -4898,12 +4989,6 @@ expr_code_doover:
|
||||
sqlite3VdbeLoadString(v, target, pExpr->u.zToken);
|
||||
return target;
|
||||
}
|
||||
case TK_NULLS: {
|
||||
/* Set a range of registers to NULL. pExpr->y.nReg registers starting
|
||||
** with target */
|
||||
sqlite3VdbeAddOp3(v, OP_Null, 0, target, target + pExpr->y.nReg - 1);
|
||||
return target;
|
||||
}
|
||||
default: {
|
||||
/* Make NULL the default case so that if a bug causes an illegal
|
||||
** Expr node to be passed into this function, it will be handled
|
||||
@@ -4962,11 +5047,17 @@ expr_code_doover:
|
||||
case TK_NE:
|
||||
case TK_EQ: {
|
||||
Expr *pLeft = pExpr->pLeft;
|
||||
int addrIsNull = 0;
|
||||
if( sqlite3ExprIsVector(pLeft) ){
|
||||
codeVectorCompare(pParse, pExpr, target, op, p5);
|
||||
}else{
|
||||
r1 = sqlite3ExprCodeTemp(pParse, pLeft, ®Free1);
|
||||
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2);
|
||||
if( ExprHasProperty(pExpr, EP_Subquery) && p5!=SQLITE_NULLEQ ){
|
||||
addrIsNull = exprComputeOperands(pParse, pExpr,
|
||||
&r1, &r2, ®Free1, ®Free2);
|
||||
}else{
|
||||
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1);
|
||||
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 1, inReg);
|
||||
codeCompare(pParse, pLeft, pExpr->pRight, op, r1, r2,
|
||||
sqlite3VdbeCurrentAddr(v)+2, p5,
|
||||
@@ -4981,9 +5072,15 @@ expr_code_doover:
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, inReg);
|
||||
}else{
|
||||
sqlite3VdbeAddOp3(v, OP_ZeroOrNull, r1, inReg, r2);
|
||||
if( addrIsNull ){
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+2);
|
||||
sqlite3VdbeJumpHere(v, addrIsNull);
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, inReg);
|
||||
}
|
||||
}
|
||||
testcase( regFree1==0 );
|
||||
testcase( regFree2==0 );
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -4999,6 +5096,7 @@ expr_code_doover:
|
||||
case TK_LSHIFT:
|
||||
case TK_RSHIFT:
|
||||
case TK_CONCAT: {
|
||||
int addrIsNull;
|
||||
assert( TK_AND==OP_And ); testcase( op==TK_AND );
|
||||
assert( TK_OR==OP_Or ); testcase( op==TK_OR );
|
||||
assert( TK_PLUS==OP_Add ); testcase( op==TK_PLUS );
|
||||
@@ -5010,11 +5108,23 @@ expr_code_doover:
|
||||
assert( TK_LSHIFT==OP_ShiftLeft ); testcase( op==TK_LSHIFT );
|
||||
assert( TK_RSHIFT==OP_ShiftRight ); testcase( op==TK_RSHIFT );
|
||||
assert( TK_CONCAT==OP_Concat ); testcase( op==TK_CONCAT );
|
||||
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1);
|
||||
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2);
|
||||
if( ExprHasProperty(pExpr, EP_Subquery) ){
|
||||
addrIsNull = exprComputeOperands(pParse, pExpr,
|
||||
&r1, &r2, ®Free1, ®Free2);
|
||||
}else{
|
||||
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1);
|
||||
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2);
|
||||
addrIsNull = 0;
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, op, r2, r1, target);
|
||||
testcase( regFree1==0 );
|
||||
testcase( regFree2==0 );
|
||||
if( addrIsNull ){
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+2);
|
||||
sqlite3VdbeJumpHere(v, addrIsNull);
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, target);
|
||||
VdbeComment((v, "short-circut value"));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TK_UMINUS: {
|
||||
@@ -5588,25 +5698,6 @@ int sqlite3ExprCodeRunJustOnce(
|
||||
return regDest;
|
||||
}
|
||||
|
||||
/*
|
||||
** Make arrangements to invoke OP_Null on a range of registers
|
||||
** during initialization.
|
||||
*/
|
||||
SQLITE_NOINLINE void sqlite3ExprNullRegisterRange(
|
||||
Parse *pParse, /* Parsing context */
|
||||
int iReg, /* First register to set to NULL */
|
||||
int nReg /* Number of sequential registers to NULL out */
|
||||
){
|
||||
u8 okConstFactor = pParse->okConstFactor;
|
||||
Expr t;
|
||||
memset(&t, 0, sizeof(t));
|
||||
t.op = TK_NULLS;
|
||||
t.y.nReg = nReg;
|
||||
pParse->okConstFactor = 1;
|
||||
sqlite3ExprCodeRunJustOnce(pParse, &t, iReg);
|
||||
pParse->okConstFactor = okConstFactor;
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code to evaluate an expression and store the results
|
||||
** into a register. Return the register number where the results
|
||||
@@ -5882,17 +5973,27 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
||||
Expr *pAlt = sqlite3ExprSimplifiedAndOr(pExpr);
|
||||
if( pAlt!=pExpr ){
|
||||
sqlite3ExprIfTrue(pParse, pAlt, dest, jumpIfNull);
|
||||
}else if( op==TK_AND ){
|
||||
int d2 = sqlite3VdbeMakeLabel(pParse);
|
||||
testcase( jumpIfNull==0 );
|
||||
sqlite3ExprIfFalse(pParse, pExpr->pLeft, d2,
|
||||
jumpIfNull^SQLITE_JUMPIFNULL);
|
||||
sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
|
||||
sqlite3VdbeResolveLabel(v, d2);
|
||||
}else{
|
||||
testcase( jumpIfNull==0 );
|
||||
sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull);
|
||||
sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
|
||||
Expr *pFirst, *pSecond;
|
||||
if( exprEvalRhsFirst(pExpr) ){
|
||||
pFirst = pExpr->pRight;
|
||||
pSecond = pExpr->pLeft;
|
||||
}else{
|
||||
pFirst = pExpr->pLeft;
|
||||
pSecond = pExpr->pRight;
|
||||
}
|
||||
if( op==TK_AND ){
|
||||
int d2 = sqlite3VdbeMakeLabel(pParse);
|
||||
testcase( jumpIfNull==0 );
|
||||
sqlite3ExprIfFalse(pParse, pFirst, d2,
|
||||
jumpIfNull^SQLITE_JUMPIFNULL);
|
||||
sqlite3ExprIfTrue(pParse, pSecond, dest, jumpIfNull);
|
||||
sqlite3VdbeResolveLabel(v, d2);
|
||||
}else{
|
||||
testcase( jumpIfNull==0 );
|
||||
sqlite3ExprIfTrue(pParse, pFirst, dest, jumpIfNull);
|
||||
sqlite3ExprIfTrue(pParse, pSecond, dest, jumpIfNull);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -5931,10 +6032,16 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
||||
case TK_GE:
|
||||
case TK_NE:
|
||||
case TK_EQ: {
|
||||
int addrIsNull;
|
||||
if( sqlite3ExprIsVector(pExpr->pLeft) ) goto default_expr;
|
||||
testcase( jumpIfNull==0 );
|
||||
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1);
|
||||
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2);
|
||||
if( ExprHasProperty(pExpr, EP_Subquery) && jumpIfNull!=SQLITE_NULLEQ ){
|
||||
addrIsNull = exprComputeOperands(pParse, pExpr,
|
||||
&r1, &r2, ®Free1, ®Free2);
|
||||
}else{
|
||||
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1);
|
||||
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2);
|
||||
addrIsNull = 0;
|
||||
}
|
||||
codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
|
||||
r1, r2, dest, jumpIfNull, ExprHasProperty(pExpr,EP_Commuted));
|
||||
assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt);
|
||||
@@ -5949,6 +6056,13 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
||||
VdbeCoverageIf(v, op==OP_Ne && jumpIfNull!=SQLITE_NULLEQ);
|
||||
testcase( regFree1==0 );
|
||||
testcase( regFree2==0 );
|
||||
if( addrIsNull ){
|
||||
if( jumpIfNull ){
|
||||
sqlite3VdbeChangeP2(v, addrIsNull, dest);
|
||||
}else{
|
||||
sqlite3VdbeJumpHere(v, addrIsNull);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TK_ISNULL:
|
||||
@@ -6056,17 +6170,27 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
||||
Expr *pAlt = sqlite3ExprSimplifiedAndOr(pExpr);
|
||||
if( pAlt!=pExpr ){
|
||||
sqlite3ExprIfFalse(pParse, pAlt, dest, jumpIfNull);
|
||||
}else if( pExpr->op==TK_AND ){
|
||||
testcase( jumpIfNull==0 );
|
||||
sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull);
|
||||
sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
|
||||
}else{
|
||||
int d2 = sqlite3VdbeMakeLabel(pParse);
|
||||
testcase( jumpIfNull==0 );
|
||||
sqlite3ExprIfTrue(pParse, pExpr->pLeft, d2,
|
||||
jumpIfNull^SQLITE_JUMPIFNULL);
|
||||
sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
|
||||
sqlite3VdbeResolveLabel(v, d2);
|
||||
Expr *pFirst, *pSecond;
|
||||
if( exprEvalRhsFirst(pExpr) ){
|
||||
pFirst = pExpr->pRight;
|
||||
pSecond = pExpr->pLeft;
|
||||
}else{
|
||||
pFirst = pExpr->pLeft;
|
||||
pSecond = pExpr->pRight;
|
||||
}
|
||||
if( pExpr->op==TK_AND ){
|
||||
testcase( jumpIfNull==0 );
|
||||
sqlite3ExprIfFalse(pParse, pFirst, dest, jumpIfNull);
|
||||
sqlite3ExprIfFalse(pParse, pSecond, dest, jumpIfNull);
|
||||
}else{
|
||||
int d2 = sqlite3VdbeMakeLabel(pParse);
|
||||
testcase( jumpIfNull==0 );
|
||||
sqlite3ExprIfTrue(pParse, pFirst, d2,
|
||||
jumpIfNull^SQLITE_JUMPIFNULL);
|
||||
sqlite3ExprIfFalse(pParse, pSecond, dest, jumpIfNull);
|
||||
sqlite3VdbeResolveLabel(v, d2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -6108,10 +6232,16 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
||||
case TK_GE:
|
||||
case TK_NE:
|
||||
case TK_EQ: {
|
||||
int addrIsNull;
|
||||
if( sqlite3ExprIsVector(pExpr->pLeft) ) goto default_expr;
|
||||
testcase( jumpIfNull==0 );
|
||||
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1);
|
||||
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2);
|
||||
if( ExprHasProperty(pExpr, EP_Subquery) && jumpIfNull!=SQLITE_NULLEQ ){
|
||||
addrIsNull = exprComputeOperands(pParse, pExpr,
|
||||
&r1, &r2, ®Free1, ®Free2);
|
||||
}else{
|
||||
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1);
|
||||
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2);
|
||||
addrIsNull = 0;
|
||||
}
|
||||
codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
|
||||
r1, r2, dest, jumpIfNull,ExprHasProperty(pExpr,EP_Commuted));
|
||||
assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt);
|
||||
@@ -6126,6 +6256,13 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
||||
VdbeCoverageIf(v, op==OP_Ne && jumpIfNull==SQLITE_NULLEQ);
|
||||
testcase( regFree1==0 );
|
||||
testcase( regFree2==0 );
|
||||
if( addrIsNull ){
|
||||
if( jumpIfNull ){
|
||||
sqlite3VdbeChangeP2(v, addrIsNull, dest);
|
||||
}else{
|
||||
sqlite3VdbeJumpHere(v, addrIsNull);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TK_ISNULL:
|
||||
|
||||
+4
-1
@@ -185,12 +185,15 @@ void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){
|
||||
** by one slot and insert a new OP_TypeCheck where the current
|
||||
** OP_MakeRecord is found */
|
||||
VdbeOp *pPrev;
|
||||
int p3;
|
||||
sqlite3VdbeAppendP4(v, pTab, P4_TABLE);
|
||||
pPrev = sqlite3VdbeGetLastOp(v);
|
||||
assert( pPrev!=0 );
|
||||
assert( pPrev->opcode==OP_MakeRecord || sqlite3VdbeDb(v)->mallocFailed );
|
||||
pPrev->opcode = OP_TypeCheck;
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, pPrev->p1, pPrev->p2, pPrev->p3);
|
||||
p3 = pPrev->p3;
|
||||
pPrev->p3 = 0;
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, pPrev->p1, pPrev->p2, p3);
|
||||
}else{
|
||||
/* Insert an isolated OP_Typecheck */
|
||||
sqlite3VdbeAddOp2(v, OP_TypeCheck, iReg, pTab->nNVCol);
|
||||
|
||||
@@ -416,6 +416,14 @@ void sqlite3_str_vappendf(
|
||||
}
|
||||
prefix = 0;
|
||||
}
|
||||
|
||||
#if WHERETRACE_ENABLED
|
||||
if( xtype==etPOINTER && sqlite3WhereTrace & 0x100000 ) longvalue = 0;
|
||||
#endif
|
||||
#if TREETRACE_ENABLED
|
||||
if( xtype==etPOINTER && sqlite3TreeTrace & 0x100000 ) longvalue = 0;
|
||||
#endif
|
||||
|
||||
if( longvalue==0 ) flag_alternateform = 0;
|
||||
if( flag_zeropad && precision<width-(prefix!=0) ){
|
||||
precision = width-(prefix!=0);
|
||||
|
||||
+4
-1
@@ -1358,11 +1358,13 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
return WRC_Prune;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
case TK_EXISTS:
|
||||
case TK_SELECT:
|
||||
case TK_EXISTS: testcase( pExpr->op==TK_EXISTS );
|
||||
#endif
|
||||
case TK_IN: {
|
||||
testcase( pExpr->op==TK_IN );
|
||||
testcase( pExpr->op==TK_EXISTS );
|
||||
testcase( pExpr->op==TK_SELECT );
|
||||
if( ExprUseXSelect(pExpr) ){
|
||||
int nRef = pNC->nRef;
|
||||
testcase( pNC->ncFlags & NC_IsCheck );
|
||||
@@ -1370,6 +1372,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
testcase( pNC->ncFlags & NC_IdxExpr );
|
||||
testcase( pNC->ncFlags & NC_GenCol );
|
||||
assert( pExpr->x.pSelect );
|
||||
if( pExpr->op==TK_EXISTS ) pParse->bHasExists = 1;
|
||||
if( pNC->ncFlags & NC_SelfRef ){
|
||||
notValidImpl(pParse, pNC, "subqueries", pExpr, pExpr);
|
||||
}else{
|
||||
|
||||
+114
-12
@@ -384,7 +384,7 @@ static int tableAndColumnIndex(
|
||||
int iEnd, /* Last member of pSrc->a[] to check */
|
||||
const char *zCol, /* Name of the column we are looking for */
|
||||
int *piTab, /* Write index of pSrc->a[] here */
|
||||
int *piCol, /* Write index of pSrc->a[*piTab].pTab->aCol[] here */
|
||||
int *piCol, /* Write index of pSrc->a[*piTab].pSTab->aCol[] here */
|
||||
int bIgnoreHidden /* Ignore hidden columns */
|
||||
){
|
||||
int i; /* For looping over tables in pSrc */
|
||||
@@ -2113,6 +2113,10 @@ static void generateColumnTypes(
|
||||
#endif
|
||||
sqlite3VdbeSetColName(v, i, COLNAME_DECLTYPE, zType, SQLITE_TRANSIENT);
|
||||
}
|
||||
#else
|
||||
UNUSED_PARAMETER(pParse);
|
||||
UNUSED_PARAMETER(pTabList);
|
||||
UNUSED_PARAMETER(pEList);
|
||||
#endif /* !defined(SQLITE_OMIT_DECLTYPE) */
|
||||
}
|
||||
|
||||
@@ -3032,7 +3036,9 @@ static int multiSelect(
|
||||
int priorOp; /* The SRT_ operation to apply to prior selects */
|
||||
Expr *pLimit; /* Saved values of p->nLimit */
|
||||
int addr;
|
||||
int emptyBypass = 0; /* IfEmpty opcode to bypass RHS */
|
||||
SelectDest uniondest;
|
||||
|
||||
|
||||
testcase( p->op==TK_EXCEPT );
|
||||
testcase( p->op==TK_UNION );
|
||||
@@ -3071,6 +3077,8 @@ static int multiSelect(
|
||||
*/
|
||||
if( p->op==TK_EXCEPT ){
|
||||
op = SRT_Except;
|
||||
emptyBypass = sqlite3VdbeAddOp1(v, OP_IfEmpty, unionTab);
|
||||
VdbeCoverage(v);
|
||||
}else{
|
||||
assert( p->op==TK_UNION );
|
||||
op = SRT_Union;
|
||||
@@ -3091,6 +3099,7 @@ static int multiSelect(
|
||||
if( p->op==TK_UNION ){
|
||||
p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
|
||||
}
|
||||
if( emptyBypass ) sqlite3VdbeJumpHere(v, emptyBypass);
|
||||
sqlite3ExprDelete(db, p->pLimit);
|
||||
p->pLimit = pLimit;
|
||||
p->iLimit = 0;
|
||||
@@ -3124,6 +3133,7 @@ static int multiSelect(
|
||||
int addr;
|
||||
SelectDest intersectdest;
|
||||
int r1;
|
||||
int emptyBypass;
|
||||
|
||||
/* INTERSECT is different from the others since it requires
|
||||
** two temporary tables. Hence it has its own case. Begin
|
||||
@@ -3147,6 +3157,7 @@ static int multiSelect(
|
||||
if( rc ){
|
||||
goto multi_select_end;
|
||||
}
|
||||
emptyBypass = sqlite3VdbeAddOp1(v, OP_IfEmpty, tab1); VdbeCoverage(v);
|
||||
|
||||
/* Code the current SELECT into temporary table "tab2"
|
||||
*/
|
||||
@@ -3178,7 +3189,7 @@ static int multiSelect(
|
||||
iBreak = sqlite3VdbeMakeLabel(pParse);
|
||||
iCont = sqlite3VdbeMakeLabel(pParse);
|
||||
computeLimitRegisters(pParse, p, iBreak);
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, tab1, iBreak); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp1(v, OP_Rewind, tab1);
|
||||
r1 = sqlite3GetTempReg(pParse);
|
||||
iStart = sqlite3VdbeAddOp2(v, OP_RowData, tab1, r1);
|
||||
sqlite3VdbeAddOp4Int(v, OP_NotFound, tab2, iCont, r1, 0);
|
||||
@@ -3190,6 +3201,7 @@ static int multiSelect(
|
||||
sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart); VdbeCoverage(v);
|
||||
sqlite3VdbeResolveLabel(v, iBreak);
|
||||
sqlite3VdbeAddOp2(v, OP_Close, tab2, 0);
|
||||
sqlite3VdbeJumpHere(v, emptyBypass);
|
||||
sqlite3VdbeAddOp2(v, OP_Close, tab1, 0);
|
||||
break;
|
||||
}
|
||||
@@ -3868,6 +3880,7 @@ typedef struct SubstContext {
|
||||
int iTable; /* Replace references to this table */
|
||||
int iNewTable; /* New table number */
|
||||
int isOuterJoin; /* Add TK_IF_NULL_ROW opcodes on each replacement */
|
||||
int nSelDepth; /* Depth of sub-query recursion. Top==1 */
|
||||
ExprList *pEList; /* Replacement expressions */
|
||||
ExprList *pCList; /* Collation sequences for replacement expr */
|
||||
} SubstContext;
|
||||
@@ -3975,6 +3988,9 @@ static Expr *substExpr(
|
||||
if( pExpr->op==TK_IF_NULL_ROW && pExpr->iTable==pSubst->iTable ){
|
||||
pExpr->iTable = pSubst->iNewTable;
|
||||
}
|
||||
if( pExpr->op==TK_AGG_FUNCTION && pExpr->op2>=pSubst->nSelDepth ){
|
||||
pExpr->op2--;
|
||||
}
|
||||
pExpr->pLeft = substExpr(pSubst, pExpr->pLeft);
|
||||
pExpr->pRight = substExpr(pSubst, pExpr->pRight);
|
||||
if( ExprUseXSelect(pExpr) ){
|
||||
@@ -4012,6 +4028,7 @@ static void substSelect(
|
||||
SrcItem *pItem;
|
||||
int i;
|
||||
if( !p ) return;
|
||||
pSubst->nSelDepth++;
|
||||
do{
|
||||
substExprList(pSubst, p->pEList);
|
||||
substExprList(pSubst, p->pGroupBy);
|
||||
@@ -4029,6 +4046,7 @@ static void substSelect(
|
||||
}
|
||||
}
|
||||
}while( doPrior && (p = p->pPrior)!=0 );
|
||||
pSubst->nSelDepth--;
|
||||
}
|
||||
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
|
||||
|
||||
@@ -4640,7 +4658,7 @@ static int flattenSubquery(
|
||||
** complete, since there may still exist Expr.pTab entries that
|
||||
** refer to the subquery even after flattening. Ticket #3346.
|
||||
**
|
||||
** pSubitem->pTab is always non-NULL by test restrictions and tests above.
|
||||
** pSubitem->pSTab is always non-NULL by test restrictions and tests above.
|
||||
*/
|
||||
if( ALWAYS(pSubitem->pSTab!=0) ){
|
||||
Table *pTabToDel = pSubitem->pSTab;
|
||||
@@ -4770,6 +4788,7 @@ static int flattenSubquery(
|
||||
x.iTable = iParent;
|
||||
x.iNewTable = iNewParent;
|
||||
x.isOuterJoin = isOuterJoin;
|
||||
x.nSelDepth = 0;
|
||||
x.pEList = pSub->pEList;
|
||||
x.pCList = findLeftmostExprlist(pSub);
|
||||
substSelect(&x, pParent, 0);
|
||||
@@ -5355,6 +5374,7 @@ static int pushDownWhereTerms(
|
||||
x.iTable = pSrc->iCursor;
|
||||
x.iNewTable = pSrc->iCursor;
|
||||
x.isOuterJoin = 0;
|
||||
x.nSelDepth = 0;
|
||||
x.pEList = pSubq->pEList;
|
||||
x.pCList = findLeftmostExprlist(pSubq);
|
||||
pNew = substExpr(&x, pNew);
|
||||
@@ -5752,7 +5772,7 @@ With *sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){
|
||||
** CTE expression, through routine checks to see if the reference is
|
||||
** a recursive reference to the CTE.
|
||||
**
|
||||
** If pFrom matches a CTE according to either of these two above, pFrom->pTab
|
||||
** If pFrom matches a CTE according to either of these two above, pFrom->pSTab
|
||||
** and other fields are populated accordingly.
|
||||
**
|
||||
** Return 0 if no match is found.
|
||||
@@ -7379,6 +7399,82 @@ static int fromClauseTermCanBeCoroutine(
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Argument pWhere is the WHERE clause belonging to SELECT statement p. This
|
||||
** function attempts to transform expressions of the form:
|
||||
**
|
||||
** EXISTS (SELECT ...)
|
||||
**
|
||||
** into joins. For example, given
|
||||
**
|
||||
** CREATE TABLE sailors(sid INTEGER PRIMARY KEY, name TEXT);
|
||||
** CREATE TABLE reserves(sid INT, day DATE, PRIMARY KEY(sid, day));
|
||||
**
|
||||
** SELECT name FROM sailors AS S WHERE EXISTS (
|
||||
** SELECT * FROM reserves AS R WHERE S.sid = R.sid AND R.day = '2022-10-25'
|
||||
** );
|
||||
**
|
||||
** the SELECT statement may be transformed as follows:
|
||||
**
|
||||
** SELECT name FROM sailors AS S, reserves AS R
|
||||
** WHERE S.sid = R.sid AND R.day = '2022-10-25';
|
||||
**
|
||||
** **Approximately**. Really, we have to ensure that the FROM-clause term
|
||||
** that was formerly inside the EXISTS is only executed once. This is handled
|
||||
** by setting the SrcItem.fg.fromExists flag, which then causes code in
|
||||
** the where.c file to exit the corresponding loop after the first successful
|
||||
** match (if any).
|
||||
*/
|
||||
static SQLITE_NOINLINE void existsToJoin(
|
||||
Parse *pParse, /* Parsing context */
|
||||
Select *p, /* The SELECT statement being optimized */
|
||||
Expr *pWhere /* part of the WHERE clause currently being examined */
|
||||
){
|
||||
if( pWhere
|
||||
&& !ExprHasProperty(pWhere, EP_OuterON|EP_InnerON)
|
||||
&& p->pSrc->nSrc<BMS
|
||||
&& pParse->db->mallocFailed==0
|
||||
){
|
||||
if( pWhere->op==TK_AND ){
|
||||
Expr *pRight = pWhere->pRight;
|
||||
existsToJoin(pParse, p, pWhere->pLeft);
|
||||
existsToJoin(pParse, p, pRight);
|
||||
}
|
||||
else if( pWhere->op==TK_EXISTS ){
|
||||
Select *pSub = pWhere->x.pSelect;
|
||||
Expr *pSubWhere = pSub->pWhere;
|
||||
if( pSub->pSrc->nSrc==1
|
||||
&& (pSub->selFlags & SF_Aggregate)==0
|
||||
&& !pSub->pSrc->a[0].fg.isSubquery
|
||||
){
|
||||
memset(pWhere, 0, sizeof(*pWhere));
|
||||
pWhere->op = TK_INTEGER;
|
||||
pWhere->u.iValue = 1;
|
||||
ExprSetProperty(pWhere, EP_IntValue);
|
||||
|
||||
assert( p->pWhere!=0 );
|
||||
pSub->pSrc->a[0].fg.fromExists = 1;
|
||||
pSub->pSrc->a[0].fg.jointype |= JT_CROSS;
|
||||
p->pSrc = sqlite3SrcListAppendList(pParse, p->pSrc, pSub->pSrc);
|
||||
if( pSubWhere ){
|
||||
p->pWhere = sqlite3PExpr(pParse, TK_AND, p->pWhere, pSubWhere);
|
||||
pSub->pWhere = 0;
|
||||
}
|
||||
pSub->pSrc = 0;
|
||||
sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pSub);
|
||||
#if TREETRACE_ENABLED
|
||||
if( sqlite3TreeTrace & 0x100000 ){
|
||||
TREETRACE(0x100000,pParse,p,
|
||||
("After EXISTS-to-JOIN optimization:\n"));
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
#endif
|
||||
existsToJoin(pParse, p, pSubWhere);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate byte-code for the SELECT statement given in the p argument.
|
||||
**
|
||||
@@ -7747,6 +7843,13 @@ int sqlite3Select(
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If there may be an "EXISTS (SELECT ...)" in the WHERE clause, attempt
|
||||
** to change it into a join. */
|
||||
if( pParse->bHasExists && OptimizationEnabled(db,SQLITE_ExistsToJoin) ){
|
||||
existsToJoin(pParse, p, p->pWhere);
|
||||
pTabList = p->pSrc;
|
||||
}
|
||||
|
||||
/* Do the WHERE-clause constant propagation optimization if this is
|
||||
** a join. No need to spend time on this operation for non-join queries
|
||||
** as the equivalent optimization will be handled by query planner in
|
||||
@@ -8363,7 +8466,6 @@ int sqlite3Select(
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, iAbortFlag);
|
||||
VdbeComment((v, "clear abort flag"));
|
||||
sqlite3VdbeAddOp3(v, OP_Null, 0, iAMem, iAMem+pGroupBy->nExpr-1);
|
||||
sqlite3ExprNullRegisterRange(pParse, iAMem, pGroupBy->nExpr);
|
||||
|
||||
/* Begin a loop that will extract all source rows in GROUP BY order.
|
||||
** This might involve two separate loops with an OP_Sort in between, or
|
||||
@@ -8534,12 +8636,12 @@ int sqlite3Select(
|
||||
** for the next GROUP BY batch.
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, regOutputRow, addrOutputRow);
|
||||
VdbeComment((v, "output one row"));
|
||||
VdbeComment((v, "output one row of %d", p->selId));
|
||||
sqlite3ExprCodeMove(pParse, iBMem, iAMem, pGroupBy->nExpr);
|
||||
sqlite3VdbeAddOp2(v, OP_IfPos, iAbortFlag, addrEnd); VdbeCoverage(v);
|
||||
VdbeComment((v, "check abort flag"));
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset);
|
||||
VdbeComment((v, "reset accumulator"));
|
||||
VdbeComment((v, "reset accumulator %d", p->selId));
|
||||
|
||||
/* Update the aggregate accumulators based on the content of
|
||||
** the current row
|
||||
@@ -8547,7 +8649,7 @@ int sqlite3Select(
|
||||
sqlite3VdbeJumpHere(v, addr1);
|
||||
updateAccumulator(pParse, iUseFlag, pAggInfo, eDist);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 1, iUseFlag);
|
||||
VdbeComment((v, "indicate data in accumulator"));
|
||||
VdbeComment((v, "indicate data in accumulator %d", p->selId));
|
||||
|
||||
/* End of the loop
|
||||
*/
|
||||
@@ -8564,7 +8666,7 @@ int sqlite3Select(
|
||||
/* Output the final row of result
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, regOutputRow, addrOutputRow);
|
||||
VdbeComment((v, "output final row"));
|
||||
VdbeComment((v, "output final row of %d", p->selId));
|
||||
|
||||
/* Jump over the subroutines
|
||||
*/
|
||||
@@ -8585,7 +8687,7 @@ int sqlite3Select(
|
||||
addrOutputRow = sqlite3VdbeCurrentAddr(v);
|
||||
sqlite3VdbeAddOp2(v, OP_IfPos, iUseFlag, addrOutputRow+2);
|
||||
VdbeCoverage(v);
|
||||
VdbeComment((v, "Groupby result generator entry point"));
|
||||
VdbeComment((v, "Groupby result generator entry point %d", p->selId));
|
||||
sqlite3VdbeAddOp1(v, OP_Return, regOutputRow);
|
||||
finalizeAggFunctions(pParse, pAggInfo);
|
||||
sqlite3ExprIfFalse(pParse, pHaving, addrOutputRow+1, SQLITE_JUMPIFNULL);
|
||||
@@ -8593,14 +8695,14 @@ int sqlite3Select(
|
||||
&sDistinct, pDest,
|
||||
addrOutputRow+1, addrSetAbort);
|
||||
sqlite3VdbeAddOp1(v, OP_Return, regOutputRow);
|
||||
VdbeComment((v, "end groupby result generator"));
|
||||
VdbeComment((v, "end groupby result generator %d", p->selId));
|
||||
|
||||
/* Generate a subroutine that will reset the group-by accumulator
|
||||
*/
|
||||
sqlite3VdbeResolveLabel(v, addrReset);
|
||||
resetAccumulator(pParse, pAggInfo);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, iUseFlag);
|
||||
VdbeComment((v, "indicate accumulator empty"));
|
||||
VdbeComment((v, "indicate accumulator %d empty", p->selId));
|
||||
sqlite3VdbeAddOp1(v, OP_Return, regReset);
|
||||
|
||||
if( distFlag!=0 && eDist!=WHERE_DISTINCT_NOOP ){
|
||||
|
||||
+63
-13
@@ -887,12 +887,21 @@ static int strlen30(const char *z){
|
||||
|
||||
/*
|
||||
** Return the length of a string in characters. Multibyte UTF8 characters
|
||||
** count as a single character.
|
||||
** count as a single character for single-width characters, or as two
|
||||
** characters for double-width characters.
|
||||
*/
|
||||
static int strlenChar(const char *z){
|
||||
int n = 0;
|
||||
while( *z ){
|
||||
if( (0xc0&*(z++))!=0x80 ) n++;
|
||||
if( (0x80&z[0])==0 ){
|
||||
n++;
|
||||
z++;
|
||||
}else{
|
||||
int u = 0;
|
||||
int len = decodeUtf8((const u8*)z, &u);
|
||||
z += len;
|
||||
n += cli_wcwidth(u);
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
@@ -1325,11 +1334,7 @@ static void shellAddSchemaName(
|
||||
#define SQLITE_EXTENSION_INIT1
|
||||
#define SQLITE_EXTENSION_INIT2(X) (void)(X)
|
||||
|
||||
#if defined(_WIN32) && defined(_MSC_VER)
|
||||
INCLUDE test_windirent.h
|
||||
INCLUDE test_windirent.c
|
||||
#define dirent DIRENT
|
||||
#endif
|
||||
INCLUDE ../ext/misc/windirent.h
|
||||
INCLUDE ../ext/misc/memtrace.c
|
||||
INCLUDE ../ext/misc/pcachetrace.c
|
||||
INCLUDE ../ext/misc/shathree.c
|
||||
@@ -9323,7 +9328,8 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
" (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x"
|
||||
" FROM sqlite_schema UNION ALL"
|
||||
" SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_schema) "
|
||||
"WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%' "
|
||||
"WHERE type!='meta' AND sql NOTNULL"
|
||||
" AND name NOT LIKE 'sqlite__%' ESCAPE '_' "
|
||||
"ORDER BY x",
|
||||
callback, &data, 0
|
||||
);
|
||||
@@ -10799,7 +10805,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
sqlite3_free(zQarg);
|
||||
}
|
||||
if( bNoSystemTabs ){
|
||||
appendText(&sSelect, "name NOT LIKE 'sqlite_%%' AND ", 0);
|
||||
appendText(&sSelect, "name NOT LIKE 'sqlite__%%' ESCAPE '_' AND ", 0);
|
||||
}
|
||||
appendText(&sSelect, "sql IS NOT NULL"
|
||||
" ORDER BY snum, rowid", 0);
|
||||
@@ -11230,7 +11236,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
}else{
|
||||
zSql = "SELECT lower(name) as tname FROM sqlite_schema"
|
||||
" WHERE type='table' AND coalesce(rootpage,0)>1"
|
||||
" AND name NOT LIKE 'sqlite_%'"
|
||||
" AND name NOT LIKE 'sqlite__%' ESCAPE '_'"
|
||||
" ORDER BY 1 collate nocase";
|
||||
}
|
||||
sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
|
||||
@@ -11295,7 +11301,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
char *zRevText = /* Query for reversible to-blob-to-text check */
|
||||
"SELECT lower(name) as tname FROM sqlite_schema\n"
|
||||
"WHERE type='table' AND coalesce(rootpage,0)>1\n"
|
||||
"AND name NOT LIKE 'sqlite_%%'%s\n"
|
||||
"AND name NOT LIKE 'sqlite__%%' ESCAPE '_'%s\n"
|
||||
"ORDER BY 1 collate nocase";
|
||||
zRevText = sqlite3_mprintf(zRevText, zLike? " AND name LIKE $tspec" : "");
|
||||
zRevText = sqlite3_mprintf(
|
||||
@@ -11491,7 +11497,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
appendText(&s, ".sqlite_schema ", 0);
|
||||
if( c=='t' ){
|
||||
appendText(&s," WHERE type IN ('table','view')"
|
||||
" AND name NOT LIKE 'sqlite_%'"
|
||||
" AND name NOT LIKE 'sqlite__%' ESCAPE '_'"
|
||||
" AND name LIKE ?1", 0);
|
||||
}else{
|
||||
appendText(&s," WHERE type='index'"
|
||||
@@ -11585,7 +11591,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
{"always", SQLITE_TESTCTRL_ALWAYS, 1, "BOOLEAN" },
|
||||
{"assert", SQLITE_TESTCTRL_ASSERT, 1, "BOOLEAN" },
|
||||
/*{"benign_malloc_hooks",SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS,1, "" },*/
|
||||
/*{"bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, 1, "" },*/
|
||||
{"bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, 1, "SIZE INT-ARRAY"},
|
||||
{"byteorder", SQLITE_TESTCTRL_BYTEORDER, 0, "" },
|
||||
{"extra_schema_checks",SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS,0,"BOOLEAN" },
|
||||
{"fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, 1,"args..." },
|
||||
@@ -11704,6 +11710,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
{ 0x08000000, 1, "OnePass" },
|
||||
{ 0x10000000, 1, "OrderBySubq" },
|
||||
{ 0x20000000, 1, "StarQuery" },
|
||||
{ 0x40000000, 1, "ExistsToJoin" },
|
||||
{ 0xffffffff, 0, "All" },
|
||||
};
|
||||
unsigned int curOpt;
|
||||
@@ -11923,6 +11930,49 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
}
|
||||
sqlite3_test_control(testctrl, &rc2);
|
||||
break;
|
||||
case SQLITE_TESTCTRL_BITVEC_TEST: {
|
||||
/* Examples:
|
||||
** .testctrl bitvec_test 100 6,1 -- Show BITVEC constants
|
||||
** .testctrl bitvec_test 1000 1,12,7,3 -- Simple test
|
||||
** ---- --------
|
||||
** size of Bitvec -----^ ^--- aOp array. 0 added at end.
|
||||
**
|
||||
** See comments on sqlite3BitvecBuiltinTest() for more information
|
||||
** about the aOp[] array.
|
||||
*/
|
||||
int iSize;
|
||||
const char *zTestArg;
|
||||
int nOp;
|
||||
int ii, jj, x;
|
||||
int *aOp;
|
||||
if( nArg!=4 ){
|
||||
sqlite3_fprintf(stderr,
|
||||
"ERROR - should be: \".testctrl bitvec_test SIZE INT-ARRAY\"\n"
|
||||
);
|
||||
rc = 1;
|
||||
goto meta_command_exit;
|
||||
}
|
||||
isOk = 3;
|
||||
iSize = (int)integerValue(azArg[2]);
|
||||
zTestArg = azArg[3];
|
||||
nOp = (int)strlen(zTestArg)+1;
|
||||
aOp = malloc( sizeof(int)*(nOp+1) );
|
||||
shell_check_oom(aOp);
|
||||
memset(aOp, 0, sizeof(int)*(nOp+1) );
|
||||
for(ii = jj = x = 0; zTestArg[ii]!=0; ii++){
|
||||
if( IsDigit(zTestArg[ii]) ){
|
||||
x = x*10 + zTestArg[ii] - '0';
|
||||
}else{
|
||||
aOp[jj++] = x;
|
||||
x = 0;
|
||||
}
|
||||
}
|
||||
aOp[jj] = x;
|
||||
x = sqlite3_test_control(testctrl, iSize, aOp);
|
||||
sqlite3_fprintf(p->out, "result: %d\n", x);
|
||||
free(aOp);
|
||||
break;
|
||||
}
|
||||
case SQLITE_TESTCTRL_FAULT_INSTALL: {
|
||||
int kk;
|
||||
int bShowHelp = nArg<=2;
|
||||
|
||||
+50
-50
@@ -168,9 +168,9 @@ extern "C" {
|
||||
** assert( strcmp(sqlite3_libversion(),SQLITE_VERSION)==0 );
|
||||
** </pre></blockquote>)^
|
||||
**
|
||||
** ^The sqlite3_version[] string constant contains the text of [SQLITE_VERSION]
|
||||
** macro. ^The sqlite3_libversion() function returns a pointer to the
|
||||
** to the sqlite3_version[] string constant. The sqlite3_libversion()
|
||||
** ^The sqlite3_version[] string constant contains the text of the
|
||||
** [SQLITE_VERSION] macro. ^The sqlite3_libversion() function returns a
|
||||
** pointer to the sqlite3_version[] string constant. The sqlite3_libversion()
|
||||
** function is provided for use in DLLs since DLL users usually do not have
|
||||
** direct access to string constants within the DLL. ^The
|
||||
** sqlite3_libversion_number() function returns an integer equal to
|
||||
@@ -370,7 +370,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
|
||||
** without having to use a lot of C code.
|
||||
**
|
||||
** ^The sqlite3_exec() interface runs zero or more UTF-8 encoded,
|
||||
** semicolon-separate SQL statements passed into its 2nd argument,
|
||||
** semicolon-separated SQL statements passed into its 2nd argument,
|
||||
** in the context of the [database connection] passed in as its 1st
|
||||
** argument. ^If the callback function of the 3rd argument to
|
||||
** sqlite3_exec() is not NULL, then it is invoked for each result row
|
||||
@@ -403,7 +403,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
|
||||
** result row is NULL then the corresponding string pointer for the
|
||||
** sqlite3_exec() callback is a NULL pointer. ^The 4th argument to the
|
||||
** sqlite3_exec() callback is an array of pointers to strings where each
|
||||
** entry represents the name of corresponding result column as obtained
|
||||
** entry represents the name of a corresponding result column as obtained
|
||||
** from [sqlite3_column_name()].
|
||||
**
|
||||
** ^If the 2nd parameter to sqlite3_exec() is a NULL pointer, a pointer
|
||||
@@ -589,7 +589,7 @@ int sqlite3_exec(
|
||||
** Note in particular that passing the SQLITE_OPEN_EXCLUSIVE flag into
|
||||
** [sqlite3_open_v2()] does *not* cause the underlying database file
|
||||
** to be opened using O_EXCL. Passing SQLITE_OPEN_EXCLUSIVE into
|
||||
** [sqlite3_open_v2()] has historically be a no-op and might become an
|
||||
** [sqlite3_open_v2()] has historically been a no-op and might become an
|
||||
** error in future versions of SQLite.
|
||||
*/
|
||||
#define SQLITE_OPEN_READONLY 0x00000001 /* Ok for sqlite3_open_v2() */
|
||||
@@ -683,7 +683,7 @@ int sqlite3_exec(
|
||||
** SQLite uses one of these integer values as the second
|
||||
** argument to calls it makes to the xLock() and xUnlock() methods
|
||||
** of an [sqlite3_io_methods] object. These values are ordered from
|
||||
** lest restrictive to most restrictive.
|
||||
** least restrictive to most restrictive.
|
||||
**
|
||||
** The argument to xLock() is always SHARED or higher. The argument to
|
||||
** xUnlock is either SHARED or NONE.
|
||||
@@ -999,7 +999,7 @@ struct sqlite3_io_methods {
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_VFSNAME]]
|
||||
** ^The [SQLITE_FCNTL_VFSNAME] opcode can be used to obtain the names of
|
||||
** all [VFSes] in the VFS stack. The names are of all VFS shims and the
|
||||
** all [VFSes] in the VFS stack. The names of all VFS shims and the
|
||||
** final bottom-level VFS are written into memory obtained from
|
||||
** [sqlite3_malloc()] and the result is stored in the char* variable
|
||||
** that the fourth parameter of [sqlite3_file_control()] points to.
|
||||
@@ -1013,7 +1013,7 @@ struct sqlite3_io_methods {
|
||||
** ^The [SQLITE_FCNTL_VFS_POINTER] opcode finds a pointer to the top-level
|
||||
** [VFSes] currently in use. ^(The argument X in
|
||||
** sqlite3_file_control(db,SQLITE_FCNTL_VFS_POINTER,X) must be
|
||||
** of type "[sqlite3_vfs] **". This opcodes will set *X
|
||||
** of type "[sqlite3_vfs] **". This opcode will set *X
|
||||
** to a pointer to the top-level VFS.)^
|
||||
** ^When there are multiple VFS shims in the stack, this opcode finds the
|
||||
** upper-most shim only.
|
||||
@@ -1203,7 +1203,7 @@ struct sqlite3_io_methods {
|
||||
** <li>[[SQLITE_FCNTL_EXTERNAL_READER]]
|
||||
** The EXPERIMENTAL [SQLITE_FCNTL_EXTERNAL_READER] opcode is used to detect
|
||||
** whether or not there is a database client in another process with a wal-mode
|
||||
** transaction open on the database or not. It is only available on unix.The
|
||||
** transaction open on the database or not. It is only available on unix. The
|
||||
** (void*) argument passed with this file-control should be a pointer to a
|
||||
** value of type (int). The integer value is set to 1 if the database is a wal
|
||||
** mode database and there exists at least one client in another process that
|
||||
@@ -1628,7 +1628,7 @@ struct sqlite3_vfs {
|
||||
** SQLite interfaces so that an application usually does not need to
|
||||
** invoke sqlite3_initialize() directly. For example, [sqlite3_open()]
|
||||
** calls sqlite3_initialize() so the SQLite library will be automatically
|
||||
** initialized when [sqlite3_open()] is called if it has not be initialized
|
||||
** initialized when [sqlite3_open()] is called if it has not been initialized
|
||||
** already. ^However, if SQLite is compiled with the [SQLITE_OMIT_AUTOINIT]
|
||||
** compile-time option, then the automatic calls to sqlite3_initialize()
|
||||
** are omitted and the application must call sqlite3_initialize() directly
|
||||
@@ -1885,21 +1885,21 @@ struct sqlite3_mem_methods {
|
||||
** The [sqlite3_mem_methods]
|
||||
** structure is filled with the currently defined memory allocation routines.)^
|
||||
** This option can be used to overload the default memory allocation
|
||||
** routines with a wrapper that simulations memory allocation failure or
|
||||
** routines with a wrapper that simulates memory allocation failure or
|
||||
** tracks memory usage, for example. </dd>
|
||||
**
|
||||
** [[SQLITE_CONFIG_SMALL_MALLOC]] <dt>SQLITE_CONFIG_SMALL_MALLOC</dt>
|
||||
** <dd> ^The SQLITE_CONFIG_SMALL_MALLOC option takes single argument of
|
||||
** <dd> ^The SQLITE_CONFIG_SMALL_MALLOC option takes a single argument of
|
||||
** type int, interpreted as a boolean, which if true provides a hint to
|
||||
** SQLite that it should avoid large memory allocations if possible.
|
||||
** SQLite will run faster if it is free to make large memory allocations,
|
||||
** but some application might prefer to run slower in exchange for
|
||||
** but some applications might prefer to run slower in exchange for
|
||||
** guarantees about memory fragmentation that are possible if large
|
||||
** allocations are avoided. This hint is normally off.
|
||||
** </dd>
|
||||
**
|
||||
** [[SQLITE_CONFIG_MEMSTATUS]] <dt>SQLITE_CONFIG_MEMSTATUS</dt>
|
||||
** <dd> ^The SQLITE_CONFIG_MEMSTATUS option takes single argument of type int,
|
||||
** <dd> ^The SQLITE_CONFIG_MEMSTATUS option takes a single argument of type int,
|
||||
** interpreted as a boolean, which enables or disables the collection of
|
||||
** memory allocation statistics. ^(When memory allocation statistics are
|
||||
** disabled, the following SQLite interfaces become non-operational:
|
||||
@@ -1944,7 +1944,7 @@ struct sqlite3_mem_methods {
|
||||
** ^If pMem is NULL and N is non-zero, then each database connection
|
||||
** does an initial bulk allocation for page cache memory
|
||||
** from [sqlite3_malloc()] sufficient for N cache lines if N is positive or
|
||||
** of -1024*N bytes if N is negative, . ^If additional
|
||||
** of -1024*N bytes if N is negative. ^If additional
|
||||
** page cache memory is needed beyond what is provided by the initial
|
||||
** allocation, then SQLite goes to [sqlite3_malloc()] separately for each
|
||||
** additional cache line. </dd>
|
||||
@@ -1973,7 +1973,7 @@ struct sqlite3_mem_methods {
|
||||
** <dd> ^(The SQLITE_CONFIG_MUTEX option takes a single argument which is a
|
||||
** pointer to an instance of the [sqlite3_mutex_methods] structure.
|
||||
** The argument specifies alternative low-level mutex routines to be used
|
||||
** in place the mutex routines built into SQLite.)^ ^SQLite makes a copy of
|
||||
** in place of the mutex routines built into SQLite.)^ ^SQLite makes a copy of
|
||||
** the content of the [sqlite3_mutex_methods] structure before the call to
|
||||
** [sqlite3_config()] returns. ^If SQLite is compiled with
|
||||
** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then
|
||||
@@ -2015,7 +2015,7 @@ struct sqlite3_mem_methods {
|
||||
**
|
||||
** [[SQLITE_CONFIG_GETPCACHE2]] <dt>SQLITE_CONFIG_GETPCACHE2</dt>
|
||||
** <dd> ^(The SQLITE_CONFIG_GETPCACHE2 option takes a single argument which
|
||||
** is a pointer to an [sqlite3_pcache_methods2] object. SQLite copies of
|
||||
** is a pointer to an [sqlite3_pcache_methods2] object. SQLite copies off
|
||||
** the current page cache implementation into that object.)^ </dd>
|
||||
**
|
||||
** [[SQLITE_CONFIG_LOG]] <dt>SQLITE_CONFIG_LOG</dt>
|
||||
@@ -2032,7 +2032,7 @@ struct sqlite3_mem_methods {
|
||||
** the logger function is a copy of the first parameter to the corresponding
|
||||
** [sqlite3_log()] call and is intended to be a [result code] or an
|
||||
** [extended result code]. ^The third parameter passed to the logger is
|
||||
** log message after formatting via [sqlite3_snprintf()].
|
||||
** a log message after formatting via [sqlite3_snprintf()].
|
||||
** The SQLite logging interface is not reentrant; the logger function
|
||||
** supplied by the application must not invoke any SQLite interface.
|
||||
** In a multi-threaded application, the application-defined logger
|
||||
@@ -2223,7 +2223,7 @@ struct sqlite3_mem_methods {
|
||||
** These constants are the available integer configuration options that
|
||||
** 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
|
||||
** The [sqlite3_db_config()] interface is a var-args function. 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
|
||||
@@ -2357,8 +2357,8 @@ struct sqlite3_mem_methods {
|
||||
** 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.
|
||||
** If the first argument is -1, then no changes are made to state of either the
|
||||
** C-API or the SQL function.
|
||||
** If the first argument is -1, then no changes are made to the state of either
|
||||
** the C-API or the SQL function.
|
||||
** The second parameter is a pointer to an integer into which
|
||||
** is written 0 or 1 to indicate whether [sqlite3_load_extension()] interface
|
||||
** is disabled or enabled following this call. The second parameter may
|
||||
@@ -2476,7 +2476,7 @@ struct sqlite3_mem_methods {
|
||||
** [[SQLITE_DBCONFIG_LEGACY_ALTER_TABLE]]
|
||||
** <dt>SQLITE_DBCONFIG_LEGACY_ALTER_TABLE</dt>
|
||||
** <dd>The SQLITE_DBCONFIG_LEGACY_ALTER_TABLE option activates or deactivates
|
||||
** the legacy behavior of the [ALTER TABLE RENAME] command such it
|
||||
** the legacy behavior of the [ALTER TABLE RENAME] command such that it
|
||||
** behaves as it did prior to [version 3.24.0] (2018-06-04). See the
|
||||
** "Compatibility Notice" on the [ALTER TABLE RENAME documentation] for
|
||||
** additional information. This feature can also be turned on and off
|
||||
@@ -2525,7 +2525,7 @@ struct sqlite3_mem_methods {
|
||||
** <dt>SQLITE_DBCONFIG_LEGACY_FILE_FORMAT</dt>
|
||||
** <dd>The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates
|
||||
** the legacy file format flag. When activated, this flag causes all newly
|
||||
** created database file to have a schema format version number (the 4-byte
|
||||
** created database files to have a schema format version number (the 4-byte
|
||||
** integer found at offset 44 into the database header) of 1. This in turn
|
||||
** means that the resulting database file will be readable and writable by
|
||||
** any SQLite version back to 3.0.0 ([dateof:3.0.0]). Without this setting,
|
||||
@@ -2552,7 +2552,7 @@ struct sqlite3_mem_methods {
|
||||
** 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. <p>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
|
||||
** 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
|
||||
** processing the first argument is written into the integer that the second
|
||||
@@ -2595,8 +2595,8 @@ struct sqlite3_mem_methods {
|
||||
** <dd>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,
|
||||
** reenable this capability using the current DBCONFIG option. If
|
||||
** 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]
|
||||
@@ -2630,7 +2630,7 @@ struct sqlite3_mem_methods {
|
||||
**
|
||||
** <p>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 first argument (the third parameter to sqlite3_db_config()) is an 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
|
||||
@@ -2920,7 +2920,7 @@ int sqlite3_is_interrupted(sqlite3*);
|
||||
** ^These routines return 0 if the statement is incomplete. ^If a
|
||||
** memory allocation fails, then SQLITE_NOMEM is returned.
|
||||
**
|
||||
** ^These routines do not parse the SQL statements thus
|
||||
** ^These routines do not parse the SQL statements and thus
|
||||
** will not detect syntactically incorrect SQL.
|
||||
**
|
||||
** ^(If SQLite has not been initialized using [sqlite3_initialize()] prior
|
||||
@@ -3037,7 +3037,7 @@ int sqlite3_busy_timeout(sqlite3*, int ms);
|
||||
** indefinitely if possible. The results of passing any other negative value
|
||||
** are undefined.
|
||||
**
|
||||
** Internally, each SQLite database handle store two timeout values - the
|
||||
** Internally, each SQLite database handle stores two timeout values - the
|
||||
** busy-timeout (used for rollback mode databases, or if the VFS does not
|
||||
** support blocking locks) and the setlk-timeout (used for blocking locks
|
||||
** on wal-mode databases). The sqlite3_busy_timeout() method sets both
|
||||
@@ -3067,7 +3067,7 @@ int sqlite3_setlk_timeout(sqlite3*, int ms, int flags);
|
||||
** This is a legacy interface that is preserved for backwards compatibility.
|
||||
** Use of this interface is not recommended.
|
||||
**
|
||||
** Definition: A <b>result table</b> is memory data structure created by the
|
||||
** Definition: A <b>result table</b> is a memory data structure created by the
|
||||
** [sqlite3_get_table()] interface. A result table records the
|
||||
** complete query results from one or more queries.
|
||||
**
|
||||
@@ -3210,7 +3210,7 @@ char *sqlite3_vsnprintf(int,char*,const char*, va_list);
|
||||
** ^Calling sqlite3_free() with a pointer previously returned
|
||||
** by sqlite3_malloc() or sqlite3_realloc() releases that memory so
|
||||
** that it might be reused. ^The sqlite3_free() routine is
|
||||
** a no-op if is called with a NULL pointer. Passing a NULL pointer
|
||||
** a no-op if it is called with a NULL pointer. Passing a NULL pointer
|
||||
** to sqlite3_free() is harmless. After being freed, memory
|
||||
** should neither be read nor written. Even reading previously freed
|
||||
** memory might result in a segmentation fault or other severe error.
|
||||
@@ -3228,13 +3228,13 @@ char *sqlite3_vsnprintf(int,char*,const char*, va_list);
|
||||
** sqlite3_free(X).
|
||||
** ^sqlite3_realloc(X,N) returns a pointer to a memory allocation
|
||||
** of at least N bytes in size or NULL if insufficient memory is available.
|
||||
** ^If M is the size of the prior allocation, then min(N,M) bytes
|
||||
** of the prior allocation are copied into the beginning of buffer returned
|
||||
** ^If M is the size of the prior allocation, then min(N,M) bytes of the
|
||||
** prior allocation are copied into the beginning of the buffer returned
|
||||
** by sqlite3_realloc(X,N) and the prior allocation is freed.
|
||||
** ^If sqlite3_realloc(X,N) returns NULL and N is positive, then the
|
||||
** prior allocation is not freed.
|
||||
**
|
||||
** ^The sqlite3_realloc64(X,N) interfaces works the same as
|
||||
** ^The sqlite3_realloc64(X,N) interface works the same as
|
||||
** sqlite3_realloc(X,N) except that N is a 64-bit unsigned integer instead
|
||||
** of a 32-bit signed integer.
|
||||
**
|
||||
@@ -3284,7 +3284,7 @@ sqlite3_uint64 sqlite3_msize(void*);
|
||||
** was last reset. ^The values returned by [sqlite3_memory_used()] and
|
||||
** [sqlite3_memory_highwater()] include any overhead
|
||||
** added by SQLite in its implementation of [sqlite3_malloc()],
|
||||
** but not overhead added by the any underlying system library
|
||||
** but not overhead added by any underlying system library
|
||||
** routines that [sqlite3_malloc()] may call.
|
||||
**
|
||||
** ^The memory high-water mark is reset to the current value of
|
||||
@@ -3736,7 +3736,7 @@ void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
|
||||
** there is no harm in trying.)
|
||||
**
|
||||
** ^(<dt>[SQLITE_OPEN_SHAREDCACHE]</dt>
|
||||
** <dd>The database is opened [shared cache] enabled, overriding
|
||||
** <dd>The database is opened with [shared cache] enabled, overriding
|
||||
** the default shared cache setting provided by
|
||||
** [sqlite3_enable_shared_cache()].)^
|
||||
** The [use of shared cache mode is discouraged] and hence shared cache
|
||||
@@ -3744,7 +3744,7 @@ void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
|
||||
** this option is a no-op.
|
||||
**
|
||||
** ^(<dt>[SQLITE_OPEN_PRIVATECACHE]</dt>
|
||||
** <dd>The database is opened [shared cache] disabled, overriding
|
||||
** <dd>The database is opened with [shared cache] disabled, overriding
|
||||
** the default shared cache setting provided by
|
||||
** [sqlite3_enable_shared_cache()].)^
|
||||
**
|
||||
@@ -4162,7 +4162,7 @@ void sqlite3_free_filename(sqlite3_filename);
|
||||
** subsequent calls to other SQLite interface functions.)^
|
||||
**
|
||||
** ^The sqlite3_errstr(E) interface returns the English-language text
|
||||
** that describes the [result code] E, as UTF-8, or NULL if E is not an
|
||||
** that describes the [result code] E, as UTF-8, or NULL if E is not a
|
||||
** result code for which a text error message is available.
|
||||
** ^(Memory to hold the error message string is managed internally
|
||||
** and must not be freed by the application)^.
|
||||
@@ -4170,7 +4170,7 @@ void sqlite3_free_filename(sqlite3_filename);
|
||||
** ^If the most recent error references a specific token in the input
|
||||
** SQL, the sqlite3_error_offset() interface returns the byte offset
|
||||
** of the start of that token. ^The byte offset returned by
|
||||
** sqlite3_error_offset() assumes that the input SQL is UTF8.
|
||||
** sqlite3_error_offset() assumes that the input SQL is UTF-8.
|
||||
** ^If the most recent error does not reference a specific token in the input
|
||||
** SQL, then the sqlite3_error_offset() function returns -1.
|
||||
**
|
||||
@@ -4269,8 +4269,8 @@ int sqlite3_limit(sqlite3*, int id, int newVal);
|
||||
**
|
||||
** These constants define various performance limits
|
||||
** that can be lowered at run-time using [sqlite3_limit()].
|
||||
** The synopsis of the meanings of the various limits is shown below.
|
||||
** Additional information is available at [limits | Limits in SQLite].
|
||||
** A concise description of these limits follows, and additional information
|
||||
** is available at [limits | Limits in SQLite].
|
||||
**
|
||||
** <dl>
|
||||
** [[SQLITE_LIMIT_LENGTH]] ^(<dt>SQLITE_LIMIT_LENGTH</dt>
|
||||
@@ -4335,7 +4335,7 @@ int sqlite3_limit(sqlite3*, int id, int newVal);
|
||||
/*
|
||||
** CAPI3REF: Prepare Flags
|
||||
**
|
||||
** These constants define various flags that can be passed into
|
||||
** These constants define various flags that can be passed into the
|
||||
** "prepFlags" parameter of the [sqlite3_prepare_v3()] and
|
||||
** [sqlite3_prepare16_v3()] interfaces.
|
||||
**
|
||||
@@ -4422,7 +4422,7 @@ int sqlite3_limit(sqlite3*, int id, int newVal);
|
||||
** there is a small performance advantage to passing an nByte parameter that
|
||||
** is the number of bytes in the input string <i>including</i>
|
||||
** the nul-terminator.
|
||||
** Note that nByte measure the length of the input in bytes, not
|
||||
** Note that nByte measures the length of the input in bytes, not
|
||||
** characters, even for the UTF-16 interfaces.
|
||||
**
|
||||
** ^If pzTail is not NULL then *pzTail is made to point to the first byte
|
||||
@@ -4556,7 +4556,7 @@ int sqlite3_prepare16_v3(
|
||||
**
|
||||
** ^The sqlite3_expanded_sql() interface returns NULL if insufficient memory
|
||||
** is available to hold the result, or if the result would exceed the
|
||||
** the maximum string length determined by the [SQLITE_LIMIT_LENGTH].
|
||||
** maximum string length determined by the [SQLITE_LIMIT_LENGTH].
|
||||
**
|
||||
** ^The [SQLITE_TRACE_SIZE_LIMIT] compile-time option limits the size of
|
||||
** bound parameter expansions. ^The [SQLITE_OMIT_TRACE] compile-time
|
||||
@@ -4744,7 +4744,7 @@ typedef struct sqlite3_value sqlite3_value;
|
||||
**
|
||||
** The context in which an SQL function executes is stored in an
|
||||
** sqlite3_context object. ^A pointer to an sqlite3_context object
|
||||
** is always first parameter to [application-defined SQL functions].
|
||||
** is always the first parameter to [application-defined SQL functions].
|
||||
** The application-defined SQL function implementation will pass this
|
||||
** pointer through into calls to [sqlite3_result_int | sqlite3_result()],
|
||||
** [sqlite3_aggregate_context()], [sqlite3_user_data()],
|
||||
@@ -5481,7 +5481,7 @@ int sqlite3_column_type(sqlite3_stmt*, int iCol);
|
||||
**
|
||||
** ^The sqlite3_finalize() function is called to delete a [prepared statement].
|
||||
** ^If the most recent evaluation of the statement encountered no errors
|
||||
** or if the statement is never been evaluated, then sqlite3_finalize() returns
|
||||
** or if the statement has never been evaluated, then sqlite3_finalize() returns
|
||||
** SQLITE_OK. ^If the most recent evaluation of statement S failed, then
|
||||
** sqlite3_finalize(S) returns the appropriate [error code] or
|
||||
** [extended error code].
|
||||
@@ -5713,7 +5713,7 @@ int sqlite3_create_window_function(
|
||||
/*
|
||||
** CAPI3REF: Text Encodings
|
||||
**
|
||||
** These constant define integer codes that represent the various
|
||||
** These constants define integer codes that represent the various
|
||||
** text encodings supported by SQLite.
|
||||
*/
|
||||
#define SQLITE_UTF8 1 /* IMP: R-37514-35566 */
|
||||
@@ -5805,7 +5805,7 @@ int sqlite3_create_window_function(
|
||||
** result.
|
||||
** Every function that invokes [sqlite3_result_subtype()] should have this
|
||||
** property. If it does not, then the call to [sqlite3_result_subtype()]
|
||||
** might become a no-op if the function is used as term in an
|
||||
** might become a no-op if the function is used as a term in an
|
||||
** [expression index]. On the other hand, SQL functions that never invoke
|
||||
** [sqlite3_result_subtype()] should avoid setting this property, as the
|
||||
** purpose of this property is to disable certain optimizations that are
|
||||
@@ -5932,7 +5932,7 @@ SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),
|
||||
** sqlite3_value_nochange(X) interface returns true if and only if
|
||||
** the column corresponding to X is unchanged by the UPDATE operation
|
||||
** that the xUpdate method call was invoked to implement and if
|
||||
** and the prior [xColumn] method call that was invoked to extracted
|
||||
** the prior [xColumn] method call that was invoked to extract
|
||||
** the value for that column returned without setting a result (probably
|
||||
** because it queried [sqlite3_vtab_nochange()] and found that the column
|
||||
** was unchanging). ^Within an [xUpdate] method, any value for which
|
||||
|
||||
+33
-14
@@ -765,7 +765,7 @@
|
||||
** ourselves.
|
||||
*/
|
||||
#ifndef offsetof
|
||||
#define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
|
||||
# define offsetof(ST,M) ((size_t)((char*)&((ST*)0)->M - (char*)0))
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -1153,6 +1153,8 @@ extern u32 sqlite3TreeTrace;
|
||||
** 0x00020000 Transform DISTINCT into GROUP BY
|
||||
** 0x00040000 SELECT tree dump after all code has been generated
|
||||
** 0x00080000 NOT NULL strength reduction
|
||||
** 0x00100000 Pointers are all shown as zero
|
||||
** 0x00200000 EXISTS-to-JOIN optimization
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -1197,6 +1199,7 @@ extern u32 sqlite3WhereTrace;
|
||||
** 0x00020000 Show WHERE terms returned from whereScanNext()
|
||||
** 0x00040000 Solver overview messages
|
||||
** 0x00080000 Star-query heuristic
|
||||
** 0x00100000 Pointers are all shown as zero
|
||||
*/
|
||||
|
||||
|
||||
@@ -1269,7 +1272,7 @@ struct BusyHandler {
|
||||
** pointer will work here as long as it is distinct from SQLITE_STATIC
|
||||
** and SQLITE_TRANSIENT.
|
||||
*/
|
||||
#define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3OomClear)
|
||||
#define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3RowSetClear)
|
||||
|
||||
/*
|
||||
** When SQLITE_OMIT_WSD is defined, it means that the target platform does
|
||||
@@ -1924,6 +1927,7 @@ struct sqlite3 {
|
||||
#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_ExistsToJoin 0x40000000 /* The EXISTS-to-JOIN optimization */
|
||||
#define SQLITE_AllOpts 0xffffffff /* All optimizations */
|
||||
|
||||
/*
|
||||
@@ -2645,9 +2649,15 @@ struct FKey {
|
||||
** argument to sqlite3VdbeKeyCompare and is used to control the
|
||||
** comparison of the two index keys.
|
||||
**
|
||||
** Note that aSortOrder[] and aColl[] have nField+1 slots. There
|
||||
** are nField slots for the columns of an index then one extra slot
|
||||
** for the rowid at the end.
|
||||
** The aSortOrder[] and aColl[] arrays have nAllField slots each. There
|
||||
** are nKeyField slots for the columns of an index then extra slots
|
||||
** for the rowid or key at the end. The aSortOrder array is located after
|
||||
** the aColl[] array.
|
||||
**
|
||||
** If SQLITE_ENABLE_PREUPDATE_HOOK is defined, then aSortFlags might be NULL
|
||||
** to indicate that this object is for use by a preupdate hook. When aSortFlags
|
||||
** is NULL, then nAllField is uninitialized and no space is allocated for
|
||||
** aColl[], so those fields may not be used.
|
||||
*/
|
||||
struct KeyInfo {
|
||||
u32 nRef; /* Number of references to this KeyInfo object */
|
||||
@@ -2659,9 +2669,18 @@ struct KeyInfo {
|
||||
CollSeq *aColl[FLEXARRAY]; /* Collating sequence for each term of the key */
|
||||
};
|
||||
|
||||
/* The size (in bytes) of a KeyInfo object with up to N fields */
|
||||
/* The size (in bytes) of a KeyInfo object with up to N fields. This includes
|
||||
** the main body of the KeyInfo object and the aColl[] array of N elements,
|
||||
** but does not count the memory used to hold aSortFlags[]. */
|
||||
#define SZ_KEYINFO(N) (offsetof(KeyInfo,aColl) + (N)*sizeof(CollSeq*))
|
||||
|
||||
/* The size of a bare KeyInfo with no aColl[] entries */
|
||||
#if FLEXARRAY+1 > 1
|
||||
# define SZ_KEYINFO_0 offsetof(KeyInfo,aColl)
|
||||
#else
|
||||
# define SZ_KEYINFO_0 sizeof(KeyInfo)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Allowed bit values for entries in the KeyInfo.aSortFlags[] array.
|
||||
*/
|
||||
@@ -2680,9 +2699,8 @@ struct KeyInfo {
|
||||
**
|
||||
** An instance of this object serves as a "key" for doing a search on
|
||||
** an index b+tree. The goal of the search is to find the entry that
|
||||
** is closed to the key described by this object. This object might hold
|
||||
** just a prefix of the key. The number of fields is given by
|
||||
** pKeyInfo->nField.
|
||||
** is closest to the key described by this object. This object might hold
|
||||
** just a prefix of the key. The number of fields is given by nField.
|
||||
**
|
||||
** The r1 and r2 fields are the values to return if this key is less than
|
||||
** or greater than a key in the btree, respectively. These are normally
|
||||
@@ -2692,7 +2710,7 @@ struct KeyInfo {
|
||||
** The key comparison functions actually return default_rc when they find
|
||||
** an equals comparison. default_rc can be -1, 0, or +1. If there are
|
||||
** multiple entries in the b-tree with the same key (when only looking
|
||||
** at the first pKeyInfo->nFields,) then default_rc can be set to -1 to
|
||||
** at the first nField elements) then default_rc can be set to -1 to
|
||||
** cause the search to find the last match, or +1 to cause the search to
|
||||
** find the first match.
|
||||
**
|
||||
@@ -2704,8 +2722,8 @@ struct KeyInfo {
|
||||
** b-tree.
|
||||
*/
|
||||
struct UnpackedRecord {
|
||||
KeyInfo *pKeyInfo; /* Collation and sort-order information */
|
||||
Mem *aMem; /* Values */
|
||||
KeyInfo *pKeyInfo; /* Comparison info for the index that is unpacked */
|
||||
Mem *aMem; /* Values for columns of the index */
|
||||
union {
|
||||
char *z; /* Cache of aMem[0].z for vdbeRecordCompareString() */
|
||||
i64 i; /* Cache of aMem[0].u.i for vdbeRecordCompareInt() */
|
||||
@@ -3064,7 +3082,6 @@ struct Expr {
|
||||
Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL
|
||||
** for a column of an index on an expression */
|
||||
Window *pWin; /* EP_WinFunc: Window/Filter defn for a function */
|
||||
int nReg; /* TK_NULLS: Number of registers to NULL out */
|
||||
struct { /* TK_IN, TK_SELECT, and TK_EXISTS */
|
||||
int iAddr; /* Subroutine entry address */
|
||||
int regReturn; /* Register used to hold return address */
|
||||
@@ -3354,6 +3371,7 @@ struct SrcItem {
|
||||
unsigned rowidUsed :1; /* The ROWID of this table is referenced */
|
||||
unsigned fixedSchema :1; /* Uses u4.pSchema, not u4.zDatabase */
|
||||
unsigned hadSchema :1; /* Had u4.zDatabase before u4.pSchema */
|
||||
unsigned fromExists :1; /* Comes from WHERE EXISTS(...) */
|
||||
} fg;
|
||||
int iCursor; /* The VDBE cursor number used to access this table */
|
||||
Bitmask colUsed; /* Bit N set if column N used. Details above for N>62 */
|
||||
@@ -3884,6 +3902,7 @@ struct Parse {
|
||||
u8 disableLookaside; /* Number of times lookaside has been disabled */
|
||||
u8 prepFlags; /* SQLITE_PREPARE_* flags */
|
||||
u8 withinRJSubrtn; /* Nesting level for RIGHT JOIN body subroutines */
|
||||
u8 bHasExists; /* Has a correlated "EXISTS (SELECT ....)" expression */
|
||||
u8 mSubrtnSig; /* mini Bloom filter on available SubrtnSig.selId */
|
||||
u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */
|
||||
u8 bReturning; /* Coding a RETURNING trigger */
|
||||
@@ -4880,6 +4899,7 @@ char *sqlite3VMPrintf(sqlite3*,const char*, va_list);
|
||||
void sqlite3ShowWindow(const Window*);
|
||||
void sqlite3ShowWinFunc(const Window*);
|
||||
#endif
|
||||
void sqlite3ShowBitvec(Bitvec*);
|
||||
#endif
|
||||
|
||||
void sqlite3SetString(char **, sqlite3*, const char*);
|
||||
@@ -5099,7 +5119,6 @@ void sqlite3ExprCodeGeneratedColumn(Parse*, Table*, Column*, int);
|
||||
void sqlite3ExprCodeCopy(Parse*, Expr*, int);
|
||||
void sqlite3ExprCodeFactorable(Parse*, Expr*, int);
|
||||
int sqlite3ExprCodeRunJustOnce(Parse*, Expr*, int);
|
||||
void sqlite3ExprNullRegisterRange(Parse*, int, int);
|
||||
int sqlite3ExprCodeTemp(Parse*, Expr*, int*);
|
||||
int sqlite3ExprCodeTarget(Parse*, Expr*, int);
|
||||
int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int, u8);
|
||||
|
||||
+97
-41
@@ -49,6 +49,10 @@
|
||||
# define CONST const
|
||||
#elif !defined(Tcl_Size)
|
||||
typedef int Tcl_Size;
|
||||
# ifndef Tcl_BounceRefCount
|
||||
# define Tcl_BounceRefCount(X) Tcl_IncrRefCount(X); Tcl_DecrRefCount(X)
|
||||
/* https://www.tcl-lang.org/man/tcl9.0/TclLib/Object.html */
|
||||
# endif
|
||||
#endif
|
||||
/**** End copy of tclsqlite.h ****/
|
||||
|
||||
@@ -1084,7 +1088,9 @@ static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){
|
||||
Tcl_DecrRefCount(pCmd);
|
||||
}
|
||||
|
||||
if( rc && rc!=TCL_RETURN ){
|
||||
if( TCL_BREAK==rc ){
|
||||
sqlite3_result_null(context);
|
||||
}else if( rc && rc!=TCL_RETURN ){
|
||||
sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1);
|
||||
}else{
|
||||
Tcl_Obj *pVar = Tcl_GetObjResult(p->interp);
|
||||
@@ -1102,7 +1108,7 @@ static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){
|
||||
}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)
|
||||
|| (c=='i' && strcmp(zType,"int")==0)
|
||||
){
|
||||
eType = SQLITE_INTEGER;
|
||||
}else if( c=='d' && strcmp(zType,"double")==0 ){
|
||||
@@ -1616,11 +1622,12 @@ struct DbEvalContext {
|
||||
SqlPreparedStmt *pPreStmt; /* Current statement */
|
||||
int nCol; /* Number of columns returned by pStmt */
|
||||
int evalFlags; /* Flags used */
|
||||
Tcl_Obj *pArray; /* Name of array variable */
|
||||
Tcl_Obj *pVarName; /* Name of target array/dict variable */
|
||||
Tcl_Obj **apColName; /* Array of column names */
|
||||
};
|
||||
|
||||
#define SQLITE_EVAL_WITHOUTNULLS 0x00001 /* Unset array(*) for NULL */
|
||||
#define SQLITE_EVAL_ASDICT 0x00002 /* Use dict instead of array */
|
||||
|
||||
/*
|
||||
** Release any cache of column names currently held as part of
|
||||
@@ -1641,20 +1648,20 @@ static void dbReleaseColumnNames(DbEvalContext *p){
|
||||
/*
|
||||
** Initialize a DbEvalContext structure.
|
||||
**
|
||||
** If pArray is not NULL, then it contains the name of a Tcl array
|
||||
** If pVarName is not NULL, then it contains the name of a Tcl array
|
||||
** variable. The "*" member of this array is set to a list containing
|
||||
** the names of the columns returned by the statement as part of each
|
||||
** call to dbEvalStep(), in order from left to right. e.g. if the names
|
||||
** of the returned columns are a, b and c, it does the equivalent of the
|
||||
** tcl command:
|
||||
**
|
||||
** set ${pArray}(*) {a b c}
|
||||
** set ${pVarName}(*) {a b c}
|
||||
*/
|
||||
static void dbEvalInit(
|
||||
DbEvalContext *p, /* Pointer to structure to initialize */
|
||||
SqliteDb *pDb, /* Database handle */
|
||||
Tcl_Obj *pSql, /* Object containing SQL script */
|
||||
Tcl_Obj *pArray, /* Name of Tcl array to set (*) element of */
|
||||
Tcl_Obj *pVarName, /* Name of Tcl array to set (*) element of */
|
||||
int evalFlags /* Flags controlling evaluation */
|
||||
){
|
||||
memset(p, 0, sizeof(DbEvalContext));
|
||||
@@ -1662,9 +1669,9 @@ static void dbEvalInit(
|
||||
p->zSql = Tcl_GetString(pSql);
|
||||
p->pSql = pSql;
|
||||
Tcl_IncrRefCount(pSql);
|
||||
if( pArray ){
|
||||
p->pArray = pArray;
|
||||
Tcl_IncrRefCount(pArray);
|
||||
if( pVarName ){
|
||||
p->pVarName = pVarName;
|
||||
Tcl_IncrRefCount(pVarName);
|
||||
}
|
||||
p->evalFlags = evalFlags;
|
||||
addDatabaseRef(p->pDb);
|
||||
@@ -1687,7 +1694,7 @@ static void dbEvalRowInfo(
|
||||
Tcl_Obj **apColName = 0; /* Array of column names */
|
||||
|
||||
p->nCol = nCol = sqlite3_column_count(pStmt);
|
||||
if( nCol>0 && (papColName || p->pArray) ){
|
||||
if( nCol>0 && (papColName || p->pVarName) ){
|
||||
apColName = (Tcl_Obj**)Tcl_Alloc( sizeof(Tcl_Obj*)*nCol );
|
||||
for(i=0; i<nCol; i++){
|
||||
apColName[i] = Tcl_NewStringObj(sqlite3_column_name(pStmt,i), -1);
|
||||
@@ -1696,20 +1703,35 @@ static void dbEvalRowInfo(
|
||||
p->apColName = apColName;
|
||||
}
|
||||
|
||||
/* If results are being stored in an array variable, then create
|
||||
** the array(*) entry for that array
|
||||
/* If results are being stored in a variable then create the
|
||||
** array(*) or dict(*) entry for that variable.
|
||||
*/
|
||||
if( p->pArray ){
|
||||
if( p->pVarName ){
|
||||
Tcl_Interp *interp = p->pDb->interp;
|
||||
Tcl_Obj *pColList = Tcl_NewObj();
|
||||
Tcl_Obj *pStar = Tcl_NewStringObj("*", -1);
|
||||
|
||||
Tcl_IncrRefCount(pColList);
|
||||
Tcl_IncrRefCount(pStar);
|
||||
for(i=0; i<nCol; i++){
|
||||
Tcl_ListObjAppendElement(interp, pColList, apColName[i]);
|
||||
}
|
||||
Tcl_IncrRefCount(pStar);
|
||||
Tcl_ObjSetVar2(interp, p->pArray, pStar, pColList, 0);
|
||||
if( 0==(SQLITE_EVAL_ASDICT & p->evalFlags) ){
|
||||
Tcl_ObjSetVar2(interp, p->pVarName, pStar, pColList, 0);
|
||||
}else{
|
||||
Tcl_Obj * pDict = Tcl_ObjGetVar2(interp, p->pVarName, NULL, 0);
|
||||
if( !pDict ){
|
||||
pDict = Tcl_NewDictObj();
|
||||
}else if( Tcl_IsShared(pDict) ){
|
||||
pDict = Tcl_DuplicateObj(pDict);
|
||||
}
|
||||
if( Tcl_DictObjPut(interp, pDict, pStar, pColList)==TCL_OK ){
|
||||
Tcl_ObjSetVar2(interp, p->pVarName, NULL, pDict, 0);
|
||||
}
|
||||
Tcl_BounceRefCount(pDict);
|
||||
}
|
||||
Tcl_DecrRefCount(pStar);
|
||||
Tcl_DecrRefCount(pColList);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1751,7 +1773,7 @@ static int dbEvalStep(DbEvalContext *p){
|
||||
if( rcs==SQLITE_ROW ){
|
||||
return TCL_OK;
|
||||
}
|
||||
if( p->pArray ){
|
||||
if( p->pVarName ){
|
||||
dbEvalRowInfo(p, 0, 0);
|
||||
}
|
||||
rcs = sqlite3_reset(pStmt);
|
||||
@@ -1802,9 +1824,9 @@ static void dbEvalFinalize(DbEvalContext *p){
|
||||
dbReleaseStmt(p->pDb, p->pPreStmt, 0);
|
||||
p->pPreStmt = 0;
|
||||
}
|
||||
if( p->pArray ){
|
||||
Tcl_DecrRefCount(p->pArray);
|
||||
p->pArray = 0;
|
||||
if( p->pVarName ){
|
||||
Tcl_DecrRefCount(p->pVarName);
|
||||
p->pVarName = 0;
|
||||
}
|
||||
Tcl_DecrRefCount(p->pSql);
|
||||
dbReleaseColumnNames(p);
|
||||
@@ -1879,7 +1901,7 @@ static int DbUseNre(void){
|
||||
/*
|
||||
** This function is part of the implementation of the command:
|
||||
**
|
||||
** $db eval SQL ?ARRAYNAME? SCRIPT
|
||||
** $db eval SQL ?TGT-NAME? SCRIPT
|
||||
*/
|
||||
static int SQLITE_TCLAPI DbEvalNextCmd(
|
||||
ClientData data[], /* data[0] is the (DbEvalContext*) */
|
||||
@@ -1893,8 +1915,8 @@ static int SQLITE_TCLAPI DbEvalNextCmd(
|
||||
** is a pointer to a Tcl_Obj containing the script to run for each row
|
||||
** returned by the queries encapsulated in data[0]. */
|
||||
DbEvalContext *p = (DbEvalContext *)data[0];
|
||||
Tcl_Obj *pScript = (Tcl_Obj *)data[1];
|
||||
Tcl_Obj *pArray = p->pArray;
|
||||
Tcl_Obj * const pScript = (Tcl_Obj *)data[1];
|
||||
Tcl_Obj * const pVarName = p->pVarName;
|
||||
|
||||
while( (rc==TCL_OK || rc==TCL_CONTINUE) && TCL_OK==(rc = dbEvalStep(p)) ){
|
||||
int i;
|
||||
@@ -1902,15 +1924,46 @@ static int SQLITE_TCLAPI DbEvalNextCmd(
|
||||
Tcl_Obj **apColName;
|
||||
dbEvalRowInfo(p, &nCol, &apColName);
|
||||
for(i=0; i<nCol; i++){
|
||||
if( pArray==0 ){
|
||||
if( pVarName==0 ){
|
||||
Tcl_ObjSetVar2(interp, apColName[i], 0, dbEvalColumnValue(p,i), 0);
|
||||
}else if( (p->evalFlags & SQLITE_EVAL_WITHOUTNULLS)!=0
|
||||
&& sqlite3_column_type(p->pPreStmt->pStmt, i)==SQLITE_NULL
|
||||
&& sqlite3_column_type(p->pPreStmt->pStmt, i)==SQLITE_NULL
|
||||
){
|
||||
Tcl_UnsetVar2(interp, Tcl_GetString(pArray),
|
||||
Tcl_GetString(apColName[i]), 0);
|
||||
/* Remove NULL-containing column from the target container... */
|
||||
if( 0==(SQLITE_EVAL_ASDICT & p->evalFlags) ){
|
||||
/* Target is an array */
|
||||
Tcl_UnsetVar2(interp, Tcl_GetString(pVarName),
|
||||
Tcl_GetString(apColName[i]), 0);
|
||||
}else{
|
||||
/* Target is a dict */
|
||||
Tcl_Obj *pDict = Tcl_ObjGetVar2(interp, pVarName, NULL, 0);
|
||||
if( pDict ){
|
||||
if( Tcl_IsShared(pDict) ){
|
||||
pDict = Tcl_DuplicateObj(pDict);
|
||||
}
|
||||
if( Tcl_DictObjRemove(interp, pDict, apColName[i])==TCL_OK ){
|
||||
Tcl_ObjSetVar2(interp, pVarName, NULL, pDict, 0);
|
||||
}
|
||||
Tcl_BounceRefCount(pDict);
|
||||
}
|
||||
}
|
||||
}else if( 0==(SQLITE_EVAL_ASDICT & p->evalFlags) ){
|
||||
/* Target is an array: set target(colName) = colValue */
|
||||
Tcl_ObjSetVar2(interp, pVarName, apColName[i],
|
||||
dbEvalColumnValue(p,i), 0);
|
||||
}else{
|
||||
Tcl_ObjSetVar2(interp, pArray, apColName[i], dbEvalColumnValue(p,i), 0);
|
||||
/* Target is a dict: set target(colName) = colValue */
|
||||
Tcl_Obj *pDict = Tcl_ObjGetVar2(interp, pVarName, NULL, 0);
|
||||
if( !pDict ){
|
||||
pDict = Tcl_NewDictObj();
|
||||
}else if( Tcl_IsShared(pDict) ){
|
||||
pDict = Tcl_DuplicateObj(pDict);
|
||||
}
|
||||
if( Tcl_DictObjPut(interp, pDict, apColName[i],
|
||||
dbEvalColumnValue(p,i))==TCL_OK ){
|
||||
Tcl_ObjSetVar2(interp, pVarName, NULL, pDict, 0);
|
||||
}
|
||||
Tcl_BounceRefCount(pDict);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2019,7 +2072,7 @@ static int SQLITE_TCLAPI DbObjCmd(
|
||||
"timeout", "total_changes", "trace",
|
||||
"trace_v2", "transaction", "unlock_notify",
|
||||
"update_hook", "version", "wal_hook",
|
||||
0
|
||||
0
|
||||
};
|
||||
enum DB_enum {
|
||||
DB_AUTHORIZER, DB_BACKUP, DB_BIND_FALLBACK,
|
||||
@@ -2853,13 +2906,15 @@ deserialize_error:
|
||||
}
|
||||
|
||||
/*
|
||||
** $db eval ?options? $sql ?array? ?{ ...code... }?
|
||||
** $db eval ?options? $sql ?varName? ?{ ...code... }?
|
||||
**
|
||||
** The SQL statement in $sql is evaluated. For each row, the values are
|
||||
** placed in elements of the array named "array" and ...code... is executed.
|
||||
** If "array" and "code" are omitted, then no callback is every invoked.
|
||||
** If "array" is an empty string, then the values are placed in variables
|
||||
** that have the same name as the fields extracted by the query.
|
||||
** The SQL statement in $sql is evaluated. For each row, the values
|
||||
** are placed in elements of the array or dict named $varName and
|
||||
** ...code... is executed. If $varName and $code are omitted, then
|
||||
** no callback is ever invoked. If $varName is an empty string,
|
||||
** then the values are placed in variables that have the same name
|
||||
** as the fields extracted by the query, and those variables are
|
||||
** accessible during the eval of $code.
|
||||
*/
|
||||
case DB_EVAL: {
|
||||
int evalFlags = 0;
|
||||
@@ -2867,8 +2922,9 @@ deserialize_error:
|
||||
while( objc>3 && (zOpt = Tcl_GetString(objv[2]))!=0 && zOpt[0]=='-' ){
|
||||
if( strcmp(zOpt, "-withoutnulls")==0 ){
|
||||
evalFlags |= SQLITE_EVAL_WITHOUTNULLS;
|
||||
}
|
||||
else{
|
||||
}else if( strcmp(zOpt, "-asdict")==0 ){
|
||||
evalFlags |= SQLITE_EVAL_ASDICT;
|
||||
}else{
|
||||
Tcl_AppendResult(interp, "unknown option: \"", zOpt, "\"", (void*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
@@ -2876,8 +2932,8 @@ deserialize_error:
|
||||
objv++;
|
||||
}
|
||||
if( objc<3 || objc>5 ){
|
||||
Tcl_WrongNumArgs(interp, 2, objv,
|
||||
"?OPTIONS? SQL ?ARRAY-NAME? ?SCRIPT?");
|
||||
Tcl_WrongNumArgs(interp, 2, objv,
|
||||
"?OPTIONS? SQL ?VAR-NAME? ?SCRIPT?");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
@@ -2903,17 +2959,17 @@ deserialize_error:
|
||||
}else{
|
||||
ClientData cd2[2];
|
||||
DbEvalContext *p;
|
||||
Tcl_Obj *pArray = 0;
|
||||
Tcl_Obj *pVarName = 0;
|
||||
Tcl_Obj *pScript;
|
||||
|
||||
if( objc>=5 && *(char *)Tcl_GetString(objv[3]) ){
|
||||
pArray = objv[3];
|
||||
pVarName = objv[3];
|
||||
}
|
||||
pScript = objv[objc-1];
|
||||
Tcl_IncrRefCount(pScript);
|
||||
|
||||
p = (DbEvalContext *)Tcl_Alloc(sizeof(DbEvalContext));
|
||||
dbEvalInit(p, pDb, objv[2], pArray, evalFlags);
|
||||
dbEvalInit(p, pDb, objv[2], pVarName, evalFlags);
|
||||
|
||||
cd2[0] = (void *)p;
|
||||
cd2[1] = (void *)pScript;
|
||||
|
||||
@@ -8458,7 +8458,12 @@ static int SQLITE_TCLAPI test_sqlite3_db_config(
|
||||
{ "DQS_DML", SQLITE_DBCONFIG_DQS_DML },
|
||||
{ "DQS_DDL", SQLITE_DBCONFIG_DQS_DDL },
|
||||
{ "LEGACY_FILE_FORMAT", SQLITE_DBCONFIG_LEGACY_FILE_FORMAT },
|
||||
{ "TRUSTED_SCHEMA", SQLITE_DBCONFIG_TRUSTED_SCHEMA },
|
||||
{ "STMT_SCANSTATUS", SQLITE_DBCONFIG_STMT_SCANSTATUS },
|
||||
{ "REVERSE_SCANORDER", SQLITE_DBCONFIG_REVERSE_SCANORDER },
|
||||
{ "ATTACH_CREATE", SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE },
|
||||
{ "ATTACH_WRITE", SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE },
|
||||
{ "COMMENTS", SQLITE_DBCONFIG_ENABLE_COMMENTS },
|
||||
};
|
||||
int i;
|
||||
int v = 0;
|
||||
|
||||
+4
-8
@@ -72,12 +72,8 @@
|
||||
#if !defined(_WIN32) || defined(__MSVCRT__)
|
||||
# include <unistd.h>
|
||||
# include <dirent.h>
|
||||
# ifndef DIRENT
|
||||
# define DIRENT dirent
|
||||
# endif
|
||||
#else
|
||||
# include <io.h>
|
||||
# include "test_windirent.h"
|
||||
# include "windirent.h"
|
||||
# ifndef S_ISREG
|
||||
# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
|
||||
# endif
|
||||
@@ -121,7 +117,7 @@ struct FsdirCsr {
|
||||
char *zDir; /* Buffer containing directory scanned */
|
||||
DIR *pDir; /* Open directory */
|
||||
sqlite3_int64 iRowid;
|
||||
struct DIRENT *pEntry;
|
||||
struct dirent *pEntry;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -483,9 +479,9 @@ static int fstreeFilter(
|
||||
char aWild[2] = { '\0', '\0' };
|
||||
|
||||
#ifdef _WIN32
|
||||
const char *zDrive = windirent_getenv("fstreeDrive");
|
||||
const char *zDrive = getenv("fstreeDrive");
|
||||
if( zDrive==0 ){
|
||||
zDrive = windirent_getenv("SystemDrive");
|
||||
zDrive = getenv("SystemDrive");
|
||||
}
|
||||
zRoot = sqlite3_mprintf("%s%c", zDrive, '/');
|
||||
nRoot = sqlite3Strlen30(zRoot);
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
/*
|
||||
** 2015 November 30
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains code to implement most of the opendir() family of
|
||||
** POSIX functions on Win32 using the MSVCRT.
|
||||
*/
|
||||
|
||||
#if defined(_WIN32) && defined(_MSC_VER)
|
||||
#include "test_windirent.h"
|
||||
|
||||
/*
|
||||
** Implementation of the POSIX getenv() function using the Win32 API.
|
||||
** This function is not thread-safe.
|
||||
*/
|
||||
const char *windirent_getenv(
|
||||
const char *name
|
||||
){
|
||||
static char value[32768]; /* Maximum length, per MSDN */
|
||||
DWORD dwSize = sizeof(value) / sizeof(char); /* Size in chars */
|
||||
DWORD dwRet; /* Value returned by GetEnvironmentVariableA() */
|
||||
|
||||
memset(value, 0, sizeof(value));
|
||||
dwRet = GetEnvironmentVariableA(name, value, dwSize);
|
||||
if( dwRet==0 || dwRet>dwSize ){
|
||||
/*
|
||||
** The function call to GetEnvironmentVariableA() failed -OR-
|
||||
** the buffer is not large enough. Either way, return NULL.
|
||||
*/
|
||||
return 0;
|
||||
}else{
|
||||
/*
|
||||
** The function call to GetEnvironmentVariableA() succeeded
|
||||
** -AND- the buffer contains the entire value.
|
||||
*/
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the POSIX opendir() function using the MSVCRT.
|
||||
*/
|
||||
LPDIR opendir(
|
||||
const char *dirname /* Directory name, UTF8 encoding */
|
||||
){
|
||||
struct _wfinddata_t data;
|
||||
LPDIR dirp = (LPDIR)sqlite3_malloc(sizeof(DIR));
|
||||
SIZE_T namesize = sizeof(data.name) / sizeof(data.name[0]);
|
||||
wchar_t *b1;
|
||||
sqlite3_int64 sz;
|
||||
|
||||
if( dirp==NULL ) return NULL;
|
||||
memset(dirp, 0, sizeof(DIR));
|
||||
|
||||
/* TODO: Remove this if Unix-style root paths are not used. */
|
||||
if( sqlite3_stricmp(dirname, "/")==0 ){
|
||||
dirname = windirent_getenv("SystemDrive");
|
||||
}
|
||||
|
||||
memset(&data, 0, sizeof(data));
|
||||
sz = strlen(dirname);
|
||||
b1 = sqlite3_malloc64( (sz+3)*sizeof(b1[0]) );
|
||||
if( b1==0 ){
|
||||
closedir(dirp);
|
||||
return NULL;
|
||||
}
|
||||
sz = MultiByteToWideChar(CP_UTF8, 0, dirname, sz, b1, sz);
|
||||
b1[sz++] = '\\';
|
||||
b1[sz++] = '*';
|
||||
b1[sz] = 0;
|
||||
if( sz+1>(sqlite3_int64)namesize ){
|
||||
closedir(dirp);
|
||||
sqlite3_free(b1);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(data.name, b1, (sz+1)*sizeof(b1[0]));
|
||||
sqlite3_free(b1);
|
||||
dirp->d_handle = _wfindfirst(data.name, &data);
|
||||
|
||||
if( dirp->d_handle==BAD_INTPTR_T ){
|
||||
closedir(dirp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* TODO: Remove this block to allow hidden and/or system files. */
|
||||
if( is_filtered(data) ){
|
||||
next:
|
||||
|
||||
memset(&data, 0, sizeof(data));
|
||||
if( _wfindnext(dirp->d_handle, &data)==-1 ){
|
||||
closedir(dirp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* TODO: Remove this block to allow hidden and/or system files. */
|
||||
if( is_filtered(data) ) goto next;
|
||||
}
|
||||
|
||||
dirp->d_first.d_attributes = data.attrib;
|
||||
WideCharToMultiByte(CP_UTF8, 0, data.name, -1,
|
||||
dirp->d_first.d_name, DIRENT_NAME_MAX, 0, 0);
|
||||
return dirp;
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the POSIX readdir() function using the MSVCRT.
|
||||
*/
|
||||
LPDIRENT readdir(
|
||||
LPDIR dirp
|
||||
){
|
||||
struct _wfinddata_t data;
|
||||
|
||||
if( dirp==NULL ) return NULL;
|
||||
|
||||
if( dirp->d_first.d_ino==0 ){
|
||||
dirp->d_first.d_ino++;
|
||||
dirp->d_next.d_ino++;
|
||||
|
||||
return &dirp->d_first;
|
||||
}
|
||||
|
||||
next:
|
||||
|
||||
memset(&data, 0, sizeof(data));
|
||||
if( _wfindnext(dirp->d_handle, &data)==-1 ) return NULL;
|
||||
|
||||
/* TODO: Remove this block to allow hidden and/or system files. */
|
||||
if( is_filtered(data) ) goto next;
|
||||
|
||||
dirp->d_next.d_ino++;
|
||||
dirp->d_next.d_attributes = data.attrib;
|
||||
WideCharToMultiByte(CP_UTF8, 0, data.name, -1,
|
||||
dirp->d_next.d_name, DIRENT_NAME_MAX, 0, 0);
|
||||
return &dirp->d_next;
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the POSIX closedir() function using the MSVCRT.
|
||||
*/
|
||||
INT closedir(
|
||||
LPDIR dirp
|
||||
){
|
||||
INT result = 0;
|
||||
|
||||
if( dirp==NULL ) return EINVAL;
|
||||
|
||||
if( dirp->d_handle!=NULL_INTPTR_T && dirp->d_handle!=BAD_INTPTR_T ){
|
||||
result = _findclose(dirp->d_handle);
|
||||
}
|
||||
|
||||
sqlite3_free(dirp);
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif /* defined(WIN32) && defined(_MSC_VER) */
|
||||
@@ -1,158 +0,0 @@
|
||||
/*
|
||||
** 2015 November 30
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains declarations for most of the opendir() family of
|
||||
** POSIX functions on Win32 using the MSVCRT.
|
||||
*/
|
||||
|
||||
#if defined(_WIN32) && defined(_MSC_VER) && !defined(SQLITE_WINDIRENT_H)
|
||||
#define SQLITE_WINDIRENT_H
|
||||
|
||||
/*
|
||||
** We need several data types from the Windows SDK header.
|
||||
*/
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#include "windows.h"
|
||||
|
||||
/*
|
||||
** We need several support functions from the SQLite core.
|
||||
*/
|
||||
|
||||
#include "sqlite3.h"
|
||||
|
||||
/*
|
||||
** We need several things from the ANSI and MSVCRT headers.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <io.h>
|
||||
#include <limits.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
/*
|
||||
** We may need several defines that should have been in "sys/stat.h".
|
||||
*/
|
||||
|
||||
#ifndef S_ISREG
|
||||
#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
|
||||
#endif
|
||||
|
||||
#ifndef S_ISDIR
|
||||
#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
|
||||
#endif
|
||||
|
||||
#ifndef S_ISLNK
|
||||
#define S_ISLNK(mode) (0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** We may need to provide the "mode_t" type.
|
||||
*/
|
||||
|
||||
#ifndef MODE_T_DEFINED
|
||||
#define MODE_T_DEFINED
|
||||
typedef unsigned short mode_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
** We may need to provide the "ino_t" type.
|
||||
*/
|
||||
|
||||
#ifndef INO_T_DEFINED
|
||||
#define INO_T_DEFINED
|
||||
typedef unsigned short ino_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
** We need to define "NAME_MAX" if it was not present in "limits.h".
|
||||
*/
|
||||
|
||||
#ifndef NAME_MAX
|
||||
# ifdef FILENAME_MAX
|
||||
# define NAME_MAX (FILENAME_MAX)
|
||||
# else
|
||||
# define NAME_MAX (260)
|
||||
# endif
|
||||
# define DIRENT_NAME_MAX (NAME_MAX)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** We need to define "NULL_INTPTR_T" and "BAD_INTPTR_T".
|
||||
*/
|
||||
|
||||
#ifndef NULL_INTPTR_T
|
||||
# define NULL_INTPTR_T ((intptr_t)(0))
|
||||
#endif
|
||||
|
||||
#ifndef BAD_INTPTR_T
|
||||
# define BAD_INTPTR_T ((intptr_t)(-1))
|
||||
#endif
|
||||
|
||||
/*
|
||||
** We need to provide the necessary structures and related types.
|
||||
*/
|
||||
|
||||
#ifndef DIRENT_DEFINED
|
||||
#define DIRENT_DEFINED
|
||||
typedef struct DIRENT DIRENT;
|
||||
typedef DIRENT *LPDIRENT;
|
||||
struct DIRENT {
|
||||
ino_t d_ino; /* Sequence number, do not use. */
|
||||
unsigned d_attributes; /* Win32 file attributes. */
|
||||
char d_name[NAME_MAX + 1]; /* Name within the directory. */
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef DIR_DEFINED
|
||||
#define DIR_DEFINED
|
||||
typedef struct DIR DIR;
|
||||
typedef DIR *LPDIR;
|
||||
struct DIR {
|
||||
intptr_t d_handle; /* Value returned by "_findfirst". */
|
||||
DIRENT d_first; /* DIRENT constructed based on "_findfirst". */
|
||||
DIRENT d_next; /* DIRENT constructed based on "_findnext". */
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Provide a macro, for use by the implementation, to determine if a
|
||||
** particular directory entry should be skipped over when searching for
|
||||
** the next directory entry that should be returned by the readdir().
|
||||
*/
|
||||
|
||||
#ifndef is_filtered
|
||||
# define is_filtered(a) ((((a).attrib)&_A_HIDDEN) || (((a).attrib)&_A_SYSTEM))
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Provide the function prototype for the POSIX compatible getenv()
|
||||
** function. This function is not thread-safe.
|
||||
*/
|
||||
|
||||
extern const char *windirent_getenv(const char *name);
|
||||
|
||||
/*
|
||||
** Finally, we can provide the function prototypes for the opendir(),
|
||||
** readdir(), and closedir() POSIX functions.
|
||||
*/
|
||||
|
||||
extern LPDIR opendir(const char *dirname);
|
||||
extern LPDIRENT readdir(LPDIR dirp);
|
||||
extern INT closedir(LPDIR dirp);
|
||||
|
||||
#endif /* defined(WIN32) && defined(_MSC_VER) */
|
||||
+2
-1
@@ -195,7 +195,8 @@ SQLITE_NOINLINE int sqlite3RunVacuum(
|
||||
saved_nChange = db->nChange;
|
||||
saved_nTotalChange = db->nTotalChange;
|
||||
saved_mTrace = db->mTrace;
|
||||
db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_Comments;
|
||||
db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_Comments
|
||||
| SQLITE_AttachCreate | SQLITE_AttachWrite;
|
||||
db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum;
|
||||
db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder
|
||||
| SQLITE_Defensive | SQLITE_CountRows);
|
||||
|
||||
+95
-12
@@ -792,6 +792,36 @@ static SQLITE_NOINLINE int vdbeColumnFromOverflow(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Send a "statement aborts" message to the error log.
|
||||
*/
|
||||
static SQLITE_NOINLINE void sqlite3VdbeLogAbort(
|
||||
Vdbe *p, /* The statement that is running at the time of failure */
|
||||
int rc, /* Error code */
|
||||
Op *pOp, /* Opcode that filed */
|
||||
Op *aOp /* All opcodes */
|
||||
){
|
||||
const char *zSql = p->zSql; /* Original SQL text */
|
||||
const char *zPrefix = ""; /* Prefix added to SQL text */
|
||||
int pc; /* Opcode address */
|
||||
char zXtra[100]; /* Buffer space to store zPrefix */
|
||||
|
||||
if( p->pFrame ){
|
||||
assert( aOp[0].opcode==OP_Init );
|
||||
if( aOp[0].p4.z!=0 ){
|
||||
assert( aOp[0].p4.z[0]=='-'
|
||||
&& aOp[0].p4.z[1]=='-'
|
||||
&& aOp[0].p4.z[2]==' ' );
|
||||
sqlite3_snprintf(sizeof(zXtra), zXtra,"/* %s */ ",aOp[0].p4.z+3);
|
||||
zPrefix = zXtra;
|
||||
}else{
|
||||
zPrefix = "/* unknown trigger */ ";
|
||||
}
|
||||
}
|
||||
pc = (int)(pOp - aOp);
|
||||
sqlite3_log(rc, "statement aborts at %d: %s; [%s%s]",
|
||||
pc, p->zErrMsg, zPrefix, zSql);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the symbolic name for the data type of a pMem
|
||||
@@ -1317,8 +1347,7 @@ case OP_Halt: {
|
||||
}else{
|
||||
sqlite3VdbeError(p, "%s", pOp->p4.z);
|
||||
}
|
||||
pcx = (int)(pOp - aOp);
|
||||
sqlite3_log(pOp->p1, "abort at %d: %s; [%s]", pcx, p->zErrMsg, p->zSql);
|
||||
sqlite3VdbeLogAbort(p, pOp->p1, pOp, aOp);
|
||||
}
|
||||
rc = sqlite3VdbeHalt(p);
|
||||
assert( rc==SQLITE_BUSY || rc==SQLITE_OK || rc==SQLITE_ERROR );
|
||||
@@ -2476,6 +2505,7 @@ case OP_Compare: {
|
||||
pKeyInfo = pOp->p4.pKeyInfo;
|
||||
assert( n>0 );
|
||||
assert( pKeyInfo!=0 );
|
||||
assert( pKeyInfo->aSortFlags!=0 );
|
||||
p1 = pOp->p1;
|
||||
p2 = pOp->p2;
|
||||
#ifdef SQLITE_DEBUG
|
||||
@@ -3238,6 +3268,15 @@ op_column_corrupt:
|
||||
** Take the affinities from the Table object in P4. If any value
|
||||
** cannot be coerced into the correct type, then raise an error.
|
||||
**
|
||||
** If P3==0, then omit checking of VIRTUAL columns.
|
||||
**
|
||||
** If P3==1, then omit checking of all generated column, both VIRTUAL
|
||||
** and STORED.
|
||||
**
|
||||
** If P3>=2, then only check column number P3-2 in the table (which will
|
||||
** be a VIRTUAL column) against the value in reg[P1]. In this case,
|
||||
** P2 will be 1.
|
||||
**
|
||||
** This opcode is similar to OP_Affinity except that this opcode
|
||||
** forces the register type to the Table column type. This is used
|
||||
** to implement "strict affinity".
|
||||
@@ -3251,8 +3290,8 @@ op_column_corrupt:
|
||||
**
|
||||
** <ul>
|
||||
** <li> P2 should be the number of non-virtual columns in the
|
||||
** table of P4.
|
||||
** <li> Table P4 should be a STRICT table.
|
||||
** table of P4 unless P3>1, in which case P2 will be 1.
|
||||
** <li> Table P4 is a STRICT table.
|
||||
** </ul>
|
||||
**
|
||||
** If any precondition is false, an assertion fault occurs.
|
||||
@@ -3261,16 +3300,28 @@ case OP_TypeCheck: {
|
||||
Table *pTab;
|
||||
Column *aCol;
|
||||
int i;
|
||||
int nCol;
|
||||
|
||||
assert( pOp->p4type==P4_TABLE );
|
||||
pTab = pOp->p4.pTab;
|
||||
assert( pTab->tabFlags & TF_Strict );
|
||||
assert( pTab->nNVCol==pOp->p2 );
|
||||
assert( pOp->p3>=0 && pOp->p3<pTab->nCol+2 );
|
||||
aCol = pTab->aCol;
|
||||
pIn1 = &aMem[pOp->p1];
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( aCol[i].colFlags & COLFLAG_GENERATED ){
|
||||
if( aCol[i].colFlags & COLFLAG_VIRTUAL ) continue;
|
||||
if( pOp->p3<2 ){
|
||||
assert( pTab->nNVCol==pOp->p2 );
|
||||
i = 0;
|
||||
nCol = pTab->nCol;
|
||||
}else{
|
||||
i = pOp->p3-2;
|
||||
nCol = i+1;
|
||||
assert( i<pTab->nCol );
|
||||
assert( aCol[i].colFlags & COLFLAG_VIRTUAL );
|
||||
assert( pOp->p2==1 );
|
||||
}
|
||||
for(; i<nCol; i++){
|
||||
if( (aCol[i].colFlags & COLFLAG_GENERATED)!=0 && pOp->p3<2 ){
|
||||
if( (aCol[i].colFlags & COLFLAG_VIRTUAL)!=0 ) continue;
|
||||
if( pOp->p3 ){ pIn1++; continue; }
|
||||
}
|
||||
assert( pIn1 < &aMem[pOp->p1+pOp->p2] );
|
||||
@@ -5349,7 +5400,7 @@ case OP_Found: { /* jump, in3, ncycle */
|
||||
if( rc ) goto no_mem;
|
||||
pIdxKey = sqlite3VdbeAllocUnpackedRecord(pC->pKeyInfo);
|
||||
if( pIdxKey==0 ) goto no_mem;
|
||||
sqlite3VdbeRecordUnpack(pC->pKeyInfo, r.aMem->n, r.aMem->z, pIdxKey);
|
||||
sqlite3VdbeRecordUnpack(r.aMem->n, r.aMem->z, pIdxKey);
|
||||
pIdxKey->default_rc = 0;
|
||||
rc = sqlite3BtreeIndexMoveto(pC->uc.pCursor, pIdxKey, &pC->seekResult);
|
||||
sqlite3DbFreeNN(db, pIdxKey);
|
||||
@@ -6347,6 +6398,32 @@ case OP_Rewind: { /* jump0, ncycle */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: IfEmpty P1 P2 * * *
|
||||
** Synopsis: if( empty(P1) ) goto P2
|
||||
**
|
||||
** Check to see if the b-tree table that cursor P1 references is empty
|
||||
** and jump to P2 if it is.
|
||||
*/
|
||||
case OP_IfEmpty: { /* jump */
|
||||
VdbeCursor *pC;
|
||||
BtCursor *pCrsr;
|
||||
int res;
|
||||
|
||||
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
||||
assert( pOp->p2>=0 && pOp->p2<p->nOp );
|
||||
|
||||
pC = p->apCsr[pOp->p1];
|
||||
assert( pC!=0 );
|
||||
assert( pC->eCurType==CURTYPE_BTREE );
|
||||
pCrsr = pC->uc.pCursor;
|
||||
assert( pCrsr );
|
||||
rc = sqlite3BtreeIsEmpty(pCrsr, &res);
|
||||
if( rc ) goto abort_due_to_error;
|
||||
VdbeBranchTaken(res!=0,2);
|
||||
if( res ) goto jump_to_p2;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: Next P1 P2 P3 * P5
|
||||
**
|
||||
** Advance cursor P1 so that it points to the next key/data pair in its
|
||||
@@ -8218,7 +8295,14 @@ case OP_VOpen: { /* ncycle */
|
||||
const sqlite3_module *pModule;
|
||||
|
||||
assert( p->bIsReader );
|
||||
pCur = 0;
|
||||
pCur = p->apCsr[pOp->p1];
|
||||
if( pCur!=0
|
||||
&& ALWAYS( pCur->eCurType==CURTYPE_VTAB )
|
||||
&& ALWAYS( pCur->uc.pVCur->pVtab==pOp->p4.pVtab->pVtab )
|
||||
){
|
||||
/* This opcode is a no-op if the cursor is already open */
|
||||
break;
|
||||
}
|
||||
pVCur = 0;
|
||||
pVtab = pOp->p4.pVtab->pVtab;
|
||||
if( pVtab==0 || NEVER(pVtab->pModule==0) ){
|
||||
@@ -9160,8 +9244,7 @@ abort_due_to_error:
|
||||
p->rc = rc;
|
||||
sqlite3SystemError(db, rc);
|
||||
testcase( sqlite3GlobalConfig.xLog!=0 );
|
||||
sqlite3_log(rc, "statement aborts at %d: %s; [%s]",
|
||||
(int)(pOp - aOp), p->zErrMsg, p->zSql);
|
||||
sqlite3VdbeLogAbort(p, rc, pOp, aOp);
|
||||
if( p->eVdbeState==VDBE_RUN_STATE ) sqlite3VdbeHalt(p);
|
||||
if( rc==SQLITE_IOERR_NOMEM ) sqlite3OomFault(db);
|
||||
if( rc==SQLITE_CORRUPT && db->autoCommit==0 ){
|
||||
|
||||
+1
-1
@@ -301,7 +301,7 @@ void sqlite3VdbeSetVarmask(Vdbe*, int);
|
||||
int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*);
|
||||
int sqlite3BlobCompare(const Mem*, const Mem*);
|
||||
|
||||
void sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,UnpackedRecord*);
|
||||
void sqlite3VdbeRecordUnpack(int,const void*,UnpackedRecord*);
|
||||
int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*);
|
||||
int sqlite3VdbeRecordCompareWithSkip(int, const void *, UnpackedRecord *, int);
|
||||
UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo*);
|
||||
|
||||
+1
-1
@@ -557,7 +557,7 @@ struct PreUpdate {
|
||||
Table *pTab; /* Schema object being updated */
|
||||
Index *pPk; /* PK index if pTab is WITHOUT ROWID */
|
||||
sqlite3_value **apDflt; /* Array of default values, if required */
|
||||
u8 keyinfoSpace[SZ_KEYINFO(0)]; /* Space to hold pKeyinfo[0] content */
|
||||
u8 keyinfoSpace[SZ_KEYINFO_0]; /* Space to hold pKeyinfo[0] content */
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
+6
-1
@@ -2163,7 +2163,7 @@ static UnpackedRecord *vdbeUnpackRecord(
|
||||
pRet = sqlite3VdbeAllocUnpackedRecord(pKeyInfo);
|
||||
if( pRet ){
|
||||
memset(pRet->aMem, 0, sizeof(Mem)*(pKeyInfo->nKeyField+1));
|
||||
sqlite3VdbeRecordUnpack(pKeyInfo, nKey, pKey, pRet);
|
||||
sqlite3VdbeRecordUnpack(nKey, pKey, pRet);
|
||||
}
|
||||
return pRet;
|
||||
}
|
||||
@@ -2192,6 +2192,9 @@ int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
|
||||
}
|
||||
if( p->pPk ){
|
||||
iStore = sqlite3TableColumnToIndex(p->pPk, iIdx);
|
||||
}else if( iIdx >= p->pTab->nCol ){
|
||||
rc = SQLITE_MISUSE_BKPT;
|
||||
goto preupdate_old_out;
|
||||
}else{
|
||||
iStore = sqlite3TableColumnToStorage(p->pTab, iIdx);
|
||||
}
|
||||
@@ -2347,6 +2350,8 @@ int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
|
||||
}
|
||||
if( p->pPk && p->op!=SQLITE_UPDATE ){
|
||||
iStore = sqlite3TableColumnToIndex(p->pPk, iIdx);
|
||||
}else if( iIdx >= p->pTab->nCol ){
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
}else{
|
||||
iStore = sqlite3TableColumnToStorage(p->pTab, iIdx);
|
||||
}
|
||||
|
||||
+10
-16
@@ -4202,30 +4202,22 @@ void sqlite3VdbeSerialGet(
|
||||
return;
|
||||
}
|
||||
/*
|
||||
** This routine is used to allocate sufficient space for an UnpackedRecord
|
||||
** structure large enough to be used with sqlite3VdbeRecordUnpack() if
|
||||
** the first argument is a pointer to KeyInfo structure pKeyInfo.
|
||||
** Allocate sufficient space for an UnpackedRecord structure large enough
|
||||
** to hold a decoded index record for pKeyInfo.
|
||||
**
|
||||
** The space is either allocated using sqlite3DbMallocRaw() or from within
|
||||
** the unaligned buffer passed via the second and third arguments (presumably
|
||||
** stack space). If the former, then *ppFree is set to a pointer that should
|
||||
** be eventually freed by the caller using sqlite3DbFree(). Or, if the
|
||||
** allocation comes from the pSpace/szSpace buffer, *ppFree is set to NULL
|
||||
** before returning.
|
||||
**
|
||||
** If an OOM error occurs, NULL is returned.
|
||||
** The space is allocated using sqlite3DbMallocRaw(). If an OOM error
|
||||
** occurs, NULL is returned.
|
||||
*/
|
||||
UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(
|
||||
KeyInfo *pKeyInfo /* Description of the record */
|
||||
){
|
||||
UnpackedRecord *p; /* Unpacked record to return */
|
||||
int nByte; /* Number of bytes required for *p */
|
||||
u64 nByte; /* Number of bytes required for *p */
|
||||
assert( sizeof(UnpackedRecord) + sizeof(Mem)*65536 < 0x7fffffff );
|
||||
nByte = ROUND8P(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nKeyField+1);
|
||||
p = (UnpackedRecord *)sqlite3DbMallocRaw(pKeyInfo->db, nByte);
|
||||
if( !p ) return 0;
|
||||
p->aMem = (Mem*)&((char*)p)[ROUND8P(sizeof(UnpackedRecord))];
|
||||
assert( pKeyInfo->aSortFlags!=0 );
|
||||
p->pKeyInfo = pKeyInfo;
|
||||
p->nField = pKeyInfo->nKeyField + 1;
|
||||
return p;
|
||||
@@ -4237,7 +4229,6 @@ UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(
|
||||
** contents of the decoded record.
|
||||
*/
|
||||
void sqlite3VdbeRecordUnpack(
|
||||
KeyInfo *pKeyInfo, /* Information about the record format */
|
||||
int nKey, /* Size of the binary record */
|
||||
const void *pKey, /* The binary record */
|
||||
UnpackedRecord *p /* Populate this structure before returning. */
|
||||
@@ -4248,6 +4239,7 @@ void sqlite3VdbeRecordUnpack(
|
||||
u16 u; /* Unsigned loop counter */
|
||||
u32 szHdr;
|
||||
Mem *pMem = p->aMem;
|
||||
KeyInfo *pKeyInfo = p->pKeyInfo;
|
||||
|
||||
p->default_rc = 0;
|
||||
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
|
||||
@@ -4275,6 +4267,8 @@ void sqlite3VdbeRecordUnpack(
|
||||
** warnings from MSAN. */
|
||||
sqlite3VdbeMemSetNull(pMem-1);
|
||||
}
|
||||
testcase( u == pKeyInfo->nKeyField + 1 );
|
||||
testcase( u < pKeyInfo->nKeyField + 1 );
|
||||
assert( u<=pKeyInfo->nKeyField + 1 );
|
||||
p->nField = u;
|
||||
}
|
||||
@@ -5134,6 +5128,7 @@ RecordCompare sqlite3VdbeFindCompare(UnpackedRecord *p){
|
||||
** The easiest way to enforce this limit is to consider only records with
|
||||
** 13 fields or less. If the first field is an integer, the maximum legal
|
||||
** header size is (12*5 + 1 + 1) bytes. */
|
||||
assert( p->pKeyInfo->aSortFlags!=0 );
|
||||
if( p->pKeyInfo->nAllField<=13 ){
|
||||
int flags = p->aMem[0].flags;
|
||||
if( p->pKeyInfo->aSortFlags[0] ){
|
||||
@@ -5492,7 +5487,6 @@ void sqlite3VdbePreUpdateHook(
|
||||
i64 iKey2;
|
||||
PreUpdate preupdate;
|
||||
const char *zTbl = pTab->zName;
|
||||
static const u8 fakeSortOrder = 0;
|
||||
#ifdef SQLITE_DEBUG
|
||||
int nRealCol;
|
||||
if( pTab->tabFlags & TF_WithoutRowid ){
|
||||
@@ -5531,7 +5525,7 @@ void sqlite3VdbePreUpdateHook(
|
||||
preupdate.pKeyinfo->db = db;
|
||||
preupdate.pKeyinfo->enc = ENC(db);
|
||||
preupdate.pKeyinfo->nKeyField = pTab->nCol;
|
||||
preupdate.pKeyinfo->aSortFlags = (u8*)&fakeSortOrder;
|
||||
preupdate.pKeyinfo->aSortFlags = 0; /* Indicate .aColl, .nAllField uninit */
|
||||
preupdate.iKey1 = iKey1;
|
||||
preupdate.iKey2 = iKey2;
|
||||
preupdate.pTab = pTab;
|
||||
|
||||
+24
-9
@@ -385,7 +385,7 @@ static int blobReadWrite(
|
||||
int iOffset,
|
||||
int (*xCall)(BtCursor*, u32, u32, void*)
|
||||
){
|
||||
int rc;
|
||||
int rc = SQLITE_OK;
|
||||
Incrblob *p = (Incrblob *)pBlob;
|
||||
Vdbe *v;
|
||||
sqlite3 *db;
|
||||
@@ -425,17 +425,32 @@ static int blobReadWrite(
|
||||
** using the incremental-blob API, this works. For the sessions module
|
||||
** anyhow.
|
||||
*/
|
||||
sqlite3_int64 iKey;
|
||||
iKey = sqlite3BtreeIntegerKey(p->pCsr);
|
||||
assert( v->apCsr[0]!=0 );
|
||||
assert( v->apCsr[0]->eCurType==CURTYPE_BTREE );
|
||||
sqlite3VdbePreUpdateHook(
|
||||
v, v->apCsr[0], SQLITE_DELETE, p->zDb, p->pTab, iKey, -1, p->iCol
|
||||
);
|
||||
if( sqlite3BtreeCursorIsValidNN(p->pCsr)==0 ){
|
||||
/* If the cursor is not currently valid, try to reseek it. This
|
||||
** always either fails or finds the correct row - the cursor will
|
||||
** have been marked permanently CURSOR_INVALID if the open row has
|
||||
** been deleted. */
|
||||
int bDiff = 0;
|
||||
rc = sqlite3BtreeCursorRestore(p->pCsr, &bDiff);
|
||||
assert( bDiff==0 || sqlite3BtreeCursorIsValidNN(p->pCsr)==0 );
|
||||
}
|
||||
if( sqlite3BtreeCursorIsValidNN(p->pCsr) ){
|
||||
sqlite3_int64 iKey;
|
||||
iKey = sqlite3BtreeIntegerKey(p->pCsr);
|
||||
assert( v->apCsr[0]!=0 );
|
||||
assert( v->apCsr[0]->eCurType==CURTYPE_BTREE );
|
||||
sqlite3VdbePreUpdateHook(
|
||||
v, v->apCsr[0], SQLITE_DELETE, p->zDb, p->pTab, iKey, -1, p->iCol
|
||||
);
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = xCall(p->pCsr, iOffset+p->iOffset, n, z);
|
||||
}
|
||||
#else
|
||||
rc = xCall(p->pCsr, iOffset+p->iOffset, n, z);
|
||||
#endif
|
||||
|
||||
rc = xCall(p->pCsr, iOffset+p->iOffset, n, z);
|
||||
sqlite3BtreeLeaveCursor(p->pCsr);
|
||||
if( rc==SQLITE_ABORT ){
|
||||
sqlite3VdbeFinalize(v);
|
||||
|
||||
+12
-4
@@ -766,7 +766,7 @@ static int vdbeSorterCompareTail(
|
||||
){
|
||||
UnpackedRecord *r2 = pTask->pUnpacked;
|
||||
if( *pbKey2Cached==0 ){
|
||||
sqlite3VdbeRecordUnpack(pTask->pSorter->pKeyInfo, nKey2, pKey2, r2);
|
||||
sqlite3VdbeRecordUnpack(nKey2, pKey2, r2);
|
||||
*pbKey2Cached = 1;
|
||||
}
|
||||
return sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, r2, 1);
|
||||
@@ -793,7 +793,7 @@ static int vdbeSorterCompare(
|
||||
){
|
||||
UnpackedRecord *r2 = pTask->pUnpacked;
|
||||
if( !*pbKey2Cached ){
|
||||
sqlite3VdbeRecordUnpack(pTask->pSorter->pKeyInfo, nKey2, pKey2, r2);
|
||||
sqlite3VdbeRecordUnpack(nKey2, pKey2, r2);
|
||||
*pbKey2Cached = 1;
|
||||
}
|
||||
return sqlite3VdbeRecordCompare(nKey1, pKey1, r2);
|
||||
@@ -833,6 +833,7 @@ static int vdbeSorterCompareText(
|
||||
);
|
||||
}
|
||||
}else{
|
||||
assert( pTask->pSorter->pKeyInfo->aSortFlags!=0 );
|
||||
assert( !(pTask->pSorter->pKeyInfo->aSortFlags[0]&KEYINFO_ORDER_BIGNULL) );
|
||||
if( pTask->pSorter->pKeyInfo->aSortFlags[0] ){
|
||||
res = res * -1;
|
||||
@@ -896,6 +897,7 @@ static int vdbeSorterCompareInt(
|
||||
}
|
||||
}
|
||||
|
||||
assert( pTask->pSorter->pKeyInfo->aSortFlags!=0 );
|
||||
if( res==0 ){
|
||||
if( pTask->pSorter->pKeyInfo->nKeyField>1 ){
|
||||
res = vdbeSorterCompareTail(
|
||||
@@ -969,7 +971,8 @@ int sqlite3VdbeSorterInit(
|
||||
assert( pCsr->eCurType==CURTYPE_SORTER );
|
||||
assert( sizeof(KeyInfo) + UMXV(pCsr->pKeyInfo->nKeyField)*sizeof(CollSeq*)
|
||||
< 0x7fffffff );
|
||||
szKeyInfo = SZ_KEYINFO(pCsr->pKeyInfo->nKeyField);
|
||||
assert( pCsr->pKeyInfo->nKeyField<=pCsr->pKeyInfo->nAllField );
|
||||
szKeyInfo = SZ_KEYINFO(pCsr->pKeyInfo->nAllField);
|
||||
sz = SZ_VDBESORTER(nWorker+1);
|
||||
|
||||
pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo);
|
||||
@@ -983,7 +986,12 @@ int sqlite3VdbeSorterInit(
|
||||
pKeyInfo->db = 0;
|
||||
if( nField && nWorker==0 ){
|
||||
pKeyInfo->nKeyField = nField;
|
||||
assert( nField<=pCsr->pKeyInfo->nAllField );
|
||||
}
|
||||
/* It is OK that pKeyInfo reuses the aSortFlags field from pCsr->pKeyInfo,
|
||||
** since the pCsr->pKeyInfo->aSortFlags[] array is invariant and lives
|
||||
** longer that pSorter. */
|
||||
assert( pKeyInfo->aSortFlags==pCsr->pKeyInfo->aSortFlags );
|
||||
sqlite3BtreeEnter(pBt);
|
||||
pSorter->pgsz = pgsz = sqlite3BtreeGetPageSize(pBt);
|
||||
sqlite3BtreeLeave(pBt);
|
||||
@@ -2763,7 +2771,7 @@ int sqlite3VdbeSorterCompare(
|
||||
assert( r2->nField==nKeyCol );
|
||||
|
||||
pKey = vdbeSorterRowkey(pSorter, &nKey);
|
||||
sqlite3VdbeRecordUnpack(pKeyInfo, nKey, pKey, r2);
|
||||
sqlite3VdbeRecordUnpack(nKey, pKey, r2);
|
||||
for(i=0; i<nKeyCol; i++){
|
||||
if( r2->aMem[i].flags & MEM_Null ){
|
||||
*pRes = -1;
|
||||
|
||||
+33
-7
@@ -426,11 +426,11 @@ static WhereTerm *whereScanNext(WhereScan *pScan){
|
||||
pScan->pWC = pWC;
|
||||
pScan->k = k+1;
|
||||
#ifdef WHERETRACE_ENABLED
|
||||
if( sqlite3WhereTrace & 0x20000 ){
|
||||
if( (sqlite3WhereTrace & 0x20000)!=0 && pScan->nEquiv>1 ){
|
||||
int ii;
|
||||
sqlite3DebugPrintf("SCAN-TERM %p: nEquiv=%d",
|
||||
pTerm, pScan->nEquiv);
|
||||
for(ii=0; ii<pScan->nEquiv; ii++){
|
||||
sqlite3DebugPrintf("EQUIVALENT TO {%d:%d} (due to TERM-%d):",
|
||||
pScan->aiCur[0], pScan->aiColumn[0], pTerm->iTerm);
|
||||
for(ii=1; ii<pScan->nEquiv; ii++){
|
||||
sqlite3DebugPrintf(" {%d:%d}",
|
||||
pScan->aiCur[ii], pScan->aiColumn[ii]);
|
||||
}
|
||||
@@ -1201,7 +1201,9 @@ static SQLITE_NOINLINE void constructAutomaticIndex(
|
||||
VdbeCoverage(v);
|
||||
VdbeComment((v, "next row of %s", pSrc->pSTab->zName));
|
||||
}else{
|
||||
addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); VdbeCoverage(v);
|
||||
assert( pLevel->addrHalt );
|
||||
addrTop = sqlite3VdbeAddOp2(v, OP_Rewind,pLevel->iTabCur,pLevel->addrHalt);
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
if( pPartial ){
|
||||
iContinue = sqlite3VdbeMakeLabel(pParse);
|
||||
@@ -1229,11 +1231,14 @@ static SQLITE_NOINLINE void constructAutomaticIndex(
|
||||
pSrc->u4.pSubq->regResult, pLevel->iIdxCur);
|
||||
sqlite3VdbeGoto(v, addrTop);
|
||||
pSrc->fg.viaCoroutine = 0;
|
||||
sqlite3VdbeJumpHere(v, addrTop);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v);
|
||||
sqlite3VdbeChangeP5(v, SQLITE_STMTSTATUS_AUTOINDEX);
|
||||
if( (pSrc->fg.jointype & JT_LEFT)!=0 ){
|
||||
sqlite3VdbeJumpHere(v, addrTop);
|
||||
}
|
||||
}
|
||||
sqlite3VdbeJumpHere(v, addrTop);
|
||||
sqlite3ReleaseTempReg(pParse, regRecord);
|
||||
|
||||
/* Jump here when skipping the initialization */
|
||||
@@ -2385,6 +2390,7 @@ void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm){
|
||||
}else{
|
||||
sqlite3_snprintf(sizeof(zLeft),zLeft,"left=%d", pTerm->leftCursor);
|
||||
}
|
||||
iTerm = pTerm->iTerm = MAX(iTerm,pTerm->iTerm);
|
||||
sqlite3DebugPrintf(
|
||||
"TERM-%-3d %p %s %-12s op=%03x wtFlags=%04x",
|
||||
iTerm, pTerm, zType, zLeft, pTerm->eOperator, pTerm->wtFlags);
|
||||
@@ -3526,6 +3532,7 @@ static int whereLoopAddBtreeIndex(
|
||||
&& pProbe->hasStat1!=0
|
||||
&& OptimizationEnabled(db, SQLITE_SkipScan)
|
||||
&& pProbe->aiRowLogEst[saved_nEq+1]>=42 /* TUNING: Minimum for skip-scan */
|
||||
&& pSrc->fg.fromExists==0
|
||||
&& (rc = whereLoopResize(db, pNew, pNew->nLTerm+1))==SQLITE_OK
|
||||
){
|
||||
LogEst nIter;
|
||||
@@ -4030,7 +4037,6 @@ static int whereLoopAddBtree(
|
||||
pNew->u.btree.nEq = 0;
|
||||
pNew->u.btree.nBtm = 0;
|
||||
pNew->u.btree.nTop = 0;
|
||||
pNew->u.btree.nDistinctCol = 0;
|
||||
pNew->nSkip = 0;
|
||||
pNew->nLTerm = 0;
|
||||
pNew->iSortIdx = 0;
|
||||
@@ -5099,6 +5105,8 @@ static i8 wherePathSatisfiesOrderBy(
|
||||
obSat = obDone;
|
||||
}
|
||||
break;
|
||||
}else if( wctrlFlags & WHERE_DISTINCTBY ){
|
||||
pLoop->u.btree.nDistinctCol = 0;
|
||||
}
|
||||
iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor;
|
||||
|
||||
@@ -7082,6 +7090,14 @@ WhereInfo *sqlite3WhereBegin(
|
||||
pTab = pTabItem->pSTab;
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
pLoop = pLevel->pWLoop;
|
||||
pLevel->addrBrk = sqlite3VdbeMakeLabel(pParse);
|
||||
if( ii==0 || (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){
|
||||
pLevel->addrHalt = pLevel->addrBrk;
|
||||
}else if( pWInfo->a[ii-1].pRJ ){
|
||||
pLevel->addrHalt = pWInfo->a[ii-1].addrBrk;
|
||||
}else{
|
||||
pLevel->addrHalt = pWInfo->a[ii-1].addrHalt;
|
||||
}
|
||||
if( (pTab->tabFlags & TF_Ephemeral)!=0 || IsView(pTab) ){
|
||||
/* Do nothing */
|
||||
}else
|
||||
@@ -7133,6 +7149,13 @@ WhereInfo *sqlite3WhereBegin(
|
||||
sqlite3VdbeAddOp4Dup8(v, OP_ColumnsUsed, pTabItem->iCursor, 0, 0,
|
||||
(const u8*)&pTabItem->colUsed, P4_INT64);
|
||||
#endif
|
||||
if( ii>=2
|
||||
&& (pTabItem[0].fg.jointype & (JT_LTORJ|JT_LEFT))==0
|
||||
&& pLevel->addrHalt==pWInfo->a[0].addrHalt
|
||||
){
|
||||
sqlite3VdbeAddOp2(v, OP_IfEmpty, pTabItem->iCursor, pWInfo->iBreak);
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
}else{
|
||||
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
|
||||
}
|
||||
@@ -7389,6 +7412,9 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 1, pLevel->p2);
|
||||
}
|
||||
#endif /* SQLITE_DISABLE_SKIPAHEAD_DISTINCT */
|
||||
if( pTabList->a[pLevel->iFrom].fg.fromExists ){
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+2);
|
||||
}
|
||||
/* The common case: Advance to the next row */
|
||||
if( pLevel->addrCont ) sqlite3VdbeResolveLabel(v, pLevel->addrCont);
|
||||
sqlite3VdbeAddOp3(v, pLevel->op, pLevel->p1, pLevel->p2, pLevel->p3);
|
||||
|
||||
@@ -75,6 +75,7 @@ struct WhereLevel {
|
||||
int iTabCur; /* The VDBE cursor used to access the table */
|
||||
int iIdxCur; /* The VDBE cursor used to access pIdx */
|
||||
int addrBrk; /* Jump here to break out of the loop */
|
||||
int addrHalt; /* Abort the query due to empty table or similar */
|
||||
int addrNxt; /* Jump here to start the next IN combination */
|
||||
int addrSkip; /* Jump here for next iteration of skip-scan */
|
||||
int addrCont; /* Jump here to continue with the next loop cycle */
|
||||
@@ -280,6 +281,9 @@ struct WhereTerm {
|
||||
u8 eMatchOp; /* Op for vtab MATCH/LIKE/GLOB/REGEXP terms */
|
||||
int iParent; /* Disable pWC->a[iParent] when this term disabled */
|
||||
int leftCursor; /* Cursor number of X in "X <op> <expr>" */
|
||||
#ifdef SQLITE_DEBUG
|
||||
int iTerm; /* Which WhereTerm is this, for debug purposes */
|
||||
#endif
|
||||
union {
|
||||
struct {
|
||||
int leftColumn; /* Column number of X in "X <op> <expr>" */
|
||||
|
||||
+47
-53
@@ -110,7 +110,7 @@ static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop){
|
||||
}
|
||||
|
||||
/*
|
||||
** This function sets the P4 value of an existing OP_Explain opcode to
|
||||
** This function sets the P4 value of an existing OP_Explain opcode to
|
||||
** text describing the loop in pLevel. If the OP_Explain opcode already has
|
||||
** a P4 value, it is freed before it is overwritten.
|
||||
*/
|
||||
@@ -126,7 +126,6 @@ void sqlite3WhereAddExplainText(
|
||||
#endif
|
||||
{
|
||||
VdbeOp *pOp = sqlite3VdbeGetOp(pParse->pVdbe, addr);
|
||||
|
||||
SrcItem *pItem = &pTabList->a[pLevel->iFrom];
|
||||
sqlite3 *db = pParse->db; /* Database handle */
|
||||
int isSearch; /* True for a SEARCH. False for SCAN. */
|
||||
@@ -149,7 +148,10 @@ void sqlite3WhereAddExplainText(
|
||||
|
||||
sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
|
||||
str.printfFlags = SQLITE_PRINTF_INTERNAL;
|
||||
sqlite3_str_appendf(&str, "%s %S", isSearch ? "SEARCH" : "SCAN", pItem);
|
||||
sqlite3_str_appendf(&str, "%s %S%s",
|
||||
isSearch ? "SEARCH" : "SCAN",
|
||||
pItem,
|
||||
pItem->fg.fromExists ? " EXISTS" : "");
|
||||
if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){
|
||||
const char *zFmt = 0;
|
||||
Index *pIdx;
|
||||
@@ -941,7 +943,7 @@ static int codeAllEqualityTerms(
|
||||
testcase( pIdx->aiColumn[j]==XN_EXPR );
|
||||
VdbeComment((v, "%s", explainIndexColumnName(pIdx, j)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Evaluate the equality constraints
|
||||
*/
|
||||
@@ -1282,7 +1284,7 @@ static void codeDeferredSeek(
|
||||
|
||||
assert( iIdxCur>0 );
|
||||
assert( pIdx->aiColumn[pIdx->nColumn-1]==-1 );
|
||||
|
||||
|
||||
pWInfo->bDeferredSeek = 1;
|
||||
sqlite3VdbeAddOp3(v, OP_DeferredSeek, iIdxCur, 0, iCur);
|
||||
if( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))
|
||||
@@ -1393,6 +1395,7 @@ static SQLITE_NOINLINE void filterPullDown(
|
||||
int addrNxt, /* Jump here to bypass inner loops */
|
||||
Bitmask notReady /* Loops that are not ready */
|
||||
){
|
||||
int saved_addrBrk;
|
||||
while( ++iLevel < pWInfo->nLevel ){
|
||||
WhereLevel *pLevel = &pWInfo->a[iLevel];
|
||||
WhereLoop *pLoop = pLevel->pWLoop;
|
||||
@@ -1401,7 +1404,7 @@ static SQLITE_NOINLINE void filterPullDown(
|
||||
/* ,--- Because sqlite3ConstructBloomFilter() has will not have set
|
||||
** vvvvv--' pLevel->regFilter if this were true. */
|
||||
if( NEVER(pLoop->prereq & notReady) ) continue;
|
||||
assert( pLevel->addrBrk==0 );
|
||||
saved_addrBrk = pLevel->addrBrk;
|
||||
pLevel->addrBrk = addrNxt;
|
||||
if( pLoop->wsFlags & WHERE_IPK ){
|
||||
WhereTerm *pTerm = pLoop->aLTerm[0];
|
||||
@@ -1431,19 +1434,19 @@ static SQLITE_NOINLINE void filterPullDown(
|
||||
VdbeCoverage(pParse->pVdbe);
|
||||
}
|
||||
pLevel->regFilter = 0;
|
||||
pLevel->addrBrk = 0;
|
||||
pLevel->addrBrk = saved_addrBrk;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Loop pLoop is a WHERE_INDEXED level that uses at least one IN(...)
|
||||
** operator. Return true if level pLoop is guaranteed to visit only one
|
||||
** Loop pLoop is a WHERE_INDEXED level that uses at least one IN(...)
|
||||
** operator. Return true if level pLoop is guaranteed to visit only one
|
||||
** row for each key generated for the index.
|
||||
*/
|
||||
static int whereLoopIsOneRow(WhereLoop *pLoop){
|
||||
if( pLoop->u.btree.pIndex->onError
|
||||
&& pLoop->nSkip==0
|
||||
&& pLoop->u.btree.nEq==pLoop->u.btree.pIndex->nKeyCol
|
||||
if( pLoop->u.btree.pIndex->onError
|
||||
&& pLoop->nSkip==0
|
||||
&& pLoop->u.btree.nEq==pLoop->u.btree.pIndex->nKeyCol
|
||||
){
|
||||
int ii;
|
||||
for(ii=0; ii<pLoop->u.btree.nEq; ii++){
|
||||
@@ -1478,7 +1481,6 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
sqlite3 *db; /* Database connection */
|
||||
SrcItem *pTabItem; /* FROM clause term being coded */
|
||||
int addrBrk; /* Jump here to break out of the loop */
|
||||
int addrHalt; /* addrBrk for the outermost loop */
|
||||
int addrCont; /* Jump here to continue with next cycle */
|
||||
int iRowidReg = 0; /* Rowid is stored in this register, if not zero */
|
||||
int iReleaseReg = 0; /* Temp register to free before returning */
|
||||
@@ -1522,7 +1524,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
** there are no IN operators in the constraints, the "addrNxt" label
|
||||
** is the same as "addrBrk".
|
||||
*/
|
||||
addrBrk = pLevel->addrBrk = pLevel->addrNxt = sqlite3VdbeMakeLabel(pParse);
|
||||
addrBrk = pLevel->addrNxt = pLevel->addrBrk;
|
||||
addrCont = pLevel->addrCont = sqlite3VdbeMakeLabel(pParse);
|
||||
|
||||
/* If this is the right table of a LEFT OUTER JOIN, allocate and
|
||||
@@ -1538,14 +1540,6 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
VdbeComment((v, "init LEFT JOIN match flag"));
|
||||
}
|
||||
|
||||
/* Compute a safe address to jump to if we discover that the table for
|
||||
** this loop is empty and can never contribute content. */
|
||||
for(j=iLevel; j>0; j--){
|
||||
if( pWInfo->a[j].iLeftJoin ) break;
|
||||
if( pWInfo->a[j].pRJ ) break;
|
||||
}
|
||||
addrHalt = pWInfo->a[j].addrBrk;
|
||||
|
||||
/* Special case of a FROM clause subquery implemented as a co-routine */
|
||||
if( pTabItem->fg.viaCoroutine ){
|
||||
int regYield;
|
||||
@@ -1784,7 +1778,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
VdbeCoverageIf(v, pX->op==TK_GE);
|
||||
sqlite3ReleaseTempReg(pParse, rTemp);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iCur, addrHalt);
|
||||
sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iCur, pLevel->addrHalt);
|
||||
VdbeCoverageIf(v, bRev==0);
|
||||
VdbeCoverageIf(v, bRev!=0);
|
||||
}
|
||||
@@ -1824,37 +1818,37 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC | SQLITE_JUMPIFNULL);
|
||||
}
|
||||
}else if( pLoop->wsFlags & WHERE_INDEXED ){
|
||||
/* Case 4: A scan using an index.
|
||||
/* Case 4: Search using an index.
|
||||
**
|
||||
** The WHERE clause may contain zero or more equality
|
||||
** terms ("==" or "IN" operators) that refer to the N
|
||||
** left-most columns of the index. It may also contain
|
||||
** inequality constraints (>, <, >= or <=) on the indexed
|
||||
** column that immediately follows the N equalities. Only
|
||||
** the right-most column can be an inequality - the rest must
|
||||
** use the "==" and "IN" operators. For example, if the
|
||||
** index is on (x,y,z), then the following clauses are all
|
||||
** optimized:
|
||||
** The WHERE clause may contain zero or more equality
|
||||
** terms ("==" or "IN" or "IS" operators) that refer to the N
|
||||
** left-most columns of the index. It may also contain
|
||||
** inequality constraints (>, <, >= or <=) on the indexed
|
||||
** column that immediately follows the N equalities. Only
|
||||
** the right-most column can be an inequality - the rest must
|
||||
** use the "==", "IN", or "IS" operators. For example, if the
|
||||
** index is on (x,y,z), then the following clauses are all
|
||||
** optimized:
|
||||
**
|
||||
** x=5
|
||||
** x=5 AND y=10
|
||||
** x=5 AND y<10
|
||||
** x=5 AND y>5 AND y<10
|
||||
** x=5 AND y=5 AND z<=10
|
||||
** x=5
|
||||
** x=5 AND y=10
|
||||
** x=5 AND y<10
|
||||
** x=5 AND y>5 AND y<10
|
||||
** x=5 AND y=5 AND z<=10
|
||||
**
|
||||
** The z<10 term of the following cannot be used, only
|
||||
** the x=5 term:
|
||||
** The z<10 term of the following cannot be used, only
|
||||
** the x=5 term:
|
||||
**
|
||||
** x=5 AND z<10
|
||||
** x=5 AND z<10
|
||||
**
|
||||
** N may be zero if there are inequality constraints.
|
||||
** If there are no inequality constraints, then N is at
|
||||
** least one.
|
||||
** N may be zero if there are inequality constraints.
|
||||
** If there are no inequality constraints, then N is at
|
||||
** least one.
|
||||
**
|
||||
** This case is also used when there are no WHERE clause
|
||||
** constraints but an index is selected anyway, in order
|
||||
** to force the output order to conform to an ORDER BY.
|
||||
*/
|
||||
** This case is also used when there are no WHERE clause
|
||||
** constraints but an index is selected anyway, in order
|
||||
** to force the output order to conform to an ORDER BY.
|
||||
*/
|
||||
static const u8 aStartOp[] = {
|
||||
0,
|
||||
0,
|
||||
@@ -2008,7 +2002,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
}
|
||||
if( zStartAff ){
|
||||
updateRangeAffinityStr(pRight, nBtm, &zStartAff[nEq]);
|
||||
}
|
||||
}
|
||||
nConstraint += nBtm;
|
||||
testcase( pRangeStart->wtFlags & TERM_VIRTUAL );
|
||||
if( sqlite3ExprIsVector(pRight)==0 ){
|
||||
@@ -2210,7 +2204,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
** a LEFT JOIN: */
|
||||
assert( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))==0 );
|
||||
}
|
||||
|
||||
|
||||
/* Record the instruction used to terminate the loop. */
|
||||
if( (pLoop->wsFlags & WHERE_ONEROW)
|
||||
|| (pLevel->u.in.nIn && regBignull==0 && whereLoopIsOneRow(pLoop))
|
||||
@@ -2579,7 +2573,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
codeCursorHint(pTabItem, pWInfo, pLevel, 0);
|
||||
pLevel->op = aStep[bRev];
|
||||
pLevel->p1 = iCur;
|
||||
pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrHalt);
|
||||
pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev],iCur,pLevel->addrHalt);
|
||||
VdbeCoverageIf(v, bRev==0);
|
||||
VdbeCoverageIf(v, bRev!=0);
|
||||
pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP;
|
||||
@@ -2599,7 +2593,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
**
|
||||
** iLoop==1: Code only expressions that are entirely covered by pIdx.
|
||||
** iLoop==2: Code remaining expressions that do not contain correlated
|
||||
** sub-queries.
|
||||
** sub-queries.
|
||||
** iLoop==3: Code all remaining expressions.
|
||||
**
|
||||
** An effort is made to skip unnecessary iterations of the loop.
|
||||
@@ -2870,7 +2864,7 @@ SQLITE_NOINLINE void sqlite3WhereRightJoinLoop(
|
||||
pSubq = pRight->u4.pSubq;
|
||||
assert( pSubq->pSelect!=0 && pSubq->pSelect->pEList!=0 );
|
||||
sqlite3VdbeAddOp3(
|
||||
v, OP_Null, 0, pSubq->regResult,
|
||||
v, OP_Null, 0, pSubq->regResult,
|
||||
pSubq->regResult + pSubq->pSelect->pEList->nExpr-1
|
||||
);
|
||||
}
|
||||
|
||||
+20
-3
@@ -1124,6 +1124,9 @@ static void exprAnalyze(
|
||||
}
|
||||
assert( pWC->nTerm > idxTerm );
|
||||
pTerm = &pWC->a[idxTerm];
|
||||
#ifdef SQLITE_DEBUG
|
||||
pTerm->iTerm = idxTerm;
|
||||
#endif
|
||||
pMaskSet = &pWInfo->sMaskSet;
|
||||
pExpr = pTerm->pExpr;
|
||||
assert( pExpr!=0 ); /* Because malloc() has not failed */
|
||||
@@ -1649,7 +1652,7 @@ static void whereAddLimitExpr(
|
||||
**
|
||||
** 1. The SELECT statement has a LIMIT clause, and
|
||||
** 2. The SELECT statement is not an aggregate or DISTINCT query, and
|
||||
** 3. The SELECT statement has exactly one object in its from clause, and
|
||||
** 3. The SELECT statement has exactly one object in its FROM clause, and
|
||||
** that object is a virtual table, and
|
||||
** 4. There are no terms in the WHERE clause that will not be passed
|
||||
** to the virtual table xBestIndex method.
|
||||
@@ -1686,8 +1689,22 @@ void SQLITE_NOINLINE sqlite3WhereAddLimit(WhereClause *pWC, Select *p){
|
||||
** (leftCursor==iCsr) test below. */
|
||||
continue;
|
||||
}
|
||||
if( pWC->a[ii].leftCursor!=iCsr ) return;
|
||||
if( pWC->a[ii].prereqRight!=0 ) return;
|
||||
if( pWC->a[ii].leftCursor==iCsr && pWC->a[ii].prereqRight==0 ) continue;
|
||||
|
||||
/* If this term has a parent with exactly one child, and the parent will
|
||||
** be passed through to xBestIndex, then this term can be ignored. */
|
||||
if( pWC->a[ii].iParent>=0 ){
|
||||
WhereTerm *pParent = &pWC->a[ pWC->a[ii].iParent ];
|
||||
if( pParent->leftCursor==iCsr
|
||||
&& pParent->prereqRight==0
|
||||
&& pParent->nChild==1
|
||||
){
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* This term will not be passed through. Do not add a LIMIT clause. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check condition (5). Return early if it is not met. */
|
||||
|
||||
@@ -349,4 +349,78 @@ do_execsql_test 5.9 {
|
||||
three six seven
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
|
||||
reset_db
|
||||
register_tcl_module db
|
||||
|
||||
proc quote {str} {
|
||||
return "'[string map {' ''} $str]'"
|
||||
}
|
||||
|
||||
proc vtab_command {lVal method args} {
|
||||
switch -- $method {
|
||||
xConnect {
|
||||
return "CREATE TABLE t1(a, b, c, d)"
|
||||
}
|
||||
|
||||
xBestIndex {
|
||||
set hdl [lindex $args 0]
|
||||
set clist [$hdl constraints]
|
||||
|
||||
set idx 0
|
||||
set idxnum 0
|
||||
|
||||
foreach c $clist {
|
||||
array set a $c
|
||||
if {$a(usable)==0} continue
|
||||
|
||||
if {$a(op)=="limit"} {
|
||||
set idxnum [$hdl rhs_value $idx 555]
|
||||
}
|
||||
|
||||
incr idx
|
||||
}
|
||||
|
||||
return "cost 1000 rows 1000 idxnum $idxnum"
|
||||
|
||||
}
|
||||
|
||||
xFilter {
|
||||
foreach {idxnum idxstr lArg} $args {}
|
||||
return [list sql "SELECT 0, $idxnum, $idxnum, $idxnum, $idxnum"]
|
||||
}
|
||||
}
|
||||
|
||||
return {}
|
||||
}
|
||||
|
||||
do_execsql_test 6.0 {
|
||||
CREATE TABLE t1(x, y);
|
||||
INSERT INTO t1 VALUES(2, 2);
|
||||
CREATE VIRTUAL TABLE x1 USING tcl(vtab_command t1);
|
||||
}
|
||||
|
||||
do_execsql_test 6.1 { SELECT * FROM x1 LIMIT 50 } {50 50 50 50}
|
||||
|
||||
do_execsql_test 6.2 { SELECT * FROM x1 WHERE b=c LIMIT 5 } {0 0 0 0}
|
||||
|
||||
do_execsql_test 6.3 {
|
||||
SELECT (SELECT a FROM x1 WHERE t1.x=t1.y LIMIT 10) FROM t1
|
||||
} {0}
|
||||
|
||||
do_execsql_test 6.4 {
|
||||
SELECT (SELECT a FROM x1 WHERE x1.a=1) FROM t1
|
||||
} {1}
|
||||
|
||||
do_execsql_test 6.5 {
|
||||
SELECT (SELECT a FROM x1 WHERE x1.a=1 LIMIT 1) FROM t1
|
||||
} {1}
|
||||
|
||||
do_execsql_test 6.6 {
|
||||
SELECT (SELECT a FROM x1 WHERE x1.a=555 LIMIT 2) FROM t1
|
||||
} {555}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
** Run this script using "sqlite3" to confirm that the command-line
|
||||
** shell properly handles the output of double-width characters.
|
||||
**
|
||||
** https://sqlite.org/forum/forumpost/008ac80276
|
||||
*/
|
||||
.mode box
|
||||
CREATE TABLE data(word TEXT, description TEXT);
|
||||
INSERT INTO data VALUES('〈οὐκέτι〉','Greek without dblwidth <...>');
|
||||
.print .mode box
|
||||
SELECT * FROM data;
|
||||
.mode table
|
||||
.print .mode table
|
||||
SELECT * FROM data;
|
||||
.mode qbox
|
||||
.print .mode qbox
|
||||
SELECT * FROM data;
|
||||
.mode column
|
||||
.print .mode column
|
||||
SELECT * FROM data;
|
||||
+1
-2
@@ -338,8 +338,7 @@ det 3.3.3 {
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN t1
|
||||
`--CORRELATED SCALAR SUBQUERY xxxxxx
|
||||
`--SCAN t2
|
||||
`--SCAN t2 EXISTS
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,426 @@
|
||||
# 2024 May 25
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/lock_common.tcl
|
||||
set testprefix existsexpr
|
||||
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE x1(a, b, PRIMARY KEY(a)) WITHOUT ROWID;
|
||||
INSERT INTO x1 VALUES(1, 2), (3, 4), (5, 6);
|
||||
CREATE INDEX x1b ON x1(b);
|
||||
|
||||
CREATE TABLE x2(x, y);
|
||||
INSERT INTO x2 VALUES(1, 2), (3, 4), (5, 6);
|
||||
}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
SELECT 1 WHERE EXISTS (SELECT 1 FROM x1 WHERE a=5)
|
||||
} {1}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
SELECT * FROM x2 WHERE EXISTS (SELECT 1 FROM x1 WHERE a=x)
|
||||
} {1 2 3 4 5 6}
|
||||
|
||||
# With "a=x", the UNIQUE index means the EXIST can be transformed to a join.
|
||||
# So no "SUBQUERY". With "b=x", the index is not UNIQUE and so there is a
|
||||
# "SUBQUERY".
|
||||
do_execsql_test 1.3.1 {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM x2 WHERE EXISTS (SELECT 1 FROM x1 WHERE a=x)
|
||||
} {~/SUBQUERY/}
|
||||
do_execsql_test 1.3.2 {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM x2 WHERE EXISTS (SELECT 1 FROM x1 WHERE b=x)
|
||||
} {~/SUBQUERY/}
|
||||
|
||||
do_execsql_test 1.4.1 {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM x2 WHERE x=1 AND EXISTS (SELECT 1 FROM x1 WHERE a=x)
|
||||
} {~/SUBQUERY/}
|
||||
do_execsql_test 1.4.2 {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM x2 WHERE EXISTS (SELECT 1 FROM x1 WHERE a=x) AND y=2
|
||||
} {~/SUBQUERY/}
|
||||
|
||||
do_execsql_test 1.5 {
|
||||
SELECT count(*) FROM x2 WHERE EXISTS (SELECT 1 FROM x1 WHERE a=x)
|
||||
} {3}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
|
||||
) INSERT INTO t1 SELECT i, i FROM s;
|
||||
|
||||
CREATE TABLE t2(c, d);
|
||||
WITH s(i) AS (
|
||||
SELECT 10 UNION ALL SELECT i+10 FROM s WHERE i<1000
|
||||
) INSERT INTO t2 SELECT i, i FROM s;
|
||||
}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
SELECT count(*) FROM t1;
|
||||
SELECT count(*) FROM t2;
|
||||
} {1000 100}
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
SELECT count(*) FROM t1, t2 WHERE a=c;
|
||||
} {100}
|
||||
|
||||
do_execsql_test 2.3 {
|
||||
SELECT count(*) FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c=a)
|
||||
} {100}
|
||||
do_eqp_test 2.4 {
|
||||
SELECT count(*) FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c=a)
|
||||
} {SCAN t1}
|
||||
|
||||
do_execsql_test 2.4.0 {
|
||||
CREATE UNIQUE INDEX t2c ON t2(c);
|
||||
CREATE UNIQUE INDEX t1a ON t1(a);
|
||||
}
|
||||
|
||||
do_eqp_test 2.4.1 {
|
||||
SELECT count(*) FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c=a);
|
||||
} {SCAN t1*t2 EXISTS}
|
||||
do_execsql_test 2.4.2 {
|
||||
ANALYZE;
|
||||
}
|
||||
do_eqp_test 2.4.3 {
|
||||
SELECT count(*) FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c=a);
|
||||
} {SCAN t1*t2 EXISTS}
|
||||
do_execsql_test 2.4.4 {
|
||||
SELECT count(*) FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c=a);
|
||||
} {100}
|
||||
|
||||
do_execsql_test 2.5.1 {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT count(*) FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.rowid=a);
|
||||
} {~/SUBQUERY/}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
proc do_subquery_test {tn bSub sql res} {
|
||||
set r1(0) ~/SUBQUERY/
|
||||
set r1(1) /SUBQUERY/
|
||||
do_execsql_test $tn.1 "explain query plan $sql" $r1($bSub)
|
||||
do_execsql_test $tn.2 $sql $res
|
||||
}
|
||||
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE y1(a, b, c);
|
||||
CREATE TABLE y2(x, y, z);
|
||||
CREATE UNIQUE INDEX y2zy ON y2(z, y);
|
||||
|
||||
INSERT INTO y1 VALUES(1, 1, 1);
|
||||
INSERT INTO y1 VALUES(2, 2, 2);
|
||||
INSERT INTO y1 VALUES(3, 3, 3);
|
||||
INSERT INTO y1 VALUES(4, 4, 4);
|
||||
|
||||
INSERT INTO y2 VALUES(1, 1, 1);
|
||||
INSERT INTO y2 VALUES(3, 3, 3);
|
||||
}
|
||||
|
||||
do_subquery_test 3.1 0 {
|
||||
SELECT * FROM y1 WHERE EXISTS (
|
||||
SELECT 1 FROM y2 WHERE z=a AND y=b AND x=z
|
||||
)
|
||||
} {
|
||||
1 1 1 3 3 3
|
||||
}
|
||||
|
||||
do_subquery_test 3.2 0 {
|
||||
SELECT * FROM y1 WHERE EXISTS (
|
||||
SELECT 1 FROM y2 WHERE z=max(a,b) AND y=min(b,a) AND x=z
|
||||
)
|
||||
} {
|
||||
1 1 1 3 3 3
|
||||
}
|
||||
|
||||
do_subquery_test 3.3 0 {
|
||||
SELECT * FROM y1 WHERE EXISTS (
|
||||
SELECT 1 FROM y2 WHERE z=max(a,b) AND y=min(b,a) AND c!=3
|
||||
)
|
||||
} {
|
||||
1 1 1
|
||||
}
|
||||
|
||||
do_subquery_test 3.4 0 {
|
||||
SELECT * FROM y1 WHERE EXISTS (
|
||||
SELECT 1 FROM y2 WHERE z=max(a,b) AND b=3
|
||||
)
|
||||
} {
|
||||
3 3 3
|
||||
}
|
||||
|
||||
do_subquery_test 3.5 0 {
|
||||
SELECT * FROM y1 WHERE EXISTS (
|
||||
SELECT 1 FROM y2 WHERE z=a-1 AND y=a-1
|
||||
)
|
||||
} {
|
||||
2 2 2
|
||||
4 4 4
|
||||
}
|
||||
|
||||
do_subquery_test 3.6 0 {
|
||||
SELECT * FROM y1 WHERE EXISTS (
|
||||
SELECT 1 FROM y2 WHERE z=a-1 AND y+1=a
|
||||
)
|
||||
} {
|
||||
2 2 2
|
||||
4 4 4
|
||||
}
|
||||
|
||||
do_subquery_test 3.7 1 {
|
||||
SELECT * FROM y1 WHERE EXISTS (
|
||||
SELECT count(*) FROM y2 WHERE z=a-1 AND y=a-1
|
||||
)
|
||||
} {
|
||||
1 1 1
|
||||
2 2 2
|
||||
3 3 3
|
||||
4 4 4
|
||||
}
|
||||
|
||||
do_subquery_test 3.8 0 {
|
||||
SELECT * FROM y1 WHERE EXISTS ( SELECT a+1 FROM y2 )
|
||||
} {
|
||||
1 1 1
|
||||
2 2 2
|
||||
3 3 3
|
||||
4 4 4
|
||||
}
|
||||
|
||||
do_subquery_test 3.9 1 {
|
||||
SELECT * FROM y1 WHERE EXISTS (
|
||||
SELECT 1 FROM y2 one, y2 two WHERE one.z=a-1 AND one.y=a-1
|
||||
)
|
||||
} {
|
||||
2 2 2
|
||||
4 4 4
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE TABLE tx1(a TEXT COLLATE nocase, b TEXT);
|
||||
CREATE UNIQUE INDEX tx1ab ON tx1(a, b);
|
||||
|
||||
INSERT INTO tx1 VALUES('a', 'a');
|
||||
INSERT INTO tx1 VALUES('B', 'b');
|
||||
INSERT INTO tx1 VALUES('c', 'c');
|
||||
INSERT INTO tx1 VALUES('D', 'd');
|
||||
INSERT INTO tx1 VALUES('e', 'e');
|
||||
|
||||
CREATE TABLE tx2(x, y);
|
||||
INSERT INTO tx2 VALUES('A', 'a');
|
||||
INSERT INTO tx2 VALUES('b', 'b');
|
||||
INSERT INTO tx2 VALUES('C', 'c');
|
||||
INSERT INTO tx2 VALUES('D', 'd');
|
||||
}
|
||||
|
||||
do_subquery_test 4.1 0 {
|
||||
SELECT * FROM tx2 WHERE EXISTS (
|
||||
SELECT 1 FROM tx1 WHERE a=x AND b=y
|
||||
)
|
||||
} {
|
||||
A a
|
||||
b b
|
||||
C c
|
||||
D d
|
||||
}
|
||||
|
||||
do_subquery_test 4.1.1 0 {
|
||||
SELECT * FROM tx2 WHERE EXISTS (
|
||||
SELECT 1 FROM tx1 WHERE (a COLLATE nocase)=x AND b=y
|
||||
)
|
||||
} {
|
||||
A a b b C c D d
|
||||
}
|
||||
do_subquery_test 4.1.2 0 {
|
||||
SELECT * FROM tx2 WHERE EXISTS (
|
||||
SELECT 1 FROM tx1 WHERE a=x AND (b COLLATE binary)=y
|
||||
)
|
||||
} {
|
||||
A a b b C c D d
|
||||
}
|
||||
do_subquery_test 4.1.1 0 {
|
||||
SELECT * FROM tx2 WHERE EXISTS (
|
||||
SELECT 1 FROM tx1 WHERE x=(a COLLATE nocase) AND b=y
|
||||
)
|
||||
} {
|
||||
A a b b C c D d
|
||||
}
|
||||
do_subquery_test 4.1.2 0 {
|
||||
SELECT * FROM tx2 WHERE EXISTS (
|
||||
SELECT 1 FROM tx1 WHERE a=x AND y=(b COLLATE binary)
|
||||
)
|
||||
} {
|
||||
A a b b C c D d
|
||||
}
|
||||
|
||||
do_subquery_test 4.2 0 {
|
||||
SELECT * FROM tx2 WHERE EXISTS (
|
||||
SELECT 1 FROM tx1 WHERE a=x AND b=y COLLATE nocase
|
||||
)
|
||||
} {
|
||||
A a
|
||||
b b
|
||||
C c
|
||||
D d
|
||||
}
|
||||
|
||||
do_execsql_test 4.3 {
|
||||
DROP INDEX tx1ab;
|
||||
CREATE UNIQUE INDEX tx1ab ON tx1(a COLLATE binary, b);
|
||||
}
|
||||
|
||||
do_subquery_test 4.4 0 {
|
||||
SELECT * FROM tx2 WHERE EXISTS (
|
||||
SELECT 1 FROM tx1 WHERE a=x AND b=y
|
||||
)
|
||||
} {
|
||||
A a
|
||||
b b
|
||||
C c
|
||||
D d
|
||||
}
|
||||
|
||||
do_subquery_test 4.4 0 {
|
||||
SELECT * FROM tx2 WHERE EXISTS (
|
||||
SELECT 1 FROM tx1 WHERE a=x COLLATE binary AND b=y
|
||||
)
|
||||
} {
|
||||
D d
|
||||
}
|
||||
|
||||
do_subquery_test 4.4 1 {
|
||||
SELECT EXISTS ( SELECT x FROM tx1 ) FROM tx2
|
||||
} {
|
||||
1 1 1 1
|
||||
}
|
||||
|
||||
do_subquery_test 4.4 1 {
|
||||
SELECT (SELECT EXISTS ( SELECT x FROM tx1 ) WHERE 1) FROM tx2
|
||||
} {
|
||||
1 1 1 1
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
proc cols {s f} {
|
||||
set lCols [list]
|
||||
for {set i $s} {$i<=$f} {incr i} {
|
||||
lappend lCols [format "c%02d" $i]
|
||||
}
|
||||
join $lCols ", "
|
||||
}
|
||||
proc vals {n val} {
|
||||
set lVal [list]
|
||||
for {set i 0} {$i<$n} {incr i} {
|
||||
lappend lVal $val
|
||||
}
|
||||
join $lVal ", "
|
||||
}
|
||||
proc exprs {s f} {
|
||||
set lExpr [list]
|
||||
for {set i $s} {$i<=$f} {incr i} {
|
||||
lappend lExpr [format "c%02d = o" $i]
|
||||
}
|
||||
join $lExpr " AND "
|
||||
}
|
||||
|
||||
|
||||
do_execsql_test 5.0 "
|
||||
CREATE TABLE a1( [cols 0 99] );
|
||||
"
|
||||
do_execsql_test 5.1 "
|
||||
-- 63 column index
|
||||
CREATE UNIQUE INDEX a1idx1 ON a1( [cols 0 62] );
|
||||
"
|
||||
do_execsql_test 5.2 "
|
||||
-- 64 column index
|
||||
CREATE UNIQUE INDEX a1idx2 ON a1( [cols 10 73] );
|
||||
"
|
||||
do_execsql_test 5.2 "
|
||||
-- 65 column index
|
||||
CREATE UNIQUE INDEX a1idx3 ON a1( [cols 20 84] );
|
||||
"
|
||||
|
||||
do_test 5.3 {
|
||||
foreach v {1 2 3 4 5 6} {
|
||||
execsql "INSERT INTO a1 VALUES( [vals 100 $v] )"
|
||||
}
|
||||
} {}
|
||||
|
||||
do_execsql_test 5.4 {
|
||||
CREATE TABLE a2(o);
|
||||
INSERT INTO a2 VALUES(2), (5);
|
||||
}
|
||||
|
||||
do_subquery_test 5.5 0 "
|
||||
SELECT o FROM a2 WHERE EXISTS (
|
||||
SELECT 1 FROM a1 WHERE [exprs 0 62]
|
||||
)
|
||||
" {
|
||||
2 5
|
||||
}
|
||||
|
||||
do_subquery_test 5.6 0 "
|
||||
SELECT o FROM a2 WHERE EXISTS (
|
||||
SELECT 1 FROM a1 WHERE [exprs 10 73]
|
||||
)
|
||||
" {
|
||||
2 5
|
||||
}
|
||||
|
||||
do_subquery_test 5.7 0 "
|
||||
SELECT o FROM a2 WHERE EXISTS (
|
||||
SELECT 1 FROM a1 WHERE [exprs 20 84]
|
||||
)
|
||||
" {
|
||||
2 5
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 6.0 {
|
||||
CREATE TABLE t1(a, b UNIQUE, c UNIQUE);
|
||||
CREATE TABLE t2(a INfEGER PRIMARY KEY, b);
|
||||
CREATE UNIQUE INDEX t2b ON t2(b);
|
||||
}
|
||||
|
||||
do_catchsql_test 6.1 {
|
||||
SELECT a FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c COLLATE f = a)
|
||||
} {1 {no such collation sequence: f}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 7.0 {
|
||||
CREATE TABLE t1(x);
|
||||
CREATE TABLE t2(y UNIQUE);
|
||||
|
||||
INSERT INTO t1 VALUES(1), (2);
|
||||
INSERT INTO t2 VALUES(1), (3);
|
||||
|
||||
SELECT * FROM t1 one LEFT JOIN t1 two ON (one.x=two.x AND EXISTS (
|
||||
SELECT 1 FROM t2 WHERE y=one.x
|
||||
));
|
||||
} {
|
||||
1 1
|
||||
2 {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,96 @@
|
||||
# 2024 June 14
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/lock_common.tcl
|
||||
set testprefix existsexpr2
|
||||
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE x1(a, b, PRIMARY KEY(a)) WITHOUT ROWID;
|
||||
INSERT INTO x1 VALUES(1, 2), (3, 4), (5, 6);
|
||||
CREATE INDEX x1b ON x1(b);
|
||||
|
||||
CREATE TABLE x2(x, y);
|
||||
INSERT INTO x2 VALUES(1, 2), (3, 4), (5, 6);
|
||||
}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
SELECT * FROM x1 WHERE EXISTS (SELECT 1 FROM x2 WHERE a!=123)
|
||||
} {1 2 3 4 5 6}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
CREATE TABLE x3(u, v);
|
||||
CREATE INDEX x3u ON x3(u);
|
||||
INSERT INTO x3 VALUES
|
||||
(1, 1), (1, 2), (1, 3),
|
||||
(2, 1), (2, 2), (2, 3);
|
||||
}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
SELECT * FROM x1 WHERE EXISTS (
|
||||
SELECT 1 FROM x3 WHERE u IN (1, 2, 3, 4) AND v=b
|
||||
);
|
||||
} {
|
||||
1 2
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE INDEX t1ab ON t1(a,b);
|
||||
|
||||
INSERT INTO t1 VALUES
|
||||
('abc', 1, 1),
|
||||
('abc', 2, 2),
|
||||
('abc', 2, 3),
|
||||
|
||||
('def', 1, 1),
|
||||
('def', 2, 2),
|
||||
('def', 2, 3);
|
||||
|
||||
CREATE TABLE t2(x, y);
|
||||
INSERT INTO t2 VALUES(1, 1), (2, 2), (3, 3);
|
||||
|
||||
ANALYZE;
|
||||
DELETE FROM sqlite_stat1;
|
||||
INSERT INTO sqlite_stat1 VALUES('t1','t1ab','10000 5000 2');
|
||||
ANALYZE sqlite_master;
|
||||
}
|
||||
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
SELECT a,b,c FROM t1 WHERE b=2 ORDER BY a
|
||||
} {
|
||||
abc 2 2
|
||||
abc 2 3
|
||||
def 2 2
|
||||
def 2 3
|
||||
}
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
SELECT x, y FROM t2 WHERE EXISTS (
|
||||
SELECT 1 FROM t1 WHERE b=x
|
||||
)
|
||||
} {
|
||||
1 1
|
||||
2 2
|
||||
}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# 2024 May 25
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/lock_common.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
set testprefix existsfault
|
||||
|
||||
db close
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_lookaside 0 0
|
||||
sqlite3_initialize
|
||||
autoinstall_test_functions
|
||||
sqlite3 db test.db
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE x1(a, b);
|
||||
INSERT INTO x1 VALUES(1, 2), (3, 4), (5, 6);
|
||||
CREATE UNIQUE INDEX x1a ON x1(a);
|
||||
CREATE INDEX x1b ON x1(b);
|
||||
|
||||
CREATE TABLE x2(x, y);
|
||||
INSERT INTO x2 VALUES(1, 2), (3, 4), (5, 6);
|
||||
}
|
||||
|
||||
do_faultsim_test 1 -faults oom* -prep {
|
||||
sqlite3 db test.db
|
||||
execsql { SELECT * FROM sqlite_schema }
|
||||
} -body {
|
||||
execsql {
|
||||
SELECT count(*) FROM x2 WHERE EXISTS (SELECT 1 FROM x1 WHERE a=x) AND y!=11
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 3}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -488,11 +488,21 @@ proc preupdate_hook {args} {
|
||||
set type [lindex $args 0]
|
||||
eval lappend ::preupdate $args
|
||||
if {$type != "INSERT"} {
|
||||
set x [catch {db preupdate old [db preupdate count]}]
|
||||
if {!$x} {
|
||||
lappend "ERROR: sqlite3_preupdate_old() accepted an out-of-bounds\
|
||||
column index"
|
||||
}
|
||||
for {set i 0} {$i < [db preupdate count]} {incr i} {
|
||||
lappend ::preupdate [db preupdate old $i]
|
||||
}
|
||||
}
|
||||
if {$type != "DELETE"} {
|
||||
set x [catch {db preupdate new [db preupdate count]}]
|
||||
if {!$x} {
|
||||
lappend "ERROR: sqlite3_preupdate_old() accepted an out-of-bounds\
|
||||
column index"
|
||||
}
|
||||
for {set i 0} {$i < [db preupdate count]} {incr i} {
|
||||
set rc [catch { db preupdate new $i } v]
|
||||
lappend ::preupdate $v
|
||||
|
||||
@@ -106,4 +106,95 @@ do_test 4.4 {
|
||||
} {SQLITE_LOCKED}
|
||||
close $blob
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
reset_db
|
||||
do_execsql_test 5.1 {
|
||||
CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
|
||||
INSERT INTO t2 VALUES(1000, 'abcdefghijklmnopqrstuvwxyz');
|
||||
INSERT INTO t2 VALUES(2000, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
|
||||
INSERT INTO t2 VALUES(3000, 'abcdefghijklmnopqrstuvwxyz');
|
||||
}
|
||||
|
||||
do_test 5.2.1 {
|
||||
execsql BEGIN
|
||||
set blob [db incrblob t2 b 2000]
|
||||
seek $blob 0
|
||||
puts -nonewline $blob "hello "
|
||||
flush $blob
|
||||
execsql ROLLBACK
|
||||
} {}
|
||||
|
||||
do_test 5.2.2 {
|
||||
puts -nonewline $blob "world"
|
||||
list [catch { flush $blob } msg] $msg
|
||||
} "1 {error flushing \"$blob\": I/O error}"
|
||||
catch { close $blob }
|
||||
|
||||
set preupdate_count 0
|
||||
proc preupdate {args} { incr ::preupdate_count ; return {} }
|
||||
db preupdate hook preupdate
|
||||
|
||||
set preupdate_count 0
|
||||
do_test 5.3.1 {
|
||||
execsql BEGIN
|
||||
set blob [db incrblob t2 b 1000]
|
||||
seek $blob 0
|
||||
puts -nonewline $blob "hello "
|
||||
flush $blob
|
||||
execsql ROLLBACK
|
||||
} {}
|
||||
|
||||
do_test 5.3.2 {
|
||||
puts -nonewline $blob "world"
|
||||
list [catch { flush $blob } msg] $msg
|
||||
} "1 {error flushing \"$blob\": I/O error}"
|
||||
catch { close $blob }
|
||||
|
||||
do_test 5.3.3 {
|
||||
set ::preupdate_count
|
||||
} {1}
|
||||
|
||||
set preupdate_count 0
|
||||
do_test 5.4.1 {
|
||||
execsql BEGIN
|
||||
set blob [db incrblob t2 b 1000]
|
||||
seek $blob 0
|
||||
puts -nonewline $blob "hello "
|
||||
flush $blob
|
||||
execsql { DELETE FROM t2 WHERE a=3000; }
|
||||
} {}
|
||||
|
||||
do_test 5.4.2 {
|
||||
puts -nonewline $blob "world"
|
||||
list [catch { flush $blob } msg] $msg
|
||||
} "0 {}"
|
||||
catch { close $blob }
|
||||
catchsql { ROLLBACK }
|
||||
|
||||
do_test 5.3.3 {
|
||||
set ::preupdate_count
|
||||
} {3}
|
||||
|
||||
set preupdate_count 0
|
||||
do_test 5.4.3 {
|
||||
execsql BEGIN
|
||||
set blob [db incrblob t2 b 2000]
|
||||
seek $blob 0
|
||||
puts -nonewline $blob "hello "
|
||||
flush $blob
|
||||
execsql { UPDATE t2 SET b='abcdefghijklmnopqrstuvwxyz' WHERE a=2000 }
|
||||
} {}
|
||||
|
||||
do_test 5.4.4 {
|
||||
puts -nonewline $blob "world"
|
||||
list [catch { flush $blob } msg] $msg
|
||||
} "1 {error flushing \"$blob\": I/O error}"
|
||||
catch { close $blob }
|
||||
catchsql { ROLLBACK }
|
||||
|
||||
do_test 5.3.3 {
|
||||
set ::preupdate_count
|
||||
} {2}
|
||||
|
||||
finish_test
|
||||
|
||||
+4
-4
@@ -892,15 +892,15 @@ do_execsql_test json101-13.100 {
|
||||
INSERT INTO t2(id,json) VALUES(4,'{"value":4}');
|
||||
INSERT INTO t2(id,json) VALUES(5,'{"value":5}');
|
||||
INSERT INTO t2(id,json) VALUES(6,'{"value":6}');
|
||||
SELECT * FROM t1 CROSS JOIN t2
|
||||
SELECT *, 'NL' FROM t1 CROSS JOIN t2
|
||||
WHERE EXISTS(SELECT 1 FROM json_each(t1.json,'$.items') AS Z
|
||||
WHERE Z.value==t2.id);
|
||||
} {1 {{"items":[3,5]}} 3 {{"value":3}} 1 {{"items":[3,5]}} 5 {{"value":5}}}
|
||||
} {1 {{"items":[3,5]}} 3 {{"value":3}} NL 1 {{"items":[3,5]}} 5 {{"value":5}} NL}
|
||||
do_execsql_test json101-13.110 {
|
||||
SELECT * FROM t2 CROSS JOIN t1
|
||||
SELECT *, 'NL' FROM t2 CROSS JOIN t1
|
||||
WHERE EXISTS(SELECT 1 FROM json_each(t1.json,'$.items') AS Z
|
||||
WHERE Z.value==t2.id);
|
||||
} {3 {{"value":3}} 1 {{"items":[3,5]}} 5 {{"value":5}} 1 {{"items":[3,5]}}}
|
||||
} {3 {{"value":3}} 1 {{"items":[3,5]}} NL 5 {{"value":5}} 1 {{"items":[3,5]}} NL}
|
||||
|
||||
# 2018-05-16
|
||||
# Incorrect fullkey output from json_each()
|
||||
|
||||
+1
-1
@@ -66,7 +66,7 @@ do_vmstep_test 1.5.2 {
|
||||
SELECT count(*) FROM t2 WHERE EXISTS(
|
||||
SELECT 1 FROM t1 WHERE t1.a=450 AND t2.c IS NULL
|
||||
)
|
||||
} +8000 {0}
|
||||
} 4000 {0}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
@@ -162,4 +162,101 @@ do_execsql_test strict1-8.2 {
|
||||
SELECT *, '|' FROM t1;
|
||||
} {/5.0 5.0 4.6116\d*e\+18 4.6116\d+e\+18 |/}
|
||||
|
||||
# 2025-06-18 https://sqlite.org/forum/forumpost/6caf195248a849e4
|
||||
#
|
||||
# Enforce STRICT table type constraints on STORED generated columns
|
||||
#
|
||||
do_execsql_test strict1-9.1 {
|
||||
CREATE TABLE strict (
|
||||
k INTEGER PRIMARY KEY,
|
||||
c1 REAL AS(if(k=11,1.5, k=12,2, k=13,'x', k=14,x'34', 0.0)) STORED,
|
||||
c2 INT AS(if(k=21,1.5, k=22,2, k=23,'x', k=24,x'34', 0)) STORED,
|
||||
c3 TEXT AS(if(k=31,1.5, k=32,2, k=33,'x', k=34,x'34', 'x')) STORED,
|
||||
c4 BLOB AS(if(k=41,1.5, k=42,2, k=43,'x', k=44,x'34', x'00')) STORED,
|
||||
c5 ANY AS(if(k=51,1.5, k=52,2, k=53,'x', k=54,x'34', 0)) STORED
|
||||
) STRICT;
|
||||
INSERT INTO strict(k) VALUES(11);
|
||||
INSERT INTO strict(k) VALUES(12);
|
||||
INSERT INTO strict(k) VALUES(22);
|
||||
INSERT INTO strict(k) VALUES(31);
|
||||
INSERT INTO strict(k) VALUES(32);
|
||||
INSERT INTO strict(k) VALUES(33);
|
||||
INSERT INTO strict(k) VALUES(44);
|
||||
PRAGMA integrity_check;
|
||||
} {ok}
|
||||
do_catchsql_test strict1-9.2.13 {
|
||||
INSERT INTO strict(k) VALUES(13);
|
||||
} {1 {cannot store TEXT value in REAL column strict.c1}}
|
||||
do_catchsql_test strict1-9.2.14 {
|
||||
INSERT INTO strict(k) VALUES(14);
|
||||
} {1 {cannot store BLOB value in REAL column strict.c1}}
|
||||
do_catchsql_test strict1-9.2.21 {
|
||||
INSERT INTO strict(k) VALUES(21);
|
||||
} {1 {cannot store REAL value in INT column strict.c2}}
|
||||
do_catchsql_test strict1-9.2.23 {
|
||||
INSERT INTO strict(k) VALUES(23);
|
||||
} {1 {cannot store TEXT value in INT column strict.c2}}
|
||||
do_catchsql_test strict1-9.2.24 {
|
||||
INSERT INTO strict(k) VALUES(24);
|
||||
} {1 {cannot store BLOB value in INT column strict.c2}}
|
||||
do_catchsql_test strict1-9.2.34 {
|
||||
INSERT INTO strict(k) VALUES(34);
|
||||
} {1 {cannot store BLOB value in TEXT column strict.c3}}
|
||||
do_catchsql_test strict1-9.2.41 {
|
||||
INSERT INTO strict(k) VALUES(41);
|
||||
} {1 {cannot store REAL value in BLOB column strict.c4}}
|
||||
do_catchsql_test strict1-9.2.42 {
|
||||
INSERT INTO strict(k) VALUES(42);
|
||||
} {1 {cannot store INT value in BLOB column strict.c4}}
|
||||
do_catchsql_test strict1-9.2.43 {
|
||||
INSERT INTO strict(k) VALUES(43);
|
||||
} {1 {cannot store TEXT value in BLOB column strict.c4}}
|
||||
|
||||
do_execsql_test strict1-9.3 {
|
||||
DROP TABLE strict;
|
||||
CREATE TABLE strict (
|
||||
k INTEGER PRIMARY KEY,
|
||||
c1 REAL AS(if(k=11,1.5, k=12,2, k=13,'x', k=14,x'34', 0.0)) VIRTUAL,
|
||||
c2 INT AS(if(k=21,1.5, k=22,2, k=23,'x', k=24,x'34', 0)) VIRTUAL,
|
||||
c3 TEXT AS(if(k=31,1.5, k=32,2, k=33,'x', k=34,x'34', 'x')) VIRTUAL,
|
||||
c4 BLOB AS(if(k=41,1.5, k=42,2, k=43,'x', k=44,x'34', x'00')) VIRTUAL,
|
||||
c5 ANY AS(if(k=51,1.5, k=52,2, k=53,'x', k=54,x'34', 0)) VIRTUAL
|
||||
) STRICT;
|
||||
INSERT INTO strict(k) VALUES(11);
|
||||
INSERT INTO strict(k) VALUES(12);
|
||||
INSERT INTO strict(k) VALUES(22);
|
||||
INSERT INTO strict(k) VALUES(31);
|
||||
INSERT INTO strict(k) VALUES(32);
|
||||
INSERT INTO strict(k) VALUES(33);
|
||||
INSERT INTO strict(k) VALUES(44);
|
||||
PRAGMA integrity_check;
|
||||
} {ok}
|
||||
do_catchsql_test strict1-9.4.13 {
|
||||
INSERT INTO strict(k) VALUES(13);
|
||||
} {1 {cannot store TEXT value in REAL column strict.c1}}
|
||||
do_catchsql_test strict1-9.4.14 {
|
||||
INSERT INTO strict(k) VALUES(14);
|
||||
} {1 {cannot store BLOB value in REAL column strict.c1}}
|
||||
do_catchsql_test strict1-9.4.21 {
|
||||
INSERT INTO strict(k) VALUES(21);
|
||||
} {1 {cannot store REAL value in INT column strict.c2}}
|
||||
do_catchsql_test strict1-9.4.23 {
|
||||
INSERT INTO strict(k) VALUES(23);
|
||||
} {1 {cannot store TEXT value in INT column strict.c2}}
|
||||
do_catchsql_test strict1-9.4.24 {
|
||||
INSERT INTO strict(k) VALUES(24);
|
||||
} {1 {cannot store BLOB value in INT column strict.c2}}
|
||||
do_catchsql_test strict1-9.4.34 {
|
||||
INSERT INTO strict(k) VALUES(34);
|
||||
} {1 {cannot store BLOB value in TEXT column strict.c3}}
|
||||
do_catchsql_test strict1-9.4.41 {
|
||||
INSERT INTO strict(k) VALUES(41);
|
||||
} {1 {cannot store REAL value in BLOB column strict.c4}}
|
||||
do_catchsql_test strict1-9.4.42 {
|
||||
INSERT INTO strict(k) VALUES(42);
|
||||
} {1 {cannot store INT value in BLOB column strict.c4}}
|
||||
do_catchsql_test strict1-9.4.43 {
|
||||
INSERT INTO strict(k) VALUES(43);
|
||||
} {1 {cannot store TEXT value in BLOB column strict.c4}}
|
||||
|
||||
finish_test
|
||||
|
||||
+108
-45
@@ -9,7 +9,7 @@
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for TCL interface to the
|
||||
# SQLite library.
|
||||
# SQLite library.
|
||||
#
|
||||
# Actually, all tests are based on the TCL interface, so the main
|
||||
# interface is pretty well tested. This file contains some addition
|
||||
@@ -121,7 +121,7 @@ ifcapable {complete} {
|
||||
do_test tcl-1.14 {
|
||||
set v [catch {db eval} msg]
|
||||
lappend v $msg
|
||||
} {1 {wrong # args: should be "db eval ?OPTIONS? SQL ?ARRAY-NAME? ?SCRIPT?"}}
|
||||
} {1 {wrong # args: should be "db eval ?OPTIONS? SQL ?VAR-NAME? ?SCRIPT?"}}
|
||||
do_test tcl-1.15 {
|
||||
set v [catch {db function} msg]
|
||||
lappend v $msg
|
||||
@@ -359,6 +359,19 @@ do_test tcl-9.3 {
|
||||
execsql {SELECT typeof(ret_int())}
|
||||
} {integer}
|
||||
|
||||
proc breakAsNullUdf args {
|
||||
if {"1" eq [lindex $args 0]} {return -code break}
|
||||
}
|
||||
do_test tcl-9.4 {
|
||||
db function banu breakAsNullUdf
|
||||
execsql {SELECT typeof(banu()), typeof(banu(1))}
|
||||
} {text null}
|
||||
do_test tcl-9.5 {
|
||||
db nullvalue banunull
|
||||
db eval {SELECT banu(), banu(1)}
|
||||
} {{} banunull}
|
||||
|
||||
|
||||
# Recursive calls to the same user-defined function
|
||||
#
|
||||
ifcapable tclvar {
|
||||
@@ -465,7 +478,7 @@ do_test tcl-10.13 {
|
||||
db eval {SELECT * FROM t4}
|
||||
} {1 2 5 6 7}
|
||||
|
||||
# Now test that [db transaction] commands may be nested with
|
||||
# Now test that [db transaction] commands may be nested with
|
||||
# the expected results.
|
||||
#
|
||||
do_test tcl-10.14 {
|
||||
@@ -475,7 +488,7 @@ do_test tcl-10.14 {
|
||||
INSERT INTO t4 VALUES('one');
|
||||
}
|
||||
|
||||
catch {
|
||||
catch {
|
||||
db transaction {
|
||||
db eval { INSERT INTO t4 VALUES('two') }
|
||||
db transaction {
|
||||
@@ -674,11 +687,11 @@ do_test tcl-15.5 {
|
||||
} {0}
|
||||
|
||||
|
||||
# 2017-06-26: The --withoutnulls flag to "db eval".
|
||||
# 2017-06-26: The -withoutnulls flag to "db eval".
|
||||
#
|
||||
# In the "db eval --withoutnulls SQL ARRAY" form, NULL results cause the
|
||||
# corresponding array entry to be unset. The default behavior (without
|
||||
# the -withoutnulls flags) is for the corresponding array value to get
|
||||
# In the "db eval -withoutnulls SQL TARGET" form, NULL results cause the
|
||||
# corresponding target entry to be unset. The default behavior (without
|
||||
# the -withoutnulls flags) is for the corresponding target value to get
|
||||
# the [db nullvalue] string.
|
||||
#
|
||||
catch {db close}
|
||||
@@ -720,64 +733,64 @@ reset_db
|
||||
proc add {a b} { return [expr $a + $b] }
|
||||
proc ret {a} { return $a }
|
||||
|
||||
db function add_i -returntype integer add
|
||||
db function add_i -returntype integer add
|
||||
db function add_r -ret real add
|
||||
db function add_t -return text add
|
||||
db function add_b -returntype blob add
|
||||
db function add_a -returntype any add
|
||||
db function add_t -return text add
|
||||
db function add_b -returntype blob add
|
||||
db function add_a -returntype any add
|
||||
|
||||
db function ret_i -returntype int ret
|
||||
db function ret_i -returntype int ret
|
||||
db function ret_r -returntype real ret
|
||||
db function ret_t -returntype text ret
|
||||
db function ret_b -returntype blob ret
|
||||
db function ret_a -r any ret
|
||||
db function ret_t -returntype text ret
|
||||
db function ret_b -returntype blob ret
|
||||
db function ret_a -r any ret
|
||||
|
||||
do_execsql_test 17.0 {
|
||||
SELECT quote( add_i(2, 3) );
|
||||
SELECT quote( add_r(2, 3) );
|
||||
SELECT quote( add_t(2, 3) );
|
||||
SELECT quote( add_b(2, 3) );
|
||||
SELECT quote( add_a(2, 3) );
|
||||
SELECT quote( add_r(2, 3) );
|
||||
SELECT quote( add_t(2, 3) );
|
||||
SELECT quote( add_b(2, 3) );
|
||||
SELECT quote( add_a(2, 3) );
|
||||
} {5 5.0 '5' X'35' 5}
|
||||
|
||||
do_execsql_test 17.1 {
|
||||
SELECT quote( add_i(2.2, 3.3) );
|
||||
SELECT quote( add_r(2.2, 3.3) );
|
||||
SELECT quote( add_t(2.2, 3.3) );
|
||||
SELECT quote( add_b(2.2, 3.3) );
|
||||
SELECT quote( add_a(2.2, 3.3) );
|
||||
SELECT quote( add_r(2.2, 3.3) );
|
||||
SELECT quote( add_t(2.2, 3.3) );
|
||||
SELECT quote( add_b(2.2, 3.3) );
|
||||
SELECT quote( add_a(2.2, 3.3) );
|
||||
} {5.5 5.5 '5.5' X'352E35' 5.5}
|
||||
|
||||
do_execsql_test 17.2 {
|
||||
SELECT quote( ret_i(2.5) );
|
||||
SELECT quote( ret_r(2.5) );
|
||||
SELECT quote( ret_t(2.5) );
|
||||
SELECT quote( ret_b(2.5) );
|
||||
SELECT quote( ret_a(2.5) );
|
||||
SELECT quote( ret_r(2.5) );
|
||||
SELECT quote( ret_t(2.5) );
|
||||
SELECT quote( ret_b(2.5) );
|
||||
SELECT quote( ret_a(2.5) );
|
||||
} {2.5 2.5 '2.5' X'322E35' 2.5}
|
||||
|
||||
do_execsql_test 17.3 {
|
||||
SELECT quote( ret_i('2.5') );
|
||||
SELECT quote( ret_r('2.5') );
|
||||
SELECT quote( ret_t('2.5') );
|
||||
SELECT quote( ret_b('2.5') );
|
||||
SELECT quote( ret_a('2.5') );
|
||||
SELECT quote( ret_r('2.5') );
|
||||
SELECT quote( ret_t('2.5') );
|
||||
SELECT quote( ret_b('2.5') );
|
||||
SELECT quote( ret_a('2.5') );
|
||||
} {2.5 2.5 '2.5' X'322E35' '2.5'}
|
||||
|
||||
do_execsql_test 17.4 {
|
||||
SELECT quote( ret_i('abc') );
|
||||
SELECT quote( ret_r('abc') );
|
||||
SELECT quote( ret_t('abc') );
|
||||
SELECT quote( ret_b('abc') );
|
||||
SELECT quote( ret_a('abc') );
|
||||
SELECT quote( ret_r('abc') );
|
||||
SELECT quote( ret_t('abc') );
|
||||
SELECT quote( ret_b('abc') );
|
||||
SELECT quote( ret_a('abc') );
|
||||
} {'abc' 'abc' 'abc' X'616263' 'abc'}
|
||||
|
||||
do_execsql_test 17.5 {
|
||||
SELECT quote( ret_i(X'616263') );
|
||||
SELECT quote( ret_r(X'616263') );
|
||||
SELECT quote( ret_t(X'616263') );
|
||||
SELECT quote( ret_b(X'616263') );
|
||||
SELECT quote( ret_a(X'616263') );
|
||||
SELECT quote( ret_r(X'616263') );
|
||||
SELECT quote( ret_t(X'616263') );
|
||||
SELECT quote( ret_b(X'616263') );
|
||||
SELECT quote( ret_a(X'616263') );
|
||||
} {'abc' 'abc' 'abc' X'616263' X'616263'}
|
||||
|
||||
do_test 17.6.1 {
|
||||
@@ -848,21 +861,70 @@ do_catchsql_test 19.911 {
|
||||
} {1 {invalid command name "bind_fallback_does_not_exist"}}
|
||||
db bind_fallback {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# 2025-05-05: the -asdict eval flag
|
||||
#
|
||||
do_test 20.0 {
|
||||
execsql {CREATE TABLE tad(a,b)}
|
||||
execsql {INSERT INTO tad(a,b) VALUES('aa','bb'),('AA','BB')}
|
||||
db eval -asdict {
|
||||
SELECT a, b FROM tad WHERE 0
|
||||
} D {}
|
||||
set D
|
||||
} {* {a b}}
|
||||
|
||||
do_test 20.1 {
|
||||
unset D
|
||||
set i 0
|
||||
set res {}
|
||||
set colNames {}
|
||||
db eval -asdict {
|
||||
SELECT a, b FROM tad ORDER BY a
|
||||
} D {
|
||||
dict set D i [incr i]
|
||||
lappend res $i [dict get $D a] [dict get $D b]
|
||||
if {1 == $i} {
|
||||
set colNames [dict get $D *]
|
||||
}
|
||||
}
|
||||
lappend res $colNames
|
||||
unset D
|
||||
set res
|
||||
} {1 AA BB 2 aa bb {a b}}
|
||||
|
||||
do_test 20.2 {
|
||||
set res {}
|
||||
db eval -asdict -withoutnulls {
|
||||
SELECT n, a, b FROM (
|
||||
SELECT 1 as n, 'aa' as a, NULL as b
|
||||
UNION ALL
|
||||
SELECT 2 as n, NULL as a, 'bb' as b
|
||||
)
|
||||
ORDER BY n
|
||||
} D {
|
||||
dict unset D *
|
||||
lappend res [dict values $D]
|
||||
}
|
||||
unset D
|
||||
execsql {DROP TABLE tad}
|
||||
set res
|
||||
} {{1 aa} {2 bb}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
do_test 21.0 {
|
||||
db transaction {
|
||||
db close
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test 20.1 {
|
||||
do_test 21.1 {
|
||||
sqlite3 db test.db
|
||||
set rc [catch {
|
||||
db eval {SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3} { db close }
|
||||
} msg]
|
||||
list $rc $msg
|
||||
} {1 {invalid command name "db"}}
|
||||
|
||||
|
||||
|
||||
|
||||
proc closedb {} {
|
||||
db close
|
||||
@@ -874,7 +936,7 @@ sqlite3 db test.db
|
||||
db func closedb closedb
|
||||
db func func1 func1
|
||||
|
||||
do_test 20.2 {
|
||||
do_test 21.2 {
|
||||
set rc [catch {
|
||||
db eval {
|
||||
SELECT closedb(),func1() UNION ALL SELECT 20,30 UNION ALL SELECT 30,40
|
||||
@@ -884,9 +946,10 @@ do_test 20.2 {
|
||||
} {0 {10 1 20 30 30 40}}
|
||||
|
||||
sqlite3 db :memory:
|
||||
do_test 21.1 {
|
||||
do_test 22.1 {
|
||||
catch {db eval {SELECT 1 2 3;}} msg
|
||||
db erroroffset
|
||||
} {9}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -401,4 +401,25 @@ do_test vacuum-10.1 {
|
||||
} {}
|
||||
do_test vacuum-10.2 { execsql VACUUM } {}
|
||||
|
||||
# Verify that VACUUM still works if ATTACH is disabled.
|
||||
#
|
||||
do_execsql_test vacuum-11.1 {
|
||||
PRAGMA page_size=1024;
|
||||
VACUUM;
|
||||
PRAGMA page_size;
|
||||
} {1024}
|
||||
sqlite3_db_config db ATTACH_CREATE 0
|
||||
do_execsql_test vacuum-11.2 {
|
||||
PRAGMA page_size=2048;
|
||||
VACUUM;
|
||||
PRAGMA page_size;
|
||||
} {2048}
|
||||
sqlite3_db_config db ATTACH_CREATE 1
|
||||
sqlite3_db_config db ATTACH_WRITE 0
|
||||
do_execsql_test vacuum-11.3 {
|
||||
PRAGMA page_size=4096;
|
||||
VACUUM;
|
||||
PRAGMA page_size;
|
||||
} {4096}
|
||||
|
||||
finish_test
|
||||
|
||||
+43
-31
@@ -17,6 +17,7 @@ Options:
|
||||
--uninstall Uninstall the extension
|
||||
--version-check Check extension version against this source tree
|
||||
--destdir DIR Installation root (used by "make install DESTDIR=...")
|
||||
--tclConfig.sh FILE Use this tclConfig.sh instead of looking for one
|
||||
|
||||
Other options are retained and passed through into the compiler.}
|
||||
|
||||
@@ -29,6 +30,7 @@ set versioncheck 0
|
||||
set CC {}
|
||||
set OPTS {}
|
||||
set DESTDIR ""; # --destdir "$(DESTDIR)"
|
||||
set tclConfigSh ""; # --tclConfig.sh FILE
|
||||
for {set ii 0} {$ii<[llength $argv]} {incr ii} {
|
||||
set a0 [lindex $argv $ii]
|
||||
if {$a0=="--install-only"} {
|
||||
@@ -56,6 +58,9 @@ for {set ii 0} {$ii<[llength $argv]} {incr ii} {
|
||||
} elseif {$a0=="--destdir" && $ii+1<[llength $argv]} {
|
||||
incr ii
|
||||
set DESTDIR [lindex $argv $ii]
|
||||
} elseif {$a0=="--tclConfig.sh" && $ii+1<[llength $argv]} {
|
||||
incr ii
|
||||
set tclConfigSh [lindex $argv $ii]
|
||||
} elseif {[string match -* $a0]} {
|
||||
append OPTS " $a0"
|
||||
} else {
|
||||
@@ -88,39 +93,46 @@ if {$tcl_platform(platform) eq "windows"} {
|
||||
}
|
||||
set OUT tclsqlite3.dll
|
||||
} else {
|
||||
# Figure out the location of the tclConfig.sh file used by the
|
||||
# tclsh that is executing this script.
|
||||
#
|
||||
if {[catch {
|
||||
set LIBDIR [tcl::pkgconfig get libdir,install]
|
||||
}]} {
|
||||
puts stderr "$argv0: tclsh does not support tcl::pkgconfig."
|
||||
exit 1
|
||||
}
|
||||
if {![file exists $LIBDIR]} {
|
||||
puts stderr "$argv0: cannot find the tclConfig.sh file."
|
||||
puts stderr "$argv0: tclsh reported library directory \"$LIBDIR\"\
|
||||
does not exist."
|
||||
exit 1
|
||||
}
|
||||
if {![file exists $LIBDIR/tclConfig.sh]
|
||||
|| [file size $LIBDIR/tclConfig.sh]<5000} {
|
||||
set n1 $LIBDIR/tcl$::tcl_version
|
||||
if {[file exists $n1/tclConfig.sh]
|
||||
&& [file size $n1/tclConfig.sh]>5000} {
|
||||
set LIBDIR $n1
|
||||
} else {
|
||||
puts stderr "$argv0: cannot find tclConfig.sh in either $LIBDIR or $n1"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# Read the tclConfig.sh file into the $tclConfig variable
|
||||
#
|
||||
#puts "using $LIBDIR/tclConfig.sh"
|
||||
set fd [open $LIBDIR/tclConfig.sh rb]
|
||||
set tclConfig [read $fd]
|
||||
close $fd
|
||||
if {"" eq $tclConfigSh} {
|
||||
# Figure out the location of the tclConfig.sh file used by the
|
||||
# tclsh that is executing this script.
|
||||
#
|
||||
if {[catch {
|
||||
set LIBDIR [tcl::pkgconfig get libdir,install]
|
||||
}]} {
|
||||
puts stderr "$argv0: tclsh does not support tcl::pkgconfig."
|
||||
exit 1
|
||||
}
|
||||
if {![file exists $LIBDIR]} {
|
||||
puts stderr "$argv0: cannot find the tclConfig.sh file."
|
||||
puts stderr "$argv0: tclsh reported library directory \"$LIBDIR\"\
|
||||
does not exist."
|
||||
exit 1
|
||||
}
|
||||
if {![file exists $LIBDIR/tclConfig.sh]
|
||||
|| [file size $LIBDIR/tclConfig.sh]<5000} {
|
||||
set n1 $LIBDIR/tcl$::tcl_version
|
||||
if {[file exists $n1/tclConfig.sh]
|
||||
&& [file size $n1/tclConfig.sh]>5000} {
|
||||
set LIBDIR $n1
|
||||
} else {
|
||||
puts stderr "$argv0: cannot find tclConfig.sh in either $LIBDIR or $n1"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
#puts "using $LIBDIR/tclConfig.sh"
|
||||
set fd [open $LIBDIR/tclConfig.sh rb]
|
||||
set tclConfig [read $fd]
|
||||
close $fd
|
||||
} else {
|
||||
# User-provided tclConfig.sh
|
||||
#
|
||||
set fd [open $tclConfigSh rb]
|
||||
set tclConfig [read $fd]
|
||||
close $fd
|
||||
}
|
||||
|
||||
# Extract parameter we will need from the tclConfig.sh file
|
||||
#
|
||||
|
||||
+3
-3
@@ -2023,10 +2023,10 @@ static char *msort(
|
||||
list = NEXT(list);
|
||||
NEXT(ep) = 0;
|
||||
for(i=0; i<LISTSIZE-1 && set[i]!=0; i++){
|
||||
ep = merge(ep,set[i],cmp,offset);
|
||||
ep = merge(set[i],ep,cmp,offset);
|
||||
set[i] = 0;
|
||||
}
|
||||
set[i] = ep;
|
||||
set[i] = merge(set[i],ep,cmp,offset);
|
||||
}
|
||||
ep = 0;
|
||||
for(i=0; i<LISTSIZE; i++) if( set[i] ) ep = merge(set[i],ep,cmp,offset);
|
||||
@@ -5925,7 +5925,7 @@ struct state **State_arrayof(void)
|
||||
PRIVATE unsigned confighash(struct config *a)
|
||||
{
|
||||
unsigned h=0;
|
||||
h = h*571 + a->rp->index*37 + a->dot;
|
||||
h = a->rp->index*37 + a->dot;
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ while {1} {
|
||||
if {[regexp {^# *include "sqlite} $lx]} {
|
||||
set lx "/* $lx */"
|
||||
}
|
||||
if {[regexp {^# *include "test_windirent.h"} $lx]} {
|
||||
if {[regexp {^# *include "windirent.h"} $lx]} {
|
||||
set lx "/* $lx */"
|
||||
}
|
||||
set lx [string map [list __declspec(dllexport) {}] $lx]
|
||||
|
||||
@@ -1797,6 +1797,7 @@ static void replicaSide(SQLiteRsync *p){
|
||||
closeDb(p);
|
||||
break;
|
||||
}
|
||||
sqlite3_db_config(p->db, SQLITE_DBCONFIG_WRITABLE_SCHEMA, 1, 0);
|
||||
runSql(p, "ATTACH %Q AS 'replica'", p->zReplica);
|
||||
if( p->wrongEncoding ){
|
||||
p->wrongEncoding = 0;
|
||||
|
||||
Reference in New Issue
Block a user