Compare commits

..

198 Commits

Author SHA1 Message Date
drh 87ed2e565e Merge the latest trunk enhancements and fixes into the wal2 branch.
FossilOrigin-Name: d8cf1a62aff38de008f9b826c4846b15e37c89dc0f8bb412b79349da4d1580b0
2026-05-29 18:08:48 +00:00
drh 3d006d0d00 Rename the ext/misc/analyze.c extension to ext/misc/diskused.c, to avoid
confusion with the src/analyze.c file.  The function is now called
"diskused(X)" instead of "analyze(X)".  The CLI command is renamed
from ".dbstat" to ".diskused".

FossilOrigin-Name: c7839f7a1749b38f8a36c06d93a7b59095e91ce753e8dd020de273ca8f73a239
2026-05-29 14:57:38 +00:00
drh 991cbd78b3 The -csv option to the CLI also sets "-limits off", for legacy
compatibility, and because that seems to make sense.
[forum:/info/2026-05-28T16:23:36Z|Forum thread 2026-05-28T16:23:36Z].

FossilOrigin-Name: d15cd64160192119a5f12afec14dbfbb30fb38a8557cb9dc559b3f061c15687a
2026-05-29 12:23:38 +00:00
drh b23a628422 Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 28d1d29ad465b150ea705af8352c0e6599b3f42c15474b2ae89e8a2c2b9b097c
2026-04-22 19:47:59 +00:00
drh 852e308f34 Merge the latest trunk into the wal2 branch.
FossilOrigin-Name: 67a72256f8d23f86d130242b4b69553232fe5ea38166e87793b349f464076f3d
2026-04-08 19:28:10 +00:00
drh b21b427e5f Merge trunk enhancements into the wal2 branch.
FossilOrigin-Name: 5d7551a30b8547c17cfff901709e27835c2ebaa1dbbd4df4d0b252f216f2f8f3
2026-04-02 11:52:54 +00:00
drh bc534cef73 Merge the latest trunk enhancements into the wal2 branch
FossilOrigin-Name: 4c313e726121ecd2c191aca41ef5e9b1f4c6fbea7b7963cc5af601b1287864a6
2026-03-27 12:38:30 +00:00
drh 38b95d70b9 Merge the latest trunk enhancements into the wal2 branch
FossilOrigin-Name: 1fde9e59fd43b353588e25e4ddddade0dbf92f91fb5beb11c4f38a1d74f89ad9
2026-03-21 17:56:26 +00:00
drh 4b3af91fbd Merge the sort-and-merge optimization bug fix into the wal2 branch.
FossilOrigin-Name: ec0fb37faa6555d3167cf7793ba7bc2d8ba30726143428b8eafee3b0a6ad83f4
2026-03-04 13:24:55 +00:00
dan 92b9d4f870 Merge latest trunk changes into this branch.
FossilOrigin-Name: 947411e56c009b8212c406eff187ddf1cace7869ebcb7d12192bdb38f2d6f6c9
2026-03-04 11:08:10 +00:00
drh e8c2de0e0f Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 4ab288bd4a0b83e988121c6f46ecea0bd19244b62b2c3010ce3f4bf449de6136
2026-02-17 16:18:59 +00:00
drh a87cf441bb Merge the latest trunk enhancements into the wal2 branch
FossilOrigin-Name: f65432340ff5845753ceac9045807fee429de92f31084428fdddac2f3fdd0048
2026-01-12 17:05:15 +00:00
drh 1039aa3bf5 Merge the latest trunk enhancements into the wal2 branch
FossilOrigin-Name: 18d8e307df9d6f9eca4f9a9c984542872f28a092810ceb50a32952fcbde448e6
2025-11-28 20:48:24 +00:00
drh 82d685dae8 Merge all the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: eae8ef1bbb149b7da25da1b4806236afdef242af154fb118e7b438b199510af3
2025-11-18 18:53:20 +00:00
drh 78d1a8447c Merge the version 3.51.0 enhancements into the wal2 branch
FossilOrigin-Name: 7ec278fc987fc56ebb631b6aa3b0d45977712d91d09947c9a2a6a5d6e4b4f957
2025-11-04 20:18:40 +00:00
stephan e1b49b635a Merge trunk into the wal2 branch. devtest passes.
FossilOrigin-Name: 0ade80f307f61f544a197ead25b6ed061706f56bd07bc49dfc63c91fb340bb5b
2025-11-01 20:47:32 +00:00
drh bc410e8769 Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: c510597295f20d343845615c4a5f8d37a4c0f508b9d8a6b6f9c7dc3a7f5980ea
2025-10-28 14:58:14 +00:00
drh 3c0047482d Merge all the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 2ae29242aa04e25f5bd47517c68d1f70c2b2173682730055f8ca8d9581c9bc23
2025-10-27 11:49:07 +00:00
drh e7b5d785a5 Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 83fa6234091abc001136b1ce168ece0da086e440ab32db9a51887ec0229e761c
2025-09-30 19:35:45 +00:00
dan 79640bb5dc Merge latest trunk changes into this branch.
FossilOrigin-Name: df9475756b3565313cdc68bdfa58c54f0a1cf3eeefb4f4fa7bb88588980dda2c
2025-09-19 17:53:53 +00:00
stephan 291e2668b5 Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 8b881fd828d6d1cc4d42064e4c2b5fa47de473de196a830c35b18d2becfba61b
2025-09-04 16:02:24 +00:00
drh 825751eb69 Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 69794955d8c7bec008ad07217e37d04f024e0ee52a3828e76a705c35aeaa7052
2025-07-17 15:24:19 +00:00
drh 67b28465fc Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: e7867c3992ea58a510aae0f34044dc0c83dd82a36fb8d037583af060b7443f9a
2025-06-28 14:24:43 +00:00
dan efa511f1a8 Merge latest trunk changes into this branch.
FossilOrigin-Name: d685bc8ce32741e549d5636c495a984bb07a404bbd09a8781ce47f6b9664f86a
2025-06-03 14:15:20 +00:00
drh 3419721072 Bring the wal2 branch to to version 3.50.0.
FossilOrigin-Name: 9f9f81ca9cbd24ac48cda7d5bd6ecea1e5c55aa40611b926715d1c79d80197e4
2025-05-29 14:47:35 +00:00
drh 497d44ec5e Merge recent trunk changes into the wal2 branch.
FossilOrigin-Name: 720d175478e05921d97c561ca6883d0531188804009f388954fdb54642dec1e5
2025-05-19 16:52:04 +00:00
drh bd18310b5f Merge the latest trunk changes into the wal2 branch.
FossilOrigin-Name: b17f5beab892028fa3e246858f7b34e9104ffb4edd970804e31e5c71bcd5bbc5
2025-05-06 21:38:45 +00:00
drh 3c430b72ca Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: c68d0d353082a5810a48f01637d642bcdfd9f03dd244e0618fc0d7cf5f7b9b12
2025-04-21 12:56:24 +00:00
drh 9f455fe428 Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 80e6ddd560b3041fe9164b940d684eeb6f28560a6c48b23ff49095da52e85df8
2025-03-22 14:19:11 +00:00
drh 55c98cfe95 Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: c8d8f613ed0d7639a8b859e8c7fa7da21bd1221a605c5da6a8fe041f7c9a3f5e
2025-03-15 20:28:42 +00:00
drh 699a832669 Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 6baea938f0230f8b81b33373f948857fc44c2b3788bf8d4ee6e863b045632b82
2025-03-03 15:57:12 +00:00
drh 6f918c6b7c Merge all the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: bd4651659548bfa523dd99f0c771b7d3c9f5dc41d0412f4f939710c4f4c6e17d
2025-02-18 14:38:00 +00:00
drh ff86bd6d25 Merge version 3.49.0 into the wal2 branch.
FossilOrigin-Name: e194c8e8c75fcfda263fbf3f51e5236e66221bd245805af95afbf6a60506d247
2025-02-06 12:43:21 +00:00
drh 9e8706d02f Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: e2d4c1890acfb97b6deda316d5308cea90d39f668319c3b42199b982d746e05c
2025-02-03 15:07:57 +00:00
drh 153f4dce38 Version 3.48.0 for the wal2 branch.
FossilOrigin-Name: ef970ef037ed2271324593a46342547979ce31bcfbc8ca65930f0e51083a9e6a
2025-01-14 12:21:30 +00:00
drh ccb5519a54 Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 0992237cd432a9339d1c1a0562ab8007458c15d94b347f4092d1f62f78ae7aed
2025-01-03 15:12:00 +00:00
drh effc0d41f4 Sync wal2 with trunk.
FossilOrigin-Name: caadbe0c0c3dee411140df7d13f6e8275f9c13562bb384be38520ee2305c32bd
2024-12-16 18:16:09 +00:00
drh 5cf971bdc7 Further reduction in divergence from trunk.
FossilOrigin-Name: e4406a6e2660f1f42614d0034d024cc206109ac71e9f8b289aeed2eba20be8e9
2024-12-16 18:13:47 +00:00
drh 6b716403f9 Reduce divergence of wal2 from trunk.
FossilOrigin-Name: 90d744d67446a270f7547910531e76593dee82ce537e75c11af0d94eca8404cf
2024-12-16 17:46:21 +00:00
drh 712ad6bf38 Sync with trunk.
FossilOrigin-Name: eab619453abf6979962a68d298c44553ef0d9eb18112e61420d4de8a9c16f620
2024-12-16 13:32:27 +00:00
drh 0524562918 Synchronize the WASM code with trunk.
FossilOrigin-Name: 3619baa19e8e7617a7c5918073a826af1cc4d5dc9e9f27b4e4187e988b534d7f
2024-12-16 13:05:47 +00:00
drh b959075416 Merge all the latest trunk enhancements and fixes into the wal2 branch.
FossilOrigin-Name: 011ce4ae693fb91340c014b28b0197b9f26e3a5614e301d8c6c4e1967c943441
2024-12-16 12:41:33 +00:00
dan 686166eb32 Update to walRestartLog() function to make it shorter easier to merge with branch "bedrock".
FossilOrigin-Name: 88d7fb2aef882fc41a4f9c1bdd2f0162289e90d62df50af1b0eb6994c5c94c28
2024-12-14 17:19:06 +00:00
dan 3d6fbe469b Fix a problem with non-PASSIVE wal2 checkpoints sometimes taking the WRITER lock.
FossilOrigin-Name: 11d7865a56c1fbb978e3ba70ddc03c2a16080e2992c580e00e4c6c1fa47c043c
2024-12-14 16:50:29 +00:00
dan 52660d4d37 Do not attempt to switch wal files when writing if the current transaction read from both wal files. This fix is already on branch 'bedrock'.
FossilOrigin-Name: 693cc8e8db6c00249211bdfc21be2bff169ca6b0c4560522dd404615e8ba3b76
2024-12-14 16:41:39 +00:00
dan 7204db1f61 Fix a problem with the output of "PRAGMA wal_checkpoint" introduced long ago by [63483e22c775183e].
FossilOrigin-Name: c5f7bba8683a9aa503750abb7fd937566983dc1ca08361eca8b9b185294b909a
2024-12-13 20:18:29 +00:00
drh 5e5362267b Update the wal2 branch with the latest trunk enhancements and fixes.
FossilOrigin-Name: d3ce95f7e0335b8522a1805ab602b64856ced3007c507ba42039c6183ef9ac3d
2024-12-07 23:24:17 +00:00
drh 6063442979 Continue to reduce divergence between wal2 and trunk.
FossilOrigin-Name: 9591ab7c6b9bf70d33607c3dc8e319906d537c13c8e6b853b294e941581bcfe1
2024-11-18 20:30:35 +00:00
drh ad77991569 Realign trunk and wal2.
FossilOrigin-Name: dcf27687209670a5a1e1924f1b333fded4d71ffd4dd641fb2b7e4faafe9455eb
2024-11-18 18:27:35 +00:00
drh ea98a50afe Attempt to reduce divergence from trunk.
FossilOrigin-Name: ffe9f9ccd72f022a8388bd6426abf4b9f00d52a18206d8c3345d9dd47d4ddd71
2024-11-18 18:03:27 +00:00
drh 37468f242b Merge the latest trunk changes into the wal2 branch.
FossilOrigin-Name: d19aeff609ae7a19e90cef1181706cee81d9e5ba8475f0f03ddb1ee600b90415
2024-11-18 15:30:58 +00:00
drh 9af5d394e3 Merge the latest trunk enhancements and especially the autosetup changes
into the wal2 branch.

