diff --git a/Makefile.in b/Makefile.in index 5a17225983..bc312706c7 100644 --- a/Makefile.in +++ b/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 diff --git a/Makefile.msc b/Makefile.msc index 7e299d650d..8907eee575 100644 --- a/Makefile.msc +++ b/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. # diff --git a/ext/fts3/fts3.c b/ext/fts3/fts3.c index 8673ab7c3e..ac492ff1af 100644 --- a/ext/fts3/fts3.c +++ b/ext/fts3/fts3.c @@ -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; diff --git a/ext/fts3/fts3Int.h b/ext/fts3/fts3Int.h index 6db20b9fae..981c37deee 100644 --- a/ext/fts3/fts3Int.h +++ b/ext/fts3/fts3Int.h @@ -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 *); diff --git a/ext/fts3/fts3_snippet.c b/ext/fts3/fts3_snippet.c index a018a2bcdf..a0771c0b30 100644 --- a/ext/fts3/fts3_snippet.c +++ b/ext/fts3/fts3_snippet.c @@ -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); diff --git a/main.mk b/main.mk index d59a80b329..db11a96d59 100644 --- a/main.mk +++ b/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 diff --git a/manifest b/manifest index 365d439600..25dfe229ee 100644 --- a/manifest +++ b/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 diff --git a/manifest.uuid b/manifest.uuid index ef15aee619..70a7baacb5 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -519054bb72e8f8977b11161c81b0e96ba7bca589 \ No newline at end of file +54bec164ebeaf62d783352b3c4d0de8845394091 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 01ce974d75..cc97dc6b1f 100644 --- a/src/btree.c +++ b/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( pciCellLast ){ 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( cbrkusableSize ){ 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; ichildPtrSize; /* 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)=0 && idxnCell ); - 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; jaData[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; inCell-1); + if( pCell<&pLeaf->aData[4] ) return SQLITE_CORRUPT_BKPT; nCell = cellSizePtr(pLeaf, pCell); assert( MX_CELL_SIZE(pBt) >= nCell ); pTmp = pBt->pTmpSpace; diff --git a/src/build.c b/src/build.c index 44d75e9557..ac423a2856 100644 --- a/src/build.c +++ b/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 diff --git a/src/main.c b/src/main.c index e452b9a941..e0de69e75d 100644 --- a/src/main.c +++ b/src/main.c @@ -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); diff --git a/src/os_win.c b/src/os_win.c index 63afac81ae..d84bda4ef5 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -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 ); diff --git a/src/pager.c b/src/pager.c index 91378f0612..291c7e4968 100644 --- a/src/pager.c +++ b/src/pager.c @@ -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 */ diff --git a/src/parse.y b/src/parse.y index 72a0a6d222..d7aa763683 100644 --- a/src/parse.y +++ b/src/parse.y @@ -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; } } diff --git a/src/pragma.h b/src/pragma.h index c9ae8e6eb8..bbf141ee2d 100644 --- a/src/pragma.h +++ b/src/pragma.h @@ -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. */ diff --git a/src/resolve.c b/src/resolve.c index 23636eacec..27eba9fd07 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -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; diff --git a/src/select.c b/src/select.c index 8fc2dbde3d..e5e1a9988b 100644 --- a/src/select.c +++ b/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; knExpr; 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 diff --git a/src/sqlite.h.in b/src/sqlite.h.in index ffa9de1380..0358047da0 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -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 diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 06233353f5..6b08165bda 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -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 */ diff --git a/src/tokenize.c b/src/tokenize.c index 702cae03ab..b5934d2476 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -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]; diff --git a/src/vdbeaux.c b/src/vdbeaux.c index fa65f6a8e6..28018ed46d 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -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; diff --git a/src/where.c b/src/where.c index d0dfb78d2d..bcd3129189 100644 --- a/src/where.c +++ b/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(; iStartp1!=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(; kp1!=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; } diff --git a/test/autoindex5.test b/test/autoindex5.test new file mode 100644 index 0000000000..2d5fad2eda --- /dev/null +++ b/test/autoindex5.test @@ -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 diff --git a/test/corruptI.test b/test/corruptI.test index c8d0176236..2d4a481c77 100644 --- a/test/corruptI.test +++ b/test/corruptI.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 diff --git a/test/fts3defer2.test b/test/fts3defer2.test index 87af52461b..275e0f0eb3 100644 --- a/test/fts3defer2.test +++ b/test/fts3defer2.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 { diff --git a/test/fuzzcheck.c b/test/fuzzcheck.c new file mode 100644 index 0000000000..6dc2a6e7fb --- /dev/null +++ b/test/fuzzcheck.c @@ -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 +#include +#include +#include +#include +#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; i0 ){ + 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=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( z0 && 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=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; iSrcDbid = 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; +} diff --git a/test/fuzzdata1.db b/test/fuzzdata1.db new file mode 100644 index 0000000000..222e598a44 Binary files /dev/null and b/test/fuzzdata1.db differ diff --git a/test/fuzzdata1.txt b/test/fuzzdata1.txt deleted file mode 100644 index 6d3b20c131..0000000000 Binary files a/test/fuzzdata1.txt and /dev/null differ diff --git a/test/fuzzdata2.txt b/test/fuzzdata2.db similarity index 75% rename from test/fuzzdata2.txt rename to test/fuzzdata2.db index b1d4bb7bb1..598814622c 100644 Binary files a/test/fuzzdata2.txt and b/test/fuzzdata2.db differ diff --git a/test/fuzzdata3.db b/test/fuzzdata3.db new file mode 100644 index 0000000000..197654a9f6 Binary files /dev/null and b/test/fuzzdata3.db differ diff --git a/test/misc1.test b/test/misc1.test index 93f417721d..25e9bd813e 100644 --- a/test/misc1.test +++ b/test/misc1.test @@ -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 diff --git a/test/mkfuzzdata1.tcl b/test/mkfuzzdata1.tcl deleted file mode 100644 index 8af45f6254..0000000000 --- a/test/mkfuzzdata1.tcl +++ /dev/null @@ -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" diff --git a/test/permutations.test b/test/permutations.test index c13bf2901a..a76811fe8e 100644 --- a/test/permutations.test +++ b/test/permutations.test @@ -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 { diff --git a/test/releasetest.tcl b/test/releasetest.tcl index 2efce04c65..a120ddf336 100644 --- a/test/releasetest.tcl +++ b/test/releasetest.tcl @@ -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 diff --git a/test/rowallock.test b/test/rowallock.test new file mode 100644 index 0000000000..57dbbb2c2a --- /dev/null +++ b/test/rowallock.test @@ -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 diff --git a/test/with1.test b/test/with1.test index 80892d2642..8d8b6f75b5 100644 --- a/test/with1.test +++ b/test/with1.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 diff --git a/test/without_rowid1.test b/test/without_rowid1.test index 9d7a6430fe..0c77773abb 100644 --- a/test/without_rowid1.test +++ b/test/without_rowid1.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 diff --git a/tool/mkpragmatab.tcl b/tool/mkpragmatab.tcl index 964946e788..cb3aed0fff 100644 --- a/tool/mkpragmatab.tcl +++ b/tool/mkpragmatab.tcl @@ -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)