diff options
author | drh <drh@noemail.net> | 2013-11-26 18:00:29 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2013-11-26 18:00:29 +0000 |
commit | 1b2ee4fe1afb4edd3d04e62daeb133df41809e38 (patch) | |
tree | 3f90016343959f3349a8086cb43c38cb4de975a3 | |
parent | 212c6be1417860712de4b93b31342b72c9fa89dd (diff) | |
parent | 55fcab39be8186caac6c7d1262a7c943564623bb (diff) | |
download | sqlite-1b2ee4fe1afb4edd3d04e62daeb133df41809e38.tar.gz sqlite-1b2ee4fe1afb4edd3d04e62daeb133df41809e38.zip |
Merge in performance enhancements from trunk.
FossilOrigin-Name: fc9ae839569eb28eb734c52d95676c59b2e27494
54 files changed, 2325 insertions, 1012 deletions
diff --git a/Makefile.in b/Makefile.in index 9a5462d57..7e92e554a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -953,6 +953,9 @@ showdb$(TEXE): $(TOP)/tool/showdb.c sqlite3.c wordcount$(TEXE): $(TOP)/test/wordcount.c sqlite3.c $(LTLINK) -o $@ $(TOP)/test/wordcount.c sqlite3.c $(TLIBS) +speedtest1$(TEXE): $(TOP)/test/wordcount.c sqlite3.lo + $(LTLINK) -o $@ $(TOP)/test/speedtest1.c sqlite3.lo $(TLIBS) + # Standard install and cleanup targets # lib_install: libsqlite3.la diff --git a/Makefile.msc b/Makefile.msc index a83840137..1696e794d 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -1357,13 +1357,17 @@ sqlite3_analyzer.exe: sqlite3_analyzer.c $(LIBRESOBJS) $(LTLINK) -DBUILD_sqlite -DTCLSH=2 -I$(TCLINCDIR) sqlite3_analyzer.c \ /link $(LTLINKOPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LTLIBS) $(TLIBS) -showdb.exe: $(TOP)/tool/showdb.c sqlite3.c +showdb.exe: $(TOP)\tool\showdb.c sqlite3.c $(LTLINK) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o $@ \ - $(TOP)/tool/showdb.c sqlite3.c + $(TOP)\tool\showdb.c sqlite3.c -wordcount.exe: $(TOP)/test/wordcount.c sqlite3.c +wordcount.exe: $(TOP)\test\wordcount.c sqlite3.c $(LTLINK) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o $@ \ - $(TOP)/test/wordcount.c sqlite3.c + $(TOP)\test\wordcount.c sqlite3.c + +speedtest1.exe: $(TOP)\test\speedtest1.c sqlite3.c + $(LTLINK) -DSQLITE_OMIT_LOAD_EXTENSION -o $@ \ + $(TOP)\test\speedtest1.c sqlite3.c clean: del /Q *.lo *.ilk *.lib *.obj *.pdb sqlite3.exe libsqlite3.lib diff --git a/addopcodes.awk b/addopcodes.awk index c90e1dd7f..c18c4f359 100644 --- a/addopcodes.awk +++ b/addopcodes.awk @@ -28,7 +28,6 @@ END { printf "#define TK_%-29s %4d\n", "COLUMN", ++max printf "#define TK_%-29s %4d\n", "AGG_FUNCTION", ++max printf "#define TK_%-29s %4d\n", "AGG_COLUMN", ++max - printf "#define TK_%-29s %4d\n", "CONST_FUNC", ++max printf "#define TK_%-29s %4d\n", "UMINUS", ++max printf "#define TK_%-29s %4d\n", "UPLUS", ++max } diff --git a/ext/fts3/fts3.c b/ext/fts3/fts3.c index 1719bac7d..3b9efee54 100644 --- a/ext/fts3/fts3.c +++ b/ext/fts3/fts3.c @@ -333,6 +333,9 @@ int sqlite3Fts3PutVarint(char *p, sqlite_int64 v){ #define GETVARINT_STEP(v, ptr, shift, mask1, mask2, var, ret) \ v = (v & mask1) | ( (*ptr++) << shift ); \ if( (v & mask2)==0 ){ var = v; return ret; } +#define GETVARINT_INIT(v, ptr, shift, mask1, mask2, var, ret) \ + v = (*ptr++); \ + if( (v & mask2)==0 ){ var = v; return ret; } /* ** Read a 64-bit variable-length integer from memory starting at p[0]. @@ -345,7 +348,7 @@ int sqlite3Fts3GetVarint(const char *p, sqlite_int64 *v){ u64 b; int shift; - GETVARINT_STEP(a, p, 0, 0x00, 0x80, *v, 1); + GETVARINT_INIT(a, p, 0, 0x00, 0x80, *v, 1); GETVARINT_STEP(a, p, 7, 0x7F, 0x4000, *v, 2); GETVARINT_STEP(a, p, 14, 0x3FFF, 0x200000, *v, 3); GETVARINT_STEP(a, p, 21, 0x1FFFFF, 0x10000000, *v, 4); @@ -357,7 +360,7 @@ int sqlite3Fts3GetVarint(const char *p, sqlite_int64 *v){ if( (c & 0x80)==0 ) break; } *v = b; - return p - pStart; + return (int)(p - pStart); } /* @@ -368,7 +371,7 @@ int sqlite3Fts3GetVarint32(const char *p, int *pi){ u32 a; #ifndef fts3GetVarint32 - GETVARINT_STEP(a, p, 0, 0x00, 0x80, *pi, 1); + GETVARINT_INIT(a, p, 0, 0x00, 0x80, *pi, 1); #else a = (*p++); assert( a & 0x80 ); @@ -641,6 +641,9 @@ wordcount$(EXE): $(TOP)/test/wordcount.c sqlite3.c $(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o wordcount$(EXE) \ $(TOP)/test/wordcount.c sqlite3.c +speedtest1$(EXE): $(TOP)/test/speedtest1.c sqlite3.o + $(TCC) -o speedtest1$(EXE) $(TOP)/test/speedtest1.c sqlite3.o $(THREADLIB) + # This target will fail if the SQLite amalgamation contains any exported # symbols that do not begin with "sqlite3_". It is run as part of the # releasetest.tcl script. @@ -1,14 +1,14 @@ -C Merge\sthe\sskip-scan\soptimization\sinto\sthe\ssessions\sbranch. -D 2013-11-14T19:18:39.185 +C Merge\sin\sperformance\senhancements\sfrom\strunk. +D 2013-11-26T18:00:29.602 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f -F Makefile.in 0c169e6029407371c6951ae77f96049990eb522d +F Makefile.in 06b851f767034811d4f6e159367c453dc28d3925 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 -F Makefile.msc cd32b587a359ab6d87f3e6ff84168c79b8d97aba +F Makefile.msc da7f2a230af6e39b71b2706dbb4c35fff7222a0f F Makefile.vxworks db21ed42a01d5740e656b16f92cb5d8d5e5dd315 F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6 F VERSION 52f7e22bfcec71a462e34194b4ae1671380fde59 F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 -F addopcodes.awk 17dc593f791f874d2c23a0f9360850ded0286531 +F addopcodes.awk 87ca612393d0f439550634bd2c156ea9ff6195ae F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2 F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90 F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2 @@ -78,7 +78,7 @@ 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 dceaa5079833caa2e7945433e1eb93bb22f623a3 +F ext/fts3/fts3.c 1e667eacb3fe4b4ad6f863920da4286f071f6e07 F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe F ext/fts3/fts3Int.h eb5f8029589f3d8f1dc7fd50c773326a640388b1 F ext/fts3/fts3_aux.c 5c211e17a64885faeb16b9ba7772f9d5445c2365 @@ -156,12 +156,12 @@ F ext/session/test_session.c d38968307c05229cc8cd603722cf305d6f768832 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt f439556c5ce01ced70987e5ee86549a45165d9ff -F main.mk df62cb5cde201a3efacba236fdb74432863554f6 +F main.mk a0a333b9ec8d5156c0b8f1a9e91da883a1890cfe F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f F mkextw.sh d2a981497b404d6498f5ff3e3b1f3816bdfcb338 F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea -F mkopcodeh.awk 987ee588ff3bb4043bed2185c1ee2bdc39b1e526 +F mkopcodeh.awk c6b3fa301db6ef7ac916b14c60868aeaec1337b5 F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504 @@ -177,38 +177,38 @@ F src/alter.c 2af0330bb1b601af7a7789bf7229675fd772a083 F src/analyze.c 581d5c18ce89c6f45d4dca65914d0de5b4dad41f F src/attach.c 0a17c9364895316ca4f52d06a97a72c0af1ae8b3 F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 -F src/backup.c 2f1987981139bd2f6d8c728d64bf09fb387443c3 +F src/backup.c 1809a7caa2504233bdddd12f5018422421789537 F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7 -F src/btree.c 260dedc13119e6fb7930380bd3d294b98362bf5a -F src/btree.h bfe0e8c5759b4ec77b0d18390064a6ef3cdffaaf +F src/btree.c ec8a4dcac5c1ec1ba9705b8c5a13e62167958317 +F src/btree.h a61ddebc78c66795a2b93181321a116746302cc9 F src/btreeInt.h f038e818bfadf75afbd09819ed93c26a333d39e0 -F src/build.c 2baeed38bdaa9f1199f101c63db41fdcc4b39ba5 +F src/build.c 07054d45319953e54a89d726e589a423e9c1c590 F src/callback.c f99a8957ba2adf369645fac0db09ad8adcf1caa2 F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac F src/ctime.c ea4b7f3623a0fcb1146e7f245d7410033e86859c F src/date.c 593c744b2623971e45affd0bde347631bdfa4625 -F src/delete.c 714f86ed9d563f12313d92d600ae1c9e68c6fa14 -F src/expr.c e7bbe3c6916e141f27a28655d3cf325b817695e4 +F src/delete.c 65c34400e401c482501dac5cd14a12621260f6d0 +F src/expr.c 31a2b65339f6c3795d4cfa5e99798cd72f9fdfdf F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb -F src/fkey.c 78364daed38e26269c53ddb94c515bceac1063c6 -F src/func.c 96caa9dfd1febf9a4b720de4c43ccfb392a52b73 +F src/fkey.c 2ab0f5384b70594468ef3ac5c7ed8ca24bfd17d5 +F src/func.c ef30d26ae4d79bbc7300c74e77fd117a0ba30235 F src/global.c 5caf4deab621abb45b4c607aad1bd21c20aac759 F src/hash.c ac3470bbf1ca4ae4e306a8ecb0fdf1731810ffe4 F src/hash.h 8890a25af81fb85a9ad7790d32eedab4b994da22 F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08 -F src/insert.c 0f4d5b41d1cd8291e98af33587f2a4bbb3d59abb +F src/insert.c db64e62555482dcdc3903b0f01d910094ac0dcb2 F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d F src/legacy.c 0df0b1550b9cc1f58229644735e317ac89131f12 F src/lempar.c cdf0a000315332fc9b50b62f3b5e22e080a0952b F src/loadext.c 867c7b330b740c6c917af9956b13b81d0a048303 -F src/main.c d802034d4a145e440b651dc67c590355f41c64b5 +F src/main.c ce3f0087c9976bed2c8955ad55f99353f0cdc8fb F src/malloc.c 543a8eb5508eaf4cadf55a9b503379eba2088128 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c c0c990fcaddff810ea277b4fb5d9138603dd5d4b F src/mem2.c dce31758da87ec2cfa52ba4c5df1aed6e07d8e8f F src/mem3.c 61c9d47b792908c532ca3a62b999cf21795c6534 -F src/mem5.c 0025308a93838022bd5696cf9627ff4e40b19918 +F src/mem5.c 2221f7e4619619d2df16fcb8a1da2a165ae56d9d F src/memjournal.c 0683aac6cab6ec2b5374c0db37c0deb2436a3785 F src/mutex.c d3b66a569368015e0fcb1ac15f81c119f504d3bc F src/mutex.h 5bc526e19dccc412b7ff04642f6fdad3fdfdabea @@ -220,29 +220,29 @@ F src/os.c b4ad71336fd96f97776f75587cd9e8218288f5be F src/os.h 4a46270a64e9193af4a0aaa3bc2c66dc07c29b3f F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04 F src/os_unix.c 143624d9eabb3b997c59cf594e0d06c56edd43e9 -F src/os_win.c ef091b347d682cb24fc575ac9a6290341af62e2b +F src/os_win.c cb3f26205ea785a63ea5bdc21a9c9f9ae3972d0f F src/pager.c 2aa4444ffe86e9282d03bc349a4a5e49bd77c0e8 F src/pager.h f094af9f6ececfaa8a1e93876905a4f34233fb0c -F src/parse.y 073a8294e1826f1b1656e84806b77e4199f4bb57 +F src/parse.y acee1a9958539e21263362b194594c5255ad2fca F src/pcache.c f8043b433a57aba85384a531e3937a804432a346 F src/pcache.h a5e4f5d9f5d592051d91212c5949517971ae6222 F src/pcache1.c a467393909a4ed7ca9de066d85ba5c5b04a5be63 -F src/pragma.c c8d70c47ec8d8ba93575d92e34d30ddff8e9b517 -F src/prepare.c fa6988589f39af8504a61731614cd4f6ae71554f +F src/pragma.c 5ab7279d132143feb77f773688a24ab05da75fd7 +F src/prepare.c 359d1a1e9c9bd4488e4dd3a1aaaf2d2ebb9bb768 F src/printf.c da9119eb31a187a4b99f60aa4a225141c0ebb74b F src/random.c 0b2dbc37fdfbfa6bd455b091dfcef5bdb32dba68 -F src/resolve.c fc4673cc49b116e51e7f12de074c0acf8f2388f9 +F src/resolve.c a70e32ae6ccb7b780f2b6d3e9e21837affc25ee5 F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0 -F src/select.c 7317406831ecced390edba972818f3c5f82238c0 -F src/shell.c 6ccc22b717afe4f6d7d3c8b6baa02e3b99a5fdf0 -F src/sqlite.h.in 7bdef2ad0d798aa6bdeb5408493e9da62e3083d3 +F src/select.c d41381d80a22d3a83352aeca274cccf264ac277a +F src/shell.c c4d06a9238a515ff4bc86b8626139633c09a00a2 +F src/sqlite.h.in c84ef520934601e4d1a95c6858fef71dc369e940 F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc -F src/sqliteInt.h a14cf06840ecc21f272b56f72d717cd2d55b5662 +F src/sqliteInt.h dad3dff932c055304fc75b339f2cf68aab9cf19e F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e -F src/tclsqlite.c c63a05eff963c3c8d412441301df8246372d072f +F src/tclsqlite.c 758fa6b6cfd39330db8b71b1f94e46f03ef375b8 F src/test1.c 5757066e503a8ed51313cb3a5d9bcdcced2991a9 F src/test2.c 7355101c085304b90024f2261e056cdff13c6c35 F src/test3.c 1c0e5d6f080b8e33c1ce8b3078e7013fdbcd560c @@ -256,7 +256,7 @@ F src/test_async.c 21e11293a2f72080eda70e1124e9102044531cd8 F src/test_autoext.c dea8a01a7153b9adc97bd26161e4226329546e12 F src/test_backup.c 3875e899222b651e18b662f86e0e50daa946344e F src/test_btree.c 5b89601dcb42a33ba8b820a6b763cc9cb48bac16 -F src/test_config.c 740b371afbfa12d47fd44d515a685b00c4015850 +F src/test_config.c cb3342a4d66e1121f094f3c2c165b72bbe289a2b F src/test_demovfs.c 69b2085076654ebc18014cbc6386f04409c959a9 F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc F src/test_fs.c ced436e3d4b8e4681328409b8081051ce614e28f @@ -267,7 +267,7 @@ F src/test_intarray.c 87847c71c3c36889c0bcc9c4baf9d31881665d61 F src/test_intarray.h 2ece66438cfd177b78d1bfda7a4180cd3a10844d F src/test_journal.c f5c0a05b7b3d5930db769b5ee6c3766dc2221a64 F src/test_loadext.c df586c27176e3c2cb2e099c78da67bf14379a56e -F src/test_malloc.c eba4e1c5847cc98e7edc98f62265cd2abafba7a6 +F src/test_malloc.c 1ff5b1243d96124c9a180f3b89424820a1f337f3 F src/test_multiplex.c 9f304bf04170c91c0318238d512df2da039eb1c8 F src/test_multiplex.h 110a8c4d356e0aa464ca8730375608a9a0b61ae1 F src/test_mutex.c 293042d623ebba969160f471a82aa1551626454f @@ -289,25 +289,25 @@ F src/test_vfs.c e72f555ef7a59080f898fcf1a233deb9eb704ea9 F src/test_vfstrace.c 34b544e80ba7fb77be15395a609c669df2e660a2 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/tokenize.c ec4c1a62b890bf1dbcdb966399e140b904c700a4 -F src/trigger.c 53d6b5d50b3b23d4fcd0a36504feb5cff9aed716 -F src/update.c faee30d7aa32a1196b9eb751b8e50d9ff0ad6ea7 +F src/trigger.c d84e1f3669e9a217731a14a9d472b1c7b87c87ba +F src/update.c 046d7df2a4b3d85442a758f484eb2d40a48b5465 F src/utf.c 6fc6c88d50448c469c5c196acf21617a24f90269 -F src/util.c 2fa6c821d28bbdbeec1b2a7b091a281c9ef8f918 +F src/util.c cbe054290f780fcd472b89d701c7404c51ec9684 F src/vacuum.c 3728d74919d4fb1356f9e9a13e27773db60b7179 -F src/vdbe.c b1939060ddf3ff9039dd2c6896ed79f4e51e103a +F src/vdbe.c 520dcda659ff32e3da7d141e9ac083d3599a2079 F src/vdbe.h b7bfa7b468fcad2cf1890969fe7459325da00385 -F src/vdbeInt.h 40461ecbac26fa15160320e45a38dc935058e752 +F src/vdbeInt.h 1a5c604f33a5d46c839fee0cab16743aa3e1bc2e F src/vdbeapi.c 8ade912f7023a3b35ee64497a94718ddbd7269c3 -F src/vdbeaux.c eade21d57ce118e433bbdc0ac9ff06c75cdb7675 -F src/vdbeblob.c 0ab871fa7466efaef05877f06d650f0f7401cbe0 -F src/vdbemem.c cc529bbf4f13e4e181bdb446bf6e6962ab030b4b +F src/vdbeaux.c 9270db4725c0143e572a2df660fabac7104a9db3 +F src/vdbeblob.c a2809461743e0b9dd9be871149ac65e8d2c80c08 +F src/vdbemem.c af650c2019dc197f062440cdb4650b7204e648bf F src/vdbesort.c 9d83601f9d6243fe70dd0169a2820c5ddfd48147 F src/vdbetrace.c e7ec40e1999ff3c6414424365d5941178966dcbc -F src/vtab.c 5a423b042eb1402ef77697d03d6a67378d97bc8d +F src/vtab.c 21b932841e51ebd7d075e2d0ad1415dce8d2d5fd F src/wal.c 7dc3966ef98b74422267e7e6e46e07ff6c6eb1b4 F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 F src/walker.c e9e593d5bb798c3e67fc3893dfe7055c9e7d8d74 -F src/where.c 2eb88f96a73d77bd61d6afd50c33ec5d63c15dd5 +F src/where.c e558bfa67009ab7de08a7a1960ae0dd443241cdd F src/whereInt.h 96a75c61f1d2b9d4a8e4bb17d89deb0cf7cba358 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -327,7 +327,7 @@ F test/analyze5.test 765c4e284aa69ca172772aa940946f55629bc8c4 F test/analyze6.test d31defa011a561b938b4608d3538c1b4e0b5e92c F test/analyze7.test bb1409afc9e8629e414387ef048b8e0e3e0bdc4f F test/analyze8.test 093d15c1c888eed5034304a98c992f7360130b88 -F test/analyze9.test 1b9b7e9a096d1536f03d9ad7b72f638ef5669347 +F test/analyze9.test 339e87723cd4dc158dc5e9095acd8df9e87faf79 F test/analyzeA.test 1a5c40079894847976d983ca39c707aaa44b6944 F test/analyzeB.test 8bf35ee0a548aea831bf56762cb8e7fdb1db083d F test/async.test 1d0e056ba1bb9729283a0f22718d3a25e82c277b @@ -335,7 +335,7 @@ F test/async2.test c0a9bd20816d7d6a2ceca7b8c03d3d69c28ffb8b F test/async3.test d73a062002376d7edc1fe3edff493edbec1fc2f7 F test/async4.test 1787e3952128aa10238bf39945126de7ca23685a F test/async5.test 383ab533fdb9f7ad228cc99ee66e1acb34cc0dc0 -F test/atof1.test 9bf1d25180a2e05fc12ce3940cc8003033642f68 +F test/atof1.test 08a61df9365c341f334a65f4348897312d8f3db7 F test/attach.test 0d112b7713611fdf0340260192749737135fda5f F test/attach2.test 0ec5defa340363de6cd50fd595046465e9aaba2d F test/attach3.test d89ccfe4fe6e2b5e368d480fcdfe4b496c54cf4e @@ -344,7 +344,7 @@ F test/attachmalloc.test 3a4bfca9545bfe906a8d2e622de10fbac5b711b0 F test/auth.test 9bea29041871807d9f289ee679d05d3ed103642f F test/auth2.test c3b415b76c033bedb81292118fb7c01f5f10cbcd F test/auth3.test a4755e6a2a2fea547ffe63c874eb569e60a28eb5 -F test/autoinc.test bd30d372d00045252f6c2e41b5f41455e1975acf +F test/autoinc.test c58912526998a39e11f66b533e23cfabea7f25b7 F test/autoindex1.test d4dfe14001dfcb74cfbd7107f45a79fc1ab6183e F test/autovacuum.test 941892505d2c0f410a0cb5970dfa1c7c4e5f6e74 F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4 @@ -437,7 +437,7 @@ F test/ctime.test 7bd009071e242aac4f18521581536b652b789a47 F test/date.test 42973251b9429f2c41b77eb98a7b0b0ba2d3b2c0 F test/dbstatus.test 8de104bb5606f19537d23cd553b41349b5ab1204 F test/dbstatus2.test 10418e62b3db5dca070f0c3eef3ea13946f339c2 -F test/default.test 6faf23ccb300114924353007795aa9a8ec0aa9dc +F test/default.test 792c3c70836f1901e2a8cb34fa0880ed71e2c1a9 F test/delete.test a065b05d2ebf60fd16639c579a4adfb7c381c701 F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa F test/delete3.test 555e84a00a99230b7d049d477a324a631126a6ab @@ -451,7 +451,7 @@ F test/e_createtable.test 3b453432cd14a12732ee9467597d2274ca37ce36 F test/e_delete.test d5186e2f5478b659f16a2c8b66c09892823e542a F test/e_droptrigger.test 3cd080807622c13e5bbb61fc9a57bd7754da2412 F test/e_dropview.test 0c9f7f60989164a70a67a9d9c26d1083bc808306 -F test/e_expr.test d5cdda0e4ffb17760858ed4c7c4ece07efc40f71 +F test/e_expr.test 0808bc72c7b2a2fab4291418ecd73f4d09d7d671 F test/e_fkey.test d83a04478bb9c02d2c513518548a69f818869f41 F test/e_fts3.test 5c02288842e4f941896fd44afdef564dd5fc1459 F test/e_insert.test 1e44f84d2abe44d66e4fbf198be4b20e3cc724a0 @@ -587,10 +587,11 @@ F test/fts4merge4.test c19c85ca1faa7b6d536832b49c12e1867235f584 F test/fts4noti.test aed33ba44808852dcb24bf70fa132e7bf530f057 F test/fts4unicode.test e28ba1a14181e709dcdf47455f207adf14c7cfe0 F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d -F test/func.test c7e80a44eebac8604397eb2ad83d0d5d9d541237 +F test/func.test 00667bbeac044d007f6f021af1b9f6150f0c7ff8 F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f F test/func3.test dbccee9133cfef1473c59ec07b5f0262b9d72f9a F test/func4.test 6beacdfcb0e18c358e6c2dcacf1b65d1fa80955f +F test/func5.test 1435dd313c0bae70d6af089c97a2a997fc5d0e53 F test/fuzz-oss1.test 4912e528ec9cf2f42134456933659d371c9e0d74 F test/fuzz.test 77fd50afc12847af50fcf1941679d90adebadde6 F test/fuzz2.test 207d0f9d06db3eaf47a6b7bfc835b8e2fc397167 @@ -711,7 +712,7 @@ F test/misc3.test cf3dda47d5dda3e53fc5804a100d3c82be736c9d F test/misc4.test 9c078510fbfff05a9869a0b6d8b86a623ad2c4f6 F test/misc5.test 528468b26d03303b1f047146e5eefc941b9069f5 F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91 -F test/misc7.test 50c02c35ef7924c246eb3d8d71dfbf90ba352f8f +F test/misc7.test 1265eb98c2e22a446a13fdef754118b272716684 F test/misuse.test ba4fb5d1a6101d1c171ea38b3c613d0661c83054 F test/mmap1.test 93d167b328255cbe6679fe1e1a23be1b1197d07b F test/mmap2.test 9d6dd9ddb4ad2379f29cc78f38ce1e63ed418022 @@ -771,6 +772,7 @@ F test/rollback.test e9504a009a202c3ed711da2e6879ff60c5a4669c F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81 F test/rowid.test f777404492adb0e00868fd706a3721328fd3af48 F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798 +F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09 F test/savepoint.test 6c53f76dffe5df0dd87646efe3e7aa159c36e07b F test/savepoint2.test 9b8543940572a2f01a18298c3135ad0c9f4f67d7 F test/savepoint3.test e328085853b14898d78ceea00dfe7db18bb6a9ec @@ -832,6 +834,7 @@ F test/speed3.test d32043614c08c53eafdc80f33191d5bd9b920523 F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715 F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b +F test/speedtest1.c aa08ae8e3591bf5c028be9396a84237640761d8c F test/spellfix.test 8c40b169b104086d8795781f670ba3c786d6d8be F test/sqllimits1.test b1aae27cc98eceb845e7f7adf918561256e31298 F test/stat.test c8eccfe8fcd3f3cfc864ce22d5b9e803a3c69940 @@ -1096,16 +1099,17 @@ F test/whereF.test 5b2ba0dbe8074aa13e416b37c753991f0a2492d7 F test/whereG.test 2a3d5181decc801b36600fa1c40b0dad2ccc267f F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31 F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c +F test/win32heap.test ea19770974795cff26e11575e12d422dbd16893c F test/win32lock.test 7a6bd73a5dcdee39b5bb93e92395e1773a194361 F test/win32longpath.test e2aafc07e6990fe86c69be22a3d1a0e210cd329b F test/without_rowid1.test aaa26da19d543cd8d3d2d0e686dfa255556c15c8 F test/without_rowid2.test af260339f79d13cb220288b67cd287fbcf81ad99 F test/without_rowid3.test eac3d5c8a1924725b58503a368f2cbd24fd6c8a0 F test/without_rowid4.test 4e08bcbaee0399f35d58b5581881e7a6243d458a -F test/wordcount.c 2c2cc1119de42e6730ca8bd149666f0e0095108a +F test/wordcount.c 9915e06cb33d8ca8109b8700791afe80d305afda F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688 F test/zerodamage.test 209d7ed441f44cc5299e4ebffbef06fd5aabfefd -F tool/build-all-msvc.bat 1bac6adc3fdb4d9204f21d17b14be25778370e48 x +F tool/build-all-msvc.bat e0917e787df675b020d250d60a00de8abaa4e30a x F tool/build-shell.sh 950f47c6174f1eea171319438b93ba67ff5bf367 F tool/checkSpacing.c 810e51703529a204fc4e1eb060e9ab663e3c06d2 F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b @@ -1154,7 +1158,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01 F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff -P 20eeee4cd34a9bffe6bf65962126ddf8ca04eb3e 24ef16548eebcdb9d8b40308f6a16dabf8f8d474 -R 7c88b1c3df374bef8148611e14f79210 +P 7596d1bf8040f7cefc7b22c5e609acc5d66820bf 6f91dca0de908dc2b15130a6593a61c3147a409f +R 4bfa42555a9c1bb80b9d6441159a988c U drh -Z 1904213259af66fd28163fe87df8620f +Z ab8c142eff787cc8d595e6a709628910 diff --git a/manifest.uuid b/manifest.uuid index bd2ba610d..c76da8bbb 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7596d1bf8040f7cefc7b22c5e609acc5d66820bf
\ No newline at end of file +fc9ae839569eb28eb734c52d95676c59b2e27494
\ No newline at end of file diff --git a/mkopcodeh.awk b/mkopcodeh.awk index f8da18e30..babfdc68d 100644 --- a/mkopcodeh.awk +++ b/mkopcodeh.awk @@ -136,8 +136,10 @@ END { || name=="OP_VUpdate" \ || name=="OP_VFilter" \ || name=="OP_Next" \ + || name=="OP_NextIfOpen" \ || name=="OP_SorterNext" \ || name=="OP_Prev" \ + || name=="OP_PrevIfOpen" \ ){ cnt++ while( used[cnt] ) cnt++ diff --git a/src/backup.c b/src/backup.c index afbaeeb10..1bac821f3 100644 --- a/src/backup.c +++ b/src/backup.c @@ -96,6 +96,7 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){ rc = SQLITE_ERROR; } sqlite3DbFree(pErrorDb, pParse->zErrMsg); + sqlite3ParserReset(pParse); sqlite3StackFree(pErrorDb, pParse); } if( rc ){ diff --git a/src/btree.c b/src/btree.c index cebf95e8d..27e714689 100644 --- a/src/btree.c +++ b/src/btree.c @@ -4206,7 +4206,7 @@ int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ */ static const unsigned char *fetchPayload( BtCursor *pCur, /* Cursor pointing to entry to read from */ - int *pAmt, /* Write the number of available bytes here */ + u32 *pAmt, /* Write the number of available bytes here */ int skipKey /* read beginning at data if this is true */ ){ unsigned char *aPayload; @@ -4219,7 +4219,7 @@ static const unsigned char *fetchPayload( assert( cursorHoldsMutex(pCur) ); pPage = pCur->apPage[pCur->iPage]; assert( pCur->aiIdx[pCur->iPage]<pPage->nCell ); - if( NEVER(pCur->info.nSize==0) ){ + if( pCur->info.nSize==0 ){ btreeParseCell(pCur->apPage[pCur->iPage], pCur->aiIdx[pCur->iPage], &pCur->info); } @@ -4256,7 +4256,7 @@ static const unsigned char *fetchPayload( ** These routines is used to get quick access to key and data ** in the common case where no overflow pages are used. */ -const void *sqlite3BtreeKeyFetch(BtCursor *pCur, int *pAmt){ +const void *sqlite3BtreeKeyFetch(BtCursor *pCur, u32 *pAmt){ const void *p = 0; assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); assert( cursorHoldsMutex(pCur) ); @@ -4265,7 +4265,7 @@ const void *sqlite3BtreeKeyFetch(BtCursor *pCur, int *pAmt){ } return p; } -const void *sqlite3BtreeDataFetch(BtCursor *pCur, int *pAmt){ +const void *sqlite3BtreeDataFetch(BtCursor *pCur, u32 *pAmt){ const void *p = 0; assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); assert( cursorHoldsMutex(pCur) ); @@ -4647,10 +4647,10 @@ int sqlite3BtreeMovetoUnpacked( } assert( pCur->apPage[0]->intKey || pIdxKey ); for(;;){ - int lwr, upr, idx; + int lwr, upr, idx, c; Pgno chldPg; MemPage *pPage = pCur->apPage[pCur->iPage]; - int c; + u8 *pCell; /* Pointer to current cell in pPage */ /* pPage->nCell must be greater than zero. If this is the root-page ** the cursor would have been INVALID above and this for(;;) loop @@ -4662,35 +4662,47 @@ int sqlite3BtreeMovetoUnpacked( assert( pPage->intKey==(pIdxKey==0) ); lwr = 0; upr = pPage->nCell-1; - if( biasRight ){ - pCur->aiIdx[pCur->iPage] = (u16)(idx = upr); - }else{ - pCur->aiIdx[pCur->iPage] = (u16)(idx = (upr+lwr)/2); - } - for(;;){ - u8 *pCell; /* Pointer to current cell in pPage */ - - assert( idx==pCur->aiIdx[pCur->iPage] ); - pCur->info.nSize = 0; - pCell = findCell(pPage, idx) + pPage->childPtrSize; - if( pPage->intKey ){ + assert( biasRight==0 || biasRight==1 ); + idx = upr>>(1-biasRight); /* idx = biasRight ? upr : (lwr+upr)/2; */ + pCur->aiIdx[pCur->iPage] = (u16)idx; + if( pPage->intKey ){ + for(;;){ i64 nCellKey; + pCell = findCell(pPage, idx) + pPage->childPtrSize; if( pPage->hasData ){ - u32 dummy; - pCell += getVarint32(pCell, dummy); + while( 0x80 <= *(pCell++) ){ + if( pCell>=pPage->aDataEnd ) return SQLITE_CORRUPT_BKPT; + } } getVarint(pCell, (u64*)&nCellKey); - if( nCellKey==intKey ){ - c = 0; - }else if( nCellKey<intKey ){ - c = -1; + if( nCellKey<intKey ){ + lwr = idx+1; + if( lwr>upr ){ c = -1; break; } + }else if( nCellKey>intKey ){ + upr = idx-1; + if( lwr>upr ){ c = +1; break; } }else{ - assert( nCellKey>intKey ); - c = +1; + assert( nCellKey==intKey ); + pCur->validNKey = 1; + pCur->info.nKey = nCellKey; + pCur->aiIdx[pCur->iPage] = (u16)idx; + if( !pPage->leaf ){ + lwr = idx; + goto moveto_next_layer; + }else{ + *pRes = 0; + rc = SQLITE_OK; + goto moveto_finish; + } } - pCur->validNKey = 1; - pCur->info.nKey = nCellKey; - }else{ + assert( lwr+upr>=0 ); + idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2; */ + } + }else{ + for(;;){ + int nCell; + pCell = findCell(pPage, idx) + pPage->childPtrSize; + /* The maximum supported page-size is 65536 bytes. This means that ** the maximum number of record bytes stored on an index B-Tree ** page is less than 16384 bytes and may be stored as a 2-byte @@ -4699,7 +4711,7 @@ int sqlite3BtreeMovetoUnpacked( ** stored entirely within the b-tree page by inspecting the first ** 2 bytes of the cell. */ - int nCell = pCell[0]; + nCell = pCell[0]; if( nCell<=pPage->max1bytePayload /* && (pCell+nCell)<pPage->aDataEnd */ ){ @@ -4730,6 +4742,7 @@ int sqlite3BtreeMovetoUnpacked( rc = SQLITE_NOMEM; goto moveto_finish; } + pCur->aiIdx[pCur->iPage] = (u16)idx; rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0); if( rc ){ sqlite3_free(pCellKey); @@ -4738,49 +4751,44 @@ int sqlite3BtreeMovetoUnpacked( c = sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey); sqlite3_free(pCellKey); } - } - if( c==0 ){ - if( pPage->intKey && !pPage->leaf ){ - lwr = idx; - break; + if( c<0 ){ + lwr = idx+1; + }else if( c>0 ){ + upr = idx-1; }else{ + assert( c==0 ); *pRes = 0; rc = SQLITE_OK; + pCur->aiIdx[pCur->iPage] = (u16)idx; goto moveto_finish; } + if( lwr>upr ) break; + assert( lwr+upr>=0 ); + idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2 */ } - if( c<0 ){ - lwr = idx+1; - }else{ - upr = idx-1; - } - if( lwr>upr ){ - break; - } - pCur->aiIdx[pCur->iPage] = (u16)(idx = (lwr+upr)/2); } assert( lwr==upr+1 || (pPage->intKey && !pPage->leaf) ); assert( pPage->isInit ); if( pPage->leaf ){ - chldPg = 0; - }else if( lwr>=pPage->nCell ){ - chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]); - }else{ - chldPg = get4byte(findCell(pPage, lwr)); - } - if( chldPg==0 ){ assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell ); + pCur->aiIdx[pCur->iPage] = (u16)idx; *pRes = c; rc = SQLITE_OK; goto moveto_finish; } +moveto_next_layer: + if( lwr>=pPage->nCell ){ + chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]); + }else{ + chldPg = get4byte(findCell(pPage, lwr)); + } pCur->aiIdx[pCur->iPage] = (u16)lwr; - pCur->info.nSize = 0; - pCur->validNKey = 0; rc = moveToChild(pCur, chldPg); - if( rc ) goto moveto_finish; + if( rc ) break; } moveto_finish: + pCur->info.nSize = 0; + pCur->validNKey = 0; return rc; } diff --git a/src/btree.h b/src/btree.h index 33b3ed1f0..3eb590695 100644 --- a/src/btree.h +++ b/src/btree.h @@ -178,8 +178,8 @@ int sqlite3BtreeEof(BtCursor*); int sqlite3BtreePrevious(BtCursor*, int *pRes); int sqlite3BtreeKeySize(BtCursor*, i64 *pSize); int sqlite3BtreeKey(BtCursor*, u32 offset, u32 amt, void*); -const void *sqlite3BtreeKeyFetch(BtCursor*, int *pAmt); -const void *sqlite3BtreeDataFetch(BtCursor*, int *pAmt); +const void *sqlite3BtreeKeyFetch(BtCursor*, u32 *pAmt); +const void *sqlite3BtreeDataFetch(BtCursor*, u32 *pAmt); int sqlite3BtreeDataSize(BtCursor*, u32 *pSize); int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*); void sqlite3BtreeSetCachedRowid(BtCursor*, sqlite3_int64); diff --git a/src/build.c b/src/build.c index d1615a128..27f026c8d 100644 --- a/src/build.c +++ b/src/build.c @@ -150,7 +150,7 @@ void sqlite3FinishCoding(Parse *pParse){ */ if( pParse->cookieGoto>0 ){ yDbMask mask; - int iDb; + int iDb, i, addr; sqlite3VdbeJumpHere(v, pParse->cookieGoto-1); for(iDb=0, mask=1; iDb<db->nDb; mask<<=1, iDb++){ if( (mask & pParse->cookieMask)==0 ) continue; @@ -164,14 +164,11 @@ void sqlite3FinishCoding(Parse *pParse){ } } #ifndef SQLITE_OMIT_VIRTUALTABLE - { - int i; - for(i=0; i<pParse->nVtabLock; i++){ - char *vtab = (char *)sqlite3GetVTable(db, pParse->apVtabLock[i]); - sqlite3VdbeAddOp4(v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB); - } - pParse->nVtabLock = 0; + for(i=0; i<pParse->nVtabLock; i++){ + char *vtab = (char *)sqlite3GetVTable(db, pParse->apVtabLock[i]); + sqlite3VdbeAddOp4(v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB); } + pParse->nVtabLock = 0; #endif /* Once all the cookies have been verified and transactions opened, @@ -184,8 +181,18 @@ void sqlite3FinishCoding(Parse *pParse){ */ sqlite3AutoincrementBegin(pParse); + /* Code constant expressions that where factored out of inner loops */ + addr = pParse->cookieGoto; + if( pParse->pConstExpr ){ + ExprList *pEL = pParse->pConstExpr; + pParse->cookieGoto = 0; + for(i=0; i<pEL->nExpr; i++){ + sqlite3ExprCode(pParse, pEL->a[i].pExpr, pEL->a[i].u.iConstExprReg); + } + } + /* Finally, jump back to the beginning of the executable code. */ - sqlite3VdbeAddOp2(v, OP_Goto, 0, pParse->cookieGoto); + sqlite3VdbeAddOp2(v, OP_Goto, 0, addr); } } diff --git a/src/delete.c b/src/delete.c index 9ebb56eca..e43072235 100644 --- a/src/delete.c +++ b/src/delete.c @@ -227,20 +227,34 @@ void sqlite3DeleteFrom( Vdbe *v; /* The virtual database engine */ Table *pTab; /* The table from which records will be deleted */ const char *zDb; /* Name of database holding pTab */ - int end, addr = 0; /* A couple addresses of generated code */ int i; /* Loop counter */ WhereInfo *pWInfo; /* Information about the WHERE clause */ Index *pIdx; /* For looping over indices of the table */ int iTabCur; /* Cursor number for the table */ int iDataCur; /* VDBE cursor for the canonical data source */ int iIdxCur; /* Cursor number of the first index */ + int nIdx; /* Number of indices */ sqlite3 *db; /* Main database structure */ AuthContext sContext; /* Authorization context */ NameContext sNC; /* Name context to resolve expressions in */ int iDb; /* Database number */ int memCnt = -1; /* Memory cell used for change counting */ int rcauth; /* Value returned by authorization callback */ - + int okOnePass; /* True for one-pass algorithm without the FIFO */ + int aiCurOnePass[2]; /* The write cursors opened by WHERE_ONEPASS */ + u8 *aToOpen = 0; /* Open cursor iTabCur+j if aToOpen[j] is true */ + Index *pPk; /* The PRIMARY KEY index on the table */ + int iPk = 0; /* First of nPk registers holding PRIMARY KEY value */ + i16 nPk = 1; /* Number of columns in the PRIMARY KEY */ + int iKey; /* Memory cell holding key of row to be deleted */ + i16 nKey; /* Number of memory cells in the row key */ + int iEphCur = 0; /* Ephemeral table holding all primary key values */ + int iRowSet = 0; /* Register for rowset of rows to delete */ + int addrBypass = 0; /* Address of jump over the delete logic */ + int addrLoop = 0; /* Top of the delete loop */ + int addrDelete = 0; /* Jump directly to the delete logic */ + int addrEphOpen = 0; /* Instruction to open the Ephermeral table */ + #ifndef SQLITE_OMIT_TRIGGER int isView; /* True if attempting to delete from a view */ Trigger *pTrigger; /* List of table triggers, if required */ @@ -295,11 +309,11 @@ void sqlite3DeleteFrom( } assert(!isView || pTrigger); - /* Assign cursor number to the table and all its indices. + /* Assign cursor numbers to the table and all its indices. */ assert( pTabList->nSrc==1 ); iTabCur = pTabList->a[0].iCursor = pParse->nTab++; - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ pParse->nTab++; } @@ -372,106 +386,129 @@ void sqlite3DeleteFrom( } }else #endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */ - if( !HasRowid(pTab) ){ - /* There is a WHERE clause on a WITHOUT ROWID table. + { + if( HasRowid(pTab) ){ + /* For a rowid table, initialize the RowSet to an empty set */ + pPk = 0; + nPk = 1; + iRowSet = ++pParse->nMem; + sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet); + }else{ + /* For a WITHOUT ROWID table, create an ephermeral table used to + ** hold all primary keys for rows to be deleted. */ + pPk = sqlite3PrimaryKeyIndex(pTab); + assert( pPk!=0 ); + nPk = pPk->nKeyCol; + iPk = pParse->nMem+1; + pParse->nMem += nPk; + iEphCur = pParse->nTab++; + addrEphOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEphCur, nPk); + sqlite3VdbeSetP4KeyInfo(pParse, pPk); + } + + /* Construct a query to find the rowid or primary key for every row + ** to be deleted, based on the WHERE clause. */ - Index *pPk; /* The PRIMARY KEY index on the table */ - int iPk; /* First of nPk memory cells holding PRIMARY KEY value */ - int iEph; /* Ephemeral table holding all primary key values */ - int iKey; /* Key value inserting into iEph */ - i16 nPk; /* Number of components of the PRIMARY KEY */ - - pPk = sqlite3PrimaryKeyIndex(pTab); - assert( pPk!=0 ); - nPk = pPk->nKeyCol; - iPk = pParse->nMem+1; - pParse->nMem += nPk; - iKey = ++pParse->nMem; - iEph = pParse->nTab++; - - sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk); - sqlite3VdbeSetP4KeyInfo(pParse, pPk); - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, 0, 0); + pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, + WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK, + iTabCur+1); if( pWInfo==0 ) goto delete_from_cleanup; - for(i=0; i<nPk; i++){ - sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur, pPk->aiColumn[i],iPk+i); - } - sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, iKey, - sqlite3IndexAffinityStr(v, pPk), P4_TRANSIENT); - sqlite3VdbeAddOp2(v, OP_IdxInsert, iEph, iKey); + okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); + + /* Keep track of the number of rows to be deleted */ if( db->flags & SQLITE_CountRows ){ sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1); } - sqlite3WhereEnd(pWInfo); - - /* Open cursors for all indices of the table. - */ - sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, - iTabCur, &iDataCur, &iIdxCur); - - /* Loop over the primary keys to be deleted. */ - addr = sqlite3VdbeAddOp1(v, OP_Rewind, iEph); - sqlite3VdbeAddOp2(v, OP_RowKey, iEph, iPk); - - /* Delete the row */ - sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur, - iPk, 0, 1, OE_Default, 0); - - /* End of the delete loop */ - sqlite3VdbeAddOp2(v, OP_Next, iEph, addr+1); - sqlite3VdbeJumpHere(v, addr); - - /* Close the cursors open on the table and its indexes. */ - assert( iDataCur>=iIdxCur ); - for(i=0, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ - sqlite3VdbeAddOp1(v, OP_Close, iIdxCur+i); + + /* Extract the rowid or primary key for the current row */ + if( pPk ){ + for(i=0; i<nPk; i++){ + sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur, + pPk->aiColumn[i], iPk+i); + } + iKey = iPk; + }else{ + iKey = pParse->nMem + 1; + iKey = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iTabCur, iKey, 0); + if( iKey>pParse->nMem ) pParse->nMem = iKey; } - }else{ - /* There is a WHERE clause on a rowid table. Run a loop that extracts - ** all rowids to be deleted into a RowSet. - */ - int iRowSet = ++pParse->nMem; /* Register for rowset of rows to delete */ - int iRowid = ++pParse->nMem; /* Used for storing rowid values. */ - int regRowid; /* Actual register containing rowids */ - - /* Collect rowids of every row to be deleted. - */ - sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet); - pWInfo = sqlite3WhereBegin( - pParse, pTabList, pWhere, 0, 0, WHERE_DUPLICATES_OK, 0 - ); - if( pWInfo==0 ) goto delete_from_cleanup; - regRowid = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iTabCur, iRowid, 0); - sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, regRowid); - if( db->flags & SQLITE_CountRows ){ - sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1); + + if( okOnePass ){ + /* For ONEPASS, no need to store the rowid/primary-key. There is only + ** one, so just keep it in its register(s) and fall through to the + ** delete code. + */ + nKey = nPk; /* OP_Found will use an unpacked key */ + aToOpen = sqlite3DbMallocRaw(db, nIdx+2); + if( aToOpen==0 ){ + sqlite3WhereEnd(pWInfo); + goto delete_from_cleanup; + } + memset(aToOpen, 1, nIdx+1); + aToOpen[nIdx+1] = 0; + if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iTabCur] = 0; + if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iTabCur] = 0; + if( addrEphOpen ) sqlite3VdbeChangeToNoop(v, addrEphOpen); + addrDelete = sqlite3VdbeAddOp0(v, OP_Goto); /* Jump to DELETE logic */ + }else if( pPk ){ + /* Construct a composite key for the row to be deleted and remember it */ + iKey = ++pParse->nMem; + nKey = 0; /* Zero tells OP_Found to use a composite key */ + sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, iKey, + sqlite3IndexAffinityStr(v, pPk), P4_TRANSIENT); + sqlite3VdbeAddOp2(v, OP_IdxInsert, iEphCur, iKey); + }else{ + /* Get the rowid of the row to be deleted and remember it in the RowSet */ + nKey = 1; /* OP_Seek always uses a single rowid */ + sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, iKey); } + + /* End of the WHERE loop */ sqlite3WhereEnd(pWInfo); - - /* Delete every item whose key was written to the list during the - ** database scan. We have to delete items after the scan is complete - ** because deleting an item can change the scan order. */ - end = sqlite3VdbeMakeLabel(v); - + if( okOnePass ){ + /* Bypass the delete logic below if the WHERE loop found zero rows */ + addrBypass = sqlite3VdbeMakeLabel(v); + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrBypass); + sqlite3VdbeJumpHere(v, addrDelete); + } + /* Unless this is a view, open cursors for the table we are ** deleting from and all its indices. If this is a view, then the ** only effect this statement has is to fire the INSTEAD OF - ** triggers. */ + ** triggers. + */ if( !isView ){ - sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, iTabCur, + sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, iTabCur, aToOpen, &iDataCur, &iIdxCur); - assert( iDataCur==iTabCur ); - assert( iIdxCur==iDataCur+1 ); + assert( pPk || iDataCur==iTabCur ); + assert( pPk || iIdxCur==iDataCur+1 ); } - - addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, end, iRowid); - + + /* Set up a loop over the rowids/primary-keys that were found in the + ** where-clause loop above. + */ + if( okOnePass ){ + /* Just one row. Hence the top-of-loop is a no-op */ + assert( nKey==nPk ); /* OP_Found will use an unpacked key */ + if( aToOpen[iDataCur-iTabCur] ){ + assert( pPk!=0 ); + sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey); + } + }else if( pPk ){ + addrLoop = sqlite3VdbeAddOp1(v, OP_Rewind, iEphCur); + sqlite3VdbeAddOp2(v, OP_RowKey, iEphCur, iKey); + assert( nKey==0 ); /* OP_Found will use a composite key */ + }else{ + addrLoop = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, 0, iKey); + assert( nKey==1 ); + } + /* Delete the row */ #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pTab) ){ const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); sqlite3VtabMakeWritable(pParse, pTab); - sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iRowid, (char*)pVTab, P4_VTAB); + sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iKey, pVTab, P4_VTAB); sqlite3VdbeChangeP5(v, OE_Abort); sqlite3MayAbort(pParse); }else @@ -479,21 +516,28 @@ void sqlite3DeleteFrom( { int count = (pParse->nested==0); /* True to count changes */ sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur, - iRowid, 1, count, OE_Default, 0); + iKey, nKey, count, OE_Default, okOnePass); } - - /* End of the delete loop */ - sqlite3VdbeAddOp2(v, OP_Goto, 0, addr); - sqlite3VdbeResolveLabel(v, end); - + + /* End of the loop over all rowids/primary-keys. */ + if( okOnePass ){ + sqlite3VdbeResolveLabel(v, addrBypass); + }else if( pPk ){ + sqlite3VdbeAddOp2(v, OP_Next, iEphCur, addrLoop+1); + sqlite3VdbeJumpHere(v, addrLoop); + }else{ + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrLoop); + sqlite3VdbeJumpHere(v, addrLoop); + } + /* Close the cursors open on the table and its indexes. */ if( !isView && !IsVirtual(pTab) ){ - sqlite3VdbeAddOp1(v, OP_Close, iDataCur); + if( !pPk ) sqlite3VdbeAddOp1(v, OP_Close, iDataCur); for(i=0, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ sqlite3VdbeAddOp1(v, OP_Close, iIdxCur + i); } } - } + } /* End non-truncate path */ /* Update the sqlite_sequence table by storing the content of the ** maximum rowid counter values recorded while inserting into @@ -517,6 +561,7 @@ delete_from_cleanup: sqlite3AuthContextPop(&sContext); sqlite3SrcListDelete(db, pTabList); sqlite3ExprDelete(db, pWhere); + sqlite3DbFree(db, aToOpen); return; } /* Make sure "isView" and other macros defined above are undefined. Otherwise @@ -583,6 +628,7 @@ void sqlite3GenerateRowDelete( if( sqlite3FkRequired(pParse, pTab, 0, 0) || pTrigger ){ u32 mask; /* Mask of OLD.* columns in use */ int iCol; /* Iterator used while populating OLD.* */ + int addrStart; /* Start of BEFORE trigger programs */ /* TODO: Could use temporary registers here. Also could attempt to ** avoid copying the contents of the rowid register. */ @@ -603,15 +649,19 @@ void sqlite3GenerateRowDelete( } /* Invoke BEFORE DELETE trigger programs. */ + addrStart = sqlite3VdbeCurrentAddr(v); sqlite3CodeRowTrigger(pParse, pTrigger, TK_DELETE, 0, TRIGGER_BEFORE, pTab, iOld, onconf, iLabel ); - /* Seek the cursor to the row to be deleted again. It may be that - ** the BEFORE triggers coded above have already removed the row - ** being deleted. Do not attempt to delete the row a second time, and - ** do not fire AFTER triggers. */ - sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk); + /* If any BEFORE triggers were coded, then seek the cursor to the + ** row to be deleted again. It may be that the BEFORE triggers moved + ** the cursor or of already deleted the row that the cursor was + ** pointing to. + */ + if( addrStart<sqlite3VdbeCurrentAddr(v) ){ + sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk); + } /* Do FK processing. This call checks that any FK constraints that ** refer to this table (i.e. constraints attached to other tables) diff --git a/src/expr.c b/src/expr.c index 7e72b44f9..6c8a8cce7 100644 --- a/src/expr.c +++ b/src/expr.c @@ -930,8 +930,7 @@ ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){ pItem->sortOrder = pOldItem->sortOrder; pItem->done = 0; pItem->bSpanIsTab = pOldItem->bSpanIsTab; - pItem->iOrderByCol = pOldItem->iOrderByCol; - pItem->iAlias = pOldItem->iAlias; + pItem->u = pOldItem->u; } return pNew; } @@ -1192,9 +1191,12 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ switch( pExpr->op ){ /* Consider functions to be constant if all their arguments are constant - ** and pWalker->u.i==2 */ + ** and either pWalker->u.i==2 or the function as the SQLITE_FUNC_CONST + ** flag. */ case TK_FUNCTION: - if( pWalker->u.i==2 ) return 0; + if( pWalker->u.i==2 || ExprHasProperty(pExpr,EP_Constant) ){ + return WRC_Continue; + } /* Fall through */ case TK_ID: case TK_COLUMN: @@ -2356,6 +2358,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ int regFree2 = 0; /* If non-zero free this temporary register */ int r1, r2, r3, r4; /* Various register numbers */ sqlite3 *db = pParse->db; /* The database connection */ + Expr tempX; /* Temporary expression node */ assert( target>0 && target<=pParse->nMem ); if( v==0 ){ @@ -2575,8 +2578,10 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ codeReal(v, pLeft->u.zToken, 1, target); #endif }else{ - regFree1 = r1 = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp2(v, OP_Integer, 0, r1); + tempX.op = TK_INTEGER; + tempX.flags = EP_IntValue|EP_TokenOnly; + tempX.u.iValue = 0; + r1 = sqlite3ExprCodeTemp(pParse, &tempX, ®Free1); r2 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free2); sqlite3VdbeAddOp3(v, OP_Subtract, r2, r1, target); testcase( regFree2==0 ); @@ -2621,7 +2626,6 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ } break; } - case TK_CONST_FUNC: case TK_FUNCTION: { ExprList *pFarg; /* List of function arguments */ int nFarg; /* Number of function arguments */ @@ -2634,8 +2638,6 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ CollSeq *pColl = 0; /* A collating sequence */ assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); - testcase( op==TK_CONST_FUNC ); - testcase( op==TK_FUNCTION ); if( ExprHasProperty(pExpr, EP_TokenOnly) ){ pFarg = 0; }else{ @@ -2679,8 +2681,21 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ break; } + for(i=0; i<nFarg; i++){ + if( i<32 && sqlite3ExprIsConstant(pFarg->a[i].pExpr) ){ + constMask |= (1<<i); + } + if( (pDef->funcFlags & SQLITE_FUNC_NEEDCOLL)!=0 && !pColl ){ + pColl = sqlite3ExprCollSeq(pParse, pFarg->a[i].pExpr); + } + } if( pFarg ){ - r1 = sqlite3GetTempRange(pParse, nFarg); + if( constMask ){ + r1 = pParse->nMem+1; + pParse->nMem += nFarg; + }else{ + r1 = sqlite3GetTempRange(pParse, nFarg); + } /* For length() and typeof() functions with a column argument, ** set the P5 parameter to the OP_Column opcode to OPFLAG_LENGTHARG @@ -2695,14 +2710,15 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ if( exprOp==TK_COLUMN || exprOp==TK_AGG_COLUMN ){ assert( SQLITE_FUNC_LENGTH==OPFLAG_LENGTHARG ); assert( SQLITE_FUNC_TYPEOF==OPFLAG_TYPEOFARG ); - testcase( (pDef->funcFlags&~SQLITE_FUNC_ENCMASK) - ==SQLITE_FUNC_LENGTH ); - pFarg->a[0].pExpr->op2 = pDef->funcFlags&~SQLITE_FUNC_ENCMASK; + testcase( pDef->funcFlags & OPFLAG_LENGTHARG ); + pFarg->a[0].pExpr->op2 = + pDef->funcFlags & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG); } } sqlite3ExprCachePush(pParse); /* Ticket 2ea2425d34be */ - sqlite3ExprCodeExprList(pParse, pFarg, r1, 1); + sqlite3ExprCodeExprList(pParse, pFarg, r1, + SQLITE_ECEL_DUP|SQLITE_ECEL_FACTOR); sqlite3ExprCachePop(pParse, 1); /* Ticket 2ea2425d34be */ }else{ r1 = 0; @@ -2726,14 +2742,6 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ pDef = sqlite3VtabOverloadFunction(db, pDef, nFarg, pFarg->a[0].pExpr); } #endif - for(i=0; i<nFarg; i++){ - if( i<32 && sqlite3ExprIsConstant(pFarg->a[i].pExpr) ){ - constMask |= (1<<i); - } - if( (pDef->funcFlags & SQLITE_FUNC_NEEDCOLL)!=0 && !pColl ){ - pColl = sqlite3ExprCollSeq(pParse, pFarg->a[i].pExpr); - } - } if( pDef->funcFlags & SQLITE_FUNC_NEEDCOLL ){ if( !pColl ) pColl = db->pDfltColl; sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ); @@ -2741,7 +2749,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ sqlite3VdbeAddOp4(v, OP_Function, constMask, r1, target, (char*)pDef, P4_FUNCDEF); sqlite3VdbeChangeP5(v, (u8)nFarg); - if( nFarg ){ + if( nFarg && constMask==0 ){ sqlite3ReleaseTempRange(pParse, r1, nFarg); } break; @@ -2892,7 +2900,6 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ ExprList *pEList; /* List of WHEN terms */ struct ExprList_item *aListelem; /* Array of WHEN terms */ Expr opCompare; /* The X==Ei expression */ - Expr cacheX; /* Cached expression X */ Expr *pX; /* The X expression */ Expr *pTest = 0; /* X==Ei (form A) or just Ei (form B) */ VVA_ONLY( int iCacheLevel = pParse->iCacheLevel; ) @@ -2904,13 +2911,12 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ nExpr = pEList->nExpr; endLabel = sqlite3VdbeMakeLabel(v); if( (pX = pExpr->pLeft)!=0 ){ - cacheX = *pX; + tempX = *pX; testcase( pX->op==TK_COLUMN ); - testcase( pX->op==TK_REGISTER ); - exprToRegister(&cacheX, sqlite3ExprCodeTemp(pParse, pX, ®Free1)); + exprToRegister(&tempX, sqlite3ExprCodeTemp(pParse, pX, ®Free1)); testcase( regFree1==0 ); opCompare.op = TK_EQ; - opCompare.pLeft = &cacheX; + opCompare.pLeft = &tempX; pTest = &opCompare; /* Ticket b351d95f9cd5ef17e9d9dbae18f5ca8611190001: ** The value in regFree1 might get SCopy-ed into the file result. @@ -2930,7 +2936,6 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ testcase( pTest->op==TK_COLUMN ); sqlite3ExprIfFalse(pParse, pTest, nextCase, SQLITE_JUMPIFNULL); testcase( aListelem[i+1].pExpr->op==TK_COLUMN ); - testcase( aListelem[i+1].pExpr->op==TK_REGISTER ); sqlite3ExprCode(pParse, aListelem[i+1].pExpr, target); sqlite3VdbeAddOp2(v, OP_Goto, 0, endLabel); sqlite3ExprCachePop(pParse, 1); @@ -2982,6 +2987,28 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ } /* +** Factor out the code of the given expression to initialization time. +*/ +void sqlite3ExprCodeAtInit( + Parse *pParse, /* Parsing context */ + Expr *pExpr, /* The expression to code when the VDBE initializes */ + int regDest, /* Store the value in this register */ + u8 reusable /* True if this expression is reusable */ +){ + ExprList *p; + assert( ConstFactorOk(pParse) ); + p = pParse->pConstExpr; + pExpr = sqlite3ExprDup(pParse->db, pExpr, 0); + p = sqlite3ExprListAppend(pParse, p, pExpr); + if( p ){ + struct ExprList_item *pItem = &p->a[p->nExpr-1]; + pItem->u.iConstExprReg = regDest; + pItem->reusable = reusable; + } + pParse->pConstExpr = p; +} + +/* ** Generate code to evaluate an expression and store the results ** into a register. Return the register number where the results ** are stored. @@ -2989,15 +3016,40 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ ** If the register is a temporary register that can be deallocated, ** then write its number into *pReg. If the result register is not ** a temporary, then set *pReg to zero. +** +** If pExpr is a constant, then this routine might generate this +** code to fill the register in the initialization section of the +** VDBE program, in order to factor it out of the evaluation loop. */ int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){ - int r1 = sqlite3GetTempReg(pParse); - int r2 = sqlite3ExprCodeTarget(pParse, pExpr, r1); - if( r2==r1 ){ - *pReg = r1; + int r2; + pExpr = sqlite3ExprSkipCollate(pExpr); + if( ConstFactorOk(pParse) + && pExpr->op!=TK_REGISTER + && sqlite3ExprIsConstantNotJoin(pExpr) + ){ + ExprList *p = pParse->pConstExpr; + int i; + *pReg = 0; + if( p ){ + struct ExprList_item *pItem; + for(pItem=p->a, i=p->nExpr; i>0; pItem++, i--){ + if( pItem->reusable && sqlite3ExprCompare(pItem->pExpr,pExpr,-1)==0 ){ + return pItem->u.iConstExprReg; + } + } + } + r2 = ++pParse->nMem; + sqlite3ExprCodeAtInit(pParse, pExpr, r2, 1); }else{ - sqlite3ReleaseTempReg(pParse, r1); - *pReg = 0; + int r1 = sqlite3GetTempReg(pParse); + r2 = sqlite3ExprCodeTarget(pParse, pExpr, r1); + if( r2==r1 ){ + *pReg = r1; + }else{ + sqlite3ReleaseTempReg(pParse, r1); + *pReg = 0; + } } return r2; } @@ -3040,12 +3092,13 @@ int sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int target){ int inReg; inReg = sqlite3ExprCode(pParse, pExpr, target); assert( target>0 ); - /* This routine is called for terms to INSERT or UPDATE. And the only - ** other place where expressions can be converted into TK_REGISTER is - ** in WHERE clause processing. So as currently implemented, there is - ** no way for a TK_REGISTER to exist here. But it seems prudent to - ** keep the ALWAYS() in case the conditions above change with future - ** modifications or enhancements. */ + /* The only place, other than this routine, where expressions can be + ** converted to TK_REGISTER is internal subexpressions in BETWEEN and + ** CASE operators. Neither ever calls this routine. And this routine + ** is never called twice on the same expression. Hence it is impossible + ** for the input to this routine to already be a register. Nevertheless, + ** it seems prudent to keep the ALWAYS() in case the conditions above + ** change with future modifications or enhancements. */ if( ALWAYS(pExpr->op!=TK_REGISTER) ){ int iMem; iMem = ++pParse->nMem; @@ -3177,7 +3230,6 @@ void sqlite3ExplainExpr(Vdbe *pOut, Expr *pExpr){ } case TK_AGG_FUNCTION: - case TK_CONST_FUNC: case TK_FUNCTION: { ExprList *pFarg; /* List of function arguments */ if( ExprHasProperty(pExpr, EP_TokenOnly) ){ @@ -3329,163 +3381,40 @@ void sqlite3ExplainExprList(Vdbe *pOut, ExprList *pList){ #endif /* SQLITE_DEBUG */ /* -** Return TRUE if pExpr is an constant expression that is appropriate -** for factoring out of a loop. Appropriate expressions are: -** -** * Any expression that evaluates to two or more opcodes. -** -** * Any OP_Integer, OP_Real, OP_String, OP_Blob, OP_Null, -** or OP_Variable that does not need to be placed in a -** specific register. -** -** There is no point in factoring out single-instruction constant -** expressions that need to be placed in a particular register. -** We could factor them out, but then we would end up adding an -** OP_SCopy instruction to move the value into the correct register -** later. We might as well just use the original instruction and -** avoid the OP_SCopy. -*/ -static int isAppropriateForFactoring(Expr *p){ - if( !sqlite3ExprIsConstantNotJoin(p) ){ - return 0; /* Only constant expressions are appropriate for factoring */ - } - if( (p->flags & EP_FixedDest)==0 ){ - return 1; /* Any constant without a fixed destination is appropriate */ - } - while( p->op==TK_UPLUS ) p = p->pLeft; - switch( p->op ){ -#ifndef SQLITE_OMIT_BLOB_LITERAL - case TK_BLOB: -#endif - case TK_VARIABLE: - case TK_INTEGER: - case TK_FLOAT: - case TK_NULL: - case TK_STRING: { - testcase( p->op==TK_BLOB ); - testcase( p->op==TK_VARIABLE ); - testcase( p->op==TK_INTEGER ); - testcase( p->op==TK_FLOAT ); - testcase( p->op==TK_NULL ); - testcase( p->op==TK_STRING ); - /* Single-instruction constants with a fixed destination are - ** better done in-line. If we factor them, they will just end - ** up generating an OP_SCopy to move the value to the destination - ** register. */ - return 0; - } - case TK_UMINUS: { - if( p->pLeft->op==TK_FLOAT || p->pLeft->op==TK_INTEGER ){ - return 0; - } - break; - } - default: { - break; - } - } - return 1; -} - -/* -** If pExpr is a constant expression that is appropriate for -** factoring out of a loop, then evaluate the expression -** into a register and convert the expression into a TK_REGISTER -** expression. -*/ -static int evalConstExpr(Walker *pWalker, Expr *pExpr){ - Parse *pParse = pWalker->pParse; - switch( pExpr->op ){ - case TK_IN: - case TK_REGISTER: { - return WRC_Prune; - } - case TK_COLLATE: { - return WRC_Continue; - } - case TK_FUNCTION: - case TK_AGG_FUNCTION: - case TK_CONST_FUNC: { - /* The arguments to a function have a fixed destination. - ** Mark them this way to avoid generated unneeded OP_SCopy - ** instructions. - */ - ExprList *pList = pExpr->x.pList; - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); - if( pList ){ - int i = pList->nExpr; - struct ExprList_item *pItem = pList->a; - for(; i>0; i--, pItem++){ - if( ALWAYS(pItem->pExpr) ) pItem->pExpr->flags |= EP_FixedDest; - } - } - break; - } - } - if( isAppropriateForFactoring(pExpr) ){ - int r1 = ++pParse->nMem; - int r2 = sqlite3ExprCodeTarget(pParse, pExpr, r1); - /* If r2!=r1, it means that register r1 is never used. That is harmless - ** but suboptimal, so we want to know about the situation to fix it. - ** Hence the following assert: */ - assert( r2==r1 ); - exprToRegister(pExpr, r2); - return WRC_Prune; - } - return WRC_Continue; -} - -/* -** Preevaluate constant subexpressions within pExpr and store the -** results in registers. Modify pExpr so that the constant subexpresions -** are TK_REGISTER opcodes that refer to the precomputed values. -** -** This routine is a no-op if the jump to the cookie-check code has -** already occur. Since the cookie-check jump is generated prior to -** any other serious processing, this check ensures that there is no -** way to accidently bypass the constant initializations. -** -** This routine is also a no-op if the SQLITE_FactorOutConst optimization -** is disabled via the sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS) -** interface. This allows test logic to verify that the same answer is -** obtained for queries regardless of whether or not constants are -** precomputed into registers or if they are inserted in-line. -*/ -void sqlite3ExprCodeConstants(Parse *pParse, Expr *pExpr){ - Walker w; - if( pParse->cookieGoto ) return; - if( OptimizationDisabled(pParse->db, SQLITE_FactorOutConst) ) return; - memset(&w, 0, sizeof(w)); - w.xExprCallback = evalConstExpr; - w.pParse = pParse; - sqlite3WalkExpr(&w, pExpr); -} - - -/* ** Generate code that pushes the value of every element of the given ** expression list into a sequence of registers beginning at target. ** ** Return the number of elements evaluated. +** +** The SQLITE_ECEL_DUP flag prevents the arguments from being +** filled using OP_SCopy. OP_Copy must be used instead. +** +** The SQLITE_ECEL_FACTOR argument allows constant arguments to be +** factored out into initialization code. */ int sqlite3ExprCodeExprList( Parse *pParse, /* Parsing context */ ExprList *pList, /* The expression list to be coded */ int target, /* Where to write results */ - int doHardCopy /* Make a hard copy of every element */ + u8 flags /* SQLITE_ECEL_* flags */ ){ struct ExprList_item *pItem; int i, n; + u8 copyOp = (flags & SQLITE_ECEL_DUP) ? OP_Copy : OP_SCopy; assert( pList!=0 ); assert( target>0 ); assert( pParse->pVdbe!=0 ); /* Never gets this far otherwise */ n = pList->nExpr; + if( !ConstFactorOk(pParse) ) flags &= ~SQLITE_ECEL_FACTOR; for(pItem=pList->a, i=0; i<n; i++, pItem++){ Expr *pExpr = pItem->pExpr; - int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i); - if( inReg!=target+i ){ - sqlite3VdbeAddOp2(pParse->pVdbe, doHardCopy ? OP_Copy : OP_SCopy, - inReg, target+i); + if( (flags & SQLITE_ECEL_FACTOR)!=0 && sqlite3ExprIsConstant(pExpr) ){ + sqlite3ExprCodeAtInit(pParse, pExpr, target+i, 0); + }else{ + int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i); + if( inReg!=target+i ){ + sqlite3VdbeAddOp2(pParse->pVdbe, copyOp, inReg, target+i); + } } } return n; @@ -3839,16 +3768,18 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ ** an incorrect 0 or 1 could lead to a malfunction. */ int sqlite3ExprCompare(Expr *pA, Expr *pB, int iTab){ - if( pA==0||pB==0 ){ + u32 combinedFlags; + if( pA==0 || pB==0 ){ return pB==pA ? 0 : 2; } - assert( !ExprHasProperty(pA, EP_TokenOnly|EP_Reduced) ); - assert( !ExprHasProperty(pB, EP_TokenOnly|EP_Reduced) ); - if( ExprHasProperty(pA, EP_xIsSelect) || ExprHasProperty(pB, EP_xIsSelect) ){ + combinedFlags = pA->flags | pB->flags; + if( combinedFlags & EP_IntValue ){ + if( (pA->flags&pB->flags&EP_IntValue)!=0 && pA->u.iValue==pB->u.iValue ){ + return 0; + } return 2; } - if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 2; - if( pA->op!=pB->op && (pA->op!=TK_REGISTER || pA->op2!=pB->op) ){ + if( pA->op!=pB->op ){ if( pA->op==TK_COLLATE && sqlite3ExprCompare(pA->pLeft, pB, iTab)<2 ){ return 1; } @@ -3857,23 +3788,23 @@ int sqlite3ExprCompare(Expr *pA, Expr *pB, int iTab){ } return 2; } - if( sqlite3ExprCompare(pA->pLeft, pB->pLeft, iTab) ) return 2; - if( sqlite3ExprCompare(pA->pRight, pB->pRight, iTab) ) return 2; - if( sqlite3ExprListCompare(pA->x.pList, pB->x.pList, iTab) ) return 2; - if( pA->iColumn!=pB->iColumn ) return 2; - if( pA->iTable!=pB->iTable - && pA->op!=TK_REGISTER - && (pA->iTable!=iTab || NEVER(pB->iTable>=0)) ) return 2; - if( ExprHasProperty(pA, EP_IntValue) ){ - if( !ExprHasProperty(pB, EP_IntValue) || pA->u.iValue!=pB->u.iValue ){ - return 2; - } - }else if( pA->op!=TK_COLUMN && ALWAYS(pA->op!=TK_AGG_COLUMN) && pA->u.zToken){ - if( ExprHasProperty(pB, EP_IntValue) || NEVER(pB->u.zToken==0) ) return 2; + if( pA->op!=TK_COLUMN && ALWAYS(pA->op!=TK_AGG_COLUMN) && pA->u.zToken ){ if( strcmp(pA->u.zToken,pB->u.zToken)!=0 ){ return pA->op==TK_COLLATE ? 1 : 2; } } + if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 2; + if( ALWAYS((combinedFlags & EP_TokenOnly)==0) ){ + if( combinedFlags & EP_xIsSelect ) return 2; + if( sqlite3ExprCompare(pA->pLeft, pB->pLeft, iTab) ) return 2; + if( sqlite3ExprCompare(pA->pRight, pB->pRight, iTab) ) return 2; + if( sqlite3ExprListCompare(pA->x.pList, pB->x.pList, iTab) ) return 2; + if( ALWAYS((combinedFlags & EP_Reduced)==0) ){ + if( pA->iColumn!=pB->iColumn ) return 2; + if( pA->iTable!=pB->iTable + && (pA->iTable!=iTab || NEVER(pB->iTable>=0)) ) return 2; + } + } return 0; } diff --git a/src/fkey.c b/src/fkey.c index 1cb6cb0d4..46c90a098 100644 --- a/src/fkey.c +++ b/src/fkey.c @@ -548,6 +548,7 @@ static void fkScanChildren( assert( pIdx==0 || pIdx->pTable==pTab ); assert( pIdx==0 || pIdx->nKeyCol==pFKey->nCol ); assert( pIdx!=0 || pFKey->nCol==1 ); + assert( pIdx!=0 || HasRowid(pTab) ); if( nIncr<0 ){ iFkIfZero = sqlite3VdbeAddOp2(v, OP_FkIfZero, pFKey->isDeferred, 0); @@ -600,6 +601,7 @@ static void fkScanChildren( }else{ Expr *pEq, *pAll = 0; Index *pPk = sqlite3PrimaryKeyIndex(pTab); + assert( pIdx!=0 ); for(i=0; i<pPk->nKeyCol; i++){ i16 iCol = pIdx->aiColumn[i]; pLeft = exprTableRegister(pParse, pTab, regData, iCol); diff --git a/src/func.c b/src/func.c index c9962f6d9..46c606ac0 100644 --- a/src/func.c +++ b/src/func.c @@ -1664,8 +1664,8 @@ void sqlite3RegisterGlobalFunctions(void){ FUNCTION2(ifnull, 2, 0, 0, noopFunc, SQLITE_FUNC_COALESCE), FUNCTION2(unlikely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY), FUNCTION2(likelihood, 2, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY), - FUNCTION(random, 0, 0, 0, randomFunc ), - FUNCTION(randomblob, 1, 0, 0, randomBlob ), + VFUNCTION(random, 0, 0, 0, randomFunc ), + VFUNCTION(randomblob, 1, 0, 0, randomBlob ), FUNCTION(nullif, 2, 0, 1, nullifFunc ), FUNCTION(sqlite_version, 0, 0, 0, versionFunc ), FUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ), @@ -1675,9 +1675,9 @@ void sqlite3RegisterGlobalFunctions(void){ FUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ), #endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ FUNCTION(quote, 1, 0, 0, quoteFunc ), - FUNCTION(last_insert_rowid, 0, 0, 0, last_insert_rowid), - FUNCTION(changes, 0, 0, 0, changes ), - FUNCTION(total_changes, 0, 0, 0, total_changes ), + VFUNCTION(last_insert_rowid, 0, 0, 0, last_insert_rowid), + VFUNCTION(changes, 0, 0, 0, changes ), + VFUNCTION(total_changes, 0, 0, 0, total_changes ), FUNCTION(replace, 3, 0, 0, replaceFunc ), FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ), #ifdef SQLITE_SOUNDEX diff --git a/src/insert.c b/src/insert.c index 1c89a38af..df1b3fc9c 100644 --- a/src/insert.c +++ b/src/insert.c @@ -820,7 +820,7 @@ void sqlite3Insert( /* If this is not a view, open the table and and all indices */ if( !isView ){ int nIdx; - nIdx = sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, -1, + nIdx = sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, -1, 0, &iDataCur, &iIdxCur); aRegIdx = sqlite3DbMallocRaw(db, sizeof(int)*(nIdx+1)); if( aRegIdx==0 ){ @@ -1692,16 +1692,19 @@ int sqlite3OpenTableAndIndices( Table *pTab, /* Table to be opened */ int op, /* OP_OpenRead or OP_OpenWrite */ int iBase, /* Use this for the table cursor, if there is one */ + u8 *aToOpen, /* If not NULL: boolean for each table and index */ int *piDataCur, /* Write the database source cursor number here */ int *piIdxCur /* Write the first index cursor number here */ ){ int i; int iDb; + int iDataCur; Index *pIdx; Vdbe *v; assert( op==OP_OpenRead || op==OP_OpenWrite ); if( IsVirtual(pTab) ){ + assert( aToOpen==0 ); *piDataCur = 0; *piIdxCur = 1; return 0; @@ -1710,20 +1713,25 @@ int sqlite3OpenTableAndIndices( v = sqlite3GetVdbe(pParse); assert( v!=0 ); if( iBase<0 ) iBase = pParse->nTab; - if( HasRowid(pTab) ){ - *piDataCur = iBase++; - sqlite3OpenTable(pParse, *piDataCur, iDb, pTab, op); + iDataCur = iBase++; + if( piDataCur ) *piDataCur = iDataCur; + if( HasRowid(pTab) && (aToOpen==0 || aToOpen[0]) ){ + sqlite3OpenTable(pParse, iDataCur, iDb, pTab, op); }else{ sqlite3TableLock(pParse, iDb, pTab->tnum, op==OP_OpenWrite, pTab->zName); } - *piIdxCur = iBase; + if( piIdxCur ) *piIdxCur = iBase; for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ int iIdxCur = iBase++; assert( pIdx->pSchema==pTab->pSchema ); - if( pIdx->autoIndex==2 && !HasRowid(pTab) ) *piDataCur = iIdxCur; - sqlite3VdbeAddOp3(v, op, iIdxCur, pIdx->tnum, iDb); - sqlite3VdbeSetP4KeyInfo(pParse, pIdx); - VdbeComment((v, "%s", pIdx->zName)); + if( pIdx->autoIndex==2 && !HasRowid(pTab) && piDataCur ){ + *piDataCur = iIdxCur; + } + if( aToOpen==0 || aToOpen[i+1] ){ + sqlite3VdbeAddOp3(v, op, iIdxCur, pIdx->tnum, iDb); + sqlite3VdbeSetP4KeyInfo(pParse, pIdx); + VdbeComment((v, "%s", pIdx->zName)); + } } if( iBase>pParse->nTab ) pParse->nTab = iBase; return i; diff --git a/src/main.c b/src/main.c index 653c8f1f1..6bd5ab024 100644 --- a/src/main.c +++ b/src/main.c @@ -515,6 +515,13 @@ int sqlite3_config(int op, ...){ break; } +#if SQLITE_OS_WIN && defined(SQLITE_WIN32_MALLOC) + case SQLITE_CONFIG_WIN32_HEAPSIZE: { + sqlite3GlobalConfig.nHeap = va_arg(ap, int); + break; + } +#endif + default: { rc = SQLITE_ERROR; break; diff --git a/src/mem5.c b/src/mem5.c index 5f99ebf43..3870e9148 100644 --- a/src/mem5.c +++ b/src/mem5.c @@ -210,25 +210,6 @@ static int memsys5Size(void *p){ } /* -** Find the first entry on the freelist iLogsize. Unlink that -** entry and return its index. -*/ -static int memsys5UnlinkFirst(int iLogsize){ - int i; - int iFirst; - - assert( iLogsize>=0 && iLogsize<=LOGMAX ); - i = iFirst = mem5.aiFreelist[iLogsize]; - assert( iFirst>=0 ); - while( i>0 ){ - if( i<iFirst ) iFirst = i; - i = MEM5LINK(i)->next; - } - memsys5Unlink(iFirst, iLogsize); - return iFirst; -} - -/* ** Return a block of memory of at least nBytes in size. ** Return NULL if unable. Return NULL if nBytes==0. ** @@ -273,7 +254,8 @@ static void *memsys5MallocUnsafe(int nByte){ sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes", nByte); return 0; } - i = memsys5UnlinkFirst(iBin); + i = mem5.aiFreelist[iBin]; + memsys5Unlink(i, iBin); while( iBin>iLogsize ){ int newSize; diff --git a/src/os_win.c b/src/os_win.c index a2f5513a2..31c4b69df 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -1401,14 +1401,20 @@ static int winMemInit(void *pAppData){ #if !SQLITE_OS_WINRT && SQLITE_WIN32_HEAP_CREATE if( !pWinMemData->hHeap ){ + DWORD dwInitialSize = SQLITE_WIN32_HEAP_INIT_SIZE; + DWORD dwMaximumSize = (DWORD)sqlite3GlobalConfig.nHeap; + if( dwMaximumSize==0 ){ + dwMaximumSize = SQLITE_WIN32_HEAP_MAX_SIZE; + }else if( dwInitialSize>dwMaximumSize ){ + dwInitialSize = dwMaximumSize; + } pWinMemData->hHeap = osHeapCreate(SQLITE_WIN32_HEAP_FLAGS, - SQLITE_WIN32_HEAP_INIT_SIZE, - SQLITE_WIN32_HEAP_MAX_SIZE); + dwInitialSize, dwMaximumSize); if( !pWinMemData->hHeap ){ sqlite3_log(SQLITE_NOMEM, - "failed to HeapCreate (%lu), flags=%u, initSize=%u, maxSize=%u", - osGetLastError(), SQLITE_WIN32_HEAP_FLAGS, - SQLITE_WIN32_HEAP_INIT_SIZE, SQLITE_WIN32_HEAP_MAX_SIZE); + "failed to HeapCreate (%lu), flags=%u, initSize=%lu, maxSize=%lu", + osGetLastError(), SQLITE_WIN32_HEAP_FLAGS, dwInitialSize, + dwMaximumSize); return SQLITE_NOMEM; } pWinMemData->bOwned = TRUE; @@ -4068,7 +4074,7 @@ static const sqlite3_io_methods winIoMethod = { ** sqlite3_vfs object. */ -#if 0 +#if defined(__CYGWIN__) /* ** Convert a filename from whatever the underlying operating system ** supports for filenames into UTF-8. Space to hold the result is @@ -4244,23 +4250,17 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ ** be used. However, we may need to convert the string containing ** its name into UTF-8 (i.e. if it is UTF-16 right now). */ - if( osIsNT() ){ - char *zUtf8 = winUnicodeToUtf8(zConverted); - if( !zUtf8 ){ - sqlite3_free(zConverted); - sqlite3_free(zBuf); - OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); - return SQLITE_IOERR_NOMEM; - } - sqlite3_snprintf(nMax, zBuf, "%s", zUtf8); - sqlite3_free(zUtf8); - sqlite3_free(zConverted); - break; - }else{ - sqlite3_snprintf(nMax, zBuf, "%s", zConverted); + char *zUtf8 = winConvertToUtf8Filename(zConverted); + if( !zUtf8 ){ sqlite3_free(zConverted); - break; + sqlite3_free(zBuf); + OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); + return SQLITE_IOERR_NOMEM; } + sqlite3_snprintf(nMax, zBuf, "%s", zUtf8); + sqlite3_free(zUtf8); + sqlite3_free(zConverted); + break; } sqlite3_free(zConverted); } @@ -4945,19 +4945,43 @@ static int winFullPathname( if( !zOut ){ return SQLITE_IOERR_NOMEM; } - if( cygwin_conv_path(CCP_POSIX_TO_WIN_A|CCP_RELATIVE, zRelative, zOut, - pVfs->mxPathname+1)<0 ){ + if( cygwin_conv_path( + (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A) | + CCP_RELATIVE, zRelative, zOut, pVfs->mxPathname+1)<0 ){ sqlite3_free(zOut); return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno, "winFullPathname1", zRelative); + }else{ + char *zUtf8 = winConvertToUtf8Filename(zOut); + if( !zUtf8 ){ + sqlite3_free(zOut); + return SQLITE_IOERR_NOMEM; + } + sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s", + sqlite3_data_directory, winGetDirSep(), zUtf8); + sqlite3_free(zUtf8); + sqlite3_free(zOut); } - sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s", - sqlite3_data_directory, winGetDirSep(), zOut); - sqlite3_free(zOut); }else{ - if( cygwin_conv_path(CCP_POSIX_TO_WIN_A, zRelative, zFull, nFull)<0 ){ + char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 ); + if( !zOut ){ + return SQLITE_IOERR_NOMEM; + } + if( cygwin_conv_path( + (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A), + zRelative, zOut, pVfs->mxPathname+1)<0 ){ + sqlite3_free(zOut); return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno, "winFullPathname2", zRelative); + }else{ + char *zUtf8 = winConvertToUtf8Filename(zOut); + if( !zUtf8 ){ + sqlite3_free(zOut); + return SQLITE_IOERR_NOMEM; + } + sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zUtf8); + sqlite3_free(zUtf8); + sqlite3_free(zOut); } } return SQLITE_OK; diff --git a/src/parse.y b/src/parse.y index 07e607dcf..ba9feb10f 100644 --- a/src/parse.y +++ b/src/parse.y @@ -854,12 +854,7 @@ expr(A) ::= ID(X) LP STAR RP(E). { spanSet(&A,&X,&E); } term(A) ::= CTIME_KW(OP). { - /* The CURRENT_TIME, CURRENT_DATE, and CURRENT_TIMESTAMP values are - ** treated as functions that return constants */ - A.pExpr = sqlite3ExprFunction(pParse, 0,&OP); - if( A.pExpr ){ - A.pExpr->op = TK_CONST_FUNC; - } + A.pExpr = sqlite3ExprFunction(pParse, 0, &OP); spanSet(&A, &OP, &OP); } diff --git a/src/pragma.c b/src/pragma.c index 76a452c46..bbd27b8c1 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -1891,7 +1891,7 @@ void sqlite3Pragma( sqlite3VdbeJumpHere(v, addr); sqlite3ExprCacheClear(pParse); sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead, - 1, &iDataCur, &iIdxCur); + 1, 0, &iDataCur, &iIdxCur); sqlite3VdbeAddOp2(v, OP_Integer, 0, 7); for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ sqlite3VdbeAddOp2(v, OP_Integer, 0, 8+j); /* index entries counter */ diff --git a/src/prepare.c b/src/prepare.c index cfc9c3485..7ea7f3bd6 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -525,6 +525,13 @@ int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){ } /* +** Free all memory allocations in the pParse object +*/ +void sqlite3ParserReset(Parse *pParse){ + if( pParse ) sqlite3ExprListDelete(pParse->db, pParse->pConstExpr); +} + +/* ** Compile the UTF-8 encoded SQL statement zSql into a statement handle. */ static int sqlite3Prepare( @@ -681,6 +688,7 @@ static int sqlite3Prepare( end_prepare: + sqlite3ParserReset(pParse); sqlite3StackFree(db, pParse); rc = sqlite3ApiExit(db, rc); assert( (rc&db->errMask)==rc ); diff --git a/src/resolve.c b/src/resolve.c index b41f23442..2c0907cc4 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -108,10 +108,10 @@ static void resolveAlias( pDup = sqlite3PExpr(pParse, TK_AS, pDup, 0, 0); if( pDup==0 ) return; ExprSetProperty(pDup, EP_Skip); - if( pEList->a[iCol].iAlias==0 ){ - pEList->a[iCol].iAlias = (u16)(++pParse->nAlias); + if( pEList->a[iCol].u.x.iAlias==0 ){ + pEList->a[iCol].u.x.iAlias = (u16)(++pParse->nAlias); } - pDup->iTable = pEList->a[iCol].iAlias; + pDup->iTable = pEList->a[iCol].u.x.iAlias; } if( pExpr->op==TK_COLLATE ){ pDup = sqlite3ExprAddCollateString(pParse, pDup, pExpr->u.zToken); @@ -667,7 +667,6 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ /* Resolve function names */ - case TK_CONST_FUNC: case TK_FUNCTION: { ExprList *pList = pExpr->x.pList; /* The argument list */ int n = pList ? pList->nExpr : 0; /* Number of arguments */ @@ -680,7 +679,6 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ FuncDef *pDef; /* Information about the function */ u8 enc = ENC(pParse->db); /* The database encoding */ - testcase( pExpr->op==TK_CONST_FUNC ); assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); notValidPartIdxWhere(pParse, pNC, "functions"); zId = pExpr->u.zToken; @@ -725,6 +723,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ pExpr->op = TK_NULL; return WRC_Prune; } + if( pDef->funcFlags & SQLITE_FUNC_CONSTANT ) ExprSetProperty(pExpr,EP_Constant); } #endif if( is_agg && (pNC->ncFlags & NC_AllowAgg)==0 ){ @@ -976,7 +975,7 @@ static int resolveCompoundOrderBy( pItem->pExpr->pLeft = pNew; } sqlite3ExprDelete(db, pE); - pItem->iOrderByCol = (u16)iCol; + pItem->u.x.iOrderByCol = (u16)iCol; pItem->done = 1; }else{ moreToDo = 1; @@ -997,8 +996,8 @@ static int resolveCompoundOrderBy( /* ** Check every term in the ORDER BY or GROUP BY clause pOrderBy of ** the SELECT statement pSelect. If any term is reference to a -** result set expression (as determined by the ExprList.a.iOrderByCol field) -** then convert that term into a copy of the corresponding result set +** result set expression (as determined by the ExprList.a.u.x.iOrderByCol +** field) then convert that term into a copy of the corresponding result set ** column. ** ** If any errors are detected, add an error message to pParse and @@ -1025,12 +1024,12 @@ int sqlite3ResolveOrderGroupBy( pEList = pSelect->pEList; assert( pEList!=0 ); /* sqlite3SelectNew() guarantees this */ for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){ - if( pItem->iOrderByCol ){ - if( pItem->iOrderByCol>pEList->nExpr ){ + if( pItem->u.x.iOrderByCol ){ + if( pItem->u.x.iOrderByCol>pEList->nExpr ){ resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr); return 1; } - resolveAlias(pParse, pEList, pItem->iOrderByCol-1, pItem->pExpr, zType,0); + resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol-1, pItem->pExpr, zType,0); } } return 0; @@ -1079,7 +1078,7 @@ static int resolveOrderGroupBy( ** a copy of the iCol-th result-set column. The subsequent call to ** sqlite3ResolveOrderGroupBy() will convert the expression to a ** copy of the iCol-th result-set expression. */ - pItem->iOrderByCol = (u16)iCol; + pItem->u.x.iOrderByCol = (u16)iCol; continue; } } @@ -1091,18 +1090,18 @@ static int resolveOrderGroupBy( resolveOutOfRangeError(pParse, zType, i+1, nResult); return 1; } - pItem->iOrderByCol = (u16)iCol; + pItem->u.x.iOrderByCol = (u16)iCol; continue; } /* Otherwise, treat the ORDER BY term as an ordinary expression */ - pItem->iOrderByCol = 0; + pItem->u.x.iOrderByCol = 0; if( sqlite3ResolveExprNames(pNC, pE) ){ return 1; } for(j=0; j<pSelect->pEList->nExpr; j++){ if( sqlite3ExprCompare(pE, pSelect->pEList->a[j].pExpr, -1)==0 ){ - pItem->iOrderByCol = j+1; + pItem->u.x.iOrderByCol = j+1; } } } diff --git a/src/select.c b/src/select.c index 5cdef3aed..aa8e54b02 100644 --- a/src/select.c +++ b/src/select.c @@ -599,7 +599,8 @@ static void selectInnerLoop( ** values returned by the SELECT are not required. */ sqlite3ExprCacheClear(pParse); - sqlite3ExprCodeExprList(pParse, pEList, regResult, eDest==SRT_Output); + sqlite3ExprCodeExprList(pParse, pEList, regResult, + (eDest==SRT_Output)?SQLITE_ECEL_DUP:0); } nColumn = nResultCol; @@ -2372,8 +2373,8 @@ static int multiSelectOrderBy( for(i=1; db->mallocFailed==0 && i<=p->pEList->nExpr; i++){ struct ExprList_item *pItem; for(j=0, pItem=pOrderBy->a; j<nOrderBy; j++, pItem++){ - assert( pItem->iOrderByCol>0 ); - if( pItem->iOrderByCol==i ) break; + assert( pItem->u.x.iOrderByCol>0 ); + if( pItem->u.x.iOrderByCol==i ) break; } if( j==nOrderBy ){ Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0); @@ -2381,7 +2382,7 @@ static int multiSelectOrderBy( pNew->flags |= EP_IntValue; pNew->u.iValue = i; pOrderBy = sqlite3ExprListAppend(pParse, pOrderBy, pNew); - if( pOrderBy ) pOrderBy->a[nOrderBy++].iOrderByCol = (u16)i; + if( pOrderBy ) pOrderBy->a[nOrderBy++].u.x.iOrderByCol = (u16)i; } } } @@ -2397,8 +2398,9 @@ static int multiSelectOrderBy( if( aPermute ){ struct ExprList_item *pItem; for(i=0, pItem=pOrderBy->a; i<nOrderBy; i++, pItem++){ - assert( pItem->iOrderByCol>0 && pItem->iOrderByCol<=p->pEList->nExpr ); - aPermute[i] = pItem->iOrderByCol - 1; + assert( pItem->u.x.iOrderByCol>0 + && pItem->u.x.iOrderByCol<=p->pEList->nExpr ); + aPermute[i] = pItem->u.x.iOrderByCol - 1; } pKeyMerge = sqlite3KeyInfoAlloc(db, nOrderBy, 1); if( pKeyMerge ){ @@ -2978,7 +2980,7 @@ static int flattenSubquery( if( p->pOrderBy ){ int ii; for(ii=0; ii<p->pOrderBy->nExpr; ii++){ - if( p->pOrderBy->a[ii].iOrderByCol==0 ) return 0; + if( p->pOrderBy->a[ii].u.x.iOrderByCol==0 ) return 0; } } } @@ -3884,7 +3886,7 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){ if( pList ){ nArg = pList->nExpr; regAgg = sqlite3GetTempRange(pParse, nArg); - sqlite3ExprCodeExprList(pParse, pList, regAgg, 1); + sqlite3ExprCodeExprList(pParse, pList, regAgg, SQLITE_ECEL_DUP); }else{ nArg = 0; regAgg = 0; @@ -4385,10 +4387,10 @@ int sqlite3Select( struct ExprList_item *pItem; /* For looping over expression in a list */ for(k=p->pEList->nExpr, pItem=p->pEList->a; k>0; k--, pItem++){ - pItem->iAlias = 0; + pItem->u.x.iAlias = 0; } for(k=pGroupBy->nExpr, pItem=pGroupBy->a; k>0; k--, pItem++){ - pItem->iAlias = 0; + pItem->u.x.iAlias = 0; } if( p->nSelectRow>100 ) p->nSelectRow = 100; }else{ diff --git a/src/shell.c b/src/shell.c index 61df7d6d5..1db1e64e7 100644 --- a/src/shell.c +++ b/src/shell.c @@ -466,6 +466,7 @@ struct callback_data { FILE *pLog; /* Write log output here */ int *aiIndent; /* Array of indents used in MODE_Explain */ int nIndent; /* Size of array aiIndent[] */ + int iIndent; /* Index of current op in aiIndent[] */ }; /* @@ -771,10 +772,10 @@ static int shell_callback(void *pArg, int nArg, char **azArg, char **azCol, int w = strlen30(azArg[i]); } if( i==1 && p->aiIndent && p->pStmt ){ - int iOp = sqlite3_column_int(p->pStmt, 0); - if( iOp<p->nIndent ){ - fprintf(p->out, "%*.s", p->aiIndent[iOp], ""); + if( p->iIndent<p->nIndent ){ + fprintf(p->out, "%*.s", p->aiIndent[p->iIndent], ""); } + p->iIndent++; } if( w<0 ){ fprintf(p->out,"%*.*s%s",-w,-w, @@ -1173,9 +1174,10 @@ static int str_in_array(const char *zStr, const char **azArray){ ** all opcodes that occur between the p2 jump destination and the opcode ** itself by 2 spaces. ** -** * For each "Goto", if the jump destination is a "Yield", "SeekGt", -** or "SeekLt" instruction that occurs earlier in the program than -** the Goto itself, indent all opcodes between the earlier instruction +** * For each "Goto", if the jump destination is earlier in the program +** and ends on one of: +** Yield SeekGt SeekLt RowSetRead +** then indent all opcodes between the earlier instruction ** and "Goto" by 2 spaces. */ static void explain_data_prepare(struct callback_data *p, sqlite3_stmt *pSql){ @@ -1183,10 +1185,10 @@ static void explain_data_prepare(struct callback_data *p, sqlite3_stmt *pSql){ const char *z; /* Used to check if this is an EXPLAIN */ int *abYield = 0; /* True if op is an OP_Yield */ int nAlloc = 0; /* Allocated size of p->aiIndent[], abYield */ - int iOp; + int iOp; /* Index of operation in p->aiIndent[] */ - const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", 0 }; - const char *azYield[] = { "Yield", "SeekLt", "SeekGt", 0 }; + const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", "SorterNext", 0 }; + const char *azYield[] = { "Yield", "SeekLt", "SeekGt", "RowSetRead", 0 }; const char *azGoto[] = { "Goto", 0 }; /* Try to figure out if this is really an EXPLAIN statement. If this @@ -1198,8 +1200,16 @@ static void explain_data_prepare(struct callback_data *p, sqlite3_stmt *pSql){ for(iOp=0; SQLITE_ROW==sqlite3_step(pSql); iOp++){ int i; + int iAddr = sqlite3_column_int(pSql, 0); const char *zOp = (const char*)sqlite3_column_text(pSql, 1); + + /* Set p2 to the P2 field of the current opcode. Then, assuming that + ** p2 is an instruction address, set variable p2op to the index of that + ** instruction in the aiIndent[] array. p2 and p2op may be different if + ** the current instruction is part of a sub-program generated by an + ** SQL trigger or foreign key. */ int p2 = sqlite3_column_int(pSql, 3); + int p2op = (p2 + (iOp-iAddr)); /* Grow the p->aiIndent array as required */ if( iOp>=nAlloc ){ @@ -1212,13 +1222,14 @@ static void explain_data_prepare(struct callback_data *p, sqlite3_stmt *pSql){ p->nIndent = iOp+1; if( str_in_array(zOp, azNext) ){ - for(i=p2; i<iOp; i++) p->aiIndent[i] += 2; + for(i=p2op; i<iOp; i++) p->aiIndent[i] += 2; } - if( str_in_array(zOp, azGoto) && p2<p->nIndent && abYield[p2] ){ - for(i=p2+1; i<iOp; i++) p->aiIndent[i] += 2; + if( str_in_array(zOp, azGoto) && p2op<p->nIndent && abYield[p2op] ){ + for(i=p2op; i<iOp; i++) p->aiIndent[i] += 2; } } + p->iIndent = 0; sqlite3_free(abYield); sqlite3_reset(pSql); } @@ -1230,6 +1241,7 @@ static void explain_data_delete(struct callback_data *p){ sqlite3_free(p->aiIndent); p->aiIndent = 0; p->nIndent = 0; + p->iIndent = 0; } /* @@ -1295,7 +1307,7 @@ static int shell_exec( /* If the shell is currently in ".explain" mode, gather the extra ** data required to add indents to the output.*/ - if( pArg->mode==MODE_Explain ){ + if( pArg && pArg->mode==MODE_Explain ){ explain_data_prepare(pArg, pStmt); } diff --git a/src/sqlite.h.in b/src/sqlite.h.in index f3466cb25..5f048e7a6 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -365,7 +365,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**); ** <ul> ** <li> The application must insure that the 1st parameter to sqlite3_exec() ** is a valid and open [database connection]. -** <li> The application must not close [database connection] specified by +** <li> The application must not close the [database connection] specified by ** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running. ** <li> The application must not modify the SQL statement text passed into ** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running. @@ -442,7 +442,7 @@ int sqlite3_exec( ** [sqlite3_extended_result_codes()] API. ** ** Some of the available extended result codes are listed here. -** One may expect the number of extended result codes will be expand +** One may expect the number of extended result codes will increase ** over time. Software that uses extended result codes should expect ** to see new result codes in future releases of SQLite. ** @@ -1380,7 +1380,7 @@ int sqlite3_db_config(sqlite3*, int op, ...); ** or [sqlite3_realloc()] first calls xRoundup. If xRoundup returns 0, ** that causes the corresponding memory allocation to fail. ** -** The xInit method initializes the memory allocator. (For example, +** The xInit method initializes the memory allocator. For example, ** it might allocate any require mutexes or initialize internal data ** structures. The xShutdown method is invoked (indirectly) by ** [sqlite3_shutdown()] and should deallocate any resources acquired @@ -1682,6 +1682,13 @@ struct sqlite3_mem_methods { ** [SQLITE_MAX_MMAP_SIZE] compile-time option.)^ ** ^If either argument to this option is negative, then that argument is ** changed to its compile-time default. +** +** [[SQLITE_CONFIG_WIN32_HEAPSIZE]] +** <dt>SQLITE_CONFIG_WIN32_HEAPSIZE +** <dd>^This option is only available if SQLite is compiled for Windows +** with the [SQLITE_WIN32_MALLOC] pre-processor macro defined. +** SQLITE_CONFIG_WIN32_HEAPSIZE takes a 32-bit unsigned integer value +** that specifies the maximum size of the created heap. ** </dl> */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ @@ -1706,6 +1713,7 @@ struct sqlite3_mem_methods { #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ #define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ #define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ +#define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ /* ** CAPI3REF: Database Connection Configuration Options @@ -3106,7 +3114,6 @@ int sqlite3_limit(sqlite3*, int id, int newVal); ** choice of query plan if the parameter is the left-hand side of a [LIKE] ** or [GLOB] operator or if the parameter is compared to an indexed column ** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled. -** the ** </li> ** </ol> */ @@ -3836,7 +3843,7 @@ int sqlite3_data_count(sqlite3_stmt *pStmt); ** described above, or until [sqlite3_step()] or [sqlite3_reset()] or ** [sqlite3_finalize()] is called. ^The memory space used to hold strings ** and BLOBs is freed automatically. Do <b>not</b> pass the pointers returned -** [sqlite3_column_blob()], [sqlite3_column_text()], etc. into +** from [sqlite3_column_blob()], [sqlite3_column_text()], etc. into ** [sqlite3_free()]. ** ** ^(If a memory allocation error occurs during the evaluation of any @@ -4914,8 +4921,8 @@ int sqlite3_release_memory(int); ** ** ^The sqlite3_db_release_memory(D) interface attempts to free as much heap ** memory as possible from database connection D. Unlike the -** [sqlite3_release_memory()] interface, this interface is effect even -** when then [SQLITE_ENABLE_MEMORY_MANAGEMENT] compile-time option is +** [sqlite3_release_memory()] interface, this interface is in effect even +** when the [SQLITE_ENABLE_MEMORY_MANAGEMENT] compile-time option is ** omitted. ** ** See also: [sqlite3_release_memory()] diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 06228b291..943b93cc3 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1086,6 +1086,13 @@ struct sqlite3 { #endif /* +** Return true if it OK to factor constant expressions into the initialization +** code. The argument is a Parse object for the code generator. +*/ +#define ConstFactorOk(P) \ + ((P)->cookieGoto>0 && OptimizationEnabled((P)->db,SQLITE_FactorOutConst)) + +/* ** Possible values for the sqlite.magic field. ** The numbers are obtained at random and have no special meaning, other ** than being distinct from one another. @@ -1151,6 +1158,7 @@ struct FuncDestructor { #define SQLITE_FUNC_COUNT 0x100 /* Built-in count(*) aggregate */ #define SQLITE_FUNC_COALESCE 0x200 /* Built-in coalesce() or ifnull() */ #define SQLITE_FUNC_UNLIKELY 0x400 /* Built-in unlikely() function */ +#define SQLITE_FUNC_CONSTANT 0x800 /* Constant inputs give a constant output */ /* ** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are @@ -1163,6 +1171,9 @@ struct FuncDestructor { ** as the user-data (sqlite3_user_data()) for the function. If ** argument bNC is true, then the SQLITE_FUNC_NEEDCOLL flag is set. ** +** VFUNCTION(zName, nArg, iArg, bNC, xFunc) +** Like FUNCTION except it omits the SQLITE_FUNC_CONSTANT flag. +** ** AGGREGATE(zName, nArg, iArg, bNC, xStep, xFinal) ** Used to create an aggregate function definition implemented by ** the C functions xStep and xFinal. The first four parameters @@ -1178,16 +1189,20 @@ struct FuncDestructor { ** parameter. */ #define FUNCTION(zName, nArg, iArg, bNC, xFunc) \ + {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ + SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0} +#define VFUNCTION(zName, nArg, iArg, bNC, xFunc) \ {nArg, SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0} #define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags) \ - {nArg, SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags, \ + {nArg,SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags,\ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0} #define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \ - {nArg, SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ + {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ pArg, 0, xFunc, 0, 0, #zName, 0, 0} #define LIKEFUNC(zName, nArg, arg, flags) \ - {nArg, SQLITE_UTF8|flags, (void *)arg, 0, likeFunc, 0, 0, #zName, 0, 0} + {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|flags, \ + (void *)arg, 0, likeFunc, 0, 0, #zName, 0, 0} #define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \ {nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL), \ SQLITE_INT_TO_PTR(arg), 0, 0, xStep,xFinal,#zName,0,0} @@ -1837,7 +1852,7 @@ struct Expr { #define EP_DblQuoted 0x000040 /* token.z was originally in "..." */ #define EP_InfixFunc 0x000080 /* True for an infix function: LIKE, GLOB, etc */ #define EP_Collate 0x000100 /* Tree contains a TK_COLLATE opeartor */ -#define EP_FixedDest 0x000200 /* Result needed in a specific register */ + /* unused 0x000200 */ #define EP_IntValue 0x000400 /* Integer value contained in u.iValue */ #define EP_xIsSelect 0x000800 /* x.pSelect is valid (otherwise x.pList is) */ #define EP_Skip 0x001000 /* COLLATE, AS, or UNLIKELY */ @@ -1847,6 +1862,7 @@ struct Expr { #define EP_MemToken 0x010000 /* Need to sqlite3DbFree() Expr.zToken */ #define EP_NoReduce 0x020000 /* Cannot EXPRDUP_REDUCE this Expr */ #define EP_Unlikely 0x040000 /* unlikely() or likelihood() function */ +#define EP_Constant 0x080000 /* Node is a constant */ /* ** These macros can be used to test, set, or clear bits in the @@ -1908,8 +1924,14 @@ struct ExprList { u8 sortOrder; /* 1 for DESC or 0 for ASC */ unsigned done :1; /* A flag to indicate when processing is finished */ unsigned bSpanIsTab :1; /* zSpan holds DB.TABLE.COLUMN */ - u16 iOrderByCol; /* For ORDER BY, column number in result set */ - u16 iAlias; /* Index into Parse.aAlias[] for zName */ + unsigned reusable :1; /* Constant expression is reusable */ + union { + struct { + u16 iOrderByCol; /* For ORDER BY, column number in result set */ + u16 iAlias; /* Index into Parse.aAlias[] for zName */ + } x; + int iConstExprReg; /* Register in which Expr value is cached */ + } u; } *a; /* Alloc a power of two greater or equal to nExpr */ }; @@ -2286,6 +2308,7 @@ struct Parse { int iReg; /* Reg with value of this column. 0 means none. */ int lru; /* Least recently used entry has the smallest value */ } aColCache[SQLITE_N_COLCACHE]; /* One for each column cache entry */ + ExprList *pConstExpr;/* Constant expressions */ yDbMask writeMask; /* Start a write transaction on these databases */ yDbMask cookieMask; /* Bitmask of schema verified databases */ int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */ @@ -2900,11 +2923,13 @@ void sqlite3ExprCacheRemove(Parse*, int, int); void sqlite3ExprCacheClear(Parse*); void sqlite3ExprCacheAffinityChange(Parse*, int, int); int sqlite3ExprCode(Parse*, Expr*, int); +void sqlite3ExprCodeAtInit(Parse*, Expr*, int, u8); int sqlite3ExprCodeTemp(Parse*, Expr*, int*); int sqlite3ExprCodeTarget(Parse*, Expr*, int); int sqlite3ExprCodeAndCache(Parse*, Expr*, int); -void sqlite3ExprCodeConstants(Parse*, Expr*); -int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int); +int sqlite3ExprCodeExprList(Parse*, ExprList*, int, u8); +#define SQLITE_ECEL_DUP 0x01 /* Deep, not shallow copies */ +#define SQLITE_ECEL_FACTOR 0x02 /* Factor out constant terms */ void sqlite3ExprIfTrue(Parse*, Expr*, int, int); void sqlite3ExprIfFalse(Parse*, Expr*, int, int); Table *sqlite3FindTable(sqlite3*,const char*, const char*); @@ -2949,7 +2974,7 @@ int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*); void sqlite3GenerateConstraintChecks(Parse*,Table*,int*,int,int,int,int, u8,u8,int,int*); void sqlite3CompleteInsertion(Parse*,Table*,int,int,int,int*,int,int,int); -int sqlite3OpenTableAndIndices(Parse*, Table*, int, int, int*, int*); +int sqlite3OpenTableAndIndices(Parse*, Table*, int, int, u8*, int*, int*); void sqlite3BeginWriteOperation(Parse*, int, int); void sqlite3MultiWrite(Parse*); void sqlite3MayAbort(Parse*); @@ -3271,6 +3296,7 @@ void sqlite3InvalidFunction(sqlite3_context*,int,sqlite3_value**); sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context*); int sqlite3VdbeParameterIndex(Vdbe*, const char*, int); int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *); +void sqlite3ParserReset(Parse*); int sqlite3Reprepare(Vdbe*); void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*); CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *); diff --git a/src/tclsqlite.c b/src/tclsqlite.c index d9a1e5778..5efc113a6 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -425,13 +425,12 @@ static int safeToUseEvalObjv(Tcl_Interp *interp, Tcl_Obj *pCmd){ */ static SqlFunc *findSqlFunc(SqliteDb *pDb, const char *zName){ SqlFunc *p, *pNew; - int i; - pNew = (SqlFunc*)Tcl_Alloc( sizeof(*pNew) + strlen30(zName) + 1 ); + int nName = strlen30(zName); + pNew = (SqlFunc*)Tcl_Alloc( sizeof(*pNew) + nName + 1 ); pNew->zName = (char*)&pNew[1]; - for(i=0; zName[i]; i++){ pNew->zName[i] = tolower(zName[i]); } - pNew->zName[i] = 0; + memcpy(pNew->zName, zName, nName+1); for(p=pDb->pFunc; p; p=p->pNext){ - if( strcmp(p->zName, pNew->zName)==0 ){ + if( sqlite3_stricmp(p->zName, pNew->zName)==0 ){ Tcl_Free((char*)pNew); return p; } @@ -1127,13 +1126,14 @@ static int dbPrepareAndBind( int nSql; /* Length of zSql in bytes */ int nVar; /* Number of variables in statement */ int iParm = 0; /* Next free entry in apParm */ + char c; int i; Tcl_Interp *interp = pDb->interp; *ppPreStmt = 0; /* Trim spaces from the start of zSql and calculate the remaining length. */ - while( isspace(zSql[0]) ){ zSql++; } + while( (c = zSql[0])==' ' || c=='\t' || c=='\r' || c=='\n' ){ zSql++; } nSql = strlen30(zSql); for(pPreStmt = pDb->stmtList; pPreStmt; pPreStmt=pPreStmt->pNext){ diff --git a/src/test_config.c b/src/test_config.c index a624a43db..5fa43515e 100644 --- a/src/test_config.c +++ b/src/test_config.c @@ -63,6 +63,12 @@ static void set_options(Tcl_Interp *interp){ Tcl_SetVar2(interp, "sqlite_options", "curdir", "0", TCL_GLOBAL_ONLY); #endif +#ifdef SQLITE_WIN32_MALLOC + Tcl_SetVar2(interp, "sqlite_options", "win32malloc", "1", TCL_GLOBAL_ONLY); +#else + Tcl_SetVar2(interp, "sqlite_options", "win32malloc", "0", TCL_GLOBAL_ONLY); +#endif + #ifdef SQLITE_DEBUG Tcl_SetVar2(interp, "sqlite_options", "debug", "1", TCL_GLOBAL_ONLY); #else diff --git a/src/test_malloc.c b/src/test_malloc.c index f513e24bf..e3cfcaa9f 100644 --- a/src/test_malloc.c +++ b/src/test_malloc.c @@ -1131,6 +1131,33 @@ static int test_config_heap( } /* +** Usage: sqlite3_config_heap_size NBYTE +*/ +static int test_config_heap_size( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + int nByte; /* Size to pass to sqlite3_config() */ + int rc; /* Return code of sqlite3_config() */ + + Tcl_Obj * CONST *aArg = &objv[1]; + int nArg = objc-1; + + if( nArg!=1 ){ + Tcl_WrongNumArgs(interp, 1, objv, "NBYTE"); + return TCL_ERROR; + } + if( Tcl_GetIntFromObj(interp, aArg[0], &nByte) ) return TCL_ERROR; + + rc = sqlite3_config(SQLITE_CONFIG_WIN32_HEAPSIZE, nByte); + + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); + return TCL_OK; +} + +/* ** Usage: sqlite3_config_error [DB] ** ** Invoke sqlite3_config() or sqlite3_db_config() with invalid @@ -1473,6 +1500,7 @@ int Sqlitetest_malloc_Init(Tcl_Interp *interp){ { "sqlite3_db_status", test_db_status ,0 }, { "install_malloc_faultsim", test_install_malloc_faultsim ,0 }, { "sqlite3_config_heap", test_config_heap ,0 }, + { "sqlite3_config_heap_size", test_config_heap_size ,0 }, { "sqlite3_config_memstatus", test_config_memstatus ,0 }, { "sqlite3_config_lookaside", test_config_lookaside ,0 }, { "sqlite3_config_error", test_config_error ,0 }, diff --git a/src/trigger.c b/src/trigger.c index 607b83124..1c68a708d 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -924,6 +924,7 @@ static TriggerPrg *codeRowTrigger( assert( !pSubParse->pAinc && !pSubParse->pZombieTab ); assert( !pSubParse->pTriggerPrg && !pSubParse->nMaxArg ); + sqlite3ParserReset(pSubParse); sqlite3StackFree(db, pSubParse); return pPrg; diff --git a/src/update.c b/src/update.c index d355d7153..3e42461cd 100644 --- a/src/update.c +++ b/src/update.c @@ -101,6 +101,7 @@ void sqlite3Update( Index *pIdx; /* For looping over indices */ Index *pPk; /* The PRIMARY KEY index for WITHOUT ROWID tables */ int nIdx; /* Number of indices that need updating */ + int iBaseCur; /* Base cursor number */ int iDataCur; /* Cursor for the canonical data btree */ int iIdxCur; /* Cursor for the first index */ sqlite3 *db; /* The database structure */ @@ -108,11 +109,11 @@ void sqlite3Update( int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the ** an expression for the i-th column of the table. ** aXRef[i]==-1 if the i-th column is not changed. */ + u8 *aToOpen; /* 1 for tables and indices to be opened */ u8 chngPk; /* PRIMARY KEY changed in a WITHOUT ROWID table */ u8 chngRowid; /* Rowid changed in a normal table */ u8 chngKey; /* Either chngPk or chngRowid */ Expr *pRowidExpr = 0; /* Expression defining the new record number */ - int openAll = 0; /* True if all indices need to be opened */ AuthContext sContext; /* The authorization context */ NameContext sNC; /* The name-context to resolve expressions in */ int iDb; /* Database containing the table being updated */ @@ -176,16 +177,13 @@ void sqlite3Update( if( sqlite3IsReadOnly(pParse, pTab, tmask) ){ goto update_cleanup; } - aXRef = sqlite3DbMallocRaw(db, sizeof(int) * pTab->nCol ); - if( aXRef==0 ) goto update_cleanup; - for(i=0; i<pTab->nCol; i++) aXRef[i] = -1; /* Allocate a cursors for the main database table and for all indices. ** The index cursors might not be used, but if they are used they ** need to occur right after the database cursor. So go ahead and ** allocate enough space, just in case. */ - pTabList->a[0].iCursor = iDataCur = pParse->nTab++; + pTabList->a[0].iCursor = iBaseCur = iDataCur = pParse->nTab++; iIdxCur = iDataCur+1; pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab); for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ @@ -196,6 +194,17 @@ void sqlite3Update( pParse->nTab++; } + /* Allocate space for aXRef[], aRegIdx[], and aToOpen[]. + ** Initialize aXRef[] and aToOpen[] to their default values. + */ + aXRef = sqlite3DbMallocRaw(db, sizeof(int) * (pTab->nCol+nIdx) + nIdx+2 ); + if( aXRef==0 ) goto update_cleanup; + aRegIdx = aXRef+pTab->nCol; + aToOpen = (u8*)(aRegIdx+nIdx); + memset(aToOpen, 1, nIdx+1); + aToOpen[nIdx+1] = 0; + for(i=0; i<pTab->nCol; i++) aXRef[i] = -1; + /* Initialize the name-context */ memset(&sNC, 0, sizeof(sNC)); sNC.pParse = pParse; @@ -254,17 +263,17 @@ void sqlite3Update( assert( chngPk==0 || chngPk==1 ); chngKey = chngRowid + chngPk; + /* The SET expressions are not actually used inside the WHERE loop. + ** So reset the colUsed mask + */ + pTabList->a[0].colUsed = 0; + hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngKey); - /* Allocate memory for the array aRegIdx[]. There is one entry in the - ** array for each index associated with table being updated. Fill in - ** the value with a register number for indices that are to be used - ** and with zero for unused indices. + /* There is one entry in the aRegIdx[] array for each index on the table + ** being updated. Fill in aRegIdx[] with a register number that will hold + ** the key for accessing each index. */ - if( nIdx>0 ){ - aRegIdx = sqlite3DbMallocRaw(db, sizeof(Index*) * nIdx ); - if( aRegIdx==0 ) goto update_cleanup; - } for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ int reg; if( chngKey || hasFK || pIdx->pPartIdxWhere || pIdx==pPk ){ @@ -278,6 +287,7 @@ void sqlite3Update( } } } + if( reg==0 ) aToOpen[j+1] = 0; aRegIdx[j] = reg; } @@ -401,42 +411,30 @@ void sqlite3Update( ** action, then we need to open all indices because we might need ** to be deleting some records. */ - if( !okOnePass && HasRowid(pTab) ){ - sqlite3OpenTable(pParse, iDataCur, iDb, pTab, OP_OpenWrite); - } - sqlite3TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName); if( onError==OE_Replace ){ - openAll = 1; + memset(aToOpen, 1, nIdx+1); }else{ - openAll = 0; for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ if( pIdx->onError==OE_Replace ){ - openAll = 1; + memset(aToOpen, 1, nIdx+1); break; } } } - for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ - int iThisCur = iIdxCur+i; - assert( aRegIdx ); - if( (openAll || aRegIdx[i]>0) - && iThisCur!=aiCurOnePass[1] - ){ - assert( iThisCur!=aiCurOnePass[0] ); - sqlite3VdbeAddOp3(v, OP_OpenWrite, iThisCur, pIdx->tnum, iDb); - sqlite3VdbeSetP4KeyInfo(pParse, pIdx); - assert( pParse->nTab>iThisCur ); - VdbeComment((v, "%s", pIdx->zName)); - if( okOnePass && pPk && iThisCur==iDataCur ){ - sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, - regKey, nKey); - } - } + if( okOnePass ){ + if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iBaseCur] = 0; + if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iBaseCur] = 0; } + sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, iBaseCur, aToOpen, + 0, 0); } /* Top of the update loop */ if( okOnePass ){ + if( aToOpen[iDataCur-iBaseCur] ){ + assert( pPk!=0 ); + sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, regKey, nKey); + } labelContinue = labelBreak; sqlite3VdbeAddOp2(v, OP_IsNull, pPk ? regKey : regOldRowid, labelBreak); }else if( pPk ){ @@ -642,7 +640,7 @@ void sqlite3Update( /* Close all tables */ for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ assert( aRegIdx ); - if( openAll || aRegIdx[i]>0 ){ + if( aToOpen[i+1] ){ sqlite3VdbeAddOp2(v, OP_Close, iIdxCur+i, 0); } } @@ -669,8 +667,7 @@ void sqlite3Update( update_cleanup: sqlite3AuthContextPop(&sContext); - sqlite3DbFree(db, aRegIdx); - sqlite3DbFree(db, aXRef); + sqlite3DbFree(db, aXRef); /* Also frees aRegIdx[] and aToOpen[] */ sqlite3SrcListDelete(db, pTabList); sqlite3ExprListDelete(db, pChanges); sqlite3ExprDelete(db, pWhere); diff --git a/src/util.c b/src/util.c index 50ffd9865..5aa8af68d 100644 --- a/src/util.c +++ b/src/util.c @@ -512,7 +512,7 @@ int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){ u = u*10 + c - '0'; } if( u>LARGEST_INT64 ){ - *pNum = SMALLEST_INT64; + *pNum = neg ? SMALLEST_INT64 : LARGEST_INT64; }else if( neg ){ *pNum = -(i64)u; }else{ @@ -543,7 +543,6 @@ int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){ /* zNum is exactly 9223372036854775808. Fits if negative. The ** special case 2 overflow if positive */ assert( u-1==LARGEST_INT64 ); - assert( (*pNum)==SMALLEST_INT64 ); return neg ? 0 : 2; } } diff --git a/src/vdbe.c b/src/vdbe.c index b47e00b68..765d5eb22 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -222,9 +222,8 @@ static VdbeCursor *allocateCursor( int nByte; VdbeCursor *pCx = 0; nByte = - ROUND8(sizeof(VdbeCursor)) + - (isBtreeCursor?sqlite3BtreeCursorSize():0) + - 2*nField*sizeof(u32); + ROUND8(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField + + (isBtreeCursor?sqlite3BtreeCursorSize():0); assert( iCur<p->nCursor ); if( p->apCsr[iCur] ){ @@ -236,12 +235,9 @@ static VdbeCursor *allocateCursor( memset(pCx, 0, sizeof(VdbeCursor)); pCx->iDb = iDb; pCx->nField = nField; - if( nField ){ - pCx->aType = (u32 *)&pMem->z[ROUND8(sizeof(VdbeCursor))]; - } if( isBtreeCursor ){ pCx->pCursor = (BtCursor*) - &pMem->z[ROUND8(sizeof(VdbeCursor))+2*nField*sizeof(u32)]; + &pMem->z[ROUND8(sizeof(VdbeCursor))+2*sizeof(u32)*nField]; sqlite3BtreeCursorZero(pCx->pCursor); } } @@ -1095,15 +1091,15 @@ case OP_Move: { int p1; /* Register to copy from */ int p2; /* Register to copy to */ - n = pOp->p3 + 1; + n = pOp->p3; p1 = pOp->p1; p2 = pOp->p2; - assert( n>0 && p1>0 && p2>0 ); + assert( n>=0 && p1>0 && p2>0 ); assert( p1+n<=p2 || p2+n<=p1 ); pIn1 = &aMem[p1]; pOut = &aMem[p2]; - while( n-- ){ + do{ assert( pOut<=&aMem[(p->nMem-p->nCursor)] ); assert( pIn1<=&aMem[(p->nMem-p->nCursor)] ); assert( memIsValid(pIn1) ); @@ -1120,7 +1116,7 @@ case OP_Move: { REGISTER_TRACE(p2++, pOut); pIn1++; pOut++; - } + }while( n-- ); break; } @@ -1332,7 +1328,7 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */ ** If either input is NULL, the result is NULL. */ /* Opcode: Divide P1 P2 P3 * * -** Synopsis: r[P3]=r[P1]/r[P2] +** Synopsis: r[P3]=r[P2]/r[P1] ** ** Divide the value in register P1 by the value in register P2 ** and store the result in register P3 (P3=P2/P1). If the value in @@ -1340,11 +1336,11 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */ ** NULL, the result is NULL. */ /* Opcode: Remainder P1 P2 P3 * * -** Synopsis: r[P3]=r[P1]%r[P2] +** Synopsis: r[P3]=r[P2]%r[P1] ** -** Compute the remainder after integer division of the value in -** register P1 by the value in register P2 and store the result in P3. -** If the value in register P2 is zero the result is NULL. +** Compute the remainder after integer register P2 is divided by +** register P1 and store the result in register P3. +** If the value in register P1 is zero the result is NULL. ** If either operand is NULL, the result is NULL. */ case OP_Add: /* same as TK_PLUS, in1, in2, out3 */ @@ -1501,10 +1497,6 @@ case OP_Function: { assert( pOp->p4type==P4_FUNCDEF ); ctx.pFunc = pOp->p4.pFunc; - ctx.s.flags = MEM_Null; - ctx.s.db = db; - ctx.s.xDel = 0; - ctx.s.zMalloc = 0; ctx.iOp = pc; ctx.pVdbe = p; @@ -1512,7 +1504,10 @@ case OP_Function: { ** the pointer to ctx.s so in case the user-function can use ** the already allocated buffer instead of allocating a new one. */ - sqlite3VdbeMemMove(&ctx.s, pOut); + memcpy(&ctx.s, pOut, sizeof(Mem)); + pOut->flags = MEM_Null; + pOut->xDel = 0; + pOut->zMalloc = 0; MemSetTypeFlag(&ctx.s, MEM_Null); ctx.fErrorOrAux = 0; @@ -1547,7 +1542,8 @@ case OP_Function: { /* Copy the result of the function into register P3 */ sqlite3VdbeChangeEncoding(&ctx.s, encoding); - sqlite3VdbeMemMove(pOut, &ctx.s); + assert( pOut->flags==MEM_Null ); + memcpy(pOut, &ctx.s, sizeof(Mem)); if( sqlite3VdbeMemTooBig(pOut) ){ goto too_big; } @@ -1672,17 +1668,19 @@ case OP_AddImm: { /* in1 */ */ case OP_MustBeInt: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; - applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding); if( (pIn1->flags & MEM_Int)==0 ){ - if( pOp->p2==0 ){ - rc = SQLITE_MISMATCH; - goto abort_due_to_error; - }else{ - pc = pOp->p2 - 1; + applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding); + if( (pIn1->flags & MEM_Int)==0 ){ + if( pOp->p2==0 ){ + rc = SQLITE_MISMATCH; + goto abort_due_to_error; + }else{ + pc = pOp->p2 - 1; + break; + } } - }else{ - MemSetTypeFlag(pIn1, MEM_Int); } + MemSetTypeFlag(pIn1, MEM_Int); break; } @@ -1807,7 +1805,7 @@ case OP_ToReal: { /* same as TK_TO_REAL, in1 */ #endif /* !defined(SQLITE_OMIT_CAST) && !defined(SQLITE_OMIT_FLOATING_POINT) */ /* Opcode: Lt P1 P2 P3 P4 P5 -** Synopsis: if r[P1]<r[P3] goto P3 +** Synopsis: if r[P1]<r[P3] goto P2 ** ** Compare the values in register P1 and P3. If reg(P3)<reg(P1) then ** jump to address P2. @@ -2261,151 +2259,103 @@ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */ ** skipped for length() and all content loading can be skipped for typeof(). */ case OP_Column: { - u32 payloadSize; /* Number of bytes in the record */ i64 payloadSize64; /* Number of bytes in the record */ - int p1; /* P1 value of the opcode */ int p2; /* column number to retrieve */ VdbeCursor *pC; /* The VDBE cursor */ - char *zRec; /* Pointer to complete record-data */ BtCursor *pCrsr; /* The BTree cursor */ u32 *aType; /* aType[i] holds the numeric type of the i-th column */ u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */ - int nField; /* number of fields in the record */ int len; /* The length of the serialized data for the column */ int i; /* Loop counter */ - char *zData; /* Part of the record being decoded */ Mem *pDest; /* Where to write the extracted value */ Mem sMem; /* For storing the record being decoded */ - u8 *zIdx; /* Index into header */ - u8 *zEndHdr; /* Pointer to first byte after the header */ + const u8 *zData; /* Part of the record being decoded */ + const u8 *zHdr; /* Next unparsed byte of the header */ + const u8 *zEndHdr; /* Pointer to first byte after the header */ u32 offset; /* Offset into the data */ u32 szField; /* Number of bytes in the content of a field */ - int szHdr; /* Size of the header size field at start of record */ - int avail; /* Number of bytes of available data */ + u32 avail; /* Number of bytes of available data */ u32 t; /* A type code from the record header */ Mem *pReg; /* PseudoTable input register */ - - p1 = pOp->p1; p2 = pOp->p2; - pC = 0; - memset(&sMem, 0, sizeof(sMem)); - assert( p1<p->nCursor ); assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) ); pDest = &aMem[pOp->p3]; memAboutToChange(p, pDest); - zRec = 0; - - /* This block sets the variable payloadSize to be the total number of - ** bytes in the record. - ** - ** zRec is set to be the complete text of the record if it is available. - ** The complete record text is always available for pseudo-tables - ** If the record is stored in a cursor, the complete record text - ** might be available in the pC->aRow cache. Or it might not be. - ** If the data is unavailable, zRec is set to NULL. - ** - ** We also compute the number of columns in the record. For cursors, - ** the number of columns is stored in the VdbeCursor.nField element. - */ - pC = p->apCsr[p1]; + assert( pOp->p1>=0 && pOp->p1<p->nCursor ); + pC = p->apCsr[pOp->p1]; assert( pC!=0 ); + assert( p2<pC->nField ); + aType = pC->aType; + aOffset = aType + pC->nField; #ifndef SQLITE_OMIT_VIRTUALTABLE - assert( pC->pVtabCursor==0 ); + assert( pC->pVtabCursor==0 ); /* OP_Column never called on virtual table */ #endif pCrsr = pC->pCursor; - if( pCrsr!=0 ){ - /* The record is stored in a B-Tree */ - rc = sqlite3VdbeCursorMoveto(pC); - if( rc ) goto abort_due_to_error; - if( pC->nullRow ){ - payloadSize = 0; - }else if( pC->cacheStatus==p->cacheCtr ){ - payloadSize = pC->payloadSize; - zRec = (char*)pC->aRow; - }else if( pC->isIndex ){ - assert( sqlite3BtreeCursorIsValid(pCrsr) ); - VVA_ONLY(rc =) sqlite3BtreeKeySize(pCrsr, &payloadSize64); - assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */ - /* sqlite3BtreeParseCellPtr() uses getVarint32() to extract the - ** payload size, so it is impossible for payloadSize64 to be - ** larger than 32 bits. */ - assert( (payloadSize64 & SQLITE_MAX_U32)==(u64)payloadSize64 ); - payloadSize = (u32)payloadSize64; - }else{ - assert( sqlite3BtreeCursorIsValid(pCrsr) ); - VVA_ONLY(rc =) sqlite3BtreeDataSize(pCrsr, &payloadSize); - assert( rc==SQLITE_OK ); /* DataSize() cannot fail */ - } - }else{ - assert( pC->pseudoTableReg>0 ); - pReg = &aMem[pC->pseudoTableReg]; - if( pC->multiPseudo ){ - sqlite3VdbeMemShallowCopy(pDest, pReg+p2, MEM_Ephem); - Deephemeralize(pDest); - goto op_column_out; - } - assert( pReg->flags & MEM_Blob ); - assert( memIsValid(pReg) ); - payloadSize = pReg->n; - zRec = pReg->z; - pC->cacheStatus = (pOp->p5&OPFLAG_CLEARCACHE) ? CACHE_STALE : p->cacheCtr; - assert( payloadSize==0 || zRec!=0 ); - } - - /* If payloadSize is 0, then just store a NULL. This can happen because of - ** nullRow or because of a corrupt database. */ - if( payloadSize==0 ){ - MemSetTypeFlag(pDest, MEM_Null); - goto op_column_out; - } - assert( db->aLimit[SQLITE_LIMIT_LENGTH]>=0 ); - if( payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ - goto too_big; - } + assert( pCrsr!=0 || pC->pseudoTableReg>0 ); /* pCrsr NULL on PseudoTables */ + assert( pCrsr!=0 || pC->nullRow ); /* pC->nullRow on PseudoTables */ - nField = pC->nField; - assert( p2<nField ); - - /* Read and parse the table header. Store the results of the parse - ** into the record header cache fields of the cursor. - */ - aType = pC->aType; - if( pC->cacheStatus==p->cacheCtr ){ - aOffset = pC->aOffset; - }else{ - assert(aType); - avail = 0; - pC->aOffset = aOffset = &aType[nField]; - pC->payloadSize = payloadSize; - pC->cacheStatus = p->cacheCtr; - - /* Figure out how many bytes are in the header */ - if( zRec ){ - zData = zRec; + /* If the cursor cache is stale, bring it up-to-date */ + rc = sqlite3VdbeCursorMoveto(pC); + if( rc ) goto abort_due_to_error; + if( pC->cacheStatus!=p->cacheCtr || (pOp->p5&OPFLAG_CLEARCACHE)!=0 ){ + if( pC->nullRow ){ + if( pCrsr==0 ){ + assert( pC->pseudoTableReg>0 ); + pReg = &aMem[pC->pseudoTableReg]; + if( pC->multiPseudo ){ + sqlite3VdbeMemShallowCopy(pDest, pReg+p2, MEM_Ephem); + Deephemeralize(pDest); + goto op_column_out; + } + assert( pReg->flags & MEM_Blob ); + assert( memIsValid(pReg) ); + pC->payloadSize = pC->szRow = avail = pReg->n; + pC->aRow = (u8*)pReg->z; + }else{ + MemSetTypeFlag(pDest, MEM_Null); + goto op_column_out; + } }else{ - if( pC->isIndex ){ - zData = (char*)sqlite3BtreeKeyFetch(pCrsr, &avail); + assert( pCrsr ); + if( pC->isTable==0 ){ + assert( sqlite3BtreeCursorIsValid(pCrsr) ); + VVA_ONLY(rc =) sqlite3BtreeKeySize(pCrsr, &payloadSize64); + assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */ + /* sqlite3BtreeParseCellPtr() uses getVarint32() to extract the + ** payload size, so it is impossible for payloadSize64 to be + ** larger than 32 bits. */ + assert( (payloadSize64 & SQLITE_MAX_U32)==(u64)payloadSize64 ); + pC->aRow = sqlite3BtreeKeyFetch(pCrsr, &avail); + pC->payloadSize = (u32)payloadSize64; }else{ - zData = (char*)sqlite3BtreeDataFetch(pCrsr, &avail); + assert( sqlite3BtreeCursorIsValid(pCrsr) ); + VVA_ONLY(rc =) sqlite3BtreeDataSize(pCrsr, &pC->payloadSize); + assert( rc==SQLITE_OK ); /* DataSize() cannot fail */ + pC->aRow = sqlite3BtreeDataFetch(pCrsr, &avail); } - /* If KeyFetch()/DataFetch() managed to get the entire payload, - ** save the payload in the pC->aRow cache. That will save us from - ** having to make additional calls to fetch the content portion of - ** the record. - */ - assert( avail>=0 ); - if( payloadSize <= (u32)avail ){ - zRec = zData; - pC->aRow = (u8*)zData; + assert( avail<=65536 ); /* Maximum page size is 64KiB */ + if( pC->payloadSize <= (u32)avail ){ + pC->szRow = pC->payloadSize; }else{ - pC->aRow = 0; + pC->szRow = avail; } + if( pC->payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ + goto too_big; + } + } + pC->cacheStatus = p->cacheCtr; + pC->iHdrOffset = getVarint32(pC->aRow, offset); + pC->nHdrParsed = 0; + aOffset[0] = offset; + if( avail<offset ){ + /* pC->aRow does not have to hold the entire row, but it does at least + ** need to cover the header of the record. If pC->aRow does not contain + ** the complete header, then set it to zero, forcing the header to be + ** dynamically allocated. */ + pC->aRow = 0; + pC->szRow = 0; } - /* The following assert is true in all cases except when - ** the database file has been corrupted externally. - ** assert( zRec!=0 || avail>=payloadSize || avail>=9 ); */ - szHdr = getVarint32((u8*)zData, offset); /* Make sure a corrupt database has not given us an oversize header. ** Do this now to avoid an oversize memory allocation. @@ -2416,155 +2366,148 @@ case OP_Column: { ** 3-byte type for each of the maximum of 32768 columns plus three ** extra bytes for the header length itself. 32768*3 + 3 = 98307. */ - if( offset > 98307 ){ + if( offset > 98307 || offset > pC->payloadSize ){ rc = SQLITE_CORRUPT_BKPT; - goto op_column_out; + goto op_column_error; } + } - /* Compute in len the number of bytes of data we need to read in order - ** to get nField type values. offset is an upper bound on this. But - ** nField might be significantly less than the true number of columns - ** in the table, and in that case, 5*nField+3 might be smaller than offset. - ** We want to minimize len in order to limit the size of the memory - ** allocation, especially if a corrupt database file has caused offset - ** to be oversized. Offset is limited to 98307 above. But 98307 might - ** still exceed Robson memory allocation limits on some configurations. - ** On systems that cannot tolerate large memory allocations, nField*5+3 - ** will likely be much smaller since nField will likely be less than - ** 20 or so. This insures that Robson memory allocation limits are - ** not exceeded even for corrupt database files. - */ - len = nField*5 + 3; - if( len > (int)offset ) len = (int)offset; - - /* The KeyFetch() or DataFetch() above are fast and will get the entire - ** record header in most cases. But they will fail to get the complete - ** record header if the record header does not fit on a single page - ** in the B-Tree. When that happens, use sqlite3VdbeMemFromBtree() to - ** acquire the complete header text. + /* Make sure at least the first p2+1 entries of the header have been + ** parsed and valid information is in aOffset[] and aType[]. + */ + if( pC->nHdrParsed<=p2 ){ + /* If there is more header available for parsing in the record, try + ** to extract additional fields up through the p2+1-th field */ - if( !zRec && avail<len ){ - sMem.flags = 0; - sMem.db = 0; - rc = sqlite3VdbeMemFromBtree(pCrsr, 0, len, pC->isIndex, &sMem); - if( rc!=SQLITE_OK ){ - goto op_column_out; + if( pC->iHdrOffset<aOffset[0] ){ + /* Make sure zData points to enough of the record to cover the header. */ + if( pC->aRow==0 ){ + memset(&sMem, 0, sizeof(sMem)); + rc = sqlite3VdbeMemFromBtree(pCrsr, 0, aOffset[0], + !pC->isTable, &sMem); + if( rc!=SQLITE_OK ){ + goto op_column_error; + } + zData = (u8*)sMem.z; + }else{ + zData = pC->aRow; } - zData = sMem.z; - } - zEndHdr = (u8 *)&zData[len]; - zIdx = (u8 *)&zData[szHdr]; - - /* Scan the header and use it to fill in the aType[] and aOffset[] - ** arrays. aType[i] will contain the type integer for the i-th - ** column and aOffset[i] will contain the offset from the beginning - ** of the record to the start of the data for the i-th column - */ - for(i=0; i<nField; i++){ - if( zIdx<zEndHdr ){ - aOffset[i] = offset; - if( zIdx[0]<0x80 ){ - t = zIdx[0]; - zIdx++; + + /* Fill in aType[i] and aOffset[i] values through the p2-th field. */ + i = pC->nHdrParsed; + offset = aOffset[i]; + zHdr = zData + pC->iHdrOffset; + zEndHdr = zData + aOffset[0]; + assert( i<=p2 && zHdr<zEndHdr ); + do{ + if( zHdr[0]<0x80 ){ + t = zHdr[0]; + zHdr++; }else{ - zIdx += sqlite3GetVarint32(zIdx, &t); + zHdr += sqlite3GetVarint32(zHdr, &t); } aType[i] = t; szField = sqlite3VdbeSerialTypeLen(t); offset += szField; if( offset<szField ){ /* True if offset overflows */ - zIdx = &zEndHdr[1]; /* Forces SQLITE_CORRUPT return below */ + zHdr = &zEndHdr[1]; /* Forces SQLITE_CORRUPT return below */ break; } - }else{ - /* If i is less that nField, then there are fewer fields in this - ** record than SetNumColumns indicated there are columns in the - ** table. Set the offset for any extra columns not present in - ** the record to 0. This tells code below to store the default value - ** for the column instead of deserializing a value from the record. - */ - aOffset[i] = 0; + i++; + aOffset[i] = offset; + }while( i<=p2 && zHdr<zEndHdr ); + pC->nHdrParsed = i; + pC->iHdrOffset = (u32)(zHdr - zData); + if( pC->aRow==0 ){ + sqlite3VdbeMemRelease(&sMem); + sMem.flags = MEM_Null; + } + + /* If we have read more header data than was contained in the header, + ** or if the end of the last field appears to be past the end of the + ** record, or if the end of the last field appears to be before the end + ** of the record (when all fields present), then we must be dealing + ** with a corrupt database. + */ + if( (zHdr > zEndHdr) + || (offset > pC->payloadSize) + || (zHdr==zEndHdr && offset!=pC->payloadSize) + ){ + rc = SQLITE_CORRUPT_BKPT; + goto op_column_error; } } - sqlite3VdbeMemRelease(&sMem); - sMem.flags = MEM_Null; - - /* If we have read more header data than was contained in the header, - ** or if the end of the last field appears to be past the end of the - ** record, or if the end of the last field appears to be before the end - ** of the record (when all fields present), then we must be dealing - ** with a corrupt database. + + /* If after trying to extra new entries from the header, nHdrParsed is + ** still not up to p2, that means that the record has fewer than p2 + ** columns. So the result will be either the default value or a NULL. */ - if( (zIdx > zEndHdr) || (offset > payloadSize) - || (zIdx==zEndHdr && offset!=payloadSize) ){ - rc = SQLITE_CORRUPT_BKPT; + if( pC->nHdrParsed<=p2 ){ + if( pOp->p4type==P4_MEM ){ + sqlite3VdbeMemShallowCopy(pDest, pOp->p4.pMem, MEM_Static); + }else{ + MemSetTypeFlag(pDest, MEM_Null); + } goto op_column_out; } } - /* Get the column information. If aOffset[p2] is non-zero, then - ** deserialize the value from the record. If aOffset[p2] is zero, - ** then there are not enough fields in the record to satisfy the - ** request. In this case, set the value NULL or to P4 if P4 is - ** a pointer to a Mem object. + /* Extract the content for the p2+1-th column. Control can only + ** reach this point if aOffset[p2], aOffset[p2+1], and aType[p2] are + ** all valid. */ - if( aOffset[p2] ){ - assert( rc==SQLITE_OK ); - if( zRec ){ - /* This is the common case where the whole row fits on a single page */ - VdbeMemRelease(pDest); - sqlite3VdbeSerialGet((u8 *)&zRec[aOffset[p2]], aType[p2], pDest); + assert( p2<pC->nHdrParsed ); + assert( rc==SQLITE_OK ); + if( pC->szRow>=aOffset[p2+1] ){ + /* This is the common case where the desired content fits on the original + ** page - where the content is not on an overflow page */ + VdbeMemRelease(pDest); + sqlite3VdbeSerialGet(pC->aRow+aOffset[p2], aType[p2], pDest); + }else{ + /* This branch happens only when content is on overflow pages */ + t = aType[p2]; + if( ((pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0 + && ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0)) + || (len = sqlite3VdbeSerialTypeLen(t))==0 + ){ + /* Content is irrelevant for the typeof() function and for + ** the length(X) function if X is a blob. So we might as well use + ** bogus content rather than reading content from disk. NULL works + ** for text and blob and whatever is in the payloadSize64 variable + ** will work for everything else. Content is also irrelevant if + ** the content length is 0. */ + zData = t<=13 ? (u8*)&payloadSize64 : 0; + sMem.zMalloc = 0; }else{ - /* This branch happens only when the row overflows onto multiple pages */ - t = aType[p2]; - if( (pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0 - && ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0) - ){ - /* Content is irrelevant for the typeof() function and for - ** the length(X) function if X is a blob. So we might as well use - ** bogus content rather than reading content from disk. NULL works - ** for text and blob and whatever is in the payloadSize64 variable - ** will work for everything else. */ - zData = t<12 ? (char*)&payloadSize64 : 0; - }else{ - len = sqlite3VdbeSerialTypeLen(t); - sqlite3VdbeMemMove(&sMem, pDest); - rc = sqlite3VdbeMemFromBtree(pCrsr, aOffset[p2], len, pC->isIndex, - &sMem); - if( rc!=SQLITE_OK ){ - goto op_column_out; - } - zData = sMem.z; + memset(&sMem, 0, sizeof(sMem)); + sqlite3VdbeMemMove(&sMem, pDest); + rc = sqlite3VdbeMemFromBtree(pCrsr, aOffset[p2], len, !pC->isTable, + &sMem); + if( rc!=SQLITE_OK ){ + goto op_column_error; } - sqlite3VdbeSerialGet((u8*)zData, t, pDest); + zData = (u8*)sMem.z; } - pDest->enc = encoding; - }else{ - if( pOp->p4type==P4_MEM ){ - sqlite3VdbeMemShallowCopy(pDest, pOp->p4.pMem, MEM_Static); - }else{ - MemSetTypeFlag(pDest, MEM_Null); + sqlite3VdbeSerialGet(zData, t, pDest); + /* If we dynamically allocated space to hold the data (in the + ** sqlite3VdbeMemFromBtree() call above) then transfer control of that + ** dynamically allocated space over to the pDest structure. + ** This prevents a memory copy. */ + if( sMem.zMalloc ){ + assert( sMem.z==sMem.zMalloc ); + assert( !(pDest->flags & MEM_Dyn) ); + assert( !(pDest->flags & (MEM_Blob|MEM_Str)) || pDest->z==sMem.z ); + pDest->flags &= ~(MEM_Ephem|MEM_Static); + pDest->flags |= MEM_Term; + pDest->z = sMem.z; + pDest->zMalloc = sMem.zMalloc; } } - - /* If we dynamically allocated space to hold the data (in the - ** sqlite3VdbeMemFromBtree() call above) then transfer control of that - ** dynamically allocated space over to the pDest structure. - ** This prevents a memory copy. - */ - if( sMem.zMalloc ){ - assert( sMem.z==sMem.zMalloc ); - assert( !(pDest->flags & MEM_Dyn) ); - assert( !(pDest->flags & (MEM_Blob|MEM_Str)) || pDest->z==sMem.z ); - pDest->flags &= ~(MEM_Ephem|MEM_Static); - pDest->flags |= MEM_Term; - pDest->z = sMem.z; - pDest->zMalloc = sMem.zMalloc; - } - - rc = sqlite3VdbeMemMakeWriteable(pDest); + pDest->enc = encoding; op_column_out: + rc = sqlite3VdbeMemMakeWriteable(pDest); +op_column_error: UPDATE_MAX_BLOBSIZE(pDest); REGISTER_TRACE(pOp->p3, pDest); break; @@ -3320,6 +3263,8 @@ case OP_OpenWrite: { nField = pOp->p4.i; } assert( pOp->p1>=0 ); + assert( nField>=0 ); + testcase( nField==0 ); /* Table with INTEGER PRIMARY KEY and nothing else */ pCur = allocateCursor(p, pOp->p1, nField, iDb, 1); if( pCur==0 ) goto no_mem; pCur->nullRow = 1; @@ -3333,12 +3278,11 @@ case OP_OpenWrite: { ** sqlite3BtreeCursor() may return is SQLITE_OK. */ assert( rc==SQLITE_OK ); - /* Set the VdbeCursor.isTable and isIndex variables. Previous versions of + /* Set the VdbeCursor.isTable variable. Previous versions of ** SQLite used to check if the root-page flags were sane at this point ** and report database corruption if they were not, but this check has ** since moved into the btree layer. */ pCur->isTable = pOp->p4type!=P4_KEYINFO; - pCur->isIndex = !pCur->isTable; break; } @@ -3380,6 +3324,7 @@ case OP_OpenEphemeral: { SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_TRANSIENT_DB; assert( pOp->p1>=0 ); + assert( pOp->p2>=0 ); pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1); if( pCx==0 ) goto no_mem; pCx->nullRow = 1; @@ -3412,7 +3357,6 @@ case OP_OpenEphemeral: { } } pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); - pCx->isIndex = !pCx->isTable; break; } @@ -3425,12 +3369,13 @@ case OP_OpenEphemeral: { case OP_SorterOpen: { VdbeCursor *pCx; + assert( pOp->p1>=0 ); + assert( pOp->p2>=0 ); pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1); if( pCx==0 ) goto no_mem; pCx->pKeyInfo = pOp->p4.pKeyInfo; assert( pCx->pKeyInfo->db==db ); assert( pCx->pKeyInfo->enc==ENC(db) ); - pCx->isSorter = 1; rc = sqlite3VdbeSorterInit(db, pCx); break; } @@ -3456,12 +3401,12 @@ case OP_OpenPseudo: { VdbeCursor *pCx; assert( pOp->p1>=0 ); + assert( pOp->p3>=0 ); pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, 0); if( pCx==0 ) goto no_mem; pCx->nullRow = 1; pCx->pseudoTableReg = pOp->p2; pCx->isTable = 1; - pCx->isIndex = 0; pCx->multiPseudo = pOp->p5; break; } @@ -3579,7 +3524,9 @@ case OP_SeekGt: { /* jump, in3 */ ** point number. */ assert( (pIn3->flags & MEM_Real)!=0 ); - if( iKey==SMALLEST_INT64 && (pIn3->r<(double)iKey || pIn3->r>0) ){ + if( (iKey==SMALLEST_INT64 && pIn3->r<(double)iKey) + || (iKey==LARGEST_INT64 && pIn3->r>(double)iKey) + ){ /* The P3 value is too large in magnitude to be expressed as an ** integer. */ res = 1; @@ -4159,7 +4106,7 @@ case OP_InsertInt: { sqlite3BtreeSetCachedRowid(pC->pCursor, 0); rc = sqlite3BtreeInsert(pC->pCursor, 0, iKey, pData->z, pData->n, nZero, - pOp->p5 & OPFLAG_APPEND, seekResult + (pOp->p5 & OPFLAG_APPEND)!=0, seekResult ); pC->rowidIsValid = 0; pC->deferredMoveto = 0; @@ -4319,7 +4266,7 @@ case OP_SorterData: { pOut = &aMem[pOp->p2]; pC = p->apCsr[pOp->p1]; - assert( pC->isSorter ); + assert( isSorter(pC) ); rc = sqlite3VdbeSorterRowkey(pC, pOut); break; } @@ -4359,9 +4306,9 @@ case OP_RowData: { /* Note that RowKey and RowData are really exactly the same instruction */ assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; - assert( pC->isSorter==0 ); + assert( isSorter(pC)==0 ); assert( pC->isTable || pOp->opcode!=OP_RowData ); - assert( pC->isIndex || pOp->opcode==OP_RowData ); + assert( pC->isTable==0 || pOp->opcode==OP_RowData ); assert( pC!=0 ); assert( pC->nullRow==0 ); assert( pC->pseudoTableReg==0 ); @@ -4378,7 +4325,7 @@ case OP_RowData: { rc = sqlite3VdbeCursorMoveto(pC); if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error; - if( pC->isIndex ){ + if( pC->isTable==0 ){ assert( !pC->isTable ); VVA_ONLY(rc =) sqlite3BtreeKeySize(pCrsr, &n64); assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */ @@ -4398,7 +4345,7 @@ case OP_RowData: { } pOut->n = n; MemSetTypeFlag(pOut, MEM_Blob); - if( pC->isIndex ){ + if( pC->isTable==0 ){ rc = sqlite3BtreeKey(pCrsr, 0, n, pOut->z); }else{ rc = sqlite3BtreeData(pCrsr, 0, n, pOut->z); @@ -4471,6 +4418,7 @@ case OP_NullRow: { assert( pC!=0 ); pC->nullRow = 1; pC->rowidIsValid = 0; + pC->cacheStatus = CACHE_STALE; assert( pC->pCursor || pC->pVtabCursor ); if( pC->pCursor ){ sqlite3BtreeClearCursor(pC->pCursor); @@ -4546,7 +4494,7 @@ case OP_Rewind: { /* jump */ assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); - assert( pC->isSorter==(pOp->opcode==OP_SorterSort) ); + assert( isSorter(pC)==(pOp->opcode==OP_SorterSort) ); res = 1; if( isSorter(pC) ){ rc = sqlite3VdbeSorterRewind(db, pC, &res); @@ -4554,7 +4502,6 @@ case OP_Rewind: { /* jump */ pCrsr = pC->pCursor; assert( pCrsr ); rc = sqlite3BtreeFirst(pCrsr, &res); - pC->atFirst = res==0 ?1:0; pC->deferredMoveto = 0; pC->cacheStatus = CACHE_STALE; pC->rowidIsValid = 0; @@ -4574,7 +4521,8 @@ case OP_Rewind: { /* jump */ ** to the following instruction. But if the cursor advance was successful, ** jump immediately to P2. ** -** The P1 cursor must be for a real table, not a pseudo-table. +** The P1 cursor must be for a real table, not a pseudo-table. P1 must have +** been opened prior to this opcode or the program will segfault. ** ** P4 is always of type P4_ADVANCE. The function pointer points to ** sqlite3BtreeNext(). @@ -4582,7 +4530,12 @@ case OP_Rewind: { /* jump */ ** If P5 is positive and the jump is taken, then event counter ** number P5-1 in the prepared statement is incremented. ** -** See also: Prev +** See also: Prev, NextIfOpen +*/ +/* Opcode: NextIfOpen P1 P2 * * P5 +** +** This opcode works just like OP_Next except that if cursor P1 is not +** open it behaves a no-op. */ /* Opcode: Prev P1 P2 * * P5 ** @@ -4591,7 +4544,8 @@ case OP_Rewind: { /* jump */ ** to the following instruction. But if the cursor backup was successful, ** jump immediately to P2. ** -** The P1 cursor must be for a real table, not a pseudo-table. +** The P1 cursor must be for a real table, not a pseudo-table. If P1 is +** not open then the behavior is undefined. ** ** P4 is always of type P4_ADVANCE. The function pointer points to ** sqlite3BtreePrevious(). @@ -4599,38 +4553,47 @@ case OP_Rewind: { /* jump */ ** If P5 is positive and the jump is taken, then event counter ** number P5-1 in the prepared statement is incremented. */ -case OP_SorterNext: /* jump */ -case OP_Prev: /* jump */ -case OP_Next: { /* jump */ +/* Opcode: PrevIfOpen P1 P2 * * P5 +** +** This opcode works just like OP_Prev except that if cursor P1 is not +** open it behaves a no-op. +*/ +case OP_SorterNext: { /* jump */ VdbeCursor *pC; int res; + pC = p->apCsr[pOp->p1]; + assert( isSorter(pC) ); + rc = sqlite3VdbeSorterNext(db, pC, &res); + goto next_tail; +case OP_PrevIfOpen: /* jump */ +case OP_NextIfOpen: /* jump */ + if( p->apCsr[pOp->p1]==0 ) break; + /* Fall through */ +case OP_Prev: /* jump */ +case OP_Next: /* jump */ assert( pOp->p1>=0 && pOp->p1<p->nCursor ); assert( pOp->p5<ArraySize(p->aCounter) ); pC = p->apCsr[pOp->p1]; - if( pC==0 ){ - break; /* See ticket #2273 */ - } - assert( pC->isSorter==(pOp->opcode==OP_SorterNext) ); - if( isSorter(pC) ){ - assert( pOp->opcode==OP_SorterNext ); - rc = sqlite3VdbeSorterNext(db, pC, &res); - }else{ - /* res = 1; // Always initialized by the xAdvance() call */ - assert( pC->deferredMoveto==0 ); - assert( pC->pCursor ); - assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite3BtreeNext ); - assert( pOp->opcode!=OP_Prev || pOp->p4.xAdvance==sqlite3BtreePrevious ); - rc = pOp->p4.xAdvance(pC->pCursor, &res); - } - pC->nullRow = (u8)res; + assert( pC!=0 ); + assert( pC->deferredMoveto==0 ); + assert( pC->pCursor ); + assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite3BtreeNext ); + assert( pOp->opcode!=OP_Prev || pOp->p4.xAdvance==sqlite3BtreePrevious ); + assert( pOp->opcode!=OP_NextIfOpen || pOp->p4.xAdvance==sqlite3BtreeNext ); + assert( pOp->opcode!=OP_PrevIfOpen || pOp->p4.xAdvance==sqlite3BtreePrevious); + rc = pOp->p4.xAdvance(pC->pCursor, &res); +next_tail: pC->cacheStatus = CACHE_STALE; if( res==0 ){ + pC->nullRow = 0; pc = pOp->p2 - 1; p->aCounter[pOp->p5]++; #ifdef SQLITE_TEST sqlite3_search_count++; #endif + }else{ + pC->nullRow = 1; } pC->rowidIsValid = 0; goto check_for_interrupt; @@ -4659,7 +4622,7 @@ case OP_IdxInsert: { /* in2 */ assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); - assert( pC->isSorter==(pOp->opcode==OP_SorterInsert) ); + assert( isSorter(pC)==(pOp->opcode==OP_SorterInsert) ); pIn2 = &aMem[pOp->p2]; assert( pIn2->flags & MEM_Blob ); pCrsr = pC->pCursor; @@ -5910,7 +5873,6 @@ case OP_VOpen: { pCur = allocateCursor(p, pOp->p1, 0, -1, 0); if( pCur ){ pCur->pVtabCursor = pVtabCursor; - pCur->pModule = pVtabCursor->pVtab->pModule; }else{ db->mallocFailed = 1; pModule->xClose(pVtabCursor); diff --git a/src/vdbeInt.h b/src/vdbeInt.h index bcd04e9d4..cf1041887 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -36,7 +36,7 @@ typedef struct VdbeOp Op; /* ** Boolean values */ -typedef unsigned char Bool; +typedef unsigned Bool; /* Opaque type used by code in vdbesort.c */ typedef struct VdbeSorter VdbeSorter; @@ -53,6 +53,9 @@ typedef struct AuxData AuxData; ** loop over all entries of the Btree. You can also insert new BTree ** entries or retrieve the key or data from the entry that the cursor ** is currently pointing to. +** +** Cursors can also point to virtual tables, sorters, or "pseudo-tables". +** A pseudo-table is a single-row table implemented by registers. ** ** Every cursor that the virtual machine has open is represented by an ** instance of the following structure. @@ -61,30 +64,24 @@ struct VdbeCursor { BtCursor *pCursor; /* The cursor structure of the backend */ Btree *pBt; /* Separate file holding temporary table */ KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */ - int iDb; /* Index of cursor database in db->aDb[] (or -1) */ + int seekResult; /* Result of previous sqlite3BtreeMoveto() */ int pseudoTableReg; /* Register holding pseudotable content. */ - int nField; /* Number of fields in the header */ - Bool zeroed; /* True if zeroed out and ready for reuse */ - Bool rowidIsValid; /* True if lastRowid is valid */ - Bool atFirst; /* True if pointing to first entry */ - Bool useRandomRowid; /* Generate new record numbers semi-randomly */ - Bool nullRow; /* True if pointing to a row with no data */ - Bool deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */ - Bool isTable; /* True if a table requiring integer keys */ - Bool isIndex; /* True if an index containing keys only - no data */ - Bool isOrdered; /* True if the underlying table is BTREE_UNORDERED */ - Bool isSorter; /* True if a new-style sorter */ - Bool multiPseudo; /* Multi-register pseudo-cursor */ + i16 nField; /* Number of fields in the header */ + u16 nHdrParsed; /* Number of header fields parsed so far */ + i8 iDb; /* Index of cursor database in db->aDb[] (or -1) */ + u8 nullRow; /* True if pointing to a row with no data */ + u8 rowidIsValid; /* True if lastRowid is valid */ + u8 deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */ + Bool useRandomRowid:1;/* Generate new record numbers semi-randomly */ + Bool isTable:1; /* True if a table requiring integer keys */ + Bool isOrdered:1; /* True if the underlying table is BTREE_UNORDERED */ + Bool multiPseudo:1; /* Multi-register pseudo-cursor */ sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */ - const sqlite3_module *pModule; /* Module for cursor pVtabCursor */ i64 seqCount; /* Sequence counter */ i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */ - i64 lastRowid; /* Last rowid from a Next or NextIdx operation */ + i64 lastRowid; /* Rowid being deleted by OP_Delete */ VdbeSorter *pSorter; /* Sorter object for OP_SorterOpen cursors */ - /* Result of last sqlite3BtreeMoveto() done by an OP_NotExists */ - int seekResult; - /* Cached information about the header for the data record that the ** cursor is currently pointing to. Only valid if cacheStatus matches ** Vdbe.cacheCtr. Vdbe.cacheCtr will never take on the value of @@ -95,10 +92,14 @@ struct VdbeCursor { ** be NULL. */ u32 cacheStatus; /* Cache is valid if this matches Vdbe.cacheCtr */ - int payloadSize; /* Total number of bytes in the record */ - u32 *aType; /* Type values for all entries in the record */ - u32 *aOffset; /* Cached offsets to the start of each columns data */ - u8 *aRow; /* Data for the current row, if all on one page */ + u32 payloadSize; /* Total number of bytes in the record */ + u32 szRow; /* Byte available in aRow */ + u32 iHdrOffset; /* Offset to next unparsed byte of the header */ + const u8 *aRow; /* Data for the current row, if all on one page */ + u32 aType[1]; /* Type values for all entries in the record */ + /* 2*nField extra array elements allocated for aType[], beyond the one + ** static element declared in the structure. nField total array slots for + ** aType[] and nField+1 array slots for aOffset[] */ }; typedef struct VdbeCursor VdbeCursor; @@ -442,7 +443,7 @@ double sqlite3VdbeRealValue(Mem*); void sqlite3VdbeIntegerAffinity(Mem*); int sqlite3VdbeMemRealify(Mem*); int sqlite3VdbeMemNumerify(Mem*); -int sqlite3VdbeMemFromBtree(BtCursor*,int,int,int,Mem*); +int sqlite3VdbeMemFromBtree(BtCursor*,u32,u32,int,Mem*); void sqlite3VdbeMemRelease(Mem *p); void sqlite3VdbeMemReleaseExternal(Mem *p); #define VdbeMemRelease(X) \ diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 14c74dd0c..65aacf7e3 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -454,12 +454,14 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ } #endif case OP_Next: + case OP_NextIfOpen: case OP_SorterNext: { pOp->p4.xAdvance = sqlite3BtreeNext; pOp->p4type = P4_ADVANCE; break; } - case OP_Prev: { + case OP_Prev: + case OP_PrevIfOpen: { pOp->p4.xAdvance = sqlite3BtreePrevious; pOp->p4type = P4_ADVANCE; break; @@ -1675,7 +1677,7 @@ void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){ #ifndef SQLITE_OMIT_VIRTUALTABLE if( pCx->pVtabCursor ){ sqlite3_vtab_cursor *pVtabCursor = pCx->pVtabCursor; - const sqlite3_module *pModule = pCx->pModule; + const sqlite3_module *pModule = pVtabCursor->pVtab->pModule; p->inVtabMethod = 1; pModule->xClose(pVtabCursor); p->inVtabMethod = 0; @@ -2659,7 +2661,7 @@ int sqlite3VdbeCursorMoveto(VdbeCursor *p){ #endif p->deferredMoveto = 0; p->cacheStatus = CACHE_STALE; - }else if( ALWAYS(p->pCursor) ){ + }else if( p->pCursor ){ int hasMoved; int rc = sqlite3BtreeCursorHasMoved(p->pCursor, &hasMoved); if( rc ) return rc; @@ -2967,15 +2969,12 @@ u32 sqlite3VdbeSerialGet( return 0; } default: { + static const u16 aFlag[] = { MEM_Blob|MEM_Ephem, MEM_Str|MEM_Ephem }; u32 len = (serial_type-12)/2; pMem->z = (char *)buf; pMem->n = len; pMem->xDel = 0; - if( serial_type&0x01 ){ - pMem->flags = MEM_Str | MEM_Ephem; - }else{ - pMem->flags = MEM_Blob | MEM_Ephem; - } + pMem->flags = aFlag[serial_type&1]; return len; } } @@ -3117,7 +3116,8 @@ int sqlite3VdbeRecordCompare( d1 = szHdr1; assert( pKeyInfo->nField+pKeyInfo->nXField>=pPKey2->nField ); assert( pKeyInfo->aSortOrder!=0 ); - while( idx1<szHdr1 && i<pPKey2->nField ){ + assert( idx1<szHdr1 && i<pPKey2->nField ); + do{ u32 serial_type1; /* Read the serial types for the next element in each key. */ @@ -3150,7 +3150,7 @@ int sqlite3VdbeRecordCompare( return rc; } i++; - } + }while( idx1<szHdr1 && i<pPKey2->nField ); /* No memory allocation is ever used on mem1. Prove this using ** the following assert(). If the assert() fails, it indicates a @@ -3208,7 +3208,7 @@ int sqlite3VdbeIdxRowid(sqlite3 *db, BtCursor *pCur, i64 *rowid){ /* Read in the complete content of the index entry */ memset(&m, 0, sizeof(m)); - rc = sqlite3VdbeMemFromBtree(pCur, 0, (int)nCellKey, 1, &m); + rc = sqlite3VdbeMemFromBtree(pCur, 0, (u32)nCellKey, 1, &m); if( rc ){ return rc; } @@ -3286,7 +3286,7 @@ int sqlite3VdbeIdxKeyCompare( return SQLITE_CORRUPT_BKPT; } memset(&m, 0, sizeof(m)); - rc = sqlite3VdbeMemFromBtree(pC->pCursor, 0, (int)nCellKey, 1, &m); + rc = sqlite3VdbeMemFromBtree(pC->pCursor, 0, (u32)nCellKey, 1, &m); if( rc ){ return rc; } diff --git a/src/vdbeblob.c b/src/vdbeblob.c index ee660603e..a1c44ebf3 100644 --- a/src/vdbeblob.c +++ b/src/vdbeblob.c @@ -66,7 +66,8 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){ rc = sqlite3_step(p->pStmt); if( rc==SQLITE_ROW ){ - u32 type = v->apCsr[0]->aType[p->iCol]; + VdbeCursor *pC = v->apCsr[0]; + u32 type = pC->aType[p->iCol]; if( type<12 ){ zErr = sqlite3MPrintf(p->db, "cannot open value of type %s", type==0?"null": type==7?"real": "integer" @@ -75,9 +76,9 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){ sqlite3_finalize(p->pStmt); p->pStmt = 0; }else{ - p->iOffset = v->apCsr[0]->aOffset[p->iCol]; + p->iOffset = pC->aType[p->iCol + pC->nField]; p->nByte = sqlite3VdbeSerialTypeLen(type); - p->pCsr = v->apCsr[0]->pCursor; + p->pCsr = pC->pCursor; sqlite3BtreeEnterCursor(p->pCsr); sqlite3BtreeCacheOverflow(p->pCsr); sqlite3BtreeLeaveCursor(p->pCsr); @@ -332,6 +333,7 @@ blob_open_out: } sqlite3Error(db, rc, (zErr ? "%s" : 0), zErr); sqlite3DbFree(db, zErr); + sqlite3ParserReset(pParse); sqlite3StackFree(db, pParse); rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(db->mutex); diff --git a/src/vdbemem.c b/src/vdbemem.c index c4bae54fd..0fe7a3bcb 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -303,15 +303,8 @@ void sqlite3VdbeMemRelease(Mem *p){ /* ** Convert a 64-bit IEEE double into a 64-bit signed integer. -** If the double is too large, return 0x8000000000000000. -** -** Most systems appear to do this simply by assigning -** variables and without the extra range tests. But -** there are reports that windows throws an expection -** if the floating point value is out of range. (See ticket #2880.) -** Because we do not completely understand the problem, we will -** take the conservative approach and always do range tests -** before attempting the conversion. +** If the double is out of range of a 64-bit signed integer then +** return the closest available 64-bit signed integer. */ static i64 doubleToInt64(double r){ #ifdef SQLITE_OMIT_FLOATING_POINT @@ -328,14 +321,10 @@ static i64 doubleToInt64(double r){ static const i64 maxInt = LARGEST_INT64; static const i64 minInt = SMALLEST_INT64; - if( r<(double)minInt ){ - return minInt; - }else if( r>(double)maxInt ){ - /* minInt is correct here - not maxInt. It turns out that assigning - ** a very large positive number to an integer results in a very large - ** negative integer. This makes no sense, but it is what x86 hardware - ** does so for compatibility we will do the same in software. */ + if( r<=(double)minInt ){ return minInt; + }else if( r>=(double)maxInt ){ + return maxInt; }else{ return (i64)r; } @@ -417,17 +406,11 @@ void sqlite3VdbeIntegerAffinity(Mem *pMem){ ** ** The second and third terms in the following conditional enforces ** the second condition under the assumption that addition overflow causes - ** values to wrap around. On x86 hardware, the third term is always - ** true and could be omitted. But we leave it in because other - ** architectures might behave differently. + ** values to wrap around. */ if( pMem->r==(double)pMem->u.i && pMem->u.i>SMALLEST_INT64 -#if defined(__i486__) || defined(__x86_64__) - && ALWAYS(pMem->u.i<LARGEST_INT64) -#else && pMem->u.i<LARGEST_INT64 -#endif ){ pMem->flags |= MEM_Int; } @@ -896,13 +879,13 @@ int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const CollSeq *pColl){ */ int sqlite3VdbeMemFromBtree( BtCursor *pCur, /* Cursor pointing at record to retrieve. */ - int offset, /* Offset from the start of data to return bytes from. */ - int amt, /* Number of bytes to return. */ + u32 offset, /* Offset from the start of data to return bytes from. */ + u32 amt, /* Number of bytes to return. */ int key, /* If true, retrieve from the btree key, not data. */ Mem *pMem /* OUT: Return data in this Mem structure. */ ){ char *zData; /* Data from the btree layer */ - int available = 0; /* Number of bytes available on the local btree page */ + u32 available = 0; /* Number of bytes available on the local btree page */ int rc = SQLITE_OK; /* Return code */ assert( sqlite3BtreeCursorIsValid(pCur) ); @@ -917,7 +900,7 @@ int sqlite3VdbeMemFromBtree( } assert( zData!=0 ); - if( offset+amt<=available && (pMem->flags&MEM_Dyn)==0 ){ + if( offset+amt<=available ){ sqlite3VdbeMemRelease(pMem); pMem->z = &zData[offset]; pMem->flags = MEM_Blob|MEM_Ephem; @@ -936,7 +919,7 @@ int sqlite3VdbeMemFromBtree( sqlite3VdbeMemRelease(pMem); } } - pMem->n = amt; + pMem->n = (int)amt; return rc; } diff --git a/src/vtab.c b/src/vtab.c index 195aa68b2..ca0db214c 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -738,6 +738,7 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ sqlite3VdbeFinalize(pParse->pVdbe); } sqlite3DeleteTable(db, pParse->pNewTable); + sqlite3ParserReset(pParse); sqlite3StackFree(db, pParse); } diff --git a/src/where.c b/src/where.c index c4f25675c..101ca1a7c 100644 --- a/src/where.c +++ b/src/where.c @@ -669,9 +669,6 @@ static int isLikeOrGlob( pRight = pList->a[0].pExpr; op = pRight->op; - if( op==TK_REGISTER ){ - op = pRight->op2; - } if( op==TK_VARIABLE ){ Vdbe *pReprepare = pParse->pReprepare; int iCol = pRight->iColumn; @@ -2409,7 +2406,7 @@ static int codeEqualityTerm( }else{ pIn->addrInTop = sqlite3VdbeAddOp3(v, OP_Column, iTab, 0, iReg); } - pIn->eEndLoopOp = bRev ? OP_Prev : OP_Next; + pIn->eEndLoopOp = bRev ? OP_PrevIfOpen : OP_NextIfOpen; sqlite3VdbeAddOp1(v, OP_IsNull, iReg); }else{ pLevel->u.in.nIn = 0; @@ -2759,7 +2756,7 @@ static Bitmask codeOneLoopStart( bRev = (pWInfo->revMask>>iLevel)&1; omitTable = (pLoop->wsFlags & WHERE_IDX_ONLY)!=0 && (pWInfo->wctrlFlags & WHERE_FORCE_TABLE)==0; - VdbeNoopComment((v, "Begin WHERE-loop%d: %s",iLevel,pTabItem->pTab->zName)); + VdbeModuleComment((v, "Begin WHERE-loop%d: %s",iLevel,pTabItem->pTab->zName)); /* Create labels for the "break" and "continue" instructions ** for the current loop. Jump to addrBrk to break out of a loop. @@ -2987,7 +2984,6 @@ static Bitmask codeOneLoopStart( OP_IdxLT /* 2: (end_constraints && bRev) */ }; u16 nEq = pLoop->u.btree.nEq; /* Number of == or IN terms */ - u16 nSkip = pLoop->u.btree.nSkip; /* Number of left index terms to skip */ int isMinQuery = 0; /* If this is an optimized SELECT min(x).. */ int regBase; /* Base register holding constraint values */ int r1; /* Temp register */ @@ -3002,11 +2998,11 @@ static Bitmask codeOneLoopStart( int nExtraReg = 0; /* Number of extra registers needed */ int op; /* Instruction opcode */ char *zStartAff; /* Affinity for start of range constraint */ - char *zEndAff; /* Affinity for end of range constraint */ + char cEndAff = 0; /* Affinity for end of range constraint */ pIdx = pLoop->u.btree.pIndex; iIdxCur = pLevel->iIdxCur; - assert( nEq>=nSkip ); + assert( nEq>=pLoop->u.btree.nSkip ); /* If this loop satisfies a sort order (pOrderBy) request that ** was passed to this function to implement a "SELECT min(x) ..." @@ -3020,7 +3016,7 @@ static Bitmask codeOneLoopStart( && (pWInfo->bOBSat!=0) && (pIdx->nKeyCol>nEq) ){ - assert( nSkip==0 ); + assert( pLoop->u.btree.nSkip==0 ); isMinQuery = 1; nExtraReg = 1; } @@ -3043,7 +3039,8 @@ static Bitmask codeOneLoopStart( ** starting at regBase. */ regBase = codeAllEqualityTerms(pParse,pLevel,bRev,nExtraReg,&zStartAff); - zEndAff = sqlite3DbStrDup(db, zStartAff); + assert( zStartAff==0 || sqlite3Strlen30(zStartAff)>=nEq ); + if( zStartAff ) cEndAff = zStartAff[nEq]; addrNxt = pLevel->addrNxt; /* If we are doing a reverse order scan on an ascending index, or @@ -3113,23 +3110,15 @@ static Bitmask codeOneLoopStart( if( (pRangeEnd->wtFlags & TERM_VNULL)==0 ){ sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt); } - if( zEndAff ){ - if( sqlite3CompareAffinity(pRight, zEndAff[nEq])==SQLITE_AFF_NONE){ - /* Since the comparison is to be performed with no conversions - ** applied to the operands, set the affinity to apply to pRight to - ** SQLITE_AFF_NONE. */ - zEndAff[nEq] = SQLITE_AFF_NONE; - } - if( sqlite3ExprNeedsNoAffinityChange(pRight, zEndAff[nEq]) ){ - zEndAff[nEq] = SQLITE_AFF_NONE; - } - } - codeApplyAffinity(pParse, regBase, nEq+1, zEndAff); + if( sqlite3CompareAffinity(pRight, cEndAff)!=SQLITE_AFF_NONE + && !sqlite3ExprNeedsNoAffinityChange(pRight, cEndAff) + ){ + codeApplyAffinity(pParse, regBase+nEq, 1, &cEndAff); + } nConstraint++; testcase( pRangeEnd->wtFlags & TERM_VIRTUAL ); } sqlite3DbFree(db, zStartAff); - sqlite3DbFree(db, zEndAff); /* Top of the loop body */ pLevel->p2 = sqlite3VdbeCurrentAddr(v); @@ -3154,6 +3143,7 @@ static Bitmask codeOneLoopStart( if( (pLoop->wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 && (j = pIdx->aiColumn[nEq])>=0 && pIdx->pTable->aCol[j].notNull==0 + && (nEq || (pLoop->wsFlags & WHERE_BTM_LIMIT)==0) ){ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, nEq, r1); VdbeComment((v, "%s", pIdx->pTable->aCol[j].zName)); @@ -3471,7 +3461,7 @@ static Bitmask codeOneLoopStart( if( pAlt->wtFlags & (TERM_CODED) ) continue; testcase( pAlt->eOperator & WO_EQ ); testcase( pAlt->eOperator & WO_IN ); - VdbeNoopComment((v, "begin transitive constraint")); + VdbeModuleComment((v, "begin transitive constraint")); pEAlt = sqlite3StackAllocRaw(db, sizeof(*pEAlt)); if( pEAlt ){ *pEAlt = *pAlt->pExpr; @@ -3928,10 +3918,15 @@ static int whereLoopAddBtreeIndex( saved_nOut = pNew->nOut; pNew->rSetup = 0; rLogSize = estLog(sqlite3LogEst(pProbe->aiRowEst[0])); + + /* Consider using a skip-scan if there are no WHERE clause constraints + ** available for the left-most terms of the index, and if the average + ** number of repeats in the left-most terms is at least 50. + */ if( pTerm==0 && saved_nEq==saved_nSkip && saved_nEq+1<pProbe->nKeyCol - && pProbe->aiRowEst[saved_nEq+1]>50 + && pProbe->aiRowEst[saved_nEq+1]>50 /* TUNING: Minimum for skip-scan */ ){ LogEst nIter; pNew->u.btree.nEq++; @@ -5427,7 +5422,6 @@ WhereInfo *sqlite3WhereBegin( */ initMaskSet(pMaskSet); whereClauseInit(&pWInfo->sWC, pWInfo); - sqlite3ExprCodeConstants(pParse, pWhere); whereSplit(&pWInfo->sWC, pWhere, TK_AND); sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */ @@ -5742,7 +5736,7 @@ WhereInfo *sqlite3WhereBegin( } /* Done. */ - VdbeNoopComment((v, "Begin WHERE-core")); + VdbeModuleComment((v, "Begin WHERE-core")); return pWInfo; /* Jump here if malloc fails */ @@ -5769,7 +5763,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ /* Generate loop termination code. */ - VdbeNoopComment((v, "End WHERE-core")); + VdbeModuleComment((v, "End WHERE-core")); sqlite3ExprCacheClear(pParse); for(i=pWInfo->nLevel-1; i>=0; i--){ int addr; @@ -5815,7 +5809,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ } sqlite3VdbeJumpHere(v, addr); } - VdbeNoopComment((v, "End WHERE-loop%d: %s", i, + VdbeModuleComment((v, "End WHERE-loop%d: %s", i, pWInfo->pTabList->a[pLevel->iFrom].pTab->zName)); } diff --git a/test/analyze9.test b/test/analyze9.test index aaf0ba3bb..0fce55ac4 100644 --- a/test/analyze9.test +++ b/test/analyze9.test @@ -805,8 +805,9 @@ do_test 16.1 { ANALYZE; } set nByte2 [lindex [sqlite3_db_status db SCHEMA_USED 0] 1] + puts -nonewline " (nByte=$nByte nByte2=$nByte2)" - expr {$nByte2 > $nByte+900 && $nByte2 < $nByte+1050} + expr {$nByte2 > $nByte+900 && $nByte2 < $nByte+1100} } {1} #------------------------------------------------------------------------- diff --git a/test/atof1.test b/test/atof1.test index 76eb4273b..5c04d0229 100644 --- a/test/atof1.test +++ b/test/atof1.test @@ -15,7 +15,7 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl -if {![info exists __GNUC__]} { +if {![info exists __GNUC__] || [regexp arm $tcl_platform(machine)]} { finish_test return } diff --git a/test/autoinc.test b/test/autoinc.test index 98f6919b0..239600616 100644 --- a/test/autoinc.test +++ b/test/autoinc.test @@ -216,7 +216,7 @@ do_test autoinc-2.27 { } {t1 1238} do_test autoinc-2.28 { execsql { - UPDATE sqlite_sequence SET seq='12345678901234567890' + UPDATE sqlite_sequence SET seq='-12345678901234567890' WHERE name='t1'; INSERT INTO t1 VALUES(NULL,6); SELECT * FROM t1; diff --git a/test/default.test b/test/default.test index 95a4ee039..d6b6f97d9 100644 --- a/test/default.test +++ b/test/default.test @@ -64,4 +64,39 @@ ifcapable pragma { } {0 c {} 0 'abc' 0} } +do_execsql_test default-3.1 { + CREATE TABLE t3( + a INTEGER PRIMARY KEY AUTOINCREMENT, + b INT DEFAULT 12345 UNIQUE NOT NULL CHECK( b>=0 AND b<99999 ), + c VARCHAR(123,456) DEFAULT 'hello' NOT NULL ON CONFLICT REPLACE, + d REAL, + e FLOATING POINT(5,10) DEFAULT 4.36, + f NATIONAL CHARACTER(15) COLLATE RTRIM, + g LONG INTEGER DEFAULT( 3600*12 ) + ); + INSERT INTO t3 VALUES(null, 5, 'row1', '5.25', 'xyz', 321, '432'); + SELECT a, typeof(a), b, typeof(b), c, typeof(c), + d, typeof(d), e, typeof(e), f, typeof(f), + g, typeof(g) FROM t3; +} {1 integer 5 integer row1 text 5.25 real xyz text 321 text 432 integer} +do_execsql_test default-3.2 { + DELETE FROM t3; + INSERT INTO t3 DEFAULT VALUES; + SELECT * FROM t3; +} {2 12345 hello {} 4.36 {} 43200} +do_execsql_test default-3.3 { + CREATE TABLE t300( + a INT DEFAULT 2147483647, + b INT DEFAULT 2147483648, + c INT DEFAULT +9223372036854775807, + d INT DEFAULT -2147483647, + e INT DEFAULT -2147483648, + f INT DEFAULT -9223372036854775808, + g INT DEFAULT (-(-9223372036854775808)), + h INT DEFAULT (-(-9223372036854775807)) + ); + INSERT INTO t300 DEFAULT VALUES; + SELECT * FROM t300; +} {2147483647 2147483648 9223372036854775807 -2147483647 -2147483648 -9223372036854775808 9.22337203685478e+18 9223372036854775807} + finish_test diff --git a/test/e_expr.test b/test/e_expr.test index f0705757a..eead6a2b9 100644 --- a/test/e_expr.test +++ b/test/e_expr.test @@ -1081,9 +1081,9 @@ ifcapable !icu { # EVIDENCE-OF: R-33693-50180 The REGEXP operator is a special syntax for # the regexp() user function. # -# EVIDENCE-OF: R-57289-13578 If a application-defined SQL function named -# "regexp" is added at run-time, that function will be called in order -# to implement the REGEXP operator. +# EVIDENCE-OF: R-65524-61849 If an application-defined SQL function +# named "regexp" is added at run-time, then the "X REGEXP Y" operator +# will be implemented as a call to "regexp(Y,X)". # proc regexpfunc {args} { eval lappend ::regexpargs $args @@ -1606,14 +1606,14 @@ do_expr_test e_expr-31.1.4 { CAST(-0.99999 AS INTEGER) } integer 0 # an INTEGER then the result of the cast is the largest negative # integer: -9223372036854775808. # -do_expr_test e_expr-31.2.1 { CAST(2e+50 AS INT) } integer -9223372036854775808 +do_expr_test e_expr-31.2.1 { CAST(2e+50 AS INT) } integer 9223372036854775807 do_expr_test e_expr-31.2.2 { CAST(-2e+50 AS INT) } integer -9223372036854775808 do_expr_test e_expr-31.2.3 { CAST(-9223372036854775809.0 AS INT) } integer -9223372036854775808 do_expr_test e_expr-31.2.4 { CAST(9223372036854775809.0 AS INT) -} integer -9223372036854775808 +} integer 9223372036854775807 # EVIDENCE-OF: R-09295-61337 Casting a TEXT or BLOB value into NUMERIC diff --git a/test/func.test b/test/func.test index 7c7d55e1b..edec591ea 100644 --- a/test/func.test +++ b/test/func.test @@ -1319,6 +1319,24 @@ do_test func-29.6 { set x } {1} +# The OP_Column opcode has an optimization that avoids loading content +# for fields with content-length=0 when the content offset is on an overflow +# page. Make sure the optimization works. +# +do_execsql_test func-29.10 { + CREATE TABLE t29b(a,b,c,d,e,f,g,h,i); + INSERT INTO t29b + VALUES(1, hex(randomblob(2000)), null, 0, 1, '', zeroblob(0),'x',x'01'); + SELECT typeof(c), typeof(d), typeof(e), typeof(f), + typeof(g), typeof(h), typeof(i) FROM t29b; +} {null integer integer text blob text blob} +do_execsql_test func-29.11 { + SELECT length(f), length(g), length(h), length(i) FROM t29b; +} {0 0 1 1} +do_execsql_test func-29.12 { + SELECT quote(f), quote(g), quote(h), quote(i) FROM t29b; +} {'' X'' 'x' X'01'} + # EVIDENCE-OF: R-29701-50711 The unicode(X) function returns the numeric # unicode code point corresponding to the first character of the string # X. diff --git a/test/func5.test b/test/func5.test new file mode 100644 index 000000000..83ecb785d --- /dev/null +++ b/test/func5.test @@ -0,0 +1,33 @@ +# 2013-11-21 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#************************************************************************* +# +# Verify that constant string expressions that get factored into initializing +# code are not reused between function parameters and other values in the +# VDBE program, as the function might have changed the encoding. +# +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_execsql_test func5-1.1 { + PRAGMA encoding=UTF16le; + CREATE TABLE t1(x,a,b,c); + INSERT INTO t1 VALUES(1,'ab','cd',1); + INSERT INTO t1 VALUES(2,'gh','ef',5); + INSERT INTO t1 VALUES(3,'pqr','fuzzy',99); + INSERT INTO t1 VALUES(4,'abcdefg','xy',22); + INSERT INTO t1 VALUES(5,'shoe','mayer',2953); + SELECT x FROM t1 WHERE c=instr('abcdefg',b) OR a='abcdefg' ORDER BY +x; +} {2 4} +do_execsql_test func5-1.2 { + SELECT x FROM t1 WHERE a='abcdefg' OR c=instr('abcdefg',b) ORDER BY +x; +} {2 4} + +finish_test diff --git a/test/misc7.test b/test/misc7.test index 96062a93b..ec042ff0e 100644 --- a/test/misc7.test +++ b/test/misc7.test @@ -355,7 +355,7 @@ do_ioerr_test misc7-16 -sqlprep { COMMIT; }} msg] - if {!$rc || ($rc && [string first "columns" $msg]==0)} { + if {!$rc || ($rc && [string first "UNIQUE" $msg]==0)} { set msg } else { error $msg diff --git a/test/run-wordcount.sh b/test/run-wordcount.sh new file mode 100644 index 000000000..1755d5816 --- /dev/null +++ b/test/run-wordcount.sh @@ -0,0 +1,78 @@ +#!/bin/sh +# +# This script runs the wordcount program in different ways, comparing +# the output from each. +# + +# Select the source text to be analyzed. +# +if test "x$1" = "x"; +then echo "Usage: $0 FILENAME [ARGS...]"; exit 1; +fi + +# Do test runs +# +rm -f wcdb1.db +./wordcount --timer --summary wcdb1.db $* --insert >wc-out.txt +mv wc-out.txt wc-baseline.txt +rm -f wcdb2.db +./wordcount --timer --summary wcdb2.db $* --insert --without-rowid >wc-out.txt + if cmp -s wc-out.txt wc-baseline.txt; + then echo hi >/dev/null; + else echo ERROR:; + diff -u wc-baseline.txt wc-out.txt; + fi + +rm -f wcdb1.db +./wordcount --timer --summary wcdb1.db $* --replace >wc-out.txt + if cmp -s wc-out.txt wc-baseline.txt; + then echo hi >/dev/null; + else echo ERROR:; + diff -u wc-baseline.txt wc-out.txt; + fi +rm -f wcdb2.db +./wordcount --timer --summary wcdb2.db $* --replace --without-rowid >wc-out.txt + if cmp -s wc-out.txt wc-baseline.txt; + then echo hi >/dev/null; + else echo ERROR:; + diff -u wc-baseline.txt wc-out.txt; + fi + +rm -f wcdb1.db +./wordcount --timer --summary wcdb1.db $* --select >wc-out.txt + if cmp -s wc-out.txt wc-baseline.txt; + then echo hi >/dev/null; + else echo ERROR:; + diff -u wc-baseline.txt wc-out.txt; + fi + +rm -f wcdb2.db +./wordcount --timer --summary wcdb2.db $* --select --without-rowid >wc-out.txt + if cmp -s wc-out.txt wc-baseline.txt; + then echo hi >/dev/null; + else echo ERROR:; + diff -u wc-baseline.txt wc-out.txt; + fi + +./wordcount --timer --summary wcdb1.db $* --query >wc-out.txt +mv wc-out.txt wc-baseline.txt +./wordcount --timer --summary wcdb2.db $* --query --without-rowid >wc-out.txt + if cmp -s wc-out.txt wc-baseline.txt; + then echo hi >/dev/null; + else echo ERROR:; + diff -u wc-baseline.txt wc-out.txt; + fi + +./wordcount --timer --summary wcdb1.db $* --delete >wc-out.txt +mv wc-out.txt wc-baseline.txt +./wordcount --timer --summary wcdb2.db $* --delete --without-rowid >wc-out.txt + if cmp -s wc-out.txt wc-baseline.txt; + then echo hi >/dev/null; + else echo ERROR:; + diff -u wc-baseline.txt wc-out.txt; + fi + + +# Clean up temporary files created. +# +rm -rf wcdb1.db wcdb2.db wc-out.txt wc-baseline.txt diff --git a/test/speedtest1.c b/test/speedtest1.c new file mode 100644 index 000000000..b15f65cfe --- /dev/null +++ b/test/speedtest1.c @@ -0,0 +1,951 @@ +/* +** A program for performance testing. +** +** The available command-line options are described below: +*/ +static const char zHelp[] = + "Usage: %s [--options] DATABASE\n" + "Options:\n" + " --autovacuum Enable AUTOVACUUM mode\n" + " --cachesize N Set the cache size to N\n" + " --exclusive Enable locking_mode=EXCLUSIVE\n" + " --heap SZ MIN Memory allocator uses SZ bytes & min allocation MIN\n" + " --incrvacuum Enable incremenatal vacuum mode\n" + " --journalmode M Set the journal_mode to MODE\n" + " --key KEY Set the encryption key to KEY\n" + " --lookaside N SZ Configure lookaside for N slots of SZ bytes each\n" + " --nosync Set PRAGMA synchronous=OFF\n" + " --notnull Add NOT NULL constraints to table columns\n" + " --pagesize N Set the page size to N\n" + " --pcache N SZ Configure N pages of pagecache each of size SZ bytes\n" + " --primarykey Use PRIMARY KEY instead of UNIQUE where appropriate\n" + " --reprepare Reprepare each statement upon every invocation\n" + " --scratch N SZ Configure scratch memory for N slots of SZ bytes each\n" + " --sqlonly No-op. Only show the SQL that would have been run.\n" + " --size N Relative test size. Default=100\n" + " --stats Show statistics at the end\n" + " --testset T Run test-set T\n" + " --trace Turn on SQL tracing\n" + " --utf16be Set text encoding to UTF-16BE\n" + " --utf16le Set text encoding to UTF-16LE\n" + " --without-rowid Use WITHOUT ROWID where appropriate\n" +; + + +#include "sqlite3.h" +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <ctype.h> + +/* All global state is held in this structure */ +static struct Global { + sqlite3 *db; /* The open database connection */ + sqlite3_stmt *pStmt; /* Current SQL statement */ + sqlite3_int64 iStart; /* Start-time for the current test */ + sqlite3_int64 iTotal; /* Total time */ + int bWithoutRowid; /* True for --without-rowid */ + int bReprepare; /* True to reprepare the SQL on each rerun */ + int bSqlOnly; /* True to print the SQL once only */ + int szTest; /* Scale factor for test iterations */ + const char *zWR; /* Might be WITHOUT ROWID */ + const char *zNN; /* Might be NOT NULL */ + const char *zPK; /* Might be UNIQUE or PRIMARY KEY */ + unsigned int x, y; /* Pseudo-random number generator state */ + int nResult; /* Size of the current result */ + char zResult[3000]; /* Text of the current result */ +} g; + + +/* Print an error message and exit */ +static void fatal_error(const char *zMsg, ...){ + va_list ap; + va_start(ap, zMsg); + vfprintf(stderr, zMsg, ap); + va_end(ap); + exit(1); +} + +/* +** Return the value of a hexadecimal digit. Return -1 if the input +** is not a hex digit. +*/ +static int hexDigitValue(char c){ + if( c>='0' && c<='9' ) return c - '0'; + if( c>='a' && c<='f' ) return c - 'a' + 10; + if( c>='A' && c<='F' ) return c - 'A' + 10; + return -1; +} + +/* +** Interpret zArg as an integer value, possibly with suffixes. +*/ +static int integerValue(const char *zArg){ + sqlite3_int64 v = 0; + static const struct { char *zSuffix; int iMult; } aMult[] = { + { "KiB", 1024 }, + { "MiB", 1024*1024 }, + { "GiB", 1024*1024*1024 }, + { "KB", 1000 }, + { "MB", 1000000 }, + { "GB", 1000000000 }, + { "K", 1000 }, + { "M", 1000000 }, + { "G", 1000000000 }, + }; + int i; + int isNeg = 0; + if( zArg[0]=='-' ){ + isNeg = 1; + zArg++; + }else if( zArg[0]=='+' ){ + zArg++; + } + if( zArg[0]=='0' && zArg[1]=='x' ){ + int x; + zArg += 2; + while( (x = hexDigitValue(zArg[0]))>=0 ){ + v = (v<<4) + x; + zArg++; + } + }else{ + while( isdigit(zArg[0]) ){ + v = v*10 + zArg[0] - '0'; + zArg++; + } + } + for(i=0; i<sizeof(aMult)/sizeof(aMult[0]); i++){ + if( sqlite3_stricmp(aMult[i].zSuffix, zArg)==0 ){ + v *= aMult[i].iMult; + break; + } + } + if( v>=2147483648 ) fatal_error("parameter to large - max 2147483648"); + return isNeg? -v : v; +} + +/* Return the current wall-clock time, in milliseconds */ +sqlite3_int64 speedtest1_timestamp(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; +} + +/* Return a pseudo-random unsigned integer */ +unsigned int speedtest1_random(void){ + g.x = (g.x>>1) ^ ((1+~(g.x&1)) & 0xd0000001); + g.y = g.y*1103515245 + 12345; + return g.x ^ g.y; +} + +/* Map the value in within the range of 1...limit into another +** number in a way that is chatic and invertable. +*/ +unsigned swizzle(unsigned in, unsigned limit){ + unsigned out = 0; + while( limit ){ + out = (out<<1) | (in&1); + in >>= 1; + limit >>= 1; + } + return out; +} + +/* Round up a number so that it is a power of two minus one +*/ +unsigned roundup_allones(unsigned limit){ + unsigned m = 1; + while( m<limit ) m = (m<<1)+1; + return m; +} + +/* The speedtest1_numbername procedure below converts its argment (an integer) +** into a string which is the English-language name for that number. +** The returned string should be freed with sqlite3_free(). +** +** Example: +** +** speedtest1_numbername(123) -> "one hundred twenty three" +*/ +int speedtest1_numbername(unsigned int n, char *zOut, int nOut){ + static const char *ones[] = { "zero", "one", "two", "three", "four", "five", + "six", "seven", "eight", "nine", "ten", "eleven", "twelve", + "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", + "eighteen", "nineteen" }; + static const char *tens[] = { "", "ten", "twenty", "thirty", "forty", + "fifty", "sixty", "seventy", "eighty", "ninety" }; + int i = 0; + + if( n>=1000000000 ){ + i += speedtest1_numbername(n/1000000000, zOut+i, nOut-i); + sqlite3_snprintf(nOut-i, zOut+i, " billion"); + i += (int)strlen(zOut+i); + n = n % 1000000000; + } + if( n>=1000000 ){ + if( i && i<nOut-1 ) zOut[i++] = ' '; + i += speedtest1_numbername(n/1000000, zOut+i, nOut-i); + sqlite3_snprintf(nOut-i, zOut+i, " million"); + i += (int)strlen(zOut+i); + n = n % 1000000; + } + if( n>=1000 ){ + if( i && i<nOut-1 ) zOut[i++] = ' '; + i += speedtest1_numbername(n/1000, zOut+i, nOut-i); + sqlite3_snprintf(nOut-i, zOut+i, " thousand"); + i += (int)strlen(zOut+i); + n = n % 1000; + } + if( n>=100 ){ + if( i && i<nOut-1 ) zOut[i++] = ' '; + sqlite3_snprintf(nOut-i, zOut+i, "%s hundred", ones[n/100]); + i += (int)strlen(zOut+i); + n = n % 100; + } + if( n>=20 ){ + if( i && i<nOut-1 ) zOut[i++] = ' '; + sqlite3_snprintf(nOut-i, zOut+i, "%s", tens[n/10]); + i += (int)strlen(zOut+i); + n = n % 10; + } + if( n>0 ){ + if( i && i<nOut-1 ) zOut[i++] = ' '; + sqlite3_snprintf(nOut-i, zOut+i, "%s", ones[n]); + i += (int)strlen(zOut+i); + } + if( i==0 ){ + sqlite3_snprintf(nOut-i, zOut+i, "zero"); + i += (int)strlen(zOut+i); + } + return i; +} + + +/* Start a new test case */ +#define NAMEWIDTH 60 +static const char zDots[] = + "......................................................................."; +void speedtest1_begin_test(int iTestNum, const char *zTestName, ...){ + int n = (int)strlen(zTestName); + char *zName; + va_list ap; + va_start(ap, zTestName); + zName = sqlite3_vmprintf(zTestName, ap); + va_end(ap); + n = (int)strlen(zName); + if( n>NAMEWIDTH ){ + zName[NAMEWIDTH] = 0; + n = NAMEWIDTH; + } + if( g.bSqlOnly ){ + printf("/* %4d - %s%.*s */\n", iTestNum, zName, NAMEWIDTH-n, zDots); + }else{ + printf("%4d - %s%.*s ", iTestNum, zName, NAMEWIDTH-n, zDots); + fflush(stdout); + } + sqlite3_free(zName); + g.nResult = 0; + g.iStart = speedtest1_timestamp(); + g.x = 2903710987; + g.y = 1157229256; +} + +/* Complete a test case */ +void speedtest1_end_test(void){ + sqlite3_int64 iElapseTime = speedtest1_timestamp() - g.iStart; + if( !g.bSqlOnly ){ + g.iTotal += iElapseTime; + printf("%4d.%03ds\n", (int)(iElapseTime/1000), (int)(iElapseTime%1000)); + } + if( g.pStmt ){ + sqlite3_finalize(g.pStmt); + g.pStmt = 0; + } +} + +/* Report end of testing */ +void speedtest1_final(void){ + if( !g.bSqlOnly ){ + printf(" TOTAL%.*s %4d.%03ds\n", NAMEWIDTH-5, zDots, + (int)(g.iTotal/1000), (int)(g.iTotal%1000)); + } +} + +/* Run SQL */ +void speedtest1_exec(const char *zFormat, ...){ + va_list ap; + char *zSql; + va_start(ap, zFormat); + zSql = sqlite3_vmprintf(zFormat, ap); + va_end(ap); + if( g.bSqlOnly ){ + int n = (int)strlen(zSql); + while( n>0 && (zSql[n-1]==';' || isspace(zSql[n-1])) ){ n--; } + printf("%.*s;\n", n, zSql); + }else{ + char *zErrMsg = 0; + int rc = sqlite3_exec(g.db, zSql, 0, 0, &zErrMsg); + if( zErrMsg ) fatal_error("SQL error: %s\n%s\n", zErrMsg, zSql); + if( rc!=SQLITE_OK ) fatal_error("exec error: %s\n", sqlite3_errmsg(g.db)); + } + sqlite3_free(zSql); +} + +/* Prepare an SQL statement */ +void speedtest1_prepare(const char *zFormat, ...){ + va_list ap; + char *zSql; + va_start(ap, zFormat); + zSql = sqlite3_vmprintf(zFormat, ap); + va_end(ap); + if( g.bSqlOnly ){ + int n = (int)strlen(zSql); + while( n>0 && (zSql[n-1]==';' || isspace(zSql[n-1])) ){ n--; } + printf("%.*s;\n", n, zSql); + }else{ + int rc; + if( g.pStmt ) sqlite3_finalize(g.pStmt); + rc = sqlite3_prepare_v2(g.db, zSql, -1, &g.pStmt, 0); + if( rc ){ + fatal_error("SQL error: %s\n", sqlite3_errmsg(g.db)); + } + } + sqlite3_free(zSql); +} + +/* Run an SQL statement previously prepared */ +void speedtest1_run(void){ + int i, n, len; + if( g.bSqlOnly ) return; + assert( g.pStmt ); + g.nResult = 0; + while( sqlite3_step(g.pStmt)==SQLITE_ROW ){ + n = sqlite3_column_count(g.pStmt); + for(i=0; i<n; i++){ + const char *z = (const char*)sqlite3_column_text(g.pStmt, i); + if( z==0 ) z = "nil"; + len = (int)strlen(z); + if( g.nResult+len<sizeof(g.zResult)-2 ){ + if( g.nResult>0 ) g.zResult[g.nResult++] = ' '; + memcpy(g.zResult + g.nResult, z, len+1); + g.nResult += len; + } + } + } + if( g.bReprepare ){ + sqlite3_stmt *pNew; + sqlite3_prepare_v2(g.db, sqlite3_sql(g.pStmt), -1, &pNew, 0); + sqlite3_finalize(g.pStmt); + g.pStmt = pNew; + }else{ + sqlite3_reset(g.pStmt); + } +} + +/* The sqlite3_trace() callback function */ +static void traceCallback(void *NotUsed, const char *zSql){ + int n = (int)strlen(zSql); + while( n>0 && (zSql[n-1]==';' || isspace(zSql[n-1])) ) n--; + fprintf(stderr,"%.*s;\n", n, zSql); +} + +/* Substitute random() function that gives the same random +** sequence on each run, for repeatability. */ +static void randomFunc( + sqlite3_context *context, + int NotUsed, + sqlite3_value **NotUsed2 +){ + sqlite3_result_int64(context, (sqlite3_int64)speedtest1_random()); +} + +/* +** The main and default testset +*/ +void testset_main(void){ + int i; /* Loop counter */ + int n; /* iteration count */ + int sz; /* Size of the tables */ + int maxb; /* Maximum swizzled value */ + unsigned x1, x2; /* Parameters */ + int len; /* Length of the zNum[] string */ + char zNum[2000]; /* A number name */ + + sz = n = g.szTest*500; + maxb = roundup_allones(sz); + speedtest1_begin_test(100, "%d INSERTs into table with no index", n); + speedtest1_exec("BEGIN"); + speedtest1_exec("CREATE TABLE t1(a INTEGER %s, b INTEGER %s, c TEXT %s);", + g.zNN, g.zNN, g.zNN); + speedtest1_prepare("INSERT INTO t1 VALUES(?1,?2,?3); -- %d times", n); + for(i=1; i<=n; i++){ + x1 = swizzle(i,maxb); + speedtest1_numbername(x1, zNum, sizeof(zNum)); + sqlite3_bind_int64(g.pStmt, 1, (sqlite3_int64)x1); + sqlite3_bind_int(g.pStmt, 2, i); + sqlite3_bind_text(g.pStmt, 3, zNum, -1, SQLITE_STATIC); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + + n = sz; + speedtest1_begin_test(110, "%d ordered INSERTS with one index/PK", n); + speedtest1_exec("BEGIN"); + speedtest1_exec("CREATE TABLE t2(a INTEGER %s %s, b INTEGER %s, c TEXT %s) %s", + g.zNN, g.zPK, g.zNN, g.zNN, g.zWR); + speedtest1_prepare("INSERT INTO t2 VALUES(?1,?2,?3); -- %d times", n); + for(i=1; i<=n; i++){ + x1 = swizzle(i,maxb); + speedtest1_numbername(x1, zNum, sizeof(zNum)); + sqlite3_bind_int(g.pStmt, 1, i); + sqlite3_bind_int64(g.pStmt, 2, (sqlite3_int64)x1); + sqlite3_bind_text(g.pStmt, 3, zNum, -1, SQLITE_STATIC); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + + n = sz; + speedtest1_begin_test(120, "%d unordered INSERTS with one index/PK", n); + speedtest1_exec("BEGIN"); + speedtest1_exec("CREATE TABLE t3(a INTEGER %s %s, b INTEGER %s, c TEXT %s) %s", + g.zNN, g.zPK, g.zNN, g.zNN, g.zWR); + speedtest1_prepare("INSERT INTO t3 VALUES(?1,?2,?3); -- %d times", n); + for(i=1; i<=n; i++){ + x1 = swizzle(i,maxb); + speedtest1_numbername(x1, zNum, sizeof(zNum)); + sqlite3_bind_int(g.pStmt, 2, i); + sqlite3_bind_int64(g.pStmt, 1, (sqlite3_int64)x1); + sqlite3_bind_text(g.pStmt, 3, zNum, -1, SQLITE_STATIC); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + + n = g.szTest/2; + speedtest1_begin_test(130, "%d SELECTS, numeric BETWEEN, unindexed", n); + speedtest1_exec("BEGIN"); + speedtest1_prepare( + "SELECT count(*), avg(b), sum(length(c)) FROM t1\n" + " WHERE b BETWEEN ?1 AND ?2; -- %d times", n + ); + for(i=1; i<=n; i++){ + x1 = speedtest1_random()%maxb; + x2 = speedtest1_random()%10 + sz/5000 + x1; + sqlite3_bind_int(g.pStmt, 1, x1); + sqlite3_bind_int(g.pStmt, 2, x2); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + + n = g.szTest/5; + speedtest1_begin_test(140, "%d SELECTS, LIKE, unindexed", n); + speedtest1_exec("BEGIN"); + speedtest1_prepare( + "SELECT count(*), avg(b), sum(length(c)) FROM t1\n" + " WHERE c LIKE ?1; -- %d times", n + ); + for(i=1; i<=n; i++){ + x1 = speedtest1_random()%maxb; + zNum[0] = '%'; + len = speedtest1_numbername(i, zNum+1, sizeof(zNum)-2); + zNum[len] = '%'; + zNum[len+1] = 0; + sqlite3_bind_text(g.pStmt, 1, zNum, len, SQLITE_STATIC); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + + speedtest1_begin_test(150, "CREATE INDEX five times"); + speedtest1_exec( + "BEGIN;\n" + "CREATE UNIQUE INDEX t1b ON t1(b);\n" + "CREATE INDEX t1c ON t1(c);\n" + "CREATE UNIQUE INDEX t2b ON t2(b);\n" + "CREATE INDEX t2c ON t2(c DESC);\n" + "CREATE INDEX t3bc ON t3(b,c);\n" + "COMMIT;\n" + ); + speedtest1_end_test(); + + + n = sz/5; + speedtest1_begin_test(160, "%d SELECTS, numeric BETWEEN, indexed", n); + speedtest1_exec("BEGIN"); + speedtest1_prepare( + "SELECT count(*), avg(b), sum(length(c)) FROM t1\n" + " WHERE b BETWEEN ?1 AND ?2; -- %d times", n + ); + for(i=1; i<=n; i++){ + x1 = speedtest1_random()%maxb; + x2 = speedtest1_random()%10 + sz/5000 + x1; + sqlite3_bind_int(g.pStmt, 1, x1); + sqlite3_bind_int(g.pStmt, 2, x2); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + + n = sz/5; + speedtest1_begin_test(161, "%d SELECTS, numeric BETWEEN, PK", n); + speedtest1_exec("BEGIN"); + speedtest1_prepare( + "SELECT count(*), avg(b), sum(length(c)) FROM t2\n" + " WHERE a BETWEEN ?1 AND ?2; -- %d times", n + ); + for(i=1; i<=n; i++){ + x1 = speedtest1_random()%maxb; + x2 = speedtest1_random()%10 + sz/5000 + x1; + sqlite3_bind_int(g.pStmt, 1, x1); + sqlite3_bind_int(g.pStmt, 2, x2); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + + n = sz/5; + speedtest1_begin_test(170, "%d SELECTS, text BETWEEN, indexed", n); + speedtest1_exec("BEGIN"); + speedtest1_prepare( + "SELECT count(*), avg(b), sum(length(c)) FROM t1\n" + " WHERE c BETWEEN ?1 AND (?1||'~'); -- %d times", n + ); + for(i=1; i<=n; i++){ + x1 = swizzle(i, maxb); + len = speedtest1_numbername(x1, zNum, sizeof(zNum)-1); + sqlite3_bind_text(g.pStmt, 1, zNum, len, SQLITE_STATIC); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + n = sz; + speedtest1_begin_test(180, "%d INSERTS with three indexes", n); + speedtest1_exec("BEGIN"); + speedtest1_exec( + "CREATE TABLE t4(\n" + " a INTEGER %s %s,\n" + " b INTEGER %s,\n" + " c TEXT %s\n" + ") %s", + g.zNN, g.zPK, g.zNN, g.zNN, g.zWR); + speedtest1_exec("CREATE INDEX t4b ON t4(b)"); + speedtest1_exec("CREATE INDEX t4c ON t4(c)"); + speedtest1_exec("INSERT INTO t4 SELECT * FROM t1"); + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + n = sz; + speedtest1_begin_test(190, "DELETE and REFILL one table", n); + speedtest1_exec( + "DELETE FROM t2;" + "INSERT INTO t2 SELECT * FROM t1;" + ); + speedtest1_end_test(); + + + speedtest1_begin_test(200, "VACUUM"); + speedtest1_exec("VACUUM"); + speedtest1_end_test(); + + + speedtest1_begin_test(210, "ALTER TABLE ADD COLUMN, and query"); + speedtest1_exec("ALTER TABLE t2 ADD COLUMN d DEFAULT 123"); + speedtest1_exec("SELECT sum(d) FROM t2"); + speedtest1_end_test(); + + + n = sz/5; + speedtest1_begin_test(230, "%d UPDATES, numeric BETWEEN, indexed", n); + speedtest1_exec("BEGIN"); + speedtest1_prepare( + "UPDATE t2 SET d=b*2 WHERE b BETWEEN ?1 AND ?2; -- %d times", n + ); + for(i=1; i<=n; i++){ + x1 = speedtest1_random()%maxb; + x2 = speedtest1_random()%10 + sz/5000 + x1; + sqlite3_bind_int(g.pStmt, 1, x1); + sqlite3_bind_int(g.pStmt, 2, x2); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + + n = sz; + speedtest1_begin_test(240, "%d UPDATES of individual rows", n); + speedtest1_exec("BEGIN"); + speedtest1_prepare( + "UPDATE t2 SET d=b*3 WHERE a=?1; -- %d times", n + ); + for(i=1; i<=n; i++){ + x1 = speedtest1_random()%sz + 1; + sqlite3_bind_int(g.pStmt, 1, x1); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + speedtest1_begin_test(250, "One big UPDATE of the whole %d-row table", sz); + speedtest1_exec("UPDATE t2 SET d=b*4"); + speedtest1_end_test(); + + + speedtest1_begin_test(260, "Query added column after filling"); + speedtest1_exec("SELECT sum(d) FROM t2"); + speedtest1_end_test(); + + + + n = sz/5; + speedtest1_begin_test(270, "%d DELETEs, numeric BETWEEN, indexed", n); + speedtest1_exec("BEGIN"); + speedtest1_prepare( + "DELETE FROM t2 WHERE b BETWEEN ?1 AND ?2; -- %d times", n + ); + for(i=1; i<=n; i++){ + x1 = speedtest1_random()%maxb + 1; + x2 = speedtest1_random()%10 + sz/5000 + x1; + sqlite3_bind_int(g.pStmt, 1, x1); + sqlite3_bind_int(g.pStmt, 2, x2); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + + n = sz; + speedtest1_begin_test(280, "%d DELETEs of individual rows", n); + speedtest1_exec("BEGIN"); + speedtest1_prepare( + "DELETE FROM t3 WHERE a=?1; -- %d times", n + ); + for(i=1; i<=n; i++){ + x1 = speedtest1_random()%sz + 1; + sqlite3_bind_int(g.pStmt, 1, x1); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + + speedtest1_begin_test(290, "Refill two %d-row tables using REPLACE", sz); + speedtest1_exec("REPLACE INTO t2(a,b,c) SELECT a,b,c FROM t1"); + speedtest1_exec("REPLACE INTO t3(a,b,c) SELECT a,b,c FROM t1"); + speedtest1_end_test(); + + + n = sz/5; + speedtest1_begin_test(290, "%d four-ways joins", n); + speedtest1_exec("BEGIN"); + speedtest1_prepare( + "SELECT t1.c FROM t1, t2, t3, t4\n" + " WHERE t4.a BETWEEN ?1 AND ?2\n" + " AND t3.a=t4.b\n" + " AND t2.a=t3.b\n" + " AND t1.c=t2.c" + ); + for(i=1; i<=n; i++){ + x1 = speedtest1_random()%sz + 1; + x2 = speedtest1_random()%10 + x1 + 4; + sqlite3_bind_int(g.pStmt, 1, x1); + sqlite3_bind_int(g.pStmt, 2, x2); + speedtest1_run(); + } + speedtest1_exec("COMMIT"); + speedtest1_end_test(); + + + + speedtest1_begin_test(980, "PRAGMA integrity_check"); + speedtest1_exec("PRAGMA integrity_check"); + speedtest1_end_test(); + + + speedtest1_begin_test(990, "ANALYZE"); + speedtest1_exec("ANALYZE"); + speedtest1_end_test(); +} + +/* +** A testset used for debugging speedtest1 itself. +*/ +void testset_debug1(void){ + unsigned i, n; + unsigned x1, x2; + char zNum[2000]; /* A number name */ + + n = g.szTest; + for(i=1; i<=n; i++){ + x1 = swizzle(i, n); + x2 = swizzle(x1, n); + speedtest1_numbername(x1, zNum, sizeof(zNum)); + printf("%5d %5d %5d %s\n", i, x1, x2, zNum); + } +} + +int main(int argc, char **argv){ + int doAutovac = 0; /* True for --autovacuum */ + int cacheSize = 0; /* Desired cache size. 0 means default */ + int doExclusive = 0; /* True for --exclusive */ + int nHeap = 0, mnHeap = 0; /* Heap size from --heap */ + int doIncrvac = 0; /* True for --incrvacuum */ + const char *zJMode = 0; /* Journal mode */ + const char *zKey = 0; /* Encryption key */ + int nLook = 0, szLook = 0; /* --lookaside configuration */ + int noSync = 0; /* True for --nosync */ + int pageSize = 0; /* Desired page size. 0 means default */ + int nPCache = 0, szPCache = 0;/* --pcache configuration */ + int nScratch = 0, szScratch=0;/* --scratch configuration */ + int showStats = 0; /* True for --stats */ + const char *zTSet = "main"; /* Which --testset torun */ + int doTrace = 0; /* True for --trace */ + const char *zEncoding = 0; /* --utf16be or --utf16le */ + const char *zDbName = 0; /* Name of the test database */ + + void *pHeap = 0; /* Allocated heap space */ + void *pLook = 0; /* Allocated lookaside space */ + void *pPCache = 0; /* Allocated storage for pcache */ + void *pScratch = 0; /* Allocated storage for scratch */ + int iCur, iHi; /* Stats values, current and "highwater" */ + int i; /* Loop counter */ + int rc; /* API return code */ + + /* Process command-line arguments */ + g.zWR = ""; + g.zNN = ""; + g.zPK = "UNIQUE"; + g.szTest = 100; + for(i=1; i<argc; i++){ + const char *z = argv[i]; + if( z[0]=='-' ){ + do{ z++; }while( z[0]=='-' ); + if( strcmp(z,"autovacuum")==0 ){ + doAutovac = 1; + }else if( strcmp(z,"cachesize")==0 ){ + if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]); + i++; + cacheSize = integerValue(argv[i]); + }else if( strcmp(z,"exclusive")==0 ){ + doExclusive = 1; + }else if( strcmp(z,"heap")==0 ){ + if( i>=argc-2 ) fatal_error("missing arguments on %s\n", argv[i]); + nHeap = integerValue(argv[i+1]); + mnHeap = integerValue(argv[i+2]); + i += 2; + }else if( strcmp(z,"incrvacuum")==0 ){ + doIncrvac = 1; + }else if( strcmp(z,"journal")==0 ){ + if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]); + zJMode = argv[++i]; + }else if( strcmp(z,"key")==0 ){ + if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]); + zKey = argv[++i]; + }else if( strcmp(z,"lookaside")==0 ){ + if( i>=argc-2 ) fatal_error("missing arguments on %s\n", argv[i]); + nLook = integerValue(argv[i+1]); + szLook = integerValue(argv[i+2]); + i += 2; + }else if( strcmp(z,"nosync")==0 ){ + noSync = 1; + }else if( strcmp(z,"notnull")==0 ){ + g.zNN = "NOT NULL"; + }else if( strcmp(z,"pagesize")==0 ){ + if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]); + pageSize = integerValue(argv[++i]); + }else if( strcmp(z,"pcache")==0 ){ + if( i>=argc-2 ) fatal_error("missing arguments on %s\n", argv[i]); + nPCache = integerValue(argv[i+1]); + szPCache = integerValue(argv[i+2]); + i += 2; + }else if( strcmp(z,"primarykey")==0 ){ + g.zPK = "PRIMARY KEY"; + }else if( strcmp(z,"reprepare")==0 ){ + g.bReprepare = 1; + }else if( strcmp(z,"scratch")==0 ){ + if( i>=argc-2 ) fatal_error("missing arguments on %s\n", argv[i]); + nScratch = integerValue(argv[i+1]); + szScratch = integerValue(argv[i+2]); + i += 2; + }else if( strcmp(z,"sqlonly")==0 ){ + g.bSqlOnly = 1; + }else if( strcmp(z,"size")==0 ){ + if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]); + g.szTest = integerValue(argv[++i]); + }else if( strcmp(z,"stats")==0 ){ + showStats = 1; + }else if( strcmp(z,"testset")==0 ){ + if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]); + zTSet = argv[++i]; + }else if( strcmp(z,"trace")==0 ){ + doTrace = 1; + }else if( strcmp(z,"utf16le")==0 ){ + zEncoding = "utf16le"; + }else if( strcmp(z,"utf16be")==0 ){ + zEncoding = "utf16be"; + }else if( strcmp(z,"without-rowid")==0 ){ + g.zWR = "WITHOUT ROWID"; + g.zPK = "PRIMARY KEY"; + }else if( strcmp(z, "help")==0 || strcmp(z,"?")==0 ){ + printf(zHelp, argv[0]); + exit(0); + }else{ + fatal_error("unknown option: %s\nUse \"%s -?\" for help\n", + argv[i], argv[0]); + } + }else if( zDbName==0 ){ + zDbName = argv[i]; + }else{ + fatal_error("surplus argument: %s\nUse \"%s -?\" for help\n", + argv[i], argv[0]); + } + } +#if 0 + if( zDbName==0 ){ + fatal_error(zHelp, argv[0]); + } +#endif + if( nHeap>0 ){ + pHeap = malloc( nHeap ); + if( pHeap==0 ) fatal_error("cannot allocate %d-byte heap\n", nHeap); + rc = sqlite3_config(SQLITE_CONFIG_HEAP, pHeap, nHeap, mnHeap); + if( rc ) fatal_error("heap configuration failed: %d\n", rc); + } + if( nPCache>0 && szPCache>0 ){ + pPCache = malloc( nPCache*(sqlite3_int64)szPCache ); + if( pPCache==0 ) fatal_error("cannot allocate %lld-byte pcache\n", + nPCache*(sqlite3_int64)szPCache); + rc = sqlite3_config(SQLITE_CONFIG_PAGECACHE, pPCache, szPCache, nPCache); + if( rc ) fatal_error("pcache configuration failed: %d\n", rc); + } + if( nScratch>0 && szScratch>0 ){ + pScratch = malloc( nScratch*(sqlite3_int64)szScratch ); + if( pScratch==0 ) fatal_error("cannot allocate %lld-byte scratch\n", + nScratch*(sqlite3_int64)szScratch); + rc = sqlite3_config(SQLITE_CONFIG_SCRATCH, pScratch, szScratch, nScratch); + if( rc ) fatal_error("scratch configuration failed: %d\n", rc); + } + if( nLook>0 ){ + sqlite3_config(SQLITE_CONFIG_LOOKASIDE, 0, 0); + } + + /* Open the database and the input file */ + if( sqlite3_open(zDbName, &g.db) ){ + fatal_error("Cannot open database file: %s\n", zDbName); + } + if( nLook>0 && szLook>0 ){ + pLook = malloc( nLook*szLook ); + rc = sqlite3_db_config(g.db, SQLITE_DBCONFIG_LOOKASIDE, pLook, szLook,nLook); + if( rc ) fatal_error("lookaside configuration failed: %d\n", rc); + } + + /* Set database connection options */ + sqlite3_create_function(g.db, "random", 0, SQLITE_UTF8, 0, randomFunc, 0, 0); + if( doTrace ) sqlite3_trace(g.db, traceCallback, 0); + if( zKey ){ + speedtest1_exec("PRAGMA key('%s')", zKey); + } + if( zEncoding ){ + speedtest1_exec("PRAGMA encoding=%s", zEncoding); + } + if( doAutovac ){ + speedtest1_exec("PRAGMA auto_vacuum=FULL"); + }else if( doIncrvac ){ + speedtest1_exec("PRAGMA auto_vacuum=INCREMENTAL"); + } + if( pageSize ){ + speedtest1_exec("PRAGMA page_size=%d", pageSize); + } + if( cacheSize ){ + speedtest1_exec("PRAGMA cache_size=%d", cacheSize); + } + if( noSync ) speedtest1_exec("PRAGMA synchronous=OFF"); + if( doExclusive ){ + speedtest1_exec("PRAGMA locking_mode=EXCLUSIVE"); + } + if( zJMode ){ + speedtest1_exec("PRAGMA journal_mode=%s", zJMode); + } + + if( strcmp(zTSet,"main")==0 ){ + testset_main(); + }else if( strcmp(zTSet,"debug1")==0 ){ + testset_debug1(); + }else{ + fatal_error("unknown testset: \"%s\"\n", zTSet); + } + speedtest1_final(); + + /* Database connection statistics printed after both prepared statements + ** have been finalized */ + if( showStats ){ + sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_USED, &iCur, &iHi, 0); + printf("-- Lookaside Slots Used: %d (max %d)\n", iCur,iHi); + sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &iCur, &iHi, 0); + printf("-- Successful lookasides: %d\n", iHi); + sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &iCur,&iHi,0); + printf("-- Lookaside size faults: %d\n", iHi); + sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, &iCur,&iHi,0); + printf("-- Lookaside OOM faults: %d\n", iHi); + sqlite3_db_status(g.db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHi, 0); + printf("-- Pager Heap Usage: %d bytes\n", iCur); + sqlite3_db_status(g.db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHi, 1); + printf("-- Page cache hits: %d\n", iCur); + sqlite3_db_status(g.db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHi, 1); + printf("-- Page cache misses: %d\n", iCur); + sqlite3_db_status(g.db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHi, 1); + printf("-- Page cache writes: %d\n", iCur); + sqlite3_db_status(g.db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHi, 0); + printf("-- Schema Heap Usage: %d bytes\n", iCur); + sqlite3_db_status(g.db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHi, 0); + printf("-- Statement Heap Usage: %d bytes\n", iCur); + } + + sqlite3_close(g.db); + + /* Global memory usage statistics printed after the database connection + ** has closed. Memory usage should be zero at this point. */ + if( showStats ){ + sqlite3_status(SQLITE_STATUS_MEMORY_USED, &iCur, &iHi, 0); + printf("-- Memory Used (bytes): %d (max %d)\n", iCur,iHi); + sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &iCur, &iHi, 0); + printf("-- Outstanding Allocations: %d (max %d)\n", iCur,iHi); + sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &iCur, &iHi, 0); + printf("-- Pcache Overflow Bytes: %d (max %d)\n", iCur,iHi); + sqlite3_status(SQLITE_STATUS_SCRATCH_OVERFLOW, &iCur, &iHi, 0); + printf("-- Scratch Overflow Bytes: %d (max %d)\n", iCur,iHi); + sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &iCur, &iHi, 0); + printf("-- Largest Allocation: %d bytes\n",iHi); + sqlite3_status(SQLITE_STATUS_PAGECACHE_SIZE, &iCur, &iHi, 0); + printf("-- Largest Pcache Allocation: %d bytes\n",iHi); + sqlite3_status(SQLITE_STATUS_SCRATCH_SIZE, &iCur, &iHi, 0); + printf("-- Largest Scratch Allocation: %d bytes\n", iHi); + } + + /* Release memory */ + free( pLook ); + free( pPCache ); + free( pScratch ); + free( pHeap ); + return 0; +} diff --git a/test/win32heap.test b/test/win32heap.test new file mode 100644 index 000000000..b92f8040e --- /dev/null +++ b/test/win32heap.test @@ -0,0 +1,74 @@ +# 2013 November 22 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this script is recovery from transient manditory locks +# that sometimes appear on database files due to anti-virus software. +# + +if {$tcl_platform(platform)!="windows"} return + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable !win32malloc { + finish_test + return +} + +set testprefix win32heap + +do_test 1.1 { + catch {db close} + sqlite3_shutdown + sqlite3_config_heap_size 1048576 + sqlite3_initialize +} {SQLITE_OK} + +do_test 1.2 { + sqlite3 db test.db + catchsql { + CREATE TABLE t1(x); + } +} {0 {}} + +do_test 1.3 { + catchsql { + INSERT INTO t1 (x) VALUES(RANDOMBLOB(1048576)); + } +} {1 {out of memory}} + +do_test 1.4 { + catchsql { + SELECT COUNT(*) FROM t1; + } +} {0 0} + +do_test 1.5 { + catch {db close} + sqlite3_shutdown + sqlite3_config_heap_size 0 + sqlite3_initialize +} {SQLITE_OK} + +do_test 1.6 { + sqlite3 db test.db + catchsql { + INSERT INTO t1 (x) VALUES(RANDOMBLOB(1048576)); + } +} {0 {}} + +do_test 1.7 { + catchsql { + SELECT COUNT(*) FROM t1; + } +} {0 1} + +finish_test diff --git a/test/wordcount.c b/test/wordcount.c index 2161c0717..cf63e983c 100644 --- a/test/wordcount.c +++ b/test/wordcount.c @@ -19,6 +19,7 @@ ** --select Use SELECT mode ** --update Use UPDATE mode ** --delete Use DELETE mode +** --query Use QUERY mode ** --nocase Add the NOCASE collating sequence to the words. ** --trace Enable sqlite3_trace() output. ** --summary Show summary information on the collected data. @@ -28,6 +29,7 @@ ** --commit NNN Commit after every NNN operations ** --nosync Use PRAGMA synchronous=OFF ** --journal MMMM Use PRAGMA journal_mode=MMMM +** --timer Time the operation of this program ** ** Modes: ** @@ -51,11 +53,15 @@ ** Delete mode means: ** (1) DELETE FROM wordcount WHERE word=$new ** -** Note that delete mode is only useful for preexisting databases. The -** wordcount table is created using IF NOT EXISTS so this utility can be -** run multiple times on the same database file. The --without-rowid, -** --nocase, and --pagesize parameters are only effective when creating -** a new database and are harmless no-ops on preexisting databases. +** Query mode means: +** (1) SELECT cnt FROM wordcount WHERE word=$new +** +** Note that delete mode and query mode are only useful for preexisting +** databases. The wordcount table is created using IF NOT EXISTS so this +** utility can be run multiple times on the same database file. The +** --without-rowid, --nocase, and --pagesize parameters are only effective +** when creating a new database and are harmless no-ops on preexisting +** databases. ** ****************************************************************************** ** @@ -75,6 +81,21 @@ #include <stdarg.h> #include "sqlite3.h" +/* Return the current wall-clock time */ +static sqlite3_int64 realTime(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; +} + /* Print an error message and exit */ static void fatal_error(const char *zMsg, ...){ va_list ap; @@ -95,7 +116,7 @@ static int printResult(void *NotUsed, int nArg, char **azArg, char **azNm){ int i; printf("--"); for(i=0; i<nArg; i++){ - printf(" %s", azArg[i]); + printf(" %s", azArg[i] ? azArg[i] : "(null)"); } printf("\n"); return 0; @@ -170,6 +191,7 @@ static void checksumFinalize(sqlite3_context *context){ #define MODE_SELECT 2 #define MODE_UPDATE 3 #define MODE_DELETE 4 +#define MODE_QUERY 5 int main(int argc, char **argv){ const char *zFileToRead = 0; /* Input file. NULL for stdin */ @@ -180,6 +202,7 @@ int main(int argc, char **argv){ int doTrace = 0; /* True for --trace */ int showStats = 0; /* True for --stats */ int showSummary = 0; /* True for --summary */ + int showTimer = 0; /* True for --timer */ int cacheSize = 0; /* Desired cache size. 0 means default */ int pageSize = 0; /* Desired page size. 0 means default */ int commitInterval = 0; /* How often to commit. 0 means never */ @@ -196,6 +219,8 @@ int main(int argc, char **argv){ FILE *in; /* The open input file */ int rc; /* Return code from an SQLite interface */ int iCur, iHiwtr; /* Statistics values, current and "highwater" */ + sqlite3_int64 sumCnt = 0; /* Sum in QUERY mode */ + sqlite3_int64 startTime; char zInput[2000]; /* A single line of input */ /* Process command-line arguments */ @@ -215,6 +240,8 @@ int main(int argc, char **argv){ iMode = MODE_UPDATE; }else if( strcmp(z,"delete")==0 ){ iMode = MODE_DELETE; + }else if( strcmp(z,"query")==0 ){ + iMode = MODE_QUERY; }else if( strcmp(z,"nocase")==0 ){ useNocase = 1; }else if( strcmp(z,"trace")==0 ){ @@ -225,6 +252,8 @@ int main(int argc, char **argv){ showStats = 1; }else if( strcmp(z,"summary")==0 ){ showSummary = 1; + }else if( strcmp(z,"timer")==0 ){ + showTimer = i; }else if( strcmp(z,"cachesize")==0 && i<argc-1 ){ i++; cacheSize = atoi(argv[i]); @@ -250,6 +279,7 @@ int main(int argc, char **argv){ if( zDbName==0 ){ fatal_error("Usage: %s [--options] DATABASE [INPUTFILE]\n", argv[0]); } + startTime = realTime(); /* Open the database and the input file */ if( sqlite3_open(zDbName, &db) ){ @@ -303,6 +333,13 @@ int main(int argc, char **argv){ sqlite3_free(zSql); /* Prepare SQL statements that will be needed */ + if( iMode==MODE_QUERY ){ + rc = sqlite3_prepare_v2(db, + "SELECT cnt FROM wordcount WHERE word=?1", + -1, &pSelect, 0); + if( rc ) fatal_error("Could not prepare the SELECT statement: %s\n", + sqlite3_errmsg(db)); + } if( iMode==MODE_SELECT ){ rc = sqlite3_prepare_v2(db, "SELECT 1 FROM wordcount WHERE word=?1", @@ -385,6 +422,12 @@ int main(int argc, char **argv){ }else{ fatal_error("SELECT failed: %s\n", sqlite3_errmsg(db)); } + }else if( iMode==MODE_QUERY ){ + sqlite3_bind_text(pSelect, 1, zInput+i, j-i, SQLITE_STATIC); + if( sqlite3_step(pSelect)==SQLITE_ROW ){ + sumCnt += sqlite3_column_int64(pSelect, 0); + } + sqlite3_reset(pSelect); }else{ sqlite3_bind_text(pInsert, 1, zInput+i, j-i, SQLITE_STATIC); if( sqlite3_step(pInsert)!=SQLITE_DONE ){ @@ -417,6 +460,25 @@ int main(int argc, char **argv){ sqlite3_finalize(pSelect); sqlite3_finalize(pDelete); + if( iMode==MODE_QUERY ){ + printf("sum of cnt: %lld\n", sumCnt); + rc = sqlite3_prepare_v2(db,"SELECT sum(cnt*cnt) FROM wordcount", -1, + &pSelect, 0); + if( rc==SQLITE_OK && sqlite3_step(pSelect)==SQLITE_ROW ){ + printf("double-check: %lld\n", sqlite3_column_int64(pSelect, 0)); + } + sqlite3_finalize(pSelect); + } + + + if( showTimer ){ + sqlite3_int64 elapseTime = realTime() - startTime; + fprintf(stderr, "%3d.%03d wordcount", (int)(elapseTime/1000), + (int)(elapseTime%1000)); + for(i=1; i<argc; i++) if( i!=showTimer ) fprintf(stderr, " %s", argv[i]); + fprintf(stderr, "\n"); + } + if( showSummary ){ sqlite3_create_function(db, "checksum", -1, SQLITE_UTF8, 0, 0, checksumStep, checksumFinalize); @@ -427,7 +489,7 @@ int main(int argc, char **argv){ "SELECT 'avg(cnt): ', avg(cnt) FROM wordcount;\n" "SELECT 'sum(cnt=1):', sum(cnt=1) FROM wordcount;\n" "SELECT 'top 10: ', group_concat(word, ', ') FROM " - "(SELECT word FROM wordcount ORDER BY cnt DESC LIMIT 10);\n" + "(SELECT word FROM wordcount ORDER BY cnt DESC, word LIMIT 10);\n" "SELECT 'checksum: ', checksum(word, cnt) FROM " "(SELECT word, cnt FROM wordcount ORDER BY word);\n" "PRAGMA integrity_check;\n", diff --git a/tool/build-all-msvc.bat b/tool/build-all-msvc.bat index c730943f6..6e0aeb572 100755 --- a/tool/build-all-msvc.bat +++ b/tool/build-all-msvc.bat @@ -492,9 +492,9 @@ GOTO no_errors GOTO :EOF
:fn_CopyVariable
- SETLOCAL
IF NOT DEFINED %1 GOTO :EOF
IF "%2" == "" GOTO :EOF
+ SETLOCAL
SET __ECHO_CMD=ECHO %%%1%%
FOR /F "delims=" %%V IN ('%__ECHO_CMD%') DO (
SET VALUE=%%V
|