Compare commits

...

466 Commits

Author SHA1 Message Date
dan
f0f625bda5 Increase maximum length of sqlite3_log() messages to 700 bytes. Mark log messages as v=10.
FossilOrigin-Name: 35aa893d4537d0b3605084a1f2f5529794e82af59b8893053815d3fcb4719a27
2024-11-13 14:42:32 +00:00
dan
0a1832e338 Optimize deferred allocation of pages on this branch by avoiding BTALLOC_LE.
FossilOrigin-Name: e0c0176793d41bef30e093975a87170981277dac20cde08e0fd3e0b6f40dca2f
2024-11-13 11:12:30 +00:00
dan
45537554fa Even more detailed logging of slow COMMIT statements.
FossilOrigin-Name: 3a45387a09398db8efdb2b6ecd773fa30970e61e5242823e04a2e968d1c93c10
2024-11-08 17:09:34 +00:00
dan
259b4fe442 Update log messages to say "v=8" instead of "v=7".
FossilOrigin-Name: 492a0a85438586a8ccd9629239304f4df3f2c6391ec05dd9837b7a553313d632
2024-11-01 15:25:34 +00:00
dan
10ad876270 Still more logging, this time focused on the sqlite3WalFrames() function.
FossilOrigin-Name: 52d332f0c7780acabb6a06b5fe14f654e98b64dbbba9984fb18913a97ed7fab6
2024-11-01 15:20:39 +00:00
dan
328d71ced2 Add extra logging to analyze COMMIT commands.
FossilOrigin-Name: 7dbb43bd307b5e297e6b2deac4e1d73d8cd68e37d880a5ecf5a3918a53e99d9d
2024-10-30 18:24:33 +00:00
dan
2a93c70dfa Update special logging messages on this branch to say "v=6" instead of "v=5".
FossilOrigin-Name: af4e778bd3bdf30cac3d9a6266f105a8cd71f947b8d71249ef74b8fdc0505dd7
2024-10-25 18:37:00 +00:00
dan
b27696b0d2 Reduce the number of malloc()/free() calls that may occur when a BEGIN CONCURRENT transaction is committed.
FossilOrigin-Name: fcd68bebf27e10e4e49efad220c9f53b7a109696d8b41e24f88c02460197b93f
2024-10-25 18:24:53 +00:00
dan
6bef80b425 Extra logging to debug slow commit statements.
FossilOrigin-Name: 9cafae1fff34ff45772910d6f2a19d08038edea4327234f577f6da30e5baa558
2024-10-23 17:16:59 +00:00
dan
b9cdea7c98 Add still more logging to determine why schema loads are so slow in some circumstances.
FossilOrigin-Name: d02ec06e5bde7c8dc0f21ca2bfc44597aca015854d5656f851f4518476d04381
2024-10-22 16:26:14 +00:00
dan
fdf8f9b72f Add extra logging to SQL statements used to load the database schema on this branch. This requires SQLITE_ENABLE_STMT_SCANSTATUS.
FossilOrigin-Name: 08e1dea9c070c9a2d668d39ceb153bc8b6d172e7273f6564a374c43055e84461
2024-10-21 11:49:04 +00:00
dan
a5a3174b3b Fix a typo in the previous commit.
FossilOrigin-Name: 6deb4794f8e24dbb946069de1e5c34fbcd4734162002f3bc7deb1a3f69adec05
2024-10-10 15:28:18 +00:00
dan
0fdeeae14d Add extra logging points to the code on this branch.
FossilOrigin-Name: f29ff2f308d7f7c757a8bdf9624fadbda55bd5291c18268e10d3bb7e311b229e
2024-10-10 14:31:37 +00:00
dan
1c55a2d5cb Add logging to help analyze slow "PRAGMA cache_size = ?" statements.
FossilOrigin-Name: cd6f585cadfe40778c3bf59dfbfa5f02c50cf5e10af1bd553cad4dfa56ea2249
2024-10-08 15:08:04 +00:00
dan
906f931c10 Add logging to help analyze commit performance.
FossilOrigin-Name: c5391f2cc913a16695efb63f7d2320fc18b8aaefdbdffa087a2e3d26b24dc218
2024-10-07 16:11:10 +00:00
drh
854c8b2b2d Update the bedrock branch to include all of the latest trunk enhancements.
FossilOrigin-Name: cf8f155280100c1a5b79b118875a93686a554d8be55114277f4f7b20cfd7835a
2024-09-04 16:54:56 +00:00
drh
81d50a0880 Merge all the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 9f530343717a891d57013c5fb58f30e422da391815a7cc79ed8beb1196c89faa
2024-09-04 16:46:09 +00:00
drh
920662eaf3 Merge the latest trunk enhancement into the bedrock branch through the wal2
intermediary.

FossilOrigin-Name: ff94464cec871bd90634e7e1e4252954eb68e77037dd290b0fbf7747c9b19308
2024-08-29 23:43:55 +00:00
drh
cb4a48e249 Merge all the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: dfecc79c8fa4b39e5211f1a7051783c750bb7e1132f3e3a60f8ae0837dbbe486
2024-08-29 23:33:17 +00:00
drh
e11232a37a Merge all the latest trunk enhancements, and especially the
order-by-subquery optimization, into the bedrock branch, via wal2.

FossilOrigin-Name: d0591db3bb84c8f6325d3d5a44467f602cf6d8da6aef6456ef0bd3a19fca3d90
2024-08-16 19:04:36 +00:00
drh
f5528a1168 Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: a78208b597ee34b1121dd8014b3c1376b46baddff41448c96a37723cc9ede921
2024-08-16 18:58:50 +00:00
dan
742f74ec19 Fix some signed/unsigned compiler warnings on this branch.
FossilOrigin-Name: 681d228023efb902b9039acf33600dd91f158e622d4dc0b434ed74f39ce0070a
2024-08-09 10:56:11 +00:00
dan
0632fb359a Fix some signed/unsigned compiler warnings on this branch.
FossilOrigin-Name: d615d5291871ba120d916c4722e6067e083f394c6a7f398059442b36c4500718
2024-08-09 10:50:20 +00:00
stephan
e4d39e70c3 Remove two unnecessary int casts, as reported in [forum:84fe63b7b3|forum post 84fe63b7b3].
FossilOrigin-Name: 1de0c1b574d53c9bd2f2c6070b42e973d63d2a74f150836ed54c78614e286f92
2024-08-09 09:49:24 +00:00
drh
d214af0b4a Merge the latest trunk enhancements into the bedrock branch.
FossilOrigin-Name: c1f616ce802fbb9c3652f8cb43970e4bda18464e765f23fb5f96029721431092
2024-08-01 00:47:59 +00:00
drh
76e67700e0 Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: eac582056fcf6210bf90576cce30c087503b864dc8d558f2998f04b3d1bfb569
2024-08-01 00:38:59 +00:00
dan
c5f1828df3 Fix another problem with the sqlite3_log() message identifying the table or index that a conflicting page belongs to.
FossilOrigin-Name: 19d5fd8a483d23262c98524386d39bb7f4fb9ed79343c574abe51b57eb8296ec
2024-05-28 18:41:32 +00:00
drh
f5266055a3 Version 3.46.0 for the bedrock branch
FossilOrigin-Name: 483d112ab4690853dd2ab4406a865e9e64a13bafb4d623ea57c04cc3980651fb
2024-05-23 14:09:13 +00:00
drh
0d8cea5109 Version 3.46.0 for the wal2 branch.
FossilOrigin-Name: fdc0e1480a941d494e556331544cb40b2a997e8709e2c7fab1bed572007eb88d
2024-05-23 14:05:34 +00:00
drh
82e5a2a123 Merge the latest trunk enhancements into the bedrock branch.
FossilOrigin-Name: c65a82f1faa6cdfc710c01869943b12033a43868e8343468e326816e5a2fd5ad
2024-05-21 15:24:33 +00:00
drh
68796a609b Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: d4217d9f70118a47c0052be940955cf85356b9a08166cef9712668175baea999
2024-05-21 15:20:13 +00:00
drh
557ba97227 Merge all of the latest trunk enhancements into the bedrock branch.
FossilOrigin-Name: 16dac180b5fcc619104c789ed8bfec691fc6b808294aa41d9e0795ad2810a83a
2024-04-15 14:45:06 +00:00
drh
ba9c7c10cc Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 4a72acbc4b87441dd0cacdcfdeac7cce7b1303d00ab494249d98f4518ae3eacf
2024-04-15 14:39:19 +00:00
drh
de4df544c1 Merge the latest trunk enhancements, and especially the enhancement to the
WHERE-clause push-down optimization, into the bedrock branch.

FossilOrigin-Name: 0f93b52c7727034f3dffd90e606f97f54fe8940db79832923a046808bb0a7699
2024-04-08 12:02:55 +00:00
drh
7d13a403c4 Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 59ccea7db787a32091466b2b49c7d9f9db4f9d712830d9b3e48aabc9e45f7ace
2024-04-08 11:57:01 +00:00
drh
bc4ac5f7c6 Merge the latest trunk enhancements into the bedrock branch.
FossilOrigin-Name: 7a2a64b64655e9fdc551765f9f6b6a4d2898a488230eca240b6ee4df86b5c491
2024-04-05 14:54:26 +00:00
drh
b7e87a53c7 Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: a8a8a2db9b879c543fb3fd25cf66717a3dfdf9857dcd1ab9a98b31fbc85dbfc9
2024-04-05 14:50:12 +00:00
dan
d12fb81174 Fix handling of an OOM case broken by [11015b4e].
FossilOrigin-Name: 1f3a805d19c03152bffba783f31d7c2043206c43221847005190c93033d97cf0
2024-03-29 18:19:51 +00:00
dan
4b2d8ce622 Fix a problem in SQLITE_DIRECT_OVERFLOW_READ builds that could allow a concurrent transaction to be committed even if it read from an overflow page that was modified concurrently, in cases where the overflow page was written without also writing the b-tree page to which it is linked.
FossilOrigin-Name: 11015b4ed161263ae14bcd0fe6a24074a545bb6d2a0b3f6f1f75ba3eaa91bfc0
2024-03-29 18:07:41 +00:00
dan
ed0cb47925 Improve the log message emitted when a BEGIN CONCURRENT transaction cannot be committed due to conflicts so that it identifies the conflicting table in a few more cases.
FossilOrigin-Name: 19ff2d4e1e7c6d29773d76ba752338c1c2e9ab355cdd45aed1d936880fa1c917
2024-03-29 18:04:56 +00:00
drh
1d16c3fc2d Merge all recent trunk enhancements into the bedrock branch.
FossilOrigin-Name: 33d3453f467791eecc81dd682890eaadbbb2afc105ef70e22a815f82ead2c350
2024-03-26 10:45:54 +00:00
drh
e24103baad Merge all recent trunk enhancements into the wal2 branch.
FossilOrigin-Name: c2139d826355009e6fb30091734c388bdcb8cb0889f7225481c163850a3e71ff
2024-03-26 10:42:52 +00:00
drh
8c6bc81290 Merge the latest trunk enhancements into the bedrock branch.
FossilOrigin-Name: 10ab5e85700945d3e87dab15e39189537ae87d138ece0045050b8b7d02662ef1
2024-03-18 18:55:47 +00:00
drh
4891bf00db Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: c0af1242d86a85cf32d9fbaa614a097e77142e7ba89c3e3c1be74c0e9768126b
2024-03-18 18:51:49 +00:00
drh
1d4bf383d7 Merge the latest trunk enhancements into the bedrock branch.
FossilOrigin-Name: 5c8e80f435dc24d5af9f217f9034215bf0b39fa5888eda9f4416da832da2735f
2024-03-13 18:00:23 +00:00
drh
467d804e1a Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 2b12da723400a9191c21108e7eaa76e61f373267380c46813031b66b61446067
2024-03-13 17:55:43 +00:00
dan
5f11f50340 Fix a problem with the sqlite3_log() message identifying the table or index that a conflicting page belongs to.
FossilOrigin-Name: 11a4178f0e6cd760303a5874b5a3529ccd78a2398b59700e878c7e6b1251de91
2024-02-16 11:05:35 +00:00
dan
83b448f75a Merge latest wal2 changes into this branch.
FossilOrigin-Name: c39f6585226dfc460372d55189b37fba07468126d3961bff625cea8a95956783
2024-01-13 20:36:34 +00:00
dan
ff46ff5c73 Merge latest trunk changes into this branch.
FossilOrigin-Name: 68c2b8d0ebd1a180c4828cb4996c6c5fd67386c3b6304edf587c255f8d067c24
2024-01-13 20:21:36 +00:00
drh
7936fef022 Merge the latest trunk enhancements into the bedrock branch.
FossilOrigin-Name: 707f79c70c8b0fd889aede8806148457c691a008d0ce030084ba79f1e66ec53d
2024-01-04 16:28:49 +00:00
drh
b60eb8bf6e Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 8fb42df89a47b716c824de8742b7e3bda1a5c0f9a85ce3f328d7aa94ab735497
2024-01-04 16:16:41 +00:00
drh
5f1abc5402 Merge the latest trunk changes into the bedrock branch.
FossilOrigin-Name: 5a17b972ed455aac751453cd8eaa8f059db1ac5c7f27965dce4956563e7911ea
2023-12-28 14:01:09 +00:00
drh
5d30acad2f Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 1f592dd32d165456d40a90a2757225e05cdb810518beee87f0700863dc73d2d0
2023-12-28 13:44:43 +00:00
drh
ca86e10cdd Merge the latest trunk changes into the bedrock branch.
FossilOrigin-Name: c2e53000f4740be9f0492fda1857c5f2f5539fe17bdaafcafdf8d14c8f3218ce
2023-12-06 21:18:03 +00:00
drh
d352e7d6ac Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 457724e7380a8e768d945afb37fb634a6ec83ddebbc4ad144229c0dd498a5b2e
2023-12-06 21:11:11 +00:00
dan
47409209c2 Update this branch with latest changes from trunk.
FossilOrigin-Name: 7f1b61a3ea2cc5132abf91b0f635be4fcaa082de7b33ca131874a75ae11ee576
2023-11-28 17:32:30 +00:00
drh
89b4fcfb0a Version 3.44.0 for the bedrock branch.
FossilOrigin-Name: 827cf67fb37e2413069bf3c76aa054cc3b183845afbdfa118de948d3cf847054
2023-11-01 12:45:01 +00:00
drh
07a7cd3714 Version 3.44.0 for the wal2 branch
FossilOrigin-Name: 470152fd2847296e0fc325fdf098e49c888e99a2332fb45533f976ad85a67d8d
2023-11-01 12:41:25 +00:00
drh
c667489332 Merge the latest 3.44.0 release candidate patches into the bedrock branch.
FossilOrigin-Name: 956b03a4627ea2b9ab7596515aaf076a9895262d9ee23e5248c18d1a75f035a6
2023-10-30 22:57:30 +00:00
drh
47e42cd143 Merge the latest 3.44.0 changes into wal2 branch.
FossilOrigin-Name: 4f65ae2df071d2064ff1494b918c1dee2147e174f79060cd031c25f1aff70a7e
2023-10-30 21:46:46 +00:00
dan
68666f36f9 Fix a test case in fts3corrupt4.test by bypassing the fast-prng.
FossilOrigin-Name: 540ce72b5089413ed5bf2f0fbdf2f9874aee6cf3e42f24b4e8bfb440816e734b
2023-10-24 19:05:55 +00:00
dan
1d2a05fc10 Merge latest changes from wal2 into this branch.
FossilOrigin-Name: c9c9bc097a622bfb6e094b1178cd44da19418cf19cb2d6ad54b9bb868de51f10
2023-10-23 16:08:37 +00:00
dan
757372ab56 Merge latest trunk changes into this branch.
FossilOrigin-Name: 3a6479d3f9d929c63e0caed6288d24ef004edee669d242285dea3fc60345038a
2023-10-23 16:00:26 +00:00
dan
9d086cc562 Fix a sanitizer runtime error in wal2 code (that (1 << 31) cannot be represented as an int).
FossilOrigin-Name: 79a920c036c91bbae737e4f4c5310052d26407fe7d23bcf95a2aa79ea3773a0f
2023-09-08 16:49:45 +00:00
dan
2f3d5b1aaf Fix an apparently harmless buffer-overrun that could occur on this branch when committing multi-file transactions in wal2 mode.
FossilOrigin-Name: ffe043ba061b9ed59e770fb3f0811e1900ecb42b7c54dea0c47c9610e578f875
2023-09-08 16:07:05 +00:00
dan
1fd73f2693 Fix an apparently harmless buffer-overrun that could occur on this branch when committing multi-file transactions in wal2 mode.
FossilOrigin-Name: dde9fcbde16bf71c4ab708e2db769b9cefacd0d7bce8223a46bf1264ae7ddecd
2023-09-08 16:05:09 +00:00
drh
5267068d3d (no comment)
FossilOrigin-Name: 9e927eed2d9e379dd79ef82aa1e41102463c5654b8f4e343bb88bb0daf55c689
2023-08-29 20:19:42 +00:00
dan
6859ca450f Fix a test case problem in wal2recover2.test.
FossilOrigin-Name: 81361769e17b3d1589c5d0f9991a139963c6633d26bc72f189a69e0391d8f52f
2023-08-24 13:42:10 +00:00
drh
4924030bf5 Version 3.43.0 for the reuse-schema branch.
FossilOrigin-Name: e7b1ee1a999ced9aaae66d3bf51d93c20909541a83ecba79a19e5573fa7d649e
2023-08-24 13:26:51 +00:00
drh
0205717b93 Version 3.43.0 for the wal2 branch.
FossilOrigin-Name: b5970fc4599d4fe81312e049420724b14e3d2c1de04769844fb1ab8d953170cd
2023-08-24 13:19:33 +00:00
dan
a7866eefb5 Merge latest changes from the wal2 branch into this one.
FossilOrigin-Name: 4d8df0c426b8ce3db6cfb71e23f752026ef886b9bb833dc4be9717db9955b1db
2023-08-21 18:31:53 +00:00
dan
aabb710293 Fix problems related to structured-exception-handling on this branch.
FossilOrigin-Name: d3d77e35ea39df9e22032a7e1af5b7f38d53a4d43bc46afe36e342cdcfd17528
2023-08-21 18:25:41 +00:00
dan
cc74eafee5 Merge latest trunk changes into this branch.
FossilOrigin-Name: 135bf72c6b5f436b11463dd40b3a73bcc610bf9c2dcbe6646dbedd0d9325f6a9
2023-08-21 18:02:57 +00:00
dan
f08877f094 Merge latest wal2 changes, including support for handling of structured-exceptions in MSVC builds, with this branch.
FossilOrigin-Name: 331f2f3e5db9b6139be984f1b959cd0d51563adaa68452aa2f42741c69bc6319
2023-08-11 19:36:24 +00:00
dan
45d92dd539 Merge latest trunk changes, including support for handling of structured-exceptions in MSVC builds, with this branch.
FossilOrigin-Name: 00bfae9ffafd71dfd0bbdbdc174e4ed7d7a850b385416f83be36a4f4a55d1873
2023-08-11 19:33:54 +00:00
dan
4e5c5daae7 Fix a problem with handling savepoints in BEGIN CONCURRENT transactions.
FossilOrigin-Name: ca422d00d7843ee72a1ae20753ba512896d6f6d2b1218523323946bf66f3c758
2023-08-11 18:31:17 +00:00
drh
08901378d9 Merge all the latest trunk enhancements into the bedrock branch.
FossilOrigin-Name: 859932ebce830c757cf4e889f2efc54807bd4d7947cf5d64a5d9e570bf75ed80
2023-08-11 11:46:08 +00:00
drh
6fe6b73a8d Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: fe1bf30ca0c529c3c68f2531e28aca5659aef5d15c2e3a6925ecd35a5098386b
2023-08-11 11:40:32 +00:00
dan
7a1cc96197 Update test scripts specific to this branch so that they work on windows.
FossilOrigin-Name: f0ca13edad058628e454cc895faac5751de77115c5fea62e98c29d9fdb6b0559
2023-08-11 11:23:35 +00:00
dan
1e6ee1929c Mark test script wal2big.test as "TESTRUNNER: slow".
FossilOrigin-Name: ae7f3bcb5babfa50084f5b4d3c5663489dbf306440706152642b7f0fb47fa1f5
2023-08-10 17:04:29 +00:00
dan
832406c2b4 Merge latest trunk changes into this branch.
FossilOrigin-Name: 72f7807b34eee1fcfd94f72d27535e5273ed1f7ff3d464179a774b7c7241f5f5
2023-08-10 17:03:33 +00:00
drh
d0c8358982 Merge the json_remove() bug fix from trunk into the bedrock branch.
FossilOrigin-Name: a8872aa588dd1367997924ea65267af6bd85e076e00f40fe150e3f33ef6d0dda
2023-08-04 13:29:22 +00:00
drh
7331bc0002 Merge the json_remove() bug fix from trunk into the wal2 branch.
FossilOrigin-Name: e6c066cae4ed5d698820c0c1ea8840773146e9d1a665551d8d51bb9df121cdb7
2023-08-04 13:24:40 +00:00
drh
afdcd07fae Merge the latest trunk enhancements into the bedrock branch.
FossilOrigin-Name: 877ffece0531054f24410c9e4c0b89599a9506689cc97233d33c18b9a15e6308
2023-08-03 23:26:39 +00:00
drh
6f5c4810bc Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 6fa71e11197111ab52d1983b35190899a5881dcb9ed822488c92bc839c72cb55
2023-08-03 23:22:28 +00:00
drh
f715af11ba Merge the latest trunk enhancements into the bedrock branch.
FossilOrigin-Name: 1d6ded3234bbfa3226ef718eec8d0ce06ae2844628866cb6197383c9bf06267c
2023-07-28 18:52:09 +00:00
drh
b9b790e8cc Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: c1fa1cb5e0c420614481636f3ce2dd9ad2142274bb7b37d39e5c26228317f992
2023-07-28 18:47:49 +00:00
drh
1671e05696 Merge the latest trunk enhancements into the bedrock branch.
FossilOrigin-Name: a6f344399472e211941dffa6586a83fc1740c60e807e85387080a88c9c54658d
2023-07-24 13:24:40 +00:00
drh
ecd48447b2 Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: b2e56f62ad8f114126fda6b8c1266d194bb1356b8a302956c5e9ad90d8c1d73b
2023-07-24 13:20:39 +00:00
drh
7ce7f18eb0 Merge the latest trunk enhancements into the bedrock branch.
FossilOrigin-Name: 35bfb5bfee1845c6749256826fdbf4e7b2f7ebe00fdceacb3edebec1c44828c4
2023-07-08 19:16:06 +00:00
drh
f578ed6838 Merge all the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: f07936535f6ad2671e4e3281214b097a0186a642055ad81e561515cd08bc4a61
2023-07-08 19:09:20 +00:00
drh
f3445f4757 Merge the latest trunk enhancements into the bedrock branch.
FossilOrigin-Name: 0551b79b17579a91d891c745ecd0e35b5142f944ed59eb3c95c37e5a4de8a461
2023-06-22 14:40:38 +00:00
drh
b17135d81f Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 80ac494f734d9720fbbb1111741aa99fc319c6c9a32e7f13b5962001d6ace828
2023-06-22 14:20:03 +00:00
dan
d095b2c3d5 Add the sqlite3_commit_status() API. For querying a connection for (a) the ranges of wal/wal2 frames written after a successful commit, and (b) the conflicting frame following a failed commit of a BEGIN CONCURRENT transaction.
FossilOrigin-Name: 4b08d4dad6b254a342353e3f765066c85cbc5450fe13501665c648627cca21cd
2023-06-19 18:16:19 +00:00
drh
e36b69a347 Merge all 3.42.0 release changes into the bedrock branch.
FossilOrigin-Name: 1348c2a59027d6e260d4c7543479093eaf7baccaa3ea61a0d091c44e0dea4b8c
2023-05-16 13:04:38 +00:00
drh
5d2f5f4655 Merge all 3.42.0 release changes into the wal2 branch.
FossilOrigin-Name: 137057f95778b3c913854d2182d0fbbfd9dd117db5566dabb5a22d927a59de62
2023-05-16 13:00:36 +00:00
drh
20035253bd Merge the latest 3.42.0 beta changes into the bedrock branch.
FossilOrigin-Name: d55ba8bb85b1796d534d2db196db63312624630738712054553bd01f2ab30df9
2023-05-13 15:10:42 +00:00
drh
1adff07cb9 Merge the latest 3.42.0 beta from trunk into the wal2 branch. For some reason,
the previous merge didn't work.  Probably a PEBKAC.

FossilOrigin-Name: 3bbfbdcd9b0e03b1ed50f41b1714ada5d241b8030f73ced8d0da8fff28a268e0
2023-05-13 15:07:27 +00:00
drh
5b39dd45e8 Merge the latest 3.42.0 beta changes into the wal2 branch.
FossilOrigin-Name: 771361e19e5b7f944cd2cfecb064b39175b892148fa877b763170744bca0b0cf
2023-05-13 15:00:47 +00:00
dan
bf1305334f Merge latest wal2 changes into this branch.
FossilOrigin-Name: b2e0800b24f8c676e189d63abd77ca45a972de9722ece5de6efe2db6ede75cbd
2023-05-04 14:41:10 +00:00
dan
d17b9114aa Update this branch with latest changes from trunk.
FossilOrigin-Name: 49777032f29517d23c8c7483536f8ba828e7000dc303415da6881cc458620be2
2023-05-04 14:31:17 +00:00
drh
6c9cc2c4f8 Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 0215dcc76d7b3a1dad10b8e6fd8b54f2ab5006a156db5cee992ab2dac98d9284
2023-05-02 19:58:35 +00:00
dan
56151f6526 Fix a problem with the sqlite3sessions_fullchangeset() and sqlite3changebatch_add() APIs on this branch.
FossilOrigin-Name: a95a4bce8892eba3ab6e76a49975a52efeedebfd9af22bb31af30301b1d92328
2023-02-24 19:58:56 +00:00
drh
7fc1cb22d1 Update the bedrock branch to version 3.41.0
FossilOrigin-Name: dc1d85707b130aae2f07b6b1961e0acd42d7a302b3518670f3dbfa96c179386d
2023-02-21 21:22:24 +00:00
drh
ca773a51de Update the wal2 branch to version 3.41.0.
FossilOrigin-Name: 7bb1f6dca2d2e0c0c70f1d2d0c7130c182fc45e48138c57a3fbe92ee1f68da96
2023-02-21 21:19:36 +00:00
drh
0bdbbdabfb Merge the lastest trunk code into the bedrock branch.
FossilOrigin-Name: 73e9f8d0bf78bbbaa3435330e485126bb09f008f35c1d0f366efd5e511f8f4a9
2023-02-18 21:52:25 +00:00
drh
ade7cd53ab Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: a36de0c803ee170b34105fadfc1f9790cda68a921fd5034790397f9853798f43
2023-02-18 21:42:38 +00:00
drh
b4a5f9ccde Fix an pcache memory leak that might occur due to the munmap() enhancement
of [754f331614f6f6e6] if "PRAGMA shrink_memory" is used.

FossilOrigin-Name: f8c1f9c44bb3da7856690ac04c1de1fb8b7e3ccc32141bf414cd85bd90644a30
2023-02-07 16:23:00 +00:00
drh
fbaf259e0e Merge the latest trunk enhancements into the bedrock branch.
FossilOrigin-Name: b7d9f14d78a52f87fcc7bc1d3e9f00d6ce1d648b1b244bc3989d7308f52063a0
2023-02-07 11:39:47 +00:00
dan
e3d452a826 Fix a dropped error code in wal.c.
FossilOrigin-Name: 2537db6f6b9df00913b57e2ad0e4deef2040ce47b28f9a9939f42c0443d70ac9
2023-02-07 11:19:20 +00:00
dan
faff28a478 Fix an assert() in sqlite3WalFindFrame() so that it matches the equivalent assert() on trunk.
FossilOrigin-Name: f5bf91d9bf3910ab3dcf44c138084dfc26fb123edb8934bfdfcdd5c4afb67f2d
2023-02-07 10:58:39 +00:00
drh
70cfee98e5 Disable an invalid assert(). Add the "WAL2" compile-time property so that
test scripts can more easily determine that they are dealing with a
WAL2-capable build.

FossilOrigin-Name: e93113259e05fb6b81ad1b4a460bd54168ff5c372b643d4845c1b1c87b7db204
2023-02-06 19:00:53 +00:00
drh
d94023257b Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 44f8f33d4ab159476800142e763dfc6beb2c2c1d857c8eebb286498dec4004fd
2023-02-06 17:48:49 +00:00
dan
dd0d7126ea Instead of free()ing them, retain allocated page buffers on an internal per-connection reuse-list when a page-cache is reset.
FossilOrigin-Name: 754f331614f6f6e630876bf23c7da84a8408c764540983c0182271268177c00b
2023-02-02 20:27:21 +00:00
dan
d9a58ab810 Merge latest enhancments into this branch from branch wal2.
FossilOrigin-Name: 5a22010c35789c6d12e4dd45c81c10b203f4662f206cb636bd0c2781f1cd9571
2023-02-02 14:27:56 +00:00
dan
1651f418e5 Merge trunk enhancements into this branch.
FossilOrigin-Name: c9b0180203378da26dc958c183ebb59274537e9fee3b346933b1e0844ed68a4a
2023-02-02 14:21:13 +00:00
dan
9271d22909 Fix a startup race condition that could occur if a wal file grows from 0 bytes while a BEGIN CONCURRENT transaction is running.
FossilOrigin-Name: 2c14f3832e0e21c4e68b556d5592860fcd79b6ca21db6c47f52a240b24559a9a
2023-01-12 15:44:48 +00:00
drh
d50f1f4f5b Merge the latest trunk enhancements into the begin-concurrent-pnu-wal2
branch.

FossilOrigin-Name: bcab19d21be42121f1ea3961aedbbe463c60d85d0a3dd8b9593040d217bac3a7
2022-12-21 20:43:53 +00:00
drh
77508c4768 Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 21ebd4cfcf843925cf8eaeb2f7e196713dba116cf618aea9f9f40e31f6aa6653
2022-12-21 20:28:58 +00:00
drh
c524ff6526 Merge the 3.40.0 release into the begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: a090de41be2a727cdb98f02df3d793d94101ca7004c1023c86ae2ad55b5f9c5b
2022-11-16 16:11:07 +00:00
drh
ce8a1720d6 Merge the 3.40.0 release into the wal2 branch.
FossilOrigin-Name: 2df915ef04a64cc8027330013f73ac0ee1fee8cb6eb61f3a608c21ea419d5928
2022-11-16 16:00:43 +00:00
drh
859e8b07dc Merge the 3.40.0 rc1 changes into the begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: b0e2b9aa2c77af8c1e5b35a49f58610c8ac7063a63fd61c34bff6050348e068f
2022-11-14 13:18:39 +00:00
drh
d9da500bd7 Merge the 3.40.0 rc1 changes into the wal2 branch.
FossilOrigin-Name: a5a610a650b6b49f4bb5fbb8e2f43c730afbe88df2c3ae4ddf3d1e5392960387
2022-11-14 13:10:42 +00:00
drh
7bc4ce3412 Merge the latest trunk enhancements into the begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: aa2e247b58dea930c4f0af3566d287f196ea178be8e994fc9eec685a89bebac8
2022-11-04 19:09:41 +00:00
drh
82ac3d8211 Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: ca63a1bee16d30677c20c7576361dfb9a359e6e1b2b2b58a574da0059d3a8822
2022-11-04 18:58:48 +00:00
drh
e62a79d8d9 Merge the latest trunk enhancements into the begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: 68a61513f9e064ed9e79638ec40f8bff1a8ee678793683e2725a4ce63563db6a
2022-09-30 14:04:15 +00:00
drh
73b1ef33ef Merge recent trunk enhancements into the wal2 branch.
FossilOrigin-Name: c22c7c879846b1357c00741700b7f6d1111c4b94895b4e7f552c92299d35712e
2022-09-30 13:54:27 +00:00
drh
12f7d4d0a2 Merge all recent trunk changes into the begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: dde76e91d3cb5aa66fcfab91ab02adfcfcf1eafc1e7e9d3520f4f396d27655d7
2022-08-22 16:08:12 +00:00
drh
b77b56bd52 Merge all recent trunk enhancements into the wal2 branch.
FossilOrigin-Name: 0b7578bf3d389f323df5da2ade9f6455d57db92aacfd219f3136dd31588c3a9c
2022-08-22 15:57:51 +00:00
dan
00a7db72ce Merge in fix for wal2 recovery.
FossilOrigin-Name: 41d4f14bc657f361976c51e1e0d38d2dd2440127fd230f80a1eb8f2a03bc9325
2022-06-27 21:43:33 +00:00
dan
86b7bb29da Fix a problem causing false corruption reports following recovery of a *-wal file that contains one or more transactions and a *-wal2 file that contains a valid header but no valid transactions.
FossilOrigin-Name: f6eafb65a43c650b065abe4a59e329c977cab1856c72dc9162046576d7bbfc8a
2022-06-27 21:42:45 +00:00
drh
acf11ef443 Merge version 3.39.0 into the begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: 09cfef38daa791c2ba114fcf960436322c5acde312234b2c5105c3d95b6c1337
2022-06-25 17:08:25 +00:00
drh
e81feadb41 Merge version 3.39.0 into the wal2 branch.
FossilOrigin-Name: ad3a7005e704711fb9de9e96883db3aed82c4f132a66cb74daf67c2527d79f73
2022-06-25 16:53:36 +00:00
drh
ed84fbf009 Merge the latest changes into the begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: d3fa692179fb2e704e22f1fa0a64152bd0d6d6b531e359fa038eecf909373d97
2022-06-24 15:26:30 +00:00
drh
6c45111957 Merge 3.39.0 release candidate 1 into the wal2 branch.
FossilOrigin-Name: 5a5e6e368f5cc8fe7cf29696a07942101f5c114084d4117acf277bb3ad8555ac
2022-06-24 15:15:39 +00:00
drh
db7a64354a Merge the latest trunk enhancements into the begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: 82e7e836011f52c09196aa6af9519cd9f584881d41dcbcdb23969e863c6e4fb0
2022-06-16 13:56:11 +00:00
drh
029eb1bf15 Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: c8ad869938b06378f49c02655c00ee4f3315e1275d15e69d4ff61d6f60230fe8
2022-06-16 13:44:49 +00:00
drh
fbc308314b Merge the latest trunk enhancements into the begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: 4d65f45b31c239d2a400376b30c486eb53113b0e7b594f2b7592df6027507e20
2022-05-28 14:51:49 +00:00
drh
2a27e5e4cb Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 934656f13dabc41ccf307b10dca7377c758b8a3b93eca57c072745c2786d6b3c
2022-05-28 14:44:19 +00:00
drh
5eedf42dbf Merge the latest trunk enhancements into theh begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: d28af1f8a5da1810d41766074560fc3a1ec8fdc22b1efa59056eae4e5c6b66a4
2022-05-10 12:45:41 +00:00
drh
6702d6dadc Merge recent trunk enhancements into the wal2 branch.
FossilOrigin-Name: 33d77fea4084c5aba9203dfeddb820424f102dcb8347dc59e32b922bdb241382
2022-05-10 12:39:00 +00:00
dan
63da55fb89 Ensure that fts5 closes the read-only blob handle used to read the fts index at the end of each xUpdate operation. This doesn't matter for stock SQLite, as the handle will be closed in the subsequent xRollback or xSync call, but prevents "BEGIN CONCURRENT" transactions from being committed on this branch.
FossilOrigin-Name: 093b60846cbc27175b67132a109d9d98bbc35f00faa9b4677c0aa32f0bd40776
2022-05-09 14:22:54 +00:00
drh
d7f2abf418 Merge the latest trunk changes into the begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: 0071d9da18c639e3865b4d216420415b40ad7fa98cd4288c4e2b98c2c59144a9
2022-03-12 15:11:53 +00:00
drh
6c75609f48 Merge the lates trunk enhancements into the wal2 branch.
FossilOrigin-Name: bafaefc6ab4fa59c0a3859cd16a9a29d3d157953fd9002c96e868fc7a54fa18c
2022-03-12 15:04:21 +00:00
dan
6471011339 Add multi-threaded test for the features on this branch. Fix a misuse of xShmLock().
FossilOrigin-Name: 1ec05168c9fe9c09586da795e9db841647c30825f32a2f2c811186fac57760e1
2022-03-07 18:42:02 +00:00
drh
60845d8a38 Merge version 3.38.0 into the begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: 8387e4f3af552322ed412e21948437bffc4cc237c5c5a80b558b8d395de4999d
2022-02-22 19:56:17 +00:00
drh
981dcb6236 Merge version 3.38.0 into the wal2 branch.
FossilOrigin-Name: de24c8ee41007bc6d25d25856b64d8bdc4581666bca53eb031530b0e498fb0fe
2022-02-22 19:46:57 +00:00
drh
9a5d28c55f Merge the latest trunk enhancements into the begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: 1336d2f23fcee5bc969cd5ca9e3be1458915e79365e21566ca2ba438f6df260b
2022-02-15 20:47:54 +00:00
drh
3f17a238bb Merge recent trunk enhancements into the wal2 branch.
FossilOrigin-Name: 7dba818893fb898e4556bb7ed9cb0dc5667b19a3d7969c7d35c902d2bf5450b1
2022-02-15 20:37:36 +00:00
drh
5794413d8c Merge recent trunk changes into the begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: cb045aba7c7f73ca4ad33f77a1ad0c3196eb4db0b2792a1bbbe079593c4f8f56
2022-02-04 18:02:39 +00:00
drh
5666f09523 Merge trunk enhancements into the wal2 branch.
FossilOrigin-Name: d2de02f372a07b10e21f56fc49755545e7c8c889a9527bf3818e5e65cb33bb80
2022-02-04 17:52:05 +00:00
drh
6ee0c5e255 Merge the latest trunk changes into begin-concurrent-pnu-wal2
FossilOrigin-Name: 3d949775e3029c6bf7cb06946ce52e2dfab451a3f7e685d654807cb5ac750b02
2022-01-25 16:05:35 +00:00
drh
8f1396ae8b Merge the latest trunk changes into the wal2 branch.
FossilOrigin-Name: 84f737abd1c05d66627b601dbd9be42c83d9c8e7a6c7fb4945dd85e7765569d9
2022-01-25 15:57:50 +00:00
drh
77dc2a7ff9 Merge the latest trunk enhancements into the begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: 45fa7efecb8d28dd68b844c9f74b751aab6e7ad7d03f03e8a5881f0e09fb86a2
2022-01-17 19:46:10 +00:00
drh
aed427074b Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 82f031b41dbf041cdbdfb2f3ccc8d118b22d9e523e54a32e882a67535e9973c5
2022-01-17 19:36:10 +00:00
dan
910bbc4892 Merge checkpoint-on-close fix from wal2 branch.
FossilOrigin-Name: a92eca6c9ca45208df5764e50dec2b257244176f5893b4bee7f9475c4e8d8c8b
2021-12-15 13:42:40 +00:00
dan
348093c719 Fix a problem causing one wal file to be deleted without being checkpointed on close in cases where the last connection to close the db does so with an out of date wal-index header in memory.
FossilOrigin-Name: b0a70a2356c44d65c54c6d9bdf05972071462e72c28d6c6e593147ffa3d27ef2
2021-12-15 12:51:02 +00:00
drh
dbe42de14f Merge version 3.37.0 into the begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: 2171996a02ad2ec571570749ccee421bc0859a690c8393c5edda6cf164d2c8db
2021-11-27 15:11:55 +00:00
drh
b90edad19d Merge version 3.37.0 into the wal2 branch.
FossilOrigin-Name: 3425af625bac6c89950a50c2a22c4b7f66b0080811fe16ec8879158420641d5c
2021-11-27 15:00:54 +00:00
drh
012813f3f9 Merge trunk enhancements into the begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: 8a9fad0f71135b0218ccddeabb7db0c41cfac08ec772f8b3b1df7751485bc30a
2021-11-22 17:01:50 +00:00
drh
b321a9b0fa Merge trunk enhancements into the wal2 branch.
FossilOrigin-Name: 135133b7a65c652e95b0642bc2358425af72e46b1b6b64d256547b5e5e8f7e41
2021-11-22 16:47:29 +00:00
drh
3f15be6b72 Merge all recent trunk enhancements into the begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: 07bc13395de3e45fec2cd571f1c9d0776e198768a4a0dd00479347ca8058a003
2021-11-09 15:18:21 +00:00
drh
234f384386 Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 0894f59569378c41207b6cef43dbff9a4873582709d954c809c18f42d918aa03
2021-11-09 13:52:20 +00:00
dan
7271df982d Merge further changes from trunk into this branch.
FossilOrigin-Name: 700876b79c47017cfbf42b8710ec0b50d7a987864db7599593817b962763bc71
2021-11-06 16:46:27 +00:00
dan
f54bc90ca3 Merge change [6979efbf07d93e7a] from trunk to this branch.
FossilOrigin-Name: 2bb2448d6042b8c1597aab53b2c1c1aa0cdf9b36f15ef5c44730558f213297da
2021-11-06 16:36:30 +00:00
dan
fa4d43fb64 Merge changes from trunk into this branch.
FossilOrigin-Name: 52667bce485354ee4fee87f19015845baef12adf2674127f8c6f1bac1ccf3b7d
2021-11-06 16:10:10 +00:00
drh
c34c456d82 Merge trunk enhancements into the begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: 1958e6facaaca8e695ae3d7e79acdba0025d6221653397ea45f9b8daa56c8d9b
2021-10-21 14:24:24 +00:00
drh
c77906eedd Merge recent trunk enhancements into the wal2 branch.
FossilOrigin-Name: 7e2bc836f6aedfd69588f5723f8797f11ee6437d3b63ffc43c88d40e3baadb1c
2021-10-21 14:01:05 +00:00
drh
23cd2fc7cf Merge recent trunk changes into the begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: bce02eaa0c379d9b90bfaa766fd24d7dd2f009f840ee18cc4f2c7d7b6f7aa368
2021-10-04 12:02:17 +00:00
drh
e1c2be1134 Merge recent trunk enhancements into the wal2 branch.
FossilOrigin-Name: 127173e0baba8839d5922a2537dc42e6bb64ed1499b0700bf3e95d42c2f260b3
2021-10-04 11:44:28 +00:00
drh
c8ff95147d Merge recent trunk enhancements into the begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: 47a9b75981e7e2d2eeb14f83f994c51d2c70d2f5537c02f6028896dd7b4d62ed
2021-08-09 18:35:52 +00:00
drh
2303f0859c Merge recent trunk enhancements into the wal2 branch.
FossilOrigin-Name: 84dac820a0b1f2af73f9563a137b2c57c4c13debcc864063c39027dc8de95ca6
2021-08-09 18:26:05 +00:00
dan
938ede2078 Merge latest trunk changes into this branch.
FossilOrigin-Name: 67ed7422f8173f95598a6b32447b9200ec369cadae71942c3e87c41d492cfc28
2021-08-09 11:03:15 +00:00
drh
8c8b9357e4 Merge recent trunk changes into the begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: 5b6f6514bd3dcfb45c0ac9a2c4f806fa2f2e8bd7ee62f766bdc218246b4d9e22
2021-08-03 16:44:57 +00:00
drh
0f1e95e616 Merge recent trunk enhancements into the wal2 branch.
FossilOrigin-Name: f615d7baa889030b230e0d4f018dc9f8a6fe95cd3a671d7e2b941ae3240fa6ca
2021-08-02 20:29:08 +00:00
drh
d59c189930 Merge recent trunk changes into branch-concurrent-pnu-wal2 via that wal2 branch.
FossilOrigin-Name: 2ff7e5624924d95b280024f67237b8be2afa1593e3ade1da6e9e68c83a21d2ea
2021-06-17 17:44:48 +00:00
drh
ebc672832f Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 41363fb970273cb5ae2bddf53f3536de047d7d22736483089fd89493c0c701ca
2021-06-17 16:53:56 +00:00
dan
b1ce26122f Update this branch with latest changes from wal2.
FossilOrigin-Name: 0ab899012cef8f32e1a4c9a47c54b73576f7fe3757e97353ccbc7fc87a4a1ea9
2021-06-14 14:25:22 +00:00
dan
67f562233f Update this branch with latest changes from trunk.
FossilOrigin-Name: b38c2b8bc4237ee2167cb5e3ec63d08f10a8718654b2210a4bc04cca6d2adfdb
2021-06-14 14:12:57 +00:00
dan
ef319309ea Merge latest changes from wal2 into this branch.
FossilOrigin-Name: 4efefb5938c1ec83ee367ae5cc4bba906729b4e8699fa81adc6ca3618c73eb03
2021-05-21 17:31:04 +00:00
drh
80189c5bfe Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 95cc7783457b39bbe0866295648a86e7ee5e1660048ab133f1ccf300498ef1bf
2021-05-21 16:42:05 +00:00
drh
b2dcfcad79 Merge up all the 3.35.3 changes into the begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: 4d425e641d3c45692904018aa8fcff678836b45509e640e67fa2d1ade625a746
2021-03-26 14:04:34 +00:00
drh
17e08c6149 Merge the 3.35.3 enhancements into the wal2 branch.
FossilOrigin-Name: a1b8d9d2131830510df291832565311803aa3e047fe07ba979fc69bb0ff329b0
2021-03-26 13:58:26 +00:00
drh
43cc438ef0 Merge the 3.35.0 release into the begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: 939b9e75307de5067a11351dd4010046a5400e8126c5c2bafea4fbea97e00f86
2021-03-12 17:18:13 +00:00
drh
e809deee13 Merge the 3.35.0 release into the wal2 branch.
FossilOrigin-Name: 03142e5f760ef46c7837fef1b21f54baf64f4b9d99d1c9e3f4d8d6c8d1ab875f
2021-03-12 17:08:43 +00:00
dan
7c4df3349d Merge latest wal2 changes into this branch.
FossilOrigin-Name: 88c3c173e04cb01a07a323242872f5d1472d45c726a90ea90386b08e9fd82f57
2021-03-11 20:07:07 +00:00
dan
226fda81af Merge latest trunk changes into this branch.
FossilOrigin-Name: 30f912d0b39ff6fcf9a555affb5990c4bce8726fbd27062cd0c95851724f91d4
2021-03-11 19:42:05 +00:00
drh
1641e35ac4 Merge in recent enhancements.
FossilOrigin-Name: b0fc8ca14ec8ed3a42bf647b112d5fba640f2adc948485da795fc802b74b3106
2021-03-03 19:41:15 +00:00
drh
c25b39e41d Merge recent trunk enhancements into the wal2 branch.
FossilOrigin-Name: 0ae81039083dbeb67cd4da9f56cdcb31fd747ba258179a46781a12b2872e9f49
2021-03-03 19:32:30 +00:00
dan
74154a7904 Merge latest changes from wal2 branch.
FossilOrigin-Name: b451bb724ea73367a27c51fcf6e48d02d774c7c5b5b8ec0bd3726a6e4689e591
2020-11-26 18:41:08 +00:00
dan
1a2a4f12dc Merge latest trunk changes into this branch.
FossilOrigin-Name: cb1f6f18e74aeff522c332cb6c8f4c202bba8fba4ff7869198e4501ffa4a0387
2020-11-26 18:08:59 +00:00
dan
50c9522c24 Declare a variable "const" in order to avoid compiler warnings.
FossilOrigin-Name: 4f5481bf291c39e228dd9c67f504dfe70879617b7266245d6236cd1d2d99274c
2020-11-20 11:17:48 +00:00
dan
9bf3da81a0 Fix things so that sqlite3_database_file_object() works with wal2 filenames.
FossilOrigin-Name: c2426ae8a80d61e1ede2d065c10145ba31d4ac615803f48b7903f5f7daaa6f89
2020-11-19 16:08:08 +00:00
drh
1d80c33599 Merge all the latest changes into the begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: ebefcb8607c91d19ee68935b01fc9c237af41df8b866a9372f7940d89ba44c9c
2020-08-11 13:22:22 +00:00
dan
174f4fa241 Merge latest trunk changes into this branch.
FossilOrigin-Name: 91262e665755a1430f3b667867b9c4fa0cc3aa0388c21e8ba436485beefa9f4a
2020-08-11 11:34:00 +00:00
dan
57f7daf312 Merge latest wal2 changes into this branch.
FossilOrigin-Name: ebd39665e5787fa2c35941ab6c6019f95f0c01229076e3859a3349b516183985
2020-07-31 17:08:40 +00:00
dan
aae84d814c Merge latest trunk changes into this branch.
FossilOrigin-Name: 0c0d0a77bc8fa68ee584ca6d7a0408dcd00734a9b6b69d127ee8477c230a749c
2020-07-31 16:01:33 +00:00
dan
d05cc8f8b7 Allow BEGIN CONCURRENT transactions to contain schema modifications. However, this causes them to conflict with all other transactions.
FossilOrigin-Name: 2160ede1d12591275c3d82026be7775fd6c890435ab6ca83600029e96ca381a8
2020-07-16 20:57:35 +00:00
drh
9ee441a3a5 Merge version 3.32.1 into the begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: 0f9464c509cb99906e2e9df3b55087bebb89d678af1736b431035d6048fface5
2020-05-25 18:18:42 +00:00
drh
ea8c9a43aa Merge version 3.32.1 into the wal2 branch.
FossilOrigin-Name: 5c1837572586902313702c46057ef5dc84030683ff3467c6f3f25903b6ab22d1
2020-05-25 18:09:15 +00:00
drh
70311ddd1b Merge version 3.32.0 into the begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: 3cb296065aba04d01cbaa0b3b17b033cd20d8e84782466e8452bdffa8cee693e
2020-05-22 18:35:37 +00:00
drh
ca97a18493 Merge version 3.32.0 into the wal2 branch.
FossilOrigin-Name: 1cb46a7431797978a37e1c6ed77f6473eb44f13d15cd37267d4f9555ac825f53
2020-05-22 18:28:59 +00:00
dan
9019e39d0d Merge latest trunk changes into this branch.
FossilOrigin-Name: 5dc355ccf6ecf0af61e73efbf65bc5e1e192f748568344afbf8bca014ef98df9
2020-05-18 19:55:43 +00:00
dan
69105498b9 Merge latest trunk changes into this branch.
FossilOrigin-Name: 16b1eca922d6e10a0ebce1deb559c6c7dd74f8655e3456bad8384c6f4637e6d6
2020-05-18 17:18:28 +00:00
drh
bf92e44f43 Merge recent trunk changes into begin-concurrent-pnu-wal2.
FossilOrigin-Name: 6c11b67bd2c2844566027d59bf5a342d8b2bebd591cd06922e4609e79aa46b82
2020-04-09 19:00:09 +00:00
drh
bd74861e91 Merge recent trunk enhancements into the wal2 branch.
FossilOrigin-Name: 6fb870625cb7007fe5055609da35f6af87f3b375b7a43fd4e842d40dfef9169f
2020-04-09 18:46:15 +00:00
dan
526161d3dc Merge latest trunk/wal2 changes, including the SQLITE_ENABLE_CORRUPT_PGNO patch, with this branch.
FossilOrigin-Name: 76668b55894a9c75b2ed1f404ecd3c93ef22586bc20a86ac1ab23f20828474cc
2020-02-04 20:22:32 +00:00
dan
6d671d4c09 Merge latest trunk changes into this branch.
FossilOrigin-Name: 6ad4d6b7ff2b34385039490f1dc8b020254e06fb70b9bff69d453d0c0affc2a2
2020-02-04 20:11:22 +00:00
drh
74b950f145 Merge version 3.31.0
FossilOrigin-Name: 9c3f46384dc73a79f1b04cd5eed8e6a279fcb4387d1dbc20b912ee5aad180140
2020-01-22 21:18:33 +00:00
drh
8caab4aebe Merge version 3.31.0
FossilOrigin-Name: 3bb267deb150c09bc424320d1de4bbaf519310e0e0c4af6a8ac5b6f8d59170b2
2020-01-22 21:12:27 +00:00
drh
7ee2126844 Merge in the latest trunk changes via the begin-concurrent-pnu branch.
FossilOrigin-Name: 350627f3b12a94723b7d52cae8a53d07917ff037c4be9312ef7e4fd2e845b9cc
2020-01-15 14:21:02 +00:00
drh
b3168a0056 Merge recent enhancements and fixes from trunk.
FossilOrigin-Name: 35eae71a4dd4bd4e2d4a8f12c5e4a0cdb93dadee353b92e67a70a79b29587984
2020-01-15 14:11:34 +00:00
drh
b9bb4e7245 Bring the begin-concurrent-pnu-wal2 branch up-to-date with 3.30.0.
FossilOrigin-Name: dd09f7ce970e8b139d105d005a4c99ae0766c1cba6e0794d46bd6879570c33bc
2019-10-04 16:24:47 +00:00
drh
2c34d3c61e Bring the wal2 branch up to date with version 3.30.0
FossilOrigin-Name: 16e1dced8b56ef422e5b747dd26accba5bf9f2df69b24b10363ef288890e21ee
2019-10-04 16:15:45 +00:00
drh
c91f60a056 Merge in the 3.30.0 beta 1 enhancements.
FossilOrigin-Name: ebf8872626e71f5fbaefb76e95d218dfa123d15134762c810d6a5f442325154e
2019-09-30 16:49:59 +00:00
drh
d59a566707 Merge 3.30.0 beta 1 changes from trunk.
FossilOrigin-Name: 918bd97d2946c0a403030fcf0eba596a742ada94b122bf0ac4b808097171056b
2019-09-30 16:44:00 +00:00
drh
80c72a78bb Bring the begin-concurrent-pnu-wal2 branch up to date with all the latest
enhancements on trunk.

FossilOrigin-Name: 15593bf3ad9650ce6a6ef8591240eba1be11974d3827cb86ebf0ecec3acda321
2019-09-16 12:29:47 +00:00
drh
254bcdd02d Merge recent trunk enhancements into the wal2 branch.
FossilOrigin-Name: da8bc974126729f1558f3c18118a32efcc5bdd9611533c34090f7e9e5176b4e6
2019-09-16 12:23:30 +00:00
drh
69a35f9232 Merge the 3.29.0 updates into the begin-concurrent-pnu-wal2 branch.
FossilOrigin-Name: 627b428fc8ca0f233cb3ac82a43fc81a22cf81ea958e6b7f69d93a0e53e05d09
2019-07-10 18:40:29 +00:00
drh
e31602c62d Merge the 3.29.0 release into the wal2 branch.
FossilOrigin-Name: 8baa402282a231d42f419ac220a32b532766e52180e4a70da874d383d8703121
2019-07-10 18:30:00 +00:00
drh
2d3b3fcb66 Merge enhancments from wal2, which also merges trunk enhancements.
FossilOrigin-Name: 08dcb4593d32b240b585b3fb72fec835c8ad94d9e9c00253e70bbb1b4fc28c94
2019-07-08 21:25:24 +00:00
drh
1e469f79b1 Merge all enhancements from trunk into the wal2 branch.
FossilOrigin-Name: dcbcbc2a6905588a63b9c926473a7097ca0a03803c5bd3f416f268ae2aa3dc87
2019-07-08 21:19:34 +00:00
drh
25b9a24ad7 Merge the latest trunk and wal2 enhancements.
FossilOrigin-Name: 3deaa6e23b2e7f90f91f48435a9a7bfa124b488cd8498d57d8c71a9419cee5db
2019-05-14 22:07:36 +00:00
drh
6fdd8fc5f0 Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 23ec34e4f61775198a2b6f8b683fa7db2c57efb1add32c12d5051558952c394c
2019-05-14 22:02:03 +00:00
drh
655af9d7b6 Merge all recent enhancements.
FossilOrigin-Name: 1cd536ecc8f21898475d4641fd67da15a8007af78d59a1f418143b348aceeaad
2019-04-15 15:28:58 +00:00
drh
e5fd180648 Merge the latest trunk enhancements.
FossilOrigin-Name: 8950f1195a5e5a67202192857fda50c359a446d20264eab328209239ae0a2d95
2019-04-15 15:17:40 +00:00
drh
76f79406ff Bring this branch up to date with all the latest enhancements.
FossilOrigin-Name: 2e964aafc2773bf6003c9e1d879d6b7467651d5a4592e8371fdc7f393d6485a9
2019-03-26 12:29:24 +00:00
drh
b83e6e9420 Merge enhancements from trunk.
FossilOrigin-Name: a0e2e90206fe09cdacc01fce93740fd515c88ed06b03c78c48076b4cb177349c
2019-03-26 12:22:30 +00:00
drh
b0120de573 Merge the latest enhancements and fixes from trunk.
FossilOrigin-Name: 543ecb30d67bdf7639ea5d57d091e8c0981d4b5042bb6f9cedd1264bbcbef0de
2019-03-08 16:06:26 +00:00
drh
d9df7cb876 Merge the latest enhancements and fixes from trunk.
FossilOrigin-Name: bf20d4ffcb7b27b519502e2986cd0eb1d34d5fd273752fb27de9cdab75772acd
2019-03-08 15:57:31 +00:00
drh
281ee2f754 Add the bgckpt.c extension to Makefile.in and Makefile.msc.
FossilOrigin-Name: 3712d625d625f962c627c74222467ad4858be46feda570449f9cb640549c9ddd
2019-03-08 15:39:24 +00:00
dan
d65b8528a5 Merge documentation changes from branch begin-concurrent-wal2 into this branch.
FossilOrigin-Name: cf8a0c71cf1032bd4271cf889304def0fab82a76eac255d734652008fff6c9b6
2019-01-11 15:26:13 +00:00
dan
d55e8f5ff0 Merge documentation changes from branch "begin-concurrent" into this branch.
FossilOrigin-Name: 41e742bd0f94894b096b30474fb548f58951756d1ea47d0afb717a54802888f4
2019-01-11 15:22:27 +00:00
dan
77428e6055 Add new documentation file begin_concurrent.md.
FossilOrigin-Name: fdbf97e611ed23f86dc0df9d53421fb04226af0d1c7171d55c11acd0dbe935f0
2019-01-11 15:06:39 +00:00
dan
22672ad742 Merge latest begin-concurrent-wal2 changes (documentation only).
FossilOrigin-Name: d0ab56a3be29d112c792efef8ae7ceacc43fa5773fe06bdc0e45bf9147f84bb4
2019-01-11 15:00:05 +00:00
dan
4853110e61 Merge latest wal2 changes (documentation only) into this branch.
FossilOrigin-Name: 820ba1cc7e374409f27d0f1e62ed17c9670ce0e1f6e1d39a02fa8d48319745e8
2019-01-11 14:59:24 +00:00
dan
f552a23740 Add new file doc/wal2.md to this branch.
FossilOrigin-Name: b495dce153f7f886f3dba09593f29ce2375718bf6508f2cfffd1af8071a995ae
2019-01-11 14:58:03 +00:00
dan
3dfff8dd8b Fix the experimental sqlite3_wal_info() API so that it works with wal2 mode.
FossilOrigin-Name: 079daedbe440e712dc944aeef831ef6531d44096da406d52ce0d85a8a49618c5
2019-01-04 19:39:04 +00:00
dan
052416f01f Add the "-external" option to the tserver_test.tcl program..
FossilOrigin-Name: ae7c5ed15d5d549326bf570c8f03b49e8e601e7389bf8d702d0d7db0c0570ee8
2019-01-04 19:25:22 +00:00
dan
a45691267f Update the "tserver" program to report the amount of time spent in COMMIT
commands.

FossilOrigin-Name: cf494b412c941704ff73b0d100c55eb9bd268ee156db88b3e60512d501689e8e
2019-01-04 17:12:28 +00:00
dan
5a19f287dc Merge changes from begin-concurrent-wal2. Also modify the unix-excl shm-locks
on this branch so that it is not an error to attempt to release an EXCLUSIVE
lock that is not held.

FossilOrigin-Name: 86685679b4cff94704fe4b52364f25f579d98ee725407cfe3bd2bbf7ecd651a8
2019-01-02 19:46:46 +00:00
dan
b93ce6d4a6 Reinstate assert() statements in os_unix.c that were removed to allow
wal-mode SHARED locks to be taken over more than one locking slot
(this branch no longer does that, so the assert() statements can go
back in).

FossilOrigin-Name: fcb7348ba190e48112a681f704313a733f7a377456e7c1d6845ba34c270d86b6
2019-01-02 17:08:06 +00:00
dan
3eeda95d36 Reinstate assert() statements in os_unix.c that were removed to allow wal-mode
SHARED locks to be taken over more than one locking slot (this branch no
longer does that, so the assert() statements can go back in).

FossilOrigin-Name: 8445fb6d5dae98a0129514a10a15f08412bac5a1d8114b12e592c744ea2bc4c9
2019-01-02 17:00:00 +00:00
dan
2cdce9c105 Merge latest wal2 changes into this branch.
FossilOrigin-Name: c31eeb3c87f260fa192638b3610a4d3b84b2c26cfaefc97ed63910495403f8c9
2019-01-02 16:30:10 +00:00
dan
1f3762607d Merge latest trunk changes into this branch.
FossilOrigin-Name: 87ef68f9174b5e21fa2fb4f2fa23e0ecd6a0ea854422c8aa74cd7306a1ed8427
2019-01-02 16:15:02 +00:00
dan
2da2a2698b Merge latest trunk changes into this branch.
FossilOrigin-Name: 5bf212f1a754fde90af0af82dd76f636754829c7372e19df1fd4fa579f0926aa
2019-01-02 16:08:14 +00:00
dan
d080e84c81 Merge latest begin-concurrent changes with this branch.
FossilOrigin-Name: 1625887c0240eb1cb37e41d79e789c4ff311352c5708e6a2d3ed1b223ff382e3
2018-12-29 20:47:45 +00:00
dan
ff9e9b27d5 Fix a problem causing a corrupt pager-cache if an OOM or IO error was
encountered while committing a concurrent transacation.

FossilOrigin-Name: 48ca30f9d7817d87a5e9a069fdc51b1a34e00585f8a35771895dd743c7bfe07c
2018-12-29 20:42:12 +00:00
dan
71fad44b38 Increase coverage provided by permutation "coverage-wal" on this branch.
FossilOrigin-Name: f664f940a7eb6938b1ee171143a691e2f497aec921f979be63ef844d38053f56
2018-12-29 16:34:22 +00:00
dan
d445ed094a Add new test file wal2rollback.test to this branch.
FossilOrigin-Name: 85a376fc6c77117b96814df800b3d68a441d69602ffc2eb8cd7dba29f25d69aa
2018-12-28 16:20:53 +00:00
dan
30db882a87 Merge latest wal2 changes with this branch.
FossilOrigin-Name: ea96001e801fbf228d1fb63b9d3c73fd9da54994d3cfe383ea76edfa6da83b8d
2018-12-27 17:11:59 +00:00
dan
ce909d1aae Increase test coverage of wal.c provided by permutation "coverage-wal" on this
branch.

FossilOrigin-Name: 2f7f893a702728745445f6ef5f914c1157058a8fbdfd1a58cfb8906e5566729d
2018-12-27 16:49:33 +00:00
dan
0f297b6e50 Improve testing of the wal.c changes on this branch.
FossilOrigin-Name: 63483e22c775183e01924b5cb3d0f3655b63c3cdd06faf26cacd4d0913c0055c
2018-12-26 20:42:32 +00:00
dan
2d111c8bbb Merge latest trunk changes with this branch.
FossilOrigin-Name: 404f9d99d325ddf82fde6d957860d30b2fb67f232c0878a3cf6df9596541f16e
2018-12-26 19:10:59 +00:00
dan
665f3d209b Enhance the wal2 header comment in wal.c to explain how the wal-hook is
invoked in wal2 mode.

FossilOrigin-Name: 118aa7e32a94ad971a955ab60db5bfc5b6241f93734a41ba83ab72ea71bc9aaa
2018-12-22 15:50:00 +00:00
dan
25117ddbfc Add the "mutexfree-shmlock" patch to this branch.
FossilOrigin-Name: c3191fc8fcb0775f87f55cb8124e0c36caa54993c2a5fe770d437e4f6ee72d77
2018-12-18 19:46:50 +00:00
dan
49967a4d9a Fix a test script problem in wal2savepoint.test.
FossilOrigin-Name: e388d02940e8c17e8e3f460643d988c8f523382df343f4fe766c2da17878e98d
2018-12-18 18:16:00 +00:00
dan
0ba73a6e4d Merge the wal2 feature into this branch.
FossilOrigin-Name: a2c618c0a45e1b1d271a4a981ee1e3b368dfcce94eda5c2d7d08f794784046d9
2018-12-18 18:01:17 +00:00
dan
db6fe08914 Merge latest begin-concurrent changes into this branch.
FossilOrigin-Name: a93ca38b432e0c9fb2e00499cfc88d09f59f27f908f377d8ae99b6717e548a60
2018-12-18 17:47:53 +00:00
dan
e7f92d27ad Merge latest trunk changes into this branch.
FossilOrigin-Name: b3a163b46cad19a2e6a90cdd5b4c2e94d66e80b7ad2a0c9857580fe74748a604
2018-12-18 17:32:08 +00:00
dan
b031157128 Merge latest trunk changes into this branch.
FossilOrigin-Name: 123cbb3312917ad5b3c32556547c5b7e8ba4e2d2def8651ff80f9fc1bdc1875c
2018-12-18 17:20:01 +00:00
dan
8b832e24d4 Add the ".integrity_check" command to tserver.
FossilOrigin-Name: fa46fa3bfcd4b6d1d219db4179ce69d0edb0786a521acaa034fdb74b312274ff
2018-12-18 16:24:21 +00:00
dan
8348abc3a2 Add wal2 related tests to this branch.
FossilOrigin-Name: 5645822039b9f36aeb986645106cac2d95b09964689deb767cadaa6ea1d1867f
2018-12-17 18:26:48 +00:00
dan
1ac9aaf3fc Merge changes from wal2 branch.
FossilOrigin-Name: 85f49f0d7344611ac4157421273660c648f73ca62afa4faa8707475f21c67f7a
2018-12-17 15:22:44 +00:00
dan
c5140b39ce Add extra test case to wal2recover.test.
FossilOrigin-Name: 1a5aa73435b11fff0567672dc921d34aad57adf0381ad430e1389f39a6f025b7
2018-12-17 15:22:24 +00:00
dan
6d537ca2d8 Merge latest wal2 changes into this branch.
FossilOrigin-Name: 06bb80eeb84f57b1e8109a35f5b14992a2e23f2bf5a8921fa29087f96cb44d10
2018-12-15 20:59:14 +00:00
dan
93543215e0 Further test cases for wal2 mode.
FossilOrigin-Name: 9cb5f8dab685f5ea36ad142cfa588dee82e87f3a89e8dcf84e0ee124bb29bc7f
2018-12-15 20:20:13 +00:00
dan
e698c1f79e Add further tests for wal2 mode.
FossilOrigin-Name: 54e628f902a5508badc8941ceda1bec12fe8f58969c91c670a37888ef2681880
2018-12-14 19:53:39 +00:00
dan
597bf19266 Add tests cases for recovery in wal2 mode.
FossilOrigin-Name: 34f56f8a4239781071edf6317723d0a7333249551c2c1ce7ed39fc116d53d780
2018-12-13 16:26:43 +00:00
dan
eeb778d506 Add new test file wal2big.test.
FossilOrigin-Name: e3e50bcdab8c91e003942d84430b3e580e034141236d19dda0e8af4ecf0e085b
2018-12-12 20:39:38 +00:00
dan
2c4328ed87 Add tests to ensure that each of the 4 wal read-locks does what it is supposed to.
FossilOrigin-Name: 4d5779f31d4931edb8bb8952d8886625ead5e51f0c308e5763e519427f6609e1
2018-12-12 19:04:19 +00:00
dan
f17e510ee5 Change the way wal2 locks work to ensure a reader only ever has to lock a
single slot.

FossilOrigin-Name: 18b2c23ac53d985ccc5798ea2d92fb75644b857c373fb490e0d04d5d0194a3d5
2018-12-11 17:56:23 +00:00
dan
ca72570a11 Merge latest trunk changes into this branch.
FossilOrigin-Name: d8dd98a39ea061dea264a7d81153f7b1be2f81b554b30d0ce289897c802209bd
2018-12-11 13:44:59 +00:00
dan
107c5de8b2 Make SQLITE_MFS_NSHARD a compile time setting.
FossilOrigin-Name: b9a741519055a8560ec1d0ab7d0c15ff8db53e740011825506cd2ede11c956f0
2018-12-10 16:52:31 +00:00
dan
1d5430f85e Add extra tests to shmlock.test (direct testing of xShmLock methods).
FossilOrigin-Name: d2c785f94cc6b704ce29147399e2c473fd0f4ff377f1379bd29178bf6d127c1a
2018-12-10 15:51:05 +00:00
dan
c967a11569 Rework the SQLITE_MUTEXFREE_SHMLOCK code to reduce contention.
FossilOrigin-Name: d9157dd176a2d18c6e02a2a0c7e16cef2da43bf44be9765e0363f34aebad23e9
2018-12-10 15:24:29 +00:00
dan
5b5cda2027 Avoid a mutex in-and-out in unixShmBarrier() on this branch. Use
__sync_synchronize() instead.

FossilOrigin-Name: a8c5fd86ce1d9b5815f82106f2e44797b743bbb3c44aee5f835ce8d278caf8f7
2018-12-10 09:45:38 +00:00
dan
b0b27ab5c6 Add multi-threaded performance test program "tserver" to this branch. Fix bugs
in the begin-concurrent/wal2 integration revealed by the same.

FossilOrigin-Name: 7bd3b35661d7d0e51113b9e4b15a0ab7f8e26edeafb43941ef5b44bb94df5109
2018-12-07 20:25:14 +00:00
dan
24fb9ffbc1 Experiment with using an atomic CPU primitive instead of a mutex for
intra-process locking with the unix-excl VFS.

FossilOrigin-Name: 8f4cb9dd3396bcaaf85dcdb4e3ae3c96f28a4d71d72665c4abf7c221370be626
2018-12-06 18:58:58 +00:00
dan
d597b966d9 Add test file wal2snapshot.test that should have been added as part of an
earlier commit.

FossilOrigin-Name: f6baa7e1163ed5f61375b0554337030fec23e8a9f60c6412e1b5d626961e93f9
2018-12-06 16:54:44 +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
dan
7b812b6b16 Add test cases to wal2concurrent.test.
FossilOrigin-Name: ef9e7a87a4ce4fe9596aed2f880f2b9fc3526c00c48f2868c625a4c24be65abe
2018-12-05 18:25:23 +00:00
dan
ff46e3f460 Fix a test script problem on this branch.
FossilOrigin-Name: 692ddc280850ea2ff5d2fa3743f85fb123b4e4ebc452aae9c58b8d80d22b75b1
2018-12-05 17:36:20 +00:00
dan
c30d437376 Fix a test script problem on this branch.
FossilOrigin-Name: 285d1c5904dd607457c6b5ff7be3d3191a90ca8b1fb0837327663c9910f978ac
2018-12-05 17:31:24 +00:00
dan
079ae546fe Fix a problem causing "PRAGMA journal_mode" to report the wrong journal mode
(wal instead of wal2) under some circumstances.

FossilOrigin-Name: bf309107dfc4d2abd68b9339c68aeaaffd278096bb2a5912e4a0b0c124fd6345
2018-12-05 17:20:47 +00:00
dan
e05f922583 Fix a problem causing "PRAGMA journal_mode" to report the wrong journal mode
(wal instead of wal2) under some circumstances.

FossilOrigin-Name: 1d8d4f689653ce80157740e339f7f1b42479bf90d82b176d8202d0a49f428398
2018-12-05 17:14:03 +00:00
dan
5330a3c6c9 Fixes for snapshots API on this branch. Also ensure that the snapshots API
cannot be used with wal2 mode databases (for now anyhow).

FossilOrigin-Name: d8c2d55fa4ec95c6506201edcd1cb2ef108a233bd87a6154f3593909977f7d3f
2018-12-05 16:45:38 +00:00
dan
6456b3929c Fixes for snapshots API on this branch. Also ensure that the snapshots API
cannot be used with wal2 mode databases (for now anyhow).

FossilOrigin-Name: 19c61ab79458936ff4dfca46cf4d1fb1ab16d7bdb5024f502eb4339ec4eef32c
2018-12-05 16:31:02 +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
7a13e692be First attempt at making features work together. Only the most minimal testing
so far.

FossilOrigin-Name: fd707001f0afb1cf32cfeeda3ec7b5622eb49ddedf8fec1a7aa4c8841c77c37a
2018-12-04 19:41:07 +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
4d8517e500 Merge the wal2 and begin-concurrent code. Both features work, but not at the
same time.

FossilOrigin-Name: b7281a1caa574870a071bea3e96b1d8210c28c17f9094449b3ce1a42b311e6a1
2018-12-03 20:49:34 +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
816fc35956 Increase a timeout in test file walprotocol2.test. To account for unix builds
without HAVE_USLEEP.

FossilOrigin-Name: 480be916c840e9e28010c22cf29a8396502c9e6387f31f750260c6290f58e9a1
2018-12-03 18:13:46 +00:00
dan
8eed108df1 Sync this branch with the latest trunk.
FossilOrigin-Name: 7a44fa5a350a3f19b8e9f5196d22535788885f8c0e849572202bf64a055ddc2d
2018-12-01 20:14:06 +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
6cbf8e173f Add new extension "bgckpt" in ext/misc/bgckpt.c. For experimenting with
running wal2 mode checkpoints in a background thread.

FossilOrigin-Name: 63955442304052f5adddd05ccaeebe87ddc5e25af695702793518f015b4f0a87
2017-10-10 20:11:10 +00:00
dan
2df2b3d05f Merge latest trunk changes with this branch.
FossilOrigin-Name: d218d815f89cb1368fdb5e3f774b7adaaf02560a367ba0f3e54987e08dd6241a
2017-10-09 19:50:09 +00:00
dan
40927fd61e Add a header comment to wal.c describing the differences between wal and wal2
mode.

FossilOrigin-Name: 9c80cd202f1c966929b279e18f19c663912686fcf92f03b85a02b9c7e55a0fc6
2017-10-09 19:49:08 +00:00
dan
50182fa846 Ignore the *-wal2 file if the *-wal file is zero bytes in size.
FossilOrigin-Name: f7360fad51f224f347bb7d263eb89056b27461c278309e00e575a0e8898c9f40
2017-10-07 19:55:37 +00:00
dan
5d122d308f Check in test file wal2simple.test.
FossilOrigin-Name: 8932b2f1d7e6a26221ea3dea01000832b2d1eb17ac0b70ef6028f9286ae450a3
2017-10-07 13:37:04 +00:00
dan
288e98fb4b Merge latest trunk changes into this branch.
FossilOrigin-Name: 7e43517861d4ecfa86766a16a2c721377b75da78771d5ba18870dcb9626a8dce
2017-10-06 14:25:25 +00:00
dan
bfcf8d6d67 Fix a bug in recovering wal2 mode databases introduced by the previous commit.
FossilOrigin-Name: 9e1502e1b650217efc361732a3dfe686caa2e6352d040c73865f1faf09bf4591
2017-10-06 14:08:39 +00:00
dan
24f1b25c7b Fix frame overwriting in wal2 mode.
FossilOrigin-Name: a4b02bc9388226da21b3837a20c6c7eb0d13854dde62b7136e04f4978528dc71
2017-10-06 13:43:42 +00:00
dan
3400e78f38 Fix test case failures on this branch.
FossilOrigin-Name: 16decc13af908087fb8aa34eeccf43e8da1b8f2e4b808028986d1ef08134c72c
2017-10-05 18:14:46 +00:00
dan
055cc1e431 Add experimental mode that uses two wal files. Activated using "PRAGMA
journal_mode = wal2".

FossilOrigin-Name: e2fc5c814cf6862d536aacb9eca66ecd31ba7e3e3033fa4c5564d533f4a18dfc
2017-10-04 20:57:14 +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
107 changed files with 13388 additions and 1442 deletions

View File

@ -420,6 +420,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/recover/sqlite3recover.c \
$(TOP)/ext/recover/dbdata.c \
$(TOP)/ext/recover/test_recover.c \
@ -433,6 +434,7 @@ TESTSRC += \
$(TOP)/ext/expert/sqlite3expert.c \
$(TOP)/ext/expert/test_expert.c \
$(TOP)/ext/misc/amatch.c \
$(TOP)/ext/misc/bgckpt.c \
$(TOP)/ext/misc/appendvfs.c \
$(TOP)/ext/misc/basexx.c \
$(TOP)/ext/misc/carray.c \

View File

@ -1581,6 +1581,7 @@ TESTEXT = \
$(TOP)\ext\misc\amatch.c \
$(TOP)\ext\misc\appendvfs.c \
$(TOP)\ext\misc\basexx.c \
$(TOP)\ext\misc\bgckpt.c \
$(TOP)\ext\misc\carray.c \
$(TOP)\ext\misc\cksumvfs.c \
$(TOP)\ext\misc\closure.c \

107
doc/begin_concurrent.md Normal file
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.

98
doc/wal2.md Normal file
View File

@ -0,0 +1,98 @@
Wal2 Mode Notes
===============
## Activating/Deactivating Wal2 Mode
"Wal2" mode is very similar to "wal" mode. To change a database to wal2 mode,
use the command:
>
PRAGMA journal_mode = wal2;
It is not possible to change a database directly from "wal" mode to "wal2"
mode. Instead, it must first be changed to rollback mode. So, to change a wal
mode database to wal2 mode, the following two commands may be used:
>
PRAGMA journal_mode = delete;
PRAGMA journal_mode = wal2;
A database in wal2 mode may only be accessed by versions of SQLite compiled
from this branch. Attempting to use any other version of SQLite results in an
SQLITE&#95;NOTADB error. A wal2 mode database may be changed back to rollback mode
(making it accessible by all versions of SQLite) using:
>
PRAGMA journal_mode = delete;
## The Advantage of Wal2 Mode
In legacy wal mode, when a writer writes data to the database, it doesn't
modify the database file directly. Instead, it appends new data to the
"&lt;database>-wal" file. Readers read data from both the original database
file and the "&lt;database>-wal" file. At some point, data is copied from the
"&lt;database>-wal" file into the database file, after which the wal file can
be deleted or overwritten. Copying data from the wal file into the database
file is called a "checkpoint", and may be done explictly (either by "PRAGMA
wal&#95;checkpoint" or sqlite3&#95;wal&#95;checkpoint&#95;v2()), or
automatically (by configuring "PRAGMA wal&#95;autocheckpoint" - this is the
default).
Checkpointers do not block writers, and writers do not block checkpointers.
However, if a writer writes to the database while a checkpoint is ongoing,
then the new data is appended to the end of the wal file. This means that,
even following the checkpoint, the wal file cannot be overwritten or deleted,
and so all subsequent transactions must also be appended to the wal file. The
work of the checkpointer is not wasted - SQLite remembers which parts of the
wal file have already been copied into the db file so that the next checkpoint
does not have to do so again - but it does mean that the wal file may grow
indefinitely if the checkpointer never gets a chance to finish without a
writer appending to the wal file. There are also circumstances in which
long-running readers may prevent a checkpointer from checkpointing the entire
wal file - also causing the wal file to grow indefinitely in a busy system.
Wal2 mode does not have this problem. In wal2 mode, wal files do not grow
indefinitely even if the checkpointer never has a chance to finish
uninterrupted.
In wal2 mode, the system uses two wal files instead of one. The files are named
"&lt;database>-wal" and "&lt;database>-wal2", where "&lt;database>" is of
course the name of the database file. When data is written to the database, the
writer begins by appending the new data to the first wal file. Once the first
wal file has grown large enough, writers switch to appending data to the second
wal file. At this point the first wal file can be checkpointed (after which it
can be overwritten). Then, once the second wal file has grown large enough and
the first wal file has been checkpointed, writers switch back to the first wal
file. And so on.
## Application Programming
From the point of view of the user, the main differences between wal and
wal2 mode are to do with checkpointing:
* In wal mode, a checkpoint may be attempted at any time. In wal2
mode, the checkpointer has to wait until writers have switched
to the "other" wal file before a checkpoint can take place.
* In wal mode, the wal-hook (callback registered using
sqlite3&#95;wal&#95;hook()) is invoked after a transaction is committed
with the total number of pages in the wal file as an argument. In wal2
mode, the argument is either the total number of uncheckpointed pages in
both wal files, or - if the "other" wal file is empty or already
checkpointed - 0.
Clients are recommended to use the same strategies for checkpointing wal2 mode
databases as for wal databases - by registering a wal-hook using
sqlite3&#95;wal&#95;hook() and attempting a checkpoint when the parameter
exceeds a certain threshold.
However, it should be noted that although the wal-hook is invoked after each
transaction is committed to disk and database locks released, it is still
invoked from within the sqlite3&#95;step() call used to execute the "COMMIT"
command. In BEGIN CONCURRENT systems, where the "COMMIT" is often protected by
an application mutex, this may reduce concurrency. In such systems, instead of
executing a checkpoint from within the wal-hook, a thread might defer this
action until after the application mutex has been released.

View File

@ -2046,6 +2046,7 @@ static int fts5UpdateMethod(
}
update_out:
sqlite3Fts5IndexCloseReader(pTab->p.pIndex);
pTab->p.pConfig->pzErrmsg = 0;
return rc;
}

View File

@ -0,0 +1,55 @@
# 2022 May 09
#
# 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 the FTS5 module.
#
source [file join [file dirname [info script]] fts5_common.tcl]
set testprefix fts5concurrent
# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
ifcapable !fts5 {
finish_test
return
}
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE ft USING fts5(line, tokenize=trigram);
}
do_execsql_test 1.1 {
BEGIN CONCURRENT;
INSERT INTO ft VALUES( hex(randomblob(50)) );
COMMIT
} {}
do_execsql_test 1.2 {
BEGIN CONCURRENT;
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<50
)
INSERT INTO ft SELECT hex(randomblob(50)) FROM s;
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<50
)
INSERT INTO ft SELECT hex(randomblob(50)) FROM s;
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<50
)
INSERT INTO ft SELECT hex(randomblob(50)) FROM s;
COMMIT;
}
finish_test

View File

@ -85,7 +85,6 @@ do_execsql_test 2.8 {
#-------------------------------------------------------------------------
# Tests with large/small rowid values.
#
foreach {tn cfg} {
1 ""
2 "INSERT INTO fff(fff, rank) VALUES('secure-delete', 1)"
@ -111,6 +110,13 @@ foreach {tn cfg} {
set ret
}
db func newdoc newdoc
proc random {} {
set res [expr { int(rand() * 0x7FFFFFFFFFFFFFFF) }]
if { int(rand() * 2) } { set res [expr $res*-1] }
return $res
}
db func random random
do_execsql_test 3.$tn.0 {
CREATE VIRTUAL TABLE fff USING fts5(y);
@ -168,4 +174,3 @@ foreach {tn cfg} {
finish_test

228
ext/misc/bgckpt.c Normal file
View File

@ -0,0 +1,228 @@
/*
** 2017-10-11
**
** 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.
**
******************************************************************************
**
*/
#if !defined(SQLITE_TEST) || defined(SQLITE_OS_UNIX)
#include "sqlite3.h"
#include <string.h>
#include <pthread.h>
/*
** API declarations.
*/
typedef struct Checkpointer Checkpointer;
int sqlite3_bgckpt_create(const char *zFilename, Checkpointer **pp);
int sqlite3_bgckpt_checkpoint(Checkpointer *p, int bBlock);
void sqlite3_bgckpt_destroy(Checkpointer *p);
struct Checkpointer {
sqlite3 *db; /* Database handle */
pthread_t thread; /* Background thread */
pthread_mutex_t mutex;
pthread_cond_t cond;
int rc; /* Error from "PRAGMA wal_checkpoint" */
int bCkpt; /* True if checkpoint requested */
int bExit; /* True if exit requested */
};
static void *bgckptThreadMain(void *pCtx){
int rc = SQLITE_OK;
Checkpointer *p = (Checkpointer*)pCtx;
while( rc==SQLITE_OK ){
int bExit;
pthread_mutex_lock(&p->mutex);
if( p->bCkpt==0 && p->bExit==0 ){
pthread_cond_wait(&p->cond, &p->mutex);
}
p->bCkpt = 0;
bExit = p->bExit;
pthread_mutex_unlock(&p->mutex);
if( bExit ) break;
rc = sqlite3_exec(p->db, "PRAGMA wal_checkpoint", 0, 0, 0);
if( rc==SQLITE_BUSY ){
rc = SQLITE_OK;
}
}
pthread_mutex_lock(&p->mutex);
p->rc = rc;
pthread_mutex_unlock(&p->mutex);
return 0;
}
void sqlite3_bgckpt_destroy(Checkpointer *p){
if( p ){
void *ret = 0;
/* Signal the background thread to exit */
pthread_mutex_lock(&p->mutex);
p->bExit = 1;
pthread_cond_broadcast(&p->cond);
pthread_mutex_unlock(&p->mutex);
pthread_join(p->thread, &ret);
sqlite3_close(p->db);
sqlite3_free(p);
}
}
int sqlite3_bgckpt_create(const char *zFilename, Checkpointer **pp){
Checkpointer *pNew = 0;
int rc;
pNew = (Checkpointer*)sqlite3_malloc(sizeof(Checkpointer));
if( pNew==0 ){
rc = SQLITE_NOMEM;
}else{
memset(pNew, 0, sizeof(Checkpointer));
rc = sqlite3_open(zFilename, &pNew->db);
}
if( rc==SQLITE_OK ){
pthread_mutex_init(&pNew->mutex, 0);
pthread_cond_init(&pNew->cond, 0);
pthread_create(&pNew->thread, 0, bgckptThreadMain, (void*)pNew);
}
if( rc!=SQLITE_OK ){
sqlite3_bgckpt_destroy(pNew);
pNew = 0;
}
*pp = pNew;
return rc;
}
int sqlite3_bgckpt_checkpoint(Checkpointer *p, int bBlock){
int rc;
pthread_mutex_lock(&p->mutex);
rc = p->rc;
if( rc==SQLITE_OK ){
p->bCkpt = 1;
pthread_cond_broadcast(&p->cond);
}
pthread_mutex_unlock(&p->mutex);
return rc;
}
#ifdef SQLITE_TEST
#include "tclsqlite.h"
const char *sqlite3ErrName(int rc);
static void SQLITE_TCLAPI bgckpt_del(void * clientData){
Checkpointer *pCkpt = (Checkpointer*)clientData;
sqlite3_bgckpt_destroy(pCkpt);
}
/*
** Tclcmd: $ckpt SUBCMD ...
*/
static int SQLITE_TCLAPI bgckpt_obj_cmd(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
Checkpointer *pCkpt = (Checkpointer*)clientData;
const char *aCmd[] = { "checkpoint", "destroy", 0 };
int iCmd;
if( objc<2 ){
Tcl_WrongNumArgs(interp, 1, objv, "SUBCMD ...");
return TCL_ERROR;
}
if( Tcl_GetIndexFromObj(interp, objv[1], aCmd, "sub-command", 0, &iCmd) ){
return TCL_ERROR;
}
switch( iCmd ){
case 0: {
int rc;
int bBlock = 0;
if( objc>3 ){
Tcl_WrongNumArgs(interp, 2, objv, "?BLOCKING?");
return TCL_ERROR;
}
if( objc==3 && Tcl_GetBooleanFromObj(interp, objv[2], &bBlock) ){
return TCL_ERROR;
}
rc = sqlite3_bgckpt_checkpoint(pCkpt, bBlock);
if( rc!=SQLITE_OK ){
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
return TCL_ERROR;
}
break;
}
case 1: {
Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
break;
}
}
return TCL_OK;
}
/*
** Tclcmd: bgckpt CMDNAME FILENAME
*/
static int SQLITE_TCLAPI bgckpt_cmd(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
const char *zCmd;
const char *zFilename;
int rc;
Checkpointer *pCkpt;
if( objc!=3 ){
Tcl_WrongNumArgs(interp, 1, objv, "CMDNAME FILENAME");
return TCL_ERROR;
}
zCmd = Tcl_GetString(objv[1]);
zFilename = Tcl_GetString(objv[2]);
rc = sqlite3_bgckpt_create(zFilename, &pCkpt);
if( rc!=SQLITE_OK ){
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
return TCL_ERROR;
}
Tcl_CreateObjCommand(interp, zCmd, bgckpt_obj_cmd, (void*)pCkpt, bgckpt_del);
Tcl_SetObjResult(interp, objv[1]);
return TCL_OK;
}
int Bgckpt_Init(Tcl_Interp *interp){
Tcl_CreateObjCommand(interp, "bgckpt", bgckpt_cmd, 0, 0);
return TCL_OK;
}
#endif /* SQLITE_TEST */
#else
# include "tclsqlite.h"
int Bgckpt_Init(Tcl_Interp *interp){ return TCL_OK; }
#endif

View File

@ -0,0 +1,223 @@
# 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');
}
$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}
cb delete
finish_test

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

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
} {}

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_v2(&pIter, nBuf, pBuf,SQLITE_CHANGESETSTART_FULL);
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 */

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_) */

View File

@ -27,6 +27,13 @@ typedef struct SessionInput SessionInput;
#define SESSIONS_ROWID "_rowid_"
/*
** 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;
@ -94,6 +101,7 @@ struct SessionInput {
struct sqlite3_changeset_iter {
SessionInput in; /* Input buffer or stream */
SessionBuffer tblhdr; /* Buffer to hold apValue/zTab/abPK/ */
int bChangebatch; /* True for changebatch_add() */
int bPatchset; /* True if this is a patchset */
int bInvert; /* True to invert changeset */
int bSkipEmpty; /* Skip noop UPDATE changes */
@ -2604,7 +2612,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 */
@ -2668,8 +2676,8 @@ static int sessionAppendUpdate(
/* Add a field to the old.* record. This is omitted if this module 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);
@ -2678,7 +2686,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);
@ -2704,7 +2712,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 */
@ -2714,7 +2722,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;
@ -2940,12 +2948,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);
@ -2963,7 +2971,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 */
@ -3008,7 +3016,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 ){
@ -3034,10 +3042,10 @@ static int sessionGenerateChangeset(
}
}else{
assert( pTab->abPK!=0 );
rc = sessionAppendUpdate(&buf, bPatchset, pSel, p, pTab->abPK);
rc = sessionAppendUpdate(&buf, ePatchset, pSel, p, pTab->abPK);
}
}else if( p->op!=SQLITE_INSERT ){
rc = sessionAppendDelete(&buf, bPatchset, p, pTab->nCol,pTab->abPK);
rc = sessionAppendDelete(&buf, ePatchset, p, pTab->nCol,pTab->abPK);
}
if( rc==SQLITE_OK ){
rc = sqlite3_reset(pSel);
@ -3096,7 +3104,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
);
@ -3112,7 +3121,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);
}
/*
@ -3124,7 +3134,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);
}
/*
@ -3140,9 +3151,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.
*/
@ -3295,8 +3317,13 @@ int sqlite3changeset_start_v2(
void *pChangeset, /* Pointer to buffer containing changeset */
int flags
){
int rc;
sqlite3_changeset_iter *pIter = 0;
int bInvert = !!(flags & SQLITE_CHANGESETSTART_INVERT);
return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, bInvert, 0);
rc = sessionChangesetStart(&pIter, 0, 0, nChangeset, pChangeset, bInvert, 0);
if( pIter && (flags & SQLITE_CHANGESETSTART_FULL) ) pIter->bChangebatch = 1;
*pp = pIter;
return rc;
}
/*
@ -3773,8 +3800,12 @@ static int sessionChangesetNextOne(
**
** Such records are technically corrupt, but the rebaser was at one
** point generating them. Under most circumstances this is benign, but
** can cause spurious SQLITE_RANGE errors when applying the changeset. */
if( p->bPatchset==0 && p->op==SQLITE_UPDATE){
** can cause spurious SQLITE_RANGE errors when applying the changeset.
**
** Update for bedrock branch: Do not do this for changebatch_add() on
** this branch, as changesets generated by sqlite3sessions_fullchangeset()
** also have this property. */
if( p->bChangebatch==0 && p->bPatchset==0 && p->op==SQLITE_UPDATE){
for(i=0; i<p->nCol; i++){
if( p->abPK[i]==0 && p->apValue[i+p->nCol]==0 ){
sqlite3ValueFree(p->apValue[i]);
@ -5935,10 +5966,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){

View File

@ -368,6 +368,32 @@ 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: 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
@ -568,12 +594,16 @@ int sqlite3changeset_start_v2(
** The following flags may passed via the 4th parameter to
** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]:
**
** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd>
** <dt>SQLITE_CHANGESETSTART_INVERT <dd>
** Invert the changeset while iterating through it. This is equivalent to
** inverting a changeset using sqlite3changeset_invert() before applying it.
** It is an error to specify this flag with a patchset.
**
** <dt>SQLITE_CHANGESETSTART_FULL <dd>
** Do not trim extra fields added to fullchangeset changesets.
*/
#define SQLITE_CHANGESETSTART_INVERT 0x0002
#define SQLITE_CHANGESETSTART_FULL 0x0004
/*

View File

@ -229,6 +229,7 @@ static int testStreamOutput(
** $session indirect INTEGER
** $session patchset
** $session table_filter SCRIPT
** $session fullchangeset
*/
static int SQLITE_TCLAPI test_session_cmd(
void *clientData,
@ -242,20 +243,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", 2, "OPTION INTEGER", }, /* 11 */
{ "diff", 2, "FROMDB TBL" }, /* 8 */
{ "fullchangeset",0, "" }, /* 9 */
{ "memory_used", 0, "", }, /* 10 */
{ "changeset_size", 0, "", }, /* 11 */
{ "object_config", 2, "OPTION INTEGER", }, /* 12 */
{ 0 }
};
int iSub;
@ -285,10 +286,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);
@ -298,12 +300,15 @@ static int SQLITE_TCLAPI test_session_cmd(
}else{
if( iSub==7 ){
rc = sqlite3session_patchset(pSession, &o.n, &o.p);
assert_changeset_is_ok(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);
assert_changeset_is_ok(o.n, o.p);
}
}
if( rc==SQLITE_OK ){
assert_changeset_is_ok(o.n, o.p);
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(o.p, o.n));
}
sqlite3_free(o.p);
@ -313,6 +318,7 @@ static int SQLITE_TCLAPI test_session_cmd(
break;
}
case 2: /* delete */
Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
break;
@ -363,18 +369,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: { /* object_config */
case 12: { /* object_config */
struct ObjConfOpt {
const char *zName;
int opt;
@ -384,7 +390,6 @@ static int SQLITE_TCLAPI test_session_cmd(
{ 0, 0 }
};
size_t sz = sizeof(aOpt[0]);
int iArg;
int iOpt;
if( Tcl_GetIndexFromObjStruct(interp,objv[2],aOpt,sz,"option",0,&iOpt) ){
@ -541,7 +546,7 @@ static int test_filter_handler(
Tcl_DecrRefCount(pEval);
return res;
}
}
static int test_conflict_handler(
void *pCtx, /* Pointer to TestConflictHandler structure */
@ -1192,6 +1197,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
@ -1746,6 +1872,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;
}

View File

@ -1,10 +1,7 @@
_fiddle_exec
_fiddle_interrupt
_fiddle_experiment
_fiddle_the_db
_fiddle_db_arg
_fiddle_db_filename
_fiddle_exec
_fiddle_experiment
_fiddle_interrupt
_fiddle_main
_fiddle_reset_db
_fiddle_db_handle
_fiddle_db_vfs
_fiddle_export_db

View File

@ -0,0 +1,72 @@
_sqlite3_bind_blob
_sqlite3_bind_double
_sqlite3_bind_int
_sqlite3_bind_int64
_sqlite3_bind_null
_sqlite3_bind_parameter_count
_sqlite3_bind_parameter_index
_sqlite3_bind_text
_sqlite3_changes
_sqlite3_changes64
_sqlite3_clear_bindings
_sqlite3_close_v2
_sqlite3_column_blob
_sqlite3_column_bytes
_sqlite3_column_count
_sqlite3_column_count
_sqlite3_column_double
_sqlite3_column_int
_sqlite3_column_int64
_sqlite3_column_name
_sqlite3_column_text
_sqlite3_column_type
_sqlite3_compileoption_get
_sqlite3_compileoption_used
_sqlite3_create_function_v2
_sqlite3_data_count
_sqlite3_db_filename
_sqlite3_db_name
_sqlite3_errmsg
_sqlite3_error_offset
_sqlite3_errstr
_sqlite3_exec
_sqlite3_expanded_sql
_sqlite3_extended_errcode
_sqlite3_extended_result_codes
_sqlite3_finalize
_sqlite3_initialize
_sqlite3_interrupt
_sqlite3_libversion
_sqlite3_libversion_number
_sqlite3_open
_sqlite3_open_v2
_sqlite3_prepare_v2
_sqlite3_prepare_v3
_sqlite3_reset
_sqlite3_result_blob
_sqlite3_result_double
_sqlite3_result_error
_sqlite3_result_error_code
_sqlite3_result_error_nomem
_sqlite3_result_error_toobig
_sqlite3_result_int
_sqlite3_result_null
_sqlite3_result_text
_sqlite3_sourceid
_sqlite3_sql
_sqlite3_step
_sqlite3_strglob
_sqlite3_strlike
_sqlite3_total_changes
_sqlite3_total_changes64
_sqlite3_value_blob
_sqlite3_value_bytes
_sqlite3_value_double
_sqlite3_value_text
_sqlite3_value_type
_sqlite3_vfs_find
_sqlite3_vfs_register
_sqlite3_wasm_db_error
_sqlite3_wasm_enum_json
_malloc
_free

View File

@ -1,158 +0,0 @@
_malloc
_free
_realloc
_sqlite3_aggregate_context
_sqlite3_auto_extension
_sqlite3_bind_blob
_sqlite3_bind_double
_sqlite3_bind_int
_sqlite3_bind_int64
_sqlite3_bind_null
_sqlite3_bind_parameter_count
_sqlite3_bind_parameter_index
_sqlite3_bind_parameter_name
_sqlite3_bind_pointer
_sqlite3_bind_text
_sqlite3_busy_handler
_sqlite3_busy_timeout
_sqlite3_cancel_auto_extension
_sqlite3_changes
_sqlite3_changes64
_sqlite3_clear_bindings
_sqlite3_close_v2
_sqlite3_collation_needed
_sqlite3_column_blob
_sqlite3_column_bytes
_sqlite3_column_count
_sqlite3_column_decltype
_sqlite3_column_double
_sqlite3_column_int
_sqlite3_column_int64
_sqlite3_column_name
_sqlite3_column_text
_sqlite3_column_type
_sqlite3_column_value
_sqlite3_commit_hook
_sqlite3_compileoption_get
_sqlite3_compileoption_used
_sqlite3_complete
_sqlite3_context_db_handle
_sqlite3_create_collation
_sqlite3_create_collation_v2
_sqlite3_create_function
_sqlite3_create_function_v2
_sqlite3_create_window_function
_sqlite3_data_count
_sqlite3_db_filename
_sqlite3_db_handle
_sqlite3_db_name
_sqlite3_db_readonly
_sqlite3_db_status
_sqlite3_deserialize
_sqlite3_drop_modules
_sqlite3_errcode
_sqlite3_errmsg
_sqlite3_error_offset
_sqlite3_errstr
_sqlite3_exec
_sqlite3_expanded_sql
_sqlite3_extended_errcode
_sqlite3_extended_result_codes
_sqlite3_file_control
_sqlite3_finalize
_sqlite3_free
_sqlite3_get_auxdata
_sqlite3_get_autocommit
_sqlite3_initialize
_sqlite3_interrupt
_sqlite3_is_interrupted
_sqlite3_keyword_count
_sqlite3_keyword_name
_sqlite3_keyword_check
_sqlite3_last_insert_rowid
_sqlite3_libversion
_sqlite3_libversion_number
_sqlite3_limit
_sqlite3_malloc
_sqlite3_malloc64
_sqlite3_msize
_sqlite3_open
_sqlite3_open_v2
_sqlite3_overload_function
_sqlite3_prepare_v2
_sqlite3_prepare_v3
_sqlite3_progress_handler
_sqlite3_randomness
_sqlite3_realloc
_sqlite3_realloc64
_sqlite3_reset
_sqlite3_reset_auto_extension
_sqlite3_result_blob
_sqlite3_result_double
_sqlite3_result_error
_sqlite3_result_error_code
_sqlite3_result_error_nomem
_sqlite3_result_error_toobig
_sqlite3_result_int
_sqlite3_result_int64
_sqlite3_result_null
_sqlite3_result_pointer
_sqlite3_result_subtype
_sqlite3_result_text
_sqlite3_result_zeroblob
_sqlite3_result_zeroblob64
_sqlite3_rollback_hook
_sqlite3_serialize
_sqlite3_set_auxdata
_sqlite3_set_last_insert_rowid
_sqlite3_shutdown
_sqlite3_sourceid
_sqlite3_sql
_sqlite3_status
_sqlite3_status64
_sqlite3_step
_sqlite3_stmt_busy
_sqlite3_stmt_explain
_sqlite3_stmt_isexplain
_sqlite3_stmt_readonly
_sqlite3_stmt_status
_sqlite3_strglob
_sqlite3_stricmp
_sqlite3_strlike
_sqlite3_strnicmp
_sqlite3_table_column_metadata
_sqlite3_total_changes
_sqlite3_total_changes64
_sqlite3_trace_v2
_sqlite3_txn_state
_sqlite3_update_hook
_sqlite3_uri_boolean
_sqlite3_uri_int64
_sqlite3_uri_key
_sqlite3_uri_parameter
_sqlite3_user_data
_sqlite3_value_blob
_sqlite3_value_bytes
_sqlite3_value_double
_sqlite3_value_dup
_sqlite3_value_free
_sqlite3_value_frombind
_sqlite3_value_int
_sqlite3_value_int64
_sqlite3_value_nochange
_sqlite3_value_numeric_type
_sqlite3_value_pointer
_sqlite3_value_subtype
_sqlite3_value_text
_sqlite3_value_type
_sqlite3_vfs_find
_sqlite3_vfs_register
_sqlite3_vfs_unregister
_sqlite3_vtab_collation
_sqlite3_vtab_distinct
_sqlite3_vtab_in
_sqlite3_vtab_in_first
_sqlite3_vtab_in_next
_sqlite3_vtab_nochange
_sqlite3_vtab_on_conflict
_sqlite3_vtab_rhs_value

View File

@ -1,4 +1,3 @@
//#ifnot omit-oo1
/*
2022-08-24
@ -42,13 +41,9 @@
- `onready` (optional, but...): this callback is called with no
arguments when the worker fires its initial
'sqlite3-api'/'worker1-ready' message, which it does when
sqlite3.initWorker1API() completes its initialization. This is the
simplest way to tell the worker to kick off work at the earliest
opportunity, and the only way to know when the worker module has
completed loading. The irony of using a callback for this, instead
of returning a promise from sqlite3Worker1Promiser() is not lost on
the developers: see sqlite3Worker1Promiser.v2() which uses a
Promise instead.
sqlite3.initWorker1API() completes its initialization. This is
the simplest way to tell the worker to kick off work at the
earliest opportunity.
- `onunhandled` (optional): a callback which gets passed the
message event object for any worker.onmessage() events which
@ -119,7 +114,7 @@
by all client code except that which tests this API. The `row`
property contains the row result in the form implied by the
`rowMode` option (defaulting to `'array'`). The `rowNumber` is a
1-based integer value incremented by 1 on each call into the
1-based integer value incremented by 1 on each call into th
callback.
At the end of the result set, the same event is fired with
@ -127,17 +122,8 @@
the end of the result set has been reached. Note that the rows
arrive via worker-posted messages, with all the implications
of that.
Notable shortcomings:
- This API was not designed with ES6 modules in mind. Neither Firefox
nor Safari support, as of March 2023, the {type:"module"} flag to the
Worker constructor, so that particular usage is not something we're going
to target for the time being:
https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker
*/
globalThis.sqlite3Worker1Promiser = function callee(config = callee.defaultConfig){
self.sqlite3Worker1Promiser = function callee(config = callee.defaultConfig){
// Inspired by: https://stackoverflow.com/a/52439530
if(1===arguments.length && 'function'===typeof arguments[0]){
const f = config;
@ -160,7 +146,6 @@ globalThis.sqlite3Worker1Promiser = function callee(config = callee.defaultConfi
if(!config.worker) config.worker = callee.defaultConfig.worker;
if('function'===typeof config.worker) config.worker = config.worker();
let dbId;
let promiserFunc;
config.worker.onmessage = function(ev){
ev = ev.data;
debug('worker1.onmessage',ev);
@ -168,14 +153,14 @@ globalThis.sqlite3Worker1Promiser = function callee(config = callee.defaultConfi
if(!msgHandler){
if(ev && 'sqlite3-api'===ev.type && 'worker1-ready'===ev.result) {
/*fired one time when the Worker1 API initializes*/
if(config.onready) config.onready(promiserFunc);
if(config.onready) config.onready();
return;
}
msgHandler = handlerMap[ev.type] /* check for exec per-row callback */;
if(msgHandler && msgHandler.onrow){
msgHandler.onrow(ev);
return;
}
}
if(config.onunhandled) config.onunhandled(arguments[0]);
else err("sqlite3Worker1Promiser() unhandled worker message:",ev);
return;
@ -197,19 +182,19 @@ globalThis.sqlite3Worker1Promiser = function callee(config = callee.defaultConfi
try {msgHandler.resolve(ev)}
catch(e){msgHandler.reject(e)}
}/*worker.onmessage()*/;
return promiserFunc = function(/*(msgType, msgArgs) || (msgEnvelope)*/){
return function(/*(msgType, msgArgs) || (msgEnvelope)*/){
let msg;
if(1===arguments.length){
msg = arguments[0];
}else if(2===arguments.length){
msg = Object.create(null);
msg.type = arguments[0];
msg.args = arguments[1];
msg.dbId = msg.args.dbId;
msg = {
type: arguments[0],
args: arguments[1]
};
}else{
toss("Invalid arguments for sqlite3Worker1Promiser()-created factory.");
toss("Invalid arugments for sqlite3Worker1Promiser()-created factory.");
}
if(!msg.dbId && msg.type!=='open') msg.dbId = dbId;
if(!msg.dbId) msg.dbId = dbId;
msg.messageId = genMsgId(msg);
msg.departureTime = performance.now();
const proxy = Object.create(null);
@ -251,96 +236,28 @@ globalThis.sqlite3Worker1Promiser = function callee(config = callee.defaultConfi
return p;
};
}/*sqlite3Worker1Promiser()*/;
globalThis.sqlite3Worker1Promiser.defaultConfig = {
self.sqlite3Worker1Promiser.defaultConfig = {
worker: function(){
//#if target=es6-module
return new Worker(new URL("sqlite3-worker1-bundler-friendly.mjs", import.meta.url),{
type: 'module'
});
//#if target=es6-bundler-friendly
return new Worker("sqlite3-worker1.js");
//#else
let theJs = "sqlite3-worker1.js";
if(this.currentScript){
const src = this.currentScript.src.split('/');
src.pop();
theJs = src.join('/')+'/' + theJs;
//sqlite3.config.warn("promiser currentScript, theJs =",this.currentScript,theJs);
}else if(globalThis.location){
//sqlite3.config.warn("promiser globalThis.location =",globalThis.location);
const urlParams = new URL(globalThis.location.href).searchParams;
//console.warn("promiser currentScript, theJs =",this.currentScript,theJs);
}else{
//console.warn("promiser self.location =",self.location);
const urlParams = new URL(self.location.href).searchParams;
if(urlParams.has('sqlite3.dir')){
theJs = urlParams.get('sqlite3.dir') + '/' + theJs;
}
}
return new Worker(theJs + globalThis.location.search);
return new Worker(theJs + self.location.search);
//#endif
}
//#ifnot target=es6-module
.bind({
currentScript: globalThis?.document?.currentScript
})
//#endif
,
}.bind({
currentScript: self?.document?.currentScript
}),
onerror: (...args)=>console.error('worker1 promiser error',...args)
}/*defaultConfig*/;
/**
sqlite3Worker1Promiser.v2(), added in 3.46, works identically to
sqlite3Worker1Promiser() except that it returns a Promise instead
of relying an an onready callback in the config object. The Promise
resolves to the same factory function which
sqlite3Worker1Promiser() returns.
If config is-a function or is an object which contains an onready
function, that function is replaced by a proxy which will resolve
after calling the original function and will reject if that
function throws.
*/
sqlite3Worker1Promiser.v2 = function(config){
let oldFunc;
if( 'function' == typeof config ){
oldFunc = config;
config = {};
}else if('function'===typeof config?.onready){
oldFunc = config.onready;
delete config.onready;
}
const promiseProxy = Object.create(null);
config = Object.assign((config || Object.create(null)),{
onready: async function(func){
try {
if( oldFunc ) await oldFunc(func);
promiseProxy.resolve(func);
}
catch(e){promiseProxy.reject(e)}
}
});
const p = new Promise(function(resolve,reject){
promiseProxy.resolve = resolve;
promiseProxy.reject = reject;
});
try{
this.original(config);
}catch(e){
promiseProxy.reject(e);
}
return p;
}.bind({
/* We do this because clients are
recommended to delete globalThis.sqlite3Worker1Promiser. */
original: sqlite3Worker1Promiser
});
//#if target=es6-module
/**
When built as a module, we export sqlite3Worker1Promiser.v2()
instead of sqlite3Worker1Promise() because (A) its interface is more
conventional for ESM usage and (B) the ESM option export option for
this API did not exist until v2 was created, so there's no backwards
incompatibility.
*/
export default sqlite3Worker1Promiser.v2;
//#endif /* target=es6-module */
//#else
/* Built with the omit-oo1 flag. */
//#endif ifnot omit-oo1
};

View File

@ -171,31 +171,6 @@
display: flex;
flex-direction: column-reverse;
}
/* emcscript-related styling, used during the module load/intialization processes... */
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
div.emscripten { text-align: center; }
div.emscripten_border { border: 1px solid black; }
#module-spinner { overflow: visible; }
#module-spinner > * {
margin-top: 1em;
}
.spinner {
height: 50px;
width: 50px;
margin: 0px auto;
animation: rotation 0.8s linear infinite;
border-left: 10px solid rgb(0,150,240);
border-right: 10px solid rgb(0,150,240);
border-bottom: 10px solid rgb(0,150,240);
border-top: 10px solid rgb(100,0,200);
border-radius: 100%;
background-color: rgb(200,100,250);
}
@keyframes rotation {
from {transform: rotate(0deg);}
to {transform: rotate(360deg);}
}
</style>
</head>
<body>
@ -298,6 +273,11 @@
</fieldset>
</div>
</div> <!-- #view-split -->
<!-- Maintenance notes:
... TODO... currently being refactored...
-->
<script src="fiddle.js"></script>
</body>
</html>

View File

@ -347,6 +347,7 @@ TESTSRC += \
$(TOP)/ext/misc/amatch.c \
$(TOP)/ext/misc/appendvfs.c \
$(TOP)/ext/misc/basexx.c \
$(TOP)/ext/misc/bgckpt.c \
$(TOP)/ext/misc/carray.c \
$(TOP)/ext/misc/cksumvfs.c \
$(TOP)/ext/misc/closure.c \
@ -435,6 +436,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
@ -1022,11 +1024,15 @@ THREADTEST3_SRC = $(TOP)/test/threadtest3.c \
$(TOP)/test/tt3_index.c \
$(TOP)/test/tt3_vacuum.c \
$(TOP)/test/tt3_stress.c \
$(TOP)/test/tt3_bcwal2.c \
$(TOP)/test/tt3_lookaside1.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)

181
manifest
View File

@ -1,11 +1,11 @@
C Fix\sa\sbug\sin\sthe\sparsing\sof\ssome\scorner-case\sJSON\sPATH\sstrings\sthat\scontain\nescaped\sdouble-quotes.
D 2024-09-04T16:01:44.145
C Increase\smaximum\slength\sof\ssqlite3_log()\smessages\sto\s700\sbytes.\sMark\slog\smessages\sas\sv=10.
D 2024-11-13T14:42:32.861
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F Makefile.in af5fbc3453b745daa68c7aa5dfdb945c09cb724971db3b783d6b5e1a62279e28
F Makefile.in 3cd3e81b3558a4ff391104b4ecba6966246cd226485841a666105740d0cab95f
F Makefile.linux-gcc f3842a0b1efbfbb74ac0ef60e56b301836d05b4d867d014f714fa750048f1ab6
F Makefile.msc 2c905f4c795a628d7fd892294e8fdec6d5f719fa5b252cb839fed147e64435a0
F Makefile.msc 9f54b105b2ceeab4cf397b9f4a119b22933acf90f0a199acb11a464a2efe6c93
F README.md 5b678e264236788390d11991f2c0052bd73f19790173883fc56d638bcb849154
F VERSION 0db40f92c04378404eb45bff93e9e42c148c7e54fd3da99469ed21e22411f5a6
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
@ -39,6 +39,7 @@ F configure 49523f0a070b583cea040d26eff53a65fb0893eca4663b1343a4d5a9a964da53 x
F configure.ac a100ebf7a07f5dedd319ef547dd467d1676ed059b85a7877aa9c44ac309f7000
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
F doc/F2FS.txt c1d4a0ae9711cfe0e1d8b019d154f1c29e0d3abfe820787ba1e9ed7691160fcd
F doc/begin_concurrent.md 4bee2c3990d1eb800f1ce3726a911292a8e4b889300b2ffd4b08d357370db299
F doc/compile-for-windows.md e8635eea9153dcd6a51fd2740666ebc4492b3813cb1ac31cd8e99150df91762d
F doc/json-enhancements.md e356fc834781f1f1aa22ee300027a270b2c960122468499bf347bb123ce1ea4f
F doc/jsonb.md 5fab4b8613aa9153fbeb6259297bd4697988af8b3d23900deba588fa7841456b
@ -49,6 +50,7 @@ F doc/trusted-schema.md 33625008620e879c7bcfbbfa079587612c434fa094d338b08242288d
F doc/vdbesort-memory.md 4da2639c14cd24a31e0af694b1a8dd37eaf277aff3867e9a8cc14046bc49df56
F doc/vfs-shm.txt e101f27ea02a8387ce46a05be2b1a902a021d37a
F doc/wal-lock.md 781726aaba20bafeceb7ba9f91d5c98c6731691b30c954e37cf0b49a053d461d
F doc/wal2.md a807405a05e19a4945c5905a9ffa0fe45b8560dd7572461192501f565c19cdb5
F ext/README.md fd5f78013b0a2bc6f0067afb19e6ad040e89a10179b4f6f03eee58fac5f169bd
F ext/async/README.txt e12275968f6fde133a80e04387d0e839b0c51f91
F ext/async/sqlite3async.c 6f247666b495c477628dd19364d279c78ea48cd90c72d9f9b98ad1aff3294f94
@ -100,7 +102,7 @@ F ext/fts5/fts5_config.c 353d2a0d12678cae6ab5b9ce54aed8dac0825667b69248b5a4ed81c
F ext/fts5/fts5_expr.c 9a56f53700d1860f0ee2f373c2b9074eaf2a7aa0637d0e27a6476de26a3fee33
F ext/fts5/fts5_hash.c adda4272be401566a6e0ba1acbe70ee5cb97fce944bc2e04dc707152a0ec91b1
F ext/fts5/fts5_index.c 571483823193f09439356741669aa8c81da838ae6f5e1bfa7517f7ee2fb3addd
F ext/fts5/fts5_main.c 1fddb53f495425d9314c74b30c5848a9dd254be0e5f445bfe38292d5ab21c288
F ext/fts5/fts5_main.c 43990841e0221e0da1bf7ec687196683692b9e8783c372b8d58dea3e9866a5e4
F ext/fts5/fts5_storage.c 9a9b880be12901f1962ae2a5a7e1b74348b3099a1e728764e419f75d98e3e612
F ext/fts5/fts5_tcl.c 4db9258a7882c5eac0da4433042132aaf15b87dd1e1636c7a6ca203abd2c8bfe
F ext/fts5/fts5_test_mi.c 08c11ec968148d4cb4119d96d819f8c1f329812c568bac3684f5464be177d3ee
@ -137,6 +139,7 @@ F ext/fts5/test/fts5cat.test bf67dd335f964482ee658287521b81e2b88697b45eb7f73933e
F ext/fts5/test/fts5circref.test f880dfd0d99f6fb73b88ccacb0927d18e833672fd906cc47d6b4e529419eaa62
F ext/fts5/test/fts5colset.test 544f4998cdbfe06a3123887fc0221612e8aa8192cdaff152872f1aadb10e6897
F ext/fts5/test/fts5columnsize.test 0af91d63985afdf663455d4b572b935238380140d74079eac362760866d3297b
F ext/fts5/test/fts5concurrent.test eaaba0037090138bc86d581ca95f3be7ef8eff60155b5b8fa3014acee4a595c2
F ext/fts5/test/fts5config.test 017daf10d2642496e97402baa0134de8b5b46b9c37e53c229cd9ab711d21522c
F ext/fts5/test/fts5conflict.test bf6030a77dbb1bedfcc42e589ed7980846c995765d77460551e448b56d741244
F ext/fts5/test/fts5connect.test 08030168fc96fc278fa81f28654fb7e90566f33aff269c073e19b3ae9126b2f4
@ -220,7 +223,7 @@ F ext/fts5/test/fts5rowid.test 8632829fec04996832a4cfb4f0bd89721ba65b7e398c17317
F ext/fts5/test/fts5savepoint.test 7f373184cf2d6c1c472d2bc732e1fce62211ffe023f13e381db0f5e4fd06e41d
F ext/fts5/test/fts5secure.test a02f771742fb2b1b9bdcb4bf523bcf2d0aa1ff597831d40fe3e72aaa6d0ec40f
F ext/fts5/test/fts5secure2.test 2e961d7eef939f294c56b5d895cac7f1c3a60b934ee2cfd5e5e620bdf1ba6bbc
F ext/fts5/test/fts5secure3.test 6d066828d225b0dbe5db818d4d6165df7bb70210e68a577e858e8762400d5a23
F ext/fts5/test/fts5secure3.test e29f7e92af78a35e1c0f7461dd598f910036d588437db50242d5823576f3d6ee
F ext/fts5/test/fts5secure4.test 0d10a80590c07891478700af7793b232962042677432b9846cf7fc8337b67c97
F ext/fts5/test/fts5secure5.test c07a68ced5951567ac116c22f2d2aafae497e47fe9fcb6a335c22f9c7a4f2c3a
F ext/fts5/test/fts5secure6.test 74bf04733cc523bccca519bb03d3b4e2ed6f6e3db7c59bf6be82c88a0ac857fd
@ -386,6 +389,7 @@ F ext/misc/appendvfs.c 9642c7a194a2a25dca7ad3e36af24a0a46d7702168c4ad7e59c9f9b0e
F ext/misc/base64.c a71b131e50300c654a66c469a25b62874481f3d1cb3beb56aca9a68edd812e0d
F ext/misc/base85.c 073054111988db593ef5fdb87ab8c459df1ea0c3aaaddf0f5bfa3d72b7e6280a
F ext/misc/basexx.c 89ad6b76558efbceb627afd5e2ef1d84b2e96d9aaf9b7ecb20e3d00b51be6fcf
F ext/misc/bgckpt.c 18cfc9c39ffab3299f730f86ae2991c8574c0bd9ec80efd2f89196798a7b7181
F ext/misc/blobio.c a867c4c4617f6ec223a307ebfe0eabb45e0992f74dd47722b96f3e631c0edb2a
F ext/misc/btreeinfo.c cb952620eedf5c0b7625b678f0f08e54d2ec0011d4e50efda5ebdc97f3df7d04
F ext/misc/carray.c 34fac63770971611c5285de0a9f0ac67d504eaf66be891f637add9290f1c76a5
@ -553,6 +557,8 @@ 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 51694900ccbdf144c2e403f99358b7a3f137354e2ba8d1033ef88a53f1a494f2
F ext/session/changebatchfault.test be49c793219bf387ad692a60856b921f0854ad6d
F ext/session/changeset.c 7a1e6a14c7e92d36ca177e92e88b5281acd709f3b726298dc34ec0fb58869cb5
F ext/session/changesetfuzz.c 227076ab0ae4447d742c01ee88a564da6478bbf26b65108bf8fac9cd8b0b24aa
F ext/session/changesetfuzz1.test 15b629004e58d5ffcc852e6842a603775bb64b1ce51254831f3d12b113b616cd
@ -571,7 +577,7 @@ F ext/session/sessionD.test f5c6a762d00bc6ca9d561695c322ba8ecca2bed370486707ef37
F ext/session/sessionE.test b2010949c9d7415306f64e3c2072ddabc4b8250c98478d3c0c4d064bce83111d
F ext/session/sessionF.test d37ed800881e742c208df443537bf29aa49fd56eac520d0f0c6df3e6320f3401
F ext/session/sessionG.test 3efe388282d641b65485b5462e67851002cd91a282dc95b685d085eb8efdad0a
F ext/session/sessionH.test 71bbff6b1abb2c4ac62b84dee53273c37e0b21e5fde3aed80929403e091ef859
F ext/session/sessionH.test 29a5441c3dc0a63fa596d745e64bc6c636e062ae04cd89bc84e32c7d98b1fa9b
F ext/session/session_common.tcl e5598096425486b363718e2cda48ee85d660c96b4f8ea9d9d7a4c3ef514769da
F ext/session/session_speed_test.c dcf0ef58d76b70c8fbd9eab3be77cf9deb8bc1638fed8be518b62d6cbdef88b3
F ext/session/sessionalter.test 460bdac2832a550519f6bc32e5db2c0cee94f335870aaf25a3a403a81ab20e17
@ -593,13 +599,15 @@ F ext/session/sessionrowid.test 85187c2f1b38861a5844868126f69f9ec62223a03449a98a
F ext/session/sessionsize.test 8fcf4685993c3dbaa46a24183940ab9f5aa9ed0d23e5fb63bfffbdb56134b795
F ext/session/sessionstat1.test 5e718d5888c0c49bbb33a7a4f816366db85f59f6a4f97544a806421b85dc2dec
F ext/session/sessionwor.test 6fd9a2256442cebde5b2284936ae9e0d54bde692d0f5fd009ecef8511f4cf3fc
F ext/session/sqlite3session.c c7473aafbd88f796391a8c25aa90975a8f3729ab7f4f8cf74ab9d3b014e10abe
F ext/session/sqlite3session.h 683ccbf16e2c2521661fc4c1cf918ce57002039efbcabcd8097fa4bca569104b
F ext/session/test_session.c 6acbe67db80ab0806147eb62a12f9e3a44930f4a740b68b0a4340dddda2c10d7
F ext/session/sqlite3changebatch.c d488b42d8fd49fb013a1e9c4535232680dabeb28ae8f9421b65ea0ccc3b430f7
F ext/session/sqlite3changebatch.h e72016998c9a22d439ddfd547b69e1ebac810c24
F ext/session/sqlite3session.c 2190541e6870ceb6609e6cee35d2554c1843e5c556a93657e879c08deba6d124
F ext/session/sqlite3session.h 3376dbf372cb00cc0f4e960ca0a0125418638da8c55aad749c9fe7a58a770506
F ext/session/test_session.c 1f0cfe31fdcc11565f4a4220382bff807448d7b82cb908d67c5506b1fd2d6f82
F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
F ext/userauth/user-auth.txt ca7e9ee82ca4e1c1744295f8184dd70edfae1992865d26c64303f539eb6c084c
F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb
F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c
F ext/wasm/EXPORTED_FUNCTIONS.fiddle 7fb73f7150ab79d83bb45a67d257553c905c78cd3d693101699243f36c5ae6c3
F ext/wasm/GNUmakefile d4f6586d9a36ee2869a8c7f77227a8b7f42b6c4623f3be594beafb1554ab20d9
F ext/wasm/README-dist.txt 6382cb9548076fca472fb3330bbdba3a55c1ea0b180ff9253f084f07ff383576
F ext/wasm/README.md a8a2962c3aebdf8d2104a9102e336c5554e78fc6072746e5daf9c61514e7d193
@ -608,8 +616,8 @@ F ext/wasm/SQLTester/SQLTester.mjs ce765c0ad7d57f93553d12ef4dca574deb00300134a26
F ext/wasm/SQLTester/SQLTester.run.mjs c72b7fe2072d05992f7a3d8c6a1d34e95712513ceabe40849784e24e41c84638
F ext/wasm/SQLTester/index.html 3f8a016df0776be76605abf20e815ecaafbe055abac0e1fe5ea080e7846b760d
F ext/wasm/SQLTester/touint8array.c 2d5ece04ec1393a6a60c4bf96385bda5e1a10ad49f3038b96460fc5e5aa7e536
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api c5eaceabb9e759aaae7d3101a4a3e542f96ab2c99d89a80ce20ec18c23115f33
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-auth 7ac80cc3b6a6d52e041bb295e85555ce797be78c15ef2008a64ae58815014080
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-core 400213eb52a7e5ad5f448053d375cacf4dac2cf45d134f3edfe485ae4a49a183
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-preupdate d1d62a2212099f2c0782d730beb8cb84a7a52d99c15ead2cb9b1411fff5fd6b1
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-see fb29e62082a658f0d81102488414d422c393c4b20cc2f685b216bc566237957b
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-session 213b6c04267cb9bd760172db011eb1650732805fb3d01f9395478a8ceec18eb0
@ -633,7 +641,7 @@ F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js e529a99b7d5a088284821e2902b20d34
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js e99e3d99f736937914527070f00ab13e9391d3f1cef884ab99a64cbcbee8d675
F ext/wasm/api/sqlite3-vtab-helper.c-pp.js e809739d71e8b35dfe1b55d24d91f02d04239e6aef7ca1ea92a15a29e704f616
F ext/wasm/api/sqlite3-wasm.c 09a938fc570f282e602acd111147c7b74b5332da72540c512a79b916ab57882a
F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js 46f303ba8ddd1b2f0a391798837beddfa72e8c897038c8047eda49ce7d5ed46b
F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js f10c3ecd9df06f6320073c2ce230a7ed7c56034d8b88c1e57095f2a97faf423a
F ext/wasm/api/sqlite3-worker1.c-pp.js 5e8706c2c4af2a57fbcdc02f4e7ef79869971bc21bb8ede777687786ce1c92d5
F ext/wasm/batch-runner-sahpool.html e9a38fdeb36a13eac7b50241dfe7ae066fe3f51f5c0b0151e7baee5fce0d07a7
F ext/wasm/batch-runner-sahpool.js 54a3ac228e6c4703fe72fb65c897e19156263a51fe9b7e21d2834a45e876aabd
@ -657,8 +665,8 @@ F ext/wasm/dist.make 653e212c1e84aa3be168d62a10616ccea45ee9585b0192745d2706707a5
F ext/wasm/example_extra_init.c 2347cd69d19d839ef4e5e77b7855103a7fe3ef2af86f2e8c95839afd8b05862f
F ext/wasm/fiddle.make 2406b02473878a99fb6a2eaff0923277017adc45eb041b2afb2d7707bf7b375c
F ext/wasm/fiddle/fiddle-worker.js 850e66fce39b89d59e161d1abac43a181a4caa89ddeea162765d660277cd84ce
F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08
F ext/wasm/fiddle/fiddle.js b444a5646a9aac9f3fc06c53d78af5e1912eb235d69a8e6010723e4eb0e9d4a1
F ext/wasm/fiddle/index.html 739e0b75bc592679665d25e2f7649d2b8b2db678f3b41a772a8720b609b8482d
F ext/wasm/index-dist.html 564b5ec5669676482c5a25dea9e721d8eafed426ecb155f93d29aeff8507511f
F ext/wasm/index.html 4337f495416756802669f69f9f9f3df9f87ee4c1918e6718719b4b5718e4713a
F ext/wasm/jaccwabyt/jaccwabyt.js 1264710db3cfbcb6887d95665b7aeba60c1126eaef789ca4cf1a4a17d5bc7f54
@ -686,7 +694,7 @@ F ext/wasm/wasmfs.make 8a4955882aaa0783b3f60a9484a1f0f3d8b6f775c0fcd17c082f31966
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 5ade0bc977aa135e79e3faaea894d5671b26107cc91e70783aa7dc83f22f3ba0
F main.mk 5a2e7d4a852c058373efc78407816de41595d06975148c766092b3cf0fea4298
F main.mk b656a84515801f1bda32776e4e8ff75bdba5021b9fec5dd0fee9f84be3f5df7a
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
F mptest/crash01.test 61e61469e257df0850df4293d7d4d6c2af301421
@ -703,15 +711,15 @@ F src/analyze.c 30bf40ec4208ead9e977bec017bccc8a9681820936e38ca5a4a7443100a6d5c5
F src/attach.c 08235ab62ed5ccc93c22bf36e640d19effcd632319615851bccf724ec9341333
F src/auth.c 4c1ea890e0069ad73bead5d17a5b12c34cfa4f1a24175c8147ea439b64be271c
F src/backup.c 5c97e8023aab1ce14a42387eb3ae00ba5a0644569e3476f38661fa6f824c3523
F src/bitvec.c 9eac5f42c11914d5ef00a75605bb205e934f435c579687f985f1f8b0995c8645
F src/bitvec.c 501daeef838fa82a9fb53540d72f29e3d9172c8867f1e19f94f681e2e20b966e
F src/btmutex.c 79a43670447eacc651519a429f6ece9fd638563cf95b469d6891185ddae2b522
F src/btree.c 8c5592c618741c5fc9733e7efe3927bfafad3e999d15b0a0f3f1d3f3e17b919e
F src/btree.h 55066f513eb095db935169dab1dc2f7c7a747ef223c533f5d4ad4dfed346cbd0
F src/btreeInt.h 98aadb6dcb77b012cab2574d6a728fad56b337fc946839b9898c4b4c969e30b6
F src/build.c 3a1840d9d171ce2d24f4c1f7acda7266ab796c664290c1acba65ff98ce2bd01e
F src/btree.c 68f32f71c33ab571b8d706940108c448e165d0c3d7317a1b5883d2b9dfcf2512
F src/btree.h bdeeb35614caa33526b603138f04c8d07a3f90a1300b5ade76848b755edf2027
F src/btreeInt.h caa893e74d2261fb0ff1681fce998533c0552858e882bd04fc6805075f5f6e75
F src/build.c 08697d6a4df78f8e289582eb58473445492319676f81cc4794ef4056d36ae5fd
F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
F src/ctime.c b224d3db0f28c4a5f1407c50107a0a8133bd244ff3c7f6f8cedeb896a8cf1b64
F src/ctime.c 193f6f9a75204274b7e7f45ac6d6517c12c70b55a5dfb39312dfc3a52e2a8138
F src/date.c 89ce1ff20512a7fa5070ba6e7dd5c171148ca7d580955795bf97c79c2456144a
F src/dbpage.c 80e46e1df623ec40486da7a5086cb723b0275a6e2a7b01d9f9b5da0f04ba2782
F src/dbstat.c 73362c0df0f40ad5523a6f5501224959d0976757b511299bf892313e79d14f5c
@ -719,7 +727,7 @@ F src/delete.c 444c4d1eaac40103461e3b6f0881846dd3aafc1cec1dd169d3482fa331667da7
F src/expr.c 6d5f2c38fe3ec06a7eac599dac822788b36064124e20112a844e9cd5156cb239
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c 928ed2517e8732113d2b9821aa37af639688d752f4ea9ac6e0e393d713eeb76f
F src/func.c df400a1d3f4625997d4dd8a81951c303e066277c29b861d37e03cd152d7858dd
F src/func.c ce9dc15867388c76894fa67b3500f5726579b766b00ba617a2ad46c16ca76c1e
F src/global.c 61a419dd9e993b9be0f91de4c4ccf322b053eb829868e089f0321dd669be3b90
F src/hash.c 9ee4269fb1d6632a6fecfb9479c93a1f29271bddbbaf215dd60420bcb80c7220
F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51
@ -727,9 +735,9 @@ F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
F src/insert.c f8d1a0f8ee258411009c6b7f2d93170e351bd19f5ad89d57e1180644297cbe70
F src/json.c 68a98c020c22127f2d65f08855f7fc7460ff352a6ce0b543d8931dde83319c22
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
F src/legacy.c 5fff8efbb4f20396136beb913fff323686cff8f6f5d7d728189f68e98f7310bb
F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36
F src/main.c e7b53893f9fb3ad76baa8513f85c167b34d5c8e25ce64608db440f5637d0fe9e
F src/main.c def50c462ce61f722196f7615d79374e2e5b9e856cce2991192faea3b9dbd33c
F src/malloc.c 410e570b30c26cc36e3372577df50f7a96ee3eed5b2b161c6b6b48773c650c5e
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c 3bb59158c38e05f6270e761a9f435bf19827a264c13d1631c58b84bdc96d73b2
@ -750,34 +758,34 @@ F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63
F src/os_common.h 6c0eb8dd40ef3e12fe585a13e709710267a258e2c8dd1c40b1948a1d14582e06
F src/os_kv.c 4d39e1f1c180b11162c6dc4aa8ad34053873a639bac6baae23272fc03349986a
F src/os_setup.h 6011ad7af5db4e05155f385eb3a9b4470688de6f65d6166b8956e58a3d872107
F src/os_unix.c 6e3e4fc75904ff85184091dbab996e6e35c1799e771788961cc3b4fcbe8f852c
F src/os_unix.c e33663c5a819e2c8532eedcefb1ea1ad7c1f0c9ac42e2149f632a642c2f9e621
F src/os_win.c 6ff43bac175bd9ed79e7c0f96840b139f2f51d01689a638fd05128becf94908a
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c b08600ebf0db90b6d1e9b8b6577c6fa3877cbe1a100bd0b2899e4c6e9adad4b3
F src/pager.h 4b1140d691860de0be1347474c51fee07d5420bd7f802d38cbab8ea4ab9f538a
F src/parse.y a7a8d42eeff01d267444ddb476029b0b1726fb70ae3d77984140f17ad02e2d61
F src/pager.c 0a82bdc31f09ab22fca997c9afb527ef52ec95a96f0013fdf9d7673ba25cbbbc
F src/pager.h 60eded76163c6a09d2b73cc40de117012d62f6d1222614c9261d07a73e20ac2e
F src/parse.y 8388b36e6cd15ebc5c1796cb72dd0a67a04abc446f52838ab3a2e8591487b2f1
F src/pcache.c 588cc3c5ccaaadde689ed35ce5c5c891a1f7b1f4d1f56f6cf0143b74d8ee6484
F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5
F src/pcache1.c 49516ad7718a3626f28f710fa7448ef1fce3c07fd169acbb4817341950264319
F src/pragma.c 52bfbf6dfd668b69b5eb9bd1186e3a67367c8453807150d6e75239229924f684
F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7
F src/prepare.c d99931f45416652895e502328ca49fe782cfc4e1ebdcda13b3736d991ebf42ce
F src/printf.c 6a87534ebfb9e5346011191b1f3a7ebc457f5938c7e4feeea478ecf53f6a41b2
F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
F src/pcache1.c 437282ad81350c98a8592425b19e1b4e132196f7a81aef49b2ca39c50937b827
F src/pragma.c 08472536cf1ecc38947d56f6cb2c8d8b3f6a26722451a38614c41a5b0cbcfc52
F src/pragma.h 6ebbdee90ed56a892d2c728e27fd9c1ce48c8a28841888d0c6c147946b38cb25
F src/prepare.c 140ce8271ba7da946668ad2e0c68fe14419319cb6c68cfcd660186557c271969
F src/printf.c 9480e90343dfde2406eeb25ff072774a77453d0f57fcd6495102f915dcc26a82
F src/random.c 9bd018738ec450bf35d28050b4b33fa9a6eebf3aaefb1a1cff42dc14a7725673
F src/resolve.c 2c127880c0634962837f16f2f48a295e514357af959330cc038de73015d5b5e8
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
F src/select.c 4b14337a2742f0c0beeba490e9a05507e9b4b12184b9cd12773501d08d48e3fe
F src/select.c 108baa344f34a17a0723067d28b5b4b7da937d02ddc2d7c1a39a4a4815628ca4
F src/shell.c.in 40de636c1d90fb8a9ca7f49dc8f50d930f1b60736e73aca5eb37c4c7d0e47f9d
F src/sqlite.h.in 77f55bd1978a04a14db211732f0a609077cf60ba4ccf9baf39988f508945419c
F src/sqlite.h.in b7ff496637807ae88b2557039fc940518db328bf5d5621e2f7c048dfba32a52b
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54
F src/sqliteInt.h 889cd632f4386bbd8619b166abb7d25f1c8ce6514e90cb7f22f63bd530fc6107
F src/sqliteInt.h 55838cada3b79d88a891e557759f444f2a007c77f61f7a731d00d1df6d95e555
F src/sqliteLimit.h 6878ab64bdeb8c24a1d762d45635e34b96da21132179023338c93f820eee6728
F src/status.c cb11f8589a6912af2da3bb1ec509a94dd8ef27df4d4c1a97e0bcf2309ece972b
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
F src/tclsqlite.c c6888598f08dee3d9112a38ef42c8f5c89ca7f3190f4694744d0b84250f4bf8c
F src/tclsqlite.h c6af51f31a2b2172d674608763a4b98fdf5cd587e4025053e546fb8077757262
F src/test1.c 3f18399557d954bc85f4564aec8ea1777d2161a81d98a3ff6c9e9046bf3554c1
F src/test1.c ecbd27140e63d6d70e221ac7e2aa565a13359c126f432b4469ad80f5d66d62ee
F src/test2.c 7ebc518e6735939d8979273a6f7b1d9b5702babf059f6ad62499f7f60a9eb9a3
F src/test3.c e7573aa0f78ee4e070a4bc8c3493941c1aa64d5c66d4825c74c0f055451f432b
F src/test4.c 13e57ae7ec7a959ee180970aef09deed141252fe9bb07c61054f0dfa4f1dfd5d
@ -791,13 +799,13 @@ F src/test_backup.c bd901e3c116c7f3b3bbbd4aae4ce87d99b400c9cbb0a9e7b4610af451d97
F src/test_bestindex.c 3401bee51665cbf7f9ed2552b5795452a8b86365e4c9ece745b54155a55670c6
F src/test_blob.c bcdf6a6c22d0bcc13c41479d63692ef413add2a4d30e1e26b9f74ab85b9fb4d5
F src/test_btree.c 28283787d32b8fa953eb77412ad0de2c9895260e4e5bd5a94b3c7411664f90d5
F src/test_config.c 345b8e383f71cecc36d0fa05f2f06639c254b188f98101c3c97749df43037086
F src/test_config.c 72fcae7f6dbe72975654a018922dbf1accc5ec6ed957010d82edc26ba978cd4f
F src/test_delete.c e2fe07646dff6300b48d49b2fee2fe192ed389e834dd635e3b3bac0ce0bf9f8f
F src/test_demovfs.c 3efa2adf4f21e10d95521721687d5ca047aea91fa62dd8cc22ac9e5a9c942383
F src/test_devsym.c 649434ed34d0b03fbd5a6b42df80f0f9a7e53f94dd1710aad5dd8831e91c4e86
F src/test_fs.c c411c40baba679536fc34e2679349f59d8225570aed3488b5b3ef1908525a3d5
F src/test_func.c 8c0e89192f70fac307822d1ac2911ee51751288780b3db0c5ab5ca75fa0fe851
F src/test_hexio.c 0f777bf9fbb2684bb4978372bacc90ef7337d5d9e3cebe067a9409941e88bacf
F src/test_hexio.c 35704e7db83fe1a0e1a014bacb17de97d7d1e89af3356a9da04e37cdaa087c09
F src/test_init.c 17313332d58e90defc527129d5eda4a08bd6b6e8de7207a231523c8d98fb445e
F src/test_intarray.c e4216aadee9df2de7d1aee7e70f6b22c80ee79ece72a63d57105db74217639e5
F src/test_intarray.h 6c3534641108cd1bea517a8e117dcba237081310a29a4c35bd2190caa8972293
@ -816,9 +824,9 @@ F src/test_quota.h 2a8ad1952d1d2ca9af0ce0465e56e6c023b5e15d
F src/test_rtree.c d844d746a3cc027247318b970025a927f14772339c991f40e7911583ea5ed0d9
F src/test_schema.c b06d3ddc3edc173c143878f3edb869dd200d57d918ae2f38820534f9a5e3d7d9
F src/test_sqllog.c 540feaea7280cd5f926168aee9deb1065ae136d0bbbe7361e2ef3541783e187a
F src/test_superlock.c 18355ca274746aa6909e3744163e5deb1196a85d5bc64b9cd377273cef626da7
F src/test_superlock.c 388f4741997beb395b6731fd0c4c8e525940bc6cdcbabead0136783450da40d0
F src/test_syscall.c 9ad7ab39910c16d29411678d91b0d27a7a996a718df5ee93dcd635e846d0275c
F src/test_tclsh.c 6077f2bdc6b4ea2bace2a0cd6ea48e0a4651007ae7382c13efc0c495eb0c6956
F src/test_tclsh.c efa390787ecfbae22e0a0a05d9ee771a9db78ffd84e2eea092899c2d80d57b0e
F src/test_tclvar.c ae873248a0188459b1c16ca7cc431265dacce524399e8b46725c2b3b7e048424
F src/test_thread.c d7a8bcea7445f37cc2a1f7f81dd6059634f45e0c61bfe80182b02872fb0328bb
F src/test_vdbecov.c 5c426d9cd2b351f5f9ceb30cabf8c64a63bfcad644c507e0bd9ce2f6ae1a3bf3
@ -832,16 +840,16 @@ F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c 3f703cacdab728d7741e5a6ac242006d74fe1c2754d4f03ed889d7253259bd68
F src/treeview.c 88aa39b754f5ef7214385c1bbbdd2f3dc20efafeed0cf590e8d1199b9c6e44aa
F src/trigger.c 0bb986a5b96047fd597c6aac28588853df56064e576e6b81ba777ef2ccaac461
F src/update.c 0e01aa6a3edf9ec112b33eb714b9016a81241497b1fb7c3e74332f4f71756508
F src/update.c 2dd1b745acc9253df1b210ac69137c7a6b290e561d3f42da24418c4e807e889b
F src/upsert.c 215328c3f91623c520ec8672c44323553f12caeb4f01b1090ebdca99fdf7b4f1
F src/utf.c f23165685a67b4caf8ec08fb274cb3f319103decfb2a980b7cfd55d18dfa855e
F src/util.c 5d1a0134cf4240648d1c6bb5cc8efaca0ea2b5d5c840985aec7e947271f04375
F src/vacuum.c b763b6457bd058d2072ef9364832351fd8d11e8abf70cbb349657360f7d55c40
F src/vdbe.c be5f58bc29f60252e041a618eae59e8d57d460ba136c5403cf0abf955560c457
F src/vdbe.h c2549a215898a390de6669cfa32adba56f0d7e17ba5a7f7b14506d6fd5f0c36a
F src/vdbeInt.h 949669dfd8a41550d27dcb905b494f2ccde9a2e6c1b0b04daa1227e2e74c2b2c
F src/vacuum.c 25e407a6dc8b288fa4295b3d92fa9ce9318503e84df53cdf403a50fccbc1ba31
F src/vdbe.c fea60cfe46d8839a08e4680d90a96898d542d3f6d7954e2e99cd92596f1a01f8
F src/vdbe.h e439c7b44191828413f461eac23360ffbf0513fe57d129853f690c4ea4d46746
F src/vdbeInt.h 92b7eabbaadbe8127741cd96e7e39c4834c2bb0b75987d5f6251975f47233690
F src/vdbeapi.c 80235ac380e9467fec1cb0883354d841f2a771976e766995f7e0c77f845406df
F src/vdbeaux.c 25d685cafe119ff890c94345e884ea558a6b5d823bfa52ba708eb8ff3c70aa71
F src/vdbeaux.c 4f7e3fc42eea9612ae59ff5f123280507935be53d0d2e9ea474957b30f9e1f29
F src/vdbeblob.c 255be187436da38b01f276c02e6a08103489bbe2a7c6c21537b7aecbe0e1f797
F src/vdbemem.c 831a244831eaa45335f9ae276b50a7a82ee10d8c46c2c72492d4eb8c98d94d89
F src/vdbesort.c d0a3c7056c081703c8b6d91ad60f17da5e062a5c64bf568ed0fa1b5f4cae311f
@ -849,8 +857,8 @@ F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf8
F src/vdbevtab.c fc46b9cbd759dc013f0b3724549cc0d71379183c667df3a5988f7e2f1bd485f3
F src/vtab.c 5fb499d20494b7eecaadb7584634af9afcb374cb0524912b475fcb1712458a1b
F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c 887fc4ca3f020ebb2e376f222069570834ac63bf50111ef0cbf3ae417048ed89
F src/wal.h ba252daaa94f889f4b2c17c027e823d9be47ce39da1d3799886bbd51f0490452
F src/wal.c c5c772a50621548398bff23df38cc338c8b874188d1fa7bf975f3381f6071126
F src/wal.h 8c59ee7a835574396d7cbd04626d11fd849613e361a46e7e9519091ab03bdb29
F src/walker.c d5006d6b005e4ea7302ad390957a8d41ed83faa177e412f89bc5600a7462a014
F src/where.c c046dd58c3410f7b7528e1e6317cb876398557bad346d568ed8562321a7d002d
F src/whereInt.h a5d079c346a658b7a6e9e47bb943d021e02fa1e6aed3b964ca112112a4892192
@ -941,6 +949,7 @@ F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f
F test/badutf2.test f310fd3b24a491b6b77bccdf14923b85d6ebcce751068c180d93a6b8ff854399
F test/basexx1.test d8a50f0744b93dca656625597bcd3499ff4b9a4ea2a82432b119b7d46e3e0c08
F test/bc_common.tcl b5e42d80305be95697e6370e015af571e5333a1c
F test/bc_test1.c e0a092579552e066ed4ce7bcdaecfa69c4aacc8d
F test/bestindex1.test 856a453dff8c68b4568601eed5a8b5e20b4763af9229f3947c215729ed878db0
F test/bestindex2.test 394ff8fbf34703391247116d6a44e1c50ee7282236ee77909044573cefc37bc0
F test/bestindex3.test 34bea272b0e0f835651b16a3931dbe7ac927039be6b2e1cb617bbe1d584b492b
@ -1014,6 +1023,18 @@ F test/collateB.test 1e68906951b846570f29f20102ed91d29e634854ee47454d725f2151eca
F test/colmeta.test 2c765ea61ee37bc43bbe6d6047f89004e6508eb1
F test/colname.test 387e880eeac0889900f7b3e9703c375515f5d323f71fd4f2bb5428a4ac8e2023
F test/columncount.test 6fe99c2f35738b0129357a1cf3fa483f76140f4cd8a89014c88c33c876d2638f
F test/commitstatus.test d5a871506ce5944a29afb7e65ce47ca7f76cadc1d09775022830258fdd6168a1
F test/concfault.test 500f17c3fcfe7705114422bcc6ddd3c740001a43
F test/concfault2.test 34b3fd258836aa305475d00e804c7450ade92f0de0bf9fa620e701446669bb12
F test/concurrent.test fb624ddac9b008f347685911f90b6b5a022fd0a3f884c0ffef8056bc440e5d76
F test/concurrent2.test 847cd11edc82229a95e1eaf88b55c974430b0f669cefd67a453d274e3480981c
F test/concurrent3.test 82923fc2ea7321144b4448f98ea38aa316ddceef9020a392c5f6dea536506434
F test/concurrent4.test e0b12cd467137e50259df3b4f837507e82aaa07c35941c88664dc8ed1d089c44
F test/concurrent5.test 5031c87134fee85352ac33ad33c81c6ec4f07d5547fe2429e1d38492a797f6bc
F test/concurrent6.test a7860e9ca13bb5fb76bcf41c5524fbfa9c37e6e258ecf84ffb5748a272488c67
F test/concurrent7.test b96fa5c4cfdf8d5c0bc66b6934214500bad0260884a736f054ccc76e81aae85d
F test/concurrent8.test b93937e74a8efb8b84f2fea7595b53418c5f29777bbe9cbdb5dc219b3dd72a7d
F test/concurrent9.test 25b6db3a56ee87208144a3793678d0dce5e10c5a600b1a13d4befb4ef19780c6
F test/conflict.test b705cddf025a675d3c13d62fa78ab1e2696fb8e07a3d7cccce1596ff8b301492
F test/conflict2.test 5557909ce683b1073982f5d1b61dfb1d41e369533bfdaf003180c5bc87282dd1
F test/conflict3.test 81865d9599609aca394fb3b9cd5f561d4729ea5b176bece3644f6ecb540f88ac
@ -1027,7 +1048,7 @@ F test/corrupt6.test fc6a891716139665dae0073b6945e3670bf92568
F test/corrupt7.test ffa86896fe63a3d00b0a131e1e64f402e4da9f7e5d89609d6501c851e511d73a
F test/corrupt8.test 2399dfe40d2c0c63af86706e30f3e6302a8d0516
F test/corrupt9.test 730a3db08d4ab9aa43392ea30d9c2b4879cbff85
F test/corruptA.test 112f4b2ae0b95ebf3ea63718642fb969a93acea557ace3a307234d19c245989b
F test/corruptA.test 56e8f321adaf3411960e9d2c7136669d8e1a91cbde6cf401ea84e6d6c7ccbe10
F test/corruptB.test 73a8d6c0b9833697ecf16b63e3c5c05c945b5dec
F test/corruptC.test 7d6d9e907334ea3ccb7111a0656cafa30a28f8a5f2aaf1c45ad712236302856a
F test/corruptD.test 614320aa519f6bf6c7dd2f581f9513ff7b6826954180cca1a606d0e25ea084a3
@ -1040,7 +1061,7 @@ F test/corruptJ.test 4d5ccc4bf959464229a836d60142831ef76a5aa4
F test/corruptK.test ac13504593d89d69690d45479547616ed12644d42b5cb7eeb2e759a76fc23dcb
F test/corruptL.test 652fc8ac0763a6fd3eb28b951d481924167b2d9936083bcc68253b2274a0c8fe
F test/corruptM.test 7d574320e08c1b36caa3e47262061f186367d593a7e305d35f15289cc2c3e067
F test/corruptN.test 7c099d153a554001b4fb829c799b01f2ea6276cbc32479131e0db0da4efd9cc4
F test/corruptN.test 57985a0737f5e008283a91c24630cd3c7003d3c7b62824edaa21258e46da9455
F test/cost.test cc434a026b1e9d0d98137a147e24e5daf1b1ad09e9ff7da63b34c83ddd136d92
F test/count.test cd4bd531066e8d77ef8fe1e3fc8253d042072e117ccab214b290cf83f1602249
F test/countofview.test 4088e461a10ee33e69803c177a69aa1d7bba81a9ffc2df66d76465a22ca7fdfc
@ -1184,7 +1205,7 @@ F test/fts3conf.test c9cd45433b6787d48a43e84949aa2eb8b3b3d242bac7276731c1476290d
F test/fts3corrupt.test 6732477c5ace050c5758a40a8b5706c8c0cccd416b9c558e0e15224805a40e57
F test/fts3corrupt2.test e318f0676e5e78d5a4b702637e2bb25265954c08a1b1e4aaf93c7880bb0c67d0
F test/fts3corrupt3.test 0d5b69a0998b4adf868cc301fc78f3d0707745f1d984ce044c205cdb764b491f
F test/fts3corrupt4.test 48bd57baed9654e511709a02dbef2d22ee54c012ad466e8648f0f825233faa08
F test/fts3corrupt4.test ab870772191b9e1d4f145eac340bc724e107d419bcfee0b197dc2026a2120c17
F test/fts3corrupt5.test 0549f85ec4bd22e992f645f13c59b99d652f2f5e643dac75568bfd23a6db7ed5
F test/fts3corrupt6.test f417c910254f32c0bc9ead7affa991a1d5aec35b3b32a183ffb05eea78289525
F test/fts3cov.test 7eacdbefd756cfa4dc2241974e3db2834e9b372ca215880e00032222f32194cf
@ -1514,12 +1535,11 @@ 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/pendingrace.test e99efc5ab3584da3dfc8cd6a0ec4e5a42214820574f5ea24ee93f1d84655f463
F test/percentile.test 52ba89d6ee6b65f770972b67dace358bab7cdbd532803d3db157845268e789cd
F test/permutations.test 405542f1d659942994a6b38a9e024cf5cfd23eaa68c806aeb24a72d7c9186e80
F test/permutations.test 3ed69d571619ab452d06f995c7c1d3f100588b20c1d47595819d9289adedabfb
F test/pg_common.tcl 3b27542224db1e713ae387459b5d117c836a5f6e328846922993b6d2b7640d9f
F test/pragma.test 11cb9310c42f921918f7f563e3c0b6e70f9f9c3a6a1cf12af8fccb6c574f3882
F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
@ -1544,7 +1564,7 @@ F test/quote.test 7b01b2a261bc26d9821aea9f4941ce1e08191d62fc55ba8862440fb3a59197
F test/randexpr1.tcl 40dec52119ed3a2b8b2a773bce24b63a3a746459
F test/randexpr1.test eda062a97e60f9c38ae8d806b03b0ddf23d796df
F test/rbu.test 168573d353cd0fd10196b87b0caa322c144ef736
F test/rdonly.test 64e2696c322e3538df0b1ed624e21f9a23ed9ff8
F test/rdonly.test 21e99ee237265d0cf95a0c84b50c784e834acaa4ef05d92a27b262626a656682
F test/readonly.test 69a7ccec846cad2e000b3539d56360d02f327061dc5e41f7f9a3e01f19719952
F test/recover.test a163a156ea9f2beea63fa83c4dcd8dea6e57b8a569fc647155e3d2754eaac1b5
F test/regexp1.test 8f2a8bc1569666e29a4cee6c1a666cd224eb6d50e2470d1dc1df995170f3e0f1
@ -1575,11 +1595,11 @@ F test/rowvaluefault.test 963ae9cdaed30a85a29668dd514e639f3556cae903ee9f172ea972
F test/rowvaluevtab.test cd9747bb3f308086944c07968f547ad6b05022e698d80b9ffbdfe09ce0b8da6f
F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798
F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09
F test/savepoint.test 6e9804a17767f08432c7a5e738b9a8f4b891d243110b63d3a41d270d3d1378ec
F test/savepoint.test 63a120ec4fbbd5025b238c259d12ed0516fbf4bca6384041cb995ade9a5f00d2
F test/savepoint2.test 9b8543940572a2f01a18298c3135ad0c9f4f67d7
F test/savepoint4.test c8f8159ade6d2acd9128be61e1230f1c1edc6cc0
F test/savepoint5.test 0735db177e0ebbaedc39812c8d065075d563c4fd
F test/savepoint6.test f41279c5e137139fa5c21485773332c7adb98cd7
F test/savepoint6.test 48a645a7bb3a59a6fcf06a7364cfe5b655c336760de39068f7c241b0fc80d963
F test/savepoint7.test cde525ea3075283eb950cdcdefe23ead4f700daa
F test/savepointfault.test f044eac64b59f09746c7020ee261734de82bf9b2
F test/scanstatus.test b249328caf4d317e71058006872b8012598a5fa045b30bf24a81eeff650ab49e
@ -1714,7 +1734,7 @@ F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30
F test/temptable2.test 76821347810ecc88203e6ef0dd6897b6036ac788e9dd3e6b04fd4d1631311a16
F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637
F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc
F test/tester.tcl 2c203a2dd664298f239f0ec3ce22fbc65b5f021c1e09edbae8452af8a694e052
F test/tester.tcl b4b9cc1520542d34ee061f9f12df0944d6ad1c438feba9db4078f6214e0a8111
F test/testrunner.tcl 982939f0f1835007298b92e52694c207d16ef79143993b35e5cbc9f0c585938b x
F test/testrunner_data.tcl dbc0bb1c5b912dfd1e32b25d544318e412edd6085bd5fc9e6619cb93a739b786
F test/thread001.test a0985c117eab62c0c65526e9fa5d1360dd1cac5b03bde223902763274ce21899
@ -1728,7 +1748,7 @@ F test/thread3.test a12656a56cdf67acb6a2ff7638826c6d6a645f79909d86df521045ad31cf
F test/thread_common.tcl 334639cadcb9f912bf82aa73f49efd5282e6cadd
F test/threadtest1.c 6029d9c5567db28e6dc908a0c63099c3ba6c383b
F test/threadtest2.c a70a8e94bef23339d34226eb9521015ef99f4df8
F test/threadtest3.c 655bff6c0895ec03f014126aa65e808fac9aae8c5a7a7da58a510cbe8b43b781
F test/threadtest3.c e947b396444f7992a942cd8db0f01589ede90dd250ec802fe800cc90bbee21e3
F test/threadtest4.c c1e67136ceb6c7ec8184e56ac61db28f96bd2925
F test/threadtest5.c 9b4d782c58d8915d7e955ff8051f3d03628bda0d33b82971ea8c0f2f2808c421
F test/time-wordcount.sh 8e0b0f8109367827ad5d58f5cc849705731e4b90
@ -1904,7 +1924,9 @@ F test/triggerF.test 5d76f0a8c428ff87a4d5ed52da06f6096a2c787a1e21b846111dfac4123
F test/triggerG.test 2b816093c91ba73c733cfa8aedcc210ad819d72a98b1da30768a3c56505233e9
F test/triggerupfrom.test d1f9e56090408115c522bee626cc33a2f3370f627a5e341d832589d72e3aa271
F test/trustschema1.test d2996bb284859c99956ac706160eab9f086919da738d19bfef3ac431cce8fd47
F test/tt3_bcwal2.c 8351577477ce58c3b21a1772c28e73ec58538c44be4a183ff7485d6814bd5385
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
@ -1937,7 +1959,7 @@ F test/upsert3.test 88d7d590a1948a9cb6eac1b54b0642f67a9f35a1fc0f19b200e97d5d39e3
F test/upsert4.test 25d2a1da92f149331ae0c51ca6e3eee78189577585eab92de149900d62994fa5
F test/upsert5.test 9953b180d02d1369cdbb6c73c900834e5fef8cb78e98e07511c8762ec21cc176
F test/upsertfault.test f21ca47740841fdb4d61acfa7b17646d773e67724fe8c185b71c018db8a94b35
F test/uri.test c1abaaaa28e9422d61e5f3f9cbc8ef993ec49fe802f581520731708561d49384
F test/uri.test 8f27eaa41804099fca15101e30fd1b29aebbebf32d4e3c24614fa6319216936d
F test/uri2.test 9d3ba7a53ee167572d53a298ee4a5d38ec4a8fb7
F test/userauth01.test e740a2697a7b40d7c5003a7d7edaee16acd349a9
F test/utf16align.test 9fde0bb5d3a821594aa68c6829ab9c5453a084384137ebb9f6153e2d678039da
@ -1983,8 +2005,21 @@ F test/vtab_shared.test 5253bff2355a9a3f014c15337da7e177ab0ef8ad
F test/vtabdistinct.test 7688f0889358f849fd60bbfde1ded38b014b18066076d4bfbb75395804dfe072
F test/vtabdrop.test 65d4cf6722972e5499bdaf0c0d70ee3b8133944a4e4bc31862563f32a7edca12
F test/vtabrhs1.test 9b5ecbc74a689500c33a4b2b36761f9bcc22fcc4e3f9d21066ee0c9c74cf5f6c
F test/wal.test 519c550255c78f55959e9159b93ebbfad2b4e9f36f5b76284da41f572f9d27da
F test/wal2.test 44fe1cb4935dbbddfa0a34c2c4fd90f0ba8654d59b83c4136eb90fb327fd264f
F test/wal.test a5d6c7f4bd79251ed344229d96d44ecdfb896bdbd32b7e65f118756699c7e473
F test/wal2.test e89ca97593b5e92849039f6b68ce1719a853ef20fa22c669ec1ac452fbc31cab
F test/wal2big.test 829141cbecdda4329db8fa38705424c4a73db72a06b9540b06811a825d330409
F test/wal2concurrent.test 7fc3e570073683a2a28f42bda46ecf516f5bc82afd703c1fbf4aa38e18fb3361
F test/wal2fault.test 2e8e60cacd5bcd451618aeffd05f676894d17202d3e2986e288d36e2c5993249
F test/wal2lock.test 0ef98d72dc6bcf7711dedd684760488400d9a9a6eec0dc5d3822060437793552
F test/wal2openclose.test 2b26be723ea7f4263c8d5d70b37efd1c359561a0526e39466c45fe8e6478daee
F test/wal2recover.test 348a3f2a4c79359790f70fd692dcd0c8f04e42a85365e688778c945e10bae02b
F test/wal2recover2.test a7eece9892b125ef92343bba9e06edd5f6ad20c548dcbf79e3f2ab759f31ef84
F test/wal2recover3.test 4a91689e165a38bc401736e6518188c2b0ff4fa1566d1810b8867536db128177
F test/wal2rewrite.test 6ca6f631ffcf871240beab5f02608913fd075c6d0d31310b026c8383c65c9f9c
F test/wal2rollback.test 23adc4a099b23f6aaea8b04fdca1c35861d887dd80f8be7da2d5273eb777e428
F test/wal2savepoint.test 3793a0ae97011fca358f79775f5d7d9f85da75c8e67686e2e19713da0cb0d99c
F test/wal2simple.test 320a08927f307d0ead26fa3fcef4e3f64279f49be9504f918cb33294f75aeec8
F test/wal2snapshot.test f6c3945bea572fd8745774e95b8dca1e5832c696e2251bb0db33391ee567fe60
F test/wal3.test 5de023bb862fd1eb9d2ad26fa8d9c43abb5370582e5b08b2ae0d6f93661bc310
F test/wal4.test 4744e155cd6299c6bd99d3eab1c82f77db9cdb3c
F test/wal5.test 9c11da7aeccd83a46d79a556ad11a18d3cb15aa9
@ -1993,7 +2028,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 4589f701d5527ace2eba43823c96c2177e1f9dd2a6098256ee2203a0a313c13a
F test/wal_common.tcl 204d1721ac13c5e0c7fae6380315b5ab7f4e8423f580d826c5e9df1995cb018d
F test/walbak.test 018d4e5a3d45c6298d11b99f09a8ef6876527946
F test/walbig.test f437473a16cfb314867c6b5d1dbcd519e73e3434
F test/walblock.test be48f3a75eff0b4456209f26b3ce186c2015497d
@ -2010,10 +2045,10 @@ F test/walnoshm.test 84ca10c544632a756467336b7c3b864d493ee496
F test/waloverwrite.test dad2f26567f1b45174e54fbf9a8dc1cb876a7f03
F test/walpersist.test 8d78a1ec91299163451417b451a2bac3481f8eb9f455b1ca507a6625c927ca6e
F test/walprotocol.test 1b3f922125e341703f6e946d77fdc564d38fb3e07a9385cfdc6c99cac1ecf878
F test/walprotocol2.test 7d3b6b4bf0b12f8007121b1e6ef714bc99101fb3b48e46371df1db868eebc131
F test/walprotocol2.test 7e4bedd5ee83607e2928ac438bf7332a396b980d3e02aa0746509ce11ad1f13c
F test/walro.test cb438d05ba0d191f10b688e39c4f0cd5b71569a1d1f4440e5bdf3c6880e08c20
F test/walro2.test 33955a6fd874dd9724005e17f77fef89d334b3171454a1256fe4941a96766cdc
F test/walrofault.test c70cb6e308c443867701856cce92ad8288cd99488fa52afab77cca6cfd51af68
F test/walrofault.test 5a25f91c16a68bae65edec7cdef4495e5c6494c8408743fe9b29045fa6665cd0
F test/walseh1.test bae700eb99519b6d5cd3f893c04759accc5a59c391d4189fe4dd6995a533442b
F test/walsetlk.test 34c901443b31ab720afc463f5b236c86ca5c4134402573dce91aa0761de8db5a
F test/walshared.test 42e3808582504878af237ea02c42ca793e8a0efaa19df7df26ac573370dbc7a3
@ -2132,13 +2167,13 @@ F tool/max-limits.c cbb635fbb37ae4d05f240bfb5b5270bb63c54439
F tool/merge-test.tcl de76b62f2de2a92d4c1ca4f976bce0aea6899e0229e250479b229b2a1914b176
F tool/mkautoconfamal.sh cbdcf993fa83dccbef7fb77b39cdeb31ef9f77d9d88c9e343b58d35ca3898a6a
F tool/mkccode.tcl 86463e68ce9c15d3041610fedd285ce32a5cf7a58fc88b3202b8b76837650dbe x
F tool/mkctimec.tcl e3af51acc2ef92062fe6d622de010a27a34b497258a248dada04388b916c61c6 x
F tool/mkctimec.tcl 24074f287208f82f6dfbbe81f9e911849171a9c633961901f2c056cddb8d1663 x
F tool/mkkeywordhash.c 6b0be901c47f9ad42215fc995eb2f4384ac49213b1fba395102ec3e999acf559
F tool/mkmsvcmin.tcl d76c45efda1cce2d4005bcea7b8a22bb752e3256009f331120fb4fecb14ebb7a
F tool/mkopcodec.tcl 33d20791e191df43209b77d37f0ff0904620b28465cca6990cf8d60da61a07ef
F tool/mkopcodeh.tcl 2b4e6967a670ef21bf53a164964c35c6163277d002a4c6f56fa231d68c88d023
F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa
F tool/mkpragmatab.tcl 32e359ccb21011958a821955254bd7a5fa7915d01a8c16fed91ffc8b40cb4adf
F tool/mkpragmatab.tcl d03737ad2ac48928d8225d1c571e487b9b7e73e8c1bdcabd61d69b244614408b
F tool/mkshellc.tcl b7adf08b82de60811d2cb6af05ff59fc17e5cd6f3e98743c14eaaa3f8971fed0
F tool/mksourceid.c 36aa8020014aed0836fd13c51d6dc9219b0df1761d6b5f58ff5b616211b079b9
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
@ -2184,6 +2219,8 @@ F tool/stack_usage.tcl f8e71b92cdb099a147dad572375595eae55eca43
F tool/stripccomments.c 20b8aabc4694d0d4af5566e42da1f1a03aff057689370326e9269a9ddcffdc37
F tool/symbols-mingw.sh 4dbcea7e74768305384c9fd2ed2b41bbf9f0414d
F tool/symbols.sh 1612bd947750e21e7b47befad5f6b3825b06cce0705441f903bf35ced65ae9b9
F tool/tserver.c 17b7f0b06f4e776e26220889941a86936b3c56ad18608baadc8faa00b7bd46ee
F tool/tserver_test.tcl 64415a134749ac3f38c9abd2bb95c7387a9b44e5116419487fd008cff8a459db
F tool/varint.c 5d94cb5003db9dbbcbcc5df08d66f16071aee003
F tool/vdbe-compress.tcl 1dcb7632e57cf57105248029e6e162fddaf6c0fccb3bb9e6215603752c5a2d4a
F tool/vdbe_profile.tcl 3ac5a4a9449f4baf77059358ea050db3e34395ccf59c5464d29b91746d5b961e
@ -2212,8 +2249,8 @@ F vsixtest/vsixtest.tcl 6195aba1f12a5e10efc2b8c0009532167be5e301abe5b31385638080
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P c0c6e9abebf76358625f30a179658319b260baba6eded2a4c5ad356143e36f97
R c9ec90e0d0ba3db3bca78a08a6c286b8
U drh
Z c8dd2f7c1df9f10ff01ca20270187b6f
P e0c0176793d41bef30e093975a87170981277dac20cde08e0fd3e0b6f40dca2f
R 40d5d8e3822854d0b02030b6b7d7027f
U dan
Z 1f420b344bc232a0c739cabd514cf0b5
# Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
60ac55c4b76355aaf7cbde38bf1f6082ff5612bf4ffc49ab69d00fd4e3d64e64
35aa893d4537d0b3605084a1f2f5529794e82af59b8893053815d3fcb4719a27

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;

File diff suppressed because it is too large Load Diff

View File

@ -357,6 +357,8 @@ sqlite3_uint64 sqlite3BtreeSeekCount(Btree*);
# define sqlite3BtreeSeekCount(X) 0
#endif
int sqlite3BtreeExclusiveLock(Btree *pBt);
#ifndef NDEBUG
int sqlite3BtreeCursorIsValid(BtCursor*);
#endif
@ -417,5 +419,4 @@ void sqlite3BtreeClearCache(Btree*);
# define sqlite3SchemaMutexHeld(X,Y,Z) 1
#endif
#endif /* SQLITE_BTREE_H */

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 */
@ -360,6 +364,12 @@ struct Btree {
#ifndef SQLITE_OMIT_SHARED_CACHE
BtLock lock; /* Object used to lock page 1 */
#endif
#ifndef SQLITE_OMIT_CONCURRENT
/* Return values for sqlite3_commit_status() requests:
** SQLITE_COMMIT_FIRSTFRAME, COMMIT_NFRAME.
*/
u32 aCommit[2];
#endif
};
/*
@ -456,7 +466,12 @@ 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() */
u64 *aCommitTime;
};
/*
@ -673,12 +688,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) (pBt->autoVacuum)
#else
#ifdef SQLITE_OMIT_AUTOVACUUM
#define ISAUTOVACUUM(pBt) 0
#else
#define ISAUTOVACUUM(pBt) (pBt->autoVacuum)
#endif
#ifdef SQLITE_OMIT_CONCURRENT
# define ISCONCURRENT 0
#else
# define ISCONCURRENT (pBt->pMap!=0)
#endif
#define REQUIRE_PTRMAP (ISAUTOVACUUM(pBt) || ISCONCURRENT)
/*
** This structure is passed around through all the PRAGMA integrity_check

View File

@ -5236,7 +5236,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;
@ -5251,7 +5251,7 @@ void sqlite3BeginTransaction(Parse *pParse, int type){
sqlite3VdbeUsesBtree(v, i);
}
}
sqlite3VdbeAddOp0(v, OP_AutoCommit);
sqlite3VdbeAddOp3(v, OP_AutoCommit, 0, 0, (type==TK_CONCURRENT));
}
/*

View File

@ -782,6 +782,7 @@ static const char * const sqlite3azCompileOpt[] = {
#ifdef SQLITE_VDBE_COVERAGE
"VDBE_COVERAGE",
#endif
"WAL2",
#ifdef SQLITE_WIN32_MALLOC
"WIN32_MALLOC",
#endif

View File

@ -559,8 +559,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
@ -586,6 +587,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]);
@ -594,7 +596,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);
}
}

View File

@ -49,6 +49,7 @@ int sqlite3_exec(
int nCol = 0;
char **azVals = 0;
sqlite3PrepareTimeSet(db->aSchemaTime, SCHEMA_TIME_BEFORE_PREPARE);
pStmt = 0;
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover);
assert( rc==SQLITE_OK || pStmt==0 );
@ -62,6 +63,7 @@ int sqlite3_exec(
}
callbackIsInit = 0;
sqlite3PrepareTimeSet(db->aSchemaTime, SCHEMA_TIME_BEFORE_STEP);
while( 1 ){
int i;
rc = sqlite3_step(pStmt);
@ -107,6 +109,7 @@ int sqlite3_exec(
}
}
sqlite3PrepareTimeSet(db->aSchemaTime, SCHEMA_TIME_BEFORE_FINALIZE);
if( rc!=SQLITE_ROW ){
rc = sqlite3VdbeFinalize((Vdbe *)pStmt);
pStmt = 0;

View File

@ -3337,7 +3337,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;
@ -5070,6 +5070,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

View File

@ -46,6 +46,16 @@
#include "sqliteInt.h"
#if SQLITE_OS_UNIX /* This file is used on unix only */
/* Turn this feature on in all builds for now */
#define SQLITE_MUTEXFREE_SHMLOCK 1
#define SQLITE_MFS_EXCLUSIVE 255
#ifndef SQLITE_MFS_NSHARD
# define SQLITE_MFS_NSHARD 8
#endif
#if SQLITE_MFS_NSHARD<1
# error "SQLITE_MFS_NSHARD must be greater than 0"
#endif
/*
** There are various methods for file locking used for concurrency
** control:
@ -1224,6 +1234,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
};
/*
@ -1380,6 +1394,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);
@ -2243,6 +2264,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);
}
@ -4083,6 +4112,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);
@ -4352,8 +4384,41 @@ struct unixShmNode {
#ifdef SQLITE_DEBUG
u8 nextShmId; /* Next available unixShm.id value */
#endif
#ifdef SQLITE_MUTEXFREE_SHMLOCK
/* In unix-excl mode, if SQLITE_MUTEXFREE_SHMLOCK is defined, all locks
** are stored in the following 64-bit value. There are in total 8
** shm-locking slots, each of which are assigned 8-bits from the 64-bit
** value. The least-significant 8 bits correspond to shm-locking slot
** 0, and so on.
**
** If the 8-bits corresponding to a shm-locking locking slot are set to
** 0xFF, then a write-lock is held on the slot. Or, if they are set to
** a non-zero value smaller than 0xFF, then they represent the total
** number of read-locks held on the slot. There is no way to distinguish
** between a write-lock and 255 read-locks. */
struct LockingSlot {
u32 nLock;
u64 aPadding[7];
} aMFSlot[3 + SQLITE_MFS_NSHARD*5];
#endif
};
/*
** Atomic CAS primitive used in multi-process mode. Equivalent to:
**
** int unixCompareAndSwap(u32 *ptr, u32 oldval, u32 newval){
** if( *ptr==oldval ){
** *ptr = newval;
** return 1;
** }
** return 0;
** }
*/
#define unixCompareAndSwap(ptr,oldval,newval) \
__sync_bool_compare_and_swap(ptr,oldval,newval)
/*
** Structure used internally by this VFS to record the state of an
** open shared memory connection.
@ -4374,6 +4439,9 @@ struct unixShm {
u8 id; /* Id of this connection within its unixShmNode */
u16 sharedMask; /* Mask of shared locks held */
u16 exclMask; /* Mask of exclusive locks held */
#ifdef SQLITE_MUTEXFREE_SHMLOCK
u8 aMFCurrent[8]; /* Current slot used for each shared lock */
#endif
};
/*
@ -4959,6 +5027,87 @@ shmpage_out:
return rc;
}
#ifdef SQLITE_MUTEXFREE_SHMLOCK
static int unixMutexFreeShmlock(
unixFile *pFd, /* Database file holding the shared memory */
int ofst, /* First lock to acquire or release */
int n, /* Number of locks to acquire or release */
int flags /* What to do with the lock */
){
struct LockMapEntry {
int iFirst;
int nSlot;
} aMap[9] = {
{ 0, 1 },
{ 1, 1 },
{ 2, 1 },
{ 3+0*SQLITE_MFS_NSHARD, SQLITE_MFS_NSHARD },
{ 3+1*SQLITE_MFS_NSHARD, SQLITE_MFS_NSHARD },
{ 3+2*SQLITE_MFS_NSHARD, SQLITE_MFS_NSHARD },
{ 3+3*SQLITE_MFS_NSHARD, SQLITE_MFS_NSHARD },
{ 3+4*SQLITE_MFS_NSHARD, SQLITE_MFS_NSHARD },
{ 3+5*SQLITE_MFS_NSHARD, 0 },
};
unixShm *p = pFd->pShm; /* The shared memory being locked */
unixShmNode *pShmNode = p->pShmNode; /* The underlying file iNode */
if( flags & SQLITE_SHM_SHARED ){
/* SHARED locks */
u32 iOld, iNew, *ptr;
int iIncr = -1;
if( (flags & SQLITE_SHM_UNLOCK)==0 ){
p->aMFCurrent[ofst] = (p->aMFCurrent[ofst] + 1) % aMap[ofst].nSlot;
iIncr = 1;
}
ptr = &pShmNode->aMFSlot[aMap[ofst].iFirst + p->aMFCurrent[ofst]].nLock;
do {
iOld = *ptr;
iNew = iOld + iIncr;
if( iNew>SQLITE_MFS_EXCLUSIVE ){
return SQLITE_BUSY;
}
}while( 0==unixCompareAndSwap(ptr, iOld, iNew) );
}else{
/* EXCLUSIVE locks */
u16 mask = (1<<(ofst+n)) - (1<<ofst);
if( (flags & SQLITE_SHM_LOCK) || (mask & p->exclMask) ){
int iFirst = aMap[ofst].iFirst;
int iLast = aMap[ofst+n].iFirst;
int i;
for(i=iFirst; i<iLast; i++){
u32 *ptr = &pShmNode->aMFSlot[i].nLock;
if( flags & SQLITE_SHM_UNLOCK ){
assert( (*ptr)==SQLITE_MFS_EXCLUSIVE );
*ptr = 0;
}else{
u32 iOld;
do {
iOld = *ptr;
if( iOld>0 ){
while( i>iFirst ){
i--;
pShmNode->aMFSlot[i].nLock = 0;
}
return SQLITE_BUSY;
}
}while( 0==unixCompareAndSwap(ptr, iOld, SQLITE_MFS_EXCLUSIVE) );
}
}
if( flags & SQLITE_SHM_UNLOCK ){
p->exclMask &= ~mask;
}else{
p->exclMask |= mask;
}
}
}
return SQLITE_OK;
}
#else
# define unixMutexFreeShmlock(a,b,c,d) SQLITE_OK
#endif
/*
** Check that the pShmNode->aLock[] array comports with the locking bitmasks
** held by each client. Return true if it does, or false otherwise. This
@ -5033,6 +5182,11 @@ static int unixShmLock(
assert( pShmNode->hShm>=0 || pDbFd->pInode->bProcessLock==1 );
assert( pShmNode->hShm<0 || pDbFd->pInode->bProcessLock==0 );
if( pDbFd->pInode->bProcessLock ){
return unixMutexFreeShmlock(pDbFd, ofst, n, flags);
}
/* Check that, if this to be a blocking lock, no locks that occur later
** in the following list than the lock being obtained are already held:
**
@ -5207,12 +5361,16 @@ static void unixShmBarrier(
sqlite3_file *fd /* Database file holding the shared memory */
){
UNUSED_PARAMETER(fd);
#ifdef SQLITE_MUTEXFREE_SHMLOCK
__sync_synchronize();
#else
sqlite3MemoryBarrier(); /* compiler-defined memory barrier */
assert( fd->pMethods->xLock==nolockLock
|| unixFileMutexNotheld((unixFile*)fd)
);
unixEnterMutex(); /* Also mutex, for redundancy */
unixLeaveMutex();
#endif
}
/*
@ -5281,6 +5439,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;
@ -5412,6 +5573,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);
@ -5854,6 +6037,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();

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 */
@ -700,6 +703,7 @@ struct Pager {
Wal *pWal; /* Write-ahead log used by "journal_mode=wal" */
char *zWal; /* File name for write-ahead log */
#endif
u64 *aCommitTime;
};
/*
@ -789,20 +793,6 @@ static const unsigned char aJournalMagic[] = {
# define USEFETCH(x) 0
#endif
/*
** The argument to this macro is a file descriptor (type sqlite3_file*).
** Return 0 if it is not open, or non-zero (but not 1) if it is.
**
** This is so that expressions can be written as:
**
** if( isOpen(pPager->jfd) ){ ...
**
** instead of
**
** if( pPager->jfd->pMethods ){ ...
*/
#define isOpen(pFd) ((pFd)->pMethods!=0)
#ifdef SQLITE_DIRECT_OVERFLOW_READ
/*
** Return true if page pgno can be read directly from the database file
@ -914,7 +904,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 );
@ -933,6 +925,7 @@ static int assert_pager_state(Pager *p){
assert( isOpen(p->jfd)
|| p->journalMode==PAGER_JOURNALMODE_OFF
|| p->journalMode==PAGER_JOURNALMODE_WAL
|| p->journalMode==PAGER_JOURNALMODE_WAL2
);
}
assert( pPager->dbOrigSize==pPager->dbFileSize );
@ -947,6 +940,7 @@ static int assert_pager_state(Pager *p){
assert( isOpen(p->jfd)
|| p->journalMode==PAGER_JOURNALMODE_OFF
|| p->journalMode==PAGER_JOURNALMODE_WAL
|| p->journalMode==PAGER_JOURNALMODE_WAL2
|| (sqlite3OsDeviceCharacteristics(p->fd)&SQLITE_IOCAP_BATCH_ATOMIC)
);
assert( pPager->dbOrigSize<=pPager->dbHintSize );
@ -959,6 +953,7 @@ static int assert_pager_state(Pager *p){
assert( isOpen(p->jfd)
|| p->journalMode==PAGER_JOURNALMODE_OFF
|| p->journalMode==PAGER_JOURNALMODE_WAL
|| p->journalMode==PAGER_JOURNALMODE_WAL2
|| (sqlite3OsDeviceCharacteristics(p->fd)&SQLITE_IOCAP_BATCH_ATOMIC)
);
break;
@ -1822,6 +1817,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
@ -1846,8 +1888,7 @@ static void pager_unlock(Pager *pPager){
|| pPager->eState==PAGER_ERROR
);
sqlite3BitvecDestroy(pPager->pInJournal);
pPager->pInJournal = 0;
pagerFreeBitvecs(pPager);
releaseAllSavepoints(pPager);
if( pagerUseWal(pPager) ){
@ -2080,7 +2121,7 @@ static int pager_end_transaction(Pager *pPager, int hasSuper, int bCommit){
}
pPager->journalOff = 0;
}else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST
|| (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL)
|| (pPager->exclusiveMode && pPager->journalMode<PAGER_JOURNALMODE_WAL)
){
rc = zeroJournalHdr(pPager, hasSuper||pPager->tempFile);
pPager->journalOff = 0;
@ -2095,6 +2136,7 @@ static int pager_end_transaction(Pager *pPager, int hasSuper, int bCommit){
assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE
|| pPager->journalMode==PAGER_JOURNALMODE_MEMORY
|| pPager->journalMode==PAGER_JOURNALMODE_WAL
|| pPager->journalMode==PAGER_JOURNALMODE_WAL2
);
sqlite3OsClose(pPager->jfd);
if( bDelete ){
@ -2114,8 +2156,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) ){
@ -3010,6 +3051,7 @@ end_playback:
static int readDbPage(PgHdr *pPg){
Pager *pPager = pPg->pPager; /* Pager object associated with page pPg */
int rc = SQLITE_OK; /* Return code */
u64 t1 = 0;
#ifndef SQLITE_OMIT_WAL
u32 iFrame = 0; /* Frame of WAL containing pgno */
@ -3021,6 +3063,9 @@ static int readDbPage(PgHdr *pPg){
rc = sqlite3WalFindFrame(pPager->pWal, pPg->pgno, &iFrame);
if( rc ) return rc;
}
if( pPager->aCommitTime ){
t1 = sqlite3STimeNow();
}
if( iFrame ){
rc = sqlite3WalReadFrame(pPager->pWal, iFrame,pPager->pageSize,pPg->pData);
}else
@ -3032,6 +3077,10 @@ static int readDbPage(PgHdr *pPg){
rc = SQLITE_OK;
}
}
if( pPager->aCommitTime ){
pPager->aCommitTime[COMMIT_TIME_RELOCATE2_READUS] += (sqlite3STimeNow() - t1);
pPager->aCommitTime[COMMIT_TIME_RELOCATE2_READCOUNT]++;
}
if( pPg->pgno==1 ){
if( rc ){
@ -3145,8 +3194,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);
@ -3196,6 +3261,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 );
@ -3205,6 +3272,7 @@ static int pagerWalFrames(
pPager->aStat[PAGER_STAT_WRITE] += nList;
if( pList->pgno==1 ) pager_write_changecounter(pList);
sqlite3CommitTimeSet(pPager->aCommitTime, COMMIT_TIME_AFTER_CHANGECOUNTER);
rc = sqlite3WalFrames(pPager->pWal,
pPager->pageSize, pList, nTruncate, isCommit, pPager->walSyncFlags
);
@ -3250,6 +3318,10 @@ static int pagerBeginReadTransaction(Pager *pPager){
if( rc!=SQLITE_OK || changed ){
pager_reset(pPager);
if( USEFETCH(pPager) ) sqlite3OsUnfetch(pPager->fd, 0, 0);
assert( pPager->journalMode==PAGER_JOURNALMODE_WAL
|| pPager->journalMode==PAGER_JOURNALMODE_WAL2
);
pPager->journalMode = sqlite3WalJournalMode(pPager->pWal);
}
return rc;
@ -3345,9 +3417,9 @@ static int pagerOpenWalIfPresent(Pager *pPager){
rc = sqlite3OsDelete(pPager->pVfs, pPager->zWal, 0);
}else{
testcase( sqlite3PcachePagecount(pPager->pPCache)==0 );
rc = sqlite3PagerOpenWal(pPager, 0);
rc = sqlite3PagerOpenWal(pPager, 0, 0);
}
}else if( pPager->journalMode==PAGER_JOURNALMODE_WAL ){
}else if( pPager->journalMode>=PAGER_JOURNALMODE_WAL ){
pPager->journalMode = PAGER_JOURNALMODE_DELETE;
}
}
@ -4266,7 +4338,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 ){
@ -4617,6 +4689,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 ){
@ -4852,6 +4930,7 @@ int sqlite3PagerOpen(
nPathname + 8 + 1 + /* Journal filename */
#ifndef SQLITE_OMIT_WAL
nPathname + 4 + 1 + /* WAL filename */
nPathname + 5 + 1 + /* Second WAL filename */
#endif
3 /* Terminator */
);
@ -4904,6 +4983,8 @@ int sqlite3PagerOpen(
sqlite3FileSuffix3(zFilename, pPager->zWal);
pPtr = (u8*)(pPager->zWal + sqlite3Strlen30(pPager->zWal)+1);
#endif
memcpy(pPtr, zPathname, nPathname); pPtr += nPathname;
memcpy(pPtr, "-wal2", 5); pPtr += 5 + 1;
}else{
pPager->zWal = 0;
}
@ -5448,6 +5529,23 @@ static void pagerUnlockIfUnused(Pager *pPager){
}
}
#ifndef SQLITE_OMIT_CONCURRENT
/*
** If this pager is currently in a concurrent transaction (pAllRead!=0),
** then set the bit in the pAllRead vector to indicate that the transaction
** read from page pgno. Return SQLITE_OK if successful, or an SQLite error
** code (i.e. SQLITE_NOMEM) if an error occurs.
*/
int sqlite3PagerUsePage(Pager *pPager, Pgno pgno){
int rc = SQLITE_OK;
if( pPager->pAllRead && pgno<=pPager->dbOrigSize ){
PAGERTRACE(("USING page %d\n", pgno));
rc = sqlite3BitvecSet(pPager->pAllRead, pgno);
}
return rc;
}
#endif
/*
** The page getter methods each try to acquire a reference to a
** page with page number pgno. If the requested reference is
@ -5521,6 +5619,14 @@ static int getPageNormal(
assert( assert_pager_state(pPager) );
assert( pPager->hasHeldSharedLock==1 );
/* 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. */
if( (rc = sqlite3PagerUsePage(pPager, pgno))!=SQLITE_OK ){
pPg = 0;
goto pager_acquire_err;
}
if( pgno==0 ) return SQLITE_CORRUPT_BKPT;
pBase = sqlite3PcacheFetch(pPager->pPCache, pgno, 3);
if( pBase==0 ){
@ -5879,11 +5985,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
@ -5901,7 +6010,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.
@ -5917,9 +6025,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
@ -5927,7 +6036,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);
}
}
@ -6227,7 +6336,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;
}
@ -6383,17 +6492,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, u32 *aConflict){
int rc = pPager->errCode;
assert( assert_pager_state(pPager) );
if( rc==SQLITE_OK ){
@ -6405,10 +6523,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, aConflict
);
}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
@ -6482,7 +6663,9 @@ int sqlite3PagerCommitPhaseOne(
}
assert( rc==SQLITE_OK );
if( ALWAYS(pList) ){
sqlite3CommitTimeSet(pPager->aCommitTime, COMMIT_TIME_BEFORE_WALFRAMES);
rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1);
sqlite3CommitTimeSet(pPager->aCommitTime, COMMIT_TIME_AFTER_WALFRAMES);
}
sqlite3PagerUnref(pPageOne);
if( rc==SQLITE_OK ){
@ -6656,6 +6839,10 @@ commit_phase_one_exit:
return rc;
}
void sqlite3PagerSetCommitTime(Pager *pPager, u64 *aCommitTime){
pPager->aCommitTime = aCommitTime;
sqlite3WalSetCommitTime(pPager->pWal, aCommitTime);
}
/*
** When this function is called, the database file has been completely
@ -7340,7 +7527,8 @@ int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){
|| eMode==PAGER_JOURNALMODE_OFF /* 2 */
|| eMode==PAGER_JOURNALMODE_TRUNCATE /* 3 */
|| eMode==PAGER_JOURNALMODE_MEMORY /* 4 */
|| eMode==PAGER_JOURNALMODE_WAL /* 5 */ );
|| eMode==PAGER_JOURNALMODE_WAL /* 5 */
|| eMode==PAGER_JOURNALMODE_WAL2 /* 6 */ );
/* This routine is only called from the OP_JournalMode opcode, and
** the logic there will never allow a temporary file to be changed
@ -7374,9 +7562,12 @@ int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){
assert( (PAGER_JOURNALMODE_MEMORY & 5)==4 );
assert( (PAGER_JOURNALMODE_OFF & 5)==0 );
assert( (PAGER_JOURNALMODE_WAL & 5)==5 );
assert( (PAGER_JOURNALMODE_WAL2 & 5)==4 );
assert( isOpen(pPager->fd) || pPager->exclusiveMode );
if( !pPager->exclusiveMode && (eOld & 5)==1 && (eMode & 1)==0 ){
if( !pPager->exclusiveMode && (eOld & 5)==1 && (eMode & 1)==0
&& eMode!=PAGER_JOURNALMODE_WAL2 /* TODO: fix this if possible */
){
/* In this case we would like to delete the journal file. If it is
** not possible, then that is not a problem. Deleting the journal file
** here is an optimization only.
@ -7551,7 +7742,7 @@ static int pagerExclusiveLock(Pager *pPager){
** lock on the database file and use heap-memory to store the wal-index
** in. Otherwise, use the normal shared-memory.
*/
static int pagerOpenWal(Pager *pPager){
static int pagerOpenWal(Pager *pPager, int bWal2){
int rc = SQLITE_OK;
assert( pPager->pWal==0 && pPager->tempFile==0 );
@ -7572,7 +7763,7 @@ static int pagerOpenWal(Pager *pPager){
if( rc==SQLITE_OK ){
rc = sqlite3WalOpen(pPager->pVfs,
pPager->fd, pPager->zWal, pPager->exclusiveMode,
pPager->journalSizeLimit, &pPager->pWal
pPager->journalSizeLimit, bWal2, &pPager->pWal
);
}
pagerFixMaplimit(pPager);
@ -7598,6 +7789,7 @@ static int pagerOpenWal(Pager *pPager){
*/
int sqlite3PagerOpenWal(
Pager *pPager, /* Pager object */
int bWal2, /* Open in wal2 mode if not already open */
int *pbOpen /* OUT: Set to true if call is a no-op */
){
int rc = SQLITE_OK; /* Return code */
@ -7614,9 +7806,9 @@ int sqlite3PagerOpenWal(
/* Close any rollback journal previously open */
sqlite3OsClose(pPager->jfd);
rc = pagerOpenWal(pPager);
rc = pagerOpenWal(pPager, bWal2);
if( rc==SQLITE_OK ){
pPager->journalMode = PAGER_JOURNALMODE_WAL;
pPager->journalMode = bWal2?PAGER_JOURNALMODE_WAL2:PAGER_JOURNALMODE_WAL;
pPager->eState = PAGER_OPEN;
}
}else{
@ -7638,7 +7830,9 @@ int sqlite3PagerOpenWal(
int sqlite3PagerCloseWal(Pager *pPager, sqlite3 *db){
int rc = SQLITE_OK;
assert( pPager->journalMode==PAGER_JOURNALMODE_WAL );
assert( pPager->journalMode==PAGER_JOURNALMODE_WAL
|| pPager->journalMode==PAGER_JOURNALMODE_WAL2
);
/* If the log file is not already open, but does exist in the file-system,
** it may need to be checkpointed before the connection can switch to
@ -7653,7 +7847,7 @@ int sqlite3PagerCloseWal(Pager *pPager, sqlite3 *db){
);
}
if( rc==SQLITE_OK && logexists ){
rc = pagerOpenWal(pPager);
rc = pagerOpenWal(pPager, 0);
}
}
@ -7776,6 +7970,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

View File

@ -82,6 +82,23 @@ typedef struct PgHdr DbPage;
#define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */
#define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */
#define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */
#define PAGER_JOURNALMODE_WAL2 6 /* Use write-ahead logging mode 2 */
#define isWalMode(x) ((x)==PAGER_JOURNALMODE_WAL || (x)==PAGER_JOURNALMODE_WAL2)
/*
** The argument to this macro is a file descriptor (type sqlite3_file*).
** Return 0 if it is not open, or non-zero (but not 1) if it is.
**
** This is so that expressions can be written as:
**
** if( isOpen(pPager->jfd) ){ ...
**
** instead of
**
** if( pPager->jfd->pMethods ){ ...
*/
#define isOpen(pFd) ((pFd)->pMethods!=0)
/*
** Flags that make up the mask passed to sqlite3PagerGet().
@ -163,7 +180,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, u32*);
int sqlite3PagerSync(Pager *pPager, const char *zSuper);
int sqlite3PagerCommitPhaseTwo(Pager*);
int sqlite3PagerRollback(Pager*);
@ -171,11 +188,12 @@ 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);
int sqlite3PagerWalCallback(Pager *pPager);
int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen);
int sqlite3PagerOpenWal(Pager *pPager, int, int *pisOpen);
int sqlite3PagerCloseWal(Pager *pPager, sqlite3*);
# ifdef SQLITE_ENABLE_SNAPSHOT
int sqlite3PagerSnapshotGet(Pager*, sqlite3_snapshot **ppSnapshot);
@ -225,10 +243,28 @@ void sqlite3PagerTruncateImage(Pager*,Pgno);
void sqlite3PagerRekey(DbPage*, Pgno, u16);
#ifndef SQLITE_OMIT_CONCURRENT
int sqlite3PagerUsePage(Pager*, Pgno);
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)
# define sqlite3PagerUsePage(x, y) SQLITE_OK
#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*);
@ -244,4 +280,6 @@ void sqlite3PagerRekey(DbPage*, Pgno, u16);
int sqlite3PagerWalSystemErrno(Pager*);
#endif
void sqlite3PagerSetCommitTime(Pager *pPager, u64 *aCommitTime);
#endif /* SQLITE_PAGER_H */

View File

@ -109,6 +109,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; };
/*
@ -168,7 +175,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);}
@ -304,7 +320,6 @@ columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,A,Y);}
// keywords. Any non-standard keyword can also be an identifier.
//
%token_class id ID|INDEXED.
// And "ids" is an identifer-or-string.
//
%token_class ids ID|STRING.
@ -1152,7 +1167,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);
@ -2023,6 +2038,7 @@ filter_clause(A) ::= FILTER LP WHERE expr(X) RP. { A = X; }
UMINUS /* Unary minus */
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 */

View File

@ -633,6 +633,14 @@ static void pcache1EnforceMaxPage(PCache1 *pCache){
pcache1RemoveFromHash(p, 1);
}
if( pCache->nPage==0 && pCache->pBulk ){
if( pcache1.separateCache ){
PgHdr1 *p1 = pCache->pFree;
while( p1 ){
PgHdr1 *pNext = p1->pNext;
if( p1->isBulkLocal==0 ) pcache1Free(p1->page.pBuf);
p1 = pNext;
}
}
sqlite3_free(pCache->pBulk);
pCache->pBulk = pCache->pFree = 0;
}
@ -678,7 +686,13 @@ static void pcache1TruncateUnsafe(
pCache->nPage--;
*pp = pPage->pNext;
if( PAGE_IS_UNPINNED(pPage) ) pcache1PinPage(pPage);
pcache1FreePage(pPage);
if( pcache1.separateCache ){
pPage->pNext = pCache->pFree;
pCache->pFree = pPage;
(*pCache->pnPurgeable)--;
}else{
pcache1FreePage(pPage);
}
}else{
pp = &pPage->pNext;
TESTONLY( if( nPage>=0 ) nPage++; )
@ -1096,7 +1110,13 @@ static void pcache1Unpin(
assert( PAGE_IS_PINNED(pPage) );
if( reuseUnlikely || pGroup->nPurgeable>pGroup->nMaxPage ){
/* If pcache1.separateCache is set, temporarily set the isBulkLocal flag
** so that pcache1RemoveFromHash() moves the page buffer to the pFree
** list instead of sqlite3_free()ing it. */
u16 isBulkLocal = pPage->isBulkLocal;
pPage->isBulkLocal = (u16)pcache1.separateCache;
pcache1RemoveFromHash(pPage, 1);
pPage->isBulkLocal = isBulkLocal;
}else{
/* Add the page to the PGroup LRU list. */
PgHdr1 **ppFirst = &pGroup->lru.pLruNext;
@ -1176,6 +1196,15 @@ static void pcache1Destroy(sqlite3_pcache *p){
assert( pCache->bPurgeable || (pCache->nMax==0 && pCache->nMin==0) );
pcache1EnterMutex(pGroup);
if( pCache->nPage ) pcache1TruncateUnsafe(pCache, 0);
if( pcache1.separateCache ){
PgHdr1 *p1 = pCache->pFree;
while( p1 ){
PgHdr1 *pNext = p1->pNext;
if( p1->isBulkLocal==0 ) pcache1Free(p1->page.pBuf);
p1 = pNext;
}
pCache->pFree = 0;
}
assert( pGroup->nMaxPage >= pCache->nMax );
pGroup->nMaxPage -= pCache->nMax;
assert( pGroup->nMinPage >= pCache->nMin );

View File

@ -290,7 +290,7 @@ const char *sqlite3JournalModename(int eMode){
static char * const azModeName[] = {
"delete", "persist", "off", "truncate", "memory"
#ifndef SQLITE_OMIT_WAL
, "wal"
, "wal", "wal2"
#endif
};
assert( PAGER_JOURNALMODE_DELETE==0 );
@ -299,6 +299,7 @@ const char *sqlite3JournalModename(int eMode){
assert( PAGER_JOURNALMODE_TRUNCATE==3 );
assert( PAGER_JOURNALMODE_MEMORY==4 );
assert( PAGER_JOURNALMODE_WAL==5 );
assert( PAGER_JOURNALMODE_WAL2==6 );
assert( eMode>=0 && eMode<=ArraySize(azModeName) );
if( eMode==ArraySize(azModeName) ) return 0;
@ -425,6 +426,7 @@ void sqlite3Pragma(
Vdbe *v = sqlite3GetVdbe(pParse); /* Prepared statement */
const PragmaName *pPragma; /* The pragma */
sqlite3PrepareTimeSet(db->aPrepareTime, PREPARE_TIME_BEGINPRAGMA);
if( v==0 ) return;
sqlite3VdbeRunOnlyOnce(v);
pParse->nMem = 2;
@ -450,11 +452,13 @@ void sqlite3Pragma(
zRight = sqlite3NameFromToken(db, pValue);
}
sqlite3PrepareTimeSet(db->aPrepareTime, PREPARE_TIME_BEGINAUTHCHECK);
assert( pId2 );
zDb = pId2->n>0 ? pDb->zDbSName : 0;
if( sqlite3AuthCheck(pParse, SQLITE_PRAGMA, zLeft, zRight, zDb) ){
goto pragma_out;
}
sqlite3PrepareTimeSet(db->aPrepareTime, PREPARE_TIME_ENDAUTHCHECK);
/* Send an SQLITE_FCNTL_PRAGMA file-control to the underlying VFS
** connection. If it returns SQLITE_OK, then assume that the VFS
@ -502,10 +506,12 @@ void sqlite3Pragma(
goto pragma_out;
}
sqlite3PrepareTimeSet(db->aPrepareTime, PREPARE_TIME_BEGINLOADSCHEMA);
/* Make sure the database schema is loaded if the pragma requires that */
if( (pPragma->mPragFlg & PragFlg_NeedSchema)!=0 ){
if( sqlite3ReadSchema(pParse) ) goto pragma_out;
}
sqlite3PrepareTimeSet(db->aPrepareTime, PREPARE_TIME_ENDLOADSCHEMA);
/* Register the result column names for pragmas that return results */
if( (pPragma->mPragFlg & PragFlg_NoColumns)==0
@ -865,6 +871,7 @@ void sqlite3Pragma(
*/
case PragTyp_CACHE_SIZE: {
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
sqlite3PrepareTimeSet(db->aPrepareTime, PREPARE_TIME_BEGINCACHESIZE);
if( !zRight ){
returnSingleInt(v, pDb->pSchema->cache_size);
}else{
@ -872,6 +879,7 @@ void sqlite3Pragma(
pDb->pSchema->cache_size = size;
sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size);
}
sqlite3PrepareTimeSet(db->aPrepareTime, PREPARE_TIME_ENDCACHESIZE);
break;
}
@ -2758,6 +2766,7 @@ void sqlite3Pragma(
pragma_out:
sqlite3DbFree(db, zLeft);
sqlite3DbFree(db, zRight);
sqlite3PrepareTimeSet(db->aPrepareTime, PREPARE_TIME_ENDPRAGMA);
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
/*****************************************************************************

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,
@ -657,4 +666,4 @@ static const PragmaName aPragmaName[] = {
/* iArg: */ SQLITE_WriteSchema|SQLITE_NoSchemaError },
#endif
};
/* Number of pragmas: 68 on by default, 78 total. */
/* Number of pragmas: 68 on by default, 79 total. */

View File

@ -209,6 +209,11 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){
int openedTransaction = 0;
int mask = ((db->mDbFlags & DBFLAG_EncodingFixed) | ~DBFLAG_EncodingFixed);
u64 aSchemaTime[SCHEMA_TIME_N];
memset(aSchemaTime, 0, sizeof(aSchemaTime));
db->aSchemaTime = aSchemaTime;
sqlite3PrepareTimeSet(aSchemaTime, SCHEMA_TIME_START);
assert( (db->mDbFlags & DBFLAG_SchemaKnownOk)==0 );
assert( iDb>=0 && iDb<db->nDb );
assert( db->aDb[iDb].pSchema );
@ -243,6 +248,8 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){
goto error_out;
}
sqlite3PrepareTimeSet(aSchemaTime, SCHEMA_TIME_AFTER_CREATE_1);
/* Create a cursor to hold the database open
*/
pDb = &db->aDb[iDb];
@ -266,6 +273,8 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){
openedTransaction = 1;
}
sqlite3PrepareTimeSet(aSchemaTime, SCHEMA_TIME_AFTER_OPEN_TRANS);
/* Get the database meta information.
**
** Meta values are as follows:
@ -291,6 +300,8 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){
}
pDb->pSchema->schema_cookie = meta[BTREE_SCHEMA_VERSION-1];
sqlite3PrepareTimeSet(aSchemaTime, SCHEMA_TIME_AFTER_GET_META);
/* If opening a non-empty database, check the text encoding. For the
** main database, set sqlite3.enc to the encoding of the main database.
** For an attached db, it is an error if the encoding is not the same
@ -326,6 +337,8 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){
}
pDb->pSchema->enc = ENC(db);
sqlite3PrepareTimeSet(aSchemaTime, SCHEMA_TIME_AFTER_FIX_ENCODING);
if( pDb->pSchema->cache_size==0 ){
#ifndef SQLITE_OMIT_DEPRECATED
size = sqlite3AbsInt32(meta[BTREE_DEFAULT_CACHE_SIZE-1]);
@ -337,6 +350,8 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){
sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size);
}
sqlite3PrepareTimeSet(aSchemaTime, SCHEMA_TIME_AFTER_SETCACHESIZE);
/*
** file_format==1 Version 3.0.0.
** file_format==2 Version 3.1.3. // ALTER TABLE ADD COLUMN
@ -377,6 +392,7 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){
xAuth = db->xAuth;
db->xAuth = 0;
#endif
sqlite3PrepareTimeSet(aSchemaTime, SCHEMA_TIME_BEGIN_EXEC);
rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0);
#ifndef SQLITE_OMIT_AUTHORIZATION
db->xAuth = xAuth;
@ -384,11 +400,13 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){
#endif
if( rc==SQLITE_OK ) rc = initData.rc;
sqlite3DbFree(db, zSql);
sqlite3PrepareTimeSet(aSchemaTime, SCHEMA_TIME_BEGIN_ANALYZE_LOAD);
#ifndef SQLITE_OMIT_ANALYZE
if( rc==SQLITE_OK ){
sqlite3AnalysisLoad(db, iDb);
}
#endif
sqlite3PrepareTimeSet(aSchemaTime, SCHEMA_TIME_END_ANALYZE_LOAD);
}
assert( pDb == &(db->aDb[iDb]) );
if( db->mallocFailed ){
@ -422,6 +440,9 @@ initone_error_out:
sqlite3BtreeLeave(pDb->pBt);
error_out:
db->aSchemaTime = 0;
sqlite3PrepareTimeSet(aSchemaTime, SCHEMA_TIME_FINISH);
sqlite3SchemaTimeLog(aSchemaTime);
if( rc ){
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
sqlite3OomFault(db);
@ -782,14 +803,18 @@ static int sqlite3Prepare(
}
zSqlCopy = sqlite3DbStrNDup(db, zSql, nBytes);
if( zSqlCopy ){
sqlite3PrepareTimeSet(db->aPrepareTime, PREPARE_TIME_BEGINPARSE);
sqlite3RunParser(&sParse, zSqlCopy);
sqlite3PrepareTimeSet(db->aPrepareTime, PREPARE_TIME_ENDPARSE);
sParse.zTail = &zSql[sParse.zTail-zSqlCopy];
sqlite3DbFree(db, zSqlCopy);
}else{
sParse.zTail = &zSql[nBytes];
}
}else{
sqlite3PrepareTimeSet(db->aPrepareTime, PREPARE_TIME_BEGINPARSE);
sqlite3RunParser(&sParse, zSql);
sqlite3PrepareTimeSet(db->aPrepareTime, PREPARE_TIME_ENDPARSE);
}
assert( 0==sParse.nQueryLoop );
@ -850,6 +875,12 @@ static int sqlite3LockAndPrepare(
){
int rc;
int cnt = 0;
u64 *aPrepareSave = db->aPrepareTime;
u64 aPrepareTime[PREPARE_TIME_N];
memset(aPrepareTime, 0, sizeof(aPrepareTime));
sqlite3PrepareTimeSet(aPrepareTime, PREPARE_TIME_START);
db->aPrepareTime = aPrepareTime;
#ifdef SQLITE_ENABLE_API_ARMOR
if( ppStmt==0 ) return SQLITE_MISUSE_BKPT;
@ -875,6 +906,11 @@ static int sqlite3LockAndPrepare(
db->busyHandler.nBusy = 0;
sqlite3_mutex_leave(db->mutex);
assert( rc==SQLITE_OK || (*ppStmt)==0 );
db->aPrepareTime = aPrepareSave;
sqlite3PrepareTimeSet(aPrepareTime, PREPARE_TIME_FINISH);
sqlite3PrepareTimeLog(zSql, nBytes, aPrepareTime);
return rc;
}

View File

@ -1317,7 +1317,7 @@ char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){
*/
static void renderLogMsg(int iErrCode, const char *zFormat, va_list ap){
StrAccum acc; /* String accumulator */
char zMsg[SQLITE_PRINT_BUF_SIZE*3]; /* Complete log message */
char zMsg[SQLITE_PRINT_BUF_SIZE*10]; /* Complete log message */
sqlite3StrAccumInit(&acc, 0, zMsg, sizeof(zMsg), 0);
sqlite3_str_vappendf(&acc, zFormat, ap);

View File

@ -129,6 +129,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

View File

@ -2284,7 +2284,7 @@ int sqlite3ColumnsFromExprList(
zName = sqlite3MPrintf(db, "%.*z:%u", nName, zName, ++cnt);
sqlite3ProgressCheck(pParse);
if( cnt>3 ){
sqlite3_randomness(sizeof(cnt), &cnt);
sqlite3FastRandomness(&db->sPrng, sizeof(cnt), &cnt);
}
}
pCol->zCnName = zName;

View File

@ -10693,6 +10693,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
**
@ -10836,6 +10861,98 @@ int sqlite3_deserialize(
#define SQLITE_DESERIALIZE_RESIZEABLE 2 /* Resize using sqlite3_realloc64() */
#define SQLITE_DESERIALIZE_READONLY 4 /* Database is read-only */
/*
** Access details of recent COMMIT commands. This function allows various
** details related to the most recent COMMIT command to be accessed.
** The requested value is always returned via output parameter (*piVal).
** The specific value requested is identified by parameter op (see
** below).
**
** SQLITE_OK is returned if successful, or SQLITE_ERROR if the "op" or
** "zDb" paramters are unrecognized.
*/
int sqlite3_commit_status(
sqlite3 *db, /* Database handle */
const char *zDb, /* Name of database - "main" etc. */
int op, /* SQLITE_COMMIT_XXX constant */
unsigned int *piVal /* OUT: Write requested value here */
);
/*
** The following describes the five requests supported by
** sqlite3_commit_status(), each identified by an SQLITE_COMMIT_XXX
** constant:
**
** SQLITE_COMMIT_FIRSTFRAME:
** In this case argument zDb must be "main", or "temp", or else the name of
** an attached database. If zDb does not correspond to any attached database,
** SQLITE_ERROR is returned.
**
** The final value of (*piVal) for this request is only defined if (a) the
** most recent attempt to write to the database connection was successful,
** (b) the most recent attempt to write to the database did write to database
** zDb, and (c) zDb is a wal mode database.
**
** If the above conditions are true, then output parameter (*piVal) is
** set to the frame number of the first frame written by the recent
** transaction. In wal mode, or in wal2 mode when a transaction is
** written into the *-wal file, the frame number indicates the frame's
** position in the wal file - frames are numbered starting from 1. In
** wal2 mode, when a transaction is written to the *-wal2 file, the frame
** number is the frame's position in the *-wal2 file, plus (1 << 31).
**
** Note: Although the a database may have up to (1<<32) pages, each wal
** file may contain at most (1<<31) frames.
**
** SQLITE_COMMIT_NFRAME:
** zDb is interpreted in the same way as, and the final value of (*piVal)
** is undefined, for SQLITE_COMMIT_FIRSTFRAME.
**
** Otherwise, (*piVal) is set to the number of frames written by the
** recent transaction.
**
** SQLITE_COMMIT_CONFLICT_DB:
** Parameter zDb is ignored for this request. The results of this
** request are only defined if the most recent attempt to write to
** the database handle was a BEGIN CONCURRENT transaction that
** failed with an SQLITE_BUSY_SNAPSHOT error.
**
** In other cases, (*piVal) is set to the index of the database
** on which the SQLITE_BUSY_SNAPSHOT error occurred (0 for main,
** a value of 2 or greater for an attached database). This value
** may be used with the sqlite3_db_name() API to find the name
** of the conflicting database.
**
** SQLITE_COMMIT_CONFLICT_FRAME:
** Parameter zDb is ignored for this request. The results of this
** request are only defined if the most recent attempt to write to
** the database handle was a BEGIN CONCURRENT transaction that
** failed with an SQLITE_BUSY_SNAPSHOT error.
**
** (*piVal) is set to the frame number of the conflicting frame for
** the recent SQLITE_BUSY_SNAPSHOT error. The conflicting transaction may
** be found by comparing this value with the FIRSTFRAME and
** NFRAME values for recent succesfully committed transactions on
** the same db. If the CONFLICT_FRAME value is F, then the conflicting
** transaction is the most recent successful commit for which
** (FIRSTFRAME <= F <= FIRSTFRAME+NFRAME) is true.
**
** SQLITE_COMMIT_CONFLICT_PGNO:
** Parameter zDb is ignored for this request. The results of this
** request are only defined if the previous attempt to write to
** the database using database handle db failed with
** SQLITE_BUSY_SNAPSHOT.
**
** Return the page number of the conflicting page for the most
** recent SQLITE_BUSY_SNAPSHOT error.
*/
#define SQLITE_COMMIT_FIRSTFRAME 0
#define SQLITE_COMMIT_NFRAME 1
#define SQLITE_COMMIT_CONFLICT_DB 2
#define SQLITE_COMMIT_CONFLICT_FRAME 3
#define SQLITE_COMMIT_CONFLICT_PGNO 4
/*
** Undo the hack that converts floating point types to integer for
** builds on processors without floating point support.

View File

@ -1309,6 +1309,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 FpDecode FpDecode;
typedef struct FuncDestructor FuncDestructor;
@ -1437,6 +1438,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
@ -1688,6 +1697,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 */
@ -1701,6 +1711,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 */
@ -1809,8 +1820,72 @@ struct sqlite3 {
#ifdef SQLITE_USER_AUTHENTICATION
sqlite3_userauth auth; /* User authentication information */
#endif
#ifndef SQLITE_OMIT_CONCURRENT
/* Return values for sqlite3_commit_status() requests:
** SQLITE_COMMIT_CONFLICT_DB, CONFLICT_FRAME and CONFLICT_PGNO.
*/
u32 aCommit[5];
#endif
u64 *aPrepareTime;
u64 *aSchemaTime;
};
#define PREPARE_TIME_START 0
#define PREPARE_TIME_BEGINPARSE 1
#define PREPARE_TIME_BEGINPRAGMA 2
#define PREPARE_TIME_BEGINAUTHCHECK 3
#define PREPARE_TIME_ENDAUTHCHECK 4
#define PREPARE_TIME_BEGINLOADSCHEMA 5
#define PREPARE_TIME_ENDLOADSCHEMA 6
#define PREPARE_TIME_BEGINCACHESIZE 7
#define PREPARE_TIME_BEGINSETCACHESIZE 8
#define PREPARE_TIME_ENDSETCACHESIZE 9
#define PREPARE_TIME_ENDCACHESIZE 10
#define PREPARE_TIME_ENDPRAGMA 11
#define PREPARE_TIME_ENDPARSE 12
#define PREPARE_TIME_FINISH 13
#define PREPARE_TIME_N 14
#define SCHEMA_TIME_START 0
#define SCHEMA_TIME_AFTER_CREATE_1 1
#define SCHEMA_TIME_AFTER_OPEN_TRANS 2
#define SCHEMA_TIME_AFTER_GET_META 3
#define SCHEMA_TIME_AFTER_FIX_ENCODING 4
#define SCHEMA_TIME_AFTER_SETCACHESIZE 5
#define SCHEMA_TIME_BEGIN_EXEC 6
#define SCHEMA_TIME_BEFORE_STEP 7
#define SCHEMA_TIME_BEFORE_PREPARE 8
#define SCHEMA_TIME_BEFORE_FINALIZE 9
#define SCHEMA_TIME_BEGIN_ANALYZE_LOAD 10
#define SCHEMA_TIME_END_ANALYZE_LOAD 11
#define SCHEMA_TIME_FINISH 12
#define SCHEMA_TIME_N 13
#define SCHEMA_TIME_TIMEOUT (2 * 1000 * 1000)
#define sqlite3PrepareTimeSet(x,y) sqlite3CommitTimeSet(x,y)
void sqlite3PrepareTimeLog(const char *zSql, int nSql, u64 *aPrepareTime);
void sqlite3SchemaTimeLog(u64 *aSchemaTime);
#define PREPARE_TIME_TIMEOUT (2 * 1000 * 1000) /* 2 second timeout */
/*
** 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.
*/
@ -1873,6 +1948,7 @@ struct sqlite3 {
#define SQLITE_ReadUncommit HI(0x00004) /* READ UNCOMMITTED in shared-cache */
#define SQLITE_FkNoAction HI(0x00008) /* Treat all FK as NO ACTION */
#define SQLITE_NoopUpdate 0x01000000 /* UPDATE operations are no-ops */
/* Flags used only if debugging */
#ifdef SQLITE_DEBUG
#define SQLITE_SqlTrace HI(0x0100000) /* Debug print SQL as it executes */
@ -5121,6 +5197,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);

View File

@ -2565,6 +2565,83 @@ static int SQLITE_TCLAPI test_create_null_module(
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
/*
** Usage: sqlite3_randomess NBYTE
*/
static int SQLITE_TCLAPI test_sqlite3_randomness(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int nByte = 0;
u8 *aBuf = 0;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "NBYTE");
return TCL_ERROR;
}
if( Tcl_GetIntFromObj(interp, objv[1], &nByte) ) return TCL_ERROR;
aBuf = ckalloc(nByte);
sqlite3_randomness(nByte, aBuf);
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(aBuf, nByte));
ckfree(aBuf);
return TCL_OK;
}
/*
** tclcmd: sqlite3_commit_status db DBNAME OP
*/
static int SQLITE_TCLAPI test_commit_status(
ClientData clientData, /* Unused */
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int objc, /* Number of arguments */
Tcl_Obj *CONST objv[] /* Command arguments */
){
struct Op {
const char *zOp;
int op;
} aOp[] = {
{ "FIRSTFRAME", SQLITE_COMMIT_FIRSTFRAME },
{ "NFRAME", SQLITE_COMMIT_NFRAME },
{ "CONFLICT_DB", SQLITE_COMMIT_CONFLICT_DB },
{ "CONFLICT_FRAME", SQLITE_COMMIT_CONFLICT_FRAME },
{ "CONFLICT_PGNO", SQLITE_COMMIT_CONFLICT_PGNO },
{ 0, 0 }
};
sqlite3 *db = 0;
const char *zDb = 0;
int op = 0;
int rc = SQLITE_OK;
unsigned int val = 0;
if( objc!=4 ){
Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME OP");
return TCL_ERROR;
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){
return TCL_ERROR;
}
zDb = Tcl_GetString(objv[2]);
if( Tcl_GetIndexFromObjStruct(
interp, objv[3], aOp, sizeof(aOp[0]), "OP", 0, &op
)){
return TCL_ERROR;
}
op = aOp[op].op;
rc = sqlite3_commit_status(db, zDb, op, &val);
if( rc==SQLITE_OK ){
Tcl_SetObjResult(interp, Tcl_NewWideIntObj((i64)val));
return TCL_OK;
}
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
return TCL_ERROR;
}
#ifdef SQLITE_ENABLE_SNAPSHOT
/*
** Usage: sqlite3_snapshot_get DB DBNAME
@ -8686,6 +8763,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
*/
@ -9284,8 +9396,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 },
@ -9297,6 +9410,8 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
#ifndef SQLITE_OMIT_VIRTUALTABLE
{ "create_null_module", test_create_null_module, 0 },
#endif
{ "sqlite3_commit_status", test_commit_status, 0 },
{ "sqlite3_randomness", test_sqlite3_randomness, 0 },
};
static int bitmask_size = sizeof(Bitmask)*8;
static int longdouble_size = sizeof(LONGDOUBLE_TYPE);

View File

@ -689,6 +689,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

View File

@ -187,7 +187,7 @@ static int SQLITE_TCLAPI hexio_write(
}
/*
** USAGE: hexio_get_int HEXDATA
** USAGE: hexio_get_int [-littleendian] HEXDATA
**
** Interpret the HEXDATA argument as a big-endian integer. Return
** the value of that integer. HEXDATA can contain between 2 and 8
@ -205,12 +205,20 @@ static int SQLITE_TCLAPI hexio_get_int(
const unsigned char *zIn;
unsigned char *aOut;
unsigned char aNum[4];
int bLittle = 0;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "HEXDATA");
if( objc==3 ){
Tcl_Size n;
char *z = Tcl_GetStringFromObj(objv[1], &n);
if( n>=2 && n<=13 && memcmp(z, "-littleendian", n)==0 ){
bLittle = 1;
}
}
if( (objc-bLittle)!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "[-littleendian] HEXDATA");
return TCL_ERROR;
}
zIn = (const unsigned char *)Tcl_GetStringFromObj(objv[1], &nIn);
zIn = (const unsigned char *)Tcl_GetStringFromObj(objv[1+bLittle], &nIn);
aOut = sqlite3_malloc64( 1 + nIn/2 );
if( aOut==0 ){
return TCL_ERROR;
@ -223,7 +231,11 @@ static int SQLITE_TCLAPI hexio_get_int(
memcpy(&aNum[4-nOut], aOut, nOut);
}
sqlite3_free(aOut);
val = (aNum[0]<<24) | (aNum[1]<<16) | (aNum[2]<<8) | aNum[3];
if( bLittle ){
val = (aNum[3]<<24) | (aNum[2]<<16) | (aNum[1]<<8) | aNum[0];
}else{
val = (aNum[0]<<24) | (aNum[1]<<16) | (aNum[2]<<8) | aNum[3];
}
Tcl_SetObjResult(interp, Tcl_NewIntObj(val));
return TCL_OK;
}

View File

@ -41,6 +41,8 @@ typedef struct SuperlockBusy SuperlockBusy;
struct Superlock {
sqlite3 *db; /* Database handle used to lock db */
int bWal; /* True if db is a WAL database */
int bRecoveryLocked; /* True if WAL RECOVERY lock is held */
int bReaderLocked; /* True if WAL READER locks are held */
};
typedef struct Superlock Superlock;
@ -107,12 +109,13 @@ static int superlockShmLock(
** Invoke the supplied busy-handler as required.
*/
static int superlockWalLock(
sqlite3 *db, /* Database handle open on WAL database */
Superlock *pLock, /* Superlock handle */
SuperlockBusy *pBusy /* Busy handler wrapper object */
){
int rc; /* Return code */
sqlite3_file *fd = 0; /* Main database file handle */
void volatile *p = 0; /* Pointer to first page of shared memory */
sqlite3 *db = pLock->db;
/* Obtain a pointer to the sqlite3_file object open on the main db file. */
rc = sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd);
@ -121,8 +124,10 @@ static int superlockWalLock(
/* Obtain the "recovery" lock. Normally, this lock is only obtained by
** clients running database recovery.
*/
assert( pLock->bRecoveryLocked==0 );
rc = superlockShmLock(fd, 2, 1, pBusy);
if( rc!=SQLITE_OK ) return rc;
pLock->bRecoveryLocked = 1;
/* Zero the start of the first shared-memory page. This means that any
** clients that open read or write transactions from this point on will
@ -139,7 +144,9 @@ static int superlockWalLock(
** are held, it is guaranteed that there are no active reader, writer or
** checkpointer clients.
*/
assert( pLock->bReaderLocked==0 );
rc = superlockShmLock(fd, 3, SQLITE_SHM_NLOCK-3, pBusy);
if( rc==SQLITE_OK ) pLock->bReaderLocked = 1;
return rc;
}
@ -156,8 +163,14 @@ void sqlite3demo_superunlock(void *pLock){
sqlite3_file *fd = 0;
rc = sqlite3_file_control(p->db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd);
if( rc==SQLITE_OK ){
fd->pMethods->xShmLock(fd, 2, 1, flags);
fd->pMethods->xShmLock(fd, 3, SQLITE_SHM_NLOCK-3, flags);
if( p->bRecoveryLocked ){
fd->pMethods->xShmLock(fd, 2, 1, flags);
p->bRecoveryLocked = 0;
}
if( p->bReaderLocked ){
fd->pMethods->xShmLock(fd, 3, SQLITE_SHM_NLOCK-3, flags);
p->bReaderLocked = 0;
}
}
}
sqlite3_close(p->db);
@ -232,7 +245,7 @@ int sqlite3demo_superlock(
if( SQLITE_OK==(rc = superlockIsWal(pLock)) && pLock->bWal ){
rc = sqlite3_exec(pLock->db, "COMMIT", 0, 0, 0);
if( rc==SQLITE_OK ){
rc = superlockWalLock(pLock->db, &busy);
rc = superlockWalLock(pLock, &busy);
}
}
}

View File

@ -91,6 +91,7 @@ const char *sqlite3TestInit(Tcl_Interp *interp){
extern int Fts5tcl_Init(Tcl_Interp *);
extern int SqliteRbu_Init(Tcl_Interp*);
extern int Sqlitetesttcl_Init(Tcl_Interp*);
extern int Bgckpt_Init(Tcl_Interp*);
#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
extern int Sqlitetestfts3_Init(Tcl_Interp *interp);
#endif
@ -161,6 +162,8 @@ const char *sqlite3TestInit(Tcl_Interp *interp){
Fts5tcl_Init(interp);
SqliteRbu_Init(interp);
Sqlitetesttcl_Init(interp);
Bgckpt_Init(interp);
#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
Sqlitetestfts3_Init(interp);

View File

@ -465,6 +465,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. */

View File

@ -401,6 +401,7 @@ end_of_vacuum:
** is closed by the DETACH.
*/
db->autoCommit = 1;
assert( db->eConcurrent==0 );
if( pDb ){
sqlite3BtreeClose(pDb->pBt);

View File

@ -3839,6 +3839,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;
@ -3925,35 +3926,55 @@ 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 );
if( desiredAutoCommit!=db->autoCommit ){
u64 aCommit[COMMIT_TIME_N];
memset(aCommit, 0, sizeof(aCommit));
sqlite3CommitTimeSet(aCommit, COMMIT_TIME_START);
if( iRollback ){
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;
@ -3963,18 +3984,28 @@ case OP_AutoCommit: {
}else{
db->autoCommit = (u8)desiredAutoCommit;
}
if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
sqlite3CommitTimeSet(aCommit, COMMIT_TIME_BEFORE_HALT);
p->aCommitTime = aCommit;
hrc = sqlite3VdbeHalt(p);
p->aCommitTime = 0;
sqlite3CommitTimeSet(aCommit, COMMIT_TIME_AFTER_HALT);
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;
}else{
rc = SQLITE_ERROR;
}
sqlite3CommitTimeSet(aCommit, COMMIT_TIME_FINISH);
if( desiredAutoCommit ) sqlite3CommitTimeLog(aCommit);
goto vdbe_return;
}else{
sqlite3VdbeError(p,
@ -4181,6 +4212,17 @@ 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 ){
@ -4330,6 +4372,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) );
@ -7900,6 +7947,7 @@ case OP_JournalMode: { /* out2 */
|| eNew==PAGER_JOURNALMODE_OFF
|| eNew==PAGER_JOURNALMODE_MEMORY
|| eNew==PAGER_JOURNALMODE_WAL
|| eNew==PAGER_JOURNALMODE_WAL2
|| eNew==PAGER_JOURNALMODE_QUERY
);
assert( pOp->p1>=0 && pOp->p1<db->nDb );
@ -7918,16 +7966,25 @@ case OP_JournalMode: { /* out2 */
/* Do not allow a transition to journal_mode=WAL for a database
** in temporary storage or if the VFS does not support shared memory
*/
if( eNew==PAGER_JOURNALMODE_WAL
if( isWalMode(eNew)
&& (sqlite3Strlen30(zFilename)==0 /* Temp file */
|| !sqlite3PagerWalSupported(pPager)) /* No shared-memory support */
){
eNew = eOld;
}
if( (eNew!=eOld)
&& (eOld==PAGER_JOURNALMODE_WAL || eNew==PAGER_JOURNALMODE_WAL)
){
if( eNew!=eOld && (isWalMode(eNew) || isWalMode(eOld)) ){
/* Prevent changing directly to wal2 from wal mode. And vice versa. */
if( isWalMode(eNew) && isWalMode(eOld) ){
rc = SQLITE_ERROR;
sqlite3VdbeError(p, "cannot change from %s to %s mode",
sqlite3JournalModename(eOld), sqlite3JournalModename(eNew)
);
goto abort_due_to_error;
}
/* Prevent switching into or out of wal/wal2 mode mid-transaction */
if( !db->autoCommit || db->nVdbeRead>1 ){
rc = SQLITE_ERROR;
sqlite3VdbeError(p,
@ -7935,31 +7992,34 @@ case OP_JournalMode: { /* out2 */
(eNew==PAGER_JOURNALMODE_WAL ? "into" : "out of")
);
goto abort_due_to_error;
}else{
if( eOld==PAGER_JOURNALMODE_WAL ){
/* If leaving WAL mode, close the log file. If successful, the call
** to PagerCloseWal() checkpoints and deletes the write-ahead-log
** file. An EXCLUSIVE lock may still be held on the database file
** after a successful return.
*/
rc = sqlite3PagerCloseWal(pPager, db);
if( rc==SQLITE_OK ){
sqlite3PagerSetJournalMode(pPager, eNew);
}
}else if( eOld==PAGER_JOURNALMODE_MEMORY ){
/* Cannot transition directly from MEMORY to WAL. Use mode OFF
** as an intermediate */
sqlite3PagerSetJournalMode(pPager, PAGER_JOURNALMODE_OFF);
}
}
/* Open a transaction on the database file. Regardless of the journal
** mode, this transaction always uses a rollback journal.
if( isWalMode(eOld) ){
/* If leaving WAL mode, close the log file. If successful, the call
** to PagerCloseWal() checkpoints and deletes the write-ahead-log
** file. An EXCLUSIVE lock may still be held on the database file
** after a successful return.
*/
assert( sqlite3BtreeTxnState(pBt)!=SQLITE_TXN_WRITE );
rc = sqlite3PagerCloseWal(pPager, db);
if( rc==SQLITE_OK ){
rc = sqlite3BtreeSetVersion(pBt, (eNew==PAGER_JOURNALMODE_WAL ? 2 : 1));
sqlite3PagerSetJournalMode(pPager, eNew);
}
}else if( eOld==PAGER_JOURNALMODE_MEMORY ){
/* Cannot transition directly from MEMORY to WAL. Use mode OFF
** as an intermediate */
sqlite3PagerSetJournalMode(pPager, PAGER_JOURNALMODE_OFF);
}
/* Open a transaction on the database file. Regardless of the journal
** mode, this transaction always uses a rollback journal.
*/
assert( sqlite3BtreeTxnState(pBt)!=SQLITE_TXN_WRITE );
if( rc==SQLITE_OK ){
/* 1==rollback, 2==wal, 3==wal2 */
rc = sqlite3BtreeSetVersion(pBt,
1 + isWalMode(eNew) + (eNew==PAGER_JOURNALMODE_WAL2)
);
}
}
#endif /* ifndef SQLITE_OMIT_WAL */
@ -8095,6 +8155,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 );

View File

@ -425,4 +425,61 @@ void sqlite3VdbePrintOp(FILE*, int, VdbeOp*);
int sqlite3CursorRangeHintExprCheck(Walker *pWalker, Expr *pExpr);
#endif
#define COMMIT_TIME_START 0
#define COMMIT_TIME_BEFORE_HALT 1
#define COMMIT_TIME_BEFORE_VDBECOMMIT 2
#define COMMIT_TIME_BEFORE_PHASEONE 3
#define COMMIT_TIME_START_FIXUNLOCKED 4
#define COMMIT_TIME_START_RELOCATE1 5
#define COMMIT_TIME_START_RELOCATE2 6
#define COMMIT_TIME_OTHERWRITERS 7
#define COMMIT_TIME_RELOCATE1COUNT 8
#define COMMIT_TIME_RELOCATE2COUNT 9
#define COMMIT_TIME_RELOCATE2_READUS 10
#define COMMIT_TIME_RELOCATE2_READCOUNT 11
#define COMMIT_TIME_RELOCATE2_ALLOCATEUS 12
#define COMMIT_TIME_RELOCATE2_RELOCATEUS 13
#define COMMIT_TIME_AFTER_FIXUNLOCKED 14
#define COMMIT_TIME_BEFORE_WALFRAMES 15
#define COMMIT_TIME_AFTER_CHANGECOUNTER 16
#define COMMIT_TIME_AFTER_RESTARTLOG 17
#define COMMIT_TIME_AFTER_WRITEHDR 18
#define COMMIT_TIME_OSWRITE 19
#define COMMIT_TIME_AFTER_WRITEFRAMES 20
#define COMMIT_TIME_NFRAME 21
#define COMMIT_TIME_HASHMAPUS 22
#define COMMIT_TIME_BEFORE_WALINDEX 23
#define COMMIT_TIME_AFTER_WALINDEX 24
#define COMMIT_TIME_AFTER_WALINDEXHDR 25
#define COMMIT_TIME_WALFRAMESFLAGS 26
#define COMMIT_TIME_AFTER_WALFRAMES 27
#define COMMIT_TIME_BEFORE_PHASETWO 28
#define COMMIT_TIME_AFTER_PHASETWO 29
#define COMMIT_TIME_AFTER_VDBECOMMIT 30
#define COMMIT_TIME_AFTER_HALT 31
#define COMMIT_TIME_FINISH 32
#define COMMIT_TIME_N 33
/* #define COMMIT_TIME_TIMEOUT (2*1000*1000) */
#define COMMIT_TIME_TIMEOUT (10*1000) /* 10ms threshold */
void sqlite3CommitTimeLog(u64*);
u64 sqlite3STimeNow();
void sqlite3CommitTimeSet(u64*, int);
#endif /* SQLITE_VDBE_H */

View File

@ -514,6 +514,7 @@ struct Vdbe {
int nScan; /* Entries in aScan[] */
ScanStatus *aScan; /* Scan definitions for sqlite3_stmt_scanstatus() */
#endif
u64 *aCommitTime;
};
/*

View File

@ -15,6 +15,8 @@
#include "sqliteInt.h"
#include "vdbeInt.h"
#include "btreeInt.h"
/* Forward references */
static void freeEphemeralFunction(sqlite3 *db, FuncDef *pDef);
static void vdbeFreeOpArray(sqlite3 *, Op *, int);
@ -2941,7 +2943,8 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
/* OFF */ 0,
/* TRUNCATE */ 1,
/* MEMORY */ 0,
/* WAL */ 0
/* WAL */ 0,
/* WAL2 */ 0
};
Pager *pPager; /* Pager associated with pBt */
needXcommit = 1;
@ -2954,10 +2957,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;
}
@ -2982,13 +3002,18 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
if( 0==sqlite3Strlen30(sqlite3BtreeGetFilename(db->aDb[0].pBt))
|| nTrans<=1
){
sqlite3CommitTimeSet(p->aCommitTime, COMMIT_TIME_BEFORE_PHASEONE);
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
if( pBt ){
pBt->pBt->aCommitTime = p->aCommitTime;
rc = sqlite3BtreeCommitPhaseOne(pBt, 0);
pBt->pBt->aCommitTime = 0;
}
}
sqlite3CommitTimeSet(p->aCommitTime, COMMIT_TIME_BEFORE_PHASETWO);
/* Do the commit only if all databases successfully complete phase 1.
** If one of the BtreeCommitPhaseOne() calls fails, this indicates an
** IO error while deleting or truncating a journal file. It is unlikely,
@ -2997,9 +3022,13 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
if( pBt ){
pBt->pBt->aCommitTime = p->aCommitTime;
rc = sqlite3BtreeCommitPhaseTwo(pBt, 0);
pBt->pBt->aCommitTime = 0;
}
}
sqlite3CommitTimeSet(p->aCommitTime, COMMIT_TIME_AFTER_PHASETWO);
if( rc==SQLITE_OK ){
sqlite3VtabCommit(db);
}
@ -3359,6 +3388,7 @@ int sqlite3VdbeHalt(Vdbe *p){
sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
sqlite3CloseSavepoints(db);
db->autoCommit = 1;
db->eConcurrent = CONCURRENT_NONE;
p->nChange = 0;
}
}
@ -3395,11 +3425,13 @@ int sqlite3VdbeHalt(Vdbe *p){
** or hit an 'OR FAIL' constraint and there are no deferred foreign
** key constraints to hold up the transaction. This means a commit
** is required. */
sqlite3CommitTimeSet(p->aCommitTime, COMMIT_TIME_BEFORE_VDBECOMMIT);
rc = vdbeCommit(db, p);
sqlite3CommitTimeSet(p->aCommitTime, COMMIT_TIME_AFTER_VDBECOMMIT);
}
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 ){
sqlite3SystemError(db, rc);
p->rc = rc;
@ -3427,6 +3459,7 @@ int sqlite3VdbeHalt(Vdbe *p){
sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
sqlite3CloseSavepoints(db);
db->autoCommit = 1;
db->eConcurrent = CONCURRENT_NONE;
p->nChange = 0;
}
}
@ -3448,6 +3481,7 @@ int sqlite3VdbeHalt(Vdbe *p){
sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
sqlite3CloseSavepoints(db);
db->autoCommit = 1;
db->eConcurrent = CONCURRENT_NONE;
p->nChange = 0;
}
}
@ -5425,6 +5459,91 @@ int sqlite3CursorRangeHintExprCheck(Walker *pWalker, Expr *pExpr){
}
#endif /* SQLITE_ENABLE_CURSOR_HINTS && SQLITE_DEBUG */
#include <sys/time.h>
void sqlite3CommitTimeLog(u64 *aCommit){
u64 i1 = aCommit[COMMIT_TIME_START];
assert( COMMIT_TIME_START==0 && COMMIT_TIME_FINISH==COMMIT_TIME_N-1 );
if( aCommit[COMMIT_TIME_FINISH]>(i1+COMMIT_TIME_TIMEOUT) ){
char *zStr = 0;
int ii;
for(ii=1; ii<COMMIT_TIME_N; ii++){
int iVal;
const char *zHash = "";
const char *zU = "";
if( ii==COMMIT_TIME_RELOCATE1COUNT
|| ii==COMMIT_TIME_RELOCATE2COUNT
|| ii==COMMIT_TIME_OTHERWRITERS
|| ii==COMMIT_TIME_NFRAME
|| ii==COMMIT_TIME_RELOCATE2_READCOUNT
){
iVal = (int)aCommit[ii];
zHash = "#";
}else if( ii==COMMIT_TIME_OSWRITE
|| ii==COMMIT_TIME_RELOCATE2_READUS
|| ii==COMMIT_TIME_RELOCATE2_ALLOCATEUS
|| ii==COMMIT_TIME_RELOCATE2_RELOCATEUS
|| ii==COMMIT_TIME_HASHMAPUS
){
iVal = (int)aCommit[ii];
zU = "us";
}else if( ii==COMMIT_TIME_WALFRAMESFLAGS ){
iVal = (int)aCommit[ii];
zHash = "flags=";
}else{
iVal = (aCommit[ii]==0 ? 0 : (int)(aCommit[ii] - i1));
}
zStr = sqlite3_mprintf("%z%s%s%d%s", zStr, (zStr?", ":""),zHash,iVal,zU);
}
sqlite3_log(SQLITE_WARNING, "slow commit (v=10): (%s)", zStr);
sqlite3_free(zStr);
}
}
u64 sqlite3STimeNow(){
struct timeval time;
gettimeofday(&time, 0);
return ((u64)time.tv_sec * 1000000 + (u64)time.tv_usec);
}
void sqlite3CommitTimeSet(u64 *aCommit, int iCommit){
if( aCommit && aCommit[iCommit]==0 ){
aCommit[iCommit] = sqlite3STimeNow();
}
}
void sqlite3PrepareTimeLog(const char *zSql, int nSql, u64 *aPrepare){
u64 i1 = aPrepare[PREPARE_TIME_START];
assert( PREPARE_TIME_START==0 && PREPARE_TIME_FINISH==PREPARE_TIME_N-1 );
if( aPrepare[PREPARE_TIME_FINISH]>(i1+PREPARE_TIME_TIMEOUT) ){
int nByte = nSql;
char *zStr = 0;
int ii;
for(ii=1; ii<PREPARE_TIME_N; ii++){
zStr = sqlite3_mprintf("%z%s%d", zStr, (zStr?", ":""),
(aPrepare[ii]==0 ? 0 : (int)(aPrepare[ii] - i1))
);
}
if( nByte<0 ){ nByte = sqlite3Strlen30(zSql); }
sqlite3_log(SQLITE_WARNING,
"slow prepare (v=10): (%s) [%.*s]", zStr, nByte, zSql
);
sqlite3_free(zStr);
}
}
void sqlite3SchemaTimeLog(u64 *aSchema){
u64 i1 = aSchema[SCHEMA_TIME_START];
assert( SCHEMA_TIME_START==0 && SCHEMA_TIME_FINISH==SCHEMA_TIME_N-1 );
if( aSchema[SCHEMA_TIME_FINISH]>(i1+SCHEMA_TIME_TIMEOUT) ){
char *zStr = 0;
int ii;
for(ii=1; ii<SCHEMA_TIME_N; ii++){
zStr = sqlite3_mprintf("%z%s%d", zStr, (zStr?", ":""),
(aSchema[ii]==0 ? 0 : (int)(aSchema[ii] - i1))
);
}
sqlite3_log(SQLITE_WARNING, "slow schema (v=10): (%s)", zStr);
sqlite3_free(zStr);
}
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
/*
** Transfer error message text from an sqlite3_vtab.zErrMsg (text stored

2629
src/wal.c

File diff suppressed because it is too large Load Diff

View File

@ -26,7 +26,7 @@
#define CKPT_SYNC_FLAGS(X) (((X)>>2)&0x03)
#ifdef SQLITE_OMIT_WAL
# define sqlite3WalOpen(x,y,z) 0
# define sqlite3WalOpen(w,x,y,z) 0
# define sqlite3WalLimit(x,y)
# define sqlite3WalClose(v,w,x,y,z) 0
# define sqlite3WalBeginReadTransaction(y,z) 0
@ -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
@ -45,6 +45,7 @@
# define sqlite3WalFramesize(z) 0
# define sqlite3WalFindFrame(x,y,z) 0
# define sqlite3WalFile(x) 0
# define sqlite3WalJournalMode(x) 0
# undef SQLITE_USE_SEH
#else
@ -56,7 +57,7 @@
typedef struct Wal Wal;
/* Open and close a connection to a write-ahead log. */
int sqlite3WalOpen(sqlite3_vfs*, sqlite3_file*, const char *, int, i64, Wal**);
int sqlite3WalOpen(sqlite3_vfs*, sqlite3_file*, const char *,int,i64,int,Wal**);
int sqlite3WalClose(Wal *pWal, sqlite3*, int sync_flags, int, u8 *);
/* Set the limiting size of a WAL file. */
@ -84,7 +85,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 */
@ -137,6 +138,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, u32*);
/* 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).
@ -147,6 +157,9 @@ int sqlite3WalFramesize(Wal *pWal);
/* Return the sqlite3_file object for the WAL file */
sqlite3_file *sqlite3WalFile(Wal *pWal);
/* Return the journal mode (WAL or WAL2) used by this Wal object. */
int sqlite3WalJournalMode(Wal *pWal);
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
int sqlite3WalWriteLock(Wal *pWal, int bLock);
void sqlite3WalDb(Wal *pWal, sqlite3 *db);
@ -156,5 +169,13 @@ void sqlite3WalDb(Wal *pWal, sqlite3 *db);
int sqlite3WalSystemErrno(Wal*);
#endif
/* sqlite3_wal_info() data */
int sqlite3WalInfo(Wal *pWal, u32 *pnPrior, u32 *pnFrame);
/* sqlite3_wal_info() data */
int sqlite3WalInfo(Wal *pWal, u32 *pnPrior, u32 *pnFrame);
void sqlite3WalSetCommitTime(Wal *pWal, u64 *aCommitTime);
#endif /* ifndef SQLITE_OMIT_WAL */
#endif /* SQLITE_WAL_H */

556
test/bc_test1.c Normal file
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;
}

297
test/commitstatus.test Normal file
View File

@ -0,0 +1,297 @@
# 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 commitstatus
ifcapable !concurrent {
finish_test
return
}
proc commit_status_frames {db} {
list [sqlite3_commit_status $db main FIRSTFRAME] \
[sqlite3_commit_status $db main NFRAME]
}
proc commit_status_conflict {db} {
list [sqlite3_commit_status $db main CONFLICT_DB] \
[sqlite3_commit_status $db main CONFLICT_FRAME] \
[sqlite3_commit_status $db main CONFLICT_PGNO]
}
do_execsql_test 1.0 {
PRAGMA journal_mode = wal;
} {wal}
do_execsql_test 1.1 {
CREATE TABLE t1(k INTEGER PRIMARY KEY, v);
}
do_test 1.2 { commit_status_frames db } {1 2}
do_execsql_test 1.3 {
INSERT INTO t1 VALUES(2, 'two');
}
do_test 1.4 { commit_status_frames db } {3 1}
do_execsql_test 1.5 {
CREATE INDEX i1 ON t1(v);
}
do_test 1.6 { commit_status_frames db } {4 2}
do_execsql_test 1.7 {
DROP INDEX i1;
}
do_test 1.8 { commit_status_frames db } {6 2}
do_execsql_test 1.9 {
PRAGMA wal_checkpoint;
INSERT INTO t1 VALUES(3, 'three');
} {0 7 7}
do_test 1.10 { commit_status_frames db } {1 1}
do_execsql_test 1.11 {
PRAGMA journal_mode = delete;
PRAGMA journal_mode = wal2;
PRAGMA journal_size_limit = 10000;
} {delete wal2 10000}
for {set ii 1} {$ii < 30} {incr ii} {
do_execsql_test 1.13.$ii.1 {
INSERT INTO t1(v) VALUES('value');
}
if {$ii<=10} {
set expect [list $ii 1]
} elseif {$ii <= 20} {
set expect [list [expr {(1+($ii-1)%10)|0x80000000}] 1]
} else {
set expect [list [expr ($ii%10)] 1]
}
do_test 1.13.$ii.2 { commit_status_frames db } $expect
if {$ii==15} {
execsql { PRAGMA wal_checkpoint }
}
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 2.0 {
CREATE TABLE t1(a, b);
CREATE TABLE t2(a, b);
PRAGMA journal_mode = wal;
} {wal}
sqlite3 db2 test.db
do_execsql_test -db db2 2.1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(1, 1);
}
do_execsql_test 2.2 {
INSERT INTO t2 VALUES(1, 1);
INSERT INTO t2 VALUES(2, 2);
INSERT INTO t2 VALUES(3, 3);
INSERT INTO t2 VALUES(4, 4);
}
do_execsql_test -db db2 2.3 {
COMMIT
}
do_test 2.4 { commit_status_frames db2 } {5 1}
do_execsql_test 2.5 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES('yes', 'no');
INSERT INTO t2 VALUES('yes', 'no');
COMMIT;
}
do_test 2.6 { commit_status_frames db } {6 2}
db2 close
do_execsql_test 2.7 {
PRAGMA journal_mode = delete;
PRAGMA journal_mode = wal2;
} {delete wal2}
sqlite3 db2 test.db
do_execsql_test 2.8 {
PRAGMA journal_size_limit = 5000;
BEGIN CONCURRENT;
INSERT INTO t1 VALUES('x', 'y');
} {5000}
do_execsql_test -db db2 2.9 {
PRAGMA journal_size_limit = 5000;
INSERT INTO t2 VALUES(1, 1);
INSERT INTO t2 VALUES(2, 2);
INSERT INTO t2 VALUES(3, 3);
INSERT INTO t2 VALUES(4, 4);
INSERT INTO t2 VALUES(5, 5);
INSERT INTO t2 VALUES(6, 6);
INSERT INTO t2 VALUES(7, 7);
INSERT INTO t2 VALUES(8, 8);
} {5000}
do_execsql_test 2.10 {
COMMIT;
}
do_test 2.11 { commit_status_frames db } [list [expr {0x80000000 | 4}] 1]
do_execsql_test 2.12 {
PRAGMA wal_checkpoint;
BEGIN CONCURRENT;
INSERT INTO t1 VALUES('a', 'b');
} {0 9 5}
do_execsql_test -db db2 2.13 {
INSERT INTO t2 VALUES(1, 1);
INSERT INTO t2 VALUES(2, 2);
INSERT INTO t2 VALUES(3, 3);
INSERT INTO t2 VALUES(4, 4);
INSERT INTO t2 VALUES(5, 5);
INSERT INTO t2 VALUES(6, 6);
INSERT INTO t2 VALUES(7, 7);
INSERT INTO t2 VALUES(8, 8);
} {}
do_execsql_test 2.14 {
COMMIT;
}
do_test 2.15 { commit_status_frames db } [list 8 1]
#-------------------------------------------------------------------------
reset_db
do_execsql_test 3.0 {
CREATE TABLE t1(x);
CREATE TABLE t2(y);
CREATE TABLE t3(z);
PRAGMA journal_mode = wal2;
PRAGMA journal_size_limit = 8000;
} {wal2 8000}
sqlite3 db2 test.db
do_execsql_test -db db2 3.1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES('x');
}
do_execsql_test 3.2.0 {
INSERT INTO t2 VALUES('y'); -- frame 1
INSERT INTO t3 VALUES('y'); -- frame 2
INSERT INTO t2 VALUES('z'); -- frame 3
INSERT INTO t3 VALUES('z'); -- frame 4
INSERT INTO t1 VALUES('a'); -- frame 5
}
do_test 3.2.1 { commit_status_frames db } {5 1}
do_execsql_test 3.2.2 {
INSERT INTO t3 VALUES('a'); -- frame 6
}
do_test 3.3 {
catchsql { COMMIT } db2
} {1 {database is locked}}
execsql ROLLBACK db2
do_test 3.4 {
commit_status_conflict db2
} {0 5 2}
do_execsql_test -db db2 3.5 {
BEGIN CONCURRENT;
INSERT INTO t3 VALUES('x');
}
do_execsql_test 3.6.0 {
INSERT INTO t2 VALUES('y'); -- frame 7
INSERT INTO t1 VALUES('y'); -- frame 8
INSERT INTO t2 VALUES('z'); -- frame 1b
INSERT INTO t1 VALUES('z'); -- frame 2b
INSERT INTO t3 VALUES('a'); -- frame 3b
}
do_test 3.6.1 { commit_status_frames db } [list [expr {0x80000000 | 3}] 1]
do_execsql_test 3.6.2 {
INSERT INTO t1 VALUES('a'); -- frame 4b
}
do_test 3.7 {
catchsql { COMMIT } db2
} {1 {database is locked}}
execsql ROLLBACK db2
do_test 3.8 {
commit_status_conflict db2
} [list 0 [expr {0x80000000 | 3}] 4]
#-------------------------------------------------------------------------
reset_db
do_execsql_test 4.0 {
CREATE TABLE t1(x);
CREATE TABLE t2(y);
PRAGMA journal_mode = wal2;
PRAGMA journal_size_limit = 10000000;
} {wal2 10000000}
sqlite3 db2 test.db
db2 eval {SELECT * FROM sqlite_schema}
foreach {tn s1 s2} {
1 100 100
2 1000 1000
3 1000 1000
4 1000 1000
5 1000 1000
6 1000 1000
7 1000 1000
8 1000 1000
9 1000 1000
10 2000 2000
11 2000 2000
12 2000 2000
13 2000 2000
14 2000 2000
15 2000 2000
16 2000 2000
17 2000 2000
} {
execsql {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(NULL);
} db2
for {set ii 0} {$ii<$s1} {incr ii} {
execsql { INSERT INTO t2 VALUES(randomblob(30)); }
}
execsql { INSERT INTO t1 VALUES(NULL) }
set frame [sqlite3_commit_status db main FIRSTFRAME]
for {set ii 0} {$ii<$s2} {incr ii} {
execsql { INSERT INTO t2 VALUES(randomblob(30)); }
}
do_test 4.$tn.1 { catchsql "COMMIT" db2 } {1 {database is locked}}
do_test 4.$tn.2 {
commit_status_conflict db2
} [list 0 $frame 2]
execsql { ROLLBACK } db2
}
finish_test

86
test/concfault.test Normal file
View File

@ -0,0 +1,86 @@
# 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
}
finish_test

69
test/concfault2.test Normal file
View File

@ -0,0 +1,69 @@
# 2018 Dec 28
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# This file 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 concfault2
ifcapable !concurrent {
finish_test
return
}
do_execsql_test 1.0 {
PRAGMA auto_vacuum = 0;
PRAGMA journal_mode = wal2;
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;
} {wal2}
do_test 1.1 {
list [expr [file size test.db-wal]>75000] [file size test.db-shm]
} {1 32768}
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

688
test/concurrent.test Normal file
View File

@ -0,0 +1,688 @@
# 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)}
}
db2 close
#-------------------------------------------------------------------------
# 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

630
test/concurrent2.test Normal file
View File

@ -0,0 +1,630 @@
# 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_test 0.1 {
llength [sqlite3_wal_info db main]
} {2}
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

234
test/concurrent3.test Normal file
View File

@ -0,0 +1,234 @@
# 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"
# }
}
catch { db close }
sqlite3_shutdown
test_sqlite3_log
finish_test

194
test/concurrent4.test Normal file
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

278
test/concurrent5.test Normal file
View File

@ -0,0 +1,278 @@
# 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...)
}
db2 close
reset_db
do_execsql_test 1.5.0 {
PRAGMA auto_vacuum = 0;
PRAGMA journal_mode = wal;
CREATE TABLE t1(a, b);
CREATE INDEX i1 ON t1(a, b);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO t1 SELECT i, randomblob(200) FROM s;
} {wal}
do_test 1.5.1 {
set ::log [list]
execsql {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(100000, '');
} db
sqlite3 db2 test.db
execsql { INSERT INTO t1(rowid, a, b) VALUES(-1, 100001, '') } db2
catchsql COMMIT db
} {1 {database is locked}}
do_test_conflict_msg 1.5.2 {
conflict at page 507 (read/write page; part of db index t1.i1; content=0A00000003025000...)
}
#-------------------------------------------------------------------------
reset_db
sqlite3 db2 test.db
set big1 [string repeat ab 10000]
set big2 "[string repeat ab 9999]xy"
do_execsql_test 1.6.0 {
CREATE TABLE x1(x, y);
INSERT INTO x1 VALUES(1, $big1);
PRAGMA journal_mode = wal;
} {wal}
do_execsql_test -db db2 1.6.1.1 {
BEGIN;
UPDATE x1 SET y=$big2;
} {}
do_execsql_test 1.6.1.2 {
BEGIN CONCURRENT;
UPDATE x1 SET y=$big2;
}
do_execsql_test -db db2 1.6.1.3 COMMIT
do_catchsql_test 1.6.1.4 {
COMMIT;
} {1 {database is locked}}
do_test_conflict_msg 1.6.1.5 {
conflict at page 21 (read/write page; part of db table x1; content=0000000061626162...)
}
catchsql ROLLBACK
do_test 1.6.2.1 {
execsql { BEGIN } db2
set fd [db2 incrblob main x1 y 1]
seek $fd 19998
puts -nonewline $fd 00
close $fd
} {}
do_test 1.6.2.2 {
execsql { BEGIN CONCURRENT } db
set fd [db incrblob main x1 y 1]
seek $fd 19998
puts -nonewline $fd 12
close $fd
} {}
do_execsql_test -db db2 1.6.2.3 COMMIT
do_catchsql_test 1.6.2.4 {
COMMIT;
} {1 {database is locked}}
do_test_conflict_msg 1.6.1.5 {
conflict at page 21 (read/write page; part of db table x1; content=0000000061626162...)
}
catchsql ROLLBACK
#--------------------------------------------------------------------------
reset_db
sqlite3 db2 test.db
set big1 [string repeat ab 10000]
set big2 "[string repeat ab 9999]xy"
do_execsql_test 1.7.0 {
CREATE TABLE ww(a);
CREATE TABLE y1(x, y);
INSERT INTO y1 VALUES(1, $big1);
PRAGMA journal_mode = wal;
} {wal}
do_execsql_test -db db2 1.7.1 {
BEGIN;
UPDATE y1 SET y=$big2;
SELECT * FROM ww;
}
do_execsql_test 1.7.2 {
BEGIN CONCURRENT;
INSERT INTO ww SELECT y FROM y1;
}
do_execsql_test -db db2 1.7.3 COMMIT
do_catchsql_test 1.7.4 {
COMMIT;
} {1 {database is locked}}
#-------------------------------------------------------------------------
reset_db
sqlite3 db2 test.db
set big1 "[string repeat ab 10000]"
set big2 "[string repeat ab 9999]xy"
do_execsql_test 2.0 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT);
CREATE TABLE t2(a INTEGER PRIMARY KEY, b TEXT);
INSERT INTO t1 VALUES(100, $big1);
} {wal}
do_execsql_test -db db2 2.1 {
BEGIN CONCURRENT;
INSERT INTO t2 SELECT * FROM t1;
}
do_execsql_test 2.2 {
UPDATE t1 SET b=$big2
}
do_test 2.3 {
list [catch { db2 eval COMMIT } msg] $msg
} {1 {database is locked}}
do_test_conflict_msg 2.4 {
conflict at page 22 (read-only page; part of db table t1; content=0000000061626162...)
}
db close
db2 close
sqlite3_shutdown
test_sqlite3_log
sqlite3_initialize
finish_test

60
test/concurrent6.test Normal file
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
test/concurrent7.test Normal file
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
test/concurrent8.test Normal file
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

120
test/concurrent9.test Normal file
View File

@ -0,0 +1,120 @@
# 2023 January 12
#
# 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.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix concurrent9
do_execsql_test 1.0 {
CREATE TABLE t1(x);
INSERT INTO t1 VALUES(1), (2);
CREATE TABLE t2(y);
INSERT INTO t2 VALUES('a'), ('b');
PRAGMA journal_mode = wal;
} {wal}
db close
#-------------------------------------------------------------------------
# Fix a problem that may occur if a BEGIN CONCURRENT transaction is
# started when the wal file is completely empty and committed after
# it has been initialized by some other connection.
#
sqlite3 db test.db
sqlite3 db2 test.db
do_execsql_test -db db 1.1 {
BEGIN CONCURRENT;
INSERT INTO t2 VALUES('c');
}
do_execsql_test -db db2 1.2 {
INSERT INTO t1 VALUES(3);
}
do_execsql_test -db db 1.3 {
COMMIT;
}
do_execsql_test -db db2 1.4 {
SELECT * FROM t1;
SELECT * FROM t2;
} {1 2 3 a b c}
db2 close
#-------------------------------------------------------------------------
reset_db
do_execsql_test 2.1 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
PRAGMA journal_mode = wal;
WITH s(i) AS (
SELECT 1 UNION SELECT i+1 FROM s WHERE i<500
)
INSERT INTO t1(b) SELECT hex(randomblob(200)) FROM s;
PRAGMA page_count;
} {wal 255}
sqlite3 db2 test.db
do_execsql_test -db db2 2.2 {
DELETE FROM t1 WHERE a<100;
PRAGMA freelist_count;
} {49}
do_execsql_test 2.3 {
BEGIN CONCURRENT;
WITH s(i) AS (
SELECT 1 UNION SELECT i+1 FROM s WHERE i<100
)
INSERT INTO t1(b) SELECT hex(randomblob(200)) FROM s;
}
sqlite3_db_status db CACHE_MISS 1
do_execsql_test 2.4.1 {
COMMIT;
}
do_test 2.4.2 {
lindex [sqlite3_db_status db CACHE_MISS 0] 1
} {1}
do_execsql_test -db db2 2.5 {
DELETE FROM t1 WHERE a<200;
PRAGMA freelist_count;
} {50}
do_execsql_test 2.6 {
BEGIN CONCURRENT;
WITH s(i) AS (
SELECT 1 UNION SELECT i+1 FROM s WHERE i<100
)
INSERT INTO t1(b) SELECT hex(randomblob(200)) FROM s;
DELETE FROM t1 WHERE rowid BETWEEN 600 AND 680;
}
sqlite3_db_status db CACHE_MISS 1
do_execsql_test 2.7.1 {
COMMIT;
}
do_test 2.7.2 {
lindex [sqlite3_db_status db CACHE_MISS 0] 1
} {1}
do_execsql_test 2.8 {
PRAGMA integrity_check;
} {ok}
finish_test

View File

@ -47,7 +47,7 @@ db close
forcecopy test.db test.db-template
set unreadable_version 02
ifcapable wal { set unreadable_version 03 }
ifcapable wal { set unreadable_version 04 }
do_test corruptA-2.1 {
forcecopy test.db-template test.db
hexio_write test.db 19 $unreadable_version ;# the read format number

View File

@ -141,16 +141,6 @@ do_test 2.0 {
| end c-b92b.txt.db
}]} {}
# This test only works with the legacy RC4 PRNG
if 0 {
prng_seed 0 db
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}}
}
reset_db
if {![info exists ::G(perm:presql)]} {
do_execsql_test 3.0 {

View File

@ -4376,6 +4376,9 @@ do_test 25.0 {
| end crash-dde9e76ed8ab2d.db
}]} {}
proc rndblob {n} { return [sqlite3_randomness $n] }
db func randomblob rndblob
do_catchsql_test 25.1 {
PRAGMA writable_schema = 1;
WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x%1 FROM c WHERE x<599237)

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

View File

@ -91,7 +91,6 @@ foreach f [glob $testdir/*.test] { lappend alltests [file tail $f] }
foreach f [glob -nocomplain \
$testdir/../ext/rtree/*.test \
$testdir/../ext/fts5/test/*.test \
$testdir/../ext/expert/*.test \
$testdir/../ext/lsm1/test/*.test \
$testdir/../ext/recover/*.test \
$testdir/../ext/rbu/*.test \
@ -465,18 +464,28 @@ lappend ::testsuitelist xxx
test_suite "coverage-wal" -description {
Coverage tests for file wal.c.
} -files {
wal.test wal2.test wal3.test wal4.test wal5.test
wal64k.test wal6.test wal7.test wal8.test wal9.test
walbak.test walbig.test walblock.test walcksum.test walcrash2.test
walcrash3.test walcrash4.test walcrash.test walfault.test walhook.test
walmode.test walnoshm.test waloverwrite.test walpersist.test
walprotocol2.test walprotocol.test walro2.test walrofault.test
walro.test walshared.test walslow.test walvfs.test
walfault2.test
nockpt.test
wal2big.test wal2recover.test wal2rewrite.test
wal2simple.test wal2snapshot.test wal2.test
wal3.test wal4.test wal5.test
wal64k.test wal6.test wal7.test wal8.test wal9.test
walbak.test walbig.test walblock.test walcksum.test
walfault.test walhook.test walmode.test walnoshm.test
waloverwrite.test walpersist.test walprotocol2.test
walprotocol.test walro2.test walrofault.test walro.test
walshared.test walslow.test wal.test
wal2savepoint.test wal2lock.test wal2recover2.test
wal2concurrent.test
concurrent.test concurrent2.test concurrent3.test
concurrent4.test concurrent5.test concurrent6.test
concurrent7.test
concfault.test concfault2.test
walvfs.test walfault2.test nockpt.test
snapshot2.test snapshot3.test snapshot4.test
snapshot_fault.test snapshot.test snapshot_up.test
walcrash2.test walcrash3.test walcrash4.test walcrash.test
wal2fault.test
}
test_suite "coverage-pager" -description {
@ -650,6 +659,16 @@ test_suite "onefile" -description {
rollback.test select1.test select2.test select3.test
}
# Run some tests using the "unix-excl" VFS.
#
test_suite "unix-excl" -description {
Run some tests using the "unix-excl" VFS
} -initialize {
set ::G(perm:sqlite3_args) [list -vfs unix-excl]
} -files {
shmlock.test
}
# Run some tests using UTF-16 databases.
#
test_suite "utf16" -description {
@ -1034,6 +1053,23 @@ test_suite "wal" -description {
fts3c.test fts3d.test fts3e.test fts3query.test
}
test_suite "wal2" -description {
Run tests with journal_mode=WAL2
} -initialize {
set ::G(savepoint6_iterations) 100
} -shutdown {
unset -nocomplain ::G(savepoint6_iterations)
} -files {
savepoint.test savepoint2.test savepoint6.test
trans.test avtrans.test
fts3aa.test fts3ab.test fts3ac.test fts3ad.test
fts3ae.test fts3af.test fts3ag.test fts3ah.test
fts3ai.test fts3aj.test fts3ak.test fts3al.test
fts3am.test fts3an.test fts3ao.test fts3b.test
fts3c.test fts3d.test fts3e.test fts3query.test
}
test_suite "rtree" -description {
All R-tree related tests. Provides coverage of source file rtree.c.
} -files [glob -nocomplain $::testdir/../ext/rtree/*.test]

View File

@ -41,7 +41,7 @@ do_test rdonly-1.1.1 {
sqlite3_db_readonly db main
} {0}
# Changes the write version from 1 to 3. Verify that the database
# Changes the write version from 1 to 4. Verify that the database
# can be read but not written.
#
do_test rdonly-1.2 {
@ -49,7 +49,7 @@ do_test rdonly-1.2 {
hexio_get_int [hexio_read test.db 18 1]
} 1
do_test rdonly-1.3 {
hexio_write test.db 18 03
hexio_write test.db 18 04
sqlite3 db test.db
execsql {
SELECT * FROM t1;
@ -83,7 +83,7 @@ do_test rdonly-1.5 {
# the database is read-only until after it is locked.
#
set ro_version 02
ifcapable wal { set ro_version 03 }
ifcapable wal { set ro_version 04 }
do_test rdonly-1.6 {
hexio_write test.db 18 $ro_version ; # write-version
hexio_write test.db 24 11223344 ; # change-counter

View File

@ -30,6 +30,7 @@ do_test savepoint-1.1 {
RELEASE sp1;
}
} {}
wal_check_journal_mode savepoint-1.1
do_test savepoint-1.2 {
execsql {
SAVEPOINT sp1;
@ -807,7 +808,8 @@ do_test savepoint-11.6 {
integrity_check savepoint-11.7
do_test savepoint-11.8 {
execsql { ROLLBACK }
execsql { PRAGMA wal_checkpoint }
db close
sqlite3 db test.db
file size test.db
} {8192}

View File

@ -15,6 +15,10 @@ set testdir [file dirname $argv0]
source $testdir/tester.tcl
proc sql {zSql} {
if {0 && $::debug_op} {
puts stderr "$zSql ;"
flush stderr
}
uplevel db eval [list $zSql]
#puts stderr "$zSql ;"
}
@ -67,11 +71,13 @@ proc x_to_y {x} {
# delete_rows XVALUES
#
proc savepoint {zName} {
if {$::debug_op} { puts stderr "savepoint $zName" ; flush stderr }
catch { sql "SAVEPOINT $zName" }
lappend ::lSavepoint [list $zName [array get ::aEntry]]
}
proc rollback {zName} {
if {$::debug_op} { puts stderr "rollback $zName" ; flush stderr }
catch { sql "ROLLBACK TO $zName" }
for {set i [expr {[llength $::lSavepoint]-1}]} {$i>=0} {incr i -1} {
set zSavepoint [lindex $::lSavepoint $i 0]
@ -89,6 +95,7 @@ proc rollback {zName} {
}
proc release {zName} {
if {$::debug_op} { puts stderr "release $zName" ; flush stderr }
catch { sql "RELEASE $zName" }
for {set i [expr {[llength $::lSavepoint]-1}]} {$i>=0} {incr i -1} {
set zSavepoint [lindex $::lSavepoint $i 0]
@ -104,6 +111,7 @@ proc release {zName} {
}
proc insert_rows {lX} {
if {$::debug_op} { puts stderr "insert_rows $lX" ; flush stderr }
foreach x $lX {
set y [x_to_y $x]
@ -116,6 +124,7 @@ proc insert_rows {lX} {
}
proc delete_rows {lX} {
if {$::debug_op} { puts stderr "delete_rows $lX" ; flush stderr }
foreach x $lX {
# Update database [db]
sql "DELETE FROM t1 WHERE x = $x"
@ -164,6 +173,11 @@ proc random_integers {nRes nRange} {
}
#-------------------------------------------------------------------------
set ::debug_op 0
proc debug_ops {} {
set ::debug_op 1
}
proc database_op {} {
set i [expr int(rand()*2)]
if {$i==0} {
@ -185,9 +199,6 @@ proc savepoint_op {} {
set C [lindex $cmds [expr int(rand()*6)]]
set N [lindex $names [expr int(rand()*5)]]
#puts stderr " $C $N ; "
#flush stderr
$C $N
return ok
}

View File

@ -553,6 +553,7 @@ proc reset_db {} {
forcedelete test.db
forcedelete test.db-journal
forcedelete test.db-wal
forcedelete test.db-wal2
sqlite3 db ./test.db
set ::DB [sqlite3_connection_pointer db]
if {[info exists ::SETUP_SQL]} {
@ -2292,17 +2293,32 @@ proc drop_all_indexes {{db db}} {
# Returns true if this test should be run in WAL mode. False otherwise.
#
proc wal_is_wal_mode {} {
expr {[permutation] eq "wal"}
if {[permutation] eq "wal"} { return 1 }
if {[permutation] eq "wal2"} { return 2 }
return 0
}
proc wal_set_journal_mode {{db db}} {
if { [wal_is_wal_mode] } {
$db eval "PRAGMA journal_mode = WAL"
switch -- [wal_is_wal_mode] {
0 {
}
1 {
$db eval "PRAGMA journal_mode = WAL"
}
2 {
$db eval "PRAGMA journal_mode = WAL2"
}
}
}
proc wal_check_journal_mode {testname {db db}} {
if { [wal_is_wal_mode] } {
$db eval { SELECT * FROM sqlite_master }
do_test $testname [list $db eval "PRAGMA main.journal_mode"] {wal}
set expected "wal"
if {[wal_is_wal_mode]==2} {
set expected "wal2"
}
do_test $testname [list $db eval "PRAGMA main.journal_mode"] $expected
}
}

View File

@ -1494,6 +1494,8 @@ static void dynamic_triggers(int nMs){
#include "tt3_stress.c"
#include "tt3_shared.c"
#include "tt3_bcwal2.c"
int main(int argc, char **argv){
struct ThreadTest {
void (*xTest)(int); /* Routine for running this test */
@ -1518,6 +1520,8 @@ int main(int argc, char **argv){
{ stress1, "stress1", 10000 },
{ stress2, "stress2", 60000 },
{ shared1, "shared1", 10000 },
{ bcwal2_1, "bcwal2_1", 100000 },
};
static char *substArgv[] = { 0, "*", 0 };
int i, iArg;

122
test/tt3_bcwal2.c Normal file
View File

@ -0,0 +1,122 @@
/*
** 2011-02-02
**
** 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 is part of the test program "threadtest3". Despite being a C
** file it is not compiled separately, but included by threadtest3.c using
** the #include directive normally used with header files.
**
** This file contains the implementation of test cases:
**
** bcwal2_1
*/
static char *bcwal2_1_checkpointer(int iTid, void *pArg){
Error err = {0}; /* Error code and message */
Sqlite db = {0}; /* SQLite database connection */
int nIter = 0;
opendb(&err, &db, "test.db", 0);
while( !timetostop(&err) ){
sql_script(&err, &db, "PRAGMA wal_checkpoint;");
nIter++;
}
closedb(&err, &db);
print_and_free_err(&err);
return sqlite3_mprintf("%d iterations", nIter);
}
static char *bcwal2_1_integrity(int iTid, void *pArg){
Error err = {0}; /* Error code and message */
Sqlite db = {0}; /* SQLite database connection */
int nIter = 0;
opendb(&err, &db, "test.db", 0);
while( !timetostop(&err) ){
// integrity_check(&err, &db);
sql_script(&err, &db, "SELECT * FROM t1;");
nIter++;
}
closedb(&err, &db);
print_and_free_err(&err);
return sqlite3_mprintf("%d integrity-checks", nIter);
}
static char *bcwal2_1_writer(int iTid, void *pArg){
Error err = {0}; /* Error code and message */
Sqlite db = {0}; /* SQLite database connection */
int nWrite = 0; /* Writes so far */
int nBusy = 0; /* Busy errors so far */
sqlite3_mutex *pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_APP1);
opendb(&err, &db, "test.db", 0);
while( !timetostop(&err) ){
sql_script(&err, &db,
"PRAGMA wal_autocheckpoint = 0;"
"BEGIN CONCURRENT;"
" REPLACE INTO t1 VALUES( abs(random() % 100000), "
" hex(randomblob( abs( random() % 200 ) + 50 ))"
" );"
);
if( err.rc==SQLITE_OK ){
sqlite3_mutex_enter(pMutex);
sql_script(&err, &db, "COMMIT");
sqlite3_mutex_leave(pMutex);
if( err.rc==SQLITE_OK ){
nWrite++;
}else{
clear_error(&err, SQLITE_BUSY);
sql_script(&err, &db, "ROLLBACK");
nBusy++;
}
assert( err.rc!=SQLITE_OK || sqlite3_get_autocommit(db.db)==1 );
}
}
closedb(&err, &db);
print_and_free_err(&err);
return sqlite3_mprintf("%d successful writes, %d busy", nWrite, nBusy);
}
static void bcwal2_1(int nMs){
Error err = {0};
Sqlite db = {0};
Threadset threads = {0};
opendb(&err, &db, "test.db", 1);
sql_script(&err, &db,
"PRAGMA page_size = 1024;"
"PRAGMA journal_mode = wal2;"
"CREATE TABLE t1(ii INTEGER PRIMARY KEY, tt TEXT);"
"CREATE INDEX t1tt ON t1(tt);"
);
setstoptime(&err, nMs);
launch_thread(&err, &threads, bcwal2_1_writer, 0);
launch_thread(&err, &threads, bcwal2_1_writer, 0);
launch_thread(&err, &threads, bcwal2_1_writer, 0);
launch_thread(&err, &threads, bcwal2_1_integrity, 0);
launch_thread(&err, &threads, bcwal2_1_checkpointer, 0);
join_all_threads(&err, &threads);
/* Do a final integrity-check on the db */
integrity_check(&err, &db);
closedb(&err, &db);
print_and_free_err(&err);
}

1035
test/tt3_core.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -282,11 +282,11 @@ ifcapable wal {
INSERT INTO t2 VALUES('x', 'y');
}
lsort [array names ::T1]
} {test.db1 test.db1-journal test.db1-wal}
} {test.db1 test.db1-journal test.db1-wal test.db1-wal2}
do_test 5.1.2 {
lsort [array names ::T2]
} {test.db2 test.db2-journal test.db2-wal}
} {test.db2 test.db2-journal test.db2-wal test.db2-wal2}
db close
tvfs1 delete

View File

@ -1175,7 +1175,7 @@ foreach {tn pgsz works} {
9 32768 1
10 65536 1
11 131072 0
11 1016 0
12 1016 0
} {
if {$::SQLITE_MAX_PAGE_SIZE < $pgsz} {
@ -1185,14 +1185,14 @@ foreach {tn pgsz works} {
for {set pg 1} {$pg <= 3} {incr pg} {
forcecopy testX.db test.db
forcedelete test.db-wal
# Check that the database now exists and consists of three pages. And
# that there is no associated wal file.
#
do_test wal-18.2.$tn.$pg.1 { file exists test.db-wal } 0
do_test wal-18.2.$tn.$pg.2 { file exists test.db } 1
do_test wal-18.2.$tn.$pg.3 { file size test.db } [expr 1024*3]
do_test wal-18.2.$tn.$pg.4 {
# Create a wal file that contains a single frame (database page
@ -1224,16 +1224,16 @@ foreach {tn pgsz works} {
puts -nonewline $fd $framehdr
puts -nonewline $fd $framebody
close $fd
file size test.db-wal
} [wal_file_size 1 $pgsz]
do_test wal-18.2.$tn.$pg.5 {
sqlite3 db test.db
set rc [catch { db one {PRAGMA integrity_check} } msg]
expr { $rc!=0 || $msg!="ok" }
} $works
db close
}
}

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.*:
#

73
test/wal2big.test Normal file
View File

@ -0,0 +1,73 @@
# 2017 September 19
#
# 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.
#
#***********************************************************************
# TESTRUNNER: slow
#
# This file implements regression tests for SQLite library. The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL2" mode.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/malloc_common.tcl
source $testdir/wal_common.tcl
set testprefix wal2big
ifcapable !wal {finish_test ; return }
do_execsql_test 1.0 {
CREATE TABLE t1(a, b, c);
CREATE INDEX t1a ON t1(a);
CREATE INDEX t1b ON t1(b);
CREATE INDEX t1c ON t1(c);
PRAGMA journal_mode = wal2;
PRAGMA journal_size_limit = 10000000;
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<200000
)
INSERT INTO t1 SELECT random(), random(), random() FROM s;
} {wal2 10000000}
do_execsql_test 1.1 {
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<200000
)
INSERT INTO t1 SELECT random(), random(), random() FROM s;
}
do_test 1.2 {
list [expr [file size test.db-wal]>10000000] \
[expr [file size test.db-wal2]>10000000]
} {1 1}
do_test 1.3 {
sqlite3 db2 test.db
execsql {
SELECT count(*) FROM t1;
PRAGMA integrity_check;
} db2
} {400000 ok}
do_test 1.4 {
db2 close
forcecopy test.db test.db2
forcecopy test.db-wal test.db2-wal
forcecopy test.db-wal2 test.db2-wal2
sqlite3 db2 test.db2
execsql {
SELECT count(*) FROM t1;
PRAGMA integrity_check;
}
} {400000 ok}
finish_test

164
test/wal2concurrent.test Normal file
View File

@ -0,0 +1,164 @@
# 2018 December 6
#
# 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 wal2concurrent
ifcapable !concurrent {
finish_test
return
}
#-------------------------------------------------------------------------
# Warm-body test.
#
foreach tn {1 2} {
reset_db
sqlite3 db2 test.db
do_execsql_test 1.0 {
PRAGMA page_size = 1024;
CREATE TABLE t1(x);
CREATE TABLE t2(y);
PRAGMA journal_size_limit = 5000;
PRAGMA journal_mode = wal2;
} {5000 wal2}
do_execsql_test 1.1 {
INSERT INTO t1 VALUES(1);
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(2);
} {}
do_test 1.2 {
execsql {
PRAGMA journal_size_limit = 5000;
INSERT INTO t1 VALUES(3)
} db2
catchsql { COMMIT }
} {1 {database is locked}}
do_catchsql_test 1.3 { COMMIT } {1 {database is locked}}
do_catchsql_test 1.4 { ROLLBACK } {0 {}}
do_test 1.5 {
list [file size test.db-wal] [file size test.db-wal2]
} {2128 0}
do_execsql_test 1.6 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(2);
} {}
do_test 1.7 {
execsql { INSERT INTO t2 VALUES(randomblob(4000)) } db2
list [file size test.db-wal] [file size test.db-wal2]
} {7368 0}
if {$tn==1} {
do_test 1.8 {
execsql {
INSERT INTO t2 VALUES(1);
INSERT INTO t1 VALUES(5);
} db2
list [file size test.db-wal] [file size test.db-wal2]
} {7368 2128}
do_catchsql_test 1.9 { COMMIT } {1 {database is locked}}
do_catchsql_test 1.10 { ROLLBACK } {0 {}}
db close
sqlite3 db test.db
do_execsql_test 1.11 { SELECT * FROM t1 } {1 3 5}
do_execsql_test 1.12 { SELECT count(*) FROM t2 } {2}
} else {
do_test 1.8 {
execsql {
INSERT INTO t2 VALUES(1);
} db2
list [file size test.db-wal] [file size test.db-wal2]
} {7368 1080}
do_catchsql_test 1.9 { COMMIT } {0 {}}
db close
sqlite3 db test.db
do_execsql_test 1.11 { SELECT * FROM t1 } {1 3 2}
do_execsql_test 1.12 { SELECT count(*) FROM t2 } {2}
do_test 1.13 {
list [file size test.db-wal] [file size test.db-wal2]
} {7368 2128}
}
}
do_multiclient_test tn {
do_test 2.$tn.1 {
sql1 {
PRAGMA auto_vacuum = OFF;
CREATE TABLE t1(x UNIQUE);
CREATE TABLE t2(x UNIQUE);
PRAGMA journal_mode = wal2;
PRAGMA journal_size_limit = 15000;
}
} {wal2 15000}
do_test 2.$tn.2 {
sql1 {
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<=10
)
INSERT INTO t1 SELECT randomblob(800) FROM s;
}
} {}
do_test 2.$tn.3 {
sql1 { DELETE FROM t1 WHERE (rowid%4)==0 }
list [expr [file size test.db-wal]>15000] \
[expr [file size test.db-wal2]>15000]
} {1 0}
do_test 2.$tn.4 {
sql1 { PRAGMA wal_checkpoint; }
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(randomblob(800));
}
} {}
do_test 2.$tn.5 {
sql2 {
PRAGMA journal_size_limit = 15000;
INSERT INTO t2 VALUES(randomblob(800));
INSERT INTO t2 VALUES(randomblob(800));
INSERT INTO t2 VALUES(randomblob(800));
INSERT INTO t2 VALUES(randomblob(800));
INSERT INTO t2 VALUES(randomblob(800));
DELETE FROM t2;
}
list [expr [file size test.db-wal]>15000] \
[expr [file size test.db-wal2]>15000]
} {1 1}
do_test 2.$tn.6 {
sql1 {
INSERT INTO t1 VALUES(randomblob(800));
COMMIT;
PRAGMA integrity_check;
}
} {ok}
}
finish_test

52
test/wal2fault.test Normal file
View File

@ -0,0 +1,52 @@
# 2010 May 03
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL" mode.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
source $testdir/lock_common.tcl
ifcapable !wal {finish_test ; return }
set testprefix wal2fault
do_execsql_test 1.0 {
CREATE TABLE t1(x,y);
PRAGMA journal_mode = wal2;
WITH s(i) AS ( SELECT 100 UNION ALL SELECT i-1 FROM s WHERE (i-1)>0 )
INSERT INTO t1 SELECT i, randomblob(i) FROM s;
WITH s(i) AS ( SELECT 100 UNION ALL SELECT i-1 FROM s WHERE (i-1)>0 )
INSERT INTO t1 SELECT i, randomblob(i) FROM s;
} {wal2}
do_test 1.1 {
expr [file size test.db-wal]>10000
} {1}
faultsim_save_and_close
do_faultsim_test 1 -prep {
faultsim_restore_and_reopen
execsql {
PRAGMA journal_size_limit = 10000;
SELECT count(*) FROM sqlite_master;
}
} -body {
execsql {
INSERT INTO t1 VALUES(1, 2);
}
} -test {
faultsim_test_result {0 {}}
}
finish_test

106
test/wal2lock.test Normal file
View File

@ -0,0 +1,106 @@
# 2018 December 15
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL2" mode.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/malloc_common.tcl
source $testdir/wal_common.tcl
set testprefix wal2lock
ifcapable !wal {finish_test ; return }
db close
testvfs tvfs
sqlite3 db test.db -vfs tvfs
do_execsql_test 1.0 {
PRAGMA journal_mode = wal2;
CREATE TABLE y1(y, yy);
CREATE INDEX y1y ON y1(y);
CREATE INDEX y1yy ON y1(yy);
INSERT INTO y1 VALUES(1, 2), (3, 4), (5, 6);
} {wal2}
tvfs script vfs_callback
tvfs filter xShmLock
set ::lock [list]
proc vfs_callback {func file name lock} {
lappend ::lock $lock
return SQLITE_OK
}
do_execsql_test 1.1.1 {
SELECT * FROM y1
} {1 2 3 4 5 6}
do_test 1.1.2 {
set ::lock
} {{4 1 lock shared} {4 1 unlock shared}}
set ::bFirst 1
proc vfs_callback {func file name lock} {
if {$::bFirst} {
set ::bFirst 0
return SQLITE_BUSY
}
return SQLITE_OK
}
do_execsql_test 1.2 {
SELECT * FROM y1
} {1 2 3 4 5 6}
set ::bFirst 1
proc vfs_callback {func file name lock} {
if {$::bFirst} {
set ::bFirst 0
return SQLITE_IOERR
}
return SQLITE_OK
}
do_catchsql_test 1.3 {
SELECT * FROM y1
} {1 {disk I/O error}}
puts "# Warning: This next test case causes SQLite to call xSleep(1) 100 times."
puts "# Normally this equates to a delay of roughly 10 seconds, but if SQLite"
puts "# is built on unix without HAVE_USLEEP defined, it may be much longer."
proc vfs_callback {func file name lock} { return SQLITE_BUSY }
do_catchsql_test 1.4 {
SELECT * FROM y1
} {1 {locking protocol}}
proc vfs_callback {func file name lock} { return SQLITE_OK }
sqlite3 db2 test.db -vfs tvfs
set ::bFirst 1
proc vfs_callback {func file name lock} {
if {$::bFirst} {
set ::bFirst 0
db2 eval { INSERT INTO y1 VALUES(7, 8) }
}
}
do_execsql_test 1.5.1 {
SELECT * FROM y1
} {1 2 3 4 5 6 7 8}
do_execsql_test 1.5.2 {
SELECT * FROM y1
} {1 2 3 4 5 6 7 8}
db close
db2 close
tvfs delete
finish_test

81
test/wal2openclose.test Normal file
View File

@ -0,0 +1,81 @@
# 2017 September 19
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL2" mode.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/malloc_common.tcl
source $testdir/wal_common.tcl
set testprefix wal2openclose
ifcapable !wal {finish_test ; return }
do_execsql_test 1.0 {
CREATE TABLE t1(a, b, c);
PRAGMA journal_mode = wal2;
PRAGMA wal_autocheckpoint = 0;
PRAGMA journal_size_limit = 75000;
} {wal2 0 75000}
do_test 1.1 {
for {set ii 1} {$ii <= 200} {incr ii} {
execsql {
INSERT INTO t1 VALUES($ii, $ii, $ii);
}
}
expr ([file size test.db-wal2] - 75000) > 30000
} {1}
do_test 1.2 {
db close
list [file exists test.db-wal] [file exists test.db-wal2]
} {0 0}
sqlite3 db test.db
do_execsql_test 1.3 {
SELECT sum(c) FROM t1
} {20100}
db close
#-------------------------------------------------------------------------
reset_db
do_execsql_test 2.0 {
CREATE TABLE t1(a, b, c);
PRAGMA journal_mode = wal2;
INSERT INTO t1 VALUES(1, 2, 3);
} {wal2}
db_save_and_close
db_restore_and_reopen
do_execsql_test 2.1 {
SELECT * FROM t1;
} {1 2 3}
do_test 2.2 {
sqlite3 db2 test.db
db2 eval {INSERT INTO t1 VALUES(4, 5, 6)}
db2 close
} {}
breakpoint
db close
sqlite3 db test.db
do_execsql_test 2.2 {
SELECT * FROM t1;
} {1 2 3 4 5 6}
finish_test

271
test/wal2recover.test Normal file
View File

@ -0,0 +1,271 @@
# 2018 December 13
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL2" mode.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/malloc_common.tcl
source $testdir/wal_common.tcl
set testprefix wal2recover
ifcapable !wal {finish_test ; return }
proc db_copy {from to} {
forcecopy $from $to
forcecopy ${from}-wal ${to}-wal
forcecopy ${from}-wal2 ${to}-wal2
}
do_execsql_test 1.0 {
CREATE TABLE t1(a, b, c);
CREATE INDEX t1a ON t1(a);
CREATE INDEX t1b ON t1(b);
CREATE INDEX t1c ON t1(c);
PRAGMA journal_mode = wal2;
PRAGMA journal_size_limit = 15000;
PRAGMA wal_autocheckpoint = 0;
} {wal2 15000 0}
do_test 1.1 {
for {set i 1} {$i <= 1000} {incr i} {
execsql { INSERT INTO t1 VALUES(random(), random(), random()) }
db_copy test.db test.db2
sqlite3 db2 test.db
set res [execsql {
SELECT count(*) FROM t1;
PRAGMA integrity_check;
} db2]
db2 close
if {$res != [list $i ok]} {
error "failure on iteration $i"
}
}
set {} {}
} {}
#--------------------------------------------------------------------------
reset_db
do_execsql_test 2.0 {
CREATE TABLE t1(x UNIQUE);
CREATE TABLE t2(x UNIQUE);
PRAGMA journal_mode = wal2;
PRAGMA journal_size_limit = 10000;
PRAGMA wal_autocheckpoint = 0;
BEGIN;
INSERT INTO t1 VALUES(randomblob(4000));
INSERT INTO t1 VALUES(randomblob(4000));
INSERT INTO t1 VALUES(randomblob(4000));
COMMIT;
BEGIN;
INSERT INTO t2 VALUES(randomblob(4000));
INSERT INTO t2 VALUES(randomblob(4000));
INSERT INTO t2 VALUES(randomblob(4000));
COMMIT;
} {wal2 10000 0}
do_test 2.0.1 {
list [file size test.db] [file size test.db-wal] [file size test.db-wal2]
} {5120 28328 28328}
# Test recovery with both wal files intact.
#
do_test 2.1 {
db_copy test.db test.db2
sqlite3 db2 test.db2
execsql {
SELECT count(*) FROM t1;
SELECT count(*) FROM t2;
PRAGMA integrity_check;
} db2
} {3 3 ok}
do_test 2.2 {
db2 close
db_copy test.db test.db2
hexio_write test.db2-wal 16 12345678
sqlite3 db2 test.db2
execsql {
SELECT count(*) FROM t1;
SELECT count(*) FROM t2;
} db2
} {0 3}
do_test 2.3 {
db2 close
db_copy test.db test.db2
hexio_write test.db2-wal2 16 12345678
sqlite3 db2 test.db2
execsql {
SELECT count(*) FROM t1;
SELECT count(*) FROM t2;
PRAGMA integrity_check;
} db2
} {3 0 ok}
do_test 2.4 {
db2 close
db_copy test.db test.db2
forcecopy test.db-wal test.db2-wal2
sqlite3 db2 test.db2
execsql {
SELECT count(*) FROM t1;
SELECT count(*) FROM t2;
PRAGMA integrity_check;
} db2
} {3 0 ok}
do_test 2.5 {
db2 close
db_copy test.db test.db2
forcecopy test.db-wal test.db2-wal2
forcecopy test.db-wal2 test.db2-wal
sqlite3 db2 test.db2
execsql {
SELECT count(*) FROM t1;
SELECT count(*) FROM t2;
PRAGMA integrity_check;
} db2
} {3 3 ok}
do_test 2.6 {
db2 close
db_copy test.db test.db2
forcecopy test.db-wal test.db2-wal2
close [open test.db-wal w]
sqlite3 db2 test.db2
execsql {
SELECT count(*) FROM t1;
SELECT count(*) FROM t2;
PRAGMA integrity_check;
} db2
} {3 0 ok}
do_test 2.7 {
db2 close
db_copy test.db test.db2
forcedelete test.db2-wal
sqlite3 db2 test.db2
execsql {
SELECT count(*) FROM t1;
SELECT count(*) FROM t2;
PRAGMA integrity_check;
} db2
} {0 0 ok}
db2 close
#-------------------------------------------------------------------------
#
reset_db
do_execsql_test 3.0 {
CREATE TABLE t1(a TEXT, b TEXT, c TEXT);
CREATE INDEX t1a ON t1(a);
CREATE INDEX t1b ON t1(b);
CREATE INDEX t1c ON t1(c);
PRAGMA journal_mode = wal2;
PRAGMA journal_size_limit = 10000;
PRAGMA wal_autocheckpoint = 0;
PRAGMA cache_size = 5;
} {wal2 10000 0}
do_execsql_test 3.1 {
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s where i < 200)
INSERT INTO t1 SELECT i, i, i FROM s;
INSERT INTO t1 VALUES(201, 201, 201);
} {}
do_test 3.2 {
list [file size test.db] [file size test.db-wal] [file size test.db-wal2]
} {5120 15752 4224}
do_test 3.3 {
forcecopy test.db test.db2
forcecopy test.db-wal test.db2-wal
forcecopy test.db-wal2 test.db2-wal2
sqlite3 db2 test.db2
execsql {
PRAGMA journal_size_limit = 10000;
PRAGMA wal_autocheckpoint = 0;
PRAGMA cache_size = 5;
BEGIN;
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s where i < 200)
INSERT INTO t1 SELECT i, i, i FROM s;
} db2
list [file size test.db2] [file size test.db2-wal] [file size test.db2-wal2]
} {5120 15752 23088}
if {$tcl_platform(platform)!="windows"} {
# These cannot be run under windows, as the *-shm file may not be read
# while it is locked by the database connection.
do_test 3.4 {
set fd [open test.db2-shm]
fconfigure $fd -translation binary
set data [read $fd]
close $fd
set fd [open test.db-shm w]
fconfigure $fd -translation binary
puts -nonewline $fd $data
close $fd
execsql {
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s where i < 10)
INSERT INTO t1 SELECT i, i, i FROM s;
SELECT count(*) FROM t1;
PRAGMA integrity_check;
}
} {211 ok}
do_test 3.5 {
list [file size test.db] [file size test.db-wal] [file size test.db-wal2]
} {5120 15752 18896}
}
#-------------------------------------------------------------------------
#
reset_db
do_execsql_test 4.0 {
PRAGMA journal_mode = wal2;
CREATE TABLE xyz(x, y, z);
INSERT INTO xyz VALUES('x', 'y', 'z');
} {wal2}
db close
do_test 4.1 {
close [open test.db-wal w]
file mkdir test.db-wal2
sqlite3 db test.db
catchsql { SELECT * FROM xyz }
} {1 {unable to open database file}}
db close
file delete test.db-wal2
db2 close
do_test 4.2 {
sqlite3 db test.db
execsql {
INSERT INTO xyz VALUES('a', 'b', 'c');
}
forcecopy test.db test.db2
forcecopy test.db-wal test.db2-wal
forcedelete test.db2-wal2
file mkdir test.db2-wal2
sqlite3 db2 test.db2
catchsql { SELECT * FROM xyz } db2
} {1 {unable to open database file}}
db2 close
file delete test.db2-wal2
finish_test

320
test/wal2recover2.test Normal file
View File

@ -0,0 +1,320 @@
# 2018 December 13
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL2" mode.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/malloc_common.tcl
source $testdir/wal_common.tcl
set testprefix wal2recover2
ifcapable !wal {finish_test ; return }
do_execsql_test 1.0 {
CREATE TABLE t1(x);
CREATE TABLE t2(x);
WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<1500 )
INSERT INTO t1 SELECT i FROM s;
WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<1500 )
INSERT INTO t2 SELECT i FROM s;
PRAGMA journal_mode = wal2;
PRAGMA journal_size_limit = 10000;
} {wal2 10000}
set ::L 1125750
set ::M 1126500
set ::H 1127250
do_execsql_test 1.1 {
UPDATE t1 SET x=x+1;
UPDATE t2 SET x=x+1 WHERE rowid<=750;
SELECT sum(x) FROM t1;
SELECT sum(x) FROM t2;
} [list $H $M]
do_test 1.2 {
list [file size test.db] [file size test.db-wal] [file size test.db-wal2]
} {31744 14704 7368}
proc cksum {zIn data} {
if {[string length $zIn]==0} {
set s0 0
set s1 0
} else {
set s0 [hexio_get_int [string range $zIn 0 7]]
set s1 [hexio_get_int [string range $zIn 8 15]]
}
set n [expr [string length $data] / 8]
for {set i 0} {$i < $n} {incr i 2} {
set x0 [hexio_get_int -l [string range $data [expr $i*8] [expr $i*8+7]]]
set x1 [hexio_get_int -l [string range $data [expr $i*8+8] [expr $i*8+8+7]]]
set s0 [expr ($s0 + $x0 + $s1) & 0xFFFFFFFF]
set s1 [expr ($s1 + $x1 + $s0) & 0xFFFFFFFF]
}
return "[hexio_render_int32 $s0][hexio_render_int32 $s1]"
}
proc fix_wal_cksums {file} {
# Fix the checksum on the wal header.
set data [hexio_read $file 0 32]
set cksum [cksum {} [string range $data 0 47]]
set salt [hexio_read $file 16 8]
hexio_write $file 24 $cksum
# Fix the checksums for all pages in the wal file.
set pgsz [hexio_get_int [hexio_read $file 8 4]]
set sz [file size $file]
for {set off 32} {$off < $sz} {incr off [expr $pgsz+24]} {
set e [hexio_read $file $off 8]
set cksum [cksum $cksum $e]
set p [hexio_read $file [expr $off+24] $pgsz]
set cksum [cksum $cksum $p]
hexio_write $file [expr $off+8] $salt
hexio_write $file [expr $off+16] $cksum
}
}
proc wal_incr_hdrfield {file field} {
switch -- $field {
nCkpt { set offset 12 }
salt0 { set offset 16 }
salt1 { set offset 20 }
default {
error "unknown field $field - should be \"nCkpt\", \"salt0\" or \"salt1\""
}
}
# Increment the value in the wal header.
set v [hexio_get_int [hexio_read $file $offset 4]]
incr v
hexio_write $file $offset [hexio_render_int32 $v]
# Fix various checksums
fix_wal_cksums $file
}
proc wal_set_nckpt {file val} {
# Increment the value in the wal header.
hexio_write $file 12 [hexio_render_int32 $val]
# Fix various checksums
fix_wal_cksums $file
}
proc wal_set_follow {file prevfile} {
set pgsz [hexio_get_int [hexio_read $prevfile 8 4]]
set sz [file size $prevfile]
set cksum [hexio_read $prevfile [expr $sz-$pgsz-8] 8]
hexio_write $file 16 $cksum
fix_wal_cksums $file
}
foreach {tn file field} {
1 test.db2-wal salt0
2 test.db2-wal salt1
3 test.db2-wal nCkpt
4 test.db2-wal2 salt0
5 test.db2-wal2 salt1
6 test.db2-wal2 nCkpt
} {
do_test 1.3.$tn {
forcecopy test.db test.db2
forcecopy test.db-wal test.db2-wal
forcecopy test.db-wal2 test.db2-wal2
wal_incr_hdrfield $file $field
sqlite3 db2 test.db2
execsql {
SELECT sum(x) FROM t1;
SELECT sum(x) FROM t2;
} db2
} [list $H $L]
db2 close
}
do_test 1.4 {
forcecopy test.db test.db2
forcecopy test.db-wal2 test.db2-wal
forcedelete test.db2-wal2
sqlite3 db2 test.db2
execsql {
SELECT sum(x) FROM t1;
SELECT sum(x) FROM t2;
} db2
} [list $L $M]
do_test 1.5 {
db2 close
forcecopy test.db test.db2
forcecopy test.db-wal2 test.db2-wal
forcecopy test.db-wal test.db2-wal2
sqlite3 db2 test.db2
execsql {
SELECT sum(x) FROM t1;
SELECT sum(x) FROM t2;
} db2
} [list $H $M]
db2 close
foreach {tn file field} {
1 test.db2-wal salt0
2 test.db2-wal salt1
3 test.db2-wal2 salt0
4 test.db2-wal2 salt1
} {
do_test 1.6.$tn {
forcecopy test.db test.db2
forcecopy test.db-wal2 test.db2-wal
forcecopy test.db-wal test.db2-wal2
wal_incr_hdrfield $file $field
sqlite3 db2 test.db2
execsql {
SELECT sum(x) FROM t1;
SELECT sum(x) FROM t2;
} db2
} [list $H $L]
db2 close
}
foreach {tn nCkpt1 nCkpt2 res} [list \
1 2 1 "$H $M" \
2 2 2 "$L $M" \
3 3 1 "$H $L" \
4 15 14 "$H $M" \
5 0 15 "$H $M" \
6 1 15 "$L $M" \
] {
do_test 1.7.$tn {
forcecopy test.db test.db2
forcecopy test.db-wal2 test.db2-wal
forcecopy test.db-wal test.db2-wal2
wal_set_nckpt test.db2-wal2 $nCkpt2
wal_set_nckpt test.db2-wal $nCkpt1
wal_set_follow test.db2-wal test.db2-wal2
sqlite3 db2 test.db2
execsql {
SELECT sum(x) FROM t1;
SELECT sum(x) FROM t2;
} db2
} $res
db2 close
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 1.8.1 {
PRAGMA autovacuum = 0;
PRAGMA page_size = 4096;
CREATE TABLE t1(x);
CREATE TABLE t2(x);
WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<1500 )
INSERT INTO t1 SELECT i FROM s;
WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<1500 )
INSERT INTO t2 SELECT i FROM s;
PRAGMA journal_mode = wal2;
PRAGMA journal_size_limit = 10000;
WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<1500 )
INSERT INTO t2 SELECT i FROM s;
} {wal2 10000}
do_test 1.8.2 {
list [file size test.db-wal] [file size test.db-wal2]
} {24752 0}
do_execsql_test 1.8.3 { PRAGMA user_version = 123 }
do_test 1.8.4 {
list [file size test.db-wal] [file size test.db-wal2]
} {24752 4152}
do_test 1.8.5 {
hexio_write test.db-wal2 [expr 56+16] 0400
fix_wal_cksums test.db-wal2
} {}
ifcapable oversize_cell_check {
set msg {database disk image is malformed}
} else {
set msg {malformed database schema (?)}
}
do_test 1.8.6 {
forcecopy test.db test.db2
forcecopy test.db-wal test.db2-wal
forcecopy test.db-wal2 test.db2-wal2
sqlite3 db2 test.db2
catchsql { SELECT * FROM sqlite_master } db2
} [list 1 $msg]
db2 close
#-------------------------------------------------------------------------
reset_db
do_execsql_test 1.0 {
CREATE TABLE t1(a, b, c);
CREATE INDEX t1a ON t1(a);
CREATE INDEX t1b ON t1(b);
CREATE INDEX t1c ON t1(c);
PRAGMA journal_mode = wal2;
INSERT INTO t1 VALUES(randomblob(50), randomblob(50), randomblob(50));
INSERT INTO t1 VALUES(randomblob(50), randomblob(50), randomblob(50));
INSERT INTO t1 VALUES(randomblob(50), randomblob(50), randomblob(50));
PRAGMA journal_size_limit = 5000;
INSERT INTO t1 VALUES(randomblob(50), randomblob(50), randomblob(50));
INSERT INTO t1 VALUES(randomblob(50), randomblob(50), randomblob(50));
INSERT INTO t1 VALUES(randomblob(50), randomblob(50), randomblob(50));
INSERT INTO t1 VALUES(randomblob(50), randomblob(50), randomblob(50));
INSERT INTO t1 VALUES(randomblob(50), randomblob(50), randomblob(50));
} {wal2 5000}
do_test 2.1 {
forcecopy test.db test.db2
forcecopy test.db-wal2 test.db2-wal
forcecopy test.db-wal test.db2-wal2
hexio_write test.db2-wal 5000 1234567890
} {5}
do_test 2.2 {
sqlite3 db2 test.db2
breakpoint
execsql {
SELECT count(*) FROM t1;
PRAGMA integrity_check
} db2
} {4 ok}
do_test 2.3 {
execsql {
INSERT INTO t1 VALUES(randomblob(50), randomblob(50), randomblob(50));
SELECT count(*) FROM t1;
PRAGMA integrity_check
} db2
} {5 ok}
finish_test

52
test/wal2recover3.test Normal file
View File

@ -0,0 +1,52 @@
# 2022 June 28
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL2" mode.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/malloc_common.tcl
source $testdir/wal_common.tcl
set testprefix wal2recover3
ifcapable !wal {finish_test ; return }
do_execsql_test 1.0 {
CREATE TABLE t1(x);
CREATE TABLE t2(x);
PRAGMA journal_mode = wal2;
PRAGMA wal_autocheckpoint = 0;
PRAGMA journal_size_limit = 10000;
} {wal2 0 10000}
do_execsql_test 1.1 {
WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<1500 )
INSERT INTO t1 SELECT i FROM s;
WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<1500 )
INSERT INTO t2 SELECT i FROM s;
}
db_save_and_close
set fd [open sv_test.db-wal2 r+]
seek $fd 4000
puts -nonewline $fd 0
close $fd
db_restore_and_reopen
do_execsql_test 1.2 {
SELECT sql FROM sqlite_schema;
} {{CREATE TABLE t1(x)} {CREATE TABLE t2(x)}}
finish_test

92
test/wal2rewrite.test Normal file
View File

@ -0,0 +1,92 @@
# 2017 September 19
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL2" mode.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/malloc_common.tcl
source $testdir/wal_common.tcl
set testprefix wal2rewrite
ifcapable !wal {finish_test ; return }
proc filesize {filename} {
if {[file exists $filename]} {
return [file size $filename]
}
return 0
}
foreach {tn jrnlmode} {
1 wal
2 wal2
} {
reset_db
execsql "PRAGMA journal_mode = $jrnlmode"
do_execsql_test $tn.1 {
PRAGMA journal_size_limit = 10000;
PRAGMA cache_size = 5;
PRAGMA wal_autocheckpoint = 10;
CREATE TABLE t1(a INTEGER PRIMARY KEY, b INTEGER, c BLOB);
CREATE INDEX t1b ON t1(b);
CREATE INDEX t1c ON t1(c);
WITH s(i) AS (
SELECT 1 UNION SELECT i+1 FROM s WHERE i<10
)
INSERT INTO t1 SELECT i, i, randomblob(800) FROM s;
} {10000 10}
for {set i 0} {$i < 4} {incr i} {
do_execsql_test $tn.$i.1 {
UPDATE t1 SET c=randomblob(800) WHERE (b%10)==5 AND ($i%2)
}
do_execsql_test $tn.$i.2 {
BEGIN;
UPDATE t1 SET b=b+10, c=randomblob(800);
UPDATE t1 SET b=b+10, c=randomblob(800);
UPDATE t1 SET b=b+10, c=randomblob(800);
UPDATE t1 SET b=b+10, c=randomblob(800);
UPDATE t1 SET b=b+10, c=randomblob(800);
UPDATE t1 SET b=b+10, c=randomblob(800);
UPDATE t1 SET b=b+10, c=randomblob(800);
UPDATE t1 SET b=b+10, c=randomblob(800);
UPDATE t1 SET b=b+10, c=randomblob(800);
UPDATE t1 SET b=b+10, c=randomblob(800);
}
execsql COMMIT
do_test $tn.$i.3 { expr [filesize test.db-wal] < 100000 } 1
do_test $tn.$i.4 { expr [filesize test.db-wal2] < 100000 } 1
set sum [db eval {SELECT sum(b), md5sum(c) FROM t1}]
do_test $tn.$i.5 {
foreach f [glob -nocomplain test.db2*] {forcedelete $f}
foreach f [glob -nocomplain test.db*] {
forcecopy $f [string map {test.db test.db2} $f]
}
sqlite3 db2 test.db2
db2 eval {SELECT sum(b), md5sum(c) FROM t1}
} $sum
db2 close
}
}
finish_test

62
test/wal2rollback.test Normal file
View File

@ -0,0 +1,62 @@
# 2017 September 19
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL2" mode.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/malloc_common.tcl
source $testdir/wal_common.tcl
set testprefix wal2rollback
ifcapable !wal {finish_test ; return }
do_execsql_test 1.0 {
CREATE TABLE t1(a, b, c);
CREATE TABLE t2(a, b, c);
CREATE INDEX i1 ON t1(a);
CREATE INDEX i2 ON t1(b);
PRAGMA journal_mode = wal2;
PRAGMA cache_size = 5;
PRAGMA journal_size_limit = 10000;
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 1000
)
INSERT INTO t1 SELECT i, i, randomblob(200) FROM s;
} {wal2 10000}
do_test 1.1 {
expr [file size test.db-wal] > 10000
} 1
do_test 1.2 {
execsql {
BEGIN;
UPDATE t1 SET b=b+1;
INSERT INTO t2 VALUES(1,2,3);
}
expr [file size test.db-wal2] > 10000
} {1}
breakpoint
do_execsql_test 1.3 {
ROLLBACK;
SELECT * FROM t2;
SELECT count(*) FROM t1 WHERE a=b;
PRAGMA integrity_check;
} {1000 ok}
finish_test

74
test/wal2savepoint.test Normal file
View File

@ -0,0 +1,74 @@
# 2018 December 13
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL2" mode.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/malloc_common.tcl
source $testdir/wal_common.tcl
set testprefix wal2savepoint
ifcapable !wal {finish_test ; return }
do_execsql_test 1.0 {
CREATE TABLE t1(a, b, c);
CREATE INDEX t1a ON t1(a);
CREATE INDEX t1b ON t1(b);
CREATE INDEX t1c ON t1(c);
PRAGMA journal_mode = wal2;
PRAGMA journal_size_limit = 15000;
PRAGMA wal_autocheckpoint = 0;
PRAGMA cache_size = 5;
} {wal2 15000 0}
do_execsql_test 1.1 {
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s where i < 200)
INSERT INTO t1 SELECT random(), random(), random() FROM s;
} {}
do_test 1.2 {
list [file size test.db] [file size test.db-wal2] \
[expr [file size test.db-wal]>20000]
} {5120 0 1}
do_execsql_test 1.3 {
BEGIN;
SAVEPOINT abc;
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s where i < 100)
INSERT INTO t1 SELECT random(), random(), random() FROM s;
ROLLBACK TO abc;
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s where i < 10)
INSERT INTO t1 SELECT random(), random(), random() FROM s;
COMMIT;
SELECT count(*) FROM t1;
PRAGMA integrity_check;
} {210 ok}
do_execsql_test 1.4 {
BEGIN;
SAVEPOINT abc;
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s where i < 100)
INSERT INTO t1 SELECT random(), random(), random() FROM s;
ROLLBACK TO abc;
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s where i < 10)
INSERT INTO t1 SELECT random(), random(), random() FROM s;
COMMIT;
SELECT count(*) FROM t1;
PRAGMA integrity_check;
} {220 ok}
finish_test

475
test/wal2simple.test Normal file
View File

@ -0,0 +1,475 @@
# 2017 September 19
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL2" mode.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/malloc_common.tcl
source $testdir/wal_common.tcl
set testprefix wal2simple
ifcapable !wal {finish_test ; return }
#-------------------------------------------------------------------------
# The following tests verify that a client can switch in and out of wal
# and wal2 mode. But that it is not possible to change directly from wal
# to wal2, or from wal2 to wal mode.
#
do_execsql_test 1.1.0 {
PRAGMA journal_mode = wal2
} {wal2}
execsql { SELECT * FROM sqlite_master}
do_execsql_test 1.x {
PRAGMA journal_mode;
PRAGMA main.journal_mode;
} {wal2 wal2}
db close
do_test 1.1.1 { file size test.db } {1024}
do_test 1.1.2 { hexio_read test.db 18 2 } 0303
sqlite3 db test.db
do_execsql_test 1.2.0 {
SELECT * FROM sqlite_master;
PRAGMA journal_mode = delete;
} {delete}
db close
do_test 1.2.1 { file size test.db } {1024}
do_test 1.2.2 { hexio_read test.db 18 2 } 0101
sqlite3 db test.db
do_execsql_test 1.3.0 {
SELECT * FROM sqlite_master;
PRAGMA journal_mode = wal;
} {wal}
db close
do_test 1.3.1 { file size test.db } {1024}
do_test 1.3.2 { hexio_read test.db 18 2 } 0202
sqlite3 db test.db
do_catchsql_test 1.4.0 {
PRAGMA journal_mode = wal2;
} {1 {cannot change from wal to wal2 mode}}
do_execsql_test 1.4.1 {
PRAGMA journal_mode = wal;
PRAGMA journal_mode = delete;
PRAGMA journal_mode = wal2;
PRAGMA journal_mode = wal2;
} {wal delete wal2 wal2}
do_catchsql_test 1.4.2 {
PRAGMA journal_mode = wal;
} {1 {cannot change from wal2 to wal mode}}
db close
do_test 1.4.3 { hexio_read test.db 18 2 } 0303
#-------------------------------------------------------------------------
# Test that recovery in wal2 mode works.
#
forcedelete test.db test.db-wal test.db-wal2
reset_db
do_execsql_test 2.0 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
PRAGMA journal_mode = wal2;
PRAGMA journal_size_limit = 5000;
} {wal2 5000}
proc wal_hook {DB nm nFrame} { $DB eval { PRAGMA wal_checkpoint } }
db wal_hook {wal_hook db}
for {set i 1} {$i <= 200} {incr i} {
execsql { INSERT INTO t1 VALUES(NULL, randomblob(100)) }
set res [db eval { SELECT sum(a), md5sum(b) FROM t1 }]
do_test 2.1.$i {
foreach f [glob -nocomplain test.db2*] { forcedelete $f }
forcecopy test.db test.db2
forcecopy test.db-wal test.db2-wal
forcecopy test.db-wal2 test.db2-wal2
sqlite3 db2 test.db2
db2 eval { SELECT sum(a), md5sum(b) FROM t1 }
} $res
db2 close
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 3.0 {
CREATE TABLE t1(x BLOB, y INTEGER PRIMARY KEY);
CREATE INDEX i1 ON t1(x);
PRAGMA cache_size = 5;
PRAGMA journal_mode = wal2;
} {wal2}
do_test 3.1 {
execsql BEGIN
for {set i 1} {$i < 1000} {incr i} {
execsql { INSERT INTO t1 VALUES(randomblob(800), $i) }
}
execsql COMMIT
} {}
do_execsql_test 3.2 {
PRAGMA integrity_check;
} {ok}
#-------------------------------------------------------------------------
catch { db close }
foreach f [glob -nocomplain test.db*] { forcedelete $f }
reset_db
do_execsql_test 4.0 {
CREATE TABLE t1(x, y);
PRAGMA journal_mode = wal2;
} {wal2}
do_execsql_test 4.1 {
SELECT * FROM t1;
} {}
do_execsql_test 4.2 {
INSERT INTO t1 VALUES(1, 2);
} {}
do_execsql_test 4.3 {
SELECT * FROM t1;
} {1 2}
do_test 4.4 {
sqlite3 db2 test.db
execsql { SELECT * FROM t1 } db2
} {1 2}
do_test 4.5 {
lsort [glob test.db*]
} {test.db test.db-shm test.db-wal test.db-wal2}
do_test 4.6 {
db close
db2 close
sqlite3 db test.db
execsql { SELECT * FROM t1 }
} {1 2}
do_execsql_test 4.7 {
PRAGMA journal_size_limit = 4000;
INSERT INTO t1 VALUES(3, 4);
INSERT INTO t1 VALUES(5, 6);
INSERT INTO t1 VALUES(7, 8);
INSERT INTO t1 VALUES(9, 10);
INSERT INTO t1 VALUES(11, 12);
INSERT INTO t1 VALUES(13, 14);
INSERT INTO t1 VALUES(15, 16);
INSERT INTO t1 VALUES(17, 18);
SELECT * FROM t1;
} {4000 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18}
do_test 4.8 {
sqlite3 db2 test.db
execsql { SELECT * FROM t1 } db2
} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18}
do_test 4.9 {
db close
db2 close
lsort [glob test.db*]
} {test.db}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 5.0 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
CREATE INDEX i1 ON t1(b, c);
PRAGMA journal_mode = wal2;
PRAGMA journal_size_limit = 4000;
} {wal2 4000}
proc wal_hook {DB nm nFrame} {
$DB eval { PRAGMA wal_checkpoint }
}
db wal_hook [list wal_hook db]
foreach js {4000 8000 12000} {
foreach NROW [list 100 200 300 400 500 600 1000] {
do_test 5.$js.$NROW.1 {
db eval "DELETE FROM t1"
db eval "PRAGMA journal_size_limit = $js"
set nTotal 0
for {set i 0} {$i < $NROW} {incr i} {
db eval { INSERT INTO t1 VALUES($i, $i, randomblob(abs(random()%50))) }
incr nTotal $i
}
set {} {}
} {}
do_test 5.$js.$NROW.2 {
sqlite3 db2 test.db
db2 eval {
PRAGMA integrity_check;
SELECT count(*), sum(b) FROM t1;
}
} [list ok $NROW $nTotal]
db2 close
}
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 6.0 {
CREATE TABLE tx(x);
PRAGMA journal_mode = wal2;
PRAGMA journal_size_limit = 3500;
} {wal2 3500}
do_test 6.1 {
for {set i 0} {$i < 10} {incr i} {
execsql "CREATE TABLE t$i (x);"
}
} {}
do_test 6.2.1 {
foreach f [glob -nocomplain test.db2*] { forcedelete $f }
forcecopy test.db-wal2 test.db2-wal2
sqlite3 db2 test.db2
db2 eval { SELECT * FROM sqlite_master }
} {}
do_test 6.2.2 {
db2 eval {
PRAGMA journal_mode = wal2;
SELECT * FROM sqlite_master;
}
} {wal2}
do_test 6.3.1 {
db2 close
foreach f [glob -nocomplain test.db2*] { forcedelete $f }
forcecopy test.db-wal2 test.db2-wal2
forcecopy test.db test.db2
sqlite3 db2 test.db2
db2 eval { SELECT * FROM sqlite_master }
} {table tx tx 2 {CREATE TABLE tx(x)}}
do_test 6.3.2 {
db2 eval {
PRAGMA journal_mode = wal2;
SELECT * FROM sqlite_master;
}
} {wal2 table tx tx 2 {CREATE TABLE tx(x)}}
do_test 6.4.1 {
db2 close
foreach f [glob -nocomplain test.db2*] { forcedelete $f }
forcecopy test.db-wal2 test.db2-wal2
forcecopy test.db-wal test.db2-wal
sqlite3 db2 test.db2
db2 eval { SELECT * FROM sqlite_master }
} {}
do_test 6.4.2 {
db2 eval {
PRAGMA journal_mode = wal2;
SELECT * FROM sqlite_master;
}
} {wal2}
db2 close
#-------------------------------------------------------------------------
reset_db
sqlite3 db2 test.db
do_execsql_test 7.0 {
PRAGMA journal_size_limit = 10000;
PRAGMA journal_mode = wal2;
PRAGMA wal_autocheckpoint = 0;
BEGIN;
CREATE TABLE t1(a);
INSERT INTO t1 VALUES( randomblob(8000) );
COMMIT;
} {10000 wal2 0}
do_test 7.1 {
list [file size test.db-wal] [file size test.db-wal2]
} {9464 0}
# Connection db2 is holding a PART1 lock.
#
# 7.2.2: Test that the PART1 does not prevent db from switching to the
# other wal file.
#
# 7.2.3: Test that the PART1 does prevent a checkpoint of test.db-wal.
#
# 7.2.4: Test that after the PART1 is released the checkpoint is possible.
#
do_test 7.2.1 {
execsql {
BEGIN;
SELECT count(*) FROM t1;
} db2
} {1}
do_test 7.2.2 {
execsql {
INSERT INTO t1 VALUES( randomblob(800) );
INSERT INTO t1 VALUES( randomblob(800) );
}
list [file size test.db-wal] [file size test.db-wal2] [file size test.db]
} {13656 3176 1024}
do_test 7.2.3 {
execsql { PRAGMA wal_checkpoint }
list [file size test.db-wal] [file size test.db-wal2] [file size test.db]
} {13656 3176 1024}
do_test 7.2.4 {
execsql { END } db2
execsql { PRAGMA wal_checkpoint }
list [file size test.db-wal] [file size test.db-wal2] [file size test.db]
} {13656 3176 11264}
# Connection db2 is holding a PART2_FULL1 lock.
#
# 7.3.2: Test that the lock does not prevent checkpointing.
#
# 7.3.3: Test that the lock does prevent the writer from overwriting
# test.db-wal.
#
# 7.3.4: Test that after the PART2_FULL1 is released the writer can
# switch wal files and overwrite test.db-wal
#
db close
db2 close
sqlite3 db test.db
sqlite3 db2 test.db
do_test 7.3.1 {
execsql {
PRAGMA wal_autocheckpoint = 0;
PRAGMA journal_size_limit = 10000;
INSERT INTO t1 VALUES(randomblob(10000));
INSERT INTO t1 VALUES(randomblob(500));
}
execsql {
BEGIN;
SELECT count(*) FROM t1;
} db2
list [file size test.db-wal] [file size test.db-wal2] [file size test.db]
} {12608 3176 12288}
do_test 7.3.2 {
execsql { PRAGMA wal_checkpoint }
list [file size test.db-wal] [file size test.db-wal2] [file size test.db]
} {12608 3176 22528}
do_test 7.3.3 {
execsql {
INSERT INTO t1 VALUES(randomblob(10000));
INSERT INTO t1 VALUES(randomblob(500));
}
list [file size test.db-wal] [file size test.db-wal2] [file size test.db]
} {12608 18896 22528}
do_test 7.3.4 {
execsql END db2
execsql { INSERT INTO t1 VALUES(randomblob(5000)); }
list [file size test.db-wal] [file size test.db-wal2] [file size test.db]
} {12608 18896 22528}
# Connection db2 is holding a PART2 lock.
#
# 7.4.2: Test that the lock does not prevent writer switching to test.db-wal.
#
# 7.3.3: Test that the lock does prevent checkpointing of test.db-wal2.
#
# 7.3.4: Test that after the PART2 is released test.db-wal2 can be
# checkpointed.
#
db close
db2 close
breakpoint
sqlite3 db test.db
sqlite3 db2 test.db
do_test 7.4.1 {
execsql {
PRAGMA wal_autocheckpoint = 0;
PRAGMA journal_size_limit = 10000;
INSERT INTO t1 VALUES(randomblob(10000));
INSERT INTO t1 VALUES(randomblob(10000));
PRAGMA wal_checkpoint;
}
execsql {
BEGIN;
SELECT count(*) FROM t1;
} db2
list [file size test.db-wal] [file size test.db-wal2] [file size test.db]
} {12608 12608 50176}
do_test 7.4.2 {
execsql {
INSERT INTO t1 VALUES(randomblob(5000));
}
list [file size test.db-wal] [file size test.db-wal2] [file size test.db]
} {12608 12608 50176}
do_test 7.4.3 {
execsql { PRAGMA wal_checkpoint }
list [file size test.db-wal] [file size test.db-wal2] [file size test.db]
} {12608 12608 50176}
do_test 7.4.4 {
execsql END db2
execsql { PRAGMA wal_checkpoint }
list [file size test.db-wal] [file size test.db-wal2] [file size test.db]
} {12608 12608 60416}
# Connection db2 is holding a PART1_FULL2 lock.
#
# 7.5.2: Test that the lock does not prevent a checkpoint of test.db-wal2.
#
# 7.5.3: Test that the lock does prevent the writer from overwriting
# test.db-wal2.
#
# 7.5.4: Test that after the PART1_FULL2 lock is released, the writer
# can switch to test.db-wal2.
#
db close
db2 close
sqlite3 db test.db
sqlite3 db2 test.db
do_test 7.5.1 {
execsql {
PRAGMA wal_autocheckpoint = 0;
PRAGMA journal_size_limit = 10000;
INSERT INTO t1 VALUES(randomblob(10000));
INSERT INTO t1 VALUES(randomblob(10000));
PRAGMA wal_checkpoint;
INSERT INTO t1 VALUES(randomblob(5000));
}
execsql {
BEGIN;
SELECT count(*) FROM t1;
} db2
list [file size test.db-wal] [file size test.db-wal2] [file size test.db]
} {12608 12608 76800}
do_test 7.5.2 {
execsql { PRAGMA wal_checkpoint }
list [file size test.db-wal] [file size test.db-wal2] [file size test.db]
} {12608 12608 87040}
do_test 7.5.3.1 {
execsql { INSERT INTO t1 VALUES(randomblob(5000)) }
list [file size test.db-wal] [file size test.db-wal2] [file size test.db]
} {14704 12608 87040}
do_test 7.5.3.2 {
execsql { INSERT INTO t1 VALUES(randomblob(5000)) }
list [file size test.db-wal] [file size test.db-wal2] [file size test.db]
} {22040 12608 87040}
do_test 7.5.4 {
execsql END db2
execsql { INSERT INTO t1 VALUES(randomblob(5000)) }
list [file size test.db-wal] [file size test.db-wal2] [file size test.db]
} {22040 12608 87040}
finish_test

94
test/wal2snapshot.test Normal file
View File

@ -0,0 +1,94 @@
# 2018 December 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.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL2" mode.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix wal2snapshot
ifcapable !wal {finish_test ; return }
ifcapable !snapshot {finish_test; return}
foreach {tn mode} {1 wal 2 wal2} {
reset_db
do_execsql_test $tn.1 "PRAGMA journal_mode = $mode" $mode
do_execsql_test $tn.2 {
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 2);
INSERT INTO t1 VALUES(3, 4);
BEGIN;
}
# Check that sqlite3_snapshot_get() is an error for a wal2 db.
#
if {$tn==1} {
do_test 1.3 {
set S [sqlite3_snapshot_get db main]
sqlite3_snapshot_free $S
} {}
} else {
do_test 2.3 {
list [catch { sqlite3_snapshot_get db main } msg] $msg
} {1 SQLITE_ERROR}
}
# Check that sqlite3_snapshot_recover() is an error for a wal2 db.
#
do_execsql_test $tn.4 COMMIT
if {$tn==1} {
do_test 1.5 {
sqlite3_snapshot_recover db main
} {}
} else {
do_test 2.5 {
list [catch { sqlite3_snapshot_recover db main } msg] $msg
} {1 SQLITE_ERROR}
}
# Check that sqlite3_snapshot_open() is an error for a wal2 db.
#
if {$tn==1} {
do_test 1.6 {
execsql BEGIN
set SNAPSHOT [sqlite3_snapshot_get_blob db main]
sqlite3_snapshot_open_blob db main $SNAPSHOT
execsql COMMIT
} {}
} else {
do_test 2.6.1 {
execsql BEGIN
set res [
list [catch { sqlite3_snapshot_open_blob db main $SNAPSHOT } msg] $msg
]
execsql COMMIT
set res
} {1 SQLITE_ERROR}
do_test 2.6.2 {
execsql BEGIN
execsql {SELECT * FROM sqlite_master}
set res [
list [catch { sqlite3_snapshot_open_blob db main $SNAPSHOT } msg] $msg
]
execsql COMMIT
set res
} {1 SQLITE_ERROR}
}
}
finish_test

Some files were not shown because too many files have changed in this diff Show More