FossilOrigin-Name: 4ddea432bb19abf33a8a202a1a6d512d008e810e98a5ff19e2d699ce40da2e9a
2024-10-25 19:40:29 +00:00
drh 19e1883874 Update the wal2 branch to version 3.47.0
FossilOrigin-Name: d34b6a91389ff82fba4086d142c560e9daff534076df99a4ed0cdbffa9fce85f
2024-10-21 17:05:17 +00:00
drh da6f00cc63 Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 2e96c8bb7aab48f373dae9edabfdc070c9d727e192ced51ab1ce633f3f9a7638
2024-10-16 15:03:12 +00:00
dan aa45304859 Fix a usan warning in test_hexio.c on this branch.
FossilOrigin-Name: 59fba0288d8fb68f9b6e4936c09ae9169bc002bfe6548354504563a674000c20
2024-09-30 17:53:47 +00:00
dan e9ef82f592 Merge the latest trunk enhancements, including fix for assert() failure in fts5, into the wal2 branch.
FossilOrigin-Name: ab6356181274016571124989bc10e16256666567bb284612a0a689cf18a50adb
2024-09-30 17:32:08 +00:00
drh 5bd2d02612 Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 0f9e2fceecdb7fad0a2528a4244a786d17274d11b83f336d6012420675a2e4cd
2024-09-30 14:41:43 +00:00
drh 7d322c14cf Merge the latest trunk enhancement into the wal2 branch.
FossilOrigin-Name: f3e3ad6945647c88f41748b5e8ce71c06d6e77ac67c607898af63ea14a91910b
2024-09-06 15:42:31 +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 cb4a48e249 Merge all the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: dfecc79c8fa4b39e5211f1a7051783c750bb7e1132f3e3a60f8ae0837dbbe486
2024-08-29 23:33:17 +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
drh 76e67700e0 Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: eac582056fcf6210bf90576cce30c087503b864dc8d558f2998f04b3d1bfb569
2024-08-01 00:38:59 +00:00
drh 0d8cea5109 Version 3.46.0 for the wal2 branch.
FossilOrigin-Name: fdc0e1480a941d494e556331544cb40b2a997e8709e2c7fab1bed572007eb88d
2024-05-23 14:05:34 +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 ba9c7c10cc Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 4a72acbc4b87441dd0cacdcfdeac7cce7b1303d00ab494249d98f4518ae3eacf
2024-04-15 14:39:19 +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 b7e87a53c7 Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: a8a8a2db9b879c543fb3fd25cf66717a3dfdf9857dcd1ab9a98b31fbc85dbfc9
2024-04-05 14:50:12 +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 4891bf00db Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: c0af1242d86a85cf32d9fbaa614a097e77142e7ba89c3e3c1be74c0e9768126b
2024-03-18 18:51:49 +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 ff46ff5c73 Merge latest trunk changes into this branch.
FossilOrigin-Name: 68c2b8d0ebd1a180c4828cb4996c6c5fd67386c3b6304edf587c255f8d067c24
2024-01-13 20:21:36 +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 5d30acad2f Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 1f592dd32d165456d40a90a2757225e05cdb810518beee87f0700863dc73d2d0
2023-12-28 13:44:43 +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 07a7cd3714 Version 3.44.0 for the wal2 branch
FossilOrigin-Name: 470152fd2847296e0fc325fdf098e49c888e99a2332fb45533f976ad85a67d8d
2023-11-01 12:41:25 +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 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 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
drh 0205717b93 Version 3.43.0 for the wal2 branch.
FossilOrigin-Name: b5970fc4599d4fe81312e049420724b14e3d2c1de04769844fb1ab8d953170cd
2023-08-24 13:19:33 +00:00
dan cc74eafee5 Merge latest trunk changes into this branch.
FossilOrigin-Name: 135bf72c6b5f436b11463dd40b3a73bcc610bf9c2dcbe6646dbedd0d9325f6a9
2023-08-21 18:02:57 +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
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 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 6f5c4810bc Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 6fa71e11197111ab52d1983b35190899a5881dcb9ed822488c92bc839c72cb55
2023-08-03 23:22:28 +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 ecd48447b2 Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: b2e56f62ad8f114126fda6b8c1266d194bb1356b8a302956c5e9ad90d8c1d73b
2023-07-24 13:20:39 +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 b17135d81f Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 80ac494f734d9720fbbb1111741aa99fc319c6c9a32e7f13b5962001d6ace828
2023-06-22 14:20:03 +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 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 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
drh ca773a51de Update the wal2 branch to version 3.41.0.
FossilOrigin-Name: 7bb1f6dca2d2e0c0c70f1d2d0c7130c182fc45e48138c57a3fbe92ee1f68da96
2023-02-21 21:19:36 +00:00
drh ade7cd53ab Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: a36de0c803ee170b34105fadfc1f9790cda68a921fd5034790397f9853798f43
2023-02-18 21:42:38 +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 1651f418e5 Merge trunk enhancements into this branch.
FossilOrigin-Name: c9b0180203378da26dc958c183ebb59274537e9fee3b346933b1e0844ed68a4a
2023-02-02 14:21:13 +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 ce8a1720d6 Merge the 3.40.0 release into the wal2 branch.
FossilOrigin-Name: 2df915ef04a64cc8027330013f73ac0ee1fee8cb6eb61f3a608c21ea419d5928
2022-11-16 16:00:43 +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 82ac3d8211 Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: ca63a1bee16d30677c20c7576361dfb9a359e6e1b2b2b58a574da0059d3a8822
2022-11-04 18:58:48 +00:00
drh 73b1ef33ef Merge recent trunk enhancements into the wal2 branch.
FossilOrigin-Name: c22c7c879846b1357c00741700b7f6d1111c4b94895b4e7f552c92299d35712e
2022-09-30 13:54:27 +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 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 e81feadb41 Merge version 3.39.0 into the wal2 branch.
FossilOrigin-Name: ad3a7005e704711fb9de9e96883db3aed82c4f132a66cb74daf67c2527d79f73
2022-06-25 16:53:36 +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 029eb1bf15 Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: c8ad869938b06378f49c02655c00ee4f3315e1275d15e69d4ff61d6f60230fe8
2022-06-16 13:44: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 6702d6dadc Merge recent trunk enhancements into the wal2 branch.
FossilOrigin-Name: 33d77fea4084c5aba9203dfeddb820424f102dcb8347dc59e32b922bdb241382
2022-05-10 12:39:00 +00:00
drh 6c75609f48 Merge the lates trunk enhancements into the wal2 branch.
FossilOrigin-Name: bafaefc6ab4fa59c0a3859cd16a9a29d3d157953fd9002c96e868fc7a54fa18c
2022-03-12 15:04:21 +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 3f17a238bb Merge recent trunk enhancements into the wal2 branch.
FossilOrigin-Name: 7dba818893fb898e4556bb7ed9cb0dc5667b19a3d7969c7d35c902d2bf5450b1
2022-02-15 20:37:36 +00:00
drh 5666f09523 Merge trunk enhancements into the wal2 branch.
FossilOrigin-Name: d2de02f372a07b10e21f56fc49755545e7c8c889a9527bf3818e5e65cb33bb80
2022-02-04 17:52:05 +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 aed427074b Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: 82f031b41dbf041cdbdfb2f3ccc8d118b22d9e523e54a32e882a67535e9973c5
2022-01-17 19:36:10 +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 b90edad19d Merge version 3.37.0 into the wal2 branch.
FossilOrigin-Name: 3425af625bac6c89950a50c2a22c4b7f66b0080811fe16ec8879158420641d5c
2021-11-27 15:00:54 +00:00
drh b321a9b0fa Merge trunk enhancements into the wal2 branch.
FossilOrigin-Name: 135133b7a65c652e95b0642bc2358425af72e46b1b6b64d256547b5e5e8f7e41
2021-11-22 16:47:29 +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 c77906eedd Merge recent trunk enhancements into the wal2 branch.
FossilOrigin-Name: 7e2bc836f6aedfd69588f5723f8797f11ee6437d3b63ffc43c88d40e3baadb1c
2021-10-21 14:01:05 +00:00
drh e1c2be1134 Merge recent trunk enhancements into the wal2 branch.
FossilOrigin-Name: 127173e0baba8839d5922a2537dc42e6bb64ed1499b0700bf3e95d42c2f260b3
2021-10-04 11:44:28 +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 0f1e95e616 Merge recent trunk enhancements into the wal2 branch.
FossilOrigin-Name: f615d7baa889030b230e0d4f018dc9f8a6fe95cd3a671d7e2b941ae3240fa6ca
2021-08-02 20:29:08 +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 67f562233f Update this branch with latest changes from trunk.
FossilOrigin-Name: b38c2b8bc4237ee2167cb5e3ec63d08f10a8718654b2210a4bc04cca6d2adfdb
2021-06-14 14:12:57 +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 17e08c6149 Merge the 3.35.3 enhancements into the wal2 branch.
FossilOrigin-Name: a1b8d9d2131830510df291832565311803aa3e047fe07ba979fc69bb0ff329b0
2021-03-26 13:58:26 +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 226fda81af Merge latest trunk changes into this branch.
FossilOrigin-Name: 30f912d0b39ff6fcf9a555affb5990c4bce8726fbd27062cd0c95851724f91d4
2021-03-11 19:42:05 +00:00
drh c25b39e41d Merge recent trunk enhancements into the wal2 branch.
FossilOrigin-Name: 0ae81039083dbeb67cd4da9f56cdcb31fd747ba258179a46781a12b2872e9f49
2021-03-03 19:32:30 +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
dan 174f4fa241 Merge latest trunk changes into this branch.
FossilOrigin-Name: 91262e665755a1430f3b667867b9c4fa0cc3aa0388c21e8ba436485beefa9f4a
2020-08-11 11:34:00 +00:00
dan aae84d814c Merge latest trunk changes into this branch.
FossilOrigin-Name: 0c0d0a77bc8fa68ee584ca6d7a0408dcd00734a9b6b69d127ee8477c230a749c
2020-07-31 16:01:33 +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 ca97a18493 Merge version 3.32.0 into the wal2 branch.
FossilOrigin-Name: 1cb46a7431797978a37e1c6ed77f6473eb44f13d15cd37267d4f9555ac825f53
2020-05-22 18:28:59 +00:00
dan 69105498b9 Merge latest trunk changes into this branch.
FossilOrigin-Name: 16b1eca922d6e10a0ebce1deb559c6c7dd74f8655e3456bad8384c6f4637e6d6
2020-05-18 17:18:28 +00:00
drh bd74861e91 Merge recent trunk enhancements into the wal2 branch.
FossilOrigin-Name: 6fb870625cb7007fe5055609da35f6af87f3b375b7a43fd4e842d40dfef9169f
2020-04-09 18:46:15 +00:00
dan 6d671d4c09 Merge latest trunk changes into this branch.
FossilOrigin-Name: 6ad4d6b7ff2b34385039490f1dc8b020254e06fb70b9bff69d453d0c0affc2a2
2020-02-04 20:11:22 +00:00
drh 8caab4aebe Merge version 3.31.0
FossilOrigin-Name: 3bb267deb150c09bc424320d1de4bbaf519310e0e0c4af6a8ac5b6f8d59170b2
2020-01-22 21:12:27 +00:00
drh b3168a0056 Merge recent enhancements and fixes from trunk.
FossilOrigin-Name: 35eae71a4dd4bd4e2d4a8f12c5e4a0cdb93dadee353b92e67a70a79b29587984
2020-01-15 14:11:34 +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 d59a566707 Merge 3.30.0 beta 1 changes from trunk.
FossilOrigin-Name: 918bd97d2946c0a403030fcf0eba596a742ada94b122bf0ac4b808097171056b
2019-09-30 16:44:00 +00:00
drh 254bcdd02d Merge recent trunk enhancements into the wal2 branch.
FossilOrigin-Name: da8bc974126729f1558f3c18118a32efcc5bdd9611533c34090f7e9e5176b4e6
2019-09-16 12:23:30 +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 1e469f79b1 Merge all enhancements from trunk into the wal2 branch.
FossilOrigin-Name: dcbcbc2a6905588a63b9c926473a7097ca0a03803c5bd3f416f268ae2aa3dc87
2019-07-08 21:19:34 +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 e5fd180648 Merge the latest trunk enhancements.
FossilOrigin-Name: 8950f1195a5e5a67202192857fda50c359a446d20264eab328209239ae0a2d95
2019-04-15 15:17:40 +00:00
drh b83e6e9420 Merge enhancements from trunk.
FossilOrigin-Name: a0e2e90206fe09cdacc01fce93740fd515c88ed06b03c78c48076b4cb177349c
2019-03-26 12:22:30 +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 f552a23740 Add new file doc/wal2.md to this branch.
FossilOrigin-Name: b495dce153f7f886f3dba09593f29ce2375718bf6508f2cfffd1af8071a995ae
2019-01-11 14:58:03 +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 1f3762607d Merge latest trunk changes into this branch.
FossilOrigin-Name: 87ef68f9174b5e21fa2fb4f2fa23e0ecd6a0ea854422c8aa74cd7306a1ed8427
2019-01-02 16:15:02 +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 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 c5140b39ce Add extra test case to wal2recover.test.
FossilOrigin-Name: 1a5aa73435b11fff0567672dc921d34aad57adf0381ad430e1389f39a6f025b7
2018-12-17 15:22:24 +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 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
dan c30d437376 Fix a test script problem on this branch.
FossilOrigin-Name: 285d1c5904dd607457c6b5ff7be3d3191a90ca8b1fb0837327663c9910f978ac
2018-12-05 17:31:24 +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 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
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 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
83 changed files with 4203 additions and 7585 deletions
+4 -4
View File
@@ -1568,8 +1568,7 @@ TESTSRC = \
$(TOP)\ext\fts3\fts3_term.c \
$(TOP)\ext\fts3\fts3_test.c \
$(TOP)\ext\rbu\test_rbu.c \
$(TOP)\ext\session\test_session.c \
$(TOP)\ext\session\sqlite3changebatch.c
$(TOP)\ext\session\test_session.c
# Statically linked extensions.
#
@@ -1579,6 +1578,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\cksumvfs.c \
$(TOP)\ext\misc\closure.c \
$(TOP)\ext\misc\csv.c \
@@ -1767,11 +1767,11 @@ FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\test\fuzzinvariants.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\test\vt02.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\recover\dbdata.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\recover\sqlite3recover.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\analyze.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\base64.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\base85.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\completion.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\decimal.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\diskused.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\ieee754.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\randomjson.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\regexp.c
@@ -2344,12 +2344,12 @@ SHELL_DEP = \
$(TOP)\ext\expert\sqlite3expert.h \
$(TOP)\ext\intck\sqlite3intck.c \
$(TOP)\ext\intck\sqlite3intck.h \
$(TOP)\ext\misc\analyze.c \
$(TOP)\ext\misc\appendvfs.c \
$(TOP)\ext\misc\base64.c \
$(TOP)\ext\misc\base85.c \
$(TOP)\ext\misc\completion.c \
$(TOP)\ext\misc\decimal.c \
$(TOP)\ext\misc\diskused.c \
$(TOP)\ext\misc\fileio.c \
$(TOP)\ext\misc\ieee754.c \
$(TOP)\ext\misc\memtrace.c \
-106
View File
@@ -1,106 +0,0 @@
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(<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
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.
+228
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
+147 -138
View File
@@ -10,8 +10,17 @@
**
******************************************************************************
**
** Partial reimplement of the sqlite3_analyzer utility program as
** loadable SQL function.
** This extension implements an SQL function:
**
** diskused(X)
**
** Where X is the schema name (typically 'main'). The output is text
** that describes how much filesystem space the various tables and indexes
** of the database consume.
**
** This function is a replacement for the (now deprecated)
** "sqlite3_analyzer" utility program. This function is built
** into the CLI and is used to implement the ".diskused" command there.
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
@@ -23,8 +32,8 @@ SQLITE_EXTENSION_INIT1
/*
** State information for the analysis
*/
typedef struct Analysis Analysis;
struct Analysis {
typedef struct DiskUsed DiskUsed;
struct DiskUsed {
sqlite3 *db; /* Database connection */
sqlite3_context *context; /* SQL function context */
sqlite3_str *pOut; /* Write output here */
@@ -33,14 +42,14 @@ struct Analysis {
};
/*
** Free all resources that the Analysis object references and
** reset the Analysis object.
** Free all resources that the DiskUsed object references and
** reset the DiskUsed object.
**
** Call this routine multiple times on the same Analysis object
** Call this routine multiple times on the same DiskUsed object
** is a harmless no-op, as long as the memory for the object itself
** has not been freed.
*/
static void analysisReset(Analysis *p){
static void diskusedReset(DiskUsed *p){
if( p->zSU ){
char *zSql = sqlite3_mprintf("DROP TABLE temp.%s;", p->zSU);
if( zSql ){
@@ -57,7 +66,7 @@ static void analysisReset(Analysis *p){
** Report an error using formatted text. If zFormat==NULL then report
** an OOM error.
*/
static void analysisError(Analysis *p, const char *zFormat, ...){
static void diskusedError(DiskUsed *p, const char *zFormat, ...){
char *zErr;
if( zFormat ){
va_list ap;
@@ -73,34 +82,34 @@ static void analysisError(Analysis *p, const char *zFormat, ...){
sqlite3_result_error(p->context, zErr, -1);
sqlite3_free(zErr);
}
analysisReset(p);
diskusedReset(p);
}
/*
** Prepare and return an SQL statement.
*/
static sqlite3_stmt *analysisVPrep(Analysis *p, const char *zFmt, va_list ap){
static sqlite3_stmt *diskusedVPrep(DiskUsed *p, const char *zFmt, va_list ap){
char *zSql;
int rc;
sqlite3_stmt *pStmt = 0;
zSql = sqlite3_vmprintf(zFmt, ap);
if( zSql==0 ){ analysisError(p,0); return 0; }
if( zSql==0 ){ diskusedError(p,0); return 0; }
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
if( rc ){
analysisError(p, "SQL parse error: %s\nOriginal SQL: %s",
diskusedError(p, "SQL parse error: %s\nOriginal SQL: %s",
sqlite3_errmsg(p->db), zSql);
sqlite3_finalize(pStmt);
analysisReset(p);
diskusedReset(p);
pStmt = 0;
}
sqlite3_free(zSql);
return pStmt;
}
static sqlite3_stmt *analysisPrepare(Analysis *p, const char *zFormat, ...){
static sqlite3_stmt *diskusedPrepare(DiskUsed *p, const char *zFormat, ...){
va_list ap;
sqlite3_stmt *pStmt = 0;
va_start(ap, zFormat);
pStmt = analysisVPrep(p,zFormat,ap);
pStmt = diskusedVPrep(p,zFormat,ap);
va_end(ap);
return pStmt;
}
@@ -113,14 +122,14 @@ static sqlite3_stmt *analysisPrepare(Analysis *p, const char *zFormat, ...){
**
** The prepared statement is closed in either case.
*/
static int analysisStmtFinish(Analysis *p, int rc, sqlite3_stmt *pStmt){
static int diskusedStmtFinish(DiskUsed *p, int rc, sqlite3_stmt *pStmt){
if( rc==SQLITE_DONE ){
rc = SQLITE_OK;
}
if( rc!=SQLITE_OK || (rc = sqlite3_reset(pStmt))!=SQLITE_OK ){
analysisError(p, "SQL run-time error: %s\nOriginal SQL: %s",
diskusedError(p, "SQL run-time error: %s\nOriginal SQL: %s",
sqlite3_errmsg(p->db), sqlite3_sql(pStmt));
analysisReset(p);
diskusedReset(p);
}
sqlite3_finalize(pStmt);
return rc;
@@ -129,21 +138,21 @@ static int analysisStmtFinish(Analysis *p, int rc, sqlite3_stmt *pStmt){
/*
** Run SQL. Return the number of errors.
*/
static int analysisSql(Analysis *p, const char *zFormat, ...){
static int diskusedSql(DiskUsed *p, const char *zFormat, ...){
va_list ap;
int rc;
sqlite3_stmt *pStmt = 0;
va_start(ap, zFormat);
pStmt = analysisVPrep(p,zFormat,ap);
pStmt = diskusedVPrep(p,zFormat,ap);
va_end(ap);
if( pStmt==0 ) return 1;
while( (rc = sqlite3_step(pStmt))==SQLITE_ROW ){}
if( rc==SQLITE_DONE ){
rc = SQLITE_OK;
}else{
analysisError(p, "SQL run-time error: %s\nOriginal SQL: %s",
diskusedError(p, "SQL run-time error: %s\nOriginal SQL: %s",
sqlite3_errmsg(p->db), sqlite3_sql(pStmt));
analysisReset(p);
diskusedReset(p);
}
sqlite3_finalize(pStmt);
return rc;
@@ -153,8 +162,8 @@ static int analysisSql(Analysis *p, const char *zFormat, ...){
** Run an SQL query that returns an integer. Write that integer
** into *piRes. Return the number of errors.
*/
static int analysisSqlInt(
Analysis *p,
static int diskusedSqlInt(
DiskUsed *p,
sqlite3_int64 *piRes,
const char *zFormat, ...
){
@@ -162,7 +171,7 @@ static int analysisSqlInt(
int rc;
sqlite3_stmt *pStmt = 0;
va_start(ap, zFormat);
pStmt = analysisVPrep(p,zFormat,ap);
pStmt = diskusedVPrep(p,zFormat,ap);
va_end(ap);
if( pStmt==0 ) return 1;
rc = sqlite3_step(pStmt);
@@ -174,10 +183,10 @@ static int analysisSqlInt(
}else{
if( p->db ){
/* p->db is NULL if there was some prior error */
analysisError(p, "SQL run-time error: %s\nOriginal SQL: %s",
diskusedError(p, "SQL run-time error: %s\nOriginal SQL: %s",
sqlite3_errmsg(p->db), sqlite3_sql(pStmt));
}
analysisReset(p);
diskusedReset(p);
}
sqlite3_finalize(pStmt);
return rc;
@@ -190,7 +199,7 @@ static int analysisSqlInt(
** comment. Otherwise begin with a new-line. Always finish with a
** newline.
*/
static void analysisTitle(Analysis *p, const char *zFormat, ...){
static void diskusedTitle(DiskUsed *p, const char *zFormat, ...){
char *zFirst;
char *zTitle;
size_t nTitle;
@@ -199,7 +208,7 @@ static void analysisTitle(Analysis *p, const char *zFormat, ...){
zTitle = sqlite3_vmprintf(zFormat, ap);
va_end(ap);
if( zTitle==0 ){
analysisError(p, 0);
diskusedError(p, 0);
return;
}
zFirst = sqlite3_str_length(p->pOut)==0 ? "/" : "\n*";
@@ -218,8 +227,8 @@ static void analysisTitle(Analysis *p, const char *zFormat, ...){
** 50 columns with "." characters, and followed by whatever text is
** described by zFormat.
*/
static void analysisLine(
Analysis *p, /* Analysis context */
static void diskusedLine(
DiskUsed *p, /* DiskUsed context */
const char *zDesc, /* Description */
const char *zFormat, /* Argument to the description */
...
@@ -231,7 +240,7 @@ static void analysisLine(
zTxt = sqlite3_vmprintf(zFormat, ap);
va_end(ap);
if( zTxt==0 ){
analysisError(p, 0);
diskusedError(p, 0);
return;
}
nDesc = strlen(zDesc);
@@ -248,7 +257,7 @@ static void analysisLine(
** two or three significant digits, with the decimal point being the fourth
** character.
*/
static void analysisPercent(Analysis *p, double r){
static void diskusedPercent(DiskUsed *p, double r){
char zNum[100];
char *zDP;
int nLeadingDigit;
@@ -277,8 +286,8 @@ static void analysisPercent(Analysis *p, double r){
** a boolean expression that can go in the WHERE clause to select
** the relevant rows of the s.zSU table.
*/
static int analysisSubreport(
Analysis *p, /* Analysis context */
static int diskusedSubreport(
DiskUsed *p, /* DiskUsed context */
char *zTitle, /* Title for this subreport */
char *zWhere, /* WHERE clause for this subreport */
sqlite3_int64 pgsz, /* Database page size */
@@ -306,10 +315,10 @@ static int analysisSubreport(
int rc;
if( zTitle==0 || zWhere==0 ){
analysisError(p, 0);
diskusedError(p, 0);
return SQLITE_NOMEM;
}
pStmt = analysisPrepare(p,
pStmt = diskusedPrepare(p,
"SELECT\n"
" sum(if(is_without_rowid OR is_index,nentry,leaf_entries)),\n" /* 0 */
" sum(payload),\n" /* 1 */
@@ -330,7 +339,7 @@ static int analysisSubreport(
if( pStmt==0 ) return 1;
rc = sqlite3_step(pStmt);
if( rc==SQLITE_ROW ){
analysisTitle(p, "%s", zTitle);
diskusedTitle(p, "%s", zTitle);
nentry = sqlite3_column_int64(pStmt, 0);
payload = sqlite3_column_int64(pStmt, 1);
@@ -349,69 +358,69 @@ static int analysisSubreport(
rc = SQLITE_DONE;
total_pages = leaf_pages + int_pages + ovfl_pages;
analysisLine(p, "Percentage of total database", "%.3g%%\n",
diskusedLine(p, "Percentage of total database", "%.3g%%\n",
(total_pages*100.0)/(double)nPage);
analysisLine(p, "Number of entries", "%lld\n", nentry);
diskusedLine(p, "Number of entries", "%lld\n", nentry);
storage = total_pages*pgsz;
analysisLine(p, "Bytes of storage consumed", "%lld\n", storage);
analysisLine(p, "Bytes of payload", "%-11lld ", payload);
analysisPercent(p, payload*100.0/(double)storage);
diskusedLine(p, "Bytes of storage consumed", "%lld\n", storage);
diskusedLine(p, "Bytes of payload", "%-11lld ", payload);
diskusedPercent(p, payload*100.0/(double)storage);
if( ovfl_cnt>0 ){
analysisLine(p, "Bytes of payload in overflow","%-11lld ",ovfl_payload);
analysisPercent(p, ovfl_payload*100.0/(double)payload);
diskusedLine(p, "Bytes of payload in overflow","%-11lld ",ovfl_payload);
diskusedPercent(p, ovfl_payload*100.0/(double)payload);
}
total_unused = leaf_unused + int_unused + ovfl_unused;
total_meta = storage - payload - total_unused;
analysisLine(p, "Bytes of metadata","%-11lld ", total_meta);
analysisPercent(p, total_meta*100.0/(double)storage);
diskusedLine(p, "Bytes of metadata","%-11lld ", total_meta);
diskusedPercent(p, total_meta*100.0/(double)storage);
if( cnt==1 ){
analysisLine(p, "B-tree depth", "%lld\n", depth);
diskusedLine(p, "B-tree depth", "%lld\n", depth);
if( int_cell>1 ){
analysisLine(p, "Average fanout", "%.1f\n",
diskusedLine(p, "Average fanout", "%.1f\n",
(double)(int_cell+int_pages)/(double)int_pages);
}
}
if( nentry>0 ){
analysisLine(p, "Average payload per entry", "%.1f\n",
diskusedLine(p, "Average payload per entry", "%.1f\n",
(double)payload/(double)nentry);
analysisLine(p, "Average unused bytes per entry", "%.1f\n",
diskusedLine(p, "Average unused bytes per entry", "%.1f\n",
(double)total_unused/(double)nentry);
analysisLine(p, "Average metadata per entry", "%.1f\n",
diskusedLine(p, "Average metadata per entry", "%.1f\n",
(double)total_meta/(double)nentry);
}
analysisLine(p, "Maximum single-entry payload", "%lld\n", mx_payload);
diskusedLine(p, "Maximum single-entry payload", "%lld\n", mx_payload);
if( nentry>0 ){
analysisLine(p, "Entries that use overflow", "%-11lld ", ovfl_cnt);
analysisPercent(p, ovfl_cnt*100.0/(double)nentry);
diskusedLine(p, "Entries that use overflow", "%-11lld ", ovfl_cnt);
diskusedPercent(p, ovfl_cnt*100.0/(double)nentry);
}
if( int_pages>0 ){
analysisLine(p, "Index pages used", "%lld\n", int_pages);
diskusedLine(p, "Index pages used", "%lld\n", int_pages);
}
analysisLine(p, "Primary pages used", "%lld\n", leaf_pages);
diskusedLine(p, "Primary pages used", "%lld\n", leaf_pages);
if( ovfl_cnt ){
analysisLine(p, "Overflow pages used", "%lld\n", ovfl_pages);
diskusedLine(p, "Overflow pages used", "%lld\n", ovfl_pages);
}
analysisLine(p, "Total pages used", "%lld\n", total_pages);
diskusedLine(p, "Total pages used", "%lld\n", total_pages);
if( int_pages>0 ){
analysisLine(p, "Unused bytes on index pages", "%lld\n", int_unused);
diskusedLine(p, "Unused bytes on index pages", "%lld\n", int_unused);
}
analysisLine(p, "Unused bytes on primary pages", "%lld\n", leaf_unused);
diskusedLine(p, "Unused bytes on primary pages", "%lld\n", leaf_unused);
if( ovfl_cnt ){
analysisLine(p, "Unused bytes on overflow pages", "%lld\n", ovfl_unused);
diskusedLine(p, "Unused bytes on overflow pages", "%lld\n", ovfl_unused);
}
analysisLine(p, "Unused bytes on all pages", "%-11lld ", total_unused);
analysisPercent(p, total_unused*100.0/(double)storage);
diskusedLine(p, "Unused bytes on all pages", "%-11lld ", total_unused);
diskusedPercent(p, total_unused*100.0/(double)storage);
}
return analysisStmtFinish(p, rc, pStmt);
return diskusedStmtFinish(p, rc, pStmt);
}
/*
** SQL Function: analyze(SCHEMA)
** SQL Function: diskused(SCHEMA)
**
** Analyze the database schema named in the argument. Return text
** containing the analysis.
** containing the space utilization stats.
*/
static void analyzeFunc(
static void diskusedFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
@@ -426,7 +435,7 @@ static void analyzeFunc(
sqlite3_int64 nFreeList;
sqlite3_int64 nIndex;
sqlite3_int64 nWORowid;
Analysis s;
DiskUsed s;
sqlite3_uint64 r[2];
(void)argc;
@@ -435,34 +444,34 @@ static void analyzeFunc(
s.context = context;
s.pOut = sqlite3_str_new(0);
if( sqlite3_str_errcode(s.pOut) ){
analysisError(&s, 0);
diskusedError(&s, 0);
return;
}
s.zSchema = (const char*)sqlite3_value_text(argv[0]);
if( s.zSchema==0 ){
s.zSchema = "main";
}else if( sqlite3_strlike("temp",s.zSchema,0)==0 ){
analysisReset(&s);
diskusedReset(&s);
sqlite3_result_text(context, "cannot analyze \"temp\"",-1,SQLITE_STATIC);
return;
}
ii = 0;
rc = analysisSqlInt(&s,&ii,"SELECT 1 FROM pragma_database_list"
rc = diskusedSqlInt(&s,&ii,"SELECT 1 FROM pragma_database_list"
" WHERE name=%Q COLLATE nocase",s.zSchema);
if( rc || ii==0 ){
analysisReset(&s);
diskusedReset(&s);
sqlite3_result_text(context,"no such database",-1,SQLITE_STATIC);
return;
}
sqlite3_randomness(sizeof(r), &r);
s.zSU = sqlite3_mprintf("analysis%016llx%016llx", r[0], r[1]);
if( s.zSU==0 ){ analysisError(&s, 0); return; }
s.zSU = sqlite3_mprintf("diskused%016llx%016llx", r[0], r[1]);
if( s.zSU==0 ){ diskusedError(&s, 0); return; }
/* The s.zSU table contains the data used for the analysis.
** The table name contains 128-bits of randomness to avoid
** collisions with preexisting tables in temp.
*/
rc = analysisSql(&s,
rc = diskusedSql(&s,
"CREATE TABLE temp.%s(\n"
" name text, -- A table or index\n"
" tblname text, -- Table that owns name\n"
@@ -489,7 +498,7 @@ static void analyzeFunc(
/* Populate the s.zSU table
*/
rc = analysisSql(&s,
rc = diskusedSql(&s,
"WITH\n"
" allidx(idxname) AS (\n"
" SELECT name FROM \"%w\".sqlite_schema WHERE type='index'\n"
@@ -541,38 +550,38 @@ static void analyzeFunc(
if( rc ) return;
nPage = 0;
rc = analysisSqlInt(&s, &nPage, "PRAGMA \"%w\".page_count", s.zSchema);
rc = diskusedSqlInt(&s, &nPage, "PRAGMA \"%w\".page_count", s.zSchema);
if( rc ) return;
if( nPage<=0 ){
/* Very brief reply for an empty database */
analysisReset(&s);
diskusedReset(&s);
sqlite3_result_text(context, "empty database", -1, SQLITE_STATIC);
return;
}
/* Begin generating the report */
analysisTitle(&s, "Database storage utilization report");
diskusedTitle(&s, "Database storage utilization report");
pgsz = 0;
rc = analysisSqlInt(&s, &pgsz, "PRAGMA \"%w\".page_size", s.zSchema);
rc = diskusedSqlInt(&s, &pgsz, "PRAGMA \"%w\".page_size", s.zSchema);
if( rc ) return;
analysisLine(&s, "Page size in bytes","%lld\n",pgsz);
analysisLine(&s, "Pages in the database", "%lld\n", nPage);
diskusedLine(&s, "Page size in bytes","%lld\n",pgsz);
diskusedLine(&s, "Pages in the database", "%lld\n", nPage);
nPageInUse = 0;
rc = analysisSqlInt(&s, &nPageInUse,
rc = diskusedSqlInt(&s, &nPageInUse,
"SELECT sum(leaf_pages+int_pages+ovfl_pages) FROM temp.%s", s.zSU);
if( rc ) return;
analysisLine(&s, "Pages that store data", "%-11lld ", nPageInUse);
analysisPercent(&s, (nPageInUse*100.0)/(double)nPage);
diskusedLine(&s, "Pages that store data", "%-11lld ", nPageInUse);
diskusedPercent(&s, (nPageInUse*100.0)/(double)nPage);
nFreeList = 0;
rc = analysisSqlInt(&s, &nFreeList, "PRAGMA \"%w\".freelist_count",s.zSchema);
rc = diskusedSqlInt(&s, &nFreeList, "PRAGMA \"%w\".freelist_count",s.zSchema);
if( rc ) return;
analysisLine(&s, "Pages on the freelist", "%-11lld ", nFreeList);
analysisPercent(&s, (nFreeList*100.0)/(double)nPage);
diskusedLine(&s, "Pages on the freelist", "%-11lld ", nFreeList);
diskusedPercent(&s, (nFreeList*100.0)/(double)nPage);
ii = 0;
rc = analysisSqlInt(&s, &ii, "PRAGMA \"%w\".auto_vacuum", s.zSchema);
rc = diskusedSqlInt(&s, &ii, "PRAGMA \"%w\".auto_vacuum", s.zSchema);
if( rc ) return;
if( ii==0 || nPage<=1 ){
ii = 0;
@@ -581,50 +590,50 @@ static void analyzeFunc(
double rAvPage = (nPage-1.0)/(rPtrsPerPage+1.0);
ii = (sqlite3_int64)ceil(rAvPage);
}
analysisLine(&s, "Pages of auto-vacuum overhead", "%-11lld ", ii);
analysisPercent(&s, (ii*100.0)/(double)nPage);
diskusedLine(&s, "Pages of auto-vacuum overhead", "%-11lld ", ii);
diskusedPercent(&s, (ii*100.0)/(double)nPage);
ii = 0;
rc = analysisSqlInt(&s, &ii,
rc = diskusedSqlInt(&s, &ii,
"SELECT count(*)+1 FROM \"%w\".sqlite_schema WHERE type='table'",
s.zSchema);
if( rc ) return;
analysisLine(&s, "Number of tables", "%lld\n", ii);
diskusedLine(&s, "Number of tables", "%lld\n", ii);
nWORowid = 0;
rc = analysisSqlInt(&s, &nWORowid,
rc = diskusedSqlInt(&s, &nWORowid,
"SELECT count(*) FROM \"%w\".pragma_table_list WHERE wr",
s.zSchema);
if( rc ) return;
if( nWORowid>0 ){
analysisLine(&s, "Number of WITHOUT ROWID tables", "%lld\n", nWORowid);
analysisLine(&s, "Number of rowid tables", "%lld\n", ii - nWORowid);
diskusedLine(&s, "Number of WITHOUT ROWID tables", "%lld\n", nWORowid);
diskusedLine(&s, "Number of rowid tables", "%lld\n", ii - nWORowid);
}
nIndex = 0;
rc = analysisSqlInt(&s, &nIndex,
rc = diskusedSqlInt(&s, &nIndex,
"SELECT count(*) FROM \"%w\".sqlite_schema WHERE type='index'",
s.zSchema);
if( rc ) return;
analysisLine(&s, "Number of indexes", "%lld\n", nIndex);
diskusedLine(&s, "Number of indexes", "%lld\n", nIndex);
ii = 0;
rc = analysisSqlInt(&s, &ii,
rc = diskusedSqlInt(&s, &ii,
"SELECT count(*) FROM \"%w\".sqlite_schema"
" WHERE name GLOB 'sqlite_autoindex_*' AND type='index'",
s.zSchema);
if( rc ) return;
analysisLine(&s, "Number of defined indexes", "%lld\n", nIndex - ii);
analysisLine(&s, "Number of implied indexes", "%lld\n", ii);
analysisLine(&s, "Size of the database in bytes", "%lld\n", pgsz*nPage);
diskusedLine(&s, "Number of defined indexes", "%lld\n", nIndex - ii);
diskusedLine(&s, "Number of implied indexes", "%lld\n", ii);
diskusedLine(&s, "Size of the database in bytes", "%lld\n", pgsz*nPage);
ii = 0;
rc = analysisSqlInt(&s, &ii,
rc = diskusedSqlInt(&s, &ii,
"SELECT sum(payload) FROM temp.%s"
" WHERE NOT is_index AND name NOT LIKE 'sqlite_schema'",
s.zSU);
if( rc ) return;
analysisLine(&s, "Bytes of payload", "%-11lld ", ii);
analysisPercent(&s, ii*100.0/(double)(pgsz*nPage));
diskusedLine(&s, "Bytes of payload", "%-11lld ", ii);
diskusedPercent(&s, ii*100.0/(double)(pgsz*nPage));
analysisTitle(&s, "Page counts for all tables with their indexes");
pStmt = analysisPrepare(&s,
diskusedTitle(&s, "Page counts for all tables with their indexes");
pStmt = diskusedPrepare(&s,
"SELECT upper(tblname),\n"
" sum(int_pages+leaf_pages+ovfl_pages)\n"
" FROM temp.%s\n"
@@ -634,13 +643,13 @@ static void analyzeFunc(
if( pStmt==0 ) return;
while( (rc = sqlite3_step(pStmt))==SQLITE_ROW ){
sqlite3_int64 nn = sqlite3_column_int64(pStmt,1);
analysisLine(&s, (const char*)sqlite3_column_text(pStmt,0), "%-11lld ", nn);
analysisPercent(&s, (nn*100.0)/(double)nPage);
diskusedLine(&s, (const char*)sqlite3_column_text(pStmt,0), "%-11lld ", nn);
diskusedPercent(&s, (nn*100.0)/(double)nPage);
}
if( analysisStmtFinish(&s, rc, pStmt) ) return;
if( diskusedStmtFinish(&s, rc, pStmt) ) return;
analysisTitle(&s, "Page counts for all tables and indexes separately");
pStmt = analysisPrepare(&s,
diskusedTitle(&s, "Page counts for all tables and indexes separately");
pStmt = diskusedPrepare(&s,
"SELECT upper(name),\n"
" sum(int_pages+leaf_pages+ovfl_pages)\n"
" FROM temp.%s\n"
@@ -650,28 +659,28 @@ static void analyzeFunc(
if( pStmt==0 ) return;
while( (rc = sqlite3_step(pStmt))==SQLITE_ROW ){
sqlite3_int64 nn = sqlite3_column_int64(pStmt,1);
analysisLine(&s, (const char*)sqlite3_column_text(pStmt,0), "%-11lld ", nn);
analysisPercent(&s, (nn*100.0)/(double)nPage);
diskusedLine(&s, (const char*)sqlite3_column_text(pStmt,0), "%-11lld ", nn);
diskusedPercent(&s, (nn*100.0)/(double)nPage);
}
if( analysisStmtFinish(&s, rc, pStmt) ) return;
if( diskusedStmtFinish(&s, rc, pStmt) ) return;
rc = analysisSubreport(&s, "All tables and indexes", "1", pgsz, nPage);
rc = diskusedSubreport(&s, "All tables and indexes", "1", pgsz, nPage);
if( rc ) return;
rc = analysisSubreport(&s, "All tables", "NOT is_index", pgsz, nPage);
rc = diskusedSubreport(&s, "All tables", "NOT is_index", pgsz, nPage);
if( rc ) return;
if( nWORowid>0 ){
rc = analysisSubreport(&s, "All WITHOUT ROWID tables", "is_without_rowid",
rc = diskusedSubreport(&s, "All WITHOUT ROWID tables", "is_without_rowid",
pgsz, nPage);
if( rc ) return;
rc = analysisSubreport(&s, "All rowid tables",
rc = diskusedSubreport(&s, "All rowid tables",
"NOT is_without_rowid AND NOT is_index",
pgsz, nPage);
if( rc ) return;
}
rc = analysisSubreport(&s, "All indexes", "is_index", pgsz, nPage);
rc = diskusedSubreport(&s, "All indexes", "is_index", pgsz, nPage);
if( rc ) return;
pStmt = analysisPrepare(&s,
pStmt = diskusedPrepare(&s,
"SELECT upper(tblname), tblname, sum(is_index) FROM temp.%s"
" GROUP BY 1 ORDER BY 1",
s.zSU);
@@ -683,7 +692,7 @@ static void analyzeFunc(
if( nSubIndex==0 ){
char *zTitle = sqlite3_mprintf("Table %s", zUpper);
char *zWhere = sqlite3_mprintf("name=%Q", zName);
rc = analysisSubreport(&s, zTitle, zWhere, pgsz, nPage);
rc = diskusedSubreport(&s, zTitle, zWhere, pgsz, nPage);
sqlite3_free(zTitle);
sqlite3_free(zWhere);
if( rc ) break;
@@ -691,25 +700,25 @@ static void analyzeFunc(
sqlite3_stmt *pS2;
char *zTitle = sqlite3_mprintf("Table %s and all its indexes", zUpper);
char *zWhere = sqlite3_mprintf("tblname=%Q", zName);
rc = analysisSubreport(&s, zTitle, zWhere, pgsz, nPage);
rc = diskusedSubreport(&s, zTitle, zWhere, pgsz, nPage);
sqlite3_free(zTitle);
sqlite3_free(zWhere);
if( rc ) break;
zTitle = sqlite3_mprintf("Table %s w/o any indexes", zUpper);
zWhere = sqlite3_mprintf("name=%Q", zName);
rc = analysisSubreport(&s, zTitle, zWhere, pgsz, nPage);
rc = diskusedSubreport(&s, zTitle, zWhere, pgsz, nPage);
sqlite3_free(zTitle);
sqlite3_free(zWhere);
if( rc ) break;
if( nSubIndex>1 ){
zTitle = sqlite3_mprintf("All indexes of table %s", zUpper);
zWhere = sqlite3_mprintf("tblname=%Q AND is_index", zName);
rc = analysisSubreport(&s, zTitle, zWhere, pgsz, nPage);
rc = diskusedSubreport(&s, zTitle, zWhere, pgsz, nPage);
sqlite3_free(zTitle);
sqlite3_free(zWhere);
if( rc ) break;
}
pS2 = analysisPrepare(&s,
pS2 = diskusedPrepare(&s,
"SELECT name, upper(name) FROM temp.%s"
" WHERE is_index AND tblname=%Q",
s.zSU, zName);
@@ -722,21 +731,21 @@ static void analyzeFunc(
const char *zN = (const char*)sqlite3_column_text(pS2, 0);
zTitle = sqlite3_mprintf("Index %s", zU);
zWhere = sqlite3_mprintf("name=%Q", zN);
rc = analysisSubreport(&s, zTitle, zWhere, pgsz, nPage);
rc = diskusedSubreport(&s, zTitle, zWhere, pgsz, nPage);
sqlite3_free(zTitle);
sqlite3_free(zWhere);
if( rc ) break;
}
rc = analysisStmtFinish(&s, rc, pS2);
rc = diskusedStmtFinish(&s, rc, pS2);
if( rc ) break;
}
}
if( analysisStmtFinish(&s, rc, pStmt) ) return;
if( diskusedStmtFinish(&s, rc, pStmt) ) return;
/* Append SQL statements that will recreate the raw data used for
** the analysis.
*/
analysisTitle(&s, "Raw data used to generate this report");
diskusedTitle(&s, "Raw data used to generate this report");
sqlite3_str_appendf(s.pOut,
"The following SQL will create a table named \"space_used\" which\n"
"contains most of the information used to generate the report above.\n"
@@ -766,7 +775,7 @@ static void analyzeFunc(
");\n"
"INSERT INTO space_used VALUES\n"
);
pStmt = analysisPrepare(&s,
pStmt = diskusedPrepare(&s,
"SELECT quote(name), quote(tblname),\n" /* 0..1 */
" is_index, is_without_rowid, nentry, leaf_entries,\n" /* 2..5 */
" depth, payload, ovfl_payload, ovfl_cnt, mx_payload,\n" /* 6..10 */
@@ -801,7 +810,7 @@ static void analyzeFunc(
sqlite3_column_int64(pStmt, 17));
}
if( rc!=SQLITE_DONE ){
analysisError(&s, "SQL run-time error: %s\nSQL: %s",
diskusedError(&s, "SQL run-time error: %s\nSQL: %s",
sqlite3_errmsg(s.db), sqlite3_sql(pStmt));
sqlite3_finalize(pStmt);
return;
@@ -814,14 +823,14 @@ static void analyzeFunc(
sqlite3_free);
s.pOut = 0;
}
analysisReset(&s);
diskusedReset(&s);
}
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_analyze_init(
int sqlite3_diskused_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
@@ -829,8 +838,8 @@ int sqlite3_analyze_init(
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
(void)pzErrMsg; /* Unused parameter */
rc = sqlite3_create_function(db, "analyze", 1,
rc = sqlite3_create_function(db, "diskused", 1,
SQLITE_UTF8|SQLITE_INNOCUOUS,
0, analyzeFunc, 0, 0);
0, diskusedFunc, 0, 0);
return rc;
}
-222
View File
@@ -1,222 +0,0 @@
# 2016 August 23
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source $testdir/tester.tcl
ifcapable !session {finish_test; return}
set testprefix changebatch1
proc sql_to_changeset {method sql} {
sqlite3session S db main
S attach *
execsql $sql
set ret [S $method]
S delete
return $ret
}
proc do_changebatch_test {tn method args} {
set C [list]
foreach a $args {
lappend C [sql_to_changeset $method $a]
}
sqlite3changebatch cb db
set i 1
foreach ::cs [lrange $C 0 end-1] {
set rc [cb add $::cs]
if {$rc!="SQLITE_OK"} { error "expected SQLITE_OK, got $rc (i=$i)" }
incr i
}
set ::cs [lindex $C end]
do_test $tn { cb add [set ::cs] } SQLITE_CONSTRAINT
cb delete
}
proc do_changebatch_test1 {tn args} {
uplevel do_changebatch_test $tn changeset $args
}
proc do_changebatch_test2 {tn args} {
uplevel do_changebatch_test $tn fullchangeset $args
}
#-------------------------------------------------------------------------
# The body of the following loop contains tests for database schemas
# that do not feature multi-column UNIQUE constraints. In this case
# it doesn't matter if the changesets are generated using
# sqlite3session_changeset() or sqlite3session_fullchangeset().
#
foreach {tn testfunction} {
1 do_changebatch_test1
2 do_changebatch_test2
} {
reset_db
#-------------------------------------------------------------------------
#
do_execsql_test $tn.1.0 {
CREATE TABLE t1(a PRIMARY KEY, b);
}
$testfunction $tn.1.1 {
INSERT INTO t1 VALUES(1, 1);
} {
DELETE FROM t1 WHERE a=1;
}
do_execsql_test $tn.1.2.0 {
INSERT INTO t1 VALUES(1, 1);
INSERT INTO t1 VALUES(2, 2);
INSERT INTO t1 VALUES(3, 3);
}
$testfunction $tn.1.2.1 {
DELETE FROM t1 WHERE a=2;
} {
INSERT INTO t1 VALUES(2, 2);
}
#-------------------------------------------------------------------------
#
do_execsql_test $tn.2.0 {
CREATE TABLE x1(a, b PRIMARY KEY, c UNIQUE);
CREATE TABLE x2(a PRIMARY KEY, b UNIQUE, c UNIQUE);
CREATE INDEX x1a ON x1(a);
INSERT INTO x1 VALUES(1, 1, 'a');
INSERT INTO x1 VALUES(1, 2, 'b');
INSERT INTO x1 VALUES(1, 3, 'c');
}
$testfunction $tn.2.1 {
DELETE FROM x1 WHERE b=2;
} {
UPDATE x1 SET c='b' WHERE b=3;
}
$testfunction $tn.2.2 {
DELETE FROM x1 WHERE b=1;
} {
INSERT INTO x1 VALUES(1, 5, 'a');
}
set L [list]
for {set i 1000} {$i < 10000} {incr i} {
lappend L "INSERT INTO x2 VALUES($i, $i, 'x' || $i)"
}
lappend L "DELETE FROM x2 WHERE b=1005"
$testfunction $tn.2.3 {*}$L
execsql { INSERT INTO x1 VALUES('f', 'f', 'f') }
$testfunction $tn.2.4 {
INSERT INTO x2 VALUES('f', 'f', 'f');
} {
INSERT INTO x1 VALUES('g', 'g', 'g');
} {
DELETE FROM x1 WHERE b='f';
} {
INSERT INTO x2 VALUES('g', 'g', 'g');
} {
INSERT INTO x1 VALUES('f', 'f', 'f');
}
execsql {
DELETE FROM x1;
INSERT INTO x1 VALUES(1.5, 1.5, 1.5);
}
$testfunction $tn.2.5 {
DELETE FROM x1 WHERE b BETWEEN 1 AND 2;
} {
INSERT INTO x1 VALUES(2.5, 2.5, 2.5);
} {
INSERT INTO x1 VALUES(1.5, 1.5, 1.5);
}
execsql {
DELETE FROM x2;
INSERT INTO x2 VALUES(X'abcd', X'1234', X'7890');
INSERT INTO x2 VALUES(X'0000', X'0000', X'0000');
}
breakpoint
$testfunction $tn.2.6 {
UPDATE x2 SET c = X'1234' WHERE a=X'abcd';
INSERT INTO x2 VALUES(X'1234', X'abcd', X'7890');
} {
DELETE FROM x2 WHERE b=X'0000';
} {
INSERT INTO x2 VALUES(1, X'0000', NULL);
}
}
#-------------------------------------------------------------------------
# Test some multi-column UNIQUE constraints. First Using _changeset() to
# demonstrate the problem, then using _fullchangeset() to show that it has
# been fixed.
#
reset_db
do_execsql_test 3.0 {
CREATE TABLE y1(a PRIMARY KEY, b, c, UNIQUE(b, c));
INSERT INTO y1 VALUES(1, 1, 1);
INSERT INTO y1 VALUES(2, 2, 2);
INSERT INTO y1 VALUES(3, 3, 3);
INSERT INTO y1 VALUES(4, 3, 4);
BEGIN;
}
do_test 3.1.1 {
set c1 [sql_to_changeset changeset { DELETE FROM y1 WHERE a=4 }]
set c2 [sql_to_changeset changeset { UPDATE y1 SET c=4 WHERE a=3 }]
sqlite3changebatch cb db
cb add $c1
cb add $c2
} {SQLITE_OK}
do_test 3.1.2 {
cb delete
execsql ROLLBACK
} {}
do_test 3.1.1 {
set c1 [sql_to_changeset fullchangeset { DELETE FROM y1 WHERE a=4 }]
set c2 [sql_to_changeset fullchangeset { UPDATE y1 SET c=4 WHERE a=3 }]
sqlite3changebatch cb db
cb add $c1
cb add $c2
} {SQLITE_OK}
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
-42
View File
@@ -1,42 +0,0 @@
# 2011 Mar 21
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# The focus of this file is testing the session module.
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source [file join [file dirname [info script]] session_common.tcl]
source $testdir/tester.tcl
ifcapable !session {finish_test; return}
set testprefix changebatchfault
do_execsql_test 1.0 {
CREATE TABLE t1(a, b, c PRIMARY KEY, UNIQUE(a, b));
INSERT INTO t1 VALUES('a', 'a', 'a');
INSERT INTO t1 VALUES('b', 'b', 'b');
}
set ::c1 [changeset_from_sql { delete from t1 where c='a' }]
set ::c2 [changeset_from_sql { insert into t1 values('c', 'c', 'c') }]
do_faultsim_test 1 -faults oom-* -body {
sqlite3changebatch cb db
cb add $::c1
cb add $::c2
} -test {
faultsim_test_result {0 SQLITE_OK} {1 SQLITE_NOMEM}
catch { cb delete }
}
finish_test
+1 -1
View File
@@ -29,7 +29,7 @@ do_test 1.0 {
WITH s(i) AS (
VALUES(1) UNION ALL SELECT i+1 FROM s WHERe i<10000
)
INSERT INTO t1 SELECT 'abcde', randomblob(18), i FROM s;
INSERT INTO t1 SELECT 'abcde', randomblob(16), i FROM s;
}
compare_db db db2
} {}
-485
View File
@@ -1,485 +0,0 @@
#if !defined(SQLITE_TEST) || (defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK))
#include "sqlite3session.h"
#include "sqlite3changebatch.h"
#include <assert.h>
#include <string.h>
typedef struct BatchTable BatchTable;
typedef struct BatchIndex BatchIndex;
typedef struct BatchIndexEntry BatchIndexEntry;
typedef struct BatchHash BatchHash;
struct sqlite3_changebatch {
sqlite3 *db; /* Database handle used to read schema */
BatchTable *pTab; /* First in linked list of tables */
int iChangesetId; /* Current changeset id */
int iNextIdxId; /* Next available index id */
int nEntry; /* Number of entries in hash table */
int nHash; /* Number of hash buckets */
BatchIndexEntry **apHash; /* Array of hash buckets */
};
struct BatchTable {
BatchIndex *pIdx; /* First in linked list of UNIQUE indexes */
BatchTable *pNext; /* Next table */
char zTab[1]; /* Table name */
};
struct BatchIndex {
BatchIndex *pNext; /* Next index on same table */
int iId; /* Index id (assigned internally) */
int bPk; /* True for PK index */
int nCol; /* Size of aiCol[] array */
int *aiCol; /* Array of columns that make up index */
};
struct BatchIndexEntry {
BatchIndexEntry *pNext; /* Next colliding hash table entry */
int iChangesetId; /* Id of associated changeset */
int iIdxId; /* Id of index this key is from */
int szRecord;
char aRecord[1];
};
/*
** Allocate and zero a block of nByte bytes. Must be freed using cbFree().
*/
static void *cbMalloc(int *pRc, int nByte){
void *pRet;
if( *pRc ){
pRet = 0;
}else{
pRet = sqlite3_malloc(nByte);
if( pRet ){
memset(pRet, 0, nByte);
}else{
*pRc = SQLITE_NOMEM;
}
}
return pRet;
}
/*
** Free an allocation made by cbMalloc().
*/
static void cbFree(void *p){
sqlite3_free(p);
}
/*
** Return the hash bucket that pEntry belongs in.
*/
static int cbHash(sqlite3_changebatch *p, BatchIndexEntry *pEntry){
unsigned int iHash = (unsigned int)pEntry->iIdxId;
unsigned char *pEnd = (unsigned char*)&pEntry->aRecord[pEntry->szRecord];
unsigned char *pIter;
for(pIter=(unsigned char*)pEntry->aRecord; pIter<pEnd; pIter++){
iHash += (iHash << 7) + *pIter;
}
return (int)(iHash % p->nHash);
}
/*
** Resize the hash table.
*/
static int cbHashResize(sqlite3_changebatch *p){
int rc = SQLITE_OK;
BatchIndexEntry **apNew;
int nNew = (p->nHash ? p->nHash*2 : 512);
int i;
apNew = cbMalloc(&rc, sizeof(BatchIndexEntry*) * nNew);
if( rc==SQLITE_OK ){
int nHash = p->nHash;
p->nHash = nNew;
for(i=0; i<nHash; i++){
BatchIndexEntry *pEntry;
while( (pEntry=p->apHash[i])!=0 ){
int iHash = cbHash(p, pEntry);
p->apHash[i] = pEntry->pNext;
pEntry->pNext = apNew[iHash];
apNew[iHash] = pEntry;
}
}
cbFree(p->apHash);
p->apHash = apNew;
}
return rc;
}
/*
** Allocate a new sqlite3_changebatch object.
*/
int sqlite3changebatch_new(sqlite3 *db, sqlite3_changebatch **pp){
sqlite3_changebatch *pRet;
int rc = SQLITE_OK;
*pp = pRet = (sqlite3_changebatch*)cbMalloc(&rc, sizeof(sqlite3_changebatch));
if( pRet ){
pRet->db = db;
}
return rc;
}
/*
** Add a BatchIndex entry for index zIdx to table pTab.
*/
static int cbAddIndex(
sqlite3_changebatch *p,
BatchTable *pTab,
const char *zIdx,
int bPk
){
int nCol = 0;
sqlite3_stmt *pIndexInfo = 0;
BatchIndex *pNew = 0;
int rc;
char *zIndexInfo;
zIndexInfo = (char*)sqlite3_mprintf("PRAGMA main.index_info = %Q", zIdx);
if( zIndexInfo ){
rc = sqlite3_prepare_v2(p->db, zIndexInfo, -1, &pIndexInfo, 0);
sqlite3_free(zIndexInfo);
}else{
rc = SQLITE_NOMEM;
}
if( rc==SQLITE_OK ){
while( SQLITE_ROW==sqlite3_step(pIndexInfo) ){ nCol++; }
rc = sqlite3_reset(pIndexInfo);
}
pNew = (BatchIndex*)cbMalloc(&rc, sizeof(BatchIndex) + sizeof(int) * nCol);
if( rc==SQLITE_OK ){
pNew->nCol = nCol;
pNew->bPk = bPk;
pNew->aiCol = (int*)&pNew[1];
pNew->iId = p->iNextIdxId++;
while( SQLITE_ROW==sqlite3_step(pIndexInfo) ){
int i = sqlite3_column_int(pIndexInfo, 0);
int j = sqlite3_column_int(pIndexInfo, 1);
pNew->aiCol[i] = j;
}
rc = sqlite3_reset(pIndexInfo);
}
if( rc==SQLITE_OK ){
pNew->pNext = pTab->pIdx;
pTab->pIdx = pNew;
}else{
cbFree(pNew);
}
sqlite3_finalize(pIndexInfo);
return rc;
}
/*
** Free the object passed as the first argument.
*/
static void cbFreeTable(BatchTable *pTab){
BatchIndex *pIdx;
BatchIndex *pIdxNext;
for(pIdx=pTab->pIdx; pIdx; pIdx=pIdxNext){
pIdxNext = pIdx->pNext;
cbFree(pIdx);
}
cbFree(pTab);
}
/*
** Find or create the BatchTable object named zTab.
*/
static int cbFindTable(
sqlite3_changebatch *p,
const char *zTab,
BatchTable **ppTab
){
BatchTable *pRet = 0;
int rc = SQLITE_OK;
for(pRet=p->pTab; pRet; pRet=pRet->pNext){
if( 0==sqlite3_stricmp(zTab, pRet->zTab) ) break;
}
if( pRet==0 ){
int nTab = strlen(zTab);
pRet = (BatchTable*)cbMalloc(&rc, nTab + sizeof(BatchTable));
if( pRet ){
sqlite3_stmt *pIndexList = 0;
char *zIndexList = 0;
int rc2;
memcpy(pRet->zTab, zTab, nTab);
zIndexList = sqlite3_mprintf("PRAGMA main.index_list = %Q", zTab);
if( zIndexList==0 ){
rc = SQLITE_NOMEM;
}else{
rc = sqlite3_prepare_v2(p->db, zIndexList, -1, &pIndexList, 0);
sqlite3_free(zIndexList);
}
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pIndexList) ){
if( sqlite3_column_int(pIndexList, 2) ){
const char *zIdx = (const char*)sqlite3_column_text(pIndexList, 1);
const char *zTyp = (const char*)sqlite3_column_text(pIndexList, 3);
rc = cbAddIndex(p, pRet, zIdx, (zTyp[0]=='p'));
}
}
rc2 = sqlite3_finalize(pIndexList);
if( rc==SQLITE_OK ) rc = rc2;
if( rc==SQLITE_OK ){
pRet->pNext = p->pTab;
p->pTab = pRet;
}else{
cbFreeTable(pRet);
pRet = 0;
}
}
}
*ppTab = pRet;
return rc;
}
/*
** Extract value iVal from the changeset iterator passed as the first
** argument. Set *ppVal to point to the value before returning.
**
** This function attempts to extract the value using function xVal
** (which is always either sqlite3changeset_new or sqlite3changeset_old).
** If the call returns SQLITE_OK but does not supply an sqlite3_value*
** pointer, an attempt to extract the value is made using the xFallback
** function.
*/
static int cbGetChangesetValue(
sqlite3_changeset_iter *pIter,
int (*xVal)(sqlite3_changeset_iter*,int,sqlite3_value**),
int (*xFallback)(sqlite3_changeset_iter*,int,sqlite3_value**),
int iVal,
sqlite3_value **ppVal
){
int rc = xVal(pIter, iVal, ppVal);
if( rc==SQLITE_OK && *ppVal==0 && xFallback ){
rc = xFallback(pIter, iVal, ppVal);
}
return rc;
}
static int cbAddToHash(
sqlite3_changebatch *p,
sqlite3_changeset_iter *pIter,
BatchIndex *pIdx,
int (*xVal)(sqlite3_changeset_iter*,int,sqlite3_value**),
int (*xFallback)(sqlite3_changeset_iter*,int,sqlite3_value**),
int *pbConf
){
BatchIndexEntry *pNew;
int sz = pIdx->nCol;
int i;
int iOut = 0;
int rc = SQLITE_OK;
for(i=0; rc==SQLITE_OK && i<pIdx->nCol; i++){
sqlite3_value *pVal;
rc = cbGetChangesetValue(pIter, xVal, xFallback, pIdx->aiCol[i], &pVal);
if( rc==SQLITE_OK ){
int eType = 0;
if( pVal ) eType = sqlite3_value_type(pVal);
switch( eType ){
case 0:
case SQLITE_NULL:
return SQLITE_OK;
case SQLITE_INTEGER:
sz += 8;
break;
case SQLITE_FLOAT:
sz += 8;
break;
default:
assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
sz += sqlite3_value_bytes(pVal);
break;
}
}
}
pNew = cbMalloc(&rc, sizeof(BatchIndexEntry) + sz);
if( pNew ){
pNew->iChangesetId = p->iChangesetId;
pNew->iIdxId = pIdx->iId;
pNew->szRecord = sz;
for(i=0; i<pIdx->nCol; i++){
int eType;
sqlite3_value *pVal;
rc = cbGetChangesetValue(pIter, xVal, xFallback, pIdx->aiCol[i], &pVal);
if( rc!=SQLITE_OK ) break; /* coverage: condition is never true */
eType = sqlite3_value_type(pVal);
pNew->aRecord[iOut++] = eType;
switch( eType ){
case SQLITE_INTEGER: {
sqlite3_int64 i64 = sqlite3_value_int64(pVal);
memcpy(&pNew->aRecord[iOut], &i64, 8);
iOut += 8;
break;
}
case SQLITE_FLOAT: {
double d64 = sqlite3_value_double(pVal);
memcpy(&pNew->aRecord[iOut], &d64, sizeof(double));
iOut += sizeof(double);
break;
}
default: {
int nByte = sqlite3_value_bytes(pVal);
const char *z = (const char*)sqlite3_value_blob(pVal);
memcpy(&pNew->aRecord[iOut], z, nByte);
iOut += nByte;
break;
}
}
}
}
if( rc==SQLITE_OK && p->nEntry>=(p->nHash/2) ){
rc = cbHashResize(p);
}
if( rc==SQLITE_OK ){
BatchIndexEntry *pIter;
int iHash = cbHash(p, pNew);
assert( iHash>=0 && iHash<p->nHash );
for(pIter=p->apHash[iHash]; pIter; pIter=pIter->pNext){
if( pNew->szRecord==pIter->szRecord
&& 0==memcmp(pNew->aRecord, pIter->aRecord, pNew->szRecord)
){
if( pNew->iChangesetId!=pIter->iChangesetId ){
*pbConf = 1;
}
cbFree(pNew);
pNew = 0;
break;
}
}
if( pNew ){
pNew->pNext = p->apHash[iHash];
p->apHash[iHash] = pNew;
p->nEntry++;
}
}else{
cbFree(pNew);
}
return rc;
}
/*
** Add a changeset to the current batch.
*/
int sqlite3changebatch_add(sqlite3_changebatch *p, void *pBuf, int nBuf){
sqlite3_changeset_iter *pIter; /* Iterator opened on pBuf/nBuf */
int rc; /* Return code */
int bConf = 0; /* Conflict was detected */
rc = sqlite3changeset_start(&pIter, nBuf, pBuf);
if( rc==SQLITE_OK ){
int rc2;
for(rc2 = sqlite3changeset_next(pIter);
rc2==SQLITE_ROW;
rc2 = sqlite3changeset_next(pIter)
){
BatchTable *pTab;
BatchIndex *pIdx;
const char *zTab; /* Table this change applies to */
int nCol; /* Number of columns in table */
int op; /* UPDATE, INSERT or DELETE */
sqlite3changeset_op(pIter, &zTab, &nCol, &op, 0);
assert( op==SQLITE_INSERT || op==SQLITE_UPDATE || op==SQLITE_DELETE );
rc = cbFindTable(p, zTab, &pTab);
assert( pTab || rc!=SQLITE_OK );
if( pTab ){
for(pIdx=pTab->pIdx; pIdx && rc==SQLITE_OK; pIdx=pIdx->pNext){
if( op==SQLITE_UPDATE && pIdx->bPk ) continue;
if( op==SQLITE_UPDATE || op==SQLITE_DELETE ){
rc = cbAddToHash(p, pIter, pIdx, sqlite3changeset_old, 0, &bConf);
}
if( op==SQLITE_UPDATE || op==SQLITE_INSERT ){
rc = cbAddToHash(p, pIter, pIdx,
sqlite3changeset_new, sqlite3changeset_old, &bConf
);
}
}
}
if( rc!=SQLITE_OK ) break;
}
rc2 = sqlite3changeset_finalize(pIter);
if( rc==SQLITE_OK ) rc = rc2;
}
if( rc==SQLITE_OK && bConf ){
rc = SQLITE_CONSTRAINT;
}
p->iChangesetId++;
return rc;
}
/*
** Zero an existing changebatch object.
*/
void sqlite3changebatch_zero(sqlite3_changebatch *p){
int i;
for(i=0; i<p->nHash; i++){
BatchIndexEntry *pEntry;
BatchIndexEntry *pNext;
for(pEntry=p->apHash[i]; pEntry; pEntry=pNext){
pNext = pEntry->pNext;
cbFree(pEntry);
}
}
cbFree(p->apHash);
p->nHash = 0;
p->apHash = 0;
}
/*
** Delete a changebatch object.
*/
void sqlite3changebatch_delete(sqlite3_changebatch *p){
BatchTable *pTab;
BatchTable *pTabNext;
sqlite3changebatch_zero(p);
for(pTab=p->pTab; pTab; pTab=pTabNext){
pTabNext = pTab->pNext;
cbFreeTable(pTab);
}
cbFree(p);
}
/*
** Return the db handle.
*/
sqlite3 *sqlite3changebatch_db(sqlite3_changebatch *p){
return p->db;
}
#endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */
-82
View File
@@ -1,82 +0,0 @@
#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_) */
+17 -39
View File
@@ -27,13 +27,6 @@ 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;
@@ -2709,7 +2702,7 @@ static void sessionAppendCol(
*/
static int sessionAppendUpdate(
SessionBuffer *pBuf, /* Buffer to append to */
int ePatchset, /* True for "patchset", 0 for "changeset" */
int bPatchset, /* 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 */
@@ -2773,8 +2766,8 @@ static int sessionAppendUpdate(
/* Add a field to the old.* record. This is omitted if this module is
** currently generating a patchset. */
if( ePatchset!=SESSIONS_PATCHSET ){
if( ePatchset==SESSIONS_FULLCHANGESET || bChanged || abPK[i] ){
if( bPatchset==0 ){
if( bChanged || abPK[i] ){
sessionAppendBlob(pBuf, pCsr, nAdvance, &rc);
}else{
sessionAppendByte(pBuf, 0, &rc);
@@ -2783,7 +2776,7 @@ static int sessionAppendUpdate(
/* Add a field to the new.* record. Or the only record if currently
** generating a patchset. */
if( bChanged || (ePatchset==SESSIONS_PATCHSET && abPK[i]) ){
if( bChanged || (bPatchset && abPK[i]) ){
sessionAppendCol(&buf2, pStmt, i, &rc);
}else{
sessionAppendByte(&buf2, 0, &rc);
@@ -2809,7 +2802,7 @@ static int sessionAppendUpdate(
*/
static int sessionAppendDelete(
SessionBuffer *pBuf, /* Buffer to append to */
int eChangeset, /* One of SESSIONS_CHANGESET etc. */
int bPatchset, /* True for "patchset", 0 for "changeset" */
SessionChange *p, /* Object containing old values */
int nCol, /* Number of columns in table */
u8 *abPK /* Boolean array - true for PK columns */
@@ -2819,7 +2812,7 @@ static int sessionAppendDelete(
sessionAppendByte(pBuf, SQLITE_DELETE, &rc);
sessionAppendByte(pBuf, p->bIndirect, &rc);
if( eChangeset!=SESSIONS_PATCHSET ){
if( bPatchset==0 ){
sessionAppendBlob(pBuf, p->aRecord, p->nRecord, &rc);
}else{
int i;
@@ -3066,12 +3059,12 @@ static int sessionSelectBind(
*/
static void sessionAppendTableHdr(
SessionBuffer *pBuf, /* Append header to this buffer */
int ePatchset, /* Use the patchset format if true */
int bPatchset, /* 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, (ePatchset==SESSIONS_PATCHSET) ? 'P' : 'T', pRc);
sessionAppendByte(pBuf, (bPatchset ? '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);
@@ -3089,7 +3082,7 @@ static void sessionAppendTableHdr(
*/
static int sessionGenerateChangeset(
sqlite3_session *pSession, /* Session object */
int ePatchset, /* One of SESSIONS_CHANGESET etc. */
int bPatchset, /* True for patchset, false for changeset */
int (*xOutput)(void *pOut, const void *pData, int nData),
void *pOut, /* First argument for xOutput */
int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
@@ -3137,7 +3130,7 @@ static int sessionGenerateChangeset(
}
/* Write a table header */
sessionAppendTableHdr(&buf, ePatchset, pTab, &rc);
sessionAppendTableHdr(&buf, bPatchset, pTab, &rc);
/* Build and compile a statement to execute: */
if( rc==SQLITE_OK ){
@@ -3163,10 +3156,10 @@ static int sessionGenerateChangeset(
}
}else{
assert( pTab->abPK!=0 );
rc = sessionAppendUpdate(&buf, ePatchset, pSel, p, pTab->abPK);
rc = sessionAppendUpdate(&buf, bPatchset, pSel, p, pTab->abPK);
}
}else if( p->op!=SQLITE_INSERT ){
rc = sessionAppendDelete(&buf, ePatchset, p, pTab->nCol,pTab->abPK);
rc = sessionAppendDelete(&buf, bPatchset, p, pTab->nCol,pTab->abPK);
}
if( rc==SQLITE_OK ){
rc = sqlite3_reset(pSel);
@@ -3225,8 +3218,7 @@ int sqlite3session_changeset(
int rc;
if( pnChangeset==0 || ppChangeset==0 ) return SQLITE_MISUSE;
rc = sessionGenerateChangeset(
pSession, SESSIONS_CHANGESET, 0, 0, pnChangeset, ppChangeset);
rc = sessionGenerateChangeset(pSession, 0, 0, 0, pnChangeset, ppChangeset);
assert( rc || pnChangeset==0
|| pSession->bEnableSize==0 || *pnChangeset<=pSession->nMaxChangesetSize
);
@@ -3242,8 +3234,7 @@ int sqlite3session_changeset_strm(
void *pOut
){
if( xOutput==0 ) return SQLITE_MISUSE;
return sessionGenerateChangeset(
pSession, SESSIONS_CHANGESET, xOutput, pOut, 0, 0);
return sessionGenerateChangeset(pSession, 0, xOutput, pOut, 0, 0);
}
/*
@@ -3255,8 +3246,7 @@ int sqlite3session_patchset_strm(
void *pOut
){
if( xOutput==0 ) return SQLITE_MISUSE;
return sessionGenerateChangeset(
pSession, SESSIONS_PATCHSET, xOutput, pOut, 0, 0);
return sessionGenerateChangeset(pSession, 1, xOutput, pOut, 0, 0);
}
/*
@@ -3272,20 +3262,9 @@ int sqlite3session_patchset(
void **ppPatchset /* OUT: Buffer containing changeset */
){
if( pnPatchset==0 || ppPatchset==0 ) return SQLITE_MISUSE;
return sessionGenerateChangeset(
pSession, SESSIONS_PATCHSET, 0, 0, pnPatchset, ppPatchset);
return sessionGenerateChangeset(pSession, 1, 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.
*/
@@ -6626,11 +6605,10 @@ 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, eChangeset, pTab, &rc);
sessionAppendTableHdr(&buf, pGrp->bPatch, pTab, &rc);
for(i=0; i<pTab->nChange; i++){
SessionChange *p;
for(p=pTab->apChange[i]; p; p=p->pNext){
-13
View File
@@ -369,19 +369,6 @@ int sqlite3session_changeset(
void **ppChangeset /* OUT: Buffer containing changeset */
);
/*
** CAPI3REF: Generate A Full Changeset From A Session Object
**
** This function is similar to sqlite3session_changeset(), except that for
** each row affected by an UPDATE statement, all old.* values are recorded
** as part of the changeset, not just those modified.
*/
int sqlite3session_fullchangeset(
sqlite3_session *pSession, /* Session object */
int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
void **ppChangeset /* OUT: Buffer containing changeset */
);
/*
** CAPI3REF: Return An Upper-limit For The Size Of The Changeset
** METHOD: sqlite3_session
+18 -147
View File
@@ -233,7 +233,6 @@ static int testStreamOutput(
** $session indirect INTEGER
** $session patchset
** $session table_filter SCRIPT
** $session fullchangeset
*/
static int SQLITE_TCLAPI test_session_cmd(
void *clientData,
@@ -247,20 +246,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 */
{ "fullchangeset",0, "" }, /* 9 */
{ "memory_used", 0, "", }, /* 10 */
{ "changeset_size", 0, "", }, /* 11 */
{ "object_config", 2, "OPTION INTEGER", }, /* 12 */
{ "diff", 2, "FROMDB TBL", }, /* 8 */
{ "memory_used", 0, "", }, /* 9 */
{ "changeset_size", 0, "", }, /* 10 */
{ "object_config", 2, "OPTION INTEGER", }, /* 11 */
{ 0 }
};
int iSub;
@@ -290,11 +289,10 @@ static int SQLITE_TCLAPI test_session_cmd(
break;
}
case 9: /* fullchangeset */
case 7: /* patchset */
case 1: { /* changeset */
TestSessionsBlob o = {0, 0};
if( iSub!=9 && test_tcl_integer(interp, SESSION_STREAM_TCL_VAR) ){
if( test_tcl_integer(interp, SESSION_STREAM_TCL_VAR) ){
void *pCtx = (void*)&o;
if( iSub==7 ){
rc = sqlite3session_patchset_strm(pSession, testStreamOutput, pCtx);
@@ -304,8 +302,6 @@ static int SQLITE_TCLAPI test_session_cmd(
}else{
if( iSub==7 ){
rc = sqlite3session_patchset(pSession, &o.n, &o.p);
}else if( iSub==9 ){
rc = sqlite3session_fullchangeset(pSession, &o.n, &o.p);
}else{
rc = sqlite3session_changeset(pSession, &o.n, &o.p);
}
@@ -321,7 +317,6 @@ static int SQLITE_TCLAPI test_session_cmd(
break;
}
case 2: /* delete */
Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
break;
@@ -372,18 +367,18 @@ static int SQLITE_TCLAPI test_session_cmd(
break;
}
case 10: { /* memory_used */
case 9: { /* memory_used */
sqlite3_int64 nMalloc = sqlite3session_memory_used(pSession);
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nMalloc));
break;
}
case 11: {
case 10: {
sqlite3_int64 nSize = sqlite3session_changeset_size(pSession);
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nSize));
break;
}
case 12: { /* object_config */
case 11: { /* object_config */
struct ObjConfOpt {
const char *zName;
int opt;
@@ -393,6 +388,7 @@ static int SQLITE_TCLAPI test_session_cmd(
{ 0, 0 }
};
int sz = (int)sizeof(aOpt[0]);
int iArg;
Tcl_Size iOpt;
if( Tcl_GetIndexFromObjStruct(interp,objv[2],aOpt,sz,"option",0,&iOpt) ){
@@ -630,7 +626,7 @@ static int test_filter_v3_handler(
Tcl_DecrRefCount(pEval);
return res;
}
}
static int test_conflict_handler(
void *pCtx, /* Pointer to TestConflictHandler structure */
@@ -1299,127 +1295,6 @@ 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 */
Tcl_Size nArg;
unsigned char *pArg = Tcl_GetByteArrayFromObj(objv[2], &nArg);
rc = sqlite3changebatch_add(pChangebatch, pArg, (int)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
@@ -2208,10 +2083,6 @@ 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;
}
+3 -3
View File
@@ -765,7 +765,6 @@ 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 \
@@ -781,6 +780,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/cksumvfs.c \
$(TOP)/ext/misc/closure.c \
$(TOP)/ext/misc/csv.c \
@@ -983,11 +983,11 @@ FUZZCHECK_SRC = sqlite3.c \
$(TOP)/ext/recover/dbdata.c \
$(TOP)/ext/recover/sqlite3recover.c \
$(TOP)/test/vt02.c \
$(TOP)/ext/misc/analyze.c \
$(TOP)/ext/misc/base64.c \
$(TOP)/ext/misc/base85.c \
$(TOP)/ext/misc/completion.c \
$(TOP)/ext/misc/decimal.c \
$(TOP)/ext/misc/diskused.c \
$(TOP)/ext/misc/ieee754.c \
$(TOP)/ext/misc/randomjson.c \
$(TOP)/ext/misc/regexp.c \
@@ -2346,12 +2346,12 @@ SHELL_DEP = \
$(TOP)/ext/expert/sqlite3expert.h \
$(TOP)/ext/intck/sqlite3intck.c \
$(TOP)/ext/intck/sqlite3intck.h \
$(TOP)/ext/misc/analyze.c \
$(TOP)/ext/misc/appendvfs.c \
$(TOP)/ext/misc/base64.c \
$(TOP)/ext/misc/base85.c \
$(TOP)/ext/misc/completion.c \
$(TOP)/ext/misc/decimal.c \
$(TOP)/ext/misc/diskused.c \
$(TOP)/ext/misc/fileio.c \
$(TOP)/ext/misc/ieee754.c \
$(TOP)/ext/misc/memtrace.c \
+67 -70
View File
@@ -1,5 +1,5 @@
C Merge\sthe\slatest\strunk\sfixes\sand\senhancements\sinto\sthe\sbegin-concurrent\sbranch.
D 2026-05-28T20:11:47.857
C Merge\sthe\slatest\strunk\senhancements\sand\sfixes\sinto\sthe\swal2\sbranch.
D 2026-05-29T18:08:48.599
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
@@ -7,7 +7,7 @@ F AGENTS.md 44ddfb38ef23e277888bd70ae378e6cbf5f5cab0e2a4420bd21d89b6caff7a4a
F LICENSE.md 6bc480fc673fb4acbc4094e77edb326267dd460162d7723c7f30bee2d3d9e97d
F Makefile.in 5fda086f33b144da08119255da1d2557f983d0764a13707f05acf0159fd89ba5
F Makefile.linux-generic bd3e3cacd369821a6241d4ea1967395c962dfe3057e38cb0a435cee0e8b789d0
F Makefile.msc bab9a161feb6c721cc9f33f7ccc4662a4cf8e3034f8ed1f6cb442a99a693062f
F Makefile.msc 1b910fff2865b4517db9e525ce33bf62988cc8f72feaf6f8fafbe6e41f7f881e
F README.md f00091ffb5c6379b783afb57ca5f443a5742e119082f37c407f64a5e16c56346
F VERSION 99cf3be5f13d091183e4314b7fc2e0c0e69accfbe64608b45a313338bbdd7b62
F art/icon-243x273.gif 9750b734f82fdb3dc43127753d5e6fbf3b62c9f4e136c2fbf573b2f57ea87af5
@@ -57,7 +57,6 @@ F autosetup/teaish/tester.tcl 091745984473faea6985254b9986c6dfd0cce06f68bc515ba4
F configure 9a00b21dfd13757bbfb8d89b30660a89ec1f8f3a79402b8f9f9b6fc475c3303a x
F contrib/sqlitecon.tcl eb4c6578e08dd353263958da0dc620f8400b869a50d06e271ab0be85a51a08d3
F doc/F2FS.txt c1d4a0ae9711cfe0e1d8b019d154f1c29e0d3abfe820787ba1e9ed7691160fcd
F doc/begin_concurrent.md e694a2a1c623833e7de8b688e81ec30fd4439a78d6b74be61bce0b1e8f590470
F doc/compile-for-unix.md ebd6e461c71d43291e7e8c6487c9c2716bd997053556ec757509011e77e25581
F doc/compile-for-windows.md 36601c95fa4070eebfe757684271d17a7c4a586912ba706d0b5e7817e1df54ad
F doc/json-enhancements.md e356fc834781f1f1aa22ee300027a270b2c960122468499bf347bb123ce1ea4f
@@ -70,6 +69,7 @@ F doc/trusted-schema.md 33625008620e879c7bcfbbfa079587612c434fa094d338b08242288d
F doc/vdbesort-memory.md 4da2639c14cd24a31e0af694b1a8dd37eaf277aff3867e9a8cc14046bc49df56
F doc/vfs-shm.txt 1a55f3f0e7b6745931b117ba5c9df3640d7a0536f532ef0052563100f4416f86
F doc/wal-lock.md 7db0cd61e2000b545b78ce89b0c2a9a8dd8d64c097839258ac10d7c5c4156ec1
F doc/wal2.md a807405a05e19a4945c5905a9ffa0fe45b8560dd7572461192501f565c19cdb5
F ext/README.md 6eb1ac267d917767952ed0ef63f55de003b6a5da433ce1fa389e1a9532e73132
F ext/expert/README.md b321c2762bb93c18ea102d5a5f7753a4b8bac646cb392b3b437f633caf2020c3
F ext/expert/expert.c d548d603a4cc9e61f446cc179c120c6713511c413f82a4a32b1e1e69d3f086a4
@@ -361,12 +361,12 @@ F ext/jni/src/tests/000-001-ignored.test e17e874c6ab3c437f1293d88093cf06286083b6
F ext/jni/src/tests/900-001-fts.test bf0ce17a8d082773450e91f2388f5bbb2dfa316d0b676c313c637a91198090f0
F ext/misc/README.md 6243cdc4d7eb791c41ef0716f3980b8b5f6aa8c61ff76a3958cbf0031c6ebfa7
F ext/misc/amatch.c 972a250631d481f38736b46740bf7f5c9646a0f2bb53800543299a746ff1bac6
F ext/misc/analyze.c d311ad12179ef4f945f18fbe7917a3a602765afd152e5bd71d358ee9a7c67473
F ext/misc/anycollseq.c 5ffdfde9829eeac52219136ad6aa7cd9a4edb3b15f4f2532de52f4a22525eddb
F ext/misc/appendvfs.c 9642c7a194a2a25dca7ad3e36af24a0a46d7702168c4ad7e59c9f9b0e16a3824
F ext/misc/base64.c 1445761667c16356e827fc6418294c869468be934429aaa8315035e76dd58acf
F ext/misc/base85.c c713c7c81b05558c488cbdd8c68d612d5a97f9f0b8adf1167148dd7c89ce3f96
F ext/misc/basexx.c 89ad6b76558efbceb627afd5e2ef1d84b2e96d9aaf9b7ecb20e3d00b51be6fcf
F ext/misc/bgckpt.c 18cfc9c39ffab3299f730f86ae2991c8574c0bd9ec80efd2f89196798a7b7181
F ext/misc/blobio.c a867c4c4617f6ec223a307ebfe0eabb45e0992f74dd47722b96f3e631c0edb2a
F ext/misc/btreeinfo.c 5fe97f798a9ee90e92b3031ed7969ec3f558661bb36b821c3ba045a17cfb951c
F ext/misc/cksumvfs.c 9d7d0cf1a8893ac5d48922bfe9f3f217b4a61a6265f559263a02bb2001259913
@@ -376,6 +376,7 @@ F ext/misc/compress.c 8191118b9b73e7796c961790db62d35d9b0fb724b045e005a5713dc9e0
F ext/misc/csv.c 5e9d4dd749e762c144104c0f01db5bf4458735b19081ebe481a64e589a66687a
F ext/misc/dbdump.c 678f1b9ae2317b4473f65d03132a2482c3f4b08920799ed80feedd2941a06680
F ext/misc/decimal.c 432e5b03a0e2a68a1846a9852a565a1b546ca9b295deda834e4653f0f5577daa
F ext/misc/diskused.c 56433ee0d1ae21975c549e873d99d07b30b1b7fee9cd58b103797d7c7a2eeab6 w ext/misc/analyze.c
F ext/misc/eval.c 04bc9aada78c888394204b4ed996ab834b99726fb59603b0ee3ed6e049755dc1
F ext/misc/explain.c 9670c8ff7b255eea7845abc5123a4958e74016c16990b10497e56380f91704b9
F ext/misc/fileio.c 936c0a7b3382a047d833ad33f62ba59a3847b79ea745bf529797cd344966fbb0
@@ -531,8 +532,6 @@ 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 2ac3b06cf86d6a5935eee8152d164d90030f29cf70218eaa856b235d4b6c18ab
F ext/session/changebatchfault.test be49c793219bf387ad692a60856b921f0854ad6d
F ext/session/changeset.c 06b585d977391d498746f002b2d5f9315d0d37888ce9551bd0cb30bfe9a4cf47
F ext/session/changesetfuzz.c 227076ab0ae4447d742c01ee88a564da6478bbf26b65108bf8fac9cd8b0b24aa
F ext/session/changesetfuzz1.test 15b629004e58d5ffcc852e6842a603775bb64b1ce51254831f3d12b113b616cd
@@ -551,7 +550,7 @@ F ext/session/sessionD.test 470ff917dc849e2eb78142ade63aaabd729d773833cff0ff01bc
F ext/session/sessionE.test b2010949c9d7415306f64e3c2072ddabc4b8250c98478d3c0c4d064bce83111d
F ext/session/sessionF.test d37ed800881e742c208df443537bf29aa49fd56eac520d0f0c6df3e6320f3401
F ext/session/sessionG.test 64c2b69531aebdb36d5977a5f832d77e4c8bda0c746a6c630adf23660bb1c7c2
F ext/session/sessionH.test 29a5441c3dc0a63fa596d745e64bc6c636e062ae04cd89bc84e32c7d98b1fa9b
F ext/session/sessionH.test 71bbff6b1abb2c4ac62b84dee53273c37e0b21e5fde3aed80929403e091ef859
F ext/session/sessionI.test 11e7b6729fc942982a5104a40132f70a2e964d64d60dc5809b8206465af74822
F ext/session/session_common.tcl a31f537a929a695a852d241c9434f2847cadf329856401921139fbb03a5a7697
F ext/session/session_gen.test 942a0002df10da53c45b40b581cc3ed25e7ff42bda1e7ba497273dc2887aa8e6
@@ -578,11 +577,9 @@ 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/sqlite3changebatch.c d5553b79e012ee2cb06c0a96bdf9dfe19e66354390ea0036cc46c4953142d517
F ext/session/sqlite3changebatch.h e72016998c9a22d439ddfd547b69e1ebac810c24
F ext/session/sqlite3session.c 58c557f87b9bbda4dd0bc8b3d0c1d7559d534aa99674dbcb059bdb413c9af402
F ext/session/sqlite3session.h 950766d9350944bc1e60a0ea58986d7cadb1c49bd0632fa5c612a1c1a6a7dec6
F ext/session/test_session.c 1a86d32a6b9c597b6e0d93e0dc4e4d899e0ef4021fc776d2db982493a14af770
F ext/session/sqlite3session.c ce9f2ce2cc6b17f46854788e47016ba9be1b59ca4037728b6c025397b98edb12
F ext/session/sqlite3session.h ca7c4422c1514a95056cc8d333217df6b1829d39058126b1de85d10cd62d7a9c
F ext/session/test_session.c d3275da24b8d362e3c2b393c00d5248f75f1cd474dadf29d8c4683f75cb52e6d
F ext/wasm/GNUmakefile 65feef4ec48e62249f90278c4c08a3fe3c69e2461ff560b61c03cd73606e0949
F ext/wasm/README-dist.txt f01081a850ce38a56706af6b481e3a7878e24e42b314cfcd4b129f0f8427066a
F ext/wasm/README.md 2e87804e12c98f1d194b7a06162a88441d33bb443efcfe00dc6565a780d2f259
@@ -665,7 +662,7 @@ F ext/wasm/tests/opfs/sahpool/index.html be736567fd92d3ecb9754c145755037cbbd2bca
F ext/wasm/tests/opfs/sahpool/sahpool-pausing.js f264925cfc82155de38cecb3d204c36e0f6991460fff0cb7c15079454679a4e2
F ext/wasm/tests/opfs/sahpool/sahpool-worker.js bd25a43fc2ab2d1bafd8f2854ad3943ef673f7c3be03e95ecf1612ff6e8e2a61
F magic.txt 5ade0bc977aa135e79e3faaea894d5671b26107cc91e70783aa7dc83f22f3ba0
F main.mk eb6d6f9254f1780e082b3baba6dc6faf239006e9858e69dd96490025d99b5ab0
F main.mk 7717e67d2545d3cdfacd8fc9fb5e48f448a35fa65e85a01db36483bd9ab4040e
F make.bat a136fd0b1c93e89854a86d5f4edcf0386d211e5d5ec2434480f6eea436c7420c
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
@@ -681,12 +678,12 @@ F src/analyze.c 03bcfc083fc0cccaa9ded93604e1d4244ea245c17285d463ef6a60425fcb247d
F src/attach.c c58278c7d2d954785591c4fde81669ec3e4d52f348c453b028a19ae8adf4f338
F src/auth.c b5ece4e1edccad082c0332fa0087df225473bae0feea9269f824312201377185
F src/backup.c 6ebe22ccbedfcb92423833992130e8d65824be4e6599c3a03f540ab38fc7d13c
F src/bitvec.c 1a048c91b2fbccbbc32dcefa757f93216c0f518ed2bc0b4db39947264479ad06
F src/bitvec.c e242d4496774dfc88fa278177dd23b607dce369ccafb3f61b41638eea2c9b399
F src/btmutex.c 30dada73a819a1ef5b7583786370dce1842e12e1ad941e4d05ac29695528daea
F src/btree.c 6e1699586e15f48b515bfe9b55a1fb10a020c07e90911b99d54b3eedaf945fca
F src/btree.h d2e6b8c39fdb577faa8f4f80a907537c474dc74e6adf3be9272a5a13eed2b5cd
F src/btreeInt.h d802a3263b11c97f04f857e3f55aa58d0af611ed9fe8bebc7d661dfb62370045
F src/build.c c0bd26433979897e8d4fd717c496aa7c5262c5f6cfb4e5fedf5cd4b45fea3d46
F src/btree.c cf1721682bc4d1cd7eb760ad63a9bb1cf9852f9a5c515fc4c55c80519653d763
F src/btree.h e823c46d87f63d904d735a24b76146d19f51f04445ea561f71cc3382fd1307f0
F src/btreeInt.h 9c0f9ea5c9b5f4dcaea18111d43efe95f2ac276cd86d770dce10fd99ccc93886
F src/build.c 866e584cdf40fbc83f530af9fd4d0991582a6fdbd8a9911b7cdbbea5f26a4a9e
F src/callback.c 3605bbf02bd7ed46c79cd48346db4a32fc51d67624400539c0532f4eead804ad
F src/carray.c 3efe3982d5fb323334c29328a4e189ccaef6b95612a6084ad5fa124fd5db1179
F src/complete.c f216b970ce99c5a657556cf1f17e7ddd494515d3beb63df426bf59ff43bd3d9a
@@ -697,7 +694,7 @@ F src/delete.c 59eeca3fb88c29329afc41bb803ee568b120d9dd7470b5f38ab55cc38390b451
F src/expr.c d2188a699ded4522f15cf23d4c82da0ad73dcda09cd943982906824ef019947f
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c 931f74cec1dc8038a0217ef340c91ce147dd1bbed08dc40c47ee0ec6edfffb08
F src/func.c 716afb645e15f28172ce72093fa3ee6acffc314e90823c04903f82ea7c66a0e8
F src/func.c e8525e6c5493149680b0ebd3352e7f004ee7283181f24809b603329afe911443
F src/global.c a19e4b1ca1335f560e9560e590fc13081e21f670643367f99cb9e8f9dc7d615b
F src/hash.c 03c8c0f4be9e8bcb6de65aa26d34a61d48a9430747084a69f9469fbb00ea52ca
F src/hash.h 46b92795a95bfefb210f52f0c316e9d7cdbcdd7e7fcfb0d8be796d3a5767cddf
@@ -707,7 +704,7 @@ F src/insert.c 8dbc22f6ddcc5f0af3abf11daeb89b1978f00059cda15ebc61251fa7724fc7ee
F src/json.c 83122c47ac1c86a3870398e872b1d1afa6439f46fdb92975aa435267771c7ee4
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
F src/loadext.c 78d5b06f18996ffa1203129b28fea043f63a87a4117539678f1d761c30b4ff65
F src/main.c 23801fa412f0d830a901f94f224337e73047fe287d257b3cbbc1bbb5409fa38b
F src/main.c 6180079f53ccdd784df2eddc3751f49ea7153c5959bee792b19ad9f4bdbcf437
F src/malloc.c 422f7e0498e1c9ef967f06283b6f2c0b16db6b905d8e06f6dbc8baaa3e4e6c5a
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c 3bb59158c38e05f6270e761a9f435bf19827a264c13d1631c58b84bdc96d73b2
@@ -728,33 +725,33 @@ F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63
F src/os_common.h 6c0eb8dd40ef3e12fe585a13e709710267a258e2c8dd1c40b1948a1d14582e06
F src/os_kv.c e7d96727db5b67e39d590a68cc61c86daf4c093c36c011a09ebfb521182ec28d
F src/os_setup.h 8efc64eda6a6c2f221387eefc2e7e45fd5a3d5c8337a7a83519ba4fbd2957ae2
F src/os_unix.c dc42cc8beaeace3e414cc9b0d01ff7c585a79070d2fdd73d5371a1b044785cdb
F src/os_unix.c 83759942d1ea8d59daed50901c123016f845fada74caf3496b8a2537c9a08838
F src/os_win.c 8df4b34ec6a08616a7ac33164999524ef773fa359d39ae9ae0e7e1ae4f167440
F src/os_win.h c06ccc3a090cf54202ea58981c298817f3309d4c9e4d52ad0a02927346493721
F src/pager.c 8d32e64ac03196c1dbbf1372b6ab274637d6a6e59829b0ce242c4f0fcc72f4cf
F src/pager.h bc603f12d047db2643989ed1c254d32aa9c610d31b5bb02d94858e658bf63af1
F src/parse.y 7ff18f44e0ae1e71902101988621809537bccf9639ecd85ccea082b1b6643f2f
F src/pager.c 5d795a66965165995dc332f2eda46dc200c394e4756e3eab23406f5c4ed7403d
F src/pager.h fc0c95d27f2aefffcb39b4373b3557e89e1b4b9a36b183cc04d6f22002bf0ad2
F src/parse.y d5a3c5b0277a441c38b35071c05e2b61ff5fc918a63309c809f4b6706179c320
F src/pcache.c 588cc3c5ccaaadde689ed35ce5c5c891a1f7b1f4d1f56f6cf0143b74d8ee6484
F src/pcache.h 092b758d2c5e4dabb30eae46d8dfad77c0f70b16bf3ff1943f7a232b0fe0d4ba
F src/pcache1.c 131ca0daf4e66b4608d2945ae76d6ed90de3f60539afbd5ef9ec65667a5f2fcd
F src/pragma.c 789ef67117b74b5be0a2db6681f7f0c55e6913791b9da309aefd280de2c8a74d
F src/pragma.c 06feccc280ac006b084e5a03a85466aba46aa75bdd7b3cca5a7343e46a0b8875
F src/prepare.c 084a037fd3810cb7ffbfc001cd58c0ffac68ba36598a5084b55ea2a090014ebd
F src/printf.c 2bc09ee91d69c709528575bbbee2199e16d6a7e68e1508ac7cf998a7289170ca
F src/random.c a3e70f8515721ff24d2c0e6afd83923e8faab5ab79ececea4c1bf9fe4049fbb2
F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
F src/resolve.c 7e936a09405cb59e2b3e51a3ad23753e4803afc5269c5171a54c9bdd70f4fc50
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
F src/select.c 6533b3358771537ec332ee5b4b741a963f352b2299e4610bfae77aad0b611fc5
F src/shell.c.in 691fac6a7f6f6fc72836aa2779d81b8cf67c896a15699c65e810cd1e8c6270f4
F src/sqlite.h.in eea00131be003fddeea961e819342fd4e7ebd79073d168db4c20b3daa2db88dc
F src/select.c 4c05cde130f26991b7411d8c6809e0630625e18078742c963a047b4b9cc01d49
F src/shell.c.in a04d063955a69449e5bd876956ba14cf69a897eda1f5477862ab6f2bbf0d3852
F src/sqlite.h.in 749454ec71c875bc130d399ff9a7e47191d143d1b9d8b4af3839b6028df9eda9
F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479
F src/sqlite3ext.h 9788c301f95370fa30e808861f1d2e6f022a816ddbe2a4f67486784c1b31db2e
F src/sqliteInt.h 62866206d9a79c0bb2b03fe5c0cfc6a7b6f441891de2ca3ab8ba681e554879ad
F src/sqliteInt.h 5bec8cfdc8346a122b35312452eb6af33fc750a6c901f2c651a2f53eba0b979f
F src/sqliteLimit.h c70656b67ab5b96741a8f1c812bdd80c81f2b1c1e443d0cc3ea8c33bb1f1a092
F src/status.c 7565d63a79aa2f326339a24a0461a60096d0bd2bce711fefb50b5c89335f3592
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
F src/tclsqlite.c 7401c73c917a4d1b380c896a324c8d8eb533a999559d9e339d479596553bebfd
F src/tclsqlite.h 614b3780a62522bc9f8f2b9fb22689e8009958e7aa77e572d0f3149050af348a
F src/test1.c 70f884e9b4f116f53c3952fb136c5f3d2d764834288e581ddb31f7150428319d
F src/test1.c fe1316579a1778eb96d0bd12807f9dddc5e3984f5950127e2e1718c050cb6304
F src/test2.c 2b9ab96bba63a1c369d5769390475259ad210f144a877805f2e32e563f9e93c1
F src/test3.c 432646f581d8af1bb495e58fc98234380250954f5d5535e507fc785eccc3987a
F src/test4.c 0ac87fc13cdb334ab3a71823f99b6c32a6bebe5d603cd6a71d84c823d43a25a0
@@ -767,7 +764,7 @@ F src/test_backup.c a2bfd90d2ff2511b8635507bdb30fa9b605ade19c16b533066cae3077f5b
F src/test_bestindex.c d75fad21369d80910238032bcf8d9ca1f2bffda13c1ceec63bfbb7f704448b15
F src/test_blob.c 77b994e17f2c87055f44fd96c9a206c5a7155bae2cda2769af60c2f3582f962c
F src/test_btree.c 28283787d32b8fa953eb77412ad0de2c9895260e4e5bd5a94b3c7411664f90d5
F src/test_config.c c5eac1386e78c410219b6749bd65337bf3e86836b106f7e8f9dfa42a23a6d206
F src/test_config.c e02566c2c4ee2916324ce17123a798b47663cead2de546cfbd71d8cddb46bb26
F src/test_delete.c d0e8f6dc55cfc98a7c27c057fb88d512260564bf0b611482656c68b8f7f401ed
F src/test_demovfs.c 3efa2adf4f21e10d95521721687d5ca047aea91fa62dd8cc22ac9e5a9c942383
F src/test_devsym.c 649434ed34d0b03fbd5a6b42df80f0f9a7e53f94dd1710aad5dd8831e91c4e86
@@ -794,7 +791,7 @@ F src/test_schema.c b06d3ddc3edc173c143878f3edb869dd200d57d918ae2f38820534f9a5e3
F src/test_sqllog.c 5abf04865758c0a3915b4ec2b2ee5ab75f74c00e2f05bf503b9083e0ab6829d7
F src/test_superlock.c 3387fc794a68d8c6b6ed059aabacbfe870dc502c5cf65562f36aac78b4a4d629
F src/test_syscall.c c5bf039261973135068aa68f4d185a6147333dcf266977989f8245b3a1968f1b
F src/test_tclsh.c c01706ac60bd3176754d3ccd37da74c6ad97c2e14489f8ed71b497c1c0ac0dd4
F src/test_tclsh.c 179e8c9fc722240275d5d9c5d9ff0d8e652c7614f601b8699c41d9c703f7ec38
F src/test_tclvar.c ae873248a0188459b1c16ca7cc431265dacce524399e8b46725c2b3b7e048424
F src/test_thread.c 3edb4a5b5aeb1a6e9a275dccc848ac95acab7f496b3e9230f6d2d04953a2b862
F src/test_vdbecov.c 5c426d9cd2b351f5f9ceb30cabf8c64a63bfcad644c507e0bd9ce2f6ae1a3bf3
@@ -805,16 +802,16 @@ F src/threads.c 85d8b9f05f78211c61e3739ab5db761d7118766d1916ae7f2764735106bc4e13
F src/tokenize.c e9d52d9f7374d82dadcd11726bea8a597d43709d6672704f3f0375bf1d726912
F src/treeview.c feaa59f14db4f7b5aacca9c5ad5aeb562c1f98262c1ffd74371f4186ade91fc5
F src/trigger.c 4bf3bfb3851d165e4404a9f9e69357345f3f7103378c07e07139fdd8aeb7bd20
F src/update.c b370610139b1a568bda5b1e50e767b8fc760dce28436001884e464f6f20a8d0a
F src/update.c 3e5e7ff66fa19ebe4d1b113d480639a24cc1175adbefabbd1a948a07f28e37cf
F src/upsert.c 215328c3f91623c520ec8672c44323553f12caeb4f01b1090ebdca99fdf7b4f1
F src/utf.c 7267c3fb9e2467020507601af3354c2446c61f444387e094c779dccd5ca62165
F src/util.c 98cf12c8ba65623a76c1eb6e6afa98ff40107c9919bf79af42f4bfc70e654232
F src/vacuum.c 09d61b53904979f2c6acb740121fd0d1b5371f962baaaa2dbbe449b5535ff581
F src/vdbe.c 6e525c6ed98a693dab6fb29069d5bfc29f611e4b35904fc35562992e1b2e2d0e
F src/vacuum.c d3d35d8ae893d419ade5fa196d761a83bddcbb62137a1a157ae751ef38b26e82
F src/vdbe.c a8ba99604891c2e7779419f00274d7711be3765a9b343f0981c28f30d7dac5f3
F src/vdbe.h 70e862ac8a11b590f8c1eaac17a0078429d42bc4ea3f757a9af0f451dd966a71
F src/vdbeInt.h c31ba4dc8d280c2b1dc89c6fcee68f2555e3813ab34279552c20b964c0e338b1
F src/vdbeapi.c 6cdcbe5c7afa754c998e73d2d5d2805556268362914b952811bdfb9c78a37cf1
F src/vdbeaux.c eb9cc84ea6f2068ff8cde9f9284e94a8a16b122ae05ebe4167bf5d8e12bf72ea
F src/vdbeaux.c c09e10e7701357ef998c053b325439a2448534213e8fee11630627a4f327deb4
F src/vdbeblob.c b3f0640db9642fbdc88bd6ebcc83d6009514cafc98f062f675f2c8d505d82692
F src/vdbemem.c efacb8f229422d2a4db0ed38e49b7f3897862a98d82b261aa3b43d7a2d98c6da
F src/vdbesort.c b69220f4ea9ffea5fdef34d968c60305444eea909252a81933b54c296d9cca70
@@ -822,8 +819,8 @@ F src/vdbetrace.c 49e689f751505839742f4a243a1a566e57d5c9eaf0d33bbaa26e2de3febf7b
F src/vdbevtab.c fc46b9cbd759dc013f0b3724549cc0d71379183c667df3a5988f7e2f1bd485f3
F src/vtab.c 5437ce986db2f70e639ce8a3fe68dcdfe64b0f1abb14eaebecdabd5e0766cc68
F src/vxworks.h 9d18819c5235b49c2340a8a4d48195ec5d5afb637b152406de95a9436beeaeab
F src/wal.c f6b7b048caea4be8221f54219f25ed3f3b763ed09ad6808b06cb9f1de3365778
F src/wal.h e9aeb67102d9b9a0b089b80bd6136a16dd6360ac3daa731f2b71c6d4f8341717
F src/wal.c 6fdaac2cb9a53f47052138a41ed7d102c701c6f98fba87826511c47ad31bf563
F src/wal.h 97b8a9903387401377b59507e86b93a148ef1ad4e5ce0f23659a12dcdce56af2
F src/walker.c d5006d6b005e4ea7302ad390957a8d41ed83faa177e412f89bc5600a7462a014
F src/where.c 33e4a6558ee69f33d6a4e7069e3a40a55959d14e5653a9a83926e70305d471f3
F src/whereInt.h 8d94cb116c9e06205c3d5ac87af065fc044f8cf08bfdccd94b6ea1c1308e65da
@@ -912,7 +909,6 @@ F test/badutf.test cff75b714866a4ffa0cdda252eb8fe8765483f5872c0076223c92d52b4fff
F test/badutf2.test f310fd3b24a491b6b77bccdf14923b85d6ebcce751068c180d93a6b8ff854399
F test/basexx1.test 4ae6ddbd92a7ebcabb5d844664c3e755d29fb69c8ddcf0c8d59bbe4e07c23919
F test/bc_common.tcl c70b896d1d4ce72f769d2c7c1fc15b2cb07559eb2093f2736c8ca51664b29ff5
F test/bc_test1.c e0a092579552e066ed4ce7bcdaecfa69c4aacc8d
F test/bestindex1.test 856a453dff8c68b4568601eed5a8b5e20b4763af9229f3947c215729ed878db0
F test/bestindex2.test 394ff8fbf34703391247116d6a44e1c50ee7282236ee77909044573cefc37bc0
F test/bestindex3.test 34bea272b0e0f835651b16a3931dbe7ac927039be6b2e1cb617bbe1d584b492b
@@ -993,16 +989,6 @@ F test/collateB.test 9c840e21f9aead6fc533cea310f0bd202d5c11511088811b7e93ae7b47f
F test/colmeta.test 248a644cec4c7c371cf1e107fd8fdba708dc290866c572672b6260e3466cce79
F test/colname.test 0e32125de701c6bd86247194c6f5639b740b4f71a0d88ee1e67ff3bda9ae99ca
F test/columncount.test 6fe99c2f35738b0129357a1cf3fa483f76140f4cd8a89014c88c33c876d2638f
F test/concfault.test e5370cd686f601a01909377cac3bbf13dac56d39dd4ad6b04ccbec9eeeaccb18
F test/concurrent.test fb624ddac9b008f347685911f90b6b5a022fd0a3f884c0ffef8056bc440e5d76
F test/concurrent2.test de748c7dd749c77e2af2c4b914b9b09a28ac09608042ca498c0251dc6f46aa1a
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 3307ffdf988e04b01c4e942d8aa369a977f085bf629f43a627c9a77f39d65926
F test/conflict2.test 5557909ce683b1073982f5d1b61dfb1d41e369533bfdaf003180c5bc87282dd1
F test/conflict3.test 81865d9599609aca394fb3b9cd5f561d4729ea5b176bece3644f6ecb540f88ac
@@ -1016,7 +1002,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
@@ -1185,7 +1171,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 3cd1654b65d540907695c6b3a704a610dec528d4b43e092086ee22d2f51c45da
F test/fts3corrupt4.test c7f414fe29b97a478d15c90382c4ae077a2bbd2283bf8c63bf66dadaaed3edb8
F test/fts3corrupt5.test 0549f85ec4bd22e992f645f13c59b99d652f2f5e643dac75568bfd23a6db7ed5
F test/fts3corrupt6.test f417c910254f32c0bc9ead7affa991a1d5aec35b3b32a183ffb05eea78289525
F test/fts3corrupt7.test 1da31776e24bb91d3c028e663456b61280b121a74496ccf2fef3fe33790ad2b0
@@ -1269,7 +1255,7 @@ F test/fuzz3.test 70ba57260364b83e964707b9d4b5625284239768ab907dd387c740c0370ce3
F test/fuzz4.test c229bcdb45518a89e1d208a21343e061503460ac69fae1539320a89f572eb634
F test/fuzz_common.tcl b7197de6ed1ee8250a4f82d67876f4561b42ee8cbbfc6160dcb66331bad3f830
F test/fuzz_malloc.test f348276e732e814802e39f042b1f6da6362a610af73a528d8f76898fde6b22f2
F test/fuzzcheck.c 5048a2426bd6968a1b9a00cef7f3aa37442a261df3b532c9e44a1f829bddbede
F test/fuzzcheck.c 674d246742da18270abeef9e8510e3cd9e0e14cddca1f1e8131428928e996a6f
F test/fuzzdata1.db 3e86d9cf5aea68ddb8e27c02d7dfdaa226347426c7eb814918e4d95475bf8517
F test/fuzzdata2.db 128b3feeb78918d075c9b14b48610145a0dd4c8d6f1ca7c2870c7e425f5bf31f
F test/fuzzdata3.db c6586d3e3cef0fbc18108f9bb649aa77bfc38aba
@@ -1483,7 +1469,6 @@ F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660
F test/nan.test 73ea63ab43668313e2f8cc9ef9e9a966672c7934f3ce76926fbe991235d07d91
F test/nockpt.test 3db354270fc63b6871eebd40285d4c55324fb27be629c958adbff6d7fcaa8e14
F test/nolock.test f196cf8b8fbea4e2ca345140a2b3f3b0da45c76e
F test/noop_update.test 74d71bbe5d37f7861cc2402ed620eb165b8e6b00159cdcada7df716420336651
F test/normalize.test f23b6c5926c59548635fcf39678ac613e726121e073dd902a3062fbb83903b72
F test/notify1.test 669b2b743618efdc18ca4b02f45423d5d2304abf
F test/notify2.test 2ecabaa1305083856b7c39cf32816b612740c161
@@ -1525,11 +1510,12 @@ F test/pagerfault2.test caf4c7facb914fd3b03a17b31ae2b180c8d6ca1f
F test/pagerfault3.test 1003fcda009bf48a8e22a516e193b6ef0dd1bbd8
F test/pageropt.test 84e4cc5cbca285357f7906e99b21be4f2bf5abc0
F test/pagesize.test 5769fc62d8c890a83a503f67d47508dfdc543305
F test/parser1.test 131f4733472252d53d8ed681115257866f55740ab697fa05900d766049348f27
F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
F test/pendingrace.test e99efc5ab3584da3dfc8cd6a0ec4e5a42214820574f5ea24ee93f1d84655f463
F test/percentile.test eaee1ff3e35d5fe933ac98d927c9a7d2f4f1a1a19ee22e7d45e97a6d9ee32077
F test/permutations.test e6de4f5777f7785737ac3d1d964b8656e5477a134665b2fe8a91884ab9b685b3
F test/permutations.test db0a650837cf1eb2128b9c33aac8059933338cf25ccd4c7a089115d6b23368cf
F test/pg_common.tcl 3b27542224db1e713ae387459b5d117c836a5f6e328846922993b6d2b7640d9f
F test/pragma.test 7d07b7bb76e273215d6a20c4f83c3062cc28976c737ccb70a686025801e86c8f
F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
@@ -1560,7 +1546,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 0d307c335b3421898cfe64a783a376138aa003849b6bff61ee2d21e805bc0051
F test/recover.test 643139b911ac880a1e881d7621f02cfb546b608b8f2494d7d26fd5ed103b1ceb
F test/regexp1.sql fd15d317973dd0c9cd9ee7b8bda45ef1c0a5e126223fc53c86d71b5d407e6fb4
@@ -1593,7 +1579,7 @@ 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
@@ -1651,7 +1637,7 @@ F test/shell3.test 91efdd545097a61a1f72cf79c9ad5b49da080f3f10282eaf4c3c272cd1012
F test/shell4.test e25580a792b7b54560c3a76b6968bd8189261f38979fe28e6bc6312c5db280db
F test/shell5.test a0c43d82a811a463a5d07d6418c5a045ab01a072544db8aa31ae394e93845d8d
F test/shell6.test 596f2eb385a1097d4e9130b4d4f663a24cba294b35f13b774b8f13553dec307d
F test/shell7.test 4468c1abb7e2708a3aa3f84c8918ca31fbf7909b1c19063853f2abb0a072dfab
F test/shell7.test 6b006d296b569a7ec78b8168af77578b3bde8f58b79c32d9f46cff1eaf763a29
F test/shell8.test 38c9e4d7e85d2a3ecfacaa9f6cda4f7a81bf4fffb5f3f37f9cd76827c6883192
F test/shell9.test c0e8871061a92151450b3332279a893b516fa73a6c46d4f51a0998407cbf8c89
F test/shellA.test 05cdaafa1f79913654487ce3aefa038d4106245d58f52e02faf506140a76d480
@@ -1738,7 +1724,7 @@ F test/temptable2.test 76821347810ecc88203e6ef0dd6897b6036ac788e9dd3e6b04fd4d163
F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637
F test/temptrigfault.tes fc5918e64f3867156fefe7cfca9d8e1f495134a5229b2b511b0dc11c07f2eab4
F test/temptrigger.test a00f258ed8d21a0e8fd4f322f15e8cfb5cef2e43655670e07a753e3fb4769d61
F test/tester.tcl 2d943f60200e0a36bcd3f1f0baf181a751cd3604ef6b6bd4c8dc39b4e8a53116
F test/tester.tcl 7a86598eed39bfbfc5dea5f1c35c3e6b0843b2155188ec6f5ed4d2c9fb9d841c
F test/testloadext.c 862b848783eaed9985fbce46c65cd214664376b549fae252b364d5d1ef350a27
F test/testrunner.tcl 8171b887ab78d55b73fd971e22690eff0fabec913fbf3b5fbeaf159b3f00b2dc x
F test/testrunner_data.tcl 4b3cf036d39c98b83f9289a5c047eb01089c932d4f59a81bf764f6800589b959
@@ -1930,7 +1916,6 @@ F test/triggerG.test b4e3fbccde6cf8995177cd6cad880256c8c00e407e07d8c67149f461062
F test/triggerupfrom.test d1f9e56090408115c522bee626cc33a2f3370f627a5e341d832589d72e3aa271
F test/trustschema1.test d2996bb284859c99956ac706160eab9f086919da738d19bfef3ac431cce8fd47
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
@@ -1963,7 +1948,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 1250724af9beeed2d6c3716f5b990c483200c54f408d3c0ec9543a3c7961f8fc
F test/uri.test 234147e5eda1ef143a8b74ac8f8e9bae363aafa6b8c5813119052f268765d62f
F test/uri2.test 9d3ba7a53ee167572d53a298ee4a5d38ec4a8fb7
F test/utf16align.test 9fde0bb5d3a821594aa68c6829ab9c5453a084384137ebb9f6153e2d678039da
F test/vacuum-into.test 5a489714feecfdabfc7b293be4111564a173dee92c0d6818dd0207f3ade65783
@@ -2009,8 +1994,20 @@ 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/wal.test a5d6c7f4bd79251ed344229d96d44ecdfb896bdbd32b7e65f118756699c7e473
F test/wal2.test f058016abe4627d2664db4b4b87990298d925e66a4c5a2c8e674a0ff6f4c841d
F test/wal2big.test 829141cbecdda4329db8fa38705424c4a73db72a06b9540b06811a825d330409
F test/wal2fault.test 2e8e60cacd5bcd451618aeffd05f676894d17202d3e2986e288d36e2c5993249
F test/wal2lock.test 0ef98d72dc6bcf7711dedd684760488400d9a9a6eec0dc5d3822060437793552
F test/wal2openclose.test 2b26be723ea7f4263c8d5d70b37efd1c359561a0526e39466c45fe8e6478daee
F test/wal2recover.test 348a3f2a4c79359790f70fd692dcd0c8f04e42a85365e688778c945e10bae02b
F test/wal2recover2.test 3fe5d5932b16a3a38304a85bafed1f965faef40c230fd450838a0502e61343bb
F test/wal2recover3.test 4a91689e165a38bc401736e6518188c2b0ff4fa1566d1810b8867536db128177
F test/wal2rewrite.test 6ca6f631ffcf871240beab5f02608913fd075c6d0d31310b026c8383c65c9f9c
F test/wal2rollback.test 23adc4a099b23f6aaea8b04fdca1c35861d887dd80f8be7da2d5273eb777e428
F test/wal2savepoint.test c380512ec1f97012cfc753e718bf3df38f51e7c319110e1cae25094a33b371c4
F test/wal2simple.test d2d94fe1f30700c9f30e46c16f1c3d7d245b861a1be2bfe5ead701484c6dd38e
F test/wal2snapshot.test 7a5f4629a3c43a43c3440b8b2ea9f07de91a46b0b9eea5f08f62b5bf5b6468df
F test/wal3.test 5de023bb862fd1eb9d2ad26fa8d9c43abb5370582e5b08b2ae0d6f93661bc310
F test/wal4.test 4744e155cd6299c6bd99d3eab1c82f77db9cdb3c
F test/wal5.test 9c11da7aeccd83a46d79a556ad11a18d3cb15aa9
@@ -2023,7 +2020,7 @@ F test/wal_common.tcl 204d1721ac13c5e0c7fae6380315b5ab7f4e8423f580d826c5e9df1995
F test/walbak.test 018d4e5a3d45c6298d11b99f09a8ef6876527946
F test/walbig.test f437473a16cfb314867c6b5d1dbcd519e73e3434
F test/walblock.test 6bb472e82730e7e4e81395e907a01d8cfc2bd9e1f01f8a9184ca572e2955a4bf
F test/walckptnoop.test 6b5dd8a98001110f4b3ca3b64743676d0be638ddf34cc9944c2e9340d1603d23
F test/walckptnoop.test 5f6123750f40cb86633a7e014f9fb805d0eb494b811840086dc72e554e68c7c1
F test/walcksum.test 50e204500eed9c691b6045e467bb2923f49aa93d8adf315e2be135fdb202c1c2
F test/walcrash.test 21038858cc552077b0522f50b0fa87e38139306a
F test/walcrash2.test a0edab4e5390f03b99a790de89aad15d6ec70b36
@@ -2037,7 +2034,7 @@ F test/walnoshm.test 844b3eb7d8e8ee76c834ef723babec57b0be51fa52ef7e321c289ed0fe3
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/walrestart.test 5168c0c2414d1971d8dec949c1070a0144cf15402361ba0d0e6a8054f5598a64
F test/walro.test 78a84bc0fdae1385c06b017215c426b6845734d6a5a3ac75c918dd9b801b1b9d
F test/walro2.test 33955a6fd874dd9724005e17f77fef89d334b3171454a1256fe4941a96766cdc
@@ -2166,14 +2163,14 @@ F tool/mkamalzip.tcl 8aa5ebe7973c8b8774062d34e15fea9815c4cc2ceea3a9b184695f00591
F tool/mkautoconfamal.sh 06fbe090b81c24e592c1f22b404334f805ba74d482a9260f2ac81e6f3d3386d8
F tool/mkccode.tcl c42a8f8cf78f92e83795d5447460dbce7aaf78a3bbf9082f1507dc71a3665f3c x
F tool/mkcombo.tcl 2a5189b219c4a495e1ff7fc980bd568d3cfb82ae9d50c84e77f7a161e96fc132
F tool/mkctimec.tcl 68f3ee9e2e6a06524b25d79b5b61a883053466080a39da59e0a0697f0c4d9a3b x
F tool/mkctimec.tcl bbaa2265949d8825c68ceacd76d5c230b247493547ce9e6c08335e73055cdf4d x
F tool/mkfptab.c 24ea40113f96584caca3f6dd06b4ad5adffa0f39023910cb83fa7ef2ffa9b0ba
F tool/mkkeywordhash.c 82d5af1d0e677900739fba59155cddac172d8c712c2d91ab73d6e6bcb30060f0
F tool/mkmsvcmin.tcl d76c45efda1cce2d4005bcea7b8a22bb752e3256009f331120fb4fecb14ebb7a
F tool/mkopcodec.tcl 33d20791e191df43209b77d37f0ff0904620b28465cca6990cf8d60da61a07ef
F tool/mkopcodeh.tcl 2b4e6967a670ef21bf53a164964c35c6163277d002a4c6f56fa231d68c88d023
F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa
F tool/mkpragmatab.tcl 10694206dfe9d1f6e24d5876d52bb56e67706ef20bc479002bb88b67b90be045
F tool/mkpragmatab.tcl 3801ce32f8c55fe63a3b279f231fb26c2c1a2ea9a09d2dd599239d87a609acec
F tool/mkshellc.tcl da6918b128e928a8f0d663519e14829153e59465bd5eb596442e99fa10a411b7
F tool/mksourceid.c 36aa8020014aed0836fd13c51d6dc9219b0df1761d6b5f58ff5b616211b079b9
F tool/mksqlite3c-noext.tcl 351c55256213154cabb051a3c870ef9f4487de905015141ae50dc7578a901b84
@@ -2225,8 +2222,8 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee
F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
P 7f954a9e2fa4203b55825dfd70a46ffde7c985a4c8b940208d74d97441f3fd04 87c37dab7e53d1bd891f3fed624963b35ab15a785706d0964b5d07ab70421c10
R c529eb9e8d67c3d1e0712f4ef1c66a47
P 28d1d29ad465b150ea705af8352c0e6599b3f42c15474b2ae89e8a2c2b9b097c c7839f7a1749b38f8a36c06d93a7b59095e91ce753e8dd020de273ca8f73a239
R da9eb2c8c6fc545fa76a8916647efb64
U drh
Z eb2c001fb4ab626a26408cdfc65d4d8f
Z 81655c913c3efc0b9200f9ede46569c5
# Remove this line to create a well-formed Fossil manifest.
+2 -2
View File
@@ -1,2 +1,2 @@
branch begin-concurrent
tag begin-concurrent
branch wal2
tag wal2
+1 -1
View File
@@ -1 +1 @@
330387a9c6a0ce6716e62f920d874bbea8b2e38f549fc03dd72d537284f1a01d
d8cf1a62aff38de008f9b826c4846b15e37c89dc0f8bb412b79349da4d1580b0
-6
View File
@@ -172,12 +172,6 @@ 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;
+55 -653
View File
File diff suppressed because it is too large Load Diff
-2
View File
@@ -361,8 +361,6 @@ sqlite3_uint64 sqlite3BtreeSeekCount(Btree*);
# define sqlite3BtreeSeekCount(X) 0
#endif
int sqlite3BtreeExclusiveLock(Btree *pBt);
#ifndef NDEBUG
int sqlite3BtreeCursorIsValid(BtCursor*);
#endif
+2 -16
View File
@@ -232,7 +232,6 @@
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
@@ -276,9 +275,6 @@ 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 */
@@ -460,9 +456,6 @@ 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() */
};
@@ -686,19 +679,12 @@ struct BtCursor {
** (sqliteMallocRaw), it is not possible to use conditional compilation.
** So, this macro is defined instead.
*/
#ifdef SQLITE_OMIT_AUTOVACUUM
#define ISAUTOVACUUM(pBt) 0
#else
#ifndef SQLITE_OMIT_AUTOVACUUM
#define ISAUTOVACUUM(pBt) (pBt->autoVacuum)
#endif
#ifdef SQLITE_OMIT_CONCURRENT
# define ISCONCURRENT 0
#else
# define ISCONCURRENT (pBt->pMap!=0)
#define ISAUTOVACUUM(pBt) 0
#endif
#define REQUIRE_PTRMAP (ISAUTOVACUUM(pBt) || ISCONCURRENT)
/*
** This structure is passed around through all the PRAGMA integrity_check
+2 -4
View File
@@ -90,10 +90,8 @@ void sqlite3TableLock(
u8 isWriteLock, /* True for a write lock */
const char *zName /* Name of the table to be locked */
){
#ifdef SQLITE_OMIT_CONCURRENT
if( iDb==1 ) return;
if( !sqlite3BtreeSharable(pParse->db->aDb[iDb].pBt) ) return;
#endif
lockTable(pParse, iDb, iTab, isWriteLock, zName);
}
@@ -5275,7 +5273,7 @@ void sqlite3BeginTransaction(Parse *pParse, int type){
}
v = sqlite3GetVdbe(pParse);
if( !v ) return;
if( type==TK_IMMEDIATE || type==TK_EXCLUSIVE ){
if( type!=TK_DEFERRED ){
for(i=0; i<db->nDb; i++){
int eTxnType;
Btree *pBt = db->aDb[i].pBt;
@@ -5290,7 +5288,7 @@ void sqlite3BeginTransaction(Parse *pParse, int type){
sqlite3VdbeUsesBtree(v, i);
}
}
sqlite3VdbeAddOp3(v, OP_AutoCommit, 0, 0, (type==TK_CONCURRENT));
sqlite3VdbeAddOp0(v, OP_AutoCommit);
}
/*
+2 -4
View File
@@ -576,9 +576,8 @@ static void randomFunc(
sqlite3_value **NotUsed2
){
sqlite_int64 r;
sqlite3 *db = sqlite3_context_db_handle(context);
UNUSED_PARAMETER2(NotUsed, NotUsed2);
sqlite3FastRandomness(&db->sPrng, sizeof(r), &r);
sqlite3_randomness(sizeof(r), &r);
if( r<0 ){
/* We need to prevent a random number of 0x8000000000000000
** (or -9223372036854775808) since when you do abs() of that
@@ -604,7 +603,6 @@ 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]);
@@ -613,7 +611,7 @@ static void randomBlob(
}
p = contextMalloc(context, n);
if( p ){
sqlite3FastRandomness(&db->sPrng, n, p);
sqlite3_randomness(n, p);
sqlite3_result_blob(context, (char*)p, n, sqlite3_free);
}
}
+1 -30
View File
@@ -3409,7 +3409,7 @@ static int openDatabase(
db->lookaside.bDisable = 1;
db->lookaside.sz = 0;
db->nFpDigit = 17;
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;
@@ -5148,35 +5148,6 @@ 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
-50
View File
@@ -1339,10 +1339,6 @@ 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
};
/*
@@ -1499,13 +1495,6 @@ 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);
@@ -2419,14 +2408,6 @@ 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);
}
@@ -4261,9 +4242,6 @@ 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);
@@ -5581,9 +5559,6 @@ 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;
@@ -5715,28 +5690,6 @@ 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);
@@ -6179,9 +6132,6 @@ static int fillInUnixFile(
if( pLockingStyle == &posixIoMethods
#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
|| pLockingStyle == &nfsIoMethods
#endif
#ifdef SQLITE_SHARED_MAPPING
|| pLockingStyle == &nolockIoMethods
#endif
){
unixEnterMutex();
+52 -213
View File
@@ -658,9 +658,6 @@ 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 */
@@ -916,9 +913,7 @@ static int assert_pager_state(Pager *p){
if( !pagerUseWal(pPager) ){
assert( p->eLock>=RESERVED_LOCK );
}
#ifndef SQLITE_OMIT_CONCURRENT
assert( pPager->dbSize==pPager->dbOrigSize || pPager->pAllRead );
#endif
assert( pPager->dbSize==pPager->dbOrigSize );
assert( pPager->dbOrigSize==pPager->dbFileSize );
assert( pPager->dbOrigSize==pPager->dbHintSize );
assert( pPager->setSuper==0 );
@@ -937,6 +932,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 );
@@ -951,6 +947,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 );
@@ -963,6 +960,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;
@@ -1826,53 +1824,6 @@ 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
@@ -1897,7 +1848,8 @@ static void pager_unlock(Pager *pPager){
|| pPager->eState==PAGER_ERROR
);
pagerFreeBitvecs(pPager);
sqlite3BitvecDestroy(pPager->pInJournal);
pPager->pInJournal = 0;
releaseAllSavepoints(pPager);
if( pagerUseWal(pPager) ){
@@ -2154,6 +2106,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 ){
@@ -2173,7 +2126,8 @@ static int pager_end_transaction(Pager *pPager, int hasSuper, int bCommit){
}
#endif
pagerFreeBitvecs(pPager);
sqlite3BitvecDestroy(pPager->pInJournal);
pPager->pInJournal = 0;
pPager->nRec = 0;
if( rc==SQLITE_OK ){
if( MEMDB || pagerFlushOnCommit(pPager, bCommit) ){
@@ -3206,24 +3160,8 @@ 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,
#ifdef SQLITE_OMIT_CONCURRENT
0
#else
pPager->pAllRead!=0
#endif
);
rc = sqlite3WalUndo(pPager->pWal, pagerUndoCallback, (void *)pPager);
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);
@@ -3273,8 +3211,6 @@ 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 );
@@ -3329,6 +3265,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;
@@ -3424,9 +3364,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;
}
}
@@ -4360,7 +4300,7 @@ static int syncJournal(Pager *pPager, int newHdr){
assert( assert_pager_state(pPager) );
assert( !pagerUseWal(pPager) );
rc = sqlite3PagerExclusiveLock(pPager, 0, 0);
rc = sqlite3PagerExclusiveLock(pPager);
if( rc!=SQLITE_OK ) return rc;
if( !pPager->noSync ){
@@ -4711,12 +4651,6 @@ 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 ){
@@ -4953,6 +4887,7 @@ int sqlite3PagerOpen(
(u64)nPathname + 8 + 1 + /* Journal filename */
#ifndef SQLITE_OMIT_WAL
(u64)nPathname + 4 + 1 + /* WAL filename */
nPathname + 5 + 1 + /* Second WAL filename */
#endif
3 /* Terminator */
);
@@ -5005,6 +4940,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;
}
@@ -5549,23 +5486,6 @@ 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
@@ -5639,14 +5559,6 @@ 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 ){
@@ -6005,14 +5917,11 @@ 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 0, then acquire at least a RESERVED
** lock on the database file. If exFlag is >0, then acquire at least
** 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
** 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
@@ -6030,6 +5939,7 @@ 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.
@@ -6045,10 +5955,9 @@ 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. */
if( exFlag>=0 ){
rc = sqlite3WalBeginWriteTransaction(pPager->pWal);
}
** holds the write-lock. If possible, the upper layer will call it.
*/
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
@@ -6056,7 +5965,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>0 ){
if( rc==SQLITE_OK && exFlag ){
rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
}
}
@@ -6356,7 +6265,7 @@ int sqlite3PagerWrite(PgHdr *pPg){
** to sqlite3PagerWrite(). In other words, return TRUE if it is ok
** to change the content of the page.
*/
#if !defined(SQLITE_OMIT_CONCURRENT) || !defined(NDEBUG)
#ifndef NDEBUG
int sqlite3PagerIswriteable(DbPage *pPg){
return pPg->flags & PGHDR_WRITEABLE;
}
@@ -6512,26 +6421,17 @@ int sqlite3PagerSync(Pager *pPager, const char *zSuper){
}
/*
** 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.
** 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.
**
** 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.
** 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.
*/
int sqlite3PagerExclusiveLock(Pager *pPager, PgHdr *pPage1, Pgno *piConflict){
int sqlite3PagerExclusiveLock(Pager *pPager){
int rc = pPager->errCode;
assert( assert_pager_state(pPager) );
if( rc==SQLITE_OK ){
@@ -6543,73 +6443,10 @@ int sqlite3PagerExclusiveLock(Pager *pPager, PgHdr *pPage1, Pgno *piConflict){
if( 0==pagerUseWal(pPager) ){
rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
}
#ifndef SQLITE_OMIT_CONCURRENT
else{
if( pPager->pAllRead ){
/* This is an CONCURRENT transaction. Attempt to lock the wal database
** here. If SQLITE_BUSY (but not SQLITE_BUSY_SNAPSHOT) is returned,
** invoke the busy-handler and try again for as long as it returns
** non-zero. */
do {
rc = sqlite3WalLockForCommit(
pPager->pWal, pPage1, pPager->pAllRead, piConflict
);
}while( rc==SQLITE_BUSY
&& pPager->xBusyHandler(pPager->pBusyHandlerArg)
);
}
}
#endif /* SQLITE_OMIT_CONCURRENT */
}
return rc;
}
#ifndef SQLITE_OMIT_CONCURRENT
/*
** This function is called as part of committing an CONCURRENT transaction.
** At this point the wal WRITER lock is held, and all pages in the cache
** except for page 1 are compatible with the snapshot at the head of the
** wal file.
**
** This function updates the in-memory data structures and reloads the
** contents of page 1 so that the client is operating on the snapshot
** at the head of the wal file.
**
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
*/
int sqlite3PagerUpgradeSnapshot(Pager *pPager, DbPage *pPage1){
int rc;
assert( pPager->pWal && pPager->pAllRead );
rc = sqlite3WalUpgradeSnapshot(pPager->pWal);
if( rc==SQLITE_OK ){
rc = readDbPage(pPage1);
}
return rc;
}
/* !defined(SQLITE_OMIT_CONCURRENT)
**
** Set the in-memory cache of the database file size to nSz pages.
*/
void sqlite3PagerSetDbsize(Pager *pPager, Pgno nSz){
pPager->dbSize = nSz;
}
/* !defined(SQLITE_OMIT_CONCURRENT)
**
** If this is a WAL mode connection and the WRITER lock is currently held,
** relinquish it.
*/
void sqlite3PagerDropExclusiveLock(Pager *pPager){
if( pagerUseWal(pPager) ){
sqlite3WalEndWriteTransaction(pPager->pWal);
}
}
#endif /* SQLITE_OMIT_CONCURRENT */
/*
** Sync the database file for the pager pPager. zSuper points to the name
** of a super-journal file that should be written into the individual
@@ -7541,7 +7378,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
@@ -7575,9 +7413,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.
@@ -7752,7 +7593,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 );
@@ -7773,7 +7614,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
);
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
if( rc==SQLITE_OK ){
@@ -7804,6 +7645,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 */
@@ -7820,9 +7662,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{
@@ -7844,7 +7686,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
@@ -7859,7 +7703,7 @@ int sqlite3PagerCloseWal(Pager *pPager, sqlite3 *db){
);
}
if( rc==SQLITE_OK && logexists ){
rc = pagerOpenWal(pPager);
rc = pagerOpenWal(pPager, 0);
}
}
@@ -7983,11 +7827,6 @@ 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
+5 -22
View File
@@ -82,8 +82,9 @@ 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)
#define isWalMode(x) ((x)==PAGER_JOURNALMODE_WAL || (x)==PAGER_JOURNALMODE_WAL2)
/*
** The argument to this macro is a file descriptor (type sqlite3_file*).
@@ -179,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*, DbPage *pPage1, Pgno*);
int sqlite3PagerExclusiveLock(Pager*);
int sqlite3PagerSync(Pager *pPager, const char *zSuper);
int sqlite3PagerCommitPhaseTwo(Pager*);
int sqlite3PagerRollback(Pager*);
@@ -191,7 +192,7 @@ int sqlite3PagerSharedLock(Pager *pPager);
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);
@@ -241,28 +242,10 @@ 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*);
+2 -11
View File
@@ -186,16 +186,7 @@ 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) ::= 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);
}
}
transtype(A) ::= EXCLUSIVE(X). {A = @X; /*A-overwrites-X*/}
cmd ::= COMMIT|END(X) trans_opt. {sqlite3EndTransaction(pParse,@X);}
cmd ::= ROLLBACK(X) trans_opt. {sqlite3EndTransaction(pParse,@X);}
@@ -333,6 +324,7 @@ 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 identifier-or-string.
//
%token_class ids ID|STRING.
@@ -2138,7 +2130,6 @@ 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 */
+2 -1
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;
-27
View File
@@ -129,33 +129,6 @@ 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 ){
/* "x" is a variant of LFSR called "Xorshift" by George Marsaglia */
pPrng->x ^= pPrng->x <<13;
pPrng->x ^= pPrng->x >>7;
pPrng->x ^= pPrng->x <<17;
/* "y" is a LCG using Don Kunth's constants from MMIX */
pPrng->y = (pPrng->y)*6364136223846793005LL + 1442695040888963407LL;
/* XOR the two streams together to give the final result */
*(pOut++) = (pPrng->x ^ pPrng->y) & 0xff;
}
}
#ifndef SQLITE_UNTESTABLE
/*
** For testing purposes, we sometimes want to preserve the state of
+1 -1
View File
@@ -2306,7 +2306,7 @@ int sqlite3ColumnsFromExprList(
zName = sqlite3MPrintf(db, "%.*z:%u", nName, zName, ++cnt);
sqlite3ProgressCheck(pParse);
if( cnt>3 ){
sqlite3FastRandomness(&db->sPrng, sizeof(cnt), &cnt);
sqlite3_randomness(sizeof(cnt), &cnt);
}
}
pCol->zCnName = zName;
+65 -45
View File
@@ -301,7 +301,7 @@ INCLUDE ../ext/intck/sqlite3intck.h
INCLUDE ../ext/intck/sqlite3intck.c
INCLUDE ../ext/misc/stmtrand.c
INCLUDE ../ext/misc/vfstrace.c
INCLUDE ../ext/misc/analyze.c
INCLUDE ../ext/misc/diskused.c
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
#define SQLITE_SHELL_HAVE_RECOVER 1
@@ -2104,6 +2104,43 @@ static void modeSetStr(char **az, const char *zNew){
}
}
/* Forward reference */
static int pickStr(const char *zArg, char **pzErr, ...);
/*
** Change the limits on the display mode. Return 0 on
** success. Return non-zero if zArg is mis-formatted.
**
** Valid arguments:
**
** "on" Default limits
** "off" All limits turned off
** L,C Line and Characters limits set
** L,C,T Line, Character, and Title limits set
**
** Anything else returns non-zero
*/
static int modeSetLimit(ShellState *p, const char *zArg){
int k = zArg==0 ? 1 : pickStr(zArg,0,"on","off","");
if( k==0 ){
p->mode.spec.nLineLimit = DFLT_LINE_LIMIT;
p->mode.spec.nCharLimit = DFLT_CHAR_LIMIT;
p->mode.spec.nTitleLimit = DFLT_TITLE_LIMIT;
}else if( k==1 ){
p->mode.spec.nLineLimit = 0;
p->mode.spec.nCharLimit = 0;
p->mode.spec.nTitleLimit = 0;
}else{
int L, C, T = 0;
int nNum = sscanf(zArg, "%d,%d,%d", &L, &C, &T);
if( nNum<2 || L<0 || C<0 || T<0) return 1;
p->mode.spec.nLineLimit = L;
p->mode.spec.nCharLimit = C;
if( nNum==3 ) p->mode.spec.nTitleLimit = T;
}
return 0;
}
/*
** Change the mode to eMode
*/
@@ -2140,6 +2177,7 @@ static void modeChange(ShellState *p, unsigned char eMode){
u8 mFlags = p->mode.mFlags;
modeFree(&p->mode);
modeChange(p, MODE_List);
modeSetLimit(p, "off");
p->mode.mFlags = mFlags;
}else if( eMode==MODE_TTY ){
u8 mFlags = p->mode.mFlags;
@@ -4228,8 +4266,8 @@ static const char *(azHelp[]) = {
#if SQLITE_SHELL_HAVE_RECOVER
".dbinfo ?DB? Show status information about the database",
#endif
".dbstat ?SCHEMA? Report database space and size stats",
".dbtotxt Hex dump of the database file",
".diskused ?SCHEMA? Report database space and size stats",
".dump ?OBJECTS? Render database content as SQL",
" Options:",
" --data-only Output only INSERT statements",
@@ -5058,7 +5096,7 @@ static void open_db(ShellState *p, int openFlags){
sqlite3_regexp_init(p->db, 0, 0);
sqlite3_ieee_init(p->db, 0, 0);
sqlite3_series_init(p->db, 0, 0);
sqlite3_analyze_init(p->db, 0, 0);
sqlite3_diskused_init(p->db, 0, 0);
#ifndef SQLITE_SHELL_FIDDLE
sqlite3_fileio_init(p->db, 0, 0);
sqlite3_completion_init(p->db, 0, 0);
@@ -8720,26 +8758,10 @@ static int dotCmdMode(ShellState *p){
dotCmdError(p, i-1, "missing argument", 0);
return 1;
}
k = pickStr(azArg[i],0,"on","off","");
if( k==0 ){
p->mode.spec.nLineLimit = DFLT_LINE_LIMIT;
p->mode.spec.nCharLimit = DFLT_CHAR_LIMIT;
p->mode.spec.nTitleLimit = DFLT_TITLE_LIMIT;
}else if( k==1 ){
p->mode.spec.nLineLimit = 0;
p->mode.spec.nCharLimit = 0;
p->mode.spec.nTitleLimit = 0;
}else{
int L, C, T = 0;
int nNum = sscanf(azArg[i], "%d,%d,%d", &L, &C, &T);
if( nNum<2 || L<0 || C<0 || T<0){
dotCmdError(p, i, "bad argument", "Should be \"L,C,T\" where L, C"
" and T are unsigned integers");
return 1;
}
p->mode.spec.nLineLimit = L;
p->mode.spec.nCharLimit = C;
if( nNum==3 ) p->mode.spec.nTitleLimit = T;
if( modeSetLimit(p, azArg[i]) ){
dotCmdError(p, i, "bad argument", "Should be \"on\" or \"off\" or "
"\"L,C,T\" where L, C, and T are unsigned integers");
return 1;
}
chng = 1;
}else if( optionMatch(z,"list") ){
@@ -9957,7 +9979,12 @@ static int do_meta_command(const char *zLine, ShellState *p){
}else
#endif /* SQLITE_SHELL_HAVE_RECOVER */
if( c=='d' && n==6 && cli_strncmp(azArg[0], "dbstat", n)==0 ){
if( c=='d' && n>=3 && cli_strncmp(azArg[0], "dbtotxt", n)==0 ){
open_db(p, 0);
rc = shell_dbtotxt_command(p, nArg, azArg);
}else
if( c=='d' && n==8 && cli_strncmp(azArg[0], "diskused", n)==0 ){
const char *zSchema = 0;
int ii;
char *zSql;
@@ -9976,23 +10003,15 @@ static int do_meta_command(const char *zLine, ShellState *p){
}
zSchema = z;
}
zSql = sqlite3_mprintf("SELECT analyze(%Q)", zSchema);
zSql = sqlite3_mprintf("SELECT diskused(%Q)", zSchema);
shell_check_oom(zSql);
modePush(p);
modeChange(p, MODE_BATCH);
p->mode.spec.nLineLimit = 0;
p->mode.spec.nCharLimit = 0;
p->mode.spec.nTitleLimit = 0;
shell_exec(p, zSql, 0);
modePop(p);
sqlite3_free(zSql);
}else
if( c=='d' && n>=3 && cli_strncmp(azArg[0], "dbtotxt", n)==0 ){
open_db(p, 0);
rc = shell_dbtotxt_command(p, nArg, azArg);
}else
if( c=='d' && cli_strncmp(azArg[0], "dump", n)==0 ){
char *zLike = 0;
char *zSql;
@@ -13352,13 +13371,13 @@ static const char zOptions[] =
" -A ARGS... run \".archive ARGS\" and exit\n"
#endif
" -append append the database to the end of the file\n"
" -ascii set output mode to 'ascii'\n"
" -ascii set '.mode ascii'\n"
" -bail stop after hitting an error\n"
" -batch force batch I/O\n"
" -box set output mode to 'box'\n"
" -box set '.mode box'\n"
" -cmd COMMAND run \"COMMAND\" before reading stdin\n"
" -column set output mode to 'column'\n"
" -csv set output mode to 'csv'\n"
" -column set '.mode column'\n"
" -csv set '.mode csv -limits off'\n"
#if !defined(SQLITE_OMIT_DESERIALIZE)
" -deserialize open the database using sqlite3_deserialize()\n"
#endif
@@ -13370,14 +13389,14 @@ static const char zOptions[] =
" -heap SIZE Size of heap for memsys3 or memsys5\n"
#endif
" -help show this message\n"
" -html set output mode to HTML\n"
" -html set '.mode html'\n"
" -ifexists only open if database already exists\n"
" -interactive force interactive I/O\n"
" -json set output mode to 'json'\n"
" -line set output mode to 'line'\n"
" -list set output mode to 'list'\n"
" -json set '.mode json'\n"
" -line set '.mode line'\n"
" -list set '.mode list'\n"
" -lookaside SIZE N use N entries of SZ bytes for lookaside memory\n"
" -markdown set output mode to 'markdown'\n"
" -markdown set '.mode markdown'\n"
#if !defined(SQLITE_OMIT_DESERIALIZE)
" -maxsize N maximum size for a --deserialize database\n"
#endif
@@ -13394,7 +13413,7 @@ static const char zOptions[] =
" -nullvalue TEXT set text string for NULL values. Default ''\n"
" -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n"
" -pcachetrace trace all page cache operations\n"
" -quote set output mode to 'quote'\n"
" -quote set '.mode quote'\n"
" -readonly open the database read-only\n"
" -safe enable safe-mode\n"
" -screenwidth N use N as the default screenwidth \n"
@@ -13403,8 +13422,8 @@ static const char zOptions[] =
" -sorterref SIZE sorter references threshold size\n"
#endif
" -stats print memory stats before each finalize\n"
" -table set output mode to 'table'\n"
" -tabs set output mode to 'tabs'\n"
" -table set '.mode table'\n"
" -tabs set '.mode tabs'\n"
" -unsafe-testing allow unsafe commands and modes for testing\n"
" -version show SQLite version\n"
" -vfs NAME use NAME as the default VFS\n"
@@ -14006,6 +14025,7 @@ int SQLITE_CDECL main(int argc, char **argv){
modeChange(&data, MODE_Box);
}else if( cli_strcmp(z,"-csv")==0 ){
modeChange(&data, MODE_Csv);
modeSetLimit(&data, "off");
}else if( cli_strcmp(z,"-escape")==0 && i+1<argc ){
/* See similar code at tag-20250224-1 */
const char *zEsc = argv[++i];
-25
View File
@@ -11137,31 +11137,6 @@ int sqlite3_snapshot_cmp(
*/
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
**
-23
View File
@@ -1336,7 +1336,6 @@ 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;
@@ -1465,14 +1464,6 @@ 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 {
sqlite3_uint64 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,7 +1679,6 @@ 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 */
@@ -1703,7 +1693,6 @@ struct sqlite3 {
u8 eOpenState; /* Current condition of the connection */
u8 nFpDigit; /* Significant digits to keep on double->text */
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 */
@@ -1816,13 +1805,6 @@ struct sqlite3 {
#endif
};
/*
** Candidate values for sqlite3.eConcurrent
*/
#define CONCURRENT_NONE 0
#define CONCURRENT_OPEN 1
#define CONCURRENT_SCHEMA 2
/*
** A macro to discover the encoding of a database.
*/
@@ -1888,9 +1870,6 @@ struct sqlite3 {
#define SQLITE_AttachWrite HI(0x00020) /* ATTACH allowed to open for write */
#define SQLITE_Comments HI(0x00040) /* Enable SQL comments */
/* Flags used by the Pragma noop_update enhancement */
#define SQLITE_NoopUpdate HI(0x0001000) /* UPDATE operations are no-ops */
/* Flags used only if debugging */
#ifdef SQLITE_DEBUG
#define SQLITE_SqlTrace HI(0x0100000) /* Debug print SQL as it executes */
@@ -5235,8 +5214,6 @@ 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);
-36
View File
@@ -8728,41 +8728,6 @@ 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
*/
@@ -9354,7 +9319,6 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "sqlite3_snapshot_cmp_blob", test_snapshot_cmp_blob, 0 },
#endif
{ "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 },
-6
View File
@@ -699,12 +699,6 @@ 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
+3
View File
@@ -90,6 +90,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
@@ -159,6 +160,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);
-11
View File
@@ -465,17 +465,6 @@ 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
/* If this is an UPDATE with a FROM clause, do not resolve expressions
** here. The call to sqlite3Select() below will do that. */
if( nChangeFrom==0 && sqlite3ResolveExprNames(&sNC, pChanges->a[i].pExpr) ){
-1
View File
@@ -412,7 +412,6 @@ end_of_vacuum:
** is closed by the DETACH.
*/
db->autoCommit = 1;
assert( db->eConcurrent==0 );
if( pDb ){
sqlite3BtreeClose(pDb->pBt);
+26 -54
View File
@@ -3950,7 +3950,6 @@ 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 = sqlite3VdbeCheckFkDeferred(p))!=SQLITE_OK ){
goto vdbe_return;
@@ -4037,31 +4036,23 @@ case OP_Savepoint: {
break;
}
/* Opcode: AutoCommit P1 P2 P3 * *
/* Opcode: AutoCommit P1 P2 * * *
**
** 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 );
@@ -4070,17 +4061,10 @@ case OP_AutoCommit: {
assert( desiredAutoCommit==1 );
sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
db->autoCommit = 1;
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. */
}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.
*/
sqlite3VdbeError(p, "cannot commit transaction - "
"SQL statements in progress");
rc = SQLITE_BUSY;
@@ -4090,16 +4074,12 @@ case OP_AutoCommit: {
}else{
db->autoCommit = (u8)desiredAutoCommit;
}
hrc = sqlite3VdbeHalt(p);
if( (hrc & 0xFF)==SQLITE_BUSY ){
if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
p->pc = (int)(pOp - aOp);
db->autoCommit = (u8)(1-desiredAutoCommit);
p->rc = hrc;
rc = SQLITE_BUSY;
p->rc = 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;
@@ -4312,17 +4292,6 @@ 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 ){
@@ -4472,11 +4441,6 @@ 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) );
@@ -8160,6 +8124,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 );
@@ -8178,16 +8143,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,
@@ -8197,7 +8171,7 @@ case OP_JournalMode: { /* out2 */
goto abort_due_to_error;
}else{
if( eOld==PAGER_JOURNALMODE_WAL ){
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
@@ -8218,7 +8192,10 @@ case OP_JournalMode: { /* out2 */
*/
assert( sqlite3BtreeTxnState(pBt)!=SQLITE_TXN_WRITE );
if( rc==SQLITE_OK ){
rc = sqlite3BtreeSetVersion(pBt, (eNew==PAGER_JOURNALMODE_WAL ? 2 : 1));
/* 1==rollback, 2==wal, 3==wal2 */
rc = sqlite3BtreeSetVersion(pBt,
1 + isWalMode(eNew) + (eNew==PAGER_JOURNALMODE_WAL2)
);
}
}
}
@@ -8355,11 +8332,6 @@ 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 );
+5 -24
View File
@@ -2966,7 +2966,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;
@@ -2979,27 +2980,10 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
assert( i!=1 );
nTrans++;
}
rc = sqlite3BtreeExclusiveLock(pBt);
rc = sqlite3PagerExclusiveLock(pPager);
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;
}
@@ -3408,7 +3392,6 @@ int sqlite3VdbeHalt(Vdbe *p){
sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
sqlite3CloseSavepoints(db);
db->autoCommit = 1;
db->eConcurrent = CONCURRENT_NONE;
p->nChange = 0;
}
}
@@ -3447,9 +3430,9 @@ int sqlite3VdbeHalt(Vdbe *p){
** is required. */
rc = vdbeCommit(db, p);
}
if( (rc & 0xFF)==SQLITE_BUSY && p->readOnly ){
if( rc==SQLITE_BUSY && p->readOnly ){
sqlite3VdbeLeave(p);
return rc;
return SQLITE_BUSY;
}else if( rc!=SQLITE_OK ){
sqlite3SystemError(db, rc);
p->rc = rc;
@@ -3477,7 +3460,6 @@ int sqlite3VdbeHalt(Vdbe *p){
sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
sqlite3CloseSavepoints(db);
db->autoCommit = 1;
db->eConcurrent = CONCURRENT_NONE;
p->nChange = 0;
}
}
@@ -3499,7 +3481,6 @@ int sqlite3VdbeHalt(Vdbe *p){
sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
sqlite3CloseSavepoints(db);
db->autoCommit = 1;
db->eConcurrent = CONCURRENT_NONE;
p->nChange = 0;
}
}
+1414 -787
View File
File diff suppressed because it is too large Load Diff
+8 -16
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(w,x,y,z) 0
# define sqlite3WalUndo(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);
int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx);
/* Return an integer that records the current (uncommitted) write
** position in the WAL */
@@ -137,15 +138,6 @@ int sqlite3WalSnapshotCheck(Wal *pWal, sqlite3_snapshot *pSnapshot);
void sqlite3WalSnapshotUnlock(Wal *pWal);
#endif
#ifndef SQLITE_OMIT_CONCURRENT
/* Tell the wal layer that we want to commit a concurrent transaction */
int sqlite3WalLockForCommit(Wal *pWal, PgHdr *pPg, Bitvec *pRead, Pgno*);
/* Upgrade the state of the client to take into account changes written
** by other connections */
int sqlite3WalUpgradeSnapshot(Wal *pWal);
#endif /* SQLITE_OMIT_CONCURRENT */
#ifdef SQLITE_ENABLE_ZIPVFS
/* If the WAL file is not empty, return the number of bytes of content
** stored in each frame (i.e. the db page-size when the WAL was created).
@@ -156,6 +148,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);
@@ -165,8 +160,5 @@ void sqlite3WalDb(Wal *pWal, sqlite3 *db);
int sqlite3WalSystemErrno(Wal*);
#endif
/* sqlite3_wal_info() data */
int sqlite3WalInfo(Wal *pWal, u32 *pnPrior, u32 *pnFrame);
#endif /* ifndef SQLITE_OMIT_WAL */
#endif /* SQLITE_WAL_H */
-556
View File
@@ -1,556 +0,0 @@
/*
** 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;
}
-127
View File
@@ -1,127 +0,0 @@
# 2015 Aug 25
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# This file contains fault injection tests designed to test the concurrent
# transactions feature.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
set testprefix concfault
# This test will not work with an in-memory journal, as the database will
# become corrupt if an error is injected into a transaction after it starts
# writing data out to the db file.
ifcapable !concurrent {
finish_test
return
}
do_test 1-pre1 {
execsql {
PRAGMA journal_mode = wal;
CREATE TABLE t1(a PRIMARY KEY, b);
INSERT INTO t1 VALUES(randomblob(1000), randomblob(100));
INSERT INTO t1 SELECT randomblob(1000), randomblob(1000) FROM t1;
INSERT INTO t1 SELECT randomblob(1000), randomblob(1000) FROM t1;
INSERT INTO t1 SELECT randomblob(1000), randomblob(1000) FROM t1;
INSERT INTO t1 SELECT randomblob(1000), randomblob(1000) FROM t1;
DELETE FROM t1 WHERE rowid%2;
}
faultsim_save_and_close
} {}
do_faultsim_test 1.1 -prep {
faultsim_restore_and_reopen
} -body {
execsql {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(randomblob(1000), randomblob(100));
COMMIT;
}
} -test {
faultsim_test_result {0 {}}
catchsql { ROLLBACK }
faultsim_integrity_check
}
do_faultsim_test 1.2 -prep {
faultsim_restore_and_reopen
} -body {
execsql {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(randomblob(1000), randomblob(100));
ROLLBACK;
}
} -test {
faultsim_test_result {0 {}}
catchsql { ROLLBACK }
faultsim_integrity_check
}
do_faultsim_test 1.3 -prep {
faultsim_restore_and_reopen
} -body {
execsql {
BEGIN CONCURRENT;
DELETE FROM t1;
COMMIT;
}
} -test {
faultsim_test_result {0 {}}
catchsql { ROLLBACK }
faultsim_integrity_check
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 2.0 {
PRAGMA auto_vacuum = 0;
PRAGMA journal_mode = wal;
CREATE TABLE t1(a PRIMARY KEY, b);
CREATE TABLE t2(a PRIMARY KEY, b);
INSERT INTO t1 VALUES(randomblob(1000), randomblob(100));
INSERT INTO t1 SELECT randomblob(1000), randomblob(1000) FROM t1;
INSERT INTO t1 SELECT randomblob(1000), randomblob(1000) FROM t1;
INSERT INTO t1 SELECT randomblob(1000), randomblob(1000) FROM t1;
INSERT INTO t1 SELECT randomblob(1000), randomblob(1000) FROM t1;
DELETE FROM t1 WHERE rowid%2;
} {wal}
faultsim_save_and_close
do_faultsim_test 1 -prep {
faultsim_restore_and_reopen
execsql {
SELECT * FROM t1;
BEGIN CONCURRENT;
INSERT INTO t2 VALUES(1, 2);
}
sqlite3 db2 test.db
execsql {
PRAGMA journal_size_limit = 10000;
INSERT INTO t1 VALUES(randomblob(1000), randomblob(1000));
} db2
db2 close
} -body {
execsql { COMMIT }
} -test {
faultsim_test_result {0 {}}
catchsql { ROLLBACK }
set res [catchsql { SELECT count(*) FROM t1 }]
if {$res!="0 9"} { error "expected {0 9} got {$res}" }
faultsim_integrity_check
}
finish_test
-688
View File
@@ -1,688 +0,0 @@
# 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
-626
View File
@@ -1,626 +0,0 @@
# 2015 July 26
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# Miscellaneous tests for transactions started with BEGIN CONCURRENT.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/wal_common.tcl
set ::testprefix concurrent2
ifcapable !concurrent {
finish_test
return
}
do_multiclient_test tn {
do_test 1.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x);
CREATE TABLE t2(y);
}
} {wal}
do_test 1.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}
# Test that an CONCURRENT transaction that allocates/frees no pages does
# not conflict with a transaction that does allocate pages.
do_test 1.$tn.2 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(4);
}
sql2 {
INSERT INTO t2 VALUES(randomblob(1500));
}
sql1 {
COMMIT;
}
} {}
do_test 1.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}
# But that an CONCURRENT transaction does conflict with a transaction
# that modifies the db schema.
do_test 1.$tn.3 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(5);
}
sql2 {
CREATE TABLE t3(z);
}
list [catch { sql1 COMMIT } msg] $msg
} {1 {database is locked}}
do_test 1.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}
# Test that an CONCURRENT transaction that allocates at least one page
# does not conflict with a transaction that allocates no pages.
do_test 1.$tn.4 {
sql1 {
ROLLBACK;
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(randomblob(1500));
}
sql2 {
INSERT INTO t2 VALUES(8);
}
sql1 {
COMMIT;
}
} {}
do_test 1.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}
}
do_multiclient_test tn {
do_test 2.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x UNIQUE);
CREATE TABLE t2(y UNIQUE);
}
} {wal}
do_test 2.$tn.2 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(randomblob(1500));
}
sql2 {
INSERT INTO t2 VALUES(randomblob(1500));
}
sql1 COMMIT
} {}
do_test 2.$tn.3 { sql3 { PRAGMA integrity_check } } {ok}
do_test 2.$tn.4 {
sql1 {
BEGIN CONCURRENT;
DELETE FROM t1;
}
sql2 {
DELETE FROM t2;
}
sql1 COMMIT
} {}
do_test 2.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}
do_test 2.$tn.6 {
sql1 {
INSERT INTO t1 VALUES(randomblob(1500));
INSERT INTO t1 VALUES(randomblob(1500));
INSERT INTO t2 VALUES(randomblob(1500));
DELETE FROM t1 WHERE rowid=1;
}
sql1 {
BEGIN CONCURRENT;
DELETE FROM t1 WHERE rowid=2;
}
sql2 {
DELETE FROM t2;
}
sql1 COMMIT
} {}
do_test 2.$tn.7 { sql3 { PRAGMA integrity_check } } {ok}
}
#-------------------------------------------------------------------------
# When an CONCURRENT transaction is opened on a database, the nFree and
# iTrunk header fields of the cached version of page 1 are both set
# to 0. This allows an CONCURRENT transaction to use its own private
# free-page-list, which is merged with the main database free-list when
# the transaction is committed.
#
# The following tests check that nFree/iTrunk are correctly restored if
# an CONCURRENT transaction is rolled back, and that savepoint rollbacks
# that occur within CONCURRENT transactions do not incorrectly restore
# these fields to their on-disk values.
#
reset_db
do_execsql_test 3.0 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x, y);
INSERT INTO t1 VALUES(randomblob(1500), randomblob(1500));
DELETE FROM t1;
} {wal}
do_execsql_test 3.1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(1, 2);
ROLLBACK;
}
do_execsql_test 3.2 { PRAGMA integrity_check } {ok}
do_execsql_test 3.3 { PRAGMA freelist_count } {2}
do_execsql_test 3.4.1 {
BEGIN CONCURRENT;
PRAGMA freelist_count;
} {2}
do_execsql_test 3.4.2 {
SAVEPOINT xyz;
INSERT INTO t1 VALUES(randomblob(1500), NULL);
PRAGMA freelist_count;
} {0}
do_execsql_test 3.4.3 {
ROLLBACK TO xyz;
} {}
do_execsql_test 3.4.4 { PRAGMA freelist_count } {0}
do_execsql_test 3.4.5 { COMMIT; PRAGMA freelist_count } {2}
do_execsql_test 3.4.6 { PRAGMA integrity_check } {ok}
do_execsql_test 3.5.1 {
BEGIN CONCURRENT;
UPDATE t1 SET x=randomblob(10) WHERE y=555;
PRAGMA freelist_count;
} {0}
do_execsql_test 3.5.2 {
ROLLBACK;
PRAGMA freelist_count;
} {2}
do_execsql_test 3.5.3 { PRAGMA integrity_check } {ok}
#-------------------------------------------------------------------------
# Test that nothing goes wrong if an CONCURRENT transaction allocates a
# page at the end of the file, frees it within the same transaction, and
# then has to move the same page to avoid a conflict on COMMIT.
#
do_multiclient_test tn {
do_test 4.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x);
CREATE TABLE t2(x);
}
} {wal}
do_test 4.$tn.2 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(randomblob(1500));
INSERT INTO t1 VALUES(randomblob(1500));
DELETE FROM t1 WHERE rowid = 1;
}
sql2 {
INSERT INTO t2 VALUES(randomblob(1500));
INSERT INTO t2 VALUES(randomblob(1500));
INSERT INTO t2 VALUES(randomblob(1500));
INSERT INTO t2 VALUES(randomblob(1500));
DELETE FROM t2 WHERE rowid IN (1, 2);
}
sql1 COMMIT
} {}
}
#-------------------------------------------------------------------------
#
do_multiclient_test tn {
do_test 5.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x);
CREATE TABLE t2(x);
INSERT INTO t1 VALUES(randomblob(1500));
PRAGMA page_count;
}
} {wal 4}
do_test 5.$tn.2 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t2 VALUES(randomblob(1500));
PRAGMA page_count;
}
} {5}
do_test 5.$tn.3 {
sql2 {
DELETE FROM t1;
PRAGMA freelist_count;
PRAGMA page_count;
}
} {1 4}
do_test 5.$tn.4 { sql1 COMMIT } {}
do_test 5.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}
}
#-------------------------------------------------------------------------
#
do_multiclient_test tn {
do_test 6.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x);
INSERT INTO t1 VALUES(randomblob(1500));
PRAGMA wal_checkpoint;
}
} {wal 0 5 5}
do_test 6.$tn.2 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(randomblob(1500));
INSERT INTO t1 VALUES(randomblob(1500));
}
} {}
do_test 6.$tn.3 {
sql2 {
BEGIN;
INSERT INTO t1 VALUES(randomblob(1500));
INSERT INTO t1 VALUES(randomblob(1500));
COMMIT;
}
} {}
do_test 6.$tn.4 {
list [catch { sql1 COMMIT } msg] $msg
} {1 {database is locked}}
do_test 6.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}
do_test 6.$tn.5 { sql3 { SELECT count(*) from t1 } } {3}
}
#-------------------------------------------------------------------------
# Test that if a corrupt wal-index-header is encountered when attempting
# to commit a CONCURRENT transaction, the transaction is not committed
# (or rolled back) and that SQLITE_BUSY_SNAPSHOT is returned to the user.
#
catch { db close }
forcedelete test.db
testvfs tvfs
sqlite3 db test.db -vfs tvfs
do_execsql_test 7.1 {
PRAGMA journal_mode = wal;
BEGIN;
CREATE TABLE t1(a, b, PRIMARY KEY(a));
INSERT INTO t1 VALUES(1, 2);
INSERT INTO t1 VALUES(3, 4);
COMMIT;
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(5, 6);
INSERT INTO t1 VALUES(7, 8);
SELECT * FROM t1;
} {wal 1 2 3 4 5 6 7 8}
# Corrupt the wal-index header
incr_tvfs_hdr test.db 11 1
do_catchsql_test 7.2.1 { COMMIT } {1 {database is locked}}
do_test 7.2.2 { sqlite3_extended_errcode db } SQLITE_BUSY_SNAPSHOT
do_execsql_test 7.3.1 {
SELECT * FROM t1;
ROLLBACK;
} {1 2 3 4 5 6 7 8}
do_execsql_test 7.3.2 {
SELECT * FROM t1;
} {1 2 3 4}
#-------------------------------------------------------------------------
# Test that "PRAGMA integrity_check" works within a concurrent
# transaction. Within a concurrent transaction, "PRAGMA integrity_check"
# is unable to detect unused database pages, but can detect other types
# of corruption.
#
reset_db
do_test 8.1 {
execsql {
PRAGMA journal_mode = wal;
CREATE TABLE kv(k INTEGER PRIMARY KEY, v UNIQUE);
INSERT INTO kv VALUES(NULL, randomblob(750));
INSERT INTO kv SELECT NULL, randomblob(750) FROM kv;
INSERT INTO kv SELECT NULL, randomblob(750) FROM kv;
INSERT INTO kv SELECT NULL, randomblob(750) FROM kv;
INSERT INTO kv SELECT NULL, randomblob(750) FROM kv;
INSERT INTO kv SELECT NULL, randomblob(750) FROM kv;
DELETE FROM kv WHERE rowid%2;
}
set v [db one {PRAGMA freelist_count}]
expr $v==33 || $v==34
} {1}
do_execsql_test 8.2 { PRAGMA integrity_check } ok
do_execsql_test 8.3 {
BEGIN CONCURRENT;
PRAGMA integrity_check;
} {ok}
do_execsql_test 8.4 {
INSERT INTO kv VALUES(1100, 1100);
PRAGMA integrity_check;
} {ok}
do_execsql_test 8.5 {
COMMIT;
PRAGMA integrity_check;
} {ok}
#-------------------------------------------------------------------------
# Test that concurrent transactions do not allow foreign-key constraints
# to be bypassed.
#
do_multiclient_test tn {
do_test 9.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE pp(i INTEGER PRIMARY KEY, j);
CREATE TABLE cc(a, b REFERENCES pp);
WITH seq(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM seq WHERE i<100)
INSERT INTO pp SELECT i, randomblob(1000) FROM seq;
PRAGMA foreign_keys = 1;
}
} {wal}
do_test 9.$tn.2.1 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO cc VALUES(42, 42);
}
} {}
do_test 9.$tn.2.2 {
sql2 { DELETE FROM pp WHERE i=42 }
list [catch { sql1 COMMIT } msg] $msg
} {1 {database is locked}}
do_test 9.$tn.2.3 {
sql1 ROLLBACK
} {}
do_test 9.$tn.3.1 {
sql1 {
PRAGMA foreign_keys = 0;
BEGIN CONCURRENT;
INSERT INTO cc VALUES(43, 43);
}
} {}
do_test 9.$tn.3.2 {
sql2 { DELETE FROM pp WHERE i=43 }
list [catch { sql1 COMMIT } msg] $msg
} {0 {}}
do_test 9.$tn.4.1 {
sql1 {
PRAGMA foreign_keys = on;
BEGIN CONCURRENT;
INSERT INTO cc VALUES(44, 44);
}
} {}
do_test 9.$tn.4.2 {
sql2 { DELETE FROM pp WHERE i=1 }
list [catch { sql1 COMMIT } msg] $msg
} {0 {}}
}
#-------------------------------------------------------------------------
# Test that even if a SELECT statement appears before all writes within
# a CONCURRENT transaction, the pages it reads are still considered when
# considering whether or not the transaction may be committed.
#
do_multiclient_test tn {
do_test 10.$tn.1.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(a);
CREATE TABLE t2(b);
CREATE TABLE t3(c);
INSERT INTO t1 VALUES(1), (2), (3);
INSERT INTO t2 VALUES(1), (2), (3);
INSERT INTO t3 VALUES(1), (2), (3);
}
} {wal}
do_test 10.$tn.1.2 {
sql1 {
BEGIN CONCURRENT;
SELECT * FROM t1;
INSERT INTO t2 VALUES(4);
}
} {1 2 3}
do_test 10.$tn.1.3 {
sql2 { INSERT INTO t1 VALUES(4) }
list [catch {sql1 COMMIT} msg] $msg
} {1 {database is locked}}
sql1 ROLLBACK
# In this case, because the "SELECT * FROM t1" is first stepped before
# the "BEGIN CONCURRENT", the pages it reads are not recorded by the
# pager object. And so the transaction can be committed. Technically
# this behaviour (the effect of an ongoing SELECT on a BEGIN CONCURRENT
# transacation) is undefined.
#
do_test 10.$tn.2.1 {
code1 {
set ::stmt [sqlite3_prepare db "SELECT * FROM t1" -1 dummy]
sqlite3_step $::stmt
}
} {SQLITE_ROW}
do_test 10.$tn.2.2 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t2 VALUES(4);
}
code1 {
set res [list]
lappend res [sqlite3_column_int $::stmt 0]
while {[sqlite3_step $::stmt]=="SQLITE_ROW"} {
lappend res [sqlite3_column_int $::stmt 0]
}
sqlite3_finalize $::stmt
set res
}
} {1 2 3 4}
do_test 10.$tn.2.3 {
sql2 { INSERT INTO t1 VALUES(5) }
sql1 COMMIT
} {}
# More tests surrounding long-lived prepared statements and concurrent
# transactions.
do_test 10.$tn.3.1 {
sql1 {
BEGIN CONCURRENT;
SELECT * FROM t1;
COMMIT;
}
sql1 {
BEGIN CONCURRENT;
INSERT INTO t2 VALUES(5);
}
sql2 {
INSERT INTO t1 VALUES(5);
}
sql1 COMMIT
sql3 {
SELECT * FROM t2;
}
} {1 2 3 4 5}
do_test 10.$tn.3.2 {
sql1 {
BEGIN CONCURRENT;
SELECT * FROM t1;
ROLLBACK;
}
sql1 {
BEGIN CONCURRENT;
INSERT INTO t2 VALUES(6);
}
sql2 {
INSERT INTO t1 VALUES(6);
}
sql1 COMMIT
sql3 { SELECT * FROM t2 }
} {1 2 3 4 5 6}
do_test 10.$tn.3.3 {
sql1 { BEGIN CONCURRENT }
code1 {
set ::stmt [sqlite3_prepare db "SELECT * FROM t1" -1 dummy]
sqlite3_step $::stmt
}
sql1 {
INSERT INTO t2 VALUES(7);
SELECT * FROM t3;
ROLLBACK;
BEGIN CONCURRENT;
}
sql2 { INSERT INTO t3 VALUES(5) }
code1 { sqlite3_finalize $::stmt }
sql1 {
INSERT INTO t2 VALUES(8);
COMMIT;
}
} {}
}
do_multiclient_test tn {
do_test 11.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(a);
}
} {wal}
do_test 11.$tn.2 {
code1 { sqlite3_wal_info db main }
} {0 2}
do_test 11.$tn.3 {
sql1 { INSERT INTO t1 VALUES(1) }
code1 { sqlite3_wal_info db main }
} {2 3}
do_test 11.$tn.4 {
sql2 { INSERT INTO t1 VALUES(2) }
code2 { sqlite3_wal_info db2 main }
} {3 4}
do_test 11.$tn.5 {
sql1 { PRAGMA wal_checkpoint }
sql2 { INSERT INTO t1 VALUES(3) }
code2 { sqlite3_wal_info db2 main }
} {0 1}
}
reset_db
do_execsql_test 12.0 {
PRAGMA journal_mode = wal;
CREATE TABLE tx(a INTEGER PRIMARY KEY, b);
} {wal}
do_test 12.1 {
for {set i 0} {$i < 50} {incr i} {
execsql {
BEGIN CONCURRENT;
INSERT INTO tx(b) VALUES( randomblob( 1200 ) );
COMMIT;
}
}
execsql { PRAGMA page_size }
} {1024}
do_execsql_test 12.2 {
DELETE FROM tx;
}
do_test 12.3 {
for {set i 0} {$i < 50} {incr i} {
execsql {
BEGIN CONCURRENT;
INSERT INTO tx(b) VALUES( randomblob( 1200 ) );
COMMIT;
}
}
execsql { PRAGMA page_size }
} {1024}
do_execsql_test 12.4 {
DELETE FROM tx;
}
do_test 12.5 {
execsql { BEGIN CONCURRENT }
for {set i 0} {$i < 5000} {incr i} {
execsql {
INSERT INTO tx(b) VALUES( randomblob( 1200 ) );
}
}
execsql { COMMIT }
execsql { PRAGMA page_size }
} {1024}
finish_test
-234
View File
@@ -1,234 +0,0 @@
# 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
View File
@@ -1,194 +0,0 @@
# 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
View File
@@ -1,278 +0,0 @@
# 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
View File
@@ -1,60 +0,0 @@
# 2017 May 26
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/wal_common.tcl
set ::testprefix concurrent6
ifcapable !concurrent {
finish_test
return
}
sqlite3 db2 test.db
do_execsql_test 1.0 {
PRAGMA page_size = 1024;
PRAGMA journal_mode = wal;
CREATE TABLE t1(x);
CREATE TABLE t2(x);
CREATE TABLE t3(x);
CREATE TABLE t4(x);
INSERT INTO t1 VALUES(zeroblob(1500));
} {wal}
do_execsql_test -db db2 1.1 {
BEGIN CONCURRENT;
INSERT INTO t3 VALUES(zeroblob(4000));
DELETE FROM t1;
}
do_execsql_test 1.2 {
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100)
INSERT INTO t2 SELECT zeroblob(1000) FROM s;
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100)
INSERT INTO t4 SELECT zeroblob(1000) FROM s;
DELETE FROM t4;
}
do_execsql_test -db db2 1.3 {
COMMIT;
}
finish_test
-52
View File
@@ -1,52 +0,0 @@
# 2018 Jan 5
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix concurrent7
sqlite3 db2 test.db
do_execsql_test 1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x);
CREATE TABLE t2(x);
} {wal}
do_execsql_test -db db2 2 {
SELECT * FROM t1;
}
do_execsql_test 3 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(randomblob(1500));
INSERT INTO t1 VALUES(randomblob(1500));
DELETE FROM t1 WHERE rowid = 1;
}
do_execsql_test -db db2 4 {
INSERT INTO t2 VALUES(randomblob(1500));
INSERT INTO t2 VALUES(randomblob(1500));
INSERT INTO t2 VALUES(randomblob(1500));
INSERT INTO t2 VALUES(randomblob(1500));
DELETE FROM t2 WHERE rowid IN (1, 2);
}
do_execsql_test 5 {
COMMIT;
PRAGMA integrity_check;
} {ok}
finish_test
-109
View File
@@ -1,109 +0,0 @@
# 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
View File
@@ -1,120 +0,0 @@
# 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
+1 -1
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
-2
View File
@@ -4398,7 +4398,6 @@ do_catchsql_test 25.4 {
INSERT INTO t1(a) SELECT randomblob(3000) FROM t2 ;
} {0 {}}
if 0 { # test incompatible with this branch due to per-connection PRNG
do_catchsql_test 25.5 {
WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x%1 FROM c WHERE x<599237)
INSERT INTO t1( a ) SELECT randomblob(3000) FROM t2 ;
@@ -4430,7 +4429,6 @@ if {$tcl_platform(byteOrder)=="littleEndian"} {
} {0 {}}
}
}
#-------------------------------------------------------------------------
reset_db
+2 -2
View File
@@ -172,11 +172,11 @@ static struct GlobalVars {
extern int sqlite3_vt02_init(sqlite3*,char**,void*);
extern int sqlite3_randomjson_init(sqlite3*,char**,void*);
extern int sqlite3_series_init(sqlite3*,char**,void*);
extern int sqlite3_analyze_init(sqlite3*,char**,void*);
extern int sqlite3_base64_init(sqlite3*,char**,void*);
extern int sqlite3_base85_init(sqlite3*,char**,void*);
extern int sqlite3_completion_init(sqlite3*,char**,void*);
extern int sqlite3_decimal_init(sqlite3*,char**,void*);
extern int sqlite3_diskused_init(sqlite3*,char**,void*);
extern int sqlite3_ieee_init(sqlite3*,char**,void*);
extern int sqlite3_regexp_init(sqlite3*,char**,void*);
extern int sqlite3_shathree_init(sqlite3*,char**,void*);
@@ -1394,11 +1394,11 @@ int runCombinedDbSqlInput(
/* Activate extensions */
sqlite3_randomjson_init(cx.db, 0, 0);
sqlite3_series_init(cx.db, 0, 0);
sqlite3_analyze_init(cx.db, 0, 0);
sqlite3_base64_init(cx.db, 0, 0);
sqlite3_base85_init(cx.db, 0, 0);
sqlite3_completion_init(cx.db, 0, 0);
sqlite3_decimal_init(cx.db, 0, 0);
sqlite3_diskused_init(cx.db, 0, 0);
sqlite3_ieee_init(cx.db, 0, 0);
sqlite3_regexp_init(cx.db, 0, 0);
sqlite3_shathree_init(cx.db, 0, 0);
-36
View File
@@ -1,36 +0,0 @@
# 2020 September 01
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix noop_update
if {[db eval {PRAGMA noop_update}]==""} {
finish_test
return
}
do_execsql_test 1.0 {
CREATE TABLE t1(x, y);
INSERT INTO t1 VALUES('a', 111);
}
do_execsql_test 1.1 {
UPDATE t1 SET y=222 WHERE x='a';
SELECT * FROM t1;
} {a 222}
do_execsql_test 1.2 {
PRAGMA noop_update = 1;
UPDATE t1 SET y=333 WHERE x='a';
SELECT * FROM t1;
} {a 222}
finish_test
+125
View File
@@ -0,0 +1,125 @@
# 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}
# 2025-07-01 https://sqlite.org/forum/forumpost/f4878de3e7dd4764
# Do not allow parse-time optimizations to omit aggregate functions,
# because doing so can change the meaning of the query.
#
unset -nocomplain zero
set zero [expr {0+0}]
do_execsql_test parser1-4.1 {
DROP TABLE IF EXISTS t1;
CREATE TABLE t1(x);
SELECT max(x) AND $zero FROM t1;
} 0
do_execsql_test parser1-4.2 {
SELECT max(x) AND 0 FROM t1;
} 0
do_execsql_test parser1-4.3 {
SELECT max(x) IN () FROM t1;
} 0
do_execsql_test parser1-4.4 {
SELECT max(x) NOT IN () FROM t1;
} 1
finish_test
+29 -9
View File
@@ -465,18 +465,21 @@ 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
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 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
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
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 {
@@ -1033,6 +1036,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]
+3 -3
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
+3 -1
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}
+2 -2
View File
@@ -58,7 +58,7 @@ if {[catch {db eval { SELECT * FROM dbstat }} msg]==0} {
}
do_test 2.1 {
catchcmd test.db "SELECT length( analyze('main') ) > 1000"
catchcmd test.db "SELECT length( diskused('main') ) > 1000"
} {0 1}
do_execsql_test 2.2 {
@@ -68,7 +68,7 @@ if {[catch {db eval { SELECT * FROM dbstat }} msg]==0} {
}
do_test 2.3 {
catchcmd test.db "SELECT length( analyze('main') ) > 1000"
catchcmd test.db "SELECT length( diskused('main') ) > 1000"
} {0 1}
}
+20 -4
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]} {
@@ -2306,17 +2307,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
}
}
-1035
View File
File diff suppressed because it is too large Load Diff
+2 -2
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
+6 -6
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
}
}
+73
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
+52
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
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
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
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
+273
View File
@@ -0,0 +1,273 @@
# 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
finish_test
+52
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
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
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
+73
View File
@@ -0,0 +1,73 @@
# 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 }
reset_prng_state
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-wal] [file size test.db-wal2]
} {5120 23088 0}
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
+543
View File
@@ -0,0 +1,543 @@
# 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}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 8.0 {
PRAGMA journal_size_limit = 10000;
PRAGMA journal_mode = wal2;
CREATE TABLE t1(x);
INSERT INTO t1 VALUES( hex( randomblob(5000) ) );
INSERT INTO t1 VALUES( hex( randomblob(5000) ) );
INSERT INTO t1 VALUES( hex( randomblob(5000) ) );
INSERT INTO t1 VALUES( hex( randomblob(5000) ) );
BEGIN;
INSERT INTO t1 VALUES( hex( randomblob(5000) ) );
} {10000 wal2}
sqlite3 db2 test.db
do_execsql_test -db db2 8.1 {
PRAGMA wal_checkpoint;
} {0 50 13}
do_execsql_test 8.2 {
COMMIT;
}
db2 close
#-------------------------------------------------------------------------
reset_db
do_execsql_test 9.0 {
PRAGMA journal_size_limit = 10000;
PRAGMA journal_mode = wal2;
CREATE TABLE t1(x);
INSERT INTO t1 VALUES( hex( randomblob(5000) ) );
INSERT INTO t1 VALUES( hex( randomblob(5000) ) );
INSERT INTO t1 VALUES( hex( randomblob(5000) ) );
INSERT INTO t1 VALUES( hex( randomblob(5000) ) );
} {10000 wal2}
do_execsql_test 9.1 {
PRAGMA wal_checkpoint;
} {0 50 13}
#-------------------------------------------------------------------------
# Check that it is possible to do a non-PASSIVE checkpoint on a wal2
# db without blocking writers.
#
reset_db
do_execsql_test 10.0 {
PRAGMA journal_size_limit = 10000;
PRAGMA journal_mode = wal2;
CREATE TABLE t1(x);
INSERT INTO t1 VALUES( hex( randomblob(5000) ) );
INSERT INTO t1 VALUES( hex( randomblob(5000) ) );
INSERT INTO t1 VALUES( hex( randomblob(5000) ) );
INSERT INTO t1 VALUES( hex( randomblob(5000) ) );
BEGIN;
INSERT INTO t1 VALUES( hex( randomblob(5000) ) );
} {10000 wal2}
sqlite3 db2 test.db
do_execsql_test -db db2 10.1 {
PRAGMA wal_checkpoint = FULL;
} {0 50 13}
do_execsql_test 10.2 {
COMMIT;
}
finish_test
+93
View File
@@ -0,0 +1,93 @@
# 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
+5 -8
View File
@@ -62,27 +62,24 @@ do_execsql_test 1.0 {
INSERT INTO t1 SELECT NULL, hex(myrandomblob(64)) FROM s;
} {0 wal}
unset -nocomplain N
set N [lindex [db eval {PRAGMA wal_checkpoint = noop}] 1]
do_execsql_test 1.1 {
PRAGMA wal_checkpoint = noop;
} [list 0 $N 0]
} {0 298 0}
do_execsql_test 1.2 {
PRAGMA wal_checkpoint = noop;
} [list 0 $N 0]
} {0 298 0}
do_execsql_test 1.3 {
PRAGMA wal_checkpoint = passive;
} [list 0 $N $N]
} {0 298 298}
do_execsql_test 1.4 {
PRAGMA wal_checkpoint = noop;
} [list 0 $N $N]
} {0 298 298}
db_save_and_close
db_restore_and_reopen
do_execsql_test 1.5 {
PRAGMA wal_checkpoint = noop;
} [list 0 $N 0]
} {0 298 0}
db close
sqlite3 db test.db
+1 -1
View File
@@ -85,7 +85,7 @@ proc lock_callback {method filename handle lock} {
db2 eval { INSERT INTO x VALUES('x') }
}
}
db timeout 10
db timeout 1100
do_catchsql_test 2.4 {
BEGIN EXCLUSIVE;
} {0 {}}
+2
View File
@@ -395,6 +395,8 @@ set options(THREADSAFE) {
#endif
}
set options(WAL2) { "WAL2", }
proc trim_name {in} {
set ret $in
if {[string range $in 0 6]=="SQLITE_"} {
-6
View File
@@ -115,12 +115,6 @@ set pragma_def {
IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
IF: defined(SQLITE_DEBUG)
NAME: noop_update
TYPE: FLAG
ARG: SQLITE_NoopUpdate
IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
IF: defined(SQLITE_ENABLE_NOOP_UPDATE)
NAME: ignore_check_constraints
TYPE: FLAG
ARG: SQLITE_IgnoreChecks