Merge branch 'master' into v2beta
Conflicts: Makefile.in
This commit is contained in:
commit
522a3d0934
40
Makefile.in
40
Makefile.in
@ -189,8 +189,8 @@ LIBOBJS0 = alter.lo analyze.lo attach.lo auth.lo \
|
||||
random.lo resolve.lo rowset.lo rtree.lo select.lo status.lo \
|
||||
table.lo tokenize.lo trigger.lo \
|
||||
update.lo util.lo vacuum.lo \
|
||||
vdbe.lo vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo vdbetrace.lo \
|
||||
wal.lo walker.lo where.lo utf.lo vtab.lo $(CRYPTOLIBOBJ)
|
||||
vdbe.lo vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo vdbesort.lo \
|
||||
vdbetrace.lo wal.lo walker.lo where.lo utf.lo vtab.lo $(CRYPTOLIBOBJ)
|
||||
|
||||
# Object files for the amalgamation.
|
||||
#
|
||||
@ -287,6 +287,7 @@ SRC = \
|
||||
$(TOP)/src/vdbeaux.c \
|
||||
$(TOP)/src/vdbeblob.c \
|
||||
$(TOP)/src/vdbemem.c \
|
||||
$(TOP)/src/vdbesort.c \
|
||||
$(TOP)/src/vdbetrace.c \
|
||||
$(TOP)/src/vdbeInt.h \
|
||||
$(TOP)/src/vtab.c \
|
||||
@ -530,6 +531,9 @@ sqlite3$(TEXE): $(TOP)/src/shell.c libsqlite3.la sqlite3.h
|
||||
sqlite3.c: .target_source $(TOP)/tool/mksqlite3c.tcl
|
||||
$(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl
|
||||
|
||||
sqlite3-all.c: sqlite3.c $(TOP)/tool/split-sqlite3c.tcl
|
||||
$(TCLSH_CMD) $(TOP)/tool/split-sqlite3c.tcl
|
||||
|
||||
# Rule to build the amalgamation
|
||||
#
|
||||
sqlite3.lo: sqlite3.c
|
||||
@ -757,6 +761,9 @@ vdbeblob.lo: $(TOP)/src/vdbeblob.c $(HDR)
|
||||
vdbemem.lo: $(TOP)/src/vdbemem.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/vdbemem.c
|
||||
|
||||
vdbesort.lo: $(TOP)/src/vdbesort.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/vdbesort.c
|
||||
|
||||
vdbetrace.lo: $(TOP)/src/vdbetrace.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/vdbetrace.c
|
||||
|
||||
@ -788,7 +795,7 @@ tclsqlite3$(TEXE): tclsqlite-shell.lo libsqlite3.la
|
||||
# Rules to build opcodes.c and opcodes.h
|
||||
#
|
||||
opcodes.c: opcodes.h $(TOP)/mkopcodec.awk
|
||||
sort -n -b -k 3 opcodes.h | $(NAWK) -f $(TOP)/mkopcodec.awk >opcodes.c
|
||||
$(NAWK) -f $(TOP)/mkopcodec.awk opcodes.h >opcodes.c
|
||||
|
||||
opcodes.h: parse.h $(TOP)/src/vdbe.c $(TOP)/mkopcodeh.awk
|
||||
cat parse.h $(TOP)/src/vdbe.c | $(NAWK) -f $(TOP)/mkopcodeh.awk >opcodes.h
|
||||
@ -805,7 +812,7 @@ parse.c: $(TOP)/src/parse.y lemon$(BEXE) $(TOP)/addopcodes.awk
|
||||
$(NAWK) -f $(TOP)/addopcodes.awk parse.h.temp >parse.h
|
||||
|
||||
sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest.uuid $(TOP)/VERSION
|
||||
tclsh $(TOP)/tool/mksqlite3h.tcl $(TOP) >sqlite3.h
|
||||
$(TCLSH_CMD) $(TOP)/tool/mksqlite3h.tcl $(TOP) >sqlite3.h
|
||||
|
||||
keywordhash.h: $(TOP)/tool/mkkeywordhash.c
|
||||
$(BCC) -o mkkeywordhash$(BEXE) $(OPT_FEATURE_FLAGS) $(OPTS) $(TOP)/tool/mkkeywordhash.c
|
||||
@ -879,6 +886,7 @@ rtree.lo: $(TOP)/ext/rtree/rtree.c $(HDR) $(EXTHDR)
|
||||
#
|
||||
TESTFIXTURE_FLAGS = -DTCLSH=1 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1
|
||||
TESTFIXTURE_FLAGS += -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE
|
||||
TESTFIXTURE_FLAGS += -DBUILD_sqlite
|
||||
|
||||
TESTFIXTURE_SRC0 = $(TESTSRC2) libsqlite3.la
|
||||
TESTFIXTURE_SRC1 = sqlite3.c
|
||||
@ -898,18 +906,16 @@ soaktest: testfixture$(TEXE) sqlite3$(TEXE)
|
||||
test: testfixture$(TEXE) sqlite3$(TEXE)
|
||||
./testfixture$(TEXE) $(TOP)/test/veryquick.test
|
||||
|
||||
sqlite3_analyzer$(TEXE): $(TESTFIXTURE_SRC) $(TOP)/tool/spaceanal.tcl
|
||||
sed \
|
||||
-e '/^#/d' \
|
||||
-e 's,\\,\\\\,g' \
|
||||
-e 's,",\\",g' \
|
||||
-e 's,^,",' \
|
||||
-e 's,$$,\\n",' \
|
||||
$(TOP)/tool/spaceanal.tcl >spaceanal_tcl.h
|
||||
$(LTLINK) -DTCLSH=2 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1 \
|
||||
-DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE \
|
||||
$(TEMP_STORE) -o $@ $(TESTFIXTURE_SRC) $(LIBTCL) $(TLIBS)
|
||||
sqlite3_analyzer.c: sqlite3.c $(TOP)/src/test_stat.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl
|
||||
echo "#define TCLSH 2" > $@
|
||||
cat sqlite3.c $(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 >> $@
|
||||
echo "; return zMainloop; }" >> $@
|
||||
|
||||
sqlite3_analyzer$(TEXE): sqlite3_analyzer.c
|
||||
$(LTLINK) sqlite3_analyzer.c -o $@ $(LIBTCL) $(TLIBS)
|
||||
|
||||
# Standard install and cleanup targets
|
||||
#
|
||||
@ -943,9 +949,11 @@ clean:
|
||||
rm -f $(PUBLISH)
|
||||
rm -f *.da *.bb *.bbg gmon.out
|
||||
rm -rf tsrc .target_source
|
||||
rm -f tclsqlite3$(TEXE)
|
||||
rm -f testfixture$(TEXE) test.db
|
||||
rm -f sqlite3.dll sqlite3.lib sqlite3.def
|
||||
rm -f sqlite3.dll sqlite3.lib sqlite3.exp sqlite3.def
|
||||
rm -f sqlite3.c
|
||||
rm -f sqlite3_analyzer$(TEXE) sqlite3_analyzer.c
|
||||
|
||||
distclean: clean
|
||||
rm -f config.log config.status libtool Makefile sqlite3.pc
|
||||
|
||||
202
Makefile.msc
202
Makefile.msc
@ -11,22 +11,43 @@ TOP = .
|
||||
#
|
||||
USE_AMALGAMATION = 1
|
||||
|
||||
# Set this non-0 to use the International Components for Unicode (ICU).
|
||||
#
|
||||
USE_ICU = 0
|
||||
|
||||
# Set this to non-0 to create and use PDBs.
|
||||
#
|
||||
SYMBOLS = 1
|
||||
|
||||
# Set this to one of the following values to enable various debugging
|
||||
# features. Each level includes the debugging options from the previous
|
||||
# levels. Currently, the recognized values for DEBUG are:
|
||||
#
|
||||
# 0 == NDEBUG: Disables assert() and other runtime diagnostics.
|
||||
# 1 == Disables NDEBUG and all optimizations and then enables PDBs.
|
||||
# 2 == SQLITE_DEBUG: Enables various diagnostics messages and code.
|
||||
# 3 == SQLITE_WIN32_MALLOC_VALIDATE: Validate the Win32 native heap per call.
|
||||
# 4 == SQLITE_DEBUG_OS_TRACE: Enables output from the OSTRACE() macros.
|
||||
# 5 == SQLITE_ENABLE_IOTRACE: Enables output from the IOTRACE() macros.
|
||||
#
|
||||
DEBUG = 0
|
||||
|
||||
# Version numbers and release number for the SQLite being compiled.
|
||||
#
|
||||
VERSION = 3.7
|
||||
VERSION_NUMBER = 3007007
|
||||
RELEASE = 3.7.7
|
||||
VERSION_NUMBER = 3007009
|
||||
RELEASE = 3.7.9
|
||||
|
||||
# C Compiler and options for use in building executables that
|
||||
# will run on the platform that is doing the build.
|
||||
#
|
||||
BCC = cl.exe -O2
|
||||
BCC = cl.exe
|
||||
|
||||
# C Compile and options for use in building executables that
|
||||
# will run on the target platform. (BCC and TCC are usually the
|
||||
# same unless your are cross-compiling.)
|
||||
#
|
||||
TCC = cl.exe -W3 -O2 -DSQLITE_OS_WIN=1 -I. -I$(TOP)\src -fp:precise
|
||||
TCC = cl.exe -W3 -DSQLITE_OS_WIN=1 -I. -I$(TOP)\src -fp:precise
|
||||
|
||||
# The mksqlite3c.tcl and mksqlite3h.tcl scripts will pull in
|
||||
# any extension header files by default. For non-amalgamation
|
||||
@ -41,18 +62,82 @@ TCC = $(TCC) -I$(TOP)\ext\rtree
|
||||
# Omitting the define will cause extra debugging code to be inserted and
|
||||
# includes extra comments when "EXPLAIN stmt" is used.
|
||||
#
|
||||
!IF $(DEBUG)==0
|
||||
TCC = $(TCC) -DNDEBUG
|
||||
!ENDIF
|
||||
|
||||
!IF $(DEBUG)>1
|
||||
TCC = $(TCC) -DSQLITE_DEBUG
|
||||
!ENDIF
|
||||
|
||||
!IF $(DEBUG)>3
|
||||
TCC = $(TCC) -DSQLITE_DEBUG_OS_TRACE=1
|
||||
!ENDIF
|
||||
|
||||
!IF $(DEBUG)>4
|
||||
TCC = $(TCC) -DSQLITE_ENABLE_IOTRACE
|
||||
!ENDIF
|
||||
|
||||
# The library that programs using TCL must link against.
|
||||
#
|
||||
LIBTCL = tcl85.lib
|
||||
# Prevent warnings about "insecure" runtime library functions being used.
|
||||
#
|
||||
TCC = $(TCC) -D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS
|
||||
|
||||
#
|
||||
# Use native Win32 heap instead of malloc/free?
|
||||
#
|
||||
# TCC = $(TCC) -DSQLITE_WIN32_MALLOC=1
|
||||
|
||||
#
|
||||
# Validate the heap on every call into the native Win32 heap subsystem?
|
||||
#
|
||||
!IF $(DEBUG)>2
|
||||
TCC = $(TCC) -DSQLITE_WIN32_MALLOC_VALIDATE=1
|
||||
!ENDIF
|
||||
|
||||
# The locations of the Tcl header and library files. Also, the library that
|
||||
# non-stubs enabled programs using Tcl must link against. These variables
|
||||
# (TCLINCDIR, TCLLIBDIR, and LIBTCL) may be overridden via the environment
|
||||
# prior to running nmake in order to match the actual installed location and
|
||||
# version on this machine.
|
||||
#
|
||||
!if "$(TCLINCDIR)" == ""
|
||||
TCLINCDIR = c:\tcl\include
|
||||
!endif
|
||||
|
||||
!if "$(TCLLIBDIR)" == ""
|
||||
TCLLIBDIR = c:\tcl\lib
|
||||
!endif
|
||||
|
||||
!if "$(LIBTCL)" == ""
|
||||
LIBTCL = tcl85.lib
|
||||
!endif
|
||||
|
||||
# The locations of the ICU header and library files. These variables
|
||||
# (ICUINCDIR, ICULIBDIR, and LIBICU) may be overridden via the environment
|
||||
# prior to running nmake in order to match the actual installed location on
|
||||
# this machine.
|
||||
#
|
||||
!if "$(ICUINCDIR)" == ""
|
||||
ICUINCDIR = c:\icu\include
|
||||
!endif
|
||||
|
||||
!if "$(ICULIBDIR)" == ""
|
||||
ICULIBDIR = c:\icu\lib
|
||||
!endif
|
||||
|
||||
!if "$(LIBICU)" == ""
|
||||
LIBICU = icuuc.lib icuin.lib
|
||||
!endif
|
||||
|
||||
# This is the command to use for tclsh - normally just "tclsh", but we may
|
||||
# know the specific version we want to use
|
||||
# know the specific version we want to use. This variable (TCLSH_CMD) may be
|
||||
# overridden via the environment prior to running nmake in order to select a
|
||||
# specific Tcl shell to use.
|
||||
#
|
||||
!if "$(TCLSH_CMD)" == ""
|
||||
TCLSH_CMD = tclsh85
|
||||
!endif
|
||||
|
||||
# Compiler options needed for programs that use the readline() library.
|
||||
#
|
||||
@ -72,7 +157,9 @@ TCC = $(TCC) -DSQLITE_THREAD_OVERRIDE_LOCK=-1
|
||||
|
||||
# Any target libraries which libsqlite must be linked against
|
||||
#
|
||||
!if "$(TLIBS)" == ""
|
||||
TLIBS =
|
||||
!endif
|
||||
|
||||
# Flags controlling use of the in memory btree implementation
|
||||
#
|
||||
@ -103,6 +190,25 @@ TCC = $(TCC) $(OPT_FEATURE_FLAGS)
|
||||
# ie. make "OPTS=-DSQLITE_ENABLE_FOO=1 -DSQLITE_OMIT_FOO=1".
|
||||
TCC = $(TCC) $(OPTS)
|
||||
|
||||
# If symbols are enabled, enable PDBs.
|
||||
# If debugging is enabled, disable all optimizations and enable PDBs.
|
||||
!IF $(DEBUG)>0
|
||||
TCC = $(TCC) -Od -D_DEBUG
|
||||
!ELSE
|
||||
TCC = $(TCC) -O2
|
||||
!ENDIF
|
||||
|
||||
!IF $(DEBUG)>0 || $(SYMBOLS)!=0
|
||||
TCC = $(TCC) -Zi
|
||||
!ENDIF
|
||||
|
||||
# If ICU support is enabled, add the compiler options for it.
|
||||
!IF $(USE_ICU)!=0
|
||||
TCC = $(TCC) -DSQLITE_ENABLE_ICU=1
|
||||
TCC = $(TCC) -I$(TOP)\ext\icu
|
||||
TCC = $(TCC) -I$(ICUINCDIR)
|
||||
!ENDIF
|
||||
|
||||
# libtool compile/link
|
||||
LTCOMPILE = $(TCC) -Fo$@
|
||||
LTLIB = lib.exe
|
||||
@ -117,8 +223,23 @@ LTLINKOPTS = /MACHINE:$(PLATFORM)
|
||||
LTLIBOPTS = /MACHINE:$(PLATFORM)
|
||||
!ENDIF
|
||||
|
||||
# If debugging is enabled, enable PDBs.
|
||||
!IF $(DEBUG)>0 || $(SYMBOLS)!=0
|
||||
LTLINKOPTS = $(LTLINKOPTS) /DEBUG
|
||||
!ENDIF
|
||||
|
||||
# Start with the Tcl related linker options.
|
||||
LTLIBPATHS = /LIBPATH:$(TCLLIBDIR)
|
||||
LTLIBS = $(LIBTCL)
|
||||
|
||||
# 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.
|
||||
NAWK = .\gawk.exe
|
||||
NAWK = gawk.exe
|
||||
|
||||
# You should not have to change anything below this line
|
||||
###############################################################################
|
||||
@ -141,8 +262,8 @@ LIBOBJS0 = alter.lo analyze.lo attach.lo auth.lo \
|
||||
random.lo resolve.lo rowset.lo rtree.lo select.lo status.lo \
|
||||
table.lo tokenize.lo trigger.lo \
|
||||
update.lo util.lo vacuum.lo \
|
||||
vdbe.lo vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo vdbetrace.lo \
|
||||
wal.lo walker.lo where.lo utf.lo vtab.lo
|
||||
vdbe.lo vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo vdbesort.lo \
|
||||
vdbetrace.lo wal.lo walker.lo where.lo utf.lo vtab.lo
|
||||
|
||||
# Object files for the amalgamation.
|
||||
#
|
||||
@ -241,6 +362,7 @@ SRC = \
|
||||
$(TOP)\src\vdbeaux.c \
|
||||
$(TOP)\src\vdbeblob.c \
|
||||
$(TOP)\src\vdbemem.c \
|
||||
$(TOP)\src\vdbesort.c \
|
||||
$(TOP)\src\vdbetrace.c \
|
||||
$(TOP)\src\vdbeInt.h \
|
||||
$(TOP)\src\vtab.c \
|
||||
@ -382,6 +504,7 @@ TESTSRC2 = \
|
||||
$(TOP)\src\vdbeaux.c \
|
||||
$(TOP)\src\vdbe.c \
|
||||
$(TOP)\src\vdbemem.c \
|
||||
$(TOP)\src\vdbesort.c \
|
||||
$(TOP)\src\vdbetrace.c \
|
||||
$(TOP)\src\where.c \
|
||||
parse.c \
|
||||
@ -439,18 +562,18 @@ EXTHDR = $(EXTHDR) \
|
||||
# This is the default Makefile target. The objects listed here
|
||||
# are what get build when you type just "make" with no arguments.
|
||||
#
|
||||
all: libsqlite3.lib sqlite3.exe libtclsqlite3.lib
|
||||
all: dll libsqlite3.lib sqlite3.exe libtclsqlite3.lib
|
||||
|
||||
libsqlite3.lib: $(LIBOBJ)
|
||||
$(LTLIB) $(LTLIBOPTS) /OUT:$@ $(LIBOBJ) $(TLIBS)
|
||||
|
||||
libtclsqlite3.lib: tclsqlite.lo libsqlite3.lib
|
||||
$(LTLIB) $(LTLIBOPTS) /LIBPATH:$(TCLLIBDIR) /OUT:$@ tclsqlite.lo libsqlite3.lib $(LIBTCL:tcl=tclstub) $(TLIBS)
|
||||
$(LTLIB) $(LTLIBOPTS) $(LTLIBPATHS) /OUT:$@ tclsqlite.lo libsqlite3.lib $(LIBTCL:tcl=tclstub) $(TLIBS)
|
||||
|
||||
sqlite3.exe: $(TOP)\src\shell.c libsqlite3.lib sqlite3.h
|
||||
$(LTLINK) $(READLINE_FLAGS) \
|
||||
$(TOP)\src\shell.c \
|
||||
/link $(LTLINKOPTS) libsqlite3.lib $(LIBREADLINE) $(TLIBS)
|
||||
/link $(LTLINKOPTS) $(LTLIBPATHS) libsqlite3.lib $(LIBREADLINE) $(LTLIBS) $(TLIBS)
|
||||
|
||||
# This target creates a directory named "tsrc" and fills it with
|
||||
# copies of all of the C source code and header files needed to
|
||||
@ -463,13 +586,16 @@ sqlite3.exe: $(TOP)\src\shell.c libsqlite3.lib 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 < 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
|
||||
|
||||
sqlite3-all.c: sqlite3.c $(TOP)/tool/split-sqlite3c.tcl
|
||||
$(TCLSH_CMD) $(TOP)/tool/split-sqlite3c.tcl
|
||||
|
||||
# Rule to build the amalgamation
|
||||
#
|
||||
sqlite3.lo: sqlite3.c
|
||||
@ -692,6 +818,9 @@ vdbeblob.lo: $(TOP)\src\vdbeblob.c $(HDR)
|
||||
vdbemem.lo: $(TOP)\src\vdbemem.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\vdbemem.c
|
||||
|
||||
vdbesort.lo: $(TOP)\src\vdbesort.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\vdbesort.c
|
||||
|
||||
vdbetrace.lo: $(TOP)\src\vdbetrace.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\vdbetrace.c
|
||||
|
||||
@ -715,15 +844,15 @@ tclsqlite-shell.lo: $(TOP)\src\tclsqlite.c $(HDR)
|
||||
|
||||
tclsqlite3.exe: tclsqlite-shell.lo libsqlite3.lib
|
||||
$(LTLINK) tclsqlite-shell.lo \
|
||||
/link $(LTLINKOPTS) /LIBPATH:$(TCLLIBDIR) libsqlite3.lib $(LIBTCL)
|
||||
/link $(LTLINKOPTS) $(LTLIBPATHS) libsqlite3.lib $(LTLIBS) $(TLIBS)
|
||||
|
||||
# Rules to build opcodes.c and opcodes.h
|
||||
#
|
||||
opcodes.c: opcodes.h $(TOP)\mkopcodec.awk
|
||||
$(NAWK) "/#define OP_/ { print }" opcodes.h | sort /+45 | $(NAWK) -f $(TOP)\mkopcodec.awk >opcodes.c
|
||||
$(NAWK) -f $(TOP)\mkopcodec.awk opcodes.h > opcodes.c
|
||||
|
||||
opcodes.h: parse.h $(TOP)\src\vdbe.c $(TOP)\mkopcodeh.awk
|
||||
type parse.h $(TOP)\src\vdbe.c | $(NAWK) -f $(TOP)\mkopcodeh.awk >opcodes.h
|
||||
type parse.h $(TOP)\src\vdbe.c | $(NAWK) -f $(TOP)\mkopcodeh.awk > opcodes.h
|
||||
|
||||
# Rules to build parse.c and parse.h - the outputs of lemon.
|
||||
#
|
||||
@ -734,16 +863,16 @@ parse.c: $(TOP)\src\parse.y lemon.exe $(TOP)\addopcodes.awk
|
||||
copy $(TOP)\src\parse.y .
|
||||
.\lemon.exe $(OPT_FEATURE_FLAGS) $(OPTS) parse.y
|
||||
move parse.h parse.h.temp
|
||||
$(NAWK) -f $(TOP)\addopcodes.awk parse.h.temp >parse.h
|
||||
$(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) -Femkkeywordhash.exe $(OPT_FEATURE_FLAGS) $(OPTS) $(TOP)\tool\mkkeywordhash.c
|
||||
|
||||
keywordhash.h: $(TOP)\tool\mkkeywordhash.c mkkeywordhash.exe
|
||||
.\mkkeywordhash.exe >keywordhash.h
|
||||
.\mkkeywordhash.exe > keywordhash.h
|
||||
|
||||
|
||||
|
||||
@ -826,7 +955,7 @@ testfixture.exe: $(TESTFIXTURE_SRC) $(HDR)
|
||||
$(LTLINK) -DSQLITE_NO_SYNC=1 $(TESTFIXTURE_FLAGS) \
|
||||
-DBUILD_sqlite -I$(TCLINCDIR) \
|
||||
$(TESTFIXTURE_SRC) \
|
||||
/link $(LTLINKOPTS) /LIBPATH:$(TCLLIBDIR) $(LIBTCL) $(TLIBS)
|
||||
/link $(LTLINKOPTS) $(LTLIBPATHS) $(LTLIBS) $(TLIBS)
|
||||
|
||||
fulltest: testfixture.exe sqlite3.exe
|
||||
.\testfixture.exe $(TOP)\test\all.test
|
||||
@ -837,28 +966,29 @@ soaktest: testfixture.exe sqlite3.exe
|
||||
test: testfixture.exe sqlite3.exe
|
||||
.\testfixture.exe $(TOP)\test\veryquick.test
|
||||
|
||||
spaceanal_tcl.h: $(TOP)\tool\spaceanal.tcl
|
||||
$(NAWK) -f $(TOP)/tool/tostr.awk \
|
||||
$(TOP)\tool\spaceanal.tcl >spaceanal_tcl.h
|
||||
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 $@
|
||||
echo static const char *tclsh_main_loop(void){ >> $@
|
||||
echo static const char *zMainloop = >> $@
|
||||
$(NAWK) -f $(TOP)\tool\tostr.awk $(TOP)\tool\spaceanal.tcl >> $@
|
||||
echo ; return zMainloop; } >> $@
|
||||
|
||||
sqlite3_analyzer.exe: $(TESTFIXTURE_SRC) spaceanal_tcl.h
|
||||
$(LTLINK) -DTCLSH=2 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1 \
|
||||
-DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE \
|
||||
-DBUILD_sqlite -I$(TCLINCDIR) \
|
||||
$(TESTFIXTURE_SRC) \
|
||||
/link $(LTLINKOPTS) /LIBPATH:$(TCLLIBDIR) $(LIBTCL) $(TLIBS)
|
||||
sqlite3_analyzer.exe: sqlite3_analyzer.c
|
||||
$(LTLINK) -DBUILD_sqlite -DTCLSH=2 -I$(TCLINCDIR) sqlite3_analyzer.c \
|
||||
/link $(LTLINKOPTS) $(LTLIBPATHS) $(LTLIBS) $(TLIBS)
|
||||
|
||||
clean:
|
||||
del /Q *.lo *.lib *.obj sqlite3.exe libsqlite3.lib
|
||||
del /Q *.lo *.ilk *.lib *.obj *.pdb sqlite3.exe libsqlite3.lib
|
||||
del /Q sqlite3.h opcodes.c opcodes.h
|
||||
del /Q lemon.exe lempar.c parse.*
|
||||
del /Q mkkeywordhash.exe keywordhash.h
|
||||
-rmdir /Q/S tsrc
|
||||
del /Q .target_source
|
||||
del /Q tclsqlite3.exe
|
||||
del /Q testfixture.exe testfixture.exp test.db
|
||||
del /Q sqlite3.dll sqlite3.lib sqlite3.exp sqlite3.def
|
||||
del /Q sqlite3.c
|
||||
del /Q sqlite3_analyzer.exe sqlite3_analyzer.exp spaceanal_tcl.h
|
||||
del /Q sqlite3_analyzer.exe sqlite3_analyzer.exp sqlite3_analyzer.c
|
||||
|
||||
#
|
||||
# Windows section
|
||||
@ -866,10 +996,10 @@ clean:
|
||||
dll: sqlite3.dll
|
||||
|
||||
sqlite3.def: libsqlite3.lib
|
||||
echo EXPORTS >sqlite3.def
|
||||
echo EXPORTS > sqlite3.def
|
||||
dumpbin /all libsqlite3.lib \
|
||||
| $(NAWK) "/ 1 _sqlite3_/ { sub(/^.* _/,\"\");print }" \
|
||||
| sort >>sqlite3.def
|
||||
| $(NAWK) "/ 1 _?sqlite3_/ { sub(/^.* _?/,\"\");print }" \
|
||||
| sort >> sqlite3.def
|
||||
|
||||
sqlite3.dll: $(LIBOBJ) sqlite3.def
|
||||
link $(LTLINKOPTS) /DLL /DEF:sqlite3.def /OUT:$@ $(LIBOBJ)
|
||||
link $(LTLINKOPTS) $(LTLIBPATHS) /DLL /DEF:sqlite3.def /OUT:$@ $(LIBOBJ) $(LTLIBS) $(TLIBS)
|
||||
|
||||
@ -517,7 +517,7 @@ tclsqlite.o: $(TOP)/src/tclsqlite.c $(HDR)
|
||||
# Rules to build opcodes.c and opcodes.h
|
||||
#
|
||||
opcodes.c: opcodes.h $(TOP)/mkopcodec.awk
|
||||
sort -n -b -k 3 opcodes.h | $(NAWK) -f $(TOP)/mkopcodec.awk >opcodes.c
|
||||
$(NAWK) -f $(TOP)/mkopcodec.awk opcodes.h >opcodes.c
|
||||
|
||||
opcodes.h: parse.h $(TOP)/src/vdbe.c $(TOP)/mkopcodeh.awk
|
||||
cat parse.h $(TOP)/src/vdbe.c | \
|
||||
|
||||
@ -282,7 +282,7 @@ tokens) and it honors the same commenting conventions as C and C++.</p>
|
||||
<p>A terminal symbol (token) is any string of alphanumeric
|
||||
and underscore characters
|
||||
that begins with an upper case letter.
|
||||
A terminal can contain lower class letters after the first character,
|
||||
A terminal can contain lowercase letters after the first character,
|
||||
but the usual convention is to make terminals all upper case.
|
||||
A nonterminal, on the other hand, is any string of alphanumeric
|
||||
and underscore characters than begins with a lower case letter.
|
||||
|
||||
178
ext/fts3/README.content
Normal file
178
ext/fts3/README.content
Normal file
@ -0,0 +1,178 @@
|
||||
|
||||
FTS4 CONTENT OPTION
|
||||
|
||||
Normally, in order to create a full-text index on a dataset, the FTS4
|
||||
module stores a copy of all indexed documents in a specially created
|
||||
database table.
|
||||
|
||||
As of SQLite version 3.7.9, FTS4 supports a new option - "content" -
|
||||
designed to extend FTS4 to support the creation of full-text indexes where:
|
||||
|
||||
* The indexed documents are not stored within the SQLite database
|
||||
at all (a "contentless" FTS4 table), or
|
||||
|
||||
* The indexed documents are stored in a database table created and
|
||||
managed by the user (an "external content" FTS4 table).
|
||||
|
||||
Because the indexed documents themselves are usually much larger than
|
||||
the full-text index, the content option can sometimes be used to achieve
|
||||
significant space savings.
|
||||
|
||||
CONTENTLESS FTS4 TABLES
|
||||
|
||||
In order to create an FTS4 table that does not store a copy of the indexed
|
||||
documents at all, the content option should be set to an empty string.
|
||||
For example, the following SQL creates such an FTS4 table with three
|
||||
columns - "a", "b", and "c":
|
||||
|
||||
CREATE VIRTUAL TABLE t1 USING fts4(content="", a, b, c);
|
||||
|
||||
Data can be inserted into such an FTS4 table using an INSERT statements.
|
||||
However, unlike ordinary FTS4 tables, the user must supply an explicit
|
||||
integer docid value. For example:
|
||||
|
||||
-- This statement is Ok:
|
||||
INSERT INTO t1(docid, a, b, c) VALUES(1, 'a b c', 'd e f', 'g h i');
|
||||
|
||||
-- This statement causes an error, as no docid value has been provided:
|
||||
INSERT INTO t1(a, b, c) VALUES('j k l', 'm n o', 'p q r');
|
||||
|
||||
It is not possible to UPDATE or DELETE a row stored in a contentless FTS4
|
||||
table. Attempting to do so is an error.
|
||||
|
||||
Contentless FTS4 tables also support SELECT statements. However, it is
|
||||
an error to attempt to retrieve the value of any table column other than
|
||||
the docid column. The auxiliary function matchinfo() may be used, but
|
||||
snippet() and offsets() may not. For example:
|
||||
|
||||
-- The following statements are Ok:
|
||||
SELECT docid FROM t1 WHERE t1 MATCH 'xxx';
|
||||
SELECT docid FROM t1 WHERE a MATCH 'xxx';
|
||||
SELECT matchinfo(t1) FROM t1 WHERE t1 MATCH 'xxx';
|
||||
|
||||
-- The following statements all cause errors, as the value of columns
|
||||
-- other than docid are required to evaluate them.
|
||||
SELECT * FROM t1;
|
||||
SELECT a, b FROM t1 WHERE t1 MATCH 'xxx';
|
||||
SELECT docid FROM t1 WHERE a LIKE 'xxx%';
|
||||
SELECT snippet(t1) FROM t1 WHERE t1 MATCH 'xxx';
|
||||
|
||||
Errors related to attempting to retrieve column values other than docid
|
||||
are runtime errors that occur within sqlite3_step(). In some cases, for
|
||||
example if the MATCH expression in a SELECT query matches zero rows, there
|
||||
may be no error at all even if a statement does refer to column values
|
||||
other than docid.
|
||||
|
||||
EXTERNAL CONTENT FTS4 TABLES
|
||||
|
||||
An "external content" FTS4 table is similar to a contentless table, except
|
||||
that if evaluation of a query requires the value of a column other than
|
||||
docid, FTS4 attempts to retrieve that value from a table (or view, or
|
||||
virtual table) nominated by the user (hereafter referred to as the "content
|
||||
table"). The FTS4 module never writes to the content table, and writing
|
||||
to the content table does not affect the full-text index. It is the
|
||||
responsibility of the user to ensure that the content table and the
|
||||
full-text index are consistent.
|
||||
|
||||
An external content FTS4 table is created by setting the content option
|
||||
to the name of a table (or view, or virtual table) that may be queried by
|
||||
FTS4 to retrieve column values when required. If the nominated table does
|
||||
not exist, then an external content table behaves in the same way as
|
||||
a contentless table. For example:
|
||||
|
||||
CREATE TABLE t2(id INTEGER PRIMARY KEY, a, b, c);
|
||||
CREATE VIRTUAL TABLE t3 USING fts4(content="t2", a, c);
|
||||
|
||||
Assuming the nominated table does exist, then its columns must be the same
|
||||
as or a superset of those defined for the FTS table.
|
||||
|
||||
When a users query on the FTS table requires a column value other than
|
||||
docid, FTS attempts to read this value from the corresponding column of
|
||||
the row in the content table with a rowid value equal to the current FTS
|
||||
docid. Or, if such a row cannot be found in the content table, a NULL
|
||||
value is used instead. For example:
|
||||
|
||||
CREATE TABLE t2(id INTEGER PRIMARY KEY, a, b, c, d);
|
||||
CREATE VIRTUAL TABLE t3 USING fts4(content="t2", b, c);
|
||||
|
||||
INSERT INTO t2 VALUES(2, 'a b', 'c d', 'e f');
|
||||
INSERT INTO t2 VALUES(3, 'g h', 'i j', 'k l');
|
||||
INSERT INTO t3(docid, b, c) SELECT id, b, c FROM t2;
|
||||
|
||||
-- The following query returns a single row with two columns containing
|
||||
-- the text values "i j" and "k l".
|
||||
--
|
||||
-- The query uses the full-text index to discover that the MATCH
|
||||
-- term matches the row with docid=3. It then retrieves the values
|
||||
-- of columns b and c from the row with rowid=3 in the content table
|
||||
-- to return.
|
||||
--
|
||||
SELECT * FROM t3 WHERE t3 MATCH 'k';
|
||||
|
||||
-- Following the UPDATE, the query still returns a single row, this
|
||||
-- time containing the text values "xxx" and "yyy". This is because the
|
||||
-- full-text index still indicates that the row with docid=3 matches
|
||||
-- the FTS4 query 'k', even though the documents stored in the content
|
||||
-- table have been modified.
|
||||
--
|
||||
UPDATE t2 SET b = 'xxx', c = 'yyy' WHERE rowid = 3;
|
||||
SELECT * FROM t3 WHERE t3 MATCH 'k';
|
||||
|
||||
-- Following the DELETE below, the query returns one row containing two
|
||||
-- NULL values. NULL values are returned because FTS is unable to find
|
||||
-- a row with rowid=3 within the content table.
|
||||
--
|
||||
DELETE FROM t2;
|
||||
SELECT * FROM t3 WHERE t3 MATCH 'k';
|
||||
|
||||
When a row is deleted from an external content FTS4 table, FTS4 needs to
|
||||
retrieve the column values of the row being deleted from the content table.
|
||||
This is so that FTS4 can update the full-text index entries for each token
|
||||
that occurs within the deleted row to indicate that that row has been
|
||||
deleted. If the content table row cannot be found, or if it contains values
|
||||
inconsistent with the contents of the FTS index, the results can be difficult
|
||||
to predict. The FTS index may be left containing entries corresponding to the
|
||||
deleted row, which can lead to seemingly nonsensical results being returned
|
||||
by subsequent SELECT queries. The same applies when a row is updated, as
|
||||
internally an UPDATE is the same as a DELETE followed by an INSERT.
|
||||
|
||||
Instead of writing separately to the full-text index and the content table,
|
||||
some users may wish to use database triggers to keep the full-text index
|
||||
up to date with respect to the set of documents stored in the content table.
|
||||
For example, using the tables from earlier examples:
|
||||
|
||||
CREATE TRIGGER t2_bu BEFORE UPDATE ON t2 BEGIN
|
||||
DELETE FROM t3 WHERE docid=old.rowid;
|
||||
END;
|
||||
CREATE TRIGGER t2_bd BEFORE DELETE ON t2 BEGIN
|
||||
DELETE FROM t3 WHERE docid=old.rowid;
|
||||
END;
|
||||
|
||||
CREATE TRIGGER t2_bu AFTER UPDATE ON t2 BEGIN
|
||||
INSERT INTO t3(docid, b, c) VALUES(new.rowid, new.b, new.c);
|
||||
END;
|
||||
CREATE TRIGGER t2_bd AFTER INSERT ON t2 BEGIN
|
||||
INSERT INTO t3(docid, b, c) VALUES(new.rowid, new.b, new.c);
|
||||
END;
|
||||
|
||||
The DELETE trigger must be fired before the actual delete takes place
|
||||
on the content table. This is so that FTS4 can still retrieve the original
|
||||
values in order to update the full-text index. And the INSERT trigger must
|
||||
be fired after the new row is inserted, so as to handle the case where the
|
||||
rowid is assigned automatically within the system. The UPDATE trigger must
|
||||
be split into two parts, one fired before and one after the update of the
|
||||
content table, for the same reasons.
|
||||
|
||||
FTS4 features a special command similar to the 'optimize' command that
|
||||
deletes the entire full-text index and rebuilds it based on the current
|
||||
set of documents in the content table. Assuming again that "t3" is the
|
||||
name of the external content FTS4 table, the command is:
|
||||
|
||||
INSERT INTO t3(t3) VALUES('rebuild');
|
||||
|
||||
This command may also be used with ordinary FTS4 tables, although it may
|
||||
only be useful if the full-text index has somehow become corrupt. It is an
|
||||
error to attempt to rebuild the full-text index maintained by a contentless
|
||||
FTS4 table.
|
||||
|
||||
|
||||
1576
ext/fts3/fts3.c
1576
ext/fts3/fts3.c
File diff suppressed because it is too large
Load Diff
@ -27,7 +27,14 @@
|
||||
# define SQLITE_ENABLE_FTS3
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS3
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
|
||||
|
||||
/* If not building as part of the core, include sqlite3ext.h. */
|
||||
#ifndef SQLITE_CORE
|
||||
# include "sqlite3ext.h"
|
||||
extern const sqlite3_api_routines *sqlite3_api;
|
||||
#endif
|
||||
|
||||
#include "sqlite3.h"
|
||||
#include "fts3_tokenizer.h"
|
||||
#include "fts3_hash.h"
|
||||
@ -150,6 +157,13 @@ typedef sqlite3_uint64 u64; /* 8-byte unsigned integer */
|
||||
|
||||
#endif /* SQLITE_AMALGAMATION */
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
int sqlite3Fts3Corrupt(void);
|
||||
# define FTS_CORRUPT_VTAB sqlite3Fts3Corrupt()
|
||||
#else
|
||||
# define FTS_CORRUPT_VTAB SQLITE_CORRUPT_VTAB
|
||||
#endif
|
||||
|
||||
typedef struct Fts3Table Fts3Table;
|
||||
typedef struct Fts3Cursor Fts3Cursor;
|
||||
typedef struct Fts3Expr Fts3Expr;
|
||||
@ -177,6 +191,7 @@ struct Fts3Table {
|
||||
int nColumn; /* number of named columns in virtual table */
|
||||
char **azColumn; /* column names. malloced */
|
||||
sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */
|
||||
char *zContentTbl; /* content=xxx option, or NULL */
|
||||
|
||||
/* Precompiled statements used by the implementation. Each of these
|
||||
** statements is run and reset within a single virtual table API call.
|
||||
@ -217,7 +232,7 @@ struct Fts3Table {
|
||||
int nPendingData; /* Current bytes of pending data */
|
||||
sqlite_int64 iPrevDocid; /* Docid of most recently inserted document */
|
||||
|
||||
#if defined(SQLITE_DEBUG)
|
||||
#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
|
||||
/* State variables used for validating that the transaction control
|
||||
** methods of the virtual table are called at appropriate times. These
|
||||
** values do not contribution to the FTS computation; they are used for
|
||||
@ -290,7 +305,7 @@ struct Fts3Doclist {
|
||||
int bFreeList; /* True if pList should be sqlite3_free()d */
|
||||
char *pList; /* Pointer to position list following iDocid */
|
||||
int nList; /* Length of position list */
|
||||
} doclist;
|
||||
};
|
||||
|
||||
/*
|
||||
** A "phrase" is a sequence of one or more tokens that must match in
|
||||
@ -302,6 +317,7 @@ struct Fts3PhraseToken {
|
||||
char *z; /* Text of the token */
|
||||
int n; /* Number of bytes in buffer z */
|
||||
int isPrefix; /* True if token ends with a "*" character */
|
||||
int bFirst; /* True if token must appear at position 0 */
|
||||
|
||||
/* Variables above this point are populated when the expression is
|
||||
** parsed (by code in fts3_expr.c). Below this point the variables are
|
||||
@ -420,6 +436,7 @@ int sqlite3Fts3SegReaderCursor(
|
||||
#define FTS3_SEGMENT_COLUMN_FILTER 0x00000004
|
||||
#define FTS3_SEGMENT_PREFIX 0x00000008
|
||||
#define FTS3_SEGMENT_SCAN 0x00000010
|
||||
#define FTS3_SEGMENT_FIRST 0x00000020
|
||||
|
||||
/* Type passed as 4th argument to SegmentReaderIterate() */
|
||||
struct Fts3SegFilter {
|
||||
@ -459,8 +476,8 @@ int sqlite3Fts3GetVarint32(const char *, int *);
|
||||
int sqlite3Fts3VarintLen(sqlite3_uint64);
|
||||
void sqlite3Fts3Dequote(char *);
|
||||
void sqlite3Fts3DoclistPrev(int,char*,int,char**,sqlite3_int64*,int*,u8*);
|
||||
|
||||
int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *);
|
||||
int sqlite3Fts3FirstFilter(sqlite3_int64, char *, int, char *);
|
||||
|
||||
/* fts3_tokenizer.c */
|
||||
const char *sqlite3Fts3NextToken(const char *, int *);
|
||||
@ -479,7 +496,7 @@ void sqlite3Fts3Matchinfo(sqlite3_context *, Fts3Cursor *, const char *);
|
||||
|
||||
/* fts3_expr.c */
|
||||
int sqlite3Fts3ExprParse(sqlite3_tokenizer *,
|
||||
char **, int, int, const char *, int, Fts3Expr **
|
||||
char **, int, int, int, const char *, int, Fts3Expr **
|
||||
);
|
||||
void sqlite3Fts3ExprFree(Fts3Expr *);
|
||||
#ifdef SQLITE_TEST
|
||||
@ -490,19 +507,8 @@ int sqlite3Fts3InitTerm(sqlite3 *db);
|
||||
/* fts3_aux.c */
|
||||
int sqlite3Fts3InitAux(sqlite3 *db);
|
||||
|
||||
int sqlite3Fts3TermSegReaderCursor(
|
||||
Fts3Cursor *pCsr, /* Virtual table cursor handle */
|
||||
const char *zTerm, /* Term to query for */
|
||||
int nTerm, /* Size of zTerm in bytes */
|
||||
int isPrefix, /* True for a prefix search */
|
||||
Fts3MultiSegReader **ppSegcsr /* OUT: Allocated seg-reader cursor */
|
||||
);
|
||||
|
||||
void sqlite3Fts3EvalPhraseCleanup(Fts3Phrase *);
|
||||
|
||||
int sqlite3Fts3EvalStart(Fts3Cursor *, Fts3Expr *, int);
|
||||
int sqlite3Fts3EvalNext(Fts3Cursor *pCsr);
|
||||
|
||||
int sqlite3Fts3MsrIncrStart(
|
||||
Fts3Table*, Fts3MultiSegReader*, int, const char*, int);
|
||||
int sqlite3Fts3MsrIncrNext(
|
||||
@ -513,5 +519,5 @@ int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr);
|
||||
|
||||
int sqlite3Fts3DeferredTokenList(Fts3DeferredToken *, char **, int *);
|
||||
|
||||
#endif /* SQLITE_ENABLE_FTS3 */
|
||||
#endif /* !SQLITE_CORE || SQLITE_ENABLE_FTS3 */
|
||||
#endif /* _FTSINT_H */
|
||||
|
||||
@ -93,6 +93,7 @@ typedef struct ParseContext ParseContext;
|
||||
struct ParseContext {
|
||||
sqlite3_tokenizer *pTokenizer; /* Tokenizer module */
|
||||
const char **azCol; /* Array of column names for fts3 table */
|
||||
int bFts4; /* True to allow FTS4-only syntax */
|
||||
int nCol; /* Number of entries in azCol[] */
|
||||
int iDefaultCol; /* Default column to query */
|
||||
int isNot; /* True if getNextNode() sees a unary - */
|
||||
@ -180,9 +181,21 @@ static int getNextToken(
|
||||
pRet->pPhrase->aToken[0].isPrefix = 1;
|
||||
iEnd++;
|
||||
}
|
||||
if( !sqlite3_fts3_enable_parentheses && iStart>0 && z[iStart-1]=='-' ){
|
||||
pParse->isNot = 1;
|
||||
|
||||
while( 1 ){
|
||||
if( !sqlite3_fts3_enable_parentheses
|
||||
&& iStart>0 && z[iStart-1]=='-'
|
||||
){
|
||||
pParse->isNot = 1;
|
||||
iStart--;
|
||||
}else if( pParse->bFts4 && iStart>0 && z[iStart-1]=='^' ){
|
||||
pRet->pPhrase->aToken[0].bFirst = 1;
|
||||
iStart--;
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
nConsumed = iEnd;
|
||||
}
|
||||
@ -281,6 +294,7 @@ static int getNextString(
|
||||
|
||||
pToken->n = nByte;
|
||||
pToken->isPrefix = (iEnd<nInput && zInput[iEnd]=='*');
|
||||
pToken->bFirst = (iBegin>0 && zInput[iBegin-1]=='^');
|
||||
nToken = ii+1;
|
||||
}
|
||||
}
|
||||
@ -302,8 +316,12 @@ static int getNextString(
|
||||
p->pPhrase->nToken = nToken;
|
||||
|
||||
zBuf = (char *)&p->pPhrase->aToken[nToken];
|
||||
memcpy(zBuf, zTemp, nTemp);
|
||||
sqlite3_free(zTemp);
|
||||
if( zTemp ){
|
||||
memcpy(zBuf, zTemp, nTemp);
|
||||
sqlite3_free(zTemp);
|
||||
}else{
|
||||
assert( nTemp==0 );
|
||||
}
|
||||
|
||||
for(jj=0; jj<p->pPhrase->nToken; jj++){
|
||||
p->pPhrase->aToken[jj].z = zBuf;
|
||||
@ -728,6 +746,7 @@ exprparse_out:
|
||||
int sqlite3Fts3ExprParse(
|
||||
sqlite3_tokenizer *pTokenizer, /* Tokenizer module */
|
||||
char **azCol, /* Array of column names for fts3 table */
|
||||
int bFts4, /* True to allow FTS4-only syntax */
|
||||
int nCol, /* Number of entries in azCol[] */
|
||||
int iDefaultCol, /* Default column to query */
|
||||
const char *z, int n, /* Text of MATCH query */
|
||||
@ -741,6 +760,7 @@ int sqlite3Fts3ExprParse(
|
||||
sParse.nCol = nCol;
|
||||
sParse.iDefaultCol = iDefaultCol;
|
||||
sParse.nNest = 0;
|
||||
sParse.bFts4 = bFts4;
|
||||
if( z==0 ){
|
||||
*ppExpr = 0;
|
||||
return SQLITE_OK;
|
||||
@ -930,7 +950,7 @@ static void fts3ExprTest(
|
||||
}
|
||||
|
||||
rc = sqlite3Fts3ExprParse(
|
||||
pTokenizer, azCol, nCol, nCol, zExpr, nExpr, &pExpr
|
||||
pTokenizer, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr
|
||||
);
|
||||
if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){
|
||||
sqlite3_result_error(context, "Error parsing expression", -1);
|
||||
|
||||
@ -30,7 +30,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "sqlite3.h"
|
||||
#include "fts3_hash.h"
|
||||
|
||||
/*
|
||||
|
||||
@ -368,6 +368,7 @@ static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){
|
||||
int iFirst = 0;
|
||||
pPhrase->pList = pCsr;
|
||||
fts3GetDeltaPosition(&pCsr, &iFirst);
|
||||
assert( iFirst>=0 );
|
||||
pPhrase->pHead = pCsr;
|
||||
pPhrase->pTail = pCsr;
|
||||
pPhrase->iHead = iFirst;
|
||||
@ -848,7 +849,7 @@ static int fts3MatchinfoSelectDoctotal(
|
||||
|
||||
a = sqlite3_column_blob(pStmt, 0);
|
||||
a += sqlite3Fts3GetVarint(a, &nDoc);
|
||||
if( nDoc==0 ) return SQLITE_CORRUPT_VTAB;
|
||||
if( nDoc==0 ) return FTS_CORRUPT_VTAB;
|
||||
*pnDoc = (u32)nDoc;
|
||||
|
||||
if( paLen ) *paLen = a;
|
||||
@ -1409,7 +1410,7 @@ void sqlite3Fts3Offsets(
|
||||
|
||||
if( !pTerm ){
|
||||
/* All offsets for this column have been gathered. */
|
||||
break;
|
||||
rc = SQLITE_DONE;
|
||||
}else{
|
||||
assert( iCurrent<=iMinPos );
|
||||
if( 0==(0xFE&*pTerm->pList) ){
|
||||
@ -1426,8 +1427,8 @@ void sqlite3Fts3Offsets(
|
||||
"%d %d %d %d ", iCol, pTerm-sCtx.aTerm, iStart, iEnd-iStart
|
||||
);
|
||||
rc = fts3StringAppend(&res, aBuffer, -1);
|
||||
}else if( rc==SQLITE_DONE ){
|
||||
rc = SQLITE_CORRUPT_VTAB;
|
||||
}else if( rc==SQLITE_DONE && pTab->zContentTbl==0 ){
|
||||
rc = FTS_CORRUPT_VTAB;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,6 +19,8 @@
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
|
||||
/* Required so that the "ifdef SQLITE_ENABLE_FTS3" below works */
|
||||
#include "fts3Int.h"
|
||||
|
||||
@ -319,3 +321,4 @@ int Sqlitetestfts3_Init(Tcl_Interp *interp){
|
||||
);
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif /* ifdef SQLITE_TEST */
|
||||
|
||||
@ -23,12 +23,7 @@
|
||||
** * The FTS3 module is being built into the core of
|
||||
** SQLite (in which case SQLITE_ENABLE_FTS3 is defined).
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
#ifndef SQLITE_CORE
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#endif
|
||||
#include "fts3Int.h"
|
||||
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
@ -256,7 +256,7 @@ static int fts3SqlStmt(
|
||||
/* 4 */ "DELETE FROM %Q.'%q_segdir'",
|
||||
/* 5 */ "DELETE FROM %Q.'%q_docsize'",
|
||||
/* 6 */ "DELETE FROM %Q.'%q_stat'",
|
||||
/* 7 */ "SELECT %s FROM %Q.'%q_content' AS x WHERE rowid=?",
|
||||
/* 7 */ "SELECT %s WHERE rowid=?",
|
||||
/* 8 */ "SELECT (SELECT max(idx) FROM %Q.'%q_segdir' WHERE level = ?) + 1",
|
||||
/* 9 */ "INSERT INTO %Q.'%q_segments'(blockid, block) VALUES(?, ?)",
|
||||
/* 10 */ "SELECT coalesce((SELECT max(blockid) FROM %Q.'%q_segments') + 1, 1)",
|
||||
@ -298,7 +298,7 @@ static int fts3SqlStmt(
|
||||
if( eStmt==SQL_CONTENT_INSERT ){
|
||||
zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, p->zWriteExprlist);
|
||||
}else if( eStmt==SQL_SELECT_CONTENT_BY_ROWID ){
|
||||
zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist, p->zDb, p->zName);
|
||||
zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist);
|
||||
}else{
|
||||
zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName);
|
||||
}
|
||||
@ -341,7 +341,7 @@ static int fts3SelectDocsize(
|
||||
rc = sqlite3_step(pStmt);
|
||||
if( rc!=SQLITE_ROW || sqlite3_column_type(pStmt, 0)!=SQLITE_BLOB ){
|
||||
rc = sqlite3_reset(pStmt);
|
||||
if( rc==SQLITE_OK ) rc = SQLITE_CORRUPT_VTAB;
|
||||
if( rc==SQLITE_OK ) rc = FTS_CORRUPT_VTAB;
|
||||
pStmt = 0;
|
||||
}else{
|
||||
rc = SQLITE_OK;
|
||||
@ -409,17 +409,24 @@ static void fts3SqlExec(
|
||||
** not what users expect when they get SQLITE_LOCKED_SHAREDCACHE. It can
|
||||
** still happen if the user reads data directly from the %_segments or
|
||||
** %_segdir tables instead of going through FTS3 though.
|
||||
**
|
||||
** This reasoning does not apply to a content=xxx table.
|
||||
*/
|
||||
int sqlite3Fts3ReadLock(Fts3Table *p){
|
||||
int rc; /* Return code */
|
||||
sqlite3_stmt *pStmt; /* Statement used to obtain lock */
|
||||
|
||||
rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pStmt, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_null(pStmt, 1);
|
||||
sqlite3_step(pStmt);
|
||||
rc = sqlite3_reset(pStmt);
|
||||
if( p->zContentTbl==0 ){
|
||||
rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pStmt, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_null(pStmt, 1);
|
||||
sqlite3_step(pStmt);
|
||||
rc = sqlite3_reset(pStmt);
|
||||
}
|
||||
}else{
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -780,6 +787,18 @@ static int fts3InsertData(
|
||||
int rc; /* Return code */
|
||||
sqlite3_stmt *pContentInsert; /* INSERT INTO %_content VALUES(...) */
|
||||
|
||||
if( p->zContentTbl ){
|
||||
sqlite3_value *pRowid = apVal[p->nColumn+3];
|
||||
if( sqlite3_value_type(pRowid)==SQLITE_NULL ){
|
||||
pRowid = apVal[1];
|
||||
}
|
||||
if( sqlite3_value_type(pRowid)!=SQLITE_INTEGER ){
|
||||
return SQLITE_CONSTRAINT;
|
||||
}
|
||||
*piDocid = sqlite3_value_int64(pRowid);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Locate the statement handle used to insert data into the %_content
|
||||
** table. The SQL for this statement is:
|
||||
**
|
||||
@ -830,14 +849,16 @@ static int fts3InsertData(
|
||||
** Remove all data from the FTS3 table. Clear the hash table containing
|
||||
** pending terms.
|
||||
*/
|
||||
static int fts3DeleteAll(Fts3Table *p){
|
||||
static int fts3DeleteAll(Fts3Table *p, int bContent){
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
|
||||
/* Discard the contents of the pending-terms hash table. */
|
||||
sqlite3Fts3PendingTermsClear(p);
|
||||
|
||||
/* Delete everything from the %_content, %_segments and %_segdir tables. */
|
||||
fts3SqlExec(&rc, p, SQL_DELETE_ALL_CONTENT, 0);
|
||||
/* Delete everything from the shadow tables. Except, leave %_content as
|
||||
** is if bContent is false. */
|
||||
assert( p->zContentTbl==0 || bContent==0 );
|
||||
if( bContent ) fts3SqlExec(&rc, p, SQL_DELETE_ALL_CONTENT, 0);
|
||||
fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGMENTS, 0);
|
||||
fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGDIR, 0);
|
||||
if( p->bHasDocsize ){
|
||||
@ -1145,7 +1166,7 @@ static int fts3SegReaderNext(
|
||||
if( nPrefix<0 || nSuffix<=0
|
||||
|| &pNext[nSuffix]>&pReader->aNode[pReader->nNode]
|
||||
){
|
||||
return SQLITE_CORRUPT_VTAB;
|
||||
return FTS_CORRUPT_VTAB;
|
||||
}
|
||||
|
||||
if( nPrefix+nSuffix>pReader->nTermAlloc ){
|
||||
@ -1175,7 +1196,7 @@ static int fts3SegReaderNext(
|
||||
if( &pReader->aDoclist[pReader->nDoclist]>&pReader->aNode[pReader->nNode]
|
||||
|| (pReader->nPopulate==0 && pReader->aDoclist[pReader->nDoclist-1])
|
||||
){
|
||||
return SQLITE_CORRUPT_VTAB;
|
||||
return FTS_CORRUPT_VTAB;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -2125,12 +2146,18 @@ static void fts3SegWriterFree(SegmentWriter *pWriter){
|
||||
static int fts3IsEmpty(Fts3Table *p, sqlite3_value *pRowid, int *pisEmpty){
|
||||
sqlite3_stmt *pStmt;
|
||||
int rc;
|
||||
rc = fts3SqlStmt(p, SQL_IS_EMPTY, &pStmt, &pRowid);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
*pisEmpty = sqlite3_column_int(pStmt, 0);
|
||||
if( p->zContentTbl ){
|
||||
/* If using the content=xxx option, assume the table is never empty */
|
||||
*pisEmpty = 0;
|
||||
rc = SQLITE_OK;
|
||||
}else{
|
||||
rc = fts3SqlStmt(p, SQL_IS_EMPTY, &pStmt, &pRowid);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
*pisEmpty = sqlite3_column_int(pStmt, 0);
|
||||
}
|
||||
rc = sqlite3_reset(pStmt);
|
||||
}
|
||||
rc = sqlite3_reset(pStmt);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@ -2482,6 +2509,7 @@ int sqlite3Fts3SegReaderStep(
|
||||
int isColFilter = (pCsr->pFilter->flags & FTS3_SEGMENT_COLUMN_FILTER);
|
||||
int isPrefix = (pCsr->pFilter->flags & FTS3_SEGMENT_PREFIX);
|
||||
int isScan = (pCsr->pFilter->flags & FTS3_SEGMENT_SCAN);
|
||||
int isFirst = (pCsr->pFilter->flags & FTS3_SEGMENT_FIRST);
|
||||
|
||||
Fts3SegReader **apSegment = pCsr->apSegment;
|
||||
int nSegment = pCsr->nSegment;
|
||||
@ -2541,6 +2569,7 @@ int sqlite3Fts3SegReaderStep(
|
||||
assert( isIgnoreEmpty || (isRequirePos && !isColFilter) );
|
||||
if( nMerge==1
|
||||
&& !isIgnoreEmpty
|
||||
&& !isFirst
|
||||
&& (p->bDescIdx==0 || fts3SegReaderIsPending(apSegment[0])==0)
|
||||
){
|
||||
pCsr->nDoclist = apSegment[0]->nDoclist;
|
||||
@ -2606,12 +2635,24 @@ int sqlite3Fts3SegReaderStep(
|
||||
}
|
||||
pCsr->aBuffer = aNew;
|
||||
}
|
||||
nDoclist += sqlite3Fts3PutVarint(&pCsr->aBuffer[nDoclist], iDelta);
|
||||
iPrev = iDocid;
|
||||
if( isRequirePos ){
|
||||
memcpy(&pCsr->aBuffer[nDoclist], pList, nList);
|
||||
nDoclist += nList;
|
||||
pCsr->aBuffer[nDoclist++] = '\0';
|
||||
|
||||
if( isFirst ){
|
||||
char *a = &pCsr->aBuffer[nDoclist];
|
||||
int nWrite;
|
||||
|
||||
nWrite = sqlite3Fts3FirstFilter(iDelta, pList, nList, a);
|
||||
if( nWrite ){
|
||||
iPrev = iDocid;
|
||||
nDoclist += nWrite;
|
||||
}
|
||||
}else{
|
||||
nDoclist += sqlite3Fts3PutVarint(&pCsr->aBuffer[nDoclist], iDelta);
|
||||
iPrev = iDocid;
|
||||
if( isRequirePos ){
|
||||
memcpy(&pCsr->aBuffer[nDoclist], pList, nList);
|
||||
nDoclist += nList;
|
||||
pCsr->aBuffer[nDoclist++] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2787,9 +2828,9 @@ static void fts3DecodeIntArray(
|
||||
** a blob of varints.
|
||||
*/
|
||||
static void fts3InsertDocsize(
|
||||
int *pRC, /* Result code */
|
||||
Fts3Table *p, /* Table into which to insert */
|
||||
u32 *aSz /* Sizes of each column */
|
||||
int *pRC, /* Result code */
|
||||
Fts3Table *p, /* Table into which to insert */
|
||||
u32 *aSz /* Sizes of each column, in tokens */
|
||||
){
|
||||
char *pBlob; /* The BLOB encoding of the document size */
|
||||
int nBlob; /* Number of bytes in the BLOB */
|
||||
@ -2911,6 +2952,86 @@ static int fts3DoOptimize(Fts3Table *p, int bReturnDone){
|
||||
return (rc==SQLITE_OK && bReturnDone && bSeenDone) ? SQLITE_DONE : rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called when the user executes the following statement:
|
||||
**
|
||||
** INSERT INTO <tbl>(<tbl>) VALUES('rebuild');
|
||||
**
|
||||
** The entire FTS index is discarded and rebuilt. If the table is one
|
||||
** created using the content=xxx option, then the new index is based on
|
||||
** the current contents of the xxx table. Otherwise, it is rebuilt based
|
||||
** on the contents of the %_content table.
|
||||
*/
|
||||
static int fts3DoRebuild(Fts3Table *p){
|
||||
int rc; /* Return Code */
|
||||
|
||||
rc = fts3DeleteAll(p, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
u32 *aSz = 0;
|
||||
u32 *aSzIns = 0;
|
||||
u32 *aSzDel = 0;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
int nEntry = 0;
|
||||
|
||||
/* Compose and prepare an SQL statement to loop through the content table */
|
||||
char *zSql = sqlite3_mprintf("SELECT %s" , p->zReadExprlist);
|
||||
if( !zSql ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
int nByte = sizeof(u32) * (p->nColumn+1)*3;
|
||||
aSz = (u32 *)sqlite3_malloc(nByte);
|
||||
if( aSz==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
memset(aSz, 0, nByte);
|
||||
aSzIns = &aSz[p->nColumn+1];
|
||||
aSzDel = &aSzIns[p->nColumn+1];
|
||||
}
|
||||
}
|
||||
|
||||
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
int iCol;
|
||||
rc = fts3PendingTermsDocid(p, sqlite3_column_int64(pStmt, 0));
|
||||
aSz[p->nColumn] = 0;
|
||||
for(iCol=0; rc==SQLITE_OK && iCol<p->nColumn; iCol++){
|
||||
const char *z = (const char *) sqlite3_column_text(pStmt, iCol+1);
|
||||
rc = fts3PendingTermsAdd(p, z, iCol, &aSz[iCol]);
|
||||
aSz[p->nColumn] += sqlite3_column_bytes(pStmt, iCol+1);
|
||||
}
|
||||
if( p->bHasDocsize ){
|
||||
fts3InsertDocsize(&rc, p, aSz);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3_finalize(pStmt);
|
||||
pStmt = 0;
|
||||
}else{
|
||||
nEntry++;
|
||||
for(iCol=0; iCol<=p->nColumn; iCol++){
|
||||
aSzIns[iCol] += aSz[iCol];
|
||||
}
|
||||
}
|
||||
}
|
||||
if( p->bHasStat ){
|
||||
fts3UpdateDocTotals(&rc, p, aSzIns, aSzDel, nEntry);
|
||||
}
|
||||
sqlite3_free(aSz);
|
||||
|
||||
if( pStmt ){
|
||||
int rc2 = sqlite3_finalize(pStmt);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = rc2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Handle a 'special' INSERT of the form:
|
||||
**
|
||||
@ -2928,6 +3049,8 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
|
||||
return SQLITE_NOMEM;
|
||||
}else if( nVal==8 && 0==sqlite3_strnicmp(zVal, "optimize", 8) ){
|
||||
rc = fts3DoOptimize(p, 0);
|
||||
}else if( nVal==7 && 0==sqlite3_strnicmp(zVal, "rebuild", 7) ){
|
||||
rc = fts3DoRebuild(p);
|
||||
#ifdef SQLITE_TEST
|
||||
}else if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){
|
||||
p->nNodeSize = atoi(&zVal[9]);
|
||||
@ -3008,6 +3131,7 @@ int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *pCsr){
|
||||
for(pDef=pCsr->pDeferred; pDef && rc==SQLITE_OK; pDef=pDef->pNext){
|
||||
Fts3PhraseToken *pPT = pDef->pToken;
|
||||
if( (pDef->iCol>=p->nColumn || pDef->iCol==i)
|
||||
&& (pPT->bFirst==0 || iPos==0)
|
||||
&& (pPT->n==nToken || (pPT->isPrefix && pPT->n<nToken))
|
||||
&& (0==memcmp(zToken, pPT->z, pPT->n))
|
||||
){
|
||||
@ -3099,14 +3223,18 @@ static int fts3DeleteByRowid(
|
||||
/* Deleting this row means the whole table is empty. In this case
|
||||
** delete the contents of all three tables and throw away any
|
||||
** data in the pendingTerms hash table. */
|
||||
rc = fts3DeleteAll(p);
|
||||
rc = fts3DeleteAll(p, 1);
|
||||
*pnDoc = *pnDoc - 1;
|
||||
}else{
|
||||
sqlite3_int64 iRemove = sqlite3_value_int64(pRowid);
|
||||
rc = fts3PendingTermsDocid(p, iRemove);
|
||||
fts3DeleteTerms(&rc, p, pRowid, aSzDel);
|
||||
fts3SqlExec(&rc, p, SQL_DELETE_CONTENT, &pRowid);
|
||||
if( sqlite3_changes(p->db) ) *pnDoc = *pnDoc - 1;
|
||||
if( p->zContentTbl==0 ){
|
||||
fts3SqlExec(&rc, p, SQL_DELETE_CONTENT, &pRowid);
|
||||
if( sqlite3_changes(p->db) ) *pnDoc = *pnDoc - 1;
|
||||
}else{
|
||||
*pnDoc = *pnDoc - 1;
|
||||
}
|
||||
if( p->bHasDocsize ){
|
||||
fts3SqlExec(&rc, p, SQL_DELETE_DOCSIZE, &pRowid);
|
||||
}
|
||||
@ -3129,7 +3257,6 @@ int sqlite3Fts3UpdateMethod(
|
||||
Fts3Table *p = (Fts3Table *)pVtab;
|
||||
int rc = SQLITE_OK; /* Return Code */
|
||||
int isRemove = 0; /* True for an UPDATE or DELETE */
|
||||
sqlite3_int64 iRemove = 0; /* Rowid removed by UPDATE or DELETE */
|
||||
u32 *aSzIns = 0; /* Sizes of inserted documents */
|
||||
u32 *aSzDel; /* Sizes of deleted documents */
|
||||
int nChng = 0; /* Net change in number of documents */
|
||||
@ -3167,7 +3294,7 @@ int sqlite3Fts3UpdateMethod(
|
||||
** detect the conflict and return SQLITE_CONSTRAINT before beginning to
|
||||
** modify the database file.
|
||||
*/
|
||||
if( nArg>1 ){
|
||||
if( nArg>1 && p->zContentTbl==0 ){
|
||||
/* Find the value object that holds the new rowid value. */
|
||||
sqlite3_value *pNewRowid = apVal[3+p->nColumn];
|
||||
if( sqlite3_value_type(pNewRowid)==SQLITE_NULL ){
|
||||
@ -3212,19 +3339,21 @@ int sqlite3Fts3UpdateMethod(
|
||||
assert( sqlite3_value_type(apVal[0])==SQLITE_INTEGER );
|
||||
rc = fts3DeleteByRowid(p, apVal[0], &nChng, aSzDel);
|
||||
isRemove = 1;
|
||||
iRemove = sqlite3_value_int64(apVal[0]);
|
||||
}
|
||||
|
||||
/* If this is an INSERT or UPDATE operation, insert the new record. */
|
||||
if( nArg>1 && rc==SQLITE_OK ){
|
||||
if( bInsertDone==0 ){
|
||||
rc = fts3InsertData(p, apVal, pRowid);
|
||||
if( rc==SQLITE_CONSTRAINT ) rc = SQLITE_CORRUPT_VTAB;
|
||||
if( rc==SQLITE_CONSTRAINT && p->zContentTbl==0 ){
|
||||
rc = FTS_CORRUPT_VTAB;
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK && (!isRemove || *pRowid!=iRemove) ){
|
||||
if( rc==SQLITE_OK && (!isRemove || *pRowid!=p->iPrevDocid ) ){
|
||||
rc = fts3PendingTermsDocid(p, *pRowid);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
assert( p->iPrevDocid==*pRowid );
|
||||
rc = fts3InsertTerms(p, apVal, aSzIns);
|
||||
}
|
||||
if( p->bHasDocsize ){
|
||||
|
||||
@ -1268,7 +1268,8 @@ static int rtreeFilter(
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
memset(pCsr->aConstraint, 0, sizeof(RtreeConstraint)*argc);
|
||||
assert( (idxStr==0 && argc==0) || (int)strlen(idxStr)==argc*2 );
|
||||
assert( (idxStr==0 && argc==0)
|
||||
|| (idxStr && (int)strlen(idxStr)==argc*2) );
|
||||
for(ii=0; ii<argc; ii++){
|
||||
RtreeConstraint *p = &pCsr->aConstraint[ii];
|
||||
p->op = idxStr[ii*2];
|
||||
@ -1569,7 +1570,10 @@ static int ChooseLeaf(
|
||||
|
||||
float fMinGrowth = 0.0;
|
||||
float fMinArea = 0.0;
|
||||
#if VARIANT_RSTARTREE_CHOOSESUBTREE
|
||||
float fMinOverlap = 0.0;
|
||||
float overlap;
|
||||
#endif
|
||||
|
||||
int nCell = NCELL(pNode);
|
||||
RtreeCell cell;
|
||||
@ -1601,7 +1605,6 @@ static int ChooseLeaf(
|
||||
int bBest = 0;
|
||||
float growth;
|
||||
float area;
|
||||
float overlap = 0.0;
|
||||
nodeGetCell(pRtree, pNode, iCell, &cell);
|
||||
growth = cellGrowth(pRtree, &cell, pCell);
|
||||
area = cellArea(pRtree, &cell);
|
||||
@ -1609,6 +1612,8 @@ static int ChooseLeaf(
|
||||
#if VARIANT_RSTARTREE_CHOOSESUBTREE
|
||||
if( ii==(pRtree->iDepth-1) ){
|
||||
overlap = cellOverlapEnlargement(pRtree,&cell,pCell,aCell,nCell,iCell);
|
||||
}else{
|
||||
overlap = 0.0;
|
||||
}
|
||||
if( (iCell==0)
|
||||
|| (overlap<fMinOverlap)
|
||||
@ -1616,6 +1621,7 @@ static int ChooseLeaf(
|
||||
|| (overlap==fMinOverlap && growth==fMinGrowth && area<fMinArea)
|
||||
){
|
||||
bBest = 1;
|
||||
fMinOverlap = overlap;
|
||||
}
|
||||
#else
|
||||
if( iCell==0||growth<fMinGrowth||(growth==fMinGrowth && area<fMinArea) ){
|
||||
@ -1623,7 +1629,6 @@ static int ChooseLeaf(
|
||||
}
|
||||
#endif
|
||||
if( bBest ){
|
||||
fMinOverlap = overlap;
|
||||
fMinGrowth = growth;
|
||||
fMinArea = area;
|
||||
iBest = cell.iRowid;
|
||||
|
||||
58
main.mk
58
main.mk
@ -65,8 +65,8 @@ LIBOBJ+= alter.o analyze.o attach.o auth.o \
|
||||
random.o resolve.o rowset.o rtree.o select.o status.o \
|
||||
table.o tokenize.o trigger.o \
|
||||
update.o util.o vacuum.o \
|
||||
vdbe.o vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbetrace.o \
|
||||
wal.o walker.o where.o utf.o vtab.o
|
||||
vdbe.o vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbesort.o \
|
||||
vdbetrace.o wal.o walker.o where.o utf.o vtab.o
|
||||
|
||||
|
||||
|
||||
@ -155,6 +155,7 @@ SRC = \
|
||||
$(TOP)/src/vdbeaux.c \
|
||||
$(TOP)/src/vdbeblob.c \
|
||||
$(TOP)/src/vdbemem.c \
|
||||
$(TOP)/src/vdbesort.c \
|
||||
$(TOP)/src/vdbetrace.c \
|
||||
$(TOP)/src/vdbeInt.h \
|
||||
$(TOP)/src/vtab.c \
|
||||
@ -361,6 +362,9 @@ sqlite3$(EXE): $(TOP)/src/shell.c libsqlite3.a sqlite3.h
|
||||
$(TOP)/src/shell.c \
|
||||
libsqlite3.a $(LIBREADLINE) $(TLIBS) $(THREADLIB)
|
||||
|
||||
sqlite3.o: sqlite3.c
|
||||
$(TCCX) -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
|
||||
# build on the target system. Some of the C source code and header
|
||||
@ -383,6 +387,17 @@ sqlite3.c: target_source $(TOP)/tool/mksqlite3c.tcl
|
||||
echo '#endif /* USE_SYSTEM_SQLITE */' >>tclsqlite3.c
|
||||
cat $(TOP)/src/tclsqlite.c >>tclsqlite3.c
|
||||
|
||||
sqlite3.c-debug: target_source $(TOP)/tool/mksqlite3c.tcl
|
||||
tclsh $(TOP)/tool/mksqlite3c.tcl --linemacros
|
||||
echo '#ifndef USE_SYSTEM_SQLITE' >tclsqlite3.c
|
||||
cat sqlite3.c >>tclsqlite3.c
|
||||
echo '#endif /* USE_SYSTEM_SQLITE */' >>tclsqlite3.c
|
||||
echo '#line 1 "tclsqlite.c"' >>tclsqlite3.c
|
||||
cat $(TOP)/src/tclsqlite.c >>tclsqlite3.c
|
||||
|
||||
sqlite3-all.c: sqlite3.c $(TOP)/tool/split-sqlite3c.tcl
|
||||
tclsh $(TOP)/tool/split-sqlite3c.tcl
|
||||
|
||||
fts2amal.c: target_source $(TOP)/ext/fts2/mkfts2amal.tcl
|
||||
tclsh $(TOP)/ext/fts2/mkfts2amal.tcl
|
||||
|
||||
@ -417,7 +432,7 @@ tclsqlite.o: $(TOP)/src/tclsqlite.c $(HDR)
|
||||
# Rules to build opcodes.c and opcodes.h
|
||||
#
|
||||
opcodes.c: opcodes.h $(TOP)/mkopcodec.awk
|
||||
sort -n -b -k 3 opcodes.h | $(NAWK) -f $(TOP)/mkopcodec.awk >opcodes.c
|
||||
$(NAWK) -f $(TOP)/mkopcodec.awk opcodes.h >opcodes.c
|
||||
|
||||
opcodes.h: parse.h $(TOP)/src/vdbe.c $(TOP)/mkopcodeh.awk
|
||||
cat parse.h $(TOP)/src/vdbe.c | \
|
||||
@ -506,6 +521,16 @@ tclsqlite3: $(TOP)/src/tclsqlite.c libsqlite3.a
|
||||
$(TCCX) $(TCL_FLAGS) -DTCLSH=1 -o tclsqlite3 \
|
||||
$(TOP)/src/tclsqlite.c libsqlite3.a $(LIBTCL) $(THREADLIB)
|
||||
|
||||
sqlite3_analyzer.c: sqlite3.c $(TOP)/src/test_stat.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl
|
||||
echo "#define TCLSH 2" > $@
|
||||
cat sqlite3.c $(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 >> $@
|
||||
echo "; return zMainloop; }" >> $@
|
||||
|
||||
sqlite3_analyzer$(EXE): sqlite3_analyzer.c
|
||||
$(TCCX) $(TCL_FLAGS) sqlite3_analyzer.c -o $@ $(LIBTCL) $(THREADLIB)
|
||||
|
||||
# Rules to build the 'testfixture' application.
|
||||
#
|
||||
@ -548,16 +573,6 @@ threadtest3$(EXE): sqlite3.o $(TOP)/test/threadtest3.c $(TOP)/test/tt3_checkpoin
|
||||
threadtest: threadtest3$(EXE)
|
||||
./threadtest3$(EXE)
|
||||
|
||||
sqlite3_analyzer$(EXE): $(TOP)/src/tclsqlite.c sqlite3.c $(TESTSRC) \
|
||||
$(TOP)/tool/spaceanal.tcl
|
||||
$(NAWK) -f $(TOP)/tool/tostr.awk $(TOP)/tool/spaceanal.tcl \
|
||||
>spaceanal_tcl.h
|
||||
$(TCCX) $(TCL_FLAGS) -DTCLSH=2 $(TESTFIXTURE_FLAGS) \
|
||||
-DSQLITE_TEST=1 -DSQLITE_PRIVATE="" \
|
||||
$(TESTSRC) $(TOP)/src/tclsqlite.c sqlite3.c \
|
||||
-o sqlite3_analyzer$(EXE) \
|
||||
$(LIBTCL) $(THREADLIB)
|
||||
|
||||
TEST_EXTENSION = $(SHPREFIX)testloadext.$(SO)
|
||||
$(TEST_EXTENSION): $(TOP)/src/test_loadext.c
|
||||
$(MKSHLIB) $(TOP)/src/test_loadext.c -o $(TEST_EXTENSION)
|
||||
@ -565,6 +580,13 @@ $(TEST_EXTENSION): $(TOP)/src/test_loadext.c
|
||||
extensiontest: testfixture$(EXE) $(TEST_EXTENSION)
|
||||
./testfixture$(EXE) $(TOP)/test/loadext.test
|
||||
|
||||
# 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.
|
||||
#
|
||||
checksymbols: sqlite3.o
|
||||
nm -g --defined-only sqlite3.o | grep -v " sqlite3_" ; test $$? -ne 0
|
||||
|
||||
|
||||
# Standard install and cleanup targets
|
||||
#
|
||||
@ -574,10 +596,16 @@ install: sqlite3 libsqlite3.a sqlite3.h
|
||||
mv sqlite3.h /usr/include
|
||||
|
||||
clean:
|
||||
rm -f *.o sqlite3 libsqlite3.a sqlite3.h opcodes.*
|
||||
rm -f lemon lempar.c parse.* sqlite*.tar.gz mkkeywordhash keywordhash.h
|
||||
rm -f *.o sqlite3 sqlite3.exe libsqlite3.a sqlite3.h opcodes.*
|
||||
rm -f lemon lemon.exe lempar.c parse.* sqlite*.tar.gz
|
||||
rm -f mkkeywordhash mkkeywordhash.exe keywordhash.h
|
||||
rm -f $(PUBLISH)
|
||||
rm -f *.da *.bb *.bbg gmon.out
|
||||
rm -rf tsrc target_source
|
||||
rm -f testloadext.dll libtestloadext.so
|
||||
rm -f amalgamation-testfixture amalgamation-testfixture.exe
|
||||
rm -f fts3-testfixture fts3-testfixture.exe
|
||||
rm -f testfixture testfixture.exe
|
||||
rm -f threadtest3 threadtest3.exe
|
||||
rm -f sqlite3.c fts?amal.c tclsqlite3.c
|
||||
rm -f sqlite3_analyzer sqlite3_analyzer.exe sqlite3_analyzer.c
|
||||
|
||||
@ -1 +1 @@
|
||||
af0d91adf497f5f36ec3813f04235a6e195a605f
|
||||
c7c6050ef060877ebe77b41d959e9df13f8c9b5e
|
||||
|
||||
@ -17,13 +17,18 @@ BEGIN {
|
||||
print " || defined(SQLITE_DEBUG)"
|
||||
print "const char *sqlite3OpcodeName(int i){"
|
||||
print " static const char *const azName[] = { \"?\","
|
||||
mx = 0
|
||||
}
|
||||
/define OP_/ {
|
||||
sub("OP_","",$2)
|
||||
i++
|
||||
printf " /* %3d */ \"%s\",\n", $3, $2
|
||||
i = $3+0
|
||||
label[i] = $2
|
||||
if( mx<i ) mx = i
|
||||
}
|
||||
END {
|
||||
for(i=1; i<=mx; i++){
|
||||
printf " /* %3d */ \"%s\",\n", i, label[i]
|
||||
}
|
||||
print " };"
|
||||
print " return azName[i];"
|
||||
print "}"
|
||||
|
||||
761
src/analyze.c
761
src/analyze.c
@ -10,6 +10,108 @@
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains code associated with the ANALYZE command.
|
||||
**
|
||||
** The ANALYZE command gather statistics about the content of tables
|
||||
** and indices. These statistics are made available to the query planner
|
||||
** to help it make better decisions about how to perform queries.
|
||||
**
|
||||
** The following system tables are or have been supported:
|
||||
**
|
||||
** CREATE TABLE sqlite_stat1(tbl, idx, stat);
|
||||
** CREATE TABLE sqlite_stat2(tbl, idx, sampleno, sample);
|
||||
** CREATE TABLE sqlite_stat3(tbl, idx, nEq, nLt, nDLt, sample);
|
||||
**
|
||||
** Additional tables might be added in future releases of SQLite.
|
||||
** The sqlite_stat2 table is not created or used unless the SQLite version
|
||||
** is between 3.6.18 and 3.7.8, inclusive, and unless SQLite is compiled
|
||||
** with SQLITE_ENABLE_STAT2. The sqlite_stat2 table is deprecated.
|
||||
** The sqlite_stat2 table is superceded by sqlite_stat3, which is only
|
||||
** created and used by SQLite versions 3.7.9 and later and with
|
||||
** SQLITE_ENABLE_STAT3 defined. The fucntionality of sqlite_stat3
|
||||
** is a superset of sqlite_stat2.
|
||||
**
|
||||
** Format of sqlite_stat1:
|
||||
**
|
||||
** There is normally one row per index, with the index identified by the
|
||||
** name in the idx column. The tbl column is the name of the table to
|
||||
** which the index belongs. In each such row, the stat column will be
|
||||
** a string consisting of a list of integers. The first integer in this
|
||||
** list is the number of rows in the index and in the table. The second
|
||||
** integer is the average number of rows in the index that have the same
|
||||
** value in the first column of the index. The third integer is the average
|
||||
** number of rows in the index that have the same value for the first two
|
||||
** columns. The N-th integer (for N>1) is the average number of rows in
|
||||
** the index which have the same value for the first N-1 columns. For
|
||||
** a K-column index, there will be K+1 integers in the stat column. If
|
||||
** the index is unique, then the last integer will be 1.
|
||||
**
|
||||
** The list of integers in the stat column can optionally be followed
|
||||
** by the keyword "unordered". The "unordered" keyword, if it is present,
|
||||
** must be separated from the last integer by a single space. If the
|
||||
** "unordered" keyword is present, then the query planner assumes that
|
||||
** the index is unordered and will not use the index for a range query.
|
||||
**
|
||||
** If the sqlite_stat1.idx column is NULL, then the sqlite_stat1.stat
|
||||
** column contains a single integer which is the (estimated) number of
|
||||
** rows in the table identified by sqlite_stat1.tbl.
|
||||
**
|
||||
** Format of sqlite_stat2:
|
||||
**
|
||||
** The sqlite_stat2 is only created and is only used if SQLite is compiled
|
||||
** with SQLITE_ENABLE_STAT2 and if the SQLite version number is between
|
||||
** 3.6.18 and 3.7.8. The "stat2" table contains additional information
|
||||
** about the distribution of keys within an index. The index is identified by
|
||||
** the "idx" column and the "tbl" column is the name of the table to which
|
||||
** the index belongs. There are usually 10 rows in the sqlite_stat2
|
||||
** table for each index.
|
||||
**
|
||||
** The sqlite_stat2 entries for an index that have sampleno between 0 and 9
|
||||
** inclusive are samples of the left-most key value in the index taken at
|
||||
** evenly spaced points along the index. Let the number of samples be S
|
||||
** (10 in the standard build) and let C be the number of rows in the index.
|
||||
** Then the sampled rows are given by:
|
||||
**
|
||||
** rownumber = (i*C*2 + C)/(S*2)
|
||||
**
|
||||
** For i between 0 and S-1. Conceptually, the index space is divided into
|
||||
** S uniform buckets and the samples are the middle row from each bucket.
|
||||
**
|
||||
** The format for sqlite_stat2 is recorded here for legacy reference. This
|
||||
** version of SQLite does not support sqlite_stat2. It neither reads nor
|
||||
** writes the sqlite_stat2 table. This version of SQLite only supports
|
||||
** sqlite_stat3.
|
||||
**
|
||||
** Format for sqlite_stat3:
|
||||
**
|
||||
** The sqlite_stat3 is an enhancement to sqlite_stat2. A new name is
|
||||
** used to avoid compatibility problems.
|
||||
**
|
||||
** The format of the sqlite_stat3 table is similar to the format of
|
||||
** the sqlite_stat2 table. There are multiple entries for each index.
|
||||
** The idx column names the index and the tbl column is the table of the
|
||||
** index. If the idx and tbl columns are the same, then the sample is
|
||||
** of the INTEGER PRIMARY KEY. The sample column is a value taken from
|
||||
** the left-most column of the index. The nEq column is the approximate
|
||||
** number of entires in the index whose left-most column exactly matches
|
||||
** the sample. nLt is the approximate number of entires whose left-most
|
||||
** column is less than the sample. The nDLt column is the approximate
|
||||
** number of distinct left-most entries in the index that are less than
|
||||
** the sample.
|
||||
**
|
||||
** Future versions of SQLite might change to store a string containing
|
||||
** multiple integers values in the nDLt column of sqlite_stat3. The first
|
||||
** integer will be the number of prior index entires that are distinct in
|
||||
** the left-most column. The second integer will be the number of prior index
|
||||
** entries that are distinct in the first two columns. The third integer
|
||||
** will be the number of prior index entries that are distinct in the first
|
||||
** three columns. And so forth. With that extension, the nDLt field is
|
||||
** similar in function to the sqlite_stat1.stat field.
|
||||
**
|
||||
** There can be an arbitrary number of sqlite_stat3 entries per index.
|
||||
** The ANALYZE command will typically generate sqlite_stat3 tables
|
||||
** that contain between 10 and 40 samples which are distributed across
|
||||
** the key space, though not uniformly, and which include samples with
|
||||
** largest possible nEq values.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_ANALYZE
|
||||
#include "sqliteInt.h"
|
||||
@ -17,16 +119,16 @@
|
||||
/*
|
||||
** This routine generates code that opens the sqlite_stat1 table for
|
||||
** writing with cursor iStatCur. If the library was built with the
|
||||
** SQLITE_ENABLE_STAT2 macro defined, then the sqlite_stat2 table is
|
||||
** SQLITE_ENABLE_STAT3 macro defined, then the sqlite_stat3 table is
|
||||
** opened for writing using cursor (iStatCur+1)
|
||||
**
|
||||
** If the sqlite_stat1 tables does not previously exist, it is created.
|
||||
** Similarly, if the sqlite_stat2 table does not exist and the library
|
||||
** is compiled with SQLITE_ENABLE_STAT2 defined, it is created.
|
||||
** Similarly, if the sqlite_stat3 table does not exist and the library
|
||||
** is compiled with SQLITE_ENABLE_STAT3 defined, it is created.
|
||||
**
|
||||
** Argument zWhere may be a pointer to a buffer containing a table name,
|
||||
** or it may be a NULL pointer. If it is not NULL, then all entries in
|
||||
** the sqlite_stat1 and (if applicable) sqlite_stat2 tables associated
|
||||
** the sqlite_stat1 and (if applicable) sqlite_stat3 tables associated
|
||||
** with the named table are deleted. If zWhere==0, then code is generated
|
||||
** to delete all stat table entries.
|
||||
*/
|
||||
@ -42,8 +144,8 @@ static void openStatTable(
|
||||
const char *zCols;
|
||||
} aTable[] = {
|
||||
{ "sqlite_stat1", "tbl,idx,stat" },
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
{ "sqlite_stat2", "tbl,idx,sampleno,sample" },
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
{ "sqlite_stat3", "tbl,idx,neq,nlt,ndlt,sample" },
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -59,6 +161,9 @@ static void openStatTable(
|
||||
assert( sqlite3VdbeDb(v)==db );
|
||||
pDb = &db->aDb[iDb];
|
||||
|
||||
/* Create new statistic tables if they do not exist, or clear them
|
||||
** if they do already exist.
|
||||
*/
|
||||
for(i=0; i<ArraySize(aTable); i++){
|
||||
const char *zTab = aTable[i].zName;
|
||||
Table *pStat;
|
||||
@ -89,7 +194,7 @@ static void openStatTable(
|
||||
}
|
||||
}
|
||||
|
||||
/* Open the sqlite_stat[12] tables for writing. */
|
||||
/* Open the sqlite_stat[13] tables for writing. */
|
||||
for(i=0; i<ArraySize(aTable); i++){
|
||||
sqlite3VdbeAddOp3(v, OP_OpenWrite, iStatCur+i, aRoot[i], iDb);
|
||||
sqlite3VdbeChangeP4(v, -1, (char *)3, P4_INT32);
|
||||
@ -97,6 +202,226 @@ static void openStatTable(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Recommended number of samples for sqlite_stat3
|
||||
*/
|
||||
#ifndef SQLITE_STAT3_SAMPLES
|
||||
# define SQLITE_STAT3_SAMPLES 24
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Three SQL functions - stat3_init(), stat3_push(), and stat3_pop() -
|
||||
** share an instance of the following structure to hold their state
|
||||
** information.
|
||||
*/
|
||||
typedef struct Stat3Accum Stat3Accum;
|
||||
struct Stat3Accum {
|
||||
tRowcnt nRow; /* Number of rows in the entire table */
|
||||
tRowcnt nPSample; /* How often to do a periodic sample */
|
||||
int iMin; /* Index of entry with minimum nEq and hash */
|
||||
int mxSample; /* Maximum number of samples to accumulate */
|
||||
int nSample; /* Current number of samples */
|
||||
u32 iPrn; /* Pseudo-random number used for sampling */
|
||||
struct Stat3Sample {
|
||||
i64 iRowid; /* Rowid in main table of the key */
|
||||
tRowcnt nEq; /* sqlite_stat3.nEq */
|
||||
tRowcnt nLt; /* sqlite_stat3.nLt */
|
||||
tRowcnt nDLt; /* sqlite_stat3.nDLt */
|
||||
u8 isPSample; /* True if a periodic sample */
|
||||
u32 iHash; /* Tiebreaker hash */
|
||||
} *a; /* An array of samples */
|
||||
};
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
/*
|
||||
** Implementation of the stat3_init(C,S) SQL function. The two parameters
|
||||
** are the number of rows in the table or index (C) and the number of samples
|
||||
** to accumulate (S).
|
||||
**
|
||||
** This routine allocates the Stat3Accum object.
|
||||
**
|
||||
** The return value is the Stat3Accum object (P).
|
||||
*/
|
||||
static void stat3Init(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
Stat3Accum *p;
|
||||
tRowcnt nRow;
|
||||
int mxSample;
|
||||
int n;
|
||||
|
||||
UNUSED_PARAMETER(argc);
|
||||
nRow = (tRowcnt)sqlite3_value_int64(argv[0]);
|
||||
mxSample = sqlite3_value_int(argv[1]);
|
||||
n = sizeof(*p) + sizeof(p->a[0])*mxSample;
|
||||
p = sqlite3_malloc( n );
|
||||
if( p==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
return;
|
||||
}
|
||||
memset(p, 0, n);
|
||||
p->a = (struct Stat3Sample*)&p[1];
|
||||
p->nRow = nRow;
|
||||
p->mxSample = mxSample;
|
||||
p->nPSample = p->nRow/(mxSample/3+1) + 1;
|
||||
sqlite3_randomness(sizeof(p->iPrn), &p->iPrn);
|
||||
sqlite3_result_blob(context, p, sizeof(p), sqlite3_free);
|
||||
}
|
||||
static const FuncDef stat3InitFuncdef = {
|
||||
2, /* nArg */
|
||||
SQLITE_UTF8, /* iPrefEnc */
|
||||
0, /* flags */
|
||||
0, /* pUserData */
|
||||
0, /* pNext */
|
||||
stat3Init, /* xFunc */
|
||||
0, /* xStep */
|
||||
0, /* xFinalize */
|
||||
"stat3_init", /* zName */
|
||||
0, /* pHash */
|
||||
0 /* pDestructor */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** Implementation of the stat3_push(nEq,nLt,nDLt,rowid,P) SQL function. The
|
||||
** arguments describe a single key instance. This routine makes the
|
||||
** decision about whether or not to retain this key for the sqlite_stat3
|
||||
** table.
|
||||
**
|
||||
** The return value is NULL.
|
||||
*/
|
||||
static void stat3Push(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
Stat3Accum *p = (Stat3Accum*)sqlite3_value_blob(argv[4]);
|
||||
tRowcnt nEq = sqlite3_value_int64(argv[0]);
|
||||
tRowcnt nLt = sqlite3_value_int64(argv[1]);
|
||||
tRowcnt nDLt = sqlite3_value_int64(argv[2]);
|
||||
i64 rowid = sqlite3_value_int64(argv[3]);
|
||||
u8 isPSample = 0;
|
||||
u8 doInsert = 0;
|
||||
int iMin = p->iMin;
|
||||
struct Stat3Sample *pSample;
|
||||
int i;
|
||||
u32 h;
|
||||
|
||||
UNUSED_PARAMETER(context);
|
||||
UNUSED_PARAMETER(argc);
|
||||
if( nEq==0 ) return;
|
||||
h = p->iPrn = p->iPrn*1103515245 + 12345;
|
||||
if( (nLt/p->nPSample)!=((nEq+nLt)/p->nPSample) ){
|
||||
doInsert = isPSample = 1;
|
||||
}else if( p->nSample<p->mxSample ){
|
||||
doInsert = 1;
|
||||
}else{
|
||||
if( nEq>p->a[iMin].nEq || (nEq==p->a[iMin].nEq && h>p->a[iMin].iHash) ){
|
||||
doInsert = 1;
|
||||
}
|
||||
}
|
||||
if( !doInsert ) return;
|
||||
if( p->nSample==p->mxSample ){
|
||||
assert( p->nSample - iMin - 1 >= 0 );
|
||||
memmove(&p->a[iMin], &p->a[iMin+1], sizeof(p->a[0])*(p->nSample-iMin-1));
|
||||
pSample = &p->a[p->nSample-1];
|
||||
}else{
|
||||
pSample = &p->a[p->nSample++];
|
||||
}
|
||||
pSample->iRowid = rowid;
|
||||
pSample->nEq = nEq;
|
||||
pSample->nLt = nLt;
|
||||
pSample->nDLt = nDLt;
|
||||
pSample->iHash = h;
|
||||
pSample->isPSample = isPSample;
|
||||
|
||||
/* Find the new minimum */
|
||||
if( p->nSample==p->mxSample ){
|
||||
pSample = p->a;
|
||||
i = 0;
|
||||
while( pSample->isPSample ){
|
||||
i++;
|
||||
pSample++;
|
||||
assert( i<p->nSample );
|
||||
}
|
||||
nEq = pSample->nEq;
|
||||
h = pSample->iHash;
|
||||
iMin = i;
|
||||
for(i++, pSample++; i<p->nSample; i++, pSample++){
|
||||
if( pSample->isPSample ) continue;
|
||||
if( pSample->nEq<nEq
|
||||
|| (pSample->nEq==nEq && pSample->iHash<h)
|
||||
){
|
||||
iMin = i;
|
||||
nEq = pSample->nEq;
|
||||
h = pSample->iHash;
|
||||
}
|
||||
}
|
||||
p->iMin = iMin;
|
||||
}
|
||||
}
|
||||
static const FuncDef stat3PushFuncdef = {
|
||||
5, /* nArg */
|
||||
SQLITE_UTF8, /* iPrefEnc */
|
||||
0, /* flags */
|
||||
0, /* pUserData */
|
||||
0, /* pNext */
|
||||
stat3Push, /* xFunc */
|
||||
0, /* xStep */
|
||||
0, /* xFinalize */
|
||||
"stat3_push", /* zName */
|
||||
0, /* pHash */
|
||||
0 /* pDestructor */
|
||||
};
|
||||
|
||||
/*
|
||||
** Implementation of the stat3_get(P,N,...) SQL function. This routine is
|
||||
** used to query the results. Content is returned for the Nth sqlite_stat3
|
||||
** row where N is between 0 and S-1 and S is the number of samples. The
|
||||
** value returned depends on the number of arguments.
|
||||
**
|
||||
** argc==2 result: rowid
|
||||
** argc==3 result: nEq
|
||||
** argc==4 result: nLt
|
||||
** argc==5 result: nDLt
|
||||
*/
|
||||
static void stat3Get(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
int n = sqlite3_value_int(argv[1]);
|
||||
Stat3Accum *p = (Stat3Accum*)sqlite3_value_blob(argv[0]);
|
||||
|
||||
assert( p!=0 );
|
||||
if( p->nSample<=n ) return;
|
||||
switch( argc ){
|
||||
case 2: sqlite3_result_int64(context, p->a[n].iRowid); break;
|
||||
case 3: sqlite3_result_int64(context, p->a[n].nEq); break;
|
||||
case 4: sqlite3_result_int64(context, p->a[n].nLt); break;
|
||||
default: sqlite3_result_int64(context, p->a[n].nDLt); break;
|
||||
}
|
||||
}
|
||||
static const FuncDef stat3GetFuncdef = {
|
||||
-1, /* nArg */
|
||||
SQLITE_UTF8, /* iPrefEnc */
|
||||
0, /* flags */
|
||||
0, /* pUserData */
|
||||
0, /* pNext */
|
||||
stat3Get, /* xFunc */
|
||||
0, /* xStep */
|
||||
0, /* xFinalize */
|
||||
"stat3_get", /* zName */
|
||||
0, /* pHash */
|
||||
0 /* pDestructor */
|
||||
};
|
||||
#endif /* SQLITE_ENABLE_STAT3 */
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Generate code to do an analysis of all indices associated with
|
||||
** a single table.
|
||||
@ -119,20 +444,27 @@ static void analyzeOneTable(
|
||||
int iDb; /* Index of database containing pTab */
|
||||
int regTabname = iMem++; /* Register containing table name */
|
||||
int regIdxname = iMem++; /* Register containing index name */
|
||||
int regSampleno = iMem++; /* Register containing next sample number */
|
||||
int regCol = iMem++; /* Content of a column analyzed table */
|
||||
int regStat1 = iMem++; /* The stat column of sqlite_stat1 */
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
int regNumEq = regStat1; /* Number of instances. Same as regStat1 */
|
||||
int regNumLt = iMem++; /* Number of keys less than regSample */
|
||||
int regNumDLt = iMem++; /* Number of distinct keys less than regSample */
|
||||
int regSample = iMem++; /* The next sample value */
|
||||
int regRowid = regSample; /* Rowid of a sample */
|
||||
int regAccum = iMem++; /* Register to hold Stat3Accum object */
|
||||
int regLoop = iMem++; /* Loop counter */
|
||||
int regCount = iMem++; /* Number of rows in the table or index */
|
||||
int regTemp1 = iMem++; /* Intermediate register */
|
||||
int regTemp2 = iMem++; /* Intermediate register */
|
||||
int once = 1; /* One-time initialization */
|
||||
int shortJump = 0; /* Instruction address */
|
||||
int iTabCur = pParse->nTab++; /* Table cursor */
|
||||
#endif
|
||||
int regCol = iMem++; /* Content of a column in analyzed table */
|
||||
int regRec = iMem++; /* Register holding completed record */
|
||||
int regTemp = iMem++; /* Temporary use register */
|
||||
int regRowid = iMem++; /* Rowid for the inserted record */
|
||||
int regNewRowid = iMem++; /* Rowid for the inserted record */
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
int addr = 0; /* Instruction address */
|
||||
int regTemp2 = iMem++; /* Temporary use register */
|
||||
int regSamplerecno = iMem++; /* Index of next sample to record */
|
||||
int regRecno = iMem++; /* Current sample index */
|
||||
int regLast = iMem++; /* Index of last sample to record */
|
||||
int regFirst = iMem++; /* Index of first sample to record */
|
||||
#endif
|
||||
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
if( v==0 || NEVER(pTab==0) ){
|
||||
@ -165,9 +497,14 @@ static void analyzeOneTable(
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
int nCol;
|
||||
KeyInfo *pKey;
|
||||
int addrIfNot = 0; /* address of OP_IfNot */
|
||||
int *aChngAddr; /* Array of jump instruction addresses */
|
||||
|
||||
if( pOnlyIdx && pOnlyIdx!=pIdx ) continue;
|
||||
VdbeNoopComment((v, "Begin analysis of %s", pIdx->zName));
|
||||
nCol = pIdx->nColumn;
|
||||
aChngAddr = sqlite3DbMallocRaw(db, sizeof(int)*nCol);
|
||||
if( aChngAddr==0 ) continue;
|
||||
pKey = sqlite3IndexKeyinfo(pParse, pIdx);
|
||||
if( iMem+1+(nCol*2)>pParse->nMem ){
|
||||
pParse->nMem = iMem+1+(nCol*2);
|
||||
@ -182,31 +519,20 @@ static void analyzeOneTable(
|
||||
/* Populate the register containing the index name. */
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, regIdxname, 0, pIdx->zName, 0);
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
|
||||
/* If this iteration of the loop is generating code to analyze the
|
||||
** first index in the pTab->pIndex list, then register regLast has
|
||||
** not been populated. In this case populate it now. */
|
||||
if( pTab->pIndex==pIdx ){
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES, regSamplerecno);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES*2-1, regTemp);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES*2, regTemp2);
|
||||
|
||||
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regLast);
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, regFirst);
|
||||
addr = sqlite3VdbeAddOp3(v, OP_Lt, regSamplerecno, 0, regLast);
|
||||
sqlite3VdbeAddOp3(v, OP_Divide, regTemp2, regLast, regFirst);
|
||||
sqlite3VdbeAddOp3(v, OP_Multiply, regLast, regTemp, regLast);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regLast, SQLITE_INDEX_SAMPLES*2-2);
|
||||
sqlite3VdbeAddOp3(v, OP_Divide, regTemp2, regLast, regLast);
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
if( once ){
|
||||
once = 0;
|
||||
sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead);
|
||||
}
|
||||
|
||||
/* Zero the regSampleno and regRecno registers. */
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regSampleno);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRecno);
|
||||
sqlite3VdbeAddOp2(v, OP_Copy, regFirst, regSamplerecno);
|
||||
#endif
|
||||
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regCount);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_STAT3_SAMPLES, regTemp1);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regNumEq);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regNumLt);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, -1, regNumDLt);
|
||||
sqlite3VdbeAddOp4(v, OP_Function, 1, regCount, regAccum,
|
||||
(char*)&stat3InitFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, 2);
|
||||
#endif /* SQLITE_ENABLE_STAT3 */
|
||||
|
||||
/* The block of memory cells initialized here is used as follows.
|
||||
**
|
||||
@ -236,75 +562,83 @@ static void analyzeOneTable(
|
||||
endOfLoop = sqlite3VdbeMakeLabel(v);
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, iIdxCur, endOfLoop);
|
||||
topOfLoop = sqlite3VdbeCurrentAddr(v);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, iMem, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, iMem, 1); /* Increment row counter */
|
||||
|
||||
for(i=0; i<nCol; i++){
|
||||
CollSeq *pColl;
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regCol);
|
||||
if( i==0 ){
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
/* Check if the record that cursor iIdxCur points to contains a
|
||||
** value that should be stored in the sqlite_stat2 table. If so,
|
||||
** store it. */
|
||||
int ne = sqlite3VdbeAddOp3(v, OP_Ne, regRecno, 0, regSamplerecno);
|
||||
assert( regTabname+1==regIdxname
|
||||
&& regTabname+2==regSampleno
|
||||
&& regTabname+3==regCol
|
||||
);
|
||||
sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL);
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 4, regRec, "aaab", 0);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regRec, regRowid);
|
||||
|
||||
/* Calculate new values for regSamplerecno and regSampleno.
|
||||
**
|
||||
** sampleno = sampleno + 1
|
||||
** samplerecno = samplerecno+(remaining records)/(remaining samples)
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regSampleno, 1);
|
||||
sqlite3VdbeAddOp3(v, OP_Subtract, regRecno, regLast, regTemp);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regTemp, -1);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES, regTemp2);
|
||||
sqlite3VdbeAddOp3(v, OP_Subtract, regSampleno, regTemp2, regTemp2);
|
||||
sqlite3VdbeAddOp3(v, OP_Divide, regTemp2, regTemp, regTemp);
|
||||
sqlite3VdbeAddOp3(v, OP_Add, regSamplerecno, regTemp, regSamplerecno);
|
||||
|
||||
sqlite3VdbeJumpHere(v, ne);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regRecno, 1);
|
||||
#endif
|
||||
|
||||
/* Always record the very first row */
|
||||
sqlite3VdbeAddOp1(v, OP_IfNot, iMem+1);
|
||||
addrIfNot = sqlite3VdbeAddOp1(v, OP_IfNot, iMem+1);
|
||||
}
|
||||
assert( pIdx->azColl!=0 );
|
||||
assert( pIdx->azColl[i]!=0 );
|
||||
pColl = sqlite3LocateCollSeq(pParse, pIdx->azColl[i]);
|
||||
sqlite3VdbeAddOp4(v, OP_Ne, regCol, 0, iMem+nCol+i+1,
|
||||
(char*)pColl, P4_COLLSEQ);
|
||||
aChngAddr[i] = sqlite3VdbeAddOp4(v, OP_Ne, regCol, 0, iMem+nCol+i+1,
|
||||
(char*)pColl, P4_COLLSEQ);
|
||||
sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
|
||||
}
|
||||
if( db->mallocFailed ){
|
||||
/* If a malloc failure has occurred, then the result of the expression
|
||||
** passed as the second argument to the call to sqlite3VdbeJumpHere()
|
||||
** below may be negative. Which causes an assert() to fail (or an
|
||||
** out-of-bounds write if SQLITE_DEBUG is not defined). */
|
||||
return;
|
||||
VdbeComment((v, "jump if column %d changed", i));
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
if( i==0 ){
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regNumEq, 1);
|
||||
VdbeComment((v, "incr repeat count"));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, endOfLoop);
|
||||
for(i=0; i<nCol; i++){
|
||||
int addr2 = sqlite3VdbeCurrentAddr(v) - (nCol*2);
|
||||
sqlite3VdbeJumpHere(v, aChngAddr[i]); /* Set jump dest for the OP_Ne */
|
||||
if( i==0 ){
|
||||
sqlite3VdbeJumpHere(v, addr2-1); /* Set jump dest for the OP_IfNot */
|
||||
sqlite3VdbeJumpHere(v, addrIfNot); /* Jump dest for OP_IfNot */
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
sqlite3VdbeAddOp4(v, OP_Function, 1, regNumEq, regTemp2,
|
||||
(char*)&stat3PushFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, 5);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, pIdx->nColumn, regRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Add, regNumEq, regNumLt, regNumLt);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regNumDLt, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 1, regNumEq);
|
||||
#endif
|
||||
}
|
||||
sqlite3VdbeJumpHere(v, addr2); /* Set jump dest for the OP_Ne */
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, iMem+i+1, 1);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, iMem+nCol+i+1);
|
||||
}
|
||||
sqlite3DbFree(db, aChngAddr);
|
||||
|
||||
/* End of the analysis loop. */
|
||||
/* Always jump here after updating the iMem+1...iMem+1+nCol counters */
|
||||
sqlite3VdbeResolveLabel(v, endOfLoop);
|
||||
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, topOfLoop);
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iIdxCur);
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
sqlite3VdbeAddOp4(v, OP_Function, 1, regNumEq, regTemp2,
|
||||
(char*)&stat3PushFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, 5);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, -1, regLoop);
|
||||
shortJump =
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regLoop, 1);
|
||||
sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regTemp1,
|
||||
(char*)&stat3GetFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, 2);
|
||||
sqlite3VdbeAddOp1(v, OP_IsNull, regTemp1);
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iTabCur, shortJump, regTemp1);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iTabCur, pIdx->aiColumn[0], regSample);
|
||||
sqlite3ColumnDefault(v, pTab, pIdx->aiColumn[0], regSample);
|
||||
sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regNumEq,
|
||||
(char*)&stat3GetFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, 3);
|
||||
sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regNumLt,
|
||||
(char*)&stat3GetFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, 4);
|
||||
sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regNumDLt,
|
||||
(char*)&stat3GetFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, 5);
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 6, regRec, "bbbbbb", 0);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regNewRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regRec, regNewRowid);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, shortJump);
|
||||
sqlite3VdbeJumpHere(v, shortJump+2);
|
||||
#endif
|
||||
|
||||
/* Store the results in sqlite_stat1.
|
||||
**
|
||||
@ -324,22 +658,22 @@ static void analyzeOneTable(
|
||||
** If K>0 then it is always the case the D>0 so division by zero
|
||||
** is never possible.
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regSampleno);
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regStat1);
|
||||
if( jZeroRows<0 ){
|
||||
jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, iMem);
|
||||
}
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, regTemp, 0, " ", 0);
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regSampleno, regSampleno);
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regStat1, regStat1);
|
||||
sqlite3VdbeAddOp3(v, OP_Add, iMem, iMem+i+1, regTemp);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regTemp, -1);
|
||||
sqlite3VdbeAddOp3(v, OP_Divide, iMem+i+1, regTemp, regTemp);
|
||||
sqlite3VdbeAddOp1(v, OP_ToInt, regTemp);
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regSampleno, regSampleno);
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regStat1, regStat1);
|
||||
}
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regRowid);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regNewRowid);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
|
||||
}
|
||||
|
||||
@ -349,22 +683,23 @@ static void analyzeOneTable(
|
||||
if( pTab->pIndex==0 ){
|
||||
sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pTab->tnum, iDb);
|
||||
VdbeComment((v, "%s", pTab->zName));
|
||||
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regSampleno);
|
||||
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat1);
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iIdxCur);
|
||||
jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regSampleno);
|
||||
jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1);
|
||||
}else{
|
||||
sqlite3VdbeJumpHere(v, jZeroRows);
|
||||
jZeroRows = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname);
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regRowid);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regNewRowid);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
|
||||
if( pParse->nMem<regRec ) pParse->nMem = regRec;
|
||||
sqlite3VdbeJumpHere(v, jZeroRows);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Generate code that will cause the most recent index analysis to
|
||||
** be loaded into internal hash tables where is can be used.
|
||||
@ -388,7 +723,7 @@ static void analyzeDatabase(Parse *pParse, int iDb){
|
||||
|
||||
sqlite3BeginWriteOperation(pParse, 0, iDb);
|
||||
iStatCur = pParse->nTab;
|
||||
pParse->nTab += 2;
|
||||
pParse->nTab += 3;
|
||||
openStatTable(pParse, iDb, iStatCur, 0, 0);
|
||||
iMem = pParse->nMem+1;
|
||||
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
|
||||
@ -413,7 +748,7 @@ static void analyzeTable(Parse *pParse, Table *pTab, Index *pOnlyIdx){
|
||||
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
|
||||
sqlite3BeginWriteOperation(pParse, 0, iDb);
|
||||
iStatCur = pParse->nTab;
|
||||
pParse->nTab += 2;
|
||||
pParse->nTab += 3;
|
||||
if( pOnlyIdx ){
|
||||
openStatTable(pParse, iDb, iStatCur, pOnlyIdx->zName, "idx");
|
||||
}else{
|
||||
@ -518,7 +853,7 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
|
||||
Index *pIndex;
|
||||
Table *pTable;
|
||||
int i, c, n;
|
||||
unsigned int v;
|
||||
tRowcnt v;
|
||||
const char *z;
|
||||
|
||||
assert( argc==3 );
|
||||
@ -561,10 +896,10 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
|
||||
** and its contents.
|
||||
*/
|
||||
void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
if( pIdx->aSample ){
|
||||
int j;
|
||||
for(j=0; j<SQLITE_INDEX_SAMPLES; j++){
|
||||
for(j=0; j<pIdx->nSample; j++){
|
||||
IndexSample *p = &pIdx->aSample[j];
|
||||
if( p->eType==SQLITE_TEXT || p->eType==SQLITE_BLOB ){
|
||||
sqlite3DbFree(db, p->u.z);
|
||||
@ -572,25 +907,157 @@ void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){
|
||||
}
|
||||
sqlite3DbFree(db, pIdx->aSample);
|
||||
}
|
||||
if( db && db->pnBytesFreed==0 ){
|
||||
pIdx->nSample = 0;
|
||||
pIdx->aSample = 0;
|
||||
}
|
||||
#else
|
||||
UNUSED_PARAMETER(db);
|
||||
UNUSED_PARAMETER(pIdx);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
/*
|
||||
** Load the content of the sqlite_stat1 and sqlite_stat2 tables. The
|
||||
** Load content from the sqlite_stat3 table into the Index.aSample[]
|
||||
** arrays of all indices.
|
||||
*/
|
||||
static int loadStat3(sqlite3 *db, const char *zDb){
|
||||
int rc; /* Result codes from subroutines */
|
||||
sqlite3_stmt *pStmt = 0; /* An SQL statement being run */
|
||||
char *zSql; /* Text of the SQL statement */
|
||||
Index *pPrevIdx = 0; /* Previous index in the loop */
|
||||
int idx = 0; /* slot in pIdx->aSample[] for next sample */
|
||||
int eType; /* Datatype of a sample */
|
||||
IndexSample *pSample; /* A slot in pIdx->aSample[] */
|
||||
|
||||
if( !sqlite3FindTable(db, "sqlite_stat3", zDb) ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
zSql = sqlite3MPrintf(db,
|
||||
"SELECT idx,count(*) FROM %Q.sqlite_stat3"
|
||||
" GROUP BY idx", zDb);
|
||||
if( !zSql ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
|
||||
sqlite3DbFree(db, zSql);
|
||||
if( rc ) return rc;
|
||||
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
char *zIndex; /* Index name */
|
||||
Index *pIdx; /* Pointer to the index object */
|
||||
int nSample; /* Number of samples */
|
||||
|
||||
zIndex = (char *)sqlite3_column_text(pStmt, 0);
|
||||
if( zIndex==0 ) continue;
|
||||
nSample = sqlite3_column_int(pStmt, 1);
|
||||
pIdx = sqlite3FindIndex(db, zIndex, zDb);
|
||||
if( pIdx==0 ) continue;
|
||||
assert( pIdx->nSample==0 );
|
||||
pIdx->nSample = nSample;
|
||||
pIdx->aSample = sqlite3MallocZero( nSample*sizeof(IndexSample) );
|
||||
pIdx->avgEq = pIdx->aiRowEst[1];
|
||||
if( pIdx->aSample==0 ){
|
||||
db->mallocFailed = 1;
|
||||
sqlite3_finalize(pStmt);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
}
|
||||
rc = sqlite3_finalize(pStmt);
|
||||
if( rc ) return rc;
|
||||
|
||||
zSql = sqlite3MPrintf(db,
|
||||
"SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat3", zDb);
|
||||
if( !zSql ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
|
||||
sqlite3DbFree(db, zSql);
|
||||
if( rc ) return rc;
|
||||
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
char *zIndex; /* Index name */
|
||||
Index *pIdx; /* Pointer to the index object */
|
||||
int i; /* Loop counter */
|
||||
tRowcnt sumEq; /* Sum of the nEq values */
|
||||
|
||||
zIndex = (char *)sqlite3_column_text(pStmt, 0);
|
||||
if( zIndex==0 ) continue;
|
||||
pIdx = sqlite3FindIndex(db, zIndex, zDb);
|
||||
if( pIdx==0 ) continue;
|
||||
if( pIdx==pPrevIdx ){
|
||||
idx++;
|
||||
}else{
|
||||
pPrevIdx = pIdx;
|
||||
idx = 0;
|
||||
}
|
||||
assert( idx<pIdx->nSample );
|
||||
pSample = &pIdx->aSample[idx];
|
||||
pSample->nEq = (tRowcnt)sqlite3_column_int64(pStmt, 1);
|
||||
pSample->nLt = (tRowcnt)sqlite3_column_int64(pStmt, 2);
|
||||
pSample->nDLt = (tRowcnt)sqlite3_column_int64(pStmt, 3);
|
||||
if( idx==pIdx->nSample-1 ){
|
||||
if( pSample->nDLt>0 ){
|
||||
for(i=0, sumEq=0; i<=idx-1; i++) sumEq += pIdx->aSample[i].nEq;
|
||||
pIdx->avgEq = (pSample->nLt - sumEq)/pSample->nDLt;
|
||||
}
|
||||
if( pIdx->avgEq<=0 ) pIdx->avgEq = 1;
|
||||
}
|
||||
eType = sqlite3_column_type(pStmt, 4);
|
||||
pSample->eType = (u8)eType;
|
||||
switch( eType ){
|
||||
case SQLITE_INTEGER: {
|
||||
pSample->u.i = sqlite3_column_int64(pStmt, 4);
|
||||
break;
|
||||
}
|
||||
case SQLITE_FLOAT: {
|
||||
pSample->u.r = sqlite3_column_double(pStmt, 4);
|
||||
break;
|
||||
}
|
||||
case SQLITE_NULL: {
|
||||
break;
|
||||
}
|
||||
default: assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB ); {
|
||||
const char *z = (const char *)(
|
||||
(eType==SQLITE_BLOB) ?
|
||||
sqlite3_column_blob(pStmt, 4):
|
||||
sqlite3_column_text(pStmt, 4)
|
||||
);
|
||||
int n = z ? sqlite3_column_bytes(pStmt, 4) : 0;
|
||||
pSample->nByte = n;
|
||||
if( n < 1){
|
||||
pSample->u.z = 0;
|
||||
}else{
|
||||
pSample->u.z = sqlite3Malloc(n);
|
||||
if( pSample->u.z==0 ){
|
||||
db->mallocFailed = 1;
|
||||
sqlite3_finalize(pStmt);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
memcpy(pSample->u.z, z, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return sqlite3_finalize(pStmt);
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_STAT3 */
|
||||
|
||||
/*
|
||||
** Load the content of the sqlite_stat1 and sqlite_stat3 tables. The
|
||||
** contents of sqlite_stat1 are used to populate the Index.aiRowEst[]
|
||||
** arrays. The contents of sqlite_stat2 are used to populate the
|
||||
** arrays. The contents of sqlite_stat3 are used to populate the
|
||||
** Index.aSample[] arrays.
|
||||
**
|
||||
** If the sqlite_stat1 table is not present in the database, SQLITE_ERROR
|
||||
** is returned. In this case, even if SQLITE_ENABLE_STAT2 was defined
|
||||
** during compilation and the sqlite_stat2 table is present, no data is
|
||||
** is returned. In this case, even if SQLITE_ENABLE_STAT3 was defined
|
||||
** during compilation and the sqlite_stat3 table is present, no data is
|
||||
** read from it.
|
||||
**
|
||||
** If SQLITE_ENABLE_STAT2 was defined during compilation and the
|
||||
** sqlite_stat2 table is not present in the database, SQLITE_ERROR is
|
||||
** If SQLITE_ENABLE_STAT3 was defined during compilation and the
|
||||
** sqlite_stat3 table is not present in the database, SQLITE_ERROR is
|
||||
** returned. However, in this case, data is read from the sqlite_stat1
|
||||
** table (if it is present) before returning.
|
||||
**
|
||||
@ -612,8 +1079,10 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
|
||||
for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){
|
||||
Index *pIdx = sqliteHashData(i);
|
||||
sqlite3DefaultRowEst(pIdx);
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
sqlite3DeleteIndexSamples(db, pIdx);
|
||||
pIdx->aSample = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Check to make sure the sqlite_stat1 table exists */
|
||||
@ -625,7 +1094,7 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
|
||||
|
||||
/* Load new statistics out of the sqlite_stat1 table */
|
||||
zSql = sqlite3MPrintf(db,
|
||||
"SELECT tbl, idx, stat FROM %Q.sqlite_stat1", sInfo.zDatabase);
|
||||
"SELECT tbl,idx,stat FROM %Q.sqlite_stat1", sInfo.zDatabase);
|
||||
if( zSql==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
@ -634,78 +1103,10 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
|
||||
}
|
||||
|
||||
|
||||
/* Load the statistics from the sqlite_stat2 table. */
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
if( rc==SQLITE_OK && !sqlite3FindTable(db, "sqlite_stat2", sInfo.zDatabase) ){
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
/* Load the statistics from the sqlite_stat3 table. */
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
|
||||
zSql = sqlite3MPrintf(db,
|
||||
"SELECT idx,sampleno,sample FROM %Q.sqlite_stat2", sInfo.zDatabase);
|
||||
if( !zSql ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
|
||||
sqlite3DbFree(db, zSql);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
char *zIndex; /* Index name */
|
||||
Index *pIdx; /* Pointer to the index object */
|
||||
|
||||
zIndex = (char *)sqlite3_column_text(pStmt, 0);
|
||||
pIdx = zIndex ? sqlite3FindIndex(db, zIndex, sInfo.zDatabase) : 0;
|
||||
if( pIdx ){
|
||||
int iSample = sqlite3_column_int(pStmt, 1);
|
||||
if( iSample<SQLITE_INDEX_SAMPLES && iSample>=0 ){
|
||||
int eType = sqlite3_column_type(pStmt, 2);
|
||||
|
||||
if( pIdx->aSample==0 ){
|
||||
static const int sz = sizeof(IndexSample)*SQLITE_INDEX_SAMPLES;
|
||||
pIdx->aSample = (IndexSample *)sqlite3DbMallocRaw(0, sz);
|
||||
if( pIdx->aSample==0 ){
|
||||
db->mallocFailed = 1;
|
||||
break;
|
||||
}
|
||||
memset(pIdx->aSample, 0, sz);
|
||||
}
|
||||
|
||||
assert( pIdx->aSample );
|
||||
{
|
||||
IndexSample *pSample = &pIdx->aSample[iSample];
|
||||
pSample->eType = (u8)eType;
|
||||
if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
|
||||
pSample->u.r = sqlite3_column_double(pStmt, 2);
|
||||
}else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
|
||||
const char *z = (const char *)(
|
||||
(eType==SQLITE_BLOB) ?
|
||||
sqlite3_column_blob(pStmt, 2):
|
||||
sqlite3_column_text(pStmt, 2)
|
||||
);
|
||||
int n = sqlite3_column_bytes(pStmt, 2);
|
||||
if( n>24 ){
|
||||
n = 24;
|
||||
}
|
||||
pSample->nByte = (u8)n;
|
||||
if( n < 1){
|
||||
pSample->u.z = 0;
|
||||
}else{
|
||||
pSample->u.z = sqlite3DbStrNDup(0, z, n);
|
||||
if( pSample->u.z==0 ){
|
||||
db->mallocFailed = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
rc = sqlite3_finalize(pStmt);
|
||||
}
|
||||
rc = loadStat3(db, sInfo.zDatabase);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
195
src/backup.c
195
src/backup.c
@ -410,102 +410,106 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
|
||||
** the case where the source and destination databases have the
|
||||
** same schema version.
|
||||
*/
|
||||
if( rc==SQLITE_DONE
|
||||
&& (rc = sqlite3BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1))==SQLITE_OK
|
||||
){
|
||||
int nDestTruncate;
|
||||
|
||||
if( p->pDestDb ){
|
||||
sqlite3ResetInternalSchema(p->pDestDb, -1);
|
||||
}
|
||||
|
||||
/* Set nDestTruncate to the final number of pages in the destination
|
||||
** database. The complication here is that the destination page
|
||||
** size may be different to the source page size.
|
||||
**
|
||||
** If the source page size is smaller than the destination page size,
|
||||
** round up. In this case the call to sqlite3OsTruncate() below will
|
||||
** fix the size of the file. However it is important to call
|
||||
** sqlite3PagerTruncateImage() here so that any pages in the
|
||||
** destination file that lie beyond the nDestTruncate page mark are
|
||||
** journalled by PagerCommitPhaseOne() before they are destroyed
|
||||
** by the file truncation.
|
||||
*/
|
||||
assert( pgszSrc==sqlite3BtreeGetPageSize(p->pSrc) );
|
||||
assert( pgszDest==sqlite3BtreeGetPageSize(p->pDest) );
|
||||
if( pgszSrc<pgszDest ){
|
||||
int ratio = pgszDest/pgszSrc;
|
||||
nDestTruncate = (nSrcPage+ratio-1)/ratio;
|
||||
if( nDestTruncate==(int)PENDING_BYTE_PAGE(p->pDest->pBt) ){
|
||||
nDestTruncate--;
|
||||
if( rc==SQLITE_DONE ){
|
||||
rc = sqlite3BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( p->pDestDb ){
|
||||
sqlite3ResetInternalSchema(p->pDestDb, -1);
|
||||
}
|
||||
if( destMode==PAGER_JOURNALMODE_WAL ){
|
||||
rc = sqlite3BtreeSetVersion(p->pDest, 2);
|
||||
}
|
||||
}else{
|
||||
nDestTruncate = nSrcPage * (pgszSrc/pgszDest);
|
||||
}
|
||||
sqlite3PagerTruncateImage(pDestPager, nDestTruncate);
|
||||
|
||||
if( pgszSrc<pgszDest ){
|
||||
/* If the source page-size is smaller than the destination page-size,
|
||||
** two extra things may need to happen:
|
||||
if( rc==SQLITE_OK ){
|
||||
int nDestTruncate;
|
||||
/* Set nDestTruncate to the final number of pages in the destination
|
||||
** database. The complication here is that the destination page
|
||||
** size may be different to the source page size.
|
||||
**
|
||||
** * The destination may need to be truncated, and
|
||||
**
|
||||
** * Data stored on the pages immediately following the
|
||||
** pending-byte page in the source database may need to be
|
||||
** copied into the destination database.
|
||||
** If the source page size is smaller than the destination page size,
|
||||
** round up. In this case the call to sqlite3OsTruncate() below will
|
||||
** fix the size of the file. However it is important to call
|
||||
** sqlite3PagerTruncateImage() here so that any pages in the
|
||||
** destination file that lie beyond the nDestTruncate page mark are
|
||||
** journalled by PagerCommitPhaseOne() before they are destroyed
|
||||
** by the file truncation.
|
||||
*/
|
||||
const i64 iSize = (i64)pgszSrc * (i64)nSrcPage;
|
||||
sqlite3_file * const pFile = sqlite3PagerFile(pDestPager);
|
||||
i64 iOff;
|
||||
i64 iEnd;
|
||||
|
||||
assert( pFile );
|
||||
assert( (i64)nDestTruncate*(i64)pgszDest >= iSize || (
|
||||
nDestTruncate==(int)(PENDING_BYTE_PAGE(p->pDest->pBt)-1)
|
||||
&& iSize>=PENDING_BYTE && iSize<=PENDING_BYTE+pgszDest
|
||||
));
|
||||
|
||||
/* This call ensures that all data required to recreate the original
|
||||
** database has been stored in the journal for pDestPager and the
|
||||
** journal synced to disk. So at this point we may safely modify
|
||||
** the database file in any way, knowing that if a power failure
|
||||
** occurs, the original database will be reconstructed from the
|
||||
** journal file. */
|
||||
rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1);
|
||||
|
||||
/* Write the extra pages and truncate the database file as required. */
|
||||
iEnd = MIN(PENDING_BYTE + pgszDest, iSize);
|
||||
for(
|
||||
iOff=PENDING_BYTE+pgszSrc;
|
||||
rc==SQLITE_OK && iOff<iEnd;
|
||||
iOff+=pgszSrc
|
||||
){
|
||||
PgHdr *pSrcPg = 0;
|
||||
const Pgno iSrcPg = (Pgno)((iOff/pgszSrc)+1);
|
||||
rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg);
|
||||
if( rc==SQLITE_OK ){
|
||||
u8 *zData = sqlite3PagerGetData(pSrcPg);
|
||||
rc = sqlite3OsWrite(pFile, zData, pgszSrc, iOff);
|
||||
assert( pgszSrc==sqlite3BtreeGetPageSize(p->pSrc) );
|
||||
assert( pgszDest==sqlite3BtreeGetPageSize(p->pDest) );
|
||||
if( pgszSrc<pgszDest ){
|
||||
int ratio = pgszDest/pgszSrc;
|
||||
nDestTruncate = (nSrcPage+ratio-1)/ratio;
|
||||
if( nDestTruncate==(int)PENDING_BYTE_PAGE(p->pDest->pBt) ){
|
||||
nDestTruncate--;
|
||||
}
|
||||
sqlite3PagerUnref(pSrcPg);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = backupTruncateFile(pFile, iSize);
|
||||
}else{
|
||||
nDestTruncate = nSrcPage * (pgszSrc/pgszDest);
|
||||
}
|
||||
sqlite3PagerTruncateImage(pDestPager, nDestTruncate);
|
||||
|
||||
/* Sync the database file to disk. */
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3PagerSync(pDestPager);
|
||||
if( pgszSrc<pgszDest ){
|
||||
/* If the source page-size is smaller than the destination page-size,
|
||||
** two extra things may need to happen:
|
||||
**
|
||||
** * The destination may need to be truncated, and
|
||||
**
|
||||
** * Data stored on the pages immediately following the
|
||||
** pending-byte page in the source database may need to be
|
||||
** copied into the destination database.
|
||||
*/
|
||||
const i64 iSize = (i64)pgszSrc * (i64)nSrcPage;
|
||||
sqlite3_file * const pFile = sqlite3PagerFile(pDestPager);
|
||||
i64 iOff;
|
||||
i64 iEnd;
|
||||
|
||||
assert( pFile );
|
||||
assert( (i64)nDestTruncate*(i64)pgszDest >= iSize || (
|
||||
nDestTruncate==(int)(PENDING_BYTE_PAGE(p->pDest->pBt)-1)
|
||||
&& iSize>=PENDING_BYTE && iSize<=PENDING_BYTE+pgszDest
|
||||
));
|
||||
|
||||
/* This call ensures that all data required to recreate the original
|
||||
** database has been stored in the journal for pDestPager and the
|
||||
** journal synced to disk. So at this point we may safely modify
|
||||
** the database file in any way, knowing that if a power failure
|
||||
** occurs, the original database will be reconstructed from the
|
||||
** journal file. */
|
||||
rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1);
|
||||
|
||||
/* Write the extra pages and truncate the database file as required */
|
||||
iEnd = MIN(PENDING_BYTE + pgszDest, iSize);
|
||||
for(
|
||||
iOff=PENDING_BYTE+pgszSrc;
|
||||
rc==SQLITE_OK && iOff<iEnd;
|
||||
iOff+=pgszSrc
|
||||
){
|
||||
PgHdr *pSrcPg = 0;
|
||||
const Pgno iSrcPg = (Pgno)((iOff/pgszSrc)+1);
|
||||
rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg);
|
||||
if( rc==SQLITE_OK ){
|
||||
u8 *zData = sqlite3PagerGetData(pSrcPg);
|
||||
rc = sqlite3OsWrite(pFile, zData, pgszSrc, iOff);
|
||||
}
|
||||
sqlite3PagerUnref(pSrcPg);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = backupTruncateFile(pFile, iSize);
|
||||
}
|
||||
|
||||
/* Sync the database file to disk. */
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3PagerSync(pDestPager);
|
||||
}
|
||||
}else{
|
||||
rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0);
|
||||
}
|
||||
|
||||
/* Finish committing the transaction to the destination database. */
|
||||
if( SQLITE_OK==rc
|
||||
&& SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest, 0))
|
||||
){
|
||||
rc = SQLITE_DONE;
|
||||
}
|
||||
}else{
|
||||
rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0);
|
||||
}
|
||||
|
||||
/* Finish committing the transaction to the destination database. */
|
||||
if( SQLITE_OK==rc
|
||||
&& SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest, 0))
|
||||
){
|
||||
rc = SQLITE_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -539,14 +543,14 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
|
||||
*/
|
||||
int sqlite3_backup_finish(sqlite3_backup *p){
|
||||
sqlite3_backup **pp; /* Ptr to head of pagers backup list */
|
||||
sqlite3_mutex *mutex; /* Mutex to protect source database */
|
||||
MUTEX_LOGIC( sqlite3_mutex *mutex; ) /* Mutex to protect source database */
|
||||
int rc; /* Value to return */
|
||||
|
||||
/* Enter the mutexes */
|
||||
if( p==0 ) return SQLITE_OK;
|
||||
sqlite3_mutex_enter(p->pSrcDb->mutex);
|
||||
sqlite3BtreeEnter(p->pSrc);
|
||||
mutex = p->pSrcDb->mutex;
|
||||
MUTEX_LOGIC( mutex = p->pSrcDb->mutex; )
|
||||
if( p->pDestDb ){
|
||||
sqlite3_mutex_enter(p->pDestDb->mutex);
|
||||
}
|
||||
@ -665,10 +669,18 @@ void sqlite3BackupRestart(sqlite3_backup *pBackup){
|
||||
*/
|
||||
int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
|
||||
int rc;
|
||||
sqlite3_file *pFd; /* File descriptor for database pTo */
|
||||
sqlite3_backup b;
|
||||
sqlite3BtreeEnter(pTo);
|
||||
sqlite3BtreeEnter(pFrom);
|
||||
|
||||
assert( sqlite3BtreeIsInTrans(pTo) );
|
||||
pFd = sqlite3PagerFile(sqlite3BtreePager(pTo));
|
||||
if( pFd->pMethods ){
|
||||
i64 nByte = sqlite3BtreeGetPageSize(pFrom)*(i64)sqlite3BtreeLastPage(pFrom);
|
||||
sqlite3OsFileControl(pFd, SQLITE_FCNTL_OVERWRITE, &nByte);
|
||||
}
|
||||
|
||||
/* Set up an sqlite3_backup object. sqlite3_backup.pDestDb must be set
|
||||
** to 0. This is used by the implementations of sqlite3_backup_step()
|
||||
** and sqlite3_backup_finish() to detect that they are being called
|
||||
@ -692,8 +704,11 @@ int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
|
||||
rc = sqlite3_backup_finish(&b);
|
||||
if( rc==SQLITE_OK ){
|
||||
pTo->pBt->pageSizeFixed = 0;
|
||||
}else{
|
||||
sqlite3PagerClearCache(sqlite3BtreePager(b.pDest));
|
||||
}
|
||||
|
||||
assert( sqlite3BtreeIsInTrans(pTo)==0 );
|
||||
sqlite3BtreeLeave(pFrom);
|
||||
sqlite3BtreeLeave(pTo);
|
||||
return rc;
|
||||
|
||||
133
src/btree.c
133
src/btree.c
@ -656,18 +656,21 @@ static int btreeMoveto(
|
||||
int rc; /* Status code */
|
||||
UnpackedRecord *pIdxKey; /* Unpacked index key */
|
||||
char aSpace[150]; /* Temp space for pIdxKey - to avoid a malloc */
|
||||
char *pFree = 0;
|
||||
|
||||
if( pKey ){
|
||||
assert( nKey==(i64)(int)nKey );
|
||||
pIdxKey = sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey,
|
||||
aSpace, sizeof(aSpace));
|
||||
pIdxKey = sqlite3VdbeAllocUnpackedRecord(
|
||||
pCur->pKeyInfo, aSpace, sizeof(aSpace), &pFree
|
||||
);
|
||||
if( pIdxKey==0 ) return SQLITE_NOMEM;
|
||||
sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey, pIdxKey);
|
||||
}else{
|
||||
pIdxKey = 0;
|
||||
}
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCur, pIdxKey, nKey, bias, pRes);
|
||||
if( pKey ){
|
||||
sqlite3VdbeDeleteUnpackedRecord(pIdxKey);
|
||||
if( pFree ){
|
||||
sqlite3DbFree(pCur->pKeyInfo->db, pFree);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@ -1763,17 +1766,19 @@ int sqlite3BtreeOpen(
|
||||
if( vfsFlags & SQLITE_OPEN_SHAREDCACHE ){
|
||||
int nFullPathname = pVfs->mxPathname+1;
|
||||
char *zFullPathname = sqlite3Malloc(nFullPathname);
|
||||
sqlite3_mutex *mutexShared;
|
||||
MUTEX_LOGIC( sqlite3_mutex *mutexShared; )
|
||||
p->sharable = 1;
|
||||
if( !zFullPathname ){
|
||||
sqlite3_free(p);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
sqlite3OsFullPathname(pVfs, zFilename, nFullPathname, zFullPathname);
|
||||
#if SQLITE_THREADSAFE
|
||||
mutexOpen = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_OPEN);
|
||||
sqlite3_mutex_enter(mutexOpen);
|
||||
mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
sqlite3_mutex_enter(mutexShared);
|
||||
#endif
|
||||
for(pBt=GLOBAL(BtShared*,sqlite3SharedCacheList); pBt; pBt=pBt->pNext){
|
||||
assert( pBt->nRef>0 );
|
||||
if( 0==strcmp(zFullPathname, sqlite3PagerFilename(pBt->pPager))
|
||||
@ -1879,9 +1884,9 @@ int sqlite3BtreeOpen(
|
||||
/* Add the new BtShared object to the linked list sharable BtShareds.
|
||||
*/
|
||||
if( p->sharable ){
|
||||
sqlite3_mutex *mutexShared;
|
||||
MUTEX_LOGIC( sqlite3_mutex *mutexShared; )
|
||||
pBt->nRef = 1;
|
||||
mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
MUTEX_LOGIC( mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);)
|
||||
if( SQLITE_THREADSAFE && sqlite3GlobalConfig.bCoreMutex ){
|
||||
pBt->mutex = sqlite3MutexAlloc(SQLITE_MUTEX_FAST);
|
||||
if( pBt->mutex==0 ){
|
||||
@ -1963,12 +1968,12 @@ btree_open_out:
|
||||
*/
|
||||
static int removeFromSharingList(BtShared *pBt){
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
sqlite3_mutex *pMaster;
|
||||
MUTEX_LOGIC( sqlite3_mutex *pMaster; )
|
||||
BtShared *pList;
|
||||
int removed = 0;
|
||||
|
||||
assert( sqlite3_mutex_notheld(pBt->mutex) );
|
||||
pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
MUTEX_LOGIC( pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); )
|
||||
sqlite3_mutex_enter(pMaster);
|
||||
pBt->nRef--;
|
||||
if( pBt->nRef<=0 ){
|
||||
@ -2743,11 +2748,12 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){
|
||||
if( eType==PTRMAP_OVERFLOW1 ){
|
||||
CellInfo info;
|
||||
btreeParseCellPtr(pPage, pCell, &info);
|
||||
if( info.iOverflow ){
|
||||
if( iFrom==get4byte(&pCell[info.iOverflow]) ){
|
||||
put4byte(&pCell[info.iOverflow], iTo);
|
||||
break;
|
||||
}
|
||||
if( info.iOverflow
|
||||
&& pCell+info.iOverflow+3<=pPage->aData+pPage->maskPage
|
||||
&& iFrom==get4byte(&pCell[info.iOverflow])
|
||||
){
|
||||
put4byte(&pCell[info.iOverflow], iTo);
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
if( get4byte(pCell)==iFrom ){
|
||||
@ -3468,7 +3474,8 @@ static int btreeCursor(
|
||||
return SQLITE_READONLY;
|
||||
}
|
||||
if( iTable==1 && btreePagecount(pBt)==0 ){
|
||||
return SQLITE_EMPTY;
|
||||
assert( wrFlag==0 );
|
||||
iTable = 0;
|
||||
}
|
||||
|
||||
/* Now that no other errors can occur, finish filling in the BtCursor
|
||||
@ -3933,21 +3940,55 @@ static int accessPayload(
|
||||
/* Need to read this page properly. It contains some of the
|
||||
** range of data that is being read (eOp==0) or written (eOp!=0).
|
||||
*/
|
||||
DbPage *pDbPage;
|
||||
#ifdef SQLITE_DIRECT_OVERFLOW_READ
|
||||
sqlite3_file *fd;
|
||||
#endif
|
||||
int a = amt;
|
||||
rc = sqlite3PagerGet(pBt->pPager, nextPage, &pDbPage);
|
||||
if( rc==SQLITE_OK ){
|
||||
aPayload = sqlite3PagerGetData(pDbPage);
|
||||
nextPage = get4byte(aPayload);
|
||||
if( a + offset > ovflSize ){
|
||||
a = ovflSize - offset;
|
||||
}
|
||||
rc = copyPayload(&aPayload[offset+4], pBuf, a, eOp, pDbPage);
|
||||
sqlite3PagerUnref(pDbPage);
|
||||
offset = 0;
|
||||
amt -= a;
|
||||
pBuf += a;
|
||||
if( a + offset > ovflSize ){
|
||||
a = ovflSize - offset;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DIRECT_OVERFLOW_READ
|
||||
/* If all the following are true:
|
||||
**
|
||||
** 1) this is a read operation, and
|
||||
** 2) data is required from the start of this overflow page, and
|
||||
** 3) the database is file-backed, and
|
||||
** 4) there is no open write-transaction, and
|
||||
** 5) the database is not a WAL database,
|
||||
**
|
||||
** then data can be read directly from the database file into the
|
||||
** output buffer, bypassing the page-cache altogether. This speeds
|
||||
** up loading large records that span many overflow pages.
|
||||
*/
|
||||
if( eOp==0 /* (1) */
|
||||
&& offset==0 /* (2) */
|
||||
&& pBt->inTransaction==TRANS_READ /* (4) */
|
||||
&& (fd = sqlite3PagerFile(pBt->pPager))->pMethods /* (3) */
|
||||
&& pBt->pPage1->aData[19]==0x01 /* (5) */
|
||||
){
|
||||
u8 aSave[4];
|
||||
u8 *aWrite = &pBuf[-4];
|
||||
memcpy(aSave, aWrite, 4);
|
||||
rc = sqlite3OsRead(fd, aWrite, a+4, pBt->pageSize * (nextPage-1));
|
||||
nextPage = get4byte(aWrite);
|
||||
memcpy(aWrite, aSave, 4);
|
||||
}else
|
||||
#endif
|
||||
|
||||
{
|
||||
DbPage *pDbPage;
|
||||
rc = sqlite3PagerGet(pBt->pPager, nextPage, &pDbPage);
|
||||
if( rc==SQLITE_OK ){
|
||||
aPayload = sqlite3PagerGetData(pDbPage);
|
||||
nextPage = get4byte(aPayload);
|
||||
rc = copyPayload(&aPayload[offset+4], pBuf, a, eOp, pDbPage);
|
||||
sqlite3PagerUnref(pDbPage);
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
amt -= a;
|
||||
pBuf += a;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4222,6 +4263,9 @@ static int moveToRoot(BtCursor *pCur){
|
||||
releasePage(pCur->apPage[i]);
|
||||
}
|
||||
pCur->iPage = 0;
|
||||
}else if( pCur->pgnoRoot==0 ){
|
||||
pCur->eState = CURSOR_INVALID;
|
||||
return SQLITE_OK;
|
||||
}else{
|
||||
rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->apPage[0]);
|
||||
if( rc!=SQLITE_OK ){
|
||||
@ -4331,7 +4375,7 @@ int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
|
||||
rc = moveToRoot(pCur);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( pCur->eState==CURSOR_INVALID ){
|
||||
assert( pCur->apPage[pCur->iPage]->nCell==0 );
|
||||
assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->nCell==0 );
|
||||
*pRes = 1;
|
||||
}else{
|
||||
assert( pCur->apPage[pCur->iPage]->nCell>0 );
|
||||
@ -4370,7 +4414,7 @@ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
|
||||
rc = moveToRoot(pCur);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( CURSOR_INVALID==pCur->eState ){
|
||||
assert( pCur->apPage[pCur->iPage]->nCell==0 );
|
||||
assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->nCell==0 );
|
||||
*pRes = 1;
|
||||
}else{
|
||||
assert( pCur->eState==CURSOR_VALID );
|
||||
@ -4443,12 +4487,12 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
if( rc ){
|
||||
return rc;
|
||||
}
|
||||
assert( pCur->apPage[pCur->iPage] );
|
||||
assert( pCur->apPage[pCur->iPage]->isInit );
|
||||
assert( pCur->apPage[pCur->iPage]->nCell>0 || pCur->eState==CURSOR_INVALID );
|
||||
assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage] );
|
||||
assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->isInit );
|
||||
assert( pCur->eState==CURSOR_INVALID || pCur->apPage[pCur->iPage]->nCell>0 );
|
||||
if( pCur->eState==CURSOR_INVALID ){
|
||||
*pRes = -1;
|
||||
assert( pCur->apPage[pCur->iPage]->nCell==0 );
|
||||
assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->nCell==0 );
|
||||
return SQLITE_OK;
|
||||
}
|
||||
assert( pCur->apPage[0]->intKey || pIdxKey );
|
||||
@ -4543,7 +4587,6 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
if( c==0 ){
|
||||
if( pPage->intKey && !pPage->leaf ){
|
||||
lwr = idx;
|
||||
upr = lwr - 1;
|
||||
break;
|
||||
}else{
|
||||
*pRes = 0;
|
||||
@ -4561,7 +4604,7 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
}
|
||||
pCur->aiIdx[pCur->iPage] = (u16)(idx = (lwr+upr)/2);
|
||||
}
|
||||
assert( lwr==upr+1 );
|
||||
assert( lwr==upr+1 || (pPage->intKey && !pPage->leaf) );
|
||||
assert( pPage->isInit );
|
||||
if( pPage->leaf ){
|
||||
chldPg = 0;
|
||||
@ -4826,6 +4869,8 @@ static int allocateBtreePage(
|
||||
pTrunk = 0;
|
||||
goto end_allocate_page;
|
||||
}
|
||||
assert( pTrunk!=0 );
|
||||
assert( pTrunk->aData!=0 );
|
||||
|
||||
k = get4byte(&pTrunk->aData[4]); /* # of leaves on this trunk page */
|
||||
if( k==0 && !searchList ){
|
||||
@ -5175,6 +5220,9 @@ static int clearCell(MemPage *pPage, unsigned char *pCell){
|
||||
if( info.iOverflow==0 ){
|
||||
return SQLITE_OK; /* No overflow pages. Return without doing anything */
|
||||
}
|
||||
if( pCell+info.iOverflow+3 > pPage->aData+pPage->maskPage ){
|
||||
return SQLITE_CORRUPT; /* Cell extends past end of page */
|
||||
}
|
||||
ovflPgno = get4byte(&pCell[info.iOverflow]);
|
||||
assert( pBt->usableSize > 4 );
|
||||
ovflPageSize = pBt->usableSize - 4;
|
||||
@ -5950,13 +5998,15 @@ static int balance_nonroot(
|
||||
** four bytes of the divider cell. So the pointer is safe to use
|
||||
** later on.
|
||||
**
|
||||
** Unless SQLite is compiled in secure-delete mode. In this case,
|
||||
** But not if we are in secure-delete mode. In secure-delete mode,
|
||||
** the dropCell() routine will overwrite the entire cell with zeroes.
|
||||
** In this case, temporarily copy the cell into the aOvflSpace[]
|
||||
** buffer. It will be copied out again as soon as the aSpace[] buffer
|
||||
** is allocated. */
|
||||
if( pBt->secureDelete ){
|
||||
int iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData);
|
||||
int iOff;
|
||||
|
||||
iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData);
|
||||
if( (iOff+szNew[i])>(int)pBt->usableSize ){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
memset(apOld, 0, (i+1)*sizeof(MemPage*));
|
||||
@ -6376,6 +6426,7 @@ static int balance_nonroot(
|
||||
/* Cell i is the cell immediately following the last cell on old
|
||||
** sibling page j. If the siblings are not leaf pages of an
|
||||
** intkey b-tree, then cell i was a divider cell. */
|
||||
assert( j+1 < ArraySize(apCopy) );
|
||||
pOld = apCopy[++j];
|
||||
iNextOld = i + !leafData + pOld->nCell + pOld->nOverflow;
|
||||
if( pOld->nOverflow ){
|
||||
@ -7358,6 +7409,11 @@ int sqlite3BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){
|
||||
int sqlite3BtreeCount(BtCursor *pCur, i64 *pnEntry){
|
||||
i64 nEntry = 0; /* Value to return in *pnEntry */
|
||||
int rc; /* Return code */
|
||||
|
||||
if( pCur->pgnoRoot==0 ){
|
||||
*pnEntry = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
rc = moveToRoot(pCur);
|
||||
|
||||
/* Unless an error occurs, the following loop runs one iteration for each
|
||||
@ -8142,7 +8198,6 @@ int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){
|
||||
BtShared *pBt = pBtree->pBt;
|
||||
int rc; /* Return code */
|
||||
|
||||
assert( pBtree->inTrans==TRANS_NONE );
|
||||
assert( iVersion==1 || iVersion==2 );
|
||||
|
||||
/* If setting the version fields to 1, do not automatically open the
|
||||
|
||||
252
src/build.c
252
src/build.c
@ -1674,7 +1674,7 @@ void sqlite3CreateView(
|
||||
const char *z;
|
||||
Token sEnd;
|
||||
DbFixer sFix;
|
||||
Token *pName;
|
||||
Token *pName = 0;
|
||||
int iDb;
|
||||
sqlite3 *db = pParse->db;
|
||||
|
||||
@ -1980,6 +1980,100 @@ static void destroyTable(Parse *pParse, Table *pTab){
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Remove entries from the sqlite_statN tables (for N in (1,2,3))
|
||||
** after a DROP INDEX or DROP TABLE command.
|
||||
*/
|
||||
static void sqlite3ClearStatTables(
|
||||
Parse *pParse, /* The parsing context */
|
||||
int iDb, /* The database number */
|
||||
const char *zType, /* "idx" or "tbl" */
|
||||
const char *zName /* Name of index or table */
|
||||
){
|
||||
int i;
|
||||
const char *zDbName = pParse->db->aDb[iDb].zName;
|
||||
for(i=1; i<=3; i++){
|
||||
char zTab[24];
|
||||
sqlite3_snprintf(sizeof(zTab),zTab,"sqlite_stat%d",i);
|
||||
if( sqlite3FindTable(pParse->db, zTab, zDbName) ){
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %Q.%s WHERE %s=%Q",
|
||||
zDbName, zTab, zType, zName
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code to drop a table.
|
||||
*/
|
||||
void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, int isView){
|
||||
Vdbe *v;
|
||||
sqlite3 *db = pParse->db;
|
||||
Trigger *pTrigger;
|
||||
Db *pDb = &db->aDb[iDb];
|
||||
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
assert( v!=0 );
|
||||
sqlite3BeginWriteOperation(pParse, 1, iDb);
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
if( IsVirtual(pTab) ){
|
||||
sqlite3VdbeAddOp0(v, OP_VBegin);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Drop all triggers associated with the table being dropped. Code
|
||||
** is generated to remove entries from sqlite_master and/or
|
||||
** sqlite_temp_master if required.
|
||||
*/
|
||||
pTrigger = sqlite3TriggerList(pParse, pTab);
|
||||
while( pTrigger ){
|
||||
assert( pTrigger->pSchema==pTab->pSchema ||
|
||||
pTrigger->pSchema==db->aDb[1].pSchema );
|
||||
sqlite3DropTriggerPtr(pParse, pTrigger);
|
||||
pTrigger = pTrigger->pNext;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTOINCREMENT
|
||||
/* Remove any entries of the sqlite_sequence table associated with
|
||||
** the table being dropped. This is done before the table is dropped
|
||||
** at the btree level, in case the sqlite_sequence table needs to
|
||||
** move as a result of the drop (can happen in auto-vacuum mode).
|
||||
*/
|
||||
if( pTab->tabFlags & TF_Autoincrement ){
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %Q.sqlite_sequence WHERE name=%Q",
|
||||
pDb->zName, pTab->zName
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Drop all SQLITE_MASTER table and index entries that refer to the
|
||||
** table. The program name loops through the master table and deletes
|
||||
** every row that refers to a table of the same name as the one being
|
||||
** dropped. Triggers are handled seperately because a trigger can be
|
||||
** created in the temp database that refers to a table in another
|
||||
** database.
|
||||
*/
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %Q.%s WHERE tbl_name=%Q and type!='trigger'",
|
||||
pDb->zName, SCHEMA_TABLE(iDb), pTab->zName);
|
||||
if( !isView && !IsVirtual(pTab) ){
|
||||
destroyTable(pParse, pTab);
|
||||
}
|
||||
|
||||
/* Remove the table entry from SQLite's internal schema and modify
|
||||
** the schema cookie.
|
||||
*/
|
||||
if( IsVirtual(pTab) ){
|
||||
sqlite3VdbeAddOp4(v, OP_VDestroy, iDb, 0, 0, pTab->zName, 0);
|
||||
}
|
||||
sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab->zName, 0);
|
||||
sqlite3ChangeCookie(pParse, iDb);
|
||||
sqliteViewResetAll(db, iDb);
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is called to do the work of a DROP TABLE statement.
|
||||
** pName is the name of the table to be dropped.
|
||||
@ -2048,7 +2142,8 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 ){
|
||||
if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0
|
||||
&& sqlite3StrNICmp(pTab->zName, "sqlite_stat", 11)!=0 ){
|
||||
sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName);
|
||||
goto exit_drop_table;
|
||||
}
|
||||
@ -2072,75 +2167,11 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
|
||||
*/
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
if( v ){
|
||||
Trigger *pTrigger;
|
||||
Db *pDb = &db->aDb[iDb];
|
||||
sqlite3BeginWriteOperation(pParse, 1, iDb);
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
if( IsVirtual(pTab) ){
|
||||
sqlite3VdbeAddOp0(v, OP_VBegin);
|
||||
}
|
||||
#endif
|
||||
sqlite3ClearStatTables(pParse, iDb, "tbl", pTab->zName);
|
||||
sqlite3FkDropTable(pParse, pName, pTab);
|
||||
|
||||
/* Drop all triggers associated with the table being dropped. Code
|
||||
** is generated to remove entries from sqlite_master and/or
|
||||
** sqlite_temp_master if required.
|
||||
*/
|
||||
pTrigger = sqlite3TriggerList(pParse, pTab);
|
||||
while( pTrigger ){
|
||||
assert( pTrigger->pSchema==pTab->pSchema ||
|
||||
pTrigger->pSchema==db->aDb[1].pSchema );
|
||||
sqlite3DropTriggerPtr(pParse, pTrigger);
|
||||
pTrigger = pTrigger->pNext;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTOINCREMENT
|
||||
/* Remove any entries of the sqlite_sequence table associated with
|
||||
** the table being dropped. This is done before the table is dropped
|
||||
** at the btree level, in case the sqlite_sequence table needs to
|
||||
** move as a result of the drop (can happen in auto-vacuum mode).
|
||||
*/
|
||||
if( pTab->tabFlags & TF_Autoincrement ){
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %s.sqlite_sequence WHERE name=%Q",
|
||||
pDb->zName, pTab->zName
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Drop all SQLITE_MASTER table and index entries that refer to the
|
||||
** table. The program name loops through the master table and deletes
|
||||
** every row that refers to a table of the same name as the one being
|
||||
** dropped. Triggers are handled seperately because a trigger can be
|
||||
** created in the temp database that refers to a table in another
|
||||
** database.
|
||||
*/
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %Q.%s WHERE tbl_name=%Q and type!='trigger'",
|
||||
pDb->zName, SCHEMA_TABLE(iDb), pTab->zName);
|
||||
|
||||
/* Drop any statistics from the sqlite_stat1 table, if it exists */
|
||||
if( sqlite3FindTable(db, "sqlite_stat1", db->aDb[iDb].zName) ){
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %Q.sqlite_stat1 WHERE tbl=%Q", pDb->zName, pTab->zName
|
||||
);
|
||||
}
|
||||
|
||||
if( !isView && !IsVirtual(pTab) ){
|
||||
destroyTable(pParse, pTab);
|
||||
}
|
||||
|
||||
/* Remove the table entry from SQLite's internal schema and modify
|
||||
** the schema cookie.
|
||||
*/
|
||||
if( IsVirtual(pTab) ){
|
||||
sqlite3VdbeAddOp4(v, OP_VDestroy, iDb, 0, 0, pTab->zName, 0);
|
||||
}
|
||||
sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab->zName, 0);
|
||||
sqlite3ChangeCookie(pParse, iDb);
|
||||
sqlite3CodeDropTable(pParse, pTab, iDb, isView);
|
||||
}
|
||||
sqliteViewResetAll(db, iDb);
|
||||
|
||||
exit_drop_table:
|
||||
sqlite3SrcListDelete(db, pName);
|
||||
@ -2308,11 +2339,15 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
|
||||
Table *pTab = pIndex->pTable; /* The table that is indexed */
|
||||
int iTab = pParse->nTab++; /* Btree cursor used for pTab */
|
||||
int iIdx = pParse->nTab++; /* Btree cursor used for pIndex */
|
||||
int iSorter; /* Cursor opened by OpenSorter (if in use) */
|
||||
int addr1; /* Address of top of loop */
|
||||
int addr2; /* Address to jump to for next iteration */
|
||||
int tnum; /* Root page of index */
|
||||
Vdbe *v; /* Generate code into this virtual machine */
|
||||
KeyInfo *pKey; /* KeyInfo for index */
|
||||
#ifdef SQLITE_OMIT_MERGE_SORT
|
||||
int regIdxKey; /* Registers containing the index key */
|
||||
#endif
|
||||
int regRecord; /* Register holding assemblied index record */
|
||||
sqlite3 *db = pParse->db; /* The database connection */
|
||||
int iDb = sqlite3SchemaToIndex(db, pIndex->pSchema);
|
||||
@ -2341,10 +2376,44 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
|
||||
if( memRootPage>=0 ){
|
||||
sqlite3VdbeChangeP5(v, 1);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_MERGE_SORT
|
||||
/* Open the sorter cursor if we are to use one. */
|
||||
iSorter = pParse->nTab++;
|
||||
sqlite3VdbeAddOp4(v, OP_SorterOpen, iSorter, 0, 0, (char*)pKey, P4_KEYINFO);
|
||||
#else
|
||||
iSorter = iTab;
|
||||
#endif
|
||||
|
||||
/* Open the table. Loop through all rows of the table, inserting index
|
||||
** records into the sorter. */
|
||||
sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead);
|
||||
addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iTab, 0);
|
||||
regRecord = sqlite3GetTempReg(pParse);
|
||||
|
||||
#ifndef SQLITE_OMIT_MERGE_SORT
|
||||
sqlite3GenerateIndexKey(pParse, pIndex, iTab, regRecord, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_SorterInsert, iSorter, regRecord);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1);
|
||||
sqlite3VdbeJumpHere(v, addr1);
|
||||
addr1 = sqlite3VdbeAddOp2(v, OP_SorterSort, iSorter, 0);
|
||||
if( pIndex->onError!=OE_None ){
|
||||
int j2 = sqlite3VdbeCurrentAddr(v) + 3;
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, j2);
|
||||
addr2 = sqlite3VdbeCurrentAddr(v);
|
||||
sqlite3VdbeAddOp3(v, OP_SorterCompare, iSorter, j2, regRecord);
|
||||
sqlite3HaltConstraint(
|
||||
pParse, OE_Abort, "indexed columns are not unique", P4_STATIC
|
||||
);
|
||||
}else{
|
||||
addr2 = sqlite3VdbeCurrentAddr(v);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_SorterData, iSorter, regRecord);
|
||||
sqlite3VdbeAddOp3(v, OP_IdxInsert, iIdx, regRecord, 1);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
|
||||
#else
|
||||
regIdxKey = sqlite3GenerateIndexKey(pParse, pIndex, iTab, regRecord, 1);
|
||||
addr2 = addr1 + 1;
|
||||
if( pIndex->onError!=OE_None ){
|
||||
const int regRowid = regIdxKey + pIndex->nColumn;
|
||||
const int j2 = sqlite3VdbeCurrentAddr(v) + 2;
|
||||
@ -2363,13 +2432,16 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
|
||||
sqlite3HaltConstraint(
|
||||
pParse, OE_Abort, "indexed columns are not unique", P4_STATIC);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdx, regRecord);
|
||||
sqlite3VdbeAddOp3(v, OP_IdxInsert, iIdx, regRecord, 0);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
|
||||
#endif
|
||||
sqlite3ReleaseTempReg(pParse, regRecord);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1);
|
||||
sqlite3VdbeAddOp2(v, OP_SorterNext, iSorter, addr2);
|
||||
sqlite3VdbeJumpHere(v, addr1);
|
||||
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iTab);
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iIdx);
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iSorter);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2439,6 +2511,7 @@ Index *sqlite3CreateIndex(
|
||||
assert( pName1 && pName2 );
|
||||
iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName);
|
||||
if( iDb<0 ) goto exit_create_index;
|
||||
assert( pName && pName->z );
|
||||
|
||||
#ifndef SQLITE_OMIT_TEMPDB
|
||||
/* If the index name was unqualified, check if the the table
|
||||
@ -2466,6 +2539,7 @@ Index *sqlite3CreateIndex(
|
||||
assert( db->aDb[iDb].pSchema==pTab->pSchema );
|
||||
}else{
|
||||
assert( pName==0 );
|
||||
assert( pStart==0 );
|
||||
pTab = pParse->pNewTable;
|
||||
if( !pTab ) goto exit_create_index;
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
@ -2508,6 +2582,7 @@ Index *sqlite3CreateIndex(
|
||||
if( pName ){
|
||||
zName = sqlite3NameFromToken(db, pName);
|
||||
if( zName==0 ) goto exit_create_index;
|
||||
assert( pName->z!=0 );
|
||||
if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
|
||||
goto exit_create_index;
|
||||
}
|
||||
@ -2587,8 +2662,8 @@ Index *sqlite3CreateIndex(
|
||||
nCol = pList->nExpr;
|
||||
pIndex = sqlite3DbMallocZero(db,
|
||||
sizeof(Index) + /* Index structure */
|
||||
sizeof(tRowcnt)*(nCol+1) + /* Index.aiRowEst */
|
||||
sizeof(int)*nCol + /* Index.aiColumn */
|
||||
sizeof(int)*(nCol+1) + /* Index.aiRowEst */
|
||||
sizeof(char *)*nCol + /* Index.azColl */
|
||||
sizeof(u8)*nCol + /* Index.aSortOrder */
|
||||
nName + 1 + /* Index.zName */
|
||||
@ -2597,10 +2672,10 @@ Index *sqlite3CreateIndex(
|
||||
if( db->mallocFailed ){
|
||||
goto exit_create_index;
|
||||
}
|
||||
pIndex->azColl = (char**)(&pIndex[1]);
|
||||
pIndex->aiRowEst = (tRowcnt*)(&pIndex[1]);
|
||||
pIndex->azColl = (char**)(&pIndex->aiRowEst[nCol+1]);
|
||||
pIndex->aiColumn = (int *)(&pIndex->azColl[nCol]);
|
||||
pIndex->aiRowEst = (unsigned *)(&pIndex->aiColumn[nCol]);
|
||||
pIndex->aSortOrder = (u8 *)(&pIndex->aiRowEst[nCol+1]);
|
||||
pIndex->aSortOrder = (u8 *)(&pIndex->aiColumn[nCol]);
|
||||
pIndex->zName = (char *)(&pIndex->aSortOrder[nCol]);
|
||||
zExtra = (char *)(&pIndex->zName[nName+1]);
|
||||
memcpy(pIndex->zName, zName, nName+1);
|
||||
@ -2791,7 +2866,7 @@ Index *sqlite3CreateIndex(
|
||||
/* A named index with an explicit CREATE INDEX statement */
|
||||
zStmt = sqlite3MPrintf(db, "CREATE%s INDEX %.*s",
|
||||
onError==OE_None ? "" : " UNIQUE",
|
||||
pEnd->z - pName->z + 1,
|
||||
(int)(pEnd->z - pName->z) + 1,
|
||||
pName->z);
|
||||
}else{
|
||||
/* An automatic index created by a PRIMARY KEY or UNIQUE constraint */
|
||||
@ -2877,9 +2952,9 @@ exit_create_index:
|
||||
** are based on typical values found in actual indices.
|
||||
*/
|
||||
void sqlite3DefaultRowEst(Index *pIdx){
|
||||
unsigned *a = pIdx->aiRowEst;
|
||||
tRowcnt *a = pIdx->aiRowEst;
|
||||
int i;
|
||||
unsigned n;
|
||||
tRowcnt n;
|
||||
assert( a!=0 );
|
||||
a[0] = pIdx->pTable->nRowEst;
|
||||
if( a[0]<10 ) a[0] = 10;
|
||||
@ -2949,15 +3024,9 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){
|
||||
sqlite3BeginWriteOperation(pParse, 1, iDb);
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %Q.%s WHERE name=%Q AND type='index'",
|
||||
db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
|
||||
pIndex->zName
|
||||
db->aDb[iDb].zName, SCHEMA_TABLE(iDb), pIndex->zName
|
||||
);
|
||||
if( sqlite3FindTable(db, "sqlite_stat1", db->aDb[iDb].zName) ){
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %Q.sqlite_stat1 WHERE idx=%Q",
|
||||
db->aDb[iDb].zName, pIndex->zName
|
||||
);
|
||||
}
|
||||
sqlite3ClearStatTables(pParse, iDb, "idx", pIndex->zName);
|
||||
sqlite3ChangeCookie(pParse, iDb);
|
||||
destroyRootPage(pParse, pIndex->tnum, iDb);
|
||||
sqlite3VdbeAddOp4(v, OP_DropIndex, iDb, 0, 0, pIndex->zName, 0);
|
||||
@ -3329,8 +3398,9 @@ void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pIndexedBy){
|
||||
** operator with A. This routine shifts that operator over to B.
|
||||
*/
|
||||
void sqlite3SrcListShiftJoinType(SrcList *p){
|
||||
if( p && p->a ){
|
||||
if( p ){
|
||||
int i;
|
||||
assert( p->a || p->nSrc==0 );
|
||||
for(i=p->nSrc-1; i>0; i--){
|
||||
p->a[i].jointype = p->a[i-1].jointype;
|
||||
}
|
||||
@ -3368,13 +3438,10 @@ void sqlite3BeginTransaction(Parse *pParse, int type){
|
||||
** Commit a transaction
|
||||
*/
|
||||
void sqlite3CommitTransaction(Parse *pParse){
|
||||
sqlite3 *db;
|
||||
Vdbe *v;
|
||||
|
||||
assert( pParse!=0 );
|
||||
db = pParse->db;
|
||||
assert( db!=0 );
|
||||
/* if( db->aDb[0].pBt==0 ) return; */
|
||||
assert( pParse->db!=0 );
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "COMMIT", 0, 0) ){
|
||||
return;
|
||||
}
|
||||
@ -3388,13 +3455,10 @@ void sqlite3CommitTransaction(Parse *pParse){
|
||||
** Rollback a transaction
|
||||
*/
|
||||
void sqlite3RollbackTransaction(Parse *pParse){
|
||||
sqlite3 *db;
|
||||
Vdbe *v;
|
||||
|
||||
assert( pParse!=0 );
|
||||
db = pParse->db;
|
||||
assert( db!=0 );
|
||||
/* if( db->aDb[0].pBt==0 ) return; */
|
||||
assert( pParse->db!=0 );
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "ROLLBACK", 0, 0) ){
|
||||
return;
|
||||
}
|
||||
|
||||
10
src/ctime.c
10
src/ctime.c
@ -114,8 +114,8 @@ static const char * const azCompileOpt[] = {
|
||||
#ifdef SQLITE_ENABLE_RTREE
|
||||
"ENABLE_RTREE",
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
"ENABLE_STAT2",
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
"ENABLE_STAT3",
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
|
||||
"ENABLE_UNLOCK_NOTIFY",
|
||||
@ -144,6 +144,9 @@ static const char * const azCompileOpt[] = {
|
||||
#ifdef SQLITE_LOCK_TRACE
|
||||
"LOCK_TRACE",
|
||||
#endif
|
||||
#ifdef SQLITE_MAX_SCHEMA_RETRY
|
||||
"MAX_SCHEMA_RETRY=" CTIMEOPT_VAL(SQLITE_MAX_SCHEMA_RETRY),
|
||||
#endif
|
||||
#ifdef SQLITE_MEMDEBUG
|
||||
"MEMDEBUG",
|
||||
#endif
|
||||
@ -257,6 +260,9 @@ static const char * const azCompileOpt[] = {
|
||||
#ifdef SQLITE_OMIT_MEMORYDB
|
||||
"OMIT_MEMORYDB",
|
||||
#endif
|
||||
#ifdef SQLITE_OMIT_MERGE_SORT
|
||||
"OMIT_MERGE_SORT",
|
||||
#endif
|
||||
#ifdef SQLITE_OMIT_OR_OPTIMIZATION
|
||||
"OMIT_OR_OPTIMIZATION",
|
||||
#endif
|
||||
|
||||
51
src/date.c
51
src/date.c
@ -289,12 +289,18 @@ static int parseYyyyMmDd(const char *zDate, DateTime *p){
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the time to the current time reported by the VFS
|
||||
** Set the time to the current time reported by the VFS.
|
||||
**
|
||||
** Return the number of errors.
|
||||
*/
|
||||
static void setDateTimeToCurrent(sqlite3_context *context, DateTime *p){
|
||||
static int setDateTimeToCurrent(sqlite3_context *context, DateTime *p){
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
sqlite3OsCurrentTimeInt64(db->pVfs, &p->iJD);
|
||||
p->validJD = 1;
|
||||
if( sqlite3OsCurrentTimeInt64(db->pVfs, &p->iJD)==SQLITE_OK ){
|
||||
p->validJD = 1;
|
||||
return 0;
|
||||
}else{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -324,8 +330,7 @@ static int parseDateOrTime(
|
||||
}else if( parseHhMmSs(zDate, p)==0 ){
|
||||
return 0;
|
||||
}else if( sqlite3StrICmp(zDate,"now")==0){
|
||||
setDateTimeToCurrent(context, p);
|
||||
return 0;
|
||||
return setDateTimeToCurrent(context, p);
|
||||
}else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8) ){
|
||||
p->iJD = (sqlite3_int64)(r*86400000.0 + 0.5);
|
||||
p->validJD = 1;
|
||||
@ -427,7 +432,9 @@ static int osLocaltime(time_t *t, struct tm *pTm){
|
||||
#if (!defined(HAVE_LOCALTIME_R) || !HAVE_LOCALTIME_R) \
|
||||
&& (!defined(HAVE_LOCALTIME_S) || !HAVE_LOCALTIME_S)
|
||||
struct tm *pX;
|
||||
#if SQLITE_THREADSAFE>0
|
||||
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
#endif
|
||||
sqlite3_mutex_enter(mutex);
|
||||
pX = localtime(t);
|
||||
#ifndef SQLITE_OMIT_BUILTIN_TEST
|
||||
@ -750,8 +757,9 @@ static int isDate(
|
||||
int eType;
|
||||
memset(p, 0, sizeof(*p));
|
||||
if( argc==0 ){
|
||||
setDateTimeToCurrent(context, p);
|
||||
}else if( (eType = sqlite3_value_type(argv[0]))==SQLITE_FLOAT
|
||||
return setDateTimeToCurrent(context, p);
|
||||
}
|
||||
if( (eType = sqlite3_value_type(argv[0]))==SQLITE_FLOAT
|
||||
|| eType==SQLITE_INTEGER ){
|
||||
p->iJD = (sqlite3_int64)(sqlite3_value_double(argv[0])*86400000.0 + 0.5);
|
||||
p->validJD = 1;
|
||||
@ -1063,31 +1071,28 @@ static void currentTimeFunc(
|
||||
char *zFormat = (char *)sqlite3_user_data(context);
|
||||
sqlite3 *db;
|
||||
sqlite3_int64 iT;
|
||||
struct tm *pTm;
|
||||
struct tm sNow;
|
||||
char zBuf[20];
|
||||
|
||||
UNUSED_PARAMETER(argc);
|
||||
UNUSED_PARAMETER(argv);
|
||||
|
||||
db = sqlite3_context_db_handle(context);
|
||||
sqlite3OsCurrentTimeInt64(db->pVfs, &iT);
|
||||
if( sqlite3OsCurrentTimeInt64(db->pVfs, &iT) ) return;
|
||||
t = iT/1000 - 10000*(sqlite3_int64)21086676;
|
||||
#ifdef HAVE_GMTIME_R
|
||||
{
|
||||
struct tm sNow;
|
||||
gmtime_r(&t, &sNow);
|
||||
strftime(zBuf, 20, zFormat, &sNow);
|
||||
}
|
||||
pTm = gmtime_r(&t, &sNow);
|
||||
#else
|
||||
{
|
||||
struct tm *pTm;
|
||||
sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
|
||||
pTm = gmtime(&t);
|
||||
strftime(zBuf, 20, zFormat, pTm);
|
||||
sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
|
||||
}
|
||||
sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
|
||||
pTm = gmtime(&t);
|
||||
if( pTm ) memcpy(&sNow, pTm, sizeof(sNow));
|
||||
sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
|
||||
#endif
|
||||
|
||||
sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
|
||||
if( pTm ){
|
||||
strftime(zBuf, 20, zFormat, &sNow);
|
||||
sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@ -371,7 +371,9 @@ void sqlite3DeleteFrom(
|
||||
/* Collect rowids of every row to be deleted.
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere,0,WHERE_DUPLICATES_OK);
|
||||
pWInfo = sqlite3WhereBegin(
|
||||
pParse, pTabList, pWhere, 0, 0, WHERE_DUPLICATES_OK
|
||||
);
|
||||
if( pWInfo==0 ) goto delete_from_cleanup;
|
||||
regRowid = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, iRowid);
|
||||
sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, regRowid);
|
||||
|
||||
49
src/expr.c
49
src/expr.c
@ -403,7 +403,8 @@ Expr *sqlite3ExprAlloc(
|
||||
}else{
|
||||
int c;
|
||||
pNew->u.zToken = (char*)&pNew[1];
|
||||
memcpy(pNew->u.zToken, pToken->z, pToken->n);
|
||||
assert( pToken->z!=0 || pToken->n==0 );
|
||||
if( pToken->n ) memcpy(pNew->u.zToken, pToken->z, pToken->n);
|
||||
pNew->u.zToken[pToken->n] = 0;
|
||||
if( dequote && nExtra>=3
|
||||
&& ((c = pToken->z[0])=='\'' || c=='"' || c=='[' || c=='`') ){
|
||||
@ -901,7 +902,9 @@ SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){
|
||||
pNewItem->zAlias = sqlite3DbStrDup(db, pOldItem->zAlias);
|
||||
pNewItem->jointype = pOldItem->jointype;
|
||||
pNewItem->iCursor = pOldItem->iCursor;
|
||||
pNewItem->isPopulated = pOldItem->isPopulated;
|
||||
pNewItem->addrFillSub = pOldItem->addrFillSub;
|
||||
pNewItem->regReturn = pOldItem->regReturn;
|
||||
pNewItem->isCorrelated = pOldItem->isCorrelated;
|
||||
pNewItem->zIndex = sqlite3DbStrDup(db, pOldItem->zIndex);
|
||||
pNewItem->notIndexed = pOldItem->notIndexed;
|
||||
pNewItem->pIndex = pOldItem->pIndex;
|
||||
@ -1440,11 +1443,19 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
||||
p = (ExprHasProperty(pX, EP_xIsSelect) ? pX->x.pSelect : 0);
|
||||
if( ALWAYS(pParse->nErr==0) && isCandidateForInOpt(p) ){
|
||||
sqlite3 *db = pParse->db; /* Database connection */
|
||||
Expr *pExpr = p->pEList->a[0].pExpr; /* Expression <column> */
|
||||
int iCol = pExpr->iColumn; /* Index of column <column> */
|
||||
Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */
|
||||
Table *pTab = p->pSrc->a[0].pTab; /* Table <table>. */
|
||||
Table *pTab; /* Table <table>. */
|
||||
Expr *pExpr; /* Expression <column> */
|
||||
int iCol; /* Index of column <column> */
|
||||
int iDb; /* Database idx for pTab */
|
||||
|
||||
assert( p ); /* Because of isCandidateForInOpt(p) */
|
||||
assert( p->pEList!=0 ); /* Because of isCandidateForInOpt(p) */
|
||||
assert( p->pEList->a[0].pExpr!=0 ); /* Because of isCandidateForInOpt(p) */
|
||||
assert( p->pSrc!=0 ); /* Because of isCandidateForInOpt(p) */
|
||||
pTab = p->pSrc->a[0].pTab;
|
||||
pExpr = p->pEList->a[0].pExpr;
|
||||
iCol = pExpr->iColumn;
|
||||
|
||||
/* Code an OP_VerifyCookie and OP_TableLock for <table>. */
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
@ -1460,8 +1471,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
||||
int iMem = ++pParse->nMem;
|
||||
int iAddr;
|
||||
|
||||
iAddr = sqlite3VdbeAddOp1(v, OP_If, iMem);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 1, iMem);
|
||||
iAddr = sqlite3VdbeAddOp1(v, OP_Once, iMem);
|
||||
|
||||
sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead);
|
||||
eType = IN_INDEX_ROWID;
|
||||
@ -1492,8 +1502,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
||||
char *pKey;
|
||||
|
||||
pKey = (char *)sqlite3IndexKeyinfo(pParse, pIdx);
|
||||
iAddr = sqlite3VdbeAddOp1(v, OP_If, iMem);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 1, iMem);
|
||||
iAddr = sqlite3VdbeAddOp1(v, OP_Once, iMem);
|
||||
|
||||
sqlite3VdbeAddOp4(v, OP_OpenRead, iTab, pIdx->tnum, iDb,
|
||||
pKey,P4_KEYINFO_HANDOFF);
|
||||
@ -1574,7 +1583,7 @@ int sqlite3CodeSubselect(
|
||||
int rMayHaveNull, /* Register that records whether NULLs exist in RHS */
|
||||
int isRowid /* If true, LHS of IN operator is a rowid */
|
||||
){
|
||||
int testAddr = 0; /* One-time test address */
|
||||
int testAddr = -1; /* One-time test address */
|
||||
int rReg = 0; /* Register storing resulting */
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
if( NEVER(v==0) ) return 0;
|
||||
@ -1592,15 +1601,13 @@ int sqlite3CodeSubselect(
|
||||
*/
|
||||
if( !ExprHasAnyProperty(pExpr, EP_VarSelect) && !pParse->pTriggerTab ){
|
||||
int mem = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp1(v, OP_If, mem);
|
||||
testAddr = sqlite3VdbeAddOp2(v, OP_Integer, 1, mem);
|
||||
assert( testAddr>0 || pParse->db->mallocFailed );
|
||||
testAddr = sqlite3VdbeAddOp1(v, OP_Once, mem);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
if( pParse->explain==2 ){
|
||||
char *zMsg = sqlite3MPrintf(
|
||||
pParse->db, "EXECUTE %s%s SUBQUERY %d", testAddr?"":"CORRELATED ",
|
||||
pParse->db, "EXECUTE %s%s SUBQUERY %d", testAddr>=0?"":"CORRELATED ",
|
||||
pExpr->op==TK_IN?"LIST":"SCALAR", pParse->iNextSelectId
|
||||
);
|
||||
sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC);
|
||||
@ -1692,9 +1699,9 @@ int sqlite3CodeSubselect(
|
||||
** this code only executes once. Because for a non-constant
|
||||
** expression we need to rerun this code each time.
|
||||
*/
|
||||
if( testAddr && !sqlite3ExprIsConstant(pE2) ){
|
||||
sqlite3VdbeChangeToNoop(v, testAddr-1, 2);
|
||||
testAddr = 0;
|
||||
if( testAddr>=0 && !sqlite3ExprIsConstant(pE2) ){
|
||||
sqlite3VdbeChangeToNoop(v, testAddr);
|
||||
testAddr = -1;
|
||||
}
|
||||
|
||||
/* Evaluate the expression and insert it into the temp table */
|
||||
@ -1763,8 +1770,8 @@ int sqlite3CodeSubselect(
|
||||
}
|
||||
}
|
||||
|
||||
if( testAddr ){
|
||||
sqlite3VdbeJumpHere(v, testAddr-1);
|
||||
if( testAddr>=0 ){
|
||||
sqlite3VdbeJumpHere(v, testAddr);
|
||||
}
|
||||
sqlite3ExprCachePop(pParse, 1);
|
||||
|
||||
@ -2286,7 +2293,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
inReg = pCol->iMem;
|
||||
break;
|
||||
}else if( pAggInfo->useSortingIdx ){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdx,
|
||||
sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdxPTab,
|
||||
pCol->iSorterColumn, target);
|
||||
break;
|
||||
}
|
||||
@ -3455,7 +3462,7 @@ int sqlite3ExprCompare(Expr *pA, Expr *pB){
|
||||
}
|
||||
}else if( pA->op!=TK_COLUMN && pA->u.zToken ){
|
||||
if( ExprHasProperty(pB, EP_IntValue) || NEVER(pB->u.zToken==0) ) return 2;
|
||||
if( sqlite3StrICmp(pA->u.zToken,pB->u.zToken)!=0 ){
|
||||
if( strcmp(pA->u.zToken,pB->u.zToken)!=0 ){
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
20
src/fkey.c
20
src/fkey.c
@ -560,7 +560,7 @@ static void fkScanChildren(
|
||||
** clause. If the constraint is not deferred, throw an exception for
|
||||
** each row found. Otherwise, for deferred constraints, increment the
|
||||
** deferred constraint counter by nIncr for each row selected. */
|
||||
pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0);
|
||||
if( nIncr>0 && pFKey->isDeferred==0 ){
|
||||
sqlite3ParseToplevel(pParse)->mayAbort = 1;
|
||||
}
|
||||
@ -734,7 +734,24 @@ void sqlite3FkCheck(
|
||||
pTo = sqlite3LocateTable(pParse, 0, pFKey->zTo, zDb);
|
||||
}
|
||||
if( !pTo || locateFkeyIndex(pParse, pTo, pFKey, &pIdx, &aiFree) ){
|
||||
assert( isIgnoreErrors==0 || (regOld!=0 && regNew==0) );
|
||||
if( !isIgnoreErrors || db->mallocFailed ) return;
|
||||
if( pTo==0 ){
|
||||
/* If isIgnoreErrors is true, then a table is being dropped. In this
|
||||
** case SQLite runs a "DELETE FROM xxx" on the table being dropped
|
||||
** before actually dropping it in order to check FK constraints.
|
||||
** If the parent table of an FK constraint on the current table is
|
||||
** missing, behave as if it is empty. i.e. decrement the relevant
|
||||
** FK counter for each row of the current table with non-NULL keys.
|
||||
*/
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
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_FkCounter, pFKey->isDeferred, -1);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
assert( pFKey->nCol==1 || (aiFree && pIdx) );
|
||||
@ -1107,6 +1124,7 @@ static Trigger *fkActionTrigger(
|
||||
fkTriggerDelete(db, pTrigger);
|
||||
return 0;
|
||||
}
|
||||
assert( pStep!=0 );
|
||||
|
||||
switch( action ){
|
||||
case OE_Restrict:
|
||||
|
||||
16
src/func.c
16
src/func.c
@ -332,16 +332,15 @@ static void upperFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
if( z2 ){
|
||||
z1 = contextMalloc(context, ((i64)n)+1);
|
||||
if( z1 ){
|
||||
memcpy(z1, z2, n+1);
|
||||
for(i=0; z1[i]; i++){
|
||||
z1[i] = (char)sqlite3Toupper(z1[i]);
|
||||
for(i=0; i<n; i++){
|
||||
z1[i] = (char)sqlite3Toupper(z2[i]);
|
||||
}
|
||||
sqlite3_result_text(context, z1, -1, sqlite3_free);
|
||||
sqlite3_result_text(context, z1, n, sqlite3_free);
|
||||
}
|
||||
}
|
||||
}
|
||||
static void lowerFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
u8 *z1;
|
||||
char *z1;
|
||||
const char *z2;
|
||||
int i, n;
|
||||
UNUSED_PARAMETER(argc);
|
||||
@ -352,11 +351,10 @@ static void lowerFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
if( z2 ){
|
||||
z1 = contextMalloc(context, ((i64)n)+1);
|
||||
if( z1 ){
|
||||
memcpy(z1, z2, n+1);
|
||||
for(i=0; z1[i]; i++){
|
||||
z1[i] = sqlite3Tolower(z1[i]);
|
||||
for(i=0; i<n; i++){
|
||||
z1[i] = sqlite3Tolower(z2[i]);
|
||||
}
|
||||
sqlite3_result_text(context, (char *)z1, -1, sqlite3_free);
|
||||
sqlite3_result_text(context, z1, n, sqlite3_free);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,7 +143,7 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
|
||||
SQLITE_THREADSAFE==1, /* bFullMutex */
|
||||
SQLITE_USE_URI, /* bOpenUri */
|
||||
0x7ffffffe, /* mxStrlen */
|
||||
100, /* szLookaside */
|
||||
128, /* szLookaside */
|
||||
500, /* nLookaside */
|
||||
{0,0,0,0,0,0,0,0}, /* m */
|
||||
{0,0,0,0,0,0,0,0,0}, /* mutex */
|
||||
|
||||
@ -1747,6 +1747,9 @@ static int xferOptimization(
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
if( (pParse->db->flags & SQLITE_CountRows)!=0 ){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If we get this far, it means either:
|
||||
**
|
||||
|
||||
@ -716,7 +716,9 @@ void Parse(
|
||||
){
|
||||
YYMINORTYPE yyminorunion;
|
||||
int yyact; /* The parser action. */
|
||||
#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY)
|
||||
int yyendofinput; /* True if we are at the end of input */
|
||||
#endif
|
||||
#ifdef YYERRORSYMBOL
|
||||
int yyerrorhit = 0; /* True if yymajor has invoked an error */
|
||||
#endif
|
||||
@ -739,7 +741,9 @@ void Parse(
|
||||
yypParser->yystack[0].major = 0;
|
||||
}
|
||||
yyminorunion.yy0 = yyminor;
|
||||
#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY)
|
||||
yyendofinput = (yymajor==0);
|
||||
#endif
|
||||
ParseARG_STORE;
|
||||
|
||||
#ifndef NDEBUG
|
||||
@ -751,7 +755,6 @@ void Parse(
|
||||
do{
|
||||
yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor);
|
||||
if( yyact<YYNSTATE ){
|
||||
assert( !yyendofinput ); /* Impossible to shift the $ token */
|
||||
yy_shift(yypParser,yyact,yymajor,&yyminorunion);
|
||||
yypParser->yyerrcnt--;
|
||||
yymajor = YYNOCODE;
|
||||
|
||||
@ -84,6 +84,8 @@
|
||||
# define sqlite3_create_module 0
|
||||
# define sqlite3_create_module_v2 0
|
||||
# define sqlite3_declare_vtab 0
|
||||
# define sqlite3_vtab_config 0
|
||||
# define sqlite3_vtab_on_conflict 0
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_SHARED_CACHE
|
||||
@ -107,6 +109,7 @@
|
||||
#define sqlite3_blob_open 0
|
||||
#define sqlite3_blob_read 0
|
||||
#define sqlite3_blob_write 0
|
||||
#define sqlite3_blob_reopen 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -372,6 +375,9 @@ static const sqlite3_api_routines sqlite3Apis = {
|
||||
0,
|
||||
0,
|
||||
#endif
|
||||
sqlite3_blob_reopen,
|
||||
sqlite3_vtab_config,
|
||||
sqlite3_vtab_on_conflict,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -397,7 +403,7 @@ static int sqlite3LoadExtension(
|
||||
int (*xInit)(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
char *zErrmsg = 0;
|
||||
void **aHandle;
|
||||
const int nMsg = 300;
|
||||
int nMsg = 300 + sqlite3Strlen30(zFile);
|
||||
|
||||
if( pzErrMsg ) *pzErrMsg = 0;
|
||||
|
||||
@ -434,6 +440,7 @@ static int sqlite3LoadExtension(
|
||||
sqlite3OsDlSym(pVfs, handle, zProc);
|
||||
if( xInit==0 ){
|
||||
if( pzErrMsg ){
|
||||
nMsg += sqlite3Strlen30(zProc);
|
||||
*pzErrMsg = zErrmsg = sqlite3_malloc(nMsg);
|
||||
if( zErrmsg ){
|
||||
sqlite3_snprintf(nMsg, zErrmsg,
|
||||
|
||||
23
src/main.c
23
src/main.c
@ -106,7 +106,7 @@ char *sqlite3_temp_directory = 0;
|
||||
** without blocking.
|
||||
*/
|
||||
int sqlite3_initialize(void){
|
||||
sqlite3_mutex *pMaster; /* The main static mutex */
|
||||
MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */
|
||||
int rc; /* Result code */
|
||||
|
||||
#ifdef SQLITE_OMIT_WSD
|
||||
@ -140,7 +140,7 @@ int sqlite3_initialize(void){
|
||||
** malloc subsystem - this implies that the allocation of a static
|
||||
** mutex must not require support from the malloc subsystem.
|
||||
*/
|
||||
pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
MUTEX_LOGIC( pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); )
|
||||
sqlite3_mutex_enter(pMaster);
|
||||
sqlite3GlobalConfig.isMutexInit = 1;
|
||||
if( !sqlite3GlobalConfig.isMallocInit ){
|
||||
@ -234,6 +234,16 @@ int sqlite3_initialize(void){
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Do extra initialization steps requested by the SQLITE_EXTRA_INIT
|
||||
** compile-time option.
|
||||
*/
|
||||
#ifdef SQLITE_EXTRA_INIT
|
||||
if( rc==SQLITE_OK && sqlite3GlobalConfig.isInit ){
|
||||
int SQLITE_EXTRA_INIT(void);
|
||||
rc = SQLITE_EXTRA_INIT();
|
||||
}
|
||||
#endif
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -1204,13 +1214,13 @@ int sqlite3_overload_function(
|
||||
int nArg
|
||||
){
|
||||
int nName = sqlite3Strlen30(zName);
|
||||
int rc;
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
if( sqlite3FindFunction(db, zName, nName, nArg, SQLITE_UTF8, 0)==0 ){
|
||||
sqlite3CreateFunc(db, zName, nArg, SQLITE_UTF8,
|
||||
0, sqlite3InvalidFunction, 0, 0, 0);
|
||||
rc = sqlite3CreateFunc(db, zName, nArg, SQLITE_UTF8,
|
||||
0, sqlite3InvalidFunction, 0, 0, 0);
|
||||
}
|
||||
rc = sqlite3ApiExit(db, SQLITE_OK);
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
}
|
||||
@ -2272,6 +2282,7 @@ opendb_out:
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
}
|
||||
rc = sqlite3_errcode(db);
|
||||
assert( db!=0 || rc==SQLITE_NOMEM );
|
||||
if( rc==SQLITE_NOMEM ){
|
||||
sqlite3_close(db);
|
||||
db = 0;
|
||||
|
||||
@ -433,7 +433,7 @@ static void *memsys3MallocUnsafe(int nByte){
|
||||
** This function assumes that the necessary mutexes, if any, are
|
||||
** already held by the caller. Hence "Unsafe".
|
||||
*/
|
||||
void memsys3FreeUnsafe(void *pOld){
|
||||
static void memsys3FreeUnsafe(void *pOld){
|
||||
Mem3Block *p = (Mem3Block*)pOld;
|
||||
int i;
|
||||
u32 size, x;
|
||||
@ -508,7 +508,7 @@ static void *memsys3Malloc(int nBytes){
|
||||
/*
|
||||
** Free memory.
|
||||
*/
|
||||
void memsys3Free(void *pPrior){
|
||||
static void memsys3Free(void *pPrior){
|
||||
assert( pPrior );
|
||||
memsys3Enter();
|
||||
memsys3FreeUnsafe(pPrior);
|
||||
@ -518,7 +518,7 @@ void memsys3Free(void *pPrior){
|
||||
/*
|
||||
** Change the size of an existing memory allocation
|
||||
*/
|
||||
void *memsys3Realloc(void *pPrior, int nBytes){
|
||||
static void *memsys3Realloc(void *pPrior, int nBytes){
|
||||
int nOld;
|
||||
void *p;
|
||||
if( pPrior==0 ){
|
||||
|
||||
@ -60,12 +60,15 @@
|
||||
*/
|
||||
#define sqlite3_mutex_alloc(X) ((sqlite3_mutex*)8)
|
||||
#define sqlite3_mutex_free(X)
|
||||
#define sqlite3_mutex_enter(X)
|
||||
#define sqlite3_mutex_enter(X)
|
||||
#define sqlite3_mutex_try(X) SQLITE_OK
|
||||
#define sqlite3_mutex_leave(X)
|
||||
#define sqlite3_mutex_leave(X)
|
||||
#define sqlite3_mutex_held(X) ((void)(X),1)
|
||||
#define sqlite3_mutex_notheld(X) ((void)(X),1)
|
||||
#define sqlite3MutexAlloc(X) ((sqlite3_mutex*)8)
|
||||
#define sqlite3MutexInit() SQLITE_OK
|
||||
#define sqlite3MutexEnd()
|
||||
#define MUTEX_LOGIC(X)
|
||||
#else
|
||||
#define MUTEX_LOGIC(X) X
|
||||
#endif /* defined(SQLITE_MUTEX_OMIT) */
|
||||
|
||||
8
src/os.c
8
src/os.c
@ -136,7 +136,7 @@ int sqlite3OsOpen(
|
||||
** down into the VFS layer. Some SQLITE_OPEN_ flags (for example,
|
||||
** SQLITE_OPEN_FULLMUTEX or SQLITE_OPEN_SHAREDCACHE) are blocked before
|
||||
** reaching the VFS. */
|
||||
rc = pVfs->xOpen(pVfs, zPath, pFile, flags & 0x87f3f, pFlagsOut);
|
||||
rc = pVfs->xOpen(pVfs, zPath, pFile, flags & 0x87f7f, pFlagsOut);
|
||||
assert( rc==SQLITE_OK || pFile->pMethods==0 );
|
||||
return rc;
|
||||
}
|
||||
@ -208,7 +208,7 @@ int sqlite3OsOpenMalloc(
|
||||
){
|
||||
int rc = SQLITE_NOMEM;
|
||||
sqlite3_file *pFile;
|
||||
pFile = (sqlite3_file *)sqlite3Malloc(pVfs->szOsFile);
|
||||
pFile = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile);
|
||||
if( pFile ){
|
||||
rc = sqlite3OsOpen(pVfs, zFile, pFile, flags, pOutFlags);
|
||||
if( rc!=SQLITE_OK ){
|
||||
@ -297,12 +297,12 @@ static void vfsUnlink(sqlite3_vfs *pVfs){
|
||||
** true.
|
||||
*/
|
||||
int sqlite3_vfs_register(sqlite3_vfs *pVfs, int makeDflt){
|
||||
sqlite3_mutex *mutex = 0;
|
||||
MUTEX_LOGIC(sqlite3_mutex *mutex;)
|
||||
#ifndef SQLITE_OMIT_AUTOINIT
|
||||
int rc = sqlite3_initialize();
|
||||
if( rc ) return rc;
|
||||
#endif
|
||||
mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
MUTEX_LOGIC( mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); )
|
||||
sqlite3_mutex_enter(mutex);
|
||||
vfsUnlink(pVfs);
|
||||
if( makeDflt || vfsList==0 ){
|
||||
|
||||
@ -29,11 +29,14 @@
|
||||
# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
int sqlite3OSTrace = 0;
|
||||
#define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X
|
||||
#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
|
||||
# ifndef SQLITE_DEBUG_OS_TRACE
|
||||
# define SQLITE_DEBUG_OS_TRACE 0
|
||||
# endif
|
||||
int sqlite3OSTrace = SQLITE_DEBUG_OS_TRACE;
|
||||
# define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X
|
||||
#else
|
||||
#define OSTRACE(X)
|
||||
# define OSTRACE(X)
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
||||
356
src/os_unix.c
356
src/os_unix.c
@ -208,7 +208,6 @@ struct unixFile {
|
||||
sqlite3_io_methods const *pMethod; /* Always the first entry */
|
||||
unixInodeInfo *pInode; /* Info about locks on this inode */
|
||||
int h; /* The file descriptor */
|
||||
int dirfd; /* File descriptor for the directory */
|
||||
unsigned char eFileLock; /* The type of lock held on this fd */
|
||||
unsigned char ctrlFlags; /* Behavioral bits. UNIXFILE_* flags */
|
||||
int lastErrno; /* The unix errno from last I/O error */
|
||||
@ -250,8 +249,14 @@ struct unixFile {
|
||||
/*
|
||||
** Allowed values for the unixFile.ctrlFlags bitmask:
|
||||
*/
|
||||
#define UNIXFILE_EXCL 0x01 /* Connections from one process only */
|
||||
#define UNIXFILE_RDONLY 0x02 /* Connection is read only */
|
||||
#define UNIXFILE_EXCL 0x01 /* Connections from one process only */
|
||||
#define UNIXFILE_RDONLY 0x02 /* Connection is read only */
|
||||
#define UNIXFILE_PERSIST_WAL 0x04 /* Persistent WAL mode */
|
||||
#ifndef SQLITE_DISABLE_DIRSYNC
|
||||
# define UNIXFILE_DIRSYNC 0x08 /* Directory sync needed */
|
||||
#else
|
||||
# define UNIXFILE_DIRSYNC 0x00
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Include code that is common to all os_*.c files
|
||||
@ -297,6 +302,9 @@ static int posixOpen(const char *zFile, int flags, int mode){
|
||||
return open(zFile, flags, mode);
|
||||
}
|
||||
|
||||
/* Forward reference */
|
||||
static int openDirectory(const char*, int*);
|
||||
|
||||
/*
|
||||
** Many system calls are accessed through pointer-to-functions so that
|
||||
** they may be overridden at runtime to facilitate fault injection during
|
||||
@ -393,6 +401,12 @@ static struct unix_syscall {
|
||||
#endif
|
||||
#define osFallocate ((int(*)(int,off_t,off_t))aSyscall[15].pCurrent)
|
||||
|
||||
{ "unlink", (sqlite3_syscall_ptr)unlink, 0 },
|
||||
#define osUnlink ((int(*)(const char*))aSyscall[16].pCurrent)
|
||||
|
||||
{ "openDirectory", (sqlite3_syscall_ptr)openDirectory, 0 },
|
||||
#define osOpenDirectory ((int(*)(const char*,int*))aSyscall[17].pCurrent)
|
||||
|
||||
}; /* End of the overrideable system calls */
|
||||
|
||||
/*
|
||||
@ -514,7 +528,7 @@ static int unixMutexHeld(void) {
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
|
||||
/*
|
||||
** Helper function for printing out trace information from debugging
|
||||
** binaries. This returns the string represetation of the supplied
|
||||
@ -677,7 +691,9 @@ static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) {
|
||||
case ENODEV:
|
||||
case ENXIO:
|
||||
case ENOENT:
|
||||
#ifdef ESTALE /* ESTALE is not defined on Interix systems */
|
||||
case ESTALE:
|
||||
#endif
|
||||
case ENOSYS:
|
||||
/* these should force the client to close the file and reconnect */
|
||||
|
||||
@ -1347,14 +1363,14 @@ static int unixLock(sqlite3_file *id, int eFileLock){
|
||||
*/
|
||||
int rc = SQLITE_OK;
|
||||
unixFile *pFile = (unixFile*)id;
|
||||
unixInodeInfo *pInode = pFile->pInode;
|
||||
unixInodeInfo *pInode;
|
||||
struct flock lock;
|
||||
int tErrno = 0;
|
||||
|
||||
assert( pFile );
|
||||
OSTRACE(("LOCK %d %s was %s(%s,%d) pid=%d (unix)\n", pFile->h,
|
||||
azFileLock(eFileLock), azFileLock(pFile->eFileLock),
|
||||
azFileLock(pInode->eFileLock), pInode->nShared , getpid()));
|
||||
azFileLock(pFile->pInode->eFileLock), pFile->pInode->nShared , getpid()));
|
||||
|
||||
/* If there is already a lock of this type or more restrictive on the
|
||||
** unixFile, do nothing. Don't use the end_lock: exit path, as
|
||||
@ -1558,7 +1574,6 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
|
||||
unixInodeInfo *pInode;
|
||||
struct flock lock;
|
||||
int rc = SQLITE_OK;
|
||||
int h;
|
||||
|
||||
assert( pFile );
|
||||
OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (unix)\n", pFile->h, eFileLock,
|
||||
@ -1570,14 +1585,10 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
unixEnterMutex();
|
||||
h = pFile->h;
|
||||
pInode = pFile->pInode;
|
||||
assert( pInode->nShared!=0 );
|
||||
if( pFile->eFileLock>SHARED_LOCK ){
|
||||
assert( pInode->eFileLock==pFile->eFileLock );
|
||||
SimulateIOErrorBenign(1);
|
||||
SimulateIOError( h=(-1) )
|
||||
SimulateIOErrorBenign(0);
|
||||
|
||||
#ifndef NDEBUG
|
||||
/* When reducing a lock such that other processes can start
|
||||
@ -1588,11 +1599,6 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
|
||||
** the file has changed and hence might not know to flush their
|
||||
** cache. The use of a stale cache can lead to database corruption.
|
||||
*/
|
||||
#if 0
|
||||
assert( pFile->inNormalWrite==0
|
||||
|| pFile->dbUpdate==0
|
||||
|| pFile->transCntrChng==1 );
|
||||
#endif
|
||||
pFile->inNormalWrite = 0;
|
||||
#endif
|
||||
|
||||
@ -1694,9 +1700,6 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
|
||||
lock.l_type = F_UNLCK;
|
||||
lock.l_whence = SEEK_SET;
|
||||
lock.l_start = lock.l_len = 0L;
|
||||
SimulateIOErrorBenign(1);
|
||||
SimulateIOError( h=(-1) )
|
||||
SimulateIOErrorBenign(0);
|
||||
if( unixFileLock(pFile, &lock)==0 ){
|
||||
pInode->eFileLock = NO_LOCK;
|
||||
}else{
|
||||
@ -1747,10 +1750,6 @@ static int unixUnlock(sqlite3_file *id, int eFileLock){
|
||||
*/
|
||||
static int closeUnixFile(sqlite3_file *id){
|
||||
unixFile *pFile = (unixFile*)id;
|
||||
if( pFile->dirfd>=0 ){
|
||||
robust_close(pFile, pFile->dirfd, __LINE__);
|
||||
pFile->dirfd=-1;
|
||||
}
|
||||
if( pFile->h>=0 ){
|
||||
robust_close(pFile, pFile->h, __LINE__);
|
||||
pFile->h = -1;
|
||||
@ -1758,7 +1757,7 @@ static int closeUnixFile(sqlite3_file *id){
|
||||
#if OS_VXWORKS
|
||||
if( pFile->pId ){
|
||||
if( pFile->isDelete ){
|
||||
unlink(pFile->pId->zCanonicalName);
|
||||
osUnlink(pFile->pId->zCanonicalName);
|
||||
}
|
||||
vxworksReleaseFileId(pFile->pId);
|
||||
pFile->pId = 0;
|
||||
@ -2007,7 +2006,7 @@ static int dotlockUnlock(sqlite3_file *id, int eFileLock) {
|
||||
|
||||
/* To fully unlock the database, delete the lock file */
|
||||
assert( eFileLock==NO_LOCK );
|
||||
if( unlink(zLockFile) ){
|
||||
if( osUnlink(zLockFile) ){
|
||||
int rc = 0;
|
||||
int tErrno = errno;
|
||||
if( ENOENT != tErrno ){
|
||||
@ -2513,11 +2512,12 @@ static int afpCheckReservedLock(sqlite3_file *id, int *pResOut){
|
||||
int rc = SQLITE_OK;
|
||||
int reserved = 0;
|
||||
unixFile *pFile = (unixFile*)id;
|
||||
afpLockingContext *context;
|
||||
|
||||
SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
|
||||
|
||||
assert( pFile );
|
||||
afpLockingContext *context = (afpLockingContext *) pFile->lockingContext;
|
||||
context = (afpLockingContext *) pFile->lockingContext;
|
||||
if( context->reserved ){
|
||||
*pResOut = 1;
|
||||
return SQLITE_OK;
|
||||
@ -2657,7 +2657,7 @@ static int afpLock(sqlite3_file *id, int eFileLock){
|
||||
** operating system calls for the specified lock.
|
||||
*/
|
||||
if( eFileLock==SHARED_LOCK ){
|
||||
int lrc1, lrc2, lrc1Errno;
|
||||
int lrc1, lrc2, lrc1Errno = 0;
|
||||
long lk, mask;
|
||||
|
||||
assert( pInode->nShared==0 );
|
||||
@ -3031,17 +3031,19 @@ static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){
|
||||
#elif defined(USE_PREAD64)
|
||||
do{ got = osPwrite64(id->h, pBuf, cnt, offset);}while( got<0 && errno==EINTR);
|
||||
#else
|
||||
newOffset = lseek(id->h, offset, SEEK_SET);
|
||||
SimulateIOError( newOffset-- );
|
||||
if( newOffset!=offset ){
|
||||
if( newOffset == -1 ){
|
||||
((unixFile*)id)->lastErrno = errno;
|
||||
}else{
|
||||
((unixFile*)id)->lastErrno = 0;
|
||||
do{
|
||||
newOffset = lseek(id->h, offset, SEEK_SET);
|
||||
SimulateIOError( newOffset-- );
|
||||
if( newOffset!=offset ){
|
||||
if( newOffset == -1 ){
|
||||
((unixFile*)id)->lastErrno = errno;
|
||||
}else{
|
||||
((unixFile*)id)->lastErrno = 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
do{ got = osWrite(id->h, pBuf, cnt); }while( got<0 && errno==EINTR );
|
||||
got = osWrite(id->h, pBuf, cnt);
|
||||
}while( got<0 && errno==EINTR );
|
||||
#endif
|
||||
TIMER_END;
|
||||
if( got<0 ){
|
||||
@ -3131,11 +3133,11 @@ int sqlite3_fullsync_count = 0;
|
||||
|
||||
/*
|
||||
** We do not trust systems to provide a working fdatasync(). Some do.
|
||||
** Others do no. To be safe, we will stick with the (slower) fsync().
|
||||
** If you know that your system does support fdatasync() correctly,
|
||||
** Others do no. To be safe, we will stick with the (slightly slower)
|
||||
** fsync(). If you know that your system does support fdatasync() correctly,
|
||||
** then simply compile with -Dfdatasync=fdatasync
|
||||
*/
|
||||
#if !defined(fdatasync) && !defined(__linux__)
|
||||
#if !defined(fdatasync)
|
||||
# define fdatasync fsync
|
||||
#endif
|
||||
|
||||
@ -3243,6 +3245,50 @@ static int full_fsync(int fd, int fullSync, int dataOnly){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a file descriptor to the directory containing file zFilename.
|
||||
** If successful, *pFd is set to the opened file descriptor and
|
||||
** SQLITE_OK is returned. If an error occurs, either SQLITE_NOMEM
|
||||
** or SQLITE_CANTOPEN is returned and *pFd is set to an undefined
|
||||
** value.
|
||||
**
|
||||
** The directory file descriptor is used for only one thing - to
|
||||
** fsync() a directory to make sure file creation and deletion events
|
||||
** are flushed to disk. Such fsyncs are not needed on newer
|
||||
** journaling filesystems, but are required on older filesystems.
|
||||
**
|
||||
** This routine can be overridden using the xSetSysCall interface.
|
||||
** The ability to override this routine was added in support of the
|
||||
** chromium sandbox. Opening a directory is a security risk (we are
|
||||
** told) so making it overrideable allows the chromium sandbox to
|
||||
** replace this routine with a harmless no-op. To make this routine
|
||||
** a no-op, replace it with a stub that returns SQLITE_OK but leaves
|
||||
** *pFd set to a negative number.
|
||||
**
|
||||
** If SQLITE_OK is returned, the caller is responsible for closing
|
||||
** the file descriptor *pFd using close().
|
||||
*/
|
||||
static int openDirectory(const char *zFilename, int *pFd){
|
||||
int ii;
|
||||
int fd = -1;
|
||||
char zDirname[MAX_PATHNAME+1];
|
||||
|
||||
sqlite3_snprintf(MAX_PATHNAME, zDirname, "%s", zFilename);
|
||||
for(ii=(int)strlen(zDirname); ii>1 && zDirname[ii]!='/'; ii--);
|
||||
if( ii>0 ){
|
||||
zDirname[ii] = '\0';
|
||||
fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0);
|
||||
if( fd>=0 ){
|
||||
#ifdef FD_CLOEXEC
|
||||
osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
|
||||
#endif
|
||||
OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname));
|
||||
}
|
||||
}
|
||||
*pFd = fd;
|
||||
return (fd>=0?SQLITE_OK:unixLogError(SQLITE_CANTOPEN_BKPT, "open", zDirname));
|
||||
}
|
||||
|
||||
/*
|
||||
** Make sure all writes to a particular file are committed to disk.
|
||||
**
|
||||
@ -3283,28 +3329,23 @@ static int unixSync(sqlite3_file *id, int flags){
|
||||
pFile->lastErrno = errno;
|
||||
return unixLogError(SQLITE_IOERR_FSYNC, "full_fsync", pFile->zPath);
|
||||
}
|
||||
if( pFile->dirfd>=0 ){
|
||||
OSTRACE(("DIRSYNC %-3d (have_fullfsync=%d fullsync=%d)\n", pFile->dirfd,
|
||||
|
||||
/* Also fsync the directory containing the file if the DIRSYNC flag
|
||||
** is set. This is a one-time occurrance. Many systems (examples: AIX)
|
||||
** are unable to fsync a directory, so ignore errors on the fsync.
|
||||
*/
|
||||
if( pFile->ctrlFlags & UNIXFILE_DIRSYNC ){
|
||||
int dirfd;
|
||||
OSTRACE(("DIRSYNC %s (have_fullfsync=%d fullsync=%d)\n", pFile->zPath,
|
||||
HAVE_FULLFSYNC, isFullsync));
|
||||
#ifndef SQLITE_DISABLE_DIRSYNC
|
||||
/* The directory sync is only attempted if full_fsync is
|
||||
** turned off or unavailable. If a full_fsync occurred above,
|
||||
** then the directory sync is superfluous.
|
||||
*/
|
||||
if( (!HAVE_FULLFSYNC || !isFullsync) && full_fsync(pFile->dirfd,0,0) ){
|
||||
/*
|
||||
** We have received multiple reports of fsync() returning
|
||||
** errors when applied to directories on certain file systems.
|
||||
** A failed directory sync is not a big deal. So it seems
|
||||
** better to ignore the error. Ticket #1657
|
||||
*/
|
||||
/* pFile->lastErrno = errno; */
|
||||
/* return SQLITE_IOERR; */
|
||||
rc = osOpenDirectory(pFile->zPath, &dirfd);
|
||||
if( rc==SQLITE_OK && dirfd>=0 ){
|
||||
full_fsync(dirfd, 0, 0);
|
||||
robust_close(pFile, dirfd, __LINE__);
|
||||
}else if( rc==SQLITE_CANTOPEN ){
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
#endif
|
||||
/* Only need to sync once, so close the directory when we are done */
|
||||
robust_close(pFile, pFile->dirfd, __LINE__);
|
||||
pFile->dirfd = -1;
|
||||
pFile->ctrlFlags &= ~UNIXFILE_DIRSYNC;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@ -3386,14 +3427,12 @@ static int proxyFileControl(sqlite3_file*,int,void*);
|
||||
|
||||
/*
|
||||
** This function is called to handle the SQLITE_FCNTL_SIZE_HINT
|
||||
** file-control operation.
|
||||
**
|
||||
** If the user has configured a chunk-size for this file, it could be
|
||||
** that the file needs to be extended at this point. Otherwise, the
|
||||
** SQLITE_FCNTL_SIZE_HINT operation is a no-op for Unix.
|
||||
** file-control operation. Enlarge the database to nBytes in size
|
||||
** (rounded up to the next chunk-size). If the database is already
|
||||
** nBytes or larger, this routine is a no-op.
|
||||
*/
|
||||
static int fcntlSizeHint(unixFile *pFile, i64 nByte){
|
||||
if( pFile->szChunk ){
|
||||
if( pFile->szChunk>0 ){
|
||||
i64 nSize; /* Required file size */
|
||||
struct stat buf; /* Used to hold return values of fstat() */
|
||||
|
||||
@ -3442,21 +3481,37 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){
|
||||
** Information and control of an open file handle.
|
||||
*/
|
||||
static int unixFileControl(sqlite3_file *id, int op, void *pArg){
|
||||
unixFile *pFile = (unixFile*)id;
|
||||
switch( op ){
|
||||
case SQLITE_FCNTL_LOCKSTATE: {
|
||||
*(int*)pArg = ((unixFile*)id)->eFileLock;
|
||||
*(int*)pArg = pFile->eFileLock;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
case SQLITE_LAST_ERRNO: {
|
||||
*(int*)pArg = ((unixFile*)id)->lastErrno;
|
||||
*(int*)pArg = pFile->lastErrno;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
case SQLITE_FCNTL_CHUNK_SIZE: {
|
||||
((unixFile*)id)->szChunk = *(int *)pArg;
|
||||
pFile->szChunk = *(int *)pArg;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
case SQLITE_FCNTL_SIZE_HINT: {
|
||||
return fcntlSizeHint((unixFile *)id, *(i64 *)pArg);
|
||||
int rc;
|
||||
SimulateIOErrorBenign(1);
|
||||
rc = fcntlSizeHint(pFile, *(i64 *)pArg);
|
||||
SimulateIOErrorBenign(0);
|
||||
return rc;
|
||||
}
|
||||
case SQLITE_FCNTL_PERSIST_WAL: {
|
||||
int bPersist = *(int*)pArg;
|
||||
if( bPersist<0 ){
|
||||
*(int*)pArg = (pFile->ctrlFlags & UNIXFILE_PERSIST_WAL)!=0;
|
||||
}else if( bPersist==0 ){
|
||||
pFile->ctrlFlags &= ~UNIXFILE_PERSIST_WAL;
|
||||
}else{
|
||||
pFile->ctrlFlags |= UNIXFILE_PERSIST_WAL;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
/* The pager calls this method to signal that it has done
|
||||
@ -3572,11 +3627,9 @@ struct unixShm {
|
||||
unixShmNode *pShmNode; /* The underlying unixShmNode object */
|
||||
unixShm *pNext; /* Next unixShm with the same unixShmNode */
|
||||
u8 hasMutex; /* True if holding the unixShmNode mutex */
|
||||
u8 id; /* Id of this connection within its unixShmNode */
|
||||
u16 sharedMask; /* Mask of shared locks held */
|
||||
u16 exclMask; /* Mask of exclusive locks held */
|
||||
#ifdef SQLITE_DEBUG
|
||||
u8 id; /* Id of this connection within its unixShmNode */
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
@ -3672,7 +3725,7 @@ static void unixShmPurge(unixFile *pFd){
|
||||
if( p && p->nRef==0 ){
|
||||
int i;
|
||||
assert( p->pInode==pFd->pInode );
|
||||
if( p->mutex ) sqlite3_mutex_free(p->mutex);
|
||||
sqlite3_mutex_free(p->mutex);
|
||||
for(i=0; i<p->nRegion; i++){
|
||||
if( p->h>=0 ){
|
||||
munmap(p->apRegion[i], p->szRegion);
|
||||
@ -3788,16 +3841,15 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
|
||||
}
|
||||
|
||||
if( pInode->bProcessLock==0 ){
|
||||
pShmNode->h = robust_open(zShmFilename, O_RDWR|O_CREAT,
|
||||
(sStat.st_mode & 0777));
|
||||
const char *zRO;
|
||||
int openFlags = O_RDWR | O_CREAT;
|
||||
zRO = sqlite3_uri_parameter(pDbFd->zPath, "readonly_shm");
|
||||
if( zRO && sqlite3GetBoolean(zRO) ){
|
||||
openFlags = O_RDONLY;
|
||||
pShmNode->isReadonly = 1;
|
||||
}
|
||||
pShmNode->h = robust_open(zShmFilename, openFlags, (sStat.st_mode&0777));
|
||||
if( pShmNode->h<0 ){
|
||||
const char *zRO;
|
||||
zRO = sqlite3_uri_parameter(pDbFd->zPath, "readonly_shm");
|
||||
if( zRO && sqlite3GetBoolean(zRO) ){
|
||||
pShmNode->h = robust_open(zShmFilename, O_RDONLY,
|
||||
(sStat.st_mode & 0777));
|
||||
pShmNode->isReadonly = 1;
|
||||
}
|
||||
if( pShmNode->h<0 ){
|
||||
rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename);
|
||||
goto shm_open_err;
|
||||
@ -4141,7 +4193,7 @@ static int unixShmUnmap(
|
||||
assert( pShmNode->nRef>0 );
|
||||
pShmNode->nRef--;
|
||||
if( pShmNode->nRef==0 ){
|
||||
if( deleteFlag && pShmNode->h>=0 ) unlink(pShmNode->zFilename);
|
||||
if( deleteFlag && pShmNode->h>=0 ) osUnlink(pShmNode->zFilename);
|
||||
unixShmPurge(pDbFd);
|
||||
}
|
||||
unixLeaveMutex();
|
||||
@ -4454,7 +4506,7 @@ typedef const sqlite3_io_methods *(*finder_type)(const char*,unixFile*);
|
||||
static int fillInUnixFile(
|
||||
sqlite3_vfs *pVfs, /* Pointer to vfs object */
|
||||
int h, /* Open file descriptor of file being opened */
|
||||
int dirfd, /* Directory file descriptor */
|
||||
int syncDir, /* True to sync directory on first sync */
|
||||
sqlite3_file *pId, /* Write to the unixFile structure here */
|
||||
const char *zFilename, /* Name of the file being opened */
|
||||
int noLock, /* Omit locking if true */
|
||||
@ -4483,9 +4535,11 @@ static int fillInUnixFile(
|
||||
assert( zFilename==0 || zFilename[0]=='/' );
|
||||
#endif
|
||||
|
||||
/* No locking occurs in temporary files */
|
||||
assert( zFilename!=0 || noLock );
|
||||
|
||||
OSTRACE(("OPEN %-3d %s\n", h, zFilename));
|
||||
pNew->h = h;
|
||||
pNew->dirfd = dirfd;
|
||||
pNew->zPath = zFilename;
|
||||
if( memcmp(pVfs->zName,"unix-excl",10)==0 ){
|
||||
pNew->ctrlFlags = UNIXFILE_EXCL;
|
||||
@ -4495,6 +4549,9 @@ static int fillInUnixFile(
|
||||
if( isReadOnly ){
|
||||
pNew->ctrlFlags |= UNIXFILE_RDONLY;
|
||||
}
|
||||
if( syncDir ){
|
||||
pNew->ctrlFlags |= UNIXFILE_DIRSYNC;
|
||||
}
|
||||
|
||||
#if OS_VXWORKS
|
||||
pNew->pId = vxworksFindFileId(zFilename);
|
||||
@ -4582,6 +4639,7 @@ static int fillInUnixFile(
|
||||
*/
|
||||
char *zLockFile;
|
||||
int nFilename;
|
||||
assert( zFilename!=0 );
|
||||
nFilename = (int)strlen(zFilename) + 6;
|
||||
zLockFile = (char *)sqlite3_malloc(nFilename);
|
||||
if( zLockFile==0 ){
|
||||
@ -4621,13 +4679,12 @@ static int fillInUnixFile(
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( h>=0 ) robust_close(pNew, h, __LINE__);
|
||||
h = -1;
|
||||
unlink(zFilename);
|
||||
osUnlink(zFilename);
|
||||
isDelete = 0;
|
||||
}
|
||||
pNew->isDelete = isDelete;
|
||||
#endif
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( dirfd>=0 ) robust_close(pNew, dirfd, __LINE__);
|
||||
if( h>=0 ) robust_close(pNew, h, __LINE__);
|
||||
}else{
|
||||
pNew->pMethod = pLockingStyle;
|
||||
@ -4636,37 +4693,6 @@ static int fillInUnixFile(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a file descriptor to the directory containing file zFilename.
|
||||
** If successful, *pFd is set to the opened file descriptor and
|
||||
** SQLITE_OK is returned. If an error occurs, either SQLITE_NOMEM
|
||||
** or SQLITE_CANTOPEN is returned and *pFd is set to an undefined
|
||||
** value.
|
||||
**
|
||||
** If SQLITE_OK is returned, the caller is responsible for closing
|
||||
** the file descriptor *pFd using close().
|
||||
*/
|
||||
static int openDirectory(const char *zFilename, int *pFd){
|
||||
int ii;
|
||||
int fd = -1;
|
||||
char zDirname[MAX_PATHNAME+1];
|
||||
|
||||
sqlite3_snprintf(MAX_PATHNAME, zDirname, "%s", zFilename);
|
||||
for(ii=(int)strlen(zDirname); ii>1 && zDirname[ii]!='/'; ii--);
|
||||
if( ii>0 ){
|
||||
zDirname[ii] = '\0';
|
||||
fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0);
|
||||
if( fd>=0 ){
|
||||
#ifdef FD_CLOEXEC
|
||||
osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
|
||||
#endif
|
||||
OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname));
|
||||
}
|
||||
}
|
||||
*pFd = fd;
|
||||
return (fd>=0?SQLITE_OK:unixLogError(SQLITE_CANTOPEN_BKPT, "open", zDirname));
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the name of a directory in which to put temporary files.
|
||||
** If no suitable temporary file directory can be found, return NULL.
|
||||
@ -4781,7 +4807,7 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
|
||||
**
|
||||
** Even if a subsequent open() call does succeed, the consequences of
|
||||
** not searching for a resusable file descriptor are not dire. */
|
||||
if( 0==stat(zPath, &sStat) ){
|
||||
if( 0==osStat(zPath, &sStat) ){
|
||||
unixInodeInfo *pInode;
|
||||
|
||||
unixEnterMutex();
|
||||
@ -4848,16 +4874,24 @@ static int findCreateFileMode(
|
||||
** "<path to db>-journalNN"
|
||||
** "<path to db>-walNN"
|
||||
**
|
||||
** where NN is a 4 digit decimal number. The NN naming schemes are
|
||||
** where NN is a decimal number. The NN naming schemes are
|
||||
** used by the test_multiplex.c module.
|
||||
*/
|
||||
nDb = sqlite3Strlen30(zPath) - 1;
|
||||
while( nDb>0 && zPath[nDb]!='-' ) nDb--;
|
||||
if( nDb==0 ) return SQLITE_OK;
|
||||
#ifdef SQLITE_ENABLE_8_3_NAMES
|
||||
while( nDb>0 && !sqlite3Isalnum(zPath[nDb]) ) nDb--;
|
||||
if( nDb==0 || zPath[nDb]!='-' ) return SQLITE_OK;
|
||||
#else
|
||||
while( zPath[nDb]!='-' ){
|
||||
assert( nDb>0 );
|
||||
assert( zPath[nDb]!='\n' );
|
||||
nDb--;
|
||||
}
|
||||
#endif
|
||||
memcpy(zDb, zPath, nDb);
|
||||
zDb[nDb] = '\0';
|
||||
|
||||
if( 0==stat(zDb, &sStat) ){
|
||||
if( 0==osStat(zDb, &sStat) ){
|
||||
*pMode = sStat.st_mode & 0777;
|
||||
}else{
|
||||
rc = SQLITE_IOERR_FSTAT;
|
||||
@ -4899,7 +4933,6 @@ static int unixOpen(
|
||||
){
|
||||
unixFile *p = (unixFile *)pFile;
|
||||
int fd = -1; /* File descriptor returned by open() */
|
||||
int dirfd = -1; /* Directory file descriptor */
|
||||
int openFlags = 0; /* Flags to pass to open() */
|
||||
int eType = flags&0xFFFFFF00; /* Type of file to open */
|
||||
int noLock; /* True to omit locking primitives */
|
||||
@ -4913,12 +4946,15 @@ static int unixOpen(
|
||||
#if SQLITE_ENABLE_LOCKING_STYLE
|
||||
int isAutoProxy = (flags & SQLITE_OPEN_AUTOPROXY);
|
||||
#endif
|
||||
#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE
|
||||
struct statfs fsInfo;
|
||||
#endif
|
||||
|
||||
/* If creating a master or main-file journal, this function will open
|
||||
** a file-descriptor on the directory too. The first time unixSync()
|
||||
** is called the directory file descriptor will be fsync()ed and close()d.
|
||||
*/
|
||||
int isOpenDirectory = (isCreate && (
|
||||
int syncDir = (isCreate && (
|
||||
eType==SQLITE_OPEN_MASTER_JOURNAL
|
||||
|| eType==SQLITE_OPEN_MAIN_JOURNAL
|
||||
|| eType==SQLITE_OPEN_WAL
|
||||
@ -4972,7 +5008,7 @@ static int unixOpen(
|
||||
p->pUnused = pUnused;
|
||||
}else if( !zName ){
|
||||
/* If zName is NULL, the upper layer is requesting a temp file. */
|
||||
assert(isDelete && !isOpenDirectory);
|
||||
assert(isDelete && !syncDir);
|
||||
rc = unixGetTempname(MAX_PATHNAME+1, zTmpname);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
@ -5028,7 +5064,7 @@ static int unixOpen(
|
||||
#if OS_VXWORKS
|
||||
zPath = zName;
|
||||
#else
|
||||
unlink(zName);
|
||||
osUnlink(zName);
|
||||
#endif
|
||||
}
|
||||
#if SQLITE_ENABLE_LOCKING_STYLE
|
||||
@ -5037,19 +5073,6 @@ static int unixOpen(
|
||||
}
|
||||
#endif
|
||||
|
||||
if( isOpenDirectory ){
|
||||
rc = openDirectory(zPath, &dirfd);
|
||||
if( rc!=SQLITE_OK ){
|
||||
/* It is safe to close fd at this point, because it is guaranteed not
|
||||
** to be open on a database file. If it were open on a database file,
|
||||
** it would not be safe to close as this would release any locks held
|
||||
** on the file by this process. */
|
||||
assert( eType!=SQLITE_OPEN_MAIN_DB );
|
||||
robust_close(p, fd, __LINE__);
|
||||
goto open_finished;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef FD_CLOEXEC
|
||||
osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
|
||||
#endif
|
||||
@ -5058,10 +5081,8 @@ static int unixOpen(
|
||||
|
||||
|
||||
#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE
|
||||
struct statfs fsInfo;
|
||||
if( fstatfs(fd, &fsInfo) == -1 ){
|
||||
((unixFile*)pFile)->lastErrno = errno;
|
||||
if( dirfd>=0 ) robust_close(p, dirfd, __LINE__);
|
||||
robust_close(p, fd, __LINE__);
|
||||
return SQLITE_IOERR_ACCESS;
|
||||
}
|
||||
@ -5083,7 +5104,6 @@ static int unixOpen(
|
||||
if( envforce!=NULL ){
|
||||
useProxy = atoi(envforce)>0;
|
||||
}else{
|
||||
struct statfs fsInfo;
|
||||
if( statfs(zPath, &fsInfo) == -1 ){
|
||||
/* In theory, the close(fd) call is sub-optimal. If the file opened
|
||||
** with fd is a database file, and there are other connections open
|
||||
@ -5093,9 +5113,6 @@ static int unixOpen(
|
||||
** not while other file descriptors opened by the same process on
|
||||
** the same file are working. */
|
||||
p->lastErrno = errno;
|
||||
if( dirfd>=0 ){
|
||||
robust_close(p, dirfd, __LINE__);
|
||||
}
|
||||
robust_close(p, fd, __LINE__);
|
||||
rc = SQLITE_IOERR_ACCESS;
|
||||
goto open_finished;
|
||||
@ -5103,7 +5120,7 @@ static int unixOpen(
|
||||
useProxy = !(fsInfo.f_flags&MNT_LOCAL);
|
||||
}
|
||||
if( useProxy ){
|
||||
rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock,
|
||||
rc = fillInUnixFile(pVfs, fd, syncDir, pFile, zPath, noLock,
|
||||
isDelete, isReadonly);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = proxyTransformUnixFile((unixFile*)pFile, ":auto:");
|
||||
@ -5121,7 +5138,7 @@ static int unixOpen(
|
||||
}
|
||||
#endif
|
||||
|
||||
rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock,
|
||||
rc = fillInUnixFile(pVfs, fd, syncDir, pFile, zPath, noLock,
|
||||
isDelete, isReadonly);
|
||||
open_finished:
|
||||
if( rc!=SQLITE_OK ){
|
||||
@ -5143,13 +5160,13 @@ static int unixDelete(
|
||||
int rc = SQLITE_OK;
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
SimulateIOError(return SQLITE_IOERR_DELETE);
|
||||
if( unlink(zPath)==(-1) && errno!=ENOENT ){
|
||||
if( osUnlink(zPath)==(-1) && errno!=ENOENT ){
|
||||
return unixLogError(SQLITE_IOERR_DELETE, "unlink", zPath);
|
||||
}
|
||||
#ifndef SQLITE_DISABLE_DIRSYNC
|
||||
if( dirSync ){
|
||||
int fd;
|
||||
rc = openDirectory(zPath, &fd);
|
||||
rc = osOpenDirectory(zPath, &fd);
|
||||
if( rc==SQLITE_OK ){
|
||||
#if OS_VXWORKS
|
||||
if( fsync(fd)==-1 )
|
||||
@ -5160,6 +5177,8 @@ static int unixDelete(
|
||||
rc = unixLogError(SQLITE_IOERR_DIR_FSYNC, "fsync", zPath);
|
||||
}
|
||||
robust_close(0, fd, __LINE__);
|
||||
}else if( rc==SQLITE_CANTOPEN ){
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -5202,7 +5221,7 @@ static int unixAccess(
|
||||
*pResOut = (osAccess(zPath, amode)==0);
|
||||
if( flags==SQLITE_ACCESS_EXISTS && *pResOut ){
|
||||
struct stat buf;
|
||||
if( 0==stat(zPath, &buf) && buf.st_size==0 ){
|
||||
if( 0==osStat(zPath, &buf) && buf.st_size==0 ){
|
||||
*pResOut = 0;
|
||||
}
|
||||
}
|
||||
@ -5400,10 +5419,12 @@ int sqlite3_current_time = 0; /* Fake system time in seconds since 1970. */
|
||||
** epoch of noon in Greenwich on November 24, 4714 B.C according to the
|
||||
** proleptic Gregorian calendar.
|
||||
**
|
||||
** On success, return 0. Return 1 if the time and date cannot be found.
|
||||
** On success, return SQLITE_OK. Return SQLITE_ERROR if the time and date
|
||||
** cannot be found.
|
||||
*/
|
||||
static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){
|
||||
static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000;
|
||||
int rc = SQLITE_OK;
|
||||
#if defined(NO_GETTOD)
|
||||
time_t t;
|
||||
time(&t);
|
||||
@ -5414,8 +5435,11 @@ static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){
|
||||
*piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_nsec/1000000;
|
||||
#else
|
||||
struct timeval sNow;
|
||||
gettimeofday(&sNow, 0);
|
||||
*piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000;
|
||||
if( gettimeofday(&sNow, 0)==0 ){
|
||||
*piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000;
|
||||
}else{
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
@ -5424,7 +5448,7 @@ static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){
|
||||
}
|
||||
#endif
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -5433,11 +5457,12 @@ static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){
|
||||
** return 0. Return 1 if the time and date cannot be found.
|
||||
*/
|
||||
static int unixCurrentTime(sqlite3_vfs *NotUsed, double *prNow){
|
||||
sqlite3_int64 i;
|
||||
sqlite3_int64 i = 0;
|
||||
int rc;
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
unixCurrentTimeInt64(0, &i);
|
||||
rc = unixCurrentTimeInt64(0, &i);
|
||||
*prNow = i/86400000.0;
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -5721,7 +5746,6 @@ static int proxyCreateUnixFile(
|
||||
int islockfile /* if non zero missing dirs will be created */
|
||||
) {
|
||||
int fd = -1;
|
||||
int dirfd = -1;
|
||||
unixFile *pNew;
|
||||
int rc = SQLITE_OK;
|
||||
int openFlags = O_RDWR | O_CREAT;
|
||||
@ -5786,7 +5810,7 @@ static int proxyCreateUnixFile(
|
||||
pUnused->flags = openFlags;
|
||||
pNew->pUnused = pUnused;
|
||||
|
||||
rc = fillInUnixFile(&dummyVfs, fd, dirfd, (sqlite3_file*)pNew, path, 0, 0, 0);
|
||||
rc = fillInUnixFile(&dummyVfs, fd, 0, (sqlite3_file*)pNew, path, 0, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
*ppFile = pNew;
|
||||
return SQLITE_OK;
|
||||
@ -5826,6 +5850,8 @@ static int proxyGetHostID(unsigned char *pHostID, int *pError){
|
||||
return SQLITE_IOERR;
|
||||
}
|
||||
}
|
||||
#else
|
||||
UNUSED_PARAMETER(pError);
|
||||
#endif
|
||||
#ifdef SQLITE_TEST
|
||||
/* simulate multiple hosts by creating unique hostid file paths */
|
||||
@ -5900,7 +5926,7 @@ static int proxyBreakConchLock(unixFile *pFile, uuid_t myHostID){
|
||||
end_breaklock:
|
||||
if( rc ){
|
||||
if( fd>=0 ){
|
||||
unlink(tPath);
|
||||
osUnlink(tPath);
|
||||
robust_close(pFile, fd, __LINE__);
|
||||
}
|
||||
fprintf(stderr, "failed to break stale lock on %s, %s\n", cPath, errmsg);
|
||||
@ -5918,6 +5944,7 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){
|
||||
int nTries = 0;
|
||||
struct timespec conchModTime;
|
||||
|
||||
memset(&conchModTime, 0, sizeof(conchModTime));
|
||||
do {
|
||||
rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, lockType);
|
||||
nTries ++;
|
||||
@ -6149,11 +6176,12 @@ static int proxyTakeConch(unixFile *pFile){
|
||||
end_takeconch:
|
||||
OSTRACE(("TRANSPROXY: CLOSE %d\n", pFile->h));
|
||||
if( rc==SQLITE_OK && pFile->openFlags ){
|
||||
int fd;
|
||||
if( pFile->h>=0 ){
|
||||
robust_close(pFile, pFile->h, __LINE__);
|
||||
}
|
||||
pFile->h = -1;
|
||||
int fd = robust_open(pCtx->dbPath, pFile->openFlags,
|
||||
fd = robust_open(pCtx->dbPath, pFile->openFlags,
|
||||
SQLITE_DEFAULT_FILE_PERMISSIONS);
|
||||
OSTRACE(("TRANSPROXY: OPEN %d\n", fd));
|
||||
if( fd>=0 ){
|
||||
@ -6723,7 +6751,7 @@ int sqlite3_os_init(void){
|
||||
|
||||
/* Double-check that the aSyscall[] array has been constructed
|
||||
** correctly. See ticket [bb3a86e890c8e96ab] */
|
||||
assert( ArraySize(aSyscall)==16 );
|
||||
assert( ArraySize(aSyscall)==18 );
|
||||
|
||||
/* Register all VFSes defined in the aVfs[] array */
|
||||
for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){
|
||||
|
||||
477
src/os_win.c
477
src/os_win.c
@ -102,8 +102,9 @@ struct winFile {
|
||||
const sqlite3_io_methods *pMethod; /*** Must be first ***/
|
||||
sqlite3_vfs *pVfs; /* The VFS used to open this file */
|
||||
HANDLE h; /* Handle for accessing the file */
|
||||
unsigned char locktype; /* Type of lock currently held on this file */
|
||||
u8 locktype; /* Type of lock currently held on this file */
|
||||
short sharedLockByte; /* Randomly chosen byte used as a shared lock */
|
||||
u8 bPersistWal; /* True to persist WAL files */
|
||||
DWORD lastErrno; /* The Windows errno from the last I/O error */
|
||||
DWORD sectorSize; /* Sector size of the device file is on */
|
||||
winShm *pShm; /* Instance of shared memory on this file */
|
||||
@ -118,6 +119,76 @@ struct winFile {
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* If compiled with SQLITE_WIN32_MALLOC on Windows, we will use the
|
||||
* various Win32 API heap functions instead of our own.
|
||||
*/
|
||||
#ifdef SQLITE_WIN32_MALLOC
|
||||
/*
|
||||
* The initial size of the Win32-specific heap. This value may be zero.
|
||||
*/
|
||||
#ifndef SQLITE_WIN32_HEAP_INIT_SIZE
|
||||
# define SQLITE_WIN32_HEAP_INIT_SIZE ((SQLITE_DEFAULT_CACHE_SIZE) * \
|
||||
(SQLITE_DEFAULT_PAGE_SIZE) + 4194304)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The maximum size of the Win32-specific heap. This value may be zero.
|
||||
*/
|
||||
#ifndef SQLITE_WIN32_HEAP_MAX_SIZE
|
||||
# define SQLITE_WIN32_HEAP_MAX_SIZE (0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The extra flags to use in calls to the Win32 heap APIs. This value may be
|
||||
* zero for the default behavior.
|
||||
*/
|
||||
#ifndef SQLITE_WIN32_HEAP_FLAGS
|
||||
# define SQLITE_WIN32_HEAP_FLAGS (0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The winMemData structure stores information required by the Win32-specific
|
||||
** sqlite3_mem_methods implementation.
|
||||
*/
|
||||
typedef struct winMemData winMemData;
|
||||
struct winMemData {
|
||||
#ifndef NDEBUG
|
||||
u32 magic; /* Magic number to detect structure corruption. */
|
||||
#endif
|
||||
HANDLE hHeap; /* The handle to our heap. */
|
||||
BOOL bOwned; /* Do we own the heap (i.e. destroy it on shutdown)? */
|
||||
};
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define WINMEM_MAGIC 0x42b2830b
|
||||
#endif
|
||||
|
||||
static struct winMemData win_mem_data = {
|
||||
#ifndef NDEBUG
|
||||
WINMEM_MAGIC,
|
||||
#endif
|
||||
NULL, FALSE
|
||||
};
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define winMemAssertMagic() assert( win_mem_data.magic==WINMEM_MAGIC )
|
||||
#else
|
||||
#define winMemAssertMagic()
|
||||
#endif
|
||||
|
||||
#define winMemGetHeap() win_mem_data.hHeap
|
||||
|
||||
static void *winMemMalloc(int nBytes);
|
||||
static void winMemFree(void *pPrior);
|
||||
static void *winMemRealloc(void *pPrior, int nBytes);
|
||||
static int winMemSize(void *p);
|
||||
static int winMemRoundup(int n);
|
||||
static int winMemInit(void *pAppData);
|
||||
static void winMemShutdown(void *pAppData);
|
||||
|
||||
const sqlite3_mem_methods *sqlite3MemGetWin32(void);
|
||||
#endif /* SQLITE_WIN32_MALLOC */
|
||||
|
||||
/*
|
||||
** Forward prototypes.
|
||||
@ -170,6 +241,188 @@ static int sqlite3_os_type = 0;
|
||||
}
|
||||
#endif /* SQLITE_OS_WINCE */
|
||||
|
||||
#ifdef SQLITE_WIN32_MALLOC
|
||||
/*
|
||||
** Allocate nBytes of memory.
|
||||
*/
|
||||
static void *winMemMalloc(int nBytes){
|
||||
HANDLE hHeap;
|
||||
void *p;
|
||||
|
||||
winMemAssertMagic();
|
||||
hHeap = winMemGetHeap();
|
||||
assert( hHeap!=0 );
|
||||
assert( hHeap!=INVALID_HANDLE_VALUE );
|
||||
#ifdef SQLITE_WIN32_MALLOC_VALIDATE
|
||||
assert ( HeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
|
||||
#endif
|
||||
assert( nBytes>=0 );
|
||||
p = HeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes);
|
||||
if( !p ){
|
||||
sqlite3_log(SQLITE_NOMEM, "failed to HeapAlloc %u bytes (%d), heap=%p",
|
||||
nBytes, GetLastError(), (void*)hHeap);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free memory.
|
||||
*/
|
||||
static void winMemFree(void *pPrior){
|
||||
HANDLE hHeap;
|
||||
|
||||
winMemAssertMagic();
|
||||
hHeap = winMemGetHeap();
|
||||
assert( hHeap!=0 );
|
||||
assert( hHeap!=INVALID_HANDLE_VALUE );
|
||||
#ifdef SQLITE_WIN32_MALLOC_VALIDATE
|
||||
assert ( HeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) );
|
||||
#endif
|
||||
if( !pPrior ) return; /* Passing NULL to HeapFree is undefined. */
|
||||
if( !HeapFree(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ){
|
||||
sqlite3_log(SQLITE_NOMEM, "failed to HeapFree block %p (%d), heap=%p",
|
||||
pPrior, GetLastError(), (void*)hHeap);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Change the size of an existing memory allocation
|
||||
*/
|
||||
static void *winMemRealloc(void *pPrior, int nBytes){
|
||||
HANDLE hHeap;
|
||||
void *p;
|
||||
|
||||
winMemAssertMagic();
|
||||
hHeap = winMemGetHeap();
|
||||
assert( hHeap!=0 );
|
||||
assert( hHeap!=INVALID_HANDLE_VALUE );
|
||||
#ifdef SQLITE_WIN32_MALLOC_VALIDATE
|
||||
assert ( HeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) );
|
||||
#endif
|
||||
assert( nBytes>=0 );
|
||||
if( !pPrior ){
|
||||
p = HeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes);
|
||||
}else{
|
||||
p = HeapReAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior, (SIZE_T)nBytes);
|
||||
}
|
||||
if( !p ){
|
||||
sqlite3_log(SQLITE_NOMEM, "failed to %s %u bytes (%d), heap=%p",
|
||||
pPrior ? "HeapReAlloc" : "HeapAlloc", nBytes, GetLastError(),
|
||||
(void*)hHeap);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the size of an outstanding allocation, in bytes.
|
||||
*/
|
||||
static int winMemSize(void *p){
|
||||
HANDLE hHeap;
|
||||
SIZE_T n;
|
||||
|
||||
winMemAssertMagic();
|
||||
hHeap = winMemGetHeap();
|
||||
assert( hHeap!=0 );
|
||||
assert( hHeap!=INVALID_HANDLE_VALUE );
|
||||
#ifdef SQLITE_WIN32_MALLOC_VALIDATE
|
||||
assert ( HeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
|
||||
#endif
|
||||
if( !p ) return 0;
|
||||
n = HeapSize(hHeap, SQLITE_WIN32_HEAP_FLAGS, p);
|
||||
if( n==(SIZE_T)-1 ){
|
||||
sqlite3_log(SQLITE_NOMEM, "failed to HeapSize block %p (%d), heap=%p",
|
||||
p, GetLastError(), (void*)hHeap);
|
||||
return 0;
|
||||
}
|
||||
return (int)n;
|
||||
}
|
||||
|
||||
/*
|
||||
** Round up a request size to the next valid allocation size.
|
||||
*/
|
||||
static int winMemRoundup(int n){
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
** Initialize this module.
|
||||
*/
|
||||
static int winMemInit(void *pAppData){
|
||||
winMemData *pWinMemData = (winMemData *)pAppData;
|
||||
|
||||
if( !pWinMemData ) return SQLITE_ERROR;
|
||||
assert( pWinMemData->magic==WINMEM_MAGIC );
|
||||
if( !pWinMemData->hHeap ){
|
||||
pWinMemData->hHeap = HeapCreate(SQLITE_WIN32_HEAP_FLAGS,
|
||||
SQLITE_WIN32_HEAP_INIT_SIZE,
|
||||
SQLITE_WIN32_HEAP_MAX_SIZE);
|
||||
if( !pWinMemData->hHeap ){
|
||||
sqlite3_log(SQLITE_NOMEM,
|
||||
"failed to HeapCreate (%d), flags=%u, initSize=%u, maxSize=%u",
|
||||
GetLastError(), SQLITE_WIN32_HEAP_FLAGS, SQLITE_WIN32_HEAP_INIT_SIZE,
|
||||
SQLITE_WIN32_HEAP_MAX_SIZE);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
pWinMemData->bOwned = TRUE;
|
||||
}
|
||||
assert( pWinMemData->hHeap!=0 );
|
||||
assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE );
|
||||
#ifdef SQLITE_WIN32_MALLOC_VALIDATE
|
||||
assert( HeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
|
||||
#endif
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Deinitialize this module.
|
||||
*/
|
||||
static void winMemShutdown(void *pAppData){
|
||||
winMemData *pWinMemData = (winMemData *)pAppData;
|
||||
|
||||
if( !pWinMemData ) return;
|
||||
if( pWinMemData->hHeap ){
|
||||
assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE );
|
||||
#ifdef SQLITE_WIN32_MALLOC_VALIDATE
|
||||
assert( HeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
|
||||
#endif
|
||||
if( pWinMemData->bOwned ){
|
||||
if( !HeapDestroy(pWinMemData->hHeap) ){
|
||||
sqlite3_log(SQLITE_NOMEM, "failed to HeapDestroy (%d), heap=%p",
|
||||
GetLastError(), (void*)pWinMemData->hHeap);
|
||||
}
|
||||
pWinMemData->bOwned = FALSE;
|
||||
}
|
||||
pWinMemData->hHeap = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Populate the low-level memory allocation function pointers in
|
||||
** sqlite3GlobalConfig.m with pointers to the routines in this file. The
|
||||
** arguments specify the block of memory to manage.
|
||||
**
|
||||
** This routine is only called by sqlite3_config(), and therefore
|
||||
** is not required to be threadsafe (it is not).
|
||||
*/
|
||||
const sqlite3_mem_methods *sqlite3MemGetWin32(void){
|
||||
static const sqlite3_mem_methods winMemMethods = {
|
||||
winMemMalloc,
|
||||
winMemFree,
|
||||
winMemRealloc,
|
||||
winMemSize,
|
||||
winMemRoundup,
|
||||
winMemInit,
|
||||
winMemShutdown,
|
||||
&win_mem_data
|
||||
};
|
||||
return &winMemMethods;
|
||||
}
|
||||
|
||||
void sqlite3MemSetDefault(void){
|
||||
sqlite3_config(SQLITE_CONFIG_MALLOC, sqlite3MemGetWin32());
|
||||
}
|
||||
#endif /* SQLITE_WIN32_MALLOC */
|
||||
|
||||
/*
|
||||
** Convert a UTF-8 string to microsoft unicode (UTF-16?).
|
||||
**
|
||||
@ -402,6 +655,54 @@ static int winLogErrorAtLine(
|
||||
return errcode;
|
||||
}
|
||||
|
||||
/*
|
||||
** The number of times that a ReadFile(), WriteFile(), and DeleteFile()
|
||||
** will be retried following a locking error - probably caused by
|
||||
** antivirus software. Also the initial delay before the first retry.
|
||||
** The delay increases linearly with each retry.
|
||||
*/
|
||||
#ifndef SQLITE_WIN32_IOERR_RETRY
|
||||
# define SQLITE_WIN32_IOERR_RETRY 10
|
||||
#endif
|
||||
#ifndef SQLITE_WIN32_IOERR_RETRY_DELAY
|
||||
# define SQLITE_WIN32_IOERR_RETRY_DELAY 25
|
||||
#endif
|
||||
static int win32IoerrRetry = SQLITE_WIN32_IOERR_RETRY;
|
||||
static int win32IoerrRetryDelay = SQLITE_WIN32_IOERR_RETRY_DELAY;
|
||||
|
||||
/*
|
||||
** If a ReadFile() or WriteFile() error occurs, invoke this routine
|
||||
** to see if it should be retried. Return TRUE to retry. Return FALSE
|
||||
** to give up with an error.
|
||||
*/
|
||||
static int retryIoerr(int *pnRetry){
|
||||
DWORD e;
|
||||
if( *pnRetry>=win32IoerrRetry ){
|
||||
return 0;
|
||||
}
|
||||
e = GetLastError();
|
||||
if( e==ERROR_ACCESS_DENIED ||
|
||||
e==ERROR_LOCK_VIOLATION ||
|
||||
e==ERROR_SHARING_VIOLATION ){
|
||||
Sleep(win32IoerrRetryDelay*(1+*pnRetry));
|
||||
++*pnRetry;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Log a I/O error retry episode.
|
||||
*/
|
||||
static void logIoerr(int nRetry){
|
||||
if( nRetry ){
|
||||
sqlite3_log(SQLITE_IOERR,
|
||||
"delayed %dms for lock/sharing conflict",
|
||||
win32IoerrRetryDelay*nRetry*(nRetry+1)/2
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#if SQLITE_OS_WINCE
|
||||
/*************************************************************************
|
||||
** This section contains code for WinCE only.
|
||||
@ -820,6 +1121,7 @@ static int winRead(
|
||||
){
|
||||
winFile *pFile = (winFile*)id; /* file handle */
|
||||
DWORD nRead; /* Number of bytes actually read from file */
|
||||
int nRetry = 0; /* Number of retrys */
|
||||
|
||||
assert( id!=0 );
|
||||
SimulateIOError(return SQLITE_IOERR_READ);
|
||||
@ -828,10 +1130,12 @@ static int winRead(
|
||||
if( seekWinFile(pFile, offset) ){
|
||||
return SQLITE_FULL;
|
||||
}
|
||||
if( !ReadFile(pFile->h, pBuf, amt, &nRead, 0) ){
|
||||
while( !ReadFile(pFile->h, pBuf, amt, &nRead, 0) ){
|
||||
if( retryIoerr(&nRetry) ) continue;
|
||||
pFile->lastErrno = GetLastError();
|
||||
return winLogError(SQLITE_IOERR_READ, "winRead", pFile->zPath);
|
||||
}
|
||||
logIoerr(nRetry);
|
||||
if( nRead<(DWORD)amt ){
|
||||
/* Unread parts of the buffer must be zero-filled */
|
||||
memset(&((char*)pBuf)[nRead], 0, amt-nRead);
|
||||
@ -853,6 +1157,7 @@ static int winWrite(
|
||||
){
|
||||
int rc; /* True if error has occured, else false */
|
||||
winFile *pFile = (winFile*)id; /* File handle */
|
||||
int nRetry = 0; /* Number of retries */
|
||||
|
||||
assert( amt>0 );
|
||||
assert( pFile );
|
||||
@ -867,7 +1172,12 @@ static int winWrite(
|
||||
int nRem = amt; /* Number of bytes yet to be written */
|
||||
DWORD nWrite; /* Bytes written by each WriteFile() call */
|
||||
|
||||
while( nRem>0 && WriteFile(pFile->h, aRem, nRem, &nWrite, 0) && nWrite>0 ){
|
||||
while( nRem>0 ){
|
||||
if( !WriteFile(pFile->h, aRem, nRem, &nWrite, 0) ){
|
||||
if( retryIoerr(&nRetry) ) continue;
|
||||
break;
|
||||
}
|
||||
if( nWrite<=0 ) break;
|
||||
aRem += nWrite;
|
||||
nRem -= nWrite;
|
||||
}
|
||||
@ -883,6 +1193,8 @@ static int winWrite(
|
||||
return SQLITE_FULL;
|
||||
}
|
||||
return winLogError(SQLITE_IOERR_WRITE, "winWrite", pFile->zPath);
|
||||
}else{
|
||||
logIoerr(nRetry);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -904,7 +1216,7 @@ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
|
||||
** actual file size after the operation may be larger than the requested
|
||||
** size).
|
||||
*/
|
||||
if( pFile->szChunk ){
|
||||
if( pFile->szChunk>0 ){
|
||||
nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
|
||||
}
|
||||
|
||||
@ -933,9 +1245,19 @@ int sqlite3_fullsync_count = 0;
|
||||
** Make sure all writes to a particular file are committed to disk.
|
||||
*/
|
||||
static int winSync(sqlite3_file *id, int flags){
|
||||
#if !defined(NDEBUG) || !defined(SQLITE_NO_SYNC) || defined(SQLITE_DEBUG)
|
||||
winFile *pFile = (winFile*)id;
|
||||
#ifndef SQLITE_NO_SYNC
|
||||
/*
|
||||
** Used only when SQLITE_NO_SYNC is not defined.
|
||||
*/
|
||||
BOOL rc;
|
||||
#endif
|
||||
#if !defined(NDEBUG) || !defined(SQLITE_NO_SYNC) || \
|
||||
(defined(SQLITE_TEST) && defined(SQLITE_DEBUG))
|
||||
/*
|
||||
** Used when SQLITE_NO_SYNC is not defined and by the assert() and/or
|
||||
** OSTRACE() macros.
|
||||
*/
|
||||
winFile *pFile = (winFile*)id;
|
||||
#else
|
||||
UNUSED_PARAMETER(id);
|
||||
#endif
|
||||
@ -1276,29 +1598,62 @@ static int winUnlock(sqlite3_file *id, int locktype){
|
||||
** Control and query of the open file handle.
|
||||
*/
|
||||
static int winFileControl(sqlite3_file *id, int op, void *pArg){
|
||||
winFile *pFile = (winFile*)id;
|
||||
switch( op ){
|
||||
case SQLITE_FCNTL_LOCKSTATE: {
|
||||
*(int*)pArg = ((winFile*)id)->locktype;
|
||||
*(int*)pArg = pFile->locktype;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
case SQLITE_LAST_ERRNO: {
|
||||
*(int*)pArg = (int)((winFile*)id)->lastErrno;
|
||||
*(int*)pArg = (int)pFile->lastErrno;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
case SQLITE_FCNTL_CHUNK_SIZE: {
|
||||
((winFile*)id)->szChunk = *(int *)pArg;
|
||||
pFile->szChunk = *(int *)pArg;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
case SQLITE_FCNTL_SIZE_HINT: {
|
||||
sqlite3_int64 sz = *(sqlite3_int64*)pArg;
|
||||
SimulateIOErrorBenign(1);
|
||||
winTruncate(id, sz);
|
||||
SimulateIOErrorBenign(0);
|
||||
if( pFile->szChunk>0 ){
|
||||
sqlite3_int64 oldSz;
|
||||
int rc = winFileSize(id, &oldSz);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_int64 newSz = *(sqlite3_int64*)pArg;
|
||||
if( newSz>oldSz ){
|
||||
SimulateIOErrorBenign(1);
|
||||
rc = winTruncate(id, newSz);
|
||||
SimulateIOErrorBenign(0);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
case SQLITE_FCNTL_PERSIST_WAL: {
|
||||
int bPersist = *(int*)pArg;
|
||||
if( bPersist<0 ){
|
||||
*(int*)pArg = pFile->bPersistWal;
|
||||
}else{
|
||||
pFile->bPersistWal = bPersist!=0;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
case SQLITE_FCNTL_SYNC_OMITTED: {
|
||||
return SQLITE_OK;
|
||||
}
|
||||
case SQLITE_FCNTL_WIN32_AV_RETRY: {
|
||||
int *a = (int*)pArg;
|
||||
if( a[0]>0 ){
|
||||
win32IoerrRetry = a[0];
|
||||
}else{
|
||||
a[0] = win32IoerrRetry;
|
||||
}
|
||||
if( a[1]>0 ){
|
||||
win32IoerrRetryDelay = a[1];
|
||||
}else{
|
||||
a[1] = win32IoerrRetryDelay;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
return SQLITE_NOTFOUND;
|
||||
}
|
||||
@ -2107,6 +2462,7 @@ static int winOpen(
|
||||
winFile *pFile = (winFile*)id;
|
||||
void *zConverted; /* Filename in OS encoding */
|
||||
const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
|
||||
int cnt = 0;
|
||||
|
||||
/* If argument zPath is a NULL pointer, this function is required to open
|
||||
** a temporary file. Use this buffer to store the file name in.
|
||||
@ -2226,31 +2582,31 @@ static int winOpen(
|
||||
#endif
|
||||
|
||||
if( isNT() ){
|
||||
h = CreateFileW((WCHAR*)zConverted,
|
||||
dwDesiredAccess,
|
||||
dwShareMode,
|
||||
NULL,
|
||||
dwCreationDisposition,
|
||||
dwFlagsAndAttributes,
|
||||
NULL
|
||||
);
|
||||
while( (h = CreateFileW((WCHAR*)zConverted,
|
||||
dwDesiredAccess,
|
||||
dwShareMode, NULL,
|
||||
dwCreationDisposition,
|
||||
dwFlagsAndAttributes,
|
||||
NULL))==INVALID_HANDLE_VALUE &&
|
||||
retryIoerr(&cnt) ){}
|
||||
/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
|
||||
** Since the ASCII version of these Windows API do not exist for WINCE,
|
||||
** it's important to not reference them for WINCE builds.
|
||||
*/
|
||||
#if SQLITE_OS_WINCE==0
|
||||
}else{
|
||||
h = CreateFileA((char*)zConverted,
|
||||
dwDesiredAccess,
|
||||
dwShareMode,
|
||||
NULL,
|
||||
dwCreationDisposition,
|
||||
dwFlagsAndAttributes,
|
||||
NULL
|
||||
);
|
||||
while( (h = CreateFileA((char*)zConverted,
|
||||
dwDesiredAccess,
|
||||
dwShareMode, NULL,
|
||||
dwCreationDisposition,
|
||||
dwFlagsAndAttributes,
|
||||
NULL))==INVALID_HANDLE_VALUE &&
|
||||
retryIoerr(&cnt) ){}
|
||||
#endif
|
||||
}
|
||||
|
||||
logIoerr(cnt);
|
||||
|
||||
OSTRACE(("OPEN %d %s 0x%lx %s\n",
|
||||
h, zName, dwDesiredAccess,
|
||||
h==INVALID_HANDLE_VALUE ? "failed" : "ok"));
|
||||
@ -2259,7 +2615,7 @@ static int winOpen(
|
||||
pFile->lastErrno = GetLastError();
|
||||
winLogError(SQLITE_CANTOPEN, "winOpen", zUtf8Name);
|
||||
free(zConverted);
|
||||
if( isReadWrite ){
|
||||
if( isReadWrite && !isExclusive ){
|
||||
return winOpen(pVfs, zName, id,
|
||||
((flags|SQLITE_OPEN_READONLY)&~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)), pOutFlags);
|
||||
}else{
|
||||
@ -2316,15 +2672,13 @@ static int winOpen(
|
||||
** to MX_DELETION_ATTEMPTs deletion attempts are run before giving
|
||||
** up and returning an error.
|
||||
*/
|
||||
#define MX_DELETION_ATTEMPTS 5
|
||||
static int winDelete(
|
||||
sqlite3_vfs *pVfs, /* Not used on win32 */
|
||||
const char *zFilename, /* Name of file to delete */
|
||||
int syncDir /* Not used on win32 */
|
||||
){
|
||||
int cnt = 0;
|
||||
DWORD rc;
|
||||
DWORD error = 0;
|
||||
int rc;
|
||||
void *zConverted;
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
UNUSED_PARAMETER(syncDir);
|
||||
@ -2335,34 +2689,30 @@ static int winDelete(
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
if( isNT() ){
|
||||
do{
|
||||
DeleteFileW(zConverted);
|
||||
}while( ( ((rc = GetFileAttributesW(zConverted)) != INVALID_FILE_ATTRIBUTES)
|
||||
|| ((error = GetLastError()) == ERROR_ACCESS_DENIED))
|
||||
&& (++cnt < MX_DELETION_ATTEMPTS)
|
||||
&& (Sleep(100), 1) );
|
||||
rc = 1;
|
||||
while( GetFileAttributesW(zConverted)!=INVALID_FILE_ATTRIBUTES &&
|
||||
(rc = DeleteFileW(zConverted))==0 && retryIoerr(&cnt) ){}
|
||||
rc = rc ? SQLITE_OK : SQLITE_ERROR;
|
||||
/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
|
||||
** Since the ASCII version of these Windows API do not exist for WINCE,
|
||||
** it's important to not reference them for WINCE builds.
|
||||
*/
|
||||
#if SQLITE_OS_WINCE==0
|
||||
}else{
|
||||
do{
|
||||
DeleteFileA(zConverted);
|
||||
}while( ( ((rc = GetFileAttributesA(zConverted)) != INVALID_FILE_ATTRIBUTES)
|
||||
|| ((error = GetLastError()) == ERROR_ACCESS_DENIED))
|
||||
&& (++cnt < MX_DELETION_ATTEMPTS)
|
||||
&& (Sleep(100), 1) );
|
||||
rc = 1;
|
||||
while( GetFileAttributesA(zConverted)!=INVALID_FILE_ATTRIBUTES &&
|
||||
(rc = DeleteFileA(zConverted))==0 && retryIoerr(&cnt) ){}
|
||||
rc = rc ? SQLITE_OK : SQLITE_ERROR;
|
||||
#endif
|
||||
}
|
||||
if( rc ){
|
||||
rc = winLogError(SQLITE_IOERR_DELETE, "winDelete", zFilename);
|
||||
}else{
|
||||
logIoerr(cnt);
|
||||
}
|
||||
free(zConverted);
|
||||
OSTRACE(("DELETE \"%s\" %s\n", zFilename,
|
||||
( (rc==INVALID_FILE_ATTRIBUTES) && (error==ERROR_FILE_NOT_FOUND)) ?
|
||||
"ok" : "failed" ));
|
||||
|
||||
return ( (rc == INVALID_FILE_ATTRIBUTES)
|
||||
&& (error == ERROR_FILE_NOT_FOUND)) ? SQLITE_OK :
|
||||
winLogError(SQLITE_IOERR_DELETE, "winDelete", zFilename);
|
||||
OSTRACE(("DELETE \"%s\" %s\n", zFilename, (rc ? "failed" : "ok" )));
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2385,11 +2735,13 @@ static int winAccess(
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
if( isNT() ){
|
||||
int cnt = 0;
|
||||
WIN32_FILE_ATTRIBUTE_DATA sAttrData;
|
||||
memset(&sAttrData, 0, sizeof(sAttrData));
|
||||
if( GetFileAttributesExW((WCHAR*)zConverted,
|
||||
while( !(rc = GetFileAttributesExW((WCHAR*)zConverted,
|
||||
GetFileExInfoStandard,
|
||||
&sAttrData) ){
|
||||
&sAttrData)) && retryIoerr(&cnt) ){}
|
||||
if( rc ){
|
||||
/* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file
|
||||
** as if it does not exist.
|
||||
*/
|
||||
@ -2401,6 +2753,7 @@ static int winAccess(
|
||||
attr = sAttrData.dwFileAttributes;
|
||||
}
|
||||
}else{
|
||||
logIoerr(cnt);
|
||||
if( GetLastError()!=ERROR_FILE_NOT_FOUND ){
|
||||
winLogError(SQLITE_IOERR_ACCESS, "winAccess", zFilename);
|
||||
free(zConverted);
|
||||
@ -2425,7 +2778,8 @@ static int winAccess(
|
||||
rc = attr!=INVALID_FILE_ATTRIBUTES;
|
||||
break;
|
||||
case SQLITE_ACCESS_READWRITE:
|
||||
rc = (attr & FILE_ATTRIBUTE_READONLY)==0;
|
||||
rc = attr!=INVALID_FILE_ATTRIBUTES &&
|
||||
(attr & FILE_ATTRIBUTE_READONLY)==0;
|
||||
break;
|
||||
default:
|
||||
assert(!"Invalid flags argument");
|
||||
@ -2627,7 +2981,7 @@ static void winDlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
getLastErrorMsg(nBuf, zBufOut);
|
||||
}
|
||||
void (*winDlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol))(void){
|
||||
static void (*winDlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol))(void){
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
#if SQLITE_OS_WINCE
|
||||
/* The GetProcAddressA() routine is only available on wince. */
|
||||
@ -2638,7 +2992,7 @@ void (*winDlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol))(void){
|
||||
return (void(*)(void))GetProcAddress((HANDLE)pHandle, zSymbol);
|
||||
#endif
|
||||
}
|
||||
void winDlClose(sqlite3_vfs *pVfs, void *pHandle){
|
||||
static void winDlClose(sqlite3_vfs *pVfs, void *pHandle){
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
FreeLibrary((HANDLE)pHandle);
|
||||
}
|
||||
@ -2712,7 +3066,8 @@ int sqlite3_current_time = 0; /* Fake system time in seconds since 1970. */
|
||||
** epoch of noon in Greenwich on November 24, 4714 B.C according to the
|
||||
** proleptic Gregorian calendar.
|
||||
**
|
||||
** On success, return 0. Return 1 if the time and date cannot be found.
|
||||
** On success, return SQLITE_OK. Return SQLITE_ERROR if the time and date
|
||||
** cannot be found.
|
||||
*/
|
||||
static int winCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){
|
||||
/* FILETIME structure is a 64-bit value representing the number of
|
||||
@ -2732,7 +3087,7 @@ static int winCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){
|
||||
GetSystemTime(&time);
|
||||
/* if SystemTimeToFileTime() fails, it returns zero. */
|
||||
if (!SystemTimeToFileTime(&time,&ft)){
|
||||
return 1;
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
#else
|
||||
GetSystemTimeAsFileTime( &ft );
|
||||
@ -2748,7 +3103,7 @@ static int winCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){
|
||||
}
|
||||
#endif
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
return 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2756,7 +3111,7 @@ static int winCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){
|
||||
** current time and date as a Julian Day number into *prNow and
|
||||
** return 0. Return 1 if the time and date cannot be found.
|
||||
*/
|
||||
int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
|
||||
static int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
|
||||
int rc;
|
||||
sqlite3_int64 i;
|
||||
rc = winCurrentTimeInt64(pVfs, &i);
|
||||
|
||||
43
src/pager.c
43
src/pager.c
@ -670,8 +670,8 @@ struct Pager {
|
||||
char *zJournal; /* Name of the journal file */
|
||||
int (*xBusyHandler)(void*); /* Function to call when busy */
|
||||
void *pBusyHandlerArg; /* Context argument for xBusyHandler */
|
||||
int nHit, nMiss; /* Total cache hits and misses */
|
||||
#ifdef SQLITE_TEST
|
||||
int nHit, nMiss; /* Cache hits and missing */
|
||||
int nRead, nWrite; /* Database pages read/written */
|
||||
#endif
|
||||
void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */
|
||||
@ -2703,7 +2703,6 @@ static int pager_playback(Pager *pPager, int isHot){
|
||||
rc = pager_playback_one_page(pPager,&pPager->journalOff,0,1,0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( rc==SQLITE_DONE ){
|
||||
rc = SQLITE_OK;
|
||||
pPager->journalOff = szJ;
|
||||
break;
|
||||
}else if( rc==SQLITE_IOERR_SHORT_READ ){
|
||||
@ -2965,6 +2964,7 @@ static int pagerWalFrames(
|
||||
#endif
|
||||
|
||||
assert( pPager->pWal );
|
||||
assert( pList );
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* Verify that the page list is in accending order */
|
||||
for(p=pList; p && p->pDirty; p=p->pDirty){
|
||||
@ -3739,6 +3739,7 @@ static int pagerSyncHotJournal(Pager *pPager){
|
||||
int sqlite3PagerClose(Pager *pPager){
|
||||
u8 *pTmp = (u8 *)pPager->pTmpSpace;
|
||||
|
||||
assert( assert_pager_state(pPager) );
|
||||
disable_simulated_io_errors();
|
||||
sqlite3BeginBenignMalloc();
|
||||
/* pPager->errCode = 0; */
|
||||
@ -4168,7 +4169,7 @@ static int pagerStress(void *p, PgHdr *pPg){
|
||||
**
|
||||
** Spilling is also prohibited when in an error state since that could
|
||||
** lead to database corruption. In the current implementaton it
|
||||
** is impossible for sqlite3PCacheFetch() to be called with createFlag==1
|
||||
** is impossible for sqlite3PcacheFetch() to be called with createFlag==1
|
||||
** while in the error state, hence it is impossible for this routine to
|
||||
** be called in the error state. Nevertheless, we include a NEVER()
|
||||
** test for the error state as a safeguard against future changes.
|
||||
@ -5004,14 +5005,13 @@ int sqlite3PagerAcquire(
|
||||
/* In this case the pcache already contains an initialized copy of
|
||||
** the page. Return without further ado. */
|
||||
assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) );
|
||||
PAGER_INCR(pPager->nHit);
|
||||
pPager->nHit++;
|
||||
return SQLITE_OK;
|
||||
|
||||
}else{
|
||||
/* The pager cache has created a new page. Its content needs to
|
||||
** be initialized. */
|
||||
|
||||
PAGER_INCR(pPager->nMiss);
|
||||
pPg = *ppPage;
|
||||
pPg->pPager = pPager;
|
||||
|
||||
@ -5047,6 +5047,7 @@ int sqlite3PagerAcquire(
|
||||
IOTRACE(("ZERO %p %d\n", pPager, pgno));
|
||||
}else{
|
||||
assert( pPg->pPager==pPager );
|
||||
pPager->nMiss++;
|
||||
rc = readDbPage(pPg);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto pager_acquire_err;
|
||||
@ -6081,6 +6082,31 @@ int *sqlite3PagerStats(Pager *pPager){
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Parameter eStat must be either SQLITE_DBSTATUS_CACHE_HIT or
|
||||
** SQLITE_DBSTATUS_CACHE_MISS. Before returning, *pnVal is incremented by the
|
||||
** current cache hit or miss count, according to the value of eStat. If the
|
||||
** reset parameter is non-zero, the cache hit or miss count is zeroed before
|
||||
** returning.
|
||||
*/
|
||||
void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, int *pnVal){
|
||||
int *piStat;
|
||||
|
||||
assert( eStat==SQLITE_DBSTATUS_CACHE_HIT
|
||||
|| eStat==SQLITE_DBSTATUS_CACHE_MISS
|
||||
);
|
||||
if( eStat==SQLITE_DBSTATUS_CACHE_HIT ){
|
||||
piStat = &pPager->nHit;
|
||||
}else{
|
||||
piStat = &pPager->nMiss;
|
||||
}
|
||||
|
||||
*pnVal += *piStat;
|
||||
if( reset ){
|
||||
*piStat = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if this is an in-memory pager.
|
||||
*/
|
||||
@ -6810,6 +6836,13 @@ int sqlite3PagerCloseWal(Pager *pPager){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Unless this is an in-memory or temporary database, clear the pager cache.
|
||||
*/
|
||||
void sqlite3PagerClearCache(Pager *pPager){
|
||||
if( !MEMDB && pPager->tempFile==0 ) pager_reset(pPager);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
/*
|
||||
** This function is called by the wal module when writing page content
|
||||
|
||||
@ -155,6 +155,8 @@ const char *sqlite3PagerJournalname(Pager*);
|
||||
int sqlite3PagerNosync(Pager*);
|
||||
void *sqlite3PagerTempSpace(Pager*);
|
||||
int sqlite3PagerIsMemdb(Pager*);
|
||||
void sqlite3PagerCacheStat(Pager *, int, int, int *);
|
||||
void sqlite3PagerClearCache(Pager *);
|
||||
|
||||
/* Functions used to truncate the database file. */
|
||||
void sqlite3PagerTruncateImage(Pager*,Pgno);
|
||||
|
||||
@ -24,6 +24,7 @@ typedef struct PgHdr1 PgHdr1;
|
||||
typedef struct PgFreeslot PgFreeslot;
|
||||
typedef struct PGroup PGroup;
|
||||
|
||||
|
||||
/* Each page cache (or PCache) belongs to a PGroup. A PGroup is a set
|
||||
** of one or more PCaches that are able to recycle each others unpinned
|
||||
** pages when they are under memory pressure. A PGroup is an instance of
|
||||
@ -288,15 +289,22 @@ static int pcache1MemSize(void *p){
|
||||
*/
|
||||
static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
|
||||
int nByte = sizeof(PgHdr1) + pCache->szPage;
|
||||
void *pPg = pcache1Alloc(nByte);
|
||||
PgHdr1 *p;
|
||||
PgHdr1 *p = 0;
|
||||
void *pPg;
|
||||
|
||||
/* The group mutex must be released before pcache1Alloc() is called. This
|
||||
** is because it may call sqlite3_release_memory(), which assumes that
|
||||
** this mutex is not held. */
|
||||
assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
|
||||
pcache1LeaveMutex(pCache->pGroup);
|
||||
pPg = pcache1Alloc(nByte);
|
||||
pcache1EnterMutex(pCache->pGroup);
|
||||
|
||||
if( pPg ){
|
||||
p = PAGE_TO_PGHDR1(pCache, pPg);
|
||||
if( pCache->bPurgeable ){
|
||||
pCache->pGroup->nCurrentPage++;
|
||||
}
|
||||
}else{
|
||||
p = 0;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
@ -311,10 +319,11 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
|
||||
static void pcache1FreePage(PgHdr1 *p){
|
||||
if( ALWAYS(p) ){
|
||||
PCache1 *pCache = p->pCache;
|
||||
assert( sqlite3_mutex_held(p->pCache->pGroup->mutex) );
|
||||
pcache1Free(PGHDR1_TO_PAGE(p));
|
||||
if( pCache->bPurgeable ){
|
||||
pCache->pGroup->nCurrentPage--;
|
||||
}
|
||||
pcache1Free(PGHDR1_TO_PAGE(p));
|
||||
}
|
||||
}
|
||||
|
||||
@ -752,9 +761,7 @@ static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){
|
||||
*/
|
||||
if( !pPage ){
|
||||
if( createFlag==1 ) sqlite3BeginBenignMalloc();
|
||||
pcache1LeaveMutex(pGroup);
|
||||
pPage = pcache1AllocPage(pCache);
|
||||
pcache1EnterMutex(pGroup);
|
||||
if( createFlag==1 ) sqlite3EndBenignMalloc();
|
||||
}
|
||||
|
||||
|
||||
10
src/pragma.c
10
src/pragma.c
@ -467,7 +467,7 @@ void sqlite3Pragma(
|
||||
if( sqlite3ReadSchema(pParse) ) goto pragma_out;
|
||||
sqlite3CodeVerifySchema(pParse, iDb);
|
||||
iReg = ++pParse->nMem;
|
||||
if( zLeft[0]=='p' ){
|
||||
if( sqlite3Tolower(zLeft[0])=='p' ){
|
||||
sqlite3VdbeAddOp2(v, OP_Pagecount, iDb, iReg);
|
||||
}else{
|
||||
sqlite3VdbeAddOp3(v, OP_MaxPgcnt, iDb, iReg, sqlite3Atoi(zRight));
|
||||
@ -533,8 +533,10 @@ void sqlite3Pragma(
|
||||
int eMode; /* One of the PAGER_JOURNALMODE_XXX symbols */
|
||||
int ii; /* Loop counter */
|
||||
|
||||
/* Force the schema to be loaded on all databases. This cases all
|
||||
** database files to be opened and the journal_modes set. */
|
||||
/* Force the schema to be loaded on all databases. This causes all
|
||||
** database files to be opened and the journal_modes set. This is
|
||||
** necessary because subsequent processing must know if the databases
|
||||
** are in WAL mode. */
|
||||
if( sqlite3ReadSchema(pParse) ){
|
||||
goto pragma_out;
|
||||
}
|
||||
@ -1078,7 +1080,7 @@ void sqlite3Pragma(
|
||||
{ OP_ResultRow, 3, 1, 0},
|
||||
};
|
||||
|
||||
int isQuick = (zLeft[0]=='q');
|
||||
int isQuick = (sqlite3Tolower(zLeft[0])=='q');
|
||||
|
||||
/* Initialize the VDBE program */
|
||||
if( sqlite3ReadSchema(pParse) ) goto pragma_out;
|
||||
|
||||
151
src/printf.c
151
src/printf.c
@ -7,48 +7,10 @@
|
||||
**
|
||||
**************************************************************************
|
||||
**
|
||||
** The following modules is an enhanced replacement for the "printf" subroutines
|
||||
** found in the standard C library. The following enhancements are
|
||||
** supported:
|
||||
**
|
||||
** + Additional functions. The standard set of "printf" functions
|
||||
** includes printf, fprintf, sprintf, vprintf, vfprintf, and
|
||||
** vsprintf. This module adds the following:
|
||||
**
|
||||
** * snprintf -- Works like sprintf, but has an extra argument
|
||||
** which is the size of the buffer written to.
|
||||
**
|
||||
** * mprintf -- Similar to sprintf. Writes output to memory
|
||||
** obtained from malloc.
|
||||
**
|
||||
** * xprintf -- Calls a function to dispose of output.
|
||||
**
|
||||
** * nprintf -- No output, but returns the number of characters
|
||||
** that would have been output by printf.
|
||||
**
|
||||
** * A v- version (ex: vsnprintf) of every function is also
|
||||
** supplied.
|
||||
**
|
||||
** + A few extensions to the formatting notation are supported:
|
||||
**
|
||||
** * The "=" flag (similar to "-") causes the output to be
|
||||
** be centered in the appropriately sized field.
|
||||
**
|
||||
** * The %b field outputs an integer in binary notation.
|
||||
**
|
||||
** * The %c field now accepts a precision. The character output
|
||||
** is repeated by the number of times the precision specifies.
|
||||
**
|
||||
** * The %' field works like %c, but takes as its character the
|
||||
** next character of the format string, instead of the next
|
||||
** argument. For example, printf("%.78'-") prints 78 minus
|
||||
** signs, the same as printf("%.78c",'-').
|
||||
**
|
||||
** + When compiled using GCC on a SPARC, this version of printf is
|
||||
** faster than the library printf for SUN OS 4.1.
|
||||
**
|
||||
** + All functions are fully reentrant.
|
||||
**
|
||||
** This file contains code for a set of "printf"-like routines. These
|
||||
** routines format strings much like the printf() from the standard C
|
||||
** library, though the implementation here has enhancements to support
|
||||
** SQLlite.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -187,43 +149,15 @@ static void appendSpace(StrAccum *pAccum, int N){
|
||||
|
||||
/*
|
||||
** On machines with a small stack size, you can redefine the
|
||||
** SQLITE_PRINT_BUF_SIZE to be less than 350.
|
||||
** SQLITE_PRINT_BUF_SIZE to be something smaller, if desired.
|
||||
*/
|
||||
#ifndef SQLITE_PRINT_BUF_SIZE
|
||||
# if defined(SQLITE_SMALL_STACK)
|
||||
# define SQLITE_PRINT_BUF_SIZE 50
|
||||
# else
|
||||
# define SQLITE_PRINT_BUF_SIZE 350
|
||||
# endif
|
||||
# define SQLITE_PRINT_BUF_SIZE 70
|
||||
#endif
|
||||
#define etBUFSIZE SQLITE_PRINT_BUF_SIZE /* Size of the output buffer */
|
||||
|
||||
/*
|
||||
** The root program. All variations call this core.
|
||||
**
|
||||
** INPUTS:
|
||||
** func This is a pointer to a function taking three arguments
|
||||
** 1. A pointer to anything. Same as the "arg" parameter.
|
||||
** 2. A pointer to the list of characters to be output
|
||||
** (Note, this list is NOT null terminated.)
|
||||
** 3. An integer number of characters to be output.
|
||||
** (Note: This number might be zero.)
|
||||
**
|
||||
** arg This is the pointer to anything which will be passed as the
|
||||
** first argument to "func". Use it for whatever you like.
|
||||
**
|
||||
** fmt This is the format string, as in the usual print.
|
||||
**
|
||||
** ap This is a pointer to a list of arguments. Same as in
|
||||
** vfprint.
|
||||
**
|
||||
** OUTPUTS:
|
||||
** The return value is the total number of characters sent to
|
||||
** the function "func". Returns -1 on a error.
|
||||
**
|
||||
** Note that the order in which automatic variables are declared below
|
||||
** seems to make a big difference in determining how fast this beast
|
||||
** will run.
|
||||
** Render a string given by "fmt" into the StrAccum object.
|
||||
*/
|
||||
void sqlite3VXPrintf(
|
||||
StrAccum *pAccum, /* Accumulate results here */
|
||||
@ -246,23 +180,23 @@ void sqlite3VXPrintf(
|
||||
etByte flag_long; /* True if "l" flag is present */
|
||||
etByte flag_longlong; /* True if the "ll" flag is present */
|
||||
etByte done; /* Loop termination flag */
|
||||
etByte xtype = 0; /* Conversion paradigm */
|
||||
char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */
|
||||
sqlite_uint64 longvalue; /* Value for integer types */
|
||||
LONGDOUBLE_TYPE realvalue; /* Value for real types */
|
||||
const et_info *infop; /* Pointer to the appropriate info structure */
|
||||
char buf[etBUFSIZE]; /* Conversion buffer */
|
||||
char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */
|
||||
etByte xtype = 0; /* Conversion paradigm */
|
||||
char *zExtra; /* Extra memory used for etTCLESCAPE conversions */
|
||||
char *zOut; /* Rendering buffer */
|
||||
int nOut; /* Size of the rendering buffer */
|
||||
char *zExtra; /* Malloced memory used by some conversion */
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
int exp, e2; /* exponent of real numbers */
|
||||
int nsd; /* Number of significant digits returned */
|
||||
double rounder; /* Used for rounding floating point values */
|
||||
etByte flag_dp; /* True if decimal point should be shown */
|
||||
etByte flag_rtz; /* True if trailing zeros should be removed */
|
||||
etByte flag_exp; /* True to force display of the exponent */
|
||||
int nsd; /* Number of significant digits returned */
|
||||
#endif
|
||||
char buf[etBUFSIZE]; /* Conversion buffer */
|
||||
|
||||
length = 0;
|
||||
bufpt = 0;
|
||||
for(; (c=(*fmt))!=0; ++fmt){
|
||||
if( c!='%' ){
|
||||
@ -307,9 +241,6 @@ void sqlite3VXPrintf(
|
||||
c = *++fmt;
|
||||
}
|
||||
}
|
||||
if( width > etBUFSIZE-10 ){
|
||||
width = etBUFSIZE-10;
|
||||
}
|
||||
/* Get the precision */
|
||||
if( c=='.' ){
|
||||
precision = 0;
|
||||
@ -356,12 +287,6 @@ void sqlite3VXPrintf(
|
||||
}
|
||||
zExtra = 0;
|
||||
|
||||
|
||||
/* Limit the precision to prevent overflowing buf[] during conversion */
|
||||
if( precision>etBUFSIZE-40 && (infop->flags & FLAG_STRING)==0 ){
|
||||
precision = etBUFSIZE-40;
|
||||
}
|
||||
|
||||
/*
|
||||
** At this point, variables are initialized as follows:
|
||||
**
|
||||
@ -426,16 +351,26 @@ void sqlite3VXPrintf(
|
||||
if( flag_zeropad && precision<width-(prefix!=0) ){
|
||||
precision = width-(prefix!=0);
|
||||
}
|
||||
bufpt = &buf[etBUFSIZE-1];
|
||||
if( precision<etBUFSIZE-10 ){
|
||||
nOut = etBUFSIZE;
|
||||
zOut = buf;
|
||||
}else{
|
||||
nOut = precision + 10;
|
||||
zOut = zExtra = sqlite3Malloc( nOut );
|
||||
if( zOut==0 ){
|
||||
pAccum->mallocFailed = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
bufpt = &zOut[nOut-1];
|
||||
if( xtype==etORDINAL ){
|
||||
static const char zOrd[] = "thstndrd";
|
||||
int x = (int)(longvalue % 10);
|
||||
if( x>=4 || (longvalue/10)%10==1 ){
|
||||
x = 0;
|
||||
}
|
||||
buf[etBUFSIZE-3] = zOrd[x*2];
|
||||
buf[etBUFSIZE-2] = zOrd[x*2+1];
|
||||
bufpt -= 2;
|
||||
*(--bufpt) = zOrd[x*2+1];
|
||||
*(--bufpt) = zOrd[x*2];
|
||||
}
|
||||
{
|
||||
register const char *cset; /* Use registers for speed */
|
||||
@ -447,7 +382,7 @@ void sqlite3VXPrintf(
|
||||
longvalue = longvalue/base;
|
||||
}while( longvalue>0 );
|
||||
}
|
||||
length = (int)(&buf[etBUFSIZE-1]-bufpt);
|
||||
length = (int)(&zOut[nOut-1]-bufpt);
|
||||
for(idx=precision-length; idx>0; idx--){
|
||||
*(--bufpt) = '0'; /* Zero pad */
|
||||
}
|
||||
@ -458,7 +393,7 @@ void sqlite3VXPrintf(
|
||||
pre = &aPrefix[infop->prefix];
|
||||
for(; (x=(*pre))!=0; pre++) *(--bufpt) = x;
|
||||
}
|
||||
length = (int)(&buf[etBUFSIZE-1]-bufpt);
|
||||
length = (int)(&zOut[nOut-1]-bufpt);
|
||||
break;
|
||||
case etFLOAT:
|
||||
case etEXP:
|
||||
@ -468,7 +403,6 @@ void sqlite3VXPrintf(
|
||||
length = 0;
|
||||
#else
|
||||
if( precision<0 ) precision = 6; /* Set default precision */
|
||||
if( precision>etBUFSIZE/2-10 ) precision = etBUFSIZE/2-10;
|
||||
if( realvalue<0.0 ){
|
||||
realvalue = -realvalue;
|
||||
prefix = '-';
|
||||
@ -516,7 +450,6 @@ void sqlite3VXPrintf(
|
||||
** If the field type is etGENERIC, then convert to either etEXP
|
||||
** or etFLOAT, as appropriate.
|
||||
*/
|
||||
flag_exp = xtype==etEXP;
|
||||
if( xtype!=etFLOAT ){
|
||||
realvalue += rounder;
|
||||
if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
|
||||
@ -537,6 +470,14 @@ void sqlite3VXPrintf(
|
||||
}else{
|
||||
e2 = exp;
|
||||
}
|
||||
if( e2+precision+width > etBUFSIZE - 15 ){
|
||||
bufpt = zExtra = sqlite3Malloc( e2+precision+width+15 );
|
||||
if( bufpt==0 ){
|
||||
pAccum->mallocFailed = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
zOut = bufpt;
|
||||
nsd = 0;
|
||||
flag_dp = (precision>0 ?1:0) | flag_alternateform | flag_altform2;
|
||||
/* The sign in front of the number */
|
||||
@ -568,7 +509,7 @@ void sqlite3VXPrintf(
|
||||
/* Remove trailing zeros and the "." if no digits follow the "." */
|
||||
if( flag_rtz && flag_dp ){
|
||||
while( bufpt[-1]=='0' ) *(--bufpt) = 0;
|
||||
assert( bufpt>buf );
|
||||
assert( bufpt>zOut );
|
||||
if( bufpt[-1]=='.' ){
|
||||
if( flag_altform2 ){
|
||||
*(bufpt++) = '0';
|
||||
@ -578,7 +519,7 @@ void sqlite3VXPrintf(
|
||||
}
|
||||
}
|
||||
/* Add the "eNNN" suffix */
|
||||
if( flag_exp || xtype==etEXP ){
|
||||
if( xtype==etEXP ){
|
||||
*(bufpt++) = aDigits[infop->charset];
|
||||
if( exp<0 ){
|
||||
*(bufpt++) = '-'; exp = -exp;
|
||||
@ -597,8 +538,8 @@ void sqlite3VXPrintf(
|
||||
/* The converted number is in buf[] and zero terminated. Output it.
|
||||
** Note that the number is in the usual order, not reversed as with
|
||||
** integer conversions. */
|
||||
length = (int)(bufpt-buf);
|
||||
bufpt = buf;
|
||||
length = (int)(bufpt-zOut);
|
||||
bufpt = zOut;
|
||||
|
||||
/* Special case: Add leading zeros if the flag_zeropad flag is
|
||||
** set and we are not left justified */
|
||||
@ -736,9 +677,7 @@ void sqlite3VXPrintf(
|
||||
appendSpace(pAccum, nspace);
|
||||
}
|
||||
}
|
||||
if( zExtra ){
|
||||
sqlite3_free(zExtra);
|
||||
}
|
||||
sqlite3_free(zExtra);
|
||||
}/* End for loop over the format string */
|
||||
} /* End of function */
|
||||
|
||||
@ -752,6 +691,7 @@ void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
|
||||
testcase(p->mallocFailed);
|
||||
return;
|
||||
}
|
||||
assert( p->zText!=0 || p->nChar==0 );
|
||||
if( N<0 ){
|
||||
N = sqlite3Strlen30(z);
|
||||
}
|
||||
@ -783,7 +723,7 @@ void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
|
||||
zNew = sqlite3_realloc(zOld, p->nAlloc);
|
||||
}
|
||||
if( zNew ){
|
||||
if( zOld==0 ) memcpy(zNew, p->zText, p->nChar);
|
||||
if( zOld==0 && p->nChar>0 ) memcpy(zNew, p->zText, p->nChar);
|
||||
p->zText = zNew;
|
||||
}else{
|
||||
p->mallocFailed = 1;
|
||||
@ -792,6 +732,7 @@ void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
|
||||
}
|
||||
}
|
||||
}
|
||||
assert( p->zText );
|
||||
memcpy(&p->zText[p->nChar], z, N);
|
||||
p->nChar += N;
|
||||
}
|
||||
|
||||
@ -98,6 +98,24 @@ static void resolveAlias(
|
||||
sqlite3DbFree(db, pDup);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Return TRUE if the name zCol occurs anywhere in the USING clause.
|
||||
**
|
||||
** Return FALSE if the USING clause is NULL or if it does not contain
|
||||
** zCol.
|
||||
*/
|
||||
static int nameInUsingClause(IdList *pUsing, const char *zCol){
|
||||
if( pUsing ){
|
||||
int k;
|
||||
for(k=0; k<pUsing->nId; k++){
|
||||
if( sqlite3StrICmp(pUsing->a[k].zName, zCol)==0 ) return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up
|
||||
** that name in the set of source tables in pSrcList and make the pExpr
|
||||
@ -189,7 +207,14 @@ static int lookupName(
|
||||
}
|
||||
for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){
|
||||
if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
|
||||
IdList *pUsing;
|
||||
/* If there has been exactly one prior match and this match
|
||||
** is for the right-hand table of a NATURAL JOIN or is in a
|
||||
** USING clause, then skip this match.
|
||||
*/
|
||||
if( cnt==1 ){
|
||||
if( pItem->jointype & JT_NATURAL ) continue;
|
||||
if( nameInUsingClause(pItem->pUsing, zCol) ) continue;
|
||||
}
|
||||
cnt++;
|
||||
pExpr->iTable = pItem->iCursor;
|
||||
pExpr->pTab = pTab;
|
||||
@ -197,26 +222,6 @@ static int lookupName(
|
||||
pSchema = pTab->pSchema;
|
||||
/* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */
|
||||
pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j;
|
||||
if( i<pSrcList->nSrc-1 ){
|
||||
if( pItem[1].jointype & JT_NATURAL ){
|
||||
/* If this match occurred in the left table of a natural join,
|
||||
** then skip the right table to avoid a duplicate match */
|
||||
pItem++;
|
||||
i++;
|
||||
}else if( (pUsing = pItem[1].pUsing)!=0 ){
|
||||
/* If this match occurs on a column that is in the USING clause
|
||||
** of a join, skip the search of the right table of the join
|
||||
** to avoid a duplicate match there. */
|
||||
int k;
|
||||
for(k=0; k<pUsing->nId; k++){
|
||||
if( sqlite3StrICmp(pUsing->a[k].zName, zCol)==0 ){
|
||||
pItem++;
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -996,11 +1001,25 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
|
||||
for(i=0; i<p->pSrc->nSrc; i++){
|
||||
struct SrcList_item *pItem = &p->pSrc->a[i];
|
||||
if( pItem->pSelect ){
|
||||
NameContext *pNC; /* Used to iterate name contexts */
|
||||
int nRef = 0; /* Refcount for pOuterNC and outer contexts */
|
||||
const char *zSavedContext = pParse->zAuthContext;
|
||||
|
||||
/* Count the total number of references to pOuterNC and all of its
|
||||
** parent contexts. After resolving references to expressions in
|
||||
** pItem->pSelect, check if this value has changed. If so, then
|
||||
** SELECT statement pItem->pSelect must be correlated. Set the
|
||||
** pItem->isCorrelated flag if this is the case. */
|
||||
for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef += pNC->nRef;
|
||||
|
||||
if( pItem->zName ) pParse->zAuthContext = pItem->zName;
|
||||
sqlite3ResolveSelectNames(pParse, pItem->pSelect, pOuterNC);
|
||||
pParse->zAuthContext = zSavedContext;
|
||||
if( pParse->nErr || db->mallocFailed ) return WRC_Abort;
|
||||
|
||||
for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef -= pNC->nRef;
|
||||
assert( pItem->isCorrelated==0 && nRef<=0 );
|
||||
pItem->isCorrelated = (nRef!=0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
208
src/select.c
208
src/select.c
@ -65,6 +65,7 @@ Select *sqlite3SelectNew(
|
||||
pNew = sqlite3DbMallocZero(db, sizeof(*pNew) );
|
||||
assert( db->mallocFailed || !pOffset || pLimit ); /* OFFSET implies LIMIT */
|
||||
if( pNew==0 ){
|
||||
assert( db->mallocFailed );
|
||||
pNew = &standin;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
}
|
||||
@ -89,7 +90,10 @@ Select *sqlite3SelectNew(
|
||||
clearSelect(db, pNew);
|
||||
if( pNew!=&standin ) sqlite3DbFree(db, pNew);
|
||||
pNew = 0;
|
||||
}else{
|
||||
assert( pNew->pSrc!=0 || pParse->nErr>0 );
|
||||
}
|
||||
assert( pNew!=&standin );
|
||||
return pNew;
|
||||
}
|
||||
|
||||
@ -419,12 +423,18 @@ static void pushOntoSorter(
|
||||
int nExpr = pOrderBy->nExpr;
|
||||
int regBase = sqlite3GetTempRange(pParse, nExpr+2);
|
||||
int regRecord = sqlite3GetTempReg(pParse);
|
||||
int op;
|
||||
sqlite3ExprCacheClear(pParse);
|
||||
sqlite3ExprCodeExprList(pParse, pOrderBy, regBase, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Sequence, pOrderBy->iECursor, regBase+nExpr);
|
||||
sqlite3ExprCodeMove(pParse, regData, regBase+nExpr+1, 1);
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nExpr + 2, regRecord);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, pOrderBy->iECursor, regRecord);
|
||||
if( pSelect->selFlags & SF_UseSorter ){
|
||||
op = OP_SorterInsert;
|
||||
}else{
|
||||
op = OP_IdxInsert;
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, op, pOrderBy->iECursor, regRecord);
|
||||
sqlite3ReleaseTempReg(pParse, regRecord);
|
||||
sqlite3ReleaseTempRange(pParse, regBase, nExpr+2);
|
||||
if( pSelect->iLimit ){
|
||||
@ -893,9 +903,20 @@ static void generateSortTail(
|
||||
}else{
|
||||
regRowid = sqlite3GetTempReg(pParse);
|
||||
}
|
||||
addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak);
|
||||
codeOffset(v, p, addrContinue);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iTab, pOrderBy->nExpr + 1, regRow);
|
||||
if( p->selFlags & SF_UseSorter ){
|
||||
int regSortOut = ++pParse->nMem;
|
||||
int ptab2 = pParse->nTab++;
|
||||
sqlite3VdbeAddOp3(v, OP_OpenPseudo, ptab2, regSortOut, pOrderBy->nExpr+2);
|
||||
addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak);
|
||||
codeOffset(v, p, addrContinue);
|
||||
sqlite3VdbeAddOp2(v, OP_SorterData, iTab, regSortOut);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, ptab2, pOrderBy->nExpr+1, regRow);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE);
|
||||
}else{
|
||||
addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak);
|
||||
codeOffset(v, p, addrContinue);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iTab, pOrderBy->nExpr+1, regRow);
|
||||
}
|
||||
switch( eDest ){
|
||||
case SRT_Table:
|
||||
case SRT_EphemTab: {
|
||||
@ -948,7 +969,11 @@ static void generateSortTail(
|
||||
/* The bottom of the loop
|
||||
*/
|
||||
sqlite3VdbeResolveLabel(v, addrContinue);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iTab, addr);
|
||||
if( p->selFlags & SF_UseSorter ){
|
||||
sqlite3VdbeAddOp2(v, OP_SorterNext, iTab, addr);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iTab, addr);
|
||||
}
|
||||
sqlite3VdbeResolveLabel(v, addrBreak);
|
||||
if( eDest==SRT_Output || eDest==SRT_Coroutine ){
|
||||
sqlite3VdbeAddOp2(v, OP_Close, pseudoTab, 0);
|
||||
@ -1247,7 +1272,10 @@ static int selectColumnsFromExprList(
|
||||
}else{
|
||||
Expr *pColExpr = p; /* The expression that is the result column name */
|
||||
Table *pTab; /* Table associated with this expression */
|
||||
while( pColExpr->op==TK_DOT ) pColExpr = pColExpr->pRight;
|
||||
while( pColExpr->op==TK_DOT ){
|
||||
pColExpr = pColExpr->pRight;
|
||||
assert( pColExpr!=0 );
|
||||
}
|
||||
if( pColExpr->op==TK_COLUMN && ALWAYS(pColExpr->pTab!=0) ){
|
||||
/* For columns use the column name name */
|
||||
int iCol = pColExpr->iColumn;
|
||||
@ -3721,6 +3749,7 @@ int sqlite3Select(
|
||||
int distinct; /* Table to use for the distinct set */
|
||||
int rc = 1; /* Value to return from this function */
|
||||
int addrSortIndex; /* Address of an OP_OpenEphemeral instruction */
|
||||
int addrDistinctIndex; /* Address of an OP_OpenEphemeral instruction */
|
||||
AggInfo sAggInfo; /* Information used by aggregate queries */
|
||||
int iEnd; /* Address of the end of the query */
|
||||
sqlite3 *db; /* The database connection */
|
||||
@ -3779,7 +3808,11 @@ int sqlite3Select(
|
||||
Select *pSub = pItem->pSelect;
|
||||
int isAggSub;
|
||||
|
||||
if( pSub==0 || pItem->isPopulated ) continue;
|
||||
if( pSub==0 ) continue;
|
||||
if( pItem->addrFillSub ){
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, pItem->regReturn, pItem->addrFillSub);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Increment Parse.nHeight by the height of the largest expression
|
||||
** tree refered to by this, the parent select. The child select
|
||||
@ -3790,21 +3823,44 @@ int sqlite3Select(
|
||||
*/
|
||||
pParse->nHeight += sqlite3SelectExprHeight(p);
|
||||
|
||||
/* Check to see if the subquery can be absorbed into the parent. */
|
||||
isAggSub = (pSub->selFlags & SF_Aggregate)!=0;
|
||||
if( flattenSubquery(pParse, p, i, isAgg, isAggSub) ){
|
||||
/* This subquery can be absorbed into its parent. */
|
||||
if( isAggSub ){
|
||||
isAgg = 1;
|
||||
p->selFlags |= SF_Aggregate;
|
||||
}
|
||||
i = -1;
|
||||
}else{
|
||||
/* Generate a subroutine that will fill an ephemeral table with
|
||||
** the content of this subquery. pItem->addrFillSub will point
|
||||
** to the address of the generated subroutine. pItem->regReturn
|
||||
** is a register allocated to hold the subroutine return address
|
||||
*/
|
||||
int topAddr;
|
||||
int onceAddr = 0;
|
||||
int retAddr;
|
||||
assert( pItem->addrFillSub==0 );
|
||||
pItem->regReturn = ++pParse->nMem;
|
||||
topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn);
|
||||
pItem->addrFillSub = topAddr+1;
|
||||
VdbeNoopComment((v, "materialize %s", pItem->pTab->zName));
|
||||
if( pItem->isCorrelated==0 && pParse->pTriggerTab==0 ){
|
||||
/* If the subquery is no correlated and if we are not inside of
|
||||
** a trigger, then we only need to compute the value of the subquery
|
||||
** once. */
|
||||
int regOnce = ++pParse->nMem;
|
||||
onceAddr = sqlite3VdbeAddOp1(v, OP_Once, regOnce);
|
||||
}
|
||||
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
|
||||
assert( pItem->isPopulated==0 );
|
||||
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
|
||||
sqlite3Select(pParse, pSub, &dest);
|
||||
pItem->isPopulated = 1;
|
||||
pItem->pTab->nRowEst = (unsigned)pSub->nSelectRow;
|
||||
if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr);
|
||||
retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn);
|
||||
VdbeComment((v, "end %s", pItem->pTab->zName));
|
||||
sqlite3VdbeChangeP1(v, topAddr, retAddr);
|
||||
|
||||
}
|
||||
if( /*pParse->nErr ||*/ db->mallocFailed ){
|
||||
goto select_end;
|
||||
@ -3847,16 +3903,6 @@ int sqlite3Select(
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If possible, rewrite the query to use GROUP BY instead of DISTINCT.
|
||||
** GROUP BY might use an index, DISTINCT never does.
|
||||
*/
|
||||
assert( p->pGroupBy==0 || (p->selFlags & SF_Aggregate)!=0 );
|
||||
if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct ){
|
||||
p->pGroupBy = sqlite3ExprListDup(db, p->pEList, 0);
|
||||
pGroupBy = p->pGroupBy;
|
||||
p->selFlags &= ~SF_Distinct;
|
||||
}
|
||||
|
||||
/* If there is both a GROUP BY and an ORDER BY clause and they are
|
||||
** identical, then disable the ORDER BY clause since the GROUP BY
|
||||
** will cause elements to come out in the correct order. This is
|
||||
@ -3869,6 +3915,30 @@ int sqlite3Select(
|
||||
pOrderBy = 0;
|
||||
}
|
||||
|
||||
/* If the query is DISTINCT with an ORDER BY but is not an aggregate, and
|
||||
** if the select-list is the same as the ORDER BY list, then this query
|
||||
** can be rewritten as a GROUP BY. In other words, this:
|
||||
**
|
||||
** SELECT DISTINCT xyz FROM ... ORDER BY xyz
|
||||
**
|
||||
** is transformed to:
|
||||
**
|
||||
** SELECT xyz FROM ... GROUP BY xyz
|
||||
**
|
||||
** The second form is preferred as a single index (or temp-table) may be
|
||||
** used for both the ORDER BY and DISTINCT processing. As originally
|
||||
** written the query must use a temp-table for at least one of the ORDER
|
||||
** BY and DISTINCT, and an index or separate temp-table for the other.
|
||||
*/
|
||||
if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct
|
||||
&& sqlite3ExprListCompare(pOrderBy, p->pEList)==0
|
||||
){
|
||||
p->selFlags &= ~SF_Distinct;
|
||||
p->pGroupBy = sqlite3ExprListDup(db, p->pEList, 0);
|
||||
pGroupBy = p->pGroupBy;
|
||||
pOrderBy = 0;
|
||||
}
|
||||
|
||||
/* If there is an ORDER BY clause, then this sorting
|
||||
** index might end up being unused if the data can be
|
||||
** extracted in pre-sorted order. If that is the case, then the
|
||||
@ -3899,27 +3969,30 @@ int sqlite3Select(
|
||||
iEnd = sqlite3VdbeMakeLabel(v);
|
||||
p->nSelectRow = (double)LARGEST_INT64;
|
||||
computeLimitRegisters(pParse, p, iEnd);
|
||||
if( p->iLimit==0 && addrSortIndex>=0 ){
|
||||
sqlite3VdbeGetOp(v, addrSortIndex)->opcode = OP_SorterOpen;
|
||||
p->selFlags |= SF_UseSorter;
|
||||
}
|
||||
|
||||
/* Open a virtual index to use for the distinct set.
|
||||
*/
|
||||
if( p->selFlags & SF_Distinct ){
|
||||
KeyInfo *pKeyInfo;
|
||||
assert( isAgg || pGroupBy );
|
||||
distinct = pParse->nTab++;
|
||||
pKeyInfo = keyInfoFromExprList(pParse, p->pEList);
|
||||
sqlite3VdbeAddOp4(v, OP_OpenEphemeral, distinct, 0, 0,
|
||||
(char*)pKeyInfo, P4_KEYINFO_HANDOFF);
|
||||
addrDistinctIndex = sqlite3VdbeAddOp4(v, OP_OpenEphemeral, distinct, 0, 0,
|
||||
(char*)pKeyInfo, P4_KEYINFO_HANDOFF);
|
||||
sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
|
||||
}else{
|
||||
distinct = -1;
|
||||
distinct = addrDistinctIndex = -1;
|
||||
}
|
||||
|
||||
/* Aggregate and non-aggregate queries are handled differently */
|
||||
if( !isAgg && pGroupBy==0 ){
|
||||
/* This case is for non-aggregate queries
|
||||
** Begin the database scan
|
||||
*/
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy, 0);
|
||||
ExprList *pDist = (isDistinct ? p->pEList : 0);
|
||||
|
||||
/* Begin the database scan. */
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy, pDist, 0);
|
||||
if( pWInfo==0 ) goto select_end;
|
||||
if( pWInfo->nRowOut < p->nSelectRow ) p->nSelectRow = pWInfo->nRowOut;
|
||||
|
||||
@ -3928,14 +4001,56 @@ int sqlite3Select(
|
||||
** into an OP_Noop.
|
||||
*/
|
||||
if( addrSortIndex>=0 && pOrderBy==0 ){
|
||||
sqlite3VdbeChangeToNoop(v, addrSortIndex, 1);
|
||||
sqlite3VdbeChangeToNoop(v, addrSortIndex);
|
||||
p->addrOpenEphm[2] = -1;
|
||||
}
|
||||
|
||||
/* Use the standard inner loop
|
||||
*/
|
||||
assert(!isDistinct);
|
||||
selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, -1, pDest,
|
||||
if( pWInfo->eDistinct ){
|
||||
VdbeOp *pOp; /* No longer required OpenEphemeral instr. */
|
||||
|
||||
assert( addrDistinctIndex>=0 );
|
||||
pOp = sqlite3VdbeGetOp(v, addrDistinctIndex);
|
||||
|
||||
assert( isDistinct );
|
||||
assert( pWInfo->eDistinct==WHERE_DISTINCT_ORDERED
|
||||
|| pWInfo->eDistinct==WHERE_DISTINCT_UNIQUE
|
||||
);
|
||||
distinct = -1;
|
||||
if( pWInfo->eDistinct==WHERE_DISTINCT_ORDERED ){
|
||||
int iJump;
|
||||
int iExpr;
|
||||
int iFlag = ++pParse->nMem;
|
||||
int iBase = pParse->nMem+1;
|
||||
int iBase2 = iBase + pEList->nExpr;
|
||||
pParse->nMem += (pEList->nExpr*2);
|
||||
|
||||
/* Change the OP_OpenEphemeral coded earlier to an OP_Integer. The
|
||||
** OP_Integer initializes the "first row" flag. */
|
||||
pOp->opcode = OP_Integer;
|
||||
pOp->p1 = 1;
|
||||
pOp->p2 = iFlag;
|
||||
|
||||
sqlite3ExprCodeExprList(pParse, pEList, iBase, 1);
|
||||
iJump = sqlite3VdbeCurrentAddr(v) + 1 + pEList->nExpr + 1 + 1;
|
||||
sqlite3VdbeAddOp2(v, OP_If, iFlag, iJump-1);
|
||||
for(iExpr=0; iExpr<pEList->nExpr; iExpr++){
|
||||
CollSeq *pColl = sqlite3ExprCollSeq(pParse, pEList->a[iExpr].pExpr);
|
||||
sqlite3VdbeAddOp3(v, OP_Ne, iBase+iExpr, iJump, iBase2+iExpr);
|
||||
sqlite3VdbeChangeP4(v, -1, (const char *)pColl, P4_COLLSEQ);
|
||||
sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, pWInfo->iContinue);
|
||||
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, iFlag);
|
||||
assert( sqlite3VdbeCurrentAddr(v)==iJump );
|
||||
sqlite3VdbeAddOp3(v, OP_Move, iBase, iBase2, pEList->nExpr);
|
||||
}else{
|
||||
pOp->opcode = OP_Noop;
|
||||
}
|
||||
}
|
||||
|
||||
/* Use the standard inner loop. */
|
||||
selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, distinct, pDest,
|
||||
pWInfo->iContinue, pWInfo->iBreak);
|
||||
|
||||
/* End the database scan loop.
|
||||
@ -3952,6 +4067,8 @@ int sqlite3Select(
|
||||
int iAbortFlag; /* Mem address which causes query abort if positive */
|
||||
int groupBySort; /* Rows come from source in GROUP BY order */
|
||||
int addrEnd; /* End of processing for this SELECT */
|
||||
int sortPTab = 0; /* Pseudotable used to decode sorting results */
|
||||
int sortOut = 0; /* Output register from the sorter */
|
||||
|
||||
/* Remove any and all aliases between the result set and the
|
||||
** GROUP BY clause.
|
||||
@ -4013,12 +4130,12 @@ int sqlite3Select(
|
||||
|
||||
/* If there is a GROUP BY clause we might need a sorting index to
|
||||
** implement it. Allocate that sorting index now. If it turns out
|
||||
** that we do not need it after all, the OpenEphemeral instruction
|
||||
** that we do not need it after all, the OP_SorterOpen instruction
|
||||
** will be converted into a Noop.
|
||||
*/
|
||||
sAggInfo.sortingIdx = pParse->nTab++;
|
||||
pKeyInfo = keyInfoFromExprList(pParse, pGroupBy);
|
||||
addrSortingIdx = sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
|
||||
addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen,
|
||||
sAggInfo.sortingIdx, sAggInfo.nSortingColumn,
|
||||
0, (char*)pKeyInfo, P4_KEYINFO_HANDOFF);
|
||||
|
||||
@ -4045,7 +4162,7 @@ int sqlite3Select(
|
||||
** in the right order to begin with.
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pGroupBy, 0);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pGroupBy, 0, 0);
|
||||
if( pWInfo==0 ) goto select_end;
|
||||
if( pGroupBy==0 ){
|
||||
/* The optimizer is able to deliver rows in group by order so
|
||||
@ -4099,11 +4216,14 @@ int sqlite3Select(
|
||||
}
|
||||
regRecord = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regRecord);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, sAggInfo.sortingIdx, regRecord);
|
||||
sqlite3VdbeAddOp2(v, OP_SorterInsert, sAggInfo.sortingIdx, regRecord);
|
||||
sqlite3ReleaseTempReg(pParse, regRecord);
|
||||
sqlite3ReleaseTempRange(pParse, regBase, nCol);
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
sqlite3VdbeAddOp2(v, OP_Sort, sAggInfo.sortingIdx, addrEnd);
|
||||
sAggInfo.sortingIdxPTab = sortPTab = pParse->nTab++;
|
||||
sortOut = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp3(v, OP_OpenPseudo, sortPTab, sortOut, nCol);
|
||||
sqlite3VdbeAddOp2(v, OP_SorterSort, sAggInfo.sortingIdx, addrEnd);
|
||||
VdbeComment((v, "GROUP BY sort"));
|
||||
sAggInfo.useSortingIdx = 1;
|
||||
sqlite3ExprCacheClear(pParse);
|
||||
@ -4116,9 +4236,13 @@ int sqlite3Select(
|
||||
*/
|
||||
addrTopOfLoop = sqlite3VdbeCurrentAddr(v);
|
||||
sqlite3ExprCacheClear(pParse);
|
||||
if( groupBySort ){
|
||||
sqlite3VdbeAddOp2(v, OP_SorterData, sAggInfo.sortingIdx, sortOut);
|
||||
}
|
||||
for(j=0; j<pGroupBy->nExpr; j++){
|
||||
if( groupBySort ){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, sAggInfo.sortingIdx, j, iBMem+j);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, sortPTab, j, iBMem+j);
|
||||
if( j==0 ) sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE);
|
||||
}else{
|
||||
sAggInfo.directMode = 1;
|
||||
sqlite3ExprCode(pParse, pGroupBy->a[j].pExpr, iBMem+j);
|
||||
@ -4157,10 +4281,10 @@ int sqlite3Select(
|
||||
/* End of the loop
|
||||
*/
|
||||
if( groupBySort ){
|
||||
sqlite3VdbeAddOp2(v, OP_Next, sAggInfo.sortingIdx, addrTopOfLoop);
|
||||
sqlite3VdbeAddOp2(v, OP_SorterNext, sAggInfo.sortingIdx, addrTopOfLoop);
|
||||
}else{
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
sqlite3VdbeChangeToNoop(v, addrSortingIdx, 1);
|
||||
sqlite3VdbeChangeToNoop(v, addrSortingIdx);
|
||||
}
|
||||
|
||||
/* Output the final row of result
|
||||
@ -4307,7 +4431,7 @@ int sqlite3Select(
|
||||
** of output.
|
||||
*/
|
||||
resetAccumulator(pParse, &sAggInfo);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax, flag);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax, 0, flag);
|
||||
if( pWInfo==0 ){
|
||||
sqlite3ExprListDelete(db, pDel);
|
||||
goto select_end;
|
||||
|
||||
165
src/shell.c
165
src/shell.c
@ -12,11 +12,22 @@
|
||||
** This file contains code to implement the "sqlite" command line
|
||||
** utility for accessing SQLite databases.
|
||||
*/
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
#if (defined(_WIN32) || defined(WIN32)) && !defined(_CRT_SECURE_NO_WARNINGS)
|
||||
/* This needs to come before any includes for MSVC compiler */
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Enable large-file support for fopen() and friends on unix.
|
||||
*/
|
||||
#ifndef SQLITE_DISABLE_LFS
|
||||
# define _LARGE_FILE 1
|
||||
# ifndef _FILE_OFFSET_BITS
|
||||
# define _FILE_OFFSET_BITS 64
|
||||
# endif
|
||||
# define _LARGEFILE_SOURCE 1
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
@ -60,7 +71,7 @@
|
||||
#else
|
||||
/* Make sure isatty() has a prototype.
|
||||
*/
|
||||
extern int isatty();
|
||||
extern int isatty(int);
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32_WCE)
|
||||
@ -74,6 +85,11 @@ extern int isatty();
|
||||
/* True if the timer is enabled */
|
||||
static int enableTimer = 0;
|
||||
|
||||
/* ctype macros that work with signed characters */
|
||||
#define IsSpace(X) isspace((unsigned char)X)
|
||||
#define IsDigit(X) isdigit((unsigned char)X)
|
||||
#define ToLower(X) (char)tolower((unsigned char)X)
|
||||
|
||||
#if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) && !defined(__RTP__) && !defined(_WRS_KERNEL)
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
@ -265,23 +281,23 @@ static void iotracePrintf(const char *zFormat, ...){
|
||||
*/
|
||||
static int isNumber(const char *z, int *realnum){
|
||||
if( *z=='-' || *z=='+' ) z++;
|
||||
if( !isdigit(*z) ){
|
||||
if( !IsDigit(*z) ){
|
||||
return 0;
|
||||
}
|
||||
z++;
|
||||
if( realnum ) *realnum = 0;
|
||||
while( isdigit(*z) ){ z++; }
|
||||
while( IsDigit(*z) ){ z++; }
|
||||
if( *z=='.' ){
|
||||
z++;
|
||||
if( !isdigit(*z) ) return 0;
|
||||
while( isdigit(*z) ){ z++; }
|
||||
if( !IsDigit(*z) ) return 0;
|
||||
while( IsDigit(*z) ){ z++; }
|
||||
if( realnum ) *realnum = 1;
|
||||
}
|
||||
if( *z=='e' || *z=='E' ){
|
||||
z++;
|
||||
if( *z=='+' || *z=='-' ) z++;
|
||||
if( !isdigit(*z) ) return 0;
|
||||
while( isdigit(*z) ){ z++; }
|
||||
if( !IsDigit(*z) ) return 0;
|
||||
while( IsDigit(*z) ){ z++; }
|
||||
if( realnum ) *realnum = 1;
|
||||
}
|
||||
return *z==0;
|
||||
@ -322,7 +338,6 @@ static char *local_getline(char *zPrompt, FILE *in){
|
||||
char *zLine;
|
||||
int nLine;
|
||||
int n;
|
||||
int eol;
|
||||
|
||||
if( zPrompt && *zPrompt ){
|
||||
printf("%s",zPrompt);
|
||||
@ -332,8 +347,7 @@ static char *local_getline(char *zPrompt, FILE *in){
|
||||
zLine = malloc( nLine );
|
||||
if( zLine==0 ) return 0;
|
||||
n = 0;
|
||||
eol = 0;
|
||||
while( !eol ){
|
||||
while( 1 ){
|
||||
if( n+100>nLine ){
|
||||
nLine = nLine*2 + 100;
|
||||
zLine = realloc(zLine, nLine);
|
||||
@ -345,7 +359,6 @@ static char *local_getline(char *zPrompt, FILE *in){
|
||||
return 0;
|
||||
}
|
||||
zLine[n] = 0;
|
||||
eol = 1;
|
||||
break;
|
||||
}
|
||||
while( zLine[n] ){ n++; }
|
||||
@ -353,7 +366,7 @@ static char *local_getline(char *zPrompt, FILE *in){
|
||||
n--;
|
||||
if( n>0 && zLine[n-1]=='\r' ) n--;
|
||||
zLine[n] = 0;
|
||||
eol = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
zLine = realloc( zLine, n+1 );
|
||||
@ -402,6 +415,7 @@ struct callback_data {
|
||||
int statsOn; /* True to display memory stats before each finalize */
|
||||
int cnt; /* Number of records displayed so far */
|
||||
FILE *out; /* Write results here */
|
||||
int nErr; /* Number of errors seen */
|
||||
int mode; /* An output mode setting */
|
||||
int writableSchema; /* True if PRAGMA writable_schema=ON */
|
||||
int showHeader; /* True to show column names in List or Column mode */
|
||||
@ -927,27 +941,33 @@ static char *appendText(char *zIn, char const *zAppend, char quote){
|
||||
** querying the SQLITE_MASTER table.
|
||||
*/
|
||||
static int run_table_dump_query(
|
||||
FILE *out, /* Send output here */
|
||||
sqlite3 *db, /* Database to query */
|
||||
const char *zSelect, /* SELECT statement to extract content */
|
||||
const char *zFirstRow /* Print before first row, if not NULL */
|
||||
struct callback_data *p, /* Query context */
|
||||
const char *zSelect, /* SELECT statement to extract content */
|
||||
const char *zFirstRow /* Print before first row, if not NULL */
|
||||
){
|
||||
sqlite3_stmt *pSelect;
|
||||
int rc;
|
||||
rc = sqlite3_prepare(db, zSelect, -1, &pSelect, 0);
|
||||
rc = sqlite3_prepare(p->db, zSelect, -1, &pSelect, 0);
|
||||
if( rc!=SQLITE_OK || !pSelect ){
|
||||
fprintf(p->out, "/**** ERROR: (%d) %s *****/\n", rc, sqlite3_errmsg(p->db));
|
||||
p->nErr++;
|
||||
return rc;
|
||||
}
|
||||
rc = sqlite3_step(pSelect);
|
||||
while( rc==SQLITE_ROW ){
|
||||
if( zFirstRow ){
|
||||
fprintf(out, "%s", zFirstRow);
|
||||
fprintf(p->out, "%s", zFirstRow);
|
||||
zFirstRow = 0;
|
||||
}
|
||||
fprintf(out, "%s;\n", sqlite3_column_text(pSelect, 0));
|
||||
fprintf(p->out, "%s;\n", sqlite3_column_text(pSelect, 0));
|
||||
rc = sqlite3_step(pSelect);
|
||||
}
|
||||
return sqlite3_finalize(pSelect);
|
||||
rc = sqlite3_finalize(pSelect);
|
||||
if( rc!=SQLITE_OK ){
|
||||
fprintf(p->out, "/**** ERROR: (%d) %s *****/\n", rc, sqlite3_errmsg(p->db));
|
||||
p->nErr++;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1029,7 +1049,12 @@ static int display_stats(
|
||||
fprintf(pArg->out, "Lookaside failures due to OOM: %d\n", iHiwtr);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Pager Heap Usage: %d bytes\n", iCur);
|
||||
fprintf(pArg->out, "Pager Heap Usage: %d bytes\n", iCur); iHiwtr = iCur = -1;
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1);
|
||||
fprintf(pArg->out, "Page cache hits: %d\n", iCur);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1);
|
||||
fprintf(pArg->out, "Page cache misses: %d\n", iCur);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Schema Heap Usage: %d bytes\n", iCur);
|
||||
@ -1069,6 +1094,7 @@ static int shell_exec(
|
||||
){
|
||||
sqlite3_stmt *pStmt = NULL; /* Statement to execute. */
|
||||
int rc = SQLITE_OK; /* Return Code */
|
||||
int rc2;
|
||||
const char *zLeftover; /* Tail of unprocessed SQL */
|
||||
|
||||
if( pzErrMsg ){
|
||||
@ -1085,7 +1111,7 @@ static int shell_exec(
|
||||
if( !pStmt ){
|
||||
/* this happens for a comment or white-space */
|
||||
zSql = zLeftover;
|
||||
while( isspace(zSql[0]) ) zSql++;
|
||||
while( IsSpace(zSql[0]) ) zSql++;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1162,10 +1188,11 @@ static int shell_exec(
|
||||
/* Finalize the statement just executed. If this fails, save a
|
||||
** copy of the error message. Otherwise, set zSql to point to the
|
||||
** next statement to execute. */
|
||||
rc = sqlite3_finalize(pStmt);
|
||||
rc2 = sqlite3_finalize(pStmt);
|
||||
if( rc!=SQLITE_NOMEM ) rc = rc2;
|
||||
if( rc==SQLITE_OK ){
|
||||
zSql = zLeftover;
|
||||
while( isspace(zSql[0]) ) zSql++;
|
||||
while( IsSpace(zSql[0]) ) zSql++;
|
||||
}else if( pzErrMsg ){
|
||||
*pzErrMsg = save_err_msg(db);
|
||||
}
|
||||
@ -1268,10 +1295,10 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
|
||||
zSelect = appendText(zSelect, "|| ')' FROM ", 0);
|
||||
zSelect = appendText(zSelect, zTable, '"');
|
||||
|
||||
rc = run_table_dump_query(p->out, p->db, zSelect, zPrepStmt);
|
||||
rc = run_table_dump_query(p, zSelect, zPrepStmt);
|
||||
if( rc==SQLITE_CORRUPT ){
|
||||
zSelect = appendText(zSelect, " ORDER BY rowid DESC", 0);
|
||||
rc = run_table_dump_query(p->out, p->db, zSelect, 0);
|
||||
run_table_dump_query(p, zSelect, 0);
|
||||
}
|
||||
if( zSelect ) free(zSelect);
|
||||
}
|
||||
@ -1287,19 +1314,30 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
|
||||
*/
|
||||
static int run_schema_dump_query(
|
||||
struct callback_data *p,
|
||||
const char *zQuery,
|
||||
char **pzErrMsg
|
||||
const char *zQuery
|
||||
){
|
||||
int rc;
|
||||
rc = sqlite3_exec(p->db, zQuery, dump_callback, p, pzErrMsg);
|
||||
char *zErr = 0;
|
||||
rc = sqlite3_exec(p->db, zQuery, dump_callback, p, &zErr);
|
||||
if( rc==SQLITE_CORRUPT ){
|
||||
char *zQ2;
|
||||
int len = strlen30(zQuery);
|
||||
if( pzErrMsg ) sqlite3_free(*pzErrMsg);
|
||||
fprintf(p->out, "/****** CORRUPTION ERROR *******/\n");
|
||||
if( zErr ){
|
||||
fprintf(p->out, "/****** %s ******/\n", zErr);
|
||||
sqlite3_free(zErr);
|
||||
zErr = 0;
|
||||
}
|
||||
zQ2 = malloc( len+100 );
|
||||
if( zQ2==0 ) return rc;
|
||||
sqlite3_snprintf(sizeof(zQ2), zQ2, "%s ORDER BY rowid DESC", zQuery);
|
||||
rc = sqlite3_exec(p->db, zQ2, dump_callback, p, pzErrMsg);
|
||||
rc = sqlite3_exec(p->db, zQ2, dump_callback, p, &zErr);
|
||||
if( rc ){
|
||||
fprintf(p->out, "/****** ERROR: %s ******/\n", zErr);
|
||||
}else{
|
||||
rc = SQLITE_CORRUPT;
|
||||
}
|
||||
sqlite3_free(zErr);
|
||||
free(zQ2);
|
||||
}
|
||||
return rc;
|
||||
@ -1436,7 +1474,7 @@ static int booleanValue(char *zArg){
|
||||
int val = atoi(zArg);
|
||||
int j;
|
||||
for(j=0; zArg[j]; j++){
|
||||
zArg[j] = (char)tolower(zArg[j]);
|
||||
zArg[j] = ToLower(zArg[j]);
|
||||
}
|
||||
if( strcmp(zArg,"on")==0 ){
|
||||
val = 1;
|
||||
@ -1462,7 +1500,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
/* Parse the input line into tokens.
|
||||
*/
|
||||
while( zLine[i] && nArg<ArraySize(azArg) ){
|
||||
while( isspace((unsigned char)zLine[i]) ){ i++; }
|
||||
while( IsSpace(zLine[i]) ){ i++; }
|
||||
if( zLine[i]==0 ) break;
|
||||
if( zLine[i]=='\'' || zLine[i]=='"' ){
|
||||
int delim = zLine[i++];
|
||||
@ -1474,7 +1512,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
if( delim=='"' ) resolve_backslashes(azArg[nArg-1]);
|
||||
}else{
|
||||
azArg[nArg++] = &zLine[i];
|
||||
while( zLine[i] && !isspace((unsigned char)zLine[i]) ){ i++; }
|
||||
while( zLine[i] && !IsSpace(zLine[i]) ){ i++; }
|
||||
if( zLine[i] ) zLine[i++] = 0;
|
||||
resolve_backslashes(azArg[nArg-1]);
|
||||
}
|
||||
@ -1545,7 +1583,6 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
}else
|
||||
|
||||
if( c=='d' && strncmp(azArg[0], "dump", n)==0 && nArg<3 ){
|
||||
char *zErrMsg = 0;
|
||||
open_db(p);
|
||||
/* When playing back a "dump", the content might appear in an order
|
||||
** which causes immediate foreign key constraints to be violated.
|
||||
@ -1553,17 +1590,18 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
fprintf(p->out, "PRAGMA foreign_keys=OFF;\n");
|
||||
fprintf(p->out, "BEGIN TRANSACTION;\n");
|
||||
p->writableSchema = 0;
|
||||
sqlite3_exec(p->db, "PRAGMA writable_schema=ON", 0, 0, 0);
|
||||
sqlite3_exec(p->db, "SAVEPOINT dump; PRAGMA writable_schema=ON", 0, 0, 0);
|
||||
p->nErr = 0;
|
||||
if( nArg==1 ){
|
||||
run_schema_dump_query(p,
|
||||
"SELECT name, type, sql FROM sqlite_master "
|
||||
"WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'", 0
|
||||
"WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'"
|
||||
);
|
||||
run_schema_dump_query(p,
|
||||
"SELECT name, type, sql FROM sqlite_master "
|
||||
"WHERE name=='sqlite_sequence'", 0
|
||||
"WHERE name=='sqlite_sequence'"
|
||||
);
|
||||
run_table_dump_query(p->out, p->db,
|
||||
run_table_dump_query(p,
|
||||
"SELECT sql FROM sqlite_master "
|
||||
"WHERE sql NOT NULL AND type IN ('index','trigger','view')", 0
|
||||
);
|
||||
@ -1574,8 +1612,8 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
run_schema_dump_query(p,
|
||||
"SELECT name, type, sql FROM sqlite_master "
|
||||
"WHERE tbl_name LIKE shellstatic() AND type=='table'"
|
||||
" AND sql NOT NULL", 0);
|
||||
run_table_dump_query(p->out, p->db,
|
||||
" AND sql NOT NULL");
|
||||
run_table_dump_query(p,
|
||||
"SELECT sql FROM sqlite_master "
|
||||
"WHERE sql NOT NULL"
|
||||
" AND type IN ('index','trigger','view')"
|
||||
@ -1588,13 +1626,9 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
fprintf(p->out, "PRAGMA writable_schema=OFF;\n");
|
||||
p->writableSchema = 0;
|
||||
}
|
||||
sqlite3_exec(p->db, "PRAGMA writable_schema=OFF", 0, 0, 0);
|
||||
if( zErrMsg ){
|
||||
fprintf(stderr,"Error: %s\n", zErrMsg);
|
||||
sqlite3_free(zErrMsg);
|
||||
}else{
|
||||
fprintf(p->out, "COMMIT;\n");
|
||||
}
|
||||
sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0);
|
||||
sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0);
|
||||
fprintf(p->out, p->nErr ? "ROLLBACK; -- due to errors\n" : "COMMIT;\n");
|
||||
}else
|
||||
|
||||
if( c=='e' && strncmp(azArg[0], "echo", n)==0 && nArg>1 && nArg<3 ){
|
||||
@ -1673,7 +1707,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
fprintf(stderr, "Error: non-null separator required for import\n");
|
||||
return 1;
|
||||
}
|
||||
zSql = sqlite3_mprintf("SELECT * FROM '%q'", zTable);
|
||||
zSql = sqlite3_mprintf("SELECT * FROM %s", zTable);
|
||||
if( zSql==0 ){
|
||||
fprintf(stderr, "Error: out of memory\n");
|
||||
return 1;
|
||||
@ -1695,7 +1729,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
fprintf(stderr, "Error: out of memory\n");
|
||||
return 1;
|
||||
}
|
||||
sqlite3_snprintf(nByte+20, zSql, "INSERT INTO '%q' VALUES(?", zTable);
|
||||
sqlite3_snprintf(nByte+20, zSql, "INSERT INTO %s VALUES(?", zTable);
|
||||
j = strlen30(zSql);
|
||||
for(i=1; i<nCol; i++){
|
||||
zSql[j++] = ',';
|
||||
@ -1727,7 +1761,6 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
zCommit = "COMMIT";
|
||||
while( (zLine = local_getline(0, in))!=0 ){
|
||||
char *z;
|
||||
i = 0;
|
||||
lineno++;
|
||||
azCol[0] = zLine;
|
||||
for(i=0, z=zLine; *z && *z!='\n' && *z!='\r'; z++){
|
||||
@ -2016,7 +2049,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
data.mode = MODE_Semi;
|
||||
if( nArg>1 ){
|
||||
int i;
|
||||
for(i=0; azArg[1][i]; i++) azArg[1][i] = (char)tolower(azArg[1][i]);
|
||||
for(i=0; azArg[1][i]; i++) azArg[1][i] = ToLower(azArg[1][i]);
|
||||
if( strcmp(azArg[1],"sqlite_master")==0 ){
|
||||
char *new_argv[2], *new_colv[2];
|
||||
new_argv[0] = "CREATE TABLE sqlite_master (\n"
|
||||
@ -2202,7 +2235,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
if( testctrl<0 ){
|
||||
testctrl = aCtrl[i].ctrlCode;
|
||||
}else{
|
||||
fprintf(stderr, "ambiguous option name: \"%s\"\n", azArg[i]);
|
||||
fprintf(stderr, "ambiguous option name: \"%s\"\n", azArg[1]);
|
||||
testctrl = -1;
|
||||
break;
|
||||
}
|
||||
@ -2339,7 +2372,7 @@ static int _contains_semicolon(const char *z, int N){
|
||||
*/
|
||||
static int _all_whitespace(const char *z){
|
||||
for(; *z; z++){
|
||||
if( isspace(*(unsigned char*)z) ) continue;
|
||||
if( IsSpace(z[0]) ) continue;
|
||||
if( *z=='/' && z[1]=='*' ){
|
||||
z += 2;
|
||||
while( *z && (*z!='*' || z[1]!='/') ){ z++; }
|
||||
@ -2364,11 +2397,11 @@ static int _all_whitespace(const char *z){
|
||||
** as is the Oracle "/".
|
||||
*/
|
||||
static int _is_command_terminator(const char *zLine){
|
||||
while( isspace(*(unsigned char*)zLine) ){ zLine++; };
|
||||
while( IsSpace(zLine[0]) ){ zLine++; };
|
||||
if( zLine[0]=='/' && _all_whitespace(&zLine[1]) ){
|
||||
return 1; /* Oracle */
|
||||
}
|
||||
if( tolower(zLine[0])=='g' && tolower(zLine[1])=='o'
|
||||
if( ToLower(zLine[0])=='g' && ToLower(zLine[1])=='o'
|
||||
&& _all_whitespace(&zLine[2]) ){
|
||||
return 1; /* SQL Server */
|
||||
}
|
||||
@ -2438,7 +2471,7 @@ static int process_input(struct callback_data *p, FILE *in){
|
||||
nSqlPrior = nSql;
|
||||
if( zSql==0 ){
|
||||
int i;
|
||||
for(i=0; zLine[i] && isspace((unsigned char)zLine[i]); i++){}
|
||||
for(i=0; zLine[i] && IsSpace(zLine[i]); i++){}
|
||||
if( zLine[i]!=0 ){
|
||||
nSql = strlen30(zLine);
|
||||
zSql = malloc( nSql+3 );
|
||||
@ -2632,6 +2665,9 @@ static const char zOptions[] =
|
||||
#ifdef SQLITE_ENABLE_VFSTRACE
|
||||
" -vfstrace enable tracing of all VFS calls\n"
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_MULTIPLEX
|
||||
" -multiplex enable the multiplexor VFS\n"
|
||||
#endif
|
||||
;
|
||||
static void usage(int showDetail){
|
||||
fprintf(stderr,
|
||||
@ -2707,6 +2743,7 @@ int main(int argc, char **argv){
|
||||
}else if( strcmp(argv[i],"-batch")==0 ){
|
||||
stdin_is_interactive = 0;
|
||||
}else if( strcmp(argv[i],"-heap")==0 ){
|
||||
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
|
||||
int j, c;
|
||||
const char *zSize;
|
||||
sqlite3_int64 szHeap;
|
||||
@ -2719,7 +2756,6 @@ int main(int argc, char **argv){
|
||||
if( c=='G' ){ szHeap *= 1000000000; break; }
|
||||
}
|
||||
if( szHeap>0x7fff0000 ) szHeap = 0x7fff0000;
|
||||
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
|
||||
sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64);
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_VFSTRACE
|
||||
@ -2732,6 +2768,11 @@ int main(int argc, char **argv){
|
||||
int makeDefault
|
||||
);
|
||||
vfstrace_register("trace",0,(int(*)(const char*,void*))fputs,stderr,1);
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_MULTIPLEX
|
||||
}else if( strcmp(argv[i],"-multiplex")==0 ){
|
||||
extern int sqlite3_multiple_initialize(const char*,int);
|
||||
sqlite3_multiplex_initialize(0, 1);
|
||||
#endif
|
||||
}else if( strcmp(argv[i],"-vfs")==0 ){
|
||||
sqlite3_vfs *pVfs = sqlite3_vfs_find(argv[++i]);
|
||||
@ -2851,8 +2892,14 @@ int main(int argc, char **argv){
|
||||
i++;
|
||||
}else if( strcmp(z,"-vfs")==0 ){
|
||||
i++;
|
||||
#ifdef SQLITE_ENABLE_VFSTRACE
|
||||
}else if( strcmp(z,"-vfstrace")==0 ){
|
||||
i++;
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_MULTIPLEX
|
||||
}else if( strcmp(z,"-multiplex")==0 ){
|
||||
i++;
|
||||
#endif
|
||||
}else if( strcmp(z,"-help")==0 || strcmp(z, "--help")==0 ){
|
||||
usage(1);
|
||||
}else{
|
||||
|
||||
@ -736,6 +736,41 @@ struct sqlite3_io_methods {
|
||||
** Applications should not call [sqlite3_file_control()] with this
|
||||
** opcode as doing so may disrupt the operation of the specialized VFSes
|
||||
** that do require it.
|
||||
**
|
||||
** ^The [SQLITE_FCNTL_WIN32_AV_RETRY] opcode is used to configure automatic
|
||||
** retry counts and intervals for certain disk I/O operations for the
|
||||
** windows [VFS] in order to work to provide robustness against
|
||||
** anti-virus programs. By default, the windows VFS will retry file read,
|
||||
** file write, and file delete operations up to 10 times, with a delay
|
||||
** of 25 milliseconds before the first retry and with the delay increasing
|
||||
** by an additional 25 milliseconds with each subsequent retry. This
|
||||
** opcode allows those to values (10 retries and 25 milliseconds of delay)
|
||||
** to be adjusted. The values are changed for all database connections
|
||||
** within the same process. The argument is a pointer to an array of two
|
||||
** integers where the first integer i the new retry count and the second
|
||||
** integer is the delay. If either integer is negative, then the setting
|
||||
** is not changed but instead the prior value of that setting is written
|
||||
** into the array entry, allowing the current retry settings to be
|
||||
** interrogated. The zDbName parameter is ignored.
|
||||
**
|
||||
** ^The [SQLITE_FCNTL_PERSIST_WAL] opcode is used to set or query the
|
||||
** persistent [WAL | Write AHead Log] setting. By default, the auxiliary
|
||||
** write ahead log and shared memory files used for transaction control
|
||||
** are automatically deleted when the latest connection to the database
|
||||
** closes. Setting persistent WAL mode causes those files to persist after
|
||||
** close. Persisting the files is useful when other processes that do not
|
||||
** have write permission on the directory containing the database file want
|
||||
** to read the database file, as the WAL and shared memory files must exist
|
||||
** in order for the database to be readable. The fourth parameter to
|
||||
** [sqlite3_file_control()] for this opcode should be a pointer to an integer.
|
||||
** That integer is 0 to disable persistent WAL mode or 1 to enable persistent
|
||||
** WAL mode. If the integer is -1, then it is overwritten with the current
|
||||
** WAL persistence setting.
|
||||
**
|
||||
** ^The [SQLITE_FCNTL_OVERWRITE] opcode is invoked by SQLite after opening
|
||||
** a write transaction to indicate that, unless it is rolled back for some
|
||||
** reason, the entire database file will be overwritten by the current
|
||||
** transaction. This is used by VACUUM operations.
|
||||
*/
|
||||
#define SQLITE_FCNTL_LOCKSTATE 1
|
||||
#define SQLITE_GET_LOCKPROXYFILE 2
|
||||
@ -745,7 +780,9 @@ struct sqlite3_io_methods {
|
||||
#define SQLITE_FCNTL_CHUNK_SIZE 6
|
||||
#define SQLITE_FCNTL_FILE_POINTER 7
|
||||
#define SQLITE_FCNTL_SYNC_OMITTED 8
|
||||
|
||||
#define SQLITE_FCNTL_WIN32_AV_RETRY 9
|
||||
#define SQLITE_FCNTL_PERSIST_WAL 10
|
||||
#define SQLITE_FCNTL_OVERWRITE 11
|
||||
|
||||
/*
|
||||
** CAPI3REF: Mutex Handle
|
||||
@ -1173,16 +1210,10 @@ int sqlite3_db_config(sqlite3*, int op, ...);
|
||||
** order to verify that SQLite recovers gracefully from such
|
||||
** conditions.
|
||||
**
|
||||
** The xMalloc and xFree methods must work like the
|
||||
** malloc() and free() functions from the standard C library.
|
||||
** The xRealloc method must work like realloc() from the standard C library
|
||||
** with the exception that if the second argument to xRealloc is zero,
|
||||
** xRealloc must be a no-op - it must not perform any allocation or
|
||||
** deallocation. ^SQLite guarantees that the second argument to
|
||||
** The xMalloc, xRealloc, and xFree methods must work like the
|
||||
** malloc(), realloc() and free() functions from the standard C library.
|
||||
** ^SQLite guarantees that the second argument to
|
||||
** xRealloc is always a value returned by a prior call to xRoundup.
|
||||
** And so in cases where xRoundup always returns a positive number,
|
||||
** xRealloc can perform exactly as the standard library realloc() and
|
||||
** still be in compliance with this specification.
|
||||
**
|
||||
** xSize should return the allocated size of a memory allocation
|
||||
** previously obtained from xMalloc or xRealloc. The allocated size
|
||||
@ -1368,8 +1399,8 @@ struct sqlite3_mem_methods {
|
||||
** allocator is engaged to handle all of SQLites memory allocation needs.
|
||||
** The first pointer (the memory pointer) must be aligned to an 8-byte
|
||||
** boundary or subsequent behavior of SQLite will be undefined.
|
||||
** The minimum allocation size is capped at 2^12. Reasonable values
|
||||
** for the minimum allocation size are 2^5 through 2^8.</dd>
|
||||
** The minimum allocation size is capped at 2**12. Reasonable values
|
||||
** for the minimum allocation size are 2**5 through 2**8.</dd>
|
||||
**
|
||||
** [[SQLITE_CONFIG_MUTEX]] <dt>SQLITE_CONFIG_MUTEX</dt>
|
||||
** <dd> ^(This option takes a single argument which is a pointer to an
|
||||
@ -2768,7 +2799,8 @@ int sqlite3_limit(sqlite3*, int id, int newVal);
|
||||
** that the supplied string is nul-terminated, then there is a small
|
||||
** performance advantage to be gained by passing an nByte parameter that
|
||||
** is equal to the number of bytes in the input string <i>including</i>
|
||||
** the nul-terminator bytes.
|
||||
** the nul-terminator bytes as this saves SQLite from having to
|
||||
** make a copy of the input string.
|
||||
**
|
||||
** ^If pzTail is not NULL then *pzTail is made to point to the first byte
|
||||
** past the end of the first SQL statement in zSql. These routines only
|
||||
@ -2819,7 +2851,7 @@ int sqlite3_limit(sqlite3*, int id, int newVal);
|
||||
** ^The specific value of WHERE-clause [parameter] might influence the
|
||||
** choice of query plan if the parameter is the left-hand side of a [LIKE]
|
||||
** or [GLOB] operator or if the parameter is compared to an indexed column
|
||||
** and the [SQLITE_ENABLE_STAT2] compile-time option is enabled.
|
||||
** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled.
|
||||
** the
|
||||
** </li>
|
||||
** </ol>
|
||||
@ -2989,6 +3021,13 @@ typedef struct sqlite3_context sqlite3_context;
|
||||
** number of <u>bytes</u> in the value, not the number of characters.)^
|
||||
** ^If the fourth parameter is negative, the length of the string is
|
||||
** the number of bytes up to the first zero terminator.
|
||||
** If a non-negative fourth parameter is provided to sqlite3_bind_text()
|
||||
** or sqlite3_bind_text16() then that parameter must be the byte offset
|
||||
** where the NUL terminator would occur assuming the string were NUL
|
||||
** terminated. If any NUL characters occur at byte offsets less than
|
||||
** the value of the fourth parameter then the resulting string value will
|
||||
** contain embedded NULs. The result of expressions involving strings
|
||||
** with embedded NULs is undefined.
|
||||
**
|
||||
** ^The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and
|
||||
** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or
|
||||
@ -3322,6 +3361,12 @@ int sqlite3_step(sqlite3_stmt*);
|
||||
** (via calls to the [sqlite3_column_int | sqlite3_column_*()] of
|
||||
** interfaces) then sqlite3_data_count(P) returns 0.
|
||||
** ^The sqlite3_data_count(P) routine also returns 0 if P is a NULL pointer.
|
||||
** ^The sqlite3_data_count(P) routine returns 0 if the previous call to
|
||||
** [sqlite3_step](P) returned [SQLITE_DONE]. ^The sqlite3_data_count(P)
|
||||
** will return non-zero if previous call to [sqlite3_step](P) returned
|
||||
** [SQLITE_ROW], except in the case of the [PRAGMA incremental_vacuum]
|
||||
** where it always returns zero since each step of that multi-step
|
||||
** pragma returns 0 columns of data.
|
||||
**
|
||||
** See also: [sqlite3_column_count()]
|
||||
*/
|
||||
@ -4001,7 +4046,12 @@ typedef void (*sqlite3_destructor_type)(void*);
|
||||
** ^If the 3rd parameter to the sqlite3_result_text* interfaces
|
||||
** is non-negative, then as many bytes (not characters) of the text
|
||||
** pointed to by the 2nd parameter are taken as the application-defined
|
||||
** function result.
|
||||
** function result. If the 3rd parameter is non-negative, then it
|
||||
** must be the byte offset into the string where the NUL terminator would
|
||||
** appear if the string where NUL terminated. If any NUL characters occur
|
||||
** in the string at a byte offset that is less than the value of the 3rd
|
||||
** parameter, then the resulting string will contain embedded NULs and the
|
||||
** result of expressions operating on strings with embedded NULs is undefined.
|
||||
** ^If the 4th parameter to the sqlite3_result_text* interfaces
|
||||
** or sqlite3_result_blob is a non-NULL pointer, then SQLite calls that
|
||||
** function as the destructor on the text or BLOB result when it has
|
||||
@ -5784,6 +5834,18 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
|
||||
** the database connection.)^
|
||||
** ^The highwater mark associated with SQLITE_DBSTATUS_STMT_USED is always 0.
|
||||
** </dd>
|
||||
**
|
||||
** [[SQLITE_DBSTATUS_CACHE_HIT]] ^(<dt>SQLITE_DBSTATUS_CACHE_HIT</dt>
|
||||
** <dd>This parameter returns the number of pager cache hits that have
|
||||
** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_HIT
|
||||
** is always 0.
|
||||
** </dd>
|
||||
**
|
||||
** [[SQLITE_DBSTATUS_CACHE_MISS]] ^(<dt>SQLITE_DBSTATUS_CACHE_MISS</dt>
|
||||
** <dd>This parameter returns the number of pager cache misses that have
|
||||
** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_MISS
|
||||
** is always 0.
|
||||
** </dd>
|
||||
** </dl>
|
||||
*/
|
||||
#define SQLITE_DBSTATUS_LOOKASIDE_USED 0
|
||||
@ -5793,7 +5855,9 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
|
||||
#define SQLITE_DBSTATUS_LOOKASIDE_HIT 4
|
||||
#define SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE 5
|
||||
#define SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL 6
|
||||
#define SQLITE_DBSTATUS_MAX 6 /* Largest defined DBSTATUS */
|
||||
#define SQLITE_DBSTATUS_CACHE_HIT 7
|
||||
#define SQLITE_DBSTATUS_CACHE_MISS 8
|
||||
#define SQLITE_DBSTATUS_MAX 8 /* Largest defined DBSTATUS */
|
||||
|
||||
|
||||
/*
|
||||
@ -5847,7 +5911,6 @@ int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
|
||||
** A non-zero value in this counter may indicate an opportunity to
|
||||
** improvement performance by adding permanent indices that do not
|
||||
** need to be reinitialized each time the statement is run.</dd>
|
||||
**
|
||||
** </dl>
|
||||
*/
|
||||
#define SQLITE_STMTSTATUS_FULLSCAN_STEP 1
|
||||
|
||||
@ -49,8 +49,10 @@ struct sqlite3_api_routines {
|
||||
int (*busy_timeout)(sqlite3*,int ms);
|
||||
int (*changes)(sqlite3*);
|
||||
int (*close)(sqlite3*);
|
||||
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,int eTextRep,const char*));
|
||||
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,int eTextRep,const void*));
|
||||
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
||||
int eTextRep,const char*));
|
||||
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
||||
int eTextRep,const void*));
|
||||
const void * (*column_blob)(sqlite3_stmt*,int iCol);
|
||||
int (*column_bytes)(sqlite3_stmt*,int iCol);
|
||||
int (*column_bytes16)(sqlite3_stmt*,int iCol);
|
||||
@ -75,10 +77,18 @@ struct sqlite3_api_routines {
|
||||
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
|
||||
int (*complete)(const char*sql);
|
||||
int (*complete16)(const void*sql);
|
||||
int (*create_collation)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*));
|
||||
int (*create_collation16)(sqlite3*,const void*,int,void*,int(*)(void*,int,const void*,int,const void*));
|
||||
int (*create_function)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*));
|
||||
int (*create_function16)(sqlite3*,const void*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*));
|
||||
int (*create_collation)(sqlite3*,const char*,int,void*,
|
||||
int(*)(void*,int,const void*,int,const void*));
|
||||
int (*create_collation16)(sqlite3*,const void*,int,void*,
|
||||
int(*)(void*,int,const void*,int,const void*));
|
||||
int (*create_function)(sqlite3*,const char*,int,int,void*,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*));
|
||||
int (*create_function16)(sqlite3*,const void*,int,int,void*,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*));
|
||||
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
|
||||
int (*data_count)(sqlite3_stmt*pStmt);
|
||||
sqlite3 * (*db_handle)(sqlite3_stmt*);
|
||||
@ -123,16 +133,19 @@ struct sqlite3_api_routines {
|
||||
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_value)(sqlite3_context*,sqlite3_value*);
|
||||
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
|
||||
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,const char*,const char*),void*);
|
||||
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
|
||||
const char*,const char*),void*);
|
||||
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
|
||||
char * (*snprintf)(int,char*,const char*,...);
|
||||
int (*step)(sqlite3_stmt*);
|
||||
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,char const**,char const**,int*,int*,int*);
|
||||
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
|
||||
char const**,char const**,int*,int*,int*);
|
||||
void (*thread_cleanup)(void);
|
||||
int (*total_changes)(sqlite3*);
|
||||
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
|
||||
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
|
||||
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,sqlite_int64),void*);
|
||||
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
|
||||
sqlite_int64),void*);
|
||||
void * (*user_data)(sqlite3_context*);
|
||||
const void * (*value_blob)(sqlite3_value*);
|
||||
int (*value_bytes)(sqlite3_value*);
|
||||
@ -154,15 +167,19 @@ struct sqlite3_api_routines {
|
||||
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
|
||||
int (*clear_bindings)(sqlite3_stmt*);
|
||||
/* Added by 3.4.1 */
|
||||
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,void (*xDestroy)(void *));
|
||||
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
|
||||
void (*xDestroy)(void *));
|
||||
/* Added by 3.5.0 */
|
||||
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
|
||||
int (*blob_bytes)(sqlite3_blob*);
|
||||
int (*blob_close)(sqlite3_blob*);
|
||||
int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,int,sqlite3_blob**);
|
||||
int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
|
||||
int,sqlite3_blob**);
|
||||
int (*blob_read)(sqlite3_blob*,void*,int,int);
|
||||
int (*blob_write)(sqlite3_blob*,const void*,int,int);
|
||||
int (*create_collation_v2)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*),void(*)(void*));
|
||||
int (*create_collation_v2)(sqlite3*,const char*,int,void*,
|
||||
int(*)(void*,int,const void*,int,const void*),
|
||||
void(*)(void*));
|
||||
int (*file_control)(sqlite3*,const char*,int,void*);
|
||||
sqlite3_int64 (*memory_highwater)(int);
|
||||
sqlite3_int64 (*memory_used)(void);
|
||||
@ -198,7 +215,11 @@ struct sqlite3_api_routines {
|
||||
int (*backup_step)(sqlite3_backup*,int);
|
||||
const char *(*compileoption_get)(int);
|
||||
int (*compileoption_used)(const char*);
|
||||
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*),void(*xDestroy)(void*));
|
||||
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*),
|
||||
void(*xDestroy)(void*));
|
||||
int (*db_config)(sqlite3*,int,...);
|
||||
sqlite3_mutex *(*db_mutex)(sqlite3*);
|
||||
int (*db_status)(sqlite3*,int,int*,int*,int);
|
||||
@ -212,6 +233,9 @@ struct sqlite3_api_routines {
|
||||
int (*wal_autocheckpoint)(sqlite3*,int);
|
||||
int (*wal_checkpoint)(sqlite3*,const char*);
|
||||
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
|
||||
int (*blob_reopen)(sqlite3_blob*,sqlite3_int64);
|
||||
int (*vtab_config)(sqlite3*,int op,...);
|
||||
int (*vtab_on_conflict)(sqlite3*);
|
||||
};
|
||||
|
||||
/*
|
||||
@ -412,6 +436,9 @@ struct sqlite3_api_routines {
|
||||
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
|
||||
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
|
||||
#define sqlite3_wal_hook sqlite3_api->wal_hook
|
||||
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
|
||||
#define sqlite3_vtab_config sqlite3_api->vtab_config
|
||||
#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict
|
||||
#endif /* SQLITE_CORE */
|
||||
|
||||
#define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api = 0;
|
||||
|
||||
@ -76,13 +76,6 @@
|
||||
#include <inttypes.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The number of samples of an index that SQLite takes in order to
|
||||
** construct a histogram of the table content when running ANALYZE
|
||||
** and with SQLITE_ENABLE_STAT2
|
||||
*/
|
||||
#define SQLITE_INDEX_SAMPLES 10
|
||||
|
||||
/*
|
||||
** The following macros are used to cast pointers to integers and
|
||||
** integers to pointers. The way you do this varies from one compiler
|
||||
@ -147,19 +140,25 @@
|
||||
** specify which memory allocation subsystem to use.
|
||||
**
|
||||
** SQLITE_SYSTEM_MALLOC // Use normal system malloc()
|
||||
** SQLITE_WIN32_MALLOC // Use Win32 native heap API
|
||||
** SQLITE_MEMDEBUG // Debugging version of system malloc()
|
||||
**
|
||||
** On Windows, if the SQLITE_WIN32_MALLOC_VALIDATE macro is defined and the
|
||||
** assert() macro is enabled, each call into the Win32 native heap subsystem
|
||||
** will cause HeapValidate to be called. If heap validation should fail, an
|
||||
** assertion will be triggered.
|
||||
**
|
||||
** (Historical note: There used to be several other options, but we've
|
||||
** pared it down to just these two.)
|
||||
** pared it down to just these three.)
|
||||
**
|
||||
** If none of the above are defined, then set SQLITE_SYSTEM_MALLOC as
|
||||
** the default.
|
||||
*/
|
||||
#if defined(SQLITE_SYSTEM_MALLOC)+defined(SQLITE_MEMDEBUG)>1
|
||||
#if defined(SQLITE_SYSTEM_MALLOC)+defined(SQLITE_WIN32_MALLOC)+defined(SQLITE_MEMDEBUG)>1
|
||||
# error "At most one of the following compile-time configuration options\
|
||||
is allows: SQLITE_SYSTEM_MALLOC, SQLITE_MEMDEBUG"
|
||||
is allows: SQLITE_SYSTEM_MALLOC, SQLITE_WIN32_MALLOC, SQLITE_MEMDEBUG"
|
||||
#endif
|
||||
#if defined(SQLITE_SYSTEM_MALLOC)+defined(SQLITE_MEMDEBUG)==0
|
||||
#if defined(SQLITE_SYSTEM_MALLOC)+defined(SQLITE_WIN32_MALLOC)+defined(SQLITE_MEMDEBUG)==0
|
||||
# define SQLITE_SYSTEM_MALLOC 1
|
||||
#endif
|
||||
|
||||
@ -445,6 +444,18 @@ typedef INT8_TYPE i8; /* 1-byte signed integer */
|
||||
*/
|
||||
#define SQLITE_MAX_U32 ((((u64)1)<<32)-1)
|
||||
|
||||
/*
|
||||
** The datatype used to store estimates of the number of rows in a
|
||||
** table or index. This is an unsigned integer type. For 99.9% of
|
||||
** the world, a 32-bit integer is sufficient. But a 64-bit integer
|
||||
** can be used at compile-time if desired.
|
||||
*/
|
||||
#ifdef SQLITE_64BIT_STATS
|
||||
typedef u64 tRowcnt; /* 64-bit only if requested at compile-time */
|
||||
#else
|
||||
typedef u32 tRowcnt; /* 32-bit is the default */
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Macros to determine whether the machine is big or little endian,
|
||||
** evaluated at runtime.
|
||||
@ -956,6 +967,7 @@ struct sqlite3 {
|
||||
#define SQLITE_GroupByOrder 0x20 /* Disable GROUPBY cover of ORDERBY */
|
||||
#define SQLITE_FactorOutConst 0x40 /* Disable factoring out constants */
|
||||
#define SQLITE_IdxRealAsInt 0x80 /* Store REAL as INT in indices */
|
||||
#define SQLITE_DistinctOpt 0x80 /* DISTINCT using indexes */
|
||||
#define SQLITE_OptMask 0xff /* Mask of all disablable opts */
|
||||
|
||||
/*
|
||||
@ -1277,7 +1289,7 @@ struct Table {
|
||||
Column *aCol; /* Information about each column */
|
||||
Index *pIndex; /* List of SQL indexes on this table. */
|
||||
int tnum; /* Root BTree node for this table (see note above) */
|
||||
unsigned nRowEst; /* Estimated rows in table - from sqlite_stat1 table */
|
||||
tRowcnt nRowEst; /* Estimated rows in table - from sqlite_stat1 table */
|
||||
Select *pSelect; /* NULL for tables. Points to definition if a view. */
|
||||
u16 nRef; /* Number of pointers to this Table */
|
||||
u8 tabFlags; /* Mask of TF_* values */
|
||||
@ -1476,7 +1488,7 @@ struct Index {
|
||||
char *zName; /* Name of this index */
|
||||
int nColumn; /* Number of columns in the table used by this index */
|
||||
int *aiColumn; /* Which columns are used by this index. 1st is 0 */
|
||||
unsigned *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */
|
||||
tRowcnt *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */
|
||||
Table *pTable; /* The SQL table being indexed */
|
||||
int tnum; /* Page containing root of this index in database file */
|
||||
u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
|
||||
@ -1487,20 +1499,29 @@ struct Index {
|
||||
Schema *pSchema; /* Schema containing this index */
|
||||
u8 *aSortOrder; /* Array of size Index.nColumn. True==DESC, False==ASC */
|
||||
char **azColl; /* Array of collation sequence names for index */
|
||||
IndexSample *aSample; /* Array of SQLITE_INDEX_SAMPLES samples */
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
int nSample; /* Number of elements in aSample[] */
|
||||
tRowcnt avgEq; /* Average nEq value for key values not in aSample */
|
||||
IndexSample *aSample; /* Samples of the left-most key */
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
** Each sample stored in the sqlite_stat2 table is represented in memory
|
||||
** using a structure of this type.
|
||||
** Each sample stored in the sqlite_stat3 table is represented in memory
|
||||
** using a structure of this type. See documentation at the top of the
|
||||
** analyze.c source file for additional information.
|
||||
*/
|
||||
struct IndexSample {
|
||||
union {
|
||||
char *z; /* Value if eType is SQLITE_TEXT or SQLITE_BLOB */
|
||||
double r; /* Value if eType is SQLITE_FLOAT or SQLITE_INTEGER */
|
||||
double r; /* Value if eType is SQLITE_FLOAT */
|
||||
i64 i; /* Value if eType is SQLITE_INTEGER */
|
||||
} u;
|
||||
u8 eType; /* SQLITE_NULL, SQLITE_INTEGER ... etc. */
|
||||
u8 nByte; /* Size in byte of text or blob. */
|
||||
int nByte; /* Size in byte of text or blob. */
|
||||
tRowcnt nEq; /* Est. number of rows where the key equals this sample */
|
||||
tRowcnt nLt; /* Est. number of rows where key is less than this sample */
|
||||
tRowcnt nDLt; /* Est. number of distinct keys less than this sample */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -1535,6 +1556,7 @@ struct AggInfo {
|
||||
u8 useSortingIdx; /* In direct mode, reference the sorting index rather
|
||||
** than the source table */
|
||||
int sortingIdx; /* Cursor number of the sorting index */
|
||||
int sortingIdxPTab; /* Cursor number of pseudo-table */
|
||||
ExprList *pGroupBy; /* The group by clause */
|
||||
int nSortingColumn; /* Number of columns in the sorting index */
|
||||
struct AggInfo_col { /* For each column used in source tables */
|
||||
@ -1844,9 +1866,11 @@ struct SrcList {
|
||||
char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */
|
||||
Table *pTab; /* An SQL table corresponding to zName */
|
||||
Select *pSelect; /* A SELECT statement used in place of a table name */
|
||||
u8 isPopulated; /* Temporary table associated with SELECT is populated */
|
||||
int addrFillSub; /* Address of subroutine to manifest a subquery */
|
||||
int regReturn; /* Register holding return address of addrFillSub */
|
||||
u8 jointype; /* Type of join between this able and the previous */
|
||||
u8 notIndexed; /* True if there is a NOT INDEXED clause */
|
||||
u8 isCorrelated; /* True if sub-query is correlated */
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
u8 iSelectId; /* If pSelect!=0, the id of the sub-select in EQP */
|
||||
#endif
|
||||
@ -1949,10 +1973,10 @@ struct WhereLevel {
|
||||
#define WHERE_ORDERBY_MAX 0x0002 /* ORDER BY processing for max() func */
|
||||
#define WHERE_ONEPASS_DESIRED 0x0004 /* Want to do one-pass UPDATE/DELETE */
|
||||
#define WHERE_DUPLICATES_OK 0x0008 /* Ok to return a row more than once */
|
||||
#define WHERE_OMIT_OPEN 0x0010 /* Table cursors are already open */
|
||||
#define WHERE_OMIT_CLOSE 0x0020 /* Omit close of table & index cursors */
|
||||
#define WHERE_FORCE_TABLE 0x0040 /* Do not use an index-only search */
|
||||
#define WHERE_ONETABLE_ONLY 0x0080 /* Only code the 1st table in pTabList */
|
||||
#define WHERE_OMIT_OPEN_CLOSE 0x0010 /* Table cursors are already open */
|
||||
#define WHERE_FORCE_TABLE 0x0020 /* Do not use an index-only search */
|
||||
#define WHERE_ONETABLE_ONLY 0x0040 /* Only code the 1st table in pTabList */
|
||||
#define WHERE_AND_ONLY 0x0080 /* Don't use indices for OR terms */
|
||||
|
||||
/*
|
||||
** The WHERE clause processing routine has two halves. The
|
||||
@ -1966,6 +1990,7 @@ struct WhereInfo {
|
||||
u16 wctrlFlags; /* Flags originally passed to sqlite3WhereBegin() */
|
||||
u8 okOnePass; /* Ok to use one-pass algorithm for UPDATE or DELETE */
|
||||
u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */
|
||||
u8 eDistinct;
|
||||
SrcList *pTabList; /* List of tables in the join */
|
||||
int iTop; /* The very beginning of the WHERE loop */
|
||||
int iContinue; /* Jump here to continue with next record */
|
||||
@ -1977,6 +2002,9 @@ struct WhereInfo {
|
||||
WhereLevel a[1]; /* Information about each nest loop in WHERE */
|
||||
};
|
||||
|
||||
#define WHERE_DISTINCT_UNIQUE 1
|
||||
#define WHERE_DISTINCT_ORDERED 2
|
||||
|
||||
/*
|
||||
** A NameContext defines a context in which to resolve table and column
|
||||
** names. The context consists of a list of tables (the pSrcList) field and
|
||||
@ -2062,6 +2090,7 @@ struct Select {
|
||||
#define SF_UsesEphemeral 0x0008 /* Uses the OpenEphemeral opcode */
|
||||
#define SF_Expanded 0x0010 /* sqlite3SelectExpand() called on this */
|
||||
#define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */
|
||||
#define SF_UseSorter 0x0040 /* Sort using a sorter */
|
||||
|
||||
|
||||
/*
|
||||
@ -2701,6 +2730,7 @@ void sqlite3CreateView(Parse*,Token*,Token*,Token*,Select*,int,int);
|
||||
#endif
|
||||
|
||||
void sqlite3DropTable(Parse*, SrcList*, int, int);
|
||||
void sqlite3CodeDropTable(Parse*, Table*, int, int);
|
||||
void sqlite3DeleteTable(sqlite3*, Table*);
|
||||
#ifndef SQLITE_OMIT_AUTOINCREMENT
|
||||
void sqlite3AutoincrementBegin(Parse *pParse);
|
||||
@ -2738,7 +2768,7 @@ Expr *sqlite3LimitWhere(Parse *, SrcList *, Expr *, ExprList *, Expr *, Expr *,
|
||||
#endif
|
||||
void sqlite3DeleteFrom(Parse*, SrcList*, Expr*);
|
||||
void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
|
||||
WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**, u16);
|
||||
WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**,ExprList*,u16);
|
||||
void sqlite3WhereEnd(WhereInfo*);
|
||||
int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int);
|
||||
void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int);
|
||||
@ -2957,7 +2987,7 @@ void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8,
|
||||
void sqlite3ValueFree(sqlite3_value*);
|
||||
sqlite3_value *sqlite3ValueNew(sqlite3 *);
|
||||
char *sqlite3Utf16to8(sqlite3 *, const void*, int, u8);
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
char *sqlite3Utf8to16(sqlite3 *, u8, char *, int, int *);
|
||||
#endif
|
||||
int sqlite3ValueFromExpr(sqlite3 *, Expr *, u8, u8, sqlite3_value **);
|
||||
@ -3059,6 +3089,7 @@ void sqlite3AutoLoadExtensions(sqlite3*);
|
||||
# define sqlite3VtabUnlock(X)
|
||||
# define sqlite3VtabUnlockList(X)
|
||||
# define sqlite3VtabSavepoint(X, Y, Z) SQLITE_OK
|
||||
# define sqlite3GetVTable(X,Y) ((VTable*)0)
|
||||
#else
|
||||
void sqlite3VtabClear(sqlite3 *db, Table*);
|
||||
int sqlite3VtabSync(sqlite3 *db, char **);
|
||||
@ -3068,6 +3099,7 @@ void sqlite3AutoLoadExtensions(sqlite3*);
|
||||
void sqlite3VtabUnlock(VTable *);
|
||||
void sqlite3VtabUnlockList(sqlite3*);
|
||||
int sqlite3VtabSavepoint(sqlite3 *, int, int);
|
||||
VTable *sqlite3GetVTable(sqlite3*, Table*);
|
||||
# define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0)
|
||||
#endif
|
||||
void sqlite3VtabMakeWritable(Parse*,Table*);
|
||||
@ -3087,7 +3119,6 @@ int sqlite3Reprepare(Vdbe*);
|
||||
void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*);
|
||||
CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *);
|
||||
int sqlite3TempInMemory(const sqlite3*);
|
||||
VTable *sqlite3GetVTable(sqlite3*, Table*);
|
||||
const char *sqlite3JournalModename(int);
|
||||
int sqlite3Checkpoint(sqlite3*, int, int, int*, int*);
|
||||
int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int);
|
||||
|
||||
22
src/status.c
22
src/status.c
@ -218,6 +218,28 @@ int sqlite3_db_status(
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
** Set *pCurrent to the total cache hits or misses encountered by all
|
||||
** pagers the database handle is connected to. *pHighwater is always set
|
||||
** to zero.
|
||||
*/
|
||||
case SQLITE_DBSTATUS_CACHE_HIT:
|
||||
case SQLITE_DBSTATUS_CACHE_MISS: {
|
||||
int i;
|
||||
int nRet = 0;
|
||||
assert( SQLITE_DBSTATUS_CACHE_MISS==SQLITE_DBSTATUS_CACHE_HIT+1 );
|
||||
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( db->aDb[i].pBt ){
|
||||
Pager *pPager = sqlite3BtreePager(db->aDb[i].pBt);
|
||||
sqlite3PagerCacheStat(pPager, op, resetFlag, &nRet);
|
||||
}
|
||||
}
|
||||
*pHighwater = 0;
|
||||
*pCurrent = nRet;
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
228
src/tclsqlite.c
228
src/tclsqlite.c
@ -107,6 +107,11 @@ typedef struct IncrblobChannel IncrblobChannel;
|
||||
/*
|
||||
** There is one instance of this structure for each SQLite database
|
||||
** that has been opened by the SQLite TCL interface.
|
||||
**
|
||||
** If this module is built with SQLITE_TEST defined (to create the SQLite
|
||||
** testfixture executable), then it may be configured to use either
|
||||
** sqlite3_prepare_v2() or sqlite3_prepare() to prepare SQL statements.
|
||||
** If SqliteDb.bLegacyPrepare is true, sqlite3_prepare() is used.
|
||||
*/
|
||||
typedef struct SqliteDb SqliteDb;
|
||||
struct SqliteDb {
|
||||
@ -135,6 +140,9 @@ struct SqliteDb {
|
||||
IncrblobChannel *pIncrblob;/* Linked list of open incrblob channels */
|
||||
int nStep, nSort, nIndex; /* Statistics for most recent operation */
|
||||
int nTransaction; /* Number of nested [transaction] methods */
|
||||
#ifdef SQLITE_TEST
|
||||
int bLegacyPrepare; /* True to use sqlite3_prepare() */
|
||||
#endif
|
||||
};
|
||||
|
||||
struct IncrblobChannel {
|
||||
@ -429,20 +437,33 @@ static SqlFunc *findSqlFunc(SqliteDb *pDb, const char *zName){
|
||||
return pNew;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free a single SqlPreparedStmt object.
|
||||
*/
|
||||
static void dbFreeStmt(SqlPreparedStmt *pStmt){
|
||||
#ifdef SQLITE_TEST
|
||||
if( sqlite3_sql(pStmt->pStmt)==0 ){
|
||||
Tcl_Free((char *)pStmt->zSql);
|
||||
}
|
||||
#endif
|
||||
sqlite3_finalize(pStmt->pStmt);
|
||||
Tcl_Free((char *)pStmt);
|
||||
}
|
||||
|
||||
/*
|
||||
** Finalize and free a list of prepared statements
|
||||
*/
|
||||
static void flushStmtCache( SqliteDb *pDb ){
|
||||
static void flushStmtCache(SqliteDb *pDb){
|
||||
SqlPreparedStmt *pPreStmt;
|
||||
SqlPreparedStmt *pNext;
|
||||
|
||||
while( pDb->stmtList ){
|
||||
sqlite3_finalize( pDb->stmtList->pStmt );
|
||||
pPreStmt = pDb->stmtList;
|
||||
pDb->stmtList = pDb->stmtList->pNext;
|
||||
Tcl_Free( (char*)pPreStmt );
|
||||
for(pPreStmt = pDb->stmtList; pPreStmt; pPreStmt=pNext){
|
||||
pNext = pPreStmt->pNext;
|
||||
dbFreeStmt(pPreStmt);
|
||||
}
|
||||
pDb->nStmt = 0;
|
||||
pDb->stmtLast = 0;
|
||||
pDb->stmtList = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -898,7 +919,7 @@ static int auth_callback(
|
||||
Tcl_DStringAppendElement(&str, zArg4 ? zArg4 : "");
|
||||
rc = Tcl_GlobalEval(pDb->interp, Tcl_DStringValue(&str));
|
||||
Tcl_DStringFree(&str);
|
||||
zReply = Tcl_GetStringResult(pDb->interp);
|
||||
zReply = rc==TCL_OK ? Tcl_GetStringResult(pDb->interp) : "SQLITE_DENY";
|
||||
if( strcmp(zReply,"SQLITE_OK")==0 ){
|
||||
rc = SQLITE_OK;
|
||||
}else if( strcmp(zReply,"SQLITE_DENY")==0 ){
|
||||
@ -947,14 +968,12 @@ static char *local_getline(char *zPrompt, FILE *in){
|
||||
char *zLine;
|
||||
int nLine;
|
||||
int n;
|
||||
int eol;
|
||||
|
||||
nLine = 100;
|
||||
zLine = malloc( nLine );
|
||||
if( zLine==0 ) return 0;
|
||||
n = 0;
|
||||
eol = 0;
|
||||
while( !eol ){
|
||||
while( 1 ){
|
||||
if( n+100>nLine ){
|
||||
nLine = nLine*2 + 100;
|
||||
zLine = realloc(zLine, nLine);
|
||||
@ -966,14 +985,13 @@ static char *local_getline(char *zPrompt, FILE *in){
|
||||
return 0;
|
||||
}
|
||||
zLine[n] = 0;
|
||||
eol = 1;
|
||||
break;
|
||||
}
|
||||
while( zLine[n] ){ n++; }
|
||||
if( n>0 && zLine[n-1]=='\n' ){
|
||||
n--;
|
||||
zLine[n] = 0;
|
||||
eol = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
zLine = realloc( zLine, n+1 );
|
||||
@ -1030,6 +1048,27 @@ static int DbTransPostCmd(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Unless SQLITE_TEST is defined, this function is a simple wrapper around
|
||||
** sqlite3_prepare_v2(). If SQLITE_TEST is defined, then it uses either
|
||||
** sqlite3_prepare_v2() or legacy interface sqlite3_prepare(), depending
|
||||
** on whether or not the [db_use_legacy_prepare] command has been used to
|
||||
** configure the connection.
|
||||
*/
|
||||
static int dbPrepare(
|
||||
SqliteDb *pDb, /* Database object */
|
||||
const char *zSql, /* SQL to compile */
|
||||
sqlite3_stmt **ppStmt, /* OUT: Prepared statement */
|
||||
const char **pzOut /* OUT: Pointer to next SQL statement */
|
||||
){
|
||||
#ifdef SQLITE_TEST
|
||||
if( pDb->bLegacyPrepare ){
|
||||
return sqlite3_prepare(pDb->db, zSql, -1, ppStmt, pzOut);
|
||||
}
|
||||
#endif
|
||||
return sqlite3_prepare_v2(pDb->db, zSql, -1, ppStmt, pzOut);
|
||||
}
|
||||
|
||||
/*
|
||||
** Search the cache for a prepared-statement object that implements the
|
||||
** first SQL statement in the buffer pointed to by parameter zIn. If
|
||||
@ -1100,7 +1139,7 @@ static int dbPrepareAndBind(
|
||||
if( pPreStmt==0 ){
|
||||
int nByte;
|
||||
|
||||
if( SQLITE_OK!=sqlite3_prepare_v2(pDb->db, zSql, -1, &pStmt, pzOut) ){
|
||||
if( SQLITE_OK!=dbPrepare(pDb, zSql, &pStmt, pzOut) ){
|
||||
Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db)));
|
||||
return TCL_ERROR;
|
||||
}
|
||||
@ -1127,6 +1166,14 @@ static int dbPrepareAndBind(
|
||||
pPreStmt->nSql = (*pzOut - zSql);
|
||||
pPreStmt->zSql = sqlite3_sql(pStmt);
|
||||
pPreStmt->apParm = (Tcl_Obj **)&pPreStmt[1];
|
||||
#ifdef SQLITE_TEST
|
||||
if( pPreStmt->zSql==0 ){
|
||||
char *zCopy = Tcl_Alloc(pPreStmt->nSql + 1);
|
||||
memcpy(zCopy, zSql, pPreStmt->nSql);
|
||||
zCopy[pPreStmt->nSql] = '\0';
|
||||
pPreStmt->zSql = zCopy;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
assert( pPreStmt );
|
||||
assert( strlen30(pPreStmt->zSql)==pPreStmt->nSql );
|
||||
@ -1180,7 +1227,6 @@ static int dbPrepareAndBind(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Release a statement reference obtained by calling dbPrepareAndBind().
|
||||
** There should be exactly one call to this function for each call to
|
||||
@ -1205,8 +1251,7 @@ static void dbReleaseStmt(
|
||||
|
||||
if( pDb->maxStmt<=0 || discard ){
|
||||
/* If the cache is turned off, deallocated the statement */
|
||||
sqlite3_finalize(pPreStmt->pStmt);
|
||||
Tcl_Free((char *)pPreStmt);
|
||||
dbFreeStmt(pPreStmt);
|
||||
}else{
|
||||
/* Add the prepared statement to the beginning of the cache list. */
|
||||
pPreStmt->pNext = pDb->stmtList;
|
||||
@ -1226,11 +1271,11 @@ static void dbReleaseStmt(
|
||||
/* If we have too many statement in cache, remove the surplus from
|
||||
** the end of the cache list. */
|
||||
while( pDb->nStmt>pDb->maxStmt ){
|
||||
sqlite3_finalize(pDb->stmtLast->pStmt);
|
||||
pDb->stmtLast = pDb->stmtLast->pPrev;
|
||||
Tcl_Free((char*)pDb->stmtLast->pNext);
|
||||
SqlPreparedStmt *pLast = pDb->stmtLast;
|
||||
pDb->stmtLast = pLast->pPrev;
|
||||
pDb->stmtLast->pNext = 0;
|
||||
pDb->nStmt--;
|
||||
dbFreeStmt(pLast);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1363,9 +1408,12 @@ static void dbEvalRowInfo(
|
||||
** no further rows available. This is similar to SQLITE_DONE.
|
||||
*/
|
||||
static int dbEvalStep(DbEvalContext *p){
|
||||
const char *zPrevSql = 0; /* Previous value of p->zSql */
|
||||
|
||||
while( p->zSql[0] || p->pPreStmt ){
|
||||
int rc;
|
||||
if( p->pPreStmt==0 ){
|
||||
zPrevSql = (p->zSql==zPrevSql ? 0 : p->zSql);
|
||||
rc = dbPrepareAndBind(p->pDb, p->zSql, &p->zSql, &p->pPreStmt);
|
||||
if( rc!=TCL_OK ) return rc;
|
||||
}else{
|
||||
@ -1392,8 +1440,19 @@ static int dbEvalStep(DbEvalContext *p){
|
||||
if( rcs!=SQLITE_OK ){
|
||||
/* If a run-time error occurs, report the error and stop reading
|
||||
** the SQL. */
|
||||
Tcl_SetObjResult(pDb->interp, dbTextToObj(sqlite3_errmsg(pDb->db)));
|
||||
dbReleaseStmt(pDb, pPreStmt, 1);
|
||||
#if SQLITE_TEST
|
||||
if( p->pDb->bLegacyPrepare && rcs==SQLITE_SCHEMA && zPrevSql ){
|
||||
/* If the runtime error was an SQLITE_SCHEMA, and the database
|
||||
** handle is configured to use the legacy sqlite3_prepare()
|
||||
** interface, retry prepare()/step() on the same SQL statement.
|
||||
** This only happens once. If there is a second SQLITE_SCHEMA
|
||||
** error, the error will be returned to the caller. */
|
||||
p->zSql = zPrevSql;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
Tcl_SetObjResult(pDb->interp, dbTextToObj(sqlite3_errmsg(pDb->db)));
|
||||
return TCL_ERROR;
|
||||
}else{
|
||||
dbReleaseStmt(pDb, pPreStmt, 0);
|
||||
@ -2059,7 +2118,6 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
zCommit = "COMMIT";
|
||||
while( (zLine = local_getline(0, in))!=0 ){
|
||||
char *z;
|
||||
i = 0;
|
||||
lineno++;
|
||||
azCol[0] = zLine;
|
||||
for(i=0, z=zLine; *z; z++){
|
||||
@ -2180,6 +2238,8 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
if( choice==DB_ONECOLUMN ){
|
||||
if( rc==TCL_OK ){
|
||||
Tcl_SetObjResult(interp, dbEvalColumnValue(&sEval, 0));
|
||||
}else if( rc==TCL_BREAK ){
|
||||
Tcl_ResetResult(interp);
|
||||
}
|
||||
}else if( rc==TCL_BREAK || rc==TCL_OK ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(rc==TCL_OK));
|
||||
@ -2486,14 +2546,16 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
** Change the encryption key on the currently open database.
|
||||
*/
|
||||
case DB_REKEY: {
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
int nKey;
|
||||
void *pKey;
|
||||
#endif
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 2, objv, "KEY");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pKey = Tcl_GetByteArrayFromObj(objv[2], &nKey);
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
pKey = Tcl_GetByteArrayFromObj(objv[2], &nKey);
|
||||
rc = sqlite3_rekey(pDb->db, pKey, nKey);
|
||||
if( rc ){
|
||||
Tcl_AppendResult(interp, sqlite3ErrStr(rc), 0);
|
||||
@ -2856,8 +2918,6 @@ static int DbObjCmdAdaptor(
|
||||
*/
|
||||
static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
SqliteDb *p;
|
||||
void *pKey = 0;
|
||||
int nKey = 0;
|
||||
const char *zArg;
|
||||
char *zErrMsg;
|
||||
int i;
|
||||
@ -2865,6 +2925,10 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
const char *zVfs = 0;
|
||||
int flags;
|
||||
Tcl_DString translatedFilename;
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
void *pKey = 0;
|
||||
int nKey = 0;
|
||||
#endif
|
||||
|
||||
/* In normal use, each TCL interpreter runs in a single thread. So
|
||||
** by default, we can turn of mutexing on SQLite database connections.
|
||||
@ -2896,7 +2960,9 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
for(i=3; i+1<objc; i+=2){
|
||||
zArg = Tcl_GetString(objv[i]);
|
||||
if( strcmp(zArg,"-key")==0 ){
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
pKey = Tcl_GetByteArrayFromObj(objv[i+1], &nKey);
|
||||
#endif
|
||||
}else if( strcmp(zArg, "-vfs")==0 ){
|
||||
zVfs = Tcl_GetString(objv[i+1]);
|
||||
}else if( strcmp(zArg, "-readonly")==0 ){
|
||||
@ -2926,7 +2992,7 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
}else{
|
||||
flags &= ~SQLITE_OPEN_NOMUTEX;
|
||||
}
|
||||
}else if( strcmp(zArg, "-fullmutex")==0 ){
|
||||
}else if( strcmp(zArg, "-fullmutex")==0 ){
|
||||
int b;
|
||||
if( Tcl_GetBooleanFromObj(interp, objv[i+1], &b) ) return TCL_ERROR;
|
||||
if( b ){
|
||||
@ -3475,33 +3541,34 @@ int Md5_Register(sqlite3 *db){
|
||||
** the TCL interpreter reads and evaluates that file.
|
||||
*/
|
||||
#if TCLSH==1
|
||||
static char zMainloop[] =
|
||||
"set line {}\n"
|
||||
"while {![eof stdin]} {\n"
|
||||
"if {$line!=\"\"} {\n"
|
||||
"puts -nonewline \"> \"\n"
|
||||
"} else {\n"
|
||||
"puts -nonewline \"% \"\n"
|
||||
"}\n"
|
||||
"flush stdout\n"
|
||||
"append line [gets stdin]\n"
|
||||
"if {[info complete $line]} {\n"
|
||||
"if {[catch {uplevel #0 $line} result]} {\n"
|
||||
"puts stderr \"Error: $result\"\n"
|
||||
"} elseif {$result!=\"\"} {\n"
|
||||
"puts $result\n"
|
||||
static const char *tclsh_main_loop(void){
|
||||
static const char zMainloop[] =
|
||||
"set line {}\n"
|
||||
"while {![eof stdin]} {\n"
|
||||
"if {$line!=\"\"} {\n"
|
||||
"puts -nonewline \"> \"\n"
|
||||
"} else {\n"
|
||||
"puts -nonewline \"% \"\n"
|
||||
"}\n"
|
||||
"flush stdout\n"
|
||||
"append line [gets stdin]\n"
|
||||
"if {[info complete $line]} {\n"
|
||||
"if {[catch {uplevel #0 $line} result]} {\n"
|
||||
"puts stderr \"Error: $result\"\n"
|
||||
"} elseif {$result!=\"\"} {\n"
|
||||
"puts $result\n"
|
||||
"}\n"
|
||||
"set line {}\n"
|
||||
"} else {\n"
|
||||
"append line \\n\n"
|
||||
"}\n"
|
||||
"set line {}\n"
|
||||
"} else {\n"
|
||||
"append line \\n\n"
|
||||
"}\n"
|
||||
"}\n"
|
||||
;
|
||||
;
|
||||
return zMainloop;
|
||||
}
|
||||
#endif
|
||||
#if TCLSH==2
|
||||
static char zMainloop[] =
|
||||
#include "spaceanal_tcl.h"
|
||||
;
|
||||
static const char *tclsh_main_loop(void);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
@ -3527,6 +3594,44 @@ static int init_all_cmd(
|
||||
init_all(slave);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Tclcmd: db_use_legacy_prepare DB BOOLEAN
|
||||
**
|
||||
** The first argument to this command must be a database command created by
|
||||
** [sqlite3]. If the second argument is true, then the handle is configured
|
||||
** to use the sqlite3_prepare_v2() function to prepare statements. If it
|
||||
** is false, sqlite3_prepare().
|
||||
*/
|
||||
static int db_use_legacy_prepare_cmd(
|
||||
ClientData cd,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
Tcl_CmdInfo cmdInfo;
|
||||
SqliteDb *pDb;
|
||||
int bPrepare;
|
||||
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB BOOLEAN");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( !Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &cmdInfo) ){
|
||||
Tcl_AppendResult(interp, "no such db: ", Tcl_GetString(objv[1]), (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pDb = (SqliteDb*)cmdInfo.objClientData;
|
||||
if( Tcl_GetBooleanFromObj(interp, objv[2], &bPrepare) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
pDb->bLegacyPrepare = bPrepare;
|
||||
|
||||
Tcl_ResetResult(interp);
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -3547,6 +3652,17 @@ static void init_all(Tcl_Interp *interp){
|
||||
Md5_Init(interp);
|
||||
#endif
|
||||
|
||||
/* Install the [register_dbstat_vtab] command to access the implementation
|
||||
** of virtual table dbstat (source file test_stat.c). This command is
|
||||
** required for testfixture and sqlite3_analyzer, but not by the production
|
||||
** Tcl extension. */
|
||||
#if defined(SQLITE_TEST) || TCLSH==2
|
||||
{
|
||||
extern int SqlitetestStat_Init(Tcl_Interp*);
|
||||
SqlitetestStat_Init(interp);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
{
|
||||
extern int Sqliteconfig_Init(Tcl_Interp*);
|
||||
@ -3576,7 +3692,6 @@ static void init_all(Tcl_Interp *interp){
|
||||
extern int Sqlitetestbackup_Init(Tcl_Interp*);
|
||||
extern int Sqlitetestintarray_Init(Tcl_Interp*);
|
||||
extern int Sqlitetestvfs_Init(Tcl_Interp *);
|
||||
extern int SqlitetestStat_Init(Tcl_Interp*);
|
||||
extern int Sqlitetestrtree_Init(Tcl_Interp*);
|
||||
extern int Sqlitequota_Init(Tcl_Interp*);
|
||||
extern int Sqlitemultiplex_Init(Tcl_Interp*);
|
||||
@ -3620,7 +3735,6 @@ static void init_all(Tcl_Interp *interp){
|
||||
Sqlitetestbackup_Init(interp);
|
||||
Sqlitetestintarray_Init(interp);
|
||||
Sqlitetestvfs_Init(interp);
|
||||
SqlitetestStat_Init(interp);
|
||||
Sqlitetestrtree_Init(interp);
|
||||
Sqlitequota_Init(interp);
|
||||
Sqlitemultiplex_Init(interp);
|
||||
@ -3633,7 +3747,12 @@ static void init_all(Tcl_Interp *interp){
|
||||
Sqlitetestfts3_Init(interp);
|
||||
#endif
|
||||
|
||||
Tcl_CreateObjCommand(interp,"load_testfixture_extensions",init_all_cmd,0,0);
|
||||
Tcl_CreateObjCommand(
|
||||
interp, "load_testfixture_extensions", init_all_cmd, 0, 0
|
||||
);
|
||||
Tcl_CreateObjCommand(
|
||||
interp, "db_use_legacy_prepare", db_use_legacy_prepare_cmd, 0, 0
|
||||
);
|
||||
|
||||
#ifdef SQLITE_SSE
|
||||
Sqlitetestsse_Init(interp);
|
||||
@ -3651,12 +3770,13 @@ int TCLSH_MAIN(int argc, char **argv){
|
||||
** sqlite3_initialize() is. */
|
||||
sqlite3_shutdown();
|
||||
|
||||
Tcl_FindExecutable(argv[0]);
|
||||
interp = Tcl_CreateInterp();
|
||||
|
||||
#if TCLSH==2
|
||||
sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
|
||||
#endif
|
||||
Tcl_FindExecutable(argv[0]);
|
||||
|
||||
interp = Tcl_CreateInterp();
|
||||
init_all(interp);
|
||||
if( argc>=2 ){
|
||||
int i;
|
||||
@ -3677,7 +3797,7 @@ int TCLSH_MAIN(int argc, char **argv){
|
||||
}
|
||||
}
|
||||
if( TCLSH==2 || argc<=1 ){
|
||||
Tcl_GlobalEval(interp, zMainloop);
|
||||
Tcl_GlobalEval(interp, tclsh_main_loop());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
189
src/test1.c
189
src/test1.c
@ -4395,7 +4395,7 @@ static u8 *sqlite3_stack_baseline = 0;
|
||||
static void prepStack(void){
|
||||
int i;
|
||||
u32 bigBuf[65536];
|
||||
for(i=0; i<sizeof(bigBuf); i++) bigBuf[i] = 0xdeadbeef;
|
||||
for(i=0; i<sizeof(bigBuf)/sizeof(bigBuf[0]); i++) bigBuf[i] = 0xdeadbeef;
|
||||
sqlite3_stack_baseline = (u8*)&bigBuf[65536];
|
||||
}
|
||||
|
||||
@ -4991,9 +4991,8 @@ static int file_control_chunksize_test(
|
||||
/*
|
||||
** tclcmd: file_control_sizehint_test DB DBNAME SIZE
|
||||
**
|
||||
** This TCL command runs the sqlite3_file_control interface and
|
||||
** verifies correct operation of the SQLITE_GET_LOCKPROXYFILE and
|
||||
** SQLITE_SET_LOCKPROXYFILE verbs.
|
||||
** This TCL command runs the sqlite3_file_control interface
|
||||
** with SQLITE_FCNTL_SIZE_HINT
|
||||
*/
|
||||
static int file_control_sizehint_test(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
@ -5096,6 +5095,71 @@ static int file_control_lockproxy_test(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: file_control_win32_av_retry DB NRETRY DELAY
|
||||
**
|
||||
** This TCL command runs the sqlite3_file_control interface with
|
||||
** the SQLITE_FCNTL_WIN32_AV_RETRY opcode.
|
||||
*/
|
||||
static int file_control_win32_av_retry(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
int a[2];
|
||||
char z[100];
|
||||
|
||||
if( objc!=4 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
Tcl_GetStringFromObj(objv[0], 0), " DB NRETRY DELAY", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &a[0]) ) return TCL_ERROR;
|
||||
if( Tcl_GetIntFromObj(interp, objv[3], &a[1]) ) return TCL_ERROR;
|
||||
rc = sqlite3_file_control(db, NULL, SQLITE_FCNTL_WIN32_AV_RETRY, (void*)a);
|
||||
sqlite3_snprintf(sizeof(z), z, "%d %d %d", rc, a[0], a[1]);
|
||||
Tcl_AppendResult(interp, z, (char*)0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: file_control_persist_wal DB PERSIST-FLAG
|
||||
**
|
||||
** This TCL command runs the sqlite3_file_control interface with
|
||||
** the SQLITE_FCNTL_PERSIST_WAL opcode.
|
||||
*/
|
||||
static int file_control_persist_wal(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
int bPersist;
|
||||
char z[100];
|
||||
|
||||
if( objc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
Tcl_GetStringFromObj(objv[0], 0), " DB FLAG", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &bPersist) ) return TCL_ERROR;
|
||||
rc = sqlite3_file_control(db, NULL, SQLITE_FCNTL_PERSIST_WAL, (void*)&bPersist);
|
||||
sqlite3_snprintf(sizeof(z), z, "%d %d", rc, bPersist);
|
||||
Tcl_AppendResult(interp, z, (char*)0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_vfs_list
|
||||
@ -5574,6 +5638,116 @@ static int test_test_control(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
#if SQLITE_OS_WIN
|
||||
/*
|
||||
** Information passed from the main thread into the windows file locker
|
||||
** background thread.
|
||||
*/
|
||||
struct win32FileLocker {
|
||||
char *evName; /* Name of event to signal thread startup */
|
||||
HANDLE h; /* Handle of the file to be locked */
|
||||
int delay1; /* Delay before locking */
|
||||
int delay2; /* Delay before unlocking */
|
||||
int ok; /* Finished ok */
|
||||
int err; /* True if an error occurs */
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
#if SQLITE_OS_WIN
|
||||
/*
|
||||
** The background thread that does file locking.
|
||||
*/
|
||||
static void win32_file_locker(void *pAppData){
|
||||
struct win32FileLocker *p = (struct win32FileLocker*)pAppData;
|
||||
if( p->evName ){
|
||||
HANDLE ev = OpenEvent(EVENT_MODIFY_STATE, FALSE, p->evName);
|
||||
if ( ev ){
|
||||
SetEvent(ev);
|
||||
CloseHandle(ev);
|
||||
}
|
||||
}
|
||||
if( p->delay1 ) Sleep(p->delay1);
|
||||
if( LockFile(p->h, 0, 0, 100000000, 0) ){
|
||||
Sleep(p->delay2);
|
||||
UnlockFile(p->h, 0, 0, 100000000, 0);
|
||||
p->ok = 1;
|
||||
}else{
|
||||
p->err = 1;
|
||||
}
|
||||
CloseHandle(p->h);
|
||||
p->h = 0;
|
||||
p->delay1 = 0;
|
||||
p->delay2 = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SQLITE_OS_WIN
|
||||
/*
|
||||
** lock_win32_file FILENAME DELAY1 DELAY2
|
||||
**
|
||||
** Get an exclusive manditory lock on file for DELAY2 milliseconds.
|
||||
** Wait DELAY1 milliseconds before acquiring the lock.
|
||||
*/
|
||||
static int win32_file_lock(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
static struct win32FileLocker x = { "win32_file_lock", 0, 0, 0, 0, 0 };
|
||||
const char *zFilename;
|
||||
char zBuf[200];
|
||||
int retry = 0;
|
||||
HANDLE ev;
|
||||
DWORD wResult;
|
||||
|
||||
if( objc!=4 && objc!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "FILENAME DELAY1 DELAY2");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( objc==1 ){
|
||||
sqlite3_snprintf(sizeof(zBuf), zBuf, "%d %d %d %d %d",
|
||||
x.ok, x.err, x.delay1, x.delay2, x.h);
|
||||
Tcl_AppendResult(interp, zBuf, (char*)0);
|
||||
return TCL_OK;
|
||||
}
|
||||
while( x.h && retry<30 ){
|
||||
retry++;
|
||||
Sleep(100);
|
||||
}
|
||||
if( x.h ){
|
||||
Tcl_AppendResult(interp, "busy", (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &x.delay1) ) return TCL_ERROR;
|
||||
if( Tcl_GetIntFromObj(interp, objv[3], &x.delay2) ) return TCL_ERROR;
|
||||
zFilename = Tcl_GetString(objv[1]);
|
||||
x.h = CreateFile(zFilename, GENERIC_READ|GENERIC_WRITE,
|
||||
FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL, 0);
|
||||
if( !x.h ){
|
||||
Tcl_AppendResult(interp, "cannot open file: ", zFilename, (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
ev = CreateEvent(NULL, TRUE, FALSE, x.evName);
|
||||
if ( !ev ){
|
||||
Tcl_AppendResult(interp, "cannot create event: ", x.evName, (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
_beginthread(win32_file_locker, 0, (void*)&x);
|
||||
Sleep(0);
|
||||
if ( (wResult = WaitForSingleObject(ev, 10000))!=WAIT_OBJECT_0 ){
|
||||
sqlite3_snprintf(sizeof(zBuf), zBuf, "0x%x", wResult);
|
||||
Tcl_AppendResult(interp, "wait failed: ", zBuf, (char*)0);
|
||||
CloseHandle(ev);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
CloseHandle(ev);
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** optimization_control DB OPT BOOLEAN
|
||||
@ -5754,6 +5928,9 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "restore_prng_state", restore_prng_state, 0 },
|
||||
{ "reset_prng_state", reset_prng_state, 0 },
|
||||
{ "optimization_control", optimization_control,0},
|
||||
#if SQLITE_OS_WIN
|
||||
{ "lock_win32_file", win32_file_lock, 0 },
|
||||
#endif
|
||||
{ "tcl_objproc", runAsObjProc, 0 },
|
||||
|
||||
/* sqlite3_column_*() API */
|
||||
@ -5802,7 +5979,9 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "file_control_lasterrno_test", file_control_lasterrno_test, 0 },
|
||||
{ "file_control_lockproxy_test", file_control_lockproxy_test, 0 },
|
||||
{ "file_control_chunksize_test", file_control_chunksize_test, 0 },
|
||||
{ "file_control_sizehint_test", file_control_sizehint_test, 0 },
|
||||
{ "file_control_sizehint_test", file_control_sizehint_test, 0 },
|
||||
{ "file_control_win32_av_retry", file_control_win32_av_retry, 0 },
|
||||
{ "file_control_persist_wal", file_control_persist_wal, 0 },
|
||||
{ "sqlite3_vfs_list", vfs_list, 0 },
|
||||
{ "sqlite3_create_function_v2", test_create_function_v2, 0 },
|
||||
|
||||
|
||||
10
src/test6.c
10
src/test6.c
@ -505,6 +505,16 @@ static int cfCheckReservedLock(sqlite3_file *pFile, int *pResOut){
|
||||
return sqlite3OsCheckReservedLock(((CrashFile *)pFile)->pRealFile, pResOut);
|
||||
}
|
||||
static int cfFileControl(sqlite3_file *pFile, int op, void *pArg){
|
||||
if( op==SQLITE_FCNTL_SIZE_HINT ){
|
||||
CrashFile *pCrash = (CrashFile *)pFile;
|
||||
i64 nByte = *(i64 *)pArg;
|
||||
if( nByte>pCrash->iSize ){
|
||||
if( SQLITE_OK==writeListAppend(pFile, nByte, 0, 0) ){
|
||||
pCrash->iSize = nByte;
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
return sqlite3OsFileControl(((CrashFile *)pFile)->pRealFile, op, pArg);
|
||||
}
|
||||
|
||||
|
||||
52
src/test8.c
52
src/test8.c
@ -1232,12 +1232,50 @@ static int echoRename(sqlite3_vtab *vtab, const char *zNewName){
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int echoSavepoint(sqlite3_vtab *pVTab, int iSavepoint){
|
||||
assert( pVTab );
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int echoRelease(sqlite3_vtab *pVTab, int iSavepoint){
|
||||
assert( pVTab );
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int echoRollbackTo(sqlite3_vtab *pVTab, int iSavepoint){
|
||||
assert( pVTab );
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** A virtual table module that merely "echos" the contents of another
|
||||
** table (like an SQL VIEW).
|
||||
*/
|
||||
static sqlite3_module echoModule = {
|
||||
0, /* iVersion */
|
||||
1, /* iVersion */
|
||||
echoCreate,
|
||||
echoConnect,
|
||||
echoBestIndex,
|
||||
echoDisconnect,
|
||||
echoDestroy,
|
||||
echoOpen, /* xOpen - open a cursor */
|
||||
echoClose, /* xClose - close a cursor */
|
||||
echoFilter, /* xFilter - configure scan constraints */
|
||||
echoNext, /* xNext - advance a cursor */
|
||||
echoEof, /* xEof */
|
||||
echoColumn, /* xColumn - read data */
|
||||
echoRowid, /* xRowid - read data */
|
||||
echoUpdate, /* xUpdate - write data */
|
||||
echoBegin, /* xBegin - begin transaction */
|
||||
echoSync, /* xSync - sync transaction */
|
||||
echoCommit, /* xCommit - commit transaction */
|
||||
echoRollback, /* xRollback - rollback transaction */
|
||||
echoFindFunction, /* xFindFunction - function overloading */
|
||||
echoRename /* xRename - rename the table */
|
||||
};
|
||||
|
||||
static sqlite3_module echoModuleV2 = {
|
||||
2, /* iVersion */
|
||||
echoCreate,
|
||||
echoConnect,
|
||||
echoBestIndex,
|
||||
@ -1257,6 +1295,9 @@ static sqlite3_module echoModule = {
|
||||
echoRollback, /* xRollback - rollback transaction */
|
||||
echoFindFunction, /* xFindFunction - function overloading */
|
||||
echoRename, /* xRename - rename the table */
|
||||
echoSavepoint,
|
||||
echoRelease,
|
||||
echoRollbackTo
|
||||
};
|
||||
|
||||
/*
|
||||
@ -1284,9 +1325,18 @@ static int register_echo_module(
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
|
||||
/* Virtual table module "echo" */
|
||||
pMod = sqlite3_malloc(sizeof(EchoModule));
|
||||
pMod->interp = interp;
|
||||
sqlite3_create_module_v2(db, "echo", &echoModule, (void*)pMod, moduleDestroy);
|
||||
|
||||
/* Virtual table module "echo_v2" */
|
||||
pMod = sqlite3_malloc(sizeof(EchoModule));
|
||||
pMod->interp = interp;
|
||||
sqlite3_create_module_v2(db, "echo_v2",
|
||||
&echoModuleV2, (void*)pMod, moduleDestroy
|
||||
);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
@ -55,6 +55,12 @@ static void set_options(Tcl_Interp *interp){
|
||||
Tcl_SetVar2(interp, "sqlite_options", "debug", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_DIRECT_OVERFLOW_READ
|
||||
Tcl_SetVar2(interp, "sqlite_options", "direct_read", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "direct_read", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_DISABLE_DIRSYNC
|
||||
Tcl_SetVar2(interp, "sqlite_options", "dirsync", "0", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
@ -363,6 +369,12 @@ Tcl_SetVar2(interp, "sqlite_options", "long_double",
|
||||
Tcl_SetVar2(interp, "sqlite_options", "memorymanage", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_MERGE_SORT
|
||||
Tcl_SetVar2(interp, "sqlite_options", "mergesort", "0", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "mergesort", "1", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_OR_OPTIMIZATION
|
||||
Tcl_SetVar2(interp, "sqlite_options", "or_opt", "0", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
@ -412,10 +424,10 @@ Tcl_SetVar2(interp, "sqlite_options", "long_double",
|
||||
Tcl_SetVar2(interp, "sqlite_options", "schema_version", "1", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
Tcl_SetVar2(interp, "sqlite_options", "stat2", "1", TCL_GLOBAL_ONLY);
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
Tcl_SetVar2(interp, "sqlite_options", "stat3", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "stat2", "0", TCL_GLOBAL_ONLY);
|
||||
Tcl_SetVar2(interp, "sqlite_options", "stat3", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#if !defined(SQLITE_ENABLE_LOCKING_STYLE)
|
||||
@ -571,6 +583,7 @@ Tcl_SetVar2(interp, "sqlite_options", "long_double",
|
||||
LINKVAR( DEFAULT_PAGE_SIZE );
|
||||
LINKVAR( DEFAULT_FILE_FORMAT );
|
||||
LINKVAR( MAX_ATTACHED );
|
||||
LINKVAR( MAX_DEFAULT_PAGE_SIZE );
|
||||
|
||||
{
|
||||
static const int cv_TEMP_STORE = SQLITE_TEMP_STORE;
|
||||
|
||||
@ -1222,7 +1222,7 @@ static int test_dump_memsys3(
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
switch( (int)clientData ){
|
||||
switch( SQLITE_PTR_TO_INT(clientData) ){
|
||||
case 3: {
|
||||
#ifdef SQLITE_ENABLE_MEMSYS3
|
||||
extern void sqlite3Memsys3Dump(const char*);
|
||||
@ -1325,11 +1325,13 @@ static int test_db_status(
|
||||
{ "STMT_USED", SQLITE_DBSTATUS_STMT_USED },
|
||||
{ "LOOKASIDE_HIT", SQLITE_DBSTATUS_LOOKASIDE_HIT },
|
||||
{ "LOOKASIDE_MISS_SIZE", SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE },
|
||||
{ "LOOKASIDE_MISS_FULL", SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL }
|
||||
{ "LOOKASIDE_MISS_FULL", SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL },
|
||||
{ "CACHE_HIT", SQLITE_DBSTATUS_CACHE_HIT },
|
||||
{ "CACHE_MISS", SQLITE_DBSTATUS_CACHE_MISS }
|
||||
};
|
||||
Tcl_Obj *pResult;
|
||||
if( objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "PARAMETER RESETFLAG");
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB PARAMETER RESETFLAG");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
@ -1460,7 +1462,7 @@ int Sqlitetest_malloc_Init(Tcl_Interp *interp){
|
||||
};
|
||||
int i;
|
||||
for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
|
||||
ClientData c = (ClientData)aObjCmd[i].clientData;
|
||||
ClientData c = (ClientData)SQLITE_INT_TO_PTR(aObjCmd[i].clientData);
|
||||
Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, c, 0);
|
||||
}
|
||||
return TCL_OK;
|
||||
|
||||
@ -39,12 +39,18 @@
|
||||
** URI.
|
||||
**
|
||||
** The multiplex VFS allows databases up to 32 GiB in size. But it splits
|
||||
** the files up into 1 GiB pieces, so that they will work even on filesystems
|
||||
** that do not support large files.
|
||||
** the files up into smaller pieces, so that they will work even on
|
||||
** filesystems that do not support large files. The default chunk size
|
||||
** is 2147418112 bytes (which is 64KiB less than 2GiB) but this can be
|
||||
** changed at compile-time by defining the SQLITE_MULTIPLEX_CHUNK_SIZE
|
||||
** macro. Use the "chunksize=NNNN" query parameter with a URI filename
|
||||
** in order to select an alternative chunk size for individual connections
|
||||
** at run-time.
|
||||
*/
|
||||
#include "sqlite3.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include "test_multiplex.h"
|
||||
|
||||
#ifndef SQLITE_CORE
|
||||
@ -78,20 +84,26 @@
|
||||
|
||||
/************************ Shim Definitions ******************************/
|
||||
|
||||
#define SQLITE_MULTIPLEX_VFS_NAME "multiplex"
|
||||
#ifndef SQLITE_MULTIPLEX_VFS_NAME
|
||||
# define SQLITE_MULTIPLEX_VFS_NAME "multiplex"
|
||||
#endif
|
||||
|
||||
/* This is the limit on the chunk size. It may be changed by calling
|
||||
** the xFileControl() interface. It will be rounded up to a
|
||||
** multiple of MAX_PAGE_SIZE. We default it here to 1GB.
|
||||
** multiple of MAX_PAGE_SIZE. We default it here to 2GiB less 64KiB.
|
||||
*/
|
||||
#define SQLITE_MULTIPLEX_CHUNK_SIZE (MAX_PAGE_SIZE*16384)
|
||||
#ifndef SQLITE_MULTIPLEX_CHUNK_SIZE
|
||||
# define SQLITE_MULTIPLEX_CHUNK_SIZE 2147418112
|
||||
#endif
|
||||
|
||||
/* Default limit on number of chunks. Care should be taken
|
||||
** so that values for chunks numbers fit in the SQLITE_MULTIPLEX_EXT_FMT
|
||||
** format specifier. It may be changed by calling
|
||||
** the xFileControl() interface.
|
||||
*/
|
||||
#define SQLITE_MULTIPLEX_MAX_CHUNKS 32
|
||||
#ifndef SQLITE_MULTIPLEX_MAX_CHUNKS
|
||||
# define SQLITE_MULTIPLEX_MAX_CHUNKS 32
|
||||
#endif
|
||||
|
||||
/* If SQLITE_MULTIPLEX_EXT_OVWR is defined, the
|
||||
** last SQLITE_MULTIPLEX_EXT_SZ characters of the
|
||||
@ -119,13 +131,15 @@ typedef struct multiplexConn multiplexConn;
|
||||
** group.
|
||||
*/
|
||||
struct multiplexGroup {
|
||||
sqlite3_file **pReal; /* Handles to each chunk */
|
||||
char *bOpen; /* array of bools - 0 if chunk not opened */
|
||||
struct multiplexReal { /* For each chunk */
|
||||
sqlite3_file *p; /* Handle for the chunk */
|
||||
char *z; /* Name of this chunk */
|
||||
} *aReal; /* list of all chunks */
|
||||
int nReal; /* Number of chunks */
|
||||
char *zName; /* Base filename of this group */
|
||||
int nName; /* Length of base filename */
|
||||
int flags; /* Flags used for original opening */
|
||||
int nChunkSize; /* Chunk size used for this group */
|
||||
int nMaxChunks; /* Max number of chunks for this group */
|
||||
unsigned int szChunk; /* Chunk size used for this group */
|
||||
int bEnabled; /* TRUE to use Multiplex VFS for this file */
|
||||
multiplexGroup *pNext, *pPrev; /* Doubly linked list of all group objects */
|
||||
};
|
||||
@ -184,12 +198,6 @@ static struct {
|
||||
/* List of multiplexGroup objects.
|
||||
*/
|
||||
multiplexGroup *pGroups;
|
||||
|
||||
/* Storage for temp file names. Allocated during
|
||||
** initialization to the max pathname of the underlying VFS.
|
||||
*/
|
||||
char *zName;
|
||||
|
||||
} gMultiplex;
|
||||
|
||||
/************************* Utility Routines *********************************/
|
||||
@ -265,7 +273,8 @@ static int multiplexGetTempname(sqlite3_vfs *pOrigVfs, int nBuf, char *zBuf){
|
||||
attempts++;
|
||||
sqlite3_randomness(8, &zBuf[j]);
|
||||
for(i=0; i<8; i++){
|
||||
zBuf[j+i] = (char)zChars[ ((unsigned char)zBuf[j+i])%(sizeof(zChars)-1) ];
|
||||
unsigned char uc = (unsigned char)zBuf[j+i];
|
||||
zBuf[j+i] = (char)zChars[uc%(sizeof(zChars)-1)];
|
||||
}
|
||||
memcpy(&zBuf[j+i], ".tmp", 5);
|
||||
rc = pOrigVfs->xAccess(pOrigVfs, zBuf, SQLITE_ACCESS_EXISTS, &exists);
|
||||
@ -279,35 +288,69 @@ static int multiplexGetTempname(sqlite3_vfs *pOrigVfs, int nBuf, char *zBuf){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Compute the filename for the iChunk-th chunk
|
||||
*/
|
||||
static int multiplexSubFilename(multiplexGroup *pGroup, int iChunk){
|
||||
if( iChunk>=pGroup->nReal ){
|
||||
struct multiplexReal *p;
|
||||
p = sqlite3_realloc(pGroup->aReal, (iChunk+1)*sizeof(*p));
|
||||
if( p==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
memset(&p[pGroup->nReal], 0, sizeof(p[0])*(iChunk+1-pGroup->nReal));
|
||||
pGroup->aReal = p;
|
||||
pGroup->nReal = iChunk+1;
|
||||
}
|
||||
if( pGroup->aReal[iChunk].z==0 ){
|
||||
char *z;
|
||||
int n = pGroup->nName;
|
||||
pGroup->aReal[iChunk].z = z = sqlite3_malloc( n+3 );
|
||||
if( z==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
memcpy(z, pGroup->zName, n+1);
|
||||
if( iChunk>0 ){
|
||||
#ifdef SQLITE_ENABLE_8_3_NAMES
|
||||
if( n>3 && z[n-3]=='.' ){
|
||||
n--;
|
||||
}else if( n>4 && z[n-4]=='.' ){
|
||||
n -= 2;
|
||||
}
|
||||
#endif
|
||||
sqlite3_snprintf(3,&z[n],"%02d",iChunk);
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Translate an sqlite3_file* that is really a multiplexGroup* into
|
||||
** the sqlite3_file* for the underlying original VFS.
|
||||
*/
|
||||
static sqlite3_file *multiplexSubOpen(multiplexConn *pConn, int iChunk, int *rc, int *pOutFlags){
|
||||
multiplexGroup *pGroup = pConn->pGroup;
|
||||
static sqlite3_file *multiplexSubOpen(
|
||||
multiplexGroup *pGroup,
|
||||
int iChunk,
|
||||
int *rc,
|
||||
int *pOutFlags
|
||||
){
|
||||
sqlite3_file *pSubOpen = 0;
|
||||
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
|
||||
if( iChunk<pGroup->nMaxChunks ){
|
||||
sqlite3_file *pSubOpen = pGroup->pReal[iChunk]; /* Real file descriptor */
|
||||
if( !pGroup->bOpen[iChunk] ){
|
||||
memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
|
||||
if( iChunk ){
|
||||
#ifdef SQLITE_MULTIPLEX_EXT_OVWR
|
||||
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, iChunk);
|
||||
#else
|
||||
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, iChunk);
|
||||
#endif
|
||||
}
|
||||
*rc = pOrigVfs->xOpen(pOrigVfs, gMultiplex.zName, pSubOpen, pGroup->flags, pOutFlags);
|
||||
if( *rc==SQLITE_OK ){
|
||||
pGroup->bOpen[iChunk] = -1;
|
||||
return pSubOpen;
|
||||
}
|
||||
return NULL;
|
||||
*rc = multiplexSubFilename(pGroup, iChunk);
|
||||
if( (*rc)==SQLITE_OK && (pSubOpen = pGroup->aReal[iChunk].p)==0 ){
|
||||
pSubOpen = sqlite3_malloc( pOrigVfs->szOsFile );
|
||||
if( pSubOpen==0 ){
|
||||
*rc = SQLITE_NOMEM;
|
||||
return 0;
|
||||
}
|
||||
pGroup->aReal[iChunk].p = pSubOpen;
|
||||
*rc = pOrigVfs->xOpen(pOrigVfs, pGroup->aReal[iChunk].z, pSubOpen,
|
||||
pGroup->flags, pOutFlags);
|
||||
if( *rc!=SQLITE_OK ){
|
||||
sqlite3_free(pSubOpen);
|
||||
pGroup->aReal[iChunk].p = 0;
|
||||
return 0;
|
||||
}
|
||||
*rc = SQLITE_OK;
|
||||
return pSubOpen;
|
||||
}
|
||||
*rc = SQLITE_FULL;
|
||||
return NULL;
|
||||
return pSubOpen;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -366,6 +409,36 @@ static int multiplexFuncInit(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a single sub-file in the connection group.
|
||||
*/
|
||||
static void multiplexSubClose(
|
||||
multiplexGroup *pGroup,
|
||||
int iChunk,
|
||||
sqlite3_vfs *pOrigVfs
|
||||
){
|
||||
sqlite3_file *pSubOpen = pGroup->aReal[iChunk].p;
|
||||
if( pSubOpen ){
|
||||
pSubOpen->pMethods->xClose(pSubOpen);
|
||||
if( pOrigVfs ) pOrigVfs->xDelete(pOrigVfs, pGroup->aReal[iChunk].z, 0);
|
||||
sqlite3_free(pGroup->aReal[iChunk].p);
|
||||
}
|
||||
sqlite3_free(pGroup->aReal[iChunk].z);
|
||||
memset(&pGroup->aReal[iChunk], 0, sizeof(pGroup->aReal[iChunk]));
|
||||
}
|
||||
|
||||
/*
|
||||
** Deallocate memory held by a multiplexGroup
|
||||
*/
|
||||
static void multiplexFreeComponents(multiplexGroup *pGroup){
|
||||
int i;
|
||||
for(i=0; i<pGroup->nReal; i++){ multiplexSubClose(pGroup, i, 0); }
|
||||
sqlite3_free(pGroup->aReal);
|
||||
pGroup->aReal = 0;
|
||||
pGroup->nReal = 0;
|
||||
}
|
||||
|
||||
|
||||
/************************* VFS Method Wrappers *****************************/
|
||||
|
||||
/*
|
||||
@ -382,16 +455,17 @@ static int multiplexOpen(
|
||||
int flags, /* Flags to control the opening */
|
||||
int *pOutFlags /* Flags showing results of opening */
|
||||
){
|
||||
int rc = SQLITE_OK; /* Result code */
|
||||
multiplexConn *pMultiplexOpen; /* The new multiplex file descriptor */
|
||||
multiplexGroup *pGroup; /* Corresponding multiplexGroup object */
|
||||
sqlite3_file *pSubOpen; /* Real file descriptor */
|
||||
int rc = SQLITE_OK; /* Result code */
|
||||
multiplexConn *pMultiplexOpen; /* The new multiplex file descriptor */
|
||||
multiplexGroup *pGroup; /* Corresponding multiplexGroup object */
|
||||
sqlite3_file *pSubOpen = 0; /* Real file descriptor */
|
||||
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
|
||||
int nName;
|
||||
int i;
|
||||
int sz;
|
||||
char *zToFree = 0;
|
||||
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
memset(pConn, 0, pVfs->szOsFile);
|
||||
|
||||
/* We need to create a group structure and manage
|
||||
** access to this group of files.
|
||||
@ -405,28 +479,22 @@ static int multiplexOpen(
|
||||
** it.
|
||||
*/
|
||||
if( !zName ){
|
||||
rc = multiplexGetTempname(pOrigVfs, pOrigVfs->mxPathname, gMultiplex.zName);
|
||||
zName = gMultiplex.zName;
|
||||
zName = zToFree = sqlite3_malloc( pOrigVfs->mxPathname + 10 );
|
||||
if( zName==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = multiplexGetTempname(pOrigVfs, pOrigVfs->mxPathname, zToFree);
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
/* allocate space for group */
|
||||
nName = multiplexStrlen30(zName);
|
||||
sz = sizeof(multiplexGroup) /* multiplexGroup */
|
||||
+ (sizeof(sqlite3_file *)*SQLITE_MULTIPLEX_MAX_CHUNKS) /* pReal[] */
|
||||
+ (pOrigVfs->szOsFile*SQLITE_MULTIPLEX_MAX_CHUNKS) /* *pReal */
|
||||
+ SQLITE_MULTIPLEX_MAX_CHUNKS /* bOpen[] */
|
||||
+ nName + 1; /* zName */
|
||||
#ifndef SQLITE_MULTIPLEX_EXT_OVWR
|
||||
sz += SQLITE_MULTIPLEX_EXT_SZ;
|
||||
assert(nName+SQLITE_MULTIPLEX_EXT_SZ < pOrigVfs->mxPathname);
|
||||
#else
|
||||
assert(nName >= SQLITE_MULTIPLEX_EXT_SZ);
|
||||
assert(nName < pOrigVfs->mxPathname);
|
||||
#endif
|
||||
sz = sizeof(multiplexGroup) /* multiplexGroup */
|
||||
+ nName + 1; /* zName */
|
||||
pGroup = sqlite3_malloc( sz );
|
||||
if( pGroup==0 ){
|
||||
rc=SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
@ -436,32 +504,58 @@ static int multiplexOpen(
|
||||
pMultiplexOpen->pGroup = pGroup;
|
||||
memset(pGroup, 0, sz);
|
||||
pGroup->bEnabled = -1;
|
||||
pGroup->nChunkSize = SQLITE_MULTIPLEX_CHUNK_SIZE;
|
||||
pGroup->nMaxChunks = SQLITE_MULTIPLEX_MAX_CHUNKS;
|
||||
pGroup->pReal = (sqlite3_file **)p;
|
||||
p += (sizeof(sqlite3_file *)*pGroup->nMaxChunks);
|
||||
for(i=0; i<pGroup->nMaxChunks; i++){
|
||||
pGroup->pReal[i] = (sqlite3_file *)p;
|
||||
p += pOrigVfs->szOsFile;
|
||||
pGroup->szChunk = SQLITE_MULTIPLEX_CHUNK_SIZE;
|
||||
if( flags & SQLITE_OPEN_URI ){
|
||||
const char *zChunkSize;
|
||||
zChunkSize = sqlite3_uri_parameter(zName, "chunksize");
|
||||
if( zChunkSize ){
|
||||
unsigned int n = 0;
|
||||
int i;
|
||||
for(i=0; zChunkSize[i]>='0' && zChunkSize[i]<='9'; i++){
|
||||
n = n*10 + zChunkSize[i] - '0';
|
||||
}
|
||||
if( n>0 ){
|
||||
pGroup->szChunk = (n+0xffff)&~0xffff;
|
||||
}else{
|
||||
/* A zero or negative chunksize disabled the multiplexor */
|
||||
pGroup->bEnabled = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* bOpen[] vals should all be zero from memset above */
|
||||
pGroup->bOpen = p;
|
||||
p += pGroup->nMaxChunks;
|
||||
pGroup->zName = p;
|
||||
/* save off base filename, name length, and original open flags */
|
||||
memcpy(pGroup->zName, zName, nName+1);
|
||||
pGroup->nName = nName;
|
||||
pGroup->flags = flags;
|
||||
pSubOpen = multiplexSubOpen(pMultiplexOpen, 0, &rc, pOutFlags);
|
||||
rc = multiplexSubFilename(pGroup, 1);
|
||||
if( rc==SQLITE_OK ){
|
||||
pSubOpen = multiplexSubOpen(pGroup, 0, &rc, pOutFlags);
|
||||
}
|
||||
if( pSubOpen ){
|
||||
/* if this file is already larger than chunk size, disable
|
||||
** the multiplex feature.
|
||||
*/
|
||||
int exists, rc2, rc3;
|
||||
sqlite3_int64 sz;
|
||||
int rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
|
||||
if( (rc2==SQLITE_OK) && (sz>pGroup->nChunkSize) ){
|
||||
pGroup->bEnabled = 0;
|
||||
|
||||
rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
|
||||
if( rc2==SQLITE_OK ){
|
||||
/* If the first overflow file exists and if the size of the main file
|
||||
** is different from the chunk size, that means the chunk size is set
|
||||
** set incorrectly. So fix it.
|
||||
**
|
||||
** Or, if the first overflow file does not exist and the main file is
|
||||
** larger than the chunk size, that means the chunk size is too small.
|
||||
** But we have no way of determining the intended chunk size, so
|
||||
** just disable the multiplexor all togethre.
|
||||
*/
|
||||
rc3 = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[1].z,
|
||||
SQLITE_ACCESS_EXISTS, &exists);
|
||||
if( rc3==SQLITE_OK && exists && sz==(sz&0xffff0000) && sz>0
|
||||
&& sz!=pGroup->szChunk ){
|
||||
pGroup->szChunk = sz;
|
||||
}else if( rc3==SQLITE_OK && !exists && sz>pGroup->szChunk ){
|
||||
pGroup->bEnabled = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if( pSubOpen->pMethods->iVersion==1 ){
|
||||
pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1;
|
||||
}else{
|
||||
@ -472,17 +566,18 @@ static int multiplexOpen(
|
||||
if( gMultiplex.pGroups ) gMultiplex.pGroups->pPrev = pGroup;
|
||||
gMultiplex.pGroups = pGroup;
|
||||
}else{
|
||||
multiplexFreeComponents(pGroup);
|
||||
sqlite3_free(pGroup);
|
||||
}
|
||||
}
|
||||
multiplexLeave();
|
||||
sqlite3_free(zToFree);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This is the xDelete method used for the "multiplex" VFS.
|
||||
** It attempts to delete the filename specified, as well
|
||||
** as additional files with the SQLITE_MULTIPLEX_EXT_FMT extension.
|
||||
** It attempts to delete the filename specified.
|
||||
*/
|
||||
static int multiplexDelete(
|
||||
sqlite3_vfs *pVfs, /* The multiplex VFS */
|
||||
@ -490,41 +585,7 @@ static int multiplexDelete(
|
||||
int syncDir
|
||||
){
|
||||
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
|
||||
int rc = SQLITE_OK;
|
||||
int nName = multiplexStrlen30(zName);
|
||||
int i;
|
||||
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
|
||||
multiplexEnter();
|
||||
memcpy(gMultiplex.zName, zName, nName+1);
|
||||
for(i=0; i<SQLITE_MULTIPLEX_MAX_CHUNKS; i++){
|
||||
int rc2;
|
||||
int exists = 0;
|
||||
if( i ){
|
||||
#ifdef SQLITE_MULTIPLEX_EXT_OVWR
|
||||
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1,
|
||||
gMultiplex.zName+nName-SQLITE_MULTIPLEX_EXT_SZ,
|
||||
SQLITE_MULTIPLEX_EXT_FMT, i);
|
||||
#else
|
||||
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1,
|
||||
gMultiplex.zName+nName,
|
||||
SQLITE_MULTIPLEX_EXT_FMT, i);
|
||||
#endif
|
||||
}
|
||||
rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName,
|
||||
SQLITE_ACCESS_EXISTS, &exists);
|
||||
if( rc2==SQLITE_OK && exists ){
|
||||
/* if it exists, delete it */
|
||||
rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, syncDir);
|
||||
if( rc2!=SQLITE_OK ) rc = rc2;
|
||||
}else{
|
||||
/* stop at first "gap" */
|
||||
break;
|
||||
}
|
||||
}
|
||||
multiplexLeave();
|
||||
return rc;
|
||||
return pOrigVfs->xDelete(pOrigVfs, zName, syncDir);
|
||||
}
|
||||
|
||||
static int multiplexAccess(sqlite3_vfs *a, const char *b, int c, int *d){
|
||||
@ -572,17 +633,8 @@ static int multiplexClose(sqlite3_file *pConn){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
multiplexGroup *pGroup = p->pGroup;
|
||||
int rc = SQLITE_OK;
|
||||
int i;
|
||||
multiplexEnter();
|
||||
/* close any open handles */
|
||||
for(i=0; i<pGroup->nMaxChunks; i++){
|
||||
if( pGroup->bOpen[i] ){
|
||||
sqlite3_file *pSubOpen = pGroup->pReal[i];
|
||||
int rc2 = pSubOpen->pMethods->xClose(pSubOpen);
|
||||
if( rc2!=SQLITE_OK ) rc = rc2;
|
||||
pGroup->bOpen[i] = 0;
|
||||
}
|
||||
}
|
||||
multiplexFreeComponents(pGroup);
|
||||
/* remove from linked list */
|
||||
if( pGroup->pNext ) pGroup->pNext->pPrev = pGroup->pPrev;
|
||||
if( pGroup->pPrev ){
|
||||
@ -610,17 +662,22 @@ static int multiplexRead(
|
||||
int rc = SQLITE_OK;
|
||||
multiplexEnter();
|
||||
if( !pGroup->bEnabled ){
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
|
||||
rc = ( !pSubOpen ) ? SQLITE_IOERR_READ : pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
|
||||
if( pSubOpen==0 ){
|
||||
rc = SQLITE_IOERR_READ;
|
||||
}else{
|
||||
rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst);
|
||||
}
|
||||
}else{
|
||||
while( iAmt > 0 ){
|
||||
int i = (int)(iOfst / pGroup->nChunkSize);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
|
||||
int i = (int)(iOfst / pGroup->szChunk);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL);
|
||||
if( pSubOpen ){
|
||||
int extra = ((int)(iOfst % pGroup->nChunkSize) + iAmt) - pGroup->nChunkSize;
|
||||
int extra = ((int)(iOfst % pGroup->szChunk) + iAmt) - pGroup->szChunk;
|
||||
if( extra<0 ) extra = 0;
|
||||
iAmt -= extra;
|
||||
rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst % pGroup->nChunkSize);
|
||||
rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt,
|
||||
iOfst % pGroup->szChunk);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
pBuf = (char *)pBuf + iAmt;
|
||||
iOfst += iAmt;
|
||||
@ -650,17 +707,23 @@ static int multiplexWrite(
|
||||
int rc = SQLITE_OK;
|
||||
multiplexEnter();
|
||||
if( !pGroup->bEnabled ){
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
|
||||
rc = ( !pSubOpen ) ? SQLITE_IOERR_WRITE : pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
|
||||
if( pSubOpen==0 ){
|
||||
rc = SQLITE_IOERR_WRITE;
|
||||
}else{
|
||||
rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst);
|
||||
}
|
||||
}else{
|
||||
while( iAmt > 0 ){
|
||||
int i = (int)(iOfst / pGroup->nChunkSize);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
|
||||
int i = (int)(iOfst / pGroup->szChunk);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL);
|
||||
if( pSubOpen ){
|
||||
int extra = ((int)(iOfst % pGroup->nChunkSize) + iAmt) - pGroup->nChunkSize;
|
||||
int extra = ((int)(iOfst % pGroup->szChunk) + iAmt) -
|
||||
pGroup->szChunk;
|
||||
if( extra<0 ) extra = 0;
|
||||
iAmt -= extra;
|
||||
rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst % pGroup->nChunkSize);
|
||||
rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt,
|
||||
iOfst % pGroup->szChunk);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
pBuf = (char *)pBuf + iAmt;
|
||||
iOfst += iAmt;
|
||||
@ -685,38 +748,24 @@ static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){
|
||||
int rc = SQLITE_OK;
|
||||
multiplexEnter();
|
||||
if( !pGroup->bEnabled ){
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
|
||||
rc = ( !pSubOpen ) ? SQLITE_IOERR_TRUNCATE : pSubOpen->pMethods->xTruncate(pSubOpen, size);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
|
||||
if( pSubOpen==0 ){
|
||||
rc = SQLITE_IOERR_TRUNCATE;
|
||||
}else{
|
||||
rc = pSubOpen->pMethods->xTruncate(pSubOpen, size);
|
||||
}
|
||||
}else{
|
||||
int rc2;
|
||||
int i;
|
||||
sqlite3_file *pSubOpen;
|
||||
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
|
||||
memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
|
||||
/* delete the chunks above the truncate limit */
|
||||
for(i=(int)(size / pGroup->nChunkSize)+1; i<pGroup->nMaxChunks; i++){
|
||||
/* close any open chunks before deleting them */
|
||||
if( pGroup->bOpen[i] ){
|
||||
pSubOpen = pGroup->pReal[i];
|
||||
rc2 = pSubOpen->pMethods->xClose(pSubOpen);
|
||||
if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
|
||||
pGroup->bOpen[i] = 0;
|
||||
}
|
||||
#ifdef SQLITE_MULTIPLEX_EXT_OVWR
|
||||
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1,
|
||||
gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ,
|
||||
SQLITE_MULTIPLEX_EXT_FMT, i);
|
||||
#else
|
||||
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1,
|
||||
gMultiplex.zName+pGroup->nName,
|
||||
SQLITE_MULTIPLEX_EXT_FMT, i);
|
||||
#endif
|
||||
rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, 0);
|
||||
if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
|
||||
for(i=(int)(size / pGroup->szChunk)+1; i<pGroup->nReal; i++){
|
||||
multiplexSubClose(pGroup, i, pOrigVfs);
|
||||
}
|
||||
pSubOpen = multiplexSubOpen(p, (int)(size / pGroup->nChunkSize), &rc2, NULL);
|
||||
pSubOpen = multiplexSubOpen(pGroup, (int)(size/pGroup->szChunk), &rc2,0);
|
||||
if( pSubOpen ){
|
||||
rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->nChunkSize);
|
||||
rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->szChunk);
|
||||
if( rc2!=SQLITE_OK ) rc = rc2;
|
||||
}else{
|
||||
rc = SQLITE_IOERR_TRUNCATE;
|
||||
@ -734,10 +783,9 @@ static int multiplexSync(sqlite3_file *pConn, int flags){
|
||||
int rc = SQLITE_OK;
|
||||
int i;
|
||||
multiplexEnter();
|
||||
for(i=0; i<pGroup->nMaxChunks; i++){
|
||||
/* if we don't have it open, we don't need to sync it */
|
||||
if( pGroup->bOpen[i] ){
|
||||
sqlite3_file *pSubOpen = pGroup->pReal[i];
|
||||
for(i=0; i<pGroup->nReal; i++){
|
||||
sqlite3_file *pSubOpen = pGroup->aReal[i].p;
|
||||
if( pSubOpen ){
|
||||
int rc2 = pSubOpen->pMethods->xSync(pSubOpen, flags);
|
||||
if( rc2!=SQLITE_OK ) rc = rc2;
|
||||
}
|
||||
@ -757,39 +805,28 @@ static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
|
||||
int i;
|
||||
multiplexEnter();
|
||||
if( !pGroup->bEnabled ){
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
|
||||
rc = ( !pSubOpen ) ? SQLITE_IOERR_FSTAT : pSubOpen->pMethods->xFileSize(pSubOpen, pSize);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
|
||||
if( pSubOpen==0 ){
|
||||
rc = SQLITE_IOERR_FSTAT;
|
||||
}else{
|
||||
rc = pSubOpen->pMethods->xFileSize(pSubOpen, pSize);
|
||||
}
|
||||
}else{
|
||||
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;
|
||||
*pSize = 0;
|
||||
for(i=0; i<pGroup->nMaxChunks; i++){
|
||||
sqlite3_file *pSubOpen = NULL;
|
||||
/* if not opened already, check to see if the chunk exists */
|
||||
if( pGroup->bOpen[i] ){
|
||||
pSubOpen = pGroup->pReal[i];
|
||||
for(i=0; 1; i++){
|
||||
sqlite3_file *pSubOpen = 0;
|
||||
int exists = 0;
|
||||
rc = multiplexSubFilename(pGroup, i);
|
||||
if( rc ) break;
|
||||
rc2 = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[i].z,
|
||||
SQLITE_ACCESS_EXISTS, &exists);
|
||||
if( rc2==SQLITE_OK && exists){
|
||||
/* if it exists, open it */
|
||||
pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL);
|
||||
}else{
|
||||
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
|
||||
int exists = 0;
|
||||
memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
|
||||
if( i ){
|
||||
#ifdef SQLITE_MULTIPLEX_EXT_OVWR
|
||||
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1,
|
||||
gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ,
|
||||
SQLITE_MULTIPLEX_EXT_FMT, i);
|
||||
#else
|
||||
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1,
|
||||
gMultiplex.zName+pGroup->nName,
|
||||
SQLITE_MULTIPLEX_EXT_FMT, i);
|
||||
#endif
|
||||
}
|
||||
rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName,
|
||||
SQLITE_ACCESS_EXISTS, &exists);
|
||||
if( rc2==SQLITE_OK && exists){
|
||||
/* if it exists, open it */
|
||||
pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
|
||||
}else{
|
||||
/* stop at first "gap" */
|
||||
break;
|
||||
}
|
||||
/* stop at first "gap" */
|
||||
break;
|
||||
}
|
||||
if( pSubOpen ){
|
||||
sqlite3_int64 sz;
|
||||
@ -797,7 +834,7 @@ static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
|
||||
if( rc2!=SQLITE_OK ){
|
||||
rc = rc2;
|
||||
}else{
|
||||
if( sz>pGroup->nChunkSize ){
|
||||
if( sz>pGroup->szChunk ){
|
||||
rc = SQLITE_IOERR_FSTAT;
|
||||
}
|
||||
*pSize += sz;
|
||||
@ -816,7 +853,7 @@ static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
|
||||
static int multiplexLock(sqlite3_file *pConn, int lock){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
|
||||
if( pSubOpen ){
|
||||
return pSubOpen->pMethods->xLock(pSubOpen, lock);
|
||||
}
|
||||
@ -828,7 +865,7 @@ static int multiplexLock(sqlite3_file *pConn, int lock){
|
||||
static int multiplexUnlock(sqlite3_file *pConn, int lock){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
|
||||
if( pSubOpen ){
|
||||
return pSubOpen->pMethods->xUnlock(pSubOpen, lock);
|
||||
}
|
||||
@ -840,7 +877,7 @@ static int multiplexUnlock(sqlite3_file *pConn, int lock){
|
||||
static int multiplexCheckReservedLock(sqlite3_file *pConn, int *pResOut){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
|
||||
if( pSubOpen ){
|
||||
return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut);
|
||||
}
|
||||
@ -867,28 +904,20 @@ static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){
|
||||
break;
|
||||
case MULTIPLEX_CTRL_SET_CHUNK_SIZE:
|
||||
if( pArg ) {
|
||||
int nChunkSize = *(int *)pArg;
|
||||
if( nChunkSize<1 ){
|
||||
unsigned int szChunk = *(unsigned*)pArg;
|
||||
if( szChunk<1 ){
|
||||
rc = SQLITE_MISUSE;
|
||||
}else{
|
||||
/* Round up to nearest multiple of MAX_PAGE_SIZE. */
|
||||
nChunkSize = (nChunkSize + (MAX_PAGE_SIZE-1));
|
||||
nChunkSize &= ~(MAX_PAGE_SIZE-1);
|
||||
pGroup->nChunkSize = nChunkSize;
|
||||
szChunk = (szChunk + (MAX_PAGE_SIZE-1));
|
||||
szChunk &= ~(MAX_PAGE_SIZE-1);
|
||||
pGroup->szChunk = szChunk;
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MULTIPLEX_CTRL_SET_MAX_CHUNKS:
|
||||
if( pArg ) {
|
||||
int nMaxChunks = *(int *)pArg;
|
||||
if(( nMaxChunks<1 ) || ( nMaxChunks>SQLITE_MULTIPLEX_MAX_CHUNKS )){
|
||||
rc = SQLITE_MISUSE;
|
||||
}else{
|
||||
pGroup->nMaxChunks = nMaxChunks;
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
}
|
||||
rc = SQLITE_OK;
|
||||
break;
|
||||
case SQLITE_FCNTL_SIZE_HINT:
|
||||
case SQLITE_FCNTL_CHUNK_SIZE:
|
||||
@ -896,7 +925,7 @@ static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){
|
||||
rc = SQLITE_OK;
|
||||
break;
|
||||
default:
|
||||
pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
|
||||
pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
|
||||
if( pSubOpen ){
|
||||
rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
|
||||
}
|
||||
@ -910,7 +939,7 @@ static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){
|
||||
static int multiplexSectorSize(sqlite3_file *pConn){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
|
||||
if( pSubOpen ){
|
||||
return pSubOpen->pMethods->xSectorSize(pSubOpen);
|
||||
}
|
||||
@ -922,7 +951,7 @@ static int multiplexSectorSize(sqlite3_file *pConn){
|
||||
static int multiplexDeviceCharacteristics(sqlite3_file *pConn){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
|
||||
if( pSubOpen ){
|
||||
return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen);
|
||||
}
|
||||
@ -940,9 +969,9 @@ static int multiplexShmMap(
|
||||
){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
|
||||
if( pSubOpen ){
|
||||
return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend, pp);
|
||||
return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend,pp);
|
||||
}
|
||||
return SQLITE_IOERR;
|
||||
}
|
||||
@ -957,7 +986,7 @@ static int multiplexShmLock(
|
||||
){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
|
||||
if( pSubOpen ){
|
||||
return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags);
|
||||
}
|
||||
@ -969,7 +998,7 @@ static int multiplexShmLock(
|
||||
static void multiplexShmBarrier(sqlite3_file *pConn){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
|
||||
if( pSubOpen ){
|
||||
pSubOpen->pMethods->xShmBarrier(pSubOpen);
|
||||
}
|
||||
@ -980,7 +1009,7 @@ static void multiplexShmBarrier(sqlite3_file *pConn){
|
||||
static int multiplexShmUnmap(sqlite3_file *pConn, int deleteFlag){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
|
||||
if( pSubOpen ){
|
||||
return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag);
|
||||
}
|
||||
@ -1010,11 +1039,6 @@ int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){
|
||||
if( !gMultiplex.pMutex ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
gMultiplex.zName = sqlite3_malloc(pOrigVfs->mxPathname);
|
||||
if( !gMultiplex.zName ){
|
||||
sqlite3_mutex_free(gMultiplex.pMutex);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
gMultiplex.pGroups = NULL;
|
||||
gMultiplex.isInitialized = 1;
|
||||
gMultiplex.pOrigVfs = pOrigVfs;
|
||||
@ -1047,7 +1071,8 @@ int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){
|
||||
gMultiplex.sIoMethodsV1.xCheckReservedLock = multiplexCheckReservedLock;
|
||||
gMultiplex.sIoMethodsV1.xFileControl = multiplexFileControl;
|
||||
gMultiplex.sIoMethodsV1.xSectorSize = multiplexSectorSize;
|
||||
gMultiplex.sIoMethodsV1.xDeviceCharacteristics = multiplexDeviceCharacteristics;
|
||||
gMultiplex.sIoMethodsV1.xDeviceCharacteristics =
|
||||
multiplexDeviceCharacteristics;
|
||||
gMultiplex.sIoMethodsV2 = gMultiplex.sIoMethodsV1;
|
||||
gMultiplex.sIoMethodsV2.iVersion = 2;
|
||||
gMultiplex.sIoMethodsV2.xShmMap = multiplexShmMap;
|
||||
@ -1074,7 +1099,6 @@ int sqlite3_multiplex_shutdown(void){
|
||||
if( gMultiplex.isInitialized==0 ) return SQLITE_MISUSE;
|
||||
if( gMultiplex.pGroups ) return SQLITE_MISUSE;
|
||||
gMultiplex.isInitialized = 0;
|
||||
sqlite3_free(gMultiplex.zName);
|
||||
sqlite3_mutex_free(gMultiplex.pMutex);
|
||||
sqlite3_vfs_unregister(&gMultiplex.sThisVfs);
|
||||
memset(&gMultiplex, 0, sizeof(gMultiplex));
|
||||
@ -1176,16 +1200,16 @@ static int test_multiplex_dump(
|
||||
Tcl_NewIntObj(pGroup->flags));
|
||||
|
||||
/* count number of chunks with open handles */
|
||||
for(i=0; i<pGroup->nMaxChunks; i++){
|
||||
if( pGroup->bOpen[i] ) nChunks++;
|
||||
for(i=0; i<pGroup->nReal; i++){
|
||||
if( pGroup->aReal[i].p!=0 ) nChunks++;
|
||||
}
|
||||
Tcl_ListObjAppendElement(interp, pGroupTerm,
|
||||
Tcl_NewIntObj(nChunks));
|
||||
|
||||
Tcl_ListObjAppendElement(interp, pGroupTerm,
|
||||
Tcl_NewIntObj(pGroup->nChunkSize));
|
||||
Tcl_NewIntObj(pGroup->szChunk));
|
||||
Tcl_ListObjAppendElement(interp, pGroupTerm,
|
||||
Tcl_NewIntObj(pGroup->nMaxChunks));
|
||||
Tcl_NewIntObj(pGroup->nReal));
|
||||
|
||||
Tcl_ListObjAppendElement(interp, pResult, pGroupTerm);
|
||||
}
|
||||
|
||||
171
src/test_quota.c
171
src/test_quota.c
@ -95,6 +95,7 @@ struct quotaFile {
|
||||
quotaGroup *pGroup; /* Quota group to which this file belongs */
|
||||
sqlite3_int64 iSize; /* Current size of this file */
|
||||
int nRef; /* Number of times this file is open */
|
||||
int deleteOnClose; /* True to delete this file when it closes */
|
||||
quotaFile *pNext, **ppPrev; /* Linked list of files in the same group */
|
||||
};
|
||||
|
||||
@ -164,12 +165,45 @@ static struct {
|
||||
static void quotaEnter(void){ sqlite3_mutex_enter(gQuota.pMutex); }
|
||||
static void quotaLeave(void){ sqlite3_mutex_leave(gQuota.pMutex); }
|
||||
|
||||
/* Count the number of open files in a quotaGroup
|
||||
*/
|
||||
static int quotaGroupOpenFileCount(quotaGroup *pGroup){
|
||||
int N = 0;
|
||||
quotaFile *pFile = pGroup->pFiles;
|
||||
while( pFile ){
|
||||
if( pFile->nRef ) N++;
|
||||
pFile = pFile->pNext;
|
||||
}
|
||||
return N;
|
||||
}
|
||||
|
||||
/* Remove a file from a quota group.
|
||||
*/
|
||||
static void quotaRemoveFile(quotaFile *pFile){
|
||||
quotaGroup *pGroup = pFile->pGroup;
|
||||
pGroup->iSize -= pFile->iSize;
|
||||
*pFile->ppPrev = pFile->pNext;
|
||||
if( pFile->pNext ) pFile->pNext->ppPrev = pFile->ppPrev;
|
||||
sqlite3_free(pFile);
|
||||
}
|
||||
|
||||
/* Remove all files from a quota group. It is always the case that
|
||||
** all files will be closed when this routine is called.
|
||||
*/
|
||||
static void quotaRemoveAllFiles(quotaGroup *pGroup){
|
||||
while( pGroup->pFiles ){
|
||||
assert( pGroup->pFiles->nRef==0 );
|
||||
quotaRemoveFile(pGroup->pFiles);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* If the reference count and threshold for a quotaGroup are both
|
||||
** zero, then destroy the quotaGroup.
|
||||
*/
|
||||
static void quotaGroupDeref(quotaGroup *pGroup){
|
||||
if( pGroup->pFiles==0 && pGroup->iLimit==0 ){
|
||||
if( pGroup->iLimit==0 && quotaGroupOpenFileCount(pGroup)==0 ){
|
||||
quotaRemoveAllFiles(pGroup);
|
||||
*pGroup->ppPrev = pGroup->pNext;
|
||||
if( pGroup->pNext ) pGroup->pNext->ppPrev = pGroup->ppPrev;
|
||||
if( pGroup->xDestroy ) pGroup->xDestroy(pGroup->pArg);
|
||||
@ -276,6 +310,17 @@ static sqlite3_file *quotaSubOpen(sqlite3_file *pConn){
|
||||
return (sqlite3_file*)&p[1];
|
||||
}
|
||||
|
||||
/* Find a file in a quota group and return a pointer to that file.
|
||||
** Return NULL if the file is not in the group.
|
||||
*/
|
||||
static quotaFile *quotaFindFile(quotaGroup *pGroup, const char *zName){
|
||||
quotaFile *pFile = pGroup->pFiles;
|
||||
while( pFile && strcmp(pFile->zFilename, zName)!=0 ){
|
||||
pFile = pFile->pNext;
|
||||
}
|
||||
return pFile;
|
||||
}
|
||||
|
||||
/************************* VFS Method Wrappers *****************************/
|
||||
/*
|
||||
** This is the xOpen method used for the "quota" VFS.
|
||||
@ -319,8 +364,7 @@ static int quotaOpen(
|
||||
pSubOpen = quotaSubOpen(pConn);
|
||||
rc = pOrigVfs->xOpen(pOrigVfs, zName, pSubOpen, flags, pOutFlags);
|
||||
if( rc==SQLITE_OK ){
|
||||
for(pFile=pGroup->pFiles; pFile && strcmp(pFile->zFilename, zName);
|
||||
pFile=pFile->pNext){}
|
||||
pFile = quotaFindFile(pGroup, zName);
|
||||
if( pFile==0 ){
|
||||
int nName = strlen(zName);
|
||||
pFile = (quotaFile *)sqlite3_malloc( sizeof(*pFile) + nName + 1 );
|
||||
@ -337,6 +381,7 @@ static int quotaOpen(
|
||||
pFile->ppPrev = &pGroup->pFiles;
|
||||
pGroup->pFiles = pFile;
|
||||
pFile->pGroup = pGroup;
|
||||
pFile->deleteOnClose = (flags & SQLITE_OPEN_DELETEONCLOSE)!=0;
|
||||
}
|
||||
pFile->nRef++;
|
||||
pQuotaOpen->pFile = pFile;
|
||||
@ -351,6 +396,49 @@ static int quotaOpen(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This is the xDelete method used for the "quota" VFS.
|
||||
**
|
||||
** If the file being deleted is part of the quota group, then reduce
|
||||
** the size of the quota group accordingly. And remove the file from
|
||||
** the set of files in the quota group.
|
||||
*/
|
||||
static int quotaDelete(
|
||||
sqlite3_vfs *pVfs, /* The quota VFS */
|
||||
const char *zName, /* Name of file to be deleted */
|
||||
int syncDir /* Do a directory sync after deleting */
|
||||
){
|
||||
int rc; /* Result code */
|
||||
quotaFile *pFile; /* Files in the quota */
|
||||
quotaGroup *pGroup; /* The group file belongs to */
|
||||
sqlite3_vfs *pOrigVfs = gQuota.pOrigVfs; /* Real VFS */
|
||||
|
||||
/* Do the actual file delete */
|
||||
rc = pOrigVfs->xDelete(pOrigVfs, zName, syncDir);
|
||||
|
||||
/* If the file just deleted is a member of a quota group, then remove
|
||||
** it from that quota group.
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
quotaEnter();
|
||||
pGroup = quotaGroupFind(zName);
|
||||
if( pGroup ){
|
||||
pFile = quotaFindFile(pGroup, zName);
|
||||
if( pFile ){
|
||||
if( pFile->nRef ){
|
||||
pFile->deleteOnClose = 1;
|
||||
}else{
|
||||
quotaRemoveFile(pFile);
|
||||
quotaGroupDeref(pGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
quotaLeave();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/************************ I/O Method Wrappers *******************************/
|
||||
|
||||
/* xClose requests get passed through to the original VFS. But we
|
||||
@ -367,11 +455,8 @@ static int quotaClose(sqlite3_file *pConn){
|
||||
pFile->nRef--;
|
||||
if( pFile->nRef==0 ){
|
||||
quotaGroup *pGroup = pFile->pGroup;
|
||||
pGroup->iSize -= pFile->iSize;
|
||||
if( pFile->pNext ) pFile->pNext->ppPrev = pFile->ppPrev;
|
||||
*pFile->ppPrev = pFile->pNext;
|
||||
if( pFile->deleteOnClose ) quotaRemoveFile(pFile);
|
||||
quotaGroupDeref(pGroup);
|
||||
sqlite3_free(pFile);
|
||||
}
|
||||
quotaLeave();
|
||||
return rc;
|
||||
@ -586,6 +671,7 @@ int sqlite3_quota_initialize(const char *zOrigVfsName, int makeDefault){
|
||||
gQuota.pOrigVfs = pOrigVfs;
|
||||
gQuota.sThisVfs = *pOrigVfs;
|
||||
gQuota.sThisVfs.xOpen = quotaOpen;
|
||||
gQuota.sThisVfs.xDelete = quotaDelete;
|
||||
gQuota.sThisVfs.szOsFile += sizeof(quotaConn);
|
||||
gQuota.sThisVfs.zName = "quota";
|
||||
gQuota.sIoMethodsV1.iVersion = 1;
|
||||
@ -617,19 +703,20 @@ int sqlite3_quota_initialize(const char *zOrigVfsName, int makeDefault){
|
||||
** All SQLite database connections must be closed before calling this
|
||||
** routine.
|
||||
**
|
||||
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly one while
|
||||
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while
|
||||
** shutting down in order to free all remaining quota groups.
|
||||
*/
|
||||
int sqlite3_quota_shutdown(void){
|
||||
quotaGroup *pGroup;
|
||||
if( gQuota.isInitialized==0 ) return SQLITE_MISUSE;
|
||||
for(pGroup=gQuota.pGroup; pGroup; pGroup=pGroup->pNext){
|
||||
if( pGroup->pFiles ) return SQLITE_MISUSE;
|
||||
if( quotaGroupOpenFileCount(pGroup)>0 ) return SQLITE_MISUSE;
|
||||
}
|
||||
while( gQuota.pGroup ){
|
||||
pGroup = gQuota.pGroup;
|
||||
gQuota.pGroup = pGroup->pNext;
|
||||
pGroup->iLimit = 0;
|
||||
assert( quotaGroupOpenFileCount(pGroup)==0 );
|
||||
quotaGroupDeref(pGroup);
|
||||
}
|
||||
gQuota.isInitialized = 0;
|
||||
@ -708,6 +795,43 @@ int sqlite3_quota_set(
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Bring the named file under quota management. Or if it is already under
|
||||
** management, update its size.
|
||||
*/
|
||||
int sqlite3_quota_file(const char *zFilename){
|
||||
char *zFull;
|
||||
sqlite3_file *fd;
|
||||
int rc;
|
||||
int outFlags = 0;
|
||||
sqlite3_int64 iSize;
|
||||
fd = sqlite3_malloc(gQuota.sThisVfs.szOsFile + gQuota.sThisVfs.mxPathname+1);
|
||||
if( fd==0 ) return SQLITE_NOMEM;
|
||||
zFull = gQuota.sThisVfs.szOsFile + (char*)fd;
|
||||
rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename,
|
||||
gQuota.sThisVfs.mxPathname+1, zFull);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = quotaOpen(&gQuota.sThisVfs, zFull, fd,
|
||||
SQLITE_OPEN_READONLY | SQLITE_OPEN_MAIN_DB, &outFlags);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
fd->pMethods->xFileSize(fd, &iSize);
|
||||
fd->pMethods->xClose(fd);
|
||||
}else if( rc==SQLITE_CANTOPEN ){
|
||||
quotaGroup *pGroup;
|
||||
quotaFile *pFile;
|
||||
quotaEnter();
|
||||
pGroup = quotaGroupFind(zFull);
|
||||
if( pGroup ){
|
||||
pFile = quotaFindFile(pGroup, zFull);
|
||||
if( pFile ) quotaRemoveFile(pFile);
|
||||
}
|
||||
quotaLeave();
|
||||
}
|
||||
sqlite3_free(fd);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/***************************** Test Code ***********************************/
|
||||
#ifdef SQLITE_TEST
|
||||
@ -884,6 +1008,32 @@ static int test_quota_set(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_file FILENAME
|
||||
*/
|
||||
static int test_quota_file(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
const char *zFilename; /* File pattern to configure */
|
||||
int rc; /* Value returned by quota_file() */
|
||||
|
||||
/* Process arguments */
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "FILENAME");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zFilename = Tcl_GetString(objv[1]);
|
||||
|
||||
/* Invoke sqlite3_quota_file() */
|
||||
rc = sqlite3_quota_file(zFilename);
|
||||
|
||||
Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_dump
|
||||
*/
|
||||
@ -917,6 +1067,8 @@ static int test_quota_dump(
|
||||
Tcl_NewWideIntObj(pFile->iSize));
|
||||
Tcl_ListObjAppendElement(interp, pFileTerm,
|
||||
Tcl_NewWideIntObj(pFile->nRef));
|
||||
Tcl_ListObjAppendElement(interp, pFileTerm,
|
||||
Tcl_NewWideIntObj(pFile->deleteOnClose));
|
||||
Tcl_ListObjAppendElement(interp, pGroupTerm, pFileTerm);
|
||||
}
|
||||
Tcl_ListObjAppendElement(interp, pResult, pGroupTerm);
|
||||
@ -939,6 +1091,7 @@ int Sqlitequota_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_quota_initialize", test_quota_initialize },
|
||||
{ "sqlite3_quota_shutdown", test_quota_shutdown },
|
||||
{ "sqlite3_quota_set", test_quota_set },
|
||||
{ "sqlite3_quota_file", test_quota_file },
|
||||
{ "sqlite3_quota_dump", test_quota_dump },
|
||||
};
|
||||
int i;
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
/* Solely for the UNUSED_PARAMETER() macro. */
|
||||
#include "sqliteInt.h"
|
||||
|
||||
#ifdef SQLITE_ENABLE_RTREE
|
||||
/*
|
||||
** Type used to cache parameter information for the "circle" r-tree geometry
|
||||
** callback.
|
||||
@ -230,6 +231,7 @@ static int cube_geom(
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_RTREE */
|
||||
|
||||
static int register_cube_geom(
|
||||
void * clientData,
|
||||
|
||||
@ -18,7 +18,9 @@
|
||||
** for an example implementation.
|
||||
*/
|
||||
|
||||
#include "sqliteInt.h"
|
||||
#ifndef SQLITE_AMALGAMATION
|
||||
# include "sqliteInt.h"
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
@ -62,20 +64,11 @@
|
||||
" ncell INTEGER, /* Cells on page (0 for overflow) */" \
|
||||
" payload INTEGER, /* Bytes of payload on this page */" \
|
||||
" unused INTEGER, /* Bytes of unused space on this page */" \
|
||||
" mx_payload INTEGER /* Largest payload size of all cells */" \
|
||||
" mx_payload INTEGER, /* Largest payload size of all cells */" \
|
||||
" pgoffset INTEGER, /* Offset of page in file */" \
|
||||
" pgsize INTEGER /* Size of the page */" \
|
||||
");"
|
||||
|
||||
#if 0
|
||||
#define VTAB_SCHEMA2 \
|
||||
"CREATE TABLE yy( " \
|
||||
" pageno INTEGER, /* B-tree page number */" \
|
||||
" cellno INTEGER, /* Cell number within page */" \
|
||||
" local INTEGER, /* Bytes of content stored locally */" \
|
||||
" payload INTEGER, /* Total cell payload size */" \
|
||||
" novfl INTEGER /* Number of overflow pages */" \
|
||||
");"
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct StatTable StatTable;
|
||||
typedef struct StatCursor StatCursor;
|
||||
@ -124,6 +117,8 @@ struct StatCursor {
|
||||
int nPayload; /* Value of 'payload' column */
|
||||
int nUnused; /* Value of 'unused' column */
|
||||
int nMxPayload; /* Value of 'mx_payload' column */
|
||||
i64 iOffset; /* Value of 'pgOffset' column */
|
||||
int szPage; /* Value of 'pgSize' column */
|
||||
};
|
||||
|
||||
struct StatTable {
|
||||
@ -281,6 +276,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){
|
||||
int iOff;
|
||||
int nHdr;
|
||||
int isLeaf;
|
||||
int szPage;
|
||||
|
||||
u8 *aData = sqlite3PagerGetData(p->pPg);
|
||||
u8 *aHdr = &aData[p->iPgno==1 ? 100 : 0];
|
||||
@ -301,10 +297,11 @@ static int statDecodePage(Btree *pBt, StatPage *p){
|
||||
}
|
||||
p->nUnused = nUnused;
|
||||
p->iRightChildPg = isLeaf ? 0 : sqlite3Get4byte(&aHdr[8]);
|
||||
szPage = sqlite3BtreeGetPageSize(pBt);
|
||||
|
||||
if( p->nCell ){
|
||||
int i; /* Used to iterate through cells */
|
||||
int nUsable = sqlite3BtreeGetPageSize(pBt) - sqlite3BtreeGetReserve(pBt);
|
||||
int nUsable = szPage - sqlite3BtreeGetReserve(pBt);
|
||||
|
||||
p->aCell = sqlite3_malloc((p->nCell+1) * sizeof(StatCell));
|
||||
memset(p->aCell, 0, (p->nCell+1) * sizeof(StatCell));
|
||||
@ -359,6 +356,32 @@ static int statDecodePage(Btree *pBt, StatPage *p){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Populate the pCsr->iOffset and pCsr->szPage member variables. Based on
|
||||
** the current value of pCsr->iPageno.
|
||||
*/
|
||||
static void statSizeAndOffset(StatCursor *pCsr){
|
||||
StatTable *pTab = (StatTable *)((sqlite3_vtab_cursor *)pCsr)->pVtab;
|
||||
Btree *pBt = pTab->db->aDb[0].pBt;
|
||||
Pager *pPager = sqlite3BtreePager(pBt);
|
||||
sqlite3_file *fd;
|
||||
sqlite3_int64 x[2];
|
||||
|
||||
/* The default page size and offset */
|
||||
pCsr->szPage = sqlite3BtreeGetPageSize(pBt);
|
||||
pCsr->iOffset = pCsr->szPage * (pCsr->iPageno - 1);
|
||||
|
||||
/* If connected to a ZIPVFS backend, override the page size and
|
||||
** offset with actual values obtained from ZIPVFS.
|
||||
*/
|
||||
fd = sqlite3PagerFile(pPager);
|
||||
x[0] = pCsr->iPageno;
|
||||
if( sqlite3OsFileControl(fd, 230440, &x)==SQLITE_OK ){
|
||||
pCsr->iOffset = x[0];
|
||||
pCsr->szPage = x[1];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Move a statvfs cursor to the next entry in the file.
|
||||
*/
|
||||
@ -417,6 +440,7 @@ static int statNext(sqlite3_vtab_cursor *pCursor){
|
||||
pCsr->nUnused = nUsable - 4 - pCsr->nPayload;
|
||||
}
|
||||
pCell->iOvfl++;
|
||||
statSizeAndOffset(pCsr);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
if( p->iRightChildPg ) break;
|
||||
@ -454,6 +478,7 @@ static int statNext(sqlite3_vtab_cursor *pCursor){
|
||||
pCsr->iPageno = p->iPgno;
|
||||
|
||||
statDecodePage(pBt, p);
|
||||
statSizeAndOffset(pCsr);
|
||||
|
||||
switch( p->flags ){
|
||||
case 0x05: /* table internal */
|
||||
@ -529,6 +554,12 @@ static int statColumn(
|
||||
case 7: /* mx_payload */
|
||||
sqlite3_result_int(ctx, pCsr->nMxPayload);
|
||||
break;
|
||||
case 8: /* pgoffset */
|
||||
sqlite3_result_int64(ctx, pCsr->iOffset);
|
||||
break;
|
||||
case 9: /* pgsize */
|
||||
sqlite3_result_int(ctx, pCsr->szPage);
|
||||
break;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -568,7 +599,7 @@ int sqlite3_dbstat_register(sqlite3 *db){
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
#if defined(SQLITE_TEST) || TCLSH==2
|
||||
#include <tcl.h>
|
||||
|
||||
static int test_dbstat(
|
||||
@ -604,4 +635,4 @@ int SqlitetestStat_Init(Tcl_Interp *interp){
|
||||
Tcl_CreateObjCommand(interp, "register_dbstat_vtab", test_dbstat, 0, 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif
|
||||
#endif /* if defined(SQLITE_TEST) || TCLSH==2 */
|
||||
|
||||
@ -325,6 +325,7 @@ static int ts_pread64(int fd, void *aBuf, size_t nBuf, off_t off){
|
||||
*/
|
||||
static int ts_write(int fd, const void *aBuf, size_t nBuf){
|
||||
if( tsIsFailErrno("write") ){
|
||||
if( tsErrno("write")==EINTR ) orig_write(fd, aBuf, nBuf/2);
|
||||
return -1;
|
||||
}
|
||||
return orig_write(fd, aBuf, nBuf);
|
||||
@ -671,4 +672,3 @@ int SqlitetestSyscall_Init(Tcl_Interp *interp){
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@ -282,6 +282,21 @@ static int sqlthread_open(
|
||||
|
||||
zFilename = Tcl_GetString(objv[2]);
|
||||
rc = sqlite3_open(zFilename, &db);
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
if( db && objc>=4 ){
|
||||
const char *zKey;
|
||||
int nKey;
|
||||
zKey = Tcl_GetStringFromObj(objv[3], &nKey);
|
||||
rc = sqlite3_key(db, zKey, nKey);
|
||||
if( rc!=SQLITE_OK ){
|
||||
char *zErrMsg = sqlite3_mprintf("error %d: %s", rc, sqlite3_errmsg(db));
|
||||
sqlite3_close(db);
|
||||
Tcl_AppendResult(interp, zErrMsg, (char*)0);
|
||||
sqlite3_free(zErrMsg);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Md5_Register(db);
|
||||
sqlite3_busy_handler(db, xBusy, 0);
|
||||
|
||||
@ -305,7 +320,7 @@ static int sqlthread_id(
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
Tcl_ThreadId id = Tcl_GetCurrentThread();
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj((int)id));
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(SQLITE_PTR_TO_INT(id)));
|
||||
UNUSED_PARAMETER(clientData);
|
||||
UNUSED_PARAMETER(objc);
|
||||
UNUSED_PARAMETER(objv);
|
||||
@ -349,7 +364,7 @@ static int sqlthread_proc(
|
||||
if( rc!=TCL_OK ) return rc;
|
||||
pSub = &aSub[iIndex];
|
||||
|
||||
if( objc!=(pSub->nArg+2) ){
|
||||
if( objc<(pSub->nArg+2) ){
|
||||
Tcl_WrongNumArgs(interp, 2, objv, pSub->zUsage);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
@ -123,6 +123,8 @@ struct Testvfs {
|
||||
#define TESTVFS_TRUNCATE_MASK 0x00002000
|
||||
#define TESTVFS_ACCESS_MASK 0x00004000
|
||||
#define TESTVFS_FULLPATHNAME_MASK 0x00008000
|
||||
#define TESTVFS_READ_MASK 0x00010000
|
||||
|
||||
#define TESTVFS_ALL_MASK 0x0001FFFF
|
||||
|
||||
|
||||
@ -325,8 +327,22 @@ static int tvfsRead(
|
||||
int iAmt,
|
||||
sqlite_int64 iOfst
|
||||
){
|
||||
TestvfsFd *p = tvfsGetFd(pFile);
|
||||
return sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
|
||||
int rc = SQLITE_OK;
|
||||
TestvfsFd *pFd = tvfsGetFd(pFile);
|
||||
Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
|
||||
if( p->pScript && p->mask&TESTVFS_READ_MASK ){
|
||||
tvfsExecTcl(p, "xRead",
|
||||
Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0
|
||||
);
|
||||
tvfsResultCode(p, &rc);
|
||||
}
|
||||
if( rc==SQLITE_OK && p->mask&TESTVFS_READ_MASK && tvfsInjectIoerr(p) ){
|
||||
rc = SQLITE_IOERR;
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3OsRead(pFd->pReal, zBuf, iAmt, iOfst);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1030,6 +1046,7 @@ static int testvfs_obj_cmd(
|
||||
{ "xSync", TESTVFS_SYNC_MASK },
|
||||
{ "xDelete", TESTVFS_DELETE_MASK },
|
||||
{ "xWrite", TESTVFS_WRITE_MASK },
|
||||
{ "xRead", TESTVFS_READ_MASK },
|
||||
{ "xTruncate", TESTVFS_TRUNCATE_MASK },
|
||||
{ "xOpen", TESTVFS_OPEN_MASK },
|
||||
{ "xClose", TESTVFS_CLOSE_MASK },
|
||||
|
||||
@ -117,15 +117,28 @@ void sqlite3BeginTrigger(
|
||||
goto trigger_cleanup;
|
||||
}
|
||||
}
|
||||
if( !pTableName || db->mallocFailed ){
|
||||
goto trigger_cleanup;
|
||||
}
|
||||
|
||||
/* A long-standing parser bug is that this syntax was allowed:
|
||||
**
|
||||
** CREATE TRIGGER attached.demo AFTER INSERT ON attached.tab ....
|
||||
** ^^^^^^^^
|
||||
**
|
||||
** To maintain backwards compatibility, ignore the database
|
||||
** name on pTableName if we are reparsing our of SQLITE_MASTER.
|
||||
*/
|
||||
if( db->init.busy && iDb!=1 ){
|
||||
sqlite3DbFree(db, pTableName->a[0].zDatabase);
|
||||
pTableName->a[0].zDatabase = 0;
|
||||
}
|
||||
|
||||
/* If the trigger name was unqualified, and the table is a temp table,
|
||||
** then set iDb to 1 to create the trigger in the temporary database.
|
||||
** If sqlite3SrcListLookup() returns 0, indicating the table does not
|
||||
** exist, the error is caught by the block below.
|
||||
*/
|
||||
if( !pTableName || db->mallocFailed ){
|
||||
goto trigger_cleanup;
|
||||
}
|
||||
pTab = sqlite3SrcListLookup(pParse, pTableName);
|
||||
if( db->init.busy==0 && pName2->n==0 && pTab
|
||||
&& pTab->pSchema==db->aDb[1].pSchema ){
|
||||
|
||||
@ -311,7 +311,9 @@ void sqlite3Update(
|
||||
/* Begin the database scan
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere,0, WHERE_ONEPASS_DESIRED);
|
||||
pWInfo = sqlite3WhereBegin(
|
||||
pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED
|
||||
);
|
||||
if( pWInfo==0 ) goto update_cleanup;
|
||||
okOnePass = pWInfo->okOnePass;
|
||||
|
||||
@ -354,6 +356,7 @@ void sqlite3Update(
|
||||
}
|
||||
}
|
||||
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
|
||||
assert( aRegIdx );
|
||||
if( openAll || aRegIdx[i]>0 ){
|
||||
KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
|
||||
sqlite3VdbeAddOp4(v, OP_OpenWrite, iCur+i+1, pIdx->tnum, iDb,
|
||||
@ -527,6 +530,7 @@ void sqlite3Update(
|
||||
|
||||
/* Close all tables */
|
||||
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
|
||||
assert( aRegIdx );
|
||||
if( openAll || aRegIdx[i]>0 ){
|
||||
sqlite3VdbeAddOp2(v, OP_Close, iCur+i+1, 0);
|
||||
}
|
||||
|
||||
@ -464,7 +464,7 @@ char *sqlite3Utf16to8(sqlite3 *db, const void *z, int nByte, u8 enc){
|
||||
** If a malloc failure occurs, NULL is returned and the db.mallocFailed
|
||||
** flag set.
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
char *sqlite3Utf8to16(sqlite3 *db, u8 enc, char *z, int n, int *pnOut){
|
||||
Mem m;
|
||||
memset(&m, 0, sizeof(m));
|
||||
|
||||
18
src/util.c
18
src/util.c
@ -331,7 +331,7 @@ int sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){
|
||||
}
|
||||
/* copy digits to exponent */
|
||||
while( z<zEnd && sqlite3Isdigit(*z) ){
|
||||
e = e*10 + (*z - '0');
|
||||
e = e<10000 ? (e*10 + (*z - '0')) : 10000;
|
||||
z+=incr;
|
||||
eValid = 1;
|
||||
}
|
||||
@ -382,6 +382,12 @@ do_atof_calc:
|
||||
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. */
|
||||
@ -1149,12 +1155,15 @@ int sqlite3AbsInt32(int x){
|
||||
|
||||
#ifdef SQLITE_ENABLE_8_3_NAMES
|
||||
/*
|
||||
** If SQLITE_ENABLE_8_3_NAME is set at compile-time and if the database
|
||||
** If SQLITE_ENABLE_8_3_NAMES is set at compile-time and if the database
|
||||
** filename in zBaseFilename is a URI with the "8_3_names=1" parameter and
|
||||
** if filename in z[] has a suffix (a.k.a. "extension") that is longer than
|
||||
** three characters, then shorten the suffix on z[] to be the last three
|
||||
** characters of the original suffix.
|
||||
**
|
||||
** If SQLITE_ENABLE_8_3_NAMES is set to 2 at compile-time, then always
|
||||
** do the suffix shortening regardless of URI parameter.
|
||||
**
|
||||
** Examples:
|
||||
**
|
||||
** test.db-journal => test.nal
|
||||
@ -1162,9 +1171,12 @@ int sqlite3AbsInt32(int x){
|
||||
** test.db-shm => test.shm
|
||||
*/
|
||||
void sqlite3FileSuffix3(const char *zBaseFilename, char *z){
|
||||
#if SQLITE_ENABLE_8_3_NAMES<2
|
||||
const char *zOk;
|
||||
zOk = sqlite3_uri_parameter(zBaseFilename, "8_3_names");
|
||||
if( zOk && sqlite3GetBoolean(zOk) ){
|
||||
if( zOk && sqlite3GetBoolean(zOk) )
|
||||
#endif
|
||||
{
|
||||
int i, sz;
|
||||
sz = sqlite3Strlen30(z);
|
||||
for(i=sz-1; i>0 && z[i]!='/' && z[i]!='.'; i--){}
|
||||
|
||||
14
src/vacuum.c
14
src/vacuum.c
@ -45,7 +45,7 @@ static int execSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
|
||||
return sqlite3_errcode(db);
|
||||
}
|
||||
VVA_ONLY( rc = ) sqlite3_step(pStmt);
|
||||
assert( rc!=SQLITE_ROW );
|
||||
assert( rc!=SQLITE_ROW || (db->flags&SQLITE_CountRows) );
|
||||
return vacuumFinalize(db, pStmt, pzErrMsg);
|
||||
}
|
||||
|
||||
@ -263,13 +263,11 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
||||
);
|
||||
if( rc ) goto end_of_vacuum;
|
||||
|
||||
/* At this point, unless the main db was completely empty, there is now a
|
||||
** transaction open on the vacuum database, but not on the main database.
|
||||
** Open a btree level transaction on the main database. This allows a
|
||||
** call to sqlite3BtreeCopyFile(). The main database btree level
|
||||
** transaction is then committed, so the SQL level never knows it was
|
||||
** opened for writing. This way, the SQL transaction used to create the
|
||||
** temporary database never needs to be committed.
|
||||
/* At this point, there is a write transaction open on both the
|
||||
** vacuum database and the main database. Assuming no error occurs,
|
||||
** both transactions are closed by this block - the main database
|
||||
** transaction by sqlite3BtreeCopyFile() and the other by an explicit
|
||||
** call to sqlite3BtreeCommit().
|
||||
*/
|
||||
{
|
||||
u32 meta;
|
||||
|
||||
254
src/vdbe.c
254
src/vdbe.c
@ -157,6 +157,13 @@ int sqlite3_found_count = 0;
|
||||
*/
|
||||
#define ExpandBlob(P) (((P)->flags&MEM_Zero)?sqlite3VdbeMemExpandBlob(P):0)
|
||||
|
||||
/* Return true if the cursor was opened using the OP_OpenSorter opcode. */
|
||||
#ifdef SQLITE_OMIT_MERGE_SORT
|
||||
# define isSorter(x) 0
|
||||
#else
|
||||
# define isSorter(x) ((x)->pSorter!=0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Argument pMem points at a register that will be passed to a
|
||||
** user-defined function or returned to the user as the result of a query.
|
||||
@ -666,7 +673,7 @@ int sqlite3VdbeExec(
|
||||
assert( pOp->p2<=p->nMem );
|
||||
pOut = &aMem[pOp->p2];
|
||||
memAboutToChange(p, pOut);
|
||||
sqlite3VdbeMemReleaseExternal(pOut);
|
||||
MemReleaseExt(pOut);
|
||||
pOut->flags = MEM_Int;
|
||||
}
|
||||
|
||||
@ -1027,6 +1034,11 @@ case OP_Move: {
|
||||
zMalloc = pOut->zMalloc;
|
||||
pOut->zMalloc = 0;
|
||||
sqlite3VdbeMemMove(pOut, pIn1);
|
||||
#ifdef SQLITE_DEBUG
|
||||
if( pOut->pScopyFrom>=&aMem[p1] && pOut->pScopyFrom<&aMem[p1+pOp->p3] ){
|
||||
pOut->pScopyFrom += p1 - pOp->p2;
|
||||
}
|
||||
#endif
|
||||
pIn1->zMalloc = zMalloc;
|
||||
REGISTER_TRACE(p2++, pOut);
|
||||
pIn1++;
|
||||
@ -2009,6 +2021,16 @@ case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: Once P1 P2 * * *
|
||||
**
|
||||
** Jump to P2 if the value in register P1 is a not null or zero. If
|
||||
** the value is NULL or zero, fall through and change the P1 register
|
||||
** to an integer 1.
|
||||
**
|
||||
** When P1 is not used otherwise in a program, this opcode falls through
|
||||
** once and jumps on all subsequent invocations. It is the equivalent
|
||||
** of "OP_If P1 P2", followed by "OP_Integer 1 P1".
|
||||
*/
|
||||
/* Opcode: If P1 P2 P3 * *
|
||||
**
|
||||
** Jump to P2 if the value in register P1 is true. The value
|
||||
@ -2021,6 +2043,7 @@ case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */
|
||||
** is considered true if it has a numeric value of zero. If the value
|
||||
** in P1 is NULL then take the jump if P3 is true.
|
||||
*/
|
||||
case OP_Once: /* jump, in1 */
|
||||
case OP_If: /* jump, in1 */
|
||||
case OP_IfNot: { /* jump, in1 */
|
||||
int c;
|
||||
@ -2037,6 +2060,12 @@ case OP_IfNot: { /* jump, in1 */
|
||||
}
|
||||
if( c ){
|
||||
pc = pOp->p2-1;
|
||||
}else if( pOp->opcode==OP_Once ){
|
||||
assert( (pIn1->flags & (MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame))==0 );
|
||||
memAboutToChange(p, pIn1);
|
||||
pIn1->flags = MEM_Int;
|
||||
pIn1->u.i = 1;
|
||||
REGISTER_TRACE(pOp->p1, pIn1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -2106,6 +2135,7 @@ case OP_Column: {
|
||||
u32 szField; /* Number of bytes in the content of a field */
|
||||
int szHdr; /* Size of the header size field at start of record */
|
||||
int avail; /* Number of bytes of available data */
|
||||
u32 t; /* A type code from the record header */
|
||||
Mem *pReg; /* PseudoTable input register */
|
||||
|
||||
|
||||
@ -2117,7 +2147,6 @@ case OP_Column: {
|
||||
assert( pOp->p3>0 && pOp->p3<=p->nMem );
|
||||
pDest = &aMem[pOp->p3];
|
||||
memAboutToChange(p, pDest);
|
||||
MemSetTypeFlag(pDest, MEM_Null);
|
||||
zRec = 0;
|
||||
|
||||
/* This block sets the variable payloadSize to be the total number of
|
||||
@ -2149,7 +2178,7 @@ case OP_Column: {
|
||||
zRec = (char*)pC->aRow;
|
||||
}else if( pC->isIndex ){
|
||||
assert( sqlite3BtreeCursorIsValid(pCrsr) );
|
||||
rc = sqlite3BtreeKeySize(pCrsr, &payloadSize64);
|
||||
VVA_ONLY(rc =) sqlite3BtreeKeySize(pCrsr, &payloadSize64);
|
||||
assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */
|
||||
/* sqlite3BtreeParseCellPtr() uses getVarint32() to extract the
|
||||
** payload size, so it is impossible for payloadSize64 to be
|
||||
@ -2158,10 +2187,10 @@ case OP_Column: {
|
||||
payloadSize = (u32)payloadSize64;
|
||||
}else{
|
||||
assert( sqlite3BtreeCursorIsValid(pCrsr) );
|
||||
rc = sqlite3BtreeDataSize(pCrsr, &payloadSize);
|
||||
VVA_ONLY(rc =) sqlite3BtreeDataSize(pCrsr, &payloadSize);
|
||||
assert( rc==SQLITE_OK ); /* DataSize() cannot fail */
|
||||
}
|
||||
}else if( pC->pseudoTableReg>0 ){
|
||||
}else if( ALWAYS(pC->pseudoTableReg>0) ){
|
||||
pReg = &aMem[pC->pseudoTableReg];
|
||||
assert( pReg->flags & MEM_Blob );
|
||||
assert( memIsValid(pReg) );
|
||||
@ -2174,9 +2203,10 @@ case OP_Column: {
|
||||
payloadSize = 0;
|
||||
}
|
||||
|
||||
/* If payloadSize is 0, then just store a NULL */
|
||||
/* If payloadSize is 0, then just store a NULL. This can happen because of
|
||||
** nullRow or because of a corrupt database. */
|
||||
if( payloadSize==0 ){
|
||||
assert( pDest->flags&MEM_Null );
|
||||
MemSetTypeFlag(pDest, MEM_Null);
|
||||
goto op_column_out;
|
||||
}
|
||||
assert( db->aLimit[SQLITE_LIMIT_LENGTH]>=0 );
|
||||
@ -2283,8 +2313,14 @@ case OP_Column: {
|
||||
for(i=0; i<nField; i++){
|
||||
if( zIdx<zEndHdr ){
|
||||
aOffset[i] = offset;
|
||||
zIdx += getVarint32(zIdx, aType[i]);
|
||||
szField = sqlite3VdbeSerialTypeLen(aType[i]);
|
||||
if( zIdx[0]<0x80 ){
|
||||
t = zIdx[0];
|
||||
zIdx++;
|
||||
}else{
|
||||
zIdx += sqlite3GetVarint32(zIdx, &t);
|
||||
}
|
||||
aType[i] = t;
|
||||
szField = sqlite3VdbeSerialTypeLen(t);
|
||||
offset += szField;
|
||||
if( offset<szField ){ /* True if offset overflows */
|
||||
zIdx = &zEndHdr[1]; /* Forces SQLITE_CORRUPT return below */
|
||||
@ -2325,7 +2361,7 @@ case OP_Column: {
|
||||
if( aOffset[p2] ){
|
||||
assert( rc==SQLITE_OK );
|
||||
if( zRec ){
|
||||
sqlite3VdbeMemReleaseExternal(pDest);
|
||||
MemReleaseExt(pDest);
|
||||
sqlite3VdbeSerialGet((u8 *)&zRec[aOffset[p2]], aType[p2], pDest);
|
||||
}else{
|
||||
len = sqlite3VdbeSerialTypeLen(aType[p2]);
|
||||
@ -2342,7 +2378,7 @@ case OP_Column: {
|
||||
if( pOp->p4type==P4_MEM ){
|
||||
sqlite3VdbeMemShallowCopy(pDest, pOp->p4.pMem, MEM_Static);
|
||||
}else{
|
||||
assert( pDest->flags&MEM_Null );
|
||||
MemSetTypeFlag(pDest, MEM_Null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2538,7 +2574,7 @@ case OP_Count: { /* out2-prerelease */
|
||||
BtCursor *pCrsr;
|
||||
|
||||
pCrsr = p->apCsr[pOp->p1]->pCursor;
|
||||
if( pCrsr ){
|
||||
if( ALWAYS(pCrsr) ){
|
||||
rc = sqlite3BtreeCount(pCrsr, &nEntry);
|
||||
}else{
|
||||
nEntry = 0;
|
||||
@ -3100,15 +3136,9 @@ case OP_OpenWrite: {
|
||||
rc = sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->pCursor);
|
||||
pCur->pKeyInfo = pKeyInfo;
|
||||
|
||||
/* Since it performs no memory allocation or IO, the only values that
|
||||
** sqlite3BtreeCursor() may return are SQLITE_EMPTY and SQLITE_OK.
|
||||
** SQLITE_EMPTY is only returned when attempting to open the table
|
||||
** rooted at page 1 of a zero-byte database. */
|
||||
assert( rc==SQLITE_EMPTY || rc==SQLITE_OK );
|
||||
if( rc==SQLITE_EMPTY ){
|
||||
pCur->pCursor = 0;
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
/* Since it performs no memory allocation or IO, the only value that
|
||||
** sqlite3BtreeCursor() may return is SQLITE_OK. */
|
||||
assert( rc==SQLITE_OK );
|
||||
|
||||
/* Set the VdbeCursor.isTable and isIndex variables. Previous versions of
|
||||
** SQLite used to check if the root-page flags were sane at this point
|
||||
@ -3119,7 +3149,7 @@ case OP_OpenWrite: {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: OpenEphemeral P1 P2 * P4 *
|
||||
/* Opcode: OpenEphemeral P1 P2 * P4 P5
|
||||
**
|
||||
** Open a new cursor P1 to a transient table.
|
||||
** The cursor is always opened read/write even if
|
||||
@ -3136,6 +3166,11 @@ case OP_OpenWrite: {
|
||||
** to a TEMP table at the SQL level, or to a table opened by
|
||||
** this opcode. Then this opcode was call OpenVirtual. But
|
||||
** that created confusion with the whole virtual-table idea.
|
||||
**
|
||||
** The P5 parameter can be a mask of the BTREE_* flags defined
|
||||
** in btree.h. These flags control aspects of the operation of
|
||||
** the btree. The BTREE_OMIT_JOURNAL and BTREE_SINGLE flags are
|
||||
** added automatically.
|
||||
*/
|
||||
/* Opcode: OpenAutoindex P1 P2 * P4 *
|
||||
**
|
||||
@ -3172,7 +3207,7 @@ case OP_OpenEphemeral: {
|
||||
if( pOp->p4.pKeyInfo ){
|
||||
int pgno;
|
||||
assert( pOp->p4type==P4_KEYINFO );
|
||||
rc = sqlite3BtreeCreateTable(pCx->pBt, &pgno, BTREE_BLOBKEY);
|
||||
rc = sqlite3BtreeCreateTable(pCx->pBt, &pgno, BTREE_BLOBKEY | pOp->p5);
|
||||
if( rc==SQLITE_OK ){
|
||||
assert( pgno==MASTER_ROOT+1 );
|
||||
rc = sqlite3BtreeCursor(pCx->pBt, pgno, 1,
|
||||
@ -3191,6 +3226,28 @@ case OP_OpenEphemeral: {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: OpenSorter P1 P2 * P4 *
|
||||
**
|
||||
** This opcode works like OP_OpenEphemeral except that it opens
|
||||
** a transient index that is specifically designed to sort large
|
||||
** tables using an external merge-sort algorithm.
|
||||
*/
|
||||
case OP_SorterOpen: {
|
||||
VdbeCursor *pCx;
|
||||
#ifndef SQLITE_OMIT_MERGE_SORT
|
||||
pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1);
|
||||
if( pCx==0 ) goto no_mem;
|
||||
pCx->pKeyInfo = pOp->p4.pKeyInfo;
|
||||
pCx->pKeyInfo->enc = ENC(p->db);
|
||||
pCx->isSorter = 1;
|
||||
rc = sqlite3VdbeSorterInit(db, pCx);
|
||||
#else
|
||||
pOp->opcode = OP_OpenEphemeral;
|
||||
pc--;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: OpenPseudo P1 P2 P3 * *
|
||||
**
|
||||
** Open a new cursor that points to a fake table that contains a single
|
||||
@ -3303,7 +3360,7 @@ case OP_SeekGt: { /* jump, in3 */
|
||||
assert( OP_SeekGe == OP_SeekLt+2 );
|
||||
assert( OP_SeekGt == OP_SeekLt+3 );
|
||||
assert( pC->isOrdered );
|
||||
if( pC->pCursor!=0 ){
|
||||
if( ALWAYS(pC->pCursor!=0) ){
|
||||
oc = pOp->opcode;
|
||||
pC->nullRow = 0;
|
||||
if( pC->isTable ){
|
||||
@ -3491,6 +3548,7 @@ case OP_Found: { /* jump, in3 */
|
||||
int alreadyExists;
|
||||
VdbeCursor *pC;
|
||||
int res;
|
||||
char *pFree;
|
||||
UnpackedRecord *pIdxKey;
|
||||
UnpackedRecord r;
|
||||
char aTempRec[ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*3 + 7];
|
||||
@ -3518,18 +3576,18 @@ case OP_Found: { /* jump, in3 */
|
||||
r.flags = UNPACKED_PREFIX_MATCH;
|
||||
pIdxKey = &r;
|
||||
}else{
|
||||
pIdxKey = sqlite3VdbeAllocUnpackedRecord(
|
||||
pC->pKeyInfo, aTempRec, sizeof(aTempRec), &pFree
|
||||
);
|
||||
if( pIdxKey==0 ) goto no_mem;
|
||||
assert( pIn3->flags & MEM_Blob );
|
||||
assert( (pIn3->flags & MEM_Zero)==0 ); /* zeroblobs already expanded */
|
||||
pIdxKey = sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z,
|
||||
aTempRec, sizeof(aTempRec));
|
||||
if( pIdxKey==0 ){
|
||||
goto no_mem;
|
||||
}
|
||||
sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, pIdxKey);
|
||||
pIdxKey->flags |= UNPACKED_PREFIX_MATCH;
|
||||
}
|
||||
rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, pIdxKey, 0, 0, &res);
|
||||
if( pOp->p4.i==0 ){
|
||||
sqlite3VdbeDeleteUnpackedRecord(pIdxKey);
|
||||
sqlite3DbFree(db, pFree);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
break;
|
||||
@ -3661,7 +3719,7 @@ case OP_NotExists: { /* jump, in3 */
|
||||
assert( pC->isTable );
|
||||
assert( pC->pseudoTableReg==0 );
|
||||
pCrsr = pC->pCursor;
|
||||
if( pCrsr!=0 ){
|
||||
if( ALWAYS(pCrsr!=0) ){
|
||||
res = 0;
|
||||
iKey = pIn3->u.i;
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res);
|
||||
@ -4039,6 +4097,45 @@ case OP_ResetCount: {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: SorterCompare P1 P2 P3
|
||||
**
|
||||
** P1 is a sorter cursor. This instruction compares the record blob in
|
||||
** register P3 with the entry that the sorter cursor currently points to.
|
||||
** If, excluding the rowid fields at the end, the two records are a match,
|
||||
** fall through to the next instruction. Otherwise, jump to instruction P2.
|
||||
*/
|
||||
case OP_SorterCompare: {
|
||||
VdbeCursor *pC;
|
||||
int res;
|
||||
|
||||
pC = p->apCsr[pOp->p1];
|
||||
assert( isSorter(pC) );
|
||||
pIn3 = &aMem[pOp->p3];
|
||||
rc = sqlite3VdbeSorterCompare(pC, pIn3, &res);
|
||||
if( res ){
|
||||
pc = pOp->p2-1;
|
||||
}
|
||||
break;
|
||||
};
|
||||
|
||||
/* Opcode: SorterData P1 P2 * * *
|
||||
**
|
||||
** Write into register P2 the current sorter data for sorter cursor P1.
|
||||
*/
|
||||
case OP_SorterData: {
|
||||
VdbeCursor *pC;
|
||||
#ifndef SQLITE_OMIT_MERGE_SORT
|
||||
pOut = &aMem[pOp->p2];
|
||||
pC = p->apCsr[pOp->p1];
|
||||
assert( pC->isSorter );
|
||||
rc = sqlite3VdbeSorterRowkey(pC, pOut);
|
||||
#else
|
||||
pOp->opcode = OP_RowKey;
|
||||
pc--;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: RowData P1 P2 * * *
|
||||
**
|
||||
** Write into register P2 the complete row data for cursor P1.
|
||||
@ -4072,11 +4169,13 @@ case OP_RowData: {
|
||||
/* Note that RowKey and RowData are really exactly the same instruction */
|
||||
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
||||
pC = p->apCsr[pOp->p1];
|
||||
assert( pC->isTable || pOp->opcode==OP_RowKey );
|
||||
assert( pC->isSorter==0 );
|
||||
assert( pC->isTable || pOp->opcode!=OP_RowData );
|
||||
assert( pC->isIndex || pOp->opcode==OP_RowData );
|
||||
assert( pC!=0 );
|
||||
assert( pC->nullRow==0 );
|
||||
assert( pC->pseudoTableReg==0 );
|
||||
assert( !pC->isSorter );
|
||||
assert( pC->pCursor!=0 );
|
||||
pCrsr = pC->pCursor;
|
||||
assert( sqlite3BtreeCursorIsValid(pCrsr) );
|
||||
@ -4092,14 +4191,14 @@ case OP_RowData: {
|
||||
|
||||
if( pC->isIndex ){
|
||||
assert( !pC->isTable );
|
||||
rc = sqlite3BtreeKeySize(pCrsr, &n64);
|
||||
VVA_ONLY(rc =) sqlite3BtreeKeySize(pCrsr, &n64);
|
||||
assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */
|
||||
if( n64>db->aLimit[SQLITE_LIMIT_LENGTH] ){
|
||||
goto too_big;
|
||||
}
|
||||
n = (u32)n64;
|
||||
}else{
|
||||
rc = sqlite3BtreeDataSize(pCrsr, &n);
|
||||
VVA_ONLY(rc =) sqlite3BtreeDataSize(pCrsr, &n);
|
||||
assert( rc==SQLITE_OK ); /* DataSize() cannot fail */
|
||||
if( n>(u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){
|
||||
goto too_big;
|
||||
@ -4181,6 +4280,7 @@ case OP_NullRow: {
|
||||
assert( pC!=0 );
|
||||
pC->nullRow = 1;
|
||||
pC->rowidIsValid = 0;
|
||||
assert( pC->pCursor || pC->pVtabCursor );
|
||||
if( pC->pCursor ){
|
||||
sqlite3BtreeClearCursor(pC->pCursor);
|
||||
}
|
||||
@ -4204,9 +4304,8 @@ case OP_Last: { /* jump */
|
||||
pC = p->apCsr[pOp->p1];
|
||||
assert( pC!=0 );
|
||||
pCrsr = pC->pCursor;
|
||||
if( pCrsr==0 ){
|
||||
res = 1;
|
||||
}else{
|
||||
res = 0;
|
||||
if( ALWAYS(pCrsr!=0) ){
|
||||
rc = sqlite3BtreeLast(pCrsr, &res);
|
||||
}
|
||||
pC->nullRow = (u8)res;
|
||||
@ -4232,6 +4331,10 @@ case OP_Last: { /* jump */
|
||||
** regression tests can determine whether or not the optimizer is
|
||||
** correctly optimizing out sorts.
|
||||
*/
|
||||
case OP_SorterSort: /* jump */
|
||||
#ifdef SQLITE_OMIT_MERGE_SORT
|
||||
pOp->opcode = OP_Sort;
|
||||
#endif
|
||||
case OP_Sort: { /* jump */
|
||||
#ifdef SQLITE_TEST
|
||||
sqlite3_sort_count++;
|
||||
@ -4256,8 +4359,13 @@ case OP_Rewind: { /* jump */
|
||||
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
||||
pC = p->apCsr[pOp->p1];
|
||||
assert( pC!=0 );
|
||||
assert( pC->isSorter==(pOp->opcode==OP_SorterSort) );
|
||||
res = 1;
|
||||
if( (pCrsr = pC->pCursor)!=0 ){
|
||||
if( isSorter(pC) ){
|
||||
rc = sqlite3VdbeSorterRewind(db, pC, &res);
|
||||
}else{
|
||||
pCrsr = pC->pCursor;
|
||||
assert( pCrsr );
|
||||
rc = sqlite3BtreeFirst(pCrsr, &res);
|
||||
pC->atFirst = res==0 ?1:0;
|
||||
pC->deferredMoveto = 0;
|
||||
@ -4272,7 +4380,7 @@ case OP_Rewind: { /* jump */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: Next P1 P2 * * P5
|
||||
/* Opcode: Next P1 P2 * P4 P5
|
||||
**
|
||||
** Advance cursor P1 so that it points to the next key/data pair in its
|
||||
** table or index. If there are no more key/value pairs then fall through
|
||||
@ -4281,6 +4389,9 @@ case OP_Rewind: { /* jump */
|
||||
**
|
||||
** The P1 cursor must be for a real table, not a pseudo-table.
|
||||
**
|
||||
** P4 is always of type P4_ADVANCE. The function pointer points to
|
||||
** sqlite3BtreeNext().
|
||||
**
|
||||
** If P5 is positive and the jump is taken, then event counter
|
||||
** number P5-1 in the prepared statement is incremented.
|
||||
**
|
||||
@ -4295,13 +4406,19 @@ case OP_Rewind: { /* jump */
|
||||
**
|
||||
** The P1 cursor must be for a real table, not a pseudo-table.
|
||||
**
|
||||
** P4 is always of type P4_ADVANCE. The function pointer points to
|
||||
** sqlite3BtreePrevious().
|
||||
**
|
||||
** If P5 is positive and the jump is taken, then event counter
|
||||
** number P5-1 in the prepared statement is incremented.
|
||||
*/
|
||||
case OP_SorterNext: /* jump */
|
||||
#ifdef SQLITE_OMIT_MERGE_SORT
|
||||
pOp->opcode = OP_Next;
|
||||
#endif
|
||||
case OP_Prev: /* jump */
|
||||
case OP_Next: { /* jump */
|
||||
VdbeCursor *pC;
|
||||
BtCursor *pCrsr;
|
||||
int res;
|
||||
|
||||
CHECK_FOR_INTERRUPT;
|
||||
@ -4311,15 +4428,18 @@ case OP_Next: { /* jump */
|
||||
if( pC==0 ){
|
||||
break; /* See ticket #2273 */
|
||||
}
|
||||
pCrsr = pC->pCursor;
|
||||
if( pCrsr==0 ){
|
||||
pC->nullRow = 1;
|
||||
break;
|
||||
assert( pC->isSorter==(pOp->opcode==OP_SorterNext) );
|
||||
if( isSorter(pC) ){
|
||||
assert( pOp->opcode==OP_SorterNext );
|
||||
rc = sqlite3VdbeSorterNext(db, pC, &res);
|
||||
}else{
|
||||
res = 1;
|
||||
assert( pC->deferredMoveto==0 );
|
||||
assert( pC->pCursor );
|
||||
assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite3BtreeNext );
|
||||
assert( pOp->opcode!=OP_Prev || pOp->p4.xAdvance==sqlite3BtreePrevious );
|
||||
rc = pOp->p4.xAdvance(pC->pCursor, &res);
|
||||
}
|
||||
res = 1;
|
||||
assert( pC->deferredMoveto==0 );
|
||||
rc = pOp->opcode==OP_Next ? sqlite3BtreeNext(pCrsr, &res) :
|
||||
sqlite3BtreePrevious(pCrsr, &res);
|
||||
pC->nullRow = (u8)res;
|
||||
pC->cacheStatus = CACHE_STALE;
|
||||
if( res==0 ){
|
||||
@ -4345,6 +4465,10 @@ case OP_Next: { /* jump */
|
||||
** This instruction only works for indices. The equivalent instruction
|
||||
** for tables is OP_Insert.
|
||||
*/
|
||||
case OP_SorterInsert: /* in2 */
|
||||
#ifdef SQLITE_OMIT_MERGE_SORT
|
||||
pOp->opcode = OP_IdxInsert;
|
||||
#endif
|
||||
case OP_IdxInsert: { /* in2 */
|
||||
VdbeCursor *pC;
|
||||
BtCursor *pCrsr;
|
||||
@ -4354,6 +4478,7 @@ case OP_IdxInsert: { /* in2 */
|
||||
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
||||
pC = p->apCsr[pOp->p1];
|
||||
assert( pC!=0 );
|
||||
assert( pC->isSorter==(pOp->opcode==OP_SorterInsert) );
|
||||
pIn2 = &aMem[pOp->p2];
|
||||
assert( pIn2->flags & MEM_Blob );
|
||||
pCrsr = pC->pCursor;
|
||||
@ -4361,13 +4486,17 @@ case OP_IdxInsert: { /* in2 */
|
||||
assert( pC->isTable==0 );
|
||||
rc = ExpandBlob(pIn2);
|
||||
if( rc==SQLITE_OK ){
|
||||
nKey = pIn2->n;
|
||||
zKey = pIn2->z;
|
||||
rc = sqlite3BtreeInsert(pCrsr, zKey, nKey, "", 0, 0, pOp->p3,
|
||||
((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0)
|
||||
);
|
||||
assert( pC->deferredMoveto==0 );
|
||||
pC->cacheStatus = CACHE_STALE;
|
||||
if( isSorter(pC) ){
|
||||
rc = sqlite3VdbeSorterWrite(db, pC, pIn2);
|
||||
}else{
|
||||
nKey = pIn2->n;
|
||||
zKey = pIn2->z;
|
||||
rc = sqlite3BtreeInsert(pCrsr, zKey, nKey, "", 0, 0, pOp->p3,
|
||||
((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0)
|
||||
);
|
||||
assert( pC->deferredMoveto==0 );
|
||||
pC->cacheStatus = CACHE_STALE;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -5337,7 +5466,7 @@ case OP_JournalMode: { /* out2-prerelease */
|
||||
** in temporary storage or if the VFS does not support shared memory
|
||||
*/
|
||||
if( eNew==PAGER_JOURNALMODE_WAL
|
||||
&& (zFilename[0]==0 /* Temp file */
|
||||
&& (sqlite3Strlen30(zFilename)==0 /* Temp file */
|
||||
|| !sqlite3PagerWalSupported(pPager)) /* No shared-memory support */
|
||||
){
|
||||
eNew = eOld;
|
||||
@ -5758,10 +5887,15 @@ case OP_VRename: {
|
||||
assert( memIsValid(pName) );
|
||||
REGISTER_TRACE(pOp->p1, pName);
|
||||
assert( pName->flags & MEM_Str );
|
||||
rc = pVtab->pModule->xRename(pVtab, pName->z);
|
||||
importVtabErrMsg(p, pVtab);
|
||||
p->expired = 0;
|
||||
|
||||
testcase( pName->enc==SQLITE_UTF8 );
|
||||
testcase( pName->enc==SQLITE_UTF16BE );
|
||||
testcase( pName->enc==SQLITE_UTF16LE );
|
||||
rc = sqlite3VdbeChangeEncoding(pName, SQLITE_UTF8);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pVtab->pModule->xRename(pVtab, pName->z);
|
||||
importVtabErrMsg(p, pVtab);
|
||||
p->expired = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
14
src/vdbe.h
14
src/vdbe.h
@ -61,6 +61,7 @@ struct VdbeOp {
|
||||
KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */
|
||||
int *ai; /* Used when p4type is P4_INTARRAY */
|
||||
SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */
|
||||
int (*xAdvance)(BtCursor *, int *);
|
||||
} p4;
|
||||
#ifdef SQLITE_DEBUG
|
||||
char *zComment; /* Comment to improve readability */
|
||||
@ -116,6 +117,7 @@ typedef struct VdbeOpList VdbeOpList;
|
||||
#define P4_INT32 (-14) /* P4 is a 32-bit signed integer */
|
||||
#define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */
|
||||
#define P4_SUBPROGRAM (-18) /* P4 is a pointer to a SubProgram structure */
|
||||
#define P4_ADVANCE (-19) /* P4 is a pointer to BtreeNext() or BtreePrev() */
|
||||
|
||||
/* When adding a P4 argument using P4_KEYINFO, a copy of the KeyInfo structure
|
||||
** is made. That copy is freed when the Vdbe is finalized. But if the
|
||||
@ -173,12 +175,12 @@ int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int);
|
||||
int sqlite3VdbeAddOp4Int(Vdbe*,int,int,int,int,int);
|
||||
int sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp);
|
||||
void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*);
|
||||
void sqlite3VdbeChangeP1(Vdbe*, int addr, int P1);
|
||||
void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2);
|
||||
void sqlite3VdbeChangeP3(Vdbe*, int addr, int P3);
|
||||
void sqlite3VdbeChangeP1(Vdbe*, u32 addr, int P1);
|
||||
void sqlite3VdbeChangeP2(Vdbe*, u32 addr, int P2);
|
||||
void sqlite3VdbeChangeP3(Vdbe*, u32 addr, int P3);
|
||||
void sqlite3VdbeChangeP5(Vdbe*, u8 P5);
|
||||
void sqlite3VdbeJumpHere(Vdbe*, int addr);
|
||||
void sqlite3VdbeChangeToNoop(Vdbe*, int addr, int N);
|
||||
void sqlite3VdbeChangeToNoop(Vdbe*, int addr);
|
||||
void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N);
|
||||
void sqlite3VdbeUsesBtree(Vdbe*, int);
|
||||
VdbeOp *sqlite3VdbeGetOp(Vdbe*, int);
|
||||
@ -210,9 +212,9 @@ void sqlite3VdbeSetVarmask(Vdbe*, int);
|
||||
char *sqlite3VdbeExpandSql(Vdbe*, const char*);
|
||||
#endif
|
||||
|
||||
UnpackedRecord *sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,char*,int);
|
||||
void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord*);
|
||||
void sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,UnpackedRecord*);
|
||||
int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*);
|
||||
UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo *, char *, int, char **);
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *);
|
||||
|
||||
@ -30,6 +30,9 @@ typedef struct VdbeOp Op;
|
||||
*/
|
||||
typedef unsigned char Bool;
|
||||
|
||||
/* Opaque type used by code in vdbesort.c */
|
||||
typedef struct VdbeSorter VdbeSorter;
|
||||
|
||||
/*
|
||||
** A cursor is a pointer into a single BTree within a database file.
|
||||
** The cursor can seek to a BTree entry with a particular key, or
|
||||
@ -56,11 +59,13 @@ struct VdbeCursor {
|
||||
Bool isTable; /* True if a table requiring integer keys */
|
||||
Bool isIndex; /* True if an index containing keys only - no data */
|
||||
Bool isOrdered; /* True if the underlying table is BTREE_UNORDERED */
|
||||
Bool isSorter; /* True if a new-style sorter */
|
||||
sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */
|
||||
const sqlite3_module *pModule; /* Module for cursor pVtabCursor */
|
||||
i64 seqCount; /* Sequence counter */
|
||||
i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */
|
||||
i64 lastRowid; /* Last rowid from a Next or NextIdx operation */
|
||||
VdbeSorter *pSorter; /* Sorter object for OP_SorterOpen cursors */
|
||||
|
||||
/* Result of last sqlite3BtreeMoveto() done by an OP_NotExists or
|
||||
** OP_IsUnique opcode on this cursor. */
|
||||
@ -380,6 +385,9 @@ int sqlite3VdbeMemNumerify(Mem*);
|
||||
int sqlite3VdbeMemFromBtree(BtCursor*,int,int,int,Mem*);
|
||||
void sqlite3VdbeMemRelease(Mem *p);
|
||||
void sqlite3VdbeMemReleaseExternal(Mem *p);
|
||||
#define MemReleaseExt(X) \
|
||||
if((X)->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame)) \
|
||||
sqlite3VdbeMemReleaseExternal(X);
|
||||
int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
|
||||
const char *sqlite3OpcodeName(int);
|
||||
int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
|
||||
@ -387,6 +395,25 @@ int sqlite3VdbeCloseStatement(Vdbe *, int);
|
||||
void sqlite3VdbeFrameDelete(VdbeFrame*);
|
||||
int sqlite3VdbeFrameRestore(VdbeFrame *);
|
||||
void sqlite3VdbeMemStoreType(Mem *pMem);
|
||||
int sqlite3VdbeTransferError(Vdbe *p);
|
||||
|
||||
#ifdef SQLITE_OMIT_MERGE_SORT
|
||||
# define sqlite3VdbeSorterInit(Y,Z) SQLITE_OK
|
||||
# define sqlite3VdbeSorterWrite(X,Y,Z) SQLITE_OK
|
||||
# define sqlite3VdbeSorterClose(Y,Z)
|
||||
# define sqlite3VdbeSorterRowkey(Y,Z) SQLITE_OK
|
||||
# define sqlite3VdbeSorterRewind(X,Y,Z) SQLITE_OK
|
||||
# define sqlite3VdbeSorterNext(X,Y,Z) SQLITE_OK
|
||||
# define sqlite3VdbeSorterCompare(X,Y,Z) SQLITE_OK
|
||||
#else
|
||||
int sqlite3VdbeSorterInit(sqlite3 *, VdbeCursor *);
|
||||
void sqlite3VdbeSorterClose(sqlite3 *, VdbeCursor *);
|
||||
int sqlite3VdbeSorterRowkey(VdbeCursor *, Mem *);
|
||||
int sqlite3VdbeSorterNext(sqlite3 *, VdbeCursor *, int *);
|
||||
int sqlite3VdbeSorterRewind(sqlite3 *, VdbeCursor *, int *);
|
||||
int sqlite3VdbeSorterWrite(sqlite3 *, VdbeCursor *, Mem *);
|
||||
int sqlite3VdbeSorterCompare(VdbeCursor *, Mem *, int *);
|
||||
#endif
|
||||
|
||||
#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0
|
||||
void sqlite3VdbeEnter(Vdbe*);
|
||||
|
||||
@ -454,7 +454,7 @@ end_of_step:
|
||||
** error has occured, then return the error code in p->rc to the
|
||||
** caller. Set the error code in the database handle to the same value.
|
||||
*/
|
||||
rc = db->errCode = p->rc;
|
||||
rc = sqlite3VdbeTransferError(p);
|
||||
}
|
||||
return (rc&db->errMask);
|
||||
}
|
||||
@ -488,7 +488,7 @@ int sqlite3_step(sqlite3_stmt *pStmt){
|
||||
&& cnt++ < SQLITE_MAX_SCHEMA_RETRY
|
||||
&& (rc2 = rc = sqlite3Reprepare(v))==SQLITE_OK ){
|
||||
sqlite3_reset(pStmt);
|
||||
v->expired = 0;
|
||||
assert( v->expired==0 );
|
||||
}
|
||||
if( rc2!=SQLITE_OK && ALWAYS(v->isPrepareV2) && ALWAYS(db->pErr) ){
|
||||
/* This case occurs after failing to recompile an sql statement.
|
||||
|
||||
233
src/vdbeaux.c
233
src/vdbeaux.c
@ -433,6 +433,12 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
|
||||
n = pOp[-1].p1;
|
||||
if( n>nMaxArgs ) nMaxArgs = n;
|
||||
#endif
|
||||
}else if( opcode==OP_Next || opcode==OP_SorterNext ){
|
||||
pOp->p4.xAdvance = sqlite3BtreeNext;
|
||||
pOp->p4type = P4_ADVANCE;
|
||||
}else if( opcode==OP_Prev ){
|
||||
pOp->p4.xAdvance = sqlite3BtreePrevious;
|
||||
pOp->p4type = P4_ADVANCE;
|
||||
}
|
||||
|
||||
if( (pOp->opflags & OPFLG_JUMP)!=0 && pOp->p2<0 ){
|
||||
@ -524,10 +530,9 @@ int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp){
|
||||
** static array using sqlite3VdbeAddOpList but we want to make a
|
||||
** few minor changes to the program.
|
||||
*/
|
||||
void sqlite3VdbeChangeP1(Vdbe *p, int addr, int val){
|
||||
void sqlite3VdbeChangeP1(Vdbe *p, u32 addr, int val){
|
||||
assert( p!=0 );
|
||||
assert( addr>=0 );
|
||||
if( p->nOp>addr ){
|
||||
if( ((u32)p->nOp)>addr ){
|
||||
p->aOp[addr].p1 = val;
|
||||
}
|
||||
}
|
||||
@ -536,10 +541,9 @@ void sqlite3VdbeChangeP1(Vdbe *p, int addr, int val){
|
||||
** Change the value of the P2 operand for a specific instruction.
|
||||
** This routine is useful for setting a jump destination.
|
||||
*/
|
||||
void sqlite3VdbeChangeP2(Vdbe *p, int addr, int val){
|
||||
void sqlite3VdbeChangeP2(Vdbe *p, u32 addr, int val){
|
||||
assert( p!=0 );
|
||||
assert( addr>=0 );
|
||||
if( p->nOp>addr ){
|
||||
if( ((u32)p->nOp)>addr ){
|
||||
p->aOp[addr].p2 = val;
|
||||
}
|
||||
}
|
||||
@ -547,10 +551,9 @@ void sqlite3VdbeChangeP2(Vdbe *p, int addr, int val){
|
||||
/*
|
||||
** Change the value of the P3 operand for a specific instruction.
|
||||
*/
|
||||
void sqlite3VdbeChangeP3(Vdbe *p, int addr, int val){
|
||||
void sqlite3VdbeChangeP3(Vdbe *p, u32 addr, int val){
|
||||
assert( p!=0 );
|
||||
assert( addr>=0 );
|
||||
if( p->nOp>addr ){
|
||||
if( ((u32)p->nOp)>addr ){
|
||||
p->aOp[addr].p3 = val;
|
||||
}
|
||||
}
|
||||
@ -572,8 +575,8 @@ void sqlite3VdbeChangeP5(Vdbe *p, u8 val){
|
||||
** the address of the next instruction to be coded.
|
||||
*/
|
||||
void sqlite3VdbeJumpHere(Vdbe *p, int addr){
|
||||
assert( addr>=0 );
|
||||
sqlite3VdbeChangeP2(p, addr, p->nOp);
|
||||
assert( addr>=0 || p->db->mallocFailed );
|
||||
if( addr>=0 ) sqlite3VdbeChangeP2(p, addr, p->nOp);
|
||||
}
|
||||
|
||||
|
||||
@ -667,18 +670,15 @@ void sqlite3VdbeLinkSubProgram(Vdbe *pVdbe, SubProgram *p){
|
||||
}
|
||||
|
||||
/*
|
||||
** Change N opcodes starting at addr to No-ops.
|
||||
** Change the opcode at addr into OP_Noop
|
||||
*/
|
||||
void sqlite3VdbeChangeToNoop(Vdbe *p, int addr, int N){
|
||||
void sqlite3VdbeChangeToNoop(Vdbe *p, int addr){
|
||||
if( p->aOp ){
|
||||
VdbeOp *pOp = &p->aOp[addr];
|
||||
sqlite3 *db = p->db;
|
||||
while( N-- ){
|
||||
freeP4(db, pOp->p4type, pOp->p4.p);
|
||||
memset(pOp, 0, sizeof(pOp[0]));
|
||||
pOp->opcode = OP_Noop;
|
||||
pOp++;
|
||||
}
|
||||
freeP4(db, pOp->p4type, pOp->p4.p);
|
||||
memset(pOp, 0, sizeof(pOp[0]));
|
||||
pOp->opcode = OP_Noop;
|
||||
}
|
||||
}
|
||||
|
||||
@ -781,30 +781,29 @@ void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){
|
||||
** makes the code easier to read during debugging. None of this happens
|
||||
** in a production build.
|
||||
*/
|
||||
void sqlite3VdbeComment(Vdbe *p, const char *zFormat, ...){
|
||||
va_list ap;
|
||||
if( !p ) return;
|
||||
static void vdbeVComment(Vdbe *p, const char *zFormat, va_list ap){
|
||||
assert( p->nOp>0 || p->aOp==0 );
|
||||
assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed );
|
||||
if( p->nOp ){
|
||||
char **pz = &p->aOp[p->nOp-1].zComment;
|
||||
assert( p->aOp );
|
||||
sqlite3DbFree(p->db, p->aOp[p->nOp-1].zComment);
|
||||
p->aOp[p->nOp-1].zComment = sqlite3VMPrintf(p->db, zFormat, ap);
|
||||
}
|
||||
}
|
||||
void sqlite3VdbeComment(Vdbe *p, const char *zFormat, ...){
|
||||
va_list ap;
|
||||
if( p ){
|
||||
va_start(ap, zFormat);
|
||||
sqlite3DbFree(p->db, *pz);
|
||||
*pz = sqlite3VMPrintf(p->db, zFormat, ap);
|
||||
vdbeVComment(p, zFormat, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
void sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){
|
||||
va_list ap;
|
||||
if( !p ) return;
|
||||
sqlite3VdbeAddOp0(p, OP_Noop);
|
||||
assert( p->nOp>0 || p->aOp==0 );
|
||||
assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed );
|
||||
if( p->nOp ){
|
||||
char **pz = &p->aOp[p->nOp-1].zComment;
|
||||
if( p ){
|
||||
sqlite3VdbeAddOp0(p, OP_Noop);
|
||||
va_start(ap, zFormat);
|
||||
sqlite3DbFree(p->db, *pz);
|
||||
*pz = sqlite3VMPrintf(p->db, zFormat, ap);
|
||||
vdbeVComment(p, zFormat, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
@ -834,7 +833,7 @@ void sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){
|
||||
VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){
|
||||
/* C89 specifies that the constant "dummy" will be initialized to all
|
||||
** zeros, which is correct. MSVC generates a warning, nevertheless. */
|
||||
static const VdbeOp dummy; /* Ignore the MSVC warning about no initializer */
|
||||
static VdbeOp dummy; /* Ignore the MSVC warning about no initializer */
|
||||
assert( p->magic==VDBE_MAGIC_INIT );
|
||||
if( addr<0 ){
|
||||
#ifdef SQLITE_OMIT_TRACE
|
||||
@ -942,6 +941,10 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
|
||||
sqlite3_snprintf(nTemp, zTemp, "program");
|
||||
break;
|
||||
}
|
||||
case P4_ADVANCE: {
|
||||
zTemp[0] = 0;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
zP4 = pOp->p4.z;
|
||||
if( zP4==0 ){
|
||||
@ -1138,7 +1141,7 @@ int sqlite3VdbeList(
|
||||
sqlite3 *db = p->db; /* The database connection */
|
||||
int i; /* Loop counter */
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
Mem *pMem = p->pResultSet = &p->aMem[1]; /* First Mem of result set */
|
||||
Mem *pMem = &p->aMem[1]; /* First Mem of result set */
|
||||
|
||||
assert( p->explain );
|
||||
assert( p->magic==VDBE_MAGIC_RUN );
|
||||
@ -1149,6 +1152,7 @@ int sqlite3VdbeList(
|
||||
** sqlite3_column_text16(), causing a translation to UTF-16 encoding.
|
||||
*/
|
||||
releaseMemArray(pMem, 8);
|
||||
p->pResultSet = 0;
|
||||
|
||||
if( p->rc==SQLITE_NOMEM ){
|
||||
/* This happens if a malloc() inside a call to sqlite3_column_text() or
|
||||
@ -1303,6 +1307,7 @@ int sqlite3VdbeList(
|
||||
}
|
||||
|
||||
p->nResColumn = 8 - 4*(p->explain-1);
|
||||
p->pResultSet = &p->aMem[1];
|
||||
p->rc = SQLITE_OK;
|
||||
rc = SQLITE_ROW;
|
||||
}
|
||||
@ -1565,6 +1570,7 @@ void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
|
||||
if( pCx==0 ){
|
||||
return;
|
||||
}
|
||||
sqlite3VdbeSorterClose(p->db, pCx);
|
||||
if( pCx->pBt ){
|
||||
sqlite3BtreeClose(pCx->pBt);
|
||||
/* The pCx->pCursor will be close automatically, if it exists, by
|
||||
@ -2304,6 +2310,30 @@ void sqlite3VdbeResetStepResult(Vdbe *p){
|
||||
p->rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Copy the error code and error message belonging to the VDBE passed
|
||||
** as the first argument to its database handle (so that they will be
|
||||
** returned by calls to sqlite3_errcode() and sqlite3_errmsg()).
|
||||
**
|
||||
** This function does not clear the VDBE error code or message, just
|
||||
** copies them to the database handle.
|
||||
*/
|
||||
int sqlite3VdbeTransferError(Vdbe *p){
|
||||
sqlite3 *db = p->db;
|
||||
int rc = p->rc;
|
||||
if( p->zErrMsg ){
|
||||
u8 mallocFailed = db->mallocFailed;
|
||||
sqlite3BeginBenignMalloc();
|
||||
sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, SQLITE_TRANSIENT);
|
||||
sqlite3EndBenignMalloc();
|
||||
db->mallocFailed = mallocFailed;
|
||||
db->errCode = rc;
|
||||
}else{
|
||||
sqlite3Error(db, rc, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Clean up a VDBE after execution but do not delete the VDBE just yet.
|
||||
** Write any error messages into *pzErrMsg. Return the result code.
|
||||
@ -2331,18 +2361,9 @@ int sqlite3VdbeReset(Vdbe *p){
|
||||
** instructions yet, leave the main database error information unchanged.
|
||||
*/
|
||||
if( p->pc>=0 ){
|
||||
if( p->zErrMsg ){
|
||||
sqlite3BeginBenignMalloc();
|
||||
sqlite3ValueSetStr(db->pErr,-1,p->zErrMsg,SQLITE_UTF8,SQLITE_TRANSIENT);
|
||||
sqlite3EndBenignMalloc();
|
||||
db->errCode = p->rc;
|
||||
sqlite3DbFree(db, p->zErrMsg);
|
||||
p->zErrMsg = 0;
|
||||
}else if( p->rc ){
|
||||
sqlite3Error(db, p->rc, 0);
|
||||
}else{
|
||||
sqlite3Error(db, SQLITE_OK, 0);
|
||||
}
|
||||
sqlite3VdbeTransferError(p);
|
||||
sqlite3DbFree(db, p->zErrMsg);
|
||||
p->zErrMsg = 0;
|
||||
if( p->runOnlyOnce ) p->expired = 1;
|
||||
}else if( p->rc && p->expired ){
|
||||
/* The expired flag was set on the VDBE before the first call
|
||||
@ -2822,57 +2843,70 @@ u32 sqlite3VdbeSerialGet(
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Given the nKey-byte encoding of a record in pKey[], parse the
|
||||
** record into a UnpackedRecord structure. Return a pointer to
|
||||
** that structure.
|
||||
** This routine is used to allocate sufficient space for an UnpackedRecord
|
||||
** structure large enough to be used with sqlite3VdbeRecordUnpack() if
|
||||
** the first argument is a pointer to KeyInfo structure pKeyInfo.
|
||||
**
|
||||
** The calling function might provide szSpace bytes of memory
|
||||
** space at pSpace. This space can be used to hold the returned
|
||||
** VDbeParsedRecord structure if it is large enough. If it is
|
||||
** not big enough, space is obtained from sqlite3_malloc().
|
||||
** The space is either allocated using sqlite3DbMallocRaw() or from within
|
||||
** the unaligned buffer passed via the second and third arguments (presumably
|
||||
** stack space). If the former, then *ppFree is set to a pointer that should
|
||||
** be eventually freed by the caller using sqlite3DbFree(). Or, if the
|
||||
** allocation comes from the pSpace/szSpace buffer, *ppFree is set to NULL
|
||||
** before returning.
|
||||
**
|
||||
** The returned structure should be closed by a call to
|
||||
** sqlite3VdbeDeleteUnpackedRecord().
|
||||
*/
|
||||
UnpackedRecord *sqlite3VdbeRecordUnpack(
|
||||
KeyInfo *pKeyInfo, /* Information about the record format */
|
||||
int nKey, /* Size of the binary record */
|
||||
const void *pKey, /* The binary record */
|
||||
char *pSpace, /* Unaligned space available to hold the object */
|
||||
int szSpace /* Size of pSpace[] in bytes */
|
||||
** If an OOM error occurs, NULL is returned.
|
||||
*/
|
||||
UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(
|
||||
KeyInfo *pKeyInfo, /* Description of the record */
|
||||
char *pSpace, /* Unaligned space available */
|
||||
int szSpace, /* Size of pSpace[] in bytes */
|
||||
char **ppFree /* OUT: Caller should free this pointer */
|
||||
){
|
||||
const unsigned char *aKey = (const unsigned char *)pKey;
|
||||
UnpackedRecord *p; /* The unpacked record that we will return */
|
||||
int nByte; /* Memory space needed to hold p, in bytes */
|
||||
int d;
|
||||
u32 idx;
|
||||
u16 u; /* Unsigned loop counter */
|
||||
u32 szHdr;
|
||||
Mem *pMem;
|
||||
int nOff; /* Increase pSpace by this much to 8-byte align it */
|
||||
|
||||
/*
|
||||
** We want to shift the pointer pSpace up such that it is 8-byte aligned.
|
||||
UnpackedRecord *p; /* Unpacked record to return */
|
||||
int nOff; /* Increment pSpace by nOff to align it */
|
||||
int nByte; /* Number of bytes required for *p */
|
||||
|
||||
/* We want to shift the pointer pSpace up such that it is 8-byte aligned.
|
||||
** Thus, we need to calculate a value, nOff, between 0 and 7, to shift
|
||||
** it by. If pSpace is already 8-byte aligned, nOff should be zero.
|
||||
*/
|
||||
nOff = (8 - (SQLITE_PTR_TO_INT(pSpace) & 7)) & 7;
|
||||
pSpace += nOff;
|
||||
szSpace -= nOff;
|
||||
nByte = ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nField+1);
|
||||
if( nByte>szSpace ){
|
||||
p = sqlite3DbMallocRaw(pKeyInfo->db, nByte);
|
||||
if( p==0 ) return 0;
|
||||
p->flags = UNPACKED_NEED_FREE | UNPACKED_NEED_DESTROY;
|
||||
if( nByte>szSpace+nOff ){
|
||||
p = (UnpackedRecord *)sqlite3DbMallocRaw(pKeyInfo->db, nByte);
|
||||
*ppFree = (char *)p;
|
||||
if( !p ) return 0;
|
||||
}else{
|
||||
p = (UnpackedRecord*)pSpace;
|
||||
p->flags = UNPACKED_NEED_DESTROY;
|
||||
p = (UnpackedRecord*)&pSpace[nOff];
|
||||
*ppFree = 0;
|
||||
}
|
||||
|
||||
p->aMem = (Mem*)&((char*)p)[ROUND8(sizeof(UnpackedRecord))];
|
||||
p->pKeyInfo = pKeyInfo;
|
||||
p->nField = pKeyInfo->nField + 1;
|
||||
p->aMem = pMem = (Mem*)&((char*)p)[ROUND8(sizeof(UnpackedRecord))];
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Given the nKey-byte encoding of a record in pKey[], populate the
|
||||
** UnpackedRecord structure indicated by the fourth argument with the
|
||||
** contents of the decoded record.
|
||||
*/
|
||||
void sqlite3VdbeRecordUnpack(
|
||||
KeyInfo *pKeyInfo, /* Information about the record format */
|
||||
int nKey, /* Size of the binary record */
|
||||
const void *pKey, /* The binary record */
|
||||
UnpackedRecord *p /* Populate this structure before returning. */
|
||||
){
|
||||
const unsigned char *aKey = (const unsigned char *)pKey;
|
||||
int d;
|
||||
u32 idx; /* Offset in aKey[] to read from */
|
||||
u16 u; /* Unsigned loop counter */
|
||||
u32 szHdr;
|
||||
Mem *pMem = p->aMem;
|
||||
|
||||
p->flags = 0;
|
||||
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
|
||||
idx = getVarint32(aKey, szHdr);
|
||||
d = szHdr;
|
||||
@ -2891,31 +2925,6 @@ UnpackedRecord *sqlite3VdbeRecordUnpack(
|
||||
}
|
||||
assert( u<=pKeyInfo->nField + 1 );
|
||||
p->nField = u;
|
||||
return (void*)p;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine destroys a UnpackedRecord object.
|
||||
*/
|
||||
void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord *p){
|
||||
#ifdef SQLITE_DEBUG
|
||||
int i;
|
||||
Mem *pMem;
|
||||
|
||||
assert( p!=0 );
|
||||
assert( p->flags & UNPACKED_NEED_DESTROY );
|
||||
for(i=0, pMem=p->aMem; i<p->nField; i++, pMem++){
|
||||
/* The unpacked record is always constructed by the
|
||||
** sqlite3VdbeUnpackRecord() function above, which makes all
|
||||
** strings and blobs static. And none of the elements are
|
||||
** ever transformed, so there is never anything to delete.
|
||||
*/
|
||||
if( NEVER(pMem->zMalloc) ) sqlite3VdbeMemRelease(pMem);
|
||||
}
|
||||
#endif
|
||||
if( p->flags & UNPACKED_NEED_FREE ){
|
||||
sqlite3DbFree(p->pKeyInfo->db, p);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3069,7 +3078,7 @@ int sqlite3VdbeIdxRowid(sqlite3 *db, BtCursor *pCur, i64 *rowid){
|
||||
** this code can safely assume that nCellKey is 32-bits
|
||||
*/
|
||||
assert( sqlite3BtreeCursorIsValid(pCur) );
|
||||
rc = sqlite3BtreeKeySize(pCur, &nCellKey);
|
||||
VVA_ONLY(rc =) sqlite3BtreeKeySize(pCur, &nCellKey);
|
||||
assert( rc==SQLITE_OK ); /* pCur is always valid so KeySize cannot fail */
|
||||
assert( (nCellKey & SQLITE_MAX_U32)==(u64)nCellKey );
|
||||
|
||||
@ -3144,7 +3153,7 @@ int sqlite3VdbeIdxKeyCompare(
|
||||
Mem m;
|
||||
|
||||
assert( sqlite3BtreeCursorIsValid(pCur) );
|
||||
rc = sqlite3BtreeKeySize(pCur, &nCellKey);
|
||||
VVA_ONLY(rc =) sqlite3BtreeKeySize(pCur, &nCellKey);
|
||||
assert( rc==SQLITE_OK ); /* pCur is always valid so KeySize cannot fail */
|
||||
/* nCellKey will always be between 0 and 0xffffffff because of the say
|
||||
** that btreeParseCellPtr() and sqlite3GetVarint32() are implemented */
|
||||
|
||||
@ -273,7 +273,7 @@ int sqlite3_blob_open(
|
||||
|
||||
/* Configure the OP_TableLock instruction */
|
||||
#ifdef SQLITE_OMIT_SHARED_CACHE
|
||||
sqlite3VdbeChangeToNoop(v, 2, 1);
|
||||
sqlite3VdbeChangeToNoop(v, 2);
|
||||
#else
|
||||
sqlite3VdbeChangeP1(v, 2, iDb);
|
||||
sqlite3VdbeChangeP2(v, 2, pTab->tnum);
|
||||
@ -283,7 +283,7 @@ int sqlite3_blob_open(
|
||||
|
||||
/* Remove either the OP_OpenWrite or OpenRead. Set the P2
|
||||
** parameter of the other to pTab->tnum. */
|
||||
sqlite3VdbeChangeToNoop(v, 4 - flags, 1);
|
||||
sqlite3VdbeChangeToNoop(v, 4 - flags);
|
||||
sqlite3VdbeChangeP2(v, 3 + flags, pTab->tnum);
|
||||
sqlite3VdbeChangeP3(v, 3 + flags, iDb);
|
||||
|
||||
|
||||
@ -271,24 +271,18 @@ int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){
|
||||
*/
|
||||
void sqlite3VdbeMemReleaseExternal(Mem *p){
|
||||
assert( p->db==0 || sqlite3_mutex_held(p->db->mutex) );
|
||||
testcase( p->flags & MEM_Agg );
|
||||
testcase( p->flags & MEM_Dyn );
|
||||
testcase( p->flags & MEM_RowSet );
|
||||
testcase( p->flags & MEM_Frame );
|
||||
if( p->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame) ){
|
||||
if( p->flags&MEM_Agg ){
|
||||
sqlite3VdbeMemFinalize(p, p->u.pDef);
|
||||
assert( (p->flags & MEM_Agg)==0 );
|
||||
sqlite3VdbeMemRelease(p);
|
||||
}else if( p->flags&MEM_Dyn && p->xDel ){
|
||||
assert( (p->flags&MEM_RowSet)==0 );
|
||||
p->xDel((void *)p->z);
|
||||
p->xDel = 0;
|
||||
}else if( p->flags&MEM_RowSet ){
|
||||
sqlite3RowSetClear(p->u.pRowSet);
|
||||
}else if( p->flags&MEM_Frame ){
|
||||
sqlite3VdbeMemSetNull(p);
|
||||
}
|
||||
if( p->flags&MEM_Agg ){
|
||||
sqlite3VdbeMemFinalize(p, p->u.pDef);
|
||||
assert( (p->flags & MEM_Agg)==0 );
|
||||
sqlite3VdbeMemRelease(p);
|
||||
}else if( p->flags&MEM_Dyn && p->xDel ){
|
||||
assert( (p->flags&MEM_RowSet)==0 );
|
||||
p->xDel((void *)p->z);
|
||||
p->xDel = 0;
|
||||
}else if( p->flags&MEM_RowSet ){
|
||||
sqlite3RowSetClear(p->u.pRowSet);
|
||||
}else if( p->flags&MEM_Frame ){
|
||||
sqlite3VdbeMemSetNull(p);
|
||||
}
|
||||
}
|
||||
|
||||
@ -298,7 +292,7 @@ void sqlite3VdbeMemReleaseExternal(Mem *p){
|
||||
** (Mem.type==SQLITE_TEXT).
|
||||
*/
|
||||
void sqlite3VdbeMemRelease(Mem *p){
|
||||
sqlite3VdbeMemReleaseExternal(p);
|
||||
MemReleaseExt(p);
|
||||
sqlite3DbFree(p->db, p->zMalloc);
|
||||
p->z = 0;
|
||||
p->zMalloc = 0;
|
||||
@ -620,7 +614,7 @@ void sqlite3VdbeMemPrepareToChange(Vdbe *pVdbe, Mem *pMem){
|
||||
*/
|
||||
void sqlite3VdbeMemShallowCopy(Mem *pTo, const Mem *pFrom, int srcType){
|
||||
assert( (pFrom->flags & MEM_RowSet)==0 );
|
||||
sqlite3VdbeMemReleaseExternal(pTo);
|
||||
MemReleaseExt(pTo);
|
||||
memcpy(pTo, pFrom, MEMCELLSIZE);
|
||||
pTo->xDel = 0;
|
||||
if( (pFrom->flags&MEM_Static)==0 ){
|
||||
@ -638,7 +632,7 @@ int sqlite3VdbeMemCopy(Mem *pTo, const Mem *pFrom){
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
assert( (pFrom->flags & MEM_RowSet)==0 );
|
||||
sqlite3VdbeMemReleaseExternal(pTo);
|
||||
MemReleaseExt(pTo);
|
||||
memcpy(pTo, pFrom, MEMCELLSIZE);
|
||||
pTo->flags &= ~MEM_Dyn;
|
||||
|
||||
@ -1032,11 +1026,11 @@ int sqlite3ValueFromExpr(
|
||||
}
|
||||
op = pExpr->op;
|
||||
|
||||
/* op can only be TK_REGISTER if we have compiled with SQLITE_ENABLE_STAT2.
|
||||
/* op can only be TK_REGISTER if we have compiled with SQLITE_ENABLE_STAT3.
|
||||
** The ifdef here is to enable us to achieve 100% branch test coverage even
|
||||
** when SQLITE_ENABLE_STAT2 is omitted.
|
||||
** when SQLITE_ENABLE_STAT3 is omitted.
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
if( op==TK_REGISTER ) op = pExpr->op2;
|
||||
#else
|
||||
if( NEVER(op==TK_REGISTER) ) op = pExpr->op2;
|
||||
|
||||
882
src/vdbesort.c
Normal file
882
src/vdbesort.c
Normal file
@ -0,0 +1,882 @@
|
||||
/*
|
||||
** 2011 July 9
|
||||
**
|
||||
** 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 for the VdbeSorter object, used in concert with
|
||||
** a VdbeCursor to sort large numbers of keys (as may be required, for
|
||||
** example, by CREATE INDEX statements on tables too large to fit in main
|
||||
** memory).
|
||||
*/
|
||||
|
||||
#include "sqliteInt.h"
|
||||
#include "vdbeInt.h"
|
||||
|
||||
#ifndef SQLITE_OMIT_MERGE_SORT
|
||||
|
||||
typedef struct VdbeSorterIter VdbeSorterIter;
|
||||
typedef struct SorterRecord SorterRecord;
|
||||
|
||||
/*
|
||||
** NOTES ON DATA STRUCTURE USED FOR N-WAY MERGES:
|
||||
**
|
||||
** As keys are added to the sorter, they are written to disk in a series
|
||||
** of sorted packed-memory-arrays (PMAs). The size of each PMA is roughly
|
||||
** the same as the cache-size allowed for temporary databases. In order
|
||||
** to allow the caller to extract keys from the sorter in sorted order,
|
||||
** all PMAs currently stored on disk must be merged together. This comment
|
||||
** describes the data structure used to do so. The structure supports
|
||||
** merging any number of arrays in a single pass with no redundant comparison
|
||||
** operations.
|
||||
**
|
||||
** The aIter[] array contains an iterator for each of the PMAs being merged.
|
||||
** An aIter[] iterator either points to a valid key or else is at EOF. For
|
||||
** the purposes of the paragraphs below, we assume that the array is actually
|
||||
** N elements in size, where N is the smallest power of 2 greater to or equal
|
||||
** to the number of iterators being merged. The extra aIter[] elements are
|
||||
** treated as if they are empty (always at EOF).
|
||||
**
|
||||
** The aTree[] array is also N elements in size. The value of N is stored in
|
||||
** the VdbeSorter.nTree variable.
|
||||
**
|
||||
** The final (N/2) elements of aTree[] contain the results of comparing
|
||||
** pairs of iterator keys together. Element i contains the result of
|
||||
** comparing aIter[2*i-N] and aIter[2*i-N+1]. Whichever key is smaller, the
|
||||
** aTree element is set to the index of it.
|
||||
**
|
||||
** For the purposes of this comparison, EOF is considered greater than any
|
||||
** other key value. If the keys are equal (only possible with two EOF
|
||||
** values), it doesn't matter which index is stored.
|
||||
**
|
||||
** The (N/4) elements of aTree[] that preceed the final (N/2) described
|
||||
** above contains the index of the smallest of each block of 4 iterators.
|
||||
** And so on. So that aTree[1] contains the index of the iterator that
|
||||
** currently points to the smallest key value. aTree[0] is unused.
|
||||
**
|
||||
** Example:
|
||||
**
|
||||
** aIter[0] -> Banana
|
||||
** aIter[1] -> Feijoa
|
||||
** aIter[2] -> Elderberry
|
||||
** aIter[3] -> Currant
|
||||
** aIter[4] -> Grapefruit
|
||||
** aIter[5] -> Apple
|
||||
** aIter[6] -> Durian
|
||||
** aIter[7] -> EOF
|
||||
**
|
||||
** aTree[] = { X, 5 0, 5 0, 3, 5, 6 }
|
||||
**
|
||||
** The current element is "Apple" (the value of the key indicated by
|
||||
** iterator 5). When the Next() operation is invoked, iterator 5 will
|
||||
** be advanced to the next key in its segment. Say the next key is
|
||||
** "Eggplant":
|
||||
**
|
||||
** aIter[5] -> Eggplant
|
||||
**
|
||||
** The contents of aTree[] are updated first by comparing the new iterator
|
||||
** 5 key to the current key of iterator 4 (still "Grapefruit"). The iterator
|
||||
** 5 value is still smaller, so aTree[6] is set to 5. And so on up the tree.
|
||||
** The value of iterator 6 - "Durian" - is now smaller than that of iterator
|
||||
** 5, so aTree[3] is set to 6. Key 0 is smaller than key 6 (Banana<Durian),
|
||||
** so the value written into element 1 of the array is 0. As follows:
|
||||
**
|
||||
** aTree[] = { X, 0 0, 6 0, 3, 5, 6 }
|
||||
**
|
||||
** In other words, each time we advance to the next sorter element, log2(N)
|
||||
** key comparison operations are required, where N is the number of segments
|
||||
** being merged (rounded up to the next power of 2).
|
||||
*/
|
||||
struct VdbeSorter {
|
||||
int nInMemory; /* Current size of pRecord list as PMA */
|
||||
int nTree; /* Used size of aTree/aIter (power of 2) */
|
||||
VdbeSorterIter *aIter; /* Array of iterators to merge */
|
||||
int *aTree; /* Current state of incremental merge */
|
||||
i64 iWriteOff; /* Current write offset within file pTemp1 */
|
||||
i64 iReadOff; /* Current read offset within file pTemp1 */
|
||||
sqlite3_file *pTemp1; /* PMA file 1 */
|
||||
int nPMA; /* Number of PMAs stored in pTemp1 */
|
||||
SorterRecord *pRecord; /* Head of in-memory record list */
|
||||
int mnPmaSize; /* Minimum PMA size, in bytes */
|
||||
int mxPmaSize; /* Maximum PMA size, in bytes. 0==no limit */
|
||||
UnpackedRecord *pUnpacked; /* Used to unpack keys */
|
||||
};
|
||||
|
||||
/*
|
||||
** The following type is an iterator for a PMA. It caches the current key in
|
||||
** variables nKey/aKey. If the iterator is at EOF, pFile==0.
|
||||
*/
|
||||
struct VdbeSorterIter {
|
||||
i64 iReadOff; /* Current read offset */
|
||||
i64 iEof; /* 1 byte past EOF for this iterator */
|
||||
sqlite3_file *pFile; /* File iterator is reading from */
|
||||
int nAlloc; /* Bytes of space at aAlloc */
|
||||
u8 *aAlloc; /* Allocated space */
|
||||
int nKey; /* Number of bytes in key */
|
||||
u8 *aKey; /* Pointer to current key */
|
||||
};
|
||||
|
||||
/*
|
||||
** A structure to store a single record. All in-memory records are connected
|
||||
** together into a linked list headed at VdbeSorter.pRecord using the
|
||||
** SorterRecord.pNext pointer.
|
||||
*/
|
||||
struct SorterRecord {
|
||||
void *pVal;
|
||||
int nVal;
|
||||
SorterRecord *pNext;
|
||||
};
|
||||
|
||||
/* Minimum allowable value for the VdbeSorter.nWorking variable */
|
||||
#define SORTER_MIN_WORKING 10
|
||||
|
||||
/* Maximum number of segments to merge in a single pass. */
|
||||
#define SORTER_MAX_MERGE_COUNT 16
|
||||
|
||||
/*
|
||||
** Free all memory belonging to the VdbeSorterIter object passed as the second
|
||||
** argument. All structure fields are set to zero before returning.
|
||||
*/
|
||||
static void vdbeSorterIterZero(sqlite3 *db, VdbeSorterIter *pIter){
|
||||
sqlite3DbFree(db, pIter->aAlloc);
|
||||
memset(pIter, 0, sizeof(VdbeSorterIter));
|
||||
}
|
||||
|
||||
/*
|
||||
** Advance iterator pIter to the next key in its PMA. Return SQLITE_OK if
|
||||
** no error occurs, or an SQLite error code if one does.
|
||||
*/
|
||||
static int vdbeSorterIterNext(
|
||||
sqlite3 *db, /* Database handle (for sqlite3DbMalloc() ) */
|
||||
VdbeSorterIter *pIter /* Iterator to advance */
|
||||
){
|
||||
int rc; /* Return Code */
|
||||
int nRead; /* Number of bytes read */
|
||||
int nRec = 0; /* Size of record in bytes */
|
||||
int iOff = 0; /* Size of serialized size varint in bytes */
|
||||
|
||||
assert( pIter->iEof>=pIter->iReadOff );
|
||||
if( pIter->iEof-pIter->iReadOff>5 ){
|
||||
nRead = 5;
|
||||
}else{
|
||||
nRead = (int)(pIter->iEof - pIter->iReadOff);
|
||||
}
|
||||
if( nRead<=0 ){
|
||||
/* This is an EOF condition */
|
||||
vdbeSorterIterZero(db, pIter);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
rc = sqlite3OsRead(pIter->pFile, pIter->aAlloc, nRead, pIter->iReadOff);
|
||||
if( rc==SQLITE_OK ){
|
||||
iOff = getVarint32(pIter->aAlloc, nRec);
|
||||
if( (iOff+nRec)>nRead ){
|
||||
int nRead2; /* Number of extra bytes to read */
|
||||
if( (iOff+nRec)>pIter->nAlloc ){
|
||||
int nNew = pIter->nAlloc*2;
|
||||
while( (iOff+nRec)>nNew ) nNew = nNew*2;
|
||||
pIter->aAlloc = sqlite3DbReallocOrFree(db, pIter->aAlloc, nNew);
|
||||
if( !pIter->aAlloc ) return SQLITE_NOMEM;
|
||||
pIter->nAlloc = nNew;
|
||||
}
|
||||
|
||||
nRead2 = iOff + nRec - nRead;
|
||||
rc = sqlite3OsRead(
|
||||
pIter->pFile, &pIter->aAlloc[nRead], nRead2, pIter->iReadOff+nRead
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
assert( rc!=SQLITE_OK || nRec>0 );
|
||||
pIter->iReadOff += iOff+nRec;
|
||||
pIter->nKey = nRec;
|
||||
pIter->aKey = &pIter->aAlloc[iOff];
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write a single varint, value iVal, to file-descriptor pFile. Return
|
||||
** SQLITE_OK if successful, or an SQLite error code if some error occurs.
|
||||
**
|
||||
** The value of *piOffset when this function is called is used as the byte
|
||||
** offset in file pFile to write to. Before returning, *piOffset is
|
||||
** incremented by the number of bytes written.
|
||||
*/
|
||||
static int vdbeSorterWriteVarint(
|
||||
sqlite3_file *pFile, /* File to write to */
|
||||
i64 iVal, /* Value to write as a varint */
|
||||
i64 *piOffset /* IN/OUT: Write offset in file pFile */
|
||||
){
|
||||
u8 aVarint[9]; /* Buffer large enough for a varint */
|
||||
int nVarint; /* Number of used bytes in varint */
|
||||
int rc; /* Result of write() call */
|
||||
|
||||
nVarint = sqlite3PutVarint(aVarint, iVal);
|
||||
rc = sqlite3OsWrite(pFile, aVarint, nVarint, *piOffset);
|
||||
*piOffset += nVarint;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Read a single varint from file-descriptor pFile. Return SQLITE_OK if
|
||||
** successful, or an SQLite error code if some error occurs.
|
||||
**
|
||||
** The value of *piOffset when this function is called is used as the
|
||||
** byte offset in file pFile from whence to read the varint. If successful
|
||||
** (i.e. if no IO error occurs), then *piOffset is set to the offset of
|
||||
** the first byte past the end of the varint before returning. *piVal is
|
||||
** set to the integer value read. If an error occurs, the final values of
|
||||
** both *piOffset and *piVal are undefined.
|
||||
*/
|
||||
static int vdbeSorterReadVarint(
|
||||
sqlite3_file *pFile, /* File to read from */
|
||||
i64 *piOffset, /* IN/OUT: Read offset in pFile */
|
||||
i64 *piVal /* OUT: Value read from file */
|
||||
){
|
||||
u8 aVarint[9]; /* Buffer large enough for a varint */
|
||||
i64 iOff = *piOffset; /* Offset in file to read from */
|
||||
int rc; /* Return code */
|
||||
|
||||
rc = sqlite3OsRead(pFile, aVarint, 9, iOff);
|
||||
if( rc==SQLITE_OK ){
|
||||
*piOffset += getVarint(aVarint, (u64 *)piVal);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Initialize iterator pIter to scan through the PMA stored in file pFile
|
||||
** starting at offset iStart and ending at offset iEof-1. This function
|
||||
** leaves the iterator pointing to the first key in the PMA (or EOF if the
|
||||
** PMA is empty).
|
||||
*/
|
||||
static int vdbeSorterIterInit(
|
||||
sqlite3 *db, /* Database handle */
|
||||
VdbeSorter *pSorter, /* Sorter object */
|
||||
i64 iStart, /* Start offset in pFile */
|
||||
VdbeSorterIter *pIter, /* Iterator to populate */
|
||||
i64 *pnByte /* IN/OUT: Increment this value by PMA size */
|
||||
){
|
||||
int rc;
|
||||
|
||||
assert( pSorter->iWriteOff>iStart );
|
||||
assert( pIter->aAlloc==0 );
|
||||
pIter->pFile = pSorter->pTemp1;
|
||||
pIter->iReadOff = iStart;
|
||||
pIter->nAlloc = 128;
|
||||
pIter->aAlloc = (u8 *)sqlite3DbMallocRaw(db, pIter->nAlloc);
|
||||
if( !pIter->aAlloc ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
i64 nByte; /* Total size of PMA in bytes */
|
||||
rc = vdbeSorterReadVarint(pSorter->pTemp1, &pIter->iReadOff, &nByte);
|
||||
*pnByte += nByte;
|
||||
pIter->iEof = pIter->iReadOff + nByte;
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = vdbeSorterIterNext(db, pIter);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Compare key1 (buffer pKey1, size nKey1 bytes) with key2 (buffer pKey2,
|
||||
** size nKey2 bytes). Argument pKeyInfo supplies the collation functions
|
||||
** used by the comparison. If an error occurs, return an SQLite error code.
|
||||
** Otherwise, return SQLITE_OK and set *pRes to a negative, zero or positive
|
||||
** value, depending on whether key1 is smaller, equal to or larger than key2.
|
||||
**
|
||||
** If the bOmitRowid argument is non-zero, assume both keys end in a rowid
|
||||
** field. For the purposes of the comparison, ignore it. Also, if bOmitRowid
|
||||
** is true and key1 contains even a single NULL value, it is considered to
|
||||
** be less than key2. Even if key2 also contains NULL values.
|
||||
**
|
||||
** If pKey2 is passed a NULL pointer, then it is assumed that the pCsr->aSpace
|
||||
** has been allocated and contains an unpacked record that is used as key2.
|
||||
*/
|
||||
static void vdbeSorterCompare(
|
||||
VdbeCursor *pCsr, /* Cursor object (for pKeyInfo) */
|
||||
int bOmitRowid, /* Ignore rowid field at end of keys */
|
||||
void *pKey1, int nKey1, /* Left side of comparison */
|
||||
void *pKey2, int nKey2, /* Right side of comparison */
|
||||
int *pRes /* OUT: Result of comparison */
|
||||
){
|
||||
KeyInfo *pKeyInfo = pCsr->pKeyInfo;
|
||||
VdbeSorter *pSorter = pCsr->pSorter;
|
||||
UnpackedRecord *r2 = pSorter->pUnpacked;
|
||||
int i;
|
||||
|
||||
if( pKey2 ){
|
||||
sqlite3VdbeRecordUnpack(pKeyInfo, nKey2, pKey2, r2);
|
||||
}
|
||||
|
||||
if( bOmitRowid ){
|
||||
r2->nField = pKeyInfo->nField;
|
||||
assert( r2->nField>0 );
|
||||
for(i=0; i<r2->nField; i++){
|
||||
if( r2->aMem[i].flags & MEM_Null ){
|
||||
*pRes = -1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
r2->flags |= UNPACKED_PREFIX_MATCH;
|
||||
}
|
||||
|
||||
*pRes = sqlite3VdbeRecordCompare(nKey1, pKey1, r2);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called to compare two iterator keys when merging
|
||||
** multiple b-tree segments. Parameter iOut is the index of the aTree[]
|
||||
** value to recalculate.
|
||||
*/
|
||||
static int vdbeSorterDoCompare(VdbeCursor *pCsr, int iOut){
|
||||
VdbeSorter *pSorter = pCsr->pSorter;
|
||||
int i1;
|
||||
int i2;
|
||||
int iRes;
|
||||
VdbeSorterIter *p1;
|
||||
VdbeSorterIter *p2;
|
||||
|
||||
assert( iOut<pSorter->nTree && iOut>0 );
|
||||
|
||||
if( iOut>=(pSorter->nTree/2) ){
|
||||
i1 = (iOut - pSorter->nTree/2) * 2;
|
||||
i2 = i1 + 1;
|
||||
}else{
|
||||
i1 = pSorter->aTree[iOut*2];
|
||||
i2 = pSorter->aTree[iOut*2+1];
|
||||
}
|
||||
|
||||
p1 = &pSorter->aIter[i1];
|
||||
p2 = &pSorter->aIter[i2];
|
||||
|
||||
if( p1->pFile==0 ){
|
||||
iRes = i2;
|
||||
}else if( p2->pFile==0 ){
|
||||
iRes = i1;
|
||||
}else{
|
||||
int res;
|
||||
assert( pCsr->pSorter->pUnpacked!=0 ); /* allocated in vdbeSorterMerge() */
|
||||
vdbeSorterCompare(
|
||||
pCsr, 0, p1->aKey, p1->nKey, p2->aKey, p2->nKey, &res
|
||||
);
|
||||
if( res<=0 ){
|
||||
iRes = i1;
|
||||
}else{
|
||||
iRes = i2;
|
||||
}
|
||||
}
|
||||
|
||||
pSorter->aTree[iOut] = iRes;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Initialize the temporary index cursor just opened as a sorter cursor.
|
||||
*/
|
||||
int sqlite3VdbeSorterInit(sqlite3 *db, VdbeCursor *pCsr){
|
||||
int pgsz; /* Page size of main database */
|
||||
int mxCache; /* Cache size */
|
||||
VdbeSorter *pSorter; /* The new sorter */
|
||||
char *d; /* Dummy */
|
||||
|
||||
assert( pCsr->pKeyInfo && pCsr->pBt==0 );
|
||||
pCsr->pSorter = pSorter = sqlite3DbMallocZero(db, sizeof(VdbeSorter));
|
||||
if( pSorter==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
pSorter->pUnpacked = sqlite3VdbeAllocUnpackedRecord(pCsr->pKeyInfo, 0, 0, &d);
|
||||
if( pSorter->pUnpacked==0 ) return SQLITE_NOMEM;
|
||||
assert( pSorter->pUnpacked==(UnpackedRecord *)d );
|
||||
|
||||
if( !sqlite3TempInMemory(db) ){
|
||||
pgsz = sqlite3BtreeGetPageSize(db->aDb[0].pBt);
|
||||
pSorter->mnPmaSize = SORTER_MIN_WORKING * pgsz;
|
||||
mxCache = db->aDb[0].pSchema->cache_size;
|
||||
if( mxCache<SORTER_MIN_WORKING ) mxCache = SORTER_MIN_WORKING;
|
||||
pSorter->mxPmaSize = mxCache * pgsz;
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free the list of sorted records starting at pRecord.
|
||||
*/
|
||||
static void vdbeSorterRecordFree(sqlite3 *db, SorterRecord *pRecord){
|
||||
SorterRecord *p;
|
||||
SorterRecord *pNext;
|
||||
for(p=pRecord; p; p=pNext){
|
||||
pNext = p->pNext;
|
||||
sqlite3DbFree(db, p);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Free any cursor components allocated by sqlite3VdbeSorterXXX routines.
|
||||
*/
|
||||
void sqlite3VdbeSorterClose(sqlite3 *db, VdbeCursor *pCsr){
|
||||
VdbeSorter *pSorter = pCsr->pSorter;
|
||||
if( pSorter ){
|
||||
if( pSorter->aIter ){
|
||||
int i;
|
||||
for(i=0; i<pSorter->nTree; i++){
|
||||
vdbeSorterIterZero(db, &pSorter->aIter[i]);
|
||||
}
|
||||
sqlite3DbFree(db, pSorter->aIter);
|
||||
}
|
||||
if( pSorter->pTemp1 ){
|
||||
sqlite3OsCloseFree(pSorter->pTemp1);
|
||||
}
|
||||
vdbeSorterRecordFree(db, pSorter->pRecord);
|
||||
sqlite3DbFree(db, pSorter->pUnpacked);
|
||||
sqlite3DbFree(db, pSorter);
|
||||
pCsr->pSorter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate space for a file-handle and open a temporary file. If successful,
|
||||
** set *ppFile to point to the malloc'd file-handle and return SQLITE_OK.
|
||||
** Otherwise, set *ppFile to 0 and return an SQLite error code.
|
||||
*/
|
||||
static int vdbeSorterOpenTempFile(sqlite3 *db, sqlite3_file **ppFile){
|
||||
int dummy;
|
||||
return sqlite3OsOpenMalloc(db->pVfs, 0, ppFile,
|
||||
SQLITE_OPEN_TEMP_JOURNAL |
|
||||
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
|
||||
SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE, &dummy
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
** Merge the two sorted lists p1 and p2 into a single list.
|
||||
** Set *ppOut to the head of the new list.
|
||||
*/
|
||||
static void vdbeSorterMerge(
|
||||
VdbeCursor *pCsr, /* For pKeyInfo */
|
||||
SorterRecord *p1, /* First list to merge */
|
||||
SorterRecord *p2, /* Second list to merge */
|
||||
SorterRecord **ppOut /* OUT: Head of merged list */
|
||||
){
|
||||
SorterRecord *pFinal = 0;
|
||||
SorterRecord **pp = &pFinal;
|
||||
void *pVal2 = p2 ? p2->pVal : 0;
|
||||
|
||||
while( p1 && p2 ){
|
||||
int res;
|
||||
vdbeSorterCompare(pCsr, 0, p1->pVal, p1->nVal, pVal2, p2->nVal, &res);
|
||||
if( res<=0 ){
|
||||
*pp = p1;
|
||||
pp = &p1->pNext;
|
||||
p1 = p1->pNext;
|
||||
pVal2 = 0;
|
||||
}else{
|
||||
*pp = p2;
|
||||
pp = &p2->pNext;
|
||||
p2 = p2->pNext;
|
||||
if( p2==0 ) break;
|
||||
pVal2 = p2->pVal;
|
||||
}
|
||||
}
|
||||
*pp = p1 ? p1 : p2;
|
||||
*ppOut = pFinal;
|
||||
}
|
||||
|
||||
/*
|
||||
** Sort the linked list of records headed at pCsr->pRecord. Return SQLITE_OK
|
||||
** if successful, or an SQLite error code (i.e. SQLITE_NOMEM) if an error
|
||||
** occurs.
|
||||
*/
|
||||
static int vdbeSorterSort(VdbeCursor *pCsr){
|
||||
int i;
|
||||
SorterRecord **aSlot;
|
||||
SorterRecord *p;
|
||||
VdbeSorter *pSorter = pCsr->pSorter;
|
||||
|
||||
aSlot = (SorterRecord **)sqlite3MallocZero(64 * sizeof(SorterRecord *));
|
||||
if( !aSlot ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
p = pSorter->pRecord;
|
||||
while( p ){
|
||||
SorterRecord *pNext = p->pNext;
|
||||
p->pNext = 0;
|
||||
for(i=0; aSlot[i]; i++){
|
||||
vdbeSorterMerge(pCsr, p, aSlot[i], &p);
|
||||
aSlot[i] = 0;
|
||||
}
|
||||
aSlot[i] = p;
|
||||
p = pNext;
|
||||
}
|
||||
|
||||
p = 0;
|
||||
for(i=0; i<64; i++){
|
||||
vdbeSorterMerge(pCsr, p, aSlot[i], &p);
|
||||
}
|
||||
pSorter->pRecord = p;
|
||||
|
||||
sqlite3_free(aSlot);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Write the current contents of the in-memory linked-list to a PMA. Return
|
||||
** SQLITE_OK if successful, or an SQLite error code otherwise.
|
||||
**
|
||||
** The format of a PMA is:
|
||||
**
|
||||
** * A varint. This varint contains the total number of bytes of content
|
||||
** in the PMA (not including the varint itself).
|
||||
**
|
||||
** * One or more records packed end-to-end in order of ascending keys.
|
||||
** Each record consists of a varint followed by a blob of data (the
|
||||
** key). The varint is the number of bytes in the blob of data.
|
||||
*/
|
||||
static int vdbeSorterListToPMA(sqlite3 *db, VdbeCursor *pCsr){
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
VdbeSorter *pSorter = pCsr->pSorter;
|
||||
|
||||
if( pSorter->nInMemory==0 ){
|
||||
assert( pSorter->pRecord==0 );
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = vdbeSorterSort(pCsr);
|
||||
|
||||
/* If the first temporary PMA file has not been opened, open it now. */
|
||||
if( rc==SQLITE_OK && pSorter->pTemp1==0 ){
|
||||
rc = vdbeSorterOpenTempFile(db, &pSorter->pTemp1);
|
||||
assert( rc!=SQLITE_OK || pSorter->pTemp1 );
|
||||
assert( pSorter->iWriteOff==0 );
|
||||
assert( pSorter->nPMA==0 );
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
i64 iOff = pSorter->iWriteOff;
|
||||
SorterRecord *p;
|
||||
SorterRecord *pNext = 0;
|
||||
static const char eightZeros[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
pSorter->nPMA++;
|
||||
rc = vdbeSorterWriteVarint(pSorter->pTemp1, pSorter->nInMemory, &iOff);
|
||||
for(p=pSorter->pRecord; rc==SQLITE_OK && p; p=pNext){
|
||||
pNext = p->pNext;
|
||||
rc = vdbeSorterWriteVarint(pSorter->pTemp1, p->nVal, &iOff);
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3OsWrite(pSorter->pTemp1, p->pVal, p->nVal, iOff);
|
||||
iOff += p->nVal;
|
||||
}
|
||||
|
||||
sqlite3DbFree(db, p);
|
||||
}
|
||||
|
||||
/* This assert verifies that unless an error has occurred, the size of
|
||||
** the PMA on disk is the same as the expected size stored in
|
||||
** pSorter->nInMemory. */
|
||||
assert( rc!=SQLITE_OK || pSorter->nInMemory==(
|
||||
iOff-pSorter->iWriteOff-sqlite3VarintLen(pSorter->nInMemory)
|
||||
));
|
||||
|
||||
pSorter->iWriteOff = iOff;
|
||||
if( rc==SQLITE_OK ){
|
||||
/* Terminate each file with 8 extra bytes so that from any offset
|
||||
** in the file we can always read 9 bytes without a SHORT_READ error */
|
||||
rc = sqlite3OsWrite(pSorter->pTemp1, eightZeros, 8, iOff);
|
||||
}
|
||||
pSorter->pRecord = p;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Add a record to the sorter.
|
||||
*/
|
||||
int sqlite3VdbeSorterWrite(
|
||||
sqlite3 *db, /* Database handle */
|
||||
VdbeCursor *pCsr, /* Sorter cursor */
|
||||
Mem *pVal /* Memory cell containing record */
|
||||
){
|
||||
VdbeSorter *pSorter = pCsr->pSorter;
|
||||
int rc = SQLITE_OK; /* Return Code */
|
||||
SorterRecord *pNew; /* New list element */
|
||||
|
||||
assert( pSorter );
|
||||
pSorter->nInMemory += sqlite3VarintLen(pVal->n) + pVal->n;
|
||||
|
||||
pNew = (SorterRecord *)sqlite3DbMallocRaw(db, pVal->n + sizeof(SorterRecord));
|
||||
if( pNew==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
pNew->pVal = (void *)&pNew[1];
|
||||
memcpy(pNew->pVal, pVal->z, pVal->n);
|
||||
pNew->nVal = pVal->n;
|
||||
pNew->pNext = pSorter->pRecord;
|
||||
pSorter->pRecord = pNew;
|
||||
}
|
||||
|
||||
/* See if the contents of the sorter should now be written out. They
|
||||
** are written out when either of the following are true:
|
||||
**
|
||||
** * The total memory allocated for the in-memory list is greater
|
||||
** than (page-size * cache-size), or
|
||||
**
|
||||
** * The total memory allocated for the in-memory list is greater
|
||||
** than (page-size * 10) and sqlite3HeapNearlyFull() returns true.
|
||||
*/
|
||||
if( rc==SQLITE_OK && pSorter->mxPmaSize>0 && (
|
||||
(pSorter->nInMemory>pSorter->mxPmaSize)
|
||||
|| (pSorter->nInMemory>pSorter->mnPmaSize && sqlite3HeapNearlyFull())
|
||||
)){
|
||||
rc = vdbeSorterListToPMA(db, pCsr);
|
||||
pSorter->nInMemory = 0;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Helper function for sqlite3VdbeSorterRewind().
|
||||
*/
|
||||
static int vdbeSorterInitMerge(
|
||||
sqlite3 *db, /* Database handle */
|
||||
VdbeCursor *pCsr, /* Cursor handle for this sorter */
|
||||
i64 *pnByte /* Sum of bytes in all opened PMAs */
|
||||
){
|
||||
VdbeSorter *pSorter = pCsr->pSorter;
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
int i; /* Used to iterator through aIter[] */
|
||||
i64 nByte = 0; /* Total bytes in all opened PMAs */
|
||||
|
||||
/* Initialize the iterators. */
|
||||
for(i=0; i<SORTER_MAX_MERGE_COUNT; i++){
|
||||
VdbeSorterIter *pIter = &pSorter->aIter[i];
|
||||
rc = vdbeSorterIterInit(db, pSorter, pSorter->iReadOff, pIter, &nByte);
|
||||
pSorter->iReadOff = pIter->iEof;
|
||||
assert( rc!=SQLITE_OK || pSorter->iReadOff<=pSorter->iWriteOff );
|
||||
if( rc!=SQLITE_OK || pSorter->iReadOff>=pSorter->iWriteOff ) break;
|
||||
}
|
||||
|
||||
/* Initialize the aTree[] array. */
|
||||
for(i=pSorter->nTree-1; rc==SQLITE_OK && i>0; i--){
|
||||
rc = vdbeSorterDoCompare(pCsr, i);
|
||||
}
|
||||
|
||||
*pnByte = nByte;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Once the sorter has been populated, this function is called to prepare
|
||||
** for iterating through its contents in sorted order.
|
||||
*/
|
||||
int sqlite3VdbeSorterRewind(sqlite3 *db, VdbeCursor *pCsr, int *pbEof){
|
||||
VdbeSorter *pSorter = pCsr->pSorter;
|
||||
int rc; /* Return code */
|
||||
sqlite3_file *pTemp2 = 0; /* Second temp file to use */
|
||||
i64 iWrite2 = 0; /* Write offset for pTemp2 */
|
||||
int nIter; /* Number of iterators used */
|
||||
int nByte; /* Bytes of space required for aIter/aTree */
|
||||
int N = 2; /* Power of 2 >= nIter */
|
||||
|
||||
assert( pSorter );
|
||||
|
||||
/* If no data has been written to disk, then do not do so now. Instead,
|
||||
** sort the VdbeSorter.pRecord list. The vdbe layer will read data directly
|
||||
** from the in-memory list. */
|
||||
if( pSorter->nPMA==0 ){
|
||||
*pbEof = !pSorter->pRecord;
|
||||
assert( pSorter->aTree==0 );
|
||||
return vdbeSorterSort(pCsr);
|
||||
}
|
||||
|
||||
/* Write the current b-tree to a PMA. Close the b-tree cursor. */
|
||||
rc = vdbeSorterListToPMA(db, pCsr);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
/* Allocate space for aIter[] and aTree[]. */
|
||||
nIter = pSorter->nPMA;
|
||||
if( nIter>SORTER_MAX_MERGE_COUNT ) nIter = SORTER_MAX_MERGE_COUNT;
|
||||
assert( nIter>0 );
|
||||
while( N<nIter ) N += N;
|
||||
nByte = N * (sizeof(int) + sizeof(VdbeSorterIter));
|
||||
pSorter->aIter = (VdbeSorterIter *)sqlite3DbMallocZero(db, nByte);
|
||||
if( !pSorter->aIter ) return SQLITE_NOMEM;
|
||||
pSorter->aTree = (int *)&pSorter->aIter[N];
|
||||
pSorter->nTree = N;
|
||||
|
||||
do {
|
||||
int iNew; /* Index of new, merged, PMA */
|
||||
|
||||
for(iNew=0;
|
||||
rc==SQLITE_OK && iNew*SORTER_MAX_MERGE_COUNT<pSorter->nPMA;
|
||||
iNew++
|
||||
){
|
||||
i64 nWrite; /* Number of bytes in new PMA */
|
||||
|
||||
/* If there are SORTER_MAX_MERGE_COUNT or less PMAs in file pTemp1,
|
||||
** initialize an iterator for each of them and break out of the loop.
|
||||
** These iterators will be incrementally merged as the VDBE layer calls
|
||||
** sqlite3VdbeSorterNext().
|
||||
**
|
||||
** Otherwise, if pTemp1 contains more than SORTER_MAX_MERGE_COUNT PMAs,
|
||||
** initialize interators for SORTER_MAX_MERGE_COUNT of them. These PMAs
|
||||
** are merged into a single PMA that is written to file pTemp2.
|
||||
*/
|
||||
rc = vdbeSorterInitMerge(db, pCsr, &nWrite);
|
||||
assert( rc!=SQLITE_OK || pSorter->aIter[ pSorter->aTree[1] ].pFile );
|
||||
if( rc!=SQLITE_OK || pSorter->nPMA<=SORTER_MAX_MERGE_COUNT ){
|
||||
break;
|
||||
}
|
||||
|
||||
/* Open the second temp file, if it is not already open. */
|
||||
if( pTemp2==0 ){
|
||||
assert( iWrite2==0 );
|
||||
rc = vdbeSorterOpenTempFile(db, &pTemp2);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = vdbeSorterWriteVarint(pTemp2, nWrite, &iWrite2);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
int bEof = 0;
|
||||
while( rc==SQLITE_OK && bEof==0 ){
|
||||
int nToWrite;
|
||||
VdbeSorterIter *pIter = &pSorter->aIter[ pSorter->aTree[1] ];
|
||||
assert( pIter->pFile );
|
||||
nToWrite = pIter->nKey + sqlite3VarintLen(pIter->nKey);
|
||||
rc = sqlite3OsWrite(pTemp2, pIter->aAlloc, nToWrite, iWrite2);
|
||||
iWrite2 += nToWrite;
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3VdbeSorterNext(db, pCsr, &bEof);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( pSorter->nPMA<=SORTER_MAX_MERGE_COUNT ){
|
||||
break;
|
||||
}else{
|
||||
sqlite3_file *pTmp = pSorter->pTemp1;
|
||||
pSorter->nPMA = iNew;
|
||||
pSorter->pTemp1 = pTemp2;
|
||||
pTemp2 = pTmp;
|
||||
pSorter->iWriteOff = iWrite2;
|
||||
pSorter->iReadOff = 0;
|
||||
iWrite2 = 0;
|
||||
}
|
||||
}while( rc==SQLITE_OK );
|
||||
|
||||
if( pTemp2 ){
|
||||
sqlite3OsCloseFree(pTemp2);
|
||||
}
|
||||
*pbEof = (pSorter->aIter[pSorter->aTree[1]].pFile==0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Advance to the next element in the sorter.
|
||||
*/
|
||||
int sqlite3VdbeSorterNext(sqlite3 *db, VdbeCursor *pCsr, int *pbEof){
|
||||
VdbeSorter *pSorter = pCsr->pSorter;
|
||||
int rc; /* Return code */
|
||||
|
||||
if( pSorter->aTree ){
|
||||
int iPrev = pSorter->aTree[1];/* Index of iterator to advance */
|
||||
int i; /* Index of aTree[] to recalculate */
|
||||
|
||||
rc = vdbeSorterIterNext(db, &pSorter->aIter[iPrev]);
|
||||
for(i=(pSorter->nTree+iPrev)/2; rc==SQLITE_OK && i>0; i=i/2){
|
||||
rc = vdbeSorterDoCompare(pCsr, i);
|
||||
}
|
||||
|
||||
*pbEof = (pSorter->aIter[pSorter->aTree[1]].pFile==0);
|
||||
}else{
|
||||
SorterRecord *pFree = pSorter->pRecord;
|
||||
pSorter->pRecord = pFree->pNext;
|
||||
pFree->pNext = 0;
|
||||
vdbeSorterRecordFree(db, pFree);
|
||||
*pbEof = !pSorter->pRecord;
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a pointer to a buffer owned by the sorter that contains the
|
||||
** current key.
|
||||
*/
|
||||
static void *vdbeSorterRowkey(
|
||||
VdbeSorter *pSorter, /* Sorter object */
|
||||
int *pnKey /* OUT: Size of current key in bytes */
|
||||
){
|
||||
void *pKey;
|
||||
if( pSorter->aTree ){
|
||||
VdbeSorterIter *pIter;
|
||||
pIter = &pSorter->aIter[ pSorter->aTree[1] ];
|
||||
*pnKey = pIter->nKey;
|
||||
pKey = pIter->aKey;
|
||||
}else{
|
||||
*pnKey = pSorter->pRecord->nVal;
|
||||
pKey = pSorter->pRecord->pVal;
|
||||
}
|
||||
return pKey;
|
||||
}
|
||||
|
||||
/*
|
||||
** Copy the current sorter key into the memory cell pOut.
|
||||
*/
|
||||
int sqlite3VdbeSorterRowkey(VdbeCursor *pCsr, Mem *pOut){
|
||||
VdbeSorter *pSorter = pCsr->pSorter;
|
||||
void *pKey; int nKey; /* Sorter key to copy into pOut */
|
||||
|
||||
pKey = vdbeSorterRowkey(pSorter, &nKey);
|
||||
if( sqlite3VdbeMemGrow(pOut, nKey, 0) ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
pOut->n = nKey;
|
||||
MemSetTypeFlag(pOut, MEM_Blob);
|
||||
memcpy(pOut->z, pKey, nKey);
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Compare the key in memory cell pVal with the key that the sorter cursor
|
||||
** passed as the first argument currently points to. For the purposes of
|
||||
** the comparison, ignore the rowid field at the end of each record.
|
||||
**
|
||||
** If an error occurs, return an SQLite error code (i.e. SQLITE_NOMEM).
|
||||
** Otherwise, set *pRes to a negative, zero or positive value if the
|
||||
** key in pVal is smaller than, equal to or larger than the current sorter
|
||||
** key.
|
||||
*/
|
||||
int sqlite3VdbeSorterCompare(
|
||||
VdbeCursor *pCsr, /* Sorter cursor */
|
||||
Mem *pVal, /* Value to compare to current sorter key */
|
||||
int *pRes /* OUT: Result of comparison */
|
||||
){
|
||||
VdbeSorter *pSorter = pCsr->pSorter;
|
||||
void *pKey; int nKey; /* Sorter key to compare pVal with */
|
||||
|
||||
pKey = vdbeSorterRowkey(pSorter, &nKey);
|
||||
vdbeSorterCompare(pCsr, 1, pVal->z, pVal->n, pKey, nKey, pRes);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#endif /* #ifndef SQLITE_OMIT_MERGE_SORT */
|
||||
@ -891,7 +891,7 @@ int sqlite3VtabSavepoint(sqlite3 *db, int op, int iSavepoint){
|
||||
for(i=0; rc==SQLITE_OK && i<db->nVTrans; i++){
|
||||
VTable *pVTab = db->aVTrans[i];
|
||||
const sqlite3_module *pMod = pVTab->pMod->pModule;
|
||||
if( pMod->iVersion>=2 ){
|
||||
if( pVTab->pVtab && pMod->iVersion>=2 ){
|
||||
int (*xMethod)(sqlite3_vtab *, int);
|
||||
switch( op ){
|
||||
case SAVEPOINT_BEGIN:
|
||||
@ -906,7 +906,7 @@ int sqlite3VtabSavepoint(sqlite3 *db, int op, int iSavepoint){
|
||||
break;
|
||||
}
|
||||
if( xMethod && pVTab->iSavepoint>iSavepoint ){
|
||||
rc = xMethod(db->aVTrans[i]->pVtab, iSavepoint);
|
||||
rc = xMethod(pVTab->pVtab, iSavepoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1804,13 +1804,15 @@ int sqlite3WalClose(
|
||||
*/
|
||||
rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE);
|
||||
if( rc==SQLITE_OK ){
|
||||
int bPersistWal = -1;
|
||||
if( pWal->exclusiveMode==WAL_NORMAL_MODE ){
|
||||
pWal->exclusiveMode = WAL_EXCLUSIVE_MODE;
|
||||
}
|
||||
rc = sqlite3WalCheckpoint(
|
||||
pWal, SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_PERSIST_WAL, &bPersistWal);
|
||||
if( rc==SQLITE_OK && bPersistWal!=1 ){
|
||||
isDelete = 1;
|
||||
}
|
||||
}
|
||||
@ -2341,7 +2343,7 @@ int sqlite3WalRead(
|
||||
int sz;
|
||||
i64 iOffset;
|
||||
sz = pWal->hdr.szPage;
|
||||
sz = (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16);
|
||||
sz = (sz&0xfe00) + ((sz&0x0001)<<16);
|
||||
testcase( sz<=32768 );
|
||||
testcase( sz>=65536 );
|
||||
iOffset = walFrameOffset(iRead, sz) + WAL_FRAME_HDRSIZE;
|
||||
|
||||
773
src/where.c
773
src/where.c
File diff suppressed because it is too large
Load Diff
@ -67,8 +67,8 @@ do_test 8_3_names-2.1 {
|
||||
file exists test.nal
|
||||
} 1
|
||||
forcedelete test2.db test2.nal test2.db-journal
|
||||
file copy test.db test2.db
|
||||
file copy test.nal test2.nal
|
||||
copy_file test.db test2.db
|
||||
copy_file test.nal test2.nal
|
||||
do_test 8_3_names-2.2 {
|
||||
db eval {
|
||||
COMMIT;
|
||||
@ -101,8 +101,8 @@ do_test 8_3_names-3.1 {
|
||||
file exists test.nal
|
||||
} 0
|
||||
forcedelete test2.db test2.nal test2.db-journal
|
||||
file copy test.db test2.db
|
||||
file copy test.db-journal test2.db-journal
|
||||
copy_file test.db test2.db
|
||||
copy_file test.db-journal test2.db-journal
|
||||
do_test 8_3_names-3.2 {
|
||||
db eval {
|
||||
COMMIT;
|
||||
|
||||
@ -38,6 +38,7 @@ run_test_suite pcache10
|
||||
run_test_suite pcache50
|
||||
run_test_suite pcache90
|
||||
run_test_suite pcache100
|
||||
run_test_suite prepare
|
||||
|
||||
if {$::tcl_platform(platform)=="unix"} {
|
||||
ifcapable !default_autovacuum {
|
||||
|
||||
@ -221,8 +221,8 @@ do_test alter-1.7 {
|
||||
#
|
||||
ifcapable attach {
|
||||
do_test alter-1.8.1 {
|
||||
file delete -force test2.db
|
||||
file delete -force test2.db-journal
|
||||
forcedelete test2.db
|
||||
forcedelete test2.db-journal
|
||||
execsql {
|
||||
ATTACH 'test2.db' AS aux;
|
||||
}
|
||||
@ -412,8 +412,8 @@ do_test alter-3.1.8 {
|
||||
|
||||
# Make sure "ON" cannot be used as a database, table or column name without
|
||||
# quoting. Otherwise the sqlite_alter_trigger() function might not work.
|
||||
file delete -force test3.db
|
||||
file delete -force test3.db-journal
|
||||
forcedelete test3.db
|
||||
forcedelete test3.db-journal
|
||||
ifcapable attach {
|
||||
do_test alter-3.2.1 {
|
||||
catchsql {
|
||||
@ -846,7 +846,7 @@ do_test alter-14.2 {
|
||||
set system_table_list {1 sqlite_master}
|
||||
catchsql ANALYZE
|
||||
ifcapable analyze { lappend system_table_list 2 sqlite_stat1 }
|
||||
ifcapable stat2 { lappend system_table_list 3 sqlite_stat2 }
|
||||
ifcapable stat3 { lappend system_table_list 4 sqlite_stat3 }
|
||||
|
||||
foreach {tn tbl} $system_table_list {
|
||||
do_test alter-15.$tn.1 {
|
||||
|
||||
@ -137,6 +137,7 @@ do_test alter2-1.8 {
|
||||
do_test alter2-1.9 {
|
||||
# ALTER TABLE abc ADD COLUMN d;
|
||||
alter_table abc {CREATE TABLE abc(a, b, c, d);}
|
||||
if {[permutation] == "prepare"} { db cache flush }
|
||||
execsql { SELECT * FROM abc; }
|
||||
execsql {
|
||||
UPDATE abc SET d = 11 WHERE c IS NULL AND a<4;
|
||||
@ -314,8 +315,8 @@ do_test alter2-6.1 {
|
||||
} {2}
|
||||
ifcapable attach {
|
||||
do_test alter2-6.2 {
|
||||
file delete -force test2.db-journal
|
||||
file delete -force test2.db
|
||||
forcedelete test2.db-journal
|
||||
forcedelete test2.db
|
||||
execsql {
|
||||
ATTACH 'test2.db' AS aux;
|
||||
CREATE TABLE aux.t1(a, b);
|
||||
|
||||
@ -196,7 +196,7 @@ ifcapable schema_version {
|
||||
|
||||
do_test alter3-4.1 {
|
||||
db close
|
||||
file delete -force test.db
|
||||
forcedelete test.db
|
||||
set ::DB [sqlite3 db test.db]
|
||||
execsql {
|
||||
PRAGMA legacy_file_format=ON;
|
||||
@ -237,8 +237,8 @@ do_test alter3-4.99 {
|
||||
|
||||
ifcapable attach {
|
||||
do_test alter3-5.1 {
|
||||
file delete -force test2.db
|
||||
file delete -force test2.db-journal
|
||||
forcedelete test2.db
|
||||
forcedelete test2.db-journal
|
||||
execsql {
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, 'one');
|
||||
|
||||
@ -178,7 +178,7 @@ ifcapable schema_version {
|
||||
|
||||
do_test alter4-4.1 {
|
||||
db close
|
||||
file delete -force test.db
|
||||
forcedelete test.db
|
||||
set ::DB [sqlite3 db test.db]
|
||||
execsql {
|
||||
CREATE TEMP TABLE t1(a, b);
|
||||
@ -213,8 +213,8 @@ do_test alter4-4.99 {
|
||||
|
||||
ifcapable attach {
|
||||
do_test alter4-5.1 {
|
||||
file delete -force test2.db
|
||||
file delete -force test2.db-journal
|
||||
forcedelete test2.db
|
||||
forcedelete test2.db-journal
|
||||
execsql {
|
||||
CREATE TEMP TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, 'one');
|
||||
|
||||
@ -287,6 +287,64 @@ do_test analyze-4.3 {
|
||||
}
|
||||
} {}
|
||||
|
||||
# Verify that DROP TABLE and DROP INDEX remove entries from the
|
||||
# sqlite_stat1 and sqlite_stat3 tables.
|
||||
#
|
||||
do_test analyze-5.0 {
|
||||
execsql {
|
||||
DELETE FROM t3;
|
||||
DELETE FROM t4;
|
||||
INSERT INTO t3 VALUES(1,2,3,4);
|
||||
INSERT INTO t3 VALUES(5,6,7,8);
|
||||
INSERT INTO t3 SELECT a+8, b+8, c+8, d+8 FROM t3;
|
||||
INSERT INTO t3 SELECT a+16, b+16, c+16, d+16 FROM t3;
|
||||
INSERT INTO t3 SELECT a+32, b+32, c+32, d+32 FROM t3;
|
||||
INSERT INTO t3 SELECT a+64, b+64, c+64, d+64 FROM t3;
|
||||
INSERT INTO t4 SELECT a, b, c FROM t3;
|
||||
ANALYZE;
|
||||
SELECT DISTINCT idx FROM sqlite_stat1 ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM sqlite_stat1 ORDER BY 1;
|
||||
}
|
||||
} {t3i1 t3i2 t3i3 t4i1 t4i2 t3 t4}
|
||||
ifcapable stat3 {
|
||||
do_test analyze-5.1 {
|
||||
execsql {
|
||||
SELECT DISTINCT idx FROM sqlite_stat3 ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM sqlite_stat3 ORDER BY 1;
|
||||
}
|
||||
} {t3i1 t3i2 t3i3 t4i1 t4i2 t3 t4}
|
||||
}
|
||||
do_test analyze-5.2 {
|
||||
execsql {
|
||||
DROP INDEX t3i2;
|
||||
SELECT DISTINCT idx FROM sqlite_stat1 ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM sqlite_stat1 ORDER BY 1;
|
||||
}
|
||||
} {t3i1 t3i3 t4i1 t4i2 t3 t4}
|
||||
ifcapable stat3 {
|
||||
do_test analyze-5.3 {
|
||||
execsql {
|
||||
SELECT DISTINCT idx FROM sqlite_stat3 ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM sqlite_stat3 ORDER BY 1;
|
||||
}
|
||||
} {t3i1 t3i3 t4i1 t4i2 t3 t4}
|
||||
}
|
||||
do_test analyze-5.4 {
|
||||
execsql {
|
||||
DROP TABLE t3;
|
||||
SELECT DISTINCT idx FROM sqlite_stat1 ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM sqlite_stat1 ORDER BY 1;
|
||||
}
|
||||
} {t4i1 t4i2 t4}
|
||||
ifcapable stat3 {
|
||||
do_test analyze-5.5 {
|
||||
execsql {
|
||||
SELECT DISTINCT idx FROM sqlite_stat3 ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM sqlite_stat3 ORDER BY 1;
|
||||
}
|
||||
} {t4i1 t4i2 t4}
|
||||
}
|
||||
|
||||
# This test corrupts the database file so it must be the last test
|
||||
# in the series.
|
||||
#
|
||||
|
||||
@ -1,554 +0,0 @@
|
||||
# 2009 August 06
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file implements regression tests for SQLite library. This file
|
||||
# implements tests for the extra functionality provided by the ANALYZE
|
||||
# command when the library is compiled with SQLITE_ENABLE_STAT2 defined.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !stat2 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
set testprefix analyze2
|
||||
|
||||
# Do not use a codec for tests in this file, as the database file is
|
||||
# manipulated directly using tcl scripts (using the [hexio_write] command).
|
||||
#
|
||||
do_not_use_codec
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Test organization:
|
||||
#
|
||||
# analyze2-1.*: Tests to verify that ANALYZE creates and populates the
|
||||
# sqlite_stat2 table as expected.
|
||||
#
|
||||
# analyze2-2.*: Test that when a table has two indexes on it and either
|
||||
# index may be used for the scan, the index suggested by
|
||||
# the contents of sqlite_stat2 table is prefered.
|
||||
#
|
||||
# analyze2-3.*: Similar to the previous block of tests, but using tables
|
||||
# that contain a mixture of NULL, numeric, text and blob
|
||||
# values.
|
||||
#
|
||||
# analyze2-4.*: Check that when an indexed column uses a collation other
|
||||
# than BINARY, the collation is taken into account when
|
||||
# using the contents of sqlite_stat2 to estimate the cost
|
||||
# of a range scan.
|
||||
#
|
||||
# analyze2-5.*: Check that collation sequences are used as described above
|
||||
# even when the only available version of the collation
|
||||
# function require UTF-16 encoded arguments.
|
||||
#
|
||||
# analyze2-6.*: Check that the library behaves correctly when one of the
|
||||
# sqlite_stat2 or sqlite_stat1 tables are missing.
|
||||
#
|
||||
# analyze2-7.*: Check that in a shared-schema situation, nothing goes
|
||||
# wrong if sqlite_stat2 data is read by one connection,
|
||||
# and freed by another.
|
||||
#
|
||||
|
||||
proc eqp {sql {db db}} {
|
||||
uplevel execsql [list "EXPLAIN QUERY PLAN $sql"] $db
|
||||
}
|
||||
|
||||
do_test analyze2-1.1 {
|
||||
execsql { CREATE TABLE t1(x PRIMARY KEY) }
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
execsql { INSERT INTO t1 VALUES($i) }
|
||||
}
|
||||
execsql {
|
||||
ANALYZE;
|
||||
SELECT * FROM sqlite_stat2;
|
||||
}
|
||||
} [list t1 sqlite_autoindex_t1_1 0 50 \
|
||||
t1 sqlite_autoindex_t1_1 1 149 \
|
||||
t1 sqlite_autoindex_t1_1 2 249 \
|
||||
t1 sqlite_autoindex_t1_1 3 349 \
|
||||
t1 sqlite_autoindex_t1_1 4 449 \
|
||||
t1 sqlite_autoindex_t1_1 5 549 \
|
||||
t1 sqlite_autoindex_t1_1 6 649 \
|
||||
t1 sqlite_autoindex_t1_1 7 749 \
|
||||
t1 sqlite_autoindex_t1_1 8 849 \
|
||||
t1 sqlite_autoindex_t1_1 9 949 \
|
||||
]
|
||||
|
||||
do_test analyze2-1.2 {
|
||||
execsql {
|
||||
DELETE FROM t1 WHERe x>9;
|
||||
ANALYZE;
|
||||
SELECT tbl, idx, group_concat(sample, ' ') FROM sqlite_stat2;
|
||||
}
|
||||
} {t1 sqlite_autoindex_t1_1 {0 1 2 3 4 5 6 7 8 9}}
|
||||
do_test analyze2-1.3 {
|
||||
execsql {
|
||||
DELETE FROM t1 WHERE x>8;
|
||||
ANALYZE;
|
||||
SELECT * FROM sqlite_stat2;
|
||||
}
|
||||
} {}
|
||||
do_test analyze2-1.4 {
|
||||
execsql {
|
||||
DELETE FROM t1;
|
||||
ANALYZE;
|
||||
SELECT * FROM sqlite_stat2;
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test analyze2-2.1 {
|
||||
execsql {
|
||||
BEGIN;
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(x, y);
|
||||
CREATE INDEX t1_x ON t1(x);
|
||||
CREATE INDEX t1_y ON t1(y);
|
||||
}
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
execsql { INSERT INTO t1 VALUES($i, $i) }
|
||||
}
|
||||
execsql COMMIT
|
||||
execsql ANALYZE
|
||||
} {}
|
||||
do_eqp_test 2.2 {
|
||||
SELECT * FROM t1 WHERE x>500 AND y>700
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>?) (~100 rows)}
|
||||
}
|
||||
do_eqp_test 2.3 {
|
||||
SELECT * FROM t1 WHERE x>700 AND y>500
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_x (x>?) (~100 rows)}
|
||||
}
|
||||
do_eqp_test 2.3 {
|
||||
SELECT * FROM t1 WHERE y>700 AND x>500
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>?) (~100 rows)}
|
||||
}
|
||||
do_eqp_test 2.4 {
|
||||
SELECT * FROM t1 WHERE y>500 AND x>700
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_x (x>?) (~100 rows)}
|
||||
}
|
||||
do_eqp_test 2.5 {
|
||||
SELECT * FROM t1 WHERE x BETWEEN 100 AND 200 AND y BETWEEN 400 AND 700
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_x (x>? AND x<?) (~25 rows)}
|
||||
}
|
||||
do_eqp_test 2.6 {
|
||||
SELECT * FROM t1 WHERE x BETWEEN 100 AND 500 AND y BETWEEN 400 AND 700
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>? AND y<?) (~75 rows)}
|
||||
}
|
||||
do_eqp_test 2.7 {
|
||||
SELECT * FROM t1 WHERE x BETWEEN -400 AND -300 AND y BETWEEN 100 AND 300
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_x (x>? AND x<?) (~12 rows)}
|
||||
}
|
||||
do_eqp_test 2.8 {
|
||||
SELECT * FROM t1 WHERE x BETWEEN 100 AND 300 AND y BETWEEN -400 AND -300
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>? AND y<?) (~12 rows)}
|
||||
}
|
||||
do_eqp_test 2.9 {
|
||||
SELECT * FROM t1 WHERE x BETWEEN 500 AND 100 AND y BETWEEN 100 AND 300
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_x (x>? AND x<?) (~12 rows)}
|
||||
}
|
||||
do_eqp_test 2.10 {
|
||||
SELECT * FROM t1 WHERE x BETWEEN 100 AND 300 AND y BETWEEN 500 AND 100
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>? AND y<?) (~12 rows)}
|
||||
}
|
||||
|
||||
do_test analyze2-3.1 {
|
||||
set alphabet [list a b c d e f g h i j]
|
||||
execsql BEGIN
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
set str [lindex $alphabet [expr ($i/100)%10]]
|
||||
append str [lindex $alphabet [expr ($i/ 10)%10]]
|
||||
append str [lindex $alphabet [expr ($i/ 1)%10]]
|
||||
execsql { INSERT INTO t1 VALUES($str, $str) }
|
||||
}
|
||||
execsql COMMIT
|
||||
execsql ANALYZE
|
||||
execsql {
|
||||
SELECT tbl,idx,group_concat(sample,' ')
|
||||
FROM sqlite_stat2
|
||||
WHERE idx = 't1_x'
|
||||
GROUP BY tbl,idx
|
||||
}
|
||||
} {t1 t1_x {100 299 499 699 899 ajj cjj ejj gjj ijj}}
|
||||
do_test analyze2-3.2 {
|
||||
execsql {
|
||||
SELECT tbl,idx,group_concat(sample,' ')
|
||||
FROM sqlite_stat2
|
||||
WHERE idx = 't1_y'
|
||||
GROUP BY tbl,idx
|
||||
}
|
||||
} {t1 t1_y {100 299 499 699 899 ajj cjj ejj gjj ijj}}
|
||||
|
||||
do_eqp_test 3.3 {
|
||||
SELECT * FROM t1 WHERE x BETWEEN 100 AND 500 AND y BETWEEN 'a' AND 'b'
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>? AND y<?) (~50 rows)}
|
||||
}
|
||||
do_eqp_test 3.4 {
|
||||
SELECT * FROM t1 WHERE x BETWEEN 100 AND 400 AND y BETWEEN 'a' AND 'h'
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_x (x>? AND x<?) (~100 rows)}
|
||||
}
|
||||
do_eqp_test 3.5 {
|
||||
SELECT * FROM t1 WHERE x<'a' AND y>'h'
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>?) (~66 rows)}
|
||||
}
|
||||
do_eqp_test 3.6 {
|
||||
SELECT * FROM t1 WHERE x<444 AND y>'h'
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>?) (~66 rows)}
|
||||
}
|
||||
do_eqp_test 3.7 {
|
||||
SELECT * FROM t1 WHERE x<221 AND y>'g'
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_x (x<?) (~66 rows)}
|
||||
}
|
||||
|
||||
do_test analyze2-4.1 {
|
||||
execsql { CREATE TABLE t3(a COLLATE nocase, b) }
|
||||
execsql { CREATE INDEX t3a ON t3(a) }
|
||||
execsql { CREATE INDEX t3b ON t3(b) }
|
||||
set alphabet [list A b C d E f G h I j]
|
||||
execsql BEGIN
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
set str [lindex $alphabet [expr ($i/100)%10]]
|
||||
append str [lindex $alphabet [expr ($i/ 10)%10]]
|
||||
append str [lindex $alphabet [expr ($i/ 1)%10]]
|
||||
execsql { INSERT INTO t3 VALUES($str, $str) }
|
||||
}
|
||||
execsql COMMIT
|
||||
execsql ANALYZE
|
||||
} {}
|
||||
do_test analyze2-4.2 {
|
||||
execsql {
|
||||
PRAGMA automatic_index=OFF;
|
||||
SELECT tbl,idx,group_concat(sample,' ')
|
||||
FROM sqlite_stat2
|
||||
WHERE idx = 't3a'
|
||||
GROUP BY tbl,idx;
|
||||
PRAGMA automatic_index=ON;
|
||||
}
|
||||
} {t3 t3a {AfA bEj CEj dEj EEj fEj GEj hEj IEj jEj}}
|
||||
do_test analyze2-4.3 {
|
||||
execsql {
|
||||
SELECT tbl,idx,group_concat(sample,' ')
|
||||
FROM sqlite_stat2
|
||||
WHERE idx = 't3b'
|
||||
GROUP BY tbl,idx
|
||||
}
|
||||
} {t3 t3b {AbA CIj EIj GIj IIj bIj dIj fIj hIj jIj}}
|
||||
|
||||
do_eqp_test 4.4 {
|
||||
SELECT * FROM t3 WHERE a > 'A' AND a < 'C' AND b > 'A' AND b < 'C'
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t3 USING INDEX t3b (b>? AND b<?) (~11 rows)}
|
||||
}
|
||||
do_eqp_test 4.5 {
|
||||
SELECT * FROM t3 WHERE a > 'A' AND a < 'c' AND b > 'A' AND b < 'c'
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t3 USING INDEX t3a (a>? AND a<?) (~22 rows)}
|
||||
}
|
||||
|
||||
ifcapable utf16 {
|
||||
proc test_collate {enc lhs rhs} {
|
||||
# puts $enc
|
||||
return [string compare $lhs $rhs]
|
||||
}
|
||||
do_test analyze2-5.1 {
|
||||
add_test_collate db 0 0 1
|
||||
execsql { CREATE TABLE t4(x COLLATE test_collate) }
|
||||
execsql { CREATE INDEX t4x ON t4(x) }
|
||||
set alphabet [list a b c d e f g h i j]
|
||||
execsql BEGIN
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
set str [lindex $alphabet [expr ($i/100)%10]]
|
||||
append str [lindex $alphabet [expr ($i/ 10)%10]]
|
||||
append str [lindex $alphabet [expr ($i/ 1)%10]]
|
||||
execsql { INSERT INTO t4 VALUES($str) }
|
||||
}
|
||||
execsql COMMIT
|
||||
execsql ANALYZE
|
||||
} {}
|
||||
do_test analyze2-5.2 {
|
||||
execsql {
|
||||
SELECT tbl,idx,group_concat(sample,' ')
|
||||
FROM sqlite_stat2
|
||||
WHERE tbl = 't4'
|
||||
GROUP BY tbl,idx
|
||||
}
|
||||
} {t4 t4x {afa bej cej dej eej fej gej hej iej jej}}
|
||||
do_eqp_test 5.3 {
|
||||
SELECT * FROM t4 WHERE x>'ccc'
|
||||
} {0 0 0 {SEARCH TABLE t4 USING COVERING INDEX t4x (x>?) (~800 rows)}}
|
||||
do_eqp_test 5.4 {
|
||||
SELECT * FROM t4 AS t41, t4 AS t42 WHERE t41.x>'ccc' AND t42.x>'ggg'
|
||||
} {
|
||||
0 0 1 {SEARCH TABLE t4 AS t42 USING COVERING INDEX t4x (x>?) (~300 rows)}
|
||||
0 1 0 {SEARCH TABLE t4 AS t41 USING COVERING INDEX t4x (x>?) (~800 rows)}
|
||||
}
|
||||
do_eqp_test 5.5 {
|
||||
SELECT * FROM t4 AS t41, t4 AS t42 WHERE t41.x>'ddd' AND t42.x>'ccc'
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t4 AS t41 USING COVERING INDEX t4x (x>?) (~700 rows)}
|
||||
0 1 1 {SEARCH TABLE t4 AS t42 USING COVERING INDEX t4x (x>?) (~800 rows)}
|
||||
}
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# These tests, analyze2-6.*, verify that the library behaves correctly
|
||||
# when one of the sqlite_stat1 and sqlite_stat2 tables is missing.
|
||||
#
|
||||
# If the sqlite_stat1 table is not present, then the sqlite_stat2
|
||||
# table is not read. However, if it is the sqlite_stat2 table that
|
||||
# is missing, the data in the sqlite_stat1 table is still used.
|
||||
#
|
||||
# Tests analyze2-6.1.* test the libary when the sqlite_stat2 table
|
||||
# is missing. Tests analyze2-6.2.* test the library when sqlite_stat1
|
||||
# is not present.
|
||||
#
|
||||
do_test analyze2-6.0 {
|
||||
execsql {
|
||||
DROP TABLE IF EXISTS t4;
|
||||
CREATE TABLE t5(a, b); CREATE INDEX t5i ON t5(a, b);
|
||||
CREATE TABLE t6(a, b); CREATE INDEX t6i ON t6(a, b);
|
||||
}
|
||||
for {set ii 0} {$ii < 20} {incr ii} {
|
||||
execsql {
|
||||
INSERT INTO t5 VALUES($ii, $ii);
|
||||
INSERT INTO t6 VALUES($ii/10, $ii/10);
|
||||
}
|
||||
}
|
||||
execsql {
|
||||
CREATE TABLE master AS
|
||||
SELECT * FROM sqlite_master WHERE name LIKE 'sqlite_stat%'
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test analyze2-6.1.1 {
|
||||
eqp {SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND
|
||||
t5.a = 1 AND
|
||||
t6.a = 1 AND t6.b = 1
|
||||
}
|
||||
} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a=? AND b=?) (~9 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
do_test analyze2-6.1.2 {
|
||||
db cache flush
|
||||
execsql ANALYZE
|
||||
eqp {SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND
|
||||
t5.a = 1 AND
|
||||
t6.a = 1 AND t6.b = 1
|
||||
}
|
||||
} {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a=?) (~1 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
do_test analyze2-6.1.3 {
|
||||
sqlite3 db test.db
|
||||
eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND
|
||||
t5.a = 1 AND
|
||||
t6.a = 1 AND t6.b = 1
|
||||
}
|
||||
} {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a=?) (~1 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
do_test analyze2-6.1.4 {
|
||||
execsql {
|
||||
PRAGMA writable_schema = 1;
|
||||
DELETE FROM sqlite_master WHERE tbl_name = 'sqlite_stat2';
|
||||
}
|
||||
sqlite3 db test.db
|
||||
eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND
|
||||
t5.a = 1 AND
|
||||
t6.a = 1 AND t6.b = 1
|
||||
}
|
||||
} {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a=?) (~1 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
do_test analyze2-6.1.5 {
|
||||
execsql {
|
||||
PRAGMA writable_schema = 1;
|
||||
DELETE FROM sqlite_master WHERE tbl_name = 'sqlite_stat1';
|
||||
}
|
||||
sqlite3 db test.db
|
||||
eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND
|
||||
t5.a = 1 AND
|
||||
t6.a = 1 AND t6.b = 1
|
||||
}
|
||||
} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a=? AND b=?) (~9 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
do_test analyze2-6.1.6 {
|
||||
execsql {
|
||||
PRAGMA writable_schema = 1;
|
||||
INSERT INTO sqlite_master SELECT * FROM master;
|
||||
}
|
||||
sqlite3 db test.db
|
||||
eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND
|
||||
t5.a = 1 AND
|
||||
t6.a = 1 AND t6.b = 1
|
||||
}
|
||||
} {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a=?) (~1 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
|
||||
do_test analyze2-6.2.1 {
|
||||
execsql {
|
||||
DELETE FROM sqlite_stat1;
|
||||
DELETE FROM sqlite_stat2;
|
||||
}
|
||||
sqlite3 db test.db
|
||||
eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND
|
||||
t5.a>1 AND t5.a<15 AND
|
||||
t6.a>1
|
||||
}
|
||||
} {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a>? AND a<?) (~60000 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
do_test analyze2-6.2.2 {
|
||||
db cache flush
|
||||
execsql ANALYZE
|
||||
eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND
|
||||
t5.a>1 AND t5.a<15 AND
|
||||
t6.a>1
|
||||
}
|
||||
} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
do_test analyze2-6.2.3 {
|
||||
sqlite3 db test.db
|
||||
eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND
|
||||
t5.a>1 AND t5.a<15 AND
|
||||
t6.a>1
|
||||
}
|
||||
} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
do_test analyze2-6.2.4 {
|
||||
execsql {
|
||||
PRAGMA writable_schema = 1;
|
||||
DELETE FROM sqlite_master WHERE tbl_name = 'sqlite_stat1';
|
||||
}
|
||||
sqlite3 db test.db
|
||||
eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND
|
||||
t5.a>1 AND t5.a<15 AND
|
||||
t6.a>1
|
||||
}
|
||||
} {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a>? AND a<?) (~60000 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
do_test analyze2-6.2.5 {
|
||||
execsql {
|
||||
PRAGMA writable_schema = 1;
|
||||
DELETE FROM sqlite_master WHERE tbl_name = 'sqlite_stat2';
|
||||
}
|
||||
sqlite3 db test.db
|
||||
eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND
|
||||
t5.a>1 AND t5.a<15 AND
|
||||
t6.a>1
|
||||
}
|
||||
} {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a>? AND a<?) (~60000 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
do_test analyze2-6.2.6 {
|
||||
execsql {
|
||||
PRAGMA writable_schema = 1;
|
||||
INSERT INTO sqlite_master SELECT * FROM master;
|
||||
}
|
||||
sqlite3 db test.db
|
||||
execsql ANALYZE
|
||||
eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND
|
||||
t5.a>1 AND t5.a<15 AND
|
||||
t6.a>1
|
||||
}
|
||||
} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# These tests, analyze2-7.*, test that the sqlite_stat2 functionality
|
||||
# works in shared-cache mode. Note that these tests reuse the database
|
||||
# created for the analyze2-6.* tests.
|
||||
#
|
||||
ifcapable shared_cache {
|
||||
db close
|
||||
set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
|
||||
|
||||
proc incr_schema_cookie {zDb} {
|
||||
foreach iOffset {24 40} {
|
||||
set cookie [hexio_get_int [hexio_read $zDb $iOffset 4]]
|
||||
incr cookie
|
||||
hexio_write $zDb $iOffset [hexio_render_int32 $cookie]
|
||||
}
|
||||
}
|
||||
|
||||
do_test analyze2-7.1 {
|
||||
sqlite3 db1 test.db
|
||||
sqlite3 db2 test.db
|
||||
db1 cache size 0
|
||||
db2 cache size 0
|
||||
execsql { SELECT count(*) FROM t5 } db1
|
||||
} {20}
|
||||
do_test analyze2-7.2 {
|
||||
incr_schema_cookie test.db
|
||||
execsql { SELECT count(*) FROM t5 } db2
|
||||
} {20}
|
||||
do_test analyze2-7.3 {
|
||||
incr_schema_cookie test.db
|
||||
execsql { SELECT count(*) FROM t5 } db1
|
||||
} {20}
|
||||
do_test analyze2-7.4 {
|
||||
incr_schema_cookie test.db
|
||||
execsql { SELECT count(*) FROM t5 } db2
|
||||
} {20}
|
||||
|
||||
do_test analyze2-7.5 {
|
||||
eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND
|
||||
t5.a>1 AND t5.a<15 AND
|
||||
t6.a>1
|
||||
} db1
|
||||
} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
do_test analyze2-7.6 {
|
||||
incr_schema_cookie test.db
|
||||
execsql { SELECT * FROM sqlite_master } db2
|
||||
eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND
|
||||
t5.a>1 AND t5.a<15 AND
|
||||
t6.a>1
|
||||
} db2
|
||||
} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
do_test analyze2-7.7 {
|
||||
incr_schema_cookie test.db
|
||||
execsql { SELECT * FROM sqlite_master } db1
|
||||
eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND
|
||||
t5.a>1 AND t5.a<15 AND
|
||||
t6.a>1
|
||||
} db1
|
||||
} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
|
||||
do_test analyze2-7.8 {
|
||||
execsql { DELETE FROM sqlite_stat2 } db2
|
||||
execsql { SELECT * FROM sqlite_master } db1
|
||||
eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND
|
||||
t5.a>1 AND t5.a<15 AND
|
||||
t6.a>1
|
||||
} db1
|
||||
} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
do_test analyze2-7.9 {
|
||||
execsql { SELECT * FROM sqlite_master } db2
|
||||
eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND
|
||||
t5.a>1 AND t5.a<15 AND
|
||||
t6.a>1
|
||||
} db2
|
||||
} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~1 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
|
||||
do_test analyze2-7.10 {
|
||||
incr_schema_cookie test.db
|
||||
execsql { SELECT * FROM sqlite_master } db1
|
||||
eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND
|
||||
t5.a>1 AND t5.a<15 AND
|
||||
t6.a>1
|
||||
} db1
|
||||
} {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a>? AND a<?) (~1 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
|
||||
db1 close
|
||||
db2 close
|
||||
sqlite3_enable_shared_cache $::enable_shared_cache
|
||||
}
|
||||
|
||||
finish_test
|
||||
@ -17,7 +17,7 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !stat2 {
|
||||
ifcapable !stat3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
@ -70,7 +70,7 @@ proc sf_execsql {sql {db db}} {
|
||||
# Show that there are two possible plans for querying the table with
|
||||
# a range constraint on the indexed column - "full table scan" or "use
|
||||
# the index". When the range is specified using literal values, SQLite
|
||||
# is able to pick the best plan based on the samples in sqlite_stat2.
|
||||
# is able to pick the best plan based on the samples in sqlite_stat3.
|
||||
#
|
||||
# analyze3-1.1.4 - 3.1.9
|
||||
# Show that using SQL variables produces the same results as using
|
||||
@ -97,10 +97,10 @@ do_test analyze3-1.1.1 {
|
||||
|
||||
do_eqp_test analyze3-1.1.2 {
|
||||
SELECT sum(y) FROM t1 WHERE x>200 AND x<300
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?) (~100 rows)}}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?) (~179 rows)}}
|
||||
do_eqp_test analyze3-1.1.3 {
|
||||
SELECT sum(y) FROM t1 WHERE x>0 AND x<1100
|
||||
} {0 0 0 {SCAN TABLE t1 (~111 rows)}}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?) (~959 rows)}}
|
||||
|
||||
do_test analyze3-1.1.4 {
|
||||
sf_execsql { SELECT sum(y) FROM t1 WHERE x>200 AND x<300 }
|
||||
@ -117,17 +117,17 @@ do_test analyze3-1.1.6 {
|
||||
} {199 0 14850}
|
||||
do_test analyze3-1.1.7 {
|
||||
sf_execsql { SELECT sum(y) FROM t1 WHERE x>0 AND x<1100 }
|
||||
} {999 999 499500}
|
||||
} {2000 0 499500}
|
||||
do_test analyze3-1.1.8 {
|
||||
set l [string range "0" 0 end]
|
||||
set u [string range "1100" 0 end]
|
||||
sf_execsql { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u }
|
||||
} {999 999 499500}
|
||||
} {2000 0 499500}
|
||||
do_test analyze3-1.1.9 {
|
||||
set l [expr int(0)]
|
||||
set u [expr int(1100)]
|
||||
sf_execsql { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u }
|
||||
} {999 999 499500}
|
||||
} {2000 0 499500}
|
||||
|
||||
|
||||
# The following tests are similar to the block above. The difference is
|
||||
@ -146,10 +146,10 @@ do_test analyze3-1.2.1 {
|
||||
} {}
|
||||
do_eqp_test analyze3-1.2.2 {
|
||||
SELECT sum(y) FROM t2 WHERE x>1 AND x<2
|
||||
} {0 0 0 {SEARCH TABLE t2 USING INDEX i2 (x>? AND x<?) (~200 rows)}}
|
||||
} {0 0 0 {SEARCH TABLE t2 USING INDEX i2 (x>? AND x<?) (~196 rows)}}
|
||||
do_eqp_test analyze3-1.2.3 {
|
||||
SELECT sum(y) FROM t2 WHERE x>0 AND x<99
|
||||
} {0 0 0 {SCAN TABLE t2 (~111 rows)}}
|
||||
} {0 0 0 {SEARCH TABLE t2 USING INDEX i2 (x>? AND x<?) (~968 rows)}}
|
||||
do_test analyze3-1.2.4 {
|
||||
sf_execsql { SELECT sum(y) FROM t2 WHERE x>12 AND x<20 }
|
||||
} {161 0 4760}
|
||||
@ -165,17 +165,17 @@ do_test analyze3-1.2.6 {
|
||||
} {161 0 integer integer 4760}
|
||||
do_test analyze3-1.2.7 {
|
||||
sf_execsql { SELECT sum(y) FROM t2 WHERE x>0 AND x<99 }
|
||||
} {999 999 490555}
|
||||
} {1981 0 490555}
|
||||
do_test analyze3-1.2.8 {
|
||||
set l [string range "0" 0 end]
|
||||
set u [string range "99" 0 end]
|
||||
sf_execsql {SELECT typeof($l), typeof($u), sum(y) FROM t2 WHERE x>$l AND x<$u}
|
||||
} {999 999 text text 490555}
|
||||
} {1981 0 text text 490555}
|
||||
do_test analyze3-1.2.9 {
|
||||
set l [expr int(0)]
|
||||
set u [expr int(99)]
|
||||
sf_execsql {SELECT typeof($l), typeof($u), sum(y) FROM t2 WHERE x>$l AND x<$u}
|
||||
} {999 999 integer integer 490555}
|
||||
} {1981 0 integer integer 490555}
|
||||
|
||||
# Same tests a third time. This time, column x has INTEGER affinity and
|
||||
# is not the leftmost column of the table. This triggered a bug causing
|
||||
@ -193,10 +193,10 @@ do_test analyze3-1.3.1 {
|
||||
} {}
|
||||
do_eqp_test analyze3-1.3.2 {
|
||||
SELECT sum(y) FROM t3 WHERE x>200 AND x<300
|
||||
} {0 0 0 {SEARCH TABLE t3 USING INDEX i3 (x>? AND x<?) (~100 rows)}}
|
||||
} {0 0 0 {SEARCH TABLE t3 USING INDEX i3 (x>? AND x<?) (~156 rows)}}
|
||||
do_eqp_test analyze3-1.3.3 {
|
||||
SELECT sum(y) FROM t3 WHERE x>0 AND x<1100
|
||||
} {0 0 0 {SCAN TABLE t3 (~111 rows)}}
|
||||
} {0 0 0 {SEARCH TABLE t3 USING INDEX i3 (x>? AND x<?) (~989 rows)}}
|
||||
|
||||
do_test analyze3-1.3.4 {
|
||||
sf_execsql { SELECT sum(y) FROM t3 WHERE x>200 AND x<300 }
|
||||
@ -213,17 +213,17 @@ do_test analyze3-1.3.6 {
|
||||
} {199 0 14850}
|
||||
do_test analyze3-1.3.7 {
|
||||
sf_execsql { SELECT sum(y) FROM t3 WHERE x>0 AND x<1100 }
|
||||
} {999 999 499500}
|
||||
} {2000 0 499500}
|
||||
do_test analyze3-1.3.8 {
|
||||
set l [string range "0" 0 end]
|
||||
set u [string range "1100" 0 end]
|
||||
sf_execsql { SELECT sum(y) FROM t3 WHERE x>$l AND x<$u }
|
||||
} {999 999 499500}
|
||||
} {2000 0 499500}
|
||||
do_test analyze3-1.3.9 {
|
||||
set l [expr int(0)]
|
||||
set u [expr int(1100)]
|
||||
sf_execsql { SELECT sum(y) FROM t3 WHERE x>$l AND x<$u }
|
||||
} {999 999 499500}
|
||||
} {2000 0 499500}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that the values of bound SQL variables may be used for the LIKE
|
||||
@ -248,7 +248,7 @@ do_test analyze3-2.1 {
|
||||
} {}
|
||||
do_eqp_test analyze3-2.2 {
|
||||
SELECT count(a) FROM t1 WHERE b LIKE 'a%'
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (b>? AND b<?) (~30000 rows)}}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (b>? AND b<?) (~31250 rows)}}
|
||||
do_eqp_test analyze3-2.3 {
|
||||
SELECT count(a) FROM t1 WHERE b LIKE '%a'
|
||||
} {0 0 0 {SCAN TABLE t1 (~500000 rows)}}
|
||||
|
||||
@ -10,14 +10,14 @@
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file implements tests for SQLite library. The focus of the tests
|
||||
# in this file is the use of the sqlite_stat2 histogram data on tables
|
||||
# in this file is the use of the sqlite_stat3 histogram data on tables
|
||||
# with many repeated values and only a few distinct values.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !stat2 {
|
||||
ifcapable !stat3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
@ -55,114 +55,102 @@ do_test analyze5-1.0 {
|
||||
CREATE INDEX t1y ON t1(y); -- integers 0 and very few 1s
|
||||
CREATE INDEX t1z ON t1(z); -- integers 0, 1, 2, and 3
|
||||
ANALYZE;
|
||||
SELECT sample FROM sqlite_stat2 WHERE idx='t1u' ORDER BY sampleno;
|
||||
SELECT sample FROM sqlite_stat3 WHERE idx='t1u' ORDER BY nlt;
|
||||
}
|
||||
} {alpha alpha alpha alpha bravo bravo bravo charlie charlie delta}
|
||||
do_test analyze5-1.1 {
|
||||
string tolower \
|
||||
[db eval {SELECT sample from sqlite_stat2 WHERE idx='t1v' ORDER BY sampleno}]
|
||||
} {alpha alpha alpha alpha bravo bravo bravo charlie charlie delta}
|
||||
do_test analyze5-1.2 {
|
||||
db eval {SELECT sample from sqlite_stat2 WHERE idx='t1w' ORDER BY sampleno}
|
||||
} {{} 0 0 0 0 1 1 1 2 2}
|
||||
do_test analyze5-1.3 {
|
||||
db eval {SELECT sample from sqlite_stat2 WHERE idx='t1x' ORDER BY sampleno}
|
||||
} {{} {} {} {} 1 1 1 2 2 3}
|
||||
do_test analyze5-1.4 {
|
||||
db eval {SELECT sample from sqlite_stat2 WHERE idx='t1y' ORDER BY sampleno}
|
||||
} {0 0 0 0 0 0 0 0 0 0}
|
||||
do_test analyze5-1.5 {
|
||||
db eval {SELECT sample from sqlite_stat2 WHERE idx='t1z' ORDER BY sampleno}
|
||||
} {0 0 0 0 1 1 1 2 2 3}
|
||||
do_test analyze5-1.6 {
|
||||
db eval {SELECT sample from sqlite_stat2 WHERE idx='t1t' ORDER BY sampleno}
|
||||
} {0.5 0.5 0.5 0.5 1.5 1.5 1.5 2.5 2.5 3.5}
|
||||
} {alpha bravo charlie delta}
|
||||
|
||||
do_test analyze5-1.1 {
|
||||
db eval {SELECT DISTINCT lower(sample) FROM sqlite_stat3 WHERE idx='t1v'
|
||||
ORDER BY 1}
|
||||
} {alpha bravo charlie delta}
|
||||
do_test analyze5-1.2 {
|
||||
db eval {SELECT idx, count(*) FROM sqlite_stat3 GROUP BY 1 ORDER BY 1}
|
||||
} {t1t 4 t1u 4 t1v 4 t1w 4 t1x 4 t1y 2 t1z 4}
|
||||
|
||||
# Verify that range queries generate the correct row count estimates
|
||||
#
|
||||
foreach {testid where index rows} {
|
||||
1 {z>=0 AND z<=0} t1z 400
|
||||
2 {z>=1 AND z<=1} t1z 300
|
||||
3 {z>=2 AND z<=2} t1z 200
|
||||
4 {z>=3 AND z<=3} t1z 100
|
||||
5 {z>=4 AND z<=4} t1z 50
|
||||
6 {z>=-1 AND z<=-1} t1z 50
|
||||
7 {z>1 AND z<3} t1z 200
|
||||
3 {z>=2 AND z<=2} t1z 175
|
||||
4 {z>=3 AND z<=3} t1z 125
|
||||
5 {z>=4 AND z<=4} t1z 1
|
||||
6 {z>=-1 AND z<=-1} t1z 1
|
||||
7 {z>1 AND z<3} t1z 175
|
||||
8 {z>0 AND z<100} t1z 600
|
||||
9 {z>=1 AND z<100} t1z 600
|
||||
10 {z>1 AND z<100} t1z 300
|
||||
11 {z>=2 AND z<100} t1z 300
|
||||
12 {z>2 AND z<100} t1z 100
|
||||
13 {z>=3 AND z<100} t1z 100
|
||||
14 {z>3 AND z<100} t1z 50
|
||||
15 {z>=4 AND z<100} t1z 50
|
||||
16 {z>=-100 AND z<=-1} t1z 50
|
||||
12 {z>2 AND z<100} t1z 125
|
||||
13 {z>=3 AND z<100} t1z 125
|
||||
14 {z>3 AND z<100} t1z 1
|
||||
15 {z>=4 AND z<100} t1z 1
|
||||
16 {z>=-100 AND z<=-1} t1z 1
|
||||
17 {z>=-100 AND z<=0} t1z 400
|
||||
18 {z>=-100 AND z<0} t1z 50
|
||||
18 {z>=-100 AND z<0} t1z 1
|
||||
19 {z>=-100 AND z<=1} t1z 700
|
||||
20 {z>=-100 AND z<2} t1z 700
|
||||
21 {z>=-100 AND z<=2} t1z 900
|
||||
22 {z>=-100 AND z<3} t1z 900
|
||||
21 {z>=-100 AND z<=2} t1z 875
|
||||
22 {z>=-100 AND z<3} t1z 875
|
||||
|
||||
31 {z>=0.0 AND z<=0.0} t1z 400
|
||||
32 {z>=1.0 AND z<=1.0} t1z 300
|
||||
33 {z>=2.0 AND z<=2.0} t1z 200
|
||||
34 {z>=3.0 AND z<=3.0} t1z 100
|
||||
35 {z>=4.0 AND z<=4.0} t1z 50
|
||||
36 {z>=-1.0 AND z<=-1.0} t1z 50
|
||||
37 {z>1.5 AND z<3.0} t1z 200
|
||||
38 {z>0.5 AND z<100} t1z 600
|
||||
33 {z>=2.0 AND z<=2.0} t1z 175
|
||||
34 {z>=3.0 AND z<=3.0} t1z 125
|
||||
35 {z>=4.0 AND z<=4.0} t1z 1
|
||||
36 {z>=-1.0 AND z<=-1.0} t1z 1
|
||||
37 {z>1.5 AND z<3.0} t1z 174
|
||||
38 {z>0.5 AND z<100} t1z 599
|
||||
39 {z>=1.0 AND z<100} t1z 600
|
||||
40 {z>1.5 AND z<100} t1z 300
|
||||
40 {z>1.5 AND z<100} t1z 299
|
||||
41 {z>=2.0 AND z<100} t1z 300
|
||||
42 {z>2.1 AND z<100} t1z 100
|
||||
43 {z>=3.0 AND z<100} t1z 100
|
||||
44 {z>3.2 AND z<100} t1z 50
|
||||
45 {z>=4.0 AND z<100} t1z 50
|
||||
46 {z>=-100 AND z<=-1.0} t1z 50
|
||||
42 {z>2.1 AND z<100} t1z 124
|
||||
43 {z>=3.0 AND z<100} t1z 125
|
||||
44 {z>3.2 AND z<100} t1z 1
|
||||
45 {z>=4.0 AND z<100} t1z 1
|
||||
46 {z>=-100 AND z<=-1.0} t1z 1
|
||||
47 {z>=-100 AND z<=0.0} t1z 400
|
||||
48 {z>=-100 AND z<0.0} t1z 50
|
||||
48 {z>=-100 AND z<0.0} t1z 1
|
||||
49 {z>=-100 AND z<=1.0} t1z 700
|
||||
50 {z>=-100 AND z<2.0} t1z 700
|
||||
51 {z>=-100 AND z<=2.0} t1z 900
|
||||
52 {z>=-100 AND z<3.0} t1z 900
|
||||
51 {z>=-100 AND z<=2.0} t1z 875
|
||||
52 {z>=-100 AND z<3.0} t1z 875
|
||||
|
||||
101 {z=-1} t1z 50
|
||||
101 {z=-1} t1z 1
|
||||
102 {z=0} t1z 400
|
||||
103 {z=1} t1z 300
|
||||
104 {z=2} t1z 200
|
||||
105 {z=3} t1z 100
|
||||
106 {z=4} t1z 50
|
||||
107 {z=-10.0} t1z 50
|
||||
104 {z=2} t1z 175
|
||||
105 {z=3} t1z 125
|
||||
106 {z=4} t1z 1
|
||||
107 {z=-10.0} t1z 1
|
||||
108 {z=0.0} t1z 400
|
||||
109 {z=1.0} t1z 300
|
||||
110 {z=2.0} t1z 200
|
||||
111 {z=3.0} t1z 100
|
||||
112 {z=4.0} t1z 50
|
||||
113 {z=1.5} t1z 50
|
||||
114 {z=2.5} t1z 50
|
||||
110 {z=2.0} t1z 175
|
||||
111 {z=3.0} t1z 125
|
||||
112 {z=4.0} t1z 1
|
||||
113 {z=1.5} t1z 1
|
||||
114 {z=2.5} t1z 1
|
||||
|
||||
201 {z IN (-1)} t1z 50
|
||||
201 {z IN (-1)} t1z 1
|
||||
202 {z IN (0)} t1z 400
|
||||
203 {z IN (1)} t1z 300
|
||||
204 {z IN (2)} t1z 200
|
||||
205 {z IN (3)} t1z 100
|
||||
206 {z IN (4)} t1z 50
|
||||
207 {z IN (0.5)} t1z 50
|
||||
204 {z IN (2)} t1z 175
|
||||
205 {z IN (3)} t1z 125
|
||||
206 {z IN (4)} t1z 1
|
||||
207 {z IN (0.5)} t1z 1
|
||||
208 {z IN (0,1)} t1z 700
|
||||
209 {z IN (0,1,2)} t1z 900
|
||||
209 {z IN (0,1,2)} t1z 875
|
||||
210 {z IN (0,1,2,3)} {} 100
|
||||
211 {z IN (0,1,2,3,4,5)} {} 100
|
||||
212 {z IN (1,2)} t1z 500
|
||||
212 {z IN (1,2)} t1z 475
|
||||
213 {z IN (2,3)} t1z 300
|
||||
214 {z=3 OR z=2} t1z 300
|
||||
215 {z IN (-1,3)} t1z 150
|
||||
216 {z=-1 OR z=3} t1z 150
|
||||
215 {z IN (-1,3)} t1z 126
|
||||
216 {z=-1 OR z=3} t1z 126
|
||||
|
||||
300 {y=0} {} 100
|
||||
301 {y=1} t1y 50
|
||||
302 {y=0.1} t1y 50
|
||||
300 {y=0} t1y 974
|
||||
301 {y=1} t1y 26
|
||||
302 {y=0.1} t1y 1
|
||||
|
||||
400 {x IS NULL} t1x 400
|
||||
|
||||
@ -204,16 +192,17 @@ db eval {
|
||||
# Verify that range queries generate the correct row count estimates
|
||||
#
|
||||
foreach {testid where index rows} {
|
||||
500 {x IS NULL AND u='charlie'} t1u 20
|
||||
501 {x=1 AND u='charlie'} t1x 5
|
||||
502 {x IS NULL} {} 100
|
||||
503 {x=1} t1x 50
|
||||
504 {x IS NOT NULL} t1x 25
|
||||
500 {x IS NULL AND u='charlie'} t1u 17
|
||||
501 {x=1 AND u='charlie'} t1x 1
|
||||
502 {x IS NULL} t1x 995
|
||||
503 {x=1} t1x 1
|
||||
504 {x IS NOT NULL} t1x 2
|
||||
505 {+x IS NOT NULL} {} 500
|
||||
506 {upper(x) IS NOT NULL} {} 500
|
||||
|
||||
} {
|
||||
# Verify that the expected index is used with the expected row count
|
||||
if {$testid==50299} {breakpoint; set sqlite_where_trace 1}
|
||||
do_test analyze5-1.${testid}a {
|
||||
set x [lindex [eqp "SELECT * FROM t1 WHERE $where"] 3]
|
||||
set idx {}
|
||||
@ -221,6 +210,7 @@ foreach {testid where index rows} {
|
||||
regexp {~([0-9]+) rows} $x all nrow
|
||||
list $idx $nrow
|
||||
} [list $index $rows]
|
||||
if {$testid==50299} exit
|
||||
|
||||
# Verify that the same result is achieved regardless of whether or not
|
||||
# the index is used
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !stat2 {
|
||||
ifcapable !stat3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
@ -82,14 +82,14 @@ do_test analyze7-3.1 {
|
||||
do_test analyze7-3.2.1 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=?;}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~86 rows)}}
|
||||
ifcapable stat2 {
|
||||
# If ENABLE_STAT2 is defined, SQLite comes up with a different estimated
|
||||
ifcapable stat3 {
|
||||
# If ENABLE_STAT3 is defined, SQLite comes up with a different estimated
|
||||
# row count for (c=2) than it does for (c=?).
|
||||
do_test analyze7-3.2.2 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~51 rows)}}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~57 rows)}}
|
||||
} else {
|
||||
# If ENABLE_STAT2 is not defined, the expected row count for (c=2) is the
|
||||
# If ENABLE_STAT3 is not defined, the expected row count for (c=2) is the
|
||||
# same as that for (c=?).
|
||||
do_test analyze7-3.2.3 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;}
|
||||
@ -98,12 +98,14 @@ ifcapable stat2 {
|
||||
do_test analyze7-3.3 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND b=123}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
|
||||
do_test analyze7-3.4 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=123 AND b=123}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~2 rows)}}
|
||||
do_test analyze7-3.5 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND c=123}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
|
||||
ifcapable {!stat3} {
|
||||
do_test analyze7-3.4 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=123 AND b=123}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~2 rows)}}
|
||||
do_test analyze7-3.5 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND c=123}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
|
||||
}
|
||||
do_test analyze7-3.6 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=123 AND d=123 AND b=123}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=? AND d=?) (~1 rows)}}
|
||||
|
||||
103
test/analyze8.test
Normal file
103
test/analyze8.test
Normal file
@ -0,0 +1,103 @@
|
||||
# 2011 August 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 file implements tests for SQLite library. The focus of the tests
|
||||
# in this file is testing the capabilities of sqlite_stat3.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !stat3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
set testprefix analyze8
|
||||
|
||||
proc eqp {sql {db db}} {
|
||||
uplevel execsql [list "EXPLAIN QUERY PLAN $sql"] $db
|
||||
}
|
||||
|
||||
# Scenario:
|
||||
#
|
||||
# Two indices. One has mostly singleton entries, but for a few
|
||||
# values there are hundreds of entries. The other has 10-20
|
||||
# entries per value.
|
||||
#
|
||||
# Verify that the query planner chooses the first index for the singleton
|
||||
# entries and the second index for the others.
|
||||
#
|
||||
do_test 1.0 {
|
||||
db eval {
|
||||
CREATE TABLE t1(a,b,c,d);
|
||||
CREATE INDEX t1a ON t1(a);
|
||||
CREATE INDEX t1b ON t1(b);
|
||||
CREATE INDEX t1c ON t1(c);
|
||||
}
|
||||
for {set i 0} {$i<1000} {incr i} {
|
||||
if {$i%2==0} {set a $i} {set a [expr {($i%8)*100}]}
|
||||
set b [expr {$i/10}]
|
||||
set c [expr {$i/8}]
|
||||
set c [expr {$c*$c*$c}]
|
||||
db eval {INSERT INTO t1 VALUES($a,$b,$c,$i)}
|
||||
}
|
||||
db eval {ANALYZE}
|
||||
} {}
|
||||
|
||||
# The a==100 comparison is expensive because there are many rows
|
||||
# with a==100. And so for those cases, choose the t1b index.
|
||||
#
|
||||
# Buf ro a==99 and a==101, there are far fewer rows so choose
|
||||
# the t1a index.
|
||||
#
|
||||
do_test 1.1 {
|
||||
eqp {SELECT * FROM t1 WHERE a=100 AND b=55}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~2 rows)}}
|
||||
do_test 1.2 {
|
||||
eqp {SELECT * FROM t1 WHERE a=99 AND b=55}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
|
||||
do_test 1.3 {
|
||||
eqp {SELECT * FROM t1 WHERE a=101 AND b=55}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
|
||||
do_test 1.4 {
|
||||
eqp {SELECT * FROM t1 WHERE a=100 AND b=56}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~2 rows)}}
|
||||
do_test 1.5 {
|
||||
eqp {SELECT * FROM t1 WHERE a=99 AND b=56}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
|
||||
do_test 1.6 {
|
||||
eqp {SELECT * FROM t1 WHERE a=101 AND b=56}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
|
||||
do_test 2.1 {
|
||||
eqp {SELECT * FROM t1 WHERE a=100 AND b BETWEEN 50 AND 54}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>? AND b<?) (~2 rows)}}
|
||||
|
||||
# There are many more values of c between 0 and 100000 than there are
|
||||
# between 800000 and 900000. So t1c is more selective for the latter
|
||||
# range.
|
||||
#
|
||||
do_test 3.1 {
|
||||
eqp {SELECT * FROM t1 WHERE b BETWEEN 50 AND 54 AND c BETWEEN 0 AND 100000}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>? AND b<?) (~6 rows)}}
|
||||
do_test 3.2 {
|
||||
eqp {SELECT * FROM t1
|
||||
WHERE b BETWEEN 50 AND 54 AND c BETWEEN 800000 AND 900000}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c>? AND c<?) (~4 rows)}}
|
||||
do_test 3.3 {
|
||||
eqp {SELECT * FROM t1 WHERE a=100 AND c BETWEEN 0 AND 100000}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~63 rows)}}
|
||||
do_test 3.4 {
|
||||
eqp {SELECT * FROM t1
|
||||
WHERE a=100 AND c BETWEEN 800000 AND 900000}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c>? AND c<?) (~2 rows)}}
|
||||
|
||||
finish_test
|
||||
@ -68,7 +68,7 @@ foreach testfile [lsort -dictionary [glob $testdir/*.test]] {
|
||||
|
||||
# Make sure everything is flushed through. This is because [source]ing
|
||||
# the next test file will delete the database file on disk (using
|
||||
# [file delete]). If the asynchronous backend still has the file
|
||||
# [delete_file]). If the asynchronous backend still has the file
|
||||
# open, it will become confused.
|
||||
#
|
||||
flush_async_queue
|
||||
|
||||
@ -51,7 +51,7 @@ foreach err [list ioerr malloc-transient malloc-persistent] {
|
||||
for {set n 1} {$::go} {incr n} {
|
||||
set ::sqlite_io_error_pending 0
|
||||
sqlite3_memdebug_fail -1
|
||||
file delete -force test.db test.db-journal
|
||||
forcedelete test.db test.db-journal
|
||||
sqlite3 db test.db
|
||||
execsql $::setup_script
|
||||
db close
|
||||
|
||||
@ -41,8 +41,8 @@ set paths {
|
||||
|
||||
do_test async3-1.0 {
|
||||
file mkdir [file join chocolate banana vanilla]
|
||||
file delete -force chocolate/banana/vanilla/file.db
|
||||
file delete -force chocolate/banana/vanilla/file.db-journal
|
||||
forcedelete chocolate/banana/vanilla/file.db
|
||||
forcedelete chocolate/banana/vanilla/file.db-journal
|
||||
} {}
|
||||
|
||||
do_test async3-1.1 {
|
||||
|
||||
@ -20,7 +20,7 @@ if {[info commands sqlite3async_initialize] eq ""} {
|
||||
}
|
||||
|
||||
db close
|
||||
file delete -force test2.db
|
||||
forcedelete test2.db
|
||||
sqlite3async_initialize "" 1
|
||||
sqlite3async_control halt never
|
||||
sqlite3 db test.db
|
||||
|
||||
@ -24,8 +24,8 @@ ifcapable !attach {
|
||||
}
|
||||
|
||||
for {set i 2} {$i<=15} {incr i} {
|
||||
file delete -force test$i.db
|
||||
file delete -force test$i.db-journal
|
||||
forcedelete test$i.db
|
||||
forcedelete test$i.db-journal
|
||||
}
|
||||
|
||||
do_test attach-1.1 {
|
||||
@ -628,7 +628,7 @@ do_test attach-5.1 {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
db2 close
|
||||
file delete -force test2.db
|
||||
forcedelete test2.db
|
||||
sqlite3 db2 test2.db
|
||||
catchsql {
|
||||
ATTACH DATABASE 'test.db' AS orig;
|
||||
@ -725,7 +725,7 @@ if {$tcl_platform(platform)=="unix"} {
|
||||
file attributes cannot-read -permission 0000
|
||||
if {[file writable cannot-read]} {
|
||||
puts "\n**** Tests do not work when run as root ****"
|
||||
file delete -force cannot-read
|
||||
forcedelete cannot-read
|
||||
exit 1
|
||||
}
|
||||
catchsql {
|
||||
@ -735,7 +735,7 @@ if {$tcl_platform(platform)=="unix"} {
|
||||
do_test attach-6.2.2 {
|
||||
db errorcode
|
||||
} {14}
|
||||
file delete -force cannot-read
|
||||
forcedelete cannot-read
|
||||
}
|
||||
|
||||
# Check the error message if we try to access a database that has
|
||||
@ -749,12 +749,12 @@ for {set i 2} {$i<=15} {incr i} {
|
||||
catch {db$i close}
|
||||
}
|
||||
db close
|
||||
file delete -force test2.db
|
||||
file delete -force no-such-file
|
||||
forcedelete test2.db
|
||||
forcedelete no-such-file
|
||||
|
||||
ifcapable subquery {
|
||||
do_test attach-7.1 {
|
||||
file delete -force test.db test.db-journal
|
||||
forcedelete test.db test.db-journal
|
||||
sqlite3 db test.db
|
||||
catchsql {
|
||||
DETACH RAISE ( IGNORE ) IN ( SELECT "AAAAAA" . * ORDER BY
|
||||
@ -777,7 +777,7 @@ do_test attach-8.1 {
|
||||
do_test attach-8.2 {
|
||||
db errorcode
|
||||
} {26}
|
||||
file delete -force test2.db
|
||||
forcedelete test2.db
|
||||
do_test attach-8.3 {
|
||||
sqlite3 db2 test2.db
|
||||
db2 eval {CREATE TABLE t1(x); BEGIN EXCLUSIVE}
|
||||
@ -789,13 +789,13 @@ do_test attach-8.4 {
|
||||
db errorcode
|
||||
} {5}
|
||||
db2 close
|
||||
file delete -force test2.db
|
||||
forcedelete test2.db
|
||||
|
||||
# Test that it is possible to attach the same database more than
|
||||
# once when not in shared-cache mode. That this is not possible in
|
||||
# shared-cache mode is tested in shared7.test.
|
||||
do_test attach-9.1 {
|
||||
file delete -force test4.db
|
||||
forcedelete test4.db
|
||||
execsql {
|
||||
ATTACH 'test4.db' AS aux1;
|
||||
CREATE TABLE aux1.t1(a, b);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user