diff --git a/Makefile.in b/Makefile.in index d76b5e4d18..0d82204e06 100644 --- a/Makefile.in +++ b/Makefile.in @@ -406,7 +406,8 @@ TESTSRC = \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/test_thread.c \ $(TOP)/src/test_vfs.c \ - $(TOP)/src/test_wsd.c \ + $(TOP)/src/test_windirent.c \ + $(TOP)/src/test_wsd.c \ $(TOP)/ext/fts3/fts3_term.c \ $(TOP)/ext/fts3/fts3_test.c \ $(TOP)/ext/session/test_session.c \ diff --git a/Makefile.msc b/Makefile.msc index 355435384e..767f3a0105 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -1084,6 +1084,7 @@ TESTSRC = \ $(TOP)\src\test_tclvar.c \ $(TOP)\src\test_thread.c \ $(TOP)\src\test_vfs.c \ + $(TOP)\src\test_windirent.c \ $(TOP)\src\test_wsd.c \ $(TOP)\ext\fts3\fts3_term.c \ $(TOP)\ext\fts3\fts3_test.c \ diff --git a/ext/fts5/fts5_config.c b/ext/fts5/fts5_config.c index 4f6272dbe2..6b0e2b28b6 100644 --- a/ext/fts5/fts5_config.c +++ b/ext/fts5/fts5_config.c @@ -216,34 +216,53 @@ static int fts5ConfigParseSpecial( if( sqlite3_strnicmp("prefix", zCmd, nCmd)==0 ){ const int nByte = sizeof(int) * FTS5_MAX_PREFIX_INDEXES; const char *p; - if( pConfig->aPrefix ){ - *pzErr = sqlite3_mprintf("multiple prefix=... directives"); - rc = SQLITE_ERROR; - }else{ + int bFirst = 1; + if( pConfig->aPrefix==0 ){ pConfig->aPrefix = sqlite3Fts5MallocZero(&rc, nByte); + if( rc ) return rc; } + p = zArg; - while( rc==SQLITE_OK && p[0] ){ + while( 1 ){ int nPre = 0; + while( p[0]==' ' ) p++; + if( bFirst==0 && p[0]==',' ){ + p++; + while( p[0]==' ' ) p++; + }else if( p[0]=='\0' ){ + break; + } + if( p[0]<'0' || p[0]>'9' ){ + *pzErr = sqlite3_mprintf("malformed prefix=... directive"); + rc = SQLITE_ERROR; + break; + } + + if( pConfig->nPrefix==FTS5_MAX_PREFIX_INDEXES ){ + *pzErr = sqlite3_mprintf( + "too many prefix indexes (max %d)", FTS5_MAX_PREFIX_INDEXES + ); + rc = SQLITE_ERROR; + break; + } + while( p[0]>='0' && p[0]<='9' && nPre<1000 ){ nPre = nPre*10 + (p[0] - '0'); p++; } - while( p[0]==' ' ) p++; - if( p[0]==',' ){ - p++; - }else if( p[0] ){ - *pzErr = sqlite3_mprintf("malformed prefix=... directive"); - rc = SQLITE_ERROR; - } - if( rc==SQLITE_OK && (nPre==0 || nPre>=1000) ){ - *pzErr = sqlite3_mprintf("prefix length out of range: %d", nPre); + + if( rc==SQLITE_OK && (nPre<=0 || nPre>=1000) ){ + *pzErr = sqlite3_mprintf("prefix length out of range (max 999)"); rc = SQLITE_ERROR; + break; } + pConfig->aPrefix[pConfig->nPrefix] = nPre; pConfig->nPrefix++; + bFirst = 0; } + assert( pConfig->nPrefix<=FTS5_MAX_PREFIX_INDEXES ); return rc; } diff --git a/ext/fts5/test/fts5config.test b/ext/fts5/test/fts5config.test index 7c88e03d38..dcda2d42a6 100644 --- a/ext/fts5/test/fts5config.test +++ b/ext/fts5/test/fts5config.test @@ -42,6 +42,8 @@ foreach {tn opt} { 1 {prefix=x} 2 {prefix='x'} 3 {prefix='$'} + 4 {prefix='1,2,'} + 5 {prefix=',1'} } { set res [list 1 {malformed prefix=... directive}] do_catchsql_test 2.$tn "CREATE VIRTUAL TABLE f1 USING fts5(x, $opt)" $res @@ -118,18 +120,15 @@ do_catchsql_test 5.3 { #------------------------------------------------------------------------- # Errors in prefix= directives. # -do_catchsql_test 6.1 { - CREATE VIRTUAL TABLE abc USING fts5(a, prefix=1, prefix=2); -} {1 {multiple prefix=... directives}} do_catchsql_test 6.2 { CREATE VIRTUAL TABLE abc USING fts5(a, prefix='1, 2, 1001'); -} {1 {prefix length out of range: 1001}} +} {1 {prefix length out of range (max 999)}} do_catchsql_test 6.3 { CREATE VIRTUAL TAbLE abc USING fts5(a, prefix='1, 2, 0000'); -} {1 {prefix length out of range: 0}} +} {1 {prefix length out of range (max 999)}} do_catchsql_test 6.4 { CREATE VIRTUAL TABLE abc USING fts5(a, prefix='1 , 1000000'); -} {1 {malformed prefix=... directive}} +} {1 {prefix length out of range (max 999)}} #------------------------------------------------------------------------- # Duplicate tokenize= and other options. @@ -204,5 +203,16 @@ do_catchsql_test 9.4.1 { INSERT INTO abc(abc, rank) VALUES('nosuchoption', 1); } {1 {SQL logic error or missing database}} +#------------------------------------------------------------------------- +# Too many prefix indexes. Maximum allowed is 31. +# +foreach {tn spec} { + 1 {prefix="1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32"} + 2 {prefix="1 2 3 4", prefix="5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32"} +} { + set sql "CREATE VIRTUAL TABLE xyz USING fts5(x, $spec)" + do_catchsql_test 10.$tn $sql {1 {too many prefix indexes (max 31)}} +} + finish_test diff --git a/ext/fts5/test/fts5prefix.test b/ext/fts5/test/fts5prefix.test index 70de0bc78a..8e0d5a2954 100644 --- a/ext/fts5/test/fts5prefix.test +++ b/ext/fts5/test/fts5prefix.test @@ -298,6 +298,48 @@ foreach {tn col pattern} { do_execsql_test 6.$tn { SELECT rowid FROM t5($query) } $res } +#------------------------------------------------------------------------- +# Check that the various ways of creating prefix indexes produce the +# same database on disk. +# +save_prng_state +foreach {tn create} { + 1 { CREATE VIRTUAL TABLE tt USING fts5(x, y, prefix="1,2,3") } + 2 { CREATE VIRTUAL TABLE tt USING fts5(x, y, prefix="1 2 3") } + 3 { CREATE VIRTUAL TABLE tt USING fts5(x, y, prefix=1, prefix=2, prefix=3) } + 4 { CREATE VIRTUAL TABLE tt USING fts5(x, y, prefix="1 2", prefix=3) } +} { + execsql { DROP TABLE IF EXISTS tt } + restore_prng_state + execsql $create + execsql { + INSERT INTO tt VALUES('cc b ggg ccc aa eee hh', 'aa g b hh a e'); + INSERT INTO tt VALUES('cc bb cc gg j g cc', 'ii jjj ggg jjj cc cc'); + INSERT INTO tt VALUES('h eee cc h iii', 'aaa iii dd iii dd'); + INSERT INTO tt VALUES('jjj hh eee c e b gg', 'j bbb jj ddd jj'); + INSERT INTO tt VALUES('ii hhh aaa ff c hhh iii', 'j cc hh bb e'); + INSERT INTO tt VALUES('e fff hhh i aaa', 'g b aa gg c aa dd'); + INSERT INTO tt VALUES('i aaa ccc gg hhh aa h', 'j bbb bbb d ff'); + INSERT INTO tt VALUES('g f gg ff ff jjj d', 'jjj d j fff fff ee j'); + INSERT INTO tt VALUES('a cc e ccc jjj c', 'ccc iii d bb a eee g'); + INSERT INTO tt VALUES('jj hh hh bb bbb gg', 'j c jjj bb iii f'); + INSERT INTO tt VALUES('a ggg g cc ccc aa', 'jjj j j aaa c'); + INSERT INTO tt VALUES('ddd j dd b i', 'aaa bbb iii ggg ff ccc ddd'); + INSERT INTO tt VALUES('jj ii hh c ii h gg', 'hhh bbb ddd bbb hh g ggg'); + INSERT INTO tt VALUES('aa hhh ccc h ggg ccc', 'iii d jj a ff ii'); + } + + #db eval {SELECT rowid, fts5_decode(rowid, block) aS r FROM tt_data} {puts $r} + + if {$tn==1} { + set ::checksum [execsql {SELECT md5sum(id, block) FROM tt_data}] + } else { + do_execsql_test 7.$tn { + SELECT md5sum(id, block) FROM tt_data + } [list $::checksum] + } +} + finish_test diff --git a/main.mk b/main.mk index 45bb6e9c1d..b925448d2a 100644 --- a/main.mk +++ b/main.mk @@ -317,6 +317,7 @@ TESTSRC = \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/test_thread.c \ $(TOP)/src/test_vfs.c \ + $(TOP)/src/test_windirent.c \ $(TOP)/src/test_wsd.c # Extensions to be statically loaded. diff --git a/manifest b/manifest index dfc4906ec6..78db225910 100644 --- a/manifest +++ b/manifest @@ -1,8 +1,8 @@ -C Merge\sthe\slatest\senhancements\sfrom\strunk. -D 2015-11-19T19:40:40.385 -F Makefile.in c9dfc95717a5af63ab90b36f22afd11e865e3559 +C Merge\srecent\senhancements\sfrom\strunk. +D 2015-12-02T20:40:26.162 +F Makefile.in e7d3be066453d66f333e7cb98f4c9d3e700bfc09 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 -F Makefile.msc 4f04d3dbe993b706c956509b1437559d7f2469b1 +F Makefile.msc 820453f6340c500e2377a6ebd681cecfa92afd79 F README.md 8ecc12493ff9f820cdea6520a9016001cb2e59b7 F VERSION 8b9d3ac6f1962f94e06ba05462422a544f9c4e36 F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 @@ -106,7 +106,7 @@ F ext/fts5/fts5.h 8b9a13b309b180e9fb88ea5666c0d8d73c6102d9 F ext/fts5/fts5Int.h acf968e43d57b6b1caf7554d34ec35d6ed3b4fe8 F ext/fts5/fts5_aux.c 1f384972d606375b8fa078319f25ab4b5feb1b35 F ext/fts5/fts5_buffer.c 1e49512a535045e621246dc7f4f65f3593fa0fc2 -F ext/fts5/fts5_config.c 6fc92c0b1bda5244c28a54c9ba740736bd5513d9 +F ext/fts5/fts5_config.c 0ee66188609a62342e9f9aeefa3c3e44518a4dd6 F ext/fts5/fts5_expr.c 28b15c9ae296204bc0a2e5cf7a667d840a9d2900 F ext/fts5/fts5_hash.c a9d4c1efebc2a91d26ad7ebdfcbf2678ceac405f F ext/fts5/fts5_index.c b622a0a70f57a96469e6828da2dd70e0872aeb37 @@ -139,7 +139,7 @@ F ext/fts5/test/fts5aux.test 8c687c948cc98e9a94be014df7d518acc1b3b74f F ext/fts5/test/fts5auxdata.test 141a7cbffcceb1bd2799b4b29c183ff8780d586e F ext/fts5/test/fts5bigpl.test 04ee0d7eebbebf17c31f5a0b5c5f9494eac3a0cb F ext/fts5/test/fts5columnsize.test a8cfef21ffa1c264b9f670a7d94eeaccb5341c07 -F ext/fts5/test/fts5config.test ad2ff42ddc856aed2d05bf89dc1c578c8a39ea3b +F ext/fts5/test/fts5config.test 42c1336cc6ed33d7e9c4a05dbce81721b765e7d0 F ext/fts5/test/fts5conflict.test 26f4e46c4d31e16221794832a990dc4e30e18de5 F ext/fts5/test/fts5content.test 9a952c95518a14182dc3b59e3c8fa71cda82a4e1 F ext/fts5/test/fts5corrupt.test c2ad090192708150d50d961278df10ae7a4b8b62 @@ -168,7 +168,7 @@ F ext/fts5/test/fts5phrase.test f6d1d464da5beb25dc56277aa4f1d6102f0d9a2f F ext/fts5/test/fts5plan.test 6a55ecbac9890765b0e16f8c421c7e0888cfe436 F ext/fts5/test/fts5porter.test 7cdc07bef301d70eebbfa75dcaf45c3680e1d0e1 F ext/fts5/test/fts5porter2.test 2e65633d58a1c525d5af0f6c01e5a59155bb3487 -F ext/fts5/test/fts5prefix.test 7ccbdf180ed561a912acef520519e85af8642239 +F ext/fts5/test/fts5prefix.test efd42e00bb8e8a36383f25c838185508681c093f F ext/fts5/test/fts5query.test f5ec25f5f2fbb70033424113cdffc101b1985a40 F ext/fts5/test/fts5rank.test 7e9e64eac7245637f6f2033aec4b292aaf611aab F ext/fts5/test/fts5rebuild.test 03935f617ace91ed23a6099c7c74d905227ff29b @@ -283,7 +283,7 @@ F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 -F main.mk 4f8d448adb9a7302cd5106f583ddc47067f1017d +F main.mk 65524a49d6bbeb624b5953ed50a8c84d9dc34d16 F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504 @@ -296,26 +296,26 @@ F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a F src/alter.c 9d649e46c780166e416fb11dbd23f8d49aab8267 -F src/analyze.c 4c308880cf53c558070cb8513bdff4ffb1a38a77 +F src/analyze.c 977bd50c751bb939ef52917892e12bedbfcea7ce F src/attach.c e944d0052b577703b9b83aac1638452ff42a8395 F src/auth.c b56c78ebe40a2110fd361379f7e8162d23f92240 F src/backup.c 2869a76c03eb393ee795416e2387005553df72bc -F src/bitvec.c d1f21d7d91690747881f03940584f4cc548c9d3d +F src/bitvec.c 1a78d450a17c5016710eec900bedfc5729bf9bdf F src/btmutex.c 45a968cc85afed9b5e6cf55bf1f42f8d18107f79 F src/btree.c d3bdd8462a86492e2ebc9aca4a0168429017de25 F src/btree.h 2d76dee44704c47eed323356a758662724b674a0 F src/btreeInt.h 3ab435ed27adea54d040584b0bcc488ee7db1e38 -F src/build.c 5a3b71786e2b96d2bb92d40f190eb1fe736f25ca +F src/build.c e83da4d004a4e050c01acbb821ff7a7b1019c29b F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0 F src/complete.c addcd8160b081131005d5bc2d34adf20c1c5c92f -F src/ctime.c 509ef9c64d1321f42448f111da86400b1799218a +F src/ctime.c 58eda76364fb6f374e044aa4493219b13abf9400 F src/date.c fb1c99172017dcc8e237339132c91a21a0788584 F src/dbstat.c ffd63fc8ba7541476ced189b95e95d7f2bc63f78 F src/delete.c 86e3940d07fe69a40270c2aaf6ca6c7adf19246c -F src/expr.c 0080c0f12806eca91e75a23a121a68918e9da357 +F src/expr.c cb1a419508e5b27769a91e00e36e94724e7b1d51 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c 31900763094a3736a5fc887469202eb579fef2d0 -F src/func.c ecdd69ec6a1e406f04cc73324be2ebbf6354197f +F src/func.c fe50a9ab977acc0bb0fcd46741e0071fa388888e F src/global.c 508e4087f7b41d688e4762dcf4d4fe28cfbc87f9 F src/hash.c 4263fbc955f26c2e8cdc0cf214bc42435aa4e4f5 F src/hash.h c8f3c31722cf3277d03713909761e152a5b81094 @@ -324,47 +324,47 @@ F src/insert.c 8ab83219eb56a103edb73bb0a1e9057c4d3b976b F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d F src/legacy.c ba1863ea58c4c840335a84ec276fc2b25e22bc4e F src/loadext.c 18586e45a215325f15096821e9c082035d4fb810 -F src/main.c 59798e68b7f93eaf243b2b4d054dd4eb6ead907a +F src/main.c cf9fcb71ea8ea46ad20b37dc1af67c1a17cf73b5 F src/malloc.c 337bbe9c7d436ef9b7d06b5dd10bbfc8f3025972 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c 6919bcf12f221868ea066eec27e579fed95ce98b F src/mem2.c f1940d9e91948dd6a908fbb9ce3835c36b5d83c3 F src/mem3.c 8768ac94694f31ffaf8b4d0ea5dc08af7010a35a -F src/mem5.c c1ab1153bd6443bdf6f71e4213c6fb31221b9eb7 +F src/mem5.c 5c267678ba9f745a2ee58102a9f482d64a58577a F src/memjournal.c 3eb2c0b51adbd869cb6a44780323f05fa904dc85 F src/msvc.h d9ba56c6851227ab44b3f228a35f3f5772296495 F src/mutex.c 8e45800ee78e0cd1f1f3fe8e398853307f4a085c F src/mutex.h 779d588e3b7756ec3ecf7d78cde1d84aba414f85 F src/mutex_noop.c 9d4309c075ba9cc7249e19412d3d62f7f94839c4 -F src/mutex_unix.c a94b46f3f7beba307dde1b298b0f99f9c3677a93 +F src/mutex_unix.c fc54f25b2a750d53b32512a4a728cec28039ae2a F src/mutex_w32.c 5e6fe1c298fb5a8a15aaed4161d5759311431c17 F src/notify.c 9711a7575036f0d3040ba61bc6e217f13a9888e7 F src/os.c 8fd25588eeba74068d41102d26810e216999b6c8 F src/os.h 3e57a24e2794a94d3cf2342c6d9a884888cd96bf F src/os_common.h abdb9a191a367793268fe553d25bab894e986a0e F src/os_setup.h c9d4553b5aaa6f73391448b265b89bed0b890faa -F src/os_unix.c eb24e0340fbe3cfd0eabfb15a71476953e54fa73 -F src/os_win.c 2d77dab5c555a18c0aff379c6a692fc3499044d9 +F src/os_unix.c 60997373a8d90bd17e1c0e49d11ef361b713439b +F src/os_win.c 386fba30419e8458b13209781c2af5590eab2811 F src/os_win.h eb7a47aa17b26b77eb97e4823f20a00b8bda12ca -F src/pager.c 18341e2b759b447cbc82fb9215d08d9c5864e92e -F src/pager.h 87c4118a71ba3965184148b379a6d93179071091 +F src/pager.c f92aacd5216d8815136c9e0190041783c602641a +F src/pager.h 9153c71a89dc82a5a77e485f3929792116c70aae F src/parse.y 23737e649c26ce327603799e57f5c2ff50e5e6ba F src/pcache.c 73895411fa6b7bd6f0091212feabbe833b358d23 F src/pcache.h 1ff11adce609ba7de139b6abfabaf9a2bac947b5 -F src/pcache1.c 902e1bc7bdaa81b40f8543407c5e2ac8ef4dc035 +F src/pcache1.c 46a110be31a8d9f9b41431733836822ca0dd27ab F src/pragma.c f3e7147299ca05ef4304a36f1fd6e002729c72c6 F src/pragma.h 3d94aebbebd2089899fecc01909bf2608b39507d F src/prepare.c 82e5db1013846a819f198336fed72c44c974e7b1 -F src/printf.c f8fc8f04e75b1e983ef2793c27ec7a43b287e94a +F src/printf.c ca05561795ad6c2fa47acdd007702586282f7feb F src/random.c ba2679f80ec82c4190062d756f22d0c358180696 -F src/resolve.c 1954a0f01bf65d78d7d559aea3d5c67f33376d91 +F src/resolve.c f4c897ca76ca6d5e0b3f0499c627392ffe657c8e F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e -F src/select.c 2376d320907a5c28c55290f18fd94aa3400bf97c -F src/shell.c 072fc3601b0ac02e50939b469fabeecfe3d66dcd -F src/sqlite.h.in eed36953a0b5ce6c099b0cb2cb6b6ba220e6ca77 +F src/select.c 1611828a7116e5f6cc1e69cd07d59b0d2c662ea9 +F src/shell.c 98bf4a35a121c9d4b91940a53794f2cbc354c45c +F src/sqlite.h.in fc89d305439df88cfe018dbbaf1770645766fa2f F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad -F src/sqlite3ext.h 4b66e3e3435da4b4c8c83696d0349f0c503b3924 -F src/sqliteInt.h d85782b8b5b2057ac015f7ec20195b7ce869960d +F src/sqlite3ext.h 41ef50b0418a7c5ad1337bb80db5a7928dee764f +F src/sqliteInt.h 0bf170994dbc42d4344eed5d59d88ed0631aa4d1 F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46 F src/status.c 70912d7be68e9e2dbc4010c93d344af61d4c59ba F src/table.c 51b46b2a62d1b3a959633d593b89bab5e2c9155e @@ -376,17 +376,17 @@ F src/test4.c d168f83cc78d02e8d35567bb5630e40dcd85ac1e F src/test5.c 5a34feec76d9b3a86aab30fd4f6cc9c48cbab4c1 F src/test6.c 41cacf3b0dd180823919bf9e1fbab287c9266723 F src/test7.c 9c89a4f1ed6bb13af0ed805b8d782bd83fcd57e3 -F src/test8.c 610e3d523018ca63b08081795e76794a2121ec38 +F src/test8.c b3a416298dab54537d5aec68697a8b31f5ddf69c F src/test9.c bea1e8cf52aa93695487badedd6e1886c321ea60 F src/test_async.c 21e11293a2f72080eda70e1124e9102044531cd8 F src/test_autoext.c dea8a01a7153b9adc97bd26161e4226329546e12 F src/test_backup.c 2e6e6a081870150f20c526a2e9d0d29cda47d803 F src/test_blob.c e5a7a81d61a780da79101aeb1e60d300af169e07 F src/test_btree.c 2e9978eca99a9a4bfa8cae949efb00886860a64f -F src/test_config.c a25edf31cc0dbe17f5d1d667b1320cbb4e52b6dd +F src/test_config.c bae65b68a216f31e225a46187497bdc007c1c5e5 F src/test_demovfs.c 0de72c2c89551629f58486fde5734b7d90758852 F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc -F src/test_fs.c ced436e3d4b8e4681328409b8081051ce614e28f +F src/test_fs.c aab47ac456316502faa265daadf9ac832fea12b9 F src/test_func.c 0d9c25956152adefee8881c6fadc8354793764d0 F src/test_hexio.c abfdecb6fa58c354623978efceb088ca18e379cd F src/test_init.c 66b33120ffe9cd853b5a905ec850d51151337b32 @@ -394,7 +394,7 @@ F src/test_intarray.c 870124b95ec4c645d4eb84f15efb7133528fb1a5 F src/test_intarray.h 9dc57417fb65bc7835cc18548852cc08cc062202 F src/test_journal.c 5360fbe1d1e4416ca36290562fd5a2e3f70f32aa F src/test_loadext.c a5251f956ab6af21e138dc1f9c0399394a510cb4 -F src/test_malloc.c 27d9e11b6e9d30267465d41ad81edbe24256408b +F src/test_malloc.c 8e1c2112fa4f543b46c990a8872f9e6d37938744 F src/test_multiplex.c 9fefd23f6cc3fa9bf0748a5e453167e7b9f193ce F src/test_multiplex.h c08e4e8f8651f0c5e0509b138ff4d5b43ed1f5d3 F src/test_mutex.c dbdfaff8580071f2212a0deae3325a93a737819c @@ -409,37 +409,39 @@ F src/test_server.c a2615049954cbb9cfb4a62e18e2f0616e4dc38fe F src/test_sqllog.c 0d138a8180a312bf996b37fa66da5c5799d4d57b F src/test_superlock.c 06797157176eb7085027d9dd278c0d7a105e3ec9 F src/test_syscall.c 2e21ca7f7dc54a028f1967b63f1e76155c356f9b -F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa +F src/test_tclvar.c d86412527da65468ee6fa1b8607c65d0af736bc4 F src/test_thread.c af391ec03d23486dffbcc250b7e58e073f172af9 F src/test_vfs.c 3b65d42e18b262805716bd96178c81da8f2d9283 F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698 +F src/test_windirent.c 8f5fada630348558d5745b334702f301da1ffc61 +F src/test_windirent.h b12055cab6227f7be10f5c19296f67c60cc5e2a5 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/threads.c bbfb74450643cb5372a43ad4f6cffd7e9dfcecb0 F src/tokenize.c 5606871a377f390af7040ec3c12e0d183512d785 F src/treeview.c 78842e90c1f71269e7a73a1d4221b6fe360bab66 F src/trigger.c 322f23aad694e8f31d384dcfa386d52a48d3c52f -F src/update.c c2fff3b60bfcabcb0372acbe8cf6f77c2bb515bc +F src/update.c d8d675aa299336ac086ad2039d7e812cd6237db0 F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c -F src/util.c fc612367108b74573c5fd13a85d0a23027f438bd +F src/util.c e802e8e311a0d6c48cd1b3e89db164f6f0248d70 F src/vacuum.c 2ddd5cad2a7b9cef7f9e431b8c7771634c6b1701 -F src/vdbe.c 9539aa0ccf4844b8b59466ef3c15dbffb479a390 +F src/vdbe.c 5c3dd0f49bfebfddbfa4d389ed394b2b28d48cbf F src/vdbe.h bfe3f80dba435377cdb64fd917f2529f0f48ab77 -F src/vdbeInt.h d6ae6e64ad16e213f37306b1735ff72fb98bec69 -F src/vdbeapi.c f5eda36a5c85ef578957ab4311e8d9b1f51a3552 -F src/vdbeaux.c 67db9a1c132207f4d6a8f4e76531cea39539fcaa -F src/vdbeblob.c b400c25ac822af3c507ef84b5cd93c1583a70321 +F src/vdbeInt.h 4f3b46806b93faa92f0511c7ce30ed31aaec65be +F src/vdbeapi.c ab2cb8fe23fb9f3195f1311eaa800495d83b6118 +F src/vdbeaux.c 514e93b162bf3e84c82478f174728cda9135b558 +F src/vdbeblob.c cc13eca96b8ec51b6248de785a1aec5df11f5805 F src/vdbemem.c 25b6cfd665b5073480452426e84136edd94140c0 -F src/vdbesort.c 8b23930a1289526f6d2a3a9f2e965bcc963e4a68 +F src/vdbesort.c a7ec02da4494c59dfd071126dd3726be5a11459d F src/vdbetrace.c 8befe829faff6d9e6f6e4dee5a7d3f85cc85f1a0 F src/vtab.c 2a8b44aa372c33f6154208e7a7f6c44254549806 F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb -F src/wal.c 18b0ed49830cf04fe2d68224b41838a73ac6cd24 +F src/wal.c 1569802364cd192bbd5c4a8ea3fd6de593edecbd F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 F src/walker.c 2e14d17f592d176b6dc879c33fbdec4fbccaa2ba -F src/where.c 6aceb72cc58dc06922a9e1604d559c8ca4c3e728 -F src/whereInt.h 7892bb54cf9ca0ae5c7e6094491b94c9286dc647 -F src/wherecode.c 4c96182e7b25e4be54008dee2da5b9c2f8480b9b -F src/whereexpr.c e63244ca06c503e5f3c5b7f3c9aea0db826089ed +F src/where.c b18edbb9e5afabb77f4f27550c471c5c824e0fe7 +F src/whereInt.h e20801d89e34de1912bb6a3babb30c390da27add +F src/wherecode.c dfbfe198e418b01f208b489e088edd230c91a4e7 +F src/whereexpr.c eebba8340c90de73b3d3bbe8c43b84559b8e6e2c F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -453,7 +455,7 @@ F test/alter4.test c461150723ac957f3b2214aa0b11552cd72023ec F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f F test/analyze.test 3eb35a4af972f98422e5dc0586501b17d103d321 -F test/analyze3.test 0f0ee6135b293a0e5af471a8423b80b688469d71 +F test/analyze3.test 1dccda46a6c374018af617fba00bfe297a61d442 F test/analyze4.test eff2df19b8dd84529966420f29ea52edc6b56213 F test/analyze5.test 765c4e284aa69ca172772aa940946f55629bc8c4 F test/analyze6.test f1c552ce39cca4ec922a7e4e0e5d0203d6b3281f @@ -526,7 +528,7 @@ F test/cacheflush.test a755c93482ce2e20c04825304bef27e7b7ea0111 F test/capi2.test 011c16da245fdc0106a2785035de6b242c05e738 F test/capi3.test bf6f0308bbbba1e770dac13aa08e5c2ac61c7324 F test/capi3b.test efb2b9cfd127efa84433cd7a2d72ce0454ae0dc4 -F test/capi3c.test fdc0d67a2cb8e8fc400d5b7735e330161ea057a2 +F test/capi3c.test 06f6261f9e9b4ef6f76afcd9900f3665408af1c8 F test/capi3d.test 485048dc5cd07bc68011e4917ad035ad6047ab82 F test/capi3e.test 3d49c01ef2a1a55f41d73cba2b23b5059ec460fe F test/cast.test 4c275cbdc8202d6f9c54a3596701719868ac7dc3 @@ -599,7 +601,7 @@ F test/descidx1.test 6d03b44c8538fe0eb4924e19fba10cdd8f3c9240 F test/descidx2.test 9f1a0c83fd57f8667c82310ca21b30a350888b5d F test/descidx3.test 09ddbe3f5295f482d2f8b687cf6db8bad7acd9a2 F test/diskfull.test 106391384780753ea6896b7b4f005d10e9866b6e -F test/distinct.test 175d49ee783febaf368192dfe7f5afbc68910230 +F test/distinct.test a1783b960ad8c15a77cd9f207be072898db1026c F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376 F test/e_blobbytes.test 9bea1d3e2b20f3010b04abba58f6ba172301f49f F test/e_blobclose.test df756753f571bc30e42e3a6cba2807576e49e716 @@ -628,7 +630,7 @@ F test/e_walckpt.test 65e29b6631e51f210f83e4ff11571e647ba93608 F test/e_walhook.test da3ea8b3483d1af72190337bda50155a91a4b664 F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea F test/enc2.test 83437a79ba1545a55fb549309175c683fb334473 -F test/enc3.test 90683ad0e6ea587b9d5542ca93568af9a9858c40 +F test/enc3.test 6807f7a7740a00361ca8d0ccd66bc60c8dc5f2b6 F test/enc4.test c8f1ce3618508fd0909945beb8b8831feef2c020 F test/eqp.test 85873fa5816c48915c82c4e74cb5c35a5b48160f F test/errmsg.test f31592a594b44ee121371d25ddd5d63497bb3401 @@ -748,7 +750,7 @@ F test/fts3tok_err.test 52273cd193b9036282f7bacb43da78c6be87418d F test/fts3varint.test 752c08ed5d32c5d7dc211b056f4ed68a76b7e36e F test/fts4aa.test 10aac8e9d62c7357590acfabe3fad01e9a9ce1cb F test/fts4check.test 9d9e818fd6cb29c0e007cd6d00447739d4fde430 -F test/fts4content.test abb0c77bc3da3df64fec72e00844d2257a90025d +F test/fts4content.test 8707425b926663f8ca81de866c007900442b5ec0 F test/fts4docid.test e33c383cfbdff0284685604d256f347a18fdbf01 F test/fts4growth.test df10fde9f47cf5c71861e63fd8efcd573c4f7e53 F test/fts4growth2.test 2f063be1902a73cd087355837c52fed42ac11a5d @@ -773,7 +775,7 @@ F test/fuzz2.test 76dc35b32b6d6f965259508508abce75a6c4d7e1 F test/fuzz3.test 53fabcd5f0f430f8b221282f6c12c4d0903c21eb F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b F test/fuzz_malloc.test 328f70aaca63adf29b4c6f06505ed0cf57ca7c26 -F test/fuzzcheck.c 7c61352f20a28429b221f406f3854cf9c912f63b +F test/fuzzcheck.c ccdcdc28579b2c9744696bca8726bdbd729eea11 F test/fuzzdata1.db 7ee3227bad0e7ccdeb08a9e6822916777073c664 F test/fuzzdata2.db f03a420d3b822cc82e4f894ca957618fbe9c4973 F test/fuzzdata3.db c6586d3e3cef0fbc18108f9bb649aa77bfc38aba @@ -781,7 +783,7 @@ F test/fuzzdata4.db 1882f0055fb63214d8407ddc7aca9b0b1c59af21 F test/fuzzer1.test d4c52aaf3ef923da293a2653cfab33d02f718a36 F test/fuzzerfault.test 8792cd77fd5bce765b05d0c8e01b9edcf8af8536 F test/genesis.tcl 1e2e2e8e5cc4058549a154ff1892fe5c9de19f98 -F test/hexlit.test 1d312fa816dfd3650a3bb488093bc09a0c927f67 +F test/hexlit.test d7b0a5f41123df1e43985b91b8b2e70f95282d21 F test/hidden.test 23c1393a79e846d68fd902d72c85d5e5dcf98711 F test/hook.test aa41c095d26822b8a51aa4c82904a14347961be6 F test/icu.test 70df4faca133254c042d02ae342c0a141f2663f4 @@ -847,9 +849,9 @@ F test/json102.test bf3fe7a706d30936a76a0f7a0375e1e8e73aff5a F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63 F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200 -F test/like.test 4f2a71d36a536233727f71995fef900756705e56 +F test/like.test edcf6d0a055816fd793eab92454597c1be91f720 F test/like2.test 3b2ee13149ba4a8a60b59756f4e5d345573852da -F test/like3.test 7b0525a39e4f25c4fd113de7e2e28eb712dcdedf +F test/like3.test 3608a2042b6f922f900fbfd5d3ce4e7eca57f7c4 F test/limit.test 0c99a27a87b14c646a9d583c7c89fd06c352663e F test/loadext.test 648cb95f324d1775c54a55c12271b2d1156b633b F test/loadext2.test 0408380b57adca04004247179837a18e866a74f7 @@ -896,7 +898,7 @@ F test/minmax.test 42fbad0e81afaa6e0de41c960329f2b2c3526efd F test/minmax2.test b44bae787fc7b227597b01b0ca5575c7cb54d3bc F test/minmax3.test cc1e8b010136db0d01a6f2a29ba5a9f321034354 F test/minmax4.test 936941484ebdceb8adec7c86b6cd9b6e5e897c1f -F test/misc1.test d614a334b777b1aded6873d76a3560329b819fea +F test/misc1.test 48ebfb5b22a6a058f7b7e1df211226dd1d21409c F test/misc2.test 00d7de54eda90e237fc9a38b9e5ccc769ebf6d4d F test/misc3.test cf3dda47d5dda3e53fc5804a100d3c82be736c9d F test/misc4.test 0d8be3466adf123a7791a66ba2bc8e8d229e87f3 @@ -1059,7 +1061,7 @@ F test/speedtest1.c f8bf04214e7b5f745feea99f7bde68b1c4870666 F test/spellfix.test 0597065ff57042df1f138e6a2611ae19c2698135 F test/spellfix2.test dfc8f519a3fc204cb2dfa8b4f29821ae90f6f8c3 F test/sqldiff1.test 8f6bc7c6a5b3585d350d779c6078869ba402f8f5 -F test/sqllimits1.test 89b3d5aad05b99f707ee3786bdd4416dccf83304 +F test/sqllimits1.test a74ee2a3740b9f9c2437c246d8fb77354862a142 F test/sqllog.test a8faa2df39610a037dd372ed872d124260d32953 F test/stat.test 8de91498c99f5298b303f70f1d1f3b9557af91bf F test/statfault.test f525a7bf633e50afd027700e9a486090684b1ac1 @@ -1070,11 +1072,11 @@ F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4 F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a F test/subtype1.test 7fe09496352f97053af1437150751be2d0a0cae8 F test/superlock.test 1cde669f68d2dd37d6c9bd35eee1d95491ae3fc2 -F test/symlink.test 2513f7c030df0f435c6415687ba8b739f3d312df +F test/symlink.test cbf6cb8c6c4b63a39e9f0f6b0d5c99e249dbc102 F test/sync.test a34cd43e98b7fb84eabbf38f7ed8f7349b3f3d85 -F test/syscall.test fba9ebdc6905d05bba6a835e691f20ed9ea2cc88 +F test/syscall.test 2aa9e111b79fb385681ff8940124def6f8faab87 F test/sysfault.test fa776e60bf46bdd3ae69f0b73e46ee3977a58ae6 -F test/tabfunc01.test 03c4ad422c6ab596cff6dcaf86dd061a9f039525 +F test/tabfunc01.test cc33684f9480fcf1fd5ce287ac28d22971cad1cc F test/table.test b708f3e5fa2542fa51dfab21fc07b36ea445cb2f F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126 F test/tableopts.test dba698ba97251017b7c80d738c198d39ab747930 @@ -1093,7 +1095,7 @@ F test/thread2.test f35d2106452b77523b3a2b7d1dcde2e5ee8f9e46 F test/thread_common.tcl 334639cadcb9f912bf82aa73f49efd5282e6cadd F test/threadtest1.c 6029d9c5567db28e6dc908a0c63099c3ba6c383b F test/threadtest2.c a70a8e94bef23339d34226eb9521015ef99f4df8 -F test/threadtest3.c 9ab4b168681c3a6f70f6c833ba08e0d48dd4af9b +F test/threadtest3.c 0707c28e0954acbef654f91675e14f3573cf9a10 F test/threadtest4.c c1e67136ceb6c7ec8184e56ac61db28f96bd2925 F test/tkt-02a8e81d44.test 6c80d9c7514e2a42d4918bf87bf6bc54f379110c F test/tkt-26ff0c2d1e.test 888324e751512972c6e0d1a09df740d8f5aaf660 @@ -1225,7 +1227,7 @@ F test/tkt3824.test 150aa00bb6220672e5f0eb14dc8eaa36750425f0 F test/tkt3832.test 2300d10d57562b89875b72148338ac3e14f8847d F test/tkt3838.test 292e72489101cd1320d7278dc111c173ebf334d4 F test/tkt3841.test 4659845bc53f809a5932c61c6ce8c5bb9d6b947f -F test/tkt3871.test 43ecbc8d90dc83908e2a454aef345acc9d160c6f +F test/tkt3871.test d921703d07c68f4fd5312073215a17fa34b0401d F test/tkt3879.test 2ad5bef2c87e9991ce941e054c31abe26ef7fb90 F test/tkt3911.test 74cd324f3ba653040cc6d94cc4857b290d12d633 F test/tkt3918.test ea78bf164e4d55cbde0d83c671ef6fbe930a0032 @@ -1280,10 +1282,10 @@ F test/vacuum4.test d3f8ecff345f166911568f397d2432c16d2867d9 F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102 F test/veryquick.test 57ab846bacf7b90cf4e9a672721ea5c5b669b661 F test/view.test f6c3a39e0c819891265e1d0754e99960d81ef6c9 -F test/vtab1.test 6210e076997f176bedc300a87ad6404651b601dd +F test/vtab1.test 7c4b81abd88361ada9cbe414c459efca26be6bda F test/vtab2.test f8cd1bb9aba7143eba97812d9617880a36d247ad F test/vtab3.test b45f47d20f225ccc9c28dc915d92740c2dee311e -F test/vtab4.test 942f8b8280b3ea8a41dae20e7822d065ca1cb275 +F test/vtab4.test 8e73ed268f3d596bc3590f45fc948fb40f28e9c3 F test/vtab5.test 889f444970393c73f1e077e2bdc5d845e157a391 F test/vtab6.test d2986cf418dc51e7fb81d12366bea2caa8b812df F test/vtab7.test ae560ebea870ed04e9aa4177cc302f910faaabb5 @@ -1293,8 +1295,10 @@ F test/vtabA.test 1317f06a03597eee29f40a49b6c21e1aaba4285f F test/vtabB.test 04df5dc531b9f44d9ca65b9c1b79f12b5922a796 F test/vtabC.test 4528f459a13136f982e75614d120aef165f17292 F test/vtabD.test 05b3f1d77117271671089e48719524b676842e96 -F test/vtabE.test 7c4693638d7797ce2eda17af74292b97e705cc61 +F test/vtabE.test d5024aa42754962f6bb0afd261681686488e7afe F test/vtabF.test fd5ad376f5a34fe0891df1f3cddb4fe7c3eb077e +F test/vtabH.test 2803a8773d1017ac93bbec6900fc7c35bb56c1c3 +F test/vtabI.test 751b07636700dbdea328e4265b6077ccd6811a3f F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5 F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8 F test/vtab_shared.test ea8778d5b0df200adef2ca7c00c3c37d4375f772 @@ -1332,7 +1336,7 @@ F test/where4.test 4a371bfcc607f41d233701bdec33ac2972908ba8 F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2 F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b F test/where7.test 5a4b0abc207d71da4deecd734ad8579e8dd40aa8 -F test/where8.test 2eafe74e01cc10355985874e1ff868ac03dbae5e +F test/where8.test 98eedca0d375fb400b8377269c4b4686582dfb45 F test/where8m.test da346596e19d54f0aba35ebade032a7c47d79739 F test/where9.test 729c3ba9b47e8f9f1aab96bae7dad2a524f1d1a2 F test/whereA.test 4d253178d135ec46d1671e440cd8f2b916aa6e6b @@ -1414,7 +1418,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 7ce07da76b5e745783e703a834417d725b7d45fd F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c -F tool/sqldiff.c db1232df457fdd4cbf2a919a497fc44bb18fb933 +F tool/sqldiff.c 653db1a9294e0b34849c4504599c282a18d8b8c6 F tool/stack_usage.tcl f8e71b92cdb099a147dad572375595eae55eca43 F tool/symbols-mingw.sh 4dbcea7e74768305384c9fd2ed2b41bbf9f0414d F tool/symbols.sh c5a617b8c61a0926747a56c65f5671ef8ac0e148 @@ -1425,7 +1429,7 @@ F tool/vdbe_profile.tcl 246d0da094856d72d2c12efec03250d71639d19f F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 78bc42e664e9fa9ee21ad9762c369f291fcdf5db 126b998cf163dcdd5a222634f1e929f04db3c700 -R d78a5057451ca68f4e40e727fe9ca899 +P 7d6cfc79e7e5534ebacd980479917bc528a638f7 4f2bcff94c672312805be1400050a7026f93a9d7 +R 50fae771d9c27e855af8c38f94348078 U drh -Z 251138aef0997d710e6be89ddc7546bd +Z 355385643ee65578020d9d55249a7cd0 diff --git a/manifest.uuid b/manifest.uuid index 77c3b5ff65..ca5a2e0553 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7d6cfc79e7e5534ebacd980479917bc528a638f7 \ No newline at end of file +d1a1278d7f3306536dc9cbd8fb300898f1e373e8 \ No newline at end of file diff --git a/src/analyze.c b/src/analyze.c index ad752d2c0e..06918eb744 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -990,7 +990,7 @@ static void analyzeOneTable( /* Do not gather statistics on views or virtual tables */ return; } - if( sqlite3_strnicmp(pTab->zName, "sqlite_", 7)==0 ){ + if( sqlite3_strlike("sqlite_%", pTab->zName, 0)==0 ){ /* Do not gather statistics on system tables */ return; } diff --git a/src/bitvec.c b/src/bitvec.c index fd908f791b..f7f544cff7 100644 --- a/src/bitvec.c +++ b/src/bitvec.c @@ -41,7 +41,8 @@ /* Round the union size down to the nearest pointer boundary, since that's how ** it will be aligned within the Bitvec struct. */ -#define BITVEC_USIZE (((BITVEC_SZ-(3*sizeof(u32)))/sizeof(Bitvec*))*sizeof(Bitvec*)) +#define BITVEC_USIZE \ + (((BITVEC_SZ-(3*sizeof(u32)))/sizeof(Bitvec*))*sizeof(Bitvec*)) /* Type of the array "element" for the bitmap representation. ** Should be a power of 2, and ideally, evenly divide into BITVEC_USIZE. diff --git a/src/build.c b/src/build.c index f928ba3075..765196f821 100644 --- a/src/build.c +++ b/src/build.c @@ -376,12 +376,7 @@ Table *sqlite3LocateTable( } pParse->checkSchema = 1; } -#if SQLITE_USER_AUTHENTICATION - else if( pParse->db->auth.authLevela[p->nSrc-1]; assert( pItem->fg.notIndexed==0 ); assert( pItem->fg.isIndexedBy==0 ); diff --git a/src/ctime.c b/src/ctime.c index 17dd710bc3..c3149ad913 100644 --- a/src/ctime.c +++ b/src/ctime.c @@ -158,6 +158,9 @@ static const char * const azCompileOpt[] = { #ifdef SQLITE_INT64_TYPE "INT64_TYPE", #endif +#ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS + "LIKE_DOESNT_MATCH_BLOBS", +#endif #if SQLITE_LOCK_TRACE "LOCK_TRACE", #endif diff --git a/src/expr.c b/src/expr.c index 6371e15ca7..8cf018f9d4 100644 --- a/src/expr.c +++ b/src/expr.c @@ -1889,9 +1889,10 @@ int sqlite3CodeSubselect( #ifndef SQLITE_OMIT_EXPLAIN if( pParse->explain==2 ){ - char *zMsg = sqlite3MPrintf( - pParse->db, "EXECUTE %s%s SUBQUERY %d", jmpIfDynamic>=0?"":"CORRELATED ", - pExpr->op==TK_IN?"LIST":"SCALAR", pParse->iNextSelectId + char *zMsg = sqlite3MPrintf(pParse->db, "EXECUTE %s%s SUBQUERY %d", + jmpIfDynamic>=0?"":"CORRELATED ", + pExpr->op==TK_IN?"LIST":"SCALAR", + pParse->iNextSelectId ); sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC); } @@ -3817,7 +3818,7 @@ int sqlite3ExprCompare(Expr *pA, Expr *pB, int iTab){ } return 2; } - if( pA->op!=TK_COLUMN && ALWAYS(pA->op!=TK_AGG_COLUMN) && pA->u.zToken ){ + if( pA->op!=TK_COLUMN && pA->op!=TK_AGG_COLUMN && pA->u.zToken ){ if( pA->op==TK_FUNCTION ){ if( sqlite3StrICmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2; }else if( strcmp(pA->u.zToken,pB->u.zToken)!=0 ){ diff --git a/src/func.c b/src/func.c index 8ea1169327..3fbd2b736b 100644 --- a/src/func.c +++ b/src/func.c @@ -763,6 +763,13 @@ int sqlite3_strglob(const char *zGlobPattern, const char *zString){ return patternCompare((u8*)zGlobPattern, (u8*)zString, &globInfo, 0)==0; } +/* +** The sqlite3_strlike() interface. +*/ +int sqlite3_strlike(const char *zPattern, const char *zStr, unsigned int esc){ + return patternCompare((u8*)zPattern, (u8*)zStr, &likeInfoNorm, esc)==0; +} + /* ** Count the number of times that the LIKE operator (or GLOB which is ** just a variation of LIKE) gets called. This is used for testing @@ -795,6 +802,17 @@ static void likeFunc( int nPat; sqlite3 *db = sqlite3_context_db_handle(context); +#ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS + if( sqlite3_value_type(argv[0])==SQLITE_BLOB + || sqlite3_value_type(argv[1])==SQLITE_BLOB + ){ +#ifdef SQLITE_TEST + sqlite3_like_count++; +#endif + sqlite3_result_int(context, 0); + return; + } +#endif zB = sqlite3_value_text(argv[0]); zA = sqlite3_value_text(argv[1]); diff --git a/src/main.c b/src/main.c index f7bab9b0ec..65434db0de 100644 --- a/src/main.c +++ b/src/main.c @@ -3441,6 +3441,9 @@ int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, void *pArg){ if( op==SQLITE_FCNTL_FILE_POINTER ){ *(sqlite3_file**)pArg = fd; rc = SQLITE_OK; + }else if( op==SQLITE_FCNTL_VFS_POINTER ){ + *(sqlite3_vfs**)pArg = sqlite3PagerVfs(pPager); + rc = SQLITE_OK; }else if( fd->pMethods ){ rc = sqlite3OsFileControl(fd, op, pArg); }else{ diff --git a/src/mem5.c b/src/mem5.c index 6bb24e5449..b34a04e8b6 100644 --- a/src/mem5.c +++ b/src/mem5.c @@ -241,7 +241,7 @@ static void *memsys5MallocUnsafe(int nByte){ } /* Round nByte up to the next valid power of two */ - for(iFullSz=mem5.szAtom, iLogsize=0; iFullSz0 { "mmap", (sqlite3_syscall_ptr)mmap, 0 }, -#define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[21].pCurrent) +#define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[22].pCurrent) { "munmap", (sqlite3_syscall_ptr)munmap, 0 }, -#define osMunmap ((void*(*)(void*,size_t))aSyscall[22].pCurrent) +#define osMunmap ((void*(*)(void*,size_t))aSyscall[23].pCurrent) #if HAVE_MREMAP { "mremap", (sqlite3_syscall_ptr)mremap, 0 }, #else { "mremap", (sqlite3_syscall_ptr)0, 0 }, #endif -#define osMremap ((void*(*)(void*,size_t,size_t,int,...))aSyscall[23].pCurrent) +#define osMremap ((void*(*)(void*,size_t,size_t,int,...))aSyscall[24].pCurrent) + { "getpagesize", (sqlite3_syscall_ptr)unixGetpagesize, 0 }, -#define osGetpagesize ((int(*)(void))aSyscall[24].pCurrent) +#define osGetpagesize ((int(*)(void))aSyscall[25].pCurrent) { "readlink", (sqlite3_syscall_ptr)readlink, 0 }, -#define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[25].pCurrent) +#define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[26].pCurrent) #endif }; /* End of the overrideable system calls */ + +/* +** On some systems, calls to fchown() will trigger a message in a security +** log if they come from non-root processes. So avoid calling fchown() if +** we are not running as root. +*/ +static int robustFchown(int fd, uid_t uid, gid_t gid){ +#if OS_VXWORKS + return 0; +#else + return osGeteuid() ? 0 : osFchown(fd,uid,gid); +#endif +} + /* ** This is the xSetSystemCall() method of sqlite3_vfs for all of the ** "unix" VFSes. Return SQLITE_OK opon successfully updating the @@ -755,23 +758,12 @@ static int robust_ftruncate(int h, sqlite3_int64 sz){ ** should handle ENOLCK, ENOTSUP, EOPNOTSUPP separately. */ static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) { + assert( (sqliteIOErr == SQLITE_IOERR_LOCK) || + (sqliteIOErr == SQLITE_IOERR_UNLOCK) || + (sqliteIOErr == SQLITE_IOERR_RDLOCK) || + (sqliteIOErr == SQLITE_IOERR_CHECKRESERVEDLOCK) ); switch (posixError) { -#if 0 - /* At one point this code was not commented out. In theory, this branch - ** should never be hit, as this function should only be called after - ** a locking-related function (i.e. fcntl()) has returned non-zero with - ** the value of errno as the first argument. Since a system call has failed, - ** errno should be non-zero. - ** - ** Despite this, if errno really is zero, we still don't want to return - ** SQLITE_OK. The system call failed, and *some* SQLite error should be - ** propagated back to the caller. Commenting this branch out means errno==0 - ** will be handled by the "default:" case below. - */ - case 0: - return SQLITE_OK; -#endif - + case EACCES: case EAGAIN: case ETIMEDOUT: case EBUSY: @@ -781,41 +773,9 @@ static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) { * introspection, in which it actually means what it says */ return SQLITE_BUSY; - case EACCES: - /* EACCES is like EAGAIN during locking operations, but not any other time*/ - if( (sqliteIOErr == SQLITE_IOERR_LOCK) || - (sqliteIOErr == SQLITE_IOERR_UNLOCK) || - (sqliteIOErr == SQLITE_IOERR_RDLOCK) || - (sqliteIOErr == SQLITE_IOERR_CHECKRESERVEDLOCK) ){ - return SQLITE_BUSY; - } - /* else fall through */ case EPERM: return SQLITE_PERM; -#if EOPNOTSUPP!=ENOTSUP - case EOPNOTSUPP: - /* something went terribly awry, unless during file system support - * introspection, in which it actually means what it says */ -#endif -#ifdef ENOTSUP - case ENOTSUP: - /* invalid fd, unless during file system support introspection, in which - * it actually means what it says */ -#endif - case EIO: - case EBADF: - case EINVAL: - case ENOTCONN: - case ENODEV: - case ENXIO: - case ENOENT: -#ifdef ESTALE /* ESTALE is not defined on Interix systems */ - case ESTALE: -#endif - case ENOSYS: - /* these should force the client to close the file and reconnect */ - default: return sqliteIOErr; } @@ -1099,7 +1059,7 @@ static unixInodeInfo *inodeList = 0; /* ** -** This function - unixLogError_x(), is only ever called via the macro +** This function - unixLogErrorAtLine(), is only ever called via the macro ** unixLogError(). ** ** It is invoked after an error occurs in an OS function and errno has been @@ -1268,7 +1228,7 @@ static int findInodeInfo( rc = osFstat(fd, &statbuf); if( rc!=0 ){ storeLastErrno(pFile, errno); -#ifdef EOVERFLOW +#if defined(EOVERFLOW) && defined(SQLITE_DISABLE_LFS) if( pFile->lastErrno==EOVERFLOW ) return SQLITE_NOLFS; #endif return SQLITE_IOERR; @@ -1355,30 +1315,21 @@ static int fileHasMoved(unixFile *pFile){ static void verifyDbFile(unixFile *pFile){ struct stat buf; int rc; - if( pFile->ctrlFlags & UNIXFILE_WARNED ){ - /* One or more of the following warnings have already been issued. Do not - ** repeat them so as not to clutter the error log */ - return; - } rc = osFstat(pFile->h, &buf); if( rc!=0 ){ sqlite3_log(SQLITE_WARNING, "cannot fstat db file %s", pFile->zPath); - pFile->ctrlFlags |= UNIXFILE_WARNED; return; } if( buf.st_nlink==0 && (pFile->ctrlFlags & UNIXFILE_DELETE)==0 ){ sqlite3_log(SQLITE_WARNING, "file unlinked while open: %s", pFile->zPath); - pFile->ctrlFlags |= UNIXFILE_WARNED; return; } if( buf.st_nlink>1 ){ sqlite3_log(SQLITE_WARNING, "multiple links to file: %s", pFile->zPath); - pFile->ctrlFlags |= UNIXFILE_WARNED; return; } if( fileHasMoved(pFile) ){ sqlite3_log(SQLITE_WARNING, "file renamed while open: %s", pFile->zPath); - pFile->ctrlFlags |= UNIXFILE_WARNED; return; } } @@ -1398,6 +1349,7 @@ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){ SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); assert( pFile ); + assert( pFile->eFileLock<=SHARED_LOCK ); unixEnterMutex(); /* Because pFile->pInode is shared across threads */ /* Check if a thread in this process holds such a lock */ @@ -1454,9 +1406,7 @@ static int unixFileLock(unixFile *pFile, struct flock *pLock){ unixInodeInfo *pInode = pFile->pInode; assert( unixMutexHeld() ); assert( pInode!=0 ); - if( ((pFile->ctrlFlags & UNIXFILE_EXCL)!=0 || pInode->bProcessLock) - && ((pFile->ctrlFlags & UNIXFILE_RDONLY)==0) - ){ + if( (pFile->ctrlFlags & (UNIXFILE_EXCL|UNIXFILE_RDONLY))==UNIXFILE_EXCL ){ if( pInode->bProcessLock==0 ){ struct flock lock; assert( pInode->nLock==0 ); @@ -1808,9 +1758,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ if( unixFileLock(pFile, &lock)==(-1) ){ tErrno = errno; rc = SQLITE_IOERR_UNLOCK; - if( IS_LOCK_ERROR(rc) ){ - storeLastErrno(pFile, tErrno); - } + storeLastErrno(pFile, tErrno); goto end_unlock; } lock.l_type = F_RDLCK; @@ -1832,9 +1780,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ if( unixFileLock(pFile, &lock)==(-1) ){ tErrno = errno; rc = SQLITE_IOERR_UNLOCK; - if( IS_LOCK_ERROR(rc) ){ - storeLastErrno(pFile, tErrno); - } + storeLastErrno(pFile, tErrno); goto end_unlock; } }else @@ -2085,17 +2031,7 @@ static int dotlockCheckReservedLock(sqlite3_file *id, int *pResOut) { SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); assert( pFile ); - - /* Check if a thread in this process holds such a lock */ - if( pFile->eFileLock>SHARED_LOCK ){ - /* Either this connection or some other connection in the same process - ** holds a lock on the file. No need to check further. */ - reserved = 1; - }else{ - /* The lock is held if and only if the lockfile exists */ - const char *zLockFile = (const char*)pFile->lockingContext; - reserved = osAccess(zLockFile, 0)==0; - } + reserved = osAccess((const char*)pFile->lockingContext, 0)==0; OSTRACE(("TEST WR-LOCK %d %d %d (dotlock)\n", pFile->h, rc, reserved)); *pResOut = reserved; return rc; @@ -2157,7 +2093,7 @@ static int dotlockLock(sqlite3_file *id, int eFileLock) { rc = SQLITE_BUSY; } else { rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); - if( IS_LOCK_ERROR(rc) ){ + if( rc!=SQLITE_BUSY ){ storeLastErrno(pFile, tErrno); } } @@ -2204,14 +2140,12 @@ static int dotlockUnlock(sqlite3_file *id, int eFileLock) { /* To fully unlock the database, delete the lock file */ assert( eFileLock==NO_LOCK ); rc = osRmdir(zLockFile); - if( rc<0 && errno==ENOTDIR ) rc = osUnlink(zLockFile); if( rc<0 ){ int tErrno = errno; - rc = 0; - if( ENOENT != tErrno ){ + if( tErrno==ENOENT ){ + rc = SQLITE_OK; + }else{ rc = SQLITE_IOERR_UNLOCK; - } - if( IS_LOCK_ERROR(rc) ){ storeLastErrno(pFile, tErrno); } return rc; @@ -2224,14 +2158,11 @@ static int dotlockUnlock(sqlite3_file *id, int eFileLock) { ** Close a file. Make sure the lock has been released before closing. */ static int dotlockClose(sqlite3_file *id) { - int rc = SQLITE_OK; - if( id ){ - unixFile *pFile = (unixFile*)id; - dotlockUnlock(id, NO_LOCK); - sqlite3_free(pFile->lockingContext); - rc = closeUnixFile(id); - } - return rc; + unixFile *pFile = (unixFile*)id; + assert( id!=0 ); + dotlockUnlock(id, NO_LOCK); + sqlite3_free(pFile->lockingContext); + return closeUnixFile(id); } /****************** End of the dot-file lock implementation ******************* ******************************************************************************/ @@ -2297,10 +2228,8 @@ static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){ int tErrno = errno; /* unlock failed with an error */ lrc = SQLITE_IOERR_UNLOCK; - if( IS_LOCK_ERROR(lrc) ){ - storeLastErrno(pFile, tErrno); - rc = lrc; - } + storeLastErrno(pFile, tErrno); + rc = lrc; } } else { int tErrno = errno; @@ -2433,12 +2362,9 @@ static int flockUnlock(sqlite3_file *id, int eFileLock) { ** Close a file. */ static int flockClose(sqlite3_file *id) { - int rc = SQLITE_OK; - if( id ){ - flockUnlock(id, NO_LOCK); - rc = closeUnixFile(id); - } - return rc; + assert( id!=0 ); + flockUnlock(id, NO_LOCK); + return closeUnixFile(id); } #endif /* SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORK */ @@ -3063,23 +2989,22 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) { */ static int afpClose(sqlite3_file *id) { int rc = SQLITE_OK; - if( id ){ - unixFile *pFile = (unixFile*)id; - afpUnlock(id, NO_LOCK); - unixEnterMutex(); - if( pFile->pInode && pFile->pInode->nLock ){ - /* If there are outstanding locks, do not actually close the file just - ** yet because that would clear those locks. Instead, add the file - ** descriptor to pInode->aPending. It will be automatically closed when - ** the last lock is cleared. - */ - setPendingFd(pFile); - } - releaseInodeInfo(pFile); - sqlite3_free(pFile->lockingContext); - rc = closeUnixFile(id); - unixLeaveMutex(); + unixFile *pFile = (unixFile*)id; + assert( id!=0 ); + afpUnlock(id, NO_LOCK); + unixEnterMutex(); + if( pFile->pInode && pFile->pInode->nLock ){ + /* If there are outstanding locks, do not actually close the file just + ** yet because that would clear those locks. Instead, add the file + ** descriptor to pInode->aPending. It will be automatically closed when + ** the last lock is cleared. + */ + setPendingFd(pFile); } + releaseInodeInfo(pFile); + sqlite3_free(pFile->lockingContext); + rc = closeUnixFile(id); + unixLeaveMutex(); return rc; } @@ -3158,13 +3083,9 @@ static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){ SimulateIOError( got = -1 ); #else newOffset = lseek(id->h, offset, SEEK_SET); - SimulateIOError( newOffset-- ); - if( newOffset!=offset ){ - if( newOffset == -1 ){ - storeLastErrno((unixFile*)id, errno); - }else{ - storeLastErrno((unixFile*)id, 0); - } + SimulateIOError( newOffset = -1 ); + if( newOffset<0 ){ + storeLastErrno((unixFile*)id, errno); return -1; } got = osRead(id->h, pBuf, cnt); @@ -3263,6 +3184,7 @@ static int seekAndWriteFd( assert( nBuf==(nBuf&0x1ffff) ); assert( fd>2 ); + assert( piErrno!=0 ); nBuf &= 0x1ffff; TIMER_START; @@ -3273,11 +3195,10 @@ static int seekAndWriteFd( #else do{ i64 iSeek = lseek(fd, iOff, SEEK_SET); - SimulateIOError( iSeek-- ); - - if( iSeek!=iOff ){ - if( piErrno ) *piErrno = (iSeek==-1 ? errno : 0); - return -1; + SimulateIOError( iSeek = -1 ); + if( iSeek<0 ){ + rc = -1; + break; } rc = osWrite(fd, pBuf, nBuf); }while( rc<0 && errno==EINTR ); @@ -3286,7 +3207,7 @@ static int seekAndWriteFd( TIMER_END; OSTRACE(("WRITE %-3d %5d %7lld %llu\n", fd, rc, iOff, TIMER_ELAPSED)); - if( rc<0 && piErrno ) *piErrno = errno; + if( rc<0 ) *piErrno = errno; return rc; } @@ -3470,10 +3391,15 @@ static int full_fsync(int fd, int fullSync, int dataOnly){ #endif /* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a - ** no-op + ** no-op. But go ahead and call fstat() to validate the file + ** descriptor as we need a method to provoke a failure during + ** coverate testing. */ #ifdef SQLITE_NO_SYNC - rc = SQLITE_OK; + { + struct stat buf; + rc = osFstat(fd, &buf); + } #elif HAVE_FULLFSYNC if( fullSync ){ rc = osFcntl(fd, F_FULLFSYNC, 0); @@ -3540,7 +3466,7 @@ static int openDirectory(const char *zFilename, int *pFd){ sqlite3_snprintf(MAX_PATHNAME, zDirname, "%s", zFilename); for(ii=(int)strlen(zDirname); ii>1 && zDirname[ii]!='/'; ii--); - if( ii>0 ){ + if( ii>1 ){ zDirname[ii] = '\0'; fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0); if( fd>=0 ){ @@ -3548,7 +3474,8 @@ static int openDirectory(const char *zFilename, int *pFd){ } } *pFd = fd; - return (fd>=0?SQLITE_OK:unixLogError(SQLITE_CANTOPEN_BKPT, "open", zDirname)); + if( fd>=0 ) return SQLITE_OK; + return unixLogError(SQLITE_CANTOPEN_BKPT, "openDirectory", zDirname); } /* @@ -3601,10 +3528,11 @@ static int unixSync(sqlite3_file *id, int flags){ OSTRACE(("DIRSYNC %s (have_fullfsync=%d fullsync=%d)\n", pFile->zPath, HAVE_FULLFSYNC, isFullsync)); rc = osOpenDirectory(pFile->zPath, &dirfd); - if( rc==SQLITE_OK && dirfd>=0 ){ + if( rc==SQLITE_OK ){ full_fsync(dirfd, 0, 0); robust_close(pFile, dirfd, __LINE__); - }else if( rc==SQLITE_CANTOPEN ){ + }else{ + assert( rc==SQLITE_CANTOPEN ); rc = SQLITE_OK; } pFile->ctrlFlags &= ~UNIXFILE_DIRSYNC; @@ -3736,18 +3664,14 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){ int nWrite = 0; /* Number of bytes written by seekAndWrite */ i64 iWrite; /* Next offset to write to */ - iWrite = ((buf.st_size + 2*nBlk - 1)/nBlk)*nBlk-1; + iWrite = (buf.st_size/nBlk)*nBlk + nBlk - 1; assert( iWrite>=buf.st_size ); - assert( (iWrite/nBlk)==((buf.st_size+nBlk-1)/nBlk) ); assert( ((iWrite+1)%nBlk)==0 ); - for(/*no-op*/; iWrite=nSize ) iWrite = nSize - 1; nWrite = seekAndWrite(pFile, iWrite, "", 1); if( nWrite!=1 ) return SQLITE_IOERR_WRITE; } - if( nWrite==0 || (nSize%nBlk) ){ - nWrite = seekAndWrite(pFile, nSize-1, "", 1); - if( nWrite!=1 ) return SQLITE_IOERR_WRITE; - } #endif } } @@ -3795,10 +3719,6 @@ static int unixGetTempname(int nBuf, char *zBuf); static int unixFileControl(sqlite3_file *id, int op, void *pArg){ unixFile *pFile = (unixFile*)id; switch( op ){ - case SQLITE_FCNTL_WAL_BLOCK: { - /* pFile->ctrlFlags |= UNIXFILE_BLOCK; // Deferred feature */ - return SQLITE_OK; - } case SQLITE_FCNTL_LOCKSTATE: { *(int*)pArg = pFile->eFileLock; return SQLITE_OK; @@ -4125,10 +4045,9 @@ static int unixShmSystemLock( assert( n==1 || lockType!=F_RDLCK ); /* Locks are within range */ - assert( n>=1 && n=1 && n<=SQLITE_SHM_NLOCK ); if( pShmNode->h>=0 ){ - int lkType; /* Initialize the locking parameters */ memset(&f, 0, sizeof(f)); f.l_type = lockType; @@ -4136,10 +4055,8 @@ static int unixShmSystemLock( f.l_start = ofst; f.l_len = n; - lkType = (pFile->ctrlFlags & UNIXFILE_BLOCK)!=0 ? F_SETLKW : F_SETLK; - rc = osFcntl(pShmNode->h, lkType, &f); + rc = osFcntl(pShmNode->h, F_SETLK, &f); rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY; - pFile->ctrlFlags &= ~UNIXFILE_BLOCK; } /* Update the global lock state and do debug tracing */ @@ -4206,7 +4123,7 @@ static int unixShmRegionPerMap(void){ static void unixShmPurge(unixFile *pFd){ unixShmNode *p = pFd->pInode->pShmNode; assert( unixMutexHeld() ); - if( p && p->nRef==0 ){ + if( p && ALWAYS(p->nRef==0) ){ int nShmPerMap = unixShmRegionPerMap(); int i; assert( p->pInode==pFd->pInode ); @@ -4293,7 +4210,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ ** a new *-shm file is created, an attempt will be made to create it ** with the same permissions. */ - if( osFstat(pDbFd->h, &sStat) && pInode->bProcessLock==0 ){ + if( osFstat(pDbFd->h, &sStat) ){ rc = SQLITE_IOERR_FSTAT; goto shm_open_err; } @@ -4343,7 +4260,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ ** is owned by the same user that owns the original database. Otherwise, ** the original owner will not be able to connect. */ - osFchown(pShmNode->h, sStat.st_uid, sStat.st_gid); + robustFchown(pShmNode->h, sStat.st_uid, sStat.st_gid); /* Check to see if another process is holding the dead-man switch. ** If not, truncate the file to zero length. @@ -4480,7 +4397,8 @@ static int unixShmMap( /* Write to the last byte of each newly allocated or extended page */ assert( (nByte % pgsz)==0 ); for(iPg=(sStat.st_size/pgsz); iPg<(nByte/pgsz); iPg++){ - if( seekAndWriteFd(pShmNode->h, iPg*pgsz + pgsz-1, "", 1, 0)!=1 ){ + int x = 0; + if( seekAndWriteFd(pShmNode->h, iPg*pgsz + pgsz-1, "", 1, &x)!=1 ){ const char *zFile = pShmNode->zFilename; rc = unixLogError(SQLITE_IOERR_SHMSIZE, "write", zFile); goto shmpage_out; @@ -4848,17 +4766,14 @@ static void unixRemapfile( ** recreated as a result of outstanding references) or an SQLite error ** code otherwise. */ -static int unixMapfile(unixFile *pFd, i64 nByte){ - i64 nMap = nByte; - int rc; - +static int unixMapfile(unixFile *pFd, i64 nMap){ assert( nMap>=0 || pFd->nFetchOut==0 ); + assert( nMap>0 || (pFd->mmapSize==0 && pFd->pMapRegion==0) ); if( pFd->nFetchOut>0 ) return SQLITE_OK; if( nMap<0 ){ struct stat statbuf; /* Low-level file information */ - rc = osFstat(pFd->h, &statbuf); - if( rc!=SQLITE_OK ){ + if( osFstat(pFd->h, &statbuf) ){ return SQLITE_IOERR_FSTAT; } nMap = statbuf.st_size; @@ -4867,12 +4782,9 @@ static int unixMapfile(unixFile *pFd, i64 nByte){ nMap = pFd->mmapSizeMax; } + assert( nMap>0 || (pFd->mmapSize==0 && pFd->pMapRegion==0) ); if( nMap!=pFd->mmapSize ){ - if( nMap>0 ){ - unixRemapfile(pFd, nMap); - }else{ - unixUnmapfile(pFd); - } + unixRemapfile(pFd, nMap); } return SQLITE_OK; @@ -5441,21 +5353,19 @@ static int fillInUnixFile( */ static const char *unixTempFileDir(void){ static const char *azDirs[] = { - 0, 0, 0, "/var/tmp", "/usr/tmp", "/tmp", - 0 /* List terminator */ + "." }; unsigned int i; struct stat buf; - const char *zDir = 0; + const char *zDir = sqlite3_temp_directory; - azDirs[0] = sqlite3_temp_directory; - if( !azDirs[1] ) azDirs[1] = getenv("SQLITE_TMPDIR"); - if( !azDirs[2] ) azDirs[2] = getenv("TMPDIR"); + if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR"); + if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR"); for(i=0; imxPathname bytes. */ static int unixGetTempname(int nBuf, char *zBuf){ - static const unsigned char zChars[] = - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "0123456789"; - unsigned int i, j; const char *zDir; + int iLimit = 0; /* It's odd to simulate an io-error here, but really this is just ** using the io-error infrastructure to test that SQLite handles this @@ -5486,24 +5392,14 @@ static int unixGetTempname(int nBuf, char *zBuf){ SimulateIOError( return SQLITE_IOERR ); zDir = unixTempFileDir(); - if( zDir==0 ) zDir = "."; - - /* Check that the output buffer is large enough for the temporary file - ** name. If it is not, return SQLITE_ERROR. - */ - if( (strlen(zDir) + strlen(SQLITE_TEMP_FILE_PREFIX) + 18) >= (size_t)nBuf ){ - return SQLITE_ERROR; - } - do{ - sqlite3_snprintf(nBuf-18, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX, zDir); - j = (int)strlen(zBuf); - sqlite3_randomness(15, &zBuf[j]); - for(i=0; i<15; i++, j++){ - zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; - } - zBuf[j] = 0; - zBuf[j+1] = 0; + u64 r; + sqlite3_randomness(sizeof(r), &r); + assert( nBuf>2 ); + zBuf[nBuf-2] = 0; + sqlite3_snprintf(nBuf, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX"%llx%c", + zDir, r, 0); + if( zBuf[nBuf-2]!=0 || (iLimit++)>10 ) return SQLITE_ERROR; }while( osAccess(zBuf,0)==0 ); return SQLITE_OK; } @@ -5775,7 +5671,7 @@ static int unixOpen( }else if( !zName ){ /* If zName is NULL, the upper layer is requesting a temp file. */ assert(isDelete && !syncDir); - rc = unixGetTempname(MAX_PATHNAME+2, zTmpname); + rc = unixGetTempname(pVfs->mxPathname, zTmpname); if( rc!=SQLITE_OK ){ return rc; } @@ -5808,7 +5704,8 @@ static int unixOpen( } fd = robust_open(zName, openFlags, openMode); OSTRACE(("OPENX %-3d %s 0%o\n", fd, zName, openFlags)); - if( fd<0 && errno!=EISDIR && isReadWrite && !isExclusive ){ + assert( !isExclusive || (openFlags & O_CREAT)!=0 ); + if( fd<0 && errno!=EISDIR && isReadWrite ){ /* Failed to open the file for read/write access. Try read-only. */ flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE); openFlags &= ~(O_RDWR|O_CREAT); @@ -5827,7 +5724,7 @@ static int unixOpen( ** the same as the original database. */ if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){ - osFchown(fd, uid, gid); + robustFchown(fd, uid, gid); } } assert( fd>=0 ); @@ -5964,7 +5861,8 @@ static int unixDelete( rc = unixLogError(SQLITE_IOERR_DIR_FSYNC, "fsync", zPath); } robust_close(0, fd, __LINE__); - }else if( rc==SQLITE_CANTOPEN ){ + }else{ + assert( rc==SQLITE_CANTOPEN ); rc = SQLITE_OK; } } @@ -5988,29 +5886,19 @@ static int unixAccess( int flags, /* What do we want to learn about the zPath file? */ int *pResOut /* Write result boolean here */ ){ - int amode = 0; UNUSED_PARAMETER(NotUsed); SimulateIOError( return SQLITE_IOERR_ACCESS; ); - switch( flags ){ - case SQLITE_ACCESS_EXISTS: - amode = F_OK; - break; - case SQLITE_ACCESS_READWRITE: - amode = W_OK|R_OK; - break; - case SQLITE_ACCESS_READ: - amode = R_OK; - break; + assert( pResOut!=0 ); - default: - assert(!"Invalid flags argument"); - } - *pResOut = (osAccess(zPath, amode)==0); - if( flags==SQLITE_ACCESS_EXISTS && *pResOut ){ + /* The spec says there are three possible values for flags. But only + ** two of them are actually used */ + assert( flags==SQLITE_ACCESS_EXISTS || flags==SQLITE_ACCESS_READWRITE ); + + if( flags==SQLITE_ACCESS_EXISTS ){ struct stat buf; - if( 0==osStat(zPath, &buf) && buf.st_size==0 ){ - *pResOut = 0; - } + *pResOut = (0==osStat(zPath, &buf) && buf.st_size>0); + }else{ + *pResOut = osAccess(zPath, W_OK|R_OK)==0; } return SQLITE_OK; } @@ -6053,8 +5941,7 @@ static int unixFullPathname( if( errno!=EINVAL && errno!=ENOENT ){ return unixLogError(SQLITE_CANTOPEN_BKPT, "readlink", zPath); } - zOut[nOut-1] = '\0'; - sqlite3_snprintf(nOut-1, zOut, "%s", zPath); + sqlite3_snprintf(nOut, zOut, "%s", zPath); nByte = sqlite3Strlen30(zOut); }else{ zOut[nByte] = '\0'; @@ -6076,7 +5963,9 @@ static int unixFullPathname( ** truncated to make it fit. This is Ok, as SQLite refuses to open any ** file for which this function returns a full path larger than (nOut-8) ** bytes in size. */ - if( zOut[0]!='/' ){ + testcase( nByte==nOut-5 ); + testcase( nByte==nOut-4 ); + if( zOut[0]!='/' && nBytelockingContext; unixFile *lockProxy = pCtx->lockProxy; @@ -7584,7 +7478,7 @@ int sqlite3_os_init(void){ /* Double-check that the aSyscall[] array has been constructed ** correctly. See ticket [bb3a86e890c8e96ab] */ - assert( ArraySize(aSyscall)==26 ); + assert( ArraySize(aSyscall)==27 ); /* Register all VFSes defined in the aVfs[] array */ for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){ diff --git a/src/os_win.c b/src/os_win.c index 9ae40e22af..c54bfd6a90 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -3150,7 +3150,7 @@ static int winCheckReservedLock(sqlite3_file *id, int *pResOut){ res = 1; OSTRACE(("TEST-WR-LOCK file=%p, result=%d (local)\n", pFile->h, res)); }else{ - res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS,RESERVED_BYTE, 0, 1, 0); + res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS,RESERVED_BYTE,0,1,0); if( res ){ winUnlockFile(&pFile->h, RESERVED_BYTE, 0, 1, 0); } diff --git a/src/pager.c b/src/pager.c index f633a77927..bf74eac549 100644 --- a/src/pager.c +++ b/src/pager.c @@ -6679,7 +6679,7 @@ const char *sqlite3PagerFilename(Pager *pPager, int nullIfMemDb){ /* ** Return the VFS structure for the pager. */ -const sqlite3_vfs *sqlite3PagerVfs(Pager *pPager){ +sqlite3_vfs *sqlite3PagerVfs(Pager *pPager){ return pPager->pVfs; } diff --git a/src/pager.h b/src/pager.h index 22e73f4c76..cf9cda625d 100644 --- a/src/pager.h +++ b/src/pager.h @@ -182,7 +182,7 @@ u32 sqlite3PagerDataVersion(Pager*); #endif int sqlite3PagerMemUsed(Pager*); const char *sqlite3PagerFilename(Pager*, int); -const sqlite3_vfs *sqlite3PagerVfs(Pager*); +sqlite3_vfs *sqlite3PagerVfs(Pager*); sqlite3_file *sqlite3PagerFile(Pager*); const char *sqlite3PagerJournalname(Pager*); int sqlite3PagerNosync(Pager*); diff --git a/src/pcache1.c b/src/pcache1.c index 256e53a579..3f56d025e2 100644 --- a/src/pcache1.c +++ b/src/pcache1.c @@ -65,7 +65,7 @@ ** that is allocated when the page cache is created. The size of the local ** bulk allocation can be adjusted using ** -** sqlite3_config(SQLITE_CONFIG_PAGECACHE, 0, 0, N). +** sqlite3_config(SQLITE_CONFIG_PAGECACHE, (void*)0, 0, N). ** ** If N is positive, then N pages worth of memory are allocated using a single ** sqlite3Malloc() call and that memory is used for the first N pages allocated. diff --git a/src/printf.c b/src/printf.c index 9caeef8ff7..88bb82e3e3 100644 --- a/src/printf.c +++ b/src/printf.c @@ -270,6 +270,12 @@ void sqlite3VXPrintf( testcase( wx>0x7fffffff ); width = wx & 0x7fffffff; } + assert( width>=0 ); +#ifdef SQLITE_PRINTF_PRECISION_LIMIT + if( width>SQLITE_PRINTF_PRECISION_LIMIT ){ + width = SQLITE_PRINTF_PRECISION_LIMIT; + } +#endif /* Get the precision */ if( c=='.' ){ @@ -296,6 +302,14 @@ void sqlite3VXPrintf( }else{ precision = -1; } + assert( precision>=(-1) ); +#ifdef SQLITE_PRINTF_PRECISION_LIMIT + if( precision>SQLITE_PRINTF_PRECISION_LIMIT ){ + precision = SQLITE_PRINTF_PRECISION_LIMIT; + } +#endif + + /* Get the conversion type modifier */ if( c=='l' ){ flag_long = 1; diff --git a/src/resolve.c b/src/resolve.c index ac1706b595..b3e887778d 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -1423,9 +1423,10 @@ int sqlite3ResolveExprListNames( ExprList *pList /* The expression list to be analyzed. */ ){ int i; - assert( pList!=0 ); - for(i=0; inExpr; i++){ - if( sqlite3ResolveExprNames(pNC, pList->a[i].pExpr) ) return WRC_Abort; + if( pList ){ + for(i=0; inExpr; i++){ + if( sqlite3ResolveExprNames(pNC, pList->a[i].pExpr) ) return WRC_Abort; + } } return WRC_Continue; } diff --git a/src/select.c b/src/select.c index 3d69dfb03a..fca8665cd7 100644 --- a/src/select.c +++ b/src/select.c @@ -1340,7 +1340,8 @@ static const char *columnTypeImpl( char const *zOrigCol = 0; #endif - if( NEVER(pExpr==0) || pNC->pSrcList==0 ) return 0; + assert( pExpr!=0 ); + assert( pNC->pSrcList!=0 ); switch( pExpr->op ){ case TK_AGG_COLUMN: case TK_COLUMN: { @@ -1528,6 +1529,7 @@ static void generateColumnNames( } #endif + assert( pTabList!=0 ); if( pParse->colNamesSet || NEVER(v==0) || db->mallocFailed ) return; pParse->colNamesSet = 1; fullNames = (db->flags & SQLITE_FullColNames)!=0; @@ -1540,7 +1542,7 @@ static void generateColumnNames( if( pEList->a[i].zName ){ char *zName = pEList->a[i].zName; sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName, SQLITE_TRANSIENT); - }else if( (p->op==TK_COLUMN || p->op==TK_AGG_COLUMN) && pTabList ){ + }else if( p->op==TK_COLUMN || p->op==TK_AGG_COLUMN ){ Table *pTab; char *zCol; int iCol = p->iColumn; @@ -1613,6 +1615,7 @@ int sqlite3ColumnsFromExprList( nCol = 0; aCol = 0; } + assert( nCol==(i16)nCol ); *pnCol = nCol; *paCol = aCol; @@ -2368,7 +2371,7 @@ static int multiSelect( if( dest.eDest==SRT_Output ){ Select *pFirst = p; while( pFirst->pPrior ) pFirst = pFirst->pPrior; - generateColumnNames(pParse, 0, pFirst->pEList); + generateColumnNames(pParse, pFirst->pSrc, pFirst->pEList); } iBreak = sqlite3VdbeMakeLabel(v); iCont = sqlite3VdbeMakeLabel(v); @@ -2443,7 +2446,7 @@ static int multiSelect( if( dest.eDest==SRT_Output ){ Select *pFirst = p; while( pFirst->pPrior ) pFirst = pFirst->pPrior; - generateColumnNames(pParse, 0, pFirst->pEList); + generateColumnNames(pParse, pFirst->pSrc, pFirst->pEList); } iBreak = sqlite3VdbeMakeLabel(v); iCont = sqlite3VdbeMakeLabel(v); @@ -3058,7 +3061,7 @@ static int multiSelectOrderBy( if( pDest->eDest==SRT_Output ){ Select *pFirst = pPrior; while( pFirst->pPrior ) pFirst = pFirst->pPrior; - generateColumnNames(pParse, 0, pFirst->pEList); + generateColumnNames(pParse, pFirst->pSrc, pFirst->pEList); } /* Reassembly the compound query so that it will be freed correctly @@ -3623,6 +3626,7 @@ static int flattenSubquery( */ for(i=0; ia[i+iFrom].pUsing); + assert( pSrc->a[i+iFrom].fg.isTabFunc==0 ); pSrc->a[i+iFrom] = pSubSrc->a[i]; memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i])); } @@ -3957,6 +3961,19 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){ return WRC_Continue; } +/* +** Check to see if the FROM clause term pFrom has table-valued function +** arguments. If it does, leave an error message in pParse and return +** non-zero, since pFrom is not allowed to be a table-valued function. +*/ +static int cannotBeFunction(Parse *pParse, struct SrcList_item *pFrom){ + if( pFrom->fg.isTabFunc ){ + sqlite3ErrorMsg(pParse, "'%s' is not a function", pFrom->zName); + return 1; + } + return 0; +} + #ifndef SQLITE_OMIT_CTE /* ** Argument pWith (which may be NULL) points to a linked list of nested @@ -4052,6 +4069,7 @@ static int withExpand( sqlite3ErrorMsg(pParse, pCte->zCteErr, pCte->zName); return SQLITE_ERROR; } + if( cannotBeFunction(pParse, pFrom) ) return SQLITE_ERROR; assert( pFrom->pTab==0 ); pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table)); @@ -4245,15 +4263,14 @@ static int selectExpander(Walker *pWalker, Select *p){ return WRC_Abort; } pTab->nRef++; + if( !IsVirtual(pTab) && cannotBeFunction(pParse, pFrom) ){ + return WRC_Abort; + } #if !defined(SQLITE_OMIT_VIEW) || !defined (SQLITE_OMIT_VIRTUALTABLE) - if( pTab->pSelect || IsVirtual(pTab) ){ + if( IsVirtual(pTab) || pTab->pSelect ){ i16 nCol; if( sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort; assert( pFrom->pSelect==0 ); - if( pFrom->fg.isTabFunc && !IsVirtual(pTab) ){ - sqlite3ErrorMsg(pParse, "'%s' is not a function", pTab->zName); - return WRC_Abort; - } pFrom->pSelect = sqlite3SelectDup(db, pTab->pSelect, 0); sqlite3SelectSetName(pFrom->pSelect, pTab->zName); nCol = pTab->nCol; @@ -4441,6 +4458,7 @@ static int selectExpander(Walker *pWalker, Select *p){ #if SQLITE_MAX_COLUMN if( p->pEList && p->pEList->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){ sqlite3ErrorMsg(pParse, "too many columns in result set"); + return WRC_Abort; } #endif return WRC_Continue; diff --git a/src/shell.c b/src/shell.c index 7822919ecd..565b58eeaf 100644 --- a/src/shell.c +++ b/src/shell.c @@ -1867,6 +1867,7 @@ static char zHelp[] = ".timeout MS Try opening locked tables for MS milliseconds\n" ".timer on|off Turn SQL timer on or off\n" ".trace FILE|off Output each SQL statement as it is run\n" + ".vfsinfo ?AUX? Information about the top-level VFS\n" ".vfsname ?AUX? Print the name of the VFS stack\n" ".width NUM1 NUM2 ... Set column widths for \"column\" mode\n" " Negative values right-justify\n" @@ -4348,6 +4349,20 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_libversion(), sqlite3_sourceid()); }else + if( c=='v' && strncmp(azArg[0], "vfsinfo", n)==0 ){ + const char *zDbName = nArg==2 ? azArg[1] : "main"; + sqlite3_vfs *pVfs; + if( p->db ){ + sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFS_POINTER, &pVfs); + if( pVfs ){ + fprintf(p->out, "vfs.zName = \"%s\"\n", pVfs->zName); + fprintf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); + fprintf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); + fprintf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); + } + } + }else + if( c=='v' && strncmp(azArg[0], "vfsname", n)==0 ){ const char *zDbName = nArg==2 ? azArg[1] : "main"; char *zVfsName = 0; diff --git a/src/sqlite.h.in b/src/sqlite.h.in index c0bed1336c..c10aadd904 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -882,6 +882,15 @@ struct sqlite3_io_methods { ** pointer in case this file-control is not implemented. This file-control ** is intended for diagnostic use only. ** +**
  • [[SQLITE_FCNTL_VFS_POINTER]] +** ^The [SQLITE_FCNTL_VFS_POINTER] opcode finds a pointer to the top-level +** [VFSes] currently in use. ^(The argument X in +** sqlite3_file_control(db,SQLITE_FCNTL_VFS_POINTER,X) must be +** of type "[sqlite3_vfs] **". This opcodes will set *X +** to a pointer to the top-level VFS.^) +** ^When there are multiple VFS shims in the stack, this opcode finds the +** upper-most shim only. +** **
  • [[SQLITE_FCNTL_PRAGMA]] ** ^Whenever a [PRAGMA] statement is parsed, an [SQLITE_FCNTL_PRAGMA] ** file control is sent to the open [sqlite3_file] object corresponding @@ -1000,6 +1009,7 @@ struct sqlite3_io_methods { #define SQLITE_FCNTL_WAL_BLOCK 24 #define SQLITE_FCNTL_ZIPVFS 25 #define SQLITE_FCNTL_RBU 26 +#define SQLITE_FCNTL_VFS_POINTER 27 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE @@ -5615,6 +5625,17 @@ struct sqlite3_module { ** ^Information about the ORDER BY clause is stored in aOrderBy[]. ** ^Each term of aOrderBy records a column of the ORDER BY clause. ** +** The colUsed field indicates which columns of the virtual table may be +** required by the current scan. Virtual table columns are numbered from +** zero in the order in which they appear within the CREATE TABLE statement +** passed to sqlite3_declare_vtab(). For the first 63 columns (columns 0-62), +** the corresponding bit is set within the colUsed mask if the column may be +** required by SQLite. If the table has at least 64 columns and any column +** to the right of the first 63 is required, then bit 63 of colUsed is also +** set. In other words, column iCol may be required if the expression +** (colUsed & ((sqlite3_uint64)1 << (iCol>=63 ? 63 : iCol))) evaluates to +** non-zero. +** ** The [xBestIndex] method must fill aConstraintUsage[] with information ** about what parameters to pass to xFilter. ^If argvIndex>0 then ** the right-hand side of the corresponding aConstraint[] is evaluated @@ -5694,6 +5715,8 @@ struct sqlite3_index_info { sqlite3_int64 estimatedRows; /* Estimated number of rows returned */ /* Fields below are only available in SQLite 3.9.0 and later */ int idxFlags; /* Mask of SQLITE_INDEX_SCAN_* flags */ + /* Fields below are only available in SQLite 3.10.0 and later */ + sqlite3_uint64 colUsed; /* Input: Mask of columns used by statement */ }; /* @@ -5709,12 +5732,15 @@ struct sqlite3_index_info { ** an operator that is part of a constraint term in the wHERE clause of ** a query that uses a [virtual table]. */ -#define SQLITE_INDEX_CONSTRAINT_EQ 2 -#define SQLITE_INDEX_CONSTRAINT_GT 4 -#define SQLITE_INDEX_CONSTRAINT_LE 8 -#define SQLITE_INDEX_CONSTRAINT_LT 16 -#define SQLITE_INDEX_CONSTRAINT_GE 32 -#define SQLITE_INDEX_CONSTRAINT_MATCH 64 +#define SQLITE_INDEX_CONSTRAINT_EQ 2 +#define SQLITE_INDEX_CONSTRAINT_GT 4 +#define SQLITE_INDEX_CONSTRAINT_LE 8 +#define SQLITE_INDEX_CONSTRAINT_LT 16 +#define SQLITE_INDEX_CONSTRAINT_GE 32 +#define SQLITE_INDEX_CONSTRAINT_MATCH 64 +#define SQLITE_INDEX_CONSTRAINT_LIKE 65 +#define SQLITE_INDEX_CONSTRAINT_GLOB 66 +#define SQLITE_INDEX_CONSTRAINT_REGEXP 67 /* ** CAPI3REF: Register A Virtual Table Implementation @@ -7365,18 +7391,43 @@ int sqlite3_strnicmp(const char *, const char *, int); /* ** CAPI3REF: String Globbing * -** ^The [sqlite3_strglob(P,X)] interface returns zero if string X matches -** the glob pattern P, and it returns non-zero if string X does not match -** the glob pattern P. ^The definition of glob pattern matching used in +** ^The [sqlite3_strglob(P,X)] interface returns zero if and only if +** string X matches the [GLOB] pattern P. +** ^The definition of [GLOB] pattern matching used in ** [sqlite3_strglob(P,X)] is the same as for the "X GLOB P" operator in the -** SQL dialect used by SQLite. ^The sqlite3_strglob(P,X) function is case -** sensitive. +** SQL dialect understood by SQLite. ^The [sqlite3_strglob(P,X)] function +** is case sensitive. ** ** Note that this routine returns zero on a match and non-zero if the strings ** do not match, the same as [sqlite3_stricmp()] and [sqlite3_strnicmp()]. +** +** See also: [sqlite3_strlike()]. */ int sqlite3_strglob(const char *zGlob, const char *zStr); +/* +** CAPI3REF: String LIKE Matching +* +** ^The [sqlite3_strlike(P,X,E)] interface returns zero if and only if +** string X matches the [LIKE] pattern P with escape character E. +** ^The definition of [LIKE] pattern matching used in +** [sqlite3_strlike(P,X,E)] is the same as for the "X LIKE P ESCAPE E" +** operator in the SQL dialect understood by SQLite. ^For "X LIKE P" without +** the ESCAPE clause, set the E parameter of [sqlite3_strlike(P,X,E)] to 0. +** ^As with the LIKE operator, the [sqlite3_strlike(P,X,E)] function is case +** insensitive - equivalent upper and lower case ASCII characters match +** one another. +** +** ^The [sqlite3_strlike(P,X,E)] function matches Unicode characters, though +** only ASCII characters are case folded. +** +** Note that this routine returns zero on a match and non-zero if the strings +** do not match, the same as [sqlite3_stricmp()] and [sqlite3_strnicmp()]. +** +** See also: [sqlite3_strglob()]. +*/ +int sqlite3_strlike(const char *zGlob, const char *zStr, unsigned int cEsc); + /* ** CAPI3REF: Error Logging Interface ** diff --git a/src/sqlite3ext.h b/src/sqlite3ext.h index 017ea308b1..3029a82fa1 100644 --- a/src/sqlite3ext.h +++ b/src/sqlite3ext.h @@ -275,6 +275,8 @@ struct sqlite3_api_routines { /* Version 3.9.0 and later */ unsigned int (*value_subtype)(sqlite3_value*); void (*result_subtype)(sqlite3_context*,unsigned int); + /* Version 3.10.0 and later */ + int (*strlike)(const char*,const char*,unsigned int); }; /* @@ -514,6 +516,8 @@ struct sqlite3_api_routines { /* Version 3.9.0 and later */ #define sqlite3_value_subtype sqlite3_api->value_subtype #define sqlite3_result_subtype sqlite3_api->result_subtype +/* Version 3.10.0 and later */ +#define sqlite3_strlike sqlite3_api->strlike #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 0f02c5052f..694a081282 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1390,7 +1390,8 @@ struct FuncDestructor { /* ** Possible values for FuncDef.flags. Note that the _LENGTH and _TYPEOF -** values must correspond to OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG. There +** values must correspond to OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG. And +** SQLITE_FUNC_CONSTANT must be the same as SQLITE_DETERMINISTIC. There ** are assert() statements in the code to verify this. */ #define SQLITE_FUNC_ENCMASK 0x0003 /* SQLITE_UTF8, SQLITE_UTF16BE or UTF16LE */ diff --git a/src/test8.c b/src/test8.c index 2107710a99..3e506e36b4 100644 --- a/src/test8.c +++ b/src/test8.c @@ -745,6 +745,34 @@ static void string_concat(char **pzStr, char *zAppend, int doFree, int *pRc){ } } +/* +** This function returns a pointer to an sqlite3_malloc()ed buffer +** containing the select-list (the thing between keywords SELECT and FROM) +** to query the underlying real table with for the scan described by +** argument pIdxInfo. +** +** If the current SQLite version is earlier than 3.10.0, this is just "*" +** (select all columns). Or, for version 3.10.0 and greater, the list of +** columns identified by the pIdxInfo->colUsed mask. +*/ +static char *echoSelectList(echo_vtab *pTab, sqlite3_index_info *pIdxInfo){ + char *zRet = 0; + if( sqlite3_libversion_number()<3010000 ){ + zRet = sqlite3_mprintf(", *"); + }else{ + int i; + for(i=0; inCol; i++){ + if( pIdxInfo->colUsed & ((sqlite3_uint64)1 << (i>=63 ? 63 : i)) ){ + zRet = sqlite3_mprintf("%z, %s", zRet, pTab->aCol[i]); + }else{ + zRet = sqlite3_mprintf("%z, NULL", zRet); + } + if( !zRet ) break; + } + } + return zRet; +} + /* ** The echo module implements the subset of query constraints and sort ** orders that may take advantage of SQLite indices on the underlying @@ -770,6 +798,7 @@ static void string_concat(char **pzStr, char *zAppend, int doFree, int *pRc){ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ int ii; char *zQuery = 0; + char *zCol = 0; char *zNew; int nArg = 0; const char *zSep = "WHERE"; @@ -817,10 +846,11 @@ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ } } - zQuery = sqlite3_mprintf("SELECT rowid, * FROM %Q", pVtab->zTableName); - if( !zQuery ){ - return SQLITE_NOMEM; - } + zCol = echoSelectList(pVtab, pIdxInfo); + if( !zCol ) return SQLITE_NOMEM; + zQuery = sqlite3_mprintf("SELECT rowid%z FROM %Q", zCol, pVtab->zTableName); + if( !zQuery ) return SQLITE_NOMEM; + for(ii=0; iinConstraint; ii++){ const struct sqlite3_index_constraint *pConstraint; struct sqlite3_index_constraint_usage *pUsage; @@ -848,7 +878,20 @@ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ case SQLITE_INDEX_CONSTRAINT_GE: zOp = ">="; break; case SQLITE_INDEX_CONSTRAINT_MATCH: + /* Purposely translate the MATCH operator into a LIKE, which + ** will be used by the next block of code to construct a new + ** query. It should also be noted here that the next block + ** of code requires the first letter of this operator to be + ** in upper-case to trigger the special MATCH handling (i.e. + ** wrapping the bound parameter with literal '%'s). + */ zOp = "LIKE"; break; + case SQLITE_INDEX_CONSTRAINT_LIKE: + zOp = "like"; break; + case SQLITE_INDEX_CONSTRAINT_GLOB: + zOp = "glob"; break; + case SQLITE_INDEX_CONSTRAINT_REGEXP: + zOp = "regexp"; break; } if( zOp[0]=='L' ){ zNew = sqlite3_mprintf(" %s %s LIKE (SELECT '%%'||?||'%%')", diff --git a/src/test_config.c b/src/test_config.c index a5c42f30d5..2e87788758 100644 --- a/src/test_config.c +++ b/src/test_config.c @@ -191,6 +191,12 @@ static void set_options(Tcl_Interp *interp){ Tcl_SetVar2(interp, "sqlite_options", "json1", "0", TCL_GLOBAL_ONLY); #endif +#ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS + Tcl_SetVar2(interp, "sqlite_options", "like_match_blobs", "0", TCL_GLOBAL_ONLY); +#else + Tcl_SetVar2(interp, "sqlite_options", "like_match_blobs", "1", TCL_GLOBAL_ONLY); +#endif + #ifdef SQLITE_OMIT_ATTACH Tcl_SetVar2(interp, "sqlite_options", "attach", "0", TCL_GLOBAL_ONLY); #else diff --git a/src/test_fs.c b/src/test_fs.c index 417c81b49f..de332fa2f5 100644 --- a/src/test_fs.c +++ b/src/test_fs.c @@ -29,6 +29,37 @@ ** Adding the row to the idx table automatically creates a row in the ** virtual table with rowid=4, path=/etc/passwd and a text field that ** contains data read from file /etc/passwd on disk. +** +************************************************************************* +** Virtual table module "fsdir" +** +** This module is designed to be used as a read-only eponymous virtual table. +** Its schema is as follows: +** +** CREATE TABLE fsdir(dir TEXT, name TEXT); +** +** When queried, a WHERE term of the form "dir = $dir" must be provided. The +** virtual table then appears to have one row for each entry in file-system +** directory $dir. Column dir contains a copy of $dir, and column "name" +** contains the name of the directory entry. +** +** If the specified $dir cannot be opened or is not a directory, it is not +** an error. The virtual table appears to be empty in this case. +** +************************************************************************* +** Virtual table module "fstree" +** +** This module is also a read-only eponymous virtual table with the +** following schema: +** +** CREATE TABLE fstree(path TEXT, size INT, data BLOB); +** +** Running a "SELECT * FROM fstree" query on this table returns the entire +** contents of the file-system, starting at "/". To restrict the search +** space, the virtual table supports LIKE and GLOB constraints on the +** 'path' column. For example: +** +** SELECT * FROM fstree WHERE path LIKE '/home/dan/sqlite/%' */ #include "sqliteInt.h" #include "tcl.h" @@ -41,9 +72,17 @@ #if SQLITE_OS_UNIX # include +# include +# ifndef DIRENT +# define DIRENT dirent +# endif #endif #if SQLITE_OS_WIN # include +# include "test_windirent.h" +# ifndef S_ISREG +# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +# endif #endif #ifndef SQLITE_OMIT_VIRTUALTABLE @@ -70,6 +109,488 @@ struct fs_cursor { int nAlloc; }; +/************************************************************************* +** Start of fsdir implementation. +*/ +typedef struct FsdirVtab FsdirVtab; +typedef struct FsdirCsr FsdirCsr; +struct FsdirVtab { + sqlite3_vtab base; +}; + +struct FsdirCsr { + sqlite3_vtab_cursor base; + char *zDir; /* Buffer containing directory scanned */ + DIR *pDir; /* Open directory */ + sqlite3_int64 iRowid; + struct DIRENT entry; /* Current entry */ +}; + +/* +** This function is the implementation of both the xConnect and xCreate +** methods of the fsdir virtual table. +** +** The argv[] array contains the following: +** +** argv[0] -> module name ("fs") +** argv[1] -> database name +** argv[2] -> table name +** argv[...] -> other module argument fields. +*/ +static int fsdirConnect( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr +){ + FsdirVtab *pTab; + + if( argc!=3 ){ + *pzErr = sqlite3_mprintf("wrong number of arguments"); + return SQLITE_ERROR; + } + + pTab = (FsdirVtab *)sqlite3_malloc(sizeof(FsdirVtab)); + if( !pTab ) return SQLITE_NOMEM; + memset(pTab, 0, sizeof(FsdirVtab)); + + *ppVtab = &pTab->base; + sqlite3_declare_vtab(db, "CREATE TABLE xyz(dir, name);"); + + return SQLITE_OK; +} + +/* +** xDestroy/xDisconnect implementation. +*/ +static int fsdirDisconnect(sqlite3_vtab *pVtab){ + sqlite3_free(pVtab); + return SQLITE_OK; +} + +/* +** xBestIndex implementation. The only constraint supported is: +** +** (dir = ?) +*/ +static int fsdirBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + int ii; + + pIdxInfo->estimatedCost = 1000000000.0; + + for(ii=0; iinConstraint; ii++){ + struct sqlite3_index_constraint const *p = &pIdxInfo->aConstraint[ii]; + if( p->iColumn==0 && p->usable && p->op==SQLITE_INDEX_CONSTRAINT_EQ ){ + struct sqlite3_index_constraint_usage *pUsage; + pUsage = &pIdxInfo->aConstraintUsage[ii]; + pUsage->omit = 1; + pUsage->argvIndex = 1; + pIdxInfo->idxNum = 1; + pIdxInfo->estimatedCost = 1.0; + break; + } + } + + return SQLITE_OK; +} + +/* +** xOpen implementation. +** +** Open a new fsdir cursor. +*/ +static int fsdirOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ + FsdirCsr *pCur; + pCur = (FsdirCsr*)sqlite3_malloc(sizeof(FsdirCsr)); + if( pCur==0 ) return SQLITE_NOMEM; + memset(pCur, 0, sizeof(FsdirCsr)); + *ppCursor = &pCur->base; + return SQLITE_OK; +} + +/* +** Close a fsdir cursor. +*/ +static int fsdirClose(sqlite3_vtab_cursor *cur){ + FsdirCsr *pCur = (FsdirCsr*)cur; + if( pCur->pDir ) closedir(pCur->pDir); + sqlite3_free(pCur->zDir); + sqlite3_free(pCur); + return SQLITE_OK; +} + +/* +** Skip the cursor to the next entry. +*/ +static int fsdirNext(sqlite3_vtab_cursor *cur){ + FsdirCsr *pCsr = (FsdirCsr*)cur; + + if( pCsr->pDir ){ + struct DIRENT *pRes = 0; + readdir_r(pCsr->pDir, &pCsr->entry, &pRes); + if( pRes==0 ){ + closedir(pCsr->pDir); + pCsr->pDir = 0; + } + pCsr->iRowid++; + } + + return SQLITE_OK; +} + +/* +** xFilter method implementation. +*/ +static int fsdirFilter( + sqlite3_vtab_cursor *pVtabCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv +){ + FsdirCsr *pCsr = (FsdirCsr*)pVtabCursor; + const char *zDir; + int nDir; + + + if( idxNum!=1 || argc!=1 ){ + return SQLITE_ERROR; + } + + pCsr->iRowid = 0; + sqlite3_free(pCsr->zDir); + if( pCsr->pDir ){ + closedir(pCsr->pDir); + pCsr->pDir = 0; + } + + zDir = (const char*)sqlite3_value_text(argv[0]); + nDir = sqlite3_value_bytes(argv[0]); + pCsr->zDir = sqlite3_malloc(nDir+1); + if( pCsr->zDir==0 ) return SQLITE_NOMEM; + memcpy(pCsr->zDir, zDir, nDir+1); + + pCsr->pDir = opendir(pCsr->zDir); + return fsdirNext(pVtabCursor); +} + +/* +** xEof method implementation. +*/ +static int fsdirEof(sqlite3_vtab_cursor *cur){ + FsdirCsr *pCsr = (FsdirCsr*)cur; + return pCsr->pDir==0; +} + +/* +** xColumn method implementation. +*/ +static int fsdirColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ + FsdirCsr *pCsr = (FsdirCsr*)cur; + switch( i ){ + case 0: /* dir */ + sqlite3_result_text(ctx, pCsr->zDir, -1, SQLITE_STATIC); + break; + + case 1: /* name */ + sqlite3_result_text(ctx, pCsr->entry.d_name, -1, SQLITE_TRANSIENT); + break; + + default: + assert( 0 ); + } + + return SQLITE_OK; +} + +/* +** xRowid method implementation. +*/ +static int fsdirRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ + FsdirCsr *pCsr = (FsdirCsr*)cur; + *pRowid = pCsr->iRowid; + return SQLITE_OK; +} +/* +** End of fsdir implementation. +*************************************************************************/ + +/************************************************************************* +** Start of fstree implementation. +*/ +typedef struct FstreeVtab FstreeVtab; +typedef struct FstreeCsr FstreeCsr; +struct FstreeVtab { + sqlite3_vtab base; + sqlite3 *db; +}; + +struct FstreeCsr { + sqlite3_vtab_cursor base; + sqlite3_stmt *pStmt; /* Statement to list paths */ + int fd; /* File descriptor open on current path */ +}; + +/* +** This function is the implementation of both the xConnect and xCreate +** methods of the fstree virtual table. +** +** The argv[] array contains the following: +** +** argv[0] -> module name ("fs") +** argv[1] -> database name +** argv[2] -> table name +** argv[...] -> other module argument fields. +*/ +static int fstreeConnect( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr +){ + FstreeVtab *pTab; + + if( argc!=3 ){ + *pzErr = sqlite3_mprintf("wrong number of arguments"); + return SQLITE_ERROR; + } + + pTab = (FstreeVtab *)sqlite3_malloc(sizeof(FstreeVtab)); + if( !pTab ) return SQLITE_NOMEM; + memset(pTab, 0, sizeof(FstreeVtab)); + pTab->db = db; + + *ppVtab = &pTab->base; + sqlite3_declare_vtab(db, "CREATE TABLE xyz(path, size, data);"); + + return SQLITE_OK; +} + +/* +** xDestroy/xDisconnect implementation. +*/ +static int fstreeDisconnect(sqlite3_vtab *pVtab){ + sqlite3_free(pVtab); + return SQLITE_OK; +} + +/* +** xBestIndex implementation. The only constraint supported is: +** +** (dir = ?) +*/ +static int fstreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + int ii; + + for(ii=0; iinConstraint; ii++){ + struct sqlite3_index_constraint const *p = &pIdxInfo->aConstraint[ii]; + if( p->iColumn==0 && p->usable && ( + p->op==SQLITE_INDEX_CONSTRAINT_GLOB + || p->op==SQLITE_INDEX_CONSTRAINT_LIKE + || p->op==SQLITE_INDEX_CONSTRAINT_EQ + )){ + struct sqlite3_index_constraint_usage *pUsage; + pUsage = &pIdxInfo->aConstraintUsage[ii]; + pIdxInfo->idxNum = p->op; + pUsage->argvIndex = 1; + pIdxInfo->estimatedCost = 100000.0; + return SQLITE_OK; + } + } + + pIdxInfo->estimatedCost = 1000000000.0; + return SQLITE_OK; +} + +/* +** xOpen implementation. +** +** Open a new fstree cursor. +*/ +static int fstreeOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ + FstreeCsr *pCur; + pCur = (FstreeCsr*)sqlite3_malloc(sizeof(FstreeCsr)); + if( pCur==0 ) return SQLITE_NOMEM; + memset(pCur, 0, sizeof(FstreeCsr)); + pCur->fd = -1; + *ppCursor = &pCur->base; + return SQLITE_OK; +} + +static void fstreeCloseFd(FstreeCsr *pCsr){ + if( pCsr->fd>=0 ){ + close(pCsr->fd); + pCsr->fd = -1; + } +} + +/* +** Close a fstree cursor. +*/ +static int fstreeClose(sqlite3_vtab_cursor *cur){ + FstreeCsr *pCsr = (FstreeCsr*)cur; + sqlite3_finalize(pCsr->pStmt); + fstreeCloseFd(pCsr); + sqlite3_free(pCsr); + return SQLITE_OK; +} + +/* +** Skip the cursor to the next entry. +*/ +static int fstreeNext(sqlite3_vtab_cursor *cur){ + FstreeCsr *pCsr = (FstreeCsr*)cur; + int rc; + + fstreeCloseFd(pCsr); + rc = sqlite3_step(pCsr->pStmt); + if( rc!=SQLITE_ROW ){ + rc = sqlite3_finalize(pCsr->pStmt); + pCsr->pStmt = 0; + }else{ + rc = SQLITE_OK; + pCsr->fd = open((const char*)sqlite3_column_text(pCsr->pStmt, 0), O_RDONLY); + } + + return rc; +} + +/* +** xFilter method implementation. +*/ +static int fstreeFilter( + sqlite3_vtab_cursor *pVtabCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv +){ + FstreeCsr *pCsr = (FstreeCsr*)pVtabCursor; + FstreeVtab *pTab = (FstreeVtab*)(pCsr->base.pVtab); + int rc; + const char *zSql = +"WITH r(d) AS (" +" SELECT CASE WHEN dir=?2 THEN ?3 ELSE dir END || '/' || name " +" FROM fsdir WHERE dir=?1 AND name NOT LIKE '.%'" +" UNION ALL" +" SELECT dir || '/' || name FROM r, fsdir WHERE dir=d AND name NOT LIKE '.%'" +") SELECT d FROM r;"; + + char *zRoot; + int nRoot; + char *zPrefix; + int nPrefix; + const char *zDir; + int nDir; + char aWild[2] = { '\0', '\0' }; + +#if SQLITE_OS_WIN + zRoot = sqlite3_mprintf("%s%c", getenv("SystemDrive"), '/'); + nRoot = strlen(zRoot); + zPrefix = sqlite3_mprintf("%s", getenv("SystemDrive")); + nPrefix = strlen(zPrefix); +#else + zRoot = "/"; + nRoot = 1; + zPrefix = ""; + nPrefix = 0; +#endif + + zDir = zRoot; + nDir = nRoot; + + fstreeCloseFd(pCsr); + sqlite3_finalize(pCsr->pStmt); + pCsr->pStmt = 0; + rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0); + if( rc!=SQLITE_OK ) return rc; + + if( idxNum ){ + const char *zQuery = (const char*)sqlite3_value_text(argv[0]); + switch( idxNum ){ + case SQLITE_INDEX_CONSTRAINT_GLOB: + aWild[0] = '*'; + aWild[1] = '?'; + break; + case SQLITE_INDEX_CONSTRAINT_LIKE: + aWild[0] = '_'; + aWild[1] = '%'; + break; + } + + if( sqlite3_strnicmp(zQuery, zPrefix, nPrefix)==0 ){ + int i; + for(i=nPrefix; zQuery[i]; i++){ + if( zQuery[i]==aWild[0] || zQuery[i]==aWild[1] ) break; + if( zQuery[i]=='/' ) nDir = i; + } + zDir = zQuery; + } + } + + sqlite3_bind_text(pCsr->pStmt, 1, zDir, nDir, SQLITE_TRANSIENT); + sqlite3_bind_text(pCsr->pStmt, 2, zRoot, nRoot, SQLITE_TRANSIENT); + sqlite3_bind_text(pCsr->pStmt, 3, zPrefix, nPrefix, SQLITE_TRANSIENT); + +#if SQLITE_OS_WIN + sqlite3_free(zPrefix); + sqlite3_free(zRoot); +#endif + + return fstreeNext(pVtabCursor); +} + +/* +** xEof method implementation. +*/ +static int fstreeEof(sqlite3_vtab_cursor *cur){ + FstreeCsr *pCsr = (FstreeCsr*)cur; + return pCsr->pStmt==0; +} + +/* +** xColumn method implementation. +*/ +static int fstreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ + FstreeCsr *pCsr = (FstreeCsr*)cur; + if( i==0 ){ /* path */ + sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pStmt, 0)); + }else{ + struct stat sBuf; + fstat(pCsr->fd, &sBuf); + + if( S_ISREG(sBuf.st_mode) ){ + if( i==1 ){ + sqlite3_result_int64(ctx, sBuf.st_size); + }else{ + int nRead; + char *aBuf = sqlite3_malloc(sBuf.st_mode+1); + if( !aBuf ) return SQLITE_NOMEM; + nRead = read(pCsr->fd, aBuf, sBuf.st_mode); + if( nRead!=sBuf.st_mode ){ + return SQLITE_IOERR; + } + sqlite3_result_blob(ctx, aBuf, nRead, SQLITE_TRANSIENT); + sqlite3_free(aBuf); + } + } + } + + return SQLITE_OK; +} + +/* +** xRowid method implementation. +*/ +static int fstreeRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ + *pRowid = 0; + return SQLITE_OK; +} +/* +** End of fstree implementation. +*************************************************************************/ + + + + /* ** This function is the implementation of both the xConnect and xCreate ** methods of the fs virtual table. @@ -109,7 +630,7 @@ static int fsConnect( memcpy(pVtab->zTbl, zTbl, strlen(zTbl)); memcpy(pVtab->zDb, zDb, strlen(zDb)); *ppVtab = &pVtab->base; - sqlite3_declare_vtab(db, "CREATE TABLE xyz(path TEXT, data TEXT)"); + sqlite3_declare_vtab(db, "CREATE TABLE x(path TEXT, data TEXT)"); return SQLITE_OK; } @@ -188,15 +709,15 @@ static int fsFilter( static int fsColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ fs_cursor *pCur = (fs_cursor*)cur; - assert( i==0 || i==1 ); + assert( i==0 || i==1 || i==2 ); if( i==0 ){ sqlite3_result_value(ctx, sqlite3_column_value(pCur->pStmt, 0)); }else{ const char *zFile = (const char *)sqlite3_column_text(pCur->pStmt, 1); struct stat sbuf; int fd; - int n; + int n; fd = open(zFile, O_RDONLY); if( fd<0 ) return SQLITE_IOERR; fstat(fd, &sbuf); @@ -284,6 +805,52 @@ static sqlite3_module fsModule = { 0, /* xRename */ }; +static sqlite3_module fsdirModule = { + 0, /* iVersion */ + fsdirConnect, /* xCreate */ + fsdirConnect, /* xConnect */ + fsdirBestIndex, /* xBestIndex */ + fsdirDisconnect, /* xDisconnect */ + fsdirDisconnect, /* xDestroy */ + fsdirOpen, /* xOpen - open a cursor */ + fsdirClose, /* xClose - close a cursor */ + fsdirFilter, /* xFilter - configure scan constraints */ + fsdirNext, /* xNext - advance a cursor */ + fsdirEof, /* xEof - check for end of scan */ + fsdirColumn, /* xColumn - read data */ + fsdirRowid, /* xRowid - read data */ + 0, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindMethod */ + 0, /* xRename */ +}; + +static sqlite3_module fstreeModule = { + 0, /* iVersion */ + fstreeConnect, /* xCreate */ + fstreeConnect, /* xConnect */ + fstreeBestIndex, /* xBestIndex */ + fstreeDisconnect, /* xDisconnect */ + fstreeDisconnect, /* xDestroy */ + fstreeOpen, /* xOpen - open a cursor */ + fstreeClose, /* xClose - close a cursor */ + fstreeFilter, /* xFilter - configure scan constraints */ + fstreeNext, /* xNext - advance a cursor */ + fstreeEof, /* xEof - check for end of scan */ + fstreeColumn, /* xColumn - read data */ + fstreeRowid, /* xRowid - read data */ + 0, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindMethod */ + 0, /* xRename */ +}; + /* ** Decode a pointer to an sqlite3 object. */ @@ -306,6 +873,8 @@ static int register_fs_module( if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; #ifndef SQLITE_OMIT_VIRTUALTABLE sqlite3_create_module(db, "fs", &fsModule, (void *)interp); + sqlite3_create_module(db, "fsdir", &fsdirModule, 0); + sqlite3_create_module(db, "fstree", &fstreeModule, 0); #endif return TCL_OK; } diff --git a/src/test_malloc.c b/src/test_malloc.c index 3ab177dcb7..a3ff9d205b 100644 --- a/src/test_malloc.c +++ b/src/test_malloc.c @@ -910,7 +910,7 @@ static int test_config_scratch( free(buf); if( sz<0 ){ buf = 0; - rc = sqlite3_config(SQLITE_CONFIG_SCRATCH, 0, 0, 0); + rc = sqlite3_config(SQLITE_CONFIG_SCRATCH, (void*)0, 0, 0); }else{ buf = malloc( sz*N + 1 ); rc = sqlite3_config(SQLITE_CONFIG_SCRATCH, buf, sz, N); @@ -957,7 +957,7 @@ static int test_config_pagecache( Tcl_SetObjResult(interp, pRes); if( sz<0 ){ - sqlite3_config(SQLITE_CONFIG_PAGECACHE, 0, 0, 0); + sqlite3_config(SQLITE_CONFIG_PAGECACHE, (void*)0, 0, 0); }else{ buf = malloc( sz*N ); sqlite3_config(SQLITE_CONFIG_PAGECACHE, buf, sz, N); diff --git a/src/test_tclvar.c b/src/test_tclvar.c index 1219190c03..63ed394734 100644 --- a/src/test_tclvar.c +++ b/src/test_tclvar.c @@ -23,6 +23,15 @@ #ifndef SQLITE_OMIT_VIRTUALTABLE +/* +** Characters that make up the idxStr created by xBestIndex for xFilter. +*/ +#define TCLVAR_NAME_EQ 'e' +#define TCLVAR_NAME_MATCH 'm' +#define TCLVAR_VALUE_GLOB 'g' +#define TCLVAR_VALUE_REGEXP 'r' +#define TCLVAR_VALUE_LIKE 'l' + typedef struct tclvar_vtab tclvar_vtab; typedef struct tclvar_cursor tclvar_cursor; @@ -155,15 +164,44 @@ static int tclvarFilter( ){ tclvar_cursor *pCur = (tclvar_cursor *)pVtabCursor; Tcl_Interp *interp = ((tclvar_vtab *)(pVtabCursor->pVtab))->interp; + Tcl_Obj *p = Tcl_NewStringObj("tclvar_filter_cmd", -1); - Tcl_Obj *p = Tcl_NewStringObj("info vars", -1); - Tcl_IncrRefCount(p); + const char *zEq = ""; + const char *zMatch = ""; + const char *zGlob = ""; + const char *zRegexp = ""; + const char *zLike = ""; + int i; - assert( argc==0 || argc==1 ); - if( argc==1 ){ - Tcl_Obj *pArg = Tcl_NewStringObj((char*)sqlite3_value_text(argv[0]), -1); - Tcl_ListObjAppendElement(0, p, pArg); + for(i=0; idxStr[i]; i++){ + switch( idxStr[i] ){ + case TCLVAR_NAME_EQ: + zEq = (const char*)sqlite3_value_text(argv[i]); + break; + case TCLVAR_NAME_MATCH: + zMatch = (const char*)sqlite3_value_text(argv[i]); + break; + case TCLVAR_VALUE_GLOB: + zGlob = (const char*)sqlite3_value_text(argv[i]); + break; + case TCLVAR_VALUE_REGEXP: + zRegexp = (const char*)sqlite3_value_text(argv[i]); + break; + case TCLVAR_VALUE_LIKE: + zLike = (const char*)sqlite3_value_text(argv[i]); + break; + default: + assert( 0 ); + } } + + Tcl_IncrRefCount(p); + Tcl_ListObjAppendElement(0, p, Tcl_NewStringObj(zEq, -1)); + Tcl_ListObjAppendElement(0, p, Tcl_NewStringObj(zMatch, -1)); + Tcl_ListObjAppendElement(0, p, Tcl_NewStringObj(zGlob, -1)); + Tcl_ListObjAppendElement(0, p, Tcl_NewStringObj(zRegexp, -1)); + Tcl_ListObjAppendElement(0, p, Tcl_NewStringObj(zLike, -1)); + Tcl_EvalObjEx(interp, p, TCL_EVAL_GLOBAL); if( pCur->pList1 ){ Tcl_DecrRefCount(pCur->pList1); @@ -176,7 +214,6 @@ static int tclvarFilter( pCur->i2 = 0; pCur->pList1 = Tcl_GetObjResult(interp); Tcl_IncrRefCount(pCur->pList1); - assert( pCur->i1==0 && pCur->i2==0 && pCur->pList2==0 ); Tcl_DecrRefCount(p); return tclvarNext(pVtabCursor); @@ -224,32 +261,113 @@ static int tclvarEof(sqlite3_vtab_cursor *cur){ return (pCur->pList2?0:1); } +/* +** If nul-terminated string zStr does not already contain the character +** passed as the second argument, append it and return 0. Or, if there is +** already an instance of x in zStr, do nothing return 1; +** +** There is guaranteed to be enough room in the buffer pointed to by zStr +** for the new character and nul-terminator. +*/ +static int tclvarAddToIdxstr(char *zStr, char x){ + int i; + for(i=0; zStr[i]; i++){ + if( zStr[i]==x ) return 1; + } + zStr[i] = x; + zStr[i+1] = '\0'; + return 0; +} + +/* +** Return true if variable $::tclvar_set_omit exists and is set to true. +** False otherwise. +*/ +static int tclvarSetOmit(Tcl_Interp *interp){ + int rc; + int res = 0; + Tcl_Obj *pRes; + rc = Tcl_Eval(interp, + "expr {[info exists ::tclvar_set_omit] && $::tclvar_set_omit}" + ); + if( rc==TCL_OK ){ + pRes = Tcl_GetObjResult(interp); + rc = Tcl_GetBooleanFromObj(0, pRes, &res); + } + return (rc==TCL_OK && res); +} + +/* +** The xBestIndex() method. This virtual table supports the following +** operators: +** +** name = ? (omit flag clear) +** name MATCH ? (omit flag set) +** value GLOB ? (omit flag set iff $::tclvar_set_omit) +** value REGEXP ? (omit flag set iff $::tclvar_set_omit) +** value LIKE ? (omit flag set iff $::tclvar_set_omit) +** +** For each constraint present, the corresponding TCLVAR_XXX character is +** appended to the idxStr value. +*/ static int tclvarBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + tclvar_vtab *pTab = (tclvar_vtab*)tab; int ii; + char *zStr = sqlite3_malloc(32); + int iStr = 0; + + if( zStr==0 ) return SQLITE_NOMEM; + zStr[0] = '\0'; for(ii=0; iinConstraint; ii++){ struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii]; - if( pCons->iColumn==0 && pCons->usable - && pCons->op==SQLITE_INDEX_CONSTRAINT_EQ ){ - struct sqlite3_index_constraint_usage *pUsage; - pUsage = &pIdxInfo->aConstraintUsage[ii]; - pUsage->omit = 0; - pUsage->argvIndex = 1; - return SQLITE_OK; - } - } + struct sqlite3_index_constraint_usage *pUsage; + + pUsage = &pIdxInfo->aConstraintUsage[ii]; + if( pCons->usable ){ + /* name = ? */ + if( pCons->op==SQLITE_INDEX_CONSTRAINT_EQ && pCons->iColumn==0 ){ + if( 0==tclvarAddToIdxstr(zStr, TCLVAR_NAME_EQ) ){ + pUsage->argvIndex = ++iStr; + pUsage->omit = 0; + } + } - for(ii=0; iinConstraint; ii++){ - struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii]; - if( pCons->iColumn==0 && pCons->usable - && pCons->op==SQLITE_INDEX_CONSTRAINT_MATCH ){ - struct sqlite3_index_constraint_usage *pUsage; - pUsage = &pIdxInfo->aConstraintUsage[ii]; - pUsage->omit = 1; - pUsage->argvIndex = 1; - return SQLITE_OK; + /* name MATCH ? */ + if( pCons->op==SQLITE_INDEX_CONSTRAINT_MATCH && pCons->iColumn==0 ){ + if( 0==tclvarAddToIdxstr(zStr, TCLVAR_NAME_MATCH) ){ + pUsage->argvIndex = ++iStr; + pUsage->omit = 1; + } + } + + /* value GLOB ? */ + if( pCons->op==SQLITE_INDEX_CONSTRAINT_GLOB && pCons->iColumn==2 ){ + if( 0==tclvarAddToIdxstr(zStr, TCLVAR_VALUE_GLOB) ){ + pUsage->argvIndex = ++iStr; + pUsage->omit = tclvarSetOmit(pTab->interp); + } + } + + /* value REGEXP ? */ + if( pCons->op==SQLITE_INDEX_CONSTRAINT_REGEXP && pCons->iColumn==2 ){ + if( 0==tclvarAddToIdxstr(zStr, TCLVAR_VALUE_REGEXP) ){ + pUsage->argvIndex = ++iStr; + pUsage->omit = tclvarSetOmit(pTab->interp); + } + } + + /* value LIKE ? */ + if( pCons->op==SQLITE_INDEX_CONSTRAINT_LIKE && pCons->iColumn==2 ){ + if( 0==tclvarAddToIdxstr(zStr, TCLVAR_VALUE_LIKE) ){ + pUsage->argvIndex = ++iStr; + pUsage->omit = tclvarSetOmit(pTab->interp); + } + } } } + pIdxInfo->idxStr = zStr; + pIdxInfo->needToFreeIdxStr = 1; return SQLITE_OK; } @@ -295,6 +413,7 @@ static int register_tclvar_module( int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ + int rc = TCL_OK; sqlite3 *db; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB"); @@ -302,9 +421,30 @@ static int register_tclvar_module( } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; #ifndef SQLITE_OMIT_VIRTUALTABLE - sqlite3_create_module(db, "tclvar", &tclvarModule, (void *)interp); + sqlite3_create_module(db, "tclvar", &tclvarModule, (void*)interp); + rc = Tcl_Eval(interp, + "proc like {pattern str} {\n" + " set p [string map {% * _ ?} $pattern]\n" + " string match $p $str\n" + "}\n" + "proc tclvar_filter_cmd {eq match glob regexp like} {\n" + " set res {}\n" + " set pattern $eq\n" + " if {$pattern=={}} { set pattern $match }\n" + " if {$pattern=={}} { set pattern * }\n" + " foreach v [uplevel #0 info vars $pattern] {\n" + " if {($glob=={} || [string match $glob [uplevel #0 set $v]])\n" + " && ($like=={} || [like $like [uplevel #0 set $v]])\n" + " && ($regexp=={} || [regexp $regexp [uplevel #0 set $v]])\n" + " } {\n" + " lappend res $v\n" + " }\n" + " }\n" + " set res\n" + "}\n" + ); #endif - return TCL_OK; + return rc; } #endif diff --git a/src/test_windirent.c b/src/test_windirent.c new file mode 100644 index 0000000000..11d7dc07d0 --- /dev/null +++ b/src/test_windirent.c @@ -0,0 +1,157 @@ +/* +** 2015 November 30 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains code to implement most of the opendir() family of +** POSIX functions on Win32 using the MSVCRT. +*/ + +#if defined(_WIN32) && defined(_MSC_VER) + +#include "test_windirent.h" + +/* +** Implementation of the POSIX opendir() function using the MSVCRT. +*/ +LPDIR opendir( + const char *dirname +){ + struct _finddata_t data; + LPDIR dirp = (LPDIR)sqlite3_malloc(sizeof(DIR)); + SIZE_T namesize = sizeof(data.name) / sizeof(data.name[0]); + + if( dirp==NULL ) return NULL; + memset(dirp, 0, sizeof(DIR)); + + /* TODO: Remove this if Unix-style root paths are not used. */ + if( sqlite3_stricmp(dirname, "/")==0 ){ + dirname = getenv("SystemDrive"); + } + + _snprintf(data.name, namesize, "%s\\*", dirname); + dirp->d_handle = _findfirst(data.name, &data); + + if( dirp->d_handle==BAD_INTPTR_T ){ + closedir(dirp); + return NULL; + } + + /* TODO: Remove this block to allow hidden and system files. */ + if( data.attrib&_A_HIDDEN || data.attrib&_A_SYSTEM ){ + if( _findnext(dirp->d_handle, &data)==-1 ){ + closedir(dirp); + return NULL; + } + } + + dirp->d_first.d_attributes = data.attrib; + strncpy(dirp->d_first.d_name, data.name, NAME_MAX); + dirp->d_first.d_name[NAME_MAX] = '\0'; + + return dirp; +} + +/* +** Implementation of the POSIX readdir() function using the MSVCRT. +*/ +LPDIRENT readdir( + LPDIR dirp +){ + struct _finddata_t data; + + if( dirp==NULL ) return NULL; + + if( dirp->d_first.d_ino==0 ){ + dirp->d_first.d_ino++; + dirp->d_next.d_ino++; + + return &dirp->d_first; + } + +next: + + if( _findnext(dirp->d_handle, &data)==-1 ) return NULL; + + /* TODO: Remove this block to allow hidden and system files. */ + if( data.attrib&_A_HIDDEN ) goto next; + if( data.attrib&_A_SYSTEM ) goto next; + + dirp->d_next.d_ino++; + dirp->d_next.d_attributes = data.attrib; + strncpy(dirp->d_next.d_name, data.name, NAME_MAX); + dirp->d_next.d_name[NAME_MAX] = '\0'; + + return &dirp->d_next; +} + +/* +** Implementation of the POSIX readdir_r() function using the MSVCRT. +*/ +INT readdir_r( + LPDIR dirp, + LPDIRENT entry, + LPDIRENT *result +){ + struct _finddata_t data; + + if( dirp==NULL ) return EBADF; + + if( dirp->d_first.d_ino==0 ){ + dirp->d_first.d_ino++; + dirp->d_next.d_ino++; + + entry->d_ino = dirp->d_first.d_ino; + entry->d_attributes = dirp->d_first.d_attributes; + strncpy(entry->d_name, dirp->d_first.d_name, NAME_MAX); + entry->d_name[NAME_MAX] = '\0'; + + *result = entry; + return 0; + } + +next: + + if( _findnext(dirp->d_handle, &data)==-1 ){ + *result = NULL; + return ENOENT; + } + + /* TODO: Remove this block to allow hidden and system files. */ + if( data.attrib&_A_HIDDEN ) goto next; + if( data.attrib&_A_SYSTEM ) goto next; + + entry->d_ino = (ino_t)-1; /* not available */ + entry->d_attributes = data.attrib; + strncpy(entry->d_name, data.name, NAME_MAX); + entry->d_name[NAME_MAX] = '\0'; + + *result = entry; + return 0; +} + +/* +** Implementation of the POSIX closedir() function using the MSVCRT. +*/ +INT closedir( + LPDIR dirp +){ + INT result = 0; + + if( dirp==NULL ) return EINVAL; + + if( dirp->d_handle!=NULL_INTPTR_T && dirp->d_handle!=BAD_INTPTR_T ){ + result = _findclose(dirp->d_handle); + } + + sqlite3_free(dirp); + return result; +} + +#endif /* defined(WIN32) && defined(_MSC_VER) */ diff --git a/src/test_windirent.h b/src/test_windirent.h new file mode 100644 index 0000000000..0b8d1a7b51 --- /dev/null +++ b/src/test_windirent.h @@ -0,0 +1,105 @@ +/* +** 2015 November 30 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains declarations for most of the opendir() family of +** POSIX functions on Win32 using the MSVCRT. +*/ + +#if defined(_WIN32) && defined(_MSC_VER) + +/* +** We need several data types from the Windows SDK header. +*/ + +#define WIN32_LEAN_AND_MEAN +#include "windows.h" + +/* +** We need several support functions from the SQLite core. +*/ + +#include "sqlite3.h" + +/* +** We need several things from the ANSI and MSVCRT headers. +*/ + +#include +#include +#include +#include +#include + +/* +** We may need to provide the "ino_t" type. +*/ + +#ifndef INO_T_DEFINED + #define INO_T_DEFINED + typedef unsigned short ino_t; +#endif + +/* +** We need to define "NAME_MAX" if it was not present in "limits.h". +*/ + +#ifndef NAME_MAX +# ifdef FILENAME_MAX +# define NAME_MAX (FILENAME_MAX) +# else +# define NAME_MAX (260) +# endif +#endif + +/* +** We need to define "NULL_INTPTR_T" and "BAD_INTPTR_T". +*/ + +#ifndef NULL_INTPTR_T +# define NULL_INTPTR_T ((intptr_t)(0)) +#endif + +#ifndef BAD_INTPTR_T +# define BAD_INTPTR_T ((intptr_t)(-1)) +#endif + +/* +** We need to provide the necessary structures and related types. +*/ + +typedef struct DIRENT DIRENT; +typedef struct DIR DIR; +typedef DIRENT *LPDIRENT; +typedef DIR *LPDIR; + +struct DIRENT { + ino_t d_ino; /* Sequence number, do not use. */ + unsigned d_attributes; /* Win32 file attributes. */ + char d_name[NAME_MAX + 1]; /* Name within the directory. */ +}; + +struct DIR { + intptr_t d_handle; /* Value returned by "_findfirst". */ + DIRENT d_first; /* DIRENT constructed based on "_findfirst". */ + DIRENT d_next; /* DIRENT constructed based on "_findnext". */ +}; + +/* +** Finally, we can provide the function prototypes for the opendir(), +** readdir(), readdir_r(), and closedir() POSIX functions. +*/ + +extern LPDIR opendir(const char *dirname); +extern LPDIRENT readdir(LPDIR dirp); +extern INT readdir_r(LPDIR dirp, LPDIRENT entry, LPDIRENT *result); +extern INT closedir(LPDIR dirp); + +#endif /* defined(WIN32) && defined(_MSC_VER) */ diff --git a/src/update.c b/src/update.c index 08b28ad7ec..d0f958a295 100644 --- a/src/update.c +++ b/src/update.c @@ -263,10 +263,12 @@ 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 + /* The SET expressions are not actually used inside the WHERE loop. + ** So reset the colUsed mask. Unless this is a virtual table. In that + ** case, set all bits of the colUsed mask (to ensure that the virtual + ** table implementation makes all columns available). */ - pTabList->a[0].colUsed = 0; + pTabList->a[0].colUsed = IsVirtual(pTab) ? (Bitmask)-1 : 0; hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngKey); diff --git a/src/util.c b/src/util.c index f218bb7941..b4c5e62bb3 100644 --- a/src/util.c +++ b/src/util.c @@ -555,7 +555,8 @@ int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){ testcase( i==18 ); testcase( i==19 ); testcase( i==20 ); - if( (c!=0 && &zNum[i]19*incr || nonNum ){ + if( (c!=0 && &zNum[i]19*incr || nonNum ){ /* zNum is empty or contains non-numeric text or is longer ** than 19 digits (thus guaranteeing that it is too large) */ return 1; @@ -844,7 +845,8 @@ u8 sqlite3GetVarint(const unsigned char *p, u64 *v){ /* a: p0<<28 | p2<<14 | p4 (unmasked) */ if (!(a&0x80)) { - /* we can skip these cause they were (effectively) done above in calc'ing s */ + /* we can skip these cause they were (effectively) done above + ** while calculating s */ /* a &= (0x7f<<28)|(0x7f<<14)|(0x7f); */ /* b &= (0x7f<<14)|(0x7f); */ b = b<<7; diff --git a/src/vdbe.c b/src/vdbe.c index 5557db6d43..9589f876ac 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -175,7 +175,7 @@ int sqlite3_found_count = 0; && sqlite3VdbeMemMakeWriteable(P) ){ goto no_mem;} /* Return true if the cursor was opened using the OP_OpenSorter opcode. */ -#define isSorter(x) ((x)->pSorter!=0) +#define isSorter(x) ((x)->eCurType==CURTYPE_SORTER) /* ** Allocate VdbeCursor number iCur. Return a pointer to it. Return NULL @@ -186,7 +186,7 @@ static VdbeCursor *allocateCursor( int iCur, /* Index of the new VdbeCursor */ int nField, /* Number of fields in the table or index */ int iDb, /* Database the cursor belongs to, or -1 */ - int isBtreeCursor /* True for B-Tree. False for pseudo-table or vtab */ + u8 eCurType /* Type of the new cursor */ ){ /* Find the memory cell that will be used to store the blob of memory ** required for this VdbeCursor structure. It is convenient to use a @@ -212,7 +212,7 @@ static VdbeCursor *allocateCursor( VdbeCursor *pCx = 0; nByte = ROUND8(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField + - (isBtreeCursor?sqlite3BtreeCursorSize():0); + (eCurType==CURTYPE_BTREE?sqlite3BtreeCursorSize():0); assert( iCurnCursor ); if( p->apCsr[iCur] ){ @@ -222,13 +222,14 @@ static VdbeCursor *allocateCursor( if( SQLITE_OK==sqlite3VdbeMemClearAndResize(pMem, nByte) ){ p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->z; memset(pCx, 0, sizeof(VdbeCursor)); + pCx->eCurType = eCurType; pCx->iDb = iDb; pCx->nField = nField; pCx->aOffset = &pCx->aType[nField]; - if( isBtreeCursor ){ - pCx->pCursor = (BtCursor*) + if( eCurType==CURTYPE_BTREE ){ + pCx->uc.pCursor = (BtCursor*) &pMem->z[ROUND8(sizeof(VdbeCursor))+2*sizeof(u32)*nField]; - sqlite3BtreeCursorZero(pCx->pCursor); + sqlite3BtreeCursorZero(pCx->uc.pCursor); } } return pCx; @@ -1097,6 +1098,7 @@ case OP_String: { /* out2 */ pOut->n = pOp->p1; pOut->enc = encoding; UPDATE_MAX_BLOBSIZE(pOut); +#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS if( pOp->p5 ){ assert( pOp->p3>0 ); assert( pOp->p3<=(p->nMem-p->nCursor) ); @@ -1104,6 +1106,7 @@ case OP_String: { /* out2 */ assert( pIn3->flags & MEM_Int ); if( pIn3->u.i ) pOut->flags = MEM_Blob|MEM_Static|MEM_Term; } +#endif break; } @@ -2393,21 +2396,19 @@ case OP_Column: { assert( pC!=0 ); assert( p2nField ); aOffset = pC->aOffset; -#ifndef SQLITE_OMIT_VIRTUALTABLE - assert( pC->pVtabCursor==0 ); /* OP_Column never called on virtual table */ -#endif - pCrsr = pC->pCursor; - assert( pCrsr!=0 || pC->pseudoTableReg>0 ); /* pCrsr NULL on PseudoTables */ - assert( pCrsr!=0 || pC->nullRow ); /* pC->nullRow on PseudoTables */ + assert( pC->eCurType!=CURTYPE_VTAB ); + assert( pC->eCurType!=CURTYPE_PSEUDO || pC->nullRow ); + assert( pC->eCurType!=CURTYPE_SORTER ); + pCrsr = pC->uc.pCursor; /* 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 ){ if( pC->nullRow ){ - if( pCrsr==0 ){ - assert( pC->pseudoTableReg>0 ); - pReg = &aMem[pC->pseudoTableReg]; + if( pC->eCurType==CURTYPE_PSEUDO ){ + assert( pC->uc.pseudoTableReg>0 ); + pReg = &aMem[pC->uc.pseudoTableReg]; assert( pReg->flags & MEM_Blob ); assert( memIsValid(pReg) ); pC->payloadSize = pC->szRow = avail = pReg->n; @@ -2417,6 +2418,7 @@ case OP_Column: { goto op_column_out; } }else{ + assert( pC->eCurType==CURTYPE_BTREE ); assert( pCrsr ); if( pC->isTable==0 ){ assert( sqlite3BtreeCursorIsValid(pCrsr) ); @@ -2437,12 +2439,11 @@ case OP_Column: { assert( avail<=65536 ); /* Maximum page size is 64KiB */ if( pC->payloadSize <= (u32)avail ){ pC->szRow = pC->payloadSize; + }else if( pC->payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ + goto too_big; }else{ 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); @@ -2806,7 +2807,8 @@ case OP_Count: { /* out2 */ i64 nEntry; BtCursor *pCrsr; - pCrsr = p->apCsr[pOp->p1]->pCursor; + assert( p->apCsr[pOp->p1]->eCurType==CURTYPE_BTREE ); + pCrsr = p->apCsr[pOp->p1]->uc.pCursor; assert( pCrsr ); nEntry = 0; /* Not needed. Only used to silence a warning. */ rc = sqlite3BtreeCount(pCrsr, &nEntry); @@ -3391,12 +3393,12 @@ case OP_OpenWrite: 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); + pCur = allocateCursor(p, pOp->p1, nField, iDb, CURTYPE_BTREE); if( pCur==0 ) goto no_mem; pCur->nullRow = 1; pCur->isOrdered = 1; pCur->pgnoRoot = p2; - rc = sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->pCursor); + rc = sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->uc.pCursor); pCur->pKeyInfo = pKeyInfo; /* Set the VdbeCursor.isTable variable. Previous versions of ** SQLite used to check if the root-page flags were sane at this point @@ -3411,7 +3413,7 @@ open_cursor_set_hints: #ifdef SQLITE_ENABLE_CURSOR_HINT testcase( pOp->p2 & OPFLAG_SEEKEQ ); #endif - sqlite3BtreeCursorHintFlags(pCur->pCursor, + sqlite3BtreeCursorHintFlags(pCur->uc.pCursor, (pOp->p5 & (OPFLAG_BULKCSR|OPFLAG_SEEKEQ))); break; } @@ -3455,7 +3457,7 @@ case OP_OpenEphemeral: { SQLITE_OPEN_TRANSIENT_DB; assert( pOp->p1>=0 ); assert( pOp->p2>=0 ); - pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1); + pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, CURTYPE_BTREE); if( pCx==0 ) goto no_mem; pCx->nullRow = 1; pCx->isEphemeral = 1; @@ -3479,11 +3481,13 @@ case OP_OpenEphemeral: { assert( pKeyInfo->db==db ); assert( pKeyInfo->enc==ENC(db) ); pCx->pKeyInfo = pKeyInfo; - rc = sqlite3BtreeCursor(pCx->pBt, pgno, BTREE_WRCSR, pKeyInfo, pCx->pCursor); + rc = sqlite3BtreeCursor(pCx->pBt, pgno, BTREE_WRCSR, + pKeyInfo, pCx->uc.pCursor); } pCx->isTable = 0; }else{ - rc = sqlite3BtreeCursor(pCx->pBt, MASTER_ROOT, BTREE_WRCSR, 0, pCx->pCursor); + rc = sqlite3BtreeCursor(pCx->pBt, MASTER_ROOT, BTREE_WRCSR, + 0, pCx->uc.pCursor); pCx->isTable = 1; } } @@ -3506,7 +3510,7 @@ case OP_SorterOpen: { assert( pOp->p1>=0 ); assert( pOp->p2>=0 ); - pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1); + pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, CURTYPE_SORTER); if( pCx==0 ) goto no_mem; pCx->pKeyInfo = pOp->p4.pKeyInfo; assert( pCx->pKeyInfo->db==db ); @@ -3526,7 +3530,7 @@ case OP_SequenceTest: { VdbeCursor *pC; assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; - assert( pC->pSorter ); + assert( isSorter(pC) ); if( (pC->seqCount++)==0 ){ goto jump_to_p2; } @@ -3554,10 +3558,10 @@ case OP_OpenPseudo: { assert( pOp->p1>=0 ); assert( pOp->p3>=0 ); - pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, 0); + pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, CURTYPE_PSEUDO); if( pCx==0 ) goto no_mem; pCx->nullRow = 1; - pCx->pseudoTableReg = pOp->p2; + pCx->uc.pseudoTableReg = pOp->p2; pCx->isTable = 1; assert( pOp->p5==0 ); break; @@ -3589,7 +3593,7 @@ case OP_Close: { case OP_ColumnsUsed: { VdbeCursor *pC; pC = p->apCsr[pOp->p1]; - assert( pC->pCursor ); + assert( pC->eCurType==CURTYPE_BTREE ); pC->maskUsed = *(u64*)pOp->p4.pI64; break; } @@ -3697,12 +3701,12 @@ case OP_SeekGT: { /* jump, in3 */ assert( pOp->p2!=0 ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); - assert( pC->pseudoTableReg==0 ); + assert( pC->eCurType==CURTYPE_BTREE ); assert( OP_SeekLE == OP_SeekLT+1 ); assert( OP_SeekGE == OP_SeekLT+2 ); assert( OP_SeekGT == OP_SeekLT+3 ); assert( pC->isOrdered ); - assert( pC->pCursor!=0 ); + assert( pC->uc.pCursor!=0 ); oc = pOp->opcode; eqOnly = 0; pC->nullRow = 0; @@ -3712,7 +3716,7 @@ case OP_SeekGT: { /* jump, in3 */ if( pC->isTable ){ /* The BTREE_SEEK_EQ flag is only set on index cursors */ - assert( sqlite3BtreeCursorHasHint(pC->pCursor, BTREE_SEEK_EQ)==0 ); + assert( sqlite3BtreeCursorHasHint(pC->uc.pCursor, BTREE_SEEK_EQ)==0 ); /* The input value in P3 might be of any type: integer, real, string, ** blob, or NULL. But it needs to be an integer before we can do @@ -3756,7 +3760,7 @@ case OP_SeekGT: { /* jump, in3 */ if( (oc & 0x0001)==(OP_SeekLT & 0x0001) ) oc++; } } - rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, 0, (u64)iKey, 0, &res); + rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, 0, (u64)iKey, 0, &res); pC->movetoTarget = iKey; /* Used by OP_Delete */ if( rc!=SQLITE_OK ){ goto abort_due_to_error; @@ -3766,7 +3770,7 @@ case OP_SeekGT: { /* jump, in3 */ ** OP_SeekLE opcodes are allowed, and these must be immediately followed ** by an OP_IdxGT or OP_IdxLT opcode, respectively, with the same key. */ - if( sqlite3BtreeCursorHasHint(pC->pCursor, BTREE_SEEK_EQ) ){ + if( sqlite3BtreeCursorHasHint(pC->uc.pCursor, BTREE_SEEK_EQ) ){ eqOnly = 1; assert( pOp->opcode==OP_SeekGE || pOp->opcode==OP_SeekLE ); assert( pOp[1].opcode==OP_IdxLT || pOp[1].opcode==OP_IdxGT ); @@ -3801,7 +3805,7 @@ case OP_SeekGT: { /* jump, in3 */ #endif ExpandBlob(r.aMem); r.eqSeen = 0; - rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, &r, 0, 0, &res); + rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, &r, 0, 0, &res); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } @@ -3818,7 +3822,7 @@ case OP_SeekGT: { /* jump, in3 */ if( oc>=OP_SeekGE ){ assert( oc==OP_SeekGE || oc==OP_SeekGT ); if( res<0 || (res==0 && oc==OP_SeekGT) ){ res = 0; - rc = sqlite3BtreeNext(pC->pCursor, &res); + rc = sqlite3BtreeNext(pC->uc.pCursor, &res); if( rc!=SQLITE_OK ) goto abort_due_to_error; }else{ res = 0; @@ -3827,13 +3831,13 @@ case OP_SeekGT: { /* jump, in3 */ assert( oc==OP_SeekLT || oc==OP_SeekLE ); if( res>0 || (res==0 && oc==OP_SeekLT) ){ res = 0; - rc = sqlite3BtreePrevious(pC->pCursor, &res); + rc = sqlite3BtreePrevious(pC->uc.pCursor, &res); if( rc!=SQLITE_OK ) goto abort_due_to_error; }else{ /* res might be negative because the table is empty. Check to ** see if this is the case. */ - res = sqlite3BtreeEof(pC->pCursor); + res = sqlite3BtreeEof(pC->uc.pCursor); } } seek_not_found: @@ -3864,7 +3868,8 @@ case OP_Seek: { /* in2 */ assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); - assert( pC->pCursor!=0 ); + assert( pC->eCurType==CURTYPE_BTREE ); + assert( pC->uc.pCursor!=0 ); assert( pC->isTable ); pC->nullRow = 0; pIn2 = &aMem[pOp->p2]; @@ -3958,7 +3963,8 @@ case OP_Found: { /* jump, in3 */ pC->seekOp = pOp->opcode; #endif pIn3 = &aMem[pOp->p3]; - assert( pC->pCursor!=0 ); + assert( pC->eCurType==CURTYPE_BTREE ); + assert( pC->uc.pCursor!=0 ); assert( pC->isTable==0 ); pFree = 0; if( pOp->p4.i>0 ){ @@ -3995,7 +4001,7 @@ case OP_Found: { /* jump, in3 */ } } } - rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, pIdxKey, 0, 0, &res); + rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, pIdxKey, 0, 0, &res); sqlite3DbFree(db, pFree); if( rc!=SQLITE_OK ){ break; @@ -4049,8 +4055,8 @@ case OP_NotExists: { /* jump, in3 */ pC->seekOp = 0; #endif assert( pC->isTable ); - assert( pC->pseudoTableReg==0 ); - pCrsr = pC->pCursor; + assert( pC->eCurType==CURTYPE_BTREE ); + pCrsr = pC->uc.pCursor; assert( pCrsr!=0 ); res = 0; iKey = pIn3->u.i; @@ -4084,6 +4090,7 @@ case OP_NotExists: { /* jump, in3 */ case OP_Sequence: { /* out2 */ assert( pOp->p1>=0 && pOp->p1nCursor ); assert( p->apCsr[pOp->p1]!=0 ); + assert( p->apCsr[pOp->p1]->eCurType!=CURTYPE_VTAB ); pOut = out2Prerelease(p, pOp); pOut->u.i = p->apCsr[pOp->p1]->seqCount++; break; @@ -4119,7 +4126,8 @@ case OP_NewRowid: { /* out2 */ assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); - assert( pC->pCursor!=0 ); + assert( pC->eCurType==CURTYPE_BTREE ); + assert( pC->uc.pCursor!=0 ); { /* The next rowid or record number (different terms for the same ** thing) is obtained in a two-step algorithm. @@ -4147,15 +4155,15 @@ case OP_NewRowid: { /* out2 */ #endif if( !pC->useRandomRowid ){ - rc = sqlite3BtreeLast(pC->pCursor, &res); + rc = sqlite3BtreeLast(pC->uc.pCursor, &res); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } if( res ){ v = 1; /* IMP: R-61914-48074 */ }else{ - assert( sqlite3BtreeCursorIsValid(pC->pCursor) ); - rc = sqlite3BtreeKeySize(pC->pCursor, &v); + assert( sqlite3BtreeCursorIsValid(pC->uc.pCursor) ); + rc = sqlite3BtreeKeySize(pC->uc.pCursor, &v); assert( rc==SQLITE_OK ); /* Cannot fail following BtreeLast() */ if( v>=MAX_ROWID ){ pC->useRandomRowid = 1; @@ -4206,7 +4214,7 @@ case OP_NewRowid: { /* out2 */ do{ sqlite3_randomness(sizeof(v), &v); v &= (MAX_ROWID>>1); v++; /* Ensure that v is greater than zero */ - }while( ((rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, 0, (u64)v, + }while( ((rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, 0, (u64)v, 0, &res))==SQLITE_OK) && (res==0) && (++cnt<100)); @@ -4286,8 +4294,8 @@ case OP_InsertInt: { assert( memIsValid(pData) ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); - assert( pC->pCursor!=0 ); - assert( pC->pseudoTableReg==0 ); + assert( pC->eCurType==CURTYPE_BTREE ); + assert( pC->uc.pCursor!=0 ); assert( pC->isTable ); assert( pOp->p4type==P4_TABLE || pOp->p4type>=P4_STATIC ); REGISTER_TRACE(pOp->p2, pData); @@ -4336,7 +4344,7 @@ case OP_InsertInt: { }else{ nZero = 0; } - rc = sqlite3BtreeInsert(pC->pCursor, 0, iKey, + rc = sqlite3BtreeInsert(pC->uc.pCursor, 0, iKey, pData->z, pData->n, nZero, (pOp->p5 & OPFLAG_APPEND)!=0, seekResult ); @@ -4387,17 +4395,17 @@ case OP_Delete: { assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); - assert( pC->pCursor!=0 ); /* Only valid for real tables, no pseudotables */ + assert( pC->eCurType==CURTYPE_BTREE ); + assert( pC->uc.pCursor!=0 ); assert( pC->deferredMoveto==0 ); - #ifdef SQLITE_DEBUG if( pOp->p4type==P4_TABLE && HasRowid(pOp->p4.pTab) && pOp->p5==0 ){ /* If p5 is zero, the seek operation that positioned the cursor prior to ** OP_Delete will have also set the pC->movetoTarget field to the rowid of ** the row that is being deleted */ i64 iKey = 0; - sqlite3BtreeKeySize(pC->pCursor, &iKey); + sqlite3BtreeKeySize(pC->uc.pCursor, &iKey); assert( pC->movetoTarget==iKey ); } #endif @@ -4412,7 +4420,7 @@ case OP_Delete: { zDb = db->aDb[pC->iDb].zName; pTab = pOp->p4.pTab; if( pOp->p5 && pC->isTable ){ - sqlite3BtreeKeySize(pC->pCursor, &pC->movetoTarget); + sqlite3BtreeKeySize(pC->uc.pCursor, &pC->movetoTarget); } } @@ -4430,7 +4438,7 @@ case OP_Delete: { if( opflags & OPFLAG_ISNOOP ) break; - rc = sqlite3BtreeDelete(pC->pCursor, pOp->p5); + rc = sqlite3BtreeDelete(pC->uc.pCursor, pOp->p5); pC->cacheStatus = CACHE_STALE; /* Invoke the update-hook if required. */ @@ -4550,14 +4558,14 @@ case OP_RowData: { /* Note that RowKey and RowData are really exactly the same instruction */ assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pC->eCurType==CURTYPE_BTREE ); assert( isSorter(pC)==0 ); assert( pC->isTable || pOp->opcode!=OP_RowData ); assert( pC->isTable==0 || pOp->opcode==OP_RowData ); - assert( pC!=0 ); assert( pC->nullRow==0 ); - assert( pC->pseudoTableReg==0 ); - assert( pC->pCursor!=0 ); - pCrsr = pC->pCursor; + assert( pC->uc.pCursor!=0 ); + pCrsr = pC->uc.pCursor; /* The OP_RowKey and OP_RowData opcodes always follow OP_NotExists or ** OP_Rewind/Op_Next with no intervening instructions that might invalidate @@ -4625,29 +4633,31 @@ case OP_Rowid: { /* out2 */ assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); - assert( pC->pseudoTableReg==0 || pC->nullRow ); + assert( pC->eCurType!=CURTYPE_PSEUDO || pC->nullRow ); if( pC->nullRow ){ pOut->flags = MEM_Null; break; }else if( pC->deferredMoveto ){ v = pC->movetoTarget; #ifndef SQLITE_OMIT_VIRTUALTABLE - }else if( pC->pVtabCursor ){ - pVtab = pC->pVtabCursor->pVtab; + }else if( pC->eCurType==CURTYPE_VTAB ){ + assert( pC->uc.pVCur!=0 ); + pVtab = pC->uc.pVCur->pVtab; pModule = pVtab->pModule; assert( pModule->xRowid ); - rc = pModule->xRowid(pC->pVtabCursor, &v); + rc = pModule->xRowid(pC->uc.pVCur, &v); sqlite3VtabImportErrmsg(p, pVtab); #endif /* SQLITE_OMIT_VIRTUALTABLE */ }else{ - assert( pC->pCursor!=0 ); + assert( pC->eCurType==CURTYPE_BTREE ); + assert( pC->uc.pCursor!=0 ); rc = sqlite3VdbeCursorRestore(pC); if( rc ) goto abort_due_to_error; if( pC->nullRow ){ pOut->flags = MEM_Null; break; } - rc = sqlite3BtreeKeySize(pC->pCursor, &v); + rc = sqlite3BtreeKeySize(pC->uc.pCursor, &v); assert( rc==SQLITE_OK ); /* Always so because of CursorRestore() above */ } pOut->u.i = v; @@ -4668,8 +4678,9 @@ case OP_NullRow: { assert( pC!=0 ); pC->nullRow = 1; pC->cacheStatus = CACHE_STALE; - if( pC->pCursor ){ - sqlite3BtreeClearCursor(pC->pCursor); + if( pC->eCurType==CURTYPE_BTREE ){ + assert( pC->uc.pCursor!=0 ); + sqlite3BtreeClearCursor(pC->uc.pCursor); } break; } @@ -4694,7 +4705,8 @@ case OP_Last: { /* jump */ assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); - pCrsr = pC->pCursor; + assert( pC->eCurType==CURTYPE_BTREE ); + pCrsr = pC->uc.pCursor; res = 0; assert( pCrsr!=0 ); rc = sqlite3BtreeLast(pCrsr, &res); @@ -4762,7 +4774,8 @@ case OP_Rewind: { /* jump */ if( isSorter(pC) ){ rc = sqlite3VdbeSorterRewind(pC, &res); }else{ - pCrsr = pC->pCursor; + assert( pC->eCurType==CURTYPE_BTREE ); + pCrsr = pC->uc.pCursor; assert( pCrsr ); rc = sqlite3BtreeFirst(pCrsr, &res); pC->deferredMoveto = 0; @@ -4859,7 +4872,7 @@ case OP_Next: /* jump */ res = pOp->p3; assert( pC!=0 ); assert( pC->deferredMoveto==0 ); - assert( pC->pCursor ); + assert( pC->eCurType==CURTYPE_BTREE ); assert( res==0 || (res==1 && pC->isTable==0) ); testcase( res==1 ); assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite3BtreeNext ); @@ -4876,7 +4889,7 @@ case OP_Next: /* jump */ || pC->seekOp==OP_SeekLT || pC->seekOp==OP_SeekLE || pC->seekOp==OP_Last ); - rc = pOp->p4.xAdvance(pC->pCursor, &res); + rc = pOp->p4.xAdvance(pC->uc.pCursor, &res); next_tail: pC->cacheStatus = CACHE_STALE; VdbeBranchTaken(res==0,2); @@ -4927,7 +4940,7 @@ case OP_IdxInsert: { /* in2 */ pIn2 = &aMem[pOp->p2]; assert( pIn2->flags & MEM_Blob ); if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; - assert( pC->pCursor!=0 ); + assert( pC->eCurType==CURTYPE_BTREE || pOp->opcode==OP_SorterInsert ); assert( pC->isTable==0 ); rc = ExpandBlob(pIn2); if( rc==SQLITE_OK ){ @@ -4936,7 +4949,7 @@ case OP_IdxInsert: { /* in2 */ }else{ nKey = pIn2->n; zKey = pIn2->z; - rc = sqlite3BtreeInsert(pC->pCursor, zKey, nKey, "", 0, 0, pOp->p3, + rc = sqlite3BtreeInsert(pC->uc.pCursor, zKey, nKey, "", 0, 0, pOp->p3, ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0) ); assert( pC->deferredMoveto==0 ); @@ -4964,7 +4977,8 @@ case OP_IdxDelete: { assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); - pCrsr = pC->pCursor; + assert( pC->eCurType==CURTYPE_BTREE ); + pCrsr = pC->uc.pCursor; assert( pCrsr!=0 ); assert( pOp->p5==0 ); r.pKeyInfo = pC->pKeyInfo; @@ -5001,7 +5015,8 @@ case OP_IdxRowid: { /* out2 */ assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); - pCrsr = pC->pCursor; + assert( pC->eCurType==CURTYPE_BTREE ); + pCrsr = pC->uc.pCursor; assert( pCrsr!=0 ); pOut->flags = MEM_Null; assert( pC->isTable==0 ); @@ -5082,7 +5097,8 @@ case OP_IdxGE: { /* jump */ pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( pC->isOrdered ); - assert( pC->pCursor!=0); + assert( pC->eCurType==CURTYPE_BTREE ); + assert( pC->uc.pCursor!=0); assert( pC->deferredMoveto==0 ); assert( pOp->p5==0 || pOp->p5==1 ); assert( pOp->p4type==P4_INT32 ); @@ -5215,11 +5231,12 @@ case OP_ResetSorter: { assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); - if( pC->pSorter ){ - sqlite3VdbeSorterReset(db, pC->pSorter); + if( isSorter(pC) ){ + sqlite3VdbeSorterReset(db, pC->uc.pSorter); }else{ + assert( pC->eCurType==CURTYPE_BTREE ); assert( pC->isEphemeral ); - rc = sqlite3BtreeClearTableOfCursor(pC->pCursor); + rc = sqlite3BtreeClearTableOfCursor(pC->uc.pCursor); } break; } @@ -6293,33 +6310,33 @@ case OP_VDestroy: { */ case OP_VOpen: { VdbeCursor *pCur; - sqlite3_vtab_cursor *pVtabCursor; + sqlite3_vtab_cursor *pVCur; sqlite3_vtab *pVtab; const sqlite3_module *pModule; assert( p->bIsReader ); pCur = 0; - pVtabCursor = 0; + pVCur = 0; pVtab = pOp->p4.pVtab->pVtab; if( pVtab==0 || NEVER(pVtab->pModule==0) ){ rc = SQLITE_LOCKED; break; } pModule = pVtab->pModule; - rc = pModule->xOpen(pVtab, &pVtabCursor); + rc = pModule->xOpen(pVtab, &pVCur); sqlite3VtabImportErrmsg(p, pVtab); if( SQLITE_OK==rc ){ /* Initialize sqlite3_vtab_cursor base class */ - pVtabCursor->pVtab = pVtab; + pVCur->pVtab = pVtab; /* Initialize vdbe cursor object */ - pCur = allocateCursor(p, pOp->p1, 0, -1, 0); + pCur = allocateCursor(p, pOp->p1, 0, -1, CURTYPE_VTAB); if( pCur ){ - pCur->pVtabCursor = pVtabCursor; + pCur->uc.pVCur = pVCur; pVtab->nRef++; }else{ assert( db->mallocFailed ); - pModule->xClose(pVtabCursor); + pModule->xClose(pVCur); goto no_mem; } } @@ -6353,7 +6370,7 @@ case OP_VFilter: { /* jump */ const sqlite3_module *pModule; Mem *pQuery; Mem *pArgc; - sqlite3_vtab_cursor *pVtabCursor; + sqlite3_vtab_cursor *pVCur; sqlite3_vtab *pVtab; VdbeCursor *pCur; int res; @@ -6365,9 +6382,9 @@ case OP_VFilter: { /* jump */ pCur = p->apCsr[pOp->p1]; assert( memIsValid(pQuery) ); REGISTER_TRACE(pOp->p3, pQuery); - assert( pCur->pVtabCursor ); - pVtabCursor = pCur->pVtabCursor; - pVtab = pVtabCursor->pVtab; + assert( pCur->eCurType==CURTYPE_VTAB ); + pVCur = pCur->uc.pVCur; + pVtab = pVCur->pVtab; pModule = pVtab->pModule; /* Grab the index number and argc parameters */ @@ -6381,10 +6398,10 @@ case OP_VFilter: { /* jump */ for(i = 0; ixFilter(pVtabCursor, iQuery, pOp->p4.z, nArg, apArg); + rc = pModule->xFilter(pVCur, iQuery, pOp->p4.z, nArg, apArg); sqlite3VtabImportErrmsg(p, pVtab); if( rc==SQLITE_OK ){ - res = pModule->xEof(pVtabCursor); + res = pModule->xEof(pVCur); } pCur->nullRow = 0; VdbeBranchTaken(res!=0,2); @@ -6408,7 +6425,7 @@ case OP_VColumn: { sqlite3_context sContext; VdbeCursor *pCur = p->apCsr[pOp->p1]; - assert( pCur->pVtabCursor ); + assert( pCur->eCurType==CURTYPE_VTAB ); assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) ); pDest = &aMem[pOp->p3]; memAboutToChange(p, pDest); @@ -6416,13 +6433,13 @@ case OP_VColumn: { sqlite3VdbeMemSetNull(pDest); break; } - pVtab = pCur->pVtabCursor->pVtab; + pVtab = pCur->uc.pVCur->pVtab; pModule = pVtab->pModule; assert( pModule->xColumn ); memset(&sContext, 0, sizeof(sContext)); sContext.pOut = pDest; MemSetTypeFlag(pDest, MEM_Null); - rc = pModule->xColumn(pCur->pVtabCursor, &sContext, pOp->p2); + rc = pModule->xColumn(pCur->uc.pVCur, &sContext, pOp->p2); sqlite3VtabImportErrmsg(p, pVtab); if( sContext.isError ){ rc = sContext.isError; @@ -6453,11 +6470,11 @@ case OP_VNext: { /* jump */ res = 0; pCur = p->apCsr[pOp->p1]; - assert( pCur->pVtabCursor ); + assert( pCur->eCurType==CURTYPE_VTAB ); if( pCur->nullRow ){ break; } - pVtab = pCur->pVtabCursor->pVtab; + pVtab = pCur->uc.pVCur->pVtab; pModule = pVtab->pModule; assert( pModule->xNext ); @@ -6467,10 +6484,10 @@ case OP_VNext: { /* jump */ ** data is available) and the error code returned when xColumn or ** some other method is next invoked on the save virtual table cursor. */ - rc = pModule->xNext(pCur->pVtabCursor); + rc = pModule->xNext(pCur->uc.pVCur); sqlite3VtabImportErrmsg(p, pVtab); if( rc==SQLITE_OK ){ - res = pModule->xEof(pCur->pVtabCursor); + res = pModule->xEof(pCur->uc.pVCur); } VdbeBranchTaken(!res,2); if( !res ){ @@ -6694,7 +6711,9 @@ case OP_CursorHint: { assert( pOp->p4type==P4_EXPR ); pC = p->apCsr[pOp->p1]; if( pC ){ - sqlite3BtreeCursorHint(pC->pCursor, BTREE_HINT_RANGE, pOp->p4.pExpr, aMem); + assert( pC->eCurType==CURTYPE_BTREE ); + sqlite3BtreeCursorHint(pC->uc.pCursor, BTREE_HINT_RANGE, + pOp->p4.pExpr, aMem); } break; } diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 02e98e14fd..c2b423695e 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -58,42 +58,48 @@ typedef struct Explain Explain; /* Elements of the linked list at Vdbe.pAuxData */ typedef struct AuxData AuxData; +/* Types of VDBE cursors */ +#define CURTYPE_BTREE 0 +#define CURTYPE_SORTER 1 +#define CURTYPE_VTAB 2 +#define CURTYPE_PSEUDO 3 + /* -** A cursor is a pointer into a single BTree within a database file. -** The cursor can seek to a BTree entry with a particular key, or -** 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. +** A VdbeCursor is an superclass (a wrapper) for various cursor objects: ** -** 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. +** * A b-tree cursor +** - In the main database or in an ephemeral database +** - On either an index or a table +** * A sorter +** * A virtual table +** * A one-row "pseudotable" stored in a single register */ 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 seekResult; /* Result of previous sqlite3BtreeMoveto() */ - int pseudoTableReg; /* Register holding pseudotable content. */ - i16 nField; /* Number of fields in the header */ - u16 nHdrParsed; /* Number of header fields parsed so far */ -#ifdef SQLITE_DEBUG - u8 seekOp; /* Most recent seek operation on this cursor */ -#endif + u8 eCurType; /* One of the CURTYPE_* values above */ i8 iDb; /* Index of cursor database in db->aDb[] (or -1) */ u8 nullRow; /* True if pointing to a row with no data */ u8 deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */ + u8 isTable; /* True for rowid tables. False for indexes */ +#ifdef SQLITE_DEBUG + u8 seekOp; /* Most recent seek operation on this cursor */ +#endif Bool isEphemeral:1; /* True for an ephemeral table */ 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 */ Pgno pgnoRoot; /* Root page of the open btree cursor */ - sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */ + i16 nField; /* Number of fields in the header */ + u16 nHdrParsed; /* Number of header fields parsed so far */ + union { + BtCursor *pCursor; /* CURTYPE_BTREE. Btree cursor */ + sqlite3_vtab_cursor *pVCur; /* CURTYPE_VTAB. Vtab cursor */ + int pseudoTableReg; /* CURTYPE_PSEUDO. Reg holding content. */ + VdbeSorter *pSorter; /* CURTYPE_SORTER. Sorter object */ + } uc; + Btree *pBt; /* Separate file holding temporary table */ + KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */ + int seekResult; /* Result of previous sqlite3BtreeMoveto() */ i64 seqCount; /* Sequence counter */ i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */ - VdbeSorter *pSorter; /* Sorter object for OP_SorterOpen cursors */ #ifdef SQLITE_ENABLE_COLUMN_USED_MASK u64 maskUsed; /* Mask of columns used by this cursor */ #endif diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 12546bdc8c..b50892b86d 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -1644,11 +1644,11 @@ int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ u32 nRec; u8 *aRec; - rc = sqlite3BtreeDataSize(p->pCsr->pCursor, &nRec); + rc = sqlite3BtreeDataSize(p->pCsr->uc.pCursor, &nRec); if( rc!=SQLITE_OK ) goto preupdate_old_out; aRec = sqlite3DbMallocRaw(db, nRec); if( !aRec ) goto preupdate_old_out; - rc = sqlite3BtreeData(p->pCsr->pCursor, 0, nRec, aRec); + rc = sqlite3BtreeData(p->pCsr->uc.pCursor, 0, nRec, aRec); if( rc==SQLITE_OK ){ p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec); if( !p->pUnpacked ) rc = SQLITE_NOMEM; diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 86099e1bb4..6082e81c8d 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -838,7 +838,6 @@ void sqlite3VdbeChangeToNoop(Vdbe *p, int addr){ freeP4(db, pOp->p4type, pOp->p4.p); memset(pOp, 0, sizeof(pOp[0])); pOp->opcode = OP_Noop; - if( addr==p->nOp-1 ) p->nOp--; } } @@ -1931,23 +1930,34 @@ void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){ if( pCx==0 ){ return; } - sqlite3VdbeSorterClose(p->db, pCx); - if( pCx->pBt ){ - sqlite3BtreeClose(pCx->pBt); - /* The pCx->pCursor will be close automatically, if it exists, by - ** the call above. */ - }else if( pCx->pCursor ){ - sqlite3BtreeCloseCursor(pCx->pCursor); - } + assert( pCx->pBt==0 || pCx->eCurType==CURTYPE_BTREE ); + switch( pCx->eCurType ){ + case CURTYPE_SORTER: { + sqlite3VdbeSorterClose(p->db, pCx); + break; + } + case CURTYPE_BTREE: { + if( pCx->pBt ){ + sqlite3BtreeClose(pCx->pBt); + /* The pCx->pCursor will be close automatically, if it exists, by + ** the call above. */ + }else{ + assert( pCx->uc.pCursor!=0 ); + sqlite3BtreeCloseCursor(pCx->uc.pCursor); + } + break; + } #ifndef SQLITE_OMIT_VIRTUALTABLE - else if( pCx->pVtabCursor ){ - sqlite3_vtab_cursor *pVtabCursor = pCx->pVtabCursor; - const sqlite3_module *pModule = pVtabCursor->pVtab->pModule; - assert( pVtabCursor->pVtab->nRef>0 ); - pVtabCursor->pVtab->nRef--; - pModule->xClose(pVtabCursor); - } + case CURTYPE_VTAB: { + sqlite3_vtab_cursor *pVCur = pCx->uc.pVCur; + const sqlite3_module *pModule = pVCur->pVtab->pModule; + assert( pVCur->pVtab->nRef>0 ); + pVCur->pVtab->nRef--; + pModule->xClose(pVCur); + break; + } #endif + } } /* @@ -2934,7 +2944,8 @@ static int SQLITE_NOINLINE handleDeferredMoveto(VdbeCursor *p){ #endif assert( p->deferredMoveto ); assert( p->isTable ); - rc = sqlite3BtreeMovetoUnpacked(p->pCursor, 0, p->movetoTarget, 0, &res); + assert( p->eCurType==CURTYPE_BTREE ); + rc = sqlite3BtreeMovetoUnpacked(p->uc.pCursor, 0, p->movetoTarget, 0, &res); if( rc ) return rc; if( res!=0 ) return SQLITE_CORRUPT_BKPT; #ifdef SQLITE_TEST @@ -2954,9 +2965,10 @@ static int SQLITE_NOINLINE handleDeferredMoveto(VdbeCursor *p){ */ static int SQLITE_NOINLINE handleMovedCursor(VdbeCursor *p){ int isDifferentRow, rc; - assert( p->pCursor!=0 ); - assert( sqlite3BtreeCursorHasMoved(p->pCursor) ); - rc = sqlite3BtreeCursorRestore(p->pCursor, &isDifferentRow); + assert( p->eCurType==CURTYPE_BTREE ); + assert( p->uc.pCursor!=0 ); + assert( sqlite3BtreeCursorHasMoved(p->uc.pCursor) ); + rc = sqlite3BtreeCursorRestore(p->uc.pCursor, &isDifferentRow); p->cacheStatus = CACHE_STALE; if( isDifferentRow ) p->nullRow = 1; return rc; @@ -2967,7 +2979,8 @@ static int SQLITE_NOINLINE handleMovedCursor(VdbeCursor *p){ ** if need be. Return any I/O error from the restore operation. */ int sqlite3VdbeCursorRestore(VdbeCursor *p){ - if( sqlite3BtreeCursorHasMoved(p->pCursor) ){ + assert( p->eCurType==CURTYPE_BTREE ); + if( sqlite3BtreeCursorHasMoved(p->uc.pCursor) ){ return handleMovedCursor(p); } return SQLITE_OK; @@ -2987,11 +3000,13 @@ int sqlite3VdbeCursorRestore(VdbeCursor *p){ ** not been deleted out from under the cursor, then this routine is a no-op. */ int sqlite3VdbeCursorMoveto(VdbeCursor *p){ - if( p->deferredMoveto ){ - return handleDeferredMoveto(p); - } - if( p->pCursor && sqlite3BtreeCursorHasMoved(p->pCursor) ){ - return handleMovedCursor(p); + if( p->eCurType==CURTYPE_BTREE ){ + if( p->deferredMoveto ){ + return handleDeferredMoveto(p); + } + if( sqlite3BtreeCursorHasMoved(p->uc.pCursor) ){ + return handleMovedCursor(p); + } } return SQLITE_OK; } @@ -4305,9 +4320,11 @@ int sqlite3VdbeIdxKeyCompare( ){ i64 nCellKey = 0; int rc; - BtCursor *pCur = pC->pCursor; + BtCursor *pCur; Mem m; + assert( pC->eCurType==CURTYPE_BTREE ); + pCur = pC->uc.pCursor; assert( sqlite3BtreeCursorIsValid(pCur) ); VVA_ONLY(rc =) sqlite3BtreeKeySize(pCur, &nCellKey); assert( rc==SQLITE_OK ); /* pCur is always valid so KeySize cannot fail */ @@ -4318,7 +4335,7 @@ int sqlite3VdbeIdxKeyCompare( return SQLITE_CORRUPT_BKPT; } sqlite3VdbeMemInit(&m, db, 0); - rc = sqlite3VdbeMemFromBtree(pC->pCursor, 0, (u32)nCellKey, 1, &m); + rc = sqlite3VdbeMemFromBtree(pCur, 0, (u32)nCellKey, 1, &m); if( rc ){ return rc; } diff --git a/src/vdbeblob.c b/src/vdbeblob.c index 80608ecec7..c64f815e2d 100644 --- a/src/vdbeblob.c +++ b/src/vdbeblob.c @@ -78,7 +78,7 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){ }else{ p->iOffset = pC->aType[p->iCol + pC->nField]; p->nByte = sqlite3VdbeSerialTypeLen(type); - p->pCsr = pC->pCursor; + p->pCsr = pC->uc.pCursor; sqlite3BtreeIncrblobCursor(p->pCsr); } } diff --git a/src/vdbesort.c b/src/vdbesort.c index 9840bed315..54e538fd50 100644 --- a/src/vdbesort.c +++ b/src/vdbesort.c @@ -961,11 +961,12 @@ int sqlite3VdbeSorterInit( #endif assert( pCsr->pKeyInfo && pCsr->pBt==0 ); + assert( pCsr->eCurType==CURTYPE_SORTER ); szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nField-1)*sizeof(CollSeq*); sz = sizeof(VdbeSorter) + nWorker * sizeof(SortSubtask); pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo); - pCsr->pSorter = pSorter; + pCsr->uc.pSorter = pSorter; if( pSorter==0 ){ rc = SQLITE_NOMEM; }else{ @@ -1249,12 +1250,14 @@ void sqlite3VdbeSorterReset(sqlite3 *db, VdbeSorter *pSorter){ ** Free any cursor components allocated by sqlite3VdbeSorterXXX routines. */ void sqlite3VdbeSorterClose(sqlite3 *db, VdbeCursor *pCsr){ - VdbeSorter *pSorter = pCsr->pSorter; + VdbeSorter *pSorter; + assert( pCsr->eCurType==CURTYPE_SORTER ); + pSorter = pCsr->uc.pSorter; if( pSorter ){ sqlite3VdbeSorterReset(db, pSorter); sqlite3_free(pSorter->list.aMemory); sqlite3DbFree(db, pSorter); - pCsr->pSorter = 0; + pCsr->uc.pSorter = 0; } } @@ -1752,15 +1755,16 @@ int sqlite3VdbeSorterWrite( const VdbeCursor *pCsr, /* Sorter cursor */ Mem *pVal /* Memory cell containing record */ ){ - VdbeSorter *pSorter = pCsr->pSorter; + VdbeSorter *pSorter; int rc = SQLITE_OK; /* Return Code */ SorterRecord *pNew; /* New list element */ - int bFlush; /* True to flush contents of memory to PMA */ int nReq; /* Bytes of memory required */ int nPMA; /* Bytes of PMA space required */ int t; /* serial type of first record field */ + assert( pCsr->eCurType==CURTYPE_SORTER ); + pSorter = pCsr->uc.pSorter; getVarint32((const u8*)&pVal->z[1], t); if( t>0 && t<10 && t!=7 ){ pSorter->typeMask &= SORTER_TYPE_INTEGER; @@ -2552,9 +2556,11 @@ static int vdbeSorterSetupMerge(VdbeSorter *pSorter){ ** in sorted order. */ int sqlite3VdbeSorterRewind(const VdbeCursor *pCsr, int *pbEof){ - VdbeSorter *pSorter = pCsr->pSorter; + VdbeSorter *pSorter; int rc = SQLITE_OK; /* Return code */ + assert( pCsr->eCurType==CURTYPE_SORTER ); + pSorter = pCsr->uc.pSorter; assert( pSorter ); /* If no data has been written to disk, then do not do so now. Instead, @@ -2598,9 +2604,11 @@ int sqlite3VdbeSorterRewind(const VdbeCursor *pCsr, int *pbEof){ ** Advance to the next element in the sorter. */ int sqlite3VdbeSorterNext(sqlite3 *db, const VdbeCursor *pCsr, int *pbEof){ - VdbeSorter *pSorter = pCsr->pSorter; + VdbeSorter *pSorter; int rc; /* Return code */ + assert( pCsr->eCurType==CURTYPE_SORTER ); + pSorter = pCsr->uc.pSorter; assert( pSorter->bUsePMA || (pSorter->pReader==0 && pSorter->pMerger==0) ); if( pSorter->bUsePMA ){ assert( pSorter->pReader==0 || pSorter->pMerger==0 ); @@ -2660,9 +2668,11 @@ static void *vdbeSorterRowkey( ** Copy the current sorter key into the memory cell pOut. */ int sqlite3VdbeSorterRowkey(const VdbeCursor *pCsr, Mem *pOut){ - VdbeSorter *pSorter = pCsr->pSorter; + VdbeSorter *pSorter; void *pKey; int nKey; /* Sorter key to copy into pOut */ + assert( pCsr->eCurType==CURTYPE_SORTER ); + pSorter = pCsr->uc.pSorter; pKey = vdbeSorterRowkey(pSorter, &nKey); if( sqlite3VdbeMemClearAndResize(pOut, nKey) ){ return SQLITE_NOMEM; @@ -2696,12 +2706,16 @@ int sqlite3VdbeSorterCompare( int nKeyCol, /* Compare this many columns */ int *pRes /* OUT: Result of comparison */ ){ - VdbeSorter *pSorter = pCsr->pSorter; - UnpackedRecord *r2 = pSorter->pUnpacked; - KeyInfo *pKeyInfo = pCsr->pKeyInfo; + VdbeSorter *pSorter; + UnpackedRecord *r2; + KeyInfo *pKeyInfo; int i; void *pKey; int nKey; /* Sorter key to compare pVal with */ + assert( pCsr->eCurType==CURTYPE_SORTER ); + pSorter = pCsr->uc.pSorter; + r2 = pSorter->pUnpacked; + pKeyInfo = pCsr->pKeyInfo; if( r2==0 ){ char *p; r2 = pSorter->pUnpacked = sqlite3VdbeAllocUnpackedRecord(pKeyInfo,0,0,&p); diff --git a/src/wal.c b/src/wal.c index d87d2c17ce..144db27a30 100644 --- a/src/wal.c +++ b/src/wal.c @@ -789,10 +789,9 @@ static void walUnlockShared(Wal *pWal, int lockIdx){ SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED); WALTRACE(("WAL%p: release SHARED-%s\n", pWal, walLockName(lockIdx))); } -static int walLockExclusive(Wal *pWal, int lockIdx, int n, int fBlock){ +static int walLockExclusive(Wal *pWal, int lockIdx, int n){ int rc; if( pWal->exclusiveMode ) return SQLITE_OK; - if( fBlock ) sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_WAL_BLOCK, 0); rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, n, SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE); WALTRACE(("WAL%p: acquire EXCLUSIVE-%s cnt=%d %s\n", pWal, @@ -1078,7 +1077,7 @@ static int walIndexRecover(Wal *pWal){ assert( pWal->writeLock ); iLock = WAL_ALL_BUT_WRITE + pWal->ckptLock; nLock = SQLITE_SHM_NLOCK - iLock; - rc = walLockExclusive(pWal, iLock, nLock, 0); + rc = walLockExclusive(pWal, iLock, nLock); if( rc ){ return rc; } @@ -1616,7 +1615,7 @@ static int walBusyLock( ){ int rc; do { - rc = walLockExclusive(pWal, lockIdx, n, 0); + rc = walLockExclusive(pWal, lockIdx, n); }while( xBusy && rc==SQLITE_BUSY && xBusy(pBusyArg) ); return rc; } @@ -2057,7 +2056,7 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){ walUnlockShared(pWal, WAL_WRITE_LOCK); rc = SQLITE_READONLY_RECOVERY; } - }else if( SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1, 1)) ){ + }else if( SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) ){ pWal->writeLock = 1; if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){ badHdr = walIndexTryHdr(pWal, pChanged); @@ -2263,7 +2262,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ && (mxReadMarkhdr.mxFrame || mxI==0) ){ for(i=1; iaReadMark[i] = pWal->hdr.mxFrame; mxI = i; @@ -2537,7 +2536,7 @@ int sqlite3WalBeginWriteTransaction(Wal *pWal){ /* Only one writer allowed at a time. Get the write lock. Return ** SQLITE_BUSY if unable. */ - rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1, 0); + rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1); if( rc ){ return rc; } @@ -2682,7 +2681,7 @@ static int walRestartLog(Wal *pWal){ if( pInfo->nBackfill>0 ){ u32 salt1; sqlite3_randomness(4, &salt1); - rc = walLockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1, 0); + rc = walLockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); if( rc==SQLITE_OK ){ /* If all readers are using WAL_READ_LOCK(0) (in other words if no ** readers are currently using the WAL), then the transactions @@ -3007,7 +3006,7 @@ int sqlite3WalCheckpoint( /* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive ** "checkpoint" lock on the database file. */ - rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1, 0); + rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1); if( rc ){ /* EVIDENCE-OF: R-10421-19736 If any other process is running a ** checkpoint operation at the same time, the lock cannot be obtained and diff --git a/src/where.c b/src/where.c index 1c87706ea2..7d68664598 100644 --- a/src/where.c +++ b/src/where.c @@ -893,6 +893,9 @@ static sqlite3_index_info *allocateIndexInfo( pIdxCons[j].iTermOffset = i; op = (u8)pTerm->eOperator & WO_ALL; if( op==WO_IN ) op = WO_EQ; + if( op==WO_MATCH ){ + op = pTerm->eMatchOp; + } pIdxCons[j].op = op; /* The direct assignment in the previous line is possible only because ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The @@ -2861,6 +2864,7 @@ static int whereLoopAddVirtual( pIdxInfo->estimatedCost = SQLITE_BIG_DBL / (double)2; pIdxInfo->estimatedRows = 25; pIdxInfo->idxFlags = 0; + pIdxInfo->colUsed = (sqlite3_int64)pSrc->colUsed; rc = vtabBestIndex(pParse, pTab, pIdxInfo); if( rc ) goto whereLoopAddVtab_exit; pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; @@ -4488,6 +4492,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ sqlite3VdbeJumpHere(v, pLevel->addrSkip); sqlite3VdbeJumpHere(v, pLevel->addrSkip-2); } +#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS if( pLevel->addrLikeRep ){ int op; if( sqlite3VdbeGetOp(v, pLevel->addrLikeRep-1)->p1 ){ @@ -4498,6 +4503,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ sqlite3VdbeAddOp2(v, op, pLevel->iLikeRepCntr, pLevel->addrLikeRep); VdbeCoverage(v); } +#endif if( pLevel->iLeftJoin ){ addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); VdbeCoverage(v); assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 diff --git a/src/whereInt.h b/src/whereInt.h index cae09acc82..63d2d71cb4 100644 --- a/src/whereInt.h +++ b/src/whereInt.h @@ -69,8 +69,10 @@ struct WhereLevel { int addrCont; /* Jump here to continue with the next loop cycle */ int addrFirst; /* First instruction of interior of the loop */ int addrBody; /* Beginning of the body of this loop */ +#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS int iLikeRepCntr; /* LIKE range processing counter register */ int addrLikeRep; /* LIKE range processing address */ +#endif u8 iFrom; /* Which entry in the FROM clause */ u8 op, p3, p5; /* Opcode, P3 & P5 of the opcode that ends the loop */ int p1, p2; /* Operands of the opcode used to ends the loop */ @@ -253,6 +255,7 @@ struct WhereTerm { u16 eOperator; /* A WO_xx value describing */ u16 wtFlags; /* TERM_xxx bit flags. See below */ u8 nChild; /* Number of children that must disable us */ + u8 eMatchOp; /* Op for vtab MATCH/LIKE/GLOB/REGEXP terms */ WhereClause *pWC; /* The clause this term is part of */ Bitmask prereqRight; /* Bitmask of tables used by pExpr->pRight */ Bitmask prereqAll; /* Bitmask of tables referenced by pExpr */ diff --git a/src/wherecode.c b/src/wherecode.c index 87db0e0a25..bc72e0ac7d 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -561,6 +561,7 @@ static int codeAllEqualityTerms( return regBase; } +#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS /* ** If the most recently coded instruction is a constant range contraint ** that originated from the LIKE optimization, then change the P3 to be @@ -572,6 +573,10 @@ static int codeAllEqualityTerms( ** The OP_String opcodes on the second pass convert the upper and lower ** bound string contants to blobs. This routine makes the necessary changes ** to the OP_String opcodes for that to happen. +** +** Except, of course, if SQLITE_LIKE_DOESNT_MATCH_BLOBS is defined, then +** only the one pass through the string space is required, so this routine +** becomes a no-op. */ static void whereLikeOptimizationStringFixup( Vdbe *v, /* prepared statement under construction */ @@ -589,6 +594,9 @@ static void whereLikeOptimizationStringFixup( pOp->p5 = 1; } } +#else +# define whereLikeOptimizationStringFixup(A,B,C) +#endif #ifdef SQLITE_ENABLE_CURSOR_HINTS /* @@ -1075,6 +1083,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( if( pLoop->wsFlags & WHERE_TOP_LIMIT ){ pRangeEnd = pLoop->aLTerm[j++]; nExtraReg = 1; +#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS if( (pRangeEnd->wtFlags & TERM_LIKEOPT)!=0 ){ assert( pRangeStart!=0 ); /* LIKE opt constraints */ assert( pRangeStart->wtFlags & TERM_LIKEOPT ); /* occur in pairs */ @@ -1087,6 +1096,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( VdbeComment((v, "LIKE loop counter")); pLevel->addrLikeRep = sqlite3VdbeCurrentAddr(v); } +#endif if( pRangeStart==0 && (j = pIdx->aiColumn[nEq])>=0 && pIdx->pTable->aCol[j].notNull==0 @@ -1590,9 +1600,13 @@ Bitmask sqlite3WhereCodeOneLoopStart( continue; } if( pTerm->wtFlags & TERM_LIKECOND ){ +#ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS + continue; +#else assert( pLevel->iLikeRepCntr>0 ); skipLikeAddr = sqlite3VdbeAddOp1(v, OP_IfNot, pLevel->iLikeRepCntr); VdbeCoverage(v); +#endif } sqlite3ExprIfFalse(pParse, pE, addrCont, SQLITE_JUMPIFNULL); if( skipLikeAddr ) sqlite3VdbeJumpHere(v, skipLikeAddr); diff --git a/src/whereexpr.c b/src/whereexpr.c index fadde79010..99a97079be 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -277,29 +277,48 @@ static int isLikeOrGlob( /* ** Check to see if the given expression is of the form ** -** column MATCH expr +** column OP expr +** +** where OP is one of MATCH, GLOB, LIKE or REGEXP and "column" is a +** column of a virtual table. ** ** If it is then return TRUE. If not, return FALSE. */ static int isMatchOfColumn( - Expr *pExpr /* Test this expression */ + Expr *pExpr, /* Test this expression */ + unsigned char *peOp2 /* OUT: 0 for MATCH, or else an op2 value */ ){ + struct Op2 { + const char *zOp; + unsigned char eOp2; + } aOp[] = { + { "match", SQLITE_INDEX_CONSTRAINT_MATCH }, + { "glob", SQLITE_INDEX_CONSTRAINT_GLOB }, + { "like", SQLITE_INDEX_CONSTRAINT_LIKE }, + { "regexp", SQLITE_INDEX_CONSTRAINT_REGEXP } + }; ExprList *pList; + Expr *pCol; /* Column reference */ + int i; if( pExpr->op!=TK_FUNCTION ){ return 0; } - if( sqlite3StrICmp(pExpr->u.zToken,"match")!=0 ){ - return 0; - } pList = pExpr->x.pList; - if( pList->nExpr!=2 ){ + if( pList==0 || pList->nExpr!=2 ){ return 0; } - if( pList->a[1].pExpr->op != TK_COLUMN ){ + pCol = pList->a[1].pExpr; + if( pCol->op!=TK_COLUMN || !IsVirtual(pCol->pTab) ){ return 0; } - return 1; + for(i=0; iu.zToken, aOp[i].zOp)==0 ){ + *peOp2 = aOp[i].eOp2; + return 1; + } + } + return 0; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -876,6 +895,7 @@ static void exprAnalyze( int op; /* Top-level operator. pExpr->op */ Parse *pParse = pWInfo->pParse; /* Parsing context */ sqlite3 *db = pParse->db; /* Database connection */ + unsigned char eOp2; /* op2 value for LIKE/REGEXP/GLOB */ if( db->mallocFailed ){ return; @@ -1099,7 +1119,7 @@ static void exprAnalyze( ** virtual tables. The native query optimizer does not attempt ** to do anything with MATCH functions. */ - if( isMatchOfColumn(pExpr) ){ + if( isMatchOfColumn(pExpr, &eOp2) ){ int idxNew; Expr *pRight, *pLeft; WhereTerm *pNewTerm; @@ -1120,6 +1140,7 @@ static void exprAnalyze( pNewTerm->leftCursor = pLeft->iTable; pNewTerm->u.leftColumn = pLeft->iColumn; pNewTerm->eOperator = WO_MATCH; + pNewTerm->eMatchOp = eOp2; markTermAsChild(pWC, idxNew, idxTerm); pTerm = &pWC->a[idxTerm]; pTerm->wtFlags |= TERM_COPIED; @@ -1222,7 +1243,8 @@ void sqlite3WhereClauseInit( /* ** Deallocate a WhereClause structure. The WhereClause structure -** itself is not freed. This routine is the inverse of sqlite3WhereClauseInit(). +** itself is not freed. This routine is the inverse of +** sqlite3WhereClauseInit(). */ void sqlite3WhereClauseClear(WhereClause *pWC){ int i; @@ -1316,9 +1338,9 @@ void sqlite3WhereTabFuncArgs( pTab = pItem->pTab; assert( pTab!=0 ); pArgs = pItem->u1.pFuncArg; - assert( pArgs!=0 ); + if( pArgs==0 ) return; for(j=k=0; jnExpr; j++){ - while( knCol && (pTab->aCol[k].colFlags & COLFLAG_HIDDEN)==0 ){ k++; } + while( knCol && (pTab->aCol[k].colFlags & COLFLAG_HIDDEN)==0 ){k++;} if( k>=pTab->nCol ){ sqlite3ErrorMsg(pParse, "too many arguments on %s() - max %d", pTab->zName, j); diff --git a/test/analyze3.test b/test/analyze3.test index d61d21a947..2fb558d16a 100644 --- a/test/analyze3.test +++ b/test/analyze3.test @@ -283,9 +283,17 @@ do_eqp_test analyze3-2.3 { SELECT count(a) FROM t1 WHERE b LIKE '%a' } {0 0 0 {SCAN TABLE t1}} +# Return the first argument if like_match_blobs is true (the default) +# or the second argument if not +# +proc ilmb {a b} { + ifcapable like_match_blobs {return $a} + return $b +} + do_test analyze3-2.4 { sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE 'a%' } -} {102 0 100} +} [list [ilmb 102 101] 0 100] do_test analyze3-2.5 { sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE '%a' } } {999 999 100} @@ -293,7 +301,7 @@ do_test analyze3-2.5 { do_test analyze3-2.6 { set like "a%" sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE $like } -} {102 0 100} +} [list [ilmb 102 101] 0 100] do_test analyze3-2.7 { set like "%a" sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE $like } @@ -301,19 +309,19 @@ do_test analyze3-2.7 { do_test analyze3-2.8 { set like "a" sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE $like } -} {102 0 0} +} [list [ilmb 102 101] 0 0] do_test analyze3-2.9 { set like "ab" sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE $like } -} {12 0 0} +} [list [ilmb 12 11] 0 0] do_test analyze3-2.10 { set like "abc" sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE $like } -} {3 0 1} +} [list [ilmb 3 2] 0 1] do_test analyze3-2.11 { set like "a_c" sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE $like } -} {102 0 10} +} [list [ilmb 102 101] 0 10] #------------------------------------------------------------------------- diff --git a/test/capi3c.test b/test/capi3c.test index 6ab3bc24f6..15307a7f7a 100644 --- a/test/capi3c.test +++ b/test/capi3c.test @@ -18,6 +18,7 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl +set testprefix capi3c # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). @@ -1375,4 +1376,26 @@ do_test capi3c-24.3 { decltype {SELECT (SELECT x FROM (SELECT t5.a AS x)) FROM t5} } {INTEGER} + +# Further tests of sqlite3_column_decltype(): +# +do_execsql_test 25.0 { + CREATE TABLE t11(a VARCHAR(10), b INTEGER); + CREATE TABLE t12(a VARCHAR(15), b FLOAT); +} + +foreach {tn sql} { + 1 "SELECT * FROM t11 UNION ALL SELECT * FROM t12" + 2 "SELECT * FROM t11 UNION SELECT * FROM t12" + 3 "SELECT * FROM t11 EXCEPT SELECT * FROM t12" + 4 "SELECT * FROM t11 INTERSECT SELECT * FROM t12" + + 5 "SELECT * FROM t11 UNION ALL SELECT * FROM t12 ORDER BY 1" + 6 "SELECT * FROM t11 UNION SELECT * FROM t12 ORDER BY 1" + 7 "SELECT * FROM t11 EXCEPT SELECT * FROM t12 ORDER BY 1" + 8 "SELECT * FROM t11 INTERSECT SELECT * FROM t12 ORDER BY 1" +} { + do_test 25.$tn { decltype $sql } {VARCHAR(10) INTEGER} +} + finish_test diff --git a/test/distinct.test b/test/distinct.test index 2fb90dc3e3..dac2269b0b 100644 --- a/test/distinct.test +++ b/test/distinct.test @@ -252,4 +252,21 @@ do_execsql_test 5.6 { SELECT DISTINCT x FROM t1 ORDER BY x; } {1 2 3 4 5 6} +#------------------------------------------------------------------------- +# 2015-11-23. Problem discovered by Kostya Serebryany using libFuzzer +# +db close +sqlite3 db :memory: +do_execsql_test 6.1 { + CREATE TABLE jjj(x); + SELECT (SELECT 'mmm' UNION SELECT DISTINCT max(name) ORDER BY 1) + FROM sqlite_master; +} {jjj} +do_execsql_test 6.2 { + CREATE TABLE nnn(x); + SELECT (SELECT 'mmm' UNION SELECT DISTINCT max(name) ORDER BY 1) + FROM sqlite_master; +} {mmm} + + finish_test diff --git a/test/enc3.test b/test/enc3.test index 1d8a258165..7ede2b716f 100644 --- a/test/enc3.test +++ b/test/enc3.test @@ -62,7 +62,7 @@ ifcapable {bloblit && utf16} { execsql { CREATE TABLE t2(a); INSERT INTO t2 VALUES(x'61006200630064006500'); - SELECT CAST(a AS text) FROM t2 WHERE a LIKE 'abc%'; + SELECT CAST(a AS text) FROM t2 WHERE CAST(a AS text) LIKE 'abc%'; } } {abcde} do_test enc3-2.3 { @@ -72,7 +72,8 @@ ifcapable {bloblit && utf16} { } {abcde} do_test enc3-2.4 { execsql { - SELECT rowid FROM t2 WHERE a LIKE x'610062002500'; + SELECT rowid FROM t2 + WHERE CAST(a AS text) LIKE CAST(x'610062002500' AS text); } } {1} } diff --git a/test/fts4content.test b/test/fts4content.test index 481c6ec008..5a91987867 100644 --- a/test/fts4content.test +++ b/test/fts4content.test @@ -587,7 +587,7 @@ do_execsql_test 10.1 { INSERT INTO idx VALUES (3, 't3.txt'); CREATE VIRTUAL TABLE vt USING fs(idx); - SELECT * FROM vt; + SELECT path, data FROM vt; } { 1 {a b c d e f g h i j k l m n o p q r s t u v w x y z} 2 {a b c d e f g h i j k l m a b c d e f g h i j k l m} @@ -595,7 +595,7 @@ do_execsql_test 10.1 { } do_execsql_test 10.2 { - SELECT * FROM vt WHERE rowid = 2; + SELECT path, data FROM vt WHERE rowid = 2; } { 2 {a b c d e f g h i j k l m a b c d e f g h i j k l m} } diff --git a/test/fuzzcheck.c b/test/fuzzcheck.c index 32690dc466..6d9c745528 100644 --- a/test/fuzzcheck.c +++ b/test/fuzzcheck.c @@ -588,18 +588,13 @@ static int inmemFullPathname( return SQLITE_OK; } -/* GetLastError() is never used */ -static int inmemGetLastError(sqlite3_vfs *pVfs, int n, char *z){ - return SQLITE_OK; -} - /* ** Register the VFS that reads from the g.aFile[] set of files. */ static void inmemVfsRegister(void){ static sqlite3_vfs inmemVfs; sqlite3_vfs *pDefault = sqlite3_vfs_find(0); - inmemVfs.iVersion = 1; + inmemVfs.iVersion = 3; inmemVfs.szOsFile = sizeof(VHandle); inmemVfs.mxPathname = 200; inmemVfs.zName = "inmem"; @@ -609,8 +604,7 @@ static void inmemVfsRegister(void){ inmemVfs.xFullPathname = inmemFullPathname; inmemVfs.xRandomness = pDefault->xRandomness; inmemVfs.xSleep = pDefault->xSleep; - inmemVfs.xCurrentTime = pDefault->xCurrentTime; - inmemVfs.xGetLastError = inmemGetLastError; + inmemVfs.xCurrentTimeInt64 = pDefault->xCurrentTimeInt64; sqlite3_vfs_register(&inmemVfs, 0); }; diff --git a/test/hexlit.test b/test/hexlit.test index 2edd458e89..c48930b49b 100644 --- a/test/hexlit.test +++ b/test/hexlit.test @@ -109,6 +109,9 @@ do_execsql_test hexlit-301 { do_catchsql_test hexlist-400 { SELECT 0x10000000000000000; } {1 {hex literal too big: 0x10000000000000000}} +do_catchsql_test hexlist-401 { + SELECT DISTINCT 0x10000000000000000; +} {1 {hex literal too big: 0x10000000000000000}} do_catchsql_test hexlist-410 { DROP TABLE IF EXISTS t1; CREATE TABLE t1(x); diff --git a/test/like.test b/test/like.test index 18a01dc996..ceb5f2bfac 100644 --- a/test/like.test +++ b/test/like.test @@ -745,11 +745,19 @@ ifcapable like_opt&&!icu { SELECT a FROM t10 WHERE e LIKE '12%' ORDER BY +a; } } {12 123 scan 5 like 6} - do_test like-10.5 { - count { - SELECT a FROM t10 WHERE f LIKE '12%' ORDER BY +a; - } - } {12 123 scan 4 like 0} + ifcapable like_match_blobs { + do_test like-10.5a { + count { + SELECT a FROM t10 WHERE f LIKE '12%' ORDER BY +a; + } + } {12 123 scan 4 like 0} + } else { + do_test like-10.5b { + count { + SELECT a FROM t10 WHERE f LIKE '12%' ORDER BY +a; + } + } {12 123 scan 3 like 0} + } do_test like-10.6 { count { SELECT a FROM t10 WHERE a LIKE '12%' ORDER BY +a; @@ -786,11 +794,19 @@ ifcapable like_opt&&!icu { SELECT a FROM t10b WHERE e GLOB '12*' ORDER BY +a; } } {12 123 scan 5 like 6} - do_test like-10.14 { - count { - SELECT a FROM t10b WHERE f GLOB '12*' ORDER BY +a; - } - } {12 123 scan 4 like 0} + ifcapable like_match_blobs { + do_test like-10.14 { + count { + SELECT a FROM t10b WHERE f GLOB '12*' ORDER BY +a; + } + } {12 123 scan 4 like 0} + } else { + do_test like-10.14 { + count { + SELECT a FROM t10b WHERE f GLOB '12*' ORDER BY +a; + } + } {12 123 scan 3 like 0} + } do_test like-10.15 { count { SELECT a FROM t10b WHERE a GLOB '12*' ORDER BY +a; diff --git a/test/like3.test b/test/like3.test index a1faf76915..9280c2c5d2 100644 --- a/test/like3.test +++ b/test/like3.test @@ -29,6 +29,11 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl +ifcapable !like_match_blobs { + finish_test + return +} + do_execsql_test like3-1.1 { PRAGMA encoding=UTF8; CREATE TABLE t1(a,b TEXT COLLATE nocase); @@ -107,6 +112,4 @@ do_execsql_test like3-4.2ck { SELECT quote(x) FROM t4 WHERE x LIKE 'ab%' ORDER BY +x ASC; } {'abc' 'abd' 'abe' X'616263' X'616264' X'616265'} - - finish_test diff --git a/test/misc1.test b/test/misc1.test index 6e0abcd108..400a4517b4 100644 --- a/test/misc1.test +++ b/test/misc1.test @@ -707,7 +707,7 @@ do_test misc1-24.0 { do_catchsql_test misc1-25.0 { SELECT-1 UNION SELECT 5 UNION SELECT 0 UNION SElECT*from(SELECT-5) UNION SELECT*from(SELECT-0) UNION SELECT:SELECT-0 UNION SELECT-1 UNION SELECT 1 UNION SELECT 1 ORDER BY S in(WITH K AS(WITH K AS(select'CREINDERcharREADEVIRTUL5TABLECONFLICT !1 USIN'' MFtOR(b38q,eWITH K AS(selectCREATe TABLE t0(a,b,c,d,e, PRIMARY KEY(a,b,c,d,c,a,b,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,a,b,b,c,d,c,a,c,d,c,d,c,e,d,d,c,a,b,b,c,c,a,b,b,E,d,c,d,c,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,a,b,c,e,d,d,c,a,b,b,c,d,d,c,a,b,c,e,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,b,c,e,d,d,c,a,b,b,c,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,e,d,d,c,a,b,b,c,c,a,b,b,E,d,c,d,c,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d'CEIl,k'',ab, g, a,b,o11b, i'nEX/charREDE IVT LR!VABLt5SG',N ,N in rement,l_vacuum,M&U,'te3(''5l' a,bB,b,l*e)SELECT:SELECT, *,*,*from(( SELECT $group,:conc ap0,1)fro,(select"",:PBAG,c,a,b,b,c,a,b,c,e,d,d,c,a,b,b,c,d,d,c,a,b,c,e,d,c,d,c,a,b,c,e,c,d,c,d,c,a,b,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,b,c,e,d,d,c,a,b,b,c,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,e,d,d,c,a,b,b,c,c,a,b,b,E,d,c,d,c,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,a,b,c,e,d,d,c,a,b,b,c,d,d,c,a,b,c,e,d,c,d,c,a,b,c,e,c,d,c,d,c,a,b,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,e,d,d,c,a,b,b,c,c,a,b,b,E,d,c,d,c,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,a,b,c,e,d,d,c,a,b,b,c,d,d,c,a,b,c,e,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,c,d,c,c,a,a,b,d,d,c,a,b,b,c,d,c,a,b,e,e,d,b,c,d,c,a,b,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d, foreign_keysc,d,c,e,d,d,c,a,b,b,c,c,a,b,b,E,d,c,d,c,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,a,b,d,d,c,a,b,b,c,d,c,a,b,e,e,d,b,c,d,c,a,b,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,a,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,e,d,d,c,a,b,b,c,c,a,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,a,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,e,d,d,c,a,b,b,c,c,a,b,b,E,d,c,d,c,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,a,b,c,e,d,d,c,a,b,b,c,d,d,c,a,b,c,e,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,c,d,c,a,b,d,d,c,a,a,b,d,d,c,a,b,b,c,d,c,a,b,e,e,d,b,c,d,c,a,b,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,e,d,d,c,a,b,b,c,c,a,b,b,E,d,c,d,c,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,a,b,d,d,c,a,b,b,c,d,c,a,b,e,e,d,b,c,d,c,a,b,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,c,a,b,b,c,d,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,d,c,e,d,d,c,a,b,b,c,c,a,b,b,E,d,c,d,c,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,a,bb,b,E,d,c,d,c,b,c,d,c,d,c,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,a,b,c,e,d,d,c,a,b,b,c,d,d,c,a,b,c,e,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,c,d,c,a,b,d,d,c,a,a,b,d,d,c,a,b,b,c,d,c,a,b,e,e,d,b,c,d,c,a,b,b,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,c,a,b,c,e,d,d,c,a,b,b,c,d,c,d,c,a,b,c,e,c,d,c,a,b,b,c,d,MAato_aecSELEC,+?b," "O,"i","a",""b ,5 ))KEY)SELECT*FROM((k()reaC,k,K) eA,k '' )t ,K M); -} {1 {too many columns in result set}} +} {1 {'k' is not a function}} finish_test diff --git a/test/sqllimits1.test b/test/sqllimits1.test index ec72723ebe..9508b5233d 100644 --- a/test/sqllimits1.test +++ b/test/sqllimits1.test @@ -874,6 +874,17 @@ do_test sqllimits1-16.2 { } } {1 {string or blob too big}} +do_catchsql_test sqllimits1.17.0 { + SELECT *,*,*,*,*,*,*,* FROM ( + SELECT *,*,*,*,*,*,*,* FROM ( + SELECT *,*,*,*,*,*,*,* FROM ( + SELECT *,*,*,*,*,*,*,* FROM ( + SELECT *,*,*,*,*,*,*,* FROM ( + SELECT 1,2,3,4,5,6,7,8,9,10 + ) + )))) +} "1 {too many columns in result set}" + foreach {key value} [array get saved] { catch {set $key $value} diff --git a/test/symlink.test b/test/symlink.test index af7ec2a67b..790624161f 100644 --- a/test/symlink.test +++ b/test/symlink.test @@ -116,4 +116,14 @@ do_execsql_test 2.5 { SELECT * FROM t1; } {1 2} +# Try to open a ridiculously long pathname. Bug found by +# Kostya Serebryany using libFuzzer on 2015-11-30. +# +do_test 3.1 { + db close + catch {sqlite3 db [string repeat [string repeat x 100]/ 6]} res + set res +} {unable to open database file} + + finish_test diff --git a/test/syscall.test b/test/syscall.test index 83b8b8b40f..a935957d39 100644 --- a/test/syscall.test +++ b/test/syscall.test @@ -60,7 +60,7 @@ foreach s { open close access getcwd stat fstat ftruncate fcntl read pread write pwrite fchmod fallocate pread64 pwrite64 unlink openDirectory mkdir rmdir - statvfs fchown umask mmap munmap mremap + statvfs fchown geteuid umask mmap munmap mremap getpagesize readlink } { if {[test_syscall exists $s]} {lappend syscall_list $s} diff --git a/test/tabfunc01.test b/test/tabfunc01.test index 47af39606e..d85cb20ff5 100644 --- a/test/tabfunc01.test +++ b/test/tabfunc01.test @@ -59,16 +59,34 @@ do_execsql_test tabfunc01-1.20 { CREATE VIEW v1(a,b) AS VALUES(1,2),(3,4); SELECT * FROM v1; } {1 2 3 4} -do_catchsql_test tabfunc01-1.21 { +do_catchsql_test tabfunc01-1.21.1 { SELECT * FROM v1(55); } {1 {'v1' is not a function}} +do_catchsql_test tabfunc01-1.21.2 { + SELECT * FROM v1(); +} {1 {'v1' is not a function}} do_execsql_test tabfunc01-1.22 { CREATE VIEW v2(x) AS SELECT value FROM generate_series(1,5); SELECT * FROM v2; } {1 2 3 4 5} -do_catchsql_test tabfunc01-1.23 { +do_catchsql_test tabfunc01-1.23.1 { SELECT * FROM v2(55); } {1 {'v2' is not a function}} +do_catchsql_test tabfunc01-1.23.2 { + SELECT * FROM v2(); +} {1 {'v2' is not a function}} +do_execsql_test tabfunc01-1.24 { + CREATE TABLE t0(x); + INSERT INTO t0(x) VALUES(123),(456),(789); + SELECT * FROM t0 ORDER BY x; +} {123 456 789} +do_catchsql_test tabfunc01-1.25 { + SELECT * FROM t0(55) ORDER BY x; +} {1 {'t0' is not a function}} +do_catchsql_test tabfunc01-1.26 { + WITH w0 AS (SELECT * FROM t0) + INSERT INTO t0(x) SELECT * FROM w0() +} {1 {'w0' is not a function}} do_execsql_test tabfunc01-2.1 { CREATE TABLE t1(x); diff --git a/test/threadtest3.c b/test/threadtest3.c index 25caeb89f9..a3fcae28cc 100644 --- a/test/threadtest3.c +++ b/test/threadtest3.c @@ -868,22 +868,28 @@ static void filecopy_x( ** Used by setstoptime() and timetostop(). */ static double timelimit = 0.0; -static sqlite3_vfs *pTimelimitVfs = 0; + +static double currentTime(void){ + double t; + static sqlite3_vfs *pTimelimitVfs = 0; + if( pTimelimitVfs==0 ) pTimelimitVfs = sqlite3_vfs_find(0); + if( pTimelimitVfs->iVersion>=1 && pTimelimitVfs->xCurrentTimeInt64!=0 ){ + sqlite3_int64 tm; + pTimelimitVfs->xCurrentTimeInt64(pTimelimitVfs, &tm); + t = tm/86400000.0; + }else{ + pTimelimitVfs->xCurrentTime(pTimelimitVfs, &t); + } + return t; +} static void setstoptime_x( Error *pErr, /* IN/OUT: Error code */ int nMs /* Milliseconds until "stop time" */ ){ if( pErr->rc==SQLITE_OK ){ - double t; - int rc; - pTimelimitVfs = sqlite3_vfs_find(0); - rc = pTimelimitVfs->xCurrentTime(pTimelimitVfs, &t); - if( rc!=SQLITE_OK ){ - pErr->rc = rc; - }else{ - timelimit = t + ((double)nMs)/(1000.0*60.0*60.0*24.0); - } + double t = currentTime(); + timelimit = t + ((double)nMs)/(1000.0*60.0*60.0*24.0); } } @@ -892,14 +898,8 @@ static int timetostop_x( ){ int ret = 1; if( pErr->rc==SQLITE_OK ){ - double t; - int rc; - rc = pTimelimitVfs->xCurrentTime(pTimelimitVfs, &t); - if( rc!=SQLITE_OK ){ - pErr->rc = rc; - }else{ - ret = (t >= timelimit); - } + double t = currentTime(); + ret = (t >= timelimit); } return ret; } diff --git a/test/tkt3871.test b/test/tkt3871.test index e7dbfde714..d587910d39 100644 --- a/test/tkt3871.test +++ b/test/tkt3871.test @@ -32,8 +32,8 @@ do_test tkt3871-1.3 { execsql { SELECT * FROM e WHERE a = 1 OR a = 2 } set echo_module } [list \ - xFilter {SELECT rowid, * FROM 't1' WHERE a = ?} 1 \ - xFilter {SELECT rowid, * FROM 't1' WHERE a = ?} 2 \ + xFilter {SELECT rowid, a, b FROM 't1' WHERE a = ?} 1 \ + xFilter {SELECT rowid, a, b FROM 't1' WHERE a = ?} 2 \ ] do_test tkt3871-1.4 { @@ -44,9 +44,9 @@ do_test tkt3871-1.5 { execsql { SELECT * FROM e WHERE a = 1 OR a = 2 OR b = 9 } set echo_module } [list \ - xFilter {SELECT rowid, * FROM 't1' WHERE a = ?} 1 \ - xFilter {SELECT rowid, * FROM 't1' WHERE a = ?} 2 \ - xFilter {SELECT rowid, * FROM 't1' WHERE b = ?} 9 + xFilter {SELECT rowid, a, b FROM 't1' WHERE a = ?} 1 \ + xFilter {SELECT rowid, a, b FROM 't1' WHERE a = ?} 2 \ + xFilter {SELECT rowid, a, b FROM 't1' WHERE b = ?} 9 ] diff --git a/test/vtab1.test b/test/vtab1.test index 84de4cffa5..6b6a0e2683 100644 --- a/test/vtab1.test +++ b/test/vtab1.test @@ -393,7 +393,7 @@ do_test vtab1-3.7 { SELECT rowid, * FROM t1; } } {1 1 2 3 2 4 5 6} -do_test vtab1-3.8 { +do_test vtab1-3.8.1 { execsql { SELECT a AS d, b AS e, c AS f FROM t1; } @@ -404,14 +404,14 @@ do_test vtab1-3.8 { # in test8.c) to make sure the xBestIndex() and xFilter() methods were # called correctly. # -do_test vtab1-3.8 { +do_test vtab1-3.8.2 { set echo_module "" execsql { SELECT * FROM t1; } set echo_module -} [list xBestIndex {SELECT rowid, * FROM 'treal'} \ - xFilter {SELECT rowid, * FROM 'treal'} ] +} [list xBestIndex {SELECT rowid, a, b, c FROM 'treal'} \ + xFilter {SELECT rowid, a, b, c FROM 'treal'} ] do_test vtab1-3.9 { set echo_module "" execsql { @@ -420,8 +420,8 @@ do_test vtab1-3.9 { } {4 5 6} do_test vtab1-3.10 { set echo_module -} [list xBestIndex {SELECT rowid, * FROM 'treal' WHERE b = ?} \ - xFilter {SELECT rowid, * FROM 'treal' WHERE b = ?} 5 ] +} [list xBestIndex {SELECT rowid, a, b, c FROM 'treal' WHERE b = ?} \ + xFilter {SELECT rowid, a, b, c FROM 'treal' WHERE b = ?} 5 ] do_test vtab1-3.10 { set echo_module "" execsql { @@ -430,8 +430,9 @@ do_test vtab1-3.10 { } {4 5 6} do_test vtab1-3.11 { set echo_module -} [list xBestIndex {SELECT rowid, * FROM 'treal' WHERE b >= ? AND b <= ?} \ - xFilter {SELECT rowid, * FROM 'treal' WHERE b >= ? AND b <= ?} 5 10 ] +} [list xBestIndex {SELECT rowid, a, b, c FROM 'treal' WHERE b >= ? AND b <= ?}\ + xFilter {SELECT rowid, a, b, c FROM 'treal' WHERE b >= ? AND b <= ?}\ + 5 10 ] do_test vtab1-3.12 { set echo_module "" execsql { @@ -440,8 +441,9 @@ do_test vtab1-3.12 { } {1 2 3 4 5 6} do_test vtab1-3.13 { set echo_module -} [list xBestIndex {SELECT rowid, * FROM 'treal' WHERE b >= ? AND b <= ?} \ - xFilter {SELECT rowid, * FROM 'treal' WHERE b >= ? AND b <= ?} 2 10 ] +} [list xBestIndex {SELECT rowid, a, b, c FROM 'treal' WHERE b >= ? AND b <= ?}\ + xFilter {SELECT rowid, a, b, c FROM 'treal' WHERE b >= ? AND b <= ?}\ + 2 10 ] # Add a function for the MATCH operator. Everything always matches! #proc test_match {lhs rhs} { @@ -459,8 +461,8 @@ do_test vtab1-3.12 { } {1 {unable to use function MATCH in the requested context}} do_test vtab1-3.13 { set echo_module -} [list xBestIndex {SELECT rowid, * FROM 'treal'} \ - xFilter {SELECT rowid, * FROM 'treal'}] +} [list xBestIndex {SELECT rowid, a, b, c FROM 'treal'} \ + xFilter {SELECT rowid, a, b, c FROM 'treal'}] ifcapable subquery { # The echo module uses a subquery internally to implement the MATCH operator. do_test vtab1-3.14 { @@ -472,9 +474,9 @@ do_test vtab1-3.14 { do_test vtab1-3.15 { set echo_module } [list xBestIndex \ - {SELECT rowid, * FROM 'treal' WHERE b LIKE (SELECT '%'||?||'%')} \ + {SELECT rowid, a, b, c FROM 'treal' WHERE b LIKE (SELECT '%'||?||'%')} \ xFilter \ - {SELECT rowid, * FROM 'treal' WHERE b LIKE (SELECT '%'||?||'%')} \ + {SELECT rowid, a, b, c FROM 'treal' WHERE b LIKE (SELECT '%'||?||'%')} \ string ] }; #ifcapable subquery @@ -505,8 +507,8 @@ do_test vtab1-4.1 { } {2 5 nosort} do_test vtab1-4.2 { set echo_module -} [list xBestIndex {SELECT rowid, * FROM 'treal' ORDER BY b ASC} \ - xFilter {SELECT rowid, * FROM 'treal' ORDER BY b ASC} ] +} [list xBestIndex {SELECT rowid, NULL, b, NULL FROM 'treal' ORDER BY b ASC} \ + xFilter {SELECT rowid, NULL, b, NULL FROM 'treal' ORDER BY b ASC} ] do_test vtab1-4.3 { set echo_module "" cksort { @@ -515,8 +517,8 @@ do_test vtab1-4.3 { } {5 2 nosort} do_test vtab1-4.4 { set echo_module -} [list xBestIndex {SELECT rowid, * FROM 'treal' ORDER BY b DESC} \ - xFilter {SELECT rowid, * FROM 'treal' ORDER BY b DESC} ] +} [list xBestIndex {SELECT rowid, NULL, b, NULL FROM 'treal' ORDER BY b DESC} \ + xFilter {SELECT rowid, NULL, b, NULL FROM 'treal' ORDER BY b DESC} ] do_test vtab1-4.3 { set echo_module "" cksort { @@ -525,8 +527,8 @@ do_test vtab1-4.3 { } {2 5 sort} do_test vtab1-4.4 { set echo_module -} [list xBestIndex {SELECT rowid, * FROM 'treal'} \ - xFilter {SELECT rowid, * FROM 'treal'} ] +} [list xBestIndex {SELECT rowid, NULL, b, NULL FROM 'treal'} \ + xFilter {SELECT rowid, NULL, b, NULL FROM 'treal'} ] execsql { DROP TABLE t1; @@ -575,9 +577,9 @@ do_test vtab1-5-2 { do_test vtab1-5-3 { filter $echo_module } [list \ - xFilter {SELECT rowid, * FROM 't1'} \ - xFilter {SELECT rowid, * FROM 't2'} \ - xFilter {SELECT rowid, * FROM 't2'} \ + xFilter {SELECT rowid, a, b, c FROM 't1'} \ + xFilter {SELECT rowid, d, e, f FROM 't2'} \ + xFilter {SELECT rowid, d, e, f FROM 't2'} \ ] do_test vtab1-5-4 { set echo_module "" @@ -591,9 +593,9 @@ do_test vtab1-5-4 { do_test vtab1-5-5 { filter $echo_module } [list \ - xFilter {SELECT rowid, * FROM 't1'} \ - xFilter {SELECT rowid, * FROM 't2'} \ - xFilter {SELECT rowid, * FROM 't2'} \ + xFilter {SELECT rowid, a, b, c FROM 't1'} \ + xFilter {SELECT rowid, d, e, f FROM 't2'} \ + xFilter {SELECT rowid, d, e, f FROM 't2'} \ ] do_test vtab1-5-6 { execsql { @@ -615,9 +617,9 @@ do_test vtab1-5-6 { do_test vtab1-5-7 { filter $::echo_module } [list \ - xFilter {SELECT rowid, * FROM 't1'} \ - xFilter {SELECT rowid, * FROM 't2' WHERE d = ?} \ - xFilter {SELECT rowid, * FROM 't2' WHERE d = ?} \ + xFilter {SELECT rowid, a, b, c FROM 't1'} \ + xFilter {SELECT rowid, d, e, f FROM 't2' WHERE d = ?} \ + xFilter {SELECT rowid, d, e, f FROM 't2' WHERE d = ?} \ ] execsql { @@ -967,8 +969,8 @@ do_test vtab1.10-5 { } set echo_module } [list \ - xBestIndex {SELECT rowid, * FROM 'r'} \ - xFilter {SELECT rowid, * FROM 'r'} \ + xBestIndex {SELECT rowid, a, b, c FROM 'r'} \ + xFilter {SELECT rowid, a, b, c FROM 'r'} \ ] proc match_func {args} {return ""} do_test vtab1.10-6 { @@ -979,8 +981,8 @@ do_test vtab1.10-6 { } set echo_module } [list \ - xBestIndex {SELECT rowid, * FROM 'r'} \ - xFilter {SELECT rowid, * FROM 'r'} \ + xBestIndex {SELECT rowid, a, b, c FROM 'r'} \ + xFilter {SELECT rowid, a, b, c FROM 'r'} \ ] @@ -1153,13 +1155,15 @@ do_test vtab1-14.2 { set echo_module "" execsql { SELECT * FROM echo_c WHERE rowid = 1 } set echo_module -} [list xBestIndex {SELECT rowid, * FROM 'c' WHERE rowid = ?} xFilter {SELECT rowid, * FROM 'c' WHERE rowid = ?} 1] +} [list xBestIndex {SELECT rowid, a, b, c FROM 'c' WHERE rowid = ?} \ + xFilter {SELECT rowid, a, b, c FROM 'c' WHERE rowid = ?} 1] do_test vtab1-14.3 { set echo_module "" execsql { SELECT * FROM echo_c WHERE a = 1 } set echo_module -} [list xBestIndex {SELECT rowid, * FROM 'c' WHERE a = ?} xFilter {SELECT rowid, * FROM 'c' WHERE a = ?} 1] +} [list xBestIndex {SELECT rowid, a, b, c FROM 'c' WHERE a = ?} \ + xFilter {SELECT rowid, a, b, c FROM 'c' WHERE a = ?} 1] #do_test vtab1-14.4 { # set echo_module "" @@ -1300,16 +1304,16 @@ do_execsql_test 18.1.0 { foreach {tn sql res filter} { 1.1 "SELECT a FROM e6 WHERE b>'James'" {4 1 5} - {xFilter {SELECT rowid, * FROM 't6' WHERE b > ?} James} + {xFilter {SELECT rowid, a, b FROM 't6' WHERE b > ?} James} 1.2 "SELECT a FROM e6 WHERE b>='J' AND b<'K'" {3 4} - {xFilter {SELECT rowid, * FROM 't6' WHERE b >= ? AND b < ?} J K} + {xFilter {SELECT rowid, a, b FROM 't6' WHERE b >= ? AND b < ?} J K} 1.3 "SELECT a FROM e6 WHERE b LIKE 'J%'" {3 4} - {xFilter {SELECT rowid, * FROM 't6'}} + {xFilter {SELECT rowid, a, b FROM 't6' WHERE b like ?} J%} 1.4 "SELECT a FROM e6 WHERE b LIKE 'j%'" {3 4} - {xFilter {SELECT rowid, * FROM 't6'}} + {xFilter {SELECT rowid, a, b FROM 't6' WHERE b like ?} j%} } { set echo_module {} do_execsql_test 18.$tn.1 $sql $res @@ -1319,10 +1323,10 @@ foreach {tn sql res filter} { do_execsql_test 18.2.0 { PRAGMA case_sensitive_like = ON } foreach {tn sql res filter} { 2.1 "SELECT a FROM e6 WHERE b LIKE 'J%'" {3 4} - {xFilter {SELECT rowid, * FROM 't6'}} + {xFilter {SELECT rowid, a, b FROM 't6' WHERE b like ?} J%} 2.2 "SELECT a FROM e6 WHERE b LIKE 'j%'" {} - {xFilter {SELECT rowid, * FROM 't6'}} + {xFilter {SELECT rowid, a, b FROM 't6' WHERE b like ?} j%} } { set echo_module {} do_execsql_test 18.$tn.1 $sql $res diff --git a/test/vtab4.test b/test/vtab4.test index 07b6e839d7..d12ca33cc0 100644 --- a/test/vtab4.test +++ b/test/vtab4.test @@ -57,9 +57,9 @@ do_test vtab4-1.3 { UPDATE techo SET a = 2; } set echo_module -} [list xBestIndex {SELECT rowid, * FROM 'treal'} \ +} [list xBestIndex {SELECT rowid, a, b, c FROM 'treal'} \ xBegin echo(treal) \ - xFilter {SELECT rowid, * FROM 'treal'} \ + xFilter {SELECT rowid, a, b, c FROM 'treal'} \ xSync echo(treal) \ xCommit echo(treal) \ ] @@ -69,9 +69,9 @@ do_test vtab4-1.4 { DELETE FROM techo; } set echo_module -} [list xBestIndex {SELECT rowid, * FROM 'treal'} \ +} [list xBestIndex {SELECT rowid, NULL, NULL, NULL FROM 'treal'} \ xBegin echo(treal) \ - xFilter {SELECT rowid, * FROM 'treal'} \ + xFilter {SELECT rowid, NULL, NULL, NULL FROM 'treal'} \ xSync echo(treal) \ xCommit echo(treal) \ ] @@ -105,12 +105,12 @@ do_test vtab4-2.2 { COMMIT; } set echo_module -} [list xBestIndex {SELECT rowid, * FROM 'treal'} \ +} [list xBestIndex {SELECT rowid, a, b, c FROM 'treal'} \ xBegin echo(sreal) \ - xFilter {SELECT rowid, * FROM 'treal'} \ - xBestIndex {SELECT rowid, * FROM 'treal'} \ + xFilter {SELECT rowid, a, b, c FROM 'treal'} \ + xBestIndex {SELECT rowid, NULL, NULL, NULL FROM 'treal'} \ xBegin echo(treal) \ - xFilter {SELECT rowid, * FROM 'treal'} \ + xFilter {SELECT rowid, NULL, NULL, NULL FROM 'treal'} \ xSync echo(sreal) \ xSync echo(treal) \ xCommit echo(sreal) \ @@ -137,12 +137,12 @@ do_test vtab4-2.5 { ROLLBACK; } set echo_module -} [list xBestIndex {SELECT rowid, * FROM 'sreal'} \ +} [list xBestIndex {SELECT rowid, a, b, c FROM 'sreal'} \ xBegin echo(treal) \ - xFilter {SELECT rowid, * FROM 'sreal'} \ - xBestIndex {SELECT rowid, * FROM 'sreal'} \ + xFilter {SELECT rowid, a, b, c FROM 'sreal'} \ + xBestIndex {SELECT rowid, NULL, NULL, NULL FROM 'sreal'} \ xBegin echo(sreal) \ - xFilter {SELECT rowid, * FROM 'sreal'} \ + xFilter {SELECT rowid, NULL, NULL, NULL FROM 'sreal'} \ xRollback echo(treal) \ xRollback echo(sreal) \ ] @@ -178,12 +178,12 @@ do_test vtab4-3.3 { COMMIT; } set echo_module -} [list xBestIndex {SELECT rowid, * FROM 'sreal'} \ +} [list xBestIndex {SELECT rowid, a, b, c FROM 'sreal'} \ xBegin echo(treal) \ - xFilter {SELECT rowid, * FROM 'sreal'} \ - xBestIndex {SELECT rowid, * FROM 'sreal'} \ + xFilter {SELECT rowid, a, b, c FROM 'sreal'} \ + xBestIndex {SELECT rowid, NULL, NULL, NULL FROM 'sreal'} \ xBegin echo(sreal) \ - xFilter {SELECT rowid, * FROM 'sreal'} \ + xFilter {SELECT rowid, NULL, NULL, NULL FROM 'sreal'} \ xSync echo(treal) \ xSync echo(sreal) \ xRollback echo(treal) \ diff --git a/test/vtabE.test b/test/vtabE.test index 22ec0181bf..aeb478e3e8 100644 --- a/test/vtabE.test +++ b/test/vtabE.test @@ -46,3 +46,5 @@ do_test vtabE-1 { ORDER BY t1.value, t2.value; } } {vtabE vtabE1 11 vtabE1 w x {} vtabE vtabE1 11 vtabE1 y z {} vtabE vtabE2 22 vtabE2 a b {} vtabE vtabE2 22 vtabE2 c d {}} + +finish_test diff --git a/test/vtabH.test b/test/vtabH.test new file mode 100644 index 0000000000..6034d95fa1 --- /dev/null +++ b/test/vtabH.test @@ -0,0 +1,218 @@ +# 2015 Nov 24 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. Specifically, +# it tests that the GLOB, LIKE and REGEXP operators are correctly exposed +# to virtual table implementations. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix vtabH + +ifcapable !vtab { + finish_test + return +} + +register_echo_module db + +do_execsql_test 1.0 { + CREATE TABLE t6(a, b TEXT); + CREATE INDEX i6 ON t6(b, a); + CREATE VIRTUAL TABLE e6 USING echo(t6); +} + +foreach {tn sql expect} { + 1 "SELECT * FROM e6 WHERE b LIKE 'abc'" { + xBestIndex {SELECT rowid, a, b FROM 't6' WHERE b like ?} + xFilter {SELECT rowid, a, b FROM 't6' WHERE b like ?} abc + } + + 2 "SELECT * FROM e6 WHERE b GLOB 'abc'" { + xBestIndex {SELECT rowid, a, b FROM 't6' WHERE b glob ?} + xFilter {SELECT rowid, a, b FROM 't6' WHERE b glob ?} abc + } +} { + do_test 1.$tn { + set echo_module {} + execsql $sql + set ::echo_module + } [list {*}$expect] +} + + +#-------------------------------------------------------------------------- + +register_tclvar_module db +set ::xyz 10 +do_execsql_test 2.0 { + CREATE VIRTUAL TABLE vars USING tclvar; + SELECT * FROM vars WHERE name = 'xyz'; +} {xyz {} 10} + +set x1 aback +set x2 abaft +set x3 abandon +set x4 abandonint +set x5 babble +set x6 baboon +set x7 backbone +set x8 backarrow +set x9 castle + +db func glob gfunc +proc gfunc {a b} { + incr ::gfunc + return 1 +} + +db func like lfunc +proc lfunc {a b} { + incr ::gfunc 100 + return 1 +} + +db func regexp rfunc +proc rfunc {a b} { + incr ::gfunc 10000 + return 1 +} + +foreach ::tclvar_set_omit {0 1} { + foreach {tn expr res cnt} { + 1 {value GLOB 'aban*'} {x3 abandon x4 abandonint} 2 + 2 {value LIKE '%ac%'} {x1 aback x7 backbone x8 backarrow} 300 + 3 {value REGEXP '^......$'} {x5 babble x6 baboon x9 castle} 30000 + } { + db cache flush + set ::gfunc 0 + if {$::tclvar_set_omit} {set cnt 0} + + do_test 2.$tclvar_set_omit.$tn.1 { + execsql "SELECT name, value FROM vars WHERE name MATCH 'x*' AND $expr" + } $res + + do_test 2.$tclvar_set_omit.$tn.2 { + set ::gfunc + } $cnt + } +} + +#------------------------------------------------------------------------- +# +if {1} { + reset_db + register_fs_module db + do_execsql_test 3.0 { + SELECT name FROM fsdir WHERE dir = '.' AND name = 'test.db'; + SELECT name FROM fsdir WHERE dir = '.' AND name = '.' + } {test.db .} + + proc list_root_files {} { + if {$::tcl_platform(platform) eq "windows"} { + set res [list] + foreach name [glob -directory $::env(SystemDrive)/ -- *] { + if {[string index [file tail $name] 0] eq "."} continue + lappend res $name + } + return $res + } else { + return [exec ls -U /] + } + } + + proc list_files { pattern } { + if {$::tcl_platform(platform) eq "windows"} { + set res [list] + foreach name [glob -nocomplain $pattern] { + if {[string index [file tail $name] 0] eq "."} continue + lappend res $name + } + return $res + } else { + return [glob -nocomplain $pattern] + } + } + + # Read the first 5 entries from the root directory. + # + set res [list] + foreach p [lrange [list_root_files] 0 4] { + if {$::tcl_platform(platform) eq "windows"} { + lappend res $p + } else { + lappend res "/$p" + } + } + do_execsql_test 3.1 { + SELECT path FROM fstree LIMIT 5; + } $res + + # Read all entries in the current directory. + # + proc contents {pattern} { + set res [list] + foreach f [list_files $pattern] { + lappend res $f + if {[file isdir $f]} { + set res [concat $res [contents "$f/*"]] + } + } + set res + } + set pwd "[pwd]/*" + set res [contents $pwd] + do_execsql_test 3.2 { + SELECT path FROM fstree WHERE path GLOB $pwd ORDER BY 1 + } [lsort $res] + + # Add some sub-directories and files to the current directory. + # + do_test 3.3 { + catch { file delete -force subdir } + foreach {path sz} { + subdir/x1.txt 143 + subdir/x2.txt 153 + } { + set dir [file dirname $path] + catch { file mkdir $dir } + set fd [open $path w] + puts -nonewline $fd [string repeat 1 $sz] + close $fd + } + } {} + + set pwd [pwd] + do_execsql_test 3.5 { + SELECT path, size FROM fstree WHERE path GLOB $pwd || '/subdir/*' ORDER BY 1 + } [list \ + "$pwd/subdir/x1.txt" 143 \ + "$pwd/subdir/x2.txt" 153 \ + ] + do_execsql_test 3.6 { + SELECT path, size FROM fstree WHERE path LIKE $pwd || '/subdir/%' ORDER BY 1 + } [list \ + "$pwd/subdir/x1.txt" 143 \ + "$pwd/subdir/x2.txt" 153 \ + ] + do_execsql_test 3.7 { + SELECT sum(size) FROM fstree WHERE path LIKE $pwd || '/subdir/%' + } 296 + do_execsql_test 3.8 { + SELECT size FROM fstree WHERE path = $pwd || '/subdir/x1.txt' + } 143 + +} + + +finish_test + + diff --git a/test/vtabI.test b/test/vtabI.test new file mode 100644 index 0000000000..4b5a0a8c32 --- /dev/null +++ b/test/vtabI.test @@ -0,0 +1,126 @@ +# 2015 Nov 26 +# +# 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. Specifically, +# it tests the sqlite3_index_info.colUsed variable is set correctly. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix vtabI + +ifcapable !vtab { + finish_test + return +} + +register_echo_module db + +do_execsql_test 1.0 { + CREATE TABLE t1(a, b, c, d, e); + CREATE VIRTUAL TABLE e1 USING echo(t1); +} + +foreach {tn query filter} { + 1 {SELECT * FROM e1} + {SELECT rowid, a, b, c, d, e FROM 't1'} + + 2 {SELECT a, b FROM e1} + {SELECT rowid, a, b, NULL, NULL, NULL FROM 't1'} + + 3 {SELECT count(*) FROM e1 GROUP BY b} + {SELECT rowid, NULL, b, NULL, NULL, NULL FROM 't1'} + + 4 {SELECT count(*) FROM e1 GROUP BY b HAVING a=?} + {SELECT rowid, a, b, NULL, NULL, NULL FROM 't1'} + + 5 {SELECT a FROM e1 WHERE c=?} + {SELECT rowid, a, NULL, c, NULL, NULL FROM 't1'} + + 6 {SELECT a FROM e1 ORDER BY e} + {SELECT rowid, a, NULL, NULL, NULL, e FROM 't1'} + + 7 {SELECT a FROM e1 ORDER BY e, d} + {SELECT rowid, a, NULL, NULL, d, e FROM 't1'} +} { + do_test 1.$tn { + set ::echo_module [list] + execsql $query + set idx [lsearch -exact $::echo_module xFilter] + lindex $::echo_module [expr $idx+1] + } $filter +} + +#------------------------------------------------------------------------- +# Tests with a table with more than 64 columns. +# +proc all_col_list {} { + set L [list] + for {set i 1} {$i <= 100} {incr i} { lappend L "c$i" } + set L +} + +proc part_col_list {cols} { + set L [list] + for {set i 1} {$i <= 100} {incr i} { + set c "c$i" + if {[lsearch $cols $c]>=0} { + lappend L "c$i" + } else { + lappend L NULL + } + } + set L +} +proc CL {args} { + join [part_col_list $args] ", " +} +proc CLT {args} { + set cols $args + for {set i 64} {$i <= 100} {incr i} { + lappend cols "c$i" + } + join [part_col_list $cols] ", " +} + +do_test 2.0 { + execsql "CREATE TABLE t2([join [all_col_list] ,])" + execsql "CREATE VIRTUAL TABLE e2 USING echo(t2)" +} {} + +foreach {tn query filter} { + 1 {SELECT c1, c10, c20 FROM e2} + {SELECT rowid, [CL c1 c10 c20] FROM 't2'} + + 2 {SELECT c40, c50, c60 FROM e2} + {SELECT rowid, [CL c40 c50 c60] FROM 't2'} + + 3 {SELECT c7, c80, c90 FROM e2} + {SELECT rowid, [CLT c7] FROM 't2'} + + 4 {SELECT c64 FROM e2} + {SELECT rowid, [CLT c64] FROM 't2'} + + 5 {SELECT c63 FROM e2} + {SELECT rowid, [CL c63] FROM 't2'} + + 6 {SELECT c22 FROM e2 ORDER BY c50, c70} + {SELECT rowid, [CLT c22 c50] FROM 't2'} + +} { + do_test 2.$tn { + set ::echo_module [list] + execsql $query + set idx [lsearch -exact $::echo_module xFilter] + lindex $::echo_module [expr $idx+1] + } [subst $filter] +} + +finish_test diff --git a/test/where8.test b/test/where8.test index a155a95ab2..38214bc895 100644 --- a/test/where8.test +++ b/test/where8.test @@ -64,13 +64,21 @@ do_test where8-1.3 { execsql_status2 { SELECT c FROM t1 WHERE a > 8 OR b = 'two' } } {IX X II 0 0 6} -do_test where8-1.4 { - execsql_status2 { SELECT c FROM t1 WHERE a > 8 OR b GLOB 't*' } -} {IX X III II 0 0 10} - -do_test where8-1.5 { - execsql_status2 { SELECT c FROM t1 WHERE a > 8 OR b GLOB 'f*' } -} {IX X V IV 0 0 10} +ifcapable like_match_blobs { + do_test where8-1.4a { + execsql_status2 { SELECT c FROM t1 WHERE a > 8 OR b GLOB 't*' } + } {IX X III II 0 0 10} + do_test where8-1.5a { + execsql_status2 { SELECT c FROM t1 WHERE a > 8 OR b GLOB 'f*' } + } {IX X V IV 0 0 10} +} else { + do_test where8-1.4b { + execsql_status2 { SELECT c FROM t1 WHERE a > 8 OR b GLOB 't*' } + } {IX X III II 0 0 9} + do_test where8-1.5 { + execsql_status2 { SELECT c FROM t1 WHERE a > 8 OR b GLOB 'f*' } + } {IX X V IV 0 0 9} +} do_test where8-1.6 { execsql_status { SELECT c FROM t1 WHERE a = 1 OR b = 'three' ORDER BY rowid } diff --git a/tool/sqldiff.c b/tool/sqldiff.c index 0f406d8a03..56ff53ee25 100644 --- a/tool/sqldiff.c +++ b/tool/sqldiff.c @@ -155,6 +155,7 @@ static char *safeId(const char *zId){ "WITH", "WITHOUT", }; int lwr, upr, mid, c, i, x; + if( zId[0]==0 ) return sqlite3_mprintf("\"\""); for(i=x=0; (c = zId[i])!=0; i++){ if( !isalpha(c) && c!='_' ){ if( i>0 && isdigit(c) ){