Merge branch 'sqlite-release' into release-integration
This commit is contained in:
commit
648c4e50fe
11
Makefile.in
11
Makefile.in
@ -391,7 +391,9 @@ TESTSRC = \
|
||||
$(TOP)/src/test_thread.c \
|
||||
$(TOP)/src/test_vfs.c \
|
||||
$(TOP)/src/test_wholenumber.c \
|
||||
$(TOP)/src/test_wsd.c
|
||||
$(TOP)/src/test_wsd.c \
|
||||
$(TOP)/ext/fts3/fts3_term.c \
|
||||
$(TOP)/ext/fts3/fts3_test.c
|
||||
|
||||
# Source code to the library files needed by the test fixture
|
||||
#
|
||||
@ -433,6 +435,7 @@ TESTSRC2 = \
|
||||
$(TOP)/ext/fts3/fts3.c \
|
||||
$(TOP)/ext/fts3/fts3_aux.c \
|
||||
$(TOP)/ext/fts3/fts3_expr.c \
|
||||
$(TOP)/ext/fts3/fts3_term.c \
|
||||
$(TOP)/ext/fts3/fts3_tokenizer.c \
|
||||
$(TOP)/ext/fts3/fts3_write.c \
|
||||
$(TOP)/ext/async/sqlite3async.c
|
||||
@ -848,12 +851,12 @@ fts3_hash.lo: $(TOP)/ext/fts3/fts3_hash.c $(HDR) $(EXTHDR)
|
||||
fts3_icu.lo: $(TOP)/ext/fts3/fts3_icu.c $(HDR) $(EXTHDR)
|
||||
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_icu.c
|
||||
|
||||
fts3_snippet.lo: $(TOP)/ext/fts3/fts3_snippet.c $(HDR) $(EXTHDR)
|
||||
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_snippet.c
|
||||
|
||||
fts3_porter.lo: $(TOP)/ext/fts3/fts3_porter.c $(HDR) $(EXTHDR)
|
||||
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_porter.c
|
||||
|
||||
fts3_snippet.lo: $(TOP)/ext/fts3/fts3_snippet.c $(HDR) $(EXTHDR)
|
||||
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_snippet.c
|
||||
|
||||
fts3_tokenizer.lo: $(TOP)/ext/fts3/fts3_tokenizer.c $(HDR) $(EXTHDR)
|
||||
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_tokenizer.c
|
||||
|
||||
|
||||
875
Makefile.msc
Normal file
875
Makefile.msc
Normal file
@ -0,0 +1,875 @@
|
||||
#
|
||||
# nmake Makefile for SQLite
|
||||
#
|
||||
|
||||
# The toplevel directory of the source tree. This is the directory
|
||||
# that contains this "Makefile.msc".
|
||||
#
|
||||
TOP = .
|
||||
|
||||
# Set this non-0 to create and use the SQLite amalgamation file.
|
||||
#
|
||||
USE_AMALGAMATION = 1
|
||||
|
||||
# Version numbers and release number for the SQLite being compiled.
|
||||
#
|
||||
VERSION = 3.7
|
||||
VERSION_NUMBER = 3007007
|
||||
RELEASE = 3.7.7
|
||||
|
||||
# C Compiler and options for use in building executables that
|
||||
# will run on the platform that is doing the build.
|
||||
#
|
||||
BCC = cl.exe -O2
|
||||
|
||||
# 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
|
||||
|
||||
# The mksqlite3c.tcl and mksqlite3h.tcl scripts will pull in
|
||||
# any extension header files by default. For non-amalgamation
|
||||
# builds, we need to make sure the compiler can find these.
|
||||
#
|
||||
!IF $(USE_AMALGAMATION)==0
|
||||
TCC = $(TCC) -I$(TOP)\ext\fts3
|
||||
TCC = $(TCC) -I$(TOP)\ext\rtree
|
||||
!ENDIF
|
||||
|
||||
# Define -DNDEBUG to compile without debugging (i.e., for production usage)
|
||||
# Omitting the define will cause extra debugging code to be inserted and
|
||||
# includes extra comments when "EXPLAIN stmt" is used.
|
||||
#
|
||||
TCC = $(TCC) -DNDEBUG
|
||||
|
||||
# The library that programs using TCL must link against.
|
||||
#
|
||||
LIBTCL = tcl85.lib
|
||||
TCLINCDIR = c:\tcl\include
|
||||
TCLLIBDIR = c:\tcl\lib
|
||||
|
||||
# This is the command to use for tclsh - normally just "tclsh", but we may
|
||||
# know the specific version we want to use
|
||||
#
|
||||
TCLSH_CMD = tclsh85
|
||||
|
||||
# Compiler options needed for programs that use the readline() library.
|
||||
#
|
||||
READLINE_FLAGS = -DHAVE_READLINE=0
|
||||
|
||||
# The library that programs using readline() must link against.
|
||||
#
|
||||
LIBREADLINE =
|
||||
|
||||
# Should the database engine be compiled threadsafe
|
||||
#
|
||||
TCC = $(TCC) -DSQLITE_THREADSAFE=1
|
||||
|
||||
# Do threads override each others locks by default (1), or do we test (-1)
|
||||
#
|
||||
TCC = $(TCC) -DSQLITE_THREAD_OVERRIDE_LOCK=-1
|
||||
|
||||
# Any target libraries which libsqlite must be linked against
|
||||
#
|
||||
TLIBS =
|
||||
|
||||
# Flags controlling use of the in memory btree implementation
|
||||
#
|
||||
# SQLITE_TEMP_STORE is 0 to force temporary tables to be in a file, 1 to
|
||||
# default to file, 2 to default to memory, and 3 to force temporary
|
||||
# tables to always be in memory.
|
||||
#
|
||||
TCC = $(TCC) -DSQLITE_TEMP_STORE=1
|
||||
|
||||
# Enable/disable loadable extensions, and other optional features
|
||||
# based on configuration. (-DSQLITE_OMIT*, -DSQLITE_ENABLE*).
|
||||
# The same set of OMIT and ENABLE flags should be passed to the
|
||||
# LEMON parser generator and the mkkeywordhash tool as well.
|
||||
|
||||
# BEGIN standard options
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS3=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RTREE=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1
|
||||
# END standard options
|
||||
|
||||
# BEGIN required Windows option
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_MAX_TRIGGER_DEPTH=100
|
||||
# END required Windows option
|
||||
|
||||
TCC = $(TCC) $(OPT_FEATURE_FLAGS)
|
||||
|
||||
# Add in any optional parameters specified on the make commane line
|
||||
# ie. make "OPTS=-DSQLITE_ENABLE_FOO=1 -DSQLITE_OMIT_FOO=1".
|
||||
TCC = $(TCC) $(OPTS)
|
||||
|
||||
# libtool compile/link
|
||||
LTCOMPILE = $(TCC) -Fo$@
|
||||
LTLIB = lib.exe
|
||||
LTLINK = $(TCC) -Fe$@
|
||||
|
||||
# If a platform was set, force the linker to target that.
|
||||
# Note that the vcvars*.bat family of batch files typically
|
||||
# set this for you. Otherwise, the linker will attempt
|
||||
# to deduce the binary type based on the object files.
|
||||
!IF "$(PLATFORM)"!=""
|
||||
LTLINKOPTS = /MACHINE:$(PLATFORM)
|
||||
LTLIBOPTS = /MACHINE:$(PLATFORM)
|
||||
!ENDIF
|
||||
|
||||
# nawk compatible awk.
|
||||
NAWK = .\gawk.exe
|
||||
|
||||
# You should not have to change anything below this line
|
||||
###############################################################################
|
||||
|
||||
# Object files for the SQLite library (non-amalgamation).
|
||||
#
|
||||
LIBOBJS0 = alter.lo analyze.lo attach.lo auth.lo \
|
||||
backup.lo bitvec.lo btmutex.lo btree.lo build.lo \
|
||||
callback.lo complete.lo ctime.lo date.lo delete.lo \
|
||||
expr.lo fault.lo fkey.lo \
|
||||
fts3.lo fts3_aux.lo fts3_expr.lo fts3_hash.lo fts3_icu.lo fts3_porter.lo \
|
||||
fts3_snippet.lo fts3_tokenizer.lo fts3_tokenizer1.lo fts3_write.lo \
|
||||
func.lo global.lo hash.lo \
|
||||
icu.lo insert.lo journal.lo legacy.lo loadext.lo \
|
||||
main.lo malloc.lo mem0.lo mem1.lo mem2.lo mem3.lo mem5.lo \
|
||||
memjournal.lo \
|
||||
mutex.lo mutex_noop.lo mutex_os2.lo mutex_unix.lo mutex_w32.lo \
|
||||
notify.lo opcodes.lo os.lo os_os2.lo os_unix.lo os_win.lo \
|
||||
pager.lo parse.lo pcache.lo pcache1.lo pragma.lo prepare.lo printf.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
|
||||
|
||||
# Object files for the amalgamation.
|
||||
#
|
||||
LIBOBJS1 = sqlite3.lo
|
||||
|
||||
# Determine the real value of LIBOBJ based on the 'configure' script
|
||||
#
|
||||
!IF $(USE_AMALGAMATION)==0
|
||||
LIBOBJ = $(LIBOBJS0)
|
||||
!ELSE
|
||||
LIBOBJ = $(LIBOBJS1)
|
||||
!ENDIF
|
||||
|
||||
# All of the source code files.
|
||||
#
|
||||
SRC = \
|
||||
$(TOP)\src\alter.c \
|
||||
$(TOP)\src\analyze.c \
|
||||
$(TOP)\src\attach.c \
|
||||
$(TOP)\src\auth.c \
|
||||
$(TOP)\src\backup.c \
|
||||
$(TOP)\src\bitvec.c \
|
||||
$(TOP)\src\btmutex.c \
|
||||
$(TOP)\src\btree.c \
|
||||
$(TOP)\src\btree.h \
|
||||
$(TOP)\src\btreeInt.h \
|
||||
$(TOP)\src\build.c \
|
||||
$(TOP)\src\callback.c \
|
||||
$(TOP)\src\complete.c \
|
||||
$(TOP)\src\ctime.c \
|
||||
$(TOP)\src\date.c \
|
||||
$(TOP)\src\delete.c \
|
||||
$(TOP)\src\expr.c \
|
||||
$(TOP)\src\fault.c \
|
||||
$(TOP)\src\fkey.c \
|
||||
$(TOP)\src\func.c \
|
||||
$(TOP)\src\global.c \
|
||||
$(TOP)\src\hash.c \
|
||||
$(TOP)\src\hash.h \
|
||||
$(TOP)\src\hwtime.h \
|
||||
$(TOP)\src\insert.c \
|
||||
$(TOP)\src\journal.c \
|
||||
$(TOP)\src\legacy.c \
|
||||
$(TOP)\src\loadext.c \
|
||||
$(TOP)\src\main.c \
|
||||
$(TOP)\src\malloc.c \
|
||||
$(TOP)\src\mem0.c \
|
||||
$(TOP)\src\mem1.c \
|
||||
$(TOP)\src\mem2.c \
|
||||
$(TOP)\src\mem3.c \
|
||||
$(TOP)\src\mem5.c \
|
||||
$(TOP)\src\memjournal.c \
|
||||
$(TOP)\src\mutex.c \
|
||||
$(TOP)\src\mutex.h \
|
||||
$(TOP)\src\mutex_noop.c \
|
||||
$(TOP)\src\mutex_os2.c \
|
||||
$(TOP)\src\mutex_unix.c \
|
||||
$(TOP)\src\mutex_w32.c \
|
||||
$(TOP)\src\notify.c \
|
||||
$(TOP)\src\os.c \
|
||||
$(TOP)\src\os.h \
|
||||
$(TOP)\src\os_common.h \
|
||||
$(TOP)\src\os_os2.c \
|
||||
$(TOP)\src\os_unix.c \
|
||||
$(TOP)\src\os_win.c \
|
||||
$(TOP)\src\pager.c \
|
||||
$(TOP)\src\pager.h \
|
||||
$(TOP)\src\parse.y \
|
||||
$(TOP)\src\pcache.c \
|
||||
$(TOP)\src\pcache.h \
|
||||
$(TOP)\src\pcache1.c \
|
||||
$(TOP)\src\pragma.c \
|
||||
$(TOP)\src\prepare.c \
|
||||
$(TOP)\src\printf.c \
|
||||
$(TOP)\src\random.c \
|
||||
$(TOP)\src\resolve.c \
|
||||
$(TOP)\src\rowset.c \
|
||||
$(TOP)\src\select.c \
|
||||
$(TOP)\src\status.c \
|
||||
$(TOP)\src\shell.c \
|
||||
$(TOP)\src\sqlite.h.in \
|
||||
$(TOP)\src\sqlite3ext.h \
|
||||
$(TOP)\src\sqliteInt.h \
|
||||
$(TOP)\src\sqliteLimit.h \
|
||||
$(TOP)\src\table.c \
|
||||
$(TOP)\src\tclsqlite.c \
|
||||
$(TOP)\src\tokenize.c \
|
||||
$(TOP)\src\trigger.c \
|
||||
$(TOP)\src\utf.c \
|
||||
$(TOP)\src\update.c \
|
||||
$(TOP)\src\util.c \
|
||||
$(TOP)\src\vacuum.c \
|
||||
$(TOP)\src\vdbe.c \
|
||||
$(TOP)\src\vdbe.h \
|
||||
$(TOP)\src\vdbeapi.c \
|
||||
$(TOP)\src\vdbeaux.c \
|
||||
$(TOP)\src\vdbeblob.c \
|
||||
$(TOP)\src\vdbemem.c \
|
||||
$(TOP)\src\vdbetrace.c \
|
||||
$(TOP)\src\vdbeInt.h \
|
||||
$(TOP)\src\vtab.c \
|
||||
$(TOP)\src\wal.c \
|
||||
$(TOP)\src\wal.h \
|
||||
$(TOP)\src\walker.c \
|
||||
$(TOP)\src\where.c
|
||||
|
||||
# Source code for extensions
|
||||
#
|
||||
SRC = $(SRC) \
|
||||
$(TOP)\ext\fts1\fts1.c \
|
||||
$(TOP)\ext\fts1\fts1.h \
|
||||
$(TOP)\ext\fts1\fts1_hash.c \
|
||||
$(TOP)\ext\fts1\fts1_hash.h \
|
||||
$(TOP)\ext\fts1\fts1_porter.c \
|
||||
$(TOP)\ext\fts1\fts1_tokenizer.h \
|
||||
$(TOP)\ext\fts1\fts1_tokenizer1.c
|
||||
SRC = $(SRC) \
|
||||
$(TOP)\ext\fts2\fts2.c \
|
||||
$(TOP)\ext\fts2\fts2.h \
|
||||
$(TOP)\ext\fts2\fts2_hash.c \
|
||||
$(TOP)\ext\fts2\fts2_hash.h \
|
||||
$(TOP)\ext\fts2\fts2_icu.c \
|
||||
$(TOP)\ext\fts2\fts2_porter.c \
|
||||
$(TOP)\ext\fts2\fts2_tokenizer.h \
|
||||
$(TOP)\ext\fts2\fts2_tokenizer.c \
|
||||
$(TOP)\ext\fts2\fts2_tokenizer1.c
|
||||
SRC = $(SRC) \
|
||||
$(TOP)\ext\fts3\fts3.c \
|
||||
$(TOP)\ext\fts3\fts3.h \
|
||||
$(TOP)\ext\fts3\fts3Int.h \
|
||||
$(TOP)\ext\fts3\fts3_aux.c \
|
||||
$(TOP)\ext\fts3\fts3_expr.c \
|
||||
$(TOP)\ext\fts3\fts3_hash.c \
|
||||
$(TOP)\ext\fts3\fts3_hash.h \
|
||||
$(TOP)\ext\fts3\fts3_icu.c \
|
||||
$(TOP)\ext\fts3\fts3_porter.c \
|
||||
$(TOP)\ext\fts3\fts3_snippet.c \
|
||||
$(TOP)\ext\fts3\fts3_tokenizer.h \
|
||||
$(TOP)\ext\fts3\fts3_tokenizer.c \
|
||||
$(TOP)\ext\fts3\fts3_tokenizer1.c \
|
||||
$(TOP)\ext\fts3\fts3_write.c
|
||||
SRC = $(SRC) \
|
||||
$(TOP)\ext\icu\sqliteicu.h \
|
||||
$(TOP)\ext\icu\icu.c
|
||||
SRC = $(SRC) \
|
||||
$(TOP)\ext\rtree\rtree.h \
|
||||
$(TOP)\ext\rtree\rtree.c
|
||||
|
||||
|
||||
# Generated source code files
|
||||
#
|
||||
SRC = $(SRC) \
|
||||
keywordhash.h \
|
||||
opcodes.c \
|
||||
opcodes.h \
|
||||
parse.c \
|
||||
parse.h \
|
||||
sqlite3.h
|
||||
|
||||
# Source code to the test files.
|
||||
#
|
||||
TESTSRC = \
|
||||
$(TOP)\src\test1.c \
|
||||
$(TOP)\src\test2.c \
|
||||
$(TOP)\src\test3.c \
|
||||
$(TOP)\src\test4.c \
|
||||
$(TOP)\src\test5.c \
|
||||
$(TOP)\src\test6.c \
|
||||
$(TOP)\src\test7.c \
|
||||
$(TOP)\src\test8.c \
|
||||
$(TOP)\src\test9.c \
|
||||
$(TOP)\src\test_autoext.c \
|
||||
$(TOP)\src\test_async.c \
|
||||
$(TOP)\src\test_backup.c \
|
||||
$(TOP)\src\test_btree.c \
|
||||
$(TOP)\src\test_config.c \
|
||||
$(TOP)\src\test_demovfs.c \
|
||||
$(TOP)\src\test_devsym.c \
|
||||
$(TOP)\src\test_func.c \
|
||||
$(TOP)\src\test_fuzzer.c \
|
||||
$(TOP)\src\test_hexio.c \
|
||||
$(TOP)\src\test_init.c \
|
||||
$(TOP)\src\test_intarray.c \
|
||||
$(TOP)\src\test_journal.c \
|
||||
$(TOP)\src\test_malloc.c \
|
||||
$(TOP)\src\test_multiplex.c \
|
||||
$(TOP)\src\test_mutex.c \
|
||||
$(TOP)\src\test_onefile.c \
|
||||
$(TOP)\src\test_osinst.c \
|
||||
$(TOP)\src\test_pcache.c \
|
||||
$(TOP)\src\test_quota.c \
|
||||
$(TOP)\src\test_rtree.c \
|
||||
$(TOP)\src\test_schema.c \
|
||||
$(TOP)\src\test_server.c \
|
||||
$(TOP)\src\test_superlock.c \
|
||||
$(TOP)\src\test_syscall.c \
|
||||
$(TOP)\src\test_stat.c \
|
||||
$(TOP)\src\test_tclvar.c \
|
||||
$(TOP)\src\test_thread.c \
|
||||
$(TOP)\src\test_vfs.c \
|
||||
$(TOP)\src\test_wholenumber.c \
|
||||
$(TOP)\src\test_wsd.c \
|
||||
$(TOP)\ext\fts3\fts3_term.c \
|
||||
$(TOP)\ext\fts3\fts3_test.c
|
||||
|
||||
# Source code to the library files needed by the test fixture
|
||||
#
|
||||
TESTSRC2 = \
|
||||
$(TOP)\src\attach.c \
|
||||
$(TOP)\src\backup.c \
|
||||
$(TOP)\src\bitvec.c \
|
||||
$(TOP)\src\btree.c \
|
||||
$(TOP)\src\build.c \
|
||||
$(TOP)\src\ctime.c \
|
||||
$(TOP)\src\date.c \
|
||||
$(TOP)\src\expr.c \
|
||||
$(TOP)\src\func.c \
|
||||
$(TOP)\src\insert.c \
|
||||
$(TOP)\src\wal.c \
|
||||
$(TOP)\src\mem5.c \
|
||||
$(TOP)\src\os.c \
|
||||
$(TOP)\src\os_os2.c \
|
||||
$(TOP)\src\os_unix.c \
|
||||
$(TOP)\src\os_win.c \
|
||||
$(TOP)\src\pager.c \
|
||||
$(TOP)\src\pragma.c \
|
||||
$(TOP)\src\prepare.c \
|
||||
$(TOP)\src\printf.c \
|
||||
$(TOP)\src\random.c \
|
||||
$(TOP)\src\pcache.c \
|
||||
$(TOP)\src\pcache1.c \
|
||||
$(TOP)\src\select.c \
|
||||
$(TOP)\src\tokenize.c \
|
||||
$(TOP)\src\utf.c \
|
||||
$(TOP)\src\util.c \
|
||||
$(TOP)\src\vdbeapi.c \
|
||||
$(TOP)\src\vdbeaux.c \
|
||||
$(TOP)\src\vdbe.c \
|
||||
$(TOP)\src\vdbemem.c \
|
||||
$(TOP)\src\vdbetrace.c \
|
||||
$(TOP)\src\where.c \
|
||||
parse.c \
|
||||
$(TOP)\ext\fts3\fts3.c \
|
||||
$(TOP)\ext\fts3\fts3_aux.c \
|
||||
$(TOP)\ext\fts3\fts3_expr.c \
|
||||
$(TOP)\ext\fts3\fts3_tokenizer.c \
|
||||
$(TOP)\ext\fts3\fts3_write.c \
|
||||
$(TOP)\ext\async\sqlite3async.c
|
||||
|
||||
# Header files used by all library source files.
|
||||
#
|
||||
HDR = \
|
||||
$(TOP)\src\btree.h \
|
||||
$(TOP)\src\btreeInt.h \
|
||||
$(TOP)\src\hash.h \
|
||||
$(TOP)\src\hwtime.h \
|
||||
keywordhash.h \
|
||||
$(TOP)\src\mutex.h \
|
||||
opcodes.h \
|
||||
$(TOP)\src\os.h \
|
||||
$(TOP)\src\os_common.h \
|
||||
$(TOP)\src\pager.h \
|
||||
$(TOP)\src\pcache.h \
|
||||
parse.h \
|
||||
sqlite3.h \
|
||||
$(TOP)\src\sqlite3ext.h \
|
||||
$(TOP)\src\sqliteInt.h \
|
||||
$(TOP)\src\sqliteLimit.h \
|
||||
$(TOP)\src\vdbe.h \
|
||||
$(TOP)\src\vdbeInt.h
|
||||
|
||||
# Header files used by extensions
|
||||
#
|
||||
EXTHDR = $(EXTHDR) \
|
||||
$(TOP)\ext\fts1\fts1.h \
|
||||
$(TOP)\ext\fts1\fts1_hash.h \
|
||||
$(TOP)\ext\fts1\fts1_tokenizer.h
|
||||
EXTHDR = $(EXTHDR) \
|
||||
$(TOP)\ext\fts2\fts2.h \
|
||||
$(TOP)\ext\fts2\fts2_hash.h \
|
||||
$(TOP)\ext\fts2\fts2_tokenizer.h
|
||||
EXTHDR = $(EXTHDR) \
|
||||
$(TOP)\ext\fts3\fts3.h \
|
||||
$(TOP)\ext\fts3\fts3Int.h \
|
||||
$(TOP)\ext\fts3\fts3_hash.h \
|
||||
$(TOP)\ext\fts3\fts3_tokenizer.h
|
||||
EXTHDR = $(EXTHDR) \
|
||||
$(TOP)\ext\rtree\rtree.h
|
||||
EXTHDR = $(EXTHDR) \
|
||||
$(TOP)\ext\icu\sqliteicu.h
|
||||
EXTHDR = $(EXTHDR) \
|
||||
$(TOP)\ext\rtree\sqlite3rtree.h
|
||||
|
||||
# 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
|
||||
|
||||
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)
|
||||
|
||||
sqlite3.exe: $(TOP)\src\shell.c libsqlite3.lib sqlite3.h
|
||||
$(LTLINK) $(READLINE_FLAGS) \
|
||||
$(TOP)\src\shell.c \
|
||||
/link $(LTLINKOPTS) libsqlite3.lib $(LIBREADLINE) $(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
|
||||
# build on the target system. Some of the C source code and header
|
||||
# files are automatically generated. This target takes care of
|
||||
# all that automatic generation.
|
||||
#
|
||||
.target_source: $(SRC) $(TOP)\tool\vdbe-compress.tcl
|
||||
-rmdir /S/Q tsrc
|
||||
-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
|
||||
move vdbe.new tsrc\vdbe.c
|
||||
echo > .target_source
|
||||
|
||||
sqlite3.c: .target_source $(TOP)\tool\mksqlite3c.tcl
|
||||
$(TCLSH_CMD) $(TOP)\tool\mksqlite3c.tcl
|
||||
|
||||
# Rule to build the amalgamation
|
||||
#
|
||||
sqlite3.lo: sqlite3.c
|
||||
$(LTCOMPILE) -c sqlite3.c
|
||||
|
||||
# Rules to build the LEMON compiler generator
|
||||
#
|
||||
lempar.c: $(TOP)\src\lempar.c
|
||||
copy $(TOP)\src\lempar.c .
|
||||
|
||||
lemon.exe: $(TOP)\tool\lemon.c lempar.c
|
||||
$(BCC) -Fe$@ $(TOP)\tool\lemon.c
|
||||
|
||||
# Rules to build individual *.lo files from generated *.c files. This
|
||||
# applies to:
|
||||
#
|
||||
# parse.lo
|
||||
# opcodes.lo
|
||||
#
|
||||
parse.lo: parse.c $(HDR)
|
||||
$(LTCOMPILE) -c parse.c
|
||||
|
||||
opcodes.lo: opcodes.c
|
||||
$(LTCOMPILE) -c opcodes.c
|
||||
|
||||
# Rules to build individual *.lo files from files in the src directory.
|
||||
#
|
||||
alter.lo: $(TOP)\src\alter.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\alter.c
|
||||
|
||||
analyze.lo: $(TOP)\src\analyze.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\analyze.c
|
||||
|
||||
attach.lo: $(TOP)\src\attach.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\attach.c
|
||||
|
||||
auth.lo: $(TOP)\src\auth.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\auth.c
|
||||
|
||||
backup.lo: $(TOP)\src\backup.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\backup.c
|
||||
|
||||
bitvec.lo: $(TOP)\src\bitvec.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\bitvec.c
|
||||
|
||||
btmutex.lo: $(TOP)\src\btmutex.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\btmutex.c
|
||||
|
||||
btree.lo: $(TOP)\src\btree.c $(HDR) $(TOP)\src\pager.h
|
||||
$(LTCOMPILE) -c $(TOP)\src\btree.c
|
||||
|
||||
build.lo: $(TOP)\src\build.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\build.c
|
||||
|
||||
callback.lo: $(TOP)\src\callback.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\callback.c
|
||||
|
||||
complete.lo: $(TOP)\src\complete.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\complete.c
|
||||
|
||||
ctime.lo: $(TOP)\src\ctime.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\ctime.c
|
||||
|
||||
date.lo: $(TOP)\src\date.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\date.c
|
||||
|
||||
delete.lo: $(TOP)\src\delete.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\delete.c
|
||||
|
||||
expr.lo: $(TOP)\src\expr.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\expr.c
|
||||
|
||||
fault.lo: $(TOP)\src\fault.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\fault.c
|
||||
|
||||
fkey.lo: $(TOP)\src\fkey.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\fkey.c
|
||||
|
||||
func.lo: $(TOP)\src\func.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\func.c
|
||||
|
||||
global.lo: $(TOP)\src\global.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\global.c
|
||||
|
||||
hash.lo: $(TOP)\src\hash.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\hash.c
|
||||
|
||||
insert.lo: $(TOP)\src\insert.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\insert.c
|
||||
|
||||
journal.lo: $(TOP)\src\journal.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\journal.c
|
||||
|
||||
legacy.lo: $(TOP)\src\legacy.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\legacy.c
|
||||
|
||||
loadext.lo: $(TOP)\src\loadext.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\loadext.c
|
||||
|
||||
main.lo: $(TOP)\src\main.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\main.c
|
||||
|
||||
malloc.lo: $(TOP)\src\malloc.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\malloc.c
|
||||
|
||||
mem0.lo: $(TOP)\src\mem0.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\mem0.c
|
||||
|
||||
mem1.lo: $(TOP)\src\mem1.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\mem1.c
|
||||
|
||||
mem2.lo: $(TOP)\src\mem2.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\mem2.c
|
||||
|
||||
mem3.lo: $(TOP)\src\mem3.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\mem3.c
|
||||
|
||||
mem5.lo: $(TOP)\src\mem5.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\mem5.c
|
||||
|
||||
memjournal.lo: $(TOP)\src\memjournal.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\memjournal.c
|
||||
|
||||
mutex.lo: $(TOP)\src\mutex.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\mutex.c
|
||||
|
||||
mutex_noop.lo: $(TOP)\src\mutex_noop.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\mutex_noop.c
|
||||
|
||||
mutex_os2.lo: $(TOP)\src\mutex_os2.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\mutex_os2.c
|
||||
|
||||
mutex_unix.lo: $(TOP)\src\mutex_unix.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\mutex_unix.c
|
||||
|
||||
mutex_w32.lo: $(TOP)\src\mutex_w32.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\mutex_w32.c
|
||||
|
||||
notify.lo: $(TOP)\src\notify.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\notify.c
|
||||
|
||||
pager.lo: $(TOP)\src\pager.c $(HDR) $(TOP)\src\pager.h
|
||||
$(LTCOMPILE) -c $(TOP)\src\pager.c
|
||||
|
||||
pcache.lo: $(TOP)\src\pcache.c $(HDR) $(TOP)\src\pcache.h
|
||||
$(LTCOMPILE) -c $(TOP)\src\pcache.c
|
||||
|
||||
pcache1.lo: $(TOP)\src\pcache1.c $(HDR) $(TOP)\src\pcache.h
|
||||
$(LTCOMPILE) -c $(TOP)\src\pcache1.c
|
||||
|
||||
os.lo: $(TOP)\src\os.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\os.c
|
||||
|
||||
os_unix.lo: $(TOP)\src\os_unix.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\os_unix.c
|
||||
|
||||
os_win.lo: $(TOP)\src\os_win.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\os_win.c
|
||||
|
||||
os_os2.lo: $(TOP)\src\os_os2.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\os_os2.c
|
||||
|
||||
pragma.lo: $(TOP)\src\pragma.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\pragma.c
|
||||
|
||||
prepare.lo: $(TOP)\src\prepare.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\prepare.c
|
||||
|
||||
printf.lo: $(TOP)\src\printf.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\printf.c
|
||||
|
||||
random.lo: $(TOP)\src\random.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\random.c
|
||||
|
||||
resolve.lo: $(TOP)\src\resolve.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\resolve.c
|
||||
|
||||
rowset.lo: $(TOP)\src\rowset.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\rowset.c
|
||||
|
||||
select.lo: $(TOP)\src\select.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\select.c
|
||||
|
||||
status.lo: $(TOP)\src\status.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\status.c
|
||||
|
||||
table.lo: $(TOP)\src\table.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\table.c
|
||||
|
||||
tokenize.lo: $(TOP)\src\tokenize.c keywordhash.h $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\tokenize.c
|
||||
|
||||
trigger.lo: $(TOP)\src\trigger.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\trigger.c
|
||||
|
||||
update.lo: $(TOP)\src\update.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\update.c
|
||||
|
||||
utf.lo: $(TOP)\src\utf.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\utf.c
|
||||
|
||||
util.lo: $(TOP)\src\util.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\util.c
|
||||
|
||||
vacuum.lo: $(TOP)\src\vacuum.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\vacuum.c
|
||||
|
||||
vdbe.lo: $(TOP)\src\vdbe.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\vdbe.c
|
||||
|
||||
vdbeapi.lo: $(TOP)\src\vdbeapi.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\vdbeapi.c
|
||||
|
||||
vdbeaux.lo: $(TOP)\src\vdbeaux.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\vdbeaux.c
|
||||
|
||||
vdbeblob.lo: $(TOP)\src\vdbeblob.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\vdbeblob.c
|
||||
|
||||
vdbemem.lo: $(TOP)\src\vdbemem.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\vdbemem.c
|
||||
|
||||
vdbetrace.lo: $(TOP)\src\vdbetrace.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\vdbetrace.c
|
||||
|
||||
vtab.lo: $(TOP)\src\vtab.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\vtab.c
|
||||
|
||||
wal.lo: $(TOP)\src\wal.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\wal.c
|
||||
|
||||
walker.lo: $(TOP)\src\walker.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\walker.c
|
||||
|
||||
where.lo: $(TOP)\src\where.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\where.c
|
||||
|
||||
tclsqlite.lo: $(TOP)\src\tclsqlite.c $(HDR)
|
||||
$(LTCOMPILE) -DUSE_TCL_STUBS=1 -DBUILD_sqlite -I$(TCLINCDIR) -c $(TOP)\src\tclsqlite.c
|
||||
|
||||
tclsqlite-shell.lo: $(TOP)\src\tclsqlite.c $(HDR)
|
||||
$(LTCOMPILE) -DTCLSH=1 -DBUILD_sqlite -I$(TCLINCDIR) -c $(TOP)\src\tclsqlite.c
|
||||
|
||||
tclsqlite3.exe: tclsqlite-shell.lo libsqlite3.lib
|
||||
$(LTLINK) tclsqlite-shell.lo \
|
||||
/link $(LTLINKOPTS) /LIBPATH:$(TCLLIBDIR) libsqlite3.lib $(LIBTCL)
|
||||
|
||||
# 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
|
||||
|
||||
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
|
||||
|
||||
# Rules to build parse.c and parse.h - the outputs of lemon.
|
||||
#
|
||||
parse.h: parse.c
|
||||
|
||||
parse.c: $(TOP)\src\parse.y lemon.exe $(TOP)\addopcodes.awk
|
||||
del /Q parse.y parse.h parse.h.temp
|
||||
copy $(TOP)\src\parse.y .
|
||||
.\lemon.exe $(OPT_FEATURE_FLAGS) $(OPTS) parse.y
|
||||
move parse.h parse.h.temp
|
||||
$(NAWK) -f $(TOP)\addopcodes.awk parse.h.temp >parse.h
|
||||
|
||||
sqlite3.h: $(TOP)\src\sqlite.h.in $(TOP)\manifest.uuid $(TOP)\VERSION
|
||||
$(TCLSH_CMD) $(TOP)\tool\mksqlite3h.tcl $(TOP) >sqlite3.h
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
# Rules to build the extension objects.
|
||||
#
|
||||
icu.lo: $(TOP)\ext\icu\icu.c $(HDR) $(EXTHDR)
|
||||
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)\ext\icu\icu.c
|
||||
|
||||
fts2.lo: $(TOP)\ext\fts2\fts2.c $(HDR) $(EXTHDR)
|
||||
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)\ext\fts2\fts2.c
|
||||
|
||||
fts2_hash.lo: $(TOP)\ext\fts2\fts2_hash.c $(HDR) $(EXTHDR)
|
||||
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)\ext\fts2\fts2_hash.c
|
||||
|
||||
fts2_icu.lo: $(TOP)\ext\fts2\fts2_icu.c $(HDR) $(EXTHDR)
|
||||
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)\ext\fts2\fts2_icu.c
|
||||
|
||||
fts2_porter.lo: $(TOP)\ext\fts2\fts2_porter.c $(HDR) $(EXTHDR)
|
||||
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)\ext\fts2\fts2_porter.c
|
||||
|
||||
fts2_tokenizer.lo: $(TOP)\ext\fts2\fts2_tokenizer.c $(HDR) $(EXTHDR)
|
||||
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)\ext\fts2\fts2_tokenizer.c
|
||||
|
||||
fts2_tokenizer1.lo: $(TOP)\ext\fts2\fts2_tokenizer1.c $(HDR) $(EXTHDR)
|
||||
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)\ext\fts2\fts2_tokenizer1.c
|
||||
|
||||
fts3.lo: $(TOP)\ext\fts3\fts3.c $(HDR) $(EXTHDR)
|
||||
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)\ext\fts3\fts3.c
|
||||
|
||||
fts3_aux.lo: $(TOP)\ext\fts3\fts3_aux.c $(HDR) $(EXTHDR)
|
||||
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)\ext\fts3\fts3_aux.c
|
||||
|
||||
fts3_expr.lo: $(TOP)\ext\fts3\fts3_expr.c $(HDR) $(EXTHDR)
|
||||
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)\ext\fts3\fts3_expr.c
|
||||
|
||||
fts3_hash.lo: $(TOP)\ext\fts3\fts3_hash.c $(HDR) $(EXTHDR)
|
||||
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)\ext\fts3\fts3_hash.c
|
||||
|
||||
fts3_icu.lo: $(TOP)\ext\fts3\fts3_icu.c $(HDR) $(EXTHDR)
|
||||
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)\ext\fts3\fts3_icu.c
|
||||
|
||||
fts3_snippet.lo: $(TOP)\ext\fts3\fts3_snippet.c $(HDR) $(EXTHDR)
|
||||
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)\ext\fts3\fts3_snippet.c
|
||||
|
||||
fts3_porter.lo: $(TOP)\ext\fts3\fts3_porter.c $(HDR) $(EXTHDR)
|
||||
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)\ext\fts3\fts3_porter.c
|
||||
|
||||
fts3_tokenizer.lo: $(TOP)\ext\fts3\fts3_tokenizer.c $(HDR) $(EXTHDR)
|
||||
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)\ext\fts3\fts3_tokenizer.c
|
||||
|
||||
fts3_tokenizer1.lo: $(TOP)\ext\fts3\fts3_tokenizer1.c $(HDR) $(EXTHDR)
|
||||
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)\ext\fts3\fts3_tokenizer1.c
|
||||
|
||||
fts3_write.lo: $(TOP)\ext\fts3\fts3_write.c $(HDR) $(EXTHDR)
|
||||
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)\ext\fts3\fts3_write.c
|
||||
|
||||
rtree.lo: $(TOP)\ext\rtree\rtree.c $(HDR) $(EXTHDR)
|
||||
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)\ext\rtree\rtree.c
|
||||
|
||||
|
||||
# Rules to build the 'testfixture' application.
|
||||
#
|
||||
# If using the amalgamation, use sqlite3.c directly to build the test
|
||||
# fixture. Otherwise link against libsqlite3.lib. (This distinction is
|
||||
# necessary because the test fixture requires non-API symbols which are
|
||||
# hidden when the library is built via the amalgamation).
|
||||
#
|
||||
TESTFIXTURE_FLAGS = -DTCLSH=1 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1
|
||||
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE
|
||||
|
||||
TESTFIXTURE_SRC0 = $(TESTSRC2) libsqlite3.lib
|
||||
TESTFIXTURE_SRC1 = sqlite3.c
|
||||
!IF $(USE_AMALGAMATION)==0
|
||||
TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC0)
|
||||
!ELSE
|
||||
TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC1)
|
||||
!ENDIF
|
||||
|
||||
testfixture.exe: $(TESTFIXTURE_SRC) $(HDR)
|
||||
$(LTLINK) -DSQLITE_NO_SYNC=1 $(TESTFIXTURE_FLAGS) \
|
||||
-DBUILD_sqlite -I$(TCLINCDIR) \
|
||||
$(TESTFIXTURE_SRC) \
|
||||
/link $(LTLINKOPTS) /LIBPATH:$(TCLLIBDIR) $(LIBTCL) $(TLIBS)
|
||||
|
||||
fulltest: testfixture.exe sqlite3.exe
|
||||
.\testfixture.exe $(TOP)\test\all.test
|
||||
|
||||
soaktest: testfixture.exe sqlite3.exe
|
||||
.\testfixture.exe $(TOP)\test\all.test -soak=1
|
||||
|
||||
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.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)
|
||||
|
||||
clean:
|
||||
del /Q *.lo *.lib *.obj 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 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
|
||||
|
||||
#
|
||||
# Windows section
|
||||
#
|
||||
dll: sqlite3.dll
|
||||
|
||||
sqlite3.def: libsqlite3.lib
|
||||
echo EXPORTS >sqlite3.def
|
||||
dumpbin /all libsqlite3.lib \
|
||||
| $(NAWK) "/ 1 _sqlite3_/ { sub(/^.* _/,\"\");print }" \
|
||||
| sort >>sqlite3.def
|
||||
|
||||
sqlite3.dll: $(LIBOBJ) sqlite3.def
|
||||
link $(LTLINKOPTS) /DLL /DEF:sqlite3.def /OUT:$@ $(LIBOBJ)
|
||||
@ -75,6 +75,9 @@
|
||||
/* Define to 1 if you have the `usleep' function. */
|
||||
#undef HAVE_USLEEP
|
||||
|
||||
/* Define to 1 if you have the utime() library function. */
|
||||
#undef HAVE_UTIME
|
||||
|
||||
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
||||
*/
|
||||
#undef LT_OBJDIR
|
||||
|
||||
108
configure
vendored
108
configure
vendored
@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.62 for sqlite 3.7.6.2.
|
||||
# Generated by GNU Autoconf 2.62 for sqlite 3.7.7.1.
|
||||
#
|
||||
# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
|
||||
# 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
|
||||
@ -743,8 +743,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='sqlite'
|
||||
PACKAGE_TARNAME='sqlite'
|
||||
PACKAGE_VERSION='3.7.6.2'
|
||||
PACKAGE_STRING='sqlite 3.7.6.2'
|
||||
PACKAGE_VERSION='3.7.7.1'
|
||||
PACKAGE_STRING='sqlite 3.7.7.1'
|
||||
PACKAGE_BUGREPORT=''
|
||||
|
||||
# Factoring default headers for most tests.
|
||||
@ -1485,7 +1485,7 @@ if test "$ac_init_help" = "long"; then
|
||||
# Omit some internal or obsolete options to make the list less imposing.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
\`configure' configures sqlite 3.7.6.2 to adapt to many kinds of systems.
|
||||
\`configure' configures sqlite 3.7.7.1 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@ -1550,7 +1550,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of sqlite 3.7.6.2:";;
|
||||
short | recursive ) echo "Configuration of sqlite 3.7.7.1:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@ -1666,7 +1666,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
sqlite configure 3.7.6.2
|
||||
sqlite configure 3.7.7.1
|
||||
generated by GNU Autoconf 2.62
|
||||
|
||||
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
|
||||
@ -1680,7 +1680,7 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by sqlite $as_me 3.7.6.2, which was
|
||||
It was created by sqlite $as_me 3.7.7.1, which was
|
||||
generated by GNU Autoconf 2.62. Invocation command line was
|
||||
|
||||
$ $0 $@
|
||||
@ -12145,7 +12145,8 @@ done
|
||||
|
||||
|
||||
|
||||
for ac_func in usleep fdatasync localtime_r gmtime_r localtime_s
|
||||
|
||||
for ac_func in usleep fdatasync localtime_r gmtime_r localtime_s utime
|
||||
do
|
||||
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
|
||||
{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
|
||||
@ -13440,6 +13441,93 @@ fi
|
||||
|
||||
if test "${use_loadextension}" = "yes" ; then
|
||||
OPT_FEATURE_FLAGS=""
|
||||
{ $as_echo "$as_me:$LINENO: checking for library containing dlopen" >&5
|
||||
$as_echo_n "checking for library containing dlopen... " >&6; }
|
||||
if test "${ac_cv_search_dlopen+set}" = set; then
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
ac_func_search_save_LIBS=$LIBS
|
||||
cat >conftest.$ac_ext <<_ACEOF
|
||||
/* confdefs.h. */
|
||||
_ACEOF
|
||||
cat confdefs.h >>conftest.$ac_ext
|
||||
cat >>conftest.$ac_ext <<_ACEOF
|
||||
/* end confdefs.h. */
|
||||
|
||||
/* Override any GCC internal prototype to avoid an error.
|
||||
Use char because int might match the return type of a GCC
|
||||
builtin and then its argument prototype would still apply. */
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
char dlopen ();
|
||||
int
|
||||
main ()
|
||||
{
|
||||
return dlopen ();
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
for ac_lib in '' dl; do
|
||||
if test -z "$ac_lib"; then
|
||||
ac_res="none required"
|
||||
else
|
||||
ac_res=-l$ac_lib
|
||||
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
|
||||
fi
|
||||
rm -f conftest.$ac_objext conftest$ac_exeext
|
||||
if { (ac_try="$ac_link"
|
||||
case "(($ac_try" in
|
||||
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
|
||||
*) ac_try_echo=$ac_try;;
|
||||
esac
|
||||
eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
|
||||
$as_echo "$ac_try_echo") >&5
|
||||
(eval "$ac_link") 2>conftest.er1
|
||||
ac_status=$?
|
||||
grep -v '^ *+' conftest.er1 >conftest.err
|
||||
rm -f conftest.er1
|
||||
cat conftest.err >&5
|
||||
$as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
|
||||
(exit $ac_status); } && {
|
||||
test -z "$ac_c_werror_flag" ||
|
||||
test ! -s conftest.err
|
||||
} && test -s conftest$ac_exeext && {
|
||||
test "$cross_compiling" = yes ||
|
||||
$as_test_x conftest$ac_exeext
|
||||
}; then
|
||||
ac_cv_search_dlopen=$ac_res
|
||||
else
|
||||
$as_echo "$as_me: failed program was:" >&5
|
||||
sed 's/^/| /' conftest.$ac_ext >&5
|
||||
|
||||
|
||||
fi
|
||||
|
||||
rm -rf conftest.dSYM
|
||||
rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
|
||||
conftest$ac_exeext
|
||||
if test "${ac_cv_search_dlopen+set}" = set; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
if test "${ac_cv_search_dlopen+set}" = set; then
|
||||
:
|
||||
else
|
||||
ac_cv_search_dlopen=no
|
||||
fi
|
||||
rm conftest.$ac_ext
|
||||
LIBS=$ac_func_search_save_LIBS
|
||||
fi
|
||||
{ $as_echo "$as_me:$LINENO: result: $ac_cv_search_dlopen" >&5
|
||||
$as_echo "$ac_cv_search_dlopen" >&6; }
|
||||
ac_res=$ac_cv_search_dlopen
|
||||
if test "$ac_res" != no; then
|
||||
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
|
||||
|
||||
fi
|
||||
|
||||
else
|
||||
OPT_FEATURE_FLAGS="-DSQLITE_OMIT_LOAD_EXTENSION=1"
|
||||
fi
|
||||
@ -13942,7 +14030,7 @@ exec 6>&1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by sqlite $as_me 3.7.6.2, which was
|
||||
This file was extended by sqlite $as_me 3.7.7.1, which was
|
||||
generated by GNU Autoconf 2.62. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@ -13995,7 +14083,7 @@ Report bugs to <bug-autoconf@gnu.org>."
|
||||
_ACEOF
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_version="\\
|
||||
sqlite config.status 3.7.6.2
|
||||
sqlite config.status 3.7.7.1
|
||||
configured by $0, generated by GNU Autoconf 2.62,
|
||||
with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
|
||||
|
||||
|
||||
@ -127,7 +127,7 @@ AC_CHECK_HEADERS([sys/types.h stdlib.h stdint.h inttypes.h])
|
||||
#########
|
||||
# Figure out whether or not we have these functions
|
||||
#
|
||||
AC_CHECK_FUNCS([usleep fdatasync localtime_r gmtime_r localtime_s])
|
||||
AC_CHECK_FUNCS([usleep fdatasync localtime_r gmtime_r localtime_s utime])
|
||||
|
||||
#########
|
||||
# By default, we use the amalgamation (this may be changed below...)
|
||||
@ -619,6 +619,7 @@ AC_ARG_ENABLE(load-extension, AC_HELP_STRING([--enable-load-extension],
|
||||
[use_loadextension=$enableval],[use_loadextension=no])
|
||||
if test "${use_loadextension}" = "yes" ; then
|
||||
OPT_FEATURE_FLAGS=""
|
||||
AC_SEARCH_LIBS(dlopen, dl)
|
||||
else
|
||||
OPT_FEATURE_FLAGS="-DSQLITE_OMIT_LOAD_EXTENSION=1"
|
||||
fi
|
||||
|
||||
3103
ext/fts3/fts3.c
3103
ext/fts3/fts3.c
File diff suppressed because it is too large
Load Diff
@ -11,7 +11,6 @@
|
||||
******************************************************************************
|
||||
**
|
||||
*/
|
||||
|
||||
#ifndef _FTSINT_H
|
||||
#define _FTSINT_H
|
||||
|
||||
@ -19,6 +18,16 @@
|
||||
# define NDEBUG 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
** FTS4 is really an extension for FTS3. It is enabled using the
|
||||
** SQLITE_ENABLE_FTS3 macro. But to avoid confusion we also all
|
||||
** the SQLITE_ENABLE_FTS4 macro to serve as an alisse for SQLITE_ENABLE_FTS3.
|
||||
*/
|
||||
#if defined(SQLITE_ENABLE_FTS4) && !defined(SQLITE_ENABLE_FTS3)
|
||||
# define SQLITE_ENABLE_FTS3
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS3
|
||||
#include "sqlite3.h"
|
||||
#include "fts3_tokenizer.h"
|
||||
#include "fts3_hash.h"
|
||||
@ -47,12 +56,35 @@
|
||||
*/
|
||||
#define SizeofArray(X) ((int)(sizeof(X)/sizeof(X[0])))
|
||||
|
||||
|
||||
#ifndef MIN
|
||||
# define MIN(x,y) ((x)<(y)?(x):(y))
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Maximum length of a varint encoded integer. The varint format is different
|
||||
** from that used by SQLite, so the maximum length is 10, not 9.
|
||||
*/
|
||||
#define FTS3_VARINT_MAX 10
|
||||
|
||||
/*
|
||||
** FTS4 virtual tables may maintain multiple indexes - one index of all terms
|
||||
** in the document set and zero or more prefix indexes. All indexes are stored
|
||||
** as one or more b+-trees in the %_segments and %_segdir tables.
|
||||
**
|
||||
** It is possible to determine which index a b+-tree belongs to based on the
|
||||
** value stored in the "%_segdir.level" column. Given this value L, the index
|
||||
** that the b+-tree belongs to is (L<<10). In other words, all b+-trees with
|
||||
** level values between 0 and 1023 (inclusive) belong to index 0, all levels
|
||||
** between 1024 and 2047 to index 1, and so on.
|
||||
**
|
||||
** It is considered impossible for an index to use more than 1024 levels. In
|
||||
** theory though this may happen, but only after at least
|
||||
** (FTS3_MERGE_COUNT^1024) separate flushes of the pending-terms tables.
|
||||
*/
|
||||
#define FTS3_SEGDIR_MAXLEVEL 1024
|
||||
#define FTS3_SEGDIR_MAXLEVEL_STR "1024"
|
||||
|
||||
/*
|
||||
** The testcase() macro is only used by the amalgamation. If undefined,
|
||||
** make it a no-op.
|
||||
@ -92,22 +124,43 @@ typedef unsigned char u8; /* 1-byte (or larger) unsigned integer */
|
||||
typedef short int i16; /* 2-byte (or larger) signed integer */
|
||||
typedef unsigned int u32; /* 4-byte unsigned integer */
|
||||
typedef sqlite3_uint64 u64; /* 8-byte unsigned integer */
|
||||
|
||||
/*
|
||||
** Macro used to suppress compiler warnings for unused parameters.
|
||||
*/
|
||||
#define UNUSED_PARAMETER(x) (void)(x)
|
||||
|
||||
/*
|
||||
** Activate assert() only if SQLITE_TEST is enabled.
|
||||
*/
|
||||
#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
|
||||
# define NDEBUG 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The TESTONLY macro is used to enclose variable declarations or
|
||||
** other bits of code that are needed to support the arguments
|
||||
** within testcase() and assert() macros.
|
||||
*/
|
||||
#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
|
||||
# define TESTONLY(X) X
|
||||
#else
|
||||
# define TESTONLY(X)
|
||||
#endif
|
||||
|
||||
#endif /* SQLITE_AMALGAMATION */
|
||||
|
||||
typedef struct Fts3Table Fts3Table;
|
||||
typedef struct Fts3Cursor Fts3Cursor;
|
||||
typedef struct Fts3Expr Fts3Expr;
|
||||
typedef struct Fts3Phrase Fts3Phrase;
|
||||
typedef struct Fts3PhraseToken Fts3PhraseToken;
|
||||
|
||||
typedef struct Fts3Doclist Fts3Doclist;
|
||||
typedef struct Fts3SegFilter Fts3SegFilter;
|
||||
typedef struct Fts3DeferredToken Fts3DeferredToken;
|
||||
typedef struct Fts3SegReader Fts3SegReader;
|
||||
typedef struct Fts3SegReaderCursor Fts3SegReaderCursor;
|
||||
typedef struct Fts3MultiSegReader Fts3MultiSegReader;
|
||||
|
||||
/*
|
||||
** A connection to a fulltext index is an instance of the following
|
||||
@ -128,7 +181,7 @@ struct Fts3Table {
|
||||
/* Precompiled statements used by the implementation. Each of these
|
||||
** statements is run and reset within a single virtual table API call.
|
||||
*/
|
||||
sqlite3_stmt *aStmt[24];
|
||||
sqlite3_stmt *aStmt[27];
|
||||
|
||||
char *zReadExprlist;
|
||||
char *zWriteExprlist;
|
||||
@ -136,21 +189,43 @@ struct Fts3Table {
|
||||
int nNodeSize; /* Soft limit for node size */
|
||||
u8 bHasStat; /* True if %_stat table exists */
|
||||
u8 bHasDocsize; /* True if %_docsize table exists */
|
||||
u8 bDescIdx; /* True if doclists are in reverse order */
|
||||
int nPgsz; /* Page size for host database */
|
||||
char *zSegmentsTbl; /* Name of %_segments table */
|
||||
sqlite3_blob *pSegments; /* Blob handle open on %_segments table */
|
||||
|
||||
/* The following hash table is used to buffer pending index updates during
|
||||
/* TODO: Fix the first paragraph of this comment.
|
||||
**
|
||||
** The following hash table is used to buffer pending index updates during
|
||||
** transactions. Variable nPendingData estimates the memory size of the
|
||||
** pending data, including hash table overhead, but not malloc overhead.
|
||||
** When nPendingData exceeds nMaxPendingData, the buffer is flushed
|
||||
** automatically. Variable iPrevDocid is the docid of the most recently
|
||||
** inserted record.
|
||||
**
|
||||
** A single FTS4 table may have multiple full-text indexes. For each index
|
||||
** there is an entry in the aIndex[] array. Index 0 is an index of all the
|
||||
** terms that appear in the document set. Each subsequent index in aIndex[]
|
||||
** is an index of prefixes of a specific length.
|
||||
*/
|
||||
int nMaxPendingData;
|
||||
int nPendingData;
|
||||
sqlite_int64 iPrevDocid;
|
||||
Fts3Hash pendingTerms;
|
||||
int nIndex; /* Size of aIndex[] */
|
||||
struct Fts3Index {
|
||||
int nPrefix; /* Prefix length (0 for main terms index) */
|
||||
Fts3Hash hPending; /* Pending terms table for this index */
|
||||
} *aIndex;
|
||||
int nMaxPendingData; /* Max pending data before flush to disk */
|
||||
int nPendingData; /* Current bytes of pending data */
|
||||
sqlite_int64 iPrevDocid; /* Docid of most recently inserted document */
|
||||
|
||||
#if defined(SQLITE_DEBUG)
|
||||
/* 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
|
||||
** verifying the SQLite core.
|
||||
*/
|
||||
int inTransaction; /* True after xBegin but before xCommit/xRollback */
|
||||
int mxSavepoint; /* Largest valid xSavepoint integer */
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
@ -171,8 +246,10 @@ struct Fts3Cursor {
|
||||
char *pNextId; /* Pointer into the body of aDoclist */
|
||||
char *aDoclist; /* List of docids for full-text queries */
|
||||
int nDoclist; /* Size of buffer at aDoclist */
|
||||
u8 bDesc; /* True to sort in descending order */
|
||||
int eEvalmode; /* An FTS3_EVAL_XX constant */
|
||||
int nRowAvg; /* Average size of database rows, in pages */
|
||||
sqlite3_int64 nDoc; /* Documents in table */
|
||||
|
||||
int isMatchinfoNeeded; /* True when aMatchinfo[] needs filling in */
|
||||
u32 *aMatchinfo; /* Information about most recent match */
|
||||
@ -203,47 +280,70 @@ struct Fts3Cursor {
|
||||
#define FTS3_DOCID_SEARCH 1 /* Lookup by rowid on %_content table */
|
||||
#define FTS3_FULLTEXT_SEARCH 2 /* Full-text index search */
|
||||
|
||||
|
||||
struct Fts3Doclist {
|
||||
char *aAll; /* Array containing doclist (or NULL) */
|
||||
int nAll; /* Size of a[] in bytes */
|
||||
char *pNextDocid; /* Pointer to next docid */
|
||||
|
||||
sqlite3_int64 iDocid; /* Current docid (if pList!=0) */
|
||||
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
|
||||
** sequence. A single token is the base case and the most common case.
|
||||
** For a sequence of tokens contained in double-quotes (i.e. "one two three")
|
||||
** nToken will be the number of tokens in the string.
|
||||
**
|
||||
** The nDocMatch and nMatch variables contain data that may be used by the
|
||||
** matchinfo() function. They are populated when the full-text index is
|
||||
** queried for hits on the phrase. If one or more tokens in the phrase
|
||||
** are deferred, the nDocMatch and nMatch variables are populated based
|
||||
** on the assumption that the
|
||||
*/
|
||||
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 bFulltext; /* True if full-text index was used */
|
||||
Fts3SegReaderCursor *pSegcsr; /* Segment-reader for this token */
|
||||
|
||||
/* Variables above this point are populated when the expression is
|
||||
** parsed (by code in fts3_expr.c). Below this point the variables are
|
||||
** used when evaluating the expression. */
|
||||
Fts3DeferredToken *pDeferred; /* Deferred token object for this token */
|
||||
Fts3MultiSegReader *pSegcsr; /* Segment-reader for this token */
|
||||
};
|
||||
|
||||
struct Fts3Phrase {
|
||||
/* Variables populated by fts3_expr.c when parsing a MATCH expression */
|
||||
/* Cache of doclist for this phrase. */
|
||||
Fts3Doclist doclist;
|
||||
int bIncr; /* True if doclist is loaded incrementally */
|
||||
int iDoclistToken;
|
||||
|
||||
/* Variables below this point are populated by fts3_expr.c when parsing
|
||||
** a MATCH expression. Everything above is part of the evaluation phase.
|
||||
*/
|
||||
int nToken; /* Number of tokens in the phrase */
|
||||
int iColumn; /* Index of column this phrase must match */
|
||||
int isNot; /* Phrase prefixed by unary not (-) operator */
|
||||
Fts3PhraseToken aToken[1]; /* One entry for each token in the phrase */
|
||||
};
|
||||
|
||||
/*
|
||||
** A tree of these objects forms the RHS of a MATCH operator.
|
||||
**
|
||||
** If Fts3Expr.eType is either FTSQUERY_NEAR or FTSQUERY_PHRASE and isLoaded
|
||||
** is true, then aDoclist points to a malloced buffer, size nDoclist bytes,
|
||||
** containing the results of the NEAR or phrase query in FTS3 doclist
|
||||
** format. As usual, the initial "Length" field found in doclists stored
|
||||
** on disk is omitted from this buffer.
|
||||
** If Fts3Expr.eType is FTSQUERY_PHRASE and isLoaded is true, then aDoclist
|
||||
** points to a malloced buffer, size nDoclist bytes, containing the results
|
||||
** of this phrase query in FTS3 doclist format. As usual, the initial
|
||||
** "Length" field found in doclists stored on disk is omitted from this
|
||||
** buffer.
|
||||
**
|
||||
** Variable pCurrent always points to the start of a docid field within
|
||||
** aDoclist. Since the doclist is usually scanned in docid order, this can
|
||||
** be used to accelerate seeking to the required docid within the doclist.
|
||||
** Variable aMI is used only for FTSQUERY_NEAR nodes to store the global
|
||||
** matchinfo data. If it is not NULL, it points to an array of size nCol*3,
|
||||
** where nCol is the number of columns in the queried FTS table. The array
|
||||
** is populated as follows:
|
||||
**
|
||||
** aMI[iCol*3 + 0] = Undefined
|
||||
** aMI[iCol*3 + 1] = Number of occurrences
|
||||
** aMI[iCol*3 + 2] = Number of rows containing at least one instance
|
||||
**
|
||||
** The aMI array is allocated using sqlite3_malloc(). It should be freed
|
||||
** when the expression node is.
|
||||
*/
|
||||
struct Fts3Expr {
|
||||
int eType; /* One of the FTSQUERY_XXX values defined below */
|
||||
@ -253,12 +353,13 @@ struct Fts3Expr {
|
||||
Fts3Expr *pRight; /* Right operand */
|
||||
Fts3Phrase *pPhrase; /* Valid if eType==FTSQUERY_PHRASE */
|
||||
|
||||
int isLoaded; /* True if aDoclist/nDoclist are initialized. */
|
||||
char *aDoclist; /* Buffer containing doclist */
|
||||
int nDoclist; /* Size of aDoclist in bytes */
|
||||
/* The following are used by the fts3_eval.c module. */
|
||||
sqlite3_int64 iDocid; /* Current docid */
|
||||
u8 bEof; /* True this expression is at EOF already */
|
||||
u8 bStart; /* True if iDocid is valid */
|
||||
u8 bDeferred; /* True if this expression is entirely deferred */
|
||||
|
||||
sqlite3_int64 iCurrent;
|
||||
char *pCurrent;
|
||||
u32 *aMI;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -286,12 +387,12 @@ void sqlite3Fts3PendingTermsClear(Fts3Table *);
|
||||
int sqlite3Fts3Optimize(Fts3Table *);
|
||||
int sqlite3Fts3SegReaderNew(int, sqlite3_int64,
|
||||
sqlite3_int64, sqlite3_int64, const char *, int, Fts3SegReader**);
|
||||
int sqlite3Fts3SegReaderPending(Fts3Table*,const char*,int,int,Fts3SegReader**);
|
||||
int sqlite3Fts3SegReaderPending(
|
||||
Fts3Table*,int,const char*,int,int,Fts3SegReader**);
|
||||
void sqlite3Fts3SegReaderFree(Fts3SegReader *);
|
||||
int sqlite3Fts3SegReaderCost(Fts3Cursor *, Fts3SegReader *, int *);
|
||||
int sqlite3Fts3AllSegdirs(Fts3Table*, int, sqlite3_stmt **);
|
||||
int sqlite3Fts3AllSegdirs(Fts3Table*, int, int, sqlite3_stmt **);
|
||||
int sqlite3Fts3ReadLock(Fts3Table *);
|
||||
int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char **, int*);
|
||||
int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char **, int*, int*);
|
||||
|
||||
int sqlite3Fts3SelectDoctotal(Fts3Table *, sqlite3_stmt **);
|
||||
int sqlite3Fts3SelectDocsize(Fts3Table *, sqlite3_int64, sqlite3_stmt **);
|
||||
@ -300,17 +401,18 @@ void sqlite3Fts3FreeDeferredTokens(Fts3Cursor *);
|
||||
int sqlite3Fts3DeferToken(Fts3Cursor *, Fts3PhraseToken *, int);
|
||||
int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *);
|
||||
void sqlite3Fts3FreeDeferredDoclists(Fts3Cursor *);
|
||||
char *sqlite3Fts3DeferredDoclist(Fts3DeferredToken *, int *);
|
||||
void sqlite3Fts3SegmentsClose(Fts3Table *);
|
||||
|
||||
#define FTS3_SEGCURSOR_PENDING -1
|
||||
#define FTS3_SEGCURSOR_ALL -2
|
||||
/* Special values interpreted by sqlite3SegReaderCursor() */
|
||||
#define FTS3_SEGCURSOR_PENDING -1
|
||||
#define FTS3_SEGCURSOR_ALL -2
|
||||
|
||||
int sqlite3Fts3SegReaderStart(Fts3Table*, Fts3MultiSegReader*, Fts3SegFilter*);
|
||||
int sqlite3Fts3SegReaderStep(Fts3Table *, Fts3MultiSegReader *);
|
||||
void sqlite3Fts3SegReaderFinish(Fts3MultiSegReader *);
|
||||
|
||||
int sqlite3Fts3SegReaderStart(Fts3Table*, Fts3SegReaderCursor*, Fts3SegFilter*);
|
||||
int sqlite3Fts3SegReaderStep(Fts3Table *, Fts3SegReaderCursor *);
|
||||
void sqlite3Fts3SegReaderFinish(Fts3SegReaderCursor *);
|
||||
int sqlite3Fts3SegReaderCursor(
|
||||
Fts3Table *, int, const char *, int, int, int, Fts3SegReaderCursor *);
|
||||
Fts3Table *, int, int, const char *, int, int, int, Fts3MultiSegReader *);
|
||||
|
||||
/* Flags allowed as part of the 4th argument to SegmentReaderIterate() */
|
||||
#define FTS3_SEGMENT_REQUIRE_POS 0x00000001
|
||||
@ -327,7 +429,7 @@ struct Fts3SegFilter {
|
||||
int flags;
|
||||
};
|
||||
|
||||
struct Fts3SegReaderCursor {
|
||||
struct Fts3MultiSegReader {
|
||||
/* Used internally by sqlite3Fts3SegReaderXXX() calls */
|
||||
Fts3SegReader **apSegment; /* Array of Fts3SegReader objects */
|
||||
int nSegment; /* Size of apSegment array */
|
||||
@ -336,8 +438,12 @@ struct Fts3SegReaderCursor {
|
||||
char *aBuffer; /* Buffer to merge doclists in */
|
||||
int nBuffer; /* Allocated size of aBuffer[] in bytes */
|
||||
|
||||
/* Cost of running this iterator. Used by fts3.c only. */
|
||||
int nCost;
|
||||
int iColFilter; /* If >=0, filter for this column */
|
||||
int bRestart;
|
||||
|
||||
/* Used by fts3.c only. */
|
||||
int nCost; /* Cost of running iterator */
|
||||
int bLookup; /* True if a lookup of a single entry. */
|
||||
|
||||
/* Output values. Valid only after Fts3SegReaderStep() returns SQLITE_ROW. */
|
||||
char *zTerm; /* Pointer to term buffer */
|
||||
@ -352,11 +458,9 @@ int sqlite3Fts3GetVarint(const char *, sqlite_int64 *);
|
||||
int sqlite3Fts3GetVarint32(const char *, int *);
|
||||
int sqlite3Fts3VarintLen(sqlite3_uint64);
|
||||
void sqlite3Fts3Dequote(char *);
|
||||
void sqlite3Fts3DoclistPrev(int,char*,int,char**,sqlite3_int64*,int*,u8*);
|
||||
|
||||
char *sqlite3Fts3FindPositions(Fts3Expr *, sqlite3_int64, int);
|
||||
int sqlite3Fts3ExprLoadDoclist(Fts3Cursor *, Fts3Expr *);
|
||||
int sqlite3Fts3ExprLoadFtDoclist(Fts3Cursor *, Fts3Expr *, char **, int *);
|
||||
int sqlite3Fts3ExprNearTrim(Fts3Expr *, Fts3Expr *, int);
|
||||
int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *);
|
||||
|
||||
/* fts3_tokenizer.c */
|
||||
const char *sqlite3Fts3NextToken(const char *, int *);
|
||||
@ -380,9 +484,34 @@ int sqlite3Fts3ExprParse(sqlite3_tokenizer *,
|
||||
void sqlite3Fts3ExprFree(Fts3Expr *);
|
||||
#ifdef SQLITE_TEST
|
||||
int sqlite3Fts3ExprInitTestInterface(sqlite3 *db);
|
||||
int sqlite3Fts3InitTerm(sqlite3 *db);
|
||||
#endif
|
||||
|
||||
/* 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(
|
||||
Fts3Table *, Fts3MultiSegReader *, sqlite3_int64 *, char **, int *);
|
||||
char *sqlite3Fts3EvalPhrasePoslist(Fts3Cursor *, Fts3Expr *, int iCol);
|
||||
int sqlite3Fts3MsrOvfl(Fts3Cursor *, Fts3MultiSegReader *, int *);
|
||||
int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr);
|
||||
|
||||
int sqlite3Fts3DeferredTokenList(Fts3DeferredToken *, char **, int *);
|
||||
|
||||
#endif /* SQLITE_ENABLE_FTS3 */
|
||||
#endif /* _FTSINT_H */
|
||||
|
||||
@ -11,10 +11,9 @@
|
||||
******************************************************************************
|
||||
**
|
||||
*/
|
||||
|
||||
#include "fts3Int.h"
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
|
||||
|
||||
#include "fts3Int.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
@ -28,7 +27,7 @@ struct Fts3auxTable {
|
||||
|
||||
struct Fts3auxCursor {
|
||||
sqlite3_vtab_cursor base; /* Base class used by SQLite core */
|
||||
Fts3SegReaderCursor csr; /* Must be right after "base" */
|
||||
Fts3MultiSegReader csr; /* Must be right after "base" */
|
||||
Fts3SegFilter filter;
|
||||
char *zStop;
|
||||
int nStop; /* Byte-length of string zStop */
|
||||
@ -96,6 +95,7 @@ static int fts3auxConnectMethod(
|
||||
p->pFts3Tab->zDb = (char *)&p->pFts3Tab[1];
|
||||
p->pFts3Tab->zName = &p->pFts3Tab->zDb[nDb+1];
|
||||
p->pFts3Tab->db = db;
|
||||
p->pFts3Tab->nIndex = 1;
|
||||
|
||||
memcpy((char *)p->pFts3Tab->zDb, zDb, nDb);
|
||||
memcpy((char *)p->pFts3Tab->zName, zFts3, nFts3);
|
||||
@ -342,6 +342,7 @@ static int fts3auxFilterMethod(
|
||||
int isScan;
|
||||
|
||||
UNUSED_PARAMETER(nVal);
|
||||
UNUSED_PARAMETER(idxStr);
|
||||
|
||||
assert( idxStr==0 );
|
||||
assert( idxNum==FTS4AUX_EQ_CONSTRAINT || idxNum==0
|
||||
@ -375,7 +376,7 @@ static int fts3auxFilterMethod(
|
||||
if( pCsr->zStop==0 ) return SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
rc = sqlite3Fts3SegReaderCursor(pFts3, FTS3_SEGCURSOR_ALL,
|
||||
rc = sqlite3Fts3SegReaderCursor(pFts3, 0, FTS3_SEGCURSOR_ALL,
|
||||
pCsr->filter.zTerm, pCsr->filter.nTerm, 0, isScan, &pCsr->csr
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
@ -459,7 +460,10 @@ int sqlite3Fts3InitAux(sqlite3 *db){
|
||||
0, /* xCommit */
|
||||
0, /* xRollback */
|
||||
0, /* xFindFunction */
|
||||
0 /* xRename */
|
||||
0, /* xRename */
|
||||
0, /* xSavepoint */
|
||||
0, /* xRelease */
|
||||
0 /* xRollbackTo */
|
||||
};
|
||||
int rc; /* Return code */
|
||||
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
** syntax is relatively simple, the whole tokenizer/parser system is
|
||||
** hand-coded.
|
||||
*/
|
||||
#include "fts3Int.h"
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
|
||||
|
||||
/*
|
||||
@ -77,16 +78,24 @@ int sqlite3_fts3_enable_parentheses = 0;
|
||||
*/
|
||||
#define SQLITE_FTS3_DEFAULT_NEAR_PARAM 10
|
||||
|
||||
#include "fts3Int.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
** isNot:
|
||||
** This variable is used by function getNextNode(). When getNextNode() is
|
||||
** called, it sets ParseContext.isNot to true if the 'next node' is a
|
||||
** FTSQUERY_PHRASE with a unary "-" attached to it. i.e. "mysql" in the
|
||||
** FTS3 query "sqlite -mysql". Otherwise, ParseContext.isNot is set to
|
||||
** zero.
|
||||
*/
|
||||
typedef struct ParseContext ParseContext;
|
||||
struct ParseContext {
|
||||
sqlite3_tokenizer *pTokenizer; /* Tokenizer module */
|
||||
const char **azCol; /* Array of column names for fts3 table */
|
||||
int nCol; /* Number of entries in azCol[] */
|
||||
int iDefaultCol; /* Default column to query */
|
||||
int isNot; /* True if getNextNode() sees a unary - */
|
||||
sqlite3_context *pCtx; /* Write error message here */
|
||||
int nNest; /* Number of nested brackets */
|
||||
};
|
||||
@ -172,7 +181,7 @@ static int getNextToken(
|
||||
iEnd++;
|
||||
}
|
||||
if( !sqlite3_fts3_enable_parentheses && iStart>0 && z[iStart-1]=='-' ){
|
||||
pRet->pPhrase->isNot = 1;
|
||||
pParse->isNot = 1;
|
||||
}
|
||||
}
|
||||
nConsumed = iEnd;
|
||||
@ -224,36 +233,55 @@ static int getNextString(
|
||||
char *zTemp = 0;
|
||||
int nTemp = 0;
|
||||
|
||||
const int nSpace = sizeof(Fts3Expr) + sizeof(Fts3Phrase);
|
||||
int nToken = 0;
|
||||
|
||||
/* The final Fts3Expr data structure, including the Fts3Phrase,
|
||||
** Fts3PhraseToken structures token buffers are all stored as a single
|
||||
** allocation so that the expression can be freed with a single call to
|
||||
** sqlite3_free(). Setting this up requires a two pass approach.
|
||||
**
|
||||
** The first pass, in the block below, uses a tokenizer cursor to iterate
|
||||
** through the tokens in the expression. This pass uses fts3ReallocOrFree()
|
||||
** to assemble data in two dynamic buffers:
|
||||
**
|
||||
** Buffer p: Points to the Fts3Expr structure, followed by the Fts3Phrase
|
||||
** structure, followed by the array of Fts3PhraseToken
|
||||
** structures. This pass only populates the Fts3PhraseToken array.
|
||||
**
|
||||
** Buffer zTemp: Contains copies of all tokens.
|
||||
**
|
||||
** The second pass, in the block that begins "if( rc==SQLITE_DONE )" below,
|
||||
** appends buffer zTemp to buffer p, and fills in the Fts3Expr and Fts3Phrase
|
||||
** structures.
|
||||
*/
|
||||
rc = pModule->xOpen(pTokenizer, zInput, nInput, &pCursor);
|
||||
if( rc==SQLITE_OK ){
|
||||
int ii;
|
||||
pCursor->pTokenizer = pTokenizer;
|
||||
for(ii=0; rc==SQLITE_OK; ii++){
|
||||
const char *zToken;
|
||||
int nToken, iBegin, iEnd, iPos;
|
||||
rc = pModule->xNext(pCursor, &zToken, &nToken, &iBegin, &iEnd, &iPos);
|
||||
const char *zByte;
|
||||
int nByte, iBegin, iEnd, iPos;
|
||||
rc = pModule->xNext(pCursor, &zByte, &nByte, &iBegin, &iEnd, &iPos);
|
||||
if( rc==SQLITE_OK ){
|
||||
int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase);
|
||||
p = fts3ReallocOrFree(p, nByte+ii*sizeof(Fts3PhraseToken));
|
||||
zTemp = fts3ReallocOrFree(zTemp, nTemp + nToken);
|
||||
if( !p || !zTemp ){
|
||||
goto no_mem;
|
||||
}
|
||||
if( ii==0 ){
|
||||
memset(p, 0, nByte);
|
||||
p->pPhrase = (Fts3Phrase *)&p[1];
|
||||
}
|
||||
p->pPhrase = (Fts3Phrase *)&p[1];
|
||||
memset(&p->pPhrase->aToken[ii], 0, sizeof(Fts3PhraseToken));
|
||||
p->pPhrase->nToken = ii+1;
|
||||
p->pPhrase->aToken[ii].n = nToken;
|
||||
memcpy(&zTemp[nTemp], zToken, nToken);
|
||||
nTemp += nToken;
|
||||
if( iEnd<nInput && zInput[iEnd]=='*' ){
|
||||
p->pPhrase->aToken[ii].isPrefix = 1;
|
||||
}else{
|
||||
p->pPhrase->aToken[ii].isPrefix = 0;
|
||||
}
|
||||
Fts3PhraseToken *pToken;
|
||||
|
||||
p = fts3ReallocOrFree(p, nSpace + ii*sizeof(Fts3PhraseToken));
|
||||
if( !p ) goto no_mem;
|
||||
|
||||
zTemp = fts3ReallocOrFree(zTemp, nTemp + nByte);
|
||||
if( !zTemp ) goto no_mem;
|
||||
|
||||
assert( nToken==ii );
|
||||
pToken = &((Fts3Phrase *)(&p[1]))->aToken[ii];
|
||||
memset(pToken, 0, sizeof(Fts3PhraseToken));
|
||||
|
||||
memcpy(&zTemp[nTemp], zByte, nByte);
|
||||
nTemp += nByte;
|
||||
|
||||
pToken->n = nByte;
|
||||
pToken->isPrefix = (iEnd<nInput && zInput[iEnd]=='*');
|
||||
nToken = ii+1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -263,28 +291,24 @@ static int getNextString(
|
||||
|
||||
if( rc==SQLITE_DONE ){
|
||||
int jj;
|
||||
char *zNew = NULL;
|
||||
int nNew = 0;
|
||||
int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase);
|
||||
nByte += (p?(p->pPhrase->nToken-1):0) * sizeof(Fts3PhraseToken);
|
||||
p = fts3ReallocOrFree(p, nByte + nTemp);
|
||||
if( !p ){
|
||||
goto no_mem;
|
||||
}
|
||||
if( zTemp ){
|
||||
zNew = &(((char *)p)[nByte]);
|
||||
memcpy(zNew, zTemp, nTemp);
|
||||
}else{
|
||||
memset(p, 0, nByte+nTemp);
|
||||
}
|
||||
p->pPhrase = (Fts3Phrase *)&p[1];
|
||||
for(jj=0; jj<p->pPhrase->nToken; jj++){
|
||||
p->pPhrase->aToken[jj].z = &zNew[nNew];
|
||||
nNew += p->pPhrase->aToken[jj].n;
|
||||
}
|
||||
sqlite3_free(zTemp);
|
||||
char *zBuf = 0;
|
||||
|
||||
p = fts3ReallocOrFree(p, nSpace + nToken*sizeof(Fts3PhraseToken) + nTemp);
|
||||
if( !p ) goto no_mem;
|
||||
memset(p, 0, (char *)&(((Fts3Phrase *)&p[1])->aToken[0])-(char *)p);
|
||||
p->eType = FTSQUERY_PHRASE;
|
||||
p->pPhrase = (Fts3Phrase *)&p[1];
|
||||
p->pPhrase->iColumn = pParse->iDefaultCol;
|
||||
p->pPhrase->nToken = nToken;
|
||||
|
||||
zBuf = (char *)&p->pPhrase->aToken[nToken];
|
||||
memcpy(zBuf, zTemp, nTemp);
|
||||
sqlite3_free(zTemp);
|
||||
|
||||
for(jj=0; jj<p->pPhrase->nToken; jj++){
|
||||
p->pPhrase->aToken[jj].z = zBuf;
|
||||
zBuf += p->pPhrase->aToken[jj].n;
|
||||
}
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
@ -341,6 +365,8 @@ static int getNextNode(
|
||||
const char *zInput = z;
|
||||
int nInput = n;
|
||||
|
||||
pParse->isNot = 0;
|
||||
|
||||
/* Skip over any whitespace before checking for a keyword, an open or
|
||||
** close bracket, or a quoted string.
|
||||
*/
|
||||
@ -559,7 +585,7 @@ static int fts3ExprParse(
|
||||
int isPhrase;
|
||||
|
||||
if( !sqlite3_fts3_enable_parentheses
|
||||
&& p->eType==FTSQUERY_PHRASE && p->pPhrase->isNot
|
||||
&& p->eType==FTSQUERY_PHRASE && pParse->isNot
|
||||
){
|
||||
/* Create an implicit NOT operator. */
|
||||
Fts3Expr *pNot = fts3MallocZero(sizeof(Fts3Expr));
|
||||
@ -577,7 +603,6 @@ static int fts3ExprParse(
|
||||
p = pPrev;
|
||||
}else{
|
||||
int eType = p->eType;
|
||||
assert( eType!=FTSQUERY_PHRASE || !p->pPhrase->isNot );
|
||||
isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft);
|
||||
|
||||
/* The isRequirePhrase variable is set to true if a phrase or
|
||||
@ -740,9 +765,11 @@ int sqlite3Fts3ExprParse(
|
||||
*/
|
||||
void sqlite3Fts3ExprFree(Fts3Expr *p){
|
||||
if( p ){
|
||||
assert( p->eType==FTSQUERY_PHRASE || p->pPhrase==0 );
|
||||
sqlite3Fts3ExprFree(p->pLeft);
|
||||
sqlite3Fts3ExprFree(p->pRight);
|
||||
sqlite3_free(p->aDoclist);
|
||||
sqlite3Fts3EvalPhraseCleanup(p->pPhrase);
|
||||
sqlite3_free(p->aMI);
|
||||
sqlite3_free(p);
|
||||
}
|
||||
}
|
||||
@ -800,7 +827,7 @@ static char *exprToString(Fts3Expr *pExpr, char *zBuf){
|
||||
Fts3Phrase *pPhrase = pExpr->pPhrase;
|
||||
int i;
|
||||
zBuf = sqlite3_mprintf(
|
||||
"%zPHRASE %d %d", zBuf, pPhrase->iColumn, pPhrase->isNot);
|
||||
"%zPHRASE %d 0", zBuf, pPhrase->iColumn);
|
||||
for(i=0; zBuf && i<pPhrase->nToken; i++){
|
||||
zBuf = sqlite3_mprintf("%z %.*s%s", zBuf,
|
||||
pPhrase->aToken[i].n, pPhrase->aToken[i].z,
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
** * The FTS3 module is being built into the core of
|
||||
** SQLite (in which case SQLITE_ENABLE_FTS3 is defined).
|
||||
*/
|
||||
#include "fts3Int.h"
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
@ -10,10 +10,8 @@
|
||||
**
|
||||
*************************************************************************
|
||||
** This file implements a tokenizer for fts3 based on the ICU library.
|
||||
**
|
||||
** $Id: fts3_icu.c,v 1.3 2008/09/01 18:34:20 danielk1977 Exp $
|
||||
*/
|
||||
|
||||
#include "fts3Int.h"
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
|
||||
#ifdef SQLITE_ENABLE_ICU
|
||||
|
||||
|
||||
@ -22,9 +22,8 @@
|
||||
** * The FTS3 module is being built into the core of
|
||||
** SQLite (in which case SQLITE_ENABLE_FTS3 is defined).
|
||||
*/
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
|
||||
|
||||
#include "fts3Int.h"
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
@ -11,9 +11,9 @@
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "fts3Int.h"
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
|
||||
|
||||
#include "fts3Int.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
@ -176,51 +176,6 @@ static int fts3ExprIterate(
|
||||
return fts3ExprIterate2(pExpr, &iPhrase, x, pCtx);
|
||||
}
|
||||
|
||||
/*
|
||||
** The argument to this function is always a phrase node. Its doclist
|
||||
** (Fts3Expr.aDoclist[]) and the doclists associated with all phrase nodes
|
||||
** to the left of this one in the query tree have already been loaded.
|
||||
**
|
||||
** If this phrase node is part of a series of phrase nodes joined by
|
||||
** NEAR operators (and is not the left-most of said series), then elements are
|
||||
** removed from the phrases doclist consistent with the NEAR restriction. If
|
||||
** required, elements may be removed from the doclists of phrases to the
|
||||
** left of this one that are part of the same series of NEAR operator
|
||||
** connected phrases.
|
||||
**
|
||||
** If an OOM error occurs, SQLITE_NOMEM is returned. Otherwise, SQLITE_OK.
|
||||
*/
|
||||
static int fts3ExprNearTrim(Fts3Expr *pExpr){
|
||||
int rc = SQLITE_OK;
|
||||
Fts3Expr *pParent = pExpr->pParent;
|
||||
|
||||
assert( pExpr->eType==FTSQUERY_PHRASE );
|
||||
while( rc==SQLITE_OK
|
||||
&& pParent
|
||||
&& pParent->eType==FTSQUERY_NEAR
|
||||
&& pParent->pRight==pExpr
|
||||
){
|
||||
/* This expression (pExpr) is the right-hand-side of a NEAR operator.
|
||||
** Find the expression to the left of the same operator.
|
||||
*/
|
||||
int nNear = pParent->nNear;
|
||||
Fts3Expr *pLeft = pParent->pLeft;
|
||||
|
||||
if( pLeft->eType!=FTSQUERY_PHRASE ){
|
||||
assert( pLeft->eType==FTSQUERY_NEAR );
|
||||
assert( pLeft->pRight->eType==FTSQUERY_PHRASE );
|
||||
pLeft = pLeft->pRight;
|
||||
}
|
||||
|
||||
rc = sqlite3Fts3ExprNearTrim(pLeft, pExpr, nNear);
|
||||
|
||||
pExpr = pLeft;
|
||||
pParent = pExpr->pParent;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This is an fts3ExprIterate() callback used while loading the doclists
|
||||
** for each phrase into Fts3Expr.aDoclist[]/nDoclist. See also
|
||||
@ -228,20 +183,13 @@ static int fts3ExprNearTrim(Fts3Expr *pExpr){
|
||||
*/
|
||||
static int fts3ExprLoadDoclistsCb(Fts3Expr *pExpr, int iPhrase, void *ctx){
|
||||
int rc = SQLITE_OK;
|
||||
Fts3Phrase *pPhrase = pExpr->pPhrase;
|
||||
LoadDoclistCtx *p = (LoadDoclistCtx *)ctx;
|
||||
|
||||
UNUSED_PARAMETER(iPhrase);
|
||||
|
||||
p->nPhrase++;
|
||||
p->nToken += pExpr->pPhrase->nToken;
|
||||
|
||||
if( pExpr->isLoaded==0 ){
|
||||
rc = sqlite3Fts3ExprLoadDoclist(p->pCsr, pExpr);
|
||||
pExpr->isLoaded = 1;
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts3ExprNearTrim(pExpr);
|
||||
}
|
||||
}
|
||||
p->nToken += pPhrase->nToken;
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -415,7 +363,7 @@ static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){
|
||||
|
||||
pPhrase->nToken = pExpr->pPhrase->nToken;
|
||||
|
||||
pCsr = sqlite3Fts3FindPositions(pExpr, p->pCsr->iPrevId, p->iCol);
|
||||
pCsr = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol);
|
||||
if( pCsr ){
|
||||
int iFirst = 0;
|
||||
pPhrase->pList = pCsr;
|
||||
@ -772,26 +720,6 @@ static int fts3ColumnlistCount(char **ppCollist){
|
||||
return nEntry;
|
||||
}
|
||||
|
||||
static void fts3LoadColumnlistCounts(char **pp, u32 *aOut, int isGlobal){
|
||||
char *pCsr = *pp;
|
||||
while( *pCsr ){
|
||||
int nHit;
|
||||
sqlite3_int64 iCol = 0;
|
||||
if( *pCsr==0x01 ){
|
||||
pCsr++;
|
||||
pCsr += sqlite3Fts3GetVarint(pCsr, &iCol);
|
||||
}
|
||||
nHit = fts3ColumnlistCount(&pCsr);
|
||||
assert( nHit>0 );
|
||||
if( isGlobal ){
|
||||
aOut[iCol*3+1]++;
|
||||
}
|
||||
aOut[iCol*3] += nHit;
|
||||
}
|
||||
pCsr++;
|
||||
*pp = pCsr;
|
||||
}
|
||||
|
||||
/*
|
||||
** fts3ExprIterate() callback used to collect the "global" matchinfo stats
|
||||
** for a single query.
|
||||
@ -825,48 +753,9 @@ static int fts3ExprGlobalHitsCb(
|
||||
void *pCtx /* Pointer to MatchInfo structure */
|
||||
){
|
||||
MatchInfo *p = (MatchInfo *)pCtx;
|
||||
Fts3Cursor *pCsr = p->pCursor;
|
||||
char *pIter;
|
||||
char *pEnd;
|
||||
char *pFree = 0;
|
||||
u32 *aOut = &p->aMatchinfo[3*iPhrase*p->nCol];
|
||||
|
||||
assert( pExpr->isLoaded );
|
||||
assert( pExpr->eType==FTSQUERY_PHRASE );
|
||||
|
||||
if( pCsr->pDeferred ){
|
||||
Fts3Phrase *pPhrase = pExpr->pPhrase;
|
||||
int ii;
|
||||
for(ii=0; ii<pPhrase->nToken; ii++){
|
||||
if( pPhrase->aToken[ii].bFulltext ) break;
|
||||
}
|
||||
if( ii<pPhrase->nToken ){
|
||||
int nFree = 0;
|
||||
int rc = sqlite3Fts3ExprLoadFtDoclist(pCsr, pExpr, &pFree, &nFree);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
pIter = pFree;
|
||||
pEnd = &pFree[nFree];
|
||||
}else{
|
||||
int iCol; /* Column index */
|
||||
for(iCol=0; iCol<p->nCol; iCol++){
|
||||
aOut[iCol*3 + 1] = (u32)p->nDoc;
|
||||
aOut[iCol*3 + 2] = (u32)p->nDoc;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}else{
|
||||
pIter = pExpr->aDoclist;
|
||||
pEnd = &pExpr->aDoclist[pExpr->nDoclist];
|
||||
}
|
||||
|
||||
/* Fill in the global hit count matrix row for this phrase. */
|
||||
while( pIter<pEnd ){
|
||||
while( *pIter++ & 0x80 ); /* Skip past docid. */
|
||||
fts3LoadColumnlistCounts(&pIter, &aOut[1], 1);
|
||||
}
|
||||
|
||||
sqlite3_free(pFree);
|
||||
return SQLITE_OK;
|
||||
return sqlite3Fts3EvalPhraseStats(
|
||||
p->pCursor, pExpr, &p->aMatchinfo[3*iPhrase*p->nCol]
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -883,14 +772,13 @@ static int fts3ExprLocalHitsCb(
|
||||
int iStart = iPhrase * p->nCol * 3;
|
||||
int i;
|
||||
|
||||
for(i=0; i<p->nCol; i++) p->aMatchinfo[iStart+i*3] = 0;
|
||||
|
||||
if( pExpr->aDoclist ){
|
||||
for(i=0; i<p->nCol; i++){
|
||||
char *pCsr;
|
||||
|
||||
pCsr = sqlite3Fts3FindPositions(pExpr, p->pCursor->iPrevId, -1);
|
||||
pCsr = sqlite3Fts3EvalPhrasePoslist(p->pCursor, pExpr, i);
|
||||
if( pCsr ){
|
||||
fts3LoadColumnlistCounts(&pCsr, &p->aMatchinfo[iStart], 0);
|
||||
p->aMatchinfo[iStart+i*3] = fts3ColumnlistCount(&pCsr);
|
||||
}else{
|
||||
p->aMatchinfo[iStart+i*3] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -960,7 +848,7 @@ static int fts3MatchinfoSelectDoctotal(
|
||||
|
||||
a = sqlite3_column_blob(pStmt, 0);
|
||||
a += sqlite3Fts3GetVarint(a, &nDoc);
|
||||
if( nDoc==0 ) return SQLITE_CORRUPT;
|
||||
if( nDoc==0 ) return SQLITE_CORRUPT_VTAB;
|
||||
*pnDoc = (u32)nDoc;
|
||||
|
||||
if( paLen ) *paLen = a;
|
||||
@ -976,9 +864,8 @@ static int fts3MatchinfoSelectDoctotal(
|
||||
typedef struct LcsIterator LcsIterator;
|
||||
struct LcsIterator {
|
||||
Fts3Expr *pExpr; /* Pointer to phrase expression */
|
||||
char *pRead; /* Cursor used to iterate through aDoclist */
|
||||
int iPosOffset; /* Tokens count up to end of this phrase */
|
||||
int iCol; /* Current column number */
|
||||
char *pRead; /* Cursor used to iterate through aDoclist */
|
||||
int iPos; /* Current position */
|
||||
};
|
||||
|
||||
@ -1009,17 +896,10 @@ static int fts3LcsIteratorAdvance(LcsIterator *pIter){
|
||||
int rc = 0;
|
||||
|
||||
pRead += sqlite3Fts3GetVarint(pRead, &iRead);
|
||||
if( iRead==0 ){
|
||||
pIter->iCol = LCS_ITERATOR_FINISHED;
|
||||
if( iRead==0 || iRead==1 ){
|
||||
pRead = 0;
|
||||
rc = 1;
|
||||
}else{
|
||||
if( iRead==1 ){
|
||||
pRead += sqlite3Fts3GetVarint(pRead, &iRead);
|
||||
pIter->iCol = (int)iRead;
|
||||
pIter->iPos = pIter->iPosOffset;
|
||||
pRead += sqlite3Fts3GetVarint(pRead, &iRead);
|
||||
rc = 1;
|
||||
}
|
||||
pIter->iPos += (int)(iRead-2);
|
||||
}
|
||||
|
||||
@ -1051,42 +931,34 @@ static int fts3MatchinfoLcs(Fts3Cursor *pCsr, MatchInfo *pInfo){
|
||||
if( !aIter ) return SQLITE_NOMEM;
|
||||
memset(aIter, 0, sizeof(LcsIterator) * pCsr->nPhrase);
|
||||
(void)fts3ExprIterate(pCsr->pExpr, fts3MatchinfoLcsCb, (void*)aIter);
|
||||
|
||||
for(i=0; i<pInfo->nPhrase; i++){
|
||||
LcsIterator *pIter = &aIter[i];
|
||||
nToken -= pIter->pExpr->pPhrase->nToken;
|
||||
pIter->iPosOffset = nToken;
|
||||
pIter->pRead = sqlite3Fts3FindPositions(pIter->pExpr, pCsr->iPrevId, -1);
|
||||
if( pIter->pRead ){
|
||||
pIter->iPos = pIter->iPosOffset;
|
||||
fts3LcsIteratorAdvance(&aIter[i]);
|
||||
}else{
|
||||
pIter->iCol = LCS_ITERATOR_FINISHED;
|
||||
}
|
||||
}
|
||||
|
||||
for(iCol=0; iCol<pInfo->nCol; iCol++){
|
||||
int nLcs = 0; /* LCS value for this column */
|
||||
int nLive = 0; /* Number of iterators in aIter not at EOF */
|
||||
|
||||
/* Loop through the iterators in aIter[]. Set nLive to the number of
|
||||
** iterators that point to a position-list corresponding to column iCol.
|
||||
*/
|
||||
for(i=0; i<pInfo->nPhrase; i++){
|
||||
assert( aIter[i].iCol>=iCol );
|
||||
if( aIter[i].iCol==iCol ) nLive++;
|
||||
LcsIterator *pIt = &aIter[i];
|
||||
pIt->pRead = sqlite3Fts3EvalPhrasePoslist(pCsr, pIt->pExpr, iCol);
|
||||
if( pIt->pRead ){
|
||||
pIt->iPos = pIt->iPosOffset;
|
||||
fts3LcsIteratorAdvance(&aIter[i]);
|
||||
nLive++;
|
||||
}
|
||||
}
|
||||
|
||||
/* The following loop runs until all iterators in aIter[] have finished
|
||||
** iterating through positions in column iCol. Exactly one of the
|
||||
** iterators is advanced each time the body of the loop is run.
|
||||
*/
|
||||
while( nLive>0 ){
|
||||
LcsIterator *pAdv = 0; /* The iterator to advance by one position */
|
||||
int nThisLcs = 0; /* LCS for the current iterator positions */
|
||||
|
||||
for(i=0; i<pInfo->nPhrase; i++){
|
||||
LcsIterator *pIter = &aIter[i];
|
||||
if( iCol!=pIter->iCol ){
|
||||
if( pIter->pRead==0 ){
|
||||
/* This iterator is already at EOF for this column. */
|
||||
nThisLcs = 0;
|
||||
}else{
|
||||
@ -1152,7 +1024,7 @@ static int fts3MatchinfoValues(
|
||||
|
||||
case FTS3_MATCHINFO_NDOC:
|
||||
if( bGlobal ){
|
||||
sqlite3_int64 nDoc;
|
||||
sqlite3_int64 nDoc = 0;
|
||||
rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &nDoc, 0);
|
||||
pInfo->aMatchinfo[0] = (u32)nDoc;
|
||||
}
|
||||
@ -1408,6 +1280,7 @@ struct TermOffset {
|
||||
};
|
||||
|
||||
struct TermOffsetCtx {
|
||||
Fts3Cursor *pCsr;
|
||||
int iCol; /* Column of table to populate aTerm for */
|
||||
int iTerm;
|
||||
sqlite3_int64 iDocid;
|
||||
@ -1425,7 +1298,7 @@ static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){
|
||||
int iPos = 0; /* First position in position-list */
|
||||
|
||||
UNUSED_PARAMETER(iPhrase);
|
||||
pList = sqlite3Fts3FindPositions(pExpr, p->iDocid, p->iCol);
|
||||
pList = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol);
|
||||
nTerm = pExpr->pPhrase->nToken;
|
||||
if( pList ){
|
||||
fts3GetDeltaPosition(&pList, &iPos);
|
||||
@ -1478,6 +1351,7 @@ void sqlite3Fts3Offsets(
|
||||
goto offsets_out;
|
||||
}
|
||||
sCtx.iDocid = pCsr->iPrevId;
|
||||
sCtx.pCsr = pCsr;
|
||||
|
||||
/* Loop through the table columns, appending offset information to
|
||||
** string-buffer res for each column.
|
||||
@ -1553,7 +1427,7 @@ void sqlite3Fts3Offsets(
|
||||
);
|
||||
rc = fts3StringAppend(&res, aBuffer, -1);
|
||||
}else if( rc==SQLITE_DONE ){
|
||||
rc = SQLITE_CORRUPT;
|
||||
rc = SQLITE_CORRUPT_VTAB;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
369
ext/fts3/fts3_term.c
Normal file
369
ext/fts3/fts3_term.c
Normal file
@ -0,0 +1,369 @@
|
||||
/*
|
||||
** 2011 Jan 27
|
||||
**
|
||||
** 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 is not part of the production FTS code. It is only used for
|
||||
** testing. It contains a virtual table implementation that provides direct
|
||||
** access to the full-text index of an FTS table.
|
||||
*/
|
||||
|
||||
#include "fts3Int.h"
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
|
||||
#ifdef SQLITE_TEST
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct Fts3termTable Fts3termTable;
|
||||
typedef struct Fts3termCursor Fts3termCursor;
|
||||
|
||||
struct Fts3termTable {
|
||||
sqlite3_vtab base; /* Base class used by SQLite core */
|
||||
int iIndex; /* Index for Fts3Table.aIndex[] */
|
||||
Fts3Table *pFts3Tab;
|
||||
};
|
||||
|
||||
struct Fts3termCursor {
|
||||
sqlite3_vtab_cursor base; /* Base class used by SQLite core */
|
||||
Fts3MultiSegReader csr; /* Must be right after "base" */
|
||||
Fts3SegFilter filter;
|
||||
|
||||
int isEof; /* True if cursor is at EOF */
|
||||
char *pNext;
|
||||
|
||||
sqlite3_int64 iRowid; /* Current 'rowid' value */
|
||||
sqlite3_int64 iDocid; /* Current 'docid' value */
|
||||
int iCol; /* Current 'col' value */
|
||||
int iPos; /* Current 'pos' value */
|
||||
};
|
||||
|
||||
/*
|
||||
** Schema of the terms table.
|
||||
*/
|
||||
#define FTS3_TERMS_SCHEMA "CREATE TABLE x(term, docid, col, pos)"
|
||||
|
||||
/*
|
||||
** This function does all the work for both the xConnect and xCreate methods.
|
||||
** These tables have no persistent representation of their own, so xConnect
|
||||
** and xCreate are identical operations.
|
||||
*/
|
||||
static int fts3termConnectMethod(
|
||||
sqlite3 *db, /* Database connection */
|
||||
void *pCtx, /* Non-zero for an fts4prefix table */
|
||||
int argc, /* Number of elements in argv array */
|
||||
const char * const *argv, /* xCreate/xConnect argument array */
|
||||
sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */
|
||||
char **pzErr /* OUT: sqlite3_malloc'd error message */
|
||||
){
|
||||
char const *zDb; /* Name of database (e.g. "main") */
|
||||
char const *zFts3; /* Name of fts3 table */
|
||||
int nDb; /* Result of strlen(zDb) */
|
||||
int nFts3; /* Result of strlen(zFts3) */
|
||||
int nByte; /* Bytes of space to allocate here */
|
||||
int rc; /* value returned by declare_vtab() */
|
||||
Fts3termTable *p; /* Virtual table object to return */
|
||||
int iIndex = 0;
|
||||
|
||||
if( argc==5 ){
|
||||
iIndex = atoi(argv[4]);
|
||||
argc--;
|
||||
}
|
||||
|
||||
/* The user should specify a single argument - the name of an fts3 table. */
|
||||
if( argc!=4 ){
|
||||
*pzErr = sqlite3_mprintf(
|
||||
"wrong number of arguments to fts4term constructor"
|
||||
);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
zDb = argv[1];
|
||||
nDb = strlen(zDb);
|
||||
zFts3 = argv[3];
|
||||
nFts3 = strlen(zFts3);
|
||||
|
||||
rc = sqlite3_declare_vtab(db, FTS3_TERMS_SCHEMA);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
nByte = sizeof(Fts3termTable) + sizeof(Fts3Table) + nDb + nFts3 + 2;
|
||||
p = (Fts3termTable *)sqlite3_malloc(nByte);
|
||||
if( !p ) return SQLITE_NOMEM;
|
||||
memset(p, 0, nByte);
|
||||
|
||||
p->pFts3Tab = (Fts3Table *)&p[1];
|
||||
p->pFts3Tab->zDb = (char *)&p->pFts3Tab[1];
|
||||
p->pFts3Tab->zName = &p->pFts3Tab->zDb[nDb+1];
|
||||
p->pFts3Tab->db = db;
|
||||
p->pFts3Tab->nIndex = iIndex+1;
|
||||
p->iIndex = iIndex;
|
||||
|
||||
memcpy((char *)p->pFts3Tab->zDb, zDb, nDb);
|
||||
memcpy((char *)p->pFts3Tab->zName, zFts3, nFts3);
|
||||
sqlite3Fts3Dequote((char *)p->pFts3Tab->zName);
|
||||
|
||||
*ppVtab = (sqlite3_vtab *)p;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function does the work for both the xDisconnect and xDestroy methods.
|
||||
** These tables have no persistent representation of their own, so xDisconnect
|
||||
** and xDestroy are identical operations.
|
||||
*/
|
||||
static int fts3termDisconnectMethod(sqlite3_vtab *pVtab){
|
||||
Fts3termTable *p = (Fts3termTable *)pVtab;
|
||||
Fts3Table *pFts3 = p->pFts3Tab;
|
||||
int i;
|
||||
|
||||
/* Free any prepared statements held */
|
||||
for(i=0; i<SizeofArray(pFts3->aStmt); i++){
|
||||
sqlite3_finalize(pFts3->aStmt[i]);
|
||||
}
|
||||
sqlite3_free(pFts3->zSegmentsTbl);
|
||||
sqlite3_free(p);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#define FTS4AUX_EQ_CONSTRAINT 1
|
||||
#define FTS4AUX_GE_CONSTRAINT 2
|
||||
#define FTS4AUX_LE_CONSTRAINT 4
|
||||
|
||||
/*
|
||||
** xBestIndex - Analyze a WHERE and ORDER BY clause.
|
||||
*/
|
||||
static int fts3termBestIndexMethod(
|
||||
sqlite3_vtab *pVTab,
|
||||
sqlite3_index_info *pInfo
|
||||
){
|
||||
UNUSED_PARAMETER(pVTab);
|
||||
|
||||
/* This vtab naturally does "ORDER BY term, docid, col, pos". */
|
||||
if( pInfo->nOrderBy ){
|
||||
int i;
|
||||
for(i=0; i<pInfo->nOrderBy; i++){
|
||||
if( pInfo->aOrderBy[i].iColumn!=i || pInfo->aOrderBy[i].desc ) break;
|
||||
}
|
||||
if( i==pInfo->nOrderBy ){
|
||||
pInfo->orderByConsumed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** xOpen - Open a cursor.
|
||||
*/
|
||||
static int fts3termOpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){
|
||||
Fts3termCursor *pCsr; /* Pointer to cursor object to return */
|
||||
|
||||
UNUSED_PARAMETER(pVTab);
|
||||
|
||||
pCsr = (Fts3termCursor *)sqlite3_malloc(sizeof(Fts3termCursor));
|
||||
if( !pCsr ) return SQLITE_NOMEM;
|
||||
memset(pCsr, 0, sizeof(Fts3termCursor));
|
||||
|
||||
*ppCsr = (sqlite3_vtab_cursor *)pCsr;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** xClose - Close a cursor.
|
||||
*/
|
||||
static int fts3termCloseMethod(sqlite3_vtab_cursor *pCursor){
|
||||
Fts3Table *pFts3 = ((Fts3termTable *)pCursor->pVtab)->pFts3Tab;
|
||||
Fts3termCursor *pCsr = (Fts3termCursor *)pCursor;
|
||||
|
||||
sqlite3Fts3SegmentsClose(pFts3);
|
||||
sqlite3Fts3SegReaderFinish(&pCsr->csr);
|
||||
sqlite3_free(pCsr);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** xNext - Advance the cursor to the next row, if any.
|
||||
*/
|
||||
static int fts3termNextMethod(sqlite3_vtab_cursor *pCursor){
|
||||
Fts3termCursor *pCsr = (Fts3termCursor *)pCursor;
|
||||
Fts3Table *pFts3 = ((Fts3termTable *)pCursor->pVtab)->pFts3Tab;
|
||||
int rc;
|
||||
sqlite3_int64 v;
|
||||
|
||||
/* Increment our pretend rowid value. */
|
||||
pCsr->iRowid++;
|
||||
|
||||
/* Advance to the next term in the full-text index. */
|
||||
if( pCsr->csr.aDoclist==0
|
||||
|| pCsr->pNext>=&pCsr->csr.aDoclist[pCsr->csr.nDoclist-1]
|
||||
){
|
||||
rc = sqlite3Fts3SegReaderStep(pFts3, &pCsr->csr);
|
||||
if( rc!=SQLITE_ROW ){
|
||||
pCsr->isEof = 1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
pCsr->iCol = 0;
|
||||
pCsr->iPos = 0;
|
||||
pCsr->iDocid = 0;
|
||||
pCsr->pNext = pCsr->csr.aDoclist;
|
||||
|
||||
/* Read docid */
|
||||
pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &pCsr->iDocid);
|
||||
}
|
||||
|
||||
pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &v);
|
||||
if( v==0 ){
|
||||
pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &v);
|
||||
pCsr->iDocid += v;
|
||||
pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &v);
|
||||
pCsr->iCol = 0;
|
||||
pCsr->iPos = 0;
|
||||
}
|
||||
|
||||
if( v==1 ){
|
||||
pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &v);
|
||||
pCsr->iCol += v;
|
||||
pCsr->iPos = 0;
|
||||
pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &v);
|
||||
}
|
||||
|
||||
pCsr->iPos += (v - 2);
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** xFilter - Initialize a cursor to point at the start of its data.
|
||||
*/
|
||||
static int fts3termFilterMethod(
|
||||
sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */
|
||||
int idxNum, /* Strategy index */
|
||||
const char *idxStr, /* Unused */
|
||||
int nVal, /* Number of elements in apVal */
|
||||
sqlite3_value **apVal /* Arguments for the indexing scheme */
|
||||
){
|
||||
Fts3termCursor *pCsr = (Fts3termCursor *)pCursor;
|
||||
Fts3termTable *p = (Fts3termTable *)pCursor->pVtab;
|
||||
Fts3Table *pFts3 = p->pFts3Tab;
|
||||
int rc;
|
||||
|
||||
UNUSED_PARAMETER(nVal);
|
||||
UNUSED_PARAMETER(idxNum);
|
||||
UNUSED_PARAMETER(idxStr);
|
||||
UNUSED_PARAMETER(apVal);
|
||||
|
||||
assert( idxStr==0 && idxNum==0 );
|
||||
|
||||
/* In case this cursor is being reused, close and zero it. */
|
||||
testcase(pCsr->filter.zTerm);
|
||||
sqlite3Fts3SegReaderFinish(&pCsr->csr);
|
||||
memset(&pCsr->csr, 0, ((u8*)&pCsr[1]) - (u8*)&pCsr->csr);
|
||||
|
||||
pCsr->filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY;
|
||||
pCsr->filter.flags |= FTS3_SEGMENT_SCAN;
|
||||
|
||||
rc = sqlite3Fts3SegReaderCursor(pFts3, p->iIndex, FTS3_SEGCURSOR_ALL,
|
||||
pCsr->filter.zTerm, pCsr->filter.nTerm, 0, 1, &pCsr->csr
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts3SegReaderStart(pFts3, &pCsr->csr, &pCsr->filter);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts3termNextMethod(pCursor);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** xEof - Return true if the cursor is at EOF, or false otherwise.
|
||||
*/
|
||||
static int fts3termEofMethod(sqlite3_vtab_cursor *pCursor){
|
||||
Fts3termCursor *pCsr = (Fts3termCursor *)pCursor;
|
||||
return pCsr->isEof;
|
||||
}
|
||||
|
||||
/*
|
||||
** xColumn - Return a column value.
|
||||
*/
|
||||
static int fts3termColumnMethod(
|
||||
sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
|
||||
sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */
|
||||
int iCol /* Index of column to read value from */
|
||||
){
|
||||
Fts3termCursor *p = (Fts3termCursor *)pCursor;
|
||||
|
||||
assert( iCol>=0 && iCol<=3 );
|
||||
switch( iCol ){
|
||||
case 0:
|
||||
sqlite3_result_text(pCtx, p->csr.zTerm, p->csr.nTerm, SQLITE_TRANSIENT);
|
||||
break;
|
||||
case 1:
|
||||
sqlite3_result_int64(pCtx, p->iDocid);
|
||||
break;
|
||||
case 2:
|
||||
sqlite3_result_int64(pCtx, p->iCol);
|
||||
break;
|
||||
default:
|
||||
sqlite3_result_int64(pCtx, p->iPos);
|
||||
break;
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** xRowid - Return the current rowid for the cursor.
|
||||
*/
|
||||
static int fts3termRowidMethod(
|
||||
sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
|
||||
sqlite_int64 *pRowid /* OUT: Rowid value */
|
||||
){
|
||||
Fts3termCursor *pCsr = (Fts3termCursor *)pCursor;
|
||||
*pRowid = pCsr->iRowid;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Register the fts3term module with database connection db. Return SQLITE_OK
|
||||
** if successful or an error code if sqlite3_create_module() fails.
|
||||
*/
|
||||
int sqlite3Fts3InitTerm(sqlite3 *db){
|
||||
static const sqlite3_module fts3term_module = {
|
||||
0, /* iVersion */
|
||||
fts3termConnectMethod, /* xCreate */
|
||||
fts3termConnectMethod, /* xConnect */
|
||||
fts3termBestIndexMethod, /* xBestIndex */
|
||||
fts3termDisconnectMethod, /* xDisconnect */
|
||||
fts3termDisconnectMethod, /* xDestroy */
|
||||
fts3termOpenMethod, /* xOpen */
|
||||
fts3termCloseMethod, /* xClose */
|
||||
fts3termFilterMethod, /* xFilter */
|
||||
fts3termNextMethod, /* xNext */
|
||||
fts3termEofMethod, /* xEof */
|
||||
fts3termColumnMethod, /* xColumn */
|
||||
fts3termRowidMethod, /* xRowid */
|
||||
0, /* xUpdate */
|
||||
0, /* xBegin */
|
||||
0, /* xSync */
|
||||
0, /* xCommit */
|
||||
0, /* xRollback */
|
||||
0, /* xFindFunction */
|
||||
0 /* xRename */
|
||||
};
|
||||
int rc; /* Return code */
|
||||
|
||||
rc = sqlite3_create_module(db, "fts4term", &fts3term_module, 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */
|
||||
321
ext/fts3/fts3_test.c
Normal file
321
ext/fts3/fts3_test.c
Normal file
@ -0,0 +1,321 @@
|
||||
/*
|
||||
** 2011 Jun 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 is not part of the production FTS code. It is only used for
|
||||
** testing. It contains a Tcl command that can be used to test if a document
|
||||
** matches an FTS NEAR expression.
|
||||
*/
|
||||
|
||||
#include <tcl.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* Required so that the "ifdef SQLITE_ENABLE_FTS3" below works */
|
||||
#include "fts3Int.h"
|
||||
|
||||
#define NM_MAX_TOKEN 12
|
||||
|
||||
typedef struct NearPhrase NearPhrase;
|
||||
typedef struct NearDocument NearDocument;
|
||||
typedef struct NearToken NearToken;
|
||||
|
||||
struct NearDocument {
|
||||
int nToken; /* Length of token in bytes */
|
||||
NearToken *aToken; /* Token array */
|
||||
};
|
||||
|
||||
struct NearToken {
|
||||
int n; /* Length of token in bytes */
|
||||
const char *z; /* Pointer to token string */
|
||||
};
|
||||
|
||||
struct NearPhrase {
|
||||
int nNear; /* Preceding NEAR value */
|
||||
int nToken; /* Number of tokens in this phrase */
|
||||
NearToken aToken[NM_MAX_TOKEN]; /* Array of tokens in this phrase */
|
||||
};
|
||||
|
||||
static int nm_phrase_match(
|
||||
NearPhrase *p,
|
||||
NearToken *aToken
|
||||
){
|
||||
int ii;
|
||||
|
||||
for(ii=0; ii<p->nToken; ii++){
|
||||
NearToken *pToken = &p->aToken[ii];
|
||||
if( pToken->n>0 && pToken->z[pToken->n-1]=='*' ){
|
||||
if( aToken[ii].n<(pToken->n-1) ) return 0;
|
||||
if( memcmp(aToken[ii].z, pToken->z, pToken->n-1) ) return 0;
|
||||
}else{
|
||||
if( aToken[ii].n!=pToken->n ) return 0;
|
||||
if( memcmp(aToken[ii].z, pToken->z, pToken->n) ) return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nm_near_chain(
|
||||
int iDir, /* Direction to iterate through aPhrase[] */
|
||||
NearDocument *pDoc, /* Document to match against */
|
||||
int iPos, /* Position at which iPhrase was found */
|
||||
int nPhrase, /* Size of phrase array */
|
||||
NearPhrase *aPhrase, /* Phrase array */
|
||||
int iPhrase /* Index of phrase found */
|
||||
){
|
||||
int iStart;
|
||||
int iStop;
|
||||
int ii;
|
||||
int nNear;
|
||||
int iPhrase2;
|
||||
NearPhrase *p;
|
||||
NearPhrase *pPrev;
|
||||
|
||||
assert( iDir==1 || iDir==-1 );
|
||||
|
||||
if( iDir==1 ){
|
||||
if( (iPhrase+1)==nPhrase ) return 1;
|
||||
nNear = aPhrase[iPhrase+1].nNear;
|
||||
}else{
|
||||
if( iPhrase==0 ) return 1;
|
||||
nNear = aPhrase[iPhrase].nNear;
|
||||
}
|
||||
pPrev = &aPhrase[iPhrase];
|
||||
iPhrase2 = iPhrase+iDir;
|
||||
p = &aPhrase[iPhrase2];
|
||||
|
||||
iStart = iPos - nNear - p->nToken;
|
||||
iStop = iPos + nNear + pPrev->nToken;
|
||||
|
||||
if( iStart<0 ) iStart = 0;
|
||||
if( iStop > pDoc->nToken - p->nToken ) iStop = pDoc->nToken - p->nToken;
|
||||
|
||||
for(ii=iStart; ii<=iStop; ii++){
|
||||
if( nm_phrase_match(p, &pDoc->aToken[ii]) ){
|
||||
if( nm_near_chain(iDir, pDoc, ii, nPhrase, aPhrase, iPhrase2) ) return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nm_match_count(
|
||||
NearDocument *pDoc, /* Document to match against */
|
||||
int nPhrase, /* Size of phrase array */
|
||||
NearPhrase *aPhrase, /* Phrase array */
|
||||
int iPhrase /* Index of phrase to count matches for */
|
||||
){
|
||||
int nOcc = 0;
|
||||
int ii;
|
||||
NearPhrase *p = &aPhrase[iPhrase];
|
||||
|
||||
for(ii=0; ii<(pDoc->nToken + 1 - p->nToken); ii++){
|
||||
if( nm_phrase_match(p, &pDoc->aToken[ii]) ){
|
||||
/* Test forward NEAR chain (i>iPhrase) */
|
||||
if( 0==nm_near_chain(1, pDoc, ii, nPhrase, aPhrase, iPhrase) ) continue;
|
||||
|
||||
/* Test reverse NEAR chain (i<iPhrase) */
|
||||
if( 0==nm_near_chain(-1, pDoc, ii, nPhrase, aPhrase, iPhrase) ) continue;
|
||||
|
||||
/* This is a real match. Increment the counter. */
|
||||
nOcc++;
|
||||
}
|
||||
}
|
||||
|
||||
return nOcc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Tclcmd: fts3_near_match DOCUMENT EXPR ?OPTIONS?
|
||||
*/
|
||||
static int fts3_near_match_cmd(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int nTotal = 0;
|
||||
int rc;
|
||||
int ii;
|
||||
int nPhrase;
|
||||
NearPhrase *aPhrase = 0;
|
||||
NearDocument doc = {0, 0};
|
||||
Tcl_Obj **apDocToken;
|
||||
Tcl_Obj *pRet;
|
||||
Tcl_Obj *pPhrasecount = 0;
|
||||
|
||||
Tcl_Obj **apExprToken;
|
||||
int nExprToken;
|
||||
|
||||
/* Must have 3 or more arguments. */
|
||||
if( objc<3 || (objc%2)==0 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DOCUMENT EXPR ?OPTION VALUE?...");
|
||||
rc = TCL_ERROR;
|
||||
goto near_match_out;
|
||||
}
|
||||
|
||||
for(ii=3; ii<objc; ii+=2){
|
||||
enum NM_enum { NM_PHRASECOUNTS };
|
||||
struct TestnmSubcmd {
|
||||
char *zName;
|
||||
enum NM_enum eOpt;
|
||||
} aOpt[] = {
|
||||
{ "-phrasecountvar", NM_PHRASECOUNTS },
|
||||
{ 0, 0 }
|
||||
};
|
||||
int iOpt;
|
||||
if( Tcl_GetIndexFromObjStruct(
|
||||
interp, objv[ii], aOpt, sizeof(aOpt[0]), "option", 0, &iOpt)
|
||||
){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
switch( aOpt[iOpt].eOpt ){
|
||||
case NM_PHRASECOUNTS:
|
||||
pPhrasecount = objv[ii+1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rc = Tcl_ListObjGetElements(interp, objv[1], &doc.nToken, &apDocToken);
|
||||
if( rc!=TCL_OK ) goto near_match_out;
|
||||
doc.aToken = (NearToken *)ckalloc(doc.nToken*sizeof(NearToken));
|
||||
for(ii=0; ii<doc.nToken; ii++){
|
||||
doc.aToken[ii].z = Tcl_GetStringFromObj(apDocToken[ii], &doc.aToken[ii].n);
|
||||
}
|
||||
|
||||
rc = Tcl_ListObjGetElements(interp, objv[2], &nExprToken, &apExprToken);
|
||||
if( rc!=TCL_OK ) goto near_match_out;
|
||||
|
||||
nPhrase = (nExprToken + 1) / 2;
|
||||
aPhrase = (NearPhrase *)ckalloc(nPhrase * sizeof(NearPhrase));
|
||||
memset(aPhrase, 0, nPhrase * sizeof(NearPhrase));
|
||||
for(ii=0; ii<nPhrase; ii++){
|
||||
Tcl_Obj *pPhrase = apExprToken[ii*2];
|
||||
Tcl_Obj **apToken;
|
||||
int nToken;
|
||||
int jj;
|
||||
|
||||
rc = Tcl_ListObjGetElements(interp, pPhrase, &nToken, &apToken);
|
||||
if( rc!=TCL_OK ) goto near_match_out;
|
||||
if( nToken>NM_MAX_TOKEN ){
|
||||
Tcl_AppendResult(interp, "Too many tokens in phrase", 0);
|
||||
rc = TCL_ERROR;
|
||||
goto near_match_out;
|
||||
}
|
||||
for(jj=0; jj<nToken; jj++){
|
||||
NearToken *pT = &aPhrase[ii].aToken[jj];
|
||||
pT->z = Tcl_GetStringFromObj(apToken[jj], &pT->n);
|
||||
}
|
||||
aPhrase[ii].nToken = nToken;
|
||||
}
|
||||
for(ii=1; ii<nPhrase; ii++){
|
||||
Tcl_Obj *pNear = apExprToken[2*ii-1];
|
||||
int nNear;
|
||||
rc = Tcl_GetIntFromObj(interp, pNear, &nNear);
|
||||
if( rc!=TCL_OK ) goto near_match_out;
|
||||
aPhrase[ii].nNear = nNear;
|
||||
}
|
||||
|
||||
pRet = Tcl_NewObj();
|
||||
Tcl_IncrRefCount(pRet);
|
||||
for(ii=0; ii<nPhrase; ii++){
|
||||
int nOcc = nm_match_count(&doc, nPhrase, aPhrase, ii);
|
||||
Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(nOcc));
|
||||
nTotal += nOcc;
|
||||
}
|
||||
if( pPhrasecount ){
|
||||
Tcl_ObjSetVar2(interp, pPhrasecount, 0, pRet, 0);
|
||||
}
|
||||
Tcl_DecrRefCount(pRet);
|
||||
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(nTotal>0));
|
||||
|
||||
near_match_out:
|
||||
ckfree((char *)aPhrase);
|
||||
ckfree((char *)doc.aToken);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Tclcmd: fts3_configure_incr_load ?CHUNKSIZE THRESHOLD?
|
||||
**
|
||||
** Normally, FTS uses hard-coded values to determine the minimum doclist
|
||||
** size eligible for incremental loading, and the size of the chunks loaded
|
||||
** when a doclist is incrementally loaded. This command allows the built-in
|
||||
** values to be overridden for testing purposes.
|
||||
**
|
||||
** If present, the first argument is the chunksize in bytes to load doclists
|
||||
** in. The second argument is the minimum doclist size in bytes to use
|
||||
** incremental loading with.
|
||||
**
|
||||
** Whether or not the arguments are present, this command returns a list of
|
||||
** two integers - the initial chunksize and threshold when the command is
|
||||
** invoked. This can be used to restore the default behaviour after running
|
||||
** tests. For example:
|
||||
**
|
||||
** # Override incr-load settings for testing:
|
||||
** set cfg [fts3_configure_incr_load $new_chunksize $new_threshold]
|
||||
**
|
||||
** .... run tests ....
|
||||
**
|
||||
** # Restore initial incr-load settings:
|
||||
** eval fts3_configure_incr_load $cfg
|
||||
*/
|
||||
static int fts3_configure_incr_load_cmd(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
#ifdef SQLITE_ENABLE_FTS3
|
||||
extern int test_fts3_node_chunksize;
|
||||
extern int test_fts3_node_chunk_threshold;
|
||||
Tcl_Obj *pRet;
|
||||
|
||||
if( objc!=1 && objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "?CHUNKSIZE THRESHOLD?");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
pRet = Tcl_NewObj();
|
||||
Tcl_IncrRefCount(pRet);
|
||||
Tcl_ListObjAppendElement(
|
||||
interp, pRet, Tcl_NewIntObj(test_fts3_node_chunksize));
|
||||
Tcl_ListObjAppendElement(
|
||||
interp, pRet, Tcl_NewIntObj(test_fts3_node_chunk_threshold));
|
||||
|
||||
if( objc==3 ){
|
||||
int iArg1;
|
||||
int iArg2;
|
||||
if( Tcl_GetIntFromObj(interp, objv[1], &iArg1)
|
||||
|| Tcl_GetIntFromObj(interp, objv[2], &iArg2)
|
||||
){
|
||||
Tcl_DecrRefCount(pRet);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
test_fts3_node_chunksize = iArg1;
|
||||
test_fts3_node_chunk_threshold = iArg2;
|
||||
}
|
||||
|
||||
Tcl_SetObjResult(interp, pRet);
|
||||
Tcl_DecrRefCount(pRet);
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
int Sqlitetestfts3_Init(Tcl_Interp *interp){
|
||||
Tcl_CreateObjCommand(interp, "fts3_near_match", fts3_near_match_cmd, 0, 0);
|
||||
Tcl_CreateObjCommand(interp,
|
||||
"fts3_configure_incr_load", fts3_configure_incr_load_cmd, 0, 0
|
||||
);
|
||||
return TCL_OK;
|
||||
}
|
||||
@ -23,14 +23,14 @@
|
||||
** * The FTS3 module is being built into the core of
|
||||
** SQLite (in which case SQLITE_ENABLE_FTS3 is defined).
|
||||
*/
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
|
||||
|
||||
#include "sqlite3ext.h"
|
||||
#ifndef SQLITE_CORE
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#endif
|
||||
|
||||
#include "fts3Int.h"
|
||||
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
@ -156,7 +156,7 @@ int sqlite3Fts3InitTokenizer(
|
||||
){
|
||||
int rc;
|
||||
char *z = (char *)zArg;
|
||||
int n;
|
||||
int n = 0;
|
||||
char *zCopy;
|
||||
char *zEnd; /* Pointer to nul-term of zCopy */
|
||||
sqlite3_tokenizer_module *m;
|
||||
|
||||
@ -22,9 +22,8 @@
|
||||
** * The FTS3 module is being built into the core of
|
||||
** SQLite (in which case SQLITE_ENABLE_FTS3 is defined).
|
||||
*/
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
|
||||
|
||||
#include "fts3Int.h"
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -517,17 +517,17 @@ nodeAcquire(
|
||||
if( pNode && iNode==1 ){
|
||||
pRtree->iDepth = readInt16(pNode->zData);
|
||||
if( pRtree->iDepth>RTREE_MAX_DEPTH ){
|
||||
rc = SQLITE_CORRUPT;
|
||||
rc = SQLITE_CORRUPT_VTAB;
|
||||
}
|
||||
}
|
||||
|
||||
/* If no error has occurred so far, check if the "number of entries"
|
||||
** field on the node is too large. If so, set the return code to
|
||||
** SQLITE_CORRUPT.
|
||||
** SQLITE_CORRUPT_VTAB.
|
||||
*/
|
||||
if( pNode && rc==SQLITE_OK ){
|
||||
if( NCELL(pNode)>((pRtree->iNodeSize-4)/pRtree->nBytesPerCell) ){
|
||||
rc = SQLITE_CORRUPT;
|
||||
rc = SQLITE_CORRUPT_VTAB;
|
||||
}
|
||||
}
|
||||
|
||||
@ -535,7 +535,7 @@ nodeAcquire(
|
||||
if( pNode!=0 ){
|
||||
nodeHashInsert(pRtree, pNode);
|
||||
}else{
|
||||
rc = SQLITE_CORRUPT;
|
||||
rc = SQLITE_CORRUPT_VTAB;
|
||||
}
|
||||
*ppNode = pNode;
|
||||
}else{
|
||||
@ -1062,7 +1062,7 @@ static int nodeRowidIndex(
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
return SQLITE_CORRUPT;
|
||||
return SQLITE_CORRUPT_VTAB;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1421,7 +1421,7 @@ static float cellArea(Rtree *pRtree, RtreeCell *p){
|
||||
float area = 1.0;
|
||||
int ii;
|
||||
for(ii=0; ii<(pRtree->nDim*2); ii+=2){
|
||||
area = area * (DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii]));
|
||||
area = (float)(area * (DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii])));
|
||||
}
|
||||
return area;
|
||||
}
|
||||
@ -1434,7 +1434,7 @@ static float cellMargin(Rtree *pRtree, RtreeCell *p){
|
||||
float margin = 0.0;
|
||||
int ii;
|
||||
for(ii=0; ii<(pRtree->nDim*2); ii+=2){
|
||||
margin += (DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii]));
|
||||
margin += (float)(DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii]));
|
||||
}
|
||||
return margin;
|
||||
}
|
||||
@ -1519,7 +1519,7 @@ static float cellOverlap(
|
||||
o = 0.0;
|
||||
break;
|
||||
}else{
|
||||
o = o * (x2-x1);
|
||||
o = o * (float)(x2-x1);
|
||||
}
|
||||
}
|
||||
overlap += o;
|
||||
@ -1538,12 +1538,12 @@ static float cellOverlapEnlargement(
|
||||
int nCell,
|
||||
int iExclude
|
||||
){
|
||||
float before;
|
||||
float after;
|
||||
double before;
|
||||
double after;
|
||||
before = cellOverlap(pRtree, p, aCell, nCell, iExclude);
|
||||
cellUnion(pRtree, p, pInsert);
|
||||
after = cellOverlap(pRtree, p, aCell, nCell, iExclude);
|
||||
return after-before;
|
||||
return (float)(after-before);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1565,11 +1565,11 @@ static int ChooseLeaf(
|
||||
|
||||
for(ii=0; rc==SQLITE_OK && ii<(pRtree->iDepth-iHeight); ii++){
|
||||
int iCell;
|
||||
sqlite3_int64 iBest;
|
||||
sqlite3_int64 iBest = 0;
|
||||
|
||||
float fMinGrowth;
|
||||
float fMinArea;
|
||||
float fMinOverlap;
|
||||
float fMinGrowth = 0.0;
|
||||
float fMinArea = 0.0;
|
||||
float fMinOverlap = 0.0;
|
||||
|
||||
int nCell = NCELL(pNode);
|
||||
RtreeCell cell;
|
||||
@ -1657,7 +1657,7 @@ static int AdjustTree(
|
||||
int iCell;
|
||||
|
||||
if( nodeParentIndex(pRtree, p, &iCell) ){
|
||||
return SQLITE_CORRUPT;
|
||||
return SQLITE_CORRUPT_VTAB;
|
||||
}
|
||||
|
||||
nodeGetCell(pRtree, pParent, iCell, &cell);
|
||||
@ -1999,9 +1999,9 @@ static int splitNodeStartree(
|
||||
int *aSpare;
|
||||
int ii;
|
||||
|
||||
int iBestDim;
|
||||
int iBestSplit;
|
||||
float fBestMargin;
|
||||
int iBestDim = 0;
|
||||
int iBestSplit = 0;
|
||||
float fBestMargin = 0.0;
|
||||
|
||||
int nByte = (pRtree->nDim+1)*(sizeof(int*)+nCell*sizeof(int));
|
||||
|
||||
@ -2023,9 +2023,9 @@ static int splitNodeStartree(
|
||||
|
||||
for(ii=0; ii<pRtree->nDim; ii++){
|
||||
float margin = 0.0;
|
||||
float fBestOverlap;
|
||||
float fBestArea;
|
||||
int iBestLeft;
|
||||
float fBestOverlap = 0.0;
|
||||
float fBestArea = 0.0;
|
||||
int iBestLeft = 0;
|
||||
int nLeft;
|
||||
|
||||
for(
|
||||
@ -2329,7 +2329,7 @@ static int fixLeafParent(Rtree *pRtree, RtreeNode *pLeaf){
|
||||
}
|
||||
rc = sqlite3_reset(pRtree->pReadParent);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
if( rc==SQLITE_OK && !pChild->pParent ) rc = SQLITE_CORRUPT;
|
||||
if( rc==SQLITE_OK && !pChild->pParent ) rc = SQLITE_CORRUPT_VTAB;
|
||||
pChild = pChild->pParent;
|
||||
}
|
||||
return rc;
|
||||
@ -2340,7 +2340,7 @@ static int deleteCell(Rtree *, RtreeNode *, int, int);
|
||||
static int removeNode(Rtree *pRtree, RtreeNode *pNode, int iHeight){
|
||||
int rc;
|
||||
int rc2;
|
||||
RtreeNode *pParent;
|
||||
RtreeNode *pParent = 0;
|
||||
int iCell;
|
||||
|
||||
assert( pNode->nRef==1 );
|
||||
@ -2488,19 +2488,19 @@ static int Reinsert(
|
||||
}
|
||||
aOrder[ii] = ii;
|
||||
for(iDim=0; iDim<pRtree->nDim; iDim++){
|
||||
aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2]);
|
||||
aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2+1]);
|
||||
aCenterCoord[iDim] += (float)DCOORD(aCell[ii].aCoord[iDim*2]);
|
||||
aCenterCoord[iDim] += (float)DCOORD(aCell[ii].aCoord[iDim*2+1]);
|
||||
}
|
||||
}
|
||||
for(iDim=0; iDim<pRtree->nDim; iDim++){
|
||||
aCenterCoord[iDim] = aCenterCoord[iDim]/((float)nCell*2.0);
|
||||
aCenterCoord[iDim] = (float)(aCenterCoord[iDim]/((float)nCell*2.0));
|
||||
}
|
||||
|
||||
for(ii=0; ii<nCell; ii++){
|
||||
aDistance[ii] = 0.0;
|
||||
for(iDim=0; iDim<pRtree->nDim; iDim++){
|
||||
float coord = DCOORD(aCell[ii].aCoord[iDim*2+1]) -
|
||||
DCOORD(aCell[ii].aCoord[iDim*2]);
|
||||
float coord = (float)(DCOORD(aCell[ii].aCoord[iDim*2+1]) -
|
||||
DCOORD(aCell[ii].aCoord[iDim*2]));
|
||||
aDistance[ii] += (coord-aCenterCoord[iDim])*(coord-aCenterCoord[iDim]);
|
||||
}
|
||||
}
|
||||
@ -2599,10 +2599,10 @@ static int reinsertNodeContent(Rtree *pRtree, RtreeNode *pNode){
|
||||
/* Find a node to store this cell in. pNode->iNode currently contains
|
||||
** the height of the sub-tree headed by the cell.
|
||||
*/
|
||||
rc = ChooseLeaf(pRtree, &cell, pNode->iNode, &pInsert);
|
||||
rc = ChooseLeaf(pRtree, &cell, (int)pNode->iNode, &pInsert);
|
||||
if( rc==SQLITE_OK ){
|
||||
int rc2;
|
||||
rc = rtreeInsertCell(pRtree, pInsert, &cell, pNode->iNode);
|
||||
rc = rtreeInsertCell(pRtree, pInsert, &cell, (int)pNode->iNode);
|
||||
rc2 = nodeRelease(pRtree, pInsert);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = rc2;
|
||||
@ -2625,6 +2625,90 @@ static int newRowid(Rtree *pRtree, i64 *piRowid){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Remove the entry with rowid=iDelete from the r-tree structure.
|
||||
*/
|
||||
static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){
|
||||
int rc; /* Return code */
|
||||
RtreeNode *pLeaf; /* Leaf node containing record iDelete */
|
||||
int iCell; /* Index of iDelete cell in pLeaf */
|
||||
RtreeNode *pRoot; /* Root node of rtree structure */
|
||||
|
||||
|
||||
/* Obtain a reference to the root node to initialise Rtree.iDepth */
|
||||
rc = nodeAcquire(pRtree, 1, 0, &pRoot);
|
||||
|
||||
/* Obtain a reference to the leaf node that contains the entry
|
||||
** about to be deleted.
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = findLeafNode(pRtree, iDelete, &pLeaf);
|
||||
}
|
||||
|
||||
/* Delete the cell in question from the leaf node. */
|
||||
if( rc==SQLITE_OK ){
|
||||
int rc2;
|
||||
rc = nodeRowidIndex(pRtree, pLeaf, iDelete, &iCell);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = deleteCell(pRtree, pLeaf, iCell, 0);
|
||||
}
|
||||
rc2 = nodeRelease(pRtree, pLeaf);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = rc2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Delete the corresponding entry in the <rtree>_rowid table. */
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_int64(pRtree->pDeleteRowid, 1, iDelete);
|
||||
sqlite3_step(pRtree->pDeleteRowid);
|
||||
rc = sqlite3_reset(pRtree->pDeleteRowid);
|
||||
}
|
||||
|
||||
/* Check if the root node now has exactly one child. If so, remove
|
||||
** it, schedule the contents of the child for reinsertion and
|
||||
** reduce the tree height by one.
|
||||
**
|
||||
** This is equivalent to copying the contents of the child into
|
||||
** the root node (the operation that Gutman's paper says to perform
|
||||
** in this scenario).
|
||||
*/
|
||||
if( rc==SQLITE_OK && pRtree->iDepth>0 && NCELL(pRoot)==1 ){
|
||||
int rc2;
|
||||
RtreeNode *pChild;
|
||||
i64 iChild = nodeGetRowid(pRtree, pRoot, 0);
|
||||
rc = nodeAcquire(pRtree, iChild, pRoot, &pChild);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = removeNode(pRtree, pChild, pRtree->iDepth-1);
|
||||
}
|
||||
rc2 = nodeRelease(pRtree, pChild);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
if( rc==SQLITE_OK ){
|
||||
pRtree->iDepth--;
|
||||
writeInt16(pRoot->zData, pRtree->iDepth);
|
||||
pRoot->isDirty = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Re-insert the contents of any underfull nodes removed from the tree. */
|
||||
for(pLeaf=pRtree->pDeleted; pLeaf; pLeaf=pRtree->pDeleted){
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = reinsertNodeContent(pRtree, pLeaf);
|
||||
}
|
||||
pRtree->pDeleted = pLeaf->pNext;
|
||||
sqlite3_free(pLeaf);
|
||||
}
|
||||
|
||||
/* Release the reference to the root node. */
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = nodeRelease(pRtree, pRoot);
|
||||
}else{
|
||||
nodeRelease(pRtree, pRoot);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** The xUpdate method for rtree module virtual tables.
|
||||
*/
|
||||
@ -2636,103 +2720,25 @@ static int rtreeUpdate(
|
||||
){
|
||||
Rtree *pRtree = (Rtree *)pVtab;
|
||||
int rc = SQLITE_OK;
|
||||
RtreeCell cell; /* New cell to insert if nData>1 */
|
||||
int bHaveRowid = 0; /* Set to 1 after new rowid is determined */
|
||||
|
||||
rtreeReference(pRtree);
|
||||
|
||||
assert(nData>=1);
|
||||
|
||||
/* If azData[0] is not an SQL NULL value, it is the rowid of a
|
||||
** record to delete from the r-tree table. The following block does
|
||||
** just that.
|
||||
/* Constraint handling. A write operation on an r-tree table may return
|
||||
** SQLITE_CONSTRAINT for two reasons:
|
||||
**
|
||||
** 1. A duplicate rowid value, or
|
||||
** 2. The supplied data violates the "x2>=x1" constraint.
|
||||
**
|
||||
** In the first case, if the conflict-handling mode is REPLACE, then
|
||||
** the conflicting row can be removed before proceeding. In the second
|
||||
** case, SQLITE_CONSTRAINT must be returned regardless of the
|
||||
** conflict-handling mode specified by the user.
|
||||
*/
|
||||
if( sqlite3_value_type(azData[0])!=SQLITE_NULL ){
|
||||
i64 iDelete; /* The rowid to delete */
|
||||
RtreeNode *pLeaf; /* Leaf node containing record iDelete */
|
||||
int iCell; /* Index of iDelete cell in pLeaf */
|
||||
RtreeNode *pRoot;
|
||||
|
||||
/* Obtain a reference to the root node to initialise Rtree.iDepth */
|
||||
rc = nodeAcquire(pRtree, 1, 0, &pRoot);
|
||||
|
||||
/* Obtain a reference to the leaf node that contains the entry
|
||||
** about to be deleted.
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
iDelete = sqlite3_value_int64(azData[0]);
|
||||
rc = findLeafNode(pRtree, iDelete, &pLeaf);
|
||||
}
|
||||
|
||||
/* Delete the cell in question from the leaf node. */
|
||||
if( rc==SQLITE_OK ){
|
||||
int rc2;
|
||||
rc = nodeRowidIndex(pRtree, pLeaf, iDelete, &iCell);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = deleteCell(pRtree, pLeaf, iCell, 0);
|
||||
}
|
||||
rc2 = nodeRelease(pRtree, pLeaf);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = rc2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Delete the corresponding entry in the <rtree>_rowid table. */
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_int64(pRtree->pDeleteRowid, 1, iDelete);
|
||||
sqlite3_step(pRtree->pDeleteRowid);
|
||||
rc = sqlite3_reset(pRtree->pDeleteRowid);
|
||||
}
|
||||
|
||||
/* Check if the root node now has exactly one child. If so, remove
|
||||
** it, schedule the contents of the child for reinsertion and
|
||||
** reduce the tree height by one.
|
||||
**
|
||||
** This is equivalent to copying the contents of the child into
|
||||
** the root node (the operation that Gutman's paper says to perform
|
||||
** in this scenario).
|
||||
*/
|
||||
if( rc==SQLITE_OK && pRtree->iDepth>0 && NCELL(pRoot)==1 ){
|
||||
int rc2;
|
||||
RtreeNode *pChild;
|
||||
i64 iChild = nodeGetRowid(pRtree, pRoot, 0);
|
||||
rc = nodeAcquire(pRtree, iChild, pRoot, &pChild);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = removeNode(pRtree, pChild, pRtree->iDepth-1);
|
||||
}
|
||||
rc2 = nodeRelease(pRtree, pChild);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
if( rc==SQLITE_OK ){
|
||||
pRtree->iDepth--;
|
||||
writeInt16(pRoot->zData, pRtree->iDepth);
|
||||
pRoot->isDirty = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Re-insert the contents of any underfull nodes removed from the tree. */
|
||||
for(pLeaf=pRtree->pDeleted; pLeaf; pLeaf=pRtree->pDeleted){
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = reinsertNodeContent(pRtree, pLeaf);
|
||||
}
|
||||
pRtree->pDeleted = pLeaf->pNext;
|
||||
sqlite3_free(pLeaf);
|
||||
}
|
||||
|
||||
/* Release the reference to the root node. */
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = nodeRelease(pRtree, pRoot);
|
||||
}else{
|
||||
nodeRelease(pRtree, pRoot);
|
||||
}
|
||||
}
|
||||
|
||||
/* If the azData[] array contains more than one element, elements
|
||||
** (azData[2]..azData[argc-1]) contain a new record to insert into
|
||||
** the r-tree structure.
|
||||
*/
|
||||
if( rc==SQLITE_OK && nData>1 ){
|
||||
/* Insert a new record into the r-tree */
|
||||
RtreeCell cell;
|
||||
if( nData>1 ){
|
||||
int ii;
|
||||
RtreeNode *pLeaf;
|
||||
|
||||
/* Populate the cell.aCoord[] array. The first coordinate is azData[3]. */
|
||||
assert( nData==(pRtree->nDim*2 + 3) );
|
||||
@ -2756,18 +2762,49 @@ static int rtreeUpdate(
|
||||
}
|
||||
}
|
||||
|
||||
/* Figure out the rowid of the new row. */
|
||||
if( sqlite3_value_type(azData[2])==SQLITE_NULL ){
|
||||
rc = newRowid(pRtree, &cell.iRowid);
|
||||
}else{
|
||||
/* If a rowid value was supplied, check if it is already present in
|
||||
** the table. If so, the constraint has failed. */
|
||||
if( sqlite3_value_type(azData[2])!=SQLITE_NULL ){
|
||||
cell.iRowid = sqlite3_value_int64(azData[2]);
|
||||
sqlite3_bind_int64(pRtree->pReadRowid, 1, cell.iRowid);
|
||||
if( SQLITE_ROW==sqlite3_step(pRtree->pReadRowid) ){
|
||||
sqlite3_reset(pRtree->pReadRowid);
|
||||
rc = SQLITE_CONSTRAINT;
|
||||
goto constraint;
|
||||
if( sqlite3_value_type(azData[0])==SQLITE_NULL
|
||||
|| sqlite3_value_int64(azData[0])!=cell.iRowid
|
||||
){
|
||||
int steprc;
|
||||
sqlite3_bind_int64(pRtree->pReadRowid, 1, cell.iRowid);
|
||||
steprc = sqlite3_step(pRtree->pReadRowid);
|
||||
rc = sqlite3_reset(pRtree->pReadRowid);
|
||||
if( SQLITE_ROW==steprc ){
|
||||
if( sqlite3_vtab_on_conflict(pRtree->db)==SQLITE_REPLACE ){
|
||||
rc = rtreeDeleteRowid(pRtree, cell.iRowid);
|
||||
}else{
|
||||
rc = SQLITE_CONSTRAINT;
|
||||
goto constraint;
|
||||
}
|
||||
}
|
||||
}
|
||||
rc = sqlite3_reset(pRtree->pReadRowid);
|
||||
bHaveRowid = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* If azData[0] is not an SQL NULL value, it is the rowid of a
|
||||
** record to delete from the r-tree table. The following block does
|
||||
** just that.
|
||||
*/
|
||||
if( sqlite3_value_type(azData[0])!=SQLITE_NULL ){
|
||||
rc = rtreeDeleteRowid(pRtree, sqlite3_value_int64(azData[0]));
|
||||
}
|
||||
|
||||
/* If the azData[] array contains more than one element, elements
|
||||
** (azData[2]..azData[argc-1]) contain a new record to insert into
|
||||
** the r-tree structure.
|
||||
*/
|
||||
if( rc==SQLITE_OK && nData>1 ){
|
||||
/* Insert the new record into the r-tree */
|
||||
RtreeNode *pLeaf;
|
||||
|
||||
/* Figure out the rowid of the new row. */
|
||||
if( bHaveRowid==0 ){
|
||||
rc = newRowid(pRtree, &cell.iRowid);
|
||||
}
|
||||
*pRowid = cell.iRowid;
|
||||
|
||||
@ -2812,7 +2849,7 @@ static int rtreeRename(sqlite3_vtab *pVtab, const char *zNewName){
|
||||
}
|
||||
|
||||
static sqlite3_module rtreeModule = {
|
||||
0, /* iVersion */
|
||||
0, /* iVersion */
|
||||
rtreeCreate, /* xCreate - create a table */
|
||||
rtreeConnect, /* xConnect - connect to an existing table */
|
||||
rtreeBestIndex, /* xBestIndex - Determine search strategy */
|
||||
@ -2831,7 +2868,10 @@ static sqlite3_module rtreeModule = {
|
||||
0, /* xCommit - commit transaction */
|
||||
0, /* xRollback - rollback transaction */
|
||||
0, /* xFindFunction - function overloading */
|
||||
rtreeRename /* xRename - rename the table */
|
||||
rtreeRename, /* xRename - rename the table */
|
||||
0, /* xSavepoint */
|
||||
0, /* xRelease */
|
||||
0 /* xRollbackTo */
|
||||
};
|
||||
|
||||
static int rtreeSqlInit(
|
||||
@ -2951,7 +2991,7 @@ static int getNodeSize(
|
||||
int rc;
|
||||
char *zSql;
|
||||
if( isCreate ){
|
||||
int iPageSize;
|
||||
int iPageSize = 0;
|
||||
zSql = sqlite3_mprintf("PRAGMA %Q.page_size", pRtree->zDb);
|
||||
rc = getIntFromStmt(db, zSql, &iPageSize);
|
||||
if( rc==SQLITE_OK ){
|
||||
@ -3008,6 +3048,8 @@ static int rtreeInit(
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
|
||||
|
||||
/* Allocate the sqlite3_vtab structure */
|
||||
nDb = strlen(argv[1]);
|
||||
nName = strlen(argv[2]);
|
||||
|
||||
@ -31,6 +31,8 @@ source $testdir/tester.tcl
|
||||
# rtree-7.*: Test renaming an r-tree table.
|
||||
# rtree-8.*: Test constrained scans of r-tree data.
|
||||
#
|
||||
# rtree-12.*: Test that on-conflict clauses are supported.
|
||||
#
|
||||
|
||||
ifcapable !rtree {
|
||||
finish_test
|
||||
@ -416,4 +418,83 @@ do_test rtree-11.2 {
|
||||
}
|
||||
} {2}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test on-conflict clause handling.
|
||||
#
|
||||
db_delete_and_reopen
|
||||
do_execsql_test 12.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING rtree_i32(idx, x1, x2, y1, y2);
|
||||
INSERT INTO t1 VALUES(1, 1, 2, 3, 4);
|
||||
INSERT INTO t1 VALUES(2, 2, 3, 4, 5);
|
||||
INSERT INTO t1 VALUES(3, 3, 4, 5, 6);
|
||||
|
||||
CREATE TABLE source(idx, x1, x2, y1, y2);
|
||||
INSERT INTO source VALUES(5, 8, 8, 8, 8);
|
||||
INSERT INTO source VALUES(2, 7, 7, 7, 7);
|
||||
|
||||
}
|
||||
db_save_and_close
|
||||
foreach {tn sql_template testdata} {
|
||||
1 "INSERT %CONF% INTO t1 VALUES(2, 7, 7, 7, 7)" {
|
||||
ROLLBACK 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
|
||||
ABORT 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
IGNORE 0 0 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
FAIL 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
REPLACE 0 0 {1 1 2 3 4 2 7 7 7 7 3 3 4 5 6 4 4 5 6 7}
|
||||
}
|
||||
|
||||
2 "INSERT %CONF% INTO t1 SELECT * FROM source" {
|
||||
ROLLBACK 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
|
||||
ABORT 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
IGNORE 1 0 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7 5 8 8 8 8}
|
||||
FAIL 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7 5 8 8 8 8}
|
||||
REPLACE 1 0 {1 1 2 3 4 2 7 7 7 7 3 3 4 5 6 4 4 5 6 7 5 8 8 8 8}
|
||||
}
|
||||
|
||||
3 "UPDATE %CONF% t1 SET idx = 2 WHERE idx = 4" {
|
||||
ROLLBACK 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
|
||||
ABORT 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
IGNORE 1 0 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
FAIL 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
REPLACE 1 0 {1 1 2 3 4 2 4 5 6 7 3 3 4 5 6}
|
||||
}
|
||||
|
||||
3 "UPDATE %CONF% t1 SET idx = ((idx+1)%5)+1 WHERE idx > 2" {
|
||||
ROLLBACK 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
|
||||
ABORT 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
IGNORE 1 0 {1 1 2 3 4 2 2 3 4 5 4 4 5 6 7 5 3 4 5 6}
|
||||
FAIL 1 1 {1 1 2 3 4 2 2 3 4 5 4 4 5 6 7 5 3 4 5 6}
|
||||
REPLACE 1 0 {1 4 5 6 7 2 2 3 4 5 5 3 4 5 6}
|
||||
}
|
||||
|
||||
4 "INSERT %CONF% INTO t1 VALUES(2, 7, 6, 7, 7)" {
|
||||
ROLLBACK 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
|
||||
ABORT 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
IGNORE 0 0 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
FAIL 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
REPLACE 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
}
|
||||
|
||||
} {
|
||||
foreach {mode uses error data} $testdata {
|
||||
db_restore_and_reopen
|
||||
|
||||
set sql [string map [list %CONF% "OR $mode"] $sql_template]
|
||||
set testname "12.$tn.[string tolower $mode]"
|
||||
|
||||
execsql {
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES(4, 4, 5, 6, 7);
|
||||
}
|
||||
|
||||
set res(0) {0 {}}
|
||||
set res(1) {1 {constraint failed}}
|
||||
do_catchsql_test $testname.1 $sql $res($error)
|
||||
do_test $testname.2 [list sql_uses_stmt db $sql] $uses
|
||||
do_execsql_test $testname.3 { SELECT * FROM t1 ORDER BY idx } $data
|
||||
|
||||
do_test $testname.4 { rtree_check db t1 } 0
|
||||
db close
|
||||
}
|
||||
}
|
||||
finish_test
|
||||
|
||||
15
main.mk
15
main.mk
@ -54,8 +54,8 @@ LIBOBJ+= alter.o analyze.o attach.o auth.o \
|
||||
backup.o bitvec.o btmutex.o btree.o build.o \
|
||||
callback.o complete.o ctime.o date.o delete.o expr.o fault.o fkey.o \
|
||||
fts3.o fts3_aux.o fts3_expr.o fts3_hash.o fts3_icu.o fts3_porter.o \
|
||||
fts3_snippet.o fts3_tokenizer.o fts3_tokenizer1.o fts3_write.o \
|
||||
func.o global.o hash.o \
|
||||
fts3_snippet.o fts3_tokenizer.o fts3_tokenizer1.o \
|
||||
fts3_write.o func.o global.o hash.o \
|
||||
icu.o insert.o journal.o legacy.o loadext.o \
|
||||
main.o malloc.o mem0.o mem1.o mem2.o mem3.o mem5.o \
|
||||
memjournal.o \
|
||||
@ -220,6 +220,8 @@ SRC += \
|
||||
# Source code to the test files.
|
||||
#
|
||||
TESTSRC = \
|
||||
$(TOP)/ext/fts3/fts3_term.c \
|
||||
$(TOP)/ext/fts3/fts3_test.c \
|
||||
$(TOP)/src/test1.c \
|
||||
$(TOP)/src/test2.c \
|
||||
$(TOP)/src/test3.c \
|
||||
@ -548,13 +550,8 @@ threadtest: threadtest3$(EXE)
|
||||
|
||||
sqlite3_analyzer$(EXE): $(TOP)/src/tclsqlite.c sqlite3.c $(TESTSRC) \
|
||||
$(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
|
||||
$(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 \
|
||||
|
||||
262
manifest
262
manifest
@ -1,11 +1,12 @@
|
||||
C Version\s3.7.6.2
|
||||
D 2011-04-17T17:25:17.173
|
||||
C Release\scandidate\s3.7.7.1.
|
||||
D 2011-06-28T17:39:05.200
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in 7a4d9524721d40ef9ee26f93f9bd6a51dba106f2
|
||||
F Makefile.in c1d7a7f4fd8da6b1815032efca950e3d5125407e
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
F Makefile.msc 11082f65b452b908d93013292c17850378c39284
|
||||
F Makefile.vxworks c85ec1d8597fe2f7bc225af12ac1666e21379151
|
||||
F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6
|
||||
F VERSION 7fbd89ecdebc63a53a5cedadb822b74f5ae42a24
|
||||
F VERSION 5d587aa6adf86fd587768fe736359bd9f5e43890
|
||||
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
|
||||
F addopcodes.awk 17dc593f791f874d2c23a0f9360850ded0286531
|
||||
F art/2005osaward.gif 0d1851b2a7c1c9d0ccce545f3e14bca42d7fd248
|
||||
@ -20,10 +21,10 @@ F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90
|
||||
F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2
|
||||
F art/src_logo.gif 9341ef09f0e53cd44c0c9b6fc3c16f7f3d6c2ad9
|
||||
F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977
|
||||
F config.h.in 868fdb48c028421a203470e15c69ada15b9ba673
|
||||
F config.h.in 405a958bdb3af382a809dccb08a44694923ddd61
|
||||
F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55
|
||||
F configure eb34ce3c85e7658b2eab3439f0e5b5c10b166b13 x
|
||||
F configure.ac 87a3c71bbe9c925381c154413eea7f3cdc397244
|
||||
F configure b8e6049719c5f649670388236e2253183243b873 x
|
||||
F configure.ac 298a759c086e72c013da459c2aec02a104f4224f
|
||||
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
|
||||
F doc/lemon.html f0f682f50210928c07e562621c3b7e8ab912a538
|
||||
F doc/pager-invariants.txt 870107036470d7c419e93768676fae2f8749cf9e
|
||||
@ -61,29 +62,31 @@ F ext/fts2/mkfts2amal.tcl 974d5d438cb3f7c4a652639262f82418c1e4cff0
|
||||
F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a
|
||||
F ext/fts3/README.tokenizers 998756696647400de63d5ba60e9655036cb966e9
|
||||
F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
|
||||
F ext/fts3/fts3.c 5653c5654ac9b65bf3646af7e1d695c7e9b991a0
|
||||
F ext/fts3/fts3.c ca776037493d0081da70a6afc0df80f5ce347cb7
|
||||
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
|
||||
F ext/fts3/fts3Int.h 945926ea4b6a686c3e9834640a252d9870b7191e
|
||||
F ext/fts3/fts3_aux.c 9e931f55eed8498dafe7bc1160f10cbb1a652fdf
|
||||
F ext/fts3/fts3_expr.c 5f49e0deaf723724b08100bb3ff40aab02ad0c93
|
||||
F ext/fts3/fts3_hash.c 3c8f6387a4a7f5305588b203fa7c887d753e1f1c
|
||||
F ext/fts3/fts3Int.h fa493ccbad78a2c99ad1c984f651c0c202e68536
|
||||
F ext/fts3/fts3_aux.c 0ebfa7b86cf8ff6a0861605fcc63b83ec1b70691
|
||||
F ext/fts3/fts3_expr.c 23791de01b3a5d313d76e02befd2601d4096bc2b
|
||||
F ext/fts3/fts3_hash.c aad95afa01cf2a5ffaa448e4b0ab043880cd1efb
|
||||
F ext/fts3/fts3_hash.h 8331fb2206c609f9fc4c4735b9ab5ad6137c88ec
|
||||
F ext/fts3/fts3_icu.c ac494aed69835008185299315403044664bda295
|
||||
F ext/fts3/fts3_porter.c d61cfd81fb0fd8fbcb25adcaee0ba671aefaa5c2
|
||||
F ext/fts3/fts3_snippet.c e857c6a89d81d3b89df59f3b44b35c68d8ed5c62
|
||||
F ext/fts3/fts3_tokenizer.c 055f3dc7369585350b28db1ee0f3b214dca6724d
|
||||
F ext/fts3/fts3_icu.c 6c8f395cdf9e1e3afa7fadb7e523dbbf381c6dfa
|
||||
F ext/fts3/fts3_porter.c 8d946908f4812c005d3d33fcbe78418b1f4eb70c
|
||||
F ext/fts3/fts3_snippet.c 58b2ba2b934c1e2a2f6ac857d7f3c7e1a14b4532
|
||||
F ext/fts3/fts3_term.c a5457992723455a58804cb75c8cbd8978db5c2ef
|
||||
F ext/fts3/fts3_test.c c0aae3219df989d3e827d07846097adf8cb09945
|
||||
F ext/fts3/fts3_tokenizer.c 6089986ebcfc19d4b7aabd2b92773368efa4354f
|
||||
F ext/fts3/fts3_tokenizer.h 13ffd9fcb397fec32a05ef5cd9e0fa659bf3dbd3
|
||||
F ext/fts3/fts3_tokenizer1.c 6e5cbaa588924ac578263a598e4fb9f5c9bb179d
|
||||
F ext/fts3/fts3_write.c 813495ed106eb9461044e3c0374f4db69b37eb09
|
||||
F ext/fts3/fts3_tokenizer1.c 0dde8f307b8045565cf63797ba9acfaff1c50c68
|
||||
F ext/fts3/fts3_write.c 194829c8fd024a448fc899e5ff02a8ed06595529
|
||||
F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9
|
||||
F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100
|
||||
F ext/icu/README.txt bf8461d8cdc6b8f514c080e4e10dc3b2bbdfefa9
|
||||
F ext/icu/icu.c eb9ae1d79046bd7871aa97ee6da51eb770134b5a
|
||||
F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37
|
||||
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
|
||||
F ext/rtree/rtree.c f5fa951eba03c41d292958064604a033021acdee
|
||||
F ext/rtree/rtree.c b431c54d1ed05f04f2987e8a4fbb931acb40c312
|
||||
F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
|
||||
F ext/rtree/rtree1.test dbd4250ac0ad367a262eb9676f7e3080b0368206
|
||||
F ext/rtree/rtree1.test 28e1b8da4da98093ce3210187434dd760a8d89d8
|
||||
F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba
|
||||
F ext/rtree/rtree3.test a494da55c30ee0bc9b01a91c80c81b387b22d2dc
|
||||
F ext/rtree/rtree4.test 0061e6f464fd3dc6a79f82454c5a1c3dadbe42af
|
||||
@ -101,7 +104,7 @@ F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de
|
||||
F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F main.mk bd4e376deea4704b2bd9c77a4e6f0fa3de25c495
|
||||
F main.mk d799fb600383cee11fed58876a7cccb749dd2614
|
||||
F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a
|
||||
F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f
|
||||
F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac
|
||||
@ -114,37 +117,37 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
|
||||
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
|
||||
F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc
|
||||
F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad
|
||||
F src/alter.c 280f5c04b11b492703a342222b3de0a999445280
|
||||
F src/alter.c ac80a0f31189f8b4a524ebf661e47e84536ee7f5
|
||||
F src/analyze.c a425d62e8fa9ebcb4359ab84ff0c62c6563d2e2a
|
||||
F src/attach.c 7f97ca76ef2453440170929531a9c778267c0830
|
||||
F src/attach.c 12c6957996908edc31c96d7c68d4942c2474405f
|
||||
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
|
||||
F src/backup.c 986c15232757f2873dff35ee3b35cbf935fc573c
|
||||
F src/bitvec.c af50f1c8c0ff54d6bdb7a80e2fceca5a93670bef
|
||||
F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7
|
||||
F src/btree.c 6a9164af8a2ef4612ee30b253635a9bd8e5e1b1b
|
||||
F src/btree.h 11753dd46597a20702bca8746cb4caa4486a82b5
|
||||
F src/btree.c 8c46f0ab69ad9549c75a3a91fed87abdaa743e2f
|
||||
F src/btree.h f5d775cd6cfc7ac32a2535b70e8d2af48ef5f2ce
|
||||
F src/btreeInt.h 67978c014fa4f7cc874032dd3aacadd8db656bc3
|
||||
F src/build.c f09c46c66a1e7668c6ee25c9a2518aaa6842044c
|
||||
F src/build.c 5a428625d21ad409514afb40ad083bee25dd957a
|
||||
F src/callback.c 0425c6320730e6d3981acfb9202c1bed9016ad1a
|
||||
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
|
||||
F src/ctime.c 7deec4534f3b5a0c3b4a4cbadf809d321f64f9c4
|
||||
F src/date.c 1548fdac51377e4e7833251de878b4058c148e1b
|
||||
F src/delete.c 7a24fcc9a31664d145acb97ce56b6d9f249a25e4
|
||||
F src/expr.c e3cf0957c6b8faaaf7386a3bc69e53c0dc9705be
|
||||
F src/date.c d3c11de76392ea62637bfac0f4655889fc2f5a85
|
||||
F src/delete.c 4925f9121525fc871f5d8d13c1f7dcc91abb38bb
|
||||
F src/expr.c ab46ab0f0c44979a8164ca31728d7d10ae5e8106
|
||||
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
||||
F src/fkey.c a43ba8a005fb5efd1deeee06853e3a6120d46a91
|
||||
F src/func.c 3a8cb2fb2de3e3aed7f39106daf4878d9d17fcce
|
||||
F src/global.c 02335177cf6946fe5525c6f0755cf181140debf3
|
||||
F src/fkey.c 9fabba17a4d4778dc660f0cb9d781fc86d7b9d41
|
||||
F src/func.c 59bb046d7e3df1ab512ac339ccb0a6f996a17cb7
|
||||
F src/global.c c70a46f28680f8d7c097dbc0430ccf3b932e90b0
|
||||
F src/hash.c 458488dcc159c301b8e7686280ab209f1fb915af
|
||||
F src/hash.h 2894c932d84d9f892d4b4023a75e501f83050970
|
||||
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
|
||||
F src/insert.c acfb89fe4a73d703e425e167bfcc72985f4299ae
|
||||
F src/insert.c 3eea5a53d2644116fb865afaa4699fabe62b441c
|
||||
F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e
|
||||
F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
|
||||
F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e
|
||||
F src/loadext.c 3ae0d52da013a6326310655be6473fd472347b85
|
||||
F src/main.c a8571665d43ff18f89a49d47a281605ce5ea825e
|
||||
F src/malloc.c 788f2ed928786dfe305b6783d551d6b1a9080976
|
||||
F src/main.c fa654e1802e14a5f6c09c37971c3b69c1001d9c8
|
||||
F src/malloc.c 591aedb20ae40813f1045f2ef253438a334775d9
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c 00bd8265c81abb665c48fea1e0c234eb3b922206
|
||||
F src/mem2.c e307323e86b5da1853d7111b68fd6b84ad6f09cf
|
||||
@ -162,32 +165,32 @@ F src/os.c 22ac61d06e72a0dac900400147333b07b13d8e1d
|
||||
F src/os.h 9dbed8c2b9c1f2f2ebabc09e49829d4777c26bf9
|
||||
F src/os_common.h a8f95b81eca8a1ab8593d23e94f8a35f35d4078f
|
||||
F src/os_os2.c 4a75888ba3dfc820ad5e8177025972d74d7f2440
|
||||
F src/os_unix.c 4389231d079f8e323d9e24db6658bfd31f6d27a2
|
||||
F src/os_win.c 24d72407a90551969744cf9bcbb1b4c72c5fa845
|
||||
F src/pager.c 055239dcdfe12b3f5d97f6f01f85da01e2d6d912
|
||||
F src/os_unix.c 07acbb3e074e52b48a4248c06f66c9a91db1a0ce
|
||||
F src/os_win.c eafcd6b91cf204a7ef29ac1ef2a1b7132e132e58
|
||||
F src/pager.c 120550e7ef01dafaa2cbb4a0528c0d87c8f12b41
|
||||
F src/pager.h 3f8c783de1d4706b40b1ac15b64f5f896bcc78d1
|
||||
F src/parse.y 12b7ebd61ea54f0e1b1083ff69cc2c8ce9353d58
|
||||
F src/pcache.c 09d38c44ab275db581f7a2f6ff8b9bc7f8c0faaa
|
||||
F src/pcache.c 49e718c095810c6b3334e3a6d89970aceaddefce
|
||||
F src/pcache.h c683390d50f856d4cd8e24342ae62027d1bb6050
|
||||
F src/pcache1.c d548e31beafa792d1994b663a29a5303569efc4e
|
||||
F src/pragma.c 49c90ab27a4339d4b5bc0b03c08cbcf20ed8d454
|
||||
F src/pcache1.c 912bd5687d6df344698d8e69560f347b6e21c18a
|
||||
F src/pragma.c ebcd20f1e654f5cb3aeef864ed69c4697719fbaa
|
||||
F src/prepare.c e64261559a3187698a3e7e6c8b001a4f4f98dab4
|
||||
F src/printf.c 585a36b6a963df832cfb69505afa3a34ed5ef8a1
|
||||
F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
|
||||
F src/resolve.c 1c0f32b64f8e3f555fe1f732f9d6f501a7f05706
|
||||
F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
|
||||
F src/select.c 649a6f10f7eb7b52a5a28847773cb9968a828ae8
|
||||
F src/shell.c 72e7e176bf46d5c6518d15ac4ad6847c4bb5df79
|
||||
F src/sqlite.h.in 4d28db70c37a1b17942820308eb59f211140da43
|
||||
F src/select.c d9d440809025a58547e39f4f268c2a296bfb56ff
|
||||
F src/shell.c 0e0173b3e79d956368013e759f084caa7995ecb1
|
||||
F src/sqlite.h.in 4b7255c10d39c5faf089dbd29cde7c367ff39f1f
|
||||
F src/sqlite3ext.h c90bd5507099f62043832d73f6425d8d5c5da754
|
||||
F src/sqliteInt.h ac8f3f5846275c634f6649969304a9e97f6f9854
|
||||
F src/sqliteInt.h 7b72f7c6929757c0678d94524dbc7e7e3d6dc398
|
||||
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
||||
F src/status.c 7ac64842c86cec2fc1a1d0e5c16d3beb8ad332bf
|
||||
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
|
||||
F src/tclsqlite.c 501c9a200fd998a268be475be5858febc90b725b
|
||||
F src/test1.c 9ca440e80e16e53920904a0a5ac7feffb9b2c9a1
|
||||
F src/tclsqlite.c 5db825be61708b1a2b3f8f6e185e9b753829acef
|
||||
F src/test1.c 8eef932f1024f1076a8adb8a6f5d9c5267c9e969
|
||||
F src/test2.c 80d323d11e909cf0eb1b6fbb4ac22276483bcf31
|
||||
F src/test3.c 056093cfef69ff4227a6bdb9108564dc7f45e4bc
|
||||
F src/test3.c 124ff9735fb6bb7d41de180d6bac90e7b1509432
|
||||
F src/test4.c d1e5a5e904d4b444cf572391fdcb017638e36ff7
|
||||
F src/test5.c e1a19845625144caf038031234a12185e40d315c
|
||||
F src/test6.c c7256cc21d2409486d094277d5b017e8eced44ba
|
||||
@ -198,8 +201,8 @@ F src/test_async.c 0612a752896fad42d55c3999a5122af10dcf22ad
|
||||
F src/test_autoext.c 30e7bd98ab6d70a62bb9ba572e4c7df347fe645e
|
||||
F src/test_backup.c c129c91127e9b46e335715ae2e75756e25ba27de
|
||||
F src/test_btree.c 47cd771250f09cdc6e12dda5bc71bc0b3abc96e2
|
||||
F src/test_config.c d536042f27226b4639f0f87d4795fd37428a9ddf
|
||||
F src/test_demovfs.c 31050680fa6925b4f677cfd4fa965b5f19195e50
|
||||
F src/test_config.c 791a9acbf9a695cf9c12ff6bbe874dfde9a8c5f3
|
||||
F src/test_demovfs.c 20a4975127993f4959890016ae9ce5535a880094
|
||||
F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
|
||||
F src/test_func.c cbdec5cededa0761daedde5baf06004a9bf416b5
|
||||
F src/test_fuzzer.c f884f6f32e8513d34248d6e1ac8a32047fead254
|
||||
@ -207,16 +210,16 @@ F src/test_hexio.c c4773049603151704a6ab25ac5e936b5109caf5a
|
||||
F src/test_init.c 5d624ffd0409d424cf9adbfe1f056b200270077c
|
||||
F src/test_intarray.c d879bbf8e4ce085ab966d1f3c896a7c8b4f5fc99
|
||||
F src/test_intarray.h 489edb9068bb926583445cb02589344961054207
|
||||
F src/test_journal.c 785edd54f963aefb3c1628124170a56697c68c70
|
||||
F src/test_journal.c 03313c693cca72959dcaaf79f8d76f21c01e19ff
|
||||
F src/test_loadext.c df586c27176e3c2cb2e099c78da67bf14379a56e
|
||||
F src/test_malloc.c fd6188b1501c0010fb4241ddc9f0d5ac402c688d
|
||||
F src/test_multiplex.c fdabd793ee7a9642c5a8a470def2347144c46d05
|
||||
F src/test_malloc.c 7ca7be34e0e09ef0ed6619544552ed95732e41f6
|
||||
F src/test_multiplex.c a7457a1ac46964b7f897d75ecfd447410ec067e6
|
||||
F src/test_multiplex.h e99c571bc4968b7a9363b661481f3934bfead61d
|
||||
F src/test_mutex.c a6bd7b9cf6e19d989e31392b06ac8d189f0d573e
|
||||
F src/test_onefile.c 40cf9e212a377a6511469384a64b01e6e34b2eec
|
||||
F src/test_osinst.c 62b0b8ef21ce754cc94e17bb42377ed8795dba32
|
||||
F src/test_pcache.c 7bf828972ac0d2403f5cfa4cd14da41f8ebe73d8
|
||||
F src/test_quota.c b5576f17d701af461effd7ca1e71f0d100071192
|
||||
F src/test_quota.c cc4f67e12558a252ea4a11720be268348f4b1595
|
||||
F src/test_rtree.c 30c981837445a4e187ee850a49c4760d9642f7c3
|
||||
F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0
|
||||
F src/test_server.c 2f99eb2837dfa06a4aacf24af24c6affdf66a84f
|
||||
@ -224,34 +227,35 @@ F src/test_stat.c f682704b5d1ba8e1d4e7e882a6d7922e2dcf066c
|
||||
F src/test_superlock.c 2b97936ca127d13962c3605dbc9a4ef269c424cd
|
||||
F src/test_syscall.c 162c4ec0137a549c009bb9ecab550527743cfc5d
|
||||
F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa
|
||||
F src/test_thread.c 361ae0a0f1cbf5a28ad0388a258b104017a370c0
|
||||
F src/test_vfs.c 2ed8853c1e51ac6f9ea091f7ce4e0d618bba8b86
|
||||
F src/test_vfstrace.c 2265c9895f350c8d3c39b079998fbe7481505cc1
|
||||
F src/test_thread.c fe9a7803fc1d69cccb60f016f28c1cedf2d9fcfa
|
||||
F src/test_vfs.c 956cb3f5cbd2a0d09129540e615bb0fb761c083d
|
||||
F src/test_vfstrace.c 0b884e06094a746da729119a2cabdc7aa790063d
|
||||
F src/test_wholenumber.c 6129adfbe7c7444f2e60cc785927f3aa74e12290
|
||||
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/tokenize.c 604607d6813e9551cf5189d899e0a25c12681080
|
||||
F src/trigger.c 144cc18bb701f3286484aae4292a9531f09278c8
|
||||
F src/update.c 81911be16ece3c3e7716aa18565b4814ec41f8b9
|
||||
F src/utf.c d83650c3ea08f7407bd9d0839d9885241c209c60
|
||||
F src/util.c 465fe10aabf0ca7d7826a156dab919b0b65c525a
|
||||
F src/tokenize.c c819d9f72168a035d545a5bdafe9b085b20df705
|
||||
F src/trigger.c c836a6caac16ba96611558922106858f6ca3d6bf
|
||||
F src/update.c 80d77311d91ebc06b27149e75701f1b3e9356622
|
||||
F src/utf.c c53eb7404b3eb5c1cbb5655c6a7a0e0ce6bd50f0
|
||||
F src/util.c 0f33bbbdfcc4a2d8cf20c3b2a16ffc3b57c58a70
|
||||
F src/vacuum.c 05513dca036a1e7848fe18d5ed1265ac0b32365e
|
||||
F src/vdbe.c 05deeec6659f2579674a5e6510b3ada2a442f8d5
|
||||
F src/vdbe.h 8a675fefdf7119441fe817c800a9a52440c2e797
|
||||
F src/vdbeInt.h fe8f58d305e629fff02f61f655aca1d299f1f6ae
|
||||
F src/vdbeapi.c e0e2672e0a96ae3f8575c8ecd02912a3e8a554a1
|
||||
F src/vdbeaux.c 9ae5074b19bdff2d8806a278533956fb281510d5
|
||||
F src/vdbeblob.c c3ccb7c8732858c680f442932e66ad06bb036562
|
||||
F src/vdbe.c 50b5b4ef29d928a4f60905f820ee1ab2f8ced29f
|
||||
F src/vdbe.h 5cf09e7ee8a3f7d93bc51f196a96550786afe7a1
|
||||
F src/vdbeInt.h ad84226cc0adcb1185c22b70696b235a1678bb45
|
||||
F src/vdbeapi.c 2f908c65337b16c6249ab378d477e74492cf759b
|
||||
F src/vdbeaux.c 4d100407e3c72e163854aff8903d19d5ecdf46c0
|
||||
F src/vdbeblob.c f024f0bf420f36b070143c32b15cc7287341ffd3
|
||||
F src/vdbemem.c 0498796b6ffbe45e32960d6a1f5adfb6e419883b
|
||||
F src/vdbetrace.c 5d0dc3d5fd54878cc8d6d28eb41deb8d5885b114
|
||||
F src/vtab.c b0abc931f95af94c9ffdf9f747eb191cda953123
|
||||
F src/wal.c 7334009b396285b658a95a3b6bc6d2b016a1f794
|
||||
F src/wal.h 7a5fbb00114b7f2cd40c7e1003d4c41ce9d26840
|
||||
F src/vtab.c 901791a47318c0562cd0c676a2c6ff1bc530e582
|
||||
F src/wal.c 0c70ad7b1cac6005fa5e2cbefd23ee05e391c290
|
||||
F src/wal.h 66b40bd91bc29a5be1c88ddd1f5ade8f3f48728a
|
||||
F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
|
||||
F src/where.c 55403ce19c506be6a321c7f129aff693d6103db5
|
||||
F test/8_3_names.test b93687beebd17f6ebf812405a6833bae5d1f4199
|
||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
|
||||
F test/all.test 51756962d522e474338e9b2ebb26e7364d4aa125
|
||||
F test/alter.test 4e47fb9ea59348b88fce4e8bb49de530128b104c
|
||||
F test/alter.test a3f570072b53d7c0fe463bab3f5affa8e113c487
|
||||
F test/alter2.test 75f731508f1bf27ba09a6075c66cd02216ba464b
|
||||
F test/alter3.test 8677e48d95536f7a6ed86a1a774744dadcc22b07
|
||||
F test/alter4.test 1e5dd6b951e9f65ca66422edff02e56df82dd403
|
||||
@ -311,7 +315,7 @@ F test/capi3b.test efb2b9cfd127efa84433cd7a2d72ce0454ae0dc4
|
||||
F test/capi3c.test bea67403a5e37a4b33230ee4723e315a2ffb31e7
|
||||
F test/capi3d.test cd36571f014f34bdc4421967f6453cbb597d5d16
|
||||
F test/capi3e.test f7408dda65c92b9056199fdc180f893015f83dde
|
||||
F test/cast.test 166951664a0b0a2e0f8fb5997a152490c6363932
|
||||
F test/cast.test 4c275cbdc8202d6f9c54a3596701719868ac7dc3
|
||||
F test/check.test db2b29d557544347d28e25b8406f5d5ecc3d1bc3
|
||||
F test/coalesce.test cee0dccb9fbd2d494b77234bccf9dc6c6786eb91
|
||||
F test/collate1.test e3eaa48c21e150814be1a7b852d2a8af24458d04
|
||||
@ -365,37 +369,38 @@ F test/descidx2.test 9f1a0c83fd57f8667c82310ca21b30a350888b5d
|
||||
F test/descidx3.test fe720e8b37d59f4cef808b0bf4e1b391c2e56b6f
|
||||
F test/diskfull.test 0cede7ef9d8f415d9d3944005c76be7589bb5ebb
|
||||
F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376
|
||||
F test/e_createtable.test b40fc61bc4f1ad2a3c84590bd1d711507263d921
|
||||
F test/e_delete.test 55d868b647acc091c261a10b9b0cb0ab660a6acb
|
||||
F test/e_createtable.test 4771686a586b6ae414f927c389b2c101cc05c028
|
||||
F test/e_delete.test e2ae0d3fce5efd70fef99025e932afffc5616fab
|
||||
F test/e_droptrigger.test ddd4b28ed8a3d81bd5153fa0ab7559529a2ca03a
|
||||
F test/e_dropview.test b347bab30fc8de67b131594b3cd6f3d3bdaa753d
|
||||
F test/e_expr.test 9e8b9790803df4de23c2d68d566959934a6179d4
|
||||
F test/e_expr.test 71b55f90c9336ecec5a99641679036931260c754
|
||||
F test/e_fkey.test 38039b840ab19331000b0f0eb1d82baa7208a67a
|
||||
F test/e_fts3.test 75bb0aee26384ef586165e21018a17f7cd843469
|
||||
F test/e_insert.test 7390c2da39f16a134dc9a439144768c727757d2c
|
||||
F test/e_insert.test 76d4bb5da9b28014d515d91ffe29a79a1e99f2bc
|
||||
F test/e_reindex.test a064f0878b8f848fbca38f1f61f82f15a3000c64
|
||||
F test/e_resolve.test dcce9308fb13b934ce29591105d031d3e14fbba6
|
||||
F test/e_select.test bf385ae3aa0f014c4933ae66fd3e1302138493eb
|
||||
F test/e_select.test 7ac53674e822d4d77bbb4a9a4aaefa5fdc9e493f
|
||||
F test/e_select2.test 5c3d3da19c7b3e90ae444579db2b70098599ab92
|
||||
F test/e_update.test 963d6876064e65f318d1c93aaed36a02b9b389bf
|
||||
F test/e_update.test b926341a65955d69a6375c9eb4fd82e7089bc83a
|
||||
F test/e_uri.test 6f35b491f80dac005c8144f38b2dfb4d96483596
|
||||
F test/e_vacuum.test 6c09c2af7f2f140518f371c5342100118f779dcf
|
||||
F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea
|
||||
F test/enc2.test 6d91a5286f59add0cfcbb2d0da913b76f2242398
|
||||
F test/enc3.test 5c550d59ff31dccdba5d1a02ae11c7047d77c041
|
||||
F test/enc4.test 4b575ef09e0eff896e73bd24076f96c2aa6a42de
|
||||
F test/enc4.test b145fa25feb56ea1d51a5bc43ca268c0cf691a67
|
||||
F test/eqp.test f14fadd76da53405e9885e2431cacf7191d83cdb
|
||||
F test/eval.test bc269c365ba877554948441e91ad5373f9f91be3
|
||||
F test/exclusive.test 53e1841b422e554cecf0160f937c473d6d0e3062
|
||||
F test/exclusive2.test 343d55130c12c67b8bf10407acec043a6c26c86b
|
||||
F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7
|
||||
F test/exists.test 5e2d64b4eb5a9d08876599bdae2e1213d2d12e2a
|
||||
F test/expr.test 19e8ac40313e2282a47b586d11c4892040990d3a
|
||||
F test/expr.test 67c9fd6f8f829e239dc8b0f4a08a73c08b09196d
|
||||
F test/fallocate.test 43dc34b8c24be6baffadc3b4401ee15710ce83c6
|
||||
F test/filectrl.test 97003734290887566e01dded09dc9e99cb937e9e
|
||||
F test/filefmt.test f178cfc29501a14565954c961b226e61877dd32c
|
||||
F test/fkey1.test 01c7de578e11747e720c2d9aeef27f239853c4da
|
||||
F test/fkey2.test 080969fe219b3b082b0e097ac18c6af2e5b0631f
|
||||
F test/fkey3.test 42f88d6048d8dc079e2a8cf7baad1cc1483a7620
|
||||
F test/fkey3.test 5ec899d12b13bcf1e9ef40eff7fb692fdb91392e
|
||||
F test/fkey4.test c6c8f9f9be885f95c85c7bceb26f243ad906fd49
|
||||
F test/fkey_malloc.test a5ede29bd2f6e56dea78c3d43fb86dd696c068c8
|
||||
F test/format4.test 1f0cac8ff3895e9359ed87e41aaabee982a812eb
|
||||
@ -450,30 +455,34 @@ F test/fts3al.test 07d64326e79bbdbab20ee87fc3328fbf01641c9f
|
||||
F test/fts3am.test 218aa6ba0dfc50c7c16b2022aac5c6be593d08d8
|
||||
F test/fts3an.test a49ccadc07a2f7d646ec1b81bc09da2d85a85b18
|
||||
F test/fts3ao.test b83f99f70e9eec85f27d75801a974b3f820e01f9
|
||||
F test/fts3atoken.test 25c2070e1e8755d414bf9c8200427b277a9f99fa
|
||||
F test/fts3aux1.test 719c35cbbcc04dde8e5a54a6f69851a0af9ed1f2
|
||||
F test/fts3atoken.test 402ef2f7c2fb4b3d4fa0587df6441c1447e799b3
|
||||
F test/fts3auto.test f1cb0a55130897013ca5850dbee2945c2908a45a
|
||||
F test/fts3aux1.test 0b02743955d56fc0d4d66236a26177bd1b726de0
|
||||
F test/fts3b.test e93bbb653e52afde110ad53bbd793f14fe7a8984
|
||||
F test/fts3c.test fc723a9cf10b397fdfc2b32e73c53c8b1ec02958
|
||||
F test/fts3comp1.test a0f5b16a2df44dd0b15751787130af2183167c0c
|
||||
F test/fts3corrupt.test 7890cc202406858386ddf390a879dcf80bc10abf
|
||||
F test/fts3conf.test 8e65ea56f88ced6cdd2252bdddb1a8327ae5af7e
|
||||
F test/fts3corrupt.test 7b0f91780ca36118d73324ec803187208ad33b32
|
||||
F test/fts3corrupt2.test 6d96efae2f8a6af3eeaf283aba437e6d0e5447ba
|
||||
F test/fts3cov.test e0fb00d8b715ddae4a94c305992dfc3ef70353d7
|
||||
F test/fts3d.test 95fb3c862cbc4297c93fceb9a635543744e9ef52
|
||||
F test/fts3defer.test d6cb0db9b5997ecf863d96ff419f83f8f2c87f4f
|
||||
F test/fts3defer2.test 288bef6de15557319b8c12d476ebdc83688ef96c
|
||||
F test/fts3defer.test 7c8a38d5f617d7b52ae1c43ed73c536e7e895a35
|
||||
F test/fts3defer2.test 35867d33ba6db03f6c73bd6f5fc333ae14f68c81
|
||||
F test/fts3e.test 1f6c6ac9cc8b772ca256e6b22aaeed50c9350851
|
||||
F test/fts3expr.test 5e745b2b6348499d9ef8d59015de3182072c564c
|
||||
F test/fts3expr2.test 18da930352e5693eaa163a3eacf96233b7290d1a
|
||||
F test/fts3fault.test f83e556465bb69dc8bc676339eca408dce4ca246
|
||||
F test/fts3fault2.test dc96203af6ba31ce20163fc35460e1556e8edf4d
|
||||
F test/fts3malloc.test 9c8cc3f885bb4dfc66d0460c52f68f45e4710d1b
|
||||
F test/fts3matchinfo.test cc0b009edbbf575283d5fdb53271179e0d8019ba
|
||||
F test/fts3matchinfo.test 08a82d18cc08abb28aec41d412b4c2ef25ba6a5f
|
||||
F test/fts3near.test 2e318ee434d32babd27c167142e2b94ddbab4844
|
||||
F test/fts3prefix.test 36246609111ec1683f7ea5ed27666ce2cefb5676
|
||||
F test/fts3query.test ef79d31fdb355d094baec1c1b24b60439a1fb8a2
|
||||
F test/fts3rnd.test 2b1a579be557ab8ac54a51b39caa4aa8043cc4ad
|
||||
F test/fts3rnd.test 1320d8826a845e38a96e769562bf83d7a92a15d0
|
||||
F test/fts3shared.test 8bb266521d7c5495c0ae522bb4d376ad5387d4a2
|
||||
F test/fts3snippet.test a12f22a3ba4dd59751a57c79b031d07ab5f51ddd
|
||||
F test/fts4aa.test eadf85621c0a113d4c7ad3ccbf8441130e007b8f
|
||||
F test/fts3sort.test 63d52c1812904b751f9e1ff487472e44833f5402
|
||||
F test/fts4aa.test 148d9eb54901af23b5d402b1f388f43e559e1728
|
||||
F test/func.test 6c5ce11e3a0021ca3c0649234e2d4454c89110ca
|
||||
F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f
|
||||
F test/func3.test 7ba2ca5a1e9bca900ba2c230cf04bd67184bc1bc
|
||||
@ -483,7 +492,7 @@ F test/fuzz3.test aec64345184d1662bd30e6a17851ff659d596dc5
|
||||
F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b
|
||||
F test/fuzz_malloc.test dd7001ac86d09c154a7dff064f4739c60e2b312c
|
||||
F test/fuzzer1.test 3105b5a89a6cb0d475f0877debec942fe4143462
|
||||
F test/hook.test f04c3412463f8ec117c1c704c74ca0f627ce733a
|
||||
F test/hook.test f2277c309e4ee8067d95d6b9b315568e9d5329b2
|
||||
F test/icu.test 70df4faca133254c042d02ae342c0a141f2663f4
|
||||
F test/in.test 19b642bb134308980a92249750ea4ce3f6c75c2d
|
||||
F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75
|
||||
@ -505,7 +514,7 @@ F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7
|
||||
F test/insert.test aef273dd1cee84cc92407469e6bd1b3cdcb76908
|
||||
F test/insert2.test 4f3a04d168c728ed5ec2c88842e772606c7ce435
|
||||
F test/insert3.test 1b7db95a03ad9c5013fdf7d6722b6cd66ee55e30
|
||||
F test/insert4.test c1469999a58e86a85b74df645a820f4cc7a8273b
|
||||
F test/insert4.test b3e02648a5fc3075c29e13c369b5127bf859b5a2
|
||||
F test/insert5.test 1f93cbe9742110119133d7e8e3ccfe6d7c249766
|
||||
F test/intarray.test 066b7d7ac38d25bf96f87f1b017bfc687551cdd4
|
||||
F test/interrupt.test 42e7cf98646fd9cb4a3b131a93ed3c50b9e149f1
|
||||
@ -543,11 +552,11 @@ F test/lock4.test c82268c031d39345d05efa672f80b025481b3ae5
|
||||
F test/lock5.test b2abb5e711bc59b0eae00f6c97a36ec9f458fada
|
||||
F test/lock6.test ad5b387a3a8096afd3c68a55b9535056431b0cf5
|
||||
F test/lock7.test 64006c84c1c616657e237c7ad6532b765611cf64
|
||||
F test/lock_common.tcl d279887a0ab16cdb6d935c1203e64113c5a000e9
|
||||
F test/lock_common.tcl 0c270b121d40959fa2f3add382200c27045b3d95
|
||||
F test/lookaside.test 93f07bac140c5bb1d49f3892d2684decafdc7af2
|
||||
F test/main.test 9d7bbfcc1b52c88ba7b2ba6554068ecf9939f252
|
||||
F test/make-where7.tcl 05c16b5d4f5d6512881dfec560cb793915932ef9
|
||||
F test/malloc.test e56c9c3358da2c18385aea15a42dc970913986c2
|
||||
F test/malloc.test 76017be66cec4375a4b4ea5c71245e27a9fe2d0b
|
||||
F test/malloc3.test 4128b1e6ffa506103b278ad97af89174f310c7ca
|
||||
F test/malloc4.test 957337613002b7058a85116493a262f679f3a261
|
||||
F test/malloc5.test 4d16d1bb26d2deddd7c4f480deec341f9b2d0e22
|
||||
@ -585,35 +594,35 @@ F test/misc5.test 45b2e3ed5f79af2b4f38ae362eaf4c49674575bd
|
||||
F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91
|
||||
F test/misc7.test 29032efcd3d826fbd409e2a7af873e7939f4a4e3
|
||||
F test/misuse.test 30b3a458e5a70c31e74c291937b6c82204c59f33
|
||||
F test/multiplex.test a88f3e2c16e567e72be7296195c59fbdd6a8d3d4
|
||||
F test/multiplex.test 555080c87abfc72ba68e2f3df01d4a9a7a4fdf58
|
||||
F test/mutex1.test 78b2b9bb320e51d156c4efdb71b99b051e7a4b41
|
||||
F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660
|
||||
F test/nan.test a44e04df1486fcfb02d32468cbcd3c8e1e433723
|
||||
F test/nan.test dc212a22b36109fd1ae37154292444ef249c5ec2
|
||||
F test/notify1.test 8433bc74bd952fb8a6e3f8d7a4c2b28dfd69e310
|
||||
F test/notify2.test 195a467e021f74197be2c4fb02d6dee644b8d8db
|
||||
F test/notify3.test d60923e186e0900f4812a845fcdfd8eea096e33a
|
||||
F test/notnull.test cc7c78340328e6112a13c3e311a9ab3127114347
|
||||
F test/null.test a8b09b8ed87852742343b33441a9240022108993
|
||||
F test/openv2.test af02ed0a9cbc0d2a61b8f35171d4d117e588e4ec
|
||||
F test/oserror.test 498d8337e9d15543eb7b004fef8594bf204ff43c
|
||||
F test/pager1.test d8672fd0af5f4f9b99b06283d00f01547809bebe
|
||||
F test/oserror.test 3fe52e0bd2891a9bf7cdeb639554992453d46301
|
||||
F test/pager1.test 228a831060dab96bc91b03ba2a85cedefd1ab38a
|
||||
F test/pager2.test 745b911dde3d1f24ae0870bd433dfa83d7c658c1
|
||||
F test/pager3.test 3856d9c80839be0668efee1b74811b1b7f7fc95f
|
||||
F test/pagerfault.test 9de4d3e0c59970b4c6cb8dac511fa242f335d8a7
|
||||
F test/pagerfault.test 4194b8ea2a5da7958cd155556605ff554e1b065a
|
||||
F test/pagerfault2.test 1f79ea40d1133b2683a2f811b00f2399f7ec2401
|
||||
F test/pagerfault3.test f16e2efcb5fc9996d1356f7cbc44c998318ae1d7
|
||||
F test/pageropt.test 8146bf448cf09e87bb1867c2217b921fb5857806
|
||||
F test/pagesize.test 76aa9f23ecb0741a4ed9d2e16c5fa82671f28efb
|
||||
F test/pcache.test 065aa286e722ab24f2e51792c1f093bf60656b16
|
||||
F test/pcache2.test 0d85f2ab6963aee28c671d4c71bec038c00a1d16
|
||||
F test/permutations.test 5b2a4cb756ffb2407cb4743163668d1d769febb6
|
||||
F test/permutations.test 1e8892ebf1bd6e9e8036f4841c72a91bf72da74a
|
||||
F test/pragma.test fdfc09067ea104a0c247a1a79d8093b56656f850
|
||||
F test/pragma2.test 5364893491b9231dd170e3459bfc2e2342658b47
|
||||
F test/printf.test 05970cde31b1a9f54bd75af60597be75a5c54fea
|
||||
F test/progress.test 5b075c3c790c7b2a61419bc199db87aaf48b8301
|
||||
F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc
|
||||
F test/quick.test 1681febc928d686362d50057c642f77a02c62e57
|
||||
F test/quota.test ddafe133653093eb9a99ccd6264884ae43f9c9b8
|
||||
F test/quota.test 48c3a5a98687d67ef06fc16d2e603284756bbec3
|
||||
F test/quote.test 215897dbe8de1a6f701265836d6601cc6ed103e6
|
||||
F test/randexpr1.tcl 40dec52119ed3a2b8b2a773bce24b63a3a746459
|
||||
F test/randexpr1.test 1084050991e9ba22c1c10edd8d84673b501cc25a
|
||||
@ -677,7 +686,7 @@ F test/subquery.test b524f57c9574b2c0347045b4510ef795d4686796
|
||||
F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4
|
||||
F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a
|
||||
F test/superlock.test 5d7a4954b0059c903f82c7b67867bc5451a7c082
|
||||
F test/sync.test ded6b39d8d8ca3c0c5518516c6371b3316d3e3a3
|
||||
F test/sync.test 2bd73b585089c99e76b3e1796c42059b7d89d872
|
||||
F test/syscall.test 707c95e4ab7863e13f1293c6b0c76bead30249b3
|
||||
F test/sysfault.test c79441d88d23696fbec7b147dba98d42a04f523f
|
||||
F test/table.test 04ba066432430657712d167ebf28080fe878d305
|
||||
@ -686,7 +695,7 @@ F test/tclsqlite.test 8c154101e704170c2be10f137a5499ac2c6da8d3
|
||||
F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c
|
||||
F test/temptable.test f42121a0d29a62f00f93274464164177ab1cc24a
|
||||
F test/temptrigger.test b0273db072ce5f37cf19140ceb1f0d524bbe9f05
|
||||
F test/tester.tcl 6fa3d2f581b479a3a088b1b5b0d145e548ebe662
|
||||
F test/tester.tcl 7d3d17914ca5397a82f801db733b81d4a82f50c3
|
||||
F test/thread001.test a3e6a7254d1cb057836cb3145b60c10bf5b7e60f
|
||||
F test/thread002.test afd20095e6e845b405df4f2c920cb93301ca69db
|
||||
F test/thread003.test b824d4f52b870ae39fc5bae4d8070eca73085dca
|
||||
@ -700,6 +709,7 @@ F test/threadtest2.c ace893054fa134af3fc8d6e7cfecddb8e3acefb9
|
||||
F test/threadtest3.c 0ed13e09690f6204d7455fac3b0e8ece490f6eef
|
||||
F test/tkt-02a8e81d44.test 58494de77be2cf249228ada3f313fa399821c6ab
|
||||
F test/tkt-26ff0c2d1e.test 888324e751512972c6e0d1a09df740d8f5aaf660
|
||||
F test/tkt-2d1a5c67d.test 73574c758502bf23260c17f97fcd9316dfb5a060
|
||||
F test/tkt-2ea2425d34.test 1cf13e6f75d149b3209a0cb32927a82d3d79fb28
|
||||
F test/tkt-31338dca7e.test 5741cd48de500347a437ba1be58c8335e83c5a5e
|
||||
F test/tkt-313723c356.test c47f8a9330523e6f35698bf4489bcb29609b53ac
|
||||
@ -715,10 +725,12 @@ F test/tkt-78e04e52ea.test ab52f0c1e2de6e46c910f4cc16b086bba05952b7
|
||||
F test/tkt-80ba201079.test a09684db1a0bd55b8838f606adccee456a51ddbf
|
||||
F test/tkt-80e031a00f.test 9a154173461a4dbe2de49cda73963e04842d52f7
|
||||
F test/tkt-8454a207b9.test c583a9f814a82a2b5ba95207f55001c9f0cd816c
|
||||
F test/tkt-91e2e8ba6f.test 08c4f94ae07696b05c9b822da0b4e5337a2f54c5
|
||||
F test/tkt-94c04eaadb.test be5ea61cb04dfdc047d19b5c5a9e75fa3da67a7f
|
||||
F test/tkt-9d68c883.test 458f7d82a523d7644b54b497c986378a7d8c8b67
|
||||
F test/tkt-b351d95f9.test d14a503c414c5c58fdde3e80f9a3cfef986498c0
|
||||
F test/tkt-b72787b1.test e6b62b2b2785c04d0d698d6a603507e384165049
|
||||
F test/tkt-bd484a090c.test 60460bf946f79a79712b71f202eda501ca99b898
|
||||
F test/tkt-cbd054fa6b.test f14f97ea43662e6f70c9e63287081e8be5d9d589
|
||||
F test/tkt-d11f09d36e.test fb44f7961aa6d4b632fb7b9768239832210b5fc7
|
||||
F test/tkt-d82e3f3721.test 731359dfdcdb36fea0559cd33fec39dd0ceae8e6
|
||||
@ -798,13 +810,13 @@ F test/tkt3793.test 754b73f0e6a9349c70dc57e522cf3247272ecd5d
|
||||
F test/tkt3810.test 90fa0635dfa7da9680c8cd3513350a49b3a8ae12
|
||||
F test/tkt3824.test 150aa00bb6220672e5f0eb14dc8eaa36750425f0
|
||||
F test/tkt3832.test 2300d10d57562b89875b72148338ac3e14f8847d
|
||||
F test/tkt3838.test f956f0719b5f805b12dd1dbf19f19d298bacebc3
|
||||
F test/tkt3838.test d8490365a1c473d214f7878007e543410cbb715f
|
||||
F test/tkt3841.test 4659845bc53f809a5932c61c6ce8c5bb9d6b947f
|
||||
F test/tkt3871.test 43ecbc8d90dc83908e2a454aef345acc9d160c6f
|
||||
F test/tkt3879.test 2ad5bef2c87e9991ce941e054c31abe26ef7fb90
|
||||
F test/tkt3911.test 74cd324f3ba653040cc6d94cc4857b290d12d633
|
||||
F test/tkt3918.test e6cdf6bfcfe9ba939d86a4238a9dc55d6eec5d42
|
||||
F test/tkt3922.test 022ace32c049e3964f68492c12eb803e8e4856d8
|
||||
F test/tkt3922.test f26be40ab4fe6c00795629bd2006d96e270d9b1a
|
||||
F test/tkt3929.test 75a862e45bcb39e9a7944c89b92afa531304afca
|
||||
F test/tkt3935.test e15261fedb9e30a4305a311da614a5d8e693c767
|
||||
F test/tkt3992.test f3e7d548ac26f763b47bc0f750da3d03c81071da
|
||||
@ -812,7 +824,7 @@ F test/tkt3997.test a335fa41ca3985660a139df7b734a26ef53284bd
|
||||
F test/tkt4018.test 7c2c9ba4df489c676a0a7a0e809a1fb9b2185bd1
|
||||
F test/tokenize.test ce430a7aed48fc98301611429595883fdfcab5d7
|
||||
F test/trace.test 4b36a41a3e9c7842151af6da5998f5080cdad9e5
|
||||
F test/trace2.test 0ce11265c83333d8f5beeca19e71ed93a88d386c
|
||||
F test/trace2.test 962175290996d5f06dc4402ca218bbfc7df4cb20
|
||||
F test/trans.test 6e1b4c6a42dba31bd65f8fa5e61a2708e08ddde6
|
||||
F test/trans2.test d5337e61de45e66b1fcbf9db833fa8c82e624b22
|
||||
F test/trans3.test d728abaa318ca364dc370e06576aa7e5fbed7e97
|
||||
@ -827,16 +839,17 @@ F test/trigger8.test 30cb0530bd7c4728055420e3f739aa00412eafa4
|
||||
F test/trigger9.test 5b0789f1c5c4600961f8e68511b825b87be53e31
|
||||
F test/triggerA.test eaf11a29db2a11967d2d4b49d37f92bce598194e
|
||||
F test/triggerB.test 56780c031b454abac2340dbb3b71ac5c56c3d7fe
|
||||
F test/triggerC.test 8a691ff6dd47df2e57395bbec4b62101fac0f363
|
||||
F test/triggerC.test 02c690febf608ae20b9af86184a9867f79855b1d
|
||||
F test/triggerD.test c6add3817351451e419f6ff9e9a259b02b6e2de7
|
||||
F test/tt3_checkpoint.c 415eccce672d681b297485fc20f44cdf0eac93af
|
||||
F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff
|
||||
F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84
|
||||
F test/types3.test a0f66bf12f80fad89493535474f7a6d16fa58150
|
||||
F test/types3.test 99e009491a54f4dc02c06bdbc0c5eea56ae3e25a
|
||||
F test/unique.test 083c7fff74695bcc27a71d75699deba3595bc9c2
|
||||
F test/unixexcl.test 9d80a54d86d2261f660758928959368ffc36151e
|
||||
F test/unordered.test c479d3027f9c4db05b44b83010735c6708abcc91
|
||||
F test/unordered.test e81169ce2a8f31b2c6b66af691887e1376ab3ced
|
||||
F test/update.test 8bc86fd7ef1a00014f76dc6a6a7c974df4aef172
|
||||
F test/uri.test 53de9a2549cbda9c343223236918ef502f6a9051
|
||||
F test/utf16align.test 54cd35a27c005a9b6e7815d887718780b6a462ae
|
||||
F test/vacuum.test 29b60e8cc9e573b39676df6c4a75fe9e02d04a09
|
||||
F test/vacuum2.test 91a84c9b08adfc4472097d2e8deb0150214e0e76
|
||||
@ -863,11 +876,12 @@ F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5
|
||||
F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8
|
||||
F test/vtab_shared.test 0eff9ce4f19facbe0a3e693f6c14b80711a4222d
|
||||
F test/wal.test 5617ad308bfdb8a8885220d8a261a6096a8d7e57
|
||||
F test/wal2.test e561a8c6fdd1c2cd1876f3e39757934e7b7361f8
|
||||
F test/wal2.test aa0fb2314b3235be4503c06873e41ebfc0757782
|
||||
F test/wal3.test 5c396cc22497244d627306f4c1d360167353f8dd
|
||||
F test/wal4.test 3404b048fa5e10605facaf70384e6d2943412e30
|
||||
F test/wal5.test 1bbfaa316dc2a1d0d1fac3f4500c38a90055a41b
|
||||
F test/wal5.test f06a0427e06db00347e32eb9fa99d6a5c0f2d088
|
||||
F test/wal6.test 07aa31ca8892d0527f2c5c5a9a2a87aa421dfaa8
|
||||
F test/wal7.test 2ae8f427d240099cc4b2dfef63cff44e2a68a1bd
|
||||
F test/wal_common.tcl a98f17fba96206122eff624db0ab13ec377be4fe
|
||||
F test/walbak.test 4df1c7369da0301caeb9a48fa45997fd592380e4
|
||||
F test/walbig.test e882bc1d014afffbfa2b6ba36e0f07d30a633ad0
|
||||
@ -878,6 +892,7 @@ F test/walfault.test 58fce626359c9376fe35101b5c0f2df8040aa839
|
||||
F test/walhook.test ed00a40ba7255da22d6b66433ab61fab16a63483
|
||||
F test/walmode.test 22ddccd073c817ac9ead62b88ac446e8dedc7d2c
|
||||
F test/walnoshm.test a074428046408f4eb5c6a00e09df8cc97ff93317
|
||||
F test/walro.test 2d5d69e2e99da19ce6faab340330234fc4ca0720
|
||||
F test/walshared.test 6dda2293880c300baf5d791c307f653094585761
|
||||
F test/walslow.test d21625e2e99e11c032ce949e8a94661576548933
|
||||
F test/walthread.test a25a393c068a2b42b44333fa3fdaae9072f1617c
|
||||
@ -895,28 +910,30 @@ F test/whereA.test 24c234263c8fe358f079d5e57d884fb569d2da0a
|
||||
F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5
|
||||
F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
|
||||
F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688
|
||||
F tool/build-shell.sh 12aa4391073a777fcb6dcc490b219a018ae98bac
|
||||
F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b
|
||||
F tool/fragck.tcl 5265a95126abcf6ab357f7efa544787e5963f439
|
||||
F tool/genfkey.README cf68fddd4643bbe3ff8e31b8b6d8b0a1b85e20f4
|
||||
F tool/genfkey.test 4196a8928b78f51d54ef58e99e99401ab2f0a7e5
|
||||
F tool/lemon.c dfd81a51b6e27e469ba21d01a75ddf092d429027
|
||||
F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
|
||||
F tool/lemon.c d51c68d405ff7f9bad99268ca3c20a198eb983ed
|
||||
F tool/lempar.c 01ca97f87610d1dac6d8cd96ab109ab1130e76dc
|
||||
F tool/mkkeywordhash.c d2e6b4a5965e23afb80fbe74bb54648cd371f309
|
||||
F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e
|
||||
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
|
||||
F tool/mksqlite3c.tcl 623e26cc8c83322e4151d3ad85ac69d41221bae8
|
||||
F tool/mksqlite3h.tcl d76c226a5e8e1f3b5f6593bcabe5e98b3b1ec9ff
|
||||
F tool/mksqlite3c.tcl 1fa0ed9cfdc768bf5de7e65fda8d97a46dd2a7e6
|
||||
F tool/mksqlite3h.tcl 78013ad79a5e492e5f764f3c7a8ef834255061f8
|
||||
F tool/mksqlite3internalh.tcl 7b43894e21bcb1bb39e11547ce7e38a063357e87
|
||||
F tool/omittest.tcl b1dd290c1596e0f31fd335160a74ec5dfea3df4a
|
||||
F tool/omittest.tcl 8086c014cbae90f1f2b564d59d05a5e4ac1783c9
|
||||
F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c
|
||||
F tool/restore_jrnl.tcl 6957a34f8f1f0f8285e07536225ec3b292a9024a
|
||||
F tool/rollback-test.c 9fc98427d1e23e84429d7e6d07d9094fbdec65a5
|
||||
F tool/shell1.test 7a389c6aaad05621be39501d6f8db410da464dcd
|
||||
F tool/shell1.test 20dfe7099cf2afe37aecd69afb7678d14f7a0abf
|
||||
F tool/shell2.test 5dc76b8005b465f420fed8241621da7513060ff3
|
||||
F tool/shell3.test 4fad469e8003938426355afdf34155f08c587836
|
||||
F tool/shell4.test 35f9c3d452b4e76d5013c63e1fd07478a62f14ce
|
||||
F tool/shell5.test 62bfaf9267296da1b91e4b1c03e44e7b393f6a94
|
||||
F tool/showdb.c 471c0f8fa472e71bb7654500096a5bdb4ea1fb2a
|
||||
F tool/showdb.c 43e913d954684c2f5007dcab46d1a1308852a0ad
|
||||
F tool/showjournal.c b62cecaab86a4053d944c276bb5232e4d17ece02
|
||||
F tool/showwal.c f09e5a80a293919290ec85a6a37c85a5ddcf37d9
|
||||
F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe
|
||||
@ -928,8 +945,11 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
||||
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
|
||||
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
|
||||
F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c
|
||||
F tool/symbols.sh bc2a3709940d47c8ac8e0a1fdf17ec801f015a00
|
||||
F tool/tostr.awk 11760e1b94a5d3dcd42378f3cc18544c06cfa576
|
||||
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
|
||||
P c429edf30accac12d5deef10e59cd31146036f11
|
||||
R 050fbfdb8675b7eaafb0b1677e5953e7
|
||||
U drh
|
||||
Z 289c52539a2a59a211a989409cbc5426
|
||||
F tool/warnings.sh 347d974d143cf132f953b565fbc03026f19fcb4d
|
||||
P de8ad5f8176d5573c02ec39b14cca324cfef5cb9
|
||||
R 627b3b91e5169f5233cd18fa7efa5202
|
||||
U dan
|
||||
Z 94b0ac9079eaca2f6ca6db06cfa18fd2
|
||||
|
||||
@ -1 +1 @@
|
||||
154ddbc17120be2915eb03edc52af1225eb7cb5e
|
||||
af0d91adf497f5f36ec3813f04235a6e195a605f
|
||||
|
||||
@ -358,14 +358,14 @@ static void reloadTableSchema(Parse *pParse, Table *pTab, const char *zName){
|
||||
/* Reload the table, index and permanent trigger schemas. */
|
||||
zWhere = sqlite3MPrintf(pParse->db, "tbl_name=%Q", zName);
|
||||
if( !zWhere ) return;
|
||||
sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0, zWhere, P4_DYNAMIC);
|
||||
sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere);
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
/* Now, if the table is not stored in the temp database, reload any temp
|
||||
** triggers. Don't use IN(...) in case SQLITE_OMIT_SUBQUERY is defined.
|
||||
*/
|
||||
if( (zWhere=whereTempTriggers(pParse, pTab))!=0 ){
|
||||
sqlite3VdbeAddOp4(v, OP_ParseSchema, 1, 0, 0, zWhere, P4_DYNAMIC);
|
||||
sqlite3VdbeAddParseSchemaOp(v, 1, zWhere);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
18
src/attach.c
18
src/attach.c
@ -70,8 +70,12 @@ static void attachFunc(
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
const char *zName;
|
||||
const char *zFile;
|
||||
char *zPath = 0;
|
||||
char *zErr = 0;
|
||||
unsigned int flags;
|
||||
Db *aNew;
|
||||
char *zErrDyn = 0;
|
||||
sqlite3_vfs *pVfs;
|
||||
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
|
||||
@ -124,8 +128,18 @@ static void attachFunc(
|
||||
** it to obtain the database schema. At this point the schema may
|
||||
** or may not be initialised.
|
||||
*/
|
||||
rc = sqlite3BtreeOpen(zFile, db, &aNew->pBt, 0,
|
||||
db->openFlags | SQLITE_OPEN_MAIN_DB);
|
||||
flags = db->openFlags;
|
||||
rc = sqlite3ParseUri(db->pVfs->zName, zFile, &flags, &pVfs, &zPath, &zErr);
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( rc==SQLITE_NOMEM ) db->mallocFailed = 1;
|
||||
sqlite3_result_error(context, zErr, -1);
|
||||
sqlite3_free(zErr);
|
||||
return;
|
||||
}
|
||||
assert( pVfs );
|
||||
flags |= SQLITE_OPEN_MAIN_DB;
|
||||
rc = sqlite3BtreeOpen(pVfs, zPath, db, &aNew->pBt, 0, flags);
|
||||
sqlite3_free( zPath );
|
||||
db->nDb++;
|
||||
if( rc==SQLITE_CONSTRAINT ){
|
||||
rc = SQLITE_ERROR;
|
||||
|
||||
71
src/btree.c
71
src/btree.c
@ -788,6 +788,7 @@ static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){
|
||||
*pRC = SQLITE_CORRUPT_BKPT;
|
||||
goto ptrmap_exit;
|
||||
}
|
||||
assert( offset <= (int)pBt->usableSize-5 );
|
||||
pPtrmap = (u8 *)sqlite3PagerGetData(pDbPage);
|
||||
|
||||
if( eType!=pPtrmap[offset] || get4byte(&pPtrmap[offset+1])!=parent ){
|
||||
@ -827,6 +828,11 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
|
||||
pPtrmap = (u8 *)sqlite3PagerGetData(pDbPage);
|
||||
|
||||
offset = PTRMAP_PTROFFSET(iPtrmap, key);
|
||||
if( offset<0 ){
|
||||
sqlite3PagerUnref(pDbPage);
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
assert( offset <= (int)pBt->usableSize-5 );
|
||||
assert( pEType!=0 );
|
||||
*pEType = pPtrmap[offset];
|
||||
if( pPgno ) *pPgno = get4byte(&pPtrmap[offset+1]);
|
||||
@ -851,6 +857,8 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
|
||||
*/
|
||||
#define findCell(P,I) \
|
||||
((P)->aData + ((P)->maskPage & get2byte(&(P)->aData[(P)->cellOffset+2*(I)])))
|
||||
#define findCellv2(D,M,O,I) (D+(M&get2byte(D+(O+2*(I)))))
|
||||
|
||||
|
||||
/*
|
||||
** This a more complex version of findCell() that works for
|
||||
@ -1688,13 +1696,13 @@ static int btreeInvokeBusyHandler(void *pArg){
|
||||
** to problems with locking.
|
||||
*/
|
||||
int sqlite3BtreeOpen(
|
||||
sqlite3_vfs *pVfs, /* VFS to use for this b-tree */
|
||||
const char *zFilename, /* Name of the file containing the BTree database */
|
||||
sqlite3 *db, /* Associated database handle */
|
||||
Btree **ppBtree, /* Pointer to new Btree object written here */
|
||||
int flags, /* Options */
|
||||
int vfsFlags /* Flags passed through to sqlite3_vfs.xOpen() */
|
||||
){
|
||||
sqlite3_vfs *pVfs; /* The VFS to use for this btree */
|
||||
BtShared *pBt = 0; /* Shared part of btree structure */
|
||||
Btree *p; /* Handle to return */
|
||||
sqlite3_mutex *mutexOpen = 0; /* Prevents a race condition. Ticket #3537 */
|
||||
@ -1716,6 +1724,7 @@ int sqlite3BtreeOpen(
|
||||
#endif
|
||||
|
||||
assert( db!=0 );
|
||||
assert( pVfs!=0 );
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
assert( (flags&0xff)==flags ); /* flags fit in 8 bits */
|
||||
|
||||
@ -1734,7 +1743,6 @@ int sqlite3BtreeOpen(
|
||||
if( (vfsFlags & SQLITE_OPEN_MAIN_DB)!=0 && (isMemdb || isTempDb) ){
|
||||
vfsFlags = (vfsFlags & ~SQLITE_OPEN_MAIN_DB) | SQLITE_OPEN_TEMP_DB;
|
||||
}
|
||||
pVfs = db->pVfs;
|
||||
p = sqlite3MallocZero(sizeof(Btree));
|
||||
if( !p ){
|
||||
return SQLITE_NOMEM;
|
||||
@ -4445,7 +4453,7 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
}
|
||||
assert( pCur->apPage[0]->intKey || pIdxKey );
|
||||
for(;;){
|
||||
int lwr, upr;
|
||||
int lwr, upr, idx;
|
||||
Pgno chldPg;
|
||||
MemPage *pPage = pCur->apPage[pCur->iPage];
|
||||
int c;
|
||||
@ -4461,14 +4469,14 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
lwr = 0;
|
||||
upr = pPage->nCell-1;
|
||||
if( biasRight ){
|
||||
pCur->aiIdx[pCur->iPage] = (u16)upr;
|
||||
pCur->aiIdx[pCur->iPage] = (u16)(idx = upr);
|
||||
}else{
|
||||
pCur->aiIdx[pCur->iPage] = (u16)((upr+lwr)/2);
|
||||
pCur->aiIdx[pCur->iPage] = (u16)(idx = (upr+lwr)/2);
|
||||
}
|
||||
for(;;){
|
||||
int idx = pCur->aiIdx[pCur->iPage]; /* Index of current cell in pPage */
|
||||
u8 *pCell; /* Pointer to current cell in pPage */
|
||||
|
||||
assert( idx==pCur->aiIdx[pCur->iPage] );
|
||||
pCur->info.nSize = 0;
|
||||
pCell = findCell(pPage, idx) + pPage->childPtrSize;
|
||||
if( pPage->intKey ){
|
||||
@ -4551,7 +4559,7 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
if( lwr>upr ){
|
||||
break;
|
||||
}
|
||||
pCur->aiIdx[pCur->iPage] = (u16)((lwr+upr)/2);
|
||||
pCur->aiIdx[pCur->iPage] = (u16)(idx = (lwr+upr)/2);
|
||||
}
|
||||
assert( lwr==upr+1 );
|
||||
assert( pPage->isInit );
|
||||
@ -5384,10 +5392,10 @@ static int fillInCell(
|
||||
** "sz" must be the number of bytes in the cell.
|
||||
*/
|
||||
static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
|
||||
int i; /* Loop counter */
|
||||
u32 pc; /* Offset to cell content of cell being deleted */
|
||||
u8 *data; /* pPage->aData */
|
||||
u8 *ptr; /* Used to move bytes around within data[] */
|
||||
u8 *endPtr; /* End of loop */
|
||||
int rc; /* The return code */
|
||||
int hdr; /* Beginning of the header. 0 most pages. 100 page 1 */
|
||||
|
||||
@ -5412,9 +5420,11 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
|
||||
*pRC = rc;
|
||||
return;
|
||||
}
|
||||
for(i=idx+1; i<pPage->nCell; i++, ptr+=2){
|
||||
ptr[0] = ptr[2];
|
||||
ptr[1] = ptr[3];
|
||||
endPtr = &data[pPage->cellOffset + 2*pPage->nCell - 2];
|
||||
assert( (SQLITE_PTR_TO_INT(ptr)&1)==0 ); /* ptr is always 2-byte aligned */
|
||||
while( ptr<endPtr ){
|
||||
*(u16*)ptr = *(u16*)&ptr[2];
|
||||
ptr += 2;
|
||||
}
|
||||
pPage->nCell--;
|
||||
put2byte(&data[hdr+3], pPage->nCell);
|
||||
@ -5454,6 +5464,7 @@ static void insertCell(
|
||||
int cellOffset; /* Address of first cell pointer in data[] */
|
||||
u8 *data; /* The content of the whole page */
|
||||
u8 *ptr; /* Used for moving information around in data[] */
|
||||
u8 *endPtr; /* End of the loop */
|
||||
|
||||
int nSkip = (iChild ? 4 : 0);
|
||||
|
||||
@ -5504,9 +5515,12 @@ static void insertCell(
|
||||
if( iChild ){
|
||||
put4byte(&data[idx], iChild);
|
||||
}
|
||||
for(j=end, ptr=&data[j]; j>ins; j-=2, ptr-=2){
|
||||
ptr[0] = ptr[-2];
|
||||
ptr[1] = ptr[-1];
|
||||
ptr = &data[end];
|
||||
endPtr = &data[ins];
|
||||
assert( (SQLITE_PTR_TO_INT(ptr)&1)==0 ); /* ptr is always 2-byte aligned */
|
||||
while( ptr>endPtr ){
|
||||
*(u16*)ptr = *(u16*)&ptr[-2];
|
||||
ptr -= 2;
|
||||
}
|
||||
put2byte(&data[ins], idx);
|
||||
put2byte(&data[pPage->hdrOffset+3], pPage->nCell);
|
||||
@ -5551,10 +5565,11 @@ static void assemblePage(
|
||||
pCellptr = &data[pPage->cellOffset + nCell*2];
|
||||
cellbody = nUsable;
|
||||
for(i=nCell-1; i>=0; i--){
|
||||
u16 sz = aSize[i];
|
||||
pCellptr -= 2;
|
||||
cellbody -= aSize[i];
|
||||
cellbody -= sz;
|
||||
put2byte(pCellptr, cellbody);
|
||||
memcpy(&data[cellbody], apCell[i], aSize[i]);
|
||||
memcpy(&data[cellbody], apCell[i], sz);
|
||||
}
|
||||
put2byte(&data[hdr+3], nCell);
|
||||
put2byte(&data[hdr+5], cellbody);
|
||||
@ -6008,12 +6023,24 @@ static int balance_nonroot(
|
||||
memcpy(pOld->aData, apOld[i]->aData, pBt->pageSize);
|
||||
|
||||
limit = pOld->nCell+pOld->nOverflow;
|
||||
for(j=0; j<limit; j++){
|
||||
assert( nCell<nMaxCells );
|
||||
apCell[nCell] = findOverflowCell(pOld, j);
|
||||
szCell[nCell] = cellSizePtr(pOld, apCell[nCell]);
|
||||
nCell++;
|
||||
}
|
||||
if( pOld->nOverflow>0 ){
|
||||
for(j=0; j<limit; j++){
|
||||
assert( nCell<nMaxCells );
|
||||
apCell[nCell] = findOverflowCell(pOld, j);
|
||||
szCell[nCell] = cellSizePtr(pOld, apCell[nCell]);
|
||||
nCell++;
|
||||
}
|
||||
}else{
|
||||
u8 *aData = pOld->aData;
|
||||
u16 maskPage = pOld->maskPage;
|
||||
u16 cellOffset = pOld->cellOffset;
|
||||
for(j=0; j<limit; j++){
|
||||
assert( nCell<nMaxCells );
|
||||
apCell[nCell] = findCellv2(aData, maskPage, cellOffset, j);
|
||||
szCell[nCell] = cellSizePtr(pOld, apCell[nCell]);
|
||||
nCell++;
|
||||
}
|
||||
}
|
||||
if( i<nOld-1 && !leafData){
|
||||
u16 sz = (u16)szNew[i];
|
||||
u8 *pTemp;
|
||||
|
||||
@ -42,6 +42,7 @@ typedef struct BtShared BtShared;
|
||||
|
||||
|
||||
int sqlite3BtreeOpen(
|
||||
sqlite3_vfs *pVfs, /* VFS to use with this b-tree */
|
||||
const char *zFilename, /* Name of database file to open */
|
||||
sqlite3 *db, /* Associated database connection */
|
||||
Btree **ppBtree, /* Return open Btree* here */
|
||||
|
||||
15
src/build.c
15
src/build.c
@ -200,9 +200,7 @@ void sqlite3FinishCoding(Parse *pParse){
|
||||
/* A minimum of one cursor is required if autoincrement is used
|
||||
* See ticket [a696379c1f08866] */
|
||||
if( pParse->pAinc!=0 && pParse->nTab==0 ) pParse->nTab = 1;
|
||||
sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem,
|
||||
pParse->nTab, pParse->nMaxArg, pParse->explain,
|
||||
pParse->isMultiWrite && pParse->mayAbort);
|
||||
sqlite3VdbeMakeReady(v, pParse);
|
||||
pParse->rc = SQLITE_DONE;
|
||||
pParse->colNamesSet = 0;
|
||||
}else{
|
||||
@ -1621,8 +1619,8 @@ void sqlite3EndTable(
|
||||
#endif
|
||||
|
||||
/* Reparse everything to update our internal data structures */
|
||||
sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0,
|
||||
sqlite3MPrintf(db, "tbl_name='%q'",p->zName), P4_DYNAMIC);
|
||||
sqlite3VdbeAddParseSchemaOp(v, iDb,
|
||||
sqlite3MPrintf(db, "tbl_name='%q'", p->zName));
|
||||
}
|
||||
|
||||
|
||||
@ -2819,9 +2817,8 @@ Index *sqlite3CreateIndex(
|
||||
if( pTblName ){
|
||||
sqlite3RefillIndex(pParse, pIndex, iMem);
|
||||
sqlite3ChangeCookie(pParse, iDb);
|
||||
sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0,
|
||||
sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName),
|
||||
P4_DYNAMIC);
|
||||
sqlite3VdbeAddParseSchemaOp(v, iDb,
|
||||
sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName));
|
||||
sqlite3VdbeAddOp1(v, OP_Expire, 0);
|
||||
}
|
||||
}
|
||||
@ -3443,7 +3440,7 @@ int sqlite3OpenTempDatabase(Parse *pParse){
|
||||
SQLITE_OPEN_DELETEONCLOSE |
|
||||
SQLITE_OPEN_TEMP_DB;
|
||||
|
||||
rc = sqlite3BtreeOpen(0, db, &pBt, 0, flags);
|
||||
rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pBt, 0, flags);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3ErrorMsg(pParse, "unable to open a temporary database "
|
||||
"file for storing temporary tables");
|
||||
|
||||
164
src/date.c
164
src/date.c
@ -50,22 +50,6 @@
|
||||
|
||||
#ifndef SQLITE_OMIT_DATETIME_FUNCS
|
||||
|
||||
/*
|
||||
** On recent Windows platforms, the localtime_s() function is available
|
||||
** as part of the "Secure CRT". It is essentially equivalent to
|
||||
** localtime_r() available under most POSIX platforms, except that the
|
||||
** order of the parameters is reversed.
|
||||
**
|
||||
** See http://msdn.microsoft.com/en-us/library/a442x3ye(VS.80).aspx.
|
||||
**
|
||||
** If the user has not indicated to use localtime_r() or localtime_s()
|
||||
** already, check for an MSVC build environment that provides
|
||||
** localtime_s().
|
||||
*/
|
||||
#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE_LOCALTIME_S) && \
|
||||
defined(_MSC_VER) && defined(_CRT_INSECURE_DEPRECATE)
|
||||
#define HAVE_LOCALTIME_S 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
** A structure for holding a single date and time.
|
||||
@ -411,15 +395,83 @@ static void clearYMD_HMS_TZ(DateTime *p){
|
||||
p->validTZ = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** On recent Windows platforms, the localtime_s() function is available
|
||||
** as part of the "Secure CRT". It is essentially equivalent to
|
||||
** localtime_r() available under most POSIX platforms, except that the
|
||||
** order of the parameters is reversed.
|
||||
**
|
||||
** See http://msdn.microsoft.com/en-us/library/a442x3ye(VS.80).aspx.
|
||||
**
|
||||
** If the user has not indicated to use localtime_r() or localtime_s()
|
||||
** already, check for an MSVC build environment that provides
|
||||
** localtime_s().
|
||||
*/
|
||||
#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE_LOCALTIME_S) && \
|
||||
defined(_MSC_VER) && defined(_CRT_INSECURE_DEPRECATE)
|
||||
#define HAVE_LOCALTIME_S 1
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_LOCALTIME
|
||||
/*
|
||||
** Compute the difference (in milliseconds)
|
||||
** between localtime and UTC (a.k.a. GMT)
|
||||
** for the time value p where p is in UTC.
|
||||
** The following routine implements the rough equivalent of localtime_r()
|
||||
** using whatever operating-system specific localtime facility that
|
||||
** is available. This routine returns 0 on success and
|
||||
** non-zero on any kind of error.
|
||||
**
|
||||
** If the sqlite3GlobalConfig.bLocaltimeFault variable is true then this
|
||||
** routine will always fail.
|
||||
*/
|
||||
static sqlite3_int64 localtimeOffset(DateTime *p){
|
||||
static int osLocaltime(time_t *t, struct tm *pTm){
|
||||
int rc;
|
||||
#if (!defined(HAVE_LOCALTIME_R) || !HAVE_LOCALTIME_R) \
|
||||
&& (!defined(HAVE_LOCALTIME_S) || !HAVE_LOCALTIME_S)
|
||||
struct tm *pX;
|
||||
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
sqlite3_mutex_enter(mutex);
|
||||
pX = localtime(t);
|
||||
#ifndef SQLITE_OMIT_BUILTIN_TEST
|
||||
if( sqlite3GlobalConfig.bLocaltimeFault ) pX = 0;
|
||||
#endif
|
||||
if( pX ) *pTm = *pX;
|
||||
sqlite3_mutex_leave(mutex);
|
||||
rc = pX==0;
|
||||
#else
|
||||
#ifndef SQLITE_OMIT_BUILTIN_TEST
|
||||
if( sqlite3GlobalConfig.bLocaltimeFault ) return 1;
|
||||
#endif
|
||||
#if defined(HAVE_LOCALTIME_R) && HAVE_LOCALTIME_R
|
||||
rc = localtime_r(t, pTm)==0;
|
||||
#else
|
||||
rc = localtime_s(pTm, t);
|
||||
#endif /* HAVE_LOCALTIME_R */
|
||||
#endif /* HAVE_LOCALTIME_R || HAVE_LOCALTIME_S */
|
||||
return rc;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_LOCALTIME */
|
||||
|
||||
|
||||
#ifndef SQLITE_OMIT_LOCALTIME
|
||||
/*
|
||||
** Compute the difference (in milliseconds) between localtime and UTC
|
||||
** (a.k.a. GMT) for the time value p where p is in UTC. If no error occurs,
|
||||
** return this value and set *pRc to SQLITE_OK.
|
||||
**
|
||||
** Or, if an error does occur, set *pRc to SQLITE_ERROR. The returned value
|
||||
** is undefined in this case.
|
||||
*/
|
||||
static sqlite3_int64 localtimeOffset(
|
||||
DateTime *p, /* Date at which to calculate offset */
|
||||
sqlite3_context *pCtx, /* Write error here if one occurs */
|
||||
int *pRc /* OUT: Error code. SQLITE_OK or ERROR */
|
||||
){
|
||||
DateTime x, y;
|
||||
time_t t;
|
||||
struct tm sLocal;
|
||||
|
||||
/* Initialize the contents of sLocal to avoid a compiler warning. */
|
||||
memset(&sLocal, 0, sizeof(sLocal));
|
||||
|
||||
x = *p;
|
||||
computeYMD_HMS(&x);
|
||||
if( x.Y<1971 || x.Y>=2038 ){
|
||||
@ -437,47 +489,23 @@ static sqlite3_int64 localtimeOffset(DateTime *p){
|
||||
x.validJD = 0;
|
||||
computeJD(&x);
|
||||
t = (time_t)(x.iJD/1000 - 21086676*(i64)10000);
|
||||
#ifdef HAVE_LOCALTIME_R
|
||||
{
|
||||
struct tm sLocal;
|
||||
localtime_r(&t, &sLocal);
|
||||
y.Y = sLocal.tm_year + 1900;
|
||||
y.M = sLocal.tm_mon + 1;
|
||||
y.D = sLocal.tm_mday;
|
||||
y.h = sLocal.tm_hour;
|
||||
y.m = sLocal.tm_min;
|
||||
y.s = sLocal.tm_sec;
|
||||
if( osLocaltime(&t, &sLocal) ){
|
||||
sqlite3_result_error(pCtx, "local time unavailable", -1);
|
||||
*pRc = SQLITE_ERROR;
|
||||
return 0;
|
||||
}
|
||||
#elif defined(HAVE_LOCALTIME_S) && HAVE_LOCALTIME_S
|
||||
{
|
||||
struct tm sLocal;
|
||||
localtime_s(&sLocal, &t);
|
||||
y.Y = sLocal.tm_year + 1900;
|
||||
y.M = sLocal.tm_mon + 1;
|
||||
y.D = sLocal.tm_mday;
|
||||
y.h = sLocal.tm_hour;
|
||||
y.m = sLocal.tm_min;
|
||||
y.s = sLocal.tm_sec;
|
||||
}
|
||||
#else
|
||||
{
|
||||
struct tm *pTm;
|
||||
sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
|
||||
pTm = localtime(&t);
|
||||
y.Y = pTm->tm_year + 1900;
|
||||
y.M = pTm->tm_mon + 1;
|
||||
y.D = pTm->tm_mday;
|
||||
y.h = pTm->tm_hour;
|
||||
y.m = pTm->tm_min;
|
||||
y.s = pTm->tm_sec;
|
||||
sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
|
||||
}
|
||||
#endif
|
||||
y.Y = sLocal.tm_year + 1900;
|
||||
y.M = sLocal.tm_mon + 1;
|
||||
y.D = sLocal.tm_mday;
|
||||
y.h = sLocal.tm_hour;
|
||||
y.m = sLocal.tm_min;
|
||||
y.s = sLocal.tm_sec;
|
||||
y.validYMD = 1;
|
||||
y.validHMS = 1;
|
||||
y.validJD = 0;
|
||||
y.validTZ = 0;
|
||||
computeJD(&y);
|
||||
*pRc = SQLITE_OK;
|
||||
return y.iJD - x.iJD;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_LOCALTIME */
|
||||
@ -501,9 +529,12 @@ static sqlite3_int64 localtimeOffset(DateTime *p){
|
||||
** localtime
|
||||
** utc
|
||||
**
|
||||
** Return 0 on success and 1 if there is any kind of error.
|
||||
** Return 0 on success and 1 if there is any kind of error. If the error
|
||||
** is in a system call (i.e. localtime()), then an error message is written
|
||||
** to context pCtx. If the error is an unrecognized modifier, no error is
|
||||
** written to pCtx.
|
||||
*/
|
||||
static int parseModifier(const char *zMod, DateTime *p){
|
||||
static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){
|
||||
int rc = 1;
|
||||
int n;
|
||||
double r;
|
||||
@ -523,9 +554,8 @@ static int parseModifier(const char *zMod, DateTime *p){
|
||||
*/
|
||||
if( strcmp(z, "localtime")==0 ){
|
||||
computeJD(p);
|
||||
p->iJD += localtimeOffset(p);
|
||||
p->iJD += localtimeOffset(p, pCtx, &rc);
|
||||
clearYMD_HMS_TZ(p);
|
||||
rc = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -546,11 +576,12 @@ static int parseModifier(const char *zMod, DateTime *p){
|
||||
else if( strcmp(z, "utc")==0 ){
|
||||
sqlite3_int64 c1;
|
||||
computeJD(p);
|
||||
c1 = localtimeOffset(p);
|
||||
p->iJD -= c1;
|
||||
clearYMD_HMS_TZ(p);
|
||||
p->iJD += c1 - localtimeOffset(p);
|
||||
rc = 0;
|
||||
c1 = localtimeOffset(p, pCtx, &rc);
|
||||
if( rc==SQLITE_OK ){
|
||||
p->iJD -= c1;
|
||||
clearYMD_HMS_TZ(p);
|
||||
p->iJD += c1 - localtimeOffset(p, pCtx, &rc);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
@ -731,9 +762,8 @@ static int isDate(
|
||||
}
|
||||
}
|
||||
for(i=1; i<argc; i++){
|
||||
if( (z = sqlite3_value_text(argv[i]))==0 || parseModifier((char*)z, p) ){
|
||||
return 1;
|
||||
}
|
||||
z = sqlite3_value_text(argv[i]);
|
||||
if( z==0 || parseModifier(context, (char*)z, p) ) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -401,6 +401,7 @@ void sqlite3DeleteFrom(
|
||||
const char *pVTab = (const char *)sqlite3GetVTable(db, pTab);
|
||||
sqlite3VtabMakeWritable(pParse, pTab);
|
||||
sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iRowid, pVTab, P4_VTAB);
|
||||
sqlite3VdbeChangeP5(v, OE_Abort);
|
||||
sqlite3MayAbort(pParse);
|
||||
}else
|
||||
#endif
|
||||
@ -635,8 +636,14 @@ int sqlite3GenerateIndexKey(
|
||||
}
|
||||
}
|
||||
if( doMakeRec ){
|
||||
const char *zAff;
|
||||
if( pTab->pSelect || (pParse->db->flags & SQLITE_IdxRealAsInt)!=0 ){
|
||||
zAff = 0;
|
||||
}else{
|
||||
zAff = sqlite3IndexAffinityStr(v, pIdx);
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol+1, regOut);
|
||||
sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), P4_TRANSIENT);
|
||||
sqlite3VdbeChangeP4(v, -1, zAff, P4_TRANSIENT);
|
||||
}
|
||||
sqlite3ReleaseTempRange(pParse, regBase, nCol+1);
|
||||
return regBase;
|
||||
|
||||
90
src/expr.c
90
src/expr.c
@ -555,53 +555,53 @@ void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){
|
||||
/* Wildcard of the form "?". Assign the next variable number */
|
||||
assert( z[0]=='?' );
|
||||
pExpr->iColumn = (ynVar)(++pParse->nVar);
|
||||
}else if( z[0]=='?' ){
|
||||
/* Wildcard of the form "?nnn". Convert "nnn" to an integer and
|
||||
** use it as the variable number */
|
||||
i64 i;
|
||||
int bOk = 0==sqlite3Atoi64(&z[1], &i, sqlite3Strlen30(&z[1]), SQLITE_UTF8);
|
||||
pExpr->iColumn = (ynVar)i;
|
||||
testcase( i==0 );
|
||||
testcase( i==1 );
|
||||
testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]-1 );
|
||||
testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] );
|
||||
if( bOk==0 || i<1 || i>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){
|
||||
sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d",
|
||||
db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]);
|
||||
}
|
||||
if( i>pParse->nVar ){
|
||||
pParse->nVar = (int)i;
|
||||
}
|
||||
}else{
|
||||
/* Wildcards like ":aaa", "$aaa" or "@aaa". Reuse the same variable
|
||||
** number as the prior appearance of the same name, or if the name
|
||||
** has never appeared before, reuse the same variable number
|
||||
*/
|
||||
int i;
|
||||
u32 n;
|
||||
n = sqlite3Strlen30(z);
|
||||
for(i=0; i<pParse->nVarExpr; i++){
|
||||
Expr *pE = pParse->apVarExpr[i];
|
||||
assert( pE!=0 );
|
||||
if( memcmp(pE->u.zToken, z, n)==0 && pE->u.zToken[n]==0 ){
|
||||
pExpr->iColumn = pE->iColumn;
|
||||
break;
|
||||
ynVar x = 0;
|
||||
u32 n = sqlite3Strlen30(z);
|
||||
if( z[0]=='?' ){
|
||||
/* Wildcard of the form "?nnn". Convert "nnn" to an integer and
|
||||
** use it as the variable number */
|
||||
i64 i;
|
||||
int bOk = 0==sqlite3Atoi64(&z[1], &i, n-1, SQLITE_UTF8);
|
||||
pExpr->iColumn = x = (ynVar)i;
|
||||
testcase( i==0 );
|
||||
testcase( i==1 );
|
||||
testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]-1 );
|
||||
testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] );
|
||||
if( bOk==0 || i<1 || i>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){
|
||||
sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d",
|
||||
db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]);
|
||||
x = 0;
|
||||
}
|
||||
if( i>pParse->nVar ){
|
||||
pParse->nVar = (int)i;
|
||||
}
|
||||
}else{
|
||||
/* Wildcards like ":aaa", "$aaa" or "@aaa". Reuse the same variable
|
||||
** number as the prior appearance of the same name, or if the name
|
||||
** has never appeared before, reuse the same variable number
|
||||
*/
|
||||
ynVar i;
|
||||
for(i=0; i<pParse->nzVar; i++){
|
||||
if( pParse->azVar[i] && memcmp(pParse->azVar[i],z,n+1)==0 ){
|
||||
pExpr->iColumn = x = (ynVar)i+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( x==0 ) x = pExpr->iColumn = (ynVar)(++pParse->nVar);
|
||||
}
|
||||
if( i>=pParse->nVarExpr ){
|
||||
pExpr->iColumn = (ynVar)(++pParse->nVar);
|
||||
if( pParse->nVarExpr>=pParse->nVarExprAlloc-1 ){
|
||||
pParse->nVarExprAlloc += pParse->nVarExprAlloc + 10;
|
||||
pParse->apVarExpr =
|
||||
sqlite3DbReallocOrFree(
|
||||
db,
|
||||
pParse->apVarExpr,
|
||||
pParse->nVarExprAlloc*sizeof(pParse->apVarExpr[0])
|
||||
);
|
||||
if( x>0 ){
|
||||
if( x>pParse->nzVar ){
|
||||
char **a;
|
||||
a = sqlite3DbRealloc(db, pParse->azVar, x*sizeof(a[0]));
|
||||
if( a==0 ) return; /* Error reported through db->mallocFailed */
|
||||
pParse->azVar = a;
|
||||
memset(&a[pParse->nzVar], 0, (x-pParse->nzVar)*sizeof(a[0]));
|
||||
pParse->nzVar = x;
|
||||
}
|
||||
if( !db->mallocFailed ){
|
||||
assert( pParse->apVarExpr!=0 );
|
||||
pParse->apVarExpr[pParse->nVarExpr++] = pExpr;
|
||||
if( z[0]!='?' || pParse->azVar[x-1]==0 ){
|
||||
sqlite3DbFree(db, pParse->azVar[x-1]);
|
||||
pParse->azVar[x-1] = sqlite3DbStrNDup(db, z, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2345,7 +2345,9 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
assert( pExpr->u.zToken[0]!=0 );
|
||||
sqlite3VdbeAddOp2(v, OP_Variable, pExpr->iColumn, target);
|
||||
if( pExpr->u.zToken[1]!=0 ){
|
||||
sqlite3VdbeChangeP4(v, -1, pExpr->u.zToken, P4_TRANSIENT);
|
||||
assert( pExpr->u.zToken[0]=='?'
|
||||
|| strcmp(pExpr->u.zToken, pParse->azVar[pExpr->iColumn-1])==0 );
|
||||
sqlite3VdbeChangeP4(v, -1, pParse->azVar[pExpr->iColumn-1], P4_STATIC);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
14
src/fkey.c
14
src/fkey.c
@ -386,13 +386,25 @@ static void fkLookupParent(
|
||||
/* If the parent table is the same as the child table, and we are about
|
||||
** to increment the constraint-counter (i.e. this is an INSERT operation),
|
||||
** then check if the row being inserted matches itself. If so, do not
|
||||
** increment the constraint-counter. */
|
||||
** increment the constraint-counter.
|
||||
**
|
||||
** If any of the parent-key values are NULL, then the row cannot match
|
||||
** itself. So set JUMPIFNULL to make sure we do the OP_Found if any
|
||||
** of the parent-key values are NULL (at this point it is known that
|
||||
** none of the child key values are).
|
||||
*/
|
||||
if( pTab==pFKey->pFrom && nIncr==1 ){
|
||||
int iJump = sqlite3VdbeCurrentAddr(v) + nCol + 1;
|
||||
for(i=0; i<nCol; i++){
|
||||
int iChild = aiCol[i]+1+regData;
|
||||
int iParent = pIdx->aiColumn[i]+1+regData;
|
||||
assert( aiCol[i]!=pTab->iPKey );
|
||||
if( pIdx->aiColumn[i]==pTab->iPKey ){
|
||||
/* The parent key is a composite key that includes the IPK column */
|
||||
iParent = regData;
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_Ne, iChild, iJump, iParent);
|
||||
sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk);
|
||||
}
|
||||
|
||||
30
src/func.c
30
src/func.c
@ -506,10 +506,10 @@ struct compareInfo {
|
||||
** whereas only characters less than 0x80 do in ASCII.
|
||||
*/
|
||||
#if defined(SQLITE_EBCDIC)
|
||||
# define sqlite3Utf8Read(A,C) (*(A++))
|
||||
# define GlogUpperToLower(A) A = sqlite3UpperToLower[A]
|
||||
# define sqlite3Utf8Read(A,C) (*(A++))
|
||||
# define GlogUpperToLower(A) A = sqlite3UpperToLower[A]
|
||||
#else
|
||||
# define GlogUpperToLower(A) if( A<0x80 ){ A = sqlite3UpperToLower[A]; }
|
||||
# define GlogUpperToLower(A) if( !((A)&~0x7f) ){ A = sqlite3UpperToLower[A]; }
|
||||
#endif
|
||||
|
||||
static const struct compareInfo globInfo = { '*', '?', '[', 0 };
|
||||
@ -552,9 +552,9 @@ static int patternCompare(
|
||||
const u8 *zPattern, /* The glob pattern */
|
||||
const u8 *zString, /* The string to compare against the glob */
|
||||
const struct compareInfo *pInfo, /* Information about how to do the compare */
|
||||
const int esc /* The escape character */
|
||||
u32 esc /* The escape character */
|
||||
){
|
||||
int c, c2;
|
||||
u32 c, c2;
|
||||
int invert;
|
||||
int seen;
|
||||
u8 matchOne = pInfo->matchOne;
|
||||
@ -608,7 +608,7 @@ static int patternCompare(
|
||||
return 0;
|
||||
}
|
||||
}else if( c==matchSet ){
|
||||
int prior_c = 0;
|
||||
u32 prior_c = 0;
|
||||
assert( esc==0 ); /* This only occurs for GLOB, not LIKE */
|
||||
seen = 0;
|
||||
invert = 0;
|
||||
@ -684,7 +684,7 @@ static void likeFunc(
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const unsigned char *zA, *zB;
|
||||
int escape = 0;
|
||||
u32 escape = 0;
|
||||
int nPat;
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
|
||||
@ -774,6 +774,21 @@ static void sourceidFunc(
|
||||
sqlite3_result_text(context, sqlite3_sourceid(), -1, SQLITE_STATIC);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the sqlite_log() function. This is a wrapper around
|
||||
** sqlite3_log(). The return value is NULL. The function exists purely for
|
||||
** its side-effects.
|
||||
*/
|
||||
static void errlogFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
UNUSED_PARAMETER(argc);
|
||||
UNUSED_PARAMETER(context);
|
||||
sqlite3_log(sqlite3_value_int(argv[0]), "%s", sqlite3_value_text(argv[1]));
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the sqlite_compileoption_used() function.
|
||||
** The result is an integer that identifies if the compiler option
|
||||
@ -1546,6 +1561,7 @@ void sqlite3RegisterGlobalFunctions(void){
|
||||
FUNCTION(nullif, 2, 0, 1, nullifFunc ),
|
||||
FUNCTION(sqlite_version, 0, 0, 0, versionFunc ),
|
||||
FUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ),
|
||||
FUNCTION(sqlite_log, 2, 0, 0, errlogFunc ),
|
||||
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
|
||||
FUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ),
|
||||
FUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ),
|
||||
|
||||
@ -129,7 +129,9 @@ const unsigned char sqlite3CtypeMap[256] = {
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef SQLITE_USE_URI
|
||||
# define SQLITE_USE_URI 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The following singleton contains the global configuration for
|
||||
@ -139,6 +141,7 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
|
||||
SQLITE_DEFAULT_MEMSTATUS, /* bMemstat */
|
||||
1, /* bCoreMutex */
|
||||
SQLITE_THREADSAFE==1, /* bFullMutex */
|
||||
SQLITE_USE_URI, /* bOpenUri */
|
||||
0x7ffffffe, /* mxStrlen */
|
||||
100, /* szLookaside */
|
||||
500, /* nLookaside */
|
||||
@ -166,6 +169,7 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
|
||||
0, /* nRefInitMutex */
|
||||
0, /* xLog */
|
||||
0, /* pLogArg */
|
||||
0, /* bLocaltimeFault */
|
||||
};
|
||||
|
||||
|
||||
|
||||
13
src/insert.c
13
src/insert.c
@ -969,6 +969,7 @@ void sqlite3Insert(
|
||||
const char *pVTab = (const char *)sqlite3GetVTable(db, pTab);
|
||||
sqlite3VtabMakeWritable(pParse, pTab);
|
||||
sqlite3VdbeAddOp4(v, OP_VUpdate, 1, pTab->nCol+2, regIns, pVTab, P4_VTAB);
|
||||
sqlite3VdbeChangeP5(v, onError==OE_Default ? OE_Abort : onError);
|
||||
sqlite3MayAbort(pParse);
|
||||
}else
|
||||
#endif
|
||||
@ -1734,6 +1735,18 @@ static int xferOptimization(
|
||||
return 0; /* Tables have different CHECK constraints. Ticket #2252 */
|
||||
}
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_FOREIGN_KEY
|
||||
/* Disallow the transfer optimization if the destination table constains
|
||||
** any foreign key constraints. This is more restrictive than necessary.
|
||||
** But the main beneficiary of the transfer optimization is the VACUUM
|
||||
** command, and the VACUUM command disables foreign key constraints. So
|
||||
** the extra complication to make this rule less restrictive is probably
|
||||
** not worth the effort. Ticket [6284df89debdfa61db8073e062908af0c9b6118e]
|
||||
*/
|
||||
if( (pParse->db->flags & SQLITE_ForeignKeys)!=0 && pDest->pFKey!=0 ){
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If we get this far, it means either:
|
||||
**
|
||||
|
||||
303
src/main.c
303
src/main.c
@ -426,6 +426,11 @@ int sqlite3_config(int op, ...){
|
||||
break;
|
||||
}
|
||||
|
||||
case SQLITE_CONFIG_URI: {
|
||||
sqlite3GlobalConfig.bOpenUri = va_arg(ap, int);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
rc = SQLITE_ERROR;
|
||||
break;
|
||||
@ -1785,6 +1790,236 @@ int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){
|
||||
return oldLimit; /* IMP: R-53341-35419 */
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is used to parse both URIs and non-URI filenames passed by the
|
||||
** user to API functions sqlite3_open() or sqlite3_open_v2(), and for database
|
||||
** URIs specified as part of ATTACH statements.
|
||||
**
|
||||
** The first argument to this function is the name of the VFS to use (or
|
||||
** a NULL to signify the default VFS) if the URI does not contain a "vfs=xxx"
|
||||
** query parameter. The second argument contains the URI (or non-URI filename)
|
||||
** itself. When this function is called the *pFlags variable should contain
|
||||
** the default flags to open the database handle with. The value stored in
|
||||
** *pFlags may be updated before returning if the URI filename contains
|
||||
** "cache=xxx" or "mode=xxx" query parameters.
|
||||
**
|
||||
** If successful, SQLITE_OK is returned. In this case *ppVfs is set to point to
|
||||
** the VFS that should be used to open the database file. *pzFile is set to
|
||||
** point to a buffer containing the name of the file to open. It is the
|
||||
** responsibility of the caller to eventually call sqlite3_free() to release
|
||||
** this buffer.
|
||||
**
|
||||
** If an error occurs, then an SQLite error code is returned and *pzErrMsg
|
||||
** may be set to point to a buffer containing an English language error
|
||||
** message. It is the responsibility of the caller to eventually release
|
||||
** this buffer by calling sqlite3_free().
|
||||
*/
|
||||
int sqlite3ParseUri(
|
||||
const char *zDefaultVfs, /* VFS to use if no "vfs=xxx" query option */
|
||||
const char *zUri, /* Nul-terminated URI to parse */
|
||||
unsigned int *pFlags, /* IN/OUT: SQLITE_OPEN_XXX flags */
|
||||
sqlite3_vfs **ppVfs, /* OUT: VFS to use */
|
||||
char **pzFile, /* OUT: Filename component of URI */
|
||||
char **pzErrMsg /* OUT: Error message (if rc!=SQLITE_OK) */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
unsigned int flags = *pFlags;
|
||||
const char *zVfs = zDefaultVfs;
|
||||
char *zFile;
|
||||
char c;
|
||||
int nUri = sqlite3Strlen30(zUri);
|
||||
|
||||
assert( *pzErrMsg==0 );
|
||||
|
||||
if( ((flags & SQLITE_OPEN_URI) || sqlite3GlobalConfig.bOpenUri)
|
||||
&& nUri>=5 && memcmp(zUri, "file:", 5)==0
|
||||
){
|
||||
char *zOpt;
|
||||
int eState; /* Parser state when parsing URI */
|
||||
int iIn; /* Input character index */
|
||||
int iOut = 0; /* Output character index */
|
||||
int nByte = nUri+2; /* Bytes of space to allocate */
|
||||
|
||||
/* Make sure the SQLITE_OPEN_URI flag is set to indicate to the VFS xOpen
|
||||
** method that there may be extra parameters following the file-name. */
|
||||
flags |= SQLITE_OPEN_URI;
|
||||
|
||||
for(iIn=0; iIn<nUri; iIn++) nByte += (zUri[iIn]=='&');
|
||||
zFile = sqlite3_malloc(nByte);
|
||||
if( !zFile ) return SQLITE_NOMEM;
|
||||
|
||||
/* Discard the scheme and authority segments of the URI. */
|
||||
if( zUri[5]=='/' && zUri[6]=='/' ){
|
||||
iIn = 7;
|
||||
while( zUri[iIn] && zUri[iIn]!='/' ) iIn++;
|
||||
|
||||
if( iIn!=7 && (iIn!=16 || memcmp("localhost", &zUri[7], 9)) ){
|
||||
*pzErrMsg = sqlite3_mprintf("invalid uri authority: %.*s",
|
||||
iIn-7, &zUri[7]);
|
||||
rc = SQLITE_ERROR;
|
||||
goto parse_uri_out;
|
||||
}
|
||||
}else{
|
||||
iIn = 5;
|
||||
}
|
||||
|
||||
/* Copy the filename and any query parameters into the zFile buffer.
|
||||
** Decode %HH escape codes along the way.
|
||||
**
|
||||
** Within this loop, variable eState may be set to 0, 1 or 2, depending
|
||||
** on the parsing context. As follows:
|
||||
**
|
||||
** 0: Parsing file-name.
|
||||
** 1: Parsing name section of a name=value query parameter.
|
||||
** 2: Parsing value section of a name=value query parameter.
|
||||
*/
|
||||
eState = 0;
|
||||
while( (c = zUri[iIn])!=0 && c!='#' ){
|
||||
iIn++;
|
||||
if( c=='%'
|
||||
&& sqlite3Isxdigit(zUri[iIn])
|
||||
&& sqlite3Isxdigit(zUri[iIn+1])
|
||||
){
|
||||
int octet = (sqlite3HexToInt(zUri[iIn++]) << 4);
|
||||
octet += sqlite3HexToInt(zUri[iIn++]);
|
||||
|
||||
assert( octet>=0 && octet<256 );
|
||||
if( octet==0 ){
|
||||
/* This branch is taken when "%00" appears within the URI. In this
|
||||
** case we ignore all text in the remainder of the path, name or
|
||||
** value currently being parsed. So ignore the current character
|
||||
** and skip to the next "?", "=" or "&", as appropriate. */
|
||||
while( (c = zUri[iIn])!=0 && c!='#'
|
||||
&& (eState!=0 || c!='?')
|
||||
&& (eState!=1 || (c!='=' && c!='&'))
|
||||
&& (eState!=2 || c!='&')
|
||||
){
|
||||
iIn++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
c = octet;
|
||||
}else if( eState==1 && (c=='&' || c=='=') ){
|
||||
if( zFile[iOut-1]==0 ){
|
||||
/* An empty option name. Ignore this option altogether. */
|
||||
while( zUri[iIn] && zUri[iIn]!='#' && zUri[iIn-1]!='&' ) iIn++;
|
||||
continue;
|
||||
}
|
||||
if( c=='&' ){
|
||||
zFile[iOut++] = '\0';
|
||||
}else{
|
||||
eState = 2;
|
||||
}
|
||||
c = 0;
|
||||
}else if( (eState==0 && c=='?') || (eState==2 && c=='&') ){
|
||||
c = 0;
|
||||
eState = 1;
|
||||
}
|
||||
zFile[iOut++] = c;
|
||||
}
|
||||
if( eState==1 ) zFile[iOut++] = '\0';
|
||||
zFile[iOut++] = '\0';
|
||||
zFile[iOut++] = '\0';
|
||||
|
||||
/* Check if there were any options specified that should be interpreted
|
||||
** here. Options that are interpreted here include "vfs" and those that
|
||||
** correspond to flags that may be passed to the sqlite3_open_v2()
|
||||
** method. */
|
||||
zOpt = &zFile[sqlite3Strlen30(zFile)+1];
|
||||
while( zOpt[0] ){
|
||||
int nOpt = sqlite3Strlen30(zOpt);
|
||||
char *zVal = &zOpt[nOpt+1];
|
||||
int nVal = sqlite3Strlen30(zVal);
|
||||
|
||||
if( nOpt==3 && memcmp("vfs", zOpt, 3)==0 ){
|
||||
zVfs = zVal;
|
||||
}else{
|
||||
struct OpenMode {
|
||||
const char *z;
|
||||
int mode;
|
||||
} *aMode = 0;
|
||||
char *zModeType = 0;
|
||||
int mask = 0;
|
||||
int limit = 0;
|
||||
|
||||
if( nOpt==5 && memcmp("cache", zOpt, 5)==0 ){
|
||||
static struct OpenMode aCacheMode[] = {
|
||||
{ "shared", SQLITE_OPEN_SHAREDCACHE },
|
||||
{ "private", SQLITE_OPEN_PRIVATECACHE },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
mask = SQLITE_OPEN_SHAREDCACHE|SQLITE_OPEN_PRIVATECACHE;
|
||||
aMode = aCacheMode;
|
||||
limit = mask;
|
||||
zModeType = "cache";
|
||||
}
|
||||
if( nOpt==4 && memcmp("mode", zOpt, 4)==0 ){
|
||||
static struct OpenMode aOpenMode[] = {
|
||||
{ "ro", SQLITE_OPEN_READONLY },
|
||||
{ "rw", SQLITE_OPEN_READWRITE },
|
||||
{ "rwc", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
mask = SQLITE_OPEN_READONLY|SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
|
||||
aMode = aOpenMode;
|
||||
limit = mask & flags;
|
||||
zModeType = "access";
|
||||
}
|
||||
|
||||
if( aMode ){
|
||||
int i;
|
||||
int mode = 0;
|
||||
for(i=0; aMode[i].z; i++){
|
||||
const char *z = aMode[i].z;
|
||||
if( nVal==sqlite3Strlen30(z) && 0==memcmp(zVal, z, nVal) ){
|
||||
mode = aMode[i].mode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( mode==0 ){
|
||||
*pzErrMsg = sqlite3_mprintf("no such %s mode: %s", zModeType, zVal);
|
||||
rc = SQLITE_ERROR;
|
||||
goto parse_uri_out;
|
||||
}
|
||||
if( mode>limit ){
|
||||
*pzErrMsg = sqlite3_mprintf("%s mode not allowed: %s",
|
||||
zModeType, zVal);
|
||||
rc = SQLITE_PERM;
|
||||
goto parse_uri_out;
|
||||
}
|
||||
flags = (flags & ~mask) | mode;
|
||||
}
|
||||
}
|
||||
|
||||
zOpt = &zVal[nVal+1];
|
||||
}
|
||||
|
||||
}else{
|
||||
zFile = sqlite3_malloc(nUri+2);
|
||||
if( !zFile ) return SQLITE_NOMEM;
|
||||
memcpy(zFile, zUri, nUri);
|
||||
zFile[nUri] = '\0';
|
||||
zFile[nUri+1] = '\0';
|
||||
}
|
||||
|
||||
*ppVfs = sqlite3_vfs_find(zVfs);
|
||||
if( *ppVfs==0 ){
|
||||
*pzErrMsg = sqlite3_mprintf("no such vfs: %s", zVfs);
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
parse_uri_out:
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3_free(zFile);
|
||||
zFile = 0;
|
||||
}
|
||||
*pFlags = flags;
|
||||
*pzFile = zFile;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This routine does the work of opening a database on behalf of
|
||||
** sqlite3_open() and sqlite3_open16(). The database filename "zFilename"
|
||||
@ -1793,12 +2028,14 @@ int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){
|
||||
static int openDatabase(
|
||||
const char *zFilename, /* Database filename UTF-8 encoded */
|
||||
sqlite3 **ppDb, /* OUT: Returned database handle */
|
||||
unsigned flags, /* Operational flags */
|
||||
unsigned int flags, /* Operational flags */
|
||||
const char *zVfs /* Name of the VFS to use */
|
||||
){
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
int isThreadsafe;
|
||||
sqlite3 *db; /* Store allocated handle here */
|
||||
int rc; /* Return code */
|
||||
int isThreadsafe; /* True for threadsafe connections */
|
||||
char *zOpen = 0; /* Filename argument to pass to BtreeOpen() */
|
||||
char *zErrMsg = 0; /* Error message from sqlite3ParseUri() */
|
||||
|
||||
*ppDb = 0;
|
||||
#ifndef SQLITE_OMIT_AUTOINIT
|
||||
@ -1822,7 +2059,7 @@ static int openDatabase(
|
||||
testcase( (1<<(flags&7))==0x02 ); /* READONLY */
|
||||
testcase( (1<<(flags&7))==0x04 ); /* READWRITE */
|
||||
testcase( (1<<(flags&7))==0x40 ); /* READWRITE | CREATE */
|
||||
if( ((1<<(flags&7)) & 0x46)==0 ) return SQLITE_MISUSE;
|
||||
if( ((1<<(flags&7)) & 0x46)==0 ) return SQLITE_MISUSE_BKPT;
|
||||
|
||||
if( sqlite3GlobalConfig.bCoreMutex==0 ){
|
||||
isThreadsafe = 0;
|
||||
@ -1903,13 +2140,6 @@ static int openDatabase(
|
||||
sqlite3HashInit(&db->aModule);
|
||||
#endif
|
||||
|
||||
db->pVfs = sqlite3_vfs_find(zVfs);
|
||||
if( !db->pVfs ){
|
||||
rc = SQLITE_ERROR;
|
||||
sqlite3Error(db, rc, "no such vfs: %s", zVfs);
|
||||
goto opendb_out;
|
||||
}
|
||||
|
||||
/* Add the default collation sequence BINARY. BINARY works for both UTF-8
|
||||
** and UTF-16, so add a version for each to avoid any unnecessary
|
||||
** conversions. The only error that can occur here is a malloc() failure.
|
||||
@ -1932,9 +2162,18 @@ static int openDatabase(
|
||||
createCollation(db, "NOCASE", SQLITE_UTF8, SQLITE_COLL_NOCASE, 0,
|
||||
nocaseCollatingFunc, 0);
|
||||
|
||||
/* Open the backend database driver */
|
||||
/* Parse the filename/URI argument. */
|
||||
db->openFlags = flags;
|
||||
rc = sqlite3BtreeOpen(zFilename, db, &db->aDb[0].pBt, 0,
|
||||
rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg);
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( rc==SQLITE_NOMEM ) db->mallocFailed = 1;
|
||||
sqlite3Error(db, rc, zErrMsg ? "%s" : 0, zErrMsg);
|
||||
sqlite3_free(zErrMsg);
|
||||
goto opendb_out;
|
||||
}
|
||||
|
||||
/* Open the backend database driver */
|
||||
rc = sqlite3BtreeOpen(db->pVfs, zOpen, db, &db->aDb[0].pBt, 0,
|
||||
flags | SQLITE_OPEN_MAIN_DB);
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( rc==SQLITE_IOERR_NOMEM ){
|
||||
@ -2027,6 +2266,7 @@ static int openDatabase(
|
||||
sqlite3_wal_autocheckpoint(db, SQLITE_DEFAULT_WAL_AUTOCHECKPOINT);
|
||||
|
||||
opendb_out:
|
||||
sqlite3_free(zOpen);
|
||||
if( db ){
|
||||
assert( db->mutex!=0 || isThreadsafe==0 || sqlite3GlobalConfig.bFullMutex==0 );
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
@ -2058,7 +2298,7 @@ int sqlite3_open_v2(
|
||||
int flags, /* Flags */
|
||||
const char *zVfs /* Name of VFS module to use */
|
||||
){
|
||||
return openDatabase(filename, ppDb, flags, zVfs);
|
||||
return openDatabase(filename, ppDb, (unsigned int)flags, zVfs);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
@ -2663,8 +2903,41 @@ int sqlite3_test_control(int op, ...){
|
||||
break;
|
||||
}
|
||||
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, int onoff);
|
||||
**
|
||||
** If parameter onoff is non-zero, configure the wrappers so that all
|
||||
** subsequent calls to localtime() and variants fail. If onoff is zero,
|
||||
** undo this setting.
|
||||
*/
|
||||
case SQLITE_TESTCTRL_LOCALTIME_FAULT: {
|
||||
sqlite3GlobalConfig.bLocaltimeFault = va_arg(ap, int);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
va_end(ap);
|
||||
#endif /* SQLITE_OMIT_BUILTIN_TEST */
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This is a utility routine, useful to VFS implementations, that checks
|
||||
** to see if a database file was a URI that contained a specific query
|
||||
** parameter, and if so obtains the value of the query parameter.
|
||||
**
|
||||
** The zFilename argument is the filename pointer passed into the xOpen()
|
||||
** method of a VFS implementation. The zParam argument is the name of the
|
||||
** query parameter we seek. This routine returns the value of the zParam
|
||||
** parameter if it exists. If the parameter does not exist, this routine
|
||||
** returns a NULL pointer.
|
||||
*/
|
||||
const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam){
|
||||
zFilename += sqlite3Strlen30(zFilename) + 1;
|
||||
while( zFilename[0] ){
|
||||
int x = strcmp(zFilename, zParam);
|
||||
zFilename += sqlite3Strlen30(zFilename) + 1;
|
||||
if( x==0 ) return zFilename;
|
||||
zFilename += sqlite3Strlen30(zFilename) + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
11
src/malloc.c
11
src/malloc.c
@ -266,7 +266,7 @@ static int mallocWithAlarm(int n, void **pp){
|
||||
sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, n);
|
||||
if( mem0.alarmCallback!=0 ){
|
||||
int nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
|
||||
if( nUsed+nFull >= mem0.alarmThreshold ){
|
||||
if( nUsed >= mem0.alarmThreshold - nFull ){
|
||||
mem0.nearlyFull = 1;
|
||||
sqlite3MallocAlarm(nFull);
|
||||
}else{
|
||||
@ -507,7 +507,7 @@ void sqlite3DbFree(sqlite3 *db, void *p){
|
||||
** Change the size of an existing memory allocation
|
||||
*/
|
||||
void *sqlite3Realloc(void *pOld, int nBytes){
|
||||
int nOld, nNew;
|
||||
int nOld, nNew, nDiff;
|
||||
void *pNew;
|
||||
if( pOld==0 ){
|
||||
return sqlite3Malloc(nBytes); /* IMP: R-28354-25769 */
|
||||
@ -530,9 +530,10 @@ void *sqlite3Realloc(void *pOld, int nBytes){
|
||||
}else if( sqlite3GlobalConfig.bMemstat ){
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, nBytes);
|
||||
if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)+nNew-nOld >=
|
||||
mem0.alarmThreshold ){
|
||||
sqlite3MallocAlarm(nNew-nOld);
|
||||
nDiff = nNew - nOld;
|
||||
if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >=
|
||||
mem0.alarmThreshold-nDiff ){
|
||||
sqlite3MallocAlarm(nDiff);
|
||||
}
|
||||
assert( sqlite3MemdebugHasType(pOld, MEMTYPE_HEAP) );
|
||||
assert( sqlite3MemdebugNoType(pOld, ~MEMTYPE_HEAP) );
|
||||
|
||||
@ -138,6 +138,10 @@
|
||||
# include <sys/mount.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UTIME
|
||||
# include <utime.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Allowed values of unixFile.fsFlags
|
||||
*/
|
||||
@ -281,6 +285,18 @@ struct unixFile {
|
||||
#define threadid 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Different Unix systems declare open() in different ways. Same use
|
||||
** open(const char*,int,mode_t). Others use open(const char*,int,...).
|
||||
** The difference is important when using a pointer to the function.
|
||||
**
|
||||
** The safest way to deal with the problem is to always use this wrapper
|
||||
** which always has the same well-defined interface.
|
||||
*/
|
||||
static int posixOpen(const char *zFile, int flags, int mode){
|
||||
return open(zFile, flags, mode);
|
||||
}
|
||||
|
||||
/*
|
||||
** Many system calls are accessed through pointer-to-functions so that
|
||||
** they may be overridden at runtime to facilitate fault injection during
|
||||
@ -292,8 +308,8 @@ static struct unix_syscall {
|
||||
sqlite3_syscall_ptr pCurrent; /* Current value of the system call */
|
||||
sqlite3_syscall_ptr pDefault; /* Default value */
|
||||
} aSyscall[] = {
|
||||
{ "open", (sqlite3_syscall_ptr)open, 0 },
|
||||
#define osOpen ((int(*)(const char*,int,...))aSyscall[0].pCurrent)
|
||||
{ "open", (sqlite3_syscall_ptr)posixOpen, 0 },
|
||||
#define osOpen ((int(*)(const char*,int,int))aSyscall[0].pCurrent)
|
||||
|
||||
{ "close", (sqlite3_syscall_ptr)close, 0 },
|
||||
#define osClose ((int(*)(int))aSyscall[1].pCurrent)
|
||||
@ -330,7 +346,7 @@ static struct unix_syscall {
|
||||
{ "read", (sqlite3_syscall_ptr)read, 0 },
|
||||
#define osRead ((ssize_t(*)(int,void*,size_t))aSyscall[8].pCurrent)
|
||||
|
||||
#if defined(USE_PREAD) || defined(SQLITE_ENABLE_LOCKING_STYLE)
|
||||
#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE
|
||||
{ "pread", (sqlite3_syscall_ptr)pread, 0 },
|
||||
#else
|
||||
{ "pread", (sqlite3_syscall_ptr)0, 0 },
|
||||
@ -347,7 +363,7 @@ static struct unix_syscall {
|
||||
{ "write", (sqlite3_syscall_ptr)write, 0 },
|
||||
#define osWrite ((ssize_t(*)(int,const void*,size_t))aSyscall[11].pCurrent)
|
||||
|
||||
#if defined(USE_PREAD) || defined(SQLITE_ENABLE_LOCKING_STYLE)
|
||||
#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE
|
||||
{ "pwrite", (sqlite3_syscall_ptr)pwrite, 0 },
|
||||
#else
|
||||
{ "pwrite", (sqlite3_syscall_ptr)0, 0 },
|
||||
@ -933,7 +949,7 @@ struct unixInodeInfo {
|
||||
UnixUnusedFd *pUnused; /* Unused file descriptors to close */
|
||||
unixInodeInfo *pNext; /* List of all unixInodeInfo objects */
|
||||
unixInodeInfo *pPrev; /* .... doubly linked */
|
||||
#if defined(SQLITE_ENABLE_LOCKING_STYLE)
|
||||
#if SQLITE_ENABLE_LOCKING_STYLE
|
||||
unsigned long long sharedByte; /* for AFP simulated shared lock */
|
||||
#endif
|
||||
#if OS_VXWORKS
|
||||
@ -1927,8 +1943,10 @@ static int dotlockLock(sqlite3_file *id, int eFileLock) {
|
||||
*/
|
||||
if( pFile->eFileLock > NO_LOCK ){
|
||||
pFile->eFileLock = eFileLock;
|
||||
#if !OS_VXWORKS
|
||||
/* Always update the timestamp on the old file */
|
||||
#ifdef HAVE_UTIME
|
||||
utime(zLockFile, NULL);
|
||||
#else
|
||||
utimes(zLockFile, NULL);
|
||||
#endif
|
||||
return SQLITE_OK;
|
||||
@ -3090,7 +3108,7 @@ static int unixWrite(
|
||||
SimulateDiskfullError(( wrote=0, amt=1 ));
|
||||
|
||||
if( amt>0 ){
|
||||
if( wrote<0 ){
|
||||
if( wrote<0 && pFile->lastErrno!=ENOSPC ){
|
||||
/* lastErrno set by seekAndWrite */
|
||||
return SQLITE_IOERR_WRITE;
|
||||
}else{
|
||||
@ -3525,7 +3543,8 @@ struct unixShmNode {
|
||||
char *zFilename; /* Name of the mmapped file */
|
||||
int h; /* Open file descriptor */
|
||||
int szRegion; /* Size of shared-memory regions */
|
||||
int nRegion; /* Size of array apRegion */
|
||||
u16 nRegion; /* Size of array apRegion */
|
||||
u8 isReadonly; /* True if read-only */
|
||||
char **apRegion; /* Array of mapped shared-memory regions */
|
||||
int nRef; /* Number of unixShm objects pointing to this */
|
||||
unixShm *pFirst; /* All unixShm objects pointing to this */
|
||||
@ -3757,6 +3776,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
|
||||
(u32)sStat.st_ino, (u32)sStat.st_dev);
|
||||
#else
|
||||
sqlite3_snprintf(nShmFilename, zShmFilename, "%s-shm", pDbFd->zPath);
|
||||
sqlite3FileSuffix3(pDbFd->zPath, zShmFilename);
|
||||
#endif
|
||||
pShmNode->h = -1;
|
||||
pDbFd->pInode->pShmNode = pShmNode;
|
||||
@ -3771,8 +3791,17 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
|
||||
pShmNode->h = robust_open(zShmFilename, O_RDWR|O_CREAT,
|
||||
(sStat.st_mode & 0777));
|
||||
if( pShmNode->h<0 ){
|
||||
rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename);
|
||||
goto shm_open_err;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check to see if another process is holding the dead-man switch.
|
||||
@ -3911,11 +3940,12 @@ static int unixShmMap(
|
||||
while(pShmNode->nRegion<=iRegion){
|
||||
void *pMem;
|
||||
if( pShmNode->h>=0 ){
|
||||
pMem = mmap(0, szRegion, PROT_READ|PROT_WRITE,
|
||||
pMem = mmap(0, szRegion,
|
||||
pShmNode->isReadonly ? PROT_READ : PROT_READ|PROT_WRITE,
|
||||
MAP_SHARED, pShmNode->h, pShmNode->nRegion*szRegion
|
||||
);
|
||||
if( pMem==MAP_FAILED ){
|
||||
rc = SQLITE_IOERR;
|
||||
rc = unixLogError(SQLITE_IOERR_SHMMAP, "mmap", pShmNode->zFilename);
|
||||
goto shmpage_out;
|
||||
}
|
||||
}else{
|
||||
@ -3937,6 +3967,7 @@ shmpage_out:
|
||||
}else{
|
||||
*pp = 0;
|
||||
}
|
||||
if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY;
|
||||
sqlite3_mutex_leave(pShmNode->mutex);
|
||||
return rc;
|
||||
}
|
||||
@ -4790,6 +4821,11 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
|
||||
** corresponding database file and sets *pMode to this value. Whenever
|
||||
** possible, WAL and journal files are created using the same permissions
|
||||
** as the associated database file.
|
||||
**
|
||||
** If the SQLITE_ENABLE_8_3_NAMES option is enabled, then the
|
||||
** original filename is unavailable. But 8_3_NAMES is only used for
|
||||
** FAT filesystems and permissions do not matter there, so just use
|
||||
** the default permissions.
|
||||
*/
|
||||
static int findCreateFileMode(
|
||||
const char *zPath, /* Path of file (possibly) being created */
|
||||
@ -4797,6 +4833,7 @@ static int findCreateFileMode(
|
||||
mode_t *pMode /* OUT: Permissions to open file with */
|
||||
){
|
||||
int rc = SQLITE_OK; /* Return Code */
|
||||
*pMode = SQLITE_DEFAULT_FILE_PERMISSIONS;
|
||||
if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){
|
||||
char zDb[MAX_PATHNAME+1]; /* Database file path */
|
||||
int nDb; /* Number of valid bytes in zDb */
|
||||
@ -4808,15 +4845,15 @@ static int findCreateFileMode(
|
||||
**
|
||||
** "<path to db>-journal"
|
||||
** "<path to db>-wal"
|
||||
** "<path to db>-journal-NNNN"
|
||||
** "<path to db>-wal-NNNN"
|
||||
** "<path to db>-journalNN"
|
||||
** "<path to db>-walNN"
|
||||
**
|
||||
** where NNNN is a 4 digit decimal number. The NNNN naming schemes are
|
||||
** where NN is a 4 digit decimal number. The NN naming schemes are
|
||||
** used by the test_multiplex.c module.
|
||||
*/
|
||||
nDb = sqlite3Strlen30(zPath) - 1;
|
||||
while( nDb>0 && zPath[nDb]!='l' ) nDb--;
|
||||
nDb -= ((flags & SQLITE_OPEN_WAL) ? 3 : 7);
|
||||
while( nDb>0 && zPath[nDb]!='-' ) nDb--;
|
||||
if( nDb==0 ) return SQLITE_OK;
|
||||
memcpy(zDb, zPath, nDb);
|
||||
zDb[nDb] = '\0';
|
||||
|
||||
@ -4827,8 +4864,6 @@ static int findCreateFileMode(
|
||||
}
|
||||
}else if( flags & SQLITE_OPEN_DELETEONCLOSE ){
|
||||
*pMode = 0600;
|
||||
}else{
|
||||
*pMode = SQLITE_DEFAULT_FILE_PERMISSIONS;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
242
src/os_win.c
242
src/os_win.c
@ -118,6 +118,7 @@ struct winFile {
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** Forward prototypes.
|
||||
*/
|
||||
@ -285,7 +286,7 @@ char *sqlite3_win32_mbcs_to_utf8(const char *zFilename){
|
||||
** Convert UTF-8 to multibyte character string. Space to hold the
|
||||
** returned string is obtained from malloc().
|
||||
*/
|
||||
static char *utf8ToMbcs(const char *zFilename){
|
||||
char *sqlite3_win32_utf8_to_mbcs(const char *zFilename){
|
||||
char *zFilenameMbcs;
|
||||
WCHAR *zTmpWide;
|
||||
|
||||
@ -298,6 +299,109 @@ static char *utf8ToMbcs(const char *zFilename){
|
||||
return zFilenameMbcs;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** The return value of getLastErrorMsg
|
||||
** is zero if the error message fits in the buffer, or non-zero
|
||||
** otherwise (if the message was truncated).
|
||||
*/
|
||||
static int getLastErrorMsg(int nBuf, char *zBuf){
|
||||
/* FormatMessage returns 0 on failure. Otherwise it
|
||||
** returns the number of TCHARs written to the output
|
||||
** buffer, excluding the terminating null char.
|
||||
*/
|
||||
DWORD error = GetLastError();
|
||||
DWORD dwLen = 0;
|
||||
char *zOut = 0;
|
||||
|
||||
if( isNT() ){
|
||||
WCHAR *zTempWide = NULL;
|
||||
dwLen = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
error,
|
||||
0,
|
||||
(LPWSTR) &zTempWide,
|
||||
0,
|
||||
0);
|
||||
if( dwLen > 0 ){
|
||||
/* allocate a buffer and convert to UTF8 */
|
||||
zOut = unicodeToUtf8(zTempWide);
|
||||
/* free the system buffer allocated by FormatMessage */
|
||||
LocalFree(zTempWide);
|
||||
}
|
||||
/* 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{
|
||||
char *zTemp = NULL;
|
||||
dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
error,
|
||||
0,
|
||||
(LPSTR) &zTemp,
|
||||
0,
|
||||
0);
|
||||
if( dwLen > 0 ){
|
||||
/* allocate a buffer and convert to UTF8 */
|
||||
zOut = sqlite3_win32_mbcs_to_utf8(zTemp);
|
||||
/* free the system buffer allocated by FormatMessage */
|
||||
LocalFree(zTemp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if( 0 == dwLen ){
|
||||
sqlite3_snprintf(nBuf, zBuf, "OsError 0x%x (%u)", error, error);
|
||||
}else{
|
||||
/* copy a maximum of nBuf chars to output buffer */
|
||||
sqlite3_snprintf(nBuf, zBuf, "%s", zOut);
|
||||
/* free the UTF8 buffer */
|
||||
free(zOut);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
**
|
||||
** This function - winLogErrorAtLine() - is only ever called via the macro
|
||||
** winLogError().
|
||||
**
|
||||
** This routine is invoked after an error occurs in an OS function.
|
||||
** It logs a message using sqlite3_log() containing the current value of
|
||||
** error code and, if possible, the human-readable equivalent from
|
||||
** FormatMessage.
|
||||
**
|
||||
** The first argument passed to the macro should be the error code that
|
||||
** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN).
|
||||
** The two subsequent arguments should be the name of the OS function that
|
||||
** failed and the the associated file-system path, if any.
|
||||
*/
|
||||
#define winLogError(a,b,c) winLogErrorAtLine(a,b,c,__LINE__)
|
||||
static int winLogErrorAtLine(
|
||||
int errcode, /* SQLite error code */
|
||||
const char *zFunc, /* Name of OS function that failed */
|
||||
const char *zPath, /* File path associated with error */
|
||||
int iLine /* Source line number where error occurred */
|
||||
){
|
||||
char zMsg[500]; /* Human readable error text */
|
||||
int i; /* Loop counter */
|
||||
DWORD iErrno = GetLastError(); /* Error code */
|
||||
|
||||
zMsg[0] = 0;
|
||||
getLastErrorMsg(sizeof(zMsg), zMsg);
|
||||
assert( errcode!=SQLITE_OK );
|
||||
if( zPath==0 ) zPath = "";
|
||||
for(i=0; zMsg[i] && zMsg[i]!='\r' && zMsg[i]!='\n'; i++){}
|
||||
zMsg[i] = 0;
|
||||
sqlite3_log(errcode,
|
||||
"os_win.c:%d: (%d) %s(%s) - %s",
|
||||
iLine, iErrno, zFunc, zPath, zMsg
|
||||
);
|
||||
|
||||
return errcode;
|
||||
}
|
||||
|
||||
#if SQLITE_OS_WINCE
|
||||
/*************************************************************************
|
||||
** This section contains code for WinCE only.
|
||||
@ -375,6 +479,7 @@ static BOOL winceCreateLock(const char *zFilename, winFile *pFile){
|
||||
pFile->hMutex = CreateMutexW(NULL, FALSE, zName);
|
||||
if (!pFile->hMutex){
|
||||
pFile->lastErrno = GetLastError();
|
||||
winLogError(SQLITE_ERROR, "winceCreateLock1", zFilename);
|
||||
free(zName);
|
||||
return FALSE;
|
||||
}
|
||||
@ -406,6 +511,7 @@ static BOOL winceCreateLock(const char *zFilename, winFile *pFile){
|
||||
/* If mapping failed, close the shared memory handle and erase it */
|
||||
if (!pFile->shared){
|
||||
pFile->lastErrno = GetLastError();
|
||||
winLogError(SQLITE_ERROR, "winceCreateLock2", zFilename);
|
||||
CloseHandle(pFile->hShared);
|
||||
pFile->hShared = NULL;
|
||||
}
|
||||
@ -651,6 +757,7 @@ static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){
|
||||
dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
|
||||
if( (dwRet==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR) ){
|
||||
pFile->lastErrno = GetLastError();
|
||||
winLogError(SQLITE_IOERR_SEEK, "seekWinFile", pFile->zPath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -696,7 +803,8 @@ static int winClose(sqlite3_file *id){
|
||||
#endif
|
||||
OSTRACE(("CLOSE %d %s\n", pFile->h, rc ? "ok" : "failed"));
|
||||
OpenCounter(-1);
|
||||
return rc ? SQLITE_OK : SQLITE_IOERR;
|
||||
return rc ? SQLITE_OK
|
||||
: winLogError(SQLITE_IOERR_CLOSE, "winClose", pFile->zPath);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -722,7 +830,7 @@ static int winRead(
|
||||
}
|
||||
if( !ReadFile(pFile->h, pBuf, amt, &nRead, 0) ){
|
||||
pFile->lastErrno = GetLastError();
|
||||
return SQLITE_IOERR_READ;
|
||||
return winLogError(SQLITE_IOERR_READ, "winRead", pFile->zPath);
|
||||
}
|
||||
if( nRead<(DWORD)amt ){
|
||||
/* Unread parts of the buffer must be zero-filled */
|
||||
@ -770,10 +878,11 @@ static int winWrite(
|
||||
}
|
||||
|
||||
if( rc ){
|
||||
if( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ){
|
||||
if( ( pFile->lastErrno==ERROR_HANDLE_DISK_FULL )
|
||||
|| ( pFile->lastErrno==ERROR_DISK_FULL )){
|
||||
return SQLITE_FULL;
|
||||
}
|
||||
return SQLITE_IOERR_WRITE;
|
||||
return winLogError(SQLITE_IOERR_WRITE, "winWrite", pFile->zPath);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -801,10 +910,10 @@ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
|
||||
|
||||
/* SetEndOfFile() returns non-zero when successful, or zero when it fails. */
|
||||
if( seekWinFile(pFile, nByte) ){
|
||||
rc = SQLITE_IOERR_TRUNCATE;
|
||||
rc = winLogError(SQLITE_IOERR_TRUNCATE, "winTruncate1", pFile->zPath);
|
||||
}else if( 0==SetEndOfFile(pFile->h) ){
|
||||
pFile->lastErrno = GetLastError();
|
||||
rc = SQLITE_IOERR_TRUNCATE;
|
||||
rc = winLogError(SQLITE_IOERR_TRUNCATE, "winTruncate2", pFile->zPath);
|
||||
}
|
||||
|
||||
OSTRACE(("TRUNCATE %d %lld %s\n", pFile->h, nByte, rc ? "failed" : "ok"));
|
||||
@ -826,6 +935,7 @@ int sqlite3_fullsync_count = 0;
|
||||
static int winSync(sqlite3_file *id, int flags){
|
||||
#if !defined(NDEBUG) || !defined(SQLITE_NO_SYNC) || defined(SQLITE_DEBUG)
|
||||
winFile *pFile = (winFile*)id;
|
||||
BOOL rc;
|
||||
#else
|
||||
UNUSED_PARAMETER(id);
|
||||
#endif
|
||||
@ -838,20 +948,19 @@ static int winSync(sqlite3_file *id, int flags){
|
||||
|
||||
OSTRACE(("SYNC %d lock=%d\n", pFile->h, pFile->locktype));
|
||||
|
||||
#ifndef SQLITE_TEST
|
||||
UNUSED_PARAMETER(flags);
|
||||
#else
|
||||
if( flags & SQLITE_SYNC_FULL ){
|
||||
sqlite3_fullsync_count++;
|
||||
}
|
||||
sqlite3_sync_count++;
|
||||
#endif
|
||||
|
||||
/* Unix cannot, but some systems may return SQLITE_FULL from here. This
|
||||
** line is to test that doing so does not cause any problems.
|
||||
*/
|
||||
SimulateDiskfullError( return SQLITE_FULL );
|
||||
SimulateIOError( return SQLITE_IOERR; );
|
||||
|
||||
#ifndef SQLITE_TEST
|
||||
UNUSED_PARAMETER(flags);
|
||||
#else
|
||||
if( (flags&0x0F)==SQLITE_SYNC_FULL ){
|
||||
sqlite3_fullsync_count++;
|
||||
}
|
||||
sqlite3_sync_count++;
|
||||
#endif
|
||||
|
||||
/* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a
|
||||
** no-op
|
||||
@ -859,11 +968,13 @@ static int winSync(sqlite3_file *id, int flags){
|
||||
#ifdef SQLITE_NO_SYNC
|
||||
return SQLITE_OK;
|
||||
#else
|
||||
if( FlushFileBuffers(pFile->h) ){
|
||||
rc = FlushFileBuffers(pFile->h);
|
||||
SimulateIOError( rc=FALSE );
|
||||
if( rc ){
|
||||
return SQLITE_OK;
|
||||
}else{
|
||||
pFile->lastErrno = GetLastError();
|
||||
return SQLITE_IOERR;
|
||||
return winLogError(SQLITE_IOERR_FSYNC, "winSync", pFile->zPath);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -884,7 +995,7 @@ static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){
|
||||
&& ((error = GetLastError()) != NO_ERROR) )
|
||||
{
|
||||
pFile->lastErrno = error;
|
||||
return SQLITE_IOERR_FSTAT;
|
||||
return winLogError(SQLITE_IOERR_FSTAT, "winFileSize", pFile->zPath);
|
||||
}
|
||||
*pSize = (((sqlite3_int64)upperBits)<<32) + lowerBits;
|
||||
return SQLITE_OK;
|
||||
@ -923,6 +1034,7 @@ static int getReadLock(winFile *pFile){
|
||||
}
|
||||
if( res == 0 ){
|
||||
pFile->lastErrno = GetLastError();
|
||||
/* No need to log a failure to lock */
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@ -941,8 +1053,9 @@ static int unlockReadLock(winFile *pFile){
|
||||
res = UnlockFile(pFile->h, SHARED_FIRST + pFile->sharedLockByte, 0, 1, 0);
|
||||
#endif
|
||||
}
|
||||
if( res == 0 ){
|
||||
if( res==0 && GetLastError()!=ERROR_NOT_LOCKED ){
|
||||
pFile->lastErrno = GetLastError();
|
||||
winLogError(SQLITE_IOERR_UNLOCK, "unlockReadLock", pFile->zPath);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@ -1143,7 +1256,7 @@ static int winUnlock(sqlite3_file *id, int locktype){
|
||||
if( locktype==SHARED_LOCK && !getReadLock(pFile) ){
|
||||
/* This should never happen. We should always be able to
|
||||
** reacquire the read lock */
|
||||
rc = SQLITE_IOERR_UNLOCK;
|
||||
rc = winLogError(SQLITE_IOERR_UNLOCK, "winUnlock", pFile->zPath);
|
||||
}
|
||||
}
|
||||
if( type>=RESERVED_LOCK ){
|
||||
@ -1458,6 +1571,7 @@ static int winOpenSharedMemory(winFile *pDbFd){
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
pNew->zFilename = (char*)&pNew[1];
|
||||
sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath);
|
||||
sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename);
|
||||
|
||||
/* Look to see if there is an existing winShmNode that can be used.
|
||||
** If no matching winShmNode currently exists, create a new one.
|
||||
@ -1500,7 +1614,7 @@ static int winOpenSharedMemory(winFile *pDbFd){
|
||||
if( winShmSystemLock(pShmNode, _SHM_WRLCK, WIN_SHM_DMS, 1)==SQLITE_OK ){
|
||||
rc = winTruncate((sqlite3_file *)&pShmNode->hFile, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
rc = SQLITE_IOERR_SHMOPEN;
|
||||
rc = winLogError(SQLITE_IOERR_SHMOPEN, "winOpenShm", pDbFd->zPath);
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
@ -1759,7 +1873,7 @@ static int winShmMap(
|
||||
*/
|
||||
rc = winFileSize((sqlite3_file *)&pShmNode->hFile, &sz);
|
||||
if( rc!=SQLITE_OK ){
|
||||
rc = SQLITE_IOERR_SHMSIZE;
|
||||
rc = winLogError(SQLITE_IOERR_SHMSIZE, "winShmMap1", pDbFd->zPath);
|
||||
goto shmpage_out;
|
||||
}
|
||||
|
||||
@ -1773,7 +1887,7 @@ static int winShmMap(
|
||||
if( !isWrite ) goto shmpage_out;
|
||||
rc = winTruncate((sqlite3_file *)&pShmNode->hFile, nByte);
|
||||
if( rc!=SQLITE_OK ){
|
||||
rc = SQLITE_IOERR_SHMSIZE;
|
||||
rc = winLogError(SQLITE_IOERR_SHMSIZE, "winShmMap2", pDbFd->zPath);
|
||||
goto shmpage_out;
|
||||
}
|
||||
}
|
||||
@ -1810,7 +1924,7 @@ static int winShmMap(
|
||||
}
|
||||
if( !pMap ){
|
||||
pShmNode->lastErrno = GetLastError();
|
||||
rc = SQLITE_IOERR;
|
||||
rc = winLogError(SQLITE_IOERR_SHMMAP, "winShmMap3", pDbFd->zPath);
|
||||
if( hMap ) CloseHandle(hMap);
|
||||
goto shmpage_out;
|
||||
}
|
||||
@ -1892,7 +2006,7 @@ static void *convertUtf8Filename(const char *zFilename){
|
||||
*/
|
||||
#if SQLITE_OS_WINCE==0
|
||||
}else{
|
||||
zConverted = utf8ToMbcs(zFilename);
|
||||
zConverted = sqlite3_win32_utf8_to_mbcs(zFilename);
|
||||
#endif
|
||||
}
|
||||
/* caller will handle out of memory */
|
||||
@ -1972,68 +2086,6 @@ static int getTempname(int nBuf, char *zBuf){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** The return value of getLastErrorMsg
|
||||
** is zero if the error message fits in the buffer, or non-zero
|
||||
** otherwise (if the message was truncated).
|
||||
*/
|
||||
static int getLastErrorMsg(int nBuf, char *zBuf){
|
||||
/* FormatMessage returns 0 on failure. Otherwise it
|
||||
** returns the number of TCHARs written to the output
|
||||
** buffer, excluding the terminating null char.
|
||||
*/
|
||||
DWORD error = GetLastError();
|
||||
DWORD dwLen = 0;
|
||||
char *zOut = 0;
|
||||
|
||||
if( isNT() ){
|
||||
WCHAR *zTempWide = NULL;
|
||||
dwLen = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
error,
|
||||
0,
|
||||
(LPWSTR) &zTempWide,
|
||||
0,
|
||||
0);
|
||||
if( dwLen > 0 ){
|
||||
/* allocate a buffer and convert to UTF8 */
|
||||
zOut = unicodeToUtf8(zTempWide);
|
||||
/* free the system buffer allocated by FormatMessage */
|
||||
LocalFree(zTempWide);
|
||||
}
|
||||
/* 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{
|
||||
char *zTemp = NULL;
|
||||
dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
error,
|
||||
0,
|
||||
(LPSTR) &zTemp,
|
||||
0,
|
||||
0);
|
||||
if( dwLen > 0 ){
|
||||
/* allocate a buffer and convert to UTF8 */
|
||||
zOut = sqlite3_win32_mbcs_to_utf8(zTemp);
|
||||
/* free the system buffer allocated by FormatMessage */
|
||||
LocalFree(zTemp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if( 0 == dwLen ){
|
||||
sqlite3_snprintf(nBuf, zBuf, "OsError 0x%x (%u)", error, error);
|
||||
}else{
|
||||
/* copy a maximum of nBuf chars to output buffer */
|
||||
sqlite3_snprintf(nBuf, zBuf, "%s", zOut);
|
||||
/* free the UTF8 buffer */
|
||||
free(zOut);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a file.
|
||||
*/
|
||||
@ -2205,6 +2257,7 @@ static int winOpen(
|
||||
|
||||
if( h==INVALID_HANDLE_VALUE ){
|
||||
pFile->lastErrno = GetLastError();
|
||||
winLogError(SQLITE_CANTOPEN, "winOpen", zUtf8Name);
|
||||
free(zConverted);
|
||||
if( isReadWrite ){
|
||||
return winOpen(pVfs, zName, id,
|
||||
@ -2308,7 +2361,8 @@ static int winDelete(
|
||||
"ok" : "failed" ));
|
||||
|
||||
return ( (rc == INVALID_FILE_ATTRIBUTES)
|
||||
&& (error == ERROR_FILE_NOT_FOUND)) ? SQLITE_OK : SQLITE_IOERR_DELETE;
|
||||
&& (error == ERROR_FILE_NOT_FOUND)) ? SQLITE_OK :
|
||||
winLogError(SQLITE_IOERR_DELETE, "winDelete", zFilename);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2348,6 +2402,7 @@ static int winAccess(
|
||||
}
|
||||
}else{
|
||||
if( GetLastError()!=ERROR_FILE_NOT_FOUND ){
|
||||
winLogError(SQLITE_IOERR_ACCESS, "winAccess", zFilename);
|
||||
free(zConverted);
|
||||
return SQLITE_IOERR_ACCESS;
|
||||
}else{
|
||||
@ -2412,6 +2467,13 @@ static int winFullPathname(
|
||||
void *zConverted;
|
||||
char *zOut;
|
||||
|
||||
/* If this path name begins with "/X:", where "X" is any alphabetic
|
||||
** character, discard the initial "/" from the pathname.
|
||||
*/
|
||||
if( zRelative[0]=='/' && sqlite3Isalpha(zRelative[1]) && zRelative[2]==':' ){
|
||||
zRelative++;
|
||||
}
|
||||
|
||||
/* It's odd to simulate an io-error here, but really this is just
|
||||
** using the io-error infrastructure to test that SQLite handles this
|
||||
** function failing. This function could fail if, for example, the
|
||||
|
||||
32
src/pager.c
32
src/pager.c
@ -4299,6 +4299,8 @@ int sqlite3PagerOpen(
|
||||
int noReadlock = (flags & PAGER_NO_READLOCK)!=0; /* True to omit read-lock */
|
||||
int pcacheSize = sqlite3PcacheSize(); /* Bytes to allocate for PCache */
|
||||
u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */
|
||||
const char *zUri = 0; /* URI args to copy */
|
||||
int nUri = 0; /* Number of bytes of URI args at *zUri */
|
||||
|
||||
/* Figure out how much space is required for each journal file-handle
|
||||
** (there are two of them, the main journal and the sub-journal). This
|
||||
@ -4329,6 +4331,7 @@ int sqlite3PagerOpen(
|
||||
** leave both nPathname and zPathname set to 0.
|
||||
*/
|
||||
if( zFilename && zFilename[0] ){
|
||||
const char *z;
|
||||
nPathname = pVfs->mxPathname+1;
|
||||
zPathname = sqlite3Malloc(nPathname*2);
|
||||
if( zPathname==0 ){
|
||||
@ -4337,6 +4340,12 @@ int sqlite3PagerOpen(
|
||||
zPathname[0] = 0; /* Make sure initialized even if FullPathname() fails */
|
||||
rc = sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname);
|
||||
nPathname = sqlite3Strlen30(zPathname);
|
||||
z = zUri = &zFilename[sqlite3Strlen30(zFilename)+1];
|
||||
while( *z ){
|
||||
z += sqlite3Strlen30(z)+1;
|
||||
z += sqlite3Strlen30(z)+1;
|
||||
}
|
||||
nUri = &z[1] - zUri;
|
||||
if( rc==SQLITE_OK && nPathname+8>pVfs->mxPathname ){
|
||||
/* This branch is taken when the journal path required by
|
||||
** the database being opened will be more than pVfs->mxPathname
|
||||
@ -4369,7 +4378,7 @@ int sqlite3PagerOpen(
|
||||
ROUND8(pcacheSize) + /* PCache object */
|
||||
ROUND8(pVfs->szOsFile) + /* The main db file */
|
||||
journalFileSize * 2 + /* The two journal files */
|
||||
nPathname + 1 + /* zFilename */
|
||||
nPathname + 1 + nUri + /* zFilename */
|
||||
nPathname + 8 + 1 /* zJournal */
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
+ nPathname + 4 + 1 /* zWal */
|
||||
@ -4391,14 +4400,17 @@ int sqlite3PagerOpen(
|
||||
/* Fill in the Pager.zFilename and Pager.zJournal buffers, if required. */
|
||||
if( zPathname ){
|
||||
assert( nPathname>0 );
|
||||
pPager->zJournal = (char*)(pPtr += nPathname + 1);
|
||||
pPager->zJournal = (char*)(pPtr += nPathname + 1 + nUri);
|
||||
memcpy(pPager->zFilename, zPathname, nPathname);
|
||||
memcpy(&pPager->zFilename[nPathname+1], zUri, nUri);
|
||||
memcpy(pPager->zJournal, zPathname, nPathname);
|
||||
memcpy(&pPager->zJournal[nPathname], "-journal", 8);
|
||||
sqlite3FileSuffix3(pPager->zFilename, pPager->zJournal);
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
pPager->zWal = &pPager->zJournal[nPathname+8+1];
|
||||
memcpy(pPager->zWal, zPathname, nPathname);
|
||||
memcpy(&pPager->zWal[nPathname], "-wal", 4);
|
||||
sqlite3FileSuffix3(pPager->zFilename, pPager->zWal);
|
||||
#endif
|
||||
sqlite3_free(zPathname);
|
||||
}
|
||||
@ -5735,11 +5747,21 @@ int sqlite3PagerCommitPhaseOne(
|
||||
}else{
|
||||
if( pagerUseWal(pPager) ){
|
||||
PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache);
|
||||
if( pList ){
|
||||
PgHdr *pPageOne = 0;
|
||||
if( pList==0 ){
|
||||
/* Must have at least one page for the WAL commit flag.
|
||||
** Ticket [2d1a5c67dfc2363e44f29d9bbd57f] 2011-05-18 */
|
||||
rc = sqlite3PagerGet(pPager, 1, &pPageOne);
|
||||
pList = pPageOne;
|
||||
pList->pDirty = 0;
|
||||
}
|
||||
assert( rc==SQLITE_OK );
|
||||
if( ALWAYS(pList) ){
|
||||
rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1,
|
||||
(pPager->fullSync ? pPager->syncFlags : 0)
|
||||
);
|
||||
}
|
||||
sqlite3PagerUnref(pPageOne);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3PcacheCleanAll(pPager->pPCache);
|
||||
}
|
||||
@ -6597,6 +6619,7 @@ int sqlite3PagerOkToChangeJournalMode(Pager *pPager){
|
||||
i64 sqlite3PagerJournalSizeLimit(Pager *pPager, i64 iLimit){
|
||||
if( iLimit>=-1 ){
|
||||
pPager->journalSizeLimit = iLimit;
|
||||
sqlite3WalLimit(pPager->pWal, iLimit);
|
||||
}
|
||||
return pPager->journalSizeLimit;
|
||||
}
|
||||
@ -6688,7 +6711,8 @@ static int pagerOpenWal(Pager *pPager){
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3WalOpen(pPager->pVfs,
|
||||
pPager->fd, pPager->zWal, pPager->exclusiveMode, &pPager->pWal
|
||||
pPager->fd, pPager->zWal, pPager->exclusiveMode,
|
||||
pPager->journalSizeLimit, &pPager->pWal
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -253,6 +253,13 @@ int sqlite3PcacheFetch(
|
||||
}
|
||||
if( pPg ){
|
||||
int rc;
|
||||
#ifdef SQLITE_LOG_CACHE_SPILL
|
||||
sqlite3_log(SQLITE_FULL,
|
||||
"spill page %d making room for %d - cache used: %d/%d",
|
||||
pPg->pgno, pgno,
|
||||
sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache),
|
||||
pCache->nMax);
|
||||
#endif
|
||||
rc = pCache->xStress(pCache->pStress, pPg);
|
||||
if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){
|
||||
return rc;
|
||||
|
||||
@ -574,7 +574,7 @@ static sqlite3_pcache *pcache1Create(int szPage, int bPurgeable){
|
||||
pGroup = (PGroup*)&pCache[1];
|
||||
pGroup->mxPinned = 10;
|
||||
}else{
|
||||
pGroup = &pcache1_g.grp;
|
||||
pGroup = &pcache1.grp;
|
||||
}
|
||||
pCache->pGroup = pGroup;
|
||||
pCache->szPage = szPage;
|
||||
|
||||
20
src/pragma.c
20
src/pragma.c
@ -13,10 +13,6 @@
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/* Ignore this whole file if pragmas are disabled
|
||||
*/
|
||||
#if !defined(SQLITE_OMIT_PRAGMA)
|
||||
|
||||
/*
|
||||
** Interpret the given string as a safety level. Return 0 for OFF,
|
||||
** 1 for ON or NORMAL and 2 for FULL. Return 1 for an empty or
|
||||
@ -49,10 +45,16 @@ static u8 getSafetyLevel(const char *z){
|
||||
/*
|
||||
** Interpret the given string as a boolean value.
|
||||
*/
|
||||
static u8 getBoolean(const char *z){
|
||||
u8 sqlite3GetBoolean(const char *z){
|
||||
return getSafetyLevel(z)&1;
|
||||
}
|
||||
|
||||
/* The sqlite3GetBoolean() function is used by other modules but the
|
||||
** remainder of this file is specific to PRAGMA processing. So omit
|
||||
** the rest of the file if PRAGMAs are omitted from the build.
|
||||
*/
|
||||
#if !defined(SQLITE_OMIT_PRAGMA)
|
||||
|
||||
/*
|
||||
** Interpret the given string as a locking mode value.
|
||||
*/
|
||||
@ -219,7 +221,7 @@ static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){
|
||||
mask &= ~(SQLITE_ForeignKeys);
|
||||
}
|
||||
|
||||
if( getBoolean(zRight) ){
|
||||
if( sqlite3GetBoolean(zRight) ){
|
||||
db->flags |= mask;
|
||||
}else{
|
||||
db->flags &= ~mask;
|
||||
@ -433,7 +435,7 @@ void sqlite3Pragma(
|
||||
int b = -1;
|
||||
assert( pBt!=0 );
|
||||
if( zRight ){
|
||||
b = getBoolean(zRight);
|
||||
b = sqlite3GetBoolean(zRight);
|
||||
}
|
||||
if( pId2->n==0 && b>=0 ){
|
||||
int ii;
|
||||
@ -1033,7 +1035,7 @@ void sqlite3Pragma(
|
||||
#ifndef NDEBUG
|
||||
if( sqlite3StrICmp(zLeft, "parser_trace")==0 ){
|
||||
if( zRight ){
|
||||
if( getBoolean(zRight) ){
|
||||
if( sqlite3GetBoolean(zRight) ){
|
||||
sqlite3ParserTrace(stderr, "parser: ");
|
||||
}else{
|
||||
sqlite3ParserTrace(0, 0);
|
||||
@ -1047,7 +1049,7 @@ void sqlite3Pragma(
|
||||
*/
|
||||
if( sqlite3StrICmp(zLeft, "case_sensitive_like")==0 ){
|
||||
if( zRight ){
|
||||
sqlite3RegisterLikeFunctions(db, getBoolean(zRight));
|
||||
sqlite3RegisterLikeFunctions(db, sqlite3GetBoolean(zRight));
|
||||
}
|
||||
}else
|
||||
|
||||
|
||||
@ -4239,11 +4239,13 @@ int sqlite3Select(
|
||||
** and pKeyInfo to the KeyInfo structure required to navigate the
|
||||
** index.
|
||||
**
|
||||
** (2011-04-15) Do not do a full scan of an unordered index.
|
||||
**
|
||||
** In practice the KeyInfo structure will not be used. It is only
|
||||
** passed to keep OP_OpenRead happy.
|
||||
*/
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
if( !pBest || pIdx->nColumn<pBest->nColumn ){
|
||||
if( pIdx->bUnordered==0 && (!pBest || pIdx->nColumn<pBest->nColumn) ){
|
||||
pBest = pIdx;
|
||||
}
|
||||
}
|
||||
|
||||
17
src/shell.c
17
src/shell.c
@ -2302,6 +2302,11 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
enableTimer = booleanValue(azArg[1]);
|
||||
}else
|
||||
|
||||
if( c=='v' && strncmp(azArg[0], "version", n)==0 ){
|
||||
printf("SQLite %s %s\n",
|
||||
sqlite3_libversion(), sqlite3_sourceid());
|
||||
}else
|
||||
|
||||
if( c=='w' && strncmp(azArg[0], "width", n)==0 && nArg>1 ){
|
||||
int j;
|
||||
assert( nArg<=ArraySize(azArg) );
|
||||
@ -2649,6 +2654,7 @@ static void main_init(struct callback_data *data) {
|
||||
data->mode = MODE_List;
|
||||
memcpy(data->separator,"|", 2);
|
||||
data->showHeader = 0;
|
||||
sqlite3_config(SQLITE_CONFIG_URI, 1);
|
||||
sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data);
|
||||
sqlite3_snprintf(sizeof(mainPrompt), mainPrompt,"sqlite> ");
|
||||
sqlite3_snprintf(sizeof(continuePrompt), continuePrompt," ...> ");
|
||||
@ -2663,6 +2669,11 @@ int main(int argc, char **argv){
|
||||
int i;
|
||||
int rc = 0;
|
||||
|
||||
if( strcmp(sqlite3_sourceid(),SQLITE_SOURCE_ID)!=0 ){
|
||||
fprintf(stderr, "SQLite header and source version mismatch\n%s\n%s\n",
|
||||
sqlite3_sourceid(), SQLITE_SOURCE_ID);
|
||||
exit(1);
|
||||
}
|
||||
Argv0 = argv[0];
|
||||
main_init(&data);
|
||||
stdin_is_interactive = isatty(0);
|
||||
@ -2830,7 +2841,7 @@ int main(int argc, char **argv){
|
||||
}else if( strcmp(z,"-bail")==0 ){
|
||||
bail_on_error = 1;
|
||||
}else if( strcmp(z,"-version")==0 ){
|
||||
printf("%s\n", sqlite3_libversion());
|
||||
printf("%s %s\n", sqlite3_libversion(), sqlite3_sourceid());
|
||||
return 0;
|
||||
}else if( strcmp(z,"-interactive")==0 ){
|
||||
stdin_is_interactive = 1;
|
||||
@ -2875,10 +2886,10 @@ int main(int argc, char **argv){
|
||||
char *zHistory = 0;
|
||||
int nHistory;
|
||||
printf(
|
||||
"SQLite version %s\n"
|
||||
"SQLite version %s %.19s\n"
|
||||
"Enter \".help\" for instructions\n"
|
||||
"Enter SQL statements terminated with a \";\"\n",
|
||||
sqlite3_libversion()
|
||||
sqlite3_libversion(), sqlite3_sourceid()
|
||||
);
|
||||
zHome = find_home_dir();
|
||||
if( zHome ){
|
||||
|
||||
433
src/sqlite.h.in
433
src/sqlite.h.in
@ -305,7 +305,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
|
||||
** argument. ^If the callback function of the 3rd argument to
|
||||
** sqlite3_exec() is not NULL, then it is invoked for each result row
|
||||
** coming out of the evaluated SQL statements. ^The 4th argument to
|
||||
** to sqlite3_exec() is relayed through to the 1st argument of each
|
||||
** sqlite3_exec() is relayed through to the 1st argument of each
|
||||
** callback invocation. ^If the callback pointer to sqlite3_exec()
|
||||
** is NULL, then no callback is ever invoked and result rows are
|
||||
** ignored.
|
||||
@ -370,7 +370,8 @@ int sqlite3_exec(
|
||||
**
|
||||
** New error codes may be added in future versions of SQLite.
|
||||
**
|
||||
** See also: [SQLITE_IOERR_READ | extended result codes]
|
||||
** See also: [SQLITE_IOERR_READ | extended result codes],
|
||||
** [sqlite3_vtab_on_conflict()] [SQLITE_ROLLBACK | result codes].
|
||||
*/
|
||||
#define SQLITE_OK 0 /* Successful result */
|
||||
/* beginning-of-error-codes */
|
||||
@ -447,17 +448,21 @@ int sqlite3_exec(
|
||||
#define SQLITE_IOERR_SHMOPEN (SQLITE_IOERR | (18<<8))
|
||||
#define SQLITE_IOERR_SHMSIZE (SQLITE_IOERR | (19<<8))
|
||||
#define SQLITE_IOERR_SHMLOCK (SQLITE_IOERR | (20<<8))
|
||||
#define SQLITE_IOERR_SHMMAP (SQLITE_IOERR | (21<<8))
|
||||
#define SQLITE_IOERR_SEEK (SQLITE_IOERR | (22<<8))
|
||||
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
|
||||
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
|
||||
#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))
|
||||
#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8))
|
||||
#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8))
|
||||
#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8))
|
||||
|
||||
/*
|
||||
** CAPI3REF: Flags For File Open Operations
|
||||
**
|
||||
** These bit values are intended for use in the
|
||||
** 3rd parameter to the [sqlite3_open_v2()] interface and
|
||||
** in the 4th parameter to the xOpen method of the
|
||||
** [sqlite3_vfs] object.
|
||||
** in the 4th parameter to the [sqlite3_vfs.xOpen] method.
|
||||
*/
|
||||
#define SQLITE_OPEN_READONLY 0x00000001 /* Ok for sqlite3_open_v2() */
|
||||
#define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for sqlite3_open_v2() */
|
||||
@ -465,6 +470,7 @@ int sqlite3_exec(
|
||||
#define SQLITE_OPEN_DELETEONCLOSE 0x00000008 /* VFS only */
|
||||
#define SQLITE_OPEN_EXCLUSIVE 0x00000010 /* VFS only */
|
||||
#define SQLITE_OPEN_AUTOPROXY 0x00000020 /* VFS only */
|
||||
#define SQLITE_OPEN_URI 0x00000040 /* Ok for sqlite3_open_v2() */
|
||||
#define SQLITE_OPEN_MAIN_DB 0x00000100 /* VFS only */
|
||||
#define SQLITE_OPEN_TEMP_DB 0x00000200 /* VFS only */
|
||||
#define SQLITE_OPEN_TRANSIENT_DB 0x00000400 /* VFS only */
|
||||
@ -575,17 +581,18 @@ struct sqlite3_file {
|
||||
/*
|
||||
** CAPI3REF: OS Interface File Virtual Methods Object
|
||||
**
|
||||
** Every file opened by the [sqlite3_vfs] xOpen method populates an
|
||||
** Every file opened by the [sqlite3_vfs.xOpen] method populates an
|
||||
** [sqlite3_file] object (or, more commonly, a subclass of the
|
||||
** [sqlite3_file] object) with a pointer to an instance of this object.
|
||||
** This object defines the methods used to perform various operations
|
||||
** against the open file represented by the [sqlite3_file] object.
|
||||
**
|
||||
** If the xOpen method sets the sqlite3_file.pMethods element
|
||||
** If the [sqlite3_vfs.xOpen] method sets the sqlite3_file.pMethods element
|
||||
** to a non-NULL pointer, then the sqlite3_io_methods.xClose method
|
||||
** may be invoked even if the xOpen reported that it failed. The
|
||||
** only way to prevent a call to xClose following a failed xOpen
|
||||
** is for the xOpen to set the sqlite3_file.pMethods element to NULL.
|
||||
** may be invoked even if the [sqlite3_vfs.xOpen] reported that it failed. The
|
||||
** only way to prevent a call to xClose following a failed [sqlite3_vfs.xOpen]
|
||||
** is for the [sqlite3_vfs.xOpen] to set the sqlite3_file.pMethods element
|
||||
** to NULL.
|
||||
**
|
||||
** The flags argument to xSync may be one of [SQLITE_SYNC_NORMAL] or
|
||||
** [SQLITE_SYNC_FULL]. The first choice is the normal fsync().
|
||||
@ -757,7 +764,8 @@ typedef struct sqlite3_mutex sqlite3_mutex;
|
||||
**
|
||||
** An instance of the sqlite3_vfs object defines the interface between
|
||||
** the SQLite core and the underlying operating system. The "vfs"
|
||||
** in the name of the object stands for "virtual file system".
|
||||
** in the name of the object stands for "virtual file system". See
|
||||
** the [VFS | VFS documentation] for further information.
|
||||
**
|
||||
** The value of the iVersion field is initially 1 but may be larger in
|
||||
** future versions of SQLite. Additional fields may be appended to this
|
||||
@ -786,6 +794,7 @@ typedef struct sqlite3_mutex sqlite3_mutex;
|
||||
** The zName field holds the name of the VFS module. The name must
|
||||
** be unique across all VFS modules.
|
||||
**
|
||||
** [[sqlite3_vfs.xOpen]]
|
||||
** ^SQLite guarantees that the zFilename parameter to xOpen
|
||||
** is either a NULL pointer or string obtained
|
||||
** from xFullPathname() with an optional suffix added.
|
||||
@ -863,6 +872,7 @@ typedef struct sqlite3_mutex sqlite3_mutex;
|
||||
** element will be valid after xOpen returns regardless of the success
|
||||
** or failure of the xOpen call.
|
||||
**
|
||||
** [[sqlite3_vfs.xAccess]]
|
||||
** ^The flags argument to xAccess() may be [SQLITE_ACCESS_EXISTS]
|
||||
** to test for the existence of a file, or [SQLITE_ACCESS_READWRITE] to
|
||||
** test whether a file is readable and writable, or [SQLITE_ACCESS_READ]
|
||||
@ -887,7 +897,7 @@ typedef struct sqlite3_mutex sqlite3_mutex;
|
||||
** method returns a Julian Day Number for the current date and time as
|
||||
** a floating point value.
|
||||
** ^The xCurrentTimeInt64() method returns, as an integer, the Julian
|
||||
** Day Number multipled by 86400000 (the number of milliseconds in
|
||||
** Day Number multiplied by 86400000 (the number of milliseconds in
|
||||
** a 24-hour day).
|
||||
** ^SQLite will use the xCurrentTimeInt64() method to get the current
|
||||
** date and time if that method is available (if iVersion is 2 or
|
||||
@ -1109,9 +1119,9 @@ int sqlite3_os_end(void);
|
||||
** implementation of an application-defined [sqlite3_os_init()].
|
||||
**
|
||||
** The first argument to sqlite3_config() is an integer
|
||||
** [SQLITE_CONFIG_SINGLETHREAD | configuration option] that determines
|
||||
** [configuration option] that determines
|
||||
** what property of SQLite is to be configured. Subsequent arguments
|
||||
** vary depending on the [SQLITE_CONFIG_SINGLETHREAD | configuration option]
|
||||
** vary depending on the [configuration option]
|
||||
** in the first argument.
|
||||
**
|
||||
** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK].
|
||||
@ -1221,6 +1231,7 @@ struct sqlite3_mem_methods {
|
||||
|
||||
/*
|
||||
** CAPI3REF: Configuration Options
|
||||
** KEYWORDS: {configuration option}
|
||||
**
|
||||
** These constants are the available integer configuration options that
|
||||
** can be passed as the first argument to the [sqlite3_config()] interface.
|
||||
@ -1233,7 +1244,7 @@ struct sqlite3_mem_methods {
|
||||
** is invoked.
|
||||
**
|
||||
** <dl>
|
||||
** <dt>SQLITE_CONFIG_SINGLETHREAD</dt>
|
||||
** [[SQLITE_CONFIG_SINGLETHREAD]] <dt>SQLITE_CONFIG_SINGLETHREAD</dt>
|
||||
** <dd>There are no arguments to this option. ^This option sets the
|
||||
** [threading mode] to Single-thread. In other words, it disables
|
||||
** all mutexing and puts SQLite into a mode where it can only be used
|
||||
@ -1244,7 +1255,7 @@ struct sqlite3_mem_methods {
|
||||
** [SQLITE_ERROR] if called with the SQLITE_CONFIG_SINGLETHREAD
|
||||
** configuration option.</dd>
|
||||
**
|
||||
** <dt>SQLITE_CONFIG_MULTITHREAD</dt>
|
||||
** [[SQLITE_CONFIG_MULTITHREAD]] <dt>SQLITE_CONFIG_MULTITHREAD</dt>
|
||||
** <dd>There are no arguments to this option. ^This option sets the
|
||||
** [threading mode] to Multi-thread. In other words, it disables
|
||||
** mutexing on [database connection] and [prepared statement] objects.
|
||||
@ -1258,7 +1269,7 @@ struct sqlite3_mem_methods {
|
||||
** [sqlite3_config()] will return [SQLITE_ERROR] if called with the
|
||||
** SQLITE_CONFIG_MULTITHREAD configuration option.</dd>
|
||||
**
|
||||
** <dt>SQLITE_CONFIG_SERIALIZED</dt>
|
||||
** [[SQLITE_CONFIG_SERIALIZED]] <dt>SQLITE_CONFIG_SERIALIZED</dt>
|
||||
** <dd>There are no arguments to this option. ^This option sets the
|
||||
** [threading mode] to Serialized. In other words, this option enables
|
||||
** all mutexes including the recursive
|
||||
@ -1274,7 +1285,7 @@ struct sqlite3_mem_methods {
|
||||
** [sqlite3_config()] will return [SQLITE_ERROR] if called with the
|
||||
** SQLITE_CONFIG_SERIALIZED configuration option.</dd>
|
||||
**
|
||||
** <dt>SQLITE_CONFIG_MALLOC</dt>
|
||||
** [[SQLITE_CONFIG_MALLOC]] <dt>SQLITE_CONFIG_MALLOC</dt>
|
||||
** <dd> ^(This option takes a single argument which is a pointer to an
|
||||
** instance of the [sqlite3_mem_methods] structure. The argument specifies
|
||||
** alternative low-level memory allocation routines to be used in place of
|
||||
@ -1282,7 +1293,7 @@ struct sqlite3_mem_methods {
|
||||
** its own private copy of the content of the [sqlite3_mem_methods] structure
|
||||
** before the [sqlite3_config()] call returns.</dd>
|
||||
**
|
||||
** <dt>SQLITE_CONFIG_GETMALLOC</dt>
|
||||
** [[SQLITE_CONFIG_GETMALLOC]] <dt>SQLITE_CONFIG_GETMALLOC</dt>
|
||||
** <dd> ^(This option takes a single argument which is a pointer to an
|
||||
** instance of the [sqlite3_mem_methods] structure. The [sqlite3_mem_methods]
|
||||
** structure is filled with the currently defined memory allocation routines.)^
|
||||
@ -1290,7 +1301,7 @@ struct sqlite3_mem_methods {
|
||||
** routines with a wrapper that simulations memory allocation failure or
|
||||
** tracks memory usage, for example. </dd>
|
||||
**
|
||||
** <dt>SQLITE_CONFIG_MEMSTATUS</dt>
|
||||
** [[SQLITE_CONFIG_MEMSTATUS]] <dt>SQLITE_CONFIG_MEMSTATUS</dt>
|
||||
** <dd> ^This option takes single argument of type int, interpreted as a
|
||||
** boolean, which enables or disables the collection of memory allocation
|
||||
** statistics. ^(When memory allocation statistics are disabled, the
|
||||
@ -1306,7 +1317,7 @@ struct sqlite3_mem_methods {
|
||||
** allocation statistics are disabled by default.
|
||||
** </dd>
|
||||
**
|
||||
** <dt>SQLITE_CONFIG_SCRATCH</dt>
|
||||
** [[SQLITE_CONFIG_SCRATCH]] <dt>SQLITE_CONFIG_SCRATCH</dt>
|
||||
** <dd> ^This option specifies a static memory buffer that SQLite can use for
|
||||
** scratch memory. There are three arguments: A pointer an 8-byte
|
||||
** aligned memory buffer from which the scratch allocations will be
|
||||
@ -1322,9 +1333,9 @@ struct sqlite3_mem_methods {
|
||||
** scratch memory beyond what is provided by this configuration option, then
|
||||
** [sqlite3_malloc()] will be used to obtain the memory needed.</dd>
|
||||
**
|
||||
** <dt>SQLITE_CONFIG_PAGECACHE</dt>
|
||||
** [[SQLITE_CONFIG_PAGECACHE]] <dt>SQLITE_CONFIG_PAGECACHE</dt>
|
||||
** <dd> ^This option specifies a static memory buffer that SQLite can use for
|
||||
** the database page cache with the default page cache implemenation.
|
||||
** the database page cache with the default page cache implementation.
|
||||
** This configuration should not be used if an application-define page
|
||||
** cache implementation is loaded using the SQLITE_CONFIG_PCACHE option.
|
||||
** There are three arguments to this option: A pointer to 8-byte aligned
|
||||
@ -1343,7 +1354,7 @@ struct sqlite3_mem_methods {
|
||||
** be aligned to an 8-byte boundary or subsequent behavior of SQLite
|
||||
** will be undefined.</dd>
|
||||
**
|
||||
** <dt>SQLITE_CONFIG_HEAP</dt>
|
||||
** [[SQLITE_CONFIG_HEAP]] <dt>SQLITE_CONFIG_HEAP</dt>
|
||||
** <dd> ^This option specifies a static memory buffer that SQLite will use
|
||||
** for all of its dynamic memory allocation needs beyond those provided
|
||||
** for by [SQLITE_CONFIG_SCRATCH] and [SQLITE_CONFIG_PAGECACHE].
|
||||
@ -1360,7 +1371,7 @@ struct sqlite3_mem_methods {
|
||||
** The minimum allocation size is capped at 2^12. Reasonable values
|
||||
** for the minimum allocation size are 2^5 through 2^8.</dd>
|
||||
**
|
||||
** <dt>SQLITE_CONFIG_MUTEX</dt>
|
||||
** [[SQLITE_CONFIG_MUTEX]] <dt>SQLITE_CONFIG_MUTEX</dt>
|
||||
** <dd> ^(This option takes a single argument which is a pointer to an
|
||||
** instance of the [sqlite3_mutex_methods] structure. The argument specifies
|
||||
** alternative low-level mutex routines to be used in place
|
||||
@ -1372,7 +1383,7 @@ struct sqlite3_mem_methods {
|
||||
** [sqlite3_config()] with the SQLITE_CONFIG_MUTEX configuration option will
|
||||
** return [SQLITE_ERROR].</dd>
|
||||
**
|
||||
** <dt>SQLITE_CONFIG_GETMUTEX</dt>
|
||||
** [[SQLITE_CONFIG_GETMUTEX]] <dt>SQLITE_CONFIG_GETMUTEX</dt>
|
||||
** <dd> ^(This option takes a single argument which is a pointer to an
|
||||
** instance of the [sqlite3_mutex_methods] structure. The
|
||||
** [sqlite3_mutex_methods]
|
||||
@ -1385,7 +1396,7 @@ struct sqlite3_mem_methods {
|
||||
** [sqlite3_config()] with the SQLITE_CONFIG_GETMUTEX configuration option will
|
||||
** return [SQLITE_ERROR].</dd>
|
||||
**
|
||||
** <dt>SQLITE_CONFIG_LOOKASIDE</dt>
|
||||
** [[SQLITE_CONFIG_LOOKASIDE]] <dt>SQLITE_CONFIG_LOOKASIDE</dt>
|
||||
** <dd> ^(This option takes two arguments that determine the default
|
||||
** memory allocation for the lookaside memory allocator on each
|
||||
** [database connection]. The first argument is the
|
||||
@ -1395,18 +1406,18 @@ struct sqlite3_mem_methods {
|
||||
** verb to [sqlite3_db_config()] can be used to change the lookaside
|
||||
** configuration on individual connections.)^ </dd>
|
||||
**
|
||||
** <dt>SQLITE_CONFIG_PCACHE</dt>
|
||||
** [[SQLITE_CONFIG_PCACHE]] <dt>SQLITE_CONFIG_PCACHE</dt>
|
||||
** <dd> ^(This option takes a single argument which is a pointer to
|
||||
** an [sqlite3_pcache_methods] object. This object specifies the interface
|
||||
** to a custom page cache implementation.)^ ^SQLite makes a copy of the
|
||||
** object and uses it for page cache memory allocations.</dd>
|
||||
**
|
||||
** <dt>SQLITE_CONFIG_GETPCACHE</dt>
|
||||
** [[SQLITE_CONFIG_GETPCACHE]] <dt>SQLITE_CONFIG_GETPCACHE</dt>
|
||||
** <dd> ^(This option takes a single argument which is a pointer to an
|
||||
** [sqlite3_pcache_methods] object. SQLite copies of the current
|
||||
** page cache implementation into that object.)^ </dd>
|
||||
**
|
||||
** <dt>SQLITE_CONFIG_LOG</dt>
|
||||
** [[SQLITE_CONFIG_LOG]] <dt>SQLITE_CONFIG_LOG</dt>
|
||||
** <dd> ^The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a
|
||||
** function with a call signature of void(*)(void*,int,const char*),
|
||||
** and a pointer to void. ^If the function pointer is not NULL, it is
|
||||
@ -1424,6 +1435,18 @@ struct sqlite3_mem_methods {
|
||||
** In a multi-threaded application, the application-defined logger
|
||||
** function must be threadsafe. </dd>
|
||||
**
|
||||
** [[SQLITE_CONFIG_URI]] <dt>SQLITE_CONFIG_URI
|
||||
** <dd> This option takes a single argument of type int. If non-zero, then
|
||||
** URI handling is globally enabled. If the parameter is zero, then URI handling
|
||||
** is globally disabled. If URI handling is globally enabled, all filenames
|
||||
** passed to [sqlite3_open()], [sqlite3_open_v2()], [sqlite3_open16()] or
|
||||
** specified as part of [ATTACH] commands are interpreted as URIs, regardless
|
||||
** of whether or not the [SQLITE_OPEN_URI] flag is set when the database
|
||||
** connection is opened. If it is globally disabled, filenames are
|
||||
** only interpreted as URIs if the SQLITE_OPEN_URI flag is set when the
|
||||
** database connection is opened. By default, URI handling is globally
|
||||
** disabled. The default value may be changed by compiling with the
|
||||
** [SQLITE_USE_URI] symbol defined.
|
||||
** </dl>
|
||||
*/
|
||||
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
|
||||
@ -1442,6 +1465,7 @@ struct sqlite3_mem_methods {
|
||||
#define SQLITE_CONFIG_PCACHE 14 /* sqlite3_pcache_methods* */
|
||||
#define SQLITE_CONFIG_GETPCACHE 15 /* sqlite3_pcache_methods* */
|
||||
#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */
|
||||
#define SQLITE_CONFIG_URI 17 /* int */
|
||||
|
||||
/*
|
||||
** CAPI3REF: Database Connection Configuration Options
|
||||
@ -1527,13 +1551,17 @@ int sqlite3_extended_result_codes(sqlite3*, int onoff);
|
||||
**
|
||||
** ^This routine returns the [rowid] of the most recent
|
||||
** successful [INSERT] into the database from the [database connection]
|
||||
** in the first argument. ^If no successful [INSERT]s
|
||||
** in the first argument. ^As of SQLite version 3.7.7, this routines
|
||||
** records the last insert rowid of both ordinary tables and [virtual tables].
|
||||
** ^If no successful [INSERT]s
|
||||
** have ever occurred on that database connection, zero is returned.
|
||||
**
|
||||
** ^(If an [INSERT] occurs within a trigger, then the [rowid] of the inserted
|
||||
** row is returned by this routine as long as the trigger is running.
|
||||
** But once the trigger terminates, the value returned by this routine
|
||||
** reverts to the last value inserted before the trigger fired.)^
|
||||
** ^(If an [INSERT] occurs within a trigger or within a [virtual table]
|
||||
** method, then this routine will return the [rowid] of the inserted
|
||||
** row as long as the trigger or virtual table method is running.
|
||||
** But once the trigger or virtual table method ends, the value returned
|
||||
** by this routine reverts to what it was before the trigger or virtual
|
||||
** table method began.)^
|
||||
**
|
||||
** ^An [INSERT] that fails due to a constraint violation is not a
|
||||
** successful [INSERT] and does not change the value returned by this
|
||||
@ -2196,6 +2224,9 @@ int sqlite3_set_authorizer(
|
||||
** to signal SQLite whether or not the action is permitted. See the
|
||||
** [sqlite3_set_authorizer | authorizer documentation] for additional
|
||||
** information.
|
||||
**
|
||||
** Note that SQLITE_IGNORE is also used as a [SQLITE_ROLLBACK | return code]
|
||||
** from the [sqlite3_vtab_on_conflict()] interface.
|
||||
*/
|
||||
#define SQLITE_DENY 1 /* Abort the SQL statement with an error */
|
||||
#define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */
|
||||
@ -2318,7 +2349,7 @@ void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
|
||||
/*
|
||||
** CAPI3REF: Opening A New Database Connection
|
||||
**
|
||||
** ^These routines open an SQLite database file whose name is given by the
|
||||
** ^These routines open an SQLite database file as specified by the
|
||||
** filename argument. ^The filename argument is interpreted as UTF-8 for
|
||||
** sqlite3_open() and sqlite3_open_v2() and as UTF-16 in the native byte
|
||||
** order for sqlite3_open16(). ^(A [database connection] handle is usually
|
||||
@ -2345,7 +2376,7 @@ void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
|
||||
** sqlite3_open_v2() can take one of
|
||||
** the following three values, optionally combined with the
|
||||
** [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], [SQLITE_OPEN_SHAREDCACHE],
|
||||
** and/or [SQLITE_OPEN_PRIVATECACHE] flags:)^
|
||||
** [SQLITE_OPEN_PRIVATECACHE], and/or [SQLITE_OPEN_URI] flags:)^
|
||||
**
|
||||
** <dl>
|
||||
** ^(<dt>[SQLITE_OPEN_READONLY]</dt>
|
||||
@ -2364,9 +2395,8 @@ void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
|
||||
** </dl>
|
||||
**
|
||||
** If the 3rd parameter to sqlite3_open_v2() is not one of the
|
||||
** combinations shown above or one of the combinations shown above combined
|
||||
** with the [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX],
|
||||
** [SQLITE_OPEN_SHAREDCACHE] and/or [SQLITE_OPEN_PRIVATECACHE] flags,
|
||||
** combinations shown above optionally combined with other
|
||||
** [SQLITE_OPEN_READONLY | SQLITE_OPEN_* bits]
|
||||
** then the behavior is undefined.
|
||||
**
|
||||
** ^If the [SQLITE_OPEN_NOMUTEX] flag is set, then the database connection
|
||||
@ -2381,6 +2411,11 @@ void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
|
||||
** [SQLITE_OPEN_PRIVATECACHE] flag causes the database connection to not
|
||||
** participate in [shared cache mode] even if it is enabled.
|
||||
**
|
||||
** ^The fourth parameter to sqlite3_open_v2() is the name of the
|
||||
** [sqlite3_vfs] object that defines the operating system interface that
|
||||
** the new database connection should use. ^If the fourth parameter is
|
||||
** a NULL pointer then the default [sqlite3_vfs] object is used.
|
||||
**
|
||||
** ^If the filename is ":memory:", then a private, temporary in-memory database
|
||||
** is created for the connection. ^This in-memory database will vanish when
|
||||
** the database connection is closed. Future versions of SQLite might
|
||||
@ -2393,10 +2428,111 @@ void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
|
||||
** on-disk database will be created. ^This private database will be
|
||||
** automatically deleted as soon as the database connection is closed.
|
||||
**
|
||||
** ^The fourth parameter to sqlite3_open_v2() is the name of the
|
||||
** [sqlite3_vfs] object that defines the operating system interface that
|
||||
** the new database connection should use. ^If the fourth parameter is
|
||||
** a NULL pointer then the default [sqlite3_vfs] object is used.
|
||||
** [[URI filenames in sqlite3_open()]] <h3>URI Filenames</h3>
|
||||
**
|
||||
** ^If [URI filename] interpretation is enabled, and the filename argument
|
||||
** begins with "file:", then the filename is interpreted as a URI. ^URI
|
||||
** filename interpretation is enabled if the [SQLITE_OPEN_URI] flag is
|
||||
** set in the fourth argument to sqlite3_open_v2(), or if it has
|
||||
** been enabled globally using the [SQLITE_CONFIG_URI] option with the
|
||||
** [sqlite3_config()] method or by the [SQLITE_USE_URI] compile-time option.
|
||||
** As of SQLite version 3.7.7, URI filename interpretation is turned off
|
||||
** by default, but future releases of SQLite might enable URI filename
|
||||
** interpretation by default. See "[URI filenames]" for additional
|
||||
** information.
|
||||
**
|
||||
** URI filenames are parsed according to RFC 3986. ^If the URI contains an
|
||||
** authority, then it must be either an empty string or the string
|
||||
** "localhost". ^If the authority is not an empty string or "localhost", an
|
||||
** error is returned to the caller. ^The fragment component of a URI, if
|
||||
** present, is ignored.
|
||||
**
|
||||
** ^SQLite uses the path component of the URI as the name of the disk file
|
||||
** which contains the database. ^If the path begins with a '/' character,
|
||||
** then it is interpreted as an absolute path. ^If the path does not begin
|
||||
** with a '/' (meaning that the authority section is omitted from the URI)
|
||||
** then the path is interpreted as a relative path.
|
||||
** ^On windows, the first component of an absolute path
|
||||
** is a drive specification (e.g. "C:").
|
||||
**
|
||||
** [[core URI query parameters]]
|
||||
** The query component of a URI may contain parameters that are interpreted
|
||||
** either by SQLite itself, or by a [VFS | custom VFS implementation].
|
||||
** SQLite interprets the following three query parameters:
|
||||
**
|
||||
** <ul>
|
||||
** <li> <b>vfs</b>: ^The "vfs" parameter may be used to specify the name of
|
||||
** a VFS object that provides the operating system interface that should
|
||||
** be used to access the database file on disk. ^If this option is set to
|
||||
** an empty string the default VFS object is used. ^Specifying an unknown
|
||||
** VFS is an error. ^If sqlite3_open_v2() is used and the vfs option is
|
||||
** present, then the VFS specified by the option takes precedence over
|
||||
** the value passed as the fourth parameter to sqlite3_open_v2().
|
||||
**
|
||||
** <li> <b>mode</b>: ^(The mode parameter may be set to either "ro", "rw" or
|
||||
** "rwc". Attempting to set it to any other value is an error)^.
|
||||
** ^If "ro" is specified, then the database is opened for read-only
|
||||
** access, just as if the [SQLITE_OPEN_READONLY] flag had been set in the
|
||||
** third argument to sqlite3_prepare_v2(). ^If the mode option is set to
|
||||
** "rw", then the database is opened for read-write (but not create)
|
||||
** access, as if SQLITE_OPEN_READWRITE (but not SQLITE_OPEN_CREATE) had
|
||||
** been set. ^Value "rwc" is equivalent to setting both
|
||||
** SQLITE_OPEN_READWRITE and SQLITE_OPEN_CREATE. ^If sqlite3_open_v2() is
|
||||
** used, it is an error to specify a value for the mode parameter that is
|
||||
** less restrictive than that specified by the flags passed as the third
|
||||
** parameter.
|
||||
**
|
||||
** <li> <b>cache</b>: ^The cache parameter may be set to either "shared" or
|
||||
** "private". ^Setting it to "shared" is equivalent to setting the
|
||||
** SQLITE_OPEN_SHAREDCACHE bit in the flags argument passed to
|
||||
** sqlite3_open_v2(). ^Setting the cache parameter to "private" is
|
||||
** equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit.
|
||||
** ^If sqlite3_open_v2() is used and the "cache" parameter is present in
|
||||
** a URI filename, its value overrides any behaviour requested by setting
|
||||
** SQLITE_OPEN_PRIVATECACHE or SQLITE_OPEN_SHAREDCACHE flag.
|
||||
** </ul>
|
||||
**
|
||||
** ^Specifying an unknown parameter in the query component of a URI is not an
|
||||
** error. Future versions of SQLite might understand additional query
|
||||
** parameters. See "[query parameters with special meaning to SQLite]" for
|
||||
** additional information.
|
||||
**
|
||||
** [[URI filename examples]] <h3>URI filename examples</h3>
|
||||
**
|
||||
** <table border="1" align=center cellpadding=5>
|
||||
** <tr><th> URI filenames <th> Results
|
||||
** <tr><td> file:data.db <td>
|
||||
** Open the file "data.db" in the current directory.
|
||||
** <tr><td> file:/home/fred/data.db<br>
|
||||
** file:///home/fred/data.db <br>
|
||||
** file://localhost/home/fred/data.db <br> <td>
|
||||
** Open the database file "/home/fred/data.db".
|
||||
** <tr><td> file://darkstar/home/fred/data.db <td>
|
||||
** An error. "darkstar" is not a recognized authority.
|
||||
** <tr><td style="white-space:nowrap">
|
||||
** file:///C:/Documents%20and%20Settings/fred/Desktop/data.db
|
||||
** <td> Windows only: Open the file "data.db" on fred's desktop on drive
|
||||
** C:. Note that the %20 escaping in this example is not strictly
|
||||
** necessary - space characters can be used literally
|
||||
** in URI filenames.
|
||||
** <tr><td> file:data.db?mode=ro&cache=private <td>
|
||||
** Open file "data.db" in the current directory for read-only access.
|
||||
** Regardless of whether or not shared-cache mode is enabled by
|
||||
** default, use a private cache.
|
||||
** <tr><td> file:/home/fred/data.db?vfs=unix-nolock <td>
|
||||
** Open file "/home/fred/data.db". Use the special VFS "unix-nolock".
|
||||
** <tr><td> file:data.db?mode=readonly <td>
|
||||
** An error. "readonly" is not a valid option for the "mode" parameter.
|
||||
** </table>
|
||||
**
|
||||
** ^URI hexadecimal escape sequences (%HH) are supported within the path and
|
||||
** query components of a URI. A hexadecimal escape sequence consists of a
|
||||
** percent sign - "%" - followed by exactly two hexadecimal digits
|
||||
** specifying an octet value. ^Before the path or query components of a
|
||||
** URI filename are interpreted, they are encoded using UTF-8 and all
|
||||
** hexadecimal escape sequences replaced by a single byte containing the
|
||||
** corresponding octet. If this process generates an invalid UTF-8 encoding,
|
||||
** the results are undefined.
|
||||
**
|
||||
** <b>Note to Windows users:</b> The encoding used for the filename argument
|
||||
** of sqlite3_open() and sqlite3_open_v2() must be UTF-8, not whatever
|
||||
@ -2419,6 +2555,26 @@ int sqlite3_open_v2(
|
||||
const char *zVfs /* Name of VFS module to use */
|
||||
);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Obtain Values For URI Parameters
|
||||
**
|
||||
** This is a utility routine, useful to VFS implementations, that checks
|
||||
** to see if a database file was a URI that contained a specific query
|
||||
** parameter, and if so obtains the value of the query parameter.
|
||||
**
|
||||
** The zFilename argument is the filename pointer passed into the xOpen()
|
||||
** method of a VFS implementation. The zParam argument is the name of the
|
||||
** query parameter we seek. This routine returns the value of the zParam
|
||||
** parameter if it exists. If the parameter does not exist, this routine
|
||||
** returns a NULL pointer.
|
||||
**
|
||||
** If the zFilename argument to this function is not a pointer that SQLite
|
||||
** passed into the xOpen VFS method, then the behavior of this routine
|
||||
** is undefined and probably undesirable.
|
||||
*/
|
||||
const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam);
|
||||
|
||||
|
||||
/*
|
||||
** CAPI3REF: Error Codes And Messages
|
||||
**
|
||||
@ -2534,43 +2690,45 @@ int sqlite3_limit(sqlite3*, int id, int newVal);
|
||||
** Additional information is available at [limits | Limits in SQLite].
|
||||
**
|
||||
** <dl>
|
||||
** ^(<dt>SQLITE_LIMIT_LENGTH</dt>
|
||||
** [[SQLITE_LIMIT_LENGTH]] ^(<dt>SQLITE_LIMIT_LENGTH</dt>
|
||||
** <dd>The maximum size of any string or BLOB or table row, in bytes.<dd>)^
|
||||
**
|
||||
** ^(<dt>SQLITE_LIMIT_SQL_LENGTH</dt>
|
||||
** [[SQLITE_LIMIT_SQL_LENGTH]] ^(<dt>SQLITE_LIMIT_SQL_LENGTH</dt>
|
||||
** <dd>The maximum length of an SQL statement, in bytes.</dd>)^
|
||||
**
|
||||
** ^(<dt>SQLITE_LIMIT_COLUMN</dt>
|
||||
** [[SQLITE_LIMIT_COLUMN]] ^(<dt>SQLITE_LIMIT_COLUMN</dt>
|
||||
** <dd>The maximum number of columns in a table definition or in the
|
||||
** result set of a [SELECT] or the maximum number of columns in an index
|
||||
** or in an ORDER BY or GROUP BY clause.</dd>)^
|
||||
**
|
||||
** ^(<dt>SQLITE_LIMIT_EXPR_DEPTH</dt>
|
||||
** [[SQLITE_LIMIT_EXPR_DEPTH]] ^(<dt>SQLITE_LIMIT_EXPR_DEPTH</dt>
|
||||
** <dd>The maximum depth of the parse tree on any expression.</dd>)^
|
||||
**
|
||||
** ^(<dt>SQLITE_LIMIT_COMPOUND_SELECT</dt>
|
||||
** [[SQLITE_LIMIT_COMPOUND_SELECT]] ^(<dt>SQLITE_LIMIT_COMPOUND_SELECT</dt>
|
||||
** <dd>The maximum number of terms in a compound SELECT statement.</dd>)^
|
||||
**
|
||||
** ^(<dt>SQLITE_LIMIT_VDBE_OP</dt>
|
||||
** [[SQLITE_LIMIT_VDBE_OP]] ^(<dt>SQLITE_LIMIT_VDBE_OP</dt>
|
||||
** <dd>The maximum number of instructions in a virtual machine program
|
||||
** used to implement an SQL statement. This limit is not currently
|
||||
** enforced, though that might be added in some future release of
|
||||
** SQLite.</dd>)^
|
||||
**
|
||||
** ^(<dt>SQLITE_LIMIT_FUNCTION_ARG</dt>
|
||||
** [[SQLITE_LIMIT_FUNCTION_ARG]] ^(<dt>SQLITE_LIMIT_FUNCTION_ARG</dt>
|
||||
** <dd>The maximum number of arguments on a function.</dd>)^
|
||||
**
|
||||
** ^(<dt>SQLITE_LIMIT_ATTACHED</dt>
|
||||
** [[SQLITE_LIMIT_ATTACHED]] ^(<dt>SQLITE_LIMIT_ATTACHED</dt>
|
||||
** <dd>The maximum number of [ATTACH | attached databases].)^</dd>
|
||||
**
|
||||
** [[SQLITE_LIMIT_LIKE_PATTERN_LENGTH]]
|
||||
** ^(<dt>SQLITE_LIMIT_LIKE_PATTERN_LENGTH</dt>
|
||||
** <dd>The maximum length of the pattern argument to the [LIKE] or
|
||||
** [GLOB] operators.</dd>)^
|
||||
**
|
||||
** [[SQLITE_LIMIT_VARIABLE_NUMBER]]
|
||||
** ^(<dt>SQLITE_LIMIT_VARIABLE_NUMBER</dt>
|
||||
** <dd>The maximum index number of any [parameter] in an SQL statement.)^
|
||||
**
|
||||
** ^(<dt>SQLITE_LIMIT_TRIGGER_DEPTH</dt>
|
||||
** [[SQLITE_LIMIT_TRIGGER_DEPTH]] ^(<dt>SQLITE_LIMIT_TRIGGER_DEPTH</dt>
|
||||
** <dd>The maximum depth of recursion for triggers.</dd>)^
|
||||
** </dl>
|
||||
*/
|
||||
@ -3099,7 +3257,7 @@ const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
|
||||
** ^[SQLITE_BUSY] means that the database engine was unable to acquire the
|
||||
** database locks it needs to do its job. ^If the statement is a [COMMIT]
|
||||
** or occurs outside of an explicit transaction, then you can retry the
|
||||
** statement. If the statement is not a [COMMIT] and occurs within a
|
||||
** statement. If the statement is not a [COMMIT] and occurs within an
|
||||
** explicit transaction then you should rollback the transaction before
|
||||
** continuing.
|
||||
**
|
||||
@ -3378,7 +3536,7 @@ sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);
|
||||
** CAPI3REF: Destroy A Prepared Statement Object
|
||||
**
|
||||
** ^The sqlite3_finalize() function is called to delete a [prepared statement].
|
||||
** ^If the most recent evaluation of the statement encountered no errors or
|
||||
** ^If the most recent evaluation of the statement encountered no errors
|
||||
** or if the statement is never been evaluated, then sqlite3_finalize() returns
|
||||
** SQLITE_OK. ^If the most recent evaluation of statement S failed, then
|
||||
** sqlite3_finalize(S) returns the appropriate [error code] or
|
||||
@ -4605,6 +4763,11 @@ struct sqlite3_module {
|
||||
void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void **ppArg);
|
||||
int (*xRename)(sqlite3_vtab *pVtab, const char *zNew);
|
||||
/* The methods above are in version 1 of the sqlite_module object. Those
|
||||
** below are for version 2 and greater. */
|
||||
int (*xSavepoint)(sqlite3_vtab *pVTab, int);
|
||||
int (*xRelease)(sqlite3_vtab *pVTab, int);
|
||||
int (*xRollbackTo)(sqlite3_vtab *pVTab, int);
|
||||
};
|
||||
|
||||
/*
|
||||
@ -5287,7 +5450,7 @@ struct sqlite3_mutex_methods {
|
||||
**
|
||||
** ^If the argument to sqlite3_mutex_held() is a NULL pointer then
|
||||
** the routine should return 1. This seems counter-intuitive since
|
||||
** clearly the mutex cannot be held if it does not exist. But the
|
||||
** clearly the mutex cannot be held if it does not exist. But
|
||||
** the reason the mutex does not exist is because the build is not
|
||||
** using mutexes. And we do not want the assert() containing the
|
||||
** call to sqlite3_mutex_held() to fail, so a non-zero return is
|
||||
@ -5410,7 +5573,8 @@ int sqlite3_test_control(int op, ...);
|
||||
#define SQLITE_TESTCTRL_ISKEYWORD 16
|
||||
#define SQLITE_TESTCTRL_PGHDRSZ 17
|
||||
#define SQLITE_TESTCTRL_SCRATCHMALLOC 18
|
||||
#define SQLITE_TESTCTRL_LAST 18
|
||||
#define SQLITE_TESTCTRL_LOCALTIME_FAULT 19
|
||||
#define SQLITE_TESTCTRL_LAST 19
|
||||
|
||||
/*
|
||||
** CAPI3REF: SQLite Runtime Status
|
||||
@ -5419,7 +5583,7 @@ int sqlite3_test_control(int op, ...);
|
||||
** about the performance of SQLite, and optionally to reset various
|
||||
** highwater marks. ^The first argument is an integer code for
|
||||
** the specific parameter to measure. ^(Recognized integer codes
|
||||
** are of the form [SQLITE_STATUS_MEMORY_USED | SQLITE_STATUS_...].)^
|
||||
** are of the form [status parameters | SQLITE_STATUS_...].)^
|
||||
** ^The current value of the parameter is returned into *pCurrent.
|
||||
** ^The highest recorded value is returned in *pHighwater. ^If the
|
||||
** resetFlag is true, then the highest record value is reset after
|
||||
@ -5446,12 +5610,13 @@ int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Status Parameters
|
||||
** KEYWORDS: {status parameters}
|
||||
**
|
||||
** These integer constants designate various run-time status parameters
|
||||
** that can be returned by [sqlite3_status()].
|
||||
**
|
||||
** <dl>
|
||||
** ^(<dt>SQLITE_STATUS_MEMORY_USED</dt>
|
||||
** [[SQLITE_STATUS_MEMORY_USED]] ^(<dt>SQLITE_STATUS_MEMORY_USED</dt>
|
||||
** <dd>This parameter is the current amount of memory checked out
|
||||
** using [sqlite3_malloc()], either directly or indirectly. The
|
||||
** figure includes calls made to [sqlite3_malloc()] by the application
|
||||
@ -5461,23 +5626,24 @@ int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag);
|
||||
** this parameter. The amount returned is the sum of the allocation
|
||||
** sizes as reported by the xSize method in [sqlite3_mem_methods].</dd>)^
|
||||
**
|
||||
** ^(<dt>SQLITE_STATUS_MALLOC_SIZE</dt>
|
||||
** [[SQLITE_STATUS_MALLOC_SIZE]] ^(<dt>SQLITE_STATUS_MALLOC_SIZE</dt>
|
||||
** <dd>This parameter records the largest memory allocation request
|
||||
** handed to [sqlite3_malloc()] or [sqlite3_realloc()] (or their
|
||||
** internal equivalents). Only the value returned in the
|
||||
** *pHighwater parameter to [sqlite3_status()] is of interest.
|
||||
** The value written into the *pCurrent parameter is undefined.</dd>)^
|
||||
**
|
||||
** ^(<dt>SQLITE_STATUS_MALLOC_COUNT</dt>
|
||||
** [[SQLITE_STATUS_MALLOC_COUNT]] ^(<dt>SQLITE_STATUS_MALLOC_COUNT</dt>
|
||||
** <dd>This parameter records the number of separate memory allocations
|
||||
** currently checked out.</dd>)^
|
||||
**
|
||||
** ^(<dt>SQLITE_STATUS_PAGECACHE_USED</dt>
|
||||
** [[SQLITE_STATUS_PAGECACHE_USED]] ^(<dt>SQLITE_STATUS_PAGECACHE_USED</dt>
|
||||
** <dd>This parameter returns the number of pages used out of the
|
||||
** [pagecache memory allocator] that was configured using
|
||||
** [SQLITE_CONFIG_PAGECACHE]. The
|
||||
** value returned is in pages, not in bytes.</dd>)^
|
||||
**
|
||||
** [[SQLITE_STATUS_PAGECACHE_OVERFLOW]]
|
||||
** ^(<dt>SQLITE_STATUS_PAGECACHE_OVERFLOW</dt>
|
||||
** <dd>This parameter returns the number of bytes of page cache
|
||||
** allocation which could not be satisfied by the [SQLITE_CONFIG_PAGECACHE]
|
||||
@ -5487,13 +5653,13 @@ int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag);
|
||||
** [SQLITE_CONFIG_PAGECACHE]) and allocations that overflowed because
|
||||
** no space was left in the page cache.</dd>)^
|
||||
**
|
||||
** ^(<dt>SQLITE_STATUS_PAGECACHE_SIZE</dt>
|
||||
** [[SQLITE_STATUS_PAGECACHE_SIZE]] ^(<dt>SQLITE_STATUS_PAGECACHE_SIZE</dt>
|
||||
** <dd>This parameter records the largest memory allocation request
|
||||
** handed to [pagecache memory allocator]. Only the value returned in the
|
||||
** *pHighwater parameter to [sqlite3_status()] is of interest.
|
||||
** The value written into the *pCurrent parameter is undefined.</dd>)^
|
||||
**
|
||||
** ^(<dt>SQLITE_STATUS_SCRATCH_USED</dt>
|
||||
** [[SQLITE_STATUS_SCRATCH_USED]] ^(<dt>SQLITE_STATUS_SCRATCH_USED</dt>
|
||||
** <dd>This parameter returns the number of allocations used out of the
|
||||
** [scratch memory allocator] configured using
|
||||
** [SQLITE_CONFIG_SCRATCH]. The value returned is in allocations, not
|
||||
@ -5501,7 +5667,7 @@ int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag);
|
||||
** outstanding at time, this parameter also reports the number of threads
|
||||
** using scratch memory at the same time.</dd>)^
|
||||
**
|
||||
** ^(<dt>SQLITE_STATUS_SCRATCH_OVERFLOW</dt>
|
||||
** [[SQLITE_STATUS_SCRATCH_OVERFLOW]] ^(<dt>SQLITE_STATUS_SCRATCH_OVERFLOW</dt>
|
||||
** <dd>This parameter returns the number of bytes of scratch memory
|
||||
** allocation which could not be satisfied by the [SQLITE_CONFIG_SCRATCH]
|
||||
** buffer and where forced to overflow to [sqlite3_malloc()]. The values
|
||||
@ -5511,13 +5677,13 @@ int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag);
|
||||
** slots were available.
|
||||
** </dd>)^
|
||||
**
|
||||
** ^(<dt>SQLITE_STATUS_SCRATCH_SIZE</dt>
|
||||
** [[SQLITE_STATUS_SCRATCH_SIZE]] ^(<dt>SQLITE_STATUS_SCRATCH_SIZE</dt>
|
||||
** <dd>This parameter records the largest memory allocation request
|
||||
** handed to [scratch memory allocator]. Only the value returned in the
|
||||
** *pHighwater parameter to [sqlite3_status()] is of interest.
|
||||
** The value written into the *pCurrent parameter is undefined.</dd>)^
|
||||
**
|
||||
** ^(<dt>SQLITE_STATUS_PARSER_STACK</dt>
|
||||
** [[SQLITE_STATUS_PARSER_STACK]] ^(<dt>SQLITE_STATUS_PARSER_STACK</dt>
|
||||
** <dd>This parameter records the deepest parser stack. It is only
|
||||
** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].</dd>)^
|
||||
** </dl>
|
||||
@ -5542,9 +5708,9 @@ int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag);
|
||||
** about a single [database connection]. ^The first argument is the
|
||||
** database connection object to be interrogated. ^The second argument
|
||||
** is an integer constant, taken from the set of
|
||||
** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros, that
|
||||
** [SQLITE_DBSTATUS options], that
|
||||
** determines the parameter to interrogate. The set of
|
||||
** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros is likely
|
||||
** [SQLITE_DBSTATUS options] is likely
|
||||
** to grow in future releases of SQLite.
|
||||
**
|
||||
** ^The current value of the requested parameter is written into *pCur
|
||||
@ -5561,6 +5727,7 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Status Parameters for database connections
|
||||
** KEYWORDS: {SQLITE_DBSTATUS options}
|
||||
**
|
||||
** These constants are the available integer "verbs" that can be passed as
|
||||
** the second argument to the [sqlite3_db_status()] interface.
|
||||
@ -5572,15 +5739,16 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
|
||||
** if a discontinued or unsupported verb is invoked.
|
||||
**
|
||||
** <dl>
|
||||
** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_USED</dt>
|
||||
** [[SQLITE_DBSTATUS_LOOKASIDE_USED]] ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_USED</dt>
|
||||
** <dd>This parameter returns the number of lookaside memory slots currently
|
||||
** checked out.</dd>)^
|
||||
**
|
||||
** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_HIT</dt>
|
||||
** [[SQLITE_DBSTATUS_LOOKASIDE_HIT]] ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_HIT</dt>
|
||||
** <dd>This parameter returns the number malloc attempts that were
|
||||
** satisfied using lookaside memory. Only the high-water value is meaningful;
|
||||
** the current value is always zero.)^
|
||||
**
|
||||
** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE]]
|
||||
** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE</dt>
|
||||
** <dd>This parameter returns the number malloc attempts that might have
|
||||
** been satisfied using lookaside memory but failed due to the amount of
|
||||
@ -5588,6 +5756,7 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
|
||||
** Only the high-water value is meaningful;
|
||||
** the current value is always zero.)^
|
||||
**
|
||||
** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL]]
|
||||
** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL</dt>
|
||||
** <dd>This parameter returns the number malloc attempts that might have
|
||||
** been satisfied using lookaside memory but failed due to all lookaside
|
||||
@ -5595,12 +5764,12 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
|
||||
** Only the high-water value is meaningful;
|
||||
** the current value is always zero.)^
|
||||
**
|
||||
** ^(<dt>SQLITE_DBSTATUS_CACHE_USED</dt>
|
||||
** [[SQLITE_DBSTATUS_CACHE_USED]] ^(<dt>SQLITE_DBSTATUS_CACHE_USED</dt>
|
||||
** <dd>This parameter returns the approximate number of of bytes of heap
|
||||
** memory used by all pager caches associated with the database connection.)^
|
||||
** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0.
|
||||
**
|
||||
** ^(<dt>SQLITE_DBSTATUS_SCHEMA_USED</dt>
|
||||
** [[SQLITE_DBSTATUS_SCHEMA_USED]] ^(<dt>SQLITE_DBSTATUS_SCHEMA_USED</dt>
|
||||
** <dd>This parameter returns the approximate number of of bytes of heap
|
||||
** memory used to store the schema for all databases associated
|
||||
** with the connection - main, temp, and any [ATTACH]-ed databases.)^
|
||||
@ -5609,7 +5778,7 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
|
||||
** [shared cache mode] being enabled.
|
||||
** ^The highwater mark associated with SQLITE_DBSTATUS_SCHEMA_USED is always 0.
|
||||
**
|
||||
** ^(<dt>SQLITE_DBSTATUS_STMT_USED</dt>
|
||||
** [[SQLITE_DBSTATUS_STMT_USED]] ^(<dt>SQLITE_DBSTATUS_STMT_USED</dt>
|
||||
** <dd>This parameter returns the approximate number of of bytes of heap
|
||||
** and lookaside memory used by all prepared statements associated with
|
||||
** the database connection.)^
|
||||
@ -5631,7 +5800,7 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
|
||||
** CAPI3REF: Prepared Statement Status
|
||||
**
|
||||
** ^(Each prepared statement maintains various
|
||||
** [SQLITE_STMTSTATUS_SORT | counters] that measure the number
|
||||
** [SQLITE_STMTSTATUS counters] that measure the number
|
||||
** of times it has performed specific operations.)^ These counters can
|
||||
** be used to monitor the performance characteristics of the prepared
|
||||
** statements. For example, if the number of table steps greatly exceeds
|
||||
@ -5642,7 +5811,7 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
|
||||
** ^(This interface is used to retrieve and reset counter values from
|
||||
** a [prepared statement]. The first argument is the prepared statement
|
||||
** object to be interrogated. The second argument
|
||||
** is an integer code for a specific [SQLITE_STMTSTATUS_SORT | counter]
|
||||
** is an integer code for a specific [SQLITE_STMTSTATUS counter]
|
||||
** to be interrogated.)^
|
||||
** ^The current value of the requested counter is returned.
|
||||
** ^If the resetFlg is true, then the counter is reset to zero after this
|
||||
@ -5654,24 +5823,25 @@ int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Status Parameters for prepared statements
|
||||
** KEYWORDS: {SQLITE_STMTSTATUS counter} {SQLITE_STMTSTATUS counters}
|
||||
**
|
||||
** These preprocessor macros define integer codes that name counter
|
||||
** values associated with the [sqlite3_stmt_status()] interface.
|
||||
** The meanings of the various counters are as follows:
|
||||
**
|
||||
** <dl>
|
||||
** <dt>SQLITE_STMTSTATUS_FULLSCAN_STEP</dt>
|
||||
** [[SQLITE_STMTSTATUS_FULLSCAN_STEP]] <dt>SQLITE_STMTSTATUS_FULLSCAN_STEP</dt>
|
||||
** <dd>^This is the number of times that SQLite has stepped forward in
|
||||
** a table as part of a full table scan. Large numbers for this counter
|
||||
** may indicate opportunities for performance improvement through
|
||||
** careful use of indices.</dd>
|
||||
**
|
||||
** <dt>SQLITE_STMTSTATUS_SORT</dt>
|
||||
** [[SQLITE_STMTSTATUS_SORT]] <dt>SQLITE_STMTSTATUS_SORT</dt>
|
||||
** <dd>^This is the number of sort operations that have occurred.
|
||||
** A non-zero value in this counter may indicate an opportunity to
|
||||
** improvement performance through careful use of indices.</dd>
|
||||
**
|
||||
** <dt>SQLITE_STMTSTATUS_AUTOINDEX</dt>
|
||||
** [[SQLITE_STMTSTATUS_AUTOINDEX]] <dt>SQLITE_STMTSTATUS_AUTOINDEX</dt>
|
||||
** <dd>^This is the number of rows inserted into transient indices that
|
||||
** were created automatically in order to help joins run faster.
|
||||
** A non-zero value in this counter may indicate an opportunity to
|
||||
@ -5722,6 +5892,7 @@ typedef struct sqlite3_pcache sqlite3_pcache;
|
||||
** the application may discard the parameter after the call to
|
||||
** [sqlite3_config()] returns.)^
|
||||
**
|
||||
** [[the xInit() page cache method]]
|
||||
** ^(The xInit() method is called once for each effective
|
||||
** call to [sqlite3_initialize()])^
|
||||
** (usually only once during the lifetime of the process). ^(The xInit()
|
||||
@ -5732,6 +5903,7 @@ typedef struct sqlite3_pcache sqlite3_pcache;
|
||||
** built-in default page cache is used instead of the application defined
|
||||
** page cache.)^
|
||||
**
|
||||
** [[the xShutdown() page cache method]]
|
||||
** ^The xShutdown() method is called by [sqlite3_shutdown()].
|
||||
** It can be used to clean up
|
||||
** any outstanding resources before process shutdown, if required.
|
||||
@ -5746,6 +5918,7 @@ typedef struct sqlite3_pcache sqlite3_pcache;
|
||||
** ^SQLite will never invoke xInit() more than once without an intervening
|
||||
** call to xShutdown().
|
||||
**
|
||||
** [[the xCreate() page cache methods]]
|
||||
** ^SQLite invokes the xCreate() method to construct a new cache instance.
|
||||
** SQLite will typically create one cache instance for each open database file,
|
||||
** though this is not guaranteed. ^The
|
||||
@ -5770,6 +5943,7 @@ typedef struct sqlite3_pcache sqlite3_pcache;
|
||||
** ^Hence, a cache created with bPurgeable false will
|
||||
** never contain any unpinned pages.
|
||||
**
|
||||
** [[the xCachesize() page cache method]]
|
||||
** ^(The xCachesize() method may be called at any time by SQLite to set the
|
||||
** suggested maximum cache-size (number of pages stored by) the cache
|
||||
** instance passed as the first argument. This is the value configured using
|
||||
@ -5777,14 +5951,16 @@ typedef struct sqlite3_pcache sqlite3_pcache;
|
||||
** parameter, the implementation is not required to do anything with this
|
||||
** value; it is advisory only.
|
||||
**
|
||||
** [[the xPagecount() page cache methods]]
|
||||
** The xPagecount() method must return the number of pages currently
|
||||
** stored in the cache, both pinned and unpinned.
|
||||
**
|
||||
** [[the xFetch() page cache methods]]
|
||||
** The xFetch() method locates a page in the cache and returns a pointer to
|
||||
** the page, or a NULL pointer.
|
||||
** A "page", in this context, means a buffer of szPage bytes aligned at an
|
||||
** 8-byte boundary. The page to be fetched is determined by the key. ^The
|
||||
** mimimum key value is 1. After it has been retrieved using xFetch, the page
|
||||
** minimum key value is 1. After it has been retrieved using xFetch, the page
|
||||
** is considered to be "pinned".
|
||||
**
|
||||
** If the requested page is already in the page cache, then the page cache
|
||||
@ -5808,6 +5984,7 @@ typedef struct sqlite3_pcache sqlite3_pcache;
|
||||
** attempt to unpin one or more cache pages by spilling the content of
|
||||
** pinned pages to disk and synching the operating system disk cache.
|
||||
**
|
||||
** [[the xUnpin() page cache method]]
|
||||
** ^xUnpin() is called by SQLite with a pointer to a currently pinned page
|
||||
** as its second argument. If the third parameter, discard, is non-zero,
|
||||
** then the page must be evicted from the cache.
|
||||
@ -5820,6 +5997,7 @@ typedef struct sqlite3_pcache sqlite3_pcache;
|
||||
** call to xUnpin() unpins the page regardless of the number of prior calls
|
||||
** to xFetch().
|
||||
**
|
||||
** [[the xRekey() page cache methods]]
|
||||
** The xRekey() method is used to change the key value associated with the
|
||||
** page passed as the second argument. If the cache
|
||||
** previously contains an entry associated with newKey, it must be
|
||||
@ -5832,6 +6010,7 @@ typedef struct sqlite3_pcache sqlite3_pcache;
|
||||
** of these pages are pinned, they are implicitly unpinned, meaning that
|
||||
** they can be safely discarded.
|
||||
**
|
||||
** [[the xDestroy() page cache method]]
|
||||
** ^The xDestroy() method is used to delete a cache allocated by xCreate().
|
||||
** All resources associated with the specified cache should be freed. ^After
|
||||
** calling the xDestroy() method, SQLite considers the [sqlite3_pcache*]
|
||||
@ -5894,7 +6073,7 @@ typedef struct sqlite3_backup sqlite3_backup;
|
||||
** There should be exactly one call to sqlite3_backup_finish() for each
|
||||
** successful call to sqlite3_backup_init().
|
||||
**
|
||||
** <b>sqlite3_backup_init()</b>
|
||||
** [[sqlite3_backup_init()]] <b>sqlite3_backup_init()</b>
|
||||
**
|
||||
** ^The D and N arguments to sqlite3_backup_init(D,N,S,M) are the
|
||||
** [database connection] associated with the destination database
|
||||
@ -5921,7 +6100,7 @@ typedef struct sqlite3_backup sqlite3_backup;
|
||||
** sqlite3_backup_finish() functions to perform the specified backup
|
||||
** operation.
|
||||
**
|
||||
** <b>sqlite3_backup_step()</b>
|
||||
** [[sqlite3_backup_step()]] <b>sqlite3_backup_step()</b>
|
||||
**
|
||||
** ^Function sqlite3_backup_step(B,N) will copy up to N pages between
|
||||
** the source and destination databases specified by [sqlite3_backup] object B.
|
||||
@ -5978,7 +6157,7 @@ typedef struct sqlite3_backup sqlite3_backup;
|
||||
** by the backup operation, then the backup database is automatically
|
||||
** updated at the same time.
|
||||
**
|
||||
** <b>sqlite3_backup_finish()</b>
|
||||
** [[sqlite3_backup_finish()]] <b>sqlite3_backup_finish()</b>
|
||||
**
|
||||
** When sqlite3_backup_step() has returned [SQLITE_DONE], or when the
|
||||
** application wishes to abandon the backup operation, the application
|
||||
@ -6001,7 +6180,8 @@ typedef struct sqlite3_backup sqlite3_backup;
|
||||
** is not a permanent error and does not affect the return value of
|
||||
** sqlite3_backup_finish().
|
||||
**
|
||||
** <b>sqlite3_backup_remaining(), sqlite3_backup_pagecount()</b>
|
||||
** [[sqlite3_backup__remaining()]] [[sqlite3_backup_pagecount()]]
|
||||
** <b>sqlite3_backup_remaining() and sqlite3_backup_pagecount()</b>
|
||||
**
|
||||
** ^Each call to sqlite3_backup_step() sets two values inside
|
||||
** the [sqlite3_backup] object: the number of pages still to be backed
|
||||
@ -6387,6 +6567,93 @@ int sqlite3_wal_checkpoint_v2(
|
||||
#define SQLITE_CHECKPOINT_FULL 1
|
||||
#define SQLITE_CHECKPOINT_RESTART 2
|
||||
|
||||
/*
|
||||
** CAPI3REF: Virtual Table Interface Configuration
|
||||
**
|
||||
** This function may be called by either the [xConnect] or [xCreate] method
|
||||
** of a [virtual table] implementation to configure
|
||||
** various facets of the virtual table interface.
|
||||
**
|
||||
** If this interface is invoked outside the context of an xConnect or
|
||||
** xCreate virtual table method then the behavior is undefined.
|
||||
**
|
||||
** At present, there is only one option that may be configured using
|
||||
** this function. (See [SQLITE_VTAB_CONSTRAINT_SUPPORT].) Further options
|
||||
** may be added in the future.
|
||||
*/
|
||||
int sqlite3_vtab_config(sqlite3*, int op, ...);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Virtual Table Configuration Options
|
||||
**
|
||||
** These macros define the various options to the
|
||||
** [sqlite3_vtab_config()] interface that [virtual table] implementations
|
||||
** can use to customize and optimize their behavior.
|
||||
**
|
||||
** <dl>
|
||||
** <dt>SQLITE_VTAB_CONSTRAINT_SUPPORT
|
||||
** <dd>Calls of the form
|
||||
** [sqlite3_vtab_config](db,SQLITE_VTAB_CONSTRAINT_SUPPORT,X) are supported,
|
||||
** where X is an integer. If X is zero, then the [virtual table] whose
|
||||
** [xCreate] or [xConnect] method invoked [sqlite3_vtab_config()] does not
|
||||
** support constraints. In this configuration (which is the default) if
|
||||
** a call to the [xUpdate] method returns [SQLITE_CONSTRAINT], then the entire
|
||||
** statement is rolled back as if [ON CONFLICT | OR ABORT] had been
|
||||
** specified as part of the users SQL statement, regardless of the actual
|
||||
** ON CONFLICT mode specified.
|
||||
**
|
||||
** If X is non-zero, then the virtual table implementation guarantees
|
||||
** that if [xUpdate] returns [SQLITE_CONSTRAINT], it will do so before
|
||||
** any modifications to internal or persistent data structures have been made.
|
||||
** If the [ON CONFLICT] mode is ABORT, FAIL, IGNORE or ROLLBACK, SQLite
|
||||
** is able to roll back a statement or database transaction, and abandon
|
||||
** or continue processing the current SQL statement as appropriate.
|
||||
** If the ON CONFLICT mode is REPLACE and the [xUpdate] method returns
|
||||
** [SQLITE_CONSTRAINT], SQLite handles this as if the ON CONFLICT mode
|
||||
** had been ABORT.
|
||||
**
|
||||
** Virtual table implementations that are required to handle OR REPLACE
|
||||
** must do so within the [xUpdate] method. If a call to the
|
||||
** [sqlite3_vtab_on_conflict()] function indicates that the current ON
|
||||
** CONFLICT policy is REPLACE, the virtual table implementation should
|
||||
** silently replace the appropriate rows within the xUpdate callback and
|
||||
** return SQLITE_OK. Or, if this is not possible, it may return
|
||||
** SQLITE_CONSTRAINT, in which case SQLite falls back to OR ABORT
|
||||
** constraint handling.
|
||||
** </dl>
|
||||
*/
|
||||
#define SQLITE_VTAB_CONSTRAINT_SUPPORT 1
|
||||
|
||||
/*
|
||||
** CAPI3REF: Determine The Virtual Table Conflict Policy
|
||||
**
|
||||
** This function may only be called from within a call to the [xUpdate] method
|
||||
** of a [virtual table] implementation for an INSERT or UPDATE operation. ^The
|
||||
** value returned is one of [SQLITE_ROLLBACK], [SQLITE_IGNORE], [SQLITE_FAIL],
|
||||
** [SQLITE_ABORT], or [SQLITE_REPLACE], according to the [ON CONFLICT] mode
|
||||
** of the SQL statement that triggered the call to the [xUpdate] method of the
|
||||
** [virtual table].
|
||||
*/
|
||||
int sqlite3_vtab_on_conflict(sqlite3 *);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Conflict resolution modes
|
||||
**
|
||||
** These constants are returned by [sqlite3_vtab_on_conflict()] to
|
||||
** inform a [virtual table] implementation what the [ON CONFLICT] mode
|
||||
** is for the SQL statement being evaluated.
|
||||
**
|
||||
** Note that the [SQLITE_IGNORE] constant is also used as a potential
|
||||
** return value from the [sqlite3_set_authorizer()] callback and that
|
||||
** [SQLITE_ABORT] is also a [result code].
|
||||
*/
|
||||
#define SQLITE_ROLLBACK 1
|
||||
/* #define SQLITE_IGNORE 2 // Also used by sqlite3_authorizer() callback */
|
||||
#define SQLITE_FAIL 3
|
||||
/* #define SQLITE_ABORT 4 // Also an error code */
|
||||
#define SQLITE_REPLACE 5
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Undo the hack that converts floating point types to integer for
|
||||
|
||||
@ -632,6 +632,7 @@ typedef struct TriggerPrg TriggerPrg;
|
||||
typedef struct TriggerStep TriggerStep;
|
||||
typedef struct UnpackedRecord UnpackedRecord;
|
||||
typedef struct VTable VTable;
|
||||
typedef struct VtabCtx VtabCtx;
|
||||
typedef struct Walker Walker;
|
||||
typedef struct WherePlan WherePlan;
|
||||
typedef struct WhereInfo WhereInfo;
|
||||
@ -681,7 +682,7 @@ struct Db {
|
||||
** A thread must be holding a mutex on the corresponding Btree in order
|
||||
** to access Schema content. This implies that the thread must also be
|
||||
** holding a mutex on the sqlite3 connection pointer that owns the Btree.
|
||||
** For a TEMP Schema, on the connection mutex is required.
|
||||
** For a TEMP Schema, only the connection mutex is required.
|
||||
*/
|
||||
struct Schema {
|
||||
int schema_cookie; /* Database schema version number for this file */
|
||||
@ -802,7 +803,7 @@ struct sqlite3 {
|
||||
int nDb; /* Number of backends currently in use */
|
||||
Db *aDb; /* All backends */
|
||||
int flags; /* Miscellaneous flags. See below */
|
||||
int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */
|
||||
unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */
|
||||
int errCode; /* Most recent error code (SQLITE_*) */
|
||||
int errMask; /* & result codes with this before returning */
|
||||
u8 autoCommit; /* The auto-commit flag. */
|
||||
@ -811,6 +812,7 @@ struct sqlite3 {
|
||||
u8 dfltLockMode; /* Default locking-mode for attached dbs */
|
||||
signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */
|
||||
u8 suppressErr; /* Do not issue error messages if true */
|
||||
u8 vtabOnConflict; /* Value to return for s3_vtab_on_conflict() */
|
||||
int nextPagesize; /* Pagesize after VACUUM if >0 */
|
||||
int nTable; /* Number of tables in the database */
|
||||
CollSeq *pDfltColl; /* The default collating sequence (BINARY) */
|
||||
@ -869,7 +871,7 @@ struct sqlite3 {
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
Hash aModule; /* populated by sqlite3_create_module() */
|
||||
Table *pVTab; /* vtab with active Connect/Create method */
|
||||
VtabCtx *pVtabCtx; /* Context for active vtab connect/create */
|
||||
VTable **aVTrans; /* Virtual tables with open transactions */
|
||||
int nVTrans; /* Allocated size of aVTrans */
|
||||
VTable *pDisconnect; /* Disconnect these in next sqlite3_prepare() */
|
||||
@ -953,6 +955,7 @@ struct sqlite3 {
|
||||
#define SQLITE_IndexCover 0x10 /* Disable index covering table */
|
||||
#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_OptMask 0xff /* Mask of all disablable opts */
|
||||
|
||||
/*
|
||||
@ -1232,6 +1235,8 @@ struct VTable {
|
||||
Module *pMod; /* Pointer to module implementation */
|
||||
sqlite3_vtab *pVtab; /* Pointer to vtab instance */
|
||||
int nRef; /* Number of pointers to this structure */
|
||||
u8 bConstraint; /* True if constraints are supported */
|
||||
int iSavepoint; /* Depth of the SAVEPOINT stack */
|
||||
VTable *pNext; /* Next in linked list (see above) */
|
||||
};
|
||||
|
||||
@ -2226,9 +2231,8 @@ struct Parse {
|
||||
** each recursion */
|
||||
|
||||
int nVar; /* Number of '?' variables seen in the SQL so far */
|
||||
int nVarExpr; /* Number of used slots in apVarExpr[] */
|
||||
int nVarExprAlloc; /* Number of allocated slots in apVarExpr[] */
|
||||
Expr **apVarExpr; /* Pointers to :aaa and $aaaa wildcard expressions */
|
||||
int nzVar; /* Number of available slots in azVar[] */
|
||||
char **azVar; /* Pointers to names of parameters */
|
||||
Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */
|
||||
int nAlias; /* Number of aliased result set columns */
|
||||
int nAliasAlloc; /* Number of allocated slots for aAlias[] */
|
||||
@ -2420,6 +2424,7 @@ struct Sqlite3Config {
|
||||
int bMemstat; /* True to enable memory status */
|
||||
int bCoreMutex; /* True to enable core mutexing */
|
||||
int bFullMutex; /* True to enable full mutexing */
|
||||
int bOpenUri; /* True to interpret filenames as URIs */
|
||||
int mxStrlen; /* Maximum string length */
|
||||
int szLookaside; /* Default lookaside buffer size */
|
||||
int nLookaside; /* Default lookaside buffer count */
|
||||
@ -2448,6 +2453,7 @@ struct Sqlite3Config {
|
||||
int nRefInitMutex; /* Number of users of pInitMutex */
|
||||
void (*xLog)(void*,int,const char*); /* Function for logging */
|
||||
void *pLogArg; /* First argument to xLog() */
|
||||
int bLocaltimeFault; /* True to fail localtime() calls */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -2669,6 +2675,8 @@ void sqlite3AddColumnType(Parse*,Token*);
|
||||
void sqlite3AddDefaultValue(Parse*,ExprSpan*);
|
||||
void sqlite3AddCollateType(Parse*, Token*);
|
||||
void sqlite3EndTable(Parse*,Token*,Token*,Select*);
|
||||
int sqlite3ParseUri(const char*,const char*,unsigned int*,
|
||||
sqlite3_vfs**,char**,char **);
|
||||
|
||||
Bitvec *sqlite3BitvecCreate(u32);
|
||||
int sqlite3BitvecTest(Bitvec*, u32);
|
||||
@ -2873,7 +2881,7 @@ int sqlite3GetInt32(const char *, int*);
|
||||
int sqlite3Atoi(const char*);
|
||||
int sqlite3Utf16ByteLen(const void *pData, int nChar);
|
||||
int sqlite3Utf8CharLen(const char *pData, int nByte);
|
||||
int sqlite3Utf8Read(const u8*, const u8**);
|
||||
u32 sqlite3Utf8Read(const u8*, const u8**);
|
||||
|
||||
/*
|
||||
** Routines to read and write variable-length integers. These used to
|
||||
@ -2919,6 +2927,7 @@ char sqlite3ExprAffinity(Expr *pExpr);
|
||||
int sqlite3Atoi64(const char*, i64*, int, u8);
|
||||
void sqlite3Error(sqlite3*, int, const char*,...);
|
||||
void *sqlite3HexToBlob(sqlite3*, const char *z, int n);
|
||||
u8 sqlite3HexToInt(int h);
|
||||
int sqlite3TwoPartName(Parse *, Token *, Token *, Token **);
|
||||
const char *sqlite3ErrStr(int);
|
||||
int sqlite3ReadSchema(Parse *pParse);
|
||||
@ -2934,6 +2943,12 @@ int sqlite3AddInt64(i64*,i64);
|
||||
int sqlite3SubInt64(i64*,i64);
|
||||
int sqlite3MulInt64(i64*,i64);
|
||||
int sqlite3AbsInt32(int);
|
||||
#ifdef SQLITE_ENABLE_8_3_NAMES
|
||||
void sqlite3FileSuffix3(const char*, char*);
|
||||
#else
|
||||
# define sqlite3FileSuffix3(X,Y)
|
||||
#endif
|
||||
u8 sqlite3GetBoolean(const char *z);
|
||||
|
||||
const void *sqlite3ValueText(sqlite3_value*, u8);
|
||||
int sqlite3ValueBytes(sqlite3_value*, u8);
|
||||
@ -3043,6 +3058,7 @@ void sqlite3AutoLoadExtensions(sqlite3*);
|
||||
# define sqlite3VtabLock(X)
|
||||
# define sqlite3VtabUnlock(X)
|
||||
# define sqlite3VtabUnlockList(X)
|
||||
# define sqlite3VtabSavepoint(X, Y, Z) SQLITE_OK
|
||||
#else
|
||||
void sqlite3VtabClear(sqlite3 *db, Table*);
|
||||
int sqlite3VtabSync(sqlite3 *db, char **);
|
||||
@ -3051,6 +3067,7 @@ void sqlite3AutoLoadExtensions(sqlite3*);
|
||||
void sqlite3VtabLock(VTable *);
|
||||
void sqlite3VtabUnlock(VTable *);
|
||||
void sqlite3VtabUnlockList(sqlite3*);
|
||||
int sqlite3VtabSavepoint(sqlite3 *, int, int);
|
||||
# define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0)
|
||||
#endif
|
||||
void sqlite3VtabMakeWritable(Parse*,Table*);
|
||||
|
||||
@ -761,7 +761,7 @@ static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){
|
||||
case SQLITE_INTEGER: {
|
||||
sqlite_int64 v = sqlite3_value_int64(pIn);
|
||||
if( v>=-2147483647 && v<=2147483647 ){
|
||||
pVal = Tcl_NewIntObj(v);
|
||||
pVal = Tcl_NewIntObj((int)v);
|
||||
}else{
|
||||
pVal = Tcl_NewWideIntObj(v);
|
||||
}
|
||||
@ -1441,7 +1441,7 @@ static Tcl_Obj *dbEvalColumnValue(DbEvalContext *p, int iCol){
|
||||
case SQLITE_INTEGER: {
|
||||
sqlite_int64 v = sqlite3_column_int64(pStmt, iCol);
|
||||
if( v>=-2147483647 && v<=2147483647 ){
|
||||
return Tcl_NewIntObj(v);
|
||||
return Tcl_NewIntObj((int)v);
|
||||
}else{
|
||||
return Tcl_NewWideIntObj(v);
|
||||
}
|
||||
@ -2367,7 +2367,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
||||
}
|
||||
if( zNull && len>0 ){
|
||||
pDb->zNull = Tcl_Alloc( len + 1 );
|
||||
strncpy(pDb->zNull, zNull, len);
|
||||
memcpy(pDb->zNull, zNull, len);
|
||||
pDb->zNull[len] = '\0';
|
||||
}else{
|
||||
pDb->zNull = 0;
|
||||
@ -3585,6 +3585,10 @@ static void init_all(Tcl_Interp *interp){
|
||||
extern int Sqlitetestfuzzer_Init(Tcl_Interp*);
|
||||
extern int Sqlitetestwholenumber_Init(Tcl_Interp*);
|
||||
|
||||
#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
|
||||
extern int Sqlitetestfts3_Init(Tcl_Interp *interp);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_ZIPVFS
|
||||
extern int Zipvfs_Init(Tcl_Interp*);
|
||||
Zipvfs_Init(interp);
|
||||
@ -3625,6 +3629,10 @@ static void init_all(Tcl_Interp *interp){
|
||||
Sqlitetestfuzzer_Init(interp);
|
||||
Sqlitetestwholenumber_Init(interp);
|
||||
|
||||
#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
|
||||
Sqlitetestfts3_Init(interp);
|
||||
#endif
|
||||
|
||||
Tcl_CreateObjCommand(interp,"load_testfixture_extensions",init_all_cmd,0,0);
|
||||
|
||||
#ifdef SQLITE_SSE
|
||||
|
||||
157
src/test1.c
157
src/test1.c
@ -14,6 +14,7 @@
|
||||
** testing of the SQLite library.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "vdbeInt.h"
|
||||
#include "tcl.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -162,6 +163,9 @@ const char *sqlite3TestErrorName(int rc){
|
||||
case SQLITE_IOERR_CHECKRESERVEDLOCK:
|
||||
zName = "SQLITE_IOERR_CHECKRESERVEDLOCK"; break;
|
||||
case SQLITE_IOERR_LOCK: zName = "SQLITE_IOERR_LOCK"; break;
|
||||
case SQLITE_CORRUPT_VTAB: zName = "SQLITE_CORRUPT_VTAB"; break;
|
||||
case SQLITE_READONLY_RECOVERY: zName = "SQLITE_READONLY_RECOVERY"; break;
|
||||
case SQLITE_READONLY_CANTLOCK: zName = "SQLITE_READONLY_CANTLOCK"; break;
|
||||
default: zName = "SQLITE_Unknown"; break;
|
||||
}
|
||||
return zName;
|
||||
@ -2326,6 +2330,32 @@ static int test_stmt_readonly(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: uses_stmt_journal STMT
|
||||
**
|
||||
** Return true if STMT uses a statement journal.
|
||||
*/
|
||||
static int uses_stmt_journal(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3_stmt *pStmt;
|
||||
int rc;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
Tcl_GetStringFromObj(objv[0], 0), " STMT", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
|
||||
rc = sqlite3_stmt_readonly(pStmt);
|
||||
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(((Vdbe *)pStmt)->usesStmtJournal));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_reset STMT
|
||||
@ -3818,6 +3848,76 @@ static int test_open(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_open_v2 FILENAME FLAGS VFS
|
||||
*/
|
||||
static int test_open_v2(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
const char *zFilename;
|
||||
const char *zVfs;
|
||||
int flags = 0;
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
char zBuf[100];
|
||||
|
||||
int nFlag;
|
||||
Tcl_Obj **apFlag;
|
||||
int i;
|
||||
|
||||
if( objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "FILENAME FLAGS VFS");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zFilename = Tcl_GetString(objv[1]);
|
||||
zVfs = Tcl_GetString(objv[3]);
|
||||
if( zVfs[0]==0x00 ) zVfs = 0;
|
||||
|
||||
rc = Tcl_ListObjGetElements(interp, objv[2], &nFlag, &apFlag);
|
||||
if( rc!=TCL_OK ) return rc;
|
||||
for(i=0; i<nFlag; i++){
|
||||
int iFlag;
|
||||
struct OpenFlag {
|
||||
const char *zFlag;
|
||||
int flag;
|
||||
} aFlag[] = {
|
||||
{ "SQLITE_OPEN_READONLY", SQLITE_OPEN_READONLY },
|
||||
{ "SQLITE_OPEN_READWRITE", SQLITE_OPEN_READWRITE },
|
||||
{ "SQLITE_OPEN_CREATE", SQLITE_OPEN_CREATE },
|
||||
{ "SQLITE_OPEN_DELETEONCLOSE", SQLITE_OPEN_DELETEONCLOSE },
|
||||
{ "SQLITE_OPEN_EXCLUSIVE", SQLITE_OPEN_EXCLUSIVE },
|
||||
{ "SQLITE_OPEN_AUTOPROXY", SQLITE_OPEN_AUTOPROXY },
|
||||
{ "SQLITE_OPEN_MAIN_DB", SQLITE_OPEN_MAIN_DB },
|
||||
{ "SQLITE_OPEN_TEMP_DB", SQLITE_OPEN_TEMP_DB },
|
||||
{ "SQLITE_OPEN_TRANSIENT_DB", SQLITE_OPEN_TRANSIENT_DB },
|
||||
{ "SQLITE_OPEN_MAIN_JOURNAL", SQLITE_OPEN_MAIN_JOURNAL },
|
||||
{ "SQLITE_OPEN_TEMP_JOURNAL", SQLITE_OPEN_TEMP_JOURNAL },
|
||||
{ "SQLITE_OPEN_SUBJOURNAL", SQLITE_OPEN_SUBJOURNAL },
|
||||
{ "SQLITE_OPEN_MASTER_JOURNAL", SQLITE_OPEN_MASTER_JOURNAL },
|
||||
{ "SQLITE_OPEN_NOMUTEX", SQLITE_OPEN_NOMUTEX },
|
||||
{ "SQLITE_OPEN_FULLMUTEX", SQLITE_OPEN_FULLMUTEX },
|
||||
{ "SQLITE_OPEN_SHAREDCACHE", SQLITE_OPEN_SHAREDCACHE },
|
||||
{ "SQLITE_OPEN_PRIVATECACHE", SQLITE_OPEN_PRIVATECACHE },
|
||||
{ "SQLITE_OPEN_WAL", SQLITE_OPEN_WAL },
|
||||
{ "SQLITE_OPEN_URI", SQLITE_OPEN_URI },
|
||||
{ 0, 0 }
|
||||
};
|
||||
rc = Tcl_GetIndexFromObjStruct(interp, apFlag[i], aFlag, sizeof(aFlag[0]),
|
||||
"flag", 0, &iFlag
|
||||
);
|
||||
if( rc!=TCL_OK ) return rc;
|
||||
flags |= aFlag[iFlag].flag;
|
||||
}
|
||||
|
||||
rc = sqlite3_open_v2(zFilename, &db, flags, zVfs);
|
||||
if( sqlite3TestMakePointerStr(interp, zBuf, db) ) return TCL_ERROR;
|
||||
Tcl_AppendResult(interp, zBuf, 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_open16 filename options
|
||||
*/
|
||||
@ -5417,11 +5517,64 @@ static int test_print_eqp(
|
||||
}
|
||||
if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
|
||||
rc = printExplainQueryPlan(pStmt);
|
||||
/* This is needed on Windows so that a test case using this
|
||||
** function can open a read pipe and get the output of
|
||||
** printExplainQueryPlan() immediately.
|
||||
*/
|
||||
fflush(stdout);
|
||||
Tcl_SetResult(interp, (char *)t1ErrorName(rc), 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_EXPLAIN */
|
||||
|
||||
/*
|
||||
** sqlite3_test_control VERB ARGS...
|
||||
*/
|
||||
static int test_test_control(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
struct Verb {
|
||||
const char *zName;
|
||||
int i;
|
||||
} aVerb[] = {
|
||||
{ "SQLITE_TESTCTRL_LOCALTIME_FAULT", SQLITE_TESTCTRL_LOCALTIME_FAULT },
|
||||
};
|
||||
int iVerb;
|
||||
int iFlag;
|
||||
int rc;
|
||||
|
||||
if( objc<2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "VERB ARGS...");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
rc = Tcl_GetIndexFromObjStruct(
|
||||
interp, objv[1], aVerb, sizeof(aVerb[0]), "VERB", 0, &iVerb
|
||||
);
|
||||
if( rc!=TCL_OK ) return rc;
|
||||
|
||||
iFlag = aVerb[iVerb].i;
|
||||
switch( iFlag ){
|
||||
case SQLITE_TESTCTRL_LOCALTIME_FAULT: {
|
||||
int val;
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 2, objv, "ONOFF");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetBooleanFromObj(interp, objv[2], &val) ) return TCL_ERROR;
|
||||
sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Tcl_ResetResult(interp);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** optimization_control DB OPT BOOLEAN
|
||||
**
|
||||
@ -5452,6 +5605,7 @@ static int optimization_control(
|
||||
{ "index-cover", SQLITE_IndexCover },
|
||||
{ "groupby-order", SQLITE_GroupByOrder },
|
||||
{ "factor-constants", SQLITE_FactorOutConst },
|
||||
{ "real-as-int", SQLITE_IdxRealAsInt },
|
||||
};
|
||||
|
||||
if( objc!=4 ){
|
||||
@ -5566,6 +5720,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_errmsg16", test_errmsg16 ,0 },
|
||||
{ "sqlite3_open", test_open ,0 },
|
||||
{ "sqlite3_open16", test_open16 ,0 },
|
||||
{ "sqlite3_open_v2", test_open_v2 ,0 },
|
||||
{ "sqlite3_complete16", test_complete16 ,0 },
|
||||
|
||||
{ "sqlite3_prepare", test_prepare ,0 },
|
||||
@ -5583,6 +5738,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_sql", test_sql ,0 },
|
||||
{ "sqlite3_next_stmt", test_next_stmt ,0 },
|
||||
{ "sqlite3_stmt_readonly", test_stmt_readonly ,0 },
|
||||
{ "uses_stmt_journal", uses_stmt_journal ,0 },
|
||||
|
||||
{ "sqlite3_release_memory", test_release_memory, 0},
|
||||
{ "sqlite3_soft_heap_limit", test_soft_heap_limit, 0},
|
||||
@ -5683,6 +5839,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
{ "print_explain_query_plan", test_print_eqp, 0 },
|
||||
#endif
|
||||
{ "sqlite3_test_control", test_test_control },
|
||||
};
|
||||
static int bitmask_size = sizeof(Bitmask)*8;
|
||||
int i;
|
||||
|
||||
@ -78,7 +78,7 @@ static int btree_open(
|
||||
sDb.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);
|
||||
sqlite3_mutex_enter(sDb.mutex);
|
||||
}
|
||||
rc = sqlite3BtreeOpen(argv[1], &sDb, &pBt, 0,
|
||||
rc = sqlite3BtreeOpen(sDb.pVfs, argv[1], &sDb, &pBt, 0,
|
||||
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
|
||||
@ -73,6 +73,12 @@ static void set_options(Tcl_Interp *interp){
|
||||
Tcl_SetVar2(interp, "sqlite_options", "memdebug", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_8_3_NAMES
|
||||
Tcl_SetVar2(interp, "sqlite_options", "8_3_names", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "8_3_names", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_MEMSYS3
|
||||
Tcl_SetVar2(interp, "sqlite_options", "mem3", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
@ -219,11 +225,7 @@ static void set_options(Tcl_Interp *interp){
|
||||
Tcl_SetVar2(interp, "sqlite_options", "compound", "1", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_CONFLICT_CLAUSE
|
||||
Tcl_SetVar2(interp, "sqlite_options", "conflict", "0", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "conflict", "1", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#if SQLITE_OS_UNIX
|
||||
Tcl_SetVar2(interp, "sqlite_options", "crashtest", "1", TCL_GLOBAL_ONLY);
|
||||
|
||||
@ -10,14 +10,12 @@
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** An example of a simple VFS implementation that omits complex features
|
||||
** often not required or not possible on embedded platforms. Also includes
|
||||
** code to buffer writes to the journal file, which can be a significant
|
||||
** performance improvement on some embedded platforms.
|
||||
** This file implements an example of a simple VFS implementation that
|
||||
** omits complex features often not required or not possible on embedded
|
||||
** platforms. Code is included to buffer writes to the journal file,
|
||||
** which can be a significant performance improvement on some embedded
|
||||
** platforms.
|
||||
**
|
||||
*/
|
||||
|
||||
/*
|
||||
** OVERVIEW
|
||||
**
|
||||
** The code in this file implements a minimal SQLite VFS that can be
|
||||
@ -128,6 +126,7 @@
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
/*
|
||||
** Size of the write buffer used by journal files in bytes.
|
||||
|
||||
@ -14,13 +14,7 @@
|
||||
** an existing VFS. The code in this file attempts to verify that SQLite
|
||||
** correctly populates and syncs a journal file before writing to a
|
||||
** corresponding database file.
|
||||
*/
|
||||
#if SQLITE_TEST /* This file is used for testing only */
|
||||
|
||||
#include "sqlite3.h"
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
**
|
||||
** INTERFACE
|
||||
**
|
||||
** The public interface to this wrapper VFS is two functions:
|
||||
@ -99,6 +93,10 @@
|
||||
**
|
||||
** c) The journal file is deleted using xDelete.
|
||||
*/
|
||||
#if SQLITE_TEST /* This file is used for testing only */
|
||||
|
||||
#include "sqlite3.h"
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** Maximum pathname length supported by the jt backend.
|
||||
|
||||
@ -1174,6 +1174,35 @@ static int test_config_error(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_config_uri BOOLEAN
|
||||
**
|
||||
** Invoke sqlite3_config() or sqlite3_db_config() with invalid
|
||||
** opcodes and verify that they return errors.
|
||||
*/
|
||||
static int test_config_uri(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc;
|
||||
int bOpenUri;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "BOOL");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetBooleanFromObj(interp, objv[1], &bOpenUri) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
rc = sqlite3_config(SQLITE_CONFIG_URI, bOpenUri);
|
||||
Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE);
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage:
|
||||
**
|
||||
@ -1422,6 +1451,7 @@ int Sqlitetest_malloc_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_config_memstatus", test_config_memstatus ,0 },
|
||||
{ "sqlite3_config_lookaside", test_config_lookaside ,0 },
|
||||
{ "sqlite3_config_error", test_config_error ,0 },
|
||||
{ "sqlite3_config_uri", test_config_uri ,0 },
|
||||
{ "sqlite3_db_config_lookaside",test_db_config_lookaside ,0 },
|
||||
{ "sqlite3_dump_memsys3", test_dump_memsys3 ,3 },
|
||||
{ "sqlite3_dump_memsys5", test_dump_memsys3 ,5 },
|
||||
|
||||
@ -11,13 +11,36 @@
|
||||
*************************************************************************
|
||||
**
|
||||
** This file contains a VFS "shim" - a layer that sits in between the
|
||||
** pager and the real VFS.
|
||||
** pager and the real VFS - that breaks up a very large database file
|
||||
** into two or more smaller files on disk. This is useful, for example,
|
||||
** in order to support large, multi-gigabyte databases on older filesystems
|
||||
** that limit the maximum file size to 2 GiB.
|
||||
**
|
||||
** This particular shim enforces a multiplex system on DB files.
|
||||
** This shim shards/partitions a single DB file into smaller
|
||||
** "chunks" such that the total DB file size may exceed the maximum
|
||||
** file size of the underlying file system.
|
||||
** USAGE:
|
||||
**
|
||||
** Compile this source file and link it with your application. Then
|
||||
** at start-time, invoke the following procedure:
|
||||
**
|
||||
** int sqlite3_multiplex_initialize(
|
||||
** const char *zOrigVfsName, // The underlying real VFS
|
||||
** int makeDefault // True to make multiplex the default VFS
|
||||
** );
|
||||
**
|
||||
** The procedure call above will create and register a new VFS shim named
|
||||
** "multiplex". The multiplex VFS will use the VFS named by zOrigVfsName to
|
||||
** do the actual disk I/O. (The zOrigVfsName parameter may be NULL, in
|
||||
** which case the default VFS at the moment sqlite3_multiplex_initialize()
|
||||
** is called will be used as the underlying real VFS.)
|
||||
**
|
||||
** If the makeDefault parameter is TRUE then multiplex becomes the new
|
||||
** default VFS. Otherwise, you can use the multiplex VFS by specifying
|
||||
** "multiplex" as the 4th parameter to sqlite3_open_v2() or by employing
|
||||
** URI filenames and adding "vfs=multiplex" as a parameter to the filename
|
||||
** 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.
|
||||
*/
|
||||
#include "sqlite3.h"
|
||||
#include <string.h>
|
||||
@ -185,13 +208,77 @@ static void multiplexLeave(void){ sqlite3_mutex_leave(gMultiplex.pMutex); }
|
||||
** than the actual length of the string. For very long strings (greater
|
||||
** than 1GiB) the value returned might be less than the true string length.
|
||||
*/
|
||||
int multiplexStrlen30(const char *z){
|
||||
static int multiplexStrlen30(const char *z){
|
||||
const char *z2 = z;
|
||||
if( z==0 ) return 0;
|
||||
while( *z2 ){ z2++; }
|
||||
return 0x3fffffff & (int)(z2 - z);
|
||||
}
|
||||
|
||||
/*
|
||||
** Create a temporary file name in zBuf. zBuf must be big enough to
|
||||
** hold at pOrigVfs->mxPathname characters. This function departs
|
||||
** from the traditional temporary name generation in the os_win
|
||||
** and os_unix VFS in several ways, but is necessary so that
|
||||
** the file name is known for temporary files (like those used
|
||||
** during vacuum.)
|
||||
**
|
||||
** N.B. This routine assumes your underlying VFS is ok with using
|
||||
** "/" as a directory seperator. This is the default for UNIXs
|
||||
** and is allowed (even mixed) for most versions of Windows.
|
||||
*/
|
||||
static int multiplexGetTempname(sqlite3_vfs *pOrigVfs, int nBuf, char *zBuf){
|
||||
static char zChars[] =
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"0123456789";
|
||||
int i,j;
|
||||
int attempts = 0;
|
||||
int exists = 0;
|
||||
int rc = SQLITE_ERROR;
|
||||
|
||||
/* Check that the output buffer is large enough for
|
||||
** pVfs->mxPathname characters.
|
||||
*/
|
||||
if( pOrigVfs->mxPathname <= nBuf ){
|
||||
char *zTmp = sqlite3_malloc(pOrigVfs->mxPathname);
|
||||
if( zTmp==0 ) return SQLITE_NOMEM;
|
||||
|
||||
/* sqlite3_temp_directory should always be less than
|
||||
** pVfs->mxPathname characters.
|
||||
*/
|
||||
sqlite3_snprintf(pOrigVfs->mxPathname,
|
||||
zTmp,
|
||||
"%s/",
|
||||
sqlite3_temp_directory ? sqlite3_temp_directory : ".");
|
||||
rc = pOrigVfs->xFullPathname(pOrigVfs, zTmp, nBuf, zBuf);
|
||||
sqlite3_free(zTmp);
|
||||
if( rc ) return rc;
|
||||
|
||||
/* Check that the output buffer is large enough for the temporary file
|
||||
** name.
|
||||
*/
|
||||
j = multiplexStrlen30(zBuf);
|
||||
if( (j + 8 + 1 + 3 + 1) <= nBuf ){
|
||||
/* Make 3 attempts to generate a unique name. */
|
||||
do {
|
||||
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) ];
|
||||
}
|
||||
memcpy(&zBuf[j+i], ".tmp", 5);
|
||||
rc = pOrigVfs->xAccess(pOrigVfs, zBuf, SQLITE_ACCESS_EXISTS, &exists);
|
||||
} while ( (rc==SQLITE_OK) && exists && (attempts<3) );
|
||||
if( rc==SQLITE_OK && exists ){
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Translate an sqlite3_file* that is really a multiplexGroup* into
|
||||
** the sqlite3_file* for the underlying original VFS.
|
||||
*/
|
||||
@ -295,12 +382,12 @@ static int multiplexOpen(
|
||||
int flags, /* Flags to control the opening */
|
||||
int *pOutFlags /* Flags showing results of opening */
|
||||
){
|
||||
int rc; /* Result code */
|
||||
int rc = SQLITE_OK; /* Result code */
|
||||
multiplexConn *pMultiplexOpen; /* The new multiplex file descriptor */
|
||||
multiplexGroup *pGroup; /* Corresponding multiplexGroup object */
|
||||
sqlite3_file *pSubOpen; /* Real file descriptor */
|
||||
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
|
||||
int nName = multiplexStrlen30(zName);
|
||||
int nName;
|
||||
int i;
|
||||
int sz;
|
||||
|
||||
@ -311,23 +398,39 @@ static int multiplexOpen(
|
||||
*/
|
||||
multiplexEnter();
|
||||
pMultiplexOpen = (multiplexConn*)pConn;
|
||||
/* allocate space for group */
|
||||
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 */
|
||||
|
||||
/* If the second argument to this function is NULL, generate a
|
||||
** temporary file name to use. This will be handled by the
|
||||
** original xOpen method. We just need to allocate space for
|
||||
** it.
|
||||
*/
|
||||
if( !zName ){
|
||||
rc = multiplexGetTempname(pOrigVfs, pOrigVfs->mxPathname, gMultiplex.zName);
|
||||
zName = gMultiplex.zName;
|
||||
}
|
||||
|
||||
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);
|
||||
sz += SQLITE_MULTIPLEX_EXT_SZ;
|
||||
assert(nName+SQLITE_MULTIPLEX_EXT_SZ < pOrigVfs->mxPathname);
|
||||
#else
|
||||
assert(nName >= SQLITE_MULTIPLEX_EXT_SZ);
|
||||
assert(nName < pOrigVfs->mxPathname);
|
||||
assert(nName >= SQLITE_MULTIPLEX_EXT_SZ);
|
||||
assert(nName < pOrigVfs->mxPathname);
|
||||
#endif
|
||||
pGroup = sqlite3_malloc( sz );
|
||||
if( pGroup==0 ){
|
||||
rc=SQLITE_NOMEM;
|
||||
}else{
|
||||
pGroup = sqlite3_malloc( sz );
|
||||
if( pGroup==0 ){
|
||||
rc=SQLITE_NOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
/* assign pointers to extra space allocated */
|
||||
char *p = (char *)&pGroup[1];
|
||||
pMultiplexOpen->pGroup = pGroup;
|
||||
@ -411,7 +514,7 @@ static int multiplexDelete(
|
||||
}
|
||||
rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName,
|
||||
SQLITE_ACCESS_EXISTS, &exists);
|
||||
if( rc2==SQLITE_OK && exists){
|
||||
if( rc2==SQLITE_OK && exists ){
|
||||
/* if it exists, delete it */
|
||||
rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, syncDir);
|
||||
if( rc2!=SQLITE_OK ) rc = rc2;
|
||||
|
||||
@ -323,7 +323,7 @@ static int quotaOpen(
|
||||
pFile=pFile->pNext){}
|
||||
if( pFile==0 ){
|
||||
int nName = strlen(zName);
|
||||
pFile = sqlite3_malloc( sizeof(*pFile) + nName + 1 );
|
||||
pFile = (quotaFile *)sqlite3_malloc( sizeof(*pFile) + nName + 1 );
|
||||
if( pFile==0 ){
|
||||
quotaLeave();
|
||||
pSubOpen->pMethods->xClose(pSubOpen);
|
||||
@ -683,7 +683,7 @@ int sqlite3_quota_set(
|
||||
quotaLeave();
|
||||
return SQLITE_OK;
|
||||
}
|
||||
pGroup = sqlite3_malloc( sizeof(*pGroup) + nPattern + 1 );
|
||||
pGroup = (quotaGroup *)sqlite3_malloc( sizeof(*pGroup) + nPattern + 1 );
|
||||
if( pGroup==0 ){
|
||||
quotaLeave();
|
||||
return SQLITE_NOMEM;
|
||||
|
||||
@ -404,9 +404,9 @@ static int clock_seconds_proc(
|
||||
*/
|
||||
typedef struct UnlockNotification UnlockNotification;
|
||||
struct UnlockNotification {
|
||||
int fired; /* True after unlock event has occured */
|
||||
pthread_cond_t cond; /* Condition variable to wait on */
|
||||
pthread_mutex_t mutex; /* Mutex to protect structure */
|
||||
int fired; /* True after unlock event has occurred */
|
||||
pthread_cond_t cond; /* Condition variable to wait on */
|
||||
pthread_mutex_t mutex; /* Mutex to protect structure */
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
133
src/test_vfs.c
133
src/test_vfs.c
@ -10,10 +10,6 @@
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
*/
|
||||
#if SQLITE_TEST /* This file is used for testing only */
|
||||
|
||||
/*
|
||||
** This file contains the implementation of the Tcl [testvfs] command,
|
||||
** used to create SQLite VFS implementations with various properties and
|
||||
** instrumentation to support testing SQLite.
|
||||
@ -28,6 +24,7 @@
|
||||
** -mxpathname INTEGER (Value for sqlite3_vfs.mxPathname)
|
||||
** -iversion INTEGER (Value for sqlite3_vfs.iVersion)
|
||||
*/
|
||||
#if SQLITE_TEST /* This file is used for testing only */
|
||||
|
||||
#include "sqlite3.h"
|
||||
#include "sqliteInt.h"
|
||||
@ -82,8 +79,6 @@ struct Testvfs {
|
||||
sqlite3_vfs *pVfs; /* The testvfs registered with SQLite */
|
||||
Tcl_Interp *interp; /* Interpreter to run script in */
|
||||
Tcl_Obj *pScript; /* Script to execute */
|
||||
int nScript; /* Number of elements in array apScript */
|
||||
Tcl_Obj **apScript; /* Array version of pScript */
|
||||
TestvfsBuffer *pBuffer; /* List of shared buffers */
|
||||
int isNoshm;
|
||||
|
||||
@ -114,20 +109,21 @@ struct Testvfs {
|
||||
** + Simulating IO errors, and
|
||||
** + Invoking the Tcl callback script.
|
||||
*/
|
||||
#define TESTVFS_SHMOPEN_MASK 0x00000001
|
||||
#define TESTVFS_SHMLOCK_MASK 0x00000010
|
||||
#define TESTVFS_SHMMAP_MASK 0x00000020
|
||||
#define TESTVFS_SHMBARRIER_MASK 0x00000040
|
||||
#define TESTVFS_SHMCLOSE_MASK 0x00000080
|
||||
#define TESTVFS_SHMOPEN_MASK 0x00000001
|
||||
#define TESTVFS_SHMLOCK_MASK 0x00000010
|
||||
#define TESTVFS_SHMMAP_MASK 0x00000020
|
||||
#define TESTVFS_SHMBARRIER_MASK 0x00000040
|
||||
#define TESTVFS_SHMCLOSE_MASK 0x00000080
|
||||
|
||||
#define TESTVFS_OPEN_MASK 0x00000100
|
||||
#define TESTVFS_SYNC_MASK 0x00000200
|
||||
#define TESTVFS_DELETE_MASK 0x00000400
|
||||
#define TESTVFS_CLOSE_MASK 0x00000800
|
||||
#define TESTVFS_WRITE_MASK 0x00001000
|
||||
#define TESTVFS_TRUNCATE_MASK 0x00002000
|
||||
#define TESTVFS_ACCESS_MASK 0x00004000
|
||||
#define TESTVFS_ALL_MASK 0x00007FFF
|
||||
#define TESTVFS_OPEN_MASK 0x00000100
|
||||
#define TESTVFS_SYNC_MASK 0x00000200
|
||||
#define TESTVFS_DELETE_MASK 0x00000400
|
||||
#define TESTVFS_CLOSE_MASK 0x00000800
|
||||
#define TESTVFS_WRITE_MASK 0x00001000
|
||||
#define TESTVFS_TRUNCATE_MASK 0x00002000
|
||||
#define TESTVFS_ACCESS_MASK 0x00004000
|
||||
#define TESTVFS_FULLPATHNAME_MASK 0x00008000
|
||||
#define TESTVFS_ALL_MASK 0x0001FFFF
|
||||
|
||||
|
||||
#define TESTVFS_MAX_PAGES 1024
|
||||
@ -269,48 +265,26 @@ static void tvfsExecTcl(
|
||||
Tcl_Obj *arg3
|
||||
){
|
||||
int rc; /* Return code from Tcl_EvalObj() */
|
||||
int nArg; /* Elements in eval'd list */
|
||||
int nScript;
|
||||
Tcl_Obj ** ap;
|
||||
|
||||
Tcl_Obj *pEval;
|
||||
assert( p->pScript );
|
||||
|
||||
if( !p->apScript ){
|
||||
int nByte;
|
||||
int i;
|
||||
if( TCL_OK!=Tcl_ListObjGetElements(p->interp, p->pScript, &nScript, &ap) ){
|
||||
Tcl_BackgroundError(p->interp);
|
||||
Tcl_ResetResult(p->interp);
|
||||
return;
|
||||
}
|
||||
p->nScript = nScript;
|
||||
nByte = (nScript+TESTVFS_MAX_ARGS)*sizeof(Tcl_Obj *);
|
||||
p->apScript = (Tcl_Obj **)ckalloc(nByte);
|
||||
memset(p->apScript, 0, nByte);
|
||||
for(i=0; i<nScript; i++){
|
||||
p->apScript[i] = ap[i];
|
||||
}
|
||||
}
|
||||
assert( zMethod );
|
||||
assert( p );
|
||||
assert( arg2==0 || arg1!=0 );
|
||||
assert( arg3==0 || arg2!=0 );
|
||||
|
||||
p->apScript[p->nScript] = Tcl_NewStringObj(zMethod, -1);
|
||||
p->apScript[p->nScript+1] = arg1;
|
||||
p->apScript[p->nScript+2] = arg2;
|
||||
p->apScript[p->nScript+3] = arg3;
|
||||
pEval = Tcl_DuplicateObj(p->pScript);
|
||||
Tcl_IncrRefCount(p->pScript);
|
||||
Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj(zMethod, -1));
|
||||
if( arg1 ) Tcl_ListObjAppendElement(p->interp, pEval, arg1);
|
||||
if( arg2 ) Tcl_ListObjAppendElement(p->interp, pEval, arg2);
|
||||
if( arg3 ) Tcl_ListObjAppendElement(p->interp, pEval, arg3);
|
||||
|
||||
for(nArg=p->nScript; p->apScript[nArg]; nArg++){
|
||||
Tcl_IncrRefCount(p->apScript[nArg]);
|
||||
}
|
||||
|
||||
rc = Tcl_EvalObjv(p->interp, nArg, p->apScript, TCL_EVAL_GLOBAL);
|
||||
rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL);
|
||||
if( rc!=TCL_OK ){
|
||||
Tcl_BackgroundError(p->interp);
|
||||
Tcl_ResetResult(p->interp);
|
||||
}
|
||||
|
||||
for(nArg=p->nScript; p->apScript[nArg]; nArg++){
|
||||
Tcl_DecrRefCount(p->apScript[nArg]);
|
||||
p->apScript[nArg] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -545,7 +519,7 @@ static int tvfsOpen(
|
||||
|
||||
/* Evaluate the Tcl script:
|
||||
**
|
||||
** SCRIPT xOpen FILENAME
|
||||
** SCRIPT xOpen FILENAME KEY-VALUE-ARGS
|
||||
**
|
||||
** If the script returns an SQLite error code other than SQLITE_OK, an
|
||||
** error is returned to the caller. If it returns SQLITE_OK, the new
|
||||
@ -554,7 +528,19 @@ static int tvfsOpen(
|
||||
*/
|
||||
Tcl_ResetResult(p->interp);
|
||||
if( p->pScript && p->mask&TESTVFS_OPEN_MASK ){
|
||||
tvfsExecTcl(p, "xOpen", Tcl_NewStringObj(pFd->zFilename, -1), 0, 0);
|
||||
Tcl_Obj *pArg = Tcl_NewObj();
|
||||
Tcl_IncrRefCount(pArg);
|
||||
if( flags&SQLITE_OPEN_MAIN_DB ){
|
||||
const char *z = &zName[strlen(zName)+1];
|
||||
while( *z ){
|
||||
Tcl_ListObjAppendElement(0, pArg, Tcl_NewStringObj(z, -1));
|
||||
z += strlen(z) + 1;
|
||||
Tcl_ListObjAppendElement(0, pArg, Tcl_NewStringObj(z, -1));
|
||||
z += strlen(z) + 1;
|
||||
}
|
||||
}
|
||||
tvfsExecTcl(p, "xOpen", Tcl_NewStringObj(pFd->zFilename, -1), pArg, 0);
|
||||
Tcl_DecrRefCount(pArg);
|
||||
if( tvfsResultCode(p, &rc) ){
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
}else{
|
||||
@ -663,6 +649,14 @@ static int tvfsFullPathname(
|
||||
int nOut,
|
||||
char *zOut
|
||||
){
|
||||
Testvfs *p = (Testvfs *)pVfs->pAppData;
|
||||
if( p->pScript && p->mask&TESTVFS_FULLPATHNAME_MASK ){
|
||||
int rc;
|
||||
tvfsExecTcl(p, "xFullPathname", Tcl_NewStringObj(zPath, -1), 0, 0);
|
||||
if( tvfsResultCode(p, &rc) ){
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
}
|
||||
}
|
||||
return sqlite3OsFullPathname(PARENTVFS(pVfs), zPath, nOut, zOut);
|
||||
}
|
||||
|
||||
@ -1028,18 +1022,19 @@ static int testvfs_obj_cmd(
|
||||
char *zName;
|
||||
int mask;
|
||||
} vfsmethod [] = {
|
||||
{ "xShmOpen", TESTVFS_SHMOPEN_MASK },
|
||||
{ "xShmLock", TESTVFS_SHMLOCK_MASK },
|
||||
{ "xShmBarrier", TESTVFS_SHMBARRIER_MASK },
|
||||
{ "xShmUnmap", TESTVFS_SHMCLOSE_MASK },
|
||||
{ "xShmMap", TESTVFS_SHMMAP_MASK },
|
||||
{ "xSync", TESTVFS_SYNC_MASK },
|
||||
{ "xDelete", TESTVFS_DELETE_MASK },
|
||||
{ "xWrite", TESTVFS_WRITE_MASK },
|
||||
{ "xTruncate", TESTVFS_TRUNCATE_MASK },
|
||||
{ "xOpen", TESTVFS_OPEN_MASK },
|
||||
{ "xClose", TESTVFS_CLOSE_MASK },
|
||||
{ "xAccess", TESTVFS_ACCESS_MASK },
|
||||
{ "xShmOpen", TESTVFS_SHMOPEN_MASK },
|
||||
{ "xShmLock", TESTVFS_SHMLOCK_MASK },
|
||||
{ "xShmBarrier", TESTVFS_SHMBARRIER_MASK },
|
||||
{ "xShmUnmap", TESTVFS_SHMCLOSE_MASK },
|
||||
{ "xShmMap", TESTVFS_SHMMAP_MASK },
|
||||
{ "xSync", TESTVFS_SYNC_MASK },
|
||||
{ "xDelete", TESTVFS_DELETE_MASK },
|
||||
{ "xWrite", TESTVFS_WRITE_MASK },
|
||||
{ "xTruncate", TESTVFS_TRUNCATE_MASK },
|
||||
{ "xOpen", TESTVFS_OPEN_MASK },
|
||||
{ "xClose", TESTVFS_CLOSE_MASK },
|
||||
{ "xAccess", TESTVFS_ACCESS_MASK },
|
||||
{ "xFullPathname", TESTVFS_FULLPATHNAME_MASK },
|
||||
};
|
||||
Tcl_Obj **apElem = 0;
|
||||
int nElem = 0;
|
||||
@ -1076,9 +1071,6 @@ static int testvfs_obj_cmd(
|
||||
int nByte;
|
||||
if( p->pScript ){
|
||||
Tcl_DecrRefCount(p->pScript);
|
||||
ckfree((char *)p->apScript);
|
||||
p->apScript = 0;
|
||||
p->nScript = 0;
|
||||
p->pScript = 0;
|
||||
}
|
||||
Tcl_GetStringFromObj(objv[2], &nByte);
|
||||
@ -1232,7 +1224,6 @@ static void testvfs_obj_del(ClientData cd){
|
||||
Testvfs *p = (Testvfs *)cd;
|
||||
if( p->pScript ) Tcl_DecrRefCount(p->pScript);
|
||||
sqlite3_vfs_unregister(p->pVfs);
|
||||
ckfree((char *)p->apScript);
|
||||
ckfree((char *)p->pVfs);
|
||||
ckfree((char *)p);
|
||||
}
|
||||
|
||||
@ -12,6 +12,100 @@
|
||||
**
|
||||
** This file contains code implements a VFS shim that writes diagnostic
|
||||
** output for each VFS call, similar to "strace".
|
||||
**
|
||||
** USAGE:
|
||||
**
|
||||
** This source file exports a single symbol which is the name of a
|
||||
** function:
|
||||
**
|
||||
** int vfstrace_register(
|
||||
** const char *zTraceName, // Name of the newly constructed VFS
|
||||
** const char *zOldVfsName, // Name of the underlying VFS
|
||||
** int (*xOut)(const char*,void*), // Output routine. ex: fputs
|
||||
** void *pOutArg, // 2nd argument to xOut. ex: stderr
|
||||
** int makeDefault // Make the new VFS the default
|
||||
** );
|
||||
**
|
||||
** Applications that want to trace their VFS usage must provide a callback
|
||||
** function with this prototype:
|
||||
**
|
||||
** int traceOutput(const char *zMessage, void *pAppData);
|
||||
**
|
||||
** This function will "output" the trace messages, where "output" can
|
||||
** mean different things to different applications. The traceOutput function
|
||||
** for the command-line shell (see shell.c) is "fputs" from the standard
|
||||
** library, which means that all trace output is written on the stream
|
||||
** specified by the second argument. In the case of the command-line shell
|
||||
** the second argument is stderr. Other applications might choose to output
|
||||
** trace information to a file, over a socket, or write it into a buffer.
|
||||
**
|
||||
** The vfstrace_register() function creates a new "shim" VFS named by
|
||||
** the zTraceName parameter. A "shim" VFS is an SQLite backend that does
|
||||
** not really perform the duties of a true backend, but simply filters or
|
||||
** interprets VFS calls before passing them off to another VFS which does
|
||||
** the actual work. In this case the other VFS - the one that does the
|
||||
** real work - is identified by the second parameter, zOldVfsName. If
|
||||
** the the 2nd parameter is NULL then the default VFS is used. The common
|
||||
** case is for the 2nd parameter to be NULL.
|
||||
**
|
||||
** The third and fourth parameters are the pointer to the output function
|
||||
** and the second argument to the output function. For the SQLite
|
||||
** command-line shell, when the -vfstrace option is used, these parameters
|
||||
** are fputs and stderr, respectively.
|
||||
**
|
||||
** The fifth argument is true (non-zero) to cause the newly created VFS
|
||||
** to become the default VFS. The common case is for the fifth parameter
|
||||
** to be true.
|
||||
**
|
||||
** The call to vfstrace_register() simply creates the shim VFS that does
|
||||
** tracing. The application must also arrange to use the new VFS for
|
||||
** all database connections that are created and for which tracing is
|
||||
** desired. This can be done by specifying the trace VFS using URI filename
|
||||
** notation, or by specifying the trace VFS as the 4th parameter to
|
||||
** sqlite3_open_v2() or by making the trace VFS be the default (by setting
|
||||
** the 5th parameter of vfstrace_register() to 1).
|
||||
**
|
||||
**
|
||||
** ENABLING VFSTRACE IN A COMMAND-LINE SHELL
|
||||
**
|
||||
** The SQLite command line shell implemented by the shell.c source file
|
||||
** can be used with this module. To compile in -vfstrace support, first
|
||||
** gather this file (test_vfstrace.c), the shell source file (shell.c),
|
||||
** and the SQLite amalgamation source files (sqlite3.c, sqlite3.h) into
|
||||
** the working directory. Then compile using a command like the following:
|
||||
**
|
||||
** gcc -o sqlite3 -Os -I. -DSQLITE_ENABLE_VFSTRACE \
|
||||
** -DSQLITE_THREADSAFE=0 -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_RTREE \
|
||||
** -DHAVE_READLINE -DHAVE_USLEEP=1 \
|
||||
** shell.c test_vfstrace.c sqlite3.c -ldl -lreadline -lncurses
|
||||
**
|
||||
** The gcc command above works on Linux and provides (in addition to the
|
||||
** -vfstrace option) support for FTS3 and FTS4, RTREE, and command-line
|
||||
** editing using the readline library. The command-line shell does not
|
||||
** use threads so we added -DSQLITE_THREADSAFE=0 just to make the code
|
||||
** run a little faster. For compiling on a Mac, you'll probably need
|
||||
** to omit the -DHAVE_READLINE, the -lreadline, and the -lncurses options.
|
||||
** The compilation could be simplified to just this:
|
||||
**
|
||||
** gcc -DSQLITE_ENABLE_VFSTRACE \
|
||||
** shell.c test_vfstrace.c sqlite3.c -ldl -lpthread
|
||||
**
|
||||
** In this second example, all unnecessary options have been removed
|
||||
** Note that since the code is now threadsafe, we had to add the -lpthread
|
||||
** option to pull in the pthreads library.
|
||||
**
|
||||
** To cross-compile for windows using MinGW, a command like this might
|
||||
** work:
|
||||
**
|
||||
** /opt/mingw/bin/i386-mingw32msvc-gcc -o sqlite3.exe -Os -I \
|
||||
** -DSQLITE_THREADSAFE=0 -DSQLITE_ENABLE_VFSTRACE \
|
||||
** shell.c test_vfstrace.c sqlite3.c
|
||||
**
|
||||
** Similar compiler commands will work on different systems. The key
|
||||
** invariants are (1) you must have -DSQLITE_ENABLE_VFSTRACE so that
|
||||
** the shell.c source file will know to include the -vfstrace command-line
|
||||
** option and (2) you must compile and link the three source files
|
||||
** shell,c, test_vfstrace.c, and sqlite3.c.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@ -353,13 +353,12 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
|
||||
testcase( z[0]=='x' ); testcase( z[0]=='X' );
|
||||
if( z[1]=='\'' ){
|
||||
*tokenType = TK_BLOB;
|
||||
for(i=2; (c=z[i])!=0 && c!='\''; i++){
|
||||
if( !sqlite3Isxdigit(c) ){
|
||||
*tokenType = TK_ILLEGAL;
|
||||
}
|
||||
for(i=2; sqlite3Isxdigit(z[i]); i++){}
|
||||
if( z[i]!='\'' || i%2 ){
|
||||
*tokenType = TK_ILLEGAL;
|
||||
while( z[i] && z[i]!='\'' ){ i++; }
|
||||
}
|
||||
if( i%2 || !c ) *tokenType = TK_ILLEGAL;
|
||||
if( c ) i++;
|
||||
if( z[i] ) i++;
|
||||
return i;
|
||||
}
|
||||
/* Otherwise fall through to the next case */
|
||||
@ -412,9 +411,8 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
|
||||
assert( pParse->pNewTable==0 );
|
||||
assert( pParse->pNewTrigger==0 );
|
||||
assert( pParse->nVar==0 );
|
||||
assert( pParse->nVarExpr==0 );
|
||||
assert( pParse->nVarExprAlloc==0 );
|
||||
assert( pParse->apVarExpr==0 );
|
||||
assert( pParse->nzVar==0 );
|
||||
assert( pParse->azVar==0 );
|
||||
enableLookaside = db->lookaside.bEnabled;
|
||||
if( db->lookaside.pStart ) db->lookaside.bEnabled = 1;
|
||||
while( !db->mallocFailed && zSql[i]!=0 ){
|
||||
@ -508,7 +506,8 @@ abort_parse:
|
||||
}
|
||||
|
||||
sqlite3DeleteTrigger(db, pParse->pNewTrigger);
|
||||
sqlite3DbFree(db, pParse->apVarExpr);
|
||||
for(i=pParse->nzVar-1; i>=0; i--) sqlite3DbFree(db, pParse->azVar[i]);
|
||||
sqlite3DbFree(db, pParse->azVar);
|
||||
sqlite3DbFree(db, pParse->aAlias);
|
||||
while( pParse->pAinc ){
|
||||
AutoincInfo *p = pParse->pAinc;
|
||||
|
||||
@ -301,9 +301,8 @@ void sqlite3FinishTrigger(
|
||||
pTrig->table, z);
|
||||
sqlite3DbFree(db, z);
|
||||
sqlite3ChangeCookie(pParse, iDb);
|
||||
sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0, sqlite3MPrintf(
|
||||
db, "type='trigger' AND name='%q'", zName), P4_DYNAMIC
|
||||
);
|
||||
sqlite3VdbeAddParseSchemaOp(v, iDb,
|
||||
sqlite3MPrintf(db, "type='trigger' AND name='%q'", zName));
|
||||
}
|
||||
|
||||
if( db->init.busy ){
|
||||
|
||||
11
src/update.c
11
src/update.c
@ -23,7 +23,8 @@ static void updateVirtualTable(
|
||||
ExprList *pChanges, /* The columns to change in the UPDATE statement */
|
||||
Expr *pRowidExpr, /* Expression used to recompute the rowid */
|
||||
int *aXRef, /* Mapping from columns of pTab to entries in pChanges */
|
||||
Expr *pWhere /* WHERE clause of the UPDATE statement */
|
||||
Expr *pWhere, /* WHERE clause of the UPDATE statement */
|
||||
int onError /* ON CONFLICT strategy */
|
||||
);
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
@ -243,7 +244,7 @@ void sqlite3Update(
|
||||
}
|
||||
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
|
||||
int reg;
|
||||
if( chngRowid ){
|
||||
if( hasFK || chngRowid ){
|
||||
reg = ++pParse->nMem;
|
||||
}else{
|
||||
reg = 0;
|
||||
@ -267,7 +268,7 @@ void sqlite3Update(
|
||||
/* Virtual tables must be handled separately */
|
||||
if( IsVirtual(pTab) ){
|
||||
updateVirtualTable(pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef,
|
||||
pWhere);
|
||||
pWhere, onError);
|
||||
pWhere = 0;
|
||||
pTabList = 0;
|
||||
goto update_cleanup;
|
||||
@ -597,7 +598,8 @@ static void updateVirtualTable(
|
||||
ExprList *pChanges, /* The columns to change in the UPDATE statement */
|
||||
Expr *pRowid, /* Expression used to recompute the rowid */
|
||||
int *aXRef, /* Mapping from columns of pTab to entries in pChanges */
|
||||
Expr *pWhere /* WHERE clause of the UPDATE statement */
|
||||
Expr *pWhere, /* WHERE clause of the UPDATE statement */
|
||||
int onError /* ON CONFLICT strategy */
|
||||
){
|
||||
Vdbe *v = pParse->pVdbe; /* Virtual machine under construction */
|
||||
ExprList *pEList = 0; /* The result set of the SELECT statement */
|
||||
@ -654,6 +656,7 @@ static void updateVirtualTable(
|
||||
}
|
||||
sqlite3VtabMakeWritable(pParse, pTab);
|
||||
sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab->nCol+2, iReg, pVTab, P4_VTAB);
|
||||
sqlite3VdbeChangeP5(v, onError==OE_Default ? OE_Abort : onError);
|
||||
sqlite3MayAbort(pParse);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1);
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
|
||||
@ -163,7 +163,7 @@ static const unsigned char sqlite3Utf8Trans1[] = {
|
||||
|| (c&0xFFFFF800)==0xD800 \
|
||||
|| (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \
|
||||
}
|
||||
int sqlite3Utf8Read(
|
||||
u32 sqlite3Utf8Read(
|
||||
const unsigned char *zIn, /* First byte of UTF-8 character */
|
||||
const unsigned char **pzNext /* Write first byte past UTF-8 char here */
|
||||
){
|
||||
|
||||
32
src/util.c
32
src/util.c
@ -983,13 +983,12 @@ void sqlite3Put4byte(unsigned char *p, u32 v){
|
||||
|
||||
|
||||
|
||||
#if !defined(SQLITE_OMIT_BLOB_LITERAL) || defined(SQLITE_HAS_CODEC)
|
||||
/*
|
||||
** Translate a single byte of Hex into an integer.
|
||||
** This routine only works if h really is a valid hexadecimal
|
||||
** character: 0..9a..fA..F
|
||||
*/
|
||||
static u8 hexToInt(int h){
|
||||
u8 sqlite3HexToInt(int h){
|
||||
assert( (h>='0' && h<='9') || (h>='a' && h<='f') || (h>='A' && h<='F') );
|
||||
#ifdef SQLITE_ASCII
|
||||
h += 9*(1&(h>>6));
|
||||
@ -999,7 +998,6 @@ static u8 hexToInt(int h){
|
||||
#endif
|
||||
return (u8)(h & 0xf);
|
||||
}
|
||||
#endif /* !SQLITE_OMIT_BLOB_LITERAL || SQLITE_HAS_CODEC */
|
||||
|
||||
#if !defined(SQLITE_OMIT_BLOB_LITERAL) || defined(SQLITE_HAS_CODEC)
|
||||
/*
|
||||
@ -1016,7 +1014,7 @@ void *sqlite3HexToBlob(sqlite3 *db, const char *z, int n){
|
||||
n--;
|
||||
if( zBlob ){
|
||||
for(i=0; i<n; i+=2){
|
||||
zBlob[i/2] = (hexToInt(z[i])<<4) | hexToInt(z[i+1]);
|
||||
zBlob[i/2] = (sqlite3HexToInt(z[i])<<4) | sqlite3HexToInt(z[i+1]);
|
||||
}
|
||||
zBlob[i/2] = 0;
|
||||
}
|
||||
@ -1148,3 +1146,29 @@ int sqlite3AbsInt32(int x){
|
||||
if( x==(int)0x80000000 ) return 0x7fffffff;
|
||||
return -x;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_8_3_NAMES
|
||||
/*
|
||||
** If SQLITE_ENABLE_8_3_NAME 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.
|
||||
**
|
||||
** Examples:
|
||||
**
|
||||
** test.db-journal => test.nal
|
||||
** test.db-wal => test.wal
|
||||
** test.db-shm => test.shm
|
||||
*/
|
||||
void sqlite3FileSuffix3(const char *zBaseFilename, char *z){
|
||||
const char *zOk;
|
||||
zOk = sqlite3_uri_parameter(zBaseFilename, "8_3_names");
|
||||
if( zOk && sqlite3GetBoolean(zOk) ){
|
||||
int i, sz;
|
||||
sz = sqlite3Strlen30(z);
|
||||
for(i=sz-1; i>0 && z[i]!='/' && z[i]!='.'; i--){}
|
||||
if( z[i]=='.' && ALWAYS(sz>i+4) ) memcpy(&z[i+1], &z[sz-3], 4);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
117
src/vdbe.c
117
src/vdbe.c
@ -564,6 +564,7 @@ int sqlite3VdbeExec(
|
||||
Mem *pOut = 0; /* Output operand */
|
||||
int iCompare = 0; /* Result of last OP_Compare operation */
|
||||
int *aPermute = 0; /* Permutation of columns for OP_Compare */
|
||||
i64 lastRowid = db->lastRowid; /* Saved value of the last insert ROWID */
|
||||
#ifdef VDBE_PROFILE
|
||||
u64 start; /* CPU clock count at start of opcode */
|
||||
int origPc; /* Program counter at start of opcode */
|
||||
@ -796,7 +797,7 @@ case OP_Yield: { /* in1 */
|
||||
|
||||
/* Opcode: HaltIfNull P1 P2 P3 P4 *
|
||||
**
|
||||
** Check the value in register P3. If is is NULL then Halt using
|
||||
** Check the value in register P3. If it is NULL then Halt using
|
||||
** parameter P1, P2, and P4 as if this were a Halt instruction. If the
|
||||
** value in register P3 is not NULL, then this routine is a no-op.
|
||||
*/
|
||||
@ -833,6 +834,7 @@ case OP_Halt: {
|
||||
p->nFrame--;
|
||||
sqlite3VdbeSetChanges(db, p->nChange);
|
||||
pc = sqlite3VdbeFrameRestore(pFrame);
|
||||
lastRowid = db->lastRowid;
|
||||
if( pOp->p2==OE_Ignore ){
|
||||
/* Instruction pc is the OP_Program that invoked the sub-program
|
||||
** currently being halted. If the p2 instruction of this OP_Halt
|
||||
@ -986,6 +988,7 @@ case OP_Variable: { /* out2-prerelease */
|
||||
Mem *pVar; /* Value being transferred */
|
||||
|
||||
assert( pOp->p1>0 && pOp->p1<=p->nVar );
|
||||
assert( pOp->p4.z==0 || pOp->p4.z==p->azVar[pOp->p1-1] );
|
||||
pVar = &p->aVar[pOp->p1 - 1];
|
||||
if( sqlite3VdbeMemTooBig(pVar) ){
|
||||
goto too_big;
|
||||
@ -1393,16 +1396,9 @@ case OP_Function: {
|
||||
assert( pOp[-1].opcode==OP_CollSeq );
|
||||
ctx.pColl = pOp[-1].p4.pColl;
|
||||
}
|
||||
db->lastRowid = lastRowid;
|
||||
(*ctx.pFunc->xFunc)(&ctx, n, apVal); /* IMP: R-24505-23230 */
|
||||
if( db->mallocFailed ){
|
||||
/* Even though a malloc() has failed, the implementation of the
|
||||
** user function may have called an sqlite3_result_XXX() function
|
||||
** to return a value. The following call releases any resources
|
||||
** associated with such a value.
|
||||
*/
|
||||
sqlite3VdbeMemRelease(&ctx.s);
|
||||
goto no_mem;
|
||||
}
|
||||
lastRowid = db->lastRowid;
|
||||
|
||||
/* If any auxiliary data functions have been called by this user function,
|
||||
** immediately call the destructor for any non-static values.
|
||||
@ -1413,6 +1409,16 @@ case OP_Function: {
|
||||
pOp->p4type = P4_VDBEFUNC;
|
||||
}
|
||||
|
||||
if( db->mallocFailed ){
|
||||
/* Even though a malloc() has failed, the implementation of the
|
||||
** user function may have called an sqlite3_result_XXX() function
|
||||
** to return a value. The following call releases any resources
|
||||
** associated with such a value.
|
||||
*/
|
||||
sqlite3VdbeMemRelease(&ctx.s);
|
||||
goto no_mem;
|
||||
}
|
||||
|
||||
/* If the function returned an error, throw an exception */
|
||||
if( ctx.isError ){
|
||||
sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&ctx.s));
|
||||
@ -1714,7 +1720,7 @@ case OP_ToReal: { /* same as TK_TO_REAL, in1 */
|
||||
** If SQLITE_NULLEQ is set in P5 then the result of comparison is always either
|
||||
** true or false and is never NULL. If both operands are NULL then the result
|
||||
** of comparison is false. If either operand is NULL then the result is true.
|
||||
** If neither operand is NULL the the result is the same as it would be if
|
||||
** If neither operand is NULL the result is the same as it would be if
|
||||
** the SQLITE_NULLEQ flag were omitted from P5.
|
||||
*/
|
||||
/* Opcode: Eq P1 P2 P3 P4 P5
|
||||
@ -1726,7 +1732,7 @@ case OP_ToReal: { /* same as TK_TO_REAL, in1 */
|
||||
** If SQLITE_NULLEQ is set in P5 then the result of comparison is always either
|
||||
** true or false and is never NULL. If both operands are NULL then the result
|
||||
** of comparison is true. If either operand is NULL then the result is false.
|
||||
** If neither operand is NULL the the result is the same as it would be if
|
||||
** If neither operand is NULL the result is the same as it would be if
|
||||
** the SQLITE_NULLEQ flag were omitted from P5.
|
||||
*/
|
||||
/* Opcode: Le P1 P2 P3 P4 P5
|
||||
@ -1762,7 +1768,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
|
||||
pIn3 = &aMem[pOp->p3];
|
||||
flags1 = pIn1->flags;
|
||||
flags3 = pIn3->flags;
|
||||
if( (pIn1->flags | pIn3->flags)&MEM_Null ){
|
||||
if( (flags1 | flags3)&MEM_Null ){
|
||||
/* One or both operands are NULL */
|
||||
if( pOp->p5 & SQLITE_NULLEQ ){
|
||||
/* If SQLITE_NULLEQ is set (which will only happen if the operator is
|
||||
@ -1770,7 +1776,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
|
||||
** or not both operands are null.
|
||||
*/
|
||||
assert( pOp->opcode==OP_Eq || pOp->opcode==OP_Ne );
|
||||
res = (pIn1->flags & pIn3->flags & MEM_Null)==0;
|
||||
res = (flags1 & flags3 & MEM_Null)==0;
|
||||
}else{
|
||||
/* SQLITE_NULLEQ is clear and at least one operand is NULL,
|
||||
** then the result is always NULL.
|
||||
@ -2005,13 +2011,13 @@ case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */
|
||||
|
||||
/* Opcode: If P1 P2 P3 * *
|
||||
**
|
||||
** Jump to P2 if the value in register P1 is true. The value is
|
||||
** Jump to P2 if the value in register P1 is true. The value
|
||||
** is considered true if it is numeric and non-zero. If the value
|
||||
** in P1 is NULL then take the jump if P3 is true.
|
||||
*/
|
||||
/* Opcode: IfNot P1 P2 P3 * *
|
||||
**
|
||||
** Jump to P2 if the value in register P1 is False. The value is
|
||||
** Jump to P2 if the value in register P1 is False. The value
|
||||
** 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.
|
||||
*/
|
||||
@ -2580,6 +2586,17 @@ case OP_Savepoint: {
|
||||
}else{
|
||||
nName = sqlite3Strlen30(zName);
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
/* This call is Ok even if this savepoint is actually a transaction
|
||||
** savepoint (and therefore should not prompt xSavepoint()) callbacks.
|
||||
** If this is a transaction savepoint being opened, it is guaranteed
|
||||
** that the db->aVTrans[] array is empty. */
|
||||
assert( db->autoCommit==0 || db->nVTrans==0 );
|
||||
rc = sqlite3VtabSavepoint(db, SAVEPOINT_BEGIN,
|
||||
db->nStatement+db->nSavepoint);
|
||||
if( rc!=SQLITE_OK ) goto abort_due_to_error;
|
||||
#endif
|
||||
|
||||
/* Create a new savepoint structure. */
|
||||
pNew = sqlite3DbMallocRaw(db, sizeof(Savepoint)+nName+1);
|
||||
if( pNew ){
|
||||
@ -2686,6 +2703,11 @@ case OP_Savepoint: {
|
||||
}else{
|
||||
db->nDeferredCons = pSavepoint->nDeferredCons;
|
||||
}
|
||||
|
||||
if( !isTransaction ){
|
||||
rc = sqlite3VtabSavepoint(db, p1, iSavepoint);
|
||||
if( rc!=SQLITE_OK ) goto abort_due_to_error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2821,7 +2843,11 @@ case OP_Transaction: {
|
||||
db->nStatement++;
|
||||
p->iStatement = db->nSavepoint + db->nStatement;
|
||||
}
|
||||
rc = sqlite3BtreeBeginStmt(pBt, p->iStatement);
|
||||
|
||||
rc = sqlite3VtabSavepoint(db, SAVEPOINT_BEGIN, p->iStatement-1);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3BtreeBeginStmt(pBt, p->iStatement);
|
||||
}
|
||||
|
||||
/* Store the current value of the database handles deferred constraint
|
||||
** counter. If the statement transaction needs to be rolled back,
|
||||
@ -3132,7 +3158,7 @@ case OP_OpenEphemeral: {
|
||||
pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1);
|
||||
if( pCx==0 ) goto no_mem;
|
||||
pCx->nullRow = 1;
|
||||
rc = sqlite3BtreeOpen(0, db, &pCx->pBt,
|
||||
rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBt,
|
||||
BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3BtreeBeginTrans(pCx->pBt, 1);
|
||||
@ -3609,7 +3635,7 @@ case OP_IsUnique: { /* jump, in3 */
|
||||
|
||||
/* Opcode: NotExists P1 P2 P3 * *
|
||||
**
|
||||
** Use the content of register P3 as a integer key. If a record
|
||||
** Use the content of register P3 as an integer key. If a record
|
||||
** with that key does not exist in table of P1, then jump to P2.
|
||||
** If the record does exist, then fall through. The cursor is left
|
||||
** pointing to the record if it exists.
|
||||
@ -3685,7 +3711,7 @@ case OP_Sequence: { /* out2-prerelease */
|
||||
** If P3>0 then P3 is a register in the root frame of this VDBE that holds
|
||||
** the largest previously generated record number. No new record numbers are
|
||||
** allowed to be less than this value. When this value reaches its maximum,
|
||||
** a SQLITE_FULL error is generated. The P3 register is updated with the '
|
||||
** an SQLITE_FULL error is generated. The P3 register is updated with the '
|
||||
** generated record number. This P3 mechanism is used to help implement the
|
||||
** AUTOINCREMENT feature.
|
||||
*/
|
||||
@ -3792,7 +3818,7 @@ case OP_NewRowid: { /* out2-prerelease */
|
||||
assert( pOp->p3==0 ); /* We cannot be in random rowid mode if this is
|
||||
** an AUTOINCREMENT table. */
|
||||
/* on the first attempt, simply do one more than previous */
|
||||
v = db->lastRowid;
|
||||
v = lastRowid;
|
||||
v &= (MAX_ROWID>>1); /* ensure doesn't go negative */
|
||||
v++; /* ensure non-zero */
|
||||
cnt = 0;
|
||||
@ -3902,7 +3928,7 @@ case OP_InsertInt: {
|
||||
}
|
||||
|
||||
if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
|
||||
if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = iKey;
|
||||
if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = lastRowid = iKey;
|
||||
if( pData->flags & MEM_Null ){
|
||||
pData->z = 0;
|
||||
pData->n = 0;
|
||||
@ -4309,7 +4335,7 @@ case OP_Next: { /* jump */
|
||||
|
||||
/* Opcode: IdxInsert P1 P2 P3 * P5
|
||||
**
|
||||
** Register P2 holds a SQL index key made using the
|
||||
** Register P2 holds an SQL index key made using the
|
||||
** MakeRecord instructions. This opcode writes that key
|
||||
** into the index P1. Data for the entry is nil.
|
||||
**
|
||||
@ -4990,7 +5016,7 @@ case OP_Program: { /* jump */
|
||||
|
||||
p->nFrame++;
|
||||
pFrame->pParent = p->pFrame;
|
||||
pFrame->lastRowid = db->lastRowid;
|
||||
pFrame->lastRowid = lastRowid;
|
||||
pFrame->nChange = p->nChange;
|
||||
p->nChange = 0;
|
||||
p->pFrame = pFrame;
|
||||
@ -5773,11 +5799,15 @@ case OP_VUpdate: {
|
||||
Mem **apArg;
|
||||
Mem *pX;
|
||||
|
||||
assert( pOp->p2==1 || pOp->p5==OE_Fail || pOp->p5==OE_Rollback
|
||||
|| pOp->p5==OE_Abort || pOp->p5==OE_Ignore || pOp->p5==OE_Replace
|
||||
);
|
||||
pVtab = pOp->p4.pVtab->pVtab;
|
||||
pModule = (sqlite3_module *)pVtab->pModule;
|
||||
nArg = pOp->p2;
|
||||
assert( pOp->p4type==P4_VTAB );
|
||||
if( ALWAYS(pModule->xUpdate) ){
|
||||
u8 vtabOnConflict = db->vtabOnConflict;
|
||||
apArg = p->apArg;
|
||||
pX = &aMem[pOp->p3];
|
||||
for(i=0; i<nArg; i++){
|
||||
@ -5787,13 +5817,23 @@ case OP_VUpdate: {
|
||||
apArg[i] = pX;
|
||||
pX++;
|
||||
}
|
||||
db->vtabOnConflict = pOp->p5;
|
||||
rc = pModule->xUpdate(pVtab, nArg, apArg, &rowid);
|
||||
db->vtabOnConflict = vtabOnConflict;
|
||||
importVtabErrMsg(p, pVtab);
|
||||
if( rc==SQLITE_OK && pOp->p1 ){
|
||||
assert( nArg>1 && apArg[0] && (apArg[0]->flags&MEM_Null) );
|
||||
db->lastRowid = rowid;
|
||||
db->lastRowid = lastRowid = rowid;
|
||||
}
|
||||
if( rc==SQLITE_CONSTRAINT && pOp->p4.pVtab->bConstraint ){
|
||||
if( pOp->p5==OE_Ignore ){
|
||||
rc = SQLITE_OK;
|
||||
}else{
|
||||
p->errorAction = ((pOp->p5==OE_Replace) ? OE_Abort : pOp->p5);
|
||||
}
|
||||
}else{
|
||||
p->nChange++;
|
||||
}
|
||||
p->nChange++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -5844,20 +5884,20 @@ case OP_MaxPgcnt: { /* out2-prerelease */
|
||||
*/
|
||||
case OP_Trace: {
|
||||
char *zTrace;
|
||||
char *z;
|
||||
|
||||
zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql);
|
||||
if( zTrace ){
|
||||
if( db->xTrace ){
|
||||
char *z = sqlite3VdbeExpandSql(p, zTrace);
|
||||
db->xTrace(db->pTraceArg, z);
|
||||
sqlite3DbFree(db, z);
|
||||
}
|
||||
#ifdef SQLITE_DEBUG
|
||||
if( (db->flags & SQLITE_SqlTrace)!=0 ){
|
||||
sqlite3DebugPrintf("SQL-trace: %s\n", zTrace);
|
||||
}
|
||||
#endif /* SQLITE_DEBUG */
|
||||
if( db->xTrace && (zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 ){
|
||||
z = sqlite3VdbeExpandSql(p, zTrace);
|
||||
db->xTrace(db->pTraceArg, z);
|
||||
sqlite3DbFree(db, z);
|
||||
}
|
||||
#ifdef SQLITE_DEBUG
|
||||
if( (db->flags & SQLITE_SqlTrace)!=0
|
||||
&& (zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0
|
||||
){
|
||||
sqlite3DebugPrintf("SQL-trace: %s\n", zTrace);
|
||||
}
|
||||
#endif /* SQLITE_DEBUG */
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
@ -5941,6 +5981,7 @@ vdbe_error_halt:
|
||||
** release the mutexes on btrees that were acquired at the
|
||||
** top. */
|
||||
vdbe_return:
|
||||
db->lastRowid = lastRowid;
|
||||
sqlite3VdbeLeave(p);
|
||||
return rc;
|
||||
|
||||
|
||||
@ -172,6 +172,7 @@ int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int);
|
||||
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);
|
||||
@ -185,7 +186,7 @@ int sqlite3VdbeMakeLabel(Vdbe*);
|
||||
void sqlite3VdbeRunOnlyOnce(Vdbe*);
|
||||
void sqlite3VdbeDelete(Vdbe*);
|
||||
void sqlite3VdbeDeleteObject(sqlite3*,Vdbe*);
|
||||
void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int,int,int);
|
||||
void sqlite3VdbeMakeReady(Vdbe*,Parse*);
|
||||
int sqlite3VdbeFinalize(Vdbe*);
|
||||
void sqlite3VdbeResolveLabel(Vdbe*, int);
|
||||
int sqlite3VdbeCurrentAddr(Vdbe*);
|
||||
@ -194,6 +195,7 @@ int sqlite3VdbeCurrentAddr(Vdbe*);
|
||||
void sqlite3VdbeTrace(Vdbe*,FILE*);
|
||||
#endif
|
||||
void sqlite3VdbeResetStepResult(Vdbe*);
|
||||
void sqlite3VdbeRewind(Vdbe*);
|
||||
int sqlite3VdbeReset(Vdbe*);
|
||||
void sqlite3VdbeSetNumCols(Vdbe*,int);
|
||||
int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, void(*)(void*));
|
||||
|
||||
@ -287,11 +287,11 @@ struct Vdbe {
|
||||
Mem *aVar; /* Values for the OP_Variable opcode. */
|
||||
char **azVar; /* Name of variables */
|
||||
ynVar nVar; /* Number of entries in aVar[] */
|
||||
ynVar nzVar; /* Number of entries in azVar[] */
|
||||
u32 cacheCtr; /* VdbeCursor row cache generation counter */
|
||||
int pc; /* The program counter */
|
||||
int rc; /* Value to return */
|
||||
u8 errorAction; /* Recovery action to do in case of an error */
|
||||
u8 okVar; /* True if azVar[] has been initialized */
|
||||
u8 explain; /* True if EXPLAIN present on SQL command */
|
||||
u8 changeCntOn; /* True to update the change-counter */
|
||||
u8 expired; /* True if the VM needs to be recompiled */
|
||||
|
||||
@ -102,7 +102,7 @@ int sqlite3_reset(sqlite3_stmt *pStmt){
|
||||
Vdbe *v = (Vdbe*)pStmt;
|
||||
sqlite3_mutex_enter(v->db->mutex);
|
||||
rc = sqlite3VdbeReset(v);
|
||||
sqlite3VdbeMakeReady(v, -1, 0, 0, 0, 0, 0);
|
||||
sqlite3VdbeRewind(v);
|
||||
assert( (rc & (v->db->errMask))==rc );
|
||||
rc = sqlite3ApiExit(v->db, rc);
|
||||
sqlite3_mutex_leave(v->db->mutex);
|
||||
@ -459,6 +459,14 @@ end_of_step:
|
||||
return (rc&db->errMask);
|
||||
}
|
||||
|
||||
/*
|
||||
** The maximum number of times that a statement will try to reparse
|
||||
** itself before giving up and returning SQLITE_SCHEMA.
|
||||
*/
|
||||
#ifndef SQLITE_MAX_SCHEMA_RETRY
|
||||
# define SQLITE_MAX_SCHEMA_RETRY 5
|
||||
#endif
|
||||
|
||||
/*
|
||||
** This is the top-level implementation of sqlite3_step(). Call
|
||||
** sqlite3Step() to do most of the work. If a schema error occurs,
|
||||
@ -477,7 +485,7 @@ int sqlite3_step(sqlite3_stmt *pStmt){
|
||||
db = v->db;
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
while( (rc = sqlite3Step(v))==SQLITE_SCHEMA
|
||||
&& cnt++ < 5
|
||||
&& cnt++ < SQLITE_MAX_SCHEMA_RETRY
|
||||
&& (rc2 = rc = sqlite3Reprepare(v))==SQLITE_OK ){
|
||||
sqlite3_reset(pStmt);
|
||||
v->expired = 0;
|
||||
@ -1167,32 +1175,6 @@ int sqlite3_bind_parameter_count(sqlite3_stmt *pStmt){
|
||||
return p ? p->nVar : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Create a mapping from variable numbers to variable names
|
||||
** in the Vdbe.azVar[] array, if such a mapping does not already
|
||||
** exist.
|
||||
*/
|
||||
static void createVarMap(Vdbe *p){
|
||||
if( !p->okVar ){
|
||||
int j;
|
||||
Op *pOp;
|
||||
sqlite3_mutex_enter(p->db->mutex);
|
||||
/* The race condition here is harmless. If two threads call this
|
||||
** routine on the same Vdbe at the same time, they both might end
|
||||
** up initializing the Vdbe.azVar[] array. That is a little extra
|
||||
** work but it results in the same answer.
|
||||
*/
|
||||
for(j=0, pOp=p->aOp; j<p->nOp; j++, pOp++){
|
||||
if( pOp->opcode==OP_Variable ){
|
||||
assert( pOp->p1>0 && pOp->p1<=p->nVar );
|
||||
p->azVar[pOp->p1-1] = pOp->p4.z;
|
||||
}
|
||||
}
|
||||
p->okVar = 1;
|
||||
sqlite3_mutex_leave(p->db->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the name of a wildcard parameter. Return NULL if the index
|
||||
** is out of range or if the wildcard is unnamed.
|
||||
@ -1201,10 +1183,9 @@ static void createVarMap(Vdbe *p){
|
||||
*/
|
||||
const char *sqlite3_bind_parameter_name(sqlite3_stmt *pStmt, int i){
|
||||
Vdbe *p = (Vdbe*)pStmt;
|
||||
if( p==0 || i<1 || i>p->nVar ){
|
||||
if( p==0 || i<1 || i>p->nzVar ){
|
||||
return 0;
|
||||
}
|
||||
createVarMap(p);
|
||||
return p->azVar[i-1];
|
||||
}
|
||||
|
||||
@ -1218,9 +1199,8 @@ int sqlite3VdbeParameterIndex(Vdbe *p, const char *zName, int nName){
|
||||
if( p==0 ){
|
||||
return 0;
|
||||
}
|
||||
createVarMap(p);
|
||||
if( zName ){
|
||||
for(i=0; i<p->nVar; i++){
|
||||
for(i=0; i<p->nzVar; i++){
|
||||
const char *z = p->azVar[i];
|
||||
if( z && memcmp(z,zName,nName)==0 && z[nName]==0 ){
|
||||
return i+1;
|
||||
|
||||
275
src/vdbeaux.c
275
src/vdbeaux.c
@ -156,13 +156,6 @@ int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){
|
||||
pOp->p3 = p3;
|
||||
pOp->p4.p = 0;
|
||||
pOp->p4type = P4_NOTUSED;
|
||||
p->expired = 0;
|
||||
if( op==OP_ParseSchema ){
|
||||
/* Any program that uses the OP_ParseSchema opcode needs to lock
|
||||
** all btrees. */
|
||||
int j;
|
||||
for(j=0; j<p->db->nDb; j++) sqlite3VdbeUsesBtree(p, j);
|
||||
}
|
||||
#ifdef SQLITE_DEBUG
|
||||
pOp->zComment = 0;
|
||||
if( sqlite3VdbeAddopTrace ) sqlite3VdbePrintOp(0, i, &p->aOp[i]);
|
||||
@ -201,6 +194,20 @@ int sqlite3VdbeAddOp4(
|
||||
return addr;
|
||||
}
|
||||
|
||||
/*
|
||||
** Add an OP_ParseSchema opcode. This routine is broken out from
|
||||
** sqlite3VdbeAddOp4() since it needs to also local all btrees.
|
||||
**
|
||||
** The zWhere string must have been obtained from sqlite3_malloc().
|
||||
** This routine will take ownership of the allocated memory.
|
||||
*/
|
||||
void sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere){
|
||||
int j;
|
||||
int addr = sqlite3VdbeAddOp3(p, OP_ParseSchema, iDb, 0, 0);
|
||||
sqlite3VdbeChangeP4(p, addr, zWhere, P4_DYNAMIC);
|
||||
for(j=0; j<p->db->nDb; j++) sqlite3VdbeUsesBtree(p, j);
|
||||
}
|
||||
|
||||
/*
|
||||
** Add an opcode that includes the p4 value as an integer.
|
||||
*/
|
||||
@ -1391,34 +1398,13 @@ static void *allocSpace(
|
||||
}
|
||||
|
||||
/*
|
||||
** Prepare a virtual machine for execution. This involves things such
|
||||
** as allocating stack space and initializing the program counter.
|
||||
** After the VDBE has be prepped, it can be executed by one or more
|
||||
** calls to sqlite3VdbeExec().
|
||||
**
|
||||
** This is the only way to move a VDBE from VDBE_MAGIC_INIT to
|
||||
** VDBE_MAGIC_RUN.
|
||||
**
|
||||
** This function may be called more than once on a single virtual machine.
|
||||
** The first call is made while compiling the SQL statement. Subsequent
|
||||
** calls are made as part of the process of resetting a statement to be
|
||||
** re-executed (from a call to sqlite3_reset()). The nVar, nMem, nCursor
|
||||
** and isExplain parameters are only passed correct values the first time
|
||||
** the function is called. On subsequent calls, from sqlite3_reset(), nVar
|
||||
** is passed -1 and nMem, nCursor and isExplain are all passed zero.
|
||||
** Rewind the VDBE back to the beginning in preparation for
|
||||
** running it.
|
||||
*/
|
||||
void sqlite3VdbeMakeReady(
|
||||
Vdbe *p, /* The VDBE */
|
||||
int nVar, /* Number of '?' see in the SQL statement */
|
||||
int nMem, /* Number of memory cells to allocate */
|
||||
int nCursor, /* Number of cursors to allocate */
|
||||
int nArg, /* Maximum number of args in SubPrograms */
|
||||
int isExplain, /* True if the EXPLAIN keywords is present */
|
||||
int usesStmtJournal /* True to set Vdbe.usesStmtJournal */
|
||||
){
|
||||
int n;
|
||||
sqlite3 *db = p->db;
|
||||
|
||||
void sqlite3VdbeRewind(Vdbe *p){
|
||||
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
|
||||
int i;
|
||||
#endif
|
||||
assert( p!=0 );
|
||||
assert( p->magic==VDBE_MAGIC_INIT );
|
||||
|
||||
@ -1429,6 +1415,71 @@ void sqlite3VdbeMakeReady(
|
||||
/* Set the magic to VDBE_MAGIC_RUN sooner rather than later. */
|
||||
p->magic = VDBE_MAGIC_RUN;
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
for(i=1; i<p->nMem; i++){
|
||||
assert( p->aMem[i].db==p->db );
|
||||
}
|
||||
#endif
|
||||
p->pc = -1;
|
||||
p->rc = SQLITE_OK;
|
||||
p->errorAction = OE_Abort;
|
||||
p->magic = VDBE_MAGIC_RUN;
|
||||
p->nChange = 0;
|
||||
p->cacheCtr = 1;
|
||||
p->minWriteFileFormat = 255;
|
||||
p->iStatement = 0;
|
||||
p->nFkConstraint = 0;
|
||||
#ifdef VDBE_PROFILE
|
||||
for(i=0; i<p->nOp; i++){
|
||||
p->aOp[i].cnt = 0;
|
||||
p->aOp[i].cycles = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Prepare a virtual machine for execution for the first time after
|
||||
** creating the virtual machine. This involves things such
|
||||
** as allocating stack space and initializing the program counter.
|
||||
** After the VDBE has be prepped, it can be executed by one or more
|
||||
** calls to sqlite3VdbeExec().
|
||||
**
|
||||
** This function may be called exact once on a each virtual machine.
|
||||
** After this routine is called the VM has been "packaged" and is ready
|
||||
** to run. After this routine is called, futher calls to
|
||||
** sqlite3VdbeAddOp() functions are prohibited. This routine disconnects
|
||||
** the Vdbe from the Parse object that helped generate it so that the
|
||||
** the Vdbe becomes an independent entity and the Parse object can be
|
||||
** destroyed.
|
||||
**
|
||||
** Use the sqlite3VdbeRewind() procedure to restore a virtual machine back
|
||||
** to its initial state after it has been run.
|
||||
*/
|
||||
void sqlite3VdbeMakeReady(
|
||||
Vdbe *p, /* The VDBE */
|
||||
Parse *pParse /* Parsing context */
|
||||
){
|
||||
sqlite3 *db; /* The database connection */
|
||||
int nVar; /* Number of parameters */
|
||||
int nMem; /* Number of VM memory registers */
|
||||
int nCursor; /* Number of cursors required */
|
||||
int nArg; /* Number of arguments in subprograms */
|
||||
int n; /* Loop counter */
|
||||
u8 *zCsr; /* Memory available for allocation */
|
||||
u8 *zEnd; /* First byte past allocated memory */
|
||||
int nByte; /* How much extra memory is needed */
|
||||
|
||||
assert( p!=0 );
|
||||
assert( p->nOp>0 );
|
||||
assert( pParse!=0 );
|
||||
assert( p->magic==VDBE_MAGIC_INIT );
|
||||
db = p->db;
|
||||
assert( db->mallocFailed==0 );
|
||||
nVar = pParse->nVar;
|
||||
nMem = pParse->nMem;
|
||||
nCursor = pParse->nTab;
|
||||
nArg = pParse->nMaxArg;
|
||||
|
||||
/* For each cursor required, also allocate a memory cell. Memory
|
||||
** cells (nMem+1-nCursor)..nMem, inclusive, will never be used by
|
||||
** the vdbe program. Instead they are used to allocate space for
|
||||
@ -1441,91 +1492,69 @@ void sqlite3VdbeMakeReady(
|
||||
nMem += nCursor;
|
||||
|
||||
/* Allocate space for memory registers, SQL variables, VDBE cursors and
|
||||
** an array to marshal SQL function arguments in. This is only done the
|
||||
** first time this function is called for a given VDBE, not when it is
|
||||
** being called from sqlite3_reset() to reset the virtual machine.
|
||||
** an array to marshal SQL function arguments in.
|
||||
*/
|
||||
if( nVar>=0 && ALWAYS(db->mallocFailed==0) ){
|
||||
u8 *zCsr = (u8 *)&p->aOp[p->nOp]; /* Memory avaliable for alloation */
|
||||
u8 *zEnd = (u8 *)&p->aOp[p->nOpAlloc]; /* First byte past available mem */
|
||||
int nByte; /* How much extra memory needed */
|
||||
zCsr = (u8*)&p->aOp[p->nOp]; /* Memory avaliable for allocation */
|
||||
zEnd = (u8*)&p->aOp[p->nOpAlloc]; /* First byte past end of zCsr[] */
|
||||
|
||||
resolveP2Values(p, &nArg);
|
||||
p->usesStmtJournal = (u8)usesStmtJournal;
|
||||
if( isExplain && nMem<10 ){
|
||||
nMem = 10;
|
||||
resolveP2Values(p, &nArg);
|
||||
p->usesStmtJournal = (u8)(pParse->isMultiWrite && pParse->mayAbort);
|
||||
if( pParse->explain && nMem<10 ){
|
||||
nMem = 10;
|
||||
}
|
||||
memset(zCsr, 0, zEnd-zCsr);
|
||||
zCsr += (zCsr - (u8*)0)&7;
|
||||
assert( EIGHT_BYTE_ALIGNMENT(zCsr) );
|
||||
p->expired = 0;
|
||||
|
||||
/* Memory for registers, parameters, cursor, etc, is allocated in two
|
||||
** passes. On the first pass, we try to reuse unused space at the
|
||||
** end of the opcode array. If we are unable to satisfy all memory
|
||||
** requirements by reusing the opcode array tail, then the second
|
||||
** pass will fill in the rest using a fresh allocation.
|
||||
**
|
||||
** This two-pass approach that reuses as much memory as possible from
|
||||
** the leftover space at the end of the opcode array can significantly
|
||||
** reduce the amount of memory held by a prepared statement.
|
||||
*/
|
||||
do {
|
||||
nByte = 0;
|
||||
p->aMem = allocSpace(p->aMem, nMem*sizeof(Mem), &zCsr, zEnd, &nByte);
|
||||
p->aVar = allocSpace(p->aVar, nVar*sizeof(Mem), &zCsr, zEnd, &nByte);
|
||||
p->apArg = allocSpace(p->apArg, nArg*sizeof(Mem*), &zCsr, zEnd, &nByte);
|
||||
p->azVar = allocSpace(p->azVar, nVar*sizeof(char*), &zCsr, zEnd, &nByte);
|
||||
p->apCsr = allocSpace(p->apCsr, nCursor*sizeof(VdbeCursor*),
|
||||
&zCsr, zEnd, &nByte);
|
||||
if( nByte ){
|
||||
p->pFree = sqlite3DbMallocZero(db, nByte);
|
||||
}
|
||||
memset(zCsr, 0, zEnd-zCsr);
|
||||
zCsr += (zCsr - (u8*)0)&7;
|
||||
assert( EIGHT_BYTE_ALIGNMENT(zCsr) );
|
||||
zCsr = p->pFree;
|
||||
zEnd = &zCsr[nByte];
|
||||
}while( nByte && !db->mallocFailed );
|
||||
|
||||
/* Memory for registers, parameters, cursor, etc, is allocated in two
|
||||
** passes. On the first pass, we try to reuse unused space at the
|
||||
** end of the opcode array. If we are unable to satisfy all memory
|
||||
** requirements by reusing the opcode array tail, then the second
|
||||
** pass will fill in the rest using a fresh allocation.
|
||||
**
|
||||
** This two-pass approach that reuses as much memory as possible from
|
||||
** the leftover space at the end of the opcode array can significantly
|
||||
** reduce the amount of memory held by a prepared statement.
|
||||
*/
|
||||
do {
|
||||
nByte = 0;
|
||||
p->aMem = allocSpace(p->aMem, nMem*sizeof(Mem), &zCsr, zEnd, &nByte);
|
||||
p->aVar = allocSpace(p->aVar, nVar*sizeof(Mem), &zCsr, zEnd, &nByte);
|
||||
p->apArg = allocSpace(p->apArg, nArg*sizeof(Mem*), &zCsr, zEnd, &nByte);
|
||||
p->azVar = allocSpace(p->azVar, nVar*sizeof(char*), &zCsr, zEnd, &nByte);
|
||||
p->apCsr = allocSpace(p->apCsr, nCursor*sizeof(VdbeCursor*),
|
||||
&zCsr, zEnd, &nByte);
|
||||
if( nByte ){
|
||||
p->pFree = sqlite3DbMallocZero(db, nByte);
|
||||
}
|
||||
zCsr = p->pFree;
|
||||
zEnd = &zCsr[nByte];
|
||||
}while( nByte && !db->mallocFailed );
|
||||
|
||||
p->nCursor = (u16)nCursor;
|
||||
if( p->aVar ){
|
||||
p->nVar = (ynVar)nVar;
|
||||
for(n=0; n<nVar; n++){
|
||||
p->aVar[n].flags = MEM_Null;
|
||||
p->aVar[n].db = db;
|
||||
}
|
||||
}
|
||||
if( p->aMem ){
|
||||
p->aMem--; /* aMem[] goes from 1..nMem */
|
||||
p->nMem = nMem; /* not from 0..nMem-1 */
|
||||
for(n=1; n<=nMem; n++){
|
||||
p->aMem[n].flags = MEM_Null;
|
||||
p->aMem[n].db = db;
|
||||
}
|
||||
p->nCursor = (u16)nCursor;
|
||||
if( p->aVar ){
|
||||
p->nVar = (ynVar)nVar;
|
||||
for(n=0; n<nVar; n++){
|
||||
p->aVar[n].flags = MEM_Null;
|
||||
p->aVar[n].db = db;
|
||||
}
|
||||
}
|
||||
#ifdef SQLITE_DEBUG
|
||||
for(n=1; n<p->nMem; n++){
|
||||
assert( p->aMem[n].db==db );
|
||||
if( p->azVar ){
|
||||
p->nzVar = pParse->nzVar;
|
||||
memcpy(p->azVar, pParse->azVar, p->nzVar*sizeof(p->azVar[0]));
|
||||
memset(pParse->azVar, 0, pParse->nzVar*sizeof(pParse->azVar[0]));
|
||||
}
|
||||
#endif
|
||||
|
||||
p->pc = -1;
|
||||
p->rc = SQLITE_OK;
|
||||
p->errorAction = OE_Abort;
|
||||
p->explain |= isExplain;
|
||||
p->magic = VDBE_MAGIC_RUN;
|
||||
p->nChange = 0;
|
||||
p->cacheCtr = 1;
|
||||
p->minWriteFileFormat = 255;
|
||||
p->iStatement = 0;
|
||||
p->nFkConstraint = 0;
|
||||
#ifdef VDBE_PROFILE
|
||||
{
|
||||
int i;
|
||||
for(i=0; i<p->nOp; i++){
|
||||
p->aOp[i].cnt = 0;
|
||||
p->aOp[i].cycles = 0;
|
||||
if( p->aMem ){
|
||||
p->aMem--; /* aMem[] goes from 1..nMem */
|
||||
p->nMem = nMem; /* not from 0..nMem-1 */
|
||||
for(n=1; n<=nMem; n++){
|
||||
p->aMem[n].flags = MEM_Null;
|
||||
p->aMem[n].db = db;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
p->explain = pParse->explain;
|
||||
sqlite3VdbeRewind(p);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1799,6 +1828,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
|
||||
if( !zMaster ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
sqlite3FileSuffix3(zMainFile, zMaster);
|
||||
rc = sqlite3OsAccess(pVfs, zMaster, SQLITE_ACCESS_EXISTS, &res);
|
||||
}while( rc==SQLITE_OK && res );
|
||||
if( rc==SQLITE_OK ){
|
||||
@ -2013,6 +2043,15 @@ int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){
|
||||
db->nStatement--;
|
||||
p->iStatement = 0;
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
if( eOp==SAVEPOINT_ROLLBACK ){
|
||||
rc = sqlite3VtabSavepoint(db, SAVEPOINT_ROLLBACK, iSavepoint);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3VtabSavepoint(db, SAVEPOINT_RELEASE, iSavepoint);
|
||||
}
|
||||
}
|
||||
|
||||
/* If the statement transaction is being rolled back, also restore the
|
||||
** database handles deferred constraint counter to the value it had when
|
||||
** the statement transaction was opened. */
|
||||
@ -2192,17 +2231,11 @@ int sqlite3VdbeHalt(Vdbe *p){
|
||||
** do so. If this operation returns an error, and the current statement
|
||||
** error code is SQLITE_OK or SQLITE_CONSTRAINT, then promote the
|
||||
** current statement error code.
|
||||
**
|
||||
** Note that sqlite3VdbeCloseStatement() can only fail if eStatementOp
|
||||
** is SAVEPOINT_ROLLBACK. But if p->rc==SQLITE_OK then eStatementOp
|
||||
** must be SAVEPOINT_RELEASE. Hence the NEVER(p->rc==SQLITE_OK) in
|
||||
** the following code.
|
||||
*/
|
||||
if( eStatementOp ){
|
||||
rc = sqlite3VdbeCloseStatement(p, eStatementOp);
|
||||
if( rc ){
|
||||
assert( eStatementOp==SAVEPOINT_ROLLBACK );
|
||||
if( NEVER(p->rc==SQLITE_OK) || p->rc==SQLITE_CONSTRAINT ){
|
||||
if( p->rc==SQLITE_OK || p->rc==SQLITE_CONSTRAINT ){
|
||||
p->rc = rc;
|
||||
sqlite3DbFree(db, p->zErrMsg);
|
||||
p->zErrMsg = 0;
|
||||
@ -2395,6 +2428,7 @@ void sqlite3VdbeDeleteAuxData(VdbeFunc *pVdbeFunc, int mask){
|
||||
*/
|
||||
void sqlite3VdbeDeleteObject(sqlite3 *db, Vdbe *p){
|
||||
SubProgram *pSub, *pNext;
|
||||
int i;
|
||||
assert( p->db==0 || p->db==db );
|
||||
releaseMemArray(p->aVar, p->nVar);
|
||||
releaseMemArray(p->aColName, p->nResColumn*COLNAME_N);
|
||||
@ -2403,6 +2437,7 @@ void sqlite3VdbeDeleteObject(sqlite3 *db, Vdbe *p){
|
||||
vdbeFreeOpArray(db, pSub->aOp, pSub->nOp);
|
||||
sqlite3DbFree(db, pSub);
|
||||
}
|
||||
for(i=p->nzVar-1; i>=0; i--) sqlite3DbFree(db, p->azVar[i]);
|
||||
vdbeFreeOpArray(db, p->aOp, p->nOp);
|
||||
sqlite3DbFree(db, p->aLabel);
|
||||
sqlite3DbFree(db, p->aColName);
|
||||
@ -2848,7 +2883,7 @@ UnpackedRecord *sqlite3VdbeRecordUnpack(
|
||||
idx += getVarint32(&aKey[idx], serial_type);
|
||||
pMem->enc = pKeyInfo->enc;
|
||||
pMem->db = pKeyInfo->db;
|
||||
pMem->flags = 0;
|
||||
/* pMem->flags = 0; // sqlite3VdbeSerialGet() will set this for us */
|
||||
pMem->zMalloc = 0;
|
||||
d += sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem);
|
||||
pMem++;
|
||||
@ -2863,6 +2898,7 @@ UnpackedRecord *sqlite3VdbeRecordUnpack(
|
||||
** This routine destroys a UnpackedRecord object.
|
||||
*/
|
||||
void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord *p){
|
||||
#ifdef SQLITE_DEBUG
|
||||
int i;
|
||||
Mem *pMem;
|
||||
|
||||
@ -2876,6 +2912,7 @@ void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord *p){
|
||||
*/
|
||||
if( NEVER(pMem->zMalloc) ) sqlite3VdbeMemRelease(pMem);
|
||||
}
|
||||
#endif
|
||||
if( p->flags & UNPACKED_NEED_FREE ){
|
||||
sqlite3DbFree(p->pKeyInfo->db, p);
|
||||
}
|
||||
@ -2929,7 +2966,7 @@ int sqlite3VdbeRecordCompare(
|
||||
|
||||
/* Compilers may complain that mem1.u.i is potentially uninitialized.
|
||||
** We could initialize it, as shown here, to silence those complaints.
|
||||
** But in fact, mem1.u.i will never actually be used initialized, and doing
|
||||
** But in fact, mem1.u.i will never actually be used uninitialized, and doing
|
||||
** the unnecessary initialization has a measurable negative performance
|
||||
** impact, since this routine is a very high runner. And so, we choose
|
||||
** to ignore the compiler warnings and leave this variable uninitialized.
|
||||
|
||||
@ -297,7 +297,10 @@ int sqlite3_blob_open(
|
||||
sqlite3VdbeChangeP4(v, 3+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32);
|
||||
sqlite3VdbeChangeP2(v, 7, pTab->nCol);
|
||||
if( !db->mallocFailed ){
|
||||
sqlite3VdbeMakeReady(v, 1, 1, 1, 0, 0, 0);
|
||||
pParse->nVar = 1;
|
||||
pParse->nMem = 1;
|
||||
pParse->nTab = 1;
|
||||
sqlite3VdbeMakeReady(v, pParse);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
166
src/vtab.c
166
src/vtab.c
@ -14,6 +14,18 @@
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** Before a virtual table xCreate() or xConnect() method is invoked, the
|
||||
** sqlite3.pVtabCtx member variable is set to point to an instance of
|
||||
** this struct allocated on the stack. It is used by the implementation of
|
||||
** the sqlite3_declare_vtab() and sqlite3_vtab_config() APIs, both of which
|
||||
** are invoked only from within xCreate and xConnect methods.
|
||||
*/
|
||||
struct VtabCtx {
|
||||
Table *pTab;
|
||||
VTable *pVTable;
|
||||
};
|
||||
|
||||
/*
|
||||
** The actual function that does the work of creating a new module.
|
||||
** This function implements the sqlite3_create_module() and
|
||||
@ -42,13 +54,13 @@ static int createModule(
|
||||
pMod->xDestroy = xDestroy;
|
||||
pDel = (Module *)sqlite3HashInsert(&db->aModule, zCopy, nName, (void*)pMod);
|
||||
if( pDel && pDel->xDestroy ){
|
||||
sqlite3ResetInternalSchema(db, -1);
|
||||
pDel->xDestroy(pDel->pAux);
|
||||
}
|
||||
sqlite3DbFree(db, pDel);
|
||||
if( pDel==pMod ){
|
||||
db->mallocFailed = 1;
|
||||
}
|
||||
sqlite3ResetInternalSchema(db, -1);
|
||||
}else if( xDestroy ){
|
||||
xDestroy(pAux);
|
||||
}
|
||||
@ -371,7 +383,7 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
|
||||
|
||||
sqlite3VdbeAddOp2(v, OP_Expire, 0, 0);
|
||||
zWhere = sqlite3MPrintf(db, "name='%q' AND type='table'", pTab->zName);
|
||||
sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0, zWhere, P4_DYNAMIC);
|
||||
sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere);
|
||||
sqlite3VdbeAddOp4(v, OP_VCreate, iDb, 0, 0,
|
||||
pTab->zName, sqlite3Strlen30(pTab->zName) + 1);
|
||||
}
|
||||
@ -434,6 +446,7 @@ static int vtabCallConstructor(
|
||||
int (*xConstruct)(sqlite3*,void*,int,const char*const*,sqlite3_vtab**,char**),
|
||||
char **pzErr
|
||||
){
|
||||
VtabCtx sCtx;
|
||||
VTable *pVTable;
|
||||
int rc;
|
||||
const char *const*azArg = (const char *const*)pTab->azModuleArg;
|
||||
@ -453,12 +466,14 @@ static int vtabCallConstructor(
|
||||
pVTable->db = db;
|
||||
pVTable->pMod = pMod;
|
||||
|
||||
assert( !db->pVTab );
|
||||
assert( xConstruct );
|
||||
db->pVTab = pTab;
|
||||
|
||||
/* Invoke the virtual table constructor */
|
||||
assert( &db->pVtabCtx );
|
||||
assert( xConstruct );
|
||||
sCtx.pTab = pTab;
|
||||
sCtx.pVTable = pVTable;
|
||||
db->pVtabCtx = &sCtx;
|
||||
rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr);
|
||||
db->pVtabCtx = 0;
|
||||
if( rc==SQLITE_NOMEM ) db->mallocFailed = 1;
|
||||
|
||||
if( SQLITE_OK!=rc ){
|
||||
@ -474,7 +489,7 @@ static int vtabCallConstructor(
|
||||
** the sqlite3_vtab object if successful. */
|
||||
pVTable->pVtab->pModule = pMod->pModule;
|
||||
pVTable->nRef = 1;
|
||||
if( db->pVTab ){
|
||||
if( sCtx.pTab ){
|
||||
const char *zFormat = "vtable constructor did not declare schema: %s";
|
||||
*pzErr = sqlite3MPrintf(db, zFormat, pTab->zName);
|
||||
sqlite3VtabUnlock(pVTable);
|
||||
@ -522,7 +537,6 @@ static int vtabCallConstructor(
|
||||
}
|
||||
|
||||
sqlite3DbFree(db, zModuleName);
|
||||
db->pVTab = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -563,11 +577,11 @@ int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Add the virtual table pVTab to the array sqlite3.aVTrans[].
|
||||
** Grow the db->aVTrans[] array so that there is room for at least one
|
||||
** more v-table. Return SQLITE_NOMEM if a malloc fails, or SQLITE_OK otherwise.
|
||||
*/
|
||||
static int addToVTrans(sqlite3 *db, VTable *pVTab){
|
||||
static int growVTrans(sqlite3 *db){
|
||||
const int ARRAY_INCR = 5;
|
||||
|
||||
/* Grow the sqlite3.aVTrans array if required */
|
||||
@ -582,10 +596,17 @@ static int addToVTrans(sqlite3 *db, VTable *pVTab){
|
||||
db->aVTrans = aVTrans;
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Add the virtual table pVTab to the array sqlite3.aVTrans[]. Space should
|
||||
** have already been reserved using growVTrans().
|
||||
*/
|
||||
static void addToVTrans(sqlite3 *db, VTable *pVTab){
|
||||
/* Add pVtab to the end of sqlite3.aVTrans */
|
||||
db->aVTrans[db->nVTrans++] = pVTab;
|
||||
sqlite3VtabLock(pVTab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -623,7 +644,10 @@ int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, char **pzErr){
|
||||
/* Justification of ALWAYS(): The xConstructor method is required to
|
||||
** create a valid sqlite3_vtab if it returns SQLITE_OK. */
|
||||
if( rc==SQLITE_OK && ALWAYS(sqlite3GetVTable(db, pTab)) ){
|
||||
rc = addToVTrans(db, sqlite3GetVTable(db, pTab));
|
||||
rc = growVTrans(db);
|
||||
if( rc==SQLITE_OK ){
|
||||
addToVTrans(db, sqlite3GetVTable(db, pTab));
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
@ -642,8 +666,7 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
|
||||
char *zErr = 0;
|
||||
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
pTab = db->pVTab;
|
||||
if( !pTab ){
|
||||
if( !db->pVtabCtx || !(pTab = db->pVtabCtx->pTab) ){
|
||||
sqlite3Error(db, SQLITE_MISUSE, 0);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
@ -670,7 +693,7 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
|
||||
pParse->pNewTable->nCol = 0;
|
||||
pParse->pNewTable->aCol = 0;
|
||||
}
|
||||
db->pVTab = 0;
|
||||
db->pVtabCtx->pTab = 0;
|
||||
}else{
|
||||
sqlite3Error(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr);
|
||||
sqlite3DbFree(db, zErr);
|
||||
@ -740,6 +763,7 @@ static void callFinaliser(sqlite3 *db, int offset){
|
||||
x = *(int (**)(sqlite3_vtab *))((char *)p->pModule + offset);
|
||||
if( x ) x(p);
|
||||
}
|
||||
pVTab->iSavepoint = 0;
|
||||
sqlite3VtabUnlock(pVTab);
|
||||
}
|
||||
sqlite3DbFree(db, db->aVTrans);
|
||||
@ -822,7 +846,6 @@ int sqlite3VtabBegin(sqlite3 *db, VTable *pVTab){
|
||||
if( pModule->xBegin ){
|
||||
int i;
|
||||
|
||||
|
||||
/* If pVtab is already in the aVTrans array, return early */
|
||||
for(i=0; i<db->nVTrans; i++){
|
||||
if( db->aVTrans[i]==pVTab ){
|
||||
@ -830,10 +853,62 @@ int sqlite3VtabBegin(sqlite3 *db, VTable *pVTab){
|
||||
}
|
||||
}
|
||||
|
||||
/* Invoke the xBegin method */
|
||||
rc = pModule->xBegin(pVTab->pVtab);
|
||||
/* Invoke the xBegin method. If successful, add the vtab to the
|
||||
** sqlite3.aVTrans[] array. */
|
||||
rc = growVTrans(db);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = addToVTrans(db, pVTab);
|
||||
rc = pModule->xBegin(pVTab->pVtab);
|
||||
if( rc==SQLITE_OK ){
|
||||
addToVTrans(db, pVTab);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Invoke either the xSavepoint, xRollbackTo or xRelease method of all
|
||||
** virtual tables that currently have an open transaction. Pass iSavepoint
|
||||
** as the second argument to the virtual table method invoked.
|
||||
**
|
||||
** If op is SAVEPOINT_BEGIN, the xSavepoint method is invoked. If it is
|
||||
** SAVEPOINT_ROLLBACK, the xRollbackTo method. Otherwise, if op is
|
||||
** SAVEPOINT_RELEASE, then the xRelease method of each virtual table with
|
||||
** an open transaction is invoked.
|
||||
**
|
||||
** If any virtual table method returns an error code other than SQLITE_OK,
|
||||
** processing is abandoned and the error returned to the caller of this
|
||||
** function immediately. If all calls to virtual table methods are successful,
|
||||
** SQLITE_OK is returned.
|
||||
*/
|
||||
int sqlite3VtabSavepoint(sqlite3 *db, int op, int iSavepoint){
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
assert( op==SAVEPOINT_RELEASE||op==SAVEPOINT_ROLLBACK||op==SAVEPOINT_BEGIN );
|
||||
assert( iSavepoint>=0 );
|
||||
if( db->aVTrans ){
|
||||
int i;
|
||||
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 ){
|
||||
int (*xMethod)(sqlite3_vtab *, int);
|
||||
switch( op ){
|
||||
case SAVEPOINT_BEGIN:
|
||||
xMethod = pMod->xSavepoint;
|
||||
pVTab->iSavepoint = iSavepoint+1;
|
||||
break;
|
||||
case SAVEPOINT_ROLLBACK:
|
||||
xMethod = pMod->xRollbackTo;
|
||||
break;
|
||||
default:
|
||||
xMethod = pMod->xRelease;
|
||||
break;
|
||||
}
|
||||
if( xMethod && pVTab->iSavepoint>iSavepoint ){
|
||||
rc = xMethod(db->aVTrans[i]->pVtab, iSavepoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
@ -937,4 +1012,55 @@ void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the ON CONFLICT resolution mode in effect for the virtual
|
||||
** table update operation currently in progress.
|
||||
**
|
||||
** The results of this routine are undefined unless it is called from
|
||||
** within an xUpdate method.
|
||||
*/
|
||||
int sqlite3_vtab_on_conflict(sqlite3 *db){
|
||||
static const unsigned char aMap[] = {
|
||||
SQLITE_ROLLBACK, SQLITE_ABORT, SQLITE_FAIL, SQLITE_IGNORE, SQLITE_REPLACE
|
||||
};
|
||||
assert( OE_Rollback==1 && OE_Abort==2 && OE_Fail==3 );
|
||||
assert( OE_Ignore==4 && OE_Replace==5 );
|
||||
assert( db->vtabOnConflict>=1 && db->vtabOnConflict<=5 );
|
||||
return (int)aMap[db->vtabOnConflict-1];
|
||||
}
|
||||
|
||||
/*
|
||||
** Call from within the xCreate() or xConnect() methods to provide
|
||||
** the SQLite core with additional information about the behavior
|
||||
** of the virtual table being implemented.
|
||||
*/
|
||||
int sqlite3_vtab_config(sqlite3 *db, int op, ...){
|
||||
va_list ap;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
|
||||
va_start(ap, op);
|
||||
switch( op ){
|
||||
case SQLITE_VTAB_CONSTRAINT_SUPPORT: {
|
||||
VtabCtx *p = db->pVtabCtx;
|
||||
if( !p ){
|
||||
rc = SQLITE_MISUSE_BKPT;
|
||||
}else{
|
||||
assert( p->pTab==0 || (p->pTab->tabFlags & TF_Virtual)!=0 );
|
||||
p->pVTable->bConstraint = (u8)va_arg(ap, int);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
rc = SQLITE_MISUSE_BKPT;
|
||||
break;
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
if( rc!=SQLITE_OK ) sqlite3Error(db, rc, 0);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
85
src/wal.c
85
src/wal.c
@ -412,6 +412,7 @@ struct Wal {
|
||||
sqlite3_file *pDbFd; /* File handle for the database file */
|
||||
sqlite3_file *pWalFd; /* File handle for WAL file */
|
||||
u32 iCallback; /* Value to pass to log callback (or 0) */
|
||||
i64 mxWalSize; /* Truncate WAL to this size upon reset */
|
||||
int nWiData; /* Size of array apWiData */
|
||||
volatile u32 **apWiData; /* Pointer to wal-index content in memory */
|
||||
u32 szPage; /* Database page size */
|
||||
@ -419,7 +420,7 @@ struct Wal {
|
||||
u8 exclusiveMode; /* Non-zero if connection is in exclusive mode */
|
||||
u8 writeLock; /* True if in a write transaction */
|
||||
u8 ckptLock; /* True if holding a checkpoint lock */
|
||||
u8 readOnly; /* True if the WAL file is open read-only */
|
||||
u8 readOnly; /* WAL_RDWR, WAL_RDONLY, or WAL_SHM_RDONLY */
|
||||
WalIndexHdr hdr; /* Wal-index header for current transaction */
|
||||
const char *zWalName; /* Name of WAL file */
|
||||
u32 nCkpt; /* Checkpoint sequence counter in the wal-header */
|
||||
@ -435,6 +436,13 @@ struct Wal {
|
||||
#define WAL_EXCLUSIVE_MODE 1
|
||||
#define WAL_HEAPMEMORY_MODE 2
|
||||
|
||||
/*
|
||||
** Possible values for WAL.readOnly
|
||||
*/
|
||||
#define WAL_RDWR 0 /* Normal read/write connection */
|
||||
#define WAL_RDONLY 1 /* The WAL file is readonly */
|
||||
#define WAL_SHM_RDONLY 2 /* The SHM file is readonly */
|
||||
|
||||
/*
|
||||
** Each page of the wal-index mapping contains a hash-table made up of
|
||||
** an array of HASHTABLE_NSLOT elements of the following type.
|
||||
@ -528,6 +536,10 @@ static int walIndexPage(Wal *pWal, int iPage, volatile u32 **ppPage){
|
||||
rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ,
|
||||
pWal->writeLock, (void volatile **)&pWal->apWiData[iPage]
|
||||
);
|
||||
if( rc==SQLITE_READONLY ){
|
||||
pWal->readOnly |= WAL_SHM_RDONLY;
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1234,6 +1246,7 @@ int sqlite3WalOpen(
|
||||
sqlite3_file *pDbFd, /* The open database file */
|
||||
const char *zWalName, /* Name of the WAL file */
|
||||
int bNoShm, /* True to run in heap-memory mode */
|
||||
i64 mxWalSize, /* Truncate WAL to this size on reset */
|
||||
Wal **ppWal /* OUT: Allocated Wal handle */
|
||||
){
|
||||
int rc; /* Return Code */
|
||||
@ -1266,6 +1279,7 @@ int sqlite3WalOpen(
|
||||
pRet->pWalFd = (sqlite3_file *)&pRet[1];
|
||||
pRet->pDbFd = pDbFd;
|
||||
pRet->readLock = -1;
|
||||
pRet->mxWalSize = mxWalSize;
|
||||
pRet->zWalName = zWalName;
|
||||
pRet->exclusiveMode = (bNoShm ? WAL_HEAPMEMORY_MODE: WAL_NORMAL_MODE);
|
||||
|
||||
@ -1273,7 +1287,7 @@ int sqlite3WalOpen(
|
||||
flags = (SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_WAL);
|
||||
rc = sqlite3OsOpen(pVfs, zWalName, pRet->pWalFd, flags, &flags);
|
||||
if( rc==SQLITE_OK && flags&SQLITE_OPEN_READONLY ){
|
||||
pRet->readOnly = 1;
|
||||
pRet->readOnly = WAL_RDONLY;
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
@ -1287,6 +1301,13 @@ int sqlite3WalOpen(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Change the size to which the WAL file is trucated on each reset.
|
||||
*/
|
||||
void sqlite3WalLimit(Wal *pWal, i64 iLimit){
|
||||
if( pWal ) pWal->mxWalSize = iLimit;
|
||||
}
|
||||
|
||||
/*
|
||||
** Find the smallest page number out of all pages held in the WAL that
|
||||
** has not been returned by any prior invocation of this method on the
|
||||
@ -1907,21 +1928,28 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){
|
||||
** with a writer. So get a WRITE lock and try again.
|
||||
*/
|
||||
assert( badHdr==0 || pWal->writeLock==0 );
|
||||
if( badHdr && SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) ){
|
||||
pWal->writeLock = 1;
|
||||
if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){
|
||||
badHdr = walIndexTryHdr(pWal, pChanged);
|
||||
if( badHdr ){
|
||||
/* If the wal-index header is still malformed even while holding
|
||||
** a WRITE lock, it can only mean that the header is corrupted and
|
||||
** needs to be reconstructed. So run recovery to do exactly that.
|
||||
*/
|
||||
rc = walIndexRecover(pWal);
|
||||
*pChanged = 1;
|
||||
if( badHdr ){
|
||||
if( pWal->readOnly & WAL_SHM_RDONLY ){
|
||||
if( SQLITE_OK==(rc = walLockShared(pWal, WAL_WRITE_LOCK)) ){
|
||||
walUnlockShared(pWal, WAL_WRITE_LOCK);
|
||||
rc = SQLITE_READONLY_RECOVERY;
|
||||
}
|
||||
}else if( SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) ){
|
||||
pWal->writeLock = 1;
|
||||
if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){
|
||||
badHdr = walIndexTryHdr(pWal, pChanged);
|
||||
if( badHdr ){
|
||||
/* If the wal-index header is still malformed even while holding
|
||||
** a WRITE lock, it can only mean that the header is corrupted and
|
||||
** needs to be reconstructed. So run recovery to do exactly that.
|
||||
*/
|
||||
rc = walIndexRecover(pWal);
|
||||
*pChanged = 1;
|
||||
}
|
||||
}
|
||||
pWal->writeLock = 0;
|
||||
walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
|
||||
}
|
||||
pWal->writeLock = 0;
|
||||
walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
|
||||
}
|
||||
|
||||
/* If the header is read successfully, check the version number to make
|
||||
@ -2108,7 +2136,9 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
|
||||
}
|
||||
/* There was once an "if" here. The extra "{" is to preserve indentation. */
|
||||
{
|
||||
if( mxReadMark < pWal->hdr.mxFrame || mxI==0 ){
|
||||
if( (pWal->readOnly & WAL_SHM_RDONLY)==0
|
||||
&& (mxReadMark<pWal->hdr.mxFrame || mxI==0)
|
||||
){
|
||||
for(i=1; i<WAL_NREADER; i++){
|
||||
rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
|
||||
if( rc==SQLITE_OK ){
|
||||
@ -2122,8 +2152,8 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
|
||||
}
|
||||
}
|
||||
if( mxI==0 ){
|
||||
assert( rc==SQLITE_BUSY );
|
||||
return WAL_RETRY;
|
||||
assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 );
|
||||
return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTLOCK;
|
||||
}
|
||||
|
||||
rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
|
||||
@ -2522,6 +2552,24 @@ static int walRestartLog(Wal *pWal){
|
||||
*/
|
||||
int i; /* Loop counter */
|
||||
u32 *aSalt = pWal->hdr.aSalt; /* Big-endian salt values */
|
||||
|
||||
/* Limit the size of WAL file if the journal_size_limit PRAGMA is
|
||||
** set to a non-negative value. Log errors encountered
|
||||
** during the truncation attempt. */
|
||||
if( pWal->mxWalSize>=0 ){
|
||||
i64 sz;
|
||||
int rx;
|
||||
sqlite3BeginBenignMalloc();
|
||||
rx = sqlite3OsFileSize(pWal->pWalFd, &sz);
|
||||
if( rx==SQLITE_OK && (sz > pWal->mxWalSize) ){
|
||||
rx = sqlite3OsTruncate(pWal->pWalFd, pWal->mxWalSize);
|
||||
}
|
||||
sqlite3EndBenignMalloc();
|
||||
if( rx ){
|
||||
sqlite3_log(rx, "cannot limit WAL size: %s", pWal->zWalName);
|
||||
}
|
||||
}
|
||||
|
||||
pWal->nCkpt++;
|
||||
pWal->hdr.mxFrame = 0;
|
||||
sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0]));
|
||||
@ -2747,6 +2795,7 @@ int sqlite3WalCheckpoint(
|
||||
assert( pWal->ckptLock==0 );
|
||||
assert( pWal->writeLock==0 );
|
||||
|
||||
if( pWal->readOnly ) return SQLITE_READONLY;
|
||||
WALTRACE(("WAL%p: checkpoint begins\n", pWal));
|
||||
rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
|
||||
if( rc ){
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
|
||||
#ifdef SQLITE_OMIT_WAL
|
||||
# define sqlite3WalOpen(x,y,z) 0
|
||||
# define sqlite3WalLimit(x,y)
|
||||
# define sqlite3WalClose(w,x,y,z) 0
|
||||
# define sqlite3WalBeginReadTransaction(y,z) 0
|
||||
# define sqlite3WalEndReadTransaction(z)
|
||||
@ -46,9 +47,12 @@
|
||||
typedef struct Wal Wal;
|
||||
|
||||
/* Open and close a connection to a write-ahead log. */
|
||||
int sqlite3WalOpen(sqlite3_vfs*, sqlite3_file*, const char *zName, int, Wal**);
|
||||
int sqlite3WalOpen(sqlite3_vfs*, sqlite3_file*, const char *, int, i64, Wal**);
|
||||
int sqlite3WalClose(Wal *pWal, int sync_flags, int, u8 *);
|
||||
|
||||
/* Set the limiting size of a WAL file. */
|
||||
void sqlite3WalLimit(Wal*, i64);
|
||||
|
||||
/* Used by readers to open (lock) and close (unlock) a snapshot. A
|
||||
** snapshot is like a read-transaction. It is the state of the database
|
||||
** at an instant in time. sqlite3WalOpenSnapshot gets a read lock and
|
||||
|
||||
197
test/8_3_names.test
Normal file
197
test/8_3_names.test
Normal file
@ -0,0 +1,197 @@
|
||||
# 2011 May 17
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Test cases for the SQLITE_ENABLE_8_3_NAMES feature that forces all
|
||||
# filename extensions to be limited to 3 characters. Some embedded
|
||||
# systems need this to work around microsoft FAT patents, but this
|
||||
# feature should be disabled on most deployments.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !8_3_names {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
db close
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_uri 1
|
||||
|
||||
do_test 8_3_names-1.0 {
|
||||
forcedelete test.db test.nal test.db-journal
|
||||
sqlite3 db test.db
|
||||
db eval {
|
||||
PRAGMA cache_size=10;
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(randomblob(20000));
|
||||
BEGIN;
|
||||
DELETE FROM t1;
|
||||
INSERT INTO t1 VALUES(randomblob(15000));
|
||||
}
|
||||
file exists test.db-journal
|
||||
} 1
|
||||
do_test 8_3_names-1.1 {
|
||||
file exists test.nal
|
||||
} 0
|
||||
do_test 8_3_names-1.2 {
|
||||
db eval {
|
||||
ROLLBACK;
|
||||
SELECT length(x) FROM t1
|
||||
}
|
||||
} 20000
|
||||
|
||||
db close
|
||||
do_test 8_3_names-2.0 {
|
||||
forcedelete test.db test.nal test.db-journal
|
||||
sqlite3 db file:./test.db?8_3_names=1
|
||||
db eval {
|
||||
PRAGMA cache_size=10;
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(randomblob(20000));
|
||||
BEGIN;
|
||||
DELETE FROM t1;
|
||||
INSERT INTO t1 VALUES(randomblob(15000));
|
||||
}
|
||||
file exists test.db-journal
|
||||
} 0
|
||||
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
|
||||
do_test 8_3_names-2.2 {
|
||||
db eval {
|
||||
COMMIT;
|
||||
SELECT length(x) FROM t1
|
||||
}
|
||||
} 15000
|
||||
do_test 8_3_names-2.3 {
|
||||
sqlite3 db2 file:./test2.db?8_3_names=1
|
||||
db2 eval {
|
||||
PRAGMA integrity_check;
|
||||
SELECT length(x) FROM t1;
|
||||
}
|
||||
} {ok 20000}
|
||||
|
||||
db close
|
||||
do_test 8_3_names-3.0 {
|
||||
forcedelete test.db test.nal test.db-journal
|
||||
sqlite3 db file:./test.db?8_3_names=0
|
||||
db eval {
|
||||
PRAGMA cache_size=10;
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(randomblob(20000));
|
||||
BEGIN;
|
||||
DELETE FROM t1;
|
||||
INSERT INTO t1 VALUES(randomblob(15000));
|
||||
}
|
||||
file exists test.db-journal
|
||||
} 1
|
||||
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
|
||||
do_test 8_3_names-3.2 {
|
||||
db eval {
|
||||
COMMIT;
|
||||
SELECT length(x) FROM t1
|
||||
}
|
||||
} 15000
|
||||
do_test 8_3_names-3.3 {
|
||||
sqlite3 db2 file:./test2.db?8_3_names=0
|
||||
db2 eval {
|
||||
PRAGMA integrity_check;
|
||||
SELECT length(x) FROM t1;
|
||||
}
|
||||
} {ok 20000}
|
||||
|
||||
##########################################################################
|
||||
# Master journals.
|
||||
#
|
||||
db close
|
||||
forcedelete test.db test2.db
|
||||
do_test 8_3_names-4.0 {
|
||||
sqlite3 db file:./test.db?8_3_names=1
|
||||
db eval {
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(1);
|
||||
ATTACH 'file:./test2.db?8_3_names=1' AS db2;
|
||||
CREATE TABLE db2.t2(y);
|
||||
INSERT INTO t2 VALUES(2);
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES(3);
|
||||
INSERT INTO t2 VALUES(4);
|
||||
COMMIT;
|
||||
SELECT * FROM t1, t2 ORDER BY x, y
|
||||
}
|
||||
} {1 2 1 4 3 2 3 4}
|
||||
|
||||
|
||||
##########################################################################
|
||||
# WAL mode.
|
||||
#
|
||||
ifcapable !wal {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
db close
|
||||
forcedelete test.db
|
||||
do_test 8_3_names-5.0 {
|
||||
sqlite3 db file:./test.db?8_3_names=1
|
||||
register_wholenumber_module db
|
||||
db eval {
|
||||
PRAGMA journal_mode=WAL;
|
||||
CREATE TABLE t1(x);
|
||||
CREATE VIRTUAL TABLE nums USING wholenumber;
|
||||
INSERT INTO t1 SELECT value FROM nums WHERE value BETWEEN 1 AND 1000;
|
||||
BEGIN;
|
||||
UPDATE t1 SET x=x*2;
|
||||
}
|
||||
sqlite3 db2 file:./test.db?8_3_names=1
|
||||
register_wholenumber_module db2
|
||||
db2 eval {
|
||||
BEGIN;
|
||||
SELECT sum(x) FROM t1;
|
||||
}
|
||||
} {500500}
|
||||
|
||||
do_test 8_3_names-5.1 {
|
||||
file exists test.db-wal
|
||||
} 0
|
||||
do_test 8_3_names-5.2 {
|
||||
file exists test.wal
|
||||
} 1
|
||||
do_test 8_3_names-5.3 {
|
||||
file exists test.db-shm
|
||||
} 0
|
||||
do_test 8_3_names-5.4 {
|
||||
file exists test.shm
|
||||
} 1
|
||||
|
||||
|
||||
do_test 8_3_names-5.5 {
|
||||
db eval {
|
||||
COMMIT;
|
||||
SELECT sum(x) FROM t1;
|
||||
}
|
||||
} {1001000}
|
||||
do_test 8_3_names-5.6 {
|
||||
db2 eval {
|
||||
SELECT sum(x) FROM t1;
|
||||
}
|
||||
} {500500}
|
||||
|
||||
|
||||
finish_test
|
||||
@ -650,7 +650,7 @@ do_test alter-6.7 {
|
||||
# Ticket #1665: Make sure ALTER TABLE ADD COLUMN works on a table
|
||||
# that includes a COLLATE clause.
|
||||
#
|
||||
do_test alter-7.1 {
|
||||
do_realnum_test alter-7.1 {
|
||||
execsql {
|
||||
CREATE TABLE t1(a TEXT COLLATE BINARY);
|
||||
ALTER TABLE t1 ADD COLUMN b INTEGER COLLATE NOCASE;
|
||||
|
||||
@ -234,7 +234,7 @@ do_test cast-3.1 {
|
||||
do_test cast-3.2 {
|
||||
execsql {SELECT CAST(9223372036854774800 AS numeric)}
|
||||
} 9223372036854774800
|
||||
do_test cast-3.3 {
|
||||
do_realnum_test cast-3.3 {
|
||||
execsql {SELECT CAST(9223372036854774800 AS real)}
|
||||
} 9.22337203685477e+18
|
||||
do_test cast-3.4 {
|
||||
@ -246,7 +246,7 @@ do_test cast-3.5 {
|
||||
do_test cast-3.6 {
|
||||
execsql {SELECT CAST(-9223372036854774800 AS numeric)}
|
||||
} -9223372036854774800
|
||||
do_test cast-3.7 {
|
||||
do_realnum_test cast-3.7 {
|
||||
execsql {SELECT CAST(-9223372036854774800 AS real)}
|
||||
} -9.22337203685477e+18
|
||||
do_test cast-3.8 {
|
||||
@ -258,7 +258,7 @@ do_test cast-3.11 {
|
||||
do_test cast-3.12 {
|
||||
execsql {SELECT CAST('9223372036854774800' AS numeric)}
|
||||
} 9223372036854774800
|
||||
do_test cast-3.13 {
|
||||
do_realnum_test cast-3.13 {
|
||||
execsql {SELECT CAST('9223372036854774800' AS real)}
|
||||
} 9.22337203685477e+18
|
||||
ifcapable long_double {
|
||||
@ -272,7 +272,7 @@ do_test cast-3.15 {
|
||||
do_test cast-3.16 {
|
||||
execsql {SELECT CAST('-9223372036854774800' AS numeric)}
|
||||
} -9223372036854774800
|
||||
do_test cast-3.17 {
|
||||
do_realnum_test cast-3.17 {
|
||||
execsql {SELECT CAST('-9223372036854774800' AS real)}
|
||||
} -9.22337203685477e+18
|
||||
ifcapable long_double {
|
||||
@ -287,7 +287,7 @@ if {[db eval {PRAGMA encoding}]=="UTF-8"} {
|
||||
do_test cast-3.22 {
|
||||
execsql {SELECT CAST(x'39323233333732303336383534373734383030' AS numeric)}
|
||||
} 9223372036854774800
|
||||
do_test cast-3.23 {
|
||||
do_realnum_test cast-3.23 {
|
||||
execsql {SELECT CAST(x'39323233333732303336383534373734383030' AS real)}
|
||||
} 9.22337203685477e+18
|
||||
ifcapable long_double {
|
||||
|
||||
@ -989,9 +989,9 @@ do_execsql_test e_createtable-3.7.4 {
|
||||
SELECT quote(a), quote(b) FROM t6;
|
||||
} {1 2 'X' 3 1 4 'X' 5}
|
||||
|
||||
# EVIDENCE-OF: R-18683-56219 If the default value of a column is
|
||||
# CURRENT_TIME, CURRENT_DATE or CURRENT_DATETIME, then the value used in
|
||||
# the new row is a text representation of the current UTC date and/or
|
||||
# EVIDENCE-OF: R-15363-55230 If the default value of a column is
|
||||
# CURRENT_TIME, CURRENT_DATE or CURRENT_TIMESTAMP, then the value used
|
||||
# in the new row is a text representation of the current UTC date and/or
|
||||
# time.
|
||||
#
|
||||
# This is difficult to test literally without knowing what time the
|
||||
|
||||
@ -440,7 +440,7 @@ do_delete_tests e_delete-3.9 -repair rebuild_t1 -query {
|
||||
}
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-26627-30313 The ORDER BY clause on an DELETE statement
|
||||
# EVIDENCE-OF: R-07548-13422 The ORDER BY clause on a DELETE statement
|
||||
# is used only to determine which rows fall within the LIMIT. The order
|
||||
# in which rows are deleted is arbitrary and is not influenced by the
|
||||
# ORDER BY clause.
|
||||
|
||||
@ -1598,8 +1598,8 @@ do_expr_test e_expr-31.1.2 { CAST(1.99999 AS INTEGER) } integer 1
|
||||
do_expr_test e_expr-31.1.3 { CAST(-1.99999 AS INTEGER) } integer -1
|
||||
do_expr_test e_expr-31.1.4 { CAST(-0.99999 AS INTEGER) } integer 0
|
||||
|
||||
# EVIDENCE-OF: R-06126-36021 If an REAL is too large to be represented
|
||||
# as an INTEGER then the result of the cast is the largest negative
|
||||
# EVIDENCE-OF: R-49503-28105 If a REAL is too large to be represented as
|
||||
# an INTEGER then the result of the cast is the largest negative
|
||||
# integer: -9223372036854775808.
|
||||
#
|
||||
do_expr_test e_expr-31.2.1 { CAST(2e+50 AS INT) } integer -9223372036854775808
|
||||
@ -1845,4 +1845,3 @@ foreach {tn expr} {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@ -152,7 +152,7 @@ do_insert_tests e_insert-1.2 -error {
|
||||
4 "INSERT INTO a2 VALUES(1,2,3,4,5)" {a2 3 5}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-52422-65517 In this case the result of evaluting the
|
||||
# EVIDENCE-OF: R-04006-57648 In this case the result of evaluating the
|
||||
# left-most expression in the VALUES list is inserted into the left-most
|
||||
# column of the new row, and so on.
|
||||
#
|
||||
|
||||
@ -1768,9 +1768,9 @@ do_select_tests e_select-8.5 {
|
||||
}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-27923-38747 Otherwise, if the ORDER BY expression is
|
||||
# any other expression, it is evaluated and the the returned value used
|
||||
# to order the output rows.
|
||||
# EVIDENCE-OF: R-65068-27207 Otherwise, if the ORDER BY expression is
|
||||
# any other expression, it is evaluated and the returned value used to
|
||||
# order the output rows.
|
||||
#
|
||||
# EVIDENCE-OF: R-03421-57988 If the SELECT statement is a simple SELECT,
|
||||
# then an ORDER BY may contain any arbitrary expressions.
|
||||
|
||||
@ -233,9 +233,9 @@ do_update_tests e_update-1.5 -query {
|
||||
{3 1 4 1 5 9 2 6 5}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-09060-20018 If a single column-name appears more than
|
||||
# EVIDENCE-OF: R-34751-18293 If a single column-name appears more than
|
||||
# once in the list of assignment expressions, all but the rightmost
|
||||
# occurence is ignored.
|
||||
# occurrence is ignored.
|
||||
#
|
||||
do_update_tests e_update-1.6 -query {
|
||||
SELECT * FROM t2
|
||||
@ -605,4 +605,3 @@ do_update_tests e_update-3.5 -query { SELECT x FROM t8 ; DELETE FROM t8 } {
|
||||
} ;# ifcapable update_delete_limit
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
456
test/e_uri.test
Normal file
456
test/e_uri.test
Normal file
@ -0,0 +1,456 @@
|
||||
# 2011 May 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix e_uri
|
||||
|
||||
db close
|
||||
|
||||
proc parse_uri {uri} {
|
||||
testvfs tvfs2
|
||||
testvfs tvfs
|
||||
tvfs filter xOpen
|
||||
tvfs script parse_uri_open_cb
|
||||
|
||||
set ::uri_open [list]
|
||||
set DB [sqlite3_open_v2 $uri {
|
||||
SQLITE_OPEN_READWRITE SQLITE_OPEN_CREATE SQLITE_OPEN_WAL
|
||||
} tvfs]
|
||||
sqlite3_close $DB
|
||||
tvfs delete
|
||||
tvfs2 delete
|
||||
|
||||
set ::uri_open
|
||||
}
|
||||
proc parse_uri_open_cb {method file arglist} {
|
||||
set ::uri_open [list $file $arglist]
|
||||
}
|
||||
|
||||
proc open_uri_error {uri} {
|
||||
set flags {SQLITE_OPEN_READWRITE SQLITE_OPEN_CREATE SQLITE_OPEN_WAL}
|
||||
set DB [sqlite3_open_v2 $uri $flags ""]
|
||||
set e [sqlite3_errmsg $DB]
|
||||
sqlite3_close $DB
|
||||
set e
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-35840-33204 If URI filename interpretation is enabled,
|
||||
# and the filename argument begins with "file:", then the filename is
|
||||
# interpreted as a URI.
|
||||
#
|
||||
# EVIDENCE-OF: R-24124-56960 URI filename interpretation is enabled if
|
||||
# the SQLITE_OPEN_URI flag is set in the fourth argument to
|
||||
# sqlite3_open_v2(), or if it has been enabled globally using the
|
||||
# SQLITE_CONFIG_URI option with the sqlite3_config() method or by the
|
||||
# SQLITE_USE_URI compile-time option.
|
||||
#
|
||||
if {$tcl_platform(platform) == "unix"} {
|
||||
set flags [list SQLITE_OPEN_READWRITE SQLITE_OPEN_CREATE]
|
||||
|
||||
# Tests with SQLITE_CONFIG_URI configured to false. URI intepretation is
|
||||
# only enabled if the SQLITE_OPEN_URI flag is specified.
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_uri 0
|
||||
do_test 1.1 {
|
||||
forcedelete file:test.db test.db
|
||||
set DB [sqlite3_open_v2 file:test.db [concat $flags SQLITE_OPEN_URI] ""]
|
||||
list [file exists file:test.db] [file exists test.db]
|
||||
} {0 1}
|
||||
do_test 1.2 {
|
||||
forcedelete file:test.db2 test.db2
|
||||
set STMT [sqlite3_prepare $DB "ATTACH 'file:test.db2' AS aux" -1 dummy]
|
||||
sqlite3_step $STMT
|
||||
sqlite3_finalize $STMT
|
||||
list [file exists file:test.db2] [file exists test.db2]
|
||||
} {0 1}
|
||||
sqlite3_close $DB
|
||||
do_test 1.3 {
|
||||
forcedelete file:test.db test.db
|
||||
set DB [sqlite3_open_v2 file:test.db [concat $flags] ""]
|
||||
list [file exists file:test.db] [file exists test.db]
|
||||
} {1 0}
|
||||
do_test 1.4 {
|
||||
forcedelete file:test.db2 test.db2
|
||||
set STMT [sqlite3_prepare $DB "ATTACH 'file:test.db2' AS aux" -1 dummy]
|
||||
sqlite3_step $STMT
|
||||
sqlite3_finalize $STMT
|
||||
list [file exists file:test.db2] [file exists test.db2]
|
||||
} {1 0}
|
||||
sqlite3_close $DB
|
||||
|
||||
# Tests with SQLITE_CONFIG_URI configured to true. URI intepretation is
|
||||
# enabled with or without SQLITE_OPEN_URI.
|
||||
#
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_uri 1
|
||||
do_test 1.5 {
|
||||
forcedelete file:test.db test.db
|
||||
set DB [sqlite3_open_v2 file:test.db [concat $flags SQLITE_OPEN_URI] ""]
|
||||
list [file exists file:test.db] [file exists test.db]
|
||||
} {0 1}
|
||||
do_test 1.6 {
|
||||
forcedelete file:test.db2 test.db2
|
||||
set STMT [sqlite3_prepare $DB "ATTACH 'file:test.db2' AS aux" -1 dummy]
|
||||
sqlite3_step $STMT
|
||||
sqlite3_finalize $STMT
|
||||
list [file exists file:test.db2] [file exists test.db2]
|
||||
} {0 1}
|
||||
sqlite3_close $DB
|
||||
do_test 1.7 {
|
||||
forcedelete file:test.db test.db
|
||||
set DB [sqlite3_open_v2 file:test.db [concat $flags] ""]
|
||||
list [file exists file:test.db] [file exists test.db]
|
||||
} {0 1}
|
||||
do_test 1.8 {
|
||||
forcedelete file:test.db2 test.db2
|
||||
set STMT [sqlite3_prepare $DB "ATTACH 'file:test.db2' AS aux" -1 dummy]
|
||||
sqlite3_step $STMT
|
||||
sqlite3_finalize $STMT
|
||||
list [file exists file:test.db2] [file exists test.db2]
|
||||
} {0 1}
|
||||
sqlite3_close $DB
|
||||
}
|
||||
|
||||
# ensure uri processing enabled for the rest of the tests
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_uri 1
|
||||
|
||||
# EVIDENCE-OF: R-17482-00398 If the authority is not an empty string or
|
||||
# "localhost", an error is returned to the caller.
|
||||
#
|
||||
if {$tcl_platform(platform) == "unix"} {
|
||||
set flags [list SQLITE_OPEN_READWRITE SQLITE_OPEN_CREATE SQLITE_OPEN_URI]
|
||||
foreach {tn uri error} "
|
||||
1 {file://localhost[pwd]/test.db} {not an error}
|
||||
2 {file://[pwd]/test.db} {not an error}
|
||||
3 {file://x[pwd]/test.db} {invalid uri authority: x}
|
||||
4 {file://invalid[pwd]/test.db} {invalid uri authority: invalid}
|
||||
" {
|
||||
do_test 2.$tn {
|
||||
set DB [sqlite3_open_v2 $uri $flags ""]
|
||||
set e [sqlite3_errmsg $DB]
|
||||
sqlite3_close $DB
|
||||
set e
|
||||
} $error
|
||||
}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-45981-25528 The fragment component of a URI, if
|
||||
# present, is ignored.
|
||||
#
|
||||
# It is difficult to test that something is ignored correctly. So these tests
|
||||
# just show that adding a fragment does not interfere with the pathname or
|
||||
# parameters passed through to the VFS xOpen() methods.
|
||||
#
|
||||
foreach {tn uri parse} "
|
||||
1 {file:test.db#abc} {[pwd]/test.db {}}
|
||||
2 {file:test.db?a=b#abc} {[pwd]/test.db {a b}}
|
||||
3 {file:test.db?a=b#?c=d} {[pwd]/test.db {a b}}
|
||||
" {
|
||||
do_filepath_test 3.$tn { parse_uri $uri } $parse
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-62557-09390 SQLite uses the path component of the URI
|
||||
# as the name of the disk file which contains the database.
|
||||
#
|
||||
# EVIDENCE-OF: R-28659-11035 If the path begins with a '/' character,
|
||||
# then it is interpreted as an absolute path.
|
||||
#
|
||||
# EVIDENCE-OF: R-46234-61323 If the path does not begin with a '/'
|
||||
# (meaning that the authority section is omitted from the URI) then the
|
||||
# path is interpreted as a relative path.
|
||||
#
|
||||
foreach {tn uri parse} "
|
||||
1 {file:test.db} {[pwd]/test.db {}}
|
||||
2 {file:/test.db} {/test.db {}}
|
||||
3 {file:///test.db} {/test.db {}}
|
||||
4 {file://localhost/test.db} {/test.db {}}
|
||||
5 {file:/a/b/c/test.db} {/a/b/c/test.db {}}
|
||||
" {
|
||||
do_filepath_test 4.$tn { parse_uri $uri } $parse
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-01612-30877 The "vfs" parameter may be used to specify
|
||||
# the name of a VFS object that provides the operating system interface
|
||||
# that should be used to access the database file on disk.
|
||||
#
|
||||
# The above is tested by cases 1.* below.
|
||||
#
|
||||
# EVIDENCE-OF: R-52293-58497 If this option is set to an empty string
|
||||
# the default VFS object is used.
|
||||
#
|
||||
# The above is tested by cases 2.* below.
|
||||
#
|
||||
# EVIDENCE-OF: R-31855-18665 If sqlite3_open_v2() is used and the vfs
|
||||
# option is present, then the VFS specified by the option takes
|
||||
# precedence over the value passed as the fourth parameter to
|
||||
# sqlite3_open_v2().
|
||||
#
|
||||
# The above is tested by cases 3.* below.
|
||||
#
|
||||
proc vfs_open_cb {name args} {
|
||||
set ::vfs $name
|
||||
}
|
||||
foreach {name default} {vfs1 0 vfs2 0 vfs3 1} {
|
||||
testvfs $name -default $default
|
||||
$name filter xOpen
|
||||
$name script [list vfs_open_cb $name]
|
||||
}
|
||||
foreach {tn uri defvfs vfs} {
|
||||
1.1 "file:test.db?vfs=vfs1" "" vfs1
|
||||
1.2 "file:test.db?vfs=vfs2" "" vfs2
|
||||
|
||||
2.1 "file:test.db" vfs1 vfs1
|
||||
2.2 "file:test.db?vfs=" vfs1 vfs3
|
||||
|
||||
3.1 "file:test.db?vfs=vfs1" vfs2 vfs1
|
||||
3.2 "file:test.db?vfs=vfs2" vfs1 vfs2
|
||||
3.3 "file:test.db?xvfs=vfs1" vfs2 vfs2
|
||||
3.4 "file:test.db?xvfs=vfs2" vfs1 vfs1
|
||||
} {
|
||||
do_test 5.$tn {
|
||||
set flags [list SQLITE_OPEN_READWRITE SQLITE_OPEN_CREATE SQLITE_OPEN_URI]
|
||||
sqlite3_close [
|
||||
sqlite3_open_v2 $uri $flags $defvfs
|
||||
]
|
||||
set ::vfs
|
||||
} $vfs
|
||||
}
|
||||
vfs1 delete
|
||||
vfs2 delete
|
||||
vfs3 delete
|
||||
|
||||
# EVIDENCE-OF: R-48365-36308 Specifying an unknown VFS is an error.
|
||||
#
|
||||
set flags [list SQLITE_OPEN_READWRITE SQLITE_OPEN_CREATE SQLITE_OPEN_URI]
|
||||
do_test 6.1 {
|
||||
set DB [sqlite3_open_v2 file:test.db?vfs=nosuchvfs $flags ""]
|
||||
set errmsg [sqlite3_errmsg $DB]
|
||||
sqlite3_close $DB
|
||||
set errmsg
|
||||
} {no such vfs: nosuchvfs}
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-60479-64270 The mode parameter may be set to either
|
||||
# "ro", "rw" or "rwc". Attempting to set it to any other value is an
|
||||
# error
|
||||
#
|
||||
sqlite3 db test.db
|
||||
db close
|
||||
foreach {tn uri error} "
|
||||
1 {file:test.db?mode=ro} {not an error}
|
||||
2 {file:test.db?mode=rw} {not an error}
|
||||
3 {file:test.db?mode=rwc} {not an error}
|
||||
4 {file:test.db?mode=Ro} {no such access mode: Ro}
|
||||
5 {file:test.db?mode=Rw} {no such access mode: Rw}
|
||||
6 {file:test.db?mode=Rwc} {no such access mode: Rwc}
|
||||
" {
|
||||
do_test 7.$tn { open_uri_error $uri } $error
|
||||
}
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-09651-31805 If "ro" is specified, then the database is
|
||||
# opened for read-only access, just as if the SQLITE_OPEN_READONLY flag
|
||||
# had been set in the third argument to sqlite3_prepare_v2().
|
||||
#
|
||||
# EVIDENCE-OF: R-40137-26050 If the mode option is set to "rw", then the
|
||||
# database is opened for read-write (but not create) access, as if
|
||||
# SQLITE_OPEN_READWRITE (but not SQLITE_OPEN_CREATE) had been set.
|
||||
#
|
||||
# EVIDENCE-OF: R-26845-32976 Value "rwc" is equivalent to setting both
|
||||
# SQLITE_OPEN_READWRITE and SQLITE_OPEN_CREATE.
|
||||
#
|
||||
foreach {tn uri read write create} {
|
||||
1 {file:test.db?mode=ro} 1 0 0
|
||||
2 {file:test.db?mode=rw} 1 1 0
|
||||
3 {file:test.db?mode=rwc} 1 1 1
|
||||
} {
|
||||
set RES(c,0) {1 {unable to open database file}}
|
||||
set RES(c,1) {0 {}}
|
||||
set RES(w,0) {1 {attempt to write a readonly database}}
|
||||
set RES(w,1) {0 {}}
|
||||
set RES(r,0) {1 {this never happens}}
|
||||
set RES(r,1) {0 {a b}}
|
||||
|
||||
# Test CREATE access:
|
||||
forcedelete test.db
|
||||
do_test 8.$tn.c { list [catch { sqlite3 db $uri } msg] $msg } $RES(c,$create)
|
||||
catch { db close }
|
||||
|
||||
sqlite3 db test.db
|
||||
db eval { CREATE TABLE t1(a, b) ; INSERT INTO t1 VALUES('a', 'b') ;}
|
||||
db close
|
||||
|
||||
# Test READ access:
|
||||
do_test 8.$tn.r {
|
||||
sqlite3 db $uri
|
||||
catchsql { SELECT * FROM t1 }
|
||||
} $RES(r,$read)
|
||||
|
||||
# Test WRITE access:
|
||||
do_test 8.$tn.w {
|
||||
sqlite3 db $uri
|
||||
catchsql { INSERT INTO t1 VALUES(1, 2) }
|
||||
} $RES(w,$write)
|
||||
|
||||
catch {db close}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-56032-32287 If sqlite3_open_v2() is used, it is an
|
||||
# error to specify a value for the mode parameter that is less
|
||||
# restrictive than that specified by the flags passed as the third
|
||||
# parameter.
|
||||
#
|
||||
forcedelete test.db
|
||||
sqlite3 db test.db
|
||||
db close
|
||||
foreach {tn uri flags error} {
|
||||
1 {file:test.db?mode=ro} ro {not an error}
|
||||
2 {file:test.db?mode=ro} rw {not an error}
|
||||
3 {file:test.db?mode=ro} rwc {not an error}
|
||||
|
||||
4 {file:test.db?mode=rw} ro {access mode not allowed: rw}
|
||||
5 {file:test.db?mode=rw} rw {not an error}
|
||||
6 {file:test.db?mode=rw} rwc {not an error}
|
||||
|
||||
7 {file:test.db?mode=rwc} ro {access mode not allowed: rwc}
|
||||
8 {file:test.db?mode=rwc} rw {access mode not allowed: rwc}
|
||||
9 {file:test.db?mode=rwc} rwc {not an error}
|
||||
} {
|
||||
set f(ro) [list SQLITE_OPEN_READONLY SQLITE_OPEN_URI]
|
||||
set f(rw) [list SQLITE_OPEN_READWRITE SQLITE_OPEN_URI]
|
||||
set f(rwc) [list SQLITE_OPEN_READWRITE SQLITE_OPEN_CREATE SQLITE_OPEN_URI]
|
||||
|
||||
set DB [sqlite3_open_v2 $uri $f($flags) ""]
|
||||
set e [sqlite3_errmsg $DB]
|
||||
sqlite3_close $DB
|
||||
|
||||
do_test 9.$tn { set e } $error
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-23182-54295 The cache parameter may be set to either
|
||||
# "shared" or "private".
|
||||
sqlite3 db test.db
|
||||
db close
|
||||
foreach {tn uri error} "
|
||||
1 {file:test.db?cache=private} {not an error}
|
||||
2 {file:test.db?cache=shared} {not an error}
|
||||
3 {file:test.db?cache=yes} {no such cache mode: yes}
|
||||
4 {file:test.db?cache=} {no such cache mode: }
|
||||
" {
|
||||
do_test 10.$tn { open_uri_error $uri } $error
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-23027-03515 Setting it to "shared" is equivalent to
|
||||
# setting the SQLITE_OPEN_SHAREDCACHE bit in the flags argument passed
|
||||
# to sqlite3_open_v2().
|
||||
#
|
||||
# EVIDENCE-OF: R-49793-28525 Setting the cache parameter to "private" is
|
||||
# equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit.
|
||||
#
|
||||
# EVIDENCE-OF: R-19510-48080 If sqlite3_open_v2() is used and the
|
||||
# "cache" parameter is present in a URI filename, its value overrides
|
||||
# any behaviour requested by setting SQLITE_OPEN_PRIVATECACHE or
|
||||
# SQLITE_OPEN_SHAREDCACHE flag.
|
||||
#
|
||||
set orig [sqlite3_enable_shared_cache]
|
||||
foreach {tn uri flags shared_default isshared} {
|
||||
1.1 "file:test.db" "" 0 0
|
||||
1.2 "file:test.db" "" 1 1
|
||||
1.3 "file:test.db" private 0 0
|
||||
1.4 "file:test.db" private 1 0
|
||||
1.5 "file:test.db" shared 0 1
|
||||
1.6 "file:test.db" shared 1 1
|
||||
|
||||
2.1 "file:test.db?cache=private" "" 0 0
|
||||
2.2 "file:test.db?cache=private" "" 1 0
|
||||
2.3 "file:test.db?cache=private" private 0 0
|
||||
2.4 "file:test.db?cache=private" private 1 0
|
||||
2.5 "file:test.db?cache=private" shared 0 0
|
||||
2.6 "file:test.db?cache=private" shared 1 0
|
||||
|
||||
3.1 "file:test.db?cache=shared" "" 0 1
|
||||
3.2 "file:test.db?cache=shared" "" 1 1
|
||||
3.3 "file:test.db?cache=shared" private 0 1
|
||||
3.4 "file:test.db?cache=shared" private 1 1
|
||||
3.5 "file:test.db?cache=shared" shared 0 1
|
||||
3.6 "file:test.db?cache=shared" shared 1 1
|
||||
} {
|
||||
forcedelete test.db
|
||||
sqlite3_enable_shared_cache 1
|
||||
sqlite3 db test.db
|
||||
sqlite3_enable_shared_cache 0
|
||||
|
||||
db eval {
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES('ok');
|
||||
}
|
||||
|
||||
unset -nocomplain f
|
||||
set f() {SQLITE_OPEN_READWRITE SQLITE_OPEN_CREATE SQLITE_OPEN_URI}
|
||||
set f(shared) [concat $f() SQLITE_OPEN_SHAREDCACHE]
|
||||
set f(private) [concat $f() SQLITE_OPEN_PRIVATECACHE]
|
||||
|
||||
sqlite3_enable_shared_cache $shared_default
|
||||
set DB [sqlite3_open_v2 $uri $f($flags) ""]
|
||||
|
||||
set STMT [sqlite3_prepare $DB "SELECT * FROM t1" -1 dummy]
|
||||
|
||||
db eval {
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES('ko');
|
||||
}
|
||||
|
||||
sqlite3_step $STMT
|
||||
sqlite3_finalize $STMT
|
||||
|
||||
set RES(0) {not an error}
|
||||
set RES(1) {database table is locked: t1}
|
||||
|
||||
do_test 11.$tn { sqlite3_errmsg $DB } $RES($isshared)
|
||||
|
||||
sqlite3_close $DB
|
||||
db close
|
||||
}
|
||||
sqlite3_enable_shared_cache $orig
|
||||
|
||||
# EVIDENCE-OF: R-63472-46769 Specifying an unknown parameter in the
|
||||
# query component of a URI is not an error.
|
||||
#
|
||||
do_filepath_test 12.1 {
|
||||
parse_uri file://localhost/test.db?an=unknown¶meter=is&ok=
|
||||
} {/test.db {an unknown parameter is ok {}}}
|
||||
do_filepath_test 12.2 {
|
||||
parse_uri file://localhost/test.db?an&unknown¶meter&is&ok
|
||||
} {/test.db {an {} unknown {} parameter {} is {} ok {}}}
|
||||
|
||||
# EVIDENCE-OF: R-27458-04043 URI hexadecimal escape sequences (%HH) are
|
||||
# supported within the path and query components of a URI.
|
||||
#
|
||||
# EVIDENCE-OF: R-52765-50368 Before the path or query components of a
|
||||
# URI filename are interpreted, they are encoded using UTF-8 and all
|
||||
# hexadecimal escape sequences replaced by a single byte containing the
|
||||
# corresponding octet.
|
||||
#
|
||||
# The second of the two statements above is tested by creating a
|
||||
# multi-byte utf-8 character using a sequence of %HH escapes.
|
||||
#
|
||||
foreach {tn uri parse} "
|
||||
1 {file:/test.%64%62} {/test.db {}}
|
||||
2 {file:/test.db?%68%65%6c%6c%6f=%77%6f%72%6c%64} {/test.db {hello world}}
|
||||
3 {file:/%C3%BF.db} {/\xFF.db {}}
|
||||
" {
|
||||
do_filepath_test 13.$tn { parse_uri $uri } $parse
|
||||
}
|
||||
|
||||
finish_test
|
||||
276
test/enc4.test
276
test/enc4.test
@ -1,139 +1,137 @@
|
||||
# 2010 Sept 29
|
||||
#
|
||||
# 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. The focus of
|
||||
# this file is testing the SQLite routines used for converting between the
|
||||
# various suported unicode encodings (UTF-8, UTF-16, UTF-16le and
|
||||
# UTF-16be).
|
||||
#
|
||||
# $Id: enc4.test,v 1.0 2010/09/29 08:29:32 shaneh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If UTF16 support is disabled, ignore the tests in this file
|
||||
#
|
||||
ifcapable {!utf16} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
db close
|
||||
|
||||
# The three unicode encodings understood by SQLite.
|
||||
set encodings [list UTF-8 UTF-16le UTF-16be]
|
||||
|
||||
# initial value to use in SELECT
|
||||
set inits [list 1 1.0 1. 1e0]
|
||||
|
||||
# vals
|
||||
set vals [list\
|
||||
"922337203685477580792233720368547758079223372036854775807"\
|
||||
"100000000000000000000000000000000000000000000000000000000"\
|
||||
"1.0000000000000000000000000000000000000000000000000000000"\
|
||||
]
|
||||
|
||||
set i 1
|
||||
foreach enc $encodings {
|
||||
|
||||
file delete -force test.db
|
||||
sqlite3 db test.db
|
||||
db eval "PRAGMA encoding = \"$enc\""
|
||||
|
||||
do_test enc4-$i.1 {
|
||||
db eval {PRAGMA encoding}
|
||||
} $enc
|
||||
|
||||
set j 1
|
||||
foreach init $inits {
|
||||
|
||||
do_test enc4-$i.$j.2 {
|
||||
set S [sqlite3_prepare_v2 db "SELECT $init+?" -1 dummy]
|
||||
sqlite3_expired $S
|
||||
} {0}
|
||||
|
||||
set k 1
|
||||
foreach val $vals {
|
||||
for {set x 1} {$x<18} {incr x} {
|
||||
set part [expr $init + [string range $val 0 [expr $x-1]]]
|
||||
regsub {e\+0} $part {e+} part
|
||||
regsub {^1e} $part {1.0e} part
|
||||
|
||||
do_test enc4-$i.$j.$k.3.$x {
|
||||
sqlite3_reset $S
|
||||
sqlite3_bind_text $S 1 $val $x
|
||||
sqlite3_step $S
|
||||
sqlite3_column_text $S 0
|
||||
} [list $part]
|
||||
|
||||
do_test enc4-$i.$j.$k.4.$x {
|
||||
sqlite3_reset $S
|
||||
sqlite3_bind_text16 $S 1 [encoding convertto unicode $val] [expr $x*2]
|
||||
sqlite3_step $S
|
||||
sqlite3_column_text $S 0
|
||||
} [list $part]
|
||||
}
|
||||
|
||||
incr k
|
||||
}
|
||||
|
||||
do_test enc4-$i.$j.5 {
|
||||
sqlite3_finalize $S
|
||||
} {SQLITE_OK}
|
||||
|
||||
incr j
|
||||
}
|
||||
|
||||
db close
|
||||
incr i
|
||||
}
|
||||
|
||||
file delete -force test.db
|
||||
sqlite3 db test.db
|
||||
|
||||
do_test enc4-4.1 {
|
||||
db eval "select 1+1."
|
||||
} {2.0}
|
||||
|
||||
do_test enc4-4.2.1 {
|
||||
set S [sqlite3_prepare_v2 db "SELECT 1+1." -1 dummy]
|
||||
sqlite3_step $S
|
||||
sqlite3_column_text $S 0
|
||||
} {2.0}
|
||||
|
||||
do_test enc4-4.2.2 {
|
||||
sqlite3_finalize $S
|
||||
} {SQLITE_OK}
|
||||
|
||||
do_test enc4-4.3.1 {
|
||||
set S [sqlite3_prepare_v2 db "SELECT 1+?" -1 dummy]
|
||||
sqlite3_bind_text $S 1 "1." 2
|
||||
sqlite3_step $S
|
||||
sqlite3_column_text $S 0
|
||||
} {2.0}
|
||||
|
||||
do_test enc4-4.3.2 {
|
||||
sqlite3_finalize $S
|
||||
} {SQLITE_OK}
|
||||
|
||||
do_test enc4-4.4.1 {
|
||||
set S [sqlite3_prepare_v2 db "SELECT 1+?" -1 dummy]
|
||||
sqlite3_bind_text $S 1 "1.0" 2
|
||||
sqlite3_step $S
|
||||
sqlite3_column_text $S 0
|
||||
} {2.0}
|
||||
|
||||
do_test enc4-4.4.2 {
|
||||
sqlite3_finalize $S
|
||||
} {SQLITE_OK}
|
||||
|
||||
db close
|
||||
|
||||
finish_test
|
||||
# 2010 Sept 29
|
||||
#
|
||||
# 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. The focus of
|
||||
# this file is testing the SQLite routines used for converting between the
|
||||
# various suported unicode encodings (UTF-8, UTF-16, UTF-16le and
|
||||
# UTF-16be).
|
||||
#
|
||||
# $Id: enc4.test,v 1.0 2010/09/29 08:29:32 shaneh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If UTF16 support is disabled, ignore the tests in this file
|
||||
#
|
||||
ifcapable {!utf16} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
db close
|
||||
|
||||
# The three unicode encodings understood by SQLite.
|
||||
set encodings [list UTF-8 UTF-16le UTF-16be]
|
||||
|
||||
# initial value to use in SELECT
|
||||
set inits [list 1 1.0 1. 1e0]
|
||||
|
||||
# vals
|
||||
set vals [list\
|
||||
"922337203685477580792233720368547758079223372036854775807"\
|
||||
"100000000000000000000000000000000000000000000000000000000"\
|
||||
"1.0000000000000000000000000000000000000000000000000000000"\
|
||||
]
|
||||
|
||||
set i 1
|
||||
foreach enc $encodings {
|
||||
|
||||
file delete -force test.db
|
||||
sqlite3 db test.db
|
||||
db eval "PRAGMA encoding = \"$enc\""
|
||||
|
||||
do_test enc4-$i.1 {
|
||||
db eval {PRAGMA encoding}
|
||||
} $enc
|
||||
|
||||
set j 1
|
||||
foreach init $inits {
|
||||
|
||||
do_test enc4-$i.$j.2 {
|
||||
set S [sqlite3_prepare_v2 db "SELECT $init+?" -1 dummy]
|
||||
sqlite3_expired $S
|
||||
} {0}
|
||||
|
||||
set k 1
|
||||
foreach val $vals {
|
||||
for {set x 1} {$x<16} {incr x} {
|
||||
set part [expr $init + [string range $val 0 [expr $x-1]]]
|
||||
|
||||
do_realnum_test enc4-$i.$j.$k.3.$x {
|
||||
sqlite3_reset $S
|
||||
sqlite3_bind_text $S 1 $val $x
|
||||
sqlite3_step $S
|
||||
sqlite3_column_text $S 0
|
||||
} [list $part]
|
||||
|
||||
do_realnum_test enc4-$i.$j.$k.4.$x {
|
||||
sqlite3_reset $S
|
||||
sqlite3_bind_text16 $S 1 [encoding convertto unicode $val] [expr $x*2]
|
||||
sqlite3_step $S
|
||||
sqlite3_column_text $S 0
|
||||
} [list $part]
|
||||
}
|
||||
|
||||
incr k
|
||||
}
|
||||
|
||||
do_test enc4-$i.$j.5 {
|
||||
sqlite3_finalize $S
|
||||
} {SQLITE_OK}
|
||||
|
||||
incr j
|
||||
}
|
||||
|
||||
db close
|
||||
incr i
|
||||
}
|
||||
|
||||
file delete -force test.db
|
||||
sqlite3 db test.db
|
||||
|
||||
do_test enc4-4.1 {
|
||||
db eval "select 1+1."
|
||||
} {2.0}
|
||||
|
||||
do_test enc4-4.2.1 {
|
||||
set S [sqlite3_prepare_v2 db "SELECT 1+1." -1 dummy]
|
||||
sqlite3_step $S
|
||||
sqlite3_column_text $S 0
|
||||
} {2.0}
|
||||
|
||||
do_test enc4-4.2.2 {
|
||||
sqlite3_finalize $S
|
||||
} {SQLITE_OK}
|
||||
|
||||
do_test enc4-4.3.1 {
|
||||
set S [sqlite3_prepare_v2 db "SELECT 1+?" -1 dummy]
|
||||
sqlite3_bind_text $S 1 "1." 2
|
||||
sqlite3_step $S
|
||||
sqlite3_column_text $S 0
|
||||
} {2.0}
|
||||
|
||||
do_test enc4-4.3.2 {
|
||||
sqlite3_finalize $S
|
||||
} {SQLITE_OK}
|
||||
|
||||
do_test enc4-4.4.1 {
|
||||
set S [sqlite3_prepare_v2 db "SELECT 1+?" -1 dummy]
|
||||
sqlite3_bind_text $S 1 "1.0" 2
|
||||
sqlite3_step $S
|
||||
sqlite3_column_text $S 0
|
||||
} {2.0}
|
||||
|
||||
do_test enc4-4.4.2 {
|
||||
sqlite3_finalize $S
|
||||
} {SQLITE_OK}
|
||||
|
||||
db close
|
||||
|
||||
finish_test
|
||||
|
||||
111
test/expr.test
111
test/expr.test
@ -32,6 +32,11 @@ proc test_expr {name settings expr result} {
|
||||
execsql {BEGIN; UPDATE test1 SET %s; SELECT %s FROM test1; ROLLBACK;}
|
||||
} $settings $expr] $result
|
||||
}
|
||||
proc test_realnum_expr {name settings expr result} {
|
||||
do_realnum_test $name [format {
|
||||
execsql {BEGIN; UPDATE test1 SET %s; SELECT %s FROM test1; ROLLBACK;}
|
||||
} $settings $expr] $result
|
||||
}
|
||||
|
||||
test_expr expr-1.1 {i1=10, i2=20} {i1+i2} 30
|
||||
test_expr expr-1.2 {i1=10, i2=20} {i1-i2} -10
|
||||
@ -164,7 +169,7 @@ ifcapable floatingpoint {
|
||||
}
|
||||
|
||||
if {[working_64bit_int]} {
|
||||
test_expr expr-1.106 {i1=0} {-9223372036854775808/-1} 9.22337203685478e+18
|
||||
test_realnum_expr expr-1.106 {i1=0} {-9223372036854775808/-1} 9.22337203685478e+18
|
||||
}
|
||||
|
||||
test_expr expr-1.107 {i1=0} {-9223372036854775808%-1} 0
|
||||
@ -203,100 +208,100 @@ test_expr expr-1.126 {i1=8, i2=8} \
|
||||
ifcapable floatingpoint {if {[working_64bit_int]} {
|
||||
test_expr expr-1.200\
|
||||
{i1=9223372036854775806, i2=1} {i1+i2} 9223372036854775807
|
||||
test_expr expr-1.201\
|
||||
test_realnum_expr expr-1.201\
|
||||
{i1=9223372036854775806, i2=2} {i1+i2} 9.22337203685478e+18
|
||||
test_expr expr-1.202\
|
||||
test_realnum_expr expr-1.202\
|
||||
{i1=9223372036854775806, i2=100000} {i1+i2} 9.22337203685488e+18
|
||||
test_expr expr-1.203\
|
||||
test_realnum_expr expr-1.203\
|
||||
{i1=9223372036854775807, i2=0} {i1+i2} 9223372036854775807
|
||||
test_expr expr-1.204\
|
||||
test_realnum_expr expr-1.204\
|
||||
{i1=9223372036854775807, i2=1} {i1+i2} 9.22337203685478e+18
|
||||
test_expr expr-1.205\
|
||||
test_realnum_expr expr-1.205\
|
||||
{i2=9223372036854775806, i1=1} {i1+i2} 9223372036854775807
|
||||
test_expr expr-1.206\
|
||||
test_realnum_expr expr-1.206\
|
||||
{i2=9223372036854775806, i1=2} {i1+i2} 9.22337203685478e+18
|
||||
test_expr expr-1.207\
|
||||
test_realnum_expr expr-1.207\
|
||||
{i2=9223372036854775806, i1=100000} {i1+i2} 9.22337203685488e+18
|
||||
test_expr expr-1.208\
|
||||
test_realnum_expr expr-1.208\
|
||||
{i2=9223372036854775807, i1=0} {i1+i2} 9223372036854775807
|
||||
test_expr expr-1.209\
|
||||
test_realnum_expr expr-1.209\
|
||||
{i2=9223372036854775807, i1=1} {i1+i2} 9.22337203685478e+18
|
||||
test_expr expr-1.210\
|
||||
test_realnum_expr expr-1.210\
|
||||
{i1=-9223372036854775807, i2=-1} {i1+i2} -9223372036854775808
|
||||
test_expr expr-1.211\
|
||||
test_realnum_expr expr-1.211\
|
||||
{i1=-9223372036854775807, i2=-2} {i1+i2} -9.22337203685478e+18
|
||||
test_expr expr-1.212\
|
||||
test_realnum_expr expr-1.212\
|
||||
{i1=-9223372036854775807, i2=-100000} {i1+i2} -9.22337203685488e+18
|
||||
test_expr expr-1.213\
|
||||
test_realnum_expr expr-1.213\
|
||||
{i1=-9223372036854775808, i2=0} {i1+i2} -9223372036854775808
|
||||
test_expr expr-1.214\
|
||||
test_realnum_expr expr-1.214\
|
||||
{i1=-9223372036854775808, i2=-1} {i1+i2} -9.22337203685478e+18
|
||||
test_expr expr-1.215\
|
||||
test_realnum_expr expr-1.215\
|
||||
{i2=-9223372036854775807, i1=-1} {i1+i2} -9223372036854775808
|
||||
test_expr expr-1.216\
|
||||
test_realnum_expr expr-1.216\
|
||||
{i2=-9223372036854775807, i1=-2} {i1+i2} -9.22337203685478e+18
|
||||
test_expr expr-1.217\
|
||||
test_realnum_expr expr-1.217\
|
||||
{i2=-9223372036854775807, i1=-100000} {i1+i2} -9.22337203685488e+18
|
||||
test_expr expr-1.218\
|
||||
test_realnum_expr expr-1.218\
|
||||
{i2=-9223372036854775808, i1=0} {i1+i2} -9223372036854775808
|
||||
test_expr expr-1.219\
|
||||
test_realnum_expr expr-1.219\
|
||||
{i2=-9223372036854775808, i1=-1} {i1+i2} -9.22337203685478e+18
|
||||
test_expr expr-1.220\
|
||||
test_realnum_expr expr-1.220\
|
||||
{i1=9223372036854775806, i2=-1} {i1-i2} 9223372036854775807
|
||||
test_expr expr-1.221\
|
||||
test_realnum_expr expr-1.221\
|
||||
{i1=9223372036854775806, i2=-2} {i1-i2} 9.22337203685478e+18
|
||||
test_expr expr-1.222\
|
||||
test_realnum_expr expr-1.222\
|
||||
{i1=9223372036854775806, i2=-100000} {i1-i2} 9.22337203685488e+18
|
||||
test_expr expr-1.223\
|
||||
test_realnum_expr expr-1.223\
|
||||
{i1=9223372036854775807, i2=0} {i1-i2} 9223372036854775807
|
||||
test_expr expr-1.224\
|
||||
test_realnum_expr expr-1.224\
|
||||
{i1=9223372036854775807, i2=-1} {i1-i2} 9.22337203685478e+18
|
||||
test_expr expr-1.225\
|
||||
test_realnum_expr expr-1.225\
|
||||
{i2=-9223372036854775806, i1=1} {i1-i2} 9223372036854775807
|
||||
test_expr expr-1.226\
|
||||
test_realnum_expr expr-1.226\
|
||||
{i2=-9223372036854775806, i1=2} {i1-i2} 9.22337203685478e+18
|
||||
test_expr expr-1.227\
|
||||
test_realnum_expr expr-1.227\
|
||||
{i2=-9223372036854775806, i1=100000} {i1-i2} 9.22337203685488e+18
|
||||
test_expr expr-1.228\
|
||||
test_realnum_expr expr-1.228\
|
||||
{i2=-9223372036854775807, i1=0} {i1-i2} 9223372036854775807
|
||||
test_expr expr-1.229\
|
||||
test_realnum_expr expr-1.229\
|
||||
{i2=-9223372036854775807, i1=1} {i1-i2} 9.22337203685478e+18
|
||||
test_expr expr-1.230\
|
||||
test_realnum_expr expr-1.230\
|
||||
{i1=-9223372036854775807, i2=1} {i1-i2} -9223372036854775808
|
||||
test_expr expr-1.231\
|
||||
test_realnum_expr expr-1.231\
|
||||
{i1=-9223372036854775807, i2=2} {i1-i2} -9.22337203685478e+18
|
||||
test_expr expr-1.232\
|
||||
test_realnum_expr expr-1.232\
|
||||
{i1=-9223372036854775807, i2=100000} {i1-i2} -9.22337203685488e+18
|
||||
test_expr expr-1.233\
|
||||
test_realnum_expr expr-1.233\
|
||||
{i1=-9223372036854775808, i2=0} {i1-i2} -9223372036854775808
|
||||
test_expr expr-1.234\
|
||||
test_realnum_expr expr-1.234\
|
||||
{i1=-9223372036854775808, i2=1} {i1-i2} -9.22337203685478e+18
|
||||
test_expr expr-1.235\
|
||||
test_realnum_expr expr-1.235\
|
||||
{i2=9223372036854775807, i1=-1} {i1-i2} -9223372036854775808
|
||||
test_expr expr-1.236\
|
||||
test_realnum_expr expr-1.236\
|
||||
{i2=9223372036854775807, i1=-2} {i1-i2} -9.22337203685478e+18
|
||||
test_expr expr-1.237\
|
||||
test_realnum_expr expr-1.237\
|
||||
{i2=9223372036854775807, i1=-100000} {i1-i2} -9.22337203685488e+18
|
||||
test_expr expr-1.238\
|
||||
test_realnum_expr expr-1.238\
|
||||
{i2=9223372036854775807, i1=0} {i1-i2} -9223372036854775807
|
||||
test_expr expr-1.239\
|
||||
test_realnum_expr expr-1.239\
|
||||
{i2=9223372036854775807, i1=-1} {i1-i2} -9223372036854775808
|
||||
|
||||
test_expr expr-1.250\
|
||||
test_realnum_expr expr-1.250\
|
||||
{i1=4294967296, i2=2147483648} {i1*i2} 9.22337203685478e+18
|
||||
test_expr expr-1.251\
|
||||
test_realnum_expr expr-1.251\
|
||||
{i1=4294967296, i2=2147483647} {i1*i2} 9223372032559808512
|
||||
test_expr expr-1.252\
|
||||
test_realnum_expr expr-1.252\
|
||||
{i1=-4294967296, i2=2147483648} {i1*i2} -9223372036854775808
|
||||
test_expr expr-1.253\
|
||||
test_realnum_expr expr-1.253\
|
||||
{i1=-4294967296, i2=2147483647} {i1*i2} -9223372032559808512
|
||||
test_expr expr-1.254\
|
||||
test_realnum_expr expr-1.254\
|
||||
{i1=4294967296, i2=-2147483648} {i1*i2} -9223372036854775808
|
||||
test_expr expr-1.255\
|
||||
test_realnum_expr expr-1.255\
|
||||
{i1=4294967296, i2=-2147483647} {i1*i2} -9223372032559808512
|
||||
test_expr expr-1.256\
|
||||
test_realnum_expr expr-1.256\
|
||||
{i1=-4294967296, i2=-2147483648} {i1*i2} 9.22337203685478e+18
|
||||
test_expr expr-1.257\
|
||||
test_realnum_expr expr-1.257\
|
||||
{i1=-4294967296, i2=-2147483647} {i1*i2} 9223372032559808512
|
||||
|
||||
}}
|
||||
@ -883,7 +888,7 @@ do_test expr-12.2 {
|
||||
} {1 {near ")": syntax error}}
|
||||
|
||||
ifcapable floatingpoint {
|
||||
do_test expr-13.1 {
|
||||
do_realnum_test expr-13.1 {
|
||||
execsql {
|
||||
SELECT 12345678901234567890;
|
||||
}
|
||||
@ -908,12 +913,12 @@ if {[working_64bit_int]} {
|
||||
# If the value is too large, use String->Float conversion.
|
||||
#
|
||||
ifcapable floatingpoint {
|
||||
do_test expr-13.4 {
|
||||
do_realnum_test expr-13.4 {
|
||||
execsql {
|
||||
SELECT 0+'9223372036854775808'
|
||||
}
|
||||
} {9.22337203685478e+18}
|
||||
do_test expr-13.5 {
|
||||
do_realnum_test expr-13.5 {
|
||||
execsql {
|
||||
SELECT '9223372036854775808'+0
|
||||
}
|
||||
@ -923,12 +928,12 @@ ifcapable floatingpoint {
|
||||
# Use String->float conversion if the value is explicitly a floating
|
||||
# point value.
|
||||
#
|
||||
do_test expr-13.6 {
|
||||
do_realnum_test expr-13.6 {
|
||||
execsql {
|
||||
SELECT 0+'9223372036854775807.0'
|
||||
}
|
||||
} {9.22337203685478e+18}
|
||||
do_test expr-13.7 {
|
||||
do_realnum_test expr-13.7 {
|
||||
execsql {
|
||||
SELECT '9223372036854775807.0'+0
|
||||
}
|
||||
|
||||
106
test/fkey3.test
106
test/fkey3.test
@ -21,6 +21,8 @@ ifcapable {!foreignkey||!trigger} {
|
||||
return
|
||||
}
|
||||
|
||||
set testprefix fkey3
|
||||
|
||||
# Create a table and some data to work with.
|
||||
#
|
||||
do_test fkey3-1.1 {
|
||||
@ -77,4 +79,108 @@ do_test fkey3-2.1 {
|
||||
}
|
||||
} {1 100 1 101 2 100 2 101}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# The following tests - fkey-3.* - test some edge cases to do with
|
||||
# inserting rows into tables that have foreign keys where the parent
|
||||
# table is the same as the child table. Especially cases where the
|
||||
# new row being inserted matches itself.
|
||||
#
|
||||
do_execsql_test 3.1.1 {
|
||||
CREATE TABLE t3(a, b, c, d,
|
||||
UNIQUE(a, b),
|
||||
FOREIGN KEY(c, d) REFERENCES t3(a, b)
|
||||
);
|
||||
INSERT INTO t3 VALUES(1, 2, 1, 2);
|
||||
} {}
|
||||
do_catchsql_test 3.1.2 {
|
||||
INSERT INTO t3 VALUES(NULL, 2, 5, 2);
|
||||
} {1 {foreign key constraint failed}}
|
||||
do_catchsql_test 3.1.3 {
|
||||
INSERT INTO t3 VALUES(NULL, 3, 5, 2);
|
||||
} {1 {foreign key constraint failed}}
|
||||
|
||||
do_execsql_test 3.2.1 {
|
||||
CREATE TABLE t4(a UNIQUE, b REFERENCES t4(a));
|
||||
}
|
||||
do_catchsql_test 3.2.2 {
|
||||
INSERT INTO t4 VALUES(NULL, 1);
|
||||
} {1 {foreign key constraint failed}}
|
||||
|
||||
do_execsql_test 3.3.1 {
|
||||
CREATE TABLE t5(a INTEGER PRIMARY KEY, b REFERENCES t5(a));
|
||||
INSERT INTO t5 VALUES(NULL, 1);
|
||||
} {}
|
||||
do_catchsql_test 3.3.2 {
|
||||
INSERT INTO t5 VALUES(NULL, 3);
|
||||
} {1 {foreign key constraint failed}}
|
||||
|
||||
do_execsql_test 3.4.1 {
|
||||
CREATE TABLE t6(a INTEGER PRIMARY KEY, b, c, d,
|
||||
FOREIGN KEY(c, d) REFERENCES t6(a, b)
|
||||
);
|
||||
CREATE UNIQUE INDEX t6i ON t6(b, a);
|
||||
}
|
||||
do_execsql_test 3.4.2 { INSERT INTO t6 VALUES(NULL, 'a', 1, 'a'); } {}
|
||||
do_execsql_test 3.4.3 { INSERT INTO t6 VALUES(2, 'a', 2, 'a'); } {}
|
||||
do_execsql_test 3.4.4 { INSERT INTO t6 VALUES(NULL, 'a', 1, 'a'); } {}
|
||||
do_execsql_test 3.4.5 { INSERT INTO t6 VALUES(5, 'a', 2, 'a'); } {}
|
||||
do_catchsql_test 3.4.6 {
|
||||
INSERT INTO t6 VALUES(NULL, 'a', 65, 'a');
|
||||
} {1 {foreign key constraint failed}}
|
||||
|
||||
do_execsql_test 3.4.7 {
|
||||
INSERT INTO t6 VALUES(100, 'one', 100, 'one');
|
||||
DELETE FROM t6 WHERE a = 100;
|
||||
}
|
||||
do_execsql_test 3.4.8 {
|
||||
INSERT INTO t6 VALUES(100, 'one', 100, 'one');
|
||||
UPDATE t6 SET c = 1, d = 'a' WHERE a = 100;
|
||||
DELETE FROM t6 WHERE a = 100;
|
||||
}
|
||||
|
||||
do_execsql_test 3.5.1 {
|
||||
CREATE TABLE t7(a, b, c, d INTEGER PRIMARY KEY,
|
||||
FOREIGN KEY(c, d) REFERENCES t7(a, b)
|
||||
);
|
||||
CREATE UNIQUE INDEX t7i ON t7(a, b);
|
||||
}
|
||||
do_execsql_test 3.5.2 { INSERT INTO t7 VALUES('x', 1, 'x', NULL) } {}
|
||||
do_execsql_test 3.5.3 { INSERT INTO t7 VALUES('x', 2, 'x', 2) } {}
|
||||
do_catchsql_test 3.5.4 {
|
||||
INSERT INTO t7 VALUES('x', 450, 'x', NULL);
|
||||
} {1 {foreign key constraint failed}}
|
||||
do_catchsql_test 3.5.5 {
|
||||
INSERT INTO t7 VALUES('x', 450, 'x', 451);
|
||||
} {1 {foreign key constraint failed}}
|
||||
|
||||
|
||||
do_execsql_test 3.6.1 {
|
||||
CREATE TABLE t8(a, b, c, d, e, FOREIGN KEY(c, d) REFERENCES t8(a, b));
|
||||
CREATE UNIQUE INDEX t8i1 ON t8(a, b);
|
||||
CREATE UNIQUE INDEX t8i2 ON t8(c);
|
||||
INSERT INTO t8 VALUES(1, 1, 1, 1, 1);
|
||||
}
|
||||
do_catchsql_test 3.6.2 {
|
||||
UPDATE t8 SET d = 2;
|
||||
} {1 {foreign key constraint failed}}
|
||||
do_execsql_test 3.6.3 { UPDATE t8 SET d = 1; }
|
||||
do_execsql_test 3.6.4 { UPDATE t8 SET e = 2; }
|
||||
|
||||
do_catchsql_test 3.6.5 {
|
||||
CREATE TABLE TestTable (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name text,
|
||||
source_id integer not null,
|
||||
parent_id integer,
|
||||
|
||||
foreign key(source_id, parent_id) references TestTable(source_id, id)
|
||||
);
|
||||
CREATE UNIQUE INDEX testindex on TestTable(source_id, id);
|
||||
PRAGMA foreign_keys=1;
|
||||
INSERT INTO TestTable VALUES (1, 'parent', 1, null);
|
||||
INSERT INTO TestTable VALUES (2, 'child', 1, 1);
|
||||
UPDATE TestTable SET parent_id=1000 where id=2;
|
||||
} {1 {foreign key constraint failed}}
|
||||
|
||||
finish_test
|
||||
|
||||
@ -24,6 +24,8 @@ ifcapable !fts3 {
|
||||
return
|
||||
}
|
||||
|
||||
set ::testprefix fts3token
|
||||
|
||||
proc escape_string {str} {
|
||||
set out ""
|
||||
foreach char [split $str ""] {
|
||||
@ -165,10 +167,21 @@ ifcapable icu {
|
||||
do_icu_test fts3token-4.6 MiddleOfTheOcean $input $output
|
||||
do_icu_test fts3token-4.7 th_TH $input $output
|
||||
do_icu_test fts3token-4.8 en_US $input $output
|
||||
|
||||
do_execsql_test 5.1 {
|
||||
CREATE VIRTUAL TABLE x1 USING fts3(name,TOKENIZE icu en_US);
|
||||
insert into x1 (name) values (NULL);
|
||||
insert into x1 (name) values (NULL);
|
||||
delete from x1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
do_test fts3token-internal {
|
||||
execsql { SELECT fts3_tokenizer_internal_test() }
|
||||
} {ok}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
||||
658
test/fts3auto.test
Normal file
658
test/fts3auto.test
Normal file
@ -0,0 +1,658 @@
|
||||
# 2011 June 10
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If this build does not include FTS3, skip the tests in this file.
|
||||
#
|
||||
ifcapable !fts3 { finish_test ; return }
|
||||
source $testdir/fts3_common.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
|
||||
set testprefix fts3auto
|
||||
set sfep $sqlite_fts3_enable_parentheses
|
||||
set sqlite_fts3_enable_parentheses 1
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# Start of Tcl infrastructure used by tests. The entry points are:
|
||||
#
|
||||
# do_fts3query_test
|
||||
# fts3_make_deferrable
|
||||
# fts3_zero_long_segments
|
||||
#
|
||||
|
||||
#
|
||||
# do_fts3query_test TESTNAME ?OPTIONS? TABLE MATCHEXPR
|
||||
#
|
||||
# This proc runs several test cases on FTS3/4 table $TABLE using match
|
||||
# expression $MATCHEXPR. All documents in $TABLE must be formatted so that
|
||||
# they can be "tokenized" using the Tcl list commands (llength, lindex etc.).
|
||||
# The name and column names used by $TABLE must not require any quoting or
|
||||
# escaping when used in SQL statements.
|
||||
#
|
||||
# $MATCHINFO may be any expression accepted by the FTS4 MATCH operator,
|
||||
# except that the "<column-name>:token" syntax is not supported. Tcl list
|
||||
# commands are used to tokenize the expression. Any parenthesis must appear
|
||||
# either as separate list elements, or as the first (for opening) or last
|
||||
# (for closing) character of a list element. i.e. the expression "(a OR b)c"
|
||||
# will not be parsed correctly, but "( a OR b) c" will.
|
||||
#
|
||||
# Available OPTIONS are:
|
||||
#
|
||||
# -deferred TOKENLIST
|
||||
#
|
||||
# If the "deferred" option is supplied, it is passed a list of tokens that
|
||||
# are deferred by FTS and result in the relevant matchinfo() stats being an
|
||||
# approximation.
|
||||
#
|
||||
set sqlite_fts3_enable_parentheses 1
|
||||
proc do_fts3query_test {tn args} {
|
||||
|
||||
set nArg [llength $args]
|
||||
if {$nArg < 2 || ($nArg % 2)} {
|
||||
set cmd do_fts3query_test
|
||||
error "wrong # args: should be \"$cmd ?-deferred LIST? TABLE MATCHEXPR\""
|
||||
}
|
||||
set tbl [lindex $args [expr $nArg-2]]
|
||||
set match [lindex $args [expr $nArg-1]]
|
||||
set deferred [list]
|
||||
|
||||
foreach {k v} [lrange $args 0 [expr $nArg-3]] {
|
||||
switch -- $k {
|
||||
-deferred {
|
||||
set deferred $v
|
||||
}
|
||||
default {
|
||||
error "bad option \"$k\": must be -deferred"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get_near_results $tbl $match $deferred aMatchinfo
|
||||
|
||||
set matchinfo_asc [list]
|
||||
foreach docid [lsort -integer -incr [array names aMatchinfo]] {
|
||||
lappend matchinfo_asc $docid $aMatchinfo($docid)
|
||||
}
|
||||
set matchinfo_desc [list]
|
||||
foreach docid [lsort -integer -decr [array names aMatchinfo]] {
|
||||
lappend matchinfo_desc $docid $aMatchinfo($docid)
|
||||
}
|
||||
|
||||
set title "(\"$match\" -> [llength [array names aMatchinfo]] rows)"
|
||||
|
||||
do_execsql_test $tn$title.1 "
|
||||
SELECT docid FROM $tbl WHERE $tbl MATCH '$match' ORDER BY docid ASC
|
||||
" [lsort -integer -incr [array names aMatchinfo]]
|
||||
|
||||
do_execsql_test $tn$title.2 "
|
||||
SELECT docid FROM $tbl WHERE $tbl MATCH '$match' ORDER BY docid DESC
|
||||
" [lsort -integer -decr [array names aMatchinfo]]
|
||||
|
||||
do_execsql_test $tn$title.3 "
|
||||
SELECT docid, mit(matchinfo($tbl, 'x')) FROM $tbl
|
||||
WHERE $tbl MATCH '$match' ORDER BY docid DESC
|
||||
" $matchinfo_desc
|
||||
|
||||
do_execsql_test $tn$title.4 "
|
||||
SELECT docid, mit(matchinfo($tbl, 'x')) FROM $tbl
|
||||
WHERE $tbl MATCH '$match' ORDER BY docid ASC
|
||||
" $matchinfo_asc
|
||||
}
|
||||
|
||||
# fts3_make_deferrable TABLE TOKEN
|
||||
#
|
||||
proc fts3_make_deferrable {tbl token} {
|
||||
|
||||
set stmt [sqlite3_prepare db "SELECT * FROM $tbl" -1 dummy]
|
||||
set name [sqlite3_column_name $stmt 0]
|
||||
sqlite3_finalize $stmt
|
||||
|
||||
set nRow [db one "SELECT count(*) FROM $tbl"]
|
||||
set pgsz [db one "PRAGMA page_size"]
|
||||
execsql BEGIN
|
||||
for {set i 0} {$i < ($nRow * $pgsz * 1.2)/100} {incr i} {
|
||||
set doc [string repeat "$token " 100]
|
||||
execsql "INSERT INTO $tbl ($name) VALUES(\$doc)"
|
||||
}
|
||||
execsql "INSERT INTO $tbl ($name) VALUES('aaaaaaa ${token}aaaaa')"
|
||||
execsql COMMIT
|
||||
|
||||
return [expr $nRow*$pgsz]
|
||||
}
|
||||
|
||||
# fts3_zero_long_segments TABLE ?LIMIT?
|
||||
#
|
||||
proc fts3_zero_long_segments {tbl limit} {
|
||||
execsql "
|
||||
UPDATE ${tbl}_segments
|
||||
SET block = zeroblob(length(block))
|
||||
WHERE length(block)>$limit
|
||||
"
|
||||
return [db changes]
|
||||
}
|
||||
|
||||
|
||||
proc mit {blob} {
|
||||
set scan(littleEndian) i*
|
||||
set scan(bigEndian) I*
|
||||
binary scan $blob $scan($::tcl_platform(byteOrder)) r
|
||||
return $r
|
||||
}
|
||||
db func mit mit
|
||||
|
||||
proc fix_phrase_expr {cols expr colfiltervar} {
|
||||
upvar $colfiltervar iColFilter
|
||||
|
||||
set out [list]
|
||||
foreach t $expr {
|
||||
if {[string match *:* $t]} {
|
||||
set col [lindex [split $t :] 0]
|
||||
set t [lindex [split $t :] 1]
|
||||
set iCol [lsearch $cols $col]
|
||||
if {$iCol<0} { error "unknown column: $col" }
|
||||
if {$iColFilter < 0} {
|
||||
set iColFilter $iCol
|
||||
} elseif {$iColFilter != $iCol} {
|
||||
set iColFilter [llength $cols]
|
||||
}
|
||||
}
|
||||
lappend out $t
|
||||
}
|
||||
|
||||
return $out
|
||||
}
|
||||
|
||||
proc fix_near_expr {cols expr colfiltervar} {
|
||||
upvar $colfiltervar iColFilter
|
||||
|
||||
set iColFilter -1
|
||||
|
||||
set out [list]
|
||||
lappend out [fix_phrase_expr $cols [lindex $expr 0] iColFilter]
|
||||
foreach {a b} [lrange $expr 1 end] {
|
||||
if {[string match -nocase near $a]} { set a 10 }
|
||||
if {[string match -nocase near/* $a]} { set a [string range $a 5 end] }
|
||||
lappend out $a
|
||||
lappend out [fix_phrase_expr $cols $b iColFilter]
|
||||
}
|
||||
return $out
|
||||
}
|
||||
|
||||
proc get_single_near_results {tbl expr deferred arrayvar nullvar} {
|
||||
upvar $arrayvar aMatchinfo
|
||||
upvar $nullvar nullentry
|
||||
catch {array unset aMatchinfo}
|
||||
|
||||
set cols [list]
|
||||
set miss [list]
|
||||
db eval "PRAGMA table_info($tbl)" A { lappend cols $A(name) ; lappend miss 0 }
|
||||
set expr [fix_near_expr $cols $expr iColFilter]
|
||||
|
||||
# Calculate the expected results using [fts3_near_match]. The following
|
||||
# loop populates the "hits" and "counts" arrays as follows:
|
||||
#
|
||||
# 1. For each document in the table that matches the NEAR expression,
|
||||
# hits($docid) is set to 1. The set of docids that match the expression
|
||||
# can therefore be found using [array names hits].
|
||||
#
|
||||
# 2. For each column of each document in the table, counts($docid,$iCol)
|
||||
# is set to the -phrasecountvar output.
|
||||
#
|
||||
set res [list]
|
||||
catch { array unset hits }
|
||||
db eval "SELECT docid, * FROM $tbl" d {
|
||||
set iCol 0
|
||||
foreach col [lrange $d(*) 1 end] {
|
||||
set docid $d(docid)
|
||||
if {$iColFilter<0 || $iCol==$iColFilter} {
|
||||
set hit [fts3_near_match $d($col) $expr -p counts($docid,$iCol)]
|
||||
if {$hit} { set hits($docid) 1 }
|
||||
} else {
|
||||
set counts($docid,$iCol) $miss
|
||||
}
|
||||
incr iCol
|
||||
}
|
||||
}
|
||||
set nPhrase [expr ([llength $expr]+1)/2]
|
||||
set nCol $iCol
|
||||
|
||||
# This block populates the nHit and nDoc arrays. For each phrase/column
|
||||
# in the query/table, array elements are set as follows:
|
||||
#
|
||||
# nHit($iPhrase,$iCol) - Total number of hits for phrase $iPhrase in
|
||||
# column $iCol.
|
||||
#
|
||||
# nDoc($iPhrase,$iCol) - Number of documents with at least one hit for
|
||||
# phrase $iPhrase in column $iCol.
|
||||
#
|
||||
for {set iPhrase 0} {$iPhrase < $nPhrase} {incr iPhrase} {
|
||||
for {set iCol 0} {$iCol < $nCol} {incr iCol} {
|
||||
set nHit($iPhrase,$iCol) 0
|
||||
set nDoc($iPhrase,$iCol) 0
|
||||
}
|
||||
}
|
||||
foreach key [array names counts] {
|
||||
set iCol [lindex [split $key ,] 1]
|
||||
set iPhrase 0
|
||||
foreach c $counts($key) {
|
||||
if {$c>0} { incr nDoc($iPhrase,$iCol) 1 }
|
||||
incr nHit($iPhrase,$iCol) $c
|
||||
incr iPhrase
|
||||
}
|
||||
}
|
||||
|
||||
if {[llength $deferred] && [llength $expr]==1} {
|
||||
set phrase [lindex $expr 0]
|
||||
set rewritten [list]
|
||||
set partial 0
|
||||
foreach tok $phrase {
|
||||
if {[lsearch $deferred $tok]>=0} {
|
||||
lappend rewritten *
|
||||
} else {
|
||||
lappend rewritten $tok
|
||||
set partial 1
|
||||
}
|
||||
}
|
||||
if {$partial==0} {
|
||||
set tblsize [db one "SELECT count(*) FROM $tbl"]
|
||||
for {set iCol 0} {$iCol < $nCol} {incr iCol} {
|
||||
set nHit(0,$iCol) $tblsize
|
||||
set nDoc(0,$iCol) $tblsize
|
||||
}
|
||||
} elseif {$rewritten != $phrase} {
|
||||
while {[lindex $rewritten end] == "*"} {
|
||||
set rewritten [lrange $rewritten 0 end-1]
|
||||
}
|
||||
while {[lindex $rewritten 0] == "*"} {
|
||||
set rewritten [lrange $rewritten 1 end]
|
||||
}
|
||||
get_single_near_results $tbl [list $rewritten] {} aRewrite nullentry
|
||||
foreach docid [array names hits] {
|
||||
set aMatchinfo($docid) $aRewrite($docid)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
# Set up the aMatchinfo array. For each document, set aMatchinfo($docid) to
|
||||
# contain the output of matchinfo('x') for the document.
|
||||
#
|
||||
foreach docid [array names hits] {
|
||||
set mi [list]
|
||||
for {set iPhrase 0} {$iPhrase<$nPhrase} {incr iPhrase} {
|
||||
for {set iCol 0} {$iCol<$nCol} {incr iCol} {
|
||||
lappend mi [lindex $counts($docid,$iCol) $iPhrase]
|
||||
lappend mi $nHit($iPhrase,$iCol)
|
||||
lappend mi $nDoc($iPhrase,$iCol)
|
||||
}
|
||||
}
|
||||
set aMatchinfo($docid) $mi
|
||||
}
|
||||
|
||||
# Set up the nullentry output.
|
||||
#
|
||||
set nullentry [list]
|
||||
for {set iPhrase 0} {$iPhrase<$nPhrase} {incr iPhrase} {
|
||||
for {set iCol 0} {$iCol<$nCol} {incr iCol} {
|
||||
lappend nullentry 0 $nHit($iPhrase,$iCol) $nDoc($iPhrase,$iCol)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
proc matching_brackets {expr} {
|
||||
if {[string range $expr 0 0]!="(" || [string range $expr end end] !=")"} {
|
||||
return 0
|
||||
}
|
||||
|
||||
set iBracket 1
|
||||
set nExpr [string length $expr]
|
||||
for {set i 1} {$iBracket && $i < $nExpr} {incr i} {
|
||||
set c [string range $expr $i $i]
|
||||
if {$c == "("} {incr iBracket}
|
||||
if {$c == ")"} {incr iBracket -1}
|
||||
}
|
||||
|
||||
return [expr ($iBracket==0 && $i==$nExpr)]
|
||||
}
|
||||
|
||||
proc get_near_results {tbl expr deferred arrayvar {nullvar ""}} {
|
||||
upvar $arrayvar aMatchinfo
|
||||
if {$nullvar != ""} { upvar $nullvar nullentry }
|
||||
|
||||
set expr [string trim $expr]
|
||||
while { [matching_brackets $expr] } {
|
||||
set expr [string trim [string range $expr 1 end-1]]
|
||||
}
|
||||
|
||||
set prec(NOT) 1
|
||||
set prec(AND) 2
|
||||
set prec(OR) 3
|
||||
|
||||
set currentprec 0
|
||||
set iBracket 0
|
||||
set expr_length [llength $expr]
|
||||
for {set i 0} {$i < $expr_length} {incr i} {
|
||||
set op [lindex $expr $i]
|
||||
if {$iBracket==0 && [info exists prec($op)] && $prec($op)>=$currentprec } {
|
||||
set opidx $i
|
||||
set currentprec $prec($op)
|
||||
} else {
|
||||
for {set j 0} {$j < [string length $op]} {incr j} {
|
||||
set c [string range $op $j $j]
|
||||
if {$c == "("} { incr iBracket +1 }
|
||||
if {$c == ")"} { incr iBracket -1 }
|
||||
}
|
||||
}
|
||||
}
|
||||
if {$iBracket!=0} { error "mismatched brackets in: $expr" }
|
||||
|
||||
if {[info exists opidx]==0} {
|
||||
get_single_near_results $tbl $expr $deferred aMatchinfo nullentry
|
||||
} else {
|
||||
set eLeft [lrange $expr 0 [expr $opidx-1]]
|
||||
set eRight [lrange $expr [expr $opidx+1] end]
|
||||
|
||||
get_near_results $tbl $eLeft $deferred aLeft nullleft
|
||||
get_near_results $tbl $eRight $deferred aRight nullright
|
||||
|
||||
switch -- [lindex $expr $opidx] {
|
||||
"NOT" {
|
||||
foreach hit [array names aLeft] {
|
||||
if {0==[info exists aRight($hit)]} {
|
||||
set aMatchinfo($hit) $aLeft($hit)
|
||||
}
|
||||
}
|
||||
set nullentry $nullleft
|
||||
}
|
||||
|
||||
"AND" {
|
||||
foreach hit [array names aLeft] {
|
||||
if {[info exists aRight($hit)]} {
|
||||
set aMatchinfo($hit) [concat $aLeft($hit) $aRight($hit)]
|
||||
}
|
||||
}
|
||||
set nullentry [concat $nullleft $nullright]
|
||||
}
|
||||
|
||||
"OR" {
|
||||
foreach hit [array names aLeft] {
|
||||
if {[info exists aRight($hit)]} {
|
||||
set aMatchinfo($hit) [concat $aLeft($hit) $aRight($hit)]
|
||||
unset aRight($hit)
|
||||
} else {
|
||||
set aMatchinfo($hit) [concat $aLeft($hit) $nullright]
|
||||
}
|
||||
}
|
||||
foreach hit [array names aRight] {
|
||||
set aMatchinfo($hit) [concat $nullleft $aRight($hit)]
|
||||
}
|
||||
|
||||
set nullentry [concat $nullleft $nullright]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# End of test procs. Actual tests are below this line.
|
||||
#--------------------------------------------------------------------------
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# The following test cases - fts3auto-1.* - focus on testing the Tcl
|
||||
# command [fts3_near_match], which is used by other tests in this file.
|
||||
#
|
||||
proc test_fts3_near_match {tn doc expr res} {
|
||||
fts3_near_match $doc $expr -phrasecountvar p
|
||||
uplevel do_test [list $tn] [list [list set {} $p]] [list $res]
|
||||
}
|
||||
|
||||
test_fts3_near_match 1.1.1 {a b c a b} a {2}
|
||||
test_fts3_near_match 1.1.2 {a b c a b} {a 5 b 6 c} {2 2 1}
|
||||
test_fts3_near_match 1.1.3 {a b c a b} {"a b"} {2}
|
||||
test_fts3_near_match 1.1.4 {a b c a b} {"b c"} {1}
|
||||
test_fts3_near_match 1.1.5 {a b c a b} {"c c"} {0}
|
||||
|
||||
test_fts3_near_match 1.2.1 "a b c d e f g" {b 2 f} {0 0}
|
||||
test_fts3_near_match 1.2.2 "a b c d e f g" {b 3 f} {1 1}
|
||||
test_fts3_near_match 1.2.3 "a b c d e f g" {f 2 b} {0 0}
|
||||
test_fts3_near_match 1.2.4 "a b c d e f g" {f 3 b} {1 1}
|
||||
test_fts3_near_match 1.2.5 "a b c d e f g" {"a b" 2 "f g"} {0 0}
|
||||
test_fts3_near_match 1.2.6 "a b c d e f g" {"a b" 3 "f g"} {1 1}
|
||||
|
||||
set A "a b c d e f g h i j k l m n o p q r s t u v w x y z"
|
||||
test_fts3_near_match 1.3.1 $A {"c d" 5 "i j" 1 "e f"} {0 0 0}
|
||||
test_fts3_near_match 1.3.2 $A {"c d" 5 "i j" 2 "e f"} {1 1 1}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# Test cases fts3auto-2.* run some simple tests using the
|
||||
# [do_fts3query_test] proc.
|
||||
#
|
||||
foreach {tn create} {
|
||||
1 "fts4(a, b)"
|
||||
2 "fts4(a, b, order=DESC)"
|
||||
3 "fts4(a, b, order=ASC)"
|
||||
4 "fts4(a, b, prefix=1)"
|
||||
5 "fts4(a, b, order=DESC, prefix=1)"
|
||||
6 "fts4(a, b, order=ASC, prefix=1)"
|
||||
} {
|
||||
do_test 2.$tn.1 {
|
||||
catchsql { DROP TABLE t1 }
|
||||
execsql "CREATE VIRTUAL TABLE t1 USING $create"
|
||||
for {set i 0} {$i<32} {incr i} {
|
||||
set doc [list]
|
||||
if {$i&0x01} {lappend doc one}
|
||||
if {$i&0x02} {lappend doc two}
|
||||
if {$i&0x04} {lappend doc three}
|
||||
if {$i&0x08} {lappend doc four}
|
||||
if {$i&0x10} {lappend doc five}
|
||||
execsql { INSERT INTO t1 VALUES($doc, null) }
|
||||
}
|
||||
} {}
|
||||
|
||||
foreach {tn2 expr} {
|
||||
1 {one}
|
||||
2 {one NEAR/1 five}
|
||||
3 {t*}
|
||||
4 {t* NEAR/0 five}
|
||||
5 {o* NEAR/1 f*}
|
||||
6 {one NEAR five NEAR two NEAR four NEAR three}
|
||||
7 {one NEAR xyz}
|
||||
8 {one OR two}
|
||||
9 {one AND two}
|
||||
10 {one NOT two}
|
||||
11 {one AND two OR three}
|
||||
12 {three OR one AND two}
|
||||
13 {(three OR one) AND two}
|
||||
14 {(three OR one) AND two NOT (five NOT four)}
|
||||
15 {"one two"}
|
||||
16 {"one two" NOT "three four"}
|
||||
} {
|
||||
do_fts3query_test 2.$tn.2.$tn2 t1 $expr
|
||||
}
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# Some test cases involving deferred tokens.
|
||||
#
|
||||
|
||||
foreach {tn create} {
|
||||
1 "fts4(x)"
|
||||
2 "fts4(x, order=DESC)"
|
||||
} {
|
||||
catchsql { DROP TABLE t1 }
|
||||
execsql "CREATE VIRTUAL TABLE t1 USING $create"
|
||||
do_execsql_test 3.$tn.1 {
|
||||
INSERT INTO t1(docid, x) VALUES(-2, 'a b c d e f g h i j k');
|
||||
INSERT INTO t1(docid, x) VALUES(-1, 'b c d e f g h i j k a');
|
||||
INSERT INTO t1(docid, x) VALUES(0, 'c d e f g h i j k a b');
|
||||
INSERT INTO t1(docid, x) VALUES(1, 'd e f g h i j k a b c');
|
||||
INSERT INTO t1(docid, x) VALUES(2, 'e f g h i j k a b c d');
|
||||
INSERT INTO t1(docid, x) VALUES(3, 'f g h i j k a b c d e');
|
||||
INSERT INTO t1(docid, x) VALUES(4, 'a c e g i k');
|
||||
INSERT INTO t1(docid, x) VALUES(5, 'a d g j');
|
||||
INSERT INTO t1(docid, x) VALUES(6, 'c a b');
|
||||
}
|
||||
|
||||
set limit [fts3_make_deferrable t1 c]
|
||||
|
||||
do_fts3query_test 3.$tn.2.1 t1 {a OR c}
|
||||
|
||||
do_test 3.$tn.3 {
|
||||
fts3_zero_long_segments t1 $limit
|
||||
} {1}
|
||||
|
||||
foreach {tn2 expr def} {
|
||||
1 {a NEAR c} {}
|
||||
2 {a AND c} c
|
||||
3 {"a c"} c
|
||||
4 {"c a"} c
|
||||
5 {"a c" NEAR/1 g} {}
|
||||
6 {"a c" NEAR/0 g} {}
|
||||
} {
|
||||
do_fts3query_test 3.$tn.4.$tn2 -deferred $def t1 $expr
|
||||
}
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
#
|
||||
foreach {tn create} {
|
||||
1 "fts4(x, y)"
|
||||
2 "fts4(x, y, order=DESC)"
|
||||
3 "fts4(x, y, order=DESC, prefix=2)"
|
||||
} {
|
||||
|
||||
execsql [subst {
|
||||
DROP TABLE t1;
|
||||
CREATE VIRTUAL TABLE t1 USING $create;
|
||||
INSERT INTO t1 VALUES('one two five four five', '');
|
||||
INSERT INTO t1 VALUES('', 'one two five four five');
|
||||
INSERT INTO t1 VALUES('one two', 'five four five');
|
||||
}]
|
||||
|
||||
do_fts3query_test 4.$tn.1.1 t1 {one AND five}
|
||||
do_fts3query_test 4.$tn.1.2 t1 {one NEAR five}
|
||||
do_fts3query_test 4.$tn.1.3 t1 {one NEAR/1 five}
|
||||
do_fts3query_test 4.$tn.1.4 t1 {one NEAR/2 five}
|
||||
do_fts3query_test 4.$tn.1.5 t1 {one NEAR/3 five}
|
||||
|
||||
do_test 4.$tn.2 {
|
||||
set limit [fts3_make_deferrable t1 five]
|
||||
execsql { INSERT INTO t1(t1) VALUES('optimize') }
|
||||
expr {[fts3_zero_long_segments t1 $limit]>0}
|
||||
} {1}
|
||||
|
||||
do_fts3query_test 4.$tn.3.1 -deferred five t1 {one AND five}
|
||||
do_fts3query_test 4.$tn.3.2 -deferred five t1 {one NEAR five}
|
||||
do_fts3query_test 4.$tn.3.3 -deferred five t1 {one NEAR/1 five}
|
||||
do_fts3query_test 4.$tn.3.4 -deferred five t1 {one NEAR/2 five}
|
||||
|
||||
do_fts3query_test 4.$tn.3.5 -deferred five t1 {one NEAR/3 five}
|
||||
|
||||
do_fts3query_test 4.$tn.4.1 -deferred fi* t1 {on* AND fi*}
|
||||
do_fts3query_test 4.$tn.4.2 -deferred fi* t1 {on* NEAR fi*}
|
||||
do_fts3query_test 4.$tn.4.3 -deferred fi* t1 {on* NEAR/1 fi*}
|
||||
do_fts3query_test 4.$tn.4.4 -deferred fi* t1 {on* NEAR/2 fi*}
|
||||
do_fts3query_test 4.$tn.4.5 -deferred fi* t1 {on* NEAR/3 fi*}
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# The following test cases - fts3auto-5.* - focus on using prefix indexes.
|
||||
#
|
||||
set chunkconfig [fts3_configure_incr_load 1 1]
|
||||
foreach {tn create pending} {
|
||||
1 "fts4(a, b)" 1
|
||||
2 "fts4(a, b, order=ASC, prefix=1)" 1
|
||||
3 "fts4(a, b, order=ASC, prefix=1,3)" 0
|
||||
4 "fts4(a, b, order=DESC, prefix=2,4)" 0
|
||||
5 "fts4(a, b, order=DESC, prefix=1)" 0
|
||||
6 "fts4(a, b, order=ASC, prefix=1,3)" 0
|
||||
} {
|
||||
|
||||
execsql [subst {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE VIRTUAL TABLE t1 USING $create;
|
||||
}]
|
||||
|
||||
if {$pending} {execsql BEGIN}
|
||||
|
||||
foreach {a b} {
|
||||
"the song of songs which is solomons"
|
||||
"let him kiss me with the kisses of his mouth for thy love is better than wine"
|
||||
"because of the savour of thy good ointments thy name is as ointment poured forth therefore do the virgins love thee"
|
||||
"draw me we will run after thee the king hath brought me into his chambers we will be glad and rejoice in thee we will remember thy love more than wine the upright love thee"
|
||||
"i am black but comely o ye daughters of jerusalem as the tents of kedar as the curtains of solomon"
|
||||
"look not upon me because i am black because the sun hath looked upon me my mothers children were angry with me they made me the keeper of the vineyards but mine own vineyard have i not kept"
|
||||
"tell me o thou whom my soul loveth where thou feedest where thou makest thy flock to rest at noon for why should i be as one that turneth aside by the flocks of thy companions?"
|
||||
"if thou know not o thou fairest among women go thy way forth by the footsteps of the flock and feed thy kids beside the shepherds tents"
|
||||
"i have compared thee o my love to a company of horses in pharaohs chariots"
|
||||
"thy cheeks are comely with rows of jewels thy neck with chains of gold"
|
||||
"we will make thee borders of gold with studs of silver"
|
||||
"while the king sitteth at his table my spikenard sendeth forth the smell thereof"
|
||||
"a bundle of myrrh is my wellbeloved unto me he shall lie all night betwixt my breasts"
|
||||
"my beloved is unto me as a cluster of camphire in the vineyards of en gedi"
|
||||
"behold thou art fair my love behold thou art fair thou hast doves eyes"
|
||||
"behold thou art fair my beloved yea pleasant also our bed is green"
|
||||
"the beams of our house are cedar and our rafters of fir"
|
||||
} {
|
||||
execsql {INSERT INTO t1(a, b) VALUES($a, $b)}
|
||||
}
|
||||
|
||||
|
||||
do_fts3query_test 5.$tn.1.1 t1 {s*}
|
||||
do_fts3query_test 5.$tn.1.2 t1 {so*}
|
||||
do_fts3query_test 5.$tn.1.3 t1 {"s* o*"}
|
||||
do_fts3query_test 5.$tn.1.4 t1 {b* NEAR/3 a*}
|
||||
do_fts3query_test 5.$tn.1.5 t1 {a*}
|
||||
do_fts3query_test 5.$tn.1.6 t1 {th* NEAR/5 a* NEAR/5 w*}
|
||||
do_fts3query_test 5.$tn.1.7 t1 {"b* th* art* fair*"}
|
||||
|
||||
if {$pending} {execsql COMMIT}
|
||||
}
|
||||
eval fts3_configure_incr_load $chunkconfig
|
||||
|
||||
foreach {tn pending create} {
|
||||
1 0 "fts4(a, b, c, d)"
|
||||
2 1 "fts4(a, b, c, d)"
|
||||
3 0 "fts4(a, b, c, d, order=DESC)"
|
||||
4 1 "fts4(a, b, c, d, order=DESC)"
|
||||
} {
|
||||
execsql [subst {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE VIRTUAL TABLE t1 USING $create;
|
||||
}]
|
||||
|
||||
|
||||
if {$pending} { execsql BEGIN }
|
||||
|
||||
foreach {a b c d} {
|
||||
"A B C" "D E F" "G H I" "J K L"
|
||||
"B C D" "E F G" "H I J" "K L A"
|
||||
"C D E" "F G H" "I J K" "L A B"
|
||||
"D E F" "G H I" "J K L" "A B C"
|
||||
"E F G" "H I J" "K L A" "B C D"
|
||||
"F G H" "I J K" "L A B" "C D E"
|
||||
} {
|
||||
execsql { INSERT INTO t1 VALUES($a, $b, $c, $d) }
|
||||
}
|
||||
|
||||
do_fts3query_test 6.$tn.1 t1 {b:G}
|
||||
do_fts3query_test 6.$tn.2 t1 {b:G AND c:I}
|
||||
do_fts3query_test 6.$tn.3 t1 {b:G NEAR c:I}
|
||||
do_fts3query_test 6.$tn.4 t1 {a:C OR b:G OR c:K OR d:C}
|
||||
do_fts3query_test 6.$tn.5 t1 {a:G OR b:G}
|
||||
|
||||
catchsql { COMMIT }
|
||||
}
|
||||
|
||||
set sqlite_fts3_enable_parentheses $sfep
|
||||
finish_test
|
||||
|
||||
@ -38,10 +38,10 @@ do_execsql_test 1.2 {
|
||||
six 1 1 three 4 6 two 1 1
|
||||
}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
DELETE FROM t1;
|
||||
do_execsql_test 1.3.1 { DELETE FROM t1; }
|
||||
do_execsql_test 1.3.2 {
|
||||
SELECT term, documents, occurrences FROM terms WHERE col = '*';
|
||||
} {}
|
||||
}
|
||||
|
||||
do_execsql_test 1.4 {
|
||||
INSERT INTO t1 VALUES('a b a b a b a');
|
||||
|
||||
139
test/fts3conf.test
Normal file
139
test/fts3conf.test
Normal file
@ -0,0 +1,139 @@
|
||||
# 2011 April 25
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#*************************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is testing the FTS3 module.
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix fts3conf
|
||||
|
||||
# If SQLITE_ENABLE_FTS3 is defined, omit this file.
|
||||
ifcapable !fts3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
proc fts3_integrity {tn db tbl} {
|
||||
|
||||
if {[sqlite3_get_autocommit $db]==0} {
|
||||
error "fts3_integrity does not work with an open transaction"
|
||||
}
|
||||
|
||||
set sql [db one {SELECT sql FROM sqlite_master WHERE name = $tbl}]
|
||||
regexp -nocase {[^(]* using (.*)} $sql -> tail
|
||||
set cols [list]
|
||||
$db eval "PRAGMA table_info($tbl)" {
|
||||
lappend cols $name
|
||||
}
|
||||
set cols [join [concat docid $cols] ,]
|
||||
|
||||
$db eval [subst {
|
||||
CREATE VIRTUAL TABLE fts3check USING fts4term($tbl);
|
||||
CREATE VIRTUAL TABLE temp.fts3check2 USING $tail;
|
||||
INSERT INTO temp.fts3check2($cols) SELECT docid, * FROM $tbl;
|
||||
CREATE VIRTUAL TABLE temp.fts3check3 USING fts4term(fts3check2);
|
||||
}]
|
||||
|
||||
set m1 [$db one {SELECT md5sum(term, docid, col, pos) FROM fts3check}]
|
||||
set m2 [$db one {SELECT md5sum(term, docid, col, pos) FROM fts3check3}]
|
||||
|
||||
$db eval {
|
||||
DROP TABLE fts3check;
|
||||
DROP TABLE temp.fts3check2;
|
||||
DROP TABLE temp.fts3check3;
|
||||
}
|
||||
|
||||
uplevel [list do_test $tn [list set {} $m1] $m2]
|
||||
}
|
||||
|
||||
do_execsql_test 1.0.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts3(x);
|
||||
INSERT INTO t1(rowid, x) VALUES(1, 'a b c d');
|
||||
INSERT INTO t1(rowid, x) VALUES(2, 'e f g h');
|
||||
|
||||
CREATE TABLE source(a, b);
|
||||
INSERT INTO source VALUES(4, 'z');
|
||||
INSERT INTO source VALUES(2, 'y');
|
||||
}
|
||||
db_save_and_close
|
||||
|
||||
set T1 "INTO t1(rowid, x) VALUES(1, 'x')"
|
||||
set T2 "INTO t1(rowid, x) SELECT * FROM source"
|
||||
|
||||
set T3 "t1 SET docid = 2 WHERE docid = 1"
|
||||
set T4 "t1 SET docid = CASE WHEN docid = 1 THEN 4 ELSE 3 END WHERE docid <=2"
|
||||
|
||||
foreach {tn sql uses constraint data} [subst {
|
||||
1 "INSERT OR ROLLBACK $T1" 0 1 {{a b c d} {e f g h}}
|
||||
2 "INSERT OR ABORT $T1" 0 1 {{a b c d} {e f g h} {i j k l}}
|
||||
3 "INSERT OR FAIL $T1" 0 1 {{a b c d} {e f g h} {i j k l}}
|
||||
4 "INSERT OR IGNORE $T1" 0 0 {{a b c d} {e f g h} {i j k l}}
|
||||
5 "INSERT OR REPLACE $T1" 0 0 {x {e f g h} {i j k l}}
|
||||
|
||||
6 "INSERT OR ROLLBACK $T2" 1 1 {{a b c d} {e f g h}}
|
||||
7 "INSERT OR ABORT $T2" 1 1 {{a b c d} {e f g h} {i j k l}}
|
||||
8 "INSERT OR FAIL $T2" 1 1 {{a b c d} {e f g h} {i j k l} z}
|
||||
9 "INSERT OR IGNORE $T2" 1 0 {{a b c d} {e f g h} {i j k l} z}
|
||||
10 "INSERT OR REPLACE $T2" 1 0 {{a b c d} y {i j k l} z}
|
||||
|
||||
11 "UPDATE OR ROLLBACK $T3" 1 1 {{a b c d} {e f g h}}
|
||||
12 "UPDATE OR ABORT $T3" 1 1 {{a b c d} {e f g h} {i j k l}}
|
||||
13 "UPDATE OR FAIL $T3" 1 1 {{a b c d} {e f g h} {i j k l}}
|
||||
14 "UPDATE OR IGNORE $T3" 1 0 {{a b c d} {e f g h} {i j k l}}
|
||||
15 "UPDATE OR REPLACE $T3" 1 0 {{a b c d} {i j k l}}
|
||||
|
||||
16 "UPDATE OR ROLLBACK $T4" 1 1 {{a b c d} {e f g h}}
|
||||
17 "UPDATE OR ABORT $T4" 1 1 {{a b c d} {e f g h} {i j k l}}
|
||||
18 "UPDATE OR FAIL $T4" 1 1 {{e f g h} {i j k l} {a b c d}}
|
||||
19 "UPDATE OR IGNORE $T4" 1 0 {{e f g h} {i j k l} {a b c d}}
|
||||
20 "UPDATE OR REPLACE $T4" 1 0 {{e f g h} {a b c d}}
|
||||
}] {
|
||||
db_restore_and_reopen
|
||||
execsql {
|
||||
BEGIN;
|
||||
INSERT INTO t1(rowid, x) VALUES(3, 'i j k l');
|
||||
}
|
||||
set R(0) {0 {}}
|
||||
set R(1) {1 {constraint failed}}
|
||||
do_catchsql_test 1.$tn.1 $sql $R($constraint)
|
||||
do_catchsql_test 1.$tn.2 { SELECT * FROM t1 } [list 0 $data]
|
||||
catchsql COMMIT
|
||||
|
||||
fts3_integrity 1.$tn.3 db t1
|
||||
|
||||
do_test 1.$tn.4 [list sql_uses_stmt db $sql] $uses
|
||||
}
|
||||
|
||||
do_execsql_test 2.1.1 {
|
||||
DELETE FROM t1;
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES('a b c');
|
||||
SAVEPOINT a;
|
||||
INSERT INTO t1 VALUES('x y z');
|
||||
ROLLBACK TO a;
|
||||
COMMIT;
|
||||
}
|
||||
fts3_integrity 2.1.2 db t1
|
||||
|
||||
do_catchsql_test 2.2.1 {
|
||||
DELETE FROM t1;
|
||||
BEGIN;
|
||||
INSERT INTO t1(docid, x) VALUES(0, 'a b c');
|
||||
INSERT INTO t1(docid, x) VALUES(1, 'a b c');
|
||||
REPLACE INTO t1(docid, x) VALUES('zero', 'd e f');
|
||||
} {1 {datatype mismatch}}
|
||||
do_execsql_test 2.2.2 { COMMIT }
|
||||
do_execsql_test 2.2.3 { SELECT * FROM t1 } {{a b c} {a b c}}
|
||||
fts3_integrity 2.2.4 db t1
|
||||
|
||||
finish_test
|
||||
@ -40,6 +40,7 @@ do_test fts3corrupt-1.2 {
|
||||
do_catchsql_test 1.3 {
|
||||
INSERT INTO t1 VALUES('world');
|
||||
} {1 {database disk image is malformed}}
|
||||
do_test 1.3.1 { sqlite3_extended_errcode db } SQLITE_CORRUPT_VTAB
|
||||
do_execsql_test 1.4 {
|
||||
DROP TABLE t1;
|
||||
}
|
||||
@ -69,6 +70,7 @@ do_test fts3corrupt-2.1 {
|
||||
do_catchsql_test 2.2 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'hello'
|
||||
} {1 {database disk image is malformed}}
|
||||
do_test 2.2.1 { sqlite3_extended_errcode db } SQLITE_CORRUPT_VTAB
|
||||
|
||||
do_execsql_test 3.0 {
|
||||
DROP TABLE t1;
|
||||
@ -86,6 +88,7 @@ do_test fts3corrupt-3.1 {
|
||||
do_catchsql_test 3.2 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'world'
|
||||
} {1 {database disk image is malformed}}
|
||||
do_test 3.2.1 { sqlite3_extended_errcode db } SQLITE_CORRUPT_VTAB
|
||||
|
||||
|
||||
do_execsql_test 4.0 {
|
||||
@ -111,6 +114,7 @@ do_catchsql_test 4.2 {
|
||||
UPDATE t1_segdir SET root = X'FFFFFFFFFFFFFFFF';
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'world';
|
||||
} {1 {database disk image is malformed}}
|
||||
do_test 4.2.1 { sqlite3_extended_errcode db } SQLITE_CORRUPT_VTAB
|
||||
|
||||
set blob [binary format cca*cca*cca*cca*cca*cca*cca*cca*cca*cca*a* \
|
||||
22 120 [string repeat a 120] \
|
||||
@ -130,6 +134,7 @@ do_catchsql_test 4.3 {
|
||||
UPDATE t1_segdir SET root = $blob;
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'world';
|
||||
} {1 {database disk image is malformed}}
|
||||
do_test 4.3.1 { sqlite3_extended_errcode db } SQLITE_CORRUPT_VTAB
|
||||
|
||||
# Test a special kind of corruption, where the %_stat table contains
|
||||
# an invalid entry. At one point this could lead to a division-by-zero
|
||||
@ -152,10 +157,12 @@ do_catchsql_test 5.2 {
|
||||
UPDATE t1_stat SET value = X'0000';
|
||||
SELECT matchinfo(t1, 'nxa') FROM t1 WHERE t1 MATCH 't*';
|
||||
} {1 {database disk image is malformed}}
|
||||
do_test 5.2.1 { sqlite3_extended_errcode db } SQLITE_CORRUPT_VTAB
|
||||
do_catchsql_test 5.3 {
|
||||
UPDATE t1_stat SET value = NULL;
|
||||
SELECT matchinfo(t1, 'nxa') FROM t1 WHERE t1 MATCH 't*';
|
||||
} {1 {database disk image is malformed}}
|
||||
do_test 5.3.1 { sqlite3_extended_errcode db } SQLITE_CORRUPT_VTAB
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@ -20,6 +20,8 @@ ifcapable !fts3 {
|
||||
|
||||
set sqlite_fts3_enable_parentheses 1
|
||||
|
||||
set fts3_simple_deferred_tokens_only 1
|
||||
|
||||
set ::testprefix fts3defer
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
@ -257,7 +259,6 @@ foreach {tn setup} {
|
||||
do_select_test 1.2 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'jk eh'
|
||||
} {100}
|
||||
if {$tn==3} breakpoint
|
||||
do_select_test 1.3 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'jk ubwrfqnbjf'
|
||||
} {7 70 98}
|
||||
@ -282,13 +283,16 @@ if {$tn==3} breakpoint
|
||||
do_select_test 1.10 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'z* vgsld'
|
||||
} {10 13 17 31 35 51 58 88 89 90 93 100}
|
||||
do_select_test 1.11 {
|
||||
SELECT rowid FROM t1
|
||||
WHERE t1 MATCH '(
|
||||
zdu OR zexh OR zf OR zhbrzadb OR zidhxhbtv OR
|
||||
zk OR zkhdvkw OR zm OR zsmhnf
|
||||
) vgsld'
|
||||
} {10 13 17 31 35 51 58 88 89 90 93 100}
|
||||
|
||||
if { $fts3_simple_deferred_tokens_only==0 } {
|
||||
do_select_test 1.11 {
|
||||
SELECT rowid FROM t1
|
||||
WHERE t1 MATCH '(
|
||||
zdu OR zexh OR zf OR zhbrzadb OR zidhxhbtv OR
|
||||
zk OR zkhdvkw OR zm OR zsmhnf
|
||||
) vgsld'
|
||||
} {10 13 17 31 35 51 58 88 89 90 93 100}
|
||||
}
|
||||
|
||||
do_select_test 2.1 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH '"zm agmckuiu"'
|
||||
@ -364,6 +368,7 @@ if {$tn==3} breakpoint
|
||||
foreach DO_MALLOC_TEST $dmt_modes {
|
||||
|
||||
# Phrase search.
|
||||
#
|
||||
do_select_test 5.$DO_MALLOC_TEST.1 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH '"jk mjpavjuhw"'
|
||||
} {8 15 36 64 67 72}
|
||||
@ -416,9 +421,11 @@ if {$tn==3} breakpoint
|
||||
do_select_test 6.2.2 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH '"zm azavwm"'
|
||||
} {15 26 92 96}
|
||||
do_select_test 6.2.3 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH '"jk xduvfhk" OR "zm azavwm"'
|
||||
} {8 15 26 92 96}
|
||||
if {$fts3_simple_deferred_tokens_only==0} {
|
||||
do_select_test 6.2.3 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH '"jk xduvfhk" OR "zm azavwm"'
|
||||
} {8 15 26 92 96}
|
||||
}
|
||||
}
|
||||
|
||||
set testprefix fts3defer
|
||||
|
||||
@ -48,6 +48,10 @@ do_execsql_test 1.1.4 {
|
||||
UPDATE t1_segments SET block = zeroblob(length(block)) WHERE length(block)>10000;
|
||||
} {2}
|
||||
|
||||
do_execsql_test 1.2.0 {
|
||||
SELECT content FROM t1 WHERE t1 MATCH 'f (e a)';
|
||||
} {{a b c d e f a x y}}
|
||||
|
||||
do_execsql_test 1.2.1 {
|
||||
SELECT content FROM t1 WHERE t1 MATCH 'f (e NEAR/2 a)';
|
||||
} {{a b c d e f a x y}}
|
||||
@ -58,7 +62,7 @@ do_execsql_test 1.2.2 {
|
||||
} [list \
|
||||
{a b c d [e] [f] [a] x y} \
|
||||
{0 1 8 1 0 0 10 1 0 2 12 1} \
|
||||
[list 3 1 1 1 1 1 8 8 1 8 8 8 5001 9]
|
||||
[list 3 1 1 1 1 1 1 1 1 1 1 8 5001 9]
|
||||
]
|
||||
|
||||
do_execsql_test 1.2.3 {
|
||||
@ -67,7 +71,7 @@ do_execsql_test 1.2.3 {
|
||||
} [list \
|
||||
{[a] b c d [e] [f] [a] x y} \
|
||||
{0 2 0 1 0 1 8 1 0 0 10 1 0 2 12 1} \
|
||||
[list 3 1 1 1 1 1 8 8 2 8 8 8 5001 9]
|
||||
[list 3 1 1 1 1 1 1 1 2 2 1 8 5001 9]
|
||||
]
|
||||
|
||||
do_execsql_test 1.3.1 { DROP TABLE t1 }
|
||||
@ -99,8 +103,14 @@ foreach {tn sql} {
|
||||
[list 2 1 1 54 54 1 3 3 54 372 7] \
|
||||
]
|
||||
|
||||
set sqlite_fts3_enable_parentheses 1
|
||||
do_execsql_test 2.2.$tn.2 {
|
||||
SELECT mit(matchinfo(t2, 'x')) FROM t2 WHERE t2 MATCH 'g z';
|
||||
} [list \
|
||||
[list 1 2 2 1 54 54] \
|
||||
]
|
||||
|
||||
set sqlite_fts3_enable_parentheses 1
|
||||
do_execsql_test 2.2.$tn.3 {
|
||||
SELECT mit(matchinfo(t2, 'x')) FROM t2 WHERE t2 MATCH 'g OR (g z)';
|
||||
} [list \
|
||||
[list 1 2 2 1 2 2 1 54 54] \
|
||||
|
||||
@ -68,7 +68,11 @@ do_execsql_test 3.1 {
|
||||
|
||||
do_execsql_test 3.2 {
|
||||
CREATE VIRTUAL TABLE xx USING FTS4;
|
||||
}
|
||||
do_execsql_test 3.3 {
|
||||
SELECT * FROM xx WHERE xx MATCH 'abc';
|
||||
}
|
||||
do_execsql_test 3.4 {
|
||||
SELECT * FROM xx WHERE xx MATCH 'a b c';
|
||||
}
|
||||
|
||||
@ -240,9 +244,13 @@ do_matchinfo_test 4.2.6 t5 {t5 MATCH 'a OR b'} { s {1 2 1} }
|
||||
|
||||
do_execsql_test 4.3.0 "INSERT INTO t5 VALUES('x y [string repeat {b } 50000]')";
|
||||
|
||||
do_matchinfo_test 4.3.1 t5 {t5 MATCH 'a a'} {
|
||||
x {{5 8 2 5 5 5} {3 8 2 3 5 5}}
|
||||
s {2 1}
|
||||
# It used to be that the second 'a' token would be deferred. That doesn't
|
||||
# work any longer.
|
||||
if 0 {
|
||||
do_matchinfo_test 4.3.1 t5 {t5 MATCH 'a a'} {
|
||||
x {{5 8 2 5 5 5} {3 8 2 3 5 5}}
|
||||
s {2 1}
|
||||
}
|
||||
}
|
||||
|
||||
do_matchinfo_test 4.3.2 t5 {t5 MATCH 'a b'} { s {2} }
|
||||
|
||||
203
test/fts3prefix.test
Normal file
203
test/fts3prefix.test
Normal file
@ -0,0 +1,203 @@
|
||||
# 2011 May 04
|
||||
#
|
||||
# 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. The
|
||||
# focus of this script is testing the FTS3 module.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix fts3prefix
|
||||
|
||||
ifcapable !fts3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# This proc tests that the prefixes index appears to represent the same content
|
||||
# as the terms index.
|
||||
#
|
||||
proc fts3_terms_and_prefixes {db tbl prefixlengths} {
|
||||
|
||||
set iIndex 0
|
||||
foreach len $prefixlengths {
|
||||
incr iIndex
|
||||
$db eval {
|
||||
DROP TABLE IF EXISTS fts3check1;
|
||||
DROP TABLE IF EXISTS fts3check2;
|
||||
}
|
||||
$db eval "CREATE VIRTUAL TABLE fts3check1 USING fts4term($tbl, 0);"
|
||||
$db eval "CREATE VIRTUAL TABLE fts3check2 USING fts4term($tbl, $iIndex);"
|
||||
|
||||
$db eval {
|
||||
DROP TABLE IF EXISTS temp.terms;
|
||||
DROP TABLE IF EXISTS temp.prefixes;
|
||||
CREATE TEMP TABLE terms AS SELECT * FROM fts3check1;
|
||||
CREATE TEMP TABLE prefixes AS SELECT * FROM fts3check2;
|
||||
CREATE INDEX temp.idx ON prefixes(term);
|
||||
DROP TABLE fts3check1;
|
||||
DROP TABLE fts3check2;
|
||||
}
|
||||
|
||||
set nExpect 0
|
||||
$db eval { SELECT term, docid, col, pos FROM temp.terms } a {
|
||||
if {[string length $a(term)]<$len} continue
|
||||
incr nExpect
|
||||
set prefix [string range $a(term) 0 [expr $len-1]]
|
||||
set r [$db one {
|
||||
SELECT count(*) FROM temp.prefixes WHERE
|
||||
term = $prefix AND docid = $a(docid) AND col = $a(col) AND pos = $a(pos)
|
||||
}]
|
||||
if {$r != 1} {
|
||||
error "$t, $a(docid), $a(col), $a(pos)"
|
||||
}
|
||||
}
|
||||
|
||||
set nCount [$db one {SELECT count(*) FROM temp.prefixes}]
|
||||
if {$nCount != $nExpect} {
|
||||
error "prefixes.count(*) is $nCount expected $nExpect"
|
||||
}
|
||||
|
||||
execsql { DROP TABLE temp.prefixes }
|
||||
execsql { DROP TABLE temp.terms }
|
||||
|
||||
set list [list]
|
||||
$db eval "
|
||||
SELECT sum( 1 << (16*(level%1024)) ) AS total, (level/1024) AS tree
|
||||
FROM ${tbl}_segdir GROUP BY tree
|
||||
" {
|
||||
lappend list [list $total $tree]
|
||||
}
|
||||
|
||||
if { [lsort -integer -index 0 $list] != [lsort -integer -index 1 $list] } {
|
||||
error "inconsistent tree structures: $list"
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
proc fts3_tap_test {tn db tbl lens} {
|
||||
uplevel [list do_test $tn [list fts3_terms_and_prefixes $db $tbl $lens] ""]
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test cases 1.* are a sanity check. They test that the prefixes index is
|
||||
# being constructed correctly for the simplest possible case.
|
||||
#
|
||||
do_execsql_test 1.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts4(prefix='1,3,6');
|
||||
|
||||
CREATE VIRTUAL TABLE p1 USING fts4term(t1, 1);
|
||||
CREATE VIRTUAL TABLE p2 USING fts4term(t1, 2);
|
||||
CREATE VIRTUAL TABLE p3 USING fts4term(t1, 3);
|
||||
CREATE VIRTUAL TABLE terms USING fts4term(t1);
|
||||
}
|
||||
do_execsql_test 1.2 {
|
||||
INSERT INTO t1 VALUES('sqlite mysql firebird');
|
||||
}
|
||||
do_execsql_test 1.3.1 { SELECT term FROM p1 } {f m s}
|
||||
do_execsql_test 1.3.2 { SELECT term FROM p2 } {fir mys sql}
|
||||
do_execsql_test 1.3.3 { SELECT term FROM p3 } {firebi sqlite}
|
||||
do_execsql_test 1.4 {
|
||||
SELECT term FROM terms;
|
||||
} {firebird mysql sqlite}
|
||||
|
||||
fts3_tap_test 1.5 db t1 {1 3 6}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# A slightly more complicated dataset. This test also verifies that DELETE
|
||||
# operations do not corrupt the prefixes index.
|
||||
#
|
||||
do_execsql_test 2.1 {
|
||||
INSERT INTO t1 VALUES('FTS3 and FTS4 are an SQLite virtual table modules');
|
||||
INSERT INTO t1 VALUES('that allows users to perform full-text searches on');
|
||||
INSERT INTO t1 VALUES('a set of documents. The most common (and');
|
||||
INSERT INTO t1 VALUES('effective) way to describe full-text searches is');
|
||||
INSERT INTO t1 VALUES('"what Google, Yahoo and Altavista do with');
|
||||
INSERT INTO t1 VALUES('documents placed on the World Wide Web". Users');
|
||||
INSERT INTO t1 VALUES('input a term, or series of terms, perhaps');
|
||||
INSERT INTO t1 VALUES('connected by a binary operator or grouped together');
|
||||
INSERT INTO t1 VALUES('into a phrase, and the full-text query system');
|
||||
INSERT INTO t1 VALUES('finds the set of documents that best matches those');
|
||||
INSERT INTO t1 VALUES('terms considering the operators and groupings the');
|
||||
INSERT INTO t1 VALUES('user has specified. This article describes the');
|
||||
INSERT INTO t1 VALUES('deployment and usage of FTS3 and FTS4.');
|
||||
INSERT INTO t1 VALUES('FTS1 and FTS2 are obsolete full-text search');
|
||||
INSERT INTO t1 VALUES('modules for SQLite. There are known issues with');
|
||||
INSERT INTO t1 VALUES('these older modules and their use should be');
|
||||
INSERT INTO t1 VALUES('avoided. Portions of the original FTS3 code were');
|
||||
INSERT INTO t1 VALUES('contributed to the SQLite project by Scott Hess of');
|
||||
INSERT INTO t1 VALUES('Google. It is now developed and maintained as part');
|
||||
INSERT INTO t1 VALUES('of SQLite. ');
|
||||
}
|
||||
fts3_tap_test 2.2 db t1 {1 3 6}
|
||||
do_execsql_test 2.3 { DELETE FROM t1 WHERE docid%2; }
|
||||
fts3_tap_test 2.4 db t1 {1 3 6}
|
||||
|
||||
do_execsql_test 2.5 { INSERT INTO t1(t1) VALUES('optimize') }
|
||||
fts3_tap_test 2.6 db t1 {1 3 6}
|
||||
|
||||
do_execsql_test 3.1 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts4(prefix='1,2,3');
|
||||
INSERT INTO t2 VALUES('On 12 September the wind direction turned and');
|
||||
INSERT INTO t2 VALUES('William''s fleet sailed. A storm blew up and the');
|
||||
INSERT INTO t2 VALUES('fleet was forced to take shelter at');
|
||||
INSERT INTO t2 VALUES('Saint-Valery-sur-Somme and again wait for the wind');
|
||||
INSERT INTO t2 VALUES('to change. On 27 September the Norman fleet');
|
||||
INSERT INTO t2 VALUES('finally set sail, landing in England at Pevensey');
|
||||
INSERT INTO t2 VALUES('Bay (Sussex) on 28 September. William then moved');
|
||||
INSERT INTO t2 VALUES('to Hastings, a few miles to the east, where he');
|
||||
INSERT INTO t2 VALUES('built a prefabricated wooden castle for a base of');
|
||||
INSERT INTO t2 VALUES('operations. From there, he ravaged the hinterland');
|
||||
INSERT INTO t2 VALUES('and waited for Harold''s return from the north.');
|
||||
INSERT INTO t2 VALUES('On 12 September the wind direction turned and');
|
||||
INSERT INTO t2 VALUES('William''s fleet sailed. A storm blew up and the');
|
||||
INSERT INTO t2 VALUES('fleet was forced to take shelter at');
|
||||
INSERT INTO t2 VALUES('Saint-Valery-sur-Somme and again wait for the wind');
|
||||
INSERT INTO t2 VALUES('to change. On 27 September the Norman fleet');
|
||||
INSERT INTO t2 VALUES('finally set sail, landing in England at Pevensey');
|
||||
INSERT INTO t2 VALUES('Bay (Sussex) on 28 September. William then moved');
|
||||
INSERT INTO t2 VALUES('to Hastings, a few miles to the east, where he');
|
||||
INSERT INTO t2 VALUES('built a prefabricated wooden castle for a base of');
|
||||
INSERT INTO t2 VALUES('operations. From there, he ravaged the hinterland');
|
||||
INSERT INTO t2 VALUES('and waited for Harold''s return from the north.');
|
||||
}
|
||||
|
||||
fts3_tap_test 3.2 db t2 {1 2 3}
|
||||
do_execsql_test 3.3 { SELECT optimize(t2) FROM t2 LIMIT 1 } {{Index optimized}}
|
||||
fts3_tap_test 3.4 db t2 {1 2 3}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Simple tests for reading the prefix-index.
|
||||
#
|
||||
do_execsql_test 4.1 {
|
||||
CREATE VIRTUAL TABLE t3 USING fts4(prefix="1,4");
|
||||
INSERT INTO t3 VALUES('one two three');
|
||||
INSERT INTO t3 VALUES('four five six');
|
||||
INSERT INTO t3 VALUES('seven eight nine');
|
||||
}
|
||||
do_execsql_test 4.2 {
|
||||
SELECT * FROM t3 WHERE t3 MATCH 'f*'
|
||||
} {{four five six}}
|
||||
do_execsql_test 4.3 {
|
||||
SELECT * FROM t3 WHERE t3 MATCH 'four*'
|
||||
} {{four five six}}
|
||||
do_execsql_test 4.4 {
|
||||
SELECT * FROM t3 WHERE t3 MATCH 's*'
|
||||
} {{four five six} {seven eight nine}}
|
||||
do_execsql_test 4.5 {
|
||||
SELECT * FROM t3 WHERE t3 MATCH 'sev*'
|
||||
} {{seven eight nine}}
|
||||
do_execsql_test 4.6 {
|
||||
SELECT * FROM t3 WHERE t3 MATCH 'one*'
|
||||
} {{one two three}}
|
||||
|
||||
finish_test
|
||||
@ -162,7 +162,7 @@ proc simple_phrase {zPrefix} {
|
||||
|
||||
# This [proc] is used to test the FTS3 matchinfo() function.
|
||||
#
|
||||
proc simple_token_matchinfo {zToken} {
|
||||
proc simple_token_matchinfo {zToken bDesc} {
|
||||
|
||||
set nDoc(0) 0
|
||||
set nDoc(1) 0
|
||||
@ -171,6 +171,8 @@ proc simple_token_matchinfo {zToken} {
|
||||
set nHit(1) 0
|
||||
set nHit(2) 0
|
||||
|
||||
set dir -inc
|
||||
if {$bDesc} { set dir -dec }
|
||||
|
||||
foreach key [array names ::t1] {
|
||||
set value $::t1($key)
|
||||
@ -184,7 +186,7 @@ proc simple_token_matchinfo {zToken} {
|
||||
}
|
||||
|
||||
set ret [list]
|
||||
foreach docid [lsort -integer [array names a]] {
|
||||
foreach docid [lsort -integer $dir [array names a]] {
|
||||
if { [lindex [lsort -integer $a($docid)] end] } {
|
||||
set matchinfo [list 1 3]
|
||||
foreach i {0 1 2} hit $a($docid) {
|
||||
@ -262,22 +264,37 @@ proc mit {blob} {
|
||||
return $r
|
||||
}
|
||||
db func mit mit
|
||||
|
||||
set sqlite_fts3_enable_parentheses 1
|
||||
|
||||
foreach nodesize {50 500 1000 2000} {
|
||||
proc do_orderbydocid_test {tn sql res} {
|
||||
uplevel [list do_select_test $tn.asc "$sql ORDER BY docid ASC" $res]
|
||||
uplevel [list do_select_test $tn.desc "$sql ORDER BY docid DESC" \
|
||||
[lsort -int -dec $res]
|
||||
]
|
||||
}
|
||||
|
||||
set NUM_TRIALS 100
|
||||
|
||||
foreach {nodesize order} {
|
||||
50 DESC
|
||||
50 ASC
|
||||
500 ASC
|
||||
1000 DESC
|
||||
2000 ASC
|
||||
} {
|
||||
catch { array unset ::t1 }
|
||||
set testname "$nodesize/$order"
|
||||
|
||||
# Create the FTS3 table. Populate it (and the Tcl array) with 100 rows.
|
||||
#
|
||||
db transaction {
|
||||
catchsql { DROP TABLE t1 }
|
||||
execsql "CREATE VIRTUAL TABLE t1 USING fts3(a, b, c)"
|
||||
execsql "CREATE VIRTUAL TABLE t1 USING fts4(a, b, c, order=$order)"
|
||||
execsql "INSERT INTO t1(t1) VALUES('nodesize=$nodesize')"
|
||||
for {set i 0} {$i < 100} {incr i} { insert_row $i }
|
||||
}
|
||||
|
||||
for {set iTest 1} {$iTest <= 100} {incr iTest} {
|
||||
for {set iTest 1} {$iTest <= $NUM_TRIALS} {incr iTest} {
|
||||
catchsql COMMIT
|
||||
|
||||
set DO_MALLOC_TEST 0
|
||||
@ -286,6 +303,8 @@ foreach nodesize {50 500 1000 2000} {
|
||||
set DO_MALLOC_TEST 1
|
||||
set nRep 2
|
||||
}
|
||||
|
||||
set ::testprefix fts3rnd-1.$testname.$iTest
|
||||
|
||||
# Delete one row, update one row and insert one row.
|
||||
#
|
||||
@ -307,7 +326,7 @@ foreach nodesize {50 500 1000 2000} {
|
||||
if {0==($iTest%2)} { execsql COMMIT }
|
||||
|
||||
if {0==($iTest%2)} {
|
||||
do_test fts3rnd-1.$nodesize.$iTest.0 { fts3_integrity_check t1 } ok
|
||||
#do_test 0 { fts3_integrity_check t1 } ok
|
||||
}
|
||||
|
||||
# Pick 10 terms from the vocabulary. Check that the results of querying
|
||||
@ -317,9 +336,14 @@ foreach nodesize {50 500 1000 2000} {
|
||||
#
|
||||
for {set i 0} {$i < 10} {incr i} {
|
||||
set term [random_term]
|
||||
do_select_test fts3rnd-1.$nodesize.$iTest.1.$i {
|
||||
do_select_test 1.$i.asc {
|
||||
SELECT docid, mit(matchinfo(t1)) FROM t1 WHERE t1 MATCH $term
|
||||
} [simple_token_matchinfo $term]
|
||||
ORDER BY docid ASC
|
||||
} [simple_token_matchinfo $term 0]
|
||||
do_select_test 1.$i.desc {
|
||||
SELECT docid, mit(matchinfo(t1)) FROM t1 WHERE t1 MATCH $term
|
||||
ORDER BY docid DESC
|
||||
} [simple_token_matchinfo $term 1]
|
||||
}
|
||||
|
||||
# This time, use the first two characters of each term as a term prefix
|
||||
@ -329,7 +353,7 @@ foreach nodesize {50 500 1000 2000} {
|
||||
for {set i 0} {$i < $nRep} {incr i} {
|
||||
set prefix [string range [random_term] 0 end-1]
|
||||
set match "${prefix}*"
|
||||
do_select_test fts3rnd-1.$nodesize.$iTest.2.$i {
|
||||
do_orderbydocid_test 2.$i {
|
||||
SELECT docid FROM t1 WHERE t1 MATCH $match
|
||||
} [simple_phrase $match]
|
||||
}
|
||||
@ -339,7 +363,7 @@ foreach nodesize {50 500 1000 2000} {
|
||||
for {set i 0} {$i < $nRep} {incr i} {
|
||||
set term [list [random_term] [random_term]]
|
||||
set match "\"$term\""
|
||||
do_select_test fts3rnd-1.$nodesize.$iTest.3.$i {
|
||||
do_orderbydocid_test 3.$i {
|
||||
SELECT docid FROM t1 WHERE t1 MATCH $match
|
||||
} [simple_phrase $term]
|
||||
}
|
||||
@ -349,7 +373,7 @@ foreach nodesize {50 500 1000 2000} {
|
||||
for {set i 0} {$i < $nRep} {incr i} {
|
||||
set term [list [random_term] [random_term] [random_term]]
|
||||
set match "\"$term\""
|
||||
do_select_test fts3rnd-1.$nodesize.$iTest.4.$i {
|
||||
do_orderbydocid_test 4.$i {
|
||||
SELECT docid FROM t1 WHERE t1 MATCH $match
|
||||
} [simple_phrase $term]
|
||||
}
|
||||
@ -362,17 +386,19 @@ foreach nodesize {50 500 1000 2000} {
|
||||
append query "[string range [random_term] 0 end-1]*"
|
||||
|
||||
set match "\"$query\""
|
||||
do_select_test fts3rnd-1.$nodesize.$iTest.5.$i {
|
||||
do_orderbydocid_test 5.$i {
|
||||
SELECT docid FROM t1 WHERE t1 MATCH $match
|
||||
} [simple_phrase $query]
|
||||
}
|
||||
|
||||
# A NEAR query with terms as the arguments.
|
||||
# A NEAR query with terms as the arguments:
|
||||
#
|
||||
# ... MATCH '$term1 NEAR $term2' ...
|
||||
#
|
||||
for {set i 0} {$i < $nRep} {incr i} {
|
||||
set terms [list [random_term] [random_term]]
|
||||
set match [join $terms " NEAR "]
|
||||
do_select_test fts3rnd-1.$nodesize.$iTest.6.$i {
|
||||
do_orderbydocid_test 6.$i {
|
||||
SELECT docid FROM t1 WHERE t1 MATCH $match
|
||||
} [simple_near $terms 10]
|
||||
}
|
||||
@ -383,7 +409,7 @@ foreach nodesize {50 500 1000 2000} {
|
||||
set terms [list [random_term] [random_term] [random_term]]
|
||||
set nNear 11
|
||||
set match [join $terms " NEAR/$nNear "]
|
||||
do_select_test fts3rnd-1.$nodesize.$iTest.7.$i {
|
||||
do_orderbydocid_test 7.$i {
|
||||
SELECT docid FROM t1 WHERE t1 MATCH $match
|
||||
} [simple_near $terms $nNear]
|
||||
}
|
||||
@ -399,7 +425,7 @@ foreach nodesize {50 500 1000 2000} {
|
||||
set term1 [random_term]
|
||||
set term2 [random_term]
|
||||
set match "$term1 $op $term2"
|
||||
do_select_test fts3rnd-1.$nodesize.$iTest.$tn.$i {
|
||||
do_orderbydocid_test $tn.$i {
|
||||
SELECT docid FROM t1 WHERE t1 MATCH $match
|
||||
} [$proc [simple_phrase $term1] [simple_phrase $term2]]
|
||||
}
|
||||
@ -408,9 +434,9 @@ foreach nodesize {50 500 1000 2000} {
|
||||
# Set operations on NEAR queries.
|
||||
#
|
||||
foreach {tn op proc} {
|
||||
8 OR setop_or
|
||||
9 NOT setop_not
|
||||
10 AND setop_and
|
||||
11 OR setop_or
|
||||
12 NOT setop_not
|
||||
13 AND setop_and
|
||||
} {
|
||||
for {set i 0} {$i < $nRep} {incr i} {
|
||||
set term1 [random_term]
|
||||
@ -418,7 +444,7 @@ foreach nodesize {50 500 1000 2000} {
|
||||
set term3 [random_term]
|
||||
set term4 [random_term]
|
||||
set match "$term1 NEAR $term2 $op $term3 NEAR $term4"
|
||||
do_select_test fts3rnd-1.$nodesize.$iTest.$tn.$i {
|
||||
do_orderbydocid_test $tn.$i {
|
||||
SELECT docid FROM t1 WHERE t1 MATCH $match
|
||||
} [$proc \
|
||||
[simple_near [list $term1 $term2] 10] \
|
||||
|
||||
162
test/fts3sort.test
Normal file
162
test/fts3sort.test
Normal file
@ -0,0 +1,162 @@
|
||||
# 2011 May 04
|
||||
#
|
||||
# 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. The
|
||||
# focus of this script is testing the FTS3 module.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If SQLITE_ENABLE_FTS3 is defined, omit this file.
|
||||
ifcapable !fts3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
proc build_database {nRow param} {
|
||||
db close
|
||||
forcedelete test.db
|
||||
sqlite3 db test.db
|
||||
|
||||
set vocab [list aa ab ac ba bb bc ca cb cc da]
|
||||
expr srand(0)
|
||||
|
||||
execsql "CREATE VIRTUAL TABLE t1 USING fts4($param)"
|
||||
for {set i 0} {$i < $nRow} {incr i} {
|
||||
set v [expr int(rand()*1000000)]
|
||||
set doc [list]
|
||||
for {set div 1} {$div < 1000000} {set div [expr $div*10]} {
|
||||
lappend doc [lindex $vocab [expr ($v/$div) % 10]]
|
||||
}
|
||||
execsql { INSERT INTO t1 VALUES($doc) }
|
||||
}
|
||||
}
|
||||
|
||||
set testprefix fts3sort
|
||||
|
||||
unset -nocomplain CONTROL
|
||||
foreach {t param} {
|
||||
1 ""
|
||||
2 "order=asc"
|
||||
3 "order=desc"
|
||||
} {
|
||||
|
||||
set testprefix fts3sort-1.$t
|
||||
|
||||
set nRow 1000
|
||||
do_test 1.0 {
|
||||
build_database $nRow $param
|
||||
execsql { SELECT count(*) FROM t1 }
|
||||
} $nRow
|
||||
|
||||
foreach {tn query} {
|
||||
1 "SELECT docid, * FROM t1"
|
||||
2 "SELECT docid, * FROM t1 WHERE t1 MATCH 'aa'"
|
||||
3 "SELECT docid, * FROM t1 WHERE t1 MATCH 'a*'"
|
||||
4 "SELECT docid, quote(matchinfo(t1)) FROM t1 WHERE t1 MATCH 'a*'"
|
||||
5 "SELECT docid, quote(matchinfo(t1,'pcnxals')) FROM t1 WHERE t1 MATCH 'b*'"
|
||||
6 "SELECT docid, * FROM t1 WHERE t1 MATCH 'a* b* c*'"
|
||||
7 "SELECT docid, * FROM t1 WHERE t1 MATCH 'aa OR da'"
|
||||
8 "SELECT docid, * FROM t1 WHERE t1 MATCH 'nosuchtoken'"
|
||||
9 "SELECT docid, snippet(t1) FROM t1 WHERE t1 MATCH 'aa OR da'"
|
||||
10 "SELECT docid, snippet(t1) FROM t1 WHERE t1 MATCH 'aa OR nosuchtoken'"
|
||||
11 "SELECT docid, snippet(t1) FROM t1 WHERE t1 MATCH 'aa NEAR bb'"
|
||||
12 "SELECT docid, snippet(t1) FROM t1 WHERE t1 MATCH '\"aa bb\"'"
|
||||
13 "SELECT docid, content FROM t1 WHERE t1 MATCH 'aa NEAR/2 bb NEAR/3 cc'"
|
||||
14 "SELECT docid, content FROM t1 WHERE t1 MATCH '\"aa bb cc\"'"
|
||||
} {
|
||||
|
||||
unset -nocomplain A B C D
|
||||
set A_list [list]
|
||||
set B_list [list]
|
||||
set C_list [list]
|
||||
set D_list [list]
|
||||
|
||||
unset -nocomplain X
|
||||
db eval "$query ORDER BY rowid ASC" X {
|
||||
set A($X(docid)) [array get X]
|
||||
lappend A_list $X(docid)
|
||||
}
|
||||
unset -nocomplain X
|
||||
db eval "$query ORDER BY rowid DESC" X {
|
||||
set B($X(docid)) [array get X]
|
||||
lappend B_list $X(docid)
|
||||
}
|
||||
unset -nocomplain X
|
||||
db eval "$query ORDER BY docid ASC" X {
|
||||
set C($X(docid)) [array get X]
|
||||
lappend C_list $X(docid)
|
||||
}
|
||||
unset -nocomplain X
|
||||
db eval "$query ORDER BY docid DESC" X {
|
||||
set D($X(docid)) [array get X]
|
||||
lappend D_list $X(docid)
|
||||
}
|
||||
|
||||
do_test $tn.1 { set A_list } [lsort -integer -increasing $A_list]
|
||||
do_test $tn.2 { set B_list } [lsort -integer -decreasing $B_list]
|
||||
do_test $tn.3 { set C_list } [lsort -integer -increasing $C_list]
|
||||
do_test $tn.4 { set D_list } [lsort -integer -decreasing $D_list]
|
||||
|
||||
unset -nocomplain DATA
|
||||
unset -nocomplain X
|
||||
db eval "$query" X {
|
||||
set DATA($X(docid)) [array get X]
|
||||
}
|
||||
|
||||
do_test $tn.5 { lsort [array get A] } [lsort [array get DATA]]
|
||||
do_test $tn.6 { lsort [array get B] } [lsort [array get DATA]]
|
||||
do_test $tn.7 { lsort [array get C] } [lsort [array get DATA]]
|
||||
do_test $tn.8 { lsort [array get D] } [lsort [array get DATA]]
|
||||
|
||||
if {[info exists CONTROL($tn)]} {
|
||||
do_test $tn.9 { set CONTROL($tn) } [lsort [array get DATA]]
|
||||
} else {
|
||||
set CONTROL($tn) [lsort [array get DATA]]
|
||||
}
|
||||
}
|
||||
}
|
||||
unset -nocomplain CONTROL
|
||||
|
||||
set testprefix fts3sort
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Tests for parsing the "order=asc" and "order=desc" directives.
|
||||
#
|
||||
foreach {tn param res} {
|
||||
1 "order=asc" {0 {}}
|
||||
2 "order=desc" {0 {}}
|
||||
3 "order=dec" {1 {unrecognized order: dec}}
|
||||
4 "order=xxx, order=asc" {1 {unrecognized order: xxx}}
|
||||
5 "order=desc, order=asc" {0 {}}
|
||||
} {
|
||||
execsql { DROP TABLE IF EXISTS t1 }
|
||||
do_catchsql_test 2.1.$tn "
|
||||
CREATE VIRTUAL TABLE t1 USING fts4(a, b, $param)
|
||||
" $res
|
||||
}
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
BEGIN;
|
||||
CREATE VIRTUAL TABLE t2 USING fts4(order=desc);
|
||||
INSERT INTO t2 VALUES('aa bb');
|
||||
INSERT INTO t2 VALUES('bb cc');
|
||||
INSERT INTO t2 VALUES('cc aa');
|
||||
SELECT docid FROM t2 WHERE t2 MATCH 'aa';
|
||||
END;
|
||||
} {3 1}
|
||||
do_execsql_test 2.3 {
|
||||
SELECT docid FROM t2 WHERE t2 MATCH 'aa';
|
||||
} {3 1}
|
||||
|
||||
finish_test
|
||||
|
||||
134
test/fts4aa.test
134
test/fts4aa.test
@ -21,14 +21,12 @@ ifcapable !fts3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
if {[db eval {SELECT sqlite_compileoption_used('ENABLE_FTS4')}]==0} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_test fts4aa-1.0 {
|
||||
# This procedure fills an existing FTS3/FTS4 table with many entries.
|
||||
# The table needs to have a single column (other than docid) named "words".
|
||||
#
|
||||
proc fts4aa_fill_table {} {
|
||||
db eval {
|
||||
CREATE VIRTUAL TABLE t1 USING fts4(words, tokenize porter);
|
||||
BEGIN TRANSACTION;
|
||||
INSERT INTO t1(docid,words) VALUES(1001001,'In the beginning God created the heaven and the earth.');
|
||||
INSERT INTO t1(docid,words) VALUES(1001002,'And the earth was without form, and void; and darkness was upon the face of the deep. And the Spirit of God moved upon the face of the waters.');
|
||||
@ -1565,9 +1563,41 @@ INSERT INTO t1(docid,words) VALUES(1050025,'And Joseph took an oath of the child
|
||||
INSERT INTO t1(docid,words) VALUES(1050026,'So Joseph died, being an hundred and ten years old: and they embalmed him, and he was put in a coffin in Egypt.');
|
||||
COMMIT;
|
||||
}
|
||||
}
|
||||
|
||||
# The following is a list of queries to perform against the above
|
||||
# FTS3/FTS4 database. We will be trying these queries in various
|
||||
# configurations to ensure that they always return the same answers.
|
||||
#
|
||||
set fts4aa_queries {
|
||||
{abraham}
|
||||
{the king}
|
||||
{"the king"}
|
||||
{abraham OR joseph}
|
||||
{ab* OR jos*}
|
||||
{lived t*}
|
||||
{spake hebrew}
|
||||
{melchizedek}
|
||||
{t* melchizedek}
|
||||
{melchizedek t*}
|
||||
}
|
||||
unset -nocomplain fts4aa_res
|
||||
|
||||
# Set up the baseline results
|
||||
#
|
||||
do_test fts4aa-1.0 {
|
||||
db eval {
|
||||
CREATE VIRTUAL TABLE t1 USING fts4(words, tokenize porter);
|
||||
}
|
||||
fts4aa_fill_table
|
||||
foreach q $::fts4aa_queries {
|
||||
set r [db eval {SELECT docid FROM t1 WHERE words MATCH $q ORDER BY docid}]
|
||||
set ::fts4aa_res($q) $r
|
||||
}
|
||||
} {}
|
||||
|
||||
|
||||
# Legacy test cases
|
||||
#
|
||||
do_test fts4aa-1.1 {
|
||||
db eval {
|
||||
SELECT docid FROM t1 EXCEPT SELECT docid FROM t1_docsize
|
||||
@ -1587,50 +1617,114 @@ proc mit {blob} {
|
||||
}
|
||||
db func mit mit
|
||||
|
||||
do_test fts4aa-2.1 {
|
||||
do_test fts4aa-1.3 {
|
||||
db eval {
|
||||
SELECT docid, mit(matchinfo(t1)) FROM t1 WHERE t1 MATCH 'melchizedek';
|
||||
SELECT docid, mit(matchinfo(t1, 'pcxnal')) FROM t1 WHERE t1 MATCH 'melchizedek';
|
||||
}
|
||||
} {1014018 {1 1 1 1 1 1533 25 20}}
|
||||
do_test fts4aa-2.2 {
|
||||
do_test fts4aa-1.4 {
|
||||
db eval {
|
||||
SELECT docid, mit(matchinfo(t1)) FROM t1
|
||||
SELECT docid, mit(matchinfo(t1, 'pcxnal')) FROM t1
|
||||
WHERE t1 MATCH 'spake hebrew'
|
||||
ORDER BY docid;
|
||||
}
|
||||
} {1039014 {2 1 1 40 40 1 6 6 1533 25 42} 1039017 {2 1 1 40 40 1 6 6 1533 25 26}}
|
||||
do_test fts4aa-2.3 {
|
||||
do_test fts4aa-1.5 {
|
||||
db eval {
|
||||
SELECT docid, mit(matchinfo(t1)) FROM t1
|
||||
SELECT docid, mit(matchinfo(t1, 'pcxnal')) FROM t1
|
||||
WHERE t1 MATCH 'laban overtook jacob'
|
||||
ORDER BY docid;
|
||||
}
|
||||
} {1031025 {3 1 2 54 46 1 3 3 2 181 160 1533 25 24}}
|
||||
|
||||
do_test fts4aa-9.1 {
|
||||
do_test fts4aa-1.6 {
|
||||
db eval {
|
||||
DELETE FROM t1 WHERE docid!=1050026;
|
||||
SELECT hex(size) FROM t1_docsize;
|
||||
SELECT hex(value) FROM t1_stat;
|
||||
}
|
||||
} {17 0117}
|
||||
} {17 01176F}
|
||||
|
||||
do_test fts4aa-9.2 {
|
||||
do_test fts4aa-1.7 {
|
||||
db eval {
|
||||
SELECT docid FROM t1 EXCEPT SELECT docid FROM t1_docsize
|
||||
}
|
||||
} {}
|
||||
do_test fts4aa-9.3 {
|
||||
do_test fts4aa-1.8 {
|
||||
db eval {
|
||||
SELECT docid FROM t1_docsize EXCEPT SELECT docid FROM t1
|
||||
}
|
||||
} {}
|
||||
do_test fts4aa-9.4 {
|
||||
do_test fts4aa-1.9 {
|
||||
# Note: Token 'in' is being deferred in the following query.
|
||||
db eval {
|
||||
SELECT docid, mit(matchinfo(t1)) FROM t1
|
||||
SELECT docid, mit(matchinfo(t1, 'pcxnal')) FROM t1
|
||||
WHERE t1 MATCH 'joseph died in egypt'
|
||||
ORDER BY docid;
|
||||
}
|
||||
} {1050026 {4 1 1 1 1 1 1 1 2 2 1 1 1 1 1 23 23}}
|
||||
} {1050026 {4 1 1 1 1 1 1 1 2 1 1 1 1 1 1 23 23}}
|
||||
|
||||
# Should get the same search results from FTS3
|
||||
#
|
||||
do_test fts4aa-2.0 {
|
||||
db eval {
|
||||
DROP TABLE t1;
|
||||
CREATE VIRTUAL TABLE t1 USING fts3(words, tokenize porter);
|
||||
}
|
||||
fts4aa_fill_table
|
||||
} {}
|
||||
unset -nocomplain ii
|
||||
set ii 0
|
||||
foreach {q r} [array get fts4aa_res] {
|
||||
incr ii
|
||||
do_test fts4aa-2.$ii {
|
||||
db eval {SELECT docid FROM t1 WHERE words MATCH $::q ORDER BY docid}
|
||||
} $r
|
||||
}
|
||||
|
||||
# Should get the same search results when the page size is very large
|
||||
#
|
||||
do_test fts4aa-3.0 {
|
||||
db close
|
||||
file delete -force test.db
|
||||
sqlite3 db test.db
|
||||
db eval {
|
||||
PRAGMA page_size=65536;
|
||||
CREATE VIRTUAL TABLE t1 USING fts4(words, tokenize porter);
|
||||
}
|
||||
fts4aa_fill_table
|
||||
} {}
|
||||
unset -nocomplain ii
|
||||
set ii 0
|
||||
foreach {q r} [array get fts4aa_res] {
|
||||
incr ii
|
||||
do_test fts4aa-3.$ii {
|
||||
db eval {SELECT docid FROM t1 WHERE words MATCH $::q ORDER BY docid}
|
||||
} $r
|
||||
}
|
||||
|
||||
# Should get the same search results when an authorizer prevents
|
||||
# all PRAGMA statements.
|
||||
#
|
||||
proc no_pragma_auth {code arg1 arg2 arg3 arg4} {
|
||||
if {$code=="SQLITE_PRAGMA"} {return SQLITE_DENY}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
do_test fts4aa-4.0 {
|
||||
db auth ::no_pragma_auth
|
||||
db eval {
|
||||
DROP TABLE t1;
|
||||
CREATE VIRTUAL TABLE t1 USING fts4(words, tokenize porter);
|
||||
}
|
||||
fts4aa_fill_table
|
||||
} {}
|
||||
unset -nocomplain ii
|
||||
set ii 0
|
||||
foreach {q r} [array get fts4aa_res] {
|
||||
incr ii
|
||||
do_test fts4aa-4.$ii {
|
||||
db eval {SELECT docid FROM t1 WHERE words MATCH $::q ORDER BY docid}
|
||||
} $r
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@ -274,6 +274,34 @@ ifcapable compound&&attach {
|
||||
set ::update_hook
|
||||
} [list]
|
||||
}
|
||||
|
||||
do_test hook-4.4 {
|
||||
execsql {
|
||||
CREATE TABLE t4(a UNIQUE, b);
|
||||
INSERT INTO t4 VALUES(1, 'a');
|
||||
INSERT INTO t4 VALUES(2, 'b');
|
||||
}
|
||||
set ::update_hook [list]
|
||||
execsql {
|
||||
REPLACE INTO t4 VALUES(1, 'c');
|
||||
}
|
||||
set ::update_hook
|
||||
} [list INSERT main t4 3 ]
|
||||
do_execsql_test hook-4.4.1 {
|
||||
SELECT * FROM t4 ORDER BY a;
|
||||
} {1 c 2 b}
|
||||
do_test hook-4.4.2 {
|
||||
set ::update_hook [list]
|
||||
execsql {
|
||||
PRAGMA recursive_triggers = on;
|
||||
REPLACE INTO t4 VALUES(1, 'd');
|
||||
}
|
||||
set ::update_hook
|
||||
} [list INSERT main t4 4 ]
|
||||
do_execsql_test hook-4.4.3 {
|
||||
SELECT * FROM t4 ORDER BY a;
|
||||
} {1 d 2 b}
|
||||
|
||||
db update_hook {}
|
||||
#
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user