mirror of
https://github.com/status-im/sqlcipher.git
synced 2026-08-30 22:11:14 +00:00
Merge sqlite-release(3.8.4.3) into prerelease-integration
Conflicts: Makefile.in README VERSION configure manifest manifest.uuid sqlcipher.1 src/pragma.c src/shell.c
This commit is contained in:
+14
-2
@@ -301,7 +301,8 @@ SRC = \
|
||||
$(TOP)/src/wal.c \
|
||||
$(TOP)/src/wal.h \
|
||||
$(TOP)/src/walker.c \
|
||||
$(TOP)/src/where.c
|
||||
$(TOP)/src/where.c \
|
||||
$(TOP)/src/whereInt.h
|
||||
|
||||
# Source code for extensions
|
||||
#
|
||||
@@ -416,6 +417,7 @@ TESTSRC += \
|
||||
$(TOP)/ext/misc/percentile.c \
|
||||
$(TOP)/ext/misc/regexp.c \
|
||||
$(TOP)/ext/misc/spellfix.c \
|
||||
$(TOP)/ext/misc/totype.c \
|
||||
$(TOP)/ext/misc/wholenumber.c
|
||||
|
||||
# Source code to the library files needed by the test fixture
|
||||
@@ -484,6 +486,7 @@ HDR = \
|
||||
$(TOP)/src/sqliteLimit.h \
|
||||
$(TOP)/src/vdbe.h \
|
||||
$(TOP)/src/vdbeInt.h \
|
||||
$(TOP)/src/whereInt.h \
|
||||
config.h
|
||||
|
||||
# Header files used by extensions
|
||||
@@ -551,7 +554,7 @@ mptester$(EXE): sqlite3.c $(TOP)/mptest/mptest.c
|
||||
mkdir tsrc
|
||||
cp -f $(SRC) tsrc
|
||||
rm tsrc/sqlite.h.in tsrc/parse.y
|
||||
$(TCLSH_CMD) $(TOP)/tool/vdbe-compress.tcl <tsrc/vdbe.c >vdbe.new
|
||||
$(TCLSH_CMD) $(TOP)/tool/vdbe-compress.tcl $(OPTS) <tsrc/vdbe.c >vdbe.new
|
||||
mv vdbe.new tsrc/vdbe.c
|
||||
touch .target_source
|
||||
|
||||
@@ -964,6 +967,15 @@ sqlite3_analyzer.c: sqlite3.c $(TOP)/src/test_stat.c $(TOP)/src/tclsqlite.c $(TO
|
||||
sqlite3_analyzer$(TEXE): sqlite3_analyzer.c
|
||||
$(LTLINK) sqlite3_analyzer.c -o $@ $(LIBTCL) $(TLIBS)
|
||||
|
||||
showdb$(TEXE): $(TOP)/tool/showdb.c sqlite3.c
|
||||
$(LTLINK) -o $@ $(TOP)/tool/showdb.c sqlite3.c $(TLIBS)
|
||||
|
||||
wordcount$(TEXE): $(TOP)/test/wordcount.c sqlite3.c
|
||||
$(LTLINK) -o $@ $(TOP)/test/wordcount.c sqlite3.c $(TLIBS)
|
||||
|
||||
speedtest1$(TEXE): $(TOP)/test/wordcount.c sqlite3.lo
|
||||
$(LTLINK) -o $@ $(TOP)/test/speedtest1.c sqlite3.lo $(TLIBS)
|
||||
|
||||
# Standard install and cleanup targets
|
||||
#
|
||||
lib_install: libsqlcipher.la
|
||||
|
||||
+133
-21
@@ -13,6 +13,13 @@ TOP = .
|
||||
USE_AMALGAMATION = 1
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to split the SQLite amalgamation file into chunks to
|
||||
# be used for debugging with Visual Studio.
|
||||
#
|
||||
!IFNDEF SPLIT_AMALGAMATION
|
||||
SPLIT_AMALGAMATION = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to use the International Components for Unicode (ICU).
|
||||
#
|
||||
!IFNDEF USE_ICU
|
||||
@@ -25,6 +32,13 @@ USE_ICU = 0
|
||||
USE_CRT_DLL = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to generate assembly code listings for the source code
|
||||
# files.
|
||||
#
|
||||
!IFNDEF USE_LISTINGS
|
||||
USE_LISTINGS = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to attempt setting the native compiler automatically
|
||||
# for cross-compiling the command line tools needed during the compilation
|
||||
# process.
|
||||
@@ -94,6 +108,14 @@ WIN32HEAP = 0
|
||||
DEBUG = 0
|
||||
!ENDIF
|
||||
|
||||
# Enable use of available compiler optimizations? Normally, this should be
|
||||
# non-zero. Setting this to zero, thus disabling all compiler optimizations,
|
||||
# can be useful for testing.
|
||||
#
|
||||
!IFNDEF OPTIMIZATIONS
|
||||
OPTIMIZATIONS = 2
|
||||
!ENDIF
|
||||
|
||||
# Check for the predefined command macro CC. This should point to the compiler
|
||||
# binary for the target platform. If it is not defined, simply define it to
|
||||
# the legacy default value 'cl.exe'.
|
||||
@@ -140,7 +162,7 @@ RC = rc.exe
|
||||
!IFDEF NCC
|
||||
NCC = $(NCC:\\=\)
|
||||
!ELSEIF $(XCOMPILE)!=0
|
||||
NCC = "$(VCINSTALLDIR)\bin\cl.exe"
|
||||
NCC = "$(VCINSTALLDIR)\bin\$(CC)"
|
||||
NCC = $(NCC:\\=\)
|
||||
!ELSE
|
||||
NCC = $(CC)
|
||||
@@ -173,6 +195,13 @@ NSDKLIBPATH = $(NSDKLIBPATH:\\=\)
|
||||
#
|
||||
BCC = $(NCC) -W3
|
||||
|
||||
# Check if assembly code listings should be generated for the source
|
||||
# code files to be compiled.
|
||||
#
|
||||
!IF $(USE_LISTINGS)!=0
|
||||
BCC = $(BCC) -FAcs
|
||||
!ENDIF
|
||||
|
||||
# Check if the native library paths should be used when compiling
|
||||
# the command line tools used during the compilation process. If
|
||||
# so, set the necessary macro now.
|
||||
@@ -185,9 +214,16 @@ NLTLIBPATHS = "/LIBPATH:$(NCRTLIBPATH)" "/LIBPATH:$(NSDKLIBPATH)"
|
||||
# will run on the target platform. (BCC and TCC are usually the
|
||||
# same unless your are cross-compiling.)
|
||||
#
|
||||
TCC = $(CC) -W3 -DSQLITE_OS_WIN=1 -I$(TOP) -I$(TOP)\src -fp:precise
|
||||
TCC = $(CC) -W3 -DSQLITE_OS_WIN=1 -I. -I$(TOP) -I$(TOP)\src -fp:precise
|
||||
RCC = $(RC) -DSQLITE_OS_WIN=1 -I$(TOP) -I$(TOP)\src
|
||||
|
||||
# Check if assembly code listings should be generated for the source
|
||||
# code files to be compiled.
|
||||
#
|
||||
!IF $(USE_LISTINGS)!=0
|
||||
TCC = $(TCC) -FAcs
|
||||
!ENDIF
|
||||
|
||||
# When compiling the library for use in the WinRT environment,
|
||||
# the following compile-time options must be used as well to
|
||||
# disable use of Win32 APIs that are not available and to enable
|
||||
@@ -234,6 +270,17 @@ TCC = $(TCC) -I$(TOP)\ext\rtree
|
||||
RCC = $(RCC) -I$(TOP)\ext\rtree
|
||||
!ENDIF
|
||||
|
||||
# The mksqlite3c.tcl script accepts some options on the command
|
||||
# line. When compiling with debugging enabled, some of these
|
||||
# options are necessary in order to allow debugging symbols to
|
||||
# work correctly with Visual Studio when using the amalgamation.
|
||||
#
|
||||
!IF $(DEBUG)>0
|
||||
MKSQLITE3C_ARGS = --linemacros
|
||||
!ELSE
|
||||
MKSQLITE3C_ARGS =
|
||||
!ENDIF
|
||||
|
||||
# Define -DNDEBUG to compile without debugging (i.e., for production usage)
|
||||
# Omitting the define will cause extra debugging code to be inserted and
|
||||
# includes extra comments when "EXPLAIN stmt" is used.
|
||||
@@ -343,11 +390,15 @@ TCLSH_CMD = tclsh85
|
||||
|
||||
# Compiler options needed for programs that use the readline() library.
|
||||
#
|
||||
!IFNDEF READLINE_FLAGS
|
||||
READLINE_FLAGS = -DHAVE_READLINE=0
|
||||
!ENDIF
|
||||
|
||||
# The library that programs using readline() must link against.
|
||||
#
|
||||
!IFNDEF LIBREADLINE
|
||||
LIBREADLINE =
|
||||
!ENDIF
|
||||
|
||||
# Should the database engine be compiled threadsafe
|
||||
#
|
||||
@@ -397,17 +448,30 @@ RCC = $(RCC) $(OPT_FEATURE_FLAGS)
|
||||
TCC = $(TCC) $(OPTS)
|
||||
RCC = $(RCC) $(OPTS)
|
||||
|
||||
# If symbols are enabled, enable PDBs.
|
||||
# If debugging is enabled, disable all optimizations and enable PDBs.
|
||||
# If compiling for debugging, add some defines.
|
||||
!IF $(DEBUG)>0
|
||||
TCC = $(TCC) -Od -D_DEBUG
|
||||
BCC = $(BCC) -Od -D_DEBUG
|
||||
TCC = $(TCC) -D_DEBUG
|
||||
BCC = $(BCC) -D_DEBUG
|
||||
RCC = $(RCC) -D_DEBUG
|
||||
!ELSE
|
||||
TCC = $(TCC) -O2
|
||||
BCC = $(BCC) -O2
|
||||
!ENDIF
|
||||
|
||||
# If optimizations are enabled or disabled (either implicitly or
|
||||
# explicitly), add the necessary flags.
|
||||
!IF $(DEBUG)>0 || $(OPTIMIZATIONS)==0
|
||||
TCC = $(TCC) -Od
|
||||
BCC = $(BCC) -Od
|
||||
!ELSEIF $(OPTIMIZATIONS)>=3
|
||||
TCC = $(TCC) -Ox
|
||||
BCC = $(BCC) -Ox
|
||||
!ELSEIF $(OPTIMIZATIONS)==2
|
||||
TCC = $(TCC) -O2
|
||||
BCC = $(BCC) -O2
|
||||
!ELSEIF $(OPTIMIZATIONS)==1
|
||||
TCC = $(TCC) -O1
|
||||
BCC = $(BCC) -O1
|
||||
!ENDIF
|
||||
|
||||
# If symbols are enabled (or compiling for debugging), enable PDBs.
|
||||
!IF $(DEBUG)>0 || $(SYMBOLS)!=0
|
||||
TCC = $(TCC) -Zi
|
||||
BCC = $(BCC) -Zi
|
||||
@@ -445,6 +509,17 @@ LTLIBOPTS = /MACHINE:$(PLATFORM)
|
||||
#
|
||||
!IF $(FOR_WINRT)!=0
|
||||
LTLINKOPTS = $(LTLINKOPTS) /APPCONTAINER
|
||||
!IF "$(VISUALSTUDIOVERSION)"=="12.0"
|
||||
!IF "$(PLATFORM)"=="x86"
|
||||
LTLINKOPTS = $(LTLINKOPTS) "/LIBPATH:$(VCINSTALLDIR)\lib\store"
|
||||
!ELSEIF "$(PLATFORM)"=="x64"
|
||||
LTLINKOPTS = $(LTLINKOPTS) "/LIBPATH:$(VCINSTALLDIR)\lib\store\amd64"
|
||||
!ELSEIF "$(PLATFORM)"=="ARM"
|
||||
LTLINKOPTS = $(LTLINKOPTS) "/LIBPATH:$(VCINSTALLDIR)\lib\store\arm"
|
||||
!ELSE
|
||||
LTLINKOPTS = $(LTLINKOPTS) "/LIBPATH:$(VCINSTALLDIR)\lib\store"
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
|
||||
# If either debugging or symbols are enabled, enable PDBs.
|
||||
@@ -465,7 +540,9 @@ LTLIBS = $(LTLIBS) $(LIBICU)
|
||||
!ENDIF
|
||||
|
||||
# nawk compatible awk.
|
||||
!IFNDEF NAWK
|
||||
NAWK = gawk.exe
|
||||
!ENDIF
|
||||
|
||||
# You should not have to change anything below this line
|
||||
###############################################################################
|
||||
@@ -602,7 +679,8 @@ SRC = \
|
||||
$(TOP)\src\wal.c \
|
||||
$(TOP)\src\wal.h \
|
||||
$(TOP)\src\walker.c \
|
||||
$(TOP)\src\where.c
|
||||
$(TOP)\src\where.c \
|
||||
$(TOP)\src\whereInt.h
|
||||
|
||||
# Source code for extensions
|
||||
#
|
||||
@@ -716,6 +794,7 @@ TESTEXT = \
|
||||
$(TOP)\ext\misc\percentile.c \
|
||||
$(TOP)\ext\misc\regexp.c \
|
||||
$(TOP)\ext\misc\spellfix.c \
|
||||
$(TOP)\ext\misc\totype.c \
|
||||
$(TOP)\ext\misc\wholenumber.c
|
||||
|
||||
|
||||
@@ -787,7 +866,8 @@ HDR = \
|
||||
$(TOP)\src\sqliteInt.h \
|
||||
$(TOP)\src\sqliteLimit.h \
|
||||
$(TOP)\src\vdbe.h \
|
||||
$(TOP)\src\vdbeInt.h
|
||||
$(TOP)\src\vdbeInt.h \
|
||||
$(TOP)\src\whereInt.h
|
||||
|
||||
# Header files used by extensions
|
||||
#
|
||||
@@ -842,22 +922,31 @@ mptester.exe: $(TOP)\mptest\mptest.c libsqlite3.lib $(LIBRESOBJS) sqlite3.h
|
||||
-mkdir tsrc
|
||||
for %i in ($(SRC)) do copy /Y %i tsrc
|
||||
del /Q tsrc\sqlite.h.in tsrc\parse.y
|
||||
$(TCLSH_CMD) $(TOP)\tool\vdbe-compress.tcl < tsrc\vdbe.c > vdbe.new
|
||||
$(TCLSH_CMD) $(TOP)\tool\vdbe-compress.tcl $(OPTS) < tsrc\vdbe.c > vdbe.new
|
||||
move vdbe.new tsrc\vdbe.c
|
||||
echo > .target_source
|
||||
|
||||
sqlite3.c: .target_source $(TOP)\tool\mksqlite3c.tcl
|
||||
$(TCLSH_CMD) $(TOP)\tool\mksqlite3c.tcl
|
||||
$(TCLSH_CMD) $(TOP)\tool\mksqlite3c.tcl $(MKSQLITE3C_ARGS)
|
||||
copy tsrc\shell.c .
|
||||
copy tsrc\sqlite3ext.h .
|
||||
|
||||
sqlite3-all.c: sqlite3.c $(TOP)\tool\split-sqlite3c.tcl
|
||||
$(TCLSH_CMD) $(TOP)\tool\split-sqlite3c.tcl
|
||||
|
||||
# Set the source code file to be used by executables and libraries when
|
||||
# they need the amalgamation.
|
||||
#
|
||||
!IF $(SPLIT_AMALGAMATION)!=0
|
||||
SQLITE3C = sqlite3-all.c
|
||||
!ELSE
|
||||
SQLITE3C = sqlite3.c
|
||||
!ENDIF
|
||||
|
||||
# Rule to build the amalgamation
|
||||
#
|
||||
sqlite3.lo: sqlite3.c
|
||||
$(LTCOMPILE) -c sqlite3.c
|
||||
sqlite3.lo: $(SQLITE3C)
|
||||
$(LTCOMPILE) -c $(SQLITE3C)
|
||||
|
||||
# Rules to build the LEMON compiler generator
|
||||
#
|
||||
@@ -1130,7 +1219,7 @@ parse.c: $(TOP)\src\parse.y lemon.exe $(TOP)\addopcodes.awk
|
||||
$(NAWK) -f $(TOP)\addopcodes.awk parse.h.temp > parse.h
|
||||
|
||||
sqlite3.h: $(TOP)\src\sqlite.h.in $(TOP)\manifest.uuid $(TOP)\VERSION
|
||||
$(TCLSH_CMD) $(TOP)\tool\mksqlite3h.tcl $(TOP) > sqlite3.h
|
||||
$(TCLSH_CMD) $(TOP)\tool\mksqlite3h.tcl $(TOP:\=/) > sqlite3.h
|
||||
|
||||
mkkeywordhash.exe: $(TOP)\tool\mkkeywordhash.c
|
||||
$(BCC) -Fe$@ $(OPT_FEATURE_FLAGS) $(OPTS) $(TOP)\tool\mkkeywordhash.c /link $(NLTLIBPATHS)
|
||||
@@ -1217,7 +1306,7 @@ TESTFIXTURE_FLAGS = -DTCLSH=1 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1
|
||||
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE
|
||||
|
||||
TESTFIXTURE_SRC0 = $(TESTEXT) $(TESTSRC2) libsqlite3.lib
|
||||
TESTFIXTURE_SRC1 = $(TESTEXT) sqlite3.c
|
||||
TESTFIXTURE_SRC1 = $(TESTEXT) $(SQLITE3C)
|
||||
!IF $(USE_AMALGAMATION)==0
|
||||
TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC0)
|
||||
!ELSE
|
||||
@@ -1230,6 +1319,9 @@ testfixture.exe: $(TESTFIXTURE_SRC) $(LIBRESOBJS) $(HDR)
|
||||
$(TESTFIXTURE_SRC) \
|
||||
/link $(LTLINKOPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LTLIBS) $(TLIBS)
|
||||
|
||||
extensiontest: testfixture.exe testloadext.dll
|
||||
.\testfixture.exe $(TOP)\test\loadext.test
|
||||
|
||||
fulltest: testfixture.exe sqlite3.exe
|
||||
.\testfixture.exe $(TOP)\test\all.test
|
||||
|
||||
@@ -1245,8 +1337,8 @@ queryplantest: testfixture.exe sqlite3.exe
|
||||
test: testfixture.exe sqlite3.exe
|
||||
.\testfixture.exe $(TOP)\test\veryquick.test
|
||||
|
||||
sqlite3_analyzer.c: sqlite3.c $(TOP)\src\test_stat.c $(TOP)\src\tclsqlite.c $(TOP)\tool\spaceanal.tcl
|
||||
copy sqlite3.c + $(TOP)\src\test_stat.c + $(TOP)\src\tclsqlite.c $@
|
||||
sqlite3_analyzer.c: $(SQLITE3C) $(TOP)\src\test_stat.c $(TOP)\src\tclsqlite.c $(TOP)\tool\spaceanal.tcl
|
||||
copy $(SQLITE3C) + $(TOP)\src\test_stat.c + $(TOP)\src\tclsqlite.c $@
|
||||
echo static const char *tclsh_main_loop(void){ >> $@
|
||||
echo static const char *zMainloop = >> $@
|
||||
$(NAWK) -f $(TOP)\tool\tostr.awk $(TOP)\tool\spaceanal.tcl >> $@
|
||||
@@ -1256,12 +1348,31 @@ sqlite3_analyzer.exe: sqlite3_analyzer.c $(LIBRESOBJS)
|
||||
$(LTLINK) -DBUILD_sqlite -DTCLSH=2 -I$(TCLINCDIR) sqlite3_analyzer.c \
|
||||
/link $(LTLINKOPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LTLIBS) $(TLIBS)
|
||||
|
||||
testloadext.lo: $(TOP)\src\test_loadext.c
|
||||
$(LTCOMPILE) -c $(TOP)\src\test_loadext.c
|
||||
|
||||
testloadext.dll: testloadext.lo
|
||||
$(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /DLL /OUT:$@ testloadext.lo
|
||||
|
||||
showdb.exe: $(TOP)\tool\showdb.c $(SQLITE3C)
|
||||
$(LTLINK) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
|
||||
$(TOP)\tool\showdb.c $(SQLITE3C)
|
||||
|
||||
wordcount.exe: $(TOP)\test\wordcount.c $(SQLITE3C)
|
||||
$(LTLINK) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
|
||||
$(TOP)\test\wordcount.c $(SQLITE3C)
|
||||
|
||||
speedtest1.exe: $(TOP)\test\speedtest1.c $(SQLITE3C)
|
||||
$(LTLINK) -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
|
||||
$(TOP)\test\speedtest1.c $(SQLITE3C)
|
||||
|
||||
clean:
|
||||
del /Q *.lo *.ilk *.lib *.obj *.pdb sqlite3.exe libsqlite3.lib
|
||||
del /Q *.da *.bb *.bbg gmon.out
|
||||
del /Q *.cod *.da *.bb *.bbg gmon.out
|
||||
del /Q sqlite3.h opcodes.c opcodes.h
|
||||
del /Q lemon.exe lempar.c parse.*
|
||||
del /Q mkkeywordhash.exe keywordhash.h
|
||||
del /Q notasharedlib.*
|
||||
-rmdir /Q/S .deps
|
||||
-rmdir /Q/S .libs
|
||||
-rmdir /Q/S quota2a
|
||||
@@ -1270,9 +1381,10 @@ clean:
|
||||
-rmdir /Q/S tsrc
|
||||
del /Q .target_source
|
||||
del /Q tclsqlite3.exe tclsqlite3.exp
|
||||
del /Q testloadext.dll testloadext.exp
|
||||
del /Q testfixture.exe testfixture.exp test.db
|
||||
del /Q sqlite3.dll sqlite3.lib sqlite3.exp sqlite3.def
|
||||
del /Q sqlite3.c
|
||||
del /Q sqlite3.c sqlite3-*.c
|
||||
del /Q sqlite3rc.h
|
||||
del /Q shell.c sqlite3ext.h
|
||||
del /Q sqlite3_analyzer.exe sqlite3_analyzer.exp sqlite3_analyzer.c
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
<h1 align="center">SQLite Source Repository</h1>
|
||||
|
||||
This repository contains the complete source code for the SQLite database
|
||||
engine. Some test scripts are also include. However, many other test scripts
|
||||
and most of the documentation are managed separately.
|
||||
|
||||
## Compiling
|
||||
|
||||
First create a directory in which to place
|
||||
the build products. It is recommended, but not required, that the
|
||||
build directory be separate from the source directory. Cd into the
|
||||
build directory and then from the build directory run the configure
|
||||
script found at the root of the source tree. Then run "make".
|
||||
|
||||
For example:
|
||||
|
||||
tar xzf sqlite.tar.gz ;# Unpack the source tree into "sqlite"
|
||||
mkdir bld ;# Build will occur in a sibling directory
|
||||
cd bld ;# Change to the build directory
|
||||
../sqlite/configure ;# Run the configure script
|
||||
make ;# Run the makefile.
|
||||
make sqlite3.c ;# Build the "amalgamation" source file
|
||||
make test ;# Run some tests (requires Tcl)
|
||||
|
||||
See the makefile for additional targets.
|
||||
|
||||
The configure script uses autoconf 2.61 and libtool. If the configure
|
||||
script does not work out for you, there is a generic makefile named
|
||||
"Makefile.linux-gcc" in the top directory of the source tree that you
|
||||
can copy and edit to suit your needs. Comments on the generic makefile
|
||||
show what changes are needed.
|
||||
|
||||
## Using MSVC
|
||||
|
||||
On Windows, all applicable build products can be compiled with MSVC.
|
||||
First open the command prompt window associated with the desired compiler
|
||||
version (e.g. "Developer Command Prompt for VS2013"). Next, use NMAKE
|
||||
with the provided "Makefile.msc" to build one of the supported targets.
|
||||
|
||||
For example:
|
||||
|
||||
mkdir bld
|
||||
cd bld
|
||||
nmake /f Makefile.msc TOP=..\sqlite
|
||||
nmake /f Makefile.msc sqlite3.c TOP=..\sqlite
|
||||
nmake /f Makefile.msc sqlite3.dll TOP=..\sqlite
|
||||
nmake /f Makefile.msc sqlite3.exe TOP=..\sqlite
|
||||
nmake /f Makefile.msc test TOP=..\sqlite
|
||||
|
||||
There are several build options that can be set via the NMAKE command
|
||||
line. For example, to build for WinRT, simply add "FOR_WINRT=1" argument
|
||||
to the "sqlite3.dll" command line above. When debugging into the SQLite
|
||||
code, adding the "DEBUG=1" argument to one of the above command lines is
|
||||
recommended.
|
||||
|
||||
SQLite does not require Tcl to run, but a Tcl installation is required
|
||||
by the makefiles (including those for MSVC). SQLite contains a lot of
|
||||
generated code and Tcl is used to do much of that code generation. The
|
||||
makefiles also require AWK.
|
||||
|
||||
## Source Code Tour
|
||||
|
||||
Most of the core source files are in the **src/** subdirectory. But
|
||||
src/ also contains files used to build the "testfixture" test harness;
|
||||
those file all begin with "test". And src/ contains the "shell.c" file
|
||||
which is the main program for the "sqlite3.exe" command-line shell and
|
||||
the "tclsqlite.c" file which implements the bindings to SQLite from the
|
||||
Tcl programming language. (Historical note: SQLite began as a Tcl
|
||||
extension and only later escaped to the wild as an independent library.)
|
||||
|
||||
Test scripts and programs are found in the **test/** subdirectory.
|
||||
There are other test suites for SQLite (see
|
||||
[How SQLite Is Tested](http://www.sqlite.org/testing.html))
|
||||
but those other test suites are
|
||||
in separate source repositories.
|
||||
|
||||
The **ext/** subdirectory contains code for extensions. The
|
||||
Full-text search engine is in **ext/fts3**. The R-Tree engine is in
|
||||
**ext/rtree**. The **ext/misc** subdirectory contains a number of
|
||||
smaller, single-file extensions, such as a REGEXP operator.
|
||||
|
||||
The **tool/** subdirectory contains various scripts and programs used
|
||||
for building generated source code files or for testing or for generating
|
||||
accessory programs such as "sqlite3_analyzer(.exe)".
|
||||
|
||||
### Generated Source Code Files
|
||||
|
||||
Several of the C-language source files used by SQLite are generated from
|
||||
other sources rather than being typed in manually by a programmer. This
|
||||
section will summarize those automatically-generated files. To create all
|
||||
of the automatically-generated files, simply run "make target_source".
|
||||
The "target_source" make target will create a subdirectory "tsrc/" and
|
||||
fill it with all the source files needed to build SQLite, both
|
||||
manually-edited files and automatically-generated files.
|
||||
|
||||
The SQLite interface is defined by the **sqlite3.h** header file, which is
|
||||
generated from src/sqlite.h.in, ./manifest.uuid, and ./VERSION. The
|
||||
Tcl script at tool/mksqlite3h.tcl does the conversion. The manifest.uuid
|
||||
file contains the SHA1 hash of the particular check-in and is used to generate
|
||||
the SQLITE_SOURCE_ID macro. The VERSION file contains the current SQLite
|
||||
version number. The sqlite3.h header is really just a copy of src/sqlite.h.in
|
||||
with the source-id and version number inserted at just the right spots.
|
||||
Note that comment text in the sqlite3.h file is used to generate much of
|
||||
the SQLite API documentation. The Tcl scripts used to generate that
|
||||
documentation are in a separate source repository.
|
||||
|
||||
The SQL language parser is **parse.c** which is generate from a grammar in
|
||||
the src/parse.y file. The conversion of "parse.y" into "parse.c" is done
|
||||
by the [lemon](./doc/lemon.html) LALR(1) parser generator. The source code
|
||||
for lemon is at tool/lemon.c. Lemon uses a
|
||||
template for generating its parser. A generic template is in tool/lempar.c,
|
||||
but SQLite uses a slightly modified template found in src/lempar.c.
|
||||
|
||||
Lemon also generates the **parse.h** header file, at the same time it
|
||||
generates parse.c. But the parse.h header file is
|
||||
modified further (to add additional symbols) using the ./addopcodes.awk
|
||||
AWK script.
|
||||
|
||||
The **opcodes.h** header file contains macros that define the numbers
|
||||
corresponding to opcodes in the "VDBE" virtual machine. The opcodes.h
|
||||
file is generated by the scanning the src/vdbe.c source file. The
|
||||
AWK script at ./mkopcodeh.awk does this scan and generates opcodes.h.
|
||||
A second AWK script, ./mkopcodec.awk, then scans opcodes.h to generate
|
||||
the **opcodes.c** source file, which contains a reverse mapping from
|
||||
opcode-number to opcode-name that is used for EXPLAIN output.
|
||||
|
||||
The **keywordhash.h** header file contains the definition of a hash table
|
||||
that maps SQL language keywords (ex: "CREATE", "SELECT", "INDEX", etc.) into
|
||||
the numeric codes used by the parse.c parser. The keywordhash.h file is
|
||||
generated by a C-language program at tool mkkeywordhash.c.
|
||||
|
||||
### The Amalgamation
|
||||
|
||||
All of the individual C source code and header files (both manually-edited
|
||||
and automatically-generated) can be combined into a single big source file
|
||||
**sqlite3.c** called "the amalgamation". The amalgamation is the recommended
|
||||
way of using SQLite in a larger application. Combining all individual
|
||||
source code files into a single big source code file allows the C compiler
|
||||
to perform more cross-procedure analysis and generate better code. SQLite
|
||||
runs about 5% faster when compiled from the amalgamation versus when compiled
|
||||
from individual source files.
|
||||
|
||||
The amalgamation is generated from the tool/mksqlite3c.tcl Tcl script.
|
||||
First, all of the individual source files must be gathered into the tsrc/
|
||||
subdirectory (using the equivalent of "make target_source") then the
|
||||
tool/mksqlite3c.tcl script is run to copy them all together in just the
|
||||
right order while resolving internal "#include" references.
|
||||
|
||||
The amalgamation source file is more than 100K lines long. Some symbolic
|
||||
debuggers (most notably MSVC) are unable to deal with files longer than 64K
|
||||
lines. To work around this, a separate Tcl script, tool/split-sqlite3c.tcl,
|
||||
can be run on the amalgamation to break it up into a single small C file
|
||||
called **sqlite3-all.c** that does #include on about five other files
|
||||
named **sqlite3-1.c**, **sqlite3-2.c**, ..., **sqlite3-5.c**. In this way,
|
||||
all of the source code is contained within a single translation unit so
|
||||
that the compiler can do extra cross-procedure optimization, but no
|
||||
individual source file exceeds 32K lines in length.
|
||||
|
||||
## How It All Fits Together
|
||||
|
||||
SQLite is modular in design.
|
||||
See the [architectural description](http://www.sqlite.org/arch.html)
|
||||
for details. Other documents that are useful in
|
||||
(helping to understand how SQLite works include the
|
||||
[file format](http://www.sqlite.org/fileformat2.html) description,
|
||||
the [virtual machine](http://www.sqlite.org/vdbe.html) that runs
|
||||
prepared statements, the description of
|
||||
[how transactions work](http://www.sqlite.org/atomiccommit.html), and
|
||||
the [overview of the query planner](http://www.sqlite.org/optoverview.html).
|
||||
|
||||
Unfortunately, years of effort have gone into optimizating SQLite, both
|
||||
for small size and high performance. And optimizations tend to result in
|
||||
complex code. So there is a lot of complexity in the SQLite implementation.
|
||||
|
||||
Key files:
|
||||
|
||||
* **sqlite3.h** - This file defines the public interface to the SQLite
|
||||
library. Readers will need to be familiar with this interface before
|
||||
trying to understand how the library works internally.
|
||||
|
||||
* **sqliteInt.h** - this header file defines many of the data objects
|
||||
used internally by SQLite.
|
||||
|
||||
* **parse.y** - This file describes the LALR(1) grammer that SQLite uses
|
||||
to parse SQL statements, and the actions that are taken at each stop
|
||||
in the parsing process.
|
||||
|
||||
* **vdbe.c** - This file implements the virtual machine that runs
|
||||
prepared statements. There are various helper files whose names
|
||||
begin with "vdbe". The VDBE has access to the vdbeInt.h header file
|
||||
which defines internal data objects. The rest of SQLite interacts
|
||||
with the VDBE through an interface defined by vdbe.h.
|
||||
|
||||
* **where.c** - This file analyzes the WHERE clause and generates
|
||||
virtual machine code to run queries efficiently. This file is
|
||||
sometimes called the "query optimizer". It has its own private
|
||||
header file, whereInt.h, that defines data objects used internally.
|
||||
|
||||
* **btree.c** - This file contains the implementation of the B-Tree
|
||||
storage engine used by SQLite.
|
||||
|
||||
* **pager.c** - This file contains the "pager" implementation, the
|
||||
module that implements transactions.
|
||||
|
||||
* **os_unix.c** and **os_win.c** - These two files implement the interface
|
||||
between SQLite and the underlying operating system using the run-time
|
||||
pluggable VFS interface.
|
||||
|
||||
|
||||
## Contacts
|
||||
|
||||
The main SQLite webpage is [http://www.sqlite.org/](http://www.sqlite.org/)
|
||||
with geographically distributed backup servers at
|
||||
[http://www2.sqlite.org/](http://www2.sqlite.org) and
|
||||
[http://www3.sqlite.org/](http://www3.sqlite.org).
|
||||
+1
-1
@@ -28,7 +28,7 @@ END {
|
||||
printf "#define TK_%-29s %4d\n", "COLUMN", ++max
|
||||
printf "#define TK_%-29s %4d\n", "AGG_FUNCTION", ++max
|
||||
printf "#define TK_%-29s %4d\n", "AGG_COLUMN", ++max
|
||||
printf "#define TK_%-29s %4d\n", "CONST_FUNC", ++max
|
||||
printf "#define TK_%-29s %4d\n", "UMINUS", ++max
|
||||
printf "#define TK_%-29s %4d\n", "UPLUS", ++max
|
||||
printf "#define TK_%-29s %4d\n", "REGISTER", ++max
|
||||
}
|
||||
|
||||
@@ -11,5 +11,5 @@ 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 http://www.sqlite.org/ and
|
||||
in particular http://www.sqlite.org/tclsqlite.html.
|
||||
For full documentation see \fIhttp://www.sqlite.org/\fR and
|
||||
in particular \fIhttp://www.sqlite.org/tclsqlite.html\fR.
|
||||
|
||||
@@ -1,119 +1,528 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# install - install a program, script, or datafile
|
||||
# This comes from X11R5; it is not part of GNU.
|
||||
|
||||
scriptversion=2011-04-20.01; # UTC
|
||||
|
||||
# This originates from X11R5 (mit/util/scripts/install.sh), which was
|
||||
# later released in X11R6 (xc/config/util/install.sh) with the
|
||||
# following copyright and license.
|
||||
#
|
||||
# $XConsortium: install.sh,v 1.2 89/12/18 14:47:22 jim Exp $
|
||||
# Copyright (C) 1994 X Consortium
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
|
||||
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Except as contained in this notice, the name of the X Consortium shall not
|
||||
# be used in advertising or otherwise to promote the sale, use or other deal-
|
||||
# ings in this Software without prior written authorization from the X Consor-
|
||||
# tium.
|
||||
#
|
||||
#
|
||||
# FSF changes to this file are in the public domain.
|
||||
#
|
||||
# Calling this script install-sh is preferred over install.sh, to prevent
|
||||
# `make' implicit rules from creating a file called install from it
|
||||
# when there is no Makefile.
|
||||
#
|
||||
# This script is compatible with the BSD install script, but was written
|
||||
# from scratch.
|
||||
#
|
||||
|
||||
nl='
|
||||
'
|
||||
IFS=" "" $nl"
|
||||
|
||||
# set DOITPROG to echo to test this script
|
||||
|
||||
# Don't use :- since 4.3BSD and earlier shells don't like it.
|
||||
doit="${DOITPROG-}"
|
||||
doit=${DOITPROG-}
|
||||
if test -z "$doit"; then
|
||||
doit_exec=exec
|
||||
else
|
||||
doit_exec=$doit
|
||||
fi
|
||||
|
||||
# Put in absolute file names if you don't have them in your path;
|
||||
# or use environment vars.
|
||||
|
||||
# put in absolute paths if you don't have them in your path; or use env. vars.
|
||||
chgrpprog=${CHGRPPROG-chgrp}
|
||||
chmodprog=${CHMODPROG-chmod}
|
||||
chownprog=${CHOWNPROG-chown}
|
||||
cmpprog=${CMPPROG-cmp}
|
||||
cpprog=${CPPROG-cp}
|
||||
mkdirprog=${MKDIRPROG-mkdir}
|
||||
mvprog=${MVPROG-mv}
|
||||
rmprog=${RMPROG-rm}
|
||||
stripprog=${STRIPPROG-strip}
|
||||
|
||||
mvprog="${MVPROG-mv}"
|
||||
cpprog="${CPPROG-cp}"
|
||||
chmodprog="${CHMODPROG-chmod}"
|
||||
chownprog="${CHOWNPROG-chown}"
|
||||
chgrpprog="${CHGRPPROG-chgrp}"
|
||||
stripprog="${STRIPPROG-strip}"
|
||||
rmprog="${RMPROG-rm}"
|
||||
posix_glob='?'
|
||||
initialize_posix_glob='
|
||||
test "$posix_glob" != "?" || {
|
||||
if (set -f) 2>/dev/null; then
|
||||
posix_glob=
|
||||
else
|
||||
posix_glob=:
|
||||
fi
|
||||
}
|
||||
'
|
||||
|
||||
instcmd="$mvprog"
|
||||
chmodcmd=""
|
||||
chowncmd=""
|
||||
chgrpcmd=""
|
||||
stripcmd=""
|
||||
posix_mkdir=
|
||||
|
||||
# Desired mode of installed file.
|
||||
mode=0755
|
||||
|
||||
chgrpcmd=
|
||||
chmodcmd=$chmodprog
|
||||
chowncmd=
|
||||
mvcmd=$mvprog
|
||||
rmcmd="$rmprog -f"
|
||||
mvcmd="$mvprog"
|
||||
src=""
|
||||
dst=""
|
||||
stripcmd=
|
||||
|
||||
while [ x"$1" != x ]; do
|
||||
case $1 in
|
||||
-c) instcmd="$cpprog"
|
||||
shift
|
||||
continue;;
|
||||
src=
|
||||
dst=
|
||||
dir_arg=
|
||||
dst_arg=
|
||||
|
||||
-m) chmodcmd="$chmodprog $2"
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
copy_on_change=false
|
||||
no_target_directory=
|
||||
|
||||
-o) chowncmd="$chownprog $2"
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
usage="\
|
||||
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
|
||||
or: $0 [OPTION]... SRCFILES... DIRECTORY
|
||||
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
|
||||
or: $0 [OPTION]... -d DIRECTORIES...
|
||||
|
||||
-g) chgrpcmd="$chgrpprog $2"
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
In the 1st form, copy SRCFILE to DSTFILE.
|
||||
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
|
||||
In the 4th, create DIRECTORIES.
|
||||
|
||||
-s) stripcmd="$stripprog"
|
||||
shift
|
||||
continue;;
|
||||
Options:
|
||||
--help display this help and exit.
|
||||
--version display version info and exit.
|
||||
|
||||
*) if [ x"$src" = x ]
|
||||
then
|
||||
src=$1
|
||||
else
|
||||
dst=$1
|
||||
fi
|
||||
shift
|
||||
continue;;
|
||||
esac
|
||||
-c (ignored)
|
||||
-C install only if different (preserve the last data modification time)
|
||||
-d create directories instead of installing files.
|
||||
-g GROUP $chgrpprog installed files to GROUP.
|
||||
-m MODE $chmodprog installed files to MODE.
|
||||
-o USER $chownprog installed files to USER.
|
||||
-s $stripprog installed files.
|
||||
-S $stripprog installed files.
|
||||
-t DIRECTORY install into DIRECTORY.
|
||||
-T report an error if DSTFILE is a directory.
|
||||
|
||||
Environment variables override the default commands:
|
||||
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
|
||||
RMPROG STRIPPROG
|
||||
"
|
||||
|
||||
while test $# -ne 0; do
|
||||
case $1 in
|
||||
-c) ;;
|
||||
|
||||
-C) copy_on_change=true;;
|
||||
|
||||
-d) dir_arg=true;;
|
||||
|
||||
-g) chgrpcmd="$chgrpprog $2"
|
||||
shift;;
|
||||
|
||||
--help) echo "$usage"; exit $?;;
|
||||
|
||||
-m) mode=$2
|
||||
case $mode in
|
||||
*' '* | *' '* | *'
|
||||
'* | *'*'* | *'?'* | *'['*)
|
||||
echo "$0: invalid mode: $mode" >&2
|
||||
exit 1;;
|
||||
esac
|
||||
shift;;
|
||||
|
||||
-o) chowncmd="$chownprog $2"
|
||||
shift;;
|
||||
|
||||
-s) stripcmd=$stripprog;;
|
||||
|
||||
-S) stripcmd="$stripprog $2"
|
||||
shift;;
|
||||
|
||||
-t) dst_arg=$2
|
||||
shift;;
|
||||
|
||||
-T) no_target_directory=true;;
|
||||
|
||||
--version) echo "$0 $scriptversion"; exit $?;;
|
||||
|
||||
--) shift
|
||||
break;;
|
||||
|
||||
-*) echo "$0: invalid option: $1" >&2
|
||||
exit 1;;
|
||||
|
||||
*) break;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [ x"$src" = x ]
|
||||
then
|
||||
echo "install: no input file specified"
|
||||
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
|
||||
# When -d is used, all remaining arguments are directories to create.
|
||||
# When -t is used, the destination is already specified.
|
||||
# Otherwise, the last argument is the destination. Remove it from $@.
|
||||
for arg
|
||||
do
|
||||
if test -n "$dst_arg"; then
|
||||
# $@ is not empty: it contains at least $arg.
|
||||
set fnord "$@" "$dst_arg"
|
||||
shift # fnord
|
||||
fi
|
||||
shift # arg
|
||||
dst_arg=$arg
|
||||
done
|
||||
fi
|
||||
|
||||
if test $# -eq 0; then
|
||||
if test -z "$dir_arg"; then
|
||||
echo "$0: no input file specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
# It's OK to call `install-sh -d' without argument.
|
||||
# This can happen when creating conditional directories.
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if test -z "$dir_arg"; then
|
||||
do_exit='(exit $ret); exit $ret'
|
||||
trap "ret=129; $do_exit" 1
|
||||
trap "ret=130; $do_exit" 2
|
||||
trap "ret=141; $do_exit" 13
|
||||
trap "ret=143; $do_exit" 15
|
||||
|
||||
# Set umask so as not to create temps with too-generous modes.
|
||||
# However, 'strip' requires both read and write access to temps.
|
||||
case $mode in
|
||||
# Optimize common cases.
|
||||
*644) cp_umask=133;;
|
||||
*755) cp_umask=22;;
|
||||
|
||||
*[0-7])
|
||||
if test -z "$stripcmd"; then
|
||||
u_plus_rw=
|
||||
else
|
||||
u_plus_rw='% 200'
|
||||
fi
|
||||
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
|
||||
*)
|
||||
if test -z "$stripcmd"; then
|
||||
u_plus_rw=
|
||||
else
|
||||
u_plus_rw=,u+rw
|
||||
fi
|
||||
cp_umask=$mode$u_plus_rw;;
|
||||
esac
|
||||
fi
|
||||
|
||||
for src
|
||||
do
|
||||
# Protect names starting with `-'.
|
||||
case $src in
|
||||
-*) src=./$src;;
|
||||
esac
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
dst=$src
|
||||
dstdir=$dst
|
||||
test -d "$dstdir"
|
||||
dstdir_status=$?
|
||||
else
|
||||
|
||||
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
|
||||
# might cause directories to be created, which would be especially bad
|
||||
# if $src (and thus $dsttmp) contains '*'.
|
||||
if test ! -f "$src" && test ! -d "$src"; then
|
||||
echo "$0: $src does not exist." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -z "$dst_arg"; then
|
||||
echo "$0: no destination specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
dst=$dst_arg
|
||||
# Protect names starting with `-'.
|
||||
case $dst in
|
||||
-*) dst=./$dst;;
|
||||
esac
|
||||
|
||||
# If destination is a directory, append the input filename; won't work
|
||||
# if double slashes aren't ignored.
|
||||
if test -d "$dst"; then
|
||||
if test -n "$no_target_directory"; then
|
||||
echo "$0: $dst_arg: Is a directory" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
dstdir=$dst
|
||||
dst=$dstdir/`basename "$src"`
|
||||
dstdir_status=0
|
||||
else
|
||||
# Prefer dirname, but fall back on a substitute if dirname fails.
|
||||
dstdir=`
|
||||
(dirname "$dst") 2>/dev/null ||
|
||||
expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
|
||||
X"$dst" : 'X\(//\)[^/]' \| \
|
||||
X"$dst" : 'X\(//\)$' \| \
|
||||
X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
|
||||
echo X"$dst" |
|
||||
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
/^X\(\/\/\)[^/].*/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
/^X\(\/\/\)$/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
/^X\(\/\).*/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
s/.*/./; q'
|
||||
`
|
||||
|
||||
if [ x"$dst" = x ]
|
||||
then
|
||||
echo "install: no destination specified"
|
||||
exit 1
|
||||
fi
|
||||
test -d "$dstdir"
|
||||
dstdir_status=$?
|
||||
fi
|
||||
fi
|
||||
|
||||
obsolete_mkdir_used=false
|
||||
|
||||
# If destination is a directory, append the input filename; if your system
|
||||
# does not like double slashes in filenames, you may need to add some logic
|
||||
if test $dstdir_status != 0; then
|
||||
case $posix_mkdir in
|
||||
'')
|
||||
# Create intermediate dirs using mode 755 as modified by the umask.
|
||||
# This is like FreeBSD 'install' as of 1997-10-28.
|
||||
umask=`umask`
|
||||
case $stripcmd.$umask in
|
||||
# Optimize common cases.
|
||||
*[2367][2367]) mkdir_umask=$umask;;
|
||||
.*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
|
||||
|
||||
if [ -d $dst ]
|
||||
then
|
||||
dst="$dst"/`basename $src`
|
||||
fi
|
||||
*[0-7])
|
||||
mkdir_umask=`expr $umask + 22 \
|
||||
- $umask % 100 % 40 + $umask % 20 \
|
||||
- $umask % 10 % 4 + $umask % 2
|
||||
`;;
|
||||
*) mkdir_umask=$umask,go-w;;
|
||||
esac
|
||||
|
||||
# Make a temp file name in the proper directory.
|
||||
# With -d, create the new directory with the user-specified mode.
|
||||
# Otherwise, rely on $mkdir_umask.
|
||||
if test -n "$dir_arg"; then
|
||||
mkdir_mode=-m$mode
|
||||
else
|
||||
mkdir_mode=
|
||||
fi
|
||||
|
||||
dstdir=`dirname $dst`
|
||||
dsttmp=$dstdir/#inst.$$#
|
||||
posix_mkdir=false
|
||||
case $umask in
|
||||
*[123567][0-7][0-7])
|
||||
# POSIX mkdir -p sets u+wx bits regardless of umask, which
|
||||
# is incompatible with FreeBSD 'install' when (umask & 300) != 0.
|
||||
;;
|
||||
*)
|
||||
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
|
||||
trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
|
||||
|
||||
# Move or copy the file name to the temp name
|
||||
if (umask $mkdir_umask &&
|
||||
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
|
||||
then
|
||||
if test -z "$dir_arg" || {
|
||||
# Check for POSIX incompatibilities with -m.
|
||||
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
|
||||
# other-writeable bit of parent directory when it shouldn't.
|
||||
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
|
||||
ls_ld_tmpdir=`ls -ld "$tmpdir"`
|
||||
case $ls_ld_tmpdir in
|
||||
d????-?r-*) different_mode=700;;
|
||||
d????-?--*) different_mode=755;;
|
||||
*) false;;
|
||||
esac &&
|
||||
$mkdirprog -m$different_mode -p -- "$tmpdir" && {
|
||||
ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
|
||||
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
|
||||
}
|
||||
}
|
||||
then posix_mkdir=:
|
||||
fi
|
||||
rmdir "$tmpdir/d" "$tmpdir"
|
||||
else
|
||||
# Remove any dirs left behind by ancient mkdir implementations.
|
||||
rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
|
||||
fi
|
||||
trap '' 0;;
|
||||
esac;;
|
||||
esac
|
||||
|
||||
$doit $instcmd $src $dsttmp
|
||||
if
|
||||
$posix_mkdir && (
|
||||
umask $mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
|
||||
)
|
||||
then :
|
||||
else
|
||||
|
||||
# and set any options; do chmod last to preserve setuid bits
|
||||
# The umask is ridiculous, or mkdir does not conform to POSIX,
|
||||
# or it failed possibly due to a race condition. Create the
|
||||
# directory the slow way, step by step, checking for races as we go.
|
||||
|
||||
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; fi
|
||||
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; fi
|
||||
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; fi
|
||||
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; fi
|
||||
case $dstdir in
|
||||
/*) prefix='/';;
|
||||
-*) prefix='./';;
|
||||
*) prefix='';;
|
||||
esac
|
||||
|
||||
# Now rename the file to the real destination.
|
||||
eval "$initialize_posix_glob"
|
||||
|
||||
$doit $rmcmd $dst
|
||||
$doit $mvcmd $dsttmp $dst
|
||||
oIFS=$IFS
|
||||
IFS=/
|
||||
$posix_glob set -f
|
||||
set fnord $dstdir
|
||||
shift
|
||||
$posix_glob set +f
|
||||
IFS=$oIFS
|
||||
|
||||
prefixes=
|
||||
|
||||
exit 0
|
||||
for d
|
||||
do
|
||||
test -z "$d" && continue
|
||||
|
||||
prefix=$prefix$d
|
||||
if test -d "$prefix"; then
|
||||
prefixes=
|
||||
else
|
||||
if $posix_mkdir; then
|
||||
(umask=$mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
|
||||
# Don't fail if two instances are running concurrently.
|
||||
test -d "$prefix" || exit 1
|
||||
else
|
||||
case $prefix in
|
||||
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
|
||||
*) qprefix=$prefix;;
|
||||
esac
|
||||
prefixes="$prefixes '$qprefix'"
|
||||
fi
|
||||
fi
|
||||
prefix=$prefix/
|
||||
done
|
||||
|
||||
if test -n "$prefixes"; then
|
||||
# Don't fail if two instances are running concurrently.
|
||||
(umask $mkdir_umask &&
|
||||
eval "\$doit_exec \$mkdirprog $prefixes") ||
|
||||
test -d "$dstdir" || exit 1
|
||||
obsolete_mkdir_used=true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
|
||||
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
|
||||
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
|
||||
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
|
||||
else
|
||||
|
||||
# Make a couple of temp file names in the proper directory.
|
||||
dsttmp=$dstdir/_inst.$$_
|
||||
rmtmp=$dstdir/_rm.$$_
|
||||
|
||||
# Trap to clean up those temp files at exit.
|
||||
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
|
||||
|
||||
# Copy the file name to the temp name.
|
||||
(umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
|
||||
|
||||
# and set any options; do chmod last to preserve setuid bits.
|
||||
#
|
||||
# If any of these fail, we abort the whole thing. If we want to
|
||||
# ignore errors from any of these, just make sure not to ignore
|
||||
# errors from the above "$doit $cpprog $src $dsttmp" command.
|
||||
#
|
||||
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
|
||||
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
|
||||
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
|
||||
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
|
||||
|
||||
# If -C, don't bother to copy if it wouldn't change the file.
|
||||
if $copy_on_change &&
|
||||
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
|
||||
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
|
||||
|
||||
eval "$initialize_posix_glob" &&
|
||||
$posix_glob set -f &&
|
||||
set X $old && old=:$2:$4:$5:$6 &&
|
||||
set X $new && new=:$2:$4:$5:$6 &&
|
||||
$posix_glob set +f &&
|
||||
|
||||
test "$old" = "$new" &&
|
||||
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
|
||||
then
|
||||
rm -f "$dsttmp"
|
||||
else
|
||||
# Rename the file to the real destination.
|
||||
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
|
||||
|
||||
# The rename failed, perhaps because mv can't rename something else
|
||||
# to itself, or perhaps because mv is so ancient that it does not
|
||||
# support -f.
|
||||
{
|
||||
# Now remove or move aside any old file at destination location.
|
||||
# We try this two ways since rm can't unlink itself on some
|
||||
# systems and the destination file might be busy for other
|
||||
# reasons. In this case, the final cleanup might fail but the new
|
||||
# file should still install successfully.
|
||||
{
|
||||
test ! -f "$dst" ||
|
||||
$doit $rmcmd -f "$dst" 2>/dev/null ||
|
||||
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
|
||||
{ $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
|
||||
} ||
|
||||
{ echo "$0: cannot unlink or rename $dst" >&2
|
||||
(exit 1); exit 1
|
||||
}
|
||||
} &&
|
||||
|
||||
# Now rename the file to the real destination.
|
||||
$doit $mvcmd "$dsttmp" "$dst"
|
||||
}
|
||||
fi || exit 1
|
||||
|
||||
trap '' 0
|
||||
fi
|
||||
done
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
||||
|
||||
+313
-145
@@ -8,8 +8,6 @@
|
||||
#
|
||||
# See the file "license.terms" for information on usage and redistribution
|
||||
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
#
|
||||
# RCS: @(#) $Id: tcl.m4,v 1.145 2010/08/17 00:33:40 hobbs Exp $
|
||||
|
||||
AC_PREREQ(2.57)
|
||||
|
||||
@@ -140,6 +138,8 @@ AC_DEFUN([TEA_PATH_TCLCONFIG], [
|
||||
`ls -d /usr/contrib/lib 2>/dev/null` \
|
||||
`ls -d /usr/lib 2>/dev/null` \
|
||||
`ls -d /usr/lib64 2>/dev/null` \
|
||||
`ls -d /usr/lib/tcl8.6 2>/dev/null` \
|
||||
`ls -d /usr/lib/tcl8.5 2>/dev/null` \
|
||||
; do
|
||||
if test -f "$i/tclConfig.sh" ; then
|
||||
ac_cv_c_tclconfig="`(cd $i; pwd)`"
|
||||
@@ -170,7 +170,7 @@ AC_DEFUN([TEA_PATH_TCLCONFIG], [
|
||||
|
||||
if test x"${ac_cv_c_tclconfig}" = x ; then
|
||||
TCL_BIN_DIR="# no Tcl configs found"
|
||||
AC_MSG_ERROR([Can't find Tcl configuration definitions])
|
||||
AC_MSG_ERROR([Can't find Tcl configuration definitions. Use --with-tcl to specify a directory containing tclConfig.sh])
|
||||
else
|
||||
no_tcl=
|
||||
TCL_BIN_DIR="${ac_cv_c_tclconfig}"
|
||||
@@ -323,7 +323,7 @@ AC_DEFUN([TEA_PATH_TKCONFIG], [
|
||||
|
||||
if test x"${ac_cv_c_tkconfig}" = x ; then
|
||||
TK_BIN_DIR="# no Tk configs found"
|
||||
AC_MSG_ERROR([Can't find Tk configuration definitions])
|
||||
AC_MSG_ERROR([Can't find Tk configuration definitions. Use --with-tk to specify a directory containing tkConfig.sh])
|
||||
else
|
||||
no_tk=
|
||||
TK_BIN_DIR="${ac_cv_c_tkconfig}"
|
||||
@@ -344,11 +344,10 @@ AC_DEFUN([TEA_PATH_TKCONFIG], [
|
||||
#
|
||||
# Results:
|
||||
#
|
||||
# Subst the following vars:
|
||||
# Substitutes the following vars:
|
||||
# TCL_BIN_DIR
|
||||
# TCL_SRC_DIR
|
||||
# TCL_LIB_FILE
|
||||
#
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
AC_DEFUN([TEA_LOAD_TCLCONFIG], [
|
||||
@@ -417,32 +416,26 @@ AC_DEFUN([TEA_LOAD_TCLCONFIG], [
|
||||
AC_SUBST(TCL_STUB_LIB_FLAG)
|
||||
AC_SUBST(TCL_STUB_LIB_SPEC)
|
||||
|
||||
case "`uname -s`" in
|
||||
*CYGWIN_*)
|
||||
AC_MSG_CHECKING([for cygwin variant])
|
||||
case ${TCL_EXTRA_CFLAGS} in
|
||||
*-mwin32*|*-mno-cygwin*)
|
||||
TEA_PLATFORM="windows"
|
||||
CFLAGS="$CFLAGS -mwin32"
|
||||
AC_MSG_RESULT([win32])
|
||||
;;
|
||||
*)
|
||||
TEA_PLATFORM="unix"
|
||||
AC_MSG_RESULT([unix])
|
||||
;;
|
||||
esac
|
||||
EXEEXT=".exe"
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
AC_MSG_CHECKING([platform])
|
||||
hold_cc=$CC; CC="$TCL_CC"
|
||||
AC_TRY_COMPILE(,[
|
||||
#ifdef _WIN32
|
||||
#error win32
|
||||
#endif
|
||||
], TEA_PLATFORM="unix",
|
||||
TEA_PLATFORM="windows"
|
||||
)
|
||||
CC=$hold_cc
|
||||
AC_MSG_RESULT($TEA_PLATFORM)
|
||||
|
||||
# The BUILD_$pkg is to define the correct extern storage class
|
||||
# handling when making this package
|
||||
AC_DEFINE_UNQUOTED(BUILD_${PACKAGE_NAME}, [],
|
||||
[Building extension source?])
|
||||
# Do this here as we have fully defined TEA_PLATFORM now
|
||||
if test "${TEA_PLATFORM}" = "windows" ; then
|
||||
# The BUILD_$pkg is to define the correct extern storage class
|
||||
# handling when making this package
|
||||
AC_DEFINE_UNQUOTED(BUILD_${PACKAGE_NAME})
|
||||
CLEANFILES="$CLEANFILES *.lib *.dll *.pdb"
|
||||
EXEEXT=".exe"
|
||||
CLEANFILES="$CLEANFILES *.lib *.dll *.pdb *.exp"
|
||||
fi
|
||||
|
||||
# TEA specific:
|
||||
@@ -566,11 +559,11 @@ AC_DEFUN([TEA_LOAD_TKCONFIG], [
|
||||
# only for running extension test cases. It should never be
|
||||
# or generation of files (like pkgIndex.tcl) at build time.
|
||||
#
|
||||
# Arguments
|
||||
# Arguments:
|
||||
# none
|
||||
#
|
||||
# Results
|
||||
# Subst's the following values:
|
||||
# Results:
|
||||
# Substitutes the following vars:
|
||||
# TCLSH_PROG
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
@@ -616,11 +609,11 @@ AC_DEFUN([TEA_PROG_TCLSH], [
|
||||
# only for running extension test cases. It should never be
|
||||
# or generation of files (like pkgIndex.tcl) at build time.
|
||||
#
|
||||
# Arguments
|
||||
# Arguments:
|
||||
# none
|
||||
#
|
||||
# Results
|
||||
# Subst's the following values:
|
||||
# Results:
|
||||
# Substitutes the following vars:
|
||||
# WISH_PROG
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
@@ -731,7 +724,6 @@ AC_DEFUN([TEA_ENABLE_SHARED], [
|
||||
# TCL_THREADS
|
||||
# _REENTRANT
|
||||
# _THREAD_SAFE
|
||||
#
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
AC_DEFUN([TEA_ENABLE_THREADS], [
|
||||
@@ -855,12 +847,11 @@ AC_DEFUN([TEA_ENABLE_THREADS], [
|
||||
#
|
||||
# Defines the following vars:
|
||||
# CFLAGS_DEFAULT Sets to $(CFLAGS_DEBUG) if true
|
||||
# Sets to $(CFLAGS_OPTIMIZE) if false
|
||||
# Sets to "$(CFLAGS_OPTIMIZE) -DNDEBUG" if false
|
||||
# LDFLAGS_DEFAULT Sets to $(LDFLAGS_DEBUG) if true
|
||||
# Sets to $(LDFLAGS_OPTIMIZE) if false
|
||||
# DBGX Formerly used as debug library extension;
|
||||
# always blank now.
|
||||
#
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
AC_DEFUN([TEA_ENABLE_SYMBOLS], [
|
||||
@@ -873,7 +864,7 @@ AC_DEFUN([TEA_ENABLE_SYMBOLS], [
|
||||
[tcl_ok=$enableval], [tcl_ok=no])
|
||||
DBGX=""
|
||||
if test "$tcl_ok" = "no"; then
|
||||
CFLAGS_DEFAULT="${CFLAGS_OPTIMIZE}"
|
||||
CFLAGS_DEFAULT="${CFLAGS_OPTIMIZE} -DNDEBUG"
|
||||
LDFLAGS_DEFAULT="${LDFLAGS_OPTIMIZE}"
|
||||
AC_MSG_RESULT([no])
|
||||
else
|
||||
@@ -920,7 +911,6 @@ AC_DEFUN([TEA_ENABLE_SYMBOLS], [
|
||||
#
|
||||
# Defines the following vars:
|
||||
# HAVE_LANGINFO Triggers use of nl_langinfo if defined.
|
||||
#
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
AC_DEFUN([TEA_ENABLE_LANGINFO], [
|
||||
@@ -961,7 +951,6 @@ AC_DEFUN([TEA_ENABLE_LANGINFO], [
|
||||
# Defines the following var:
|
||||
#
|
||||
# system - System/platform/version identification code.
|
||||
#
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
AC_DEFUN([TEA_CONFIG_SYSTEM], [
|
||||
@@ -1030,21 +1019,20 @@ AC_DEFUN([TEA_CONFIG_SYSTEM], [
|
||||
# extensions. An empty string means we don't know how
|
||||
# to use shared libraries on this platform.
|
||||
# LIB_SUFFIX - Specifies everything that comes after the "libfoo"
|
||||
# in a static or shared library name, using the $VERSION variable
|
||||
# in a static or shared library name, using the $PACKAGE_VERSION variable
|
||||
# to put the version in the right place. This is used
|
||||
# by platforms that need non-standard library names.
|
||||
# Examples: ${VERSION}.so.1.1 on NetBSD, since it needs
|
||||
# to have a version after the .so, and ${VERSION}.a
|
||||
# Examples: ${PACKAGE_VERSION}.so.1.1 on NetBSD, since it needs
|
||||
# to have a version after the .so, and ${PACKAGE_VERSION}.a
|
||||
# on AIX, since a shared library needs to have
|
||||
# a .a extension whereas shared objects for loadable
|
||||
# extensions have a .so extension. Defaults to
|
||||
# ${VERSION}${SHLIB_SUFFIX}.
|
||||
# ${PACKAGE_VERSION}${SHLIB_SUFFIX}.
|
||||
# CFLAGS_DEBUG -
|
||||
# Flags used when running the compiler in debug mode
|
||||
# CFLAGS_OPTIMIZE -
|
||||
# Flags used when running the compiler in optimize mode
|
||||
# CFLAGS - Additional CFLAGS added as necessary (usually 64-bit)
|
||||
#
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
AC_DEFUN([TEA_CONFIG_CFLAGS], [
|
||||
@@ -1086,6 +1074,7 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
|
||||
AC_DEFINE(MODULE_SCOPE,
|
||||
[extern __attribute__((__visibility__("hidden")))],
|
||||
[Compiler support for module scope symbols])
|
||||
AC_DEFINE(HAVE_HIDDEN, [1], [Compiler support for module scope symbols])
|
||||
])
|
||||
|
||||
# Step 0.d: Disable -rpath support?
|
||||
@@ -1134,15 +1123,14 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
|
||||
ECHO_VERSION='`echo ${PACKAGE_VERSION}`'
|
||||
TCL_LIB_VERSIONS_OK=ok
|
||||
CFLAGS_DEBUG=-g
|
||||
CFLAGS_OPTIMIZE=-O
|
||||
AS_IF([test "$GCC" = yes], [
|
||||
# TEA specific:
|
||||
CFLAGS_OPTIMIZE=-O2
|
||||
CFLAGS_WARNING="-Wall"
|
||||
], [CFLAGS_WARNING=""])
|
||||
dnl FIXME: Replace AC_CHECK_PROG with AC_CHECK_TOOL once cross compiling is fixed.
|
||||
dnl AC_CHECK_TOOL(AR, ar)
|
||||
AC_CHECK_PROG(AR, ar, ar)
|
||||
], [
|
||||
CFLAGS_OPTIMIZE=-O
|
||||
CFLAGS_WARNING=""
|
||||
])
|
||||
AC_CHECK_TOOL(AR, ar)
|
||||
STLIB_LD='${AR} cr'
|
||||
LD_LIBRARY_PATH_VAR="LD_LIBRARY_PATH"
|
||||
AS_IF([test "x$SHLIB_VERSION" = x],[SHLIB_VERSION="1.0"])
|
||||
@@ -1171,7 +1159,7 @@ dnl AC_CHECK_TOOL(AR, ar)
|
||||
PATH64="${MSSDK}/Bin/Win64"
|
||||
;;
|
||||
esac
|
||||
if test ! -d "${PATH64}" ; then
|
||||
if test "$GCC" != "yes" -a ! -d "${PATH64}" ; then
|
||||
AC_MSG_WARN([Could not find 64-bit $MACHINE SDK to enable 64bit mode])
|
||||
AC_MSG_WARN([Ensure latest Platform SDK is installed])
|
||||
do64bit="no"
|
||||
@@ -1288,7 +1276,7 @@ dnl AC_CHECK_TOOL(AR, ar)
|
||||
else
|
||||
RC="rc"
|
||||
lflags="-nologo"
|
||||
LINKBIN="link"
|
||||
LINKBIN="link"
|
||||
CFLAGS_DEBUG="-nologo -Z7 -Od -W3 -WX ${runtime}d"
|
||||
CFLAGS_OPTIMIZE="-nologo -O2 -W2 ${runtime}"
|
||||
fi
|
||||
@@ -1296,13 +1284,43 @@ dnl AC_CHECK_TOOL(AR, ar)
|
||||
|
||||
if test "$GCC" = "yes"; then
|
||||
# mingw gcc mode
|
||||
RC="windres"
|
||||
AC_CHECK_TOOL(RC, windres)
|
||||
CFLAGS_DEBUG="-g"
|
||||
CFLAGS_OPTIMIZE="-O2 -fomit-frame-pointer"
|
||||
SHLIB_LD="$CC -shared"
|
||||
SHLIB_LD='${CC} -shared'
|
||||
UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a'
|
||||
LDFLAGS_CONSOLE="-wl,--subsystem,console ${lflags}"
|
||||
LDFLAGS_WINDOW="-wl,--subsystem,windows ${lflags}"
|
||||
|
||||
AC_CACHE_CHECK(for cross-compile version of gcc,
|
||||
ac_cv_cross,
|
||||
AC_TRY_COMPILE([
|
||||
#ifdef _WIN32
|
||||
#error cross-compiler
|
||||
#endif
|
||||
], [],
|
||||
ac_cv_cross=yes,
|
||||
ac_cv_cross=no)
|
||||
)
|
||||
if test "$ac_cv_cross" = "yes"; then
|
||||
case "$do64bit" in
|
||||
amd64|x64|yes)
|
||||
CC="x86_64-w64-mingw32-gcc"
|
||||
LD="x86_64-w64-mingw32-ld"
|
||||
AR="x86_64-w64-mingw32-ar"
|
||||
RANLIB="x86_64-w64-mingw32-ranlib"
|
||||
RC="x86_64-w64-mingw32-windres"
|
||||
;;
|
||||
*)
|
||||
CC="i686-w64-mingw32-gcc"
|
||||
LD="i686-w64-mingw32-ld"
|
||||
AR="i686-w64-mingw32-ar"
|
||||
RANLIB="i686-w64-mingw32-ranlib"
|
||||
RC="i686-w64-mingw32-windres"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
else
|
||||
SHLIB_LD="${LINKBIN} -dll ${lflags}"
|
||||
# link -lib only works when -lib is the first arg
|
||||
@@ -1409,7 +1427,8 @@ dnl AC_CHECK_TOOL(AR, ar)
|
||||
SHLIB_CFLAGS=""
|
||||
SHLIB_LD='${CC} -shared'
|
||||
SHLIB_SUFFIX=".dll"
|
||||
EXE_SUFFIX=".exe"
|
||||
EXEEXT=".exe"
|
||||
do64bit_ok=yes
|
||||
CC_SEARCH_FLAGS=""
|
||||
LD_SEARCH_FLAGS=""
|
||||
;;
|
||||
@@ -1438,7 +1457,7 @@ dnl AC_CHECK_TOOL(AR, ar)
|
||||
])
|
||||
AC_CHECK_LIB(dld, shl_load, tcl_ok=yes, tcl_ok=no)
|
||||
AS_IF([test "$tcl_ok" = yes], [
|
||||
LDFLAGS="$LDFLAGS -E"
|
||||
LDFLAGS="$LDFLAGS -Wl,-E"
|
||||
CC_SEARCH_FLAGS='-Wl,+s,+b,${LIB_RUNTIME_DIR}:.'
|
||||
LD_SEARCH_FLAGS='+s +b ${LIB_RUNTIME_DIR}:.'
|
||||
LD_LIBRARY_PATH_VAR="SHLIB_PATH"
|
||||
@@ -1520,7 +1539,7 @@ dnl AC_CHECK_TOOL(AR, ar)
|
||||
])
|
||||
])
|
||||
;;
|
||||
Linux*)
|
||||
Linux*|GNU*|NetBSD-Debian)
|
||||
SHLIB_CFLAGS="-fPIC"
|
||||
SHLIB_SUFFIX=".so"
|
||||
|
||||
@@ -1553,17 +1572,6 @@ dnl AC_CHECK_TOOL(AR, ar)
|
||||
# files in compat/*.c is being linked in.
|
||||
|
||||
AS_IF([test x"${USE_COMPAT}" != x],[CFLAGS="$CFLAGS -fno-inline"])
|
||||
|
||||
;;
|
||||
GNU*)
|
||||
SHLIB_CFLAGS="-fPIC"
|
||||
SHLIB_SUFFIX=".so"
|
||||
|
||||
SHLIB_LD='${CC} -shared'
|
||||
LDFLAGS="$LDFLAGS -Wl,--export-dynamic"
|
||||
CC_SEARCH_FLAGS=""
|
||||
LD_SEARCH_FLAGS=""
|
||||
AS_IF([test "`uname -m`" = "alpha"], [CFLAGS="$CFLAGS -mieee"])
|
||||
;;
|
||||
Lynx*)
|
||||
SHLIB_CFLAGS="-fPIC"
|
||||
@@ -1576,35 +1584,44 @@ dnl AC_CHECK_TOOL(AR, ar)
|
||||
LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'])
|
||||
;;
|
||||
OpenBSD-*)
|
||||
SHLIB_CFLAGS="-fPIC"
|
||||
SHLIB_LD='${CC} -shared ${SHLIB_CFLAGS}'
|
||||
SHLIB_SUFFIX=".so"
|
||||
AS_IF([test $doRpath = yes], [
|
||||
CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'])
|
||||
LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
|
||||
SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so.${SHLIB_VERSION}'
|
||||
AC_CACHE_CHECK([for ELF], tcl_cv_ld_elf, [
|
||||
AC_EGREP_CPP(yes, [
|
||||
#ifdef __ELF__
|
||||
yes
|
||||
#endif
|
||||
], tcl_cv_ld_elf=yes, tcl_cv_ld_elf=no)])
|
||||
AS_IF([test $tcl_cv_ld_elf = yes], [
|
||||
LDFLAGS=-Wl,-export-dynamic
|
||||
], [LDFLAGS=""])
|
||||
arch=`arch -s`
|
||||
case "$arch" in
|
||||
vax)
|
||||
SHLIB_SUFFIX=""
|
||||
SHARED_LIB_SUFFIX=""
|
||||
LDFLAGS=""
|
||||
;;
|
||||
*)
|
||||
SHLIB_CFLAGS="-fPIC"
|
||||
SHLIB_LD='${CC} -shared ${SHLIB_CFLAGS}'
|
||||
SHLIB_SUFFIX=".so"
|
||||
AS_IF([test $doRpath = yes], [
|
||||
CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'])
|
||||
LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
|
||||
SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so.${SHLIB_VERSION}'
|
||||
LDFLAGS="-Wl,-export-dynamic"
|
||||
;;
|
||||
esac
|
||||
case "$arch" in
|
||||
vax)
|
||||
CFLAGS_OPTIMIZE="-O1"
|
||||
;;
|
||||
*)
|
||||
CFLAGS_OPTIMIZE="-O2"
|
||||
;;
|
||||
esac
|
||||
AS_IF([test "${TCL_THREADS}" = "1"], [
|
||||
# OpenBSD builds and links with -pthread, never -lpthread.
|
||||
# On OpenBSD: Compile with -pthread
|
||||
# Don't link with -lpthread
|
||||
LIBS=`echo $LIBS | sed s/-lpthread//`
|
||||
CFLAGS="$CFLAGS -pthread"
|
||||
SHLIB_CFLAGS="$SHLIB_CFLAGS -pthread"
|
||||
])
|
||||
# OpenBSD doesn't do version numbers with dots.
|
||||
UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a'
|
||||
TCL_LIB_VERSIONS_OK=nodots
|
||||
;;
|
||||
NetBSD-*|FreeBSD-[[3-4]].*)
|
||||
# FreeBSD 3.* and greater have ELF.
|
||||
# NetBSD 2.* has ELF and can use 'cc -shared' to build shared libs
|
||||
NetBSD-*)
|
||||
# NetBSD has ELF and can use 'cc -shared' to build shared libs
|
||||
SHLIB_CFLAGS="-fPIC"
|
||||
SHLIB_LD='${CC} -shared ${SHLIB_CFLAGS}'
|
||||
SHLIB_SUFFIX=".so"
|
||||
@@ -1618,32 +1635,24 @@ dnl AC_CHECK_TOOL(AR, ar)
|
||||
CFLAGS="$CFLAGS -pthread"
|
||||
LDFLAGS="$LDFLAGS -pthread"
|
||||
])
|
||||
case $system in
|
||||
FreeBSD-3.*)
|
||||
# FreeBSD-3 doesn't handle version numbers with dots.
|
||||
UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a'
|
||||
SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so'
|
||||
TCL_LIB_VERSIONS_OK=nodots
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
FreeBSD-*)
|
||||
# This configuration from FreeBSD Ports.
|
||||
SHLIB_CFLAGS="-fPIC"
|
||||
SHLIB_LD="${CC} -shared"
|
||||
TCL_SHLIB_LD_EXTRAS="-soname \$[@]"
|
||||
TCL_SHLIB_LD_EXTRAS="-Wl,-soname=\$[@]"
|
||||
SHLIB_SUFFIX=".so"
|
||||
LDFLAGS=""
|
||||
AS_IF([test $doRpath = yes], [
|
||||
CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
|
||||
LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}'])
|
||||
LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'])
|
||||
AS_IF([test "${TCL_THREADS}" = "1"], [
|
||||
# The -pthread needs to go in the LDFLAGS, not LIBS
|
||||
LIBS=`echo $LIBS | sed s/-pthread//`
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
LDFLAGS="$LDFLAGS $PTHREAD_LIBS"])
|
||||
# Version numbers are dot-stripped by system policy.
|
||||
TCL_TRIM_DOTS=`echo ${VERSION} | tr -d .`
|
||||
TCL_TRIM_DOTS=`echo ${PACKAGE_VERSION} | tr -d .`
|
||||
UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a'
|
||||
SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}\$\{DBGX\}.so.1'
|
||||
TCL_LIB_VERSIONS_OK=nodots
|
||||
@@ -1705,7 +1714,7 @@ dnl AC_CHECK_TOOL(AR, ar)
|
||||
AS_IF([test $tcl_cv_ld_single_module = yes], [
|
||||
SHLIB_LD="${SHLIB_LD} -Wl,-single_module"
|
||||
])
|
||||
# TEA specific: link shlib with current and compatiblity version flags
|
||||
# TEA specific: link shlib with current and compatibility version flags
|
||||
vers=`echo ${PACKAGE_VERSION} | sed -e 's/^\([[0-9]]\{1,5\}\)\(\(\.[[0-9]]\{1,3\}\)\{0,2\}\).*$/\1\2/p' -e d`
|
||||
SHLIB_LD="${SHLIB_LD} -current_version ${vers:-0} -compatibility_version ${vers:-0}"
|
||||
SHLIB_SUFFIX=".dylib"
|
||||
@@ -1941,6 +1950,24 @@ dnl AC_CHECK_TOOL(AR, ar)
|
||||
LD_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}'
|
||||
])
|
||||
;;
|
||||
UNIX_SV* | UnixWare-5*)
|
||||
SHLIB_CFLAGS="-KPIC"
|
||||
SHLIB_LD='${CC} -G'
|
||||
SHLIB_LD_LIBS=""
|
||||
SHLIB_SUFFIX=".so"
|
||||
# Some UNIX_SV* systems (unixware 1.1.2 for example) have linkers
|
||||
# that don't grok the -Bexport option. Test that it does.
|
||||
AC_CACHE_CHECK([for ld accepts -Bexport flag], tcl_cv_ld_Bexport, [
|
||||
hold_ldflags=$LDFLAGS
|
||||
LDFLAGS="$LDFLAGS -Wl,-Bexport"
|
||||
AC_TRY_LINK(, [int i;], tcl_cv_ld_Bexport=yes, tcl_cv_ld_Bexport=no)
|
||||
LDFLAGS=$hold_ldflags])
|
||||
AS_IF([test $tcl_cv_ld_Bexport = yes], [
|
||||
LDFLAGS="$LDFLAGS -Wl,-Bexport"
|
||||
])
|
||||
CC_SEARCH_FLAGS=""
|
||||
LD_SEARCH_FLAGS=""
|
||||
;;
|
||||
esac
|
||||
|
||||
AS_IF([test "$do64bit" = yes -a "$do64bit_ok" = no], [
|
||||
@@ -1965,7 +1992,7 @@ dnl # preprocessing tests use only CPPFLAGS.
|
||||
case $system in
|
||||
AIX-*) ;;
|
||||
BSD/OS*) ;;
|
||||
CYGWIN_*) ;;
|
||||
CYGWIN_*|MINGW32_*) ;;
|
||||
IRIX*) ;;
|
||||
NetBSD-*|FreeBSD-*|OpenBSD-*) ;;
|
||||
Darwin-*) ;;
|
||||
@@ -1977,15 +2004,109 @@ dnl # preprocessing tests use only CPPFLAGS.
|
||||
AS_IF([test "$tcl_cv_cc_visibility_hidden" != yes], [
|
||||
AC_DEFINE(MODULE_SCOPE, [extern],
|
||||
[No Compiler support for module scope symbols])
|
||||
AC_DEFINE(NO_VIZ)
|
||||
])
|
||||
|
||||
AS_IF([test "$SHARED_LIB_SUFFIX" = ""], [
|
||||
# TEA specific: use PACKAGE_VERSION instead of VERSION
|
||||
SHARED_LIB_SUFFIX='${PACKAGE_VERSION}${SHLIB_SUFFIX}'])
|
||||
# TEA specific: use PACKAGE_VERSION instead of VERSION
|
||||
SHARED_LIB_SUFFIX='${PACKAGE_VERSION}${SHLIB_SUFFIX}'])
|
||||
AS_IF([test "$UNSHARED_LIB_SUFFIX" = ""], [
|
||||
# TEA specific: use PACKAGE_VERSION instead of VERSION
|
||||
UNSHARED_LIB_SUFFIX='${PACKAGE_VERSION}.a'])
|
||||
# TEA specific: use PACKAGE_VERSION instead of VERSION
|
||||
UNSHARED_LIB_SUFFIX='${PACKAGE_VERSION}.a'])
|
||||
|
||||
if test "${GCC}" = "yes" -a ${SHLIB_SUFFIX} = ".dll"; then
|
||||
AC_CACHE_CHECK(for SEH support in compiler,
|
||||
tcl_cv_seh,
|
||||
AC_TRY_RUN([
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int a, b = 0;
|
||||
__try {
|
||||
a = 666 / b;
|
||||
}
|
||||
__except (EXCEPTION_EXECUTE_HANDLER) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
],
|
||||
tcl_cv_seh=yes,
|
||||
tcl_cv_seh=no,
|
||||
tcl_cv_seh=no)
|
||||
)
|
||||
if test "$tcl_cv_seh" = "no" ; then
|
||||
AC_DEFINE(HAVE_NO_SEH, 1,
|
||||
[Defined when mingw does not support SEH])
|
||||
fi
|
||||
|
||||
#
|
||||
# Check to see if the excpt.h include file provided contains the
|
||||
# definition for EXCEPTION_DISPOSITION; if not, which is the case
|
||||
# with Cygwin's version as of 2002-04-10, define it to be int,
|
||||
# sufficient for getting the current code to work.
|
||||
#
|
||||
AC_CACHE_CHECK(for EXCEPTION_DISPOSITION support in include files,
|
||||
tcl_cv_eh_disposition,
|
||||
AC_TRY_COMPILE([
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# include <windows.h>
|
||||
# undef WIN32_LEAN_AND_MEAN
|
||||
],[
|
||||
EXCEPTION_DISPOSITION x;
|
||||
],
|
||||
tcl_cv_eh_disposition=yes,
|
||||
tcl_cv_eh_disposition=no)
|
||||
)
|
||||
if test "$tcl_cv_eh_disposition" = "no" ; then
|
||||
AC_DEFINE(EXCEPTION_DISPOSITION, int,
|
||||
[Defined when cygwin/mingw does not support EXCEPTION DISPOSITION])
|
||||
fi
|
||||
|
||||
# Check to see if winnt.h defines CHAR, SHORT, and LONG
|
||||
# even if VOID has already been #defined. The win32api
|
||||
# used by mingw and cygwin is known to do this.
|
||||
|
||||
AC_CACHE_CHECK(for winnt.h that ignores VOID define,
|
||||
tcl_cv_winnt_ignore_void,
|
||||
AC_TRY_COMPILE([
|
||||
#define VOID void
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
], [
|
||||
CHAR c;
|
||||
SHORT s;
|
||||
LONG l;
|
||||
],
|
||||
tcl_cv_winnt_ignore_void=yes,
|
||||
tcl_cv_winnt_ignore_void=no)
|
||||
)
|
||||
if test "$tcl_cv_winnt_ignore_void" = "yes" ; then
|
||||
AC_DEFINE(HAVE_WINNT_IGNORE_VOID, 1,
|
||||
[Defined when cygwin/mingw ignores VOID define in winnt.h])
|
||||
fi
|
||||
fi
|
||||
|
||||
# See if the compiler supports casting to a union type.
|
||||
# This is used to stop gcc from printing a compiler
|
||||
# warning when initializing a union member.
|
||||
|
||||
AC_CACHE_CHECK(for cast to union support,
|
||||
tcl_cv_cast_to_union,
|
||||
AC_TRY_COMPILE([],
|
||||
[
|
||||
union foo { int i; double d; };
|
||||
union foo f = (union foo) (int) 0;
|
||||
],
|
||||
tcl_cv_cast_to_union=yes,
|
||||
tcl_cv_cast_to_union=no)
|
||||
)
|
||||
if test "$tcl_cv_cast_to_union" = "yes"; then
|
||||
AC_DEFINE(HAVE_CAST_TO_UNION, 1,
|
||||
[Defined when compiler supports casting to union type.])
|
||||
fi
|
||||
|
||||
AC_SUBST(CFLAGS_DEBUG)
|
||||
AC_SUBST(CFLAGS_OPTIMIZE)
|
||||
@@ -2024,7 +2145,6 @@ dnl # preprocessing tests use only CPPFLAGS.
|
||||
# USE_TERMIOS
|
||||
# USE_TERMIO
|
||||
# USE_SGTTY
|
||||
#
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
AC_DEFUN([TEA_SERIAL_PORT], [
|
||||
@@ -2236,7 +2356,6 @@ closedir(d);
|
||||
# XINCLUDES
|
||||
# XLIBSW
|
||||
# PKG_LIBS (appends to)
|
||||
#
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
AC_DEFUN([TEA_PATH_X], [
|
||||
@@ -2250,9 +2369,9 @@ AC_DEFUN([TEA_PATH_UNIX_X], [
|
||||
not_really_there=""
|
||||
if test "$no_x" = ""; then
|
||||
if test "$x_includes" = ""; then
|
||||
AC_TRY_CPP([#include <X11/XIntrinsic.h>], , not_really_there="yes")
|
||||
AC_TRY_CPP([#include <X11/Xlib.h>], , not_really_there="yes")
|
||||
else
|
||||
if test ! -r $x_includes/X11/Intrinsic.h; then
|
||||
if test ! -r $x_includes/X11/Xlib.h; then
|
||||
not_really_there="yes"
|
||||
fi
|
||||
fi
|
||||
@@ -2260,11 +2379,11 @@ AC_DEFUN([TEA_PATH_UNIX_X], [
|
||||
if test "$no_x" = "yes" -o "$not_really_there" = "yes"; then
|
||||
AC_MSG_CHECKING([for X11 header files])
|
||||
found_xincludes="no"
|
||||
AC_TRY_CPP([#include <X11/Intrinsic.h>], found_xincludes="yes", found_xincludes="no")
|
||||
AC_TRY_CPP([#include <X11/Xlib.h>], found_xincludes="yes", found_xincludes="no")
|
||||
if test "$found_xincludes" = "no"; then
|
||||
dirs="/usr/unsupported/include /usr/local/include /usr/X386/include /usr/X11R6/include /usr/X11R5/include /usr/include/X11R5 /usr/include/X11R4 /usr/openwin/include /usr/X11/include /usr/sww/include"
|
||||
for i in $dirs ; do
|
||||
if test -r $i/X11/Intrinsic.h; then
|
||||
if test -r $i/X11/Xlib.h; then
|
||||
AC_MSG_RESULT([$i])
|
||||
XINCLUDES=" -I$i"
|
||||
found_xincludes="yes"
|
||||
@@ -2332,7 +2451,6 @@ AC_DEFUN([TEA_PATH_UNIX_X], [
|
||||
# HAVE_SYS_FILIO_H
|
||||
# USE_FIONBIO
|
||||
# O_NONBLOCK
|
||||
#
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
AC_DEFUN([TEA_BLOCKING_STYLE], [
|
||||
@@ -2367,7 +2485,6 @@ AC_DEFUN([TEA_BLOCKING_STYLE], [
|
||||
# HAVE_TM_GMTOFF
|
||||
# HAVE_TM_TZADJ
|
||||
# HAVE_TIMEZONE_VAR
|
||||
#
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
AC_DEFUN([TEA_TIME_HANDLER], [
|
||||
@@ -2436,7 +2553,6 @@ AC_DEFUN([TEA_TIME_HANDLER], [
|
||||
#
|
||||
# Might defines some of the following vars:
|
||||
# strtod (=fixstrtod)
|
||||
#
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
AC_DEFUN([TEA_BUGGY_STRTOD], [
|
||||
@@ -2487,7 +2603,7 @@ AC_DEFUN([TEA_BUGGY_STRTOD], [
|
||||
#
|
||||
# Results:
|
||||
#
|
||||
# Subst's the following var:
|
||||
# Substitutes the following vars:
|
||||
# TCL_LIBS
|
||||
# MATH_LIBS
|
||||
#
|
||||
@@ -2496,7 +2612,6 @@ AC_DEFUN([TEA_BUGGY_STRTOD], [
|
||||
#
|
||||
# Might define the following vars:
|
||||
# HAVE_NET_ERRNO_H
|
||||
#
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
AC_DEFUN([TEA_TCL_LINK_LIBS], [
|
||||
@@ -2574,7 +2689,6 @@ AC_DEFUN([TEA_TCL_LINK_LIBS], [
|
||||
# _ISOC99_SOURCE
|
||||
# _LARGEFILE64_SOURCE
|
||||
# _LARGEFILE_SOURCE64
|
||||
#
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
AC_DEFUN([TEA_TCL_EARLY_FLAG],[
|
||||
@@ -2622,7 +2736,6 @@ AC_DEFUN([TEA_TCL_EARLY_FLAGS],[
|
||||
# HAVE_STRUCT_DIRENT64
|
||||
# HAVE_STRUCT_STAT64
|
||||
# HAVE_TYPE_OFF64_T
|
||||
#
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
AC_DEFUN([TEA_TCL_64BIT_FLAGS], [
|
||||
@@ -2654,7 +2767,7 @@ AC_DEFUN([TEA_TCL_64BIT_FLAGS], [
|
||||
# Now check for auxiliary declarations
|
||||
AC_CACHE_CHECK([for struct dirent64], tcl_cv_struct_dirent64,[
|
||||
AC_TRY_COMPILE([#include <sys/types.h>
|
||||
#include <sys/dirent.h>],[struct dirent64 p;],
|
||||
#include <dirent.h>],[struct dirent64 p;],
|
||||
tcl_cv_struct_dirent64=yes,tcl_cv_struct_dirent64=no)])
|
||||
if test "x${tcl_cv_struct_dirent64}" = "xyes" ; then
|
||||
AC_DEFINE(HAVE_STRUCT_DIRENT64, 1, [Is 'struct dirent64' in <sys/types.h>?])
|
||||
@@ -2739,6 +2852,13 @@ TEA version not specified.])
|
||||
else
|
||||
AC_MSG_RESULT([ok (TEA ${TEA_VERSION})])
|
||||
fi
|
||||
|
||||
# If the user did not set CFLAGS, set it now to keep macros
|
||||
# like AC_PROG_CC and AC_TRY_COMPILE from adding "-g -O2".
|
||||
if test "${CFLAGS+set}" != "set" ; then
|
||||
CFLAGS=""
|
||||
fi
|
||||
|
||||
case "`uname -s`" in
|
||||
*win32*|*WIN32*|*MINGW32_*)
|
||||
AC_CHECK_PROG(CYGPATH, cygpath, cygpath -w, echo)
|
||||
@@ -2752,8 +2872,17 @@ TEA version not specified.])
|
||||
;;
|
||||
*)
|
||||
CYGPATH=echo
|
||||
EXEEXT=""
|
||||
TEA_PLATFORM="unix"
|
||||
# Maybe we are cross-compiling....
|
||||
case ${host_alias} in
|
||||
*mingw32*)
|
||||
EXEEXT=".exe"
|
||||
TEA_PLATFORM="windows"
|
||||
;;
|
||||
*)
|
||||
EXEEXT=""
|
||||
TEA_PLATFORM="unix"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -2766,6 +2895,8 @@ TEA version not specified.])
|
||||
exec_prefix=$prefix
|
||||
fi
|
||||
|
||||
AC_MSG_NOTICE([configuring ${PACKAGE_NAME} ${PACKAGE_VERSION}])
|
||||
|
||||
AC_SUBST(EXEEXT)
|
||||
AC_SUBST(CYGPATH)
|
||||
|
||||
@@ -3001,6 +3132,22 @@ AC_DEFUN([TEA_ADD_CFLAGS], [
|
||||
AC_SUBST(PKG_CFLAGS)
|
||||
])
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
# TEA_ADD_CLEANFILES --
|
||||
#
|
||||
# Specify one or more CLEANFILES.
|
||||
#
|
||||
# Arguments:
|
||||
# one or more file names to clean target
|
||||
#
|
||||
# Results:
|
||||
#
|
||||
# Appends to CLEANFILES, already defined for subst in LOAD_TCLCONFIG
|
||||
#------------------------------------------------------------------------
|
||||
AC_DEFUN([TEA_ADD_CLEANFILES], [
|
||||
CLEANFILES="$CLEANFILES $@"
|
||||
])
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
# TEA_PREFIX --
|
||||
#
|
||||
@@ -3055,16 +3202,17 @@ AC_DEFUN([TEA_SETUP_COMPILER_CC], [
|
||||
# Don't put any macros that use the compiler (e.g. AC_TRY_COMPILE)
|
||||
# in this macro, they need to go into TEA_SETUP_COMPILER instead.
|
||||
|
||||
# If the user did not set CFLAGS, set it now to keep
|
||||
# the AC_PROG_CC macro from adding "-g -O2".
|
||||
if test "${CFLAGS+set}" != "set" ; then
|
||||
CFLAGS=""
|
||||
fi
|
||||
|
||||
AC_PROG_CC
|
||||
AC_PROG_CPP
|
||||
|
||||
AC_PROG_INSTALL
|
||||
INSTALL="\$(SHELL) \$(srcdir)/tclconfig/install-sh -c"
|
||||
AC_SUBST(INSTALL)
|
||||
INSTALL_DATA="\${INSTALL} -m 644"
|
||||
AC_SUBST(INSTALL_DATA)
|
||||
INSTALL_PROGRAM="\${INSTALL}"
|
||||
AC_SUBST(INSTALL_PROGRAM)
|
||||
INSTALL_SCRIPT="\${INSTALL}"
|
||||
AC_SUBST(INSTALL_SCRIPT)
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Checks to see if the make program sets the $MAKE variable.
|
||||
@@ -3076,7 +3224,7 @@ AC_DEFUN([TEA_SETUP_COMPILER_CC], [
|
||||
# Find ranlib
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
AC_PROG_RANLIB
|
||||
AC_CHECK_TOOL(RANLIB, ranlib)
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Determines the correct binary file extension (.o, .obj, .exe etc.)
|
||||
@@ -3155,13 +3303,26 @@ AC_DEFUN([TEA_SETUP_COMPILER], [
|
||||
# MAKE_SHARED_LIB Makefile rule for building a shared library
|
||||
# MAKE_STATIC_LIB Makefile rule for building a static library
|
||||
# MAKE_STUB_LIB Makefile rule for building a stub library
|
||||
# VC_MANIFEST_EMBED_DLL Makefile rule for embedded VC manifest in DLL
|
||||
# VC_MANIFEST_EMBED_EXE Makefile rule for embedded VC manifest in EXE
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
AC_DEFUN([TEA_MAKE_LIB], [
|
||||
if test "${TEA_PLATFORM}" = "windows" -a "$GCC" != "yes"; then
|
||||
MAKE_STATIC_LIB="\${STLIB_LD} -out:\[$]@ \$(PKG_OBJECTS)"
|
||||
MAKE_SHARED_LIB="\${SHLIB_LD} \${SHLIB_LD_LIBS} \${LDFLAGS_DEFAULT} -out:\[$]@ \$(PKG_OBJECTS)"
|
||||
MAKE_STUB_LIB="\${STLIB_LD} -out:\[$]@ \$(PKG_STUB_OBJECTS)"
|
||||
AC_EGREP_CPP([manifest needed], [
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
||||
print("manifest needed")
|
||||
#endif
|
||||
], [
|
||||
# Could do a CHECK_PROG for mt, but should always be with MSVC8+
|
||||
VC_MANIFEST_EMBED_DLL="if test -f \[$]@.manifest ; then mt.exe -nologo -manifest \[$]@.manifest -outputresource:\[$]@\;2 ; fi"
|
||||
VC_MANIFEST_EMBED_EXE="if test -f \[$]@.manifest ; then mt.exe -nologo -manifest \[$]@.manifest -outputresource:\[$]@\;1 ; fi"
|
||||
MAKE_SHARED_LIB="${MAKE_SHARED_LIB} ; ${VC_MANIFEST_EMBED_DLL}"
|
||||
TEA_ADD_CLEANFILES([*.manifest])
|
||||
])
|
||||
MAKE_STUB_LIB="\${STLIB_LD} -nodefaultlib -out:\[$]@ \$(PKG_STUB_OBJECTS)"
|
||||
else
|
||||
MAKE_STATIC_LIB="\${STLIB_LD} \[$]@ \$(PKG_OBJECTS)"
|
||||
MAKE_SHARED_LIB="\${SHLIB_LD} -o \[$]@ \$(PKG_OBJECTS) \${SHLIB_LD_LIBS}"
|
||||
@@ -3184,13 +3345,19 @@ AC_DEFUN([TEA_MAKE_LIB], [
|
||||
if test "${SHARED_BUILD}" = "1" ; then
|
||||
# We force the unresolved linking of symbols that are really in
|
||||
# the private libraries of Tcl and Tk.
|
||||
SHLIB_LD_LIBS="${SHLIB_LD_LIBS} \"`${CYGPATH} ${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}`\""
|
||||
if test x"${TK_BIN_DIR}" != x ; then
|
||||
SHLIB_LD_LIBS="${SHLIB_LD_LIBS} \"`${CYGPATH} ${TK_BIN_DIR}/${TK_STUB_LIB_FILE}`\""
|
||||
fi
|
||||
SHLIB_LD_LIBS="${SHLIB_LD_LIBS} \"`${CYGPATH} ${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}`\""
|
||||
if test "$GCC" = "yes"; then
|
||||
SHLIB_LD_LIBS="${SHLIB_LD_LIBS} -static-libgcc"
|
||||
fi
|
||||
eval eval "PKG_LIB_FILE=${PACKAGE_NAME}${SHARED_LIB_SUFFIX}"
|
||||
else
|
||||
eval eval "PKG_LIB_FILE=${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}"
|
||||
if test "$GCC" = "yes"; then
|
||||
PKG_LIB_FILE=lib${PKG_LIB_FILE}
|
||||
fi
|
||||
fi
|
||||
# Some packages build their own stubs libraries
|
||||
eval eval "PKG_STUB_LIB_FILE=${PACKAGE_NAME}stub${UNSHARED_LIB_SUFFIX}"
|
||||
@@ -3228,6 +3395,8 @@ AC_DEFUN([TEA_MAKE_LIB], [
|
||||
AC_SUBST(MAKE_STATIC_LIB)
|
||||
AC_SUBST(MAKE_STUB_LIB)
|
||||
AC_SUBST(RANLIB_STUB)
|
||||
AC_SUBST(VC_MANIFEST_EMBED_DLL)
|
||||
AC_SUBST(VC_MANIFEST_EMBED_EXE)
|
||||
])
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
@@ -3316,7 +3485,7 @@ AC_DEFUN([TEA_LIB_SPEC], [
|
||||
#
|
||||
# Results:
|
||||
#
|
||||
# Substs the following vars:
|
||||
# Substitutes the following vars:
|
||||
# TCL_TOP_DIR_NATIVE
|
||||
# TCL_INCLUDES
|
||||
#------------------------------------------------------------------------
|
||||
@@ -3394,7 +3563,7 @@ AC_DEFUN([TEA_PRIVATE_TCL_HEADERS], [
|
||||
# Adds a --with-tclinclude switch to configure.
|
||||
# Result is cached.
|
||||
#
|
||||
# Substs the following vars:
|
||||
# Substitutes the following vars:
|
||||
# TCL_INCLUDES
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
@@ -3484,7 +3653,7 @@ AC_DEFUN([TEA_PUBLIC_TCL_HEADERS], [
|
||||
#
|
||||
# Results:
|
||||
#
|
||||
# Substs the following vars:
|
||||
# Substitutes the following vars:
|
||||
# TK_INCLUDES
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
@@ -3573,7 +3742,7 @@ AC_DEFUN([TEA_PRIVATE_TK_HEADERS], [
|
||||
# Adds a --with-tkinclude switch to configure.
|
||||
# Result is cached.
|
||||
#
|
||||
# Substs the following vars:
|
||||
# Substitutes the following vars:
|
||||
# TK_INCLUDES
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
@@ -3791,11 +3960,10 @@ AC_DEFUN([TEA_PATH_CONFIG], [
|
||||
#
|
||||
# Results:
|
||||
#
|
||||
# Subst the following vars:
|
||||
# Substitutes the following vars:
|
||||
# $1_SRC_DIR
|
||||
# $1_LIB_FILE
|
||||
# $1_LIB_SPEC
|
||||
#
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
AC_DEFUN([TEA_LOAD_CONFIG], [
|
||||
@@ -3822,6 +3990,8 @@ AC_DEFUN([TEA_LOAD_CONFIG], [
|
||||
$1_LIB_SPEC=${$1_BUILD_LIB_SPEC}
|
||||
$1_STUB_LIB_SPEC=${$1_BUILD_STUB_LIB_SPEC}
|
||||
$1_STUB_LIB_PATH=${$1_BUILD_STUB_LIB_PATH}
|
||||
$1_INCLUDE_SPEC=${$1_BUILD_INCLUDE_SPEC}
|
||||
$1_LIBRARY_PATH=${$1_LIBRARY_PATH}
|
||||
fi
|
||||
|
||||
AC_SUBST($1_VERSION)
|
||||
@@ -3854,7 +4024,6 @@ AC_DEFUN([TEA_LOAD_CONFIG], [
|
||||
#
|
||||
# Results:
|
||||
# Adds to LIBS the appropriate extension library
|
||||
#
|
||||
#------------------------------------------------------------------------
|
||||
AC_DEFUN([TEA_LOAD_CONFIG_LIB], [
|
||||
AC_MSG_CHECKING([For $1 library for LIBS])
|
||||
@@ -3886,11 +4055,10 @@ AC_DEFUN([TEA_LOAD_CONFIG_LIB], [
|
||||
# $1
|
||||
#
|
||||
# Results:
|
||||
# Subst the following vars:
|
||||
#
|
||||
# Substitutes the following vars:
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
AC_DEFUN(TEA_EXPORT_CONFIG, [
|
||||
AC_DEFUN([TEA_EXPORT_CONFIG], [
|
||||
#--------------------------------------------------------------------
|
||||
# These are for $1Config.sh
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.69 for sqlcipher 3.8.0.2.
|
||||
# Generated by GNU Autoconf 2.69 for sqlcipher 3.8.4.3.
|
||||
#
|
||||
#
|
||||
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
|
||||
@@ -726,8 +726,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='sqlcipher'
|
||||
PACKAGE_TARNAME='sqlcipher'
|
||||
PACKAGE_VERSION='3.8.0.2'
|
||||
PACKAGE_STRING='sqlcipher 3.8.0.2'
|
||||
PACKAGE_VERSION='3.8.4.3'
|
||||
PACKAGE_STRING='sqlcipher 3.8.4.3'
|
||||
PACKAGE_BUGREPORT=''
|
||||
PACKAGE_URL=''
|
||||
|
||||
@@ -789,7 +789,6 @@ TCL_SRC_DIR
|
||||
TCL_BIN_DIR
|
||||
TCL_VERSION
|
||||
TARGET_EXEEXT
|
||||
SQLITE_OS_OS2
|
||||
SQLITE_OS_WIN
|
||||
SQLITE_OS_UNIX
|
||||
BUILD_EXEEXT
|
||||
@@ -1458,7 +1457,7 @@ if test "$ac_init_help" = "long"; then
|
||||
# Omit some internal or obsolete options to make the list less imposing.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
\`configure' configures sqlcipher 3.8.0.2 to adapt to many kinds of systems.
|
||||
\`configure' configures sqlcipher 3.8.4.3 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -1523,7 +1522,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of sqlcipher 3.8.0.2:";;
|
||||
short | recursive ) echo "Configuration of sqlcipher 3.8.4.3:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@@ -1641,7 +1640,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
sqlcipher configure 3.8.0.2
|
||||
sqlcipher configure 3.8.4.3
|
||||
generated by GNU Autoconf 2.69
|
||||
|
||||
Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
@@ -2060,7 +2059,7 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by sqlcipher $as_me 3.8.0.2, which was
|
||||
It was created by sqlcipher $as_me 3.8.4.3, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
$ $0 $@
|
||||
@@ -3921,13 +3920,13 @@ if ${lt_cv_nm_interface+:} false; then :
|
||||
else
|
||||
lt_cv_nm_interface="BSD nm"
|
||||
echo "int some_variable = 0;" > conftest.$ac_ext
|
||||
(eval echo "\"\$as_me:3924: $ac_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:3923: $ac_compile\"" >&5)
|
||||
(eval "$ac_compile" 2>conftest.err)
|
||||
cat conftest.err >&5
|
||||
(eval echo "\"\$as_me:3927: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
|
||||
(eval echo "\"\$as_me:3926: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
|
||||
(eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
|
||||
cat conftest.err >&5
|
||||
(eval echo "\"\$as_me:3930: output\"" >&5)
|
||||
(eval echo "\"\$as_me:3929: output\"" >&5)
|
||||
cat conftest.out >&5
|
||||
if $GREP 'External.*some_variable' conftest.out > /dev/null; then
|
||||
lt_cv_nm_interface="MS dumpbin"
|
||||
@@ -5133,7 +5132,7 @@ ia64-*-hpux*)
|
||||
;;
|
||||
*-*-irix6*)
|
||||
# Find out which ABI we are using.
|
||||
echo '#line 5136 "configure"' > conftest.$ac_ext
|
||||
echo '#line 5135 "configure"' > conftest.$ac_ext
|
||||
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
|
||||
(eval $ac_compile) 2>&5
|
||||
ac_status=$?
|
||||
@@ -6658,11 +6657,11 @@ else
|
||||
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
||||
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
||||
-e 's:$: $lt_compiler_flag:'`
|
||||
(eval echo "\"\$as_me:6661: $lt_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:6660: $lt_compile\"" >&5)
|
||||
(eval "$lt_compile" 2>conftest.err)
|
||||
ac_status=$?
|
||||
cat conftest.err >&5
|
||||
echo "$as_me:6665: \$? = $ac_status" >&5
|
||||
echo "$as_me:6664: \$? = $ac_status" >&5
|
||||
if (exit $ac_status) && test -s "$ac_outfile"; then
|
||||
# The compiler can only warn and ignore the option if not recognized
|
||||
# So say no if there are warnings other than the usual output.
|
||||
@@ -6997,11 +6996,11 @@ else
|
||||
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
||||
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
||||
-e 's:$: $lt_compiler_flag:'`
|
||||
(eval echo "\"\$as_me:7000: $lt_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:6999: $lt_compile\"" >&5)
|
||||
(eval "$lt_compile" 2>conftest.err)
|
||||
ac_status=$?
|
||||
cat conftest.err >&5
|
||||
echo "$as_me:7004: \$? = $ac_status" >&5
|
||||
echo "$as_me:7003: \$? = $ac_status" >&5
|
||||
if (exit $ac_status) && test -s "$ac_outfile"; then
|
||||
# The compiler can only warn and ignore the option if not recognized
|
||||
# So say no if there are warnings other than the usual output.
|
||||
@@ -7102,11 +7101,11 @@ else
|
||||
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
||||
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
||||
-e 's:$: $lt_compiler_flag:'`
|
||||
(eval echo "\"\$as_me:7105: $lt_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:7104: $lt_compile\"" >&5)
|
||||
(eval "$lt_compile" 2>out/conftest.err)
|
||||
ac_status=$?
|
||||
cat out/conftest.err >&5
|
||||
echo "$as_me:7109: \$? = $ac_status" >&5
|
||||
echo "$as_me:7108: \$? = $ac_status" >&5
|
||||
if (exit $ac_status) && test -s out/conftest2.$ac_objext
|
||||
then
|
||||
# The compiler can only warn and ignore the option if not recognized
|
||||
@@ -7157,11 +7156,11 @@ else
|
||||
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
||||
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
||||
-e 's:$: $lt_compiler_flag:'`
|
||||
(eval echo "\"\$as_me:7160: $lt_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:7159: $lt_compile\"" >&5)
|
||||
(eval "$lt_compile" 2>out/conftest.err)
|
||||
ac_status=$?
|
||||
cat out/conftest.err >&5
|
||||
echo "$as_me:7164: \$? = $ac_status" >&5
|
||||
echo "$as_me:7163: \$? = $ac_status" >&5
|
||||
if (exit $ac_status) && test -s out/conftest2.$ac_objext
|
||||
then
|
||||
# The compiler can only warn and ignore the option if not recognized
|
||||
@@ -9537,7 +9536,7 @@ else
|
||||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||
lt_status=$lt_dlunknown
|
||||
cat > conftest.$ac_ext <<_LT_EOF
|
||||
#line 9540 "configure"
|
||||
#line 9539 "configure"
|
||||
#include "confdefs.h"
|
||||
|
||||
#if HAVE_DLFCN_H
|
||||
@@ -9633,7 +9632,7 @@ else
|
||||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||
lt_status=$lt_dlunknown
|
||||
cat > conftest.$ac_ext <<_LT_EOF
|
||||
#line 9636 "configure"
|
||||
#line 9635 "configure"
|
||||
#include "confdefs.h"
|
||||
|
||||
#if HAVE_DLFCN_H
|
||||
@@ -10336,7 +10335,7 @@ USE_AMALGAMATION=1
|
||||
# if not, then we fall back to plain tclsh.
|
||||
# TODO: try other versions before falling back?
|
||||
#
|
||||
for ac_prog in tclsh8.5 tclsh
|
||||
for ac_prog in tclsh8.6 tclsh8.5 tclsh
|
||||
do
|
||||
# Extract the first word of "$ac_prog", so it can be a program name with args.
|
||||
set dummy $ac_prog; ac_word=$2
|
||||
@@ -10853,21 +10852,12 @@ else
|
||||
TARGET_EXEEXT=$config_TARGET_EXEEXT
|
||||
fi
|
||||
if test "$TARGET_EXEEXT" = ".exe"; then
|
||||
if test $OS2_SHELL ; then
|
||||
SQLITE_OS_UNIX=0
|
||||
SQLITE_OS_WIN=0
|
||||
SQLITE_OS_OS2=1
|
||||
CFLAGS="$CFLAGS -DSQLITE_OS_OS2=1"
|
||||
else
|
||||
SQLITE_OS_UNIX=0
|
||||
SQLITE_OS_WIN=1
|
||||
SQLITE_OS_OS2=0
|
||||
CFLAGS="$CFLAGS -DSQLITE_OS_WIN=1"
|
||||
fi
|
||||
SQLITE_OS_UNIX=0
|
||||
SQLITE_OS_WIN=1
|
||||
CFLAGS="$CFLAGS -DSQLITE_OS_WIN=1"
|
||||
else
|
||||
SQLITE_OS_UNIX=1
|
||||
SQLITE_OS_WIN=0
|
||||
SQLITE_OS_OS2=0
|
||||
CFLAGS="$CFLAGS -DSQLITE_OS_UNIX=1"
|
||||
fi
|
||||
|
||||
@@ -10876,7 +10866,6 @@ fi
|
||||
|
||||
|
||||
|
||||
|
||||
##########
|
||||
# Figure out all the parameters needed to compile against Tcl.
|
||||
#
|
||||
@@ -12005,7 +11994,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by sqlcipher $as_me 3.8.0.2, which was
|
||||
This file was extended by sqlcipher $as_me 3.8.4.3, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@@ -12071,7 +12060,7 @@ _ACEOF
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
||||
ac_cs_version="\\
|
||||
sqlcipher config.status 3.8.0.2
|
||||
sqlcipher config.status 3.8.4.3
|
||||
configured by $0, generated by GNU Autoconf 2.69,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
||||
+4
-14
@@ -139,7 +139,7 @@ USE_AMALGAMATION=1
|
||||
# if not, then we fall back to plain tclsh.
|
||||
# TODO: try other versions before falling back?
|
||||
#
|
||||
AC_CHECK_PROGS(TCLSH_CMD, [tclsh8.5 tclsh], none)
|
||||
AC_CHECK_PROGS(TCLSH_CMD, [tclsh8.6 tclsh8.5 tclsh], none)
|
||||
if test "$TCLSH_CMD" = "none"; then
|
||||
# If we can't find a local tclsh, then building the amalgamation will fail.
|
||||
# We act as though --disable-amalgamation has been used.
|
||||
@@ -370,28 +370,18 @@ else
|
||||
TARGET_EXEEXT=$config_TARGET_EXEEXT
|
||||
fi
|
||||
if test "$TARGET_EXEEXT" = ".exe"; then
|
||||
if test $OS2_SHELL ; then
|
||||
SQLITE_OS_UNIX=0
|
||||
SQLITE_OS_WIN=0
|
||||
SQLITE_OS_OS2=1
|
||||
CFLAGS="$CFLAGS -DSQLITE_OS_OS2=1"
|
||||
else
|
||||
SQLITE_OS_UNIX=0
|
||||
SQLITE_OS_WIN=1
|
||||
SQLITE_OS_OS2=0
|
||||
CFLAGS="$CFLAGS -DSQLITE_OS_WIN=1"
|
||||
fi
|
||||
SQLITE_OS_UNIX=0
|
||||
SQLITE_OS_WIN=1
|
||||
CFLAGS="$CFLAGS -DSQLITE_OS_WIN=1"
|
||||
else
|
||||
SQLITE_OS_UNIX=1
|
||||
SQLITE_OS_WIN=0
|
||||
SQLITE_OS_OS2=0
|
||||
CFLAGS="$CFLAGS -DSQLITE_OS_UNIX=1"
|
||||
fi
|
||||
|
||||
AC_SUBST(BUILD_EXEEXT)
|
||||
AC_SUBST(SQLITE_OS_UNIX)
|
||||
AC_SUBST(SQLITE_OS_WIN)
|
||||
AC_SUBST(SQLITE_OS_OS2)
|
||||
AC_SUBST(TARGET_EXEEXT)
|
||||
|
||||
##########
|
||||
|
||||
+502
-130
@@ -330,21 +330,37 @@ int sqlite3Fts3PutVarint(char *p, sqlite_int64 v){
|
||||
return (int) (q - (unsigned char *)p);
|
||||
}
|
||||
|
||||
#define GETVARINT_STEP(v, ptr, shift, mask1, mask2, var, ret) \
|
||||
v = (v & mask1) | ( (*ptr++) << shift ); \
|
||||
if( (v & mask2)==0 ){ var = v; return ret; }
|
||||
#define GETVARINT_INIT(v, ptr, shift, mask1, mask2, var, ret) \
|
||||
v = (*ptr++); \
|
||||
if( (v & mask2)==0 ){ var = v; return ret; }
|
||||
|
||||
/*
|
||||
** Read a 64-bit variable-length integer from memory starting at p[0].
|
||||
** Return the number of bytes read, or 0 on error.
|
||||
** The value is stored in *v.
|
||||
*/
|
||||
int sqlite3Fts3GetVarint(const char *p, sqlite_int64 *v){
|
||||
const unsigned char *q = (const unsigned char *) p;
|
||||
sqlite_uint64 x = 0, y = 1;
|
||||
while( (*q&0x80)==0x80 && q-(unsigned char *)p<FTS3_VARINT_MAX ){
|
||||
x += y * (*q++ & 0x7f);
|
||||
y <<= 7;
|
||||
const char *pStart = p;
|
||||
u32 a;
|
||||
u64 b;
|
||||
int shift;
|
||||
|
||||
GETVARINT_INIT(a, p, 0, 0x00, 0x80, *v, 1);
|
||||
GETVARINT_STEP(a, p, 7, 0x7F, 0x4000, *v, 2);
|
||||
GETVARINT_STEP(a, p, 14, 0x3FFF, 0x200000, *v, 3);
|
||||
GETVARINT_STEP(a, p, 21, 0x1FFFFF, 0x10000000, *v, 4);
|
||||
b = (a & 0x0FFFFFFF );
|
||||
|
||||
for(shift=28; shift<=63; shift+=7){
|
||||
u64 c = *p++;
|
||||
b += (c&0x7F) << shift;
|
||||
if( (c & 0x80)==0 ) break;
|
||||
}
|
||||
x += y * (*q++);
|
||||
*v = (sqlite_int64) x;
|
||||
return (int) (q - (unsigned char *)p);
|
||||
*v = b;
|
||||
return (int)(p - pStart);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -352,10 +368,21 @@ int sqlite3Fts3GetVarint(const char *p, sqlite_int64 *v){
|
||||
** 32-bit integer before it is returned.
|
||||
*/
|
||||
int sqlite3Fts3GetVarint32(const char *p, int *pi){
|
||||
sqlite_int64 i;
|
||||
int ret = sqlite3Fts3GetVarint(p, &i);
|
||||
*pi = (int) i;
|
||||
return ret;
|
||||
u32 a;
|
||||
|
||||
#ifndef fts3GetVarint32
|
||||
GETVARINT_INIT(a, p, 0, 0x00, 0x80, *pi, 1);
|
||||
#else
|
||||
a = (*p++);
|
||||
assert( a & 0x80 );
|
||||
#endif
|
||||
|
||||
GETVARINT_STEP(a, p, 7, 0x7F, 0x4000, *pi, 2);
|
||||
GETVARINT_STEP(a, p, 14, 0x3FFF, 0x200000, *pi, 3);
|
||||
GETVARINT_STEP(a, p, 21, 0x1FFFFF, 0x10000000, *pi, 4);
|
||||
a = (a & 0x0FFFFFFF );
|
||||
*pi = (int)(a | ((u32)(*p & 0x0F) << 28));
|
||||
return 5;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1445,6 +1472,19 @@ static int fts3CreateMethod(
|
||||
return fts3InitVtab(1, db, pAux, argc, argv, ppVtab, pzErr);
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the pIdxInfo->estimatedRows variable to nRow. Unless this
|
||||
** extension is currently being used by a version of SQLite too old to
|
||||
** support estimatedRows. In that case this function is a no-op.
|
||||
*/
|
||||
static void fts3SetEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){
|
||||
#if SQLITE_VERSION_NUMBER>=3008002
|
||||
if( sqlite3_libversion_number()>=3008002 ){
|
||||
pIdxInfo->estimatedRows = nRow;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the xBestIndex method for FTS3 tables. There
|
||||
** are three possible strategies, in order of preference:
|
||||
@@ -1457,7 +1497,11 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
|
||||
Fts3Table *p = (Fts3Table *)pVTab;
|
||||
int i; /* Iterator variable */
|
||||
int iCons = -1; /* Index of constraint to use */
|
||||
|
||||
int iLangidCons = -1; /* Index of langid=x constraint, if present */
|
||||
int iDocidGe = -1; /* Index of docid>=x constraint, if present */
|
||||
int iDocidLe = -1; /* Index of docid<=x constraint, if present */
|
||||
int iIdx;
|
||||
|
||||
/* By default use a full table scan. This is an expensive option,
|
||||
** so search through the constraints to see if a more efficient
|
||||
@@ -1466,14 +1510,27 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
|
||||
pInfo->idxNum = FTS3_FULLSCAN_SEARCH;
|
||||
pInfo->estimatedCost = 5000000;
|
||||
for(i=0; i<pInfo->nConstraint; i++){
|
||||
int bDocid; /* True if this constraint is on docid */
|
||||
struct sqlite3_index_constraint *pCons = &pInfo->aConstraint[i];
|
||||
if( pCons->usable==0 ) continue;
|
||||
if( pCons->usable==0 ){
|
||||
if( pCons->op==SQLITE_INDEX_CONSTRAINT_MATCH ){
|
||||
/* There exists an unusable MATCH constraint. This means that if
|
||||
** the planner does elect to use the results of this call as part
|
||||
** of the overall query plan the user will see an "unable to use
|
||||
** function MATCH in the requested context" error. To discourage
|
||||
** this, return a very high cost here. */
|
||||
pInfo->idxNum = FTS3_FULLSCAN_SEARCH;
|
||||
pInfo->estimatedCost = 1e50;
|
||||
fts3SetEstimatedRows(pInfo, ((sqlite3_int64)1) << 50);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
bDocid = (pCons->iColumn<0 || pCons->iColumn==p->nColumn+1);
|
||||
|
||||
/* A direct lookup on the rowid or docid column. Assign a cost of 1.0. */
|
||||
if( iCons<0
|
||||
&& pCons->op==SQLITE_INDEX_CONSTRAINT_EQ
|
||||
&& (pCons->iColumn<0 || pCons->iColumn==p->nColumn+1 )
|
||||
){
|
||||
if( iCons<0 && pCons->op==SQLITE_INDEX_CONSTRAINT_EQ && bDocid ){
|
||||
pInfo->idxNum = FTS3_DOCID_SEARCH;
|
||||
pInfo->estimatedCost = 1.0;
|
||||
iCons = i;
|
||||
@@ -1502,14 +1559,38 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
|
||||
){
|
||||
iLangidCons = i;
|
||||
}
|
||||
|
||||
if( bDocid ){
|
||||
switch( pCons->op ){
|
||||
case SQLITE_INDEX_CONSTRAINT_GE:
|
||||
case SQLITE_INDEX_CONSTRAINT_GT:
|
||||
iDocidGe = i;
|
||||
break;
|
||||
|
||||
case SQLITE_INDEX_CONSTRAINT_LE:
|
||||
case SQLITE_INDEX_CONSTRAINT_LT:
|
||||
iDocidLe = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iIdx = 1;
|
||||
if( iCons>=0 ){
|
||||
pInfo->aConstraintUsage[iCons].argvIndex = 1;
|
||||
pInfo->aConstraintUsage[iCons].argvIndex = iIdx++;
|
||||
pInfo->aConstraintUsage[iCons].omit = 1;
|
||||
}
|
||||
if( iLangidCons>=0 ){
|
||||
pInfo->aConstraintUsage[iLangidCons].argvIndex = 2;
|
||||
pInfo->idxNum |= FTS3_HAVE_LANGID;
|
||||
pInfo->aConstraintUsage[iLangidCons].argvIndex = iIdx++;
|
||||
}
|
||||
if( iDocidGe>=0 ){
|
||||
pInfo->idxNum |= FTS3_HAVE_DOCID_GE;
|
||||
pInfo->aConstraintUsage[iDocidGe].argvIndex = iIdx++;
|
||||
}
|
||||
if( iDocidLe>=0 ){
|
||||
pInfo->idxNum |= FTS3_HAVE_DOCID_LE;
|
||||
pInfo->aConstraintUsage[iDocidLe].argvIndex = iIdx++;
|
||||
}
|
||||
|
||||
/* Regardless of the strategy selected, FTS can deliver rows in rowid (or
|
||||
@@ -1687,10 +1768,10 @@ static int fts3ScanInteriorNode(
|
||||
/* Load the next term on the node into zBuffer. Use realloc() to expand
|
||||
** the size of zBuffer if required. */
|
||||
if( !isFirstTerm ){
|
||||
zCsr += sqlite3Fts3GetVarint32(zCsr, &nPrefix);
|
||||
zCsr += fts3GetVarint32(zCsr, &nPrefix);
|
||||
}
|
||||
isFirstTerm = 0;
|
||||
zCsr += sqlite3Fts3GetVarint32(zCsr, &nSuffix);
|
||||
zCsr += fts3GetVarint32(zCsr, &nSuffix);
|
||||
|
||||
if( nPrefix<0 || nSuffix<0 || &zCsr[nSuffix]>zEnd ){
|
||||
rc = FTS_CORRUPT_VTAB;
|
||||
@@ -1778,7 +1859,7 @@ static int fts3SelectLeaf(
|
||||
|
||||
assert( piLeaf || piLeaf2 );
|
||||
|
||||
sqlite3Fts3GetVarint32(zNode, &iHeight);
|
||||
fts3GetVarint32(zNode, &iHeight);
|
||||
rc = fts3ScanInteriorNode(zTerm, nTerm, zNode, nNode, piLeaf, piLeaf2);
|
||||
assert( !piLeaf2 || !piLeaf || rc!=SQLITE_OK || (*piLeaf<=*piLeaf2) );
|
||||
|
||||
@@ -1980,11 +2061,11 @@ static void fts3PoslistMerge(
|
||||
int iCol1; /* The current column index in pp1 */
|
||||
int iCol2; /* The current column index in pp2 */
|
||||
|
||||
if( *p1==POS_COLUMN ) sqlite3Fts3GetVarint32(&p1[1], &iCol1);
|
||||
if( *p1==POS_COLUMN ) fts3GetVarint32(&p1[1], &iCol1);
|
||||
else if( *p1==POS_END ) iCol1 = POSITION_LIST_END;
|
||||
else iCol1 = 0;
|
||||
|
||||
if( *p2==POS_COLUMN ) sqlite3Fts3GetVarint32(&p2[1], &iCol2);
|
||||
if( *p2==POS_COLUMN ) fts3GetVarint32(&p2[1], &iCol2);
|
||||
else if( *p2==POS_END ) iCol2 = POSITION_LIST_END;
|
||||
else iCol2 = 0;
|
||||
|
||||
@@ -2077,11 +2158,11 @@ static int fts3PoslistPhraseMerge(
|
||||
assert( p!=0 && *p1!=0 && *p2!=0 );
|
||||
if( *p1==POS_COLUMN ){
|
||||
p1++;
|
||||
p1 += sqlite3Fts3GetVarint32(p1, &iCol1);
|
||||
p1 += fts3GetVarint32(p1, &iCol1);
|
||||
}
|
||||
if( *p2==POS_COLUMN ){
|
||||
p2++;
|
||||
p2 += sqlite3Fts3GetVarint32(p2, &iCol2);
|
||||
p2 += fts3GetVarint32(p2, &iCol2);
|
||||
}
|
||||
|
||||
while( 1 ){
|
||||
@@ -2131,9 +2212,9 @@ static int fts3PoslistPhraseMerge(
|
||||
if( 0==*p1 || 0==*p2 ) break;
|
||||
|
||||
p1++;
|
||||
p1 += sqlite3Fts3GetVarint32(p1, &iCol1);
|
||||
p1 += fts3GetVarint32(p1, &iCol1);
|
||||
p2++;
|
||||
p2 += sqlite3Fts3GetVarint32(p2, &iCol2);
|
||||
p2 += fts3GetVarint32(p2, &iCol2);
|
||||
}
|
||||
|
||||
/* Advance pointer p1 or p2 (whichever corresponds to the smaller of
|
||||
@@ -2145,12 +2226,12 @@ static int fts3PoslistPhraseMerge(
|
||||
fts3ColumnlistCopy(0, &p1);
|
||||
if( 0==*p1 ) break;
|
||||
p1++;
|
||||
p1 += sqlite3Fts3GetVarint32(p1, &iCol1);
|
||||
p1 += fts3GetVarint32(p1, &iCol1);
|
||||
}else{
|
||||
fts3ColumnlistCopy(0, &p2);
|
||||
if( 0==*p2 ) break;
|
||||
p2++;
|
||||
p2 += sqlite3Fts3GetVarint32(p2, &iCol2);
|
||||
p2 += fts3GetVarint32(p2, &iCol2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2956,6 +3037,33 @@ static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** The following are copied from sqliteInt.h.
|
||||
**
|
||||
** Constants for the largest and smallest possible 64-bit signed integers.
|
||||
** These macros are designed to work correctly on both 32-bit and 64-bit
|
||||
** compilers.
|
||||
*/
|
||||
#ifndef SQLITE_AMALGAMATION
|
||||
# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
|
||||
# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** If the numeric type of argument pVal is "integer", then return it
|
||||
** converted to a 64-bit signed integer. Otherwise, return a copy of
|
||||
** the second parameter, iDefault.
|
||||
*/
|
||||
static sqlite3_int64 fts3DocidRange(sqlite3_value *pVal, i64 iDefault){
|
||||
if( pVal ){
|
||||
int eType = sqlite3_value_numeric_type(pVal);
|
||||
if( eType==SQLITE_INTEGER ){
|
||||
return sqlite3_value_int64(pVal);
|
||||
}
|
||||
}
|
||||
return iDefault;
|
||||
}
|
||||
|
||||
/*
|
||||
** This is the xFilter interface for the virtual table. See
|
||||
** the virtual table xFilter method documentation for additional
|
||||
@@ -2981,40 +3089,58 @@ static int fts3FilterMethod(
|
||||
){
|
||||
int rc;
|
||||
char *zSql; /* SQL statement used to access %_content */
|
||||
int eSearch;
|
||||
Fts3Table *p = (Fts3Table *)pCursor->pVtab;
|
||||
Fts3Cursor *pCsr = (Fts3Cursor *)pCursor;
|
||||
|
||||
sqlite3_value *pCons = 0; /* The MATCH or rowid constraint, if any */
|
||||
sqlite3_value *pLangid = 0; /* The "langid = ?" constraint, if any */
|
||||
sqlite3_value *pDocidGe = 0; /* The "docid >= ?" constraint, if any */
|
||||
sqlite3_value *pDocidLe = 0; /* The "docid <= ?" constraint, if any */
|
||||
int iIdx;
|
||||
|
||||
UNUSED_PARAMETER(idxStr);
|
||||
UNUSED_PARAMETER(nVal);
|
||||
|
||||
assert( idxNum>=0 && idxNum<=(FTS3_FULLTEXT_SEARCH+p->nColumn) );
|
||||
assert( nVal==0 || nVal==1 || nVal==2 );
|
||||
assert( (nVal==0)==(idxNum==FTS3_FULLSCAN_SEARCH) );
|
||||
eSearch = (idxNum & 0x0000FFFF);
|
||||
assert( eSearch>=0 && eSearch<=(FTS3_FULLTEXT_SEARCH+p->nColumn) );
|
||||
assert( p->pSegments==0 );
|
||||
|
||||
/* Collect arguments into local variables */
|
||||
iIdx = 0;
|
||||
if( eSearch!=FTS3_FULLSCAN_SEARCH ) pCons = apVal[iIdx++];
|
||||
if( idxNum & FTS3_HAVE_LANGID ) pLangid = apVal[iIdx++];
|
||||
if( idxNum & FTS3_HAVE_DOCID_GE ) pDocidGe = apVal[iIdx++];
|
||||
if( idxNum & FTS3_HAVE_DOCID_LE ) pDocidLe = apVal[iIdx++];
|
||||
assert( iIdx==nVal );
|
||||
|
||||
/* In case the cursor has been used before, clear it now. */
|
||||
sqlite3_finalize(pCsr->pStmt);
|
||||
sqlite3_free(pCsr->aDoclist);
|
||||
sqlite3Fts3ExprFree(pCsr->pExpr);
|
||||
memset(&pCursor[1], 0, sizeof(Fts3Cursor)-sizeof(sqlite3_vtab_cursor));
|
||||
|
||||
/* Set the lower and upper bounds on docids to return */
|
||||
pCsr->iMinDocid = fts3DocidRange(pDocidGe, SMALLEST_INT64);
|
||||
pCsr->iMaxDocid = fts3DocidRange(pDocidLe, LARGEST_INT64);
|
||||
|
||||
if( idxStr ){
|
||||
pCsr->bDesc = (idxStr[0]=='D');
|
||||
}else{
|
||||
pCsr->bDesc = p->bDescIdx;
|
||||
}
|
||||
pCsr->eSearch = (i16)idxNum;
|
||||
pCsr->eSearch = (i16)eSearch;
|
||||
|
||||
if( idxNum!=FTS3_DOCID_SEARCH && idxNum!=FTS3_FULLSCAN_SEARCH ){
|
||||
int iCol = idxNum-FTS3_FULLTEXT_SEARCH;
|
||||
const char *zQuery = (const char *)sqlite3_value_text(apVal[0]);
|
||||
if( eSearch!=FTS3_DOCID_SEARCH && eSearch!=FTS3_FULLSCAN_SEARCH ){
|
||||
int iCol = eSearch-FTS3_FULLTEXT_SEARCH;
|
||||
const char *zQuery = (const char *)sqlite3_value_text(pCons);
|
||||
|
||||
if( zQuery==0 && sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
|
||||
if( zQuery==0 && sqlite3_value_type(pCons)!=SQLITE_NULL ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
pCsr->iLangid = 0;
|
||||
if( nVal==2 ) pCsr->iLangid = sqlite3_value_int(apVal[1]);
|
||||
if( pLangid ) pCsr->iLangid = sqlite3_value_int(pLangid);
|
||||
|
||||
assert( p->base.zErrMsg==0 );
|
||||
rc = sqlite3Fts3ExprParse(p->pTokenizer, pCsr->iLangid,
|
||||
@@ -3037,7 +3163,7 @@ static int fts3FilterMethod(
|
||||
** full-text query or docid lookup, the statement retrieves a single
|
||||
** row by docid.
|
||||
*/
|
||||
if( idxNum==FTS3_FULLSCAN_SEARCH ){
|
||||
if( eSearch==FTS3_FULLSCAN_SEARCH ){
|
||||
zSql = sqlite3_mprintf(
|
||||
"SELECT %s ORDER BY rowid %s",
|
||||
p->zReadExprlist, (pCsr->bDesc ? "DESC" : "ASC")
|
||||
@@ -3048,10 +3174,10 @@ static int fts3FilterMethod(
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
}else if( idxNum==FTS3_DOCID_SEARCH ){
|
||||
}else if( eSearch==FTS3_DOCID_SEARCH ){
|
||||
rc = fts3CursorSeekStmt(pCsr, &pCsr->pStmt);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]);
|
||||
rc = sqlite3_bind_value(pCsr->pStmt, 1, pCons);
|
||||
}
|
||||
}
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
@@ -3942,6 +4068,12 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Maximum number of tokens a phrase may have to be considered for the
|
||||
** incremental doclists strategy.
|
||||
*/
|
||||
#define MAX_INCR_PHRASE_TOKENS 4
|
||||
|
||||
/*
|
||||
** This function is called for each Fts3Phrase in a full-text query
|
||||
** expression to initialize the mechanism for returning rows. Once this
|
||||
@@ -3955,23 +4087,43 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
|
||||
** SQLITE_OK is returned if no error occurs, otherwise an SQLite error code.
|
||||
*/
|
||||
static int fts3EvalPhraseStart(Fts3Cursor *pCsr, int bOptOk, Fts3Phrase *p){
|
||||
int rc; /* Error code */
|
||||
Fts3PhraseToken *pFirst = &p->aToken[0];
|
||||
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
|
||||
int rc = SQLITE_OK; /* Error code */
|
||||
int i;
|
||||
|
||||
if( pCsr->bDesc==pTab->bDescIdx
|
||||
&& bOptOk==1
|
||||
&& p->nToken==1
|
||||
&& pFirst->pSegcsr
|
||||
&& pFirst->pSegcsr->bLookup
|
||||
&& pFirst->bFirst==0
|
||||
){
|
||||
/* Determine if doclists may be loaded from disk incrementally. This is
|
||||
** possible if the bOptOk argument is true, the FTS doclists will be
|
||||
** scanned in forward order, and the phrase consists of
|
||||
** MAX_INCR_PHRASE_TOKENS or fewer tokens, none of which are are "^first"
|
||||
** tokens or prefix tokens that cannot use a prefix-index. */
|
||||
int bHaveIncr = 0;
|
||||
int bIncrOk = (bOptOk
|
||||
&& pCsr->bDesc==pTab->bDescIdx
|
||||
&& p->nToken<=MAX_INCR_PHRASE_TOKENS && p->nToken>0
|
||||
&& p->nToken<=MAX_INCR_PHRASE_TOKENS && p->nToken>0
|
||||
#ifdef SQLITE_TEST
|
||||
&& pTab->bNoIncrDoclist==0
|
||||
#endif
|
||||
);
|
||||
for(i=0; bIncrOk==1 && i<p->nToken; i++){
|
||||
Fts3PhraseToken *pToken = &p->aToken[i];
|
||||
if( pToken->bFirst || (pToken->pSegcsr!=0 && !pToken->pSegcsr->bLookup) ){
|
||||
bIncrOk = 0;
|
||||
}
|
||||
if( pToken->pSegcsr ) bHaveIncr = 1;
|
||||
}
|
||||
|
||||
if( bIncrOk && bHaveIncr ){
|
||||
/* Use the incremental approach. */
|
||||
int iCol = (p->iColumn >= pTab->nColumn ? -1 : p->iColumn);
|
||||
rc = sqlite3Fts3MsrIncrStart(
|
||||
pTab, pFirst->pSegcsr, iCol, pFirst->z, pFirst->n);
|
||||
for(i=0; rc==SQLITE_OK && i<p->nToken; i++){
|
||||
Fts3PhraseToken *pToken = &p->aToken[i];
|
||||
Fts3MultiSegReader *pSegcsr = pToken->pSegcsr;
|
||||
if( pSegcsr ){
|
||||
rc = sqlite3Fts3MsrIncrStart(pTab, pSegcsr, iCol, pToken->z, pToken->n);
|
||||
}
|
||||
}
|
||||
p->bIncr = 1;
|
||||
|
||||
}else{
|
||||
/* Load the full doclist for the phrase into memory. */
|
||||
rc = fts3EvalPhraseLoad(pCsr, p);
|
||||
@@ -4080,6 +4232,216 @@ void sqlite3Fts3DoclistNext(
|
||||
*ppIter = p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Advance the iterator pDL to the next entry in pDL->aAll/nAll. Set *pbEof
|
||||
** to true if EOF is reached.
|
||||
*/
|
||||
static void fts3EvalDlPhraseNext(
|
||||
Fts3Table *pTab,
|
||||
Fts3Doclist *pDL,
|
||||
u8 *pbEof
|
||||
){
|
||||
char *pIter; /* Used to iterate through aAll */
|
||||
char *pEnd = &pDL->aAll[pDL->nAll]; /* 1 byte past end of aAll */
|
||||
|
||||
if( pDL->pNextDocid ){
|
||||
pIter = pDL->pNextDocid;
|
||||
}else{
|
||||
pIter = pDL->aAll;
|
||||
}
|
||||
|
||||
if( pIter>=pEnd ){
|
||||
/* We have already reached the end of this doclist. EOF. */
|
||||
*pbEof = 1;
|
||||
}else{
|
||||
sqlite3_int64 iDelta;
|
||||
pIter += sqlite3Fts3GetVarint(pIter, &iDelta);
|
||||
if( pTab->bDescIdx==0 || pDL->pNextDocid==0 ){
|
||||
pDL->iDocid += iDelta;
|
||||
}else{
|
||||
pDL->iDocid -= iDelta;
|
||||
}
|
||||
pDL->pList = pIter;
|
||||
fts3PoslistCopy(0, &pIter);
|
||||
pDL->nList = (int)(pIter - pDL->pList);
|
||||
|
||||
/* pIter now points just past the 0x00 that terminates the position-
|
||||
** list for document pDL->iDocid. However, if this position-list was
|
||||
** edited in place by fts3EvalNearTrim(), then pIter may not actually
|
||||
** point to the start of the next docid value. The following line deals
|
||||
** with this case by advancing pIter past the zero-padding added by
|
||||
** fts3EvalNearTrim(). */
|
||||
while( pIter<pEnd && *pIter==0 ) pIter++;
|
||||
|
||||
pDL->pNextDocid = pIter;
|
||||
assert( pIter>=&pDL->aAll[pDL->nAll] || *pIter );
|
||||
*pbEof = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Helper type used by fts3EvalIncrPhraseNext() and incrPhraseTokenNext().
|
||||
*/
|
||||
typedef struct TokenDoclist TokenDoclist;
|
||||
struct TokenDoclist {
|
||||
int bIgnore;
|
||||
sqlite3_int64 iDocid;
|
||||
char *pList;
|
||||
int nList;
|
||||
};
|
||||
|
||||
/*
|
||||
** Token pToken is an incrementally loaded token that is part of a
|
||||
** multi-token phrase. Advance it to the next matching document in the
|
||||
** database and populate output variable *p with the details of the new
|
||||
** entry. Or, if the iterator has reached EOF, set *pbEof to true.
|
||||
**
|
||||
** If an error occurs, return an SQLite error code. Otherwise, return
|
||||
** SQLITE_OK.
|
||||
*/
|
||||
static int incrPhraseTokenNext(
|
||||
Fts3Table *pTab, /* Virtual table handle */
|
||||
Fts3Phrase *pPhrase, /* Phrase to advance token of */
|
||||
int iToken, /* Specific token to advance */
|
||||
TokenDoclist *p, /* OUT: Docid and doclist for new entry */
|
||||
u8 *pbEof /* OUT: True if iterator is at EOF */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
if( pPhrase->iDoclistToken==iToken ){
|
||||
assert( p->bIgnore==0 );
|
||||
assert( pPhrase->aToken[iToken].pSegcsr==0 );
|
||||
fts3EvalDlPhraseNext(pTab, &pPhrase->doclist, pbEof);
|
||||
p->pList = pPhrase->doclist.pList;
|
||||
p->nList = pPhrase->doclist.nList;
|
||||
p->iDocid = pPhrase->doclist.iDocid;
|
||||
}else{
|
||||
Fts3PhraseToken *pToken = &pPhrase->aToken[iToken];
|
||||
assert( pToken->pDeferred==0 );
|
||||
assert( pToken->pSegcsr || pPhrase->iDoclistToken>=0 );
|
||||
if( pToken->pSegcsr ){
|
||||
assert( p->bIgnore==0 );
|
||||
rc = sqlite3Fts3MsrIncrNext(
|
||||
pTab, pToken->pSegcsr, &p->iDocid, &p->pList, &p->nList
|
||||
);
|
||||
if( p->pList==0 ) *pbEof = 1;
|
||||
}else{
|
||||
p->bIgnore = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** The phrase iterator passed as the second argument:
|
||||
**
|
||||
** * features at least one token that uses an incremental doclist, and
|
||||
**
|
||||
** * does not contain any deferred tokens.
|
||||
**
|
||||
** Advance it to the next matching documnent in the database and populate
|
||||
** the Fts3Doclist.pList and nList fields.
|
||||
**
|
||||
** If there is no "next" entry and no error occurs, then *pbEof is set to
|
||||
** 1 before returning. Otherwise, if no error occurs and the iterator is
|
||||
** successfully advanced, *pbEof is set to 0.
|
||||
**
|
||||
** If an error occurs, return an SQLite error code. Otherwise, return
|
||||
** SQLITE_OK.
|
||||
*/
|
||||
static int fts3EvalIncrPhraseNext(
|
||||
Fts3Cursor *pCsr, /* FTS Cursor handle */
|
||||
Fts3Phrase *p, /* Phrase object to advance to next docid */
|
||||
u8 *pbEof /* OUT: Set to 1 if EOF */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
Fts3Doclist *pDL = &p->doclist;
|
||||
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
|
||||
u8 bEof = 0;
|
||||
|
||||
/* This is only called if it is guaranteed that the phrase has at least
|
||||
** one incremental token. In which case the bIncr flag is set. */
|
||||
assert( p->bIncr==1 );
|
||||
|
||||
if( p->nToken==1 && p->bIncr ){
|
||||
rc = sqlite3Fts3MsrIncrNext(pTab, p->aToken[0].pSegcsr,
|
||||
&pDL->iDocid, &pDL->pList, &pDL->nList
|
||||
);
|
||||
if( pDL->pList==0 ) bEof = 1;
|
||||
}else{
|
||||
int bDescDoclist = pCsr->bDesc;
|
||||
struct TokenDoclist a[MAX_INCR_PHRASE_TOKENS];
|
||||
|
||||
memset(a, 0, sizeof(a));
|
||||
assert( p->nToken<=MAX_INCR_PHRASE_TOKENS );
|
||||
assert( p->iDoclistToken<MAX_INCR_PHRASE_TOKENS );
|
||||
|
||||
while( bEof==0 ){
|
||||
int bMaxSet = 0;
|
||||
sqlite3_int64 iMax = 0; /* Largest docid for all iterators */
|
||||
int i; /* Used to iterate through tokens */
|
||||
|
||||
/* Advance the iterator for each token in the phrase once. */
|
||||
for(i=0; rc==SQLITE_OK && i<p->nToken && bEof==0; i++){
|
||||
rc = incrPhraseTokenNext(pTab, p, i, &a[i], &bEof);
|
||||
if( a[i].bIgnore==0 && (bMaxSet==0 || DOCID_CMP(iMax, a[i].iDocid)<0) ){
|
||||
iMax = a[i].iDocid;
|
||||
bMaxSet = 1;
|
||||
}
|
||||
}
|
||||
assert( rc!=SQLITE_OK || a[p->nToken-1].bIgnore==0 );
|
||||
assert( rc!=SQLITE_OK || bMaxSet );
|
||||
|
||||
/* Keep advancing iterators until they all point to the same document */
|
||||
for(i=0; i<p->nToken; i++){
|
||||
while( rc==SQLITE_OK && bEof==0
|
||||
&& a[i].bIgnore==0 && DOCID_CMP(a[i].iDocid, iMax)<0
|
||||
){
|
||||
rc = incrPhraseTokenNext(pTab, p, i, &a[i], &bEof);
|
||||
if( DOCID_CMP(a[i].iDocid, iMax)>0 ){
|
||||
iMax = a[i].iDocid;
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if the current entries really are a phrase match */
|
||||
if( bEof==0 ){
|
||||
int nList = 0;
|
||||
int nByte = a[p->nToken-1].nList;
|
||||
char *aDoclist = sqlite3_malloc(nByte+1);
|
||||
if( !aDoclist ) return SQLITE_NOMEM;
|
||||
memcpy(aDoclist, a[p->nToken-1].pList, nByte+1);
|
||||
|
||||
for(i=0; i<(p->nToken-1); i++){
|
||||
if( a[i].bIgnore==0 ){
|
||||
char *pL = a[i].pList;
|
||||
char *pR = aDoclist;
|
||||
char *pOut = aDoclist;
|
||||
int nDist = p->nToken-1-i;
|
||||
int res = fts3PoslistPhraseMerge(&pOut, nDist, 0, 1, &pL, &pR);
|
||||
if( res==0 ) break;
|
||||
nList = (int)(pOut - aDoclist);
|
||||
}
|
||||
}
|
||||
if( i==(p->nToken-1) ){
|
||||
pDL->iDocid = iMax;
|
||||
pDL->pList = aDoclist;
|
||||
pDL->nList = nList;
|
||||
pDL->bFreeList = 1;
|
||||
break;
|
||||
}
|
||||
sqlite3_free(aDoclist);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*pbEof = bEof;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Attempt to move the phrase iterator to point to the next matching docid.
|
||||
** If an error occurs, return an SQLite error code. Otherwise, return
|
||||
@@ -4099,55 +4461,14 @@ static int fts3EvalPhraseNext(
|
||||
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
|
||||
|
||||
if( p->bIncr ){
|
||||
assert( p->nToken==1 );
|
||||
assert( pDL->pNextDocid==0 );
|
||||
rc = sqlite3Fts3MsrIncrNext(pTab, p->aToken[0].pSegcsr,
|
||||
&pDL->iDocid, &pDL->pList, &pDL->nList
|
||||
);
|
||||
if( rc==SQLITE_OK && !pDL->pList ){
|
||||
*pbEof = 1;
|
||||
}
|
||||
rc = fts3EvalIncrPhraseNext(pCsr, p, pbEof);
|
||||
}else if( pCsr->bDesc!=pTab->bDescIdx && pDL->nAll ){
|
||||
sqlite3Fts3DoclistPrev(pTab->bDescIdx, pDL->aAll, pDL->nAll,
|
||||
&pDL->pNextDocid, &pDL->iDocid, &pDL->nList, pbEof
|
||||
);
|
||||
pDL->pList = pDL->pNextDocid;
|
||||
}else{
|
||||
char *pIter; /* Used to iterate through aAll */
|
||||
char *pEnd = &pDL->aAll[pDL->nAll]; /* 1 byte past end of aAll */
|
||||
if( pDL->pNextDocid ){
|
||||
pIter = pDL->pNextDocid;
|
||||
}else{
|
||||
pIter = pDL->aAll;
|
||||
}
|
||||
|
||||
if( pIter>=pEnd ){
|
||||
/* We have already reached the end of this doclist. EOF. */
|
||||
*pbEof = 1;
|
||||
}else{
|
||||
sqlite3_int64 iDelta;
|
||||
pIter += sqlite3Fts3GetVarint(pIter, &iDelta);
|
||||
if( pTab->bDescIdx==0 || pDL->pNextDocid==0 ){
|
||||
pDL->iDocid += iDelta;
|
||||
}else{
|
||||
pDL->iDocid -= iDelta;
|
||||
}
|
||||
pDL->pList = pIter;
|
||||
fts3PoslistCopy(0, &pIter);
|
||||
pDL->nList = (int)(pIter - pDL->pList);
|
||||
|
||||
/* pIter now points just past the 0x00 that terminates the position-
|
||||
** list for document pDL->iDocid. However, if this position-list was
|
||||
** edited in place by fts3EvalNearTrim(), then pIter may not actually
|
||||
** point to the start of the next docid value. The following line deals
|
||||
** with this case by advancing pIter past the zero-padding added by
|
||||
** fts3EvalNearTrim(). */
|
||||
while( pIter<pEnd && *pIter==0 ) pIter++;
|
||||
|
||||
pDL->pNextDocid = pIter;
|
||||
assert( pIter>=&pDL->aAll[pDL->nAll] || *pIter );
|
||||
*pbEof = 0;
|
||||
}
|
||||
fts3EvalDlPhraseNext(pTab, pDL, pbEof);
|
||||
}
|
||||
|
||||
return rc;
|
||||
@@ -4172,7 +4493,6 @@ static int fts3EvalPhraseNext(
|
||||
static void fts3EvalStartReaders(
|
||||
Fts3Cursor *pCsr, /* FTS Cursor handle */
|
||||
Fts3Expr *pExpr, /* Expression to initialize phrases in */
|
||||
int bOptOk, /* True to enable incremental loading */
|
||||
int *pRc /* IN/OUT: Error code */
|
||||
){
|
||||
if( pExpr && SQLITE_OK==*pRc ){
|
||||
@@ -4183,10 +4503,10 @@ static void fts3EvalStartReaders(
|
||||
if( pExpr->pPhrase->aToken[i].pDeferred==0 ) break;
|
||||
}
|
||||
pExpr->bDeferred = (i==nToken);
|
||||
*pRc = fts3EvalPhraseStart(pCsr, bOptOk, pExpr->pPhrase);
|
||||
*pRc = fts3EvalPhraseStart(pCsr, 1, pExpr->pPhrase);
|
||||
}else{
|
||||
fts3EvalStartReaders(pCsr, pExpr->pLeft, bOptOk, pRc);
|
||||
fts3EvalStartReaders(pCsr, pExpr->pRight, bOptOk, pRc);
|
||||
fts3EvalStartReaders(pCsr, pExpr->pLeft, pRc);
|
||||
fts3EvalStartReaders(pCsr, pExpr->pRight, pRc);
|
||||
pExpr->bDeferred = (pExpr->pLeft->bDeferred && pExpr->pRight->bDeferred);
|
||||
}
|
||||
}
|
||||
@@ -4428,7 +4748,7 @@ static int fts3EvalSelectDeferred(
|
||||
** overflowing the 32-bit integer it is stored in. */
|
||||
if( ii<12 ) nLoad4 = nLoad4*4;
|
||||
|
||||
if( ii==0 || pTC->pPhrase->nToken>1 ){
|
||||
if( ii==0 || (pTC->pPhrase->nToken>1 && ii!=nToken-1) ){
|
||||
/* Either this is the cheapest token in the entire query, or it is
|
||||
** part of a multi-token phrase. Either way, the entire doclist will
|
||||
** (eventually) be loaded into memory. It may as well be now. */
|
||||
@@ -4508,7 +4828,7 @@ static int fts3EvalStart(Fts3Cursor *pCsr){
|
||||
}
|
||||
#endif
|
||||
|
||||
fts3EvalStartReaders(pCsr, pCsr->pExpr, 1, &rc);
|
||||
fts3EvalStartReaders(pCsr, pCsr->pExpr, &rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -4991,6 +5311,16 @@ static int fts3EvalNext(Fts3Cursor *pCsr){
|
||||
pCsr->iPrevId = pExpr->iDocid;
|
||||
}while( pCsr->isEof==0 && fts3EvalTestDeferredAndNear(pCsr, &rc) );
|
||||
}
|
||||
|
||||
/* Check if the cursor is past the end of the docid range specified
|
||||
** by Fts3Cursor.iMinDocid/iMaxDocid. If so, set the EOF flag. */
|
||||
if( rc==SQLITE_OK && (
|
||||
(pCsr->bDesc==0 && pCsr->iPrevId>pCsr->iMaxDocid)
|
||||
|| (pCsr->bDesc!=0 && pCsr->iPrevId<pCsr->iMinDocid)
|
||||
)){
|
||||
pCsr->isEof = 1;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -5014,12 +5344,16 @@ static void fts3EvalRestart(
|
||||
if( pPhrase ){
|
||||
fts3EvalInvalidatePoslist(pPhrase);
|
||||
if( pPhrase->bIncr ){
|
||||
assert( pPhrase->nToken==1 );
|
||||
assert( pPhrase->aToken[0].pSegcsr );
|
||||
sqlite3Fts3MsrIncrRestart(pPhrase->aToken[0].pSegcsr);
|
||||
int i;
|
||||
for(i=0; i<pPhrase->nToken; i++){
|
||||
Fts3PhraseToken *pToken = &pPhrase->aToken[i];
|
||||
assert( pToken->pDeferred==0 );
|
||||
if( pToken->pSegcsr ){
|
||||
sqlite3Fts3MsrIncrRestart(pToken->pSegcsr);
|
||||
}
|
||||
}
|
||||
*pRc = fts3EvalPhraseStart(pCsr, 0, pPhrase);
|
||||
}
|
||||
|
||||
pPhrase->doclist.pNextDocid = 0;
|
||||
pPhrase->doclist.iDocid = 0;
|
||||
}
|
||||
@@ -5064,7 +5398,7 @@ static void fts3EvalUpdateCounts(Fts3Expr *pExpr){
|
||||
pExpr->aMI[iCol*3 + 2] += (iCnt>0);
|
||||
if( *p==0x00 ) break;
|
||||
p++;
|
||||
p += sqlite3Fts3GetVarint32(p, &iCol);
|
||||
p += fts3GetVarint32(p, &iCol);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5268,15 +5602,23 @@ int sqlite3Fts3EvalPhrasePoslist(
|
||||
pIter = pPhrase->doclist.pList;
|
||||
if( iDocid!=pCsr->iPrevId || pExpr->bEof ){
|
||||
int bDescDoclist = pTab->bDescIdx; /* For DOCID_CMP macro */
|
||||
int iMul; /* +1 if csr dir matches index dir, else -1 */
|
||||
int bOr = 0;
|
||||
u8 bEof = 0;
|
||||
Fts3Expr *p;
|
||||
u8 bTreeEof = 0;
|
||||
Fts3Expr *p; /* Used to iterate from pExpr to root */
|
||||
Fts3Expr *pNear; /* Most senior NEAR ancestor (or pExpr) */
|
||||
|
||||
/* Check if this phrase descends from an OR expression node. If not,
|
||||
** return NULL. Otherwise, the entry that corresponds to docid
|
||||
** pCsr->iPrevId may lie earlier in the doclist buffer. */
|
||||
** pCsr->iPrevId may lie earlier in the doclist buffer. Or, if the
|
||||
** tree that the node is part of has been marked as EOF, but the node
|
||||
** itself is not EOF, then it may point to an earlier entry. */
|
||||
pNear = pExpr;
|
||||
for(p=pExpr->pParent; p; p=p->pParent){
|
||||
if( p->eType==FTSQUERY_OR ) bOr = 1;
|
||||
if( p->eType==FTSQUERY_NEAR ) pNear = p;
|
||||
if( p->bEof ) bTreeEof = 1;
|
||||
}
|
||||
if( bOr==0 ) return SQLITE_OK;
|
||||
|
||||
@@ -5295,29 +5637,59 @@ int sqlite3Fts3EvalPhrasePoslist(
|
||||
assert( rc!=SQLITE_OK || pPhrase->bIncr==0 );
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
}
|
||||
|
||||
if( pExpr->bEof ){
|
||||
pIter = 0;
|
||||
iDocid = 0;
|
||||
|
||||
iMul = ((pCsr->bDesc==bDescDoclist) ? 1 : -1);
|
||||
while( bTreeEof==1
|
||||
&& pNear->bEof==0
|
||||
&& (DOCID_CMP(pNear->iDocid, pCsr->iPrevId) * iMul)<0
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
fts3EvalNextRow(pCsr, pExpr, &rc);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
iDocid = pExpr->iDocid;
|
||||
pIter = pPhrase->doclist.pList;
|
||||
}
|
||||
|
||||
bEof = (pPhrase->doclist.nAll==0);
|
||||
assert( bDescDoclist==0 || bDescDoclist==1 );
|
||||
assert( pCsr->bDesc==0 || pCsr->bDesc==1 );
|
||||
|
||||
if( pCsr->bDesc==bDescDoclist ){
|
||||
int dummy;
|
||||
while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)>0 ) && bEof==0 ){
|
||||
sqlite3Fts3DoclistPrev(
|
||||
bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll,
|
||||
&pIter, &iDocid, &dummy, &bEof
|
||||
);
|
||||
}
|
||||
}else{
|
||||
while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)<0 ) && bEof==0 ){
|
||||
sqlite3Fts3DoclistNext(
|
||||
bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll,
|
||||
&pIter, &iDocid, &bEof
|
||||
);
|
||||
if( bEof==0 ){
|
||||
if( pCsr->bDesc==bDescDoclist ){
|
||||
int dummy;
|
||||
if( pNear->bEof ){
|
||||
/* This expression is already at EOF. So position it to point to the
|
||||
** last entry in the doclist at pPhrase->doclist.aAll[]. Variable
|
||||
** iDocid is already set for this entry, so all that is required is
|
||||
** to set pIter to point to the first byte of the last position-list
|
||||
** in the doclist.
|
||||
**
|
||||
** It would also be correct to set pIter and iDocid to zero. In
|
||||
** this case, the first call to sqltie3Fts4DoclistPrev() below
|
||||
** would also move the iterator to point to the last entry in the
|
||||
** doclist. However, this is expensive, as to do so it has to
|
||||
** iterate through the entire doclist from start to finish (since
|
||||
** it does not know the docid for the last entry). */
|
||||
pIter = &pPhrase->doclist.aAll[pPhrase->doclist.nAll-1];
|
||||
fts3ReversePoslist(pPhrase->doclist.aAll, &pIter);
|
||||
}
|
||||
while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)>0 ) && bEof==0 ){
|
||||
sqlite3Fts3DoclistPrev(
|
||||
bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll,
|
||||
&pIter, &iDocid, &dummy, &bEof
|
||||
);
|
||||
}
|
||||
}else{
|
||||
if( pNear->bEof ){
|
||||
pIter = 0;
|
||||
iDocid = 0;
|
||||
}
|
||||
while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)<0 ) && bEof==0 ){
|
||||
sqlite3Fts3DoclistNext(
|
||||
bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll,
|
||||
&pIter, &iDocid, &bEof
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5327,7 +5699,7 @@ int sqlite3Fts3EvalPhrasePoslist(
|
||||
|
||||
if( *pIter==0x01 ){
|
||||
pIter++;
|
||||
pIter += sqlite3Fts3GetVarint32(pIter, &iThis);
|
||||
pIter += fts3GetVarint32(pIter, &iThis);
|
||||
}else{
|
||||
iThis = 0;
|
||||
}
|
||||
@@ -5335,7 +5707,7 @@ int sqlite3Fts3EvalPhrasePoslist(
|
||||
fts3ColumnlistCopy(0, &pIter);
|
||||
if( *pIter==0x00 ) return 0;
|
||||
pIter++;
|
||||
pIter += sqlite3Fts3GetVarint32(pIter, &iThis);
|
||||
pIter += fts3GetVarint32(pIter, &iThis);
|
||||
}
|
||||
|
||||
*ppOut = ((iCol==iThis)?pIter:0);
|
||||
|
||||
+21
-1
@@ -267,6 +267,12 @@ struct Fts3Table {
|
||||
int inTransaction; /* True after xBegin but before xCommit/xRollback */
|
||||
int mxSavepoint; /* Largest valid xSavepoint integer */
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
/* True to disable the incremental doclist optimization. This is controled
|
||||
** by special insert command 'test-no-incr-doclist'. */
|
||||
int bNoIncrDoclist;
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -292,7 +298,8 @@ struct Fts3Cursor {
|
||||
int eEvalmode; /* An FTS3_EVAL_XX constant */
|
||||
int nRowAvg; /* Average size of database rows, in pages */
|
||||
sqlite3_int64 nDoc; /* Documents in table */
|
||||
|
||||
i64 iMinDocid; /* Minimum docid to return */
|
||||
i64 iMaxDocid; /* Maximum docid to return */
|
||||
int isMatchinfoNeeded; /* True when aMatchinfo[] needs filling in */
|
||||
u32 *aMatchinfo; /* Information about most recent match */
|
||||
int nMatchinfo; /* Number of elements in aMatchinfo[] */
|
||||
@@ -322,6 +329,15 @@ struct Fts3Cursor {
|
||||
#define FTS3_DOCID_SEARCH 1 /* Lookup by rowid on %_content table */
|
||||
#define FTS3_FULLTEXT_SEARCH 2 /* Full-text index search */
|
||||
|
||||
/*
|
||||
** The lower 16-bits of the sqlite3_index_info.idxNum value set by
|
||||
** the xBestIndex() method contains the Fts3Cursor.eSearch value described
|
||||
** above. The upper 16-bits contain a combination of the following
|
||||
** bits, used to describe extra constraints on full-text searches.
|
||||
*/
|
||||
#define FTS3_HAVE_LANGID 0x00010000 /* languageid=? */
|
||||
#define FTS3_HAVE_DOCID_GE 0x00020000 /* docid>=? */
|
||||
#define FTS3_HAVE_DOCID_LE 0x00040000 /* docid<=? */
|
||||
|
||||
struct Fts3Doclist {
|
||||
char *aAll; /* Array containing doclist (or NULL) */
|
||||
@@ -508,6 +524,10 @@ struct Fts3MultiSegReader {
|
||||
|
||||
int sqlite3Fts3Incrmerge(Fts3Table*,int,int);
|
||||
|
||||
#define fts3GetVarint32(p, piVal) ( \
|
||||
(*(u8*)(p)&0x80) ? sqlite3Fts3GetVarint32(p, piVal) : (*piVal=*(u8*)(p), 1) \
|
||||
)
|
||||
|
||||
/* fts3.c */
|
||||
int sqlite3Fts3PutVarint(char *, sqlite3_int64);
|
||||
int sqlite3Fts3GetVarint(const char *, sqlite_int64 *);
|
||||
|
||||
+96
-33
@@ -31,6 +31,7 @@ struct Fts3auxCursor {
|
||||
Fts3SegFilter filter;
|
||||
char *zStop;
|
||||
int nStop; /* Byte-length of string zStop */
|
||||
int iLangid; /* Language id to query */
|
||||
int isEof; /* True if cursor is at EOF */
|
||||
sqlite3_int64 iRowid; /* Current rowid */
|
||||
|
||||
@@ -45,7 +46,8 @@ struct Fts3auxCursor {
|
||||
/*
|
||||
** Schema of the terms table.
|
||||
*/
|
||||
#define FTS3_TERMS_SCHEMA "CREATE TABLE x(term, col, documents, occurrences)"
|
||||
#define FTS3_AUX_SCHEMA \
|
||||
"CREATE TABLE x(term, col, documents, occurrences, languageid HIDDEN)"
|
||||
|
||||
/*
|
||||
** This function does all the work for both the xConnect and xCreate methods.
|
||||
@@ -92,7 +94,7 @@ static int fts3auxConnectMethod(
|
||||
}
|
||||
nFts3 = (int)strlen(zFts3);
|
||||
|
||||
rc = sqlite3_declare_vtab(db, FTS3_TERMS_SCHEMA);
|
||||
rc = sqlite3_declare_vtab(db, FTS3_AUX_SCHEMA);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
nByte = sizeof(Fts3auxTable) + sizeof(Fts3Table) + nDb + nFts3 + 2;
|
||||
@@ -152,6 +154,8 @@ static int fts3auxBestIndexMethod(
|
||||
int iEq = -1;
|
||||
int iGe = -1;
|
||||
int iLe = -1;
|
||||
int iLangid = -1;
|
||||
int iNext = 1; /* Next free argvIndex value */
|
||||
|
||||
UNUSED_PARAMETER(pVTab);
|
||||
|
||||
@@ -163,36 +167,48 @@ static int fts3auxBestIndexMethod(
|
||||
pInfo->orderByConsumed = 1;
|
||||
}
|
||||
|
||||
/* Search for equality and range constraints on the "term" column. */
|
||||
/* Search for equality and range constraints on the "term" column.
|
||||
** And equality constraints on the hidden "languageid" column. */
|
||||
for(i=0; i<pInfo->nConstraint; i++){
|
||||
if( pInfo->aConstraint[i].usable && pInfo->aConstraint[i].iColumn==0 ){
|
||||
if( pInfo->aConstraint[i].usable ){
|
||||
int op = pInfo->aConstraint[i].op;
|
||||
if( op==SQLITE_INDEX_CONSTRAINT_EQ ) iEq = i;
|
||||
if( op==SQLITE_INDEX_CONSTRAINT_LT ) iLe = i;
|
||||
if( op==SQLITE_INDEX_CONSTRAINT_LE ) iLe = i;
|
||||
if( op==SQLITE_INDEX_CONSTRAINT_GT ) iGe = i;
|
||||
if( op==SQLITE_INDEX_CONSTRAINT_GE ) iGe = i;
|
||||
int iCol = pInfo->aConstraint[i].iColumn;
|
||||
|
||||
if( iCol==0 ){
|
||||
if( op==SQLITE_INDEX_CONSTRAINT_EQ ) iEq = i;
|
||||
if( op==SQLITE_INDEX_CONSTRAINT_LT ) iLe = i;
|
||||
if( op==SQLITE_INDEX_CONSTRAINT_LE ) iLe = i;
|
||||
if( op==SQLITE_INDEX_CONSTRAINT_GT ) iGe = i;
|
||||
if( op==SQLITE_INDEX_CONSTRAINT_GE ) iGe = i;
|
||||
}
|
||||
if( iCol==4 ){
|
||||
if( op==SQLITE_INDEX_CONSTRAINT_EQ ) iLangid = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( iEq>=0 ){
|
||||
pInfo->idxNum = FTS4AUX_EQ_CONSTRAINT;
|
||||
pInfo->aConstraintUsage[iEq].argvIndex = 1;
|
||||
pInfo->aConstraintUsage[iEq].argvIndex = iNext++;
|
||||
pInfo->estimatedCost = 5;
|
||||
}else{
|
||||
pInfo->idxNum = 0;
|
||||
pInfo->estimatedCost = 20000;
|
||||
if( iGe>=0 ){
|
||||
pInfo->idxNum += FTS4AUX_GE_CONSTRAINT;
|
||||
pInfo->aConstraintUsage[iGe].argvIndex = 1;
|
||||
pInfo->aConstraintUsage[iGe].argvIndex = iNext++;
|
||||
pInfo->estimatedCost /= 2;
|
||||
}
|
||||
if( iLe>=0 ){
|
||||
pInfo->idxNum += FTS4AUX_LE_CONSTRAINT;
|
||||
pInfo->aConstraintUsage[iLe].argvIndex = 1 + (iGe>=0);
|
||||
pInfo->aConstraintUsage[iLe].argvIndex = iNext++;
|
||||
pInfo->estimatedCost /= 2;
|
||||
}
|
||||
}
|
||||
if( iLangid>=0 ){
|
||||
pInfo->aConstraintUsage[iLangid].argvIndex = iNext++;
|
||||
pInfo->estimatedCost--;
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -352,7 +368,14 @@ static int fts3auxFilterMethod(
|
||||
Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor;
|
||||
Fts3Table *pFts3 = ((Fts3auxTable *)pCursor->pVtab)->pFts3Tab;
|
||||
int rc;
|
||||
int isScan;
|
||||
int isScan = 0;
|
||||
int iLangVal = 0; /* Language id to query */
|
||||
|
||||
int iEq = -1; /* Index of term=? value in apVal */
|
||||
int iGe = -1; /* Index of term>=? value in apVal */
|
||||
int iLe = -1; /* Index of term<=? value in apVal */
|
||||
int iLangid = -1; /* Index of languageid=? value in apVal */
|
||||
int iNext = 0;
|
||||
|
||||
UNUSED_PARAMETER(nVal);
|
||||
UNUSED_PARAMETER(idxStr);
|
||||
@@ -362,7 +385,21 @@ static int fts3auxFilterMethod(
|
||||
|| idxNum==FTS4AUX_LE_CONSTRAINT || idxNum==FTS4AUX_GE_CONSTRAINT
|
||||
|| idxNum==(FTS4AUX_LE_CONSTRAINT|FTS4AUX_GE_CONSTRAINT)
|
||||
);
|
||||
isScan = (idxNum!=FTS4AUX_EQ_CONSTRAINT);
|
||||
|
||||
if( idxNum==FTS4AUX_EQ_CONSTRAINT ){
|
||||
iEq = iNext++;
|
||||
}else{
|
||||
isScan = 1;
|
||||
if( idxNum & FTS4AUX_GE_CONSTRAINT ){
|
||||
iGe = iNext++;
|
||||
}
|
||||
if( idxNum & FTS4AUX_LE_CONSTRAINT ){
|
||||
iLe = iNext++;
|
||||
}
|
||||
}
|
||||
if( iNext<nVal ){
|
||||
iLangid = iNext++;
|
||||
}
|
||||
|
||||
/* In case this cursor is being reused, close and zero it. */
|
||||
testcase(pCsr->filter.zTerm);
|
||||
@@ -374,22 +411,35 @@ static int fts3auxFilterMethod(
|
||||
pCsr->filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY;
|
||||
if( isScan ) pCsr->filter.flags |= FTS3_SEGMENT_SCAN;
|
||||
|
||||
if( idxNum&(FTS4AUX_EQ_CONSTRAINT|FTS4AUX_GE_CONSTRAINT) ){
|
||||
if( iEq>=0 || iGe>=0 ){
|
||||
const unsigned char *zStr = sqlite3_value_text(apVal[0]);
|
||||
assert( (iEq==0 && iGe==-1) || (iEq==-1 && iGe==0) );
|
||||
if( zStr ){
|
||||
pCsr->filter.zTerm = sqlite3_mprintf("%s", zStr);
|
||||
pCsr->filter.nTerm = sqlite3_value_bytes(apVal[0]);
|
||||
if( pCsr->filter.zTerm==0 ) return SQLITE_NOMEM;
|
||||
}
|
||||
}
|
||||
if( idxNum&FTS4AUX_LE_CONSTRAINT ){
|
||||
int iIdx = (idxNum&FTS4AUX_GE_CONSTRAINT) ? 1 : 0;
|
||||
pCsr->zStop = sqlite3_mprintf("%s", sqlite3_value_text(apVal[iIdx]));
|
||||
pCsr->nStop = sqlite3_value_bytes(apVal[iIdx]);
|
||||
|
||||
if( iLe>=0 ){
|
||||
pCsr->zStop = sqlite3_mprintf("%s", sqlite3_value_text(apVal[iLe]));
|
||||
pCsr->nStop = sqlite3_value_bytes(apVal[iLe]);
|
||||
if( pCsr->zStop==0 ) return SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
if( iLangid>=0 ){
|
||||
iLangVal = sqlite3_value_int(apVal[iLangid]);
|
||||
|
||||
rc = sqlite3Fts3SegReaderCursor(pFts3, 0, 0, FTS3_SEGCURSOR_ALL,
|
||||
/* If the user specified a negative value for the languageid, use zero
|
||||
** instead. This works, as the "languageid=?" constraint will also
|
||||
** be tested by the VDBE layer. The test will always be false (since
|
||||
** this module will not return a row with a negative languageid), and
|
||||
** so the overall query will return zero rows. */
|
||||
if( iLangVal<0 ) iLangVal = 0;
|
||||
}
|
||||
pCsr->iLangid = iLangVal;
|
||||
|
||||
rc = sqlite3Fts3SegReaderCursor(pFts3, iLangVal, 0, FTS3_SEGCURSOR_ALL,
|
||||
pCsr->filter.zTerm, pCsr->filter.nTerm, 0, isScan, &pCsr->csr
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
@@ -413,24 +463,37 @@ static int fts3auxEofMethod(sqlite3_vtab_cursor *pCursor){
|
||||
*/
|
||||
static int fts3auxColumnMethod(
|
||||
sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
|
||||
sqlite3_context *pContext, /* Context for sqlite3_result_xxx() calls */
|
||||
sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */
|
||||
int iCol /* Index of column to read value from */
|
||||
){
|
||||
Fts3auxCursor *p = (Fts3auxCursor *)pCursor;
|
||||
|
||||
assert( p->isEof==0 );
|
||||
if( iCol==0 ){ /* Column "term" */
|
||||
sqlite3_result_text(pContext, p->csr.zTerm, p->csr.nTerm, SQLITE_TRANSIENT);
|
||||
}else if( iCol==1 ){ /* Column "col" */
|
||||
if( p->iCol ){
|
||||
sqlite3_result_int(pContext, p->iCol-1);
|
||||
}else{
|
||||
sqlite3_result_text(pContext, "*", -1, SQLITE_STATIC);
|
||||
}
|
||||
}else if( iCol==2 ){ /* Column "documents" */
|
||||
sqlite3_result_int64(pContext, p->aStat[p->iCol].nDoc);
|
||||
}else{ /* Column "occurrences" */
|
||||
sqlite3_result_int64(pContext, p->aStat[p->iCol].nOcc);
|
||||
switch( iCol ){
|
||||
case 0: /* term */
|
||||
sqlite3_result_text(pCtx, p->csr.zTerm, p->csr.nTerm, SQLITE_TRANSIENT);
|
||||
break;
|
||||
|
||||
case 1: /* col */
|
||||
if( p->iCol ){
|
||||
sqlite3_result_int(pCtx, p->iCol-1);
|
||||
}else{
|
||||
sqlite3_result_text(pCtx, "*", -1, SQLITE_STATIC);
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: /* documents */
|
||||
sqlite3_result_int64(pCtx, p->aStat[p->iCol].nDoc);
|
||||
break;
|
||||
|
||||
case 3: /* occurrences */
|
||||
sqlite3_result_int64(pCtx, p->aStat[p->iCol].nOcc);
|
||||
break;
|
||||
|
||||
default: /* languageid */
|
||||
assert( iCol==4 );
|
||||
sqlite3_result_int(pCtx, p->iLangid);
|
||||
break;
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
|
||||
+30
-28
@@ -155,6 +155,11 @@ int sqlite3Fts3OpenTokenizer(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Function getNextNode(), which is called by fts3ExprParse(), may itself
|
||||
** call fts3ExprParse(). So this forward declaration is required.
|
||||
*/
|
||||
static int fts3ExprParse(ParseContext *, const char *, int, Fts3Expr **, int *);
|
||||
|
||||
/*
|
||||
** Extract the next token from buffer z (length n) using the tokenizer
|
||||
@@ -189,7 +194,31 @@ static int getNextToken(
|
||||
int nByte; /* total space to allocate */
|
||||
|
||||
rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition);
|
||||
if( rc==SQLITE_OK ){
|
||||
|
||||
if( (rc==SQLITE_OK || rc==SQLITE_DONE) && sqlite3_fts3_enable_parentheses ){
|
||||
int i;
|
||||
if( rc==SQLITE_DONE ) iStart = n;
|
||||
for(i=0; i<iStart; i++){
|
||||
if( z[i]=='(' ){
|
||||
pParse->nNest++;
|
||||
rc = fts3ExprParse(pParse, &z[i+1], n-i-1, &pRet, &nConsumed);
|
||||
if( rc==SQLITE_OK && !pRet ){
|
||||
rc = SQLITE_DONE;
|
||||
}
|
||||
nConsumed = (int)(i + 1 + nConsumed);
|
||||
break;
|
||||
}
|
||||
|
||||
if( z[i]==')' ){
|
||||
rc = SQLITE_DONE;
|
||||
pParse->nNest--;
|
||||
nConsumed = i+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( nConsumed==0 && rc==SQLITE_OK ){
|
||||
nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken;
|
||||
pRet = (Fts3Expr *)fts3MallocZero(nByte);
|
||||
if( !pRet ){
|
||||
@@ -369,12 +398,6 @@ no_mem:
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
** Function getNextNode(), which is called by fts3ExprParse(), may itself
|
||||
** call fts3ExprParse(). So this forward declaration is required.
|
||||
*/
|
||||
static int fts3ExprParse(ParseContext *, const char *, int, Fts3Expr **, int *);
|
||||
|
||||
/*
|
||||
** The output variable *ppExpr is populated with an allocated Fts3Expr
|
||||
** structure, or set to 0 if the end of the input buffer is reached.
|
||||
@@ -471,27 +494,6 @@ static int getNextNode(
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for an open bracket. */
|
||||
if( sqlite3_fts3_enable_parentheses ){
|
||||
if( *zInput=='(' ){
|
||||
int nConsumed;
|
||||
pParse->nNest++;
|
||||
rc = fts3ExprParse(pParse, &zInput[1], nInput-1, ppExpr, &nConsumed);
|
||||
if( rc==SQLITE_OK && !*ppExpr ){
|
||||
rc = SQLITE_DONE;
|
||||
}
|
||||
*pnConsumed = (int)((zInput - z) + 1 + nConsumed);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Check for a close bracket. */
|
||||
if( *zInput==')' ){
|
||||
pParse->nNest--;
|
||||
*pnConsumed = (int)((zInput - z) + 1);
|
||||
return SQLITE_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
/* See if we are dealing with a quoted phrase. If this is the case, then
|
||||
** search for the closing quote and pass the whole string to getNextString()
|
||||
** for processing. This is easy to do, as fts3 has no syntax for escaping
|
||||
|
||||
@@ -96,13 +96,13 @@ void sqlite3Fts3HashClear(Fts3Hash *pH){
|
||||
*/
|
||||
static int fts3StrHash(const void *pKey, int nKey){
|
||||
const char *z = (const char *)pKey;
|
||||
int h = 0;
|
||||
unsigned h = 0;
|
||||
if( nKey<=0 ) nKey = (int) strlen(z);
|
||||
while( nKey > 0 ){
|
||||
h = (h<<3) ^ h ^ *z++;
|
||||
nKey--;
|
||||
}
|
||||
return h & 0x7fffffff;
|
||||
return (int)(h & 0x7fffffff);
|
||||
}
|
||||
static int fts3StrCompare(const void *pKey1, int n1, const void *pKey2, int n2){
|
||||
if( n1!=n2 ) return 1;
|
||||
|
||||
+45
-29
@@ -403,12 +403,14 @@ static void porter_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
|
||||
/* Step 2 */
|
||||
switch( z[1] ){
|
||||
case 'a':
|
||||
stem(&z, "lanoita", "ate", m_gt_0) ||
|
||||
stem(&z, "lanoit", "tion", m_gt_0);
|
||||
if( !stem(&z, "lanoita", "ate", m_gt_0) ){
|
||||
stem(&z, "lanoit", "tion", m_gt_0);
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
stem(&z, "icne", "ence", m_gt_0) ||
|
||||
stem(&z, "icna", "ance", m_gt_0);
|
||||
if( !stem(&z, "icne", "ence", m_gt_0) ){
|
||||
stem(&z, "icna", "ance", m_gt_0);
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
stem(&z, "rezi", "ize", m_gt_0);
|
||||
@@ -417,43 +419,54 @@ static void porter_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
|
||||
stem(&z, "igol", "log", m_gt_0);
|
||||
break;
|
||||
case 'l':
|
||||
stem(&z, "ilb", "ble", m_gt_0) ||
|
||||
stem(&z, "illa", "al", m_gt_0) ||
|
||||
stem(&z, "iltne", "ent", m_gt_0) ||
|
||||
stem(&z, "ile", "e", m_gt_0) ||
|
||||
stem(&z, "ilsuo", "ous", m_gt_0);
|
||||
if( !stem(&z, "ilb", "ble", m_gt_0)
|
||||
&& !stem(&z, "illa", "al", m_gt_0)
|
||||
&& !stem(&z, "iltne", "ent", m_gt_0)
|
||||
&& !stem(&z, "ile", "e", m_gt_0)
|
||||
){
|
||||
stem(&z, "ilsuo", "ous", m_gt_0);
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
stem(&z, "noitazi", "ize", m_gt_0) ||
|
||||
stem(&z, "noita", "ate", m_gt_0) ||
|
||||
stem(&z, "rota", "ate", m_gt_0);
|
||||
if( !stem(&z, "noitazi", "ize", m_gt_0)
|
||||
&& !stem(&z, "noita", "ate", m_gt_0)
|
||||
){
|
||||
stem(&z, "rota", "ate", m_gt_0);
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
stem(&z, "msila", "al", m_gt_0) ||
|
||||
stem(&z, "ssenevi", "ive", m_gt_0) ||
|
||||
stem(&z, "ssenluf", "ful", m_gt_0) ||
|
||||
stem(&z, "ssensuo", "ous", m_gt_0);
|
||||
if( !stem(&z, "msila", "al", m_gt_0)
|
||||
&& !stem(&z, "ssenevi", "ive", m_gt_0)
|
||||
&& !stem(&z, "ssenluf", "ful", m_gt_0)
|
||||
){
|
||||
stem(&z, "ssensuo", "ous", m_gt_0);
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
stem(&z, "itila", "al", m_gt_0) ||
|
||||
stem(&z, "itivi", "ive", m_gt_0) ||
|
||||
stem(&z, "itilib", "ble", m_gt_0);
|
||||
if( !stem(&z, "itila", "al", m_gt_0)
|
||||
&& !stem(&z, "itivi", "ive", m_gt_0)
|
||||
){
|
||||
stem(&z, "itilib", "ble", m_gt_0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Step 3 */
|
||||
switch( z[0] ){
|
||||
case 'e':
|
||||
stem(&z, "etaci", "ic", m_gt_0) ||
|
||||
stem(&z, "evita", "", m_gt_0) ||
|
||||
stem(&z, "ezila", "al", m_gt_0);
|
||||
if( !stem(&z, "etaci", "ic", m_gt_0)
|
||||
&& !stem(&z, "evita", "", m_gt_0)
|
||||
){
|
||||
stem(&z, "ezila", "al", m_gt_0);
|
||||
}
|
||||
break;
|
||||
case 'i':
|
||||
stem(&z, "itici", "ic", m_gt_0);
|
||||
break;
|
||||
case 'l':
|
||||
stem(&z, "laci", "ic", m_gt_0) ||
|
||||
stem(&z, "luf", "", m_gt_0);
|
||||
if( !stem(&z, "laci", "ic", m_gt_0) ){
|
||||
stem(&z, "luf", "", m_gt_0);
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
stem(&z, "ssen", "", m_gt_0);
|
||||
@@ -494,9 +507,11 @@ static void porter_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
|
||||
z += 3;
|
||||
}
|
||||
}else if( z[2]=='e' ){
|
||||
stem(&z, "tneme", "", m_gt_1) ||
|
||||
stem(&z, "tnem", "", m_gt_1) ||
|
||||
stem(&z, "tne", "", m_gt_1);
|
||||
if( !stem(&z, "tneme", "", m_gt_1)
|
||||
&& !stem(&z, "tnem", "", m_gt_1)
|
||||
){
|
||||
stem(&z, "tne", "", m_gt_1);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -515,8 +530,9 @@ static void porter_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
stem(&z, "eta", "", m_gt_1) ||
|
||||
stem(&z, "iti", "", m_gt_1);
|
||||
if( !stem(&z, "eta", "", m_gt_1) ){
|
||||
stem(&z, "iti", "", m_gt_1);
|
||||
}
|
||||
break;
|
||||
case 'u':
|
||||
if( z[0]=='s' && z[2]=='o' && m_gt_1(z+3) ){
|
||||
|
||||
@@ -128,7 +128,7 @@ struct StrBuffer {
|
||||
*/
|
||||
static void fts3GetDeltaPosition(char **pp, int *piPos){
|
||||
int iVal;
|
||||
*pp += sqlite3Fts3GetVarint32(*pp, &iVal);
|
||||
*pp += fts3GetVarint32(*pp, &iVal);
|
||||
*piPos += (iVal-2);
|
||||
}
|
||||
|
||||
|
||||
@@ -517,6 +517,51 @@ static int fts3_test_tokenizer_cmd(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int fts3_test_varint_cmd(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
#ifdef SQLITE_ENABLE_FTS3
|
||||
char aBuf[24];
|
||||
int rc;
|
||||
Tcl_WideInt w, w2;
|
||||
int nByte, nByte2;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "INTEGER");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
rc = Tcl_GetWideIntFromObj(interp, objv[1], &w);
|
||||
if( rc!=TCL_OK ) return rc;
|
||||
|
||||
nByte = sqlite3Fts3PutVarint(aBuf, w);
|
||||
nByte2 = sqlite3Fts3GetVarint(aBuf, &w2);
|
||||
if( w!=w2 || nByte!=nByte2 ){
|
||||
char *zErr = sqlite3_mprintf("error testing %lld", w);
|
||||
Tcl_ResetResult(interp);
|
||||
Tcl_AppendResult(interp, zErr, 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( w<=2147483647 && w>=0 ){
|
||||
int i;
|
||||
nByte2 = fts3GetVarint32(aBuf, &i);
|
||||
if( (int)w!=i || nByte!=nByte2 ){
|
||||
char *zErr = sqlite3_mprintf("error testing %lld (32-bit)", w);
|
||||
Tcl_ResetResult(interp);
|
||||
Tcl_AppendResult(interp, zErr, 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
UNUSED_PARAMETER(clientData);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** End of tokenizer code.
|
||||
**************************************************************************/
|
||||
@@ -529,6 +574,10 @@ int Sqlitetestfts3_Init(Tcl_Interp *interp){
|
||||
Tcl_CreateObjCommand(
|
||||
interp, "fts3_test_tokenizer", fts3_test_tokenizer_cmd, 0, 0
|
||||
);
|
||||
|
||||
Tcl_CreateObjCommand(
|
||||
interp, "fts3_test_varint", fts3_test_varint_cmd, 0, 0
|
||||
);
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_FTS3 || SQLITE_ENABLE_FTS4 */
|
||||
|
||||
+12
-9
@@ -1344,8 +1344,8 @@ static int fts3SegReaderNext(
|
||||
|
||||
/* Because of the FTS3_NODE_PADDING bytes of padding, the following is
|
||||
** safe (no risk of overread) even if the node data is corrupted. */
|
||||
pNext += sqlite3Fts3GetVarint32(pNext, &nPrefix);
|
||||
pNext += sqlite3Fts3GetVarint32(pNext, &nSuffix);
|
||||
pNext += fts3GetVarint32(pNext, &nPrefix);
|
||||
pNext += fts3GetVarint32(pNext, &nSuffix);
|
||||
if( nPrefix<0 || nSuffix<=0
|
||||
|| &pNext[nSuffix]>&pReader->aNode[pReader->nNode]
|
||||
){
|
||||
@@ -1368,7 +1368,7 @@ static int fts3SegReaderNext(
|
||||
memcpy(&pReader->zTerm[nPrefix], pNext, nSuffix);
|
||||
pReader->nTerm = nPrefix+nSuffix;
|
||||
pNext += nSuffix;
|
||||
pNext += sqlite3Fts3GetVarint32(pNext, &pReader->nDoclist);
|
||||
pNext += fts3GetVarint32(pNext, &pReader->nDoclist);
|
||||
pReader->aDoclist = pNext;
|
||||
pReader->pOffsetList = 0;
|
||||
|
||||
@@ -2529,7 +2529,7 @@ static void fts3ColumnFilter(
|
||||
break;
|
||||
}
|
||||
p = &pList[1];
|
||||
p += sqlite3Fts3GetVarint32(p, &iCurrent);
|
||||
p += fts3GetVarint32(p, &iCurrent);
|
||||
}
|
||||
|
||||
if( bZero && &pList[nList]!=pEnd ){
|
||||
@@ -3494,9 +3494,9 @@ static int nodeReaderNext(NodeReader *p){
|
||||
p->aNode = 0;
|
||||
}else{
|
||||
if( bFirst==0 ){
|
||||
p->iOff += sqlite3Fts3GetVarint32(&p->aNode[p->iOff], &nPrefix);
|
||||
p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &nPrefix);
|
||||
}
|
||||
p->iOff += sqlite3Fts3GetVarint32(&p->aNode[p->iOff], &nSuffix);
|
||||
p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &nSuffix);
|
||||
|
||||
blobGrowBuffer(&p->term, nPrefix+nSuffix, &rc);
|
||||
if( rc==SQLITE_OK ){
|
||||
@@ -3504,7 +3504,7 @@ static int nodeReaderNext(NodeReader *p){
|
||||
p->term.n = nPrefix+nSuffix;
|
||||
p->iOff += nSuffix;
|
||||
if( p->iChild==0 ){
|
||||
p->iOff += sqlite3Fts3GetVarint32(&p->aNode[p->iOff], &p->nDoclist);
|
||||
p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &p->nDoclist);
|
||||
p->aDoclist = &p->aNode[p->iOff];
|
||||
p->iOff += p->nDoclist;
|
||||
}
|
||||
@@ -4556,7 +4556,7 @@ static int fts3IncrmergeHintPop(Blob *pHint, i64 *piAbsLevel, int *pnInput){
|
||||
|
||||
pHint->n = i;
|
||||
i += sqlite3Fts3GetVarint(&pHint->a[i], piAbsLevel);
|
||||
i += sqlite3Fts3GetVarint32(&pHint->a[i], pnInput);
|
||||
i += fts3GetVarint32(&pHint->a[i], pnInput);
|
||||
if( i!=nHint ) return SQLITE_CORRUPT_VTAB;
|
||||
|
||||
return SQLITE_OK;
|
||||
@@ -4780,7 +4780,7 @@ static int fts3DoAutoincrmerge(
|
||||
if( rc ) return rc;
|
||||
}
|
||||
rc = fts3SqlStmt(p, SQL_REPLACE_STAT, &pStmt, 0);
|
||||
if( rc ) return rc;;
|
||||
if( rc ) return rc;
|
||||
sqlite3_bind_int(pStmt, 1, FTS_STAT_AUTOINCRMERGE);
|
||||
sqlite3_bind_int(pStmt, 2, p->bAutoincrmerge);
|
||||
sqlite3_step(pStmt);
|
||||
@@ -5050,6 +5050,9 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
|
||||
}else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){
|
||||
p->nMaxPendingData = atoi(&zVal[11]);
|
||||
rc = SQLITE_OK;
|
||||
}else if( nVal>21 && 0==sqlite3_strnicmp(zVal, "test-no-incr-doclist=", 21) ){
|
||||
p->bNoIncrDoclist = atoi(&zVal[21]);
|
||||
rc = SQLITE_OK;
|
||||
#endif
|
||||
}else{
|
||||
rc = SQLITE_ERROR;
|
||||
|
||||
+6
-2
@@ -786,6 +786,7 @@ static void amatchFree(amatch_vtab *p){
|
||||
sqlite3_free(p->zVocabTab);
|
||||
sqlite3_free(p->zVocabWord);
|
||||
sqlite3_free(p->zVocabLang);
|
||||
sqlite3_free(p->zSelf);
|
||||
memset(p, 0, sizeof(*p));
|
||||
sqlite3_free(p);
|
||||
}
|
||||
@@ -948,6 +949,9 @@ static void amatchClearCursor(amatch_cursor *pCur){
|
||||
pCur->pAllWords = 0;
|
||||
sqlite3_free(pCur->zInput);
|
||||
pCur->zInput = 0;
|
||||
sqlite3_free(pCur->zBuf);
|
||||
pCur->zBuf = 0;
|
||||
pCur->nBuf = 0;
|
||||
pCur->pCost = 0;
|
||||
pCur->pWord = 0;
|
||||
pCur->pCurrent = 0;
|
||||
@@ -1103,7 +1107,7 @@ static int amatchNext(sqlite3_vtab_cursor *cur){
|
||||
char *zSql;
|
||||
if( p->zVocabLang && p->zVocabLang[0] ){
|
||||
zSql = sqlite3_mprintf(
|
||||
"SELECT \"%s\" FROM \"%s\"",
|
||||
"SELECT \"%w\" FROM \"%w\"",
|
||||
" WHERE \"%w\">=?1 AND \"%w\"=?2"
|
||||
" ORDER BY 1",
|
||||
p->zVocabWord, p->zVocabTab,
|
||||
@@ -1111,7 +1115,7 @@ static int amatchNext(sqlite3_vtab_cursor *cur){
|
||||
);
|
||||
}else{
|
||||
zSql = sqlite3_mprintf(
|
||||
"SELECT \"%s\" FROM \"%s\""
|
||||
"SELECT \"%w\" FROM \"%w\""
|
||||
" WHERE \"%w\">=?1"
|
||||
" ORDER BY 1",
|
||||
p->zVocabWord, p->zVocabTab,
|
||||
|
||||
+16
-3
@@ -38,6 +38,19 @@
|
||||
** out) run the following query:
|
||||
**
|
||||
** SELECT next_char('cha','dictionary','word');
|
||||
**
|
||||
** IMPLEMENTATION NOTES:
|
||||
**
|
||||
** The next_char function is implemented using recursive SQL that makes
|
||||
** use of the table name and column name as part of a query. If either
|
||||
** the table name or column name are keywords or contain special characters,
|
||||
** then they should be escaped. For example:
|
||||
**
|
||||
** SELECT next_char('cha','[dictionary]','[word]');
|
||||
**
|
||||
** This also means that the table name can be a subquery:
|
||||
**
|
||||
** SELECT next_char('cha','(SELECT word AS w FROM dictionary)','w');
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
@@ -231,9 +244,9 @@ static void nextCharFunc(
|
||||
zColl = "";
|
||||
}
|
||||
zSql = sqlite3_mprintf(
|
||||
"SELECT \"%w\" FROM \"%w\""
|
||||
" WHERE \"%w\">=(?1 || ?2) %s"
|
||||
" AND \"%w\"<=(?1 || char(1114111)) %s" /* 1114111 == 0x10ffff */
|
||||
"SELECT %s FROM %s"
|
||||
" WHERE %s>=(?1 || ?2) %s"
|
||||
" AND %s<=(?1 || char(1114111)) %s" /* 1114111 == 0x10ffff */
|
||||
" %s"
|
||||
" ORDER BY 1 %s ASC LIMIT 1",
|
||||
zField, zTable, zField, zColl, zField, zColl, zWhereClause, zColl
|
||||
|
||||
+32
-10
@@ -26,8 +26,8 @@ SQLITE_EXTENSION_INIT1
|
||||
# define NEVER(X) 0
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
# include <ctype.h>
|
||||
#endif
|
||||
#include <ctype.h>
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
@@ -1933,7 +1933,6 @@ static int spellfix1Init(
|
||||
#define SPELLFIX_COL_COMMAND 11
|
||||
}
|
||||
if( rc==SQLITE_OK && isCreate ){
|
||||
sqlite3_uint64 r;
|
||||
spellfix1DbExec(&rc, db,
|
||||
"CREATE TABLE IF NOT EXISTS \"%w\".\"%w_vocab\"(\n"
|
||||
" id INTEGER PRIMARY KEY,\n"
|
||||
@@ -1945,11 +1944,10 @@ static int spellfix1Init(
|
||||
");\n",
|
||||
zDbName, zTableName
|
||||
);
|
||||
sqlite3_randomness(sizeof(r), &r);
|
||||
spellfix1DbExec(&rc, db,
|
||||
"CREATE INDEX IF NOT EXISTS \"%w\".\"%w_index_%llx\" "
|
||||
"CREATE INDEX IF NOT EXISTS \"%w\".\"%w_vocab_index_langid_k2\" "
|
||||
"ON \"%w_vocab\"(langid,k2);",
|
||||
zDbName, zModule, r, zTableName
|
||||
zDbName, zModule, zTableName
|
||||
);
|
||||
}
|
||||
for(i=3; rc==SQLITE_OK && i<argc; i++){
|
||||
@@ -2051,6 +2049,7 @@ static int spellfix1Close(sqlite3_vtab_cursor *cur){
|
||||
** (D) scope = $scope
|
||||
** (E) distance < $distance
|
||||
** (F) distance <= $distance
|
||||
** (G) rowid = $rowid
|
||||
**
|
||||
** The plan number is a bit mask formed with these bits:
|
||||
**
|
||||
@@ -2060,8 +2059,9 @@ static int spellfix1Close(sqlite3_vtab_cursor *cur){
|
||||
** 0x08 (D) is found
|
||||
** 0x10 (E) is found
|
||||
** 0x20 (F) is found
|
||||
** 0x40 (G) is found
|
||||
**
|
||||
** filter.argv[*] values contains $str, $langid, $top, and $scope,
|
||||
** filter.argv[*] values contains $str, $langid, $top, $scope and $rowid
|
||||
** if specified and in that order.
|
||||
*/
|
||||
static int spellfix1BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
@@ -2070,6 +2070,7 @@ static int spellfix1BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
int iTopTerm = -1;
|
||||
int iScopeTerm = -1;
|
||||
int iDistTerm = -1;
|
||||
int iRowidTerm = -1;
|
||||
int i;
|
||||
const struct sqlite3_index_constraint *pConstraint;
|
||||
pConstraint = pIdxInfo->aConstraint;
|
||||
@@ -2122,6 +2123,15 @@ static int spellfix1BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
iPlan |= pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ? 16 : 32;
|
||||
iDistTerm = i;
|
||||
}
|
||||
|
||||
/* Terms of the form: distance < $dist or distance <= $dist */
|
||||
if( (iPlan & 64)==0
|
||||
&& pConstraint->iColumn<0
|
||||
&& pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
||||
){
|
||||
iPlan |= 64;
|
||||
iRowidTerm = i;
|
||||
}
|
||||
}
|
||||
if( iPlan&1 ){
|
||||
int idx = 2;
|
||||
@@ -2149,6 +2159,11 @@ static int spellfix1BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
pIdxInfo->aConstraintUsage[iDistTerm].omit = 1;
|
||||
}
|
||||
pIdxInfo->estimatedCost = 1e5;
|
||||
}else if( (iPlan & 64) ){
|
||||
pIdxInfo->idxNum = 64;
|
||||
pIdxInfo->aConstraintUsage[iRowidTerm].argvIndex = 1;
|
||||
pIdxInfo->aConstraintUsage[iRowidTerm].omit = 1;
|
||||
pIdxInfo->estimatedCost = 5;
|
||||
}else{
|
||||
pIdxInfo->idxNum = 0;
|
||||
pIdxInfo->estimatedCost = 1e50;
|
||||
@@ -2465,16 +2480,23 @@ static int spellfix1FilterForFullScan(
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
int rc;
|
||||
int rc = SQLITE_OK;
|
||||
char *zSql;
|
||||
spellfix1_vtab *pVTab = pCur->pVTab;
|
||||
spellfix1ResetCursor(pCur);
|
||||
assert( idxNum==0 || idxNum==64 );
|
||||
zSql = sqlite3_mprintf(
|
||||
"SELECT word, rank, NULL, langid, id FROM \"%w\".\"%w_vocab\"",
|
||||
pVTab->zDbName, pVTab->zTableName);
|
||||
"SELECT word, rank, NULL, langid, id FROM \"%w\".\"%w_vocab\"%s",
|
||||
pVTab->zDbName, pVTab->zTableName,
|
||||
((idxNum & 64) ? " WHERE rowid=?" : "")
|
||||
);
|
||||
if( zSql==0 ) return SQLITE_NOMEM;
|
||||
rc = sqlite3_prepare_v2(pVTab->db, zSql, -1, &pCur->pFullScan, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( rc==SQLITE_OK && (idxNum & 64) ){
|
||||
assert( argc==1 );
|
||||
rc = sqlite3_bind_value(pCur->pFullScan, 1, argv[0]);
|
||||
}
|
||||
pCur->nRow = pCur->iRow = 0;
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_step(pCur->pFullScan);
|
||||
@@ -2672,7 +2694,7 @@ static int spellfix1Update(
|
||||
const char *zCmd =
|
||||
(const char*)sqlite3_value_text(argv[SPELLFIX_COL_COMMAND+2]);
|
||||
if( zCmd==0 ){
|
||||
pVTab->zErrMsg = sqlite3_mprintf("%s.word may not be NULL",
|
||||
pVTab->zErrMsg = sqlite3_mprintf("NOT NULL constraint failed: %s.word",
|
||||
p->zTableName);
|
||||
return SQLITE_CONSTRAINT_NOTNULL;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,512 @@
|
||||
/*
|
||||
** 2013-10-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.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** This SQLite extension implements functions tointeger(X) and toreal(X).
|
||||
**
|
||||
** If X is an integer, real, or string value that can be
|
||||
** losslessly represented as an integer, then tointeger(X)
|
||||
** returns the corresponding integer value.
|
||||
** If X is an 8-byte BLOB then that blob is interpreted as
|
||||
** a signed two-compliment little-endian encoding of an integer
|
||||
** and tointeger(X) returns the corresponding integer value.
|
||||
** Otherwise tointeger(X) return NULL.
|
||||
**
|
||||
** If X is an integer, real, or string value that can be
|
||||
** convert into a real number, preserving at least 15 digits
|
||||
** of precision, then toreal(X) returns the corresponding real value.
|
||||
** If X is an 8-byte BLOB then that blob is interpreted as
|
||||
** a 64-bit IEEE754 big-endian floating point value
|
||||
** and toreal(X) returns the corresponding real value.
|
||||
** Otherwise toreal(X) return NULL.
|
||||
**
|
||||
** Note that tointeger(X) of an 8-byte BLOB assumes a little-endian
|
||||
** encoding whereas toreal(X) of an 8-byte BLOB assumes a big-endian
|
||||
** encoding.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
** Determine if this is running on a big-endian or little-endian
|
||||
** processor
|
||||
*/
|
||||
#if defined(i386) || defined(__i386__) || defined(_M_IX86)\
|
||||
|| defined(__x86_64) || defined(__x86_64__)
|
||||
# define TOTYPE_BIGENDIAN 0
|
||||
# define TOTYPE_LITTLEENDIAN 1
|
||||
#else
|
||||
const int totype_one = 1;
|
||||
# define TOTYPE_BIGENDIAN (*(char *)(&totype_one)==0)
|
||||
# define TOTYPE_LITTLEENDIAN (*(char *)(&totype_one)==1)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Constants for the largest and smallest possible 64-bit signed integers.
|
||||
** These macros are designed to work correctly on both 32-bit and 64-bit
|
||||
** compilers.
|
||||
*/
|
||||
#ifndef LARGEST_INT64
|
||||
# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
|
||||
#endif
|
||||
|
||||
#ifndef SMALLEST_INT64
|
||||
# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Return TRUE if character c is a whitespace character
|
||||
*/
|
||||
static int totypeIsspace(unsigned char c){
|
||||
return c==' ' || c=='\t' || c=='\n' || c=='\v' || c=='\f' || c=='\r';
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if character c is a digit
|
||||
*/
|
||||
static int totypeIsdigit(unsigned char c){
|
||||
return c>='0' && c<='9';
|
||||
}
|
||||
|
||||
/*
|
||||
** Compare the 19-character string zNum against the text representation
|
||||
** value 2^63: 9223372036854775808. Return negative, zero, or positive
|
||||
** if zNum is less than, equal to, or greater than the string.
|
||||
** Note that zNum must contain exactly 19 characters.
|
||||
**
|
||||
** Unlike memcmp() this routine is guaranteed to return the difference
|
||||
** in the values of the last digit if the only difference is in the
|
||||
** last digit. So, for example,
|
||||
**
|
||||
** totypeCompare2pow63("9223372036854775800")
|
||||
**
|
||||
** will return -8.
|
||||
*/
|
||||
static int totypeCompare2pow63(const char *zNum){
|
||||
int c = 0;
|
||||
int i;
|
||||
/* 012345678901234567 */
|
||||
const char *pow63 = "922337203685477580";
|
||||
for(i=0; c==0 && i<18; i++){
|
||||
c = (zNum[i]-pow63[i])*10;
|
||||
}
|
||||
if( c==0 ){
|
||||
c = zNum[18] - '8';
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert zNum to a 64-bit signed integer.
|
||||
**
|
||||
** If the zNum value is representable as a 64-bit twos-complement
|
||||
** integer, then write that value into *pNum and return 0.
|
||||
**
|
||||
** If zNum is exactly 9223372036854665808, return 2. This special
|
||||
** case is broken out because while 9223372036854665808 cannot be a
|
||||
** signed 64-bit integer, its negative -9223372036854665808 can be.
|
||||
**
|
||||
** If zNum is too big for a 64-bit integer and is not
|
||||
** 9223372036854665808 or if zNum contains any non-numeric text,
|
||||
** then return 1.
|
||||
**
|
||||
** The string is not necessarily zero-terminated.
|
||||
*/
|
||||
static int totypeAtoi64(const char *zNum, sqlite3_int64 *pNum, int length){
|
||||
sqlite3_uint64 u = 0;
|
||||
int neg = 0; /* assume positive */
|
||||
int i;
|
||||
int c = 0;
|
||||
int nonNum = 0;
|
||||
const char *zStart;
|
||||
const char *zEnd = zNum + length;
|
||||
|
||||
while( zNum<zEnd && totypeIsspace(*zNum) ) zNum++;
|
||||
if( zNum<zEnd ){
|
||||
if( *zNum=='-' ){
|
||||
neg = 1;
|
||||
zNum++;
|
||||
}else if( *zNum=='+' ){
|
||||
zNum++;
|
||||
}
|
||||
}
|
||||
zStart = zNum;
|
||||
while( zNum<zEnd && zNum[0]=='0' ){ zNum++; } /* Skip leading zeros. */
|
||||
for(i=0; &zNum[i]<zEnd && (c=zNum[i])>='0' && c<='9'; i++){
|
||||
u = u*10 + c - '0';
|
||||
}
|
||||
if( u>LARGEST_INT64 ){
|
||||
*pNum = SMALLEST_INT64;
|
||||
}else if( neg ){
|
||||
*pNum = -(sqlite3_int64)u;
|
||||
}else{
|
||||
*pNum = (sqlite3_int64)u;
|
||||
}
|
||||
if( (c!=0 && &zNum[i]<zEnd) || (i==0 && zStart==zNum) || i>19 || nonNum ){
|
||||
/* zNum is empty or contains non-numeric text or is longer
|
||||
** than 19 digits (thus guaranteeing that it is too large) */
|
||||
return 1;
|
||||
}else if( i<19 ){
|
||||
/* Less than 19 digits, so we know that it fits in 64 bits */
|
||||
assert( u<=LARGEST_INT64 );
|
||||
return 0;
|
||||
}else{
|
||||
/* zNum is a 19-digit numbers. Compare it against 9223372036854775808. */
|
||||
c = totypeCompare2pow63(zNum);
|
||||
if( c<0 ){
|
||||
/* zNum is less than 9223372036854775808 so it fits */
|
||||
assert( u<=LARGEST_INT64 );
|
||||
return 0;
|
||||
}else if( c>0 ){
|
||||
/* zNum is greater than 9223372036854775808 so it overflows */
|
||||
return 1;
|
||||
}else{
|
||||
/* zNum is exactly 9223372036854775808. Fits if negative. The
|
||||
** special case 2 overflow if positive */
|
||||
assert( u-1==LARGEST_INT64 );
|
||||
assert( (*pNum)==SMALLEST_INT64 );
|
||||
return neg ? 0 : 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** The string z[] is an text representation of a real number.
|
||||
** Convert this string to a double and write it into *pResult.
|
||||
**
|
||||
** The string is not necessarily zero-terminated.
|
||||
**
|
||||
** Return TRUE if the result is a valid real number (or integer) and FALSE
|
||||
** if the string is empty or contains extraneous text. Valid numbers
|
||||
** are in one of these formats:
|
||||
**
|
||||
** [+-]digits[E[+-]digits]
|
||||
** [+-]digits.[digits][E[+-]digits]
|
||||
** [+-].digits[E[+-]digits]
|
||||
**
|
||||
** Leading and trailing whitespace is ignored for the purpose of determining
|
||||
** validity.
|
||||
**
|
||||
** If some prefix of the input string is a valid number, this routine
|
||||
** returns FALSE but it still converts the prefix and writes the result
|
||||
** into *pResult.
|
||||
*/
|
||||
static int totypeAtoF(const char *z, double *pResult, int length){
|
||||
const char *zEnd = z + length;
|
||||
/* sign * significand * (10 ^ (esign * exponent)) */
|
||||
int sign = 1; /* sign of significand */
|
||||
sqlite3_int64 s = 0; /* significand */
|
||||
int d = 0; /* adjust exponent for shifting decimal point */
|
||||
int esign = 1; /* sign of exponent */
|
||||
int e = 0; /* exponent */
|
||||
int eValid = 1; /* True exponent is either not used or is well-formed */
|
||||
double result;
|
||||
int nDigits = 0;
|
||||
int nonNum = 0;
|
||||
|
||||
*pResult = 0.0; /* Default return value, in case of an error */
|
||||
|
||||
/* skip leading spaces */
|
||||
while( z<zEnd && totypeIsspace(*z) ) z++;
|
||||
if( z>=zEnd ) return 0;
|
||||
|
||||
/* get sign of significand */
|
||||
if( *z=='-' ){
|
||||
sign = -1;
|
||||
z++;
|
||||
}else if( *z=='+' ){
|
||||
z++;
|
||||
}
|
||||
|
||||
/* skip leading zeroes */
|
||||
while( z<zEnd && z[0]=='0' ) z++, nDigits++;
|
||||
|
||||
/* copy max significant digits to significand */
|
||||
while( z<zEnd && totypeIsdigit(*z) && s<((LARGEST_INT64-9)/10) ){
|
||||
s = s*10 + (*z - '0');
|
||||
z++, nDigits++;
|
||||
}
|
||||
|
||||
/* skip non-significant significand digits
|
||||
** (increase exponent by d to shift decimal left) */
|
||||
while( z<zEnd && totypeIsdigit(*z) ) z++, nDigits++, d++;
|
||||
if( z>=zEnd ) goto totype_atof_calc;
|
||||
|
||||
/* if decimal point is present */
|
||||
if( *z=='.' ){
|
||||
z++;
|
||||
/* copy digits from after decimal to significand
|
||||
** (decrease exponent by d to shift decimal right) */
|
||||
while( z<zEnd && totypeIsdigit(*z) && s<((LARGEST_INT64-9)/10) ){
|
||||
s = s*10 + (*z - '0');
|
||||
z++, nDigits++, d--;
|
||||
}
|
||||
/* skip non-significant digits */
|
||||
while( z<zEnd && totypeIsdigit(*z) ) z++, nDigits++;
|
||||
}
|
||||
if( z>=zEnd ) goto totype_atof_calc;
|
||||
|
||||
/* if exponent is present */
|
||||
if( *z=='e' || *z=='E' ){
|
||||
z++;
|
||||
eValid = 0;
|
||||
if( z>=zEnd ) goto totype_atof_calc;
|
||||
/* get sign of exponent */
|
||||
if( *z=='-' ){
|
||||
esign = -1;
|
||||
z++;
|
||||
}else if( *z=='+' ){
|
||||
z++;
|
||||
}
|
||||
/* copy digits to exponent */
|
||||
while( z<zEnd && totypeIsdigit(*z) ){
|
||||
e = e<10000 ? (e*10 + (*z - '0')) : 10000;
|
||||
z++;
|
||||
eValid = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* skip trailing spaces */
|
||||
if( nDigits && eValid ){
|
||||
while( z<zEnd && totypeIsspace(*z) ) z++;
|
||||
}
|
||||
|
||||
totype_atof_calc:
|
||||
/* adjust exponent by d, and update sign */
|
||||
e = (e*esign) + d;
|
||||
if( e<0 ) {
|
||||
esign = -1;
|
||||
e *= -1;
|
||||
} else {
|
||||
esign = 1;
|
||||
}
|
||||
|
||||
/* if 0 significand */
|
||||
if( !s ) {
|
||||
/* In the IEEE 754 standard, zero is signed.
|
||||
** Add the sign if we've seen at least one digit */
|
||||
result = (sign<0 && nDigits) ? -(double)0 : (double)0;
|
||||
} else {
|
||||
/* attempt to reduce exponent */
|
||||
if( esign>0 ){
|
||||
while( s<(LARGEST_INT64/10) && e>0 ) e--,s*=10;
|
||||
}else{
|
||||
while( !(s%10) && e>0 ) e--,s/=10;
|
||||
}
|
||||
|
||||
/* adjust the sign of significand */
|
||||
s = sign<0 ? -s : s;
|
||||
|
||||
/* if exponent, scale significand as appropriate
|
||||
** and store in result. */
|
||||
if( e ){
|
||||
double scale = 1.0;
|
||||
/* attempt to handle extremely small/large numbers better */
|
||||
if( e>307 && e<342 ){
|
||||
while( e%308 ) { scale *= 1.0e+1; e -= 1; }
|
||||
if( esign<0 ){
|
||||
result = s / scale;
|
||||
result /= 1.0e+308;
|
||||
}else{
|
||||
result = s * scale;
|
||||
result *= 1.0e+308;
|
||||
}
|
||||
}else if( e>=342 ){
|
||||
if( esign<0 ){
|
||||
result = 0.0*s;
|
||||
}else{
|
||||
result = 1e308*1e308*s; /* Infinity */
|
||||
}
|
||||
}else{
|
||||
/* 1.0e+22 is the largest power of 10 than can be
|
||||
** represented exactly. */
|
||||
while( e%22 ) { scale *= 1.0e+1; e -= 1; }
|
||||
while( e>0 ) { scale *= 1.0e+22; e -= 22; }
|
||||
if( esign<0 ){
|
||||
result = s / scale;
|
||||
}else{
|
||||
result = s * scale;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = (double)s;
|
||||
}
|
||||
}
|
||||
|
||||
/* store the result */
|
||||
*pResult = result;
|
||||
|
||||
/* return true if number and no extra non-whitespace chracters after */
|
||||
return z>=zEnd && nDigits>0 && eValid && nonNum==0;
|
||||
}
|
||||
|
||||
/*
|
||||
** tointeger(X): If X is any value (integer, double, blob, or string) that
|
||||
** can be losslessly converted into an integer, then make the conversion and
|
||||
** return the result. Otherwise, return NULL.
|
||||
*/
|
||||
static void tointegerFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
assert( argc==1 );
|
||||
(void)argc;
|
||||
switch( sqlite3_value_type(argv[0]) ){
|
||||
case SQLITE_FLOAT: {
|
||||
double rVal = sqlite3_value_double(argv[0]);
|
||||
sqlite3_int64 iVal = (sqlite3_int64)rVal;
|
||||
if( rVal==(double)iVal ){
|
||||
sqlite3_result_int64(context, iVal);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SQLITE_INTEGER: {
|
||||
sqlite3_result_int64(context, sqlite3_value_int64(argv[0]));
|
||||
break;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
const unsigned char *zBlob = sqlite3_value_blob(argv[0]);
|
||||
if( zBlob ){
|
||||
int nBlob = sqlite3_value_bytes(argv[0]);
|
||||
if( nBlob==sizeof(sqlite3_int64) ){
|
||||
sqlite3_int64 iVal;
|
||||
if( TOTYPE_BIGENDIAN ){
|
||||
int i;
|
||||
unsigned char zBlobRev[sizeof(sqlite3_int64)];
|
||||
for(i=0; i<sizeof(sqlite3_int64); i++){
|
||||
zBlobRev[i] = zBlob[sizeof(sqlite3_int64)-1-i];
|
||||
}
|
||||
memcpy(&iVal, zBlobRev, sizeof(sqlite3_int64));
|
||||
}else{
|
||||
memcpy(&iVal, zBlob, sizeof(sqlite3_int64));
|
||||
}
|
||||
sqlite3_result_int64(context, iVal);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SQLITE_TEXT: {
|
||||
const unsigned char *zStr = sqlite3_value_text(argv[0]);
|
||||
if( zStr ){
|
||||
int nStr = sqlite3_value_bytes(argv[0]);
|
||||
if( nStr && !totypeIsspace(zStr[0]) ){
|
||||
sqlite3_int64 iVal;
|
||||
if( !totypeAtoi64((const char*)zStr, &iVal, nStr) ){
|
||||
sqlite3_result_int64(context, iVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assert( sqlite3_value_type(argv[0])==SQLITE_NULL );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** toreal(X): If X is any value (integer, double, blob, or string) that can
|
||||
** be losslessly converted into a real number, then do so and return that
|
||||
** real number. Otherwise return NULL.
|
||||
*/
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(disable: 4748)
|
||||
#pragma optimize("", off)
|
||||
#endif
|
||||
static void torealFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
assert( argc==1 );
|
||||
(void)argc;
|
||||
switch( sqlite3_value_type(argv[0]) ){
|
||||
case SQLITE_FLOAT: {
|
||||
sqlite3_result_double(context, sqlite3_value_double(argv[0]));
|
||||
break;
|
||||
}
|
||||
case SQLITE_INTEGER: {
|
||||
sqlite3_int64 iVal = sqlite3_value_int64(argv[0]);
|
||||
double rVal = (double)iVal;
|
||||
if( iVal==(sqlite3_int64)rVal ){
|
||||
sqlite3_result_double(context, rVal);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
const unsigned char *zBlob = sqlite3_value_blob(argv[0]);
|
||||
if( zBlob ){
|
||||
int nBlob = sqlite3_value_bytes(argv[0]);
|
||||
if( nBlob==sizeof(double) ){
|
||||
double rVal;
|
||||
if( TOTYPE_LITTLEENDIAN ){
|
||||
int i;
|
||||
unsigned char zBlobRev[sizeof(double)];
|
||||
for(i=0; i<sizeof(double); i++){
|
||||
zBlobRev[i] = zBlob[sizeof(double)-1-i];
|
||||
}
|
||||
memcpy(&rVal, zBlobRev, sizeof(double));
|
||||
}else{
|
||||
memcpy(&rVal, zBlob, sizeof(double));
|
||||
}
|
||||
sqlite3_result_double(context, rVal);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SQLITE_TEXT: {
|
||||
const unsigned char *zStr = sqlite3_value_text(argv[0]);
|
||||
if( zStr ){
|
||||
int nStr = sqlite3_value_bytes(argv[0]);
|
||||
if( nStr && !totypeIsspace(zStr[0]) && !totypeIsspace(zStr[nStr-1]) ){
|
||||
double rVal;
|
||||
if( totypeAtoF((const char*)zStr, &rVal, nStr) ){
|
||||
sqlite3_result_double(context, rVal);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assert( sqlite3_value_type(argv[0])==SQLITE_NULL );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#if defined(_MSC_VER)
|
||||
#pragma optimize("", on)
|
||||
#pragma warning(default: 4748)
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_totype_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "tointeger", 1, SQLITE_UTF8, 0,
|
||||
tointegerFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "toreal", 1, SQLITE_UTF8, 0,
|
||||
torealFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -0,0 +1,759 @@
|
||||
/*
|
||||
** 2013-10-09
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** This file contains the implementation of an SQLite vfs wrapper for
|
||||
** unix that generates per-database log files of all disk activity.
|
||||
*/
|
||||
|
||||
/*
|
||||
** This module contains code for a wrapper VFS that causes a log of
|
||||
** most VFS calls to be written into a file on disk.
|
||||
**
|
||||
** Each database connection creates a separate log file in the same
|
||||
** directory as the original database and named after the original
|
||||
** database. A unique suffix is added to avoid name collisions.
|
||||
** Separate log files are used so that concurrent processes do not
|
||||
** try to write log operations to the same file at the same instant,
|
||||
** resulting in overwritten or comingled log text.
|
||||
**
|
||||
** Each individual log file records operations by a single database
|
||||
** connection on both the original database and its associated rollback
|
||||
** journal.
|
||||
**
|
||||
** The log files are in the comma-separated-value (CSV) format. The
|
||||
** log files can be imported into an SQLite database using the ".import"
|
||||
** command of the SQLite command-line shell for analysis.
|
||||
**
|
||||
** One technique for using this module is to append the text of this
|
||||
** module to the end of a standard "sqlite3.c" amalgamation file then
|
||||
** add the following compile-time options:
|
||||
**
|
||||
** -DSQLITE_EXTRA_INIT=sqlite3_register_vfslog
|
||||
** -DSQLITE_USE_FCNTL_TRACE
|
||||
**
|
||||
** The first compile-time option causes the sqlite3_register_vfslog()
|
||||
** function, defined below, to be invoked when SQLite is initialized.
|
||||
** That causes this custom VFS to become the default VFS for all
|
||||
** subsequent connections. The SQLITE_USE_FCNTL_TRACE option causes
|
||||
** the SQLite core to issue extra sqlite3_file_control() operations
|
||||
** with SQLITE_FCNTL_TRACE to give some indication of what is going
|
||||
** on in the core.
|
||||
*/
|
||||
|
||||
#include "sqlite3.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#if SQLITE_OS_UNIX
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Forward declaration of objects used by this utility
|
||||
*/
|
||||
typedef struct VLogLog VLogLog;
|
||||
typedef struct VLogVfs VLogVfs;
|
||||
typedef struct VLogFile VLogFile;
|
||||
|
||||
/* There is a pair (an array of size 2) of the following objects for
|
||||
** each database file being logged. The first contains the filename
|
||||
** and is used to log I/O with the main database. The second has
|
||||
** a NULL filename and is used to log I/O for the journal. Both
|
||||
** out pointers are the same.
|
||||
*/
|
||||
struct VLogLog {
|
||||
VLogLog *pNext; /* Next in a list of all active logs */
|
||||
VLogLog **ppPrev; /* Pointer to this in the list */
|
||||
int nRef; /* Number of references to this object */
|
||||
int nFilename; /* Length of zFilename in bytes */
|
||||
char *zFilename; /* Name of database file. NULL for journal */
|
||||
FILE *out; /* Write information here */
|
||||
};
|
||||
|
||||
struct VLogVfs {
|
||||
sqlite3_vfs base; /* VFS methods */
|
||||
sqlite3_vfs *pVfs; /* Parent VFS */
|
||||
};
|
||||
|
||||
struct VLogFile {
|
||||
sqlite3_file base; /* IO methods */
|
||||
sqlite3_file *pReal; /* Underlying file handle */
|
||||
VLogLog *pLog; /* The log file for this file */
|
||||
};
|
||||
|
||||
#define REALVFS(p) (((VLogVfs*)(p))->pVfs)
|
||||
|
||||
/*
|
||||
** Methods for VLogFile
|
||||
*/
|
||||
static int vlogClose(sqlite3_file*);
|
||||
static int vlogRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
|
||||
static int vlogWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
|
||||
static int vlogTruncate(sqlite3_file*, sqlite3_int64 size);
|
||||
static int vlogSync(sqlite3_file*, int flags);
|
||||
static int vlogFileSize(sqlite3_file*, sqlite3_int64 *pSize);
|
||||
static int vlogLock(sqlite3_file*, int);
|
||||
static int vlogUnlock(sqlite3_file*, int);
|
||||
static int vlogCheckReservedLock(sqlite3_file*, int *pResOut);
|
||||
static int vlogFileControl(sqlite3_file*, int op, void *pArg);
|
||||
static int vlogSectorSize(sqlite3_file*);
|
||||
static int vlogDeviceCharacteristics(sqlite3_file*);
|
||||
|
||||
/*
|
||||
** Methods for VLogVfs
|
||||
*/
|
||||
static int vlogOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
|
||||
static int vlogDelete(sqlite3_vfs*, const char *zName, int syncDir);
|
||||
static int vlogAccess(sqlite3_vfs*, const char *zName, int flags, int *);
|
||||
static int vlogFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
|
||||
static void *vlogDlOpen(sqlite3_vfs*, const char *zFilename);
|
||||
static void vlogDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
|
||||
static void (*vlogDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
|
||||
static void vlogDlClose(sqlite3_vfs*, void*);
|
||||
static int vlogRandomness(sqlite3_vfs*, int nByte, char *zOut);
|
||||
static int vlogSleep(sqlite3_vfs*, int microseconds);
|
||||
static int vlogCurrentTime(sqlite3_vfs*, double*);
|
||||
static int vlogGetLastError(sqlite3_vfs*, int, char *);
|
||||
static int vlogCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
|
||||
|
||||
static VLogVfs vlog_vfs = {
|
||||
{
|
||||
1, /* iVersion */
|
||||
0, /* szOsFile (set by register_vlog()) */
|
||||
1024, /* mxPathname */
|
||||
0, /* pNext */
|
||||
"vfslog", /* zName */
|
||||
0, /* pAppData */
|
||||
vlogOpen, /* xOpen */
|
||||
vlogDelete, /* xDelete */
|
||||
vlogAccess, /* xAccess */
|
||||
vlogFullPathname, /* xFullPathname */
|
||||
vlogDlOpen, /* xDlOpen */
|
||||
vlogDlError, /* xDlError */
|
||||
vlogDlSym, /* xDlSym */
|
||||
vlogDlClose, /* xDlClose */
|
||||
vlogRandomness, /* xRandomness */
|
||||
vlogSleep, /* xSleep */
|
||||
vlogCurrentTime, /* xCurrentTime */
|
||||
vlogGetLastError, /* xGetLastError */
|
||||
vlogCurrentTimeInt64 /* xCurrentTimeInt64 */
|
||||
},
|
||||
0
|
||||
};
|
||||
|
||||
static sqlite3_io_methods vlog_io_methods = {
|
||||
1, /* iVersion */
|
||||
vlogClose, /* xClose */
|
||||
vlogRead, /* xRead */
|
||||
vlogWrite, /* xWrite */
|
||||
vlogTruncate, /* xTruncate */
|
||||
vlogSync, /* xSync */
|
||||
vlogFileSize, /* xFileSize */
|
||||
vlogLock, /* xLock */
|
||||
vlogUnlock, /* xUnlock */
|
||||
vlogCheckReservedLock, /* xCheckReservedLock */
|
||||
vlogFileControl, /* xFileControl */
|
||||
vlogSectorSize, /* xSectorSize */
|
||||
vlogDeviceCharacteristics, /* xDeviceCharacteristics */
|
||||
0, /* xShmMap */
|
||||
0, /* xShmLock */
|
||||
0, /* xShmBarrier */
|
||||
0 /* xShmUnmap */
|
||||
};
|
||||
|
||||
#if SQLITE_OS_UNIX && !defined(NO_GETTOD)
|
||||
#include <sys/time.h>
|
||||
static sqlite3_uint64 vlog_time(){
|
||||
struct timeval sTime;
|
||||
gettimeofday(&sTime, 0);
|
||||
return sTime.tv_usec + (sqlite3_uint64)sTime.tv_sec * 1000000;
|
||||
}
|
||||
#elif SQLITE_OS_WIN
|
||||
#include <windows.h>
|
||||
#include <time.h>
|
||||
static sqlite3_uint64 vlog_time(){
|
||||
FILETIME ft;
|
||||
sqlite3_uint64 u64time = 0;
|
||||
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
|
||||
u64time |= ft.dwHighDateTime;
|
||||
u64time <<= 32;
|
||||
u64time |= ft.dwLowDateTime;
|
||||
|
||||
/* ft is 100-nanosecond intervals, we want microseconds */
|
||||
return u64time /(sqlite3_uint64)10;
|
||||
}
|
||||
#else
|
||||
static sqlite3_uint64 vlog_time(){
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Write a message to the log file
|
||||
*/
|
||||
static void vlogLogPrint(
|
||||
VLogLog *pLog, /* The log file to write into */
|
||||
sqlite3_int64 tStart, /* Start time of system call */
|
||||
sqlite3_int64 tElapse, /* Elapse time of system call */
|
||||
const char *zOp, /* Type of system call */
|
||||
sqlite3_int64 iArg1, /* First argument */
|
||||
sqlite3_int64 iArg2, /* Second argument */
|
||||
const char *zArg3, /* Third argument */
|
||||
int iRes /* Result */
|
||||
){
|
||||
char z1[40], z2[40], z3[2000];
|
||||
if( pLog==0 ) return;
|
||||
if( iArg1>=0 ){
|
||||
sqlite3_snprintf(sizeof(z1), z1, "%lld", iArg1);
|
||||
}else{
|
||||
z1[0] = 0;
|
||||
}
|
||||
if( iArg2>=0 ){
|
||||
sqlite3_snprintf(sizeof(z2), z2, "%lld", iArg2);
|
||||
}else{
|
||||
z2[0] = 0;
|
||||
}
|
||||
if( zArg3 ){
|
||||
sqlite3_snprintf(sizeof(z3), z3, "\"%.*w\"", sizeof(z3)-4, zArg3);
|
||||
}else{
|
||||
z3[0] = 0;
|
||||
}
|
||||
fprintf(pLog->out,"%lld,%lld,%s,%d,%s,%s,%s,%d\n",
|
||||
tStart, tElapse, zOp, pLog->zFilename==0, z1, z2, z3, iRes);
|
||||
}
|
||||
|
||||
/*
|
||||
** List of all active log connections. Protected by the master mutex.
|
||||
*/
|
||||
static VLogLog *allLogs = 0;
|
||||
|
||||
/*
|
||||
** Close a VLogLog object
|
||||
*/
|
||||
static void vlogLogClose(VLogLog *p){
|
||||
if( p ){
|
||||
sqlite3_mutex *pMutex;
|
||||
p->nRef--;
|
||||
if( p->nRef>0 || p->zFilename==0 ) return;
|
||||
pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
sqlite3_mutex_enter(pMutex);
|
||||
*p->ppPrev = p->pNext;
|
||||
if( p->pNext ) p->pNext->ppPrev = p->ppPrev;
|
||||
sqlite3_mutex_leave(pMutex);
|
||||
fclose(p->out);
|
||||
sqlite3_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a VLogLog object on the given file
|
||||
*/
|
||||
static VLogLog *vlogLogOpen(const char *zFilename){
|
||||
int nName = (int)strlen(zFilename);
|
||||
int isJournal = 0;
|
||||
sqlite3_mutex *pMutex;
|
||||
VLogLog *pLog, *pTemp;
|
||||
sqlite3_int64 tNow = 0;
|
||||
if( nName>4 && strcmp(zFilename+nName-4,"-wal")==0 ){
|
||||
return 0; /* Do not log wal files */
|
||||
}else
|
||||
if( nName>8 && strcmp(zFilename+nName-8,"-journal")==0 ){
|
||||
nName -= 8;
|
||||
isJournal = 1;
|
||||
}else if( nName>12
|
||||
&& sqlite3_strglob("-mj??????9??", zFilename+nName-12)==0 ){
|
||||
return 0; /* Do not log master journal files */
|
||||
}
|
||||
pTemp = sqlite3_malloc( sizeof(*pLog)*2 + nName + 60 );
|
||||
if( pTemp==0 ) return 0;
|
||||
pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
sqlite3_mutex_enter(pMutex);
|
||||
for(pLog=allLogs; pLog; pLog=pLog->pNext){
|
||||
if( pLog->nFilename==nName && !memcmp(pLog->zFilename, zFilename, nName) ){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( pLog==0 ){
|
||||
pLog = pTemp;
|
||||
pTemp = 0;
|
||||
memset(pLog, 0, sizeof(*pLog)*2);
|
||||
pLog->zFilename = (char*)&pLog[2];
|
||||
tNow = vlog_time();
|
||||
sqlite3_snprintf(nName+60, pLog->zFilename, "%.*s-debuglog-%lld",
|
||||
nName, zFilename, tNow);
|
||||
pLog->out = fopen(pLog->zFilename, "a");
|
||||
if( pLog->out==0 ){
|
||||
sqlite3_mutex_leave(pMutex);
|
||||
sqlite3_free(pLog);
|
||||
return 0;
|
||||
}
|
||||
pLog->nFilename = nName;
|
||||
pLog[1].out = pLog[0].out;
|
||||
pLog->ppPrev = &allLogs;
|
||||
if( allLogs ) allLogs->ppPrev = &pLog->pNext;
|
||||
pLog->pNext = allLogs;
|
||||
allLogs = pLog;
|
||||
}
|
||||
sqlite3_mutex_leave(pMutex);
|
||||
if( pTemp ){
|
||||
sqlite3_free(pTemp);
|
||||
}else{
|
||||
#if SQLITE_OS_UNIX
|
||||
char zHost[200];
|
||||
zHost[0] = 0;
|
||||
gethostname(zHost, sizeof(zHost)-1);
|
||||
zHost[sizeof(zHost)-1] = 0;
|
||||
vlogLogPrint(pLog, tNow, 0, "IDENT", getpid(), -1, zHost, 0);
|
||||
#endif
|
||||
}
|
||||
if( pLog && isJournal ) pLog++;
|
||||
pLog->nRef++;
|
||||
return pLog;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Close an vlog-file.
|
||||
*/
|
||||
static int vlogClose(sqlite3_file *pFile){
|
||||
sqlite3_uint64 tStart, tElapse;
|
||||
int rc = SQLITE_OK;
|
||||
VLogFile *p = (VLogFile *)pFile;
|
||||
|
||||
tStart = vlog_time();
|
||||
if( p->pReal->pMethods ){
|
||||
rc = p->pReal->pMethods->xClose(p->pReal);
|
||||
}
|
||||
tElapse = vlog_time() - tStart;
|
||||
vlogLogPrint(p->pLog, tStart, tElapse, "CLOSE", -1, -1, 0, rc);
|
||||
vlogLogClose(p->pLog);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Compute signature for a block of content.
|
||||
**
|
||||
** For blocks of 16 or fewer bytes, the signature is just a hex dump of
|
||||
** the entire block.
|
||||
**
|
||||
** For blocks of more than 16 bytes, the signature is a hex dump of the
|
||||
** first 8 bytes followed by a 64-bit has of the entire block.
|
||||
*/
|
||||
static void vlogSignature(unsigned char *p, int n, char *zCksum){
|
||||
unsigned int s0 = 0, s1 = 0;
|
||||
unsigned int *pI;
|
||||
int i;
|
||||
if( n<=16 ){
|
||||
for(i=0; i<n; i++) sqlite3_snprintf(3, zCksum+i*2, "%02x", p[i]);
|
||||
}else{
|
||||
pI = (unsigned int*)p;
|
||||
for(i=0; i<n-7; i+=8){
|
||||
s0 += pI[0] + s1;
|
||||
s1 += pI[1] + s0;
|
||||
pI += 2;
|
||||
}
|
||||
for(i=0; i<8; i++) sqlite3_snprintf(3, zCksum+i*2, "%02x", p[i]);
|
||||
sqlite3_snprintf(18, zCksum+i*2, "-%08x%08x", s0, s1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert a big-endian 32-bit integer into a native integer
|
||||
*/
|
||||
static int bigToNative(const unsigned char *x){
|
||||
return (x[0]<<24) + (x[1]<<16) + (x[2]<<8) + x[3];
|
||||
}
|
||||
|
||||
/*
|
||||
** Read data from an vlog-file.
|
||||
*/
|
||||
static int vlogRead(
|
||||
sqlite3_file *pFile,
|
||||
void *zBuf,
|
||||
int iAmt,
|
||||
sqlite_int64 iOfst
|
||||
){
|
||||
int rc;
|
||||
sqlite3_uint64 tStart, tElapse;
|
||||
VLogFile *p = (VLogFile *)pFile;
|
||||
char zSig[40];
|
||||
|
||||
tStart = vlog_time();
|
||||
rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
|
||||
tElapse = vlog_time() - tStart;
|
||||
if( rc==SQLITE_OK ){
|
||||
vlogSignature(zBuf, iAmt, zSig);
|
||||
}else{
|
||||
zSig[0] = 0;
|
||||
}
|
||||
vlogLogPrint(p->pLog, tStart, tElapse, "READ", iAmt, iOfst, zSig, rc);
|
||||
if( rc==SQLITE_OK
|
||||
&& p->pLog
|
||||
&& p->pLog->zFilename
|
||||
&& iOfst<=24
|
||||
&& iOfst+iAmt>=28
|
||||
){
|
||||
unsigned char *x = ((unsigned char*)zBuf)+(24-iOfst);
|
||||
unsigned iCtr, nFree = -1;
|
||||
char *zFree = 0;
|
||||
char zStr[12];
|
||||
iCtr = bigToNative(x);
|
||||
if( iOfst+iAmt>=40 ){
|
||||
zFree = zStr;
|
||||
sqlite3_snprintf(sizeof(zStr), zStr, "%d", bigToNative(x+8));
|
||||
nFree = bigToNative(x+12);
|
||||
}
|
||||
vlogLogPrint(p->pLog, tStart, 0, "CHNGCTR-READ", iCtr, nFree, zFree, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write data to an vlog-file.
|
||||
*/
|
||||
static int vlogWrite(
|
||||
sqlite3_file *pFile,
|
||||
const void *z,
|
||||
int iAmt,
|
||||
sqlite_int64 iOfst
|
||||
){
|
||||
int rc;
|
||||
sqlite3_uint64 tStart, tElapse;
|
||||
VLogFile *p = (VLogFile *)pFile;
|
||||
char zSig[40];
|
||||
|
||||
tStart = vlog_time();
|
||||
vlogSignature((unsigned char*)z, iAmt, zSig);
|
||||
rc = p->pReal->pMethods->xWrite(p->pReal, z, iAmt, iOfst);
|
||||
tElapse = vlog_time() - tStart;
|
||||
vlogLogPrint(p->pLog, tStart, tElapse, "WRITE", iAmt, iOfst, zSig, rc);
|
||||
if( rc==SQLITE_OK
|
||||
&& p->pLog
|
||||
&& p->pLog->zFilename
|
||||
&& iOfst<=24
|
||||
&& iOfst+iAmt>=28
|
||||
){
|
||||
unsigned char *x = ((unsigned char*)z)+(24-iOfst);
|
||||
unsigned iCtr, nFree = -1;
|
||||
char *zFree = 0;
|
||||
char zStr[12];
|
||||
iCtr = bigToNative(x);
|
||||
if( iOfst+iAmt>=40 ){
|
||||
zFree = zStr;
|
||||
sqlite3_snprintf(sizeof(zStr), zStr, "%d", bigToNative(x+8));
|
||||
nFree = bigToNative(x+12);
|
||||
}
|
||||
vlogLogPrint(p->pLog, tStart, 0, "CHNGCTR-WRITE", iCtr, nFree, zFree, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Truncate an vlog-file.
|
||||
*/
|
||||
static int vlogTruncate(sqlite3_file *pFile, sqlite_int64 size){
|
||||
int rc;
|
||||
sqlite3_uint64 tStart, tElapse;
|
||||
VLogFile *p = (VLogFile *)pFile;
|
||||
tStart = vlog_time();
|
||||
rc = p->pReal->pMethods->xTruncate(p->pReal, size);
|
||||
tElapse = vlog_time() - tStart;
|
||||
vlogLogPrint(p->pLog, tStart, tElapse, "TRUNCATE", size, -1, 0, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Sync an vlog-file.
|
||||
*/
|
||||
static int vlogSync(sqlite3_file *pFile, int flags){
|
||||
int rc;
|
||||
sqlite3_uint64 tStart, tElapse;
|
||||
VLogFile *p = (VLogFile *)pFile;
|
||||
tStart = vlog_time();
|
||||
rc = p->pReal->pMethods->xSync(p->pReal, flags);
|
||||
tElapse = vlog_time() - tStart;
|
||||
vlogLogPrint(p->pLog, tStart, tElapse, "SYNC", flags, -1, 0, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the current file-size of an vlog-file.
|
||||
*/
|
||||
static int vlogFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
|
||||
int rc;
|
||||
sqlite3_uint64 tStart, tElapse;
|
||||
VLogFile *p = (VLogFile *)pFile;
|
||||
tStart = vlog_time();
|
||||
rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
|
||||
tElapse = vlog_time() - tStart;
|
||||
vlogLogPrint(p->pLog, tStart, tElapse, "FILESIZE", *pSize, -1, 0, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Lock an vlog-file.
|
||||
*/
|
||||
static int vlogLock(sqlite3_file *pFile, int eLock){
|
||||
int rc;
|
||||
sqlite3_uint64 tStart, tElapse;
|
||||
VLogFile *p = (VLogFile *)pFile;
|
||||
tStart = vlog_time();
|
||||
rc = p->pReal->pMethods->xLock(p->pReal, eLock);
|
||||
tElapse = vlog_time() - tStart;
|
||||
vlogLogPrint(p->pLog, tStart, tElapse, "LOCK", eLock, -1, 0, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Unlock an vlog-file.
|
||||
*/
|
||||
static int vlogUnlock(sqlite3_file *pFile, int eLock){
|
||||
int rc;
|
||||
sqlite3_uint64 tStart;
|
||||
VLogFile *p = (VLogFile *)pFile;
|
||||
tStart = vlog_time();
|
||||
vlogLogPrint(p->pLog, tStart, 0, "UNLOCK", eLock, -1, 0, 0);
|
||||
rc = p->pReal->pMethods->xUnlock(p->pReal, eLock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Check if another file-handle holds a RESERVED lock on an vlog-file.
|
||||
*/
|
||||
static int vlogCheckReservedLock(sqlite3_file *pFile, int *pResOut){
|
||||
int rc;
|
||||
sqlite3_uint64 tStart, tElapse;
|
||||
VLogFile *p = (VLogFile *)pFile;
|
||||
tStart = vlog_time();
|
||||
rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
|
||||
tElapse = vlog_time() - tStart;
|
||||
vlogLogPrint(p->pLog, tStart, tElapse, "CHECKRESERVEDLOCK",
|
||||
*pResOut, -1, "", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** File control method. For custom operations on an vlog-file.
|
||||
*/
|
||||
static int vlogFileControl(sqlite3_file *pFile, int op, void *pArg){
|
||||
VLogFile *p = (VLogFile *)pFile;
|
||||
sqlite3_uint64 tStart, tElapse;
|
||||
int rc;
|
||||
tStart = vlog_time();
|
||||
rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
|
||||
if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
|
||||
*(char**)pArg = sqlite3_mprintf("vlog/%z", *(char**)pArg);
|
||||
}
|
||||
tElapse = vlog_time() - tStart;
|
||||
if( op==SQLITE_FCNTL_TRACE ){
|
||||
vlogLogPrint(p->pLog, tStart, tElapse, "TRACE", op, -1, pArg, rc);
|
||||
}else if( op==SQLITE_FCNTL_PRAGMA ){
|
||||
const char **azArg = (const char **)pArg;
|
||||
vlogLogPrint(p->pLog, tStart, tElapse, "FILECONTROL", op, -1, azArg[1], rc);
|
||||
}else if( op==SQLITE_FCNTL_SIZE_HINT ){
|
||||
sqlite3_int64 sz = *(sqlite3_int64*)pArg;
|
||||
vlogLogPrint(p->pLog, tStart, tElapse, "FILECONTROL", op, sz, 0, rc);
|
||||
}else{
|
||||
vlogLogPrint(p->pLog, tStart, tElapse, "FILECONTROL", op, -1, 0, rc);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the sector-size in bytes for an vlog-file.
|
||||
*/
|
||||
static int vlogSectorSize(sqlite3_file *pFile){
|
||||
int rc;
|
||||
sqlite3_uint64 tStart, tElapse;
|
||||
VLogFile *p = (VLogFile *)pFile;
|
||||
tStart = vlog_time();
|
||||
rc = p->pReal->pMethods->xSectorSize(p->pReal);
|
||||
tElapse = vlog_time() - tStart;
|
||||
vlogLogPrint(p->pLog, tStart, tElapse, "SECTORSIZE", -1, -1, 0, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the device characteristic flags supported by an vlog-file.
|
||||
*/
|
||||
static int vlogDeviceCharacteristics(sqlite3_file *pFile){
|
||||
int rc;
|
||||
sqlite3_uint64 tStart, tElapse;
|
||||
VLogFile *p = (VLogFile *)pFile;
|
||||
tStart = vlog_time();
|
||||
rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
|
||||
tElapse = vlog_time() - tStart;
|
||||
vlogLogPrint(p->pLog, tStart, tElapse, "DEVCHAR", -1, -1, 0, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Open an vlog file handle.
|
||||
*/
|
||||
static int vlogOpen(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zName,
|
||||
sqlite3_file *pFile,
|
||||
int flags,
|
||||
int *pOutFlags
|
||||
){
|
||||
int rc;
|
||||
sqlite3_uint64 tStart, tElapse;
|
||||
sqlite3_int64 iArg2;
|
||||
VLogFile *p = (VLogFile*)pFile;
|
||||
|
||||
p->pReal = (sqlite3_file*)&p[1];
|
||||
if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_MAIN_JOURNAL))!=0 ){
|
||||
p->pLog = vlogLogOpen(zName);
|
||||
}else{
|
||||
p->pLog = 0;
|
||||
}
|
||||
tStart = vlog_time();
|
||||
rc = REALVFS(pVfs)->xOpen(REALVFS(pVfs), zName, p->pReal, flags, pOutFlags);
|
||||
tElapse = vlog_time() - tStart;
|
||||
iArg2 = pOutFlags ? *pOutFlags : -1;
|
||||
vlogLogPrint(p->pLog, tStart, tElapse, "OPEN", flags, iArg2, 0, rc);
|
||||
if( rc==SQLITE_OK ){
|
||||
pFile->pMethods = &vlog_io_methods;
|
||||
}else{
|
||||
if( p->pLog ) vlogLogClose(p->pLog);
|
||||
p->pLog = 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Delete the file located at zPath. If the dirSync argument is true,
|
||||
** ensure the file-system modifications are synced to disk before
|
||||
** returning.
|
||||
*/
|
||||
static int vlogDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
|
||||
int rc;
|
||||
sqlite3_uint64 tStart, tElapse;
|
||||
VLogLog *pLog;
|
||||
tStart = vlog_time();
|
||||
rc = REALVFS(pVfs)->xDelete(REALVFS(pVfs), zPath, dirSync);
|
||||
tElapse = vlog_time() - tStart;
|
||||
pLog = vlogLogOpen(zPath);
|
||||
vlogLogPrint(pLog, tStart, tElapse, "DELETE", dirSync, -1, 0, rc);
|
||||
vlogLogClose(pLog);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Test for access permissions. Return true if the requested permission
|
||||
** is available, or false otherwise.
|
||||
*/
|
||||
static int vlogAccess(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zPath,
|
||||
int flags,
|
||||
int *pResOut
|
||||
){
|
||||
int rc;
|
||||
sqlite3_uint64 tStart, tElapse;
|
||||
VLogLog *pLog;
|
||||
tStart = vlog_time();
|
||||
rc = REALVFS(pVfs)->xAccess(REALVFS(pVfs), zPath, flags, pResOut);
|
||||
tElapse = vlog_time() - tStart;
|
||||
pLog = vlogLogOpen(zPath);
|
||||
vlogLogPrint(pLog, tStart, tElapse, "ACCESS", flags, *pResOut, 0, rc);
|
||||
vlogLogClose(pLog);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Populate buffer zOut with the full canonical pathname corresponding
|
||||
** to the pathname in zPath. zOut is guaranteed to point to a buffer
|
||||
** of at least (INST_MAX_PATHNAME+1) bytes.
|
||||
*/
|
||||
static int vlogFullPathname(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zPath,
|
||||
int nOut,
|
||||
char *zOut
|
||||
){
|
||||
return REALVFS(pVfs)->xFullPathname(REALVFS(pVfs), zPath, nOut, zOut);
|
||||
}
|
||||
|
||||
/*
|
||||
** Open the dynamic library located at zPath and return a handle.
|
||||
*/
|
||||
static void *vlogDlOpen(sqlite3_vfs *pVfs, const char *zPath){
|
||||
return REALVFS(pVfs)->xDlOpen(REALVFS(pVfs), zPath);
|
||||
}
|
||||
|
||||
/*
|
||||
** Populate the buffer zErrMsg (size nByte bytes) with a human readable
|
||||
** utf-8 string describing the most recent error encountered associated
|
||||
** with dynamic libraries.
|
||||
*/
|
||||
static void vlogDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
|
||||
REALVFS(pVfs)->xDlError(REALVFS(pVfs), nByte, zErrMsg);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
|
||||
*/
|
||||
static void (*vlogDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
|
||||
return REALVFS(pVfs)->xDlSym(REALVFS(pVfs), p, zSym);
|
||||
}
|
||||
|
||||
/*
|
||||
** Close the dynamic library handle pHandle.
|
||||
*/
|
||||
static void vlogDlClose(sqlite3_vfs *pVfs, void *pHandle){
|
||||
REALVFS(pVfs)->xDlClose(REALVFS(pVfs), pHandle);
|
||||
}
|
||||
|
||||
/*
|
||||
** Populate the buffer pointed to by zBufOut with nByte bytes of
|
||||
** random data.
|
||||
*/
|
||||
static int vlogRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
|
||||
return REALVFS(pVfs)->xRandomness(REALVFS(pVfs), nByte, zBufOut);
|
||||
}
|
||||
|
||||
/*
|
||||
** Sleep for nMicro microseconds. Return the number of microseconds
|
||||
** actually slept.
|
||||
*/
|
||||
static int vlogSleep(sqlite3_vfs *pVfs, int nMicro){
|
||||
return REALVFS(pVfs)->xSleep(REALVFS(pVfs), nMicro);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the current time as a Julian Day number in *pTimeOut.
|
||||
*/
|
||||
static int vlogCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
|
||||
return REALVFS(pVfs)->xCurrentTime(REALVFS(pVfs), pTimeOut);
|
||||
}
|
||||
|
||||
static int vlogGetLastError(sqlite3_vfs *pVfs, int a, char *b){
|
||||
return REALVFS(pVfs)->xGetLastError(REALVFS(pVfs), a, b);
|
||||
}
|
||||
static int vlogCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
|
||||
return REALVFS(pVfs)->xCurrentTimeInt64(REALVFS(pVfs), p);
|
||||
}
|
||||
|
||||
/*
|
||||
** Register debugvfs as the default VFS for this process.
|
||||
*/
|
||||
int sqlite3_register_vfslog(const char *zArg){
|
||||
vlog_vfs.pVfs = sqlite3_vfs_find(0);
|
||||
vlog_vfs.base.szOsFile = sizeof(VLogFile) + vlog_vfs.pVfs->szOsFile;
|
||||
return sqlite3_vfs_register(&vlog_vfs.base, 1);
|
||||
}
|
||||
+67
-5
@@ -137,6 +137,16 @@ typedef union RtreeCoord RtreeCoord;
|
||||
*/
|
||||
#define HASHSIZE 128
|
||||
|
||||
/* The xBestIndex method of this virtual table requires an estimate of
|
||||
** the number of rows in the virtual table to calculate the costs of
|
||||
** various strategies. If possible, this estimate is loaded from the
|
||||
** sqlite_stat1 table (with RTREE_MIN_ROWEST as a hard-coded minimum).
|
||||
** Otherwise, if no sqlite_stat1 entry is available, use
|
||||
** RTREE_DEFAULT_ROWEST.
|
||||
*/
|
||||
#define RTREE_DEFAULT_ROWEST 1048576
|
||||
#define RTREE_MIN_ROWEST 100
|
||||
|
||||
/*
|
||||
** An rtree virtual-table object.
|
||||
*/
|
||||
@@ -151,6 +161,7 @@ struct Rtree {
|
||||
char *zName; /* Name of r-tree table */
|
||||
RtreeNode *aHash[HASHSIZE]; /* Hash table of in-memory nodes. */
|
||||
int nBusy; /* Current number of users of this structure */
|
||||
i64 nRowEst; /* Estimated number of rows in this table */
|
||||
|
||||
/* List of nodes removed during a CondenseTree operation. List is
|
||||
** linked together via the pointer normally used for hash chains -
|
||||
@@ -1343,6 +1354,19 @@ static int rtreeFilter(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the pIdxInfo->estimatedRows variable to nRow. Unless this
|
||||
** extension is currently being used by a version of SQLite too old to
|
||||
** support estimatedRows. In that case this function is a no-op.
|
||||
*/
|
||||
static void setEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){
|
||||
#if SQLITE_VERSION_NUMBER>=3008002
|
||||
if( sqlite3_libversion_number()>=3008002 ){
|
||||
pIdxInfo->estimatedRows = nRow;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Rtree virtual table module xBestIndex method. There are three
|
||||
** table scan strategies to choose from (in order from most to
|
||||
@@ -1378,13 +1402,14 @@ static int rtreeFilter(
|
||||
** is 'a', the second from the left 'b' etc.
|
||||
*/
|
||||
static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
Rtree *pRtree = (Rtree*)tab;
|
||||
int rc = SQLITE_OK;
|
||||
int ii;
|
||||
i64 nRow; /* Estimated rows returned by this scan */
|
||||
|
||||
int iIdx = 0;
|
||||
char zIdxStr[RTREE_MAX_DIMENSIONS*8+1];
|
||||
memset(zIdxStr, 0, sizeof(zIdxStr));
|
||||
UNUSED_PARAMETER(tab);
|
||||
|
||||
assert( pIdxInfo->idxStr==0 );
|
||||
for(ii=0; ii<pIdxInfo->nConstraint && iIdx<(int)(sizeof(zIdxStr)-1); ii++){
|
||||
@@ -1404,9 +1429,11 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
/* This strategy involves a two rowid lookups on an B-Tree structures
|
||||
** and then a linear search of an R-Tree node. This should be
|
||||
** considered almost as quick as a direct rowid lookup (for which
|
||||
** sqlite uses an internal cost of 0.0).
|
||||
** sqlite uses an internal cost of 0.0). It is expected to return
|
||||
** a single row.
|
||||
*/
|
||||
pIdxInfo->estimatedCost = 10.0;
|
||||
pIdxInfo->estimatedCost = 30.0;
|
||||
setEstimatedRows(pIdxInfo, 1);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -1435,8 +1462,11 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
if( iIdx>0 && 0==(pIdxInfo->idxStr = sqlite3_mprintf("%s", zIdxStr)) ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
assert( iIdx>=0 );
|
||||
pIdxInfo->estimatedCost = (2000000.0 / (double)(iIdx + 1));
|
||||
|
||||
nRow = pRtree->nRowEst / (iIdx + 1);
|
||||
pIdxInfo->estimatedCost = (double)6.0 * (double)nRow;
|
||||
setEstimatedRows(pIdxInfo, nRow);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -2911,6 +2941,37 @@ static int rtreeRename(sqlite3_vtab *pVtab, const char *zNewName){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function populates the pRtree->nRowEst variable with an estimate
|
||||
** of the number of rows in the virtual table. If possible, this is based
|
||||
** on sqlite_stat1 data. Otherwise, use RTREE_DEFAULT_ROWEST.
|
||||
*/
|
||||
static int rtreeQueryStat1(sqlite3 *db, Rtree *pRtree){
|
||||
const char *zSql = "SELECT stat FROM sqlite_stat1 WHERE tbl= ? || '_rowid'";
|
||||
sqlite3_stmt *p;
|
||||
int rc;
|
||||
i64 nRow = 0;
|
||||
|
||||
rc = sqlite3_prepare_v2(db, zSql, -1, &p, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_text(p, 1, pRtree->zName, -1, SQLITE_STATIC);
|
||||
if( sqlite3_step(p)==SQLITE_ROW ) nRow = sqlite3_column_int64(p, 0);
|
||||
rc = sqlite3_finalize(p);
|
||||
}else if( rc!=SQLITE_NOMEM ){
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
if( nRow==0 ){
|
||||
pRtree->nRowEst = RTREE_DEFAULT_ROWEST;
|
||||
}else{
|
||||
pRtree->nRowEst = MAX(nRow, RTREE_MIN_ROWEST);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static sqlite3_module rtreeModule = {
|
||||
0, /* iVersion */
|
||||
rtreeCreate, /* xCreate - create a table */
|
||||
@@ -2996,6 +3057,7 @@ static int rtreeSqlInit(
|
||||
appStmt[7] = &pRtree->pWriteParent;
|
||||
appStmt[8] = &pRtree->pDeleteParent;
|
||||
|
||||
rc = rtreeQueryStat1(db, pRtree);
|
||||
for(i=0; i<N_STATEMENT && rc==SQLITE_OK; i++){
|
||||
char *zSql = sqlite3_mprintf(azSql[i], zDb, zPrefix);
|
||||
if( zSql ){
|
||||
|
||||
@@ -96,7 +96,7 @@ do_eqp_test rtree6.2.4 {
|
||||
SELECT * FROM t1,t2 WHERE v=10 and x1<10 and x2>10
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:CaEb}
|
||||
0 1 1 {SCAN TABLE t2}
|
||||
0 1 1 {SEARCH TABLE t2 USING AUTOMATIC COVERING INDEX (v=?)}
|
||||
}
|
||||
|
||||
do_eqp_test rtree6.2.5 {
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
# 2011 March 2
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# Make sure the rtreenode() testing function can handle entries with
|
||||
# 64-bit rowids.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !rtree { finish_test ; return }
|
||||
set testprefix rtreeC
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE r_tree USING rtree(id, min_x, max_x, min_y, max_y);
|
||||
CREATE TABLE t(x, y);
|
||||
}
|
||||
|
||||
do_eqp_test 1.1 {
|
||||
SELECT * FROM r_tree, t
|
||||
WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND t.x<=max_y
|
||||
} {
|
||||
0 0 1 {SCAN TABLE t}
|
||||
0 1 0 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:DdBcDbBa}
|
||||
}
|
||||
|
||||
do_eqp_test 1.2 {
|
||||
SELECT * FROM t, r_tree
|
||||
WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND t.x<=max_y
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t}
|
||||
0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:DdBcDbBa}
|
||||
}
|
||||
|
||||
do_eqp_test 1.3 {
|
||||
SELECT * FROM t, r_tree
|
||||
WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND ?<=max_y
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t}
|
||||
0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:DdBcDbBa}
|
||||
}
|
||||
|
||||
do_eqp_test 1.5 {
|
||||
SELECT * FROM t, r_tree
|
||||
} {
|
||||
0 0 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:}
|
||||
0 1 0 {SCAN TABLE t}
|
||||
}
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
INSERT INTO t VALUES(0, 0);
|
||||
INSERT INTO t VALUES(0, 1);
|
||||
INSERT INTO t VALUES(0, 2);
|
||||
INSERT INTO t VALUES(0, 3);
|
||||
INSERT INTO t VALUES(0, 4);
|
||||
INSERT INTO t VALUES(0, 5);
|
||||
INSERT INTO t VALUES(0, 6);
|
||||
INSERT INTO t VALUES(0, 7);
|
||||
INSERT INTO t VALUES(0, 8);
|
||||
INSERT INTO t VALUES(0, 9);
|
||||
|
||||
INSERT INTO t SELECT x+1, y FROM t;
|
||||
INSERT INTO t SELECT x+2, y FROM t;
|
||||
INSERT INTO t SELECT x+4, y FROM t;
|
||||
INSERT INTO r_tree SELECT NULL, x-1, x+1, y-1, y+1 FROM t;
|
||||
ANALYZE;
|
||||
}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
do_eqp_test 2.1 {
|
||||
SELECT * FROM r_tree, t
|
||||
WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND t.x<=max_y
|
||||
} {
|
||||
0 0 1 {SCAN TABLE t}
|
||||
0 1 0 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:DdBcDbBa}
|
||||
}
|
||||
|
||||
do_eqp_test 2.2 {
|
||||
SELECT * FROM t, r_tree
|
||||
WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND t.x<=max_y
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t}
|
||||
0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:DdBcDbBa}
|
||||
}
|
||||
|
||||
do_eqp_test 2.3 {
|
||||
SELECT * FROM t, r_tree
|
||||
WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND ?<=max_y
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t}
|
||||
0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:DdBcDbBa}
|
||||
}
|
||||
|
||||
do_eqp_test 2.5 {
|
||||
SELECT * FROM t, r_tree
|
||||
} {
|
||||
0 0 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:}
|
||||
0 1 0 {SCAN TABLE t}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that the special CROSS JOIN handling works with rtree tables.
|
||||
#
|
||||
do_execsql_test 3.1 {
|
||||
CREATE TABLE t1(x);
|
||||
CREATE TABLE t2(y);
|
||||
CREATE VIRTUAL TABLE t3 USING rtree(z, x1,x2, y1,y2);
|
||||
}
|
||||
|
||||
do_eqp_test 3.2.1 { SELECT * FROM t1 CROSS JOIN t2 } {
|
||||
0 0 0 {SCAN TABLE t1}
|
||||
0 1 1 {SCAN TABLE t2}
|
||||
}
|
||||
do_eqp_test 3.2.2 { SELECT * FROM t2 CROSS JOIN t1 } {
|
||||
0 0 0 {SCAN TABLE t2} 0 1 1 {SCAN TABLE t1}
|
||||
}
|
||||
|
||||
do_eqp_test 3.3.1 { SELECT * FROM t1 CROSS JOIN t3 } {
|
||||
0 0 0 {SCAN TABLE t1}
|
||||
0 1 1 {SCAN TABLE t3 VIRTUAL TABLE INDEX 2:}
|
||||
}
|
||||
do_eqp_test 3.3.2 { SELECT * FROM t3 CROSS JOIN t1 } {
|
||||
0 0 0 {SCAN TABLE t3 VIRTUAL TABLE INDEX 2:}
|
||||
0 1 1 {SCAN TABLE t1}
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Test that LEFT JOINs are not reordered if the right-hand-side is
|
||||
# a virtual table.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 4.1 {
|
||||
CREATE TABLE t1(a);
|
||||
CREATE VIRTUAL TABLE t2 USING rtree(b, x1,x2);
|
||||
|
||||
INSERT INTO t1 VALUES(1);
|
||||
INSERT INTO t1 VALUES(2);
|
||||
|
||||
INSERT INTO t2 VALUES(1, 0.0, 0.1);
|
||||
INSERT INTO t2 VALUES(3, 0.0, 0.1);
|
||||
}
|
||||
|
||||
do_execsql_test 4.2 {
|
||||
SELECT a, b FROM t1 LEFT JOIN t2 ON (+a = +b);
|
||||
} {1 1 2 {}}
|
||||
|
||||
do_execsql_test 4.3 {
|
||||
SELECT b, a FROM t2 LEFT JOIN t1 ON (+a = +b);
|
||||
} {1 1 3 {}}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# 2014 March 11
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Miscellaneous tests for errors in the rtree constructor.
|
||||
#
|
||||
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] rtree_util.tcl]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/lock_common.tcl
|
||||
ifcapable !rtree {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
set testprefix rtreeD
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that if an SQLITE_BUSY is encountered within the vtable
|
||||
# constructor, a relevant error message is returned.
|
||||
#
|
||||
do_multiclient_test tn {
|
||||
do_test 1.$tn.1 {
|
||||
sql1 {
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1,2);
|
||||
CREATE VIRTUAL TABLE rt USING rtree(id, minx, maxx, miny, maxy);
|
||||
INSERT INTO rt VALUES(1,2,3,4,5);
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test 1.$tn.2 {
|
||||
sql2 { SELECT * FROM t1; }
|
||||
} {1 2}
|
||||
|
||||
do_test 1.$tn.3 {
|
||||
sql1 { BEGIN EXCLUSIVE; INSERT INTO t1 VALUES(3, 4); }
|
||||
} {}
|
||||
|
||||
do_test 1.$tn.4 {
|
||||
list [catch { sql2 { SELECT * FROM rt } } msg] $msg
|
||||
} {1 {database is locked}}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -26,4 +26,6 @@
|
||||
>68 belong =0x42654c6e Bentley Systems Localization File -
|
||||
>60 belong =0x5f4d544e Monotone source repository -
|
||||
>68 belong =0x47504b47 OGC GeoPackage file -
|
||||
>68 belong =0x47503130 OGC GeoPackage version 1.0 file -
|
||||
>68 belong =0x45737269 Esri Spatially-Enabled Database -
|
||||
>0 string =SQLite SQLite3 database
|
||||
|
||||
@@ -163,7 +163,8 @@ SRC = \
|
||||
$(TOP)/src/wal.c \
|
||||
$(TOP)/src/wal.h \
|
||||
$(TOP)/src/walker.c \
|
||||
$(TOP)/src/where.c
|
||||
$(TOP)/src/where.c \
|
||||
$(TOP)/src/whereInt.h
|
||||
|
||||
# Source code for extensions
|
||||
#
|
||||
@@ -279,7 +280,9 @@ TESTSRC += \
|
||||
$(TOP)/ext/misc/percentile.c \
|
||||
$(TOP)/ext/misc/regexp.c \
|
||||
$(TOP)/ext/misc/spellfix.c \
|
||||
$(TOP)/ext/misc/wholenumber.c
|
||||
$(TOP)/ext/misc/totype.c \
|
||||
$(TOP)/ext/misc/wholenumber.c \
|
||||
$(TOP)/ext/misc/vfslog.c
|
||||
|
||||
|
||||
#TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c
|
||||
@@ -344,7 +347,8 @@ HDR = \
|
||||
$(TOP)/src/sqliteInt.h \
|
||||
$(TOP)/src/sqliteLimit.h \
|
||||
$(TOP)/src/vdbe.h \
|
||||
$(TOP)/src/vdbeInt.h
|
||||
$(TOP)/src/vdbeInt.h \
|
||||
$(TOP)/src/whereInt.h
|
||||
|
||||
# Header files used by extensions
|
||||
#
|
||||
@@ -385,7 +389,7 @@ mptester$(EXE): sqlite3.c $(TOP)/mptest/mptest.c
|
||||
$(TLIBS) $(THREADLIB)
|
||||
|
||||
sqlite3.o: sqlite3.c
|
||||
$(TCCX) -c sqlite3.c
|
||||
$(TCCX) -I. -c sqlite3.c
|
||||
|
||||
# This target creates a directory named "tsrc" and fills it with
|
||||
# copies of all of the C source code and header files needed to
|
||||
@@ -398,7 +402,7 @@ target_source: $(SRC) $(TOP)/tool/vdbe-compress.tcl
|
||||
mkdir tsrc
|
||||
cp -f $(SRC) tsrc
|
||||
rm tsrc/sqlite.h.in tsrc/parse.y
|
||||
tclsh $(TOP)/tool/vdbe-compress.tcl <tsrc/vdbe.c >vdbe.new
|
||||
tclsh $(TOP)/tool/vdbe-compress.tcl $(OPTS) <tsrc/vdbe.c >vdbe.new
|
||||
mv vdbe.new tsrc/vdbe.c
|
||||
touch target_source
|
||||
|
||||
@@ -618,6 +622,17 @@ $(TEST_EXTENSION): $(TOP)/src/test_loadext.c
|
||||
extensiontest: testfixture$(EXE) $(TEST_EXTENSION)
|
||||
./testfixture$(EXE) $(TOP)/test/loadext.test
|
||||
|
||||
showdb$(EXE): $(TOP)/tool/showdb.c sqlite3.c
|
||||
$(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o showdb$(EXE) \
|
||||
$(TOP)/tool/showdb.c sqlite3.c
|
||||
|
||||
wordcount$(EXE): $(TOP)/test/wordcount.c sqlite3.c
|
||||
$(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o wordcount$(EXE) \
|
||||
$(TOP)/test/wordcount.c sqlite3.c
|
||||
|
||||
speedtest1$(EXE): $(TOP)/test/speedtest1.c sqlite3.o
|
||||
$(TCC) -I. -o speedtest1$(EXE) $(TOP)/test/speedtest1.c sqlite3.o $(THREADLIB)
|
||||
|
||||
# This target will fail if the SQLite amalgamation contains any exported
|
||||
# symbols that do not begin with "sqlite3_". It is run as part of the
|
||||
# releasetest.tcl script.
|
||||
@@ -651,9 +666,10 @@ clean:
|
||||
rm -f fts3-testfixture fts3-testfixture.exe
|
||||
rm -f testfixture testfixture.exe
|
||||
rm -f threadtest3 threadtest3.exe
|
||||
rm -f sqlite3.c fts?amal.c tclsqlite3.c
|
||||
rm -f sqlite3.c sqlite3-*.c fts?amal.c tclsqlite3.c
|
||||
rm -f sqlite3rc.h
|
||||
rm -f shell.c sqlite3ext.h
|
||||
rm -f sqlite3_analyzer sqlite3_analyzer.exe sqlite3_analyzer.c
|
||||
rm -f sqlite-*-output.vsix
|
||||
rm -f mptester mptester.exe
|
||||
rm -f showdb
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
7dd4968f235d6e1ca9547cda9cf3bd570e1609ef
|
||||
a611fa96c4a848614efe899130359c9f6fb889c3
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# This script is used to compile SQLite into a DLL.
|
||||
#
|
||||
# Two separate DLLs are generated. "sqlite3.dll" is the core
|
||||
# library. "tclsqlite3.dll" contains the TCL bindings and is the
|
||||
# library that is loaded into TCL in order to run SQLite.
|
||||
#
|
||||
make sqlite3.c
|
||||
PATH=$PATH:/opt/mingw/bin
|
||||
TCLDIR=/home/drh/tcltk/846/win/846win
|
||||
TCLSTUBLIB=$TCLDIR/libtcl84stub.a
|
||||
OPTS='-DUSE_TCL_STUBS=1 -DBUILD_sqlite=1 -DSQLITE_OS_WIN=1'
|
||||
OPTS="$OPTS -DSQLITE_THREADSAFE=1"
|
||||
OPTS="$OPTS -DSQLITE_ENABLE_FTS3=1"
|
||||
OPTS="$OPTS -DSQLITE_ENABLE_RTREE=1"
|
||||
OPTS="$OPTS -DSQLITE_ENABLE_COLUMN_METADATA=1"
|
||||
CC="i386-mingw32msvc-gcc -Os $OPTS -Itsrc -I$TCLDIR"
|
||||
NM="i386-mingw32msvc-nm"
|
||||
CMD="$CC -c sqlite3.c"
|
||||
echo $CMD
|
||||
$CMD
|
||||
CMD="$CC -c tclsqlite3.c"
|
||||
echo $CMD
|
||||
$CMD
|
||||
echo 'EXPORTS' >tclsqlite3.def
|
||||
$NM tclsqlite3.o | grep ' T ' >temp1
|
||||
grep '_Init$' temp1 >temp2
|
||||
grep '_SafeInit$' temp1 >>temp2
|
||||
grep ' T _sqlite3_' temp1 >>temp2
|
||||
echo 'EXPORTS' >tclsqlite3.def
|
||||
sed 's/^.* T _//' temp2 | sort | uniq >>tclsqlite3.def
|
||||
i386-mingw32msvc-dllwrap \
|
||||
--def tclsqlite3.def -v --export-all \
|
||||
--driver-name i386-mingw32msvc-gcc \
|
||||
--dlltool-name i386-mingw32msvc-dlltool \
|
||||
--as i386-mingw32msvc-as \
|
||||
--target i386-mingw32 \
|
||||
-dllname tclsqlite3.dll -lmsvcrt tclsqlite3.o $TCLSTUBLIB
|
||||
$NM sqlite3.o | grep ' T ' >temp1
|
||||
echo 'EXPORTS' >sqlite3.def
|
||||
grep ' _sqlite3_' temp1 | sed 's/^.* _//' >>sqlite3.def
|
||||
i386-mingw32msvc-dllwrap \
|
||||
--def sqlite3.def -v --export-all \
|
||||
--driver-name i386-mingw32msvc-gcc \
|
||||
--dlltool-name i386-mingw32msvc-dlltool \
|
||||
--as i386-mingw32msvc-as \
|
||||
--target i386-mingw32 \
|
||||
-dllname sqlite3.dll -lmsvcrt sqlite3.o
|
||||
@@ -1,13 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# This script is used to compile SQLite into a shared library on Linux.
|
||||
#
|
||||
# Two separate shared libraries are generated. "sqlite3.so" is the core
|
||||
# library. "tclsqlite3.so" contains the TCL bindings and is the
|
||||
# library that is loaded into TCL in order to run SQLite.
|
||||
#
|
||||
CFLAGS=-O2 -Wall
|
||||
make fts2amal.c
|
||||
echo gcc $CFLAGS -shared fts2amal.c -o fts2.so
|
||||
gcc $CFLAGS -shared fts2amal.c -o fts2.so
|
||||
strip fts2.so
|
||||
@@ -1,22 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# This script is used to compile SQLite extensions into DLLs.
|
||||
#
|
||||
make fts2amal.c
|
||||
PATH=$PATH:/opt/mingw/bin
|
||||
OPTS='-DTHREADSAFE=1 -DBUILD_sqlite=1 -DSQLITE_OS_WIN=1'
|
||||
CC="i386-mingw32msvc-gcc -O2 $OPTS -Itsrc"
|
||||
NM="i386-mingw32msvc-nm"
|
||||
CMD="$CC -c fts2amal.c"
|
||||
echo $CMD
|
||||
$CMD
|
||||
echo 'EXPORTS' >fts2.def
|
||||
echo 'sqlite3_fts2_init' >>fts2.def
|
||||
i386-mingw32msvc-dllwrap \
|
||||
--def fts2.def -v --export-all \
|
||||
--driver-name i386-mingw32msvc-gcc \
|
||||
--dlltool-name i386-mingw32msvc-dlltool \
|
||||
--as i386-mingw32msvc-as \
|
||||
--target i386-mingw32 \
|
||||
-dllname fts2.dll -lmsvcrt fts2amal.o
|
||||
zip fts2dll.zip fts2.dll fts2.def
|
||||
+17
-3
@@ -12,22 +12,36 @@ BEGIN {
|
||||
print "/* Automatically generated. Do not edit */"
|
||||
print "/* See the mkopcodec.awk script for details. */"
|
||||
printf "#if !defined(SQLITE_OMIT_EXPLAIN)"
|
||||
printf " || !defined(NDEBUG)"
|
||||
printf " || defined(VDBE_PROFILE)"
|
||||
print " || defined(SQLITE_DEBUG)"
|
||||
print "#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) || defined(SQLITE_DEBUG)"
|
||||
print "# define OpHelp(X) \"\\0\" X"
|
||||
print "#else"
|
||||
print "# define OpHelp(X)"
|
||||
print "#endif"
|
||||
print "const char *sqlite3OpcodeName(int i){"
|
||||
print " static const char *const azName[] = { \"?\","
|
||||
mx = 0
|
||||
}
|
||||
/define OP_/ {
|
||||
/^.define OP_/ {
|
||||
sub("OP_","",$2)
|
||||
i = $3+0
|
||||
label[i] = $2
|
||||
if( mx<i ) mx = i
|
||||
for(j=5; j<NF; j++) if( $j=="synopsis:" ) break
|
||||
if( j<NF ){
|
||||
j++
|
||||
x = $j
|
||||
for(j=j+1; j<NF; j++) x = x " " $j
|
||||
synopsis[i] = x
|
||||
}else{
|
||||
synopsis[i] = ""
|
||||
}
|
||||
}
|
||||
END {
|
||||
for(i=1; i<=mx; i++){
|
||||
printf " /* %3d */ \"%s\",\n", i, label[i]
|
||||
printf " /* %3d */ %-18s OpHelp(\"%s\"),\n", i, \
|
||||
"\"" label[i] "\"", synopsis[i]
|
||||
}
|
||||
print " };"
|
||||
print " return azName[i];"
|
||||
|
||||
+55
-3
@@ -38,6 +38,33 @@
|
||||
tk[$2] = 0+$3 # tk[x] holds the numeric value for TK symbol X
|
||||
}
|
||||
|
||||
# Find "/* Opcode: " lines in the vdbe.c file. Each one introduces
|
||||
# a new opcode. Remember which parameters are used.
|
||||
/^.. Opcode: / {
|
||||
currentOp = "OP_" $3
|
||||
m = 0
|
||||
for(i=4; i<=NF; i++){
|
||||
x = $i
|
||||
if( x=="P1" ) m += 1
|
||||
if( x=="P2" ) m += 2
|
||||
if( x=="P3" ) m += 4
|
||||
if( x=="P4" ) m += 8
|
||||
if( x=="P5" ) m += 16
|
||||
}
|
||||
paramused[currentOp] = m
|
||||
}
|
||||
|
||||
# Find "** Synopsis: " lines that follow Opcode:
|
||||
/^.. Synopsis: / {
|
||||
if( currentOp ){
|
||||
x = $3
|
||||
for(i=4; i<=NF; i++){
|
||||
x = x " " $i
|
||||
}
|
||||
synopsis[currentOp] = x
|
||||
}
|
||||
}
|
||||
|
||||
# Scan for "case OP_aaaa:" lines in the vdbe.c file
|
||||
/^case OP_/ {
|
||||
name = $2
|
||||
@@ -109,8 +136,10 @@ END {
|
||||
|| name=="OP_VUpdate" \
|
||||
|| name=="OP_VFilter" \
|
||||
|| name=="OP_Next" \
|
||||
|| name=="OP_NextIfOpen" \
|
||||
|| name=="OP_SorterNext" \
|
||||
|| name=="OP_Prev" \
|
||||
|| name=="OP_PrevIfOpen" \
|
||||
){
|
||||
cnt++
|
||||
while( used[cnt] ) cnt++
|
||||
@@ -136,10 +165,22 @@ END {
|
||||
if( !used[i] ){
|
||||
def[i] = "OP_NotUsed_" i
|
||||
}
|
||||
printf "#define %-25s %15d", def[i], i
|
||||
printf "#define %-16s %3d", def[i], i
|
||||
com = ""
|
||||
if( sameas[i] ){
|
||||
printf " /* same as %-12s*/", sameas[i]
|
||||
}
|
||||
com = "same as " sameas[i]
|
||||
}
|
||||
x = synopsis[def[i]]
|
||||
if( x ){
|
||||
if( com=="" ){
|
||||
com = "synopsis: " x
|
||||
} else {
|
||||
com = com ", synopsis: " x
|
||||
}
|
||||
}
|
||||
if( com!="" ){
|
||||
printf " /* %-42s */", com
|
||||
}
|
||||
printf "\n"
|
||||
}
|
||||
|
||||
@@ -180,4 +221,15 @@ END {
|
||||
if( i%8==7 ) printf("\\\n");
|
||||
}
|
||||
print "}"
|
||||
if( 0 ){
|
||||
print "\n/* Bitmask to indicate which fields (P1..P5) of each opcode are"
|
||||
print "** actually used.\n*/"
|
||||
print "#define OP_PARAM_USED_INITIALIZER {\\"
|
||||
for(i=0; i<=max; i++){
|
||||
if( i%8==0 ) printf("/* %3d */",i)
|
||||
printf " 0x%02x,", paramused[def[i]]
|
||||
if( i%8==7 ) printf("\\\n");
|
||||
}
|
||||
print "}"
|
||||
}
|
||||
}
|
||||
|
||||
+73
-23
@@ -2,7 +2,7 @@
|
||||
.\" First parameter, NAME, should be all caps
|
||||
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
||||
.\" other parameters are allowed: see man(7), man(1)
|
||||
.TH SQLCIPHER 1 "Mon Apr 15 23:49:17 2002"
|
||||
.TH SQLCIPHER 1 "Mon Jan 31 11:14:00 2014"
|
||||
.\" Please adjust this date whenever revising the manpage.
|
||||
.\"
|
||||
.\" Some roff macros, for reference:
|
||||
@@ -49,7 +49,7 @@ a table named "memos" and insert a couple of records into that table:
|
||||
$
|
||||
.B sqlcipher mydata.db
|
||||
.br
|
||||
SQLite version 2.0.3
|
||||
SQLite version 3.8.3
|
||||
.br
|
||||
Enter ".help" for instructions
|
||||
.br
|
||||
@@ -108,15 +108,24 @@ sqlite>
|
||||
.B .help
|
||||
.nf
|
||||
.cc |
|
||||
.backup ?DB? FILE Backup DB (default "main") to FILE
|
||||
.bail ON|OFF Stop after hitting an error. Default OFF
|
||||
.databases List names and files of attached databases
|
||||
.dump ?TABLE? ... Dump the database in an SQL text format
|
||||
If TABLE specified, only dump tables matching
|
||||
LIKE pattern TABLE.
|
||||
.echo ON|OFF Turn command echo on or off
|
||||
.exit Exit this program
|
||||
.explain ON|OFF Turn output mode suitable for EXPLAIN on or off.
|
||||
.explain ?ON|OFF? Turn output mode suitable for EXPLAIN on or off.
|
||||
With no args, it turns EXPLAIN on.
|
||||
.header(s) ON|OFF Turn display of headers on or off
|
||||
.help Show this message
|
||||
.import FILE TABLE Import data from FILE into TABLE
|
||||
.indices TABLE Show names of all indices on TABLE
|
||||
.indices ?TABLE? Show names of all indices
|
||||
If TABLE specified, only show indices for tables
|
||||
matching LIKE pattern TABLE.
|
||||
.load FILE ?ENTRY? Load an extension library
|
||||
.log FILE|off Turn logging on or off. FILE can be stderr/stdout
|
||||
.mode MODE ?TABLE? Set output mode where MODE is one of:
|
||||
csv Comma-separated values
|
||||
column Left-aligned columns. (See .width)
|
||||
@@ -126,46 +135,76 @@ sqlite>
|
||||
list Values delimited by .separator string
|
||||
tabs Tab-separated values
|
||||
tcl TCL list elements
|
||||
.nullvalue STRING Print STRING in place of NULL values
|
||||
.nullvalue STRING Use STRING in place of NULL values
|
||||
.open ?FILENAME? Close existing database and reopen FILENAME
|
||||
.output FILENAME Send output to FILENAME
|
||||
.output stdout Send output to the screen
|
||||
.print STRING... Print literal STRING
|
||||
.prompt MAIN CONTINUE Replace the standard prompts
|
||||
.quit Exit this program
|
||||
.read FILENAME Execute SQL in FILENAME
|
||||
.restore ?DB? FILE Restore content of DB (default "main") from FILE
|
||||
.schema ?TABLE? Show the CREATE statements
|
||||
If TABLE specified, only show tables matching
|
||||
LIKE pattern TABLE.
|
||||
.separator STRING Change separator used by output mode and .import
|
||||
.show Show the current values for various settings
|
||||
.tables ?PATTERN? List names of tables matching a LIKE pattern
|
||||
.stats ON|OFF Turn stats on or off
|
||||
.tables ?TABLE? List names of tables
|
||||
If TABLE specified, only list tables matching
|
||||
LIKE pattern TABLE.
|
||||
.timeout MS Try opening locked tables for MS milliseconds
|
||||
.width NUM NUM ... Set column widths for "column" mode
|
||||
.trace FILE|off Output each SQL statement as it is run
|
||||
.vfsname ?AUX? Print the name of the VFS stack
|
||||
.width NUM1 NUM2 ... Set column widths for "column" mode
|
||||
.timer ON|OFF Turn the CPU timer measurement on or off
|
||||
sqlite>
|
||||
|cc .
|
||||
.sp
|
||||
.fi
|
||||
|
||||
.SH OPTIONS
|
||||
.B sqlcipher
|
||||
has the following options:
|
||||
.TP
|
||||
.BI \-init\ file
|
||||
Read and execute commands from
|
||||
.I file
|
||||
, which can contain a mix of SQL statements and meta-commands.
|
||||
.B \-bail
|
||||
Stop after hitting an error.
|
||||
.TP
|
||||
.B \-echo
|
||||
Print commands before execution.
|
||||
.TP
|
||||
.B \-[no]header
|
||||
Turn headers on or off.
|
||||
.B \-batch
|
||||
Force batch I/O.
|
||||
.TP
|
||||
.B \-column
|
||||
Query results will be displayed in a table like form, using
|
||||
whitespace characters to separate the columns and align the
|
||||
output.
|
||||
.TP
|
||||
.BI \-cmd\ command
|
||||
run
|
||||
.I command
|
||||
before reading stdin
|
||||
.TP
|
||||
.B \-csv
|
||||
Set output mode to CSV (comma separated values).
|
||||
.TP
|
||||
.B \-echo
|
||||
Print commands before execution.
|
||||
.TP
|
||||
.BI \-init\ file
|
||||
Read and execute commands from
|
||||
.I file
|
||||
, which can contain a mix of SQL statements and meta-commands.
|
||||
.TP
|
||||
.B \-[no]header
|
||||
Turn headers on or off.
|
||||
.TP
|
||||
.B \-help
|
||||
Show help on options and exit.
|
||||
.TP
|
||||
.B \-html
|
||||
Query results will be output as simple HTML tables.
|
||||
.TP
|
||||
.B \-interactive
|
||||
Force interactive I/O.
|
||||
.TP
|
||||
.B \-line
|
||||
Query results will be displayed with one value per line, rows
|
||||
separated by a blank line. Designed to be easily parsed by
|
||||
@@ -175,18 +214,28 @@ scripts or other programs
|
||||
Query results will be displayed with the separator (|, by default)
|
||||
character between each field value. The default.
|
||||
.TP
|
||||
.BI \-separator\ separator
|
||||
Set output field separator. Default is '|'.
|
||||
.BI \-mmap\ N
|
||||
Set default mmap size to
|
||||
.I N
|
||||
\.
|
||||
.TP
|
||||
.BI \-nullvalue\ string
|
||||
Set string used to represent NULL values. Default is ''
|
||||
(empty string).
|
||||
.TP
|
||||
.BI \-separator\ separator
|
||||
Set output field separator. Default is '|'.
|
||||
.TP
|
||||
.B \-stats
|
||||
Print memory stats before each finalize.
|
||||
.TP
|
||||
.B \-version
|
||||
Show SQLite version.
|
||||
.TP
|
||||
.B \-help
|
||||
Show help on options and exit.
|
||||
.BI \-vfs\ name
|
||||
Use
|
||||
.I name
|
||||
as the default VFS.
|
||||
|
||||
|
||||
.SH INIT FILE
|
||||
@@ -222,8 +271,9 @@ o All other command line options are processed.
|
||||
.SH SEE ALSO
|
||||
http://www.sqlcipher.net/
|
||||
.br
|
||||
The sqlite-doc package
|
||||
The sqlite3-doc package.
|
||||
.SH AUTHOR
|
||||
This manual page was originally written by Andreas Rottmann
|
||||
<rotty@debian.org>, for the Debian GNU/Linux system (but may be used
|
||||
by others). It was subsequently revised by Bill Bumgarner <bbum@mac.com>.
|
||||
by others). It was subsequently revised by Bill Bumgarner <bbum@mac.com> and
|
||||
further updated by Laszlo Boszormenyi <gcs@debian.hu> .
|
||||
|
||||
+8
-7
@@ -77,8 +77,8 @@ static void renameTableFunc(
|
||||
assert( len>0 );
|
||||
} while( token!=TK_LP && token!=TK_USING );
|
||||
|
||||
zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", ((u8*)tname.z) - zSql, zSql,
|
||||
zTableName, tname.z+tname.n);
|
||||
zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", (int)(((u8*)tname.z) - zSql),
|
||||
zSql, zTableName, tname.z+tname.n);
|
||||
sqlite3_result_text(context, zRet, -1, SQLITE_DYNAMIC);
|
||||
}
|
||||
}
|
||||
@@ -130,7 +130,7 @@ static void renameParentFunc(
|
||||
sqlite3Dequote(zParent);
|
||||
if( 0==sqlite3StrICmp((const char *)zOld, zParent) ){
|
||||
char *zOut = sqlite3MPrintf(db, "%s%.*s\"%w\"",
|
||||
(zOutput?zOutput:""), z-zInput, zInput, (const char *)zNew
|
||||
(zOutput?zOutput:""), (int)(z-zInput), zInput, (const char *)zNew
|
||||
);
|
||||
sqlite3DbFree(db, zOutput);
|
||||
zOutput = zOut;
|
||||
@@ -216,8 +216,8 @@ static void renameTriggerFunc(
|
||||
/* Variable tname now contains the token that is the old table-name
|
||||
** in the CREATE TRIGGER statement.
|
||||
*/
|
||||
zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", ((u8*)tname.z) - zSql, zSql,
|
||||
zTableName, tname.z+tname.n);
|
||||
zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", (int)(((u8*)tname.z) - zSql),
|
||||
zSql, zTableName, tname.z+tname.n);
|
||||
sqlite3_result_text(context, zRet, -1, SQLITE_DYNAMIC);
|
||||
}
|
||||
}
|
||||
@@ -469,7 +469,7 @@ void sqlite3AlterRenameTable(
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Begin a transaction and code the VerifyCookie for database iDb.
|
||||
/* Begin a transaction for database iDb.
|
||||
** Then modify the schema cookie (since the ALTER TABLE modifies the
|
||||
** schema). Open a statement transaction if the table is a virtual
|
||||
** table.
|
||||
@@ -605,6 +605,7 @@ void sqlite3MinimumFileFormat(Parse *pParse, int iDb, int minFormat){
|
||||
sqlite3VdbeUsesBtree(v, iDb);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, minFormat, r2);
|
||||
j1 = sqlite3VdbeAddOp3(v, OP_Ge, r2, 0, r1);
|
||||
sqlite3VdbeChangeP5(v, SQLITE_NOTNULL); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, r2);
|
||||
sqlite3VdbeJumpHere(v, j1);
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
@@ -687,7 +688,7 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
|
||||
** can handle (i.e. not CURRENT_TIME etc.)
|
||||
*/
|
||||
if( pDflt ){
|
||||
sqlite3_value *pVal;
|
||||
sqlite3_value *pVal = 0;
|
||||
if( sqlite3ValueFromExpr(db, pDflt, SQLITE_UTF8, SQLITE_AFF_NONE, &pVal) ){
|
||||
db->mallocFailed = 1;
|
||||
return;
|
||||
|
||||
+1179
-532
File diff suppressed because it is too large
Load Diff
+35
-23
@@ -38,10 +38,6 @@ static int resolveAttachExpr(NameContext *pName, Expr *pExpr)
|
||||
if( pExpr ){
|
||||
if( pExpr->op!=TK_ID ){
|
||||
rc = sqlite3ResolveExprNames(pName, pExpr);
|
||||
if( rc==SQLITE_OK && !sqlite3ExprIsConstant(pExpr) ){
|
||||
sqlite3ErrorMsg(pName->pParse, "invalid name: \"%s\"", pExpr->u.zToken);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
}else{
|
||||
pExpr->op = TK_STRING;
|
||||
}
|
||||
@@ -379,8 +375,7 @@ attach_end:
|
||||
void sqlite3Detach(Parse *pParse, Expr *pDbname){
|
||||
static const FuncDef detach_func = {
|
||||
1, /* nArg */
|
||||
SQLITE_UTF8, /* iPrefEnc */
|
||||
0, /* flags */
|
||||
SQLITE_UTF8, /* funcFlags */
|
||||
0, /* pUserData */
|
||||
0, /* pNext */
|
||||
detachFunc, /* xFunc */
|
||||
@@ -401,8 +396,7 @@ void sqlite3Detach(Parse *pParse, Expr *pDbname){
|
||||
void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey){
|
||||
static const FuncDef attach_func = {
|
||||
3, /* nArg */
|
||||
SQLITE_UTF8, /* iPrefEnc */
|
||||
0, /* flags */
|
||||
SQLITE_UTF8, /* funcFlags */
|
||||
0, /* pUserData */
|
||||
0, /* pNext */
|
||||
attachFunc, /* xFunc */
|
||||
@@ -419,11 +413,8 @@ void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey){
|
||||
/*
|
||||
** Initialize a DbFixer structure. This routine must be called prior
|
||||
** to passing the structure to one of the sqliteFixAAAA() routines below.
|
||||
**
|
||||
** The return value indicates whether or not fixation is required. TRUE
|
||||
** means we do need to fix the database references, FALSE means we do not.
|
||||
*/
|
||||
int sqlite3FixInit(
|
||||
void sqlite3FixInit(
|
||||
DbFixer *pFix, /* The fixer to be initialized */
|
||||
Parse *pParse, /* Error messages will be written here */
|
||||
int iDb, /* This is the database that must be used */
|
||||
@@ -432,7 +423,6 @@ int sqlite3FixInit(
|
||||
){
|
||||
sqlite3 *db;
|
||||
|
||||
if( NEVER(iDb<0) || iDb==1 ) return 0;
|
||||
db = pParse->db;
|
||||
assert( db->nDb>iDb );
|
||||
pFix->pParse = pParse;
|
||||
@@ -440,7 +430,7 @@ int sqlite3FixInit(
|
||||
pFix->pSchema = db->aDb[iDb].pSchema;
|
||||
pFix->zType = zType;
|
||||
pFix->pName = pName;
|
||||
return 1;
|
||||
pFix->bVarOnly = (iDb==1);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -468,15 +458,17 @@ int sqlite3FixSrcList(
|
||||
if( NEVER(pList==0) ) return 0;
|
||||
zDb = pFix->zDb;
|
||||
for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){
|
||||
if( pItem->zDatabase && sqlite3StrICmp(pItem->zDatabase, zDb) ){
|
||||
sqlite3ErrorMsg(pFix->pParse,
|
||||
"%s %T cannot reference objects in database %s",
|
||||
pFix->zType, pFix->pName, pItem->zDatabase);
|
||||
return 1;
|
||||
if( pFix->bVarOnly==0 ){
|
||||
if( pItem->zDatabase && sqlite3StrICmp(pItem->zDatabase, zDb) ){
|
||||
sqlite3ErrorMsg(pFix->pParse,
|
||||
"%s %T cannot reference objects in database %s",
|
||||
pFix->zType, pFix->pName, pItem->zDatabase);
|
||||
return 1;
|
||||
}
|
||||
sqlite3DbFree(pFix->pParse->db, pItem->zDatabase);
|
||||
pItem->zDatabase = 0;
|
||||
pItem->pSchema = pFix->pSchema;
|
||||
}
|
||||
sqlite3DbFree(pFix->pParse->db, pItem->zDatabase);
|
||||
pItem->zDatabase = 0;
|
||||
pItem->pSchema = pFix->pSchema;
|
||||
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER)
|
||||
if( sqlite3FixSelect(pFix, pItem->pSelect) ) return 1;
|
||||
if( sqlite3FixExpr(pFix, pItem->pOn) ) return 1;
|
||||
@@ -499,9 +491,21 @@ int sqlite3FixSelect(
|
||||
if( sqlite3FixExpr(pFix, pSelect->pWhere) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqlite3FixExprList(pFix, pSelect->pGroupBy) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqlite3FixExpr(pFix, pSelect->pHaving) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqlite3FixExprList(pFix, pSelect->pOrderBy) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqlite3FixExpr(pFix, pSelect->pLimit) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqlite3FixExpr(pFix, pSelect->pOffset) ){
|
||||
return 1;
|
||||
}
|
||||
pSelect = pSelect->pPrior;
|
||||
}
|
||||
return 0;
|
||||
@@ -511,7 +515,15 @@ int sqlite3FixExpr(
|
||||
Expr *pExpr /* The expression to be fixed to one database */
|
||||
){
|
||||
while( pExpr ){
|
||||
if( ExprHasAnyProperty(pExpr, EP_TokenOnly) ) break;
|
||||
if( pExpr->op==TK_VARIABLE ){
|
||||
if( pFix->pParse->db->init.busy ){
|
||||
pExpr->op = TK_NULL;
|
||||
}else{
|
||||
sqlite3ErrorMsg(pFix->pParse, "%s cannot use variables", pFix->zType);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if( ExprHasProperty(pExpr, EP_TokenOnly) ) break;
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
if( sqlite3FixSelect(pFix, pExpr->x.pSelect) ) return 1;
|
||||
}else{
|
||||
|
||||
+5
-4
@@ -96,6 +96,7 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
sqlite3DbFree(pErrorDb, pParse->zErrMsg);
|
||||
sqlite3ParserReset(pParse);
|
||||
sqlite3StackFree(pErrorDb, pParse);
|
||||
}
|
||||
if( rc ){
|
||||
@@ -525,7 +526,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
|
||||
|
||||
/* Sync the database file to disk. */
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3PagerSync(pDestPager);
|
||||
rc = sqlite3PagerSync(pDestPager, 0);
|
||||
}
|
||||
}else{
|
||||
sqlite3PagerTruncateImage(pDestPager, nDestTruncate);
|
||||
@@ -600,10 +601,10 @@ int sqlite3_backup_finish(sqlite3_backup *p){
|
||||
|
||||
/* Set the error code of the destination database handle. */
|
||||
rc = (p->rc==SQLITE_DONE) ? SQLITE_OK : p->rc;
|
||||
sqlite3Error(p->pDestDb, rc, 0);
|
||||
|
||||
/* Exit the mutexes and free the backup context structure. */
|
||||
if( p->pDestDb ){
|
||||
sqlite3Error(p->pDestDb, rc, 0);
|
||||
|
||||
/* Exit the mutexes and free the backup context structure. */
|
||||
sqlite3LeaveMutexAndCloseZombie(p->pDestDb);
|
||||
}
|
||||
sqlite3BtreeLeave(p->pSrc);
|
||||
|
||||
+172
-195
@@ -684,7 +684,7 @@ static int btreeMoveto(
|
||||
){
|
||||
int rc; /* Status code */
|
||||
UnpackedRecord *pIdxKey; /* Unpacked index key */
|
||||
char aSpace[150]; /* Temp space for pIdxKey - to avoid a malloc */
|
||||
char aSpace[200]; /* Temp space for pIdxKey - to avoid a malloc */
|
||||
char *pFree = 0;
|
||||
|
||||
if( pKey ){
|
||||
@@ -694,6 +694,10 @@ static int btreeMoveto(
|
||||
);
|
||||
if( pIdxKey==0 ) return SQLITE_NOMEM;
|
||||
sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey, pIdxKey);
|
||||
if( pIdxKey->nField==0 ){
|
||||
sqlite3DbFree(pCur->pKeyInfo->db, pFree);
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
}else{
|
||||
pIdxKey = 0;
|
||||
}
|
||||
@@ -1538,13 +1542,12 @@ static void zeroPage(MemPage *pPage, int flags){
|
||||
memset(&data[hdr], 0, pBt->usableSize - hdr);
|
||||
}
|
||||
data[hdr] = (char)flags;
|
||||
first = hdr + 8 + 4*((flags&PTF_LEAF)==0 ?1:0);
|
||||
first = hdr + ((flags&PTF_LEAF)==0 ? 12 : 8);
|
||||
memset(&data[hdr+1], 0, 4);
|
||||
data[hdr+7] = 0;
|
||||
put2byte(&data[hdr+5], pBt->usableSize);
|
||||
pPage->nFree = (u16)(pBt->usableSize - first);
|
||||
decodeFlags(pPage, flags);
|
||||
pPage->hdrOffset = hdr;
|
||||
pPage->cellOffset = first;
|
||||
pPage->aDataEnd = &data[pBt->usableSize];
|
||||
pPage->aCellIdx = &data[first];
|
||||
@@ -1648,7 +1651,7 @@ static int getAndInitPage(
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
}else{
|
||||
rc = btreeGetPage(pBt, pgno, ppPage, bReadonly);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( rc==SQLITE_OK && (*ppPage)->isInit==0 ){
|
||||
rc = btreeInitPage(*ppPage);
|
||||
if( rc!=SQLITE_OK ){
|
||||
releasePage(*ppPage);
|
||||
@@ -1669,10 +1672,11 @@ static void releasePage(MemPage *pPage){
|
||||
if( pPage ){
|
||||
assert( pPage->aData );
|
||||
assert( pPage->pBt );
|
||||
assert( pPage->pDbPage!=0 );
|
||||
assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage );
|
||||
assert( sqlite3PagerGetData(pPage->pDbPage)==pPage->aData );
|
||||
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
|
||||
sqlite3PagerUnref(pPage->pDbPage);
|
||||
sqlite3PagerUnrefNotNull(pPage->pDbPage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2052,6 +2056,18 @@ static int removeFromSharingList(BtShared *pBt){
|
||||
static void allocateTempSpace(BtShared *pBt){
|
||||
if( !pBt->pTmpSpace ){
|
||||
pBt->pTmpSpace = sqlite3PageMalloc( pBt->pageSize );
|
||||
|
||||
/* One of the uses of pBt->pTmpSpace is to format cells before
|
||||
** inserting them into a leaf page (function fillInCell()). If
|
||||
** a cell is less than 4 bytes in size, it is rounded up to 4 bytes
|
||||
** by the various routines that manipulate binary cells. Which
|
||||
** can mean that fillInCell() only initializes the first 2 or 3
|
||||
** bytes of pTmpSpace, but that the first 4 bytes are copied from
|
||||
** it into a database page. This is not actually a problem, but it
|
||||
** does cause a valgrind error when the 1 or 2 bytes of unitialized
|
||||
** data is passed to system call write(). So to avoid this error,
|
||||
** zero the first 4 bytes of temp space here. */
|
||||
if( pBt->pTmpSpace ) memset(pBt->pTmpSpace, 0, 4);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2667,7 +2683,7 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
|
||||
if( p->inTrans==TRANS_WRITE || (p->inTrans==TRANS_READ && !wrflag) ){
|
||||
goto trans_begun;
|
||||
}
|
||||
assert( IfNotOmitAV(pBt->bDoTruncate)==0 );
|
||||
assert( pBt->inTransaction==TRANS_WRITE || IfNotOmitAV(pBt->bDoTruncate)==0 );
|
||||
|
||||
/* Write transactions are not possible on a read-only database */
|
||||
if( (pBt->btsFlags & BTS_READ_ONLY)!=0 && wrflag ){
|
||||
@@ -3615,7 +3631,6 @@ static int btreeCursor(
|
||||
}
|
||||
pBt->pCursor = pCur;
|
||||
pCur->eState = CURSOR_INVALID;
|
||||
pCur->cachedRowid = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
int sqlite3BtreeCursor(
|
||||
@@ -3656,36 +3671,6 @@ void sqlite3BtreeCursorZero(BtCursor *p){
|
||||
memset(p, 0, offsetof(BtCursor, iPage));
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the cached rowid value of every cursor in the same database file
|
||||
** as pCur and having the same root page number as pCur. The value is
|
||||
** set to iRowid.
|
||||
**
|
||||
** Only positive rowid values are considered valid for this cache.
|
||||
** The cache is initialized to zero, indicating an invalid cache.
|
||||
** A btree will work fine with zero or negative rowids. We just cannot
|
||||
** cache zero or negative rowids, which means tables that use zero or
|
||||
** negative rowids might run a little slower. But in practice, zero
|
||||
** or negative rowids are very uncommon so this should not be a problem.
|
||||
*/
|
||||
void sqlite3BtreeSetCachedRowid(BtCursor *pCur, sqlite3_int64 iRowid){
|
||||
BtCursor *p;
|
||||
for(p=pCur->pBt->pCursor; p; p=p->pNext){
|
||||
if( p->pgnoRoot==pCur->pgnoRoot ) p->cachedRowid = iRowid;
|
||||
}
|
||||
assert( pCur->cachedRowid==iRowid );
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the cached rowid for the given cursor. A negative or zero
|
||||
** return value indicates that the rowid cache is invalid and should be
|
||||
** ignored. If the rowid cache has never before been set, then a
|
||||
** zero is returned.
|
||||
*/
|
||||
sqlite3_int64 sqlite3BtreeGetCachedRowid(BtCursor *pCur){
|
||||
return pCur->cachedRowid;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a cursor. The read lock on the database file is released
|
||||
** when the last cursor is closed.
|
||||
@@ -3737,7 +3722,7 @@ int sqlite3BtreeCloseCursor(BtCursor *pCur){
|
||||
int iPage = pCur->iPage;
|
||||
memset(&info, 0, sizeof(info));
|
||||
btreeParseCell(pCur->apPage[iPage], pCur->aiIdx[iPage], &info);
|
||||
assert( memcmp(&info, &pCur->info, sizeof(info))==0 );
|
||||
assert( CORRUPT_DB || memcmp(&info, &pCur->info, sizeof(info))==0 );
|
||||
}
|
||||
#else
|
||||
#define assertCellInfo(x)
|
||||
@@ -4176,10 +4161,10 @@ int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
|
||||
/*
|
||||
** Return a pointer to payload information from the entry that the
|
||||
** pCur cursor is pointing to. The pointer is to the beginning of
|
||||
** the key if skipKey==0 and it points to the beginning of data if
|
||||
** skipKey==1. The number of bytes of available key/data is written
|
||||
** into *pAmt. If *pAmt==0, then the value returned will not be
|
||||
** a valid pointer.
|
||||
** the key if index btrees (pPage->intKey==0) and is the data for
|
||||
** table btrees (pPage->intKey==1). The number of bytes of available
|
||||
** key/data is written into *pAmt. If *pAmt==0, then the value
|
||||
** returned will not be a valid pointer.
|
||||
**
|
||||
** This routine is an optimization. It is common for the entire key
|
||||
** and data to fit on the local page and for there to be no overflow
|
||||
@@ -4192,41 +4177,21 @@ int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
|
||||
** page of the database. The data might change or move the next time
|
||||
** any btree routine is called.
|
||||
*/
|
||||
static const unsigned char *fetchPayload(
|
||||
static const void *fetchPayload(
|
||||
BtCursor *pCur, /* Cursor pointing to entry to read from */
|
||||
int *pAmt, /* Write the number of available bytes here */
|
||||
int skipKey /* read beginning at data if this is true */
|
||||
u32 *pAmt /* Write the number of available bytes here */
|
||||
){
|
||||
unsigned char *aPayload;
|
||||
MemPage *pPage;
|
||||
u32 nKey;
|
||||
u32 nLocal;
|
||||
|
||||
assert( pCur!=0 && pCur->iPage>=0 && pCur->apPage[pCur->iPage]);
|
||||
assert( pCur->eState==CURSOR_VALID );
|
||||
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
pPage = pCur->apPage[pCur->iPage];
|
||||
assert( pCur->aiIdx[pCur->iPage]<pPage->nCell );
|
||||
if( NEVER(pCur->info.nSize==0) ){
|
||||
assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell );
|
||||
if( pCur->info.nSize==0 ){
|
||||
btreeParseCell(pCur->apPage[pCur->iPage], pCur->aiIdx[pCur->iPage],
|
||||
&pCur->info);
|
||||
}
|
||||
aPayload = pCur->info.pCell;
|
||||
aPayload += pCur->info.nHeader;
|
||||
if( pPage->intKey ){
|
||||
nKey = 0;
|
||||
}else{
|
||||
nKey = (int)pCur->info.nKey;
|
||||
}
|
||||
if( skipKey ){
|
||||
aPayload += nKey;
|
||||
nLocal = pCur->info.nLocal - nKey;
|
||||
}else{
|
||||
nLocal = pCur->info.nLocal;
|
||||
assert( nLocal<=nKey );
|
||||
}
|
||||
*pAmt = nLocal;
|
||||
return aPayload;
|
||||
*pAmt = pCur->info.nLocal;
|
||||
return (void*)(pCur->info.pCell + pCur->info.nHeader);
|
||||
}
|
||||
|
||||
|
||||
@@ -4244,23 +4209,11 @@ static const unsigned char *fetchPayload(
|
||||
** These routines is used to get quick access to key and data
|
||||
** in the common case where no overflow pages are used.
|
||||
*/
|
||||
const void *sqlite3BtreeKeyFetch(BtCursor *pCur, int *pAmt){
|
||||
const void *p = 0;
|
||||
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
if( ALWAYS(pCur->eState==CURSOR_VALID) ){
|
||||
p = (const void*)fetchPayload(pCur, pAmt, 0);
|
||||
}
|
||||
return p;
|
||||
const void *sqlite3BtreeKeyFetch(BtCursor *pCur, u32 *pAmt){
|
||||
return fetchPayload(pCur, pAmt);
|
||||
}
|
||||
const void *sqlite3BtreeDataFetch(BtCursor *pCur, int *pAmt){
|
||||
const void *p = 0;
|
||||
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
if( ALWAYS(pCur->eState==CURSOR_VALID) ){
|
||||
p = (const void*)fetchPayload(pCur, pAmt, 1);
|
||||
}
|
||||
return p;
|
||||
const void *sqlite3BtreeDataFetch(BtCursor *pCur, u32 *pAmt){
|
||||
return fetchPayload(pCur, pAmt);
|
||||
}
|
||||
|
||||
|
||||
@@ -4379,8 +4332,6 @@ static void moveToParent(BtCursor *pCur){
|
||||
static int moveToRoot(BtCursor *pCur){
|
||||
MemPage *pRoot;
|
||||
int rc = SQLITE_OK;
|
||||
Btree *p = pCur->pBtree;
|
||||
BtShared *pBt = p->pBt;
|
||||
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
assert( CURSOR_INVALID < CURSOR_REQUIRESEEK );
|
||||
@@ -4395,56 +4346,52 @@ static int moveToRoot(BtCursor *pCur){
|
||||
}
|
||||
|
||||
if( pCur->iPage>=0 ){
|
||||
int i;
|
||||
for(i=1; i<=pCur->iPage; i++){
|
||||
releasePage(pCur->apPage[i]);
|
||||
}
|
||||
pCur->iPage = 0;
|
||||
while( pCur->iPage ) releasePage(pCur->apPage[pCur->iPage--]);
|
||||
}else if( pCur->pgnoRoot==0 ){
|
||||
pCur->eState = CURSOR_INVALID;
|
||||
return SQLITE_OK;
|
||||
}else{
|
||||
rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->apPage[0],
|
||||
rc = getAndInitPage(pCur->pBtree->pBt, pCur->pgnoRoot, &pCur->apPage[0],
|
||||
pCur->wrFlag==0 ? PAGER_GET_READONLY : 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
pCur->eState = CURSOR_INVALID;
|
||||
return rc;
|
||||
}
|
||||
pCur->iPage = 0;
|
||||
|
||||
/* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor
|
||||
** expected to open it on an index b-tree. Otherwise, if pKeyInfo is
|
||||
** NULL, the caller expects a table b-tree. If this is not the case,
|
||||
** return an SQLITE_CORRUPT error. */
|
||||
assert( pCur->apPage[0]->intKey==1 || pCur->apPage[0]->intKey==0 );
|
||||
if( (pCur->pKeyInfo==0)!=pCur->apPage[0]->intKey ){
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
}
|
||||
|
||||
/* Assert that the root page is of the correct type. This must be the
|
||||
** case as the call to this function that loaded the root-page (either
|
||||
** this call or a previous invocation) would have detected corruption
|
||||
** if the assumption were not true, and it is not possible for the flags
|
||||
** byte to have been modified while this cursor is holding a reference
|
||||
** to the page. */
|
||||
pRoot = pCur->apPage[0];
|
||||
assert( pRoot->pgno==pCur->pgnoRoot );
|
||||
assert( pRoot->isInit && (pCur->pKeyInfo==0)==pRoot->intKey );
|
||||
|
||||
/* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor
|
||||
** expected to open it on an index b-tree. Otherwise, if pKeyInfo is
|
||||
** NULL, the caller expects a table b-tree. If this is not the case,
|
||||
** return an SQLITE_CORRUPT error.
|
||||
**
|
||||
** Earlier versions of SQLite assumed that this test could not fail
|
||||
** if the root page was already loaded when this function was called (i.e.
|
||||
** if pCur->iPage>=0). But this is not so if the database is corrupted
|
||||
** in such a way that page pRoot is linked into a second b-tree table
|
||||
** (or the freelist). */
|
||||
assert( pRoot->intKey==1 || pRoot->intKey==0 );
|
||||
if( pRoot->isInit==0 || (pCur->pKeyInfo==0)!=pRoot->intKey ){
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
|
||||
pCur->aiIdx[0] = 0;
|
||||
pCur->info.nSize = 0;
|
||||
pCur->atLast = 0;
|
||||
pCur->validNKey = 0;
|
||||
|
||||
if( pRoot->nCell==0 && !pRoot->leaf ){
|
||||
if( pRoot->nCell>0 ){
|
||||
pCur->eState = CURSOR_VALID;
|
||||
}else if( !pRoot->leaf ){
|
||||
Pgno subpage;
|
||||
if( pRoot->pgno!=1 ) return SQLITE_CORRUPT_BKPT;
|
||||
subpage = get4byte(&pRoot->aData[pRoot->hdrOffset+8]);
|
||||
pCur->eState = CURSOR_VALID;
|
||||
rc = moveToChild(pCur, subpage);
|
||||
}else{
|
||||
pCur->eState = ((pRoot->nCell>0)?CURSOR_VALID:CURSOR_INVALID);
|
||||
pCur->eState = CURSOR_INVALID;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -4600,6 +4547,7 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
int *pRes /* Write search results here */
|
||||
){
|
||||
int rc;
|
||||
RecordCompare xRecordCompare;
|
||||
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
|
||||
@@ -4621,6 +4569,16 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
}
|
||||
}
|
||||
|
||||
if( pIdxKey ){
|
||||
xRecordCompare = sqlite3VdbeFindCompare(pIdxKey);
|
||||
assert( pIdxKey->default_rc==1
|
||||
|| pIdxKey->default_rc==0
|
||||
|| pIdxKey->default_rc==-1
|
||||
);
|
||||
}else{
|
||||
xRecordCompare = 0; /* All keys are integers */
|
||||
}
|
||||
|
||||
rc = moveToRoot(pCur);
|
||||
if( rc ){
|
||||
return rc;
|
||||
@@ -4635,10 +4593,10 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
}
|
||||
assert( pCur->apPage[0]->intKey || pIdxKey );
|
||||
for(;;){
|
||||
int lwr, upr, idx;
|
||||
int lwr, upr, idx, c;
|
||||
Pgno chldPg;
|
||||
MemPage *pPage = pCur->apPage[pCur->iPage];
|
||||
int c;
|
||||
u8 *pCell; /* Pointer to current cell in pPage */
|
||||
|
||||
/* pPage->nCell must be greater than zero. If this is the root-page
|
||||
** the cursor would have been INVALID above and this for(;;) loop
|
||||
@@ -4650,35 +4608,47 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
assert( pPage->intKey==(pIdxKey==0) );
|
||||
lwr = 0;
|
||||
upr = pPage->nCell-1;
|
||||
if( biasRight ){
|
||||
pCur->aiIdx[pCur->iPage] = (u16)(idx = upr);
|
||||
}else{
|
||||
pCur->aiIdx[pCur->iPage] = (u16)(idx = (upr+lwr)/2);
|
||||
}
|
||||
for(;;){
|
||||
u8 *pCell; /* Pointer to current cell in pPage */
|
||||
|
||||
assert( idx==pCur->aiIdx[pCur->iPage] );
|
||||
pCur->info.nSize = 0;
|
||||
pCell = findCell(pPage, idx) + pPage->childPtrSize;
|
||||
if( pPage->intKey ){
|
||||
assert( biasRight==0 || biasRight==1 );
|
||||
idx = upr>>(1-biasRight); /* idx = biasRight ? upr : (lwr+upr)/2; */
|
||||
pCur->aiIdx[pCur->iPage] = (u16)idx;
|
||||
if( xRecordCompare==0 ){
|
||||
for(;;){
|
||||
i64 nCellKey;
|
||||
pCell = findCell(pPage, idx) + pPage->childPtrSize;
|
||||
if( pPage->hasData ){
|
||||
u32 dummy;
|
||||
pCell += getVarint32(pCell, dummy);
|
||||
while( 0x80 <= *(pCell++) ){
|
||||
if( pCell>=pPage->aDataEnd ) return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
}
|
||||
getVarint(pCell, (u64*)&nCellKey);
|
||||
if( nCellKey==intKey ){
|
||||
c = 0;
|
||||
}else if( nCellKey<intKey ){
|
||||
c = -1;
|
||||
if( nCellKey<intKey ){
|
||||
lwr = idx+1;
|
||||
if( lwr>upr ){ c = -1; break; }
|
||||
}else if( nCellKey>intKey ){
|
||||
upr = idx-1;
|
||||
if( lwr>upr ){ c = +1; break; }
|
||||
}else{
|
||||
assert( nCellKey>intKey );
|
||||
c = +1;
|
||||
assert( nCellKey==intKey );
|
||||
pCur->validNKey = 1;
|
||||
pCur->info.nKey = nCellKey;
|
||||
pCur->aiIdx[pCur->iPage] = (u16)idx;
|
||||
if( !pPage->leaf ){
|
||||
lwr = idx;
|
||||
goto moveto_next_layer;
|
||||
}else{
|
||||
*pRes = 0;
|
||||
rc = SQLITE_OK;
|
||||
goto moveto_finish;
|
||||
}
|
||||
}
|
||||
pCur->validNKey = 1;
|
||||
pCur->info.nKey = nCellKey;
|
||||
}else{
|
||||
assert( lwr+upr>=0 );
|
||||
idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2; */
|
||||
}
|
||||
}else{
|
||||
for(;;){
|
||||
int nCell;
|
||||
pCell = findCell(pPage, idx) + pPage->childPtrSize;
|
||||
|
||||
/* The maximum supported page-size is 65536 bytes. This means that
|
||||
** the maximum number of record bytes stored on an index B-Tree
|
||||
** page is less than 16384 bytes and may be stored as a 2-byte
|
||||
@@ -4687,23 +4657,20 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
** stored entirely within the b-tree page by inspecting the first
|
||||
** 2 bytes of the cell.
|
||||
*/
|
||||
int nCell = pCell[0];
|
||||
if( nCell<=pPage->max1bytePayload
|
||||
/* && (pCell+nCell)<pPage->aDataEnd */
|
||||
){
|
||||
nCell = pCell[0];
|
||||
if( nCell<=pPage->max1bytePayload ){
|
||||
/* This branch runs if the record-size field of the cell is a
|
||||
** single byte varint and the record fits entirely on the main
|
||||
** b-tree page. */
|
||||
testcase( pCell+nCell+1==pPage->aDataEnd );
|
||||
c = sqlite3VdbeRecordCompare(nCell, (void*)&pCell[1], pIdxKey);
|
||||
c = xRecordCompare(nCell, (void*)&pCell[1], pIdxKey, 0);
|
||||
}else if( !(pCell[1] & 0x80)
|
||||
&& (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal
|
||||
/* && (pCell+nCell+2)<=pPage->aDataEnd */
|
||||
){
|
||||
/* The record-size field is a 2 byte varint and the record
|
||||
** fits entirely on the main b-tree page. */
|
||||
testcase( pCell+nCell+2==pPage->aDataEnd );
|
||||
c = sqlite3VdbeRecordCompare(nCell, (void*)&pCell[2], pIdxKey);
|
||||
c = xRecordCompare(nCell, (void*)&pCell[2], pIdxKey, 0);
|
||||
}else{
|
||||
/* The record flows over onto one or more overflow pages. In
|
||||
** this case the whole cell needs to be parsed, a buffer allocated
|
||||
@@ -4718,57 +4685,53 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
rc = SQLITE_NOMEM;
|
||||
goto moveto_finish;
|
||||
}
|
||||
pCur->aiIdx[pCur->iPage] = (u16)idx;
|
||||
rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0);
|
||||
if( rc ){
|
||||
sqlite3_free(pCellKey);
|
||||
goto moveto_finish;
|
||||
}
|
||||
c = sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey);
|
||||
c = xRecordCompare(nCell, pCellKey, pIdxKey, 0);
|
||||
sqlite3_free(pCellKey);
|
||||
}
|
||||
}
|
||||
if( c==0 ){
|
||||
if( pPage->intKey && !pPage->leaf ){
|
||||
lwr = idx;
|
||||
break;
|
||||
if( c<0 ){
|
||||
lwr = idx+1;
|
||||
}else if( c>0 ){
|
||||
upr = idx-1;
|
||||
}else{
|
||||
assert( c==0 );
|
||||
*pRes = 0;
|
||||
rc = SQLITE_OK;
|
||||
pCur->aiIdx[pCur->iPage] = (u16)idx;
|
||||
goto moveto_finish;
|
||||
}
|
||||
if( lwr>upr ) break;
|
||||
assert( lwr+upr>=0 );
|
||||
idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2 */
|
||||
}
|
||||
if( c<0 ){
|
||||
lwr = idx+1;
|
||||
}else{
|
||||
upr = idx-1;
|
||||
}
|
||||
if( lwr>upr ){
|
||||
break;
|
||||
}
|
||||
pCur->aiIdx[pCur->iPage] = (u16)(idx = (lwr+upr)/2);
|
||||
}
|
||||
assert( lwr==upr+1 || (pPage->intKey && !pPage->leaf) );
|
||||
assert( pPage->isInit );
|
||||
if( pPage->leaf ){
|
||||
chldPg = 0;
|
||||
}else if( lwr>=pPage->nCell ){
|
||||
chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]);
|
||||
}else{
|
||||
chldPg = get4byte(findCell(pPage, lwr));
|
||||
}
|
||||
if( chldPg==0 ){
|
||||
assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell );
|
||||
pCur->aiIdx[pCur->iPage] = (u16)idx;
|
||||
*pRes = c;
|
||||
rc = SQLITE_OK;
|
||||
goto moveto_finish;
|
||||
}
|
||||
moveto_next_layer:
|
||||
if( lwr>=pPage->nCell ){
|
||||
chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]);
|
||||
}else{
|
||||
chldPg = get4byte(findCell(pPage, lwr));
|
||||
}
|
||||
pCur->aiIdx[pCur->iPage] = (u16)lwr;
|
||||
pCur->info.nSize = 0;
|
||||
pCur->validNKey = 0;
|
||||
rc = moveToChild(pCur, chldPg);
|
||||
if( rc ) goto moveto_finish;
|
||||
if( rc ) break;
|
||||
}
|
||||
moveto_finish:
|
||||
pCur->info.nSize = 0;
|
||||
pCur->validNKey = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -4793,6 +4756,15 @@ int sqlite3BtreeEof(BtCursor *pCur){
|
||||
** successful then set *pRes=0. If the cursor
|
||||
** was already pointing to the last entry in the database before
|
||||
** this routine was called, then set *pRes=1.
|
||||
**
|
||||
** The calling function will set *pRes to 0 or 1. The initial *pRes value
|
||||
** will be 1 if the cursor being stepped corresponds to an SQL index and
|
||||
** if this routine could have been skipped if that SQL index had been
|
||||
** a unique index. Otherwise the caller will have set *pRes to zero.
|
||||
** Zero is the common case. The btree implementation is free to use the
|
||||
** initial *pRes value as a hint to improve performance, but the current
|
||||
** SQLite btree implementation does not. (Note that the comdb2 btree
|
||||
** implementation does use this hint, however.)
|
||||
*/
|
||||
int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
|
||||
int rc;
|
||||
@@ -4801,6 +4773,7 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
|
||||
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
assert( pRes!=0 );
|
||||
assert( *pRes==0 || *pRes==1 );
|
||||
assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID );
|
||||
if( pCur->eState!=CURSOR_VALID ){
|
||||
rc = restoreCursorPosition(pCur);
|
||||
@@ -4879,6 +4852,15 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
|
||||
** successful then set *pRes=0. If the cursor
|
||||
** was already pointing to the first entry in the database before
|
||||
** this routine was called, then set *pRes=1.
|
||||
**
|
||||
** The calling function will set *pRes to 0 or 1. The initial *pRes value
|
||||
** will be 1 if the cursor being stepped corresponds to an SQL index and
|
||||
** if this routine could have been skipped if that SQL index had been
|
||||
** a unique index. Otherwise the caller will have set *pRes to zero.
|
||||
** Zero is the common case. The btree implementation is free to use the
|
||||
** initial *pRes value as a hint to improve performance, but the current
|
||||
** SQLite btree implementation does not. (Note that the comdb2 btree
|
||||
** implementation does use this hint, however.)
|
||||
*/
|
||||
int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
|
||||
int rc;
|
||||
@@ -4886,6 +4868,7 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
|
||||
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
assert( pRes!=0 );
|
||||
assert( *pRes==0 || *pRes==1 );
|
||||
assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID );
|
||||
pCur->atLast = 0;
|
||||
if( pCur->eState!=CURSOR_VALID ){
|
||||
@@ -5265,6 +5248,7 @@ end_allocate_page:
|
||||
if( rc==SQLITE_OK ){
|
||||
if( sqlite3PagerPageRefcount((*ppPage)->pDbPage)>1 ){
|
||||
releasePage(*ppPage);
|
||||
*ppPage = 0;
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
(*ppPage)->isInit = 0;
|
||||
@@ -5526,7 +5510,7 @@ static int fillInCell(
|
||||
nHeader += 4;
|
||||
}
|
||||
if( pPage->hasData ){
|
||||
nHeader += putVarint(&pCell[nHeader], nData+nZero);
|
||||
nHeader += putVarint32(&pCell[nHeader], nData+nZero);
|
||||
}else{
|
||||
nData = nZero = 0;
|
||||
}
|
||||
@@ -5654,7 +5638,6 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
|
||||
u32 pc; /* Offset to cell content of cell being deleted */
|
||||
u8 *data; /* pPage->aData */
|
||||
u8 *ptr; /* Used to move bytes around within data[] */
|
||||
u8 *endPtr; /* End of loop */
|
||||
int rc; /* The return code */
|
||||
int hdr; /* Beginning of the header. 0 most pages. 100 page 1 */
|
||||
|
||||
@@ -5679,13 +5662,8 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
|
||||
*pRC = rc;
|
||||
return;
|
||||
}
|
||||
endPtr = &pPage->aCellIdx[2*pPage->nCell - 2];
|
||||
assert( (SQLITE_PTR_TO_INT(ptr)&1)==0 ); /* ptr is always 2-byte aligned */
|
||||
while( ptr<endPtr ){
|
||||
*(u16*)ptr = *(u16*)&ptr[2];
|
||||
ptr += 2;
|
||||
}
|
||||
pPage->nCell--;
|
||||
memmove(ptr, ptr+2, 2*(pPage->nCell - idx));
|
||||
put2byte(&data[hdr+3], pPage->nCell);
|
||||
pPage->nFree += 2;
|
||||
}
|
||||
@@ -5722,9 +5700,6 @@ static void insertCell(
|
||||
int ins; /* Index in data[] where new cell pointer is inserted */
|
||||
int cellOffset; /* Address of first cell pointer in data[] */
|
||||
u8 *data; /* The content of the whole page */
|
||||
u8 *ptr; /* Used for moving information around in data[] */
|
||||
u8 *endPtr; /* End of the loop */
|
||||
|
||||
int nSkip = (iChild ? 4 : 0);
|
||||
|
||||
if( *pRC ) return;
|
||||
@@ -5775,13 +5750,7 @@ static void insertCell(
|
||||
if( iChild ){
|
||||
put4byte(&data[idx], iChild);
|
||||
}
|
||||
ptr = &data[end];
|
||||
endPtr = &data[ins];
|
||||
assert( (SQLITE_PTR_TO_INT(ptr)&1)==0 ); /* ptr is always 2-byte aligned */
|
||||
while( ptr>endPtr ){
|
||||
*(u16*)ptr = *(u16*)&ptr[-2];
|
||||
ptr -= 2;
|
||||
}
|
||||
memmove(&data[ins+2], &data[ins], end-ins);
|
||||
put2byte(&data[ins], idx);
|
||||
put2byte(&data[pPage->hdrOffset+3], pPage->nCell);
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
@@ -6996,11 +6965,17 @@ int sqlite3BtreeInsert(
|
||||
rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
|
||||
if( rc ) return rc;
|
||||
|
||||
/* If this is an insert into a table b-tree, invalidate any incrblob
|
||||
** cursors open on the row being replaced (assuming this is a replace
|
||||
** operation - if it is not, the following is a no-op). */
|
||||
if( pCur->pKeyInfo==0 ){
|
||||
/* If this is an insert into a table b-tree, invalidate any incrblob
|
||||
** cursors open on the row being replaced */
|
||||
invalidateIncrblobCursors(p, nKey, 0);
|
||||
|
||||
/* If the cursor is currently on the last row and we are appending a
|
||||
** new row onto the end, set the "loc" to avoid an unnecessary btreeMoveto()
|
||||
** call */
|
||||
if( pCur->validNKey && nKey>0 && pCur->info.nKey==nKey-1 ){
|
||||
loc = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if( !loc ){
|
||||
@@ -7070,8 +7045,8 @@ int sqlite3BtreeInsert(
|
||||
** row without seeking the cursor. This can be a big performance boost.
|
||||
*/
|
||||
pCur->info.nSize = 0;
|
||||
pCur->validNKey = 0;
|
||||
if( rc==SQLITE_OK && pPage->nOverflow ){
|
||||
pCur->validNKey = 0;
|
||||
rc = balance(pCur);
|
||||
|
||||
/* Must make sure nOverflow is reset to zero even if the balance()
|
||||
@@ -7126,7 +7101,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){
|
||||
** sub-tree headed by the child page of the cell being deleted. This makes
|
||||
** balancing the tree following the delete operation easier. */
|
||||
if( !pPage->leaf ){
|
||||
int notUsed;
|
||||
int notUsed = 0;
|
||||
rc = sqlite3BtreePrevious(pCur, ¬Used);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
@@ -7379,6 +7354,7 @@ static int clearDatabasePage(
|
||||
int rc;
|
||||
unsigned char *pCell;
|
||||
int i;
|
||||
int hdr;
|
||||
|
||||
assert( sqlite3_mutex_held(pBt->mutex) );
|
||||
if( pgno>btreePagecount(pBt) ){
|
||||
@@ -7387,6 +7363,7 @@ static int clearDatabasePage(
|
||||
|
||||
rc = getAndInitPage(pBt, pgno, &pPage, 0);
|
||||
if( rc ) return rc;
|
||||
hdr = pPage->hdrOffset;
|
||||
for(i=0; i<pPage->nCell; i++){
|
||||
pCell = findCell(pPage, i);
|
||||
if( !pPage->leaf ){
|
||||
@@ -7397,7 +7374,7 @@ static int clearDatabasePage(
|
||||
if( rc ) goto cleardatabasepage_out;
|
||||
}
|
||||
if( !pPage->leaf ){
|
||||
rc = clearDatabasePage(pBt, get4byte(&pPage->aData[8]), 1, pnChange);
|
||||
rc = clearDatabasePage(pBt, get4byte(&pPage->aData[hdr+8]), 1, pnChange);
|
||||
if( rc ) goto cleardatabasepage_out;
|
||||
}else if( pnChange ){
|
||||
assert( pPage->intKey );
|
||||
@@ -7406,7 +7383,7 @@ static int clearDatabasePage(
|
||||
if( freePageFlag ){
|
||||
freePage(pPage, &rc);
|
||||
}else if( (rc = sqlite3PagerWrite(pPage->pDbPage))==0 ){
|
||||
zeroPage(pPage, pPage->aData[0] | PTF_LEAF);
|
||||
zeroPage(pPage, pPage->aData[hdr] | PTF_LEAF);
|
||||
}
|
||||
|
||||
cleardatabasepage_out:
|
||||
@@ -7743,7 +7720,7 @@ static void checkAppendMsg(
|
||||
sqlite3StrAccumAppend(&pCheck->errMsg, "\n", 1);
|
||||
}
|
||||
if( zMsg1 ){
|
||||
sqlite3StrAccumAppend(&pCheck->errMsg, zMsg1, -1);
|
||||
sqlite3StrAccumAppendAll(&pCheck->errMsg, zMsg1);
|
||||
}
|
||||
sqlite3VXPrintf(&pCheck->errMsg, 1, zFormat, ap);
|
||||
va_end(ap);
|
||||
|
||||
+2
-4
@@ -178,12 +178,10 @@ int sqlite3BtreeEof(BtCursor*);
|
||||
int sqlite3BtreePrevious(BtCursor*, int *pRes);
|
||||
int sqlite3BtreeKeySize(BtCursor*, i64 *pSize);
|
||||
int sqlite3BtreeKey(BtCursor*, u32 offset, u32 amt, void*);
|
||||
const void *sqlite3BtreeKeyFetch(BtCursor*, int *pAmt);
|
||||
const void *sqlite3BtreeDataFetch(BtCursor*, int *pAmt);
|
||||
const void *sqlite3BtreeKeyFetch(BtCursor*, u32 *pAmt);
|
||||
const void *sqlite3BtreeDataFetch(BtCursor*, u32 *pAmt);
|
||||
int sqlite3BtreeDataSize(BtCursor*, u32 *pSize);
|
||||
int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*);
|
||||
void sqlite3BtreeSetCachedRowid(BtCursor*, sqlite3_int64);
|
||||
sqlite3_int64 sqlite3BtreeGetCachedRowid(BtCursor*);
|
||||
|
||||
char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*);
|
||||
struct Pager *sqlite3BtreePager(Btree*);
|
||||
|
||||
+8
-8
@@ -56,13 +56,13 @@
|
||||
**
|
||||
** OFFSET SIZE DESCRIPTION
|
||||
** 0 16 Header string: "SQLite format 3\000"
|
||||
** 16 2 Page size in bytes.
|
||||
** 16 2 Page size in bytes. (1 means 65536)
|
||||
** 18 1 File format write version
|
||||
** 19 1 File format read version
|
||||
** 20 1 Bytes of unused space at the end of each page
|
||||
** 21 1 Max embedded payload fraction
|
||||
** 22 1 Min embedded payload fraction
|
||||
** 23 1 Min leaf payload fraction
|
||||
** 21 1 Max embedded payload fraction (must be 64)
|
||||
** 22 1 Min embedded payload fraction (must be 32)
|
||||
** 23 1 Min leaf payload fraction (must be 32)
|
||||
** 24 4 File change counter
|
||||
** 28 4 Reserved for future use
|
||||
** 32 4 First freelist page
|
||||
@@ -76,9 +76,10 @@
|
||||
** 56 4 1=UTF-8 2=UTF16le 3=UTF16be
|
||||
** 60 4 User version
|
||||
** 64 4 Incremental vacuum mode
|
||||
** 68 4 unused
|
||||
** 72 4 unused
|
||||
** 76 4 unused
|
||||
** 68 4 Application-ID
|
||||
** 72 20 unused
|
||||
** 92 4 The version-valid-for number
|
||||
** 96 4 SQLITE_VERSION_NUMBER
|
||||
**
|
||||
** All of the integer values are big-endian (most significant byte first).
|
||||
**
|
||||
@@ -499,7 +500,6 @@ struct BtCursor {
|
||||
Pgno *aOverflow; /* Cache of overflow page locations */
|
||||
#endif
|
||||
Pgno pgnoRoot; /* The root page of this tree */
|
||||
sqlite3_int64 cachedRowid; /* Next rowid cache. 0 means not valid */
|
||||
CellInfo info; /* A parse of the cell we are pointing at */
|
||||
i64 nKey; /* Size of pKey, or last integer key */
|
||||
void *pKey; /* Saved key that was cursor's last known position */
|
||||
|
||||
+623
-211
File diff suppressed because it is too large
Load Diff
+3
-4
@@ -270,9 +270,9 @@ static int matchQuality(
|
||||
}
|
||||
|
||||
/* Bonus points if the text encoding matches */
|
||||
if( enc==p->iPrefEnc ){
|
||||
if( enc==(p->funcFlags & SQLITE_FUNC_ENCMASK) ){
|
||||
match += 2; /* Exact encoding match */
|
||||
}else if( (enc & p->iPrefEnc & 2)!=0 ){
|
||||
}else if( (enc & p->funcFlags & 2)!=0 ){
|
||||
match += 1; /* Both are UTF16, but with different byte orders */
|
||||
}
|
||||
|
||||
@@ -357,7 +357,6 @@ FuncDef *sqlite3FindFunction(
|
||||
|
||||
assert( nArg>=(-2) );
|
||||
assert( nArg>=(-1) || createFlag==0 );
|
||||
assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
|
||||
h = (sqlite3UpperToLower[(u8)zName[0]] + nName) % ArraySize(db->aFunc.a);
|
||||
|
||||
/* First search for a match amongst the application-defined functions.
|
||||
@@ -406,7 +405,7 @@ FuncDef *sqlite3FindFunction(
|
||||
(pBest = sqlite3DbMallocZero(db, sizeof(*pBest)+nName+1))!=0 ){
|
||||
pBest->zName = (char *)&pBest[1];
|
||||
pBest->nArg = (u16)nArg;
|
||||
pBest->iPrefEnc = enc;
|
||||
pBest->funcFlags = enc;
|
||||
memcpy(pBest->zName, zName, nName);
|
||||
pBest->zName[nName] = 0;
|
||||
sqlite3FuncDefInsert(&db->aFunc, pBest);
|
||||
|
||||
+12
-1
@@ -117,7 +117,9 @@ static const char * const azCompileOpt[] = {
|
||||
#ifdef SQLITE_ENABLE_RTREE
|
||||
"ENABLE_RTREE",
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
#if defined(SQLITE_ENABLE_STAT4)
|
||||
"ENABLE_STAT4",
|
||||
#elif defined(SQLITE_ENABLE_STAT3)
|
||||
"ENABLE_STAT3",
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
|
||||
@@ -213,6 +215,9 @@ static const char * const azCompileOpt[] = {
|
||||
#ifdef SQLITE_OMIT_COMPOUND_SELECT
|
||||
"OMIT_COMPOUND_SELECT",
|
||||
#endif
|
||||
#ifdef SQLITE_OMIT_CTE
|
||||
"OMIT_CTE",
|
||||
#endif
|
||||
#ifdef SQLITE_OMIT_DATETIME_FUNCS
|
||||
"OMIT_DATETIME_FUNCS",
|
||||
#endif
|
||||
@@ -345,6 +350,9 @@ static const char * const azCompileOpt[] = {
|
||||
#ifdef SQLITE_SOUNDEX
|
||||
"SOUNDEX",
|
||||
#endif
|
||||
#ifdef SQLITE_SYSTEM_MALLOC
|
||||
"SYSTEM_MALLOC",
|
||||
#endif
|
||||
#ifdef SQLITE_TCL
|
||||
"TCL",
|
||||
#endif
|
||||
@@ -360,6 +368,9 @@ static const char * const azCompileOpt[] = {
|
||||
#ifdef SQLITE_USE_ALLOCA
|
||||
"USE_ALLOCA",
|
||||
#endif
|
||||
#ifdef SQLITE_WIN32_MALLOC
|
||||
"WIN32_MALLOC",
|
||||
#endif
|
||||
#ifdef SQLITE_ZERO_MALLOC
|
||||
"ZERO_MALLOC"
|
||||
#endif
|
||||
|
||||
+13
-4
@@ -294,8 +294,8 @@ static int parseYyyyMmDd(const char *zDate, DateTime *p){
|
||||
** Return the number of errors.
|
||||
*/
|
||||
static int setDateTimeToCurrent(sqlite3_context *context, DateTime *p){
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
if( sqlite3OsCurrentTimeInt64(db->pVfs, &p->iJD)==SQLITE_OK ){
|
||||
p->iJD = sqlite3StmtCurrentTime(context);
|
||||
if( p->iJD>0 ){
|
||||
p->validJD = 1;
|
||||
return 0;
|
||||
}else{
|
||||
@@ -426,6 +426,10 @@ static void clearYMD_HMS_TZ(DateTime *p){
|
||||
**
|
||||
** If the sqlite3GlobalConfig.bLocaltimeFault variable is true then this
|
||||
** routine will always fail.
|
||||
**
|
||||
** EVIDENCE-OF: R-62172-00036 In this implementation, the standard C
|
||||
** library function localtime_r() is used to assist in the calculation of
|
||||
** local time.
|
||||
*/
|
||||
static int osLocaltime(time_t *t, struct tm *pTm){
|
||||
int rc;
|
||||
@@ -482,6 +486,11 @@ static sqlite3_int64 localtimeOffset(
|
||||
x = *p;
|
||||
computeYMD_HMS(&x);
|
||||
if( x.Y<1971 || x.Y>=2038 ){
|
||||
/* EVIDENCE-OF: R-55269-29598 The localtime_r() C function normally only
|
||||
** works for years between 1970 and 2037. For dates outside this range,
|
||||
** SQLite attempts to map the year into an equivalent year within this
|
||||
** range, do the calculation, then map the year back.
|
||||
*/
|
||||
x.Y = 2000;
|
||||
x.M = 1;
|
||||
x.D = 1;
|
||||
@@ -1078,8 +1087,8 @@ static void currentTimeFunc(
|
||||
UNUSED_PARAMETER(argc);
|
||||
UNUSED_PARAMETER(argv);
|
||||
|
||||
db = sqlite3_context_db_handle(context);
|
||||
if( sqlite3OsCurrentTimeInt64(db->pVfs, &iT) ) return;
|
||||
iT = sqlite3StmtCurrentTime(context);
|
||||
if( iT<=0 ) return;
|
||||
t = iT/1000 - 10000*(sqlite3_int64)21086676;
|
||||
#ifdef HAVE_GMTIME_R
|
||||
pTm = gmtime_r(&t, &sNow);
|
||||
|
||||
+277
-127
@@ -97,10 +97,8 @@ void sqlite3MaterializeView(
|
||||
SrcList *pFrom;
|
||||
sqlite3 *db = pParse->db;
|
||||
int iDb = sqlite3SchemaToIndex(db, pView->pSchema);
|
||||
|
||||
pWhere = sqlite3ExprDup(db, pWhere, 0);
|
||||
pFrom = sqlite3SrcListAppend(db, 0, 0, 0);
|
||||
|
||||
if( pFrom ){
|
||||
assert( pFrom->nSrc==1 );
|
||||
pFrom->a[0].zName = sqlite3DbStrDup(db, pView->zName);
|
||||
@@ -108,10 +106,7 @@ void sqlite3MaterializeView(
|
||||
assert( pFrom->a[0].pOn==0 );
|
||||
assert( pFrom->a[0].pUsing==0 );
|
||||
}
|
||||
|
||||
pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, 0, 0, 0, 0);
|
||||
if( pSel ) pSel->selFlags |= SF_Materialize;
|
||||
|
||||
sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur);
|
||||
sqlite3Select(pParse, pSel, &dest);
|
||||
sqlite3SelectDelete(db, pSel);
|
||||
@@ -134,7 +129,7 @@ Expr *sqlite3LimitWhere(
|
||||
ExprList *pOrderBy, /* The ORDER BY clause. May be null */
|
||||
Expr *pLimit, /* The LIMIT clause. May be null */
|
||||
Expr *pOffset, /* The OFFSET clause. May be null */
|
||||
char *zStmtType /* Either DELETE or UPDATE. For error messages. */
|
||||
char *zStmtType /* Either DELETE or UPDATE. For err msgs. */
|
||||
){
|
||||
Expr *pWhereRowid = NULL; /* WHERE rowid .. */
|
||||
Expr *pInClause = NULL; /* WHERE rowid IN ( select ) */
|
||||
@@ -209,7 +204,8 @@ limit_where_cleanup_2:
|
||||
sqlite3ExprDelete(pParse->db, pOffset);
|
||||
return 0;
|
||||
}
|
||||
#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) */
|
||||
#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) */
|
||||
/* && !defined(SQLITE_OMIT_SUBQUERY) */
|
||||
|
||||
/*
|
||||
** Generate code for a DELETE FROM statement.
|
||||
@@ -226,18 +222,34 @@ void sqlite3DeleteFrom(
|
||||
Vdbe *v; /* The virtual database engine */
|
||||
Table *pTab; /* The table from which records will be deleted */
|
||||
const char *zDb; /* Name of database holding pTab */
|
||||
int end, addr = 0; /* A couple addresses of generated code */
|
||||
int i; /* Loop counter */
|
||||
WhereInfo *pWInfo; /* Information about the WHERE clause */
|
||||
Index *pIdx; /* For looping over indices of the table */
|
||||
int iCur; /* VDBE Cursor number for pTab */
|
||||
int iTabCur; /* Cursor number for the table */
|
||||
int iDataCur; /* VDBE cursor for the canonical data source */
|
||||
int iIdxCur; /* Cursor number of the first index */
|
||||
int nIdx; /* Number of indices */
|
||||
sqlite3 *db; /* Main database structure */
|
||||
AuthContext sContext; /* Authorization context */
|
||||
NameContext sNC; /* Name context to resolve expressions in */
|
||||
int iDb; /* Database number */
|
||||
int memCnt = -1; /* Memory cell used for change counting */
|
||||
int rcauth; /* Value returned by authorization callback */
|
||||
|
||||
int okOnePass; /* True for one-pass algorithm without the FIFO */
|
||||
int aiCurOnePass[2]; /* The write cursors opened by WHERE_ONEPASS */
|
||||
u8 *aToOpen = 0; /* Open cursor iTabCur+j if aToOpen[j] is true */
|
||||
Index *pPk; /* The PRIMARY KEY index on the table */
|
||||
int iPk = 0; /* First of nPk registers holding PRIMARY KEY value */
|
||||
i16 nPk = 1; /* Number of columns in the PRIMARY KEY */
|
||||
int iKey; /* Memory cell holding key of row to be deleted */
|
||||
i16 nKey; /* Number of memory cells in the row key */
|
||||
int iEphCur = 0; /* Ephemeral table holding all primary key values */
|
||||
int iRowSet = 0; /* Register for rowset of rows to delete */
|
||||
int addrBypass = 0; /* Address of jump over the delete logic */
|
||||
int addrLoop = 0; /* Top of the delete loop */
|
||||
int addrDelete = 0; /* Jump directly to the delete logic */
|
||||
int addrEphOpen = 0; /* Instruction to open the Ephermeral table */
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
int isView; /* True if attempting to delete from a view */
|
||||
Trigger *pTrigger; /* List of table triggers, if required */
|
||||
@@ -292,11 +304,11 @@ void sqlite3DeleteFrom(
|
||||
}
|
||||
assert(!isView || pTrigger);
|
||||
|
||||
/* Assign cursor number to the table and all its indices.
|
||||
/* Assign cursor numbers to the table and all its indices.
|
||||
*/
|
||||
assert( pTabList->nSrc==1 );
|
||||
iCur = pTabList->a[0].iCursor = pParse->nTab++;
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
iTabCur = pTabList->a[0].iCursor = pParse->nTab++;
|
||||
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){
|
||||
pParse->nTab++;
|
||||
}
|
||||
|
||||
@@ -320,7 +332,8 @@ void sqlite3DeleteFrom(
|
||||
*/
|
||||
#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
|
||||
if( isView ){
|
||||
sqlite3MaterializeView(pParse, pTab, pWhere, iCur);
|
||||
sqlite3MaterializeView(pParse, pTab, pWhere, iTabCur);
|
||||
iDataCur = iIdxCur = iTabCur;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -350,78 +363,171 @@ void sqlite3DeleteFrom(
|
||||
&& 0==sqlite3FkRequired(pParse, pTab, 0, 0)
|
||||
){
|
||||
assert( !isView );
|
||||
sqlite3VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt,
|
||||
pTab->zName, P4_STATIC);
|
||||
sqlite3TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName);
|
||||
if( HasRowid(pTab) ){
|
||||
sqlite3VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt,
|
||||
pTab->zName, P4_STATIC);
|
||||
}
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
assert( pIdx->pSchema==pTab->pSchema );
|
||||
sqlite3VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb);
|
||||
}
|
||||
}else
|
||||
#endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */
|
||||
/* The usual case: There is a WHERE clause so we have to scan through
|
||||
** the table and pick which records to delete.
|
||||
*/
|
||||
{
|
||||
int iRowSet = ++pParse->nMem; /* Register for rowset of rows to delete */
|
||||
int iRowid = ++pParse->nMem; /* Used for storing rowid values. */
|
||||
int regRowid; /* Actual register containing rowids */
|
||||
|
||||
/* Collect rowids of every row to be deleted.
|
||||
if( HasRowid(pTab) ){
|
||||
/* For a rowid table, initialize the RowSet to an empty set */
|
||||
pPk = 0;
|
||||
nPk = 1;
|
||||
iRowSet = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet);
|
||||
}else{
|
||||
/* For a WITHOUT ROWID table, create an ephermeral table used to
|
||||
** hold all primary keys for rows to be deleted. */
|
||||
pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
assert( pPk!=0 );
|
||||
nPk = pPk->nKeyCol;
|
||||
iPk = pParse->nMem+1;
|
||||
pParse->nMem += nPk;
|
||||
iEphCur = pParse->nTab++;
|
||||
addrEphOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEphCur, nPk);
|
||||
sqlite3VdbeSetP4KeyInfo(pParse, pPk);
|
||||
}
|
||||
|
||||
/* Construct a query to find the rowid or primary key for every row
|
||||
** to be deleted, based on the WHERE clause.
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet);
|
||||
pWInfo = sqlite3WhereBegin(
|
||||
pParse, pTabList, pWhere, 0, 0, WHERE_DUPLICATES_OK, 0
|
||||
);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0,
|
||||
WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK,
|
||||
iTabCur+1);
|
||||
if( pWInfo==0 ) goto delete_from_cleanup;
|
||||
regRowid = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, iRowid, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, regRowid);
|
||||
okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
|
||||
|
||||
/* Keep track of the number of rows to be deleted */
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1);
|
||||
}
|
||||
|
||||
/* Extract the rowid or primary key for the current row */
|
||||
if( pPk ){
|
||||
for(i=0; i<nPk; i++){
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur,
|
||||
pPk->aiColumn[i], iPk+i);
|
||||
}
|
||||
iKey = iPk;
|
||||
}else{
|
||||
iKey = pParse->nMem + 1;
|
||||
iKey = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iTabCur, iKey, 0);
|
||||
if( iKey>pParse->nMem ) pParse->nMem = iKey;
|
||||
}
|
||||
|
||||
if( okOnePass ){
|
||||
/* For ONEPASS, no need to store the rowid/primary-key. There is only
|
||||
** one, so just keep it in its register(s) and fall through to the
|
||||
** delete code.
|
||||
*/
|
||||
nKey = nPk; /* OP_Found will use an unpacked key */
|
||||
aToOpen = sqlite3DbMallocRaw(db, nIdx+2);
|
||||
if( aToOpen==0 ){
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
memset(aToOpen, 1, nIdx+1);
|
||||
aToOpen[nIdx+1] = 0;
|
||||
if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iTabCur] = 0;
|
||||
if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iTabCur] = 0;
|
||||
if( addrEphOpen ) sqlite3VdbeChangeToNoop(v, addrEphOpen);
|
||||
addrDelete = sqlite3VdbeAddOp0(v, OP_Goto); /* Jump to DELETE logic */
|
||||
}else if( pPk ){
|
||||
/* Construct a composite key for the row to be deleted and remember it */
|
||||
iKey = ++pParse->nMem;
|
||||
nKey = 0; /* Zero tells OP_Found to use a composite key */
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, iKey,
|
||||
sqlite3IndexAffinityStr(v, pPk), nPk);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iEphCur, iKey);
|
||||
}else{
|
||||
/* Get the rowid of the row to be deleted and remember it in the RowSet */
|
||||
nKey = 1; /* OP_Seek always uses a single rowid */
|
||||
sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, iKey);
|
||||
}
|
||||
|
||||
/* End of the WHERE loop */
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
|
||||
/* Delete every item whose key was written to the list during the
|
||||
** database scan. We have to delete items after the scan is complete
|
||||
** because deleting an item can change the scan order. */
|
||||
end = sqlite3VdbeMakeLabel(v);
|
||||
|
||||
if( okOnePass ){
|
||||
/* Bypass the delete logic below if the WHERE loop found zero rows */
|
||||
addrBypass = sqlite3VdbeMakeLabel(v);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrBypass);
|
||||
sqlite3VdbeJumpHere(v, addrDelete);
|
||||
}
|
||||
|
||||
/* Unless this is a view, open cursors for the table we are
|
||||
** deleting from and all its indices. If this is a view, then the
|
||||
** only effect this statement has is to fire the INSTEAD OF
|
||||
** triggers. */
|
||||
** triggers.
|
||||
*/
|
||||
if( !isView ){
|
||||
sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite);
|
||||
sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, iTabCur, aToOpen,
|
||||
&iDataCur, &iIdxCur);
|
||||
assert( pPk || iDataCur==iTabCur );
|
||||
assert( pPk || iIdxCur==iDataCur+1 );
|
||||
}
|
||||
|
||||
addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, end, iRowid);
|
||||
|
||||
|
||||
/* Set up a loop over the rowids/primary-keys that were found in the
|
||||
** where-clause loop above.
|
||||
*/
|
||||
if( okOnePass ){
|
||||
/* Just one row. Hence the top-of-loop is a no-op */
|
||||
assert( nKey==nPk ); /* OP_Found will use an unpacked key */
|
||||
if( aToOpen[iDataCur-iTabCur] ){
|
||||
assert( pPk!=0 );
|
||||
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey);
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
}else if( pPk ){
|
||||
addrLoop = sqlite3VdbeAddOp1(v, OP_Rewind, iEphCur); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp2(v, OP_RowKey, iEphCur, iKey);
|
||||
assert( nKey==0 ); /* OP_Found will use a composite key */
|
||||
}else{
|
||||
addrLoop = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, 0, iKey);
|
||||
VdbeCoverage(v);
|
||||
assert( nKey==1 );
|
||||
}
|
||||
|
||||
/* Delete the row */
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
if( IsVirtual(pTab) ){
|
||||
const char *pVTab = (const char *)sqlite3GetVTable(db, pTab);
|
||||
sqlite3VtabMakeWritable(pParse, pTab);
|
||||
sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iRowid, pVTab, P4_VTAB);
|
||||
sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iKey, pVTab, P4_VTAB);
|
||||
sqlite3VdbeChangeP5(v, OE_Abort);
|
||||
sqlite3MayAbort(pParse);
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
int count = (pParse->nested==0); /* True to count changes */
|
||||
sqlite3GenerateRowDelete(pParse, pTab, iCur, iRowid, count, pTrigger, OE_Default);
|
||||
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
|
||||
iKey, nKey, count, OE_Default, okOnePass);
|
||||
}
|
||||
|
||||
/* End of the delete loop */
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
|
||||
sqlite3VdbeResolveLabel(v, end);
|
||||
|
||||
|
||||
/* End of the loop over all rowids/primary-keys. */
|
||||
if( okOnePass ){
|
||||
sqlite3VdbeResolveLabel(v, addrBypass);
|
||||
}else if( pPk ){
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iEphCur, addrLoop+1); VdbeCoverage(v);
|
||||
sqlite3VdbeJumpHere(v, addrLoop);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrLoop);
|
||||
sqlite3VdbeJumpHere(v, addrLoop);
|
||||
}
|
||||
|
||||
/* Close the cursors open on the table and its indexes. */
|
||||
if( !isView && !IsVirtual(pTab) ){
|
||||
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||
sqlite3VdbeAddOp2(v, OP_Close, iCur + i, pIdx->tnum);
|
||||
if( !pPk ) sqlite3VdbeAddOp1(v, OP_Close, iDataCur);
|
||||
for(i=0, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iIdxCur + i);
|
||||
}
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iCur);
|
||||
}
|
||||
}
|
||||
} /* End non-truncate path */
|
||||
|
||||
/* Update the sqlite_sequence table by storing the content of the
|
||||
** maximum rowid counter values recorded while inserting into
|
||||
@@ -445,6 +551,7 @@ delete_from_cleanup:
|
||||
sqlite3AuthContextPop(&sContext);
|
||||
sqlite3SrcListDelete(db, pTabList);
|
||||
sqlite3ExprDelete(db, pWhere);
|
||||
sqlite3DbFree(db, aToOpen);
|
||||
return;
|
||||
}
|
||||
/* Make sure "isView" and other macros defined above are undefined. Otherwise
|
||||
@@ -459,50 +566,63 @@ delete_from_cleanup:
|
||||
|
||||
/*
|
||||
** This routine generates VDBE code that causes a single row of a
|
||||
** single table to be deleted.
|
||||
** single table to be deleted. Both the original table entry and
|
||||
** all indices are removed.
|
||||
**
|
||||
** The VDBE must be in a particular state when this routine is called.
|
||||
** These are the requirements:
|
||||
** Preconditions:
|
||||
**
|
||||
** 1. A read/write cursor pointing to pTab, the table containing the row
|
||||
** to be deleted, must be opened as cursor number $iCur.
|
||||
** 1. iDataCur is an open cursor on the btree that is the canonical data
|
||||
** store for the table. (This will be either the table itself,
|
||||
** in the case of a rowid table, or the PRIMARY KEY index in the case
|
||||
** of a WITHOUT ROWID table.)
|
||||
**
|
||||
** 2. Read/write cursors for all indices of pTab must be open as
|
||||
** cursor number base+i for the i-th index.
|
||||
** cursor number iIdxCur+i for the i-th index.
|
||||
**
|
||||
** 3. The record number of the row to be deleted must be stored in
|
||||
** memory cell iRowid.
|
||||
**
|
||||
** This routine generates code to remove both the table record and all
|
||||
** index entries that point to that record.
|
||||
** 3. The primary key for the row to be deleted must be stored in a
|
||||
** sequence of nPk memory cells starting at iPk. If nPk==0 that means
|
||||
** that a search record formed from OP_MakeRecord is contained in the
|
||||
** single memory location iPk.
|
||||
*/
|
||||
void sqlite3GenerateRowDelete(
|
||||
Parse *pParse, /* Parsing context */
|
||||
Table *pTab, /* Table containing the row to be deleted */
|
||||
int iCur, /* Cursor number for the table */
|
||||
int iRowid, /* Memory cell that contains the rowid to delete */
|
||||
int count, /* If non-zero, increment the row change counter */
|
||||
Trigger *pTrigger, /* List of triggers to (potentially) fire */
|
||||
int onconf /* Default ON CONFLICT policy for triggers */
|
||||
int iDataCur, /* Cursor from which column data is extracted */
|
||||
int iIdxCur, /* First index cursor */
|
||||
int iPk, /* First memory cell containing the PRIMARY KEY */
|
||||
i16 nPk, /* Number of PRIMARY KEY memory cells */
|
||||
u8 count, /* If non-zero, increment the row change counter */
|
||||
u8 onconf, /* Default ON CONFLICT policy for triggers */
|
||||
u8 bNoSeek /* iDataCur is already pointing to the row to delete */
|
||||
){
|
||||
Vdbe *v = pParse->pVdbe; /* Vdbe */
|
||||
int iOld = 0; /* First register in OLD.* array */
|
||||
int iLabel; /* Label resolved to end of generated code */
|
||||
u8 opSeek; /* Seek opcode */
|
||||
|
||||
/* Vdbe is guaranteed to have been allocated by this stage. */
|
||||
assert( v );
|
||||
VdbeModuleComment((v, "BEGIN: GenRowDel(%d,%d,%d,%d)",
|
||||
iDataCur, iIdxCur, iPk, (int)nPk));
|
||||
|
||||
/* Seek cursor iCur to the row to delete. If this row no longer exists
|
||||
** (this can happen if a trigger program has already deleted it), do
|
||||
** not attempt to delete it or fire any DELETE triggers. */
|
||||
iLabel = sqlite3VdbeMakeLabel(v);
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, iLabel, iRowid);
|
||||
opSeek = HasRowid(pTab) ? OP_NotExists : OP_NotFound;
|
||||
if( !bNoSeek ){
|
||||
sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk);
|
||||
VdbeCoverageIf(v, opSeek==OP_NotExists);
|
||||
VdbeCoverageIf(v, opSeek==OP_NotFound);
|
||||
}
|
||||
|
||||
/* If there are any triggers to fire, allocate a range of registers to
|
||||
** use for the old.* references in the triggers. */
|
||||
if( sqlite3FkRequired(pParse, pTab, 0, 0) || pTrigger ){
|
||||
u32 mask; /* Mask of OLD.* columns in use */
|
||||
int iCol; /* Iterator used while populating OLD.* */
|
||||
int addrStart; /* Start of BEFORE trigger programs */
|
||||
|
||||
/* TODO: Could use temporary registers here. Also could attempt to
|
||||
** avoid copying the contents of the rowid register. */
|
||||
@@ -515,36 +635,44 @@ void sqlite3GenerateRowDelete(
|
||||
|
||||
/* Populate the OLD.* pseudo-table register array. These values will be
|
||||
** used by any BEFORE and AFTER triggers that exist. */
|
||||
sqlite3VdbeAddOp2(v, OP_Copy, iRowid, iOld);
|
||||
sqlite3VdbeAddOp2(v, OP_Copy, iPk, iOld);
|
||||
for(iCol=0; iCol<pTab->nCol; iCol++){
|
||||
if( mask==0xffffffff || mask&(1<<iCol) ){
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, iCol, iOld+iCol+1);
|
||||
testcase( mask!=0xffffffff && iCol==31 );
|
||||
testcase( mask!=0xffffffff && iCol==32 );
|
||||
if( mask==0xffffffff || (iCol<=31 && (mask & MASKBIT32(iCol))!=0) ){
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, iCol, iOld+iCol+1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Invoke BEFORE DELETE trigger programs. */
|
||||
addrStart = sqlite3VdbeCurrentAddr(v);
|
||||
sqlite3CodeRowTrigger(pParse, pTrigger,
|
||||
TK_DELETE, 0, TRIGGER_BEFORE, pTab, iOld, onconf, iLabel
|
||||
);
|
||||
|
||||
/* Seek the cursor to the row to be deleted again. It may be that
|
||||
** the BEFORE triggers coded above have already removed the row
|
||||
** being deleted. Do not attempt to delete the row a second time, and
|
||||
** do not fire AFTER triggers. */
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, iLabel, iRowid);
|
||||
/* If any BEFORE triggers were coded, then seek the cursor to the
|
||||
** row to be deleted again. It may be that the BEFORE triggers moved
|
||||
** the cursor or of already deleted the row that the cursor was
|
||||
** pointing to.
|
||||
*/
|
||||
if( addrStart<sqlite3VdbeCurrentAddr(v) ){
|
||||
sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk);
|
||||
VdbeCoverageIf(v, opSeek==OP_NotExists);
|
||||
VdbeCoverageIf(v, opSeek==OP_NotFound);
|
||||
}
|
||||
|
||||
/* Do FK processing. This call checks that any FK constraints that
|
||||
** refer to this table (i.e. constraints attached to other tables)
|
||||
** are not violated by deleting this row. */
|
||||
sqlite3FkCheck(pParse, pTab, iOld, 0);
|
||||
sqlite3FkCheck(pParse, pTab, iOld, 0, 0, 0);
|
||||
}
|
||||
|
||||
/* Delete the index and table entries. Skip this step if pTab is really
|
||||
** a view (in which case the only effect of the DELETE statement is to
|
||||
** fire the INSTEAD OF triggers). */
|
||||
if( pTab->pSelect==0 ){
|
||||
sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Delete, iCur, (count?OPFLAG_NCHANGE:0));
|
||||
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, (count?OPFLAG_NCHANGE:0));
|
||||
if( count ){
|
||||
sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT);
|
||||
}
|
||||
@@ -553,7 +681,7 @@ void sqlite3GenerateRowDelete(
|
||||
/* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to
|
||||
** handle rows (possibly in other tables) that refer via a foreign key
|
||||
** to the row just deleted. */
|
||||
sqlite3FkActions(pParse, pTab, 0, iOld);
|
||||
sqlite3FkActions(pParse, pTab, 0, iOld, 0, 0);
|
||||
|
||||
/* Invoke AFTER DELETE trigger programs. */
|
||||
sqlite3CodeRowTrigger(pParse, pTrigger,
|
||||
@@ -564,49 +692,64 @@ void sqlite3GenerateRowDelete(
|
||||
** trigger programs were invoked. Or if a trigger program throws a
|
||||
** RAISE(IGNORE) exception. */
|
||||
sqlite3VdbeResolveLabel(v, iLabel);
|
||||
VdbeModuleComment((v, "END: GenRowDel()"));
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine generates VDBE code that causes the deletion of all
|
||||
** index entries associated with a single row of a single table.
|
||||
** index entries associated with a single row of a single table, pTab
|
||||
**
|
||||
** The VDBE must be in a particular state when this routine is called.
|
||||
** These are the requirements:
|
||||
** Preconditions:
|
||||
**
|
||||
** 1. A read/write cursor pointing to pTab, the table containing the row
|
||||
** to be deleted, must be opened as cursor number "iCur".
|
||||
** 1. A read/write cursor "iDataCur" must be open on the canonical storage
|
||||
** btree for the table pTab. (This will be either the table itself
|
||||
** for rowid tables or to the primary key index for WITHOUT ROWID
|
||||
** tables.)
|
||||
**
|
||||
** 2. Read/write cursors for all indices of pTab must be open as
|
||||
** cursor number iCur+i for the i-th index.
|
||||
** cursor number iIdxCur+i for the i-th index. (The pTab->pIndex
|
||||
** index is the 0-th index.)
|
||||
**
|
||||
** 3. The "iCur" cursor must be pointing to the row that is to be
|
||||
** deleted.
|
||||
** 3. The "iDataCur" cursor must be already be positioned on the row
|
||||
** that is to be deleted.
|
||||
*/
|
||||
void sqlite3GenerateRowIndexDelete(
|
||||
Parse *pParse, /* Parsing and code generating context */
|
||||
Table *pTab, /* Table containing the row to be deleted */
|
||||
int iCur, /* Cursor number for the table */
|
||||
int iDataCur, /* Cursor of table holding data. */
|
||||
int iIdxCur, /* First index cursor */
|
||||
int *aRegIdx /* Only delete if aRegIdx!=0 && aRegIdx[i]>0 */
|
||||
){
|
||||
int i;
|
||||
Index *pIdx;
|
||||
int r1;
|
||||
int iPartIdxLabel;
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
int i; /* Index loop counter */
|
||||
int r1 = -1; /* Register holding an index key */
|
||||
int iPartIdxLabel; /* Jump destination for skipping partial index entries */
|
||||
Index *pIdx; /* Current index */
|
||||
Index *pPrior = 0; /* Prior index */
|
||||
Vdbe *v; /* The prepared statement under construction */
|
||||
Index *pPk; /* PRIMARY KEY index, or NULL for rowid tables */
|
||||
|
||||
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||
if( aRegIdx!=0 && aRegIdx[i-1]==0 ) continue;
|
||||
r1 = sqlite3GenerateIndexKey(pParse, pIdx, iCur, 0, 0, &iPartIdxLabel);
|
||||
sqlite3VdbeAddOp3(v, OP_IdxDelete, iCur+i, r1, pIdx->nColumn+1);
|
||||
v = pParse->pVdbe;
|
||||
pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
|
||||
for(i=0, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||
assert( iIdxCur+i!=iDataCur || pPk==pIdx );
|
||||
if( aRegIdx!=0 && aRegIdx[i]==0 ) continue;
|
||||
if( pIdx==pPk ) continue;
|
||||
VdbeModuleComment((v, "GenRowIdxDel for %s", pIdx->zName));
|
||||
r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1,
|
||||
&iPartIdxLabel, pPrior, r1);
|
||||
sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1,
|
||||
pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn);
|
||||
sqlite3VdbeResolveLabel(v, iPartIdxLabel);
|
||||
pPrior = pIdx;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code that will assemble an index key and put it in register
|
||||
** Generate code that will assemble an index key and stores it in register
|
||||
** regOut. The key with be for index pIdx which is an index on pTab.
|
||||
** iCur is the index of a cursor open on the pTab table and pointing to
|
||||
** the entry that needs indexing.
|
||||
** the entry that needs indexing. If pTab is a WITHOUT ROWID table, then
|
||||
** iCur must be the cursor of the PRIMARY KEY index.
|
||||
**
|
||||
** Return a register number which is the first in a block of
|
||||
** registers that holds the elements of the index key. The
|
||||
@@ -619,14 +762,27 @@ void sqlite3GenerateRowIndexDelete(
|
||||
** to false or null. If pIdx is not a partial index, *piPartIdxLabel
|
||||
** will be set to zero which is an empty label that is ignored by
|
||||
** sqlite3VdbeResolveLabel().
|
||||
**
|
||||
** The pPrior and regPrior parameters are used to implement a cache to
|
||||
** avoid unnecessary register loads. If pPrior is not NULL, then it is
|
||||
** a pointer to a different index for which an index key has just been
|
||||
** computed into register regPrior. If the current pIdx index is generating
|
||||
** its key into the same sequence of registers and if pPrior and pIdx share
|
||||
** a column in common, then the register corresponding to that column already
|
||||
** holds the correct value and the loading of that register is skipped.
|
||||
** This optimization is helpful when doing a DELETE or an INTEGRITY_CHECK
|
||||
** on a table with multiple indices, and especially with the ROWID or
|
||||
** PRIMARY KEY columns of the index.
|
||||
*/
|
||||
int sqlite3GenerateIndexKey(
|
||||
Parse *pParse, /* Parsing context */
|
||||
Index *pIdx, /* The index for which to generate a key */
|
||||
int iCur, /* Cursor number for the pIdx->pTable table */
|
||||
int regOut, /* Write the new index key to this register */
|
||||
int doMakeRec, /* Run the OP_MakeRecord instruction if true */
|
||||
int *piPartIdxLabel /* OUT: Jump to this label to skip partial index */
|
||||
int iDataCur, /* Cursor number from which to take column data */
|
||||
int regOut, /* Put the new key into this register if not 0 */
|
||||
int prefixOnly, /* Compute only a unique prefix of the key */
|
||||
int *piPartIdxLabel, /* OUT: Jump to this label to skip partial index */
|
||||
Index *pPrior, /* Previously generated index key */
|
||||
int regPrior /* Register holding previous generated key */
|
||||
){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
int j;
|
||||
@@ -637,37 +793,31 @@ int sqlite3GenerateIndexKey(
|
||||
if( piPartIdxLabel ){
|
||||
if( pIdx->pPartIdxWhere ){
|
||||
*piPartIdxLabel = sqlite3VdbeMakeLabel(v);
|
||||
pParse->iPartIdxTab = iCur;
|
||||
pParse->iPartIdxTab = iDataCur;
|
||||
sqlite3ExprIfFalse(pParse, pIdx->pPartIdxWhere, *piPartIdxLabel,
|
||||
SQLITE_JUMPIFNULL);
|
||||
}else{
|
||||
*piPartIdxLabel = 0;
|
||||
}
|
||||
}
|
||||
nCol = pIdx->nColumn;
|
||||
regBase = sqlite3GetTempRange(pParse, nCol+1);
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regBase+nCol);
|
||||
nCol = (prefixOnly && pIdx->uniqNotNull) ? pIdx->nKeyCol : pIdx->nColumn;
|
||||
regBase = sqlite3GetTempRange(pParse, nCol);
|
||||
if( pPrior && (regBase!=regPrior || pPrior->pPartIdxWhere) ) pPrior = 0;
|
||||
for(j=0; j<nCol; j++){
|
||||
int idx = pIdx->aiColumn[j];
|
||||
if( idx==pTab->iPKey ){
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, regBase+nCol, regBase+j);
|
||||
}else{
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iCur, idx, regBase+j);
|
||||
sqlite3ColumnDefault(v, pTab, idx, -1);
|
||||
}
|
||||
if( pPrior && pPrior->aiColumn[j]==pIdx->aiColumn[j] ) continue;
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, pIdx->aiColumn[j],
|
||||
regBase+j);
|
||||
/* If the column affinity is REAL but the number is an integer, then it
|
||||
** might be stored in the table as an integer (using a compact
|
||||
** representation) then converted to REAL by an OP_RealAffinity opcode.
|
||||
** But we are getting ready to store this value back into an index, where
|
||||
** it should be converted by to INTEGER again. So omit the OP_RealAffinity
|
||||
** opcode if it is present */
|
||||
sqlite3VdbeDeletePriorOpcode(v, OP_RealAffinity);
|
||||
}
|
||||
if( doMakeRec ){
|
||||
const char *zAff;
|
||||
if( pTab->pSelect
|
||||
|| OptimizationDisabled(pParse->db, SQLITE_IdxRealAsInt)
|
||||
){
|
||||
zAff = 0;
|
||||
}else{
|
||||
zAff = sqlite3IndexAffinityStr(v, pIdx);
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol+1, regOut);
|
||||
sqlite3VdbeChangeP4(v, -1, zAff, P4_TRANSIENT);
|
||||
if( regOut ){
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regOut);
|
||||
}
|
||||
sqlite3ReleaseTempRange(pParse, regBase, nCol+1);
|
||||
sqlite3ReleaseTempRange(pParse, regBase, nCol);
|
||||
return regBase;
|
||||
}
|
||||
|
||||
+410
-406
File diff suppressed because it is too large
Load Diff
+226
-95
@@ -225,7 +225,7 @@ int sqlite3FkLocateIndex(
|
||||
}
|
||||
|
||||
for(pIdx=pParent->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
if( pIdx->nColumn==nCol && pIdx->onError!=OE_None ){
|
||||
if( pIdx->nKeyCol==nCol && pIdx->onError!=OE_None ){
|
||||
/* pIdx is a UNIQUE index (or a PRIMARY KEY) and has the right number
|
||||
** of columns. If each indexed column corresponds to a foreign key
|
||||
** column of pFKey, then this index is a winner. */
|
||||
@@ -248,7 +248,7 @@ int sqlite3FkLocateIndex(
|
||||
** the default collation sequences for each column. */
|
||||
int i, j;
|
||||
for(i=0; i<nCol; i++){
|
||||
int iCol = pIdx->aiColumn[i]; /* Index of column in parent tbl */
|
||||
i16 iCol = pIdx->aiColumn[i]; /* Index of column in parent tbl */
|
||||
char *zDfltColl; /* Def. collation for column */
|
||||
char *zIdxCol; /* Name of indexed column */
|
||||
|
||||
@@ -340,10 +340,11 @@ static void fkLookupParent(
|
||||
** search for a matching row in the parent table. */
|
||||
if( nIncr<0 ){
|
||||
sqlite3VdbeAddOp2(v, OP_FkIfZero, pFKey->isDeferred, iOk);
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
for(i=0; i<pFKey->nCol; i++){
|
||||
int iReg = aiCol[i] + regData + 1;
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iOk);
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iOk); VdbeCoverage(v);
|
||||
}
|
||||
|
||||
if( isIgnore==0 ){
|
||||
@@ -360,17 +361,19 @@ static void fkLookupParent(
|
||||
** will have INTEGER affinity applied to it, which may not be correct. */
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, aiCol[0]+1+regData, regTemp);
|
||||
iMustBeInt = sqlite3VdbeAddOp2(v, OP_MustBeInt, regTemp, 0);
|
||||
VdbeCoverage(v);
|
||||
|
||||
/* If the parent table is the same as the child table, and we are about
|
||||
** to increment the constraint-counter (i.e. this is an INSERT operation),
|
||||
** then check if the row being inserted matches itself. If so, do not
|
||||
** increment the constraint-counter. */
|
||||
if( pTab==pFKey->pFrom && nIncr==1 ){
|
||||
sqlite3VdbeAddOp3(v, OP_Eq, regData, iOk, regTemp);
|
||||
sqlite3VdbeAddOp3(v, OP_Eq, regData, iOk, regTemp); VdbeCoverage(v);
|
||||
sqlite3VdbeChangeP5(v, SQLITE_NOTNULL);
|
||||
}
|
||||
|
||||
sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regTemp);
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regTemp); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk);
|
||||
sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
|
||||
sqlite3VdbeJumpHere(v, iMustBeInt);
|
||||
@@ -379,10 +382,9 @@ static void fkLookupParent(
|
||||
int nCol = pFKey->nCol;
|
||||
int regTemp = sqlite3GetTempRange(pParse, nCol);
|
||||
int regRec = sqlite3GetTempReg(pParse);
|
||||
KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
|
||||
|
||||
sqlite3VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb);
|
||||
sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF);
|
||||
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3VdbeAddOp2(v, OP_Copy, aiCol[i]+1+regData, regTemp+i);
|
||||
}
|
||||
@@ -407,15 +409,15 @@ static void fkLookupParent(
|
||||
/* The parent key is a composite key that includes the IPK column */
|
||||
iParent = regData;
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_Ne, iChild, iJump, iParent);
|
||||
sqlite3VdbeAddOp3(v, OP_Ne, iChild, iJump, iParent); VdbeCoverage(v);
|
||||
sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk);
|
||||
}
|
||||
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regTemp, nCol, regRec);
|
||||
sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v,pIdx), P4_TRANSIENT);
|
||||
sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0);
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTemp, nCol, regRec,
|
||||
sqlite3IndexAffinityStr(v,pIdx), nCol);
|
||||
sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0); VdbeCoverage(v);
|
||||
|
||||
sqlite3ReleaseTempReg(pParse, regRec);
|
||||
sqlite3ReleaseTempRange(pParse, regTemp, nCol);
|
||||
@@ -432,8 +434,7 @@ static void fkLookupParent(
|
||||
** generated for will not open a statement transaction. */
|
||||
assert( nIncr==1 );
|
||||
sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY,
|
||||
OE_Abort, "foreign key constraint failed", P4_STATIC
|
||||
);
|
||||
OE_Abort, 0, P4_STATIC, P5_ConstraintFK);
|
||||
}else{
|
||||
if( nIncr>0 && pFKey->isDeferred==0 ){
|
||||
sqlite3ParseToplevel(pParse)->mayAbort = 1;
|
||||
@@ -445,6 +446,62 @@ static void fkLookupParent(
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iCur);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Return an Expr object that refers to a memory register corresponding
|
||||
** to column iCol of table pTab.
|
||||
**
|
||||
** regBase is the first of an array of register that contains the data
|
||||
** for pTab. regBase itself holds the rowid. regBase+1 holds the first
|
||||
** column. regBase+2 holds the second column, and so forth.
|
||||
*/
|
||||
static Expr *exprTableRegister(
|
||||
Parse *pParse, /* Parsing and code generating context */
|
||||
Table *pTab, /* The table whose content is at r[regBase]... */
|
||||
int regBase, /* Contents of table pTab */
|
||||
i16 iCol /* Which column of pTab is desired */
|
||||
){
|
||||
Expr *pExpr;
|
||||
Column *pCol;
|
||||
const char *zColl;
|
||||
sqlite3 *db = pParse->db;
|
||||
|
||||
pExpr = sqlite3Expr(db, TK_REGISTER, 0);
|
||||
if( pExpr ){
|
||||
if( iCol>=0 && iCol!=pTab->iPKey ){
|
||||
pCol = &pTab->aCol[iCol];
|
||||
pExpr->iTable = regBase + iCol + 1;
|
||||
pExpr->affinity = pCol->affinity;
|
||||
zColl = pCol->zColl;
|
||||
if( zColl==0 ) zColl = db->pDfltColl->zName;
|
||||
pExpr = sqlite3ExprAddCollateString(pParse, pExpr, zColl);
|
||||
}else{
|
||||
pExpr->iTable = regBase;
|
||||
pExpr->affinity = SQLITE_AFF_INTEGER;
|
||||
}
|
||||
}
|
||||
return pExpr;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return an Expr object that refers to column iCol of table pTab which
|
||||
** has cursor iCur.
|
||||
*/
|
||||
static Expr *exprTableColumn(
|
||||
sqlite3 *db, /* The database connection */
|
||||
Table *pTab, /* The table whose column is desired */
|
||||
int iCursor, /* The open cursor on the table */
|
||||
i16 iCol /* The column that is wanted */
|
||||
){
|
||||
Expr *pExpr = sqlite3Expr(db, TK_COLUMN, 0);
|
||||
if( pExpr ){
|
||||
pExpr->pTab = pTab;
|
||||
pExpr->iTable = iCursor;
|
||||
pExpr->iColumn = iCol;
|
||||
}
|
||||
return pExpr;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called to generate code executed when a row is deleted
|
||||
** from the parent table of foreign key constraint pFKey and, if pFKey is
|
||||
@@ -460,13 +517,13 @@ static void fkLookupParent(
|
||||
** --------------------------------------------------------------------------
|
||||
** DELETE immediate Increment the "immediate constraint counter".
|
||||
** Or, if the ON (UPDATE|DELETE) action is RESTRICT,
|
||||
** throw a "foreign key constraint failed" exception.
|
||||
** throw a "FOREIGN KEY constraint failed" exception.
|
||||
**
|
||||
** INSERT immediate Decrement the "immediate constraint counter".
|
||||
**
|
||||
** DELETE deferred Increment the "deferred constraint counter".
|
||||
** Or, if the ON (UPDATE|DELETE) action is RESTRICT,
|
||||
** throw a "foreign key constraint failed" exception.
|
||||
** throw a "FOREIGN KEY constraint failed" exception.
|
||||
**
|
||||
** INSERT deferred Decrement the "deferred constraint counter".
|
||||
**
|
||||
@@ -475,12 +532,12 @@ static void fkLookupParent(
|
||||
*/
|
||||
static void fkScanChildren(
|
||||
Parse *pParse, /* Parse context */
|
||||
SrcList *pSrc, /* SrcList containing the table to scan */
|
||||
Table *pTab,
|
||||
Index *pIdx, /* Foreign key index */
|
||||
FKey *pFKey, /* Foreign key relationship */
|
||||
SrcList *pSrc, /* The child table to be scanned */
|
||||
Table *pTab, /* The parent table */
|
||||
Index *pIdx, /* Index on parent covering the foreign key */
|
||||
FKey *pFKey, /* The foreign key linking pSrc to pTab */
|
||||
int *aiCol, /* Map from pIdx cols to child table cols */
|
||||
int regData, /* Referenced table data starts here */
|
||||
int regData, /* Parent row data starts here */
|
||||
int nIncr /* Amount to increment deferred counter by */
|
||||
){
|
||||
sqlite3 *db = pParse->db; /* Database handle */
|
||||
@@ -491,10 +548,14 @@ static void fkScanChildren(
|
||||
int iFkIfZero = 0; /* Address of OP_FkIfZero */
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
|
||||
assert( !pIdx || pIdx->pTable==pTab );
|
||||
assert( pIdx==0 || pIdx->pTable==pTab );
|
||||
assert( pIdx==0 || pIdx->nKeyCol==pFKey->nCol );
|
||||
assert( pIdx!=0 || pFKey->nCol==1 );
|
||||
assert( pIdx!=0 || HasRowid(pTab) );
|
||||
|
||||
if( nIncr<0 ){
|
||||
iFkIfZero = sqlite3VdbeAddOp2(v, OP_FkIfZero, pFKey->isDeferred, 0);
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
|
||||
/* Create an Expr object representing an SQL expression like:
|
||||
@@ -509,29 +570,11 @@ static void fkScanChildren(
|
||||
Expr *pLeft; /* Value from parent table row */
|
||||
Expr *pRight; /* Column ref to child table */
|
||||
Expr *pEq; /* Expression (pLeft = pRight) */
|
||||
int iCol; /* Index of column in child table */
|
||||
i16 iCol; /* Index of column in child table */
|
||||
const char *zCol; /* Name of column in child table */
|
||||
|
||||
pLeft = sqlite3Expr(db, TK_REGISTER, 0);
|
||||
if( pLeft ){
|
||||
/* Set the collation sequence and affinity of the LHS of each TK_EQ
|
||||
** expression to the parent key column defaults. */
|
||||
if( pIdx ){
|
||||
Column *pCol;
|
||||
const char *zColl;
|
||||
iCol = pIdx->aiColumn[i];
|
||||
pCol = &pTab->aCol[iCol];
|
||||
if( pTab->iPKey==iCol ) iCol = -1;
|
||||
pLeft->iTable = regData+iCol+1;
|
||||
pLeft->affinity = pCol->affinity;
|
||||
zColl = pCol->zColl;
|
||||
if( zColl==0 ) zColl = db->pDfltColl->zName;
|
||||
pLeft = sqlite3ExprAddCollateString(pParse, pLeft, zColl);
|
||||
}else{
|
||||
pLeft->iTable = regData;
|
||||
pLeft->affinity = SQLITE_AFF_INTEGER;
|
||||
}
|
||||
}
|
||||
iCol = pIdx ? pIdx->aiColumn[i] : -1;
|
||||
pLeft = exprTableRegister(pParse, pTab, regData, iCol);
|
||||
iCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom;
|
||||
assert( iCol>=0 );
|
||||
zCol = pFKey->pFrom->aCol[iCol].zName;
|
||||
@@ -540,24 +583,39 @@ static void fkScanChildren(
|
||||
pWhere = sqlite3ExprAnd(db, pWhere, pEq);
|
||||
}
|
||||
|
||||
/* If the child table is the same as the parent table, and this scan
|
||||
** is taking place as part of a DELETE operation (operation D.2), omit the
|
||||
** row being deleted from the scan by adding ($rowid != rowid) to the WHERE
|
||||
** clause, where $rowid is the rowid of the row being deleted. */
|
||||
/* If the child table is the same as the parent table, then add terms
|
||||
** to the WHERE clause that prevent this entry from being scanned.
|
||||
** The added WHERE clause terms are like this:
|
||||
**
|
||||
** $current_rowid!=rowid
|
||||
** NOT( $current_a==a AND $current_b==b AND ... )
|
||||
**
|
||||
** The first form is used for rowid tables. The second form is used
|
||||
** for WITHOUT ROWID tables. In the second form, the primary key is
|
||||
** (a,b,...)
|
||||
*/
|
||||
if( pTab==pFKey->pFrom && nIncr>0 ){
|
||||
Expr *pEq; /* Expression (pLeft = pRight) */
|
||||
Expr *pNe; /* Expression (pLeft != pRight) */
|
||||
Expr *pLeft; /* Value from parent table row */
|
||||
Expr *pRight; /* Column ref to child table */
|
||||
pLeft = sqlite3Expr(db, TK_REGISTER, 0);
|
||||
pRight = sqlite3Expr(db, TK_COLUMN, 0);
|
||||
if( pLeft && pRight ){
|
||||
pLeft->iTable = regData;
|
||||
pLeft->affinity = SQLITE_AFF_INTEGER;
|
||||
pRight->iTable = pSrc->a[0].iCursor;
|
||||
pRight->iColumn = -1;
|
||||
if( HasRowid(pTab) ){
|
||||
pLeft = exprTableRegister(pParse, pTab, regData, -1);
|
||||
pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, -1);
|
||||
pNe = sqlite3PExpr(pParse, TK_NE, pLeft, pRight, 0);
|
||||
}else{
|
||||
Expr *pEq, *pAll = 0;
|
||||
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
assert( pIdx!=0 );
|
||||
for(i=0; i<pPk->nKeyCol; i++){
|
||||
i16 iCol = pIdx->aiColumn[i];
|
||||
pLeft = exprTableRegister(pParse, pTab, regData, iCol);
|
||||
pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, iCol);
|
||||
pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight, 0);
|
||||
pAll = sqlite3ExprAnd(db, pAll, pEq);
|
||||
}
|
||||
pNe = sqlite3PExpr(pParse, TK_NOT, pAll, 0, 0);
|
||||
}
|
||||
pEq = sqlite3PExpr(pParse, TK_NE, pLeft, pRight, 0);
|
||||
pWhere = sqlite3ExprAnd(db, pWhere, pEq);
|
||||
pWhere = sqlite3ExprAnd(db, pWhere, pNe);
|
||||
}
|
||||
|
||||
/* Resolve the references in the WHERE clause. */
|
||||
@@ -587,8 +645,8 @@ static void fkScanChildren(
|
||||
}
|
||||
|
||||
/*
|
||||
** This function returns a pointer to the head of a linked list of FK
|
||||
** constraints for which table pTab is the parent table. For example,
|
||||
** This function returns a linked list of FKey objects (connected by
|
||||
** FKey.pNextTo) holding all children of table pTab. For example,
|
||||
** given the following schema:
|
||||
**
|
||||
** CREATE TABLE t1(a PRIMARY KEY);
|
||||
@@ -656,11 +714,11 @@ void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){
|
||||
** when this statement is run. */
|
||||
FKey *p;
|
||||
for(p=pTab->pFKey; p; p=p->pNextFrom){
|
||||
if( p->isDeferred ) break;
|
||||
if( p->isDeferred || (db->flags & SQLITE_DeferFKs) ) break;
|
||||
}
|
||||
if( !p ) return;
|
||||
iSkip = sqlite3VdbeMakeLabel(v);
|
||||
sqlite3VdbeAddOp2(v, OP_FkIfZero, 1, iSkip);
|
||||
sqlite3VdbeAddOp2(v, OP_FkIfZero, 1, iSkip); VdbeCoverage(v);
|
||||
}
|
||||
|
||||
pParse->disableTriggers = 1;
|
||||
@@ -670,11 +728,18 @@ void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){
|
||||
/* If the DELETE has generated immediate foreign key constraint
|
||||
** violations, halt the VDBE and return an error at this point, before
|
||||
** any modifications to the schema are made. This is because statement
|
||||
** transactions are not able to rollback schema changes. */
|
||||
sqlite3VdbeAddOp2(v, OP_FkIfZero, 0, sqlite3VdbeCurrentAddr(v)+2);
|
||||
sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY,
|
||||
OE_Abort, "foreign key constraint failed", P4_STATIC
|
||||
);
|
||||
** transactions are not able to rollback schema changes.
|
||||
**
|
||||
** If the SQLITE_DeferFKs flag is set, then this is not required, as
|
||||
** the statement transaction will not be rolled back even if FK
|
||||
** constraints are violated.
|
||||
*/
|
||||
if( (db->flags & SQLITE_DeferFKs)==0 ){
|
||||
sqlite3VdbeAddOp2(v, OP_FkIfZero, 0, sqlite3VdbeCurrentAddr(v)+2);
|
||||
VdbeCoverage(v);
|
||||
sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY,
|
||||
OE_Abort, 0, P4_STATIC, P5_ConstraintFK);
|
||||
}
|
||||
|
||||
if( iSkip ){
|
||||
sqlite3VdbeResolveLabel(v, iSkip);
|
||||
@@ -682,6 +747,70 @@ void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** The second argument points to an FKey object representing a foreign key
|
||||
** for which pTab is the child table. An UPDATE statement against pTab
|
||||
** is currently being processed. For each column of the table that is
|
||||
** actually updated, the corresponding element in the aChange[] array
|
||||
** is zero or greater (if a column is unmodified the corresponding element
|
||||
** is set to -1). If the rowid column is modified by the UPDATE statement
|
||||
** the bChngRowid argument is non-zero.
|
||||
**
|
||||
** This function returns true if any of the columns that are part of the
|
||||
** child key for FK constraint *p are modified.
|
||||
*/
|
||||
static int fkChildIsModified(
|
||||
Table *pTab, /* Table being updated */
|
||||
FKey *p, /* Foreign key for which pTab is the child */
|
||||
int *aChange, /* Array indicating modified columns */
|
||||
int bChngRowid /* True if rowid is modified by this update */
|
||||
){
|
||||
int i;
|
||||
for(i=0; i<p->nCol; i++){
|
||||
int iChildKey = p->aCol[i].iFrom;
|
||||
if( aChange[iChildKey]>=0 ) return 1;
|
||||
if( iChildKey==pTab->iPKey && bChngRowid ) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** The second argument points to an FKey object representing a foreign key
|
||||
** for which pTab is the parent table. An UPDATE statement against pTab
|
||||
** is currently being processed. For each column of the table that is
|
||||
** actually updated, the corresponding element in the aChange[] array
|
||||
** is zero or greater (if a column is unmodified the corresponding element
|
||||
** is set to -1). If the rowid column is modified by the UPDATE statement
|
||||
** the bChngRowid argument is non-zero.
|
||||
**
|
||||
** This function returns true if any of the columns that are part of the
|
||||
** parent key for FK constraint *p are modified.
|
||||
*/
|
||||
static int fkParentIsModified(
|
||||
Table *pTab,
|
||||
FKey *p,
|
||||
int *aChange,
|
||||
int bChngRowid
|
||||
){
|
||||
int i;
|
||||
for(i=0; i<p->nCol; i++){
|
||||
char *zKey = p->aCol[i].zCol;
|
||||
int iKey;
|
||||
for(iKey=0; iKey<pTab->nCol; iKey++){
|
||||
if( aChange[iKey]>=0 || (iKey==pTab->iPKey && bChngRowid) ){
|
||||
Column *pCol = &pTab->aCol[iKey];
|
||||
if( zKey ){
|
||||
if( 0==sqlite3StrICmp(pCol->zName, zKey) ) return 1;
|
||||
}else if( pCol->colFlags & COLFLAG_PRIMKEY ){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called when inserting, deleting or updating a row of
|
||||
** table pTab to generate VDBE code to perform foreign key constraint
|
||||
@@ -706,7 +835,9 @@ void sqlite3FkCheck(
|
||||
Parse *pParse, /* Parse context */
|
||||
Table *pTab, /* Row is being deleted from this table */
|
||||
int regOld, /* Previous row data is stored here */
|
||||
int regNew /* New row data is stored here */
|
||||
int regNew, /* New row data is stored here */
|
||||
int *aChange, /* Array indicating UPDATEd columns (or 0) */
|
||||
int bChngRowid /* True if rowid is UPDATEd */
|
||||
){
|
||||
sqlite3 *db = pParse->db; /* Database handle */
|
||||
FKey *pFKey; /* Used to iterate through FKs */
|
||||
@@ -734,6 +865,13 @@ void sqlite3FkCheck(
|
||||
int i;
|
||||
int isIgnore = 0;
|
||||
|
||||
if( aChange
|
||||
&& sqlite3_stricmp(pTab->zName, pFKey->zTo)!=0
|
||||
&& fkChildIsModified(pTab, pFKey, aChange, bChngRowid)==0
|
||||
){
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Find the parent table of this foreign key. Also find a unique index
|
||||
** on the parent key columns in the parent table. If either of these
|
||||
** schema items cannot be located, set an error in pParse and return
|
||||
@@ -758,7 +896,7 @@ void sqlite3FkCheck(
|
||||
int iJump = sqlite3VdbeCurrentAddr(v) + pFKey->nCol + 1;
|
||||
for(i=0; i<pFKey->nCol; i++){
|
||||
int iReg = pFKey->aCol[i].iFrom + regOld + 1;
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iJump);
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iJump); VdbeCoverage(v);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, -1);
|
||||
}
|
||||
@@ -810,12 +948,17 @@ void sqlite3FkCheck(
|
||||
sqlite3DbFree(db, aiFree);
|
||||
}
|
||||
|
||||
/* Loop through all the foreign key constraints that refer to this table */
|
||||
/* Loop through all the foreign key constraints that refer to this table.
|
||||
** (the "child" constraints) */
|
||||
for(pFKey = sqlite3FkReferences(pTab); pFKey; pFKey=pFKey->pNextTo){
|
||||
Index *pIdx = 0; /* Foreign key index for pFKey */
|
||||
SrcList *pSrc;
|
||||
int *aiCol = 0;
|
||||
|
||||
if( aChange && fkParentIsModified(pTab, pFKey, aChange, bChngRowid)==0 ){
|
||||
continue;
|
||||
}
|
||||
|
||||
if( !pFKey->isDeferred && !(db->flags & SQLITE_DeferFKs)
|
||||
&& !pParse->pToplevel && !pParse->isMultiWrite
|
||||
){
|
||||
@@ -831,9 +974,8 @@ void sqlite3FkCheck(
|
||||
}
|
||||
assert( aiCol || pFKey->nCol==1 );
|
||||
|
||||
/* Create a SrcList structure containing a single table (the table
|
||||
** the foreign key that refers to this table is attached to). This
|
||||
** is required for the sqlite3WhereXXX() interface. */
|
||||
/* Create a SrcList structure containing the child table. We need the
|
||||
** child table as a SrcList for sqlite3WhereBegin() */
|
||||
pSrc = sqlite3SrcListAppend(db, 0, 0, 0);
|
||||
if( pSrc ){
|
||||
struct SrcList_item *pItem = pSrc->a;
|
||||
@@ -882,13 +1024,14 @@ u32 sqlite3FkOldmask(
|
||||
Index *pIdx = 0;
|
||||
sqlite3FkLocateIndex(pParse, pTab, p, &pIdx, 0);
|
||||
if( pIdx ){
|
||||
for(i=0; i<pIdx->nColumn; i++) mask |= COLUMN_MASK(pIdx->aiColumn[i]);
|
||||
for(i=0; i<pIdx->nKeyCol; i++) mask |= COLUMN_MASK(pIdx->aiColumn[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This function is called before generating code to update or delete a
|
||||
** row contained in table pTab. If the operation is a DELETE, then
|
||||
@@ -918,32 +1061,16 @@ int sqlite3FkRequired(
|
||||
}else{
|
||||
/* This is an UPDATE. Foreign key processing is only required if the
|
||||
** operation modifies one or more child or parent key columns. */
|
||||
int i;
|
||||
FKey *p;
|
||||
|
||||
/* Check if any child key columns are being modified. */
|
||||
for(p=pTab->pFKey; p; p=p->pNextFrom){
|
||||
for(i=0; i<p->nCol; i++){
|
||||
int iChildKey = p->aCol[i].iFrom;
|
||||
if( aChange[iChildKey]>=0 ) return 1;
|
||||
if( iChildKey==pTab->iPKey && chngRowid ) return 1;
|
||||
}
|
||||
if( fkChildIsModified(pTab, p, aChange, chngRowid) ) return 1;
|
||||
}
|
||||
|
||||
/* Check if any parent key columns are being modified. */
|
||||
for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){
|
||||
for(i=0; i<p->nCol; i++){
|
||||
char *zKey = p->aCol[i].zCol;
|
||||
int iKey;
|
||||
for(iKey=0; iKey<pTab->nCol; iKey++){
|
||||
Column *pCol = &pTab->aCol[iKey];
|
||||
if( (zKey ? !sqlite3StrICmp(pCol->zName, zKey)
|
||||
: (pCol->colFlags & COLFLAG_PRIMKEY)!=0) ){
|
||||
if( aChange[iKey]>=0 ) return 1;
|
||||
if( iKey==pTab->iPKey && chngRowid ) return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( fkParentIsModified(pTab, p, aChange, chngRowid) ) return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1089,7 +1216,7 @@ static Trigger *fkActionTrigger(
|
||||
|
||||
tFrom.z = zFrom;
|
||||
tFrom.n = nFrom;
|
||||
pRaise = sqlite3Expr(db, TK_RAISE, "foreign key constraint failed");
|
||||
pRaise = sqlite3Expr(db, TK_RAISE, "FOREIGN KEY constraint failed");
|
||||
if( pRaise ){
|
||||
pRaise->affinity = OE_Abort;
|
||||
}
|
||||
@@ -1169,7 +1296,9 @@ void sqlite3FkActions(
|
||||
Parse *pParse, /* Parse context */
|
||||
Table *pTab, /* Table being updated or deleted from */
|
||||
ExprList *pChanges, /* Change-list for UPDATE, NULL for DELETE */
|
||||
int regOld /* Address of array containing old row */
|
||||
int regOld, /* Address of array containing old row */
|
||||
int *aChange, /* Array indicating UPDATEd columns (or 0) */
|
||||
int bChngRowid /* True if rowid is UPDATEd */
|
||||
){
|
||||
/* If foreign-key support is enabled, iterate through all FKs that
|
||||
** refer to table pTab. If there is an action associated with the FK
|
||||
@@ -1178,9 +1307,11 @@ void sqlite3FkActions(
|
||||
if( pParse->db->flags&SQLITE_ForeignKeys ){
|
||||
FKey *pFKey; /* Iterator variable */
|
||||
for(pFKey = sqlite3FkReferences(pTab); pFKey; pFKey=pFKey->pNextTo){
|
||||
Trigger *pAction = fkActionTrigger(pParse, pTab, pFKey, pChanges);
|
||||
if( pAction ){
|
||||
sqlite3CodeRowTriggerDirect(pParse, pAction, pTab, regOld, OE_Abort, 0);
|
||||
if( aChange==0 || fkParentIsModified(pTab, pFKey, aChange, bChngRowid) ){
|
||||
Trigger *pAct = fkActionTrigger(pParse, pTab, pFKey, pChanges);
|
||||
if( pAct ){
|
||||
sqlite3CodeRowTriggerDirect(pParse, pAct, pTab, regOld, OE_Abort, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+63
-31
@@ -137,9 +137,9 @@ static void absFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
case SQLITE_INTEGER: {
|
||||
i64 iVal = sqlite3_value_int64(argv[0]);
|
||||
if( iVal<0 ){
|
||||
if( (iVal<<1)==0 ){
|
||||
/* IMP: R-35460-15084 If X is the integer -9223372036854775807 then
|
||||
** abs(X) throws an integer overflow error since there is no
|
||||
if( iVal==SMALLEST_INT64 ){
|
||||
/* IMP: R-31676-45509 If X is the integer -9223372036854775808
|
||||
** then abs(X) throws an integer overflow error since there is no
|
||||
** equivalent positive 64-bit two complement value. */
|
||||
sqlite3_result_error(context, "integer overflow", -1);
|
||||
return;
|
||||
@@ -218,6 +218,32 @@ static void instrFunc(
|
||||
sqlite3_result_int(context, N);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the printf() function.
|
||||
*/
|
||||
static void printfFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
PrintfArguments x;
|
||||
StrAccum str;
|
||||
const char *zFormat;
|
||||
int n;
|
||||
|
||||
if( argc>=1 && (zFormat = (const char*)sqlite3_value_text(argv[0]))!=0 ){
|
||||
x.nArg = argc-1;
|
||||
x.nUsed = 0;
|
||||
x.apArg = argv+1;
|
||||
sqlite3StrAccumInit(&str, 0, 0, SQLITE_MAX_LENGTH);
|
||||
str.db = sqlite3_context_db_handle(context);
|
||||
sqlite3XPrintf(&str, SQLITE_PRINTF_SQLFUNC, zFormat, &x);
|
||||
n = str.nChar;
|
||||
sqlite3_result_text(context, sqlite3StrAccumFinish(&str), n,
|
||||
SQLITE_DYNAMIC);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the substr() function.
|
||||
**
|
||||
@@ -418,14 +444,14 @@ static void lowerFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
}
|
||||
|
||||
/*
|
||||
** The COALESCE() and IFNULL() functions are implemented as VDBE code so
|
||||
** that unused argument values do not have to be computed. However, we
|
||||
** still need some kind of function implementation for this routines in
|
||||
** the function table. That function implementation will never be called
|
||||
** so it doesn't matter what the implementation is. We might as well use
|
||||
** the "version()" function as a substitute.
|
||||
** Some functions like COALESCE() and IFNULL() and UNLIKELY() are implemented
|
||||
** as VDBE code so that unused argument values do not have to be computed.
|
||||
** However, we still need some kind of function implementation for this
|
||||
** routines in the function table. The noopFunc macro provides this.
|
||||
** noopFunc will never be called so it doesn't matter what the implementation
|
||||
** is. We might as well use the "version()" function as a substitute.
|
||||
*/
|
||||
#define ifnullFunc versionFunc /* Substitute function - never called */
|
||||
#define noopFunc versionFunc /* Substitute function - never called */
|
||||
|
||||
/*
|
||||
** Implementation of random(). Return a random integer.
|
||||
@@ -544,9 +570,9 @@ struct compareInfo {
|
||||
*/
|
||||
#if defined(SQLITE_EBCDIC)
|
||||
# define sqlite3Utf8Read(A) (*((*A)++))
|
||||
# define GlogUpperToLower(A) A = sqlite3UpperToLower[A]
|
||||
# define GlobUpperToLower(A) A = sqlite3UpperToLower[A]
|
||||
#else
|
||||
# define GlogUpperToLower(A) if( !((A)&~0x7f) ){ A = sqlite3UpperToLower[A]; }
|
||||
# define GlobUpperToLower(A) if( !((A)&~0x7f) ){ A = sqlite3UpperToLower[A]; }
|
||||
#endif
|
||||
|
||||
static const struct compareInfo globInfo = { '*', '?', '[', 0 };
|
||||
@@ -625,11 +651,11 @@ static int patternCompare(
|
||||
}
|
||||
while( (c2 = sqlite3Utf8Read(&zString))!=0 ){
|
||||
if( noCase ){
|
||||
GlogUpperToLower(c2);
|
||||
GlogUpperToLower(c);
|
||||
GlobUpperToLower(c2);
|
||||
GlobUpperToLower(c);
|
||||
while( c2 != 0 && c2 != c ){
|
||||
c2 = sqlite3Utf8Read(&zString);
|
||||
GlogUpperToLower(c2);
|
||||
GlobUpperToLower(c2);
|
||||
}
|
||||
}else{
|
||||
while( c2 != 0 && c2 != c ){
|
||||
@@ -681,8 +707,8 @@ static int patternCompare(
|
||||
}else{
|
||||
c2 = sqlite3Utf8Read(&zString);
|
||||
if( noCase ){
|
||||
GlogUpperToLower(c);
|
||||
GlogUpperToLower(c2);
|
||||
GlobUpperToLower(c);
|
||||
GlobUpperToLower(c2);
|
||||
}
|
||||
if( c!=c2 ){
|
||||
return 0;
|
||||
@@ -991,7 +1017,7 @@ static void charFunc(
|
||||
){
|
||||
unsigned char *z, *zOut;
|
||||
int i;
|
||||
zOut = z = sqlite3_malloc( argc*4 );
|
||||
zOut = z = sqlite3_malloc( argc*4+1 );
|
||||
if( z==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
return;
|
||||
@@ -1511,11 +1537,11 @@ static void groupConcatStep(
|
||||
zSep = ",";
|
||||
nSep = 1;
|
||||
}
|
||||
sqlite3StrAccumAppend(pAccum, zSep, nSep);
|
||||
if( nSep ) sqlite3StrAccumAppend(pAccum, zSep, nSep);
|
||||
}
|
||||
zVal = (char*)sqlite3_value_text(argv[0]);
|
||||
nVal = sqlite3_value_bytes(argv[0]);
|
||||
sqlite3StrAccumAppend(pAccum, zVal, nVal);
|
||||
if( nVal ) sqlite3StrAccumAppend(pAccum, zVal, nVal);
|
||||
}
|
||||
}
|
||||
static void groupConcatFinalize(sqlite3_context *context){
|
||||
@@ -1568,7 +1594,7 @@ static void setLikeOptFlag(sqlite3 *db, const char *zName, u8 flagVal){
|
||||
pDef = sqlite3FindFunction(db, zName, sqlite3Strlen30(zName),
|
||||
2, SQLITE_UTF8, 0);
|
||||
if( ALWAYS(pDef) ){
|
||||
pDef->flags = flagVal;
|
||||
pDef->funcFlags |= flagVal;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1612,7 +1638,7 @@ int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){
|
||||
pDef = sqlite3FindFunction(db, pExpr->u.zToken,
|
||||
sqlite3Strlen30(pExpr->u.zToken),
|
||||
2, SQLITE_UTF8, 0);
|
||||
if( NEVER(pDef==0) || (pDef->flags & SQLITE_FUNC_LIKE)==0 ){
|
||||
if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_FUNC_LIKE)==0 ){
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1624,7 +1650,7 @@ int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){
|
||||
assert( (char*)&likeInfoAlt == (char*)&likeInfoAlt.matchAll );
|
||||
assert( &((char*)&likeInfoAlt)[1] == (char*)&likeInfoAlt.matchOne );
|
||||
assert( &((char*)&likeInfoAlt)[2] == (char*)&likeInfoAlt.matchSet );
|
||||
*pIsNocase = (pDef->flags & SQLITE_FUNC_CASE)==0;
|
||||
*pIsNocase = (pDef->funcFlags & SQLITE_FUNC_CASE)==0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1662,6 +1688,7 @@ void sqlite3RegisterGlobalFunctions(void){
|
||||
FUNCTION(instr, 2, 0, 0, instrFunc ),
|
||||
FUNCTION(substr, 2, 0, 0, substrFunc ),
|
||||
FUNCTION(substr, 3, 0, 0, substrFunc ),
|
||||
FUNCTION(printf, -1, 0, 0, printfFunc ),
|
||||
FUNCTION(unicode, 1, 0, 0, unicodeFunc ),
|
||||
FUNCTION(char, -1, 0, 0, charFunc ),
|
||||
FUNCTION(abs, 1, 0, 0, absFunc ),
|
||||
@@ -1673,11 +1700,13 @@ void sqlite3RegisterGlobalFunctions(void){
|
||||
FUNCTION(lower, 1, 0, 0, lowerFunc ),
|
||||
FUNCTION(coalesce, 1, 0, 0, 0 ),
|
||||
FUNCTION(coalesce, 0, 0, 0, 0 ),
|
||||
FUNCTION2(coalesce, -1, 0, 0, ifnullFunc, SQLITE_FUNC_COALESCE),
|
||||
FUNCTION2(coalesce, -1, 0, 0, noopFunc, SQLITE_FUNC_COALESCE),
|
||||
FUNCTION(hex, 1, 0, 0, hexFunc ),
|
||||
FUNCTION2(ifnull, 2, 0, 0, ifnullFunc, SQLITE_FUNC_COALESCE),
|
||||
FUNCTION(random, 0, 0, 0, randomFunc ),
|
||||
FUNCTION(randomblob, 1, 0, 0, randomBlob ),
|
||||
FUNCTION2(ifnull, 2, 0, 0, noopFunc, SQLITE_FUNC_COALESCE),
|
||||
FUNCTION2(unlikely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
|
||||
FUNCTION2(likelihood, 2, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
|
||||
VFUNCTION(random, 0, 0, 0, randomFunc ),
|
||||
VFUNCTION(randomblob, 1, 0, 0, randomBlob ),
|
||||
FUNCTION(nullif, 2, 0, 1, nullifFunc ),
|
||||
FUNCTION(sqlite_version, 0, 0, 0, versionFunc ),
|
||||
FUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ),
|
||||
@@ -1687,9 +1716,9 @@ void sqlite3RegisterGlobalFunctions(void){
|
||||
FUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ),
|
||||
#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */
|
||||
FUNCTION(quote, 1, 0, 0, quoteFunc ),
|
||||
FUNCTION(last_insert_rowid, 0, 0, 0, last_insert_rowid),
|
||||
FUNCTION(changes, 0, 0, 0, changes ),
|
||||
FUNCTION(total_changes, 0, 0, 0, total_changes ),
|
||||
VFUNCTION(last_insert_rowid, 0, 0, 0, last_insert_rowid),
|
||||
VFUNCTION(changes, 0, 0, 0, changes ),
|
||||
VFUNCTION(total_changes, 0, 0, 0, total_changes ),
|
||||
FUNCTION(replace, 3, 0, 0, replaceFunc ),
|
||||
FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ),
|
||||
#ifdef SQLITE_SOUNDEX
|
||||
@@ -1703,7 +1732,7 @@ void sqlite3RegisterGlobalFunctions(void){
|
||||
AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize ),
|
||||
AGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize ),
|
||||
/* AGGREGATE(count, 0, 0, 0, countStep, countFinalize ), */
|
||||
{0,SQLITE_UTF8,SQLITE_FUNC_COUNT,0,0,0,countStep,countFinalize,"count",0,0},
|
||||
{0,SQLITE_UTF8|SQLITE_FUNC_COUNT,0,0,0,countStep,countFinalize,"count",0,0},
|
||||
AGGREGATE(count, 1, 0, 0, countStep, countFinalize ),
|
||||
AGGREGATE(group_concat, 1, 0, 0, groupConcatStep, groupConcatFinalize),
|
||||
AGGREGATE(group_concat, 2, 0, 0, groupConcatStep, groupConcatFinalize),
|
||||
@@ -1729,4 +1758,7 @@ void sqlite3RegisterGlobalFunctions(void){
|
||||
#ifndef SQLITE_OMIT_ALTERTABLE
|
||||
sqlite3AlterFunctions();
|
||||
#endif
|
||||
#if defined(SQLITE_ENABLE_STAT3) || defined(SQLITE_ENABLE_STAT4)
|
||||
sqlite3AnalyzeFunctions();
|
||||
#endif
|
||||
}
|
||||
|
||||
+1
-1
@@ -148,6 +148,7 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
|
||||
SQLITE_USE_URI, /* bOpenUri */
|
||||
SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */
|
||||
0x7ffffffe, /* mxStrlen */
|
||||
0, /* neverCorrupt */
|
||||
128, /* szLookaside */
|
||||
500, /* nLookaside */
|
||||
{0,0,0,0,0,0,0,0}, /* m */
|
||||
@@ -183,7 +184,6 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** Hash table for global functions - functions common to all
|
||||
** database connections. After initialization, this table is
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ void sqlite3HashClear(Hash *pH){
|
||||
** The hashing function.
|
||||
*/
|
||||
static unsigned int strHash(const char *z, int nKey){
|
||||
int h = 0;
|
||||
unsigned int h = 0;
|
||||
assert( nKey>=0 );
|
||||
while( nKey > 0 ){
|
||||
h = (h<<3) ^ h ^ sqlite3UpperToLower[(unsigned char)*z++];
|
||||
|
||||
+596
-512
File diff suppressed because it is too large
Load Diff
+85
-22
@@ -135,13 +135,6 @@ int sqlite3_initialize(void){
|
||||
*/
|
||||
if( sqlite3GlobalConfig.isInit ) return SQLITE_OK;
|
||||
|
||||
#ifdef SQLITE_ENABLE_SQLLOG
|
||||
{
|
||||
extern void sqlite3_init_sqllog(void);
|
||||
sqlite3_init_sqllog();
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Make sure the mutex subsystem is initialized. If unable to
|
||||
** initialize the mutex subsystem, return early with the error.
|
||||
** If the system is so sick that we are unable to allocate a mutex,
|
||||
@@ -515,6 +508,13 @@ int sqlite3_config(int op, ...){
|
||||
break;
|
||||
}
|
||||
|
||||
#if SQLITE_OS_WIN && defined(SQLITE_WIN32_MALLOC)
|
||||
case SQLITE_CONFIG_WIN32_HEAPSIZE: {
|
||||
sqlite3GlobalConfig.nHeap = va_arg(ap, int);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default: {
|
||||
rc = SQLITE_ERROR;
|
||||
break;
|
||||
@@ -581,7 +581,8 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
|
||||
db->lookaside.bEnabled = 1;
|
||||
db->lookaside.bMalloced = pBuf==0 ?1:0;
|
||||
}else{
|
||||
db->lookaside.pEnd = 0;
|
||||
db->lookaside.pStart = db;
|
||||
db->lookaside.pEnd = db;
|
||||
db->lookaside.bEnabled = 0;
|
||||
db->lookaside.bMalloced = 0;
|
||||
}
|
||||
@@ -979,9 +980,7 @@ void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){
|
||||
#endif
|
||||
|
||||
sqlite3Error(db, SQLITE_OK, 0); /* Deallocates any cached error strings. */
|
||||
if( db->pErr ){
|
||||
sqlite3ValueFree(db->pErr);
|
||||
}
|
||||
sqlite3ValueFree(db->pErr);
|
||||
sqlite3CloseExtensions(db);
|
||||
|
||||
db->magic = SQLITE_MAGIC_ERROR;
|
||||
@@ -1056,8 +1055,7 @@ void sqlite3RollbackAll(sqlite3 *db, int tripCode){
|
||||
** Return a static string containing the name corresponding to the error code
|
||||
** specified in the argument.
|
||||
*/
|
||||
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) || \
|
||||
defined(SQLITE_DEBUG_OS_TRACE)
|
||||
#if defined(SQLITE_TEST)
|
||||
const char *sqlite3ErrName(int rc){
|
||||
const char *zName = 0;
|
||||
int i, origRc = rc;
|
||||
@@ -1079,6 +1077,7 @@ const char *sqlite3ErrName(int rc){
|
||||
case SQLITE_READONLY_RECOVERY: zName = "SQLITE_READONLY_RECOVERY"; break;
|
||||
case SQLITE_READONLY_CANTLOCK: zName = "SQLITE_READONLY_CANTLOCK"; break;
|
||||
case SQLITE_READONLY_ROLLBACK: zName = "SQLITE_READONLY_ROLLBACK"; break;
|
||||
case SQLITE_READONLY_DBMOVED: zName = "SQLITE_READONLY_DBMOVED"; break;
|
||||
case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break;
|
||||
case SQLITE_IOERR: zName = "SQLITE_IOERR"; break;
|
||||
case SQLITE_IOERR_READ: zName = "SQLITE_IOERR_READ"; break;
|
||||
@@ -1107,6 +1106,7 @@ const char *sqlite3ErrName(int rc){
|
||||
case SQLITE_IOERR_DELETE_NOENT: zName = "SQLITE_IOERR_DELETE_NOENT";break;
|
||||
case SQLITE_IOERR_MMAP: zName = "SQLITE_IOERR_MMAP"; break;
|
||||
case SQLITE_IOERR_GETTEMPPATH: zName = "SQLITE_IOERR_GETTEMPPATH"; break;
|
||||
case SQLITE_IOERR_CONVPATH: zName = "SQLITE_IOERR_CONVPATH"; break;
|
||||
case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break;
|
||||
case SQLITE_CORRUPT_VTAB: zName = "SQLITE_CORRUPT_VTAB"; break;
|
||||
case SQLITE_NOTFOUND: zName = "SQLITE_NOTFOUND"; break;
|
||||
@@ -1115,6 +1115,7 @@ const char *sqlite3ErrName(int rc){
|
||||
case SQLITE_CANTOPEN_NOTEMPDIR: zName = "SQLITE_CANTOPEN_NOTEMPDIR";break;
|
||||
case SQLITE_CANTOPEN_ISDIR: zName = "SQLITE_CANTOPEN_ISDIR"; break;
|
||||
case SQLITE_CANTOPEN_FULLPATH: zName = "SQLITE_CANTOPEN_FULLPATH"; break;
|
||||
case SQLITE_CANTOPEN_CONVPATH: zName = "SQLITE_CANTOPEN_CONVPATH"; break;
|
||||
case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break;
|
||||
case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break;
|
||||
case SQLITE_SCHEMA: zName = "SQLITE_SCHEMA"; break;
|
||||
@@ -1133,6 +1134,7 @@ const char *sqlite3ErrName(int rc){
|
||||
case SQLITE_CONSTRAINT_VTAB: zName = "SQLITE_CONSTRAINT_VTAB"; break;
|
||||
case SQLITE_CONSTRAINT_FUNCTION:
|
||||
zName = "SQLITE_CONSTRAINT_FUNCTION"; break;
|
||||
case SQLITE_CONSTRAINT_ROWID: zName = "SQLITE_CONSTRAINT_ROWID"; break;
|
||||
case SQLITE_MISMATCH: zName = "SQLITE_MISMATCH"; break;
|
||||
case SQLITE_MISUSE: zName = "SQLITE_MISUSE"; break;
|
||||
case SQLITE_NOLFS: zName = "SQLITE_NOLFS"; break;
|
||||
@@ -1360,6 +1362,7 @@ int sqlite3CreateFunc(
|
||||
){
|
||||
FuncDef *p;
|
||||
int nName;
|
||||
int extraFlags;
|
||||
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
if( zFunctionName==0 ||
|
||||
@@ -1370,6 +1373,10 @@ int sqlite3CreateFunc(
|
||||
(255<(nName = sqlite3Strlen30( zFunctionName))) ){
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
}
|
||||
|
||||
assert( SQLITE_FUNC_CONSTANT==SQLITE_DETERMINISTIC );
|
||||
extraFlags = enc & SQLITE_DETERMINISTIC;
|
||||
enc &= (SQLITE_FUNC_ENCMASK|SQLITE_ANY);
|
||||
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
/* If SQLITE_UTF16 is specified as the encoding type, transform this
|
||||
@@ -1383,10 +1390,10 @@ int sqlite3CreateFunc(
|
||||
enc = SQLITE_UTF16NATIVE;
|
||||
}else if( enc==SQLITE_ANY ){
|
||||
int rc;
|
||||
rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF8,
|
||||
rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF8|extraFlags,
|
||||
pUserData, xFunc, xStep, xFinal, pDestructor);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF16LE,
|
||||
rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF16LE|extraFlags,
|
||||
pUserData, xFunc, xStep, xFinal, pDestructor);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
@@ -1404,7 +1411,7 @@ int sqlite3CreateFunc(
|
||||
** operation to continue but invalidate all precompiled statements.
|
||||
*/
|
||||
p = sqlite3FindFunction(db, zFunctionName, nName, nArg, (u8)enc, 0);
|
||||
if( p && p->iPrefEnc==enc && p->nArg==nArg ){
|
||||
if( p && (p->funcFlags & SQLITE_FUNC_ENCMASK)==enc && p->nArg==nArg ){
|
||||
if( db->nVdbeActive ){
|
||||
sqlite3Error(db, SQLITE_BUSY,
|
||||
"unable to delete/modify user-function due to active statements");
|
||||
@@ -1429,7 +1436,8 @@ int sqlite3CreateFunc(
|
||||
pDestructor->nRef++;
|
||||
}
|
||||
p->pDestructor = pDestructor;
|
||||
p->flags = 0;
|
||||
p->funcFlags = (p->funcFlags & SQLITE_FUNC_ENCMASK) | extraFlags;
|
||||
testcase( p->funcFlags & SQLITE_DETERMINISTIC );
|
||||
p->xFunc = xFunc;
|
||||
p->xStep = xStep;
|
||||
p->xFinalize = xFinal;
|
||||
@@ -1859,6 +1867,7 @@ const char *sqlite3_errmsg(sqlite3 *db){
|
||||
if( db->mallocFailed ){
|
||||
z = sqlite3ErrStr(SQLITE_NOMEM);
|
||||
}else{
|
||||
testcase( db->pErr==0 );
|
||||
z = (char*)sqlite3_value_text(db->pErr);
|
||||
assert( !db->mallocFailed );
|
||||
if( z==0 ){
|
||||
@@ -1900,8 +1909,7 @@ const void *sqlite3_errmsg16(sqlite3 *db){
|
||||
}else{
|
||||
z = sqlite3_value_text16(db->pErr);
|
||||
if( z==0 ){
|
||||
sqlite3ValueSetStr(db->pErr, -1, sqlite3ErrStr(db->errCode),
|
||||
SQLITE_UTF8, SQLITE_STATIC);
|
||||
sqlite3Error(db, db->errCode, sqlite3ErrStr(db->errCode));
|
||||
z = sqlite3_value_text16(db->pErr);
|
||||
}
|
||||
/* A malloc() may have failed within the call to sqlite3_value_text16()
|
||||
@@ -1948,6 +1956,32 @@ const char *sqlite3_errstr(int rc){
|
||||
return sqlite3ErrStr(rc);
|
||||
}
|
||||
|
||||
/*
|
||||
** Invalidate all cached KeyInfo objects for database connection "db"
|
||||
*/
|
||||
static void invalidateCachedKeyInfo(sqlite3 *db){
|
||||
Db *pDb; /* A single database */
|
||||
int iDb; /* The database index number */
|
||||
HashElem *k; /* For looping over tables in pDb */
|
||||
Table *pTab; /* A table in the database */
|
||||
Index *pIdx; /* Each index */
|
||||
|
||||
for(iDb=0, pDb=db->aDb; iDb<db->nDb; iDb++, pDb++){
|
||||
if( pDb->pBt==0 ) continue;
|
||||
sqlite3BtreeEnter(pDb->pBt);
|
||||
for(k=sqliteHashFirst(&pDb->pSchema->tblHash); k; k=sqliteHashNext(k)){
|
||||
pTab = (Table*)sqliteHashData(k);
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
if( pIdx->pKeyInfo && pIdx->pKeyInfo->db==db ){
|
||||
sqlite3KeyInfoUnref(pIdx->pKeyInfo);
|
||||
pIdx->pKeyInfo = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3BtreeLeave(pDb->pBt);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Create a new collating function for database "db". The name is zName
|
||||
** and the encoding is enc.
|
||||
@@ -1992,6 +2026,7 @@ static int createCollation(
|
||||
return SQLITE_BUSY;
|
||||
}
|
||||
sqlite3ExpirePreparedStatements(db);
|
||||
invalidateCachedKeyInfo(db);
|
||||
|
||||
/* If collation sequence pColl was created directly by a call to
|
||||
** sqlite3_create_collation, and not generated by synthCollSeq(),
|
||||
@@ -2588,8 +2623,6 @@ static int openDatabase(
|
||||
}
|
||||
#endif
|
||||
|
||||
sqlite3Error(db, rc, 0);
|
||||
|
||||
/* -DSQLITE_DEFAULT_LOCKING_MODE=1 makes EXCLUSIVE the default locking
|
||||
** mode. -DSQLITE_DEFAULT_LOCKING_MODE=0 make NORMAL the default locking
|
||||
** mode. Doing nothing at all also makes NORMAL the default.
|
||||
@@ -2600,6 +2633,8 @@ static int openDatabase(
|
||||
SQLITE_DEFAULT_LOCKING_MODE);
|
||||
#endif
|
||||
|
||||
if( rc ) sqlite3Error(db, rc, 0);
|
||||
|
||||
/* Enable the lookaside-malloc subsystem */
|
||||
setupLookaside(db, 0, sqlite3GlobalConfig.szLookaside,
|
||||
sqlite3GlobalConfig.nLookaside);
|
||||
@@ -3059,7 +3094,7 @@ int sqlite3_test_control(int op, ...){
|
||||
** to the xRandomness method of the default VFS.
|
||||
*/
|
||||
case SQLITE_TESTCTRL_PRNG_RESET: {
|
||||
sqlite3PrngResetState();
|
||||
sqlite3_randomness(0,0);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3259,6 +3294,34 @@ int sqlite3_test_control(int op, ...){
|
||||
}
|
||||
#endif
|
||||
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_NEVER_CORRUPT, int);
|
||||
**
|
||||
** Set or clear a flag that indicates that the database file is always well-
|
||||
** formed and never corrupt. This flag is clear by default, indicating that
|
||||
** database files might have arbitrary corruption. Setting the flag during
|
||||
** testing causes certain assert() statements in the code to be activated
|
||||
** that demonstrat invariants on well-formed database files.
|
||||
*/
|
||||
case SQLITE_TESTCTRL_NEVER_CORRUPT: {
|
||||
sqlite3GlobalConfig.neverCorrupt = va_arg(ap, int);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_VDBE_COVERAGE, xCallback, ptr);
|
||||
**
|
||||
** Set the VDBE coverage callback function to xCallback with context
|
||||
** pointer ptr.
|
||||
*/
|
||||
case SQLITE_TESTCTRL_VDBE_COVERAGE: {
|
||||
#ifdef SQLITE_VDBE_COVERAGE
|
||||
typedef void (*branch_callback)(void*,int,u8,u8);
|
||||
sqlite3GlobalConfig.xVdbeBranch = va_arg(ap,branch_callback);
|
||||
sqlite3GlobalConfig.pVdbeBranchArg = va_arg(ap,void*);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
va_end(ap);
|
||||
#endif /* SQLITE_OMIT_BUILTIN_TEST */
|
||||
|
||||
+5
-3
@@ -433,7 +433,7 @@ void sqlite3ScratchFree(void *p){
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_LOOKASIDE
|
||||
static int isLookaside(sqlite3 *db, void *p){
|
||||
return p && p>=db->lookaside.pStart && p<db->lookaside.pEnd;
|
||||
return p>=db->lookaside.pStart && p<db->lookaside.pEnd;
|
||||
}
|
||||
#else
|
||||
#define isLookaside(A,B) 0
|
||||
@@ -449,8 +449,9 @@ int sqlite3MallocSize(void *p){
|
||||
return sqlite3GlobalConfig.m.xSize(p);
|
||||
}
|
||||
int sqlite3DbMallocSize(sqlite3 *db, void *p){
|
||||
assert( db==0 || sqlite3_mutex_held(db->mutex) );
|
||||
if( db && isLookaside(db, p) ){
|
||||
assert( db!=0 );
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
if( isLookaside(db, p) ){
|
||||
return db->lookaside.sz;
|
||||
}else{
|
||||
assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) );
|
||||
@@ -484,6 +485,7 @@ void sqlite3_free(void *p){
|
||||
*/
|
||||
void sqlite3DbFree(sqlite3 *db, void *p){
|
||||
assert( db==0 || sqlite3_mutex_held(db->mutex) );
|
||||
if( p==0 ) return;
|
||||
if( db ){
|
||||
if( db->pnBytesFreed ){
|
||||
*db->pnBytesFreed += sqlite3DbMallocSize(db, p);
|
||||
|
||||
+39
-23
@@ -49,16 +49,6 @@
|
||||
** macros.
|
||||
*/
|
||||
#ifdef SQLITE_SYSTEM_MALLOC
|
||||
|
||||
/*
|
||||
** The MSVCRT has malloc_usable_size() but it is called _msize().
|
||||
** The use of _msize() is automatic, but can be disabled by compiling
|
||||
** with -DSQLITE_WITHOUT_MSIZE
|
||||
*/
|
||||
#if defined(_MSC_VER) && !defined(SQLITE_WITHOUT_MSIZE)
|
||||
# define SQLITE_MALLOCSIZE _msize
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) && !defined(SQLITE_WITHOUT_ZONEMALLOC)
|
||||
|
||||
/*
|
||||
@@ -81,22 +71,48 @@ static malloc_zone_t* _sqliteZone_;
|
||||
** Use standard C library malloc and free on non-Apple systems.
|
||||
** Also used by Apple systems if SQLITE_WITHOUT_ZONEMALLOC is defined.
|
||||
*/
|
||||
#define SQLITE_MALLOC(x) malloc(x)
|
||||
#define SQLITE_FREE(x) free(x)
|
||||
#define SQLITE_REALLOC(x,y) realloc((x),(y))
|
||||
#define SQLITE_MALLOC(x) malloc(x)
|
||||
#define SQLITE_FREE(x) free(x)
|
||||
#define SQLITE_REALLOC(x,y) realloc((x),(y))
|
||||
|
||||
#if (defined(_MSC_VER) && !defined(SQLITE_WITHOUT_MSIZE)) \
|
||||
|| (defined(HAVE_MALLOC_H) && defined(HAVE_MALLOC_USABLE_SIZE))
|
||||
# include <malloc.h> /* Needed for malloc_usable_size on linux */
|
||||
#endif
|
||||
#ifdef HAVE_MALLOC_USABLE_SIZE
|
||||
# ifndef SQLITE_MALLOCSIZE
|
||||
# define SQLITE_MALLOCSIZE(x) malloc_usable_size(x)
|
||||
# endif
|
||||
#else
|
||||
# undef SQLITE_MALLOCSIZE
|
||||
/*
|
||||
** The malloc.h header file is needed for malloc_usable_size() function
|
||||
** on some systems (e.g. Linux).
|
||||
*/
|
||||
#if defined(HAVE_MALLOC_H) && defined(HAVE_MALLOC_USABLE_SIZE)
|
||||
# define SQLITE_USE_MALLOC_H
|
||||
# define SQLITE_USE_MALLOC_USABLE_SIZE
|
||||
/*
|
||||
** The MSVCRT has malloc_usable_size(), but it is called _msize(). The
|
||||
** use of _msize() is automatic, but can be disabled by compiling with
|
||||
** -DSQLITE_WITHOUT_MSIZE. Using the _msize() function also requires
|
||||
** the malloc.h header file.
|
||||
*/
|
||||
#elif defined(_MSC_VER) && !defined(SQLITE_WITHOUT_MSIZE)
|
||||
# define SQLITE_USE_MALLOC_H
|
||||
# define SQLITE_USE_MSIZE
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Include the malloc.h header file, if necessary. Also set define macro
|
||||
** SQLITE_MALLOCSIZE to the appropriate function name, which is _msize()
|
||||
** for MSVC and malloc_usable_size() for most other systems (e.g. Linux).
|
||||
** The memory size function can always be overridden manually by defining
|
||||
** the macro SQLITE_MALLOCSIZE to the desired function name.
|
||||
*/
|
||||
#if defined(SQLITE_USE_MALLOC_H)
|
||||
# include <malloc.h>
|
||||
# if defined(SQLITE_USE_MALLOC_USABLE_SIZE)
|
||||
# if !defined(SQLITE_MALLOCSIZE)
|
||||
# define SQLITE_MALLOCSIZE(x) malloc_usable_size(x)
|
||||
# endif
|
||||
# elif defined(SQLITE_USE_MSIZE)
|
||||
# if !defined(SQLITE_MALLOCSIZE)
|
||||
# define SQLITE_MALLOCSIZE _msize
|
||||
# endif
|
||||
# endif
|
||||
#endif /* defined(SQLITE_USE_MALLOC_H) */
|
||||
|
||||
#endif /* __APPLE__ or not __APPLE__ */
|
||||
|
||||
/*
|
||||
|
||||
+8
-8
@@ -179,7 +179,7 @@ static int sqlite3MemSize(void *p){
|
||||
return 0;
|
||||
}
|
||||
pHdr = sqlite3MemsysGetHeader(p);
|
||||
return pHdr->iSize;
|
||||
return (int)pHdr->iSize;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -221,7 +221,7 @@ static void randomFill(char *pBuf, int nByte){
|
||||
x = SQLITE_PTR_TO_INT(pBuf);
|
||||
y = nByte | 1;
|
||||
while( nByte >= 4 ){
|
||||
x = (x>>1) ^ (-(x&1) & 0xd0000001);
|
||||
x = (x>>1) ^ (-(int)(x&1) & 0xd0000001);
|
||||
y = y*1103515245 + 12345;
|
||||
r = x ^ y;
|
||||
*(int*)pBuf = r;
|
||||
@@ -229,7 +229,7 @@ static void randomFill(char *pBuf, int nByte){
|
||||
nByte -= 4;
|
||||
}
|
||||
while( nByte-- > 0 ){
|
||||
x = (x>>1) ^ (-(x&1) & 0xd0000001);
|
||||
x = (x>>1) ^ (-(int)(x&1) & 0xd0000001);
|
||||
y = y*1103515245 + 12345;
|
||||
r = x ^ y;
|
||||
*(pBuf++) = r & 0xff;
|
||||
@@ -324,9 +324,9 @@ static void sqlite3MemFree(void *pPrior){
|
||||
}
|
||||
z = (char*)pBt;
|
||||
z -= pHdr->nTitle;
|
||||
adjustStats(pHdr->iSize, -1);
|
||||
adjustStats((int)pHdr->iSize, -1);
|
||||
randomFill(z, sizeof(void*)*pHdr->nBacktraceSlots + sizeof(*pHdr) +
|
||||
pHdr->iSize + sizeof(int) + pHdr->nTitle);
|
||||
(int)pHdr->iSize + sizeof(int) + pHdr->nTitle);
|
||||
free(z);
|
||||
sqlite3_mutex_leave(mem.mutex);
|
||||
}
|
||||
@@ -348,9 +348,9 @@ static void *sqlite3MemRealloc(void *pPrior, int nByte){
|
||||
pOldHdr = sqlite3MemsysGetHeader(pPrior);
|
||||
pNew = sqlite3MemMalloc(nByte);
|
||||
if( pNew ){
|
||||
memcpy(pNew, pPrior, nByte<pOldHdr->iSize ? nByte : pOldHdr->iSize);
|
||||
memcpy(pNew, pPrior, (int)(nByte<pOldHdr->iSize ? nByte : pOldHdr->iSize));
|
||||
if( nByte>pOldHdr->iSize ){
|
||||
randomFill(&((char*)pNew)[pOldHdr->iSize], nByte - pOldHdr->iSize);
|
||||
randomFill(&((char*)pNew)[pOldHdr->iSize], nByte - (int)pOldHdr->iSize);
|
||||
}
|
||||
sqlite3MemFree(pPrior);
|
||||
}
|
||||
@@ -465,7 +465,7 @@ void sqlite3MemdebugSync(){
|
||||
for(pHdr=mem.pFirst; pHdr; pHdr=pHdr->pNext){
|
||||
void **pBt = (void**)pHdr;
|
||||
pBt -= pHdr->nBacktraceSlots;
|
||||
mem.xBacktrace(pHdr->iSize, pHdr->nBacktrace-1, &pBt[1]);
|
||||
mem.xBacktrace((int)pHdr->iSize, pHdr->nBacktrace-1, &pBt[1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+17
-22
@@ -202,32 +202,13 @@ static void memsys5Leave(void){
|
||||
static int memsys5Size(void *p){
|
||||
int iSize = 0;
|
||||
if( p ){
|
||||
int i = ((u8 *)p-mem5.zPool)/mem5.szAtom;
|
||||
int i = (int)(((u8 *)p-mem5.zPool)/mem5.szAtom);
|
||||
assert( i>=0 && i<mem5.nBlock );
|
||||
iSize = mem5.szAtom * (1 << (mem5.aCtrl[i]&CTRL_LOGSIZE));
|
||||
}
|
||||
return iSize;
|
||||
}
|
||||
|
||||
/*
|
||||
** Find the first entry on the freelist iLogsize. Unlink that
|
||||
** entry and return its index.
|
||||
*/
|
||||
static int memsys5UnlinkFirst(int iLogsize){
|
||||
int i;
|
||||
int iFirst;
|
||||
|
||||
assert( iLogsize>=0 && iLogsize<=LOGMAX );
|
||||
i = iFirst = mem5.aiFreelist[iLogsize];
|
||||
assert( iFirst>=0 );
|
||||
while( i>0 ){
|
||||
if( i<iFirst ) iFirst = i;
|
||||
i = MEM5LINK(i)->next;
|
||||
}
|
||||
memsys5Unlink(iFirst, iLogsize);
|
||||
return iFirst;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a block of memory of at least nBytes in size.
|
||||
** Return NULL if unable. Return NULL if nBytes==0.
|
||||
@@ -273,7 +254,8 @@ static void *memsys5MallocUnsafe(int nByte){
|
||||
sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes", nByte);
|
||||
return 0;
|
||||
}
|
||||
i = memsys5UnlinkFirst(iBin);
|
||||
i = mem5.aiFreelist[iBin];
|
||||
memsys5Unlink(i, iBin);
|
||||
while( iBin>iLogsize ){
|
||||
int newSize;
|
||||
|
||||
@@ -293,6 +275,12 @@ static void *memsys5MallocUnsafe(int nByte){
|
||||
if( mem5.maxCount<mem5.currentCount ) mem5.maxCount = mem5.currentCount;
|
||||
if( mem5.maxOut<mem5.currentOut ) mem5.maxOut = mem5.currentOut;
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* Make sure the allocated memory does not assume that it is set to zero
|
||||
** or retains a value from a previous allocation */
|
||||
memset(&mem5.zPool[i*mem5.szAtom], 0xAA, iFullSz);
|
||||
#endif
|
||||
|
||||
/* Return a pointer to the allocated memory. */
|
||||
return (void*)&mem5.zPool[i*mem5.szAtom];
|
||||
}
|
||||
@@ -307,7 +295,7 @@ static void memsys5FreeUnsafe(void *pOld){
|
||||
/* Set iBlock to the index of the block pointed to by pOld in
|
||||
** the array of mem5.szAtom byte blocks pointed to by mem5.zPool.
|
||||
*/
|
||||
iBlock = ((u8 *)pOld-mem5.zPool)/mem5.szAtom;
|
||||
iBlock = (int)(((u8 *)pOld-mem5.zPool)/mem5.szAtom);
|
||||
|
||||
/* Check that the pointer pOld points to a valid, non-free block. */
|
||||
assert( iBlock>=0 && iBlock<mem5.nBlock );
|
||||
@@ -350,6 +338,13 @@ static void memsys5FreeUnsafe(void *pOld){
|
||||
}
|
||||
size *= 2;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* Overwrite freed memory with the 0x55 bit pattern to verify that it is
|
||||
** not used after being freed */
|
||||
memset(&mem5.zPool[iBlock*mem5.szAtom], 0x55, size);
|
||||
#endif
|
||||
|
||||
memsys5Link(iBlock, iLogsize);
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -69,7 +69,7 @@ struct sqlite3_mutex {
|
||||
}
|
||||
return osType==2;
|
||||
}
|
||||
#endif /* SQLITE_OS_WINCE */
|
||||
#endif /* SQLITE_OS_WINCE || SQLITE_OS_WINRT */
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
@@ -107,7 +107,7 @@ static int winMutex_isInit = 0;
|
||||
** processing, the "interlocked" magic is probably not
|
||||
** strictly necessary.
|
||||
*/
|
||||
static long winMutex_lock = 0;
|
||||
static LONG winMutex_lock = 0;
|
||||
|
||||
void sqlite3_win32_sleep(DWORD milliseconds); /* os_win.c */
|
||||
|
||||
|
||||
@@ -107,7 +107,21 @@ int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut){
|
||||
** routine has no return value since the return value would be meaningless.
|
||||
*/
|
||||
int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){
|
||||
DO_OS_MALLOC_TEST(id);
|
||||
#ifdef SQLITE_TEST
|
||||
if( op!=SQLITE_FCNTL_COMMIT_PHASETWO ){
|
||||
/* Faults are not injected into COMMIT_PHASETWO because, assuming SQLite
|
||||
** is using a regular VFS, it is called after the corresponding
|
||||
** transaction has been committed. Injecting a fault at this point
|
||||
** confuses the test scripts - the COMMIT comand returns SQLITE_NOMEM
|
||||
** but the transaction is committed anyway.
|
||||
**
|
||||
** The core must call OsFileControl() though, not OsFileControlHint(),
|
||||
** as if a custom VFS (e.g. zipvfs) returns an error here, it probably
|
||||
** means the commit really has failed and an error should be returned
|
||||
** to the user. */
|
||||
DO_OS_MALLOC_TEST(id);
|
||||
}
|
||||
#endif
|
||||
return id->pMethods->xFileControl(id, op, pArg);
|
||||
}
|
||||
void sqlite3OsFileControlHint(sqlite3_file *id, int op, void *pArg){
|
||||
|
||||
+88
-47
@@ -83,32 +83,6 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
** These #defines should enable >2GB file support on Posix if the
|
||||
** underlying operating system supports it. If the OS lacks
|
||||
** large file support, these should be no-ops.
|
||||
**
|
||||
** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch
|
||||
** on the compiler command line. This is necessary if you are compiling
|
||||
** on a recent machine (ex: RedHat 7.2) but you want your code to work
|
||||
** on an older machine (ex: RedHat 6.0). If you compile on RedHat 7.2
|
||||
** without this option, LFS is enable. But LFS does not exist in the kernel
|
||||
** in RedHat 6.0, so the code won't work. Hence, for maximum binary
|
||||
** portability you should omit LFS.
|
||||
**
|
||||
** The previous paragraph was written in 2005. (This paragraph is written
|
||||
** on 2008-11-28.) These days, all Linux kernels support large files, so
|
||||
** you should probably leave LFS enabled. But some embedded platforms might
|
||||
** lack LFS in which case the SQLITE_DISABLE_LFS macro might still be useful.
|
||||
*/
|
||||
#ifndef SQLITE_DISABLE_LFS
|
||||
# define _LARGE_FILE 1
|
||||
# ifndef _FILE_OFFSET_BITS
|
||||
# define _FILE_OFFSET_BITS 64
|
||||
# endif
|
||||
# define _LARGEFILE_SOURCE 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
** standard include files.
|
||||
*/
|
||||
@@ -218,11 +192,13 @@ struct unixFile {
|
||||
const char *zPath; /* Name of the file */
|
||||
unixShm *pShm; /* Shared memory segment information */
|
||||
int szChunk; /* Configured by FCNTL_CHUNK_SIZE */
|
||||
#if SQLITE_MAX_MMAP_SIZE>0
|
||||
int nFetchOut; /* Number of outstanding xFetch refs */
|
||||
sqlite3_int64 mmapSize; /* Usable size of mapping at pMapRegion */
|
||||
sqlite3_int64 mmapSizeActual; /* Actual size of mapping at pMapRegion */
|
||||
sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */
|
||||
void *pMapRegion; /* Memory mapped region */
|
||||
#endif
|
||||
#ifdef __QNXNTO__
|
||||
int sectorSize; /* Device sector size */
|
||||
int deviceCharacteristics; /* Precomputed device characteristics */
|
||||
@@ -258,6 +234,12 @@ struct unixFile {
|
||||
#endif
|
||||
};
|
||||
|
||||
/* This variable holds the process id (pid) from when the xRandomness()
|
||||
** method was called. If xOpen() is called from a different process id,
|
||||
** indicating that a fork() has occurred, the PRNG will be reset.
|
||||
*/
|
||||
static int randomnessPid = 0;
|
||||
|
||||
/*
|
||||
** Allowed values for the unixFile.ctrlFlags bitmask:
|
||||
*/
|
||||
@@ -449,6 +431,7 @@ static struct unix_syscall {
|
||||
{ "fchown", (sqlite3_syscall_ptr)posixFchown, 0 },
|
||||
#define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent)
|
||||
|
||||
#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
|
||||
{ "mmap", (sqlite3_syscall_ptr)mmap, 0 },
|
||||
#define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[21].pCurrent)
|
||||
|
||||
@@ -461,6 +444,7 @@ static struct unix_syscall {
|
||||
{ "mremap", (sqlite3_syscall_ptr)0, 0 },
|
||||
#endif
|
||||
#define osMremap ((void*(*)(void*,size_t,size_t,int,...))aSyscall[23].pCurrent)
|
||||
#endif
|
||||
|
||||
}; /* End of the overrideable system calls */
|
||||
|
||||
@@ -547,6 +531,15 @@ static const char *unixNextSystemCall(sqlite3_vfs *p, const char *zName){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Do not accept any file descriptor less than this value, in order to avoid
|
||||
** opening database file using file descriptors that are commonly used for
|
||||
** standard input, output, and error.
|
||||
*/
|
||||
#ifndef SQLITE_MINIMUM_FILE_DESCRIPTOR
|
||||
# define SQLITE_MINIMUM_FILE_DESCRIPTOR 3
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Invoke open(). Do so multiple times, until it either succeeds or
|
||||
** fails for some reason other than EINTR.
|
||||
@@ -567,13 +560,23 @@ static const char *unixNextSystemCall(sqlite3_vfs *p, const char *zName){
|
||||
static int robust_open(const char *z, int f, mode_t m){
|
||||
int fd;
|
||||
mode_t m2 = m ? m : SQLITE_DEFAULT_FILE_PERMISSIONS;
|
||||
do{
|
||||
while(1){
|
||||
#if defined(O_CLOEXEC)
|
||||
fd = osOpen(z,f|O_CLOEXEC,m2);
|
||||
#else
|
||||
fd = osOpen(z,f,m2);
|
||||
#endif
|
||||
}while( fd<0 && errno==EINTR );
|
||||
if( fd<0 ){
|
||||
if( errno==EINTR ) continue;
|
||||
break;
|
||||
}
|
||||
if( fd>=SQLITE_MINIMUM_FILE_DESCRIPTOR ) break;
|
||||
osClose(fd);
|
||||
sqlite3_log(SQLITE_WARNING,
|
||||
"attempt to open \"%s\" as file descriptor %d", z, fd);
|
||||
fd = -1;
|
||||
if( osOpen("/dev/null", f, m)<0 ) break;
|
||||
}
|
||||
if( fd>=0 ){
|
||||
if( m!=0 ){
|
||||
struct stat statbuf;
|
||||
@@ -1292,6 +1295,15 @@ static int findInodeInfo(
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if pFile has been renamed or unlinked since it was first opened.
|
||||
*/
|
||||
static int fileHasMoved(unixFile *pFile){
|
||||
struct stat buf;
|
||||
return pFile->pInode!=0 &&
|
||||
(osStat(pFile->zPath, &buf)!=0 || buf.st_ino!=pFile->pInode->fileId.ino);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Check a unixFile that is a database. Verify the following:
|
||||
@@ -1326,10 +1338,7 @@ static void verifyDbFile(unixFile *pFile){
|
||||
pFile->ctrlFlags |= UNIXFILE_WARNED;
|
||||
return;
|
||||
}
|
||||
if( pFile->pInode!=0
|
||||
&& ((rc = osStat(pFile->zPath, &buf))!=0
|
||||
|| buf.st_ino!=pFile->pInode->fileId.ino)
|
||||
){
|
||||
if( fileHasMoved(pFile) ){
|
||||
sqlite3_log(SQLITE_WARNING, "file renamed while open: %s", pFile->zPath);
|
||||
pFile->ctrlFlags |= UNIXFILE_WARNED;
|
||||
return;
|
||||
@@ -1867,12 +1876,16 @@ end_unlock:
|
||||
** the requested locking level, this routine is a no-op.
|
||||
*/
|
||||
static int unixUnlock(sqlite3_file *id, int eFileLock){
|
||||
#if SQLITE_MAX_MMAP_SIZE>0
|
||||
assert( eFileLock==SHARED_LOCK || ((unixFile *)id)->nFetchOut==0 );
|
||||
#endif
|
||||
return posixUnlock(id, eFileLock, 0);
|
||||
}
|
||||
|
||||
#if SQLITE_MAX_MMAP_SIZE>0
|
||||
static int unixMapfile(unixFile *pFd, i64 nByte);
|
||||
static void unixUnmapfile(unixFile *pFd);
|
||||
#endif
|
||||
|
||||
/*
|
||||
** This function performs the parts of the "close file" operation
|
||||
@@ -1886,7 +1899,9 @@ static void unixUnmapfile(unixFile *pFd);
|
||||
*/
|
||||
static int closeUnixFile(sqlite3_file *id){
|
||||
unixFile *pFile = (unixFile*)id;
|
||||
#if SQLITE_MAX_MMAP_SIZE>0
|
||||
unixUnmapfile(pFile);
|
||||
#endif
|
||||
if( pFile->h>=0 ){
|
||||
robust_close(pFile, pFile->h, __LINE__);
|
||||
pFile->h = -1;
|
||||
@@ -3091,6 +3106,7 @@ static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){
|
||||
#endif
|
||||
TIMER_START;
|
||||
assert( cnt==(cnt&0x1ffff) );
|
||||
assert( id->h>2 );
|
||||
cnt &= 0x1ffff;
|
||||
do{
|
||||
#if defined(USE_PREAD)
|
||||
@@ -3205,6 +3221,7 @@ static int seekAndWriteFd(
|
||||
int rc = 0; /* Value returned by system call */
|
||||
|
||||
assert( nBuf==(nBuf&0x1ffff) );
|
||||
assert( fd>2 );
|
||||
nBuf &= 0x1ffff;
|
||||
TIMER_START;
|
||||
|
||||
@@ -3590,6 +3607,7 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SQLITE_MAX_MMAP_SIZE>0
|
||||
/* If the file was just truncated to a size smaller than the currently
|
||||
** mapped region, reduce the effective mapping size as well. SQLite will
|
||||
** use read() and write() to access data beyond this point from now on.
|
||||
@@ -3597,6 +3615,7 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){
|
||||
if( nByte<pFile->mmapSize ){
|
||||
pFile->mmapSize = nByte;
|
||||
}
|
||||
#endif
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -3686,6 +3705,7 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){
|
||||
}
|
||||
}
|
||||
|
||||
#if SQLITE_MAX_MMAP_SIZE>0
|
||||
if( pFile->mmapSizeMax>0 && nByte>pFile->mmapSize ){
|
||||
int rc;
|
||||
if( pFile->szChunk<=0 ){
|
||||
@@ -3698,6 +3718,7 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){
|
||||
rc = unixMapfile(pFile, nByte);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -3766,6 +3787,11 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
case SQLITE_FCNTL_HAS_MOVED: {
|
||||
*(int*)pArg = fileHasMoved(pFile);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
#if SQLITE_MAX_MMAP_SIZE>0
|
||||
case SQLITE_FCNTL_MMAP_SIZE: {
|
||||
i64 newLimit = *(i64*)pArg;
|
||||
int rc = SQLITE_OK;
|
||||
@@ -3782,6 +3808,7 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* The pager calls this method to signal that it has done
|
||||
** a rollback and that the database is therefore unchanged and
|
||||
@@ -4044,7 +4071,7 @@ static int unixShmSystemLock(
|
||||
#ifdef SQLITE_DEBUG
|
||||
{ u16 mask;
|
||||
OSTRACE(("SHM-LOCK "));
|
||||
mask = (1<<(ofst+n)) - (1<<ofst);
|
||||
mask = ofst>31 ? 0xffff : (1<<(ofst+n)) - (1<<ofst);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( lockType==F_UNLCK ){
|
||||
OSTRACE(("unlock %d ok", ofst));
|
||||
@@ -4592,22 +4619,20 @@ static int unixShmUnmap(
|
||||
# define unixShmUnmap 0
|
||||
#endif /* #ifndef SQLITE_OMIT_WAL */
|
||||
|
||||
#if SQLITE_MAX_MMAP_SIZE>0
|
||||
/*
|
||||
** If it is currently memory mapped, unmap file pFd.
|
||||
*/
|
||||
static void unixUnmapfile(unixFile *pFd){
|
||||
assert( pFd->nFetchOut==0 );
|
||||
#if SQLITE_MAX_MMAP_SIZE>0
|
||||
if( pFd->pMapRegion ){
|
||||
osMunmap(pFd->pMapRegion, pFd->mmapSizeActual);
|
||||
pFd->pMapRegion = 0;
|
||||
pFd->mmapSize = 0;
|
||||
pFd->mmapSizeActual = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if SQLITE_MAX_MMAP_SIZE>0
|
||||
/*
|
||||
** Return the system page size.
|
||||
*/
|
||||
@@ -4620,9 +4645,7 @@ static int unixGetPagesize(void){
|
||||
return (int)sysconf(_SC_PAGESIZE);
|
||||
#endif
|
||||
}
|
||||
#endif /* SQLITE_MAX_MMAP_SIZE>0 */
|
||||
|
||||
#if SQLITE_MAX_MMAP_SIZE>0
|
||||
/*
|
||||
** Attempt to set the size of the memory mapping maintained by file
|
||||
** descriptor pFd to nNew bytes. Any existing mapping is discarded.
|
||||
@@ -4707,7 +4730,6 @@ static void unixRemapfile(
|
||||
pFd->pMapRegion = (void *)pNew;
|
||||
pFd->mmapSize = pFd->mmapSizeActual = nNew;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Memory map or remap the file opened by file-descriptor pFd (if the file
|
||||
@@ -4726,7 +4748,6 @@ static void unixRemapfile(
|
||||
** code otherwise.
|
||||
*/
|
||||
static int unixMapfile(unixFile *pFd, i64 nByte){
|
||||
#if SQLITE_MAX_MMAP_SIZE>0
|
||||
i64 nMap = nByte;
|
||||
int rc;
|
||||
|
||||
@@ -4752,10 +4773,10 @@ static int unixMapfile(unixFile *pFd, i64 nByte){
|
||||
unixUnmapfile(pFd);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
#endif /* SQLITE_MAX_MMAP_SIZE>0 */
|
||||
|
||||
/*
|
||||
** If possible, return a pointer to a mapping of file fd starting at offset
|
||||
@@ -4801,6 +4822,7 @@ static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
|
||||
** may now be invalid and should be unmapped.
|
||||
*/
|
||||
static int unixUnfetch(sqlite3_file *fd, i64 iOff, void *p){
|
||||
#if SQLITE_MAX_MMAP_SIZE>0
|
||||
unixFile *pFd = (unixFile *)fd; /* The underlying database file */
|
||||
UNUSED_PARAMETER(iOff);
|
||||
|
||||
@@ -4819,6 +4841,11 @@ static int unixUnfetch(sqlite3_file *fd, i64 iOff, void *p){
|
||||
}
|
||||
|
||||
assert( pFd->nFetchOut>=0 );
|
||||
#else
|
||||
UNUSED_PARAMETER(fd);
|
||||
UNUSED_PARAMETER(p);
|
||||
UNUSED_PARAMETER(iOff);
|
||||
#endif
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -5150,7 +5177,9 @@ static int fillInUnixFile(
|
||||
pNew->pVfs = pVfs;
|
||||
pNew->zPath = zFilename;
|
||||
pNew->ctrlFlags = (u8)ctrlFlags;
|
||||
#if SQLITE_MAX_MMAP_SIZE>0
|
||||
pNew->mmapSizeMax = sqlite3GlobalConfig.szMmap;
|
||||
#endif
|
||||
if( sqlite3_uri_boolean(((ctrlFlags & UNIXFILE_URI) ? zFilename : 0),
|
||||
"psow", SQLITE_POWERSAFE_OVERWRITE) ){
|
||||
pNew->ctrlFlags |= UNIXFILE_PSOW;
|
||||
@@ -5305,6 +5334,7 @@ static int fillInUnixFile(
|
||||
*/
|
||||
static const char *unixTempFileDir(void){
|
||||
static const char *azDirs[] = {
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
"/var/tmp",
|
||||
@@ -5317,7 +5347,8 @@ static const char *unixTempFileDir(void){
|
||||
const char *zDir = 0;
|
||||
|
||||
azDirs[0] = sqlite3_temp_directory;
|
||||
if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR");
|
||||
if( !azDirs[1] ) azDirs[1] = getenv("SQLITE_TMPDIR");
|
||||
if( !azDirs[2] ) azDirs[2] = getenv("TMPDIR");
|
||||
for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){
|
||||
if( zDir==0 ) continue;
|
||||
if( osStat(zDir, &buf) ) continue;
|
||||
@@ -5604,6 +5635,16 @@ static int unixOpen(
|
||||
|| eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL
|
||||
);
|
||||
|
||||
/* Detect a pid change and reset the PRNG. There is a race condition
|
||||
** here such that two or more threads all trying to open databases at
|
||||
** the same instant might all reset the PRNG. But multiple resets
|
||||
** are harmless.
|
||||
*/
|
||||
if( randomnessPid!=getpid() ){
|
||||
randomnessPid = getpid();
|
||||
sqlite3_randomness(0,0);
|
||||
}
|
||||
|
||||
memset(p, 0, sizeof(unixFile));
|
||||
|
||||
if( eType==SQLITE_OPEN_MAIN_DB ){
|
||||
@@ -5991,18 +6032,18 @@ static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){
|
||||
** tests repeatable.
|
||||
*/
|
||||
memset(zBuf, 0, nBuf);
|
||||
randomnessPid = getpid();
|
||||
#if !defined(SQLITE_TEST)
|
||||
{
|
||||
int pid, fd, got;
|
||||
int fd, got;
|
||||
fd = robust_open("/dev/urandom", O_RDONLY, 0);
|
||||
if( fd<0 ){
|
||||
time_t t;
|
||||
time(&t);
|
||||
memcpy(zBuf, &t, sizeof(t));
|
||||
pid = getpid();
|
||||
memcpy(&zBuf[sizeof(t)], &pid, sizeof(pid));
|
||||
assert( sizeof(t)+sizeof(pid)<=(size_t)nBuf );
|
||||
nBuf = sizeof(t) + sizeof(pid);
|
||||
memcpy(&zBuf[sizeof(t)], &randomnessPid, sizeof(randomnessPid));
|
||||
assert( sizeof(t)+sizeof(randomnessPid)<=(size_t)nBuf );
|
||||
nBuf = sizeof(t) + sizeof(randomnessPid);
|
||||
}else{
|
||||
do{ got = osRead(fd, zBuf, nBuf); }while( got<0 && errno==EINTR );
|
||||
robust_close(0, fd, __LINE__);
|
||||
|
||||
+802
-301
File diff suppressed because it is too large
Load Diff
+105
-70
@@ -1024,15 +1024,12 @@ static char *print_pager_state(Pager *p){
|
||||
static int subjRequiresPage(PgHdr *pPg){
|
||||
Pager *pPager = pPg->pPager;
|
||||
PagerSavepoint *p;
|
||||
Pgno pgno;
|
||||
Pgno pgno = pPg->pgno;
|
||||
int i;
|
||||
if( pPager->nSavepoint ){
|
||||
pgno = pPg->pgno;
|
||||
for(i=0; i<pPager->nSavepoint; i++){
|
||||
p = &pPager->aSavepoint[i];
|
||||
if( p->nOrig>=pgno && 0==sqlite3BitvecTest(p->pInSavepoint, pgno) ){
|
||||
return 1;
|
||||
}
|
||||
for(i=0; i<pPager->nSavepoint; i++){
|
||||
p = &pPager->aSavepoint[i];
|
||||
if( p->nOrig>=pgno && 0==sqlite3BitvecTest(p->pInSavepoint, pgno) ){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -1041,8 +1038,8 @@ static int subjRequiresPage(PgHdr *pPg){
|
||||
/*
|
||||
** Return true if the page is already in the journal file.
|
||||
*/
|
||||
static int pageInJournal(PgHdr *pPg){
|
||||
return sqlite3BitvecTest(pPg->pPager->pInJournal, pPg->pgno);
|
||||
static int pageInJournal(Pager *pPager, PgHdr *pPg){
|
||||
return sqlite3BitvecTest(pPager->pInJournal, pPg->pgno);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1249,6 +1246,7 @@ static int readMasterJournal(sqlite3_file *pJrnl, char *zMaster, u32 nMaster){
|
||||
|| szJ<16
|
||||
|| SQLITE_OK!=(rc = read32bits(pJrnl, szJ-16, &len))
|
||||
|| len>=nMaster
|
||||
|| len==0
|
||||
|| SQLITE_OK!=(rc = read32bits(pJrnl, szJ-12, &cksum))
|
||||
|| SQLITE_OK!=(rc = sqlite3OsRead(pJrnl, aMagic, 8, szJ-8))
|
||||
|| memcmp(aMagic, aJournalMagic, 8)
|
||||
@@ -1685,7 +1683,7 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){
|
||||
** already in memory.
|
||||
*/
|
||||
static PgHdr *pager_lookup(Pager *pPager, Pgno pgno){
|
||||
PgHdr *p; /* Return value */
|
||||
PgHdr *p = 0; /* Return value */
|
||||
|
||||
/* It is not possible for a call to PcacheFetch() with createFlag==0 to
|
||||
** fail, since no attempt to allocate dynamic memory will be made.
|
||||
@@ -1989,7 +1987,7 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
|
||||
PgHdr *p = pager_lookup(pPager, 1);
|
||||
if( p ){
|
||||
p->pageHash = 0;
|
||||
sqlite3PagerUnref(p);
|
||||
sqlite3PagerUnrefNotNull(p);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -2018,6 +2016,11 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
|
||||
rc = pager_truncate(pPager, pPager->dbSize);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && bCommit && isOpen(pPager->fd) ){
|
||||
rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_COMMIT_PHASETWO, 0);
|
||||
if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
if( !pPager->exclusiveMode
|
||||
&& (!pagerUseWal(pPager) || sqlite3WalExclusiveMode(pPager->pWal, 0))
|
||||
){
|
||||
@@ -2831,7 +2834,7 @@ end_playback:
|
||||
if( rc==SQLITE_OK
|
||||
&& (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
|
||||
){
|
||||
rc = sqlite3PagerSync(pPager);
|
||||
rc = sqlite3PagerSync(pPager, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pager_end_transaction(pPager, zMaster[0]!='\0', 0);
|
||||
@@ -2977,7 +2980,7 @@ static int pagerUndoCallback(void *pCtx, Pgno iPg){
|
||||
if( rc==SQLITE_OK ){
|
||||
pPager->xReiniter(pPg);
|
||||
}
|
||||
sqlite3PagerUnref(pPg);
|
||||
sqlite3PagerUnrefNotNull(pPg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4332,7 +4335,7 @@ static int subjournalPage(PgHdr *pPg){
|
||||
assert( isOpen(pPager->jfd) || pagerUseWal(pPager) );
|
||||
assert( isOpen(pPager->sjfd) || pPager->nSubRec==0 );
|
||||
assert( pagerUseWal(pPager)
|
||||
|| pageInJournal(pPg)
|
||||
|| pageInJournal(pPager, pPg)
|
||||
|| pPg->pgno>pPager->dbOrigSize
|
||||
);
|
||||
rc = openSubJournal(pPager);
|
||||
@@ -4797,6 +4800,30 @@ int sqlite3PagerOpen(
|
||||
}
|
||||
|
||||
|
||||
/* Verify that the database file has not be deleted or renamed out from
|
||||
** under the pager. Return SQLITE_OK if the database is still were it ought
|
||||
** to be on disk. Return non-zero (SQLITE_READONLY_DBMOVED or some other error
|
||||
** code from sqlite3OsAccess()) if the database has gone missing.
|
||||
*/
|
||||
static int databaseIsUnmoved(Pager *pPager){
|
||||
int bHasMoved = 0;
|
||||
int rc;
|
||||
|
||||
if( pPager->tempFile ) return SQLITE_OK;
|
||||
if( pPager->dbSize==0 ) return SQLITE_OK;
|
||||
assert( pPager->zFilename && pPager->zFilename[0] );
|
||||
rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_HAS_MOVED, &bHasMoved);
|
||||
if( rc==SQLITE_NOTFOUND ){
|
||||
/* If the HAS_MOVED file-control is unimplemented, assume that the file
|
||||
** has not been moved. That is the historical behavior of SQLite: prior to
|
||||
** version 3.8.3, it never checked */
|
||||
rc = SQLITE_OK;
|
||||
}else if( rc==SQLITE_OK && bHasMoved ){
|
||||
rc = SQLITE_READONLY_DBMOVED;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This function is called after transitioning from PAGER_UNLOCK to
|
||||
@@ -4862,15 +4889,17 @@ static int hasHotJournal(Pager *pPager, int *pExists){
|
||||
if( rc==SQLITE_OK && !locked ){
|
||||
Pgno nPage; /* Number of pages in database file */
|
||||
|
||||
/* Check the size of the database file. If it consists of 0 pages,
|
||||
** then delete the journal file. See the header comment above for
|
||||
** the reasoning here. Delete the obsolete journal file under
|
||||
** a RESERVED lock to avoid race conditions and to avoid violating
|
||||
** [H33020].
|
||||
*/
|
||||
rc = pagerPagecount(pPager, &nPage);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( nPage==0 ){
|
||||
/* If the database is zero pages in size, that means that either (1) the
|
||||
** journal is a remnant from a prior database with the same name where
|
||||
** the database file but not the journal was deleted, or (2) the initial
|
||||
** transaction that populates a new database is being rolled back.
|
||||
** In either case, the journal file can be deleted. However, take care
|
||||
** not to delete the journal file if it is already open due to
|
||||
** journal_mode=PERSIST.
|
||||
*/
|
||||
if( nPage==0 && !jrnlOpen ){
|
||||
sqlite3BeginBenignMalloc();
|
||||
if( pagerLockDb(pPager, RESERVED_LOCK)==SQLITE_OK ){
|
||||
sqlite3OsDelete(pVfs, pPager->zJournal, 0);
|
||||
@@ -5268,7 +5297,7 @@ int sqlite3PagerAcquire(
|
||||
if( rc!=SQLITE_OK ) goto pager_acquire_err;
|
||||
}
|
||||
|
||||
if( iFrame==0 && bMmapOk ){
|
||||
if( bMmapOk && iFrame==0 ){
|
||||
void *pData = 0;
|
||||
|
||||
rc = sqlite3OsFetch(pPager->fd,
|
||||
@@ -5409,16 +5438,19 @@ DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){
|
||||
** are released, a rollback occurs and the lock on the database is
|
||||
** removed.
|
||||
*/
|
||||
void sqlite3PagerUnref(DbPage *pPg){
|
||||
if( pPg ){
|
||||
Pager *pPager = pPg->pPager;
|
||||
if( pPg->flags & PGHDR_MMAP ){
|
||||
pagerReleaseMapPage(pPg);
|
||||
}else{
|
||||
sqlite3PcacheRelease(pPg);
|
||||
}
|
||||
pagerUnlockIfUnused(pPager);
|
||||
void sqlite3PagerUnrefNotNull(DbPage *pPg){
|
||||
Pager *pPager;
|
||||
assert( pPg!=0 );
|
||||
pPager = pPg->pPager;
|
||||
if( pPg->flags & PGHDR_MMAP ){
|
||||
pagerReleaseMapPage(pPg);
|
||||
}else{
|
||||
sqlite3PcacheRelease(pPg);
|
||||
}
|
||||
pagerUnlockIfUnused(pPager);
|
||||
}
|
||||
void sqlite3PagerUnref(DbPage *pPg){
|
||||
if( pPg ) sqlite3PagerUnrefNotNull(pPg);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -5473,13 +5505,19 @@ static int pager_open_journal(Pager *pPager){
|
||||
(SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL):
|
||||
(SQLITE_OPEN_MAIN_JOURNAL)
|
||||
);
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
rc = sqlite3JournalOpen(
|
||||
pVfs, pPager->zJournal, pPager->jfd, flags, jrnlBufferSize(pPager)
|
||||
);
|
||||
#else
|
||||
rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0);
|
||||
#endif
|
||||
|
||||
/* Verify that the database still has the same name as it did when
|
||||
** it was originally opened. */
|
||||
rc = databaseIsUnmoved(pPager);
|
||||
if( rc==SQLITE_OK ){
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
rc = sqlite3JournalOpen(
|
||||
pVfs, pPager->zJournal, pPager->jfd, flags, jrnlBufferSize(pPager)
|
||||
);
|
||||
#else
|
||||
rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
assert( rc!=SQLITE_OK || isOpen(pPager->jfd) );
|
||||
}
|
||||
@@ -5600,9 +5638,9 @@ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){
|
||||
** of any open savepoints as appropriate.
|
||||
*/
|
||||
static int pager_write(PgHdr *pPg){
|
||||
void *pData = pPg->pData;
|
||||
Pager *pPager = pPg->pPager;
|
||||
int rc = SQLITE_OK;
|
||||
int inJournal;
|
||||
|
||||
/* This routine is not called unless a write-transaction has already
|
||||
** been started. The journal file may or may not be open at this point.
|
||||
@@ -5613,14 +5651,8 @@ static int pager_write(PgHdr *pPg){
|
||||
|| pPager->eState==PAGER_WRITER_DBMOD
|
||||
);
|
||||
assert( assert_pager_state(pPager) );
|
||||
|
||||
/* If an error has been previously detected, report the same error
|
||||
** again. This should not happen, but the check provides robustness. */
|
||||
if( NEVER(pPager->errCode) ) return pPager->errCode;
|
||||
|
||||
/* Higher-level routines never call this function if database is not
|
||||
** writable. But check anyway, just for robustness. */
|
||||
if( NEVER(pPager->readOnly) ) return SQLITE_PERM;
|
||||
assert( pPager->errCode==0 );
|
||||
assert( pPager->readOnly==0 );
|
||||
|
||||
CHECK_PAGE(pPg);
|
||||
|
||||
@@ -5644,7 +5676,8 @@ static int pager_write(PgHdr *pPg){
|
||||
** to the journal then we can return right away.
|
||||
*/
|
||||
sqlite3PcacheMakeDirty(pPg);
|
||||
if( pageInJournal(pPg) && !subjRequiresPage(pPg) ){
|
||||
inJournal = pageInJournal(pPager, pPg);
|
||||
if( inJournal && (pPager->nSavepoint==0 || !subjRequiresPage(pPg)) ){
|
||||
assert( !pagerUseWal(pPager) );
|
||||
}else{
|
||||
|
||||
@@ -5652,7 +5685,7 @@ static int pager_write(PgHdr *pPg){
|
||||
** EXCLUSIVE lock on the main database file. Write the current page to
|
||||
** the transaction journal if it is not there already.
|
||||
*/
|
||||
if( !pageInJournal(pPg) && !pagerUseWal(pPager) ){
|
||||
if( !inJournal && !pagerUseWal(pPager) ){
|
||||
assert( pagerUseWal(pPager)==0 );
|
||||
if( pPg->pgno<=pPager->dbOrigSize && isOpen(pPager->jfd) ){
|
||||
u32 cksum;
|
||||
@@ -5665,7 +5698,7 @@ static int pager_write(PgHdr *pPg){
|
||||
assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) );
|
||||
|
||||
assert( pPager->journalHdr<=pPager->journalOff );
|
||||
CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2);
|
||||
CODEC2(pPager, pPg->pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2);
|
||||
cksum = pager_cksum(pPager, (u8*)pData2);
|
||||
|
||||
/* Even if an IO or diskfull error occurs while journalling the
|
||||
@@ -5717,7 +5750,7 @@ static int pager_write(PgHdr *pPg){
|
||||
** the statement journal format differs from the standard journal format
|
||||
** in that it omits the checksums and the header.
|
||||
*/
|
||||
if( subjRequiresPage(pPg) ){
|
||||
if( pPager->nSavepoint>0 && subjRequiresPage(pPg) ){
|
||||
rc = subjournalPage(pPg);
|
||||
}
|
||||
}
|
||||
@@ -5749,19 +5782,19 @@ int sqlite3PagerWrite(DbPage *pDbPage){
|
||||
|
||||
PgHdr *pPg = pDbPage;
|
||||
Pager *pPager = pPg->pPager;
|
||||
Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize);
|
||||
|
||||
assert( (pPg->flags & PGHDR_MMAP)==0 );
|
||||
assert( pPager->eState>=PAGER_WRITER_LOCKED );
|
||||
assert( pPager->eState!=PAGER_ERROR );
|
||||
assert( assert_pager_state(pPager) );
|
||||
|
||||
if( nPagePerSector>1 ){
|
||||
if( pPager->sectorSize > (u32)pPager->pageSize ){
|
||||
Pgno nPageCount; /* Total number of pages in database file */
|
||||
Pgno pg1; /* First page of the sector pPg is located on. */
|
||||
int nPage = 0; /* Number of pages starting at pg1 to journal */
|
||||
int ii; /* Loop counter */
|
||||
int needSync = 0; /* True if any page has PGHDR_NEED_SYNC */
|
||||
Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize);
|
||||
|
||||
/* Set the doNotSpill NOSYNC bit to 1. This is because we cannot allow
|
||||
** a journal header to be written between the pages journaled by
|
||||
@@ -5800,14 +5833,14 @@ int sqlite3PagerWrite(DbPage *pDbPage){
|
||||
if( pPage->flags&PGHDR_NEED_SYNC ){
|
||||
needSync = 1;
|
||||
}
|
||||
sqlite3PagerUnref(pPage);
|
||||
sqlite3PagerUnrefNotNull(pPage);
|
||||
}
|
||||
}
|
||||
}else if( (pPage = pager_lookup(pPager, pg))!=0 ){
|
||||
if( pPage->flags&PGHDR_NEED_SYNC ){
|
||||
needSync = 1;
|
||||
}
|
||||
sqlite3PagerUnref(pPage);
|
||||
sqlite3PagerUnrefNotNull(pPage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5823,7 +5856,7 @@ int sqlite3PagerWrite(DbPage *pDbPage){
|
||||
PgHdr *pPage = pager_lookup(pPager, pg1+ii);
|
||||
if( pPage ){
|
||||
pPage->flags |= PGHDR_NEED_SYNC;
|
||||
sqlite3PagerUnref(pPage);
|
||||
sqlite3PagerUnrefNotNull(pPage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5976,17 +6009,17 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
|
||||
** If successful, or if called on a pager for which it is a no-op, this
|
||||
** function returns SQLITE_OK. Otherwise, an IO error code is returned.
|
||||
*/
|
||||
int sqlite3PagerSync(Pager *pPager){
|
||||
int sqlite3PagerSync(Pager *pPager, const char *zMaster){
|
||||
int rc = SQLITE_OK;
|
||||
if( !pPager->noSync ){
|
||||
|
||||
if( isOpen(pPager->fd) ){
|
||||
void *pArg = (void*)zMaster;
|
||||
rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC, pArg);
|
||||
if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
|
||||
}
|
||||
if( rc==SQLITE_OK && !pPager->noSync ){
|
||||
assert( !MEMDB );
|
||||
rc = sqlite3OsSync(pPager->fd, pPager->syncFlags);
|
||||
}else if( isOpen(pPager->fd) ){
|
||||
assert( !MEMDB );
|
||||
rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC_OMITTED, 0);
|
||||
if( rc==SQLITE_NOTFOUND ){
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -6185,7 +6218,7 @@ int sqlite3PagerCommitPhaseOne(
|
||||
|
||||
/* Finally, sync the database file. */
|
||||
if( !noSync ){
|
||||
rc = sqlite3PagerSync(pPager);
|
||||
rc = sqlite3PagerSync(pPager, zMaster);
|
||||
}
|
||||
IOTRACE(("DBSYNC %p\n", pPager))
|
||||
}
|
||||
@@ -6314,7 +6347,9 @@ int sqlite3PagerRollback(Pager *pPager){
|
||||
|
||||
assert( pPager->eState==PAGER_READER || rc!=SQLITE_OK );
|
||||
assert( rc==SQLITE_OK || rc==SQLITE_FULL || rc==SQLITE_CORRUPT
|
||||
|| rc==SQLITE_NOMEM || (rc&0xFF)==SQLITE_IOERR );
|
||||
|| rc==SQLITE_NOMEM || (rc&0xFF)==SQLITE_IOERR
|
||||
|| rc==SQLITE_CANTOPEN
|
||||
);
|
||||
|
||||
/* If an error occurs during a ROLLBACK, we can no longer trust the pager
|
||||
** cache. So call pager_error() on the way out to make any error persistent.
|
||||
@@ -6717,7 +6752,7 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
|
||||
if( (pPg->flags&PGHDR_NEED_SYNC) && !isCommit ){
|
||||
needSyncPgno = pPg->pgno;
|
||||
assert( pPager->journalMode==PAGER_JOURNALMODE_OFF ||
|
||||
pageInJournal(pPg) || pPg->pgno>pPager->dbOrigSize );
|
||||
pageInJournal(pPager, pPg) || pPg->pgno>pPager->dbOrigSize );
|
||||
assert( pPg->flags&PGHDR_DIRTY );
|
||||
}
|
||||
|
||||
@@ -6751,7 +6786,7 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
|
||||
if( MEMDB ){
|
||||
assert( pPgOld );
|
||||
sqlite3PcacheMove(pPgOld, origPgno);
|
||||
sqlite3PagerUnref(pPgOld);
|
||||
sqlite3PagerUnrefNotNull(pPgOld);
|
||||
}
|
||||
|
||||
if( needSyncPgno ){
|
||||
@@ -6780,7 +6815,7 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
|
||||
}
|
||||
pPgHdr->flags |= PGHDR_NEED_SYNC;
|
||||
sqlite3PcacheMakeDirty(pPgHdr);
|
||||
sqlite3PagerUnref(pPgHdr);
|
||||
sqlite3PagerUnrefNotNull(pPgHdr);
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
|
||||
+2
-1
@@ -136,6 +136,7 @@ int sqlite3PagerAcquire(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag);
|
||||
DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno);
|
||||
void sqlite3PagerRef(DbPage*);
|
||||
void sqlite3PagerUnref(DbPage*);
|
||||
void sqlite3PagerUnrefNotNull(DbPage*);
|
||||
|
||||
/* Operations on page references. */
|
||||
int sqlite3PagerWrite(DbPage*);
|
||||
@@ -150,7 +151,7 @@ void sqlite3PagerPagecount(Pager*, int*);
|
||||
int sqlite3PagerBegin(Pager*, int exFlag, int);
|
||||
int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int);
|
||||
int sqlite3PagerExclusiveLock(Pager*);
|
||||
int sqlite3PagerSync(Pager *pPager);
|
||||
int sqlite3PagerSync(Pager *pPager, const char *zMaster);
|
||||
int sqlite3PagerCommitPhaseTwo(Pager*);
|
||||
int sqlite3PagerRollback(Pager*);
|
||||
int sqlite3PagerOpenSavepoint(Pager *pPager, int n);
|
||||
|
||||
+143
-120
@@ -94,14 +94,6 @@ struct TrigEvent { int a; IdList * b; };
|
||||
*/
|
||||
struct AttachKey { int type; Token key; };
|
||||
|
||||
/*
|
||||
** One or more VALUES claues
|
||||
*/
|
||||
struct ValueList {
|
||||
ExprList *pList;
|
||||
Select *pSelect;
|
||||
};
|
||||
|
||||
} // end %include
|
||||
|
||||
// Input is a single SQL command
|
||||
@@ -163,13 +155,23 @@ ifnotexists(A) ::= IF NOT EXISTS. {A = 1;}
|
||||
temp(A) ::= TEMP. {A = 1;}
|
||||
%endif SQLITE_OMIT_TEMPDB
|
||||
temp(A) ::= . {A = 0;}
|
||||
create_table_args ::= LP columnlist conslist_opt(X) RP(Y). {
|
||||
sqlite3EndTable(pParse,&X,&Y,0);
|
||||
create_table_args ::= LP columnlist conslist_opt(X) RP(E) table_options(F). {
|
||||
sqlite3EndTable(pParse,&X,&E,F,0);
|
||||
}
|
||||
create_table_args ::= AS select(S). {
|
||||
sqlite3EndTable(pParse,0,0,S);
|
||||
sqlite3EndTable(pParse,0,0,0,S);
|
||||
sqlite3SelectDelete(pParse->db, S);
|
||||
}
|
||||
%type table_options {u8}
|
||||
table_options(A) ::= . {A = 0;}
|
||||
table_options(A) ::= WITHOUT nm(X). {
|
||||
if( X.n==5 && sqlite3_strnicmp(X.z,"rowid",5)==0 ){
|
||||
A = TF_WithoutRowid;
|
||||
}else{
|
||||
A = 0;
|
||||
sqlite3ErrorMsg(pParse, "unknown table option: %.*s", X.n, X.z);
|
||||
}
|
||||
}
|
||||
columnlist ::= columnlist COMMA column.
|
||||
columnlist ::= column.
|
||||
|
||||
@@ -192,9 +194,7 @@ columnid(A) ::= nm(X). {
|
||||
// An IDENTIFIER can be a generic identifier, or one of several
|
||||
// keywords. Any non-standard keyword can also be an identifier.
|
||||
//
|
||||
%type id {Token}
|
||||
id(A) ::= ID(X). {A = X;}
|
||||
id(A) ::= INDEXED(X). {A = X;}
|
||||
%token_class id ID|INDEXED.
|
||||
|
||||
// The following directive causes tokens ABORT, AFTER, ASC, etc. to
|
||||
// fallback to ID if they will not parse as their original value.
|
||||
@@ -204,8 +204,8 @@ id(A) ::= INDEXED(X). {A = X;}
|
||||
ABORT ACTION AFTER ANALYZE ASC ATTACH BEFORE BEGIN BY CASCADE CAST COLUMNKW
|
||||
CONFLICT DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL FOR
|
||||
IGNORE IMMEDIATE INITIALLY INSTEAD LIKE_KW MATCH NO PLAN
|
||||
QUERY KEY OF OFFSET PRAGMA RAISE RELEASE REPLACE RESTRICT ROW ROLLBACK
|
||||
SAVEPOINT TEMP TRIGGER VACUUM VIEW VIRTUAL
|
||||
QUERY KEY OF OFFSET PRAGMA RAISE RECURSIVE RELEASE REPLACE RESTRICT ROW
|
||||
ROLLBACK SAVEPOINT TEMP TRIGGER VACUUM VIEW VIRTUAL WITH WITHOUT
|
||||
%ifdef SQLITE_OMIT_COMPOUND_SELECT
|
||||
EXCEPT INTERSECT UNION
|
||||
%endif SQLITE_OMIT_COMPOUND_SELECT
|
||||
@@ -239,8 +239,7 @@ id(A) ::= INDEXED(X). {A = X;}
|
||||
|
||||
// And "ids" is an identifer-or-string.
|
||||
//
|
||||
%type ids {Token}
|
||||
ids(A) ::= ID|STRING(X). {A = X;}
|
||||
%token_class ids ID|STRING.
|
||||
|
||||
// The name of a column or table can be any of the following:
|
||||
//
|
||||
@@ -398,7 +397,7 @@ cmd ::= DROP VIEW ifexists(E) fullname(X). {
|
||||
//////////////////////// The SELECT statement /////////////////////////////////
|
||||
//
|
||||
cmd ::= select(X). {
|
||||
SelectDest dest = {SRT_Output, 0, 0, 0, 0};
|
||||
SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0};
|
||||
sqlite3Select(pParse, X, &dest);
|
||||
sqlite3ExplainBegin(pParse->pVdbe);
|
||||
sqlite3ExplainSelect(pParse->pVdbe, X);
|
||||
@@ -408,20 +407,52 @@ cmd ::= select(X). {
|
||||
|
||||
%type select {Select*}
|
||||
%destructor select {sqlite3SelectDelete(pParse->db, $$);}
|
||||
%type selectnowith {Select*}
|
||||
%destructor selectnowith {sqlite3SelectDelete(pParse->db, $$);}
|
||||
%type oneselect {Select*}
|
||||
%destructor oneselect {sqlite3SelectDelete(pParse->db, $$);}
|
||||
|
||||
select(A) ::= oneselect(X). {A = X;}
|
||||
select(A) ::= with(W) selectnowith(X). {
|
||||
Select *p = X, *pNext, *pLoop;
|
||||
if( p ){
|
||||
int cnt = 0, mxSelect;
|
||||
p->pWith = W;
|
||||
if( p->pPrior ){
|
||||
pNext = 0;
|
||||
for(pLoop=p; pLoop; pNext=pLoop, pLoop=pLoop->pPrior, cnt++){
|
||||
pLoop->pNext = pNext;
|
||||
pLoop->selFlags |= SF_Compound;
|
||||
}
|
||||
mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT];
|
||||
if( mxSelect && cnt>mxSelect ){
|
||||
sqlite3ErrorMsg(pParse, "too many terms in compound SELECT");
|
||||
}
|
||||
}
|
||||
}else{
|
||||
sqlite3WithDelete(pParse->db, W);
|
||||
}
|
||||
A = p;
|
||||
}
|
||||
|
||||
selectnowith(A) ::= oneselect(X). {A = X;}
|
||||
%ifndef SQLITE_OMIT_COMPOUND_SELECT
|
||||
select(A) ::= select(X) multiselect_op(Y) oneselect(Z). {
|
||||
if( Z ){
|
||||
Z->op = (u8)Y;
|
||||
Z->pPrior = X;
|
||||
selectnowith(A) ::= selectnowith(X) multiselect_op(Y) oneselect(Z). {
|
||||
Select *pRhs = Z;
|
||||
if( pRhs && pRhs->pPrior ){
|
||||
SrcList *pFrom;
|
||||
Token x;
|
||||
x.n = 0;
|
||||
pFrom = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&x,pRhs,0,0);
|
||||
pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0,0);
|
||||
}
|
||||
if( pRhs ){
|
||||
pRhs->op = (u8)Y;
|
||||
pRhs->pPrior = X;
|
||||
if( Y!=TK_ALL ) pParse->hasCompound = 1;
|
||||
}else{
|
||||
sqlite3SelectDelete(pParse->db, X);
|
||||
}
|
||||
A = Z;
|
||||
A = pRhs;
|
||||
}
|
||||
%type multiselect_op {int}
|
||||
multiselect_op(A) ::= UNION(OP). {A = @OP;}
|
||||
@@ -432,6 +463,23 @@ oneselect(A) ::= SELECT distinct(D) selcollist(W) from(X) where_opt(Y)
|
||||
groupby_opt(P) having_opt(Q) orderby_opt(Z) limit_opt(L). {
|
||||
A = sqlite3SelectNew(pParse,W,X,Y,P,Q,Z,D,L.pLimit,L.pOffset);
|
||||
}
|
||||
oneselect(A) ::= values(X). {A = X;}
|
||||
|
||||
%type values {Select*}
|
||||
%destructor values {sqlite3SelectDelete(pParse->db, $$);}
|
||||
values(A) ::= VALUES LP nexprlist(X) RP. {
|
||||
A = sqlite3SelectNew(pParse,X,0,0,0,0,0,SF_Values,0,0);
|
||||
}
|
||||
values(A) ::= values(X) COMMA LP exprlist(Y) RP. {
|
||||
Select *pRight = sqlite3SelectNew(pParse,Y,0,0,0,0,0,SF_Values,0,0);
|
||||
if( pRight ){
|
||||
pRight->op = TK_ALL;
|
||||
pRight->pPrior = X;
|
||||
A = pRight;
|
||||
}else{
|
||||
A = X;
|
||||
}
|
||||
}
|
||||
|
||||
// The "distinct" nonterminal is true (1) if the DISTINCT keyword is
|
||||
// present and false (0) if it is not.
|
||||
@@ -573,7 +621,7 @@ indexed_opt(A) ::= NOT INDEXED. {A.z=0; A.n=1;}
|
||||
|
||||
%type using_opt {IdList*}
|
||||
%destructor using_opt {sqlite3IdListDelete(pParse->db, $$);}
|
||||
using_opt(U) ::= USING LP inscollist(L) RP. {U = L;}
|
||||
using_opt(U) ::= USING LP idlist(L) RP. {U = L;}
|
||||
using_opt(U) ::= . {U = 0;}
|
||||
|
||||
|
||||
@@ -632,15 +680,17 @@ limit_opt(A) ::= LIMIT expr(X) COMMA expr(Y).
|
||||
/////////////////////////// The DELETE statement /////////////////////////////
|
||||
//
|
||||
%ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
||||
cmd ::= DELETE FROM fullname(X) indexed_opt(I) where_opt(W)
|
||||
cmd ::= with(C) DELETE FROM fullname(X) indexed_opt(I) where_opt(W)
|
||||
orderby_opt(O) limit_opt(L). {
|
||||
sqlite3WithPush(pParse, C, 1);
|
||||
sqlite3SrcListIndexedBy(pParse, X, &I);
|
||||
W = sqlite3LimitWhere(pParse, X, W, O, L.pLimit, L.pOffset, "DELETE");
|
||||
sqlite3DeleteFrom(pParse,X,W);
|
||||
}
|
||||
%endif
|
||||
%ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
||||
cmd ::= DELETE FROM fullname(X) indexed_opt(I) where_opt(W). {
|
||||
cmd ::= with(C) DELETE FROM fullname(X) indexed_opt(I) where_opt(W). {
|
||||
sqlite3WithPush(pParse, C, 1);
|
||||
sqlite3SrcListIndexedBy(pParse, X, &I);
|
||||
sqlite3DeleteFrom(pParse,X,W);
|
||||
}
|
||||
@@ -655,8 +705,9 @@ where_opt(A) ::= WHERE expr(X). {A = X.pExpr;}
|
||||
////////////////////////// The UPDATE command ////////////////////////////////
|
||||
//
|
||||
%ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
||||
cmd ::= UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) where_opt(W)
|
||||
orderby_opt(O) limit_opt(L). {
|
||||
cmd ::= with(C) UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y)
|
||||
where_opt(W) orderby_opt(O) limit_opt(L). {
|
||||
sqlite3WithPush(pParse, C, 1);
|
||||
sqlite3SrcListIndexedBy(pParse, X, &I);
|
||||
sqlite3ExprListCheckLength(pParse,Y,"set list");
|
||||
W = sqlite3LimitWhere(pParse, X, W, O, L.pLimit, L.pOffset, "UPDATE");
|
||||
@@ -664,8 +715,9 @@ cmd ::= UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) where_opt(W)
|
||||
}
|
||||
%endif
|
||||
%ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
||||
cmd ::= UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y)
|
||||
cmd ::= with(C) UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y)
|
||||
where_opt(W). {
|
||||
sqlite3WithPush(pParse, C, 1);
|
||||
sqlite3SrcListIndexedBy(pParse, X, &I);
|
||||
sqlite3ExprListCheckLength(pParse,Y,"set list");
|
||||
sqlite3Update(pParse,X,Y,W,R);
|
||||
@@ -686,68 +738,30 @@ setlist(A) ::= nm(X) EQ expr(Y). {
|
||||
|
||||
////////////////////////// The INSERT command /////////////////////////////////
|
||||
//
|
||||
cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F) valuelist(Y).
|
||||
{sqlite3Insert(pParse, X, Y.pList, Y.pSelect, F, R);}
|
||||
cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F) select(S).
|
||||
{sqlite3Insert(pParse, X, 0, S, F, R);}
|
||||
cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F) DEFAULT VALUES.
|
||||
{sqlite3Insert(pParse, X, 0, 0, F, R);}
|
||||
cmd ::= with(W) insert_cmd(R) INTO fullname(X) inscollist_opt(F) select(S). {
|
||||
sqlite3WithPush(pParse, W, 1);
|
||||
sqlite3Insert(pParse, X, S, F, R);
|
||||
}
|
||||
cmd ::= with(W) insert_cmd(R) INTO fullname(X) inscollist_opt(F) DEFAULT VALUES.
|
||||
{
|
||||
sqlite3WithPush(pParse, W, 1);
|
||||
sqlite3Insert(pParse, X, 0, F, R);
|
||||
}
|
||||
|
||||
%type insert_cmd {u8}
|
||||
insert_cmd(A) ::= INSERT orconf(R). {A = R;}
|
||||
insert_cmd(A) ::= REPLACE. {A = OE_Replace;}
|
||||
|
||||
// A ValueList is either a single VALUES clause or a comma-separated list
|
||||
// of VALUES clauses. If it is a single VALUES clause then the
|
||||
// ValueList.pList field points to the expression list of that clause.
|
||||
// If it is a list of VALUES clauses, then those clauses are transformed
|
||||
// into a set of SELECT statements without FROM clauses and connected by
|
||||
// UNION ALL and the ValueList.pSelect points to the right-most SELECT in
|
||||
// that compound.
|
||||
%type valuelist {struct ValueList}
|
||||
%destructor valuelist {
|
||||
sqlite3ExprListDelete(pParse->db, $$.pList);
|
||||
sqlite3SelectDelete(pParse->db, $$.pSelect);
|
||||
}
|
||||
valuelist(A) ::= VALUES LP nexprlist(X) RP. {
|
||||
A.pList = X;
|
||||
A.pSelect = 0;
|
||||
}
|
||||
|
||||
// Since a list of VALUEs is inplemented as a compound SELECT, we have
|
||||
// to disable the value list option if compound SELECTs are disabled.
|
||||
%ifndef SQLITE_OMIT_COMPOUND_SELECT
|
||||
valuelist(A) ::= valuelist(X) COMMA LP exprlist(Y) RP. {
|
||||
Select *pRight = sqlite3SelectNew(pParse, Y, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
if( X.pList ){
|
||||
X.pSelect = sqlite3SelectNew(pParse, X.pList, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
X.pList = 0;
|
||||
}
|
||||
A.pList = 0;
|
||||
if( X.pSelect==0 || pRight==0 ){
|
||||
sqlite3SelectDelete(pParse->db, pRight);
|
||||
sqlite3SelectDelete(pParse->db, X.pSelect);
|
||||
A.pSelect = 0;
|
||||
}else{
|
||||
pRight->op = TK_ALL;
|
||||
pRight->pPrior = X.pSelect;
|
||||
pRight->selFlags |= SF_Values;
|
||||
pRight->pPrior->selFlags |= SF_Values;
|
||||
A.pSelect = pRight;
|
||||
}
|
||||
}
|
||||
%endif SQLITE_OMIT_COMPOUND_SELECT
|
||||
|
||||
%type inscollist_opt {IdList*}
|
||||
%destructor inscollist_opt {sqlite3IdListDelete(pParse->db, $$);}
|
||||
%type inscollist {IdList*}
|
||||
%destructor inscollist {sqlite3IdListDelete(pParse->db, $$);}
|
||||
%type idlist {IdList*}
|
||||
%destructor idlist {sqlite3IdListDelete(pParse->db, $$);}
|
||||
|
||||
inscollist_opt(A) ::= . {A = 0;}
|
||||
inscollist_opt(A) ::= LP inscollist(X) RP. {A = X;}
|
||||
inscollist(A) ::= inscollist(X) COMMA nm(Y).
|
||||
inscollist_opt(A) ::= LP idlist(X) RP. {A = X;}
|
||||
idlist(A) ::= idlist(X) COMMA nm(Y).
|
||||
{A = sqlite3IdListAppend(pParse->db,X,&Y);}
|
||||
inscollist(A) ::= nm(Y).
|
||||
idlist(A) ::= nm(Y).
|
||||
{A = sqlite3IdListAppend(pParse->db,0,&Y);}
|
||||
|
||||
/////////////////////////// Expression Processing /////////////////////////////
|
||||
@@ -800,22 +814,22 @@ expr(A) ::= nm(X) DOT nm(Y) DOT nm(Z). {
|
||||
}
|
||||
term(A) ::= INTEGER|FLOAT|BLOB(X). {spanExpr(&A, pParse, @X, &X);}
|
||||
term(A) ::= STRING(X). {spanExpr(&A, pParse, @X, &X);}
|
||||
expr(A) ::= REGISTER(X). {
|
||||
/* When doing a nested parse, one can include terms in an expression
|
||||
** that look like this: #1 #2 ... These terms refer to registers
|
||||
** in the virtual machine. #N is the N-th register. */
|
||||
if( pParse->nested==0 ){
|
||||
sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &X);
|
||||
A.pExpr = 0;
|
||||
}else{
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_REGISTER, 0, 0, &X);
|
||||
if( A.pExpr ) sqlite3GetInt32(&X.z[1], &A.pExpr->iTable);
|
||||
}
|
||||
spanSet(&A, &X, &X);
|
||||
}
|
||||
expr(A) ::= VARIABLE(X). {
|
||||
spanExpr(&A, pParse, TK_VARIABLE, &X);
|
||||
sqlite3ExprAssignVarNumber(pParse, A.pExpr);
|
||||
if( X.n>=2 && X.z[0]=='#' && sqlite3Isdigit(X.z[1]) ){
|
||||
/* When doing a nested parse, one can include terms in an expression
|
||||
** that look like this: #1 #2 ... These terms refer to registers
|
||||
** in the virtual machine. #N is the N-th register. */
|
||||
if( pParse->nested==0 ){
|
||||
sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &X);
|
||||
A.pExpr = 0;
|
||||
}else{
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_REGISTER, 0, 0, &X);
|
||||
if( A.pExpr ) sqlite3GetInt32(&X.z[1], &A.pExpr->iTable);
|
||||
}
|
||||
}else{
|
||||
spanExpr(&A, pParse, TK_VARIABLE, &X);
|
||||
sqlite3ExprAssignVarNumber(pParse, A.pExpr);
|
||||
}
|
||||
spanSet(&A, &X, &X);
|
||||
}
|
||||
expr(A) ::= expr(E) COLLATE ids(C). {
|
||||
@@ -829,7 +843,7 @@ expr(A) ::= CAST(X) LP expr(E) AS typetoken(T) RP(Y). {
|
||||
spanSet(&A,&X,&Y);
|
||||
}
|
||||
%endif SQLITE_OMIT_CAST
|
||||
expr(A) ::= ID(X) LP distinct(D) exprlist(Y) RP(E). {
|
||||
expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP(E). {
|
||||
if( Y && Y->nExpr>pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){
|
||||
sqlite3ErrorMsg(pParse, "too many arguments on function %T", &X);
|
||||
}
|
||||
@@ -839,17 +853,12 @@ expr(A) ::= ID(X) LP distinct(D) exprlist(Y) RP(E). {
|
||||
A.pExpr->flags |= EP_Distinct;
|
||||
}
|
||||
}
|
||||
expr(A) ::= ID(X) LP STAR RP(E). {
|
||||
expr(A) ::= id(X) LP STAR RP(E). {
|
||||
A.pExpr = sqlite3ExprFunction(pParse, 0, &X);
|
||||
spanSet(&A,&X,&E);
|
||||
}
|
||||
term(A) ::= CTIME_KW(OP). {
|
||||
/* The CURRENT_TIME, CURRENT_DATE, and CURRENT_TIMESTAMP values are
|
||||
** treated as functions that return constants */
|
||||
A.pExpr = sqlite3ExprFunction(pParse, 0,&OP);
|
||||
if( A.pExpr ){
|
||||
A.pExpr->op = TK_CONST_FUNC;
|
||||
}
|
||||
A.pExpr = sqlite3ExprFunction(pParse, 0, &OP);
|
||||
spanSet(&A, &OP, &OP);
|
||||
}
|
||||
|
||||
@@ -883,10 +892,8 @@ expr(A) ::= expr(X) STAR|SLASH|REM(OP) expr(Y).
|
||||
{spanBinaryExpr(&A,pParse,@OP,&X,&Y);}
|
||||
expr(A) ::= expr(X) CONCAT(OP) expr(Y). {spanBinaryExpr(&A,pParse,@OP,&X,&Y);}
|
||||
%type likeop {struct LikeOp}
|
||||
likeop(A) ::= LIKE_KW(X). {A.eOperator = X; A.bNot = 0;}
|
||||
likeop(A) ::= NOT LIKE_KW(X). {A.eOperator = X; A.bNot = 1;}
|
||||
likeop(A) ::= MATCH(X). {A.eOperator = X; A.bNot = 0;}
|
||||
likeop(A) ::= NOT MATCH(X). {A.eOperator = X; A.bNot = 1;}
|
||||
likeop(A) ::= LIKE_KW|MATCH(X). {A.eOperator = X; A.bNot = 0;}
|
||||
likeop(A) ::= NOT LIKE_KW|MATCH(X). {A.eOperator = X; A.bNot = 1;}
|
||||
expr(A) ::= expr(X) likeop(OP) expr(Y). [LIKE_KW] {
|
||||
ExprList *pList;
|
||||
pList = sqlite3ExprListAppend(pParse,0, Y.pExpr);
|
||||
@@ -1081,12 +1088,13 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
|
||||
|
||||
/* CASE expressions */
|
||||
expr(A) ::= CASE(C) case_operand(X) case_exprlist(Y) case_else(Z) END(E). {
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_CASE, X, Z, 0);
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_CASE, X, 0, 0);
|
||||
if( A.pExpr ){
|
||||
A.pExpr->x.pList = Y;
|
||||
A.pExpr->x.pList = Z ? sqlite3ExprListAppend(pParse,Y,Z) : Y;
|
||||
sqlite3ExprSetHeight(pParse, A.pExpr);
|
||||
}else{
|
||||
sqlite3ExprListDelete(pParse->db, Y);
|
||||
sqlite3ExprDelete(pParse->db, Z);
|
||||
}
|
||||
A.zStart = C.z;
|
||||
A.zEnd = &E.z[E.n];
|
||||
@@ -1193,11 +1201,10 @@ nmnum(A) ::= ON(X). {A = X;}
|
||||
nmnum(A) ::= DELETE(X). {A = X;}
|
||||
nmnum(A) ::= DEFAULT(X). {A = X;}
|
||||
%endif SQLITE_OMIT_PRAGMA
|
||||
%token_class number INTEGER|FLOAT.
|
||||
plus_num(A) ::= PLUS number(X). {A = X;}
|
||||
plus_num(A) ::= number(X). {A = X;}
|
||||
minus_num(A) ::= MINUS number(X). {A = X;}
|
||||
number(A) ::= INTEGER|FLOAT(X). {A = X;}
|
||||
|
||||
//////////////////////////// The CREATE TRIGGER command /////////////////////
|
||||
|
||||
%ifndef SQLITE_OMIT_TRIGGER
|
||||
@@ -1226,7 +1233,7 @@ trigger_time(A) ::= . { A = TK_BEFORE; }
|
||||
%destructor trigger_event {sqlite3IdListDelete(pParse->db, $$.b);}
|
||||
trigger_event(A) ::= DELETE|INSERT(OP). {A.a = @OP; A.b = 0;}
|
||||
trigger_event(A) ::= UPDATE(OP). {A.a = @OP; A.b = 0;}
|
||||
trigger_event(A) ::= UPDATE OF inscollist(X). {A.a = TK_UPDATE; A.b = X;}
|
||||
trigger_event(A) ::= UPDATE OF idlist(X). {A.a = TK_UPDATE; A.b = X;}
|
||||
|
||||
foreach_clause ::= .
|
||||
foreach_clause ::= FOR EACH ROW.
|
||||
@@ -1289,12 +1296,8 @@ trigger_cmd(A) ::=
|
||||
{ A = sqlite3TriggerUpdateStep(pParse->db, &X, Y, Z, R); }
|
||||
|
||||
// INSERT
|
||||
trigger_cmd(A) ::=
|
||||
insert_cmd(R) INTO trnm(X) inscollist_opt(F) valuelist(Y).
|
||||
{A = sqlite3TriggerInsertStep(pParse->db, &X, F, Y.pList, Y.pSelect, R);}
|
||||
|
||||
trigger_cmd(A) ::= insert_cmd(R) INTO trnm(X) inscollist_opt(F) select(S).
|
||||
{A = sqlite3TriggerInsertStep(pParse->db, &X, F, 0, S, R);}
|
||||
{A = sqlite3TriggerInsertStep(pParse->db, &X, F, S, R);}
|
||||
|
||||
// DELETE
|
||||
trigger_cmd(A) ::= DELETE FROM trnm(X) tridxby where_opt(Y).
|
||||
@@ -1400,3 +1403,23 @@ anylist ::= .
|
||||
anylist ::= anylist LP anylist RP.
|
||||
anylist ::= anylist ANY.
|
||||
%endif SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
|
||||
//////////////////////// COMMON TABLE EXPRESSIONS ////////////////////////////
|
||||
%type with {With*}
|
||||
%type wqlist {With*}
|
||||
%destructor with {sqlite3WithDelete(pParse->db, $$);}
|
||||
%destructor wqlist {sqlite3WithDelete(pParse->db, $$);}
|
||||
|
||||
with(A) ::= . {A = 0;}
|
||||
%ifndef SQLITE_OMIT_CTE
|
||||
with(A) ::= WITH wqlist(W). { A = W; }
|
||||
with(A) ::= WITH RECURSIVE wqlist(W). { A = W; }
|
||||
|
||||
wqlist(A) ::= nm(X) idxlist_opt(Y) AS LP select(Z) RP. {
|
||||
A = sqlite3WithAdd(pParse, 0, &X, Y, Z);
|
||||
}
|
||||
wqlist(A) ::= wqlist(W) COMMA nm(X) idxlist_opt(Y) AS LP select(Z) RP. {
|
||||
A = sqlite3WithAdd(pParse, W, &X, Y, Z);
|
||||
}
|
||||
%endif SQLITE_OMIT_CTE
|
||||
|
||||
+26
-8
@@ -23,7 +23,8 @@ struct PCache {
|
||||
int szCache; /* Configured cache size */
|
||||
int szPage; /* Size of every page in this cache */
|
||||
int szExtra; /* Size of extra space for each page */
|
||||
int bPurgeable; /* True if pages are on backing store */
|
||||
u8 bPurgeable; /* True if pages are on backing store */
|
||||
u8 eCreate; /* eCreate value for for xFetch() */
|
||||
int (*xStress)(void*,PgHdr*); /* Call to try make a page clean */
|
||||
void *pStress; /* Argument to xStress */
|
||||
sqlite3_pcache *pCache; /* Pluggable cache module */
|
||||
@@ -90,6 +91,10 @@ static void pcacheRemoveFromDirtyList(PgHdr *pPage){
|
||||
}else{
|
||||
assert( pPage==p->pDirty );
|
||||
p->pDirty = pPage->pDirtyNext;
|
||||
if( p->pDirty==0 && p->bPurgeable ){
|
||||
assert( p->eCreate==1 );
|
||||
p->eCreate = 2;
|
||||
}
|
||||
}
|
||||
pPage->pDirtyNext = 0;
|
||||
pPage->pDirtyPrev = 0;
|
||||
@@ -110,6 +115,9 @@ static void pcacheAddToDirtyList(PgHdr *pPage){
|
||||
if( pPage->pDirtyNext ){
|
||||
assert( pPage->pDirtyNext->pDirtyPrev==0 );
|
||||
pPage->pDirtyNext->pDirtyPrev = pPage;
|
||||
}else if( p->bPurgeable ){
|
||||
assert( p->eCreate==2 );
|
||||
p->eCreate = 1;
|
||||
}
|
||||
p->pDirty = pPage;
|
||||
if( !p->pDirtyTail ){
|
||||
@@ -179,6 +187,7 @@ void sqlite3PcacheOpen(
|
||||
p->szPage = szPage;
|
||||
p->szExtra = szExtra;
|
||||
p->bPurgeable = bPurgeable;
|
||||
p->eCreate = 2;
|
||||
p->xStress = xStress;
|
||||
p->pStress = pStress;
|
||||
p->szCache = 100;
|
||||
@@ -218,7 +227,7 @@ int sqlite3PcacheFetch(
|
||||
int createFlag, /* If true, create page if it does not exist already */
|
||||
PgHdr **ppPage /* Write the page here */
|
||||
){
|
||||
sqlite3_pcache_page *pPage = 0;
|
||||
sqlite3_pcache_page *pPage;
|
||||
PgHdr *pPgHdr = 0;
|
||||
int eCreate;
|
||||
|
||||
@@ -229,8 +238,12 @@ int sqlite3PcacheFetch(
|
||||
/* If the pluggable cache (sqlite3_pcache*) has not been allocated,
|
||||
** allocate it now.
|
||||
*/
|
||||
if( !pCache->pCache && createFlag ){
|
||||
if( !pCache->pCache ){
|
||||
sqlite3_pcache *p;
|
||||
if( !createFlag ){
|
||||
*ppPage = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
p = sqlite3GlobalConfig.pcache2.xCreate(
|
||||
pCache->szPage, pCache->szExtra + sizeof(PgHdr), pCache->bPurgeable
|
||||
);
|
||||
@@ -241,11 +254,16 @@ int sqlite3PcacheFetch(
|
||||
pCache->pCache = p;
|
||||
}
|
||||
|
||||
eCreate = createFlag * (1 + (!pCache->bPurgeable || !pCache->pDirty));
|
||||
if( pCache->pCache ){
|
||||
pPage = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate);
|
||||
}
|
||||
|
||||
/* eCreate defines what to do if the page does not exist.
|
||||
** 0 Do not allocate a new page. (createFlag==0)
|
||||
** 1 Allocate a new page if doing so is inexpensive.
|
||||
** (createFlag==1 AND bPurgeable AND pDirty)
|
||||
** 2 Allocate a new page even it doing so is difficult.
|
||||
** (createFlag==1 AND !(bPurgeable AND pDirty)
|
||||
*/
|
||||
eCreate = createFlag==0 ? 0 : pCache->eCreate;
|
||||
assert( (createFlag*(1+(!pCache->bPurgeable||!pCache->pDirty)))==eCreate );
|
||||
pPage = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate);
|
||||
if( !pPage && eCreate==1 ){
|
||||
PgHdr *pPg;
|
||||
|
||||
|
||||
+33
-23
@@ -96,6 +96,7 @@ struct PCache1 {
|
||||
struct PgHdr1 {
|
||||
sqlite3_pcache_page page;
|
||||
unsigned int iKey; /* Key value (page number) */
|
||||
u8 isPinned; /* Page in use, not on the LRU list */
|
||||
PgHdr1 *pNext; /* Next in hash table chain */
|
||||
PCache1 *pCache; /* Cache that currently owns this page */
|
||||
PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */
|
||||
@@ -424,34 +425,32 @@ static int pcache1ResizeHash(PCache1 *p){
|
||||
** LRU list, then this function is a no-op.
|
||||
**
|
||||
** The PGroup mutex must be held when this function is called.
|
||||
**
|
||||
** If pPage is NULL then this routine is a no-op.
|
||||
*/
|
||||
static void pcache1PinPage(PgHdr1 *pPage){
|
||||
PCache1 *pCache;
|
||||
PGroup *pGroup;
|
||||
|
||||
if( pPage==0 ) return;
|
||||
assert( pPage!=0 );
|
||||
assert( pPage->isPinned==0 );
|
||||
pCache = pPage->pCache;
|
||||
pGroup = pCache->pGroup;
|
||||
assert( pPage->pLruNext || pPage==pGroup->pLruTail );
|
||||
assert( pPage->pLruPrev || pPage==pGroup->pLruHead );
|
||||
assert( sqlite3_mutex_held(pGroup->mutex) );
|
||||
if( pPage->pLruNext || pPage==pGroup->pLruTail ){
|
||||
if( pPage->pLruPrev ){
|
||||
pPage->pLruPrev->pLruNext = pPage->pLruNext;
|
||||
}
|
||||
if( pPage->pLruNext ){
|
||||
pPage->pLruNext->pLruPrev = pPage->pLruPrev;
|
||||
}
|
||||
if( pGroup->pLruHead==pPage ){
|
||||
pGroup->pLruHead = pPage->pLruNext;
|
||||
}
|
||||
if( pGroup->pLruTail==pPage ){
|
||||
pGroup->pLruTail = pPage->pLruPrev;
|
||||
}
|
||||
pPage->pLruNext = 0;
|
||||
pPage->pLruPrev = 0;
|
||||
pPage->pCache->nRecyclable--;
|
||||
if( pPage->pLruPrev ){
|
||||
pPage->pLruPrev->pLruNext = pPage->pLruNext;
|
||||
}else{
|
||||
pGroup->pLruHead = pPage->pLruNext;
|
||||
}
|
||||
if( pPage->pLruNext ){
|
||||
pPage->pLruNext->pLruPrev = pPage->pLruPrev;
|
||||
}else{
|
||||
pGroup->pLruTail = pPage->pLruPrev;
|
||||
}
|
||||
pPage->pLruNext = 0;
|
||||
pPage->pLruPrev = 0;
|
||||
pPage->isPinned = 1;
|
||||
pCache->nRecyclable--;
|
||||
}
|
||||
|
||||
|
||||
@@ -483,6 +482,7 @@ static void pcache1EnforceMaxPage(PGroup *pGroup){
|
||||
while( pGroup->nCurrentPage>pGroup->nMaxPage && pGroup->pLruTail ){
|
||||
PgHdr1 *p = pGroup->pLruTail;
|
||||
assert( p->pCache->pGroup==pGroup );
|
||||
assert( p->isPinned==0 );
|
||||
pcache1PinPage(p);
|
||||
pcache1RemoveFromHash(p);
|
||||
pcache1FreePage(p);
|
||||
@@ -510,7 +510,7 @@ static void pcache1TruncateUnsafe(
|
||||
if( pPage->iKey>=iLimit ){
|
||||
pCache->nPage--;
|
||||
*pp = pPage->pNext;
|
||||
pcache1PinPage(pPage);
|
||||
if( !pPage->isPinned ) pcache1PinPage(pPage);
|
||||
pcache1FreePage(pPage);
|
||||
}else{
|
||||
pp = &pPage->pNext;
|
||||
@@ -720,6 +720,7 @@ static sqlite3_pcache_page *pcache1Fetch(
|
||||
PGroup *pGroup;
|
||||
PgHdr1 *pPage = 0;
|
||||
|
||||
assert( offsetof(PgHdr1,page)==0 );
|
||||
assert( pCache->bPurgeable || createFlag!=1 );
|
||||
assert( pCache->bPurgeable || pCache->nMin==0 );
|
||||
assert( pCache->bPurgeable==0 || pCache->nMin==10 );
|
||||
@@ -733,8 +734,11 @@ static sqlite3_pcache_page *pcache1Fetch(
|
||||
}
|
||||
|
||||
/* Step 2: Abort if no existing page is found and createFlag is 0 */
|
||||
if( pPage || createFlag==0 ){
|
||||
pcache1PinPage(pPage);
|
||||
if( pPage ){
|
||||
if( !pPage->isPinned ) pcache1PinPage(pPage);
|
||||
goto fetch_out;
|
||||
}
|
||||
if( createFlag==0 ){
|
||||
goto fetch_out;
|
||||
}
|
||||
|
||||
@@ -775,6 +779,7 @@ static sqlite3_pcache_page *pcache1Fetch(
|
||||
)){
|
||||
PCache1 *pOther;
|
||||
pPage = pGroup->pLruTail;
|
||||
assert( pPage->isPinned==0 );
|
||||
pcache1RemoveFromHash(pPage);
|
||||
pcache1PinPage(pPage);
|
||||
pOther = pPage->pCache;
|
||||
@@ -811,6 +816,7 @@ static sqlite3_pcache_page *pcache1Fetch(
|
||||
pPage->pCache = pCache;
|
||||
pPage->pLruPrev = 0;
|
||||
pPage->pLruNext = 0;
|
||||
pPage->isPinned = 1;
|
||||
*(void **)pPage->page.pExtra = 0;
|
||||
pCache->apHash[h] = pPage;
|
||||
}
|
||||
@@ -820,7 +826,7 @@ fetch_out:
|
||||
pCache->iMaxKey = iKey;
|
||||
}
|
||||
pcache1LeaveMutex(pGroup);
|
||||
return &pPage->page;
|
||||
return (sqlite3_pcache_page*)pPage;
|
||||
}
|
||||
|
||||
|
||||
@@ -846,6 +852,7 @@ static void pcache1Unpin(
|
||||
*/
|
||||
assert( pPage->pLruPrev==0 && pPage->pLruNext==0 );
|
||||
assert( pGroup->pLruHead!=pPage && pGroup->pLruTail!=pPage );
|
||||
assert( pPage->isPinned==1 );
|
||||
|
||||
if( reuseUnlikely || pGroup->nCurrentPage>pGroup->nMaxPage ){
|
||||
pcache1RemoveFromHash(pPage);
|
||||
@@ -861,6 +868,7 @@ static void pcache1Unpin(
|
||||
pGroup->pLruHead = pPage;
|
||||
}
|
||||
pCache->nRecyclable++;
|
||||
pPage->isPinned = 0;
|
||||
}
|
||||
|
||||
pcache1LeaveMutex(pCache->pGroup);
|
||||
@@ -987,6 +995,7 @@ int sqlite3PcacheReleaseMemory(int nReq){
|
||||
#ifdef SQLITE_PCACHE_SEPARATE_HEADER
|
||||
nFree += sqlite3MemSize(p);
|
||||
#endif
|
||||
assert( p->isPinned==0 );
|
||||
pcache1PinPage(p);
|
||||
pcache1RemoveFromHash(p);
|
||||
pcache1FreePage(p);
|
||||
@@ -1011,6 +1020,7 @@ void sqlite3PcacheStats(
|
||||
PgHdr1 *p;
|
||||
int nRecyclable = 0;
|
||||
for(p=pcache1.grp.pLruHead; p; p=p->pLruNext){
|
||||
assert( p->isPinned==0 );
|
||||
nRecyclable++;
|
||||
}
|
||||
*pnCurrent = pcache1.grp.nCurrentPage;
|
||||
|
||||
+839
-371
File diff suppressed because it is too large
Load Diff
@@ -524,6 +524,17 @@ int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free all memory allocations in the pParse object
|
||||
*/
|
||||
void sqlite3ParserReset(Parse *pParse){
|
||||
if( pParse ){
|
||||
sqlite3 *db = pParse->db;
|
||||
sqlite3DbFree(db, pParse->aLabel);
|
||||
sqlite3ExprListDelete(db, pParse->pConstExpr);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Compile the UTF-8 encoded SQL statement zSql into a statement handle.
|
||||
*/
|
||||
@@ -681,6 +692,7 @@ static int sqlite3Prepare(
|
||||
|
||||
end_prepare:
|
||||
|
||||
sqlite3ParserReset(pParse);
|
||||
sqlite3StackFree(db, pParse);
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
assert( (rc&db->errMask)==rc );
|
||||
|
||||
+117
-42
@@ -148,6 +148,31 @@ void sqlite3AppendSpace(StrAccum *pAccum, int N){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the StrAccum object to an error mode.
|
||||
*/
|
||||
static void setStrAccumError(StrAccum *p, u8 eError){
|
||||
p->accError = eError;
|
||||
p->nAlloc = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Extra argument values from a PrintfArguments object
|
||||
*/
|
||||
static sqlite3_int64 getIntArg(PrintfArguments *p){
|
||||
if( p->nArg<=p->nUsed ) return 0;
|
||||
return sqlite3_value_int64(p->apArg[p->nUsed++]);
|
||||
}
|
||||
static double getDoubleArg(PrintfArguments *p){
|
||||
if( p->nArg<=p->nUsed ) return 0.0;
|
||||
return sqlite3_value_double(p->apArg[p->nUsed++]);
|
||||
}
|
||||
static char *getTextArg(PrintfArguments *p){
|
||||
if( p->nArg<=p->nUsed ) return 0;
|
||||
return (char*)sqlite3_value_text(p->apArg[p->nUsed++]);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** On machines with a small stack size, you can redefine the
|
||||
** SQLITE_PRINT_BUF_SIZE to be something smaller, if desired.
|
||||
@@ -161,10 +186,10 @@ void sqlite3AppendSpace(StrAccum *pAccum, int N){
|
||||
** Render a string given by "fmt" into the StrAccum object.
|
||||
*/
|
||||
void sqlite3VXPrintf(
|
||||
StrAccum *pAccum, /* Accumulate results here */
|
||||
int useExtended, /* Allow extended %-conversions */
|
||||
const char *fmt, /* Format string */
|
||||
va_list ap /* arguments */
|
||||
StrAccum *pAccum, /* Accumulate results here */
|
||||
u32 bFlags, /* SQLITE_PRINTF_* flags */
|
||||
const char *fmt, /* Format string */
|
||||
va_list ap /* arguments */
|
||||
){
|
||||
int c; /* Next character in the format string */
|
||||
char *bufpt; /* Pointer to the conversion buffer */
|
||||
@@ -182,6 +207,8 @@ void sqlite3VXPrintf(
|
||||
etByte flag_longlong; /* True if the "ll" flag is present */
|
||||
etByte done; /* Loop termination flag */
|
||||
etByte xtype = 0; /* Conversion paradigm */
|
||||
u8 bArgList; /* True for SQLITE_PRINTF_SQLFUNC */
|
||||
u8 useIntern; /* Ok to use internal conversions (ex: %T) */
|
||||
char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */
|
||||
sqlite_uint64 longvalue; /* Value for integer types */
|
||||
LONGDOUBLE_TYPE realvalue; /* Value for real types */
|
||||
@@ -196,9 +223,18 @@ void sqlite3VXPrintf(
|
||||
etByte flag_dp; /* True if decimal point should be shown */
|
||||
etByte flag_rtz; /* True if trailing zeros should be removed */
|
||||
#endif
|
||||
PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */
|
||||
char buf[etBUFSIZE]; /* Conversion buffer */
|
||||
|
||||
bufpt = 0;
|
||||
if( bFlags ){
|
||||
if( (bArgList = (bFlags & SQLITE_PRINTF_SQLFUNC))!=0 ){
|
||||
pArgList = va_arg(ap, PrintfArguments*);
|
||||
}
|
||||
useIntern = bFlags & SQLITE_PRINTF_INTERNAL;
|
||||
}else{
|
||||
bArgList = useIntern = 0;
|
||||
}
|
||||
for(; (c=(*fmt))!=0; ++fmt){
|
||||
if( c!='%' ){
|
||||
int amt;
|
||||
@@ -230,7 +266,11 @@ void sqlite3VXPrintf(
|
||||
/* Get the field width */
|
||||
width = 0;
|
||||
if( c=='*' ){
|
||||
width = va_arg(ap,int);
|
||||
if( bArgList ){
|
||||
width = (int)getIntArg(pArgList);
|
||||
}else{
|
||||
width = va_arg(ap,int);
|
||||
}
|
||||
if( width<0 ){
|
||||
flag_leftjustify = 1;
|
||||
width = -width;
|
||||
@@ -247,7 +287,11 @@ void sqlite3VXPrintf(
|
||||
precision = 0;
|
||||
c = *++fmt;
|
||||
if( c=='*' ){
|
||||
precision = va_arg(ap,int);
|
||||
if( bArgList ){
|
||||
precision = (int)getIntArg(pArgList);
|
||||
}else{
|
||||
precision = va_arg(ap,int);
|
||||
}
|
||||
if( precision<0 ) precision = -precision;
|
||||
c = *++fmt;
|
||||
}else{
|
||||
@@ -278,7 +322,7 @@ void sqlite3VXPrintf(
|
||||
for(idx=0; idx<ArraySize(fmtinfo); idx++){
|
||||
if( c==fmtinfo[idx].fmttype ){
|
||||
infop = &fmtinfo[idx];
|
||||
if( useExtended || (infop->flags & FLAG_INTERN)==0 ){
|
||||
if( useIntern || (infop->flags & FLAG_INTERN)==0 ){
|
||||
xtype = infop->type;
|
||||
}else{
|
||||
return;
|
||||
@@ -318,7 +362,9 @@ void sqlite3VXPrintf(
|
||||
case etRADIX:
|
||||
if( infop->flags & FLAG_SIGNED ){
|
||||
i64 v;
|
||||
if( flag_longlong ){
|
||||
if( bArgList ){
|
||||
v = getIntArg(pArgList);
|
||||
}else if( flag_longlong ){
|
||||
v = va_arg(ap,i64);
|
||||
}else if( flag_long ){
|
||||
v = va_arg(ap,long int);
|
||||
@@ -339,7 +385,9 @@ void sqlite3VXPrintf(
|
||||
else prefix = 0;
|
||||
}
|
||||
}else{
|
||||
if( flag_longlong ){
|
||||
if( bArgList ){
|
||||
longvalue = (u64)getIntArg(pArgList);
|
||||
}else if( flag_longlong ){
|
||||
longvalue = va_arg(ap,u64);
|
||||
}else if( flag_long ){
|
||||
longvalue = va_arg(ap,unsigned long int);
|
||||
@@ -359,7 +407,7 @@ void sqlite3VXPrintf(
|
||||
nOut = precision + 10;
|
||||
zOut = zExtra = sqlite3Malloc( nOut );
|
||||
if( zOut==0 ){
|
||||
pAccum->accError = STRACCUM_NOMEM;
|
||||
setStrAccumError(pAccum, STRACCUM_NOMEM);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -399,7 +447,11 @@ void sqlite3VXPrintf(
|
||||
case etFLOAT:
|
||||
case etEXP:
|
||||
case etGENERIC:
|
||||
realvalue = va_arg(ap,double);
|
||||
if( bArgList ){
|
||||
realvalue = getDoubleArg(pArgList);
|
||||
}else{
|
||||
realvalue = va_arg(ap,double);
|
||||
}
|
||||
#ifdef SQLITE_OMIT_FLOATING_POINT
|
||||
length = 0;
|
||||
#else
|
||||
@@ -471,7 +523,7 @@ void sqlite3VXPrintf(
|
||||
if( MAX(e2,0)+precision+width > etBUFSIZE - 15 ){
|
||||
bufpt = zExtra = sqlite3Malloc( MAX(e2,0)+precision+width+15 );
|
||||
if( bufpt==0 ){
|
||||
pAccum->accError = STRACCUM_NOMEM;
|
||||
setStrAccumError(pAccum, STRACCUM_NOMEM);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -554,7 +606,9 @@ void sqlite3VXPrintf(
|
||||
#endif /* !defined(SQLITE_OMIT_FLOATING_POINT) */
|
||||
break;
|
||||
case etSIZE:
|
||||
*(va_arg(ap,int*)) = pAccum->nChar;
|
||||
if( !bArgList ){
|
||||
*(va_arg(ap,int*)) = pAccum->nChar;
|
||||
}
|
||||
length = width = 0;
|
||||
break;
|
||||
case etPERCENT:
|
||||
@@ -563,7 +617,12 @@ void sqlite3VXPrintf(
|
||||
length = 1;
|
||||
break;
|
||||
case etCHARX:
|
||||
c = va_arg(ap,int);
|
||||
if( bArgList ){
|
||||
bufpt = getTextArg(pArgList);
|
||||
c = bufpt ? bufpt[0] : 0;
|
||||
}else{
|
||||
c = va_arg(ap,int);
|
||||
}
|
||||
buf[0] = (char)c;
|
||||
if( precision>=0 ){
|
||||
for(idx=1; idx<precision; idx++) buf[idx] = (char)c;
|
||||
@@ -575,10 +634,14 @@ void sqlite3VXPrintf(
|
||||
break;
|
||||
case etSTRING:
|
||||
case etDYNSTRING:
|
||||
bufpt = va_arg(ap,char*);
|
||||
if( bArgList ){
|
||||
bufpt = getTextArg(pArgList);
|
||||
}else{
|
||||
bufpt = va_arg(ap,char*);
|
||||
}
|
||||
if( bufpt==0 ){
|
||||
bufpt = "";
|
||||
}else if( xtype==etDYNSTRING ){
|
||||
}else if( xtype==etDYNSTRING && !bArgList ){
|
||||
zExtra = bufpt;
|
||||
}
|
||||
if( precision>=0 ){
|
||||
@@ -594,7 +657,13 @@ void sqlite3VXPrintf(
|
||||
int needQuote;
|
||||
char ch;
|
||||
char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote character */
|
||||
char *escarg = va_arg(ap,char*);
|
||||
char *escarg;
|
||||
|
||||
if( bArgList ){
|
||||
escarg = getTextArg(pArgList);
|
||||
}else{
|
||||
escarg = va_arg(ap,char*);
|
||||
}
|
||||
isnull = escarg==0;
|
||||
if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)");
|
||||
k = precision;
|
||||
@@ -606,7 +675,7 @@ void sqlite3VXPrintf(
|
||||
if( n>etBUFSIZE ){
|
||||
bufpt = zExtra = sqlite3Malloc( n );
|
||||
if( bufpt==0 ){
|
||||
pAccum->accError = STRACCUM_NOMEM;
|
||||
setStrAccumError(pAccum, STRACCUM_NOMEM);
|
||||
return;
|
||||
}
|
||||
}else{
|
||||
@@ -629,7 +698,8 @@ void sqlite3VXPrintf(
|
||||
}
|
||||
case etTOKEN: {
|
||||
Token *pToken = va_arg(ap, Token*);
|
||||
if( pToken ){
|
||||
assert( bArgList==0 );
|
||||
if( pToken && pToken->n ){
|
||||
sqlite3StrAccumAppend(pAccum, (const char*)pToken->z, pToken->n);
|
||||
}
|
||||
length = width = 0;
|
||||
@@ -639,12 +709,13 @@ void sqlite3VXPrintf(
|
||||
SrcList *pSrc = va_arg(ap, SrcList*);
|
||||
int k = va_arg(ap, int);
|
||||
struct SrcList_item *pItem = &pSrc->a[k];
|
||||
assert( bArgList==0 );
|
||||
assert( k>=0 && k<pSrc->nSrc );
|
||||
if( pItem->zDatabase ){
|
||||
sqlite3StrAccumAppend(pAccum, pItem->zDatabase, -1);
|
||||
sqlite3StrAccumAppendAll(pAccum, pItem->zDatabase);
|
||||
sqlite3StrAccumAppend(pAccum, ".", 1);
|
||||
}
|
||||
sqlite3StrAccumAppend(pAccum, pItem->zName, -1);
|
||||
sqlite3StrAccumAppendAll(pAccum, pItem->zName);
|
||||
length = width = 0;
|
||||
break;
|
||||
}
|
||||
@@ -675,7 +746,7 @@ void sqlite3VXPrintf(
|
||||
sqlite3AppendSpace(pAccum, nspace);
|
||||
}
|
||||
}
|
||||
sqlite3_free(zExtra);
|
||||
if( zExtra ) sqlite3_free(zExtra);
|
||||
}/* End for loop over the format string */
|
||||
} /* End of function */
|
||||
|
||||
@@ -683,22 +754,20 @@ void sqlite3VXPrintf(
|
||||
** Append N bytes of text from z to the StrAccum object.
|
||||
*/
|
||||
void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
|
||||
assert( z!=0 || N==0 );
|
||||
if( p->accError ){
|
||||
testcase(p->accError==STRACCUM_TOOBIG);
|
||||
testcase(p->accError==STRACCUM_NOMEM);
|
||||
return;
|
||||
}
|
||||
assert( p->zText!=0 || p->nChar==0 );
|
||||
if( N<=0 ){
|
||||
if( N==0 || z[0]==0 ) return;
|
||||
N = sqlite3Strlen30(z);
|
||||
}
|
||||
assert( z!=0 );
|
||||
assert( p->zText!=0 || p->nChar==0 || p->accError );
|
||||
assert( N>=0 );
|
||||
assert( p->accError==0 || p->nAlloc==0 );
|
||||
if( p->nChar+N >= p->nAlloc ){
|
||||
char *zNew;
|
||||
if( p->accError ){
|
||||
testcase(p->accError==STRACCUM_TOOBIG);
|
||||
testcase(p->accError==STRACCUM_NOMEM);
|
||||
return;
|
||||
}
|
||||
if( !p->useMalloc ){
|
||||
p->accError = STRACCUM_TOOBIG;
|
||||
N = p->nAlloc - p->nChar - 1;
|
||||
setStrAccumError(p, STRACCUM_TOOBIG);
|
||||
if( N<=0 ){
|
||||
return;
|
||||
}
|
||||
@@ -708,7 +777,7 @@ void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
|
||||
szNew += N + 1;
|
||||
if( szNew > p->mxAlloc ){
|
||||
sqlite3StrAccumReset(p);
|
||||
p->accError = STRACCUM_TOOBIG;
|
||||
setStrAccumError(p, STRACCUM_TOOBIG);
|
||||
return;
|
||||
}else{
|
||||
p->nAlloc = (int)szNew;
|
||||
@@ -722,8 +791,8 @@ void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
|
||||
if( zOld==0 && p->nChar>0 ) memcpy(zNew, p->zText, p->nChar);
|
||||
p->zText = zNew;
|
||||
}else{
|
||||
p->accError = STRACCUM_NOMEM;
|
||||
sqlite3StrAccumReset(p);
|
||||
setStrAccumError(p, STRACCUM_NOMEM);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -733,6 +802,14 @@ void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
|
||||
p->nChar += N;
|
||||
}
|
||||
|
||||
/*
|
||||
** Append the complete text of zero-terminated string z[] to the p string.
|
||||
*/
|
||||
void sqlite3StrAccumAppendAll(StrAccum *p, const char *z){
|
||||
sqlite3StrAccumAppend(p, z, sqlite3Strlen30(z));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Finish off a string by making sure it is zero-terminated.
|
||||
** Return a pointer to the resulting string. Return a NULL
|
||||
@@ -750,7 +827,7 @@ char *sqlite3StrAccumFinish(StrAccum *p){
|
||||
if( p->zText ){
|
||||
memcpy(p->zText, p->zBase, p->nChar+1);
|
||||
}else{
|
||||
p->accError = STRACCUM_NOMEM;
|
||||
setStrAccumError(p, STRACCUM_NOMEM);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -796,7 +873,7 @@ char *sqlite3VMPrintf(sqlite3 *db, const char *zFormat, va_list ap){
|
||||
sqlite3StrAccumInit(&acc, zBase, sizeof(zBase),
|
||||
db->aLimit[SQLITE_LIMIT_LENGTH]);
|
||||
acc.db = db;
|
||||
sqlite3VXPrintf(&acc, 1, zFormat, ap);
|
||||
sqlite3VXPrintf(&acc, SQLITE_PRINTF_INTERNAL, zFormat, ap);
|
||||
z = sqlite3StrAccumFinish(&acc);
|
||||
if( acc.accError==STRACCUM_NOMEM ){
|
||||
db->mallocFailed = 1;
|
||||
@@ -952,14 +1029,12 @@ void sqlite3DebugPrintf(const char *zFormat, ...){
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_TRACE
|
||||
/*
|
||||
** variable-argument wrapper around sqlite3VXPrintf().
|
||||
*/
|
||||
void sqlite3XPrintf(StrAccum *p, const char *zFormat, ...){
|
||||
void sqlite3XPrintf(StrAccum *p, u32 bFlags, const char *zFormat, ...){
|
||||
va_list ap;
|
||||
va_start(ap,zFormat);
|
||||
sqlite3VXPrintf(p, 1, zFormat, ap);
|
||||
sqlite3VXPrintf(p, bFlags, zFormat, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
#endif
|
||||
|
||||
+9
-5
@@ -52,6 +52,12 @@ void sqlite3_randomness(int N, void *pBuf){
|
||||
sqlite3_mutex_enter(mutex);
|
||||
#endif
|
||||
|
||||
if( N<=0 ){
|
||||
wsdPrng.isInit = 0;
|
||||
sqlite3_mutex_leave(mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Initialize the state of the random number generator once,
|
||||
** the first time this routine is called. The seed value does
|
||||
** not need to contain a lot of randomness since we are not
|
||||
@@ -79,7 +85,8 @@ void sqlite3_randomness(int N, void *pBuf){
|
||||
wsdPrng.isInit = 1;
|
||||
}
|
||||
|
||||
while( N-- ){
|
||||
assert( N>0 );
|
||||
do{
|
||||
wsdPrng.i++;
|
||||
t = wsdPrng.s[wsdPrng.i];
|
||||
wsdPrng.j += t;
|
||||
@@ -87,7 +94,7 @@ void sqlite3_randomness(int N, void *pBuf){
|
||||
wsdPrng.s[wsdPrng.j] = t;
|
||||
t += wsdPrng.s[wsdPrng.i];
|
||||
*(zBuf++) = wsdPrng.s[t];
|
||||
}
|
||||
}while( --N );
|
||||
sqlite3_mutex_leave(mutex);
|
||||
}
|
||||
|
||||
@@ -116,7 +123,4 @@ void sqlite3PrngRestoreState(void){
|
||||
sizeof(sqlite3Prng)
|
||||
);
|
||||
}
|
||||
void sqlite3PrngResetState(void){
|
||||
GLOBAL(struct sqlite3PrngType, sqlite3Prng).isInit = 0;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_BUILTIN_TEST */
|
||||
|
||||
+62
-30
@@ -107,10 +107,11 @@ static void resolveAlias(
|
||||
incrAggFunctionDepth(pDup, nSubquery);
|
||||
pDup = sqlite3PExpr(pParse, TK_AS, pDup, 0, 0);
|
||||
if( pDup==0 ) return;
|
||||
if( pEList->a[iCol].iAlias==0 ){
|
||||
pEList->a[iCol].iAlias = (u16)(++pParse->nAlias);
|
||||
ExprSetProperty(pDup, EP_Skip);
|
||||
if( pEList->a[iCol].u.x.iAlias==0 ){
|
||||
pEList->a[iCol].u.x.iAlias = (u16)(++pParse->nAlias);
|
||||
}
|
||||
pDup->iTable = pEList->a[iCol].iAlias;
|
||||
pDup->iTable = pEList->a[iCol].u.x.iAlias;
|
||||
}
|
||||
if( pExpr->op==TK_COLLATE ){
|
||||
pDup = sqlite3ExprAddCollateString(pParse, pDup, pExpr->u.zToken);
|
||||
@@ -129,7 +130,7 @@ static void resolveAlias(
|
||||
if( !ExprHasProperty(pExpr, EP_IntValue) && pExpr->u.zToken!=0 ){
|
||||
assert( (pExpr->flags & (EP_Reduced|EP_TokenOnly))==0 );
|
||||
pExpr->u.zToken = sqlite3DbStrDup(db, pExpr->u.zToken);
|
||||
pExpr->flags2 |= EP2_MallocedToken;
|
||||
pExpr->flags |= EP_MemToken;
|
||||
}
|
||||
sqlite3DbFree(db, pDup);
|
||||
}
|
||||
@@ -225,16 +226,18 @@ static int lookupName(
|
||||
struct SrcList_item *pMatch = 0; /* The matching pSrcList item */
|
||||
NameContext *pTopNC = pNC; /* First namecontext in the list */
|
||||
Schema *pSchema = 0; /* Schema of the expression */
|
||||
int isTrigger = 0;
|
||||
int isTrigger = 0; /* True if resolved to a trigger column */
|
||||
Table *pTab = 0; /* Table hold the row */
|
||||
Column *pCol; /* A column of pTab */
|
||||
|
||||
assert( pNC ); /* the name context cannot be NULL. */
|
||||
assert( zCol ); /* The Z in X.Y.Z cannot be NULL */
|
||||
assert( !ExprHasAnyProperty(pExpr, EP_TokenOnly|EP_Reduced) );
|
||||
assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) );
|
||||
|
||||
/* Initialize the node to no-match */
|
||||
pExpr->iTable = -1;
|
||||
pExpr->pTab = 0;
|
||||
ExprSetIrreducible(pExpr);
|
||||
ExprSetVVAProperty(pExpr, EP_NoReduce);
|
||||
|
||||
/* Translate the schema name in zDb into a pointer to the corresponding
|
||||
** schema. If not found, pSchema will remain NULL and nothing will match
|
||||
@@ -266,9 +269,6 @@ static int lookupName(
|
||||
|
||||
if( pSrcList ){
|
||||
for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){
|
||||
Table *pTab;
|
||||
Column *pCol;
|
||||
|
||||
pTab = pItem->pTab;
|
||||
assert( pTab!=0 && pTab->zName!=0 );
|
||||
assert( pTab->nCol>0 );
|
||||
@@ -328,9 +328,8 @@ static int lookupName(
|
||||
/* If we have not already resolved the name, then maybe
|
||||
** it is a new.* or old.* trigger argument reference
|
||||
*/
|
||||
if( zDb==0 && zTab!=0 && cnt==0 && pParse->pTriggerTab!=0 ){
|
||||
if( zDb==0 && zTab!=0 && cntTab==0 && pParse->pTriggerTab!=0 ){
|
||||
int op = pParse->eTriggerOp;
|
||||
Table *pTab = 0;
|
||||
assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT );
|
||||
if( op!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){
|
||||
pExpr->iTable = 1;
|
||||
@@ -338,14 +337,15 @@ static int lookupName(
|
||||
}else if( op!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){
|
||||
pExpr->iTable = 0;
|
||||
pTab = pParse->pTriggerTab;
|
||||
}else{
|
||||
pTab = 0;
|
||||
}
|
||||
|
||||
if( pTab ){
|
||||
int iCol;
|
||||
pSchema = pTab->pSchema;
|
||||
cntTab++;
|
||||
for(iCol=0; iCol<pTab->nCol; iCol++){
|
||||
Column *pCol = &pTab->aCol[iCol];
|
||||
for(iCol=0, pCol=pTab->aCol; iCol<pTab->nCol; iCol++, pCol++){
|
||||
if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
|
||||
if( iCol==pTab->iPKey ){
|
||||
iCol = -1;
|
||||
@@ -353,8 +353,10 @@ static int lookupName(
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( iCol>=pTab->nCol && sqlite3IsRowid(zCol) ){
|
||||
iCol = -1; /* IMP: R-44911-55124 */
|
||||
if( iCol>=pTab->nCol && sqlite3IsRowid(zCol) && HasRowid(pTab) ){
|
||||
/* IMP: R-24309-18625 */
|
||||
/* IMP: R-44911-55124 */
|
||||
iCol = -1;
|
||||
}
|
||||
if( iCol<pTab->nCol ){
|
||||
cnt++;
|
||||
@@ -380,7 +382,8 @@ static int lookupName(
|
||||
/*
|
||||
** Perhaps the name is a reference to the ROWID
|
||||
*/
|
||||
if( cnt==0 && cntTab==1 && sqlite3IsRowid(zCol) ){
|
||||
if( cnt==0 && cntTab==1 && pMatch && sqlite3IsRowid(zCol)
|
||||
&& HasRowid(pMatch->pTab) ){
|
||||
cnt = 1;
|
||||
pExpr->iColumn = -1; /* IMP: R-44911-55124 */
|
||||
pExpr->affinity = SQLITE_AFF_INTEGER;
|
||||
@@ -570,6 +573,19 @@ static void notValidCheckConstraint(
|
||||
# define notValidCheckConstraint(P,N,M)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Expression p should encode a floating point value between 1.0 and 0.0.
|
||||
** Return 1024 times this value. Or return -1 if p is not a floating point
|
||||
** value between 1.0 and 0.0.
|
||||
*/
|
||||
static int exprProbability(Expr *p){
|
||||
double r = -1.0;
|
||||
if( p->op!=TK_FLOAT ) return -1;
|
||||
sqlite3AtoF(p->u.zToken, &r, sqlite3Strlen30(p->u.zToken), SQLITE_UTF8);
|
||||
assert( r>=0.0 );
|
||||
if( r>1.0 ) return -1;
|
||||
return (int)(r*1000.0);
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is callback for sqlite3WalkExpr().
|
||||
@@ -591,7 +607,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
pParse = pNC->pParse;
|
||||
assert( pParse==pWalker->pParse );
|
||||
|
||||
if( ExprHasAnyProperty(pExpr, EP_Resolved) ) return WRC_Prune;
|
||||
if( ExprHasProperty(pExpr, EP_Resolved) ) return WRC_Prune;
|
||||
ExprSetProperty(pExpr, EP_Resolved);
|
||||
#ifndef NDEBUG
|
||||
if( pNC->pSrcList && pNC->pSrcList->nAlloc>0 ){
|
||||
@@ -655,7 +671,6 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
|
||||
/* Resolve function names
|
||||
*/
|
||||
case TK_CONST_FUNC:
|
||||
case TK_FUNCTION: {
|
||||
ExprList *pList = pExpr->x.pList; /* The argument list */
|
||||
int n = pList ? pList->nExpr : 0; /* Number of arguments */
|
||||
@@ -668,7 +683,6 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
FuncDef *pDef; /* Information about the function */
|
||||
u8 enc = ENC(pParse->db); /* The database encoding */
|
||||
|
||||
testcase( pExpr->op==TK_CONST_FUNC );
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
notValidPartIdxWhere(pParse, pNC, "functions");
|
||||
zId = pExpr->u.zToken;
|
||||
@@ -683,6 +697,23 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
}
|
||||
}else{
|
||||
is_agg = pDef->xFunc==0;
|
||||
if( pDef->funcFlags & SQLITE_FUNC_UNLIKELY ){
|
||||
ExprSetProperty(pExpr, EP_Unlikely|EP_Skip);
|
||||
if( n==2 ){
|
||||
pExpr->iTable = exprProbability(pList->a[1].pExpr);
|
||||
if( pExpr->iTable<0 ){
|
||||
sqlite3ErrorMsg(pParse, "second argument to likelihood() must be a "
|
||||
"constant between 0.0 and 1.0");
|
||||
pNC->nErr++;
|
||||
}
|
||||
}else{
|
||||
/* EVIDENCE-OF: R-61304-29449 The unlikely(X) function is equivalent to
|
||||
** likelihood(X, 0.0625).
|
||||
** EVIDENCE-OF: R-01283-11636 The unlikely(X) function is short-hand for
|
||||
** likelihood(X,0.0625). */
|
||||
pExpr->iTable = 62; /* TUNING: Default 2nd arg to unlikely() is 0.0625 */
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
if( pDef ){
|
||||
@@ -696,6 +727,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
pExpr->op = TK_NULL;
|
||||
return WRC_Prune;
|
||||
}
|
||||
if( pDef->funcFlags & SQLITE_FUNC_CONSTANT ) ExprSetProperty(pExpr,EP_Constant);
|
||||
}
|
||||
#endif
|
||||
if( is_agg && (pNC->ncFlags & NC_AllowAgg)==0 ){
|
||||
@@ -947,7 +979,7 @@ static int resolveCompoundOrderBy(
|
||||
pItem->pExpr->pLeft = pNew;
|
||||
}
|
||||
sqlite3ExprDelete(db, pE);
|
||||
pItem->iOrderByCol = (u16)iCol;
|
||||
pItem->u.x.iOrderByCol = (u16)iCol;
|
||||
pItem->done = 1;
|
||||
}else{
|
||||
moreToDo = 1;
|
||||
@@ -968,8 +1000,8 @@ static int resolveCompoundOrderBy(
|
||||
/*
|
||||
** Check every term in the ORDER BY or GROUP BY clause pOrderBy of
|
||||
** the SELECT statement pSelect. If any term is reference to a
|
||||
** result set expression (as determined by the ExprList.a.iOrderByCol field)
|
||||
** then convert that term into a copy of the corresponding result set
|
||||
** result set expression (as determined by the ExprList.a.u.x.iOrderByCol
|
||||
** field) then convert that term into a copy of the corresponding result set
|
||||
** column.
|
||||
**
|
||||
** If any errors are detected, add an error message to pParse and
|
||||
@@ -996,12 +1028,12 @@ int sqlite3ResolveOrderGroupBy(
|
||||
pEList = pSelect->pEList;
|
||||
assert( pEList!=0 ); /* sqlite3SelectNew() guarantees this */
|
||||
for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
|
||||
if( pItem->iOrderByCol ){
|
||||
if( pItem->iOrderByCol>pEList->nExpr ){
|
||||
if( pItem->u.x.iOrderByCol ){
|
||||
if( pItem->u.x.iOrderByCol>pEList->nExpr ){
|
||||
resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr);
|
||||
return 1;
|
||||
}
|
||||
resolveAlias(pParse, pEList, pItem->iOrderByCol-1, pItem->pExpr, zType,0);
|
||||
resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol-1, pItem->pExpr, zType,0);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -1050,7 +1082,7 @@ static int resolveOrderGroupBy(
|
||||
** a copy of the iCol-th result-set column. The subsequent call to
|
||||
** sqlite3ResolveOrderGroupBy() will convert the expression to a
|
||||
** copy of the iCol-th result-set expression. */
|
||||
pItem->iOrderByCol = (u16)iCol;
|
||||
pItem->u.x.iOrderByCol = (u16)iCol;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -1062,18 +1094,18 @@ static int resolveOrderGroupBy(
|
||||
resolveOutOfRangeError(pParse, zType, i+1, nResult);
|
||||
return 1;
|
||||
}
|
||||
pItem->iOrderByCol = (u16)iCol;
|
||||
pItem->u.x.iOrderByCol = (u16)iCol;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Otherwise, treat the ORDER BY term as an ordinary expression */
|
||||
pItem->iOrderByCol = 0;
|
||||
pItem->u.x.iOrderByCol = 0;
|
||||
if( sqlite3ResolveExprNames(pNC, pE) ){
|
||||
return 1;
|
||||
}
|
||||
for(j=0; j<pSelect->pEList->nExpr; j++){
|
||||
if( sqlite3ExprCompare(pE, pSelect->pEList->a[j].pExpr, -1)==0 ){
|
||||
pItem->iOrderByCol = j+1;
|
||||
pItem->u.x.iOrderByCol = j+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+831
-364
File diff suppressed because it is too large
Load Diff
+513
-55
File diff suppressed because it is too large
Load Diff
+154
-65
@@ -365,7 +365,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
|
||||
** <ul>
|
||||
** <li> The application must insure that the 1st parameter to sqlite3_exec()
|
||||
** is a valid and open [database connection].
|
||||
** <li> The application must not close [database connection] specified by
|
||||
** <li> The application must not close the [database connection] specified by
|
||||
** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running.
|
||||
** <li> The application must not modify the SQL statement text passed into
|
||||
** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running.
|
||||
@@ -442,7 +442,7 @@ int sqlite3_exec(
|
||||
** [sqlite3_extended_result_codes()] API.
|
||||
**
|
||||
** Some of the available extended result codes are listed here.
|
||||
** One may expect the number of extended result codes will be expand
|
||||
** One may expect the number of extended result codes will increase
|
||||
** over time. Software that uses extended result codes should expect
|
||||
** to see new result codes in future releases of SQLite.
|
||||
**
|
||||
@@ -474,16 +474,19 @@ int sqlite3_exec(
|
||||
#define SQLITE_IOERR_DELETE_NOENT (SQLITE_IOERR | (23<<8))
|
||||
#define SQLITE_IOERR_MMAP (SQLITE_IOERR | (24<<8))
|
||||
#define SQLITE_IOERR_GETTEMPPATH (SQLITE_IOERR | (25<<8))
|
||||
#define SQLITE_IOERR_CONVPATH (SQLITE_IOERR | (26<<8))
|
||||
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
|
||||
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
|
||||
#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
|
||||
#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))
|
||||
#define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8))
|
||||
#define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8))
|
||||
#define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8))
|
||||
#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8))
|
||||
#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8))
|
||||
#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8))
|
||||
#define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8))
|
||||
#define SQLITE_READONLY_DBMOVED (SQLITE_READONLY | (4<<8))
|
||||
#define SQLITE_ABORT_ROLLBACK (SQLITE_ABORT | (2<<8))
|
||||
#define SQLITE_CONSTRAINT_CHECK (SQLITE_CONSTRAINT | (1<<8))
|
||||
#define SQLITE_CONSTRAINT_COMMITHOOK (SQLITE_CONSTRAINT | (2<<8))
|
||||
@@ -494,6 +497,7 @@ int sqlite3_exec(
|
||||
#define SQLITE_CONSTRAINT_TRIGGER (SQLITE_CONSTRAINT | (7<<8))
|
||||
#define SQLITE_CONSTRAINT_UNIQUE (SQLITE_CONSTRAINT | (8<<8))
|
||||
#define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8))
|
||||
#define SQLITE_CONSTRAINT_ROWID (SQLITE_CONSTRAINT |(10<<8))
|
||||
#define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8))
|
||||
#define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8))
|
||||
#define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8))
|
||||
@@ -550,7 +554,8 @@ int sqlite3_exec(
|
||||
** after reboot following a crash or power loss, the only bytes in a
|
||||
** file that were written at the application level might have changed
|
||||
** and that adjacent bytes, even bytes within the same sector are
|
||||
** guaranteed to be unchanged.
|
||||
** guaranteed to be unchanged. The SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN
|
||||
** flag indicate that a file cannot be deleted when open.
|
||||
*/
|
||||
#define SQLITE_IOCAP_ATOMIC 0x00000001
|
||||
#define SQLITE_IOCAP_ATOMIC512 0x00000002
|
||||
@@ -781,15 +786,29 @@ struct sqlite3_io_methods {
|
||||
** additional information.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_SYNC_OMITTED]]
|
||||
** ^(The [SQLITE_FCNTL_SYNC_OMITTED] opcode is generated internally by
|
||||
** SQLite and sent to all VFSes in place of a call to the xSync method
|
||||
** when the database connection has [PRAGMA synchronous] set to OFF.)^
|
||||
** Some specialized VFSes need this signal in order to operate correctly
|
||||
** when [PRAGMA synchronous | PRAGMA synchronous=OFF] is set, but most
|
||||
** VFSes do not need this signal and should silently ignore this opcode.
|
||||
** Applications should not call [sqlite3_file_control()] with this
|
||||
** opcode as doing so may disrupt the operation of the specialized VFSes
|
||||
** that do require it.
|
||||
** No longer in use.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_SYNC]]
|
||||
** The [SQLITE_FCNTL_SYNC] opcode is generated internally by SQLite and
|
||||
** sent to the VFS immediately before the xSync method is invoked on a
|
||||
** database file descriptor. Or, if the xSync method is not invoked
|
||||
** because the user has configured SQLite with
|
||||
** [PRAGMA synchronous | PRAGMA synchronous=OFF] it is invoked in place
|
||||
** of the xSync method. In most cases, the pointer argument passed with
|
||||
** this file-control is NULL. However, if the database file is being synced
|
||||
** as part of a multi-database commit, the argument points to a nul-terminated
|
||||
** string containing the transactions master-journal file name. VFSes that
|
||||
** do not need this signal should silently ignore this opcode. Applications
|
||||
** should not call [sqlite3_file_control()] with this opcode as doing so may
|
||||
** disrupt the operation of the specialized VFSes that do require it.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_COMMIT_PHASETWO]]
|
||||
** The [SQLITE_FCNTL_COMMIT_PHASETWO] opcode is generated internally by SQLite
|
||||
** and sent to the VFS after a transaction has been committed immediately
|
||||
** but before the database is unlocked. VFSes that do not need this signal
|
||||
** should silently ignore this opcode. Applications should not call
|
||||
** [sqlite3_file_control()] with this opcode as doing so may disrupt the
|
||||
** operation of the specialized VFSes that do require it.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_WIN32_AV_RETRY]]
|
||||
** ^The [SQLITE_FCNTL_WIN32_AV_RETRY] opcode is used to configure automatic
|
||||
@@ -905,6 +924,20 @@ struct sqlite3_io_methods {
|
||||
** can be queried by passing in a pointer to a negative number. This
|
||||
** file-control is used internally to implement [PRAGMA mmap_size].
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_TRACE]]
|
||||
** The [SQLITE_FCNTL_TRACE] file control provides advisory information
|
||||
** to the VFS about what the higher layers of the SQLite stack are doing.
|
||||
** This file control is used by some VFS activity tracing [shims].
|
||||
** The argument is a zero-terminated string. Higher layers in the
|
||||
** SQLite stack may generate instances of this file control if
|
||||
** the [SQLITE_USE_FCNTL_TRACE] compile-time option is enabled.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_HAS_MOVED]]
|
||||
** The [SQLITE_FCNTL_HAS_MOVED] file control interprets its argument as a
|
||||
** pointer to an integer and it writes a boolean into that integer depending
|
||||
** on whether or not the file has been renamed, moved, or deleted since it
|
||||
** was first opened.
|
||||
**
|
||||
** </ul>
|
||||
*/
|
||||
#define SQLITE_FCNTL_LOCKSTATE 1
|
||||
@@ -924,6 +957,10 @@ struct sqlite3_io_methods {
|
||||
#define SQLITE_FCNTL_BUSYHANDLER 15
|
||||
#define SQLITE_FCNTL_TEMPFILENAME 16
|
||||
#define SQLITE_FCNTL_MMAP_SIZE 18
|
||||
#define SQLITE_FCNTL_TRACE 19
|
||||
#define SQLITE_FCNTL_HAS_MOVED 20
|
||||
#define SQLITE_FCNTL_SYNC 21
|
||||
#define SQLITE_FCNTL_COMMIT_PHASETWO 22
|
||||
|
||||
/*
|
||||
** CAPI3REF: Mutex Handle
|
||||
@@ -1368,7 +1405,7 @@ int sqlite3_db_config(sqlite3*, int op, ...);
|
||||
** or [sqlite3_realloc()] first calls xRoundup. If xRoundup returns 0,
|
||||
** that causes the corresponding memory allocation to fail.
|
||||
**
|
||||
** The xInit method initializes the memory allocator. (For example,
|
||||
** The xInit method initializes the memory allocator. For example,
|
||||
** it might allocate any require mutexes or initialize internal data
|
||||
** structures. The xShutdown method is invoked (indirectly) by
|
||||
** [sqlite3_shutdown()] and should deallocate any resources acquired
|
||||
@@ -1610,27 +1647,27 @@ struct sqlite3_mem_methods {
|
||||
** function must be threadsafe. </dd>
|
||||
**
|
||||
** [[SQLITE_CONFIG_URI]] <dt>SQLITE_CONFIG_URI
|
||||
** <dd> This option takes a single argument of type int. If non-zero, then
|
||||
** <dd>^(This option takes a single argument of type int. If non-zero, then
|
||||
** URI handling is globally enabled. If the parameter is zero, then URI handling
|
||||
** is globally disabled. If URI handling is globally enabled, all filenames
|
||||
** is globally disabled.)^ ^If URI handling is globally enabled, all filenames
|
||||
** passed to [sqlite3_open()], [sqlite3_open_v2()], [sqlite3_open16()] or
|
||||
** specified as part of [ATTACH] commands are interpreted as URIs, regardless
|
||||
** of whether or not the [SQLITE_OPEN_URI] flag is set when the database
|
||||
** connection is opened. If it is globally disabled, filenames are
|
||||
** connection is opened. ^If it is globally disabled, filenames are
|
||||
** only interpreted as URIs if the SQLITE_OPEN_URI flag is set when the
|
||||
** database connection is opened. By default, URI handling is globally
|
||||
** database connection is opened. ^(By default, URI handling is globally
|
||||
** disabled. The default value may be changed by compiling with the
|
||||
** [SQLITE_USE_URI] symbol defined.
|
||||
** [SQLITE_USE_URI] symbol defined.)^
|
||||
**
|
||||
** [[SQLITE_CONFIG_COVERING_INDEX_SCAN]] <dt>SQLITE_CONFIG_COVERING_INDEX_SCAN
|
||||
** <dd> This option takes a single integer argument which is interpreted as
|
||||
** <dd>^This option takes a single integer argument which is interpreted as
|
||||
** a boolean in order to enable or disable the use of covering indices for
|
||||
** full table scans in the query optimizer. The default setting is determined
|
||||
** full table scans in the query optimizer. ^The default setting is determined
|
||||
** by the [SQLITE_ALLOW_COVERING_INDEX_SCAN] compile-time option, or is "on"
|
||||
** if that compile-time option is omitted.
|
||||
** The ability to disable the use of covering indices for full table scans
|
||||
** is because some incorrectly coded legacy applications might malfunction
|
||||
** malfunction when the optimization is enabled. Providing the ability to
|
||||
** when the optimization is enabled. Providing the ability to
|
||||
** disable the optimization allows the older, buggy application code to work
|
||||
** without change even with newer versions of SQLite.
|
||||
**
|
||||
@@ -1659,17 +1696,24 @@ struct sqlite3_mem_methods {
|
||||
**
|
||||
** [[SQLITE_CONFIG_MMAP_SIZE]]
|
||||
** <dt>SQLITE_CONFIG_MMAP_SIZE
|
||||
** <dd>SQLITE_CONFIG_MMAP_SIZE takes two 64-bit integer (sqlite3_int64) values
|
||||
** <dd>^SQLITE_CONFIG_MMAP_SIZE takes two 64-bit integer (sqlite3_int64) values
|
||||
** that are the default mmap size limit (the default setting for
|
||||
** [PRAGMA mmap_size]) and the maximum allowed mmap size limit.
|
||||
** The default setting can be overridden by each database connection using
|
||||
** ^The default setting can be overridden by each database connection using
|
||||
** either the [PRAGMA mmap_size] command, or by using the
|
||||
** [SQLITE_FCNTL_MMAP_SIZE] file control. The maximum allowed mmap size
|
||||
** [SQLITE_FCNTL_MMAP_SIZE] file control. ^(The maximum allowed mmap size
|
||||
** cannot be changed at run-time. Nor may the maximum allowed mmap size
|
||||
** exceed the compile-time maximum mmap size set by the
|
||||
** [SQLITE_MAX_MMAP_SIZE] compile-time option.
|
||||
** If either argument to this option is negative, then that argument is
|
||||
** [SQLITE_MAX_MMAP_SIZE] compile-time option.)^
|
||||
** ^If either argument to this option is negative, then that argument is
|
||||
** changed to its compile-time default.
|
||||
**
|
||||
** [[SQLITE_CONFIG_WIN32_HEAPSIZE]]
|
||||
** <dt>SQLITE_CONFIG_WIN32_HEAPSIZE
|
||||
** <dd>^This option is only available if SQLite is compiled for Windows
|
||||
** with the [SQLITE_WIN32_MALLOC] pre-processor macro defined.
|
||||
** SQLITE_CONFIG_WIN32_HEAPSIZE takes a 32-bit unsigned integer value
|
||||
** that specifies the maximum size of the created heap.
|
||||
** </dl>
|
||||
*/
|
||||
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
|
||||
@@ -1694,6 +1738,7 @@ struct sqlite3_mem_methods {
|
||||
#define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */
|
||||
#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */
|
||||
#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */
|
||||
#define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */
|
||||
|
||||
/*
|
||||
** CAPI3REF: Database Connection Configuration Options
|
||||
@@ -1770,19 +1815,21 @@ int sqlite3_extended_result_codes(sqlite3*, int onoff);
|
||||
/*
|
||||
** CAPI3REF: Last Insert Rowid
|
||||
**
|
||||
** ^Each entry in an SQLite table has a unique 64-bit signed
|
||||
** ^Each entry in most SQLite tables (except for [WITHOUT ROWID] tables)
|
||||
** has a unique 64-bit signed
|
||||
** integer key called the [ROWID | "rowid"]. ^The rowid is always available
|
||||
** as an undeclared column named ROWID, OID, or _ROWID_ as long as those
|
||||
** names are not also used by explicitly declared columns. ^If
|
||||
** the table has a column of type [INTEGER PRIMARY KEY] then that column
|
||||
** is another alias for the rowid.
|
||||
**
|
||||
** ^This routine returns the [rowid] of the most recent
|
||||
** successful [INSERT] into the database from the [database connection]
|
||||
** in the first argument. ^As of SQLite version 3.7.7, this routines
|
||||
** records the last insert rowid of both ordinary tables and [virtual tables].
|
||||
** ^If no successful [INSERT]s
|
||||
** have ever occurred on that database connection, zero is returned.
|
||||
** ^The sqlite3_last_insert_rowid(D) interface returns the [rowid] of the
|
||||
** most recent successful [INSERT] into a rowid table or [virtual table]
|
||||
** on database connection D.
|
||||
** ^Inserts into [WITHOUT ROWID] tables are not recorded.
|
||||
** ^If no successful [INSERT]s into rowid tables
|
||||
** have ever occurred on the database connection D,
|
||||
** then sqlite3_last_insert_rowid(D) returns zero.
|
||||
**
|
||||
** ^(If an [INSERT] occurs within a trigger or within a [virtual table]
|
||||
** method, then this routine will return the [rowid] of the inserted
|
||||
@@ -2348,11 +2395,13 @@ sqlite3_int64 sqlite3_memory_highwater(int resetFlag);
|
||||
** applications to access the same PRNG for other purposes.
|
||||
**
|
||||
** ^A call to this routine stores N bytes of randomness into buffer P.
|
||||
** ^If N is less than one, then P can be a NULL pointer.
|
||||
**
|
||||
** ^The first time this routine is invoked (either internally or by
|
||||
** the application) the PRNG is seeded using randomness obtained
|
||||
** from the xRandomness method of the default [sqlite3_vfs] object.
|
||||
** ^On all subsequent invocations, the pseudo-randomness is generated
|
||||
** ^If this routine has not been previously called or if the previous
|
||||
** call had N less than one, then the PRNG is seeded using randomness
|
||||
** obtained from the xRandomness method of the default [sqlite3_vfs] object.
|
||||
** ^If the previous call to this routine had an N of 1 or more then
|
||||
** the pseudo-randomness is generated
|
||||
** internally and without recourse to the [sqlite3_vfs] xRandomness
|
||||
** method.
|
||||
*/
|
||||
@@ -2512,6 +2561,7 @@ int sqlite3_set_authorizer(
|
||||
#define SQLITE_FUNCTION 31 /* NULL Function Name */
|
||||
#define SQLITE_SAVEPOINT 32 /* Operation Savepoint Name */
|
||||
#define SQLITE_COPY 0 /* No longer used */
|
||||
#define SQLITE_RECURSIVE 33 /* NULL NULL */
|
||||
|
||||
/*
|
||||
** CAPI3REF: Tracing And Profiling Functions
|
||||
@@ -3092,7 +3142,6 @@ int sqlite3_limit(sqlite3*, int id, int newVal);
|
||||
** choice of query plan if the parameter is the left-hand side of a [LIKE]
|
||||
** or [GLOB] operator or if the parameter is compared to an indexed column
|
||||
** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled.
|
||||
** the
|
||||
** </li>
|
||||
** </ol>
|
||||
*/
|
||||
@@ -3754,19 +3803,19 @@ int sqlite3_data_count(sqlite3_stmt *pStmt);
|
||||
**
|
||||
** <tr><td> NULL <td> INTEGER <td> Result is 0
|
||||
** <tr><td> NULL <td> FLOAT <td> Result is 0.0
|
||||
** <tr><td> NULL <td> TEXT <td> Result is NULL pointer
|
||||
** <tr><td> NULL <td> BLOB <td> Result is NULL pointer
|
||||
** <tr><td> NULL <td> TEXT <td> Result is a NULL pointer
|
||||
** <tr><td> NULL <td> BLOB <td> Result is a NULL pointer
|
||||
** <tr><td> INTEGER <td> FLOAT <td> Convert from integer to float
|
||||
** <tr><td> INTEGER <td> TEXT <td> ASCII rendering of the integer
|
||||
** <tr><td> INTEGER <td> BLOB <td> Same as INTEGER->TEXT
|
||||
** <tr><td> FLOAT <td> INTEGER <td> Convert from float to integer
|
||||
** <tr><td> FLOAT <td> INTEGER <td> [CAST] to INTEGER
|
||||
** <tr><td> FLOAT <td> TEXT <td> ASCII rendering of the float
|
||||
** <tr><td> FLOAT <td> BLOB <td> Same as FLOAT->TEXT
|
||||
** <tr><td> TEXT <td> INTEGER <td> Use atoi()
|
||||
** <tr><td> TEXT <td> FLOAT <td> Use atof()
|
||||
** <tr><td> FLOAT <td> BLOB <td> [CAST] to BLOB
|
||||
** <tr><td> TEXT <td> INTEGER <td> [CAST] to INTEGER
|
||||
** <tr><td> TEXT <td> FLOAT <td> [CAST] to REAL
|
||||
** <tr><td> TEXT <td> BLOB <td> No change
|
||||
** <tr><td> BLOB <td> INTEGER <td> Convert to TEXT then use atoi()
|
||||
** <tr><td> BLOB <td> FLOAT <td> Convert to TEXT then use atof()
|
||||
** <tr><td> BLOB <td> INTEGER <td> [CAST] to INTEGER
|
||||
** <tr><td> BLOB <td> FLOAT <td> [CAST] to REAL
|
||||
** <tr><td> BLOB <td> TEXT <td> Add a zero terminator if needed
|
||||
** </table>
|
||||
** </blockquote>)^
|
||||
@@ -3822,7 +3871,7 @@ int sqlite3_data_count(sqlite3_stmt *pStmt);
|
||||
** described above, or until [sqlite3_step()] or [sqlite3_reset()] or
|
||||
** [sqlite3_finalize()] is called. ^The memory space used to hold strings
|
||||
** and BLOBs is freed automatically. Do <b>not</b> pass the pointers returned
|
||||
** [sqlite3_column_blob()], [sqlite3_column_text()], etc. into
|
||||
** from [sqlite3_column_blob()], [sqlite3_column_text()], etc. into
|
||||
** [sqlite3_free()].
|
||||
**
|
||||
** ^(If a memory allocation error occurs during the evaluation of any
|
||||
@@ -3931,15 +3980,24 @@ int sqlite3_reset(sqlite3_stmt *pStmt);
|
||||
**
|
||||
** ^The fourth parameter, eTextRep, specifies what
|
||||
** [SQLITE_UTF8 | text encoding] this SQL function prefers for
|
||||
** its parameters. Every SQL function implementation must be able to work
|
||||
** with UTF-8, UTF-16le, or UTF-16be. But some implementations may be
|
||||
** more efficient with one encoding than another. ^An application may
|
||||
** invoke sqlite3_create_function() or sqlite3_create_function16() multiple
|
||||
** times with the same function but with different values of eTextRep.
|
||||
** its parameters. The application should set this parameter to
|
||||
** [SQLITE_UTF16LE] if the function implementation invokes
|
||||
** [sqlite3_value_text16le()] on an input, or [SQLITE_UTF16BE] if the
|
||||
** implementation invokes [sqlite3_value_text16be()] on an input, or
|
||||
** [SQLITE_UTF16] if [sqlite3_value_text16()] is used, or [SQLITE_UTF8]
|
||||
** otherwise. ^The same SQL function may be registered multiple times using
|
||||
** different preferred text encodings, with different implementations for
|
||||
** each encoding.
|
||||
** ^When multiple implementations of the same function are available, SQLite
|
||||
** will pick the one that involves the least amount of data conversion.
|
||||
** If there is only a single implementation which does not care what text
|
||||
** encoding is used, then the fourth argument should be [SQLITE_ANY].
|
||||
**
|
||||
** ^The fourth parameter may optionally be ORed with [SQLITE_DETERMINISTIC]
|
||||
** to signal that the function will always return the same result given
|
||||
** the same inputs within a single SQL statement. Most SQL functions are
|
||||
** deterministic. The built-in [random()] SQL function is an example of a
|
||||
** function that is not deterministic. The SQLite query planner is able to
|
||||
** perform additional optimizations on deterministic functions, so use
|
||||
** of the [SQLITE_DETERMINISTIC] flag is recommended where possible.
|
||||
**
|
||||
** ^(The fifth parameter is an arbitrary pointer. The implementation of the
|
||||
** function can gain access to this pointer using [sqlite3_user_data()].)^
|
||||
@@ -4025,9 +4083,19 @@ int sqlite3_create_function_v2(
|
||||
#define SQLITE_UTF16LE 2
|
||||
#define SQLITE_UTF16BE 3
|
||||
#define SQLITE_UTF16 4 /* Use native byte order */
|
||||
#define SQLITE_ANY 5 /* sqlite3_create_function only */
|
||||
#define SQLITE_ANY 5 /* Deprecated */
|
||||
#define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */
|
||||
|
||||
/*
|
||||
** CAPI3REF: Function Flags
|
||||
**
|
||||
** These constants may be ORed together with the
|
||||
** [SQLITE_UTF8 | preferred text encoding] as the fourth argument
|
||||
** to [sqlite3_create_function()], [sqlite3_create_function16()], or
|
||||
** [sqlite3_create_function_v2()].
|
||||
*/
|
||||
#define SQLITE_DETERMINISTIC 0x800
|
||||
|
||||
/*
|
||||
** CAPI3REF: Deprecated Functions
|
||||
** DEPRECATED
|
||||
@@ -4799,12 +4867,13 @@ void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
|
||||
**
|
||||
** ^The sqlite3_update_hook() interface registers a callback function
|
||||
** with the [database connection] identified by the first argument
|
||||
** to be invoked whenever a row is updated, inserted or deleted.
|
||||
** to be invoked whenever a row is updated, inserted or deleted in
|
||||
** a rowid table.
|
||||
** ^Any callback set by a previous call to this function
|
||||
** for the same database connection is overridden.
|
||||
**
|
||||
** ^The second argument is a pointer to the function to invoke when a
|
||||
** row is updated, inserted or deleted.
|
||||
** row is updated, inserted or deleted in a rowid table.
|
||||
** ^The first argument to the callback is a copy of the third argument
|
||||
** to sqlite3_update_hook().
|
||||
** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
|
||||
@@ -4817,6 +4886,7 @@ void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
|
||||
**
|
||||
** ^(The update hook is not invoked when internal system tables are
|
||||
** modified (i.e. sqlite_master and sqlite_sequence).)^
|
||||
** ^The update hook is not invoked when [WITHOUT ROWID] tables are modified.
|
||||
**
|
||||
** ^In the current implementation, the update hook
|
||||
** is not invoked when duplication rows are deleted because of an
|
||||
@@ -4898,8 +4968,8 @@ int sqlite3_release_memory(int);
|
||||
**
|
||||
** ^The sqlite3_db_release_memory(D) interface attempts to free as much heap
|
||||
** memory as possible from database connection D. Unlike the
|
||||
** [sqlite3_release_memory()] interface, this interface is effect even
|
||||
** when then [SQLITE_ENABLE_MEMORY_MANAGEMENT] compile-time option is
|
||||
** [sqlite3_release_memory()] interface, this interface is in effect even
|
||||
** when the [SQLITE_ENABLE_MEMORY_MANAGEMENT] compile-time option is
|
||||
** omitted.
|
||||
**
|
||||
** See also: [sqlite3_release_memory()]
|
||||
@@ -5274,10 +5344,22 @@ struct sqlite3_module {
|
||||
** the correct order to satisfy the ORDER BY clause so that no separate
|
||||
** sorting step is required.
|
||||
**
|
||||
** ^The estimatedCost value is an estimate of the cost of doing the
|
||||
** particular lookup. A full scan of a table with N entries should have
|
||||
** a cost of N. A binary search of a table of N entries should have a
|
||||
** cost of approximately log(N).
|
||||
** ^The estimatedCost value is an estimate of the cost of a particular
|
||||
** strategy. A cost of N indicates that the cost of the strategy is similar
|
||||
** to a linear scan of an SQLite table with N rows. A cost of log(N)
|
||||
** indicates that the expense of the operation is similar to that of a
|
||||
** binary search on a unique indexed field of an SQLite table with N rows.
|
||||
**
|
||||
** ^The estimatedRows value is an estimate of the number of rows that
|
||||
** will be returned by the strategy.
|
||||
**
|
||||
** IMPORTANT: The estimatedRows field was added to the sqlite3_index_info
|
||||
** structure for SQLite version 3.8.2. If a virtual table extension is
|
||||
** used with an SQLite version earlier than 3.8.2, the results of attempting
|
||||
** to read or write the estimatedRows field are undefined (but are likely
|
||||
** to included crashing the application). The estimatedRows field should
|
||||
** therefore only be used if [sqlite3_libversion_number()] returns a
|
||||
** value greater than or equal to 3008002.
|
||||
*/
|
||||
struct sqlite3_index_info {
|
||||
/* Inputs */
|
||||
@@ -5302,7 +5384,9 @@ struct sqlite3_index_info {
|
||||
char *idxStr; /* String, possibly obtained from sqlite3_malloc */
|
||||
int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */
|
||||
int orderByConsumed; /* True if output is already ordered */
|
||||
double estimatedCost; /* Estimated cost of using this index */
|
||||
double estimatedCost; /* Estimated cost of using this index */
|
||||
/* Fields below are only available in SQLite 3.8.2 and later */
|
||||
sqlite3_int64 estimatedRows; /* Estimated number of rows returned */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -5506,6 +5590,9 @@ typedef struct sqlite3_blob sqlite3_blob;
|
||||
** interface. Use the [UPDATE] SQL command to change the size of a
|
||||
** blob.
|
||||
**
|
||||
** ^The [sqlite3_blob_open()] interface will fail for a [WITHOUT ROWID]
|
||||
** table. Incremental BLOB I/O is not possible on [WITHOUT ROWID] tables.
|
||||
**
|
||||
** ^The [sqlite3_bind_zeroblob()] and [sqlite3_result_zeroblob()] interfaces
|
||||
** and the built-in [zeroblob] SQL function can be used, if desired,
|
||||
** to create an empty, zero-filled blob in which to read or write using
|
||||
@@ -6029,7 +6116,9 @@ int sqlite3_test_control(int op, ...);
|
||||
#define SQLITE_TESTCTRL_SCRATCHMALLOC 17
|
||||
#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18
|
||||
#define SQLITE_TESTCTRL_EXPLAIN_STMT 19
|
||||
#define SQLITE_TESTCTRL_LAST 19
|
||||
#define SQLITE_TESTCTRL_NEVER_CORRUPT 20
|
||||
#define SQLITE_TESTCTRL_VDBE_COVERAGE 21
|
||||
#define SQLITE_TESTCTRL_LAST 21
|
||||
|
||||
/*
|
||||
** CAPI3REF: SQLite Runtime Status
|
||||
|
||||
+13
-9
@@ -17,7 +17,11 @@
|
||||
#include "winresrc.h"
|
||||
#else
|
||||
#include "windows.h"
|
||||
#endif
|
||||
#endif /* !defined(_WIN32_WCE) */
|
||||
|
||||
#if !defined(VS_FF_NONE)
|
||||
# define VS_FF_NONE 0x00000000L
|
||||
#endif /* !defined(VS_FF_NONE) */
|
||||
|
||||
#include "sqlite3.h"
|
||||
#include "sqlite3rc.h"
|
||||
@@ -26,10 +30,10 @@
|
||||
* English (U.S.) resources
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#if defined(_WIN32)
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
#endif /* _WIN32 */
|
||||
#endif /* defined(_WIN32) */
|
||||
|
||||
/*
|
||||
* Version
|
||||
@@ -38,14 +42,14 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION SQLITE_RESOURCE_VERSION
|
||||
PRODUCTVERSION SQLITE_RESOURCE_VERSION
|
||||
FILEFLAGSMASK 0x3F
|
||||
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
|
||||
#if defined(_DEBUG)
|
||||
FILEFLAGS 0x1L
|
||||
FILEFLAGS VS_FF_DEBUG
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEFLAGS VS_FF_NONE
|
||||
#endif /* defined(_DEBUG) */
|
||||
FILEOS VOS__WINDOWS32
|
||||
FILETYPE VFT_APP
|
||||
FILETYPE VFT_DLL
|
||||
FILESUBTYPE VFT2_UNKNOWN
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
@@ -64,6 +68,6 @@ BEGIN
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
VALUE "Translation", 0x409, 0x4b0
|
||||
END
|
||||
END
|
||||
|
||||
+452
-161
File diff suppressed because it is too large
Load Diff
+56
-51
@@ -424,13 +424,12 @@ static int safeToUseEvalObjv(Tcl_Interp *interp, Tcl_Obj *pCmd){
|
||||
*/
|
||||
static SqlFunc *findSqlFunc(SqliteDb *pDb, const char *zName){
|
||||
SqlFunc *p, *pNew;
|
||||
int i;
|
||||
pNew = (SqlFunc*)Tcl_Alloc( sizeof(*pNew) + strlen30(zName) + 1 );
|
||||
int nName = strlen30(zName);
|
||||
pNew = (SqlFunc*)Tcl_Alloc( sizeof(*pNew) + nName + 1 );
|
||||
pNew->zName = (char*)&pNew[1];
|
||||
for(i=0; zName[i]; i++){ pNew->zName[i] = tolower(zName[i]); }
|
||||
pNew->zName[i] = 0;
|
||||
memcpy(pNew->zName, zName, nName+1);
|
||||
for(p=pDb->pFunc; p; p=p->pNext){
|
||||
if( strcmp(p->zName, pNew->zName)==0 ){
|
||||
if( sqlite3_stricmp(p->zName, pNew->zName)==0 ){
|
||||
Tcl_Free((char*)pNew);
|
||||
return p;
|
||||
}
|
||||
@@ -874,7 +873,7 @@ static int auth_callback(
|
||||
const char *zArg3,
|
||||
const char *zArg4
|
||||
){
|
||||
char *zCode;
|
||||
const char *zCode;
|
||||
Tcl_DString str;
|
||||
int rc;
|
||||
const char *zReply;
|
||||
@@ -915,6 +914,7 @@ static int auth_callback(
|
||||
case SQLITE_DROP_VTABLE : zCode="SQLITE_DROP_VTABLE"; break;
|
||||
case SQLITE_FUNCTION : zCode="SQLITE_FUNCTION"; break;
|
||||
case SQLITE_SAVEPOINT : zCode="SQLITE_SAVEPOINT"; break;
|
||||
case SQLITE_RECURSIVE : zCode="SQLITE_RECURSIVE"; break;
|
||||
default : zCode="????"; break;
|
||||
}
|
||||
Tcl_DStringInit(&str);
|
||||
@@ -999,7 +999,7 @@ static int DbTransPostCmd(
|
||||
Tcl_Interp *interp, /* Tcl interpreter */
|
||||
int result /* Result of evaluating SCRIPT */
|
||||
){
|
||||
static const char *azEnd[] = {
|
||||
static const char *const azEnd[] = {
|
||||
"RELEASE _tcl_transaction", /* rc==TCL_ERROR, nTransaction!=0 */
|
||||
"COMMIT", /* rc!=TCL_ERROR, nTransaction==0 */
|
||||
"ROLLBACK TO _tcl_transaction ; RELEASE _tcl_transaction",
|
||||
@@ -1025,7 +1025,7 @@ static int DbTransPostCmd(
|
||||
** this method's logic. Not clear how this would be best handled.
|
||||
*/
|
||||
if( rc!=TCL_ERROR ){
|
||||
Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), 0);
|
||||
Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), (char*)0);
|
||||
rc = TCL_ERROR;
|
||||
}
|
||||
sqlite3_exec(pDb->db, "ROLLBACK", 0, 0, 0);
|
||||
@@ -1083,13 +1083,14 @@ static int dbPrepareAndBind(
|
||||
int nSql; /* Length of zSql in bytes */
|
||||
int nVar; /* Number of variables in statement */
|
||||
int iParm = 0; /* Next free entry in apParm */
|
||||
char c;
|
||||
int i;
|
||||
Tcl_Interp *interp = pDb->interp;
|
||||
|
||||
*ppPreStmt = 0;
|
||||
|
||||
/* Trim spaces from the start of zSql and calculate the remaining length. */
|
||||
while( isspace(zSql[0]) ){ zSql++; }
|
||||
while( (c = zSql[0])==' ' || c=='\t' || c=='\r' || c=='\n' ){ zSql++; }
|
||||
nSql = strlen30(zSql);
|
||||
|
||||
for(pPreStmt = pDb->stmtList; pPreStmt; pPreStmt=pPreStmt->pNext){
|
||||
@@ -1530,9 +1531,9 @@ static int DbUseNre(void){
|
||||
*/
|
||||
# define SQLITE_TCL_NRE 0
|
||||
# define DbUseNre() 0
|
||||
# define Tcl_NRAddCallback(a,b,c,d,e,f) 0
|
||||
# define Tcl_NRAddCallback(a,b,c,d,e,f) (void)0
|
||||
# define Tcl_NREvalObj(a,b,c) 0
|
||||
# define Tcl_NRCreateCommand(a,b,c,d,e,f) 0
|
||||
# define Tcl_NRCreateCommand(a,b,c,d,e,f) (void)0
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -1674,7 +1675,8 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
*/
|
||||
case DB_AUTHORIZER: {
|
||||
#ifdef SQLITE_OMIT_AUTHORIZATION
|
||||
Tcl_AppendResult(interp, "authorization not available in this build", 0);
|
||||
Tcl_AppendResult(interp, "authorization not available in this build",
|
||||
(char*)0);
|
||||
return TCL_ERROR;
|
||||
#else
|
||||
if( objc>3 ){
|
||||
@@ -1682,7 +1684,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
return TCL_ERROR;
|
||||
}else if( objc==2 ){
|
||||
if( pDb->zAuth ){
|
||||
Tcl_AppendResult(interp, pDb->zAuth, 0);
|
||||
Tcl_AppendResult(interp, pDb->zAuth, (char*)0);
|
||||
}
|
||||
}else{
|
||||
char *zAuth;
|
||||
@@ -1768,7 +1770,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
return TCL_ERROR;
|
||||
}else if( objc==2 ){
|
||||
if( pDb->zBusy ){
|
||||
Tcl_AppendResult(interp, pDb->zBusy, 0);
|
||||
Tcl_AppendResult(interp, pDb->zBusy, (char*)0);
|
||||
}
|
||||
}else{
|
||||
char *zBusy;
|
||||
@@ -1822,7 +1824,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
}else{
|
||||
if( TCL_ERROR==Tcl_GetIntFromObj(interp, objv[3], &n) ){
|
||||
Tcl_AppendResult( interp, "cannot convert \"",
|
||||
Tcl_GetStringFromObj(objv[3],0), "\" to integer", 0);
|
||||
Tcl_GetStringFromObj(objv[3],0), "\" to integer", (char*)0);
|
||||
return TCL_ERROR;
|
||||
}else{
|
||||
if( n<0 ){
|
||||
@@ -1836,7 +1838,8 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
}
|
||||
}else{
|
||||
Tcl_AppendResult( interp, "bad option \"",
|
||||
Tcl_GetStringFromObj(objv[2],0), "\": must be flush or size", 0);
|
||||
Tcl_GetStringFromObj(objv[2],0), "\": must be flush or size",
|
||||
(char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
break;
|
||||
@@ -1933,10 +1936,10 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
return TCL_ERROR;
|
||||
}else if( objc==2 ){
|
||||
if( pDb->zCommit ){
|
||||
Tcl_AppendResult(interp, pDb->zCommit, 0);
|
||||
Tcl_AppendResult(interp, pDb->zCommit, (char*)0);
|
||||
}
|
||||
}else{
|
||||
char *zCommit;
|
||||
const char *zCommit;
|
||||
int len;
|
||||
if( pDb->zCommit ){
|
||||
Tcl_Free(pDb->zCommit);
|
||||
@@ -2009,14 +2012,14 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
char *zSql; /* An SQL statement */
|
||||
char *zLine; /* A single line of input from the file */
|
||||
char **azCol; /* zLine[] broken up into columns */
|
||||
char *zCommit; /* How to commit changes */
|
||||
const char *zCommit; /* How to commit changes */
|
||||
FILE *in; /* The input file */
|
||||
int lineno = 0; /* Line number of input file */
|
||||
char zLineNum[80]; /* Line number print buffer */
|
||||
Tcl_Obj *pResult; /* interp result */
|
||||
|
||||
char *zSep;
|
||||
char *zNull;
|
||||
const char *zSep;
|
||||
const char *zNull;
|
||||
if( objc<5 || objc>7 ){
|
||||
Tcl_WrongNumArgs(interp, 2, objv,
|
||||
"CONFLICT-ALGORITHM TABLE FILENAME ?SEPARATOR? ?NULLINDICATOR?");
|
||||
@@ -2038,7 +2041,8 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
nSep = strlen30(zSep);
|
||||
nNull = strlen30(zNull);
|
||||
if( nSep==0 ){
|
||||
Tcl_AppendResult(interp,"Error: non-null separator required for copy",0);
|
||||
Tcl_AppendResult(interp,"Error: non-null separator required for copy",
|
||||
(char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if(strcmp(zConflict, "rollback") != 0 &&
|
||||
@@ -2048,19 +2052,19 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
strcmp(zConflict, "replace" ) != 0 ) {
|
||||
Tcl_AppendResult(interp, "Error: \"", zConflict,
|
||||
"\", conflict-algorithm must be one of: rollback, "
|
||||
"abort, fail, ignore, or replace", 0);
|
||||
"abort, fail, ignore, or replace", (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zSql = sqlite3_mprintf("SELECT * FROM '%q'", zTable);
|
||||
if( zSql==0 ){
|
||||
Tcl_AppendResult(interp, "Error: no such table: ", zTable, 0);
|
||||
Tcl_AppendResult(interp, "Error: no such table: ", zTable, (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
nByte = strlen30(zSql);
|
||||
rc = sqlite3_prepare(pDb->db, zSql, -1, &pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( rc ){
|
||||
Tcl_AppendResult(interp, "Error: ", sqlite3_errmsg(pDb->db), 0);
|
||||
Tcl_AppendResult(interp, "Error: ", sqlite3_errmsg(pDb->db), (char*)0);
|
||||
nCol = 0;
|
||||
}else{
|
||||
nCol = sqlite3_column_count(pStmt);
|
||||
@@ -2071,7 +2075,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
}
|
||||
zSql = malloc( nByte + 50 + nCol*2 );
|
||||
if( zSql==0 ) {
|
||||
Tcl_AppendResult(interp, "Error: can't malloc()", 0);
|
||||
Tcl_AppendResult(interp, "Error: can't malloc()", (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
sqlite3_snprintf(nByte+50, zSql, "INSERT OR %q INTO '%q' VALUES(?",
|
||||
@@ -2086,7 +2090,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
rc = sqlite3_prepare(pDb->db, zSql, -1, &pStmt, 0);
|
||||
free(zSql);
|
||||
if( rc ){
|
||||
Tcl_AppendResult(interp, "Error: ", sqlite3_errmsg(pDb->db), 0);
|
||||
Tcl_AppendResult(interp, "Error: ", sqlite3_errmsg(pDb->db), (char*)0);
|
||||
sqlite3_finalize(pStmt);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
@@ -2098,7 +2102,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
}
|
||||
azCol = malloc( sizeof(azCol[0])*(nCol+1) );
|
||||
if( azCol==0 ) {
|
||||
Tcl_AppendResult(interp, "Error: can't malloc()", 0);
|
||||
Tcl_AppendResult(interp, "Error: can't malloc()", (char*)0);
|
||||
fclose(in);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
@@ -2126,7 +2130,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
sqlite3_snprintf(nErr, zErr,
|
||||
"Error: %s line %d: expected %d columns of data but found %d",
|
||||
zFile, lineno, nCol, i+1);
|
||||
Tcl_AppendResult(interp, zErr, 0);
|
||||
Tcl_AppendResult(interp, zErr, (char*)0);
|
||||
free(zErr);
|
||||
}
|
||||
zCommit = "ROLLBACK";
|
||||
@@ -2146,7 +2150,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
rc = sqlite3_reset(pStmt);
|
||||
free(zLine);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp,"Error: ", sqlite3_errmsg(pDb->db), 0);
|
||||
Tcl_AppendResult(interp,"Error: ", sqlite3_errmsg(pDb->db), (char*)0);
|
||||
zCommit = "ROLLBACK";
|
||||
break;
|
||||
}
|
||||
@@ -2164,7 +2168,8 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
}else{
|
||||
/* failure, append lineno where failed */
|
||||
sqlite3_snprintf(sizeof(zLineNum), zLineNum,"%d",lineno);
|
||||
Tcl_AppendResult(interp,", failed while processing line: ",zLineNum,0);
|
||||
Tcl_AppendResult(interp,", failed while processing line: ",zLineNum,
|
||||
(char*)0);
|
||||
rc = TCL_ERROR;
|
||||
}
|
||||
break;
|
||||
@@ -2190,7 +2195,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
break;
|
||||
#else
|
||||
Tcl_AppendResult(interp, "extension loading is turned off at compile-time",
|
||||
0);
|
||||
(char*)0);
|
||||
return TCL_ERROR;
|
||||
#endif
|
||||
}
|
||||
@@ -2348,7 +2353,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
*/
|
||||
case DB_INCRBLOB: {
|
||||
#ifdef SQLITE_OMIT_INCRBLOB
|
||||
Tcl_AppendResult(interp, "incrblob not available in this build", 0);
|
||||
Tcl_AppendResult(interp, "incrblob not available in this build", (char*)0);
|
||||
return TCL_ERROR;
|
||||
#else
|
||||
int isReadonly = 0;
|
||||
@@ -2455,7 +2460,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
case DB_PROGRESS: {
|
||||
if( objc==2 ){
|
||||
if( pDb->zProgress ){
|
||||
Tcl_AppendResult(interp, pDb->zProgress, 0);
|
||||
Tcl_AppendResult(interp, pDb->zProgress, (char*)0);
|
||||
}
|
||||
}else if( objc==4 ){
|
||||
char *zProgress;
|
||||
@@ -2501,7 +2506,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
return TCL_ERROR;
|
||||
}else if( objc==2 ){
|
||||
if( pDb->zProfile ){
|
||||
Tcl_AppendResult(interp, pDb->zProfile, 0);
|
||||
Tcl_AppendResult(interp, pDb->zProfile, (char*)0);
|
||||
}
|
||||
}else{
|
||||
char *zProfile;
|
||||
@@ -2546,7 +2551,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
pKey = Tcl_GetByteArrayFromObj(objv[2], &nKey);
|
||||
rc = sqlite3_rekey(pDb->db, pKey, nKey);
|
||||
if( rc ){
|
||||
Tcl_AppendResult(interp, sqlite3_errstr(rc), 0);
|
||||
Tcl_AppendResult(interp, sqlite3_errstr(rc), (char*)0);
|
||||
rc = TCL_ERROR;
|
||||
}
|
||||
#endif
|
||||
@@ -2687,7 +2692,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
return TCL_ERROR;
|
||||
}else if( objc==2 ){
|
||||
if( pDb->zTrace ){
|
||||
Tcl_AppendResult(interp, pDb->zTrace, 0);
|
||||
Tcl_AppendResult(interp, pDb->zTrace, (char*)0);
|
||||
}
|
||||
}else{
|
||||
char *zTrace;
|
||||
@@ -2758,7 +2763,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
rc = sqlite3_exec(pDb->db, zBegin, 0, 0, 0);
|
||||
pDb->disableAuth--;
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), 0);
|
||||
Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pDb->nTransaction++;
|
||||
@@ -2770,7 +2775,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
** or savepoint. */
|
||||
if( DbUseNre() ){
|
||||
Tcl_NRAddCallback(interp, DbTransPostCmd, cd, 0, 0, 0);
|
||||
Tcl_NREvalObj(interp, pScript, 0);
|
||||
(void)Tcl_NREvalObj(interp, pScript, 0);
|
||||
}else{
|
||||
rc = DbTransPostCmd(&cd, interp, Tcl_EvalObjEx(interp, pScript, 0));
|
||||
}
|
||||
@@ -2782,7 +2787,8 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
*/
|
||||
case DB_UNLOCK_NOTIFY: {
|
||||
#ifndef SQLITE_ENABLE_UNLOCK_NOTIFY
|
||||
Tcl_AppendResult(interp, "unlock_notify not available in this build", 0);
|
||||
Tcl_AppendResult(interp, "unlock_notify not available in this build",
|
||||
(char*)0);
|
||||
rc = TCL_ERROR;
|
||||
#else
|
||||
if( objc!=2 && objc!=3 ){
|
||||
@@ -2805,7 +2811,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
}
|
||||
|
||||
if( sqlite3_unlock_notify(pDb->db, xNotify, pNotifyArg) ){
|
||||
Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), 0);
|
||||
Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), (char*)0);
|
||||
rc = TCL_ERROR;
|
||||
}
|
||||
}
|
||||
@@ -2934,14 +2940,14 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
if( objc==2 ){
|
||||
zArg = Tcl_GetStringFromObj(objv[1], 0);
|
||||
if( strcmp(zArg,"-version")==0 ){
|
||||
Tcl_AppendResult(interp,sqlite3_version,0);
|
||||
Tcl_AppendResult(interp,sqlite3_libversion(), (char*)0);
|
||||
return TCL_OK;
|
||||
}
|
||||
if( strcmp(zArg,"-has-codec")==0 ){
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
Tcl_AppendResult(interp,"1",0);
|
||||
Tcl_AppendResult(interp,"1",(char*)0);
|
||||
#else
|
||||
Tcl_AppendResult(interp,"0",0);
|
||||
Tcl_AppendResult(interp,"0",(char*)0);
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
@@ -3016,7 +3022,7 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
zErrMsg = 0;
|
||||
p = (SqliteDb*)Tcl_Alloc( sizeof(*p) );
|
||||
if( p==0 ){
|
||||
Tcl_SetResult(interp, "malloc failed", TCL_STATIC);
|
||||
Tcl_SetResult(interp, (char *)"malloc failed", TCL_STATIC);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
memset(p, 0, sizeof(*p));
|
||||
@@ -3371,13 +3377,11 @@ static void MD5Final(unsigned char digest[16], MD5Context *ctx){
|
||||
byteReverse(ctx->in, 14);
|
||||
|
||||
/* Append length in bits and transform */
|
||||
((uint32 *)ctx->in)[ 14 ] = ctx->bits[0];
|
||||
((uint32 *)ctx->in)[ 15 ] = ctx->bits[1];
|
||||
memcpy(ctx->in + 14*4, ctx->bits, 8);
|
||||
|
||||
MD5Transform(ctx->buf, (uint32 *)ctx->in);
|
||||
byteReverse((unsigned char *)ctx->buf, 4);
|
||||
memcpy(digest, ctx->buf, 16);
|
||||
memset(ctx, 0, sizeof(ctx)); /* In case it is sensitive */
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3425,7 +3429,7 @@ static int md5_cmd(void*cd, Tcl_Interp *interp, int argc, const char **argv){
|
||||
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
|
||||
" TEXT\"", 0);
|
||||
" TEXT\"", (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
MD5Init(&ctx);
|
||||
@@ -3450,13 +3454,13 @@ static int md5file_cmd(void*cd, Tcl_Interp*interp, int argc, const char **argv){
|
||||
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
|
||||
" FILENAME\"", 0);
|
||||
" FILENAME\"", (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
in = fopen(argv[1],"rb");
|
||||
if( in==0 ){
|
||||
Tcl_AppendResult(interp,"unable to open file \"", argv[1],
|
||||
"\" for reading", 0);
|
||||
"\" for reading", (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
MD5Init(&ctx);
|
||||
@@ -3774,6 +3778,7 @@ int TCLSH_MAIN(int argc, char **argv){
|
||||
sqlite3_shutdown();
|
||||
|
||||
Tcl_FindExecutable(argv[0]);
|
||||
Tcl_SetSystemEncoding(NULL, "utf-8");
|
||||
interp = Tcl_CreateInterp();
|
||||
|
||||
#if TCLSH==2
|
||||
|
||||
+233
-8
@@ -242,7 +242,28 @@ static int test_io_trace(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Usage: clang_sanitize_address
|
||||
**
|
||||
** Returns true if the program was compiled using clang with the
|
||||
** -fsanitize=address switch on the command line. False otherwise.
|
||||
*/
|
||||
static int clang_sanitize_address(
|
||||
void *NotUsed,
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int argc, /* Number of arguments */
|
||||
char **argv /* Text of each argument */
|
||||
){
|
||||
int res = 0;
|
||||
#if defined(__has_feature)
|
||||
# if __has_feature(address_sanitizer)
|
||||
res = 1;
|
||||
# endif
|
||||
#endif
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(res));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_exec_printf DB FORMAT STRING
|
||||
**
|
||||
@@ -942,9 +963,21 @@ static void ptrChngFunction(
|
||||
sqlite3_result_int(context, p1!=p2);
|
||||
}
|
||||
|
||||
/*
|
||||
** This SQL function returns a different answer each time it is called, even if
|
||||
** the arguments are the same.
|
||||
*/
|
||||
static void nondeterministicFunction(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
static int cnt = 0;
|
||||
sqlite3_result_int(context, cnt++);
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite_test_create_function DB
|
||||
** Usage: sqlite3_create_function DB
|
||||
**
|
||||
** Call the sqlite3_create_function API on the given database in order
|
||||
** to create a function named "x_coalesce". This function does the same thing
|
||||
@@ -973,16 +1006,16 @@ static int test_create_function(
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
|
||||
rc = sqlite3_create_function(db, "x_coalesce", -1, SQLITE_ANY, 0,
|
||||
rc = sqlite3_create_function(db, "x_coalesce", -1, SQLITE_UTF8, 0,
|
||||
t1_ifnullFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "hex8", 1, SQLITE_ANY, 0,
|
||||
hex8Func, 0, 0);
|
||||
rc = sqlite3_create_function(db, "hex8", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC,
|
||||
0, hex8Func, 0, 0);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "hex16", 1, SQLITE_ANY, 0,
|
||||
hex16Func, 0, 0);
|
||||
rc = sqlite3_create_function(db, "hex16", 1, SQLITE_UTF16 | SQLITE_DETERMINISTIC,
|
||||
0, hex16Func, 0, 0);
|
||||
}
|
||||
#endif
|
||||
if( rc==SQLITE_OK ){
|
||||
@@ -994,6 +1027,19 @@ static int test_create_function(
|
||||
ptrChngFunction, 0, 0);
|
||||
}
|
||||
|
||||
/* Functions counter1() and counter2() have the same implementation - they
|
||||
** both return an ascending integer with each call. But counter1() is marked
|
||||
** as non-deterministic and counter2() is marked as deterministic.
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "counter1", -1, SQLITE_UTF8,
|
||||
0, nondeterministicFunction, 0, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "counter2", -1, SQLITE_UTF8|SQLITE_DETERMINISTIC,
|
||||
0, nondeterministicFunction, 0, 0);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
/* Use the sqlite3_create_function16() API here. Mainly for fun, but also
|
||||
** because it is not tested anywhere else. */
|
||||
@@ -5453,6 +5499,37 @@ static int reset_prng_state(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: database_may_be_corrupt
|
||||
**
|
||||
** Indicate that database files might be corrupt. In other words, set the normal
|
||||
** state of operation.
|
||||
*/
|
||||
static int database_may_be_corrupt(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3_test_control(SQLITE_TESTCTRL_NEVER_CORRUPT, 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
/*
|
||||
** tclcmd: database_never_corrupt
|
||||
**
|
||||
** Indicate that database files are always well-formed. This enables extra assert()
|
||||
** statements that test conditions that are always true for well-formed databases.
|
||||
*/
|
||||
static int database_never_corrupt(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3_test_control(SQLITE_TESTCTRL_NEVER_CORRUPT, 1);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: pcache_stats
|
||||
*/
|
||||
@@ -5934,6 +6011,145 @@ static int win32_file_lock(
|
||||
CloseHandle(ev);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** exists_win32_path PATH
|
||||
**
|
||||
** Returns non-zero if the specified path exists, whose fully qualified name
|
||||
** may exceed 260 characters if it is prefixed with "\\?\".
|
||||
*/
|
||||
static int win32_exists_path(
|
||||
void *clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "PATH");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(
|
||||
GetFileAttributesW( Tcl_GetUnicode(objv[1]))!=INVALID_FILE_ATTRIBUTES ));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** find_win32_file PATTERN
|
||||
**
|
||||
** Returns a list of entries in a directory that match the specified pattern,
|
||||
** whose fully qualified name may exceed 248 characters if it is prefixed with
|
||||
** "\\?\".
|
||||
*/
|
||||
static int win32_find_file(
|
||||
void *clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
HANDLE hFindFile = INVALID_HANDLE_VALUE;
|
||||
WIN32_FIND_DATAW findData;
|
||||
Tcl_Obj *listObj;
|
||||
DWORD lastErrno;
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "PATTERN");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
hFindFile = FindFirstFileW(Tcl_GetUnicode(objv[1]), &findData);
|
||||
if( hFindFile==INVALID_HANDLE_VALUE ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError()));
|
||||
return TCL_ERROR;
|
||||
}
|
||||
listObj = Tcl_NewObj();
|
||||
Tcl_IncrRefCount(listObj);
|
||||
do {
|
||||
Tcl_ListObjAppendElement(interp, listObj, Tcl_NewUnicodeObj(
|
||||
findData.cFileName, -1));
|
||||
Tcl_ListObjAppendElement(interp, listObj, Tcl_NewWideIntObj(
|
||||
findData.dwFileAttributes));
|
||||
} while( FindNextFileW(hFindFile, &findData) );
|
||||
lastErrno = GetLastError();
|
||||
if( lastErrno!=NO_ERROR && lastErrno!=ERROR_NO_MORE_FILES ){
|
||||
FindClose(hFindFile);
|
||||
Tcl_DecrRefCount(listObj);
|
||||
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError()));
|
||||
return TCL_ERROR;
|
||||
}
|
||||
FindClose(hFindFile);
|
||||
Tcl_SetObjResult(interp, listObj);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** delete_win32_file FILENAME
|
||||
**
|
||||
** Deletes the specified file, whose fully qualified name may exceed 260
|
||||
** characters if it is prefixed with "\\?\".
|
||||
*/
|
||||
static int win32_delete_file(
|
||||
void *clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "FILENAME");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( !DeleteFileW(Tcl_GetUnicode(objv[1])) ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError()));
|
||||
return TCL_ERROR;
|
||||
}
|
||||
Tcl_ResetResult(interp);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** make_win32_dir DIRECTORY
|
||||
**
|
||||
** Creates the specified directory, whose fully qualified name may exceed 248
|
||||
** characters if it is prefixed with "\\?\".
|
||||
*/
|
||||
static int win32_mkdir(
|
||||
void *clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DIRECTORY");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( !CreateDirectoryW(Tcl_GetUnicode(objv[1]), NULL) ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError()));
|
||||
return TCL_ERROR;
|
||||
}
|
||||
Tcl_ResetResult(interp);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** remove_win32_dir DIRECTORY
|
||||
**
|
||||
** Removes the specified directory, whose fully qualified name may exceed 248
|
||||
** characters if it is prefixed with "\\?\".
|
||||
*/
|
||||
static int win32_rmdir(
|
||||
void *clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DIRECTORY");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( !RemoveDirectoryW(Tcl_GetUnicode(objv[1])) ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError()));
|
||||
return TCL_ERROR;
|
||||
}
|
||||
Tcl_ResetResult(interp);
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -5965,7 +6181,6 @@ static int optimization_control(
|
||||
{ "column-cache", SQLITE_ColumnCache },
|
||||
{ "groupby-order", SQLITE_GroupByOrder },
|
||||
{ "factor-constants", SQLITE_FactorOutConst },
|
||||
{ "real-as-int", SQLITE_IdxRealAsInt },
|
||||
{ "distinct-opt", SQLITE_DistinctOpt },
|
||||
{ "cover-idx-scan", SQLITE_CoverIdxScan },
|
||||
{ "order-by-idx-join", SQLITE_OrderByIdxJoin },
|
||||
@@ -6021,6 +6236,7 @@ static int tclLoadStaticExtensionCmd(
|
||||
extern int sqlite3_percentile_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
extern int sqlite3_regexp_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
extern int sqlite3_spellfix_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
extern int sqlite3_totype_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
extern int sqlite3_wholenumber_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
static const struct {
|
||||
const char *zExtName;
|
||||
@@ -6034,6 +6250,7 @@ static int tclLoadStaticExtensionCmd(
|
||||
{ "percentile", sqlite3_percentile_init },
|
||||
{ "regexp", sqlite3_regexp_init },
|
||||
{ "spellfix", sqlite3_spellfix_init },
|
||||
{ "totype", sqlite3_totype_init },
|
||||
{ "wholenumber", sqlite3_wholenumber_init },
|
||||
};
|
||||
sqlite3 *db;
|
||||
@@ -6127,6 +6344,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_busy_timeout", (Tcl_CmdProc*)test_busy_timeout },
|
||||
{ "printf", (Tcl_CmdProc*)test_printf },
|
||||
{ "sqlite3IoTrace", (Tcl_CmdProc*)test_io_trace },
|
||||
{ "clang_sanitize_address", (Tcl_CmdProc*)clang_sanitize_address },
|
||||
};
|
||||
static struct {
|
||||
char *zName;
|
||||
@@ -6190,9 +6408,16 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "save_prng_state", save_prng_state, 0 },
|
||||
{ "restore_prng_state", restore_prng_state, 0 },
|
||||
{ "reset_prng_state", reset_prng_state, 0 },
|
||||
{ "database_never_corrupt", database_never_corrupt, 0},
|
||||
{ "database_may_be_corrupt", database_may_be_corrupt, 0},
|
||||
{ "optimization_control", optimization_control,0},
|
||||
#if SQLITE_OS_WIN
|
||||
{ "lock_win32_file", win32_file_lock, 0 },
|
||||
{ "exists_win32_path", win32_exists_path, 0 },
|
||||
{ "find_win32_file", win32_find_file, 0 },
|
||||
{ "delete_win32_file", win32_delete_file, 0 },
|
||||
{ "make_win32_dir", win32_mkdir, 0 },
|
||||
{ "remove_win32_dir", win32_rmdir, 0 },
|
||||
#endif
|
||||
{ "tcl_objproc", runAsObjProc, 0 },
|
||||
|
||||
|
||||
@@ -76,7 +76,6 @@ static int test_value_overhead(
|
||||
|
||||
val.flags = MEM_Str|MEM_Term|MEM_Static;
|
||||
val.z = "hello world";
|
||||
val.type = SQLITE_TEXT;
|
||||
val.enc = SQLITE_UTF8;
|
||||
|
||||
for(i=0; i<repeat_count; i++){
|
||||
|
||||
+10
-11
@@ -133,9 +133,9 @@ struct CrashFile {
|
||||
** OsFileSize() calls. Although both could be done by traversing the
|
||||
** write-list, in practice this is impractically slow.
|
||||
*/
|
||||
int iSize; /* Size of file in bytes */
|
||||
int nData; /* Size of buffer allocated at zData */
|
||||
u8 *zData; /* Buffer containing file contents */
|
||||
int nData; /* Size of buffer allocated at zData */
|
||||
i64 iSize; /* Size of file in bytes */
|
||||
};
|
||||
|
||||
struct CrashGlobal {
|
||||
@@ -173,9 +173,6 @@ static void *crash_realloc(void *p, int n){
|
||||
static int writeDbFile(CrashFile *p, u8 *z, i64 iAmt, i64 iOff){
|
||||
int rc = SQLITE_OK;
|
||||
int iSkip = 0;
|
||||
if( iOff==PENDING_BYTE && (p->flags&SQLITE_OPEN_MAIN_DB) ){
|
||||
iSkip = 512;
|
||||
}
|
||||
if( (iAmt-iSkip)>0 ){
|
||||
rc = sqlite3OsWrite(p->pRealFile, &z[iSkip], (int)(iAmt-iSkip), iOff+iSkip);
|
||||
}
|
||||
@@ -409,13 +406,17 @@ static int cfRead(
|
||||
sqlite_int64 iOfst
|
||||
){
|
||||
CrashFile *pCrash = (CrashFile *)pFile;
|
||||
int nCopy = (int)MIN((i64)iAmt, (pCrash->iSize - iOfst));
|
||||
|
||||
if( nCopy>0 ){
|
||||
memcpy(zBuf, &pCrash->zData[iOfst], nCopy);
|
||||
}
|
||||
|
||||
/* Check the file-size to see if this is a short-read */
|
||||
if( pCrash->iSize<(iOfst+iAmt) ){
|
||||
if( nCopy<iAmt ){
|
||||
return SQLITE_IOERR_SHORT_READ;
|
||||
}
|
||||
|
||||
memcpy(zBuf, &pCrash->zData[iOfst], iAmt);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -621,7 +622,7 @@ static int cfOpen(
|
||||
pWrapper->flags = flags;
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
pWrapper->nData = (4096 + pWrapper->iSize);
|
||||
pWrapper->nData = (int)(4096 + pWrapper->iSize);
|
||||
pWrapper->zData = crash_malloc(pWrapper->nData);
|
||||
if( pWrapper->zData ){
|
||||
/* os_unix.c contains an assert() that fails if the caller attempts
|
||||
@@ -632,14 +633,12 @@ static int cfOpen(
|
||||
** UPDATE: It also contains an assert() verifying that each call
|
||||
** to the xRead() method reads less than 128KB of data.
|
||||
*/
|
||||
const int isDb = (flags&SQLITE_OPEN_MAIN_DB);
|
||||
i64 iOff;
|
||||
|
||||
memset(pWrapper->zData, 0, pWrapper->nData);
|
||||
for(iOff=0; iOff<pWrapper->iSize; iOff += 512){
|
||||
int nRead = pWrapper->iSize - (int)iOff;
|
||||
int nRead = (int)(pWrapper->iSize - iOff);
|
||||
if( nRead>512 ) nRead = 512;
|
||||
if( isDb && iOff==PENDING_BYTE ) continue;
|
||||
rc = sqlite3OsRead(pReal, &pWrapper->zData[iOff], nRead, iOff);
|
||||
}
|
||||
}else{
|
||||
|
||||
+17
-1
@@ -40,6 +40,7 @@ int sqlite3_client_finalize(sqlite3_stmt*);
|
||||
int sqlite3_client_close(sqlite3*);
|
||||
int sqlite3_server_start(void);
|
||||
int sqlite3_server_stop(void);
|
||||
void sqlite3_server_start2(int *pnDecr);
|
||||
|
||||
/*
|
||||
** Each thread is controlled by an instance of the following
|
||||
@@ -68,6 +69,13 @@ struct Thread {
|
||||
int argc; /* number of columns in result */
|
||||
const char *argv[100]; /* result columns */
|
||||
const char *colv[100]; /* result column names */
|
||||
|
||||
/* Initialized to 1 by the supervisor thread when the client is
|
||||
** created, and then deemed read-only to the supervisor thread.
|
||||
** Is set to 0 by the server thread belonging to this client
|
||||
** just before it exits.
|
||||
*/
|
||||
int nServer; /* Number of server threads running */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -175,7 +183,10 @@ static int tcl_client_create(
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pthread_detach(x);
|
||||
sqlite3_server_start();
|
||||
if( threadset[i].nServer==0 ){
|
||||
threadset[i].nServer = 1;
|
||||
sqlite3_server_start2(&threadset[i].nServer);
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
@@ -268,6 +279,11 @@ static int tcl_client_halt(
|
||||
for(i=0; i<N_THREAD && threadset[i].busy==0; i++){}
|
||||
if( i>=N_THREAD ){
|
||||
sqlite3_server_stop();
|
||||
while( 1 ){
|
||||
for(i=0; i<N_THREAD && threadset[i].nServer==0; i++);
|
||||
if( i==N_THREAD ) break;
|
||||
sched_yield();
|
||||
}
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
+2
-1
@@ -265,6 +265,7 @@ static int getIndexArray(
|
||||
while( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
const char *zIdx = (const char *)sqlite3_column_text(pStmt, 1);
|
||||
sqlite3_stmt *pStmt2 = 0;
|
||||
if( zIdx==0 ) continue;
|
||||
zSql = sqlite3_mprintf("PRAGMA index_info(%s)", zIdx);
|
||||
if( !zSql ){
|
||||
rc = SQLITE_NOMEM;
|
||||
@@ -891,7 +892,7 @@ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
pIdxInfo->estimatedCost = cost;
|
||||
}else if( useIdx ){
|
||||
/* Approximation of log2(nRow). */
|
||||
for( ii=0; ii<(sizeof(int)*8); ii++ ){
|
||||
for( ii=0; ii<(sizeof(int)*8)-1; ii++ ){
|
||||
if( nRow & (1<<ii) ){
|
||||
pIdxInfo->estimatedCost = (double)ii;
|
||||
}
|
||||
|
||||
+18
-6
@@ -63,6 +63,12 @@ static void set_options(Tcl_Interp *interp){
|
||||
Tcl_SetVar2(interp, "sqlite_options", "curdir", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_WIN32_MALLOC
|
||||
Tcl_SetVar2(interp, "sqlite_options", "win32malloc", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "win32malloc", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
Tcl_SetVar2(interp, "sqlite_options", "debug", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
@@ -219,6 +225,12 @@ static void set_options(Tcl_Interp *interp){
|
||||
Tcl_SetVar2(interp, "sqlite_options", "check", "1", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_CTE
|
||||
Tcl_SetVar2(interp, "sqlite_options", "cte", "0", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "cte", "1", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_COLUMN_METADATA
|
||||
Tcl_SetVar2(interp, "sqlite_options", "columnmetadata", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
@@ -252,12 +264,7 @@ static void set_options(Tcl_Interp *interp){
|
||||
#endif
|
||||
|
||||
Tcl_SetVar2(interp, "sqlite_options", "conflict", "1", TCL_GLOBAL_ONLY);
|
||||
|
||||
#if SQLITE_OS_UNIX
|
||||
Tcl_SetVar2(interp, "sqlite_options", "crashtest", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "crashtest", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_DATETIME_FUNCS
|
||||
Tcl_SetVar2(interp, "sqlite_options", "datetime", "0", TCL_GLOBAL_ONLY);
|
||||
@@ -458,7 +465,12 @@ Tcl_SetVar2(interp, "sqlite_options", "mergesort", "1", TCL_GLOBAL_ONLY);
|
||||
Tcl_SetVar2(interp, "sqlite_options", "schema_version", "1", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
Tcl_SetVar2(interp, "sqlite_options", "stat4", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "stat4", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
#if defined(SQLITE_ENABLE_STAT3) && !defined(SQLITE_ENABLE_STAT4)
|
||||
Tcl_SetVar2(interp, "sqlite_options", "stat3", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "stat3", "0", TCL_GLOBAL_ONLY);
|
||||
|
||||
+144
@@ -18,6 +18,9 @@
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "sqliteInt.h"
|
||||
#include "vdbeInt.h"
|
||||
|
||||
|
||||
/*
|
||||
** Allocate nByte bytes of space using sqlite3_malloc(). If the
|
||||
@@ -458,6 +461,145 @@ static void real2hex(
|
||||
sqlite3_result_text(context, zOut, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: test_extract(record, field)
|
||||
**
|
||||
** This function implements an SQL user-function that accepts a blob
|
||||
** containing a formatted database record as the first argument. The
|
||||
** second argument is the index of the field within that record to
|
||||
** extract and return.
|
||||
*/
|
||||
static void test_extract(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
u8 *pRec;
|
||||
u8 *pEndHdr; /* Points to one byte past record header */
|
||||
u8 *pHdr; /* Current point in record header */
|
||||
u8 *pBody; /* Current point in record data */
|
||||
u64 nHdr; /* Bytes in record header */
|
||||
int iIdx; /* Required field */
|
||||
int iCurrent = 0; /* Current field */
|
||||
|
||||
assert( argc==2 );
|
||||
pRec = (u8*)sqlite3_value_blob(argv[0]);
|
||||
iIdx = sqlite3_value_int(argv[1]);
|
||||
|
||||
pHdr = pRec + sqlite3GetVarint(pRec, &nHdr);
|
||||
pBody = pEndHdr = &pRec[nHdr];
|
||||
|
||||
for(iCurrent=0; pHdr<pEndHdr && iCurrent<=iIdx; iCurrent++){
|
||||
u64 iSerialType;
|
||||
Mem mem;
|
||||
|
||||
memset(&mem, 0, sizeof(mem));
|
||||
mem.db = db;
|
||||
mem.enc = ENC(db);
|
||||
pHdr += sqlite3GetVarint(pHdr, &iSerialType);
|
||||
pBody += sqlite3VdbeSerialGet(pBody, (u32)iSerialType, &mem);
|
||||
|
||||
if( iCurrent==iIdx ){
|
||||
sqlite3_result_value(context, &mem);
|
||||
}
|
||||
|
||||
sqlite3DbFree(db, mem.zMalloc);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: test_decode(record)
|
||||
**
|
||||
** This function implements an SQL user-function that accepts a blob
|
||||
** containing a formatted database record as its only argument. It returns
|
||||
** a tcl list (type SQLITE_TEXT) containing each of the values stored
|
||||
** in the record.
|
||||
*/
|
||||
static void test_decode(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
u8 *pRec;
|
||||
u8 *pEndHdr; /* Points to one byte past record header */
|
||||
u8 *pHdr; /* Current point in record header */
|
||||
u8 *pBody; /* Current point in record data */
|
||||
u64 nHdr; /* Bytes in record header */
|
||||
Tcl_Obj *pRet; /* Return value */
|
||||
|
||||
pRet = Tcl_NewObj();
|
||||
Tcl_IncrRefCount(pRet);
|
||||
|
||||
assert( argc==1 );
|
||||
pRec = (u8*)sqlite3_value_blob(argv[0]);
|
||||
|
||||
pHdr = pRec + sqlite3GetVarint(pRec, &nHdr);
|
||||
pBody = pEndHdr = &pRec[nHdr];
|
||||
while( pHdr<pEndHdr ){
|
||||
Tcl_Obj *pVal = 0;
|
||||
u64 iSerialType;
|
||||
Mem mem;
|
||||
|
||||
memset(&mem, 0, sizeof(mem));
|
||||
mem.db = db;
|
||||
mem.enc = ENC(db);
|
||||
pHdr += sqlite3GetVarint(pHdr, &iSerialType);
|
||||
pBody += sqlite3VdbeSerialGet(pBody, (u32)iSerialType, &mem);
|
||||
|
||||
switch( sqlite3_value_type(&mem) ){
|
||||
case SQLITE_TEXT:
|
||||
pVal = Tcl_NewStringObj((const char*)sqlite3_value_text(&mem), -1);
|
||||
break;
|
||||
|
||||
case SQLITE_BLOB: {
|
||||
char hexdigit[] = {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
|
||||
};
|
||||
int n = sqlite3_value_bytes(&mem);
|
||||
u8 *z = (u8*)sqlite3_value_blob(&mem);
|
||||
int i;
|
||||
pVal = Tcl_NewStringObj("x'", -1);
|
||||
for(i=0; i<n; i++){
|
||||
char hex[3];
|
||||
hex[0] = hexdigit[((z[i] >> 4) & 0x0F)];
|
||||
hex[1] = hexdigit[(z[i] & 0x0F)];
|
||||
hex[2] = '\0';
|
||||
Tcl_AppendStringsToObj(pVal, hex, 0);
|
||||
}
|
||||
Tcl_AppendStringsToObj(pVal, "'", 0);
|
||||
break;
|
||||
}
|
||||
|
||||
case SQLITE_FLOAT:
|
||||
pVal = Tcl_NewDoubleObj(sqlite3_value_double(&mem));
|
||||
break;
|
||||
|
||||
case SQLITE_INTEGER:
|
||||
pVal = Tcl_NewWideIntObj(sqlite3_value_int64(&mem));
|
||||
break;
|
||||
|
||||
case SQLITE_NULL:
|
||||
pVal = Tcl_NewStringObj("NULL", -1);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert( 0 );
|
||||
}
|
||||
|
||||
Tcl_ListObjAppendElement(0, pRet, pVal);
|
||||
|
||||
if( mem.zMalloc ){
|
||||
sqlite3DbFree(db, mem.zMalloc);
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_result_text(context, Tcl_GetString(pRet), -1, SQLITE_TRANSIENT);
|
||||
Tcl_DecrRefCount(pRet);
|
||||
}
|
||||
|
||||
|
||||
static int registerTestFunctions(sqlite3 *db){
|
||||
static const struct {
|
||||
@@ -482,6 +624,8 @@ static int registerTestFunctions(sqlite3 *db){
|
||||
{ "test_isolation", 2, SQLITE_UTF8, test_isolation},
|
||||
{ "test_counter", 1, SQLITE_UTF8, counterFunc},
|
||||
{ "real2hex", 1, SQLITE_UTF8, real2hex},
|
||||
{ "test_decode", 1, SQLITE_UTF8, test_decode},
|
||||
{ "test_extract", 2, SQLITE_UTF8, test_extract},
|
||||
};
|
||||
int i;
|
||||
|
||||
|
||||
@@ -219,7 +219,6 @@ static int init_wrapper_uninstall(
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
memset(&wrapped, 0, sizeof(&wrapped));
|
||||
sqlite3_shutdown();
|
||||
sqlite3_config(SQLITE_CONFIG_MUTEX, &wrapped.mutex);
|
||||
sqlite3_config(SQLITE_CONFIG_MALLOC, &wrapped.mem);
|
||||
|
||||
@@ -91,6 +91,9 @@ static void statusFunc(
|
||||
/*
|
||||
** Extension load function.
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int testloadext_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
@@ -109,6 +112,9 @@ int testloadext_init(
|
||||
/*
|
||||
** Another extension entry point. This one always fails.
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int testbrokenext_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
|
||||
+29
-1
@@ -749,7 +749,7 @@ static void test_memdebug_callback(int nByte, int nFrame, void **aFrame){
|
||||
int isNew;
|
||||
|
||||
int aKey[MALLOC_LOG_KEYINTS];
|
||||
int nKey = sizeof(int)*MALLOC_LOG_KEYINTS;
|
||||
unsigned int nKey = sizeof(int)*MALLOC_LOG_KEYINTS;
|
||||
|
||||
memset(aKey, 0, nKey);
|
||||
if( (sizeof(void*)*nFrame)<nKey ){
|
||||
@@ -1130,6 +1130,33 @@ static int test_config_heap(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_config_heap_size NBYTE
|
||||
*/
|
||||
static int test_config_heap_size(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int nByte; /* Size to pass to sqlite3_config() */
|
||||
int rc; /* Return code of sqlite3_config() */
|
||||
|
||||
Tcl_Obj * CONST *aArg = &objv[1];
|
||||
int nArg = objc-1;
|
||||
|
||||
if( nArg!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "NBYTE");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetIntFromObj(interp, aArg[0], &nByte) ) return TCL_ERROR;
|
||||
|
||||
rc = sqlite3_config(SQLITE_CONFIG_WIN32_HEAPSIZE, nByte);
|
||||
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_config_error [DB]
|
||||
**
|
||||
@@ -1473,6 +1500,7 @@ int Sqlitetest_malloc_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_db_status", test_db_status ,0 },
|
||||
{ "install_malloc_faultsim", test_install_malloc_faultsim ,0 },
|
||||
{ "sqlite3_config_heap", test_config_heap ,0 },
|
||||
{ "sqlite3_config_heap_size", test_config_heap_size ,0 },
|
||||
{ "sqlite3_config_memstatus", test_config_memstatus ,0 },
|
||||
{ "sqlite3_config_lookaside", test_config_lookaside ,0 },
|
||||
{ "sqlite3_config_error", test_config_error ,0 },
|
||||
|
||||
@@ -755,9 +755,11 @@ static int multiplexRead(
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
multiplexGroup *pGroup = p->pGroup;
|
||||
int rc = SQLITE_OK;
|
||||
multiplexEnter();
|
||||
int nMutex = 0;
|
||||
multiplexEnter(); nMutex++;
|
||||
if( !pGroup->bEnabled ){
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
|
||||
multiplexLeave(); nMutex--;
|
||||
if( pSubOpen==0 ){
|
||||
rc = SQLITE_IOERR_READ;
|
||||
}else{
|
||||
@@ -766,7 +768,10 @@ static int multiplexRead(
|
||||
}else{
|
||||
while( iAmt > 0 ){
|
||||
int i = (int)(iOfst / pGroup->szChunk);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL, 1);
|
||||
sqlite3_file *pSubOpen;
|
||||
if( nMutex==0 ){ multiplexEnter(); nMutex++; }
|
||||
pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL, 1);
|
||||
multiplexLeave(); nMutex--;
|
||||
if( pSubOpen ){
|
||||
int extra = ((int)(iOfst % pGroup->szChunk) + iAmt) - pGroup->szChunk;
|
||||
if( extra<0 ) extra = 0;
|
||||
@@ -783,7 +788,8 @@ static int multiplexRead(
|
||||
}
|
||||
}
|
||||
}
|
||||
multiplexLeave();
|
||||
assert( nMutex==0 || nMutex==1 );
|
||||
if( nMutex ) multiplexLeave();
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
@@ -472,6 +472,32 @@ void sqlite3_server_start(void){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** A wrapper around sqlite3_server() that decrements the int variable
|
||||
** pointed to by the first argument after the sqlite3_server() call
|
||||
** returns.
|
||||
*/
|
||||
static void *serverWrapper(void *pnDecr){
|
||||
void *p = sqlite3_server(0);
|
||||
(*(int*)pnDecr)--;
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is the similar to sqlite3_server_start(), except that
|
||||
** the integer pointed to by the first argument is decremented when
|
||||
** the server thread exits.
|
||||
*/
|
||||
void sqlite3_server_start2(int *pnDecr){
|
||||
pthread_t x;
|
||||
int rc;
|
||||
g.serverHalt = 0;
|
||||
rc = pthread_create(&x, 0, serverWrapper, (void*)pnDecr);
|
||||
if( rc==0 ){
|
||||
pthread_detach(x);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** If a server thread is running, then stop it. If no server is
|
||||
** running, this routine is effectively a no-op.
|
||||
|
||||
+3
-2
@@ -397,6 +397,7 @@ static int statNext(sqlite3_vtab_cursor *pCursor){
|
||||
sqlite3_free(pCsr->zPath);
|
||||
pCsr->zPath = 0;
|
||||
|
||||
statNextRestart:
|
||||
if( pCsr->aPage[0].pPg==0 ){
|
||||
rc = sqlite3_step(pCsr->pStmt);
|
||||
if( rc==SQLITE_ROW ){
|
||||
@@ -448,11 +449,11 @@ static int statNext(sqlite3_vtab_cursor *pCursor){
|
||||
p->iCell++;
|
||||
}
|
||||
|
||||
while( !p->iRightChildPg || p->iCell>p->nCell ){
|
||||
if( !p->iRightChildPg || p->iCell>p->nCell ){
|
||||
statClearPage(p);
|
||||
if( pCsr->iPage==0 ) return statNext(pCursor);
|
||||
pCsr->iPage--;
|
||||
p = &pCsr->aPage[pCsr->iPage];
|
||||
goto statNextRestart; /* Tail recursion */
|
||||
}
|
||||
pCsr->iPage++;
|
||||
assert( p==&pCsr->aPage[pCsr->iPage-1] );
|
||||
|
||||
@@ -258,6 +258,11 @@ static void vfstrace_print_errcode(
|
||||
case SQLITE_IOERR_SHMOPEN: zVal = "SQLITE_IOERR_SHMOPEN"; break;
|
||||
case SQLITE_IOERR_SHMSIZE: zVal = "SQLITE_IOERR_SHMSIZE"; break;
|
||||
case SQLITE_IOERR_SHMLOCK: zVal = "SQLITE_IOERR_SHMLOCK"; break;
|
||||
case SQLITE_IOERR_SHMMAP: zVal = "SQLITE_IOERR_SHMMAP"; break;
|
||||
case SQLITE_IOERR_SEEK: zVal = "SQLITE_IOERR_SEEK"; break;
|
||||
case SQLITE_IOERR_GETTEMPPATH: zVal = "SQLITE_IOERR_GETTEMPPATH"; break;
|
||||
case SQLITE_IOERR_CONVPATH: zVal = "SQLITE_IOERR_CONVPATH"; break;
|
||||
case SQLITE_READONLY_DBMOVED: zVal = "SQLITE_READONLY_DBMOVED"; break;
|
||||
case SQLITE_LOCKED_SHAREDCACHE: zVal = "SQLITE_LOCKED_SHAREDCACHE"; break;
|
||||
case SQLITE_BUSY_RECOVERY: zVal = "SQLITE_BUSY_RECOVERY"; break;
|
||||
case SQLITE_CANTOPEN_NOTEMPDIR: zVal = "SQLITE_CANTOPEN_NOTEMPDIR"; break;
|
||||
|
||||
+4
-13
@@ -303,24 +303,15 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
|
||||
for(i=1; sqlite3Isdigit(z[i]); i++){}
|
||||
return i;
|
||||
}
|
||||
case '#': {
|
||||
for(i=1; sqlite3Isdigit(z[i]); i++){}
|
||||
if( i>1 ){
|
||||
/* Parameters of the form #NNN (where NNN is a number) are used
|
||||
** internally by sqlite3NestedParse. */
|
||||
*tokenType = TK_REGISTER;
|
||||
return i;
|
||||
}
|
||||
/* Fall through into the next case if the '#' is not followed by
|
||||
** a digit. Try to match #AAAA where AAAA is a parameter name. */
|
||||
}
|
||||
#ifndef SQLITE_OMIT_TCL_VARIABLE
|
||||
case '$':
|
||||
#endif
|
||||
case '@': /* For compatibility with MS SQL Server */
|
||||
case '#':
|
||||
case ':': {
|
||||
int n = 0;
|
||||
testcase( z[0]=='$' ); testcase( z[0]=='@' ); testcase( z[0]==':' );
|
||||
testcase( z[0]=='$' ); testcase( z[0]=='@' );
|
||||
testcase( z[0]==':' ); testcase( z[0]=='#' );
|
||||
*tokenType = TK_VARIABLE;
|
||||
for(i=1; (c=z[i])!=0; i++){
|
||||
if( IdChar(c) ){
|
||||
@@ -503,10 +494,10 @@ abort_parse:
|
||||
sqlite3DeleteTable(db, pParse->pNewTable);
|
||||
}
|
||||
|
||||
if( pParse->bFreeWith ) sqlite3WithDelete(db, pParse->pWith);
|
||||
sqlite3DeleteTrigger(db, pParse->pNewTrigger);
|
||||
for(i=pParse->nzVar-1; i>=0; i--) sqlite3DbFree(db, pParse->azVar[i]);
|
||||
sqlite3DbFree(db, pParse->azVar);
|
||||
sqlite3DbFree(db, pParse->aAlias);
|
||||
while( pParse->pAinc ){
|
||||
AutoincInfo *p = pParse->pAinc;
|
||||
pParse->pAinc = p->pNext;
|
||||
|
||||
+12
-21
@@ -148,8 +148,8 @@ void sqlite3BeginTrigger(
|
||||
/* Ensure the table name matches database name and that the table exists */
|
||||
if( db->mallocFailed ) goto trigger_cleanup;
|
||||
assert( pTableName->nSrc==1 );
|
||||
if( sqlite3FixInit(&sFix, pParse, iDb, "trigger", pName) &&
|
||||
sqlite3FixSrcList(&sFix, pTableName) ){
|
||||
sqlite3FixInit(&sFix, pParse, iDb, "trigger", pName);
|
||||
if( sqlite3FixSrcList(&sFix, pTableName) ){
|
||||
goto trigger_cleanup;
|
||||
}
|
||||
pTab = sqlite3SrcListLookup(pParse, pTableName);
|
||||
@@ -291,8 +291,10 @@ void sqlite3FinishTrigger(
|
||||
}
|
||||
nameToken.z = pTrig->zName;
|
||||
nameToken.n = sqlite3Strlen30(nameToken.z);
|
||||
if( sqlite3FixInit(&sFix, pParse, iDb, "trigger", &nameToken)
|
||||
&& sqlite3FixTriggerStep(&sFix, pTrig->step_list) ){
|
||||
sqlite3FixInit(&sFix, pParse, iDb, "trigger", &nameToken);
|
||||
if( sqlite3FixTriggerStep(&sFix, pTrig->step_list)
|
||||
|| sqlite3FixExpr(&sFix, pTrig->pWhen)
|
||||
){
|
||||
goto triggerfinish_cleanup;
|
||||
}
|
||||
|
||||
@@ -395,25 +397,21 @@ TriggerStep *sqlite3TriggerInsertStep(
|
||||
sqlite3 *db, /* The database connection */
|
||||
Token *pTableName, /* Name of the table into which we insert */
|
||||
IdList *pColumn, /* List of columns in pTableName to insert into */
|
||||
ExprList *pEList, /* The VALUE clause: a list of values to be inserted */
|
||||
Select *pSelect, /* A SELECT statement that supplies values */
|
||||
u8 orconf /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */
|
||||
){
|
||||
TriggerStep *pTriggerStep;
|
||||
|
||||
assert(pEList == 0 || pSelect == 0);
|
||||
assert(pEList != 0 || pSelect != 0 || db->mallocFailed);
|
||||
assert(pSelect != 0 || db->mallocFailed);
|
||||
|
||||
pTriggerStep = triggerStepAllocate(db, TK_INSERT, pTableName);
|
||||
if( pTriggerStep ){
|
||||
pTriggerStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
|
||||
pTriggerStep->pIdList = pColumn;
|
||||
pTriggerStep->pExprList = sqlite3ExprListDup(db, pEList, EXPRDUP_REDUCE);
|
||||
pTriggerStep->orconf = orconf;
|
||||
}else{
|
||||
sqlite3IdListDelete(db, pColumn);
|
||||
}
|
||||
sqlite3ExprListDelete(db, pEList);
|
||||
sqlite3SelectDelete(db, pSelect);
|
||||
|
||||
return pTriggerStep;
|
||||
@@ -568,6 +566,7 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
|
||||
assert( pTable!=0 );
|
||||
if( (v = sqlite3GetVdbe(pParse))!=0 ){
|
||||
int base;
|
||||
static const int iLn = VDBE_OFFSET_LINENO(2);
|
||||
static const VdbeOpList dropTrigger[] = {
|
||||
{ OP_Rewind, 0, ADDR(9), 0},
|
||||
{ OP_String8, 0, 1, 0}, /* 1 */
|
||||
@@ -582,7 +581,7 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
|
||||
|
||||
sqlite3BeginWriteOperation(pParse, 0, iDb);
|
||||
sqlite3OpenMasterTable(pParse, iDb);
|
||||
base = sqlite3VdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger);
|
||||
base = sqlite3VdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger, iLn);
|
||||
sqlite3VdbeChangeP4(v, base+1, pTrigger->zName, P4_TRANSIENT);
|
||||
sqlite3VdbeChangeP4(v, base+4, "trigger", P4_STATIC);
|
||||
sqlite3ChangeCookie(pParse, iDb);
|
||||
@@ -728,15 +727,7 @@ static int codeTriggerProgram(
|
||||
** INSERT OR IGNORE INTO t1 ... ; -- insert into t2 uses IGNORE policy
|
||||
*/
|
||||
pParse->eOrconf = (orconf==OE_Default)?pStep->orconf:(u8)orconf;
|
||||
|
||||
/* Clear the cookieGoto flag. When coding triggers, the cookieGoto
|
||||
** variable is used as a flag to indicate to sqlite3ExprCodeConstants()
|
||||
** that it is not safe to refactor constants (this happens after the
|
||||
** start of the first loop in the SQL statement is coded - at that
|
||||
** point code may be conditionally executed, so it is no longer safe to
|
||||
** initialize constant register values). */
|
||||
assert( pParse->cookieGoto==0 || pParse->cookieGoto==-1 );
|
||||
pParse->cookieGoto = 0;
|
||||
assert( pParse->okConstFactor==0 );
|
||||
|
||||
switch( pStep->op ){
|
||||
case TK_UPDATE: {
|
||||
@@ -751,7 +742,6 @@ static int codeTriggerProgram(
|
||||
case TK_INSERT: {
|
||||
sqlite3Insert(pParse,
|
||||
targetSrcList(pParse, pStep),
|
||||
sqlite3ExprListDup(db, pStep->pExprList, 0),
|
||||
sqlite3SelectDup(db, pStep->pSelect, 0),
|
||||
sqlite3IdListDup(db, pStep->pIdList),
|
||||
pParse->eOrconf
|
||||
@@ -782,7 +772,7 @@ static int codeTriggerProgram(
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
||||
/*
|
||||
** This function is used to add VdbeComment() annotations to a VDBE
|
||||
** program. It is not used in production code, only for debugging.
|
||||
@@ -922,6 +912,7 @@ static TriggerPrg *codeRowTrigger(
|
||||
|
||||
assert( !pSubParse->pAinc && !pSubParse->pZombieTab );
|
||||
assert( !pSubParse->pTriggerPrg && !pSubParse->nMaxArg );
|
||||
sqlite3ParserReset(pSubParse);
|
||||
sqlite3StackFree(db, pSubParse);
|
||||
|
||||
return pPrg;
|
||||
|
||||
+203
-105
@@ -61,7 +61,7 @@ static void updateVirtualTable(
|
||||
void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){
|
||||
assert( pTab!=0 );
|
||||
if( !pTab->pSelect ){
|
||||
sqlite3_value *pValue;
|
||||
sqlite3_value *pValue = 0;
|
||||
u8 enc = ENC(sqlite3VdbeDb(v));
|
||||
Column *pCol = &pTab->aCol[i];
|
||||
VdbeComment((v, "%s.%s", pTab->zName, pCol->zName));
|
||||
@@ -72,7 +72,7 @@ void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){
|
||||
sqlite3VdbeChangeP4(v, -1, (const char *)pValue, P4_MEM);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
if( iReg>=0 && pTab->aCol[i].affinity==SQLITE_AFF_REAL ){
|
||||
if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){
|
||||
sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg);
|
||||
}
|
||||
#endif
|
||||
@@ -95,25 +95,32 @@ void sqlite3Update(
|
||||
){
|
||||
int i, j; /* Loop counters */
|
||||
Table *pTab; /* The table to be updated */
|
||||
int addr = 0; /* VDBE instruction address of the start of the loop */
|
||||
int addrTop = 0; /* VDBE instruction address of the start of the loop */
|
||||
WhereInfo *pWInfo; /* Information about the WHERE clause */
|
||||
Vdbe *v; /* The virtual database engine */
|
||||
Index *pIdx; /* For looping over indices */
|
||||
Index *pPk; /* The PRIMARY KEY index for WITHOUT ROWID tables */
|
||||
int nIdx; /* Number of indices that need updating */
|
||||
int iCur; /* VDBE Cursor number of pTab */
|
||||
int iBaseCur; /* Base cursor number */
|
||||
int iDataCur; /* Cursor for the canonical data btree */
|
||||
int iIdxCur; /* Cursor for the first index */
|
||||
sqlite3 *db; /* The database structure */
|
||||
int *aRegIdx = 0; /* One register assigned to each index to be updated */
|
||||
int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the
|
||||
** an expression for the i-th column of the table.
|
||||
** aXRef[i]==-1 if the i-th column is not changed. */
|
||||
int chngRowid; /* True if the record number is being changed */
|
||||
u8 *aToOpen; /* 1 for tables and indices to be opened */
|
||||
u8 chngPk; /* PRIMARY KEY changed in a WITHOUT ROWID table */
|
||||
u8 chngRowid; /* Rowid changed in a normal table */
|
||||
u8 chngKey; /* Either chngPk or chngRowid */
|
||||
Expr *pRowidExpr = 0; /* Expression defining the new record number */
|
||||
int openAll = 0; /* True if all indices need to be opened */
|
||||
AuthContext sContext; /* The authorization context */
|
||||
NameContext sNC; /* The name-context to resolve expressions in */
|
||||
int iDb; /* Database containing the table being updated */
|
||||
int okOnePass; /* True for one-pass algorithm without the FIFO */
|
||||
int hasFK; /* True if foreign key processing is required */
|
||||
int labelBreak; /* Jump here to break out of UPDATE loop */
|
||||
int labelContinue; /* Jump here to continue next step of UPDATE loop */
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
int isView; /* True when updating a view (INSTEAD OF trigger) */
|
||||
@@ -121,6 +128,9 @@ void sqlite3Update(
|
||||
int tmask; /* Mask of TRIGGER_BEFORE|TRIGGER_AFTER */
|
||||
#endif
|
||||
int newmask; /* Mask of NEW.* columns accessed by BEFORE triggers */
|
||||
int iEph = 0; /* Ephemeral table holding all primary key values */
|
||||
int nKey = 0; /* Number of elements in regKey for WITHOUT ROWID */
|
||||
int aiCurOnePass[2]; /* The write cursors opened by WHERE_ONEPASS */
|
||||
|
||||
/* Register Allocations */
|
||||
int regRowCount = 0; /* A count of rows changed */
|
||||
@@ -129,6 +139,7 @@ void sqlite3Update(
|
||||
int regNew; /* Content of the NEW.* table in triggers */
|
||||
int regOld = 0; /* Content of OLD.* table in triggers */
|
||||
int regRowSet = 0; /* Rowset of rows to be updated */
|
||||
int regKey = 0; /* composite PRIMARY KEY value */
|
||||
|
||||
memset(&sContext, 0, sizeof(sContext));
|
||||
db = pParse->db;
|
||||
@@ -166,20 +177,34 @@ void sqlite3Update(
|
||||
if( sqlite3IsReadOnly(pParse, pTab, tmask) ){
|
||||
goto update_cleanup;
|
||||
}
|
||||
aXRef = sqlite3DbMallocRaw(db, sizeof(int) * pTab->nCol );
|
||||
if( aXRef==0 ) goto update_cleanup;
|
||||
for(i=0; i<pTab->nCol; i++) aXRef[i] = -1;
|
||||
|
||||
/* Allocate a cursors for the main database table and for all indices.
|
||||
** The index cursors might not be used, but if they are used they
|
||||
** need to occur right after the database cursor. So go ahead and
|
||||
** allocate enough space, just in case.
|
||||
*/
|
||||
pTabList->a[0].iCursor = iCur = pParse->nTab++;
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
pTabList->a[0].iCursor = iBaseCur = iDataCur = pParse->nTab++;
|
||||
iIdxCur = iDataCur+1;
|
||||
pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
|
||||
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){
|
||||
if( pIdx->autoIndex==2 && pPk!=0 ){
|
||||
iDataCur = pParse->nTab;
|
||||
pTabList->a[0].iCursor = iDataCur;
|
||||
}
|
||||
pParse->nTab++;
|
||||
}
|
||||
|
||||
/* Allocate space for aXRef[], aRegIdx[], and aToOpen[].
|
||||
** Initialize aXRef[] and aToOpen[] to their default values.
|
||||
*/
|
||||
aXRef = sqlite3DbMallocRaw(db, sizeof(int) * (pTab->nCol+nIdx) + nIdx+2 );
|
||||
if( aXRef==0 ) goto update_cleanup;
|
||||
aRegIdx = aXRef+pTab->nCol;
|
||||
aToOpen = (u8*)(aRegIdx+nIdx);
|
||||
memset(aToOpen, 1, nIdx+1);
|
||||
aToOpen[nIdx+1] = 0;
|
||||
for(i=0; i<pTab->nCol; i++) aXRef[i] = -1;
|
||||
|
||||
/* Initialize the name-context */
|
||||
memset(&sNC, 0, sizeof(sNC));
|
||||
sNC.pParse = pParse;
|
||||
@@ -191,7 +216,7 @@ void sqlite3Update(
|
||||
** column to be updated, make sure we have authorization to change
|
||||
** that column.
|
||||
*/
|
||||
chngRowid = 0;
|
||||
chngRowid = chngPk = 0;
|
||||
for(i=0; i<pChanges->nExpr; i++){
|
||||
if( sqlite3ResolveExprNames(&sNC, pChanges->a[i].pExpr) ){
|
||||
goto update_cleanup;
|
||||
@@ -201,13 +226,15 @@ void sqlite3Update(
|
||||
if( j==pTab->iPKey ){
|
||||
chngRowid = 1;
|
||||
pRowidExpr = pChanges->a[i].pExpr;
|
||||
}else if( pPk && (pTab->aCol[j].colFlags & COLFLAG_PRIMKEY)!=0 ){
|
||||
chngPk = 1;
|
||||
}
|
||||
aXRef[j] = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( j>=pTab->nCol ){
|
||||
if( sqlite3IsRowid(pChanges->a[i].zName) ){
|
||||
if( pPk==0 && sqlite3IsRowid(pChanges->a[i].zName) ){
|
||||
j = -1;
|
||||
chngRowid = 1;
|
||||
pRowidExpr = pChanges->a[i].pExpr;
|
||||
@@ -231,32 +258,36 @@ void sqlite3Update(
|
||||
}
|
||||
#endif
|
||||
}
|
||||
assert( (chngRowid & chngPk)==0 );
|
||||
assert( chngRowid==0 || chngRowid==1 );
|
||||
assert( chngPk==0 || chngPk==1 );
|
||||
chngKey = chngRowid + chngPk;
|
||||
|
||||
hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngRowid);
|
||||
/* The SET expressions are not actually used inside the WHERE loop.
|
||||
** So reset the colUsed mask
|
||||
*/
|
||||
pTabList->a[0].colUsed = 0;
|
||||
|
||||
/* Allocate memory for the array aRegIdx[]. There is one entry in the
|
||||
** array for each index associated with table being updated. Fill in
|
||||
** the value with a register number for indices that are to be used
|
||||
** and with zero for unused indices.
|
||||
hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngKey);
|
||||
|
||||
/* There is one entry in the aRegIdx[] array for each index on the table
|
||||
** being updated. Fill in aRegIdx[] with a register number that will hold
|
||||
** the key for accessing each index.
|
||||
*/
|
||||
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){}
|
||||
if( nIdx>0 ){
|
||||
aRegIdx = sqlite3DbMallocRaw(db, sizeof(Index*) * nIdx );
|
||||
if( aRegIdx==0 ) goto update_cleanup;
|
||||
}
|
||||
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
|
||||
int reg;
|
||||
if( hasFK || chngRowid || pIdx->pPartIdxWhere ){
|
||||
if( chngKey || hasFK || pIdx->pPartIdxWhere || pIdx==pPk ){
|
||||
reg = ++pParse->nMem;
|
||||
}else{
|
||||
reg = 0;
|
||||
for(i=0; i<pIdx->nColumn; i++){
|
||||
for(i=0; i<pIdx->nKeyCol; i++){
|
||||
if( aXRef[pIdx->aiColumn[i]]>=0 ){
|
||||
reg = ++pParse->nMem;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( reg==0 ) aToOpen[j+1] = 0;
|
||||
aRegIdx[j] = reg;
|
||||
}
|
||||
|
||||
@@ -280,11 +311,11 @@ void sqlite3Update(
|
||||
/* Allocate required registers. */
|
||||
regRowSet = ++pParse->nMem;
|
||||
regOldRowid = regNewRowid = ++pParse->nMem;
|
||||
if( pTrigger || hasFK ){
|
||||
if( chngPk || pTrigger || hasFK ){
|
||||
regOld = pParse->nMem + 1;
|
||||
pParse->nMem += pTab->nCol;
|
||||
}
|
||||
if( chngRowid || pTrigger || hasFK ){
|
||||
if( chngKey || pTrigger || hasFK ){
|
||||
regNewRowid = ++pParse->nMem;
|
||||
}
|
||||
regNew = pParse->nMem + 1;
|
||||
@@ -300,7 +331,7 @@ void sqlite3Update(
|
||||
*/
|
||||
#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
|
||||
if( isView ){
|
||||
sqlite3MaterializeView(pParse, pTab, pWhere, iCur);
|
||||
sqlite3MaterializeView(pParse, pTab, pWhere, iDataCur);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -313,24 +344,58 @@ void sqlite3Update(
|
||||
|
||||
/* Begin the database scan
|
||||
*/
|
||||
sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid);
|
||||
pWInfo = sqlite3WhereBegin(
|
||||
pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED, 0
|
||||
);
|
||||
if( pWInfo==0 ) goto update_cleanup;
|
||||
okOnePass = sqlite3WhereOkOnePass(pWInfo);
|
||||
if( HasRowid(pTab) ){
|
||||
sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid);
|
||||
pWInfo = sqlite3WhereBegin(
|
||||
pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED, iIdxCur
|
||||
);
|
||||
if( pWInfo==0 ) goto update_cleanup;
|
||||
okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
|
||||
|
||||
/* Remember the rowid of every item to be updated.
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid);
|
||||
if( !okOnePass ){
|
||||
sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
|
||||
}
|
||||
|
||||
/* End the database scan loop.
|
||||
*/
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
}else{
|
||||
int iPk; /* First of nPk memory cells holding PRIMARY KEY value */
|
||||
i16 nPk; /* Number of components of the PRIMARY KEY */
|
||||
int addrOpen; /* Address of the OpenEphemeral instruction */
|
||||
|
||||
/* Remember the rowid of every item to be updated.
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regOldRowid);
|
||||
if( !okOnePass ){
|
||||
sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
|
||||
assert( pPk!=0 );
|
||||
nPk = pPk->nKeyCol;
|
||||
iPk = pParse->nMem+1;
|
||||
pParse->nMem += nPk;
|
||||
regKey = ++pParse->nMem;
|
||||
iEph = pParse->nTab++;
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, iPk);
|
||||
addrOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk);
|
||||
sqlite3VdbeSetP4KeyInfo(pParse, pPk);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0,
|
||||
WHERE_ONEPASS_DESIRED, iIdxCur);
|
||||
if( pWInfo==0 ) goto update_cleanup;
|
||||
okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
|
||||
for(i=0; i<nPk; i++){
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, pPk->aiColumn[i],
|
||||
iPk+i);
|
||||
}
|
||||
if( okOnePass ){
|
||||
sqlite3VdbeChangeToNoop(v, addrOpen);
|
||||
nKey = nPk;
|
||||
regKey = iPk;
|
||||
}else{
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, regKey,
|
||||
sqlite3IndexAffinityStr(v, pPk), nPk);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iEph, regKey);
|
||||
}
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
}
|
||||
|
||||
/* End the database scan loop.
|
||||
*/
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
|
||||
/* Initialize the count of updated rows
|
||||
*/
|
||||
if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab ){
|
||||
@@ -338,6 +403,7 @@ void sqlite3Update(
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
|
||||
}
|
||||
|
||||
labelBreak = sqlite3VdbeMakeLabel(v);
|
||||
if( !isView ){
|
||||
/*
|
||||
** Open every index that needs updating. Note that if any
|
||||
@@ -345,68 +411,77 @@ void sqlite3Update(
|
||||
** action, then we need to open all indices because we might need
|
||||
** to be deleting some records.
|
||||
*/
|
||||
if( !okOnePass ) sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite);
|
||||
if( onError==OE_Replace ){
|
||||
openAll = 1;
|
||||
memset(aToOpen, 1, nIdx+1);
|
||||
}else{
|
||||
openAll = 0;
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
if( pIdx->onError==OE_Replace ){
|
||||
openAll = 1;
|
||||
memset(aToOpen, 1, nIdx+1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
|
||||
assert( aRegIdx );
|
||||
if( openAll || aRegIdx[i]>0 ){
|
||||
KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
|
||||
sqlite3VdbeAddOp4(v, OP_OpenWrite, iCur+i+1, pIdx->tnum, iDb,
|
||||
(char*)pKey, P4_KEYINFO_HANDOFF);
|
||||
assert( pParse->nTab>iCur+i+1 );
|
||||
}
|
||||
if( okOnePass ){
|
||||
if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iBaseCur] = 0;
|
||||
if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iBaseCur] = 0;
|
||||
}
|
||||
sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, iBaseCur, aToOpen,
|
||||
0, 0);
|
||||
}
|
||||
|
||||
/* Top of the update loop */
|
||||
if( okOnePass ){
|
||||
int a1 = sqlite3VdbeAddOp1(v, OP_NotNull, regOldRowid);
|
||||
addr = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||
sqlite3VdbeJumpHere(v, a1);
|
||||
if( aToOpen[iDataCur-iBaseCur] ){
|
||||
assert( pPk!=0 );
|
||||
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, regKey, nKey);
|
||||
VdbeCoverageNeverTaken(v);
|
||||
}
|
||||
labelContinue = labelBreak;
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, pPk ? regKey : regOldRowid, labelBreak);
|
||||
VdbeCoverage(v);
|
||||
}else if( pPk ){
|
||||
labelContinue = sqlite3VdbeMakeLabel(v);
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); VdbeCoverage(v);
|
||||
addrTop = sqlite3VdbeAddOp2(v, OP_RowKey, iEph, regKey);
|
||||
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey, 0);
|
||||
VdbeCoverage(v);
|
||||
}else{
|
||||
addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, 0, regOldRowid);
|
||||
labelContinue = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, labelBreak,
|
||||
regOldRowid);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid);
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
|
||||
/* Make cursor iCur point to the record that is being updated. If
|
||||
** this record does not exist for some reason (deleted by a trigger,
|
||||
** for example, then jump to the next iteration of the RowSet loop. */
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid);
|
||||
|
||||
/* If the record number will change, set register regNewRowid to
|
||||
** contain the new value. If the record number is not being modified,
|
||||
** then regNewRowid is the same register as regOldRowid, which is
|
||||
** already populated. */
|
||||
assert( chngRowid || pTrigger || hasFK || regOldRowid==regNewRowid );
|
||||
assert( chngKey || pTrigger || hasFK || regOldRowid==regNewRowid );
|
||||
if( chngRowid ){
|
||||
sqlite3ExprCode(pParse, pRowidExpr, regNewRowid);
|
||||
sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid);
|
||||
sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid); VdbeCoverage(v);
|
||||
}
|
||||
|
||||
/* If there are triggers on this table, populate an array of registers
|
||||
** with the required old.* column data. */
|
||||
if( hasFK || pTrigger ){
|
||||
/* Compute the old pre-UPDATE content of the row being changed, if that
|
||||
** information is needed */
|
||||
if( chngPk || hasFK || pTrigger ){
|
||||
u32 oldmask = (hasFK ? sqlite3FkOldmask(pParse, pTab) : 0);
|
||||
oldmask |= sqlite3TriggerColmask(pParse,
|
||||
pTrigger, pChanges, 0, TRIGGER_BEFORE|TRIGGER_AFTER, pTab, onError
|
||||
);
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( aXRef[i]<0 || oldmask==0xffffffff || (i<32 && (oldmask & (1<<i))) ){
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOld+i);
|
||||
if( oldmask==0xffffffff
|
||||
|| (i<32 && (oldmask & MASKBIT32(i))!=0)
|
||||
|| (pTab->aCol[i].colFlags & COLFLAG_PRIMKEY)!=0
|
||||
){
|
||||
testcase( oldmask!=0xffffffff && i==31 );
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regOld+i);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, regOld+i);
|
||||
}
|
||||
}
|
||||
if( chngRowid==0 ){
|
||||
if( chngRowid==0 && pPk==0 ){
|
||||
sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid);
|
||||
}
|
||||
}
|
||||
@@ -427,15 +502,15 @@ void sqlite3Update(
|
||||
newmask = sqlite3TriggerColmask(
|
||||
pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError
|
||||
);
|
||||
sqlite3VdbeAddOp3(v, OP_Null, 0, regNew, regNew+pTab->nCol-1);
|
||||
/*sqlite3VdbeAddOp3(v, OP_Null, 0, regNew, regNew+pTab->nCol-1);*/
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( i==pTab->iPKey ){
|
||||
/*sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);*/
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);
|
||||
}else{
|
||||
j = aXRef[i];
|
||||
if( j>=0 ){
|
||||
sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regNew+i);
|
||||
}else if( 0==(tmask&TRIGGER_BEFORE) || i>31 || (newmask&(1<<i)) ){
|
||||
}else if( 0==(tmask&TRIGGER_BEFORE) || i>31 || (newmask & MASKBIT32(i)) ){
|
||||
/* This branch loads the value of a column that will not be changed
|
||||
** into a register. This is done if there are no BEFORE triggers, or
|
||||
** if there are one or more BEFORE triggers that use this value via
|
||||
@@ -443,8 +518,9 @@ void sqlite3Update(
|
||||
*/
|
||||
testcase( i==31 );
|
||||
testcase( i==32 );
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew+i);
|
||||
sqlite3ColumnDefault(v, pTab, i, regNew+i);
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regNew+i);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -453,10 +529,9 @@ void sqlite3Update(
|
||||
** verified. One could argue that this is wrong.
|
||||
*/
|
||||
if( tmask&TRIGGER_BEFORE ){
|
||||
sqlite3VdbeAddOp2(v, OP_Affinity, regNew, pTab->nCol);
|
||||
sqlite3TableAffinityStr(v, pTab);
|
||||
sqlite3TableAffinity(v, pTab, regNew);
|
||||
sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
|
||||
TRIGGER_BEFORE, pTab, regOldRowid, onError, addr);
|
||||
TRIGGER_BEFORE, pTab, regOldRowid, onError, labelContinue);
|
||||
|
||||
/* The row-trigger may have deleted the row being updated. In this
|
||||
** case, jump to the next row. No updates or AFTER triggers are
|
||||
@@ -464,7 +539,13 @@ void sqlite3Update(
|
||||
** is deleted or renamed by a BEFORE trigger - is left undefined in the
|
||||
** documentation.
|
||||
*/
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid);
|
||||
if( pPk ){
|
||||
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue,regKey,nKey);
|
||||
VdbeCoverage(v);
|
||||
}else{
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid);
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
|
||||
/* If it did not delete it, the row-trigger may still have modified
|
||||
** some of the columns of the row being updated. Load the values for
|
||||
@@ -473,46 +554,57 @@ void sqlite3Update(
|
||||
*/
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( aXRef[i]<0 && i!=pTab->iPKey ){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew+i);
|
||||
sqlite3ColumnDefault(v, pTab, i, regNew+i);
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regNew+i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !isView ){
|
||||
int j1; /* Address of jump instruction */
|
||||
int j1 = 0; /* Address of jump instruction */
|
||||
int bReplace = 0; /* True if REPLACE conflict resolution might happen */
|
||||
|
||||
/* Do constraint checks. */
|
||||
sqlite3GenerateConstraintChecks(pParse, pTab, iCur, regNewRowid,
|
||||
aRegIdx, (chngRowid?regOldRowid:0), 1, onError, addr, 0);
|
||||
assert( regOldRowid>0 );
|
||||
sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur,
|
||||
regNewRowid, regOldRowid, chngKey, onError, labelContinue, &bReplace);
|
||||
|
||||
/* Do FK constraint checks. */
|
||||
if( hasFK ){
|
||||
sqlite3FkCheck(pParse, pTab, regOldRowid, 0);
|
||||
sqlite3FkCheck(pParse, pTab, regOldRowid, 0, aXRef, chngKey);
|
||||
}
|
||||
|
||||
/* Delete the index entries associated with the current record. */
|
||||
j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regOldRowid);
|
||||
sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, aRegIdx);
|
||||
if( bReplace || chngKey ){
|
||||
if( pPk ){
|
||||
j1 = sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, 0, regKey, nKey);
|
||||
}else{
|
||||
j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, 0, regOldRowid);
|
||||
}
|
||||
VdbeCoverageNeverTaken(v);
|
||||
}
|
||||
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, aRegIdx);
|
||||
|
||||
/* If changing the record number, delete the old record. */
|
||||
if( hasFK || chngRowid ){
|
||||
sqlite3VdbeAddOp2(v, OP_Delete, iCur, 0);
|
||||
if( hasFK || chngKey || pPk!=0 ){
|
||||
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, 0);
|
||||
}
|
||||
if( bReplace || chngKey ){
|
||||
sqlite3VdbeJumpHere(v, j1);
|
||||
}
|
||||
sqlite3VdbeJumpHere(v, j1);
|
||||
|
||||
if( hasFK ){
|
||||
sqlite3FkCheck(pParse, pTab, 0, regNewRowid);
|
||||
sqlite3FkCheck(pParse, pTab, 0, regNewRowid, aXRef, chngKey);
|
||||
}
|
||||
|
||||
/* Insert the new index entries and the new record. */
|
||||
sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid, aRegIdx, 1, 0, 0);
|
||||
sqlite3CompleteInsertion(pParse, pTab, iDataCur, iIdxCur,
|
||||
regNewRowid, aRegIdx, 1, 0, 0);
|
||||
|
||||
/* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to
|
||||
** handle rows (possibly in other tables) that refer via a foreign key
|
||||
** to the row just updated. */
|
||||
if( hasFK ){
|
||||
sqlite3FkActions(pParse, pTab, pChanges, regOldRowid);
|
||||
sqlite3FkActions(pParse, pTab, pChanges, regOldRowid, aXRef, chngKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,22 +615,29 @@ void sqlite3Update(
|
||||
}
|
||||
|
||||
sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
|
||||
TRIGGER_AFTER, pTab, regOldRowid, onError, addr);
|
||||
TRIGGER_AFTER, pTab, regOldRowid, onError, labelContinue);
|
||||
|
||||
/* Repeat the above with the next record to be updated, until
|
||||
** all record selected by the WHERE clause have been updated.
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
if( okOnePass ){
|
||||
/* Nothing to do at end-of-loop for a single-pass */
|
||||
}else if( pPk ){
|
||||
sqlite3VdbeResolveLabel(v, labelContinue);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); VdbeCoverage(v);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, labelContinue);
|
||||
}
|
||||
sqlite3VdbeResolveLabel(v, labelBreak);
|
||||
|
||||
/* Close all tables */
|
||||
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
|
||||
assert( aRegIdx );
|
||||
if( openAll || aRegIdx[i]>0 ){
|
||||
sqlite3VdbeAddOp2(v, OP_Close, iCur+i+1, 0);
|
||||
if( aToOpen[i+1] ){
|
||||
sqlite3VdbeAddOp2(v, OP_Close, iIdxCur+i, 0);
|
||||
}
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Close, iCur, 0);
|
||||
if( iDataCur<iIdxCur ) sqlite3VdbeAddOp2(v, OP_Close, iDataCur, 0);
|
||||
|
||||
/* Update the sqlite_sequence table by storing the content of the
|
||||
** maximum rowid counter values recorded while inserting into
|
||||
@@ -561,8 +660,7 @@ void sqlite3Update(
|
||||
|
||||
update_cleanup:
|
||||
sqlite3AuthContextPop(&sContext);
|
||||
sqlite3DbFree(db, aRegIdx);
|
||||
sqlite3DbFree(db, aXRef);
|
||||
sqlite3DbFree(db, aXRef); /* Also frees aRegIdx[] and aToOpen[] */
|
||||
sqlite3SrcListDelete(db, pTabList);
|
||||
sqlite3ExprListDelete(db, pChanges);
|
||||
sqlite3ExprDelete(db, pWhere);
|
||||
@@ -655,7 +753,7 @@ static void updateVirtualTable(
|
||||
/* Generate code to scan the ephemeral table and call VUpdate. */
|
||||
iReg = ++pParse->nMem;
|
||||
pParse->nMem += pTab->nCol+1;
|
||||
addr = sqlite3VdbeAddOp2(v, OP_Rewind, ephemTab, 0);
|
||||
addr = sqlite3VdbeAddOp2(v, OP_Rewind, ephemTab, 0); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, ephemTab, 0, iReg);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, ephemTab, (pRowid?1:0), iReg+1);
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
@@ -665,7 +763,7 @@ static void updateVirtualTable(
|
||||
sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab->nCol+2, iReg, pVTab, P4_VTAB);
|
||||
sqlite3VdbeChangeP5(v, onError==OE_Default ? OE_Abort : onError);
|
||||
sqlite3MayAbort(pParse);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1); VdbeCoverage(v);
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0);
|
||||
|
||||
|
||||
@@ -317,7 +317,7 @@ int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){
|
||||
sqlite3VdbeMemRelease(pMem);
|
||||
pMem->flags &= ~(MEM_Static|MEM_Dyn|MEM_Ephem);
|
||||
pMem->enc = desiredEnc;
|
||||
pMem->flags |= (MEM_Term|MEM_Dyn);
|
||||
pMem->flags |= (MEM_Term);
|
||||
pMem->z = (char*)zOut;
|
||||
pMem->zMalloc = pMem->z;
|
||||
|
||||
@@ -445,37 +445,10 @@ char *sqlite3Utf16to8(sqlite3 *db, const void *z, int nByte, u8 enc){
|
||||
}
|
||||
assert( (m.flags & MEM_Term)!=0 || db->mallocFailed );
|
||||
assert( (m.flags & MEM_Str)!=0 || db->mallocFailed );
|
||||
assert( (m.flags & MEM_Dyn)!=0 || db->mallocFailed );
|
||||
assert( m.z || db->mallocFailed );
|
||||
return m.z;
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert a UTF-8 string to the UTF-16 encoding specified by parameter
|
||||
** enc. A pointer to the new string is returned, and the value of *pnOut
|
||||
** is set to the length of the returned string in bytes. The call should
|
||||
** arrange to call sqlite3DbFree() on the returned pointer when it is
|
||||
** no longer required.
|
||||
**
|
||||
** If a malloc failure occurs, NULL is returned and the db.mallocFailed
|
||||
** flag set.
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
char *sqlite3Utf8to16(sqlite3 *db, u8 enc, char *z, int n, int *pnOut){
|
||||
Mem m;
|
||||
memset(&m, 0, sizeof(m));
|
||||
m.db = db;
|
||||
sqlite3VdbeMemSetStr(&m, z, n, SQLITE_UTF8, SQLITE_STATIC);
|
||||
if( sqlite3VdbeMemTranslate(&m, enc) ){
|
||||
assert( db->mallocFailed );
|
||||
return 0;
|
||||
}
|
||||
assert( m.z==m.zMalloc );
|
||||
*pnOut = m.n;
|
||||
return m.z;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** zIn is a UTF-16 encoded unicode string at least nChar characters long.
|
||||
** Return the number of bytes in the first nChar unicode characters
|
||||
|
||||
+112
-25
@@ -115,18 +115,17 @@ int sqlite3Strlen30(const char *z){
|
||||
** to NULL.
|
||||
*/
|
||||
void sqlite3Error(sqlite3 *db, int err_code, const char *zFormat, ...){
|
||||
if( db && (db->pErr || (db->pErr = sqlite3ValueNew(db))!=0) ){
|
||||
db->errCode = err_code;
|
||||
if( zFormat ){
|
||||
char *z;
|
||||
va_list ap;
|
||||
va_start(ap, zFormat);
|
||||
z = sqlite3VMPrintf(db, zFormat, ap);
|
||||
va_end(ap);
|
||||
sqlite3ValueSetStr(db->pErr, -1, z, SQLITE_UTF8, SQLITE_DYNAMIC);
|
||||
}else{
|
||||
sqlite3ValueSetStr(db->pErr, 0, 0, SQLITE_UTF8, SQLITE_STATIC);
|
||||
}
|
||||
assert( db!=0 );
|
||||
db->errCode = err_code;
|
||||
if( zFormat && (db->pErr || (db->pErr = sqlite3ValueNew(db))!=0) ){
|
||||
char *z;
|
||||
va_list ap;
|
||||
va_start(ap, zFormat);
|
||||
z = sqlite3VMPrintf(db, zFormat, ap);
|
||||
va_end(ap);
|
||||
sqlite3ValueSetStr(db->pErr, -1, z, SQLITE_UTF8, SQLITE_DYNAMIC);
|
||||
}else if( db->pErr ){
|
||||
sqlite3ValueSetNull(db->pErr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,7 +192,8 @@ int sqlite3Dequote(char *z){
|
||||
case '[': quote = ']'; break; /* For MS SqlServer compatibility */
|
||||
default: return -1;
|
||||
}
|
||||
for(i=1, j=0; ALWAYS(z[i]); i++){
|
||||
for(i=1, j=0;; i++){
|
||||
assert( z[i] );
|
||||
if( z[i]==quote ){
|
||||
if( z[i+1]==quote ){
|
||||
z[j++] = quote;
|
||||
@@ -464,12 +464,12 @@ static int compare2pow63(const char *zNum, int incr){
|
||||
** If the zNum value is representable as a 64-bit twos-complement
|
||||
** integer, then write that value into *pNum and return 0.
|
||||
**
|
||||
** If zNum is exactly 9223372036854665808, return 2. This special
|
||||
** case is broken out because while 9223372036854665808 cannot be a
|
||||
** signed 64-bit integer, its negative -9223372036854665808 can be.
|
||||
** If zNum is exactly 9223372036854775808, return 2. This special
|
||||
** case is broken out because while 9223372036854775808 cannot be a
|
||||
** signed 64-bit integer, its negative -9223372036854775808 can be.
|
||||
**
|
||||
** If zNum is too big for a 64-bit integer and is not
|
||||
** 9223372036854665808 or if zNum contains any non-numeric text,
|
||||
** 9223372036854775808 or if zNum contains any non-numeric text,
|
||||
** then return 1.
|
||||
**
|
||||
** length is the number of bytes in the string (bytes, not characters).
|
||||
@@ -511,7 +511,7 @@ int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){
|
||||
u = u*10 + c - '0';
|
||||
}
|
||||
if( u>LARGEST_INT64 ){
|
||||
*pNum = SMALLEST_INT64;
|
||||
*pNum = neg ? SMALLEST_INT64 : LARGEST_INT64;
|
||||
}else if( neg ){
|
||||
*pNum = -(i64)u;
|
||||
}else{
|
||||
@@ -542,7 +542,6 @@ int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){
|
||||
/* zNum is exactly 9223372036854775808. Fits if negative. The
|
||||
** special case 2 overflow if positive */
|
||||
assert( u-1==LARGEST_INT64 );
|
||||
assert( (*pNum)==SMALLEST_INT64 );
|
||||
return neg ? 0 : 2;
|
||||
}
|
||||
}
|
||||
@@ -1002,7 +1001,8 @@ int sqlite3VarintLen(u64 v){
|
||||
** Read or write a four-byte big-endian integer value.
|
||||
*/
|
||||
u32 sqlite3Get4byte(const u8 *p){
|
||||
return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
|
||||
testcase( p[0]&0x80 );
|
||||
return ((unsigned)p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
|
||||
}
|
||||
void sqlite3Put4byte(unsigned char *p, u32 v){
|
||||
p[0] = (u8)(v>>24);
|
||||
@@ -1123,13 +1123,12 @@ int sqlite3AddInt64(i64 *pA, i64 iB){
|
||||
testcase( iA>0 && LARGEST_INT64 - iA == iB );
|
||||
testcase( iA>0 && LARGEST_INT64 - iA == iB - 1 );
|
||||
if( iA>0 && LARGEST_INT64 - iA < iB ) return 1;
|
||||
*pA += iB;
|
||||
}else{
|
||||
testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 1 );
|
||||
testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 2 );
|
||||
if( iA<0 && -(iA + LARGEST_INT64) > iB + 1 ) return 1;
|
||||
*pA += iB;
|
||||
}
|
||||
*pA += iB;
|
||||
return 0;
|
||||
}
|
||||
int sqlite3SubInt64(i64 *pA, i64 iB){
|
||||
@@ -1153,9 +1152,18 @@ int sqlite3MulInt64(i64 *pA, i64 iB){
|
||||
iA0 = iA % TWOPOWER32;
|
||||
iB1 = iB/TWOPOWER32;
|
||||
iB0 = iB % TWOPOWER32;
|
||||
if( iA1*iB1 != 0 ) return 1;
|
||||
assert( iA1*iB0==0 || iA0*iB1==0 );
|
||||
r = iA1*iB0 + iA0*iB1;
|
||||
if( iA1==0 ){
|
||||
if( iB1==0 ){
|
||||
*pA *= iB;
|
||||
return 0;
|
||||
}
|
||||
r = iA0*iB1;
|
||||
}else if( iB1==0 ){
|
||||
r = iA1*iB0;
|
||||
}else{
|
||||
/* If both iA1 and iB1 are non-zero, overflow will result */
|
||||
return 1;
|
||||
}
|
||||
testcase( r==(-TWOPOWER31)-1 );
|
||||
testcase( r==(-TWOPOWER31) );
|
||||
testcase( r==TWOPOWER31 );
|
||||
@@ -1207,3 +1215,82 @@ void sqlite3FileSuffix3(const char *zBaseFilename, char *z){
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Find (an approximate) sum of two LogEst values. This computation is
|
||||
** not a simple "+" operator because LogEst is stored as a logarithmic
|
||||
** value.
|
||||
**
|
||||
*/
|
||||
LogEst sqlite3LogEstAdd(LogEst a, LogEst b){
|
||||
static const unsigned char x[] = {
|
||||
10, 10, /* 0,1 */
|
||||
9, 9, /* 2,3 */
|
||||
8, 8, /* 4,5 */
|
||||
7, 7, 7, /* 6,7,8 */
|
||||
6, 6, 6, /* 9,10,11 */
|
||||
5, 5, 5, /* 12-14 */
|
||||
4, 4, 4, 4, /* 15-18 */
|
||||
3, 3, 3, 3, 3, 3, /* 19-24 */
|
||||
2, 2, 2, 2, 2, 2, 2, /* 25-31 */
|
||||
};
|
||||
if( a>=b ){
|
||||
if( a>b+49 ) return a;
|
||||
if( a>b+31 ) return a+1;
|
||||
return a+x[a-b];
|
||||
}else{
|
||||
if( b>a+49 ) return b;
|
||||
if( b>a+31 ) return b+1;
|
||||
return b+x[b-a];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert an integer into a LogEst. In other words, compute a
|
||||
** good approximatation for 10*log2(x).
|
||||
*/
|
||||
LogEst sqlite3LogEst(u64 x){
|
||||
static LogEst a[] = { 0, 2, 3, 5, 6, 7, 8, 9 };
|
||||
LogEst y = 40;
|
||||
if( x<8 ){
|
||||
if( x<2 ) return 0;
|
||||
while( x<8 ){ y -= 10; x <<= 1; }
|
||||
}else{
|
||||
while( x>255 ){ y += 40; x >>= 4; }
|
||||
while( x>15 ){ y += 10; x >>= 1; }
|
||||
}
|
||||
return a[x&7] + y - 10;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
/*
|
||||
** Convert a double into a LogEst
|
||||
** In other words, compute an approximation for 10*log2(x).
|
||||
*/
|
||||
LogEst sqlite3LogEstFromDouble(double x){
|
||||
u64 a;
|
||||
LogEst e;
|
||||
assert( sizeof(x)==8 && sizeof(a)==8 );
|
||||
if( x<=1 ) return 0;
|
||||
if( x<=2000000000 ) return sqlite3LogEst((u64)x);
|
||||
memcpy(&a, &x, 8);
|
||||
e = (a>>52) - 1022;
|
||||
return e*10;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
/*
|
||||
** Convert a LogEst into an integer.
|
||||
*/
|
||||
u64 sqlite3LogEstToInt(LogEst x){
|
||||
u64 n;
|
||||
if( x<10 ) return 1;
|
||||
n = x%10;
|
||||
x /= 10;
|
||||
if( n>=5 ) n -= 2;
|
||||
else if( n>=1 ) n -= 1;
|
||||
if( x>=3 ){
|
||||
return x>60 ? (u64)LARGEST_INT64 : (n+8)<<(x-3);
|
||||
}
|
||||
return (n+8)>>(3-x);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user