Compare commits

...

236 Commits

Author SHA1 Message Date
drh 4ceada8d03 Merge 3.37.2 changes into the begin-concurrent-pnu branch.
FossilOrigin-Name: e525892d3dcdd9914000b169ddab2cf651d7f9bcd5ff4d8a74f591e017149985
2022-01-07 14:09:33 +00:00
drh eecb3e6b16 Version 3.37.2
FossilOrigin-Name: 872ba256cbf61d9290b571c0e6d82a20c224ca3ad82971edc46b29818d5d17a0
2022-01-06 13:25:41 +00:00
drh 7bc0d3862a Remove two NEVER() macros that can sometimes be true if the database is
corrupt.  dbsqlfuzz 0414d2c18290fc80fd5fb540def7d3e46c1ae9c6.

FossilOrigin-Name: e1871201e73c1d970f0434d3c7190da2c1c49ded3a359640d959ec0c0450b8ed
2022-01-05 21:08:48 +00:00
drh 2300c5a646 Increase the version number to 3.37.2
FossilOrigin-Name: 7b911356af8f985eefdb190e1205933eaa0bef35e5d755b279514e7b96f9d0c4
2022-01-05 18:56:59 +00:00
drh dde17957b7 Fix an assert() failure that could follow an OOM when coding a RETURNING trigger. dbsqlfuzz case 5d3e2438f15dc32b473d9f29413157857efa1212.
FossilOrigin-Name: 93965e67e2478fc99d7abfd6790edac353358373873bb6ac82c7c65dbe52a5de
2022-01-05 16:02:23 +00:00
drh cf6b0e27d2 Improved handling of OOM errors in sqlite3ExpandReturning().
dbsqlfuzz 1040b720f0bbc3bdcfe7336acffbf71517e3ef82.

FossilOrigin-Name: b9ed2a2921a8f3f07a3a2c9b954806df924b3806795ab876bbbda28578175288
2022-01-05 12:01:34 +00:00
drh 36d39bcc48 Do not open a rollback journal file when the journal_mode is OFF, even if
such a file exists on disk. See
[forum/forumpost/ec2a102440|forum post ec2a102440] for a description.  I so
far have been unable to find any harm to come of the problem, other than the
assertion fault when in DEBUG mode.

FossilOrigin-Name: 71f21f172cbc033f70604d166f17118472491fcf8b78350bd347de016132b464
2022-01-02 19:43:58 +00:00
drh 024806ffb6 Clear the cache of triggers used to implement CASCADE foreign key constraints
whenever the schema changes.  Fix for the problem identified by
[forum:/forumpost/2831335356|forum post 2831335356].

FossilOrigin-Name: 4f1313c67f8c3025ed15793e370617d14e950c13217f179bfb01f8f48fbbead9
2022-01-02 18:10:48 +00:00
drh c84cb56f81 Earlier detection of corruption in sqlite3BtreeDelete(). Fix for
the assertion fault reported by
[forum:/forumpost/9d78389221|forum post 9d78389221].

FossilOrigin-Name: eb07219965721e0a2017d0462747b07178c23f51e3333036e0cb8f5b4751f404
2022-01-02 17:46:49 +00:00
drh 2f2b676b7b Do not raise an SQLITE_SCHEMA error if in sqlite3Init(). Fix for PoC #2 in
[forum:/forumpost/b03d86f951|forum post b03d86f951].  See TH3 for test
cases.

FossilOrigin-Name: 25beca31cf3dbad0e78085c5ceed797a461cfaddc2d41fb7a093aa27098e50d5
2022-01-02 17:37:56 +00:00
drh 2718c7dec7 Fix an obscure problem with releasing savepoints stored in an in-memory journal that could cause subsequent savepoint rollback to fail.
FossilOrigin-Name: 3c0806e4df3ba795962b0111236b2b6f27f63a35a02055c251f750ef02622803
2022-01-02 17:24:29 +00:00
drh 4a6393e96e Remove an obsolete assert() statement that no longer does anything useful
and which is not always true.  Fix for PoC #1 of
[forum:/forumpost/b03d86f951|forum post b03d86f951].

FossilOrigin-Name: 0829c1001bcfadf0d0af2b2c2e01ed60b24156b7c4e7c49cb29f3002ce8e36db
2022-01-02 17:14:39 +00:00
drh 48b9d17680 Fix a faulty assert() statement - adding a CORRUPT_DB term - based on a test
case derived from [562805cf488a455c].  Also add a test case to that prior
issue.

FossilOrigin-Name: f9505fcb28d05ac2319a56d936f840b8de502a49dda16ded8b7ed9dd9dffe509
2022-01-02 17:13:36 +00:00
drh 9dba2cc038 Fix harmless compiler warnings seen with MSVC.
FossilOrigin-Name: 6e08a542ad6af7032e5badcdd50a8ed95c07bf7b15ae2b62334376b75a2e9731
2022-01-02 17:02:54 +00:00
drh adb21d4482 Move the pTriggerPrg and pCleanup elements of the Parse object up into the
section of that object that is persisted across calls to sqlite3NestedParse().
This fixes a memory leak
[forum:/info/24bd1fef7e9323ef|reported in forum post 24bd1fef7e9323ef].

FossilOrigin-Name: ee8b2397a67c14badb836570f815ca6c59f6936100669adafe815d7cb997cb43
2022-01-02 17:00:19 +00:00
drh 57acbb1380 Version 3.37.1
FossilOrigin-Name: 378629bf2ea546f73eee84063c5358439a12f7300e433f18c9e1bddd948dea62
2021-12-30 15:30:28 +00:00
drh f74840dadb Disable the atoi1.test test module on non-x64 hardware.
FossilOrigin-Name: 986a94d036f029539c3bb3873096f4a85ee26fc674f56856c57ca3dfd3bd0f37
2021-12-30 12:26:16 +00:00
drh 2e4f1fd0f8 Fix windowC tests to specify little-endian or big-endian UTF16, for cross-platform portability. [forum post 559b84a5c6|forum:/forumpost/559b84a5c6]. And
give the correct prefix to tests in the windowC.test module.

FossilOrigin-Name: 8af7cb2a3e7ca778695a7a44359537457bf57dc8bdad9b2b4d20a73385c028d8
2021-12-30 11:35:38 +00:00
drh b484ae62b8 Do not attempt to reprepare a prepared statement that returns SQLITE_SCHEMA
if there was an OOM.  This enhancement supercedes check-in
[fee469925231d074].

FossilOrigin-Name: 103f95db3e17d7d021b489c160b84e66c630b4f13516be11d10fffe2b42599fa
2021-12-30 11:27:53 +00:00
drh be531827a6 Change an assert() in FTS3 into assert_fts3_nc(), since it is a harmless
condition that can occur when processing a corrupt database file.

FossilOrigin-Name: 35e2b27af7242a3f74b31e0e25a0ea892f6aec66341b9194c0f5957f9d1926bc
2021-12-30 11:13:44 +00:00
drh bbefc9e821 Fix a minor issue in the configure script.
FossilOrigin-Name: 0413d37e327525d7747d9f9643eb73197a83d9e810bcdc3e0cd35762a94ba22b
2021-12-30 03:56:30 +00:00
drh 7771ab2192 Following a prior error, an ALWAYS() in sqlite3ExprCanBeNull() might be false.
dbsqlfuzz 5dbec6678a20e7595a34dfdd869a3b9722b3ca43.

FossilOrigin-Name: b33e47709a2eb56b543ee3a34af201cd05e316bde8dcf45cebcf4e157d728508
2021-12-30 03:22:21 +00:00
drh fe553b671c When a table has an INTEGER PRIMARY KEY ON CONFLICT REPLACE and some other
uniqueness constraint, and it participates in an upsert on that other
constraint, ensure that code that checks for conflicts on the INTEGER PRIMARY
KEY is well-formed.  Fix for the problem reported by
[forum:/forumpost/06b16b8b29f8c8c3|forum post 06b16b8b29f8c8c3].

FossilOrigin-Name: 1f1e0b4e6d3a63fecaef8fafc8374ff17cff6d7af4c03994fa245e9953ee42cb
2021-12-30 03:12:37 +00:00
drh 3d25b94a13 Fix an OOB read that could occur in fts5 when processing corrupt records.
FossilOrigin-Name: 734606ea1a42c2e31ea62c114f2480b52182114b6bd87eb2f1145a171daad80f
2021-12-06 19:03:56 +00:00
drh b8b877b3dd Do not allow SQLITE_LIMIT_LENGTH to be set lower than 1 as an
SQLITE_LIMIT_LENGTH of 0 causes lots of unnecessary problems for
users of the sqlite3_str object.

FossilOrigin-Name: 286243f3a49f721b2ed5b278738e9edd8ca0f190abdbec645866bce97fcc7d04
2021-12-06 16:56:06 +00:00
drh 5b6692dddd Do not allow certain dangerous ".testctrl" commands in safe mode.
FossilOrigin-Name: 4602644f858eb3b55fec0ead1de58de1f60cb85165cbb309bfe6bb136099a895
2021-12-06 16:51:03 +00:00
drh 7956fbb7b6 Fix SQLITE_TESTCTRL_IMPOSTER so that it is a harmless no-op if the schema
parameter is invalid.

FossilOrigin-Name: b15c553c2ca3143c269e25f41e1a4e6c00f72a89e4b0a382260620567f159081
2021-12-06 16:42:31 +00:00
drh 764d1809ce In the shell tool, avoid modifying internal data structures until after the arguments to ".open" have been parsed.
FossilOrigin-Name: cbd69ed8afd3facda39fe8c81a3e8e4a1928923bcfd93e99d1bb8b3868ef3e5b
2021-12-06 16:36:37 +00:00
drh 5aa46f9bd6 Start a new branch on which to cherrypick fixes against 3.37.0.
FossilOrigin-Name: cea9e41aaee9b43496909fa2ba80dddaee1dd77d44bcb4d8d38034d1b003d762
2021-12-06 16:29:53 +00:00
drh bfc3e65a04 Merge version 3.37.0 into the begin-concurrent-pnu branch.
FossilOrigin-Name: 910001f28368896c3e6f11bb6ced3ec3171dcafefd9e6702106b32f3c451e229
2021-11-27 14:48:36 +00:00
drh 5d0ce656d4 Merge the latest trunk enhancements into the begin-concurrent-pnu branch.
FossilOrigin-Name: aa045e7b09ee47fb281579178add904fffc2299b05cfc67cf872ec3caaffd69b
2021-11-22 16:27:23 +00:00
drh 3c3a5d4ada Merge all recent trunk enhancements into the begin-concurrent-pnu branch.
FossilOrigin-Name: b9802d0b479ee910b7f50ea7995776040ec183ecbdc44fc00086154fb819ef12
2021-11-09 15:26:28 +00:00
drh fc9f56c453 Merge trunk enhancements into begin-concurrent-pnu branch.
FossilOrigin-Name: 4d972744c3d76254540837f0339731f4e62601cee924320aa3f3c0de7ee97ff3
2021-10-21 14:27:00 +00:00
drh 9826456ab0 Merge recent trunk enhancements into begin-concurrent-pnu branch.
FossilOrigin-Name: 35ad7bc6e0b31d838a80451300123e160c4a9367a3ea600f01e515a8135563ef
2021-10-04 12:09:44 +00:00
drh 945e5d112e Merge recent trunk enhancements into the begin-concurrent-pnu branch.
FossilOrigin-Name: 514b3cef6b7403cde90dcc926d67355ad6340b79b6a1b06004d97bebfc4eaced
2021-08-09 19:33:21 +00:00
drh adcbc76bb6 Merge recent trunk changes into the begin-concurrent-pnu branch.
FossilOrigin-Name: 2c25f25b22fe8e9c0da8a6ff5f2f117bd1716900c03acc8ed3ee8fce2286e15b
2021-08-03 16:49:31 +00:00
drh f0629994ab Merge recent trunk enhancements into the begin-concurrent-pnu branch.
FossilOrigin-Name: 229107b196abe5ab458ce84361d4b1e40d9d077effab7ebe9caac475fb96f7c0
2021-06-17 17:30:18 +00:00
dan ab8ad8dde3 Update this branch with latest changes from trunk.
FossilOrigin-Name: 74b42754d075a91e8f7d9067a3fcd5c60af03b520984fb3f8bb0c51faa376857
2021-06-14 13:49:11 +00:00
drh 7a5d37f540 Merge recent trunk changes into the begin-concurrent-pnu branch.
FossilOrigin-Name: a8d05f8e92b13bb5c150c1e3e7cf861d15bc7d50149ebb83646a2376cbfbd52b
2021-05-21 16:17:42 +00:00
dan 5b2b13c8c2 Merge latest trunk changes into this branch.
FossilOrigin-Name: 1f3f7e4bf3f16eeb68261c06b14201e445a8544d54505cef57a24d7ab0e93191
2021-04-07 17:32:39 +00:00
dan b6c8623045 Merge latest trunk changes into this branch.
FossilOrigin-Name: a7e612ee8fd942cf766e5304912161ee0b76ea21f32c7b7a72e048b73ed2a9e4
2021-04-02 20:40:52 +00:00
drh cb2ec5c3fd Merge version 3.35.3 changes into the begin-concurrent-pnu branch.
FossilOrigin-Name: 7e4c48afcc662940afe3ad976fcd75d71150e66461ad2d09c103eb0e238674a3
2021-03-26 13:38:36 +00:00
drh 05c3446396 Merge the 3.35.0 release into the begin-concurrent-pnu branch.
FossilOrigin-Name: a0a9dd52a7b514a8b919d67650178c76167cdbb6e23523222c3df709953b0f25
2021-03-12 16:24:43 +00:00
dan 6bfb2e062e Merge latest trunk changes with this branch.
FossilOrigin-Name: eb3101364faabb49d180f74c795e4f1430541937fd19ce95205618c98324c797
2021-03-11 19:08:44 +00:00
drh fb9d9cc7c4 Merge recent trunk enhancemenets into the begin-concurrent-pnu branch.
FossilOrigin-Name: 59d3ec295a6e640886cdccd49bd4522af29b37220fb9c99e8e39552ea2cbcdcb
2021-03-03 19:25:45 +00:00
drh 612beb5e38 Merge the latest trunk enhancements into the begin-concurrent-pnu branch.
FossilOrigin-Name: daf04650213c39804370924f6a724c816c154c33a3b2ae306bb5ed5fb14a855b
2020-12-14 16:50:45 +00:00
dan a240942122 Merge latest changes from trunk into this branch.
FossilOrigin-Name: a42e6f34f560d19df7e3835718c697ea19f905c3d2ee1bbd83ed5f2a5c36d965
2020-11-26 17:36:42 +00:00
dan 5e9d4c221d Restore the "PRAGMA noop_update" feature that was broken by [2b3241cf67c8eea7].
FossilOrigin-Name: 5fa4cbcb785125e72c7fd5cc2050637f2a2e854ee22aee05e8f09f751cd36291
2020-08-31 20:27:44 +00:00
drh 23dbdf125c Merge recent trunk fixes into the begin-concurrent-pnu branch.
FossilOrigin-Name: 2ae78c993e6a4ffd9ac1c8aaadf4976eacb5486e23c228988e9d3f379ed801c3
2020-08-10 21:30:17 +00:00
dan bd14b600fa Merge latest trunk change into this branch.
FossilOrigin-Name: 2b3241cf67c8eea761760ac27df7a136bcca2dca10c152a36b216c5dc88cdb53
2020-07-30 19:37:38 +00:00
dan 3dc850321e Allow BEGIN CONCURRENT transactions to contain schema modifications. However, this causes them to conflict with all other transactions.
FossilOrigin-Name: e9393a18cb987d258fff56f80ad6b1525f124fb19e8e4a9c953b86a57ef9a7e6
2020-07-16 20:39:59 +00:00
drh 5a344ef67c Merge versoin 3.32.1 into the begin-concurrent-pnu branch.
FossilOrigin-Name: e8d79d2bae50d7443ea6b7274ca36ded4f64e0f540494651d705612474f9aeb1
2020-05-25 18:03:34 +00:00
drh e3e03777c7 Merge the 3.32.0 release into the begin-concurrent-pnu branch.
FossilOrigin-Name: 79663e66129f9522c4c2287d095129c7989f5bee41f2073f7c2c60ca5739f2f6
2020-05-22 18:17:06 +00:00
drh 27fab1c045 Merge recent changes from trunk.
FossilOrigin-Name: c69f030b1ed9bddcc9ea79fe2361f39901bf1461ba3efdb32ddabcaa44319b2a
2020-05-18 16:19:59 +00:00
drh cd35fbc9ca Merge recent trunk enhancements into the begin-concurrent-pnu branch.
FossilOrigin-Name: cedd138c749b777de0479a9e57aa9fe4f21cb99660abdc78c14e61afdef4f2f3
2020-04-09 18:44:15 +00:00
dan f17089eb01 Fix a typo causing SQLITE_NOOP_UPDATE builds to fail.
FossilOrigin-Name: 1e2109fd339cfb3b906673479672ed2e5a0dc17a94210f364c6fc1a657a62ae8
2020-03-13 07:23:20 +00:00
dan ab18639258 Merge latest trunk changes, including the SQLITE_ENABLE_CORRUPT_PGNO patch, into this branch.
FossilOrigin-Name: 23bc80cc8681762e42376e5d878fe60ccc9a6533b7d017e54280996ea363c3f5
2020-02-04 20:09:45 +00:00
drh d4a1c953a1 Merge version 3.31.0
FossilOrigin-Name: e65e6c01d632beec9a21758cce6f7ac2e3bc2c5fac88b3bb393b9040fb2cbea6
2020-01-22 21:06:17 +00:00
drh 93b95c6c84 Merge all recent enhancements and fixes from trunk.
FossilOrigin-Name: 110a081f12ec83870bd42efe69363ca6c10d7d550c950008b40848f030919ecb
2020-01-15 13:48:19 +00:00
drh c697613beb Bring the begin-concurrent-pnu branch up-to-date with version 3.30.0.
FossilOrigin-Name: a4d26bcfb44a827d610d383b42a142ec807bb31700b9ba0842d9caaa47e8d9c6
2019-10-04 16:08:18 +00:00
drh f5078e74dd Merge version 3.30.0 beta 1 changes from trunk.
FossilOrigin-Name: ac4b64426822b8857acd6d4a97dffa9ce74ed85902a0f0e5c896ff2ec839a9d4
2019-09-30 16:43:38 +00:00
drh 47f677d8d7 Merge recent trunk enhancements into the begin-concurrent-pnu branch.
FossilOrigin-Name: 54930b7e0a585d0db3633673f33cac28cb257633224d6436ae29abdccbab1032
2019-09-16 12:17:11 +00:00
drh 3b5a4942f1 Merge the 3.29.0 changes into the begin-concurrent-pnu branch.
FossilOrigin-Name: c89949ccd102a180bf6254aef825bd0c29494d450a64bd1c3e0d347cb3dfad66
2019-07-10 18:21:47 +00:00
drh 42f5c182f6 Merge enhancements from trunk into the begin-concurrent-pnu branch.
FossilOrigin-Name: 05aa93afdbf9ae3569c827b24973b6c0af4c03b5faecb4283ba571f8bc64f0e9
2019-07-08 21:12:38 +00:00
drh 2bbacc1cf3 Merge the latest trunk enhancements into begin-concurrent-pnu.
FossilOrigin-Name: e70878b363db8ed0ed8aa345342a4174d54a8d95330b1bffc2843f0b07332526
2019-05-14 21:51:57 +00:00
drh 7773fffb18 Merge the latest enhancements from trunk.
FossilOrigin-Name: 6caaaf21c930b91636127480b6e06f6fc86b1a2e34c96780b64cd16e6e4c2d81
2019-04-15 15:07:11 +00:00
drh bfaa3dbd61 Bring this branch into closer alignment with begin-concurrent.
FossilOrigin-Name: 6433d366175c5be586a0e11b540e76a2515466b358332c4356388f4c449ec0d7
2019-03-26 13:28:15 +00:00
drh 841efe0ca1 Merge the latest trunk changes.
FossilOrigin-Name: 51e3e835490a12dca6359ca02b8cbf31091fc4376b265995a986d002d376fad9
2019-03-26 12:16:24 +00:00
drh 7e1f717b09 Merge all the latest enhancements from trunk.
FossilOrigin-Name: 667cce3dce39487e970c4eb43efd1bae7efed7ac6ee31030c582bfafa846dcce
2019-03-08 15:30:48 +00:00
dan db6fe08914 Merge latest begin-concurrent changes into this branch.
FossilOrigin-Name: a93ca38b432e0c9fb2e00499cfc88d09f59f27f908f377d8ae99b6717e548a60
2018-12-18 17:47:53 +00:00
dan b031157128 Merge latest trunk changes into this branch.
FossilOrigin-Name: 123cbb3312917ad5b3c32556547c5b7e8ba4e2d2def8651ff80f9fc1bdc1875c
2018-12-18 17:20:01 +00:00
drh ef180caded Merge fixes from trunk.
FossilOrigin-Name: f2083ee410b7504e8478f7373e76ded913e3a6a92cb7345b8c1ac27554f6edf8
2018-12-06 03:13:02 +00:00
drh 9c1eb40d69 Merge bug fixes from trunk.
FossilOrigin-Name: 1e13aaa29fb6324b60b3758bdab0491fdef9727e0de603d3da3e1885b52c5caa
2018-12-06 02:08:05 +00:00
drh 9ee5641ae0 Merge ALTER TABLE and sqlite3_normalized_sql() bug fixes from trunk.
FossilOrigin-Name: 3793e5d5d60f909480c83bc2a6473d27e414d69b0c0e3289413c9bfd2f15bb08
2018-12-06 00:08:48 +00:00
drh 254fa054ec Merge ALTER TABLE and sqlite3_normalized_sql() fixes from trunk.
FossilOrigin-Name: acf10b3f8d6675ddf787f7db55a9ff0ec5729adb6fa70b48b13d2cd71862cffd
2018-12-06 00:05:18 +00:00
drh a693b000df Merge enhancements from trunk, especially the sqlite3_normalized_sql()
enhancement.

FossilOrigin-Name: 342c9538d9c6a993ac0acaa6f74ad58886bcef7bb53783d053f9b24c131aec5d
2018-12-05 13:49:23 +00:00
drh f8d7432aa3 Merge enhancements from trunk, especially the enhanced
sqlite3_normalized_sql() interface.

FossilOrigin-Name: 47b73f6bfee8c5e41c408fb70dff0e4596c0a3eb0aeba40ad232a6cb1fe75532
2018-12-05 13:44:19 +00:00
dan 50232dd821 Fix a problem with SQLITE_ENABLE_EXPENSIVE_ASSERT builds on this branch.
FossilOrigin-Name: ddb4a6fbf8619db058b5eb8fcd687084ed4b65a6f69810357e324158257a911f
2018-12-04 13:51:43 +00:00
dan 834c48c279 Minor change to wal.c on this branch to make it more similar to trunk.
FossilOrigin-Name: 6a7af3ead5949c461430c1fa92798dc2bbbc58c8cd504005c5afa38993f0be82
2018-12-03 20:38:15 +00:00
dan 51883dfc8b Cherrypick a couple of fixes from begin-concurrent-pnu into this branch. The
differences between the two branches are now that this one does not have
"PRAGMA noop_update" or the mutex-free PRNG.

FossilOrigin-Name: a56506b9387a067ef259504d127694ad20223f4b08781d1676ff7f5fdd9443d8
2018-12-03 19:29:37 +00:00
drh 15e8543ecd Merge in all changes for release 3.26.0.
FossilOrigin-Name: 85fd92c71fd5b29bd45bb1717a5be761600fc23bed12eaff9eed708701a0fdf7
2018-12-03 18:24:31 +00:00
drh a63585eb8e Bring up to date with version 3.26.0.
FossilOrigin-Name: f0ddb358cc68e5ec6d9e758893ab3da058a3b2e705124a7449279c992e672a4a
2018-12-03 18:15:52 +00:00
dan 9b5c67f784 Merge the mutex-free PRNG change into this branch.
FossilOrigin-Name: 81e626f4a44be75425cf916ec61b6b36df0907ebac4adbf6786f87ad4f3a0674
2018-11-30 16:26:39 +00:00
dan 8e90c85bfb Merge latest begin-concurrent changes into this branch.
FossilOrigin-Name: 76608f750ab13c0a165def9672759fee43cf4e9895df3bfa21765e08358b07a0
2018-11-26 07:34:33 +00:00
dan dcb3992c66 Merge latest trunk changes into this branch.
FossilOrigin-Name: 28a615a2e0f48b0fee3eaf7841ff902e069fa6c221df6ad9a57b8709c88561fb
2018-11-26 07:21:06 +00:00
dan 0131a914d3 Merge latest begin-concurrent changes into this branch.
FossilOrigin-Name: 6f3dd9809fdef7d6195f1f93428a662d5a8c01dba9815daa22d1b94059a2eb43
2018-09-28 21:11:12 +00:00
dan 2dd93c8838 Merge latest trunk into this branch.
FossilOrigin-Name: 86750c927cb5ba36acad33d5279629699c42b598e70fdc4887b40a1a16771ff6
2018-09-28 20:58:10 +00:00
dan c1b7fdd43e Merge latest begin-concurrent changes with this branch.
FossilOrigin-Name: d33527d2231db9eda53124d3e11582a6b2eefa994eb7d9435fb349d3d8522ed7
2018-07-12 19:39:45 +00:00
dan fccd5d43a9 Merge latest trunk changes into this branch.
FossilOrigin-Name: 6a00a34e198408d69d638a25763f64b8a5beec61a60affb4d344426a2e232d72
2018-07-12 19:28:43 +00:00
dan ec6e332f01 Merge begin-concurrent changes into this branch.
FossilOrigin-Name: af17432eb135895f9a55115c0eb364745715bb2dc14da01cbb2dc498e61c1853
2018-07-10 15:48:39 +00:00
dan eb30704cc9 Merge latest trunk changes into this branch.
FossilOrigin-Name: e9a3e8642e7acc8e63b35662ffae3a5a0da8d4f1ecefcb1db3110be19cbdab2b
2018-07-10 15:45:54 +00:00
drh de26c16ea1 Merge the 3.24.0 changes plus a few subsequent enhancements.
FossilOrigin-Name: be7004a971a174ae5c862cb3fa29f3586882d9ed1630d3e50e686d7310ccbc91
2018-06-06 17:12:46 +00:00
drh 0c1fa5c8c6 Merge changes from trunk, including all the 3.24.0 changes plus some
later enhancements.

FossilOrigin-Name: d7299bfeb1eb031d6106b46139715047fe759d27b34b617ca43f2cd377b8188e
2018-06-06 17:03:53 +00:00
dan 6ccceeb3bd Merge latest trunk changes into this branch.
FossilOrigin-Name: 72f39efa9b1b97a54fe35d005b48f7e8b57d6285ba42f36487f796e09c710958
2018-05-15 11:55:15 +00:00
dan 4921c159bd Merge latest trunk changes with this branch.
FossilOrigin-Name: ae86cf60b6648a7ec789e233c9b0cc826efbbb0f301140b4848dc84d691ccd4f
2018-05-15 11:45:47 +00:00
dan 1d735ad3bb Instead of just the flags byte, include the first 8 bytes of the relevant page
in an on-commit conflict log message.

FossilOrigin-Name: 52e443eb5523963a6b09be66ab1c7281930d0155bf3df13eee0ec9066dbc7f0b
2018-05-15 11:33:42 +00:00
dan b7ee5667c2 Instead of just the flags byte, include the first 8 bytes of the relevant page
in an on-commit conflict log message.

FossilOrigin-Name: e7dc03e7439f224e4bec458f27fe364f2fb1ac637328657a578ada693ae22842
2018-05-15 11:28:36 +00:00
dan a233f09330 Include the value of the "flags" byte of the relevant page in the log message
emitted when a BEGIN CONCURRENT commit conflict is detected.

FossilOrigin-Name: de19abb950e4186c491c39112a52062871ac44fc2bdc5b611e9fdfa611352716
2018-05-15 09:03:57 +00:00
dan bfbe2b8c80 Include the value of the "flags" byte of the relevant page in the log message
emitted when a BEGIN CONCURRENT commit conflict is detected.

FossilOrigin-Name: fbfa547177ac7d57dcb0775d761a7f92fc1f2263cd83e2843cd109139a8c5121
2018-05-15 08:51:05 +00:00
dan 886fac36ba Merge latest changes from trunk. Including the "ORDER BY ... LIMIT" optimization.
FossilOrigin-Name: 83a4c9f0885768ed3cbe556aed9f4709a1d7a1cd52f2e3632ade28091febee47
2018-04-26 18:01:48 +00:00
dan 4af5f425f2 Merge latest changes from trunk. Including the "ORDER BY ... LIMIT"
optimization.

FossilOrigin-Name: d8ae7ba083551f05b963f13c5164ff60257c7b615cd48d675027cb6289b463da
2018-04-26 17:54:11 +00:00
dan 8ba5d0d4f7 Update this branch with latest changes from trunk.
FossilOrigin-Name: 2d8d13e4a169cbfa957c4e978b559817a13927a85e30fe275129bf7e3fb54920
2018-04-24 19:22:49 +00:00
dan 51abf049c7 Merge latest trunk changes into this branch.
FossilOrigin-Name: b27bd799ea66748b89223044c536813d56d0aa513f077499cbf8ed9c974d7b01
2018-04-24 19:21:38 +00:00
drh ed432167dc Merge all recent trunk enchancements, and especially the autoincrement
write reduction fix.

FossilOrigin-Name: 257900494f9aac7e4181b65d0eddc9cf2e9a52dc794ad68284f085d1c402addb
2018-03-20 14:00:25 +00:00
drh cdbf6d01a4 Remove debugging puts from concurrrent3.test.
FossilOrigin-Name: 13b4975681f249831a22562d1c26958f841ea77ca779858b0bee735dd30b710e
2018-03-20 13:54:22 +00:00
drh 3a5fc00808 Merge all recent enhancements from trunk.
FossilOrigin-Name: b0c2f760a637ee973f4dcc27308eec44950e6d0a9c5ab5c828c1210c1f868efa
2018-03-20 13:52:42 +00:00
dan eb816a7547 Merge latest changes from begin-concurrent into this branch.
FossilOrigin-Name: 8ade94ba6712827c672ddad8436f8b97cc520ced122e7d0043ad3c3ba94f1ed6
2018-03-02 18:26:50 +00:00
dan 908803f5e0 Update this branch with recent checkpoint related changes from trunk.
FossilOrigin-Name: fb6b7938601505186c0b1f0df6c45630039027f85ff394d290bc2c86b16a3a07
2018-03-02 17:59:37 +00:00
dan 699bd8121e Merge latest trunk changes into this branch.
FossilOrigin-Name: 36801effa9ec67b551f58972e21794466420f10cd0420701fcd87695e6cd11ee
2018-03-02 17:40:23 +00:00
dan c7a82772dc Add extra code to log details when corruption is detected in the pointer-map
structure maintained by the b-tree layer in begin-concurrent transactions.

FossilOrigin-Name: 570233716032f258b878d52c4d5a47e07292d66fa84e3a85c0388ec15efee625
2018-02-20 21:00:45 +00:00
dan 5d9c916150 Fix problem causing free-list corruption when merging free-lists for two
concurrent transactions that have both used page X as an in-memory free-list
trunk page, where X lies past the end of the initial database images.

FossilOrigin-Name: dc0fc2aa7cbefeb5f0ba8c992fd3e9adcfb5a4d61e2321c1bd93f4d36ba9aafc
2018-01-04 18:36:39 +00:00
dan 55fba4f05c Fix a spurious SQLITE_CORRUPT error that could occur within a COMMIT of a
concurrent transaction.

FossilOrigin-Name: 50c8952c92b9f0c61935fb0df04ed1426d9e266a812071b7bf5b0215c5552757
2018-01-02 19:57:26 +00:00
dan 02bc6f8fb9 Merge latest begin-concurrent changes into this branch.
FossilOrigin-Name: 3fde0b4d05c249b9d2a54dd721185202c353cee23c0351b634cac349dc0a7b14
2017-12-12 18:17:09 +00:00
drh cb0267185c Abort on an invalid paramater to sqlite3BitvecSet().
FossilOrigin-Name: 163c870950f386f6b0bb1ff9b3886cf95ba0deed414cae75baf87621ed3528c2
2017-12-11 14:02:10 +00:00
dan 165ba4a2a5 Merge changes from trunk. This fixes the SQLITE_ENABLE_UPDATE_DELETE_LIMIT functionality so that it works with views and WITHOUT ROWID tables.
FossilOrigin-Name: d90e5f346bcf7adab26ca8dad9dfbd0fbb86604a15f2fe827f11b3faab036750
2017-11-14 20:06:15 +00:00
dan 8706485d1a Merge latest trunk changes into this branch.
FossilOrigin-Name: 7f217edab4575554f657d38e2a1bc6b3f577998fdbecb04eb200aeb8b8406581
2017-11-06 20:02:08 +00:00
dan c0f69fcec7 Allow "BEGIN CONCURRENT" transactions to modify the temp schema.
FossilOrigin-Name: 0fb6d91cea347384fc081ce4c79582b365801dd4f56f5bf2ed40922bbfca0344
2017-11-06 10:04:45 +00:00
drh 4d13729861 Fix the SQLITE_NoopUpdate #define so that it occurs under the correct
conditions.

FossilOrigin-Name: bdf791f9f7f7ab4b1871e1d3a0d5edcad46fc67d7336ae7f3f7b20e69801be8e
2017-11-01 19:30:41 +00:00
drh 56391f2769 The "PRAGMA noop_update" command now requires SQLITE_ENABLE_NOOP_UPDATE
and no longer requires SQLITE_DEBUG.

FossilOrigin-Name: 81baf67c4493468e4feb2f4990bf82d59804ce4f3149252c0e1e8c43f90d6bc1
2017-11-01 18:48:54 +00:00
dan 7393c7424e Cherrypick [ec37ad6d08] into this branch. With this patch, if
SQLITE_SHARED_MAPPING is defined at build-time SQLite will use a single memory
mapping for multiple connections to the same database file within a single
process.

FossilOrigin-Name: c7a5880d6d898299b4c9414b7702cfa450aa5f7bf4ec8f417b94d2a7b6558264
2017-09-22 11:09:09 +00:00
dan 3eba5d00b6 Merge latest trunk changes into this branch.
FossilOrigin-Name: 307b802e8627c93a51e4c54851a4fab33db5061bb80e3d327ce53b127d6d511b
2017-09-22 10:49:03 +00:00
drh 03d405f046 Add the highly-experimental "PRAGMA noop_update=TRUE" command.
FossilOrigin-Name: afe45271b9c0cd379cf0beb94657e2396068c4a18f84003c4c48297760fd83ee
2017-09-12 20:09:31 +00:00
drh b25057c291 Merge recent enhancements from trunk.
FossilOrigin-Name: d53108e76307fb24ef1d8f75fe82c180f5588ad8e65eeceace92f4cecadfa452
2017-08-28 17:19:35 +00:00
dan 9c54156a9c Test BEGIN CONCURRENT transactions that consist entirely of read-only
statements.

FossilOrigin-Name: c3fe1f4b7e8dcadcb516622719d000b808effe3ad497244ba44f57d52dc2cc08
2017-08-24 10:10:28 +00:00
dan d828d0debc Add a more rigorous test case for the bug fixed by the previous commit on this
branch.

FossilOrigin-Name: 4256072399f44f48ed0856aa8112226af6feaf8676923612bde6cea239ebf920
2017-08-12 14:06:15 +00:00
dan 6aab028db0 Fix a problem allowing a conflicting transaction to be committed in the case
where more than one 32KB shared-memory page has been written to since the
transaction was started.

FossilOrigin-Name: 38dd9b50fe260d853cbc2551bc1bb60ddf5752f0456e0da3afe4cbf728c891d8
2017-08-11 21:16:23 +00:00
drh 9f16a96d4b Add another PAGERTRACE() macro to show when pages are added to Pager.pAllRead.
No impact on production builds.

FossilOrigin-Name: 11054cf5e8c24ef9ca869d558a0ca6750b56103c3b3793dc4afcef75192ea943
2017-08-11 20:22:30 +00:00
drh 741802e825 Add a PAGERTRACE() macro to log when frames are written into the WAL file.
This is for analysis only and is a no-op for production builds.

FossilOrigin-Name: 39f39e3d9a083eebe2f8dd5812d20c5b07cc17607b04b573a52fde6d00666f36
2017-08-11 19:31:53 +00:00
drh fd80ce4b10 Merge the latest enhancements from trunk.
FossilOrigin-Name: 47e716952d4a5d893b75726a2c52202cb0bc8ce1f75131e920a3ac2e202a507e
2017-08-11 19:16:55 +00:00
drh 9ee9b0f4c3 Merge recent enhancements from trunk.
FossilOrigin-Name: aafe1fec803ddf207bbcb3c669da0a9639411375fc04a52066dde4eb99c3c11b
2017-08-01 13:54:55 +00:00
drh c5856def1e Minor fix to the concurrent.test module so that it works on windows.
FossilOrigin-Name: 58553d61d199477f88ea9b45055ddaf821eca2aff1bf67c7d81fa80b23c2575a
2017-07-20 19:28:49 +00:00
drh 0a2afca904 Fix compiler warnings.
FossilOrigin-Name: 44c760d150b494ddd88382360cfcc4734fec936ed620f88b9df33ea7215c2fc2
2017-07-20 19:08:35 +00:00
drh 1af0a4e1df Fix a faulty #ifdef on the previous check-in
FossilOrigin-Name: 7355e74239fc20f3d948867902675069e6a4efd103bf734f1b24a132f6d2edd1
2017-07-20 18:56:21 +00:00
drh 7365bcd72c Get the build working with SQLITE_OMIT_CONCURRENT.
FossilOrigin-Name: a29401e924e6f82ca1c3eec98c6c0b2f1d5b9d7fba9af41a78516f21a7bab3a2
2017-07-20 18:28:33 +00:00
drh 1c6276eac6 Merge all the latest trunk enhancements.
FossilOrigin-Name: 213c61cb82d4ee51cc638fc5bb2b54cd51b963c40d088b2f01caf77032ea32fb
2017-07-20 17:47:12 +00:00
dan 827c9b785e Remove sqlite3_log() and abort() calls added to this branch to debug the
pointer-map problem ([fda22108]).

FossilOrigin-Name: 79544fc2856f30cac8b0962d00698974e8918562f09769a68264d17e1e1176fe
2017-06-10 17:23:20 +00:00
dan 4956bd5f9a Update the recent auto-vacuum fix so that it works for the in-memory
pointer-map structure used by this branch.

FossilOrigin-Name: 8e311a6dba202e8733830d8f31b8f0ce11eaefb3a0ab5e5e95ac0d2e5136043b
2017-06-08 16:23:55 +00:00
dan 63624a0ab8 Merge 3.19.3 changes with this branch.
FossilOrigin-Name: e2d38d51a9cf1c3dfef742507ec76e3d35853bd09b0d09bf2d404c4b036a184d
2017-06-08 16:10:41 +00:00
dan dc1bf28ad4 Merge changes for version 3.19.0 with this branch.
FossilOrigin-Name: 0f59bb94d5e8e16a47c53ef0d6138a35c87230ad384b58008e8e7127780446d8
2017-06-08 15:54:02 +00:00
drh d6396e9b16 Version 3.19.3
FossilOrigin-Name: 0ee482a1e0eae22e08edc8978c9733a96603d4509645f348ebf55b579e89636b
2017-06-08 14:26:16 +00:00
drh d4d01a689d Fix an issue with OPT_FEATURE_FLAGS in configure.ac.
FossilOrigin-Name: 97b5c4a53d0af3792c5ae5ceafdb02245f841c2bd4d79bcfd0ffdf6c5ff27c9e
2017-06-08 11:32:19 +00:00
drh a096fcc73c Increase the version number to 3.19.3.
FossilOrigin-Name: 903fff53b3f02cbc011c4cd47d841f45a447746fdf3bcb01202e27c3f2b57589
2017-06-08 11:27:05 +00:00
drh 50179f91f3 Ensure pointer map entries are always added when a row that does use overflow
pages replaces one that does not in an auto-vacuum database. Fix for
[fda22108].

FossilOrigin-Name: 9478106ca963e3ae5cfe59da40301c5a0a07494d03e656b5eb10ab45e441b870
2017-06-08 11:26:13 +00:00
dan 35dcd0e892 Extend even further the logging designed to find problems in the pointer-map
structure. Call abort() to dump a core as soon as such a problem is seen.

FossilOrigin-Name: f131677dcb4937e0dd62626afa91756aa28079e92acd6e9e127f6f676aa334f9
2017-06-02 09:31:27 +00:00
dan 7f002db7e3 Generate extra log messages in response to irregularites in the pointer-map
used by "BEGIN CONCURRENT" transactions.

FossilOrigin-Name: f7e3e2bc88f110d9282ce5d2fa58580c585faeb57cb707253f05001e5f4bd91b
2017-05-31 17:06:13 +00:00
dan 995b2457e2 Instead of a root page number, log the object (table or index) name if a page
level locking conflict is detected.

FossilOrigin-Name: 9ad846e57bd427adc7c29768cabca18905f7f978168e0642a5917d894fda8bfd
2017-05-29 19:23:56 +00:00
dan 7fff2e1cb9 Enhance the log messages emitted when a page conflict is detected.
FossilOrigin-Name: 92618492b048867af38922825f3d094eaaa2dd919b1ed2f7372483cc53f892bf
2017-05-29 14:27:37 +00:00
dan e3b047b3d7 Adjust the bitvec related sqlite3_log messages added by [9527089b].
FossilOrigin-Name: a7e0e7a4835314a04e065839a7f88b074eda795da2b2da5741b2afc096421a32
2017-05-26 18:18:51 +00:00
dan 7037787d30 Add extra test cases for deferred page allocation.
FossilOrigin-Name: 9df0195780306a26709e87daabf9d426856aebf2082060b98818f2d48c7472ec
2017-05-26 16:51:15 +00:00
dan 606f718480 Fix a problem with deferred page allocation in transactions that revert page
allocations by savepoint rollbacks.

FossilOrigin-Name: a4a3bbe64690856403642352f36e664a5a7fba686463a63446c6ada99df4e89f
2017-05-26 16:15:05 +00:00
dan e3c3be8511 Fix a problem with the deferred page allocation on this branch that could
occur when the database file is just slightly smaller than the PENDING_BYTE
page offset.

FossilOrigin-Name: 47a7dd92355ffd74645cf8da8186d5799c05a95907877a09c28de41135deeede
2017-05-25 21:02:00 +00:00
dan aa59505ae8 Add experimental new API sqlite3_wal_info().
FossilOrigin-Name: 5b9d498f6e9de6ee2ab86370c02c28a2a8b83d717b96d23b1fc52107677e45a2
2017-05-23 19:23:45 +00:00
dan 55b36d5c07 Invoke sqlite3_log() in response to irregularities surrounding the
Pager.pAllRead bit-vector.

FossilOrigin-Name: 9527089b7aa3695cd577f31b263b4777e9bd62dbbc1bd3af892c570e52e8c3a1
2017-05-19 19:57:15 +00:00
dan 6dbb452b38 Fix a problem on this branch causing some page-level read-locks to be omitted.
FossilOrigin-Name: 0eed152162b4721f7aaba8b480426476978a869e9578f100fca7b1d32942fe1a
2017-05-18 20:47:31 +00:00
drh 2ecf1ec8c9 Merge the latest changes from trunk.
FossilOrigin-Name: 14ea84003600ada6f4605e47028cb72fe621a14bcce3637ad48b1512dcce41cd
2017-05-15 17:34:23 +00:00
drh 9a89254e7a Merge changes from the 3.18.0 release.
FossilOrigin-Name: 785c37d9dbdba9f3deedecbdd72f6761428ebfb58e9b9393b9ca4feab3f66f02
2017-03-30 20:35:30 +00:00
drh fe329a7370 Version 3.18.0 Release Candidate
FossilOrigin-Name: 424a0d380332858ee55bdebc4af3789f74e70a2b3ba1cf29d84b9b4bcf3e2e37
2017-03-28 18:48:43 +00:00
drh 4d55d35df6 Merge all recent enhancements from trunk.
FossilOrigin-Name: fd5676fe7f55b3d4fa15ca119af7c064c6f9d053affdfd5e748785e300affbeb
2017-03-24 15:09:47 +00:00
dan 246e1d8faf Merge the "changebatch" functionality into this branch.
FossilOrigin-Name: 50fb1eb368f14e4ddce2da875601c227f9f937fd
2017-01-09 07:00:41 +00:00
dan 0195d8ef5d Disable the optimization from [8cb8516d] (omit TableLock instructions for
non-sharable databases) on this branch. This branch uses the TableLock
instructions to ensure that the db schema is not written from within a BEGIN
CONCURRENT transaction.

FossilOrigin-Name: c8ca3e0a8d82dbb077551c2d952cb2043f78333b
2017-01-09 06:53:00 +00:00
dan e32c7c2b64 Upgrade this branch to 3.16 plus the various fixes that appeared after its
release.

FossilOrigin-Name: d0e212d08f82dfb5e42a156b3e2bb03dd8e21258
2017-01-09 06:33:50 +00:00
dan 0ba9725489 Merge latest trunk changes into this branch.
FossilOrigin-Name: 391344d88a284f92c59b8d96a315b69292641de0
2016-12-07 07:46:50 +00:00
dan ff8e42e2ca Add the sqlite3changebatch_db() API.
FossilOrigin-Name: bee44ebc532f37e3fe61c18878e0d3db06805190
2016-08-24 19:14:59 +00:00
dan a8dee8df64 Add further tests for changebatch. And a fix to prevent a changeset from conflicting with itself.
FossilOrigin-Name: 506d6ff4b64c72d4ca65f0d15e1fdf8a132556bc
2016-08-23 19:02:21 +00:00
drh 7d931b985a Fix harmless compiler warnings in changebatch.
FossilOrigin-Name: a721a738184d914fcde3f5684099984a9373dff3
2016-08-23 18:09:37 +00:00
dan 8bbf544747 Add a new method to sessions - sqlite3sessions_fullchangeset() - to return a changeset that always contains values for all old.* fields. Update changebatch to use these values to more reliably detect multi-column UNIQUE constraint violations.
FossilOrigin-Name: efa761b2f509844b9212dd20bf0d082c6338e83f
2016-08-23 17:02:28 +00:00
dan a7d16a5cd1 Add a couple of extra tests to changebatch1.test.
FossilOrigin-Name: 207d970b7956c38af42c389b91a741a68b2c4eec
2016-08-22 21:01:39 +00:00
dan c18d304066 Add an experimental module to detect conflicts between sessions changesets.
FossilOrigin-Name: 0c9fd6b723041955b5182caa430312e5124fdc83
2016-08-22 20:49:06 +00:00
drh 3d4ad18cbe Merge version 3.14 plus some subsequent patches (including the page-cache
performance patch) from trunk.

FossilOrigin-Name: d9f8918c5b7b6c8540b3f433142e1b4aa4433885
2016-08-11 19:12:25 +00:00
dan 21ac1de75b Fix a typo in test program bc_test1.c.
FossilOrigin-Name: 2c61b7ab1808d5576a21fea84c3f414db51abfa5
2016-07-06 08:32:53 +00:00
dan 96b9dc9b36 Minor tweaks to the bc_test1 test program.
FossilOrigin-Name: d0d0bab4e92402b6af98366be6e8955588613a51
2016-05-30 05:45:32 +00:00
dan adeb47e57b Add further instrumentation to the bc_test1.c test app.
FossilOrigin-Name: 5528de4a53c19557798b6169e1d738f1a301e131
2016-05-26 20:52:15 +00:00
dan 12891cc492 Use pthreads mutexes and conditions to synchronize threads in bc_test1.
FossilOrigin-Name: f33aa76f074d8686a5a5c0edecabb71cb259c48d
2016-05-21 18:50:56 +00:00
dan 820fc4933b Add options to bc_test1.c to make it more flexible.
FossilOrigin-Name: ec6ef5f2c2dc18e1a19c205f365f4071f0870b68
2016-05-07 18:02:53 +00:00
dan b00083429a Add test/bc_test1.c, for testing the degree of concurrency provided by this branch under various conditions.
FossilOrigin-Name: 128c7eaed5479b615d75ebce1d781ea661e0fb2d
2016-05-06 21:04:47 +00:00
drh b0b18f6dca Merge the latest enhancements from trunk.
FossilOrigin-Name: 91e5c07eaf884d3598df8eb54f0910a80df48397
2016-04-29 16:01:18 +00:00
drh 76cb74da4d Merge all recent enhancements from trunk.
FossilOrigin-Name: 1f709fbf931e4d75848fc90532e2bc67ccd47cd4
2016-04-20 12:16:05 +00:00
drh db454d72b6 Merge 3.12.0 changes.
FossilOrigin-Name: d7381efff47c0a2f307478f196d03df7534f19e3
2016-03-29 10:36:07 +00:00
drh 99b14894eb Merge all recent changes from trunk.
FossilOrigin-Name: 8ee7d346c1d9beedd02bf11d0a45810985a45480
2016-03-21 15:54:46 +00:00
drh 03206c433c Merge the latest enhancements from trunk.
FossilOrigin-Name: a7978ab6d26b865f55c6b3e86ba3a24777a3c1f0
2016-03-16 19:34:45 +00:00
drh 93f88ebbb1 Merge version 3.11.0
FossilOrigin-Name: c393ddc71a041a711f8eaa6fbd75371309df5602
2016-02-15 17:44:45 +00:00
drh faf010d77a Merge PRAGMA synchronous=EXTRA and OOM performance enhancements from trunk.
FossilOrigin-Name: 332c8036ca8a97087ced210e322c8a91be2a9036
2016-02-05 14:34:51 +00:00
drh 7c9ecc62c5 Merge recent enhancements from trunk.
FossilOrigin-Name: 347f6a80c2aa6dde9b58d28069156db22cd21b6d
2016-02-02 02:53:49 +00:00
drh 04b9cf1e7e Merge recent enhancements from trunk.
FossilOrigin-Name: 5520f600eade720f465575ab1661dcddaba5adcf
2016-01-20 12:18:31 +00:00
dan f687ba597f Merge the latest enhancements and fixes from trunk.
FossilOrigin-Name: fccc5f20c360fa17c3692a9f1fc1d16612632a7f
2016-01-14 15:46:31 +00:00
drh 76ff0699bb Merge changes for version 3.10.0.
FossilOrigin-Name: e1d2ffc06d47b550ca9447a71443dea08b98d1ad
2016-01-06 15:49:19 +00:00
drh 607d4c2184 Merge recent trunk enhancements.
FossilOrigin-Name: 6bea792c3d71be34ccb9c14a0df8a5244d2d3678
2016-01-01 14:14:55 +00:00
drh 8c9ea487d0 Merge recent enhancements from trunk.
FossilOrigin-Name: cb22efaf50d83d9a73fdf8d986e6ea2fc6500cfb
2015-12-16 19:55:57 +00:00
drh 1d9497a0d0 Merge recent enhancements from trunk.
FossilOrigin-Name: 9130661a786e4c158f15103be57467a5cc03875a
2015-12-02 20:53:14 +00:00
drh 31441fb50b Merge all the latest enhancements and fixes from trunk.
FossilOrigin-Name: 41c8b8e39bc0483cfbc0b4bfcc8ef8b2737a70a9
2015-11-20 13:49:01 +00:00
drh f12cc2a68c Merge the sqlite3_db_cacheflush() enhancements and other changes from trunk.
FossilOrigin-Name: f2cde4cfc58cc372f59ae274bf0c2f7cf6e7ddf9
2015-10-30 17:17:12 +00:00
drh cd9491ca2f Merge the 3.9.1 updates from trunk.
FossilOrigin-Name: bf866e6c0d95d48744c86ff0c0be9b2653629a18
2015-10-16 20:55:12 +00:00
drh e8a1ea6d8b Merge in the 3.9.0 changes from trunk.
FossilOrigin-Name: 5c3a2a6ed64f4d0e10238720bc2e4ae3af3eded3
2015-10-15 07:44:58 +00:00
drh 0faad42d59 Merge all recent trunk enhancements and fixes into the begin-concurrent branch.
FossilOrigin-Name: c63c1e15f8cc8981aa4dbec82d510ed70cd09797
2015-09-24 15:17:54 +00:00
drh 411547ec6c Merge enhancements from trunk.
FossilOrigin-Name: fc4d1de8aeb39a4c0ea9275a0bd2447535f1a955
2015-09-15 19:16:47 +00:00
drh 9a5a469c7a Merge parser enhancements and other improvements and bug fixes from trunk.
FossilOrigin-Name: 9cf3e51bcce1268dbb22cc8fa77160db3cb72746
2015-09-07 20:22:22 +00:00
drh 573ebef187 Merge the latest trunk changes, and especially the fix for allowing
strings as identifiers in CREATE INDEX statements.

FossilOrigin-Name: a9b84885aa572b7f92e5aafa246af328d13e3e6e
2015-09-04 17:22:03 +00:00
drh 144cd3df05 Remove the EXCLUSIVE and CONCURRENT tokens from the tokenizer. Let the
BEGIN statement be followed by an ID, but throw a syntax error if the ID
is anything other than EXCLUSIVE or CONCURRENT.

FossilOrigin-Name: c0bf92eca4d3d4666e8a0476ef30fa8123de1cb0
2015-09-04 16:39:16 +00:00
drh 3561ec2ede Merge performance enhancements from trunk. This branch now runs (slightly)
faster than the 3.8.11.1 release, though still slightly slower than trunk.

FossilOrigin-Name: c490bfb150c66763226a35e30ba289abbf29906d
2015-09-03 20:52:44 +00:00
drh 01be463eeb Changes from ENABLE_CONCURRENT (default off) to OMIT_CONCURRENT (default on).
This is not a clear-cut decision and might be changed back.

FossilOrigin-Name: f8ae9bfd05abc35293ad6bc62ab1bdbe357d964e
2015-09-03 15:17:12 +00:00
drh a62b6bd93c Merge trunk optimizations.
FossilOrigin-Name: 71e7299e8d501618b10f8c1f78572789b1b6853a
2015-09-03 14:04:05 +00:00
drh f56af5ee2d Merge the latest trunk enhancements.
FossilOrigin-Name: 3dea047465fa8e3031046a33016b6ed760336ddc
2015-09-01 18:01:16 +00:00
drh 3f531da564 Fixes so that it builds without warnings both with and without
SQLITE_ENABLE_CONCURRENT.

FossilOrigin-Name: 5ed2a445a164f0f0c2669c6681ea76618e639961
2015-09-01 17:48:54 +00:00
dan 7b8996c878 Merge latest trunk changes with this branch.
FossilOrigin-Name: 57bc0194f41dbcd2c343e665e7af475cd4dd7328
2015-08-28 09:27:51 +00:00
dan c8a9d15887 Add header comments for new methods in pager.c.
FossilOrigin-Name: 437c7e219d3240767a28f73487bc26c3be3044b3
2015-08-27 19:57:16 +00:00
dan 6b3e51bd33 Add test cases for concurrent transactions and long-lived SELECT statements.
FossilOrigin-Name: fd4798cb7af263409c20d3cf81236b830bd68570
2015-08-27 19:22:56 +00:00
dan 987f821f79 Fix a problem whereby concurrent transactions would not consider pages read by the transaction before the first write statement.
FossilOrigin-Name: fc17f73170a27c2fe511ed6b6d488535c4e35bae
2015-08-27 17:42:38 +00:00
dan f6cf5ea790 Fix an assert() in pager.c that could fail in a concurrent transaction.
FossilOrigin-Name: 69394ddaa2bc9d26477b4359c676c598b733ac9f
2015-08-26 18:54:45 +00:00
dan de36c76a23 Fix a problem allowing some conflicting transactions to be committed.
FossilOrigin-Name: a0566382d564ca17fd13475a44fed8f714742d97
2015-08-26 18:02:20 +00:00
dan fef3410f7f Add miscellaneous test cases for concurrent transactions.
FossilOrigin-Name: 779b1d0e17bc54062b2b09cdbf94e9e2f4bae4f7
2015-08-25 19:10:29 +00:00
dan 57888f7300 If "PRAGMA integrity_check" is run while the database is being written by a CONCURRENT transaction, do not consider unreferenced pages to be an error. They may be part of the free-page list, which is not visible at the b-tree layer when running a CONCURRENT transaction.
FossilOrigin-Name: f32b57b49311693eb0c0c9f6f14859e7b1fa93d8
2015-08-25 17:16:33 +00:00
dan 4073b26a20 Test that if a corrupt wal-index header is encountered when attempting to commit a concurrent transaction, SQLITE_BUSY_SNAPSHOT is returned to the caller.
FossilOrigin-Name: c746e0bd20cb136eed2b691f326657d266e2f1ed
2015-08-25 16:01:04 +00:00
dan ac0a42233a Fix a segfault that could occur following an OOM condition in the concurrent transaction code.
FossilOrigin-Name: 231b5880228cf01efe3981bc8be3150d79b422e5
2015-08-25 14:37:39 +00:00
dan 3c40ed4281 Merge latest trunk changes with this branch.
FossilOrigin-Name: 3e7d6dd62dfa63d7def00bd00ac055a606a0c80d
2015-08-25 11:16:02 +00:00
mistachkin 0cd4f69c0c Remove duplicated line of code.
FossilOrigin-Name: 47280f2a2b7cc83bf11ab86284204f565c278c55
2015-08-24 22:06:02 +00:00
dan bf3cf57e15 Change "BEGIN UNLOCKED" to "BEGIN CONCURRENT".
FossilOrigin-Name: ba1ab858e2997c88dd7eee6e6893a8616d85c665
2015-08-24 19:56:04 +00:00
dan f5e89dba9d Fix handling of attempts to modify the database schema, application_id or user_version within an UNLOCKED transaction.
FossilOrigin-Name: 5b9f272113d21fd606903509d6f830fe60fac039
2015-08-24 19:08:10 +00:00
dan 0408529b48 Fix compilation without SQLITE_ENABLE_UNLOCKED. Also other code organization issues.
FossilOrigin-Name: 041135575417201bbcf0544cc69dcb7369c7fb34
2015-08-24 16:00:08 +00:00
dan 5cf03728a6 Consolidate two blocks of similar code in btreeFixUnlocked().
FossilOrigin-Name: 701302b4bd62ca7aefe643eac096a0ee672a62fa
2015-08-24 10:05:03 +00:00
dan 8b994f29a5 Merge trunk changes with this branch.
FossilOrigin-Name: 876810c28b3ad573ae46050ec699ef7eea4e313d
2015-08-24 06:44:17 +00:00
dan 654a965367 Fix another problem involving unlocked transactions and wal-file restarts.
FossilOrigin-Name: 4460764ea8fc948fe02f0a09476857839b3aa1ae
2015-08-24 06:43:25 +00:00
dan 0c52b373a0 Fix a problem to do with detecting unlocked transaction conflicts if another client restarts the wal while the transaction is running.
FossilOrigin-Name: e3968b256282d8c0a87c26667b1ba02faf7a5a17
2015-08-22 20:32:39 +00:00
dan f5cebf71fe Add further tests for deferred page allocation. And fixes for the same.
FossilOrigin-Name: ed0a31be726e60115a5dd73d4ed580201b400ab7
2015-08-22 17:28:55 +00:00
dan c299a11c02 Merge further trunk changes.
FossilOrigin-Name: c2327a3b8e5d604ab948b1e9f6cfc401429e51db
2015-08-22 07:56:49 +00:00
dan 8c5847a257 Merge trunk changes into this branch.
FossilOrigin-Name: 9021f7369f4b32db6126b3dc0ac75d11039625be
2015-08-22 07:55:08 +00:00
dan b87b25f219 Fix many minor issues in the unlocked transaction code.
FossilOrigin-Name: 53aaeea6c98f82f3b55d4b3ab139ee41b727243c
2015-08-21 20:11:23 +00:00
dan 572a21c8e9 When committing an UNLOCKED transaction, try to move pages allocated at the end of the file to free slots within the file (like an incremental-vacuum operation does).
FossilOrigin-Name: 069679162d8d50e9731831e658aa58f280dbb3e7
2015-08-21 18:55:22 +00:00
dan 70af25d03d Fix a problem with UNLOCKED transactions that free pages allocated within the same transaction.
FossilOrigin-Name: 227bb8a1815c4dc6084970f06b0a6bfccdff3fd2
2015-08-21 17:57:16 +00:00
dan cec0beebb3 Merge trunk changes with this branch.
FossilOrigin-Name: deaf3b18569025d8d168da29fae76142cfffe9e5
2015-08-21 16:22:04 +00:00
dan 64b310ed1a Add extra tests and a fix for rollbacks of UNLOCKED transactions.
FossilOrigin-Name: 82cd837e72ed4cf5821be717369694be29a2997e
2015-08-21 14:21:22 +00:00
dan 699bdf056b Fix a problem causing corruption when an UNLOCKED transaction is rolled back.
FossilOrigin-Name: 7c36147846484c96023939864417b5624f3bc5f8
2015-08-20 20:25:56 +00:00
dan 7b3d71e9cc When committing an unlocked transaction, relocate newly allocated database pages within the file to avoid conflicting with committed transactions. There are lots of things still to fix in this code.
FossilOrigin-Name: 3bbc31d515ba9fc920c5cbc7059d3eb1ba9c7f8e
2015-08-19 20:27:05 +00:00
dan 1a9cde3ba9 Handle writes to auto-vacuum databases within UNLOCKED transactions in the same way as for non-UNLOCKED transactions.
FossilOrigin-Name: de1ea450db33b140b11af5b801ea6a15875e774e
2015-08-15 18:16:46 +00:00
dan 773d2d6c09 Only allow UNLOCKED transactions to commit if none of the pages read by the transaction have been modified since it was opened.
FossilOrigin-Name: 0b9718426e44df092850c5d095ce1b84a1e567cf
2015-07-29 12:14:28 +00:00
dan 37d36205f3 Add some test cases and fix some small problems with BEGIN UNLOCKED transactions.
FossilOrigin-Name: 6da0e962ad2aa5e52c1f1b5c3dbf77a2cb16ac2d
2015-07-28 16:46:49 +00:00
dan 3d39434c36 Add an experimental "BEGIN UNLOCKED" command.
FossilOrigin-Name: 8079421604dbd40d03471dad6d12115119b554c2
2015-07-27 19:31:45 +00:00
77 changed files with 7021 additions and 545 deletions
+3 -1
View File
@@ -433,6 +433,7 @@ TESTSRC = \
$(TOP)/ext/fts3/fts3_term.c \
$(TOP)/ext/fts3/fts3_test.c \
$(TOP)/ext/session/test_session.c \
$(TOP)/ext/session/sqlite3changebatch.c \
$(TOP)/ext/rbu/test_rbu.c
# Statically linked extensions
@@ -522,8 +523,9 @@ TESTSRC2 = \
$(TOP)/ext/fts3/fts3_tokenizer.c \
$(TOP)/ext/fts3/fts3_write.c \
$(TOP)/ext/async/sqlite3async.c \
$(TOP)/ext/session/sqlite3session.c \
$(TOP)/ext/misc/stmt.c \
$(TOP)/ext/session/sqlite3session.c \
$(TOP)/ext/session/test_session.c \
fts5.c
# Header files used by all library source files.
+1 -1
View File
@@ -1 +1 @@
3.37.0
3.37.2
Vendored
+35 -45
View File
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69 for sqlite 3.37.0.
# Generated by GNU Autoconf 2.69 for sqlite 3.37.2.
#
#
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
@@ -726,8 +726,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='sqlite'
PACKAGE_TARNAME='sqlite'
PACKAGE_VERSION='3.37.0'
PACKAGE_STRING='sqlite 3.37.0'
PACKAGE_VERSION='3.37.2'
PACKAGE_STRING='sqlite 3.37.2'
PACKAGE_BUGREPORT=''
PACKAGE_URL=''
@@ -770,11 +770,11 @@ ac_includes_default="\
ac_subst_vars='LTLIBOBJS
LIBOBJS
BUILD_CFLAGS
AMALGAMATION_LINE_MACROS
USE_GCOV
OPT_FEATURE_FLAGS
HAVE_ZLIB
USE_AMALGAMATION
AMALGAMATION_LINE_MACROS
TARGET_DEBUG
TARGET_HAVE_EDITLINE
TARGET_HAVE_READLINE
@@ -903,7 +903,6 @@ with_readline_lib
with_readline_inc
enable_debug
enable_amalgamation
amalgamation_line_macros
enable_load_extension
enable_math
enable_all
@@ -1469,7 +1468,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures sqlite 3.37.0 to adapt to many kinds of systems.
\`configure' configures sqlite 3.37.2 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1534,7 +1533,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of sqlite 3.37.0:";;
short | recursive ) echo "Configuration of sqlite 3.37.2:";;
esac
cat <<\_ACEOF
@@ -1662,7 +1661,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
sqlite configure 3.37.0
sqlite configure 3.37.2
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2081,7 +2080,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by sqlite $as_me 3.37.0, which was
It was created by sqlite $as_me 3.37.2, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -3939,13 +3938,13 @@ if ${lt_cv_nm_interface+:} false; then :
else
lt_cv_nm_interface="BSD nm"
echo "int some_variable = 0;" > conftest.$ac_ext
(eval echo "\"\$as_me:3940: $ac_compile\"" >&5)
(eval echo "\"\$as_me:3941: $ac_compile\"" >&5)
(eval "$ac_compile" 2>conftest.err)
cat conftest.err >&5
(eval echo "\"\$as_me:3943: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
(eval echo "\"\$as_me:3944: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
(eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
cat conftest.err >&5
(eval echo "\"\$as_me:3946: output\"" >&5)
(eval echo "\"\$as_me:3947: output\"" >&5)
cat conftest.out >&5
if $GREP 'External.*some_variable' conftest.out > /dev/null; then
lt_cv_nm_interface="MS dumpbin"
@@ -5151,7 +5150,7 @@ ia64-*-hpux*)
;;
*-*-irix6*)
# Find out which ABI we are using.
echo '#line 5152 "configure"' > conftest.$ac_ext
echo '#line 5153 "configure"' > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
ac_status=$?
@@ -6676,11 +6675,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:6677: $lt_compile\"" >&5)
(eval echo "\"\$as_me:6678: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
echo "$as_me:6681: \$? = $ac_status" >&5
echo "$as_me:6682: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@@ -7015,11 +7014,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:7016: $lt_compile\"" >&5)
(eval echo "\"\$as_me:7017: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
echo "$as_me:7020: \$? = $ac_status" >&5
echo "$as_me:7021: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@@ -7120,11 +7119,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:7121: $lt_compile\"" >&5)
(eval echo "\"\$as_me:7122: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
echo "$as_me:7125: \$? = $ac_status" >&5
echo "$as_me:7126: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@@ -7175,11 +7174,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:7176: $lt_compile\"" >&5)
(eval echo "\"\$as_me:7177: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
echo "$as_me:7180: \$? = $ac_status" >&5
echo "$as_me:7181: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@@ -9555,7 +9554,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
#line 9556 "configure"
#line 9557 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@@ -9651,7 +9650,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
#line 9652 "configure"
#line 9653 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@@ -10307,13 +10306,6 @@ done
#
USE_AMALGAMATION=1
#########
# By default, amalgamation sqlite3.c will have #line directives.
# This is a build option not shown by ./configure --help
# To control it, use configure option: amalgamation_line_macros=?
# where ? is no to suppress #line directives or yes to create them.
AMALGAMATION_LINE_MACROS=--linemacros=0
#########
# See whether we can run specific tclsh versions known to work well;
# if not, then we fall back to plain tclsh.
@@ -11284,19 +11276,6 @@ if test "${enable_amalgamation}" = "no" ; then
USE_AMALGAMATION=0
fi
########
# See whether --disable
if test "${amalgamation_line_macros+set}" = set; then :
enableval=$amalgamation_line_macros;
fi
if test "${amalgamation_line_macros}" = "yes" ; then
AMALGAMATION_LINE_MACROS=--linemacros=1
fi
if test "${amalgamation_line_macros}" = "no" ; then
AMALGAMATION_LINE_MACROS=--linemacros=0
fi
#########
# Look for zlib. Only needed by extensions and by the sqlite3.exe shell
@@ -11882,6 +11861,17 @@ else
fi
#########
# Enable/disabled amalagamation line macros
########
AMALGAMATION_LINE_MACROS=--linemacros=0
if test "${amalgamation_line_macros}" = "yes" ; then
AMALGAMATION_LINE_MACROS=--linemacros=1
fi
if test "${amalgamation_line_macros}" = "no" ; then
AMALGAMATION_LINE_MACROS=--linemacros=0
fi
#########
# Output the config header
@@ -12400,7 +12390,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by sqlite $as_me 3.37.0, which was
This file was extended by sqlite $as_me 3.37.2, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -12466,7 +12456,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
sqlite config.status 3.37.0
sqlite config.status 3.37.2
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
+11
View File
@@ -791,6 +791,17 @@ else
fi
AC_SUBST(USE_GCOV)
#########
# Enable/disabled amalagamation line macros
########
AMALGAMATION_LINE_MACROS=--linemacros=0
if test "${amalgamation_line_macros}" = "yes" ; then
AMALGAMATION_LINE_MACROS=--linemacros=1
fi
if test "${amalgamation_line_macros}" = "no" ; then
AMALGAMATION_LINE_MACROS=--linemacros=0
fi
AC_SUBST(AMALGAMATION_LINE_MACROS)
#########
# Output the config header
+107
View File
@@ -0,0 +1,107 @@
Begin Concurrent
================
## Overview
Usually, SQLite allows at most one writer to proceed concurrently. The
BEGIN CONCURRENT enhancement allows multiple writers to process write
transactions simultanously if the database is in "wal" or "wal2" mode,
although the system still serializes COMMIT commands.
When a write-transaction is opened with "BEGIN CONCURRENT", actually
locking the database is deferred until a COMMIT is executed. This means
that any number of transactions started with BEGIN CONCURRENT may proceed
concurrently. The system uses optimistic page-level-locking to prevent
conflicting concurrent transactions from being committed.
When a BEGIN CONCURRENT transaction is committed, the system checks whether
or not any of the database pages that the transaction has read have been
modified since the BEGIN CONCURRENT was opened. In other words - it asks
if the transaction being committed operates on a different set of data than
all other concurrently executing transactions. If the answer is "yes, this
transaction did not read or modify any data modified by any concurrent
transaction", then the transaction is committed as normal. Otherwise, if the
transaction does conflict, it cannot be committed and an SQLITE&#95;BUSY&#95;SNAPSHOT
error is returned. At this point, all the client can do is ROLLBACK the
transaction.
If SQLITE&#95;BUSY&#95;SNAPSHOT is returned, messages are output via the sqlite3&#95;log
mechanism indicating the page and table or index on which the conflict
occurred. This can be useful when optimizing concurrency.
## Application Programming Notes
In order to serialize COMMIT processing, SQLite takes a lock on the database
as part of each COMMIT command and releases it before returning. At most one
writer may hold this lock at any one time. If a writer cannot obtain the lock,
it uses SQLite's busy-handler to pause and retry for a while:
<a href=https://www.sqlite.org/c3ref/busy_handler.html>
https://www.sqlite.org/c3ref/busy_handler.html
</a>
If there is significant contention for the writer lock, this mechanism can be
inefficient. In this case it is better for the application to use a mutex or
some other mechanism that supports blocking to ensure that at most one writer
is attempting to COMMIT a BEGIN CONCURRENT transaction at a time. This is
usually easier if all writers are part of the same operating system process.
If all database clients (readers and writers) are located in the same OS
process, and if that OS is a Unix variant, then it can be more efficient to
the built-in VFS "unix-excl" instead of the default "unix". This is because it
uses more efficient locking primitives.
The key to maximizing concurrency using BEGIN CONCURRENT is to ensure that
there are a large number of non-conflicting transactions. In SQLite, each
table and each index is stored as a separate b-tree, each of which is
distributed over a discrete set of database pages. This means that:
* Two transactions that write to different sets of tables never
conflict, and that
* Two transactions that write to the same tables or indexes only
conflict if the values of the keys (either primary keys or indexed
rows) are fairly close together. For example, given a large
table with the schema:
<pre> CREATE TABLE t1(a INTEGER PRIMARY KEY, b BLOB);</pre>
writing two rows with adjacent values for "a" probably will cause a
conflict (as the two keys are stored on the same page), but writing two
rows with vastly different values for "a" will not (as the keys will likly
be stored on different pages).
Note that, in SQLite, if values are not explicitly supplied for an INTEGER
PRIMARY KEY, as for example in:
>
INSERT INTO t1(b) VALUES(&lt;blob-value>);
then monotonically increasing values are assigned automatically. This is
terrible for concurrency, as it all but ensures that all new rows are
added to the same database page. In such situations, it is better to
explicitly assign random values to INTEGER PRIMARY KEY fields.
This problem also comes up for non-WITHOUT ROWID tables that do not have an
explicit INTEGER PRIMARY KEY column. In these cases each table has an implicit
INTEGER PRIMARY KEY column that is assigned increasing values, leading to the
same problem as omitting to assign a value to an explicit INTEGER PRIMARY KEY
column.
For both explicit and implicit INTEGER PRIMARY KEYs, it is possible to have
SQLite assign values at random (instead of the monotonically increasing
values) by writing a row with a rowid equal to the largest possible signed
64-bit integer to the table. For example:
INSERT INTO t1(a) VALUES(9223372036854775807);
Applications should take care not to malfunction due to the presence of such
rows.
The nature of some types of indexes, for example indexes on timestamp fields,
can also cause problems (as concurrent transactions may assign similar
timestamps that will be stored on the same db page to new records). In these
cases the database schema may need to be rethought to increase the concurrency
provided by page-level-locking.
+1 -1
View File
@@ -4473,7 +4473,7 @@ void sqlite3Fts3DoclistPrev(
assert( nDoclist>0 );
assert( *pbEof==0 );
assert( p || *piDocid==0 );
assert_fts3_nc( p || *piDocid==0 );
assert( !p || (p>aDoclist && p<&aDoclist[nDoclist]) );
if( p==0 ){
+6 -2
View File
@@ -1879,8 +1879,12 @@ static void fts5SegIterReverseNewPage(Fts5Index *p, Fts5SegIter *pIter){
int iRowidOff;
iRowidOff = fts5LeafFirstRowidOff(pNew);
if( iRowidOff ){
pIter->pLeaf = pNew;
pIter->iLeafOffset = iRowidOff;
if( iRowidOff>=pNew->szLeaf ){
p->rc = FTS5_CORRUPT;
}else{
pIter->pLeaf = pNew;
pIter->iLeafOffset = iRowidOff;
}
}
}
+54
View File
@@ -0,0 +1,54 @@
# 2015 Apr 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 tests that FTS5 handles corrupt databases (i.e. internal
# inconsistencies in the backing tables) correctly. In this case
# "correctly" means without crashing.
#
source [file join [file dirname [info script]] fts5_common.tcl]
set testprefix fts5corrupt6
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
ifcapable !fts5 {
finish_test
return
}
sqlite3_fts5_may_be_corrupt 1
database_may_be_corrupt
proc editblock {block} {
binary format Sa* 20000 [string range $block 2 end]
}
db func editblock editblock
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE ft USING fts5(abc, def);
WITH a(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM a WHERE i<1000
)
INSERT INTO ft SELECT
'abc abc abc abc abc abc abc abc abc abc',
'def def def def def def def def def def'
FROM a;
UPDATE ft_data SET block = editblock(block) WHERE id=(
SELECT id FROM ft_data ORDER BY id LIMIT 1 OFFSET 5
);
}
do_catchsql_test 1.1 {
SELECT rowid FROM ft('def') ORDER BY rowid DESC LIMIT 1 OFFSET 9999;
} {1 {database disk image is malformed}}
sqlite3_fts5_may_be_corrupt 0
finish_test
+1 -1
View File
@@ -304,7 +304,7 @@ static int box_query(sqlite3_rtree_query_info *pInfo){
static void box_query_destroy(void *p){
BoxQueryCtx *pCtx = (BoxQueryCtx*)p;
Tcl_DecrRefCount(pCtx->pScript);
ckfree(pCtx);
ckfree((char*)pCtx);
}
static int SQLITE_TCLAPI register_box_query(
+222
View File
@@ -0,0 +1,222 @@
# 2016 August 23
#
# 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.
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source $testdir/tester.tcl
ifcapable !session {finish_test; return}
set testprefix changebatch1
proc sql_to_changeset {method sql} {
sqlite3session S db main
S attach *
execsql $sql
set ret [S $method]
S delete
return $ret
}
proc do_changebatch_test {tn method args} {
set C [list]
foreach a $args {
lappend C [sql_to_changeset $method $a]
}
sqlite3changebatch cb db
set i 1
foreach ::cs [lrange $C 0 end-1] {
set rc [cb add $::cs]
if {$rc!="SQLITE_OK"} { error "expected SQLITE_OK, got $rc (i=$i)" }
incr i
}
set ::cs [lindex $C end]
do_test $tn { cb add [set ::cs] } SQLITE_CONSTRAINT
cb delete
}
proc do_changebatch_test1 {tn args} {
uplevel do_changebatch_test $tn changeset $args
}
proc do_changebatch_test2 {tn args} {
uplevel do_changebatch_test $tn fullchangeset $args
}
#-------------------------------------------------------------------------
# The body of the following loop contains tests for database schemas
# that do not feature multi-column UNIQUE constraints. In this case
# it doesn't matter if the changesets are generated using
# sqlite3session_changeset() or sqlite3session_fullchangeset().
#
foreach {tn testfunction} {
1 do_changebatch_test1
2 do_changebatch_test2
} {
reset_db
#-------------------------------------------------------------------------
#
do_execsql_test $tn.1.0 {
CREATE TABLE t1(a PRIMARY KEY, b);
}
$testfunction $tn.1.1 {
INSERT INTO t1 VALUES(1, 1);
} {
DELETE FROM t1 WHERE a=1;
}
do_execsql_test $tn.1.2.0 {
INSERT INTO t1 VALUES(1, 1);
INSERT INTO t1 VALUES(2, 2);
INSERT INTO t1 VALUES(3, 3);
}
$testfunction $tn.1.2.1 {
DELETE FROM t1 WHERE a=2;
} {
INSERT INTO t1 VALUES(2, 2);
}
#-------------------------------------------------------------------------
#
do_execsql_test $tn.2.0 {
CREATE TABLE x1(a, b PRIMARY KEY, c UNIQUE);
CREATE TABLE x2(a PRIMARY KEY, b UNIQUE, c UNIQUE);
CREATE INDEX x1a ON x1(a);
INSERT INTO x1 VALUES(1, 1, 'a');
INSERT INTO x1 VALUES(1, 2, 'b');
INSERT INTO x1 VALUES(1, 3, 'c');
}
$testfunction $tn.2.1 {
DELETE FROM x1 WHERE b=2;
} {
UPDATE x1 SET c='b' WHERE b=3;
}
$testfunction $tn.2.2 {
DELETE FROM x1 WHERE b=1;
} {
INSERT INTO x1 VALUES(1, 5, 'a');
}
set L [list]
for {set i 1000} {$i < 10000} {incr i} {
lappend L "INSERT INTO x2 VALUES($i, $i, 'x' || $i)"
}
lappend L "DELETE FROM x2 WHERE b=1005"
$testfunction $tn.2.3 {*}$L
execsql { INSERT INTO x1 VALUES('f', 'f', 'f') }
$testfunction $tn.2.4 {
INSERT INTO x2 VALUES('f', 'f', 'f');
} {
INSERT INTO x1 VALUES('g', 'g', 'g');
} {
DELETE FROM x1 WHERE b='f';
} {
INSERT INTO x2 VALUES('g', 'g', 'g');
} {
INSERT INTO x1 VALUES('f', 'f', 'f');
}
execsql {
DELETE FROM x1;
INSERT INTO x1 VALUES(1.5, 1.5, 1.5);
}
$testfunction $tn.2.5 {
DELETE FROM x1 WHERE b BETWEEN 1 AND 2;
} {
INSERT INTO x1 VALUES(2.5, 2.5, 2.5);
} {
INSERT INTO x1 VALUES(1.5, 1.5, 1.5);
}
execsql {
DELETE FROM x2;
INSERT INTO x2 VALUES(X'abcd', X'1234', X'7890');
INSERT INTO x2 VALUES(X'0000', X'0000', X'0000');
}
breakpoint
$testfunction $tn.2.6 {
UPDATE x2 SET c = X'1234' WHERE a=X'abcd';
INSERT INTO x2 VALUES(X'1234', X'abcd', X'7890');
} {
DELETE FROM x2 WHERE b=X'0000';
} {
INSERT INTO x2 VALUES(1, X'0000', NULL);
}
}
#-------------------------------------------------------------------------
# Test some multi-column UNIQUE constraints. First Using _changeset() to
# demonstrate the problem, then using _fullchangeset() to show that it has
# been fixed.
#
reset_db
do_execsql_test 3.0 {
CREATE TABLE y1(a PRIMARY KEY, b, c, UNIQUE(b, c));
INSERT INTO y1 VALUES(1, 1, 1);
INSERT INTO y1 VALUES(2, 2, 2);
INSERT INTO y1 VALUES(3, 3, 3);
INSERT INTO y1 VALUES(4, 3, 4);
BEGIN;
}
do_test 3.1.1 {
set c1 [sql_to_changeset changeset { DELETE FROM y1 WHERE a=4 }]
set c2 [sql_to_changeset changeset { UPDATE y1 SET c=4 WHERE a=3 }]
sqlite3changebatch cb db
cb add $c1
cb add $c2
} {SQLITE_OK}
do_test 3.1.2 {
cb delete
execsql ROLLBACK
} {}
do_test 3.1.1 {
set c1 [sql_to_changeset fullchangeset { DELETE FROM y1 WHERE a=4 }]
set c2 [sql_to_changeset fullchangeset { UPDATE y1 SET c=4 WHERE a=3 }]
sqlite3changebatch cb db
cb add $c1
cb add $c2
} {SQLITE_CONSTRAINT}
do_test 3.1.2 {
cb delete
} {}
#-------------------------------------------------------------------------
#
reset_db
do_execsql_test 4.0 {
CREATE TABLE t1(x, y, z, PRIMARY KEY(x, y), UNIQUE(z));
}
do_test 4.1 {
set c1 [sql_to_changeset fullchangeset { INSERT INTO t1 VALUES(1, 2, 3) }]
execsql {
DROP TABLE t1;
CREATE TABLE t1(w, x, y, z, PRIMARY KEY(x, y), UNIQUE(z));
}
sqlite3changebatch cb db
list [catch { cb add $c1 } msg] $msg
} {1 SQLITE_RANGE}
finish_test
+42
View File
@@ -0,0 +1,42 @@
# 2011 Mar 21
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# The focus of this file is testing the session module.
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source [file join [file dirname [info script]] session_common.tcl]
source $testdir/tester.tcl
ifcapable !session {finish_test; return}
set testprefix changebatchfault
do_execsql_test 1.0 {
CREATE TABLE t1(a, b, c PRIMARY KEY, UNIQUE(a, b));
INSERT INTO t1 VALUES('a', 'a', 'a');
INSERT INTO t1 VALUES('b', 'b', 'b');
}
set ::c1 [changeset_from_sql { delete from t1 where c='a' }]
set ::c2 [changeset_from_sql { insert into t1 values('c', 'c', 'c') }]
do_faultsim_test 1 -faults oom-* -body {
sqlite3changebatch cb db
cb add $::c1
cb add $::c2
} -test {
faultsim_test_result {0 SQLITE_OK} {1 SQLITE_NOMEM}
catch { cb delete }
}
finish_test
+1 -1
View File
@@ -29,7 +29,7 @@ do_test 1.0 {
WITH s(i) AS (
VALUES(1) UNION ALL SELECT i+1 FROM s WHERe i<10000
)
INSERT INTO t1 SELECT 'abcde', randomblob(16), i FROM s;
INSERT INTO t1 SELECT 'abcde', randomblob(18), i FROM s;
}
compare_db db db2
} {}
+485
View File
@@ -0,0 +1,485 @@
#if !defined(SQLITE_TEST) || (defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK))
#include "sqlite3session.h"
#include "sqlite3changebatch.h"
#include <assert.h>
#include <string.h>
typedef struct BatchTable BatchTable;
typedef struct BatchIndex BatchIndex;
typedef struct BatchIndexEntry BatchIndexEntry;
typedef struct BatchHash BatchHash;
struct sqlite3_changebatch {
sqlite3 *db; /* Database handle used to read schema */
BatchTable *pTab; /* First in linked list of tables */
int iChangesetId; /* Current changeset id */
int iNextIdxId; /* Next available index id */
int nEntry; /* Number of entries in hash table */
int nHash; /* Number of hash buckets */
BatchIndexEntry **apHash; /* Array of hash buckets */
};
struct BatchTable {
BatchIndex *pIdx; /* First in linked list of UNIQUE indexes */
BatchTable *pNext; /* Next table */
char zTab[1]; /* Table name */
};
struct BatchIndex {
BatchIndex *pNext; /* Next index on same table */
int iId; /* Index id (assigned internally) */
int bPk; /* True for PK index */
int nCol; /* Size of aiCol[] array */
int *aiCol; /* Array of columns that make up index */
};
struct BatchIndexEntry {
BatchIndexEntry *pNext; /* Next colliding hash table entry */
int iChangesetId; /* Id of associated changeset */
int iIdxId; /* Id of index this key is from */
int szRecord;
char aRecord[1];
};
/*
** Allocate and zero a block of nByte bytes. Must be freed using cbFree().
*/
static void *cbMalloc(int *pRc, int nByte){
void *pRet;
if( *pRc ){
pRet = 0;
}else{
pRet = sqlite3_malloc(nByte);
if( pRet ){
memset(pRet, 0, nByte);
}else{
*pRc = SQLITE_NOMEM;
}
}
return pRet;
}
/*
** Free an allocation made by cbMalloc().
*/
static void cbFree(void *p){
sqlite3_free(p);
}
/*
** Return the hash bucket that pEntry belongs in.
*/
static int cbHash(sqlite3_changebatch *p, BatchIndexEntry *pEntry){
unsigned int iHash = (unsigned int)pEntry->iIdxId;
unsigned char *pEnd = (unsigned char*)&pEntry->aRecord[pEntry->szRecord];
unsigned char *pIter;
for(pIter=(unsigned char*)pEntry->aRecord; pIter<pEnd; pIter++){
iHash += (iHash << 7) + *pIter;
}
return (int)(iHash % p->nHash);
}
/*
** Resize the hash table.
*/
static int cbHashResize(sqlite3_changebatch *p){
int rc = SQLITE_OK;
BatchIndexEntry **apNew;
int nNew = (p->nHash ? p->nHash*2 : 512);
int i;
apNew = cbMalloc(&rc, sizeof(BatchIndexEntry*) * nNew);
if( rc==SQLITE_OK ){
int nHash = p->nHash;
p->nHash = nNew;
for(i=0; i<nHash; i++){
BatchIndexEntry *pEntry;
while( (pEntry=p->apHash[i])!=0 ){
int iHash = cbHash(p, pEntry);
p->apHash[i] = pEntry->pNext;
pEntry->pNext = apNew[iHash];
apNew[iHash] = pEntry;
}
}
cbFree(p->apHash);
p->apHash = apNew;
}
return rc;
}
/*
** Allocate a new sqlite3_changebatch object.
*/
int sqlite3changebatch_new(sqlite3 *db, sqlite3_changebatch **pp){
sqlite3_changebatch *pRet;
int rc = SQLITE_OK;
*pp = pRet = (sqlite3_changebatch*)cbMalloc(&rc, sizeof(sqlite3_changebatch));
if( pRet ){
pRet->db = db;
}
return rc;
}
/*
** Add a BatchIndex entry for index zIdx to table pTab.
*/
static int cbAddIndex(
sqlite3_changebatch *p,
BatchTable *pTab,
const char *zIdx,
int bPk
){
int nCol = 0;
sqlite3_stmt *pIndexInfo = 0;
BatchIndex *pNew = 0;
int rc;
char *zIndexInfo;
zIndexInfo = (char*)sqlite3_mprintf("PRAGMA main.index_info = %Q", zIdx);
if( zIndexInfo ){
rc = sqlite3_prepare_v2(p->db, zIndexInfo, -1, &pIndexInfo, 0);
sqlite3_free(zIndexInfo);
}else{
rc = SQLITE_NOMEM;
}
if( rc==SQLITE_OK ){
while( SQLITE_ROW==sqlite3_step(pIndexInfo) ){ nCol++; }
rc = sqlite3_reset(pIndexInfo);
}
pNew = (BatchIndex*)cbMalloc(&rc, sizeof(BatchIndex) + sizeof(int) * nCol);
if( rc==SQLITE_OK ){
pNew->nCol = nCol;
pNew->bPk = bPk;
pNew->aiCol = (int*)&pNew[1];
pNew->iId = p->iNextIdxId++;
while( SQLITE_ROW==sqlite3_step(pIndexInfo) ){
int i = sqlite3_column_int(pIndexInfo, 0);
int j = sqlite3_column_int(pIndexInfo, 1);
pNew->aiCol[i] = j;
}
rc = sqlite3_reset(pIndexInfo);
}
if( rc==SQLITE_OK ){
pNew->pNext = pTab->pIdx;
pTab->pIdx = pNew;
}else{
cbFree(pNew);
}
sqlite3_finalize(pIndexInfo);
return rc;
}
/*
** Free the object passed as the first argument.
*/
static void cbFreeTable(BatchTable *pTab){
BatchIndex *pIdx;
BatchIndex *pIdxNext;
for(pIdx=pTab->pIdx; pIdx; pIdx=pIdxNext){
pIdxNext = pIdx->pNext;
cbFree(pIdx);
}
cbFree(pTab);
}
/*
** Find or create the BatchTable object named zTab.
*/
static int cbFindTable(
sqlite3_changebatch *p,
const char *zTab,
BatchTable **ppTab
){
BatchTable *pRet = 0;
int rc = SQLITE_OK;
for(pRet=p->pTab; pRet; pRet=pRet->pNext){
if( 0==sqlite3_stricmp(zTab, pRet->zTab) ) break;
}
if( pRet==0 ){
int nTab = strlen(zTab);
pRet = (BatchTable*)cbMalloc(&rc, nTab + sizeof(BatchTable));
if( pRet ){
sqlite3_stmt *pIndexList = 0;
char *zIndexList = 0;
int rc2;
memcpy(pRet->zTab, zTab, nTab);
zIndexList = sqlite3_mprintf("PRAGMA main.index_list = %Q", zTab);
if( zIndexList==0 ){
rc = SQLITE_NOMEM;
}else{
rc = sqlite3_prepare_v2(p->db, zIndexList, -1, &pIndexList, 0);
sqlite3_free(zIndexList);
}
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pIndexList) ){
if( sqlite3_column_int(pIndexList, 2) ){
const char *zIdx = (const char*)sqlite3_column_text(pIndexList, 1);
const char *zTyp = (const char*)sqlite3_column_text(pIndexList, 3);
rc = cbAddIndex(p, pRet, zIdx, (zTyp[0]=='p'));
}
}
rc2 = sqlite3_finalize(pIndexList);
if( rc==SQLITE_OK ) rc = rc2;
if( rc==SQLITE_OK ){
pRet->pNext = p->pTab;
p->pTab = pRet;
}else{
cbFreeTable(pRet);
pRet = 0;
}
}
}
*ppTab = pRet;
return rc;
}
/*
** Extract value iVal from the changeset iterator passed as the first
** argument. Set *ppVal to point to the value before returning.
**
** This function attempts to extract the value using function xVal
** (which is always either sqlite3changeset_new or sqlite3changeset_old).
** If the call returns SQLITE_OK but does not supply an sqlite3_value*
** pointer, an attempt to extract the value is made using the xFallback
** function.
*/
static int cbGetChangesetValue(
sqlite3_changeset_iter *pIter,
int (*xVal)(sqlite3_changeset_iter*,int,sqlite3_value**),
int (*xFallback)(sqlite3_changeset_iter*,int,sqlite3_value**),
int iVal,
sqlite3_value **ppVal
){
int rc = xVal(pIter, iVal, ppVal);
if( rc==SQLITE_OK && *ppVal==0 && xFallback ){
rc = xFallback(pIter, iVal, ppVal);
}
return rc;
}
static int cbAddToHash(
sqlite3_changebatch *p,
sqlite3_changeset_iter *pIter,
BatchIndex *pIdx,
int (*xVal)(sqlite3_changeset_iter*,int,sqlite3_value**),
int (*xFallback)(sqlite3_changeset_iter*,int,sqlite3_value**),
int *pbConf
){
BatchIndexEntry *pNew;
int sz = pIdx->nCol;
int i;
int iOut = 0;
int rc = SQLITE_OK;
for(i=0; rc==SQLITE_OK && i<pIdx->nCol; i++){
sqlite3_value *pVal;
rc = cbGetChangesetValue(pIter, xVal, xFallback, pIdx->aiCol[i], &pVal);
if( rc==SQLITE_OK ){
int eType = 0;
if( pVal ) eType = sqlite3_value_type(pVal);
switch( eType ){
case 0:
case SQLITE_NULL:
return SQLITE_OK;
case SQLITE_INTEGER:
sz += 8;
break;
case SQLITE_FLOAT:
sz += 8;
break;
default:
assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
sz += sqlite3_value_bytes(pVal);
break;
}
}
}
pNew = cbMalloc(&rc, sizeof(BatchIndexEntry) + sz);
if( pNew ){
pNew->iChangesetId = p->iChangesetId;
pNew->iIdxId = pIdx->iId;
pNew->szRecord = sz;
for(i=0; i<pIdx->nCol; i++){
int eType;
sqlite3_value *pVal;
rc = cbGetChangesetValue(pIter, xVal, xFallback, pIdx->aiCol[i], &pVal);
if( rc!=SQLITE_OK ) break; /* coverage: condition is never true */
eType = sqlite3_value_type(pVal);
pNew->aRecord[iOut++] = eType;
switch( eType ){
case SQLITE_INTEGER: {
sqlite3_int64 i64 = sqlite3_value_int64(pVal);
memcpy(&pNew->aRecord[iOut], &i64, 8);
iOut += 8;
break;
}
case SQLITE_FLOAT: {
double d64 = sqlite3_value_double(pVal);
memcpy(&pNew->aRecord[iOut], &d64, sizeof(double));
iOut += sizeof(double);
break;
}
default: {
int nByte = sqlite3_value_bytes(pVal);
const char *z = (const char*)sqlite3_value_blob(pVal);
memcpy(&pNew->aRecord[iOut], z, nByte);
iOut += nByte;
break;
}
}
}
}
if( rc==SQLITE_OK && p->nEntry>=(p->nHash/2) ){
rc = cbHashResize(p);
}
if( rc==SQLITE_OK ){
BatchIndexEntry *pIter;
int iHash = cbHash(p, pNew);
assert( iHash>=0 && iHash<p->nHash );
for(pIter=p->apHash[iHash]; pIter; pIter=pIter->pNext){
if( pNew->szRecord==pIter->szRecord
&& 0==memcmp(pNew->aRecord, pIter->aRecord, pNew->szRecord)
){
if( pNew->iChangesetId!=pIter->iChangesetId ){
*pbConf = 1;
}
cbFree(pNew);
pNew = 0;
break;
}
}
if( pNew ){
pNew->pNext = p->apHash[iHash];
p->apHash[iHash] = pNew;
p->nEntry++;
}
}else{
cbFree(pNew);
}
return rc;
}
/*
** Add a changeset to the current batch.
*/
int sqlite3changebatch_add(sqlite3_changebatch *p, void *pBuf, int nBuf){
sqlite3_changeset_iter *pIter; /* Iterator opened on pBuf/nBuf */
int rc; /* Return code */
int bConf = 0; /* Conflict was detected */
rc = sqlite3changeset_start(&pIter, nBuf, pBuf);
if( rc==SQLITE_OK ){
int rc2;
for(rc2 = sqlite3changeset_next(pIter);
rc2==SQLITE_ROW;
rc2 = sqlite3changeset_next(pIter)
){
BatchTable *pTab;
BatchIndex *pIdx;
const char *zTab; /* Table this change applies to */
int nCol; /* Number of columns in table */
int op; /* UPDATE, INSERT or DELETE */
sqlite3changeset_op(pIter, &zTab, &nCol, &op, 0);
assert( op==SQLITE_INSERT || op==SQLITE_UPDATE || op==SQLITE_DELETE );
rc = cbFindTable(p, zTab, &pTab);
assert( pTab || rc!=SQLITE_OK );
if( pTab ){
for(pIdx=pTab->pIdx; pIdx && rc==SQLITE_OK; pIdx=pIdx->pNext){
if( op==SQLITE_UPDATE && pIdx->bPk ) continue;
if( op==SQLITE_UPDATE || op==SQLITE_DELETE ){
rc = cbAddToHash(p, pIter, pIdx, sqlite3changeset_old, 0, &bConf);
}
if( op==SQLITE_UPDATE || op==SQLITE_INSERT ){
rc = cbAddToHash(p, pIter, pIdx,
sqlite3changeset_new, sqlite3changeset_old, &bConf
);
}
}
}
if( rc!=SQLITE_OK ) break;
}
rc2 = sqlite3changeset_finalize(pIter);
if( rc==SQLITE_OK ) rc = rc2;
}
if( rc==SQLITE_OK && bConf ){
rc = SQLITE_CONSTRAINT;
}
p->iChangesetId++;
return rc;
}
/*
** Zero an existing changebatch object.
*/
void sqlite3changebatch_zero(sqlite3_changebatch *p){
int i;
for(i=0; i<p->nHash; i++){
BatchIndexEntry *pEntry;
BatchIndexEntry *pNext;
for(pEntry=p->apHash[i]; pEntry; pEntry=pNext){
pNext = pEntry->pNext;
cbFree(pEntry);
}
}
cbFree(p->apHash);
p->nHash = 0;
p->apHash = 0;
}
/*
** Delete a changebatch object.
*/
void sqlite3changebatch_delete(sqlite3_changebatch *p){
BatchTable *pTab;
BatchTable *pTabNext;
sqlite3changebatch_zero(p);
for(pTab=p->pTab; pTab; pTab=pTabNext){
pTabNext = pTab->pNext;
cbFreeTable(pTab);
}
cbFree(p);
}
/*
** Return the db handle.
*/
sqlite3 *sqlite3changebatch_db(sqlite3_changebatch *p){
return p->db;
}
#endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */
+82
View File
@@ -0,0 +1,82 @@
#if !defined(SQLITECHANGEBATCH_H_)
#define SQLITECHANGEBATCH_H_ 1
typedef struct sqlite3_changebatch sqlite3_changebatch;
/*
** Create a new changebatch object for detecting conflicts between
** changesets associated with a schema equivalent to that of the "main"
** database of the open database handle db passed as the first
** parameter. It is the responsibility of the caller to ensure that
** the database handle is not closed until after the changebatch
** object has been deleted.
**
** A changebatch object is used to detect batches of non-conflicting
** changesets. Changesets that do not conflict may be applied to the
** target database in any order without affecting the final state of
** the database.
**
** The changebatch object only works reliably if PRIMARY KEY and UNIQUE
** constraints on tables affected by the changesets use collation
** sequences that are equivalent to built-in collation sequence
** BINARY for the == operation.
**
** If successful, SQLITE_OK is returned and (*pp) set to point to
** the new changebatch object. If an error occurs, an SQLite error
** code is returned and the final value of (*pp) is undefined.
*/
int sqlite3changebatch_new(sqlite3 *db, sqlite3_changebatch **pp);
/*
** Argument p points to a buffer containing a changeset n bytes in
** size. Assuming no error occurs, this function returns SQLITE_OK
** if the changeset does not conflict with any changeset passed
** to an sqlite3changebatch_add() call made on the same
** sqlite3_changebatch* handle since the most recent call to
** sqlite3changebatch_zero(). If the changeset does conflict with
** an earlier such changeset, SQLITE_CONSTRAINT is returned. Or,
** if an error occurs, some other SQLite error code may be returned.
**
** One changeset is said to conflict with another if
** either:
**
** * the two changesets contain operations (INSERT, UPDATE or
** DELETE) on the same row, identified by primary key, or
**
** * the two changesets contain operations (INSERT, UPDATE or
** DELETE) on rows with identical values in any combination
** of fields constrained by a UNIQUE constraint.
**
** Even if this function returns SQLITE_CONFLICT, the current
** changeset is added to the internal data structures - so future
** calls to this function may conflict with it. If this function
** returns any result code other than SQLITE_OK or SQLITE_CONFLICT,
** the result of any future call to sqlite3changebatch_add() is
** undefined.
**
** Only changesets may be passed to this function. Passing a
** patchset to this function results in an SQLITE_MISUSE error.
*/
int sqlite3changebatch_add(sqlite3_changebatch*, void *p, int n);
/*
** Zero a changebatch object. This causes the records of all earlier
** calls to sqlite3changebatch_add() to be discarded.
*/
void sqlite3changebatch_zero(sqlite3_changebatch*);
/*
** Return a copy of the first argument passed to the sqlite3changebatch_new()
** call used to create the changebatch object passed as the only argument
** to this function.
*/
sqlite3 *sqlite3changebatch_db(sqlite3_changebatch*);
/*
** Delete a changebatch object.
*/
void sqlite3changebatch_delete(sqlite3_changebatch*);
#endif /* !defined(SQLITECHANGEBATCH_H_) */
+39 -17
View File
@@ -25,6 +25,13 @@ typedef struct SessionInput SessionInput;
# endif
#endif
/*
** The three different types of changesets generated.
*/
#define SESSIONS_PATCHSET 0
#define SESSIONS_CHANGESET 1
#define SESSIONS_FULLCHANGESET 2
static int sessions_strm_chunk_size = SESSIONS_STRM_CHUNK_SIZE;
typedef struct SessionHook SessionHook;
@@ -2233,7 +2240,7 @@ static void sessionAppendCol(
*/
static int sessionAppendUpdate(
SessionBuffer *pBuf, /* Buffer to append to */
int bPatchset, /* True for "patchset", 0 for "changeset" */
int ePatchset, /* True for "patchset", 0 for "changeset" */
sqlite3_stmt *pStmt, /* Statement handle pointing at new row */
SessionChange *p, /* Object containing old values */
u8 *abPK /* Boolean array - true for PK columns */
@@ -2297,8 +2304,8 @@ static int sessionAppendUpdate(
/* Add a field to the old.* record. This is omitted if this modules is
** currently generating a patchset. */
if( bPatchset==0 ){
if( bChanged || abPK[i] ){
if( ePatchset!=SESSIONS_PATCHSET ){
if( ePatchset==SESSIONS_FULLCHANGESET || bChanged || abPK[i] ){
sessionAppendBlob(pBuf, pCsr, nAdvance, &rc);
}else{
sessionAppendByte(pBuf, 0, &rc);
@@ -2307,7 +2314,7 @@ static int sessionAppendUpdate(
/* Add a field to the new.* record. Or the only record if currently
** generating a patchset. */
if( bChanged || (bPatchset && abPK[i]) ){
if( bChanged || (ePatchset==SESSIONS_PATCHSET && abPK[i]) ){
sessionAppendCol(&buf2, pStmt, i, &rc);
}else{
sessionAppendByte(&buf2, 0, &rc);
@@ -2333,7 +2340,7 @@ static int sessionAppendUpdate(
*/
static int sessionAppendDelete(
SessionBuffer *pBuf, /* Buffer to append to */
int bPatchset, /* True for "patchset", 0 for "changeset" */
int eChangeset, /* One of SESSIONS_CHANGESET etc. */
SessionChange *p, /* Object containing old values */
int nCol, /* Number of columns in table */
u8 *abPK /* Boolean array - true for PK columns */
@@ -2343,7 +2350,7 @@ static int sessionAppendDelete(
sessionAppendByte(pBuf, SQLITE_DELETE, &rc);
sessionAppendByte(pBuf, p->bIndirect, &rc);
if( bPatchset==0 ){
if( eChangeset!=SESSIONS_PATCHSET ){
sessionAppendBlob(pBuf, p->aRecord, p->nRecord, &rc);
}else{
int i;
@@ -2516,12 +2523,12 @@ static int sessionSelectBind(
*/
static void sessionAppendTableHdr(
SessionBuffer *pBuf, /* Append header to this buffer */
int bPatchset, /* Use the patchset format if true */
int ePatchset, /* Use the patchset format if true */
SessionTable *pTab, /* Table object to append header for */
int *pRc /* IN/OUT: Error code */
){
/* Write a table header */
sessionAppendByte(pBuf, (bPatchset ? 'P' : 'T'), pRc);
sessionAppendByte(pBuf, (ePatchset==SESSIONS_PATCHSET) ? 'P' : 'T', pRc);
sessionAppendVarint(pBuf, pTab->nCol, pRc);
sessionAppendBlob(pBuf, pTab->abPK, pTab->nCol, pRc);
sessionAppendBlob(pBuf, (u8 *)pTab->zName, (int)strlen(pTab->zName)+1, pRc);
@@ -2539,7 +2546,7 @@ static void sessionAppendTableHdr(
*/
static int sessionGenerateChangeset(
sqlite3_session *pSession, /* Session object */
int bPatchset, /* True for patchset, false for changeset */
int ePatchset, /* One of SESSIONS_CHANGESET etc. */
int (*xOutput)(void *pOut, const void *pData, int nData),
void *pOut, /* First argument for xOutput */
int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
@@ -2586,7 +2593,7 @@ static int sessionGenerateChangeset(
}
/* Write a table header */
sessionAppendTableHdr(&buf, bPatchset, pTab, &rc);
sessionAppendTableHdr(&buf, ePatchset, pTab, &rc);
/* Build and compile a statement to execute: */
if( rc==SQLITE_OK ){
@@ -2611,10 +2618,10 @@ static int sessionGenerateChangeset(
}
}else{
assert( abPK!=0 ); /* Because sessionSelectStmt() returned ok */
rc = sessionAppendUpdate(&buf, bPatchset, pSel, p, abPK);
rc = sessionAppendUpdate(&buf, ePatchset, pSel, p, abPK);
}
}else if( p->op!=SQLITE_INSERT ){
rc = sessionAppendDelete(&buf, bPatchset, p, nCol, abPK);
rc = sessionAppendDelete(&buf, ePatchset, p, nCol, abPK);
}
if( rc==SQLITE_OK ){
rc = sqlite3_reset(pSel);
@@ -2674,7 +2681,8 @@ int sqlite3session_changeset(
int rc;
if( pnChangeset==0 || ppChangeset==0 ) return SQLITE_MISUSE;
rc = sessionGenerateChangeset(pSession, 0, 0, 0, pnChangeset,ppChangeset);
rc = sessionGenerateChangeset(
pSession, SESSIONS_CHANGESET, 0, 0, pnChangeset, ppChangeset);
assert( rc || pnChangeset==0
|| pSession->bEnableSize==0 || *pnChangeset<=pSession->nMaxChangesetSize
);
@@ -2690,7 +2698,8 @@ int sqlite3session_changeset_strm(
void *pOut
){
if( xOutput==0 ) return SQLITE_MISUSE;
return sessionGenerateChangeset(pSession, 0, xOutput, pOut, 0, 0);
return sessionGenerateChangeset(
pSession, SESSIONS_CHANGESET, xOutput, pOut, 0, 0);
}
/*
@@ -2702,7 +2711,8 @@ int sqlite3session_patchset_strm(
void *pOut
){
if( xOutput==0 ) return SQLITE_MISUSE;
return sessionGenerateChangeset(pSession, 1, xOutput, pOut, 0, 0);
return sessionGenerateChangeset(
pSession, SESSIONS_PATCHSET, xOutput, pOut, 0, 0);
}
/*
@@ -2718,9 +2728,20 @@ int sqlite3session_patchset(
void **ppPatchset /* OUT: Buffer containing changeset */
){
if( pnPatchset==0 || ppPatchset==0 ) return SQLITE_MISUSE;
return sessionGenerateChangeset(pSession, 1, 0, 0, pnPatchset, ppPatchset);
return sessionGenerateChangeset(
pSession, SESSIONS_PATCHSET, 0, 0, pnPatchset, ppPatchset);
}
int sqlite3session_fullchangeset(
sqlite3_session *pSession, /* Session object */
int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
void **ppChangeset /* OUT: Buffer containing changeset */
){
return sessionGenerateChangeset(
pSession, SESSIONS_FULLCHANGESET, 0, 0, pnChangeset, ppChangeset);
}
/*
** Enable or disable the session object passed as the first argument.
*/
@@ -5263,10 +5284,11 @@ static int sessionChangegroupOutput(
** hash tables attached to the SessionTable objects in list p->pList.
*/
for(pTab=pGrp->pList; rc==SQLITE_OK && pTab; pTab=pTab->pNext){
int eChangeset = pGrp->bPatch ? SESSIONS_PATCHSET : SESSIONS_CHANGESET;
int i;
if( pTab->nEntry==0 ) continue;
sessionAppendTableHdr(&buf, pGrp->bPatch, pTab, &rc);
sessionAppendTableHdr(&buf, eChangeset, pTab, &rc);
for(i=0; i<pTab->nChange; i++){
SessionChange *p;
for(p=pTab->apChange[i]; p; p=p->pNext){
+13
View File
@@ -355,6 +355,19 @@ int sqlite3session_changeset(
void **ppChangeset /* OUT: Buffer containing changeset */
);
/*
** CAPI3REF: Generate A Full Changeset From A Session Object
**
** This function is similar to sqlite3session_changeset(), except that for
** each row affected by an UPDATE statement, all old.* values are recorded
** as part of the changeset, not just those modified.
*/
int sqlite3session_fullchangeset(
sqlite3_session *pSession, /* Session object */
int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
void **ppChangeset /* OUT: Buffer containing changeset */
);
/*
** CAPI3REF: Return An Upper-limit For The Size Of The Changeset
** METHOD: sqlite3_session
+147 -17
View File
@@ -221,6 +221,7 @@ static int testStreamOutput(
** $session indirect INTEGER
** $session patchset
** $session table_filter SCRIPT
** $session fullchangeset
*/
static int SQLITE_TCLAPI test_session_cmd(
void *clientData,
@@ -234,20 +235,20 @@ static int SQLITE_TCLAPI test_session_cmd(
const char *zSub;
int nArg;
const char *zMsg;
int iSub;
} aSub[] = {
{ "attach", 1, "TABLE", }, /* 0 */
{ "changeset", 0, "", }, /* 1 */
{ "delete", 0, "", }, /* 2 */
{ "enable", 1, "BOOL", }, /* 3 */
{ "indirect", 1, "BOOL", }, /* 4 */
{ "isempty", 0, "", }, /* 5 */
{ "table_filter", 1, "SCRIPT", }, /* 6 */
{ "attach", 1, "TABLE" }, /* 0 */
{ "changeset", 0, "" }, /* 1 */
{ "delete", 0, "" }, /* 2 */
{ "enable", 1, "BOOL" }, /* 3 */
{ "indirect", 1, "BOOL" }, /* 4 */
{ "isempty", 0, "" }, /* 5 */
{ "table_filter", 1, "SCRIPT" }, /* 6 */
{ "patchset", 0, "", }, /* 7 */
{ "diff", 2, "FROMDB TBL", }, /* 8 */
{ "memory_used", 0, "", }, /* 9 */
{ "changeset_size", 0, "", }, /* 10 */
{ "object_config_size", 1, "INTEGER", }, /* 11 */
{ "diff", 2, "FROMDB TBL" }, /* 8 */
{ "fullchangeset",0, "" }, /* 9 */
{ "memory_used", 0, "", }, /* 10 */
{ "changeset_size", 0, "", }, /* 11 */
{ "object_config_size", 1, "INTEGER", }, /* 12 */
{ 0 }
};
int iSub;
@@ -277,10 +278,11 @@ static int SQLITE_TCLAPI test_session_cmd(
break;
}
case 9: /* fullchangeset */
case 7: /* patchset */
case 1: { /* changeset */
TestSessionsBlob o = {0, 0};
if( test_tcl_integer(interp, SESSION_STREAM_TCL_VAR) ){
if( iSub!=9 && test_tcl_integer(interp, SESSION_STREAM_TCL_VAR) ){
void *pCtx = (void*)&o;
if( iSub==7 ){
rc = sqlite3session_patchset_strm(pSession, testStreamOutput, pCtx);
@@ -290,6 +292,8 @@ static int SQLITE_TCLAPI test_session_cmd(
}else{
if( iSub==7 ){
rc = sqlite3session_patchset(pSession, &o.n, &o.p);
}else if( iSub==9 ){
rc = sqlite3session_fullchangeset(pSession, &o.n, &o.p);
}else{
rc = sqlite3session_changeset(pSession, &o.n, &o.p);
}
@@ -304,6 +308,7 @@ static int SQLITE_TCLAPI test_session_cmd(
break;
}
case 2: /* delete */
Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
break;
@@ -354,18 +359,18 @@ static int SQLITE_TCLAPI test_session_cmd(
break;
}
case 9: { /* memory_used */
case 10: { /* memory_used */
sqlite3_int64 nMalloc = sqlite3session_memory_used(pSession);
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nMalloc));
break;
}
case 10: {
case 11: {
sqlite3_int64 nSize = sqlite3session_changeset_size(pSession);
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nSize));
break;
}
case 11: {
case 12: {
int rc;
int iArg;
if( Tcl_GetIntFromObj(interp, objv[2], &iArg) ){
@@ -521,7 +526,7 @@ static int test_filter_handler(
Tcl_DecrRefCount(pEval);
return res;
}
}
static int test_conflict_handler(
void *pCtx, /* Pointer to TestConflictHandler structure */
@@ -1163,6 +1168,127 @@ static int SQLITE_TCLAPI test_sqlite3session_foreach(
return TCL_OK;
}
#include "sqlite3changebatch.h"
typedef struct TestChangebatch TestChangebatch;
struct TestChangebatch {
sqlite3_changebatch *pChangebatch;
};
/*
** Tclcmd: $changebatch add BLOB
** $changebatch zero
** $changebatch delete
*/
static int SQLITE_TCLAPI test_changebatch_cmd(
void *clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
TestChangebatch *p = (TestChangebatch*)clientData;
sqlite3_changebatch *pChangebatch = p->pChangebatch;
struct SessionSubcmd {
const char *zSub;
int nArg;
const char *zMsg;
int iSub;
} aSub[] = {
{ "add", 1, "CHANGESET", }, /* 0 */
{ "zero", 0, "", }, /* 1 */
{ "delete", 0, "", }, /* 2 */
{ 0 }
};
int iSub;
int rc;
if( objc<2 ){
Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
return TCL_ERROR;
}
rc = Tcl_GetIndexFromObjStruct(interp,
objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub
);
if( rc!=TCL_OK ) return rc;
if( objc!=2+aSub[iSub].nArg ){
Tcl_WrongNumArgs(interp, 2, objv, aSub[iSub].zMsg);
return TCL_ERROR;
}
switch( iSub ){
case 0: { /* add */
int nArg;
unsigned char *pArg = Tcl_GetByteArrayFromObj(objv[2], &nArg);
rc = sqlite3changebatch_add(pChangebatch, pArg, nArg);
if( rc!=SQLITE_OK && rc!=SQLITE_CONSTRAINT ){
return test_session_error(interp, rc, 0);
}else{
extern const char *sqlite3ErrName(int);
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
}
break;
}
case 1: { /* zero */
sqlite3changebatch_zero(pChangebatch);
break;
}
case 2: /* delete */
Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
break;
}
return TCL_OK;
}
static void SQLITE_TCLAPI test_changebatch_del(void *clientData){
TestChangebatch *p = (TestChangebatch*)clientData;
sqlite3changebatch_delete(p->pChangebatch);
ckfree((char*)p);
}
/*
** Tclcmd: sqlite3changebatch CMD DB-HANDLE
*/
static int SQLITE_TCLAPI test_sqlite3changebatch(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
sqlite3 *db;
Tcl_CmdInfo info;
int rc; /* sqlite3session_create() return code */
TestChangebatch *p; /* New wrapper object */
if( objc!=3 ){
Tcl_WrongNumArgs(interp, 1, objv, "CMD DB-HANDLE");
return TCL_ERROR;
}
if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[2]), &info) ){
Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(objv[2]), 0);
return TCL_ERROR;
}
db = *(sqlite3 **)info.objClientData;
p = (TestChangebatch*)ckalloc(sizeof(TestChangebatch));
memset(p, 0, sizeof(TestChangebatch));
rc = sqlite3changebatch_new(db, &p->pChangebatch);
if( rc!=SQLITE_OK ){
ckfree((char*)p);
return test_session_error(interp, rc, 0);
}
Tcl_CreateObjCommand(
interp, Tcl_GetString(objv[1]), test_changebatch_cmd, (ClientData)p,
test_changebatch_del
);
Tcl_SetObjResult(interp, objv[1]);
return TCL_OK;
}
/*
** tclcmd: CMD configure REBASE-BLOB
** tclcmd: CMD rebase CHANGESET
@@ -1345,6 +1471,10 @@ int TestSession_Init(Tcl_Interp *interp){
Tcl_CreateObjCommand(interp, p->zCmd, p->xProc, 0, 0);
}
Tcl_CreateObjCommand(
interp, "sqlite3changebatch", test_sqlite3changebatch, 0, 0
);
return TCL_OK;
}
+4
View File
@@ -441,6 +441,7 @@ TESTSRC2 = \
$(TOP)/ext/async/sqlite3async.c \
$(TOP)/ext/misc/stmt.c \
$(TOP)/ext/session/sqlite3session.c \
$(TOP)/ext/session/sqlite3changebatch.c \
$(TOP)/ext/session/test_session.c \
fts5.c
@@ -1007,6 +1008,9 @@ THREADTEST3_SRC = $(TOP)/test/threadtest3.c \
threadtest3$(EXE): sqlite3.o $(THREADTEST3_SRC) $(TOP)/src/test_multiplex.c
$(TCCX) $(TOP)/test/threadtest3.c $(TOP)/src/test_multiplex.c sqlite3.o -o $@ $(THREADLIB)
bc_test1$(EXE): sqlite3.o $(TOP)/test/bc_test1.c $(TOP)/test/tt3_core.c
$(TCCX) $(TOP)/test/bc_test1.c sqlite3.o -o $@ $(THREADLIB)
threadtest: threadtest3$(EXE)
./threadtest3$(EXE)
+83 -63
View File
@@ -1,13 +1,13 @@
C Version\s3.37.0
D 2021-11-27T14:13:22.430
C Merge\s3.37.2\schanges\sinto\sthe\sbegin-concurrent-pnu\sbranch.
D 2022-01-07T14:09:33.971
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F Makefile.in 0e91c42a1dd13a569b1fa4f4dfb7d3632f3164a1c05c71341533d67db5b641dd
F Makefile.in f4d094d775d43e371d0f45d1d2bce6f3455a0f8d06aaf83f023787b31d3a54b8
F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241
F Makefile.msc 88f05063ee36a5fb77d69b877ef824d0743b5325c95bfbf74d6ff17bd1c9fb1f
F README.md 27fb76aa7eb57ed63a53bbba7292b6bf71f51125554f79f16b5d040edd1e6110
F VERSION c6595fef606851f2bc3ebed6a7386c73751835fc909feab7c093739fa4b3c1d1
F VERSION b8218d35896c0f381cf64a499abbab64eeb86b40116a7d35c5a39c6677e8bb89
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90
@@ -34,10 +34,11 @@ F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63
F config.guess 883205ddf25b46f10c181818bf42c09da9888884af96f79e1719264345053bd6
F config.h.in 6376abec766e9a0785178b1823b5a587e9f1ccbc
F config.sub c2d0260f17f3e4bc0b6808fccf1b291cb5e9126c14fc5890efc77b9fd0175559
F configure e8003577b8c433cf61371d27d4beeca3a3ed37611a504ff5f4d4286923b87d03 x
F configure.ac 4e4b58b32f88c8da9914a2f2c3158f80e69907eccc019fcc7e3ba14ffd91c640
F configure f8a03394ccec572e76a570aa858ccd24e0d54fd76c995b7220a4dfda8b899d27 x
F configure.ac c8ba54bac7e73e000acdfef5e394fe21a3876aa09d0f5c07131bf5ac5a525299
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
F doc/F2FS.txt c1d4a0ae9711cfe0e1d8b019d154f1c29e0d3abfe820787ba1e9ed7691160fcd
F doc/begin_concurrent.md 4bee2c3990d1eb800f1ce3726a911292a8e4b889300b2ffd4b08d357370db299
F doc/lemon.html efc0cd2345d66905505d98f862e1c571512def0ceb5b016cb658fd4918eb76a3
F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710
F doc/trusted-schema.md 33625008620e879c7bcfbbfa079587612c434fa094d338b08242288d358c3e8a
@@ -84,7 +85,7 @@ F ext/fts3/README.content b9078d0843a094d86af0d48dffbff13c906702b4c3558012e67b9c
F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a
F ext/fts3/README.tokenizers b92bdeb8b46503f0dd301d364efc5ef59ef9fa8e2758b8e742f39fa93a2e422d
F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
F ext/fts3/fts3.c da41de21622774b1cb8c3415e8d2a6961ad7c978ab534f2a54434f76a5c4dfbc
F ext/fts3/fts3.c 6bfdc941372ee431e3d362986674a8708eb0e700b28c3d35d46d953a9351155f
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
F ext/fts3/fts3Int.h cff59b8b13dafe9d59924a5d710f771ed8b121a55cccbc99b6e2a723fcde14dc
F ext/fts3/fts3_aux.c f0dc9bd98582615b7750218899bd0c729879b6bbf94d1be57ca1833ff49afc6f
@@ -119,7 +120,7 @@ F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b7292
F ext/fts5/fts5_config.c 501e7d3566bc92766b0e11c0109a7c5a6146bc41144195459af5422f6c2078aa
F ext/fts5/fts5_expr.c fcd0770d53028c2b53a15d0f53bf6d0e01b1bf3dd97630b9fedf0801f03aa3ec
F ext/fts5/fts5_hash.c d4fb70940359f2120ccd1de7ffe64cc3efe65de9e8995b822cd536ff64c96982
F ext/fts5/fts5_index.c a3ada4897c3b14b8a15a8695d2cb3a46b5761137aae0964fc44efe96a877ddd0
F ext/fts5/fts5_index.c b1b2e5d4a9e3b54c740d8354cc47e3fa879f54c2176de55e0b882dab45ab7b07
F ext/fts5/fts5_main.c 7c6092a53e6802962fa07b0fad3e61cb077b6c98b74b727d8d44ac2cf63bd914
F ext/fts5/fts5_storage.c 76c6085239eb44424004c022e9da17a5ecd5aaec859fba90ad47d3b08f4c8082
F ext/fts5/fts5_tcl.c b1445cbe69908c411df8084a10b2485500ac70a9c747cdc8cda175a3da59d8ae
@@ -163,6 +164,7 @@ F ext/fts5/test/fts5corrupt2.test 7453752ba12ce91690c469a6449d412561cc604b1dec99
F ext/fts5/test/fts5corrupt3.test 0e473620582a53ac61f468f364db8a151c1e18d2a879b16439d172c12c4c9828
F ext/fts5/test/fts5corrupt4.test f4c08e2182a48d8b70975fd869ee5391855c06d8a0ff87b6a2529e7c5a88a1d3
F ext/fts5/test/fts5corrupt5.test 550d0884c14424f9acad051a741f1dd99ec9342277d938e91ff3daf9123d1209
F ext/fts5/test/fts5corrupt6.test bf8eeae07825b088b9665d9d8e4accbd8dc9bf3cb85b6c64cf6c9e18ccc420a4
F ext/fts5/test/fts5delete.test 619295b20dbc1d840b403ee07c878f52378849c3c02e44f2ee143b3e978a0aa7
F ext/fts5/test/fts5detail.test 54015e9c43ec4ba542cfb93268abdf280e0300f350efd08ee411284b03595cc4
F ext/fts5/test/fts5determin.test 1b77879b2ae818b5b71c859e534ee334dac088b7cf3ff3bf76a2c82b1c788d11
@@ -425,11 +427,13 @@ F ext/rtree/rtreedoc2.test 194ebb7d561452dcdc10bf03f44e30c082c2f0c14efeb07f5e02c
F ext/rtree/rtreedoc3.test 555a878c4d79c4e37fa439a1c3b02ee65d3ebaf75d9e8d96a9c55d66db3efbf8
F ext/rtree/rtreefuzz001.test 0fc793f67897c250c5fde96cefee455a5e2fb92f4feeabde5b85ea02040790ee
F ext/rtree/sqlite3rtree.h 03c8db3261e435fbddcfc961471795cbf12b24e03001d0015b2636b0f3881373
F ext/rtree/test_rtreedoc.c 0167f9243a56d08e79230f604f3979d6b7cde4816355acf7a7d436d9d788bf38
F ext/rtree/test_rtreedoc.c 5ad4029d6804eb9efafcac1598a9e0582f6119e48f818854f5b4db1788ca8bd4
F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de
F ext/rtree/util/randomshape.tcl 54ee03d0d4a1c621806f7f44d5b78d2db8fac26e0e8687c36c4bd0203b27dbff
F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
F ext/rtree/visual01.txt e9c2564083bcd30ec51b07f881bffbf0e12b50a3f6fced0c222c5c1d2f94ac66
F ext/session/changebatch1.test 9ceaa8f7b2a505933e250fbe6cbc550e4ce1e59d
F ext/session/changebatchfault.test be49c793219bf387ad692a60856b921f0854ad6d
F ext/session/changeset.c 7a1e6a14c7e92d36ca177e92e88b5281acd709f3b726298dc34ec0fb58869cb5
F ext/session/changesetfuzz.c 227076ab0ae4447d742c01ee88a564da6478bbf26b65108bf8fac9cd8b0b24aa
F ext/session/changesetfuzz1.test 2e1b90d888fbf0eea5e1bd2f1e527a48cc85f8e0ff75df1ec4e320b21f580b3a
@@ -448,7 +452,7 @@ F ext/session/sessionD.test 4f91d0ca8afc4c3969c72c9f0b5ea9527e21de29039937d0d973
F ext/session/sessionE.test b2010949c9d7415306f64e3c2072ddabc4b8250c98478d3c0c4d064bce83111d
F ext/session/sessionF.test d37ed800881e742c208df443537bf29aa49fd56eac520d0f0c6df3e6320f3401
F ext/session/sessionG.test 3828b944cd1285f4379340fd36f8b64c464fc84df6ff3ccbc95578fd87140b9c
F ext/session/sessionH.test b17afdbd3b8f17e9bab91e235acf167cf35485db2ab2df0ea8893fbb914741a4
F ext/session/sessionH.test ecabc041e04e48671a94cd7c02993bc73f75dced943280a026e89cdf64374eef
F ext/session/session_common.tcl f613174665456b2d916ae8df3e5735092a1c1712f36f46840172e9a01e8cc53e
F ext/session/session_speed_test.c dcf0ef58d76b70c8fbd9eab3be77cf9deb8bc1638fed8be518b62d6cbdef88b3
F ext/session/sessionat.test 52993535f1230a42f70886643574ba7ae60ef854f8add9c8e3fcc3eb5c564bd2
@@ -463,16 +467,18 @@ F ext/session/sessionrebase.test ccfa716b23bd1d3b03217ee58cfd90c78d4b99f53e6a9a2
F ext/session/sessionsize.test 6f644aff31c7f1e4871e9ff3542766e18da68fc7e587b83a347ea9820a002dd8
F ext/session/sessionstat1.test 218d351cf9fcd6648f125a26b607b140310160184723c2666091b54450a68fb5
F ext/session/sessionwor.test 6fd9a2256442cebde5b2284936ae9e0d54bde692d0f5fd009ecef8511f4cf3fc
F ext/session/sqlite3session.c 1d019c5caf51936ef24c761db63552b06e0e0d951c8740bba9639b17fa0cb107
F ext/session/sqlite3session.h 0907de79bc13a2e3af30a6dc29acc60792a3eaf7d33d44cf52500d0f3c2b2171
F ext/session/test_session.c f433f68a8a8c64b0f5bc74dc725078f12483301ad4ae8375205eef790274a787
F ext/session/sqlite3changebatch.c d5553b79e012ee2cb06c0a96bdf9dfe19e66354390ea0036cc46c4953142d517
F ext/session/sqlite3changebatch.h e72016998c9a22d439ddfd547b69e1ebac810c24
F ext/session/sqlite3session.c 421854336ece362552508426d89091c1832148066f0641e8d72e22f58d9241f3
F ext/session/sqlite3session.h c26e54521584ad26dcb459171d94bbe6412ca26ed4613551c7f0c80aad129abd
F ext/session/test_session.c 226461035031632255c8098f3bcfa4ea27f0b051efd88857a2a36bcea5c294b9
F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
F main.mk 002e77acdfeb08d1d8f4d360b01e130aa243fb5701728e81fac9085794f27155
F main.mk 81e3b80b9efe1227bcdb64e3169b703fbbc0b36b83e4239df777eba87bc2622a
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
@@ -489,12 +495,12 @@ F src/analyze.c 7518b99e07c5494111fe3bd867f28f804b6c5c1ad0703ec3d116de9bab3fa516
F src/attach.c e3f9d9a2a4a844750f3f348f37afb244535f21382cbfcd840152cb21cb41cfaf
F src/auth.c f4fa91b6a90bbc8e0d0f738aa284551739c9543a367071f55574681e0f24f8cf
F src/backup.c 3014889fa06e20e6adfa0d07b60097eec1f6e5b06671625f476a714d2356513d
F src/bitvec.c 7c849aac407230278445cb069bebc5f89bf2ddd87c5ed9459b070a9175707b3d
F src/bitvec.c 3907fcbe8a0c8c2db58d97087d15cdabbf2842adb9125df9ab9ff87d3db16775
F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6
F src/btree.c 13b965a0f3cd57221e3b4e61e24452ec264a5b163de347b03b5039ddcd95cd54
F src/btree.h 74d64b8f28cfa4a894d14d4ed64fa432cd697b98b61708d4351482ae15913e22
F src/btreeInt.h ee9348c4cb9077243b049edc93a82c1f32ca48baeabf2140d41362b9f9139ff7
F src/build.c c46bd4f5a69f398410c4472f7c1c4291fb8078d2c9758a2dad5916edd1d30ecc
F src/btree.c 163c266508279ccec096e3492edef9702682d0babd67e8e74dea8d00e58fa417
F src/btree.h 900067641b64d619e6e2a93bd115c952a52f41d3bee32e551e2a4ceee05fc431
F src/btreeInt.h 3f19f0be5af0b68cff55e58df4b11e7a0692d9e8a820ceaeba4084659a86cf28
F src/build.c fba6792e5d9852620e6a6df4229dc3ffbe9c6a2340e858c70605385d1d083232
F src/callback.c 106b585da1edd57d75fa579d823a5218e0bf37f191dbf7417eeb4a8a9a267dbc
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
F src/ctime.c 8159d5f706551861c18ec6c8f6bdf105e15ea00367f05d9ab65d31a1077facc1
@@ -502,19 +508,19 @@ F src/date.c fa928630fecf1d436cdc7a7a5c950c781709023ca782c21b7a43cc7361a9451e
F src/dbpage.c 8a01e865bf8bc6d7b1844b4314443a6436c07c3efe1d488ed89e81719047833a
F src/dbstat.c 861e08690fcb0f2ee1165eff0060ea8d4f3e2ea10f80dab7d32ad70443a6ff2d
F src/delete.c 0c151975fa99560767d7747f9b60543d0093d9f8b89f13d2d6058e9c83ad19e7
F src/expr.c 4b6dfb224b6234ff4f529023993b503048e1b045ff49cbb911e7d28a28cca795
F src/expr.c 827179c78d2ca7cc318392811de8151c60eacf7ce804b13e61bb7ef38f954846
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c 187b67af20c5795953a592832c5d985e4313fe503ebd8f95e3e9e9ad5a730bb5
F src/func.c 1cfb09d7ffca81238eccefdb0293e1f5b7cfebbd1816dfad5ec6024742a7496b
F src/fkey.c 5b73f7a7c00f06017531a5bd258cbc2c7a294e55a7f84a729fe27aa525242560
F src/func.c a5dce8a8ff85310e2c88262a25bcded0a7e92be9a4bda24bbf6eab78e99c2f13
F src/global.c 1f56aead86e8a18c4415638f5e6c4d0a0550427f4b3f5d065ba5164cc09c22e8
F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19
F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51
F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
F src/insert.c 7fcbbe9114ac402ea3c0c6a3810f13fc89cae8131ea1659ec472be7caac10192
F src/insert.c 04c9b133be6152b4f229aab8e38230b3bf6d2e7e76455a893bff5f70e566237b
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
F src/loadext.c e1dcff1c916bf6834e150b492eddda5d9792453182d2ad64294d2266b6e93c4c
F src/main.c 7bd4fdc41ef53535271a1816ff043ba153cda03842b444b6e2f57b27b2cb9090
F src/main.c cd5a17d7b575917fc6ec90205aa4b516839573b73e574df6ca1e6e944f8b89f5
F src/malloc.c ef796bcc0e81d845d59a469f1cf235056caf9024172fd524e32136e65593647b
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de
@@ -522,7 +528,7 @@ F src/mem2.c c8bfc9446fd0798bddd495eb5d9dbafa7d4b7287d8c22d50a83ac9daa26d8a75
F src/mem3.c 30301196cace2a085cbedee1326a49f4b26deff0af68774ca82c1f7c06fda4f6
F src/mem5.c 9bf955937b07f8c32541c8a9991f33ce3173d944
F src/memdb.c c2dc88f97c410eb68a24468344b65526685e18354ddfd15906750c1eaf9dc2dd
F src/memjournal.c a85f0dc5c02a42453d0bc3819ecfb5666cb6433e5deefcd93ccbe05c9f088b83
F src/memjournal.c ff4336a98b05ede2adee7595f22d6f7d1cdc6bf0f0a5c3d77b0acdf017b2e8b2
F src/msvc.h 3a15918220367a8876be3fa4f2abe423a861491e84b864fb2b7426bf022a28f8
F src/mutex.c 5e3409715552348732e97b9194abe92fdfcd934cfb681df4ba0ab87ac6c18d25
F src/mutex.h a7b2293c48db5f27007c3bdb21d438873637d12658f5a0bf8ad025bb96803c4a
@@ -534,33 +540,33 @@ F src/os.c b1c4f2d485961e9a5b6b648c36687d25047c252222e9660b7cc25a6e1ea436ab
F src/os.h 26890f540b475598cd9881dcc68931377b8d429d3ea3e2eeb64470cde64199f8
F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85
F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586
F src/os_unix.c f5ad51cfd024116db8531feab9efd831c2621436dca1464e4ff1e8af9bf3252e
F src/os_unix.c d6815223d2a91a63b34d240a98f51341e74f8aabd3f8391fb3131c0ab1bd4b1e
F src/os_win.c 77d39873836f1831a9b0b91894fec45ab0e9ca8e067dc8c549e1d1eca1566fe9
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c bc10c191d18bffd3d76eda5f162799e43a9f875ecfe7c4869f752e2ddef87ea2
F src/pager.h 4bf9b3213a4b2bebbced5eaa8b219cf25d4a82f385d093cd64b7e93e5285f66f
F src/parse.y 0bd7971a7489bbf6c3726f1b50da6e508bdff8fa493e9cc3f5a96b12cbb2361e
F src/pager.c b862b8f488e994a7205838172d8ae3a4516c56bcc0216336275056e874aa7804
F src/pager.h fdc30693c403aa9b4293d0b126346db7e450f8f0e5d2c1bb8355acb1cb8da6fd
F src/parse.y 59631359574901cc5cd4780939a6740f6bc597bd473334e744c1a1c32d9adef3
F src/pcache.c 084e638432c610f95aea72b8509f0845d2791293f39d1b82f0c0a7e089c3bb6b
F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586
F src/pcache1.c 54881292a9a5db202b2c0ac541c5e3ef9a5e8c4f1c1383adb2601d5499a60e65
F src/pragma.c c536665ce8431c8b1efbf7e0a5c01852f49f7bf28f1954f8118b2d28e4a3797f
F src/pragma.h 87330ed2fbfa2a1274de93ca0ab850fba336189228cb256089202c3b52766fad
F src/prepare.c 7520a371f1de8a53e3023eba75bc0d3473196833c6363d285cad8d002eabef0b
F src/pragma.h f98354c48571c490927029510566839bf9e7242569bfbb48032dafeb008481d2
F src/prepare.c dab0c2995a33ee2c458354cb7dd13b2b33362425c52301e41d833add7660e7ca
F src/printf.c 5901672228f305f7d493cbc4e7d76a61a5caecdbc1cd06b1f9ec42ea4265cf8d
F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c
F src/random.c c6e61d041f230d46c658e6dfe7165fc1ecb0093d5fe28cfe74f389d261dc3af8
F src/resolve.c 4a1db4aadd802683db40ca2dbbb268187bd195f10cbdb7206dbd8ac988795571
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
F src/select.c a7a3d9f54eb24821ec5f67f2e5589b68a5d42d46fc5849d7376886777d93a85a
F src/shell.c.in 975f268ef261773fcbed1e519dfa10c4f33e8b1cffc12120563e61857fff07c6
F src/sqlite.h.in 5cd209ac7dc4180f0e19292846f40440b8488015849ca0110c70b906b57d68f0
F src/select.c 7e8082835f8b8891a0785dc163724a13b5c200abd0931fd3204b2578339c49bc
F src/shell.c.in 1458b700144c8326fda2514aaddeda49d6f01f1d1ccf7b9b696c53a3535a119c
F src/sqlite.h.in 32f7d112cb85d3f2fd56500e11a6bd6d19a969306650200dcde97c2638cfecfc
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 8ff2fd2c166150b2e48639f5e506fb44e29f1a3f65031710b9e89d1c126ac839
F src/sqliteInt.h 193e716a67c877a6054a8c261c932bdc64f8c8be9b66388c74f21d94a259b7ce
F src/sqliteInt.h 4d301955804f800c3e2809fad8103458a42a1df92a1abf3bc6fc445d2717720d
F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
F src/tclsqlite.c 48f291e1a7e672a7204884d4c164a8ed3a522ff087c361ada2991f5d54e987f6
F src/test1.c d6c945a8bb211ed72ea515f0b9743caeaf38c66da6418a7b1dcf3764e1368bbb
F src/test1.c 2785b6fd05e4810f7215bf8de01c15670f39a19e49e3d927045f5e72ab08b4cd
F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5
F src/test3.c 61798bb0d38b915067a8c8e03f5a534b431181f802659a6616f9b4ff7d872644
F src/test4.c 7c4420e01c577b5c4add2cb03119743b1a357543d347773b9e717195ea967159
@@ -575,7 +581,7 @@ F src/test_backup.c bf5da90c9926df0a4b941f2d92825a01bbe090a0
F src/test_bestindex.c 78809f11026f18a93fcfd798d9479cba37e1201c830260bf1edc674b2fa9b857
F src/test_blob.c ae4a0620b478548afb67963095a7417cd06a4ec0a56adb453542203bfdcb31ce
F src/test_btree.c 8b2dc8b8848cf3a4db93f11578f075e82252a274
F src/test_config.c 284c29912736f68b0a583a920bf63fd8f9125dffb8a75cb0676e58502b2f7908
F src/test_config.c 8544c71df9131c25c4d0255188ec37a6e9af452f5520ee9df4bf30f35c4c7d29
F src/test_delete.c e2fe07646dff6300b48d49b2fee2fe192ed389e834dd635e3b3bac0ce0bf9f8f
F src/test_demovfs.c 86142ba864d4297d54c5b2e972e74f3141ae4b30f05b3a95824184ed2d3d7f91
F src/test_devsym.c aff2255ea290d7718da08af30cdf18e470ff7325a5eff63e0057b1496ed66593
@@ -616,17 +622,17 @@ F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c 865911afa00fed589cd03b25c140ca88544842aaef7b81f7d41ed769a7a54120
F src/treeview.c 9dfdb7ff7f6645d0a6458dbdf4ffac041c071c4533a6db8bb6e502b979ac67bc
F src/trigger.c 2ef56f0b7b75349a5557d0604b475126329c2e1a02432e7d49c4c710613e8254
F src/update.c 69c4c10bc6873a80c0a77cb578f9fc60ee90003d03f9530bc3370fa24615772d
F src/trigger.c 043d66ecb25a223c614681c8ab758f1aaf6e507c901d3a4668113afab1cc2dc7
F src/update.c 440b9aae32b930fc8c40beff27cdc6060a2fc4c99d911e415f92bb8a4e6eab68
F src/upsert.c 8789047a8f0a601ea42fa0256d1ba3190c13746b6ba940fe2d25643a7e991937
F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
F src/util.c 30df8356e231dad33be10bb27897655002668343280004ba28c734489414a167
F src/vacuum.c 6c38ddc52f0619865c91dae9c441d4d48bf3040d7dc1bc5b22da1e45547ed0b3
F src/vdbe.c e98f1baf54a00db2c4669dbd04f8bbc89b5909a5b43e76fbbbf1a97007adba2b
F src/vacuum.c 72867c740476d13f1c397015e4d3168b4e96a237a80b9afa67e1bb8500bfeeab
F src/vdbe.c 5bf3a2db17f2cfc946e7e014cc66d01ad9ef5fe86087ab67a6dc4b9515800c69
F src/vdbe.h 25dabb25c7e157b84e59260cfb5b466c3ac103ede9f36f4db371332c47601abe
F src/vdbeInt.h 31fbabdc1ed61d9695337dfe5269ea94e1cf615c17f5cafeaa1bb01066820bab
F src/vdbeapi.c 22c79072ae7d8a01e9bcae8ba16e918d60d202eaa9553b5fda38f99f7464d99a
F src/vdbeaux.c 21db442d159fd745a7693d157b5f998260b6af4ca60de559fa3b7b68c7405af2
F src/vdbeaux.c 0986116cd4d7eeafd3662ab7d6c17190c85e6d26e43d535f104a7fb42dd2764c
F src/vdbeblob.c 29c4118f7ee615cdee829e8401f6ead1b96b95d545b4de0042f6de39c962c652
F src/vdbemem.c a3d91dc9bb9ef725db77e4e9de7e1acef43192c9f8406c307665d503e3c2837c
F src/vdbesort.c 513b481c8bab4a6578c92194a60cf3bc3b48736e4a53f8d2d7918121c5b594e7
@@ -634,8 +640,8 @@ F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf8
F src/vdbevtab.c f99b275366c5fc5e2d99f734729880994ab9500bdafde7fae3b02d562b9d323c
F src/vtab.c 9d5c3f49d3a6959b6eef287bb8fa773563102a80a835c3314c57144412709e78
F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c ed0398a7adf02c31e34aada42cc86c58f413a7afe5f741a5d373ad087abde028
F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
F src/wal.c 80c323b66657ff8edffb10ab231990bc999fff098c6cff9982a1b5fa1739a93b
F src/wal.h 7ffe787437f20a098af347011967a6d3bb8e5c3dc645e6be59eff44d2b2c5297
F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
F src/where.c de0d4ff409c7b62a8803f9f267cc2c7fedddbc00de9ab7b5382c507383c18665
F src/whereInt.h 83877a75a1bce056ea44aff02f1dfa958ad1d6038c213ddadb8652003b45151d
@@ -688,7 +694,7 @@ F test/async2.test c0a9bd20816d7d6a2ceca7b8c03d3d69c28ffb8b
F test/async3.test d73a062002376d7edc1fe3edff493edbec1fc2f7
F test/async4.test 1787e3952128aa10238bf39945126de7ca23685a
F test/async5.test 383ab533fdb9f7ad228cc99ee66e1acb34cc0dc0
F test/atof1.test 10049623e77006691c4c2978c1dc8a3f75276377a53417811aa85bda7493f963
F test/atof1.test 77e8517df10cb39f2e4e65dbefe1e81c8d8e65f29f68b09265ca6e534c68227e
F test/atomic.test 065a453dde33c77ff586d91ccaa6ed419829d492dbb1a5694b8a09f3f9d7d061
F test/atomic2.test b6863b4aa552543874f80b42fb3063f1c8c2e3d8e56b6562f00a3cc347b5c1da
F test/atrc.c c388fac43dbba05c804432a7135ae688b32e8f25818e9994ffba4b64cf60c27c
@@ -722,6 +728,7 @@ F test/backup_malloc.test 0c9abdf74c51e7bedb66d504cd684f28d4bd4027
F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f
F test/badutf2.test f310fd3b24a491b6b77bccdf14923b85d6ebcce751068c180d93a6b8ff854399
F test/bc_common.tcl b5e42d80305be95697e6370e015af571e5333a1c
F test/bc_test1.c e0a092579552e066ed4ce7bcdaecfa69c4aacc8d
F test/bestindex1.test 7cc626f1f4a7483bb6b38487d467db4477083be5cd93958aeda5d5127640dc81
F test/bestindex2.test 60266e2854055788459cbfd86cef575601eabe74a2c61faba72601739fea4398
F test/bestindex3.test e061a6ed0f519beee037ba7e7a4c37f80c8a7e4a303e2559ed1f760e4b0235eb
@@ -785,6 +792,15 @@ F test/collateB.test 1e68906951b846570f29f20102ed91d29e634854ee47454d725f2151eca
F test/colmeta.test 2c765ea61ee37bc43bbe6d6047f89004e6508eb1
F test/colname.test 87ad5458bb8709312dac0d6755fd30e8e4ca83298d0a9ef6e5c24277a3c3390e
F test/columncount.test 6fe99c2f35738b0129357a1cf3fa483f76140f4cd8a89014c88c33c876d2638f
F test/concfault.test af01b1f6cca45b8f17bb13b4406ec43ab1689181a08850b990596c7a9f4e656f
F test/concurrent.test a0248ec6e3e79a5948453649cf86b5b359175cba55ea636b15426d6f0fa6c3da
F test/concurrent2.test de748c7dd749c77e2af2c4b914b9b09a28ac09608042ca498c0251dc6f46aa1a
F test/concurrent3.test 530671ac706f6a1d0f4992dbdd33a86408330d03cd90fb9e82ecb1b27f5fd081
F test/concurrent4.test e0b12cd467137e50259df3b4f837507e82aaa07c35941c88664dc8ed1d089c44
F test/concurrent5.test 0c16cbf7446af162a14e6def30445e94016064eb994e5aa4ebb2bebc59554176
F test/concurrent6.test a7860e9ca13bb5fb76bcf41c5524fbfa9c37e6e258ecf84ffb5748a272488c67
F test/concurrent7.test b96fa5c4cfdf8d5c0bc66b6934214500bad0260884a736f054ccc76e81aae85d
F test/concurrent8.test b93937e74a8efb8b84f2fea7595b53418c5f29777bbe9cbdb5dc219b3dd72a7d
F test/conflict.test ac0667090f66130ac77d5fb764655558ca6600dd6d88f670ca9123b61c448337
F test/conflict2.test 5557909ce683b1073982f5d1b61dfb1d41e369533bfdaf003180c5bc87282dd1
F test/conflict3.test 81865d9599609aca394fb3b9cd5f561d4729ea5b176bece3644f6ecb540f88ac
@@ -811,7 +827,7 @@ F test/corruptJ.test 4d5ccc4bf959464229a836d60142831ef76a5aa4
F test/corruptK.test 5b4212fe346699831c5ad559a62c54e11c0611bdde1ea8423a091f9c01aa32af
F test/corruptL.test 7d3440831ca24ba64305583c4d4506d417d3f89f5775c0b7cc8102db078f8ff5
F test/corruptM.test 7d574320e08c1b36caa3e47262061f186367d593a7e305d35f15289cc2c3e067
F test/corruptN.test 60b5a62944b4f0029ba07edaa5fd8e670539d6b0a8d99db26c068d435675cbfe
F test/corruptN.test f0b26fda7556581af9667904ce2fa1e5749cd018b56c8fb77bda143d3ab6ccbf
F test/cost.test b11cdbf9f11ffe8ef99c9881bf390e61fe92baf2182bad1dbe6de59a7295c576
F test/count.test 013d64569c15563c59472d17ec4ddc681034aaee65e8b01880bc88315fd4c673
F test/countofview.test e17d6e6688cf74f22783c9ec6e788c0790ee4fbbaee713affd00b1ac0bb39b86
@@ -907,7 +923,7 @@ F test/filter1.test 6c483ecf7886c8843a8612c021aa23f33c581f584151f251842b3a3592c9
F test/filter2.tcl 44e525497ce07382915f01bd29ffd0fa49dab3adb87253b5e5103ba8f93393e8
F test/filter2.test 485cf95d1f6d6ceee5632201ca52a71868599836f430cdee42e5f7f14666e30a
F test/filterfault.test c08fb491d698e8df6c122c98f7db1c65ffcfcad2c1ab0e07fa8a5be1b34eaa8b
F test/fkey1.test 03503639d266d565db90ee3b8fe211ba446624030ac4eb24895cec265e9631d0
F test/fkey1.test 55663090ab6735319a52647057b9f19f8ec8c6c7d7da25170b71a75e3e5bdeb7
F test/fkey2.test 1063d65e5923c054cfb8f0555a92a3ae0fa8c067275a33ee1715bd856cdb304c
F test/fkey3.test 76d475c80b84ee7a5d062e56ccb6ea68882e2b49
F test/fkey4.test 86446017011273aad8f9a99c1a65019e7bd9ca9d
@@ -1202,6 +1218,7 @@ F test/mem5.test c6460fba403c5703141348cd90de1c294188c68f
F test/memdb.test c1f2a343ad14398d5d6debda6ea33e80d0dafcc7
F test/memdb1.test 2c4e9cc10d21c6bf4e217d72b7f6b8ba9b2605971bb2c5e6df76018e189f98f5
F test/memjournal.test 70f3a00c7f84ee2978ad14e831231caa1e7f23915a2c54b4f775a021d5740c6c
F test/memjournal2.test 89a4e0d1084170a281efa4d54c2677599f986f44227f98f7dfae282802737b65
F test/memleak.test 10b9c6c57e19fc68c32941495e9ba1c50123f6e2
F test/memsubsys1.test 9e7555a22173b8f1c96c281ce289b338fcba2abe8b157f8798ca195bbf1d347e
F test/memsubsys2.test 3e4a8d0c05fd3e5fa92017c64666730a520c7e08
@@ -1234,6 +1251,7 @@ F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660
F test/nan.test 437d40e6d0778b050d7750726c0cbd2c9936b81962926e8f8c48ca698f00f4d1
F test/nockpt.test 8c43b25af63b0bd620cf1b003529e37b6f1dc53bd22690e96a1bd73f78dde53a
F test/nolock.test f196cf8b8fbea4e2ca345140a2b3f3b0da45c76e
F test/noop_update.test 74d71bbe5d37f7861cc2402ed620eb165b8e6b00159cdcada7df716420336651
F test/normalize.test f23b6c5926c59548635fcf39678ac613e726121e073dd902a3062fbb83903b72
F test/notify1.test 669b2b743618efdc18ca4b02f45423d5d2304abf
F test/notify2.test 2ecabaa1305083856b7c39cf32816b612740c161
@@ -1273,7 +1291,6 @@ F test/pagerfault2.test caf4c7facb914fd3b03a17b31ae2b180c8d6ca1f
F test/pagerfault3.test 1003fcda009bf48a8e22a516e193b6ef0dd1bbd8
F test/pageropt.test 84e4cc5cbca285357f7906e99b21be4f2bf5abc0
F test/pagesize.test 5769fc62d8c890a83a503f67d47508dfdc543305
F test/parser1.test 6ccdf5e459a5dc4673d3273dc311a7e9742ca952dd0551a6a6320d27035ce4b3
F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
@@ -1309,6 +1326,7 @@ F test/releasetest_data.tcl 7cea6c852ae6bb3a9ff1a2b910e4dd13c16a05f74443984dfd52
F test/resetdb.test 8062cf10a09d8c048f8de7711e94571c38b38168db0e5877ba7561789e5eeb2b
F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb
F test/returning1.test f96c7245f6ac16038e802760cd90b93479369939a8a7a44e2329ee5aed28239c
F test/returningfault.test ae4c4b5e8745813287a359d9ccdb9d5c883c2e68afb18fb0767937d5de5692a4
F test/rollback.test 06680159bc6746d0f26276e339e3ae2f951c64812468308838e0a3362d911eaa
F test/rollback2.test 3f3a4e20401825017df7e7671e9f31b6de5fae5620c2b9b49917f52f8c160a8f
F test/rollbackfault.test 0e646aeab8840c399cfbfa43daab46fd609cf04a
@@ -1650,6 +1668,7 @@ F test/triggerG.test 2b816093c91ba73c733cfa8aedcc210ad819d72a98b1da30768a3c56505
F test/triggerupfrom.test d1f9e56090408115c522bee626cc33a2f3370f627a5e341d832589d72e3aa271
F test/trustschema1.test 4e970aef0bfe0cee139703cc7209d0e0f07725d999b180ba50770f49edef1494
F test/tt3_checkpoint.c ac7ca661d739280c89d9c253897df64a59a49369bd1247207ac0f655b622579d
F test/tt3_core.c 8cd89ead95410f70e7fb02c79f1e040f9c5ad5cf
F test/tt3_index.c 39eec10a35f57672225be4d182862152896dee4a
F test/tt3_lookaside1.c 0377e202c3c2a50d688cb65ba203afeda6fafeb9
F test/tt3_shared.c b37d22defc944a2ac4c91c927fd06c1d48cd51e2ce9d004fe868625bd2399f93
@@ -1674,7 +1693,7 @@ F test/upfrom1.test 8cb06689e99cd707d884faa16da0e8eb26ff658bb01c47ddf72fadade666
F test/upfrom2.test 88d39cb755db5789541e645d4e2764abc697a56958f28a3f8451a0e9342bbd6b
F test/upfrom3.test 6130f24ebf97f5ea865e5d2a14a2d543fe5428a62e87cc60f62d875e45c1f5f0
F test/upfromfault.test 3a10075a0043f0c4fad6614b2c371f88a8ba5a4acab68b907438413865d6a8d6
F test/upsert1.test 88f9e258c6a0eeeb85937b08831e8daad440ba41f125af48439e9d33f266fb18
F test/upsert1.test b0ae2f58680c5205b4bc1cdeed3c3d444057c506f6c44494fa3eac60731d68a2
F test/upsert2.test 9c3cdbb1a890227f6504ce4b0e3de68f4cdfa16bb21d8641208a9239896c5a09
F test/upsert3.test 88d7d590a1948a9cb6eac1b54b0642f67a9f35a1fc0f19b200e97d5d39e3179c
F test/upsert4.test 25d2a1da92f149331ae0c51ca6e3eee78189577585eab92de149900d62994fa5
@@ -1720,7 +1739,7 @@ F test/vtab_err.test dcc8b7b9cb67522b3fe7a272c73856829dae4ab7fdb30399aea1b6981bd
F test/vtab_shared.test 5253bff2355a9a3f014c15337da7e177ab0ef8ad
F test/vtabdrop.test 65d4cf6722972e5499bdaf0c0d70ee3b8133944a4e4bc31862563f32a7edca12
F test/wal.test b7cc6984709f54afbf8441747ced1f646af120bf0c1b1d847bfa39306fbea089
F test/wal2.test 31f6e2c404b9f2cdf9ca19b105a1742fdc19653c2c936da39e3658c617524046
F test/wal2.test 88d34cc62bf5460ad6b1f837cd56ab55731bb0637ff99e113b790873d9b60b14
F test/wal3.test 2a93004bc0fb2b5c29888964024695bade278ab2
F test/wal4.test 4744e155cd6299c6bd99d3eab1c82f77db9cdb3c
F test/wal5.test 9c11da7aeccd83a46d79a556ad11a18d3cb15aa9
@@ -1729,7 +1748,7 @@ F test/wal64k.test 2a525c0f45d709bae3765c71045ccec5df7d100ccbd3a7860fdba46c9addb
F test/wal7.test 2ae8f427d240099cc4b2dfef63cff44e2a68a1bd
F test/wal8.test d9df3fba4caad5854ed69ed673c68482514203c8
F test/wal9.test 378e76a9ad09cd9bee06c172ad3547b0129a6750
F test/wal_common.tcl a98f17fba96206122eff624db0ab13ec377be4fe
F test/wal_common.tcl 37902864b63794e9bf59393b8887e21a9cbd4db8
F test/walbak.test 018d4e5a3d45c6298d11b99f09a8ef6876527946
F test/walbig.test f437473a16cfb314867c6b5d1dbcd519e73e3434
F test/walblock.test be48f3a75eff0b4456209f26b3ce186c2015497d
@@ -1803,7 +1822,7 @@ F test/window8.test 4ab16817414af0c904abe2ebdf88eb6c2b00058b84f9748c6174ff11fc45
F test/window9.test 349c71eab4288a1ffc19e2f65872ec2c37e6cf8a1dda2ad300364b7450ae4836
F test/windowA.test 6d63dc1260daa17141a55007600581778523a8b420629f1282d2acfc36af23be
F test/windowB.test b67bda5645f3226790e1a360c4225241840b84adb5aa2e69bfb0b27eef3b84d9
F test/windowC.test 8799158a2a3ea365980371400f08fd4dff70eadffa5a1e45d42430246da70706
F test/windowC.test 6fd75f5bb2f1343d34e470e36e68f0ff638d8a42f6aa7d99471261b31a0d42f2
F test/windowerr.tcl f5acd6fbc210d7b5546c0e879d157888455cd4a17a1d3f28f07c1c8a387019e0
F test/windowerr.test a8b752402109c15aa1c5efe1b93ccb0ce1ef84fa964ae1cd6684dd0b3cc1819b
F test/windowfault.test 15094c1529424e62f798bc679e3fe9dfab6e8ba2f7dfe8c923b6248c31660a7c
@@ -1815,7 +1834,7 @@ F test/with4.test 257be66c0c67fee1defbbac0f685c3465e2cad037f21ce65f23f86084f1982
F test/with5.test 6248213c41fab36290b5b73aa3f937309dfba337004d9d8434c3fabc8c7d4be8
F test/with6.test 661d7e416bef6c0a2556b2c9f0c8178a5b15932bed65246abed99723a8d4e7c0
F test/withM.test 693b61765f2b387b5e3e24a4536e2e82de15ff64
F test/without_rowid1.test df3de14f1cc422d2b0f9b79969b5ef8e51c86ed87834ab35fb5139403e7f5a03
F test/without_rowid1.test 78fd9b437f4cdb46f76e6a510d545334e4f58e3e4ce37aaf19384eda5b27de8c
F test/without_rowid2.test af260339f79d13cb220288b67cd287fbcf81ad99
F test/without_rowid3.test 39ab0dd773eaa62e59b17093f875327630f54c4145458f6d2b053d68d4b2f67b
F test/without_rowid4.test 4e08bcbaee0399f35d58b5581881e7a6243d458a
@@ -1855,7 +1874,7 @@ F tool/lemon.c 258881835bd5bccd0c74fb110fe54244ff18e8e7ef3d949cbdab7187f02132bb
F tool/lempar.c 57478ea48420da05faa873c6d1616321caa5464644588c97fbe8e0ea04450748
F tool/libvers.c caafc3b689638a1d88d44bc5f526c2278760d9b9
F tool/loadfts.c c3c64e4d5e90e8ba41159232c2189dba4be7b862
F tool/logest.c 11346aa019e2e77a00902aa7d0cabd27bd2e8cca
F tool/logest.c 06446d42942a7128ab4963eb438eb3f528e07095d5d1080f9105a6f337238d33
F tool/max-limits.c cbb635fbb37ae4d05f240bfb5b5270bb63c54439
F tool/merge-test.tcl de76b62f2de2a92d4c1ca4f976bce0aea6899e0229e250479b229b2a1914b176
F tool/mkautoconfamal.sh f62353eb6c06ab264da027fd4507d09914433dbdcab9cb011cdc18016f1ab3b8
@@ -1866,7 +1885,7 @@ F tool/mkmsvcmin.tcl 6ecab9fe22c2c8de4d82d4c46797bda3d2deac8e763885f5a38d0c44a89
F tool/mkopcodec.tcl 33d20791e191df43209b77d37f0ff0904620b28465cca6990cf8d60da61a07ef
F tool/mkopcodeh.tcl 130b88697da6ec5b89b41844d955d08fb62c2552e889dec8c7bcecb28d8f50bd
F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa
F tool/mkpragmatab.tcl de206c64b6e9ac8cd5e3cbd0ffe456f07d5710605ef8385d677e60ce3335ea12
F tool/mkpragmatab.tcl 6c5c676b10f9ccac33b38b2b27d3b0578561f5c39137410e1e616ac3b79a4316
F tool/mkshellc.tcl df5d249617f9cc94d5c48eb0401673eb3f31f383ecbc54e8a13ca3dd97e89450
F tool/mksourceid.c 36aa8020014aed0836fd13c51d6dc9219b0df1761d6b5f58ff5b616211b079b9
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
@@ -1883,7 +1902,7 @@ F tool/replace.tcl 937c931ad560688e85bdd6258bdc754371bb1e2732e1fb28ef441e44c9228
F tool/restore_jrnl.tcl 6957a34f8f1f0f8285e07536225ec3b292a9024a
F tool/rollback-test.c 9fc98427d1e23e84429d7e6d07d9094fbdec65a5
F tool/run-speed-test.sh f95d19fd669b68c4c38b6b475242841d47c66076
F tool/showdb.c 7cc12c6deeddfe40ba5d948b408730696d8365988da05fcb6b6a90ea4965e2b4
F tool/showdb.c 72239e95e1d05a2941c6a1d86b4d857812be4dadd71f24e381db572f350fc172
F tool/showjournal.c 5bad7ae8784a43d2b270d953060423b8bd480818
F tool/showlocks.c 9cc5e66d4ebbf2d194f39db2527ece92077e86ae627ddd233ee48e16e8142564
F tool/showshm.c a0ab6ec32dd1f11218ca2a4018f8fb875b59414801ab8ceed8b2e69b7b45a809
@@ -1933,10 +1952,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 7c567338ae4dd31097e9ae5c693b84369c35c5a52534797e8186b4be7297ba78
R 1bdaa083fa0abfc53c61429627c688f2
T +sym-major-release *
T +sym-release *
T +sym-version-3.37.0 *
P 910001f28368896c3e6f11bb6ced3ec3171dcafefd9e6702106b32f3c451e229 872ba256cbf61d9290b571c0e6d82a20c224ca3ad82971edc46b29818d5d17a0
R dbfa2c5ab48ce7f85490f2e1eb4b1b9a
T *branch * begin-concurrent-3.37
T *sym-begin-concurrent-3.37 *
T -sym-begin-concurrent-pnu *
U drh
Z 6f6dbee10adfe1c5275055f73bc6984f
Z bd2e7b7884e9a174ca68f763f142b3b9
# Remove this line to create a well-formed Fossil manifest.
+1 -1
View File
@@ -1 +1 @@
bd41822c7424d393a30e92ff6cb254d25c26769889c1499a18a0b9339f5d6c8a
e525892d3dcdd9914000b169ddab2cf651d7f9bcd5ff4d8a74f591e017149985
+6
View File
@@ -171,6 +171,12 @@ int sqlite3BitvecSet(Bitvec *p, u32 i){
if( p==0 ) return SQLITE_OK;
assert( i>0 );
assert( i<=p->iSize );
if( i>p->iSize || i==0 ){
sqlite3_log(SQLITE_ERROR,
"Bitvec: setting bit %d of bitvec size %d\n", (int)i, (int)p->iSize
);
abort();
}
i--;
while((p->iSize > BITVEC_NBIT) && p->iDivisor) {
u32 bin = i/p->iDivisor;
+629 -52
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -350,6 +350,8 @@ sqlite3_uint64 sqlite3BtreeSeekCount(Btree*);
# define sqlite3BtreeSeekCount(X) 0
#endif
int sqlite3BtreeExclusiveLock(Btree *pBt);
#ifndef NDEBUG
int sqlite3BtreeCursorIsValid(BtCursor*);
#endif
@@ -408,5 +410,4 @@ int sqlite3BtreeTransferRow(BtCursor*, BtCursor*, i64);
# define sqlite3SchemaMutexHeld(X,Y,Z) 1
#endif
#endif /* SQLITE_BTREE_H */
+17 -3
View File
@@ -232,6 +232,7 @@
typedef struct MemPage MemPage;
typedef struct BtLock BtLock;
typedef struct CellInfo CellInfo;
typedef struct BtreePtrmap BtreePtrmap;
/*
** This is a magic string that appears at the beginning of every
@@ -275,6 +276,9 @@ struct MemPage {
u8 intKey; /* True if table b-trees. False for index b-trees */
u8 intKeyLeaf; /* True if the leaf of an intKey table */
Pgno pgno; /* Page number for this page */
#ifndef SQLITE_OMIT_CONCURRENT
Pgno pgnoRoot; /* Root page of b-tree that this page belongs to */
#endif
/* Only the first 8 bytes (above) are zeroed by pager.c when a new page
** is allocated. All fields that follow must be initialized before use */
u8 leaf; /* True if a leaf page */
@@ -454,6 +458,9 @@ struct BtShared {
Btree *pWriter; /* Btree with currently open write transaction */
#endif
u8 *pTmpSpace; /* Temp space sufficient to hold a single cell */
#ifndef SQLITE_OMIT_CONCURRENT
BtreePtrmap *pMap;
#endif
int nPreformatSize; /* Size of last cell written by TransferRow() */
};
@@ -671,12 +678,19 @@ struct BtCursor {
** (sqliteMallocRaw), it is not possible to use conditional compilation.
** So, this macro is defined instead.
*/
#ifndef SQLITE_OMIT_AUTOVACUUM
#define ISAUTOVACUUM (pBt->autoVacuum)
#else
#ifdef SQLITE_OMIT_AUTOVACUUM
#define ISAUTOVACUUM 0
#else
#define ISAUTOVACUUM (pBt->autoVacuum)
#endif
#ifdef SQLITE_OMIT_CONCURRENT
# define ISCONCURRENT 0
#else
# define ISCONCURRENT (pBt->pMap!=0)
#endif
#define REQUIRE_PTRMAP (ISAUTOVACUUM || ISCONCURRENT)
/*
** This structure is passed around through all the sanity checking routines
+4 -2
View File
@@ -89,8 +89,10 @@ void sqlite3TableLock(
u8 isWriteLock, /* True for a write lock */
const char *zName /* Name of the table to be locked */
){
#ifdef SQLITE_OMIT_CONCURRENT
if( iDb==1 ) return;
if( !sqlite3BtreeSharable(pParse->db->aDb[iDb].pBt) ) return;
#endif
lockTable(pParse, iDb, iTab, isWriteLock, zName);
}
@@ -5070,7 +5072,7 @@ void sqlite3BeginTransaction(Parse *pParse, int type){
}
v = sqlite3GetVdbe(pParse);
if( !v ) return;
if( type!=TK_DEFERRED ){
if( type==TK_IMMEDIATE || type==TK_EXCLUSIVE ){
for(i=0; i<db->nDb; i++){
int eTxnType;
Btree *pBt = db->aDb[i].pBt;
@@ -5085,7 +5087,7 @@ void sqlite3BeginTransaction(Parse *pParse, int type){
sqlite3VdbeUsesBtree(v, i);
}
}
sqlite3VdbeAddOp0(v, OP_AutoCommit);
sqlite3VdbeAddOp3(v, OP_AutoCommit, 0, 0, (type==TK_CONCURRENT));
}
/*
+1 -1
View File
@@ -2457,7 +2457,7 @@ int sqlite3ExprCanBeNull(const Expr *p){
return ExprHasProperty(p, EP_CanBeNull) ||
p->y.pTab==0 || /* Reference to column of index on expression */
(p->iColumn>=0
&& ALWAYS(p->y.pTab->aCol!=0) /* Defense against OOM problems */
&& p->y.pTab->aCol!=0 /* Possible due to prior error */
&& p->y.pTab->aCol[p->iColumn].notNull==0);
default:
return 1;
+19
View File
@@ -702,6 +702,25 @@ static void fkTriggerDelete(sqlite3 *dbMem, Trigger *p){
}
}
/*
** Clear the apTrigger[] cache of CASCADE triggers for all foreign keys
** in a particular database. This needs to happen when the schema
** changes.
*/
void sqlite3FkClearTriggerCache(sqlite3 *db, int iDb){
HashElem *k;
Hash *pHash = &db->aDb[iDb].pSchema->tblHash;
for(k=sqliteHashFirst(pHash); k; k=sqliteHashNext(k)){
Table *pTab = sqliteHashData(k);
FKey *pFKey;
if( !IsOrdinaryTable(pTab) ) continue;
for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){
fkTriggerDelete(db, pFKey->apTrigger[0]); pFKey->apTrigger[0] = 0;
fkTriggerDelete(db, pFKey->apTrigger[1]); pFKey->apTrigger[1] = 0;
}
}
}
/*
** This function is called to generate code that runs when table pTab is
** being dropped from the database. The SrcList passed as the second argument
+4 -2
View File
@@ -511,8 +511,9 @@ static void randomFunc(
sqlite3_value **NotUsed2
){
sqlite_int64 r;
sqlite3 *db = sqlite3_context_db_handle(context);
UNUSED_PARAMETER2(NotUsed, NotUsed2);
sqlite3_randomness(sizeof(r), &r);
sqlite3FastRandomness(&db->sPrng, sizeof(r), &r);
if( r<0 ){
/* We need to prevent a random number of 0x8000000000000000
** (or -9223372036854775808) since when you do abs() of that
@@ -538,6 +539,7 @@ static void randomBlob(
){
sqlite3_int64 n;
unsigned char *p;
sqlite3 *db = sqlite3_context_db_handle(context);
assert( argc==1 );
UNUSED_PARAMETER(argc);
n = sqlite3_value_int64(argv[0]);
@@ -546,7 +548,7 @@ static void randomBlob(
}
p = contextMalloc(context, n);
if( p ){
sqlite3_randomness(n, p);
sqlite3FastRandomness(&db->sPrng, n, p);
sqlite3_result_blob(context, (char*)p, n, sqlite3_free);
}
}
+3 -2
View File
@@ -43,7 +43,7 @@ void sqlite3OpenTable(
}else{
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
assert( pPk!=0 );
assert( pPk->tnum==pTab->tnum );
assert( pPk->tnum==pTab->tnum || CORRUPT_DB );
sqlite3VdbeAddOp3(v, opcode, iCur, pPk->tnum, iDb);
sqlite3VdbeSetP4KeyInfo(pParse, pPk);
VdbeComment((v, "%s", pTab->zName));
@@ -2009,6 +2009,7 @@ void sqlite3GenerateConstraintChecks(
if( onError==OE_Replace /* IPK rule is REPLACE */
&& onError!=overrideError /* Rules for other constraints are different */
&& pTab->pIndex /* There exist other constraints */
&& !upsertIpkDelay /* IPK check already deferred by UPSERT */
){
ipkTop = sqlite3VdbeAddOp0(v, OP_Goto)+1;
VdbeComment((v, "defer IPK REPLACE until last"));
@@ -2417,6 +2418,7 @@ void sqlite3GenerateConstraintChecks(
if( ipkTop ){
sqlite3VdbeGoto(v, ipkTop);
VdbeComment((v, "Do IPK REPLACE"));
assert( ipkBottom>0 );
sqlite3VdbeJumpHere(v, ipkBottom);
}
@@ -2547,7 +2549,6 @@ void sqlite3CompleteInsertion(
}
pik_flags = (useSeekResult ? OPFLAG_USESEEKRESULT : 0);
if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){
assert( pParse->nested==0 );
pik_flags |= OPFLAG_NCHANGE;
pik_flags |= (update_flags & OPFLAG_SAVEPOSITION);
if( update_flags==0 ){
+41 -6
View File
@@ -2857,6 +2857,8 @@ int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){
if( newLimit>=0 ){ /* IMP: R-52476-28732 */
if( newLimit>aHardLimit[limitId] ){
newLimit = aHardLimit[limitId]; /* IMP: R-51463-25634 */
}else if( newLimit<1 && limitId==SQLITE_LIMIT_LENGTH ){
newLimit = 1;
}
db->aLimit[limitId] = newLimit;
}
@@ -3230,7 +3232,7 @@ static int openDatabase(
db->aDb = db->aDbStatic;
db->lookaside.bDisable = 1;
db->lookaside.sz = 0;
sqlite3FastPrngInit(&db->sPrng);
assert( sizeof(db->aLimit)==sizeof(aHardLimit) );
memcpy(db->aLimit, aHardLimit, sizeof(db->aLimit));
db->aLimit[SQLITE_LIMIT_WORKER_THREADS] = SQLITE_DEFAULT_WORKER_THREADS;
@@ -4260,12 +4262,16 @@ int sqlite3_test_control(int op, ...){
*/
case SQLITE_TESTCTRL_IMPOSTER: {
sqlite3 *db = va_arg(ap, sqlite3*);
int iDb;
sqlite3_mutex_enter(db->mutex);
db->init.iDb = sqlite3FindDbName(db, va_arg(ap,const char*));
db->init.busy = db->init.imposterTable = va_arg(ap,int);
db->init.newTnum = va_arg(ap,int);
if( db->init.busy==0 && db->init.newTnum>0 ){
sqlite3ResetAllSchemasOfConnection(db);
iDb = sqlite3FindDbName(db, va_arg(ap,const char*));
if( iDb>=0 ){
db->init.iDb = iDb;
db->init.busy = db->init.imposterTable = va_arg(ap,int);
db->init.newTnum = va_arg(ap,int);
if( db->init.busy==0 && db->init.newTnum>0 ){
sqlite3ResetAllSchemasOfConnection(db);
}
}
sqlite3_mutex_leave(db->mutex);
break;
@@ -4716,6 +4722,35 @@ void sqlite3_snapshot_free(sqlite3_snapshot *pSnapshot){
}
#endif /* SQLITE_ENABLE_SNAPSHOT */
SQLITE_EXPERIMENTAL int sqlite3_wal_info(
sqlite3 *db, const char *zDb,
unsigned int *pnPrior, unsigned int *pnFrame
){
int rc = SQLITE_OK;
#ifndef SQLITE_OMIT_WAL
Btree *pBt;
int iDb;
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) ){
return SQLITE_MISUSE_BKPT;
}
#endif
sqlite3_mutex_enter(db->mutex);
iDb = sqlite3FindDbName(db, zDb);
if( iDb<0 ){
return SQLITE_ERROR;
}
pBt = db->aDb[iDb].pBt;
rc = sqlite3PagerWalInfo(sqlite3BtreePager(pBt), pnPrior, pnFrame);
sqlite3_mutex_leave(db->mutex);
#endif /* SQLITE_OMIT_WAL */
return rc;
}
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
/*
** Given the name of a compile-time option, return true if that option
+1 -1
View File
@@ -265,7 +265,7 @@ static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
p->pFirst = 0;
}else{
i64 iOff = p->nChunkSize;
for(pIter=p->pFirst; ALWAYS(pIter) && iOff<=size; pIter=pIter->pNext){
for(pIter=p->pFirst; ALWAYS(pIter) && iOff<size; pIter=pIter->pNext){
iOff += p->nChunkSize;
}
if( ALWAYS(pIter) ){
+50
View File
@@ -1177,6 +1177,10 @@ struct unixInodeInfo {
sem_t *pSem; /* Named POSIX semaphore */
char aSemName[MAX_PATHNAME+2]; /* Name of that semaphore */
#endif
#ifdef SQLITE_SHARED_MAPPING
sqlite3_int64 nSharedMapping; /* Size of mapped region in bytes */
void *pSharedMapping; /* Memory mapped region */
#endif
};
/*
@@ -1329,6 +1333,13 @@ static void releaseInodeInfo(unixFile *pFile){
pInode->nRef--;
if( pInode->nRef==0 ){
assert( pInode->pShmNode==0 );
#ifdef SQLITE_SHARED_MAPPING
if( pInode->pSharedMapping ){
osMunmap(pInode->pSharedMapping, pInode->nSharedMapping);
pInode->pSharedMapping = 0;
pInode->nSharedMapping = 0;
}
#endif
sqlite3_mutex_enter(pInode->pLockMutex);
closePendingFds(pFile);
sqlite3_mutex_leave(pInode->pLockMutex);
@@ -2192,6 +2203,14 @@ static int nolockUnlock(sqlite3_file *NotUsed, int NotUsed2){
** Close the file.
*/
static int nolockClose(sqlite3_file *id) {
#ifdef SQLITE_SHARED_MAPPING
unixFile *pFd = (unixFile*)id;
if( pFd->pInode ){
unixEnterMutex();
releaseInodeInfo(pFd);
unixLeaveMutex();
}
#endif
return closeUnixFile(id);
}
@@ -4045,6 +4064,9 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
*(i64*)pArg = pFile->mmapSizeMax;
if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){
pFile->mmapSizeMax = newLimit;
#ifdef SQLITE_SHARED_MAPPING
if( pFile->pInode==0 )
#endif
if( pFile->mmapSize>0 ){
unixUnmapfile(pFile);
rc = unixMapfile(pFile, -1);
@@ -5113,6 +5135,9 @@ static int unixShmUnmap(
*/
static void unixUnmapfile(unixFile *pFd){
assert( pFd->nFetchOut==0 );
#ifdef SQLITE_SHARED_MAPPING
if( pFd->pInode ) return;
#endif
if( pFd->pMapRegion ){
osMunmap(pFd->pMapRegion, pFd->mmapSizeActual);
pFd->pMapRegion = 0;
@@ -5244,6 +5269,28 @@ static int unixMapfile(unixFile *pFd, i64 nMap){
nMap = pFd->mmapSizeMax;
}
#ifdef SQLITE_SHARED_MAPPING
if( pFd->pInode ){
unixInodeInfo *pInode = pFd->pInode;
if( pFd->pMapRegion ) return SQLITE_OK;
unixEnterMutex();
if( pInode->pSharedMapping==0 ){
u8 *pNew = osMmap(0, nMap, PROT_READ, MAP_SHARED, pFd->h, 0);
if( pNew==MAP_FAILED ){
unixLogError(SQLITE_OK, "mmap", pFd->zPath);
pFd->mmapSizeMax = 0;
}else{
pInode->pSharedMapping = pNew;
pInode->nSharedMapping = nMap;
}
}
pFd->pMapRegion = pInode->pSharedMapping;
pFd->mmapSizeActual = pFd->mmapSize = pInode->nSharedMapping;
unixLeaveMutex();
return SQLITE_OK;
}
#endif
assert( nMap>0 || (pFd->mmapSize==0 && pFd->pMapRegion==0) );
if( nMap!=pFd->mmapSize ){
unixRemapfile(pFd, nMap);
@@ -5681,6 +5728,9 @@ static int fillInUnixFile(
if( pLockingStyle == &posixIoMethods
#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
|| pLockingStyle == &nfsIoMethods
#endif
#ifdef SQLITE_SHARED_MAPPING
|| pLockingStyle == &nolockIoMethods
#endif
){
unixEnterMutex();
+192 -26
View File
@@ -658,6 +658,9 @@ struct Pager {
u32 cksumInit; /* Quasi-random value added to every checksum */
u32 nSubRec; /* Number of records written to sub-journal */
Bitvec *pInJournal; /* One bit for each page in the database file */
#ifndef SQLITE_OMIT_CONCURRENT
Bitvec *pAllRead; /* Pages read within current CONCURRENT trans. */
#endif
sqlite3_file *fd; /* File descriptor for database */
sqlite3_file *jfd; /* File descriptor for main journal */
sqlite3_file *sjfd; /* File descriptor for sub-journal */
@@ -914,7 +917,9 @@ static int assert_pager_state(Pager *p){
if( !pagerUseWal(pPager) ){
assert( p->eLock>=RESERVED_LOCK );
}
assert( pPager->dbSize==pPager->dbOrigSize );
#ifndef SQLITE_OMIT_CONCURRENT
assert( pPager->dbSize==pPager->dbOrigSize || pPager->pAllRead );
#endif
assert( pPager->dbOrigSize==pPager->dbFileSize );
assert( pPager->dbOrigSize==pPager->dbHintSize );
assert( pPager->setSuper==0 );
@@ -1799,6 +1804,53 @@ static int addToSavepointBitvecs(Pager *pPager, Pgno pgno){
return rc;
}
#ifndef SQLITE_OMIT_CONCURRENT
/*
** If they are not already, begin recording all pages read from the pager layer
** by the b-tree layer This is used by concurrent transactions. Return
** SQLITE_OK if successful, or an SQLite error code (SQLITE_NOMEM) if an error
** occurs.
*/
int sqlite3PagerBeginConcurrent(Pager *pPager){
int rc = SQLITE_OK;
if( pPager->pAllRead==0 ){
pPager->pAllRead = sqlite3BitvecCreate(pPager->dbSize);
pPager->dbOrigSize = pPager->dbSize;
if( pPager->pAllRead==0 ){
rc = SQLITE_NOMEM;
}
}
return rc;
}
/* !defined(SQLITE_OMIT_CONCURRENT)
**
** Stop recording all pages read from the pager layer by the b-tree layer
** and discard any current records.
*/
void sqlite3PagerEndConcurrent(Pager *pPager){
sqlite3BitvecDestroy(pPager->pAllRead);
pPager->pAllRead = 0;
}
/* !defined(SQLITE_OMIT_CONCURRENT)
**
** Return true if the database is in wal mode. False otherwise.
*/
int sqlite3PagerIsWal(Pager *pPager){
return pPager->pWal!=0;
}
#endif /* SQLITE_OMIT_CONCURRENT */
/*
** Free the Pager.pInJournal and Pager.pAllRead bitvec objects.
*/
static void pagerFreeBitvecs(Pager *pPager){
sqlite3BitvecDestroy(pPager->pInJournal);
pPager->pInJournal = 0;
sqlite3PagerEndConcurrent(pPager);
}
/*
** This function is a no-op if the pager is in exclusive mode and not
** in the ERROR state. Otherwise, it switches the pager to PAGER_OPEN
@@ -1823,8 +1875,7 @@ static void pager_unlock(Pager *pPager){
|| pPager->eState==PAGER_ERROR
);
sqlite3BitvecDestroy(pPager->pInJournal);
pPager->pInJournal = 0;
pagerFreeBitvecs(pPager);
releaseAllSavepoints(pPager);
if( pagerUseWal(pPager) ){
@@ -2091,8 +2142,7 @@ static int pager_end_transaction(Pager *pPager, int hasSuper, int bCommit){
}
#endif
sqlite3BitvecDestroy(pPager->pInJournal);
pPager->pInJournal = 0;
pagerFreeBitvecs(pPager);
pPager->nRec = 0;
if( rc==SQLITE_OK ){
if( MEMDB || pagerFlushOnCommit(pPager, bCommit) ){
@@ -3098,8 +3148,24 @@ static int pagerRollbackWal(Pager *pPager){
** + Reload page content from the database (if refcount>0).
*/
pPager->dbSize = pPager->dbOrigSize;
rc = sqlite3WalUndo(pPager->pWal, pagerUndoCallback, (void *)pPager);
rc = sqlite3WalUndo(pPager->pWal, pagerUndoCallback, (void *)pPager,
#ifdef SQLITE_OMIT_CONCURRENT
0
#else
pPager->pAllRead!=0
#endif
);
pList = sqlite3PcacheDirtyList(pPager->pPCache);
#ifndef SQLITE_OMIT_CONCURRENT
/* If this is an CONCURRENT transaction, then page 1 must be reread from
** the db file, even if it is not dirty. This is because the b-tree layer
** may have already zeroed the nFree and iTrunk header fields. */
if( rc==SQLITE_OK && (pList==0 || pList->pgno!=1) && pPager->pAllRead ){
rc = pagerUndoCallback((void*)pPager, 1);
}
#endif
while( pList && rc==SQLITE_OK ){
PgHdr *pNext = pList->pDirty;
rc = pagerUndoCallback((void *)pPager, pList->pgno);
@@ -3149,6 +3215,8 @@ static int pagerWalFrames(
if( p->pgno<=nTruncate ){
ppNext = &p->pDirty;
nList++;
PAGERTRACE(("TO-WAL %d page %d hash(%08x)\n",
PAGERID(pPager), p->pgno, pager_pagehash(p)));
}
}
assert( pList );
@@ -3917,7 +3985,7 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){
#if defined(SQLITE_DEBUG)
static void assertTruncateConstraintCb(PgHdr *pPg){
assert( pPg->flags&PGHDR_DIRTY );
assert( !subjRequiresPage(pPg) || pPg->pgno<=pPg->pPager->dbSize );
assert( pPg->pgno<=pPg->pPager->dbSize || !subjRequiresPage(pPg) );
}
static void assertTruncateConstraint(Pager *pPager){
sqlite3PcacheIterateDirty(pPager->pPCache, assertTruncateConstraintCb);
@@ -4213,7 +4281,7 @@ static int syncJournal(Pager *pPager, int newHdr){
assert( assert_pager_state(pPager) );
assert( !pagerUseWal(pPager) );
rc = sqlite3PagerExclusiveLock(pPager);
rc = sqlite3PagerExclusiveLock(pPager, 0, 0);
if( rc!=SQLITE_OK ) return rc;
if( !pPager->noSync ){
@@ -4564,6 +4632,12 @@ static int pagerStress(void *p, PgHdr *pPg){
pPager->aStat[PAGER_STAT_SPILL]++;
pPg->pDirty = 0;
if( pagerUseWal(pPager) ){
#ifndef SQLITE_OMIT_CONCURRENT
/* If the transaction is a "BEGIN CONCURRENT" transaction, the page
** cannot be flushed to disk. Return early in this case. */
if( pPager->pAllRead ) return SQLITE_OK;
#endif
/* Write a single frame for this page to the log. */
rc = subjournalPageIfRequired(pPg);
if( rc==SQLITE_OK ){
@@ -5258,7 +5332,7 @@ int sqlite3PagerSharedLock(Pager *pPager){
** may mean that the pager was in the error-state when this
** function was called and the journal file does not exist.
*/
if( !isOpen(pPager->jfd) ){
if( !isOpen(pPager->jfd) && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){
sqlite3_vfs * const pVfs = pPager->pVfs;
int bExists; /* True if journal file exists */
rc = sqlite3OsAccess(
@@ -5483,6 +5557,18 @@ static int getPageNormal(
assert( assert_pager_state(pPager) );
assert( pPager->hasHeldSharedLock==1 );
#ifndef SQLITE_OMIT_CONCURRENT
/* If this is an CONCURRENT transaction and the page being read was
** present in the database file when the transaction was opened,
** mark it as read in the pAllRead vector. */
pPg = 0;
if( pPager->pAllRead && pgno<=pPager->dbOrigSize ){
PAGERTRACE(("USING page %d\n", pgno));
rc = sqlite3BitvecSet(pPager->pAllRead, pgno);
if( rc!=SQLITE_OK ) goto pager_acquire_err;
}
#endif
if( pgno==0 ) return SQLITE_CORRUPT_BKPT;
pBase = sqlite3PcacheFetch(pPager->pPCache, pgno, 3);
if( pBase==0 ){
@@ -5820,11 +5906,14 @@ static int pager_open_journal(Pager *pPager){
** Begin a write-transaction on the specified pager object. If a
** write-transaction has already been opened, this function is a no-op.
**
** If the exFlag argument is false, then acquire at least a RESERVED
** lock on the database file. If exFlag is true, then acquire at least
** If the exFlag argument is 0, then acquire at least a RESERVED
** lock on the database file. If exFlag is >0, then acquire at least
** an EXCLUSIVE lock. If such a lock is already held, no locking
** functions need be called.
**
** If (exFlag<0) and the database is in WAL mode, do not take any locks.
** The transaction will run in CONCURRENT mode instead.
**
** If the subjInMemory argument is non-zero, then any sub-journal opened
** within this transaction will be opened as an in-memory file. This
** has no effect if the sub-journal is already opened (as it may be when
@@ -5842,7 +5931,6 @@ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){
if( pPager->eState==PAGER_READER ){
assert( pPager->pInJournal==0 );
if( pagerUseWal(pPager) ){
/* If the pager is configured to use locking_mode=exclusive, and an
** exclusive lock on the database is not already held, obtain it now.
@@ -5858,9 +5946,10 @@ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){
/* Grab the write lock on the log file. If successful, upgrade to
** PAGER_RESERVED state. Otherwise, return an error code to the caller.
** The busy-handler is not invoked if another connection already
** holds the write-lock. If possible, the upper layer will call it.
*/
rc = sqlite3WalBeginWriteTransaction(pPager->pWal);
** holds the write-lock. If possible, the upper layer will call it. */
if( exFlag>=0 ){
rc = sqlite3WalBeginWriteTransaction(pPager->pWal);
}
}else{
/* Obtain a RESERVED lock on the database file. If the exFlag parameter
** is true, then immediately upgrade this to an EXCLUSIVE lock. The
@@ -5868,7 +5957,7 @@ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){
** lock, but not when obtaining the RESERVED lock.
*/
rc = pagerLockDb(pPager, RESERVED_LOCK);
if( rc==SQLITE_OK && exFlag ){
if( rc==SQLITE_OK && exFlag>0 ){
rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
}
}
@@ -6168,7 +6257,7 @@ int sqlite3PagerWrite(PgHdr *pPg){
** to sqlite3PagerWrite(). In other words, return TRUE if it is ok
** to change the content of the page.
*/
#ifndef NDEBUG
#if !defined(SQLITE_OMIT_CONCURRENT) || !defined(NDEBUG)
int sqlite3PagerIswriteable(DbPage *pPg){
return pPg->flags & PGHDR_WRITEABLE;
}
@@ -6324,17 +6413,26 @@ int sqlite3PagerSync(Pager *pPager, const char *zSuper){
}
/*
** This function may only be called while a write-transaction is active in
** rollback. If the connection is in WAL mode, this call is a no-op.
** Otherwise, if the connection does not already have an EXCLUSIVE lock on
** the database file, an attempt is made to obtain one.
** This function is called to ensure that all locks required to commit the
** current write-transaction to the database file are held. If the db is
** in rollback mode, this means the EXCLUSIVE lock on the database file.
**
** If the EXCLUSIVE lock is already held or the attempt to obtain it is
** successful, or the connection is in WAL mode, SQLITE_OK is returned.
** Otherwise, either SQLITE_BUSY or an SQLITE_IOERR_XXX error code is
** returned.
** Or, if this is a non-CONCURRENT transaction on a wal-mode database, this
** function is a no-op.
**
** If this is an CONCURRENT transaction on a wal-mode database, this function
** attempts to obtain the WRITER lock on the wal file and also checks to
** see that the transaction can be safely committed (does not commit with
** any other transaction committed since it was opened).
**
** If the required locks are already held or successfully obtained and
** the transaction can be committed, SQLITE_OK is returned. If a required lock
** cannot be obtained, SQLITE_BUSY is returned. Or, if the current transaction
** is CONCURRENT and cannot be committed due to a conflict, SQLITE_BUSY_SNAPSHOT
** is returned. Otherwise, if some other error occurs (IO error, OOM etc.),
** and SQLite error code is returned.
*/
int sqlite3PagerExclusiveLock(Pager *pPager){
int sqlite3PagerExclusiveLock(Pager *pPager, PgHdr *pPage1, Pgno *piConflict){
int rc = pPager->errCode;
assert( assert_pager_state(pPager) );
if( rc==SQLITE_OK ){
@@ -6346,10 +6444,73 @@ int sqlite3PagerExclusiveLock(Pager *pPager){
if( 0==pagerUseWal(pPager) ){
rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
}
#ifndef SQLITE_OMIT_CONCURRENT
else{
if( pPager->pAllRead ){
/* This is an CONCURRENT transaction. Attempt to lock the wal database
** here. If SQLITE_BUSY (but not SQLITE_BUSY_SNAPSHOT) is returned,
** invoke the busy-handler and try again for as long as it returns
** non-zero. */
do {
rc = sqlite3WalLockForCommit(
pPager->pWal, pPage1, pPager->pAllRead, piConflict
);
}while( rc==SQLITE_BUSY
&& pPager->xBusyHandler(pPager->pBusyHandlerArg)
);
}
}
#endif /* SQLITE_OMIT_CONCURRENT */
}
return rc;
}
#ifndef SQLITE_OMIT_CONCURRENT
/*
** This function is called as part of committing an CONCURRENT transaction.
** At this point the wal WRITER lock is held, and all pages in the cache
** except for page 1 are compatible with the snapshot at the head of the
** wal file.
**
** This function updates the in-memory data structures and reloads the
** contents of page 1 so that the client is operating on the snapshot
** at the head of the wal file.
**
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
*/
int sqlite3PagerUpgradeSnapshot(Pager *pPager, DbPage *pPage1){
int rc;
assert( pPager->pWal && pPager->pAllRead );
rc = sqlite3WalUpgradeSnapshot(pPager->pWal);
if( rc==SQLITE_OK ){
rc = readDbPage(pPage1);
}
return rc;
}
/* !defined(SQLITE_OMIT_CONCURRENT)
**
** Set the in-memory cache of the database file size to nSz pages.
*/
void sqlite3PagerSetDbsize(Pager *pPager, Pgno nSz){
pPager->dbSize = nSz;
}
/* !defined(SQLITE_OMIT_CONCURRENT)
**
** If this is a WAL mode connection and the WRITER lock is currently held,
** relinquish it.
*/
void sqlite3PagerDropExclusiveLock(Pager *pPager){
if( pagerUseWal(pPager) ){
sqlite3WalEndWriteTransaction(pPager->pWal);
}
}
#endif /* SQLITE_OMIT_CONCURRENT */
/*
** Sync the database file for the pager pPager. zSuper points to the name
** of a super-journal file that should be written into the individual
@@ -7693,6 +7854,11 @@ void sqlite3PagerSnapshotUnlock(Pager *pPager){
}
#endif /* SQLITE_ENABLE_SNAPSHOT */
int sqlite3PagerWalInfo(Pager *pPager, u32 *pnPrior, u32 *pnFrame){
return sqlite3WalInfo(pPager->pWal, pnPrior, pnFrame);
}
#endif /* !SQLITE_OMIT_WAL */
#ifdef SQLITE_ENABLE_ZIPVFS
+19 -2
View File
@@ -162,7 +162,7 @@ void *sqlite3PagerGetExtra(DbPage *);
void sqlite3PagerPagecount(Pager*, int*);
int sqlite3PagerBegin(Pager*, int exFlag, int);
int sqlite3PagerCommitPhaseOne(Pager*,const char *zSuper, int);
int sqlite3PagerExclusiveLock(Pager*);
int sqlite3PagerExclusiveLock(Pager*, DbPage *pPage1, Pgno*);
int sqlite3PagerSync(Pager *pPager, const char *zSuper);
int sqlite3PagerCommitPhaseTwo(Pager*);
int sqlite3PagerRollback(Pager*);
@@ -170,6 +170,7 @@ int sqlite3PagerOpenSavepoint(Pager *pPager, int n);
int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint);
int sqlite3PagerSharedLock(Pager *pPager);
#ifndef SQLITE_OMIT_WAL
int sqlite3PagerCheckpoint(Pager *pPager, sqlite3*, int, int*, int*);
int sqlite3PagerWalSupported(Pager *pPager);
@@ -224,10 +225,26 @@ void sqlite3PagerTruncateImage(Pager*,Pgno);
void sqlite3PagerRekey(DbPage*, Pgno, u16);
#ifndef SQLITE_OMIT_CONCURRENT
void sqlite3PagerEndConcurrent(Pager*);
int sqlite3PagerBeginConcurrent(Pager*);
void sqlite3PagerDropExclusiveLock(Pager*);
int sqlite3PagerUpgradeSnapshot(Pager *pPager, DbPage*);
void sqlite3PagerSetDbsize(Pager *pPager, Pgno);
int sqlite3PagerIsWal(Pager*);
#else
# define sqlite3PagerEndConcurrent(x)
#endif
#if defined(SQLITE_DEBUG) || !defined(SQLITE_OMIT_CONCURRENT)
int sqlite3PagerIswriteable(DbPage*);
#endif
int sqlite3PagerWalInfo(Pager*, u32 *pnPrior, u32 *pnFrame);
/* Functions to support testing and debugging. */
#if !defined(NDEBUG) || defined(SQLITE_TEST)
Pgno sqlite3PagerPagenumber(DbPage*);
int sqlite3PagerIswriteable(DbPage*);
#endif
#ifdef SQLITE_TEST
int *sqlite3PagerStats(Pager*);
+19 -3
View File
@@ -105,6 +105,13 @@
*/
struct TrigEvent { int a; IdList * b; };
/*
** Generate a syntax error
*/
static void parserSyntaxError(Parse *pParse, Token *p){
sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", p);
}
struct FrameBound { int eType; Expr *pExpr; };
/*
@@ -164,7 +171,16 @@ trans_opt ::= TRANSACTION nm.
transtype(A) ::= . {A = TK_DEFERRED;}
transtype(A) ::= DEFERRED(X). {A = @X; /*A-overwrites-X*/}
transtype(A) ::= IMMEDIATE(X). {A = @X; /*A-overwrites-X*/}
transtype(A) ::= EXCLUSIVE(X). {A = @X; /*A-overwrites-X*/}
transtype(A) ::= ID(X). {
Token *p = &X;
if( p->n==9 && sqlite3_strnicmp(p->z,"exclusive",9)==0 ){
A = TK_EXCLUSIVE;
}else if( p->n==10 && sqlite3_strnicmp(p->z,"concurrent",10)==0 ){
A = TK_CONCURRENT; /*A-overwrites-X*/
}else{
parserSyntaxError(pParse, p);
}
}
cmd ::= COMMIT|END(X) trans_opt. {sqlite3EndTransaction(pParse,@X);}
cmd ::= ROLLBACK(X) trans_opt. {sqlite3EndTransaction(pParse,@X);}
@@ -296,7 +312,6 @@ columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,A,Y);}
//
%token_class id ID|INDEXED.
// And "ids" is an identifer-or-string.
//
%token_class ids ID|STRING.
@@ -1104,7 +1119,7 @@ expr(A) ::= VARIABLE(X). {
Token t = X; /*A-overwrites-X*/
assert( t.n>=2 );
if( pParse->nested==0 ){
sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t);
parserSyntaxError(pParse, &t);
A = 0;
}else{
A = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
@@ -1871,6 +1886,7 @@ filter_clause(A) ::= FILTER LP WHERE expr(X) RP. { A = X; }
UPLUS /* Unary plus */
TRUTH /* IS TRUE or IS FALSE or IS NOT TRUE or IS NOT FALSE */
REGISTER /* Reference to a VDBE register */
CONCURRENT /* BEGIN CONCURRENT */
VECTOR /* Vector */
SELECT_COLUMN /* Choose a single column from a multi-column SELECT */
IF_NULL_ROW /* the if-null-row operator */
+9
View File
@@ -441,6 +441,15 @@ static const PragmaName aPragmaName[] = {
/* iArg: */ 0 },
#endif
#endif
#endif
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
#if defined(SQLITE_ENABLE_NOOP_UPDATE)
{/* zName: */ "noop_update",
/* ePragTyp: */ PragTyp_FLAG,
/* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1,
/* ColNames: */ 0, 0,
/* iArg: */ SQLITE_NoopUpdate },
#endif
#endif
{/* zName: */ "optimize",
/* ePragTyp: */ PragTyp_OPTIMIZE,
+2 -1
View File
@@ -747,7 +747,7 @@ static int sqlite3Prepare(
sParse.checkSchema = 0;
}
if( sParse.rc!=SQLITE_OK && sParse.rc!=SQLITE_DONE ){
if( sParse.checkSchema ){
if( sParse.checkSchema && db->init.busy==0 ){
schemaIsValid(&sParse);
}
if( sParse.pVdbe ){
@@ -808,6 +808,7 @@ static int sqlite3LockAndPrepare(
** reset is considered a permanent error. */
rc = sqlite3Prepare(db, zSql, nBytes, prepFlags, pOld, ppStmt, pzTail);
assert( rc==SQLITE_OK || *ppStmt==0 );
if( rc==SQLITE_OK || db->mallocFailed ) break;
}while( rc==SQLITE_ERROR_RETRY
|| (rc==SQLITE_SCHEMA && (sqlite3ResetOneSchema(db,-1), cnt++)==0) );
sqlite3BtreeLeaveAll(db);
+22
View File
@@ -111,6 +111,28 @@ void sqlite3_randomness(int N, void *pBuf){
sqlite3_mutex_leave(mutex);
}
/*
** Initialize a fast PRNG. A Fast PRNG is called "fast" because it does
** not need a mutex to operate, though it does use a mutex to initialize.
** The quality of the randomness is not as good as the global PRNG.
*/
void sqlite3FastPrngInit(FastPrng *pPrng){
sqlite3_randomness(sizeof(*pPrng), pPrng);
pPrng->x |= 1;
}
/*
** Generate N bytes of pseudo-randomness using a FastPrng
*/
void sqlite3FastRandomness(FastPrng *pPrng, int N, void *P){
unsigned char *pOut = (unsigned char*)P;
while( N-->0 ){
pPrng->x = ((pPrng->x)>>1) ^ ((1+~((pPrng->x)&1)) & 0xd0000001);
pPrng->y = (pPrng->y)*1103515245 + 12345;
*(pOut++) = (pPrng->x ^ pPrng->y) & 0xff;
}
}
#ifndef SQLITE_UNTESTABLE
/*
** For testing purposes, we sometimes want to preserve the state of
+1 -1
View File
@@ -2140,7 +2140,7 @@ int sqlite3ColumnsFromExprList(
if( zName[j]==':' ) nName = j;
}
zName = sqlite3MPrintf(db, "%.*z:%u", nName, zName, ++cnt);
if( cnt>3 ) sqlite3_randomness(sizeof(cnt), &cnt);
if( cnt>3 ) sqlite3FastRandomness(&db->sPrng, sizeof(cnt), &cnt);
}
pCol->zCnName = zName;
pCol->hName = sqlite3StrIHash(zName);
+45 -35
View File
@@ -8941,16 +8941,8 @@ static int do_meta_command(char *zLine, ShellState *p){
char *zNewFilename = 0; /* Name of the database file to open */
int iName = 1; /* Index in azArg[] of the filename */
int newFlag = 0; /* True to delete file before opening */
/* Close the existing database */
session_close_all(p, -1);
close_db(p->db);
p->db = 0;
p->pAuxDb->zDbFilename = 0;
sqlite3_free(p->pAuxDb->zFreeOnClose);
p->pAuxDb->zFreeOnClose = 0;
p->openMode = SHELL_OPEN_UNSPEC;
p->openFlags = 0;
p->szMax = 0;
int openMode = SHELL_OPEN_UNSPEC;
/* Check for command-line arguments */
for(iName=1; iName<nArg; iName++){
const char *z = azArg[iName];
@@ -8958,19 +8950,19 @@ static int do_meta_command(char *zLine, ShellState *p){
newFlag = 1;
#ifdef SQLITE_HAVE_ZLIB
}else if( optionMatch(z, "zip") ){
p->openMode = SHELL_OPEN_ZIPFILE;
openMode = SHELL_OPEN_ZIPFILE;
#endif
}else if( optionMatch(z, "append") ){
p->openMode = SHELL_OPEN_APPENDVFS;
openMode = SHELL_OPEN_APPENDVFS;
}else if( optionMatch(z, "readonly") ){
p->openMode = SHELL_OPEN_READONLY;
openMode = SHELL_OPEN_READONLY;
}else if( optionMatch(z, "nofollow") ){
p->openFlags |= SQLITE_OPEN_NOFOLLOW;
#ifndef SQLITE_OMIT_DESERIALIZE
}else if( optionMatch(z, "deserialize") ){
p->openMode = SHELL_OPEN_DESERIALIZE;
openMode = SHELL_OPEN_DESERIALIZE;
}else if( optionMatch(z, "hexdb") ){
p->openMode = SHELL_OPEN_HEXDB;
openMode = SHELL_OPEN_HEXDB;
}else if( optionMatch(z, "maxsize") && iName+1<nArg ){
p->szMax = integerValue(azArg[++iName]);
#endif /* SQLITE_OMIT_DESERIALIZE */
@@ -8986,6 +8978,18 @@ static int do_meta_command(char *zLine, ShellState *p){
zNewFilename = sqlite3_mprintf("%s", z);
}
}
/* Close the existing database */
session_close_all(p, -1);
close_db(p->db);
p->db = 0;
p->pAuxDb->zDbFilename = 0;
sqlite3_free(p->pAuxDb->zFreeOnClose);
p->pAuxDb->zFreeOnClose = 0;
p->openMode = openMode;
p->openFlags = 0;
p->szMax = 0;
/* If a filename is specified, try to open it first */
if( zNewFilename || p->openMode==SHELL_OPEN_HEXDB ){
if( newFlag && !p->bSafeMode ) shellDeleteFile(zNewFilename);
@@ -10204,30 +10208,31 @@ static int do_meta_command(char *zLine, ShellState *p){
static const struct {
const char *zCtrlName; /* Name of a test-control option */
int ctrlCode; /* Integer code for that option */
int unSafe; /* Not valid for --safe mode */
const char *zUsage; /* Usage notes */
} aCtrl[] = {
{ "always", SQLITE_TESTCTRL_ALWAYS, "BOOLEAN" },
{ "assert", SQLITE_TESTCTRL_ASSERT, "BOOLEAN" },
/*{ "benign_malloc_hooks",SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS, "" },*/
/*{ "bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, "" },*/
{ "byteorder", SQLITE_TESTCTRL_BYTEORDER, "" },
{ "extra_schema_checks",SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS,"BOOLEAN" },
/*{ "fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, "" },*/
{ "imposter", SQLITE_TESTCTRL_IMPOSTER, "SCHEMA ON/OFF ROOTPAGE"},
{ "internal_functions", SQLITE_TESTCTRL_INTERNAL_FUNCTIONS, "" },
{ "localtime_fault", SQLITE_TESTCTRL_LOCALTIME_FAULT,"BOOLEAN" },
{ "never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT, "BOOLEAN" },
{ "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS, "DISABLE-MASK" },
{ "always", SQLITE_TESTCTRL_ALWAYS, 1, "BOOLEAN" },
{ "assert", SQLITE_TESTCTRL_ASSERT, 1, "BOOLEAN" },
/*{ "benign_malloc_hooks",SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS,1, "" },*/
/*{ "bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, 1, "" },*/
{ "byteorder", SQLITE_TESTCTRL_BYTEORDER, 0, "" },
{ "extra_schema_checks",SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS,0,"BOOLEAN" },
/*{ "fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, 1,"" },*/
{ "imposter", SQLITE_TESTCTRL_IMPOSTER,1,"SCHEMA ON/OFF ROOTPAGE"},
{ "internal_functions", SQLITE_TESTCTRL_INTERNAL_FUNCTIONS,0,"" },
{ "localtime_fault", SQLITE_TESTCTRL_LOCALTIME_FAULT,0,"BOOLEAN" },
{ "never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT,1, "BOOLEAN" },
{ "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS,0,"DISABLE-MASK" },
#ifdef YYCOVERAGE
{ "parser_coverage", SQLITE_TESTCTRL_PARSER_COVERAGE, "" },
{ "parser_coverage", SQLITE_TESTCTRL_PARSER_COVERAGE,0,"" },
#endif
{ "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE, "OFFSET " },
{ "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE, "" },
{ "prng_save", SQLITE_TESTCTRL_PRNG_SAVE, "" },
{ "prng_seed", SQLITE_TESTCTRL_PRNG_SEED, "SEED ?db?" },
{ "seek_count", SQLITE_TESTCTRL_SEEK_COUNT, "" },
{ "sorter_mmap", SQLITE_TESTCTRL_SORTER_MMAP, "NMAX" },
{ "tune", SQLITE_TESTCTRL_TUNE, "ID VALUE" },
{ "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE,0, "OFFSET " },
{ "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE,0, "" },
{ "prng_save", SQLITE_TESTCTRL_PRNG_SAVE, 0, "" },
{ "prng_seed", SQLITE_TESTCTRL_PRNG_SEED, 0, "SEED ?db?" },
{ "seek_count", SQLITE_TESTCTRL_SEEK_COUNT, 0, "" },
{ "sorter_mmap", SQLITE_TESTCTRL_SORTER_MMAP, 0, "NMAX" },
{ "tune", SQLITE_TESTCTRL_TUNE, 1, "ID VALUE" },
};
int testctrl = -1;
int iCtrl = -1;
@@ -10275,6 +10280,11 @@ static int do_meta_command(char *zLine, ShellState *p){
if( testctrl<0 ){
utf8_printf(stderr,"Error: unknown test-control: %s\n"
"Use \".testctrl --help\" for help\n", zCmd);
}else if( aCtrl[iCtrl].unSafe && p->bSafeMode ){
utf8_printf(stderr,
"line %d: \".testctrl %s\" may not be used in safe mode\n",
p->lineno, aCtrl[iCtrl].zCtrlName);
exit(1);
}else{
switch(testctrl){
+25
View File
@@ -9932,6 +9932,31 @@ SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp(
*/
SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb);
/*
** CAPI3REF: Wal related information regarding the most recent COMMIT
** EXPERIMENTAL
**
** This function reports on the state of the wal file (if any) for database
** zDb, which should be "main", "temp", or the name of the attached database.
** Its results - the values written to the output parameters - are only
** defined if the most recent SQL command on the connection was a successful
** COMMIT that wrote data to wal-mode database zDb.
**
** Assuming the above conditions are met, output parameter (*pnFrame) is set
** to the total number of frames in the wal file. Parameter (*pnPrior) is
** set to the number of frames that were present in the wal file before the
** most recent transaction was committed. So that the number of frames written
** by the most recent transaction is (*pnFrame)-(*pnPrior).
**
** If successful, SQLITE_OK is returned. Otherwise, an SQLite error code. It
** is not an error if this function is called at a time when the results
** are undefined.
*/
SQLITE_EXPERIMENTAL int sqlite3_wal_info(
sqlite3 *db, const char *zDb,
unsigned int *pnPrior, unsigned int *pnFrame
);
/*
** CAPI3REF: Serialize a database
**
+27 -2
View File
@@ -1166,6 +1166,7 @@ typedef struct DbFixer DbFixer;
typedef struct Schema Schema;
typedef struct Expr Expr;
typedef struct ExprList ExprList;
typedef struct FastPrng FastPrng;
typedef struct FKey FKey;
typedef struct FuncDestructor FuncDestructor;
typedef struct FuncDef FuncDef;
@@ -1287,6 +1288,14 @@ typedef int VList;
# define SQLITE_DEFAULT_WAL_SYNCHRONOUS SQLITE_DEFAULT_SYNCHRONOUS
#endif
/*
** State of a simple PRNG used for the per-connection and per-pager
** pseudo-random number generators.
*/
struct FastPrng {
unsigned int x, y;
};
/*
** Each database file to be accessed by the system is an instance
** of the following structure. There are normally two of these structures
@@ -1532,6 +1541,7 @@ struct sqlite3 {
u32 dbOptFlags; /* Flags to enable/disable optimizations */
u8 enc; /* Text encoding */
u8 autoCommit; /* The auto-commit flag. */
u8 eConcurrent; /* CONCURRENT_* value */
u8 temp_store; /* 1: file 2: memory 0: default */
u8 mallocFailed; /* True if we have seen a malloc failure */
u8 bBenignMalloc; /* Do not require OOMs if true */
@@ -1545,6 +1555,7 @@ struct sqlite3 {
u8 nSqlExec; /* Number of pending OP_SqlExec opcodes */
u8 eOpenState; /* Current condition of the connection */
int nextPagesize; /* Pagesize after VACUUM if >0 */
FastPrng sPrng; /* State of the per-connection PRNG */
i64 nChange; /* Value returned by sqlite3_changes() */
i64 nTotalChange; /* Value returned by sqlite3_total_changes() */
int aLimit[SQLITE_N_LIMIT]; /* Limits */
@@ -1654,6 +1665,13 @@ struct sqlite3 {
#endif
};
/*
** Candidate values for sqlite3.eConcurrent
*/
#define CONCURRENT_NONE 0
#define CONCURRENT_OPEN 1
#define CONCURRENT_SCHEMA 2
/*
** A macro to discover the encoding of a database.
*/
@@ -1714,6 +1732,9 @@ struct sqlite3 {
/* the count using a callback. */
#define SQLITE_CorruptRdOnly HI(0x00002) /* Prohibit writes due to error */
/* Flags used by the Pragma noop_update enhancement */
#define SQLITE_NoopUpdate HI(0x0001000) /* UPDATE operations are no-ops */
/* Flags used only if debugging */
#ifdef SQLITE_DEBUG
#define SQLITE_SqlTrace HI(0x0100000) /* Debug print SQL as it executes */
@@ -3555,6 +3576,8 @@ struct Parse {
AutoincInfo *pAinc; /* Information about AUTOINCREMENT counters */
Parse *pToplevel; /* Parse structure for main program (or NULL) */
Table *pTriggerTab; /* Table triggers are being coded for */
TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */
ParseCleanup *pCleanup; /* List of cleanup operations to run after parse */
union {
int addrCrTab; /* Address of OP_CreateBtree on CREATE TABLE */
Returning *pReturning; /* The RETURNING clause */
@@ -3609,9 +3632,7 @@ struct Parse {
Token sArg; /* Complete text of a module argument */
Table **apVtabLock; /* Pointer to virtual tables needing locking */
#endif
TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */
With *pWith; /* Current WITH clause, or NULL */
ParseCleanup *pCleanup; /* List of cleanup operations to run after parse */
#ifndef SQLITE_OMIT_ALTERTABLE
RenameToken *pRename; /* Tokens subject to renaming by ALTER TABLE */
#endif
@@ -4629,6 +4650,8 @@ Vdbe *sqlite3GetVdbe(Parse*);
void sqlite3PrngSaveState(void);
void sqlite3PrngRestoreState(void);
#endif
void sqlite3FastPrngInit(FastPrng*);
void sqlite3FastRandomness(FastPrng*, int N, void *P);
void sqlite3RollbackAll(sqlite3*,int);
void sqlite3CodeVerifySchema(Parse*, int);
void sqlite3CodeVerifyNamedSchema(Parse*, const char *zDb);
@@ -5145,6 +5168,7 @@ const char *sqlite3JournalModename(int);
int sqlite3FkRequired(Parse*, Table*, int*, int);
u32 sqlite3FkOldmask(Parse*, Table*);
FKey *sqlite3FkReferences(Table *);
void sqlite3FkClearTriggerCache(sqlite3*,int);
#else
#define sqlite3FkActions(a,b,c,d,e,f)
#define sqlite3FkCheck(a,b,c,d,e,f)
@@ -5152,6 +5176,7 @@ const char *sqlite3JournalModename(int);
#define sqlite3FkOldmask(a,b) 0
#define sqlite3FkRequired(a,b,c,d) 0
#define sqlite3FkReferences(a) 0
#define sqlite3FkClearTriggerCache(a,b)
#endif
#ifndef SQLITE_OMIT_FOREIGN_KEY
void sqlite3FkDelete(sqlite3 *, Table*);
+38 -2
View File
@@ -8051,6 +8051,41 @@ static int SQLITE_TCLAPI test_dbconfig_maindbname_icecube(
}
}
/*
** Usage: sqlite3_wal_info DB DBNAME
*/
static int SQLITE_TCLAPI test_wal_info(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int rc;
sqlite3 *db;
char *zName;
unsigned int nPrior;
unsigned int nFrame;
if( objc!=3 ){
Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME");
return TCL_ERROR;
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
zName = Tcl_GetString(objv[2]);
rc = sqlite3_wal_info(db, zName, &nPrior, &nFrame);
if( rc!=SQLITE_OK ){
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
return TCL_ERROR;
}else{
Tcl_Obj *pNew = Tcl_NewObj();
Tcl_ListObjAppendElement(interp, pNew, Tcl_NewWideIntObj((i64)nPrior));
Tcl_ListObjAppendElement(interp, pNew, Tcl_NewWideIntObj((i64)nFrame));
Tcl_SetObjResult(interp, pNew);
}
return TCL_OK;
}
/*
** Usage: sqlite3_mmap_warm DB DBNAME
*/
@@ -8611,8 +8646,9 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "sqlite3_snapshot_open_blob", test_snapshot_open_blob, 0 },
{ "sqlite3_snapshot_cmp_blob", test_snapshot_cmp_blob, 0 },
#endif
{ "sqlite3_delete_database", test_delete_database, 0 },
{ "atomic_batch_write", test_atomic_batch_write, 0 },
{ "sqlite3_delete_database", test_delete_database, 0 },
{ "sqlite3_wal_info", test_wal_info, 0 },
{ "atomic_batch_write", test_atomic_batch_write, 0 },
{ "sqlite3_mmap_warm", test_mmap_warm, 0 },
{ "sqlite3_config_sorterref", test_config_sorterref, 0 },
{ "sqlite3_autovacuum_pages", test_autovacuum_pages, 0 },
+6
View File
@@ -681,6 +681,12 @@ Tcl_SetVar2(interp, "sqlite_options", "mergesort", "1", TCL_GLOBAL_ONLY);
Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "1", TCL_GLOBAL_ONLY);
#endif
#ifndef SQLITE_OMIT_CONCURRENT
Tcl_SetVar2(interp, "sqlite_options", "concurrent", "1", TCL_GLOBAL_ONLY);
#else
Tcl_SetVar2(interp, "sqlite_options", "concurrent", "0", TCL_GLOBAL_ONLY);
#endif
#ifdef SQLITE_OMIT_UTF16
Tcl_SetVar2(interp, "sqlite_options", "utf16", "0", TCL_GLOBAL_ONLY);
#else
+8 -5
View File
@@ -933,7 +933,7 @@ static void codeReturningTrigger(
}
sqlite3ExprListDelete(db, sSelect.pEList);
pNew = sqlite3ExpandReturning(pParse, pReturning->pReturnEL, pTab);
if( pNew ){
if( !db->mallocFailed ){
NameContext sNC;
memset(&sNC, 0, sizeof(sNC));
if( pReturning->nRetCol==0 ){
@@ -945,7 +945,9 @@ static void codeReturningTrigger(
sNC.ncFlags = NC_UBaseReg;
pParse->eTriggerOp = pTrigger->op;
pParse->pTriggerTab = pTab;
if( sqlite3ResolveExprListNames(&sNC, pNew)==SQLITE_OK ){
if( sqlite3ResolveExprListNames(&sNC, pNew)==SQLITE_OK
&& !db->mallocFailed
){
int i;
int nCol = pNew->nExpr;
int reg = pParse->nMem+1;
@@ -953,16 +955,17 @@ static void codeReturningTrigger(
pReturning->iRetReg = reg;
for(i=0; i<nCol; i++){
Expr *pCol = pNew->a[i].pExpr;
assert( pCol!=0 ); /* Due to !db->mallocFailed ~9 lines above */
sqlite3ExprCodeFactorable(pParse, pCol, reg+i);
}
sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, i, reg+i);
sqlite3VdbeAddOp2(v, OP_NewRowid, pReturning->iRetCur, reg+i+1);
sqlite3VdbeAddOp3(v, OP_Insert, pReturning->iRetCur, reg+i, reg+i+1);
}
sqlite3ExprListDelete(db, pNew);
pParse->eTriggerOp = 0;
pParse->pTriggerTab = 0;
}
sqlite3ExprListDelete(db, pNew);
pParse->eTriggerOp = 0;
pParse->pTriggerTab = 0;
}
+11
View File
@@ -451,6 +451,17 @@ void sqlite3Update(
*/
chngRowid = chngPk = 0;
for(i=0; i<pChanges->nExpr; i++){
#if defined(SQLITE_ENABLE_NOOP_UPDATE) && !defined(SQLITE_OMIT_FLAG_PRAGMAS)
if( db->flags & SQLITE_NoopUpdate ){
Token x;
sqlite3ExprDelete(db, pChanges->a[i].pExpr);
x.z = pChanges->a[i].zEName;
x.n = sqlite3Strlen30(x.z);
pChanges->a[i].pExpr =
sqlite3PExpr(pParse, TK_UPLUS, sqlite3ExprAlloc(db, TK_ID, &x, 0), 0);
if( db->mallocFailed ) goto update_cleanup;
}
#endif
u8 hCol = sqlite3StrIHash(pChanges->a[i].zEName);
/* If this is an UPDATE with a FROM clause, do not resolve expressions
** here. The call to sqlite3Select() below will do that. */
+1
View File
@@ -389,6 +389,7 @@ end_of_vacuum:
** is closed by the DETACH.
*/
db->autoCommit = 1;
assert( db->eConcurrent==0 );
if( pDb ){
sqlite3BtreeClose(pDb->pBt);
+49 -7
View File
@@ -3474,6 +3474,7 @@ case OP_Savepoint: {
** is committed.
*/
int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint;
assert( db->eConcurrent==0 || db->isTransactionSavepoint==0 );
if( isTransaction && p1==SAVEPOINT_RELEASE ){
if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){
goto vdbe_return;
@@ -3557,23 +3558,31 @@ case OP_Savepoint: {
break;
}
/* Opcode: AutoCommit P1 P2 * * *
/* Opcode: AutoCommit P1 P2 P3 * *
**
** Set the database auto-commit flag to P1 (1 or 0). If P2 is true, roll
** back any currently active btree transactions. If there are any active
** VMs (apart from this one), then a ROLLBACK fails. A COMMIT fails if
** there are active writing VMs or active VMs that use shared cache.
**
** If P3 is non-zero, then this instruction is being executed as part of
** a "BEGIN CONCURRENT" command.
**
** This instruction causes the VM to halt.
*/
case OP_AutoCommit: {
int desiredAutoCommit;
int iRollback;
int bConcurrent;
int hrc;
desiredAutoCommit = pOp->p1;
iRollback = pOp->p2;
bConcurrent = pOp->p3;
assert( desiredAutoCommit==1 || desiredAutoCommit==0 );
assert( desiredAutoCommit==1 || iRollback==0 );
assert( desiredAutoCommit==0 || bConcurrent==0 );
assert( db->autoCommit==0 || db->eConcurrent==CONCURRENT_NONE );
assert( db->nVdbeActive>0 ); /* At least this one VM is active */
assert( p->bIsReader );
@@ -3582,10 +3591,17 @@ case OP_AutoCommit: {
assert( desiredAutoCommit==1 );
sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
db->autoCommit = 1;
}else if( desiredAutoCommit && db->nVdbeWrite>0 ){
/* If this instruction implements a COMMIT and other VMs are writing
** return an error indicating that the other VMs must complete first.
*/
db->eConcurrent = CONCURRENT_NONE;
}else if( desiredAutoCommit
&& (db->nVdbeWrite>0 || (db->eConcurrent && db->nVdbeActive>1)) ){
/* A transaction may only be committed if there are no other active
** writer VMs. If the transaction is CONCURRENT, then it may only be
** committed if there are no active VMs at all (readers or writers).
**
** If this instruction is a COMMIT and the transaction may not be
** committed due to one of the conditions above, return an error
** indicating that other VMs must complete before the COMMIT can
** be processed. */
sqlite3VdbeError(p, "cannot commit transaction - "
"SQL statements in progress");
rc = SQLITE_BUSY;
@@ -3595,12 +3611,16 @@ case OP_AutoCommit: {
}else{
db->autoCommit = (u8)desiredAutoCommit;
}
if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
hrc = sqlite3VdbeHalt(p);
if( (hrc & 0xFF)==SQLITE_BUSY ){
p->pc = (int)(pOp - aOp);
db->autoCommit = (u8)(1-desiredAutoCommit);
p->rc = rc = SQLITE_BUSY;
p->rc = hrc;
rc = SQLITE_BUSY;
goto vdbe_return;
}
assert( bConcurrent==CONCURRENT_NONE || bConcurrent==CONCURRENT_OPEN );
db->eConcurrent = (u8)bConcurrent;
sqlite3CloseSavepoints(db);
if( p->rc==SQLITE_OK ){
rc = SQLITE_DONE;
@@ -3807,12 +3827,24 @@ case OP_SetCookie: {
pDb = &db->aDb[pOp->p1];
assert( pDb->pBt!=0 );
assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) );
#ifndef SQLITE_OMIT_CONCURRENT
if( db->eConcurrent
&& (pOp->p2==BTREE_USER_VERSION || pOp->p2==BTREE_APPLICATION_ID)
){
rc = SQLITE_ERROR;
sqlite3VdbeError(p, "cannot modify %s within CONCURRENT transaction",
pOp->p2==BTREE_USER_VERSION ? "user_version" : "application_id"
);
goto abort_due_to_error;
}
#endif
/* See note about index shifting on OP_ReadCookie */
rc = sqlite3BtreeUpdateMeta(pDb->pBt, pOp->p2, pOp->p3);
if( pOp->p2==BTREE_SCHEMA_VERSION ){
/* When the schema cookie changes, record the new cookie internally */
pDb->pSchema->schema_cookie = pOp->p3 - pOp->p5;
db->mDbFlags |= DBFLAG_SchemaChange;
sqlite3FkClearTriggerCache(db, pOp->p1);
}else if( pOp->p2==BTREE_FILE_FORMAT ){
/* Record changes in the file format */
pDb->pSchema->file_format = pOp->p3;
@@ -3955,6 +3987,11 @@ case OP_OpenWrite:
pX = pDb->pBt;
assert( pX!=0 );
if( pOp->opcode==OP_OpenWrite ){
#ifndef SQLITE_OMIT_CONCURRENT
if( db->eConcurrent==CONCURRENT_OPEN && p2==1 && iDb!=1 ){
db->eConcurrent = CONCURRENT_SCHEMA;
}
#endif
assert( OPFLAG_FORDELETE==BTREE_FORDELETE );
wrFlag = BTREE_WRCSR | (pOp->p5 & OPFLAG_FORDELETE);
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
@@ -7578,6 +7615,11 @@ case OP_CursorUnlock: {
*/
case OP_TableLock: {
u8 isWriteLock = (u8)pOp->p3;
#ifndef SQLITE_OMIT_CONCURRENT
if( isWriteLock && db->eConcurrent && pOp->p2==1 && pOp->p1!=1 ){
db->eConcurrent = CONCURRENT_SCHEMA;
}
#endif
if( isWriteLock || 0==(db->flags&SQLITE_ReadUncommit) ){
int p1 = pOp->p1;
assert( p1>=0 && p1<db->nDb );
+24 -4
View File
@@ -2682,10 +2682,27 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
assert( i!=1 );
nTrans++;
}
rc = sqlite3PagerExclusiveLock(pPager);
rc = sqlite3BtreeExclusiveLock(pBt);
sqlite3BtreeLeave(pBt);
}
}
#ifndef SQLITE_OMIT_CONCURRENT
if( db->eConcurrent && (rc & 0xFF)==SQLITE_BUSY ){
/* An SQLITE_BUSY or SQLITE_BUSY_SNAPSHOT was encountered while
** attempting to take the WRITER lock on a wal file. Release the
** WRITER locks on all wal files and return early. */
for(i=0; i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
if( sqlite3BtreeTxnState(pBt)==SQLITE_TXN_WRITE ){
sqlite3BtreeEnter(pBt);
sqlite3PagerDropExclusiveLock(sqlite3BtreePager(pBt));
sqlite3BtreeLeave(pBt);
}
}
}
#endif
if( rc!=SQLITE_OK ){
return rc;
}
@@ -3088,6 +3105,7 @@ int sqlite3VdbeHalt(Vdbe *p){
sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
sqlite3CloseSavepoints(db);
db->autoCommit = 1;
db->eConcurrent = CONCURRENT_NONE;
p->nChange = 0;
}
}
@@ -3126,9 +3144,9 @@ int sqlite3VdbeHalt(Vdbe *p){
** is required. */
rc = vdbeCommit(db, p);
}
if( rc==SQLITE_BUSY && p->readOnly ){
if( (rc & 0xFF)==SQLITE_BUSY && p->readOnly ){
sqlite3VdbeLeave(p);
return SQLITE_BUSY;
return rc;
}else if( rc!=SQLITE_OK ){
p->rc = rc;
sqlite3RollbackAll(db, SQLITE_OK);
@@ -3153,6 +3171,7 @@ int sqlite3VdbeHalt(Vdbe *p){
sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
sqlite3CloseSavepoints(db);
db->autoCommit = 1;
db->eConcurrent = CONCURRENT_NONE;
p->nChange = 0;
}
}
@@ -3174,6 +3193,7 @@ int sqlite3VdbeHalt(Vdbe *p){
sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
sqlite3CloseSavepoints(db);
db->autoCommit = 1;
db->eConcurrent = CONCURRENT_NONE;
p->nChange = 0;
}
}
@@ -4934,7 +4954,7 @@ int sqlite3VdbeIdxRowid(sqlite3 *db, BtCursor *pCur, i64 *rowid){
/* The index entry must begin with a header size */
getVarint32NR((u8*)m.z, szHdr);
testcase( szHdr==3 );
testcase( szHdr==m.n );
testcase( szHdr==(u32)m.n );
testcase( szHdr>0x7fffffff );
assert( m.n>=0 );
if( unlikely(szHdr<3 || szHdr>(unsigned)m.n) ){
+323 -88
View File
@@ -526,8 +526,10 @@ struct Wal {
WalIndexHdr hdr; /* Wal-index header for current transaction */
u32 minFrame; /* Ignore wal frames before this one */
u32 iReCksum; /* On commit, recalculate checksums from here */
u32 nPriorFrame; /* For sqlite3WalInfo() */
const char *zWalName; /* Name of WAL file */
u32 nCkpt; /* Checkpoint sequence counter in the wal-header */
FastPrng sPrng; /* Random number generator */
#ifdef SQLITE_DEBUG
u8 lockError; /* True if a locking error has occurred */
#endif
@@ -925,7 +927,7 @@ static const char *walLockName(int lockIdx){
/*
** Set or release locks on the WAL. Locks are either shared or exclusive.
** A lock cannot be moved directly between shared and exclusive - it must go
** through the unlocked state first.
** through the concurrent state first.
**
** In locking_mode=EXCLUSIVE, all of these routines become no-ops.
*/
@@ -1228,7 +1230,7 @@ static int walIndexRecover(Wal *pWal){
/* Obtain an exclusive lock on all byte in the locking range not already
** locked by the caller. The caller is guaranteed to have locked the
** WAL_WRITE_LOCK byte, and may have also locked the WAL_CKPT_LOCK byte.
** If successful, the same bytes that are locked here are unlocked before
** If successful, the same bytes that are locked here are concurrent before
** this function returns.
*/
assert( pWal->ckptLock==1 || pWal->ckptLock==0 );
@@ -1546,6 +1548,7 @@ int sqlite3WalOpen(
pRet->syncHeader = 1;
pRet->padToSectorBoundary = 1;
pRet->exclusiveMode = (bNoShm ? WAL_HEAPMEMORY_MODE: WAL_NORMAL_MODE);
sqlite3FastPrngInit(&pRet->sPrng);
/* Open file handle on the write-ahead log file. */
flags = (SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_WAL);
@@ -2183,7 +2186,7 @@ static int walCheckpoint(
rc = SQLITE_BUSY;
}else if( eMode>=SQLITE_CHECKPOINT_RESTART ){
u32 salt1;
sqlite3_randomness(4, &salt1);
sqlite3FastRandomness(&pWal->sPrng, 4, &salt1);
assert( pInfo->nBackfill==pWal->hdr.mxFrame );
rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(1), WAL_NREADER-1);
if( rc==SQLITE_OK ){
@@ -2299,6 +2302,49 @@ int sqlite3WalClose(
return rc;
}
/*
** Try to copy the wal-index header from shared-memory into (*pHdr). Return
** zero if successful or non-zero otherwise. If the header is corrupted
** (either because the two copies are inconsistent or because the checksum
** values are incorrect), the read fails and non-zero is returned.
*/
static int walIndexLoadHdr(Wal *pWal, WalIndexHdr *pHdr){
u32 aCksum[2]; /* Checksum on the header content */
WalIndexHdr h2; /* Second copy of the header content */
WalIndexHdr volatile *aHdr; /* Header in shared memory */
/* The first page of the wal-index must be mapped at this point. */
assert( pWal->nWiData>0 && pWal->apWiData[0] );
/* Read the header. This might happen concurrently with a write to the
** same area of shared memory on a different CPU in a SMP,
** meaning it is possible that an inconsistent snapshot is read
** from the file. If this happens, return non-zero.
**
** There are two copies of the header at the beginning of the wal-index.
** When reading, read [0] first then [1]. Writes are in the reverse order.
** Memory barriers are used to prevent the compiler or the hardware from
** reordering the reads and writes.
*/
aHdr = walIndexHdr(pWal);
memcpy(pHdr, (void *)&aHdr[0], sizeof(h2));
walShmBarrier(pWal);
memcpy(&h2, (void *)&aHdr[1], sizeof(h2));
if( memcmp(&h2, pHdr, sizeof(h2))!=0 ){
return 1; /* Dirty read */
}
if( h2.isInit==0 ){
return 1; /* Malformed header - probably all zeros */
}
walChecksumBytes(1, (u8*)&h2, sizeof(h2)-sizeof(h2.aCksum), 0, aCksum);
if( aCksum[0]!=h2.aCksum[0] || aCksum[1]!=h2.aCksum[1] ){
return 1; /* Checksum does not match */
}
return 0;
}
/*
** Try to read the wal-index header. Return 0 on success and 1 if
** there is a problem.
@@ -2317,43 +2363,10 @@ int sqlite3WalClose(
** is read successfully and the checksum verified, return zero.
*/
static SQLITE_NO_TSAN int walIndexTryHdr(Wal *pWal, int *pChanged){
u32 aCksum[2]; /* Checksum on the header content */
WalIndexHdr h1, h2; /* Two copies of the header content */
WalIndexHdr volatile *aHdr; /* Header in shared memory */
WalIndexHdr h1; /* Copy of the header content */
/* The first page of the wal-index must be mapped at this point. */
assert( pWal->nWiData>0 && pWal->apWiData[0] );
/* Read the header. This might happen concurrently with a write to the
** same area of shared memory on a different CPU in a SMP,
** meaning it is possible that an inconsistent snapshot is read
** from the file. If this happens, return non-zero.
**
** tag-20200519-1:
** There are two copies of the header at the beginning of the wal-index.
** When reading, read [0] first then [1]. Writes are in the reverse order.
** Memory barriers are used to prevent the compiler or the hardware from
** reordering the reads and writes. TSAN and similar tools can sometimes
** give false-positive warnings about these accesses because the tools do not
** account for the double-read and the memory barrier. The use of mutexes
** here would be problematic as the memory being accessed is potentially
** shared among multiple processes and not all mutex implementions work
** reliably in that environment.
*/
aHdr = walIndexHdr(pWal);
memcpy(&h1, (void *)&aHdr[0], sizeof(h1)); /* Possible TSAN false-positive */
walShmBarrier(pWal);
memcpy(&h2, (void *)&aHdr[1], sizeof(h2));
if( memcmp(&h1, &h2, sizeof(h1))!=0 ){
return 1; /* Dirty read */
}
if( h1.isInit==0 ){
return 1; /* Malformed header - probably all zeros */
}
walChecksumBytes(1, (u8*)&h1, sizeof(h1)-sizeof(h1.aCksum), 0, aCksum);
if( aCksum[0]!=h1.aCksum[0] || aCksum[1]!=h1.aCksum[1] ){
return 1; /* Checksum does not match */
if( walIndexLoadHdr(pWal, &h1) ){
return 1;
}
if( memcmp(&pWal->hdr, &h1, sizeof(WalIndexHdr)) ){
@@ -3049,6 +3062,7 @@ int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
testcase( rc==SQLITE_PROTOCOL );
testcase( rc==SQLITE_OK );
pWal->nPriorFrame = pWal->hdr.mxFrame;
#ifdef SQLITE_ENABLE_SNAPSHOT
if( rc==SQLITE_OK ){
if( pSnapshot && memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){
@@ -3154,8 +3168,7 @@ int sqlite3WalFindFrame(
return SQLITE_OK;
}
/* Search the hash table or tables for an entry matching page number
** pgno. Each iteration of the following for() loop searches one
/* Each iteration of the following for() loop searches one
** hash table (each hash table indexes up to HASHTABLE_NPAGE frames).
**
** This code might run concurrently to the code in walIndexAppend()
@@ -3261,6 +3274,35 @@ Pgno sqlite3WalDbsize(Wal *pWal){
return 0;
}
/*
** Take the WRITER lock on the WAL file. Return SQLITE_OK if successful,
** or an SQLite error code otherwise. This routine does not invoke any
** busy-handler callbacks, that is done at a higher level.
*/
static int walWriteLock(Wal *pWal){
int rc;
/* Cannot start a write transaction without first holding a read lock */
assert( pWal->readLock>=0 );
assert( pWal->writeLock==0 );
assert( pWal->iReCksum==0 );
/* If this is a read-only connection, obtaining a write-lock is not
** possible. In this case return SQLITE_READONLY. Otherwise, attempt
** to grab the WRITER lock. Set Wal.writeLock to true and return
** SQLITE_OK if successful, or leave Wal.writeLock clear and return
** an SQLite error code (possibly SQLITE_BUSY) otherwise. */
if( pWal->readOnly ){
rc = SQLITE_READONLY;
}else{
rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1);
if( rc==SQLITE_OK ){
pWal->writeLock = 1;
}
}
return rc;
}
/*
** This function starts a write transaction on the WAL.
@@ -3287,38 +3329,209 @@ int sqlite3WalBeginWriteTransaction(Wal *pWal){
return SQLITE_OK;
}
#endif
/* Cannot start a write transaction without first holding a read
** transaction. */
assert( pWal->readLock>=0 );
assert( pWal->writeLock==0 && pWal->iReCksum==0 );
if( pWal->readOnly ){
return SQLITE_READONLY;
rc = walWriteLock(pWal);
if( rc==SQLITE_OK ){
/* If another connection has written to the database file since the
** time the read transaction on this connection was started, then
** the write is disallowed. Release the WRITER lock and return
** SQLITE_BUSY_SNAPSHOT in this case. */
if( memcmp(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0 ){
walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
pWal->writeLock = 0;
rc = SQLITE_BUSY_SNAPSHOT;
}
}
/* Only one writer allowed at a time. Get the write lock. Return
** SQLITE_BUSY if unable.
*/
rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1);
if( rc ){
return rc;
}
pWal->writeLock = 1;
/* If another connection has written to the database file since the
** time the read transaction on this connection was started, then
** the write is disallowed.
*/
if( memcmp(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0 ){
walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
pWal->writeLock = 0;
rc = SQLITE_BUSY_SNAPSHOT;
}
return rc;
}
/*
** This function is called by a writer that has a read-lock on aReadmark[0]
** (pWal->readLock==0). This function relinquishes that lock and takes a
** lock on a different aReadmark[] slot.
**
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
*/
static int walUpgradeReadlock(Wal *pWal){
int cnt;
int rc;
assert( pWal->writeLock && pWal->readLock==0 );
walUnlockShared(pWal, WAL_READ_LOCK(0));
pWal->readLock = -1;
cnt = 0;
do{
int notUsed;
rc = walTryBeginRead(pWal, &notUsed, 1, ++cnt);
}while( rc==WAL_RETRY );
assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */
testcase( (rc&0xff)==SQLITE_IOERR );
testcase( rc==SQLITE_PROTOCOL );
testcase( rc==SQLITE_OK );
return rc;
}
#ifndef SQLITE_OMIT_CONCURRENT
/*
** This function is only ever called when committing a "BEGIN CONCURRENT"
** transaction. It may be assumed that no frames have been written to
** the wal file. The second parameter is a pointer to the in-memory
** representation of page 1 of the database (which may or may not be
** dirty). The third is a bitvec with a bit set for each page in the
** database file that was read by the current concurrent transaction.
**
** This function performs three tasks:
**
** 1) It obtains the WRITER lock on the wal file,
**
** 2) It checks that there are no conflicts between the current
** transaction and any transactions committed to the wal file since
** it was opened, and
**
** 3) It ejects any non-dirty pages from the page-cache that have been
** written by another client since the CONCURRENT transaction was started
** (so as to avoid ending up with an inconsistent cache after the
** current transaction is committed).
**
** If no error occurs and the caller may proceed with committing the
** transaction, SQLITE_OK is returned. SQLITE_BUSY is returned if the WRITER
** lock cannot be obtained. Or, if the WRITER lock can be obtained but there
** are conflicts with a committed transaction, SQLITE_BUSY_SNAPSHOT. Finally,
** if an error (i.e. an OOM condition or IO error), an SQLite error code
** is returned.
*/
int sqlite3WalLockForCommit(
Wal *pWal,
PgHdr *pPg1,
Bitvec *pAllRead,
Pgno *piConflict
){
int rc = walWriteLock(pWal);
/* If the database has been modified since this transaction was started,
** check if it is still possible to commit. The transaction can be
** committed if:
**
** a) None of the pages in pList have been modified since the
** transaction opened, and
**
** b) The database schema cookie has not been modified since the
** transaction was started.
*/
if( rc==SQLITE_OK ){
WalIndexHdr head;
if( walIndexLoadHdr(pWal, &head) ){
/* This branch is taken if the wal-index header is corrupted. This
** occurs if some other writer has crashed while committing a
** transaction to this database since the current concurrent transaction
** was opened. */
rc = SQLITE_BUSY_SNAPSHOT;
}else if( memcmp(&pWal->hdr, (void*)&head, sizeof(WalIndexHdr))!=0 ){
int iHash;
int iLast = walFramePage(head.mxFrame);
u32 iFirst = pWal->hdr.mxFrame+1; /* First wal frame to check */
if( memcmp(pWal->hdr.aSalt, (u32*)head.aSalt, sizeof(u32)*2) ){
assert( pWal->readLock==0 );
iFirst = 1;
}
if( pPg1==0 ){
/* If pPg1==0, then the current transaction modified the database
** schema. This means it conflicts with all other transactions. */
*piConflict = 1;
rc = SQLITE_BUSY_SNAPSHOT;
}
for(iHash=walFramePage(iFirst); rc==SQLITE_OK && iHash<=iLast; iHash++){
WalHashLoc sLoc;
rc = walHashGet(pWal, iHash, &sLoc);
if( rc==SQLITE_OK ){
u32 i, iMin, iMax;
assert( head.mxFrame>=sLoc.iZero );
iMin = (sLoc.iZero >= iFirst) ? 1 : (iFirst - sLoc.iZero);
iMax = (iHash==0) ? HASHTABLE_NPAGE_ONE : HASHTABLE_NPAGE;
if( iMax>(head.mxFrame-sLoc.iZero) ) iMax = (head.mxFrame-sLoc.iZero);
for(i=iMin; rc==SQLITE_OK && i<=iMax; i++){
PgHdr *pPg;
if( sLoc.aPgno[i-1]==1 ){
/* Check that the schema cookie has not been modified. If
** it has not, the commit can proceed. */
u8 aNew[4];
u8 *aOld = &((u8*)pPg1->pData)[40];
int sz;
i64 iOffset;
sz = pWal->hdr.szPage;
sz = (sz&0xfe00) + ((sz&0x0001)<<16);
iOffset = walFrameOffset(i+sLoc.iZero, sz) + WAL_FRAME_HDRSIZE+40;
rc = sqlite3OsRead(pWal->pWalFd, aNew, sizeof(aNew), iOffset);
if( rc==SQLITE_OK && memcmp(aOld, aNew, sizeof(aNew)) ){
rc = SQLITE_BUSY_SNAPSHOT;
}
}else if( sqlite3BitvecTestNotNull(pAllRead, sLoc.aPgno[i-1]) ){
*piConflict = sLoc.aPgno[i-1];
rc = SQLITE_BUSY_SNAPSHOT;
}else
if( (pPg = sqlite3PagerLookup(pPg1->pPager, sLoc.aPgno[i-1])) ){
/* Page aPgno[i-1], which is present in the pager cache, has been
** modified since the current CONCURRENT transaction was started.
** However it was not read by the current transaction, so is not
** a conflict. There are two possibilities: (a) the page was
** allocated at the of the file by the current transaction or
** (b) was present in the cache at the start of the transaction.
**
** For case (a), do nothing. This page will be moved within the
** database file by the commit code to avoid the conflict. The
** call to PagerUnref() is to release the reference grabbed by
** the sqlite3PagerLookup() above.
**
** In case (b), drop the page from the cache - otherwise
** following the snapshot upgrade the cache would be inconsistent
** with the database as stored on disk. */
if( sqlite3PagerIswriteable(pPg) ){
sqlite3PagerUnref(pPg);
}else{
sqlite3PcacheDrop(pPg);
}
}
}
}
}
}
}
pWal->nPriorFrame = pWal->hdr.mxFrame;
return rc;
}
/* !defined(SQLITE_OMIT_CONCURRENT)
**
** This function is called as part of committing an CONCURRENT transaction.
** It is assumed that sqlite3WalLockForCommit() has already been successfully
** called and so (a) the WRITER lock is held and (b) it is known that the
** wal-index-header stored in shared memory is not corrupt.
**
** Before returning, this function upgrades the client so that it is
** operating on the database snapshot currently at the head of the wal file
** (even if the CONCURRENT transaction ran against an older snapshot).
**
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
*/
int sqlite3WalUpgradeSnapshot(Wal *pWal){
int rc = SQLITE_OK;
assert( pWal->writeLock );
memcpy(&pWal->hdr, (void*)walIndexHdr(pWal), sizeof(WalIndexHdr));
/* If this client has its read-lock on slot aReadmark[0] and the entire
** wal has not been checkpointed, switch it to a different slot. Otherwise
** any reads performed between now and committing the transaction will
** read from the old snapshot - not the one just upgraded to. */
if( pWal->readLock==0 && pWal->hdr.mxFrame!=walCkptInfo(pWal)->nBackfill ){
rc = walUpgradeReadlock(pWal);
}
return rc;
}
#endif /* SQLITE_OMIT_CONCURRENT */
/*
** End a write transaction. The commit has already been done. This
** routine merely releases the lock.
@@ -3345,9 +3558,14 @@ int sqlite3WalEndWriteTransaction(Wal *pWal){
** Otherwise, if the callback function does not return an error, this
** function returns SQLITE_OK.
*/
int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){
int sqlite3WalUndo(
Wal *pWal,
int (*xUndo)(void *, Pgno),
void *pUndoCtx,
int bConcurrent /* True if this is a CONCURRENT transaction */
){
int rc = SQLITE_OK;
if( ALWAYS(pWal->writeLock) ){
if( pWal->writeLock ){
Pgno iMax = pWal->hdr.mxFrame;
Pgno iFrame;
@@ -3355,6 +3573,13 @@ int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){
** was in before the client began writing to the database.
*/
memcpy(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr));
#ifndef SQLITE_OMIT_CONCURRENT
if( bConcurrent ){
pWal->hdr.aCksum[0]++;
}
#else
UNUSED_PARAMETER(bConcurrent);
#endif
for(iFrame=pWal->hdr.mxFrame+1;
ALWAYS(rc==SQLITE_OK) && iFrame<=iMax;
@@ -3386,7 +3611,6 @@ int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){
** point in the event of a savepoint rollback (via WalSavepointUndo()).
*/
void sqlite3WalSavepoint(Wal *pWal, u32 *aWalData){
assert( pWal->writeLock );
aWalData[0] = pWal->hdr.mxFrame;
aWalData[1] = pWal->hdr.aFrameCksum[0];
aWalData[2] = pWal->hdr.aFrameCksum[1];
@@ -3402,7 +3626,7 @@ void sqlite3WalSavepoint(Wal *pWal, u32 *aWalData){
int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData){
int rc = SQLITE_OK;
assert( pWal->writeLock );
assert( pWal->writeLock || aWalData[0]==pWal->hdr.mxFrame );
assert( aWalData[3]!=pWal->nCkpt || aWalData[0]<=pWal->hdr.mxFrame );
if( aWalData[3]!=pWal->nCkpt ){
@@ -3438,14 +3662,13 @@ int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData){
*/
static int walRestartLog(Wal *pWal){
int rc = SQLITE_OK;
int cnt;
if( pWal->readLock==0 ){
volatile WalCkptInfo *pInfo = walCkptInfo(pWal);
assert( pInfo->nBackfill==pWal->hdr.mxFrame );
if( pInfo->nBackfill>0 ){
u32 salt1;
sqlite3_randomness(4, &salt1);
sqlite3FastRandomness(&pWal->sPrng, 4, &salt1);
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
@@ -3459,21 +3682,21 @@ static int walRestartLog(Wal *pWal){
** to handle if this transaction is rolled back. */
walRestartHdr(pWal, salt1);
walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
pWal->nPriorFrame = 0;
}else if( rc!=SQLITE_BUSY ){
return rc;
}
}
walUnlockShared(pWal, WAL_READ_LOCK(0));
pWal->readLock = -1;
cnt = 0;
do{
int notUsed;
rc = walTryBeginRead(pWal, &notUsed, 1, ++cnt);
}while( rc==WAL_RETRY );
assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */
testcase( (rc&0xff)==SQLITE_IOERR );
testcase( rc==SQLITE_PROTOCOL );
testcase( rc==SQLITE_OK );
/* Regardless of whether or not the wal file was restarted, change the
** read-lock held by this client to a slot other than aReadmark[0].
** Clients with a lock on aReadmark[0] read from the database file
** only - never from the wal file. This means that if a writer holding
** a lock on aReadmark[0] were to commit a transaction but not close the
** read-transaction, subsequent read operations would read directly from
** the database file - ignoring the new pages just appended
** to the wal file. */
rc = walUpgradeReadlock(pWal);
}
return rc;
}
@@ -3657,7 +3880,7 @@ int sqlite3WalFrames(
sqlite3Put4byte(&aWalHdr[4], WAL_MAX_VERSION);
sqlite3Put4byte(&aWalHdr[8], szPage);
sqlite3Put4byte(&aWalHdr[12], pWal->nCkpt);
if( pWal->nCkpt==0 ) sqlite3_randomness(8, pWal->hdr.aSalt);
if( pWal->nCkpt==0 ) sqlite3FastRandomness(&pWal->sPrng, 8, pWal->hdr.aSalt);
memcpy(&aWalHdr[16], pWal->hdr.aSalt, 8);
walChecksumBytes(1, aWalHdr, WAL_HDRSIZE-2*4, 0, aCksum);
sqlite3Put4byte(&aWalHdr[24], aCksum[0]);
@@ -3734,6 +3957,7 @@ int sqlite3WalFrames(
p->flags |= PGHDR_WAL_APPEND;
}
/* Recalculate checksums within the wal file if required. */
if( isCommit && pWal->iReCksum ){
rc = walRewriteChecksums(pWal, iFrame);
@@ -3917,7 +4141,6 @@ int sqlite3WalCheckpoint(
/* Copy data from the log to the database file. */
if( rc==SQLITE_OK ){
if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){
rc = SQLITE_CORRUPT_BKPT;
}else{
@@ -4150,4 +4373,16 @@ sqlite3_file *sqlite3WalFile(Wal *pWal){
return pWal->pWalFd;
}
/*
** Return the values required by sqlite3_wal_info().
*/
int sqlite3WalInfo(Wal *pWal, u32 *pnPrior, u32 *pnFrame){
int rc = SQLITE_OK;
if( pWal ){
*pnFrame = pWal->hdr.mxFrame;
*pnPrior = pWal->nPriorFrame;
}
return rc;
}
#endif /* #ifndef SQLITE_OMIT_WAL */
+14 -2
View File
@@ -34,7 +34,7 @@
# define sqlite3WalDbsize(y) 0
# define sqlite3WalBeginWriteTransaction(y) 0
# define sqlite3WalEndWriteTransaction(x) 0
# define sqlite3WalUndo(x,y,z) 0
# define sqlite3WalUndo(w,x,y,z) 0
# define sqlite3WalSavepoint(y,z)
# define sqlite3WalSavepointUndo(y,z) 0
# define sqlite3WalFrames(u,v,w,x,y,z) 0
@@ -83,7 +83,7 @@ int sqlite3WalBeginWriteTransaction(Wal *pWal);
int sqlite3WalEndWriteTransaction(Wal *pWal);
/* Undo any frames written (but not committed) to the log */
int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx);
int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx, int);
/* Return an integer that records the current (uncommitted) write
** position in the WAL */
@@ -136,6 +136,15 @@ int sqlite3WalSnapshotCheck(Wal *pWal, sqlite3_snapshot *pSnapshot);
void sqlite3WalSnapshotUnlock(Wal *pWal);
#endif
#ifndef SQLITE_OMIT_CONCURRENT
/* Tell the wal layer that we want to commit a concurrent transaction */
int sqlite3WalLockForCommit(Wal *pWal, PgHdr *pPg, Bitvec *pRead, Pgno*);
/* Upgrade the state of the client to take into account changes written
** by other connections */
int sqlite3WalUpgradeSnapshot(Wal *pWal);
#endif /* SQLITE_OMIT_CONCURRENT */
#ifdef SQLITE_ENABLE_ZIPVFS
/* If the WAL file is not empty, return the number of bytes of content
** stored in each frame (i.e. the db page-size when the WAL was created).
@@ -151,5 +160,8 @@ int sqlite3WalWriteLock(Wal *pWal, int bLock);
void sqlite3WalDb(Wal *pWal, sqlite3 *db);
#endif
/* sqlite3_wal_info() data */
int sqlite3WalInfo(Wal *pWal, u32 *pnPrior, u32 *pnFrame);
#endif /* ifndef SQLITE_OMIT_WAL */
#endif /* SQLITE_WAL_H */
+5
View File
@@ -19,6 +19,11 @@ if {$::longdouble_size<=8} {
finish_test
return
}
if {![info exists tcl_platform(machine)]
|| $::tcl_platform(machine)!="x86_64"} {
finish_test
return
}
expr srand(1)
for {set i 1} {$i<20000} {incr i} {
+556
View File
@@ -0,0 +1,556 @@
/*
** 2016-05-07
**
** 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.
**
*************************************************************************
*/
#include <sqlite3.h>
#include <stdlib.h>
#include <stddef.h>
#include "tt3_core.c"
#ifdef USE_OSINST
# include "../src/test_osinst.c"
#else
# define vfslog_time() 0
#endif
typedef struct Config Config;
typedef struct ThreadCtx ThreadCtx;
#define THREAD_TIME_INSERT 0
#define THREAD_TIME_COMMIT 1
#define THREAD_TIME_ROLLBACK 2
#define THREAD_TIME_WRITER 3
#define THREAD_TIME_CKPT 4
struct ThreadCtx {
Config *pConfig;
Sqlite *pDb;
Error *pErr;
sqlite3_int64 aTime[5];
};
struct Config {
int nIPT; /* --inserts-per-transaction */
int nThread; /* --threads */
int nSecond; /* --seconds */
int bMutex; /* --mutex */
int nAutoCkpt; /* --autockpt */
int bRm; /* --rm */
int bClearCache; /* --clear-cache */
int nMmap; /* mmap limit in MB */
char *zFile;
int bOsinst; /* True to use osinst */
ThreadCtx *aCtx; /* Array of size nThread */
pthread_cond_t cond;
pthread_mutex_t mutex;
int nCondWait; /* Number of threads waiting on hCond */
sqlite3_vfs *pVfs;
};
typedef struct VfsWrapperFd VfsWrapperFd;
struct VfsWrapperFd {
sqlite3_file base; /* Base class */
int bWriter; /* True if holding shm WRITER lock */
int iTid;
Config *pConfig;
sqlite3_file *pFd; /* Underlying file descriptor */
};
/* Methods of the wrapper VFS */
static int vfsWrapOpen(sqlite3_vfs*, const char*, sqlite3_file*, int, int*);
static int vfsWrapDelete(sqlite3_vfs*, const char*, int);
static int vfsWrapAccess(sqlite3_vfs*, const char*, int, int*);
static int vfsWrapFullPathname(sqlite3_vfs*, const char *, int, char*);
static void *vfsWrapDlOpen(sqlite3_vfs*, const char*);
static void vfsWrapDlError(sqlite3_vfs*, int, char*);
static void (*vfsWrapDlSym(sqlite3_vfs*,void*, const char*))(void);
static void vfsWrapDlClose(sqlite3_vfs*, void*);
static int vfsWrapRandomness(sqlite3_vfs*, int, char*);
static int vfsWrapSleep(sqlite3_vfs*, int);
static int vfsWrapCurrentTime(sqlite3_vfs*, double*);
static int vfsWrapGetLastError(sqlite3_vfs*, int, char*);
static int vfsWrapCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
static int vfsWrapSetSystemCall(sqlite3_vfs*, const char*, sqlite3_syscall_ptr);
static sqlite3_syscall_ptr vfsWrapGetSystemCall(sqlite3_vfs*, const char*);
static const char *vfsWrapNextSystemCall(sqlite3_vfs*, const char*);
/* Methods of wrapper sqlite3_io_methods object (see vfsWrapOpen()) */
static int vfsWrapClose(sqlite3_file*);
static int vfsWrapRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
static int vfsWrapWrite(sqlite3_file*, const void*, int iAmt, sqlite3_int64);
static int vfsWrapTruncate(sqlite3_file*, sqlite3_int64 size);
static int vfsWrapSync(sqlite3_file*, int flags);
static int vfsWrapFileSize(sqlite3_file*, sqlite3_int64 *pSize);
static int vfsWrapLock(sqlite3_file*, int);
static int vfsWrapUnlock(sqlite3_file*, int);
static int vfsWrapCheckReservedLock(sqlite3_file*, int *pResOut);
static int vfsWrapFileControl(sqlite3_file*, int op, void *pArg);
static int vfsWrapSectorSize(sqlite3_file*);
static int vfsWrapDeviceCharacteristics(sqlite3_file*);
static int vfsWrapShmMap(sqlite3_file*, int iPg, int, int, void volatile**);
static int vfsWrapShmLock(sqlite3_file*, int offset, int n, int flags);
static void vfsWrapShmBarrier(sqlite3_file*);
static int vfsWrapShmUnmap(sqlite3_file*, int deleteFlag);
static int vfsWrapFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **);
static int vfsWrapUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p);
static int vfsWrapOpen(
sqlite3_vfs *pVfs,
const char *zName,
sqlite3_file *pFd,
int flags,
int *fout
){
static sqlite3_io_methods methods = {
3,
vfsWrapClose, vfsWrapRead, vfsWrapWrite,
vfsWrapTruncate, vfsWrapSync, vfsWrapFileSize,
vfsWrapLock, vfsWrapUnlock, vfsWrapCheckReservedLock,
vfsWrapFileControl, vfsWrapSectorSize, vfsWrapDeviceCharacteristics,
vfsWrapShmMap, vfsWrapShmLock, vfsWrapShmBarrier,
vfsWrapShmUnmap, vfsWrapFetch, vfsWrapUnfetch
};
Config *pConfig = (Config*)pVfs->pAppData;
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
int rc;
memset(pWrapper, 0, sizeof(VfsWrapperFd));
if( flags & SQLITE_OPEN_MAIN_DB ){
pWrapper->iTid = (int)sqlite3_uri_int64(zName, "tid", 0);
}
pWrapper->pFd = (sqlite3_file*)&pWrapper[1];
pWrapper->pConfig = pConfig;
rc = pConfig->pVfs->xOpen(pConfig->pVfs, zName, pWrapper->pFd, flags, fout);
if( rc==SQLITE_OK ){
pWrapper->base.pMethods = &methods;
}
return rc;
}
static int vfsWrapDelete(sqlite3_vfs *pVfs, const char *a, int b){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xDelete(pConfig->pVfs, a, b);
}
static int vfsWrapAccess(sqlite3_vfs *pVfs, const char *a, int b, int *c){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xAccess(pConfig->pVfs, a, b, c);
}
static int vfsWrapFullPathname(sqlite3_vfs *pVfs, const char *a, int b, char*c){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xFullPathname(pConfig->pVfs, a, b, c);
}
static void *vfsWrapDlOpen(sqlite3_vfs *pVfs, const char *a){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xDlOpen(pConfig->pVfs, a);
}
static void vfsWrapDlError(sqlite3_vfs *pVfs, int a, char *b){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xDlError(pConfig->pVfs, a, b);
}
static void (*vfsWrapDlSym(sqlite3_vfs *pVfs, void *a, const char *b))(void){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xDlSym(pConfig->pVfs, a, b);
}
static void vfsWrapDlClose(sqlite3_vfs *pVfs, void *a){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xDlClose(pConfig->pVfs, a);
}
static int vfsWrapRandomness(sqlite3_vfs *pVfs, int a, char *b){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xRandomness(pConfig->pVfs, a, b);
}
static int vfsWrapSleep(sqlite3_vfs *pVfs, int a){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xSleep(pConfig->pVfs, a);
}
static int vfsWrapCurrentTime(sqlite3_vfs *pVfs, double *a){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xCurrentTime(pConfig->pVfs, a);
}
static int vfsWrapGetLastError(sqlite3_vfs *pVfs, int a, char *b){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xGetLastError(pConfig->pVfs, a, b);
}
static int vfsWrapCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *a){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xCurrentTimeInt64(pConfig->pVfs, a);
}
static int vfsWrapSetSystemCall(
sqlite3_vfs *pVfs,
const char *a,
sqlite3_syscall_ptr b
){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xSetSystemCall(pConfig->pVfs, a, b);
}
static sqlite3_syscall_ptr vfsWrapGetSystemCall(
sqlite3_vfs *pVfs,
const char *a
){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xGetSystemCall(pConfig->pVfs, a);
}
static const char *vfsWrapNextSystemCall(sqlite3_vfs *pVfs, const char *a){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xNextSystemCall(pConfig->pVfs, a);
}
static int vfsWrapClose(sqlite3_file *pFd){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
pWrapper->pFd->pMethods->xClose(pWrapper->pFd);
pWrapper->pFd = 0;
return SQLITE_OK;
}
static int vfsWrapRead(sqlite3_file *pFd, void *a, int b, sqlite3_int64 c){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xRead(pWrapper->pFd, a, b, c);
}
static int vfsWrapWrite(
sqlite3_file *pFd,
const void *a, int b,
sqlite3_int64 c
){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xWrite(pWrapper->pFd, a, b, c);
}
static int vfsWrapTruncate(sqlite3_file *pFd, sqlite3_int64 a){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xTruncate(pWrapper->pFd, a);
}
static int vfsWrapSync(sqlite3_file *pFd, int a){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xSync(pWrapper->pFd, a);
}
static int vfsWrapFileSize(sqlite3_file *pFd, sqlite3_int64 *a){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xFileSize(pWrapper->pFd, a);
}
static int vfsWrapLock(sqlite3_file *pFd, int a){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xLock(pWrapper->pFd, a);
}
static int vfsWrapUnlock(sqlite3_file *pFd, int a){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xUnlock(pWrapper->pFd, a);
}
static int vfsWrapCheckReservedLock(sqlite3_file *pFd, int *a){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xCheckReservedLock(pWrapper->pFd, a);
}
static int vfsWrapFileControl(sqlite3_file *pFd, int a, void *b){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xFileControl(pWrapper->pFd, a, b);
}
static int vfsWrapSectorSize(sqlite3_file *pFd){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xSectorSize(pWrapper->pFd);
}
static int vfsWrapDeviceCharacteristics(sqlite3_file *pFd){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xDeviceCharacteristics(pWrapper->pFd);
}
static int vfsWrapShmMap(
sqlite3_file *pFd,
int a, int b, int c,
void volatile **d
){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xShmMap(pWrapper->pFd, a, b, c, d);
}
static int vfsWrapShmLock(sqlite3_file *pFd, int offset, int n, int flags){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
Config *pConfig = pWrapper->pConfig;
int bMutex = 0;
int rc;
if( (offset==0 && n==1)
&& (flags & SQLITE_SHM_LOCK) && (flags & SQLITE_SHM_EXCLUSIVE)
){
pthread_mutex_lock(&pConfig->mutex);
pWrapper->bWriter = 1;
bMutex = 1;
if( pWrapper->iTid ){
sqlite3_int64 t = vfslog_time();
pConfig->aCtx[pWrapper->iTid-1].aTime[THREAD_TIME_WRITER] -= t;
}
}
rc = pWrapper->pFd->pMethods->xShmLock(pWrapper->pFd, offset, n, flags);
if( (rc!=SQLITE_OK && bMutex)
|| (offset==0 && (flags & SQLITE_SHM_UNLOCK) && pWrapper->bWriter)
){
assert( pWrapper->bWriter );
pthread_mutex_unlock(&pConfig->mutex);
pWrapper->bWriter = 0;
if( pWrapper->iTid ){
sqlite3_int64 t = vfslog_time();
pConfig->aCtx[pWrapper->iTid-1].aTime[THREAD_TIME_WRITER] += t;
}
}
return rc;
}
static void vfsWrapShmBarrier(sqlite3_file *pFd){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xShmBarrier(pWrapper->pFd);
}
static int vfsWrapShmUnmap(sqlite3_file *pFd, int a){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xShmUnmap(pWrapper->pFd, a);
}
static int vfsWrapFetch(sqlite3_file *pFd, sqlite3_int64 a, int b, void **c){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xFetch(pWrapper->pFd, a, b, c);
}
static int vfsWrapUnfetch(sqlite3_file *pFd, sqlite3_int64 a, void *b){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xUnfetch(pWrapper->pFd, a, b);
}
static void create_vfs(Config *pConfig){
static sqlite3_vfs vfs = {
3, 0, 0, 0, "wrapper", 0,
vfsWrapOpen, vfsWrapDelete, vfsWrapAccess,
vfsWrapFullPathname, vfsWrapDlOpen, vfsWrapDlError,
vfsWrapDlSym, vfsWrapDlClose, vfsWrapRandomness,
vfsWrapSleep, vfsWrapCurrentTime, vfsWrapGetLastError,
vfsWrapCurrentTimeInt64, vfsWrapSetSystemCall, vfsWrapGetSystemCall,
vfsWrapNextSystemCall
};
sqlite3_vfs *pVfs;
pVfs = sqlite3_vfs_find(0);
vfs.mxPathname = pVfs->mxPathname;
vfs.szOsFile = pVfs->szOsFile + sizeof(VfsWrapperFd);
vfs.pAppData = (void*)pConfig;
pConfig->pVfs = pVfs;
sqlite3_vfs_register(&vfs, 1);
}
/*
** Wal hook used by connections in thread_main().
*/
static int thread_wal_hook(
void *pArg, /* Pointer to ThreadCtx object */
sqlite3 *db,
const char *zDb,
int nFrame
){
ThreadCtx *pCtx = (ThreadCtx*)pArg;
Config *pConfig = pCtx->pConfig;
if( pConfig->nAutoCkpt && nFrame>=pConfig->nAutoCkpt ){
pCtx->aTime[THREAD_TIME_CKPT] -= vfslog_time();
pthread_mutex_lock(&pConfig->mutex);
if( pConfig->nCondWait>=0 ){
pConfig->nCondWait++;
if( pConfig->nCondWait==pConfig->nThread ){
execsql(pCtx->pErr, pCtx->pDb, "PRAGMA wal_checkpoint");
pthread_cond_broadcast(&pConfig->cond);
}else{
pthread_cond_wait(&pConfig->cond, &pConfig->mutex);
}
pConfig->nCondWait--;
}
pthread_mutex_unlock(&pConfig->mutex);
pCtx->aTime[THREAD_TIME_CKPT] += vfslog_time();
}
return SQLITE_OK;
}
static char *thread_main(int iTid, void *pArg){
Config *pConfig = (Config*)pArg;
Error err = {0}; /* Error code and message */
Sqlite db = {0}; /* SQLite database connection */
int nAttempt = 0; /* Attempted transactions */
int nCommit = 0; /* Successful transactions */
int j;
ThreadCtx *pCtx = &pConfig->aCtx[iTid-1];
char *zUri = 0;
#ifdef USE_OSINST
char *zOsinstName = 0;
char *zLogName = 0;
if( pConfig->bOsinst ){
zOsinstName = sqlite3_mprintf("osinst%d", iTid);
zLogName = sqlite3_mprintf("bc_test1.log.%d.%d", (int)getpid(), iTid);
zUri = sqlite3_mprintf(
"file:%s?vfs=%s&tid=%d", pConfig->zFile, zOsinstName, iTid
);
sqlite3_vfslog_new(zOsinstName, 0, zLogName);
opendb(&err, &db, zUri, 0);
}else
#endif
{
zUri = sqlite3_mprintf("file:%s?tid=%d", pConfig->zFile, iTid);
opendb(&err, &db, zUri, 0);
}
sqlite3_busy_handler(db.db, 0, 0);
sql_script_printf(&err, &db,
"PRAGMA wal_autocheckpoint = 0;"
"PRAGMA synchronous = 0;"
"PRAGMA mmap_size = %lld;",
(i64)(pConfig->nMmap) * 1024 * 1024
);
pCtx->pConfig = pConfig;
pCtx->pErr = &err;
pCtx->pDb = &db;
sqlite3_wal_hook(db.db, thread_wal_hook, (void*)pCtx);
while( !timetostop(&err) ){
execsql(&err, &db, "BEGIN CONCURRENT");
pCtx->aTime[THREAD_TIME_INSERT] -= vfslog_time();
for(j=0; j<pConfig->nIPT; j++){
execsql(&err, &db,
"INSERT INTO t1 VALUES"
"(randomblob(10), randomblob(20), randomblob(30), randomblob(200))"
);
}
pCtx->aTime[THREAD_TIME_INSERT] += vfslog_time();
pCtx->aTime[THREAD_TIME_COMMIT] -= vfslog_time();
execsql(&err, &db, "COMMIT");
pCtx->aTime[THREAD_TIME_COMMIT] += vfslog_time();
pCtx->aTime[THREAD_TIME_ROLLBACK] -= vfslog_time();
nAttempt++;
if( err.rc==SQLITE_OK ){
nCommit++;
}else{
clear_error(&err, SQLITE_BUSY);
execsql(&err, &db, "ROLLBACK");
}
pCtx->aTime[THREAD_TIME_ROLLBACK] += vfslog_time();
if( pConfig->bClearCache ){
sqlite3_db_release_memory(db.db);
}
}
closedb(&err, &db);
#ifdef USE_OSINST
if( pConfig->bOsinst ){
sqlite3_vfslog_finalize(zOsinstName);
sqlite3_free(zOsinstName);
sqlite3_free(zLogName);
}
#endif
sqlite3_free(zUri);
pthread_mutex_lock(&pConfig->mutex);
pConfig->nCondWait = -1;
pthread_cond_broadcast(&pConfig->cond);
pthread_mutex_unlock(&pConfig->mutex);
return sqlite3_mprintf("commits: %d/%d insert: %dms"
" commit: %dms"
" rollback: %dms"
" writer: %dms"
" checkpoint: %dms",
nCommit, nAttempt,
(int)(pCtx->aTime[THREAD_TIME_INSERT]/1000),
(int)(pCtx->aTime[THREAD_TIME_COMMIT]/1000),
(int)(pCtx->aTime[THREAD_TIME_ROLLBACK]/1000),
(int)(pCtx->aTime[THREAD_TIME_WRITER]/1000),
(int)(pCtx->aTime[THREAD_TIME_CKPT]/1000)
);
}
int main(int argc, const char **argv){
Error err = {0}; /* Error code and message */
Sqlite db = {0}; /* SQLite database connection */
Threadset threads = {0}; /* Test threads */
Config conf = {5, 3, 5};
int i;
CmdlineArg apArg[] = {
{ "-seconds", CMDLINE_INT, offsetof(Config, nSecond) },
{ "-inserts", CMDLINE_INT, offsetof(Config, nIPT) },
{ "-threads", CMDLINE_INT, offsetof(Config, nThread) },
{ "-mutex", CMDLINE_BOOL, offsetof(Config, bMutex) },
{ "-rm", CMDLINE_BOOL, offsetof(Config, bRm) },
{ "-autockpt",CMDLINE_INT, offsetof(Config, nAutoCkpt) },
{ "-mmap", CMDLINE_INT, offsetof(Config, nMmap) },
{ "-clear-cache", CMDLINE_BOOL, offsetof(Config, bClearCache) },
{ "-file", CMDLINE_STRING, offsetof(Config, zFile) },
{ "-osinst", CMDLINE_BOOL, offsetof(Config, bOsinst) },
{ 0, 0, 0 }
};
conf.nAutoCkpt = 1000;
cmdline_process(apArg, argc, argv, (void*)&conf);
if( err.rc==SQLITE_OK ){
char *z = cmdline_construct(apArg, (void*)&conf);
printf("With: %s\n", z);
sqlite3_free(z);
}
if( conf.zFile==0 ){
conf.zFile = "xyz.db";
}
/* Create the special VFS - "wrapper". And the mutex and condition
** variable. */
create_vfs(&conf);
pthread_mutex_init(&conf.mutex, 0);
pthread_cond_init(&conf.cond, 0);
conf.aCtx = sqlite3_malloc(sizeof(ThreadCtx) * conf.nThread);
memset(conf.aCtx, 0, sizeof(ThreadCtx) * conf.nThread);
/* Ensure the schema has been created */
opendb(&err, &db, conf.zFile, conf.bRm);
sql_script(&err, &db,
"PRAGMA journal_mode = wal;"
"CREATE TABLE IF NOT EXISTS t1(a PRIMARY KEY, b, c, d) WITHOUT ROWID;"
"CREATE INDEX IF NOT EXISTS t1b ON t1(b);"
"CREATE INDEX IF NOT EXISTS t1c ON t1(c);"
);
setstoptime(&err, conf.nSecond*1000);
if( conf.nThread==1 ){
char *z = thread_main(1, (void*)&conf);
printf("Thread 0 says: %s\n", (z==0 ? "..." : z));
fflush(stdout);
}else{
for(i=0; i<conf.nThread; i++){
launch_thread(&err, &threads, thread_main, (void*)&conf);
}
join_all_threads(&err, &threads);
}
if( err.rc==SQLITE_OK ){
printf("Database is %dK\n", (int)(filesize(&err, conf.zFile) / 1024));
}
if( err.rc==SQLITE_OK ){
char *zWal = sqlite3_mprintf("%s-wal", conf.zFile);
printf("Wal file is %dK\n", (int)(filesize(&err, zWal) / 1024));
}
closedb(&err, &db);
print_and_free_err(&err);
return 0;
}
+126
View File
@@ -0,0 +1,126 @@
# 2015 Aug 25
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# This file contains fault injection tests designed to test the concurrent
# transactions feature.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
set testprefix concfault
# This test will not work with an in-memory journal, as the database will
# become corrupt if an error is injected into a transaction after it starts
# writing data out to the db file.
ifcapable !concurrent {
finish_test
return
}
do_test 1-pre1 {
execsql {
PRAGMA journal_mode = wal;
CREATE TABLE t1(a PRIMARY KEY, b);
INSERT INTO t1 VALUES(randomblob(1000), randomblob(100));
INSERT INTO t1 SELECT randomblob(1000), randomblob(1000) FROM t1;
INSERT INTO t1 SELECT randomblob(1000), randomblob(1000) FROM t1;
INSERT INTO t1 SELECT randomblob(1000), randomblob(1000) FROM t1;
INSERT INTO t1 SELECT randomblob(1000), randomblob(1000) FROM t1;
DELETE FROM t1 WHERE rowid%2;
}
faultsim_save_and_close
} {}
do_faultsim_test 1.1 -prep {
faultsim_restore_and_reopen
} -body {
execsql {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(randomblob(1000), randomblob(100));
COMMIT;
}
} -test {
faultsim_test_result {0 {}}
catchsql { ROLLBACK }
faultsim_integrity_check
}
do_faultsim_test 1.2 -prep {
faultsim_restore_and_reopen
} -body {
execsql {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(randomblob(1000), randomblob(100));
ROLLBACK;
}
} -test {
faultsim_test_result {0 {}}
catchsql { ROLLBACK }
faultsim_integrity_check
}
do_faultsim_test 1.3 -prep {
faultsim_restore_and_reopen
} -body {
execsql {
BEGIN CONCURRENT;
DELETE FROM t1;
COMMIT;
}
} -test {
faultsim_test_result {0 {}}
catchsql { ROLLBACK }
faultsim_integrity_check
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 2.0 {
PRAGMA auto_vacuum = 0;
PRAGMA journal_mode = wal;
CREATE TABLE t1(a PRIMARY KEY, b);
CREATE TABLE t2(a PRIMARY KEY, b);
INSERT INTO t1 VALUES(randomblob(1000), randomblob(100));
INSERT INTO t1 SELECT randomblob(1000), randomblob(1000) FROM t1;
INSERT INTO t1 SELECT randomblob(1000), randomblob(1000) FROM t1;
INSERT INTO t1 SELECT randomblob(1000), randomblob(1000) FROM t1;
INSERT INTO t1 SELECT randomblob(1000), randomblob(1000) FROM t1;
DELETE FROM t1 WHERE rowid%2;
} {wal}
faultsim_save_and_close
do_faultsim_test 1 -prep {
faultsim_restore_and_reopen
execsql {
SELECT * FROM t1;
BEGIN CONCURRENT;
INSERT INTO t2 VALUES(1, 2);
}
sqlite3 db2 test.db
execsql {
PRAGMA journal_size_limit = 10000;
INSERT INTO t1 VALUES(randomblob(1000), randomblob(1000));
} db2
db2 close
} -body {
execsql { COMMIT }
} -test {
faultsim_test_result {0 {}}
catchsql { ROLLBACK }
set res [catchsql { SELECT count(*) FROM t1 }]
if {$res!="0 9"} { error "expected {0 9} got {$res}" }
faultsim_integrity_check
}
finish_test
+687
View File
@@ -0,0 +1,687 @@
# 2015 July 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.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
set ::testprefix concurrent
ifcapable !concurrent {
finish_test
return
}
do_execsql_test 1.0 {
PRAGMA journal_mode = wal;
} {wal}
do_execsql_test 1.1 {
CREATE TABLE t1(k INTEGER PRIMARY KEY, v);
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(1, 'abcd');
COMMIT;
}
do_execsql_test 1.2 {
SELECT * FROM t1;
} {1 abcd}
do_execsql_test 1.3 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(2, 'efgh');
ROLLBACK;
}
do_execsql_test 1.4 {
SELECT * FROM t1;
} {1 abcd}
#-------------------------------------------------------------------------
# CONCURRENT transactions cannot do cache spills.
#
foreach {tn trans spill} {
1 {BEGIN CONCURRENT} 0
2 {BEGIN} 1
} {
do_test 1.5.$tn {
sqlite3 db2 test.db
set walsz [file size test.db-wal]
execsql { PRAGMA cache_size = 10 } db2
execsql $trans db2
execsql {
WITH cnt(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM cnt WHERE i<50)
INSERT INTO t1(v) SELECT randomblob(900) FROM cnt;
} db2
expr {[file size test.db-wal]==$walsz}
} [expr !$spill]
execsql ROLLBACK db2
db2 close
}
#-------------------------------------------------------------------------
# CONCURRENT transactions man not be committed while there are active
# readers.
do_execsql_test 1.6.setup {
DROP TABLE t1;
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 2);
INSERT INTO t1 VALUES(3, 4);
INSERT INTO t1 VALUES(5, 6);
}
foreach {tn trans commit_ok} {
1 {BEGIN CONCURRENT} 0
2 {BEGIN} 1
} {
do_test 1.6.$tn.1 {
set stmt [sqlite3_prepare db "SELECT * FROM t1" -1 dummy]
sqlite3_step $stmt
} SQLITE_ROW
do_test 1.6.$tn.2 {
execsql $trans
execsql { INSERT INTO t1 VALUES(7, 8) }
} {}
if { $commit_ok } {
do_test 1.6.$tn.3 { catchsql COMMIT } {0 {}}
} else {
do_test 1.6.$tn.4 { catchsql COMMIT } {/1 {cannot commit transaction .*}/}
}
sqlite3_finalize $stmt
catchsql ROLLBACK
}
#-------------------------------------------------------------------------
# CONCURRENT transactions may not modify the db schema.
#
sqlite3 db2 test.db
foreach {tn sql} {
1 { CREATE TABLE xx(a, b) }
2 { DROP TABLE t1 }
3 { CREATE INDEX i1 ON t1(a) }
4 { CREATE VIEW v1 AS SELECT * FROM t1 }
} {
do_catchsql_test 1.7.0.$tn.1 "
BEGIN CONCURRENT;
$sql
" {0 {}}
db2 eval {INSERT INTO t1 DEFAULT VALUES}
do_catchsql_test 1.7.0.$tn.2 {
COMMIT
} {1 {database is locked}}
do_execsql_test 1.7.0.$tn.2 ROLLBACK
do_execsql_test 1.7.0.$tn.3 {
SELECT sql FROM sqlite_master;
SELECT sql FROM sqlite_temp_master;
} {{CREATE TABLE t1(a, b)}}
#do_execsql_test 1.7.0.$tn.3 COMMIT
}
# Except the temp db schema.
foreach {tn sql} {
1 { CREATE TEMP TABLE xx(a, b) }
2 { DROP TABLE xx }
3 { CREATE TEMP TABLE yy(a, b) }
4 { CREATE VIEW temp.v1 AS SELECT * FROM t1 }
5 { CREATE INDEX yyi1 ON yy(a); }
6 { CREATE TABLE temp.zz(a, b) }
} {
do_catchsql_test 1.7.1.$tn.1 "
BEGIN CONCURRENT;
$sql
" {0 {}}
do_execsql_test 1.7.1.$tn.2 COMMIT
}
do_execsql_test 1.7.1.x {
SELECT sql FROM sqlite_master;
SELECT sql FROM sqlite_temp_master;
} {
{CREATE TABLE t1(a, b)}
{CREATE TABLE yy(a, b)}
{CREATE VIEW v1 AS SELECT * FROM t1}
{CREATE INDEX yyi1 ON yy(a)}
{CREATE TABLE zz(a, b)}
}
#-------------------------------------------------------------------------
# If an auto-vacuum database is written within an CONCURRENT transaction, it
# is handled in the same way as for a non-CONCURRENT transaction.
#
reset_db
do_execsql_test 1.8.1 {
PRAGMA auto_vacuum = 1;
PRAGMA journal_mode = wal;
CREATE TABLE t1(x, y);
INSERT INTO t1 VALUES('x', 'y');
} {wal}
do_execsql_test 1.8.2 {
BEGIN CONCURRENT;
SELECT * FROM t1;
COMMIT;
} {x y}
do_catchsql_test 1.8.3 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES('a', 'b');
} {0 {}}
do_test 1.8.4 {
sqlite3 db2 test.db
catchsql {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES('c', 'd');
} db2
} {1 {database is locked}}
do_test 1.8.5 {
db eval COMMIT
db2 eval COMMIT
} {}
db close
db2 close
do_multiclient_test tn {
#-----------------------------------------------------------------------
# 1. Start an CONCURRENT transaction using [db1].
#
# 2. Start and then rollback a regular transaction using [db2]. This
# can be done as the ongoing [db1] transaction is CONCURRENT.
#
# 3. The [db1] transaction can now be committed, as [db2] has relinquished
# the write lock.
#
do_test 2.$tn.1.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(k INTEGER PRIMARY KEY, v);
INSERT INTO t1 VALUES(1, 'one');
}
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(2, 'two');
}
code1 { sqlite3_get_autocommit db }
} 0
do_test 2.$tn.1.2 {
sql2 {
BEGIN;
INSERT INTO t1 VALUES(3, 'three');
ROLLBACK;
}
} {}
do_test 2.$tn.1.3 {
sql1 COMMIT
sql2 { SELECT * FROM t1 }
} {1 one 2 two}
#-----------------------------------------------------------------------
# 1. Start an CONCURRENT transaction using [db1].
#
# 2. Commit a transaction using [db2].
#
# 3. Try to commit with [db1]. Check that SQLITE_BUSY_SNAPSHOT is returned,
# and the transaction is not rolled back.
#
do_test 2.$tn.2.1 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(-1, 'hello world');
}
} {}
do_test 2.$tn.2.2 {
sql2 {
INSERT INTO t1 VALUES(3, 'three');
}
} {}
do_test 2.$tn.2.3.1 {
set rc [catch { sql1 COMMIT } msg]
list $rc $msg
} {1 {database is locked}}
do_test 2.$tn.2.3.2 {
code1 { list [sqlite3_extended_errcode db] [sqlite3_get_autocommit db] }
} {SQLITE_BUSY_SNAPSHOT 0}
do_test 2.$tn.2.3.3 {
sql1 {
SELECT * FROM t1;
ROLLBACK;
}
} {-1 {hello world} 1 one 2 two}
#-----------------------------------------------------------------------
# 1. Start an CONCURRENT transaction using [db1].
#
# 2. Open a transaction using [db2].
#
# 3. Try to commit with [db1]. Check that SQLITE_BUSY is returned,
# and the transaction is not rolled back.
#
# 4. Have [db2] roll its transaction back. Then check that [db1] can
# commit.
#
do_test 2.$tn.3.1 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(4, 'four');
}
} {}
do_test 2.$tn.3.2 {
sql2 {
BEGIN;
INSERT INTO t1 VALUES(-1, 'xyz');
}
} {}
do_test 2.$tn.3.3.1 {
set rc [catch { sql1 COMMIT } msg]
list $rc $msg
} {1 {database is locked}}
do_test 2.$tn.3.3.2 {
code1 { list [sqlite3_extended_errcode db] [sqlite3_get_autocommit db] }
} {SQLITE_BUSY 0}
do_test 2.$tn.3.3.3 {
sql1 { SELECT * FROM t1; }
} {1 one 2 two 3 three 4 four}
do_test 2.$tn.3.4 {
sql2 ROLLBACK
sql1 COMMIT
sql1 { SELECT * FROM t1; }
} {1 one 2 two 3 three 4 four}
#-----------------------------------------------------------------------
# 1. Create a second table - t2.
#
# 2. Write to t1 with [db] and t2 with [db2].
#
# 3. See if it worked.
#
do_test 2.$tn.4.1 {
sql1 { CREATE TABLE t2(a, b) }
} {}
do_test 2.$tn.4.2 {
sql2 {
BEGIN CONCURRENT;
INSERT INTO t2 VALUES('i', 'n');
}
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(5, 'five');
COMMIT;
}
sql2 COMMIT
} {}
do_test 2.$tn.4.3.1 {
sql2 {SELECT * FROM t1}
} {1 one 2 two 3 three 4 four 5 five}
do_test 2.$tn.4.3.2 {
sql1 {SELECT * FROM t1}
} {1 one 2 two 3 three 4 four 5 five}
do_test 2.$tn.4.3.3 { sql2 {SELECT * FROM t2} } {i n}
do_test 2.$tn.4.3.4 { sql1 {SELECT * FROM t2} } {i n}
#-----------------------------------------------------------------------
# The "schema cookie" issue.
#
# 1. Begin and CONCURRENT write to "t1" using [db]
#
# 2. Create an index on t1 using [db2].
#
# 3. Attempt to commit the CONCURRENT write. This is an SQLITE_BUSY_SNAPSHOT,
# even though there is no page collision.
#
do_test 2.$tn.5.1 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(6, 'six');
}
} {}
do_test 2.$tn.5.2 {
sql2 { CREATE INDEX i1 ON t1(v); }
} {}
do_test 2.$tn.5.3 {
list [catch { sql1 { COMMIT } } msg] $msg [sqlite3_errcode db]
} {1 {database is locked} SQLITE_BUSY_SNAPSHOT}
do_test 2.$tn.5.4 {
sql2 { PRAGMA integrity_check }
} {ok}
catch { sql1 ROLLBACK }
#-----------------------------------------------------------------------
#
# 1. Begin an CONCURRENT write to "t1" using [db]
#
# 2. Lots of inserts into t2. Enough to grow the db file and modify page 1.
#
# 3. Check that the CONCURRENT transaction can not be committed.
#
do_test 2.$tn.6.1 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(6, 'six');
}
} {}
do_test 2.$tn.6.2 {
sql2 {
WITH src(a,b) AS (
VALUES(1,1) UNION ALL SELECT a+1,b+1 FROM src WHERE a<10000
) INSERT INTO t2 SELECT * FROM src;
}
} {}
do_test 2.$tn.6.3 {
sql1 { SELECT count(*) FROM t2 }
list [catch { sql1 { COMMIT } } msg] $msg [sqlite3_errcode db]
} {1 {database is locked} SQLITE_BUSY_SNAPSHOT}
sql1 ROLLBACK
do_test 2.$tn.6.4 {
sql1 {
SELECT count(*) FROM t1;
SELECT count(*) FROM t2;
}
} {5 10001}
#-----------------------------------------------------------------------
#
# 1. Begin an big CONCURRENT write to "t1" using [db] - large enough to
# grow the db file.
#
# 2. Lots of inserts into t2. Also enough to grow the db file.
#
# 3. Check that the CONCURRENT transaction cannot be committed (due to a clash
# on page 1 - the db size field).
#
do_test 2.$tn.7.1 {
sql1 {
BEGIN CONCURRENT;
WITH src(a,b) AS (
VALUES(10000,10000) UNION ALL SELECT a+1,b+1 FROM src WHERE a<20000
) INSERT INTO t1 SELECT * FROM src;
}
} {}
do_test 2.$tn.7.2 {
sql2 {
WITH src(a,b) AS (
VALUES(1,1) UNION ALL SELECT a+1,b+1 FROM src WHERE a<10000
) INSERT INTO t2 SELECT * FROM src;
}
} {}
do_test 2.$tn.7.3 {
list [catch { sql1 { COMMIT } } msg] $msg [sqlite3_errcode db]
} {0 {} SQLITE_OK}
do_test 2.$tn.7.4 { sql3 { PRAGMA integrity_check } } ok
}
#-------------------------------------------------------------------------
# Concurrent transactions may not modify the user_version or application_id.
#
reset_db
do_execsql_test 3.0 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x, y);
INSERT INTO t1 VALUES('a', 'b');
PRAGMA user_version = 10;
} {wal}
do_execsql_test 3.1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES('c', 'd');
SELECT * FROM t1;
} {a b c d}
do_catchsql_test 3.2 {
PRAGMA user_version = 11;
} {1 {cannot modify user_version within CONCURRENT transaction}}
do_execsql_test 3.3 {
PRAGMA user_version;
SELECT * FROM t1;
} {10 a b c d}
do_catchsql_test 3.4 {
PRAGMA application_id = 11;
} {1 {cannot modify application_id within CONCURRENT transaction}}
do_execsql_test 3.5 {
COMMIT;
PRAGMA user_version;
PRAGMA application_id;
SELECT * FROM t1;
} {10 0 a b c d}
#-------------------------------------------------------------------------
# However, another transaction modifying the user_version or application_id
# should not cause a conflict. And committing a concurrent transaction does not
# clobber the modification - even if the concurrent transaction allocates or
# frees database pages.
#
do_multiclient_test tn {
do_test 4.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE ttt(y UNIQUE, z UNIQUE);
PRAGMA user_version = 14;
BEGIN CONCURRENT;
INSERT INTO ttt VALUES('y', 'z');
}
} {wal}
do_test 4.$tn.2 {
sql2 { PRAGMA user_version = 16 }
sql1 COMMIT
sql1 { PRAGMA user_version }
} {16}
do_test 4.$tn.3 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO ttt VALUES(randomblob(10000), randomblob(4));
PRAGMA user_version;
}
} {16}
do_test 4.$tn.4 {
sql2 { PRAGMA user_version = 1234 }
sql1 {
PRAGMA user_version;
COMMIT;
PRAGMA user_version;
PRAGMA integrity_check;
}
} {16 1234 ok}
do_test 4.$tn.5 {
sql1 {
BEGIN CONCURRENT;
DELETE FROM ttt;
PRAGMA user_version;
}
} {1234}
do_test 4.$tn.4 {
sql2 { PRAGMA user_version = 5678 }
sql1 {
PRAGMA user_version;
COMMIT;
PRAGMA user_version;
PRAGMA integrity_check;
}
} {1234 5678 ok}
}
do_multiclient_test tn {
do_test 5.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE tt(a INTEGER PRIMARY KEY, b);
CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
INSERT INTO tt VALUES(1, randomblob(400));
BEGIN CONCURRENT;
}
} {wal}
do_test 5.$tn.2 {
sql1 { UPDATE t2 SET b=5 WHERE a=3 }
sql2 { INSERT INTO tt VALUES(2, randomblob(6000)) }
} {}
do_test 5.$tn.3 {
sql1 { COMMIT }
} {}
}
do_multiclient_test tn {
do_test 6.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
INSERT INTO t1 VALUES(1, 'one');
INSERT INTO t2 VALUES(2, 'two');
}
} {wal}
do_test 6.$tn.2 {
sql2 {
BEGIN CONCURRENT;
SELECT * FROM t2;
INSERT INTO t1 VALUES(3, 'three');
}
} {2 two}
do_test 6.$tn.3 {
sql1 {
INSERT INTO t2 VALUES(3, 'three');
}
} {}
do_test 6.$tn.2 {
list [catch { sql2 { COMMIT } } msg] $msg
} {1 {database is locked}}
}
do_multiclient_test tn {
do_test 7.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<100)
INSERT INTO t1 SELECT NULL, randomblob(400) FROM s;
CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<50000)
INSERT INTO t2 SELECT NULL, randomblob(400) FROM s;
CREATE TABLE t3(a INTEGER PRIMARY KEY, b);
WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<100)
INSERT INTO t3 SELECT NULL, randomblob(400) FROM s;
CREATE TABLE t4(a INTEGER PRIMARY KEY, b);
}
set {} {}
} {}
do_test 7.$tn.2 {
sql2 {
BEGIN CONCURRENT;
SELECT * FROM t1;
INSERT INTO t4 VALUES(1, 2);
}
set {} {}
} {}
do_test 7.$tn.3 {
sql3 {
BEGIN CONCURRENT;
SELECT * FROM t3;
INSERT INTO t4 VALUES(1, 2);
}
set {} {}
} {}
do_test 7.$tn.4 {
sql1 {
UPDATE t1 SET b=randomblob(400);
UPDATE t2 SET b=randomblob(400);
UPDATE t3 SET b=randomblob(400);
}
} {}
do_test 7.$tn.5 {
csql2 { COMMIT }
} {1 {database is locked}}
do_test 7.$tn.6 {
csql3 { COMMIT }
} {1 {database is locked}}
csql2 ROLLBACK
csql3 ROLLBACK
# The following test works with $tn==1 (sql2 and sql3 use separate
# processes), but is quite slow. So only run it with $tn==2 (all
# connections in the same process).
#
if {$tn==2} {
do_test 7.$tn.7 {
for {set i 1} {$i < 10000} {incr i} {
sql3 {
PRAGMA wal_checkpoint;
BEGIN CONCURRENT;
SELECT * FROM t3;
INSERT INTO t4 VALUES(1, 2);
}
sql1 {
UPDATE t2 SET b = randomblob(400) WHERE rowid <= $i;
UPDATE t3 SET b = randomblob(400) WHERE rowid = 1;
}
if {[csql3 COMMIT]!={1 {database is locked}}} {
error "Failed at i=$i"
}
csql3 ROLLBACK
}
} {}
}
}
finish_test
+626
View File
@@ -0,0 +1,626 @@
# 2015 July 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.
#
#***********************************************************************
#
# Miscellaneous tests for transactions started with BEGIN CONCURRENT.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/wal_common.tcl
set ::testprefix concurrent2
ifcapable !concurrent {
finish_test
return
}
do_multiclient_test tn {
do_test 1.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x);
CREATE TABLE t2(y);
}
} {wal}
do_test 1.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}
# Test that an CONCURRENT transaction that allocates/frees no pages does
# not conflict with a transaction that does allocate pages.
do_test 1.$tn.2 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(4);
}
sql2 {
INSERT INTO t2 VALUES(randomblob(1500));
}
sql1 {
COMMIT;
}
} {}
do_test 1.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}
# But that an CONCURRENT transaction does conflict with a transaction
# that modifies the db schema.
do_test 1.$tn.3 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(5);
}
sql2 {
CREATE TABLE t3(z);
}
list [catch { sql1 COMMIT } msg] $msg
} {1 {database is locked}}
do_test 1.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}
# Test that an CONCURRENT transaction that allocates at least one page
# does not conflict with a transaction that allocates no pages.
do_test 1.$tn.4 {
sql1 {
ROLLBACK;
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(randomblob(1500));
}
sql2 {
INSERT INTO t2 VALUES(8);
}
sql1 {
COMMIT;
}
} {}
do_test 1.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}
}
do_multiclient_test tn {
do_test 2.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x UNIQUE);
CREATE TABLE t2(y UNIQUE);
}
} {wal}
do_test 2.$tn.2 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(randomblob(1500));
}
sql2 {
INSERT INTO t2 VALUES(randomblob(1500));
}
sql1 COMMIT
} {}
do_test 2.$tn.3 { sql3 { PRAGMA integrity_check } } {ok}
do_test 2.$tn.4 {
sql1 {
BEGIN CONCURRENT;
DELETE FROM t1;
}
sql2 {
DELETE FROM t2;
}
sql1 COMMIT
} {}
do_test 2.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}
do_test 2.$tn.6 {
sql1 {
INSERT INTO t1 VALUES(randomblob(1500));
INSERT INTO t1 VALUES(randomblob(1500));
INSERT INTO t2 VALUES(randomblob(1500));
DELETE FROM t1 WHERE rowid=1;
}
sql1 {
BEGIN CONCURRENT;
DELETE FROM t1 WHERE rowid=2;
}
sql2 {
DELETE FROM t2;
}
sql1 COMMIT
} {}
do_test 2.$tn.7 { sql3 { PRAGMA integrity_check } } {ok}
}
#-------------------------------------------------------------------------
# When an CONCURRENT transaction is opened on a database, the nFree and
# iTrunk header fields of the cached version of page 1 are both set
# to 0. This allows an CONCURRENT transaction to use its own private
# free-page-list, which is merged with the main database free-list when
# the transaction is committed.
#
# The following tests check that nFree/iTrunk are correctly restored if
# an CONCURRENT transaction is rolled back, and that savepoint rollbacks
# that occur within CONCURRENT transactions do not incorrectly restore
# these fields to their on-disk values.
#
reset_db
do_execsql_test 3.0 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x, y);
INSERT INTO t1 VALUES(randomblob(1500), randomblob(1500));
DELETE FROM t1;
} {wal}
do_execsql_test 3.1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(1, 2);
ROLLBACK;
}
do_execsql_test 3.2 { PRAGMA integrity_check } {ok}
do_execsql_test 3.3 { PRAGMA freelist_count } {2}
do_execsql_test 3.4.1 {
BEGIN CONCURRENT;
PRAGMA freelist_count;
} {2}
do_execsql_test 3.4.2 {
SAVEPOINT xyz;
INSERT INTO t1 VALUES(randomblob(1500), NULL);
PRAGMA freelist_count;
} {0}
do_execsql_test 3.4.3 {
ROLLBACK TO xyz;
} {}
do_execsql_test 3.4.4 { PRAGMA freelist_count } {0}
do_execsql_test 3.4.5 { COMMIT; PRAGMA freelist_count } {2}
do_execsql_test 3.4.6 { PRAGMA integrity_check } {ok}
do_execsql_test 3.5.1 {
BEGIN CONCURRENT;
UPDATE t1 SET x=randomblob(10) WHERE y=555;
PRAGMA freelist_count;
} {0}
do_execsql_test 3.5.2 {
ROLLBACK;
PRAGMA freelist_count;
} {2}
do_execsql_test 3.5.3 { PRAGMA integrity_check } {ok}
#-------------------------------------------------------------------------
# Test that nothing goes wrong if an CONCURRENT transaction allocates a
# page at the end of the file, frees it within the same transaction, and
# then has to move the same page to avoid a conflict on COMMIT.
#
do_multiclient_test tn {
do_test 4.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x);
CREATE TABLE t2(x);
}
} {wal}
do_test 4.$tn.2 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(randomblob(1500));
INSERT INTO t1 VALUES(randomblob(1500));
DELETE FROM t1 WHERE rowid = 1;
}
sql2 {
INSERT INTO t2 VALUES(randomblob(1500));
INSERT INTO t2 VALUES(randomblob(1500));
INSERT INTO t2 VALUES(randomblob(1500));
INSERT INTO t2 VALUES(randomblob(1500));
DELETE FROM t2 WHERE rowid IN (1, 2);
}
sql1 COMMIT
} {}
}
#-------------------------------------------------------------------------
#
do_multiclient_test tn {
do_test 5.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x);
CREATE TABLE t2(x);
INSERT INTO t1 VALUES(randomblob(1500));
PRAGMA page_count;
}
} {wal 4}
do_test 5.$tn.2 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t2 VALUES(randomblob(1500));
PRAGMA page_count;
}
} {5}
do_test 5.$tn.3 {
sql2 {
DELETE FROM t1;
PRAGMA freelist_count;
PRAGMA page_count;
}
} {1 4}
do_test 5.$tn.4 { sql1 COMMIT } {}
do_test 5.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}
}
#-------------------------------------------------------------------------
#
do_multiclient_test tn {
do_test 6.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x);
INSERT INTO t1 VALUES(randomblob(1500));
PRAGMA wal_checkpoint;
}
} {wal 0 5 5}
do_test 6.$tn.2 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(randomblob(1500));
INSERT INTO t1 VALUES(randomblob(1500));
}
} {}
do_test 6.$tn.3 {
sql2 {
BEGIN;
INSERT INTO t1 VALUES(randomblob(1500));
INSERT INTO t1 VALUES(randomblob(1500));
COMMIT;
}
} {}
do_test 6.$tn.4 {
list [catch { sql1 COMMIT } msg] $msg
} {1 {database is locked}}
do_test 6.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}
do_test 6.$tn.5 { sql3 { SELECT count(*) from t1 } } {3}
}
#-------------------------------------------------------------------------
# Test that if a corrupt wal-index-header is encountered when attempting
# to commit a CONCURRENT transaction, the transaction is not committed
# (or rolled back) and that SQLITE_BUSY_SNAPSHOT is returned to the user.
#
catch { db close }
forcedelete test.db
testvfs tvfs
sqlite3 db test.db -vfs tvfs
do_execsql_test 7.1 {
PRAGMA journal_mode = wal;
BEGIN;
CREATE TABLE t1(a, b, PRIMARY KEY(a));
INSERT INTO t1 VALUES(1, 2);
INSERT INTO t1 VALUES(3, 4);
COMMIT;
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(5, 6);
INSERT INTO t1 VALUES(7, 8);
SELECT * FROM t1;
} {wal 1 2 3 4 5 6 7 8}
# Corrupt the wal-index header
incr_tvfs_hdr test.db 11 1
do_catchsql_test 7.2.1 { COMMIT } {1 {database is locked}}
do_test 7.2.2 { sqlite3_extended_errcode db } SQLITE_BUSY_SNAPSHOT
do_execsql_test 7.3.1 {
SELECT * FROM t1;
ROLLBACK;
} {1 2 3 4 5 6 7 8}
do_execsql_test 7.3.2 {
SELECT * FROM t1;
} {1 2 3 4}
#-------------------------------------------------------------------------
# Test that "PRAGMA integrity_check" works within a concurrent
# transaction. Within a concurrent transaction, "PRAGMA integrity_check"
# is unable to detect unused database pages, but can detect other types
# of corruption.
#
reset_db
do_test 8.1 {
execsql {
PRAGMA journal_mode = wal;
CREATE TABLE kv(k INTEGER PRIMARY KEY, v UNIQUE);
INSERT INTO kv VALUES(NULL, randomblob(750));
INSERT INTO kv SELECT NULL, randomblob(750) FROM kv;
INSERT INTO kv SELECT NULL, randomblob(750) FROM kv;
INSERT INTO kv SELECT NULL, randomblob(750) FROM kv;
INSERT INTO kv SELECT NULL, randomblob(750) FROM kv;
INSERT INTO kv SELECT NULL, randomblob(750) FROM kv;
DELETE FROM kv WHERE rowid%2;
}
set v [db one {PRAGMA freelist_count}]
expr $v==33 || $v==34
} {1}
do_execsql_test 8.2 { PRAGMA integrity_check } ok
do_execsql_test 8.3 {
BEGIN CONCURRENT;
PRAGMA integrity_check;
} {ok}
do_execsql_test 8.4 {
INSERT INTO kv VALUES(1100, 1100);
PRAGMA integrity_check;
} {ok}
do_execsql_test 8.5 {
COMMIT;
PRAGMA integrity_check;
} {ok}
#-------------------------------------------------------------------------
# Test that concurrent transactions do not allow foreign-key constraints
# to be bypassed.
#
do_multiclient_test tn {
do_test 9.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE pp(i INTEGER PRIMARY KEY, j);
CREATE TABLE cc(a, b REFERENCES pp);
WITH seq(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM seq WHERE i<100)
INSERT INTO pp SELECT i, randomblob(1000) FROM seq;
PRAGMA foreign_keys = 1;
}
} {wal}
do_test 9.$tn.2.1 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO cc VALUES(42, 42);
}
} {}
do_test 9.$tn.2.2 {
sql2 { DELETE FROM pp WHERE i=42 }
list [catch { sql1 COMMIT } msg] $msg
} {1 {database is locked}}
do_test 9.$tn.2.3 {
sql1 ROLLBACK
} {}
do_test 9.$tn.3.1 {
sql1 {
PRAGMA foreign_keys = 0;
BEGIN CONCURRENT;
INSERT INTO cc VALUES(43, 43);
}
} {}
do_test 9.$tn.3.2 {
sql2 { DELETE FROM pp WHERE i=43 }
list [catch { sql1 COMMIT } msg] $msg
} {0 {}}
do_test 9.$tn.4.1 {
sql1 {
PRAGMA foreign_keys = on;
BEGIN CONCURRENT;
INSERT INTO cc VALUES(44, 44);
}
} {}
do_test 9.$tn.4.2 {
sql2 { DELETE FROM pp WHERE i=1 }
list [catch { sql1 COMMIT } msg] $msg
} {0 {}}
}
#-------------------------------------------------------------------------
# Test that even if a SELECT statement appears before all writes within
# a CONCURRENT transaction, the pages it reads are still considered when
# considering whether or not the transaction may be committed.
#
do_multiclient_test tn {
do_test 10.$tn.1.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(a);
CREATE TABLE t2(b);
CREATE TABLE t3(c);
INSERT INTO t1 VALUES(1), (2), (3);
INSERT INTO t2 VALUES(1), (2), (3);
INSERT INTO t3 VALUES(1), (2), (3);
}
} {wal}
do_test 10.$tn.1.2 {
sql1 {
BEGIN CONCURRENT;
SELECT * FROM t1;
INSERT INTO t2 VALUES(4);
}
} {1 2 3}
do_test 10.$tn.1.3 {
sql2 { INSERT INTO t1 VALUES(4) }
list [catch {sql1 COMMIT} msg] $msg
} {1 {database is locked}}
sql1 ROLLBACK
# In this case, because the "SELECT * FROM t1" is first stepped before
# the "BEGIN CONCURRENT", the pages it reads are not recorded by the
# pager object. And so the transaction can be committed. Technically
# this behaviour (the effect of an ongoing SELECT on a BEGIN CONCURRENT
# transacation) is undefined.
#
do_test 10.$tn.2.1 {
code1 {
set ::stmt [sqlite3_prepare db "SELECT * FROM t1" -1 dummy]
sqlite3_step $::stmt
}
} {SQLITE_ROW}
do_test 10.$tn.2.2 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t2 VALUES(4);
}
code1 {
set res [list]
lappend res [sqlite3_column_int $::stmt 0]
while {[sqlite3_step $::stmt]=="SQLITE_ROW"} {
lappend res [sqlite3_column_int $::stmt 0]
}
sqlite3_finalize $::stmt
set res
}
} {1 2 3 4}
do_test 10.$tn.2.3 {
sql2 { INSERT INTO t1 VALUES(5) }
sql1 COMMIT
} {}
# More tests surrounding long-lived prepared statements and concurrent
# transactions.
do_test 10.$tn.3.1 {
sql1 {
BEGIN CONCURRENT;
SELECT * FROM t1;
COMMIT;
}
sql1 {
BEGIN CONCURRENT;
INSERT INTO t2 VALUES(5);
}
sql2 {
INSERT INTO t1 VALUES(5);
}
sql1 COMMIT
sql3 {
SELECT * FROM t2;
}
} {1 2 3 4 5}
do_test 10.$tn.3.2 {
sql1 {
BEGIN CONCURRENT;
SELECT * FROM t1;
ROLLBACK;
}
sql1 {
BEGIN CONCURRENT;
INSERT INTO t2 VALUES(6);
}
sql2 {
INSERT INTO t1 VALUES(6);
}
sql1 COMMIT
sql3 { SELECT * FROM t2 }
} {1 2 3 4 5 6}
do_test 10.$tn.3.3 {
sql1 { BEGIN CONCURRENT }
code1 {
set ::stmt [sqlite3_prepare db "SELECT * FROM t1" -1 dummy]
sqlite3_step $::stmt
}
sql1 {
INSERT INTO t2 VALUES(7);
SELECT * FROM t3;
ROLLBACK;
BEGIN CONCURRENT;
}
sql2 { INSERT INTO t3 VALUES(5) }
code1 { sqlite3_finalize $::stmt }
sql1 {
INSERT INTO t2 VALUES(8);
COMMIT;
}
} {}
}
do_multiclient_test tn {
do_test 11.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(a);
}
} {wal}
do_test 11.$tn.2 {
code1 { sqlite3_wal_info db main }
} {0 2}
do_test 11.$tn.3 {
sql1 { INSERT INTO t1 VALUES(1) }
code1 { sqlite3_wal_info db main }
} {2 3}
do_test 11.$tn.4 {
sql2 { INSERT INTO t1 VALUES(2) }
code2 { sqlite3_wal_info db2 main }
} {3 4}
do_test 11.$tn.5 {
sql1 { PRAGMA wal_checkpoint }
sql2 { INSERT INTO t1 VALUES(3) }
code2 { sqlite3_wal_info db2 main }
} {0 1}
}
reset_db
do_execsql_test 12.0 {
PRAGMA journal_mode = wal;
CREATE TABLE tx(a INTEGER PRIMARY KEY, b);
} {wal}
do_test 12.1 {
for {set i 0} {$i < 50} {incr i} {
execsql {
BEGIN CONCURRENT;
INSERT INTO tx(b) VALUES( randomblob( 1200 ) );
COMMIT;
}
}
execsql { PRAGMA page_size }
} {1024}
do_execsql_test 12.2 {
DELETE FROM tx;
}
do_test 12.3 {
for {set i 0} {$i < 50} {incr i} {
execsql {
BEGIN CONCURRENT;
INSERT INTO tx(b) VALUES( randomblob( 1200 ) );
COMMIT;
}
}
execsql { PRAGMA page_size }
} {1024}
do_execsql_test 12.4 {
DELETE FROM tx;
}
do_test 12.5 {
execsql { BEGIN CONCURRENT }
for {set i 0} {$i < 5000} {incr i} {
execsql {
INSERT INTO tx(b) VALUES( randomblob( 1200 ) );
}
}
execsql { COMMIT }
execsql { PRAGMA page_size }
} {1024}
finish_test
+231
View File
@@ -0,0 +1,231 @@
# 2015 July 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.
#
#***********************************************************************
#
# Tests for transactions started with BEGIN CONCURRENT. The tests in this
# file focus on testing that deferred page allocation works properly.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
set ::testprefix concurrent3
if {$AUTOVACUUM} { finish_test ; return }
ifcapable !concurrent {
finish_test
return
}
db close
sqlite3_shutdown
test_sqlite3_log xLog
proc xLog {error_code msg} {
# puts "$error_code: $msg"
# Enable the previous for debugging
}
reset_db
proc create_schema {} {
db eval {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x, y);
CREATE TABLE t2(x, y);
CREATE TABLE t3(x, y);
CREATE TABLE t4(x, y);
CREATE INDEX i1 ON t1(y, x);
CREATE INDEX i2 ON t2(y, x);
CREATE INDEX i3 ON t3(y, x);
CREATE INDEX i4 ON t4(y, x);
}
}
proc do_sql_op {iTbl iOp} {
set db "db$iTbl"
switch $iOp {
"i" {
set sql "
WITH cnt(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM cnt WHERE i<10)
INSERT INTO t$iTbl SELECT randomblob(800), randomblob(800) FROM cnt;
"
}
"d" {
set sql "
DELETE FROM t$iTbl WHERE rowid IN (
SELECT rowid FROM t$iTbl ORDER BY 1 ASC LIMIT 10
)
"
}
"D" {
set sql "
DELETE FROM t$iTbl WHERE rowid IN (
SELECT rowid FROM t$iTbl o WHERE (
SELECT count(*) FROM t$iTbl i WHERE i.rowid<o.rowid
) % 2
)
"
}
"I" {
set sql "
INSERT INTO t$iTbl SELECT randomblob(800), randomblob(800) FROM t$iTbl;
"
}
default {
error "bad iOp parameter: $iOp"
}
}
$db eval $sql
}
set DBLIST {db1 db2 db3 db4}
create_schema
foreach {tn oplist} {
1 {1i 2i 3i 4i}
2 {1iii 2iii 3iii 4iii}
3 {1d 2d 3d 4d}
. -----------------------
4 {1i}
5 {1d 2i}
. -----------------------
6 {1iii 2iii 3iii 4iii}
7 {1di 2id 3iii 4ddd}
8 {1iii 2iii 3iii 4iii}
9 {1D 2II}
10 {1I 2D 3I 4D}
11 {1III 3dddddd 4III}
} {
if {[string range $oplist 0 0]=="-"} {
reset_db
create_schema
continue
}
foreach db $DBLIST { sqlite3 $db test.db }
do_test 1.$tn {
foreach db $DBLIST { $db eval "BEGIN CONCURRENT" }
foreach op $oplist {
set iTbl [string range $op 0 0]
foreach char [split [string range $op 1 end] {}] {
do_sql_op $iTbl $char
}
}
foreach db $DBLIST { $db eval "COMMIT" }
db eval {PRAGMA integrity_check}
} {ok}
foreach db $DBLIST {
$db close
}
}
#-------------------------------------------------------------------------
#
proc create_schema2 {} {
db eval {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x INTEGER PRIMARY KEY, y);
CREATE INDEX i1 ON t1(y);
}
}
proc randint {nMax} {
db eval {SELECT abs(random() % $nMax)}
}
proc do_sql_op2 {db iOp} {
switch -- $iOp {
i {
# Insert 1 rows.
set r [randint 1000000000]
set ::rows($r) 1
#puts "insert row $r"
$db eval { INSERT OR IGNORE INTO t1 VALUES($r, randomblob(50)); }
}
d {
# Insert 1 row
set keys [array names ::rows]
set r [randint [llength $keys]]
set rowid [lindex $keys $r]
$db eval { DELETE FROM t1 WHERE x=$rowid }
unset ::rows($rowid)
}
}
}
foreach {tn nRepeat oplist} {
- - ----------------------------
1 100 { 1iiiiiiiiii }
2 100 { 1i 2d }
3 100 { 1d 2i }
4 50 { 1d 2i 3d }
5 500 { 1i 2i 3i 4i }
6 500 { 1i 2d 3d 4d }
} {
if {[string range $oplist 0 0]=="-"} {
array unset rows
reset_db
create_schema2
continue
}
foreach db $DBLIST {
sqlite3 $db test.db
set stats($db,0) 0
set stats($db,1) 0
}
array unset used
do_test 2.$tn {
for {set i 0} {$i < $nRepeat} {incr i} {
foreach db $DBLIST { $db eval "BEGIN CONCURRENT" }
foreach op $oplist {
set iDb [string range $op 0 0]
set used(db$iDb) 1
foreach char [split [string range $op 1 end] {}] {
do_sql_op2 "db$iDb" $char
}
}
foreach db $DBLIST {
set rc [catch { $db eval COMMIT } msg]
if {$rc} { $db eval ROLLBACK }
incr stats($db,$rc)
}
set res [db eval {PRAGMA integrity_check}]
if {$res != "ok"} { puts "after $db $rc: $res" ; after 1000000 }
}
} {}
foreach db $DBLIST {
$db close
}
# foreach k [lsort [array names used]] {
# puts "$k: $stats($k,0) committed, $stats($k,1) rolled back"
# }
}
finish_test
+194
View File
@@ -0,0 +1,194 @@
# 2017 May 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.
#
#***********************************************************************
#
# Miscellaneous tests for transactions started with BEGIN CONCURRENT.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/wal_common.tcl
set ::testprefix concurrent4
ifcapable !concurrent {
finish_test
return
}
do_execsql_test 1.0 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x PRIMARY KEY, y UNIQUE);
WITH s(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100)
INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM s;
DELETE FROM t1 WHERE rowid<2;
} {wal}
do_execsql_test 1.1 {
BEGIN CONCURRENT;
INSERT INTO t1(rowid, x, y) VALUES(1000, randomblob(3000), randomblob(3000));
SAVEPOINT abc;
DELETE FROM t1 WHERE rowid = 1000;
}
do_execsql_test 1.2 { ROLLBACK TO abc }
do_execsql_test 1.3 { COMMIT }
do_execsql_test 1.4 { PRAGMA integrity_check } {ok}
do_multiclient_test tn {
do_test 2.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(a, b);
WITH s(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100)
INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM s;
CREATE TABLE t2(a, b);
WITH s(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100)
INSERT INTO t2 SELECT randomblob(400), randomblob(400) FROM s;
}
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(randomblob(3000), randomblob(3000));
}
} {}
do_test 2.$tn.2 {
sql2 {
WITH s(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<10)
INSERT INTO t2 SELECT randomblob(400), randomblob(400) FROM s;
}
sql2 {
DELETE FROM t2 WHERE rowid<10;
}
} {}
do_test 2.$tn.3 {
sql1 {
COMMIT;
PRAGMA integrity_check;
}
} {ok}
do_test 2.$tn.4 {
sql2 {
PRAGMA integrity_check;
}
} {ok}
}
reset_db
do_execsql_test 3.1 {
PRAGMA page_size = 1024;
PRAGMA journal_mode = wal;
CREATE TABLE t2(x);
INSERT INTO t2 VALUES(randomblob(5000));
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
INSERT INTO t1 VALUES(25, randomblob(104));
DELETE FROM t2;
} {wal}
do_execsql_test 3.2 {
BEGIN CONCURRENT;
REPLACE INTO t1 VALUES(25, randomblob(1117));
COMMIT;
} {}
#-------------------------------------------------------------------------
# Test the effect of BEGIN CONCURRENT transactions that consist entirely
# of read-only statements.
#
reset_db
do_execsql_test 4.0 {
PRAGMA page_size = 1024;
PRAGMA journal_mode = wal;
CREATE TABLE t4(a, b);
INSERT INTO t4 VALUES(1, 2);
INSERT INTO t4 VALUES(3, 4);
} {wal}
sqlite3 db2 test.db
do_test 4.1.1 {
db eval {
BEGIN CONCURRENT;
INSERT INTO t4 VALUES(5, 6);
}
db2 eval {
BEGIN CONCURRENT;
SELECT * FROM t4;
ROLLBACK;
}
} {1 2 3 4}
do_test 4.1.2 {
db eval { COMMIT }
db2 eval { SELECT * FROM t4 }
} {1 2 3 4 5 6}
do_test 4.2.1 {
db eval {
BEGIN CONCURRENT;
INSERT INTO t4 VALUES(7, 8);
}
db2 eval {
BEGIN CONCURRENT;
SELECT * FROM t4;
COMMIT;
}
} {1 2 3 4 5 6}
do_test 4.2.2 {
db eval { COMMIT }
db2 eval { SELECT * FROM t4 }
} {1 2 3 4 5 6 7 8}
do_test 4.3 {
db2 eval {
BEGIN CONCURRENT;
SELECT * FROM t4;
}
db eval {
BEGIN CONCURRENT;
INSERT INTO t4 VALUES(9, 10);
COMMIT;
}
db2 eval {
SELECT * FROM t4;
COMMIT;
}
} {1 2 3 4 5 6 7 8}
set sz [file size test.db-wal]
do_test 4.4.1 {
db eval {
BEGIN CONCURRENT;
SELECT * FROM t4;
SELECT * FROM sqlite_master;
}
db eval COMMIT
file size test.db-wal
} $sz
do_test 4.4.2 {
db eval {
BEGIN CONCURRENT;
SELECT * FROM t4;
SELECT * FROM sqlite_master;
ROLLBACK;
}
file size test.db-wal
} $sz
finish_test
+133
View File
@@ -0,0 +1,133 @@
# 2017 May 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.
#
#***********************************************************************
#
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/wal_common.tcl
set ::testprefix concurrent5
ifcapable !concurrent {
finish_test
return
}
db close
sqlite3_shutdown
test_sqlite3_log [list lappend ::log]
set ::log [list]
sqlite3 db test.db
proc do_test_conflict_msg {tn msg} {
set msg "cannot commit CONCURRENT transaction - [string trim $msg]"
uplevel [list do_test $tn {lindex $::log end} $msg]
}
do_execsql_test 1.0 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x, y);
CREATE TABLE t2(c);
WITH s(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100)
INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM s;
} {wal}
sqlite3 db2 test.db
do_test 1.1.1 {
set ::log [list]
db2 eval {
BEGIN CONCURRENT;
SELECT count(*) FROM t1;
INSERT INTO t2 VALUES(10);
}
db eval {
INSERT INTO t1 VALUES(randomblob(200), randomblob(200));
}
catchsql COMMIT db2
} {1 {database is locked}}
do_test_conflict_msg 1.1.2 {
conflict at page 2 (read-only page; part of db table t1; content=0500000063021100...)
}
do_test 1.2.1 {
set ::log [list]
db2 eval {
ROLLBACK;
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(11, 12);
}
db eval {
INSERT INTO t1 VALUES(12, 11);
}
catchsql COMMIT db2
} {1 {database is locked}}
do_test_conflict_msg 1.2.2 {
conflict at page 105 (read/write page; part of db table t1; content=0D00000002026100...)
}
do_test 1.3.1 {
set ::log [list]
db2 eval {
ROLLBACK;
BEGIN CONCURRENT;
INSERT INTO t2 VALUES('x');
}
db eval {
INSERT INTO t2 VALUES('y');
}
catchsql COMMIT db2
} {1 {database is locked}}
do_test_conflict_msg 1.3.2 {
conflict at page 3 (read/write page; part of db table t2; content=0D0000000103FB00...)
}
do_test 1.4.1 {
set ::log [list]
execsql {
ROLLBACK;
CREATE TABLE t3(a INTEGER PRIMARY KEY, b INTEGER);
CREATE INDEX i3 ON t3(b);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<5000
) INSERT INTO t3 SELECT i, i FROM s;
BEGIN CONCURRENT;
INSERT INTO t3 VALUES(0, 5001);
} db2
execsql { INSERT INTO t3 VALUES(NULL, 5002) } db
catchsql COMMIT db2
} {1 {database is locked}}
do_test_conflict_msg 1.3.2 {
conflict at page 211 (read/write page; part of db index t3.i3; content=0A0310006300D800...)
}
db close
db2 close
sqlite3_shutdown
test_sqlite3_log
sqlite3_initialize
finish_test
+60
View File
@@ -0,0 +1,60 @@
# 2017 May 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.
#
#***********************************************************************
#
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/wal_common.tcl
set ::testprefix concurrent6
ifcapable !concurrent {
finish_test
return
}
sqlite3 db2 test.db
do_execsql_test 1.0 {
PRAGMA page_size = 1024;
PRAGMA journal_mode = wal;
CREATE TABLE t1(x);
CREATE TABLE t2(x);
CREATE TABLE t3(x);
CREATE TABLE t4(x);
INSERT INTO t1 VALUES(zeroblob(1500));
} {wal}
do_execsql_test -db db2 1.1 {
BEGIN CONCURRENT;
INSERT INTO t3 VALUES(zeroblob(4000));
DELETE FROM t1;
}
do_execsql_test 1.2 {
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100)
INSERT INTO t2 SELECT zeroblob(1000) FROM s;
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100)
INSERT INTO t4 SELECT zeroblob(1000) FROM s;
DELETE FROM t4;
}
do_execsql_test -db db2 1.3 {
COMMIT;
}
finish_test
+52
View File
@@ -0,0 +1,52 @@
# 2018 Jan 5
#
# 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.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix concurrent7
sqlite3 db2 test.db
do_execsql_test 1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x);
CREATE TABLE t2(x);
} {wal}
do_execsql_test -db db2 2 {
SELECT * FROM t1;
}
do_execsql_test 3 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(randomblob(1500));
INSERT INTO t1 VALUES(randomblob(1500));
DELETE FROM t1 WHERE rowid = 1;
}
do_execsql_test -db db2 4 {
INSERT INTO t2 VALUES(randomblob(1500));
INSERT INTO t2 VALUES(randomblob(1500));
INSERT INTO t2 VALUES(randomblob(1500));
INSERT INTO t2 VALUES(randomblob(1500));
DELETE FROM t2 WHERE rowid IN (1, 2);
}
do_execsql_test 5 {
COMMIT;
PRAGMA integrity_check;
} {ok}
finish_test
+109
View File
@@ -0,0 +1,109 @@
# 2020 July 17
#
# 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.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix concurrent8
source $testdir/lock_common.tcl
ifcapable !concurrent {
finish_test
return
}
do_multiclient_test tn {
do_test 1.$tn.0 {
sql1 {
CREATE TABLE t1(x, y);
PRAGMA journal_mode = wal;
}
} {wal}
do_test 1.$tn.1 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(1, 1);
}
} {}
do_test 1.$tn.2 {
sql2 {
CREATE TABLE t2(a, b);
}
} {}
do_test 1.$tn.3 {
list [catch { sql1 { COMMIT } } msg] $msg
} {1 {database is locked}}
do_test 1.$tn.4 {
code1 { db errorcode }
} {517} ;# SQLITE_BUSY_SNAPSHOT
do_test 1.$tn.5 {
sql1 {
ROLLBACK;
BEGIN CONCURRENT;
CREATE TABLE t3(a, b);
COMMIT;
}
} {}
do_test 1.$tn.6 {
set nPg [sql1 {PRAGMA page_count}]
sql1 "BEGIN CONCURRENT"
for {set i 0} {$i<250} {incr i} {
sql1 "CREATE TABLE z$i (a, b, c)"
}
sql1 "COMMIT"
set nPg2 [sql1 {PRAGMA page_count}]
expr $nPg2>$nPg
} {1}
do_test 1.$tn.7 {
sql2 { PRAGMA integrity_check }
} {ok}
do_test 1.$tn.8 {
sql1 {
BEGIN CONCURRENT;
CREATE TABLE t4(a, b);
}
sql2 {
INSERT INTO t1 VALUES(2, 2);
}
list [catch { sql1 COMMIT } msg] $msg
} {1 {database is locked}}
sql1 ROLLBACK
do_test 1.$tn.9 {
sql1 {
BEGIN CONCURRENT;
CREATE TEMP TABLE t5(a, b);
INSERT INTO t2 VALUES('x', 'x');
}
sql2 {
INSERT INTO t1 VALUES(3, 3);
CREATE TEMP TABLE t1(x, y);
}
sql1 COMMIT
} {}
}
finish_test
+1 -1
View File
@@ -146,7 +146,7 @@ do_catchsql_test 2.1 {
SELECT count(*) FROM sqlite_schema;
WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<1000)
INSERT INTO t1(a) SELECT randomblob(null) FROM c;
} {1 {database disk image is malformed}}
} {0 4}
reset_db
if {![info exists ::G(perm:presql)]} {
+30
View File
@@ -241,5 +241,35 @@ do_execsql_test 7.2 {
PRAGMA foreign_key_check;
} {}
# 2021-12-31 forum https://sqlite.org/forum/forumpost/24bd1fef7e9323ef
# Memory leak caused by sqlite3NestedParse() running on a corrupt system
# table. Discovered by Jingzhou Fu.
#
reset_db
do_execsql_test 8.1 {
PRAGMA writable_schema=ON;
PRAGMA foreign_keys = ON;
CREATE TABLE sqlite_stat1 (tbl INTEGER PRIMARY KEY DESC, idx UNIQUE DEFAULT NULL) WITHOUT ROWID;
PRAGMA writable_schema=OFF;
CREATE TABLE sqlsim4(stat PRIMARY KEY);;
CREATE TABLE t1(sqlsim7 REFERENCES sqlite_stat1 ON DELETE CASCADE);
DROP table "sqlsim4";
} {}
# 2022-01-01 dbsqlfuzz 1c57440219f6f0aedf5e8f72a8ddd75f15aea381
# Follow-up case to the above. Assertion is not true if the schema
# is corrupt.
reset_db
database_may_be_corrupt
do_execsql_test 8.2 {
CREATE TABLE t1(a REFERENCES sqlite_stat1 ON DELETE CASCADE);
CREATE TABLE t2(a TEXT PRIMARY KEY);
PRAGMA writable_schema=ON;
CREATE TABLE sqlite_stat1(tbl INTEGER PRIMARY KEY DESC, idx UNIQUE DEFAULT NULL) WITHOUT ROWID;
UPDATE sqlite_schema SET name='sqlite_autoindex_sqlite_stat1_1' WHERE name='sqlite_autoindex_sqlite_stat1_2';
PRAGMA writable_schema=RESET;
} {}
do_catchsql_test 8.3 {
REINDEX;
} {1 {database disk image is malformed}}
finish_test
+62
View File
@@ -0,0 +1,62 @@
# 2022 Jan 01
#
# 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.
#
#***********************************************************************
# Tests focused on the in-memory journal.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
set testprefix memjournal2
do_execsql_test 1.0 {
PRAGMA journal_mode = memory;
CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE);
} {memory}
set nRow [expr 2000]
do_execsql_test 1.1 {
BEGIN;
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<$nRow
)
INSERT INTO t1 SELECT NULL, randomblob(700) FROM s;
}
for {set jj 200} {$jj <= 300} {incr jj} {
do_execsql_test 1.2.$jj.1 {
SAVEPOINT one;
UPDATE t1 SET b=randomblob(700) WHERE a<=$jj;
}
do_execsql_test 1.2.$jj.2 {
SAVEPOINT two;
UPDATE t1 SET b=randomblob(700) WHERE a==1;
ROLLBACK TO two;
RELEASE two;
}
do_execsql_test 1.2.$jj.3 {
SAVEPOINT two;
UPDATE t1 SET b=randomblob(700) WHERE a==1;
ROLLBACK TO two;
RELEASE two;
}
do_execsql_test 1.2.$jj.4 {
PRAGMA integrity_check;
ROLLBACK TO one;
RELEASE one;
} {ok}
}
finish_test
+36
View File
@@ -0,0 +1,36 @@
# 2020 September 01
#
# 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.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix noop_update
if {[db eval {PRAGMA noop_update}]==""} {
finish_test
return
}
do_execsql_test 1.0 {
CREATE TABLE t1(x, y);
INSERT INTO t1 VALUES('a', 111);
}
do_execsql_test 1.1 {
UPDATE t1 SET y=222 WHERE x='a';
SELECT * FROM t1;
} {a 222}
do_execsql_test 1.2 {
PRAGMA noop_update = 1;
UPDATE t1 SET y=333 WHERE x='a';
SELECT * FROM t1;
} {a 222}
finish_test
-103
View File
@@ -1,103 +0,0 @@
# 2014-08-24
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.
# The focus of this script is testing details of the SQL language parser.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
do_catchsql_test parser1-1.1 {
CREATE TABLE t1(
a TEXT PRIMARY KEY,
b TEXT,
FOREIGN KEY(b COLLATE nocase DESC) REFERENCES t1(a COLLATE binary ASC)
);
} {1 {syntax error after column name "b"}}
# Verify that a legacy schema in the sqlite_master file is allowed to have
# COLLATE, ASC, and DESC keywords on the id list of a FK constraint, and that
# those keywords are silently ignored.
#
sqlite3_db_config db DEFENSIVE 0
do_execsql_test parser1-1.2 {
CREATE TABLE t1(
a TEXT PRIMARY KEY,
b TEXT,
FOREIGN KEY(b) REFERENCES t1(a)
);
INSERT INTO t1 VALUES('abc',NULL),('xyz','abc');
PRAGMA writable_schema=on;
UPDATE sqlite_master SET sql='CREATE TABLE t1(
a TEXT PRIMARY KEY,
b TEXT,
FOREIGN KEY(b COLLATE nocase) REFERENCES t1(a)
)' WHERE name='t1';
SELECT name FROM sqlite_master WHERE sql LIKE '%collate%';
} {t1}
sqlite3 db2 test.db
do_test parser1-1.3 {
sqlite3 db2 test.db
db2 eval {SELECT * FROM t1 ORDER BY 1}
} {abc {} xyz abc}
db2 close
do_execsql_test parser1-1.4 {
UPDATE sqlite_master SET sql='CREATE TABLE t1(
a TEXT PRIMARY KEY,
b TEXT,
FOREIGN KEY(b ASC) REFERENCES t1(a)
)' WHERE name='t1';
SELECT name FROM sqlite_master WHERE sql LIKE '%ASC%';
} {t1}
sqlite3 db2 test.db
do_test parser1-1.5 {
sqlite3 db2 test.db
db2 eval {SELECT * FROM t1 ORDER BY 1}
} {abc {} xyz abc}
db2 close
do_catchsql_test parser1-2.1 {
WITH RECURSIVE
c(x COLLATE binary) AS (VALUES(1) UNION SELECT x+1 FROM c WHERE x<5)
SELECT x FROM c;
} {1 {syntax error after column name "x"}}
do_catchsql_test parser1-2.2 {
WITH RECURSIVE
c(x ASC) AS (VALUES(1) UNION SELECT x+1 FROM c WHERE x<5)
SELECT x FROM c;
} {1 {syntax error after column name "x"}}
# Verify that the comma between multiple table constraints is
# optional.
#
# The missing comma is technically a syntax error. But we have to support
# it because there might be legacy databases that omit the commas in their
# sqlite_master tables.
#
do_execsql_test parser1-3.1 {
CREATE TABLE t300(id INTEGER PRIMARY KEY);
CREATE TABLE t301(
id INTEGER PRIMARY KEY,
c1 INTEGER NOT NULL,
c2 INTEGER NOT NULL,
c3 BOOLEAN NOT NULL DEFAULT 0,
FOREIGN KEY(c1) REFERENCES t300(id) ON DELETE CASCADE ON UPDATE RESTRICT
/* no comma */
FOREIGN KEY(c2) REFERENCES t300(id) ON DELETE CASCADE ON UPDATE RESTRICT
/* no comma */
UNIQUE(c1, c2)
);
PRAGMA foreign_key_list(t301);
} {0 0 t300 c2 id RESTRICT CASCADE NONE 1 0 t300 c1 id RESTRICT CASCADE NONE}
finish_test
+36
View File
@@ -0,0 +1,36 @@
# 2022 January 5
#
# 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.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
do_execsql_test 1.0 {
CREATE TABLE t1 (b);
} {}
faultsim_save_and_close
do_faultsim_test pagerfault-1 -faults oom-t* -prep {
faultsim_restore_and_reopen
} -body {
execsql {
INSERT INTO t1(b) VALUES(65) RETURNING (
SELECT * FROM sqlite_temp_schema
) AS aaa;
}
} -test {
faultsim_test_result {1 {sub-select returns 5 columns - expected 1}}
}
finish_test
+1035
View File
File diff suppressed because it is too large Load Diff
+14
View File
@@ -241,4 +241,18 @@ do_catchsql_test upsert1-1000 {
ON CONFLICT(c2) DO UPDATE SET c1 = c0;
} {1 {NOT NULL constraint failed: t0.c0}}
# 2021-12-29 forum post https://sqlite.org/forum/forumpost/06b16b8b29f8c8c3
# By Jingzhou Fu. When there is both an INTEGER PRIMARY KEY ON CONFLICT REPLACE
# and an upsert on a constraint other than the INTEGER PRIMARY KEY, the
# constraint checking logic generates invalid bytecode which might result
# in a NULL pointer dereference.
#
reset_db
do_execsql_test upsert1-1100 {
CREATE TABLE t1(a INTEGER PRIMARY KEY ON CONFLICT REPLACE, b UNIQUE);
INSERT INTO t1(b) VALUES(22);
INSERT INTO t1 VALUES(2,22) ON CONFLICT (b) DO NOTHING;
SELECT * FROM t1;
} {1 22}
finish_test
-37
View File
@@ -35,43 +35,6 @@ proc cond_incr_sync_count {adj} {
}
}
proc set_tvfs_hdr {file args} {
# Set $nHdr to the number of bytes in the wal-index header:
set nHdr 48
set nInt [expr {$nHdr/4}]
if {[llength $args]>2} {
error {wrong # args: should be "set_tvfs_hdr fileName ?val1? ?val2?"}
}
set blob [tvfs shm $file]
if {$::tcl_platform(byteOrder)=="bigEndian"} {set fmt I} {set fmt i}
if {[llength $args]} {
set ia [lindex $args 0]
set ib $ia
if {[llength $args]==2} {
set ib [lindex $args 1]
}
binary scan $blob a[expr $nHdr*2]a* dummy tail
set blob [binary format ${fmt}${nInt}${fmt}${nInt}a* $ia $ib $tail]
tvfs shm $file $blob
}
binary scan $blob ${fmt}${nInt} ints
return $ints
}
proc incr_tvfs_hdr {file idx incrval} {
set ints [set_tvfs_hdr $file]
set v [lindex $ints $idx]
incr v $incrval
lset ints $idx $v
set_tvfs_hdr $file $ints
}
#-------------------------------------------------------------------------
# Test case wal2-1.*:
#
+40
View File
@@ -90,4 +90,44 @@ proc wal_fix_walindex_cksum {hdrvar} {
lset hdr 11 $c2
}
# This command assumes that $file is the name of a database file opened
# in wal mode using a [testvfs] VFS. It returns a list of the 12 32-bit
# integers that make up the wal-index-header for the named file.
#
proc set_tvfs_hdr {file args} {
# Set $nHdr to the number of bytes in the wal-index header:
set nHdr 48
set nInt [expr {$nHdr/4}]
if {[llength $args]>2} {
error {wrong # args: should be "set_tvfs_hdr fileName ?val1? ?val2?"}
}
set blob [tvfs shm $file]
if {$::tcl_platform(byteOrder)=="bigEndian"} {set fmt I} {set fmt i}
if {[llength $args]} {
set ia [lindex $args 0]
set ib $ia
if {[llength $args]==2} {
set ib [lindex $args 1]
}
binary scan $blob a[expr $nHdr*2]a* dummy tail
set blob [binary format ${fmt}${nInt}${fmt}${nInt}a* $ia $ib $tail]
tvfs shm $file $blob
}
binary scan $blob ${fmt}${nInt} ints
return $ints
}
proc incr_tvfs_hdr {file idx incrval} {
set ints [set_tvfs_hdr $file]
set v [lindex $ints $idx]
incr v $incrval
lset ints $idx $v
set_tvfs_hdr $file $ints
}
+10 -2
View File
@@ -13,7 +13,7 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix windowB
set testprefix windowC
ifcapable !windowfunc {
finish_test
@@ -67,11 +67,19 @@ foreach {tn bBlob seps} {
#
reset_db
do_execsql_test 2.0 {
PRAGMA encoding=UTF16;
PRAGMA encoding=UTF16le;
WITH separator(x) AS (VALUES(',a,'),(',bc,')),
value(y) AS (VALUES(1),(x'5585d09013455178cd11ce4a'))
SELECT group_concat(y,x) OVER (ORDER BY x ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING)
FROM separator, value;
} {{} 1 蕕郐䔓硑ᇍ䫎 1}
reset_db
do_execsql_test 2.1 {
PRAGMA encoding=UTF16be;
WITH separator(x) AS (VALUES(',a,'),(',bc,')),
value(y) AS (VALUES(1),(x'5585d09013455178cd11ce4a'))
SELECT group_concat(y,x) OVER (ORDER BY x ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING)
FROM separator, value;
} {{} 1 喅킐ፅ典촑칊 1}
finish_test
+16 -1
View File
@@ -470,5 +470,20 @@ ifcapable altertable {
EXPLAIN QUERY PLAN SELECT * FROM dual, t1 WHERE a=10 AND b=10;
} {~/b=/}
}
# 2022-01-01 https://sqlite.org/forum/forumpost/b03d86f951 PoC #1
# Omit an assert() from 2013 that no longer serves any purpose and
# is no longer always true.
#
reset_db
do_execsql_test 15.1 {
PRAGMA writable_schema=ON;
CREATE TABLE sqlite_sequence (name PRIMARY KEY) WITHOUT ROWID;
PRAGMA writable_schema=OFF;
CREATE TABLE c1(x);
INSERT INTO sqlite_sequence(name) VALUES('c0'),('c1'),('c2');
ALTER TABLE c1 RENAME TO a;
SELECT name FROM sqlite_sequence ORDER BY +name;
} {a c0 c2}
finish_test
+3
View File
@@ -75,6 +75,9 @@ static sqlite3_uint64 logEstToInt(LogEst x){
x /= 10;
if( n>=5 ) n -= 2;
else if( n>=1 ) n -= 1;
if( x>60 ){
return (((sqlite3_uint64)0xffffffff)<<32)+(sqlite3_uint64)0xffffffff;
}
if( x>=3 ) return (n+8)<<(x-3);
return (n+8)>>(3-x);
}
+6
View File
@@ -107,6 +107,12 @@ set pragma_def {
IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
IF: defined(SQLITE_DEBUG)
NAME: noop_update
TYPE: FLAG
ARG: SQLITE_NoopUpdate
IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
IF: defined(SQLITE_ENABLE_NOOP_UPDATE)
NAME: ignore_check_constraints
TYPE: FLAG
ARG: SQLITE_IgnoreChecks
+3 -3
View File
@@ -726,7 +726,7 @@ static void decode_btree_page(
}
if( showMap ){
printf("Page map: (H=header P=cell-index 1=page-1-header .=free-space)\n");
for(i=0; i<g.pagesize; i+=64){
for(i=0; (u32)i<g.pagesize; i+=64){
printf(" %03x: %.64s\n", i, &zMap[i]);
}
sqlite3_free(zMap);
@@ -861,7 +861,7 @@ static int allZero(unsigned char *a, int n){
*/
static void page_usage_btree(
u32 pgno, /* Page to describe */
u32 parent, /* Parent of this page. 0 for root pages */
int parent, /* Parent of this page. 0 for root pages */
int idx, /* Which child of the parent */
const char *zName /* Name of the table */
){
@@ -954,7 +954,7 @@ static void page_usage_freelist(u32 pgno){
int iNext;
int parent = 1;
while( pgno>0 && pgno<=g.mxPage && (cnt++)<g.mxPage ){
while( pgno>0 && pgno<=g.mxPage && (u32)(cnt++)<g.mxPage ){
page_usage_msg(pgno, "freelist trunk #%d child of %d", cnt, parent);
a = fileRead((pgno-1)*g.pagesize, g.pagesize);
iNext = decodeInt32(a);