mirror of
https://github.com/status-im/sqlcipher.git
synced 2026-08-30 22:11:14 +00:00
Merge branch 'prerelease' into ditto
This commit is contained in:
@@ -37,3 +37,4 @@ xcuserdata/*
|
||||
/sqlcipher.pc
|
||||
/sqlite3ext.h
|
||||
/libsqlcipher.la
|
||||
/.DS_Store
|
||||
|
||||
+37
-2
@@ -259,8 +259,10 @@ SRC = \
|
||||
$(TOP)/src/os.c \
|
||||
$(TOP)/src/os.h \
|
||||
$(TOP)/src/os_common.h \
|
||||
$(TOP)/src/os_setup.h \
|
||||
$(TOP)/src/os_unix.c \
|
||||
$(TOP)/src/os_win.c \
|
||||
$(TOP)/src/os_win.h \
|
||||
$(TOP)/src/pager.c \
|
||||
$(TOP)/src/pager.h \
|
||||
$(TOP)/src/parse.y \
|
||||
@@ -301,7 +303,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
|
||||
#
|
||||
@@ -410,12 +413,14 @@ TESTSRC = \
|
||||
TESTSRC += \
|
||||
$(TOP)/ext/misc/amatch.c \
|
||||
$(TOP)/ext/misc/closure.c \
|
||||
$(TOP)/ext/misc/fileio.c \
|
||||
$(TOP)/ext/misc/fuzzer.c \
|
||||
$(TOP)/ext/misc/ieee754.c \
|
||||
$(TOP)/ext/misc/nextchar.c \
|
||||
$(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
|
||||
@@ -475,6 +480,8 @@ HDR = \
|
||||
opcodes.h \
|
||||
$(TOP)/src/os.h \
|
||||
$(TOP)/src/os_common.h \
|
||||
$(TOP)/src/os_setup.h \
|
||||
$(TOP)/src/os_win.h \
|
||||
$(TOP)/src/pager.h \
|
||||
$(TOP)/src/pcache.h \
|
||||
parse.h \
|
||||
@@ -484,6 +491,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 +559,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 +972,30 @@ 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.lo
|
||||
$(LTLINK) -o $@ $(TOP)/tool/showdb.c sqlite3.lo $(TLIBS)
|
||||
|
||||
showstat4$(TEXE): $(TOP)/tool/showstat4.c sqlite3.lo
|
||||
$(LTLINK) -o $@ $(TOP)/tool/showstat4.c sqlite3.lo $(TLIBS)
|
||||
|
||||
showjournal$(TEXE): $(TOP)/tool/showjournal.c sqlite3.lo
|
||||
$(LTLINK) -o $@ $(TOP)/tool/showjournal.c sqlite3.lo $(TLIBS)
|
||||
|
||||
showwal$(TEXE): $(TOP)/tool/showwal.c sqlite3.lo
|
||||
$(LTLINK) -o $@ $(TOP)/tool/showwal.c sqlite3.lo $(TLIBS)
|
||||
|
||||
rollback-test$(TEXE): $(TOP)/tool/rollback-test.c sqlite3.lo
|
||||
$(LTLINK) -o $@ $(TOP)/tool/rollback-test.c sqlite3.lo $(TLIBS)
|
||||
|
||||
LogEst$(TEXE): $(TOP)/tool/logest.c sqlite3.h
|
||||
$(LTLINK) -I. -o $@ $(TOP)/tool/logest.c
|
||||
|
||||
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
|
||||
@@ -998,6 +1030,9 @@ clean:
|
||||
rm -rf tsrc .target_source
|
||||
rm -f tclsqlcipher$(TEXE)
|
||||
rm -f testfixture$(TEXE) test.db
|
||||
rm -f LogEst$(TEXE) fts3view$(TEXE) rollback-test$(TEXE) showdb$(TEXE)
|
||||
rm -f showjournal$(TEXE) showstat4$(TEXE) showwal$(TEXE) speedtest1$(TEXE)
|
||||
rm -f wordcount$(TEXE)
|
||||
rm -f sqlite3.dll sqlite3.lib sqlite3.exp sqlite3.def
|
||||
rm -f sqlite3.c
|
||||
rm -f sqlite3rc.h
|
||||
|
||||
+261
-46
@@ -1,6 +1,9 @@
|
||||
#
|
||||
# nmake Makefile for SQLite
|
||||
#
|
||||
###############################################################################
|
||||
############################## START OF OPTIONS ###############################
|
||||
###############################################################################
|
||||
|
||||
# The toplevel directory of the source tree. This is the directory
|
||||
# that contains this "Makefile.msc".
|
||||
@@ -13,6 +16,20 @@ TOP = .
|
||||
USE_AMALGAMATION = 1
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to use the library paths and other options necessary for
|
||||
# Windows Phone 8.1.
|
||||
#
|
||||
!IFNDEF USE_WP81_OPTS
|
||||
USE_WP81_OPTS = 0
|
||||
!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 +42,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 +118,33 @@ 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
|
||||
|
||||
# These are the "standard" SQLite compilation options used when compiling for
|
||||
# the Windows platform.
|
||||
#
|
||||
!IFNDEF OPT_FEATURE_FLAGS
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS3=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RTREE=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1
|
||||
!ENDIF
|
||||
|
||||
###############################################################################
|
||||
############################### END OF OPTIONS ################################
|
||||
###############################################################################
|
||||
|
||||
# This assumes that MSVC is always installed in 32-bit Program Files directory
|
||||
# and sets the variable for use in locating other 32-bit installs accordingly.
|
||||
#
|
||||
PROGRAMFILES_X86 = $(VCINSTALLDIR)\..\..
|
||||
PROGRAMFILES_X86 = $(PROGRAMFILES_X86:\\=\)
|
||||
|
||||
# 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'.
|
||||
@@ -118,6 +169,15 @@ LD = link.exe
|
||||
RC = rc.exe
|
||||
!ENDIF
|
||||
|
||||
# Check for the MSVC runtime library path macro. Othertise, this value will
|
||||
# default to the 'lib' directory underneath the MSVC installation directory.
|
||||
#
|
||||
!IFNDEF CRTLIBPATH
|
||||
CRTLIBPATH = $(VCINSTALLDIR)\lib
|
||||
!ENDIF
|
||||
|
||||
CRTLIBPATH = $(CRTLIBPATH:\\=\)
|
||||
|
||||
# Check for the command macro NCC. This should point to the compiler binary
|
||||
# for the platform the compilation process is taking place on. If it is not
|
||||
# defined, simply define it to have the same value as the CC macro. When
|
||||
@@ -140,14 +200,14 @@ RC = rc.exe
|
||||
!IFDEF NCC
|
||||
NCC = $(NCC:\\=\)
|
||||
!ELSEIF $(XCOMPILE)!=0
|
||||
NCC = "$(VCINSTALLDIR)\bin\cl.exe"
|
||||
NCC = "$(VCINSTALLDIR)\bin\$(CC)"
|
||||
NCC = $(NCC:\\=\)
|
||||
!ELSE
|
||||
NCC = $(CC)
|
||||
!ENDIF
|
||||
|
||||
# Check for the MSVC runtime library path macro. Othertise, this
|
||||
# value will default to the 'lib' directory underneath the MSVC
|
||||
# Check for the MSVC native runtime library path macro. Othertise,
|
||||
# this value will default to the 'lib' directory underneath the MSVC
|
||||
# installation directory.
|
||||
#
|
||||
!IFNDEF NCRTLIBPATH
|
||||
@@ -173,6 +233,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 +252,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 +308,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.
|
||||
@@ -259,7 +344,6 @@ TCC = $(TCC) -DSQLITE_ENABLE_IOTRACE
|
||||
RCC = $(RCC) -DSQLITE_ENABLE_IOTRACE
|
||||
!ENDIF
|
||||
|
||||
#
|
||||
# Prevent warnings about "insecure" MSVC runtime library functions
|
||||
# being used.
|
||||
#
|
||||
@@ -267,28 +351,24 @@ TCC = $(TCC) -D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS
|
||||
BCC = $(BCC) -D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS
|
||||
RCC = $(RCC) -D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS
|
||||
|
||||
#
|
||||
# Prevent warnings about "deprecated" POSIX functions being used.
|
||||
#
|
||||
TCC = $(TCC) -D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_NONSTDC_NO_WARNINGS
|
||||
BCC = $(BCC) -D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_NONSTDC_NO_WARNINGS
|
||||
RCC = $(RCC) -D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_NONSTDC_NO_WARNINGS
|
||||
|
||||
#
|
||||
# Use the SQLite debugging heap subsystem?
|
||||
#
|
||||
!IF $(MEMDEBUG)!=0
|
||||
TCC = $(TCC) -DSQLITE_MEMDEBUG=1
|
||||
RCC = $(RCC) -DSQLITE_MEMDEBUG=1
|
||||
|
||||
#
|
||||
# Use native Win32 heap subsystem instead of malloc/free?
|
||||
#
|
||||
!ELSEIF $(WIN32HEAP)!=0
|
||||
TCC = $(TCC) -DSQLITE_WIN32_MALLOC=1
|
||||
RCC = $(RCC) -DSQLITE_WIN32_MALLOC=1
|
||||
|
||||
#
|
||||
# Validate the heap on every call into the native Win32 heap subsystem?
|
||||
#
|
||||
!IF $(DEBUG)>2
|
||||
@@ -343,11 +423,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
|
||||
#
|
||||
@@ -379,41 +463,57 @@ RCC = $(RCC) -DSQLITE_TEMP_STORE=1
|
||||
# The same set of OMIT and ENABLE flags should be passed to the
|
||||
# LEMON parser generator and the mkkeywordhash tool as well.
|
||||
|
||||
# BEGIN standard options
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS3=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RTREE=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1
|
||||
# END standard options
|
||||
# These are the required SQLite compilation options used when compiling for
|
||||
# the Windows platform.
|
||||
#
|
||||
REQ_FEATURE_FLAGS = $(REQ_FEATURE_FLAGS) -DSQLITE_MAX_TRIGGER_DEPTH=100
|
||||
|
||||
# BEGIN required Windows option
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_MAX_TRIGGER_DEPTH=100
|
||||
# END required Windows option
|
||||
# Add the required and optional SQLite compilation options into the command
|
||||
# lines used to invoke the MSVC code and resource compilers.
|
||||
#
|
||||
TCC = $(TCC) $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS)
|
||||
RCC = $(RCC) $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS)
|
||||
|
||||
TCC = $(TCC) $(OPT_FEATURE_FLAGS)
|
||||
RCC = $(RCC) $(OPT_FEATURE_FLAGS)
|
||||
|
||||
# Add in any optional parameters specified on the make commane line
|
||||
# ie. make "OPTS=-DSQLITE_ENABLE_FOO=1 -DSQLITE_OMIT_FOO=1".
|
||||
# Add in any optional parameters specified on the commane line, e.g.
|
||||
# nmake /f Makefile.msc all "OPTS=-DSQLITE_ENABLE_FOO=1 -DSQLITE_OMIT_FOO=1"
|
||||
#
|
||||
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
|
||||
!ENDIF
|
||||
|
||||
# If ICU support is enabled, add the compiler options for it.
|
||||
#
|
||||
!IF $(USE_ICU)!=0
|
||||
TCC = $(TCC) -DSQLITE_ENABLE_ICU=1
|
||||
RCC = $(RCC) -DSQLITE_ENABLE_ICU=1
|
||||
@@ -425,6 +525,7 @@ RCC = $(RCC) -I$(ICUINCDIR)
|
||||
|
||||
# Command line prefixes for compiling code, compiling resources,
|
||||
# linking, etc.
|
||||
#
|
||||
LTCOMPILE = $(TCC) -Fo$@
|
||||
LTRCOMPILE = $(RCC) -r
|
||||
LTLIB = lib.exe
|
||||
@@ -445,27 +546,75 @@ LTLIBOPTS = /MACHINE:$(PLATFORM)
|
||||
#
|
||||
!IF $(FOR_WINRT)!=0
|
||||
LTLINKOPTS = $(LTLINKOPTS) /APPCONTAINER
|
||||
!IF "$(VISUALSTUDIOVERSION)"=="12.0"
|
||||
!IFNDEF STORELIBPATH
|
||||
!IF "$(PLATFORM)"=="x86"
|
||||
STORELIBPATH = $(CRTLIBPATH)\store
|
||||
!ELSEIF "$(PLATFORM)"=="x64"
|
||||
STORELIBPATH = $(CRTLIBPATH)\store\amd64
|
||||
!ELSEIF "$(PLATFORM)"=="ARM"
|
||||
STORELIBPATH = $(CRTLIBPATH)\store\arm
|
||||
!ELSE
|
||||
STORELIBPATH = $(CRTLIBPATH)\store
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
STORELIBPATH = $(STORELIBPATH:\\=\)
|
||||
LTLINKOPTS = $(LTLINKOPTS) "/LIBPATH:$(STORELIBPATH)"
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
|
||||
# When compiling for Windows Phone 8.1, an extra library path is
|
||||
# required.
|
||||
#
|
||||
!IF $(USE_WP81_OPTS)!=0
|
||||
!IFNDEF WP81LIBPATH
|
||||
!IF "$(PLATFORM)"=="x86"
|
||||
WP81LIBPATH = $(PROGRAMFILES_X86)\Windows Phone Kits\8.1\lib\x86
|
||||
!ELSEIF "$(PLATFORM)"=="ARM"
|
||||
WP81LIBPATH = $(PROGRAMFILES_X86)\Windows Phone Kits\8.1\lib\ARM
|
||||
!ELSE
|
||||
WP81LIBPATH = $(PROGRAMFILES_X86)\Windows Phone Kits\8.1\lib\x86
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
|
||||
# When compiling for Windows Phone 8.1, some extra linker options
|
||||
# are also required.
|
||||
#
|
||||
!IF $(USE_WP81_OPTS)!=0
|
||||
!IFDEF WP81LIBPATH
|
||||
LTLINKOPTS = $(LTLINKOPTS) "/LIBPATH:$(WP81LIBPATH)"
|
||||
!ENDIF
|
||||
LTLINKOPTS = $(LTLINKOPTS) /DYNAMICBASE
|
||||
LTLINKOPTS = $(LTLINKOPTS) WindowsPhoneCore.lib RuntimeObject.lib PhoneAppModelHost.lib
|
||||
LTLINKOPTS = $(LTLINKOPTS) /NODEFAULTLIB:kernel32.lib /NODEFAULTLIB:ole32.lib
|
||||
!ENDIF
|
||||
|
||||
# If either debugging or symbols are enabled, enable PDBs.
|
||||
#
|
||||
!IF $(DEBUG)>0 || $(SYMBOLS)!=0
|
||||
LDFLAGS = /DEBUG
|
||||
!ENDIF
|
||||
|
||||
# Start with the Tcl related linker options.
|
||||
#
|
||||
!IF $(NO_TCL)==0
|
||||
LTLIBPATHS = /LIBPATH:$(TCLLIBDIR)
|
||||
LTLIBS = $(LIBTCL)
|
||||
!ENDIF
|
||||
|
||||
# If ICU support is enabled, add the linker options for it.
|
||||
#
|
||||
!IF $(USE_ICU)!=0
|
||||
LTLIBPATHS = $(LTLIBPATHS) /LIBPATH:$(ICULIBDIR)
|
||||
LTLIBS = $(LTLIBS) $(LIBICU)
|
||||
!ENDIF
|
||||
|
||||
# nawk compatible awk.
|
||||
#
|
||||
!IFNDEF NAWK
|
||||
NAWK = gawk.exe
|
||||
!ENDIF
|
||||
|
||||
# You should not have to change anything below this line
|
||||
###############################################################################
|
||||
@@ -560,8 +709,10 @@ SRC = \
|
||||
$(TOP)\src\os.c \
|
||||
$(TOP)\src\os.h \
|
||||
$(TOP)\src\os_common.h \
|
||||
$(TOP)\src\os_setup.h \
|
||||
$(TOP)\src\os_unix.c \
|
||||
$(TOP)\src\os_win.c \
|
||||
$(TOP)\src\os_win.h \
|
||||
$(TOP)\src\pager.c \
|
||||
$(TOP)\src\pager.h \
|
||||
$(TOP)\src\parse.y \
|
||||
@@ -602,7 +753,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
|
||||
#
|
||||
@@ -710,12 +862,14 @@ TESTSRC = \
|
||||
TESTEXT = \
|
||||
$(TOP)\ext\misc\amatch.c \
|
||||
$(TOP)\ext\misc\closure.c \
|
||||
$(TOP)\ext\misc\fileio.c \
|
||||
$(TOP)\ext\misc\fuzzer.c \
|
||||
$(TOP)\ext\misc\ieee754.c \
|
||||
$(TOP)\ext\misc\nextchar.c \
|
||||
$(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
|
||||
|
||||
|
||||
@@ -779,6 +933,8 @@ HDR = \
|
||||
opcodes.h \
|
||||
$(TOP)\src\os.h \
|
||||
$(TOP)\src\os_common.h \
|
||||
$(TOP)\src\os_setup.h \
|
||||
$(TOP)\src\os_win.h \
|
||||
$(TOP)\src\pager.h \
|
||||
$(TOP)\src\pcache.h \
|
||||
parse.h \
|
||||
@@ -787,7 +943,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 +999,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
|
||||
#
|
||||
@@ -865,7 +1031,7 @@ lempar.c: $(TOP)\src\lempar.c
|
||||
copy $(TOP)\src\lempar.c .
|
||||
|
||||
lemon.exe: $(TOP)\tool\lemon.c lempar.c
|
||||
$(BCC) -Daccess=_access -Fe$@ $(TOP)\tool\lemon.c /link $(NLTLIBPATHS)
|
||||
$(BCC) -Daccess=_access -Fe$@ $(TOP)\tool\lemon.c /link $(NLTLINKOPTS) $(NLTLIBPATHS)
|
||||
|
||||
# Rules to build individual *.lo files from generated *.c files. This
|
||||
# applies to:
|
||||
@@ -1125,15 +1291,15 @@ parse.h: parse.c
|
||||
parse.c: $(TOP)\src\parse.y lemon.exe $(TOP)\addopcodes.awk
|
||||
del /Q parse.y parse.h parse.h.temp
|
||||
copy $(TOP)\src\parse.y .
|
||||
.\lemon.exe $(OPT_FEATURE_FLAGS) $(OPTS) parse.y
|
||||
.\lemon.exe $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(OPTS) parse.y
|
||||
move parse.h parse.h.temp
|
||||
$(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)
|
||||
$(BCC) -Fe$@ $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(OPTS) $(TOP)\tool\mkkeywordhash.c /link $(NLTLINKOPTS) $(NLTLIBPATHS)
|
||||
|
||||
keywordhash.h: $(TOP)\tool\mkkeywordhash.c mkkeywordhash.exe
|
||||
.\mkkeywordhash.exe > keywordhash.h
|
||||
@@ -1217,7 +1383,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 +1396,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 +1414,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 +1425,54 @@ 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)
|
||||
|
||||
showstat4.exe: $(TOP)\tool\showstat4.c $(SQLITE3C)
|
||||
$(LTLINK) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
|
||||
$(TOP)\tool\showstat4.c $(SQLITE3C)
|
||||
|
||||
showjournal.exe: $(TOP)\tool\showjournal.c $(SQLITE3C)
|
||||
$(LTLINK) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
|
||||
$(TOP)\tool\showjournal.c $(SQLITE3C)
|
||||
|
||||
showwal.exe: $(TOP)\tool\showwal.c $(SQLITE3C)
|
||||
$(LTLINK) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
|
||||
$(TOP)\tool\showwal.c $(SQLITE3C)
|
||||
|
||||
fts3view.exe: $(TOP)\ext\fts3\tool\fts3view.c $(SQLITE3C)
|
||||
$(LTLINK) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
|
||||
$(TOP)\ext\fts3\tool\fts3view.c $(SQLITE3C)
|
||||
|
||||
rollback-test.exe: $(TOP)\tool\rollback-test.c $(SQLITE3C)
|
||||
$(LTLINK) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
|
||||
$(TOP)\tool\rollback-test.c $(SQLITE3C)
|
||||
|
||||
LogEst.exe: $(TOP)\tool\logest.c sqlite3.h
|
||||
$(LTLINK) -Fe$@ $(TOP)\tool\LogEst.c
|
||||
|
||||
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 lemon.* lempar.c parse.*
|
||||
del /Q mkkeywordhash.* keywordhash.h
|
||||
del /Q notasharedlib.*
|
||||
-rmdir /Q/S .deps
|
||||
-rmdir /Q/S .libs
|
||||
-rmdir /Q/S quota2a
|
||||
@@ -1270,9 +1481,13 @@ 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 LogEst.exe fts3view.exe rollback-test.exe showdb.exe
|
||||
del /Q showjournal.exe showstat4.exe showwal.exe speedtest1.exe
|
||||
del /Q wordcount.exe
|
||||
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
|
||||
|
||||
@@ -262,8 +262,10 @@ SRC = \
|
||||
$(TOP)/src/os.c \
|
||||
$(TOP)/src/os.h \
|
||||
$(TOP)/src/os_common.h \
|
||||
$(TOP)/src/os_setup.h \
|
||||
$(TOP)/src/os_unix.c \
|
||||
$(TOP)/src/os_win.c \
|
||||
$(TOP)/src/os_win.h \
|
||||
$(TOP)/src/pager.c \
|
||||
$(TOP)/src/pager.h \
|
||||
$(TOP)/src/parse.y \
|
||||
@@ -416,6 +418,8 @@ HDR = \
|
||||
opcodes.h \
|
||||
$(TOP)/src/os.h \
|
||||
$(TOP)/src/os_common.h \
|
||||
$(TOP)/src/os_setup.h \
|
||||
$(TOP)/src/os_win.h \
|
||||
$(TOP)/src/pager.h \
|
||||
$(TOP)/src/pcache.h \
|
||||
parse.h \
|
||||
|
||||
+25
-26
@@ -1,4 +1,4 @@
|
||||
== SQLCipher ==
|
||||
## SQLCipher
|
||||
|
||||
SQLCipher is an SQLite extension that provides transparent 256-bit AES encryption of
|
||||
database files. Pages are encrypted before being written to disk and are decrypted
|
||||
@@ -11,7 +11,7 @@ SQLCipher was initially developed by Stephen Lombardo at Zetetic LLC
|
||||
(sjlombardo@zetetic.net) as the encrypted database layer for Strip,
|
||||
an iPhone data vault and password manager (http://getstrip.com).
|
||||
|
||||
[Features]
|
||||
## Features
|
||||
|
||||
- Fast performance with as little as 5-15% overhead for encryption on many operations
|
||||
- 100% of data in the database file is encrypted
|
||||
@@ -20,7 +20,7 @@ an iPhone data vault and password manager (http://getstrip.com).
|
||||
- Algorithms provided by the peer reviewed OpenSSL crypto library.
|
||||
- Configurable crypto providers
|
||||
|
||||
[Compiling]
|
||||
## Compiling
|
||||
|
||||
Building SQLCipher is almost the same as compiling a regular version of
|
||||
SQLite with two small exceptions:
|
||||
@@ -30,57 +30,56 @@ SQLite with two small exceptions:
|
||||
|
||||
Example Static linking (replace /opt/local/lib with the path to libcrypto.a)
|
||||
|
||||
$ ./configure --enable-tempstore=yes CFLAGS="-DSQLITE_HAS_CODEC" \
|
||||
LDFLAGS="/opt/local/lib/libcrypto.a"
|
||||
$ make
|
||||
$ ./configure --enable-tempstore=yes CFLAGS="-DSQLITE_HAS_CODEC" \
|
||||
LDFLAGS="/opt/local/lib/libcrypto.a"
|
||||
$ make
|
||||
|
||||
Example Dynamic linking
|
||||
|
||||
$ ./configure --enable-tempstore=yes CFLAGS="-DSQLITE_HAS_CODEC" \
|
||||
LDFLAGS="-lcrypto"
|
||||
$ make
|
||||
$ ./configure --enable-tempstore=yes CFLAGS="-DSQLITE_HAS_CODEC" \
|
||||
LDFLAGS="-lcrypto"
|
||||
$ make
|
||||
|
||||
[Encrypting a database]
|
||||
## Encrypting a database
|
||||
|
||||
To specify an encryption passphrase for the database via the SQL interface you
|
||||
use a pragma. The passphrase you enter is passed through PBKDF2 key derivation to
|
||||
obtain the encryption key for the database
|
||||
|
||||
PRAGMA key = 'passphrase';
|
||||
PRAGMA key = 'passphrase';
|
||||
|
||||
Alternately, you can specify an exact byte sequence using a blob literal. If you
|
||||
use this method it is your responsibility to ensure that the data you provide a
|
||||
64 character hex string, which will be converted directly to 32 bytes (256 bits) of
|
||||
key data without key derivation.
|
||||
|
||||
PRAGMA key = "x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'";
|
||||
PRAGMA key = "x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'";
|
||||
|
||||
To encrypt a database programatically you can use the sqlite3_key function.
|
||||
The data provided in pKey is converted to an encryption key according to the
|
||||
same rules as PRAGMA key.
|
||||
|
||||
|
||||
int sqlite3_key(sqlite3 *db, const void *pKey, int nKey);
|
||||
int sqlite3_key(sqlite3 *db, const void *pKey, int nKey);
|
||||
|
||||
PRAGMA key or sqlite3_key should be called as the first operation when a database is open.
|
||||
|
||||
[Changing a database key]
|
||||
## Changing a database key
|
||||
|
||||
To change the encryption passphrase for an existing database you may use the rekey pragma
|
||||
after you've supplied the correct database password;
|
||||
|
||||
PRAGMA key = 'passphrase'; -- start with the existing database passphrase
|
||||
PRAGMA rekey = 'new-passphrase'; -- rekey will reencrypt with the new passphrase
|
||||
PRAGMA key = 'passphrase'; -- start with the existing database passphrase
|
||||
PRAGMA rekey = 'new-passphrase'; -- rekey will reencrypt with the new passphrase
|
||||
|
||||
The hexrekey pragma may be used to rekey to a specific binary value
|
||||
|
||||
PRAGMA rekey = "x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'";
|
||||
PRAGMA rekey = "x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'";
|
||||
|
||||
This can be accomplished programtically by using sqlite3_rekey;
|
||||
|
||||
sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey)
|
||||
sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey)
|
||||
|
||||
[Support]
|
||||
## Support
|
||||
|
||||
The primary avenue for support and discussions is the SQLCipher users mailing list:
|
||||
|
||||
@@ -89,7 +88,7 @@ http://groups.google.com/group/sqlcipher
|
||||
Issues or support questions on using SQLCipher should be entered into the
|
||||
GitHub Issue tracker:
|
||||
|
||||
http://github.com/sjlombardo/sqlcipher/issues
|
||||
https://github.com/sqlcipher/sqlcipher/issues
|
||||
|
||||
Please DO NOT post issues, support questions, or other problems to blog
|
||||
posts about SQLCipher as we do not monitor them frequently.
|
||||
@@ -97,7 +96,7 @@ posts about SQLCipher as we do not monitor them frequently.
|
||||
If you are using SQLCipher in your own software please let us know at
|
||||
support@zetetic.net!
|
||||
|
||||
[License]
|
||||
## License
|
||||
|
||||
Copyright (c) 2008, ZETETIC LLC
|
||||
All rights reserved.
|
||||
@@ -124,11 +123,11 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
== End SQLCipher ==
|
||||
## End SQLCipher
|
||||
|
||||
This directory contains source code to
|
||||
|
||||
SQLite: An Embeddable SQL Database Engine
|
||||
SQLite: An Embeddable SQL Database Engine
|
||||
|
||||
To compile the project, first create a directory in which to place
|
||||
the build products. It is recommended, but not required, that the
|
||||
@@ -138,7 +137,7 @@ 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"
|
||||
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
|
||||
@@ -164,4 +163,4 @@ AWK.
|
||||
|
||||
Contacts:
|
||||
|
||||
http://www.sqlite.org/
|
||||
http://www.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,10 +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.
|
||||
#
|
||||
#
|
||||
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
|
||||
#
|
||||
# Generated by GNU Autoconf 2.62 for sqlcipher 3.8.6.
|
||||
#
|
||||
# This configure script is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy, distribute and modify it.
|
||||
@@ -726,8 +722,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.6'
|
||||
PACKAGE_STRING='sqlite 3.8.6'
|
||||
PACKAGE_BUGREPORT=''
|
||||
PACKAGE_URL=''
|
||||
|
||||
@@ -789,7 +785,6 @@ TCL_SRC_DIR
|
||||
TCL_BIN_DIR
|
||||
TCL_VERSION
|
||||
TARGET_EXEEXT
|
||||
SQLITE_OS_OS2
|
||||
SQLITE_OS_WIN
|
||||
SQLITE_OS_UNIX
|
||||
BUILD_EXEEXT
|
||||
@@ -1458,7 +1453,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.6 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -1523,7 +1518,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.6:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@@ -1641,8 +1636,8 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
sqlcipher configure 3.8.0.2
|
||||
generated by GNU Autoconf 2.69
|
||||
sqlcipher configure 3.8.6
|
||||
generated by GNU Autoconf 2.62
|
||||
|
||||
Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
This configure script is free software; the Free Software Foundation
|
||||
@@ -1651,10 +1646,6 @@ _ACEOF
|
||||
exit
|
||||
fi
|
||||
|
||||
## ------------------------ ##
|
||||
## Autoconf initialization. ##
|
||||
## ------------------------ ##
|
||||
|
||||
# ac_fn_c_try_compile LINENO
|
||||
# --------------------------
|
||||
# Try to compile conftest.$ac_ext, and return whether this succeeded.
|
||||
@@ -2060,7 +2051,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 +3912,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 +5124,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 +6649,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 +6988,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 +7093,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 +7148,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 +9528,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 +9624,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 +10327,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 +10844,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 +10858,6 @@ fi
|
||||
|
||||
|
||||
|
||||
|
||||
##########
|
||||
# Figure out all the parameters needed to compile against Tcl.
|
||||
#
|
||||
@@ -12005,8 +11986,8 @@ 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
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
This file was extended by sqlcipher $as_me 3.8.6, which was
|
||||
generated by GNU Autoconf 2.62. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
CONFIG_HEADERS = $CONFIG_HEADERS
|
||||
@@ -12071,9 +12052,9 @@ _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
|
||||
configured by $0, generated by GNU Autoconf 2.69,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
sqlcipher config.status 3.8.6
|
||||
configured by $0, generated by GNU Autoconf 2.62,
|
||||
with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
|
||||
|
||||
Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
This config.status script is free software; the Free Software Foundation
|
||||
|
||||
+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)
|
||||
|
||||
##########
|
||||
|
||||
+552
-145
File diff suppressed because it is too large
Load Diff
+25
-5
@@ -210,20 +210,20 @@ struct Fts3Table {
|
||||
sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */
|
||||
char *zContentTbl; /* content=xxx option, or NULL */
|
||||
char *zLanguageid; /* languageid=xxx option, or NULL */
|
||||
u8 bAutoincrmerge; /* True if automerge=1 */
|
||||
int nAutoincrmerge; /* Value configured by 'automerge' */
|
||||
u32 nLeafAdd; /* Number of leaf blocks added this trans */
|
||||
|
||||
/* Precompiled statements used by the implementation. Each of these
|
||||
** statements is run and reset within a single virtual table API call.
|
||||
*/
|
||||
sqlite3_stmt *aStmt[37];
|
||||
sqlite3_stmt *aStmt[40];
|
||||
|
||||
char *zReadExprlist;
|
||||
char *zWriteExprlist;
|
||||
|
||||
int nNodeSize; /* Soft limit for node size */
|
||||
u8 bFts4; /* True for FTS4, false for FTS3 */
|
||||
u8 bHasStat; /* True if %_stat table exists */
|
||||
u8 bHasStat; /* True if %_stat table exists (2==unknown) */
|
||||
u8 bHasDocsize; /* True if %_docsize table exists */
|
||||
u8 bDescIdx; /* True if doclists are in reverse order */
|
||||
u8 bIgnoreSavepoint; /* True to ignore xSavepoint invocations */
|
||||
@@ -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 *);
|
||||
@@ -565,7 +585,7 @@ int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr);
|
||||
int sqlite3Fts3InitTok(sqlite3*, Fts3Hash *);
|
||||
|
||||
/* fts3_unicode2.c (functions generated by parsing unicode text files) */
|
||||
#ifdef SQLITE_ENABLE_FTS4_UNICODE61
|
||||
#ifndef SQLITE_DISABLE_FTS3_UNICODE
|
||||
int sqlite3FtsUnicodeFold(int, int);
|
||||
int sqlite3FtsUnicodeIsalnum(int);
|
||||
int sqlite3FtsUnicodeIsdiacritic(int);
|
||||
|
||||
+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;
|
||||
|
||||
+108
-103
@@ -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
|
||||
@@ -180,9 +185,16 @@ static int getNextToken(
|
||||
int rc;
|
||||
sqlite3_tokenizer_cursor *pCursor;
|
||||
Fts3Expr *pRet = 0;
|
||||
int nConsumed = 0;
|
||||
int i = 0;
|
||||
|
||||
rc = sqlite3Fts3OpenTokenizer(pTokenizer, pParse->iLangid, z, n, &pCursor);
|
||||
/* Set variable i to the maximum number of bytes of input to tokenize. */
|
||||
for(i=0; i<n; i++){
|
||||
if( sqlite3_fts3_enable_parentheses && (z[i]=='(' || z[i]==')') ) break;
|
||||
if( z[i]=='*' || z[i]=='"' ) break;
|
||||
}
|
||||
|
||||
*pnConsumed = i;
|
||||
rc = sqlite3Fts3OpenTokenizer(pTokenizer, pParse->iLangid, z, i, &pCursor);
|
||||
if( rc==SQLITE_OK ){
|
||||
const char *zToken;
|
||||
int nToken = 0, iStart = 0, iEnd = 0, iPosition = 0;
|
||||
@@ -223,13 +235,14 @@ static int getNextToken(
|
||||
}
|
||||
|
||||
}
|
||||
nConsumed = iEnd;
|
||||
*pnConsumed = iEnd;
|
||||
}else if( i && rc==SQLITE_DONE ){
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
pModule->xClose(pCursor);
|
||||
}
|
||||
|
||||
*pnConsumed = nConsumed;
|
||||
*ppExpr = pRet;
|
||||
return rc;
|
||||
}
|
||||
@@ -369,12 +382,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 +478,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
|
||||
@@ -506,6 +492,21 @@ static int getNextNode(
|
||||
return getNextString(pParse, &zInput[1], ii-1, ppExpr);
|
||||
}
|
||||
|
||||
if( sqlite3_fts3_enable_parentheses ){
|
||||
if( *zInput=='(' ){
|
||||
int nConsumed = 0;
|
||||
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;
|
||||
}else if( *zInput==')' ){
|
||||
pParse->nNest--;
|
||||
*pnConsumed = (int)((zInput - z) + 1);
|
||||
*ppExpr = 0;
|
||||
return SQLITE_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
/* If control flows to this point, this must be a regular token, or
|
||||
** the end of the input. Read a regular token using the sqlite3_tokenizer
|
||||
@@ -624,96 +625,100 @@ static int fts3ExprParse(
|
||||
while( rc==SQLITE_OK ){
|
||||
Fts3Expr *p = 0;
|
||||
int nByte = 0;
|
||||
|
||||
rc = getNextNode(pParse, zIn, nIn, &p, &nByte);
|
||||
assert( nByte>0 || (rc!=SQLITE_OK && p==0) );
|
||||
if( rc==SQLITE_OK ){
|
||||
int isPhrase;
|
||||
if( p ){
|
||||
int isPhrase;
|
||||
|
||||
if( !sqlite3_fts3_enable_parentheses
|
||||
&& p->eType==FTSQUERY_PHRASE && pParse->isNot
|
||||
){
|
||||
/* Create an implicit NOT operator. */
|
||||
Fts3Expr *pNot = fts3MallocZero(sizeof(Fts3Expr));
|
||||
if( !pNot ){
|
||||
sqlite3Fts3ExprFree(p);
|
||||
rc = SQLITE_NOMEM;
|
||||
goto exprparse_out;
|
||||
}
|
||||
pNot->eType = FTSQUERY_NOT;
|
||||
pNot->pRight = p;
|
||||
p->pParent = pNot;
|
||||
if( pNotBranch ){
|
||||
pNot->pLeft = pNotBranch;
|
||||
pNotBranch->pParent = pNot;
|
||||
}
|
||||
pNotBranch = pNot;
|
||||
p = pPrev;
|
||||
}else{
|
||||
int eType = p->eType;
|
||||
isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft);
|
||||
|
||||
/* The isRequirePhrase variable is set to true if a phrase or
|
||||
** an expression contained in parenthesis is required. If a
|
||||
** binary operator (AND, OR, NOT or NEAR) is encounted when
|
||||
** isRequirePhrase is set, this is a syntax error.
|
||||
*/
|
||||
if( !isPhrase && isRequirePhrase ){
|
||||
sqlite3Fts3ExprFree(p);
|
||||
rc = SQLITE_ERROR;
|
||||
goto exprparse_out;
|
||||
}
|
||||
|
||||
if( isPhrase && !isRequirePhrase ){
|
||||
/* Insert an implicit AND operator. */
|
||||
Fts3Expr *pAnd;
|
||||
assert( pRet && pPrev );
|
||||
pAnd = fts3MallocZero(sizeof(Fts3Expr));
|
||||
if( !pAnd ){
|
||||
if( !sqlite3_fts3_enable_parentheses
|
||||
&& p->eType==FTSQUERY_PHRASE && pParse->isNot
|
||||
){
|
||||
/* Create an implicit NOT operator. */
|
||||
Fts3Expr *pNot = fts3MallocZero(sizeof(Fts3Expr));
|
||||
if( !pNot ){
|
||||
sqlite3Fts3ExprFree(p);
|
||||
rc = SQLITE_NOMEM;
|
||||
goto exprparse_out;
|
||||
}
|
||||
pAnd->eType = FTSQUERY_AND;
|
||||
insertBinaryOperator(&pRet, pPrev, pAnd);
|
||||
pPrev = pAnd;
|
||||
}
|
||||
pNot->eType = FTSQUERY_NOT;
|
||||
pNot->pRight = p;
|
||||
p->pParent = pNot;
|
||||
if( pNotBranch ){
|
||||
pNot->pLeft = pNotBranch;
|
||||
pNotBranch->pParent = pNot;
|
||||
}
|
||||
pNotBranch = pNot;
|
||||
p = pPrev;
|
||||
}else{
|
||||
int eType = p->eType;
|
||||
isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft);
|
||||
|
||||
/* This test catches attempts to make either operand of a NEAR
|
||||
** operator something other than a phrase. For example, either of
|
||||
** the following:
|
||||
**
|
||||
** (bracketed expression) NEAR phrase
|
||||
** phrase NEAR (bracketed expression)
|
||||
**
|
||||
** Return an error in either case.
|
||||
*/
|
||||
if( pPrev && (
|
||||
/* The isRequirePhrase variable is set to true if a phrase or
|
||||
** an expression contained in parenthesis is required. If a
|
||||
** binary operator (AND, OR, NOT or NEAR) is encounted when
|
||||
** isRequirePhrase is set, this is a syntax error.
|
||||
*/
|
||||
if( !isPhrase && isRequirePhrase ){
|
||||
sqlite3Fts3ExprFree(p);
|
||||
rc = SQLITE_ERROR;
|
||||
goto exprparse_out;
|
||||
}
|
||||
|
||||
if( isPhrase && !isRequirePhrase ){
|
||||
/* Insert an implicit AND operator. */
|
||||
Fts3Expr *pAnd;
|
||||
assert( pRet && pPrev );
|
||||
pAnd = fts3MallocZero(sizeof(Fts3Expr));
|
||||
if( !pAnd ){
|
||||
sqlite3Fts3ExprFree(p);
|
||||
rc = SQLITE_NOMEM;
|
||||
goto exprparse_out;
|
||||
}
|
||||
pAnd->eType = FTSQUERY_AND;
|
||||
insertBinaryOperator(&pRet, pPrev, pAnd);
|
||||
pPrev = pAnd;
|
||||
}
|
||||
|
||||
/* This test catches attempts to make either operand of a NEAR
|
||||
** operator something other than a phrase. For example, either of
|
||||
** the following:
|
||||
**
|
||||
** (bracketed expression) NEAR phrase
|
||||
** phrase NEAR (bracketed expression)
|
||||
**
|
||||
** Return an error in either case.
|
||||
*/
|
||||
if( pPrev && (
|
||||
(eType==FTSQUERY_NEAR && !isPhrase && pPrev->eType!=FTSQUERY_PHRASE)
|
||||
|| (eType!=FTSQUERY_PHRASE && isPhrase && pPrev->eType==FTSQUERY_NEAR)
|
||||
)){
|
||||
sqlite3Fts3ExprFree(p);
|
||||
rc = SQLITE_ERROR;
|
||||
goto exprparse_out;
|
||||
}
|
||||
|
||||
if( isPhrase ){
|
||||
if( pRet ){
|
||||
assert( pPrev && pPrev->pLeft && pPrev->pRight==0 );
|
||||
pPrev->pRight = p;
|
||||
p->pParent = pPrev;
|
||||
}else{
|
||||
pRet = p;
|
||||
)){
|
||||
sqlite3Fts3ExprFree(p);
|
||||
rc = SQLITE_ERROR;
|
||||
goto exprparse_out;
|
||||
}
|
||||
}else{
|
||||
insertBinaryOperator(&pRet, pPrev, p);
|
||||
|
||||
if( isPhrase ){
|
||||
if( pRet ){
|
||||
assert( pPrev && pPrev->pLeft && pPrev->pRight==0 );
|
||||
pPrev->pRight = p;
|
||||
p->pParent = pPrev;
|
||||
}else{
|
||||
pRet = p;
|
||||
}
|
||||
}else{
|
||||
insertBinaryOperator(&pRet, pPrev, p);
|
||||
}
|
||||
isRequirePhrase = !isPhrase;
|
||||
}
|
||||
isRequirePhrase = !isPhrase;
|
||||
pPrev = p;
|
||||
}
|
||||
assert( nByte>0 );
|
||||
}
|
||||
assert( rc!=SQLITE_OK || (nByte>0 && nByte<=nIn) );
|
||||
nIn -= nByte;
|
||||
zIn += nByte;
|
||||
pPrev = p;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_DONE && pRet && isRequirePhrase ){
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
** Implementation of the "unicode" full-text-search tokenizer.
|
||||
*/
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS4_UNICODE61
|
||||
#ifndef SQLITE_DISABLE_FTS3_UNICODE
|
||||
|
||||
#include "fts3Int.h"
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
|
||||
@@ -231,7 +231,7 @@ static int unicodeCreate(
|
||||
|
||||
for(i=0; rc==SQLITE_OK && i<nArg; i++){
|
||||
const char *z = azArg[i];
|
||||
int n = strlen(z);
|
||||
int n = (int)strlen(z);
|
||||
|
||||
if( n==19 && memcmp("remove_diacritics=1", z, 19)==0 ){
|
||||
pNew->bRemoveDiacritic = 1;
|
||||
@@ -318,7 +318,7 @@ static int unicodeNext(
|
||||
){
|
||||
unicode_cursor *pCsr = (unicode_cursor *)pC;
|
||||
unicode_tokenizer *p = ((unicode_tokenizer *)pCsr->base.pTokenizer);
|
||||
int iCode;
|
||||
int iCode = 0;
|
||||
char *zOut;
|
||||
const unsigned char *z = &pCsr->aInput[pCsr->iOff];
|
||||
const unsigned char *zStart = z;
|
||||
@@ -363,11 +363,11 @@ static int unicodeNext(
|
||||
);
|
||||
|
||||
/* Set the output variables and return. */
|
||||
pCsr->iOff = (z - pCsr->aInput);
|
||||
pCsr->iOff = (int)(z - pCsr->aInput);
|
||||
*paToken = pCsr->zToken;
|
||||
*pnToken = zOut - pCsr->zToken;
|
||||
*piStart = (zStart - pCsr->aInput);
|
||||
*piEnd = (zEnd - pCsr->aInput);
|
||||
*pnToken = (int)(zOut - pCsr->zToken);
|
||||
*piStart = (int)(zStart - pCsr->aInput);
|
||||
*piEnd = (int)(zEnd - pCsr->aInput);
|
||||
*piPos = pCsr->iToken++;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -390,4 +390,4 @@ void sqlite3Fts3UnicodeTokenizer(sqlite3_tokenizer_module const **ppModule){
|
||||
}
|
||||
|
||||
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */
|
||||
#endif /* ifndef SQLITE_ENABLE_FTS4_UNICODE61 */
|
||||
#endif /* ifndef SQLITE_DISABLE_FTS3_UNICODE */
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
** DO NOT EDIT THIS MACHINE GENERATED FILE.
|
||||
*/
|
||||
|
||||
#if defined(SQLITE_ENABLE_FTS4_UNICODE61)
|
||||
#ifndef SQLITE_DISABLE_FTS3_UNICODE
|
||||
#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
|
||||
|
||||
#include <assert.h>
|
||||
@@ -39,7 +39,7 @@ int sqlite3FtsUnicodeIsalnum(int c){
|
||||
** C. It is not possible to represent a range larger than 1023 codepoints
|
||||
** using this format.
|
||||
*/
|
||||
const static unsigned int aEntry[] = {
|
||||
static const unsigned int aEntry[] = {
|
||||
0x00000030, 0x0000E807, 0x00016C06, 0x0001EC2F, 0x0002AC07,
|
||||
0x0002D001, 0x0002D803, 0x0002EC01, 0x0002FC01, 0x00035C01,
|
||||
0x0003DC01, 0x000B0804, 0x000B480E, 0x000B9407, 0x000BB401,
|
||||
@@ -131,7 +131,7 @@ int sqlite3FtsUnicodeIsalnum(int c){
|
||||
return ( (aAscii[c >> 5] & (1 << (c & 0x001F)))==0 );
|
||||
}else if( c<(1<<22) ){
|
||||
unsigned int key = (((unsigned int)c)<<10) | 0x000003FF;
|
||||
int iRes;
|
||||
int iRes = 0;
|
||||
int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1;
|
||||
int iLo = 0;
|
||||
while( iHi>=iLo ){
|
||||
@@ -202,7 +202,7 @@ static int remove_diacritic(int c){
|
||||
}
|
||||
assert( key>=aDia[iRes] );
|
||||
return ((c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : (int)aChar[iRes]);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
@@ -362,4 +362,4 @@ int sqlite3FtsUnicodeFold(int c, int bRemoveDiacritic){
|
||||
return ret;
|
||||
}
|
||||
#endif /* defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) */
|
||||
#endif /* !defined(SQLITE_ENABLE_FTS4_UNICODE61) */
|
||||
#endif /* !defined(SQLITE_DISABLE_FTS3_UNICODE) */
|
||||
|
||||
+310
-66
@@ -193,6 +193,7 @@ struct SegmentWriter {
|
||||
int nSize; /* Size of allocation at aData */
|
||||
int nData; /* Bytes of data in aData */
|
||||
char *aData; /* Pointer to block from malloc() */
|
||||
i64 nLeafData; /* Number of bytes of leaf data written */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -268,6 +269,10 @@ struct SegmentNode {
|
||||
#define SQL_SELECT_INDEXES 35
|
||||
#define SQL_SELECT_MXLEVEL 36
|
||||
|
||||
#define SQL_SELECT_LEVEL_RANGE2 37
|
||||
#define SQL_UPDATE_LEVEL_IDX 38
|
||||
#define SQL_UPDATE_LEVEL 39
|
||||
|
||||
/*
|
||||
** This function is used to obtain an SQLite prepared statement handle
|
||||
** for the statement identified by the second argument. If successful,
|
||||
@@ -369,7 +374,18 @@ static int fts3SqlStmt(
|
||||
|
||||
/* SQL_SELECT_MXLEVEL
|
||||
** Return the largest relative level in the FTS index or indexes. */
|
||||
/* 36 */ "SELECT max( level %% 1024 ) FROM %Q.'%q_segdir'"
|
||||
/* 36 */ "SELECT max( level %% 1024 ) FROM %Q.'%q_segdir'",
|
||||
|
||||
/* Return segments in order from oldest to newest.*/
|
||||
/* 37 */ "SELECT level, idx, end_block "
|
||||
"FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ? "
|
||||
"ORDER BY level DESC, idx ASC",
|
||||
|
||||
/* Update statements used while promoting segments */
|
||||
/* 38 */ "UPDATE OR FAIL %Q.'%q_segdir' SET level=-1,idx=? "
|
||||
"WHERE level=? AND idx=?",
|
||||
/* 39 */ "UPDATE OR FAIL %Q.'%q_segdir' SET level=? WHERE level=-1"
|
||||
|
||||
};
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3_stmt *pStmt;
|
||||
@@ -1344,8 +1360,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 +1384,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;
|
||||
|
||||
@@ -1910,6 +1926,7 @@ static int fts3WriteSegdir(
|
||||
sqlite3_int64 iStartBlock, /* Value for "start_block" field */
|
||||
sqlite3_int64 iLeafEndBlock, /* Value for "leaves_end_block" field */
|
||||
sqlite3_int64 iEndBlock, /* Value for "end_block" field */
|
||||
sqlite3_int64 nLeafData, /* Bytes of leaf data in segment */
|
||||
char *zRoot, /* Blob value for "root" field */
|
||||
int nRoot /* Number of bytes in buffer zRoot */
|
||||
){
|
||||
@@ -1920,7 +1937,13 @@ static int fts3WriteSegdir(
|
||||
sqlite3_bind_int(pStmt, 2, iIdx);
|
||||
sqlite3_bind_int64(pStmt, 3, iStartBlock);
|
||||
sqlite3_bind_int64(pStmt, 4, iLeafEndBlock);
|
||||
sqlite3_bind_int64(pStmt, 5, iEndBlock);
|
||||
if( nLeafData==0 ){
|
||||
sqlite3_bind_int64(pStmt, 5, iEndBlock);
|
||||
}else{
|
||||
char *zEnd = sqlite3_mprintf("%lld %lld", iEndBlock, nLeafData);
|
||||
if( !zEnd ) return SQLITE_NOMEM;
|
||||
sqlite3_bind_text(pStmt, 5, zEnd, -1, sqlite3_free);
|
||||
}
|
||||
sqlite3_bind_blob(pStmt, 6, zRoot, nRoot, SQLITE_STATIC);
|
||||
sqlite3_step(pStmt);
|
||||
rc = sqlite3_reset(pStmt);
|
||||
@@ -2246,6 +2269,9 @@ static int fts3SegWriterAdd(
|
||||
nDoclist; /* Doclist data */
|
||||
}
|
||||
|
||||
/* Increase the total number of bytes written to account for the new entry. */
|
||||
pWriter->nLeafData += nReq;
|
||||
|
||||
/* If the buffer currently allocated is too small for this entry, realloc
|
||||
** the buffer to make it large enough.
|
||||
*/
|
||||
@@ -2317,13 +2343,13 @@ static int fts3SegWriterFlush(
|
||||
pWriter->iFirst, pWriter->iFree, &iLast, &zRoot, &nRoot);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts3WriteSegdir(
|
||||
p, iLevel, iIdx, pWriter->iFirst, iLastLeaf, iLast, zRoot, nRoot);
|
||||
rc = fts3WriteSegdir(p, iLevel, iIdx,
|
||||
pWriter->iFirst, iLastLeaf, iLast, pWriter->nLeafData, zRoot, nRoot);
|
||||
}
|
||||
}else{
|
||||
/* The entire tree fits on the root node. Write it to the segdir table. */
|
||||
rc = fts3WriteSegdir(
|
||||
p, iLevel, iIdx, 0, 0, 0, pWriter->aData, pWriter->nData);
|
||||
rc = fts3WriteSegdir(p, iLevel, iIdx,
|
||||
0, 0, 0, pWriter->nLeafData, pWriter->aData, pWriter->nData);
|
||||
}
|
||||
p->nLeafAdd++;
|
||||
return rc;
|
||||
@@ -2407,6 +2433,37 @@ static int fts3SegmentMaxLevel(
|
||||
return sqlite3_reset(pStmt);
|
||||
}
|
||||
|
||||
/*
|
||||
** iAbsLevel is an absolute level that may be assumed to exist within
|
||||
** the database. This function checks if it is the largest level number
|
||||
** within its index. Assuming no error occurs, *pbMax is set to 1 if
|
||||
** iAbsLevel is indeed the largest level, or 0 otherwise, and SQLITE_OK
|
||||
** is returned. If an error occurs, an error code is returned and the
|
||||
** final value of *pbMax is undefined.
|
||||
*/
|
||||
static int fts3SegmentIsMaxLevel(Fts3Table *p, i64 iAbsLevel, int *pbMax){
|
||||
|
||||
/* Set pStmt to the compiled version of:
|
||||
**
|
||||
** SELECT max(level) FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ?
|
||||
**
|
||||
** (1024 is actually the value of macro FTS3_SEGDIR_PREFIXLEVEL_STR).
|
||||
*/
|
||||
sqlite3_stmt *pStmt;
|
||||
int rc = fts3SqlStmt(p, SQL_SELECT_SEGDIR_MAX_LEVEL, &pStmt, 0);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
sqlite3_bind_int64(pStmt, 1, iAbsLevel+1);
|
||||
sqlite3_bind_int64(pStmt, 2,
|
||||
((iAbsLevel/FTS3_SEGDIR_MAXLEVEL)+1) * FTS3_SEGDIR_MAXLEVEL
|
||||
);
|
||||
|
||||
*pbMax = 0;
|
||||
if( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
*pbMax = sqlite3_column_type(pStmt, 0)==SQLITE_NULL;
|
||||
}
|
||||
return sqlite3_reset(pStmt);
|
||||
}
|
||||
|
||||
/*
|
||||
** Delete all entries in the %_segments table associated with the segment
|
||||
** opened with seg-reader pSeg. This function does not affect the contents
|
||||
@@ -2529,7 +2586,7 @@ static void fts3ColumnFilter(
|
||||
break;
|
||||
}
|
||||
p = &pList[1];
|
||||
p += sqlite3Fts3GetVarint32(p, &iCurrent);
|
||||
p += fts3GetVarint32(p, &iCurrent);
|
||||
}
|
||||
|
||||
if( bZero && &pList[nList]!=pEnd ){
|
||||
@@ -2942,6 +2999,140 @@ void sqlite3Fts3SegReaderFinish(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Decode the "end_block" field, selected by column iCol of the SELECT
|
||||
** statement passed as the first argument.
|
||||
**
|
||||
** The "end_block" field may contain either an integer, or a text field
|
||||
** containing the text representation of two non-negative integers separated
|
||||
** by one or more space (0x20) characters. In the first case, set *piEndBlock
|
||||
** to the integer value and *pnByte to zero before returning. In the second,
|
||||
** set *piEndBlock to the first value and *pnByte to the second.
|
||||
*/
|
||||
static void fts3ReadEndBlockField(
|
||||
sqlite3_stmt *pStmt,
|
||||
int iCol,
|
||||
i64 *piEndBlock,
|
||||
i64 *pnByte
|
||||
){
|
||||
const unsigned char *zText = sqlite3_column_text(pStmt, iCol);
|
||||
if( zText ){
|
||||
int i;
|
||||
int iMul = 1;
|
||||
i64 iVal = 0;
|
||||
for(i=0; zText[i]>='0' && zText[i]<='9'; i++){
|
||||
iVal = iVal*10 + (zText[i] - '0');
|
||||
}
|
||||
*piEndBlock = iVal;
|
||||
while( zText[i]==' ' ) i++;
|
||||
iVal = 0;
|
||||
if( zText[i]=='-' ){
|
||||
i++;
|
||||
iMul = -1;
|
||||
}
|
||||
for(/* no-op */; zText[i]>='0' && zText[i]<='9'; i++){
|
||||
iVal = iVal*10 + (zText[i] - '0');
|
||||
}
|
||||
*pnByte = (iVal * (i64)iMul);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** A segment of size nByte bytes has just been written to absolute level
|
||||
** iAbsLevel. Promote any segments that should be promoted as a result.
|
||||
*/
|
||||
static int fts3PromoteSegments(
|
||||
Fts3Table *p, /* FTS table handle */
|
||||
sqlite3_int64 iAbsLevel, /* Absolute level just updated */
|
||||
sqlite3_int64 nByte /* Size of new segment at iAbsLevel */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3_stmt *pRange;
|
||||
|
||||
rc = fts3SqlStmt(p, SQL_SELECT_LEVEL_RANGE2, &pRange, 0);
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
int bOk = 0;
|
||||
i64 iLast = (iAbsLevel/FTS3_SEGDIR_MAXLEVEL + 1) * FTS3_SEGDIR_MAXLEVEL - 1;
|
||||
i64 nLimit = (nByte*3)/2;
|
||||
|
||||
/* Loop through all entries in the %_segdir table corresponding to
|
||||
** segments in this index on levels greater than iAbsLevel. If there is
|
||||
** at least one such segment, and it is possible to determine that all
|
||||
** such segments are smaller than nLimit bytes in size, they will be
|
||||
** promoted to level iAbsLevel. */
|
||||
sqlite3_bind_int64(pRange, 1, iAbsLevel+1);
|
||||
sqlite3_bind_int64(pRange, 2, iLast);
|
||||
while( SQLITE_ROW==sqlite3_step(pRange) ){
|
||||
i64 nSize = 0, dummy;
|
||||
fts3ReadEndBlockField(pRange, 2, &dummy, &nSize);
|
||||
if( nSize<=0 || nSize>nLimit ){
|
||||
/* If nSize==0, then the %_segdir.end_block field does not not
|
||||
** contain a size value. This happens if it was written by an
|
||||
** old version of FTS. In this case it is not possible to determine
|
||||
** the size of the segment, and so segment promotion does not
|
||||
** take place. */
|
||||
bOk = 0;
|
||||
break;
|
||||
}
|
||||
bOk = 1;
|
||||
}
|
||||
rc = sqlite3_reset(pRange);
|
||||
|
||||
if( bOk ){
|
||||
int iIdx = 0;
|
||||
sqlite3_stmt *pUpdate1;
|
||||
sqlite3_stmt *pUpdate2;
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts3SqlStmt(p, SQL_UPDATE_LEVEL_IDX, &pUpdate1, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts3SqlStmt(p, SQL_UPDATE_LEVEL, &pUpdate2, 0);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
|
||||
/* Loop through all %_segdir entries for segments in this index with
|
||||
** levels equal to or greater than iAbsLevel. As each entry is visited,
|
||||
** updated it to set (level = -1) and (idx = N), where N is 0 for the
|
||||
** oldest segment in the range, 1 for the next oldest, and so on.
|
||||
**
|
||||
** In other words, move all segments being promoted to level -1,
|
||||
** setting the "idx" fields as appropriate to keep them in the same
|
||||
** order. The contents of level -1 (which is never used, except
|
||||
** transiently here), will be moved back to level iAbsLevel below. */
|
||||
sqlite3_bind_int64(pRange, 1, iAbsLevel);
|
||||
while( SQLITE_ROW==sqlite3_step(pRange) ){
|
||||
sqlite3_bind_int(pUpdate1, 1, iIdx++);
|
||||
sqlite3_bind_int(pUpdate1, 2, sqlite3_column_int(pRange, 0));
|
||||
sqlite3_bind_int(pUpdate1, 3, sqlite3_column_int(pRange, 1));
|
||||
sqlite3_step(pUpdate1);
|
||||
rc = sqlite3_reset(pUpdate1);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3_reset(pRange);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_reset(pRange);
|
||||
}
|
||||
|
||||
/* Move level -1 to level iAbsLevel */
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_int64(pUpdate2, 1, iAbsLevel);
|
||||
sqlite3_step(pUpdate2);
|
||||
rc = sqlite3_reset(pUpdate2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Merge all level iLevel segments in the database into a single
|
||||
** iLevel+1 segment. Or, if iLevel<0, merge all segments into a
|
||||
@@ -2966,6 +3157,7 @@ static int fts3SegmentMerge(
|
||||
Fts3SegFilter filter; /* Segment term filter condition */
|
||||
Fts3MultiSegReader csr; /* Cursor to iterate through level(s) */
|
||||
int bIgnoreEmpty = 0; /* True to ignore empty segments */
|
||||
i64 iMaxLevel = 0; /* Max level number for this index/langid */
|
||||
|
||||
assert( iLevel==FTS3_SEGCURSOR_ALL
|
||||
|| iLevel==FTS3_SEGCURSOR_PENDING
|
||||
@@ -2977,6 +3169,11 @@ static int fts3SegmentMerge(
|
||||
rc = sqlite3Fts3SegReaderCursor(p, iLangid, iIndex, iLevel, 0, 0, 1, 0, &csr);
|
||||
if( rc!=SQLITE_OK || csr.nSegment==0 ) goto finished;
|
||||
|
||||
if( iLevel!=FTS3_SEGCURSOR_PENDING ){
|
||||
rc = fts3SegmentMaxLevel(p, iLangid, iIndex, &iMaxLevel);
|
||||
if( rc!=SQLITE_OK ) goto finished;
|
||||
}
|
||||
|
||||
if( iLevel==FTS3_SEGCURSOR_ALL ){
|
||||
/* This call is to merge all segments in the database to a single
|
||||
** segment. The level of the new segment is equal to the numerically
|
||||
@@ -2986,21 +3183,21 @@ static int fts3SegmentMerge(
|
||||
rc = SQLITE_DONE;
|
||||
goto finished;
|
||||
}
|
||||
rc = fts3SegmentMaxLevel(p, iLangid, iIndex, &iNewLevel);
|
||||
iNewLevel = iMaxLevel;
|
||||
bIgnoreEmpty = 1;
|
||||
|
||||
}else if( iLevel==FTS3_SEGCURSOR_PENDING ){
|
||||
iNewLevel = getAbsoluteLevel(p, iLangid, iIndex, 0);
|
||||
rc = fts3AllocateSegdirIdx(p, iLangid, iIndex, 0, &iIdx);
|
||||
}else{
|
||||
/* This call is to merge all segments at level iLevel. find the next
|
||||
** available segment index at level iLevel+1. The call to
|
||||
** fts3AllocateSegdirIdx() will merge the segments at level iLevel+1 to
|
||||
** a single iLevel+2 segment if necessary. */
|
||||
rc = fts3AllocateSegdirIdx(p, iLangid, iIndex, iLevel+1, &iIdx);
|
||||
assert( FTS3_SEGCURSOR_PENDING==-1 );
|
||||
iNewLevel = getAbsoluteLevel(p, iLangid, iIndex, iLevel+1);
|
||||
rc = fts3AllocateSegdirIdx(p, iLangid, iIndex, iLevel+1, &iIdx);
|
||||
bIgnoreEmpty = (iLevel!=FTS3_SEGCURSOR_PENDING) && (iNewLevel>iMaxLevel);
|
||||
}
|
||||
if( rc!=SQLITE_OK ) goto finished;
|
||||
|
||||
assert( csr.nSegment>0 );
|
||||
assert( iNewLevel>=getAbsoluteLevel(p, iLangid, iIndex, 0) );
|
||||
assert( iNewLevel<getAbsoluteLevel(p, iLangid, iIndex,FTS3_SEGDIR_MAXLEVEL) );
|
||||
@@ -3017,7 +3214,7 @@ static int fts3SegmentMerge(
|
||||
csr.zTerm, csr.nTerm, csr.aDoclist, csr.nDoclist);
|
||||
}
|
||||
if( rc!=SQLITE_OK ) goto finished;
|
||||
assert( pWriter );
|
||||
assert( pWriter || bIgnoreEmpty );
|
||||
|
||||
if( iLevel!=FTS3_SEGCURSOR_PENDING ){
|
||||
rc = fts3DeleteSegdir(
|
||||
@@ -3025,7 +3222,14 @@ static int fts3SegmentMerge(
|
||||
);
|
||||
if( rc!=SQLITE_OK ) goto finished;
|
||||
}
|
||||
rc = fts3SegWriterFlush(p, pWriter, iNewLevel, iIdx);
|
||||
if( pWriter ){
|
||||
rc = fts3SegWriterFlush(p, pWriter, iNewLevel, iIdx);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( iLevel==FTS3_SEGCURSOR_PENDING || iNewLevel<iMaxLevel ){
|
||||
rc = fts3PromoteSegments(p, iNewLevel, pWriter->nLeafData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
finished:
|
||||
fts3SegWriterFree(pWriter);
|
||||
@@ -3035,7 +3239,7 @@ static int fts3SegmentMerge(
|
||||
|
||||
|
||||
/*
|
||||
** Flush the contents of pendingTerms to level 0 segments.
|
||||
** Flush the contents of pendingTerms to level 0 segments.
|
||||
*/
|
||||
int sqlite3Fts3PendingTermsFlush(Fts3Table *p){
|
||||
int rc = SQLITE_OK;
|
||||
@@ -3051,14 +3255,19 @@ int sqlite3Fts3PendingTermsFlush(Fts3Table *p){
|
||||
** estimate the number of leaf blocks of content to be written
|
||||
*/
|
||||
if( rc==SQLITE_OK && p->bHasStat
|
||||
&& p->bAutoincrmerge==0xff && p->nLeafAdd>0
|
||||
&& p->nAutoincrmerge==0xff && p->nLeafAdd>0
|
||||
){
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
rc = fts3SqlStmt(p, SQL_SELECT_STAT, &pStmt, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_int(pStmt, 1, FTS_STAT_AUTOINCRMERGE);
|
||||
rc = sqlite3_step(pStmt);
|
||||
p->bAutoincrmerge = (rc==SQLITE_ROW && sqlite3_column_int(pStmt, 0));
|
||||
if( rc==SQLITE_ROW ){
|
||||
p->nAutoincrmerge = sqlite3_column_int(pStmt, 0);
|
||||
if( p->nAutoincrmerge==1 ) p->nAutoincrmerge = 8;
|
||||
}else if( rc==SQLITE_DONE ){
|
||||
p->nAutoincrmerge = 0;
|
||||
}
|
||||
rc = sqlite3_reset(pStmt);
|
||||
}
|
||||
}
|
||||
@@ -3426,6 +3635,8 @@ struct IncrmergeWriter {
|
||||
int iIdx; /* Index of *output* segment in iAbsLevel+1 */
|
||||
sqlite3_int64 iStart; /* Block number of first allocated block */
|
||||
sqlite3_int64 iEnd; /* Block number of last allocated block */
|
||||
sqlite3_int64 nLeafData; /* Bytes of leaf page data so far */
|
||||
u8 bNoLeafData; /* If true, store 0 for segment size */
|
||||
NodeWriter aNodeWriter[FTS_MAX_APPENDABLE_HEIGHT];
|
||||
};
|
||||
|
||||
@@ -3494,9 +3705,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 +3715,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;
|
||||
}
|
||||
@@ -3764,8 +3975,8 @@ static int fts3IncrmergeAppend(
|
||||
nSpace += sqlite3Fts3VarintLen(nDoclist) + nDoclist;
|
||||
}
|
||||
|
||||
pWriter->nLeafData += nSpace;
|
||||
blobGrowBuffer(&pLeaf->block, pLeaf->block.n + nSpace, &rc);
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
if( pLeaf->block.n==0 ){
|
||||
pLeaf->block.n = 1;
|
||||
@@ -3864,6 +4075,7 @@ static void fts3IncrmergeRelease(
|
||||
pWriter->iStart, /* start_block */
|
||||
pWriter->aNodeWriter[0].iBlock, /* leaves_end_block */
|
||||
pWriter->iEnd, /* end_block */
|
||||
(pWriter->bNoLeafData==0 ? pWriter->nLeafData : 0), /* end_block */
|
||||
pRoot->block.a, pRoot->block.n /* root */
|
||||
);
|
||||
}
|
||||
@@ -3965,7 +4177,11 @@ static int fts3IncrmergeLoad(
|
||||
if( sqlite3_step(pSelect)==SQLITE_ROW ){
|
||||
iStart = sqlite3_column_int64(pSelect, 1);
|
||||
iLeafEnd = sqlite3_column_int64(pSelect, 2);
|
||||
iEnd = sqlite3_column_int64(pSelect, 3);
|
||||
fts3ReadEndBlockField(pSelect, 3, &iEnd, &pWriter->nLeafData);
|
||||
if( pWriter->nLeafData<0 ){
|
||||
pWriter->nLeafData = pWriter->nLeafData * -1;
|
||||
}
|
||||
pWriter->bNoLeafData = (pWriter->nLeafData==0);
|
||||
nRoot = sqlite3_column_bytes(pSelect, 4);
|
||||
aRoot = sqlite3_column_blob(pSelect, 4);
|
||||
}else{
|
||||
@@ -4556,7 +4772,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;
|
||||
@@ -4566,11 +4782,11 @@ static int fts3IncrmergeHintPop(Blob *pHint, i64 *piAbsLevel, int *pnInput){
|
||||
/*
|
||||
** Attempt an incremental merge that writes nMerge leaf blocks.
|
||||
**
|
||||
** Incremental merges happen nMin segments at a time. The two
|
||||
** segments to be merged are the nMin oldest segments (the ones with
|
||||
** the smallest indexes) in the highest level that contains at least
|
||||
** nMin segments. Multiple merges might occur in an attempt to write the
|
||||
** quota of nMerge leaf blocks.
|
||||
** Incremental merges happen nMin segments at a time. The segments
|
||||
** to be merged are the nMin oldest segments (the ones with the smallest
|
||||
** values for the _segdir.idx field) in the highest level that contains
|
||||
** at least nMin segments. Multiple merges might occur in an attempt to
|
||||
** write the quota of nMerge leaf blocks.
|
||||
*/
|
||||
int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
|
||||
int rc; /* Return code */
|
||||
@@ -4595,6 +4811,7 @@ int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
|
||||
const i64 nMod = FTS3_SEGDIR_MAXLEVEL * p->nIndex;
|
||||
sqlite3_stmt *pFindLevel = 0; /* SQL used to determine iAbsLevel */
|
||||
int bUseHint = 0; /* True if attempting to append */
|
||||
int iIdx = 0; /* Largest idx in level (iAbsLevel+1) */
|
||||
|
||||
/* Search the %_segdir table for the absolute level with the smallest
|
||||
** relative level number that contains at least nMin segments, if any.
|
||||
@@ -4648,6 +4865,19 @@ int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
|
||||
** to start work on some other level. */
|
||||
memset(pWriter, 0, nAlloc);
|
||||
pFilter->flags = FTS3_SEGMENT_REQUIRE_POS;
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts3IncrmergeOutputIdx(p, iAbsLevel, &iIdx);
|
||||
assert( bUseHint==1 || bUseHint==0 );
|
||||
if( iIdx==0 || (bUseHint && iIdx==1) ){
|
||||
int bIgnore = 0;
|
||||
rc = fts3SegmentIsMaxLevel(p, iAbsLevel+1, &bIgnore);
|
||||
if( bIgnore ){
|
||||
pFilter->flags |= FTS3_SEGMENT_IGNORE_EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts3IncrmergeCsr(p, iAbsLevel, nSeg, pCsr);
|
||||
}
|
||||
@@ -4655,16 +4885,12 @@ int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
|
||||
&& SQLITE_OK==(rc = sqlite3Fts3SegReaderStart(p, pCsr, pFilter))
|
||||
&& SQLITE_ROW==(rc = sqlite3Fts3SegReaderStep(p, pCsr))
|
||||
){
|
||||
int iIdx = 0; /* Largest idx in level (iAbsLevel+1) */
|
||||
rc = fts3IncrmergeOutputIdx(p, iAbsLevel, &iIdx);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( bUseHint && iIdx>0 ){
|
||||
const char *zKey = pCsr->zTerm;
|
||||
int nKey = pCsr->nTerm;
|
||||
rc = fts3IncrmergeLoad(p, iAbsLevel, iIdx-1, zKey, nKey, pWriter);
|
||||
}else{
|
||||
rc = fts3IncrmergeWriter(p, iAbsLevel, iIdx, pCsr, pWriter);
|
||||
}
|
||||
if( bUseHint && iIdx>0 ){
|
||||
const char *zKey = pCsr->zTerm;
|
||||
int nKey = pCsr->nTerm;
|
||||
rc = fts3IncrmergeLoad(p, iAbsLevel, iIdx-1, zKey, nKey, pWriter);
|
||||
}else{
|
||||
rc = fts3IncrmergeWriter(p, iAbsLevel, iIdx, pCsr, pWriter);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && pWriter->nLeafEst ){
|
||||
@@ -4686,7 +4912,13 @@ int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
|
||||
}
|
||||
}
|
||||
|
||||
if( nSeg!=0 ){
|
||||
pWriter->nLeafData = pWriter->nLeafData * -1;
|
||||
}
|
||||
fts3IncrmergeRelease(p, pWriter, &rc);
|
||||
if( nSeg==0 && pWriter->bNoLeafData==0 ){
|
||||
fts3PromoteSegments(p, iAbsLevel+1, pWriter->nLeafData);
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3Fts3SegReaderFinish(pCsr);
|
||||
@@ -4773,16 +5005,19 @@ static int fts3DoAutoincrmerge(
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
p->bAutoincrmerge = fts3Getint(&zParam)!=0;
|
||||
p->nAutoincrmerge = fts3Getint(&zParam);
|
||||
if( p->nAutoincrmerge==1 || p->nAutoincrmerge>FTS3_MERGE_COUNT ){
|
||||
p->nAutoincrmerge = 8;
|
||||
}
|
||||
if( !p->bHasStat ){
|
||||
assert( p->bFts4==0 );
|
||||
sqlite3Fts3CreateStatTable(&rc, p);
|
||||
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_bind_int(pStmt, 2, p->nAutoincrmerge);
|
||||
sqlite3_step(pStmt);
|
||||
rc = sqlite3_reset(pStmt);
|
||||
return rc;
|
||||
@@ -4939,34 +5174,36 @@ static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){
|
||||
int iCol;
|
||||
|
||||
for(iCol=0; rc==SQLITE_OK && iCol<p->nColumn; iCol++){
|
||||
const char *zText = (const char *)sqlite3_column_text(pStmt, iCol+1);
|
||||
int nText = sqlite3_column_bytes(pStmt, iCol+1);
|
||||
sqlite3_tokenizer_cursor *pT = 0;
|
||||
if( p->abNotindexed[iCol]==0 ){
|
||||
const char *zText = (const char *)sqlite3_column_text(pStmt, iCol+1);
|
||||
int nText = sqlite3_column_bytes(pStmt, iCol+1);
|
||||
sqlite3_tokenizer_cursor *pT = 0;
|
||||
|
||||
rc = sqlite3Fts3OpenTokenizer(p->pTokenizer, iLang, zText, nText, &pT);
|
||||
while( rc==SQLITE_OK ){
|
||||
char const *zToken; /* Buffer containing token */
|
||||
int nToken = 0; /* Number of bytes in token */
|
||||
int iDum1 = 0, iDum2 = 0; /* Dummy variables */
|
||||
int iPos = 0; /* Position of token in zText */
|
||||
rc = sqlite3Fts3OpenTokenizer(p->pTokenizer, iLang, zText, nText,&pT);
|
||||
while( rc==SQLITE_OK ){
|
||||
char const *zToken; /* Buffer containing token */
|
||||
int nToken = 0; /* Number of bytes in token */
|
||||
int iDum1 = 0, iDum2 = 0; /* Dummy variables */
|
||||
int iPos = 0; /* Position of token in zText */
|
||||
|
||||
rc = pModule->xNext(pT, &zToken, &nToken, &iDum1, &iDum2, &iPos);
|
||||
if( rc==SQLITE_OK ){
|
||||
int i;
|
||||
cksum2 = cksum2 ^ fts3ChecksumEntry(
|
||||
zToken, nToken, iLang, 0, iDocid, iCol, iPos
|
||||
);
|
||||
for(i=1; i<p->nIndex; i++){
|
||||
if( p->aIndex[i].nPrefix<=nToken ){
|
||||
cksum2 = cksum2 ^ fts3ChecksumEntry(
|
||||
zToken, p->aIndex[i].nPrefix, iLang, i, iDocid, iCol, iPos
|
||||
);
|
||||
rc = pModule->xNext(pT, &zToken, &nToken, &iDum1, &iDum2, &iPos);
|
||||
if( rc==SQLITE_OK ){
|
||||
int i;
|
||||
cksum2 = cksum2 ^ fts3ChecksumEntry(
|
||||
zToken, nToken, iLang, 0, iDocid, iCol, iPos
|
||||
);
|
||||
for(i=1; i<p->nIndex; i++){
|
||||
if( p->aIndex[i].nPrefix<=nToken ){
|
||||
cksum2 = cksum2 ^ fts3ChecksumEntry(
|
||||
zToken, p->aIndex[i].nPrefix, iLang, i, iDocid, iCol, iPos
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if( pT ) pModule->xClose(pT);
|
||||
if( rc==SQLITE_DONE ) rc = SQLITE_OK;
|
||||
}
|
||||
if( pT ) pModule->xClose(pT);
|
||||
if( rc==SQLITE_DONE ) rc = SQLITE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5050,6 +5287,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;
|
||||
@@ -5268,6 +5508,10 @@ int sqlite3Fts3UpdateMethod(
|
||||
int nChng = 0; /* Net change in number of documents */
|
||||
int bInsertDone = 0;
|
||||
|
||||
/* At this point it must be known if the %_stat table exists or not.
|
||||
** So bHasStat may not be 2. */
|
||||
assert( p->bHasStat==0 || p->bHasStat==1 );
|
||||
|
||||
assert( p->pSegments==0 );
|
||||
assert(
|
||||
nArg==1 /* DELETE operations */
|
||||
|
||||
@@ -376,7 +376,7 @@ static void showSegmentStats(sqlite3 *db, const char *zTab){
|
||||
sqlite3_finalize(pStmt);
|
||||
nLeaf = nSeg - nIdx;
|
||||
printf("Leaf segments larger than %5d bytes.... %9d %5.2f%%\n",
|
||||
pgsz-45, n, n*100.0/nLeaf);
|
||||
pgsz-45, n, nLeaf>0 ? n*100.0/nLeaf : 0.0);
|
||||
|
||||
pStmt = prepare(db, "SELECT max(level%%1024) FROM '%q_segdir'", zTab);
|
||||
mxLevel = 0;
|
||||
@@ -554,7 +554,7 @@ static void decodeSegment(
|
||||
sqlite3_int64 n;
|
||||
sqlite3_int64 iDocsz;
|
||||
int iHeight;
|
||||
int i = 0;
|
||||
sqlite3_int64 i = 0;
|
||||
int cnt = 0;
|
||||
char zTerm[1000];
|
||||
|
||||
@@ -576,12 +576,12 @@ static void decodeSegment(
|
||||
fprintf(stderr, "term to long\n");
|
||||
exit(1);
|
||||
}
|
||||
memcpy(zTerm+iPrefix, aData+i, nTerm);
|
||||
memcpy(zTerm+iPrefix, aData+i, (size_t)nTerm);
|
||||
zTerm[iPrefix+nTerm] = 0;
|
||||
i += nTerm;
|
||||
if( iHeight==0 ){
|
||||
i += getVarint(aData+i, &iDocsz);
|
||||
printf("term: %-25s doclist %7lld bytes offset %d\n", zTerm, iDocsz, i);
|
||||
printf("term: %-25s doclist %7lld bytes offset %lld\n", zTerm, iDocsz, i);
|
||||
i += iDocsz;
|
||||
}else{
|
||||
printf("term: %-25s child %lld\n", zTerm, ++iChild);
|
||||
@@ -749,18 +749,19 @@ static void decodeDoclist(
|
||||
*/
|
||||
static void showDoclist(sqlite3 *db, const char *zTab){
|
||||
const unsigned char *aData;
|
||||
sqlite3_int64 offset, nData;
|
||||
sqlite3_int64 offset;
|
||||
int nData;
|
||||
sqlite3_stmt *pStmt;
|
||||
|
||||
offset = atoi64(azExtra[1]);
|
||||
nData = atoi64(azExtra[2]);
|
||||
nData = atoi(azExtra[2]);
|
||||
pStmt = prepareToGetSegment(db, zTab, azExtra[0]);
|
||||
if( sqlite3_step(pStmt)!=SQLITE_ROW ){
|
||||
sqlite3_finalize(pStmt);
|
||||
return;
|
||||
}
|
||||
aData = sqlite3_column_blob(pStmt, 0);
|
||||
printf("Doclist at %s offset %lld of size %lld bytes:\n",
|
||||
printf("Doclist at %s offset %lld of size %d bytes:\n",
|
||||
azExtra[0], offset, nData);
|
||||
if( findOption("raw", 0, 0)!=0 ){
|
||||
printBlob(aData+offset, nData);
|
||||
|
||||
@@ -160,7 +160,7 @@ proc print_rd {map} {
|
||||
}
|
||||
assert( key>=aDia[iRes] );
|
||||
return ((c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : (int)aChar[iRes]);}
|
||||
puts "\};"
|
||||
puts "\}"
|
||||
}
|
||||
|
||||
proc print_isdiacritic {zFunc map} {
|
||||
@@ -298,7 +298,7 @@ proc an_print_range_array {lRange} {
|
||||
** using this format.
|
||||
*/
|
||||
}]
|
||||
puts -nonewline " const static unsigned int aEntry\[\] = \{"
|
||||
puts -nonewline " static const unsigned int aEntry\[\] = \{"
|
||||
set i 0
|
||||
foreach range $lRange {
|
||||
foreach {iFirst nRange} $range {}
|
||||
@@ -349,7 +349,7 @@ proc print_isalnum {zFunc lRange} {
|
||||
return ( (aAscii[c >> 5] & (1 << (c & 0x001F)))==0 );
|
||||
}else if( c<(1<<22) ){
|
||||
unsigned int key = (((unsigned int)c)<<10) | 0x000003FF;
|
||||
int iRes;
|
||||
int iRes = 0;
|
||||
int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1;
|
||||
int iLo = 0;
|
||||
while( iHi>=iLo ){
|
||||
@@ -732,7 +732,7 @@ proc print_fileheader {} {
|
||||
*/
|
||||
}]
|
||||
puts ""
|
||||
puts "#if defined(SQLITE_ENABLE_FTS4_UNICODE61)"
|
||||
puts "#ifndef SQLITE_DISABLE_FTS3_UNICODE"
|
||||
puts "#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)"
|
||||
puts ""
|
||||
puts "#include <assert.h>"
|
||||
@@ -808,4 +808,4 @@ if {$::generate_test_code} {
|
||||
}
|
||||
|
||||
puts "#endif /* defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) */"
|
||||
puts "#endif /* !defined(SQLITE_ENABLE_FTS4_UNICODE61) */"
|
||||
puts "#endif /* !defined(SQLITE_DISABLE_FTS3_UNICODE) */"
|
||||
|
||||
+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,
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
** 2014-06-13
|
||||
**
|
||||
** 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 SQL compression functions
|
||||
** compress() and uncompress() using ZLIB.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <zlib.h>
|
||||
|
||||
/*
|
||||
** Implementation of the "compress(X)" SQL function. The input X is
|
||||
** compressed using zLib and the output is returned.
|
||||
**
|
||||
** The output is a BLOB that begins with a variable-length integer that
|
||||
** is the input size in bytes (the size of X before compression). The
|
||||
** variable-length integer is implemented as 1 to 5 bytes. There are
|
||||
** seven bits per integer stored in the lower seven bits of each byte.
|
||||
** More significant bits occur first. The most significant bit (0x80)
|
||||
** is a flag to indicate the end of the integer.
|
||||
*/
|
||||
static void compressFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const unsigned char *pIn;
|
||||
unsigned char *pOut;
|
||||
unsigned int nIn;
|
||||
unsigned long int nOut;
|
||||
unsigned char x[8];
|
||||
int i, j;
|
||||
|
||||
pIn = sqlite3_value_blob(argv[0]);
|
||||
nIn = sqlite3_value_bytes(argv[0]);
|
||||
nOut = 13 + nIn + (nIn+999)/1000;
|
||||
pOut = sqlite3_malloc( nOut+5 );
|
||||
for(i=4; i>=0; i--){
|
||||
x[i] = (nIn >> (7*(4-i)))&0x7f;
|
||||
}
|
||||
for(i=0; i<4 && x[i]==0; i++){}
|
||||
for(j=0; i<=4; i++, j++) pOut[j] = x[i];
|
||||
pOut[j-1] |= 0x80;
|
||||
compress(&pOut[j], &nOut, pIn, nIn);
|
||||
sqlite3_result_blob(context, pOut, nOut+j, sqlite3_free);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the "uncompress(X)" SQL function. The argument X
|
||||
** is a blob which was obtained from compress(Y). The output will be
|
||||
** the value Y.
|
||||
*/
|
||||
static void uncompressFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const unsigned char *pIn;
|
||||
unsigned char *pOut;
|
||||
unsigned int nIn;
|
||||
unsigned long int nOut;
|
||||
int rc;
|
||||
int i;
|
||||
|
||||
pIn = sqlite3_value_blob(argv[0]);
|
||||
nIn = sqlite3_value_bytes(argv[0]);
|
||||
nOut = 0;
|
||||
for(i=0; i<nIn && i<5; i++){
|
||||
nOut = (nOut<<7) | (pIn[i]&0x7f);
|
||||
if( (pIn[i]&0x80)!=0 ){ i++; break; }
|
||||
}
|
||||
pOut = sqlite3_malloc( nOut+1 );
|
||||
rc = uncompress(pOut, &nOut, &pIn[i], nIn-i);
|
||||
if( rc==Z_OK ){
|
||||
sqlite3_result_blob(context, pOut, nOut, sqlite3_free);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_compress_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, "compress", 1, SQLITE_UTF8, 0,
|
||||
compressFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "uncompress", 1, SQLITE_UTF8, 0,
|
||||
uncompressFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
** 2014-06-13
|
||||
**
|
||||
** 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 SQL functions readfile() and
|
||||
** writefile().
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <stdio.h>
|
||||
|
||||
/*
|
||||
** Implementation of the "readfile(X)" SQL function. The entire content
|
||||
** of the file named X is read and returned as a BLOB. NULL is returned
|
||||
** if the file does not exist or is unreadable.
|
||||
*/
|
||||
static void readfileFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zName;
|
||||
FILE *in;
|
||||
long nIn;
|
||||
void *pBuf;
|
||||
|
||||
zName = (const char*)sqlite3_value_text(argv[0]);
|
||||
if( zName==0 ) return;
|
||||
in = fopen(zName, "rb");
|
||||
if( in==0 ) return;
|
||||
fseek(in, 0, SEEK_END);
|
||||
nIn = ftell(in);
|
||||
rewind(in);
|
||||
pBuf = sqlite3_malloc( nIn );
|
||||
if( pBuf && 1==fread(pBuf, nIn, 1, in) ){
|
||||
sqlite3_result_blob(context, pBuf, nIn, sqlite3_free);
|
||||
}else{
|
||||
sqlite3_free(pBuf);
|
||||
}
|
||||
fclose(in);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the "writefile(X,Y)" SQL function. The argument Y
|
||||
** is written into file X. The number of bytes written is returned. Or
|
||||
** NULL is returned if something goes wrong, such as being unable to open
|
||||
** file X for writing.
|
||||
*/
|
||||
static void writefileFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
FILE *out;
|
||||
const char *z;
|
||||
sqlite3_int64 rc;
|
||||
const char *zFile;
|
||||
|
||||
zFile = (const char*)sqlite3_value_text(argv[0]);
|
||||
if( zFile==0 ) return;
|
||||
out = fopen(zFile, "wb");
|
||||
if( out==0 ) return;
|
||||
z = (const char*)sqlite3_value_blob(argv[1]);
|
||||
if( z==0 ){
|
||||
rc = 0;
|
||||
}else{
|
||||
rc = fwrite(z, 1, sqlite3_value_bytes(argv[1]), out);
|
||||
}
|
||||
fclose(out);
|
||||
sqlite3_result_int64(context, rc);
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_fileio_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, "readfile", 1, SQLITE_UTF8, 0,
|
||||
readfileFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "writefile", 2, SQLITE_UTF8, 0,
|
||||
writefileFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
+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
|
||||
|
||||
+33
-11
@@ -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
|
||||
|
||||
@@ -1893,7 +1893,7 @@ static int spellfix1Init(
|
||||
char **pzErr
|
||||
){
|
||||
spellfix1_vtab *pNew = 0;
|
||||
const char *zModule = argv[0];
|
||||
/* const char *zModule = argv[0]; // not used */
|
||||
const char *zDbName = argv[1];
|
||||
const char *zTableName = argv[2];
|
||||
int nDbName;
|
||||
@@ -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, zTableName, 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);
|
||||
}
|
||||
+842
-740
File diff suppressed because it is too large
Load Diff
+33
-8
@@ -33,6 +33,7 @@ set testprefix rtree1
|
||||
# rtree-8.*: Test constrained scans of r-tree data.
|
||||
#
|
||||
# rtree-12.*: Test that on-conflict clauses are supported.
|
||||
# rtree-13.*: Test that bug [d2889096e7bdeac6d] has been fixed.
|
||||
#
|
||||
|
||||
ifcapable !rtree {
|
||||
@@ -120,12 +121,13 @@ proc execsql_intout {sql} {
|
||||
# Test that it is possible to open an existing database that contains
|
||||
# r-tree tables.
|
||||
#
|
||||
do_test rtree-1.4.1 {
|
||||
execsql {
|
||||
CREATE VIRTUAL TABLE t1 USING rtree(ii, x1, x2);
|
||||
INSERT INTO t1 VALUES(1, 5.0, 10.0);
|
||||
INSERT INTO t1 VALUES(2, 15.0, 20.0);
|
||||
}
|
||||
do_execsql_test rtree-1.4.1a {
|
||||
CREATE VIRTUAL TABLE t1 USING rtree(ii, x1, x2);
|
||||
INSERT INTO t1 VALUES(1, 5.0, 10.0);
|
||||
SELECT substr(hex(data),1,40) FROM t1_node;
|
||||
} {00000001000000000000000140A0000041200000}
|
||||
do_execsql_test rtree-1.4.1b {
|
||||
INSERT INTO t1 VALUES(2, 15.0, 20.0);
|
||||
} {}
|
||||
do_test rtree-1.4.2 {
|
||||
db close
|
||||
@@ -435,16 +437,18 @@ do_test rtree-11.2 {
|
||||
# Test on-conflict clause handling.
|
||||
#
|
||||
db_delete_and_reopen
|
||||
do_execsql_test 12.0 {
|
||||
do_execsql_test 12.0.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING rtree_i32(idx, x1, x2, y1, y2);
|
||||
INSERT INTO t1 VALUES(1, 1, 2, 3, 4);
|
||||
SELECT substr(hex(data),1,56) FROM t1_node;
|
||||
} {00000001000000000000000100000001000000020000000300000004}
|
||||
do_execsql_test 12.0.2 {
|
||||
INSERT INTO t1 VALUES(2, 2, 3, 4, 5);
|
||||
INSERT INTO t1 VALUES(3, 3, 4, 5, 6);
|
||||
|
||||
CREATE TABLE source(idx, x1, x2, y1, y2);
|
||||
INSERT INTO source VALUES(5, 8, 8, 8, 8);
|
||||
INSERT INTO source VALUES(2, 7, 7, 7, 7);
|
||||
|
||||
}
|
||||
db_save_and_close
|
||||
foreach {tn sql_template testdata} {
|
||||
@@ -510,4 +514,25 @@ foreach {tn sql_template testdata} {
|
||||
db close
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that bug [d2889096e7bdeac6d] has been fixed.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 13.1 {
|
||||
CREATE VIRTUAL TABLE t9 USING rtree(id, xmin, xmax);
|
||||
INSERT INTO t9 VALUES(1,0,0);
|
||||
INSERT INTO t9 VALUES(2,0,0);
|
||||
SELECT * FROM t9 WHERE id IN (1, 2);
|
||||
} {1 0.0 0.0 2 0.0 0.0}
|
||||
|
||||
do_execsql_test 13.2 {
|
||||
WITH r(x) AS (
|
||||
SELECT 1 UNION ALL
|
||||
SELECT 2 UNION ALL
|
||||
SELECT 3
|
||||
)
|
||||
SELECT * FROM r CROSS JOIN t9 WHERE id=x;
|
||||
} {1 1 0.0 0.0 2 2 0.0 0.0}
|
||||
|
||||
finish_test
|
||||
|
||||
+17
-11
@@ -57,31 +57,31 @@ do_test rtree6-1.1 {
|
||||
|
||||
do_test rtree6-1.2 {
|
||||
rtree_strategy {SELECT * FROM t1 WHERE x1>10}
|
||||
} {Ea}
|
||||
} {E0}
|
||||
|
||||
do_test rtree6-1.3 {
|
||||
rtree_strategy {SELECT * FROM t1 WHERE x1<10}
|
||||
} {Ca}
|
||||
} {C0}
|
||||
|
||||
do_test rtree6-1.4 {
|
||||
rtree_strategy {SELECT * FROM t1,t2 WHERE k=ii AND x1<10}
|
||||
} {Ca}
|
||||
} {C0}
|
||||
|
||||
do_test rtree6-1.5 {
|
||||
rtree_strategy {SELECT * FROM t1,t2 WHERE k=+ii AND x1<10}
|
||||
} {Ca}
|
||||
} {C0}
|
||||
|
||||
do_eqp_test rtree6.2.1 {
|
||||
SELECT * FROM t1,t2 WHERE k=+ii AND x1<10
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:Ca}
|
||||
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:C0}
|
||||
0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)}
|
||||
}
|
||||
|
||||
do_eqp_test rtree6.2.2 {
|
||||
SELECT * FROM t1,t2 WHERE k=ii AND x1<10
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:Ca}
|
||||
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:C0}
|
||||
0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)}
|
||||
}
|
||||
|
||||
@@ -92,11 +92,17 @@ do_eqp_test rtree6.2.3 {
|
||||
0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)}
|
||||
}
|
||||
|
||||
do_eqp_test rtree6.2.4 {
|
||||
do_eqp_test rtree6.2.4.1 {
|
||||
SELECT * FROM t1,t2 WHERE v=+ii and x1<10 and x2>10
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:C0E1}
|
||||
0 1 1 {SEARCH TABLE t2 USING AUTOMATIC COVERING INDEX (v=?)}
|
||||
}
|
||||
do_eqp_test rtree6.2.4.2 {
|
||||
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 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:C0E1}
|
||||
0 1 1 {SEARCH TABLE t2 USING AUTOMATIC COVERING INDEX (v=?)}
|
||||
}
|
||||
|
||||
do_eqp_test rtree6.2.5 {
|
||||
@@ -126,7 +132,7 @@ do_test rtree6.3.2 {
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5
|
||||
}
|
||||
} {EaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEa}
|
||||
} {E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0}
|
||||
do_test rtree6.3.3 {
|
||||
rtree_strategy {
|
||||
SELECT * FROM t3 WHERE
|
||||
@@ -137,7 +143,7 @@ do_test rtree6.3.3 {
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
|
||||
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5
|
||||
}
|
||||
} {EaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEa}
|
||||
} {E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0}
|
||||
|
||||
do_execsql_test rtree6-3.4 {
|
||||
SELECT * FROM t3 WHERE x1>0.5 AND x1>0.8 AND x1>1.1
|
||||
|
||||
@@ -41,7 +41,7 @@ ifcapable rtree_int_only {
|
||||
INSERT INTO t1 VALUES(9223372036854775807, 150, 150, 400, 400);
|
||||
SELECT rtreenode(2, data) FROM t1_node;
|
||||
}
|
||||
} {{{1073741824 0.000000 0.000000 100.000000 100.000000} {2147483646 0.000000 0.000000 200.000000 200.000000} {4294967296 0.000000 0.000000 300.000000 300.000000} {8589934592 20.000000 20.000000 150.000000 150.000000} {9223372036854775807 150.000000 150.000000 400.000000 400.000000}}}
|
||||
} {{{1073741824 0 0 100 100} {2147483646 0 0 200 200} {4294967296 0 0 300 300} {8589934592 20 20 150 150} {9223372036854775807 150 150 400 400}}}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
# 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:D3B2D1B0}
|
||||
}
|
||||
|
||||
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:D3B2D1B0}
|
||||
}
|
||||
|
||||
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:D3B2D1B0}
|
||||
}
|
||||
|
||||
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:D3B2D1B0}
|
||||
}
|
||||
|
||||
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:D3B2D1B0}
|
||||
}
|
||||
|
||||
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:D3B2D1B0}
|
||||
}
|
||||
|
||||
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 {}}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Test that the sqlite_stat1 data is used correctly.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 5.1 {
|
||||
CREATE TABLE t1(x PRIMARY KEY, y);
|
||||
CREATE VIRTUAL TABLE rt USING rtree(id, x1, x2);
|
||||
|
||||
INSERT INTO t1(x) VALUES(1);
|
||||
INSERT INTO t1(x) SELECT x+1 FROM t1; -- 2
|
||||
INSERT INTO t1(x) SELECT x+2 FROM t1; -- 4
|
||||
INSERT INTO t1(x) SELECT x+4 FROM t1; -- 8
|
||||
INSERT INTO t1(x) SELECT x+8 FROM t1; -- 16
|
||||
INSERT INTO t1(x) SELECT x+16 FROM t1; -- 32
|
||||
INSERT INTO t1(x) SELECT x+32 FROM t1; -- 64
|
||||
INSERT INTO t1(x) SELECT x+64 FROM t1; -- 128
|
||||
INSERT INTO t1(x) SELECT x+128 FROM t1; -- 256
|
||||
INSERT INTO t1(x) SELECT x+256 FROM t1; -- 512
|
||||
INSERT INTO t1(x) SELECT x+512 FROM t1; --1024
|
||||
|
||||
INSERT INTO rt SELECT x, x, x+1 FROM t1 WHERE x<=5;
|
||||
}
|
||||
|
||||
# First test a query with no ANALYZE data at all. The outer loop is
|
||||
# real table "t1".
|
||||
#
|
||||
do_eqp_test 5.2 {
|
||||
SELECT * FROM t1, rt WHERE x==id;
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1}
|
||||
0 1 1 {SCAN TABLE rt VIRTUAL TABLE INDEX 1:}
|
||||
}
|
||||
|
||||
# Now create enough ANALYZE data to tell SQLite that virtual table "rt"
|
||||
# contains very few rows. This causes it to move "rt" to the outer loop.
|
||||
#
|
||||
do_execsql_test 5.3 {
|
||||
ANALYZE;
|
||||
DELETE FROM sqlite_stat1 WHERE tbl='t1';
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
do_eqp_test 5.4 {
|
||||
SELECT * FROM t1, rt WHERE x==id;
|
||||
} {
|
||||
0 0 1 {SCAN TABLE rt VIRTUAL TABLE INDEX 2:}
|
||||
0 1 0 {SEARCH TABLE t1 USING INDEX sqlite_autoindex_t1_1 (x=?)}
|
||||
}
|
||||
|
||||
# Delete the ANALYZE data. "t1" should be the outer loop again.
|
||||
#
|
||||
do_execsql_test 5.5 { DROP TABLE sqlite_stat1; }
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
do_eqp_test 5.6 {
|
||||
SELECT * FROM t1, rt WHERE x==id;
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1}
|
||||
0 1 1 {SCAN TABLE rt VIRTUAL TABLE INDEX 1:}
|
||||
}
|
||||
|
||||
# This time create and attach a database that contains ANALYZE data for
|
||||
# tables of the same names as those used internally by virtual table
|
||||
# "rt". Check that the rtree module is not fooled into using this data.
|
||||
# Table "t1" should remain the outer loop.
|
||||
#
|
||||
do_test 5.7 {
|
||||
db backup test.db2
|
||||
sqlite3 db2 test.db2
|
||||
db2 eval {
|
||||
ANALYZE;
|
||||
DELETE FROM sqlite_stat1 WHERE tbl='t1';
|
||||
}
|
||||
db2 close
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
execsql { ATTACH 'test.db2' AS aux; }
|
||||
} {}
|
||||
do_eqp_test 5.8 {
|
||||
SELECT * FROM t1, rt WHERE x==id;
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1}
|
||||
0 1 1 {SCAN TABLE rt VIRTUAL TABLE INDEX 1:}
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Test that having a second connection drop the sqlite_stat1 table
|
||||
# before it is required by rtreeConnect() does not cause problems.
|
||||
#
|
||||
ifcapable rtree {
|
||||
reset_db
|
||||
do_execsql_test 6.1 {
|
||||
CREATE TABLE t1(x);
|
||||
CREATE VIRTUAL TABLE rt USING rtree(id, x1, x2);
|
||||
INSERT INTO t1 VALUES(1);
|
||||
INSERT INTO rt VALUES(1,2,3);
|
||||
ANALYZE;
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
do_execsql_test 6.2 { SELECT * FROM t1 } {1}
|
||||
|
||||
do_test 6.3 {
|
||||
sqlite3 db2 test.db
|
||||
db2 eval { DROP TABLE sqlite_stat1 }
|
||||
db2 close
|
||||
execsql { SELECT * FROM rt }
|
||||
} {1 2.0 3.0}
|
||||
db close
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
# 2010 August 28
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file contains tests for the r-tree module. Specifically, it tests
|
||||
# that new-style custom r-tree queries (geometry callbacks) work.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !rtree { finish_test ; return }
|
||||
ifcapable rtree_int_only { finish_test; return }
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test the example 2d "circle" geometry callback.
|
||||
#
|
||||
register_circle_geom db
|
||||
|
||||
do_execsql_test rtreeE-1.1 {
|
||||
PRAGMA page_size=512;
|
||||
CREATE VIRTUAL TABLE rt1 USING rtree(id,x0,x1,y0,y1);
|
||||
|
||||
/* A tight pattern of small boxes near 0,0 */
|
||||
WITH RECURSIVE
|
||||
x(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM x WHERE x<4),
|
||||
y(y) AS (VALUES(0) UNION ALL SELECT y+1 FROM y WHERE y<4)
|
||||
INSERT INTO rt1 SELECT x+5*y, x, x+2, y, y+2 FROM x, y;
|
||||
|
||||
/* A looser pattern of small boxes near 100, 0 */
|
||||
WITH RECURSIVE
|
||||
x(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM x WHERE x<4),
|
||||
y(y) AS (VALUES(0) UNION ALL SELECT y+1 FROM y WHERE y<4)
|
||||
INSERT INTO rt1 SELECT 100+x+5*y, x*3+100, x*3+102, y*3, y*3+2 FROM x, y;
|
||||
|
||||
/* A looser pattern of larger boxes near 0, 200 */
|
||||
WITH RECURSIVE
|
||||
x(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM x WHERE x<4),
|
||||
y(y) AS (VALUES(0) UNION ALL SELECT y+1 FROM y WHERE y<4)
|
||||
INSERT INTO rt1 SELECT 200+x+5*y, x*7, x*7+15, y*7+200, y*7+215 FROM x, y;
|
||||
} {}
|
||||
|
||||
# Queries against each of the three clusters */
|
||||
do_execsql_test rtreeE-1.1 {
|
||||
SELECT id FROM rt1 WHERE id MATCH Qcircle(0.0, 0.0, 50.0, 3) ORDER BY id;
|
||||
} {0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24}
|
||||
do_execsql_test rtreeE-1.2 {
|
||||
SELECT id FROM rt1 WHERE id MATCH Qcircle(100.0, 0.0, 50.0, 3) ORDER BY id;
|
||||
} {100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124}
|
||||
do_execsql_test rtreeE-1.3 {
|
||||
SELECT id FROM rt1 WHERE id MATCH Qcircle(0.0, 200.0, 50.0, 3) ORDER BY id;
|
||||
} {200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224}
|
||||
|
||||
# The Qcircle geometry function gives a lower score to larger leaf-nodes.
|
||||
# This causes the 200s to sort before the 100s and the 0s to sort before
|
||||
# last.
|
||||
#
|
||||
do_execsql_test rtreeE-1.4 {
|
||||
SELECT id FROM rt1 WHERE id MATCH Qcircle(0,0,1000,3) AND id%100==0
|
||||
} {200 100 0}
|
||||
|
||||
# Exclude odd rowids on a depth-first search
|
||||
do_execsql_test rtreeE-1.5 {
|
||||
SELECT id FROM rt1 WHERE id MATCH Qcircle(0,0,1000,4) ORDER BY +id
|
||||
} {0 2 4 6 8 10 12 14 16 18 20 22 24 100 102 104 106 108 110 112 114 116 118 120 122 124 200 202 204 206 208 210 212 214 216 218 220 222 224}
|
||||
|
||||
# Exclude odd rowids on a breadth-first search.
|
||||
do_execsql_test rtreeE-1.6 {
|
||||
SELECT id FROM rt1 WHERE id MATCH Qcircle(0,0,1000,5) ORDER BY +id
|
||||
} {0 2 4 6 8 10 12 14 16 18 20 22 24 100 102 104 106 108 110 112 114 116 118 120 122 124 200 202 204 206 208 210 212 214 216 218 220 222 224}
|
||||
|
||||
# Construct a large 2-D RTree with thousands of random entries.
|
||||
#
|
||||
do_test rtreeE-2.1 {
|
||||
db eval {
|
||||
CREATE TABLE t2(id,x0,x1,y0,y1);
|
||||
CREATE VIRTUAL TABLE rt2 USING rtree(id,x0,x1,y0,y1);
|
||||
BEGIN;
|
||||
}
|
||||
expr srand(0)
|
||||
for {set i 1} {$i<=10000} {incr i} {
|
||||
set dx [expr {int(rand()*40)+1}]
|
||||
set dy [expr {int(rand()*40)+1}]
|
||||
set x0 [expr {int(rand()*(10000 - $dx))}]
|
||||
set x1 [expr {$x0+$dx}]
|
||||
set y0 [expr {int(rand()*(10000 - $dy))}]
|
||||
set y1 [expr {$y0+$dy}]
|
||||
set id [expr {$i+10000}]
|
||||
db eval {INSERT INTO t2 VALUES($id,$x0,$x1,$y0,$y1)}
|
||||
}
|
||||
db eval {
|
||||
INSERT INTO rt2 SELECT * FROM t2;
|
||||
COMMIT;
|
||||
}
|
||||
} {}
|
||||
|
||||
for {set i 1} {$i<=200} {incr i} {
|
||||
set dx [expr {int(rand()*100)}]
|
||||
set dy [expr {int(rand()*100)}]
|
||||
set x0 [expr {int(rand()*(10000 - $dx))}]
|
||||
set x1 [expr {$x0+$dx}]
|
||||
set y0 [expr {int(rand()*(10000 - $dy))}]
|
||||
set y1 [expr {$y0+$dy}]
|
||||
set ans [db eval {SELECT id FROM t2 WHERE x1>=$x0 AND x0<=$x1 AND y1>=$y0 AND y0<=$y1 ORDER BY id}]
|
||||
do_execsql_test rtreeE-2.2.$i {
|
||||
SELECT id FROM rt2 WHERE id MATCH breadthfirstsearch($x0,$x1,$y0,$y1) ORDER BY id
|
||||
} $ans
|
||||
}
|
||||
|
||||
# Run query that have very deep priority queues
|
||||
#
|
||||
set ans [db eval {SELECT id FROM t2 WHERE x1>=0 AND x0<=5000 AND y1>=0 AND y0<=5000 ORDER BY id}]
|
||||
do_execsql_test rtreeE-2.3 {
|
||||
SELECT id FROM rt2 WHERE id MATCH breadthfirstsearch(0,5000,0,5000) ORDER BY id
|
||||
} $ans
|
||||
set ans [db eval {SELECT id FROM t2 WHERE x1>=0 AND x0<=10000 AND y1>=0 AND y0<=10000 ORDER BY id}]
|
||||
do_execsql_test rtreeE-2.4 {
|
||||
SELECT id FROM rt2 WHERE id MATCH breadthfirstsearch(0,10000,0,10000) ORDER BY id
|
||||
} $ans
|
||||
|
||||
finish_test
|
||||
@@ -21,6 +21,16 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry;
|
||||
typedef struct sqlite3_rtree_query_info sqlite3_rtree_query_info;
|
||||
|
||||
/* The double-precision datatype used by RTree depends on the
|
||||
** SQLITE_RTREE_INT_ONLY compile-time option.
|
||||
*/
|
||||
#ifdef SQLITE_RTREE_INT_ONLY
|
||||
typedef sqlite3_int64 sqlite3_rtree_dbl;
|
||||
#else
|
||||
typedef double sqlite3_rtree_dbl;
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Register a geometry callback named zGeom that can be used as part of an
|
||||
@@ -31,11 +41,7 @@ typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry;
|
||||
int sqlite3_rtree_geometry_callback(
|
||||
sqlite3 *db,
|
||||
const char *zGeom,
|
||||
#ifdef SQLITE_RTREE_INT_ONLY
|
||||
int (*xGeom)(sqlite3_rtree_geometry*, int n, sqlite3_int64 *a, int *pRes),
|
||||
#else
|
||||
int (*xGeom)(sqlite3_rtree_geometry*, int n, double *a, int *pRes),
|
||||
#endif
|
||||
int (*xGeom)(sqlite3_rtree_geometry*, int, sqlite3_rtree_dbl*,int*),
|
||||
void *pContext
|
||||
);
|
||||
|
||||
@@ -47,11 +53,60 @@ int sqlite3_rtree_geometry_callback(
|
||||
struct sqlite3_rtree_geometry {
|
||||
void *pContext; /* Copy of pContext passed to s_r_g_c() */
|
||||
int nParam; /* Size of array aParam[] */
|
||||
double *aParam; /* Parameters passed to SQL geom function */
|
||||
sqlite3_rtree_dbl *aParam; /* Parameters passed to SQL geom function */
|
||||
void *pUser; /* Callback implementation user data */
|
||||
void (*xDelUser)(void *); /* Called by SQLite to clean up pUser */
|
||||
};
|
||||
|
||||
/*
|
||||
** Register a 2nd-generation geometry callback named zScore that can be
|
||||
** used as part of an R-Tree geometry query as follows:
|
||||
**
|
||||
** SELECT ... FROM <rtree> WHERE <rtree col> MATCH $zQueryFunc(... params ...)
|
||||
*/
|
||||
int sqlite3_rtree_query_callback(
|
||||
sqlite3 *db,
|
||||
const char *zQueryFunc,
|
||||
int (*xQueryFunc)(sqlite3_rtree_query_info*),
|
||||
void *pContext,
|
||||
void (*xDestructor)(void*)
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
** A pointer to a structure of the following type is passed as the
|
||||
** argument to scored geometry callback registered using
|
||||
** sqlite3_rtree_query_callback().
|
||||
**
|
||||
** Note that the first 5 fields of this structure are identical to
|
||||
** sqlite3_rtree_geometry. This structure is a subclass of
|
||||
** sqlite3_rtree_geometry.
|
||||
*/
|
||||
struct sqlite3_rtree_query_info {
|
||||
void *pContext; /* pContext from when function registered */
|
||||
int nParam; /* Number of function parameters */
|
||||
sqlite3_rtree_dbl *aParam; /* value of function parameters */
|
||||
void *pUser; /* callback can use this, if desired */
|
||||
void (*xDelUser)(void*); /* function to free pUser */
|
||||
sqlite3_rtree_dbl *aCoord; /* Coordinates of node or entry to check */
|
||||
unsigned int *anQueue; /* Number of pending entries in the queue */
|
||||
int nCoord; /* Number of coordinates */
|
||||
int iLevel; /* Level of current node or entry */
|
||||
int mxLevel; /* The largest iLevel value in the tree */
|
||||
sqlite3_int64 iRowid; /* Rowid for current entry */
|
||||
sqlite3_rtree_dbl rParentScore; /* Score of parent node */
|
||||
int eParentWithin; /* Visibility of parent node */
|
||||
int eWithin; /* OUT: Visiblity */
|
||||
sqlite3_rtree_dbl rScore; /* OUT: Write the score here */
|
||||
};
|
||||
|
||||
/*
|
||||
** Allowed values for sqlite3_rtree_query.eWithin and .eParentWithin.
|
||||
*/
|
||||
#define NOT_WITHIN 0 /* Object completely outside of query region */
|
||||
#define PARTLY_WITHIN 1 /* Object partially overlaps query region */
|
||||
#define FULLY_WITHIN 2 /* Object fully contained within query region */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* end of the 'extern "C"' block */
|
||||
|
||||
@@ -26,4 +26,7 @@
|
||||
>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 -
|
||||
>68 belong =0x4d504258 MBTiles tileset -
|
||||
>0 string =SQLite SQLite3 database
|
||||
|
||||
@@ -121,8 +121,10 @@ SRC = \
|
||||
$(TOP)/src/os.c \
|
||||
$(TOP)/src/os.h \
|
||||
$(TOP)/src/os_common.h \
|
||||
$(TOP)/src/os_setup.h \
|
||||
$(TOP)/src/os_unix.c \
|
||||
$(TOP)/src/os_win.c \
|
||||
$(TOP)/src/os_win.h \
|
||||
$(TOP)/src/pager.c \
|
||||
$(TOP)/src/pager.h \
|
||||
$(TOP)/src/parse.y \
|
||||
@@ -163,7 +165,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
|
||||
#
|
||||
@@ -207,6 +210,7 @@ SRC += \
|
||||
$(TOP)/ext/icu/sqliteicu.h \
|
||||
$(TOP)/ext/icu/icu.c
|
||||
SRC += \
|
||||
$(TOP)/ext/rtree/sqlite3rtree.h \
|
||||
$(TOP)/ext/rtree/rtree.h \
|
||||
$(TOP)/ext/rtree/rtree.c
|
||||
|
||||
@@ -273,13 +277,16 @@ TESTSRC = \
|
||||
TESTSRC += \
|
||||
$(TOP)/ext/misc/amatch.c \
|
||||
$(TOP)/ext/misc/closure.c \
|
||||
$(TOP)/ext/misc/fileio.c \
|
||||
$(TOP)/ext/misc/fuzzer.c \
|
||||
$(TOP)/ext/misc/ieee754.c \
|
||||
$(TOP)/ext/misc/nextchar.c \
|
||||
$(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
|
||||
@@ -336,6 +343,8 @@ HDR = \
|
||||
opcodes.h \
|
||||
$(TOP)/src/os.h \
|
||||
$(TOP)/src/os_common.h \
|
||||
$(TOP)/src/os_setup.h \
|
||||
$(TOP)/src/os_win.h \
|
||||
$(TOP)/src/pager.h \
|
||||
$(TOP)/src/pcache.h \
|
||||
parse.h \
|
||||
@@ -344,7 +353,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 +395,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 +408,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
|
||||
|
||||
@@ -472,7 +482,7 @@ parse.c: $(TOP)/src/parse.y lemon $(TOP)/addopcodes.awk
|
||||
mv parse.h parse.h.temp
|
||||
$(NAWK) -f $(TOP)/addopcodes.awk parse.h.temp >parse.h
|
||||
|
||||
sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest.uuid $(TOP)/VERSION
|
||||
sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest.uuid $(TOP)/VERSION $(TOP)/ext/rtree/sqlite3rtree.h
|
||||
tclsh $(TOP)/tool/mksqlite3h.tcl $(TOP) >sqlite3.h
|
||||
|
||||
keywordhash.h: $(TOP)/tool/mkkeywordhash.c
|
||||
@@ -618,6 +628,40 @@ $(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.o
|
||||
$(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o showdb$(EXE) \
|
||||
$(TOP)/tool/showdb.c sqlite3.o $(THREADLIB)
|
||||
|
||||
showstat4$(EXE): $(TOP)/tool/showstat4.c sqlite3.o
|
||||
$(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o showstat4$(EXE) \
|
||||
$(TOP)/tool/showstat4.c sqlite3.o $(THREADLIB)
|
||||
|
||||
showjournal$(EXE): $(TOP)/tool/showjournal.c sqlite3.o
|
||||
$(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o showjournal$(EXE) \
|
||||
$(TOP)/tool/showjournal.c sqlite3.o $(THREADLIB)
|
||||
|
||||
showwal$(EXE): $(TOP)/tool/showwal.c sqlite3.o
|
||||
$(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o showwal$(EXE) \
|
||||
$(TOP)/tool/showwal.c sqlite3.o $(THREADLIB)
|
||||
|
||||
fts3view$(EXE): $(TOP)/ext/fts3/tool/fts3view.c sqlite3.o
|
||||
$(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o fts3view$(EXE) \
|
||||
$(TOP)/ext/fts3/tool/fts3view.c sqlite3.o $(THREADLIB)
|
||||
|
||||
rollback-test$(EXE): $(TOP)/tool/rollback-test.c sqlite3.o
|
||||
$(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o rollback-test$(EXE) \
|
||||
$(TOP)/tool/rollback-test.c sqlite3.o $(THREADLIB)
|
||||
|
||||
LogEst$(EXE): $(TOP)/tool/logest.c sqlite3.h
|
||||
$(TCC) -o LogEst$(EXE) $(TOP)/tool/logest.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,7 +695,16 @@ 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 LogEst LogEst.exe
|
||||
rm -f fts3view fts3view.exe
|
||||
rm -f rollback-test rollback-test.exe
|
||||
rm -f showdb showdb.exe
|
||||
rm -f showjournal showjournal.exe
|
||||
rm -f showstat4 showstat4.exe
|
||||
rm -f showwal showwal.exe
|
||||
rm -f speedtest1 speedtest1.exe
|
||||
rm -f wordcount wordcount.exe
|
||||
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
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
7dd4968f235d6e1ca9547cda9cf3bd570e1609ef
|
||||
9491ba7d738528f168657adb43a198238abde19e
|
||||
|
||||
@@ -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> .
|
||||
|
||||
@@ -255,8 +255,14 @@
|
||||
1DEB91F008733DB70010E9CD /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
"ARCHS[sdk=iphoneos*]" = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||
"ARCHS[sdk=iphonesimulator*]" = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||
"ARCHS[sdk=iphoneos*]" = (
|
||||
"$(ARCHS_STANDARD)",
|
||||
armv7s,
|
||||
);
|
||||
"ARCHS[sdk=iphonesimulator*]" = (
|
||||
"$(ARCHS_STANDARD)",
|
||||
armv7s,
|
||||
);
|
||||
"ARCHS[sdk=macosx*]" = (
|
||||
x86_64,
|
||||
i386,
|
||||
@@ -278,8 +284,14 @@
|
||||
1DEB91F108733DB70010E9CD /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
"ARCHS[sdk=iphoneos*]" = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||
"ARCHS[sdk=iphonesimulator*]" = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||
"ARCHS[sdk=iphoneos*]" = (
|
||||
"$(ARCHS_STANDARD)",
|
||||
armv7s,
|
||||
);
|
||||
"ARCHS[sdk=iphonesimulator*]" = (
|
||||
"$(ARCHS_STANDARD)",
|
||||
armv7s,
|
||||
);
|
||||
"ARCHS[sdk=macosx*]" = (
|
||||
i386,
|
||||
x86_64,
|
||||
@@ -304,6 +316,8 @@
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
PRODUCT_NAME = amalgamation;
|
||||
SDKROOT = macosx;
|
||||
SUPPORTED_PLATFORMS = macosx;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -314,6 +328,8 @@
|
||||
COPY_PHASE_STRIP = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
PRODUCT_NAME = amalgamation;
|
||||
SDKROOT = macosx;
|
||||
SUPPORTED_PLATFORMS = macosx;
|
||||
ZERO_LINK = NO;
|
||||
};
|
||||
name = Release;
|
||||
|
||||
+9
-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);
|
||||
}
|
||||
}
|
||||
@@ -116,6 +116,7 @@ static void renameParentFunc(
|
||||
int token; /* Type of token */
|
||||
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
if( zInput==0 || zOld==0 ) return;
|
||||
for(z=zInput; *z; z=z+n){
|
||||
n = sqlite3GetToken(z, &token);
|
||||
if( token==TK_REFERENCES ){
|
||||
@@ -130,7 +131,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 +217,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 +470,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 +606,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 +689,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;
|
||||
|
||||
+1261
-534
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);
|
||||
|
||||
+311
-291
File diff suppressed because it is too large
Load Diff
+8
-6
@@ -63,7 +63,9 @@ int sqlite3BtreeOpen(
|
||||
|
||||
int sqlite3BtreeClose(Btree*);
|
||||
int sqlite3BtreeSetCacheSize(Btree*,int);
|
||||
int sqlite3BtreeSetMmapLimit(Btree*,sqlite3_int64);
|
||||
#if SQLITE_MAX_MMAP_SIZE>0
|
||||
int sqlite3BtreeSetMmapLimit(Btree*,sqlite3_int64);
|
||||
#endif
|
||||
int sqlite3BtreeSetPagerFlags(Btree*,unsigned);
|
||||
int sqlite3BtreeSyncDisabled(Btree*);
|
||||
int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix);
|
||||
@@ -113,6 +115,7 @@ int sqlite3BtreeIncrVacuum(Btree *);
|
||||
|
||||
int sqlite3BtreeDropTable(Btree*, int, int*);
|
||||
int sqlite3BtreeClearTable(Btree*, int, int*);
|
||||
int sqlite3BtreeClearTableOfCursor(BtCursor*);
|
||||
void sqlite3BtreeTripAllCursors(Btree*, int);
|
||||
|
||||
void sqlite3BtreeGetMeta(Btree *pBtree, int idx, u32 *pValue);
|
||||
@@ -178,21 +181,20 @@ 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*);
|
||||
|
||||
int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*);
|
||||
void sqlite3BtreeCacheOverflow(BtCursor *);
|
||||
void sqlite3BtreeIncrblobCursor(BtCursor *);
|
||||
void sqlite3BtreeClearCursor(BtCursor *);
|
||||
int sqlite3BtreeSetVersion(Btree *pBt, int iVersion);
|
||||
void sqlite3BtreeCursorHints(BtCursor *, unsigned int mask);
|
||||
int sqlite3BtreeIsReadonly(Btree *pBt);
|
||||
|
||||
#ifndef NDEBUG
|
||||
int sqlite3BtreeCursorIsValid(BtCursor*);
|
||||
|
||||
+22
-19
@@ -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).
|
||||
**
|
||||
@@ -495,28 +496,30 @@ struct BtCursor {
|
||||
BtShared *pBt; /* The BtShared this cursor points to */
|
||||
BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */
|
||||
struct KeyInfo *pKeyInfo; /* Argument passed to comparison function */
|
||||
#ifndef SQLITE_OMIT_INCRBLOB
|
||||
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 */
|
||||
i64 nKey; /* Size of pKey, or last integer key */
|
||||
void *pKey; /* Saved key that was cursor last known position */
|
||||
Pgno pgnoRoot; /* The root page of this tree */
|
||||
int nOvflAlloc; /* Allocated size of aOverflow[] array */
|
||||
int skipNext; /* Prev() is noop if negative. Next() is noop if positive */
|
||||
u8 wrFlag; /* True if writable */
|
||||
u8 atLast; /* Cursor pointing to the last entry */
|
||||
u8 validNKey; /* True if info.nKey is valid */
|
||||
u8 curFlags; /* zero or more BTCF_* flags defined below */
|
||||
u8 eState; /* One of the CURSOR_XXX constants (see below) */
|
||||
#ifndef SQLITE_OMIT_INCRBLOB
|
||||
u8 isIncrblobHandle; /* True if this cursor is an incr. io handle */
|
||||
#endif
|
||||
u8 hints; /* As configured by CursorSetHints() */
|
||||
i16 iPage; /* Index of current page in apPage */
|
||||
u16 aiIdx[BTCURSOR_MAX_DEPTH]; /* Current index in apPage[i] */
|
||||
MemPage *apPage[BTCURSOR_MAX_DEPTH]; /* Pages from root to current page */
|
||||
};
|
||||
|
||||
/*
|
||||
** Legal values for BtCursor.curFlags
|
||||
*/
|
||||
#define BTCF_WriteFlag 0x01 /* True if a write cursor */
|
||||
#define BTCF_ValidNKey 0x02 /* True if info.nKey is valid */
|
||||
#define BTCF_ValidOvfl 0x04 /* True if aOverflow is valid */
|
||||
#define BTCF_AtLast 0x08 /* Cursor is pointing ot the last entry */
|
||||
#define BTCF_Incrblob 0x10 /* True if an incremental I/O handle */
|
||||
|
||||
/*
|
||||
** Potential values for BtCursor.eState.
|
||||
**
|
||||
|
||||
+673
-238
File diff suppressed because it is too large
Load Diff
+5
-6
@@ -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);
|
||||
@@ -448,9 +447,9 @@ void sqlite3SchemaClear(void *p){
|
||||
sqlite3HashClear(&temp1);
|
||||
sqlite3HashClear(&pSchema->fkeyHash);
|
||||
pSchema->pSeqTab = 0;
|
||||
if( pSchema->flags & DB_SchemaLoaded ){
|
||||
if( pSchema->schemaFlags & DB_SchemaLoaded ){
|
||||
pSchema->iGeneration++;
|
||||
pSchema->flags &= ~DB_SchemaLoaded;
|
||||
pSchema->schemaFlags &= ~DB_SchemaLoaded;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+21
-3
@@ -88,7 +88,20 @@ int sqlcipher_codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLef
|
||||
}
|
||||
|
||||
CODEC_TRACE(("sqlcipher_codec_pragma: entered db=%p iDb=%d pParse=%p zLeft=%s zRight=%s ctx=%p\n", db, iDb, pParse, zLeft, zRight, ctx));
|
||||
|
||||
|
||||
if( sqlite3StrICmp(zLeft, "cipher_store_pass")==0 && zRight ) {
|
||||
sqlcipher_codec_set_store_pass(ctx, sqlite3GetBoolean(zRight, 1));
|
||||
} else
|
||||
if( sqlite3StrICmp(zLeft, "cipher_store_pass")==0 && !zRight ) {
|
||||
char *store_pass_value = sqlite3_mprintf("%d", sqlcipher_codec_get_store_pass(ctx));
|
||||
codec_vdbe_return_static_string(pParse, "cipher_store_pass", store_pass_value);
|
||||
sqlite3_free(store_pass_value);
|
||||
}
|
||||
if( sqlite3StrICmp(zLeft, "cipher_profile")== 0 && zRight ){
|
||||
char *profile_status = sqlite3_mprintf("%d", sqlcipher_cipher_profile(db, zRight));
|
||||
codec_vdbe_return_static_string(pParse, "cipher_profile", profile_status);
|
||||
sqlite3_free(profile_status);
|
||||
} else
|
||||
if( sqlite3StrICmp(zLeft, "cipher_add_random")==0 && zRight ){
|
||||
if(ctx) {
|
||||
char *add_random_status = sqlite3_mprintf("%d", sqlcipher_codec_add_random(ctx, zRight, sqlite3Strlen30(zRight)));
|
||||
@@ -243,6 +256,7 @@ int sqlcipher_codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLef
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* sqlite3Codec can be called in multiple modes.
|
||||
* encrypt mode - expected to return a pointer to the
|
||||
@@ -464,8 +478,12 @@ void sqlite3CodecGetKey(sqlite3* db, int nDb, void **zKey, int *nKey) {
|
||||
if( pDb->pBt ) {
|
||||
codec_ctx *ctx;
|
||||
sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx);
|
||||
if(ctx) { /* if the codec has an attached codec_context user the raw key data */
|
||||
sqlcipher_codec_get_keyspec(ctx, zKey, nKey);
|
||||
if(ctx) {
|
||||
if(sqlcipher_codec_get_store_pass(ctx) == 1) {
|
||||
sqlcipher_codec_get_pass(ctx, zKey, nKey);
|
||||
} else {
|
||||
sqlcipher_codec_get_keyspec(ctx, zKey, nKey);
|
||||
}
|
||||
} else {
|
||||
*zKey = NULL;
|
||||
*nKey = 0;
|
||||
|
||||
+7
-1
@@ -44,7 +44,7 @@
|
||||
#define FILE_HEADER_SZ 16
|
||||
|
||||
#ifndef CIPHER_VERSION
|
||||
#define CIPHER_VERSION "3.0.1"
|
||||
#define CIPHER_VERSION "3.2.0"
|
||||
#endif
|
||||
|
||||
#ifndef CIPHER
|
||||
@@ -214,6 +214,12 @@ int sqlcipher_codec_ctx_get_flag(codec_ctx *ctx, unsigned int flag, int for_ctx)
|
||||
const char* sqlcipher_codec_get_cipher_provider(codec_ctx *ctx);
|
||||
int sqlcipher_codec_ctx_migrate(codec_ctx *ctx);
|
||||
int sqlcipher_codec_add_random(codec_ctx *ctx, const char *data, int random_sz);
|
||||
int sqlcipher_cipher_profile(sqlite3 *db, const char *destination);
|
||||
static void sqlcipher_profile_callback(void *file, const char *sql, sqlite3_uint64 run_time);
|
||||
static int sqlcipher_codec_get_store_pass(codec_ctx *ctx);
|
||||
static void sqlcipher_codec_get_pass(codec_ctx *ctx, void **zKey, int *nKey);
|
||||
static void sqlcipher_codec_set_store_pass(codec_ctx *ctx, int value);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
/* END SQLCIPHER */
|
||||
|
||||
+1
-1
@@ -109,7 +109,7 @@ static int sqlcipher_cc_ctx_copy(void *target_ctx, void *source_ctx) {
|
||||
}
|
||||
|
||||
static int sqlcipher_cc_ctx_cmp(void *c1, void *c2) {
|
||||
return SQLITE_OK;
|
||||
return 1; /* always indicate contexts are the same */
|
||||
}
|
||||
|
||||
static int sqlcipher_cc_ctx_init(void **ctx) {
|
||||
|
||||
+80
-8
@@ -47,6 +47,7 @@
|
||||
to keep track of read / write state separately. The following
|
||||
struct and associated functions are defined here */
|
||||
typedef struct {
|
||||
int store_pass;
|
||||
int derive_key;
|
||||
int kdf_iter;
|
||||
int fast_kdf_iter;
|
||||
@@ -309,9 +310,7 @@ static void sqlcipher_cipher_ctx_free(cipher_ctx **iCtx) {
|
||||
* returns 1 otherwise
|
||||
*/
|
||||
static int sqlcipher_cipher_ctx_cmp(cipher_ctx *c1, cipher_ctx *c2) {
|
||||
CODEC_TRACE(("sqlcipher_cipher_ctx_cmp: entered c1=%p c2=%p\n", c1, c2));
|
||||
|
||||
if(
|
||||
int are_equal = (
|
||||
c1->iv_sz == c2->iv_sz
|
||||
&& c1->kdf_iter == c2->kdf_iter
|
||||
&& c1->fast_kdf_iter == c2->fast_kdf_iter
|
||||
@@ -325,9 +324,43 @@ static int sqlcipher_cipher_ctx_cmp(cipher_ctx *c1, cipher_ctx *c2) {
|
||||
|| !sqlcipher_memcmp((const unsigned char*)c1->pass,
|
||||
(const unsigned char*)c2->pass,
|
||||
c1->pass_sz)
|
||||
)
|
||||
) return 0;
|
||||
return 1;
|
||||
));
|
||||
|
||||
CODEC_TRACE(("sqlcipher_cipher_ctx_cmp: entered \
|
||||
c1=%p c2=%p \
|
||||
c1->iv_sz=%d c2->iv_sz=%d \
|
||||
c1->kdf_iter=%d c2->kdf_iter=%d \
|
||||
c1->fast_kdf_iter=%d c2->fast_kdf_iter=%d \
|
||||
c1->key_sz=%d c2->key_sz=%d \
|
||||
c1->pass_sz=%d c2->pass_sz=%d \
|
||||
c1->flags=%d c2->flags=%d \
|
||||
c1->hmac_sz=%d c2->hmac_sz=%d \
|
||||
c1->provider_ctx=%p c2->provider_ctx=%p \
|
||||
c1->pass=%p c2->pass=%p \
|
||||
c1->pass=%s c2->pass=%s \
|
||||
provider->ctx_cmp=%d \
|
||||
sqlcipher_memcmp=%d \
|
||||
are_equal=%d \
|
||||
\n",
|
||||
c1, c2,
|
||||
c1->iv_sz, c2->iv_sz,
|
||||
c1->kdf_iter, c2->kdf_iter,
|
||||
c1->fast_kdf_iter, c2->fast_kdf_iter,
|
||||
c1->key_sz, c2->key_sz,
|
||||
c1->pass_sz, c2->pass_sz,
|
||||
c1->flags, c2->flags,
|
||||
c1->hmac_sz, c2->hmac_sz,
|
||||
c1->provider_ctx, c2->provider_ctx,
|
||||
c1->pass, c2->pass,
|
||||
c1->pass, c2->pass,
|
||||
c1->provider->ctx_cmp(c1->provider_ctx, c2->provider_ctx),
|
||||
sqlcipher_memcmp((const unsigned char*)c1->pass,
|
||||
(const unsigned char*)c2->pass,
|
||||
c1->pass_sz),
|
||||
are_equal
|
||||
));
|
||||
|
||||
return !are_equal; /* return 0 if they are the same, 1 otherwise */
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -401,6 +434,19 @@ static int sqlcipher_cipher_ctx_set_keyspec(cipher_ctx *ctx, const unsigned char
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int sqlcipher_codec_get_store_pass(codec_ctx *ctx) {
|
||||
return ctx->read_ctx->store_pass;
|
||||
}
|
||||
|
||||
static void sqlcipher_codec_set_store_pass(codec_ctx *ctx, int value) {
|
||||
ctx->read_ctx->store_pass = value;
|
||||
}
|
||||
|
||||
static void sqlcipher_codec_get_pass(codec_ctx *ctx, void **zKey, int *nKey) {
|
||||
*zKey = ctx->read_ctx->pass;
|
||||
*nKey = ctx->read_ctx->pass_sz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the passphrase for the cipher_ctx
|
||||
*
|
||||
@@ -897,8 +943,10 @@ int sqlcipher_codec_key_derive(codec_ctx *ctx) {
|
||||
}
|
||||
|
||||
/* TODO: wipe and free passphrase after key derivation */
|
||||
sqlcipher_cipher_ctx_set_pass(ctx->read_ctx, NULL, 0);
|
||||
sqlcipher_cipher_ctx_set_pass(ctx->write_ctx, NULL, 0);
|
||||
if(ctx->read_ctx->store_pass != 1) {
|
||||
sqlcipher_cipher_ctx_set_pass(ctx->read_ctx, NULL, 0);
|
||||
sqlcipher_cipher_ctx_set_pass(ctx->write_ctx, NULL, 0);
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -1146,6 +1194,30 @@ int sqlcipher_codec_add_random(codec_ctx *ctx, const char *zRight, int random_sz
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
int sqlcipher_cipher_profile(sqlite3 *db, const char *destination){
|
||||
FILE *f;
|
||||
if( strcmp(destination,"stdout")==0 ){
|
||||
f = stdout;
|
||||
}else if( strcmp(destination, "stderr")==0 ){
|
||||
f = stderr;
|
||||
}else if( strcmp(destination, "off")==0 ){
|
||||
f = 0;
|
||||
}else{
|
||||
f = fopen(destination, "wb");
|
||||
if( f==0 ){
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
sqlite3_profile(db, sqlcipher_profile_callback, f);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static void sqlcipher_profile_callback(void *file, const char *sql, sqlite3_uint64 run_time){
|
||||
FILE *f = (FILE*)file;
|
||||
double elapsed = run_time/1000000.0;
|
||||
if( f ) fprintf(f, "Elapsed time:%.3f ms - %s\n", elapsed, sql);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
/* END SQLCIPHER */
|
||||
|
||||
+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);
|
||||
|
||||
+293
-129
@@ -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);
|
||||
sqlite3VdbeResolveLabel(v, iPartIdxLabel);
|
||||
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);
|
||||
sqlite3ResolvePartIdxLabel(pParse, 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
|
||||
@@ -615,18 +758,32 @@ void sqlite3GenerateRowIndexDelete(
|
||||
**
|
||||
** If *piPartIdxLabel is not NULL, fill it in with a label and jump
|
||||
** to that label if pIdx is a partial index that should be skipped.
|
||||
** The label should be resolved using sqlite3ResolvePartIdxLabel().
|
||||
** A partial index should be skipped if its WHERE clause evaluates
|
||||
** 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().
|
||||
** sqlite3ResolvePartIdxLabel().
|
||||
**
|
||||
** 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 +794,44 @@ int sqlite3GenerateIndexKey(
|
||||
if( piPartIdxLabel ){
|
||||
if( pIdx->pPartIdxWhere ){
|
||||
*piPartIdxLabel = sqlite3VdbeMakeLabel(v);
|
||||
pParse->iPartIdxTab = iCur;
|
||||
pParse->iPartIdxTab = iDataCur;
|
||||
sqlite3ExprCachePush(pParse);
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
** If a prior call to sqlite3GenerateIndexKey() generated a jump-over label
|
||||
** because it was a partial index, then this routine should be called to
|
||||
** resolve that label.
|
||||
*/
|
||||
void sqlite3ResolvePartIdxLabel(Parse *pParse, int iLabel){
|
||||
if( iLabel ){
|
||||
sqlite3VdbeResolveLabel(pParse->pVdbe, iLabel);
|
||||
sqlite3ExprCachePop(pParse);
|
||||
}
|
||||
}
|
||||
|
||||
+652
-549
File diff suppressed because it is too large
Load Diff
+228
-97
@@ -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 && IsUniqueIndex(pIdx) ){
|
||||
/* 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. */
|
||||
@@ -233,8 +233,8 @@ int sqlite3FkLocateIndex(
|
||||
if( zKey==0 ){
|
||||
/* If zKey is NULL, then this foreign key is implicitly mapped to
|
||||
** the PRIMARY KEY of table pParent. The PRIMARY KEY index may be
|
||||
** identified by the test (Index.autoIndex==2). */
|
||||
if( pIdx->autoIndex==2 ){
|
||||
** identified by the test. */
|
||||
if( IsPrimaryKeyIndex(pIdx) ){
|
||||
if( aiCol ){
|
||||
int i;
|
||||
for(i=0; i<nCol; i++) aiCol[i] = pFKey->aCol[i].iFrom;
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+67
-37
@@ -9,12 +9,9 @@
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains the C functions that implement various SQL
|
||||
** functions of SQLite.
|
||||
**
|
||||
** There is only one exported symbol in this file - the function
|
||||
** sqliteRegisterBuildinFunctions() found at the bottom of the file.
|
||||
** All other code has file scope.
|
||||
** This file contains the C-language implementions for many of the SQL
|
||||
** functions of SQLite. (Some function, and in particular the date and
|
||||
** time functions, are implemented separately.)
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <stdlib.h>
|
||||
@@ -137,9 +134,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 +215,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 +441,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 +567,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 +648,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 +704,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 +1014,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 +1534,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( zVal ) sqlite3StrAccumAppend(pAccum, zVal, nVal);
|
||||
}
|
||||
}
|
||||
static void groupConcatFinalize(sqlite3_context *context){
|
||||
@@ -1570,7 +1593,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1614,7 +1637,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;
|
||||
}
|
||||
|
||||
@@ -1626,7 +1649,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;
|
||||
}
|
||||
|
||||
@@ -1664,6 +1687,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 ),
|
||||
@@ -1675,11 +1699,14 @@ 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),
|
||||
FUNCTION2(likely, 1, 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 ),
|
||||
@@ -1689,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
|
||||
@@ -1705,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),
|
||||
@@ -1731,4 +1758,7 @@ void sqlite3RegisterGlobalFunctions(void){
|
||||
#ifndef SQLITE_OMIT_ALTERTABLE
|
||||
sqlite3AlterFunctions();
|
||||
#endif
|
||||
#if defined(SQLITE_ENABLE_STAT3) || defined(SQLITE_ENABLE_STAT4)
|
||||
sqlite3AnalyzeFunctions();
|
||||
#endif
|
||||
}
|
||||
|
||||
+11
-4
@@ -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 */
|
||||
@@ -172,18 +173,24 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
|
||||
0, /* isMutexInit */
|
||||
0, /* isMallocInit */
|
||||
0, /* isPCacheInit */
|
||||
0, /* pInitMutex */
|
||||
0, /* nRefInitMutex */
|
||||
0, /* pInitMutex */
|
||||
0, /* xLog */
|
||||
0, /* pLogArg */
|
||||
0, /* bLocaltimeFault */
|
||||
#ifdef SQLITE_ENABLE_SQLLOG
|
||||
0, /* xSqllog */
|
||||
0 /* pSqllogArg */
|
||||
0, /* pSqllogArg */
|
||||
#endif
|
||||
#ifdef SQLITE_VDBE_COVERAGE
|
||||
0, /* xVdbeBranch */
|
||||
0, /* pVbeBranchArg */
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_BUILTIN_TEST
|
||||
0, /* xTestCallback */
|
||||
#endif
|
||||
0 /* bLocaltimeFault */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** 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++];
|
||||
|
||||
+610
-516
File diff suppressed because it is too large
Load Diff
@@ -96,6 +96,9 @@ int sqlite3_exec(
|
||||
}
|
||||
}
|
||||
if( xCallback(pArg, nCol, azVals, azCols) ){
|
||||
/* EVIDENCE-OF: R-38229-40159 If the callback function to
|
||||
** sqlite3_exec() returns non-zero, then sqlite3_exec() will
|
||||
** return SQLITE_ABORT. */
|
||||
rc = SQLITE_ABORT;
|
||||
sqlite3VdbeFinalize((Vdbe *)pStmt);
|
||||
pStmt = 0;
|
||||
|
||||
+141
-28
@@ -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;
|
||||
}
|
||||
@@ -799,6 +800,7 @@ static void disconnectAllVtab(sqlite3 *db){
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3VtabUnlockList(db);
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
#else
|
||||
UNUSED_PARAMETER(db);
|
||||
@@ -825,6 +827,8 @@ static int connectionIsBusy(sqlite3 *db){
|
||||
*/
|
||||
static int sqlite3Close(sqlite3 *db, int forceZombie){
|
||||
if( !db ){
|
||||
/* EVIDENCE-OF: R-63257-11740 Calling sqlite3_close() or
|
||||
** sqlite3_close_v2() with a NULL pointer argument is a harmless no-op. */
|
||||
return SQLITE_OK;
|
||||
}
|
||||
if( !sqlite3SafetyCheckSickOrOk(db) ){
|
||||
@@ -979,9 +983,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 +1058,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_DEBUG) && SQLITE_OS_WIN) || defined(SQLITE_TEST)
|
||||
const char *sqlite3ErrName(int rc){
|
||||
const char *zName = 0;
|
||||
int i, origRc = rc;
|
||||
@@ -1079,6 +1080,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;
|
||||
@@ -1091,7 +1093,6 @@ const char *sqlite3ErrName(int rc){
|
||||
case SQLITE_IOERR_UNLOCK: zName = "SQLITE_IOERR_UNLOCK"; break;
|
||||
case SQLITE_IOERR_RDLOCK: zName = "SQLITE_IOERR_RDLOCK"; break;
|
||||
case SQLITE_IOERR_DELETE: zName = "SQLITE_IOERR_DELETE"; break;
|
||||
case SQLITE_IOERR_BLOCKED: zName = "SQLITE_IOERR_BLOCKED"; break;
|
||||
case SQLITE_IOERR_NOMEM: zName = "SQLITE_IOERR_NOMEM"; break;
|
||||
case SQLITE_IOERR_ACCESS: zName = "SQLITE_IOERR_ACCESS"; break;
|
||||
case SQLITE_IOERR_CHECKRESERVEDLOCK:
|
||||
@@ -1107,6 +1108,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 +1117,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 +1136,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 +1364,7 @@ int sqlite3CreateFunc(
|
||||
){
|
||||
FuncDef *p;
|
||||
int nName;
|
||||
int extraFlags;
|
||||
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
if( zFunctionName==0 ||
|
||||
@@ -1370,6 +1375,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 +1392,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 +1413,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 +1438,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 +1869,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 +1911,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 +1958,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 +2028,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(),
|
||||
@@ -2040,7 +2077,7 @@ static const int aHardLimit[] = {
|
||||
SQLITE_MAX_FUNCTION_ARG,
|
||||
SQLITE_MAX_ATTACHED,
|
||||
SQLITE_MAX_LIKE_PATTERN_LENGTH,
|
||||
SQLITE_MAX_VARIABLE_NUMBER,
|
||||
SQLITE_MAX_VARIABLE_NUMBER, /* IMP: R-38091-32352 */
|
||||
SQLITE_MAX_TRIGGER_DEPTH,
|
||||
};
|
||||
|
||||
@@ -2065,8 +2102,8 @@ static const int aHardLimit[] = {
|
||||
#if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>1000
|
||||
# error SQLITE_MAX_FUNCTION_ARG must be between 0 and 1000
|
||||
#endif
|
||||
#if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>62
|
||||
# error SQLITE_MAX_ATTACHED must be between 0 and 62
|
||||
#if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>125
|
||||
# error SQLITE_MAX_ATTACHED must be between 0 and 125
|
||||
#endif
|
||||
#if SQLITE_MAX_LIKE_PATTERN_LENGTH<1
|
||||
# error SQLITE_MAX_LIKE_PATTERN_LENGTH must be at least 1
|
||||
@@ -2588,8 +2625,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 +2635,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 +3096,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;
|
||||
}
|
||||
|
||||
@@ -3078,6 +3115,28 @@ int sqlite3_test_control(int op, ...){
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_test_control(FAULT_INSTALL, xCallback)
|
||||
**
|
||||
** Arrange to invoke xCallback() whenever sqlite3FaultSim() is called,
|
||||
** if xCallback is not NULL.
|
||||
**
|
||||
** As a test of the fault simulator mechanism itself, sqlite3FaultSim(0)
|
||||
** is called immediately after installing the new callback and the return
|
||||
** value from sqlite3FaultSim(0) becomes the return from
|
||||
** sqlite3_test_control().
|
||||
*/
|
||||
case SQLITE_TESTCTRL_FAULT_INSTALL: {
|
||||
/* MSVC is picky about pulling func ptrs from va lists.
|
||||
** http://support.microsoft.com/kb/47961
|
||||
** sqlite3GlobalConfig.xTestCallback = va_arg(ap, int(*)(int));
|
||||
*/
|
||||
typedef int(*TESTCALLBACKFUNC_t)(int);
|
||||
sqlite3GlobalConfig.xTestCallback = va_arg(ap, TESTCALLBACKFUNC_t);
|
||||
rc = sqlite3FaultSim(0);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_test_control(BENIGN_MALLOC_HOOKS, xBegin, xEnd)
|
||||
**
|
||||
@@ -3169,6 +3228,22 @@ int sqlite3_test_control(int op, ...){
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_test_control(SQLITE_TESTCTRL_BYTEORDER);
|
||||
**
|
||||
** The integer returned reveals the byte-order of the computer on which
|
||||
** SQLite is running:
|
||||
**
|
||||
** 1 big-endian, determined at run-time
|
||||
** 10 little-endian, determined at run-time
|
||||
** 432101 big-endian, determined at compile-time
|
||||
** 123410 little-endian, determined at compile-time
|
||||
*/
|
||||
case SQLITE_TESTCTRL_BYTEORDER: {
|
||||
rc = SQLITE_BYTEORDER*100 + SQLITE_LITTLEENDIAN*10 + SQLITE_BIGENDIAN;
|
||||
break;
|
||||
}
|
||||
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_RESERVE, sqlite3 *db, int N)
|
||||
**
|
||||
** Set the nReserve size to N for the main database on the database
|
||||
@@ -3259,6 +3334,44 @@ 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;
|
||||
}
|
||||
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_ISINIT);
|
||||
**
|
||||
** Return SQLITE_OK if SQLite has been initialized and SQLITE_ERROR if
|
||||
** not.
|
||||
*/
|
||||
case SQLITE_TESTCTRL_ISINIT: {
|
||||
if( sqlite3GlobalConfig.isInit==0 ) rc = SQLITE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
va_end(ap);
|
||||
#endif /* SQLITE_OMIT_BUILTIN_TEST */
|
||||
@@ -3307,7 +3420,7 @@ sqlite3_int64 sqlite3_uri_int64(
|
||||
){
|
||||
const char *z = sqlite3_uri_parameter(zFilename, zParam);
|
||||
sqlite3_int64 v;
|
||||
if( z && sqlite3Atoi64(z, &v, sqlite3Strlen30(z), SQLITE_UTF8)==SQLITE_OK ){
|
||||
if( z && sqlite3DecOrHexToI64(z, &v)==SQLITE_OK ){
|
||||
bDflt = v;
|
||||
}
|
||||
return bDflt;
|
||||
@@ -3343,5 +3456,5 @@ const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName){
|
||||
*/
|
||||
int sqlite3_db_readonly(sqlite3 *db, const char *zDbName){
|
||||
Btree *pBt = sqlite3DbNameToBtree(db, zDbName);
|
||||
return pBt ? sqlite3PagerIsreadonly(sqlite3BtreePager(pBt)) : -1;
|
||||
return pBt ? sqlite3BtreeIsReadonly(pBt) : -1;
|
||||
}
|
||||
|
||||
+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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+18
-23
@@ -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.
|
||||
@@ -267,13 +248,14 @@ static void *memsys5MallocUnsafe(int nByte){
|
||||
** block. If not, then split a block of the next larger power of
|
||||
** two in order to create a new free block of size iLogsize.
|
||||
*/
|
||||
for(iBin=iLogsize; mem5.aiFreelist[iBin]<0 && iBin<=LOGMAX; iBin++){}
|
||||
for(iBin=iLogsize; iBin<=LOGMAX && mem5.aiFreelist[iBin]<0; iBin++){}
|
||||
if( iBin>LOGMAX ){
|
||||
testcase( sqlite3GlobalConfig.xLog!=0 );
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -81,7 +81,7 @@ int sqlite3MutexEnd(void){
|
||||
*/
|
||||
sqlite3_mutex *sqlite3_mutex_alloc(int id){
|
||||
#ifndef SQLITE_OMIT_AUTOINIT
|
||||
if( sqlite3_initialize() ) return 0;
|
||||
if( id<=SQLITE_MUTEX_RECURSIVE && sqlite3_initialize() ) return 0;
|
||||
#endif
|
||||
return sqlite3GlobalConfig.mutex.xMutexAlloc(id);
|
||||
}
|
||||
|
||||
+1
-1
@@ -107,7 +107,7 @@ static int debugMutexEnd(void){ return SQLITE_OK; }
|
||||
** that means that a mutex could not be allocated.
|
||||
*/
|
||||
static sqlite3_mutex *debugMutexAlloc(int id){
|
||||
static sqlite3_debug_mutex aStatic[6];
|
||||
static sqlite3_debug_mutex aStatic[SQLITE_MUTEX_STATIC_APP3 - 1];
|
||||
sqlite3_debug_mutex *pNew = 0;
|
||||
switch( id ){
|
||||
case SQLITE_MUTEX_FAST:
|
||||
|
||||
+7
-1
@@ -96,10 +96,13 @@ static int pthreadMutexEnd(void){ return SQLITE_OK; }
|
||||
** <li> SQLITE_MUTEX_RECURSIVE
|
||||
** <li> SQLITE_MUTEX_STATIC_MASTER
|
||||
** <li> SQLITE_MUTEX_STATIC_MEM
|
||||
** <li> SQLITE_MUTEX_STATIC_MEM2
|
||||
** <li> SQLITE_MUTEX_STATIC_OPEN
|
||||
** <li> SQLITE_MUTEX_STATIC_PRNG
|
||||
** <li> SQLITE_MUTEX_STATIC_LRU
|
||||
** <li> SQLITE_MUTEX_STATIC_PMEM
|
||||
** <li> SQLITE_MUTEX_STATIC_APP1
|
||||
** <li> SQLITE_MUTEX_STATIC_APP2
|
||||
** <li> SQLITE_MUTEX_STATIC_APP3
|
||||
** </ul>
|
||||
**
|
||||
** The first two constants cause sqlite3_mutex_alloc() to create
|
||||
@@ -133,6 +136,9 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER
|
||||
};
|
||||
sqlite3_mutex *p;
|
||||
|
||||
+102
-73
@@ -9,13 +9,25 @@
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains the C functions that implement mutexes for win32
|
||||
** This file contains the C functions that implement mutexes for Win32.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
#if SQLITE_OS_WIN
|
||||
/*
|
||||
** Include code that is common to all os_*.c files
|
||||
*/
|
||||
#include "os_common.h"
|
||||
|
||||
/*
|
||||
** Include the header file for the Windows VFS.
|
||||
*/
|
||||
#include "os_win.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The code in this file is only used if we are compiling multithreaded
|
||||
** on a win32 system.
|
||||
** on a Win32 system.
|
||||
*/
|
||||
#ifdef SQLITE_MUTEX_W32
|
||||
|
||||
@@ -28,48 +40,22 @@ struct sqlite3_mutex {
|
||||
#ifdef SQLITE_DEBUG
|
||||
volatile int nRef; /* Number of enterances */
|
||||
volatile DWORD owner; /* Thread holding this mutex */
|
||||
int trace; /* True to trace changes */
|
||||
volatile int trace; /* True to trace changes */
|
||||
#endif
|
||||
};
|
||||
#define SQLITE_W32_MUTEX_INITIALIZER { 0 }
|
||||
#ifdef SQLITE_DEBUG
|
||||
#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0, 0L, (DWORD)0, 0 }
|
||||
#else
|
||||
#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0 }
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Return true (non-zero) if we are running under WinNT, Win2K, WinXP,
|
||||
** or WinCE. Return false (zero) for Win95, Win98, or WinME.
|
||||
**
|
||||
** Here is an interesting observation: Win95, Win98, and WinME lack
|
||||
** the LockFileEx() API. But we can still statically link against that
|
||||
** API as long as we don't call it win running Win95/98/ME. A call to
|
||||
** this routine is used to determine if the host is Win95/98/ME or
|
||||
** WinNT/2K/XP so that we will know whether or not we can safely call
|
||||
** the LockFileEx() API.
|
||||
**
|
||||
** mutexIsNT() is only used for the TryEnterCriticalSection() API call,
|
||||
** which is only available if your application was compiled with
|
||||
** _WIN32_WINNT defined to a value >= 0x0400. Currently, the only
|
||||
** call to TryEnterCriticalSection() is #ifdef'ed out, so #ifdef
|
||||
** this out as well.
|
||||
** These are the initializer values used when declaring a "static" mutex
|
||||
** on Win32. It should be noted that all mutexes require initialization
|
||||
** on the Win32 platform.
|
||||
*/
|
||||
#if 0
|
||||
#if SQLITE_OS_WINCE || SQLITE_OS_WINRT
|
||||
# define mutexIsNT() (1)
|
||||
#define SQLITE_W32_MUTEX_INITIALIZER { 0 }
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0, \
|
||||
0L, (DWORD)0, 0 }
|
||||
#else
|
||||
static int mutexIsNT(void){
|
||||
static int osType = 0;
|
||||
if( osType==0 ){
|
||||
OSVERSIONINFO sInfo;
|
||||
sInfo.dwOSVersionInfoSize = sizeof(sInfo);
|
||||
GetVersionEx(&sInfo);
|
||||
osType = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1;
|
||||
}
|
||||
return osType==2;
|
||||
}
|
||||
#endif /* SQLITE_OS_WINCE */
|
||||
#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0 }
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
@@ -80,20 +66,24 @@ struct sqlite3_mutex {
|
||||
static int winMutexHeld(sqlite3_mutex *p){
|
||||
return p->nRef!=0 && p->owner==GetCurrentThreadId();
|
||||
}
|
||||
|
||||
static int winMutexNotheld2(sqlite3_mutex *p, DWORD tid){
|
||||
return p->nRef==0 || p->owner!=tid;
|
||||
}
|
||||
|
||||
static int winMutexNotheld(sqlite3_mutex *p){
|
||||
DWORD tid = GetCurrentThreadId();
|
||||
DWORD tid = GetCurrentThreadId();
|
||||
return winMutexNotheld2(p, tid);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Initialize and deinitialize the mutex subsystem.
|
||||
*/
|
||||
static sqlite3_mutex winMutex_staticMutexes[6] = {
|
||||
static sqlite3_mutex winMutex_staticMutexes[] = {
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
@@ -101,17 +91,20 @@ static sqlite3_mutex winMutex_staticMutexes[6] = {
|
||||
SQLITE3_MUTEX_INITIALIZER,
|
||||
SQLITE3_MUTEX_INITIALIZER
|
||||
};
|
||||
static int winMutex_isInit = 0;
|
||||
/* As winMutexInit() and winMutexEnd() are called as part
|
||||
** of the sqlite3_initialize and sqlite3_shutdown()
|
||||
** processing, the "interlocked" magic is probably not
|
||||
** strictly necessary.
|
||||
*/
|
||||
static long winMutex_lock = 0;
|
||||
|
||||
static int winMutex_isInit = 0;
|
||||
static int winMutex_isNt = -1; /* <0 means "need to query" */
|
||||
|
||||
/* As the winMutexInit() and winMutexEnd() functions are called as part
|
||||
** of the sqlite3_initialize() and sqlite3_shutdown() processing, the
|
||||
** "interlocked" magic used here is probably not strictly necessary.
|
||||
*/
|
||||
static LONG volatile winMutex_lock = 0;
|
||||
|
||||
int sqlite3_win32_is_nt(void); /* os_win.c */
|
||||
void sqlite3_win32_sleep(DWORD milliseconds); /* os_win.c */
|
||||
|
||||
static int winMutexInit(void){
|
||||
static int winMutexInit(void){
|
||||
/* The first to increment to 1 does actual initialization */
|
||||
if( InterlockedCompareExchange(&winMutex_lock, 1, 0)==0 ){
|
||||
int i;
|
||||
@@ -124,16 +117,17 @@ static int winMutexInit(void){
|
||||
}
|
||||
winMutex_isInit = 1;
|
||||
}else{
|
||||
/* Someone else is in the process of initing the static mutexes */
|
||||
/* Another thread is (in the process of) initializing the static
|
||||
** mutexes */
|
||||
while( !winMutex_isInit ){
|
||||
sqlite3_win32_sleep(1);
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int winMutexEnd(void){
|
||||
/* The first to decrement to 0 does actual shutdown
|
||||
static int winMutexEnd(void){
|
||||
/* The first to decrement to 0 does actual shutdown
|
||||
** (which should be the last to shutdown.) */
|
||||
if( InterlockedCompareExchange(&winMutex_lock, 0, 1)==1 ){
|
||||
if( winMutex_isInit==1 ){
|
||||
@@ -144,7 +138,7 @@ static int winMutexEnd(void){
|
||||
winMutex_isInit = 0;
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -159,10 +153,13 @@ static int winMutexEnd(void){
|
||||
** <li> SQLITE_MUTEX_RECURSIVE
|
||||
** <li> SQLITE_MUTEX_STATIC_MASTER
|
||||
** <li> SQLITE_MUTEX_STATIC_MEM
|
||||
** <li> SQLITE_MUTEX_STATIC_MEM2
|
||||
** <li> SQLITE_MUTEX_STATIC_OPEN
|
||||
** <li> SQLITE_MUTEX_STATIC_PRNG
|
||||
** <li> SQLITE_MUTEX_STATIC_LRU
|
||||
** <li> SQLITE_MUTEX_STATIC_PMEM
|
||||
** <li> SQLITE_MUTEX_STATIC_APP1
|
||||
** <li> SQLITE_MUTEX_STATIC_APP2
|
||||
** <li> SQLITE_MUTEX_STATIC_APP3
|
||||
** </ul>
|
||||
**
|
||||
** The first two constants cause sqlite3_mutex_alloc() to create
|
||||
@@ -185,7 +182,7 @@ static int winMutexEnd(void){
|
||||
**
|
||||
** Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST
|
||||
** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc()
|
||||
** returns a different mutex on every call. But for the static
|
||||
** returns a different mutex on every call. But for the static
|
||||
** mutex types, the same mutex is returned on every call that has
|
||||
** the same type number.
|
||||
*/
|
||||
@@ -196,9 +193,12 @@ static sqlite3_mutex *winMutexAlloc(int iType){
|
||||
case SQLITE_MUTEX_FAST:
|
||||
case SQLITE_MUTEX_RECURSIVE: {
|
||||
p = sqlite3MallocZero( sizeof(*p) );
|
||||
if( p ){
|
||||
if( p ){
|
||||
#ifdef SQLITE_DEBUG
|
||||
p->id = iType;
|
||||
#ifdef SQLITE_WIN32_MUTEX_TRACE_DYNAMIC
|
||||
p->trace = 1;
|
||||
#endif
|
||||
#endif
|
||||
#if SQLITE_OS_WINRT
|
||||
InitializeCriticalSectionEx(&p->mutex, 0, 0);
|
||||
@@ -209,12 +209,15 @@ static sqlite3_mutex *winMutexAlloc(int iType){
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assert( winMutex_isInit==1 );
|
||||
assert( iType-2 >= 0 );
|
||||
assert( iType-2 < ArraySize(winMutex_staticMutexes) );
|
||||
assert( winMutex_isInit==1 );
|
||||
p = &winMutex_staticMutexes[iType-2];
|
||||
#ifdef SQLITE_DEBUG
|
||||
p->id = iType;
|
||||
#ifdef SQLITE_WIN32_MUTEX_TRACE_STATIC
|
||||
p->trace = 1;
|
||||
#endif
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@@ -230,8 +233,11 @@ static sqlite3_mutex *winMutexAlloc(int iType){
|
||||
*/
|
||||
static void winMutexFree(sqlite3_mutex *p){
|
||||
assert( p );
|
||||
#ifdef SQLITE_DEBUG
|
||||
assert( p->nRef==0 && p->owner==0 );
|
||||
assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );
|
||||
#endif
|
||||
assert( winMutex_isInit==1 );
|
||||
DeleteCriticalSection(&p->mutex);
|
||||
sqlite3_free(p);
|
||||
}
|
||||
@@ -248,30 +254,39 @@ static void winMutexFree(sqlite3_mutex *p){
|
||||
** more than once, the behavior is undefined.
|
||||
*/
|
||||
static void winMutexEnter(sqlite3_mutex *p){
|
||||
#ifdef SQLITE_DEBUG
|
||||
DWORD tid = GetCurrentThreadId();
|
||||
assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) );
|
||||
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
|
||||
DWORD tid = GetCurrentThreadId();
|
||||
#endif
|
||||
#ifdef SQLITE_DEBUG
|
||||
assert( p );
|
||||
assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) );
|
||||
#else
|
||||
assert( p );
|
||||
#endif
|
||||
assert( winMutex_isInit==1 );
|
||||
EnterCriticalSection(&p->mutex);
|
||||
#ifdef SQLITE_DEBUG
|
||||
assert( p->nRef>0 || p->owner==0 );
|
||||
p->owner = tid;
|
||||
p->owner = tid;
|
||||
p->nRef++;
|
||||
if( p->trace ){
|
||||
printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
|
||||
OSTRACE(("ENTER-MUTEX tid=%lu, mutex=%p (%d), nRef=%d\n",
|
||||
tid, p, p->trace, p->nRef));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int winMutexTry(sqlite3_mutex *p){
|
||||
#ifndef NDEBUG
|
||||
DWORD tid = GetCurrentThreadId();
|
||||
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
|
||||
DWORD tid = GetCurrentThreadId();
|
||||
#endif
|
||||
int rc = SQLITE_BUSY;
|
||||
assert( p );
|
||||
assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) );
|
||||
/*
|
||||
** The sqlite3_mutex_try() routine is very rarely used, and when it
|
||||
** is used it is merely an optimization. So it is OK for it to always
|
||||
** fail.
|
||||
** fail.
|
||||
**
|
||||
** The TryEnterCriticalSection() interface is only available on WinNT.
|
||||
** And some windows compilers complain if you try to use it without
|
||||
@@ -279,18 +294,27 @@ static int winMutexTry(sqlite3_mutex *p){
|
||||
** For that reason, we will omit this optimization for now. See
|
||||
** ticket #2685.
|
||||
*/
|
||||
#if 0
|
||||
if( mutexIsNT() && TryEnterCriticalSection(&p->mutex) ){
|
||||
#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0400
|
||||
assert( winMutex_isInit==1 );
|
||||
assert( winMutex_isNt>=-1 && winMutex_isNt<=1 );
|
||||
if( winMutex_isNt<0 ){
|
||||
winMutex_isNt = sqlite3_win32_is_nt();
|
||||
}
|
||||
assert( winMutex_isNt==0 || winMutex_isNt==1 );
|
||||
if( winMutex_isNt && TryEnterCriticalSection(&p->mutex) ){
|
||||
#ifdef SQLITE_DEBUG
|
||||
p->owner = tid;
|
||||
p->nRef++;
|
||||
#endif
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
#else
|
||||
UNUSED_PARAMETER(p);
|
||||
#endif
|
||||
#ifdef SQLITE_DEBUG
|
||||
if( rc==SQLITE_OK && p->trace ){
|
||||
printf("try mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
|
||||
if( p->trace ){
|
||||
OSTRACE(("TRY-MUTEX tid=%lu, mutex=%p (%d), owner=%lu, nRef=%d, rc=%s\n",
|
||||
tid, p, p->trace, p->owner, p->nRef, sqlite3ErrName(rc)));
|
||||
}
|
||||
#endif
|
||||
return rc;
|
||||
@@ -303,18 +327,23 @@ static int winMutexTry(sqlite3_mutex *p){
|
||||
** is not currently allocated. SQLite will never do either.
|
||||
*/
|
||||
static void winMutexLeave(sqlite3_mutex *p){
|
||||
#ifndef NDEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
|
||||
DWORD tid = GetCurrentThreadId();
|
||||
#endif
|
||||
assert( p );
|
||||
#ifdef SQLITE_DEBUG
|
||||
assert( p->nRef>0 );
|
||||
assert( p->owner==tid );
|
||||
p->nRef--;
|
||||
if( p->nRef==0 ) p->owner = 0;
|
||||
assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
|
||||
#endif
|
||||
assert( winMutex_isInit==1 );
|
||||
LeaveCriticalSection(&p->mutex);
|
||||
#ifdef SQLITE_DEBUG
|
||||
if( p->trace ){
|
||||
printf("leave mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
|
||||
OSTRACE(("LEAVE-MUTEX tid=%lu, mutex=%p (%d), nRef=%d\n",
|
||||
tid, p, p->trace, p->nRef));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -336,7 +365,7 @@ sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
|
||||
0
|
||||
#endif
|
||||
};
|
||||
|
||||
return &sMutex;
|
||||
}
|
||||
|
||||
#endif /* SQLITE_MUTEX_W32 */
|
||||
|
||||
@@ -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){
|
||||
|
||||
@@ -21,83 +21,10 @@
|
||||
#define _SQLITE_OS_H_
|
||||
|
||||
/*
|
||||
** Figure out if we are dealing with Unix, Windows, or some other
|
||||
** operating system. After the following block of preprocess macros,
|
||||
** all of SQLITE_OS_UNIX, SQLITE_OS_WIN, and SQLITE_OS_OTHER
|
||||
** will defined to either 1 or 0. One of the four will be 1. The other
|
||||
** three will be 0.
|
||||
** Attempt to automatically detect the operating system and setup the
|
||||
** necessary pre-processor macros for it.
|
||||
*/
|
||||
#if defined(SQLITE_OS_OTHER)
|
||||
# if SQLITE_OS_OTHER==1
|
||||
# undef SQLITE_OS_UNIX
|
||||
# define SQLITE_OS_UNIX 0
|
||||
# undef SQLITE_OS_WIN
|
||||
# define SQLITE_OS_WIN 0
|
||||
# else
|
||||
# undef SQLITE_OS_OTHER
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_OTHER)
|
||||
# define SQLITE_OS_OTHER 0
|
||||
# ifndef SQLITE_OS_WIN
|
||||
# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__)
|
||||
# define SQLITE_OS_WIN 1
|
||||
# define SQLITE_OS_UNIX 0
|
||||
# else
|
||||
# define SQLITE_OS_WIN 0
|
||||
# define SQLITE_OS_UNIX 1
|
||||
# endif
|
||||
# else
|
||||
# define SQLITE_OS_UNIX 0
|
||||
# endif
|
||||
#else
|
||||
# ifndef SQLITE_OS_WIN
|
||||
# define SQLITE_OS_WIN 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if SQLITE_OS_WIN
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Determine if we are dealing with Windows NT.
|
||||
**
|
||||
** We ought to be able to determine if we are compiling for win98 or winNT
|
||||
** using the _WIN32_WINNT macro as follows:
|
||||
**
|
||||
** #if defined(_WIN32_WINNT)
|
||||
** # define SQLITE_OS_WINNT 1
|
||||
** #else
|
||||
** # define SQLITE_OS_WINNT 0
|
||||
** #endif
|
||||
**
|
||||
** However, vs2005 does not set _WIN32_WINNT by default, as it ought to,
|
||||
** so the above test does not work. We'll just assume that everything is
|
||||
** winNT unless the programmer explicitly says otherwise by setting
|
||||
** SQLITE_OS_WINNT to 0.
|
||||
*/
|
||||
#if SQLITE_OS_WIN && !defined(SQLITE_OS_WINNT)
|
||||
# define SQLITE_OS_WINNT 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Determine if we are dealing with WindowsCE - which has a much
|
||||
** reduced API.
|
||||
*/
|
||||
#if defined(_WIN32_WCE)
|
||||
# define SQLITE_OS_WINCE 1
|
||||
#else
|
||||
# define SQLITE_OS_WINCE 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Determine if we are dealing with WinRT, which provides only a subset of
|
||||
** the full Win32 API.
|
||||
*/
|
||||
#if !defined(SQLITE_OS_WINRT)
|
||||
# define SQLITE_OS_WINRT 0
|
||||
#endif
|
||||
#include "os_setup.h"
|
||||
|
||||
/* If the SET_FULLSYNC macro is not defined above, then make it
|
||||
** a no-op
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
** 2013 November 25
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** This file contains pre-processor directives related to operating system
|
||||
** detection and/or setup.
|
||||
*/
|
||||
#ifndef _OS_SETUP_H_
|
||||
#define _OS_SETUP_H_
|
||||
|
||||
/*
|
||||
** Figure out if we are dealing with Unix, Windows, or some other operating
|
||||
** system.
|
||||
**
|
||||
** After the following block of preprocess macros, all of SQLITE_OS_UNIX,
|
||||
** SQLITE_OS_WIN, and SQLITE_OS_OTHER will defined to either 1 or 0. One of
|
||||
** the three will be 1. The other two will be 0.
|
||||
*/
|
||||
#if defined(SQLITE_OS_OTHER)
|
||||
# if SQLITE_OS_OTHER==1
|
||||
# undef SQLITE_OS_UNIX
|
||||
# define SQLITE_OS_UNIX 0
|
||||
# undef SQLITE_OS_WIN
|
||||
# define SQLITE_OS_WIN 0
|
||||
# else
|
||||
# undef SQLITE_OS_OTHER
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_OTHER)
|
||||
# define SQLITE_OS_OTHER 0
|
||||
# ifndef SQLITE_OS_WIN
|
||||
# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || \
|
||||
defined(__MINGW32__) || defined(__BORLANDC__)
|
||||
# define SQLITE_OS_WIN 1
|
||||
# define SQLITE_OS_UNIX 0
|
||||
# else
|
||||
# define SQLITE_OS_WIN 0
|
||||
# define SQLITE_OS_UNIX 1
|
||||
# endif
|
||||
# else
|
||||
# define SQLITE_OS_UNIX 0
|
||||
# endif
|
||||
#else
|
||||
# ifndef SQLITE_OS_WIN
|
||||
# define SQLITE_OS_WIN 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif /* _OS_SETUP_H_ */
|
||||
+181
-89
@@ -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.
|
||||
*/
|
||||
@@ -120,11 +94,10 @@
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
|
||||
#include <sys/mman.h>
|
||||
# include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
|
||||
#if SQLITE_ENABLE_LOCKING_STYLE
|
||||
#if SQLITE_ENABLE_LOCKING_STYLE || OS_VXWORKS
|
||||
# include <sys/ioctl.h>
|
||||
# if OS_VXWORKS
|
||||
# include <semaphore.h>
|
||||
@@ -218,11 +191,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 +233,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:
|
||||
*/
|
||||
@@ -336,11 +317,16 @@ static int posixOpen(const char *zFile, int flags, int mode){
|
||||
** we are not running as root.
|
||||
*/
|
||||
static int posixFchown(int fd, uid_t uid, gid_t gid){
|
||||
#if OS_VXWORKS
|
||||
return 0;
|
||||
#else
|
||||
return geteuid() ? 0 : fchown(fd,uid,gid);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Forward reference */
|
||||
static int openDirectory(const char*, int*);
|
||||
static int unixGetpagesize(void);
|
||||
|
||||
/*
|
||||
** Many system calls are accessed through pointer-to-functions so that
|
||||
@@ -391,7 +377,7 @@ static struct unix_syscall {
|
||||
{ "read", (sqlite3_syscall_ptr)read, 0 },
|
||||
#define osRead ((ssize_t(*)(int,void*,size_t))aSyscall[8].pCurrent)
|
||||
|
||||
#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE
|
||||
#if defined(USE_PREAD) || (SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS)
|
||||
{ "pread", (sqlite3_syscall_ptr)pread, 0 },
|
||||
#else
|
||||
{ "pread", (sqlite3_syscall_ptr)0, 0 },
|
||||
@@ -408,7 +394,7 @@ static struct unix_syscall {
|
||||
{ "write", (sqlite3_syscall_ptr)write, 0 },
|
||||
#define osWrite ((ssize_t(*)(int,const void*,size_t))aSyscall[11].pCurrent)
|
||||
|
||||
#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE
|
||||
#if defined(USE_PREAD) || (SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS)
|
||||
{ "pwrite", (sqlite3_syscall_ptr)pwrite, 0 },
|
||||
#else
|
||||
{ "pwrite", (sqlite3_syscall_ptr)0, 0 },
|
||||
@@ -449,6 +435,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 +448,10 @@ static struct unix_syscall {
|
||||
{ "mremap", (sqlite3_syscall_ptr)0, 0 },
|
||||
#endif
|
||||
#define osMremap ((void*(*)(void*,size_t,size_t,int,...))aSyscall[23].pCurrent)
|
||||
{ "getpagesize", (sqlite3_syscall_ptr)unixGetpagesize, 0 },
|
||||
#define osGetpagesize ((int(*)(void))aSyscall[24].pCurrent)
|
||||
|
||||
#endif
|
||||
|
||||
}; /* End of the overrideable system calls */
|
||||
|
||||
@@ -547,6 +538,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 +567,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;
|
||||
@@ -754,16 +764,6 @@ static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) {
|
||||
case EPERM:
|
||||
return SQLITE_PERM;
|
||||
|
||||
/* EDEADLK is only possible if a call to fcntl(F_SETLKW) is made. And
|
||||
** this module never makes such a call. And the code in SQLite itself
|
||||
** asserts that SQLITE_IOERR_BLOCKED is never returned. For these reasons
|
||||
** this case is also commented out. If the system does set errno to EDEADLK,
|
||||
** the default SQLITE_IOERR_XXX code will be returned. */
|
||||
#if 0
|
||||
case EDEADLK:
|
||||
return SQLITE_IOERR_BLOCKED;
|
||||
#endif
|
||||
|
||||
#if EOPNOTSUPP!=ENOTSUP
|
||||
case EOPNOTSUPP:
|
||||
/* something went terribly awry, unless during file system support
|
||||
@@ -1292,6 +1292,19 @@ 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){
|
||||
#if OS_VXWORKS
|
||||
return pFile->pInode!=0 && pFile->pId!=pFile->pInode->fileId.pId;
|
||||
#else
|
||||
struct stat buf;
|
||||
return pFile->pInode!=0 &&
|
||||
(osStat(pFile->zPath, &buf)!=0 || buf.st_ino!=pFile->pInode->fileId.ino);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Check a unixFile that is a database. Verify the following:
|
||||
@@ -1326,10 +1339,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 +1877,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 +1900,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;
|
||||
@@ -1899,6 +1915,13 @@ static int closeUnixFile(sqlite3_file *id){
|
||||
vxworksReleaseFileId(pFile->pId);
|
||||
pFile->pId = 0;
|
||||
}
|
||||
#endif
|
||||
#ifdef SQLITE_UNLINK_AFTER_CLOSE
|
||||
if( pFile->ctrlFlags & UNIXFILE_DELETE ){
|
||||
osUnlink(pFile->zPath);
|
||||
sqlite3_free(*(char**)&pFile->zPath);
|
||||
pFile->zPath = 0;
|
||||
}
|
||||
#endif
|
||||
OSTRACE(("CLOSE %-3d\n", pFile->h));
|
||||
OpenCounter(-1);
|
||||
@@ -2422,7 +2445,6 @@ static int semCheckReservedLock(sqlite3_file *id, int *pResOut) {
|
||||
/* Otherwise see if some other process holds it. */
|
||||
if( !reserved ){
|
||||
sem_t *pSem = pFile->pInode->pSem;
|
||||
struct stat statBuf;
|
||||
|
||||
if( sem_trywait(pSem)==-1 ){
|
||||
int tErrno = errno;
|
||||
@@ -2475,7 +2497,6 @@ static int semCheckReservedLock(sqlite3_file *id, int *pResOut) {
|
||||
*/
|
||||
static int semLock(sqlite3_file *id, int eFileLock) {
|
||||
unixFile *pFile = (unixFile*)id;
|
||||
int fd;
|
||||
sem_t *pSem = pFile->pInode->pSem;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
@@ -3091,6 +3112,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 +3227,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 +3613,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 +3621,7 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){
|
||||
if( nByte<pFile->mmapSize ){
|
||||
pFile->mmapSize = nByte;
|
||||
}
|
||||
#endif
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -3686,6 +3711,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 +3724,7 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){
|
||||
rc = unixMapfile(pFile, nByte);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -3766,6 +3793,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 +3814,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
|
||||
@@ -3926,8 +3959,25 @@ static int unixDeviceCharacteristics(sqlite3_file *id){
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
|
||||
|
||||
/*
|
||||
** Return the system page size.
|
||||
**
|
||||
** This function should not be called directly by other code in this file.
|
||||
** Instead, it should be called via macro osGetpagesize().
|
||||
*/
|
||||
static int unixGetpagesize(void){
|
||||
#if defined(_BSD_SOURCE)
|
||||
return getpagesize();
|
||||
#else
|
||||
return (int)sysconf(_SC_PAGESIZE);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 */
|
||||
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
|
||||
/*
|
||||
** Object used to represent an shared memory buffer.
|
||||
@@ -4044,7 +4094,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));
|
||||
@@ -4078,6 +4128,22 @@ static int unixShmSystemLock(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the minimum number of 32KB shm regions that should be mapped at
|
||||
** a time, assuming that each mapping must be an integer multiple of the
|
||||
** current system page-size.
|
||||
**
|
||||
** Usually, this is 1. The exception seems to be systems that are configured
|
||||
** to use 64KB pages - in this case each mapping must cover at least two
|
||||
** shm regions.
|
||||
*/
|
||||
static int unixShmRegionPerMap(void){
|
||||
int shmsz = 32*1024; /* SHM region size */
|
||||
int pgsz = osGetpagesize(); /* System page size */
|
||||
assert( ((pgsz-1)&pgsz)==0 ); /* Page size must be a power of 2 */
|
||||
if( pgsz<shmsz ) return 1;
|
||||
return pgsz/shmsz;
|
||||
}
|
||||
|
||||
/*
|
||||
** Purge the unixShmNodeList list of all entries with unixShmNode.nRef==0.
|
||||
@@ -4089,10 +4155,11 @@ static void unixShmPurge(unixFile *pFd){
|
||||
unixShmNode *p = pFd->pInode->pShmNode;
|
||||
assert( unixMutexHeld() );
|
||||
if( p && p->nRef==0 ){
|
||||
int nShmPerMap = unixShmRegionPerMap();
|
||||
int i;
|
||||
assert( p->pInode==pFd->pInode );
|
||||
sqlite3_mutex_free(p->mutex);
|
||||
for(i=0; i<p->nRegion; i++){
|
||||
for(i=0; i<p->nRegion; i+=nShmPerMap){
|
||||
if( p->h>=0 ){
|
||||
osMunmap(p->apRegion[i], p->szRegion);
|
||||
}else{
|
||||
@@ -4299,6 +4366,8 @@ static int unixShmMap(
|
||||
unixShm *p;
|
||||
unixShmNode *pShmNode;
|
||||
int rc = SQLITE_OK;
|
||||
int nShmPerMap = unixShmRegionPerMap();
|
||||
int nReqRegion;
|
||||
|
||||
/* If the shared-memory file has not yet been opened, open it now. */
|
||||
if( pDbFd->pShm==0 ){
|
||||
@@ -4314,9 +4383,12 @@ static int unixShmMap(
|
||||
assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 );
|
||||
assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 );
|
||||
|
||||
if( pShmNode->nRegion<=iRegion ){
|
||||
/* Minimum number of regions required to be mapped. */
|
||||
nReqRegion = ((iRegion+nShmPerMap) / nShmPerMap) * nShmPerMap;
|
||||
|
||||
if( pShmNode->nRegion<nReqRegion ){
|
||||
char **apNew; /* New apRegion[] array */
|
||||
int nByte = (iRegion+1)*szRegion; /* Minimum required file size */
|
||||
int nByte = nReqRegion*szRegion; /* Minimum required file size */
|
||||
struct stat sStat; /* Used by fstat() */
|
||||
|
||||
pShmNode->szRegion = szRegion;
|
||||
@@ -4365,17 +4437,19 @@ static int unixShmMap(
|
||||
|
||||
/* Map the requested memory region into this processes address space. */
|
||||
apNew = (char **)sqlite3_realloc(
|
||||
pShmNode->apRegion, (iRegion+1)*sizeof(char *)
|
||||
pShmNode->apRegion, nReqRegion*sizeof(char *)
|
||||
);
|
||||
if( !apNew ){
|
||||
rc = SQLITE_IOERR_NOMEM;
|
||||
goto shmpage_out;
|
||||
}
|
||||
pShmNode->apRegion = apNew;
|
||||
while(pShmNode->nRegion<=iRegion){
|
||||
while( pShmNode->nRegion<nReqRegion ){
|
||||
int nMap = szRegion*nShmPerMap;
|
||||
int i;
|
||||
void *pMem;
|
||||
if( pShmNode->h>=0 ){
|
||||
pMem = osMmap(0, szRegion,
|
||||
pMem = osMmap(0, nMap,
|
||||
pShmNode->isReadonly ? PROT_READ : PROT_READ|PROT_WRITE,
|
||||
MAP_SHARED, pShmNode->h, szRegion*(i64)pShmNode->nRegion
|
||||
);
|
||||
@@ -4391,8 +4465,11 @@ static int unixShmMap(
|
||||
}
|
||||
memset(pMem, 0, szRegion);
|
||||
}
|
||||
pShmNode->apRegion[pShmNode->nRegion] = pMem;
|
||||
pShmNode->nRegion++;
|
||||
|
||||
for(i=0; i<nShmPerMap; i++){
|
||||
pShmNode->apRegion[pShmNode->nRegion+i] = &((char*)pMem)[szRegion*i];
|
||||
}
|
||||
pShmNode->nRegion += nShmPerMap;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4592,37 +4669,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.
|
||||
*/
|
||||
static int unixGetPagesize(void){
|
||||
#if HAVE_MREMAP
|
||||
return 512;
|
||||
#elif defined(_BSD_SOURCE)
|
||||
return getpagesize();
|
||||
#else
|
||||
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.
|
||||
@@ -4659,8 +4719,12 @@ static void unixRemapfile(
|
||||
if( (pFd->ctrlFlags & UNIXFILE_RDONLY)==0 ) flags |= PROT_WRITE;
|
||||
|
||||
if( pOrig ){
|
||||
const int szSyspage = unixGetPagesize();
|
||||
#if HAVE_MREMAP
|
||||
i64 nReuse = pFd->mmapSize;
|
||||
#else
|
||||
const int szSyspage = osGetpagesize();
|
||||
i64 nReuse = (pFd->mmapSize & ~(szSyspage-1));
|
||||
#endif
|
||||
u8 *pReq = &pOrig[nReuse];
|
||||
|
||||
/* Unmap any pages of the existing mapping that cannot be reused. */
|
||||
@@ -4707,7 +4771,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 +4789,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 +4814,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 +4863,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 +4882,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 +5218,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 +5375,7 @@ static int fillInUnixFile(
|
||||
*/
|
||||
static const char *unixTempFileDir(void){
|
||||
static const char *azDirs[] = {
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
"/var/tmp",
|
||||
@@ -5317,7 +5388,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 +5676,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 ){
|
||||
@@ -5695,6 +5777,12 @@ static int unixOpen(
|
||||
if( isDelete ){
|
||||
#if OS_VXWORKS
|
||||
zPath = zName;
|
||||
#elif defined(SQLITE_UNLINK_AFTER_CLOSE)
|
||||
zPath = sqlite3_mprintf("%s", zName);
|
||||
if( zPath==0 ){
|
||||
robust_close(p, fd, __LINE__);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
#else
|
||||
osUnlink(zName);
|
||||
#endif
|
||||
@@ -5795,7 +5883,11 @@ static int unixDelete(
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
SimulateIOError(return SQLITE_IOERR_DELETE);
|
||||
if( osUnlink(zPath)==(-1) ){
|
||||
if( errno==ENOENT ){
|
||||
if( errno==ENOENT
|
||||
#if OS_VXWORKS
|
||||
|| errno==0x380003
|
||||
#endif
|
||||
){
|
||||
rc = SQLITE_IOERR_DELETE_NOENT;
|
||||
}else{
|
||||
rc = unixLogError(SQLITE_IOERR_DELETE, "unlink", zPath);
|
||||
@@ -5991,18 +6083,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__);
|
||||
@@ -7388,7 +7480,7 @@ int sqlite3_os_init(void){
|
||||
|
||||
/* Double-check that the aSyscall[] array has been constructed
|
||||
** correctly. See ticket [bb3a86e890c8e96ab] */
|
||||
assert( ArraySize(aSyscall)==24 );
|
||||
assert( ArraySize(aSyscall)==25 );
|
||||
|
||||
/* Register all VFSes defined in the aVfs[] array */
|
||||
for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){
|
||||
|
||||
+991
-413
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
** 2013 November 25
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** This file contains code that is specific to Windows.
|
||||
*/
|
||||
#ifndef _OS_WIN_H_
|
||||
#define _OS_WIN_H_
|
||||
|
||||
/*
|
||||
** Include the primary Windows SDK header file.
|
||||
*/
|
||||
#include "windows.h"
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
# include <sys/cygwin.h>
|
||||
# include <errno.h> /* amalgamator: dontcache */
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Determine if we are dealing with Windows NT.
|
||||
**
|
||||
** We ought to be able to determine if we are compiling for Windows 9x or
|
||||
** Windows NT using the _WIN32_WINNT macro as follows:
|
||||
**
|
||||
** #if defined(_WIN32_WINNT)
|
||||
** # define SQLITE_OS_WINNT 1
|
||||
** #else
|
||||
** # define SQLITE_OS_WINNT 0
|
||||
** #endif
|
||||
**
|
||||
** However, Visual Studio 2005 does not set _WIN32_WINNT by default, as
|
||||
** it ought to, so the above test does not work. We'll just assume that
|
||||
** everything is Windows NT unless the programmer explicitly says otherwise
|
||||
** by setting SQLITE_OS_WINNT to 0.
|
||||
*/
|
||||
#if SQLITE_OS_WIN && !defined(SQLITE_OS_WINNT)
|
||||
# define SQLITE_OS_WINNT 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Determine if we are dealing with Windows CE - which has a much reduced
|
||||
** API.
|
||||
*/
|
||||
#if defined(_WIN32_WCE)
|
||||
# define SQLITE_OS_WINCE 1
|
||||
#else
|
||||
# define SQLITE_OS_WINCE 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Determine if we are dealing with WinRT, which provides only a subset of
|
||||
** the full Win32 API.
|
||||
*/
|
||||
#if !defined(SQLITE_OS_WINRT)
|
||||
# define SQLITE_OS_WINRT 0
|
||||
#endif
|
||||
|
||||
#endif /* _OS_WIN_H_ */
|
||||
+143
-99
@@ -626,7 +626,8 @@ struct Pager {
|
||||
u8 ckptSyncFlags; /* SYNC_NORMAL or SYNC_FULL for checkpoint */
|
||||
u8 walSyncFlags; /* SYNC_NORMAL or SYNC_FULL for wal writes */
|
||||
u8 syncFlags; /* SYNC_NORMAL or SYNC_FULL otherwise */
|
||||
u8 tempFile; /* zFilename is a temporary file */
|
||||
u8 tempFile; /* zFilename is a temporary or immutable file */
|
||||
u8 noLock; /* Do not lock (except in WAL mode) */
|
||||
u8 readOnly; /* True for a read-only database */
|
||||
u8 memDb; /* True to inhibit all file I/O */
|
||||
|
||||
@@ -1024,15 +1025,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 +1039,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);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1094,7 +1092,7 @@ static int pagerUnlockDb(Pager *pPager, int eLock){
|
||||
assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 );
|
||||
if( isOpen(pPager->fd) ){
|
||||
assert( pPager->eLock>=eLock );
|
||||
rc = sqlite3OsUnlock(pPager->fd, eLock);
|
||||
rc = pPager->noLock ? SQLITE_OK : sqlite3OsUnlock(pPager->fd, eLock);
|
||||
if( pPager->eLock!=UNKNOWN_LOCK ){
|
||||
pPager->eLock = (u8)eLock;
|
||||
}
|
||||
@@ -1118,7 +1116,7 @@ static int pagerLockDb(Pager *pPager, int eLock){
|
||||
|
||||
assert( eLock==SHARED_LOCK || eLock==RESERVED_LOCK || eLock==EXCLUSIVE_LOCK );
|
||||
if( pPager->eLock<eLock || pPager->eLock==UNKNOWN_LOCK ){
|
||||
rc = sqlite3OsLock(pPager->fd, eLock);
|
||||
rc = pPager->noLock ? SQLITE_OK : sqlite3OsLock(pPager->fd, eLock);
|
||||
if( rc==SQLITE_OK && (pPager->eLock!=UNKNOWN_LOCK||eLock==EXCLUSIVE_LOCK) ){
|
||||
pPager->eLock = (u8)eLock;
|
||||
IOTRACE(("LOCK %p %d\n", pPager, eLock))
|
||||
@@ -1249,6 +1247,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)
|
||||
@@ -1626,12 +1625,11 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){
|
||||
|
||||
if( !zMaster
|
||||
|| pPager->journalMode==PAGER_JOURNALMODE_MEMORY
|
||||
|| pPager->journalMode==PAGER_JOURNALMODE_OFF
|
||||
|| !isOpen(pPager->jfd)
|
||||
){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
pPager->setMaster = 1;
|
||||
assert( isOpen(pPager->jfd) );
|
||||
assert( pPager->journalHdr <= pPager->journalOff );
|
||||
|
||||
/* Calculate the length in bytes and the checksum of zMaster */
|
||||
@@ -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);
|
||||
@@ -4672,30 +4675,38 @@ int sqlite3PagerOpen(
|
||||
** + The value returned by sqlite3OsSectorSize()
|
||||
** + The largest page size that can be written atomically.
|
||||
*/
|
||||
if( rc==SQLITE_OK && !readOnly ){
|
||||
setSectorSize(pPager);
|
||||
assert(SQLITE_DEFAULT_PAGE_SIZE<=SQLITE_MAX_DEFAULT_PAGE_SIZE);
|
||||
if( szPageDflt<pPager->sectorSize ){
|
||||
if( pPager->sectorSize>SQLITE_MAX_DEFAULT_PAGE_SIZE ){
|
||||
szPageDflt = SQLITE_MAX_DEFAULT_PAGE_SIZE;
|
||||
}else{
|
||||
szPageDflt = (u32)pPager->sectorSize;
|
||||
if( rc==SQLITE_OK ){
|
||||
int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
|
||||
if( !readOnly ){
|
||||
setSectorSize(pPager);
|
||||
assert(SQLITE_DEFAULT_PAGE_SIZE<=SQLITE_MAX_DEFAULT_PAGE_SIZE);
|
||||
if( szPageDflt<pPager->sectorSize ){
|
||||
if( pPager->sectorSize>SQLITE_MAX_DEFAULT_PAGE_SIZE ){
|
||||
szPageDflt = SQLITE_MAX_DEFAULT_PAGE_SIZE;
|
||||
}else{
|
||||
szPageDflt = (u32)pPager->sectorSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
{
|
||||
int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
|
||||
int ii;
|
||||
assert(SQLITE_IOCAP_ATOMIC512==(512>>8));
|
||||
assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8));
|
||||
assert(SQLITE_MAX_DEFAULT_PAGE_SIZE<=65536);
|
||||
for(ii=szPageDflt; ii<=SQLITE_MAX_DEFAULT_PAGE_SIZE; ii=ii*2){
|
||||
if( iDc&(SQLITE_IOCAP_ATOMIC|(ii>>8)) ){
|
||||
szPageDflt = ii;
|
||||
{
|
||||
int ii;
|
||||
assert(SQLITE_IOCAP_ATOMIC512==(512>>8));
|
||||
assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8));
|
||||
assert(SQLITE_MAX_DEFAULT_PAGE_SIZE<=65536);
|
||||
for(ii=szPageDflt; ii<=SQLITE_MAX_DEFAULT_PAGE_SIZE; ii=ii*2){
|
||||
if( iDc&(SQLITE_IOCAP_ATOMIC|(ii>>8)) ){
|
||||
szPageDflt = ii;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
pPager->noLock = sqlite3_uri_boolean(zFilename, "nolock", 0);
|
||||
if( (iDc & SQLITE_IOCAP_IMMUTABLE)!=0
|
||||
|| sqlite3_uri_boolean(zFilename, "immutable", 0) ){
|
||||
vfsFlags |= SQLITE_OPEN_READONLY;
|
||||
goto act_like_temp_file;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
/* If a temporary file is requested, it is not opened immediately.
|
||||
@@ -4705,10 +4716,14 @@ int sqlite3PagerOpen(
|
||||
** This branch is also run for an in-memory database. An in-memory
|
||||
** database is the same as a temp-file that is never written out to
|
||||
** disk and uses an in-memory rollback journal.
|
||||
**
|
||||
** This branch also runs for files marked as immutable.
|
||||
*/
|
||||
act_like_temp_file:
|
||||
tempFile = 1;
|
||||
pPager->eState = PAGER_READER;
|
||||
pPager->eLock = EXCLUSIVE_LOCK;
|
||||
pPager->eState = PAGER_READER; /* Pretend we already have a lock */
|
||||
pPager->eLock = EXCLUSIVE_LOCK; /* Pretend we are in EXCLUSIVE locking mode */
|
||||
pPager->noLock = 1; /* Do no locking */
|
||||
readOnly = (vfsFlags&SQLITE_OPEN_READONLY);
|
||||
}
|
||||
|
||||
@@ -4749,9 +4764,6 @@ int sqlite3PagerOpen(
|
||||
/* pPager->nPage = 0; */
|
||||
pPager->mxPgno = SQLITE_MAX_PAGE_COUNT;
|
||||
/* pPager->state = PAGER_UNLOCK; */
|
||||
#if 0
|
||||
assert( pPager->state == (tempFile ? PAGER_EXCLUSIVE : PAGER_UNLOCK) );
|
||||
#endif
|
||||
/* pPager->errMask = 0; */
|
||||
pPager->tempFile = (u8)tempFile;
|
||||
assert( tempFile==PAGER_LOCKINGMODE_NORMAL
|
||||
@@ -4797,6 +4809,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 +4898,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 +5306,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 +5447,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 +5514,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 +5647,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 +5660,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 +5685,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 +5694,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 +5707,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 +5759,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 +5791,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 +5842,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 +5865,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 +6018,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 +6227,7 @@ int sqlite3PagerCommitPhaseOne(
|
||||
|
||||
/* Finally, sync the database file. */
|
||||
if( !noSync ){
|
||||
rc = sqlite3PagerSync(pPager);
|
||||
rc = sqlite3PagerSync(pPager, zMaster);
|
||||
}
|
||||
IOTRACE(("DBSYNC %p\n", pPager))
|
||||
}
|
||||
@@ -6314,7 +6356,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 +6761,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 +6795,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 +6824,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);
|
||||
|
||||
+170
-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);
|
||||
@@ -1013,6 +1020,33 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
|
||||
*/
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_INTEGER, 0, 0, &sqlite3IntTokens[N]);
|
||||
sqlite3ExprDelete(pParse->db, X.pExpr);
|
||||
}else if( Y->nExpr==1 ){
|
||||
/* Expressions of the form:
|
||||
**
|
||||
** expr1 IN (?1)
|
||||
** expr1 NOT IN (?2)
|
||||
**
|
||||
** with exactly one value on the RHS can be simplified to something
|
||||
** like this:
|
||||
**
|
||||
** expr1 == ?1
|
||||
** expr1 <> ?2
|
||||
**
|
||||
** But, the RHS of the == or <> is marked with the EP_Generic flag
|
||||
** so that it may not contribute to the computation of comparison
|
||||
** affinity or the collating sequence to use for comparison. Otherwise,
|
||||
** the semantics would be subtly different from IN or NOT IN.
|
||||
*/
|
||||
Expr *pRHS = Y->a[0].pExpr;
|
||||
Y->a[0].pExpr = 0;
|
||||
sqlite3ExprListDelete(pParse->db, Y);
|
||||
/* pRHS cannot be NULL because a malloc error would have been detected
|
||||
** before now and control would have never reached this point */
|
||||
if( ALWAYS(pRHS) ){
|
||||
pRHS->flags &= ~EP_Collate;
|
||||
pRHS->flags |= EP_Generic;
|
||||
}
|
||||
A.pExpr = sqlite3PExpr(pParse, N ? TK_NE : TK_EQ, X.pExpr, pRHS, 0);
|
||||
}else{
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_IN, X.pExpr, 0, 0);
|
||||
if( A.pExpr ){
|
||||
@@ -1081,12 +1115,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 +1228,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 +1260,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 +1323,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 +1430,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;
|
||||
|
||||
+895
-377
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 );
|
||||
|
||||
+186
-103
@@ -135,19 +135,30 @@ static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
|
||||
#endif /* SQLITE_OMIT_FLOATING_POINT */
|
||||
|
||||
/*
|
||||
** Append N space characters to the given string buffer.
|
||||
** Set the StrAccum object to an error mode.
|
||||
*/
|
||||
void sqlite3AppendSpace(StrAccum *pAccum, int N){
|
||||
static const char zSpaces[] = " ";
|
||||
while( N>=(int)sizeof(zSpaces)-1 ){
|
||||
sqlite3StrAccumAppend(pAccum, zSpaces, sizeof(zSpaces)-1);
|
||||
N -= sizeof(zSpaces)-1;
|
||||
}
|
||||
if( N>0 ){
|
||||
sqlite3StrAccumAppend(pAccum, zSpaces, N);
|
||||
}
|
||||
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 +172,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 +193,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,16 +209,23 @@ 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;
|
||||
bufpt = (char *)fmt;
|
||||
amt = 1;
|
||||
while( (c=(*++fmt))!='%' && c!=0 ) amt++;
|
||||
sqlite3StrAccumAppend(pAccum, bufpt, amt);
|
||||
while( (c=(*++fmt))!='%' && c!=0 ){};
|
||||
sqlite3StrAccumAppend(pAccum, bufpt, (int)(fmt - bufpt));
|
||||
if( c==0 ) break;
|
||||
}
|
||||
if( (c=(*++fmt))==0 ){
|
||||
@@ -230,7 +250,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 +271,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 +306,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 +346,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 +369,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 +391,7 @@ void sqlite3VXPrintf(
|
||||
nOut = precision + 10;
|
||||
zOut = zExtra = sqlite3Malloc( nOut );
|
||||
if( zOut==0 ){
|
||||
pAccum->accError = STRACCUM_NOMEM;
|
||||
setStrAccumError(pAccum, STRACCUM_NOMEM);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -374,10 +406,8 @@ void sqlite3VXPrintf(
|
||||
*(--bufpt) = zOrd[x*2];
|
||||
}
|
||||
{
|
||||
register const char *cset; /* Use registers for speed */
|
||||
register int base;
|
||||
cset = &aDigits[infop->charset];
|
||||
base = infop->base;
|
||||
const char *cset = &aDigits[infop->charset];
|
||||
u8 base = infop->base;
|
||||
do{ /* Convert to ascii */
|
||||
*(--bufpt) = cset[longvalue%base];
|
||||
longvalue = longvalue/base;
|
||||
@@ -399,7 +429,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 +505,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 +588,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 +599,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 +616,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 +639,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 +657,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 +680,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 +691,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;
|
||||
}
|
||||
@@ -658,81 +711,113 @@ void sqlite3VXPrintf(
|
||||
** "length" characters long. The field width is "width". Do
|
||||
** the output.
|
||||
*/
|
||||
if( !flag_leftjustify ){
|
||||
register int nspace;
|
||||
nspace = width-length;
|
||||
if( nspace>0 ){
|
||||
sqlite3AppendSpace(pAccum, nspace);
|
||||
}
|
||||
}
|
||||
if( length>0 ){
|
||||
sqlite3StrAccumAppend(pAccum, bufpt, length);
|
||||
}
|
||||
if( flag_leftjustify ){
|
||||
register int nspace;
|
||||
nspace = width-length;
|
||||
if( nspace>0 ){
|
||||
sqlite3AppendSpace(pAccum, nspace);
|
||||
}
|
||||
}
|
||||
sqlite3_free(zExtra);
|
||||
width -= length;
|
||||
if( width>0 && !flag_leftjustify ) sqlite3AppendSpace(pAccum, width);
|
||||
sqlite3StrAccumAppend(pAccum, bufpt, length);
|
||||
if( width>0 && flag_leftjustify ) sqlite3AppendSpace(pAccum, width);
|
||||
|
||||
if( zExtra ) sqlite3_free(zExtra);
|
||||
}/* End for loop over the format string */
|
||||
} /* End of function */
|
||||
|
||||
/*
|
||||
** Append N bytes of text from z to the StrAccum object.
|
||||
** Enlarge the memory allocation on a StrAccum object so that it is
|
||||
** able to accept at least N more bytes of text.
|
||||
**
|
||||
** Return the number of bytes of text that StrAccum is able to accept
|
||||
** after the attempted enlargement. The value returned might be zero.
|
||||
*/
|
||||
void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
|
||||
assert( z!=0 || N==0 );
|
||||
static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
|
||||
char *zNew;
|
||||
assert( p->nChar+N >= p->nAlloc ); /* Only called if really needed */
|
||||
if( p->accError ){
|
||||
testcase(p->accError==STRACCUM_TOOBIG);
|
||||
testcase(p->accError==STRACCUM_NOMEM);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
assert( p->zText!=0 || p->nChar==0 );
|
||||
if( N<=0 ){
|
||||
if( N==0 || z[0]==0 ) return;
|
||||
N = sqlite3Strlen30(z);
|
||||
}
|
||||
if( p->nChar+N >= p->nAlloc ){
|
||||
char *zNew;
|
||||
if( !p->useMalloc ){
|
||||
p->accError = STRACCUM_TOOBIG;
|
||||
N = p->nAlloc - p->nChar - 1;
|
||||
if( N<=0 ){
|
||||
return;
|
||||
}
|
||||
if( !p->useMalloc ){
|
||||
N = p->nAlloc - p->nChar - 1;
|
||||
setStrAccumError(p, STRACCUM_TOOBIG);
|
||||
return N;
|
||||
}else{
|
||||
char *zOld = (p->zText==p->zBase ? 0 : p->zText);
|
||||
i64 szNew = p->nChar;
|
||||
szNew += N + 1;
|
||||
if( szNew > p->mxAlloc ){
|
||||
sqlite3StrAccumReset(p);
|
||||
setStrAccumError(p, STRACCUM_TOOBIG);
|
||||
return 0;
|
||||
}else{
|
||||
char *zOld = (p->zText==p->zBase ? 0 : p->zText);
|
||||
i64 szNew = p->nChar;
|
||||
szNew += N + 1;
|
||||
if( szNew > p->mxAlloc ){
|
||||
sqlite3StrAccumReset(p);
|
||||
p->accError = STRACCUM_TOOBIG;
|
||||
return;
|
||||
}else{
|
||||
p->nAlloc = (int)szNew;
|
||||
}
|
||||
if( p->useMalloc==1 ){
|
||||
zNew = sqlite3DbRealloc(p->db, zOld, p->nAlloc);
|
||||
}else{
|
||||
zNew = sqlite3_realloc(zOld, p->nAlloc);
|
||||
}
|
||||
if( zNew ){
|
||||
if( zOld==0 && p->nChar>0 ) memcpy(zNew, p->zText, p->nChar);
|
||||
p->zText = zNew;
|
||||
}else{
|
||||
p->accError = STRACCUM_NOMEM;
|
||||
sqlite3StrAccumReset(p);
|
||||
return;
|
||||
}
|
||||
p->nAlloc = (int)szNew;
|
||||
}
|
||||
if( p->useMalloc==1 ){
|
||||
zNew = sqlite3DbRealloc(p->db, zOld, p->nAlloc);
|
||||
}else{
|
||||
zNew = sqlite3_realloc(zOld, p->nAlloc);
|
||||
}
|
||||
if( zNew ){
|
||||
assert( p->zText!=0 || p->nChar==0 );
|
||||
if( zOld==0 && p->nChar>0 ) memcpy(zNew, p->zText, p->nChar);
|
||||
p->zText = zNew;
|
||||
}else{
|
||||
sqlite3StrAccumReset(p);
|
||||
setStrAccumError(p, STRACCUM_NOMEM);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return N;
|
||||
}
|
||||
|
||||
/*
|
||||
** Append N space characters to the given string buffer.
|
||||
*/
|
||||
void sqlite3AppendSpace(StrAccum *p, int N){
|
||||
if( p->nChar+N >= p->nAlloc && (N = sqlite3StrAccumEnlarge(p, N))<=0 ) return;
|
||||
while( (N--)>0 ) p->zText[p->nChar++] = ' ';
|
||||
}
|
||||
|
||||
/*
|
||||
** The StrAccum "p" is not large enough to accept N new bytes of z[].
|
||||
** So enlarge if first, then do the append.
|
||||
**
|
||||
** This is a helper routine to sqlite3StrAccumAppend() that does special-case
|
||||
** work (enlarging the buffer) using tail recursion, so that the
|
||||
** sqlite3StrAccumAppend() routine can use fast calling semantics.
|
||||
*/
|
||||
static void enlargeAndAppend(StrAccum *p, const char *z, int N){
|
||||
N = sqlite3StrAccumEnlarge(p, N);
|
||||
if( N>0 ){
|
||||
memcpy(&p->zText[p->nChar], z, N);
|
||||
p->nChar += N;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Append N bytes of text from z to the StrAccum object. Increase the
|
||||
** size of the memory allocation for StrAccum if necessary.
|
||||
*/
|
||||
void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
|
||||
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 ){
|
||||
enlargeAndAppend(p,z,N);
|
||||
return;
|
||||
}
|
||||
assert( p->zText );
|
||||
memcpy(&p->zText[p->nChar], z, 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 +835,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 +881,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 +1037,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 */
|
||||
|
||||
+67
-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-51414-32910 */
|
||||
/* 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,28 @@ 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).
|
||||
** EVIDENCE-OF: R-36850-34127 The likely(X) function is short-hand for
|
||||
** likelihood(X,0.9375).
|
||||
** EVIDENCE-OF: R-53436-40973 The likely(X) function is equivalent to
|
||||
** likelihood(X,0.9375). */
|
||||
/* TUNING: unlikely() probability is 0.0625. likely() is 0.9375 */
|
||||
pExpr->iTable = pDef->zName[0]=='u' ? 62 : 938;
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
if( pDef ){
|
||||
@@ -696,6 +732,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 +984,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 +1005,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 +1033,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 +1087,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 +1099,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -112,8 +112,8 @@ struct RowSet {
|
||||
struct RowSetEntry *pFresh; /* Source of new entry objects */
|
||||
struct RowSetEntry *pForest; /* List of binary trees of entries */
|
||||
u16 nFresh; /* Number of objects on pFresh */
|
||||
u8 rsFlags; /* Various flags */
|
||||
u8 iBatch; /* Current insert batch */
|
||||
u16 rsFlags; /* Various flags */
|
||||
int iBatch; /* Current insert batch */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -447,7 +447,7 @@ int sqlite3RowSetNext(RowSet *p, i64 *pRowid){
|
||||
** on pRowSet->pEntry, then sort those entires into the forest at
|
||||
** pRowSet->pForest so that they can be tested.
|
||||
*/
|
||||
int sqlite3RowSetTest(RowSet *pRowSet, u8 iBatch, sqlite3_int64 iRowid){
|
||||
int sqlite3RowSetTest(RowSet *pRowSet, int iBatch, sqlite3_int64 iRowid){
|
||||
struct RowSetEntry *p, *pTree;
|
||||
|
||||
/* This routine is never called after sqlite3RowSetNext() */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user