From: adam Date: Fri, 24 Jun 2011 20:47:06 +0000 (+0000) Subject: Merging local changes to apple-osx X-Git-Tag: mountain-lion~28 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=2e4491d4b1dbc8fdd706703744dcb3bd8d4fd132;p=thirdparty%2Fsqlite.git Merging local changes to apple-osx FossilOrigin-Name: 34f0efa2b12e8eb09331768b1524a076baeb34ea --- diff --git a/manifest b/manifest index 99539b6a6c..8b38b14ad9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Pull\sthe\slast-minute\sfixes\sfor\s3.7.7\sinto\sthe\sapple-osx\sbranch. -D 2011-06-23T17:42:45.824 +C Merging\slocal\schanges\sto\sapple-osx +D 2011-06-24T20:47:06.827 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 8410b02448997eb43bdf0ffa482c9bc2d2624e45 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -127,7 +127,7 @@ F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 F src/backup.c 986c15232757f2873dff35ee3b35cbf935fc573c F src/bitvec.c af50f1c8c0ff54d6bdb7a80e2fceca5a93670bef F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7 -F src/btree.c 8c46f0ab69ad9549c75a3a91fed87abdaa743e2f +F src/btree.c fea8eaab6b7bf0877e35a44827049226e2c397ec F src/btree.h f5d775cd6cfc7ac32a2535b70e8d2af48ef5f2ce F src/btreeInt.h 67978c014fa4f7cc874032dd3aacadd8db656bc3 F src/build.c 5a428625d21ad409514afb40ad083bee25dd957a @@ -149,7 +149,7 @@ F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e F src/legacy.c 015826a958f690302d27e096a68d50b3657e4201 F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e F src/loadext.c 3ae0d52da013a6326310655be6473fd472347b85 -F src/main.c fbe239ee3e61b0e83a8d53028e9efaf8368bc73f +F src/main.c a5414c66da45e65518e9f84e9cfe4ac1ff30ea06 F src/malloc.c 591aedb20ae40813f1045f2ef253438a334775d9 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c 46095d62b241466ef51970e592aa3a7a87e443e1 @@ -164,35 +164,35 @@ F src/mutex_os2.c 882d735098c07c8c6a5472b8dd66e19675fe117f F src/mutex_unix.c b4f4e923bb8de93ec3f251fadb50855f23df9579 F src/mutex_w32.c 5e54f3ba275bcb5d00248b8c23107df2e2f73e33 F src/notify.c 976dd0f6171d4588e89e874fcc765e92914b6d30 -F src/os.c 22ac61d06e72a0dac900400147333b07b13d8e1d +F src/os.c 1663f3754a3da185369a121a4f417762f77880b6 F src/os.h 9dbed8c2b9c1f2f2ebabc09e49829d4777c26bf9 F src/os_common.h a8f95b81eca8a1ab8593d23e94f8a35f35d4078f F src/os_os2.c 4a75888ba3dfc820ad5e8177025972d74d7f2440 -F src/os_unix.c aeef5f00753ee92478ae1d6c0c92b1aee26691c5 +F src/os_unix.c af3aa6c091f501a590b45c95aa6af269ce728a94 F src/os_win.c eafcd6b91cf204a7ef29ac1ef2a1b7132e132e58 -F src/pager.c 120550e7ef01dafaa2cbb4a0528c0d87c8f12b41 +F src/pager.c e3688b37e781e8e069ed6375299da82a34a41794 F src/pager.h 3f8c783de1d4706b40b1ac15b64f5f896bcc78d1 F src/parse.y 12b7ebd61ea54f0e1b1083ff69cc2c8ce9353d58 F src/pcache.c 49e718c095810c6b3334e3a6d89970aceaddefce F src/pcache.h c683390d50f856d4cd8e24342ae62027d1bb6050 F src/pcache1.c 912bd5687d6df344698d8e69560f347b6e21c18a -F src/pragma.c 2824f4c8f34fd917ba33948ae564ff947c1e6576 +F src/pragma.c a1010255cd602e2d0cd7fa6f63f9de338ce8ac78 F src/prepare.c 9d7403fe75fefa134351b41400d09ba1b189134b F src/printf.c 585a36b6a963df832cfb69505afa3a34ed5ef8a1 F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 F src/resolve.c 1c0f32b64f8e3f555fe1f732f9d6f501a7f05706 F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697 -F src/select.c d9d440809025a58547e39f4f268c2a296bfb56ff +F src/select.c 75b7dd8c6a5ff9b8ec3f7ed51d31af8b30f114bc F src/shell.c 0e0173b3e79d956368013e759f084caa7995ecb1 -F src/sqlite.h.in 4b7255c10d39c5faf089dbd29cde7c367ff39f1f -F src/sqlite3_private.h 2a814d17913732831acf13e7e87860105a3416e4 +F src/sqlite.h.in 6961961e59fdb55d6719f11db79fb167c19bb275 +F src/sqlite3_private.h 1d18557420cb0cc51ff31ec0a3fcce11e5cd6f5a F src/sqlite3ext.h c90bd5507099f62043832d73f6425d8d5c5da754 F src/sqliteInt.h 7b72f7c6929757c0678d94524dbc7e7e3d6dc398 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c 7ac64842c86cec2fc1a1d0e5c16d3beb8ad332bf F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e F src/tclsqlite.c 5db825be61708b1a2b3f8f6e185e9b753829acef -F src/test1.c 07463c4177b450dd4d9e2425c58ea8cc34502247 +F src/test1.c 4671911a936bc2c9a845bf32f70d770e7a5cd0b8 F src/test2.c 80d323d11e909cf0eb1b6fbb4ac22276483bcf31 F src/test3.c 124ff9735fb6bb7d41de180d6bac90e7b1509432 F src/test4.c d1e5a5e904d4b444cf572391fdcb017638e36ff7 @@ -205,7 +205,7 @@ F src/test_async.c 0612a752896fad42d55c3999a5122af10dcf22ad F src/test_autoext.c 30e7bd98ab6d70a62bb9ba572e4c7df347fe645e F src/test_backup.c 64fd6173ad99daade1227aa17c3ca0d18fa5e5fa F src/test_btree.c 47cd771250f09cdc6e12dda5bc71bc0b3abc96e2 -F src/test_config.c a7e692af95c211b29bd6a6c4b3dfe0a2a8f808a7 +F src/test_config.c 90707f2dd59c01f11bbbbb168833fdef73f1e5de F src/test_demovfs.c 20a4975127993f4959890016ae9ce5535a880094 F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc F src/test_func.c cbdec5cededa0761daedde5baf06004a9bf416b5 @@ -224,11 +224,11 @@ F src/test_onefile.c 40cf9e212a377a6511469384a64b01e6e34b2eec F src/test_osinst.c 62b0b8ef21ce754cc94e17bb42377ed8795dba32 F src/test_pcache.c 7bf828972ac0d2403f5cfa4cd14da41f8ebe73d8 F src/test_quota.c cc4f67e12558a252ea4a11720be268348f4b1595 -F src/test_rtree.c 30c981837445a4e187ee850a49c4760d9642f7c3 +F src/test_rtree.c af76d5a2ead69280ba33d318263167ad9e674bb0 F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0 F src/test_server.c 2f99eb2837dfa06a4aacf24af24c6affdf66a84f F src/test_stat.c f682704b5d1ba8e1d4e7e882a6d7922e2dcf066c -F src/test_superlock.c 2b97936ca127d13962c3605dbc9a4ef269c424cd +F src/test_superlock.c 12e2bc484c6c2ba837327d37f2e6a6fd9d1464f8 F src/test_syscall.c 162c4ec0137a549c009bb9ecab550527743cfc5d F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa F src/test_thread.c fe9a7803fc1d69cccb60f016f28c1cedf2d9fcfa @@ -249,10 +249,10 @@ F src/vdbeapi.c 7f01db7d26758b2be316116f8325a0b9f0d8edd6 F src/vdbeaux.c 0b2e2880f13af400a27c92a7673287c3eaec8b21 F src/vdbeblob.c f024f0bf420f36b070143c32b15cc7287341ffd3 F src/vdbemem.c 0498796b6ffbe45e32960d6a1f5adfb6e419883b -F src/vdbetrace.c 5d0dc3d5fd54878cc8d6d28eb41deb8d5885b114 +F src/vdbetrace.c 4b92fe7355f682368203d29b2be7125cbab85e79 F src/vtab.c 901791a47318c0562cd0c676a2c6ff1bc530e582 -F src/wal.c 0c70ad7b1cac6005fa5e2cbefd23ee05e391c290 -F src/wal.h 66b40bd91bc29a5be1c88ddd1f5ade8f3f48728a +F src/wal.c 876f9b68df44e65a32c5106010a0a95ee554a284 +F src/wal.h e75d87752bd5df3dc4152ee2cb3b0dcd0b309e5e F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f F src/where.c 55403ce19c506be6a321c7f129aff693d6103db5 F test/8_3_names.test b93687beebd17f6ebf812405a6833bae5d1f4199 @@ -399,7 +399,7 @@ F test/exclusive2.test 343d55130c12c67b8bf10407acec043a6c26c86b F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7 F test/exists.test 5e2d64b4eb5a9d08876599bdae2e1213d2d12e2a F test/expr.test 67c9fd6f8f829e239dc8b0f4a08a73c08b09196d -F test/fallocate.test a9927b638567e2e776c112f54d701402a0e74023 +F test/fallocate.test 95a45d7a705ab985a62ceed8ac7b145fcb61154e F test/filectrl.test 97003734290887566e01dded09dc9e99cb937e9e F test/filefmt.test f178cfc29501a14565954c961b226e61877dd32c F test/fkey1.test 01c7de578e11747e720c2d9aeef27f239853c4da @@ -504,7 +504,7 @@ F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0 F test/in4.test 64f3cc1acde1b9161ccdd8e5bde3daefdb5b2617 F test/incrblob.test 76e787ca3301d9bfa6906031c626d26f8dd707de F test/incrblob2.test edc3a96e557bd61fb39acc8d2edd43371fbbaa19 -F test/incrblob3.test aedbb35ea1b6450c33b98f2b6ed98e5020be8dc7 +F test/incrblob3.test 086482e7937202be9f8cf327117a042d8628fe7d F test/incrblob_err.test c577c91d4ed9e8336cdb188b15d6ee2a6fe9604e F test/incrblobfault.test 917c0292224c64a56ef7215fd633a3a82f805be0 F test/incrvacuum.test 453d1e490d8f5ad2c9b3a54282a0690d6ae56462 @@ -599,7 +599,7 @@ F test/misc5.test 45b2e3ed5f79af2b4f38ae362eaf4c49674575bd F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91 F test/misc7.test 29032efcd3d826fbd409e2a7af873e7939f4a4e3 F test/misuse.test 30b3a458e5a70c31e74c291937b6c82204c59f33 -F test/multiplex.test 555080c87abfc72ba68e2f3df01d4a9a7a4fdf58 +F test/multiplex.test afd41c4fcf132a8940761c7852b427c37705e4bb F test/mutex1.test 78b2b9bb320e51d156c4efdb71b99b051e7a4b41 F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660 F test/nan.test dc212a22b36109fd1ae37154292444ef249c5ec2 @@ -610,7 +610,7 @@ F test/notnull.test cc7c78340328e6112a13c3e311a9ab3127114347 F test/null.test a8b09b8ed87852742343b33441a9240022108993 F test/openv2.test af02ed0a9cbc0d2a61b8f35171d4d117e588e4ec F test/oserror.test 3fe52e0bd2891a9bf7cdeb639554992453d46301 -F test/pager1.test 228a831060dab96bc91b03ba2a85cedefd1ab38a +F test/pager1.test 4b7c08cab0717a051e9faf093d23ac202a280050 F test/pager2.test 745b911dde3d1f24ae0870bd433dfa83d7c658c1 F test/pager3.test 3856d9c80839be0668efee1b74811b1b7f7fc95f F test/pagerfault.test b0a275779f0b0233809d2644dec94d689357e00a @@ -621,7 +621,7 @@ F test/pagesize.test 76aa9f23ecb0741a4ed9d2e16c5fa82671f28efb F test/pcache.test 065aa286e722ab24f2e51792c1f093bf60656b16 F test/pcache2.test 0d85f2ab6963aee28c671d4c71bec038c00a1d16 F test/permutations.test 1e8892ebf1bd6e9e8036f4841c72a91bf72da74a -F test/pragma.test 9b08f84e691065281c5e705988a754bf26c2bfaa +F test/pragma.test 6c4b98a9f5e3fbc23631fcbfa53b095f3955ca72 F test/pragma2.test 5364893491b9231dd170e3459bfc2e2342658b47 F test/printf.test 05970cde31b1a9f54bd75af60597be75a5c54fea F test/progress.test 5b075c3c790c7b2a61419bc199db87aaf48b8301 @@ -690,7 +690,7 @@ F test/stmt.test 78a6764439cfa5abdcbf98d4d084739e81eeec4f F test/subquery.test b524f57c9574b2c0347045b4510ef795d4686796 F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4 F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a -F test/superlock.test 5d7a4954b0059c903f82c7b67867bc5451a7c082 +F test/superlock.test 9c567d759fe383abefde0e801e36a3b3aadb082e F test/sync.test 2bd73b585089c99e76b3e1796c42059b7d89d872 F test/syscall.test 707c95e4ab7863e13f1293c6b0c76bead30249b3 F test/sysfault.test c79441d88d23696fbec7b147dba98d42a04f523f @@ -700,7 +700,7 @@ F test/tclsqlite.test 8c154101e704170c2be10f137a5499ac2c6da8d3 F test/tempdb.test 3263e5c3f0604e54d307481e8587327c54544d18 F test/temptable.test f42121a0d29a62f00f93274464164177ab1cc24a F test/temptrigger.test b0273db072ce5f37cf19140ceb1f0d524bbe9f05 -F test/tester.tcl 801cb7ba763bf5a19475cfb865ad55dddb39ad6a +F test/tester.tcl be83dc1662e7bce357d53f7ff9cecdc16a7046eb F test/thread001.test a3e6a7254d1cb057836cb3145b60c10bf5b7e60f F test/thread002.test afd20095e6e845b405df4f2c920cb93301ca69db F test/thread003.test b824d4f52b870ae39fc5bae4d8070eca73085dca @@ -881,23 +881,23 @@ F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5 F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8 F test/vtab_shared.test 0eff9ce4f19facbe0a3e693f6c14b80711a4222d F test/wal.test 3ff610479cd005117f54a8c003a8d86432e6f2ac -F test/wal2.test 24239bb8cd8ca5e47c9ab4ac705f7343d2f9b5a2 -F test/wal3.test 34c65b1cb78e1da5d24c9ab74534c376784f96ea +F test/wal2.test 6c65a48c23309baec605d8b14dbc41c2d5baceb0 +F test/wal3.test 804be49c55b362c356f2b98cd8bac77145cfa647 F test/wal4.test 6a68c45bc1ca24a3592ec449ddcb92b29d0e0e87 F test/wal5.test f06a0427e06db00347e32eb9fa99d6a5c0f2d088 -F test/wal6.test 07aa31ca8892d0527f2c5c5a9a2a87aa421dfaa8 +F test/wal6.test 8df39843b1471dc02993d173937b2c6589e2cb59 F test/wal7.test 2ae8f427d240099cc4b2dfef63cff44e2a68a1bd F test/wal_common.tcl a98f17fba96206122eff624db0ab13ec377be4fe F test/walbak.test 767e1c9e0ea0cfb907873b332883e66e187fa4bc F test/walbig.test 78ac493db2abdb65b9c6cace5b851cc32df1d449 F test/walcksum.test cf6787f2ee1a6a3da6f0c2b20b9ede5153e4e03f F test/walcrash.test 80c1cc3173a0ef09d8303fa556cb0187a36d82ea -F test/walcrash2.test 929c99d14ee2e3e3ef82585058968a8b12f72706 +F test/walcrash2.test c032d0040374ae28b41f99fc0cc290b4e2e34f17 F test/walfault.test 7db81f3dac64ce8897196f199c2909078bcabf8d F test/walhook.test c934ac5219fee2b4e7653d291db9107b8dc73bba F test/walmode.test feb39956ec6f415fbb9dcb12d91243391c2c4715 F test/walnoshm.test a074428046408f4eb5c6a00e09df8cc97ff93317 -F test/walro.test 2d5d69e2e99da19ce6faab340330234fc4ca0720 +F test/walro.test 05769ae10ddce1b6ad41bdd5326ab7ef951c8278 F test/walshared.test 0befc811dcf0b287efae21612304d15576e35417 F test/walslow.test 989854bc5c214700a9f2d545bb158643813b8881 F test/walthread.test e6e32e93ccebfa401dfc0dd930c79daa3472b0ae @@ -954,7 +954,7 @@ F tool/symbols.sh bc2a3709940d47c8ac8e0a1fdf17ec801f015a00 F tool/tostr.awk 11760e1b94a5d3dcd42378f3cc18544c06cfa576 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f F tool/warnings.sh 347d974d143cf132f953b565fbc03026f19fcb4d -P b5acda0445db39d58229d65a361d539db30f3833 b61a76a53af04f731fe7617f7b6b4fb2aef6587b -R c25239c8ec2046d490b64494ae9d6208 -U drh -Z bf5f3fb5150b87e28dd1ca0cfe21c8ad +P 2d4458af59c697ad453996815d14442230654670 +R 7252e755eb943c8802f791d94d90614b +U adam +Z 048c280bdc3796d3c8e3318197ddc394 diff --git a/manifest.uuid b/manifest.uuid index f2650fe019..cccdb92cdb 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2d4458af59c697ad453996815d14442230654670 \ No newline at end of file +34f0efa2b12e8eb09331768b1524a076baeb34ea \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 3d7162dbb3..3a58cbdc21 100644 --- a/src/btree.c +++ b/src/btree.c @@ -68,8 +68,14 @@ static BtShared *SQLITE_WSD sqlite3SharedCacheList = 0; ** sqlite3_open(), sqlite3_open16(), or sqlite3_open_v2(). */ int sqlite3_enable_shared_cache(int enable){ +#if defined(__APPLE__) && !defined(SQLITE_TEST) && !defined(TH3_COMPATIBILITY) + /* Enable global shared cache function for debugging and unit tests, + ** but not for release */ + return SQLITE_MISUSE; +#else sqlite3GlobalConfig.sharedCacheEnabled = enable; return SQLITE_OK; +#endif } #endif @@ -2350,6 +2356,18 @@ static int lockBtree(BtShared *pBt){ if( rc!=SQLITE_OK ){ goto page1_init_failed; }else if( isOpen==0 ){ +#ifdef SQLITE_DEFAULT_WAL_SAFETYLEVEL + /* Default to specified safety_level for WAL mode */ + if( pBt->db!=0 && pBt->db->aDb!=0 ){ + sqlite3 *db = pBt->db; + if( db->aDb[0].safety_level != SQLITE_DEFAULT_WAL_SAFETYLEVEL) { + db->aDb[0].safety_level = SQLITE_DEFAULT_WAL_SAFETYLEVEL; + sqlite3PagerSetSafetyLevel(pBt->pPager, SQLITE_DEFAULT_WAL_SAFETYLEVEL, + (db->flags&SQLITE_FullFSync)!=0, + (db->flags&SQLITE_CkptFullFSync)!=0); + } + } +#endif releasePage(pPage1); return SQLITE_OK; } diff --git a/src/main.c b/src/main.c index b55fca0a40..e03c9b6e86 100644 --- a/src/main.c +++ b/src/main.c @@ -1797,10 +1797,15 @@ int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){ return oldLimit; /* IMP: R-53341-35419 */ } #if defined(SQLITE_ENABLE_AUTO_PROFILE) -static void profile_sql(void *aux, const char *sql, u64 ns) { +void _sqlite_auto_profile(void *aux, const char *sql, u64 ns); +void _sqlite_auto_trace(void *aux, const char *sql); +void _sqlite_auto_profile(void *aux, const char *sql, u64 ns) { #pragma unused(aux) fprintf(stderr, "Query: %s\n Execution Time: %llu ms\n", sql, ns / 1000000); } +void _sqlite_auto_trace(void *aux, const char *sql) { + fprintf(stderr, "TraceSQL(%p): %s\n", aux, sql); +} #endif /* @@ -2135,6 +2140,9 @@ static int openDatabase( db->nextAutovac = -1; db->nextPagesize = 0; db->flags |= SQLITE_ShortColNames | SQLITE_AutoIndex | SQLITE_EnableTrigger +#if SQLITE_DEFAULT_CKPTFULLFSYNC + | SQLITE_CkptFullFSync +#endif #if SQLITE_DEFAULT_FILE_FORMAT<4 | SQLITE_LegacyFileFmt #endif @@ -2294,9 +2302,13 @@ opendb_out: #if defined(SQLITE_ENABLE_AUTO_PROFILE) if( db && !rc ){ char *envprofile = getenv("SQLITE_AUTO_PROFILE"); + char *envtrace = getenv("SQLITE_AUTO_TRACE"); if( envprofile!=NULL ){ - sqlite3_profile(db, profile_sql, NULL); + sqlite3_profile(db, _sqlite_auto_profile, db); + } + if( envtrace!=NULL ){ + sqlite3_trace(db, _sqlite_auto_trace, db); } } #endif @@ -2966,3 +2978,145 @@ const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam){ } return 0; } + +#if (SQLITE_ENABLE_APPLE_SPI>0) +#define SQLITE_FILE_HEADER_LEN 16 +#include +#include "sqlite3_private.h" +#include "btreeInt.h" +#include +#include + +/* Check for a conflicting lock. If one is found, print an this + ** on standard output using the format string given and return 1. + ** If there are no conflicting locks, return 0. + */ +static int isLocked( + pid_t pid, /* PID to test for lock owner */ + int h, /* File descriptor to check */ + int type, /* F_RDLCK or F_WRLCK */ + unsigned int iOfst, /* First byte of the lock */ + unsigned int iCnt, /* Number of bytes in the lock range */ + const char *zType /* Type of lock */ +){ + struct flock lk; + int err; + + memset(&lk, 0, sizeof(lk)); + lk.l_type = type; + lk.l_whence = SEEK_SET; + lk.l_start = iOfst; + lk.l_len = iCnt; + + if( pid!=SQLITE_LOCKSTATE_ANYPID ){ +#ifndef F_GETLKPID +# warning F_GETLKPID undefined, _sqlite3_lockstate falling back to F_GETLK + err = fcntl(h, F_GETLK, &lk); +#else + lk.l_pid = pid; + err = fcntl(h, F_GETLKPID, &lk); +#endif + }else{ + err = fcntl(h, F_GETLK, &lk); + } + + if( err==(-1) ){ + fprintf(stderr, "fcntl(%d) failed: errno=%d\n", h, errno); + return -1; + } + + if( lk.l_type!=F_UNLCK && (pid==SQLITE_LOCKSTATE_ANYPID || lk.l_pid==pid) ){ +#ifdef SQLITE_DEBUG + fprintf(stderr, "%s lock held by %d\n", zType, (int)lk.l_pid); +#endif + return 1; + } + return 0; +} + +/* + ** Location of locking bytes in the database file + */ +#ifndef PENDING_BYTE +# define PENDING_BYTE (0x40000000) +# define RESERVED_BYTE (PENDING_BYTE+1) +# define SHARED_FIRST (PENDING_BYTE+2) +# define SHARED_SIZE 510 +#endif /* PENDING_BYTE */ + +/* + ** Lock locations for shared-memory locks used by WAL mode. + */ +#ifndef SHM_BASE +# define SHM_BASE 120 +# define SHM_WRITE SHM_BASE +# define SHM_CHECKPOINT (SHM_BASE+1) +# define SHM_RECOVER (SHM_BASE+2) +# define SHM_READ_FIRST (SHM_BASE+3) +# define SHM_READ_SIZE 5 +#endif /* SHM_BASE */ + +/* +** Testing a file path for sqlite locks held by a process ID. +** Returns SQLITE_LOCKSTATE_ON if locks are present on path +** that would prevent writing to the database. +** +** This test only works for lock testing on unix/posix VFS. +** Adapted from tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce +*/ +int _sqlite3_lockstate(const char *path, pid_t pid){ + int hDb; /* File descriptor for the open database file */ + int hShm; /* File descriptor for WAL shared-memory file */ + ssize_t got; /* Bytes read from header */ + int isWal; /* True if in WAL mode */ + int nLock = 0; /* Number of locks held */ + unsigned char aHdr[100]; /* Database header */ + + /* Open the file at path and make sure we are dealing with a database file */ + hDb = open(path, O_RDONLY | O_NOCTTY); + if( hDb<0 ){ + return SQLITE_LOCKSTATE_ERROR; + } + assert( (strlen(SQLITE_FILE_HEADER)+1)==SQLITE_FILE_HEADER_LEN ); + got = pread(hDb, aHdr, 100, 0); + if( got<0 ){ + close(hDb); + return SQLITE_LOCKSTATE_ERROR; + } + if( got!=100 || memcmp(aHdr, SQLITE_FILE_HEADER, SQLITE_FILE_HEADER_LEN)!=0 ){ + close(hDb); + return SQLITE_LOCKSTATE_NOTADB; + } + + /* First check for an exclusive lock */ + nLock += isLocked(pid, hDb, F_RDLCK, SHARED_FIRST, SHARED_SIZE, "EXCLUSIVE"); + isWal = aHdr[18]==2; + if( nLock==0 && isWal==0 ){ + /* Rollback mode */ + nLock += isLocked(pid, hDb, F_WRLCK, PENDING_BYTE, SHARED_SIZE+2, "PENDING|RESERVED|SHARED"); + } + close(hDb); + if( nLock==0 && isWal!=0 ){ + char zShm[MAXPATHLEN]; + + close(hDb); + /* WAL mode */ + strlcpy(zShm, path, MAXPATHLEN); + strlcat(zShm, "-shm", MAXPATHLEN); + hShm = open(zShm, O_RDONLY, 0); + if( hShm<0 ){ + return SQLITE_LOCKSTATE_OFF; + } + if( isLocked(pid, hShm, F_RDLCK, SHM_RECOVER, 1, "WAL-RECOVERY") || + isLocked(pid, hShm, F_RDLCK, SHM_WRITE, 1, "WAL-WRITE") ){ + nLock = 1; + } + close(hShm); + } + if( nLock>0 ){ + return SQLITE_LOCKSTATE_ON; + } + return SQLITE_LOCKSTATE_OFF; +} + +#endif /* SQLITE_ENABLE_APPLE_SPI */ diff --git a/src/os.c b/src/os.c index ba0438adef..49e11b290a 100644 --- a/src/os.c +++ b/src/os.c @@ -131,12 +131,18 @@ int sqlite3OsOpen( int *pFlagsOut ){ int rc; + int openFlags; DO_OS_MALLOC_TEST(0); /* 0x87f3f is a mask of SQLITE_OPEN_ flags that are valid to be passed ** down into the VFS layer. Some SQLITE_OPEN_ flags (for example, ** SQLITE_OPEN_FULLMUTEX or SQLITE_OPEN_SHAREDCACHE) are blocked before ** reaching the VFS. */ - rc = pVfs->xOpen(pVfs, zPath, pFile, flags & 0x87f3f, pFlagsOut); +#if SQLITE_ENABLE_DATA_PROTECTION + openFlags = flags & (0x87f3f | SQLITE_OPEN_FILEPROTECTION_MASK); +#else + openFlags = flags & 0x87f3f; +#endif + rc = pVfs->xOpen(pVfs, zPath, pFile, openFlags, pFlagsOut); assert( rc==SQLITE_OK || pFile->pMethods==0 ); return rc; } diff --git a/src/os_unix.c b/src/os_unix.c index d641a1f4a9..85e17598bb 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -125,6 +125,11 @@ #if SQLITE_ENABLE_LOCKING_STYLE # include +# include +# if defined(__APPLE__) && ((__MAC_OS_X_VERSION_MIN_REQUIRED > 1050) || \ + (__IPHONE_OS_VERSION_MIN_REQUIRED > 2000)) +# define HAVE_GETHOSTUUID 1 +# endif # if OS_VXWORKS # include # include @@ -220,6 +225,9 @@ struct unixFile { #if SQLITE_ENABLE_LOCKING_STYLE int openFlags; /* The flags specified at open() */ #endif +#if SQLITE_ENABLE_DATA_PROTECTION + int protFlags; /* Data protection flags from unixOpen */ +#endif #if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__) unsigned fsFlags; /* cached details from statfs() */ #endif @@ -285,6 +293,285 @@ struct unixFile { #define threadid 0 #endif +#ifdef __APPLE__ +#define SQLITE_ENABLE_SUPERLOCK 1 +#endif + +#if SQLITE_ENABLE_SUPERLOCK +#include "sqlite3.h" +#include /* memset(), strlen() */ +#include /* assert() */ + +/* +** A structure to collect a busy-handler callback and argument and a count +** of the number of times it has been invoked. +*/ +struct SuperlockBusy { + int (*xBusy)(void*,int); /* Pointer to busy-handler function */ + void *pBusyArg; /* First arg to pass to xBusy */ + int nBusy; /* Number of times xBusy has been invoked */ +}; +typedef struct SuperlockBusy SuperlockBusy; + +/* +** An instance of the following structure is allocated for each active +** superlock. The opaque handle returned by sqlite3demo_superlock() is +** actually a pointer to an instance of this structure. +*/ +struct Superlock { + sqlite3 *db; /* Database handle used to lock db */ + int bWal; /* True if db is a WAL database */ +}; +typedef struct Superlock Superlock; + +/* +** The pCtx pointer passed to this function is actually a pointer to a +** SuperlockBusy structure. Invoke the busy-handler function encapsulated +** by the structure and return the result. +*/ +static int superlockBusyHandler(void *pCtx, int UNUSED){ + SuperlockBusy *pBusy = (SuperlockBusy *)pCtx; + if( pBusy->xBusy==0 ) return 0; + return pBusy->xBusy(pBusy->pBusyArg, pBusy->nBusy++); +} + +/* +** This function is used to determine if the main database file for +** connection db is open in WAL mode or not. If no error occurs and the +** database file is in WAL mode, set *pbWal to true and return SQLITE_OK. +** If it is not in WAL mode, set *pbWal to false. +** +** If an error occurs, return an SQLite error code. The value of *pbWal +** is undefined in this case. +*/ +static int superlockIsWal(Superlock *pLock){ + int rc; /* Return Code */ + sqlite3_stmt *pStmt; /* Compiled PRAGMA journal_mode statement */ + + rc = sqlite3_prepare(pLock->db, "PRAGMA main.journal_mode", -1, &pStmt, 0); + if( rc!=SQLITE_OK ) return rc; + + pLock->bWal = 0; + if( SQLITE_ROW==sqlite3_step(pStmt) ){ + const char *zMode = (const char *)sqlite3_column_text(pStmt, 0); + if( zMode && strlen(zMode)==3 && sqlite3_strnicmp("wal", zMode, 3)==0 ){ + pLock->bWal = 1; + } + } + + return sqlite3_finalize(pStmt); +} + +/* +** Obtain an exclusive shm-lock on nByte bytes starting at offset idx +** of the file fd. If the lock cannot be obtained immediately, invoke +** the busy-handler until either it is obtained or the busy-handler +** callback returns 0. +*/ +static int superlockShmLock( + sqlite3_file *fd, /* Database file handle */ + int idx, /* Offset of shm-lock to obtain */ + int nByte, /* Number of consective bytes to lock */ + SuperlockBusy *pBusy /* Busy-handler wrapper object */ +){ + int rc; + int (*xShmLock)(sqlite3_file*, int, int, int) = fd->pMethods->xShmLock; + do { + rc = xShmLock(fd, idx, nByte, SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE); + }while( rc==SQLITE_BUSY && superlockBusyHandler((void *)pBusy, 0) ); + return rc; +} + +/* +** Obtain the extra locks on the database file required for WAL databases. +** Invoke the supplied busy-handler as required. +*/ +static int superlockWalLock( + sqlite3 *db, /* Database handle open on WAL database */ + SuperlockBusy *pBusy /* Busy handler wrapper object */ +){ + int rc; /* Return code */ + sqlite3_file *fd = 0; /* Main database file handle */ + void volatile *p = 0; /* Pointer to first page of shared memory */ + + /* Obtain a pointer to the sqlite3_file object open on the main db file. */ + rc = sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd); + if( rc!=SQLITE_OK ) return rc; + + /* Obtain the "recovery" lock. Normally, this lock is only obtained by + ** clients running database recovery. + */ + rc = superlockShmLock(fd, 2, 1, pBusy); + if( rc!=SQLITE_OK ) return rc; + + /* Zero the start of the first shared-memory page. This means that any + ** clients that open read or write transactions from this point on will + ** have to run recovery before proceeding. Since they need the "recovery" + ** lock that this process is holding to do that, no new read or write + ** transactions may now be opened. Nor can a checkpoint be run, for the + ** same reason. + */ + rc = fd->pMethods->xShmMap(fd, 0, 32*1024, 1, &p); + if( rc!=SQLITE_OK ) return rc; + memset((void *)p, 0, 32); + + /* Obtain exclusive locks on all the "read-lock" slots. Once these locks + ** are held, it is guaranteed that there are no active reader, writer or + ** checkpointer clients. + */ + rc = superlockShmLock(fd, 3, SQLITE_SHM_NLOCK-3, pBusy); + return rc; +} + +/* +** Release a superlock held on a database file. The argument passed to +** this function must have been obtained from a successful call to +** sqlite3demo_superlock(). +*/ +static void sqlite3demo_superunlock(void *pLock){ + Superlock *p = (Superlock *)pLock; + if( p->bWal ){ + int rc; /* Return code */ + int flags = SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE; + sqlite3_file *fd = 0; + rc = sqlite3_file_control(p->db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd); + if( rc==SQLITE_OK ){ + fd->pMethods->xShmLock(fd, 2, 1, flags); + fd->pMethods->xShmLock(fd, 3, SQLITE_SHM_NLOCK-3, flags); + } + } + sqlite3_close(p->db); + sqlite3_free(p); +} + +/* +** Obtain a superlock on the database file identified by zPath, using the +** locking primitives provided by VFS zVfs. If successful, SQLITE_OK is +** returned and output variable *ppLock is populated with an opaque handle +** that may be used with sqlite3demo_superunlock() to release the lock. +** +** If an error occurs, *ppLock is set to 0 and an SQLite error code +** (e.g. SQLITE_BUSY) is returned. +** +** If a required lock cannot be obtained immediately and the xBusy parameter +** to this function is not NULL, then xBusy is invoked in the same way +** as a busy-handler registered with SQLite (using sqlite3_busy_handler()) +** until either the lock can be obtained or the busy-handler function returns +** 0 (indicating "give up"). +*/ +static int sqlite3demo_superlock( + const char *zPath, /* Path to database file to lock */ + const char *zVfs, /* VFS to use to access database file */ + int flags, /* Additional flags to pass to sqlite3_open_v2 */ + int (*xBusy)(void*,int), /* Busy handler callback */ + void *pBusyArg, /* Context arg for busy handler */ + void **ppLock /* OUT: Context to pass to superunlock() */ +){ + SuperlockBusy busy = {0, 0, 0}; /* Busy handler wrapper object */ + int rc; /* Return code */ + Superlock *pLock; + + pLock = sqlite3_malloc(sizeof(Superlock)); + if( !pLock ) return SQLITE_NOMEM; + memset(pLock, 0, sizeof(Superlock)); + + /* Open a database handle on the file to superlock. */ + rc = sqlite3_open_v2( + zPath, &pLock->db, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|flags, zVfs + ); + + /* Install a busy-handler and execute a BEGIN EXCLUSIVE. If this is not + ** a WAL database, this is all we need to do. + ** + ** A wrapper function is used to invoke the busy-handler instead of + ** registering the busy-handler function supplied by the user directly + ** with SQLite. This is because the same busy-handler function may be + ** invoked directly later on when attempting to obtain the extra locks + ** required in WAL mode. By using the wrapper, we are able to guarantee + ** that the "nBusy" integer parameter passed to the users busy-handler + ** represents the total number of busy-handler invocations made within + ** this call to sqlite3demo_superlock(), including any made during the + ** "BEGIN EXCLUSIVE". + */ + if( rc==SQLITE_OK ){ + busy.xBusy = xBusy; + busy.pBusyArg = pBusyArg; + sqlite3_busy_handler(pLock->db, superlockBusyHandler, (void *)&busy); + rc = sqlite3_exec(pLock->db, "BEGIN EXCLUSIVE", 0, 0, 0); + } + + /* If the BEGIN EXCLUSIVE was executed successfully and this is a WAL + ** database, call superlockWalLock() to obtain the extra locks required + ** to prevent readers, writers and/or checkpointers from accessing the + ** db while this process is holding the superlock. + ** + ** Before attempting any WAL locks, commit the transaction started above + ** to drop the WAL read and write locks currently held. Otherwise, the + ** new WAL locks may conflict with the old. + */ + if( rc==SQLITE_OK ){ + if( SQLITE_OK==(rc = superlockIsWal(pLock)) && pLock->bWal ){ + rc = sqlite3_exec(pLock->db, "COMMIT", 0, 0, 0); + if( rc==SQLITE_OK ){ + rc = superlockWalLock(pLock->db, &busy); + } + } + } + + if( rc!=SQLITE_OK ){ + sqlite3demo_superunlock(pLock); + *ppLock = 0; + }else{ + *ppLock = pLock; + } + + return rc; +} + +/* A corrupt DB won't work with the sql-based locking attempt, grab an +** exclusive lock and return SQLITE_OK or SQLITE_BUSY if the lock fails +** returns the current lock level held on sqlite3_file +*/ +static int sqlite3demo_superlock_corrupt(sqlite3_file *id, int eTargetFileLock, int *pFileLock) { + unixFile *pFile = (unixFile*)id; + int eFileLock = pFile->eFileLock; + int rc = SQLITE_OK; + + if( eFileLockpMethod->xLock(id, SQLITE_LOCK_SHARED); + } + if( !rc && eFileLockpMethod->xLock(id, SQLITE_LOCK_EXCLUSIVE); + } + if( rc ){ + if( pFile->eFileLock > eFileLock ){ + pFile->pMethod->xUnlock(id, eFileLock); + } + return rc; + } + if (pFileLock) { + *pFileLock = eFileLock; + } + return SQLITE_OK; +} + +static int sqlite3demo_superunlock_corrupt(sqlite3_file *id, int eFileLock) { + unixFile *pFile = (unixFile*)id; + int rc = SQLITE_OK; + + if( pFile->eFileLock > eFileLock ){ + rc = pFile->pMethod->xUnlock(id, SQLITE_LOCK_SHARED); + } + if( pFile->eFileLock > eFileLock ){ + int unlockRC = pFile->pMethod->xUnlock(id, SQLITE_LOCK_NONE); + if (!rc) rc = unlockRC; + } + return rc; +} + +#endif /* SQLITE_ENABLE_SUPERLOCK */ + + /* ** Different Unix systems declare open() in different ways. Same use ** open(const char*,int,mode_t). Others use open(const char*,int,...). @@ -1062,8 +1349,19 @@ static void closePendingFds(unixFile *pFile){ UnixUnusedFd *pNext; for(p=pInode->pUnused; p; p=pNext){ pNext = p->pNext; +#if OSCLOSE_CHECK_CLOSE_IOERR + if( close(p->fd) ){ + pFile->lastErrno = errno; + rc = SQLITE_IOERR_CLOSE; + p->pNext = pError; + pError = p; + }else{ + sqlite3_free(p); + } +#else robust_close(pFile, p->fd, __LINE__); sqlite3_free(p); +#endif } pInode->pUnused = 0; } @@ -1220,8 +1518,14 @@ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){ lock.l_len = 1; lock.l_type = F_WRLCK; if( osFcntl(pFile->h, F_GETLK, &lock) ){ +#if OSLOCKING_CHECK_BUSY_IOERR + int tErrno = errno; + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_CHECKRESERVEDLOCK); + pFile->lastErrno = tErrno; +#else rc = SQLITE_IOERR_CHECKRESERVEDLOCK; pFile->lastErrno = errno; +#endif } else if( lock.l_type!=F_UNLCK ){ reserved = 1; } @@ -1420,7 +1724,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ if( unixFileLock(pFile, &lock) ){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); - if( rc!=SQLITE_BUSY ){ + if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } goto end_lock; @@ -1451,11 +1755,15 @@ static int unixLock(sqlite3_file *id, int eFileLock){ if( unixFileLock(pFile, &lock) && rc==SQLITE_OK ){ /* This could happen with a network mount */ tErrno = errno; +#if OSLOCKING_CHECK_BUSY_IOERR + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); +#else rc = SQLITE_IOERR_UNLOCK; +#endif } if( rc ){ - if( rc!=SQLITE_BUSY ){ + if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } goto end_lock; @@ -1622,7 +1930,11 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ lock.l_len = divSize; if( unixFileLock(pFile, &lock)==(-1) ){ tErrno = errno; +#if OSLOCKING_CHECK_BUSY_IOERR + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); +#else rc = SQLITE_IOERR_UNLOCK; +#endif if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } @@ -1646,7 +1958,11 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ lock.l_len = SHARED_SIZE-divSize; if( unixFileLock(pFile, &lock)==(-1) ){ tErrno = errno; +#if OSLOCKING_CHECK_BUSY_IOERR + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); +#else rc = SQLITE_IOERR_UNLOCK; +#endif if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } @@ -1660,6 +1976,13 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ lock.l_start = SHARED_FIRST; lock.l_len = SHARED_SIZE; if( unixFileLock(pFile, &lock) ){ +#if OSLOCKING_CHECK_BUSY_IOERR + tErrno = errno; + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK); + if( IS_LOCK_ERROR(rc) ){ + pFile->lastErrno = tErrno; + } +#else /* In theory, the call to unixFileLock() cannot fail because another ** process is holding an incompatible lock. If it does, this ** indicates that the other process is not following the locking @@ -1668,6 +1991,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ ** an assert to fail). */ rc = SQLITE_IOERR_RDLOCK; pFile->lastErrno = errno; +#endif goto end_unlock; } } @@ -1679,8 +2003,16 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ if( unixFileLock(pFile, &lock)==0 ){ pInode->eFileLock = SHARED_LOCK; }else{ +#if OSLOCKING_CHECK_BUSY_IOERR + tErrno = errno; + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); + if( IS_LOCK_ERROR(rc) ){ + pFile->lastErrno = tErrno; + } +#else rc = SQLITE_IOERR_UNLOCK; pFile->lastErrno = errno; +#endif goto end_unlock; } } @@ -1700,8 +2032,16 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ if( unixFileLock(pFile, &lock)==0 ){ pInode->eFileLock = NO_LOCK; }else{ +#if OSLOCKING_CHECK_BUSY_IOERR + tErrno = errno; + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); + if( IS_LOCK_ERROR(rc) ){ + pFile->lastErrno = tErrno; + } +#else rc = SQLITE_IOERR_UNLOCK; - pFile->lastErrno = errno; + pFile->lastErrno = errno; +#endif pInode->eFileLock = NO_LOCK; pFile->eFileLock = NO_LOCK; } @@ -1747,6 +2087,26 @@ static int unixUnlock(sqlite3_file *id, int eFileLock){ */ static int closeUnixFile(sqlite3_file *id){ unixFile *pFile = (unixFile*)id; +#if OSCLOSE_CHECK_CLOSE_IOERR + if( pFile->dirfd>=0 ){ + int err = close(pFile->dirfd); + if( err ){ + pFile->lastErrno = errno; + return SQLITE_IOERR_DIR_CLOSE; + }else{ + pFile->dirfd=-1; + } + } + if( pFile->h>=0 ){ + int err = close(pFile->h); + if( err ){ + pFile->lastErrno = errno; + return SQLITE_IOERR_CLOSE; + }else{ + pFile->h=-1; + } + } +#else if( pFile->dirfd>=0 ){ robust_close(pFile, pFile->dirfd, __LINE__); pFile->dirfd=-1; @@ -1755,6 +2115,7 @@ static int closeUnixFile(sqlite3_file *id){ robust_close(pFile, pFile->h, __LINE__); pFile->h = -1; } +#endif #if OS_VXWORKS if( pFile->pId ){ if( pFile->isDelete ){ @@ -1967,7 +2328,14 @@ static int dotlockLock(sqlite3_file *id, int eFileLock) { } return rc; } +#if OSCLOSE_CHECK_CLOSE_IOERR + if( close(fd) ){ + pFile->lastErrno = errno; + rc = SQLITE_IOERR_CLOSE; + } +#else robust_close(pFile, fd, __LINE__); +#endif /* got it, set the type and return ok */ pFile->eFileLock = eFileLock; @@ -2011,7 +2379,11 @@ static int dotlockUnlock(sqlite3_file *id, int eFileLock) { int rc = 0; int tErrno = errno; if( ENOENT != tErrno ){ +#if OSLOCKING_CHECK_BUSY_IOERR + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); +#else rc = SQLITE_IOERR_UNLOCK; +#endif } if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; @@ -2099,7 +2471,11 @@ static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){ if ( lrc ) { int tErrno = errno; /* unlock failed with an error */ +#if OSLOCKING_CHECK_BUSY_IOERR + lrc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); +#else lrc = SQLITE_IOERR_UNLOCK; +#endif if( IS_LOCK_ERROR(lrc) ){ pFile->lastErrno = tErrno; rc = lrc; @@ -3317,9 +3693,18 @@ static int unixSync(sqlite3_file *id, int flags){ /* return SQLITE_IOERR; */ } #endif - /* Only need to sync once, so close the directory when we are done */ + /* Only need to sync once, so close the directory when we are done */ +#if OSCLOSE_CHECK_CLOSE_IOERR + if( close(pFile->dirfd)==0 ){ + pFile->dirfd = -1; + }else{ + pFile->lastErrno = errno; + rc = SQLITE_IOERR_DIR_CLOSE; + } +#else robust_close(pFile, pFile->dirfd, __LINE__); pFile->dirfd = -1; +#endif } return rc; } @@ -3458,6 +3843,7 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){ #include static int getDbPathForUnixFile(unixFile *pFile, char *dbPath); #endif +static int isProxyLockingMode(unixFile *); /* ** Information and control of an open file handle. @@ -3499,212 +3885,258 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ #if (SQLITE_ENABLE_APPLE_SPI>0) && defined(__APPLE__) case SQLITE_TRUNCATE_DATABASE: { unixFile *pFile = (unixFile*)id; - int trc = SQLITE_OK; - int eFileLock = pFile->eFileLock; int rc = SQLITE_OK; - char jPath[MAXPATHLEN+9]; - size_t jLen; - - if( eFileLockpMethod->xLock(id, SQLITE_LOCK_SHARED); - } - if( !rc && eFileLockpMethod->xLock(id, SQLITE_LOCK_EXCLUSIVE); + void *pLock = NULL; + int flags = 0; + int corruptFileLock = 0; + int isCorrupt = 0; + +#if SQLITE_ENABLE_DATA_PROTECTION + flags |= pFile->protFlags; +#endif +#if SQLITE_ENABLE_LOCKING_STYLE + if( isProxyLockingMode(pFile) ){ + flags |= SQLITE_OPEN_AUTOPROXY; } +#endif + + rc = sqlite3demo_superlock(pFile->zPath, 0, flags, 0, 0, &pLock); if( rc ){ - if( pFile->eFileLock > eFileLock ){ - pFile->pMethod->xUnlock(id, eFileLock); + if( rc==SQLITE_CORRUPT || rc==SQLITE_NOTADB ){ + isCorrupt = 1; + rc = sqlite3demo_superlock_corrupt(id, SQLITE_LOCK_EXCLUSIVE, &corruptFileLock); + } + if( rc ){ + return rc; } - return rc; } rc = pFile->pMethod->xTruncate(id, ((pFile->fsFlags & SQLITE_FSFLAGS_IS_MSDOS) != 0) ? 1L : 0L); - if( !rc && (SQLITE_OK==getDbPathForUnixFile(pFile, jPath)) ){ - jLen = strlcat(jPath, "-journal", MAXPATHLEN+9); - if( jLen < MAXPATHLEN+9 ){ - int jfd = open(jPath, O_TRUNC); - if( (jfd == -1) ){ - if ( errno!=ENOENT ){ - perror(jPath); + + if( rc==SQLITE_OK ){ + char jPath[MAXPATHLEN+9]; + int zLen = strlcpy(jPath, pFile->zPath, MAXPATHLEN+9); + if( zLenpMethod->xSync(id, SQLITE_SYNC_FULL); - } - if( pFile->eFileLock > eFileLock ){ - int unlockRC = pFile->pMethod->xUnlock(id, SQLITE_LOCK_SHARED); - if (!rc) rc = unlockRC; + pFile->pMethod->xSync(id, SQLITE_SYNC_FULL); } - if( pFile->eFileLock > eFileLock ){ - int unlockRC = pFile->pMethod->xUnlock(id, SQLITE_LOCK_NONE); - if (!rc) rc = unlockRC; - } - if( trc ){ - return trc; + if( isCorrupt ){ + sqlite3demo_superunlock_corrupt(id, corruptFileLock); + }else{ + sqlite3demo_superunlock(pLock); } return rc; } case SQLITE_REPLACE_DATABASE: { unixFile *pFile = (unixFile*)id; - int trc = SQLITE_OK; - int eFileLock = pFile->eFileLock; - int rc = SQLITE_OK; - char jPath[MAXPATHLEN+9]; - size_t jLen; sqlite3 *srcdb = (sqlite3 *)pArg; Btree *pSrcBtree = NULL; - int eSrcFileLock = SQLITE_LOCK_NONE; - int srcLockRC = -1; sqlite3_file *src_file = NULL; unixFile *pSrcFile = NULL; - + char srcWalPath[MAXPATHLEN+5]; + int srcWalFD = -1; + int rc = SQLITE_OK; + void *pLock = NULL; + int flags = 0; + sqlite3 *srcdb2 = NULL; + copyfile_state_t s; + int corruptSrcFileLock = 0; + int corruptDstFileLock = 0; + int isSrcCorrupt = 0; + int isDstCorrupt = 0; + if( !sqlite3SafetyCheckOk(srcdb) ){ return SQLITE_MISUSE; } - if( eFileLockpMethod->xLock(id, SQLITE_LOCK_SHARED); - } - if( !rc && eFileLockpMethod->xLock(id, SQLITE_LOCK_EXCLUSIVE); + +#if SQLITE_ENABLE_DATA_PROTECTION + flags |= pFile->protFlags; +#endif +#if SQLITE_ENABLE_LOCKING_STYLE + if( isProxyLockingMode(pFile) ){ + flags |= SQLITE_OPEN_AUTOPROXY; } - if( !rc ){ - /* get the src file descriptor adhering to the db struct access rules - ** this code is modeled after sqlite3_file_control() in main.c - */ - sqlite3_mutex_enter(srcdb->mutex); - if( srcdb->nDb>0 ){ - pSrcBtree = srcdb->aDb[0].pBt; +#endif + + rc = sqlite3demo_superlock(pFile->zPath, 0, flags, 0, 0, &pLock); + if( rc ){ + if( rc==SQLITE_CORRUPT || rc==SQLITE_NOTADB ){ + isDstCorrupt = 1; + rc = sqlite3demo_superlock_corrupt(id, SQLITE_LOCK_EXCLUSIVE, &corruptDstFileLock); } - if( pSrcBtree ){ - Pager *pSrcPager; - sqlite3BtreeEnter(pSrcBtree); - pSrcPager = sqlite3BtreePager(pSrcBtree); - assert( pSrcPager!=0 ); - src_file = sqlite3PagerFile(pSrcPager); - assert( src_file!=0 ); - if( src_file->pMethods ){ - pSrcFile = (unixFile *)src_file; - eSrcFileLock = pSrcFile->eFileLock; - if( eSrcFileLockpMethod->xLock(src_file, SQLITE_LOCK_SHARED); - srcLockRC = rc; /* SQLITE_OK means we need to unlock later */ - } else if( eSrcFileLock==SQLITE_LOCK_EXCLUSIVE ){ - /* if the src database has an exclusive lock, verify that the - ** it doesn't have a journal file with open transactions - */ - if( getDbPathForUnixFile(pSrcFile, jPath) ){ - rc = SQLITE_INTERNAL; - }else{ - jLen = strlcat(jPath, "-journal", MAXPATHLEN+9); - if( jLen < MAXPATHLEN+9 ){ - int jfd = open(jPath, O_RDONLY); - if( jfd==-1 ){ - if( errno!=ENOENT ){ - pFile->lastErrno = errno; - rc = SQLITE_IOERR; - } - }else{ - /* if the journal exists ensure there's no pending - ** transaction by checking the journal header */ - char magic[8]; - ssize_t rlen = pread(jfd, magic, 8, 0); - if( rlen<0 ){ - pFile->lastErrno = errno; - rc = SQLITE_IOERR; - }else if( rlen==8 ){ - char test[8] = {'\0','\0','\0','\0','\0','\0','\0','\0'}; - if( memcmp(magic,test,8) ){ - rc = SQLITE_LOCKED; - } - }else if( rlen!=0 ){ - rc = SQLITE_INTERNAL; - } - close(jfd); - } - } - } - } - }else{ - rc = SQLITE_MISUSE; + if( rc ){ + return rc; + } + } + /* get the src file descriptor adhering to the db struct access rules + ** this code is modeled after sqlite3_file_control() in main.c + */ + sqlite3_mutex_enter(srcdb->mutex); + if( srcdb->nDb>0 ){ + pSrcBtree = srcdb->aDb[0].pBt; + } + if( pSrcBtree ){ + Pager *pSrcPager; + sqlite3BtreeEnter(pSrcBtree); + pSrcPager = sqlite3BtreePager(pSrcBtree); + assert( pSrcPager!=0 ); + src_file = sqlite3PagerFile(pSrcPager); + assert( src_file!=0 ); + if( src_file->pMethods ){ + int srcFlags = 0; + pSrcFile = (unixFile *)src_file; + /* wal mode db cannot be opened readonly */ + if ((pSrcFile->openFlags & O_RDWR) == O_RDWR) { + srcFlags = SQLITE_OPEN_READWRITE; + } else { + srcFlags = SQLITE_OPEN_READONLY; + } +#if SQLITE_ENABLE_DATA_PROTECTION + srcFlags |= pSrcFile->protFlags; +#endif +#if SQLITE_ENABLE_LOCKING_STYLE + if( isProxyLockingMode(pSrcFile) ){ + srcFlags |= SQLITE_OPEN_AUTOPROXY; } - if( rc ){ - if( srcLockRC==SQLITE_OK ){ - pSrcFile->pMethod->xUnlock(src_file, eSrcFileLock); +#endif + rc = sqlite3_open_v2(pSrcFile->zPath, &srcdb2, srcFlags, 0); + if( rc==SQLITE_OK ){ + /* start a deferred transaction and read to establish a read lock */ + rc = sqlite3_exec(srcdb2, "BEGIN DEFERRED; PRAGMA schema_version", 0, 0, 0); + if( rc==SQLITE_CORRUPT || rc==SQLITE_NOTADB ){ + isSrcCorrupt = 1; + rc = sqlite3demo_superlock_corrupt(src_file, SQLITE_LOCK_SHARED, &corruptSrcFileLock); } - sqlite3BtreeLeave(pSrcBtree); } } - if( pSrcFile==NULL || (pSrcFile->h<0) ){ - rc = SQLITE_INTERNAL; - sqlite3_mutex_leave(srcdb->mutex); - } } - if( rc ){ - /* unroll state changes and return error code */ - if( pFile->eFileLock > eFileLock ){ - pFile->pMethod->xUnlock(id, eFileLock); + if( !srcdb2 || pSrcFile==NULL || pSrcFile->h<0){ + rc = SQLITE_INTERNAL; + } + if( rc!=SQLITE_OK ){ + goto end_replace_database; + } + /* both databases are locked appropriately, copy the src wal journal if + ** one exists and then the actual database file + */ + strlcpy(srcWalPath, pSrcFile->zPath, MAXPATHLEN+5); + strlcat(srcWalPath, "-wal", MAXPATHLEN+5); + srcWalFD = open(srcWalPath, O_RDONLY); + if( !(srcWalFD<0) ){ + char dstWalPath[MAXPATHLEN+5]; + int dstWalFD = -1; + strlcpy(dstWalPath, pFile->zPath, MAXPATHLEN+5); + strlcat(dstWalPath, "-wal", MAXPATHLEN+5); + dstWalFD = open(dstWalPath, O_RDWR|O_CREAT, SQLITE_DEFAULT_FILE_PERMISSIONS); + if( !(dstWalFD<0) ){ + s = copyfile_state_alloc(); + lseek(srcWalFD, 0, SEEK_SET); + lseek(dstWalFD, 0, SEEK_SET); + if( fcopyfile(srcWalFD, dstWalFD, s, COPYFILE_ALL) ){ + int err=errno; + switch(err) { + case ENOMEM: + rc = SQLITE_NOMEM; + break; + default: + pFile->lastErrno = err; + rc = SQLITE_IOERR; + } + } + copyfile_state_free(s); + close(dstWalFD); } - return rc; - }else{ - /* both databases are locked appropriately, copy file data - ** and then unroll the locks we added. - */ - copyfile_state_t s; + close(srcWalFD); + } + if( rc==SQLITE_OK ){ + /* before we copy, ensure that the file change counter will be modified */ + uint32_t srcChange = 0; + uint32_t dstChange = 0; + pread(pSrcFile->h, &srcChange, 4, 24); + pread(pFile->h, &dstChange, 4, 24); + /* copy the actual database */ s = copyfile_state_alloc(); + lseek(pSrcFile->h, 0, SEEK_SET); + lseek(pFile->h, 0, SEEK_SET); if( fcopyfile(pSrcFile->h, pFile->h, s, COPYFILE_ALL) ){ - switch(errno) { + int err=errno; + switch(err) { case ENOMEM: rc = SQLITE_NOMEM; break; default: - rc = SQLITE_INTERNAL; + pFile->lastErrno = err; + rc = SQLITE_IOERR; } } copyfile_state_free(s); - if( srcLockRC==SQLITE_OK ){ - pSrcFile->pMethod->xUnlock(src_file, eSrcFileLock); + + if (srcChange == dstChange) { + /* modify the change counter to force page zero to be reloaded */ + dstChange ++; + pwrite(pFile->h, &dstChange, 4, 24); } - sqlite3BtreeLeave(pSrcBtree); - sqlite3_mutex_leave(srcdb->mutex); } - - if( !rc && (SQLITE_OK==getDbPathForUnixFile(pFile, jPath)) ){ - jLen = strlcat(jPath, "-journal", MAXPATHLEN+9); - if( jLen < MAXPATHLEN+9 ){ - int jfd = open(jPath, O_TRUNC); - if( (jfd == -1) ){ - if ( errno!=ENOENT ){ - perror(jPath); + if( isSrcCorrupt ){ + sqlite3demo_superunlock_corrupt(src_file, corruptSrcFileLock); + }else{ + /* done with the source db so end the transaction */ + sqlite3_exec(srcdb2, "COMMIT", 0, 0, 0); + } + /* zero out any old journal clutter */ + if( rc==SQLITE_OK ){ + char jPath[MAXPATHLEN+9]; + int zLen = strlcpy(jPath, pFile->zPath, MAXPATHLEN+9); + if( zLenpMethod->xSync(id, SQLITE_SYNC_FULL); + pFile->pMethod->xSync(id, SQLITE_SYNC_FULL); } - if( pFile->eFileLock > eFileLock ){ - int unlockRC = pFile->pMethod->xUnlock(id, SQLITE_LOCK_SHARED); - if (!rc) rc = unlockRC; - } - if( pFile->eFileLock > eFileLock ){ - int unlockRC = pFile->pMethod->xUnlock(id, SQLITE_LOCK_NONE); - if (!rc) rc = unlockRC; + + end_replace_database: + if( pSrcBtree ){ + sqlite3_close(srcdb2); + sqlite3BtreeLeave(pSrcBtree); } - if( trc ){ - return trc; + sqlite3_mutex_leave(srcdb->mutex); + if( isDstCorrupt ){ + sqlite3demo_superunlock_corrupt(id, corruptDstFileLock); + }else{ + sqlite3demo_superunlock(pLock); } return rc; } @@ -3924,7 +4356,6 @@ static void unixShmPurge(unixFile *pFd){ } } -static int isProxyLockingMode(unixFile *); static const char *proxySharedMemoryBasePath(unixFile *); /* @@ -4062,6 +4493,14 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ if( unixShmSystemLock(pShmNode, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){ if( robust_ftruncate(pShmNode->h, 0) ){ rc = unixLogError(SQLITE_IOERR_SHMOPEN, "ftruncate", zShmFilename); + }else{ + /* If running as root set the uid/gid of the shm file to match the database */ + uid_t euid = geteuid(); + if( euid==0 && (euid!=sStat.st_uid || getegid()!=sStat.st_gid) ){ + if( fchown(pShmNode->h, sStat.st_uid, sStat.st_gid) ){ + rc = SQLITE_IOERR_SHMOPEN; + } + } } } if( rc==SQLITE_OK ){ @@ -5081,7 +5520,9 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){ static int findCreateFileMode( const char *zPath, /* Path of file (possibly) being created */ int flags, /* Flags passed as 4th argument to xOpen() */ - mode_t *pMode /* OUT: Permissions to open file with */ + mode_t *pMode, /* OUT: Permissions to open file with */ + uid_t *pUid, /* OUT: uid to set on the file */ + gid_t *pGid /* OUT: gid to set on the file */ ){ int rc = SQLITE_OK; /* Return Code */ *pMode = SQLITE_DEFAULT_FILE_PERMISSIONS; @@ -5110,6 +5551,8 @@ static int findCreateFileMode( if( 0==stat(zDb, &sStat) ){ *pMode = sStat.st_mode & 0777; + *pUid = sStat.st_uid; + *pGid = sStat.st_gid; }else{ rc = SQLITE_IOERR_FSTAT; } @@ -5152,7 +5595,11 @@ static int unixOpen( int fd = -1; /* File descriptor returned by open() */ int dirfd = -1; /* Directory file descriptor */ int openFlags = 0; /* Flags to pass to open() */ +#if SQLITE_ENABLE_DATA_PROTECTION + int eType = flags&0xFF0FFF00; /* Type of file to open */ +#else int eType = flags&0xFFFFFF00; /* Type of file to open */ +#endif int noLock; /* True to omit locking primitives */ int rc = SQLITE_OK; /* Function Return Code */ @@ -5241,9 +5688,15 @@ static int unixOpen( if( isExclusive ) openFlags |= (O_EXCL|O_NOFOLLOW); openFlags |= (O_LARGEFILE|O_BINARY); +#if SQLITE_ENABLE_DATA_PROTECTION + p->protFlags = (flags & SQLITE_OPEN_FILEPROTECTION_MASK); +#endif + if( fd<0 ){ mode_t openMode; /* Permissions to create file with */ - rc = findCreateFileMode(zName, flags, &openMode); + uid_t uid; + gid_t gid; + rc = findCreateFileMode(zName, flags, &openMode, &uid, &gid); if( rc!=SQLITE_OK ){ assert( !p->pUnused ); assert( eType==SQLITE_OPEN_WAL || eType==SQLITE_OPEN_MAIN_JOURNAL ); @@ -5264,6 +5717,16 @@ static int unixOpen( rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zName); goto open_finished; } + /* if we're opening the wal or journal and running as root, set the journal uid/gid */ + if( !isReadonly && (flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL)) ){ + uid_t euid = geteuid(); + if( euid==0 && (euid!=uid || getegid()!=gid) ){ + if( fchown(fd, uid, gid) ){ + rc = SQLITE_CANTOPEN_BKPT; + goto open_finished; + } + } + } } assert( fd>=0 ); if( pOutFlags ){ @@ -5337,23 +5800,6 @@ static int unixOpen( if( envforce!=NULL ){ useProxy = atoi(envforce)>0; }else{ - struct statfs fsInfo; - if( statfs(zPath, &fsInfo) == -1 ){ - /* In theory, the close(fd) call is sub-optimal. If the file opened - ** with fd is a database file, and there are other connections open - ** on that file that are currently holding advisory locks on it, - ** then the call to close() will cancel those locks. In practice, - ** we're assuming that statfs() doesn't fail very often. At least - ** not while other file descriptors opened by the same process on - ** the same file are working. */ - p->lastErrno = errno; - if( dirfd>=0 ){ - robust_close(p, dirfd, __LINE__); - } - robust_close(p, fd, __LINE__); - rc = SQLITE_IOERR_ACCESS; - goto open_finished; - } useProxy = !(fsInfo.f_flags&MNT_LOCAL); } if( useProxy ){ @@ -5419,7 +5865,13 @@ static int unixDelete( { rc = unixLogError(SQLITE_IOERR_DIR_FSYNC, "fsync", zPath); } +#if OSCLOSE_CHECK_CLOSE_IOERR + if( close(fd)&&!rc ){ + rc = SQLITE_IOERR_DIR_CLOSE; + } +#else robust_close(0, fd, __LINE__); +#endif } } #endif @@ -5885,6 +6337,7 @@ struct proxyLockingContext { char *lockProxyPath; /* Name of the proxy lock file */ char *dbPath; /* Name of the open file */ int conchHeld; /* 1 if the conch is held, -1 if lockless */ + int nFails; /* Number of conch taking failures */ void *oldLockingContext; /* Original lockingcontext to restore on close */ sqlite3_io_methods const *pOldMethod; /* Original I/O methods for close */ }; @@ -6097,10 +6550,10 @@ extern int gethostuuid(uuid_t id, const struct timespec *wait); static int proxyGetHostID(unsigned char *pHostID, int *pError){ assert(PROXY_HOSTIDLEN == sizeof(uuid_t)); memset(pHostID, 0, PROXY_HOSTIDLEN); -#if defined(__MAX_OS_X_VERSION_MIN_REQUIRED)\ - && __MAC_OS_X_VERSION_MIN_REQUIRED<1050 +#if HAVE_GETHOSTUUID { - static const struct timespec timeout = {1, 0}; /* 1 sec timeout */ + struct timespec timeout = {1, 0}; /* 1 sec timeout */ + if( gethostuuid(pHostID, &timeout) ){ int err = errno; if( pError ){ @@ -6198,13 +6651,12 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; unixFile *conchFile = pCtx->conchFile; int rc = SQLITE_OK; - int nTries = 0; struct timespec conchModTime; do { rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, lockType); - nTries ++; if( rc==SQLITE_BUSY ){ + pCtx->nFails ++; /* If the lock failed (busy): * 1st try: get the mod time of the conch, wait 0.5s and try again. * 2nd try: fail if the mod time changed or host id is different, wait @@ -6217,19 +6669,19 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ return SQLITE_IOERR_LOCK; } - if( nTries==1 ){ + if( pCtx->nFails==1 ){ conchModTime = buf.st_mtimespec; usleep(500000); /* wait 0.5 sec and try the lock again*/ continue; } - assert( nTries>1 ); + assert( pCtx->nFails>1 ); if( conchModTime.tv_sec != buf.st_mtimespec.tv_sec || conchModTime.tv_nsec != buf.st_mtimespec.tv_nsec ){ return SQLITE_BUSY; } - if( nTries==2 ){ + if( pCtx->nFails==2 ){ char tBuf[PROXY_MAXCONCHLEN]; int len = osPread(conchFile->h, tBuf, PROXY_MAXCONCHLEN, 0); if( len<0 ){ @@ -6237,8 +6689,23 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ return SQLITE_IOERR_LOCK; } if( len>PROXY_PATHINDEX && tBuf[0]==(char)PROXY_CONCHVERSION){ - /* don't break the lock if the host id doesn't match */ + /* don't break the lock if the host id doesn't match, but do log + * an error to console so users can diagnose stale NFS locks more + * easily + */ if( 0!=memcmp(&tBuf[PROXY_HEADERLEN], myHostID, PROXY_HOSTIDLEN) ){ + uuid_t conchUUID; + uuid_string_t conchUUIDString; + uuid_string_t myUUIDString; + assert(PROXY_HOSTIDLEN == sizeof(uuid_t)); + memcpy(conchUUID, &tBuf[PROXY_HEADERLEN], PROXY_HOSTIDLEN); + uuid_unparse(conchUUID, conchUUIDString); + uuid_unparse(myHostID, myUUIDString); + fprintf(stderr, "ERROR: sqlite database is locked because it is in use " + "by another host that holds a host-exclusive lock on %s; " + "this host (UUID %s) cannot override the host-exclusive lock " + "until the other host (UUID %s) releases its locks on %s\n", + pFile->zPath, myUUIDString, conchUUIDString, conchFile->zPath); return SQLITE_BUSY; } }else{ @@ -6249,8 +6716,8 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ continue; } - assert( nTries==3 ); - if( 0==proxyBreakConchLock(pFile, myHostID) ){ + assert( pCtx->nFails>=3 ); + if( (pCtx->nFails==3)&&(0==proxyBreakConchLock(pFile, myHostID)) ){ rc = SQLITE_OK; if( lockType==EXCLUSIVE_LOCK ){ rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, SHARED_LOCK); @@ -6260,7 +6727,7 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ } } } - } while( rc==SQLITE_BUSY && nTries<3 ); + } while( rc==SQLITE_BUSY && pCtx->nFails<3 ); return rc; } @@ -6379,7 +6846,7 @@ static int proxyTakeConch(unixFile *pFile){ rc = proxyConchLock(pFile, myHostID, EXCLUSIVE_LOCK); } }else{ - rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, EXCLUSIVE_LOCK); + rc = proxyConchLock(pFile, myHostID, EXCLUSIVE_LOCK); } if( rc==SQLITE_OK ){ char writeBuffer[PROXY_MAXCONCHLEN]; @@ -6433,7 +6900,14 @@ static int proxyTakeConch(unixFile *pFile){ OSTRACE(("TRANSPROXY: CLOSE %d\n", pFile->h)); if( rc==SQLITE_OK && pFile->openFlags ){ if( pFile->h>=0 ){ +#if defined(STRICT_CLOSE_ERROR) && OSCLOSE_CHECK_CLOSE_IOERR + if( close(pFile->h) ){ + pFile->lastErrno = errno; + return SQLITE_IOERR_CLOSE; + } +#else robust_close(pFile, pFile->h, __LINE__); +#endif } pFile->h = -1; int fd = robust_open(pCtx->dbPath, pFile->openFlags, diff --git a/src/pager.c b/src/pager.c index 7ff9a9a000..22bc145468 100644 --- a/src/pager.c +++ b/src/pager.c @@ -2377,7 +2377,11 @@ static int pager_delmaster(Pager *pPager, const char *zMaster){ if( !pMaster ){ rc = SQLITE_NOMEM; }else{ - const int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MASTER_JOURNAL); + const int flags = +#if SQLITE_ENABLE_DATA_PROTECTION + (pPager->vfsFlags&SQLITE_OPEN_FILEPROTECTION_MASK)| +#endif + (SQLITE_OPEN_READONLY|SQLITE_OPEN_MASTER_JOURNAL); rc = sqlite3OsOpen(pVfs, zMaster, pMaster, flags, 0); } if( rc!=SQLITE_OK ) goto delmaster_out; @@ -2413,7 +2417,11 @@ static int pager_delmaster(Pager *pPager, const char *zMaster){ ** so, return without deleting the master journal file. */ int c; - int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL); + int flags = +#if SQLITE_ENABLE_DATA_PROTECTION + (pPager->vfsFlags&SQLITE_OPEN_FILEPROTECTION_MASK)| +#endif + (SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL); rc = sqlite3OsOpen(pVfs, zJournal, pJournal, flags, 0); if( rc!=SQLITE_OK ){ goto delmaster_out; @@ -3398,7 +3406,11 @@ static int pagerOpentemp( sqlite3_opentemp_count++; /* Used for testing and analysis only */ #endif - vfsFlags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | + vfsFlags |= +#if SQLITE_ENABLE_DATA_PROTECTION + (pPager->vfsFlags&SQLITE_OPEN_FILEPROTECTION_MASK)| +#endif + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE; rc = sqlite3OsOpen(pPager->pVfs, 0, pFile, vfsFlags, 0); assert( rc!=SQLITE_OK || isOpen(pFile) ); @@ -4527,7 +4539,11 @@ int sqlite3PagerOpen( pPager->noSync = pPager->tempFile; pPager->fullSync = pPager->noSync ?0:1; pPager->syncFlags = pPager->noSync ? 0 : SQLITE_SYNC_NORMAL; +#if SQLITE_DEFAULT_CKPTFULLFSYNC + pPager->ckptSyncFlags = pPager->noSync ? 0 : SQLITE_SYNC_FULL; +#else pPager->ckptSyncFlags = pPager->syncFlags; +#endif /* pPager->pFirst = 0; */ /* pPager->pFirstSynced = 0; */ /* pPager->pLast = 0; */ @@ -4638,7 +4654,11 @@ static int hasHotJournal(Pager *pPager, int *pExists){ ** it can be ignored. */ if( !jrnlOpen ){ - int f = SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL; + int f = +#if SQLITE_ENABLE_DATA_PROTECTION + (pPager->vfsFlags&SQLITE_OPEN_FILEPROTECTION_MASK)| +#endif + SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL; rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &f); } if( rc==SQLITE_OK ){ @@ -4776,7 +4796,11 @@ int sqlite3PagerSharedLock(Pager *pPager){ pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &bExists); if( rc==SQLITE_OK && bExists ){ int fout = 0; - int f = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_JOURNAL; + int f = +#if SQLITE_ENABLE_DATA_PROTECTION + (pPager->vfsFlags&SQLITE_OPEN_FILEPROTECTION_MASK)| +#endif + SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_JOURNAL; assert( !pPager->tempFile ); rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &fout); assert( rc!=SQLITE_OK || isOpen(pPager->jfd) ); @@ -5152,6 +5176,9 @@ static int pager_open_journal(Pager *pPager){ sqlite3MemJournalOpen(pPager->jfd); }else{ const int flags = /* VFS flags to open journal file */ +#if SQLITE_ENABLE_DATA_PROTECTION + (pPager->vfsFlags&SQLITE_OPEN_FILEPROTECTION_MASK)| +#endif SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE| (pPager->tempFile ? (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL): @@ -6710,10 +6737,14 @@ static int pagerOpenWal(Pager *pPager){ ** (e.g. due to malloc() failure), return an error code. */ if( rc==SQLITE_OK ){ - rc = sqlite3WalOpen(pPager->pVfs, - pPager->fd, pPager->zWal, pPager->exclusiveMode, - pPager->journalSizeLimit, &pPager->pWal - ); +#if SQLITE_ENABLE_DATA_PROTECTION + rc = sqlite3WalOpen(pPager->pVfs, pPager->fd, pPager->zWal, pPager->exclusiveMode, + pPager->journalSizeLimit, (pPager->vfsFlags & SQLITE_OPEN_FILEPROTECTION_MASK), + &pPager->pWal); +#else + rc = sqlite3WalOpen(pPager->pVfs, pPager->fd, pPager->zWal, pPager->exclusiveMode, + pPager->journalSizeLimit, 0, &pPager->pWal); +#endif } return rc; diff --git a/src/pragma.c b/src/pragma.c index c36707a2f2..a4eb404499 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -441,10 +441,10 @@ void sqlite3Pragma( if( pId2->n==0 && b>=0 ){ int ii; for(ii=0; iinDb; ii++){ - sqlite3BtreeSecureDelete(db->aDb[ii].pBt, b); + sqlite3BtreeSecureDelete(db->aDb[ii].pBt, (int)b); } } - b = sqlite3BtreeSecureDelete(pBt, b); + b = (int)sqlite3BtreeSecureDelete(pBt, (int)b); returnSingleInt(pParse, "secure_delete", &b); }else @@ -564,6 +564,14 @@ void sqlite3Pragma( iDb = 0; pId2->n = 1; } +#ifdef SQLITE_DEFAULT_WAL_SAFETYLEVEL + if (eMode == PAGER_JOURNALMODE_WAL) { + /* when entering wal mode, immediately switch the safety_level + * so that a query to pragma synchronous returns the correct value + */ + pDb->safety_level = SQLITE_DEFAULT_WAL_SAFETYLEVEL; + } +#endif /* SQLITE_DEFAULT_WAL_SAFETYLEVEL */ for(ii=db->nDb-1; ii>=0; ii--){ if( db->aDb[ii].pBt && (ii==iDb || pId2->n==0) ){ sqlite3VdbeUsesBtree(v, ii); diff --git a/src/select.c b/src/select.c index 8dcfb84b8a..983a805c52 100644 --- a/src/select.c +++ b/src/select.c @@ -4371,6 +4371,9 @@ select_end: } #if defined(SQLITE_DEBUG) +void sqlite3PrintExpr(Expr *p); +void sqlite3PrintExprList(ExprList *pList); +void sqlite3PrintSelect(Select *p, int indent); /* ******************************************************************************* ** The following code is used for testing and debugging only. The code diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 2e6ca2b74a..e5d805aa88 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -64,6 +64,9 @@ extern "C" { */ #define SQLITE_DEPRECATED #define SQLITE_EXPERIMENTAL +#ifndef __OSX_AVAILABLE_BUT_DEPRECATED +#define __OSX_AVAILABLE_BUT_DEPRECATED(MacOSAvailable, MacOSDeprecated, iPhoneOSAvailable, iPhoneOSDeprecated) +#endif /* ** Ensure these symbols were not defined by some previous header file. @@ -483,6 +486,7 @@ int sqlite3_exec( #define SQLITE_OPEN_SHAREDCACHE 0x00020000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_PRIVATECACHE 0x00040000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_WAL 0x00080000 /* VFS only */ +#define SQLITE_OPEN_FILEPROTECTION_MASK 0x00700000 /* Reserved: 0x00F00000 */ @@ -4455,9 +4459,14 @@ void *sqlite3_update_hook( ** future releases of SQLite. Applications that care about shared ** cache setting should set it explicitly. ** +** ^Note: This method is deprecated on MacOS X 10.7 and iOS version 5.0 +** and will always return SQLITE_MISUSE, instead of calling this function +** shared cache mode should be enabled per-database connection via +** sqlite3_open_v2 with SQLITE_OPEN_SHAREDCACHE instead. +** ** See Also: [SQLite Shared-Cache Mode] */ -int sqlite3_enable_shared_cache(int); +int sqlite3_enable_shared_cache(int) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_0, __MAC_10_7, __IPHONE_2_0, __IPHONE_5_0); /* ** CAPI3REF: Attempt To Free Heap Memory diff --git a/src/sqlite3_private.h b/src/sqlite3_private.h index 907da81822..5d39495ee8 100644 --- a/src/sqlite3_private.h +++ b/src/sqlite3_private.h @@ -5,6 +5,28 @@ #ifndef _SQLITE3_PRIVATE_H #define _SQLITE3_PRIVATE_H +#define SQLITE_LOCKSTATE_OFF 0 +#define SQLITE_LOCKSTATE_ON 1 +#define SQLITE_LOCKSTATE_NOTADB 2 +#define SQLITE_LOCKSTATE_ERROR -1 + +#define SQLITE_LOCKSTATE_ANYPID -1 + +/* +** Test a file path for sqlite locks held by a process ID (-1 = any PID). +** Returns one of the following integer codes: +** +** SQLITE_LOCKSTATE_OFF no active sqlite file locks match the specified pid +** SQLITE_LOCKSTATE_ON active sqlite file locks match the specified pid +** SQLITE_LOCKSTATE_NOTADB path points to a file that is not an sqlite db file +** SQLITE_LOCKSTATE_ERROR path was not vaild or was unreadable +** +** There is no support for identifying db files encrypted via SEE encryption +** currently. Zero byte files are tested for sqlite locks, but if no sqlite +** locks are present then SQLITE_LOCKSTATE_NOTADB is returned. +*/ +extern int _sqlite3_lockstate(const char *path, pid_t pid); + /* ** Pass the SQLITE_TRUNCATE_DATABASE operation code to sqlite3_file_control() ** to truncate a database and its associated journal file to zero length. diff --git a/src/test1.c b/src/test1.c index 6171e980bc..dc5f5d2de4 100644 --- a/src/test1.c +++ b/src/test1.c @@ -4968,6 +4968,83 @@ static int file_control_lasterrno_test( return TCL_OK; } +#ifdef __APPLE__ +/* From sqlite3_priavet.h */ +# ifndef SQLITE_TRUNCATE_DATABASE +# define SQLITE_TRUNCATE_DATABASE 101 +# endif +# ifndef SQLITE_REPLACE_DATABASE +# define SQLITE_REPLACE_DATABASE 102 +# endif + +/* +** tclcmd: file_control_truncate_test DB +** +** This TCL command runs the sqlite3_file_control interface and +** verifies correct operation of the SQLITE_TRUNCATE_DATABASE verb. +*/ +static int file_control_truncate_test( + ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[] /* Command arguments */ +){ + sqlite3 *db; + int rc; + + if( objc!=2 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", + Tcl_GetStringFromObj(objv[0], 0), " DB", 0); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){ + return TCL_ERROR; + } + rc = sqlite3_file_control(db, NULL, SQLITE_TRUNCATE_DATABASE, 0); + if( rc ){ + Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); + return TCL_ERROR; + } + return TCL_OK; +} + +/* +** tclcmd: file_control_replace_test DB +** +** This TCL command runs the sqlite3_file_control interface and +** verifies correct operation of the SQLITE_REPLACE_DATABASE verb. +*/ +static int file_control_replace_test( + ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[] /* Command arguments */ +){ + int iArg = 0; + sqlite3 *src_db; + sqlite3 *dst_db; + int rc; + + if( objc!=3 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", + Tcl_GetStringFromObj(objv[0], 0), " DST_DB SRC_DB", 0); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &dst_db) ){ + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[2]), &src_db) ){ + return TCL_ERROR; + } + rc = sqlite3_file_control(dst_db, NULL, SQLITE_REPLACE_DATABASE, src_db); + if( rc ){ + Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); + return TCL_ERROR; + } + return TCL_OK; +} +#endif /* __APPLE__ */ + /* ** tclcmd: file_control_chunksize_test DB DBNAME SIZE ** @@ -5911,6 +5988,10 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ { "file_control_test", file_control_test, 0 }, { "file_control_lasterrno_test", file_control_lasterrno_test, 0 }, { "file_control_lockproxy_test", file_control_lockproxy_test, 0 }, +#ifdef __APPLE__ + { "file_control_truncate_test", file_control_truncate_test, 0 }, + { "file_control_replace_test", file_control_replace_test, 0 }, +#endif { "file_control_chunksize_test", file_control_chunksize_test, 0 }, { "file_control_sizehint_test", file_control_sizehint_test, 0 }, { "sqlite3_vfs_list", vfs_list, 0 }, diff --git a/src/test_config.c b/src/test_config.c index 466f84f5df..51c9215b18 100644 --- a/src/test_config.c +++ b/src/test_config.c @@ -435,7 +435,17 @@ Tcl_SetVar2(interp, "sqlite_options", "long_double", #else Tcl_SetVar2(interp,"sqlite_options","prefer_proxy_locking","0",TCL_GLOBAL_ONLY); #endif - +#if SQLITE_DEFAULT_CKPTFULLFSYNC + Tcl_SetVar2(interp,"sqlite_options","default_ckptfullfsync","1",TCL_GLOBAL_ONLY); +#else + Tcl_SetVar2(interp,"sqlite_options","default_ckptfullfsync","0",TCL_GLOBAL_ONLY); +#endif +#if SQLITE_DEFAULT_WAL_SAFETYLEVEL + Tcl_SetVar2(interp,"sqlite_options","default_wal_safetylevel", + STRINGVALUE(SQLITE_DEFAULT_WAL_SAFETYLEVEL),TCL_GLOBAL_ONLY); +#else + Tcl_SetVar2(interp,"sqlite_options","default_wal_safetylevel","0",TCL_GLOBAL_ONLY); +#endif #ifdef SQLITE_OMIT_SHARED_CACHE Tcl_SetVar2(interp, "sqlite_options", "shared_cache", "0", TCL_GLOBAL_ONLY); diff --git a/src/test_rtree.c b/src/test_rtree.c index 5fc994ddf2..be1f16bc28 100644 --- a/src/test_rtree.c +++ b/src/test_rtree.c @@ -13,7 +13,7 @@ ** is not included in the SQLite library. */ -#include +#include "sqlite3.h" /* Solely for the UNUSED_PARAMETER() macro. */ #include "sqliteInt.h" diff --git a/src/test_superlock.c b/src/test_superlock.c index 936fcad0c5..fe08b1b125 100644 --- a/src/test_superlock.c +++ b/src/test_superlock.c @@ -18,7 +18,7 @@ ** sqlite3demo_superunlock() */ -#include +#include "sqlite3.h" #include /* memset(), strlen() */ #include /* assert() */ @@ -148,7 +148,7 @@ static int superlockWalLock( ** this function must have been obtained from a successful call to ** sqlite3demo_superlock(). */ -void sqlite3demo_superunlock(void *pLock){ +static void sqlite3demo_superunlock(void *pLock){ Superlock *p = (Superlock *)pLock; if( p->bWal ){ int rc; /* Return code */ @@ -179,9 +179,10 @@ void sqlite3demo_superunlock(void *pLock){ ** until either the lock can be obtained or the busy-handler function returns ** 0 (indicating "give up"). */ -int sqlite3demo_superlock( +static int sqlite3demo_superlock( const char *zPath, /* Path to database file to lock */ const char *zVfs, /* VFS to use to access database file */ + int flags, /* Additional flags to pass to sqlite3_open_v2 */ int (*xBusy)(void*,int), /* Busy handler callback */ void *pBusyArg, /* Context arg for busy handler */ void **ppLock /* OUT: Context to pass to superunlock() */ @@ -196,7 +197,7 @@ int sqlite3demo_superlock( /* Open a database handle on the file to superlock. */ rc = sqlite3_open_v2( - zPath, &pLock->db, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, zVfs + zPath, &pLock->db, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|flags, zVfs ); /* Install a busy-handler and execute a BEGIN EXCLUSIVE. If this is not @@ -331,7 +332,7 @@ static int superlock_cmd( xBusy = superlock_busy; } - rc = sqlite3demo_superlock(zPath, zVfs, xBusy, &busy, &pLock); + rc = sqlite3demo_superlock(zPath, zVfs, 0, xBusy, &busy, &pLock); assert( rc==SQLITE_OK || pLock==0 ); assert( rc!=SQLITE_OK || pLock!=0 ); diff --git a/src/vdbetrace.c b/src/vdbetrace.c index de123b550f..c2a2052537 100644 --- a/src/vdbetrace.c +++ b/src/vdbetrace.c @@ -83,9 +83,12 @@ char *sqlite3VdbeExpandSql( if( db->vdbeExecCnt>1 ){ while( *zRawSql ){ const char *zStart = zRawSql; + sqlite_int64 iStart = SQLITE_PTR_TO_INT(zRawSql); + sqlite_int64 iCurrent; while( *(zRawSql++)!='\n' && *zRawSql ); sqlite3StrAccumAppend(&out, "-- ", 3); - sqlite3StrAccumAppend(&out, zStart, (int)(zRawSql-zStart)); + iCurrent = SQLITE_PTR_TO_INT(zRawSql); + sqlite3StrAccumAppend(&out, zStart, (int)(iCurrent-iStart)); } }else{ while( zRawSql[0] ){ diff --git a/src/wal.c b/src/wal.c index b9a03dff23..74334e9688 100644 --- a/src/wal.c +++ b/src/wal.c @@ -1247,11 +1247,12 @@ int sqlite3WalOpen( const char *zWalName, /* Name of the WAL file */ int bNoShm, /* True to run in heap-memory mode */ i64 mxWalSize, /* Truncate WAL to this size on reset */ + int flags, /* VFS file protection flags */ Wal **ppWal /* OUT: Allocated Wal handle */ ){ int rc; /* Return Code */ Wal *pRet; /* Object to allocate and return */ - int flags; /* Flags passed to OsOpen() */ + int vfsFlags; /* Flags passed to OsOpen() */ assert( zWalName && zWalName[0] ); assert( pDbFd ); @@ -1284,9 +1285,9 @@ int sqlite3WalOpen( pRet->exclusiveMode = (bNoShm ? WAL_HEAPMEMORY_MODE: WAL_NORMAL_MODE); /* Open file handle on the write-ahead log file. */ - flags = (SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_WAL); - rc = sqlite3OsOpen(pVfs, zWalName, pRet->pWalFd, flags, &flags); - if( rc==SQLITE_OK && flags&SQLITE_OPEN_READONLY ){ + vfsFlags = flags | (SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_WAL); + rc = sqlite3OsOpen(pVfs, zWalName, pRet->pWalFd, vfsFlags, &vfsFlags); + if( rc==SQLITE_OK && vfsFlags&SQLITE_OPEN_READONLY ){ pRet->readOnly = WAL_RDONLY; } diff --git a/src/wal.h b/src/wal.h index a62b23bbdc..526ea2b117 100644 --- a/src/wal.h +++ b/src/wal.h @@ -47,7 +47,7 @@ typedef struct Wal Wal; /* Open and close a connection to a write-ahead log. */ -int sqlite3WalOpen(sqlite3_vfs*, sqlite3_file*, const char *, int, i64, Wal**); +int sqlite3WalOpen(sqlite3_vfs*, sqlite3_file*, const char *, int, i64, int, Wal**); int sqlite3WalClose(Wal *pWal, int sync_flags, int, u8 *); /* Set the limiting size of a WAL file. */ diff --git a/test/fallocate.test b/test/fallocate.test index f3f2f6ac50..3fb50fb534 100644 --- a/test/fallocate.test +++ b/test/fallocate.test @@ -81,6 +81,7 @@ if {![wal_is_ok]} { set skipwaltests 1 } if {!$skipwaltests} { db close file delete -force test.db + if {[forced_proxy_locking]} { file delete -force .test.db-conch } sqlite3 db test.db file_control_chunksize_test db main [expr 32*1024] diff --git a/test/incrblob3.test b/test/incrblob3.test index 4c49f1563c..98ca38878b 100644 --- a/test/incrblob3.test +++ b/test/incrblob3.test @@ -260,12 +260,17 @@ proc access_method {args} { return "" } -do_test incrblob3-7.2 { - sqlite3 db test.db - sqlite3_db_config_lookaside db 0 0 0 - list [catch {db incrblob blobs v 1} msg] $msg -} {1 {database schema has changed}} -db close +#set sqlite_os_trace 1 +# AFP asserts because the "db incrblob blobs v 1" clears the file locks and the unlock fails (HFS doesn't care about a failed unlock) +if {[path_is_local "."]} { + + do_test incrblob3-7.2 { + sqlite3 db test.db + sqlite3_db_config_lookaside db 0 0 0 + list [catch {db incrblob blobs v 1} msg] $msg + } {1 {database schema has changed}} + db close +} tvfs delete finish_test diff --git a/test/multiplex.test b/test/multiplex.test index 9278e84274..dc3bd66b9e 100644 --- a/test/multiplex.test +++ b/test/multiplex.test @@ -14,6 +14,12 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/malloc_common.tcl +# AFP doesn't like multiplex db tests +if { ![path_is_local "."] } { + finish_test + return +} + set g_chunk_size [ expr ($::SQLITE_MAX_PAGE_SIZE*16384) ] set g_max_chunks 32 diff --git a/test/pager1.test b/test/pager1.test index 136ca05f46..5c5f8677c7 100644 --- a/test/pager1.test +++ b/test/pager1.test @@ -534,6 +534,8 @@ proc copy_on_mj_delete {method filename args} { } set pwd [pwd] +if {![forced_proxy_locking]} { + # proxy locking uses can't deal with auto proxy file paths longer than MAXPATHLEN foreach {tn1 tcl} { 1 { set prefix "test.db" } 2 { @@ -665,6 +667,7 @@ foreach {tn1 tcl} { db close tv delete file delete -force $dirname +} # Set up a VFS to make a copy of the file-system just before deleting a @@ -2008,8 +2011,8 @@ do_test pager1-22.2.1 { set ::synccount 0 sqlite3 db test.db execsql { - PRAGMA synchronous = off; PRAGMA journal_mode = WAL; + PRAGMA synchronous = off; INSERT INTO ko DEFAULT VALUES; } execsql { PRAGMA wal_checkpoint } diff --git a/test/pragma.test b/test/pragma.test index f2ef3a069d..d974d6ea23 100644 --- a/test/pragma.test +++ b/test/pragma.test @@ -1348,7 +1348,7 @@ if !$skip_lock_proxy_tests { } db2 } {1 {database is locked}} - set lpp3 [exec mktemp -t 'proxy3'] + set lpp3 [exec mktemp -t "proxy3"] # lock proxy file can be renamed if no other connections are active do_test pragma-16.4 { @@ -1404,7 +1404,7 @@ if !$skip_lock_proxy_tests { } {1 {database is locked}} db2 close - set lpp4 [exec mktemp -t 'proxy4'] + set lpp4 [exec mktemp -t "proxy4"] # check that db is unlocked after first host connection closes do_test pragma-16.8.1 { @@ -1433,9 +1433,9 @@ if !$skip_lock_proxy_tests { } {1} # ensure creating directories for a lock proxy file works - set lpp5 [exec mktemp -d -t "proxy5"]/sub/dir/lock + set lpp5d [exec mktemp -d -t "proxy5"] + set lpp5 $lpp5d/sub/dir/lock db close - file delete -force proxytest do_test pragma-16.10.1 { sqlite3 db proxytest.db execsql "PRAGMA lock_proxy_file='$lpp5'" @@ -1447,7 +1447,7 @@ if !$skip_lock_proxy_tests { # ensure that after deleting the path, setting ":auto:" works correctly db close - file delete -force proxytest + file delete -force $lpp5d do_test pragma-16.10.2 { sqlite3 db proxytest.db set lockpath3 [execsql { @@ -1461,8 +1461,8 @@ if !$skip_lock_proxy_tests { # ensure that if the path can not be created (file instead of dir) # setting :auto: deals with it by creating a new autonamed lock file db close - file delete -force proxytest - close [open "proxytest" a] + file delete -force $lpp5d + close [open "$lpp5d" a] do_test pragma-16.10.3 { sqlite3 db proxytest.db set lockpath2 [execsql { @@ -1474,9 +1474,9 @@ if !$skip_lock_proxy_tests { } {1} # make sure we can deal with ugly file paths correctly - set lpp6 [exec mktemp -d -t "proxy6"]/./././////./proxytest/../proxytest/sub/dir/lock db close - file delete -force proxytest + file delete -force $lpp5d + set lpp6 [exec mktemp -d -t "proxy6"]/./././////./proxytest/../proxytest/sub/dir/lock do_test pragma-16.10.4 { sqlite3 db proxytest.db execsql "PRAGMA lock_proxy_file='$lpp6'" @@ -1489,7 +1489,7 @@ if !$skip_lock_proxy_tests { # ensure that if the path can not be created (perm), setting :auto: deals db close - file delete -force proxytest + file delete -force $lpp5d do_test pragma-16.10.5 { sqlite3 db proxytest.db execsql "PRAGMA lock_proxy_file='$lpp5'" @@ -1497,9 +1497,9 @@ if !$skip_lock_proxy_tests { create table if not exists bb(bb); } db close - file delete -force proxytest - file mkdir proxytest - file attributes proxytest -permission 0000 + file delete -force $lpp5d + file mkdir $lpp5d + file attributes $lpp5d -permission 0000 sqlite3 db proxytest.db set lockpath5 [execsql { PRAGMA lock_proxy_file=":auto:"; @@ -1521,8 +1521,8 @@ if !$skip_lock_proxy_tests { } {1 {database is locked}} db close - file attributes proxytest -permission 0777 - file delete -force proxytest + file attributes $lpp5d -permission 0777 + file delete -force $lpp5d set env(SQLITE_FORCE_PROXY_LOCKING) $using_proxy set sqlite_hostid_num 0 diff --git a/test/superlock.test b/test/superlock.test index b12863f7ff..7fc8cf5fdf 100644 --- a/test/superlock.test +++ b/test/superlock.test @@ -52,6 +52,12 @@ do_test 1.2 { sqlite3demo_superlock unlock test.db } {unlock} do_catchsql_test 1.3 { SELECT * FROM t1 } {1 {database is locked}} do_test 1.4 { unlock } {} +ifcapable !wal {finish_test ; return } +if { ![wal_is_ok] } { + finish_test + return +} + do_execsql_test 2.1 { INSERT INTO t1 VALUES(3, 4); PRAGMA journal_mode = WAL; diff --git a/test/tester.tcl b/test/tester.tcl index 391382f4f4..40eb2e2633 100644 --- a/test/tester.tcl +++ b/test/tester.tcl @@ -242,6 +242,23 @@ if {[info exists cmdlinearg]==0} { # sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit) +proc forced_proxy_locking {} { + if $::sqlite_options(lock_proxy_pragmas)&&$::sqlite_options(prefer_proxy_locking) { + set force_proxy_value 0 + set force_key "SQLITE_FORCE_PROXY_LOCKING=" + foreach {env_pair} [exec env] { + if { [string first $force_key $env_pair] == 0} { + set force_proxy_value [string range $env_pair [string length $force_key] end] + } + } + if { "$force_proxy_value " == "1 " } { + return 1 + } + } + return 0 +} + + # Create a test database # proc reset_db {} { @@ -249,6 +266,14 @@ proc reset_db {} { file delete -force test.db file delete -force test.db-journal file delete -force test.db-wal + if {[forced_proxy_locking]} { + sqlite3 db ./test.db + set lock_proxy_path [db eval "PRAGMA lock_proxy_file;"] + catch {db close} + # puts "deleting $lock_proxy_path" + file delete -force $lock_proxy_path + file delete -force test.db + } sqlite3 db ./test.db set ::DB [sqlite3_connection_pointer db] if {[info exists ::SETUP_SQL]} { @@ -1334,22 +1359,6 @@ proc presql {} { set presql } -proc forced_proxy_locking {} { - ifcapable lock_proxy_pragmas&&prefer_proxy_locking { - set force_proxy_value 0 - set force_key "SQLITE_FORCE_PROXY_LOCKING=" - foreach {env_pair} [exec env] { - if { [string first $force_key $env_pair] == 0} { - set force_proxy_value [string range $env_pair [string length $force_key] end] - } - } - if { "$force_proxy_value " == "1 " } { - return 1 - } - } - return 0 -} - proc wal_is_ok {} { if { [forced_proxy_locking] } { return 1 diff --git a/test/wal2.test b/test/wal2.test index d3657dec9b..ad69b39380 100644 --- a/test/wal2.test +++ b/test/wal2.test @@ -1183,6 +1183,11 @@ foreach {tn sql reslist} { 2 { PRAGMA checkpoint_fullfsync = 1 } {8 4 3 2 5 2} 3 { PRAGMA checkpoint_fullfsync = 0 } {8 0 3 0 5 0} } { + if { $::sqlite_options(default_ckptfullfsync) && $tn == 1} { + # checkpoint_fullfsync on by default + set reslist {8 4 3 2 5 2} + } + faultsim_delete_and_reopen execsql {PRAGMA auto_vacuum = 0} diff --git a/test/wal3.test b/test/wal3.test index 99da0c98aa..13303e2c4d 100644 --- a/test/wal3.test +++ b/test/wal3.test @@ -207,6 +207,16 @@ foreach {tn syncmode synccount} { {test.db-wal normal test.db-wal normal test.db-wal normal test.db normal} } { + if { $::sqlite_options(default_ckptfullfsync) } { + # checkpoint_fullfsync on by default + if { $tn == 2} { + set synccount {test.db-wal full test.db full} + } + if { $tn == 3} { + set synccount {test.db-wal normal test.db-wal normal test.db-wal full test.db full} + } + } + proc sync_counter {args} { foreach {method filename id flags} $args break lappend ::syncs [file tail $filename] $flags diff --git a/test/wal6.test b/test/wal6.test index 6fae48e9ea..2db4d5d1e4 100644 --- a/test/wal6.test +++ b/test/wal6.test @@ -20,6 +20,8 @@ source $testdir/wal_common.tcl source $testdir/malloc_common.tcl ifcapable !wal {finish_test ; return } +if { ![wal_is_ok] } {finish_test ; return } + #------------------------------------------------------------------------- # Changing to WAL mode in one connection forces the change in others. # diff --git a/test/walcrash2.test b/test/walcrash2.test index 60265de485..0ad48ec6b5 100644 --- a/test/walcrash2.test +++ b/test/walcrash2.test @@ -16,10 +16,7 @@ source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/wal_common.tcl ifcapable !wal {finish_test ; return } -if { ![wal_is_ok] } { - finish_test - return -} +if { ![wal_is_ok] } { finish_test ; return } #------------------------------------------------------------------------- diff --git a/test/walro.test b/test/walro.test index 60bdce9e04..4dbfa08840 100644 --- a/test/walro.test +++ b/test/walro.test @@ -24,6 +24,13 @@ if {$::tcl_platform(platform) != "unix"} { return } +# these tests can't deal with the location of the -shm file under proxy locking +# +if {[forced_proxy_locking]} { + finish_test + return +} + do_multiclient_test tn { # Do not run tests with the connections in the same process. #