mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-07 02:42:48 +03:00
Import recent enhancements from trunk.
FossilOrigin-Name: 54bec164ebeaf62d783352b3c4d0de8845394091
This commit is contained in:
29
Makefile.in
29
Makefile.in
@@ -529,6 +529,12 @@ TESTPROGS = \
|
||||
sqlite3_analyzer$(TEXE) \
|
||||
sqldiff$(TEXE)
|
||||
|
||||
# Databases containing fuzzer test cases
|
||||
#
|
||||
FUZZDATA = \
|
||||
$(TOP)/test/fuzzdata1.db \
|
||||
$(TOP)/test/fuzzdata2.db \
|
||||
$(TOP)/test/fuzzdata3.db
|
||||
|
||||
# This is the default Makefile target. The objects listed here
|
||||
# are what get build when you type just "make" with no arguments.
|
||||
@@ -558,10 +564,13 @@ sqlite3$(TEXE): $(TOP)/src/shell.c libsqlite3.la sqlite3.h
|
||||
$(LIBREADLINE) $(TLIBS) -rpath "$(libdir)"
|
||||
|
||||
sqldiff$(TEXE): $(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h
|
||||
$(LTLINK) -o $@ $(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS)
|
||||
$(LTLINK) -o $@ $(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS)
|
||||
|
||||
fuzzershell$(TEXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h
|
||||
$(LTLINK) -o $@ $(TOP)/tool/fuzzershell.c sqlite3.c $(TLIBS)
|
||||
$(LTLINK) -o $@ $(TOP)/tool/fuzzershell.c sqlite3.c $(TLIBS)
|
||||
|
||||
fuzzcheck$(TEXE): $(TOP)/test/fuzzcheck.c sqlite3.c sqlite3.h
|
||||
$(LTLINK) -o $@ $(TOP)/test/fuzzcheck.c sqlite3.c $(TLIBS)
|
||||
|
||||
mptester$(TEXE): sqlite3.c $(TOP)/mptest/mptest.c
|
||||
$(LTLINK) -o $@ -I. $(TOP)/mptest/mptest.c sqlite3.c \
|
||||
@@ -983,7 +992,7 @@ fulltest: $(TESTPROGS) fuzztest
|
||||
./testfixture$(TEXE) $(TOP)/test/all.test
|
||||
|
||||
# Really really long testing
|
||||
soaktest: $(TESTPROGS) fuzzoomtest
|
||||
soaktest: $(TESTPROGS)
|
||||
./testfixture$(TEXE) $(TOP)/test/all.test -soak=1
|
||||
|
||||
# Do extra testing but not everything.
|
||||
@@ -991,11 +1000,8 @@ fulltestonly: $(TESTPROGS)
|
||||
./testfixture$(TEXE) $(TOP)/test/full.test
|
||||
|
||||
# Fuzz testing
|
||||
fuzztest: fuzzershell$(TEXE)
|
||||
./fuzzershell$(TEXE) $(TOP)/test/fuzzdata1.txt $(TOP)/test/fuzzdata2.txt
|
||||
|
||||
fuzzoomtest: fuzzershell$(TEXE)
|
||||
./fuzzershell$(TEXE) -f $(TOP)/test/fuzzdata1.txt --oom
|
||||
fuzztest: fuzzcheck$(TEXE)
|
||||
./fuzzcheck$(TEXE) $(FUZZDATA)
|
||||
|
||||
# This is the common case. Run many tests but not those that take
|
||||
# a really long time.
|
||||
@@ -1006,15 +1012,15 @@ test: $(TESTPROGS) fuzztest
|
||||
# Run a test using valgrind. This can take a really long time
|
||||
# because valgrind is so much slower than a native machine.
|
||||
#
|
||||
valgrindtest: $(TESTPROGS) fuzzershell$(TEXE)
|
||||
valgrind -v ./fuzzershell$(TEXE) -f $(TOP)/test/fuzzdata1.txt
|
||||
valgrindtest: $(TESTPROGS) fuzzcheck$(TEXE)
|
||||
valgrind -v ./fuzzcheck$(TEXE) --cell-size-check --quiet $(FUZZDATA)
|
||||
OMIT_MISUSE=1 valgrind -v ./testfixture$(TEXE) $(TOP)/test/permutations.test valgrind
|
||||
|
||||
# A very fast test that checks basic sanity. The name comes from
|
||||
# the 60s-era electronics testing: "Turn it on and see if smoke
|
||||
# comes out."
|
||||
#
|
||||
smoketest: $(TESTPROGS) fuzzershell$(TEXE)
|
||||
smoketest: $(TESTPROGS) fuzzcheck$(TEXE)
|
||||
./testfixture$(TEXE) $(TOP)/test/main.test
|
||||
|
||||
sqlite3_analyzer.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl
|
||||
@@ -1134,6 +1140,7 @@ clean:
|
||||
rm -f sqlite-*-output.vsix
|
||||
rm -f mptester mptester.exe
|
||||
rm -f fuzzershell fuzzershell.exe
|
||||
rm -f fuzzcheck fuzzcheck.exe
|
||||
rm -f sqldiff sqldiff.exe
|
||||
|
||||
distclean: clean
|
||||
|
29
Makefile.msc
29
Makefile.msc
@@ -1214,6 +1214,13 @@ TESTPROGS = \
|
||||
sqlite3_analyzer.exe \
|
||||
sqldiff.exe
|
||||
|
||||
# Databases containing fuzzer test cases
|
||||
#
|
||||
FUZZDATA = \
|
||||
$(TOP)\test\fuzzdata1.db \
|
||||
$(TOP)\test\fuzzdata2.db \
|
||||
$(TOP)\test\fuzzdata3.db
|
||||
|
||||
|
||||
# This is the default Makefile target. The objects listed here
|
||||
# are what get build when you type just "make" with no arguments.
|
||||
@@ -1231,13 +1238,16 @@ sqlite3.exe: $(TOP)\src\shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) sqlite3.h
|
||||
/link /pdb:sqlite3sh.pdb $(LTLINKOPTS) $(SHELL_LINK_OPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS)
|
||||
|
||||
sqldiff.exe: $(TOP)\tool\sqldiff.c sqlite3.c sqlite3.h
|
||||
$(LTLINK) $(TOP)\tool\sqldiff.c sqlite3.c
|
||||
$(LTLINK) $(NO_WARN) $(TOP)\tool\sqldiff.c sqlite3.c
|
||||
|
||||
fuzzershell.exe: $(TOP)\tool\fuzzershell.c sqlite3.c sqlite3.h
|
||||
$(LTLINK) $(TOP)\tool\fuzzershell.c sqlite3.c
|
||||
$(LTLINK) $(NO_WARN) $(TOP)\tool\fuzzershell.c sqlite3.c
|
||||
|
||||
fuzzcheck.exe: $(TOP)\test\fuzzcheck.c sqlite3.c sqlite3.h
|
||||
$(LTLINK) $(NO_WARN) $(TOP)\test\fuzzcheck.c sqlite3.c
|
||||
|
||||
mptester.exe: $(TOP)\mptest\mptest.c $(SHELL_CORE_DEP) $(LIBRESOBJS) sqlite3.h
|
||||
$(LTLINK) $(SHELL_COMPILE_OPTS) $(TOP)\mptest\mptest.c \
|
||||
$(LTLINK) $(NO_WARN) $(SHELL_COMPILE_OPTS) $(TOP)\mptest\mptest.c \
|
||||
/link $(LTLINKOPTS) $(LTLIBPATHS) $(SHELL_LINK_OPTS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS)
|
||||
|
||||
MPTEST1 = mptester mptest.db $(TOP)\mptest\crash01.test --repeat 20
|
||||
@@ -1685,7 +1695,7 @@ extensiontest: testfixture.exe testloadext.dll
|
||||
fulltest: $(TESTPROGS) fuzztest
|
||||
.\testfixture.exe $(TOP)\test\all.test
|
||||
|
||||
soaktest: $(TESTPROGS) fuzzoomtest
|
||||
soaktest: $(TESTPROGS)
|
||||
.\testfixture.exe $(TOP)\test\all.test -soak=1
|
||||
|
||||
fulltestonly: $(TESTPROGS) fuzztest
|
||||
@@ -1694,16 +1704,13 @@ fulltestonly: $(TESTPROGS) fuzztest
|
||||
queryplantest: testfixture.exe sqlite3.exe
|
||||
.\testfixture.exe $(TOP)\test\permutations.test queryplanner
|
||||
|
||||
fuzztest: fuzzershell.exe
|
||||
.\fuzzershell.exe $(TOP)\test\fuzzdata1.txt $(TOP)\test\fuzzdata2.txt
|
||||
|
||||
fuzzoomtest: fuzzershell.exe
|
||||
.\fuzzershell.exe -f $(TOP)\test\fuzzdata1.txt --oom
|
||||
fuzztest: fuzzcheck.exe
|
||||
.\fuzzcheck.exe $(FUZZDATA)
|
||||
|
||||
test: $(TESTPROGS) fuzztest
|
||||
.\testfixture.exe $(TOP)\test\veryquick.test
|
||||
|
||||
smoketest: $(TESTPROGS) fuzzershell.exe
|
||||
smoketest: $(TESTPROGS)
|
||||
.\testfixture.exe $(TOP)\test\main.test
|
||||
|
||||
sqlite3_analyzer.c: $(SQLITE3C) $(TOP)\src\tclsqlite.c $(TOP)\tool\spaceanal.tcl
|
||||
@@ -1791,7 +1798,7 @@ clean:
|
||||
del /Q shell.c sqlite3ext.h 2>NUL
|
||||
del /Q sqlite3_analyzer.exe sqlite3_analyzer.c 2>NUL
|
||||
del /Q sqlite-*-output.vsix 2>NUL
|
||||
del /Q fuzzershell.exe sqldiff.exe 2>NUL
|
||||
del /Q fuzzershell.exe fuzzcheck.exe sqldiff.exe 2>NUL
|
||||
|
||||
# Dynamic link library section.
|
||||
#
|
||||
|
@@ -5074,7 +5074,7 @@ static int fts3EvalNearTrim(
|
||||
** 2. NEAR is treated as AND. If the expression is "x NEAR y", it is
|
||||
** advanced to point to the next row that matches "x AND y".
|
||||
**
|
||||
** See fts3EvalTestDeferredAndNear() for details on testing if a row is
|
||||
** See sqlite3Fts3EvalTestDeferred() for details on testing if a row is
|
||||
** really a match, taking into account deferred tokens and NEAR operators.
|
||||
*/
|
||||
static void fts3EvalNextRow(
|
||||
@@ -5294,7 +5294,7 @@ static int fts3EvalNearTest(Fts3Expr *pExpr, int *pRc){
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is a helper function for fts3EvalTestDeferredAndNear().
|
||||
** This function is a helper function for sqlite3Fts3EvalTestDeferred().
|
||||
** Assuming no error occurs or has occurred, It returns non-zero if the
|
||||
** expression passed as the second argument matches the row that pCsr
|
||||
** currently points to, or zero if it does not.
|
||||
@@ -5415,7 +5415,7 @@ static int fts3EvalTestExpr(
|
||||
** Or, if no error occurs and it seems the current row does match the FTS
|
||||
** query, return 0.
|
||||
*/
|
||||
static int fts3EvalTestDeferredAndNear(Fts3Cursor *pCsr, int *pRc){
|
||||
int sqlite3Fts3EvalTestDeferred(Fts3Cursor *pCsr, int *pRc){
|
||||
int rc = *pRc;
|
||||
int bMiss = 0;
|
||||
if( rc==SQLITE_OK ){
|
||||
@@ -5462,7 +5462,7 @@ static int fts3EvalNext(Fts3Cursor *pCsr){
|
||||
pCsr->isRequireSeek = 1;
|
||||
pCsr->isMatchinfoNeeded = 1;
|
||||
pCsr->iPrevId = pExpr->iDocid;
|
||||
}while( pCsr->isEof==0 && fts3EvalTestDeferredAndNear(pCsr, &rc) );
|
||||
}while( pCsr->isEof==0 && sqlite3Fts3EvalTestDeferred(pCsr, &rc) );
|
||||
}
|
||||
|
||||
/* Check if the cursor is past the end of the docid range specified
|
||||
@@ -5623,7 +5623,7 @@ static int fts3EvalGatherStats(
|
||||
pCsr->iPrevId = pRoot->iDocid;
|
||||
}while( pCsr->isEof==0
|
||||
&& pRoot->eType==FTSQUERY_NEAR
|
||||
&& fts3EvalTestDeferredAndNear(pCsr, &rc)
|
||||
&& sqlite3Fts3EvalTestDeferred(pCsr, &rc)
|
||||
);
|
||||
|
||||
if( rc==SQLITE_OK && pCsr->isEof==0 ){
|
||||
@@ -5648,7 +5648,6 @@ static int fts3EvalGatherStats(
|
||||
fts3EvalNextRow(pCsr, pRoot, &rc);
|
||||
assert( pRoot->bEof==0 );
|
||||
}while( pRoot->iDocid!=iDocid && rc==SQLITE_OK );
|
||||
fts3EvalTestDeferredAndNear(pCsr, &rc);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
|
@@ -551,6 +551,7 @@ void sqlite3Fts3DoclistPrev(int,char*,int,char**,sqlite3_int64*,int*,u8*);
|
||||
int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *);
|
||||
int sqlite3Fts3FirstFilter(sqlite3_int64, char *, int, char *);
|
||||
void sqlite3Fts3CreateStatTable(int*, Fts3Table*);
|
||||
int sqlite3Fts3EvalTestDeferred(Fts3Cursor *pCsr, int *pRc);
|
||||
|
||||
/* fts3_tokenizer.c */
|
||||
const char *sqlite3Fts3NextToken(const char *, int *);
|
||||
|
@@ -104,7 +104,7 @@ struct MatchinfoBuffer {
|
||||
int nElem;
|
||||
int bGlobal; /* Set if global data is loaded */
|
||||
char *zMatchinfo;
|
||||
u32 aMatchinfo[0];
|
||||
u32 aMatchinfo[1];
|
||||
};
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ struct StrBuffer {
|
||||
*/
|
||||
static MatchinfoBuffer *fts3MIBufferNew(int nElem, const char *zMatchinfo){
|
||||
MatchinfoBuffer *pRet;
|
||||
int nByte = sizeof(u32) * (2*nElem + 2) + sizeof(MatchinfoBuffer);
|
||||
int nByte = sizeof(u32) * (2*nElem + 1) + sizeof(MatchinfoBuffer);
|
||||
int nStr = (int)strlen(zMatchinfo);
|
||||
|
||||
pRet = sqlite3_malloc(nByte + nStr+1);
|
||||
@@ -1288,6 +1288,7 @@ static int fts3MatchinfoValues(
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
}
|
||||
rc = fts3ExprIterate(pExpr, fts3ExprGlobalHitsCb,(void*)pInfo);
|
||||
sqlite3Fts3EvalTestDeferred(pCsr, &rc);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
}
|
||||
(void)fts3ExprIterate(pExpr, fts3ExprLocalHitsCb,(void*)pInfo);
|
||||
|
31
main.mk
31
main.mk
@@ -411,6 +411,13 @@ TESTPROGS = \
|
||||
sqlite3_analyzer$(EXE) \
|
||||
sqldiff$(EXE)
|
||||
|
||||
# Databases containing fuzzer test cases
|
||||
#
|
||||
FUZZDATA = \
|
||||
$(TOP)/test/fuzzdata1.db \
|
||||
$(TOP)/test/fuzzdata2.db \
|
||||
$(TOP)/test/fuzzdata3.db
|
||||
|
||||
# This is the default Makefile target. The objects listed here
|
||||
# are what get build when you type just "make" with no arguments.
|
||||
#
|
||||
@@ -427,11 +434,15 @@ sqlite3$(EXE): $(TOP)/src/shell.c libsqlite3.a sqlite3.h
|
||||
|
||||
sqldiff$(EXE): $(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h
|
||||
$(TCCX) -o sqldiff$(EXE) -DSQLITE_THREADSAFE=0 \
|
||||
$(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS) $(THREADLIB)
|
||||
$(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS) $(THREADLIB)
|
||||
|
||||
fuzzershell$(EXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h
|
||||
$(TCCX) -o fuzzershell$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION\
|
||||
$(TOP)/tool/fuzzershell.c sqlite3.c $(TLIBS) $(THREADLIB)
|
||||
$(TOP)/tool/fuzzershell.c sqlite3.c $(TLIBS) $(THREADLIB)
|
||||
|
||||
fuzzcheck$(EXE): $(TOP)/test/fuzzcheck.c sqlite3.c sqlite3.h
|
||||
$(TCCX) -o fuzzcheck$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION\
|
||||
$(TOP)/test/fuzzcheck.c sqlite3.c $(TLIBS) $(THREADLIB)
|
||||
|
||||
mptester$(EXE): sqlite3.c $(TOP)/mptest/mptest.c
|
||||
$(TCCX) -o $@ -I. $(TOP)/mptest/mptest.c sqlite3.c \
|
||||
@@ -666,7 +677,7 @@ fts3-testfixture$(EXE): sqlite3.c fts3amal.c $(TESTSRC) $(TOP)/src/tclsqlite.c
|
||||
fulltest: $(TESTPROGS) fuzztest
|
||||
./testfixture$(EXE) $(TOP)/test/all.test
|
||||
|
||||
soaktest: $(TESTPROGS) fuzzoomtest
|
||||
soaktest: $(TESTPROGS)
|
||||
./testfixture$(EXE) $(TOP)/test/all.test -soak=1
|
||||
|
||||
fulltestonly: $(TESTPROGS) fuzztest
|
||||
@@ -675,11 +686,8 @@ fulltestonly: $(TESTPROGS) fuzztest
|
||||
queryplantest: testfixture$(EXE) sqlite3$(EXE)
|
||||
./testfixture$(EXE) $(TOP)/test/permutations.test queryplanner
|
||||
|
||||
fuzztest: fuzzershell$(EXE)
|
||||
./fuzzershell$(EXE) $(TOP)/test/fuzzdata1.txt $(TOP)/test/fuzzdata2.txt
|
||||
|
||||
fuzzoomtest: fuzzershell$(EXE)
|
||||
./fuzzershell$(EXE) -f $(TOP)/test/fuzzdata1.txt --oom
|
||||
fuzztest: fuzzcheck$(EXE) $(FUZZDATA)
|
||||
./fuzzcheck$(EXE) $(FUZZDATA)
|
||||
|
||||
test: $(TESTPROGS) fuzztest
|
||||
./testfixture$(EXE) $(TOP)/test/veryquick.test
|
||||
@@ -687,15 +695,15 @@ test: $(TESTPROGS) fuzztest
|
||||
# Run a test using valgrind. This can take a really long time
|
||||
# because valgrind is so much slower than a native machine.
|
||||
#
|
||||
valgrindtest: $(TESTPROGS) fuzzershell$(EXE)
|
||||
valgrind -v ./fuzzershell$(EXE) -f $(TOP)/test/fuzzdata1.txt
|
||||
valgrindtest: $(TESTPROGS) fuzzcheck$(EXE) $(FUZZDATA)
|
||||
valgrind -v ./fuzzcheck$(EXE) --cell-size-check --quiet $(FUZZDATA)
|
||||
OMIT_MISUSE=1 valgrind -v ./testfixture$(EXE) $(TOP)/test/permutations.test valgrind
|
||||
|
||||
# A very fast test that checks basic sanity. The name comes from
|
||||
# the 60s-era electronics testing: "Turn it on and see if smoke
|
||||
# comes out."
|
||||
#
|
||||
smoketest: $(TESTPROGS) fuzzershell$(EXE)
|
||||
smoketest: $(TESTPROGS) fuzzcheck$(EXE)
|
||||
./testfixture$(EXE) $(TOP)/test/main.test
|
||||
|
||||
# The next two rules are used to support the "threadtest" target. Building
|
||||
@@ -814,4 +822,5 @@ clean:
|
||||
rm -f sqlite-*-output.vsix
|
||||
rm -f mptester mptester.exe
|
||||
rm -f fuzzershell fuzzershell.exe
|
||||
rm -f fuzzcheck fuzzcheck.exe
|
||||
rm -f sqldiff sqldiff.exe
|
||||
|
75
manifest
75
manifest
@@ -1,9 +1,9 @@
|
||||
C Merge\sbug\sfixes\sfrom\strunk.
|
||||
D 2015-05-22T23:51:30.691
|
||||
C Import\srecent\senhancements\sfrom\strunk.
|
||||
D 2015-05-29T19:04:58.908
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in 66db4ec2c6cd63d0f3e29d366aaaab769aba175b
|
||||
F Makefile.in 58c16cc8cd876ed112902e70cf33d33f3270b5aa
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
F Makefile.msc ea72907f6c4b92181dd6dfca665253025705d85f
|
||||
F Makefile.msc f4c7677780d417c7574b61904ad9195124675b26
|
||||
F Makefile.vxworks e1b65dea203f054e71653415bd8f96dcaed47858
|
||||
F README.md 0bfccb18927349653c09137a458b961fa8ab4cb9
|
||||
F VERSION ce0ae95abd7121c534f6917c1c8f2b70d9acd4db
|
||||
@@ -78,16 +78,16 @@ F ext/fts3/README.content fdc666a70d5257a64fee209f97cf89e0e6e32b51
|
||||
F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a
|
||||
F ext/fts3/README.tokenizers e0a8b81383ea60d0334d274fadf305ea14a8c314
|
||||
F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
|
||||
F ext/fts3/fts3.c 341e9d9a3c7615bac8e815a8937d576265b22f78
|
||||
F ext/fts3/fts3.c a95de5190cf52f4fa9d5952890399cab63e632b9
|
||||
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
|
||||
F ext/fts3/fts3Int.h 142837a7544dff49121b67091a71c4f7a4546b0f
|
||||
F ext/fts3/fts3Int.h 601743955ac43a0e82e6828a931c07bb3b0c95ff
|
||||
F ext/fts3/fts3_aux.c 9edc3655fcb287f0467d0a4b886a01c6185fe9f1
|
||||
F ext/fts3/fts3_expr.c 71c063da9c2a4167fb54aec089dd5ef33a58c9cb
|
||||
F ext/fts3/fts3_hash.c 29b986e43f4e9dd40110eafa377dc0d63c422c60
|
||||
F ext/fts3/fts3_hash.h 39cf6874dc239d6b4e30479b1975fe5b22a3caaf
|
||||
F ext/fts3/fts3_icu.c e319e108661147bcca8dd511cd562f33a1ba81b5
|
||||
F ext/fts3/fts3_porter.c 3565faf04b626cddf85f03825e86056a4562c009
|
||||
F ext/fts3/fts3_snippet.c 97a16ea233106b83b84a7a1e821318ea333688f7
|
||||
F ext/fts3/fts3_snippet.c 68ae118b0f834ea53d2b89e4087fc0f0b8c4ee4e
|
||||
F ext/fts3/fts3_term.c 88c55a6fa1a51ab494e33dced0401a6c28791fd7
|
||||
F ext/fts3/fts3_test.c 8a3a78c4458b2d7c631fcf4b152a5cd656fa7038
|
||||
F ext/fts3/fts3_tokenize_vtab.c a27593ab19657166f6fa5ec073b678cc29a75860
|
||||
@@ -189,7 +189,7 @@ F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
|
||||
F main.mk 4a6b557c390edd263cf975a9d2837d5efb6a1217
|
||||
F main.mk 3ba6bc8133b4b2f020a9c6043aa1fe7e35d16cb2
|
||||
F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea
|
||||
F mkopcodeh.awk d5e22023b5238985bb54a72d33e0ac71fe4f8a32
|
||||
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
|
||||
@@ -210,10 +210,10 @@ F src/auth.c b56c78ebe40a2110fd361379f7e8162d23f92240
|
||||
F src/backup.c ff743689c4d6c5cb55ad42ed9d174b2b3e71f1e3
|
||||
F src/bitvec.c 5eb7958c3bf65210211cbcfc44eff86d0ded7c9d
|
||||
F src/btmutex.c 45a968cc85afed9b5e6cf55bf1f42f8d18107f79
|
||||
F src/btree.c d2bf38cf256bd2fcaa9ed87f2f217167d849fc1b
|
||||
F src/btree.c 9e837a0e7e35c54bedddf55db906b7902d175078
|
||||
F src/btree.h 969adc948e89e449220ff0ff724c94bb2a52e9f1
|
||||
F src/btreeInt.h 973a22a6fd61350b454ad614832b1f0a5e25a1e4
|
||||
F src/build.c d5d9090788118178190c5724c19f93953b8c7a4e
|
||||
F src/build.c 85a169a0a22f8b80caf513eaf2944d39b979f571
|
||||
F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0
|
||||
F src/complete.c a5cf5b4b56390cfb7b8636e8f7ddef90258dd575
|
||||
F src/ctime.c 5a0b735dc95604766f5dac73973658eef782ee8b
|
||||
@@ -233,7 +233,7 @@ F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
|
||||
F src/legacy.c ba1863ea58c4c840335a84ec276fc2b25e22bc4e
|
||||
F src/lempar.c 7274c97d24bb46631e504332ccd3bd1b37841770
|
||||
F src/loadext.c 29255bbe1cfb2ce9bbff2526a5ecfddcb49b9271
|
||||
F src/main.c 3fd7ec2399ce67b7dda1b155925d95b8afe2b9ea
|
||||
F src/main.c 1659e2e8deee8b4ce7c0be8cd37739b13bbfa2bd
|
||||
F src/malloc.c 908c780fdddd472163c2d1b1820ae4081f01ad20
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c abe6ee469b6c5a35c7f22bfeb9c9bac664a1c987
|
||||
@@ -253,27 +253,27 @@ F src/os.h 3e57a24e2794a94d3cf2342c6d9a884888cd96bf
|
||||
F src/os_common.h abdb9a191a367793268fe553d25bab894e986a0e
|
||||
F src/os_setup.h c9d4553b5aaa6f73391448b265b89bed0b890faa
|
||||
F src/os_unix.c 23eb5f56fac54d8fe0cb204291f3b3b2d94f23fc
|
||||
F src/os_win.c 97f7828a9554d753665b6fcf7540e31c2b3d6a6e
|
||||
F src/os_win.c 27cc135e2d0b8b1e2e4944db1e2669a6a18fa0f8
|
||||
F src/os_win.h eb7a47aa17b26b77eb97e4823f20a00b8bda12ca
|
||||
F src/pager.c 97110085b1321298412f1e5c37bddb95b36d9208
|
||||
F src/pager.c 9bc918a009285f96ec6dac62dd764c7063552455
|
||||
F src/pager.h c3476e7c89cdf1c6914e50a11f3714e30b4e0a77
|
||||
F src/parse.y af55d4fb5e588705112e9788364ca3af09651fcf
|
||||
F src/parse.y 6d60dda8f8d418b6dc034f1fbccd816c459983a8
|
||||
F src/pcache.c 10539fb959849ad6efff80050541cab3d25089d4
|
||||
F src/pcache.h b44658c9c932d203510279439d891a2a83e12ba8
|
||||
F src/pcache1.c 69d137620a305f814398bd29a0c998038c0695e9
|
||||
F src/pragma.c c1f4d012ea9f6b1ce52d341b2cd0ad72d560afd7
|
||||
F src/pragma.h 09c89bca58e9a44de2116cc8272b8d454657129f
|
||||
F src/pragma.h b8632d7cdda7b25323fa580e3e558a4f0d4502cc
|
||||
F src/prepare.c 82e5db1013846a819f198336fed72c44c974e7b1
|
||||
F src/printf.c 13ce37e5574f9b0682fa86dbcf9faf76b9d82a15
|
||||
F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
|
||||
F src/resolve.c 99eabf7eff0bfa65b75939b46caa82e2b2133f28
|
||||
F src/resolve.c 84c571794e3ee5806274d95158a4c0177c6c4708
|
||||
F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e
|
||||
F src/select.c 6adad8d698a382f83009eed7b73080273fa993ca
|
||||
F src/select.c 5978cc521cb8fc1aa6a0089e35edaf531accb52a
|
||||
F src/shell.c f26cca96f7dadab5efb5e655edf548f4b91695c5
|
||||
F src/sqlite.h.in 5808551a21911fb58584316ccf878c4cd74d3258
|
||||
F src/sqlite.h.in 9d68f87febe52dbba2f3fb30d68aeba38ab957e2
|
||||
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
|
||||
F src/sqlite3ext.h 2ebeb634e751a61a6f0eebfa0f4669f46a42f6cd
|
||||
F src/sqliteInt.h 88f58e88856dcfed5b03ae020335e3a562c91f29
|
||||
F src/sqliteInt.h 7f3d18ce72c0a3c81961941f9f288975bd134378
|
||||
F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46
|
||||
F src/status.c f266ad8a2892d659b74f0f50cb6a88b6e7c12179
|
||||
F src/table.c 51b46b2a62d1b3a959633d593b89bab5e2c9155e
|
||||
@@ -324,7 +324,7 @@ F src/test_vfs.c 3b65d42e18b262805716bd96178c81da8f2d9283
|
||||
F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698
|
||||
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/threads.c 6bbcc9fe50c917864d48287b4792d46d6e873481
|
||||
F src/tokenize.c af8cbbca6db6b664ffecafa236b06629ef6d35c4
|
||||
F src/tokenize.c baa0e550dfa76a8d781732a7bfb1f0aa094942f2
|
||||
F src/trigger.c 322f23aad694e8f31d384dcfa386d52a48d3c52f
|
||||
F src/update.c 24dd6a45b8b3470e62702128ebf11be1f2693145
|
||||
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
|
||||
@@ -334,7 +334,7 @@ F src/vdbe.c e4b07daec26aaeb3700308f82770485f0a28a988
|
||||
F src/vdbe.h 01d8c35cb877faca74331bb690f0327493c2cb50
|
||||
F src/vdbeInt.h 50e298245b66b320c8930219c8aeab492c9a4cce
|
||||
F src/vdbeapi.c a5d2e8afd53b4f81934f5ca59c04465cd1a6d50d
|
||||
F src/vdbeaux.c 38233cc7faa4ad1dfbc1a9bc5a54ecf996d6840b
|
||||
F src/vdbeaux.c 9b50d9248b54a9961fdd4d5a9ec5c05f93045a7b
|
||||
F src/vdbeblob.c ab33f9b57cfce7dddb23853090186da614be4846
|
||||
F src/vdbemem.c c704f0f2515a658d8d1566a5f2f3dc9870622427
|
||||
F src/vdbesort.c f5009e7a35e3065635d8918b9a31f498a499976b
|
||||
@@ -344,7 +344,7 @@ F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb
|
||||
F src/wal.c ce2cb2d06faab54d1bce3e739bec79e063dd9113
|
||||
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
|
||||
F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804
|
||||
F src/where.c e71eae3b1383249c3b5d136d6b71ca8d28fb8d61
|
||||
F src/where.c f6f41c2f8b9903854992170ea5178898f9cb6c9c
|
||||
F src/whereInt.h a6f5a762bc1b4b1c76e1cea79976b437ac35a435
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||
@@ -391,6 +391,7 @@ F test/autoindex1.test 14b63a9f1e405fe6d5bfc8c8d00249c2ebaf13ea
|
||||
F test/autoindex2.test af7e595c6864cc6ef5fc38d5db579a3e34940cb8
|
||||
F test/autoindex3.test a3be0d1a53a7d2edff208a5e442312957047e972
|
||||
F test/autoindex4.test 49d3cd791a9baa16fb461d7ea3de80d019a819cf
|
||||
F test/autoindex5.test 6f487290ce2a667c24517191651bfb8c7d86b6dc
|
||||
F test/autovacuum.test 941892505d2c0f410a0cb5970dfa1c7c4e5f6e74
|
||||
F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4
|
||||
F test/avtrans.test 0252654f4295ddda3b2cce0e894812259e655a85
|
||||
@@ -470,7 +471,7 @@ F test/corruptE.test 193b4ca4e927e77c1d5f4f56203ddc998432a7ee
|
||||
F test/corruptF.test be9fde98e4c93648f1ba52b74e5318edc8f59fe4
|
||||
F test/corruptG.test 1ab3bf97ee7bdba70e0ff3ba2320657df55d1804
|
||||
F test/corruptH.test 5dd4fa98c6c1ed33b178f9e8a48c4fdd3cfc9067
|
||||
F test/corruptI.test 221ad8b7f0a9ac6b80fc577e73b5ad8cdea31243
|
||||
F test/corruptI.test ddf8c7146db0bc6080eedced67453b4cc69b5340
|
||||
F test/corruptJ.test 9e29e7a81ee3b6ac50f77ea7a9e2f3fa03f32d91
|
||||
F test/cost.test 19d314526616ce4473eb4e4e450fcb94499ce318
|
||||
F test/count.test cb2e0f934c6eb33670044520748d2ecccd46259c
|
||||
@@ -617,7 +618,7 @@ F test/fts3corrupt2.test 6d96efae2f8a6af3eeaf283aba437e6d0e5447ba
|
||||
F test/fts3cov.test e0fb00d8b715ddae4a94c305992dfc3ef70353d7
|
||||
F test/fts3d.test 95c17d1b67b33a5eac0bf5a0d11116a0c0ac7a3a
|
||||
F test/fts3defer.test 0be4440b73a2e651fc1e472066686d6ada4b9963
|
||||
F test/fts3defer2.test e880e3b65bdf999f4746cdaefa65f14a98b9b724
|
||||
F test/fts3defer2.test c540f5f5c2840f70c68fd9b597df817ec7170468
|
||||
F test/fts3defer3.test dd53fc13223c6d8264a98244e9b19abd35ed71cd
|
||||
F test/fts3drop.test 1b906e293d6773812587b3dc458cb9e8f3f0c297
|
||||
F test/fts3e.test 1f6c6ac9cc8b772ca256e6b22aaeed50c9350851
|
||||
@@ -669,8 +670,10 @@ F test/fuzz2.test 76dc35b32b6d6f965259508508abce75a6c4d7e1
|
||||
F test/fuzz3.test efd384b896c647b61a2c1848ba70d42aad60a7b3
|
||||
F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b
|
||||
F test/fuzz_malloc.test 328f70aaca63adf29b4c6f06505ed0cf57ca7c26
|
||||
F test/fuzzdata1.txt 9fceb50868e0b798160e83742bd7e44e457176a0
|
||||
F test/fuzzdata2.txt ba9b4467d7ec46cc85d32c0d031540cd727ae6ad
|
||||
F test/fuzzcheck.c a60f926e3fa86c8d33908406d75eec868c22b9ca
|
||||
F test/fuzzdata1.db b60254eeb6bc11474071b883059662a73c48da7f
|
||||
F test/fuzzdata2.db f03a420d3b822cc82e4f894ca957618fbe9c4973
|
||||
F test/fuzzdata3.db 2701a08185d24d8570eb6e765201131fe75eff84
|
||||
F test/fuzzer1.test d4c52aaf3ef923da293a2653cfab33d02f718a36
|
||||
F test/fuzzerfault.test 8792cd77fd5bce765b05d0c8e01b9edcf8af8536
|
||||
F test/genesis.tcl 1e2e2e8e5cc4058549a154ff1892fe5c9de19f98
|
||||
@@ -784,7 +787,7 @@ F test/minmax.test 42fbad0e81afaa6e0de41c960329f2b2c3526efd
|
||||
F test/minmax2.test b44bae787fc7b227597b01b0ca5575c7cb54d3bc
|
||||
F test/minmax3.test cc1e8b010136db0d01a6f2a29ba5a9f321034354
|
||||
F test/minmax4.test 936941484ebdceb8adec7c86b6cd9b6e5e897c1f
|
||||
F test/misc1.test 2bb46a3656e97f80c82880a94ea10d76a3b60cb0
|
||||
F test/misc1.test 3f1c479c5a093a6280f378c0fbff1c2701486660
|
||||
F test/misc2.test 00d7de54eda90e237fc9a38b9e5ccc769ebf6d4d
|
||||
F test/misc3.test cf3dda47d5dda3e53fc5804a100d3c82be736c9d
|
||||
F test/misc4.test 0d8be3466adf123a7791a66ba2bc8e8d229e87f3
|
||||
@@ -793,7 +796,6 @@ F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91
|
||||
F test/misc7.test edd0b63e2ee29a256900b0514f6fff27e19e9bb2
|
||||
F test/misc8.test fc2754d38892f7dac30c22db3616c2764f117d66
|
||||
F test/misuse.test 3c34719944ba045cc6c188a4852ba04680728912
|
||||
F test/mkfuzzdata1.tcl 5f9c33fadc64b078bb4a2c04c18b6dd3da075bec
|
||||
F test/mmap1.test 1bfd611b9841eafb44f7d83c0788e146d84a33c9
|
||||
F test/mmap2.test 9d6dd9ddb4ad2379f29cc78f38ce1e63ed418022
|
||||
F test/mmap3.test c92273e16eb8d23c1d55c9815b446bb72ef0512e
|
||||
@@ -836,7 +838,7 @@ F test/pagesize.test 5769fc62d8c890a83a503f67d47508dfdc543305
|
||||
F test/pcache.test b09104b03160aca0d968d99e8cd2c5b1921a993d
|
||||
F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025
|
||||
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
|
||||
F test/permutations.test 7019d6cb0452372b4b5475ebd145a65f746f1f1b
|
||||
F test/permutations.test 47e3d26e7412ae56b990c93e25ee2c49caa7f28e
|
||||
F test/pragma.test be7195f0aa72bdb8a512133e9640ac40f15b57a2
|
||||
F test/pragma2.test f624a496a95ee878e81e59961eade66d5c00c028
|
||||
F test/pragma3.test 6f849ccffeee7e496d2f2b5e74152306c0b8757c
|
||||
@@ -855,11 +857,12 @@ F test/randexpr1.test eda062a97e60f9c38ae8d806b03b0ddf23d796df
|
||||
F test/rdonly.test 64e2696c322e3538df0b1ed624e21f9a23ed9ff8
|
||||
F test/regexp1.test 497ea812f264d12b6198d6e50a76be4a1973a9d8
|
||||
F test/reindex.test 44edd3966b474468b823d481eafef0c305022254
|
||||
F test/releasetest.tcl e340abab899d4b8bdd87ff434e10a83ebe38c5ac
|
||||
F test/releasetest.tcl 3e906a8bbd047b8e1f035984fbdc96df4caaea47
|
||||
F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb
|
||||
F test/rollback.test 458fe73eb3ffdfdf9f6ba3e9b7350a6220414dea
|
||||
F test/rollback2.test fc14cf6d1a2b250d2735ef16124b971bce152f14
|
||||
F test/rollbackfault.test 6a004f71087cc399296cffbb5429ea6da655ae65
|
||||
F test/rowallock.test f7f834125f11ff62f6e1ae7d0b07fd9228f2d5a2
|
||||
F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
|
||||
F test/rowid.test 742b5741584a8a44fd83e856cc2896688401d645
|
||||
F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798
|
||||
@@ -1230,10 +1233,10 @@ F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c
|
||||
F test/win32heap.test ea19770974795cff26e11575e12d422dbd16893c
|
||||
F test/win32lock.test fbf107c91d8f5512be5a5b87c4c42ab9fdd54972
|
||||
F test/win32longpath.test 169c75a3b2e43481f4a62122510210c67b08f26d
|
||||
F test/with1.test a86bf7f9288ba759a25ee57221d3bffaca36032a
|
||||
F test/with1.test e99845d4f4bf7863b61f104de554c61739d65764
|
||||
F test/with2.test ee227a663586aa09771cafd4fa269c5217eaf775
|
||||
F test/withM.test e97f2a8c506ab3ea9eab94e6f6072f6cc924c991
|
||||
F test/without_rowid1.test 7862e605753c8d25329f665fa09072e842183151
|
||||
F test/without_rowid1.test 1a7b9bd51b899928d327052df9741d2fe8dbe701
|
||||
F test/without_rowid2.test af260339f79d13cb220288b67cd287fbcf81ad99
|
||||
F test/without_rowid3.test 1081aabf60a1e1123b7f9a8f6ae19954351843b0
|
||||
F test/without_rowid4.test 4e08bcbaee0399f35d58b5581881e7a6243d458a
|
||||
@@ -1259,7 +1262,7 @@ F tool/logest.c eef612f8adf4d0993dafed0416064cf50d5d33c6
|
||||
F tool/mkautoconfamal.sh d1a2da0e15b2ed33d60af35c7e9d483f13a8eb9f
|
||||
F tool/mkkeywordhash.c dfff09dbbfaf950e89af294f48f902181b144670
|
||||
F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e
|
||||
F tool/mkpragmatab.tcl 94f196c9961e0ca3513e29f57125a3197808be2d
|
||||
F tool/mkpragmatab.tcl 40c287d3f929ece67da6e9e7c49885789960accf
|
||||
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
|
||||
F tool/mksqlite3c-noext.tcl 69bae8ce4aa52d2ff82d4a8a856bf283ec035b2e
|
||||
F tool/mksqlite3c.tcl 508aac36881e12c4f6e5eb72d9920d1fbc1a1216
|
||||
@@ -1297,7 +1300,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
P cdc0ca6fb36e787b981fb544a27c6df838f85704 c404fcace83613a50015f15097b7f28123c221c3
|
||||
R 957ffe62dcf25805db0612a32b474c17
|
||||
P 519054bb72e8f8977b11161c81b0e96ba7bca589 09a38bf665902834936d39341627ded88142e6ae
|
||||
R 8892830f1d56f04b6b2e14d9642ea36b
|
||||
U drh
|
||||
Z cbbadb818c188a30c0fd9c403a8bf4fd
|
||||
Z 5694d275e23a4dea203ff0f897ed1e57
|
||||
|
@@ -1 +1 @@
|
||||
519054bb72e8f8977b11161c81b0e96ba7bca589
|
||||
54bec164ebeaf62d783352b3c4d0de8845394091
|
154
src/btree.c
154
src/btree.c
@@ -1196,26 +1196,18 @@ static int defragmentPage(MemPage *pPage){
|
||||
pc = get2byte(pAddr);
|
||||
testcase( pc==iCellFirst );
|
||||
testcase( pc==iCellLast );
|
||||
#if !defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK)
|
||||
/* These conditions have already been verified in btreeInitPage()
|
||||
** if SQLITE_ENABLE_OVERSIZE_CELL_CHECK is defined
|
||||
** if PRAGMA cell_size_check=ON.
|
||||
*/
|
||||
if( pc<iCellFirst || pc>iCellLast ){
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
#endif
|
||||
assert( pc>=iCellFirst && pc<=iCellLast );
|
||||
size = cellSizePtr(pPage, &src[pc]);
|
||||
cbrk -= size;
|
||||
#if defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK)
|
||||
if( cbrk<iCellFirst ){
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
#else
|
||||
if( cbrk<iCellFirst || pc+size>usableSize ){
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
#endif
|
||||
assert( cbrk+size<=usableSize && cbrk>=iCellFirst );
|
||||
testcase( cbrk+size==usableSize );
|
||||
testcase( pc+size==usableSize );
|
||||
@@ -1291,7 +1283,7 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc, int *pbDefrag){
|
||||
** fragmented bytes within the page. */
|
||||
memcpy(&aData[iAddr], &aData[pc], 2);
|
||||
aData[hdr+7] += (u8)x;
|
||||
}else if( size+pc > usableSize ){
|
||||
}else if( pc < pPg->cellOffset+2*pPg->nCell || size+pc > usableSize ){
|
||||
*pRc = SQLITE_CORRUPT_BKPT;
|
||||
return 0;
|
||||
}else{
|
||||
@@ -1343,7 +1335,11 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
|
||||
** However, that integer is too large to be stored in a 2-byte unsigned
|
||||
** integer, so a value of 0 is used in its place. */
|
||||
top = get2byteNotZero(&data[hdr+5]);
|
||||
if( gap>top ) return SQLITE_CORRUPT_BKPT;
|
||||
if( gap>top || NEVER((u32)top>pPage->pBt->usableSize) ){
|
||||
/* The NEVER() is because a oversize "top" value will be blocked from
|
||||
** reaching this point by btreeInitPage() or btreeGetUnusedPage() */
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
|
||||
/* If there is enough space between gap and top for one more cell pointer
|
||||
** array entry offset, and if the freelist is not empty, then search the
|
||||
@@ -1416,7 +1412,7 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
|
||||
|
||||
assert( pPage->pBt!=0 );
|
||||
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
|
||||
assert( iStart>=pPage->hdrOffset+6+pPage->childPtrSize );
|
||||
assert( CORRUPT_DB || iStart>=pPage->hdrOffset+6+pPage->childPtrSize );
|
||||
assert( CORRUPT_DB || iEnd <= pPage->pBt->usableSize );
|
||||
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
|
||||
assert( iSize>=4 ); /* Minimum cell size is 4 */
|
||||
@@ -1556,6 +1552,7 @@ static int decodeFlags(MemPage *pPage, int flagByte){
|
||||
static int btreeInitPage(MemPage *pPage){
|
||||
|
||||
assert( pPage->pBt!=0 );
|
||||
assert( pPage->pBt->db!=0 );
|
||||
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
|
||||
assert( pPage->pgno==sqlite3PagerPagenumber(pPage->pDbPage) );
|
||||
assert( pPage == sqlite3PagerGetExtra(pPage->pDbPage) );
|
||||
@@ -1614,8 +1611,7 @@ static int btreeInitPage(MemPage *pPage){
|
||||
*/
|
||||
iCellFirst = cellOffset + 2*pPage->nCell;
|
||||
iCellLast = usableSize - 4;
|
||||
#if defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK)
|
||||
{
|
||||
if( pBt->db->flags & SQLITE_CellSizeCk ){
|
||||
int i; /* Index into the cell pointer array */
|
||||
int sz; /* Size of a cell */
|
||||
|
||||
@@ -1635,7 +1631,6 @@ static int btreeInitPage(MemPage *pPage){
|
||||
}
|
||||
if( !pPage->leaf ) iCellLast++;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Compute the total free space on the page
|
||||
** EVIDENCE-OF: R-23588-34450 The two-byte integer at offset 1 gives the
|
||||
@@ -1732,10 +1727,10 @@ static MemPage *btreePageFromDbPage(DbPage *pDbPage, Pgno pgno, BtShared *pBt){
|
||||
|
||||
/*
|
||||
** Get a page from the pager. Initialize the MemPage.pBt and
|
||||
** MemPage.aData elements if needed.
|
||||
** MemPage.aData elements if needed. See also: btreeGetUnusedPage().
|
||||
**
|
||||
** If the noContent flag is set, it means that we do not care about
|
||||
** the content of the page at this time. So do not go to the disk
|
||||
** If the PAGER_GET_NOCONTENT flag is set, it means that we do not care
|
||||
** about the content of the page at this time. So do not go to the disk
|
||||
** to fetch the content. Just fill in the content with zeros for now.
|
||||
** If in the future we call sqlite3PagerWrite() on this page, that
|
||||
** means we have started to be concerned about content and the disk
|
||||
@@ -1837,6 +1832,36 @@ static void releasePage(MemPage *pPage){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Get an unused page.
|
||||
**
|
||||
** This works just like btreeGetPage() with the addition:
|
||||
**
|
||||
** * If the page is already in use for some other purpose, immediately
|
||||
** release it and return an SQLITE_CURRUPT error.
|
||||
** * Make sure the isInit flag is clear
|
||||
*/
|
||||
static int btreeGetUnusedPage(
|
||||
BtShared *pBt, /* The btree */
|
||||
Pgno pgno, /* Number of the page to fetch */
|
||||
MemPage **ppPage, /* Return the page in this parameter */
|
||||
int flags /* PAGER_GET_NOCONTENT or PAGER_GET_READONLY */
|
||||
){
|
||||
int rc = btreeGetPage(pBt, pgno, ppPage, flags);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( sqlite3PagerPageRefcount((*ppPage)->pDbPage)>1 ){
|
||||
releasePage(*ppPage);
|
||||
*ppPage = 0;
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
(*ppPage)->isInit = 0;
|
||||
}else{
|
||||
*ppPage = 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** During a rollback, when the pager reloads information into the cache
|
||||
** so that the cache is restored to its original state at the start of
|
||||
@@ -3084,8 +3109,10 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){
|
||||
u8 isInitOrig = pPage->isInit;
|
||||
int i;
|
||||
int nCell;
|
||||
int rc;
|
||||
|
||||
btreeInitPage(pPage);
|
||||
rc = btreeInitPage(pPage);
|
||||
if( rc ) return rc;
|
||||
nCell = pPage->nCell;
|
||||
|
||||
for(i=0; i<nCell; i++){
|
||||
@@ -3886,9 +3913,13 @@ int sqlite3BtreeCursor(
|
||||
BtCursor *pCur /* Write new cursor here */
|
||||
){
|
||||
int rc;
|
||||
sqlite3BtreeEnter(p);
|
||||
rc = btreeCursor(p, iTable, wrFlag, pKeyInfo, pCur);
|
||||
sqlite3BtreeLeave(p);
|
||||
if( iTable<1 ){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
}else{
|
||||
sqlite3BtreeEnter(p);
|
||||
rc = btreeCursor(p, iTable, wrFlag, pKeyInfo, pCur);
|
||||
sqlite3BtreeLeave(p);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -4916,7 +4947,7 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
}
|
||||
}else{
|
||||
for(;;){
|
||||
int nCell;
|
||||
int nCell; /* Size of the pCell cell in bytes */
|
||||
pCell = findCell(pPage, idx) + pPage->childPtrSize;
|
||||
|
||||
/* The maximum supported page-size is 65536 bytes. This means that
|
||||
@@ -4945,12 +4976,25 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
/* The record flows over onto one or more overflow pages. In
|
||||
** this case the whole cell needs to be parsed, a buffer allocated
|
||||
** and accessPayload() used to retrieve the record into the
|
||||
** buffer before VdbeRecordCompare() can be called. */
|
||||
** buffer before VdbeRecordCompare() can be called.
|
||||
**
|
||||
** If the record is corrupt, the xRecordCompare routine may read
|
||||
** up to two varints past the end of the buffer. An extra 18
|
||||
** bytes of padding is allocated at the end of the buffer in
|
||||
** case this happens. */
|
||||
void *pCellKey;
|
||||
u8 * const pCellBody = pCell - pPage->childPtrSize;
|
||||
btreeParseCellPtr(pPage, pCellBody, &pCur->info);
|
||||
nCell = (int)pCur->info.nKey;
|
||||
pCellKey = sqlite3Malloc( nCell );
|
||||
testcase( nCell<0 ); /* True if key size is 2^32 or more */
|
||||
testcase( nCell==0 ); /* Invalid key size: 0x80 0x80 0x00 */
|
||||
testcase( nCell==1 ); /* Invalid key size: 0x80 0x80 0x01 */
|
||||
testcase( nCell==2 ); /* Minimum legal index key size */
|
||||
if( nCell<2 ){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
goto moveto_finish;
|
||||
}
|
||||
pCellKey = sqlite3Malloc( nCell+18 );
|
||||
if( pCellKey==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
goto moveto_finish;
|
||||
@@ -5243,8 +5287,7 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
|
||||
** sqlite3PagerUnref() on the new page when it is done.
|
||||
**
|
||||
** SQLITE_OK is returned on success. Any other return value indicates
|
||||
** an error. *ppPage and *pPgno are undefined in the event of an error.
|
||||
** Do not invoke sqlite3PagerUnref() on *ppPage if an error is returned.
|
||||
** an error. *ppPage is set to NULL in the event of an error.
|
||||
**
|
||||
** If the "nearby" parameter is not 0, then an effort is made to
|
||||
** locate a page close to the page number "nearby". This can be used in an
|
||||
@@ -5338,7 +5381,7 @@ static int allocateBtreePage(
|
||||
if( iTrunk>mxPage ){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
}else{
|
||||
rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0);
|
||||
rc = btreeGetUnusedPage(pBt, iTrunk, &pTrunk, 0);
|
||||
}
|
||||
if( rc ){
|
||||
pTrunk = 0;
|
||||
@@ -5403,7 +5446,7 @@ static int allocateBtreePage(
|
||||
goto end_allocate_page;
|
||||
}
|
||||
testcase( iNewTrunk==mxPage );
|
||||
rc = btreeGetPage(pBt, iNewTrunk, &pNewTrunk, 0);
|
||||
rc = btreeGetUnusedPage(pBt, iNewTrunk, &pNewTrunk, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto end_allocate_page;
|
||||
}
|
||||
@@ -5483,11 +5526,12 @@ static int allocateBtreePage(
|
||||
}
|
||||
put4byte(&aData[4], k-1);
|
||||
noContent = !btreeGetHasContent(pBt, *pPgno)? PAGER_GET_NOCONTENT : 0;
|
||||
rc = btreeGetPage(pBt, *pPgno, ppPage, noContent);
|
||||
rc = btreeGetUnusedPage(pBt, *pPgno, ppPage, noContent);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3PagerWrite((*ppPage)->pDbPage);
|
||||
if( rc!=SQLITE_OK ){
|
||||
releasePage(*ppPage);
|
||||
*ppPage = 0;
|
||||
}
|
||||
}
|
||||
searchList = 0;
|
||||
@@ -5531,7 +5575,7 @@ static int allocateBtreePage(
|
||||
MemPage *pPg = 0;
|
||||
TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", pBt->nPage));
|
||||
assert( pBt->nPage!=PENDING_BYTE_PAGE(pBt) );
|
||||
rc = btreeGetPage(pBt, pBt->nPage, &pPg, bNoContent);
|
||||
rc = btreeGetUnusedPage(pBt, pBt->nPage, &pPg, bNoContent);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3PagerWrite(pPg->pDbPage);
|
||||
releasePage(pPg);
|
||||
@@ -5545,11 +5589,12 @@ static int allocateBtreePage(
|
||||
*pPgno = pBt->nPage;
|
||||
|
||||
assert( *pPgno!=PENDING_BYTE_PAGE(pBt) );
|
||||
rc = btreeGetPage(pBt, *pPgno, ppPage, bNoContent);
|
||||
rc = btreeGetUnusedPage(pBt, *pPgno, ppPage, bNoContent);
|
||||
if( rc ) return rc;
|
||||
rc = sqlite3PagerWrite((*ppPage)->pDbPage);
|
||||
if( rc!=SQLITE_OK ){
|
||||
releasePage(*ppPage);
|
||||
*ppPage = 0;
|
||||
}
|
||||
TRACE(("ALLOCATE: %d from end of file\n", *pPgno));
|
||||
}
|
||||
@@ -5559,17 +5604,8 @@ static int allocateBtreePage(
|
||||
end_allocate_page:
|
||||
releasePage(pTrunk);
|
||||
releasePage(pPrevTrunk);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( sqlite3PagerPageRefcount((*ppPage)->pDbPage)>1 ){
|
||||
releasePage(*ppPage);
|
||||
*ppPage = 0;
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
(*ppPage)->isInit = 0;
|
||||
}else{
|
||||
*ppPage = 0;
|
||||
}
|
||||
assert( rc!=SQLITE_OK || sqlite3PagerIswriteable((*ppPage)->pDbPage) );
|
||||
assert( rc!=SQLITE_OK || sqlite3PagerPageRefcount((*ppPage)->pDbPage)<=1 );
|
||||
assert( rc!=SQLITE_OK || (*ppPage)->isInit==0 );
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -5594,9 +5630,10 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
|
||||
int nFree; /* Initial number of pages on free-list */
|
||||
|
||||
assert( sqlite3_mutex_held(pBt->mutex) );
|
||||
assert( iPage>1 );
|
||||
assert( CORRUPT_DB || iPage>1 );
|
||||
assert( !pMemPage || pMemPage->pgno==iPage );
|
||||
|
||||
if( iPage<2 ) return SQLITE_CORRUPT_BKPT;
|
||||
if( pMemPage ){
|
||||
pPage = pMemPage;
|
||||
sqlite3PagerRef(pPage->pDbPage);
|
||||
@@ -5748,7 +5785,9 @@ static int clearCell(
|
||||
assert( pBt->usableSize > 4 );
|
||||
ovflPageSize = pBt->usableSize - 4;
|
||||
nOvfl = (info.nPayload - info.nLocal + ovflPageSize - 1)/ovflPageSize;
|
||||
assert( ovflPgno==0 || nOvfl>0 );
|
||||
assert( nOvfl>0 ||
|
||||
(CORRUPT_DB && (info.nPayload + ovflPageSize)<ovflPageSize)
|
||||
);
|
||||
while( nOvfl-- ){
|
||||
Pgno iNext = 0;
|
||||
MemPage *pOvfl = 0;
|
||||
@@ -6003,7 +6042,7 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
|
||||
if( *pRC ) return;
|
||||
|
||||
assert( idx>=0 && idx<pPage->nCell );
|
||||
assert( sz==cellSize(pPage, idx) );
|
||||
assert( CORRUPT_DB || sz==cellSize(pPage, idx) );
|
||||
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
|
||||
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
|
||||
data = pPage->aData;
|
||||
@@ -6168,7 +6207,7 @@ static void rebuildPage(
|
||||
put2byte(pCellptr, (pData - aData));
|
||||
pCellptr += 2;
|
||||
assert( szCell[i]==cellSizePtr(pPg, pCell) || CORRUPT_DB );
|
||||
testcase( szCell[i]!=cellSizePtr(pPg,pCell) );
|
||||
testcase( szCell[i]==cellSizePtr(pPg,pCell) );
|
||||
}
|
||||
|
||||
/* The pPg->nFree field is now set incorrectly. The caller will fix it. */
|
||||
@@ -6846,6 +6885,14 @@ static int balance_nonroot(
|
||||
int limit;
|
||||
MemPage *pOld = apOld[i];
|
||||
|
||||
/* Verify that all sibling pages are of the same "type" (table-leaf,
|
||||
** table-interior, index-leaf, or index-interior).
|
||||
*/
|
||||
if( pOld->aData[0]!=apOld[0]->aData[0] ){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
goto balance_cleanup;
|
||||
}
|
||||
|
||||
limit = pOld->nCell+pOld->nOverflow;
|
||||
if( pOld->nOverflow>0 ){
|
||||
for(j=0; j<limit; j++){
|
||||
@@ -6887,13 +6934,13 @@ static int balance_nonroot(
|
||||
memcpy(apCell[nCell], &pOld->aData[8], 4);
|
||||
}else{
|
||||
assert( leafCorrection==4 );
|
||||
if( szCell[nCell]<4 ){
|
||||
while( 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] );
|
||||
assert( szCell[nCell]==3 || CORRUPT_DB );
|
||||
assert( apCell[nCell]==&aSpace1[iSpace1-3] || CORRUPT_DB );
|
||||
aSpace1[iSpace1++] = 0x00;
|
||||
szCell[nCell] = 4;
|
||||
szCell[nCell]++;
|
||||
}
|
||||
}
|
||||
nCell++;
|
||||
@@ -6984,10 +7031,6 @@ static int balance_nonroot(
|
||||
/*
|
||||
** Allocate k new pages. Reuse old pages where possible.
|
||||
*/
|
||||
if( apOld[0]->pgno<=1 ){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
goto balance_cleanup;
|
||||
}
|
||||
pageFlags = apOld[0]->aData[0];
|
||||
for(i=0; i<k; i++){
|
||||
MemPage *pNew;
|
||||
@@ -7769,6 +7812,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){
|
||||
unsigned char *pTmp;
|
||||
|
||||
pCell = findCell(pLeaf, pLeaf->nCell-1);
|
||||
if( pCell<&pLeaf->aData[4] ) return SQLITE_CORRUPT_BKPT;
|
||||
nCell = cellSizePtr(pLeaf, pCell);
|
||||
assert( MX_CELL_SIZE(pBt) >= nCell );
|
||||
pTmp = pBt->pTmpSpace;
|
||||
|
36
src/build.c
36
src/build.c
@@ -1325,14 +1325,11 @@ void sqlite3AddPrimaryKey(
|
||||
"INTEGER PRIMARY KEY");
|
||||
#endif
|
||||
}else{
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
Index *p;
|
||||
if( v ) pParse->addrSkipPK = sqlite3VdbeAddOp0(v, OP_Noop);
|
||||
p = sqlite3CreateIndex(pParse, 0, 0, 0, pList, onError, 0,
|
||||
0, sortOrder, 0);
|
||||
if( p ){
|
||||
p->idxType = SQLITE_IDXTYPE_PRIMARYKEY;
|
||||
if( v ) sqlite3VdbeJumpHere(v, pParse->addrSkipPK);
|
||||
}
|
||||
pList = 0;
|
||||
}
|
||||
@@ -1685,14 +1682,6 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
|
||||
sqlite3VdbeGetOp(v, pParse->addrCrTab)->opcode = OP_CreateIndex;
|
||||
}
|
||||
|
||||
/* Bypass the creation of the PRIMARY KEY btree and the sqlite_master
|
||||
** table entry.
|
||||
*/
|
||||
if( pParse->addrSkipPK ){
|
||||
assert( v );
|
||||
sqlite3VdbeGetOp(v, pParse->addrSkipPK)->opcode = OP_Goto;
|
||||
}
|
||||
|
||||
/* Locate the PRIMARY KEY index. Or, if this table was originally
|
||||
** an INTEGER PRIMARY KEY table, create a new PRIMARY KEY index.
|
||||
*/
|
||||
@@ -1710,6 +1699,16 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
|
||||
pTab->iPKey = -1;
|
||||
}else{
|
||||
pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
|
||||
/* Bypass the creation of the PRIMARY KEY btree and the sqlite_master
|
||||
** table entry. This is only required if currently generating VDBE
|
||||
** code for a CREATE TABLE (not when parsing one as part of reading
|
||||
** a database schema). */
|
||||
if( v ){
|
||||
assert( db->init.busy==0 );
|
||||
sqlite3VdbeGetOp(v, pPk->tnum)->opcode = OP_Goto;
|
||||
}
|
||||
|
||||
/*
|
||||
** 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
|
||||
@@ -1845,7 +1844,7 @@ void sqlite3EndTable(
|
||||
if( (p->tabFlags & TF_HasPrimaryKey)==0 ){
|
||||
sqlite3ErrorMsg(pParse, "PRIMARY KEY missing on table %s", p->zName);
|
||||
}else{
|
||||
p->tabFlags |= TF_WithoutRowid;
|
||||
p->tabFlags |= TF_WithoutRowid | TF_NoVisibleRowid;
|
||||
convertToWithoutRowidTable(pParse, p);
|
||||
}
|
||||
}
|
||||
@@ -3250,10 +3249,15 @@ Index *sqlite3CreateIndex(
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
if( v==0 ) goto exit_create_index;
|
||||
|
||||
|
||||
/* Create the rootpage for the index
|
||||
*/
|
||||
sqlite3BeginWriteOperation(pParse, 1, iDb);
|
||||
|
||||
/* Create the rootpage for the index using CreateIndex. But before
|
||||
** doing so, code a Noop instruction and store its address in
|
||||
** Index.tnum. This is required in case this index is actually a
|
||||
** PRIMARY KEY and the table is actually a WITHOUT ROWID table. In
|
||||
** that case the convertToWithoutRowidTable() routine will replace
|
||||
** the Noop with a Goto to jump over the VDBE code generated below. */
|
||||
pIndex->tnum = sqlite3VdbeAddOp0(v, OP_Noop);
|
||||
sqlite3VdbeAddOp2(v, OP_CreateIndex, iDb, iMem);
|
||||
|
||||
/* Gather the complete text of the CREATE INDEX statement into
|
||||
@@ -3293,6 +3297,8 @@ Index *sqlite3CreateIndex(
|
||||
sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName));
|
||||
sqlite3VdbeAddOp1(v, OP_Expire, 0);
|
||||
}
|
||||
|
||||
sqlite3VdbeJumpHere(v, pIndex->tnum);
|
||||
}
|
||||
|
||||
/* When adding an index to the list of indices for a table, make
|
||||
|
@@ -2779,6 +2779,9 @@ static int openDatabase(
|
||||
#endif
|
||||
#if defined(SQLITE_REVERSE_UNORDERED_SELECTS)
|
||||
| SQLITE_ReverseOrder
|
||||
#endif
|
||||
#if defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK)
|
||||
| SQLITE_CellSizeCk
|
||||
#endif
|
||||
;
|
||||
sqlite3HashInit(&db->aCollSeq);
|
||||
|
@@ -3015,6 +3015,12 @@ static int winLock(sqlite3_file *id, int locktype){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Do not allow any kind of write-lock on a read-only database
|
||||
*/
|
||||
if( (pFile->ctrlFlags & WINFILE_RDONLY)!=0 && locktype>=RESERVED_LOCK ){
|
||||
return SQLITE_IOERR_LOCK;
|
||||
}
|
||||
|
||||
/* Make sure the locking sequence is correct
|
||||
*/
|
||||
assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK );
|
||||
|
@@ -3149,11 +3149,10 @@ static int pagerPagecount(Pager *pPager, Pgno *pnPage){
|
||||
assert( pPager->eLock>=SHARED_LOCK );
|
||||
nPage = sqlite3WalDbsize(pPager->pWal);
|
||||
|
||||
/* If the database size was not available from the WAL sub-system,
|
||||
** determine it based on the size of the database file. If the size
|
||||
** of the database file is not an integer multiple of the page-size,
|
||||
** round down to the nearest page. Except, any file larger than 0
|
||||
** bytes in size is considered to contain at least one page.
|
||||
/* If the number of pages in the database is not available from the
|
||||
** WAL sub-system, determine the page counte based on the size of
|
||||
** the database file. If the size of the database file is not an
|
||||
** integer multiple of the page-size, round up the result.
|
||||
*/
|
||||
if( nPage==0 ){
|
||||
i64 n = 0; /* Size of db file in bytes */
|
||||
|
@@ -166,7 +166,7 @@ create_table_args ::= AS select(S). {
|
||||
table_options(A) ::= . {A = 0;}
|
||||
table_options(A) ::= WITHOUT nm(X). {
|
||||
if( X.n==5 && sqlite3_strnicmp(X.z,"rowid",5)==0 ){
|
||||
A = TF_WithoutRowid;
|
||||
A = TF_WithoutRowid | TF_NoVisibleRowid;
|
||||
}else{
|
||||
A = 0;
|
||||
sqlite3ErrorMsg(pParse, "unknown table option: %.*s", X.n, X.z);
|
||||
@@ -527,7 +527,7 @@ values(A) ::= values(X) COMMA LP exprlist(Y) RP. {
|
||||
//
|
||||
%type distinct {u16}
|
||||
distinct(A) ::= DISTINCT. {A = SF_Distinct;}
|
||||
distinct(A) ::= ALL. {A = 0;}
|
||||
distinct(A) ::= ALL. {A = SF_All;}
|
||||
distinct(A) ::= . {A = 0;}
|
||||
|
||||
// selcollist is a list of expressions that are to become the return
|
||||
@@ -890,7 +890,7 @@ expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP(E). {
|
||||
}
|
||||
A.pExpr = sqlite3ExprFunction(pParse, Y, &X);
|
||||
spanSet(&A,&X,&E);
|
||||
if( D && A.pExpr ){
|
||||
if( D==SF_Distinct && A.pExpr ){
|
||||
A.pExpr->flags |= EP_Distinct;
|
||||
}
|
||||
}
|
||||
|
@@ -99,6 +99,10 @@ static const struct sPragmaNames {
|
||||
/* ePragTyp: */ PragTyp_CASE_SENSITIVE_LIKE,
|
||||
/* ePragFlag: */ 0,
|
||||
/* iArg: */ 0 },
|
||||
{ /* zName: */ "cell_size_check",
|
||||
/* ePragTyp: */ PragTyp_FLAG,
|
||||
/* ePragFlag: */ 0,
|
||||
/* iArg: */ SQLITE_CellSizeCk },
|
||||
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
|
||||
{ /* zName: */ "checkpoint_fullfsync",
|
||||
/* ePragTyp: */ PragTyp_FLAG,
|
||||
@@ -456,4 +460,4 @@ static const struct sPragmaNames {
|
||||
/* iArg: */ SQLITE_WriteSchema|SQLITE_RecoveryMode },
|
||||
#endif
|
||||
};
|
||||
/* Number of pragmas: 59 on by default, 72 total. */
|
||||
/* Number of pragmas: 60 on by default, 73 total. */
|
||||
|
@@ -358,7 +358,7 @@ static int lookupName(
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( iCol>=pTab->nCol && sqlite3IsRowid(zCol) && HasRowid(pTab) ){
|
||||
if( iCol>=pTab->nCol && sqlite3IsRowid(zCol) && VisibleRowid(pTab) ){
|
||||
/* IMP: R-51414-32910 */
|
||||
/* IMP: R-44911-55124 */
|
||||
iCol = -1;
|
||||
@@ -388,7 +388,7 @@ static int lookupName(
|
||||
** Perhaps the name is a reference to the ROWID
|
||||
*/
|
||||
if( cnt==0 && cntTab==1 && pMatch && sqlite3IsRowid(zCol)
|
||||
&& HasRowid(pMatch->pTab) ){
|
||||
&& VisibleRowid(pMatch->pTab) ){
|
||||
cnt = 1;
|
||||
pExpr->iColumn = -1; /* IMP: R-44911-55124 */
|
||||
pExpr->affinity = SQLITE_AFF_INTEGER;
|
||||
|
10
src/select.c
10
src/select.c
@@ -3991,7 +3991,7 @@ static int withExpand(
|
||||
pTab->zName = sqlite3DbStrDup(db, pCte->zName);
|
||||
pTab->iPKey = -1;
|
||||
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
|
||||
pTab->tabFlags |= TF_Ephemeral;
|
||||
pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid;
|
||||
pFrom->pSelect = sqlite3SelectDup(db, pCte->pSelect, 0);
|
||||
if( db->mallocFailed ) return SQLITE_NOMEM;
|
||||
assert( pFrom->pSelect );
|
||||
@@ -4236,13 +4236,6 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
int longNames = (flags & SQLITE_FullColNames)!=0
|
||||
&& (flags & SQLITE_ShortColNames)==0;
|
||||
|
||||
/* When processing FROM-clause subqueries, it is always the case
|
||||
** that full_column_names=OFF and short_column_names=ON. The
|
||||
** sqlite3ResultSetOfSelect() routine makes it so. */
|
||||
assert( (p->selFlags & SF_NestedFrom)==0
|
||||
|| ((flags & SQLITE_FullColNames)==0 &&
|
||||
(flags & SQLITE_ShortColNames)!=0) );
|
||||
|
||||
for(k=0; k<pEList->nExpr; k++){
|
||||
pE = a[k].pExpr;
|
||||
pRight = pE->pRight;
|
||||
@@ -4824,6 +4817,7 @@ int sqlite3Select(
|
||||
}
|
||||
i = -1;
|
||||
}else if( pTabList->nSrc==1
|
||||
&& (p->selFlags & SF_All)==0
|
||||
&& OptimizationEnabled(db, SQLITE_SubqCoroutine)
|
||||
){
|
||||
/* Implement a co-routine that will return a single row of the result
|
||||
|
@@ -3400,7 +3400,9 @@ int sqlite3_stmt_busy(sqlite3_stmt*);
|
||||
** Some interfaces require a protected sqlite3_value. Other interfaces
|
||||
** will accept either a protected or an unprotected sqlite3_value.
|
||||
** Every interface that accepts sqlite3_value arguments specifies
|
||||
** whether or not it requires a protected sqlite3_value.
|
||||
** whether or not it requires a protected sqlite3_value. The
|
||||
** [sqlite3_value_dup()] interface can be used to construct a new
|
||||
** protected sqlite3_value from an unprotected sqlite3_value.
|
||||
**
|
||||
** The terms "protected" and "unprotected" refer to whether or not
|
||||
** a mutex is held. An internal mutex is held for a protected
|
||||
@@ -4363,7 +4365,7 @@ int sqlite3_value_numeric_type(sqlite3_value*);
|
||||
** memory allocation fails.
|
||||
**
|
||||
** ^The sqlite3_value_free(V) interface frees an [sqlite3_value] object
|
||||
** previously obtained from [sqlite_value_dup()]. ^If V is a NULL pointer
|
||||
** previously obtained from [sqlite3_value_dup()]. ^If V is a NULL pointer
|
||||
** then sqlite3_value_free(V) is a harmless no-op.
|
||||
*/
|
||||
SQLITE_EXPERIMENTAL sqlite3_value *sqlite3_value_dup(const sqlite3_value*);
|
||||
@@ -4616,7 +4618,7 @@ typedef void (*sqlite3_destructor_type)(void*);
|
||||
** from [sqlite3_malloc()] before it returns.
|
||||
**
|
||||
** ^The sqlite3_result_value() interface sets the result of
|
||||
** the application-defined function to be a copy the
|
||||
** the application-defined function to be a copy of the
|
||||
** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The
|
||||
** sqlite3_result_value() interface makes a copy of the [sqlite3_value]
|
||||
** so that the [sqlite3_value] specified in the parameter may change or
|
||||
|
@@ -1261,6 +1261,7 @@ struct sqlite3 {
|
||||
#define SQLITE_QueryOnly 0x02000000 /* Disable database changes */
|
||||
#define SQLITE_VdbeEQP 0x04000000 /* Debug EXPLAIN QUERY PLAN */
|
||||
#define SQLITE_Vacuum 0x08000000 /* Currently in a VACUUM */
|
||||
#define SQLITE_CellSizeCk 0x10000000 /* Check btree cell sizes on load */
|
||||
|
||||
|
||||
/*
|
||||
@@ -1642,8 +1643,9 @@ struct Table {
|
||||
#define TF_HasPrimaryKey 0x04 /* Table has a primary key */
|
||||
#define TF_Autoincrement 0x08 /* Integer primary key is autoincrement */
|
||||
#define TF_Virtual 0x10 /* Is a virtual table */
|
||||
#define TF_WithoutRowid 0x20 /* No rowid used. PRIMARY KEY is the key */
|
||||
#define TF_OOOHidden 0x40 /* Out-of-Order hidden columns */
|
||||
#define TF_WithoutRowid 0x20 /* No rowid. PRIMARY KEY is the key */
|
||||
#define TF_NoVisibleRowid 0x40 /* No user-visible "rowid" column */
|
||||
#define TF_OOOHidden 0x80 /* Out-of-Order hidden columns */
|
||||
|
||||
|
||||
/*
|
||||
@@ -1661,6 +1663,7 @@ struct Table {
|
||||
|
||||
/* Does the table have a rowid */
|
||||
#define HasRowid(X) (((X)->tabFlags & TF_WithoutRowid)==0)
|
||||
#define VisibleRowid(X) (((X)->tabFlags & TF_NoVisibleRowid)==0)
|
||||
|
||||
/*
|
||||
** Each foreign key constraint is an instance of the following structure.
|
||||
@@ -1819,6 +1822,14 @@ struct UnpackedRecord {
|
||||
** and the value of Index.onError indicate the which conflict resolution
|
||||
** algorithm to employ whenever an attempt is made to insert a non-unique
|
||||
** element.
|
||||
**
|
||||
** While parsing a CREATE TABLE or CREATE INDEX statement in order to
|
||||
** generate VDBE code (as opposed to parsing one read from an sqlite_master
|
||||
** table as part of parsing an existing database schema), transient instances
|
||||
** of this structure may be created. In this case the Index.tnum variable is
|
||||
** used to store the address of a VDBE instruction, not a database page
|
||||
** number (it cannot - the database page is not allocated until the VDBE
|
||||
** program is executed). See convertToWithoutRowidTable() for details.
|
||||
*/
|
||||
struct Index {
|
||||
char *zName; /* Name of this index */
|
||||
@@ -2393,19 +2404,20 @@ struct Select {
|
||||
** "Select Flag".
|
||||
*/
|
||||
#define SF_Distinct 0x0001 /* Output should be DISTINCT */
|
||||
#define SF_Resolved 0x0002 /* Identifiers have been resolved */
|
||||
#define SF_Aggregate 0x0004 /* Contains aggregate functions */
|
||||
#define SF_UsesEphemeral 0x0008 /* Uses the OpenEphemeral opcode */
|
||||
#define SF_Expanded 0x0010 /* sqlite3SelectExpand() called on this */
|
||||
#define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */
|
||||
#define SF_Compound 0x0040 /* Part of a compound query */
|
||||
#define SF_Values 0x0080 /* Synthesized from VALUES clause */
|
||||
#define SF_MultiValue 0x0100 /* Single VALUES term with multiple rows */
|
||||
#define SF_NestedFrom 0x0200 /* Part of a parenthesized FROM clause */
|
||||
#define SF_MaybeConvert 0x0400 /* Need convertCompoundSelectToSubquery() */
|
||||
#define SF_Recursive 0x0800 /* The recursive part of a recursive CTE */
|
||||
#define SF_All 0x0002 /* Includes the ALL keyword */
|
||||
#define SF_Resolved 0x0004 /* Identifiers have been resolved */
|
||||
#define SF_Aggregate 0x0008 /* Contains aggregate functions */
|
||||
#define SF_UsesEphemeral 0x0010 /* Uses the OpenEphemeral opcode */
|
||||
#define SF_Expanded 0x0020 /* sqlite3SelectExpand() called on this */
|
||||
#define SF_HasTypeInfo 0x0040 /* FROM subqueries have Table metadata */
|
||||
#define SF_Compound 0x0080 /* Part of a compound query */
|
||||
#define SF_Values 0x0100 /* Synthesized from VALUES clause */
|
||||
#define SF_MultiValue 0x0200 /* Single VALUES term with multiple rows */
|
||||
#define SF_NestedFrom 0x0400 /* Part of a parenthesized FROM clause */
|
||||
#define SF_MaybeConvert 0x0800 /* Need convertCompoundSelectToSubquery() */
|
||||
#define SF_MinMaxAgg 0x1000 /* Aggregate containing min() or max() */
|
||||
#define SF_Converted 0x2000 /* By convertCompoundSelectToSubquery() */
|
||||
#define SF_Recursive 0x2000 /* The recursive part of a recursive CTE */
|
||||
#define SF_Converted 0x4000 /* By convertCompoundSelectToSubquery() */
|
||||
|
||||
|
||||
/*
|
||||
@@ -2647,7 +2659,6 @@ struct Parse {
|
||||
Parse *pToplevel; /* Parse structure for main program (or NULL) */
|
||||
Table *pTriggerTab; /* Table triggers are being coded for */
|
||||
int addrCrTab; /* Address of OP_CreateTable opcode on CREATE TABLE */
|
||||
int addrSkipPK; /* Address of instruction to skip PRIMARY KEY index */
|
||||
u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */
|
||||
u32 oldmask; /* Mask of old.* columns referenced */
|
||||
u32 newmask; /* Mask of new.* columns referenced */
|
||||
|
@@ -450,7 +450,8 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
|
||||
}
|
||||
abort_parse:
|
||||
assert( nErr==0 );
|
||||
if( zSql[i]==0 && pParse->rc==SQLITE_OK && db->mallocFailed==0 ){
|
||||
if( pParse->rc==SQLITE_OK && db->mallocFailed==0 ){
|
||||
assert( zSql[i]==0 );
|
||||
if( lastTokenParsed!=TK_SEMI ){
|
||||
sqlite3Parser(pEngine, TK_SEMI, pParse->sLastToken, pParse);
|
||||
pParse->zTail = &zSql[i];
|
||||
|
@@ -3653,7 +3653,7 @@ int sqlite3VdbeRecordCompareWithSkip(
|
||||
if( pRhs->flags & MEM_Int ){
|
||||
serial_type = aKey1[idx1];
|
||||
testcase( serial_type==12 );
|
||||
if( serial_type>=12 ){
|
||||
if( serial_type>=10 ){
|
||||
rc = +1;
|
||||
}else if( serial_type==0 ){
|
||||
rc = -1;
|
||||
@@ -3679,7 +3679,11 @@ int sqlite3VdbeRecordCompareWithSkip(
|
||||
/* RHS is real */
|
||||
else if( pRhs->flags & MEM_Real ){
|
||||
serial_type = aKey1[idx1];
|
||||
if( serial_type>=12 ){
|
||||
if( serial_type>=10 ){
|
||||
/* Serial types 12 or greater are strings and blobs (greater than
|
||||
** numbers). Types 10 and 11 are currently "reserved for future
|
||||
** use", so it doesn't really matter what the results of comparing
|
||||
** them to numberic values are. */
|
||||
rc = +1;
|
||||
}else if( serial_type==0 ){
|
||||
rc = -1;
|
||||
|
83
src/where.c
83
src/where.c
@@ -1660,6 +1660,36 @@ static LogEst estLog(LogEst N){
|
||||
return N<=10 ? 0 : sqlite3LogEst(N) - 33;
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert OP_Column opcodes to OP_Copy in previously generated code.
|
||||
**
|
||||
** This routine runs over generated VDBE code and translates OP_Column
|
||||
** opcodes into OP_Copy, and OP_Rowid into OP_Null, when the table is being
|
||||
** accessed via co-routine instead of via table lookup.
|
||||
*/
|
||||
static void translateColumnToCopy(
|
||||
Vdbe *v, /* The VDBE containing code to translate */
|
||||
int iStart, /* Translate from this opcode to the end */
|
||||
int iTabCur, /* OP_Column/OP_Rowid references to this table */
|
||||
int iRegister /* The first column is in this register */
|
||||
){
|
||||
VdbeOp *pOp = sqlite3VdbeGetOp(v, iStart);
|
||||
int iEnd = sqlite3VdbeCurrentAddr(v);
|
||||
for(; iStart<iEnd; iStart++, pOp++){
|
||||
if( pOp->p1!=iTabCur ) continue;
|
||||
if( pOp->opcode==OP_Column ){
|
||||
pOp->opcode = OP_Copy;
|
||||
pOp->p1 = pOp->p2 + iRegister;
|
||||
pOp->p2 = pOp->p3;
|
||||
pOp->p3 = 0;
|
||||
}else if( pOp->opcode==OP_Rowid ){
|
||||
pOp->opcode = OP_Null;
|
||||
pOp->p1 = 0;
|
||||
pOp->p3 = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Two routines for printing the content of an sqlite3_index_info
|
||||
** structure. Used for testing and debugging only. If neither
|
||||
@@ -1762,6 +1792,7 @@ static void constructAutomaticIndex(
|
||||
u8 sentWarning = 0; /* True if a warnning has been issued */
|
||||
Expr *pPartial = 0; /* Partial Index Expression */
|
||||
int iContinue = 0; /* Jump here to skip excluded rows */
|
||||
struct SrcList_item *pTabItem; /* FROM clause term being indexed */
|
||||
|
||||
/* Generate code to skip over the creation and initialization of the
|
||||
** transient index on 2nd and subsequent iterations of the loop. */
|
||||
@@ -1887,7 +1918,16 @@ static void constructAutomaticIndex(
|
||||
|
||||
/* Fill the automatic index with content */
|
||||
sqlite3ExprCachePush(pParse);
|
||||
addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); VdbeCoverage(v);
|
||||
pTabItem = &pWC->pWInfo->pTabList->a[pLevel->iFrom];
|
||||
if( pTabItem->viaCoroutine ){
|
||||
int regYield = pTabItem->regReturn;
|
||||
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub);
|
||||
addrTop = sqlite3VdbeAddOp1(v, OP_Yield, regYield);
|
||||
VdbeCoverage(v);
|
||||
VdbeComment((v, "next row of \"%s\"", pTabItem->pTab->zName));
|
||||
}else{
|
||||
addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); VdbeCoverage(v);
|
||||
}
|
||||
if( pPartial ){
|
||||
iContinue = sqlite3VdbeMakeLabel(v);
|
||||
sqlite3ExprIfFalse(pParse, pPartial, iContinue, SQLITE_JUMPIFNULL);
|
||||
@@ -1898,7 +1938,13 @@ static void constructAutomaticIndex(
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
|
||||
if( pPartial ) sqlite3VdbeResolveLabel(v, iContinue);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v);
|
||||
if( pTabItem->viaCoroutine ){
|
||||
translateColumnToCopy(v, addrTop, pLevel->iTabCur, pTabItem->regResult);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop);
|
||||
pTabItem->viaCoroutine = 0;
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v);
|
||||
}
|
||||
sqlite3VdbeChangeP5(v, SQLITE_STMTSTATUS_AUTOINDEX);
|
||||
sqlite3VdbeJumpHere(v, addrTop);
|
||||
sqlite3ReleaseTempReg(pParse, regRecord);
|
||||
@@ -5174,15 +5220,14 @@ static int whereLoopAddBtree(
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
|
||||
/* Automatic indexes */
|
||||
if( !pBuilder->pOrSet
|
||||
if( !pBuilder->pOrSet /* Not part of an OR optimization */
|
||||
&& (pWInfo->wctrlFlags & WHERE_NO_AUTOINDEX)==0
|
||||
&& (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0
|
||||
&& pSrc->pIndex==0
|
||||
&& !pSrc->viaCoroutine
|
||||
&& !pSrc->notIndexed
|
||||
&& HasRowid(pTab)
|
||||
&& !pSrc->isCorrelated
|
||||
&& !pSrc->isRecursive
|
||||
&& pSrc->pIndex==0 /* Has no INDEXED BY clause */
|
||||
&& !pSrc->notIndexed /* Has no NOT INDEXED clause */
|
||||
&& HasRowid(pTab) /* Is not a WITHOUT ROWID table. (FIXME: Why not?) */
|
||||
&& !pSrc->isCorrelated /* Not a correlated subquery */
|
||||
&& !pSrc->isRecursive /* Not a recursive common table expression. */
|
||||
){
|
||||
/* Generate auto-index WhereLoops */
|
||||
WhereTerm *pTerm;
|
||||
@@ -7017,26 +7062,12 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
pLoop = pLevel->pWLoop;
|
||||
|
||||
/* For a co-routine, change all OP_Column references to the table of
|
||||
** the co-routine into OP_SCopy of result contained in a register.
|
||||
** the co-routine into OP_Copy of result contained in a register.
|
||||
** OP_Rowid becomes OP_Null.
|
||||
*/
|
||||
if( pTabItem->viaCoroutine && !db->mallocFailed ){
|
||||
last = sqlite3VdbeCurrentAddr(v);
|
||||
k = pLevel->addrBody;
|
||||
pOp = sqlite3VdbeGetOp(v, k);
|
||||
for(; k<last; k++, pOp++){
|
||||
if( pOp->p1!=pLevel->iTabCur ) continue;
|
||||
if( pOp->opcode==OP_Column ){
|
||||
pOp->opcode = OP_Copy;
|
||||
pOp->p1 = pOp->p2 + pTabItem->regResult;
|
||||
pOp->p2 = pOp->p3;
|
||||
pOp->p3 = 0;
|
||||
}else if( pOp->opcode==OP_Rowid ){
|
||||
pOp->opcode = OP_Null;
|
||||
pOp->p1 = 0;
|
||||
pOp->p3 = 0;
|
||||
}
|
||||
}
|
||||
translateColumnToCopy(v, pLevel->addrBody, pLevel->iTabCur,
|
||||
pTabItem->regResult);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
108
test/autoindex5.test
Normal file
108
test/autoindex5.test
Normal file
@@ -0,0 +1,108 @@
|
||||
# 2014-10-24
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is testing automatic index creation logic,
|
||||
# and specifically ensuring that automatic indexes can be used with
|
||||
# co-routine subqueries.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# Schema is from the Debian security database
|
||||
#
|
||||
do_execsql_test autoindex5-1.0 {
|
||||
CREATE TABLE source_package_status
|
||||
(bug_name TEXT NOT NULL,
|
||||
package INTEGER NOT NULL,
|
||||
vulnerable INTEGER NOT NULL,
|
||||
urgency TEXT NOT NULL,
|
||||
PRIMARY KEY (bug_name, package));
|
||||
CREATE INDEX source_package_status_package
|
||||
ON source_package_status(package);
|
||||
|
||||
CREATE TABLE source_packages
|
||||
(name TEXT NOT NULL,
|
||||
release TEXT NOT NULL,
|
||||
subrelease TEXT NOT NULL,
|
||||
archive TEXT NOT NULL,
|
||||
version TEXT NOT NULL,
|
||||
version_id INTEGER NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (name, release, subrelease, archive));
|
||||
|
||||
CREATE TABLE bugs
|
||||
(name TEXT NOT NULL PRIMARY KEY,
|
||||
cve_status TEXT NOT NULL
|
||||
CHECK (cve_status IN
|
||||
('', 'CANDIDATE', 'ASSIGNED', 'RESERVED', 'REJECTED')),
|
||||
not_for_us INTEGER NOT NULL CHECK (not_for_us IN (0, 1)),
|
||||
description TEXT NOT NULL,
|
||||
release_date TEXT NOT NULL,
|
||||
source_file TEXT NOT NULL,
|
||||
source_line INTEGER NOT NULL);
|
||||
|
||||
CREATE TABLE package_notes
|
||||
(id INTEGER NOT NULL PRIMARY KEY,
|
||||
bug_name TEXT NOT NULL,
|
||||
package TEXT NOT NULL,
|
||||
fixed_version TEXT
|
||||
CHECK (fixed_version IS NULL OR fixed_version <> ''),
|
||||
fixed_version_id INTEGER NOT NULL DEFAULT 0,
|
||||
release TEXT NOT NULL,
|
||||
package_kind TEXT NOT NULL DEFAULT 'unknown',
|
||||
urgency TEXT NOT NULL,
|
||||
bug_origin TEXT NOT NULL DEFAULT '');
|
||||
CREATE INDEX package_notes_package
|
||||
ON package_notes(package);
|
||||
CREATE UNIQUE INDEX package_notes_bug
|
||||
ON package_notes(bug_name, package, release);
|
||||
|
||||
CREATE TABLE debian_bugs
|
||||
(bug INTEGER NOT NULL,
|
||||
note INTEGER NOT NULL,
|
||||
PRIMARY KEY (bug, note));
|
||||
|
||||
|
||||
CREATE VIEW debian_cve AS
|
||||
SELECT DISTINCT debian_bugs.bug, st.bug_name
|
||||
FROM package_notes, debian_bugs, source_package_status AS st
|
||||
WHERE package_notes.bug_name = st.bug_name
|
||||
AND debian_bugs.note = package_notes.id
|
||||
ORDER BY debian_bugs.bug;
|
||||
} {}
|
||||
|
||||
# The following query should use an automatic index for the view
|
||||
# in FROM clause of the subquery of the second result column.
|
||||
#
|
||||
do_execsql_test autoindex5-1.1 {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT
|
||||
st.bug_name,
|
||||
(SELECT ALL debian_cve.bug FROM debian_cve
|
||||
WHERE debian_cve.bug_name = st.bug_name
|
||||
ORDER BY debian_cve.bug),
|
||||
sp.release
|
||||
FROM
|
||||
source_package_status AS st,
|
||||
source_packages AS sp,
|
||||
bugs
|
||||
WHERE
|
||||
sp.rowid = st.package
|
||||
AND st.bug_name = bugs.name
|
||||
AND ( st.bug_name LIKE 'CVE-%' OR st.bug_name LIKE 'TEMP-%' )
|
||||
AND ( sp.release = 'sid' OR sp.release = 'stretch' OR sp.release = 'jessie'
|
||||
OR sp.release = 'wheezy' OR sp.release = 'squeeze' )
|
||||
ORDER BY sp.name, st.bug_name, sp.release, sp.subrelease;
|
||||
} {/SEARCH SUBQUERY 2 USING AUTOMATIC COVERING INDEX .bug_name=/}
|
||||
|
||||
|
||||
finish_test
|
@@ -105,4 +105,159 @@ if {[db one {SELECT sqlite_compileoption_used('ENABLE_OVERSIZE_CELL_CHECK')}]} {
|
||||
INSERT INTO t1 VALUES(9, 'klmnopqrst');
|
||||
} {1 {database disk image is malformed}}
|
||||
} ;# end-if !defined(ENABLE_OVERSIZE_CELL_CHECK)
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that an assert() failure discovered by AFL corrupt database file
|
||||
# testing has been fixed.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
PRAGMA page_size = 65536;
|
||||
PRAGMA autovacuum = 0;
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||
INSERT INTO t1 VALUES(-1, 'abcdefghij');
|
||||
INSERT INTO t1 VALUES(0, 'abcdefghij');
|
||||
}
|
||||
|
||||
set root [db one {SELECT rootpage FROM sqlite_master}]
|
||||
set offset [expr ($root-1) * 65536]
|
||||
|
||||
ifcapable oversize_cell_check {
|
||||
set res {1 {database disk image is malformed}}
|
||||
} else {
|
||||
set res {0 {}}
|
||||
}
|
||||
do_test 4.1 {
|
||||
db close
|
||||
hexio_write test.db [expr $offset + 8 + 2] 0000
|
||||
hexio_write test.db [expr $offset + 5] 0000
|
||||
sqlite3 db test.db
|
||||
catchsql { DELETE FROM t1 WHERE a=0 }
|
||||
} $res
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Database properties:
|
||||
#
|
||||
# * Incremental vacuum mode.
|
||||
# * Database root table has a single leaf page.
|
||||
# * Free list consists of a single trunk page.
|
||||
#
|
||||
# The db is then corrupted by adding the root table leaf page as a free-list
|
||||
# leaf page (so that it is referenced twice).
|
||||
#
|
||||
# Then, a new table is created. The new root page is the current free-list
|
||||
# trunk. This means that the root table leaf page is made into the new
|
||||
# free list trunk, which corrupts its header. Then, when the new entry is
|
||||
# inserted into the root table, things would get chaotic.
|
||||
#
|
||||
reset_db
|
||||
do_test 5.0 {
|
||||
execsql {
|
||||
PRAGMA page_size = 512;
|
||||
PRAGMA auto_vacuum = 2;
|
||||
}
|
||||
for {set i 3} {1} {incr i} {
|
||||
execsql "CREATE TABLE t${i}(x)"
|
||||
if {[db one {PRAGMA page_count}]>$i} break
|
||||
}
|
||||
set nPage [db one {PRAGMA page_count}]
|
||||
execsql {
|
||||
CREATE TABLE t100(x);
|
||||
DROP TABLE t100;
|
||||
}
|
||||
} {}
|
||||
|
||||
do_execsql_test 5.1 {
|
||||
PRAGMA page_count
|
||||
} [expr $nPage+1]
|
||||
|
||||
do_test 5.2 {
|
||||
# The last page of the db is now the only leaf of the sqlite_master table.
|
||||
# Corrupt the db by adding it to the free-list as well (the second last
|
||||
# page of the db is the free-list trunk).
|
||||
db close
|
||||
hexio_write test.db [expr 512*($nPage-1)] [
|
||||
format "%.8X%.8X%.8X" 0 1 [expr $nPage+1]
|
||||
]
|
||||
} {12}
|
||||
|
||||
do_test 5.3 {
|
||||
sqlite3 db test.db
|
||||
catchsql { CREATE TABLE tx(x); }
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Set the payload size of a cell to just less than 2^32 bytes (not
|
||||
# possible in an uncorrupted db). Then try to delete the cell. At one
|
||||
# point this led to an integer overflow that caused an assert() to fail.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 6.0 {
|
||||
PRAGMA page_size = 512;
|
||||
PRAGMA auto_vacuum=0;
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(zeroblob(300));
|
||||
INSERT INTO t1 VALUES(zeroblob(600));
|
||||
} {}
|
||||
do_test 6.1 {
|
||||
db close
|
||||
hexio_write test.db 616 EAFFFFFF0202
|
||||
sqlite3 db test.db
|
||||
breakpoint
|
||||
execsql { DELETE FROM t1 WHERE rowid=2 }
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# See what happens if the sqlite_master entry associated with a PRIMARY
|
||||
# KEY or UNIQUE index is removed.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 7.0 {
|
||||
PRAGMA auto_vacuum=0;
|
||||
CREATE TABLE t1(x PRIMARY KEY, y);
|
||||
INSERT INTO t1 VALUES('a', 'A');
|
||||
INSERT INTO t1 VALUES('b', 'A');
|
||||
INSERT INTO t1 VALUES('c', 'A');
|
||||
SELECT name FROM sqlite_master;
|
||||
} {t1 sqlite_autoindex_t1_1}
|
||||
do_execsql_test 7.1 {
|
||||
PRAGMA writable_schema = 1;
|
||||
DELETE FROM sqlite_master WHERE name = 'sqlite_autoindex_t1_1';
|
||||
}
|
||||
do_test 7.2 {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
catchsql { UPDATE t1 SET x='d' AND y='D' WHERE rowid = 2 }
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# At one point an assert() would fail if attempt was made to free page 1.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 8.0 {
|
||||
PRAGMA auto_vacuum=0;
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(zeroblob(300));
|
||||
INSERT INTO t1 VALUES(zeroblob(300));
|
||||
INSERT INTO t1 VALUES(zeroblob(300));
|
||||
INSERT INTO t1 VALUES(zeroblob(300));
|
||||
} {}
|
||||
|
||||
do_test 8.1 {
|
||||
db close
|
||||
hexio_write test.db [expr 1024 + 8] 00000001
|
||||
sqlite3 db test.db
|
||||
catchsql { DELETE FROM t1 }
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
do_test 8.2 {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
execsql { PRAGMA integrity_check }
|
||||
} {/.*in database main.*/}
|
||||
|
||||
|
||||
finish_test
|
||||
|
@@ -121,6 +121,12 @@ foreach {tn sql} {
|
||||
[list 1 2 2 1 2 2 0 54 54] \
|
||||
]
|
||||
set sqlite_fts3_enable_parentheses 0
|
||||
|
||||
do_execsql_test 2.2.$tn.4 {
|
||||
SELECT mit(matchinfo(t2, 'x')) FROM t2 WHERE t2 MATCH 'e "g z"';
|
||||
} [list \
|
||||
[list 1 2 2 1 2 2] \
|
||||
]
|
||||
}
|
||||
|
||||
do_execsql_test 2.3.1 {
|
||||
|
880
test/fuzzcheck.c
Normal file
880
test/fuzzcheck.c
Normal file
@@ -0,0 +1,880 @@
|
||||
/*
|
||||
** 2015-05-25
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This is a utility program designed to aid running regressions tests
|
||||
** on SQLite library using data from an external fuzzer, such as American
|
||||
** Fuzzy Lop (AFL) (http://lcamtuf.coredump.cx/afl/).
|
||||
**
|
||||
** This program reads content from an SQLite database file with the following
|
||||
** schema:
|
||||
**
|
||||
** CREATE TABLE db(
|
||||
** dbid INTEGER PRIMARY KEY, -- database id
|
||||
** dbcontent BLOB -- database disk file image
|
||||
** );
|
||||
** CREATE TABLE xsql(
|
||||
** sqlid INTEGER PRIMARY KEY, -- SQL script id
|
||||
** sqltext TEXT -- Text of SQL statements to run
|
||||
** );
|
||||
**
|
||||
** For each database file in the DB table, the SQL text in the XSQL table
|
||||
** is run against that database. This program is looking for crashes,
|
||||
** assertion faults, and/or memory leaks. No attempt is made to verify
|
||||
** the output. The assumption is that either all of the database files
|
||||
** or all of the SQL statements are malformed inputs, generated by a fuzzer,
|
||||
** that need to be checked to make sure they do not present a security risk.
|
||||
**
|
||||
** This program also includes some command-line options to help with
|
||||
** creation and maintenance of the source content database.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
#include "sqlite3.h"
|
||||
|
||||
/*
|
||||
** Files in the virtual file system.
|
||||
*/
|
||||
typedef struct VFile VFile;
|
||||
struct VFile {
|
||||
char *zFilename; /* Filename. NULL for delete-on-close. From malloc() */
|
||||
int sz; /* Size of the file in bytes */
|
||||
int nRef; /* Number of references to this file */
|
||||
unsigned char *a; /* Content of the file. From malloc() */
|
||||
};
|
||||
typedef struct VHandle VHandle;
|
||||
struct VHandle {
|
||||
sqlite3_file base; /* Base class. Must be first */
|
||||
VFile *pVFile; /* The underlying file */
|
||||
};
|
||||
|
||||
/*
|
||||
** The value of a database file template, or of an SQL script
|
||||
*/
|
||||
typedef struct Blob Blob;
|
||||
struct Blob {
|
||||
Blob *pNext; /* Next in a list */
|
||||
int id; /* Id of this Blob */
|
||||
int seq; /* Sequence number */
|
||||
int sz; /* Size of this Blob in bytes */
|
||||
unsigned char a[1]; /* Blob content. Extra space allocated as needed. */
|
||||
};
|
||||
|
||||
/*
|
||||
** Maximum number of files in the in-memory virtual filesystem.
|
||||
*/
|
||||
#define MX_FILE 10
|
||||
|
||||
/*
|
||||
** Maximum allowed file size
|
||||
*/
|
||||
#define MX_FILE_SZ 10000000
|
||||
|
||||
/*
|
||||
** All global variables are gathered into the "g" singleton.
|
||||
*/
|
||||
static struct GlobalVars {
|
||||
const char *zArgv0; /* Name of program */
|
||||
VFile aFile[MX_FILE]; /* The virtual filesystem */
|
||||
int nDb; /* Number of template databases */
|
||||
Blob *pFirstDb; /* Content of first template database */
|
||||
int nSql; /* Number of SQL scripts */
|
||||
Blob *pFirstSql; /* First SQL script */
|
||||
char zTestName[100]; /* Name of current test */
|
||||
} g;
|
||||
|
||||
/*
|
||||
** Print an error message and quit.
|
||||
*/
|
||||
static void fatalError(const char *zFormat, ...){
|
||||
va_list ap;
|
||||
if( g.zTestName[0] ){
|
||||
fprintf(stderr, "%s (%s): ", g.zArgv0, g.zTestName);
|
||||
}else{
|
||||
fprintf(stderr, "%s: ", g.zArgv0);
|
||||
}
|
||||
va_start(ap, zFormat);
|
||||
vfprintf(stderr, zFormat, ap);
|
||||
va_end(ap);
|
||||
fprintf(stderr, "\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Reallocate memory. Show and error and quit if unable.
|
||||
*/
|
||||
static void *safe_realloc(void *pOld, int szNew){
|
||||
void *pNew = realloc(pOld, szNew);
|
||||
if( pNew==0 ) fatalError("unable to realloc for %d bytes", szNew);
|
||||
return pNew;
|
||||
}
|
||||
|
||||
/*
|
||||
** Initialize the virtual file system.
|
||||
*/
|
||||
static void formatVfs(void){
|
||||
int i;
|
||||
for(i=0; i<MX_FILE; i++){
|
||||
g.aFile[i].sz = -1;
|
||||
g.aFile[i].zFilename = 0;
|
||||
g.aFile[i].a = 0;
|
||||
g.aFile[i].nRef = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Erase all information in the virtual file system.
|
||||
*/
|
||||
static void reformatVfs(void){
|
||||
int i;
|
||||
for(i=0; i<MX_FILE; i++){
|
||||
if( g.aFile[i].sz<0 ) continue;
|
||||
if( g.aFile[i].zFilename ){
|
||||
free(g.aFile[i].zFilename);
|
||||
g.aFile[i].zFilename = 0;
|
||||
}
|
||||
if( g.aFile[i].nRef>0 ){
|
||||
fatalError("file %d still open. nRef=%d", i, g.aFile[i].nRef);
|
||||
}
|
||||
g.aFile[i].sz = -1;
|
||||
free(g.aFile[i].a);
|
||||
g.aFile[i].a = 0;
|
||||
g.aFile[i].nRef = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Find a VFile by name
|
||||
*/
|
||||
static VFile *findVFile(const char *zName){
|
||||
int i;
|
||||
if( zName==0 ) return 0;
|
||||
for(i=0; i<MX_FILE; i++){
|
||||
if( g.aFile[i].zFilename==0 ) continue;
|
||||
if( strcmp(g.aFile[i].zFilename, zName)==0 ) return &g.aFile[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Find a VFile by name. Create it if it does not already exist and
|
||||
** initialize it to the size and content given.
|
||||
**
|
||||
** Return NULL only if the filesystem is full.
|
||||
*/
|
||||
static VFile *createVFile(const char *zName, int sz, unsigned char *pData){
|
||||
VFile *pNew = findVFile(zName);
|
||||
int i;
|
||||
if( pNew ) return pNew;
|
||||
for(i=0; i<MX_FILE && g.aFile[i].sz>=0; i++){}
|
||||
if( i>=MX_FILE ) return 0;
|
||||
pNew = &g.aFile[i];
|
||||
if( zName ){
|
||||
pNew->zFilename = safe_realloc(0, strlen(zName)+1);
|
||||
memcpy(pNew->zFilename, zName, strlen(zName)+1);
|
||||
}else{
|
||||
pNew->zFilename = 0;
|
||||
}
|
||||
pNew->nRef = 0;
|
||||
pNew->sz = sz;
|
||||
pNew->a = safe_realloc(0, sz);
|
||||
if( sz>0 ) memcpy(pNew->a, pData, sz);
|
||||
return pNew;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Implementation of the "readfile(X)" SQL function. The entire content
|
||||
** of the file named X is read and returned as a BLOB. NULL is returned
|
||||
** if the file does not exist or is unreadable.
|
||||
*/
|
||||
static void readfileFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zName;
|
||||
FILE *in;
|
||||
long nIn;
|
||||
void *pBuf;
|
||||
|
||||
zName = (const char*)sqlite3_value_text(argv[0]);
|
||||
if( zName==0 ) return;
|
||||
in = fopen(zName, "rb");
|
||||
if( in==0 ) return;
|
||||
fseek(in, 0, SEEK_END);
|
||||
nIn = ftell(in);
|
||||
rewind(in);
|
||||
pBuf = sqlite3_malloc64( nIn );
|
||||
if( pBuf && 1==fread(pBuf, nIn, 1, in) ){
|
||||
sqlite3_result_blob(context, pBuf, nIn, sqlite3_free);
|
||||
}else{
|
||||
sqlite3_free(pBuf);
|
||||
}
|
||||
fclose(in);
|
||||
}
|
||||
|
||||
/*
|
||||
** Load a list of Blob objects from the database
|
||||
*/
|
||||
static void blobListLoadFromDb(
|
||||
sqlite3 *db, /* Read from this database */
|
||||
const char *zSql, /* Query used to extract the blobs */
|
||||
int onlyId, /* Only load where id is this value */
|
||||
int *pN, /* OUT: Write number of blobs loaded here */
|
||||
Blob **ppList /* OUT: Write the head of the blob list here */
|
||||
){
|
||||
Blob head;
|
||||
Blob *p;
|
||||
sqlite3_stmt *pStmt;
|
||||
int n = 0;
|
||||
int rc;
|
||||
char *z2;
|
||||
|
||||
if( onlyId>0 ){
|
||||
z2 = sqlite3_mprintf("%s WHERE rowid=%d", zSql, onlyId);
|
||||
}else{
|
||||
z2 = sqlite3_mprintf("%s", zSql);
|
||||
}
|
||||
rc = sqlite3_prepare_v2(db, z2, -1, &pStmt, 0);
|
||||
sqlite3_free(z2);
|
||||
if( rc ) fatalError("%s", sqlite3_errmsg(db));
|
||||
head.pNext = 0;
|
||||
p = &head;
|
||||
while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
int sz = sqlite3_column_bytes(pStmt, 1);
|
||||
Blob *pNew = safe_realloc(0, sizeof(*pNew)+sz );
|
||||
pNew->id = sqlite3_column_int(pStmt, 0);
|
||||
pNew->sz = sz;
|
||||
pNew->seq = n++;
|
||||
pNew->pNext = 0;
|
||||
memcpy(pNew->a, sqlite3_column_blob(pStmt,1), sz);
|
||||
pNew->a[sz] = 0;
|
||||
p->pNext = pNew;
|
||||
p = pNew;
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
*pN = n;
|
||||
*ppList = head.pNext;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free a list of Blob objects
|
||||
*/
|
||||
static void blobListFree(Blob *p){
|
||||
Blob *pNext;
|
||||
while( p ){
|
||||
pNext = p->pNext;
|
||||
free(p);
|
||||
p = pNext;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Return the current wall-clock time */
|
||||
static sqlite3_int64 timeOfDay(void){
|
||||
static sqlite3_vfs *clockVfs = 0;
|
||||
sqlite3_int64 t;
|
||||
if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0);
|
||||
if( clockVfs->iVersion>=1 && clockVfs->xCurrentTimeInt64!=0 ){
|
||||
clockVfs->xCurrentTimeInt64(clockVfs, &t);
|
||||
}else{
|
||||
double r;
|
||||
clockVfs->xCurrentTime(clockVfs, &r);
|
||||
t = (sqlite3_int64)(r*86400000.0);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Methods for the VHandle object
|
||||
*/
|
||||
static int inmemClose(sqlite3_file *pFile){
|
||||
VHandle *p = (VHandle*)pFile;
|
||||
VFile *pVFile = p->pVFile;
|
||||
pVFile->nRef--;
|
||||
if( pVFile->nRef==0 && pVFile->zFilename==0 ){
|
||||
pVFile->sz = -1;
|
||||
free(pVFile->a);
|
||||
pVFile->a = 0;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int inmemRead(
|
||||
sqlite3_file *pFile, /* Read from this open file */
|
||||
void *pData, /* Store content in this buffer */
|
||||
int iAmt, /* Bytes of content */
|
||||
sqlite3_int64 iOfst /* Start reading here */
|
||||
){
|
||||
VHandle *pHandle = (VHandle*)pFile;
|
||||
VFile *pVFile = pHandle->pVFile;
|
||||
if( iOfst<0 || iOfst>=pVFile->sz ){
|
||||
memset(pData, 0, iAmt);
|
||||
return SQLITE_IOERR_SHORT_READ;
|
||||
}
|
||||
if( iOfst+iAmt>pVFile->sz ){
|
||||
memset(pData, 0, iAmt);
|
||||
iAmt = (int)(pVFile->sz - iOfst);
|
||||
memcpy(pData, pVFile->a, iAmt);
|
||||
return SQLITE_IOERR_SHORT_READ;
|
||||
}
|
||||
memcpy(pData, pVFile->a + iOfst, iAmt);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int inmemWrite(
|
||||
sqlite3_file *pFile, /* Write to this file */
|
||||
const void *pData, /* Content to write */
|
||||
int iAmt, /* bytes to write */
|
||||
sqlite3_int64 iOfst /* Start writing here */
|
||||
){
|
||||
VHandle *pHandle = (VHandle*)pFile;
|
||||
VFile *pVFile = pHandle->pVFile;
|
||||
if( iOfst+iAmt > pVFile->sz ){
|
||||
if( iOfst+iAmt >= MX_FILE_SZ ){
|
||||
return SQLITE_FULL;
|
||||
}
|
||||
pVFile->a = safe_realloc(pVFile->a, (int)(iOfst+iAmt));
|
||||
if( iOfst > pVFile->sz ){
|
||||
memset(pVFile->a + pVFile->sz, 0, (int)(iOfst - pVFile->sz));
|
||||
}
|
||||
pVFile->sz = (int)(iOfst + iAmt);
|
||||
}
|
||||
memcpy(pVFile->a + iOfst, pData, iAmt);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int inmemTruncate(sqlite3_file *pFile, sqlite3_int64 iSize){
|
||||
VHandle *pHandle = (VHandle*)pFile;
|
||||
VFile *pVFile = pHandle->pVFile;
|
||||
if( pVFile->sz>iSize && iSize>=0 ) pVFile->sz = (int)iSize;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int inmemSync(sqlite3_file *pFile, int flags){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int inmemFileSize(sqlite3_file *pFile, sqlite3_int64 *pSize){
|
||||
*pSize = ((VHandle*)pFile)->pVFile->sz;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int inmemLock(sqlite3_file *pFile, int type){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int inmemUnlock(sqlite3_file *pFile, int type){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int inmemCheckReservedLock(sqlite3_file *pFile, int *pOut){
|
||||
*pOut = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int inmemFileControl(sqlite3_file *pFile, int op, void *pArg){
|
||||
return SQLITE_NOTFOUND;
|
||||
}
|
||||
static int inmemSectorSize(sqlite3_file *pFile){
|
||||
return 512;
|
||||
}
|
||||
static int inmemDeviceCharacteristics(sqlite3_file *pFile){
|
||||
return
|
||||
SQLITE_IOCAP_SAFE_APPEND |
|
||||
SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN |
|
||||
SQLITE_IOCAP_POWERSAFE_OVERWRITE;
|
||||
}
|
||||
|
||||
|
||||
/* Method table for VHandle
|
||||
*/
|
||||
static sqlite3_io_methods VHandleMethods = {
|
||||
/* iVersion */ 1,
|
||||
/* xClose */ inmemClose,
|
||||
/* xRead */ inmemRead,
|
||||
/* xWrite */ inmemWrite,
|
||||
/* xTruncate */ inmemTruncate,
|
||||
/* xSync */ inmemSync,
|
||||
/* xFileSize */ inmemFileSize,
|
||||
/* xLock */ inmemLock,
|
||||
/* xUnlock */ inmemUnlock,
|
||||
/* xCheck... */ inmemCheckReservedLock,
|
||||
/* xFileCtrl */ inmemFileControl,
|
||||
/* xSectorSz */ inmemSectorSize,
|
||||
/* xDevchar */ inmemDeviceCharacteristics,
|
||||
/* xShmMap */ 0,
|
||||
/* xShmLock */ 0,
|
||||
/* xShmBarrier */ 0,
|
||||
/* xShmUnmap */ 0,
|
||||
/* xFetch */ 0,
|
||||
/* xUnfetch */ 0
|
||||
};
|
||||
|
||||
/*
|
||||
** Open a new file in the inmem VFS. All files are anonymous and are
|
||||
** delete-on-close.
|
||||
*/
|
||||
static int inmemOpen(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zFilename,
|
||||
sqlite3_file *pFile,
|
||||
int openFlags,
|
||||
int *pOutFlags
|
||||
){
|
||||
VFile *pVFile = createVFile(zFilename, 0, (unsigned char*)"");
|
||||
VHandle *pHandle = (VHandle*)pFile;
|
||||
if( pVFile==0 ){
|
||||
return SQLITE_FULL;
|
||||
}
|
||||
pHandle->pVFile = pVFile;
|
||||
pVFile->nRef++;
|
||||
pFile->pMethods = &VHandleMethods;
|
||||
if( pOutFlags ) *pOutFlags = openFlags;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Delete a file by name
|
||||
*/
|
||||
static int inmemDelete(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zFilename,
|
||||
int syncdir
|
||||
){
|
||||
VFile *pVFile = findVFile(zFilename);
|
||||
if( pVFile==0 ) return SQLITE_OK;
|
||||
if( pVFile->nRef==0 ){
|
||||
free(pVFile->zFilename);
|
||||
pVFile->zFilename = 0;
|
||||
pVFile->sz = -1;
|
||||
free(pVFile->a);
|
||||
pVFile->a = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
return SQLITE_IOERR_DELETE;
|
||||
}
|
||||
|
||||
/* Check for the existance of a file
|
||||
*/
|
||||
static int inmemAccess(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zFilename,
|
||||
int flags,
|
||||
int *pResOut
|
||||
){
|
||||
VFile *pVFile = findVFile(zFilename);
|
||||
*pResOut = pVFile!=0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Get the canonical pathname for a file
|
||||
*/
|
||||
static int inmemFullPathname(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zFilename,
|
||||
int nOut,
|
||||
char *zOut
|
||||
){
|
||||
sqlite3_snprintf(nOut, zOut, "%s", zFilename);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* GetLastError() is never used */
|
||||
static int inmemGetLastError(sqlite3_vfs *pVfs, int n, char *z){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Register the VFS that reads from the g.aFile[] set of files.
|
||||
*/
|
||||
static void inmemVfsRegister(void){
|
||||
static sqlite3_vfs inmemVfs;
|
||||
sqlite3_vfs *pDefault = sqlite3_vfs_find(0);
|
||||
inmemVfs.iVersion = 1;
|
||||
inmemVfs.szOsFile = sizeof(VHandle);
|
||||
inmemVfs.mxPathname = 200;
|
||||
inmemVfs.zName = "inmem";
|
||||
inmemVfs.xOpen = inmemOpen;
|
||||
inmemVfs.xDelete = inmemDelete;
|
||||
inmemVfs.xAccess = inmemAccess;
|
||||
inmemVfs.xFullPathname = inmemFullPathname;
|
||||
inmemVfs.xRandomness = pDefault->xRandomness;
|
||||
inmemVfs.xSleep = pDefault->xSleep;
|
||||
inmemVfs.xCurrentTime = pDefault->xCurrentTime;
|
||||
inmemVfs.xGetLastError = inmemGetLastError;
|
||||
sqlite3_vfs_register(&inmemVfs, 0);
|
||||
};
|
||||
|
||||
/*
|
||||
** Allowed values for the runFlags parameter to runSql()
|
||||
*/
|
||||
#define SQL_TRACE 0x0001 /* Print each SQL statement as it is prepared */
|
||||
#define SQL_OUTPUT 0x0002 /* Show the SQL output */
|
||||
|
||||
/*
|
||||
** Run multiple commands of SQL. Similar to sqlite3_exec(), but does not
|
||||
** stop if an error is encountered.
|
||||
*/
|
||||
static void runSql(sqlite3 *db, const char *zSql, unsigned runFlags){
|
||||
const char *zMore;
|
||||
sqlite3_stmt *pStmt;
|
||||
|
||||
while( zSql && zSql[0] ){
|
||||
zMore = 0;
|
||||
pStmt = 0;
|
||||
sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zMore);
|
||||
if( zMore==zSql ) break;
|
||||
if( runFlags & SQL_TRACE ){
|
||||
const char *z = zSql;
|
||||
int n;
|
||||
while( z<zMore && isspace(z[0]) ) z++;
|
||||
n = (int)(zMore - z);
|
||||
while( n>0 && isspace(z[n-1]) ) n--;
|
||||
if( n==0 ) break;
|
||||
if( pStmt==0 ){
|
||||
printf("TRACE: %.*s (error: %s)\n", n, z, sqlite3_errmsg(db));
|
||||
}else{
|
||||
printf("TRACE: %.*s\n", n, z);
|
||||
}
|
||||
}
|
||||
zSql = zMore;
|
||||
if( pStmt ){
|
||||
if( (runFlags & SQL_OUTPUT)==0 ){
|
||||
while( SQLITE_ROW==sqlite3_step(pStmt) ){}
|
||||
}else{
|
||||
int nCol = -1;
|
||||
while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
int i;
|
||||
if( nCol<0 ){
|
||||
nCol = sqlite3_column_count(pStmt);
|
||||
}else if( nCol>0 ){
|
||||
printf("--------------------------------------------\n");
|
||||
}
|
||||
for(i=0; i<nCol; i++){
|
||||
int eType = sqlite3_column_type(pStmt,i);
|
||||
printf("%s = ", sqlite3_column_name(pStmt,i));
|
||||
switch( eType ){
|
||||
case SQLITE_NULL: {
|
||||
printf("NULL\n");
|
||||
break;
|
||||
}
|
||||
case SQLITE_INTEGER: {
|
||||
printf("INT %s\n", sqlite3_column_text(pStmt,i));
|
||||
break;
|
||||
}
|
||||
case SQLITE_FLOAT: {
|
||||
printf("FLOAT %s\n", sqlite3_column_text(pStmt,i));
|
||||
break;
|
||||
}
|
||||
case SQLITE_TEXT: {
|
||||
printf("TEXT [%s]\n", sqlite3_column_text(pStmt,i));
|
||||
break;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
printf("BLOB (%d bytes)\n", sqlite3_column_bytes(pStmt,i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Print sketchy documentation for this utility program
|
||||
*/
|
||||
static void showHelp(void){
|
||||
printf("Usage: %s [options] SOURCE-DB ?ARGS...?\n", g.zArgv0);
|
||||
printf(
|
||||
"Read databases and SQL scripts from SOURCE-DB and execute each script against\n"
|
||||
"each database, checking for crashes and memory leaks.\n"
|
||||
"Options:\n"
|
||||
" --cell-size-check Set the PRAGMA cell_size_check=ON\n"
|
||||
" --dbid N Use only the database where dbid=N\n"
|
||||
" --help Show this help text\n"
|
||||
" -q Reduced output\n"
|
||||
" --quiet Reduced output\n"
|
||||
" --load-sql ARGS... Load SQL scripts fro files into SOURCE-DB\n"
|
||||
" --load-db ARGS... Load template databases from files into SOURCE_DB\n"
|
||||
" -m TEXT Add a description to the database\n"
|
||||
" --native-vfs Use the native VFS for initially empty database files\n"
|
||||
" --result-trace Show the results of each SQL command\n"
|
||||
" --sqlid N Use only SQL where sqlid=N\n"
|
||||
" -v Increased output\n"
|
||||
" --verbose Increased output\n"
|
||||
);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv){
|
||||
sqlite3_int64 iBegin; /* Start time of this program */
|
||||
int quietFlag = 0; /* True if --quiet or -q */
|
||||
int verboseFlag = 0; /* True if --verbose or -v */
|
||||
char *zInsSql = 0; /* SQL statement for --load-db or --load-sql */
|
||||
int iFirstInsArg = 0; /* First argv[] to use for --load-db or --load-sql */
|
||||
sqlite3 *db = 0; /* The open database connection */
|
||||
sqlite3_stmt *pStmt; /* A prepared statement */
|
||||
int rc; /* Result code from SQLite interface calls */
|
||||
Blob *pSql; /* For looping over SQL scripts */
|
||||
Blob *pDb; /* For looping over template databases */
|
||||
int i; /* Loop index for the argv[] loop */
|
||||
int onlySqlid = -1; /* --sqlid */
|
||||
int onlyDbid = -1; /* --dbid */
|
||||
int nativeFlag = 0; /* --native-vfs */
|
||||
int runFlags = 0; /* Flags sent to runSql() */
|
||||
char *zMsg = 0; /* Add this message */
|
||||
int nSrcDb = 0; /* Number of source databases */
|
||||
char **azSrcDb = 0; /* Array of source database names */
|
||||
int iSrcDb; /* Loop over all source databases */
|
||||
int nTest = 0; /* Total number of tests performed */
|
||||
char *zDbName = ""; /* Appreviated name of a source database */
|
||||
const char *zFailCode = 0; /* Value of the TEST_FAILURE environment variable */
|
||||
int cellSzCkFlag = 0; /* --cell-size-check */
|
||||
|
||||
iBegin = timeOfDay();
|
||||
g.zArgv0 = argv[0];
|
||||
zFailCode = getenv("TEST_FAILURE");
|
||||
for(i=1; i<argc; i++){
|
||||
const char *z = argv[i];
|
||||
if( z[0]=='-' ){
|
||||
z++;
|
||||
if( z[0]=='-' ) z++;
|
||||
if( strcmp(z,"cell-size-check")==0 ){
|
||||
cellSzCkFlag = 1;
|
||||
}else
|
||||
if( strcmp(z,"dbid")==0 ){
|
||||
if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]);
|
||||
onlyDbid = atoi(argv[++i]);
|
||||
}else
|
||||
if( strcmp(z,"help")==0 ){
|
||||
showHelp();
|
||||
return 0;
|
||||
}else
|
||||
if( strcmp(z,"load-sql")==0 ){
|
||||
zInsSql = "INSERT INTO xsql(sqltext) VALUES(CAST(readfile(?1) AS text))";
|
||||
iFirstInsArg = i+1;
|
||||
break;
|
||||
}else
|
||||
if( strcmp(z,"load-db")==0 ){
|
||||
zInsSql = "INSERT INTO db(dbcontent) VALUES(readfile(?1))";
|
||||
iFirstInsArg = i+1;
|
||||
break;
|
||||
}else
|
||||
if( strcmp(z,"m")==0 ){
|
||||
if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]);
|
||||
zMsg = argv[++i];
|
||||
}else
|
||||
if( strcmp(z,"native-vfs")==0 ){
|
||||
nativeFlag = 1;
|
||||
}else
|
||||
if( strcmp(z,"quiet")==0 || strcmp(z,"q")==0 ){
|
||||
quietFlag = 1;
|
||||
verboseFlag = 0;
|
||||
}else
|
||||
if( strcmp(z,"result-trace")==0 ){
|
||||
runFlags |= SQL_OUTPUT;
|
||||
}else
|
||||
if( strcmp(z,"sqlid")==0 ){
|
||||
if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]);
|
||||
onlySqlid = atoi(argv[++i]);
|
||||
}else
|
||||
if( strcmp(z,"verbose")==0 || strcmp(z,"v")==0 ){
|
||||
quietFlag = 0;
|
||||
verboseFlag = 1;
|
||||
runFlags |= SQL_TRACE;
|
||||
}else
|
||||
{
|
||||
fatalError("unknown option: %s", argv[i]);
|
||||
}
|
||||
}else{
|
||||
nSrcDb++;
|
||||
azSrcDb = safe_realloc(azSrcDb, nSrcDb*sizeof(azSrcDb[0]));
|
||||
azSrcDb[nSrcDb-1] = argv[i];
|
||||
}
|
||||
}
|
||||
if( nSrcDb==0 ) fatalError("no source database specified");
|
||||
if( nSrcDb>1 ){
|
||||
if( zMsg ){
|
||||
fatalError("cannot change the description of more than one database");
|
||||
}
|
||||
if( zInsSql ){
|
||||
fatalError("cannot import into more than one database");
|
||||
}
|
||||
}
|
||||
|
||||
/* Process each source database separately */
|
||||
for(iSrcDb=0; iSrcDb<nSrcDb; iSrcDb++){
|
||||
rc = sqlite3_open(azSrcDb[iSrcDb], &db);
|
||||
if( rc ){
|
||||
fatalError("cannot open source database %s - %s",
|
||||
azSrcDb[iSrcDb], sqlite3_errmsg(db));
|
||||
}
|
||||
rc = sqlite3_exec(db,
|
||||
"CREATE TABLE IF NOT EXISTS db(\n"
|
||||
" dbid INTEGER PRIMARY KEY, -- database id\n"
|
||||
" dbcontent BLOB -- database disk file image\n"
|
||||
");\n"
|
||||
"CREATE TABLE IF NOT EXISTS xsql(\n"
|
||||
" sqlid INTEGER PRIMARY KEY, -- SQL script id\n"
|
||||
" sqltext TEXT -- Text of SQL statements to run\n"
|
||||
");"
|
||||
"CREATE TABLE IF NOT EXISTS readme(\n"
|
||||
" msg TEXT -- Human-readable description of this file\n"
|
||||
");", 0, 0, 0);
|
||||
if( rc ) fatalError("cannot create schema: %s", sqlite3_errmsg(db));
|
||||
if( zMsg ){
|
||||
char *zSql;
|
||||
zSql = sqlite3_mprintf(
|
||||
"DELETE FROM readme; INSERT INTO readme(msg) VALUES(%Q)", zMsg);
|
||||
rc = sqlite3_exec(db, zSql, 0, 0, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( rc ) fatalError("cannot change description: %s", sqlite3_errmsg(db));
|
||||
}
|
||||
if( zInsSql ){
|
||||
sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0,
|
||||
readfileFunc, 0, 0);
|
||||
rc = sqlite3_prepare_v2(db, zInsSql, -1, &pStmt, 0);
|
||||
if( rc ) fatalError("cannot prepare statement [%s]: %s",
|
||||
zInsSql, sqlite3_errmsg(db));
|
||||
rc = sqlite3_exec(db, "BEGIN", 0, 0, 0);
|
||||
if( rc ) fatalError("cannot start a transaction");
|
||||
for(i=iFirstInsArg; i<argc; i++){
|
||||
sqlite3_bind_text(pStmt, 1, argv[i], -1, SQLITE_STATIC);
|
||||
sqlite3_step(pStmt);
|
||||
rc = sqlite3_reset(pStmt);
|
||||
if( rc ) fatalError("insert failed for %s", argv[i]);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
rc = sqlite3_exec(db, "COMMIT", 0, 0, 0);
|
||||
if( rc ) fatalError("cannot commit the transaction: %s", sqlite3_errmsg(db));
|
||||
sqlite3_close(db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Load all SQL script content and all initial database images from the
|
||||
** source db
|
||||
*/
|
||||
blobListLoadFromDb(db, "SELECT sqlid, sqltext FROM xsql", onlySqlid,
|
||||
&g.nSql, &g.pFirstSql);
|
||||
if( g.nSql==0 ) fatalError("need at least one SQL script");
|
||||
blobListLoadFromDb(db, "SELECT dbid, dbcontent FROM db", onlyDbid,
|
||||
&g.nDb, &g.pFirstDb);
|
||||
if( g.nDb==0 ){
|
||||
g.pFirstDb = safe_realloc(0, sizeof(Blob));
|
||||
memset(g.pFirstDb, 0, sizeof(Blob));
|
||||
g.pFirstDb->id = 1;
|
||||
g.pFirstDb->seq = 0;
|
||||
g.nDb = 1;
|
||||
}
|
||||
|
||||
/* Print the description, if there is one */
|
||||
if( !quietFlag ){
|
||||
int i;
|
||||
zDbName = azSrcDb[iSrcDb];
|
||||
i = strlen(zDbName) - 1;
|
||||
while( i>0 && zDbName[i-1]!='/' && zDbName[i-1]!='\\' ){ i--; }
|
||||
zDbName += i;
|
||||
sqlite3_prepare_v2(db, "SELECT msg FROM readme", -1, &pStmt, 0);
|
||||
if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
printf("%s: %s\n", zDbName, sqlite3_column_text(pStmt,0));
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
}
|
||||
|
||||
/* Close the source database. Verify that no SQLite memory allocations are
|
||||
** outstanding.
|
||||
*/
|
||||
sqlite3_close(db);
|
||||
if( sqlite3_memory_used()>0 ){
|
||||
fatalError("SQLite has memory in use before the start of testing");
|
||||
}
|
||||
|
||||
/* Register the in-memory virtual filesystem
|
||||
*/
|
||||
formatVfs();
|
||||
inmemVfsRegister();
|
||||
|
||||
/* Run a test using each SQL script against each database.
|
||||
*/
|
||||
if( !verboseFlag && !quietFlag ) printf("%s:", zDbName);
|
||||
for(pSql=g.pFirstSql; pSql; pSql=pSql->pNext){
|
||||
for(pDb=g.pFirstDb; pDb; pDb=pDb->pNext){
|
||||
int openFlags;
|
||||
const char *zVfs = "inmem";
|
||||
sqlite3_snprintf(sizeof(g.zTestName), g.zTestName, "sqlid=%d,dbid=%d",
|
||||
pSql->id, pDb->id);
|
||||
if( verboseFlag ){
|
||||
printf("%s\n", g.zTestName);
|
||||
fflush(stdout);
|
||||
}else if( !quietFlag ){
|
||||
static int prevAmt = -1;
|
||||
int idx = pSql->seq*g.nDb + pDb->id - 1;
|
||||
int amt = idx*10/(g.nDb*g.nSql);
|
||||
if( amt!=prevAmt ){
|
||||
printf(" %d%%", amt*10);
|
||||
fflush(stdout);
|
||||
prevAmt = amt;
|
||||
}
|
||||
}
|
||||
createVFile("main.db", pDb->sz, pDb->a);
|
||||
openFlags = SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE;
|
||||
if( nativeFlag && pDb->sz==0 ){
|
||||
openFlags |= SQLITE_OPEN_MEMORY;
|
||||
zVfs = 0;
|
||||
}
|
||||
rc = sqlite3_open_v2("main.db", &db, openFlags, zVfs);
|
||||
if( rc ) fatalError("cannot open inmem database");
|
||||
if( cellSzCkFlag ) runSql(db, "PRAGMA cell_size_check=ON", runFlags);
|
||||
runSql(db, (char*)pSql->a, runFlags);
|
||||
sqlite3_close(db);
|
||||
if( sqlite3_memory_used()>0 ) fatalError("memory leak");
|
||||
reformatVfs();
|
||||
nTest++;
|
||||
g.zTestName[0] = 0;
|
||||
|
||||
/* Simulate an error if the TEST_FAILURE environment variable is "5".
|
||||
** This is used to verify that automated test script really do spot
|
||||
** errors that occur in this test program.
|
||||
*/
|
||||
if( zFailCode ){
|
||||
if( zFailCode[0]=='5' && zFailCode[1]==0 ){
|
||||
fatalError("simulated failure");
|
||||
}else if( zFailCode[0]!=0 ){
|
||||
/* If TEST_FAILURE is something other than 5, just exit the test
|
||||
** early */
|
||||
printf("\nExit early due to TEST_FAILURE being set\n");
|
||||
iSrcDb = nSrcDb-1;
|
||||
goto sourcedb_cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if( !quietFlag && !verboseFlag ){
|
||||
printf(" 100%% - %d tests\n", g.nDb*g.nSql);
|
||||
}
|
||||
|
||||
/* Clean up at the end of processing a single source database
|
||||
*/
|
||||
sourcedb_cleanup:
|
||||
blobListFree(g.pFirstSql);
|
||||
blobListFree(g.pFirstDb);
|
||||
reformatVfs();
|
||||
|
||||
} /* End loop over all source databases */
|
||||
|
||||
if( !quietFlag ){
|
||||
sqlite3_int64 iElapse = timeOfDay() - iBegin;
|
||||
printf("fuzzcheck: 0 errors out of %d tests in %d.%03d seconds\n"
|
||||
"SQLite %s %s\n",
|
||||
nTest, (int)(iElapse/1000), (int)(iElapse%1000),
|
||||
sqlite3_libversion(), sqlite3_sourceid());
|
||||
}
|
||||
free(azSrcDb);
|
||||
return 0;
|
||||
}
|
BIN
test/fuzzdata1.db
Normal file
BIN
test/fuzzdata1.db
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
test/fuzzdata3.db
Normal file
BIN
test/fuzzdata3.db
Normal file
Binary file not shown.
@@ -693,4 +693,12 @@ do_catchsql_test misc1-23.3 {
|
||||
DROP TABLE IF EXISTS t;
|
||||
} {0 {}}
|
||||
|
||||
|
||||
# At one point, running this would read one byte passed the end of a
|
||||
# buffer, upsetting valgrind.
|
||||
#
|
||||
do_test misc1-24.0 {
|
||||
list [catch { sqlite3_prepare_v2 db ! -1 dummy } msg] $msg
|
||||
} {1 {(1) unrecognized token: "!}}
|
||||
|
||||
finish_test
|
||||
|
@@ -1,113 +0,0 @@
|
||||
#!/usr/bin/tclsh
|
||||
#
|
||||
# Run this script in order to rebuild the fuzzdata1.txt file containing
|
||||
# fuzzer data for the fuzzershell utility that is create by afl-fuzz.
|
||||
#
|
||||
# This script gathers all of the test cases identified by afl-fuzz and
|
||||
# runs afl-cmin and afl-tmin over them all to try to generate a mimimum
|
||||
# set of tests that cover all observed behavior.
|
||||
#
|
||||
# Options:
|
||||
#
|
||||
# --afl-bin DIR1 DIR1 contains the AFL binaries
|
||||
# --fuzzershell PATH Full pathname of instrumented fuzzershell
|
||||
# --afl-data DIR3 DIR3 is the "-o" directory from afl-fuzz
|
||||
# -o FILE Write results into FILE
|
||||
#
|
||||
set AFLBIN {}
|
||||
set FUZZERSHELL {}
|
||||
set AFLDATA {}
|
||||
set OUTFILE {}
|
||||
|
||||
proc usage {} {
|
||||
puts stderr "Usage: $::argv0 --afl-bin DIR --fuzzershell PATH\
|
||||
--afl-data DIR -o FILE"
|
||||
exit 1
|
||||
}
|
||||
proc cmdlineerr {msg} {
|
||||
puts stderr $msg
|
||||
usage
|
||||
}
|
||||
|
||||
for {set i 0} {$i<[llength $argv]} {incr i} {
|
||||
set x [lindex $argv $i]
|
||||
if {[string index $x 0]!="-"} {cmdlineerr "illegal argument: $x"}
|
||||
set x [string trimleft $x -]
|
||||
incr i
|
||||
if {$i>=[llength $argv]} {cmdlineerr "no argument on --$x"}
|
||||
set a [lindex $argv $i]
|
||||
switch -- $x {
|
||||
afl-bin {set AFLBIN $a}
|
||||
afl-data {set AFLDATA $a}
|
||||
fuzzershell {set FUZZERSHELL $a}
|
||||
o {set OUTFILE $a}
|
||||
default {cmdlineerr "unknown option: --$x"}
|
||||
}
|
||||
}
|
||||
proc checkarg {varname option} {
|
||||
set val [set ::$varname]
|
||||
if {$val==""} {cmdlineerr "required option missing: --$option"}
|
||||
}
|
||||
checkarg AFLBIN afl-bin
|
||||
checkarg AFLDATA afl-data
|
||||
checkarg FUZZERSHELL fuzzershell
|
||||
checkarg OUTFILE o
|
||||
proc checkexec {x} {
|
||||
if {![file exec $x]} {cmdlineerr "cannot find $x"}
|
||||
}
|
||||
checkexec $AFLBIN/afl-cmin
|
||||
checkexec $AFLBIN/afl-tmin
|
||||
checkexec $FUZZERSHELL
|
||||
proc checkdir {x} {
|
||||
if {![file isdir $x]} {cmdlineerr "no such directory: $x"}
|
||||
}
|
||||
checkdir $AFLDATA/queue
|
||||
|
||||
proc progress {msg} {
|
||||
puts "******** $msg"
|
||||
flush stdout
|
||||
}
|
||||
progress "mkdir tmp1 tmp2"
|
||||
file mkdir tmp1 tmp2
|
||||
progress "copying test cases from $AFLDATA into tmp1..."
|
||||
set n 0
|
||||
foreach file [glob -nocomplain $AFLDATA/queue/id:*] {
|
||||
incr n
|
||||
file copy $file tmp1/$n
|
||||
}
|
||||
foreach file [glob -nocomplain $AFLDATA/crash*/id:*] {
|
||||
incr n
|
||||
file copy $file tmp1/$n
|
||||
}
|
||||
progress "total $n files copied."
|
||||
progress "running: $AFLBIN/afl-cmin -i tmp1 -o tmp2 $FUZZERSHELL"
|
||||
exec $AFLBIN/afl-cmin -i tmp1 -o tmp2 $FUZZERSHELL >&@ stdout
|
||||
progress "afl-cmin complete."
|
||||
#
|
||||
# Experiments show that running afl-tmin is too slow for this application.
|
||||
# And it doesn't really make the test cases that much smaller. So let's
|
||||
# just skip it.
|
||||
#
|
||||
# foreach file [glob tmp2/*] {
|
||||
# progress "$AFLBIN/afl-tmin -i $file -o tmp3/[file tail $file] $FUZZERSHELL"
|
||||
# exec $AFLBIN/afl-tmin -i $file -o tmp3/[file tail $file] \
|
||||
# $FUZZERSHELL >&@ stdout
|
||||
# }
|
||||
progress "generating final output into $OUTFILE"
|
||||
set out [open $OUTFILE wb]
|
||||
puts $out "# Test data for use with fuzzershell. Automatically
|
||||
# generated using $argv0. This file contains binary data
|
||||
#"
|
||||
set n 0
|
||||
foreach file [glob tmp2/*] {
|
||||
incr n
|
||||
puts -nonewline $out "/****<$n>****/"
|
||||
set in [open $file rb]
|
||||
puts -nonewline $out [read $in]
|
||||
close $in
|
||||
}
|
||||
close $out
|
||||
progress "done. $n test cases written to $OUTFILE"
|
||||
progress "clean-up..."
|
||||
file delete -force tmp1
|
||||
progress "culled test cases left in the tmp2 directory"
|
@@ -708,7 +708,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 e_wal* rowallock.test
|
||||
}]
|
||||
|
||||
ifcapable mem3 {
|
||||
|
@@ -225,7 +225,7 @@ array set ::Platforms [strip_comments {
|
||||
"No-lookaside" test
|
||||
"Devkit" test
|
||||
"Sanitize" {QUICKTEST_OMIT=func4.test,nan.test test}
|
||||
"Fast-One" fuzzoomtest
|
||||
"Fast-One" fuzztest
|
||||
"Valgrind" valgrindtest
|
||||
"Default" "threadtest fulltest"
|
||||
"Device-One" fulltest
|
||||
|
62
test/rowallock.test
Normal file
62
test/rowallock.test
Normal file
@@ -0,0 +1,62 @@
|
||||
|
||||
# 2015-05-28
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing locks on read-only WAL-mode databases.
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/lock_common.tcl
|
||||
set testprefix rowallock
|
||||
|
||||
do_multiclient_test tn {
|
||||
code2 { db2 close }
|
||||
code3 { db3 close }
|
||||
|
||||
do_execsql_test 1.$tn.1 {
|
||||
PRAGMA page_size = 4096;
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE t2(a, b);
|
||||
INSERT INTO t1 VALUES(1, 2), (3, 4);
|
||||
PRAGMA journal_mode = wal;
|
||||
} {wal}
|
||||
|
||||
code1 {
|
||||
db close
|
||||
sqlite3 db test.db -readonly 1
|
||||
}
|
||||
|
||||
do_execsql_test 1.$tn.2 {
|
||||
PRAGMA mmap_size = 1000000;
|
||||
} {1000000}
|
||||
do_execsql_test 1.$tn.2.1 {
|
||||
SELECT * FROM t1;
|
||||
} {1 2 3 4}
|
||||
|
||||
do_catchsql_test 1.$tn.3 {
|
||||
INSERT INTO t1 VALUES(5, 6);
|
||||
} {1 {attempt to write a readonly database}}
|
||||
|
||||
do_test 1.$tn.4 {
|
||||
code2 { sqlite3 db2 test.db }
|
||||
sql2 { INSERT INTO t1 VALUES(5, 6); }
|
||||
code2 { db2 close }
|
||||
file exists test.db-wal
|
||||
} {1}
|
||||
|
||||
do_test 1.$tn.5 {
|
||||
sql1 { SELECT * FROM t2 }
|
||||
code1 { db close }
|
||||
file exists test.db-wal
|
||||
} {1}
|
||||
}
|
||||
|
||||
finish_test
|
@@ -849,4 +849,13 @@ do_execsql_test 14.1 {
|
||||
WITH x AS (SELECT * FROM t) SELECT 0 EXCEPT SELECT 0 ORDER BY 1 COLLATE binary;
|
||||
} {}
|
||||
|
||||
# 2015-05-27: Do not allow rowid usage within a CTE
|
||||
#
|
||||
do_catchsql_test 15.1 {
|
||||
WITH RECURSIVE
|
||||
d(x) AS (VALUES(1) UNION ALL SELECT rowid+1 FROM d WHERE rowid<10)
|
||||
SELECT x FROM d;
|
||||
} {1 {no such column: rowid}}
|
||||
|
||||
|
||||
finish_test
|
||||
|
@@ -277,5 +277,56 @@ foreach {tn cnt where eqp} $queries {
|
||||
do_eqp_test 5.7.$tn.2 "SELECT count(*) FROM t46 WHERE $where" $eqp
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that redundant UNIQUE constraints do not cause a problem.
|
||||
#
|
||||
do_execsql_test 6.0 {
|
||||
CREATE TABLE t47(a, b UNIQUE PRIMARY KEY) WITHOUT ROWID;
|
||||
CREATE INDEX i47 ON t47(a);
|
||||
INSERT INTO t47 VALUES(1, 2);
|
||||
INSERT INTO t47 VALUES(2, 4);
|
||||
INSERT INTO t47 VALUES(3, 6);
|
||||
INSERT INTO t47 VALUES(4, 8);
|
||||
|
||||
VACUUM;
|
||||
PRAGMA integrity_check;
|
||||
SELECT name FROM sqlite_master WHERE tbl_name = 't47';
|
||||
} {ok t47 i47}
|
||||
|
||||
do_execsql_test 6.1 {
|
||||
CREATE TABLE t48(
|
||||
a UNIQUE UNIQUE,
|
||||
b UNIQUE,
|
||||
PRIMARY KEY(a),
|
||||
UNIQUE(a)
|
||||
) WITHOUT ROWID;
|
||||
INSERT INTO t48 VALUES('a', 'b'), ('c', 'd'), ('e', 'f');
|
||||
VACUUM;
|
||||
PRAGMA integrity_check;
|
||||
SELECT name FROM sqlite_master WHERE tbl_name = 't48';
|
||||
} {
|
||||
ok t48 sqlite_autoindex_t48_2
|
||||
}
|
||||
|
||||
# 2015-05-28: CHECK constraints can refer to the rowid in a
|
||||
# rowid table, but not in a WITHOUT ROWID table.
|
||||
#
|
||||
do_execsql_test 7.1 {
|
||||
CREATE TABLE t70a(
|
||||
a INT CHECK( rowid!=33 ),
|
||||
b TEXT PRIMARY KEY
|
||||
);
|
||||
INSERT INTO t70a(a,b) VALUES(99,'hello');
|
||||
} {}
|
||||
do_catchsql_test 7.2 {
|
||||
INSERT INTO t70a(rowid,a,b) VALUES(33,99,'xyzzy');
|
||||
} {1 {CHECK constraint failed: t70a}}
|
||||
do_catchsql_test 7.3 {
|
||||
CREATE TABLE t70b(
|
||||
a INT CHECK( rowid!=33 ),
|
||||
b TEXT PRIMARY KEY
|
||||
) WITHOUT ROWID;
|
||||
} {1 {no such column: rowid}}
|
||||
|
||||
|
||||
finish_test
|
||||
|
@@ -136,6 +136,10 @@ set pragma_def {
|
||||
IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
|
||||
IF: !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
|
||||
|
||||
NAME: cell_size_check
|
||||
TYPE: FLAG
|
||||
ARG: SQLITE_CellSizeCk
|
||||
|
||||
NAME: default_cache_size
|
||||
FLAG: NeedSchema
|
||||
IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED)
|
||||
|
Reference in New Issue
Block a user