Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 464a2dd1f5 |
+3
-40
@@ -22,15 +22,11 @@ TOP = @srcdir@
|
||||
#
|
||||
BCC = @BUILD_CC@ @BUILD_CFLAGS@
|
||||
|
||||
# TCC is the C Compile and options for use in building executables that
|
||||
# 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.) Separate CC and CFLAGS macros
|
||||
# are provide so that these aspects of the build process can be changed
|
||||
# on the "make" command-line. Ex: "make CC=clang CFLAGS=-fsanitize=undefined"
|
||||
# same unless your are cross-compiling.)
|
||||
#
|
||||
CC = @CC@
|
||||
CFLAGS = @CPPFLAGS@ @CFLAGS@
|
||||
TCC = $(CC) $(CFLAGS) -I. -I${TOP}/src -I${TOP}/ext/rtree
|
||||
TCC = @CC@ @CPPFLAGS@ @CFLAGS@ -I. -I${TOP}/src -I${TOP}/ext/rtree
|
||||
|
||||
# Define this for the autoconf-based build, so that the code knows it can
|
||||
# include the generated config.h
|
||||
@@ -534,11 +530,6 @@ mptester$(EXE): sqlite3.c $(TOP)/mptest/mptest.c
|
||||
$(LTLINK) -o $@ -I. $(TOP)/mptest/mptest.c sqlite3.c \
|
||||
$(TLIBS) -rpath "$(libdir)"
|
||||
|
||||
mptest: mptester$(EXE)
|
||||
rm -f mptest1.db
|
||||
./mptester$(EXE) mptest1.db $(TOP)/mptest/crash01.test
|
||||
rm -f mptest2.db
|
||||
./mptester$(EXE) mptest2.db $(TOP)/mptest/multiwrite01.test
|
||||
|
||||
# This target creates a directory named "tsrc" and fills it with
|
||||
# copies of all of the C source code and header files needed to
|
||||
@@ -978,34 +969,6 @@ wordcount$(TEXE): $(TOP)/test/wordcount.c sqlite3.c
|
||||
speedtest1$(TEXE): $(TOP)/test/wordcount.c sqlite3.lo
|
||||
$(LTLINK) -o $@ $(TOP)/test/speedtest1.c sqlite3.lo $(TLIBS)
|
||||
|
||||
# This target will fail if the SQLite amalgamation contains any exported
|
||||
# symbols that do not begin with "sqlite3_". It is run as part of the
|
||||
# releasetest.tcl script.
|
||||
#
|
||||
checksymbols: sqlite3.lo
|
||||
nm -g --defined-only sqlite3.o | grep -v " sqlite3_" ; test $$? -ne 0
|
||||
echo '0 errors out of 1 tests'
|
||||
|
||||
# The next two rules are used to support the "threadtest" target. Building
|
||||
# threadtest runs a few thread-safety tests that are implemented in C. This
|
||||
# target is invoked by the releasetest.tcl script.
|
||||
#
|
||||
THREADTEST3_SRC = $(TOP)/test/threadtest3.c \
|
||||
$(TOP)/test/tt3_checkpoint.c \
|
||||
$(TOP)/test/tt3_index.c \
|
||||
$(TOP)/test/tt3_vacuum.c \
|
||||
$(TOP)/test/tt3_stress.c \
|
||||
$(TOP)/test/tt3_lookaside1.c
|
||||
|
||||
threadtest3$(TEXE): sqlite3.lo $(THREADTEST3_SRC)
|
||||
$(LTLINK) $(TOP)/test/threadtest3.c sqlite3.lo -o $@ $(TLIBS)
|
||||
|
||||
threadtest: threadtest3$(TEXE)
|
||||
./threadtest3$(TEXE)
|
||||
|
||||
releasetest:
|
||||
$(TCLSH_CMD) $(TOP)/test/releasetest.tcl
|
||||
|
||||
# Standard install and cleanup targets
|
||||
#
|
||||
lib_install: libsqlite3.la
|
||||
|
||||
+16
-22
@@ -108,12 +108,11 @@ WIN32HEAP = 0
|
||||
# levels. Currently, the recognized values for DEBUG are:
|
||||
#
|
||||
# 0 == NDEBUG: Disables assert() and other runtime diagnostics.
|
||||
# 1 == SQLITE_ENABLE_API_ARMOR: extra attempts to detect misuse of the API.
|
||||
# 2 == Disables NDEBUG and all optimizations and then enables PDBs.
|
||||
# 3 == SQLITE_DEBUG: Enables various diagnostics messages and code.
|
||||
# 4 == SQLITE_WIN32_MALLOC_VALIDATE: Validate the Win32 native heap per call.
|
||||
# 5 == SQLITE_DEBUG_OS_TRACE: Enables output from the OSTRACE() macros.
|
||||
# 6 == SQLITE_ENABLE_IOTRACE: Enables output from the IOTRACE() macros.
|
||||
# 1 == Disables NDEBUG and all optimizations and then enables PDBs.
|
||||
# 2 == SQLITE_DEBUG: Enables various diagnostics messages and code.
|
||||
# 3 == SQLITE_WIN32_MALLOC_VALIDATE: Validate the Win32 native heap per call.
|
||||
# 4 == SQLITE_DEBUG_OS_TRACE: Enables output from the OSTRACE() macros.
|
||||
# 5 == SQLITE_ENABLE_IOTRACE: Enables output from the IOTRACE() macros.
|
||||
#
|
||||
!IFNDEF DEBUG
|
||||
DEBUG = 0
|
||||
@@ -281,7 +280,7 @@ RCC = $(RCC) -DWINAPI_FAMILY=WINAPI_FAMILY_APP
|
||||
# MSVC runtime library.
|
||||
#
|
||||
!IF $(FOR_WINRT)!=0 || $(USE_CRT_DLL)!=0
|
||||
!IF $(DEBUG)>1
|
||||
!IF $(DEBUG)>0
|
||||
TCC = $(TCC) -MDd
|
||||
BCC = $(BCC) -MDd
|
||||
!ELSE
|
||||
@@ -289,7 +288,7 @@ TCC = $(TCC) -MD
|
||||
BCC = $(BCC) -MD
|
||||
!ENDIF
|
||||
!ELSE
|
||||
!IF $(DEBUG)>1
|
||||
!IF $(DEBUG)>0
|
||||
TCC = $(TCC) -MTd
|
||||
BCC = $(BCC) -MTd
|
||||
!ELSE
|
||||
@@ -314,7 +313,7 @@ RCC = $(RCC) -I$(TOP)\ext\rtree
|
||||
# options are necessary in order to allow debugging symbols to
|
||||
# work correctly with Visual Studio when using the amalgamation.
|
||||
#
|
||||
!IF $(DEBUG)>1
|
||||
!IF $(DEBUG)>0
|
||||
MKSQLITE3C_ARGS = --linemacros
|
||||
!ELSE
|
||||
MKSQLITE3C_ARGS =
|
||||
@@ -330,22 +329,17 @@ BCC = $(BCC) -DNDEBUG
|
||||
RCC = $(RCC) -DNDEBUG
|
||||
!ENDIF
|
||||
|
||||
!IF $(DEBUG)>0
|
||||
TCC = $(TCC) -DSQLITE_ENABLE_API_ARMOR
|
||||
RCC = $(RCC) -DSQLITE_ENABLE_API_ARMOR
|
||||
!ENDIF
|
||||
|
||||
!IF $(DEBUG)>2
|
||||
!IF $(DEBUG)>1
|
||||
TCC = $(TCC) -DSQLITE_DEBUG
|
||||
RCC = $(RCC) -DSQLITE_DEBUG
|
||||
!ENDIF
|
||||
|
||||
!IF $(DEBUG)>4
|
||||
!IF $(DEBUG)>3
|
||||
TCC = $(TCC) -DSQLITE_DEBUG_OS_TRACE=1
|
||||
RCC = $(RCC) -DSQLITE_DEBUG_OS_TRACE=1
|
||||
!ENDIF
|
||||
|
||||
!IF $(DEBUG)>5
|
||||
!IF $(DEBUG)>4
|
||||
TCC = $(TCC) -DSQLITE_ENABLE_IOTRACE
|
||||
RCC = $(RCC) -DSQLITE_ENABLE_IOTRACE
|
||||
!ENDIF
|
||||
@@ -377,7 +371,7 @@ RCC = $(RCC) -DSQLITE_WIN32_MALLOC=1
|
||||
|
||||
# Validate the heap on every call into the native Win32 heap subsystem?
|
||||
#
|
||||
!IF $(DEBUG)>3
|
||||
!IF $(DEBUG)>2
|
||||
TCC = $(TCC) -DSQLITE_WIN32_MALLOC_VALIDATE=1
|
||||
RCC = $(RCC) -DSQLITE_WIN32_MALLOC_VALIDATE=1
|
||||
!ENDIF
|
||||
@@ -488,7 +482,7 @@ RCC = $(RCC) $(OPTS)
|
||||
|
||||
# If compiling for debugging, add some defines.
|
||||
#
|
||||
!IF $(DEBUG)>1
|
||||
!IF $(DEBUG)>0
|
||||
TCC = $(TCC) -D_DEBUG
|
||||
BCC = $(BCC) -D_DEBUG
|
||||
RCC = $(RCC) -D_DEBUG
|
||||
@@ -497,7 +491,7 @@ RCC = $(RCC) -D_DEBUG
|
||||
# If optimizations are enabled or disabled (either implicitly or
|
||||
# explicitly), add the necessary flags.
|
||||
#
|
||||
!IF $(DEBUG)>1 || $(OPTIMIZATIONS)==0
|
||||
!IF $(DEBUG)>0 || $(OPTIMIZATIONS)==0
|
||||
TCC = $(TCC) -Od
|
||||
BCC = $(BCC) -Od
|
||||
!ELSEIF $(OPTIMIZATIONS)>=3
|
||||
@@ -513,7 +507,7 @@ BCC = $(BCC) -O1
|
||||
|
||||
# If symbols are enabled (or compiling for debugging), enable PDBs.
|
||||
#
|
||||
!IF $(DEBUG)>1 || $(SYMBOLS)!=0
|
||||
!IF $(DEBUG)>0 || $(SYMBOLS)!=0
|
||||
TCC = $(TCC) -Zi
|
||||
BCC = $(BCC) -Zi
|
||||
!ENDIF
|
||||
@@ -598,7 +592,7 @@ LTLINKOPTS = $(LTLINKOPTS) /NODEFAULTLIB:kernel32.lib /NODEFAULTLIB:ole32.lib
|
||||
|
||||
# If either debugging or symbols are enabled, enable PDBs.
|
||||
#
|
||||
!IF $(DEBUG)>1 || $(SYMBOLS)!=0
|
||||
!IF $(DEBUG)>0 || $(SYMBOLS)!=0
|
||||
LDFLAGS = /DEBUG
|
||||
!ENDIF
|
||||
|
||||
|
||||
@@ -53,10 +53,10 @@ to the "sqlite3.dll" command line above. When debugging into the SQLite
|
||||
code, adding the "DEBUG=1" argument to one of the above command lines is
|
||||
recommended.
|
||||
|
||||
SQLite does not require [Tcl](http://www.tcl.tk/) to run, but a Tcl installation
|
||||
is required by the makefiles (including those for MSVC). SQLite contains
|
||||
a lot of generated code and Tcl is used to do much of that code generation.
|
||||
The makefiles also require AWK.
|
||||
SQLite does not require Tcl to run, but a Tcl installation is required
|
||||
by the makefiles (including those for MSVC). SQLite contains a lot of
|
||||
generated code and Tcl is used to do much of that code generation. The
|
||||
makefiles also require AWK.
|
||||
|
||||
## Source Code Tour
|
||||
|
||||
@@ -95,14 +95,14 @@ manually-edited files and automatically-generated files.
|
||||
|
||||
The SQLite interface is defined by the **sqlite3.h** header file, which is
|
||||
generated from src/sqlite.h.in, ./manifest.uuid, and ./VERSION. The
|
||||
[Tcl script](http://www.tcl.tk) at tool/mksqlite3h.tcl does the conversion.
|
||||
The manifest.uuid file contains the SHA1 hash of the particular check-in
|
||||
and is used to generate the SQLITE\_SOURCE\_ID macro. The VERSION file
|
||||
contains the current SQLite version number. The sqlite3.h header is really
|
||||
just a copy of src/sqlite.h.in with the source-id and version number inserted
|
||||
at just the right spots. Note that comment text in the sqlite3.h file is
|
||||
used to generate much of the SQLite API documentation. The Tcl scripts
|
||||
used to generate that documentation are in a separate source repository.
|
||||
Tcl script at tool/mksqlite3h.tcl does the conversion. The manifest.uuid
|
||||
file contains the SHA1 hash of the particular check-in and is used to generate
|
||||
the SQLITE_SOURCE_ID macro. The VERSION file contains the current SQLite
|
||||
version number. The sqlite3.h header is really just a copy of src/sqlite.h.in
|
||||
with the source-id and version number inserted at just the right spots.
|
||||
Note that comment text in the sqlite3.h file is used to generate much of
|
||||
the SQLite API documentation. The Tcl scripts used to generate that
|
||||
documentation are in a separate source repository.
|
||||
|
||||
The SQL language parser is **parse.c** which is generate from a grammar in
|
||||
the src/parse.y file. The conversion of "parse.y" into "parse.c" is done
|
||||
|
||||
+1
-2
@@ -342,8 +342,7 @@ static int fuzzerLoadOneRule(
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
memset(pRule, 0, sizeof(*pRule));
|
||||
pRule->zFrom = pRule->zTo;
|
||||
pRule->zFrom += nTo + 1;
|
||||
pRule->zFrom = &pRule->zTo[nTo+1];
|
||||
pRule->nFrom = nFrom;
|
||||
memcpy(pRule->zFrom, zFrom, nFrom+1);
|
||||
memcpy(pRule->zTo, zTo, nTo+1);
|
||||
|
||||
+1
-1
@@ -1182,7 +1182,7 @@ static RtreeSearchPoint *rtreeEnqueue(
|
||||
pNew = pCur->aPoint + i;
|
||||
pNew->rScore = rScore;
|
||||
pNew->iLevel = iLevel;
|
||||
assert( iLevel<=RTREE_MAX_DEPTH );
|
||||
assert( iLevel>=0 && iLevel<=RTREE_MAX_DEPTH );
|
||||
while( i>0 ){
|
||||
RtreeSearchPoint *pParent;
|
||||
j = (i-1)/2;
|
||||
|
||||
@@ -625,15 +625,9 @@ test: testfixture$(EXE) sqlite3$(EXE)
|
||||
# threadtest runs a few thread-safety tests that are implemented in C. This
|
||||
# target is invoked by the releasetest.tcl script.
|
||||
#
|
||||
THREADTEST3_SRC = $(TOP)/test/threadtest3.c \
|
||||
$(TOP)/test/tt3_checkpoint.c \
|
||||
$(TOP)/test/tt3_index.c \
|
||||
$(TOP)/test/tt3_vacuum.c \
|
||||
$(TOP)/test/tt3_stress.c \
|
||||
$(TOP)/test/tt3_lookaside1.c
|
||||
|
||||
threadtest3$(EXE): sqlite3.o $(THREADTEST3_SRC)
|
||||
$(TCCX) $(TOP)/test/threadtest3.c sqlite3.o -o $@ $(THREADLIB)
|
||||
threadtest3$(EXE): sqlite3.o $(TOP)/test/threadtest3.c $(TOP)/test/tt3_checkpoint.c
|
||||
$(TCCX) -O2 sqlite3.o $(TOP)/test/threadtest3.c \
|
||||
-o threadtest3$(EXE) $(THREADLIB)
|
||||
|
||||
threadtest: threadtest3$(EXE)
|
||||
./threadtest3$(EXE)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
C Merge\supdates\sfrom\strunk.
|
||||
D 2015-01-02T20:06:27.572
|
||||
C Add\scode\sto\scheck\sthe\svalidity\sof\sCollSeq\sobjects\sduring\sruntime.\s\sThis\scode\nwas\snot\sable\sto\sdetect\sanomalies\ssuch\sas\scame\sup\sas\sa\sresult\sof\nticket\s[e4a18565a36884b00edf66541f38c693827968ab]\sso\sit\sis\sput\sinto\sa\sbranch\nfor\shistorical\sreference,\swith\sthe\sintent\sof\sleaving\sit\sout\sof\strunk.
|
||||
D 2014-12-05T14:36:15.903
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in 335e2d3ff0f2455eacbfa3075fc37495e3321410
|
||||
F Makefile.in 6c4f961fa91d0b4fa121946a19f9e5eac2f2f809
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
F Makefile.msc b363b90fe1bfc3b87d190f2f728a126c00d9ce09
|
||||
F Makefile.msc 10720782f88648bf2b5dcedf4c1524b067d43e47
|
||||
F Makefile.vxworks 034289efa9d591b04b1a73598623119c306cbba0
|
||||
F README.md d58e3bebc0a4145e0f2a87994015fdb575a8e866
|
||||
F README.md 64f270c43c38c46de749e419c22f0ae2f4499fe8
|
||||
F VERSION d846487aff892625eb8e75960234e7285f0462fe
|
||||
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
|
||||
F addopcodes.awk 9eb448a552d5c0185cf62c463f9c173cedae3811
|
||||
@@ -111,7 +111,7 @@ F ext/misc/closure.c 636024302cde41b2bf0c542f81c40c624cfb7012
|
||||
F ext/misc/compress.c 76e45655f4046e756064ab10c62e18f2eb846b9f
|
||||
F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2
|
||||
F ext/misc/fileio.c d4171c815d6543a9edef8308aab2951413cd8d0f
|
||||
F ext/misc/fuzzer.c e3e18f47252c151b5553d7e806f38e757d37c4cc
|
||||
F ext/misc/fuzzer.c 136533c53cfce0957f0b48fa11dba27e21c5c01d
|
||||
F ext/misc/ieee754.c b0362167289170627659e84173f5d2e8fee8566e
|
||||
F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342
|
||||
F ext/misc/percentile.c bcbee3c061b884eccb80e21651daaae8e1e43c63
|
||||
@@ -124,7 +124,7 @@ F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95
|
||||
F ext/misc/vtshim.c babb0dc2bf116029e3e7c9a618b8a1377045303e
|
||||
F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212
|
||||
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
|
||||
F ext/rtree/rtree.c a03ebd2480a1a4969cb363bb696d9d911e5a6ecd
|
||||
F ext/rtree/rtree.c 57bec53e1a677ab74217fe1f20a58c3a47261d6b
|
||||
F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
|
||||
F ext/rtree/rtree1.test 541bbcab74613907fea08b2ecdcdd5b7aa724cc9
|
||||
F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba
|
||||
@@ -152,7 +152,7 @@ F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
|
||||
F main.mk 9f8c54fe62b60e0a24a2e65cfc8d2add063dda07
|
||||
F main.mk 084976077a4aa3bd985154b5423e7aed88e4a2e9
|
||||
F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea
|
||||
F mkopcodeh.awk c6b3fa301db6ef7ac916b14c60868aeaec1337b5
|
||||
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
|
||||
@@ -160,33 +160,33 @@ F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
|
||||
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
|
||||
F mptest/crash01.test cce8e306d8596d5a2e497e27112dae1f6e5e3538
|
||||
F mptest/crash02.subtest f4ef05adcd15d60e5d2bd654204f2c008b519df8
|
||||
F mptest/mptest.c 24c5f72415df2eab7088ef8c9f99f163aed590c8
|
||||
F mptest/mptest.c 499a74af4be293b7c1c7c3d40f332b67227dd739
|
||||
F mptest/multiwrite01.test 499ad0310da8dff8e8f98d2e272fc2a8aa741b2e
|
||||
F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
|
||||
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
|
||||
F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
|
||||
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
|
||||
F src/alter.c ba266a779bc7ce10e52e59e7d3dc79fa342e8fdb
|
||||
F src/analyze.c 91540f835163d5369ccbae78e2e6c74d0dd53c1d
|
||||
F src/attach.c 7f6b3fafa2290b407e4a94dcf1afda7ec0fe394b
|
||||
F src/analyze.c f7d774356ba5a14e7ad4fb637681af16875ad88f
|
||||
F src/attach.c f4e94df2d1826feda65eb0939f7f6f5f923a0ad9
|
||||
F src/auth.c b56c78ebe40a2110fd361379f7e8162d23f92240
|
||||
F src/backup.c 7ddee9c7d505e07e959a575b18498f17c71e53ea
|
||||
F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb
|
||||
F src/btmutex.c 49ca66250c7dfa844a4d4cb8272b87420d27d3a5
|
||||
F src/btree.c 4c098bb6e8678e4596983862abf78f7a0fcb807e
|
||||
F src/btree.h 94277c1d30c0b75705974bcc8b0c05e79c03d474
|
||||
F src/btreeInt.h a3d0ae1d511365e1a2b76ad10960dbe55c286f34
|
||||
F src/build.c f5cfd7b32216f695b995bbc7c1a395f6d451d11f
|
||||
F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0
|
||||
F src/complete.c 198a0066ba60ab06fc00fba1998d870a4d575463
|
||||
F src/btree.c 44b58cd798a32579816ce274e415de262df9843d
|
||||
F src/btree.h e31a3a3ebdedb1caf9bda3ad5dbab3db9b780f6e
|
||||
F src/btreeInt.h 3363e18fd76f69a27a870b25221b2345b3fd4d21
|
||||
F src/build.c 67bb05b1077e0cdaccb2e36bfcbe7a5df9ed31e8
|
||||
F src/callback.c 759c9aff944f72fd7fb810594d95af1092a8433a
|
||||
F src/complete.c c4ba6e0626bb94bc77a0861735f3382fcf7cc818
|
||||
F src/ctime.c df19848891c8a553c80e6f5a035e768280952d1a
|
||||
F src/date.c 93594514aae68de117ca4a2a0d6cc63eddf26744
|
||||
F src/delete.c 0750b1eb4d96cd3fb2c798599a3a7c85e92f1417
|
||||
F src/expr.c 00da3072f362b06f39ce4052baa1d4ce2bb36d1c
|
||||
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
||||
F src/fkey.c e0444b61bed271a76840cbe6182df93a9baa3f12
|
||||
F src/fkey.c da985ae673efef2c712caef825a5d2edb087ead7
|
||||
F src/func.c 6d3c4ebd72aa7923ce9b110a7dc15f9b8c548430
|
||||
F src/global.c 12561d70a1b25f67b21154622bb1723426724f75
|
||||
F src/global.c 6ded36dda9466fc1c9a3c5492ded81d79bf3977d
|
||||
F src/hash.c 4263fbc955f26c2e8cdc0cf214bc42435aa4e4f5
|
||||
F src/hash.h c8f3c31722cf3277d03713909761e152a5b81094
|
||||
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
|
||||
@@ -194,8 +194,8 @@ F src/insert.c 5b9243a33726008cc4132897d2be371db12a13be
|
||||
F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
|
||||
F src/legacy.c ba1863ea58c4c840335a84ec276fc2b25e22bc4e
|
||||
F src/lempar.c 7274c97d24bb46631e504332ccd3bd1b37841770
|
||||
F src/loadext.c 86bd4e2fccd520b748cba52492ab60c4a770f660
|
||||
F src/main.c b0a7f3f9ff70106b7735f38c602d1b3ca56eb065
|
||||
F src/loadext.c de741e66e5ddc1598d904d7289239696e40ed994
|
||||
F src/main.c fa9f0e511a64caf66b8e7bb835c55172e7d61527
|
||||
F src/malloc.c 740db54387204c9a2eb67c6d98e68b08e9ef4eab
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c faf615aafd8be74a71494dfa027c113ea5c6615f
|
||||
@@ -207,45 +207,45 @@ F src/mutex.c 19bf9acba69ca2f367c3761080f8a9f0cf4670a8
|
||||
F src/mutex.h 779d588e3b7756ec3ecf7d78cde1d84aba414f85
|
||||
F src/mutex_noop.c f3f09fd7a2eb4287cfc799753ffc30380e7b71a1
|
||||
F src/mutex_unix.c 551e2f25f0fa0ee8fd7a43f50fc3d8be00e95dde
|
||||
F src/mutex_w32.c df48fe07562a45c5c927c45b8d5873a27f98bbb0
|
||||
F src/mutex_w32.c 06bfff9a3a83b53389a51a967643db3967032e1e
|
||||
F src/notify.c 9711a7575036f0d3040ba61bc6e217f13a9888e7
|
||||
F src/os.c 8fd25588eeba74068d41102d26810e216999b6c8
|
||||
F src/os.h 3e57a24e2794a94d3cf2342c6d9a884888cd96bf
|
||||
F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04
|
||||
F src/os_setup.h c9d4553b5aaa6f73391448b265b89bed0b890faa
|
||||
F src/os_unix.c 08c0346d2ea5e5ffd5b1a796f9becf1976d648d7
|
||||
F src/os_win.c 91d3d08e33ec0258d180d4c8255492f47d15e007
|
||||
F src/os_unix.c fb587121840f690101336879adfa6d0b2cd0e8c7
|
||||
F src/os_win.c a9e500dd963fb1f67d7860e58b5772abe6123862
|
||||
F src/os_win.h 09e751b20bbc107ffbd46e13555dc73576d88e21
|
||||
F src/pager.c 4120a49ecd37697e28f5ed807f470b9c0b88410c
|
||||
F src/pager.h c3476e7c89cdf1c6914e50a11f3714e30b4e0a77
|
||||
F src/pager.c 7a5c5bc0e29b9b16834f5558a9d5d22bbae59a08
|
||||
F src/pager.h d1eee3c3f741be247ce6d82752a178515fc8578b
|
||||
F src/parse.y 5dfead8aed90cb0c7c1115898ee2266804daff45
|
||||
F src/pcache.c b83d160ce81ca101f98f0d27498e6d6bd49f1599
|
||||
F src/pcache.c ace1b67632deeaa84859b4c16c27711dfb7db3d4
|
||||
F src/pcache.h b44658c9c932d203510279439d891a2a83e12ba8
|
||||
F src/pcache1.c 1e77432b40b7d3288327d9cdf399dcdfd2b6d3bf
|
||||
F src/pragma.c bd33aa24456f043bb6f6d32a918bbeed41d8c591
|
||||
F src/prepare.c 173a5a499138451b2561614ecb87d78f9f4644b9
|
||||
F src/pcache1.c facbdd3ecc09c8f750089d941305694301328e98
|
||||
F src/pragma.c d54cdd40b63d608f2d95b7482c710690e3593a73
|
||||
F src/prepare.c b7b7bf020bd4c962f7c8aed5a3c542c7dfe9f9c7
|
||||
F src/printf.c 9e75a6a0b55bf61cfff7d7e19d89834a1b938236
|
||||
F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
|
||||
F src/resolve.c f6c46d3434439ab2084618d603e6d6dbeb0d6ada
|
||||
F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e
|
||||
F src/select.c f377fb8a5c73c10678ea74f3400f7913943e3d75
|
||||
F src/shell.c 1b7cb3efc5ae6fe82e36407508c4a6b00d8edde1
|
||||
F src/sqlite.h.in ed799ff5c814227c7957eb4f4a217f67fdc0da48
|
||||
F src/shell.c 45d9c9bd7cde07845af957f2d849933b990773cf
|
||||
F src/sqlite.h.in 6ec654324cb490ea3d8a7be28b8c7d37fe4ad282
|
||||
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
|
||||
F src/sqlite3ext.h 17d487c3c91b0b8c584a32fbeb393f6f795eea7d
|
||||
F src/sqliteInt.h 3e0079855cb7b3c85dffac5bcbb1cd712ac62e65
|
||||
F src/sqliteInt.h ef42a0515a846ab2a670bda705be494d7acc2535
|
||||
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
||||
F src/status.c 81712116e826b0089bb221b018929536b2b5406f
|
||||
F src/table.c e7a09215315a978057fb42c640f890160dbcc45e
|
||||
F src/tclsqlite.c c6a21c64da1490e14d53cdc2062d1e2e57942622
|
||||
F src/test1.c 041c4edf2f9c49a329add297e26ee86a83852f51
|
||||
F src/table.c f142bba7903e93ca8d113a5b8877a108ad1a27dc
|
||||
F src/tclsqlite.c 0a874655dd39a9875e39c5d3c464db662171d228
|
||||
F src/test1.c c24d7f67252348d756773a6dbe23a61b4552f709
|
||||
F src/test2.c 98049e51a17dc62606a99a9eb95ee477f9996712
|
||||
F src/test3.c 1c0e5d6f080b8e33c1ce8b3078e7013fdbcd560c
|
||||
F src/test4.c 9b32d22f5f150abe23c1830e2057c4037c45b3df
|
||||
F src/test5.c 5a34feec76d9b3a86aab30fd4f6cc9c48cbab4c1
|
||||
F src/test6.c 41cacf3b0dd180823919bf9e1fbab287c9266723
|
||||
F src/test7.c 72b732baa5642f795655ba1126ea032af46ecfd2
|
||||
F src/test8.c bf3bad9a7df2e7c58d3371ea913abeaedb429a38
|
||||
F src/test8.c 54ccd7b1df5062f0ecbf50a8f7b618f8b1f13b20
|
||||
F src/test9.c bea1e8cf52aa93695487badedd6e1886c321ea60
|
||||
F src/test_async.c 21e11293a2f72080eda70e1124e9102044531cd8
|
||||
F src/test_autoext.c dea8a01a7153b9adc97bd26161e4226329546e12
|
||||
@@ -263,7 +263,7 @@ F src/test_intarray.c 6c610a21ab8edde85a3a2c7f2b069244ecf4d834
|
||||
F src/test_intarray.h 9dc57417fb65bc7835cc18548852cc08cc062202
|
||||
F src/test_journal.c f5c0a05b7b3d5930db769b5ee6c3766dc2221a64
|
||||
F src/test_loadext.c a5251f956ab6af21e138dc1f9c0399394a510cb4
|
||||
F src/test_malloc.c fffc66b2e7743ce93f039187d3fd78664193c664
|
||||
F src/test_malloc.c ba34143f941a9d74b30bbffc8818389bb73a1ca2
|
||||
F src/test_multiplex.c caadb62cc777268b4f8fb94d5b27b80156c8f7c0
|
||||
F src/test_multiplex.h c08e4e8f8651f0c5e0509b138ff4d5b43ed1f5d3
|
||||
F src/test_mutex.c 293042d623ebba969160f471a82aa1551626454f
|
||||
@@ -285,7 +285,7 @@ F src/test_vfs.c f84075a388527892ff184988f43b69ce69b8083c
|
||||
F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698
|
||||
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/threads.c 6de09362b657f19ba83e5fa521ee715787ce9fee
|
||||
F src/tokenize.c e00458c9938072b0ea711c850b8dcf4ddcb5fe18
|
||||
F src/tokenize.c cc9016e5007fc5e76789079616d2f26741bcc689
|
||||
F src/trigger.c 25571661fdeae8c7f975ff40ffec205520a3f92f
|
||||
F src/update.c 3c4ecc282accf12d39edb8d524cf089645e55a13
|
||||
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
|
||||
@@ -294,17 +294,17 @@ F src/vacuum.c 9b30ec729337dd012ed88d4c292922c8ef9cf00c
|
||||
F src/vdbe.c 1a9e671c9cfc259e4d2affc71f7df4a4c00a842c
|
||||
F src/vdbe.h 6fc69d9c5e146302c56e163cb4b31d1ee64a18c3
|
||||
F src/vdbeInt.h 9bb69ff2447c34b6ccc58b34ec35b615f86ead78
|
||||
F src/vdbeapi.c 4bc511a46b9839392ae0e90844a71dc96d9dbd71
|
||||
F src/vdbeaux.c 07ef87c6d4b5abdf13ff33babb10205702fdab0a
|
||||
F src/vdbeapi.c 07acb615d1e4170e71fc1b0d087f3c53a1ad8e83
|
||||
F src/vdbeaux.c 7e0a097ac7cf716300128f18147ef9af7c9d1130
|
||||
F src/vdbeblob.c 4af4bfb71f6df7778397b4a0ebc1879793276778
|
||||
F src/vdbemem.c 31d8eabb0cd78bfeab4e5124c7363c3e9e54db9f
|
||||
F src/vdbesort.c fc1aa0d1a8b539bf3a272945d02c9f9ede93039b
|
||||
F src/vdbesort.c 42c166f7ca78cb643c7f4e4bdfa83c59d363d1a6
|
||||
F src/vdbetrace.c 7e4222955e07dd707a2f360c0eb73452be1cb010
|
||||
F src/vtab.c c08ec66f45919eaa726bf88aa53eb08379d607f9
|
||||
F src/wal.c 85353539f2d9d0c91ebd057c32525b1e1aa3335e
|
||||
F src/wal.c 847692349eb6e1fb8543dbc97e69ddbfa4cc7ea7
|
||||
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
|
||||
F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804
|
||||
F src/where.c d46de821bc604a4fd36fa3928c086950e91aafb1
|
||||
F src/where.c d67fe69dd1bb32ad3d488a8c5bc803a29814f357
|
||||
F src/whereInt.h d3633e9b592103241b74b0ec76185f3e5b8b62e0
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||
@@ -366,7 +366,7 @@ F test/between.test 34d375fb5ce1ae283ffe82b6b233e9f38e84fc6c
|
||||
F test/bigfile.test aa74f4e5db51c8e54a1d9de9fa65d01d1eb20b59
|
||||
F test/bigfile2.test 1b489a3a39ae90c7f027b79110d6b4e1dbc71bfc
|
||||
F test/bigrow.test f0aeb7573dcb8caaafea76454be3ade29b7fc747
|
||||
F test/bigsort.test 8299fa9298f4f1e02fc7d2712e8b77d6cd60e5a2
|
||||
F test/bigsort.test 835478d0ce83bd1e5b05c90571dedd9871a09196
|
||||
F test/bind.test 3c7b320969000c441a70952b0b15938fbb66237c
|
||||
F test/bindxfer.test efecd12c580c14df5f4ad3b3e83c667744a4f7e0
|
||||
F test/bitvec.test 75894a880520164d73b1305c1c3f96882615e142
|
||||
@@ -404,7 +404,7 @@ F test/collate7.test 8ec29d98f3ee4ccebce6e16ce3863fb6b8c7b868
|
||||
F test/collate8.test df26649cfcbddf109c04122b340301616d3a88f6
|
||||
F test/collate9.test 3adcc799229545940df2f25308dd1ad65869145a
|
||||
F test/collateA.test b8218ab90d1fa5c59dcf156efabb1b2599c580d6
|
||||
F test/colmeta.test 2c765ea61ee37bc43bbe6d6047f89004e6508eb1
|
||||
F test/colmeta.test 087c42997754b8c648819832241daf724f813322
|
||||
F test/colname.test 08948a4809d22817e0e5de89c7c0a8bd90cb551b
|
||||
F test/conflict.test 841bcf7cabbfca39c577eb8411ea8601843b46a8
|
||||
F test/conflict2.test 0d3af4fb534fa1bd020c79960bb56e4d52655f09
|
||||
@@ -478,9 +478,6 @@ F test/e_update.test 312cb8f5ccfe41515a6bb092f8ea562a9bd54d52
|
||||
F test/e_uri.test 5ae33760fb2039c61aa2d90886f1664664173585
|
||||
F test/e_vacuum.test 5bfbdc21b65c0abf24398d0ba31dc88d93ca77a9
|
||||
F test/e_wal.test 0967f0b8f1dfda871dc7b9b5574198f1f4f7d69a
|
||||
F test/e_walauto.test d2dfc6681aade1f1306a8a336f5a258d8b62becd
|
||||
F test/e_walckpt.test 65e29b6631e51f210f83e4ff11571e647ba93608
|
||||
F test/e_walhook.test da3ea8b3483d1af72190337bda50155a91a4b664
|
||||
F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea
|
||||
F test/enc2.test 83437a79ba1545a55fb549309175c683fb334473
|
||||
F test/enc3.test 90683ad0e6ea587b9d5542ca93568af9a9858c40
|
||||
@@ -501,10 +498,9 @@ F test/fkey1.test e1d1fa84cde579185ea01358436839703e415a5b
|
||||
F test/fkey2.test 1db212cda86b0d3ce72714001f7b6381c321341c
|
||||
F test/fkey3.test 76d475c80b84ee7a5d062e56ccb6ea68882e2b49
|
||||
F test/fkey4.test 86446017011273aad8f9a99c1a65019e7bd9ca9d
|
||||
F test/fkey5.test 488601fbda8350619b3029487e56830447056fd2
|
||||
F test/fkey5.test 8a1fde4e7721ae00b05b3178888833726ca2df8d
|
||||
F test/fkey6.test abb59f866c1b44926fd02d1fdd217d831fe04f48
|
||||
F test/fkey7.test 72e915890ee4a005daaf3002cb208e8fe973ac13
|
||||
F test/fkey8.test 8f08203458321e6c19a263829de4cfc936274ab0
|
||||
F test/fkey_malloc.test 594a7ea1fbab553c036c70813cd8bd9407d63749
|
||||
F test/format4.test 1f0cac8ff3895e9359ed87e41aaabee982a812eb
|
||||
F test/fts-9fd058691.test 78b887e30ae6816df0e1fed6259de4b5a64ad33c
|
||||
@@ -727,7 +723,7 @@ F test/manydb.test 28385ae2087967aa05c38624cec7d96ec74feb3e
|
||||
F test/mem5.test c6460fba403c5703141348cd90de1c294188c68f
|
||||
F test/memdb.test fcb5297b321b562084fc79d64d5a12a1cd2b639b
|
||||
F test/memleak.test 10b9c6c57e19fc68c32941495e9ba1c50123f6e2
|
||||
F test/memsubsys1.test 690d142525a7d31efb638b0d232e25fac3afeb1a
|
||||
F test/memsubsys1.test bf270964ab83bc2da5927960f78304a866fb9a9d
|
||||
F test/memsubsys2.test 3a1c1a9de48e5726faa85108b02459fae8cb9ee9
|
||||
F test/minmax.test 42fbad0e81afaa6e0de41c960329f2b2c3526efd
|
||||
F test/minmax2.test b44bae787fc7b227597b01b0ca5575c7cb54d3bc
|
||||
@@ -755,7 +751,7 @@ F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660
|
||||
F test/nan.test e9648b9d007c7045242af35e11a984d4b169443a
|
||||
F test/nolock.test 0540dd96f39b8876e3ffdd8814fad0ea425efeee
|
||||
F test/notify1.test 669b2b743618efdc18ca4b02f45423d5d2304abf
|
||||
F test/notify2.test 2ecabaa1305083856b7c39cf32816b612740c161
|
||||
F test/notify2.test ce23eb522c9e1fff6443f96376fe67872202061c
|
||||
F test/notify3.test 10ff25cde502e72a92053a2f215d64bece4ef934
|
||||
F test/notnull.test f8fcf58669ddba79274daa2770d61dfad8274f62
|
||||
F test/null.test a8b09b8ed87852742343b33441a9240022108993
|
||||
@@ -781,11 +777,10 @@ F test/pageropt.test 6b8f6a123a5572c195ad4ae40f2987007923bbd6
|
||||
F test/pagesize.test 1dd51367e752e742f58e861e65ed7390603827a0
|
||||
F test/pcache.test b09104b03160aca0d968d99e8cd2c5b1921a993d
|
||||
F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025
|
||||
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
|
||||
F test/permutations.test 3a58611f8b703d385015bbe1710c722ff900f2ee
|
||||
F test/pragma.test aa16dedfe01c02c8895169012f7dfde9c163f0d5
|
||||
F test/percentile.test b98fc868d71eb5619d42a1702e9ab91718cbed54
|
||||
F test/permutations.test 4e12d43f4639ea8a0e366d9c64e0009afe2eb544
|
||||
F test/pragma.test 49ac8a73c0daa574824538fed28727d1259fe735
|
||||
F test/pragma2.test aea7b3d82c76034a2df2b38a13745172ddc0bc13
|
||||
F test/pragma3.test 6f849ccffeee7e496d2f2b5e74152306c0b8757c
|
||||
F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552
|
||||
F test/printf2.test b4acd4bf8734243257f01ddefa17c4fb090acc8a
|
||||
F test/progress.test a282973d1d17f08071bc58a77d6b80f2a81c354d
|
||||
@@ -801,7 +796,8 @@ F test/randexpr1.test eda062a97e60f9c38ae8d806b03b0ddf23d796df
|
||||
F test/rdonly.test dd30a4858d8e0fbad2304c2bd74a33d4df36412a
|
||||
F test/regexp1.test 497ea812f264d12b6198d6e50a76be4a1973a9d8
|
||||
F test/reindex.test 44edd3966b474468b823d481eafef0c305022254
|
||||
F test/releasetest.tcl facd103f341b2ecb5b86569b7e657d59027bd9dd
|
||||
F test/releasetest.mk 2eced2f9ae701fd0a29e714a241760503ccba25a
|
||||
F test/releasetest.tcl a4279c890698584feb2ffc86735857a4e4474180
|
||||
F test/resolver01.test 33abf37ff8335e6bf98f2b45a0af3e06996ccd9a
|
||||
F test/rollback.test 458fe73eb3ffdfdf9f6ba3e9b7350a6220414dea
|
||||
F test/rollback2.test fc14cf6d1a2b250d2735ef16124b971bce152f14
|
||||
@@ -853,11 +849,11 @@ F test/sharedA.test 0cdf1a76dfa00e6beee66af5b534b1e8df2720f5
|
||||
F test/sharedB.test 16cc7178e20965d75278f410943109b77b2e645e
|
||||
F test/shared_err.test 2f2aee20db294b9924e81f6ccbe60f19e21e8506
|
||||
F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
|
||||
F test/shell1.test cdeb849acc2c37aada70d084564b0cc0a2c7df08
|
||||
F test/shell1.test ab6025d941f9c84c5b83412c6b4d8b57f78dfa3a
|
||||
F test/shell2.test 12b8bf901b0e3a8ac58cf5c0c63a0a388d4d1862
|
||||
F test/shell3.test 5e8545ec72c4413a0e8d4c6be56496e3c257ca29
|
||||
F test/shell4.test 8a9c08976291e6c6c808b4d718f4a8b299f339f5
|
||||
F test/shell5.test d17a02d2327aadc6c062d5ba16b737d32601b11b
|
||||
F test/shell5.test 15a419cc1df21c892ed64f5596ae7a501f2816f2
|
||||
F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3
|
||||
F test/show_speedtest1_rtree.tcl 32e6c5f073d7426148a6936a0408f4b5b169aba5
|
||||
F test/shrink.test 8c70f62b6e8eb4d54533de6d65bd06b1b9a17868
|
||||
@@ -869,12 +865,12 @@ F test/skipscan5.test 67817a4b6857c47e0e33ba3e506da6f23ef68de2
|
||||
F test/skipscan6.test 5866039d03a56f5bd0b3d172a012074a1d90a15b
|
||||
F test/soak.test 0b5b6375c9f4110c828070b826b3b4b0bb65cd5f
|
||||
F test/softheap1.test 40562fe6cac6d9827b7b42b86d45aedf12c15e24
|
||||
F test/sort.test 3f492e5b7be1d3f756728d2ff6edf4f6091e84cb
|
||||
F test/sort2.test 37afbc03f5559f2eb0f18940b55d38dfbb5172ac
|
||||
F test/sort.test c4400e7533748f6bd7413851ff148645e82b9e2d
|
||||
F test/sort2.test 84a92eebf697feee9c6697746918c7d67373eea1
|
||||
F test/sort3.test 6178ade30810ac9166fcdf14b7065e49c0f534e2
|
||||
F test/sort4.test d5e8903194ae551551349ce25dc8d0b40ca2b9c3
|
||||
F test/sort4.test 6c37d85f7cd28d50cce222fcab84ccd771e105cb
|
||||
F test/sort5.test a448240a42b49239edc00f85d6d7ac7a1b261e1f
|
||||
F test/sortfault.test d4ccf606a0c77498e2beb542764fd9394acb4d66
|
||||
F test/sortfault.test b8e35177f97438b930ee87c9419ca2599e8073e1
|
||||
F test/speed1.test f2974a91d79f58507ada01864c0e323093065452
|
||||
F test/speed1p.explain d841e650a04728b39e6740296b852dccdca9b2cb
|
||||
F test/speed1p.test b180e98609c7677382cf618c0ec9b69f789033a8
|
||||
@@ -914,8 +910,7 @@ F test/thread2.test f35d2106452b77523b3a2b7d1dcde2e5ee8f9e46
|
||||
F test/thread_common.tcl 334639cadcb9f912bf82aa73f49efd5282e6cadd
|
||||
F test/threadtest1.c 6029d9c5567db28e6dc908a0c63099c3ba6c383b
|
||||
F test/threadtest2.c ace893054fa134af3fc8d6e7cfecddb8e3acefb9
|
||||
F test/threadtest3.c 9ab4b168681c3a6f70f6c833ba08e0d48dd4af9b
|
||||
F test/threadtest4.c c1e67136ceb6c7ec8184e56ac61db28f96bd2925
|
||||
F test/threadtest3.c 0ed13e09690f6204d7455fac3b0e8ece490f6eef
|
||||
F test/tkt-02a8e81d44.test 6c80d9c7514e2a42d4918bf87bf6bc54f379110c
|
||||
F test/tkt-26ff0c2d1e.test 888324e751512972c6e0d1a09df740d8f5aaf660
|
||||
F test/tkt-2a5629202f.test 0521bd25658428baa26665aa53ffed9367d33af2
|
||||
@@ -1078,11 +1073,7 @@ F test/triggerB.test 56780c031b454abac2340dbb3b71ac5c56c3d7fe
|
||||
F test/triggerC.test a68980c5955d62ee24be6f97129d824f199f9a4c
|
||||
F test/triggerD.test 8e7f3921a92a5797d472732108109e44575fa650
|
||||
F test/triggerE.test 355e9c5cbaed5cd039a60baad1fb2197caeb8e52
|
||||
F test/tt3_checkpoint.c 9e75cf7c1c364f52e1c47fd0f14c4340a9db0fe1
|
||||
F test/tt3_index.c 39eec10a35f57672225be4d182862152896dee4a
|
||||
F test/tt3_lookaside1.c 0377e202c3c2a50d688cb65ba203afeda6fafeb9
|
||||
F test/tt3_stress.c c57d804716165811d979d4a719e05baccd79277f
|
||||
F test/tt3_vacuum.c 1753f45917699c9c1f66b64c717a717c9379f776
|
||||
F test/tt3_checkpoint.c 415eccce672d681b297485fc20f44cdf0eac93af
|
||||
F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff
|
||||
F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84
|
||||
F test/types3.test 99e009491a54f4dc02c06bdbc0c5eea56ae3e25a
|
||||
@@ -1123,7 +1114,7 @@ F test/wal.test 885f32b2b390b30b4aa3dbb0e568f8f78d40f5cc
|
||||
F test/wal2.test 1f841d2048080d32f552942e333fd99ce541dada
|
||||
F test/wal3.test b22eb662bcbc148c5f6d956eaf94b047f7afe9c0
|
||||
F test/wal4.test 4744e155cd6299c6bd99d3eab1c82f77db9cdb3c
|
||||
F test/wal5.test 11b8658dd4d5448f4604124bebd9b68be5bc3e66
|
||||
F test/wal5.test 174cc1512e304a7dfa28ac30527e28ea02fc37df
|
||||
F test/wal6.test 527581f5527bf9c24394991e2be83000aace5f9e
|
||||
F test/wal64k.test 163655ecd2cb8afef4737cac2a40fdd2eeaf20b8
|
||||
F test/wal7.test 2ae8f427d240099cc4b2dfef63cff44e2a68a1bd
|
||||
@@ -1178,7 +1169,6 @@ F test/without_rowid2.test af260339f79d13cb220288b67cd287fbcf81ad99
|
||||
F test/without_rowid3.test 1081aabf60a1e1123b7f9a8f6ae19954351843b0
|
||||
F test/without_rowid4.test 4e08bcbaee0399f35d58b5581881e7a6243d458a
|
||||
F test/without_rowid5.test 61256715b686359df48ca1742db50cc7e3e7b862
|
||||
F test/without_rowid6.test deddb78ef539c355bddec00cdfaea6c56efd8b3f
|
||||
F test/wordcount.c 9915e06cb33d8ca8109b8700791afe80d305afda
|
||||
F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688
|
||||
F test/zerodamage.test cf6748bad89553cc1632be51a6f54e487e4039ac
|
||||
@@ -1192,16 +1182,16 @@ F tool/fragck.tcl 5265a95126abcf6ab357f7efa544787e5963f439
|
||||
F tool/genfkey.README cf68fddd4643bbe3ff8e31b8b6d8b0a1b85e20f4
|
||||
F tool/genfkey.test 4196a8928b78f51d54ef58e99e99401ab2f0a7e5
|
||||
F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
|
||||
F tool/lemon.c 20f3132a1a150d50b022aa0b1fa26bd46aebf7b5
|
||||
F tool/lemon.c 3ff0fec22f92dfb54e62eeb48772eddffdbeb0d6
|
||||
F tool/lempar.c 01ca97f87610d1dac6d8cd96ab109ab1130e76dc
|
||||
F tool/logest.c eef612f8adf4d0993dafed0416064cf50d5d33c6
|
||||
F tool/mkautoconfamal.sh 5dc5010e2e748a9e1bba67baca5956a2c2deda7b
|
||||
F tool/mkkeywordhash.c dfff09dbbfaf950e89af294f48f902181b144670
|
||||
F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e
|
||||
F tool/mkpragmatab.tcl 07a5124cf2dbafa1b375eefcf8ac4227028b0f8b
|
||||
F tool/mkpragmatab.tcl cce51d8f60c7f145d8fccabe6b5dfdedf31c5f5c
|
||||
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
|
||||
F tool/mksqlite3c-noext.tcl 88a1e3b0c769773fb7a9ebb363ffc603a4ac21d8
|
||||
F tool/mksqlite3c.tcl e94bdc37b531bba50d421e82efbe3738d0c1e950
|
||||
F tool/mksqlite3c.tcl e72c0c97fe1a105fa9616483e652949be2199fe6
|
||||
F tool/mksqlite3h.tcl ba24038056f51fde07c0079c41885ab85e2cff12
|
||||
F tool/mksqlite3internalh.tcl b6514145a7d5321b47e64e19b8116cc44f973eb1
|
||||
F tool/mkvsix.tcl 52a4c613707ac34ae9c226e5ccc69cb948556105
|
||||
@@ -1234,7 +1224,10 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
P 555fb71f6c479936446c2ce75b1b120a9a121f49 acb0d1e8324f19da3d4d577d58748848de1bcef7
|
||||
R 0ad9c82a4c41300c9e42937cd025154e
|
||||
U mistachkin
|
||||
Z 58d3b1fc40da37c01849f39453222e08
|
||||
P 71f589e3f82a07513425e212072f32748c0732d4
|
||||
R 02b8ea23886487ef9860b5ee40d238ab
|
||||
T *branch * collseq-checking
|
||||
T *sym-collseq-checking *
|
||||
T -sym-fix-stale-keyinfo-cache *
|
||||
U drh
|
||||
Z 74adfe1c6f95aa3cd0aa6e2d3a771e07
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
ea99f4b29afb98dd474d96889c934763f5636891
|
||||
68b23c3d41ce9303f42251d0c199181312e148f9
|
||||
+1
-1
@@ -1395,7 +1395,7 @@ int main(int argc, char **argv){
|
||||
maybeClose(g.pLog);
|
||||
maybeClose(g.pErrLog);
|
||||
if( iClient==0 ){
|
||||
printf("Summary: %d errors out of %d tests\n", g.nError, g.nTest);
|
||||
printf("Summary: %d errors in %d tests\n", g.nError, g.nTest);
|
||||
}
|
||||
return g.nError>0;
|
||||
}
|
||||
|
||||
+2
-2
@@ -448,7 +448,7 @@ static void statInit(
|
||||
p->mxSample = mxSample;
|
||||
p->nPSample = (tRowcnt)(sqlite3_value_int64(argv[2])/(mxSample/3+1) + 1);
|
||||
p->current.anLt = &p->current.anEq[nColUp];
|
||||
p->iPrn = 0x689e962d*(u32)nCol ^ 0xd0944565*(u32)sqlite3_value_int(argv[2]);
|
||||
p->iPrn = nCol*0x689e962d ^ sqlite3_value_int(argv[2])*0xd0944565;
|
||||
|
||||
/* Set up the Stat4Accum.a[] and aBest[] arrays */
|
||||
p->a = (struct Stat4Sample*)&p->current.anLt[nColUp];
|
||||
@@ -1596,7 +1596,7 @@ static void initAvgEq(Index *pIdx){
|
||||
i64 nSum100 = 0; /* Number of terms contributing to sumEq */
|
||||
i64 nDist100; /* Number of distinct values in index */
|
||||
|
||||
if( !pIdx->aiRowEst || iCol>=pIdx->nKeyCol || pIdx->aiRowEst[iCol+1]==0 ){
|
||||
if( pIdx->aiRowEst==0 || pIdx->aiRowEst[iCol+1]==0 ){
|
||||
nRow = pFinal->anLt[iCol];
|
||||
nDist100 = (i64)100 * pFinal->anDLt[iCol];
|
||||
nSample--;
|
||||
|
||||
@@ -150,7 +150,6 @@ static void attachFunc(
|
||||
"attached databases must use the same text encoding as main database");
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
sqlite3BtreeEnter(aNew->pBt);
|
||||
pPager = sqlite3BtreePager(aNew->pBt);
|
||||
sqlite3PagerLockingMode(pPager, db->dfltLockMode);
|
||||
sqlite3BtreeSecureDelete(aNew->pBt,
|
||||
@@ -158,7 +157,6 @@ static void attachFunc(
|
||||
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
|
||||
sqlite3BtreeSetPagerFlags(aNew->pBt, 3 | (db->flags & PAGER_FLAGS_MASK));
|
||||
#endif
|
||||
sqlite3BtreeLeave(aNew->pBt);
|
||||
}
|
||||
aNew->safety_level = 3;
|
||||
aNew->zName = sqlite3DbStrDup(db, zName);
|
||||
|
||||
+9
-31
@@ -3550,7 +3550,6 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p, int bCleanup){
|
||||
sqlite3BtreeLeave(p);
|
||||
return rc;
|
||||
}
|
||||
p->iDataVersion--; /* Compensate for pPager->iDataVersion++; */
|
||||
pBt->inTransaction = TRANS_READ;
|
||||
btreeClearHasContent(pBt);
|
||||
}
|
||||
@@ -3914,7 +3913,7 @@ int sqlite3BtreeCloseCursor(BtCursor *pCur){
|
||||
releasePage(pCur->apPage[i]);
|
||||
}
|
||||
unlockBtreeIfUnused(pBt);
|
||||
sqlite3_free(pCur->aOverflow);
|
||||
sqlite3DbFree(pBtree->db, pCur->aOverflow);
|
||||
/* sqlite3_free(pCur); */
|
||||
sqlite3BtreeLeave(pBtree);
|
||||
}
|
||||
@@ -4209,7 +4208,6 @@ static int accessPayload(
|
||||
offset -= pCur->info.nLocal;
|
||||
}
|
||||
|
||||
|
||||
if( rc==SQLITE_OK && amt>0 ){
|
||||
const u32 ovflSize = pBt->usableSize - 4; /* Bytes content per ovfl page */
|
||||
Pgno nextPage;
|
||||
@@ -4227,8 +4225,8 @@ static int accessPayload(
|
||||
if( eOp!=2 && (pCur->curFlags & BTCF_ValidOvfl)==0 ){
|
||||
int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize;
|
||||
if( nOvfl>pCur->nOvflAlloc ){
|
||||
Pgno *aNew = (Pgno*)sqlite3Realloc(
|
||||
pCur->aOverflow, nOvfl*2*sizeof(Pgno)
|
||||
Pgno *aNew = (Pgno*)sqlite3DbRealloc(
|
||||
pCur->pBtree->db, pCur->aOverflow, nOvfl*2*sizeof(Pgno)
|
||||
);
|
||||
if( aNew==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
@@ -4275,7 +4273,6 @@ static int accessPayload(
|
||||
*/
|
||||
assert( eOp!=2 );
|
||||
assert( pCur->curFlags & BTCF_ValidOvfl );
|
||||
assert( pCur->pBtree->db==pBt->db );
|
||||
if( pCur->aOverflow[iIdx+1] ){
|
||||
nextPage = pCur->aOverflow[iIdx+1];
|
||||
}else{
|
||||
@@ -6788,7 +6785,7 @@ static int balance_nonroot(
|
||||
|
||||
/* EVIDENCE-OF: R-28375-38319 SQLite will never request a scratch buffer
|
||||
** that is more than 6 times the database page size. */
|
||||
assert( szScratch<=6*(int)pBt->pageSize );
|
||||
assert( szScratch<=6*pBt->pageSize );
|
||||
apCell = sqlite3ScratchMalloc( szScratch );
|
||||
if( apCell==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
@@ -6862,11 +6859,7 @@ static int balance_nonroot(
|
||||
}else{
|
||||
assert( leafCorrection==4 );
|
||||
if( szCell[nCell]<4 ){
|
||||
/* Do not allow any cells smaller than 4 bytes. If a smaller cell
|
||||
** does exist, pad it with 0x00 bytes. */
|
||||
assert( szCell[nCell]==3 );
|
||||
assert( apCell[nCell]==&aSpace1[iSpace1-3] );
|
||||
aSpace1[iSpace1++] = 0x00;
|
||||
/* Do not allow any cells smaller than 4 bytes. */
|
||||
szCell[nCell] = 4;
|
||||
}
|
||||
}
|
||||
@@ -7101,11 +7094,7 @@ static int balance_nonroot(
|
||||
** if sibling page iOld had the same page number as pNew, and if
|
||||
** pCell really was a part of sibling page iOld (not a divider or
|
||||
** overflow cell), we can skip updating the pointer map entries. */
|
||||
if( iOld>=nNew
|
||||
|| pNew->pgno!=aPgno[iOld]
|
||||
|| pCell<aOld
|
||||
|| pCell>=&aOld[usableSize]
|
||||
){
|
||||
if( pNew->pgno!=aPgno[iOld] || pCell<aOld || pCell>=&aOld[usableSize] ){
|
||||
if( !leafCorrection ){
|
||||
ptrmapPut(pBt, get4byte(pCell), PTRMAP_BTREE, pNew->pgno, &rc);
|
||||
}
|
||||
@@ -8178,13 +8167,6 @@ int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){
|
||||
** The schema layer numbers meta values differently. At the schema
|
||||
** layer (and the SetCookie and ReadCookie opcodes) the number of
|
||||
** free pages is not visible. So Cookie[0] is the same as Meta[1].
|
||||
**
|
||||
** This routine treats Meta[BTREE_DATA_VERSION] as a special case. Instead
|
||||
** of reading the value out of the header, it instead loads the "DataVersion"
|
||||
** from the pager. The BTREE_DATA_VERSION value is not actually stored in the
|
||||
** database file. It is a number computed by the pager. But its access
|
||||
** pattern is the same as header meta values, and so it is convenient to
|
||||
** read it from this routine.
|
||||
*/
|
||||
void sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
|
||||
BtShared *pBt = p->pBt;
|
||||
@@ -8195,11 +8177,7 @@ void sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
|
||||
assert( pBt->pPage1 );
|
||||
assert( idx>=0 && idx<=15 );
|
||||
|
||||
if( idx==BTREE_DATA_VERSION ){
|
||||
*pMeta = sqlite3PagerDataVersion(pBt->pPager) + p->iDataVersion;
|
||||
}else{
|
||||
*pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]);
|
||||
}
|
||||
*pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]);
|
||||
|
||||
/* If auto-vacuum is disabled in this build and this is an auto-vacuum
|
||||
** database, mark the database as read-only. */
|
||||
@@ -8290,7 +8268,7 @@ int sqlite3BtreeCount(BtCursor *pCur, i64 *pnEntry){
|
||||
if( pCur->iPage==0 ){
|
||||
/* All pages of the b-tree have been visited. Return successfully. */
|
||||
*pnEntry = nEntry;
|
||||
return moveToRoot(pCur);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
moveToParent(pCur);
|
||||
}while ( pCur->aiIdx[pCur->iPage]>=pCur->apPage[pCur->iPage]->nCell );
|
||||
@@ -9146,4 +9124,4 @@ int sqlite3BtreeIsReadonly(Btree *p){
|
||||
/*
|
||||
** Return the size of the header added to each page by this module.
|
||||
*/
|
||||
int sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); }
|
||||
int sqlite3HeaderSizeBtree(void){ return sizeof(MemPage); }
|
||||
|
||||
+1
-7
@@ -19,7 +19,7 @@
|
||||
/* TODO: This definition is just included so other modules compile. It
|
||||
** needs to be revisited.
|
||||
*/
|
||||
#define SQLITE_N_BTREE_META 16
|
||||
#define SQLITE_N_BTREE_META 10
|
||||
|
||||
/*
|
||||
** If defined as non-zero, auto-vacuum is enabled by default. Otherwise
|
||||
@@ -134,11 +134,6 @@ int sqlite3BtreeNewDb(Btree *p);
|
||||
** For example, the free-page-count field is located at byte offset 36 of
|
||||
** the database file header. The incr-vacuum-flag field is located at
|
||||
** byte offset 64 (== 36+4*7).
|
||||
**
|
||||
** The BTREE_DATA_VERSION value is not really a value stored in the header.
|
||||
** It is a read-only number computed by the pager. But we merge it with
|
||||
** the header value access routines since its access pattern is the same.
|
||||
** Call it a "virtual meta value".
|
||||
*/
|
||||
#define BTREE_FREE_PAGE_COUNT 0
|
||||
#define BTREE_SCHEMA_VERSION 1
|
||||
@@ -149,7 +144,6 @@ int sqlite3BtreeNewDb(Btree *p);
|
||||
#define BTREE_USER_VERSION 6
|
||||
#define BTREE_INCR_VACUUM 7
|
||||
#define BTREE_APPLICATION_ID 8
|
||||
#define BTREE_DATA_VERSION 15 /* A virtual meta-value */
|
||||
|
||||
/*
|
||||
** Values that may be OR'd together to form the second argument of an
|
||||
|
||||
@@ -351,7 +351,6 @@ struct Btree {
|
||||
u8 locked; /* True if db currently has pBt locked */
|
||||
int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */
|
||||
int nBackup; /* Number of backup operations reading this btree */
|
||||
u32 iDataVersion; /* Combines with pBt->pPager->iDataVersion */
|
||||
Btree *pNext; /* List of other sharable Btrees from the same db */
|
||||
Btree *pPrev; /* Back pointer of the same list */
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
|
||||
+30
-33
@@ -435,6 +435,7 @@ static void freeIndex(sqlite3 *db, Index *p){
|
||||
#ifndef SQLITE_OMIT_ANALYZE
|
||||
sqlite3DeleteIndexSamples(db, p);
|
||||
#endif
|
||||
if( db==0 || db->pnBytesFreed==0 ) sqlite3KeyInfoUnref(p->pKeyInfo);
|
||||
sqlite3ExprDelete(db, p->pPartIdxWhere);
|
||||
sqlite3DbFree(db, p->zColAff);
|
||||
if( p->isResized ) sqlite3DbFree(db, p->azColl);
|
||||
@@ -1713,19 +1714,6 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
|
||||
pTab->iPKey = -1;
|
||||
}else{
|
||||
pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
/*
|
||||
** Remove all redundant columns from the PRIMARY KEY. For example, change
|
||||
** "PRIMARY KEY(a,b,a,b,c,b,c,d)" into just "PRIMARY KEY(a,b,c,d)". Later
|
||||
** code assumes the PRIMARY KEY contains no repeated columns.
|
||||
*/
|
||||
for(i=j=1; i<pPk->nKeyCol; i++){
|
||||
if( hasColumn(pPk->aiColumn, j, pPk->aiColumn[i]) ){
|
||||
pPk->nColumn--;
|
||||
}else{
|
||||
pPk->aiColumn[j++] = pPk->aiColumn[i];
|
||||
}
|
||||
}
|
||||
pPk->nKeyCol = j;
|
||||
}
|
||||
pPk->isCovering = 1;
|
||||
assert( pPk!=0 );
|
||||
@@ -4202,31 +4190,40 @@ void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){
|
||||
** when it has finished using it.
|
||||
*/
|
||||
KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){
|
||||
int i;
|
||||
int nCol = pIdx->nColumn;
|
||||
int nKey = pIdx->nKeyCol;
|
||||
KeyInfo *pKey;
|
||||
if( pParse->nErr ) return 0;
|
||||
if( pIdx->uniqNotNull ){
|
||||
pKey = sqlite3KeyInfoAlloc(pParse->db, nKey, nCol-nKey);
|
||||
}else{
|
||||
pKey = sqlite3KeyInfoAlloc(pParse->db, nCol, 0);
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
if( pIdx->pKeyInfo && pIdx->pKeyInfo->db!=pParse->db ){
|
||||
sqlite3KeyInfoUnref(pIdx->pKeyInfo);
|
||||
pIdx->pKeyInfo = 0;
|
||||
}
|
||||
if( pKey ){
|
||||
assert( sqlite3KeyInfoIsWriteable(pKey) );
|
||||
for(i=0; i<nCol; i++){
|
||||
char *zColl = pIdx->azColl[i];
|
||||
assert( zColl!=0 );
|
||||
pKey->aColl[i] = strcmp(zColl,"BINARY")==0 ? 0 :
|
||||
sqlite3LocateCollSeq(pParse, zColl);
|
||||
pKey->aSortOrder[i] = pIdx->aSortOrder[i];
|
||||
#endif
|
||||
if( pIdx->pKeyInfo==0 ){
|
||||
int i;
|
||||
int nCol = pIdx->nColumn;
|
||||
int nKey = pIdx->nKeyCol;
|
||||
KeyInfo *pKey;
|
||||
if( pIdx->uniqNotNull ){
|
||||
pKey = sqlite3KeyInfoAlloc(pParse->db, nKey, nCol-nKey);
|
||||
}else{
|
||||
pKey = sqlite3KeyInfoAlloc(pParse->db, nCol, 0);
|
||||
}
|
||||
if( pParse->nErr ){
|
||||
sqlite3KeyInfoUnref(pKey);
|
||||
pKey = 0;
|
||||
if( pKey ){
|
||||
assert( sqlite3KeyInfoIsWriteable(pKey) );
|
||||
for(i=0; i<nCol; i++){
|
||||
char *zColl = pIdx->azColl[i];
|
||||
assert( zColl!=0 );
|
||||
pKey->aColl[i] = strcmp(zColl,"BINARY")==0 ? 0 :
|
||||
sqlite3LocateCollSeq(pParse, zColl);
|
||||
pKey->aSortOrder[i] = pIdx->aSortOrder[i];
|
||||
}
|
||||
if( pParse->nErr ){
|
||||
sqlite3KeyInfoUnref(pKey);
|
||||
}else{
|
||||
pIdx->pKeyInfo = pKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
return pKey;
|
||||
return sqlite3KeyInfoRef(pIdx->pKeyInfo);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_CTE
|
||||
|
||||
+30
-2
@@ -102,7 +102,7 @@ CollSeq *sqlite3GetCollSeq(
|
||||
if( p && !p->xCmp && synthCollSeq(db, p) ){
|
||||
p = 0;
|
||||
}
|
||||
assert( !p || p->xCmp );
|
||||
assert( p==0 || (sqlite3ValidCollSeq(p) && p->xCmp!=0) );
|
||||
if( p==0 ){
|
||||
sqlite3ErrorMsg(pParse, "no such collation sequence: %s", zName);
|
||||
}
|
||||
@@ -186,6 +186,31 @@ static CollSeq *findCollSeqEntry(
|
||||
return pColl;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/*
|
||||
** The following routine does sanity checking on a CollSeq object and
|
||||
** returns 1 if everything looks ok and 0 if the CollSeq object appears
|
||||
** to be corrupt. This routine is used only inside of assert() statements.
|
||||
*/
|
||||
int sqlite3ValidCollSeq(const CollSeq *p){
|
||||
/* The CollSeq must be one of a triple and the zName field must
|
||||
** point to the first byte after that triple
|
||||
*/
|
||||
int n = (int)(p->zName - (char*)p)/sizeof(CollSeq);
|
||||
if( n<=0 || n>3 ) return 0;
|
||||
|
||||
/* Check for valid enc values */
|
||||
if( p->enc==SQLITE_UTF8 ) return 1;
|
||||
if( p->enc==SQLITE_UTF16LE ) return 1;
|
||||
if( p->enc==SQLITE_UTF16BE ) return 1;
|
||||
if( p->enc==(SQLITE_UTF16LE|SQLITE_UTF16_ALIGNED) ) return 1;
|
||||
if( p->enc==(SQLITE_UTF16BE|SQLITE_UTF16_ALIGNED) ) return 1;
|
||||
|
||||
/* Otherwise, malformed */
|
||||
return 0;
|
||||
}
|
||||
#endif /* SQLITE_DEBUG */
|
||||
|
||||
/*
|
||||
** Parameter zName points to a UTF-8 encoded string nName bytes long.
|
||||
** Return the CollSeq* pointer for the collation sequence named zName
|
||||
@@ -215,7 +240,10 @@ CollSeq *sqlite3FindCollSeq(
|
||||
}
|
||||
assert( SQLITE_UTF8==1 && SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 );
|
||||
assert( enc>=SQLITE_UTF8 && enc<=SQLITE_UTF16BE );
|
||||
if( pColl ) pColl += enc-1;
|
||||
if( pColl ){
|
||||
pColl += enc-1;
|
||||
assert( sqlite3ValidCollSeq(pColl) );
|
||||
}
|
||||
return pColl;
|
||||
}
|
||||
|
||||
|
||||
+7
-7
@@ -105,6 +105,13 @@ int sqlite3_complete(const char *zSql){
|
||||
u8 state = 0; /* Current state, using numbers defined in header comment */
|
||||
u8 token; /* Value of the next token */
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( zSql==0 ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
/* A complex statement machine used to detect the end of a CREATE TRIGGER
|
||||
** statement. This is the normal case.
|
||||
@@ -134,13 +141,6 @@ int sqlite3_complete(const char *zSql){
|
||||
};
|
||||
#endif /* SQLITE_OMIT_TRIGGER */
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( zSql==0 ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
while( *zSql ){
|
||||
switch( *zSql ){
|
||||
case ';': { /* A semicolon */
|
||||
|
||||
+21
-60
@@ -437,7 +437,7 @@ static void fkLookupParent(
|
||||
OE_Abort, 0, P4_STATIC, P5_ConstraintFK);
|
||||
}else{
|
||||
if( nIncr>0 && pFKey->isDeferred==0 ){
|
||||
sqlite3MayAbort(pParse);
|
||||
sqlite3ParseToplevel(pParse)->mayAbort = 1;
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr);
|
||||
}
|
||||
@@ -509,10 +509,6 @@ static Expr *exprTableColumn(
|
||||
** code for an SQL UPDATE operation, this function may be called twice -
|
||||
** once to "delete" the old row and once to "insert" the new row.
|
||||
**
|
||||
** Parameter nIncr is passed -1 when inserting a row (as this may decrease
|
||||
** the number of FK violations in the db) or +1 when deleting one (as this
|
||||
** may increase the number of FK constraint problems).
|
||||
**
|
||||
** The code generated by this function scans through the rows in the child
|
||||
** table that correspond to the parent table row being deleted or inserted.
|
||||
** For each child row found, one of the following actions is taken:
|
||||
@@ -629,9 +625,13 @@ static void fkScanChildren(
|
||||
sqlite3ResolveExprNames(&sNameContext, pWhere);
|
||||
|
||||
/* Create VDBE to loop through the entries in pSrc that match the WHERE
|
||||
** clause. For each row found, increment either the deferred or immediate
|
||||
** foreign key constraint counter. */
|
||||
** clause. If the constraint is not deferred, throw an exception for
|
||||
** each row found. Otherwise, for deferred constraints, increment the
|
||||
** deferred constraint counter by nIncr for each row selected. */
|
||||
pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0);
|
||||
if( nIncr>0 && pFKey->isDeferred==0 ){
|
||||
sqlite3ParseToplevel(pParse)->mayAbort = 1;
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr);
|
||||
if( pWInfo ){
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
@@ -810,24 +810,6 @@ static int fkParentIsModified(
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if the parser passed as the first argument is being
|
||||
** used to code a trigger that is really a "SET NULL" action belonging
|
||||
** to trigger pFKey.
|
||||
*/
|
||||
static int isSetNullAction(Parse *pParse, FKey *pFKey){
|
||||
Parse *pTop = sqlite3ParseToplevel(pParse);
|
||||
if( pTop->pTriggerPrg ){
|
||||
Trigger *p = pTop->pTriggerPrg->pTrigger;
|
||||
if( (p==pFKey->apTrigger[0] && pFKey->aAction[0]==OE_SetNull)
|
||||
|| (p==pFKey->apTrigger[1] && pFKey->aAction[1]==OE_SetNull)
|
||||
){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called when inserting, deleting or updating a row of
|
||||
** table pTab to generate VDBE code to perform foreign key constraint
|
||||
@@ -880,7 +862,7 @@ void sqlite3FkCheck(
|
||||
int *aiCol;
|
||||
int iCol;
|
||||
int i;
|
||||
int bIgnore = 0;
|
||||
int isIgnore = 0;
|
||||
|
||||
if( aChange
|
||||
&& sqlite3_stricmp(pTab->zName, pFKey->zTo)!=0
|
||||
@@ -939,7 +921,7 @@ void sqlite3FkCheck(
|
||||
int rcauth;
|
||||
char *zCol = pTo->aCol[pIdx ? pIdx->aiColumn[i] : pTo->iPKey].zName;
|
||||
rcauth = sqlite3AuthReadCol(pParse, pTo->zName, zCol, iDb);
|
||||
bIgnore = (rcauth==SQLITE_IGNORE);
|
||||
isIgnore = (rcauth==SQLITE_IGNORE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -954,18 +936,12 @@ void sqlite3FkCheck(
|
||||
/* A row is being removed from the child table. Search for the parent.
|
||||
** If the parent does not exist, removing the child row resolves an
|
||||
** outstanding foreign key constraint violation. */
|
||||
fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regOld, -1, bIgnore);
|
||||
fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regOld, -1,isIgnore);
|
||||
}
|
||||
if( regNew!=0 && !isSetNullAction(pParse, pFKey) ){
|
||||
if( regNew!=0 ){
|
||||
/* A row is being added to the child table. If a parent row cannot
|
||||
** be found, adding the child row has violated the FK constraint.
|
||||
**
|
||||
** If this operation is being performed as part of a trigger program
|
||||
** that is actually a "SET NULL" action belonging to this very
|
||||
** foreign key, then omit this scan altogether. As all child key
|
||||
** values are guaranteed to be NULL, it is not possible for adding
|
||||
** this row to cause an FK violation. */
|
||||
fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regNew, +1, bIgnore);
|
||||
** be found, adding the child row has violated the FK constraint. */
|
||||
fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regNew, +1,isIgnore);
|
||||
}
|
||||
|
||||
sqlite3DbFree(db, aiFree);
|
||||
@@ -986,8 +962,8 @@ void sqlite3FkCheck(
|
||||
&& !pParse->pToplevel && !pParse->isMultiWrite
|
||||
){
|
||||
assert( regOld==0 && regNew!=0 );
|
||||
/* Inserting a single row into a parent table cannot cause (or fix)
|
||||
** an immediate foreign key violation. So do nothing in this case. */
|
||||
/* Inserting a single row into a parent table cannot cause an immediate
|
||||
** foreign key violation. So do nothing in this case. */
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1011,28 +987,13 @@ void sqlite3FkCheck(
|
||||
fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regNew, -1);
|
||||
}
|
||||
if( regOld!=0 ){
|
||||
int eAction = pFKey->aAction[aChange!=0];
|
||||
/* If there is a RESTRICT action configured for the current operation
|
||||
** on the parent table of this FK, then throw an exception
|
||||
** immediately if the FK constraint is violated, even if this is a
|
||||
** deferred trigger. That's what RESTRICT means. To defer checking
|
||||
** the constraint, the FK should specify NO ACTION (represented
|
||||
** using OE_None). NO ACTION is the default. */
|
||||
fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regOld, 1);
|
||||
/* If this is a deferred FK constraint, or a CASCADE or SET NULL
|
||||
** action applies, then any foreign key violations caused by
|
||||
** removing the parent key will be rectified by the action trigger.
|
||||
** So do not set the "may-abort" flag in this case.
|
||||
**
|
||||
** Note 1: If the FK is declared "ON UPDATE CASCADE", then the
|
||||
** may-abort flag will eventually be set on this statement anyway
|
||||
** (when this function is called as part of processing the UPDATE
|
||||
** within the action trigger).
|
||||
**
|
||||
** Note 2: At first glance it may seem like SQLite could simply omit
|
||||
** all OP_FkCounter related scans when either CASCADE or SET NULL
|
||||
** applies. The trouble starts if the CASCADE or SET NULL action
|
||||
** trigger causes other triggers or action rules attached to the
|
||||
** child table to fire. In these cases the fk constraint counters
|
||||
** might be set incorrectly if any OP_FkCounter related scans are
|
||||
** omitted. */
|
||||
if( !pFKey->isDeferred && eAction!=OE_Cascade && eAction!=OE_SetNull ){
|
||||
sqlite3MayAbort(pParse);
|
||||
}
|
||||
}
|
||||
pItem->zName = 0;
|
||||
sqlite3SrcListDelete(db, pSrc);
|
||||
|
||||
@@ -152,13 +152,6 @@ const unsigned char sqlite3CtypeMap[256] = {
|
||||
# define SQLITE_ALLOW_COVERING_INDEX_SCAN 1
|
||||
#endif
|
||||
|
||||
/* The minimum PMA size is set to this value multiplied by the database
|
||||
** page size in bytes.
|
||||
*/
|
||||
#ifndef SQLITE_SORTER_PMASZ
|
||||
# define SQLITE_SORTER_PMASZ 250
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The following singleton contains the global configuration for
|
||||
** the SQLite library.
|
||||
@@ -189,7 +182,6 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
|
||||
0, /* nPage */
|
||||
0, /* mxParserStack */
|
||||
0, /* sharedCacheEnabled */
|
||||
SQLITE_SORTER_PMASZ, /* szPma */
|
||||
/* All the rest should always be initialized to zero */
|
||||
0, /* isInit */
|
||||
0, /* inProgress */
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
# define sqlite3_column_table_name16 0
|
||||
# define sqlite3_column_origin_name 0
|
||||
# define sqlite3_column_origin_name16 0
|
||||
# define sqlite3_table_column_metadata 0
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_AUTHORIZATION
|
||||
|
||||
+47
-34
@@ -62,7 +62,7 @@ int sqlite3_threadsafe(void){ return SQLITE_THREADSAFE; }
|
||||
** I/O active are written using this function. These messages
|
||||
** are intended for debugging activity only.
|
||||
*/
|
||||
/* not-private */ void (*sqlite3IoTrace)(const char*, ...) = 0;
|
||||
void (*sqlite3IoTrace)(const char*, ...) = 0;
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -271,13 +271,6 @@ int sqlite3_initialize(void){
|
||||
** when this routine is invoked, then this routine is a harmless no-op.
|
||||
*/
|
||||
int sqlite3_shutdown(void){
|
||||
#ifdef SQLITE_OMIT_WSD
|
||||
int rc = sqlite3_wsd_init(4096, 24);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
if( sqlite3GlobalConfig.isInit ){
|
||||
#ifdef SQLITE_EXTRA_SHUTDOWN
|
||||
void SQLITE_EXTRA_SHUTDOWN(void);
|
||||
@@ -594,11 +587,6 @@ int sqlite3_config(int op, ...){
|
||||
}
|
||||
#endif
|
||||
|
||||
case SQLITE_CONFIG_PMASZ: {
|
||||
sqlite3GlobalConfig.szPma = va_arg(ap, unsigned int);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
rc = SQLITE_ERROR;
|
||||
break;
|
||||
@@ -1046,7 +1034,13 @@ void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){
|
||||
if( pDb->pBt ){
|
||||
sqlite3BtreeClose(pDb->pBt);
|
||||
pDb->pBt = 0;
|
||||
if( j!=1 ){
|
||||
if( j!=1 && pDb->pSchema ){
|
||||
/* Must clear the KeyInfo cache. See ticket [e4a18565a36884b00edf] */
|
||||
for(i=sqliteHashFirst(&pDb->pSchema->idxHash); i; i=sqliteHashNext(i)){
|
||||
Index *pIdx = sqliteHashData(i);
|
||||
sqlite3KeyInfoUnref(pIdx->pKeyInfo);
|
||||
pIdx->pKeyInfo = 0;
|
||||
}
|
||||
pDb->pSchema = 0;
|
||||
}
|
||||
}
|
||||
@@ -2171,6 +2165,32 @@ const char *sqlite3_errstr(int rc){
|
||||
return sqlite3ErrStr(rc);
|
||||
}
|
||||
|
||||
/*
|
||||
** Invalidate all cached KeyInfo objects for database connection "db"
|
||||
*/
|
||||
static void invalidateCachedKeyInfo(sqlite3 *db){
|
||||
Db *pDb; /* A single database */
|
||||
int iDb; /* The database index number */
|
||||
HashElem *k; /* For looping over tables in pDb */
|
||||
Table *pTab; /* A table in the database */
|
||||
Index *pIdx; /* Each index */
|
||||
|
||||
for(iDb=0, pDb=db->aDb; iDb<db->nDb; iDb++, pDb++){
|
||||
if( pDb->pBt==0 ) continue;
|
||||
sqlite3BtreeEnter(pDb->pBt);
|
||||
for(k=sqliteHashFirst(&pDb->pSchema->tblHash); k; k=sqliteHashNext(k)){
|
||||
pTab = (Table*)sqliteHashData(k);
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
if( pIdx->pKeyInfo && pIdx->pKeyInfo->db==db ){
|
||||
sqlite3KeyInfoUnref(pIdx->pKeyInfo);
|
||||
pIdx->pKeyInfo = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3BtreeLeave(pDb->pBt);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Create a new collating function for database "db". The name is zName
|
||||
** and the encoding is enc.
|
||||
@@ -2214,6 +2234,7 @@ static int createCollation(
|
||||
return SQLITE_BUSY;
|
||||
}
|
||||
sqlite3ExpirePreparedStatements(db);
|
||||
invalidateCachedKeyInfo(db);
|
||||
|
||||
/* If collation sequence pColl was created directly by a call to
|
||||
** sqlite3_create_collation, and not generated by synthCollSeq(),
|
||||
@@ -2717,9 +2738,6 @@ static int openDatabase(
|
||||
#endif
|
||||
#if defined(SQLITE_DEFAULT_FOREIGN_KEYS) && SQLITE_DEFAULT_FOREIGN_KEYS
|
||||
| SQLITE_ForeignKeys
|
||||
#endif
|
||||
#if defined(SQLITE_REVERSE_UNORDERED_SELECTS)
|
||||
| SQLITE_ReverseOrder
|
||||
#endif
|
||||
;
|
||||
sqlite3HashInit(&db->aCollSeq);
|
||||
@@ -2768,10 +2786,7 @@ static int openDatabase(
|
||||
sqlite3Error(db, rc);
|
||||
goto opendb_out;
|
||||
}
|
||||
sqlite3BtreeEnter(db->aDb[0].pBt);
|
||||
db->aDb[0].pSchema = sqlite3SchemaGet(db, db->aDb[0].pBt);
|
||||
if( !db->mallocFailed ) ENC(db) = SCHEMA_ENC(db);
|
||||
sqlite3BtreeLeave(db->aDb[0].pBt);
|
||||
db->aDb[1].pSchema = sqlite3SchemaGet(db, 0);
|
||||
|
||||
/* The default safety_level for the main database is 'full'; for the temp
|
||||
@@ -2929,7 +2944,7 @@ int sqlite3_open16(
|
||||
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0);
|
||||
assert( *ppDb || rc==SQLITE_NOMEM );
|
||||
if( rc==SQLITE_OK && !DbHasProperty(*ppDb, 0, DB_SchemaLoaded) ){
|
||||
SCHEMA_ENC(*ppDb) = ENC(*ppDb) = SQLITE_UTF16NATIVE;
|
||||
ENC(*ppDb) = SQLITE_UTF16NATIVE;
|
||||
}
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
@@ -3125,6 +3140,7 @@ void sqlite3_thread_cleanup(void){
|
||||
** Return meta information about a specific column of a database table.
|
||||
** See comment in sqlite3.h (sqlite.h.in) for details.
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_COLUMN_METADATA
|
||||
int sqlite3_table_column_metadata(
|
||||
sqlite3 *db, /* Connection handle */
|
||||
const char *zDbName, /* Database name or NULL */
|
||||
@@ -3164,8 +3180,11 @@ int sqlite3_table_column_metadata(
|
||||
}
|
||||
|
||||
/* Find the column for which info is requested */
|
||||
if( zColumnName==0 ){
|
||||
/* Query for existance of table only */
|
||||
if( sqlite3IsRowid(zColumnName) ){
|
||||
iCol = pTab->iPKey;
|
||||
if( iCol>=0 ){
|
||||
pCol = &pTab->aCol[iCol];
|
||||
}
|
||||
}else{
|
||||
for(iCol=0; iCol<pTab->nCol; iCol++){
|
||||
pCol = &pTab->aCol[iCol];
|
||||
@@ -3174,13 +3193,8 @@ int sqlite3_table_column_metadata(
|
||||
}
|
||||
}
|
||||
if( iCol==pTab->nCol ){
|
||||
if( HasRowid(pTab) && sqlite3IsRowid(zColumnName) ){
|
||||
iCol = pTab->iPKey;
|
||||
pCol = iCol>=0 ? &pTab->aCol[iCol] : 0;
|
||||
}else{
|
||||
pTab = 0;
|
||||
goto error_out;
|
||||
}
|
||||
pTab = 0;
|
||||
goto error_out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3233,6 +3247,7 @@ error_out:
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Sleep for a little while. Return the amount of time slept.
|
||||
@@ -3671,14 +3686,13 @@ Btree *sqlite3DbNameToBtree(sqlite3 *db, const char *zDbName){
|
||||
** connection.
|
||||
*/
|
||||
const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName){
|
||||
Btree *pBt;
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
pBt = sqlite3DbNameToBtree(db, zDbName);
|
||||
Btree *pBt = sqlite3DbNameToBtree(db, zDbName);
|
||||
return pBt ? sqlite3BtreeGetFilename(pBt) : 0;
|
||||
}
|
||||
|
||||
@@ -3687,13 +3701,12 @@ const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName){
|
||||
** no such database exists.
|
||||
*/
|
||||
int sqlite3_db_readonly(sqlite3 *db, const char *zDbName){
|
||||
Btree *pBt;
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
pBt = sqlite3DbNameToBtree(db, zDbName);
|
||||
Btree *pBt = sqlite3DbNameToBtree(db, zDbName);
|
||||
return pBt ? sqlite3BtreeIsReadonly(pBt) : -1;
|
||||
}
|
||||
|
||||
@@ -209,12 +209,6 @@ static sqlite3_mutex *winMutexAlloc(int iType){
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( iType-2<0 || iType-2>=ArraySize(winMutex_staticMutexes) ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
assert( iType-2 >= 0 );
|
||||
assert( iType-2 < ArraySize(winMutex_staticMutexes) );
|
||||
assert( winMutex_isInit==1 );
|
||||
|
||||
+11
-14
@@ -3709,27 +3709,24 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){
|
||||
}while( err==EINTR );
|
||||
if( err ) return SQLITE_IOERR_WRITE;
|
||||
#else
|
||||
/* If the OS does not have posix_fallocate(), fake it. Write a
|
||||
** single byte to the last byte in each block that falls entirely
|
||||
** within the extended region. Then, if required, a single byte
|
||||
** at offset (nSize-1), to set the size of the file correctly.
|
||||
** This is a similar technique to that used by glibc on systems
|
||||
** that do not have a real fallocate() call.
|
||||
/* If the OS does not have posix_fallocate(), fake it. First use
|
||||
** ftruncate() to set the file size, then write a single byte to
|
||||
** the last byte in each block within the extended region. This
|
||||
** is the same technique used by glibc to implement posix_fallocate()
|
||||
** on systems that do not have a real fallocate() system call.
|
||||
*/
|
||||
int nBlk = buf.st_blksize; /* File-system block size */
|
||||
i64 iWrite; /* Next offset to write to */
|
||||
|
||||
if( robust_ftruncate(pFile->h, nSize) ){
|
||||
pFile->lastErrno = errno;
|
||||
return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath);
|
||||
}
|
||||
iWrite = ((buf.st_size + 2*nBlk - 1)/nBlk)*nBlk-1;
|
||||
assert( iWrite>=buf.st_size );
|
||||
assert( (iWrite/nBlk)==((buf.st_size+nBlk-1)/nBlk) );
|
||||
assert( ((iWrite+1)%nBlk)==0 );
|
||||
for(/*no-op*/; iWrite<nSize; iWrite+=nBlk ){
|
||||
while( iWrite<nSize ){
|
||||
int nWrite = seekAndWrite(pFile, iWrite, "", 1);
|
||||
if( nWrite!=1 ) return SQLITE_IOERR_WRITE;
|
||||
}
|
||||
if( nSize%nBlk ){
|
||||
int nWrite = seekAndWrite(pFile, nSize-1, "", 1);
|
||||
if( nWrite!=1 ) return SQLITE_IOERR_WRITE;
|
||||
iWrite += nBlk;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
+9
-9
@@ -1203,8 +1203,8 @@ int sqlite3_win32_reset_heap(){
|
||||
int rc;
|
||||
MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */
|
||||
MUTEX_LOGIC( sqlite3_mutex *pMem; ) /* The memsys static mutex */
|
||||
MUTEX_LOGIC( pMaster = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER); )
|
||||
MUTEX_LOGIC( pMem = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MEM); )
|
||||
MUTEX_LOGIC( pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); )
|
||||
MUTEX_LOGIC( pMem = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); )
|
||||
sqlite3_mutex_enter(pMaster);
|
||||
sqlite3_mutex_enter(pMem);
|
||||
winMemAssertMagic();
|
||||
@@ -2479,7 +2479,7 @@ static int winRead(
|
||||
int amt, /* Number of bytes to read */
|
||||
sqlite3_int64 offset /* Begin reading at this offset */
|
||||
){
|
||||
#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED)
|
||||
#if !SQLITE_OS_WINCE
|
||||
OVERLAPPED overlapped; /* The offset for ReadFile. */
|
||||
#endif
|
||||
winFile *pFile = (winFile*)id; /* file handle */
|
||||
@@ -2511,7 +2511,7 @@ static int winRead(
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED)
|
||||
#if SQLITE_OS_WINCE
|
||||
if( winSeekFile(pFile, offset) ){
|
||||
OSTRACE(("READ file=%p, rc=SQLITE_FULL\n", pFile->h));
|
||||
return SQLITE_FULL;
|
||||
@@ -2583,13 +2583,13 @@ static int winWrite(
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED)
|
||||
#if SQLITE_OS_WINCE
|
||||
rc = winSeekFile(pFile, offset);
|
||||
if( rc==0 ){
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED)
|
||||
#if !SQLITE_OS_WINCE
|
||||
OVERLAPPED overlapped; /* The offset for WriteFile. */
|
||||
#endif
|
||||
u8 *aRem = (u8 *)pBuf; /* Data yet to be written */
|
||||
@@ -2597,14 +2597,14 @@ static int winWrite(
|
||||
DWORD nWrite; /* Bytes written by each WriteFile() call */
|
||||
DWORD lastErrno = NO_ERROR; /* Value returned by GetLastError() */
|
||||
|
||||
#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED)
|
||||
#if !SQLITE_OS_WINCE
|
||||
memset(&overlapped, 0, sizeof(OVERLAPPED));
|
||||
overlapped.Offset = (LONG)(offset & 0xffffffff);
|
||||
overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff);
|
||||
#endif
|
||||
|
||||
while( nRem>0 ){
|
||||
#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED)
|
||||
#if SQLITE_OS_WINCE
|
||||
if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, 0) ){
|
||||
#else
|
||||
if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, &overlapped) ){
|
||||
@@ -2617,7 +2617,7 @@ static int winWrite(
|
||||
lastErrno = osGetLastError();
|
||||
break;
|
||||
}
|
||||
#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED)
|
||||
#if !SQLITE_OS_WINCE
|
||||
offset += nWrite;
|
||||
overlapped.Offset = (LONG)(offset & 0xffffffff);
|
||||
overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff);
|
||||
|
||||
+12
-22
@@ -646,8 +646,6 @@ struct Pager {
|
||||
u8 setMaster; /* True if a m-j name has been written to jrnl */
|
||||
u8 doNotSpill; /* Do not spill the cache when non-zero */
|
||||
u8 subjInMemory; /* True to use in-memory sub-journals */
|
||||
u8 bUseFetch; /* True to use xFetch() */
|
||||
u8 hasBeenUsed; /* True if any content previously read from this pager*/
|
||||
Pgno dbSize; /* Number of pages in the database */
|
||||
Pgno dbOrigSize; /* dbSize before the current transaction */
|
||||
Pgno dbFileSize; /* Number of pages in the database file */
|
||||
@@ -665,9 +663,9 @@ struct Pager {
|
||||
sqlite3_backup *pBackup; /* Pointer to list of ongoing backup processes */
|
||||
PagerSavepoint *aSavepoint; /* Array of active savepoints */
|
||||
int nSavepoint; /* Number of elements in aSavepoint[] */
|
||||
u32 iDataVersion; /* Changes whenever database content changes */
|
||||
char dbFileVers[16]; /* Changes whenever database file changes */
|
||||
|
||||
u8 bUseFetch; /* True to use xFetch() */
|
||||
int nMmapOut; /* Number of mmap pages currently outstanding */
|
||||
sqlite3_int64 szMmap; /* Desired maximum mmap size */
|
||||
PgHdr *pMmapFreelist; /* List of free mmap page headers (pDirty) */
|
||||
@@ -1683,19 +1681,10 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){
|
||||
** Discard the entire contents of the in-memory page-cache.
|
||||
*/
|
||||
static void pager_reset(Pager *pPager){
|
||||
pPager->iDataVersion++;
|
||||
sqlite3BackupRestart(pPager->pBackup);
|
||||
sqlite3PcacheClear(pPager->pPCache);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the pPager->iDataVersion value
|
||||
*/
|
||||
u32 sqlite3PagerDataVersion(Pager *pPager){
|
||||
assert( pPager->eState>PAGER_OPEN );
|
||||
return pPager->iDataVersion;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free all structures in the Pager.aSavepoint[] array and set both
|
||||
** Pager.aSavepoint and Pager.nSavepoint to zero. Close the sub-journal
|
||||
@@ -3898,7 +3887,7 @@ static int pagerAcquireMapPage(
|
||||
PgHdr **ppPage /* OUT: Acquired page object */
|
||||
){
|
||||
PgHdr *p; /* Memory mapped page to return */
|
||||
|
||||
|
||||
if( pPager->pMmapFreelist ){
|
||||
*ppPage = p = pPager->pMmapFreelist;
|
||||
pPager->pMmapFreelist = p->pDirty;
|
||||
@@ -5129,12 +5118,16 @@ int sqlite3PagerSharedLock(Pager *pPager){
|
||||
);
|
||||
}
|
||||
|
||||
if( !pPager->tempFile && pPager->hasBeenUsed ){
|
||||
/* The shared-lock has just been acquired then check to
|
||||
** see if the database has been modified. If the database has changed,
|
||||
** flush the cache. The pPager->hasBeenUsed flag prevents this from
|
||||
** occurring on the very first access to a file, in order to save a
|
||||
** single unnecessary sqlite3OsRead() call at the start-up.
|
||||
if( !pPager->tempFile && (
|
||||
pPager->pBackup
|
||||
|| sqlite3PcachePagecount(pPager->pPCache)>0
|
||||
|| USEFETCH(pPager)
|
||||
)){
|
||||
/* The shared-lock has just been acquired on the database file
|
||||
** and there are already pages in the cache (from a previous
|
||||
** read or write transaction). Check to see if the database
|
||||
** has been modified. If the database has changed, flush the
|
||||
** cache.
|
||||
**
|
||||
** Database changes is detected by looking at 15 bytes beginning
|
||||
** at offset 24 into the file. The first 4 of these 16 bytes are
|
||||
@@ -5299,7 +5292,6 @@ int sqlite3PagerAcquire(
|
||||
if( pgno==0 ){
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
pPager->hasBeenUsed = 1;
|
||||
|
||||
/* If the pager is in the error state, return an error immediately.
|
||||
** Otherwise, request the page from the PCache layer. */
|
||||
@@ -5449,7 +5441,6 @@ DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){
|
||||
assert( pgno!=0 );
|
||||
assert( pPager->pPCache!=0 );
|
||||
pPage = sqlite3PcacheFetch(pPager->pPCache, pgno, 0);
|
||||
assert( pPage==0 || pPager->hasBeenUsed );
|
||||
return sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pPage);
|
||||
}
|
||||
|
||||
@@ -6316,7 +6307,6 @@ int sqlite3PagerCommitPhaseTwo(Pager *pPager){
|
||||
}
|
||||
|
||||
PAGERTRACE(("COMMIT %d\n", PAGERID(pPager)));
|
||||
pPager->iDataVersion++;
|
||||
rc = pager_end_transaction(pPager, pPager->setMaster, 1);
|
||||
return pager_error(pPager, rc);
|
||||
}
|
||||
|
||||
@@ -172,7 +172,6 @@ int sqlite3PagerSharedLock(Pager *pPager);
|
||||
|
||||
/* Functions used to query pager state and configuration. */
|
||||
u8 sqlite3PagerIsreadonly(Pager*);
|
||||
u32 sqlite3PagerDataVersion(Pager*);
|
||||
int sqlite3PagerRefcount(Pager*);
|
||||
int sqlite3PagerMemUsed(Pager*);
|
||||
const char *sqlite3PagerFilename(Pager*, int);
|
||||
|
||||
+2
-3
@@ -196,8 +196,7 @@ int sqlite3PcacheSetPageSize(PCache *pCache, int szPage){
|
||||
if( pCache->szPage ){
|
||||
sqlite3_pcache *pNew;
|
||||
pNew = sqlite3GlobalConfig.pcache2.xCreate(
|
||||
szPage, pCache->szExtra + ROUND8(sizeof(PgHdr)),
|
||||
pCache->bPurgeable
|
||||
szPage, pCache->szExtra + sizeof(PgHdr), pCache->bPurgeable
|
||||
);
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
sqlite3GlobalConfig.pcache2.xCachesize(pNew, numberOfCachePages(pCache));
|
||||
@@ -656,7 +655,7 @@ void sqlite3PcacheShrink(PCache *pCache){
|
||||
** Return the size of the header added by this middleware layer
|
||||
** in the page-cache hierarchy.
|
||||
*/
|
||||
int sqlite3HeaderSizePcache(void){ return ROUND8(sizeof(PgHdr)); }
|
||||
int sqlite3HeaderSizePcache(void){ return sizeof(PgHdr); }
|
||||
|
||||
|
||||
#if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG)
|
||||
|
||||
+2
-2
@@ -296,7 +296,7 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
|
||||
pPg = 0;
|
||||
}
|
||||
#else
|
||||
pPg = pcache1Alloc(ROUND8(sizeof(PgHdr1)) + pCache->szPage + pCache->szExtra);
|
||||
pPg = pcache1Alloc(sizeof(PgHdr1) + pCache->szPage + pCache->szExtra);
|
||||
p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage];
|
||||
#endif
|
||||
pcache1EnterMutex(pCache->pGroup);
|
||||
@@ -984,7 +984,7 @@ void sqlite3PCacheSetDefault(void){
|
||||
/*
|
||||
** Return the size of the header on each page of this PCACHE implementation.
|
||||
*/
|
||||
int sqlite3HeaderSizePcache1(void){ return ROUND8(sizeof(PgHdr1)); }
|
||||
int sqlite3HeaderSizePcache1(void){ return sizeof(PgHdr1); }
|
||||
|
||||
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
||||
/*
|
||||
|
||||
+24
-17
@@ -71,7 +71,6 @@
|
||||
#define PragTyp_LOCK_STATUS 40
|
||||
#define PragTyp_PARSER_TRACE 41
|
||||
#define PragFlag_NeedSchema 0x01
|
||||
#define PragFlag_ReadOnly 0x02
|
||||
static const struct sPragmaNames {
|
||||
const char *const zName; /* Name of pragma */
|
||||
u8 ePragTyp; /* PragTyp_XXX value */
|
||||
@@ -88,7 +87,7 @@ static const struct sPragmaNames {
|
||||
{ /* zName: */ "application_id",
|
||||
/* ePragTyp: */ PragTyp_HEADER_VALUE,
|
||||
/* ePragFlag: */ 0,
|
||||
/* iArg: */ BTREE_APPLICATION_ID },
|
||||
/* iArg: */ 0 },
|
||||
#endif
|
||||
#if !defined(SQLITE_OMIT_AUTOVACUUM)
|
||||
{ /* zName: */ "auto_vacuum",
|
||||
@@ -154,12 +153,6 @@ static const struct sPragmaNames {
|
||||
/* ePragFlag: */ 0,
|
||||
/* iArg: */ 0 },
|
||||
#endif
|
||||
#if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
|
||||
{ /* zName: */ "data_version",
|
||||
/* ePragTyp: */ PragTyp_HEADER_VALUE,
|
||||
/* ePragFlag: */ PragFlag_ReadOnly,
|
||||
/* iArg: */ BTREE_DATA_VERSION },
|
||||
#endif
|
||||
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
|
||||
{ /* zName: */ "database_list",
|
||||
/* ePragTyp: */ PragTyp_DATABASE_LIST,
|
||||
@@ -215,8 +208,8 @@ static const struct sPragmaNames {
|
||||
#if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
|
||||
{ /* zName: */ "freelist_count",
|
||||
/* ePragTyp: */ PragTyp_HEADER_VALUE,
|
||||
/* ePragFlag: */ PragFlag_ReadOnly,
|
||||
/* iArg: */ BTREE_FREE_PAGE_COUNT },
|
||||
/* ePragFlag: */ 0,
|
||||
/* iArg: */ 0 },
|
||||
#endif
|
||||
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
|
||||
{ /* zName: */ "full_column_names",
|
||||
@@ -368,7 +361,7 @@ static const struct sPragmaNames {
|
||||
{ /* zName: */ "schema_version",
|
||||
/* ePragTyp: */ PragTyp_HEADER_VALUE,
|
||||
/* ePragFlag: */ 0,
|
||||
/* iArg: */ BTREE_SCHEMA_VERSION },
|
||||
/* iArg: */ 0 },
|
||||
#endif
|
||||
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
|
||||
{ /* zName: */ "secure_delete",
|
||||
@@ -434,7 +427,7 @@ static const struct sPragmaNames {
|
||||
{ /* zName: */ "user_version",
|
||||
/* ePragTyp: */ PragTyp_HEADER_VALUE,
|
||||
/* ePragFlag: */ 0,
|
||||
/* iArg: */ BTREE_USER_VERSION },
|
||||
/* iArg: */ 0 },
|
||||
#endif
|
||||
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
|
||||
#if defined(SQLITE_DEBUG)
|
||||
@@ -477,7 +470,7 @@ static const struct sPragmaNames {
|
||||
/* iArg: */ SQLITE_WriteSchema|SQLITE_RecoveryMode },
|
||||
#endif
|
||||
};
|
||||
/* Number of pragmas: 58 on by default, 71 total. */
|
||||
/* Number of pragmas: 57 on by default, 70 total. */
|
||||
/* End of the automatically generated pragma table.
|
||||
***************************************************************************/
|
||||
|
||||
@@ -2087,8 +2080,7 @@ void sqlite3Pragma(
|
||||
){
|
||||
for(pEnc=&encnames[0]; pEnc->zName; pEnc++){
|
||||
if( 0==sqlite3StrICmp(zRight, pEnc->zName) ){
|
||||
SCHEMA_ENC(db) = ENC(db) =
|
||||
pEnc->enc ? pEnc->enc : SQLITE_UTF16NATIVE;
|
||||
ENC(pParse->db) = pEnc->enc ? pEnc->enc : SQLITE_UTF16NATIVE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2133,9 +2125,24 @@ void sqlite3Pragma(
|
||||
** applications for any purpose.
|
||||
*/
|
||||
case PragTyp_HEADER_VALUE: {
|
||||
int iCookie = aPragmaNames[mid].iArg; /* Which cookie to read or write */
|
||||
int iCookie; /* Cookie index. 1 for schema-cookie, 6 for user-cookie. */
|
||||
sqlite3VdbeUsesBtree(v, iDb);
|
||||
if( zRight && (aPragmaNames[mid].mPragFlag & PragFlag_ReadOnly)==0 ){
|
||||
switch( zLeft[0] ){
|
||||
case 'a': case 'A':
|
||||
iCookie = BTREE_APPLICATION_ID;
|
||||
break;
|
||||
case 'f': case 'F':
|
||||
iCookie = BTREE_FREE_PAGE_COUNT;
|
||||
break;
|
||||
case 's': case 'S':
|
||||
iCookie = BTREE_SCHEMA_VERSION;
|
||||
break;
|
||||
default:
|
||||
iCookie = BTREE_USER_VERSION;
|
||||
break;
|
||||
}
|
||||
|
||||
if( zRight && iCookie!=BTREE_FREE_PAGE_COUNT ){
|
||||
/* Write the specified cookie value */
|
||||
static const VdbeOpList setCookie[] = {
|
||||
{ OP_Transaction, 0, 1, 0}, /* 0 */
|
||||
|
||||
@@ -394,11 +394,9 @@ int sqlite3Init(sqlite3 *db, char **pzErrMsg){
|
||||
int commit_internal = !(db->flags&SQLITE_InternChanges);
|
||||
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
assert( sqlite3BtreeHoldsMutex(db->aDb[0].pBt) );
|
||||
assert( db->init.busy==0 );
|
||||
rc = SQLITE_OK;
|
||||
db->init.busy = 1;
|
||||
ENC(db) = SCHEMA_ENC(db);
|
||||
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
|
||||
if( DbHasProperty(db, i, DB_SchemaLoaded) || i==1 ) continue;
|
||||
rc = sqlite3InitOne(db, i, pzErrMsg);
|
||||
|
||||
+122
-246
@@ -471,11 +471,11 @@ struct ShellState {
|
||||
int showHeader; /* True to show column names in List or Column mode */
|
||||
unsigned shellFlgs; /* Various flags */
|
||||
char *zDestTable; /* Name of destination table when MODE_Insert */
|
||||
char colSeparator[20]; /* Column separator character for several modes */
|
||||
char rowSeparator[20]; /* Row separator character for MODE_Ascii */
|
||||
char separator[20]; /* Separator character for MODE_List */
|
||||
char newline[20]; /* Record separator in MODE_Csv */
|
||||
int colWidth[100]; /* Requested width of each column when in column mode*/
|
||||
int actualWidth[100]; /* Actual width of each column */
|
||||
char nullValue[20]; /* The text to print when a NULL comes back from
|
||||
char nullvalue[20]; /* The text to print when a NULL comes back from
|
||||
** the database */
|
||||
SavedModeInfo normalMode;/* Holds the mode just before .explain ON */
|
||||
char outfile[FILENAME_MAX]; /* Filename for *out */
|
||||
@@ -508,7 +508,6 @@ struct ShellState {
|
||||
#define MODE_Tcl 6 /* Generate ANSI-C or TCL quoted elements */
|
||||
#define MODE_Csv 7 /* Quote strings, numbers are plain */
|
||||
#define MODE_Explain 8 /* Like MODE_Column, but do not truncate data */
|
||||
#define MODE_Ascii 9 /* Use ASCII unit and record separators (0x1F/0x1E) */
|
||||
|
||||
static const char *modeDescr[] = {
|
||||
"line",
|
||||
@@ -520,22 +519,8 @@ static const char *modeDescr[] = {
|
||||
"tcl",
|
||||
"csv",
|
||||
"explain",
|
||||
"ascii",
|
||||
};
|
||||
|
||||
/*
|
||||
** These are the column/row/line separators used by the various
|
||||
** import/export modes.
|
||||
*/
|
||||
#define SEP_Column "|"
|
||||
#define SEP_Row "\n"
|
||||
#define SEP_Tab "\t"
|
||||
#define SEP_Space " "
|
||||
#define SEP_Comma ","
|
||||
#define SEP_CrLf "\r\n"
|
||||
#define SEP_Unit "\x1F"
|
||||
#define SEP_Record "\x1E"
|
||||
|
||||
/*
|
||||
** Number of elements in an array
|
||||
*/
|
||||
@@ -692,22 +677,22 @@ static const char needCsvQuote[] = {
|
||||
};
|
||||
|
||||
/*
|
||||
** Output a single term of CSV. Actually, p->colSeparator is used for
|
||||
** the separator, which may or may not be a comma. p->nullValue is
|
||||
** Output a single term of CSV. Actually, p->separator is used for
|
||||
** the separator, which may or may not be a comma. p->nullvalue is
|
||||
** the null value. Strings are quoted if necessary. The separator
|
||||
** is only issued if bSep is true.
|
||||
*/
|
||||
static void output_csv(ShellState *p, const char *z, int bSep){
|
||||
FILE *out = p->out;
|
||||
if( z==0 ){
|
||||
fprintf(out,"%s",p->nullValue);
|
||||
fprintf(out,"%s",p->nullvalue);
|
||||
}else{
|
||||
int i;
|
||||
int nSep = strlen30(p->colSeparator);
|
||||
int nSep = strlen30(p->separator);
|
||||
for(i=0; z[i]; i++){
|
||||
if( needCsvQuote[((unsigned char*)z)[i]]
|
||||
|| (z[i]==p->colSeparator[0] &&
|
||||
(nSep==1 || memcmp(z, p->colSeparator, nSep)==0)) ){
|
||||
|| (z[i]==p->separator[0] &&
|
||||
(nSep==1 || memcmp(z, p->separator, nSep)==0)) ){
|
||||
i = 0;
|
||||
break;
|
||||
}
|
||||
@@ -724,7 +709,7 @@ static void output_csv(ShellState *p, const char *z, int bSep){
|
||||
}
|
||||
}
|
||||
if( bSep ){
|
||||
fprintf(p->out, "%s", p->colSeparator);
|
||||
fprintf(p->out, "%s", p->separator);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -762,10 +747,10 @@ static int shell_callback(
|
||||
int len = strlen30(azCol[i] ? azCol[i] : "");
|
||||
if( len>w ) w = len;
|
||||
}
|
||||
if( p->cnt++>0 ) fprintf(p->out, "%s", p->rowSeparator);
|
||||
if( p->cnt++>0 ) fprintf(p->out,"\n");
|
||||
for(i=0; i<nArg; i++){
|
||||
fprintf(p->out,"%*s = %s%s", w, azCol[i],
|
||||
azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator);
|
||||
fprintf(p->out,"%*s = %s\n", w, azCol[i],
|
||||
azArg[i] ? azArg[i] : p->nullvalue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -782,7 +767,7 @@ static int shell_callback(
|
||||
if( w==0 ){
|
||||
w = strlen30(azCol[i] ? azCol[i] : "");
|
||||
if( w<10 ) w = 10;
|
||||
n = strlen30(azArg && azArg[i] ? azArg[i] : p->nullValue);
|
||||
n = strlen30(azArg && azArg[i] ? azArg[i] : p->nullvalue);
|
||||
if( w<n ) w = n;
|
||||
}
|
||||
if( i<ArraySize(p->actualWidth) ){
|
||||
@@ -790,11 +775,9 @@ static int shell_callback(
|
||||
}
|
||||
if( p->showHeader ){
|
||||
if( w<0 ){
|
||||
fprintf(p->out,"%*.*s%s",-w,-w,azCol[i],
|
||||
i==nArg-1 ? p->rowSeparator : " ");
|
||||
fprintf(p->out,"%*.*s%s",-w,-w,azCol[i], i==nArg-1 ? "\n": " ");
|
||||
}else{
|
||||
fprintf(p->out,"%-*.*s%s",w,w,azCol[i],
|
||||
i==nArg-1 ? p->rowSeparator : " ");
|
||||
fprintf(p->out,"%-*.*s%s",w,w,azCol[i], i==nArg-1 ? "\n": " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -809,7 +792,7 @@ static int shell_callback(
|
||||
}
|
||||
fprintf(p->out,"%-*.*s%s",w,w,"-----------------------------------"
|
||||
"----------------------------------------------------------",
|
||||
i==nArg-1 ? p->rowSeparator : " ");
|
||||
i==nArg-1 ? "\n": " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -832,12 +815,10 @@ static int shell_callback(
|
||||
}
|
||||
if( w<0 ){
|
||||
fprintf(p->out,"%*.*s%s",-w,-w,
|
||||
azArg[i] ? azArg[i] : p->nullValue,
|
||||
i==nArg-1 ? p->rowSeparator : " ");
|
||||
azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " ");
|
||||
}else{
|
||||
fprintf(p->out,"%-*.*s%s",w,w,
|
||||
azArg[i] ? azArg[i] : p->nullValue,
|
||||
i==nArg-1 ? p->rowSeparator : " ");
|
||||
azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " ");
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -846,21 +827,20 @@ static int shell_callback(
|
||||
case MODE_List: {
|
||||
if( p->cnt++==0 && p->showHeader ){
|
||||
for(i=0; i<nArg; i++){
|
||||
fprintf(p->out,"%s%s",azCol[i],
|
||||
i==nArg-1 ? p->rowSeparator : p->colSeparator);
|
||||
fprintf(p->out,"%s%s",azCol[i], i==nArg-1 ? "\n" : p->separator);
|
||||
}
|
||||
}
|
||||
if( azArg==0 ) break;
|
||||
for(i=0; i<nArg; i++){
|
||||
char *z = azArg[i];
|
||||
if( z==0 ) z = p->nullValue;
|
||||
if( z==0 ) z = p->nullvalue;
|
||||
fprintf(p->out, "%s", z);
|
||||
if( i<nArg-1 ){
|
||||
fprintf(p->out, "%s", p->colSeparator);
|
||||
fprintf(p->out, "%s", p->separator);
|
||||
}else if( p->mode==MODE_Semi ){
|
||||
fprintf(p->out, ";%s", p->rowSeparator);
|
||||
fprintf(p->out, ";\n");
|
||||
}else{
|
||||
fprintf(p->out, "%s", p->rowSeparator);
|
||||
fprintf(p->out, "\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -879,7 +859,7 @@ static int shell_callback(
|
||||
fprintf(p->out,"<TR>");
|
||||
for(i=0; i<nArg; i++){
|
||||
fprintf(p->out,"<TD>");
|
||||
output_html_string(p->out, azArg[i] ? azArg[i] : p->nullValue);
|
||||
output_html_string(p->out, azArg[i] ? azArg[i] : p->nullvalue);
|
||||
fprintf(p->out,"</TD>\n");
|
||||
}
|
||||
fprintf(p->out,"</TR>\n");
|
||||
@@ -889,16 +869,16 @@ static int shell_callback(
|
||||
if( p->cnt++==0 && p->showHeader ){
|
||||
for(i=0; i<nArg; i++){
|
||||
output_c_string(p->out,azCol[i] ? azCol[i] : "");
|
||||
if(i<nArg-1) fprintf(p->out, "%s", p->colSeparator);
|
||||
if(i<nArg-1) fprintf(p->out, "%s", p->separator);
|
||||
}
|
||||
fprintf(p->out, "%s", p->rowSeparator);
|
||||
fprintf(p->out,"\n");
|
||||
}
|
||||
if( azArg==0 ) break;
|
||||
for(i=0; i<nArg; i++){
|
||||
output_c_string(p->out, azArg[i] ? azArg[i] : p->nullValue);
|
||||
if(i<nArg-1) fprintf(p->out, "%s", p->colSeparator);
|
||||
output_c_string(p->out, azArg[i] ? azArg[i] : p->nullvalue);
|
||||
if(i<nArg-1) fprintf(p->out, "%s", p->separator);
|
||||
}
|
||||
fprintf(p->out, "%s", p->rowSeparator);
|
||||
fprintf(p->out,"\n");
|
||||
break;
|
||||
}
|
||||
case MODE_Csv: {
|
||||
@@ -910,13 +890,13 @@ static int shell_callback(
|
||||
for(i=0; i<nArg; i++){
|
||||
output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1);
|
||||
}
|
||||
fprintf(p->out, "%s", p->rowSeparator);
|
||||
fprintf(p->out,"%s",p->newline);
|
||||
}
|
||||
if( nArg>0 ){
|
||||
for(i=0; i<nArg; i++){
|
||||
output_csv(p, azArg[i], i<nArg-1);
|
||||
}
|
||||
fprintf(p->out, "%s", p->rowSeparator);
|
||||
fprintf(p->out,"%s",p->newline);
|
||||
}
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
fflush(p->out);
|
||||
@@ -953,22 +933,6 @@ static int shell_callback(
|
||||
fprintf(p->out,");\n");
|
||||
break;
|
||||
}
|
||||
case MODE_Ascii: {
|
||||
if( p->cnt++==0 && p->showHeader ){
|
||||
for(i=0; i<nArg; i++){
|
||||
if( i>0 ) fprintf(p->out, "%s", p->colSeparator);
|
||||
fprintf(p->out,"%s",azCol[i] ? azCol[i] : "");
|
||||
}
|
||||
fprintf(p->out, "%s", p->rowSeparator);
|
||||
}
|
||||
if( azArg==0 ) break;
|
||||
for(i=0; i<nArg; i++){
|
||||
if( i>0 ) fprintf(p->out, "%s", p->colSeparator);
|
||||
fprintf(p->out,"%s",azArg[i] ? azArg[i] : p->nullValue);
|
||||
}
|
||||
fprintf(p->out, "%s", p->rowSeparator);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1734,13 +1698,12 @@ static char zHelp[] =
|
||||
#endif
|
||||
".log FILE|off Turn logging on or off. FILE can be stderr/stdout\n"
|
||||
".mode MODE ?TABLE? Set output mode where MODE is one of:\n"
|
||||
" ascii Columns/rows delimited by 0x1F and 0x1E\n"
|
||||
" csv Comma-separated values\n"
|
||||
" column Left-aligned columns. (See .width)\n"
|
||||
" html HTML <table> code\n"
|
||||
" insert SQL insert statements for TABLE\n"
|
||||
" line One value per line\n"
|
||||
" list Values delimited by .separator strings\n"
|
||||
" list Values delimited by .separator string\n"
|
||||
" tabs Tab-separated values\n"
|
||||
" tcl TCL list elements\n"
|
||||
".nullvalue STRING Use STRING in place of NULL values\n"
|
||||
@@ -1757,8 +1720,8 @@ static char zHelp[] =
|
||||
".schema ?TABLE? Show the CREATE statements\n"
|
||||
" If TABLE specified, only show tables matching\n"
|
||||
" LIKE pattern TABLE.\n"
|
||||
".separator COL ?ROW? Change the column separator and optionally the row\n"
|
||||
" separator for both the output mode and .import\n"
|
||||
".separator STRING ?NL? Change separator used by output mode and .import\n"
|
||||
" NL is the end-of-line mark for CSV\n"
|
||||
".shell CMD ARGS... Run CMD ARGS... in a system shell\n"
|
||||
".show Show the current values for various settings\n"
|
||||
".stats on|off Turn stats on or off\n"
|
||||
@@ -2039,10 +2002,10 @@ static void test_breakpoint(void){
|
||||
}
|
||||
|
||||
/*
|
||||
** An object used to read a CSV and other files for import.
|
||||
** An object used to read a CSV file
|
||||
*/
|
||||
typedef struct ImportCtx ImportCtx;
|
||||
struct ImportCtx {
|
||||
typedef struct CSVReader CSVReader;
|
||||
struct CSVReader {
|
||||
const char *zFile; /* Name of the input file */
|
||||
FILE *in; /* Read the CSV text from this input stream */
|
||||
char *z; /* Accumulated text for a field */
|
||||
@@ -2050,12 +2013,11 @@ struct ImportCtx {
|
||||
int nAlloc; /* Space allocated for z[] */
|
||||
int nLine; /* Current line number */
|
||||
int cTerm; /* Character that terminated the most recent field */
|
||||
int cColSep; /* The column separator character. (Usually ",") */
|
||||
int cRowSep; /* The row separator character. (Usually "\n") */
|
||||
int cSeparator; /* The separator character. (Usually ",") */
|
||||
};
|
||||
|
||||
/* Append a single byte to z[] */
|
||||
static void import_append_char(ImportCtx *p, int c){
|
||||
static void csv_append_char(CSVReader *p, int c){
|
||||
if( p->n+1>=p->nAlloc ){
|
||||
p->nAlloc += p->nAlloc + 100;
|
||||
p->z = sqlite3_realloc(p->z, p->nAlloc);
|
||||
@@ -2073,17 +2035,15 @@ static void import_append_char(ImportCtx *p, int c){
|
||||
** + Input comes from p->in.
|
||||
** + Store results in p->z of length p->n. Space to hold p->z comes
|
||||
** from sqlite3_malloc().
|
||||
** + Use p->cSep as the column separator. The default is ",".
|
||||
** + Use p->rSep as the row separator. The default is "\n".
|
||||
** + Use p->cSep as the separator. The default is ",".
|
||||
** + Keep track of the line number in p->nLine.
|
||||
** + Store the character that terminates the field in p->cTerm. Store
|
||||
** EOF on end-of-file.
|
||||
** + Report syntax errors on stderr
|
||||
*/
|
||||
static char *csv_read_one_field(ImportCtx *p){
|
||||
int c;
|
||||
int cSep = p->cColSep;
|
||||
int rSep = p->cRowSep;
|
||||
static char *csv_read_one_field(CSVReader *p){
|
||||
int c, pc, ppc;
|
||||
int cSep = p->cSeparator;
|
||||
p->n = 0;
|
||||
c = fgetc(p->in);
|
||||
if( c==EOF || seenInterrupt ){
|
||||
@@ -2091,13 +2051,12 @@ static char *csv_read_one_field(ImportCtx *p){
|
||||
return 0;
|
||||
}
|
||||
if( c=='"' ){
|
||||
int pc, ppc;
|
||||
int startLine = p->nLine;
|
||||
int cQuote = c;
|
||||
pc = ppc = 0;
|
||||
while( 1 ){
|
||||
c = fgetc(p->in);
|
||||
if( c==rSep ) p->nLine++;
|
||||
if( c=='\n' ) p->nLine++;
|
||||
if( c==cQuote ){
|
||||
if( pc==cQuote ){
|
||||
pc = 0;
|
||||
@@ -2105,8 +2064,8 @@ static char *csv_read_one_field(ImportCtx *p){
|
||||
}
|
||||
}
|
||||
if( (c==cSep && pc==cQuote)
|
||||
|| (c==rSep && pc==cQuote)
|
||||
|| (c==rSep && pc=='\r' && ppc==cQuote)
|
||||
|| (c=='\n' && pc==cQuote)
|
||||
|| (c=='\n' && pc=='\r' && ppc==cQuote)
|
||||
|| (c==EOF && pc==cQuote)
|
||||
){
|
||||
do{ p->n--; }while( p->z[p->n]!=cQuote );
|
||||
@@ -2120,19 +2079,19 @@ static char *csv_read_one_field(ImportCtx *p){
|
||||
if( c==EOF ){
|
||||
fprintf(stderr, "%s:%d: unterminated %c-quoted field\n",
|
||||
p->zFile, startLine, cQuote);
|
||||
p->cTerm = c;
|
||||
p->cTerm = EOF;
|
||||
break;
|
||||
}
|
||||
import_append_char(p, c);
|
||||
csv_append_char(p, c);
|
||||
ppc = pc;
|
||||
pc = c;
|
||||
}
|
||||
}else{
|
||||
while( c!=EOF && c!=cSep && c!=rSep ){
|
||||
import_append_char(p, c);
|
||||
while( c!=EOF && c!=cSep && c!='\n' ){
|
||||
csv_append_char(p, c);
|
||||
c = fgetc(p->in);
|
||||
}
|
||||
if( c==rSep ){
|
||||
if( c=='\n' ){
|
||||
p->nLine++;
|
||||
if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--;
|
||||
}
|
||||
@@ -2142,40 +2101,6 @@ static char *csv_read_one_field(ImportCtx *p){
|
||||
return p->z;
|
||||
}
|
||||
|
||||
/* Read a single field of ASCII delimited text.
|
||||
**
|
||||
** + Input comes from p->in.
|
||||
** + Store results in p->z of length p->n. Space to hold p->z comes
|
||||
** from sqlite3_malloc().
|
||||
** + Use p->cSep as the column separator. The default is "\x1F".
|
||||
** + Use p->rSep as the row separator. The default is "\x1E".
|
||||
** + Keep track of the row number in p->nLine.
|
||||
** + Store the character that terminates the field in p->cTerm. Store
|
||||
** EOF on end-of-file.
|
||||
** + Report syntax errors on stderr
|
||||
*/
|
||||
static char *ascii_read_one_field(ImportCtx *p){
|
||||
int c;
|
||||
int cSep = p->cColSep;
|
||||
int rSep = p->cRowSep;
|
||||
p->n = 0;
|
||||
c = fgetc(p->in);
|
||||
if( c==EOF || seenInterrupt ){
|
||||
p->cTerm = EOF;
|
||||
return 0;
|
||||
}
|
||||
while( c!=EOF && c!=cSep && c!=rSep ){
|
||||
import_append_char(p, c);
|
||||
c = fgetc(p->in);
|
||||
}
|
||||
if( c==rSep ){
|
||||
p->nLine++;
|
||||
}
|
||||
p->cTerm = c;
|
||||
if( p->z ) p->z[p->n] = 0;
|
||||
return p->z;
|
||||
}
|
||||
|
||||
/*
|
||||
** Try to transfer data for table zTable. If an error is seen while
|
||||
** moving forward, try to go backwards. The backwards movement won't
|
||||
@@ -2730,10 +2655,9 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
int nByte; /* Number of bytes in an SQL string */
|
||||
int i, j; /* Loop counters */
|
||||
int needCommit; /* True to COMMIT or ROLLBACK at end */
|
||||
int nSep; /* Number of bytes in p->colSeparator[] */
|
||||
int nSep; /* Number of bytes in p->separator[] */
|
||||
char *zSql; /* An SQL statement */
|
||||
ImportCtx sCtx; /* Reader context */
|
||||
char *(*xRead)(ImportCtx*); /* Procedure to read one value */
|
||||
CSVReader sCsv; /* Reader context */
|
||||
int (*xCloser)(FILE*); /* Procedure to close th3 connection */
|
||||
|
||||
if( nArg!=3 ){
|
||||
@@ -2743,79 +2667,55 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
zFile = azArg[1];
|
||||
zTable = azArg[2];
|
||||
seenInterrupt = 0;
|
||||
memset(&sCtx, 0, sizeof(sCtx));
|
||||
memset(&sCsv, 0, sizeof(sCsv));
|
||||
open_db(p, 0);
|
||||
nSep = strlen30(p->colSeparator);
|
||||
nSep = strlen30(p->separator);
|
||||
if( nSep==0 ){
|
||||
fprintf(stderr, "Error: non-null column separator required for import\n");
|
||||
fprintf(stderr, "Error: non-null separator required for import\n");
|
||||
return 1;
|
||||
}
|
||||
if( nSep>1 ){
|
||||
fprintf(stderr, "Error: multi-character column separators not allowed"
|
||||
fprintf(stderr, "Error: multi-character separators not allowed"
|
||||
" for import\n");
|
||||
return 1;
|
||||
}
|
||||
nSep = strlen30(p->rowSeparator);
|
||||
if( nSep==0 ){
|
||||
fprintf(stderr, "Error: non-null row separator required for import\n");
|
||||
return 1;
|
||||
}
|
||||
if( nSep==2 && p->mode==MODE_Csv && strcmp(p->rowSeparator, SEP_CrLf)==0 ){
|
||||
/* When importing CSV (only), if the row separator is set to the
|
||||
** default output row separator, change it to the default input
|
||||
** row separator. This avoids having to maintain different input
|
||||
** and output row separators. */
|
||||
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
|
||||
nSep = strlen30(p->rowSeparator);
|
||||
}
|
||||
if( nSep>1 ){
|
||||
fprintf(stderr, "Error: multi-character row separators not allowed"
|
||||
" for import\n");
|
||||
return 1;
|
||||
}
|
||||
sCtx.zFile = zFile;
|
||||
sCtx.nLine = 1;
|
||||
if( sCtx.zFile[0]=='|' ){
|
||||
sCtx.in = popen(sCtx.zFile+1, "r");
|
||||
sCtx.zFile = "<pipe>";
|
||||
sCsv.zFile = zFile;
|
||||
sCsv.nLine = 1;
|
||||
if( sCsv.zFile[0]=='|' ){
|
||||
sCsv.in = popen(sCsv.zFile+1, "r");
|
||||
sCsv.zFile = "<pipe>";
|
||||
xCloser = pclose;
|
||||
}else{
|
||||
sCtx.in = fopen(sCtx.zFile, "rb");
|
||||
sCsv.in = fopen(sCsv.zFile, "rb");
|
||||
xCloser = fclose;
|
||||
}
|
||||
if( p->mode==MODE_Ascii ){
|
||||
xRead = ascii_read_one_field;
|
||||
}else{
|
||||
xRead = csv_read_one_field;
|
||||
}
|
||||
if( sCtx.in==0 ){
|
||||
if( sCsv.in==0 ){
|
||||
fprintf(stderr, "Error: cannot open \"%s\"\n", zFile);
|
||||
return 1;
|
||||
}
|
||||
sCtx.cColSep = p->colSeparator[0];
|
||||
sCtx.cRowSep = p->rowSeparator[0];
|
||||
sCsv.cSeparator = p->separator[0];
|
||||
zSql = sqlite3_mprintf("SELECT * FROM %s", zTable);
|
||||
if( zSql==0 ){
|
||||
fprintf(stderr, "Error: out of memory\n");
|
||||
xCloser(sCtx.in);
|
||||
xCloser(sCsv.in);
|
||||
return 1;
|
||||
}
|
||||
nByte = strlen30(zSql);
|
||||
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
|
||||
import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */
|
||||
csv_append_char(&sCsv, 0); /* To ensure sCsv.z is allocated */
|
||||
if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(db))==0 ){
|
||||
char *zCreate = sqlite3_mprintf("CREATE TABLE %s", zTable);
|
||||
char cSep = '(';
|
||||
while( xRead(&sCtx) ){
|
||||
zCreate = sqlite3_mprintf("%z%c\n \"%s\" TEXT", zCreate, cSep, sCtx.z);
|
||||
while( csv_read_one_field(&sCsv) ){
|
||||
zCreate = sqlite3_mprintf("%z%c\n \"%s\" TEXT", zCreate, cSep, sCsv.z);
|
||||
cSep = ',';
|
||||
if( sCtx.cTerm!=sCtx.cColSep ) break;
|
||||
if( sCsv.cTerm!=sCsv.cSeparator ) break;
|
||||
}
|
||||
if( cSep=='(' ){
|
||||
sqlite3_free(zCreate);
|
||||
sqlite3_free(sCtx.z);
|
||||
xCloser(sCtx.in);
|
||||
fprintf(stderr,"%s: empty file\n", sCtx.zFile);
|
||||
sqlite3_free(sCsv.z);
|
||||
xCloser(sCsv.in);
|
||||
fprintf(stderr,"%s: empty file\n", sCsv.zFile);
|
||||
return 1;
|
||||
}
|
||||
zCreate = sqlite3_mprintf("%z\n)", zCreate);
|
||||
@@ -2824,8 +2724,8 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
if( rc ){
|
||||
fprintf(stderr, "CREATE TABLE %s(...) failed: %s\n", zTable,
|
||||
sqlite3_errmsg(db));
|
||||
sqlite3_free(sCtx.z);
|
||||
xCloser(sCtx.in);
|
||||
sqlite3_free(sCsv.z);
|
||||
xCloser(sCsv.in);
|
||||
return 1;
|
||||
}
|
||||
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
|
||||
@@ -2834,7 +2734,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
if( rc ){
|
||||
if (pStmt) sqlite3_finalize(pStmt);
|
||||
fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db));
|
||||
xCloser(sCtx.in);
|
||||
xCloser(sCsv.in);
|
||||
return 1;
|
||||
}
|
||||
nCol = sqlite3_column_count(pStmt);
|
||||
@@ -2844,7 +2744,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
zSql = sqlite3_malloc( nByte*2 + 20 + nCol*2 );
|
||||
if( zSql==0 ){
|
||||
fprintf(stderr, "Error: out of memory\n");
|
||||
xCloser(sCtx.in);
|
||||
xCloser(sCsv.in);
|
||||
return 1;
|
||||
}
|
||||
sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\" VALUES(?", zTable);
|
||||
@@ -2860,56 +2760,46 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
if( rc ){
|
||||
fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db));
|
||||
if (pStmt) sqlite3_finalize(pStmt);
|
||||
xCloser(sCtx.in);
|
||||
xCloser(sCsv.in);
|
||||
return 1;
|
||||
}
|
||||
needCommit = sqlite3_get_autocommit(db);
|
||||
if( needCommit ) sqlite3_exec(db, "BEGIN", 0, 0, 0);
|
||||
do{
|
||||
int startLine = sCtx.nLine;
|
||||
int startLine = sCsv.nLine;
|
||||
for(i=0; i<nCol; i++){
|
||||
char *z = xRead(&sCtx);
|
||||
/*
|
||||
** Did we reach end-of-file before finding any columns?
|
||||
** If so, stop instead of NULL filling the remaining columns.
|
||||
*/
|
||||
char *z = csv_read_one_field(&sCsv);
|
||||
if( z==0 && i==0 ) break;
|
||||
/*
|
||||
** Did we reach end-of-file OR end-of-line before finding any
|
||||
** columns in ASCII mode? If so, stop instead of NULL filling
|
||||
** the remaining columns.
|
||||
*/
|
||||
if( p->mode==MODE_Ascii && (z==0 || z[0]==0) && i==0 ) break;
|
||||
sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT);
|
||||
if( i<nCol-1 && sCtx.cTerm!=sCtx.cColSep ){
|
||||
if( i<nCol-1 && sCsv.cTerm!=sCsv.cSeparator ){
|
||||
fprintf(stderr, "%s:%d: expected %d columns but found %d - "
|
||||
"filling the rest with NULL\n",
|
||||
sCtx.zFile, startLine, nCol, i+1);
|
||||
sCsv.zFile, startLine, nCol, i+1);
|
||||
i++;
|
||||
while( i<=nCol ){ sqlite3_bind_null(pStmt, i); i++; }
|
||||
}
|
||||
}
|
||||
if( sCtx.cTerm==sCtx.cColSep ){
|
||||
if( sCsv.cTerm==sCsv.cSeparator ){
|
||||
do{
|
||||
xRead(&sCtx);
|
||||
csv_read_one_field(&sCsv);
|
||||
i++;
|
||||
}while( sCtx.cTerm==sCtx.cColSep );
|
||||
}while( sCsv.cTerm==sCsv.cSeparator );
|
||||
fprintf(stderr, "%s:%d: expected %d columns but found %d - "
|
||||
"extras ignored\n",
|
||||
sCtx.zFile, startLine, nCol, i);
|
||||
sCsv.zFile, startLine, nCol, i);
|
||||
}
|
||||
if( i>=nCol ){
|
||||
sqlite3_step(pStmt);
|
||||
rc = sqlite3_reset(pStmt);
|
||||
if( rc!=SQLITE_OK ){
|
||||
fprintf(stderr, "%s:%d: INSERT failed: %s\n", sCtx.zFile, startLine,
|
||||
fprintf(stderr, "%s:%d: INSERT failed: %s\n", sCsv.zFile, startLine,
|
||||
sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
}while( sCtx.cTerm!=EOF );
|
||||
}while( sCsv.cTerm!=EOF );
|
||||
|
||||
xCloser(sCtx.in);
|
||||
sqlite3_free(sCtx.z);
|
||||
xCloser(sCsv.in);
|
||||
sqlite3_free(sCsv.z);
|
||||
sqlite3_finalize(pStmt);
|
||||
if( needCommit ) sqlite3_exec(db, "COMMIT", 0, 0, 0);
|
||||
}else
|
||||
@@ -3027,32 +2917,28 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
p->mode = MODE_Html;
|
||||
}else if( c2=='t' && strncmp(azArg[1],"tcl",n2)==0 ){
|
||||
p->mode = MODE_Tcl;
|
||||
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Space);
|
||||
sqlite3_snprintf(sizeof(p->separator), p->separator, " ");
|
||||
}else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){
|
||||
p->mode = MODE_Csv;
|
||||
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma);
|
||||
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf);
|
||||
sqlite3_snprintf(sizeof(p->separator), p->separator, ",");
|
||||
sqlite3_snprintf(sizeof(p->newline), p->newline, "\r\n");
|
||||
}else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){
|
||||
p->mode = MODE_List;
|
||||
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Tab);
|
||||
sqlite3_snprintf(sizeof(p->separator), p->separator, "\t");
|
||||
}else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){
|
||||
p->mode = MODE_Insert;
|
||||
set_table_name(p, nArg>=3 ? azArg[2] : "table");
|
||||
}else if( c2=='a' && strncmp(azArg[1],"ascii",n2)==0 ){
|
||||
p->mode = MODE_Ascii;
|
||||
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Unit);
|
||||
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Record);
|
||||
}else {
|
||||
fprintf(stderr,"Error: mode should be one of: "
|
||||
"ascii column csv html insert line list tabs tcl\n");
|
||||
"column csv html insert line list tabs tcl\n");
|
||||
rc = 1;
|
||||
}
|
||||
}else
|
||||
|
||||
if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){
|
||||
if( nArg==2 ){
|
||||
sqlite3_snprintf(sizeof(p->nullValue), p->nullValue,
|
||||
"%.*s", (int)ArraySize(p->nullValue)-1, azArg[1]);
|
||||
sqlite3_snprintf(sizeof(p->nullvalue), p->nullvalue,
|
||||
"%.*s", (int)ArraySize(p->nullvalue)-1, azArg[1]);
|
||||
}else{
|
||||
fprintf(stderr, "Usage: .nullvalue STRING\n");
|
||||
rc = 1;
|
||||
@@ -3337,16 +3223,14 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
|
||||
if( c=='s' && strncmp(azArg[0], "separator", n)==0 ){
|
||||
if( nArg<2 || nArg>3 ){
|
||||
fprintf(stderr, "Usage: .separator COL ?ROW?\n");
|
||||
fprintf(stderr, "Usage: .separator SEPARATOR ?NEWLINE?\n");
|
||||
rc = 1;
|
||||
}
|
||||
if( nArg>=2 ){
|
||||
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator,
|
||||
"%.*s", (int)ArraySize(p->colSeparator)-1, azArg[1]);
|
||||
sqlite3_snprintf(sizeof(p->separator), p->separator, azArg[1]);
|
||||
}
|
||||
if( nArg>=3 ){
|
||||
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator,
|
||||
"%.*s", (int)ArraySize(p->rowSeparator)-1, azArg[2]);
|
||||
sqlite3_snprintf(sizeof(p->newline), p->newline, azArg[2]);
|
||||
}
|
||||
}else
|
||||
|
||||
@@ -3377,24 +3261,23 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
rc = 1;
|
||||
goto meta_command_exit;
|
||||
}
|
||||
fprintf(p->out,"%12.12s: %s\n","echo", p->echoOn ? "on" : "off");
|
||||
fprintf(p->out,"%12.12s: %s\n","eqp", p->autoEQP ? "on" : "off");
|
||||
fprintf(p->out,"%9.9s: %s\n","echo", p->echoOn ? "on" : "off");
|
||||
fprintf(p->out,"%9.9s: %s\n","eqp", p->autoEQP ? "on" : "off");
|
||||
fprintf(p->out,"%9.9s: %s\n","explain", p->normalMode.valid ? "on" :"off");
|
||||
fprintf(p->out,"%12.12s: %s\n","headers", p->showHeader ? "on" : "off");
|
||||
fprintf(p->out,"%12.12s: %s\n","mode", modeDescr[p->mode]);
|
||||
fprintf(p->out,"%12.12s: ", "nullvalue");
|
||||
output_c_string(p->out, p->nullValue);
|
||||
fprintf(p->out,"%9.9s: %s\n","headers", p->showHeader ? "on" : "off");
|
||||
fprintf(p->out,"%9.9s: %s\n","mode", modeDescr[p->mode]);
|
||||
fprintf(p->out,"%9.9s: ", "nullvalue");
|
||||
output_c_string(p->out, p->nullvalue);
|
||||
fprintf(p->out, "\n");
|
||||
fprintf(p->out,"%12.12s: %s\n","output",
|
||||
fprintf(p->out,"%9.9s: %s\n","output",
|
||||
strlen30(p->outfile) ? p->outfile : "stdout");
|
||||
fprintf(p->out,"%12.12s: ", "colseparator");
|
||||
output_c_string(p->out, p->colSeparator);
|
||||
fprintf(p->out,"%9.9s: ", "separator");
|
||||
output_c_string(p->out, p->separator);
|
||||
fprintf(p->out," ");
|
||||
output_c_string(p->out, p->newline);
|
||||
fprintf(p->out, "\n");
|
||||
fprintf(p->out,"%12.12s: ", "rowseparator");
|
||||
output_c_string(p->out, p->rowSeparator);
|
||||
fprintf(p->out, "\n");
|
||||
fprintf(p->out,"%12.12s: %s\n","stats", p->statsOn ? "on" : "off");
|
||||
fprintf(p->out,"%12.12s: ","width");
|
||||
fprintf(p->out,"%9.9s: %s\n","stats", p->statsOn ? "on" : "off");
|
||||
fprintf(p->out,"%9.9s: ","width");
|
||||
for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) {
|
||||
fprintf(p->out,"%d ",p->colWidth[i]);
|
||||
}
|
||||
@@ -4055,7 +3938,6 @@ static int process_sqliterc(
|
||||
** Show available command line options
|
||||
*/
|
||||
static const char zOptions[] =
|
||||
" -ascii set output mode to 'ascii'\n"
|
||||
" -bail stop after hitting an error\n"
|
||||
" -batch force batch I/O\n"
|
||||
" -column set output mode to 'column'\n"
|
||||
@@ -4077,11 +3959,11 @@ static const char zOptions[] =
|
||||
#ifdef SQLITE_ENABLE_MULTIPLEX
|
||||
" -multiplex enable the multiplexor VFS\n"
|
||||
#endif
|
||||
" -newline SEP set output row separator. Default: '\\n'\n"
|
||||
" -newline SEP set newline character(s) for CSV\n"
|
||||
" -nullvalue TEXT set text string for NULL values. Default ''\n"
|
||||
" -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n"
|
||||
" -scratch SIZE N use N slots of SZ bytes each for scratch memory\n"
|
||||
" -separator SEP set output column separator. Default: '|'\n"
|
||||
" -separator SEP set output field separator. Default: '|'\n"
|
||||
" -stats print memory stats before each finalize\n"
|
||||
" -version show SQLite version\n"
|
||||
" -vfs NAME use NAME as the default VFS\n"
|
||||
@@ -4108,8 +3990,8 @@ static void usage(int showDetail){
|
||||
static void main_init(ShellState *data) {
|
||||
memset(data, 0, sizeof(*data));
|
||||
data->mode = MODE_List;
|
||||
memcpy(data->colSeparator,SEP_Column, 2);
|
||||
memcpy(data->rowSeparator,SEP_Row, 2);
|
||||
memcpy(data->separator,"|", 2);
|
||||
memcpy(data->newline,"\r\n", 3);
|
||||
data->showHeader = 0;
|
||||
data->shellFlgs = SHFLG_Lookaside;
|
||||
sqlite3_config(SQLITE_CONFIG_URI, 1);
|
||||
@@ -4348,21 +4230,15 @@ int main(int argc, char **argv){
|
||||
data.mode = MODE_Column;
|
||||
}else if( strcmp(z,"-csv")==0 ){
|
||||
data.mode = MODE_Csv;
|
||||
memcpy(data.colSeparator,",",2);
|
||||
}else if( strcmp(z,"-ascii")==0 ){
|
||||
data.mode = MODE_Ascii;
|
||||
sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator,
|
||||
SEP_Unit);
|
||||
sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator,
|
||||
SEP_Record);
|
||||
memcpy(data.separator,",",2);
|
||||
}else if( strcmp(z,"-separator")==0 ){
|
||||
sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator,
|
||||
sqlite3_snprintf(sizeof(data.separator), data.separator,
|
||||
"%s",cmdline_option_value(argc,argv,++i));
|
||||
}else if( strcmp(z,"-newline")==0 ){
|
||||
sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator,
|
||||
sqlite3_snprintf(sizeof(data.newline), data.newline,
|
||||
"%s",cmdline_option_value(argc,argv,++i));
|
||||
}else if( strcmp(z,"-nullvalue")==0 ){
|
||||
sqlite3_snprintf(sizeof(data.nullValue), data.nullValue,
|
||||
sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue,
|
||||
"%s",cmdline_option_value(argc,argv,++i));
|
||||
}else if( strcmp(z,"-header")==0 ){
|
||||
data.showHeader = 1;
|
||||
|
||||
+25
-39
@@ -196,7 +196,7 @@ const char *sqlite3_compileoption_get(int N);
|
||||
** SQLITE_THREADSAFE=1 or =2 then mutexes are enabled by default but
|
||||
** can be fully or partially disabled using a call to [sqlite3_config()]
|
||||
** with the verbs [SQLITE_CONFIG_SINGLETHREAD], [SQLITE_CONFIG_MULTITHREAD],
|
||||
** or [SQLITE_CONFIG_SERIALIZED]. ^(The return value of the
|
||||
** or [SQLITE_CONFIG_MUTEX]. ^(The return value of the
|
||||
** sqlite3_threadsafe() function shows only the compile-time setting of
|
||||
** thread safety, not any run-time changes to that setting made by
|
||||
** sqlite3_config(). In other words, the return value from sqlite3_threadsafe()
|
||||
@@ -1565,7 +1565,7 @@ struct sqlite3_mem_methods {
|
||||
** 8-byte aligned
|
||||
** memory, the size of each page buffer (sz), and the number of pages (N).
|
||||
** The sz argument should be the size of the largest database page
|
||||
** (a power of two between 512 and 65536) plus some extra bytes for each
|
||||
** (a power of two between 512 and 32768) plus some extra bytes for each
|
||||
** page header. ^The number of extra bytes needed by the page header
|
||||
** can be determined using the [SQLITE_CONFIG_PCACHE_HDRSZ] option
|
||||
** to [sqlite3_config()].
|
||||
@@ -1745,17 +1745,6 @@ struct sqlite3_mem_methods {
|
||||
** bytes per page required for each page in [SQLITE_CONFIG_PAGECACHE].
|
||||
** The amount of extra space required can change depending on the compiler,
|
||||
** target platform, and SQLite version.
|
||||
**
|
||||
** [[SQLITE_CONFIG_PMASZ]]
|
||||
** <dt>SQLITE_CONFIG_PMASZ
|
||||
** <dd>^The SQLITE_CONFIG_PMASZ option takes a single parameter which
|
||||
** is an unsigned integer and sets the "Minimum PMA Size" for the multithreaded
|
||||
** sorter to that integer. The default minimum PMA Size is set by the
|
||||
** [SQLITE_SORTER_PMASZ] compile-time option. New threads are launched
|
||||
** to help with sort operations when multithreaded sorting
|
||||
** is enabled (using the [PRAGMA threads] command) and the amount of content
|
||||
** to be sorted exceeds the page size times the minimum of the
|
||||
** [PRAGMA cache_size] setting and this value.
|
||||
** </dl>
|
||||
*/
|
||||
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
|
||||
@@ -1782,7 +1771,6 @@ struct sqlite3_mem_methods {
|
||||
#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */
|
||||
#define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */
|
||||
#define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */
|
||||
#define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */
|
||||
|
||||
/*
|
||||
** CAPI3REF: Database Connection Configuration Options
|
||||
@@ -5164,27 +5152,20 @@ SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N);
|
||||
/*
|
||||
** CAPI3REF: Extract Metadata About A Column Of A Table
|
||||
**
|
||||
** ^(The sqlite3_table_column_metadata(X,D,T,C,....) routine returns
|
||||
** information about column C of table T in database D
|
||||
** on [database connection] X.)^ ^The sqlite3_table_column_metadata()
|
||||
** interface returns SQLITE_OK and fills in the non-NULL pointers in
|
||||
** the final five arguments with appropriate values if the specified
|
||||
** column exists. ^The sqlite3_table_column_metadata() interface returns
|
||||
** SQLITE_ERROR and if the specified column does not exist.
|
||||
** ^If the column-name parameter to sqlite3_table_column_metadata() is a
|
||||
** NULL pointer, then this routine simply checks for the existance of the
|
||||
** table and returns SQLITE_OK if the table exists and SQLITE_ERROR if it
|
||||
** does not.
|
||||
** ^This routine returns metadata about a specific column of a specific
|
||||
** database table accessible using the [database connection] handle
|
||||
** passed as the first function argument.
|
||||
**
|
||||
** ^The column is identified by the second, third and fourth parameters to
|
||||
** this function. ^(The second parameter is either the name of the database
|
||||
** this function. ^The second parameter is either the name of the database
|
||||
** (i.e. "main", "temp", or an attached database) containing the specified
|
||||
** table or NULL.)^ ^If it is NULL, then all attached databases are searched
|
||||
** table or NULL. ^If it is NULL, then all attached databases are searched
|
||||
** for the table using the same algorithm used by the database engine to
|
||||
** resolve unqualified table references.
|
||||
**
|
||||
** ^The third and fourth parameters to this function are the table and column
|
||||
** name of the desired column, respectively.
|
||||
** name of the desired column, respectively. Neither of these parameters
|
||||
** may be NULL.
|
||||
**
|
||||
** ^Metadata is returned by writing to the memory locations passed as the 5th
|
||||
** and subsequent parameters to this function. ^Any of these arguments may be
|
||||
@@ -5203,17 +5184,16 @@ SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N);
|
||||
** </blockquote>)^
|
||||
**
|
||||
** ^The memory pointed to by the character pointers returned for the
|
||||
** declaration type and collation sequence is valid until the next
|
||||
** declaration type and collation sequence is valid only until the next
|
||||
** call to any SQLite API function.
|
||||
**
|
||||
** ^If the specified table is actually a view, an [error code] is returned.
|
||||
**
|
||||
** ^If the specified column is "rowid", "oid" or "_rowid_" and the table
|
||||
** is not a [WITHOUT ROWID] table and an
|
||||
** ^If the specified column is "rowid", "oid" or "_rowid_" and an
|
||||
** [INTEGER PRIMARY KEY] column has been explicitly declared, then the output
|
||||
** parameters are set for the explicitly declared column. ^(If there is no
|
||||
** [INTEGER PRIMARY KEY] column, then the outputs
|
||||
** for the [rowid] are set as follows:
|
||||
** explicitly declared [INTEGER PRIMARY KEY] column, then the output
|
||||
** parameters are set as follows:
|
||||
**
|
||||
** <pre>
|
||||
** data type: "INTEGER"
|
||||
@@ -5223,9 +5203,13 @@ SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N);
|
||||
** auto increment: 0
|
||||
** </pre>)^
|
||||
**
|
||||
** ^This function causes all database schemas to be read from disk and
|
||||
** parsed, if that has not already been done, and returns an error if
|
||||
** any errors are encountered while loading the schema.
|
||||
** ^(This function may load one or more schemas from database files. If an
|
||||
** error occurs during this process, or if the requested table or column
|
||||
** cannot be found, an [error code] is returned and an error message left
|
||||
** in the [database connection] (to be retrieved using sqlite3_errmsg()).)^
|
||||
**
|
||||
** ^This API is only available if the library was compiled with the
|
||||
** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol defined.
|
||||
*/
|
||||
int sqlite3_table_column_metadata(
|
||||
sqlite3 *db, /* Connection handle */
|
||||
@@ -7192,10 +7176,12 @@ void sqlite3_log(int iErrCode, const char *zFormat, ...);
|
||||
** CAPI3REF: Write-Ahead Log Commit Hook
|
||||
**
|
||||
** ^The [sqlite3_wal_hook()] function is used to register a callback that
|
||||
** is invoked each time data is committed to a database in wal mode.
|
||||
** will be invoked each time a database connection commits data to a
|
||||
** [write-ahead log] (i.e. whenever a transaction is committed in
|
||||
** [journal_mode | journal_mode=WAL mode]).
|
||||
**
|
||||
** ^(The callback is invoked by SQLite after the commit has taken place and
|
||||
** the associated write-lock on the database released)^, so the implementation
|
||||
** ^The callback is invoked by SQLite after the commit has taken place and
|
||||
** the associated write-lock on the database released, so the implementation
|
||||
** may read, write or [checkpoint] the database as required.
|
||||
**
|
||||
** ^The first parameter passed to the callback function when it is invoked
|
||||
|
||||
+5
-4
@@ -1059,7 +1059,6 @@ struct sqlite3 {
|
||||
int errCode; /* Most recent error code (SQLITE_*) */
|
||||
int errMask; /* & result codes with this before returning */
|
||||
u16 dbOptFlags; /* Flags to enable/disable optimizations */
|
||||
u8 enc; /* Text encoding */
|
||||
u8 autoCommit; /* The auto-commit flag. */
|
||||
u8 temp_store; /* 1: file 2: memory 0: default */
|
||||
u8 mallocFailed; /* True if we have seen a malloc failure */
|
||||
@@ -1161,8 +1160,7 @@ struct sqlite3 {
|
||||
/*
|
||||
** A macro to discover the encoding of a database.
|
||||
*/
|
||||
#define SCHEMA_ENC(db) ((db)->aDb[0].pSchema->enc)
|
||||
#define ENC(db) ((db)->enc)
|
||||
#define ENC(db) ((db)->aDb[0].pSchema->enc)
|
||||
|
||||
/*
|
||||
** Possible values for the sqlite3.flags.
|
||||
@@ -1786,6 +1784,7 @@ struct Index {
|
||||
u8 *aSortOrder; /* for each column: True==DESC, False==ASC */
|
||||
char **azColl; /* Array of collation sequence names for index */
|
||||
Expr *pPartIdxWhere; /* WHERE clause for partial indices */
|
||||
KeyInfo *pKeyInfo; /* A KeyInfo object suitable for this index */
|
||||
int tnum; /* DB Page containing root of this index */
|
||||
LogEst szIdxRow; /* Estimated average row size in bytes */
|
||||
u16 nKeyCol; /* Number of columns forming the key */
|
||||
@@ -2839,7 +2838,6 @@ struct Sqlite3Config {
|
||||
int nPage; /* Number of pages in pPage[] */
|
||||
int mxParserStack; /* maximum depth of the parser stack */
|
||||
int sharedCacheEnabled; /* true if shared-cache mode enabled */
|
||||
u32 szPma; /* Maximum Sorter PMA size */
|
||||
/* The above might be initialized to non-zero. The following need to always
|
||||
** initially be zero, however. */
|
||||
int isInit; /* True after initialization has finished */
|
||||
@@ -3455,6 +3453,9 @@ const char *sqlite3ErrName(int);
|
||||
|
||||
const char *sqlite3ErrStr(int);
|
||||
int sqlite3ReadSchema(Parse *pParse);
|
||||
#if defined(SQLITE_DEBUG)
|
||||
int sqlite3ValidCollSeq(const CollSeq*);
|
||||
#endif
|
||||
CollSeq *sqlite3FindCollSeq(sqlite3*,u8 enc, const char*,int);
|
||||
CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char*zName);
|
||||
CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr);
|
||||
|
||||
+1
-1
@@ -127,7 +127,7 @@ int sqlite3_get_table(
|
||||
TabResult res;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) || pazResult==0 ) return SQLITE_MISUSE_BKPT;
|
||||
if( pazResult==0 ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
*pazResult = 0;
|
||||
if( pnColumn ) *pnColumn = 0;
|
||||
|
||||
@@ -635,7 +635,6 @@ static int DbWalHandler(
|
||||
Tcl_Interp *interp = pDb->interp;
|
||||
assert(pDb->pWalHook);
|
||||
|
||||
assert( db==pDb->db );
|
||||
p = Tcl_DuplicateObj(pDb->pWalHook);
|
||||
Tcl_IncrRefCount(p);
|
||||
Tcl_ListObjAppendElement(interp, p, Tcl_NewStringObj(zDb, -1));
|
||||
|
||||
+11
-48
@@ -1569,6 +1569,7 @@ static int test_libversion_number(
|
||||
** Usage: sqlite3_table_column_metadata DB dbname tblname colname
|
||||
**
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_COLUMN_METADATA
|
||||
static int test_table_column_metadata(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
@@ -1588,14 +1589,14 @@ static int test_table_column_metadata(
|
||||
int primarykey;
|
||||
int autoincrement;
|
||||
|
||||
if( objc!=5 && objc!=4 ){
|
||||
if( objc!=5 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB dbname tblname colname");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
zDb = Tcl_GetString(objv[2]);
|
||||
zTbl = Tcl_GetString(objv[3]);
|
||||
zCol = objc==5 ? Tcl_GetString(objv[4]) : 0;
|
||||
zCol = Tcl_GetString(objv[4]);
|
||||
|
||||
if( strlen(zDb)==0 ) zDb = 0;
|
||||
|
||||
@@ -1617,6 +1618,7 @@ static int test_table_column_metadata(
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_INCRBLOB
|
||||
|
||||
@@ -3670,7 +3672,7 @@ static int test_prepare_v2(
|
||||
assert(rc==SQLITE_OK || pStmt==0);
|
||||
Tcl_ResetResult(interp);
|
||||
if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
|
||||
if( rc==SQLITE_OK && zTail && objc>=5 ){
|
||||
if( zTail && objc>=5 ){
|
||||
if( bytes>=0 ){
|
||||
bytes = bytes - (int)(zTail-zSql);
|
||||
}
|
||||
@@ -5702,18 +5704,15 @@ static int test_wal_checkpoint_v2(
|
||||
if( objc==4 ){
|
||||
zDb = Tcl_GetString(objv[3]);
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) || (
|
||||
TCL_OK!=Tcl_GetIntFromObj(0, objv[2], &eMode)
|
||||
&& TCL_OK!=Tcl_GetIndexFromObj(interp, objv[2], aMode, "mode", 0, &eMode)
|
||||
)){
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db)
|
||||
|| Tcl_GetIndexFromObj(interp, objv[2], aMode, "mode", 0, &eMode)
|
||||
){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
rc = sqlite3_wal_checkpoint_v2(db, zDb, eMode, &nLog, &nCkpt);
|
||||
if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){
|
||||
const char *zErrCode = sqlite3ErrName(rc);
|
||||
Tcl_ResetResult(interp);
|
||||
Tcl_AppendResult(interp, zErrCode, " - ", (char *)sqlite3_errmsg(db), 0);
|
||||
Tcl_SetResult(interp, (char *)sqlite3_errmsg(db), TCL_VOLATILE);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
@@ -5726,43 +5725,6 @@ static int test_wal_checkpoint_v2(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_wal_autocheckpoint db VALUE
|
||||
*/
|
||||
static int test_wal_autocheckpoint(
|
||||
ClientData clientData, /* Unused */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
int iVal;
|
||||
|
||||
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB VALUE");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db)
|
||||
|| Tcl_GetIntFromObj(0, objv[2], &iVal)
|
||||
){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
rc = sqlite3_wal_autocheckpoint(db, iVal);
|
||||
Tcl_ResetResult(interp);
|
||||
if( rc!=SQLITE_OK ){
|
||||
const char *zErrCode = sqlite3ErrName(rc);
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(zErrCode, -1));
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** tclcmd: test_sqlite3_log ?SCRIPT?
|
||||
*/
|
||||
@@ -6814,7 +6776,9 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_shared_cache_report", sqlite3BtreeSharedCacheReport, 0},
|
||||
#endif
|
||||
{ "sqlite3_libversion_number", test_libversion_number, 0 },
|
||||
#ifdef SQLITE_ENABLE_COLUMN_METADATA
|
||||
{ "sqlite3_table_column_metadata", test_table_column_metadata, 0 },
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_INCRBLOB
|
||||
{ "sqlite3_blob_reopen", test_blob_reopen, 0 },
|
||||
#endif
|
||||
@@ -6824,7 +6788,6 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
#endif
|
||||
{ "sqlite3_wal_checkpoint", test_wal_checkpoint, 0 },
|
||||
{ "sqlite3_wal_checkpoint_v2",test_wal_checkpoint_v2, 0 },
|
||||
{ "sqlite3_wal_autocheckpoint",test_wal_autocheckpoint, 0 },
|
||||
{ "test_sqlite3_log", test_sqlite3_log, 0 },
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
{ "print_explain_query_plan", test_print_eqp, 0 },
|
||||
|
||||
+2
-2
@@ -648,12 +648,12 @@ static int echoRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
** indeed the hash of the supplied idxStr.
|
||||
*/
|
||||
static int hashString(const char *zString){
|
||||
u32 val = 0;
|
||||
int val = 0;
|
||||
int ii;
|
||||
for(ii=0; zString[ii]; ii++){
|
||||
val = (val << 3) + (int)zString[ii];
|
||||
}
|
||||
return (int)(val&0x7fffffff);
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -1260,34 +1260,6 @@ static int test_config_cis(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_config_pmasz INTEGER
|
||||
**
|
||||
** Set the minimum PMA size.
|
||||
*/
|
||||
static int test_config_pmasz(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc;
|
||||
int iPmaSz;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "BOOL");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetIntFromObj(interp, objv[1], &iPmaSz) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
rc = sqlite3_config(SQLITE_CONFIG_PMASZ, iPmaSz);
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_dump_memsys3 FILENAME
|
||||
@@ -1542,7 +1514,6 @@ int Sqlitetest_malloc_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_config_error", test_config_error ,0 },
|
||||
{ "sqlite3_config_uri", test_config_uri ,0 },
|
||||
{ "sqlite3_config_cis", test_config_cis ,0 },
|
||||
{ "sqlite3_config_pmasz", test_config_pmasz ,0 },
|
||||
{ "sqlite3_db_config_lookaside",test_db_config_lookaside ,0 },
|
||||
{ "sqlite3_dump_memsys3", test_dump_memsys3 ,3 },
|
||||
{ "sqlite3_dump_memsys5", test_dump_memsys3 ,5 },
|
||||
|
||||
@@ -391,9 +391,6 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
|
||||
int mxSqlLen; /* Max length of an SQL string */
|
||||
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( zSql==0 || pzErrMsg==0 ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];
|
||||
if( db->nVdbeActive==0 ){
|
||||
db->u1.isInterrupted = 0;
|
||||
|
||||
+2
-4
@@ -400,10 +400,7 @@ static int doWalCallbacks(sqlite3 *db){
|
||||
for(i=0; i<db->nDb; i++){
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
if( pBt ){
|
||||
int nEntry;
|
||||
sqlite3BtreeEnter(pBt);
|
||||
nEntry = sqlite3PagerWalCallback(sqlite3BtreePager(pBt));
|
||||
sqlite3BtreeLeave(pBt);
|
||||
int nEntry = sqlite3PagerWalCallback(sqlite3BtreePager(pBt));
|
||||
if( db->xWalCallback && nEntry>0 && rc==SQLITE_OK ){
|
||||
rc = db->xWalCallback(db->pWalArg, db, db->aDb[i].zName, nEntry);
|
||||
}
|
||||
@@ -583,6 +580,7 @@ int sqlite3_step(sqlite3_stmt *pStmt){
|
||||
** sqlite3_errmsg() and sqlite3_errcode().
|
||||
*/
|
||||
const char *zErr = (const char *)sqlite3_value_text(db->pErr);
|
||||
assert( zErr!=0 || db->mallocFailed );
|
||||
sqlite3DbFree(db, v->zErrMsg);
|
||||
if( !db->mallocFailed ){
|
||||
v->zErrMsg = sqlite3DbStrDup(db, zErr);
|
||||
|
||||
+7
-7
@@ -396,7 +396,6 @@ static Op *opIterNext(VdbeOpIter *p){
|
||||
*/
|
||||
int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){
|
||||
int hasAbort = 0;
|
||||
int hasFkCounter = 0;
|
||||
Op *pOp;
|
||||
VdbeOpIter sIter;
|
||||
memset(&sIter, 0, sizeof(sIter));
|
||||
@@ -405,17 +404,15 @@ int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){
|
||||
while( (pOp = opIterNext(&sIter))!=0 ){
|
||||
int opcode = pOp->opcode;
|
||||
if( opcode==OP_Destroy || opcode==OP_VUpdate || opcode==OP_VRename
|
||||
#ifndef SQLITE_OMIT_FOREIGN_KEY
|
||||
|| (opcode==OP_FkCounter && pOp->p1==0 && pOp->p2==1)
|
||||
#endif
|
||||
|| ((opcode==OP_Halt || opcode==OP_HaltIfNull)
|
||||
&& ((pOp->p1&0xff)==SQLITE_CONSTRAINT && pOp->p2==OE_Abort))
|
||||
){
|
||||
hasAbort = 1;
|
||||
break;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_FOREIGN_KEY
|
||||
if( opcode==OP_FkCounter && pOp->p1==0 && pOp->p2==1 ){
|
||||
hasFkCounter = 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
sqlite3DbFree(v->db, sIter.apSub);
|
||||
|
||||
@@ -424,7 +421,7 @@ int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){
|
||||
** through all opcodes and hasAbort may be set incorrectly. Return
|
||||
** true for this case to prevent the assert() in the callers frame
|
||||
** from failing. */
|
||||
return ( v->db->mallocFailed || hasAbort==mayAbort || hasFkCounter );
|
||||
return ( v->db->mallocFailed || hasAbort==mayAbort );
|
||||
}
|
||||
#endif /* SQLITE_DEBUG - the sqlite3AssertMayAbort() function */
|
||||
|
||||
@@ -1057,6 +1054,7 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
|
||||
CollSeq *pColl = pKeyInfo->aColl[j];
|
||||
const char *zColl = pColl ? pColl->zName : "nil";
|
||||
int n = sqlite3Strlen30(zColl);
|
||||
assert( pColl==0 || sqlite3ValidCollSeq(pColl) );
|
||||
if( n==6 && memcmp(zColl,"BINARY",6)==0 ){
|
||||
zColl = "B";
|
||||
n = 1;
|
||||
@@ -3361,6 +3359,7 @@ static int vdbeCompareMemString(
|
||||
const CollSeq *pColl,
|
||||
u8 *prcErr /* If an OOM occurs, set to SQLITE_NOMEM */
|
||||
){
|
||||
assert( sqlite3ValidCollSeq(pColl) );
|
||||
if( pMem1->enc==pColl->enc ){
|
||||
/* The strings are already in the correct encoding. Call the
|
||||
** comparison function directly */
|
||||
@@ -3476,6 +3475,7 @@ int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const CollSeq *pColl){
|
||||
assert( !pColl || pColl->xCmp );
|
||||
|
||||
if( pColl ){
|
||||
assert( sqlite3ValidCollSeq(pColl) );
|
||||
return vdbeCompareMemString(pMem1, pMem2, pColl, 0);
|
||||
}
|
||||
/* If a NULL pointer was passed as the collate function, fall through
|
||||
|
||||
+13
-11
@@ -152,7 +152,7 @@
|
||||
** to a level 0 PMA. The purpose of this limit is to prevent various integer
|
||||
** overflows. 512MiB.
|
||||
*/
|
||||
#define SQLITE_MAX_PMASZ (1<<29)
|
||||
#define SQLITE_MAX_MXPMASIZE (1<<29)
|
||||
|
||||
/*
|
||||
** Private objects used by the sorter
|
||||
@@ -448,6 +448,9 @@ struct SorterRecord {
|
||||
*/
|
||||
#define SRVAL(p) ((void*)((SorterRecord*)(p) + 1))
|
||||
|
||||
/* The minimum PMA size is set to this value multiplied by the database
|
||||
** page size in bytes. */
|
||||
#define SORTER_MIN_WORKING 10
|
||||
|
||||
/* Maximum number of PMAs that a single MergeEngine can merge */
|
||||
#define SORTER_MAX_MERGE_COUNT 16
|
||||
@@ -846,11 +849,10 @@ int sqlite3VdbeSorterInit(
|
||||
}
|
||||
|
||||
if( !sqlite3TempInMemory(db) ){
|
||||
u32 szPma = sqlite3GlobalConfig.szPma;
|
||||
pSorter->mnPmaSize = szPma * pgsz;
|
||||
pSorter->mnPmaSize = SORTER_MIN_WORKING * pgsz;
|
||||
mxCache = db->aDb[0].pSchema->cache_size;
|
||||
if( mxCache<szPma ) mxCache = szPma;
|
||||
pSorter->mxPmaSize = MIN((i64)mxCache*pgsz, SQLITE_MAX_PMASZ);
|
||||
if( mxCache<SORTER_MIN_WORKING ) mxCache = SORTER_MIN_WORKING;
|
||||
pSorter->mxPmaSize = MIN((i64)mxCache*pgsz, SQLITE_MAX_MXPMASIZE);
|
||||
|
||||
/* EVIDENCE-OF: R-26747-61719 When the application provides any amount of
|
||||
** scratch memory using SQLITE_CONFIG_SCRATCH, SQLite avoids unnecessary
|
||||
@@ -1128,12 +1130,12 @@ void sqlite3VdbeSorterClose(sqlite3 *db, VdbeCursor *pCsr){
|
||||
*/
|
||||
static void vdbeSorterExtendFile(sqlite3 *db, sqlite3_file *pFd, i64 nByte){
|
||||
if( nByte<=(i64)(db->nMaxSorterMmap) && pFd->pMethods->iVersion>=3 ){
|
||||
void *p = 0;
|
||||
int chunksize = 4*1024;
|
||||
sqlite3OsFileControlHint(pFd, SQLITE_FCNTL_CHUNK_SIZE, &chunksize);
|
||||
sqlite3OsFileControlHint(pFd, SQLITE_FCNTL_SIZE_HINT, &nByte);
|
||||
sqlite3OsFetch(pFd, 0, (int)nByte, &p);
|
||||
sqlite3OsUnfetch(pFd, 0, p);
|
||||
int rc = sqlite3OsTruncate(pFd, nByte);
|
||||
if( rc==SQLITE_OK ){
|
||||
void *p = 0;
|
||||
sqlite3OsFetch(pFd, 0, (int)nByte, &p);
|
||||
sqlite3OsUnfetch(pFd, 0, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
@@ -2412,7 +2412,7 @@ int sqlite3WalFindFrame(
|
||||
for(iKey=walHash(pgno); aHash[iKey]; iKey=walNextHash(iKey)){
|
||||
u32 iFrame = aHash[iKey] + iZero;
|
||||
if( iFrame<=iLast && aPgno[aHash[iKey]]==pgno ){
|
||||
assert( iFrame>iRead || CORRUPT_DB );
|
||||
/* assert( iFrame>iRead ); -- not true if there is corruption */
|
||||
iRead = iFrame;
|
||||
}
|
||||
if( (nCollide--)==0 ){
|
||||
|
||||
+2
-2
@@ -222,7 +222,6 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u8 wtFlags){
|
||||
sqlite3DbFree(db, pOld);
|
||||
}
|
||||
pWC->nSlot = sqlite3DbMallocSize(db, pWC->a)/sizeof(pWC->a[0]);
|
||||
memset(&pWC->a[pWC->nTerm], 0, sizeof(pWC->a[0])*(pWC->nSlot-pWC->nTerm));
|
||||
}
|
||||
pTerm = &pWC->a[idx = pWC->nTerm++];
|
||||
if( p && ExprHasProperty(p, EP_Unlikely) ){
|
||||
@@ -2948,7 +2947,7 @@ static void addScanStatus(
|
||||
){
|
||||
const char *zObj = 0;
|
||||
WhereLoop *pLoop = pLvl->pWLoop;
|
||||
if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 && pLoop->u.btree.pIndex!=0 ){
|
||||
if( (pLoop->wsFlags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){
|
||||
zObj = pLoop->u.btree.pIndex->zName;
|
||||
}else{
|
||||
zObj = pSrclist->a[pLvl->iFrom].zName;
|
||||
@@ -3941,6 +3940,7 @@ static void whereLoopClearUnion(sqlite3 *db, WhereLoop *p){
|
||||
p->u.vtab.idxStr = 0;
|
||||
}else if( (p->wsFlags & WHERE_AUTO_INDEX)!=0 && p->u.btree.pIndex!=0 ){
|
||||
sqlite3DbFree(db, p->u.btree.pIndex->zColAff);
|
||||
sqlite3KeyInfoUnref(p->u.btree.pIndex->pKeyInfo);
|
||||
sqlite3DbFree(db, p->u.btree.pIndex);
|
||||
p->u.btree.pIndex = 0;
|
||||
}
|
||||
|
||||
+2
-9
@@ -20,15 +20,6 @@ set testprefix bigsort
|
||||
# loop if the product was also an integer multiple of 2^32, or
|
||||
# inefficiency otherwise.
|
||||
#
|
||||
# This test causes thrashing on machines with smaller amounts of
|
||||
# memory. Make sure the host has at least 8GB available before running
|
||||
# this test.
|
||||
#
|
||||
if {[catch {exec free | grep Mem:} out] || [lindex $out 1]<8000000} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
PRAGMA page_size = 1024;
|
||||
CREATE TABLE t1(a, b);
|
||||
@@ -48,3 +39,5 @@ do_execsql_test 1.1 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
||||
+12
-28
@@ -17,14 +17,17 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !columnmetadata {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Set up a schema in the main and temp test databases.
|
||||
do_test colmeta-0 {
|
||||
execsql {
|
||||
CREATE TABLE abc(a, b, c);
|
||||
CREATE TABLE abc2(a PRIMARY KEY COLLATE NOCASE, b VARCHAR(32), c);
|
||||
CREATE TABLE abc3(a NOT NULL, b INTEGER PRIMARY KEY, c);
|
||||
CREATE TABLE abc5(w,x,y,z,PRIMARY KEY(x,z)) WITHOUT ROWID;
|
||||
CREATE TABLE abc6(rowid TEXT COLLATE rtrim, oid REAL, _rowid_ BLOB);
|
||||
}
|
||||
ifcapable autoinc {
|
||||
execsql {
|
||||
@@ -54,27 +57,19 @@ set tests {
|
||||
13 {main abc rowid} {0 {INTEGER BINARY 0 1 0}}
|
||||
14 {main abc3 rowid} {0 {INTEGER BINARY 0 1 0}}
|
||||
16 {main abc d} {1 {no such table column: abc.d}}
|
||||
20 {main abc5 w} {0 {{} BINARY 0 0 0}}
|
||||
21 {main abc5 x} {0 {{} BINARY 1 1 0}}
|
||||
22 {main abc5 y} {0 {{} BINARY 0 0 0}}
|
||||
23 {main abc5 z} {0 {{} BINARY 1 1 0}}
|
||||
24 {main abc5 rowid} {1 {no such table column: abc5.rowid}}
|
||||
30 {main abc6 rowid} {0 {TEXT rtrim 0 0 0}}
|
||||
31 {main abc6 oid} {0 {REAL BINARY 0 0 0}}
|
||||
32 {main abc6 _rowid_} {0 {BLOB BINARY 0 0 0}}
|
||||
}
|
||||
ifcapable autoinc {
|
||||
ifcapable view {
|
||||
set tests [concat $tests {
|
||||
100 {{} abc4 b} {0 {INTEGER BINARY 0 1 1}}
|
||||
101 {main abc4 rowid} {0 {INTEGER BINARY 0 1 1}}
|
||||
8 {{} abc4 b} {0 {INTEGER BINARY 0 1 1}}
|
||||
15 {main abc4 rowid} {0 {INTEGER BINARY 0 1 1}}
|
||||
}]
|
||||
}
|
||||
ifcapable view {
|
||||
set tests [concat $tests {
|
||||
200 {{} v1 a} {1 {no such table column: v1.a}}
|
||||
201 {main v1 b} {1 {no such table column: v1.b}}
|
||||
202 {main v1 badname} {1 {no such table column: v1.badname}}
|
||||
203 {main v1 rowid} {1 {no such table column: v1.rowid}}
|
||||
9 {{} v1 a} {1 {no such table column: v1.a}}
|
||||
10 {main v1 b} {1 {no such table column: v1.b}}
|
||||
11 {main v1 badname} {1 {no such table column: v1.badname}}
|
||||
12 {main v1 rowid} {1 {no such table column: v1.rowid}}
|
||||
}]
|
||||
}
|
||||
|
||||
@@ -96,15 +91,4 @@ foreach {tn params results} $tests {
|
||||
} $results
|
||||
}
|
||||
|
||||
# Calling sqlite3_table_column_metadata with a NULL column name merely
|
||||
# checks for the existance of the table.
|
||||
#
|
||||
do_test colmeta-300 {
|
||||
catch {sqlite3_table_column_metadata $::DB main xyzzy} res
|
||||
} {1}
|
||||
do_test colmeta-301 {
|
||||
catch {sqlite3_table_column_metadata $::DB main abc} res
|
||||
} {0}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -1,202 +0,0 @@
|
||||
# 2014 December 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/wal_common.tcl
|
||||
set testprefix e_walauto
|
||||
|
||||
|
||||
proc read_nbackfill {} {
|
||||
seek $::shmfd 96
|
||||
binary scan [read $::shmfd 4] i nBackfill
|
||||
set nBackfill
|
||||
}
|
||||
proc read_mxframe {} {
|
||||
seek $::shmfd 16
|
||||
binary scan [read $::shmfd 4] i mxFrame
|
||||
set mxFrame
|
||||
}
|
||||
|
||||
# Assuming that the main db for database handle
|
||||
#
|
||||
proc do_autocommit_threshold_test {tn value} {
|
||||
|
||||
set nBackfillSaved [read_nbackfill]
|
||||
while {1} {
|
||||
db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) }
|
||||
if {[read_mxframe] >= $value} break
|
||||
}
|
||||
|
||||
set nBackfillNew [read_nbackfill]
|
||||
uplevel [list do_test $tn "expr $nBackfillNew > $nBackfillSaved" 1]
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-30135-06439 The wal_autocheckpoint pragma can be used
|
||||
# to invoke this interface from SQL.
|
||||
#
|
||||
# All tests in this file are run twice - once using the
|
||||
# sqlite3_wal_autocheckpoint() API, and once using "PRAGMA
|
||||
# wal_autocheckpoint".
|
||||
#
|
||||
foreach {tn code} {
|
||||
1 {
|
||||
proc autocheckpoint {db value} {
|
||||
uplevel [list $db eval "PRAGMA wal_autocheckpoint = $value"]
|
||||
}
|
||||
}
|
||||
|
||||
2 {
|
||||
proc autocheckpoint {db value} {
|
||||
uplevel [list sqlite3_wal_autocheckpoint $db $value]
|
||||
return $value
|
||||
}
|
||||
}
|
||||
} {
|
||||
|
||||
eval $code
|
||||
|
||||
reset_db
|
||||
execsql { PRAGMA auto_vacuum = 0 }
|
||||
do_execsql_test 1.$tn.0 { PRAGMA journal_mode = WAL } {wal}
|
||||
do_execsql_test 1.$tn.1 { CREATE TABLE t1(a, b) }
|
||||
set shmfd [open "test.db-shm" rb]
|
||||
|
||||
# EVIDENCE-OF: R-41531-51083 Every new database connection defaults to
|
||||
# having the auto-checkpoint enabled with a threshold of 1000 or
|
||||
# SQLITE_DEFAULT_WAL_AUTOCHECKPOINT pages.
|
||||
#
|
||||
do_autocommit_threshold_test 1.$tn.2 1000
|
||||
db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) }
|
||||
do_autocommit_threshold_test 1.$tn.3 1000
|
||||
|
||||
# EVIDENCE-OF: R-38128-34102 The sqlite3_wal_autocheckpoint(D,N) is a
|
||||
# wrapper around sqlite3_wal_hook() that causes any database on database
|
||||
# connection D to automatically checkpoint after committing a
|
||||
# transaction if there are N or more frames in the write-ahead log file.
|
||||
#
|
||||
do_test 1.$tn.4 {
|
||||
db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) }
|
||||
autocheckpoint db 100
|
||||
} {100}
|
||||
do_autocommit_threshold_test 1.$tn.5 100
|
||||
|
||||
do_test 1.$tn.6 {
|
||||
db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) }
|
||||
autocheckpoint db 500
|
||||
} {500}
|
||||
do_autocommit_threshold_test 1.$tn.7 500
|
||||
|
||||
# EVIDENCE-OF: R-26993-43540 Passing zero or a negative value as the
|
||||
# nFrame parameter disables automatic checkpoints entirely.
|
||||
#
|
||||
do_test 1.$tn.7 {
|
||||
autocheckpoint db 0 ;# Set to zero
|
||||
for {set i 0} {$i < 10000} {incr i} {
|
||||
db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) }
|
||||
}
|
||||
expr {[file size test.db-wal] > (5 * 1024 * 1024)}
|
||||
} 1
|
||||
do_test 1.$tn.8 {
|
||||
sqlite3_wal_checkpoint_v2 db truncate
|
||||
file size test.db-wal
|
||||
} 0
|
||||
do_test 1.$tn.9 {
|
||||
autocheckpoint db -4 ;# Set to a negative value
|
||||
for {set i 0} {$i < 10000} {incr i} {
|
||||
db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) }
|
||||
}
|
||||
expr {[file size test.db-wal] > (5 * 1024 * 1024)}
|
||||
} 1
|
||||
|
||||
# EVIDENCE-OF: R-10203-42688 The callback registered by this function
|
||||
# replaces any existing callback registered using sqlite3_wal_hook().
|
||||
#
|
||||
set ::wal_hook_callback 0
|
||||
proc wal_hook_callback {args} { incr ::wal_hook_callback ; return 0 }
|
||||
do_test 1.$tn.10.1 {
|
||||
db wal_hook wal_hook_callback
|
||||
db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) }
|
||||
db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) }
|
||||
set ::wal_hook_callback
|
||||
} 2
|
||||
do_test 1.$tn.10.2 {
|
||||
autocheckpoint db 100
|
||||
db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) }
|
||||
db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) }
|
||||
set ::wal_hook_callback
|
||||
} 2
|
||||
|
||||
# EVIDENCE-OF: R-17497-43474 Likewise, registering a callback using
|
||||
# sqlite3_wal_hook() disables the automatic checkpoint mechanism
|
||||
# configured by this function.
|
||||
do_test 1.$tn.11.1 {
|
||||
sqlite3_wal_checkpoint_v2 db truncate
|
||||
file size test.db-wal
|
||||
} 0
|
||||
do_test 1.$tn.11.2 {
|
||||
autocheckpoint db 100
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) }
|
||||
}
|
||||
expr {[file size test.db-wal] < (1 * 1024 * 1024)}
|
||||
} 1
|
||||
do_test 1.$tn.11.3 {
|
||||
db wal_hook wal_hook_callback
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) }
|
||||
}
|
||||
expr {[file size test.db-wal] < (1 * 1024 * 1024)}
|
||||
} 0
|
||||
|
||||
# EVIDENCE-OF: R-33080-59193 Checkpoints initiated by this mechanism
|
||||
# are PASSIVE.
|
||||
#
|
||||
set ::busy_callback_count 0
|
||||
proc busy_callback {args} {
|
||||
puts Hello
|
||||
incr ::busy_callback_count
|
||||
return 0
|
||||
}
|
||||
do_test 1.$tn.12.1 {
|
||||
sqlite3_wal_checkpoint_v2 db truncate
|
||||
autocheckpoint db 100
|
||||
db busy busy_callback
|
||||
db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) }
|
||||
db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) }
|
||||
} {}
|
||||
do_test 1.$tn.12.2 {
|
||||
sqlite3 db2 test.db
|
||||
db2 eval { BEGIN; SELECT * FROM t1 LIMIT 10; }
|
||||
read_nbackfill
|
||||
} {0}
|
||||
do_test 1.$tn.12.3 {
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) }
|
||||
}
|
||||
read_nbackfill
|
||||
} {2}
|
||||
do_test 1.$tn.12.4 {
|
||||
set ::busy_callback_count
|
||||
} {0}
|
||||
db2 close
|
||||
|
||||
do_test 1.$tn.12.5 {
|
||||
db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) }
|
||||
read_nbackfill
|
||||
} {1559}
|
||||
|
||||
db close
|
||||
close $shmfd
|
||||
}
|
||||
|
||||
finish_test
|
||||
@@ -1,754 +0,0 @@
|
||||
# 2014 December 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/lock_common.tcl
|
||||
source $testdir/wal_common.tcl
|
||||
set testprefix e_walckpt
|
||||
|
||||
# The following two commands are used to determine if any of the files
|
||||
# "test.db", "test.db2" and "test.db3" are modified by a test case.
|
||||
#
|
||||
# The [save_db_hashes] command saves a hash of the current contents of
|
||||
# all three files in global variables. The [compare_db_hashes] compares
|
||||
# the current contents with the saved hashes and returns a list of the
|
||||
# files that have changed.
|
||||
#
|
||||
proc save_db_hashes {} {
|
||||
global H
|
||||
foreach f {test.db test.db2 test.db3} {
|
||||
set H($f) 0
|
||||
catch { set H($f) [md5file $f] }
|
||||
}
|
||||
}
|
||||
proc compare_db_hashes {} {
|
||||
global H
|
||||
set ret [list]
|
||||
foreach f {test.db test.db2 test.db3} {
|
||||
set expect 0
|
||||
catch { set expect [md5file $f] }
|
||||
if {$H($f) != $expect} { lappend ret $f }
|
||||
}
|
||||
set ret
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# All calls to the [sqlite3_wal_checkpoint_v2] command made within this
|
||||
# file use this wrapper. It's sole purpose is to throw an error if the
|
||||
# following requirement is violated:
|
||||
#
|
||||
# EVIDENCE-OF: R-60567-47780 Unless it returns SQLITE_MISUSE, the
|
||||
# sqlite3_wal_checkpoint_v2() interface sets the error information that
|
||||
# is queried by sqlite3_errcode() and sqlite3_errmsg().
|
||||
#
|
||||
proc wal_checkpoint_v2 {db args} {
|
||||
set rc [catch {
|
||||
uplevel sqlite3_wal_checkpoint_v2 $db $args
|
||||
} msg]
|
||||
|
||||
set errcode "SQLITE_OK"
|
||||
if {$rc} {
|
||||
set errcode [lindex [split $msg " "] 0]
|
||||
} elseif { [lindex $msg 0] } {
|
||||
set errcode "SQLITE_BUSY"
|
||||
}
|
||||
|
||||
if {$errcode != "SQLITE_MISUSE" && [sqlite3_errcode $db] != $errcode} {
|
||||
error "sqlite3_errcode mismatch! (1) $errcode!=[sqlite3_errcode $db]"
|
||||
}
|
||||
|
||||
if {$rc==0} {
|
||||
return $msg
|
||||
} else {
|
||||
error $msg
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# The following tests are run 3 times, each using a different method of
|
||||
# invoking a checkpoint:
|
||||
#
|
||||
# 1) Using sqlite3_wal_checkpoint_v2()
|
||||
# 2) Using "PRAGMA wal_checkpoint"
|
||||
# 3) Using sqlite3_wal_checkpoint() in place of checkpoint_v2(PASSIVE)
|
||||
#
|
||||
# Cases (2) and (3) are to show that the following statements are
|
||||
# correct, respectively:
|
||||
#
|
||||
# EVIDENCE-OF: R-36706-10507 The PRAGMA wal_checkpoint command can be
|
||||
# used to invoke this interface from SQL.
|
||||
#
|
||||
# EVIDENCE-OF: R-41613-20553 The sqlite3_wal_checkpoint(D,X) is
|
||||
# equivalent to
|
||||
# sqlite3_wal_checkpoint_v2(D,X,SQLITE_CHECKPOINT_PASSIVE,0,0).
|
||||
#
|
||||
foreach {tn script} {
|
||||
1 {
|
||||
proc checkpoint {db mode args} {
|
||||
eval wal_checkpoint_v2 [list $db] [list $mode] $args
|
||||
}
|
||||
}
|
||||
|
||||
2 {
|
||||
proc checkpoint {db mode args} {
|
||||
set sql "PRAGMA wal_checkpoint = $mode"
|
||||
if {[llength $args] && [lindex $args 0]!=""} {
|
||||
set sql "PRAGMA [lindex $args 0].wal_checkpoint = $mode"
|
||||
}
|
||||
set rc [catch { $db eval $sql } msg]
|
||||
if {$rc} {
|
||||
regsub {database} $msg {database:} msg
|
||||
error "[sqlite3_errcode $db] - $msg"
|
||||
}
|
||||
set msg
|
||||
}
|
||||
}
|
||||
|
||||
3 {
|
||||
proc checkpoint {db mode args} {
|
||||
if {$mode == "passive"} {
|
||||
set rc [eval sqlite3_wal_checkpoint [list $db] $args]
|
||||
if {$rc != "SQLITE_OK"} {
|
||||
error "$rc - [sqlite3_errmsg $db]"
|
||||
}
|
||||
} else {
|
||||
eval wal_checkpoint_v2 [list $db] [list $mode] $args
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} {
|
||||
|
||||
eval $script
|
||||
|
||||
reset_db
|
||||
forcedelete test.db2 test.db3 test.db4
|
||||
execsql {
|
||||
ATTACH 'test.db2' AS aux;
|
||||
ATTACH 'test.db3' AS aux2;
|
||||
ATTACH 'test.db4' AS aux3;
|
||||
CREATE TABLE t1(x);
|
||||
CREATE TABLE aux.t2(x);
|
||||
CREATE TABLE aux2.t3(x);
|
||||
CREATE TABLE aux3.t4(x);
|
||||
PRAGMA main.journal_mode = WAL;
|
||||
PRAGMA aux.journal_mode = WAL;
|
||||
PRAGMA aux2.journal_mode = WAL;
|
||||
/* Leave aux4 in rollback mode */
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-49787-09095 The sqlite3_wal_checkpoint_v2(D,X,M,L,C)
|
||||
# interface runs a checkpoint operation on database X of database
|
||||
# connection D in mode M. Status information is written back into
|
||||
# integers pointed to by L and C.
|
||||
#
|
||||
# Tests 1, 2 and 3 below verify the "on database X" part of the
|
||||
# above. Other parts of this requirement are tested below.
|
||||
#
|
||||
# EVIDENCE-OF: R-00653-06026 If parameter zDb is NULL or points to a
|
||||
# zero length string, then the specified operation is attempted on all
|
||||
# WAL databases attached to database connection db.
|
||||
#
|
||||
# Tests 4 and 5 below test this.
|
||||
#
|
||||
foreach {tn2 zDb dblist} {
|
||||
1 main test.db
|
||||
2 aux test.db2
|
||||
3 aux2 test.db3
|
||||
4 "" {test.db test.db2 test.db3}
|
||||
5 - {test.db test.db2 test.db3}
|
||||
6 temp {}
|
||||
} {
|
||||
do_test $tn.1.$tn2 {
|
||||
execsql {
|
||||
INSERT INTO t1 VALUES(1);
|
||||
INSERT INTO t2 VALUES(2);
|
||||
INSERT INTO t3 VALUES(3);
|
||||
}
|
||||
save_db_hashes
|
||||
|
||||
if {$zDb == "-"} {
|
||||
checkpoint db passive
|
||||
} else {
|
||||
checkpoint db passive $zDb
|
||||
}
|
||||
|
||||
compare_db_hashes
|
||||
} $dblist
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-38207-48996 If zDb is not NULL (or a zero length
|
||||
# string) and is not the name of any attached database, SQLITE_ERROR is
|
||||
# returned to the caller.
|
||||
do_test $tn.2.1 {
|
||||
list [catch { checkpoint db passive notadb } msg] $msg
|
||||
} {1 {SQLITE_ERROR - unknown database: notadb}}
|
||||
|
||||
# EVIDENCE-OF: R-14303-42483 If database zDb is the name of an attached
|
||||
# database that is not in WAL mode, SQLITE_OK is returned and both
|
||||
# *pnLog and *pnCkpt set to -1.
|
||||
#
|
||||
if {$tn==3} {
|
||||
# With sqlite3_wal_checkpoint() the two output variables cannot be
|
||||
# tested. So just test that no error is returned when attempting to
|
||||
# checkpoint a db in rollback mode.
|
||||
do_test $tn.2.2.a { checkpoint db passive aux3 } {}
|
||||
} else {
|
||||
do_test $tn.2.2.b { checkpoint db passive aux3 } {0 -1 -1}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-62028-47212 All calls obtain an exclusive "checkpoint"
|
||||
# lock on the database file.
|
||||
db close
|
||||
testvfs tvfs
|
||||
tvfs filter xShmLock
|
||||
tvfs script filelock
|
||||
proc filelock {method file handle details} {
|
||||
# Test for an exclusive checkpoint lock. A checkpoint lock locks a
|
||||
# single byte starting at offset 1.
|
||||
if {$details == "1 1 lock exclusive"} { set ::seen_checkpoint_lock 1 }
|
||||
}
|
||||
sqlite3 db test.db -vfs tvfs
|
||||
do_test $tn.3.1 {
|
||||
execsql { INSERT INTO t1 VALUES('xyz') }
|
||||
unset -nocomplain ::seen_checkpoint_lock
|
||||
checkpoint db passive
|
||||
set ::seen_checkpoint_lock
|
||||
} {1}
|
||||
db close
|
||||
tvfs delete
|
||||
reset_db
|
||||
|
||||
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-10421-19736 If any other process is running a
|
||||
# checkpoint operation at the same time, the lock cannot be obtained and
|
||||
# SQLITE_BUSY is returned.
|
||||
#
|
||||
# EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured,
|
||||
# it will not be invoked in this case.
|
||||
#
|
||||
testvfs tvfs
|
||||
tvfs filter xWrite
|
||||
sqlite3 db test.db -vfs tvfs
|
||||
sqlite3 db2 test.db -vfs tvfs
|
||||
|
||||
do_test $tn.3.2.1 {
|
||||
db2 eval {
|
||||
PRAGMA auto_vacuum = 0;
|
||||
PRAGMA journal_mode = WAL;
|
||||
CREATE TABLE t1(x, y);
|
||||
INSERT INTO t1 VALUES(1,2);
|
||||
INSERT INTO t1 VALUES(3,4);
|
||||
INSERT INTO t1 VALUES(5,6);
|
||||
}
|
||||
file size test.db-wal
|
||||
} [wal_file_size 5 1024]
|
||||
|
||||
|
||||
# Connection [db] runs a checkpoint. During this checkpoint, each
|
||||
# time it calls xWrite() to write a page into the database file, we
|
||||
# attempt to start a checkpoint using [db2]. According to the
|
||||
# first requirement being tested, this should return SQLITE_BUSY. According
|
||||
# to the second, the busy-handler belonging to [db2] should not be
|
||||
# invoked.
|
||||
#
|
||||
set ::write_count 0
|
||||
set ::write_errors [list]
|
||||
proc busy_callback {args} {
|
||||
lappend ::write_errors "busy handler called!"
|
||||
}
|
||||
proc write_callback {args} {
|
||||
set rc [catch {checkpoint db2 passive} msg]
|
||||
if {0==[regexp "database is locked" $msg] && $msg!="1 -1 -1"} {
|
||||
lappend ::write_errors "$rc $msg"
|
||||
}
|
||||
incr ::write_count
|
||||
}
|
||||
db2 busy busy_callback
|
||||
tvfs script write_callback
|
||||
|
||||
do_test $tn.3.2.2 {
|
||||
db eval {SELECT * FROM sqlite_master}
|
||||
checkpoint db full
|
||||
set ::write_count
|
||||
} {2}
|
||||
|
||||
do_test $tn.3.2.3 {
|
||||
set ::write_errors
|
||||
} {}
|
||||
|
||||
db close
|
||||
db2 close
|
||||
tvfs delete
|
||||
|
||||
proc busy_handler {mode busy_handler_mode n} {
|
||||
incr ::busy_handler_counter
|
||||
switch -- $busy_handler_mode {
|
||||
1 {
|
||||
# Do nothing. Do not block.
|
||||
return 1
|
||||
}
|
||||
|
||||
2 {
|
||||
# Close first the reader, then later the writer. Give up before
|
||||
# closing the [db6] reader.
|
||||
if {$n==5} { catch {db2 eval commit} }
|
||||
if {$n==10} { catch {db3 eval commit} }
|
||||
if {$n==15} { return 1 }
|
||||
return 0
|
||||
}
|
||||
|
||||
3 {
|
||||
# Close first the writer, then later the reader. And finally the
|
||||
# [db6] reader.
|
||||
if {$n==5} { catch {db2 eval commit} }
|
||||
if {$n==10} { catch {db3 eval commit} }
|
||||
if {$n==15} { catch {db6 eval commit} }
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach {mode busy_handler_mode} {
|
||||
passive 1
|
||||
full 1 full 2 full 3
|
||||
restart 1 restart 2 restart 3
|
||||
truncate 1 truncate 2 truncate 3
|
||||
} {
|
||||
set tp "$tn.$mode.$busy_handler_mode"
|
||||
|
||||
set ::sync_counter 0
|
||||
|
||||
# Set up a callback function for xSync and xWrite calls made during
|
||||
# the checkpoint.
|
||||
#
|
||||
set ::checkpoint_ongoing 0
|
||||
proc tvfs_callback {method args} {
|
||||
if {$::checkpoint_ongoing==0} return
|
||||
|
||||
set tail [file tail [lindex $args 0]]
|
||||
if {$method == "xSync" && $tail == "test.db"} {
|
||||
incr ::sync_counter
|
||||
}
|
||||
if {$method == "xWrite" && $tail=="test.db"} {
|
||||
if {$::write_ok < 0} {
|
||||
set ::write_ok [expr ![catch {db5 eval { BEGIN IMMEDIATE }}]]
|
||||
catch { db5 eval ROLLBACK }
|
||||
}
|
||||
if {$::read_ok < 0} {
|
||||
set ::read_ok [expr ![catch {db5 eval { SELECT * FROM t1 }}]]
|
||||
}
|
||||
|
||||
# If one has not already been opened, open a read-transaction using
|
||||
# connection [db6]
|
||||
catch { db6 eval { BEGIN ; SELECT * FROM sqlite_master } } msg
|
||||
}
|
||||
if {$method == "xShmLock" } {
|
||||
set details [lindex $args 2]
|
||||
if {$details == "0 1 lock exclusive"} { set ::seen_writer_lock 1 }
|
||||
}
|
||||
}
|
||||
|
||||
catch { db close }
|
||||
forcedelete test.db
|
||||
testvfs tvfs
|
||||
sqlite3 db test.db -vfs tvfs
|
||||
#tvfs filter xSync
|
||||
tvfs script tvfs_callback
|
||||
|
||||
do_execsql_test $tp.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE t2(a, b);
|
||||
PRAGMA journal_mode = wal;
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
INSERT INTO t1 VALUES(3, 4);
|
||||
INSERT INTO t1 VALUES(5, 6);
|
||||
} {wal}
|
||||
|
||||
# Open a reader on the current database snapshot.
|
||||
do_test $tp.1 {
|
||||
sqlite3 db2 test.db -vfs tvfs
|
||||
execsql {
|
||||
BEGIN;
|
||||
SELECT * FROM t1 UNION ALL SELECT * FROM t2;
|
||||
} db2
|
||||
} {1 2 3 4 5 6}
|
||||
|
||||
# Open a writer. Write a transaction. Then begin, but do not commit,
|
||||
# a second transaction.
|
||||
do_test $tp.2 {
|
||||
sqlite3 db3 test.db -vfs tvfs
|
||||
execsql {
|
||||
INSERT INTO t2 VALUES(7, 8);
|
||||
BEGIN;
|
||||
INSERT INTO t2 VALUES(9, 10);
|
||||
SELECT * FROM t1 UNION ALL SELECT * FROM t2;
|
||||
} db3
|
||||
} {1 2 3 4 5 6 7 8 9 10}
|
||||
|
||||
sqlite3 db5 test.db -vfs tvfs
|
||||
sqlite3 db6 test.db -vfs tvfs
|
||||
|
||||
# Register a busy-handler with connection [db].
|
||||
#
|
||||
db busy [list busy_handler $mode $busy_handler_mode]
|
||||
set ::sync_counter 0
|
||||
set ::busy_handler_counter 0
|
||||
set ::read_ok -1
|
||||
set ::write_ok -1
|
||||
set ::seen_writer_lock 0
|
||||
|
||||
set ::checkpoint_ongoing 1
|
||||
do_test $tp.3 {
|
||||
checkpoint db $mode main
|
||||
set {} {}
|
||||
} {}
|
||||
set ::checkpoint_ongoing 0
|
||||
set ::did_restart_blocking [expr {[catch {db6 eval commit}]}]
|
||||
|
||||
if { $mode=="passive" } {
|
||||
# EVIDENCE-OF: R-16333-64433 Checkpoint as many frames as possible
|
||||
# without waiting for any database readers or writers to finish, then
|
||||
# sync the database file if all frames in the log were checkpointed.
|
||||
#
|
||||
# "As many frames as possible" means all but the last two transactions
|
||||
# (the two that write to table t2, of which the scond is unfinished).
|
||||
# So copying the db file only we see the t1 change, but not the t2
|
||||
# modifications.
|
||||
#
|
||||
# The busy handler is not invoked (see below) and the db reader and
|
||||
# writer are still active - so the checkpointer did not wait for either
|
||||
# readers or writers. As a result the checkpoint was not finished and
|
||||
# so the db file is not synced.
|
||||
#
|
||||
# EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked
|
||||
# in the SQLITE_CHECKPOINT_PASSIVE mode.
|
||||
#
|
||||
# It's not. Test case "$tp.6".
|
||||
#
|
||||
do_test $tp.4 {
|
||||
forcecopy test.db abc.db
|
||||
sqlite3 db4 abc.db
|
||||
db4 eval { SELECT * FROM t1 UNION ALL SELECT * FROM t2 }
|
||||
} {1 2 3 4 5 6}
|
||||
do_test $tp.5 { set ::sync_counter } 0
|
||||
do_test $tp.6 { set ::busy_handler_counter } 0
|
||||
db4 close
|
||||
|
||||
db2 eval COMMIT
|
||||
db3 eval COMMIT
|
||||
|
||||
# EVIDENCE-OF: R-65499-53765 On the other hand, passive mode might leave
|
||||
# the checkpoint unfinished if there are concurrent readers or writers.
|
||||
#
|
||||
# The reader and writer have now dropped their locks. And so a
|
||||
# checkpoint now is able to checkpoint more frames. Showing that the
|
||||
# attempt above was left "unfinished".
|
||||
#
|
||||
# Also, because the checkpoint finishes this time, the db is synced.
|
||||
# Which is part of R-16333-64433 above.
|
||||
#
|
||||
set ::checkpoint_ongoing 1
|
||||
do_test $tp.7 {
|
||||
checkpoint db $mode main
|
||||
forcecopy test.db abc.db
|
||||
sqlite3 db4 abc.db
|
||||
db4 eval { SELECT * FROM t1 UNION ALL SELECT * FROM t2 }
|
||||
} {1 2 3 4 5 6 7 8 9 10}
|
||||
set ::checkpoint_ongoing 0
|
||||
do_test $tp.7 { set ::sync_counter } 1
|
||||
do_test $tp.8 { set ::busy_handler_counter } 0
|
||||
db4 close
|
||||
}
|
||||
|
||||
if { $mode=="full" || $mode=="restart" || $mode=="truncate" } {
|
||||
|
||||
# EVIDENCE-OF: R-59782-36818 The SQLITE_CHECKPOINT_FULL, RESTART and
|
||||
# TRUNCATE modes also obtain the exclusive "writer" lock on the
|
||||
# database file.
|
||||
#
|
||||
# Or at least attempts to obtain.
|
||||
#
|
||||
do_test $tp.9 {
|
||||
set ::seen_writer_lock
|
||||
} {1}
|
||||
|
||||
if {$busy_handler_mode==2 || $busy_handler_mode==3} {
|
||||
# EVIDENCE-OF: R-59171-47567 This mode blocks (it invokes the
|
||||
# busy-handler callback) until there is no database writer and all
|
||||
# readers are reading from the most recent database snapshot.
|
||||
#
|
||||
# The test below shows that both the reader and writer have
|
||||
# finished:
|
||||
#
|
||||
# Also restated by the following two. That both busy_handler_mode
|
||||
# values 2 and 3 work show that both of the following are true - as
|
||||
# they release the reader and writer transactions in different
|
||||
# orders.
|
||||
#
|
||||
# EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained
|
||||
# immediately, and a busy-handler is configured, it is invoked and the
|
||||
# writer lock retried until either the busy-handler returns 0 or the
|
||||
# lock is successfully obtained.
|
||||
#
|
||||
# EVIDENCE-OF: R-48107-00250 The busy-handler is also invoked while
|
||||
# waiting for database readers as described above.
|
||||
#
|
||||
do_test $tp.7 {
|
||||
list [catchsql COMMIT db2] [catchsql COMMIT db3]
|
||||
} [list \
|
||||
{1 {cannot commit - no transaction is active}} \
|
||||
{1 {cannot commit - no transaction is active}} \
|
||||
]
|
||||
|
||||
# EVIDENCE-OF: R-29177-48281 It then checkpoints all frames in the log
|
||||
# file and syncs the database file.
|
||||
#
|
||||
do_test $tp.8 {
|
||||
forcecopy test.db abc.db
|
||||
sqlite3 db4 abc.db
|
||||
db4 eval { SELECT * FROM t1 UNION ALL SELECT * FROM t2 }
|
||||
} {1 2 3 4 5 6 7 8 9 10}
|
||||
do_test $tp.9 { set ::sync_counter } 1
|
||||
db4 close
|
||||
|
||||
# EVIDENCE-OF: R-51867-44713 This mode blocks new database writers
|
||||
# while it is pending, but new database readers are allowed to continue
|
||||
# unimpeded.
|
||||
#
|
||||
# EVIDENCE-OF: R-47276-58266 Like SQLITE_CHECKPOINT_FULL, this mode
|
||||
# blocks new database writer attempts while it is pending, but does not
|
||||
# impede readers.
|
||||
#
|
||||
# The first of the above two refers to "full" mode. The second
|
||||
# to "restart".
|
||||
#
|
||||
do_test $tp.10.1 {
|
||||
list $::write_ok $::read_ok
|
||||
} {0 1}
|
||||
|
||||
# EVIDENCE-OF: R-12410-31217 This mode works the same way as
|
||||
# SQLITE_CHECKPOINT_FULL with the addition that after checkpointing the
|
||||
# log file it blocks (calls the busy-handler callback) until all
|
||||
# readers are reading from the database file only.
|
||||
#
|
||||
# The stuff above passed, so the first part of this requirement
|
||||
# is met. The second part is tested below. If the checkpoint mode
|
||||
# was "restart" or "truncate", then the busy-handler will have
|
||||
# been called to block on wal-file readers.
|
||||
#
|
||||
do_test $tp.11 {
|
||||
set ::did_restart_blocking
|
||||
} [expr {($mode=="restart"||$mode=="truncate")&&$busy_handler_mode==3}]
|
||||
|
||||
# EVIDENCE-OF: R-44699-57140 This mode works the same way as
|
||||
# SQLITE_CHECKPOINT_RESTART with the addition that it also truncates
|
||||
# the log file to zero bytes just prior to a successful return.
|
||||
if {$mode=="truncate" && $busy_handler_mode==3} {
|
||||
do_test $tp.12 {
|
||||
file size test.db-wal
|
||||
} 0
|
||||
}
|
||||
} elseif {$busy_handler_mode==1} {
|
||||
|
||||
# EVIDENCE-OF: R-34519-06271 SQLITE_BUSY is returned in this case.
|
||||
if {$tn!=2} {
|
||||
# ($tn==2) is the loop that uses "PRAGMA wal_checkpoint"
|
||||
do_test $tp.13 { sqlite3_errcode db } {SQLITE_BUSY}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-49155-63541 If the busy-handler returns 0 before the
|
||||
# writer lock is obtained or while waiting for database readers, the
|
||||
# checkpoint operation proceeds from that point in the same way as
|
||||
# SQLITE_CHECKPOINT_PASSIVE - checkpointing as many frames as possible
|
||||
# without blocking any further.
|
||||
do_test $tp.14 {
|
||||
forcecopy test.db abc.db
|
||||
sqlite3 db4 abc.db
|
||||
db4 eval { SELECT * FROM t1 UNION ALL SELECT * FROM t2 }
|
||||
} {1 2 3 4 5 6}
|
||||
do_test $tp.15 { set ::sync_counter } 0
|
||||
do_test $tp.16 { set ::busy_handler_counter } 1
|
||||
db4 close
|
||||
}
|
||||
}
|
||||
|
||||
db2 close
|
||||
db3 close
|
||||
db5 close
|
||||
db6 close
|
||||
}
|
||||
|
||||
db close
|
||||
tvfs delete
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-03996-12088 The M parameter must be a valid checkpoint
|
||||
# mode:
|
||||
#
|
||||
# Valid checkpoint modes are 0, 1, 2 and 3.
|
||||
#
|
||||
sqlite3 db test.db
|
||||
foreach {tn mode res} {
|
||||
0 -1001 {1 {SQLITE_MISUSE - not an error}}
|
||||
1 -1 {1 {SQLITE_MISUSE - not an error}}
|
||||
2 0 {0 {0 -1 -1}}
|
||||
3 1 {0 {0 -1 -1}}
|
||||
4 2 {0 {0 -1 -1}}
|
||||
5 3 {0 {0 -1 -1}}
|
||||
6 4 {1 {SQLITE_MISUSE - not an error}}
|
||||
7 114 {1 {SQLITE_MISUSE - not an error}}
|
||||
8 1000000 {1 {SQLITE_MISUSE - not an error}}
|
||||
} {
|
||||
do_test 4.$tn {
|
||||
list [catch "wal_checkpoint_v2 db $mode" msg] $msg
|
||||
} $res
|
||||
}
|
||||
db close
|
||||
|
||||
foreach tn {1 2 3} {
|
||||
forcedelete test.db test.db2 test.db3
|
||||
testvfs tvfs
|
||||
|
||||
sqlite3 db test.db -vfs tvfs
|
||||
execsql {
|
||||
ATTACH 'test.db2' AS aux2;
|
||||
ATTACH 'test.db3' AS aux3;
|
||||
PRAGMA main.journal_mode = WAL;
|
||||
PRAGMA aux2.journal_mode = WAL;
|
||||
PRAGMA aux3.journal_mode = WAL;
|
||||
|
||||
CREATE TABLE main.t1(x,y);
|
||||
CREATE TABLE aux2.t2(x,y);
|
||||
CREATE TABLE aux3.t3(x,y);
|
||||
|
||||
INSERT INTO t1 VALUES('a', 'b');
|
||||
INSERT INTO t2 VALUES('a', 'b');
|
||||
INSERT INTO t3 VALUES('a', 'b');
|
||||
}
|
||||
sqlite3 db2 test.db2 -vfs tvfs
|
||||
|
||||
switch -- $tn {
|
||||
1 {
|
||||
# EVIDENCE-OF: R-41299-52117 If no error (SQLITE_BUSY or otherwise) is
|
||||
# encountered while processing the attached databases, SQLITE_OK is
|
||||
# returned.
|
||||
do_test 5.$tn.1 {
|
||||
lindex [wal_checkpoint_v2 db truncate] 0
|
||||
} {0} ;# 0 -> SQLITE_OK
|
||||
do_test 5.$tn.2 {
|
||||
list [expr [file size test.db-wal]==0] \
|
||||
[expr [file size test.db2-wal]==0] \
|
||||
[expr [file size test.db3-wal]==0]
|
||||
} {1 1 1}
|
||||
}
|
||||
|
||||
2 {
|
||||
# EVIDENCE-OF: R-38578-34175 If an SQLITE_BUSY error is encountered when
|
||||
# processing one or more of the attached WAL databases, the operation is
|
||||
# still attempted on any remaining attached databases and SQLITE_BUSY is
|
||||
# returned at the end.
|
||||
db2 eval { BEGIN; INSERT INTO t2 VALUES('d', 'e'); }
|
||||
do_test 5.$tn.1 {
|
||||
lindex [wal_checkpoint_v2 db truncate] 0
|
||||
} {1} ;# 1 -> SQLITE_BUSY
|
||||
do_test 5.$tn.2 {
|
||||
list [expr [file size test.db-wal]==0] \
|
||||
[expr [file size test.db2-wal]==0] \
|
||||
[expr [file size test.db3-wal]==0]
|
||||
} {1 0 1}
|
||||
db2 eval ROLLBACK
|
||||
}
|
||||
|
||||
3 {
|
||||
# EVIDENCE-OF: R-38049-07913 If any other error occurs while processing
|
||||
# an attached database, processing is abandoned and the error code is
|
||||
# returned to the caller immediately.
|
||||
tvfs filter xWrite
|
||||
tvfs script inject_ioerr
|
||||
proc inject_ioerr {method file args} {
|
||||
if {[file tail $file]=="test.db2"} {
|
||||
return "SQLITE_IOERR"
|
||||
}
|
||||
return 0
|
||||
}
|
||||
do_test 5.$tn.1 {
|
||||
list [catch { wal_checkpoint_v2 db truncate } msg] $msg
|
||||
} {1 {SQLITE_IOERR - disk I/O error}}
|
||||
do_test 5.$tn.2 {
|
||||
list [expr [file size test.db-wal]==0] \
|
||||
[expr [file size test.db2-wal]==0] \
|
||||
[expr [file size test.db3-wal]==0]
|
||||
} {1 0 0}
|
||||
tvfs script ""
|
||||
}
|
||||
}
|
||||
|
||||
db close
|
||||
db2 close
|
||||
}
|
||||
|
||||
reset_db
|
||||
sqlite3 db2 test.db
|
||||
|
||||
do_test 6.1 {
|
||||
execsql {
|
||||
PRAGMA auto_vacuum = 0;
|
||||
PRAGMA journal_mode = WAL;
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
}
|
||||
file size test.db-wal
|
||||
} [wal_file_size 3 1024]
|
||||
|
||||
do_test 6.2 {
|
||||
db2 eval { BEGIN; SELECT * FROM t1; }
|
||||
db eval { INSERT INTO t1 VALUES(3, 4) }
|
||||
file size test.db-wal
|
||||
} [wal_file_size 4 1024]
|
||||
|
||||
# At this point the log file contains 4 frames. 3 of which it should
|
||||
# be possible to checkpoint.
|
||||
#
|
||||
# EVIDENCE-OF: R-16642-42503 If pnLog is not NULL, then *pnLog is set to
|
||||
# the total number of frames in the log file or to -1 if the checkpoint
|
||||
# could not run because of an error or because the database is not in
|
||||
# WAL mode.
|
||||
#
|
||||
# EVIDENCE-OF: R-10514-25250 If pnCkpt is not NULL,then *pnCkpt is set
|
||||
# to the total number of checkpointed frames in the log file (including
|
||||
# any that were already checkpointed before the function was called) or
|
||||
# to -1 if the checkpoint could not run due to an error or because the
|
||||
# database is not in WAL mode.
|
||||
#
|
||||
do_test 6.4 {
|
||||
lrange [wal_checkpoint_v2 db passive] 1 2
|
||||
} {4 3}
|
||||
|
||||
# EVIDENCE-OF: R-37257-17813 Note that upon successful completion of an
|
||||
# SQLITE_CHECKPOINT_TRUNCATE, the log file will have been truncated to
|
||||
# zero bytes and so both *pnLog and *pnCkpt will be set to zero.
|
||||
#
|
||||
do_test 6.5 {
|
||||
db2 eval COMMIT
|
||||
wal_checkpoint_v2 db truncate
|
||||
} {0 0 0}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
# 2014 December 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/wal_common.tcl
|
||||
set testprefix e_walhook
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-00752-43975 The sqlite3_wal_hook() function is used to
|
||||
# register a callback that is invoked each time data is committed to a
|
||||
# database in wal mode.
|
||||
#
|
||||
# 1.1: shows that the wal-hook is not invoked in rollback mode.
|
||||
# 1.2: but is invoked in wal mode.
|
||||
#
|
||||
set ::wal_hook_count 0
|
||||
proc my_wal_hook {args} {
|
||||
incr ::wal_hook_count
|
||||
return 0
|
||||
}
|
||||
|
||||
do_test 1.1.1 {
|
||||
db wal_hook my_wal_hook
|
||||
execsql {
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(1);
|
||||
}
|
||||
set ::wal_hook_count
|
||||
} 0
|
||||
do_test 1.1.2 {
|
||||
execsql { PRAGMA journal_mode = wal }
|
||||
set ::wal_hook_count
|
||||
} 0
|
||||
|
||||
do_test 1.3 {
|
||||
execsql { INSERT INTO t1 VALUES(2) }
|
||||
set wal_hook_count
|
||||
} 1
|
||||
|
||||
do_test 1.4 {
|
||||
execsql {
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES(3);
|
||||
INSERT INTO t1 VALUES(4);
|
||||
COMMIT;
|
||||
}
|
||||
set wal_hook_count
|
||||
} 2
|
||||
|
||||
# EVIDENCE-OF: R-65366-15139 The callback is invoked by SQLite after the
|
||||
# commit has taken place and the associated write-lock on the database
|
||||
# released
|
||||
#
|
||||
set ::read_ok 0
|
||||
proc my_wal_hook {args} {
|
||||
sqlite3 db2 test.db
|
||||
if {[db2 eval { SELECT * FROM t1 }] == "1 2 3 4 5"} {
|
||||
set ::read_ok 1
|
||||
}
|
||||
db2 close
|
||||
}
|
||||
do_test 2.1 {
|
||||
execsql { INSERT INTO t1 VALUES(5) }
|
||||
set ::read_ok
|
||||
} 1
|
||||
|
||||
# EVIDENCE-OF: R-44294-52863 The third parameter is the name of the
|
||||
# database that was written to - either "main" or the name of an
|
||||
# ATTACH-ed database.
|
||||
#
|
||||
# EVIDENCE-OF: R-18913-19355 The fourth parameter is the number of pages
|
||||
# currently in the write-ahead log file, including those that were just
|
||||
# committed.
|
||||
#
|
||||
set ::wal_hook_args [list]
|
||||
proc my_wal_hook {dbname nEntry} {
|
||||
set ::wal_hook_args [list $dbname $nEntry]
|
||||
}
|
||||
forcedelete test.db2
|
||||
do_test 3.0 {
|
||||
execsql {
|
||||
ATTACH 'test.db2' AS aux;
|
||||
CREATE TABLE aux.t2(x);
|
||||
PRAGMA aux.journal_mode = wal;
|
||||
}
|
||||
} {wal}
|
||||
|
||||
# Database "aux"
|
||||
do_test 3.1.1 {
|
||||
set wal_hook_args [list]
|
||||
execsql { INSERT INTO t2 VALUES('a') }
|
||||
} {}
|
||||
do_test 3.1.2 {
|
||||
set wal_hook_args
|
||||
} [list aux [wal_frame_count test.db2-wal 1024]]
|
||||
|
||||
# Database "main"
|
||||
do_test 3.2.1 {
|
||||
set wal_hook_args [list]
|
||||
execsql { INSERT INTO t1 VALUES(6) }
|
||||
} {}
|
||||
do_test 3.1.2 {
|
||||
set wal_hook_args
|
||||
} [list main [wal_frame_count test.db-wal 1024]]
|
||||
|
||||
# EVIDENCE-OF: R-14034-00929 If an error code is returned, that error
|
||||
# will propagate back up through the SQLite code base to cause the
|
||||
# statement that provoked the callback to report an error, though the
|
||||
# commit will have still occurred.
|
||||
#
|
||||
proc my_wal_hook {args} { return 1 ;# SQLITE_ERROR }
|
||||
do_catchsql_test 4.1 {
|
||||
INSERT INTO t1 VALUES(7)
|
||||
} {1 {SQL logic error or missing database}}
|
||||
|
||||
proc my_wal_hook {args} { return 5 ;# SQLITE_BUSY }
|
||||
do_catchsql_test 4.2 {
|
||||
INSERT INTO t1 VALUES(8)
|
||||
} {1 {database is locked}}
|
||||
|
||||
proc my_wal_hook {args} { return 14 ;# SQLITE_CANTOPEN }
|
||||
do_catchsql_test 4.3 {
|
||||
INSERT INTO t1 VALUES(9)
|
||||
} {1 {unable to open database file}}
|
||||
|
||||
do_execsql_test 4.4 {
|
||||
SELECT * FROM t1
|
||||
} {1 2 3 4 5 6 7 8 9}
|
||||
|
||||
# EVIDENCE-OF: R-10466-53920 Calling sqlite3_wal_hook() replaces any
|
||||
# previously registered write-ahead log callback.
|
||||
set ::old_wal_hook 0
|
||||
proc my_old_wal_hook {args} {
|
||||
incr ::old_wal_hook
|
||||
return 0
|
||||
}
|
||||
db wal_hook my_old_wal_hook
|
||||
do_test 5.1 {
|
||||
execsql { INSERT INTO t1 VALUES(10) }
|
||||
set ::old_wal_hook
|
||||
} {1}
|
||||
|
||||
# Replace old_wal_hook. Observe that it is not invoked after it has
|
||||
# been replaced.
|
||||
proc my_new_wal_hook {args} { return 0 }
|
||||
db wal_hook my_new_wal_hook
|
||||
do_test 5.2 {
|
||||
execsql { INSERT INTO t1 VALUES(11) }
|
||||
set ::old_wal_hook
|
||||
} {1}
|
||||
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-42842-27162 Note that the sqlite3_wal_autocheckpoint()
|
||||
# interface and the wal_autocheckpoint pragma both invoke
|
||||
# sqlite3_wal_hook() and will those overwrite any prior
|
||||
# sqlite3_wal_hook() settings.
|
||||
#
|
||||
set ::old_wal_hook 0
|
||||
proc my_old_wal_hook {args} { incr ::old_wal_hook ; return 0 }
|
||||
db wal_hook my_old_wal_hook
|
||||
do_test 6.1.1 {
|
||||
execsql { INSERT INTO t1 VALUES(12) }
|
||||
set ::old_wal_hook
|
||||
} {1}
|
||||
do_test 6.1.2 {
|
||||
execsql { PRAGMA wal_autocheckpoint = 1000 }
|
||||
execsql { INSERT INTO t1 VALUES(12) }
|
||||
set ::old_wal_hook
|
||||
} {1}
|
||||
|
||||
# EVIDENCE-OF: R-52629-38967 The first parameter passed to the callback
|
||||
# function when it is invoked is a copy of the third parameter passed to
|
||||
# sqlite3_wal_hook() when registering the callback.
|
||||
#
|
||||
# This is tricky to test using the tcl interface. However, the
|
||||
# mechanism used to invoke the tcl script registered as a wal-hook
|
||||
# depends on the context pointer being correctly passed through. And
|
||||
# since multiple different wal-hook scripts have been successfully
|
||||
# invoked by this test script, consider this tested.
|
||||
#
|
||||
# EVIDENCE-OF: R-23378-42536 The second is a copy of the database
|
||||
# handle.
|
||||
#
|
||||
# There is an assert() in the C wal-hook used by tclsqlite.c to
|
||||
# prove this. And that hook has been invoked multiple times when
|
||||
# running this script. So consider this requirement tested as well.
|
||||
#
|
||||
|
||||
finish_test
|
||||
+1
-5
@@ -252,17 +252,13 @@ do_test fkey5-6.5 {
|
||||
} {c12 1 p4 0 c12 3 p4 0 c12 6 p4 0}
|
||||
|
||||
do_test fkey5-7.1 {
|
||||
set res {}
|
||||
db eval {
|
||||
INSERT OR IGNORE INTO c13 SELECT * FROM c12;
|
||||
INSERT OR IGNORE INTO C14 SELECT * FROM c12;
|
||||
DELETE FROM c12;
|
||||
PRAGMA foreign_key_check;
|
||||
} {
|
||||
lappend res [list $table $rowid $fkid $parent]
|
||||
}
|
||||
lsort $res
|
||||
} {{c13 1 0 p3} {c13 2 0 p3} {c13 3 0 p3} {c13 4 0 p3} {c13 5 0 p3} {c13 6 0 p3} {c14 1 0 p4} {c14 3 0 p4} {c14 6 0 p4}}
|
||||
} {c14 1 p4 0 c14 3 p4 0 c14 6 p4 0 c13 1 p3 0 c13 2 p3 0 c13 3 p3 0 c13 4 p3 0 c13 5 p3 0 c13 6 p3 0}
|
||||
do_test fkey5-7.2 {
|
||||
db eval {
|
||||
PRAGMA foreign_key_check(c14);
|
||||
|
||||
-106
@@ -1,106 +0,0 @@
|
||||
# 2001 September 15
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library.
|
||||
#
|
||||
# This file implements tests for foreign keys.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix fkey8
|
||||
|
||||
ifcapable {!foreignkey} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
do_execsql_test 1.0 { PRAGMA foreign_keys = 1; }
|
||||
|
||||
|
||||
foreach {tn use_stmt sql schema} {
|
||||
1 1 "DELETE FROM p1" {
|
||||
CREATE TABLE p1(a PRIMARY KEY);
|
||||
CREATE TABLE c1(b REFERENCES p1);
|
||||
}
|
||||
|
||||
2.1 0 "DELETE FROM p1" {
|
||||
CREATE TABLE p1(a PRIMARY KEY);
|
||||
CREATE TABLE c1(b REFERENCES p1 ON DELETE CASCADE);
|
||||
}
|
||||
2.2 0 "DELETE FROM p1" {
|
||||
CREATE TABLE p1(a PRIMARY KEY);
|
||||
CREATE TABLE c1(b REFERENCES p1 ON DELETE SET NULL);
|
||||
}
|
||||
2.3 1 "DELETE FROM p1" {
|
||||
CREATE TABLE p1(a PRIMARY KEY);
|
||||
CREATE TABLE c1(b REFERENCES p1 ON DELETE SET DEFAULT);
|
||||
}
|
||||
|
||||
3 1 "DELETE FROM p1" {
|
||||
CREATE TABLE p1(a PRIMARY KEY);
|
||||
CREATE TABLE c1(b REFERENCES p1 ON DELETE CASCADE);
|
||||
CREATE TRIGGER ct1 AFTER DELETE ON c1 BEGIN
|
||||
INSERT INTO p1 VALUES('x');
|
||||
END;
|
||||
}
|
||||
|
||||
4 1 "DELETE FROM p1" {
|
||||
CREATE TABLE p1(a PRIMARY KEY);
|
||||
CREATE TABLE c1(b REFERENCES p1 ON DELETE CASCADE, c PRIMARY KEY);
|
||||
CREATE TABLE cc1(d REFERENCES c1);
|
||||
}
|
||||
|
||||
5.1 0 "DELETE FROM p1" {
|
||||
CREATE TABLE p1(a PRIMARY KEY);
|
||||
CREATE TABLE c1(b REFERENCES p1 ON DELETE CASCADE, c PRIMARY KEY);
|
||||
CREATE TABLE cc1(d REFERENCES c1 ON DELETE CASCADE);
|
||||
}
|
||||
5.2 0 "DELETE FROM p1" {
|
||||
CREATE TABLE p1(a PRIMARY KEY);
|
||||
CREATE TABLE c1(b REFERENCES p1 ON DELETE CASCADE, c PRIMARY KEY);
|
||||
CREATE TABLE cc1(d REFERENCES c1 ON DELETE SET NULL);
|
||||
}
|
||||
5.3 1 "DELETE FROM p1" {
|
||||
CREATE TABLE p1(a PRIMARY KEY);
|
||||
CREATE TABLE c1(b REFERENCES p1 ON DELETE CASCADE, c PRIMARY KEY);
|
||||
CREATE TABLE cc1(d REFERENCES c1 ON DELETE SET DEFAULT);
|
||||
}
|
||||
|
||||
6.1 1 "UPDATE p1 SET a = ?" {
|
||||
CREATE TABLE p1(a PRIMARY KEY);
|
||||
CREATE TABLE c1(b REFERENCES p1 ON UPDATE SET NULL, c);
|
||||
}
|
||||
6.2 0 "UPDATE OR IGNORE p1 SET a = ?" {
|
||||
CREATE TABLE p1(a PRIMARY KEY);
|
||||
CREATE TABLE c1(b REFERENCES p1 ON UPDATE SET NULL, c);
|
||||
}
|
||||
6.3 1 "UPDATE OR IGNORE p1 SET a = ?" {
|
||||
CREATE TABLE p1(a PRIMARY KEY);
|
||||
CREATE TABLE c1(b REFERENCES p1 ON UPDATE CASCADE, c);
|
||||
}
|
||||
6.4 1 "UPDATE OR IGNORE p1 SET a = ?" {
|
||||
CREATE TABLE p1(a PRIMARY KEY);
|
||||
CREATE TABLE c1(b NOT NULL REFERENCES p1 ON UPDATE SET NULL, c);
|
||||
}
|
||||
|
||||
} {
|
||||
drop_all_tables
|
||||
do_test 1.$tn {
|
||||
execsql $schema
|
||||
set stmt [sqlite3_prepare_v2 db $sql -1 dummy]
|
||||
set ret [uses_stmt_journal $stmt]
|
||||
sqlite3_finalize $stmt
|
||||
set ret
|
||||
} $use_stmt
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -176,20 +176,20 @@ do_test memsubsys1-4.6 {
|
||||
set s_used [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_USED 0] 2]
|
||||
} 1
|
||||
|
||||
# Test 5: Activate both PAGECACHE and SCRATCH. But make the page size is
|
||||
# Test 5: Activate both PAGECACHE and SCRATCH. But make the page size
|
||||
# such that the SCRATCH allocations are too small.
|
||||
#
|
||||
db close
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_pagecache [expr 4096+$xtra_size] 24
|
||||
sqlite3_config_scratch 4000 2
|
||||
sqlite3_config_scratch 6000 2
|
||||
sqlite3_initialize
|
||||
reset_highwater_marks
|
||||
build_test_db memsubsys1-5 {PRAGMA page_size=4096}
|
||||
#show_memstats
|
||||
do_test memsubsys1-5.3 {
|
||||
set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2]
|
||||
} {/^2[34]$/}
|
||||
} 24
|
||||
do_test memsubsys1-5.4 {
|
||||
set maxreq [lindex [sqlite3_status SQLITE_STATUS_MALLOC_SIZE 0] 2]
|
||||
expr {$maxreq>4096}
|
||||
@@ -215,7 +215,7 @@ build_test_db memsubsys1-6 {PRAGMA page_size=4096}
|
||||
#show_memstats
|
||||
do_test memsubsys1-6.3 {
|
||||
set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2]
|
||||
} {/^2[34]$/}
|
||||
} 24
|
||||
#do_test memsubsys1-6.4 {
|
||||
# set maxreq [lindex [sqlite3_status SQLITE_STATUS_MALLOC_SIZE 0] 2]
|
||||
# expr {$maxreq>4096 && $maxreq<=(4096+$xtra_size)}
|
||||
|
||||
+8
-33
@@ -101,8 +101,6 @@ set sql $zSql
|
||||
# This loop runs for ~20 seconds.
|
||||
#
|
||||
set iStart [clock_seconds]
|
||||
set nOp 0
|
||||
set nAttempt 0
|
||||
while { ([clock_seconds]-$iStart) < $nSecond } {
|
||||
|
||||
# Each transaction does 3 operations. Each operation is either a read
|
||||
@@ -130,7 +128,6 @@ set sql $zSql
|
||||
|
||||
# Execute the SQL transaction.
|
||||
#
|
||||
incr nAttempt
|
||||
set rc [catch { execsql_blocking $::DB "
|
||||
BEGIN;
|
||||
$SQL(1);
|
||||
@@ -157,14 +154,13 @@ set sql $zSql
|
||||
# returned "1". Otherwise, the invariant was false, indicating that
|
||||
# some malfunction has occurred.
|
||||
foreach r $msg { if {$r != 1} { puts "Invariant check failed: $msg" } }
|
||||
incr nOp
|
||||
}
|
||||
}
|
||||
|
||||
# Close the database connection and return 0.
|
||||
#
|
||||
sqlite3_close $::DB
|
||||
list $nOp $nAttempt
|
||||
expr 0
|
||||
}
|
||||
|
||||
foreach {iTest xStep xPrepare} {
|
||||
@@ -208,9 +204,7 @@ foreach {iTest xStep xPrepare} {
|
||||
for {set ii 0} {$ii < $nThread} {incr ii} {
|
||||
do_test notify2-$iTest.2.$ii {
|
||||
if {![info exists finished($ii)]} { vwait finished($ii) }
|
||||
incr anSuccess($xStep) [lindex $finished($ii) 0]
|
||||
incr anAttempt($xStep) [lindex $finished($ii) 1]
|
||||
expr 0
|
||||
set finished($ii)
|
||||
} {0}
|
||||
}
|
||||
|
||||
@@ -231,36 +225,17 @@ foreach {iTest xStep xPrepare} {
|
||||
}
|
||||
|
||||
# The following tests checks to make sure sqlite3_blocking_step() is
|
||||
# faster than sqlite3_step(). "Faster" in this case means uses fewer
|
||||
# CPU cycles. This is not always the same as faster in wall-clock time
|
||||
# for this type of test. The number of CPU cycles per transaction is
|
||||
# roughly proportional to the number of attempts made (i.e. one plus the
|
||||
# number of SQLITE_BUSY or SQLITE_LOCKED errors that require the transaction
|
||||
# to be retried). So this test just measures that a greater percentage of
|
||||
# transactions attempted using blocking_step() succeed.
|
||||
#
|
||||
# The blocking_step() function is almost always faster on multi-core and is
|
||||
# usually faster on single-core. But sometimes, by chance, step() will be
|
||||
# faster on a single core, in which case the
|
||||
# faster than sqlite3_step(). blocking_step() is always faster on
|
||||
# multi-core and is usually faster on single-core. But sometimes, by
|
||||
# chance, step() will be faster on a single core, in which case the
|
||||
# following test will fail.
|
||||
#
|
||||
puts "The following test seeks to demonstrate that the sqlite3_unlock_notify()"
|
||||
puts "interface helps multi-core systems to run more efficiently. This test"
|
||||
puts "sometimes fails on single-core machines."
|
||||
puts "interface helps multi-core systems to run faster. This test sometimes"
|
||||
puts "fails on single-core machines."
|
||||
puts [array get anWrite]
|
||||
do_test notify2-3 {
|
||||
set blocking [expr {
|
||||
double($anSuccess(sqlite3_blocking_step)) /
|
||||
double($anAttempt(sqlite3_blocking_step))
|
||||
}]
|
||||
set non [expr {
|
||||
double($anSuccess(sqlite3_step)) /
|
||||
double($anAttempt(sqlite3_step))
|
||||
}]
|
||||
puts -nonewline [format " blocking: %.1f%% non-blocking %.1f%% ..." \
|
||||
[expr $blocking*100.0] [expr $non*100.0]]
|
||||
|
||||
expr {$blocking > $non}
|
||||
expr {$anWrite(sqlite3_blocking_step) > $anWrite(sqlite3_step)}
|
||||
} {1}
|
||||
|
||||
sqlite3_enable_shared_cache $::enable_shared_cache
|
||||
|
||||
@@ -200,7 +200,7 @@ ifcapable vtab {
|
||||
} {
|
||||
do_test percentile-2.1.$in {
|
||||
execsql {
|
||||
SELECT round(percentile(x, $in),1) from t3;
|
||||
SELECT percentile(x, $in) from t3;
|
||||
}
|
||||
} $out
|
||||
}
|
||||
|
||||
@@ -118,11 +118,6 @@ set allquicktests [test_set $alltests -exclude {
|
||||
if {[info exists ::env(QUICKTEST_INCLUDE)]} {
|
||||
set allquicktests [concat $allquicktests $::env(QUICKTEST_INCLUDE)]
|
||||
}
|
||||
if {[info exists ::env(QUICKTEST_OMIT)]} {
|
||||
foreach x [split $::env(QUICKTEST_OMIT) ,] {
|
||||
regsub -all \\y$x\\y $allquicktests {} allquicktests
|
||||
}
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# Start of tests
|
||||
@@ -146,7 +141,7 @@ test_suite "veryquick" -prefix "" -description {
|
||||
]
|
||||
|
||||
test_suite "mmap" -prefix "mm-" -description {
|
||||
Similar to veryquick. Except with memory mapping enabled.
|
||||
Similar to veryquick. Except with memory mapping disabled.
|
||||
} -presql {
|
||||
pragma mmap_size = 268435456;
|
||||
} -files [
|
||||
@@ -695,7 +690,7 @@ test_suite "inmemory_journal" -description {
|
||||
zerodamage.test
|
||||
|
||||
# WAL mode is different.
|
||||
wal* tkt-2d1a5c67d.test backcompat.test e_wal*
|
||||
wal* tkt-2d1a5c67d.test backcompat.test
|
||||
}]
|
||||
|
||||
ifcapable mem3 {
|
||||
|
||||
+1
-25
@@ -454,34 +454,10 @@ do_execsql_test pragma-3.21 {
|
||||
do_execsql_test pragma-3.22 {
|
||||
PRAGMA integrity_check(2);
|
||||
} {{non-unique entry in index t1a} {NULL value in t1x.a}}
|
||||
do_execsql_test pragma-3.23 {
|
||||
do_execsql_test pragma-3.21 {
|
||||
PRAGMA integrity_check(1);
|
||||
} {{non-unique entry in index t1a}}
|
||||
|
||||
# PRAGMA integrity check (or more specifically the sqlite3BtreeCount()
|
||||
# interface) used to leave index cursors in an inconsistent state
|
||||
# which could result in an assertion fault in sqlite3BtreeKey()
|
||||
# called from saveCursorPosition() if content is removed from the
|
||||
# index while the integrity_check is still running. This test verifies
|
||||
# that problem has been fixed.
|
||||
#
|
||||
do_test pragma-3.30 {
|
||||
db close
|
||||
delete_file test.db
|
||||
sqlite3 db test.db
|
||||
db eval {
|
||||
CREATE TABLE t1(a,b,c);
|
||||
WITH RECURSIVE
|
||||
c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<100)
|
||||
INSERT INTO t1(a,b,c) SELECT i, printf('xyz%08x',i), 2000-i FROM c;
|
||||
CREATE INDEX t1a ON t1(a);
|
||||
CREATE INDEX t1bc ON t1(b,c);
|
||||
}
|
||||
db eval {PRAGMA integrity_check} {
|
||||
db eval {DELETE FROM t1}
|
||||
}
|
||||
} {}
|
||||
|
||||
# Test modifying the cache_size of an attached database.
|
||||
ifcapable pager_pragmas&&attach {
|
||||
do_test pragma-4.1 {
|
||||
|
||||
@@ -1,253 +0,0 @@
|
||||
# 2014-12-19
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library.
|
||||
#
|
||||
# This file implements tests for PRAGMA data_version command.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
do_execsql_test pragma3-100 {
|
||||
PRAGMA data_version;
|
||||
} {1}
|
||||
do_execsql_test pragma3-101 {
|
||||
PRAGMA temp.data_version;
|
||||
} {1}
|
||||
|
||||
# Writing to the pragma is a no-op
|
||||
do_execsql_test pragma3-102 {
|
||||
PRAGMA main.data_version=1234;
|
||||
PRAGMA main.data_version;
|
||||
} {1 1}
|
||||
|
||||
# EVIDENCE-OF: R-27726-60934 The "PRAGMA data_version" command provides
|
||||
# an indication that the database file has been modified.
|
||||
#
|
||||
# EVIDENCE-OF: R-47505-58569 The "PRAGMA data_version" value is
|
||||
# unchanged for commits made on the same database connection.
|
||||
#
|
||||
do_execsql_test pragma3-110 {
|
||||
PRAGMA data_version;
|
||||
BEGIN IMMEDIATE;
|
||||
PRAGMA data_version;
|
||||
CREATE TABLE t1(a);
|
||||
INSERT INTO t1 VALUES(100),(200),(300);
|
||||
PRAGMA data_version;
|
||||
COMMIT;
|
||||
SELECT * FROM t1;
|
||||
PRAGMA data_version;
|
||||
} {1 1 1 100 200 300 1}
|
||||
|
||||
sqlite3 db2 test.db
|
||||
do_test pragma3-120 {
|
||||
db2 eval {
|
||||
SELECT * FROM t1;
|
||||
PRAGMA data_version;
|
||||
}
|
||||
} {100 200 300 1}
|
||||
|
||||
do_execsql_test pragma3-130 {
|
||||
PRAGMA data_version;
|
||||
BEGIN IMMEDIATE;
|
||||
PRAGMA data_version;
|
||||
INSERT INTO t1 VALUES(400),(500);
|
||||
PRAGMA data_version;
|
||||
COMMIT;
|
||||
SELECT * FROM t1;
|
||||
PRAGMA data_version;
|
||||
PRAGMA shrink_memory;
|
||||
} {1 1 1 100 200 300 400 500 1}
|
||||
|
||||
# EVIDENCE-OF: R-63005-41812 The integer values returned by two
|
||||
# invocations of "PRAGMA data_version" from the same connection will be
|
||||
# different if changes were committed to the database by any other
|
||||
# connection in the interim.
|
||||
#
|
||||
# Value went from 1 in pragma3-120 to 2 here.
|
||||
#
|
||||
do_test pragma3-140 {
|
||||
db2 eval {
|
||||
SELECT * FROM t1;
|
||||
PRAGMA data_version;
|
||||
BEGIN IMMEDIATE;
|
||||
PRAGMA data_version;
|
||||
UPDATE t1 SET a=a+1;
|
||||
COMMIT;
|
||||
SELECT * FROM t1;
|
||||
PRAGMA data_version;
|
||||
}
|
||||
} {100 200 300 400 500 2 2 101 201 301 401 501 2}
|
||||
do_execsql_test pragma3-150 {
|
||||
SELECT * FROM t1;
|
||||
PRAGMA data_version;
|
||||
} {101 201 301 401 501 2}
|
||||
|
||||
#
|
||||
do_test pragma3-160 {
|
||||
db eval {
|
||||
BEGIN;
|
||||
PRAGMA data_version;
|
||||
UPDATE t1 SET a=555 WHERE a=501;
|
||||
PRAGMA data_version;
|
||||
SELECT * FROM t1 ORDER BY a;
|
||||
PRAGMA data_version;
|
||||
}
|
||||
} {2 2 101 201 301 401 555 2}
|
||||
do_test pragma3-170 {
|
||||
db2 eval {
|
||||
PRAGMA data_version;
|
||||
}
|
||||
} {2}
|
||||
do_test pragma3-180 {
|
||||
db eval {
|
||||
COMMIT;
|
||||
PRAGMA data_version;
|
||||
}
|
||||
} {2}
|
||||
do_test pragma3-190 {
|
||||
db2 eval {
|
||||
PRAGMA data_version;
|
||||
}
|
||||
} {3}
|
||||
|
||||
# EVIDENCE-OF: R-19326-44825 The "PRAGMA data_version" value is a local
|
||||
# property of each database connection and so values returned by two
|
||||
# concurrent invocations of "PRAGMA data_version" on separate database
|
||||
# connections are often different even though the underlying database is
|
||||
# identical.
|
||||
#
|
||||
do_test pragma3-195 {
|
||||
expr {[db eval {PRAGMA data_version}]!=[db2 eval {PRAGMA data_version}]}
|
||||
} {1}
|
||||
|
||||
# EVIDENCE-OF: R-54562-06892 The behavior of "PRAGMA data_version" is
|
||||
# the same for all database connections, including database connections
|
||||
# in separate processes and shared cache database connections.
|
||||
#
|
||||
# The next block checks the behavior for separate processes.
|
||||
#
|
||||
do_test pragma3-200 {
|
||||
db eval {PRAGMA data_version; SELECT * FROM t1;}
|
||||
} {2 101 201 301 401 555}
|
||||
do_test pragma3-201 {
|
||||
set fd [open pragma3.txt wb]
|
||||
puts $fd {
|
||||
sqlite3 db test.db;
|
||||
db eval {DELETE FROM t1 WHERE a>300};
|
||||
db close;
|
||||
exit;
|
||||
}
|
||||
close $fd
|
||||
exec [info nameofexec] pragma3.txt
|
||||
forcedelete pragma3.txt
|
||||
db eval {
|
||||
PRAGMA data_version;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {3 101 201}
|
||||
db2 close
|
||||
db close
|
||||
|
||||
# EVIDENCE-OF: R-54562-06892 The behavior of "PRAGMA data_version" is
|
||||
# the same for all database connections, including database connections
|
||||
# in separate processes and shared cache database connections.
|
||||
#
|
||||
# The next block checks that behavior is the same for shared-cache.
|
||||
#
|
||||
ifcapable shared_cache {
|
||||
set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
|
||||
sqlite3 db test.db
|
||||
sqlite3 db2 test.db
|
||||
do_test pragma3-300 {
|
||||
db eval {
|
||||
PRAGMA data_version;
|
||||
BEGIN;
|
||||
CREATE TABLE t3(a,b,c);
|
||||
CREATE TABLE t4(x,y,z);
|
||||
INSERT INTO t4 VALUES(123,456,789);
|
||||
PRAGMA data_version;
|
||||
COMMIT;
|
||||
PRAGMA data_version;
|
||||
}
|
||||
} {1 1 1}
|
||||
do_test pragma3-310 {
|
||||
db2 eval {
|
||||
PRAGMA data_version;
|
||||
BEGIN;
|
||||
INSERT INTO t3(a,b,c) VALUES('abc','def','ghi');
|
||||
SELECT * FROM t3;
|
||||
PRAGMA data_version;
|
||||
}
|
||||
} {2 abc def ghi 2}
|
||||
# The transaction in db2 has not yet committed, so the data_version in
|
||||
# db is unchanged.
|
||||
do_test pragma3-320 {
|
||||
db eval {
|
||||
PRAGMA data_version;
|
||||
SELECT * FROM t4;
|
||||
}
|
||||
} {1 123 456 789}
|
||||
do_test pragma3-330 {
|
||||
db2 eval {
|
||||
COMMIT;
|
||||
PRAGMA data_version;
|
||||
SELECT * FROM t4;
|
||||
}
|
||||
} {2 123 456 789}
|
||||
do_test pragma3-340 {
|
||||
db eval {
|
||||
PRAGMA data_version;
|
||||
SELECT * FROM t3;
|
||||
SELECT * FROM t4;
|
||||
}
|
||||
} {2 abc def ghi 123 456 789}
|
||||
db2 close
|
||||
db close
|
||||
sqlite3_enable_shared_cache $::enable_shared_cache
|
||||
}
|
||||
|
||||
# Make sure this also works in WAL mode
|
||||
#
|
||||
# This will not work with the in-memory journal permutation, as opening
|
||||
# [db2] switches the journal mode back to "memory"
|
||||
#
|
||||
ifcapable wal {
|
||||
if {[permutation]!="inmemory_journal"} {
|
||||
|
||||
sqlite3 db test.db
|
||||
db eval {PRAGMA journal_mode=WAL}
|
||||
sqlite3 db2 test.db
|
||||
do_test pragma3-400 {
|
||||
db eval {
|
||||
PRAGMA data_version;
|
||||
PRAGMA journal_mode;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {2 wal 101 201}
|
||||
do_test pragma3-410 {
|
||||
db2 eval {
|
||||
PRAGMA data_version;
|
||||
PRAGMA journal_mode;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {2 wal 101 201}
|
||||
do_test pragma3-420 {
|
||||
db eval {UPDATE t1 SET a=111*(a/100); PRAGMA data_version; SELECT * FROM t1}
|
||||
} {2 111 222}
|
||||
do_test pragma3-430 {
|
||||
db2 eval {PRAGMA data_version; SELECT * FROM t1;}
|
||||
} {3 111 222}
|
||||
db2 close
|
||||
}
|
||||
}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,14 @@
|
||||
########################################################
|
||||
TOP=/home/drh/sqlite/sqlite
|
||||
|
||||
TCL_FLAGS=-I/home/drh/tcltk/86linux
|
||||
LIBTCL=/home/drh/tcltk/86linux/libtcl8.6.a -lm -ldl -lpthread
|
||||
|
||||
BCC = gcc
|
||||
TCC = gcc -ansi -g $(CFLAGS)
|
||||
NAWK = awk
|
||||
AR = ar cr
|
||||
RANLIB = ranlib
|
||||
THREADLIB = -lpthread -ldl -lz
|
||||
include $(TOP)/main.mk
|
||||
########################################################
|
||||
+92
-212
@@ -1,31 +1,60 @@
|
||||
#!/usr/bin/tclsh
|
||||
#
|
||||
|
||||
set rcsid {$Id: $}
|
||||
|
||||
# Documentation for this script. This may be output to stderr
|
||||
# if the script is invoked incorrectly. See the [process_options]
|
||||
# proc below.
|
||||
#
|
||||
set ::USAGE_MESSAGE {
|
||||
This Tcl script is used to test the various configurations required
|
||||
before releasing a new version. Supported command line options (all
|
||||
before releasing a new version. Supported command line options (all
|
||||
optional) are:
|
||||
|
||||
--srcdir TOP-OF-SQLITE-TREE (see below)
|
||||
--platform PLATFORM (see below)
|
||||
--config CONFIGNAME (Run only CONFIGNAME)
|
||||
--quick (Run "veryquick.test" only)
|
||||
--buildonly (Just build testfixture - do not run)
|
||||
--dryrun (Print what would have happened)
|
||||
--info (Show diagnostic info)
|
||||
-makefile PATH-TO-MAKEFILE (default "releasetest.mk")
|
||||
-platform PLATFORM (see below)
|
||||
-quick BOOLEAN (default "0")
|
||||
-config CONFIGNAME (Run only CONFIGNAME)
|
||||
|
||||
The default value for --srcdir is the parent of the directory holding
|
||||
this script.
|
||||
The default value for -makefile is "./releasetest.mk".
|
||||
|
||||
The script determines the default value for --platform using the
|
||||
$tcl_platform(os) and $tcl_platform(machine) variables. Supported
|
||||
The script determines the default value for -platform using the
|
||||
$tcl_platform(os) and $tcl_platform(machine) variables. Supported
|
||||
platforms are "Linux-x86", "Linux-x86_64" and "Darwin-i386".
|
||||
|
||||
Every test begins with a fresh run of the configure script at the top
|
||||
of the SQLite source tree.
|
||||
If the -quick option is set to true, then the "veryquick.test" script
|
||||
is run for all compilation configurations. Otherwise, sometimes "all.test"
|
||||
is run, sometimes "veryquick.test".
|
||||
|
||||
Almost any SQLite makefile (except those generated by configure - see below)
|
||||
should work. The following properties are required:
|
||||
|
||||
* The makefile should support the "fulltest" target.
|
||||
* The makefile should support the variable "OPTS" as a way to pass
|
||||
options from the make command line to lemon and the C compiler.
|
||||
|
||||
More precisely, the following invocation must be supported:
|
||||
|
||||
make -f $::MAKEFILE fulltest OPTS="-DSQLITE_SECURE_DELETE=1 -DSQLITE_DEBUG=1"
|
||||
|
||||
Makefiles generated by the sqlite configure program cannot be used as
|
||||
they do not respect the OPTS variable.
|
||||
|
||||
Example Makefile contents:
|
||||
|
||||
########################################################
|
||||
TOP=/home/dan/work/sqlite/sqlite
|
||||
|
||||
TCL_FLAGS=-I/home/dan/tcl/include
|
||||
LIBTCL=-L/home/dan/tcl/lib -ltcl
|
||||
|
||||
BCC = gcc
|
||||
TCC = gcc -ansi -g $(CFLAGS)
|
||||
NAWK = awk
|
||||
AR = ar cr
|
||||
RANLIB = ranlib
|
||||
THREADLIB = -lpthread -ldl
|
||||
include $(TOP)/main.mk
|
||||
########################################################
|
||||
}
|
||||
|
||||
array set ::Configs {
|
||||
@@ -34,13 +63,9 @@ array set ::Configs {
|
||||
}
|
||||
"Ftrapv" {
|
||||
-O2 -ftrapv
|
||||
-DSQLITE_MAX_ATTACHED=125
|
||||
-DSQLITE_MAX_ATTACHED=55
|
||||
-DSQLITE_TCL_DEFAULT_FULLMUTEX=1
|
||||
}
|
||||
"Sanitize" {
|
||||
CC=clang -fsanitize=undefined
|
||||
-DSQLITE_ENABLE_STAT4
|
||||
}
|
||||
"Unlock-Notify" {
|
||||
-O2
|
||||
-DSQLITE_ENABLE_UNLOCK_NOTIFY
|
||||
@@ -69,9 +94,9 @@ array set ::Configs {
|
||||
-DSQLITE_SECURE_DELETE=1
|
||||
-DSQLITE_SOUNDEX=1
|
||||
-DSQLITE_ENABLE_ATOMIC_WRITE=1
|
||||
-DSQLITE_ENABLE_IOTRACE=1
|
||||
-DSQLITE_ENABLE_MEMORY_MANAGEMENT=1
|
||||
-DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1
|
||||
-DSQLITE_ENABLE_STAT4
|
||||
}
|
||||
"Debug-One" {
|
||||
-O2
|
||||
@@ -84,7 +109,6 @@ array set ::Configs {
|
||||
-DSQLITE_ENABLE_MEMSYS5=1
|
||||
-DSQLITE_ENABLE_MEMSYS3=1
|
||||
-DSQLITE_ENABLE_COLUMN_METADATA=1
|
||||
-DSQLITE_ENABLE_STAT4
|
||||
}
|
||||
"Device-One" {
|
||||
-O2
|
||||
@@ -125,7 +149,6 @@ array set ::Configs {
|
||||
-DSQLITE_ENABLE_LOCKING_STYLE=1
|
||||
}
|
||||
"OS-X" {
|
||||
-O1 # Avoid a compiler bug in gcc 4.2.1 build 5658
|
||||
-DSQLITE_OMIT_LOAD_EXTENSION=1
|
||||
-DSQLITE_DEFAULT_MEMSTATUS=0
|
||||
-DSQLITE_THREADSAFE=2
|
||||
@@ -138,9 +161,8 @@ array set ::Configs {
|
||||
-DSQLITE_DEFAULT_CACHE_SIZE=1000
|
||||
-DSQLITE_MAX_LENGTH=2147483645
|
||||
-DSQLITE_MAX_VARIABLE_NUMBER=500000
|
||||
-DSQLITE_DEBUG=1
|
||||
-DSQLITE_DEBUG=1
|
||||
-DSQLITE_PREFER_PROXY_LOCKING=1
|
||||
-DSQLITE_ENABLE_API_ARMOR=1
|
||||
}
|
||||
"Extra-Robustness" {
|
||||
-DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1
|
||||
@@ -166,14 +188,13 @@ array set ::Configs {
|
||||
array set ::Platforms {
|
||||
Linux-x86_64 {
|
||||
"Check-Symbols" checksymbols
|
||||
"Debug-One" "mptest test"
|
||||
"Debug-One" test
|
||||
"Secure-Delete" test
|
||||
"Unlock-Notify" "QUICKTEST_INCLUDE=notify2.test test"
|
||||
"Update-Delete-Limit" test
|
||||
"Extra-Robustness" test
|
||||
"Device-Two" test
|
||||
"Ftrapv" test
|
||||
"Sanitize" {QUICKTEST_OMIT=func4.test,nan.test test}
|
||||
"No-lookaside" test
|
||||
"Devkit" test
|
||||
"Default" "threadtest fulltest"
|
||||
@@ -187,16 +208,9 @@ array set ::Platforms {
|
||||
"Default" "threadtest fulltest"
|
||||
}
|
||||
Darwin-i386 {
|
||||
"Locking-Style" "mptest test"
|
||||
"Locking-Style" test
|
||||
"OS-X" "threadtest fulltest"
|
||||
}
|
||||
Darwin-x86_64 {
|
||||
"Locking-Style" "mptest test"
|
||||
"OS-X" "threadtest fulltest"
|
||||
}
|
||||
"Windows NT-intel" {
|
||||
"Default" "mptest fulltestonly"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -213,58 +227,18 @@ foreach {key value} [array get ::Platforms] {
|
||||
}
|
||||
}
|
||||
|
||||
# Open the file $logfile and look for a report on the number of errors
|
||||
# and the number of test cases run. Add these values to the global
|
||||
# $::NERRCASE and $::NTESTCASE variables.
|
||||
#
|
||||
# If any errors occur, then write into $errmsgVar the text of an appropriate
|
||||
# one-line error message to show on the output.
|
||||
#
|
||||
proc count_tests_and_errors {logfile rcVar errmsgVar} {
|
||||
if {$::DRYRUN} return
|
||||
upvar 1 $rcVar rc $errmsgVar errmsg
|
||||
set fd [open $logfile rb]
|
||||
set seen 0
|
||||
while {![eof $fd]} {
|
||||
set line [gets $fd]
|
||||
if {[regexp {(\d+) errors out of (\d+) tests} $line all nerr ntest]} {
|
||||
incr ::NERRCASE $nerr
|
||||
incr ::NTESTCASE $ntest
|
||||
set seen 1
|
||||
if {$nerr>0} {
|
||||
set rc 1
|
||||
set errmsg $line
|
||||
}
|
||||
}
|
||||
if {[regexp {runtime error: +(.*)} $line all msg]} {
|
||||
incr ::NERRCASE
|
||||
if {$rc==0} {
|
||||
set rc 1
|
||||
set errmsg $msg
|
||||
}
|
||||
}
|
||||
}
|
||||
close $fd
|
||||
if {!$seen} {
|
||||
set rc 1
|
||||
set errmsg "Test did not complete"
|
||||
}
|
||||
}
|
||||
|
||||
proc run_test_suite {name testtarget config} {
|
||||
# Tcl variable $opts is used to build up the value used to set the
|
||||
|
||||
# Tcl variable $opts is used to build up the value used to set the
|
||||
# OPTS Makefile variable. Variable $cflags holds the value for
|
||||
# CFLAGS. The makefile will pass OPTS to both gcc and lemon, but
|
||||
# CFLAGS is only passed to gcc.
|
||||
#
|
||||
set cflags "-g"
|
||||
set cflags ""
|
||||
set opts ""
|
||||
regsub -all {#[^\n]*\n} $config \n config
|
||||
foreach arg $config {
|
||||
if {[string match -D* $arg]} {
|
||||
lappend opts $arg
|
||||
} elseif {[string match CC=* $arg]} {
|
||||
lappend testtarget $arg
|
||||
} else {
|
||||
lappend cflags $arg
|
||||
}
|
||||
@@ -284,72 +258,30 @@ proc run_test_suite {name testtarget config} {
|
||||
append opts " -DSQLITE_OS_UNIX=1"
|
||||
}
|
||||
|
||||
dryrun file mkdir $dir
|
||||
if {!$::DRYRUN} {
|
||||
set title ${name}($testtarget)
|
||||
set n [string length $title]
|
||||
puts -nonewline "${title}[string repeat . [expr {63-$n}]]"
|
||||
flush stdout
|
||||
}
|
||||
# Run the test.
|
||||
#
|
||||
set makefile [file normalize $::MAKEFILE]
|
||||
file mkdir $dir
|
||||
puts -nonewline "Testing configuration \"$name\" (logfile=$dir/test.log)..."
|
||||
flush stdout
|
||||
|
||||
set tm1 [clock seconds]
|
||||
set origdir [pwd]
|
||||
dryrun cd $dir
|
||||
set errmsg {}
|
||||
set rc [catch [configureCommand]]
|
||||
if {!$rc} {
|
||||
set rc [catch [makeCommand $testtarget $cflags $opts]]
|
||||
count_tests_and_errors test.log rc errmsg
|
||||
}
|
||||
set makecmd [concat \
|
||||
[list exec make -C $dir -f $makefile clean] \
|
||||
$testtarget \
|
||||
[list CFLAGS=$cflags OPTS=$opts >& $dir/test.log] \
|
||||
]
|
||||
|
||||
set tm1 [clock seconds]
|
||||
set rc [catch $makecmd]
|
||||
set tm2 [clock seconds]
|
||||
dryrun cd $origdir
|
||||
|
||||
if {!$::DRYRUN} {
|
||||
set hours [expr {($tm2-$tm1)/3600}]
|
||||
set minutes [expr {(($tm2-$tm1)/60)%60}]
|
||||
set seconds [expr {($tm2-$tm1)%60}]
|
||||
set tm [format (%02d:%02d:%02d) $hours $minutes $seconds]
|
||||
if {$rc} {
|
||||
puts " FAIL $tm"
|
||||
incr ::NERR
|
||||
if {$errmsg!=""} {puts " $errmsg"}
|
||||
} else {
|
||||
puts " Ok $tm"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# The following procedure returns the "configure" command to be exectued for
|
||||
# the current platform, which may be Windows (via MinGW, etc).
|
||||
#
|
||||
proc configureCommand {} {
|
||||
set result [list dryrun exec]
|
||||
if {$::tcl_platform(platform)=="windows"} {
|
||||
lappend result sh
|
||||
}
|
||||
lappend result $::SRCDIR/configure -enable-load-extension >& test.log
|
||||
}
|
||||
|
||||
# The following procedure returns the "make" command to be executed for the
|
||||
# specified targets, compiler flags, and options.
|
||||
#
|
||||
proc makeCommand { targets cflags opts } {
|
||||
set result [list dryrun exec make clean]
|
||||
foreach target $targets {
|
||||
lappend result $target
|
||||
}
|
||||
lappend result CFLAGS=$cflags OPTS=$opts >>& test.log
|
||||
}
|
||||
|
||||
# The following procedure either prints its arguments (if ::DRYRUN is true)
|
||||
# or executes the command of its arguments in the calling context
|
||||
# (if ::DRYRUN is false).
|
||||
#
|
||||
proc dryrun {args} {
|
||||
if {$::DRYRUN} {
|
||||
puts $args
|
||||
set minutes [expr {($tm2-$tm1)/60}]
|
||||
set seconds [expr {($tm2-$tm1)%60}]
|
||||
puts -nonewline [format " (%d:%.2d) " $minutes $seconds]
|
||||
if {$rc} {
|
||||
puts "FAILED."
|
||||
} else {
|
||||
uplevel 1 $args
|
||||
puts "Ok."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,21 +292,16 @@ proc dryrun {args} {
|
||||
# option.
|
||||
#
|
||||
proc process_options {argv} {
|
||||
set ::SRCDIR [file normalize [file dirname [file dirname $::argv0]]]
|
||||
set ::QUICK 0
|
||||
set ::BUILDONLY 0
|
||||
set ::DRYRUN 0
|
||||
set ::EXEC exec
|
||||
set ::MAKEFILE releasetest.mk ;# Default value
|
||||
set ::QUICK 0 ;# Default value
|
||||
set config {}
|
||||
set platform $::tcl_platform(os)-$::tcl_platform(machine)
|
||||
|
||||
for {set i 0} {$i < [llength $argv]} {incr i} {
|
||||
set x [lindex $argv $i]
|
||||
if {[regexp {^--[a-z]} $x]} {set x [string range $x 1 end]}
|
||||
switch -- $x {
|
||||
-srcdir {
|
||||
switch -- [lindex $argv $i] {
|
||||
-makefile {
|
||||
incr i
|
||||
set ::SRCDIR [file normalize [lindex $argv $i]]
|
||||
set ::MAKEFILE [lindex $argv $i]
|
||||
}
|
||||
|
||||
-platform {
|
||||
@@ -383,41 +310,15 @@ proc process_options {argv} {
|
||||
}
|
||||
|
||||
-quick {
|
||||
set ::QUICK 1
|
||||
incr i
|
||||
set ::QUICK [lindex $argv $i]
|
||||
}
|
||||
|
||||
-config {
|
||||
incr i
|
||||
set config [lindex $argv $i]
|
||||
}
|
||||
|
||||
-buildonly {
|
||||
set ::BUILDONLY 1
|
||||
}
|
||||
|
||||
-dryrun {
|
||||
set ::DRYRUN 1
|
||||
}
|
||||
|
||||
-info {
|
||||
puts "Command-line Options:"
|
||||
puts " --srcdir $::SRCDIR"
|
||||
puts " --platform [list $platform]"
|
||||
puts " --config [list $config]"
|
||||
if {$::QUICK} {puts " --quick"}
|
||||
if {$::BUILDONLY} {puts " --buildonly"}
|
||||
if {$::DRYRUN} {puts " --dryrun"}
|
||||
puts "\nAvailable --platform options:"
|
||||
foreach y [lsort [array names ::Platforms]] {
|
||||
puts " [list $y]"
|
||||
}
|
||||
puts "\nAvailable --config options:"
|
||||
foreach y [lsort [array names ::Configs]] {
|
||||
puts " [list $y]"
|
||||
}
|
||||
exit
|
||||
}
|
||||
|
||||
|
||||
default {
|
||||
puts stderr ""
|
||||
puts stderr [string trim $::USAGE_MESSAGE]
|
||||
@@ -426,6 +327,8 @@ proc process_options {argv} {
|
||||
}
|
||||
}
|
||||
|
||||
set ::MAKEFILE [file normalize $::MAKEFILE]
|
||||
|
||||
if {0==[info exists ::Platforms($platform)]} {
|
||||
puts "Unknown platform: $platform"
|
||||
puts -nonewline "Set the -platform option to "
|
||||
@@ -444,13 +347,8 @@ proc process_options {argv} {
|
||||
} else {
|
||||
set ::CONFIGLIST $::Platforms($platform)
|
||||
}
|
||||
puts "Running the following test configurations for $platform:"
|
||||
puts "Running the following configurations for $platform:"
|
||||
puts " [string trim $::CONFIGLIST]"
|
||||
puts -nonewline "Flags:"
|
||||
if {$::DRYRUN} {puts -nonewline " --dryrun"}
|
||||
if {$::BUILDONLY} {puts -nonewline " --buildonly"}
|
||||
if {$::QUICK} {puts -nonewline " --quick"}
|
||||
puts ""
|
||||
}
|
||||
|
||||
# Main routine.
|
||||
@@ -459,48 +357,30 @@ proc main {argv} {
|
||||
|
||||
# Process any command line options.
|
||||
process_options $argv
|
||||
puts [string repeat * 79]
|
||||
|
||||
set ::NERR 0
|
||||
set ::NTEST 0
|
||||
set ::NTESTCASE 0
|
||||
set ::NERRCASE 0
|
||||
set STARTTIME [clock seconds]
|
||||
foreach {zConfig target} $::CONFIGLIST {
|
||||
if {$::QUICK} {set target test}
|
||||
if {$::BUILDONLY} {set target testfixture}
|
||||
set config_options $::Configs($zConfig)
|
||||
|
||||
incr NTEST
|
||||
run_test_suite $zConfig $target $config_options
|
||||
|
||||
# If the configuration included the SQLITE_DEBUG option, then remove
|
||||
# it and run veryquick.test. If it did not include the SQLITE_DEBUG option
|
||||
# add it and run veryquick.test.
|
||||
if {$target!="checksymbols" && !$::BUILDONLY} {
|
||||
if {$target!="checksymbols"} {
|
||||
set debug_idx [lsearch -glob $config_options -DSQLITE_DEBUG*]
|
||||
set xtarget $target
|
||||
regsub -all {fulltest[a-z]*} $xtarget test xtarget
|
||||
if {$debug_idx < 0} {
|
||||
incr NTEST
|
||||
append config_options " -DSQLITE_DEBUG=1"
|
||||
run_test_suite "${zConfig}_debug" $xtarget $config_options
|
||||
run_test_suite "${zConfig}_debug" test [
|
||||
concat $config_options -DSQLITE_DEBUG=1
|
||||
]
|
||||
} else {
|
||||
incr NTEST
|
||||
regsub { *-DSQLITE_MEMDEBUG[^ ]* *} $config_options { } config_options
|
||||
regsub { *-DSQLITE_DEBUG[^ ]* *} $config_options { } config_options
|
||||
run_test_suite "${zConfig}_ndebug" $xtarget $config_options
|
||||
run_test_suite "${zConfig}_ndebug" test [
|
||||
lreplace $config_options $debug_idx $debug_idx
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set elapsetime [expr {[clock seconds]-$STARTTIME}]
|
||||
set hr [expr {$elapsetime/3600}]
|
||||
set min [expr {($elapsetime/60)%60}]
|
||||
set sec [expr {$elapsetime%60}]
|
||||
set etime [format (%02d:%02d:%02d) $hr $min $sec]
|
||||
puts [string repeat * 79]
|
||||
puts "$::NERRCASE failures of $::NTESTCASE tests run in $etime"
|
||||
}
|
||||
}
|
||||
|
||||
main $argv
|
||||
|
||||
+13
-15
@@ -207,10 +207,10 @@ do_test shell1-2.2.4 {
|
||||
} {0 {}}
|
||||
do_test shell1-2.2.5 {
|
||||
catchcmd "test.db" ".mode \"insert FOO"
|
||||
} {1 {Error: mode should be one of: ascii column csv html insert line list tabs tcl}}
|
||||
} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}}
|
||||
do_test shell1-2.2.6 {
|
||||
catchcmd "test.db" ".mode \'insert FOO"
|
||||
} {1 {Error: mode should be one of: ascii column csv html insert line list tabs tcl}}
|
||||
} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}}
|
||||
|
||||
# check multiple tokens, and quoted tokens
|
||||
do_test shell1-2.3.1 {
|
||||
@@ -238,7 +238,7 @@ do_test shell1-2.3.7 {
|
||||
# check quoted args are unquoted
|
||||
do_test shell1-2.4.1 {
|
||||
catchcmd "test.db" ".mode FOO"
|
||||
} {1 {Error: mode should be one of: ascii column csv html insert line list tabs tcl}}
|
||||
} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}}
|
||||
do_test shell1-2.4.2 {
|
||||
catchcmd "test.db" ".mode csv"
|
||||
} {0 {}}
|
||||
@@ -421,21 +421,20 @@ do_test shell1-3.12.3 {
|
||||
} {1 {Usage: .indices ?LIKE-PATTERN?}}
|
||||
|
||||
# .mode MODE ?TABLE? Set output mode where MODE is one of:
|
||||
# ascii Columns/rows delimited by 0x1F and 0x1E
|
||||
# csv Comma-separated values
|
||||
# column Left-aligned columns. (See .width)
|
||||
# html HTML <table> code
|
||||
# insert SQL insert statements for TABLE
|
||||
# line One value per line
|
||||
# list Values delimited by .separator strings
|
||||
# list Values delimited by .separator string
|
||||
# tabs Tab-separated values
|
||||
# tcl TCL list elements
|
||||
do_test shell1-3.13.1 {
|
||||
catchcmd "test.db" ".mode"
|
||||
} {1 {Error: mode should be one of: ascii column csv html insert line list tabs tcl}}
|
||||
} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}}
|
||||
do_test shell1-3.13.2 {
|
||||
catchcmd "test.db" ".mode FOO"
|
||||
} {1 {Error: mode should be one of: ascii column csv html insert line list tabs tcl}}
|
||||
} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}}
|
||||
do_test shell1-3.13.3 {
|
||||
catchcmd "test.db" ".mode csv"
|
||||
} {0 {}}
|
||||
@@ -468,10 +467,10 @@ do_test shell1-3.13.11 {
|
||||
# don't allow partial mode type matches
|
||||
do_test shell1-3.13.12 {
|
||||
catchcmd "test.db" ".mode l"
|
||||
} {1 {Error: mode should be one of: ascii column csv html insert line list tabs tcl}}
|
||||
} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}}
|
||||
do_test shell1-3.13.13 {
|
||||
catchcmd "test.db" ".mode li"
|
||||
} {1 {Error: mode should be one of: ascii column csv html insert line list tabs tcl}}
|
||||
} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}}
|
||||
do_test shell1-3.13.14 {
|
||||
catchcmd "test.db" ".mode lin"
|
||||
} {0 {}}
|
||||
@@ -587,10 +586,10 @@ CREATE VIEW v2 AS SELECT x+1 AS y FROM t1;
|
||||
CREATE VIEW v1 AS SELECT y+1 FROM v2;}}
|
||||
db eval {DROP VIEW v1; DROP VIEW v2; DROP TABLE t1;}
|
||||
|
||||
# .separator STRING Change column separator used by output and .import
|
||||
# .separator STRING Change separator used by output mode and .import
|
||||
do_test shell1-3.22.1 {
|
||||
catchcmd "test.db" ".separator"
|
||||
} {1 {Usage: .separator COL ?ROW?}}
|
||||
} {1 {Usage: .separator SEPARATOR ?NEWLINE?}}
|
||||
do_test shell1-3.22.2 {
|
||||
catchcmd "test.db" ".separator FOO"
|
||||
} {0 {}}
|
||||
@@ -600,7 +599,7 @@ do_test shell1-3.22.3 {
|
||||
do_test shell1-3.22.4 {
|
||||
# too many arguments
|
||||
catchcmd "test.db" ".separator FOO BAD BAD2"
|
||||
} {1 {Usage: .separator COL ?ROW?}}
|
||||
} {1 {Usage: .separator SEPARATOR ?NEWLINE?}}
|
||||
|
||||
# .show Show the current values for various settings
|
||||
do_test shell1-3.23.1 {
|
||||
@@ -611,11 +610,10 @@ do_test shell1-3.23.1 {
|
||||
[regexp {mode:} $res] \
|
||||
[regexp {nullvalue:} $res] \
|
||||
[regexp {output:} $res] \
|
||||
[regexp {colseparator:} $res] \
|
||||
[regexp {rowseparator:} $res] \
|
||||
[regexp {separator:} $res] \
|
||||
[regexp {stats:} $res] \
|
||||
[regexp {width:} $res]
|
||||
} {1 1 1 1 1 1 1 1 1 1}
|
||||
} {1 1 1 1 1 1 1 1 1}
|
||||
do_test shell1-3.23.2 {
|
||||
# too many arguments
|
||||
catchcmd "test.db" ".show BAD"
|
||||
|
||||
+5
-38
@@ -55,7 +55,7 @@ do_test shell5-1.1.3 {
|
||||
# .separator STRING Change separator used by output mode and .import
|
||||
do_test shell5-1.2.1 {
|
||||
catchcmd "test.db" ".separator"
|
||||
} {1 {Usage: .separator COL ?ROW?}}
|
||||
} {1 {Usage: .separator SEPARATOR ?NEWLINE?}}
|
||||
do_test shell5-1.2.2 {
|
||||
catchcmd "test.db" ".separator ONE"
|
||||
} {0 {}}
|
||||
@@ -65,18 +65,12 @@ do_test shell5-1.2.3 {
|
||||
do_test shell5-1.2.4 {
|
||||
# too many arguments
|
||||
catchcmd "test.db" ".separator ONE TWO THREE"
|
||||
} {1 {Usage: .separator COL ?ROW?}}
|
||||
} {1 {Usage: .separator SEPARATOR ?NEWLINE?}}
|
||||
|
||||
# column separator should default to "|"
|
||||
do_test shell5-1.3.1.1 {
|
||||
# separator should default to "|"
|
||||
do_test shell5-1.3.1 {
|
||||
set res [catchcmd "test.db" ".show"]
|
||||
list [regexp {colseparator: \"\|\"} $res]
|
||||
} {1}
|
||||
|
||||
# row separator should default to "\n"
|
||||
do_test shell5-1.3.1.2 {
|
||||
set res [catchcmd "test.db" ".show"]
|
||||
list [regexp {rowseparator: \"\\n\"} $res]
|
||||
list [regexp {separator: \"\|\"} $res]
|
||||
} {1}
|
||||
|
||||
# set separator to different value.
|
||||
@@ -378,32 +372,5 @@ CREATE TABLE t4(a, b);
|
||||
db eval { SELECT * FROM t4 }
|
||||
} {xy\" hello one 2 {} {}}
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Tests for the shell "ascii" import/export mode.
|
||||
#
|
||||
do_test shell5-3.1 {
|
||||
set fd [open shell5.csv w]
|
||||
fconfigure $fd -encoding binary -translation binary
|
||||
puts -nonewline $fd "\"test 1\"\x1F,test 2\r\n\x1E"
|
||||
puts -nonewline $fd "test 3\x1Ftest 4\n"
|
||||
close $fd
|
||||
catchcmd test.db {
|
||||
.mode ascii
|
||||
CREATE TABLE t5(a, b);
|
||||
.import shell5.csv t5
|
||||
}
|
||||
db eval { SELECT * FROM t5 }
|
||||
} "\{\"test 1\"} \{,test 2\r\n\} \{test 3\} \{test 4\n\}"
|
||||
|
||||
#
|
||||
# NOTE: This test ends up converting the "\r\n" to "\n\n" due
|
||||
# to end-of-line translation on the "stdout" channel.
|
||||
#
|
||||
do_test shell5-3.2 {
|
||||
catchcmd test.db {
|
||||
.mode ascii
|
||||
SELECT * FROM t5;
|
||||
}
|
||||
} "0 \{\"test 1\"\x1F,test 2\n\n\x1Etest 3\x1Ftest 4\n\x1E\}"
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -16,11 +16,6 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix sort
|
||||
db close
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_pmasz 10
|
||||
sqlite3_initialize
|
||||
sqlite3 db test.db
|
||||
|
||||
# Create a bunch of data to sort against
|
||||
#
|
||||
|
||||
@@ -17,11 +17,6 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix sort2
|
||||
db close
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_pmasz 10
|
||||
sqlite3_initialize
|
||||
sqlite3 db test.db
|
||||
|
||||
foreach {tn script} {
|
||||
1 { }
|
||||
|
||||
@@ -17,12 +17,6 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix sort4
|
||||
db close
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_pmasz 10
|
||||
sqlite3_initialize
|
||||
sqlite3 db test.db
|
||||
|
||||
|
||||
# Configure the sorter to use 3 background threads.
|
||||
db eval {PRAGMA threads=3}
|
||||
|
||||
@@ -17,12 +17,6 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix sortfault
|
||||
db close
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_pmasz 10
|
||||
sqlite3_initialize
|
||||
sqlite3 db test.db
|
||||
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
PRAGMA cache_size = 5;
|
||||
|
||||
+117
-149
@@ -1,41 +1,41 @@
|
||||
|
||||
/*
|
||||
** 2010-07-22
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** The code in this file runs a few multi-threaded test cases using the
|
||||
** SQLite library. It can be compiled to an executable on unix using the
|
||||
** following command:
|
||||
**
|
||||
** gcc -O2 threadtest3.c sqlite3.c -ldl -lpthread -lm
|
||||
**
|
||||
** Even though threadtest3.c is the only C source code file mentioned on
|
||||
** the compiler command-line, #include macros are used to pull in additional
|
||||
** C code files named "tt3_*.c".
|
||||
** Then run the compiled program. The exit status is non-zero if any tests
|
||||
** failed (hopefully there is also some output to stdout to clarify what went
|
||||
** wrong).
|
||||
**
|
||||
** After compiling, run this program with an optional argument telling
|
||||
** which test to run. All tests are run if no argument is given. The
|
||||
** argument can be a glob pattern to match multiple tests. Examples:
|
||||
** There are three parts to the code in this file, in the following order:
|
||||
**
|
||||
** ./a.out -- Run all tests
|
||||
** ./a.out walthread3 -- Run the "walthread3" test
|
||||
** ./a.out 'wal*' -- Run all of the wal* tests
|
||||
** ./a.out --help -- List all available tests
|
||||
** 1. Code for the SQL aggregate function md5sum() copied from
|
||||
** tclsqlite.c in the SQLite distribution. The names of all the
|
||||
** types and functions in this section begin with "MD5" or "md5".
|
||||
**
|
||||
** The exit status is non-zero if any test fails.
|
||||
** 2. A set of utility functions that may be used to implement
|
||||
** multi-threaded test cases. These are all called by test code
|
||||
** via macros that help with error reporting. The macros are defined
|
||||
** immediately below this comment.
|
||||
**
|
||||
** 3. The test code itself. And a main() routine to drive the test
|
||||
** code.
|
||||
*/
|
||||
|
||||
/*
|
||||
** The "Set Error Line" macro.
|
||||
/*************************************************************************
|
||||
** Start of test code/infrastructure interface macros.
|
||||
**
|
||||
** The following macros constitute the interface between the test
|
||||
** programs and the test infrastructure. Test infrastructure code
|
||||
** does not itself use any of these macros. Test code should not
|
||||
** call any of the macroname_x() functions directly.
|
||||
**
|
||||
** See the header comments above the corresponding macroname_x()
|
||||
** function for a description of each interface.
|
||||
*/
|
||||
#define SEL(e) ((e)->iLine = ((e)->rc ? (e)->iLine : __LINE__))
|
||||
|
||||
/* Database functions */
|
||||
#define opendb(w,x,y,z) (SEL(w), opendb_x(w,x,y,z))
|
||||
@@ -47,13 +47,10 @@
|
||||
#define execsql_i64(x,y,...) (SEL(x), execsql_i64_x(x,y,__VA_ARGS__))
|
||||
#define execsql_text(x,y,z,...) (SEL(x), execsql_text_x(x,y,z,__VA_ARGS__))
|
||||
#define execsql(x,y,...) (SEL(x), (void)execsql_i64_x(x,y,__VA_ARGS__))
|
||||
#define sql_script_printf(x,y,z,...) ( \
|
||||
SEL(x), sql_script_printf_x(x,y,z,__VA_ARGS__) \
|
||||
)
|
||||
|
||||
/* Thread functions */
|
||||
#define launch_thread(w,x,y,z) (SEL(w), launch_thread_x(w,x,y,z))
|
||||
#define join_all_threads(y,z) (SEL(y), join_all_threads_x(y,z))
|
||||
#define launch_thread(w,x,y,z) (SEL(w), launch_thread_x(w,x,y,z))
|
||||
#define join_all_threads(y,z) (SEL(y), join_all_threads_x(y,z))
|
||||
|
||||
/* Timer functions */
|
||||
#define setstoptime(y,z) (SEL(y), setstoptime_x(y,z))
|
||||
@@ -67,9 +64,6 @@
|
||||
#define filesize(y,z) (SEL(y), filesize_x(y,z))
|
||||
#define filecopy(x,y,z) (SEL(x), filecopy_x(x,y,z))
|
||||
|
||||
#define PTR2INT(x) ((int)((intptr_t)x))
|
||||
#define INT2PTR(x) ((void*)((intptr_t)x))
|
||||
|
||||
/*
|
||||
** End of test code/infrastructure interface macros.
|
||||
*************************************************************************/
|
||||
@@ -121,10 +115,7 @@ struct MD5Context {
|
||||
int isInit;
|
||||
uint32 buf[4];
|
||||
uint32 bits[2];
|
||||
union {
|
||||
unsigned char in[64];
|
||||
uint32 in32[16];
|
||||
} u;
|
||||
unsigned char in[64];
|
||||
};
|
||||
typedef struct MD5Context MD5Context;
|
||||
|
||||
@@ -273,7 +264,7 @@ void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len){
|
||||
/* Handle any leading odd-sized chunks */
|
||||
|
||||
if ( t ) {
|
||||
unsigned char *p = (unsigned char *)ctx->u.in + t;
|
||||
unsigned char *p = (unsigned char *)ctx->in + t;
|
||||
|
||||
t = 64-t;
|
||||
if (len < t) {
|
||||
@@ -281,8 +272,8 @@ void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len){
|
||||
return;
|
||||
}
|
||||
memcpy(p, buf, t);
|
||||
byteReverse(ctx->u.in, 16);
|
||||
MD5Transform(ctx->buf, (uint32 *)ctx->u.in);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, (uint32 *)ctx->in);
|
||||
buf += t;
|
||||
len -= t;
|
||||
}
|
||||
@@ -290,16 +281,16 @@ void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len){
|
||||
/* Process data in 64-byte chunks */
|
||||
|
||||
while (len >= 64) {
|
||||
memcpy(ctx->u.in, buf, 64);
|
||||
byteReverse(ctx->u.in, 16);
|
||||
MD5Transform(ctx->buf, (uint32 *)ctx->u.in);
|
||||
memcpy(ctx->in, buf, 64);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, (uint32 *)ctx->in);
|
||||
buf += 64;
|
||||
len -= 64;
|
||||
}
|
||||
|
||||
/* Handle any remaining bytes of data. */
|
||||
|
||||
memcpy(ctx->u.in, buf, len);
|
||||
memcpy(ctx->in, buf, len);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -315,7 +306,7 @@ static void MD5Final(unsigned char digest[16], MD5Context *ctx){
|
||||
|
||||
/* Set the first char of padding to 0x80. This is safe since there is
|
||||
always at least one byte free */
|
||||
p = ctx->u.in + count;
|
||||
p = ctx->in + count;
|
||||
*p++ = 0x80;
|
||||
|
||||
/* Bytes of padding needed to make 64 bytes */
|
||||
@@ -325,25 +316,25 @@ static void MD5Final(unsigned char digest[16], MD5Context *ctx){
|
||||
if (count < 8) {
|
||||
/* Two lots of padding: Pad the first block to 64 bytes */
|
||||
memset(p, 0, count);
|
||||
byteReverse(ctx->u.in, 16);
|
||||
MD5Transform(ctx->buf, (uint32 *)ctx->u.in);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, (uint32 *)ctx->in);
|
||||
|
||||
/* Now fill the next block with 56 bytes */
|
||||
memset(ctx->u.in, 0, 56);
|
||||
memset(ctx->in, 0, 56);
|
||||
} else {
|
||||
/* Pad block to 56 bytes */
|
||||
memset(p, 0, count-8);
|
||||
}
|
||||
byteReverse(ctx->u.in, 14);
|
||||
byteReverse(ctx->in, 14);
|
||||
|
||||
/* Append length in bits and transform */
|
||||
ctx->u.in32[14] = ctx->bits[0];
|
||||
ctx->u.in32[15] = ctx->bits[1];
|
||||
((uint32 *)ctx->in)[ 14 ] = ctx->bits[0];
|
||||
((uint32 *)ctx->in)[ 15 ] = ctx->bits[1];
|
||||
|
||||
MD5Transform(ctx->buf, (uint32 *)ctx->u.in);
|
||||
MD5Transform(ctx->buf, (uint32 *)ctx->in);
|
||||
byteReverse((unsigned char *)ctx->buf, 4);
|
||||
memcpy(digest, ctx->buf, 16);
|
||||
memset(ctx, 0, sizeof(*ctx)); /* In case it is sensitive */
|
||||
memset(ctx, 0, sizeof(ctx)); /* In case it is sensitive */
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -391,9 +382,9 @@ static void md5finalize(sqlite3_context *context){
|
||||
sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
/*
|
||||
/*************************************************************************
|
||||
** End of copied md5sum() code.
|
||||
**************************************************************************/
|
||||
*/
|
||||
|
||||
typedef sqlite3_int64 i64;
|
||||
|
||||
@@ -407,6 +398,9 @@ typedef struct Thread Thread;
|
||||
/* Total number of errors in this process so far. */
|
||||
static int nGlobalErr = 0;
|
||||
|
||||
/* Set to true to run in "process" instead of "thread" mode. */
|
||||
static int bProcessMode = 0;
|
||||
|
||||
struct Error {
|
||||
int rc;
|
||||
int iLine;
|
||||
@@ -427,10 +421,10 @@ struct Statement {
|
||||
|
||||
struct Thread {
|
||||
int iTid; /* Thread number within test */
|
||||
void* pArg; /* Pointer argument passed by caller */
|
||||
int iArg; /* Integer argument passed by caller */
|
||||
|
||||
pthread_t tid; /* Thread id */
|
||||
char *(*xProc)(int, void*); /* Thread main proc */
|
||||
char *(*xProc)(int, int); /* Thread main proc */
|
||||
Thread *pNext; /* Next in this list of threads */
|
||||
};
|
||||
|
||||
@@ -447,13 +441,8 @@ static void free_err(Error *p){
|
||||
|
||||
static void print_err(Error *p){
|
||||
if( p->rc!=SQLITE_OK ){
|
||||
int isWarn = 0;
|
||||
if( p->rc==SQLITE_SCHEMA ) isWarn = 1;
|
||||
if( sqlite3_strglob("* - no such table: *",p->zErr)==0 ) isWarn = 1;
|
||||
printf("%s: (%d) \"%s\" at line %d\n", isWarn ? "Warning" : "Error",
|
||||
p->rc, p->zErr, p->iLine);
|
||||
if( !isWarn ) nGlobalErr++;
|
||||
fflush(stdout);
|
||||
printf("Error: (%d) \"%s\" at line %d\n", p->rc, p->zErr, p->iLine);
|
||||
nGlobalErr++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -517,9 +506,8 @@ static void opendb_x(
|
||||
){
|
||||
if( pErr->rc==SQLITE_OK ){
|
||||
int rc;
|
||||
int flags = SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_URI;
|
||||
if( bDelete ) unlink(zFile);
|
||||
rc = sqlite3_open_v2(zFile, &pDb->db, flags, 0);
|
||||
rc = sqlite3_open(zFile, &pDb->db);
|
||||
if( rc ){
|
||||
sqlite_error(pErr, pDb, "open");
|
||||
sqlite3_close(pDb->db);
|
||||
@@ -568,22 +556,6 @@ static void sql_script_x(
|
||||
}
|
||||
}
|
||||
|
||||
static void sql_script_printf_x(
|
||||
Error *pErr, /* IN/OUT: Error code */
|
||||
Sqlite *pDb, /* Database handle */
|
||||
const char *zFormat, /* SQL printf format string */
|
||||
... /* Printf args */
|
||||
){
|
||||
va_list ap; /* ... printf arguments */
|
||||
va_start(ap, zFormat);
|
||||
if( pErr->rc==SQLITE_OK ){
|
||||
char *zSql = sqlite3_vmprintf(zFormat, ap);
|
||||
pErr->rc = sqlite3_exec(pDb->db, zSql, 0, 0, &pErr->zErr);
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static Statement *getSqlStatement(
|
||||
Error *pErr, /* IN/OUT: Error code */
|
||||
Sqlite *pDb, /* Database handle */
|
||||
@@ -652,9 +624,11 @@ static i64 execsql_i64_x(
|
||||
if( pErr->rc==SQLITE_OK ){
|
||||
sqlite3_stmt *pStmt; /* SQL statement to execute */
|
||||
va_list ap; /* ... arguments */
|
||||
int i; /* Used to iterate through parameters */
|
||||
va_start(ap, pDb);
|
||||
pStmt = getAndBindSqlStatement(pErr, pDb, ap);
|
||||
if( pStmt ){
|
||||
int rc;
|
||||
int first = 1;
|
||||
while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
if( first && sqlite3_column_count(pStmt)>0 ){
|
||||
@@ -689,9 +663,11 @@ static char * execsql_text_x(
|
||||
if( pErr->rc==SQLITE_OK ){
|
||||
sqlite3_stmt *pStmt; /* SQL statement to execute */
|
||||
va_list ap; /* ... arguments */
|
||||
int i; /* Used to iterate through parameters */
|
||||
va_start(ap, iSlot);
|
||||
pStmt = getAndBindSqlStatement(pErr, pDb, ap);
|
||||
if( pStmt ){
|
||||
int rc;
|
||||
int first = 1;
|
||||
while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
if( first && sqlite3_column_count(pStmt)>0 ){
|
||||
@@ -717,13 +693,14 @@ static void integrity_check_x(
|
||||
){
|
||||
if( pErr->rc==SQLITE_OK ){
|
||||
Statement *pStatement; /* Statement to execute */
|
||||
int rc; /* Return code */
|
||||
char *zErr = 0; /* Integrity check error */
|
||||
|
||||
pStatement = getSqlStatement(pErr, pDb, "PRAGMA integrity_check");
|
||||
if( pStatement ){
|
||||
sqlite3_stmt *pStmt = pStatement->pStmt;
|
||||
while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
const char *z = (const char*)sqlite3_column_text(pStmt, 0);
|
||||
const char *z = sqlite3_column_text(pStmt, 0);
|
||||
if( strcmp(z, "ok") ){
|
||||
if( zErr==0 ){
|
||||
zErr = sqlite3_mprintf("%s", z);
|
||||
@@ -744,14 +721,14 @@ static void integrity_check_x(
|
||||
|
||||
static void *launch_thread_main(void *pArg){
|
||||
Thread *p = (Thread *)pArg;
|
||||
return (void *)p->xProc(p->iTid, p->pArg);
|
||||
return (void *)p->xProc(p->iTid, p->iArg);
|
||||
}
|
||||
|
||||
static void launch_thread_x(
|
||||
Error *pErr, /* IN/OUT: Error code */
|
||||
Threadset *pThreads, /* Thread set */
|
||||
char *(*xProc)(int, void*), /* Proc to run */
|
||||
void *pArg /* Argument passed to thread proc */
|
||||
char *(*xProc)(int, int), /* Proc to run */
|
||||
int iArg /* Argument passed to thread proc */
|
||||
){
|
||||
if( pErr->rc==SQLITE_OK ){
|
||||
int iTid = ++pThreads->iMaxTid;
|
||||
@@ -761,7 +738,7 @@ static void launch_thread_x(
|
||||
p = (Thread *)sqlite3_malloc(sizeof(Thread));
|
||||
memset(p, 0, sizeof(Thread));
|
||||
p->iTid = iTid;
|
||||
p->pArg = pArg;
|
||||
p->iArg = iArg;
|
||||
p->xProc = xProc;
|
||||
|
||||
rc = pthread_create(&p->tid, NULL, launch_thread_main, (void *)p);
|
||||
@@ -790,7 +767,6 @@ static void join_all_threads_x(
|
||||
if( pErr->rc==SQLITE_OK ) system_error(pErr, rc);
|
||||
}else{
|
||||
printf("Thread %d says: %s\n", p->iTid, (ret==0 ? "..." : (char *)ret));
|
||||
fflush(stdout);
|
||||
}
|
||||
sqlite3_free(p);
|
||||
}
|
||||
@@ -904,6 +880,11 @@ static int timetostop_x(
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
** The "Set Error Line" macro.
|
||||
*/
|
||||
#define SEL(e) ((e)->iLine = ((e)->rc ? (e)->iLine : __LINE__))
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
**************************************************************************
|
||||
@@ -914,7 +895,7 @@ static int timetostop_x(
|
||||
#define WALTHREAD1_NTHREAD 10
|
||||
#define WALTHREAD3_NTHREAD 6
|
||||
|
||||
static char *walthread1_thread(int iTid, void *pArg){
|
||||
static char *walthread1_thread(int iTid, int iArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
int nIter = 0; /* Iterations so far */
|
||||
@@ -953,7 +934,7 @@ static char *walthread1_thread(int iTid, void *pArg){
|
||||
return sqlite3_mprintf("%d iterations", nIter);
|
||||
}
|
||||
|
||||
static char *walthread1_ckpt_thread(int iTid, void *pArg){
|
||||
static char *walthread1_ckpt_thread(int iTid, int iArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
int nCkpt = 0; /* Checkpoints so far */
|
||||
@@ -985,7 +966,6 @@ static void walthread1(int nMs){
|
||||
"INSERT INTO t1 VALUES(randomblob(100));"
|
||||
"INSERT INTO t1 SELECT md5sum(x) FROM t1;"
|
||||
);
|
||||
closedb(&err, &db);
|
||||
|
||||
setstoptime(&err, nMs);
|
||||
for(i=0; i<WALTHREAD1_NTHREAD; i++){
|
||||
@@ -997,11 +977,10 @@ static void walthread1(int nMs){
|
||||
print_and_free_err(&err);
|
||||
}
|
||||
|
||||
static char *walthread2_thread(int iTid, void *pArg){
|
||||
static char *walthread2_thread(int iTid, int iArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
int anTrans[2] = {0, 0}; /* Number of WAL and Rollback transactions */
|
||||
int iArg = PTR2INT(pArg);
|
||||
|
||||
const char *zJournal = "PRAGMA journal_mode = WAL";
|
||||
if( iArg ){ zJournal = "PRAGMA journal_mode = DELETE"; }
|
||||
@@ -1047,18 +1026,17 @@ static void walthread2(int nMs){
|
||||
setstoptime(&err, nMs);
|
||||
launch_thread(&err, &threads, walthread2_thread, 0);
|
||||
launch_thread(&err, &threads, walthread2_thread, 0);
|
||||
launch_thread(&err, &threads, walthread2_thread, (void*)1);
|
||||
launch_thread(&err, &threads, walthread2_thread, (void*)1);
|
||||
launch_thread(&err, &threads, walthread2_thread, 1);
|
||||
launch_thread(&err, &threads, walthread2_thread, 1);
|
||||
join_all_threads(&err, &threads);
|
||||
|
||||
print_and_free_err(&err);
|
||||
}
|
||||
|
||||
static char *walthread3_thread(int iTid, void *pArg){
|
||||
static char *walthread3_thread(int iTid, int iArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
i64 iNextWrite; /* Next value this thread will write */
|
||||
int iArg = PTR2INT(pArg);
|
||||
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
sql_script(&err, &db, "PRAGMA wal_autocheckpoint = 10");
|
||||
@@ -1109,14 +1087,14 @@ static void walthread3(int nMs){
|
||||
|
||||
setstoptime(&err, nMs);
|
||||
for(i=0; i<WALTHREAD3_NTHREAD; i++){
|
||||
launch_thread(&err, &threads, walthread3_thread, INT2PTR(i));
|
||||
launch_thread(&err, &threads, walthread3_thread, i);
|
||||
}
|
||||
join_all_threads(&err, &threads);
|
||||
|
||||
print_and_free_err(&err);
|
||||
}
|
||||
|
||||
static char *walthread4_reader_thread(int iTid, void *pArg){
|
||||
static char *walthread4_reader_thread(int iTid, int iArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
|
||||
@@ -1130,7 +1108,7 @@ static char *walthread4_reader_thread(int iTid, void *pArg){
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *walthread4_writer_thread(int iTid, void *pArg){
|
||||
static char *walthread4_writer_thread(int iTid, int iArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
i64 iRow = 1;
|
||||
@@ -1170,7 +1148,7 @@ static void walthread4(int nMs){
|
||||
print_and_free_err(&err);
|
||||
}
|
||||
|
||||
static char *walthread5_thread(int iTid, void *pArg){
|
||||
static char *walthread5_thread(int iTid, int iArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
i64 nRow;
|
||||
@@ -1302,7 +1280,7 @@ static void cgt_pager_1(int nMs){
|
||||
** is an attempt to find a bug reported to us.
|
||||
*/
|
||||
|
||||
static char *dynamic_triggers_1(int iTid, void *pArg){
|
||||
static char *dynamic_triggers_1(int iTid, int iArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
int nDrop = 0;
|
||||
@@ -1348,13 +1326,12 @@ static char *dynamic_triggers_1(int iTid, void *pArg){
|
||||
nDrop++;
|
||||
}
|
||||
}
|
||||
closedb(&err, &db);
|
||||
|
||||
print_and_free_err(&err);
|
||||
return sqlite3_mprintf("%d created, %d dropped", nCreate, nDrop);
|
||||
}
|
||||
|
||||
static char *dynamic_triggers_2(int iTid, void *pArg){
|
||||
static char *dynamic_triggers_2(int iTid, int iArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
i64 iVal = 0;
|
||||
@@ -1375,7 +1352,6 @@ static char *dynamic_triggers_2(int iTid, void *pArg){
|
||||
nDelete++;
|
||||
} while( iVal );
|
||||
}
|
||||
closedb(&err, &db);
|
||||
|
||||
print_and_free_err(&err);
|
||||
return sqlite3_mprintf("%d inserts, %d deletes", nInsert, nDelete);
|
||||
@@ -1400,16 +1376,15 @@ static void dynamic_triggers(int nMs){
|
||||
"CREATE TABLE t8(x, y);"
|
||||
"CREATE TABLE t9(x, y);"
|
||||
);
|
||||
closedb(&err, &db);
|
||||
|
||||
setstoptime(&err, nMs);
|
||||
|
||||
sqlite3_enable_shared_cache(1);
|
||||
launch_thread(&err, &threads, dynamic_triggers_2, 0);
|
||||
launch_thread(&err, &threads, dynamic_triggers_2, 0);
|
||||
sqlite3_enable_shared_cache(0);
|
||||
|
||||
sleep(2);
|
||||
sqlite3_enable_shared_cache(0);
|
||||
|
||||
launch_thread(&err, &threads, dynamic_triggers_2, 0);
|
||||
launch_thread(&err, &threads, dynamic_triggers_1, 0);
|
||||
@@ -1419,71 +1394,62 @@ static void dynamic_triggers(int nMs){
|
||||
print_and_free_err(&err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#include "tt3_checkpoint.c"
|
||||
#include "tt3_index.c"
|
||||
#include "tt3_lookaside1.c"
|
||||
#include "tt3_vacuum.c"
|
||||
#include "tt3_stress.c"
|
||||
|
||||
int main(int argc, char **argv){
|
||||
struct ThreadTest {
|
||||
void (*xTest)(int); /* Routine for running this test */
|
||||
const char *zTest; /* Name of this test */
|
||||
int nMs; /* How long to run this test, in milliseconds */
|
||||
void (*xTest)(int);
|
||||
const char *zTest;
|
||||
int nMs;
|
||||
} aTest[] = {
|
||||
{ walthread1, "walthread1", 20000 },
|
||||
{ walthread2, "walthread2", 20000 },
|
||||
{ walthread3, "walthread3", 20000 },
|
||||
{ walthread4, "walthread4", 20000 },
|
||||
{ walthread5, "walthread5", 1000 },
|
||||
{ walthread5, "walthread5", 1000 },
|
||||
|
||||
{ cgt_pager_1, "cgt_pager_1", 0 },
|
||||
{ dynamic_triggers, "dynamic_triggers", 20000 },
|
||||
|
||||
{ checkpoint_starvation_1, "checkpoint_starvation_1", 10000 },
|
||||
{ checkpoint_starvation_2, "checkpoint_starvation_2", 10000 },
|
||||
|
||||
{ create_drop_index_1, "create_drop_index_1", 10000 },
|
||||
{ lookaside1, "lookaside1", 10000 },
|
||||
{ vacuum1, "vacuum1", 10000 },
|
||||
{ stress1, "stress1", 10000 },
|
||||
{ stress2, "stress2", 60000 },
|
||||
};
|
||||
static char *substArgv[] = { 0, "*", 0 };
|
||||
int i, iArg;
|
||||
int nTestfound = 0;
|
||||
|
||||
int i;
|
||||
char *zTest = 0;
|
||||
int nTest = 0;
|
||||
int bTestfound = 0;
|
||||
int bPrefix = 0;
|
||||
|
||||
if( argc>2 ) goto usage;
|
||||
if( argc==2 ){
|
||||
zTest = argv[1];
|
||||
nTest = strlen(zTest);
|
||||
if( zTest[nTest-1]=='*' ){
|
||||
nTest--;
|
||||
bPrefix = 1;
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
|
||||
if( argc<2 ){
|
||||
argc = 2;
|
||||
argv = substArgv;
|
||||
}
|
||||
for(iArg=1; iArg<argc; iArg++){
|
||||
for(i=0; i<sizeof(aTest)/sizeof(aTest[0]); i++){
|
||||
if( sqlite3_strglob(argv[iArg],aTest[i].zTest)==0 ) break;
|
||||
}
|
||||
if( i>=sizeof(aTest)/sizeof(aTest[0]) ) goto usage;
|
||||
}
|
||||
for(iArg=1; iArg<argc; iArg++){
|
||||
for(i=0; i<sizeof(aTest)/sizeof(aTest[0]); i++){
|
||||
char const *z = aTest[i].zTest;
|
||||
if( sqlite3_strglob(argv[iArg],z)==0 ){
|
||||
printf("Running %s for %d seconds...\n", z, aTest[i].nMs/1000);
|
||||
fflush(stdout);
|
||||
aTest[i].xTest(aTest[i].nMs);
|
||||
nTestfound++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( nTestfound==0 ) goto usage;
|
||||
|
||||
printf("%d errors out of %d tests\n", nGlobalErr, nTestfound);
|
||||
for(i=0; i<sizeof(aTest)/sizeof(aTest[0]); i++){
|
||||
char const *z = aTest[i].zTest;
|
||||
int n = strlen(z);
|
||||
if( !zTest || ((bPrefix || n==nTest) && 0==strncmp(zTest, z, nTest)) ){
|
||||
printf("Running %s for %d seconds...\n", z, aTest[i].nMs/1000);
|
||||
aTest[i].xTest(aTest[i].nMs);
|
||||
bTestfound++;
|
||||
}
|
||||
}
|
||||
if( bTestfound==0 ) goto usage;
|
||||
|
||||
printf("Total of %d errors across all tests\n", nGlobalErr);
|
||||
return (nGlobalErr>0 ? 255 : 0);
|
||||
|
||||
usage:
|
||||
printf("Usage: %s [testname|testprefix*]...\n", argv[0]);
|
||||
printf("Usage: %s [testname|testprefix*]\n", argv[0]);
|
||||
printf("Available tests are:\n");
|
||||
for(i=0; i<sizeof(aTest)/sizeof(aTest[0]); i++){
|
||||
printf(" %s\n", aTest[i].zTest);
|
||||
@@ -1491,3 +1457,5 @@ int main(int argc, char **argv){
|
||||
|
||||
return 254;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,484 +0,0 @@
|
||||
/*
|
||||
** 2014-12-11
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file implements a simple standalone program used to stress the
|
||||
** SQLite library when accessing the same set of databases simultaneously
|
||||
** from multiple threads in shared-cache mode.
|
||||
**
|
||||
** This test program runs on unix-like systems only. It uses pthreads.
|
||||
** To compile:
|
||||
**
|
||||
** gcc -g -Wall -I. threadtest4.c sqlite3.c -ldl -lpthread
|
||||
**
|
||||
** To run:
|
||||
**
|
||||
** ./a.out 10
|
||||
**
|
||||
** The argument is the number of threads. There are also options, such
|
||||
** as -wal and -multithread and -serialized.
|
||||
**
|
||||
** Consider also compiling with clang instead of gcc and adding the
|
||||
** -fsanitize=thread option.
|
||||
*/
|
||||
#include "sqlite3.h"
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/*
|
||||
** An instance of the following structure is passed into each worker
|
||||
** thread.
|
||||
*/
|
||||
typedef struct WorkerInfo WorkerInfo;
|
||||
struct WorkerInfo {
|
||||
int tid; /* Thread ID */
|
||||
int nWorker; /* Total number of workers */
|
||||
unsigned wkrFlags; /* Flags */
|
||||
sqlite3 *mainDb; /* Database connection of the main thread */
|
||||
sqlite3 *db; /* Database connection of this thread */
|
||||
int nErr; /* Number of errors seen by this thread */
|
||||
int nTest; /* Number of tests run by this thread */
|
||||
char *zMsg; /* Message returned by this thread */
|
||||
pthread_t id; /* Thread id */
|
||||
pthread_mutex_t *pWrMutex; /* Hold this mutex while writing */
|
||||
};
|
||||
|
||||
/*
|
||||
** Allowed values for WorkerInfo.wkrFlags
|
||||
*/
|
||||
#define TT4_SERIALIZED 0x0000001 /* The --serialized option is used */
|
||||
#define TT4_WAL 0x0000002 /* WAL mode in use */
|
||||
#define TT4_TRACE 0x0000004 /* Trace activity */
|
||||
|
||||
|
||||
/*
|
||||
** Report an OOM error and die if the argument is NULL
|
||||
*/
|
||||
static void check_oom(void *x){
|
||||
if( x==0 ){
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate memory. If the allocation fails, print an error message and
|
||||
** kill the process.
|
||||
*/
|
||||
static void *safe_malloc(int sz){
|
||||
void *x = sqlite3_malloc(sz>0?sz:1);
|
||||
check_oom(x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
** Print a trace message for a worker
|
||||
*/
|
||||
static void worker_trace(WorkerInfo *p, const char *zFormat, ...){
|
||||
va_list ap;
|
||||
char *zMsg;
|
||||
if( (p->wkrFlags & TT4_TRACE)==0 ) return;
|
||||
va_start(ap, zFormat);
|
||||
zMsg = sqlite3_vmprintf(zFormat, ap);
|
||||
check_oom(zMsg);
|
||||
va_end(ap);
|
||||
fprintf(stderr, "TRACE(%02d): %s\n", p->tid, zMsg);
|
||||
sqlite3_free(zMsg);
|
||||
}
|
||||
|
||||
/*
|
||||
** Prepare a single SQL query
|
||||
*/
|
||||
static sqlite3_stmt *prep_sql(sqlite3 *db, const char *zFormat, ...){
|
||||
va_list ap;
|
||||
char *zSql;
|
||||
int rc;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
|
||||
va_start(ap, zFormat);
|
||||
zSql = sqlite3_vmprintf(zFormat, ap);
|
||||
va_end(ap);
|
||||
check_oom(zSql);
|
||||
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
fprintf(stderr, "SQL error (%d,%d): %s\nWhile preparing: [%s]\n",
|
||||
rc, sqlite3_extended_errcode(db), sqlite3_errmsg(db), zSql);
|
||||
exit(1);
|
||||
}
|
||||
sqlite3_free(zSql);
|
||||
return pStmt;
|
||||
}
|
||||
|
||||
/*
|
||||
** Run a SQL statements. Panic if unable.
|
||||
*/
|
||||
static void run_sql(WorkerInfo *p, const char *zFormat, ...){
|
||||
va_list ap;
|
||||
char *zSql;
|
||||
int rc;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
int nRetry = 0;
|
||||
|
||||
va_start(ap, zFormat);
|
||||
zSql = sqlite3_vmprintf(zFormat, ap);
|
||||
va_end(ap);
|
||||
check_oom(zSql);
|
||||
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
fprintf(stderr, "SQL error (%d,%d): %s\nWhile preparing: [%s]\n",
|
||||
rc, sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zSql);
|
||||
exit(1);
|
||||
}
|
||||
worker_trace(p, "running [%s]", zSql);
|
||||
while( (rc = sqlite3_step(pStmt))!=SQLITE_DONE ){
|
||||
if( (rc&0xff)==SQLITE_BUSY || (rc&0xff)==SQLITE_LOCKED ){
|
||||
sqlite3_reset(pStmt);
|
||||
nRetry++;
|
||||
if( nRetry<10 ){
|
||||
worker_trace(p, "retry %d for [%s]", nRetry, zSql);
|
||||
sched_yield();
|
||||
continue;
|
||||
}else{
|
||||
fprintf(stderr, "Deadlock in thread %d while running [%s]\n",
|
||||
p->tid, zSql);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if( rc!=SQLITE_ROW ){
|
||||
fprintf(stderr, "SQL error (%d,%d): %s\nWhile running [%s]\n",
|
||||
rc, sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zSql);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
sqlite3_free(zSql);
|
||||
sqlite3_finalize(pStmt);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Open the database connection for WorkerInfo. The order in which
|
||||
** the files are opened is a function of the tid value.
|
||||
*/
|
||||
static void worker_open_connection(WorkerInfo *p, int iCnt){
|
||||
char *zFile;
|
||||
int x;
|
||||
int rc;
|
||||
static const unsigned char aOrder[6][3] = {
|
||||
{ 1, 2, 3},
|
||||
{ 1, 3, 2},
|
||||
{ 2, 1, 3},
|
||||
{ 2, 3, 1},
|
||||
{ 3, 1, 2},
|
||||
{ 3, 2, 1}
|
||||
};
|
||||
x = (p->tid + iCnt) % 6;
|
||||
zFile = sqlite3_mprintf("tt4-test%d.db", aOrder[x][0]);
|
||||
check_oom(zFile);
|
||||
worker_trace(p, "open %s", zFile);
|
||||
rc = sqlite3_open_v2(zFile, &p->db,
|
||||
SQLITE_OPEN_READWRITE|SQLITE_OPEN_SHAREDCACHE, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
fprintf(stderr, "sqlite_open_v2(%s) failed on thread %d\n",
|
||||
zFile, p->tid);
|
||||
exit(1);
|
||||
}
|
||||
sqlite3_free(zFile);
|
||||
run_sql(p, "PRAGMA read_uncommitted=ON;");
|
||||
sqlite3_busy_timeout(p->db, 10000);
|
||||
run_sql(p, "PRAGMA synchronous=OFF;");
|
||||
run_sql(p, "ATTACH 'tt4-test%d.db' AS aux1", aOrder[x][1]);
|
||||
run_sql(p, "ATTACH 'tt4-test%d.db' AS aux2", aOrder[x][2]);
|
||||
}
|
||||
|
||||
/*
|
||||
** Close the worker database connection
|
||||
*/
|
||||
static void worker_close_connection(WorkerInfo *p){
|
||||
if( p->db ){
|
||||
worker_trace(p, "close");
|
||||
sqlite3_close(p->db);
|
||||
p->db = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Delete all content in the three databases associated with a
|
||||
** single thread. Make this happen all in a single transaction if
|
||||
** inTrans is true, or separately for each database if inTrans is
|
||||
** false.
|
||||
*/
|
||||
static void worker_delete_all_content(WorkerInfo *p, int inTrans){
|
||||
if( inTrans ){
|
||||
pthread_mutex_lock(p->pWrMutex);
|
||||
run_sql(p, "BEGIN");
|
||||
run_sql(p, "DELETE FROM t1 WHERE tid=%d", p->tid);
|
||||
run_sql(p, "DELETE FROM t2 WHERE tid=%d", p->tid);
|
||||
run_sql(p, "DELETE FROM t3 WHERE tid=%d", p->tid);
|
||||
run_sql(p, "COMMIT");
|
||||
pthread_mutex_unlock(p->pWrMutex);
|
||||
p->nTest++;
|
||||
}else{
|
||||
pthread_mutex_lock(p->pWrMutex);
|
||||
run_sql(p, "DELETE FROM t1 WHERE tid=%d", p->tid);
|
||||
pthread_mutex_unlock(p->pWrMutex);
|
||||
p->nTest++;
|
||||
pthread_mutex_lock(p->pWrMutex);
|
||||
run_sql(p, "DELETE FROM t2 WHERE tid=%d", p->tid);
|
||||
pthread_mutex_unlock(p->pWrMutex);
|
||||
p->nTest++;
|
||||
pthread_mutex_lock(p->pWrMutex);
|
||||
run_sql(p, "DELETE FROM t3 WHERE tid=%d", p->tid);
|
||||
pthread_mutex_unlock(p->pWrMutex);
|
||||
p->nTest++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Create rows mn through mx in table iTab for the given worker
|
||||
*/
|
||||
static void worker_add_content(WorkerInfo *p, int mn, int mx, int iTab){
|
||||
char *zTabDef;
|
||||
switch( iTab ){
|
||||
case 1: zTabDef = "t1(tid,sp,a,b,c)"; break;
|
||||
case 2: zTabDef = "t2(tid,sp,d,e,f)"; break;
|
||||
case 3: zTabDef = "t3(tid,sp,x,y,z)"; break;
|
||||
}
|
||||
pthread_mutex_lock(p->pWrMutex);
|
||||
run_sql(p,
|
||||
"WITH RECURSIVE\n"
|
||||
" c(i) AS (VALUES(%d) UNION ALL SELECT i+1 FROM c WHERE i<%d)\n"
|
||||
"INSERT INTO %s SELECT %d, zeroblob(3000), i, printf('%%d',i), i FROM c;",
|
||||
mn, mx, zTabDef, p->tid
|
||||
);
|
||||
pthread_mutex_unlock(p->pWrMutex);
|
||||
p->nTest++;
|
||||
}
|
||||
|
||||
/*
|
||||
** Set an error message on a worker
|
||||
*/
|
||||
static void worker_error(WorkerInfo *p, const char *zFormat, ...){
|
||||
va_list ap;
|
||||
p->nErr++;
|
||||
sqlite3_free(p->zMsg);
|
||||
va_start(ap, zFormat);
|
||||
p->zMsg = sqlite3_vmprintf(zFormat, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/*
|
||||
** Each thread runs the following function.
|
||||
*/
|
||||
static void *worker_thread(void *pArg){
|
||||
WorkerInfo *p = (WorkerInfo*)pArg;
|
||||
int iOuter;
|
||||
int i;
|
||||
int rc;
|
||||
sqlite3_stmt *pStmt;
|
||||
|
||||
printf("worker %d startup\n", p->tid); fflush(stdout);
|
||||
for(iOuter=1; iOuter<=p->nWorker; iOuter++){
|
||||
worker_open_connection(p, iOuter);
|
||||
for(i=0; i<4; i++){
|
||||
worker_add_content(p, i*100+1, (i+1)*100, (p->tid+iOuter)%3 + 1);
|
||||
worker_add_content(p, i*100+1, (i+1)*100, (p->tid+iOuter+1)%3 + 1);
|
||||
worker_add_content(p, i*100+1, (i+1)*100, (p->tid+iOuter+2)%3 + 1);
|
||||
}
|
||||
|
||||
pStmt = prep_sql(p->db, "SELECT count(a) FROM t1 WHERE tid=%d", p->tid);
|
||||
worker_trace(p, "query [%s]", sqlite3_sql(pStmt));
|
||||
rc = sqlite3_step(pStmt);
|
||||
if( rc!=SQLITE_ROW ){
|
||||
worker_error(p, "Failed to step: %s", sqlite3_sql(pStmt));
|
||||
}else if( sqlite3_column_int(pStmt, 0)!=400 ){
|
||||
worker_error(p, "Wrong result: %d", sqlite3_column_int(pStmt,0));
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
if( p->nErr ) break;
|
||||
|
||||
if( ((iOuter+p->tid)%3)==0 ){
|
||||
sqlite3_db_release_memory(p->db);
|
||||
p->nTest++;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(p->pWrMutex);
|
||||
run_sql(p, "BEGIN;");
|
||||
run_sql(p, "UPDATE t1 SET c=NULL WHERE a=55");
|
||||
run_sql(p, "UPDATE t2 SET f=NULL WHERE d=42");
|
||||
run_sql(p, "UPDATE t3 SET z=NULL WHERE x=31");
|
||||
run_sql(p, "ROLLBACK;");
|
||||
p->nTest++;
|
||||
pthread_mutex_unlock(p->pWrMutex);
|
||||
|
||||
|
||||
if( iOuter==p->tid ){
|
||||
pthread_mutex_lock(p->pWrMutex);
|
||||
run_sql(p, "VACUUM");
|
||||
pthread_mutex_unlock(p->pWrMutex);
|
||||
}
|
||||
|
||||
pStmt = prep_sql(p->db,
|
||||
"SELECT t1.rowid, t2.rowid, t3.rowid"
|
||||
" FROM t1, t2, t3"
|
||||
" WHERE t1.tid=%d AND t2.tid=%d AND t3.tid=%d"
|
||||
" AND t1.a<>t2.d AND t2.d<>t3.x"
|
||||
" ORDER BY 1, 2, 3"
|
||||
,p->tid, p->tid, p->tid);
|
||||
worker_trace(p, "query [%s]", sqlite3_sql(pStmt));
|
||||
for(i=0; i<p->nWorker; i++){
|
||||
rc = sqlite3_step(pStmt);
|
||||
if( rc!=SQLITE_ROW ){
|
||||
worker_error(p, "Failed to step: %s", sqlite3_sql(pStmt));
|
||||
break;
|
||||
}
|
||||
sched_yield();
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
if( p->nErr ) break;
|
||||
|
||||
worker_delete_all_content(p, (p->tid+iOuter)%2);
|
||||
worker_close_connection(p);
|
||||
p->db = 0;
|
||||
}
|
||||
worker_close_connection(p);
|
||||
printf("worker %d finished\n", p->tid); fflush(stdout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv){
|
||||
int nWorker = 0; /* Number of worker threads */
|
||||
int i; /* Loop counter */
|
||||
WorkerInfo *aInfo; /* Information for each worker */
|
||||
unsigned wkrFlags = 0; /* Default worker flags */
|
||||
int nErr = 0; /* Number of errors */
|
||||
int nTest = 0; /* Number of tests */
|
||||
int rc; /* Return code */
|
||||
sqlite3 *db = 0; /* Main database connection */
|
||||
pthread_mutex_t wrMutex; /* The write serialization mutex */
|
||||
WorkerInfo infoTop; /* WorkerInfo for the main thread */
|
||||
WorkerInfo *p; /* Pointer to infoTop */
|
||||
|
||||
sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
|
||||
for(i=1; i<argc; i++){
|
||||
const char *z = argv[i];
|
||||
if( z[0]=='-' ){
|
||||
if( z[1]=='-' && z[2]!=0 ) z++;
|
||||
if( strcmp(z,"-multithread")==0 ){
|
||||
sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
|
||||
wkrFlags &= ~TT4_SERIALIZED;
|
||||
}else if( strcmp(z,"-serialized")==0 ){
|
||||
sqlite3_config(SQLITE_CONFIG_SERIALIZED);
|
||||
wkrFlags |= TT4_SERIALIZED;
|
||||
}else if( strcmp(z,"-wal")==0 ){
|
||||
wkrFlags |= TT4_WAL;
|
||||
}else if( strcmp(z,"-trace")==0 ){
|
||||
wkrFlags |= TT4_TRACE;
|
||||
}else{
|
||||
fprintf(stderr, "unknown command-line option: %s\n", argv[i]);
|
||||
exit(1);
|
||||
}
|
||||
}else if( z[0]>='1' && z[0]<='9' && nWorker==0 ){
|
||||
nWorker = atoi(z);
|
||||
if( nWorker<2 ){
|
||||
fprintf(stderr, "minimum of 2 threads\n");
|
||||
exit(1);
|
||||
}
|
||||
}else{
|
||||
fprintf(stderr, "extra command-line argument: \"%s\"\n", argv[i]);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if( nWorker==0 ){
|
||||
fprintf(stderr,
|
||||
"usage: %s ?OPTIONS? N\n"
|
||||
"N is the number of threads and must be at least 2.\n"
|
||||
"Options:\n"
|
||||
" --serialized\n"
|
||||
" --multithread\n"
|
||||
" --wal\n"
|
||||
" --trace\n"
|
||||
,argv[0]
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
if( !sqlite3_threadsafe() ){
|
||||
fprintf(stderr, "requires a threadsafe build of SQLite\n");
|
||||
exit(1);
|
||||
}
|
||||
sqlite3_initialize();
|
||||
sqlite3_enable_shared_cache(1);
|
||||
pthread_mutex_init(&wrMutex, 0);
|
||||
|
||||
/* Initialize the test database files */
|
||||
(void)unlink("tt4-test1.db");
|
||||
(void)unlink("tt4-test2.db");
|
||||
(void)unlink("tt4-test3.db");
|
||||
rc = sqlite3_open("tt4-test1.db", &db);
|
||||
if( rc!=SQLITE_OK ){
|
||||
fprintf(stderr, "Unable to open test database: tt4-test2.db\n");
|
||||
exit(1);
|
||||
}
|
||||
memset(&infoTop, 0, sizeof(infoTop));
|
||||
infoTop.db = db;
|
||||
infoTop.wkrFlags = wkrFlags;
|
||||
p = &infoTop;
|
||||
if( wkrFlags & TT4_WAL ){
|
||||
run_sql(p, "PRAGMA journal_mode=WAL");
|
||||
}
|
||||
run_sql(p, "PRAGMA synchronous=OFF");
|
||||
run_sql(p, "CREATE TABLE IF NOT EXISTS t1(tid INTEGER, sp, a, b, c)");
|
||||
run_sql(p, "CREATE INDEX t1tid ON t1(tid)");
|
||||
run_sql(p, "CREATE INDEX t1ab ON t1(a,b)");
|
||||
run_sql(p, "ATTACH 'tt4-test2.db' AS 'test2'");
|
||||
run_sql(p, "CREATE TABLE IF NOT EXISTS test2.t2(tid INTEGER, sp, d, e, f)");
|
||||
run_sql(p, "CREATE INDEX test2.t2tid ON t2(tid)");
|
||||
run_sql(p, "CREATE INDEX test2.t2de ON t2(d,e)");
|
||||
run_sql(p, "ATTACH 'tt4-test3.db' AS 'test3'");
|
||||
run_sql(p, "CREATE TABLE IF NOT EXISTS test3.t3(tid INTEGER, sp, x, y, z)");
|
||||
run_sql(p, "CREATE INDEX test3.t3tid ON t3(tid)");
|
||||
run_sql(p, "CREATE INDEX test3.t3xy ON t3(x,y)");
|
||||
aInfo = safe_malloc( sizeof(*aInfo)*nWorker );
|
||||
memset(aInfo, 0, sizeof(*aInfo)*nWorker);
|
||||
for(i=0; i<nWorker; i++){
|
||||
aInfo[i].tid = i+1;
|
||||
aInfo[i].nWorker = nWorker;
|
||||
aInfo[i].wkrFlags = wkrFlags;
|
||||
aInfo[i].mainDb = db;
|
||||
aInfo[i].pWrMutex = &wrMutex;
|
||||
rc = pthread_create(&aInfo[i].id, 0, worker_thread, &aInfo[i]);
|
||||
if( rc!=0 ){
|
||||
fprintf(stderr, "thread creation failed for thread %d\n", i+1);
|
||||
exit(1);
|
||||
}
|
||||
sched_yield();
|
||||
}
|
||||
for(i=0; i<nWorker; i++){
|
||||
pthread_join(aInfo[i].id, 0);
|
||||
printf("Joined thread %d: %d errors in %d tests",
|
||||
aInfo[i].tid, aInfo[i].nErr, aInfo[i].nTest);
|
||||
if( aInfo[i].zMsg ){
|
||||
printf(": %s\n", aInfo[i].zMsg);
|
||||
}else{
|
||||
printf("\n");
|
||||
}
|
||||
nErr += aInfo[i].nErr;
|
||||
nTest += aInfo[i].nTest;
|
||||
fflush(stdout);
|
||||
}
|
||||
sqlite3_close(db);
|
||||
sqlite3_free(aInfo);
|
||||
printf("Total %d errors in %d tests\n", nErr, nTest);
|
||||
return nErr;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** 2011-02-02
|
||||
** 2001 September 15
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
@@ -66,7 +66,7 @@ static int checkpoint_starvation_walhook(
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static char *checkpoint_starvation_reader(int iTid, void *pArg){
|
||||
static char *checkpoint_starvation_reader(int iTid, int iArg){
|
||||
Error err = {0};
|
||||
Sqlite db = {0};
|
||||
|
||||
@@ -146,3 +146,5 @@ static void checkpoint_starvation_2(int nMs){
|
||||
}
|
||||
print_and_free_err(&err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
** 2014 December 9
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** create_drop_index_1
|
||||
*/
|
||||
|
||||
|
||||
static char *create_drop_index_thread(int iTid, void *pArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
|
||||
while( !timetostop(&err) ){
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
|
||||
sql_script(&err, &db,
|
||||
"DROP INDEX IF EXISTS i1;"
|
||||
"DROP INDEX IF EXISTS i2;"
|
||||
"DROP INDEX IF EXISTS i3;"
|
||||
"DROP INDEX IF EXISTS i4;"
|
||||
|
||||
"CREATE INDEX IF NOT EXISTS i1 ON t11(a);"
|
||||
"CREATE INDEX IF NOT EXISTS i2 ON t11(b);"
|
||||
"CREATE INDEX IF NOT EXISTS i3 ON t11(c);"
|
||||
"CREATE INDEX IF NOT EXISTS i4 ON t11(d);"
|
||||
|
||||
"SELECT * FROM t11 ORDER BY a;"
|
||||
"SELECT * FROM t11 ORDER BY b;"
|
||||
"SELECT * FROM t11 ORDER BY c;"
|
||||
"SELECT * FROM t11 ORDER BY d;"
|
||||
);
|
||||
clear_error(&err, SQLITE_LOCKED);
|
||||
|
||||
closedb(&err, &db);
|
||||
}
|
||||
|
||||
print_and_free_err(&err);
|
||||
return sqlite3_mprintf("ok");
|
||||
}
|
||||
|
||||
static void create_drop_index_1(int nMs){
|
||||
Error err = {0};
|
||||
Sqlite db = {0};
|
||||
Threadset threads = {0};
|
||||
|
||||
opendb(&err, &db, "test.db", 1);
|
||||
sql_script(&err, &db,
|
||||
"CREATE TABLE t11(a, b, c, d);"
|
||||
"WITH data(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM data WHERE x<100) "
|
||||
"INSERT INTO t11 SELECT x,x,x,x FROM data;"
|
||||
);
|
||||
closedb(&err, &db);
|
||||
|
||||
setstoptime(&err, nMs);
|
||||
|
||||
sqlite3_enable_shared_cache(1);
|
||||
launch_thread(&err, &threads, create_drop_index_thread, 0);
|
||||
launch_thread(&err, &threads, create_drop_index_thread, 0);
|
||||
launch_thread(&err, &threads, create_drop_index_thread, 0);
|
||||
launch_thread(&err, &threads, create_drop_index_thread, 0);
|
||||
launch_thread(&err, &threads, create_drop_index_thread, 0);
|
||||
|
||||
join_all_threads(&err, &threads);
|
||||
sqlite3_enable_shared_cache(0);
|
||||
print_and_free_err(&err);
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
** 2014 December 9
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** lookaside1
|
||||
*/
|
||||
|
||||
/*
|
||||
** The test in this file attempts to expose a specific race condition
|
||||
** that is suspected to exist at time of writing.
|
||||
*/
|
||||
|
||||
static char *lookaside1_thread_reader(int iTid, void *pArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
|
||||
while( !timetostop(&err) ){
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
int rc;
|
||||
|
||||
sqlite3_prepare_v2(db.db, "SELECT 1 FROM t1", -1, &pStmt, 0);
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
execsql(&err, &db, "SELECT length(x||y||z) FROM t2");
|
||||
}
|
||||
rc = sqlite3_finalize(pStmt);
|
||||
if( err.rc==SQLITE_OK && rc!=SQLITE_OK ){
|
||||
sqlite_error(&err, &db, "finalize");
|
||||
}
|
||||
}
|
||||
|
||||
closedb(&err, &db);
|
||||
print_and_free_err(&err);
|
||||
return sqlite3_mprintf("ok");
|
||||
}
|
||||
|
||||
static char *lookaside1_thread_writer(int iTid, void *pArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
|
||||
do{
|
||||
sql_script(&err, &db,
|
||||
"BEGIN;"
|
||||
"UPDATE t3 SET i=i+1 WHERE x=1;"
|
||||
"ROLLBACK;"
|
||||
);
|
||||
}while( !timetostop(&err) );
|
||||
|
||||
closedb(&err, &db);
|
||||
print_and_free_err(&err);
|
||||
return sqlite3_mprintf("ok");
|
||||
}
|
||||
|
||||
|
||||
static void lookaside1(int nMs){
|
||||
Error err = {0};
|
||||
Sqlite db = {0};
|
||||
Threadset threads = {0};
|
||||
|
||||
opendb(&err, &db, "test.db", 1);
|
||||
sql_script(&err, &db,
|
||||
"CREATE TABLE t1(x PRIMARY KEY) WITHOUT ROWID;"
|
||||
"WITH data(x,y) AS ("
|
||||
" SELECT 1, quote(randomblob(750)) UNION ALL "
|
||||
" SELECT x*2, y||y FROM data WHERE x<5) "
|
||||
"INSERT INTO t1 SELECT y FROM data;"
|
||||
|
||||
"CREATE TABLE t3(x PRIMARY KEY,i) WITHOUT ROWID;"
|
||||
"INSERT INTO t3 VALUES(1, 1);"
|
||||
|
||||
"CREATE TABLE t2(x,y,z);"
|
||||
"INSERT INTO t2 VALUES(randomblob(50), randomblob(50), randomblob(50));"
|
||||
);
|
||||
closedb(&err, &db);
|
||||
|
||||
setstoptime(&err, nMs);
|
||||
|
||||
sqlite3_enable_shared_cache(1);
|
||||
launch_thread(&err, &threads, lookaside1_thread_reader, 0);
|
||||
launch_thread(&err, &threads, lookaside1_thread_reader, 0);
|
||||
launch_thread(&err, &threads, lookaside1_thread_reader, 0);
|
||||
launch_thread(&err, &threads, lookaside1_thread_reader, 0);
|
||||
launch_thread(&err, &threads, lookaside1_thread_reader, 0);
|
||||
launch_thread(&err, &threads, lookaside1_thread_writer, 0);
|
||||
join_all_threads(&err, &threads);
|
||||
sqlite3_enable_shared_cache(0);
|
||||
print_and_free_err(&err);
|
||||
}
|
||||
@@ -1,368 +0,0 @@
|
||||
/*
|
||||
** 2014 December 9
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
** Thread 1. CREATE and DROP a table.
|
||||
*/
|
||||
static char *stress_thread_1(int iTid, void *pArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
while( !timetostop(&err) ){
|
||||
sql_script(&err, &db, "CREATE TABLE IF NOT EXISTS t1(a PRIMARY KEY, b)");
|
||||
clear_error(&err, SQLITE_LOCKED);
|
||||
sql_script(&err, &db, "DROP TABLE IF EXISTS t1");
|
||||
clear_error(&err, SQLITE_LOCKED);
|
||||
}
|
||||
closedb(&err, &db);
|
||||
print_and_free_err(&err);
|
||||
return sqlite3_mprintf("ok");
|
||||
}
|
||||
|
||||
/*
|
||||
** Thread 2. Open and close database connections.
|
||||
*/
|
||||
static char *stress_thread_2(int iTid, void *pArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
while( !timetostop(&err) ){
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
sql_script(&err, &db, "SELECT * FROM sqlite_master;");
|
||||
clear_error(&err, SQLITE_LOCKED);
|
||||
closedb(&err, &db);
|
||||
}
|
||||
print_and_free_err(&err);
|
||||
return sqlite3_mprintf("ok");
|
||||
}
|
||||
|
||||
/*
|
||||
** Thread 3. Attempt many small SELECT statements.
|
||||
*/
|
||||
static char *stress_thread_3(int iTid, void *pArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
|
||||
int i1 = 0;
|
||||
int i2 = 0;
|
||||
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
while( !timetostop(&err) ){
|
||||
sql_script(&err, &db, "SELECT * FROM t1 ORDER BY a;");
|
||||
i1++;
|
||||
if( err.rc ) i2++;
|
||||
clear_error(&err, SQLITE_LOCKED);
|
||||
clear_error(&err, SQLITE_ERROR);
|
||||
}
|
||||
closedb(&err, &db);
|
||||
print_and_free_err(&err);
|
||||
return sqlite3_mprintf("read t1 %d/%d attempts", i2, i1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Thread 5. Attempt INSERT statements.
|
||||
*/
|
||||
static char *stress_thread_4(int iTid, void *pArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
int i1 = 0;
|
||||
int i2 = 0;
|
||||
int iArg = PTR2INT(pArg);
|
||||
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
while( !timetostop(&err) ){
|
||||
if( iArg ){
|
||||
closedb(&err, &db);
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
}
|
||||
sql_script(&err, &db,
|
||||
"WITH loop(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM loop LIMIT 200) "
|
||||
"INSERT INTO t1 VALUES(randomblob(60), randomblob(60));"
|
||||
);
|
||||
i1++;
|
||||
if( err.rc ) i2++;
|
||||
clear_error(&err, SQLITE_LOCKED);
|
||||
clear_error(&err, SQLITE_ERROR);
|
||||
}
|
||||
closedb(&err, &db);
|
||||
print_and_free_err(&err);
|
||||
return sqlite3_mprintf("wrote t1 %d/%d attempts", i2, i1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Thread 6. Attempt DELETE operations.
|
||||
*/
|
||||
static char *stress_thread_5(int iTid, void *pArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
int iArg = PTR2INT(pArg);
|
||||
|
||||
int i1 = 0;
|
||||
int i2 = 0;
|
||||
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
while( !timetostop(&err) ){
|
||||
i64 i = (i1 % 4);
|
||||
if( iArg ){
|
||||
closedb(&err, &db);
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
}
|
||||
execsql(&err, &db, "DELETE FROM t1 WHERE (rowid % 4)==:i", &i);
|
||||
i1++;
|
||||
if( err.rc ) i2++;
|
||||
clear_error(&err, SQLITE_LOCKED);
|
||||
}
|
||||
closedb(&err, &db);
|
||||
print_and_free_err(&err);
|
||||
return sqlite3_mprintf("deleted from t1 %d/%d attempts", i2, i1);
|
||||
}
|
||||
|
||||
|
||||
static void stress1(int nMs){
|
||||
Error err = {0};
|
||||
Threadset threads = {0};
|
||||
|
||||
setstoptime(&err, nMs);
|
||||
sqlite3_enable_shared_cache(1);
|
||||
|
||||
launch_thread(&err, &threads, stress_thread_1, 0);
|
||||
launch_thread(&err, &threads, stress_thread_1, 0);
|
||||
|
||||
launch_thread(&err, &threads, stress_thread_2, 0);
|
||||
launch_thread(&err, &threads, stress_thread_2, 0);
|
||||
|
||||
launch_thread(&err, &threads, stress_thread_3, 0);
|
||||
launch_thread(&err, &threads, stress_thread_3, 0);
|
||||
|
||||
launch_thread(&err, &threads, stress_thread_4, 0);
|
||||
launch_thread(&err, &threads, stress_thread_4, 0);
|
||||
|
||||
launch_thread(&err, &threads, stress_thread_5, 0);
|
||||
launch_thread(&err, &threads, stress_thread_5, (void*)1);
|
||||
|
||||
join_all_threads(&err, &threads);
|
||||
sqlite3_enable_shared_cache(0);
|
||||
|
||||
print_and_free_err(&err);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
***************************************************************************
|
||||
** Start of test case "stress2"
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** 1. CREATE TABLE statements.
|
||||
** 2. DROP TABLE statements.
|
||||
** 3. Small SELECT statements.
|
||||
** 4. Big SELECT statements.
|
||||
** 5. Small INSERT statements.
|
||||
** 6. Big INSERT statements.
|
||||
** 7. Small UPDATE statements.
|
||||
** 8. Big UPDATE statements.
|
||||
** 9. Small DELETE statements.
|
||||
** 10. Big DELETE statements.
|
||||
** 11. VACUUM.
|
||||
** 14. Integrity-check.
|
||||
** 17. Switch the journal mode from delete to wal and back again.
|
||||
** 19. Open and close database connections rapidly.
|
||||
*/
|
||||
|
||||
#define STRESS2_TABCNT 5 /* count1 in SDS test */
|
||||
|
||||
#define STRESS2_COUNT2 200 /* count2 in SDS test */
|
||||
#define STRESS2_COUNT3 57 /* count2 in SDS test */
|
||||
|
||||
static void stress2_workload1(Error *pErr, Sqlite *pDb, int i){
|
||||
int iTab = (i % (STRESS2_TABCNT-1)) + 1;
|
||||
sql_script_printf(pErr, pDb,
|
||||
"CREATE TABLE IF NOT EXISTS t%d(x PRIMARY KEY, y, z);", iTab
|
||||
);
|
||||
}
|
||||
|
||||
static void stress2_workload2(Error *pErr, Sqlite *pDb, int i){
|
||||
int iTab = (i % (STRESS2_TABCNT-1)) + 1;
|
||||
sql_script_printf(pErr, pDb, "DROP TABLE IF EXISTS t%d;", iTab);
|
||||
}
|
||||
|
||||
static void stress2_workload3(Error *pErr, Sqlite *pDb, int i){
|
||||
sql_script(pErr, pDb, "SELECT * FROM t0 WHERE z = 'small'");
|
||||
}
|
||||
|
||||
static void stress2_workload4(Error *pErr, Sqlite *pDb, int i){
|
||||
sql_script(pErr, pDb, "SELECT * FROM t0 WHERE z = 'big'");
|
||||
}
|
||||
|
||||
static void stress2_workload5(Error *pErr, Sqlite *pDb, int i){
|
||||
sql_script(pErr, pDb,
|
||||
"INSERT INTO t0 VALUES(hex(random()), hex(randomblob(200)), 'small');"
|
||||
);
|
||||
}
|
||||
|
||||
static void stress2_workload6(Error *pErr, Sqlite *pDb, int i){
|
||||
sql_script(pErr, pDb,
|
||||
"INSERT INTO t0 VALUES(hex(random()), hex(randomblob(57)), 'big');"
|
||||
);
|
||||
}
|
||||
|
||||
static void stress2_workload7(Error *pErr, Sqlite *pDb, int i){
|
||||
sql_script_printf(pErr, pDb,
|
||||
"UPDATE t0 SET y = hex(randomblob(200)) "
|
||||
"WHERE x LIKE hex((%d %% 5)) AND z='small';"
|
||||
,i
|
||||
);
|
||||
}
|
||||
static void stress2_workload8(Error *pErr, Sqlite *pDb, int i){
|
||||
sql_script_printf(pErr, pDb,
|
||||
"UPDATE t0 SET y = hex(randomblob(57)) "
|
||||
"WHERE x LIKE hex(%d %% 5) AND z='big';"
|
||||
,i
|
||||
);
|
||||
}
|
||||
|
||||
static void stress2_workload9(Error *pErr, Sqlite *pDb, int i){
|
||||
sql_script_printf(pErr, pDb,
|
||||
"DELETE FROM t0 WHERE x LIKE hex(%d %% 5) AND z='small';", i
|
||||
);
|
||||
}
|
||||
static void stress2_workload10(Error *pErr, Sqlite *pDb, int i){
|
||||
sql_script_printf(pErr, pDb,
|
||||
"DELETE FROM t0 WHERE x LIKE hex(%d %% 5) AND z='big';", i
|
||||
);
|
||||
}
|
||||
|
||||
static void stress2_workload11(Error *pErr, Sqlite *pDb, int i){
|
||||
sql_script(pErr, pDb, "VACUUM");
|
||||
}
|
||||
|
||||
static void stress2_workload14(Error *pErr, Sqlite *pDb, int i){
|
||||
sql_script(pErr, pDb, "PRAGMA integrity_check");
|
||||
}
|
||||
|
||||
static void stress2_workload17(Error *pErr, Sqlite *pDb, int i){
|
||||
sql_script_printf(pErr, pDb,
|
||||
"PRAGMA journal_mode = %q", (i%2) ? "delete" : "wal"
|
||||
);
|
||||
}
|
||||
|
||||
static char *stress2_workload19(int iTid, void *pArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
const char *zDb = (const char*)pArg;
|
||||
while( !timetostop(&err) ){
|
||||
opendb(&err, &db, zDb, 0);
|
||||
sql_script(&err, &db, "SELECT * FROM sqlite_master;");
|
||||
clear_error(&err, SQLITE_LOCKED);
|
||||
closedb(&err, &db);
|
||||
}
|
||||
print_and_free_err(&err);
|
||||
return sqlite3_mprintf("ok");
|
||||
}
|
||||
|
||||
|
||||
typedef struct Stress2Ctx Stress2Ctx;
|
||||
struct Stress2Ctx {
|
||||
const char *zDb;
|
||||
void (*xProc)(Error*, Sqlite*, int);
|
||||
};
|
||||
|
||||
static char *stress2_thread_wrapper(int iTid, void *pArg){
|
||||
Stress2Ctx *pCtx = (Stress2Ctx*)pArg;
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
int i1 = 0;
|
||||
int i2 = 0;
|
||||
|
||||
while( !timetostop(&err) ){
|
||||
int cnt;
|
||||
opendb(&err, &db, pCtx->zDb, 0);
|
||||
for(cnt=0; err.rc==SQLITE_OK && cnt<STRESS2_TABCNT; cnt++){
|
||||
pCtx->xProc(&err, &db, i1);
|
||||
i2 += (err.rc==SQLITE_OK);
|
||||
clear_error(&err, SQLITE_LOCKED);
|
||||
i1++;
|
||||
}
|
||||
closedb(&err, &db);
|
||||
}
|
||||
|
||||
print_and_free_err(&err);
|
||||
return sqlite3_mprintf("ok %d/%d", i2, i1);
|
||||
}
|
||||
|
||||
static void stress2_launch_thread_loop(
|
||||
Error *pErr, /* IN/OUT: Error code */
|
||||
Threadset *pThreads, /* Thread set */
|
||||
const char *zDb, /* Database name */
|
||||
void (*x)(Error*,Sqlite*,int) /* Run this until error or timeout */
|
||||
){
|
||||
Stress2Ctx *pCtx = sqlite3_malloc(sizeof(Stress2Ctx));
|
||||
pCtx->zDb = zDb;
|
||||
pCtx->xProc = x;
|
||||
launch_thread(pErr, pThreads, stress2_thread_wrapper, (void*)pCtx);
|
||||
}
|
||||
|
||||
static void stress2(int nMs){
|
||||
struct Stress2Task {
|
||||
void (*x)(Error*,Sqlite*,int);
|
||||
} aTask[] = {
|
||||
{ stress2_workload1 },
|
||||
{ stress2_workload2 },
|
||||
{ stress2_workload3 },
|
||||
{ stress2_workload4 },
|
||||
{ stress2_workload5 },
|
||||
{ stress2_workload6 },
|
||||
{ stress2_workload7 },
|
||||
{ stress2_workload8 },
|
||||
{ stress2_workload9 },
|
||||
{ stress2_workload10 },
|
||||
{ stress2_workload11 },
|
||||
{ stress2_workload14 },
|
||||
{ stress2_workload17 },
|
||||
};
|
||||
const char *zDb = "test.db";
|
||||
|
||||
int i;
|
||||
Error err = {0};
|
||||
Sqlite db = {0};
|
||||
Threadset threads = {0};
|
||||
|
||||
/* To make sure the db file is empty before commencing */
|
||||
opendb(&err, &db, zDb, 1);
|
||||
sql_script(&err, &db,
|
||||
"CREATE TABLE IF NOT EXISTS t0(x PRIMARY KEY, y, z);"
|
||||
"CREATE INDEX IF NOT EXISTS i0 ON t0(y);"
|
||||
);
|
||||
closedb(&err, &db);
|
||||
|
||||
setstoptime(&err, nMs);
|
||||
sqlite3_enable_shared_cache(1);
|
||||
|
||||
for(i=0; i<sizeof(aTask)/sizeof(aTask[0]); i++){
|
||||
stress2_launch_thread_loop(&err, &threads, zDb, aTask[i].x);
|
||||
}
|
||||
launch_thread(&err, &threads, stress2_workload19, (void*)zDb);
|
||||
launch_thread(&err, &threads, stress2_workload19, (void*)zDb);
|
||||
|
||||
join_all_threads(&err, &threads);
|
||||
sqlite3_enable_shared_cache(0);
|
||||
print_and_free_err(&err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
** 2014 December 9
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file contains multi-threaded tests that use shared-cache and
|
||||
** the VACUUM command.
|
||||
**
|
||||
** Tests:
|
||||
**
|
||||
** vacuum1
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
static char *vacuum1_thread_writer(int iTid, void *pArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
i64 i = 0;
|
||||
|
||||
while( !timetostop(&err) ){
|
||||
i++;
|
||||
|
||||
/* Insert lots of rows. Then delete some. */
|
||||
execsql(&err, &db,
|
||||
"WITH loop(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM loop WHERE i<100) "
|
||||
"INSERT INTO t1 SELECT randomblob(50), randomblob(2500) FROM loop"
|
||||
);
|
||||
|
||||
/* Delete lots of rows */
|
||||
execsql(&err, &db, "DELETE FROM t1 WHERE rowid = :i", &i);
|
||||
clear_error(&err, SQLITE_LOCKED);
|
||||
|
||||
/* Select the rows */
|
||||
execsql(&err, &db, "SELECT * FROM t1 ORDER BY x");
|
||||
clear_error(&err, SQLITE_LOCKED);
|
||||
}
|
||||
|
||||
closedb(&err, &db);
|
||||
print_and_free_err(&err);
|
||||
return sqlite3_mprintf("ok");
|
||||
}
|
||||
|
||||
static char *vacuum1_thread_vacuumer(int iTid, void *pArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
|
||||
do{
|
||||
sql_script(&err, &db, "VACUUM");
|
||||
clear_error(&err, SQLITE_LOCKED);
|
||||
}while( !timetostop(&err) );
|
||||
|
||||
closedb(&err, &db);
|
||||
print_and_free_err(&err);
|
||||
return sqlite3_mprintf("ok");
|
||||
}
|
||||
|
||||
static void vacuum1(int nMs){
|
||||
Error err = {0};
|
||||
Sqlite db = {0};
|
||||
Threadset threads = {0};
|
||||
|
||||
opendb(&err, &db, "test.db", 1);
|
||||
sql_script(&err, &db,
|
||||
"CREATE TABLE t1(x PRIMARY KEY, y BLOB);"
|
||||
"CREATE INDEX i1 ON t1(y);"
|
||||
);
|
||||
closedb(&err, &db);
|
||||
|
||||
setstoptime(&err, nMs);
|
||||
|
||||
sqlite3_enable_shared_cache(1);
|
||||
launch_thread(&err, &threads, vacuum1_thread_writer, 0);
|
||||
launch_thread(&err, &threads, vacuum1_thread_writer, 0);
|
||||
launch_thread(&err, &threads, vacuum1_thread_writer, 0);
|
||||
launch_thread(&err, &threads, vacuum1_thread_vacuumer, 0);
|
||||
join_all_threads(&err, &threads);
|
||||
sqlite3_enable_shared_cache(0);
|
||||
|
||||
print_and_free_err(&err);
|
||||
}
|
||||
+5
-6
@@ -363,10 +363,9 @@ foreach {testprefix do_wal_checkpoint} {
|
||||
code2 $do_wal_checkpoint
|
||||
code3 $do_wal_checkpoint
|
||||
|
||||
do_test 4.$tn.1 {
|
||||
do_test 3.$tn.1 {
|
||||
sql1 {
|
||||
PRAGMA page_size = 1024;
|
||||
PRAGMA auto_vacuum = 0;
|
||||
PRAGMA journal_mode = WAL;
|
||||
PRAGMA synchronous = normal;
|
||||
CREATE TABLE t1(x, y);
|
||||
@@ -377,14 +376,14 @@ foreach {testprefix do_wal_checkpoint} {
|
||||
file size test.db-wal
|
||||
} [wal_file_size 8 1024]
|
||||
|
||||
do_test 4.$tn.2 { do_wal_checkpoint db -mode truncate } {0 0 0}
|
||||
do_test 4.$tn.3 { file size test.db-wal } 0
|
||||
do_test 3.$tn.2 { do_wal_checkpoint db -mode truncate } {0 0 0}
|
||||
do_test 3.$tn.3 { file size test.db-wal } 0
|
||||
|
||||
do_test 4.$tn.4 {
|
||||
do_test 3.$tn.4 {
|
||||
sql2 { SELECT * FROM t1 }
|
||||
} {1 2 3 4}
|
||||
|
||||
do_test 4.$tn.5 {
|
||||
do_test 3.$tn.5 {
|
||||
sql2 { INSERT INTO t1 VALUES('a', 'b') }
|
||||
file size test.db-wal
|
||||
} [wal_file_size 2 1024]
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
# 2014-12-28
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Verify that WITHOUT ROWID tables work correctly when the PRIMARY KEY
|
||||
# has redundant columns.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
do_execsql_test without_rowid6-100 {
|
||||
CREATE TABLE t1(a,b,c,d,e, PRIMARY KEY(a,b,c,a,b,c,d,a,b,c)) WITHOUT ROWID;
|
||||
CREATE INDEX t1a ON t1(b, b);
|
||||
WITH RECURSIVE
|
||||
c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<1000)
|
||||
INSERT INTO t1(a,b,c,d,e) SELECT i, i+1000, printf('x%dy',i), 0, 0 FROM c;
|
||||
ANALYZE;
|
||||
} {}
|
||||
do_execsql_test without_rowid6-110 {
|
||||
SELECT c FROM t1 WHERE a=123;
|
||||
} {x123y}
|
||||
do_execsql_test without_rowid6-120 {
|
||||
SELECT c FROM t1 WHERE b=1123;
|
||||
} {x123y}
|
||||
do_execsql_test without_rowid6-130 {
|
||||
SELECT c FROM t1 ORDER BY a DESC LIMIT 5;
|
||||
} {x1000y x999y x998y x997y x996y}
|
||||
do_execsql_test without_rowid6-140 {
|
||||
SELECT c FROM t1 ORDER BY b LIMIT 5;
|
||||
} {x1y x2y x3y x4y x5y}
|
||||
|
||||
|
||||
finish_test
|
||||
+4
-10
@@ -1497,12 +1497,10 @@ int main(int argc, char **argv)
|
||||
{OPT_FLAG, "b", (char*)&basisflag, "Print only the basis in report."},
|
||||
{OPT_FLAG, "c", (char*)&compress, "Don't compress the action table."},
|
||||
{OPT_FSTR, "D", (char*)handle_D_option, "Define an %ifdef macro."},
|
||||
{OPT_FSTR, "f", 0, "Ignored. (Placeholder for -f compiler options.)"},
|
||||
{OPT_FSTR, "T", (char*)handle_T_option, "Specify a template file."},
|
||||
{OPT_FLAG, "g", (char*)&rpflag, "Print grammar without actions."},
|
||||
{OPT_FSTR, "I", 0, "Ignored. (Placeholder for '-I' compiler options.)"},
|
||||
{OPT_FLAG, "m", (char*)&mhflag, "Output a makeheaders compatible file."},
|
||||
{OPT_FLAG, "l", (char*)&nolinenosflag, "Do not print #line statements."},
|
||||
{OPT_FSTR, "O", 0, "Ignored. (Placeholder for '-O' compiler options.)"},
|
||||
{OPT_FLAG, "p", (char*)&showPrecedenceConflict,
|
||||
"Show conflicts resolved by precedence rules"},
|
||||
{OPT_FLAG, "q", (char*)&quiet, "(Quiet) Don't print the report file."},
|
||||
@@ -1510,8 +1508,6 @@ int main(int argc, char **argv)
|
||||
{OPT_FLAG, "s", (char*)&statistics,
|
||||
"Print parser stats to standard output."},
|
||||
{OPT_FLAG, "x", (char*)&version, "Print the version number."},
|
||||
{OPT_FSTR, "T", (char*)handle_T_option, "Specify a template file."},
|
||||
{OPT_FSTR, "W", 0, "Ignored. (Placeholder for '-W' compiler options.)"},
|
||||
{OPT_FLAG,0,0,0}
|
||||
};
|
||||
int i;
|
||||
@@ -1816,8 +1812,6 @@ static int handleflags(int i, FILE *err)
|
||||
errline(i,1,err);
|
||||
}
|
||||
errcnt++;
|
||||
}else if( op[j].arg==0 ){
|
||||
/* Ignore this option */
|
||||
}else if( op[j].type==OPT_FLAG ){
|
||||
*((int*)op[j].arg) = v;
|
||||
}else if( op[j].type==OPT_FFLAG ){
|
||||
@@ -2007,17 +2001,17 @@ void OptPrint(){
|
||||
break;
|
||||
case OPT_INT:
|
||||
case OPT_FINT:
|
||||
fprintf(errstream," -%s<integer>%*s %s\n",op[i].label,
|
||||
fprintf(errstream," %s=<integer>%*s %s\n",op[i].label,
|
||||
(int)(max-lemonStrlen(op[i].label)-9),"",op[i].message);
|
||||
break;
|
||||
case OPT_DBL:
|
||||
case OPT_FDBL:
|
||||
fprintf(errstream," -%s<real>%*s %s\n",op[i].label,
|
||||
fprintf(errstream," %s=<real>%*s %s\n",op[i].label,
|
||||
(int)(max-lemonStrlen(op[i].label)-6),"",op[i].message);
|
||||
break;
|
||||
case OPT_STR:
|
||||
case OPT_FSTR:
|
||||
fprintf(errstream," -%s<string>%*s %s\n",op[i].label,
|
||||
fprintf(errstream," %s=<string>%*s %s\n",op[i].label,
|
||||
(int)(max-lemonStrlen(op[i].label)-8),"",op[i].message);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -246,29 +246,18 @@ set pragma_def {
|
||||
|
||||
NAME: schema_version
|
||||
TYPE: HEADER_VALUE
|
||||
ARG: BTREE_SCHEMA_VERSION
|
||||
IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
|
||||
|
||||
NAME: user_version
|
||||
TYPE: HEADER_VALUE
|
||||
ARG: BTREE_USER_VERSION
|
||||
IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
|
||||
|
||||
NAME: data_version
|
||||
TYPE: HEADER_VALUE
|
||||
ARG: BTREE_DATA_VERSION
|
||||
FLAG: ReadOnly
|
||||
IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
|
||||
|
||||
NAME: freelist_count
|
||||
TYPE: HEADER_VALUE
|
||||
ARG: BTREE_FREE_PAGE_COUNT
|
||||
FLAG: ReadOnly
|
||||
IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
|
||||
|
||||
NAME: application_id
|
||||
TYPE: HEADER_VALUE
|
||||
ARG: BTREE_APPLICATION_ID
|
||||
IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
|
||||
|
||||
NAME: compile_options
|
||||
|
||||
+1
-1
@@ -212,7 +212,7 @@ proc copy_file {filename} {
|
||||
}
|
||||
} elseif {[regexp {^(SQLITE_EXTERN )?void \(\*sqlite3IoTrace\)} $line]} {
|
||||
regsub {^SQLITE_EXTERN } $line {} line
|
||||
puts $out $line
|
||||
puts $out "SQLITE_PRIVATE $line"
|
||||
} elseif {[regexp {^void \(\*sqlite3Os} $line]} {
|
||||
puts $out "SQLITE_PRIVATE $line"
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user