Compare commits

...

319 Commits

Author SHA1 Message Date
dan
d9d80f5924 Optimize deferred allocation of pages on this branch by avoiding BTALLOC_LE.
FossilOrigin-Name: 0812161025272851af6233f183a6abdee5c583decc688c4763965b50e7ab1b9d
2024-11-11 18:11:34 +00:00
dan
cdc3aae877 Update this branch with latest changes and build system updates from trunk.
FossilOrigin-Name: 52bd7953f1ff26942e6e4227dd112d8454f60a98ef1c5f62ef123085c769069a
2024-11-11 17:03:07 +00:00
drh
43dbb88608 Merge the latest trunk enhancements and especially the new autosetup changes
from trunk into the begin-concurrent branch.

FossilOrigin-Name: af43791d471c8d09df5cccb413ebcbfcaa234f0ab3d101b10facd32ef1f08a51
2024-10-25 19:32:29 +00:00
drh
c44447f728 Update the begin-concurrent branch to version-3.47.0
FossilOrigin-Name: 457bb035e3c8e064f0f9084da300595248d1fcaabb2222f46dac45278cc7b7b9
2024-10-21 17:00:22 +00:00
drh
ca048854a6 Merge the latest trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 92cd032aa9558ad9ac311b82899ca49c703d0c8d376412ae252f2a3fab71476e
2024-10-16 14:52:14 +00:00
drh
a07fd90b05 Merge all the latest trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 1bf073d7c01b84626bf62dc7c10dfd8f7c33eca065d6f65cc3afd4f8a19c1db8
2024-09-30 12:39:26 +00:00
drh
ea83b3a2b9 Merge all the latest trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 2a87a969e790376e58ab21c986222a34fe564bdbacb3b17fca4e5027a36463f5
2024-09-06 15:39:23 +00:00
drh
ec23306ae1 Sync the amalgamation makefile for MSVC with the main makefile.
FossilOrigin-Name: fcc4c3b4844c8f6babc0b97a4b7677d04a004b1ee8f319867f421c05e10d849f
2024-08-29 23:26:21 +00:00
drh
3e690f3d8f Merge all the latest trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: b05d30bec1c9dd51526fcf81bd9faed4e9531a82fe675e4aac9de113188a37eb
2024-08-29 23:24:32 +00:00
dan
d61eef9599 Remove two unnecessary int casts, as reported in [forum:84fe63b7b3|forum post 84fe63b7b3].
FossilOrigin-Name: 137cc3e9e8b080678868da34e61f65faf5feb19f146bb7789c659bcdb8f5e950
2024-08-09 10:37:24 +00:00
drh
19a9f9d905 Merge all the latest trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 3b131bd9aaca63f4b97d5b40a3b0d3e91b38a22c05efcb3aebf458ec494a2887
2024-08-01 00:02:23 +00:00
dan
357e2fee7c Fix another problem with the sqlite3_log() message identifying the table or index that a conflicting page belongs to.
FossilOrigin-Name: d033d9435007c7753d48f019626cf22c82569f93506a7e0158a09d5c4274baae
2024-05-28 18:35:12 +00:00
drh
f4a38e3633 Version 3.46.0 for the begin-concurrent branch
FossilOrigin-Name: e3f8c70ef5a7349c0ebcb807b3ec4ad1f228f8afce1122889571fbf7471e68e4
2024-05-23 14:04:16 +00:00
drh
3190bb8667 Merge the latest trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 5c1c8651d122f811dfc3dd08007214c1b38e1229b13b3a8a50a8753ef3767988
2024-05-21 15:17:13 +00:00
drh
7611de6597 Merge the latest trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: eedf6bed2abdaa8a245a4871f9455e3660a7dfeb3bd5f2631d3305cb41591aeb
2024-04-15 14:36:02 +00:00
drh
e66ad6c3ea Merge the latest trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 4ff83342415db6c1a55f877481df77dbc671673ff46bd0afa1826481bb67380d
2024-04-05 14:46:00 +00:00
dan
d4dc4ac6da Fix handling of an OOM case broken by [49263c91].
FossilOrigin-Name: 9e6b64decb42dfebd3b882fd93f3bbfec11eca83b754514cc72c90616ca6f9dd
2024-03-29 18:16:16 +00:00
dan
ad7341c0f2 Fix a problem in SQLITE_DIRECT_OVERFLOW_READ builds that could allow a concurrent transaction to be committed even if it read from an overflow page that was modified concurrently, in cases where the overflow page was written without also writing the b-tree page to which it is linked.
FossilOrigin-Name: 49263c9136c81638833aa71c9d590e318ead2ca60c4d7207ebf8884174df9c8f
2024-03-29 17:58:51 +00:00
dan
cd57479c6b Improve the log message emitted when a BEGIN CONCURRENT transaction cannot be committed due to conflicts so that it identifies the conflicting table in a few more cases.
FossilOrigin-Name: 5d30e362cf72da3e17663dcb4299047ebe797ab6054fb14b2150ba82c2e698e1
2024-03-29 17:32:00 +00:00
drh
7baebbadd1 Merge all recent trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 9b854e54b0d32542fd9b360319a784522c6a80190565094b74b2ab2c37efa14f
2024-03-26 10:42:28 +00:00
drh
5fc257aade Merge the latest trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 50b43b02da3cabb136c7a11e0d36e93b270f07b05359f52c0b9d03ff54f021c1
2024-03-18 18:47:56 +00:00
drh
5c833b6808 Merge the latest trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 238fa84a9a0351ca353fed63592bbfac5fd417dc395a433233a9199abd56a81f
2024-03-13 17:51:36 +00:00
dan
9d23e88a99 Fix a problem with the sqlite3_log() message identifying the table or index that a conflicting page belongs to.
FossilOrigin-Name: d6c629f1655a207a511444680c3e89089b4e9ee30a699f396549324d5c4a238e
2024-02-16 11:03:07 +00:00
dan
d332b91d46 Merge latest trunk changes into this branch.
FossilOrigin-Name: 95bf4bc2e2cc1e7489d82c68deb68feefbdc34fec1baeb9bb8a92ff1063b806c
2024-01-13 19:57:37 +00:00
drh
c8e6fe0cde Merge the latest trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: c407d3bb9979933d7f5418ac2baa238b1f13c1aa769bf452c5c33088fdb1e959
2024-01-04 15:49:36 +00:00
drh
ba3e9ba673 Merge recent trunk enhancements into the begin-concurrrent branch.
FossilOrigin-Name: b3a2adfda722b025f43a105300e1c9860e7586eee5a77f548c4581a7be49518b
2023-12-28 13:40:07 +00:00
drh
a839ddd6e8 Merge the latest trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: b1c58f485f9ef64692230e8dc8f4fc1997ab1b8eb12a0735875cfcd9e4702860
2023-12-06 21:05:13 +00:00
dan
9c3c839861 Update this branch with latest changes from trunk.
FossilOrigin-Name: bead0d8f5a19f38aba0a9251a7c5dc6dcd5b2f31e5665e6b575188c600f31c6f
2023-11-28 17:47:47 +00:00
drh
dba104de4a Version 3.44.0 for the begin-concurrent branch.
FossilOrigin-Name: 291d6b44035c14647f5c8e2f264fd33ed6a9d969ae357fe3f74a9faf3d62faff
2023-11-01 12:38:28 +00:00
drh
925d2e136a Merge the latest trunk changes into the begin-concurrent branch.
FossilOrigin-Name: 9fa405eb5aa786e7d7f5ee5a9ccf39adcc1dc25552da3a344c443367dc5881a3
2023-10-30 21:39:11 +00:00
dan
75e1099a72 Merge latest trunk changes into this branch.
FossilOrigin-Name: c5ef898a79c74d313e23be130d687675bda72925fb78470f147ba4ec82d52aeb
2023-10-23 15:48:44 +00:00
drh
44484fa927 Version 3.43.0 for the begin-concurrent branch.
FossilOrigin-Name: e67bf282459eb089c736cf6de43e1dbe78fa42c72c7e226e09a2d53a87dc0d24
2023-08-24 13:17:59 +00:00
drh
41ec231715 Merge the latest trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 76a99a2c408658d950a4d99c04a2a4267a946b482e996aa07d8f1b76af0228bf
2023-08-21 18:03:41 +00:00
dan
ef79b85811 Fix problems related to structured-exception-handling on this branch.
FossilOrigin-Name: dd86c2729bdd0ed9646d4b1e2b90dcd40b8dce72dd33f4c1279c08b2a51675d3
2023-08-21 17:49:54 +00:00
dan
1da0e9fdec Fix MSVC testfixture.exe builds.
FossilOrigin-Name: 7004c9027e3c318513e62320f2f40839bd5c6a4b7647f6a8977d6b67ecfe90e0
2023-08-21 17:26:36 +00:00
dan
9a090940bd Merge latest trunk changes, including support for handling of structured-exceptions in MSVC builds, with this branch.
FossilOrigin-Name: cb3cf9bab0a75260c683454fb6c677f34b6c4b3555af2494678fd4811f586ac3
2023-08-11 19:38:35 +00:00
dan
fb5a69072b Fix a problem with handling savepoints in BEGIN CONCURRENT transactions.
FossilOrigin-Name: 4a5c0439ae3ce6c98cea26f7e96c5bce543e76b9ad2a3a275601d6e5060899c8
2023-08-11 18:25:26 +00:00
drh
78513226f8 Merge the latest trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 7cb2b4e35ff901cd597e2bffbb50f25f0445beba0c994de775580876da74ed10
2023-08-11 11:34:42 +00:00
drh
888a6145d6 Merge the latest trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 7407dda8b4c704cff665ba06108ea33c4099ff6446357020e5e8ecad0c86ed57
2023-07-28 18:43:28 +00:00
drh
2be88f2f0a Merge the latest trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 967be041ea97ad1e96f46e18330e6477fdda125c83345f15e7c5502a41432c57
2023-07-24 13:15:05 +00:00
drh
bb31923805 Merge the latest trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 3d0823c48eb8d91b747e896ee416da22f60456f14de51ff5cf31657d94c343f3
2023-07-08 19:00:34 +00:00
drh
e64de298da Merge recent trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 5bd26fea6a0720897a6c8384249b1e41c1838ef98f8f48d463d0cdde631477b9
2023-06-22 13:28:30 +00:00
drh
f5c321d9d7 Merge all 3.42.0 release changes into the begin-concurrent branch.
FossilOrigin-Name: 804bb138f536f64bfa397eed5e7223e34945cff97894633b26fa677818bf6fcd
2023-05-16 12:56:38 +00:00
drh
a25989993e Merge the latest 3.42.0 changes from trunk into the begin-concurrent branch.
FossilOrigin-Name: 18c5e16b55ac63b92c1516f387cf9f5625156027cc23dc230d14afd778357203
2023-05-13 15:17:24 +00:00
dan
bd0adc6326 Fix a problem in test script changebatch1.test.
FossilOrigin-Name: 98ecffdfb62fbafcf3bc5b9b2f04dd10820023d61ddf3a4c2ca4b28c23876136
2023-05-03 10:56:21 +00:00
dan
1890c21aa6 Update this branch with latest changes from trunk.
FossilOrigin-Name: 6f22c7679f235ffe22cd59bb780396b2285b352e3a1abc2c3fa7092bf54c69b6
2023-05-02 21:10:14 +00:00
drh
adadaee4ac Update the begin-concurrent branch to version 3.41.0
FossilOrigin-Name: 918aea50ed23191df4b2fa94c633bf76d11dcd7357a68090106c1cdc4a8ad669
2023-02-21 21:19:26 +00:00
drh
b63552c4a1 Merge the latest trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 6fb8bb2e8f486a1e0cd65481737fc10ad89aa47e30219ff1846fd38688eb59d1
2023-02-18 21:35:14 +00:00
drh
3d8910436b Merge the latest trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 46e79167b9426c33c016cd0f9f10cbb66dd78f602ad3144fd2ae1575b16d22d5
2023-02-06 17:35:43 +00:00
dan
d157dda00b Fix a startup race condition that could occur if a wal file grows from 0 bytes while a BEGIN CONCURRENT transaction is running.
FossilOrigin-Name: c1feca4ddaa93fa1cf75e8df061967995a18d726a23f4006d239d3e03c8570df
2023-01-12 15:30:02 +00:00
drh
43fa1a5e08 Update the begin-concurrent branch with the latest enhancements on trunk.
FossilOrigin-Name: 0c198aee53d35cf0999f880c433140c3dfc6bd620c30920b08d7534c0f905e05
2022-12-21 20:07:58 +00:00
drh
5615eac7d0 Merge 3.40.0 into the begin-concurrent branch.
FossilOrigin-Name: a06d57ee9e2862b2c67dc97e3702c00339341ba308d6558b7694e5b9e47231ac
2022-11-16 15:59:15 +00:00
drh
8b67214ff1 Merge 3.40.0 rc1 into the begin-concurrent branch.
FossilOrigin-Name: 5ba588e7eb04205aac134061c7c33c844c8b8ac33ff2ead3f53c59ae52afdc2e
2022-11-14 12:44:31 +00:00
drh
060e0b2f04 Merge the latest trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: b361ee983ca45e8ede70413c549d50d20c69a055f387c5ce749da9600374921b
2022-11-04 18:33:52 +00:00
drh
e5b707dfed Merge all recent trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: ba2539f6dee1fff4a7603554da2d91be76f8af3c523e113a7629c2dd31dacd72
2022-09-30 13:29:27 +00:00
drh
24d9189b59 Merge all recent trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 7eac6136ab7fbb96c4bc2ba219191b2273a6fc0eca929bb0f12f666f7b1d12ab
2022-08-22 15:42:15 +00:00
drh
cf5696ff0b Merge version 3.39.0 into the begin concurrent branch.
FossilOrigin-Name: fb45624a319ed56268749d584839fa0a3c35d331513ca41451438dda988f9df8
2022-06-25 16:24:45 +00:00
drh
3ab59dd981 Merge 3.39.0 release candidate 1 changes into the begin-concurrent branch.
FossilOrigin-Name: 5ae706a401224581e370b934aa32fd8001f6597b6b38c45a75f13ed0c72b9118
2022-06-24 14:55:19 +00:00
drh
137c1b48d5 Merge the latest trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 221e07ed783b878c0148feb57aaee0b80ee5567d9176b3ddb8dd853e02a9d08b
2022-06-16 13:37:36 +00:00
drh
ac00efc996 Merge the latest trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: d6f6ee5cbcd9b263bc2423ce90700ec238deea212d32888fa9302fa2479b7d99
2022-05-28 14:25:10 +00:00
drh
472e709136 Merge recent trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: f65bd76760320754652311adfd45e27fecaea4e5077b3c1eb59aecbf65b24ecd
2022-05-10 12:00:36 +00:00
drh
9077e4652f Merge the latest trunk enhancements, including the 3.38.1 patches plus
additional performance enhancements, into the begin-concurrent branch.

FossilOrigin-Name: aa8b5e9558c7cd3e7e3e4cb9f7828dec6f3e0d89bc91a974e4891e75cf8c953d
2022-03-12 14:47:32 +00:00
drh
9783c82de2 Merge version 3.38.0 into the begin-concurrent branch.
FossilOrigin-Name: c5998c43c160d1c84624a516e5c1c91a365fedea1082dc02589d04495ef090cf
2022-02-22 19:30:14 +00:00
drh
a294d7f208 Merge the latest trunk changes into the begin-concurrent branch.
FossilOrigin-Name: 94838f16baf810054926196f277be4688359c502cb7e6872e3e3273a9c67e56d
2022-02-15 20:18:11 +00:00
drh
0ca5fd6f50 Merge trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 85054a8691cec593b0490b78715a506f25a8cbe04cbf673129c0e17f7f5fa912
2022-02-04 17:40:59 +00:00
drh
a819ff7c54 Merge the latest trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: dae81f45d2bee8e5bced2564a6a85ffb23dc93fce6547760cd811acddbc09e25
2022-01-25 15:20:16 +00:00
drh
a58abfe7db Merge recent trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 02daae7a67927e5e6188e16219a453e7fa1b27cface6616171dd7cde717aca51
2022-01-17 19:17:02 +00:00
drh
02369f514d Merge the begin-concurrent-pnuu branch into begin-concurrent.
FossilOrigin-Name: 22fb50500505462baa0a8baa9f6dad6140c451429e420efd304cf8bbe35bdd69
2022-01-17 19:10:25 +00:00
drh
bfc3e65a04 Merge version 3.37.0 into the begin-concurrent-pnu branch.
FossilOrigin-Name: 910001f28368896c3e6f11bb6ced3ec3171dcafefd9e6702106b32f3c451e229
2021-11-27 14:48:36 +00:00
drh
55d070bae8 Merge version 3.37.0 into the begin-concurrent branch.
FossilOrigin-Name: c5f2ad19423785d5d6fc2a41f232fee4bdc1a489e548fbc4d874578f45533812
2021-11-27 14:45:54 +00:00
drh
5d0ce656d4 Merge the latest trunk enhancements into the begin-concurrent-pnu branch.
FossilOrigin-Name: aa045e7b09ee47fb281579178add904fffc2299b05cfc67cf872ec3caaffd69b
2021-11-22 16:27:23 +00:00
drh
5954108ab1 Merge the latest trunk changes into the begin-concurrent branch.
FossilOrigin-Name: 8f3e68424ae8dfffd01312b178376f3666a28995145c6f4028d8c71a7da1b93a
2021-11-22 16:14:33 +00:00
drh
3c3a5d4ada Merge all recent trunk enhancements into the begin-concurrent-pnu branch.
FossilOrigin-Name: b9802d0b479ee910b7f50ea7995776040ec183ecbdc44fc00086154fb819ef12
2021-11-09 15:26:28 +00:00
drh
08fc368485 Merge all recent trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 2d0c8f3099819ab7336d91a2dcaef927220f3e854d092b5662784bd19ad23aa2
2021-11-09 15:06:07 +00:00
drh
6a608be78c Merge the walIndexPage() enhancement from trunk into the begin-concurrent
branch.

FossilOrigin-Name: ff2238397f78a3c276fa96f2a9cafb6f99c8e7eeaa69cf513a9f7b7581778ec5
2021-11-09 14:52:22 +00:00
drh
5f9b5fcbcc Merge all trunk enhancements prior to the walIndexPage() fix into the
begin-concurrent branch.

FossilOrigin-Name: 6603f00581ed29b3fa9e10f22357a8e5154934c0df4737d7b6b6c0f8d4f22a80
2021-11-09 14:36:58 +00:00
drh
fc9f56c453 Merge trunk enhancements into begin-concurrent-pnu branch.
FossilOrigin-Name: 4d972744c3d76254540837f0339731f4e62601cee924320aa3f3c0de7ee97ff3
2021-10-21 14:27:00 +00:00
drh
59b15e7b0c Merge recent trunk enhancements into begin-concurrent branch.
FossilOrigin-Name: ae4eed0b7eccbba0ea91ae209fd94a9aefad41d57c1e5e2d07a796fc7b4f17db
2021-10-21 14:13:49 +00:00
drh
9826456ab0 Merge recent trunk enhancements into begin-concurrent-pnu branch.
FossilOrigin-Name: 35ad7bc6e0b31d838a80451300123e160c4a9367a3ea600f01e515a8135563ef
2021-10-04 12:09:44 +00:00
drh
6a25ed176c Merge recent trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 4f5ef7aa4cfeb3acb6f169ffe6bfca64e1e0658e2d8e222e01a0d3902ac89471
2021-10-04 11:54:57 +00:00
drh
945e5d112e Merge recent trunk enhancements into the begin-concurrent-pnu branch.
FossilOrigin-Name: 514b3cef6b7403cde90dcc926d67355ad6340b79b6a1b06004d97bebfc4eaced
2021-08-09 19:33:21 +00:00
drh
9ba15fdb8f Merge recent trunk enhancemenets into the begin-concurrent branch.
FossilOrigin-Name: b88b6328304640f27ba457c6812d3b1dac3d059ce07089e0f95afb2aa9cec528
2021-08-09 18:21:59 +00:00
drh
adcbc76bb6 Merge recent trunk changes into the begin-concurrent-pnu branch.
FossilOrigin-Name: 2c25f25b22fe8e9c0da8a6ff5f2f117bd1716900c03acc8ed3ee8fce2286e15b
2021-08-03 16:49:31 +00:00
drh
db7959d111 Merge recent trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 8d806cf93c643007e2d2b0d547bf896e8715fff4ed37cd5d0bf45a4a8618f4e7
2021-08-03 16:31:54 +00:00
drh
f0629994ab Merge recent trunk enhancements into the begin-concurrent-pnu branch.
FossilOrigin-Name: 229107b196abe5ab458ce84361d4b1e40d9d077effab7ebe9caac475fb96f7c0
2021-06-17 17:30:18 +00:00
drh
7d6837fc94 Merge recent trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 59303e69c3de3fb4e64d4db2b25bf4fcd30dd1332781bff70f710dc01c5aa25c
2021-06-17 17:29:56 +00:00
dan
ab8ad8dde3 Update this branch with latest changes from trunk.
FossilOrigin-Name: 74b42754d075a91e8f7d9067a3fcd5c60af03b520984fb3f8bb0c51faa376857
2021-06-14 13:49:11 +00:00
dan
850853be76 Update this branch with latest trunk changes.
FossilOrigin-Name: d81996340ae4b25a534dd921117ee458565812ea493ab9f685fbd5541f7864a9
2021-06-14 12:23:21 +00:00
drh
7a5d37f540 Merge recent trunk changes into the begin-concurrent-pnu branch.
FossilOrigin-Name: a8d05f8e92b13bb5c150c1e3e7cf861d15bc7d50149ebb83646a2376cbfbd52b
2021-05-21 16:17:42 +00:00
drh
95003feeb2 Merge all recent trunk changes into the begin-concurrent branch.
FossilOrigin-Name: 0dba9010ad29e83d726f3c63e21baabc4185e1966a5eeb8bc4447c5199681436
2021-05-21 15:33:49 +00:00
dan
5b2b13c8c2 Merge latest trunk changes into this branch.
FossilOrigin-Name: 1f3f7e4bf3f16eeb68261c06b14201e445a8544d54505cef57a24d7ab0e93191
2021-04-07 17:32:39 +00:00
dan
b6c8623045 Merge latest trunk changes into this branch.
FossilOrigin-Name: a7e612ee8fd942cf766e5304912161ee0b76ea21f32c7b7a72e048b73ed2a9e4
2021-04-02 20:40:52 +00:00
dan
684c1705c9 Merge latest trunk changes into this branch.
FossilOrigin-Name: 9842084230b8a0a4e0a470b6fdacfe2ac5d36f4272b8d1141e0d3b7b66ae6d6d
2021-04-02 20:29:38 +00:00
drh
cb2ec5c3fd Merge version 3.35.3 changes into the begin-concurrent-pnu branch.
FossilOrigin-Name: 7e4c48afcc662940afe3ad976fcd75d71150e66461ad2d09c103eb0e238674a3
2021-03-26 13:38:36 +00:00
drh
e09460ea13 Merge from 3.35.3 into the begin-concurrent branch.
FossilOrigin-Name: 988da36c862f187aea2411520c127545e0fff34143d5cd0e61c928b4972fce64
2021-03-26 13:31:18 +00:00
drh
05c3446396 Merge the 3.35.0 release into the begin-concurrent-pnu branch.
FossilOrigin-Name: a0a9dd52a7b514a8b919d67650178c76167cdbb6e23523222c3df709953b0f25
2021-03-12 16:24:43 +00:00
drh
99320d2dd8 Merge version 3.35.0 changes into the begin-concurrent branch.
FossilOrigin-Name: 7e38c889e7eece521db24c379cb1be7c4c49e2ba4dc04c2a10151793a6bb04df
2021-03-12 16:18:00 +00:00
dan
6bfb2e062e Merge latest trunk changes with this branch.
FossilOrigin-Name: eb3101364faabb49d180f74c795e4f1430541937fd19ce95205618c98324c797
2021-03-11 19:08:44 +00:00
dan
2492eda559 Update this branch with latest changes from trunk.
FossilOrigin-Name: 79d44ebd5313fe261d7b51826deb119ac6d46388f955457c020ce623b9a3e53d
2021-03-11 18:52:12 +00:00
drh
fb9d9cc7c4 Merge recent trunk enhancemenets into the begin-concurrent-pnu branch.
FossilOrigin-Name: 59d3ec295a6e640886cdccd49bd4522af29b37220fb9c99e8e39552ea2cbcdcb
2021-03-03 19:25:45 +00:00
drh
09f78fff30 Merge recent trunk changes into the begin-concurrent branch.
FossilOrigin-Name: acdafef836605ba2079966abde1fa40c43995b6a049d84f631e613605cabda3d
2021-03-03 19:17:25 +00:00
drh
612beb5e38 Merge the latest trunk enhancements into the begin-concurrent-pnu branch.
FossilOrigin-Name: daf04650213c39804370924f6a724c816c154c33a3b2ae306bb5ed5fb14a855b
2020-12-14 16:50:45 +00:00
drh
c9cb469013 Merge recent trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: a1708e845772237ca95be5ec44ad1a43b4abeeb25ba8ae6188f0c56d09e5f062
2020-12-14 16:26:52 +00:00
dan
a240942122 Merge latest changes from trunk into this branch.
FossilOrigin-Name: a42e6f34f560d19df7e3835718c697ea19f905c3d2ee1bbd83ed5f2a5c36d965
2020-11-26 17:36:42 +00:00
dan
a905c73baf Update this branch with latest changes from trunk.
FossilOrigin-Name: ddb97eeef7598dc16dad0ad6bd512d0da22275d0c993018d258ef1bed347074e
2020-11-26 11:37:54 +00:00
dan
5e9d4c221d Restore the "PRAGMA noop_update" feature that was broken by [2b3241cf67c8eea7].
FossilOrigin-Name: 5fa4cbcb785125e72c7fd5cc2050637f2a2e854ee22aee05e8f09f751cd36291
2020-08-31 20:27:44 +00:00
drh
23dbdf125c Merge recent trunk fixes into the begin-concurrent-pnu branch.
FossilOrigin-Name: 2ae78c993e6a4ffd9ac1c8aaadf4976eacb5486e23c228988e9d3f379ed801c3
2020-08-10 21:30:17 +00:00
drh
7e4f836942 Merge recent trunk enhancements into begin-concurrent.
FossilOrigin-Name: ed4c742c4e7789d19a1ec56a7642389966f6334fffaac4c45b1fea3046f0b478
2020-08-10 21:16:29 +00:00
drh
94281e049f Improved non-locking PRNG.
FossilOrigin-Name: eb7a9c04bdbb297938672526d8c86b5666fb0f9841b9ddc2d9002399301cf665
2020-07-31 20:56:15 +00:00
dan
bd14b600fa Merge latest trunk change into this branch.
FossilOrigin-Name: 2b3241cf67c8eea761760ac27df7a136bcca2dca10c152a36b216c5dc88cdb53
2020-07-30 19:37:38 +00:00
dan
1098bdb8e5 Merge latest trunk changes into this branch.
FossilOrigin-Name: e8a6651539a1967185ef7a77f29475fc97c1b600dd050caa793777ed4b69aa94
2020-07-30 19:19:12 +00:00
dan
3dc850321e Allow BEGIN CONCURRENT transactions to contain schema modifications. However, this causes them to conflict with all other transactions.
FossilOrigin-Name: e9393a18cb987d258fff56f80ad6b1525f124fb19e8e4a9c953b86a57ef9a7e6
2020-07-16 20:39:59 +00:00
dan
9a47771710 Allow BEGIN CONCURRENT transactions to contain schema modifications. However, this causes them to conflict with all other transactions.
FossilOrigin-Name: 9a4a02304e3f5dc1d567cf2a9eae4cdb575e02cb37f9ee25928f2325ab192e76
2020-07-16 20:24:11 +00:00
drh
5a344ef67c Merge versoin 3.32.1 into the begin-concurrent-pnu branch.
FossilOrigin-Name: e8d79d2bae50d7443ea6b7274ca36ded4f64e0f540494651d705612474f9aeb1
2020-05-25 18:03:34 +00:00
drh
8efab83cf8 Merge version 3.32.1 into the begin-concurrent branch.
FossilOrigin-Name: 5e08e36067c509360040638cd0c6c79ed1eb105c492c7e72af660bd355da4fba
2020-05-25 17:27:31 +00:00
drh
e3e03777c7 Merge the 3.32.0 release into the begin-concurrent-pnu branch.
FossilOrigin-Name: 79663e66129f9522c4c2287d095129c7989f5bee41f2073f7c2c60ca5739f2f6
2020-05-22 18:17:06 +00:00
drh
fcf3e8076d Merge the 3.32.0 release into the begin-concurrent branch.
FossilOrigin-Name: 79ad4ea2c8e7f4c90983d78a9a6ebb6a70a057124d708076a0bbf0db8696cdaa
2020-05-22 18:09:51 +00:00
drh
27fab1c045 Merge recent changes from trunk.
FossilOrigin-Name: c69f030b1ed9bddcc9ea79fe2361f39901bf1461ba3efdb32ddabcaa44319b2a
2020-05-18 16:19:59 +00:00
dan
cf7c1bfc9b Merge latest trunk changes into this branch.
FossilOrigin-Name: 9e4a7b3ed8c699c59524cc3c0d20c05f8e33e220cfa901f3b9300f75a7276673
2020-05-18 15:41:31 +00:00
drh
cd35fbc9ca Merge recent trunk enhancements into the begin-concurrent-pnu branch.
FossilOrigin-Name: cedd138c749b777de0479a9e57aa9fe4f21cb99660abdc78c14e61afdef4f2f3
2020-04-09 18:44:15 +00:00
drh
af77b082e8 Merge recent trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 92f71a88c4aea990710b94f6f35c8c9a02cfdbc2f5ae01cbf62c40d2d35d6c5b
2020-04-09 18:29:11 +00:00
dan
f17089eb01 Fix a typo causing SQLITE_NOOP_UPDATE builds to fail.
FossilOrigin-Name: 1e2109fd339cfb3b906673479672ed2e5a0dc17a94210f364c6fc1a657a62ae8
2020-03-13 07:23:20 +00:00
drh
98424ffbea Fix a typo in the in-tree begin-concurrent documentation
FossilOrigin-Name: b13dae1cb3f9e20b6dcd697b1d11316e88b6c2d7c8dfe1aebc80ad88827dc887
2020-02-10 19:37:20 +00:00
dan
ab18639258 Merge latest trunk changes, including the SQLITE_ENABLE_CORRUPT_PGNO patch, into this branch.
FossilOrigin-Name: 23bc80cc8681762e42376e5d878fe60ccc9a6533b7d017e54280996ea363c3f5
2020-02-04 20:09:45 +00:00
dan
70de00f144 Merge latest trunk changes, including the SQLITE_ENABLE_CORRUPT_PGNO patch, into this branch.
FossilOrigin-Name: f253618ac6b7d8dbc6f14c5c243b45c1e2542e51cc2c176c03ebb0979a36e5fd
2020-02-04 20:08:19 +00:00
drh
d4a1c953a1 Merge version 3.31.0
FossilOrigin-Name: e65e6c01d632beec9a21758cce6f7ac2e3bc2c5fac88b3bb393b9040fb2cbea6
2020-01-22 21:06:17 +00:00
drh
36832d61d8 Merge version 3.31.0
FossilOrigin-Name: bbbc2881b9e47988ca2b5fcab2b399823c64dac00071cfc7083636e9ee0d2f50
2020-01-22 20:56:08 +00:00
drh
93b95c6c84 Merge all recent enhancements and fixes from trunk.
FossilOrigin-Name: 110a081f12ec83870bd42efe69363ca6c10d7d550c950008b40848f030919ecb
2020-01-15 13:48:19 +00:00
drh
58f6bb447e Merge fixes and enhancements from trunk into begin-concurrent.
FossilOrigin-Name: 95ba2f50fdfd3527e8211f8789a4365677be6daff08b951a6175a8892c879a1f
2020-01-15 13:34:17 +00:00
drh
c697613beb Bring the begin-concurrent-pnu branch up-to-date with version 3.30.0.
FossilOrigin-Name: a4d26bcfb44a827d610d383b42a142ec807bb31700b9ba0842d9caaa47e8d9c6
2019-10-04 16:08:18 +00:00
drh
0f654ac668 Bring the begin-concurrent branch up-to-date with the 3.30.0 release.
FossilOrigin-Name: abd1b4df4f847a32a2b9379774397a6674bab2e027fa36e921da049f26be2988
2019-10-04 15:52:19 +00:00
drh
f5078e74dd Merge version 3.30.0 beta 1 changes from trunk.
FossilOrigin-Name: ac4b64426822b8857acd6d4a97dffa9ce74ed85902a0f0e5c896ff2ec839a9d4
2019-09-30 16:43:38 +00:00
drh
788e6d3424 Merge the 3.30.0 beta 1 changes from trunk.
FossilOrigin-Name: a003818f708c336359d62b32f7e896ea0da5095dc20f47fefddecfcecda43e95
2019-09-30 16:14:05 +00:00
drh
47f677d8d7 Merge recent trunk enhancements into the begin-concurrent-pnu branch.
FossilOrigin-Name: 54930b7e0a585d0db3633673f33cac28cb257633224d6436ae29abdccbab1032
2019-09-16 12:17:11 +00:00
drh
db5eb1d353 Merge recent trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 93b9ef3f470a04658f5a7931a7ea978f2c35326a634586893670df9476833835
2019-09-16 12:08:33 +00:00
drh
3b5a4942f1 Merge the 3.29.0 changes into the begin-concurrent-pnu branch.
FossilOrigin-Name: c89949ccd102a180bf6254aef825bd0c29494d450a64bd1c3e0d347cb3dfad66
2019-07-10 18:21:47 +00:00
drh
87909ef6fb Merge the 3.29.0 release into the begin-concurrent branch.
FossilOrigin-Name: 983733aeb866b2cc0ba7ec70bf8e79a53fca5afa62a39f11b2ac86dc11615ac7
2019-07-10 18:11:11 +00:00
drh
42f5c182f6 Merge enhancements from trunk into the begin-concurrent-pnu branch.
FossilOrigin-Name: 05aa93afdbf9ae3569c827b24973b6c0af4c03b5faecb4283ba571f8bc64f0e9
2019-07-08 21:12:38 +00:00
drh
0132ba7496 Merge recent trunk enhancements into the begin-concurrent branch.
FossilOrigin-Name: 7fff0803351b4236f69c20586722c5ee160a002ba8b857e81d6574ea3f65e34d
2019-07-08 21:06:15 +00:00
drh
2bbacc1cf3 Merge the latest trunk enhancements into begin-concurrent-pnu.
FossilOrigin-Name: e70878b363db8ed0ed8aa345342a4174d54a8d95330b1bffc2843f0b07332526
2019-05-14 21:51:57 +00:00
drh
0c27f2c2b2 Merge the latest trunk enhancements into begin-concurrent.
FossilOrigin-Name: 5d3a6e18d4ba7e2c638564479a274cc0210129a90164d277c7a3198cb8315a3e
2019-05-14 21:51:36 +00:00
drh
7773fffb18 Merge the latest enhancements from trunk.
FossilOrigin-Name: 6caaaf21c930b91636127480b6e06f6fc86b1a2e34c96780b64cd16e6e4c2d81
2019-04-15 15:07:11 +00:00
drh
91a3ea48a4 Merge the latest enhancements from trunk.
FossilOrigin-Name: 7c328e889430a44db4b5ca4d24640d95c6c9e3fcff26f29fe8ca35898ea92fe0
2019-04-15 14:59:30 +00:00
drh
2ef7860be5 Use the mutex-free PRNG in this branch, since anybody using this branch is
probably interested in high concurrency.

FossilOrigin-Name: e88fc6d0cd6c470394176e259001a7810eab0a1f56e5feb8cf0b8b3eea870c2d
2019-03-26 14:04:43 +00:00
drh
bfaa3dbd61 Bring this branch into closer alignment with begin-concurrent.
FossilOrigin-Name: 6433d366175c5be586a0e11b540e76a2515466b358332c4356388f4c449ec0d7
2019-03-26 13:28:15 +00:00
drh
841efe0ca1 Merge the latest trunk changes.
FossilOrigin-Name: 51e3e835490a12dca6359ca02b8cbf31091fc4376b265995a986d002d376fad9
2019-03-26 12:16:24 +00:00
drh
25a8d24b7f Merge recent enhancements from trunk.
FossilOrigin-Name: 774d0d5288afb75f26d95be2177f0ff362eec6b4be7ba72c49c8e047a8cbd210
2019-03-26 12:07:23 +00:00
drh
7e1f717b09 Merge all the latest enhancements from trunk.
FossilOrigin-Name: 667cce3dce39487e970c4eb43efd1bae7efed7ac6ee31030c582bfafa846dcce
2019-03-08 15:30:48 +00:00
dan
77428e6055 Add new documentation file begin_concurrent.md.
FossilOrigin-Name: fdbf97e611ed23f86dc0df9d53421fb04226af0d1c7171d55c11acd0dbe935f0
2019-01-11 15:06:39 +00:00
dan
2da2a2698b Merge latest trunk changes into this branch.
FossilOrigin-Name: 5bf212f1a754fde90af0af82dd76f636754829c7372e19df1fd4fa579f0926aa
2019-01-02 16:08:14 +00:00
dan
ff9e9b27d5 Fix a problem causing a corrupt pager-cache if an OOM or IO error was
encountered while committing a concurrent transacation.

FossilOrigin-Name: 48ca30f9d7817d87a5e9a069fdc51b1a34e00585f8a35771895dd743c7bfe07c
2018-12-29 20:42:12 +00:00
dan
db6fe08914 Merge latest begin-concurrent changes into this branch.
FossilOrigin-Name: a93ca38b432e0c9fb2e00499cfc88d09f59f27f908f377d8ae99b6717e548a60
2018-12-18 17:47:53 +00:00
dan
b031157128 Merge latest trunk changes into this branch.
FossilOrigin-Name: 123cbb3312917ad5b3c32556547c5b7e8ba4e2d2def8651ff80f9fc1bdc1875c
2018-12-18 17:20:01 +00:00
drh
ef180caded Merge fixes from trunk.
FossilOrigin-Name: f2083ee410b7504e8478f7373e76ded913e3a6a92cb7345b8c1ac27554f6edf8
2018-12-06 03:13:02 +00:00
drh
9c1eb40d69 Merge bug fixes from trunk.
FossilOrigin-Name: 1e13aaa29fb6324b60b3758bdab0491fdef9727e0de603d3da3e1885b52c5caa
2018-12-06 02:08:05 +00:00
drh
9ee5641ae0 Merge ALTER TABLE and sqlite3_normalized_sql() bug fixes from trunk.
FossilOrigin-Name: 3793e5d5d60f909480c83bc2a6473d27e414d69b0c0e3289413c9bfd2f15bb08
2018-12-06 00:08:48 +00:00
drh
254fa054ec Merge ALTER TABLE and sqlite3_normalized_sql() fixes from trunk.
FossilOrigin-Name: acf10b3f8d6675ddf787f7db55a9ff0ec5729adb6fa70b48b13d2cd71862cffd
2018-12-06 00:05:18 +00:00
drh
a693b000df Merge enhancements from trunk, especially the sqlite3_normalized_sql()
enhancement.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

FossilOrigin-Name: c7a5880d6d898299b4c9414b7702cfa450aa5f7bf4ec8f417b94d2a7b6558264
2017-09-22 11:09:09 +00:00
dan
3eba5d00b6 Merge latest trunk changes into this branch.
FossilOrigin-Name: 307b802e8627c93a51e4c54851a4fab33db5061bb80e3d327ce53b127d6d511b
2017-09-22 10:49:03 +00:00
drh
03d405f046 Add the highly-experimental "PRAGMA noop_update=TRUE" command.
FossilOrigin-Name: afe45271b9c0cd379cf0beb94657e2396068c4a18f84003c4c48297760fd83ee
2017-09-12 20:09:31 +00:00
drh
d351e7643f Use a mutex-free PRNG for the random() and randomblob() SQL functions and for
the randomness used during checkpoint operations.

FossilOrigin-Name: e63d1a7cd317811f74d206ae47bd11a8fc6b6311218fbc6d0cbf49e8281b362f
2017-09-09 08:03:28 +00:00
drh
b25057c291 Merge recent enhancements from trunk.
FossilOrigin-Name: d53108e76307fb24ef1d8f75fe82c180f5588ad8e65eeceace92f4cecadfa452
2017-08-28 17:19:35 +00:00
dan
9c54156a9c Test BEGIN CONCURRENT transactions that consist entirely of read-only
statements.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

FossilOrigin-Name: 5ed2a445a164f0f0c2669c6681ea76618e639961
2015-09-01 17:48:54 +00:00
dan
7b8996c878 Merge latest trunk changes with this branch.
FossilOrigin-Name: 57bc0194f41dbcd2c343e665e7af475cd4dd7328
2015-08-28 09:27:51 +00:00
dan
c8a9d15887 Add header comments for new methods in pager.c.
FossilOrigin-Name: 437c7e219d3240767a28f73487bc26c3be3044b3
2015-08-27 19:57:16 +00:00
dan
6b3e51bd33 Add test cases for concurrent transactions and long-lived SELECT statements.
FossilOrigin-Name: fd4798cb7af263409c20d3cf81236b830bd68570
2015-08-27 19:22:56 +00:00
dan
987f821f79 Fix a problem whereby concurrent transactions would not consider pages read by the transaction before the first write statement.
FossilOrigin-Name: fc17f73170a27c2fe511ed6b6d488535c4e35bae
2015-08-27 17:42:38 +00:00
dan
f6cf5ea790 Fix an assert() in pager.c that could fail in a concurrent transaction.
FossilOrigin-Name: 69394ddaa2bc9d26477b4359c676c598b733ac9f
2015-08-26 18:54:45 +00:00
dan
de36c76a23 Fix a problem allowing some conflicting transactions to be committed.
FossilOrigin-Name: a0566382d564ca17fd13475a44fed8f714742d97
2015-08-26 18:02:20 +00:00
dan
fef3410f7f Add miscellaneous test cases for concurrent transactions.
FossilOrigin-Name: 779b1d0e17bc54062b2b09cdbf94e9e2f4bae4f7
2015-08-25 19:10:29 +00:00
dan
57888f7300 If "PRAGMA integrity_check" is run while the database is being written by a CONCURRENT transaction, do not consider unreferenced pages to be an error. They may be part of the free-page list, which is not visible at the b-tree layer when running a CONCURRENT transaction.
FossilOrigin-Name: f32b57b49311693eb0c0c9f6f14859e7b1fa93d8
2015-08-25 17:16:33 +00:00
dan
4073b26a20 Test that if a corrupt wal-index header is encountered when attempting to commit a concurrent transaction, SQLITE_BUSY_SNAPSHOT is returned to the caller.
FossilOrigin-Name: c746e0bd20cb136eed2b691f326657d266e2f1ed
2015-08-25 16:01:04 +00:00
dan
ac0a42233a Fix a segfault that could occur following an OOM condition in the concurrent transaction code.
FossilOrigin-Name: 231b5880228cf01efe3981bc8be3150d79b422e5
2015-08-25 14:37:39 +00:00
dan
3c40ed4281 Merge latest trunk changes with this branch.
FossilOrigin-Name: 3e7d6dd62dfa63d7def00bd00ac055a606a0c80d
2015-08-25 11:16:02 +00:00
mistachkin
0cd4f69c0c Remove duplicated line of code.
FossilOrigin-Name: 47280f2a2b7cc83bf11ab86284204f565c278c55
2015-08-24 22:06:02 +00:00
dan
bf3cf57e15 Change "BEGIN UNLOCKED" to "BEGIN CONCURRENT".
FossilOrigin-Name: ba1ab858e2997c88dd7eee6e6893a8616d85c665
2015-08-24 19:56:04 +00:00
dan
f5e89dba9d Fix handling of attempts to modify the database schema, application_id or user_version within an UNLOCKED transaction.
FossilOrigin-Name: 5b9f272113d21fd606903509d6f830fe60fac039
2015-08-24 19:08:10 +00:00
dan
0408529b48 Fix compilation without SQLITE_ENABLE_UNLOCKED. Also other code organization issues.
FossilOrigin-Name: 041135575417201bbcf0544cc69dcb7369c7fb34
2015-08-24 16:00:08 +00:00
dan
5cf03728a6 Consolidate two blocks of similar code in btreeFixUnlocked().
FossilOrigin-Name: 701302b4bd62ca7aefe643eac096a0ee672a62fa
2015-08-24 10:05:03 +00:00
dan
8b994f29a5 Merge trunk changes with this branch.
FossilOrigin-Name: 876810c28b3ad573ae46050ec699ef7eea4e313d
2015-08-24 06:44:17 +00:00
dan
654a965367 Fix another problem involving unlocked transactions and wal-file restarts.
FossilOrigin-Name: 4460764ea8fc948fe02f0a09476857839b3aa1ae
2015-08-24 06:43:25 +00:00
dan
0c52b373a0 Fix a problem to do with detecting unlocked transaction conflicts if another client restarts the wal while the transaction is running.
FossilOrigin-Name: e3968b256282d8c0a87c26667b1ba02faf7a5a17
2015-08-22 20:32:39 +00:00
dan
f5cebf71fe Add further tests for deferred page allocation. And fixes for the same.
FossilOrigin-Name: ed0a31be726e60115a5dd73d4ed580201b400ab7
2015-08-22 17:28:55 +00:00
dan
c299a11c02 Merge further trunk changes.
FossilOrigin-Name: c2327a3b8e5d604ab948b1e9f6cfc401429e51db
2015-08-22 07:56:49 +00:00
dan
8c5847a257 Merge trunk changes into this branch.
FossilOrigin-Name: 9021f7369f4b32db6126b3dc0ac75d11039625be
2015-08-22 07:55:08 +00:00
dan
b87b25f219 Fix many minor issues in the unlocked transaction code.
FossilOrigin-Name: 53aaeea6c98f82f3b55d4b3ab139ee41b727243c
2015-08-21 20:11:23 +00:00
dan
572a21c8e9 When committing an UNLOCKED transaction, try to move pages allocated at the end of the file to free slots within the file (like an incremental-vacuum operation does).
FossilOrigin-Name: 069679162d8d50e9731831e658aa58f280dbb3e7
2015-08-21 18:55:22 +00:00
dan
70af25d03d Fix a problem with UNLOCKED transactions that free pages allocated within the same transaction.
FossilOrigin-Name: 227bb8a1815c4dc6084970f06b0a6bfccdff3fd2
2015-08-21 17:57:16 +00:00
dan
cec0beebb3 Merge trunk changes with this branch.
FossilOrigin-Name: deaf3b18569025d8d168da29fae76142cfffe9e5
2015-08-21 16:22:04 +00:00
dan
64b310ed1a Add extra tests and a fix for rollbacks of UNLOCKED transactions.
FossilOrigin-Name: 82cd837e72ed4cf5821be717369694be29a2997e
2015-08-21 14:21:22 +00:00
dan
699bdf056b Fix a problem causing corruption when an UNLOCKED transaction is rolled back.
FossilOrigin-Name: 7c36147846484c96023939864417b5624f3bc5f8
2015-08-20 20:25:56 +00:00
dan
7b3d71e9cc When committing an unlocked transaction, relocate newly allocated database pages within the file to avoid conflicting with committed transactions. There are lots of things still to fix in this code.
FossilOrigin-Name: 3bbc31d515ba9fc920c5cbc7059d3eb1ba9c7f8e
2015-08-19 20:27:05 +00:00
dan
1a9cde3ba9 Handle writes to auto-vacuum databases within UNLOCKED transactions in the same way as for non-UNLOCKED transactions.
FossilOrigin-Name: de1ea450db33b140b11af5b801ea6a15875e774e
2015-08-15 18:16:46 +00:00
dan
773d2d6c09 Only allow UNLOCKED transactions to commit if none of the pages read by the transaction have been modified since it was opened.
FossilOrigin-Name: 0b9718426e44df092850c5d095ce1b84a1e567cf
2015-07-29 12:14:28 +00:00
dan
37d36205f3 Add some test cases and fix some small problems with BEGIN UNLOCKED transactions.
FossilOrigin-Name: 6da0e962ad2aa5e52c1f1b5c3dbf77a2cb16ac2d
2015-07-28 16:46:49 +00:00
dan
3d39434c36 Add an experimental "BEGIN UNLOCKED" command.
FossilOrigin-Name: 8079421604dbd40d03471dad6d12115119b554c2
2015-07-27 19:31:45 +00:00
61 changed files with 7067 additions and 733 deletions

View File

@ -1622,7 +1622,8 @@ 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\test_session.c \
$(TOP)\ext\session\sqlite3changebatch.c
# Statically linked extensions.
#

106
doc/begin_concurrent.md Normal file
View File

@ -0,0 +1,106 @@
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_BUSY_SNAPSHOT
error is returned. At this point, all the client can do is ROLLBACK the
transaction.
If SQLITE_BUSY_SNAPSHOT is returned, messages are output via the sqlite3_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.

View File

@ -0,0 +1,222 @@
# 2016 August 23
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source $testdir/tester.tcl
ifcapable !session {finish_test; return}
set testprefix changebatch1
proc sql_to_changeset {method sql} {
sqlite3session S db main
S attach *
execsql $sql
set ret [S $method]
S delete
return $ret
}
proc do_changebatch_test {tn method args} {
set C [list]
foreach a $args {
lappend C [sql_to_changeset $method $a]
}
sqlite3changebatch cb db
set i 1
foreach ::cs [lrange $C 0 end-1] {
set rc [cb add $::cs]
if {$rc!="SQLITE_OK"} { error "expected SQLITE_OK, got $rc (i=$i)" }
incr i
}
set ::cs [lindex $C end]
do_test $tn { cb add [set ::cs] } SQLITE_CONSTRAINT
cb delete
}
proc do_changebatch_test1 {tn args} {
uplevel do_changebatch_test $tn changeset $args
}
proc do_changebatch_test2 {tn args} {
uplevel do_changebatch_test $tn fullchangeset $args
}
#-------------------------------------------------------------------------
# The body of the following loop contains tests for database schemas
# that do not feature multi-column UNIQUE constraints. In this case
# it doesn't matter if the changesets are generated using
# sqlite3session_changeset() or sqlite3session_fullchangeset().
#
foreach {tn testfunction} {
1 do_changebatch_test1
2 do_changebatch_test2
} {
reset_db
#-------------------------------------------------------------------------
#
do_execsql_test $tn.1.0 {
CREATE TABLE t1(a PRIMARY KEY, b);
}
$testfunction $tn.1.1 {
INSERT INTO t1 VALUES(1, 1);
} {
DELETE FROM t1 WHERE a=1;
}
do_execsql_test $tn.1.2.0 {
INSERT INTO t1 VALUES(1, 1);
INSERT INTO t1 VALUES(2, 2);
INSERT INTO t1 VALUES(3, 3);
}
$testfunction $tn.1.2.1 {
DELETE FROM t1 WHERE a=2;
} {
INSERT INTO t1 VALUES(2, 2);
}
#-------------------------------------------------------------------------
#
do_execsql_test $tn.2.0 {
CREATE TABLE x1(a, b PRIMARY KEY, c UNIQUE);
CREATE TABLE x2(a PRIMARY KEY, b UNIQUE, c UNIQUE);
CREATE INDEX x1a ON x1(a);
INSERT INTO x1 VALUES(1, 1, 'a');
INSERT INTO x1 VALUES(1, 2, 'b');
INSERT INTO x1 VALUES(1, 3, 'c');
}
$testfunction $tn.2.1 {
DELETE FROM x1 WHERE b=2;
} {
UPDATE x1 SET c='b' WHERE b=3;
}
$testfunction $tn.2.2 {
DELETE FROM x1 WHERE b=1;
} {
INSERT INTO x1 VALUES(1, 5, 'a');
}
set L [list]
for {set i 1000} {$i < 10000} {incr i} {
lappend L "INSERT INTO x2 VALUES($i, $i, 'x' || $i)"
}
lappend L "DELETE FROM x2 WHERE b=1005"
$testfunction $tn.2.3 {*}$L
execsql { INSERT INTO x1 VALUES('f', 'f', 'f') }
$testfunction $tn.2.4 {
INSERT INTO x2 VALUES('f', 'f', 'f');
} {
INSERT INTO x1 VALUES('g', 'g', 'g');
} {
DELETE FROM x1 WHERE b='f';
} {
INSERT INTO x2 VALUES('g', 'g', 'g');
} {
INSERT INTO x1 VALUES('f', 'f', 'f');
}
execsql {
DELETE FROM x1;
INSERT INTO x1 VALUES(1.5, 1.5, 1.5);
}
$testfunction $tn.2.5 {
DELETE FROM x1 WHERE b BETWEEN 1 AND 2;
} {
INSERT INTO x1 VALUES(2.5, 2.5, 2.5);
} {
INSERT INTO x1 VALUES(1.5, 1.5, 1.5);
}
execsql {
DELETE FROM x2;
INSERT INTO x2 VALUES(X'abcd', X'1234', X'7890');
INSERT INTO x2 VALUES(X'0000', X'0000', X'0000');
}
breakpoint
$testfunction $tn.2.6 {
UPDATE x2 SET c = X'1234' WHERE a=X'abcd';
INSERT INTO x2 VALUES(X'1234', X'abcd', X'7890');
} {
DELETE FROM x2 WHERE b=X'0000';
} {
INSERT INTO x2 VALUES(1, X'0000', NULL);
}
}
#-------------------------------------------------------------------------
# Test some multi-column UNIQUE constraints. First Using _changeset() to
# demonstrate the problem, then using _fullchangeset() to show that it has
# been fixed.
#
reset_db
do_execsql_test 3.0 {
CREATE TABLE y1(a PRIMARY KEY, b, c, UNIQUE(b, c));
INSERT INTO y1 VALUES(1, 1, 1);
INSERT INTO y1 VALUES(2, 2, 2);
INSERT INTO y1 VALUES(3, 3, 3);
INSERT INTO y1 VALUES(4, 3, 4);
BEGIN;
}
do_test 3.1.1 {
set c1 [sql_to_changeset changeset { DELETE FROM y1 WHERE a=4 }]
set c2 [sql_to_changeset changeset { UPDATE y1 SET c=4 WHERE a=3 }]
sqlite3changebatch cb db
cb add $c1
cb add $c2
} {SQLITE_OK}
do_test 3.1.2 {
cb delete
execsql ROLLBACK
} {}
do_test 3.1.1 {
set c1 [sql_to_changeset fullchangeset { DELETE FROM y1 WHERE a=4 }]
set c2 [sql_to_changeset fullchangeset { UPDATE y1 SET c=4 WHERE a=3 }]
sqlite3changebatch cb db
cb add $c1
cb add $c2
} {SQLITE_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

View File

@ -0,0 +1,42 @@
# 2011 Mar 21
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# The focus of this file is testing the session module.
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source [file join [file dirname [info script]] session_common.tcl]
source $testdir/tester.tcl
ifcapable !session {finish_test; return}
set testprefix changebatchfault
do_execsql_test 1.0 {
CREATE TABLE t1(a, b, c PRIMARY KEY, UNIQUE(a, b));
INSERT INTO t1 VALUES('a', 'a', 'a');
INSERT INTO t1 VALUES('b', 'b', 'b');
}
set ::c1 [changeset_from_sql { delete from t1 where c='a' }]
set ::c2 [changeset_from_sql { insert into t1 values('c', 'c', 'c') }]
do_faultsim_test 1 -faults oom-* -body {
sqlite3changebatch cb db
cb add $::c1
cb add $::c2
} -test {
faultsim_test_result {0 SQLITE_OK} {1 SQLITE_NOMEM}
catch { cb delete }
}
finish_test

View File

@ -29,7 +29,7 @@ do_test 1.0 {
WITH s(i) AS (
VALUES(1) UNION ALL SELECT i+1 FROM s WHERe i<10000
)
INSERT INTO t1 SELECT 'abcde', randomblob(16), i FROM s;
INSERT INTO t1 SELECT 'abcde', randomblob(18), i FROM s;
}
compare_db db db2
} {}

View File

@ -0,0 +1,485 @@
#if !defined(SQLITE_TEST) || (defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK))
#include "sqlite3session.h"
#include "sqlite3changebatch.h"
#include <assert.h>
#include <string.h>
typedef struct BatchTable BatchTable;
typedef struct BatchIndex BatchIndex;
typedef struct BatchIndexEntry BatchIndexEntry;
typedef struct BatchHash BatchHash;
struct sqlite3_changebatch {
sqlite3 *db; /* Database handle used to read schema */
BatchTable *pTab; /* First in linked list of tables */
int iChangesetId; /* Current changeset id */
int iNextIdxId; /* Next available index id */
int nEntry; /* Number of entries in hash table */
int nHash; /* Number of hash buckets */
BatchIndexEntry **apHash; /* Array of hash buckets */
};
struct BatchTable {
BatchIndex *pIdx; /* First in linked list of UNIQUE indexes */
BatchTable *pNext; /* Next table */
char zTab[1]; /* Table name */
};
struct BatchIndex {
BatchIndex *pNext; /* Next index on same table */
int iId; /* Index id (assigned internally) */
int bPk; /* True for PK index */
int nCol; /* Size of aiCol[] array */
int *aiCol; /* Array of columns that make up index */
};
struct BatchIndexEntry {
BatchIndexEntry *pNext; /* Next colliding hash table entry */
int iChangesetId; /* Id of associated changeset */
int iIdxId; /* Id of index this key is from */
int szRecord;
char aRecord[1];
};
/*
** Allocate and zero a block of nByte bytes. Must be freed using cbFree().
*/
static void *cbMalloc(int *pRc, int nByte){
void *pRet;
if( *pRc ){
pRet = 0;
}else{
pRet = sqlite3_malloc(nByte);
if( pRet ){
memset(pRet, 0, nByte);
}else{
*pRc = SQLITE_NOMEM;
}
}
return pRet;
}
/*
** Free an allocation made by cbMalloc().
*/
static void cbFree(void *p){
sqlite3_free(p);
}
/*
** Return the hash bucket that pEntry belongs in.
*/
static int cbHash(sqlite3_changebatch *p, BatchIndexEntry *pEntry){
unsigned int iHash = (unsigned int)pEntry->iIdxId;
unsigned char *pEnd = (unsigned char*)&pEntry->aRecord[pEntry->szRecord];
unsigned char *pIter;
for(pIter=(unsigned char*)pEntry->aRecord; pIter<pEnd; pIter++){
iHash += (iHash << 7) + *pIter;
}
return (int)(iHash % p->nHash);
}
/*
** Resize the hash table.
*/
static int cbHashResize(sqlite3_changebatch *p){
int rc = SQLITE_OK;
BatchIndexEntry **apNew;
int nNew = (p->nHash ? p->nHash*2 : 512);
int i;
apNew = cbMalloc(&rc, sizeof(BatchIndexEntry*) * nNew);
if( rc==SQLITE_OK ){
int nHash = p->nHash;
p->nHash = nNew;
for(i=0; i<nHash; i++){
BatchIndexEntry *pEntry;
while( (pEntry=p->apHash[i])!=0 ){
int iHash = cbHash(p, pEntry);
p->apHash[i] = pEntry->pNext;
pEntry->pNext = apNew[iHash];
apNew[iHash] = pEntry;
}
}
cbFree(p->apHash);
p->apHash = apNew;
}
return rc;
}
/*
** Allocate a new sqlite3_changebatch object.
*/
int sqlite3changebatch_new(sqlite3 *db, sqlite3_changebatch **pp){
sqlite3_changebatch *pRet;
int rc = SQLITE_OK;
*pp = pRet = (sqlite3_changebatch*)cbMalloc(&rc, sizeof(sqlite3_changebatch));
if( pRet ){
pRet->db = db;
}
return rc;
}
/*
** Add a BatchIndex entry for index zIdx to table pTab.
*/
static int cbAddIndex(
sqlite3_changebatch *p,
BatchTable *pTab,
const char *zIdx,
int bPk
){
int nCol = 0;
sqlite3_stmt *pIndexInfo = 0;
BatchIndex *pNew = 0;
int rc;
char *zIndexInfo;
zIndexInfo = (char*)sqlite3_mprintf("PRAGMA main.index_info = %Q", zIdx);
if( zIndexInfo ){
rc = sqlite3_prepare_v2(p->db, zIndexInfo, -1, &pIndexInfo, 0);
sqlite3_free(zIndexInfo);
}else{
rc = SQLITE_NOMEM;
}
if( rc==SQLITE_OK ){
while( SQLITE_ROW==sqlite3_step(pIndexInfo) ){ nCol++; }
rc = sqlite3_reset(pIndexInfo);
}
pNew = (BatchIndex*)cbMalloc(&rc, sizeof(BatchIndex) + sizeof(int) * nCol);
if( rc==SQLITE_OK ){
pNew->nCol = nCol;
pNew->bPk = bPk;
pNew->aiCol = (int*)&pNew[1];
pNew->iId = p->iNextIdxId++;
while( SQLITE_ROW==sqlite3_step(pIndexInfo) ){
int i = sqlite3_column_int(pIndexInfo, 0);
int j = sqlite3_column_int(pIndexInfo, 1);
pNew->aiCol[i] = j;
}
rc = sqlite3_reset(pIndexInfo);
}
if( rc==SQLITE_OK ){
pNew->pNext = pTab->pIdx;
pTab->pIdx = pNew;
}else{
cbFree(pNew);
}
sqlite3_finalize(pIndexInfo);
return rc;
}
/*
** Free the object passed as the first argument.
*/
static void cbFreeTable(BatchTable *pTab){
BatchIndex *pIdx;
BatchIndex *pIdxNext;
for(pIdx=pTab->pIdx; pIdx; pIdx=pIdxNext){
pIdxNext = pIdx->pNext;
cbFree(pIdx);
}
cbFree(pTab);
}
/*
** Find or create the BatchTable object named zTab.
*/
static int cbFindTable(
sqlite3_changebatch *p,
const char *zTab,
BatchTable **ppTab
){
BatchTable *pRet = 0;
int rc = SQLITE_OK;
for(pRet=p->pTab; pRet; pRet=pRet->pNext){
if( 0==sqlite3_stricmp(zTab, pRet->zTab) ) break;
}
if( pRet==0 ){
int nTab = strlen(zTab);
pRet = (BatchTable*)cbMalloc(&rc, nTab + sizeof(BatchTable));
if( pRet ){
sqlite3_stmt *pIndexList = 0;
char *zIndexList = 0;
int rc2;
memcpy(pRet->zTab, zTab, nTab);
zIndexList = sqlite3_mprintf("PRAGMA main.index_list = %Q", zTab);
if( zIndexList==0 ){
rc = SQLITE_NOMEM;
}else{
rc = sqlite3_prepare_v2(p->db, zIndexList, -1, &pIndexList, 0);
sqlite3_free(zIndexList);
}
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pIndexList) ){
if( sqlite3_column_int(pIndexList, 2) ){
const char *zIdx = (const char*)sqlite3_column_text(pIndexList, 1);
const char *zTyp = (const char*)sqlite3_column_text(pIndexList, 3);
rc = cbAddIndex(p, pRet, zIdx, (zTyp[0]=='p'));
}
}
rc2 = sqlite3_finalize(pIndexList);
if( rc==SQLITE_OK ) rc = rc2;
if( rc==SQLITE_OK ){
pRet->pNext = p->pTab;
p->pTab = pRet;
}else{
cbFreeTable(pRet);
pRet = 0;
}
}
}
*ppTab = pRet;
return rc;
}
/*
** Extract value iVal from the changeset iterator passed as the first
** argument. Set *ppVal to point to the value before returning.
**
** This function attempts to extract the value using function xVal
** (which is always either sqlite3changeset_new or sqlite3changeset_old).
** If the call returns SQLITE_OK but does not supply an sqlite3_value*
** pointer, an attempt to extract the value is made using the xFallback
** function.
*/
static int cbGetChangesetValue(
sqlite3_changeset_iter *pIter,
int (*xVal)(sqlite3_changeset_iter*,int,sqlite3_value**),
int (*xFallback)(sqlite3_changeset_iter*,int,sqlite3_value**),
int iVal,
sqlite3_value **ppVal
){
int rc = xVal(pIter, iVal, ppVal);
if( rc==SQLITE_OK && *ppVal==0 && xFallback ){
rc = xFallback(pIter, iVal, ppVal);
}
return rc;
}
static int cbAddToHash(
sqlite3_changebatch *p,
sqlite3_changeset_iter *pIter,
BatchIndex *pIdx,
int (*xVal)(sqlite3_changeset_iter*,int,sqlite3_value**),
int (*xFallback)(sqlite3_changeset_iter*,int,sqlite3_value**),
int *pbConf
){
BatchIndexEntry *pNew;
int sz = pIdx->nCol;
int i;
int iOut = 0;
int rc = SQLITE_OK;
for(i=0; rc==SQLITE_OK && i<pIdx->nCol; i++){
sqlite3_value *pVal;
rc = cbGetChangesetValue(pIter, xVal, xFallback, pIdx->aiCol[i], &pVal);
if( rc==SQLITE_OK ){
int eType = 0;
if( pVal ) eType = sqlite3_value_type(pVal);
switch( eType ){
case 0:
case SQLITE_NULL:
return SQLITE_OK;
case SQLITE_INTEGER:
sz += 8;
break;
case SQLITE_FLOAT:
sz += 8;
break;
default:
assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
sz += sqlite3_value_bytes(pVal);
break;
}
}
}
pNew = cbMalloc(&rc, sizeof(BatchIndexEntry) + sz);
if( pNew ){
pNew->iChangesetId = p->iChangesetId;
pNew->iIdxId = pIdx->iId;
pNew->szRecord = sz;
for(i=0; i<pIdx->nCol; i++){
int eType;
sqlite3_value *pVal;
rc = cbGetChangesetValue(pIter, xVal, xFallback, pIdx->aiCol[i], &pVal);
if( rc!=SQLITE_OK ) break; /* coverage: condition is never true */
eType = sqlite3_value_type(pVal);
pNew->aRecord[iOut++] = eType;
switch( eType ){
case SQLITE_INTEGER: {
sqlite3_int64 i64 = sqlite3_value_int64(pVal);
memcpy(&pNew->aRecord[iOut], &i64, 8);
iOut += 8;
break;
}
case SQLITE_FLOAT: {
double d64 = sqlite3_value_double(pVal);
memcpy(&pNew->aRecord[iOut], &d64, sizeof(double));
iOut += sizeof(double);
break;
}
default: {
int nByte = sqlite3_value_bytes(pVal);
const char *z = (const char*)sqlite3_value_blob(pVal);
memcpy(&pNew->aRecord[iOut], z, nByte);
iOut += nByte;
break;
}
}
}
}
if( rc==SQLITE_OK && p->nEntry>=(p->nHash/2) ){
rc = cbHashResize(p);
}
if( rc==SQLITE_OK ){
BatchIndexEntry *pIter;
int iHash = cbHash(p, pNew);
assert( iHash>=0 && iHash<p->nHash );
for(pIter=p->apHash[iHash]; pIter; pIter=pIter->pNext){
if( pNew->szRecord==pIter->szRecord
&& 0==memcmp(pNew->aRecord, pIter->aRecord, pNew->szRecord)
){
if( pNew->iChangesetId!=pIter->iChangesetId ){
*pbConf = 1;
}
cbFree(pNew);
pNew = 0;
break;
}
}
if( pNew ){
pNew->pNext = p->apHash[iHash];
p->apHash[iHash] = pNew;
p->nEntry++;
}
}else{
cbFree(pNew);
}
return rc;
}
/*
** Add a changeset to the current batch.
*/
int sqlite3changebatch_add(sqlite3_changebatch *p, void *pBuf, int nBuf){
sqlite3_changeset_iter *pIter; /* Iterator opened on pBuf/nBuf */
int rc; /* Return code */
int bConf = 0; /* Conflict was detected */
rc = sqlite3changeset_start(&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 */

View File

@ -0,0 +1,82 @@
#if !defined(SQLITECHANGEBATCH_H_)
#define SQLITECHANGEBATCH_H_ 1
typedef struct sqlite3_changebatch sqlite3_changebatch;
/*
** Create a new changebatch object for detecting conflicts between
** changesets associated with a schema equivalent to that of the "main"
** database of the open database handle db passed as the first
** parameter. It is the responsibility of the caller to ensure that
** the database handle is not closed until after the changebatch
** object has been deleted.
**
** A changebatch object is used to detect batches of non-conflicting
** changesets. Changesets that do not conflict may be applied to the
** target database in any order without affecting the final state of
** the database.
**
** The changebatch object only works reliably if PRIMARY KEY and UNIQUE
** constraints on tables affected by the changesets use collation
** sequences that are equivalent to built-in collation sequence
** BINARY for the == operation.
**
** If successful, SQLITE_OK is returned and (*pp) set to point to
** the new changebatch object. If an error occurs, an SQLite error
** code is returned and the final value of (*pp) is undefined.
*/
int sqlite3changebatch_new(sqlite3 *db, sqlite3_changebatch **pp);
/*
** Argument p points to a buffer containing a changeset n bytes in
** size. Assuming no error occurs, this function returns SQLITE_OK
** if the changeset does not conflict with any changeset passed
** to an sqlite3changebatch_add() call made on the same
** sqlite3_changebatch* handle since the most recent call to
** sqlite3changebatch_zero(). If the changeset does conflict with
** an earlier such changeset, SQLITE_CONSTRAINT is returned. Or,
** if an error occurs, some other SQLite error code may be returned.
**
** One changeset is said to conflict with another if
** either:
**
** * the two changesets contain operations (INSERT, UPDATE or
** DELETE) on the same row, identified by primary key, or
**
** * the two changesets contain operations (INSERT, UPDATE or
** DELETE) on rows with identical values in any combination
** of fields constrained by a UNIQUE constraint.
**
** Even if this function returns SQLITE_CONFLICT, the current
** changeset is added to the internal data structures - so future
** calls to this function may conflict with it. If this function
** returns any result code other than SQLITE_OK or SQLITE_CONFLICT,
** the result of any future call to sqlite3changebatch_add() is
** undefined.
**
** Only changesets may be passed to this function. Passing a
** patchset to this function results in an SQLITE_MISUSE error.
*/
int sqlite3changebatch_add(sqlite3_changebatch*, void *p, int n);
/*
** Zero a changebatch object. This causes the records of all earlier
** calls to sqlite3changebatch_add() to be discarded.
*/
void sqlite3changebatch_zero(sqlite3_changebatch*);
/*
** Return a copy of the first argument passed to the sqlite3changebatch_new()
** call used to create the changebatch object passed as the only argument
** to this function.
*/
sqlite3 *sqlite3changebatch_db(sqlite3_changebatch*);
/*
** Delete a changebatch object.
*/
void sqlite3changebatch_delete(sqlite3_changebatch*);
#endif /* !defined(SQLITECHANGEBATCH_H_) */

View File

@ -27,6 +27,13 @@ typedef struct SessionInput SessionInput;
#define SESSIONS_ROWID "_rowid_"
/*
** The three different types of changesets generated.
*/
#define SESSIONS_PATCHSET 0
#define SESSIONS_CHANGESET 1
#define SESSIONS_FULLCHANGESET 2
static int sessions_strm_chunk_size = SESSIONS_STRM_CHUNK_SIZE;
typedef struct SessionHook SessionHook;
@ -2611,7 +2618,7 @@ static void sessionAppendCol(
*/
static int sessionAppendUpdate(
SessionBuffer *pBuf, /* Buffer to append to */
int bPatchset, /* True for "patchset", 0 for "changeset" */
int ePatchset, /* True for "patchset", 0 for "changeset" */
sqlite3_stmt *pStmt, /* Statement handle pointing at new row */
SessionChange *p, /* Object containing old values */
u8 *abPK /* Boolean array - true for PK columns */
@ -2675,8 +2682,8 @@ static int sessionAppendUpdate(
/* Add a field to the old.* record. This is omitted if this module is
** currently generating a patchset. */
if( bPatchset==0 ){
if( bChanged || abPK[i] ){
if( ePatchset!=SESSIONS_PATCHSET ){
if( ePatchset==SESSIONS_FULLCHANGESET || bChanged || abPK[i] ){
sessionAppendBlob(pBuf, pCsr, nAdvance, &rc);
}else{
sessionAppendByte(pBuf, 0, &rc);
@ -2685,7 +2692,7 @@ static int sessionAppendUpdate(
/* Add a field to the new.* record. Or the only record if currently
** generating a patchset. */
if( bChanged || (bPatchset && abPK[i]) ){
if( bChanged || (ePatchset==SESSIONS_PATCHSET && abPK[i]) ){
sessionAppendCol(&buf2, pStmt, i, &rc);
}else{
sessionAppendByte(&buf2, 0, &rc);
@ -2711,7 +2718,7 @@ static int sessionAppendUpdate(
*/
static int sessionAppendDelete(
SessionBuffer *pBuf, /* Buffer to append to */
int bPatchset, /* True for "patchset", 0 for "changeset" */
int eChangeset, /* One of SESSIONS_CHANGESET etc. */
SessionChange *p, /* Object containing old values */
int nCol, /* Number of columns in table */
u8 *abPK /* Boolean array - true for PK columns */
@ -2721,7 +2728,7 @@ static int sessionAppendDelete(
sessionAppendByte(pBuf, SQLITE_DELETE, &rc);
sessionAppendByte(pBuf, p->bIndirect, &rc);
if( bPatchset==0 ){
if( eChangeset!=SESSIONS_PATCHSET ){
sessionAppendBlob(pBuf, p->aRecord, p->nRecord, &rc);
}else{
int i;
@ -2947,12 +2954,12 @@ static int sessionSelectBind(
*/
static void sessionAppendTableHdr(
SessionBuffer *pBuf, /* Append header to this buffer */
int bPatchset, /* Use the patchset format if true */
int ePatchset, /* Use the patchset format if true */
SessionTable *pTab, /* Table object to append header for */
int *pRc /* IN/OUT: Error code */
){
/* Write a table header */
sessionAppendByte(pBuf, (bPatchset ? 'P' : 'T'), pRc);
sessionAppendByte(pBuf, (ePatchset==SESSIONS_PATCHSET) ? 'P' : 'T', pRc);
sessionAppendVarint(pBuf, pTab->nCol, pRc);
sessionAppendBlob(pBuf, pTab->abPK, pTab->nCol, pRc);
sessionAppendBlob(pBuf, (u8 *)pTab->zName, (int)strlen(pTab->zName)+1, pRc);
@ -2970,7 +2977,7 @@ static void sessionAppendTableHdr(
*/
static int sessionGenerateChangeset(
sqlite3_session *pSession, /* Session object */
int bPatchset, /* True for patchset, false for changeset */
int ePatchset, /* One of SESSIONS_CHANGESET etc. */
int (*xOutput)(void *pOut, const void *pData, int nData),
void *pOut, /* First argument for xOutput */
int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
@ -3015,7 +3022,7 @@ static int sessionGenerateChangeset(
}
/* Write a table header */
sessionAppendTableHdr(&buf, bPatchset, pTab, &rc);
sessionAppendTableHdr(&buf, ePatchset, pTab, &rc);
/* Build and compile a statement to execute: */
if( rc==SQLITE_OK ){
@ -3041,10 +3048,10 @@ static int sessionGenerateChangeset(
}
}else{
assert( pTab->abPK!=0 );
rc = sessionAppendUpdate(&buf, bPatchset, pSel, p, pTab->abPK);
rc = sessionAppendUpdate(&buf, ePatchset, pSel, p, pTab->abPK);
}
}else if( p->op!=SQLITE_INSERT ){
rc = sessionAppendDelete(&buf, bPatchset, p, pTab->nCol,pTab->abPK);
rc = sessionAppendDelete(&buf, ePatchset, p, pTab->nCol,pTab->abPK);
}
if( rc==SQLITE_OK ){
rc = sqlite3_reset(pSel);
@ -3103,7 +3110,8 @@ int sqlite3session_changeset(
int rc;
if( pnChangeset==0 || ppChangeset==0 ) return SQLITE_MISUSE;
rc = sessionGenerateChangeset(pSession, 0, 0, 0, pnChangeset, ppChangeset);
rc = sessionGenerateChangeset(
pSession, SESSIONS_CHANGESET, 0, 0, pnChangeset, ppChangeset);
assert( rc || pnChangeset==0
|| pSession->bEnableSize==0 || *pnChangeset<=pSession->nMaxChangesetSize
);
@ -3119,7 +3127,8 @@ int sqlite3session_changeset_strm(
void *pOut
){
if( xOutput==0 ) return SQLITE_MISUSE;
return sessionGenerateChangeset(pSession, 0, xOutput, pOut, 0, 0);
return sessionGenerateChangeset(
pSession, SESSIONS_CHANGESET, xOutput, pOut, 0, 0);
}
/*
@ -3131,7 +3140,8 @@ int sqlite3session_patchset_strm(
void *pOut
){
if( xOutput==0 ) return SQLITE_MISUSE;
return sessionGenerateChangeset(pSession, 1, xOutput, pOut, 0, 0);
return sessionGenerateChangeset(
pSession, SESSIONS_PATCHSET, xOutput, pOut, 0, 0);
}
/*
@ -3147,9 +3157,20 @@ int sqlite3session_patchset(
void **ppPatchset /* OUT: Buffer containing changeset */
){
if( pnPatchset==0 || ppPatchset==0 ) return SQLITE_MISUSE;
return sessionGenerateChangeset(pSession, 1, 0, 0, pnPatchset, ppPatchset);
return sessionGenerateChangeset(
pSession, SESSIONS_PATCHSET, 0, 0, pnPatchset, ppPatchset);
}
int sqlite3session_fullchangeset(
sqlite3_session *pSession, /* Session object */
int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
void **ppChangeset /* OUT: Buffer containing changeset */
){
return sessionGenerateChangeset(
pSession, SESSIONS_FULLCHANGESET, 0, 0, pnChangeset, ppChangeset);
}
/*
** Enable or disable the session object passed as the first argument.
*/
@ -5950,10 +5971,11 @@ static int sessionChangegroupOutput(
** hash tables attached to the SessionTable objects in list p->pList.
*/
for(pTab=pGrp->pList; rc==SQLITE_OK && pTab; pTab=pTab->pNext){
int eChangeset = pGrp->bPatch ? SESSIONS_PATCHSET : SESSIONS_CHANGESET;
int i;
if( pTab->nEntry==0 ) continue;
sessionAppendTableHdr(&buf, pGrp->bPatch, pTab, &rc);
sessionAppendTableHdr(&buf, eChangeset, pTab, &rc);
for(i=0; i<pTab->nChange; i++){
SessionChange *p;
for(p=pTab->apChange[i]; p; p=p->pNext){

View File

@ -368,6 +368,19 @@ int sqlite3session_changeset(
void **ppChangeset /* OUT: Buffer containing changeset */
);
/*
** CAPI3REF: Generate A Full Changeset From A Session Object
**
** This function is similar to sqlite3session_changeset(), except that for
** each row affected by an UPDATE statement, all old.* values are recorded
** as part of the changeset, not just those modified.
*/
int sqlite3session_fullchangeset(
sqlite3_session *pSession, /* Session object */
int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
void **ppChangeset /* OUT: Buffer containing changeset */
);
/*
** CAPI3REF: Return An Upper-limit For The Size Of The Changeset
** METHOD: sqlite3_session

View File

@ -229,6 +229,7 @@ static int testStreamOutput(
** $session indirect INTEGER
** $session patchset
** $session table_filter SCRIPT
** $session fullchangeset
*/
static int SQLITE_TCLAPI test_session_cmd(
void *clientData,
@ -242,20 +243,20 @@ static int SQLITE_TCLAPI test_session_cmd(
const char *zSub;
int nArg;
const char *zMsg;
int iSub;
} aSub[] = {
{ "attach", 1, "TABLE", }, /* 0 */
{ "changeset", 0, "", }, /* 1 */
{ "delete", 0, "", }, /* 2 */
{ "enable", 1, "BOOL", }, /* 3 */
{ "indirect", 1, "BOOL", }, /* 4 */
{ "isempty", 0, "", }, /* 5 */
{ "table_filter", 1, "SCRIPT", }, /* 6 */
{ "attach", 1, "TABLE" }, /* 0 */
{ "changeset", 0, "" }, /* 1 */
{ "delete", 0, "" }, /* 2 */
{ "enable", 1, "BOOL" }, /* 3 */
{ "indirect", 1, "BOOL" }, /* 4 */
{ "isempty", 0, "" }, /* 5 */
{ "table_filter", 1, "SCRIPT" }, /* 6 */
{ "patchset", 0, "", }, /* 7 */
{ "diff", 2, "FROMDB TBL", }, /* 8 */
{ "memory_used", 0, "", }, /* 9 */
{ "changeset_size", 0, "", }, /* 10 */
{ "object_config", 2, "OPTION INTEGER", }, /* 11 */
{ "diff", 2, "FROMDB TBL" }, /* 8 */
{ "fullchangeset",0, "" }, /* 9 */
{ "memory_used", 0, "", }, /* 10 */
{ "changeset_size", 0, "", }, /* 11 */
{ "object_config", 2, "OPTION INTEGER", }, /* 12 */
{ 0 }
};
int iSub;
@ -285,10 +286,11 @@ static int SQLITE_TCLAPI test_session_cmd(
break;
}
case 9: /* fullchangeset */
case 7: /* patchset */
case 1: { /* changeset */
TestSessionsBlob o = {0, 0};
if( test_tcl_integer(interp, SESSION_STREAM_TCL_VAR) ){
if( iSub!=9 && test_tcl_integer(interp, SESSION_STREAM_TCL_VAR) ){
void *pCtx = (void*)&o;
if( iSub==7 ){
rc = sqlite3session_patchset_strm(pSession, testStreamOutput, pCtx);
@ -298,6 +300,8 @@ static int SQLITE_TCLAPI test_session_cmd(
}else{
if( iSub==7 ){
rc = sqlite3session_patchset(pSession, &o.n, &o.p);
}else if( iSub==9 ){
rc = sqlite3session_fullchangeset(pSession, &o.n, &o.p);
}else{
rc = sqlite3session_changeset(pSession, &o.n, &o.p);
}
@ -313,6 +317,7 @@ static int SQLITE_TCLAPI test_session_cmd(
break;
}
case 2: /* delete */
Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
break;
@ -363,18 +368,18 @@ static int SQLITE_TCLAPI test_session_cmd(
break;
}
case 9: { /* memory_used */
case 10: { /* memory_used */
sqlite3_int64 nMalloc = sqlite3session_memory_used(pSession);
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nMalloc));
break;
}
case 10: {
case 11: {
sqlite3_int64 nSize = sqlite3session_changeset_size(pSession);
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nSize));
break;
}
case 11: { /* object_config */
case 12: { /* object_config */
struct ObjConfOpt {
const char *zName;
int opt;
@ -384,7 +389,6 @@ 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) ){
@ -541,7 +545,7 @@ static int test_filter_handler(
Tcl_DecrRefCount(pEval);
return res;
}
}
static int test_conflict_handler(
void *pCtx, /* Pointer to TestConflictHandler structure */
@ -1192,6 +1196,127 @@ static int SQLITE_TCLAPI test_sqlite3session_foreach(
return TCL_OK;
}
#include "sqlite3changebatch.h"
typedef struct TestChangebatch TestChangebatch;
struct TestChangebatch {
sqlite3_changebatch *pChangebatch;
};
/*
** Tclcmd: $changebatch add BLOB
** $changebatch zero
** $changebatch delete
*/
static int SQLITE_TCLAPI test_changebatch_cmd(
void *clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
TestChangebatch *p = (TestChangebatch*)clientData;
sqlite3_changebatch *pChangebatch = p->pChangebatch;
struct SessionSubcmd {
const char *zSub;
int nArg;
const char *zMsg;
int iSub;
} aSub[] = {
{ "add", 1, "CHANGESET", }, /* 0 */
{ "zero", 0, "", }, /* 1 */
{ "delete", 0, "", }, /* 2 */
{ 0 }
};
int iSub;
int rc;
if( objc<2 ){
Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
return TCL_ERROR;
}
rc = Tcl_GetIndexFromObjStruct(interp,
objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub
);
if( rc!=TCL_OK ) return rc;
if( objc!=2+aSub[iSub].nArg ){
Tcl_WrongNumArgs(interp, 2, objv, aSub[iSub].zMsg);
return TCL_ERROR;
}
switch( iSub ){
case 0: { /* add */
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
@ -1746,6 +1871,10 @@ int TestSession_Init(Tcl_Interp *interp){
Tcl_CreateObjCommand(interp, p->zCmd, p->xProc, 0, 0);
}
Tcl_CreateObjCommand(
interp, "sqlite3changebatch", test_sqlite3changebatch, 0, 0
);
return TCL_OK;
}

View File

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

View File

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

View File

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

View File

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

View File

@ -9,6 +9,7 @@
two lines and ensure that these files are on the web server. -->
<!--script src="jqterm/jqterm-bundle.min.js"></script>
<link rel="stylesheet" href="jqterm/jquery.terminal.min.css"/-->
<link rel="stylesheet" href="emscripten.css"/>
<style>
/* The following styles are for app-level use. */
:root {
@ -170,31 +171,6 @@
display: flex;
flex-direction: column-reverse;
}
/* emcscript-related styling, used during the module load/intialization processes... */
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
div.emscripten { text-align: center; }
div.emscripten_border { border: 1px solid black; }
#module-spinner { overflow: visible; }
#module-spinner > * {
margin-top: 1em;
}
.spinner {
height: 50px;
width: 50px;
margin: 0px auto;
animation: rotation 0.8s linear infinite;
border-left: 10px solid rgb(0,150,240);
border-right: 10px solid rgb(0,150,240);
border-bottom: 10px solid rgb(0,150,240);
border-top: 10px solid rgb(100,0,200);
border-radius: 100%;
background-color: rgb(200,100,250);
}
@keyframes rotation {
from {transform: rotate(0deg);}
to {transform: rotate(360deg);}
}
</style>
</head>
<body>
@ -297,6 +273,11 @@
</fieldset>
</div>
</div> <!-- #view-split -->
<!-- Maintenance notes:
... TODO... currently being refactored...
-->
<script src="fiddle.js"></script>
</body>
</html>

View File

@ -716,6 +716,7 @@ TESTSRC = \
$(TOP)/ext/fts3/fts3_term.c \
$(TOP)/ext/fts3/fts3_test.c \
$(TOP)/ext/session/test_session.c \
$(TOP)/ext/session/sqlite3changebatch.c \
$(TOP)/ext/recover/sqlite3recover.c \
$(TOP)/ext/recover/dbdata.c \
$(TOP)/ext/recover/test_recover.c \
@ -815,6 +816,7 @@ TESTSRC2 = \
$(TOP)/ext/fts3/fts3_write.c \
$(TOP)/ext/session/sqlite3session.c \
$(TOP)/ext/misc/stmt.c \
$(TOP)/ext/session/test_session.c \
fts5.c
# Header files used by all library source files.

109
manifest
View File

@ -1,11 +1,11 @@
C Doc\supdate\sto\saccount\sfor\s[05073350087b].
D 2024-11-11T09:53:40.171
C Optimize\sdeferred\sallocation\sof\spages\son\sthis\sbranch\sby\savoiding\sBTALLOC_LE.
D 2024-11-11T18:11:34.465
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md e108e1e69ae8e8a59e93c455654b8ac9356a11720d3345df2a4743e9590fb20d
F Makefile.in 580a60aa8deb37060c7973d9399c51c4388f1e0ad0be6555dcd44bc8d2ac3260
F Makefile.linux-generic bd3e3cacd369821a6241d4ea1967395c962dfe3057e38cb0a435cee0e8b789d0
F Makefile.msc a92237976eb92c5efaa0dd2524746aec12c196e12df8d4dbff9543a4648c3312
F Makefile.msc fb7e2c94fceec795ead5fb993b5be415cc02c838930a4416373cec1371181258
F README.md c3c0f19532ce28f6297a71870f3c7b424729f0e6d9ab889616d3587dd2332159
F VERSION 8dc0c3df15fd5ff0622f88fc483533fce990b1cbb2f5fb9fdfb4dbd71eef2889
F art/icon-243x273.gif 9750b734f82fdb3dc43127753d5e6fbf3b62c9f4e136c2fbf573b2f57ea87af5
@ -54,6 +54,7 @@ F autosetup/system.tcl 51d4be76cd9a9074704b584e5c9cbba616202c8468cf9ba8a4f8294a7
F configure 9a00b21dfd13757bbfb8d89b30660a89ec1f8f3a79402b8f9f9b6fc475c3303a x
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
F doc/F2FS.txt c1d4a0ae9711cfe0e1d8b019d154f1c29e0d3abfe820787ba1e9ed7691160fcd
F doc/begin_concurrent.md e694a2a1c623833e7de8b688e81ec30fd4439a78d6b74be61bce0b1e8f590470
F doc/compile-for-unix.md 7d6a5770611ea0643de456b385581923dac7c0a7c3758825dda810d12fc3e5b2
F doc/compile-for-windows.md 17e1491897a117ff0247531a61671b26d487bc1dad25c3894c04ad4fca936a7f
F doc/json-enhancements.md e356fc834781f1f1aa22ee300027a270b2c960122468499bf347bb123ce1ea4f
@ -569,6 +570,8 @@ F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de
F ext/rtree/util/randomshape.tcl 54ee03d0d4a1c621806f7f44d5b78d2db8fac26e0e8687c36c4bd0203b27dbff
F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
F ext/rtree/visual01.txt e9c2564083bcd30ec51b07f881bffbf0e12b50a3f6fced0c222c5c1d2f94ac66
F ext/session/changebatch1.test 2ac3b06cf86d6a5935eee8152d164d90030f29cf70218eaa856b235d4b6c18ab
F ext/session/changebatchfault.test be49c793219bf387ad692a60856b921f0854ad6d
F ext/session/changeset.c 7a1e6a14c7e92d36ca177e92e88b5281acd709f3b726298dc34ec0fb58869cb5
F ext/session/changesetfuzz.c 227076ab0ae4447d742c01ee88a564da6478bbf26b65108bf8fac9cd8b0b24aa
F ext/session/changesetfuzz1.test 15b629004e58d5ffcc852e6842a603775bb64b1ce51254831f3d12b113b616cd
@ -587,7 +590,7 @@ F ext/session/sessionD.test f5c6a762d00bc6ca9d561695c322ba8ecca2bed370486707ef37
F ext/session/sessionE.test b2010949c9d7415306f64e3c2072ddabc4b8250c98478d3c0c4d064bce83111d
F ext/session/sessionF.test d37ed800881e742c208df443537bf29aa49fd56eac520d0f0c6df3e6320f3401
F ext/session/sessionG.test 3efe388282d641b65485b5462e67851002cd91a282dc95b685d085eb8efdad0a
F ext/session/sessionH.test 71bbff6b1abb2c4ac62b84dee53273c37e0b21e5fde3aed80929403e091ef859
F ext/session/sessionH.test 29a5441c3dc0a63fa596d745e64bc6c636e062ae04cd89bc84e32c7d98b1fa9b
F ext/session/session_common.tcl e5598096425486b363718e2cda48ee85d660c96b4f8ea9d9d7a4c3ef514769da
F ext/session/session_speed_test.c dcf0ef58d76b70c8fbd9eab3be77cf9deb8bc1638fed8be518b62d6cbdef88b3
F ext/session/sessionalter.test e852acb3d2357aac7d0b920a2109da758c4331bfdf85b41d39aa3a8c18914f65
@ -610,10 +613,12 @@ F ext/session/sessionrowid.test 85187c2f1b38861a5844868126f69f9ec62223a03449a98a
F ext/session/sessionsize.test 8fcf4685993c3dbaa46a24183940ab9f5aa9ed0d23e5fb63bfffbdb56134b795
F ext/session/sessionstat1.test 5e718d5888c0c49bbb33a7a4f816366db85f59f6a4f97544a806421b85dc2dec
F ext/session/sessionwor.test 6fd9a2256442cebde5b2284936ae9e0d54bde692d0f5fd009ecef8511f4cf3fc
F ext/session/sqlite3session.c 3d0a7f0f7a1c946e01818c716a55a40ae30542a29a9045cb05daf7fb658cdafa
F ext/session/sqlite3session.h 683ccbf16e2c2521661fc4c1cf918ce57002039efbcabcd8097fa4bca569104b
F ext/session/test_session.c aa29abdcc9011ac02f4fa38e8ede226106eaeee7c3ea7d8b2b999a124e0c368c
F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c
F ext/session/sqlite3changebatch.c d5553b79e012ee2cb06c0a96bdf9dfe19e66354390ea0036cc46c4953142d517
F ext/session/sqlite3changebatch.h e72016998c9a22d439ddfd547b69e1ebac810c24
F ext/session/sqlite3session.c 6e8fcd7f83d765d17e3f58f528d298e5742759faf2207b8c3b44dcd43d1c6640
F ext/session/sqlite3session.h a1f5b299b2cd41ddb638c4b3b20cf00413aada7e6d897e9534490c3c74a8c873
F ext/session/test_session.c c17515ec28b17178b2c4ae97e3f9927bd22da585a205fae13a1b400a4abd0394
F ext/wasm/EXPORTED_FUNCTIONS.fiddle 7fb73f7150ab79d83bb45a67d257553c905c78cd3d693101699243f36c5ae6c3
F ext/wasm/GNUmakefile 311aa0d5edc7006409962cc77cc26560d92f9be69c2c4302e8bbc68189fd02db
F ext/wasm/README-dist.txt 6382cb9548076fca472fb3330bbdba3a55c1ea0b180ff9253f084f07ff383576
F ext/wasm/README.md a8a2962c3aebdf8d2104a9102e336c5554e78fc6072746e5daf9c61514e7d193
@ -622,7 +627,7 @@ F ext/wasm/SQLTester/SQLTester.mjs 66e1adc3d79467b68e3e40614fd42c1a577c7e219ec09
F ext/wasm/SQLTester/SQLTester.run.mjs 57f2adb33f43f2784abbf8026c1bfd049d8013af1998e7dcb8b50c89ffc332e0
F ext/wasm/SQLTester/index.html 64f3435084c7d6139b08d1f2a713828a73f68de2ae6a3112cbb5980d991ba06f
F ext/wasm/SQLTester/touint8array.c 2d5ece04ec1393a6a60c4bf96385bda5e1a10ad49f3038b96460fc5e5aa7e536
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-core 2bcbbfe3b95c043ed6037e2708a2ee078d212dd1612c364f93588d8dc97300fe
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api c5eaceabb9e759aaae7d3101a4a3e542f96ab2c99d89a80ce20ec18c23115f33
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-extras fe40d6d758646e38f8b15f709044951e10884214f5453d35502100179c388c13
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-see fb29e62082a658f0d81102488414d422c393c4b20cc2f685b216bc566237957b
F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287
@ -644,7 +649,7 @@ F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js bb5e96cd0fd6e1e54538256433f1c60a
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 9b86ca2d8276cf919fbc9ba2a10e9786033b64f92c2db844d951804dee6c4b4e
F ext/wasm/api/sqlite3-vtab-helper.c-pp.js e809739d71e8b35dfe1b55d24d91f02d04239e6aef7ca1ea92a15a29e704f616
F ext/wasm/api/sqlite3-wasm.c 83f5e9f998e9fa4261eb84e9f092210e3ffe03895119f5ded0429eb34ab9d2be
F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js 46f303ba8ddd1b2f0a391798837beddfa72e8c897038c8047eda49ce7d5ed46b
F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js c5ac33e39f21a3481812d7333ca6e18853640d423a01960ca8dbc6e7c5c3c21c
F ext/wasm/api/sqlite3-worker1.c-pp.js 5e8706c2c4af2a57fbcdc02f4e7ef79869971bc21bb8ede777687786ce1c92d5
F ext/wasm/batch-runner-sahpool.html e9a38fdeb36a13eac7b50241dfe7ae066fe3f51f5c0b0151e7baee5fce0d07a7
F ext/wasm/batch-runner-sahpool.js 54a3ac228e6c4703fe72fb65c897e19156263a51fe9b7e21d2834a45e876aabd
@ -668,8 +673,8 @@ F ext/wasm/dist.make 653e212c1e84aa3be168d62a10616ccea45ee9585b0192745d2706707a5
F ext/wasm/example_extra_init.c 2347cd69d19d839ef4e5e77b7855103a7fe3ef2af86f2e8c95839afd8b05862f
F ext/wasm/fiddle.make d4969f0322a582c57a22ce3541f10a5b09a609d14eab32891f613f43b3c14d8b
F ext/wasm/fiddle/fiddle-worker.js 850e66fce39b89d59e161d1abac43a181a4caa89ddeea162765d660277cd84ce
F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08
F ext/wasm/fiddle/fiddle.js b444a5646a9aac9f3fc06c53d78af5e1912eb235d69a8e6010723e4eb0e9d4a1
F ext/wasm/fiddle/index.html c79b1741cbeba78f88af0a84cf5ec7de87a909a6a8d10a369b1f4824c66c2088
F ext/wasm/index-dist.html 564b5ec5669676482c5a25dea9e721d8eafed426ecb155f93d29aeff8507511f
F ext/wasm/index.html e4bbffdb3d40eff12b3f9c7abedef91787e2935620b7f8d40f2c774b80ad8fa9
F ext/wasm/jaccwabyt/jaccwabyt.js 1264710db3cfbcb6887d95665b7aeba60c1126eaef789ca4cf1a4a17d5bc7f54
@ -696,7 +701,7 @@ F ext/wasm/tests/opfs/concurrency/test.js d08889a5bb6e61937d0b8cbb78c9efbefbf65a
F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2
F ext/wasm/wasmfs.make bc8bb227f35d5bd3863a7bd2233437c37472a0d81585979f058f9b9b503bef35
F magic.txt 5ade0bc977aa135e79e3faaea894d5671b26107cc91e70783aa7dc83f22f3ba0
F main.mk efb8f627c5793126ff7a86d698676f4e6509a296b0b113ec284e6f723561f0bc
F main.mk d98a373400950321b3823d789d92d6b00c498ff20d8e8f966bf58d33ca503043
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
F mptest/crash01.test 61e61469e257df0850df4293d7d4d6c2af301421
@ -712,12 +717,12 @@ F src/analyze.c 9a8b67239d899ac12289db5db3f5bfe7f7a0ad1277f80f87ead1d048085876eb
F src/attach.c f35bb8cc1fcdde8f6815a7ef09ae413bcac71821d530796800ba24b3c7da1e80
F src/auth.c 54ab9c6c5803b47c0d45b76ce27eff22a03b4b1f767c5945a3a4eb13aa4c78dc
F src/backup.c 5c97e8023aab1ce14a42387eb3ae00ba5a0644569e3476f38661fa6f824c3523
F src/bitvec.c 9eac5f42c11914d5ef00a75605bb205e934f435c579687f985f1f8b0995c8645
F src/bitvec.c 501daeef838fa82a9fb53540d72f29e3d9172c8867f1e19f94f681e2e20b966e
F src/btmutex.c 79a43670447eacc651519a429f6ece9fd638563cf95b469d6891185ddae2b522
F src/btree.c 63ca6b647342e8cef643863cd0962a542f133e1069460725ba4461dcda92b03c
F src/btree.h 18e5e7b2124c23426a283523e5f31a4bff029131b795bb82391f9d2f3136fc50
F src/btreeInt.h 98aadb6dcb77b012cab2574d6a728fad56b337fc946839b9898c4b4c969e30b6
F src/build.c c6b09342d870a509529244ed8e19b4175a261f2e3163c199241d69e1d8a57607
F src/btree.c 9e32862e7369e8e5ee3f60f084cf40fb41cc1a8ba52a61093d08ecb3f941fd6c
F src/btree.h df26089b055c4cffe243e5bc98edc729c4ad880bfeb8f229fd16248e4cec10ff
F src/btreeInt.h 8efd30e75e35a3c6a1c4dad7410d4ddfcd560f5f46401b208fa79eceef34525a
F src/build.c 196415a804a362dbae6d1939d6b8327d527fe08fb399834191e06d22a33a3aa4
F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
F src/ctime.c d35723024b963edce9c0fad5b3303e8bb9266083784844baed10a6dedfe26f3b
@ -728,7 +733,7 @@ F src/delete.c 03a77ba20e54f0f42ebd8eddf15411ed6bdb06a2c472ac4b6b336521bf7cea42
F src/expr.c a9d9f5fdfbdd3b2c94d7af1b11f181464b8a641736cf32cb92fa3c5e7ecb30df
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c 928ed2517e8732113d2b9821aa37af639688d752f4ea9ac6e0e393d713eeb76f
F src/func.c fa138d44348e189817542f6efa6232420b3e0081c835ced65883adc7fd777d65
F src/func.c f9308e7a4ab350afabbb239c4580e6c48a84b8933e67a457ddb3cb5fc6c0e511
F src/global.c a19e4b1ca1335f560e9560e590fc13081e21f670643367f99cb9e8f9dc7d615b
F src/hash.c 9ee4269fb1d6632a6fecfb9479c93a1f29271bddbbaf215dd60420bcb80c7220
F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51
@ -738,7 +743,7 @@ F src/insert.c f8d1a0f8ee258411009c6b7f2d93170e351bd19f5ad89d57e1180644297cbe70
F src/json.c 68a98c020c22127f2d65f08855f7fc7460ff352a6ce0b543d8931dde83319c22
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36
F src/main.c 9f4286302727f58fddc03a820d24cb7618a1e27473501792fbe979726f846d1f
F src/main.c 5f612d6ae459c975ead07d485e2f5037d6aeca586434f3e9d5665f5c7e90e320
F src/malloc.c 410e570b30c26cc36e3372577df50f7a96ee3eed5b2b161c6b6b48773c650c5e
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c 3bb59158c38e05f6270e761a9f435bf19827a264c13d1631c58b84bdc96d73b2
@ -759,34 +764,34 @@ F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63
F src/os_common.h 6c0eb8dd40ef3e12fe585a13e709710267a258e2c8dd1c40b1948a1d14582e06
F src/os_kv.c 4d39e1f1c180b11162c6dc4aa8ad34053873a639bac6baae23272fc03349986a
F src/os_setup.h 6011ad7af5db4e05155f385eb3a9b4470688de6f65d6166b8956e58a3d872107
F src/os_unix.c c84a3add1e480499261a41d77d3f87d18f27aaebec6376655c177a3886a5b67c
F src/os_unix.c 2ea7d70a6650092a0dfbdf5948af64cc38023e6d3ef14082a40a4b3bec75acbc
F src/os_win.c 69fa1aaff68270423c85cff4327ba17ef99a1eb017e1a2bfb97416d9b8398b05
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c 9656ad4e8331efb8a4f94f7a0c6440b98caea073950a367ea0c728a53b8e62c9
F src/pager.h 4b1140d691860de0be1347474c51fee07d5420bd7f802d38cbab8ea4ab9f538a
F src/parse.y 8ec56598aa0df92428627502267d0d1c9778cc27308f8ffd31dfb2d017a8755f
F src/pager.c 4c8b569b6745efd0e2af57ac00b9a4c73451b8aa0ed51fedecbaa7523c1111bb
F src/pager.h e2df6b92e0402bc8d516016f361da82758b7d7769ef1a18e2abeadece18103e0
F src/parse.y 091a23e828f9aa1ec560340162deab14b0563664a1ffe7d9073c4d99215d39fa
F src/pcache.c 588cc3c5ccaaadde689ed35ce5c5c891a1f7b1f4d1f56f6cf0143b74d8ee6484
F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5
F src/pcache1.c 49516ad7718a3626f28f710fa7448ef1fce3c07fd169acbb4817341950264319
F src/pragma.c a2ec3657a953fa7dea7c1e680e4358b6ce6ae570b6c5234e0f5ef219d308d223
F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7
F src/pragma.h 6ebbdee90ed56a892d2c728e27fd9c1ce48c8a28841888d0c6c147946b38cb25
F src/prepare.c 1832be043fce7d489959aae6f994c452d023914714c4d5457beaed51c0f3d126
F src/printf.c 6a87534ebfb9e5346011191b1f3a7ebc457f5938c7e4feeea478ecf53f6a41b2
F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
F src/random.c a3e70f8515721ff24d2c0e6afd83923e8faab5ab79ececea4c1bf9fe4049fbb2
F src/resolve.c c8a5372b97b2a2e972a280676f06ddb5b74e885d3b1f5ce383f839907b57ef68
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
F src/select.c 4b14337a2742f0c0beeba490e9a05507e9b4b12184b9cd12773501d08d48e3fe
F src/select.c 108baa344f34a17a0723067d28b5b4b7da937d02ddc2d7c1a39a4a4815628ca4
F src/shell.c.in bb97e0afcc4a73b73f38cc868330854a2df109095a7a10182ddfdd261fbec312
F src/sqlite.h.in 599203aa6cf3a662f879e7581f4b7f2678738c0b7c71ddda3c0cb5c59867c399
F src/sqlite.h.in 5349bff5d19fb47f6da373fb3e9e6abaaa427b965d0fae070286f130ec94cd07
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54
F src/sqliteInt.h 77be043f8694f4a8702d0ee882022b2e5a6489a0493e77c5d9a73f1efc5a2cc1
F src/sqliteInt.h d0972a1e590cd514b84b4a09f44c860e61884896f4175c753732df897c223788
F src/sqliteLimit.h 6993c9cfe3af5b8169ae0e5f15627fc15596726d4f1dc90a221309f79715ce88
F src/status.c cb11f8589a6912af2da3bb1ec509a94dd8ef27df4d4c1a97e0bcf2309ece972b
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
F src/tclsqlite.c ff2dc3ec1bd318ee7a45d6b246a367703d5fb2a4c8da99d675ee7eb987b3a153
F src/tclsqlite.h 65e2c761446e1c9fa0342b7d2612a703483643c8b6a316d12a65b745a4727395
F src/test1.c 2d507751bfb4aa254dc22588ef1e3c5c5cfcb2e636d0e6e1fa0bbd307669c2a8
F src/test1.c 9c64f39482d44bb82f610179256951140b6917e7a08712d3dd604d3a0757275a
F src/test2.c 7ebc518e6735939d8979273a6f7b1d9b5702babf059f6ad62499f7f60a9eb9a3
F src/test3.c e7573aa0f78ee4e070a4bc8c3493941c1aa64d5c66d4825c74c0f055451f432b
F src/test4.c 13e57ae7ec7a959ee180970aef09deed141252fe9bb07c61054f0dfa4f1dfd5d
@ -799,7 +804,7 @@ F src/test_backup.c bd901e3c116c7f3b3bbbd4aae4ce87d99b400c9cbb0a9e7b4610af451d97
F src/test_bestindex.c 3401bee51665cbf7f9ed2552b5795452a8b86365e4c9ece745b54155a55670c6
F src/test_blob.c bcdf6a6c22d0bcc13c41479d63692ef413add2a4d30e1e26b9f74ab85b9fb4d5
F src/test_btree.c 28283787d32b8fa953eb77412ad0de2c9895260e4e5bd5a94b3c7411664f90d5
F src/test_config.c bff5e1625c007f14a9ea4d346b6a741149b5e1f885c1c7ae69bb28a8ddade151
F src/test_config.c 872eb3cab4573b8b9512454bf11bc68092a2b61cffd9b16465414c70a3357d8a
F src/test_delete.c e2fe07646dff6300b48d49b2fee2fe192ed389e834dd635e3b3bac0ce0bf9f8f
F src/test_demovfs.c 3efa2adf4f21e10d95521721687d5ca047aea91fa62dd8cc22ac9e5a9c942383
F src/test_devsym.c 649434ed34d0b03fbd5a6b42df80f0f9a7e53f94dd1710aad5dd8831e91c4e86
@ -839,16 +844,16 @@ F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c 3f703cacdab728d7741e5a6ac242006d74fe1c2754d4f03ed889d7253259bd68
F src/treeview.c 88aa39b754f5ef7214385c1bbbdd2f3dc20efafeed0cf590e8d1199b9c6e44aa
F src/trigger.c 0bb986a5b96047fd597c6aac28588853df56064e576e6b81ba777ef2ccaac461
F src/update.c 0e01aa6a3edf9ec112b33eb714b9016a81241497b1fb7c3e74332f4f71756508
F src/update.c 50baf763bfe5f3bf6b0e1ee1e6c0b7d10dca2ec28f2ff61b787d78d0edf81ec4
F src/upsert.c 215328c3f91623c520ec8672c44323553f12caeb4f01b1090ebdca99fdf7b4f1
F src/utf.c 8b29d9a5956569ea2700f869669b8ef67a9662ee5e724ff77ab3c387e27094ba
F src/util.c ceebf912f673247e305f16f97f0bb7285fca1d37413b79680714a553a9021d33
F src/vacuum.c b763b6457bd058d2072ef9364832351fd8d11e8abf70cbb349657360f7d55c40
F src/vdbe.c 8a6eb02823b424b273614bae41579392a5c495424592b60423dd2c443a583df0
F src/vacuum.c 25e407a6dc8b288fa4295b3d92fa9ce9318503e84df53cdf403a50fccbc1ba31
F src/vdbe.c 09fdbd710d193968819a491cb76d2fd1d5f33a9a7997c739f6bf7fe8acea2361
F src/vdbe.h c2549a215898a390de6669cfa32adba56f0d7e17ba5a7f7b14506d6fd5f0c36a
F src/vdbeInt.h 2da01c73e8e3736a9015d5b04aa04d209bc9023d279d237d4d409205e921ea1e
F src/vdbeapi.c 6353de05e8e78e497ccb33381ba5662ccc11c0339e5b1455faff01b6dacc3075
F src/vdbeaux.c f0706ad786b8a6c5bc7ea622f3916c2ba2b883abc872d0b4911c4f021945c0e5
F src/vdbeaux.c 38c7c7fe45645c122c36866b0ba065898d5ab23834a7b19b5a71b09337e5a4fe
F src/vdbeblob.c 255be187436da38b01f276c02e6a08103489bbe2a7c6c21537b7aecbe0e1f797
F src/vdbemem.c df568ef0187e4be2788c35174f6d9b8566ab9475f9aff2d73907ed05aa5684b2
F src/vdbesort.c d0a3c7056c081703c8b6d91ad60f17da5e062a5c64bf568ed0fa1b5f4cae311f
@ -856,8 +861,8 @@ F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf8
F src/vdbevtab.c fc46b9cbd759dc013f0b3724549cc0d71379183c667df3a5988f7e2f1bd485f3
F src/vtab.c 316cd48e9320660db3047cd306cd056e4361180cebb4d0f10a39244e10c11422
F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c 8b7e309a8012659ac9275ad8cdcc6acaf73fa04b1090e38a01335f230fd10681
F src/wal.h ba252daaa94f889f4b2c17c027e823d9be47ce39da1d3799886bbd51f0490452
F src/wal.c 3f6502c1e35aafcc972d1fbc085e7d92e12073fd9cec1f45c47aeefdb94b9755
F src/wal.h e9aeb67102d9b9a0b089b80bd6136a16dd6360ac3daa731f2b71c6d4f8341717
F src/walker.c d5006d6b005e4ea7302ad390957a8d41ed83faa177e412f89bc5600a7462a014
F src/where.c 4de9e7ca5f49e4a21c1d733e2b2fbbc8b62b1a157a58a562c569da84cfcb005b
F src/whereInt.h 1e36ec50392f7cc3d93d1152d4338064cd522b87156a0739388b7e273735f0ca
@ -943,6 +948,7 @@ F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f
F test/badutf2.test f310fd3b24a491b6b77bccdf14923b85d6ebcce751068c180d93a6b8ff854399
F test/basexx1.test d8a50f0744b93dca656625597bcd3499ff4b9a4ea2a82432b119b7d46e3e0c08
F test/bc_common.tcl b5e42d80305be95697e6370e015af571e5333a1c
F test/bc_test1.c e0a092579552e066ed4ce7bcdaecfa69c4aacc8d
F test/bestindex1.test 856a453dff8c68b4568601eed5a8b5e20b4763af9229f3947c215729ed878db0
F test/bestindex2.test 394ff8fbf34703391247116d6a44e1c50ee7282236ee77909044573cefc37bc0
F test/bestindex3.test 34bea272b0e0f835651b16a3931dbe7ac927039be6b2e1cb617bbe1d584b492b
@ -1016,6 +1022,16 @@ F test/collateB.test 1e68906951b846570f29f20102ed91d29e634854ee47454d725f2151eca
F test/colmeta.test 2c765ea61ee37bc43bbe6d6047f89004e6508eb1
F test/colname.test 387e880eeac0889900f7b3e9703c375515f5d323f71fd4f2bb5428a4ac8e2023
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 b705cddf025a675d3c13d62fa78ab1e2696fb8e07a3d7cccce1596ff8b301492
F test/conflict2.test 5557909ce683b1073982f5d1b61dfb1d41e369533bfdaf003180c5bc87282dd1
F test/conflict3.test 81865d9599609aca394fb3b9cd5f561d4729ea5b176bece3644f6ecb540f88ac
@ -1042,7 +1058,7 @@ F test/corruptJ.test 4d5ccc4bf959464229a836d60142831ef76a5aa4
F test/corruptK.test ac13504593d89d69690d45479547616ed12644d42b5cb7eeb2e759a76fc23dcb
F test/corruptL.test 652fc8ac0763a6fd3eb28b951d481924167b2d9936083bcc68253b2274a0c8fe
F test/corruptM.test 7d574320e08c1b36caa3e47262061f186367d593a7e305d35f15289cc2c3e067
F test/corruptN.test 40bc47aee4af9aadff902be43f14d69dc17b3731448dad6c7cc722da913f1455
F test/corruptN.test a034bb217bebd8d007625dfb078e76ec3d53515052dbceb68bd47b2c27674d5c
F test/cost.test cc434a026b1e9d0d98137a147e24e5daf1b1ad09e9ff7da63b34c83ddd136d92
F test/count.test cd4bd531066e8d77ef8fe1e3fc8253d042072e117ccab214b290cf83f1602249
F test/countofview.test 4088e461a10ee33e69803c177a69aa1d7bba81a9ffc2df66d76465a22ca7fdfc
@ -1186,7 +1202,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 c7f414fe29b97a478d15c90382c4ae077a2bbd2283bf8c63bf66dadaaed3edb8
F test/fts3corrupt4.test 3cd1654b65d540907695c6b3a704a610dec528d4b43e092086ee22d2f51c45da
F test/fts3corrupt5.test 0549f85ec4bd22e992f645f13c59b99d652f2f5e643dac75568bfd23a6db7ed5
F test/fts3corrupt6.test f417c910254f32c0bc9ead7affa991a1d5aec35b3b32a183ffb05eea78289525
F test/fts3corrupt7.test 1da31776e24bb91d3c028e663456b61280b121a74496ccf2fef3fe33790ad2b0
@ -1478,6 +1494,7 @@ F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660
F test/nan.test 73ea63ab43668313e2f8cc9ef9e9a966672c7934f3ce76926fbe991235d07d91
F test/nockpt.test 8c43b25af63b0bd620cf1b003529e37b6f1dc53bd22690e96a1bd73f78dde53a
F test/nolock.test f196cf8b8fbea4e2ca345140a2b3f3b0da45c76e
F test/noop_update.test 74d71bbe5d37f7861cc2402ed620eb165b8e6b00159cdcada7df716420336651
F test/normalize.test f23b6c5926c59548635fcf39678ac613e726121e073dd902a3062fbb83903b72
F test/notify1.test 669b2b743618efdc18ca4b02f45423d5d2304abf
F test/notify2.test 2ecabaa1305083856b7c39cf32816b612740c161
@ -1518,7 +1535,6 @@ F test/pagerfault2.test caf4c7facb914fd3b03a17b31ae2b180c8d6ca1f
F test/pagerfault3.test 1003fcda009bf48a8e22a516e193b6ef0dd1bbd8
F test/pageropt.test 84e4cc5cbca285357f7906e99b21be4f2bf5abc0
F test/pagesize.test 5769fc62d8c890a83a503f67d47508dfdc543305
F test/parser1.test 6ccdf5e459a5dc4673d3273dc311a7e9742ca952dd0551a6a6320d27035ce4b3
F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
F test/pendingrace.test e99efc5ab3584da3dfc8cd6a0ec4e5a42214820574f5ea24ee93f1d84655f463
@ -1908,6 +1924,7 @@ F test/triggerG.test 2b816093c91ba73c733cfa8aedcc210ad819d72a98b1da30768a3c56505
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
@ -1986,7 +2003,7 @@ F test/vtabdistinct.test 7688f0889358f849fd60bbfde1ded38b014b18066076d4bfbb75395
F test/vtabdrop.test 65d4cf6722972e5499bdaf0c0d70ee3b8133944a4e4bc31862563f32a7edca12
F test/vtabrhs1.test 9b5ecbc74a689500c33a4b2b36761f9bcc22fcc4e3f9d21066ee0c9c74cf5f6c
F test/wal.test 519c550255c78f55959e9159b93ebbfad2b4e9f36f5b76284da41f572f9d27da
F test/wal2.test 44fe1cb4935dbbddfa0a34c2c4fd90f0ba8654d59b83c4136eb90fb327fd264f
F test/wal2.test e89ca97593b5e92849039f6b68ce1719a853ef20fa22c669ec1ac452fbc31cab
F test/wal3.test 5de023bb862fd1eb9d2ad26fa8d9c43abb5370582e5b08b2ae0d6f93661bc310
F test/wal4.test 4744e155cd6299c6bd99d3eab1c82f77db9cdb3c
F test/wal5.test 9c11da7aeccd83a46d79a556ad11a18d3cb15aa9
@ -1995,7 +2012,7 @@ F test/wal64k.test 2a525c0f45d709bae3765c71045ccec5df7d100ccbd3a7860fdba46c9addb
F test/wal7.test 2ae8f427d240099cc4b2dfef63cff44e2a68a1bd
F test/wal8.test d9df3fba4caad5854ed69ed673c68482514203c8
F test/wal9.test 378e76a9ad09cd9bee06c172ad3547b0129a6750
F test/wal_common.tcl 4589f701d5527ace2eba43823c96c2177e1f9dd2a6098256ee2203a0a313c13a
F test/wal_common.tcl 204d1721ac13c5e0c7fae6380315b5ab7f4e8423f580d826c5e9df1995cb018d
F test/walbak.test 018d4e5a3d45c6298d11b99f09a8ef6876527946
F test/walbig.test f437473a16cfb314867c6b5d1dbcd519e73e3434
F test/walblock.test be48f3a75eff0b4456209f26b3ce186c2015497d
@ -2142,7 +2159,7 @@ F tool/mkmsvcmin.tcl d76c45efda1cce2d4005bcea7b8a22bb752e3256009f331120fb4fecb14
F tool/mkopcodec.tcl 33d20791e191df43209b77d37f0ff0904620b28465cca6990cf8d60da61a07ef
F tool/mkopcodeh.tcl 2b4e6967a670ef21bf53a164964c35c6163277d002a4c6f56fa231d68c88d023
F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa
F tool/mkpragmatab.tcl 32e359ccb21011958a821955254bd7a5fa7915d01a8c16fed91ffc8b40cb4adf
F tool/mkpragmatab.tcl d03737ad2ac48928d8225d1c571e487b9b7e73e8c1bdcabd61d69b244614408b
F tool/mkshellc.tcl 2bc29c201933ae72a16a79070fe80aded80c24ea487ecd2f8df20c2973c87bfc
F tool/mksourceid.c 36aa8020014aed0836fd13c51d6dc9219b0df1761d6b5f58ff5b616211b079b9
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
@ -2198,8 +2215,8 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350
F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
F tool/warnings.sh 49a486c5069de041aedcbde4de178293e0463ae9918ecad7539eedf0ec77a139
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
P 05073350087b368312515134bdf9a266eb8289a065f208421fe08aa38b562d4b
R 3e7bc73740255e6a3248a16815c573a3
U stephan
Z 9ae48f5cefd523f44e5eeeffcff1033d
P 52bd7953f1ff26942e6e4227dd112d8454f60a98ef1c5f62ef123085c769069a
R 51355028405b6ebf515d12a5a6724571
U dan
Z dee158dc87e41100c2ffca6b82aee077
# Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
b81976c520fbad0bbdbbb877fe85691bcda25c12cf5597cfe224fb6306cd65b3
0812161025272851af6233f183a6abdee5c583decc688c4763965b50e7ab1b9d

View File

@ -171,6 +171,12 @@ int sqlite3BitvecSet(Bitvec *p, u32 i){
if( p==0 ) return SQLITE_OK;
assert( i>0 );
assert( i<=p->iSize );
if( i>p->iSize || i==0 ){
sqlite3_log(SQLITE_ERROR,
"Bitvec: setting bit %d of bitvec size %d\n", (int)i, (int)p->iSize
);
abort();
}
i--;
while((p->iSize > BITVEC_NBIT) && p->iDivisor) {
u32 bin = i/p->iDivisor;

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -232,6 +232,7 @@
typedef struct MemPage MemPage;
typedef struct BtLock BtLock;
typedef struct CellInfo CellInfo;
typedef struct BtreePtrmap BtreePtrmap;
/*
** This is a magic string that appears at the beginning of every
@ -275,6 +276,9 @@ struct MemPage {
u8 intKey; /* True if table b-trees. False for index b-trees */
u8 intKeyLeaf; /* True if the leaf of an intKey table */
Pgno pgno; /* Page number for this page */
#ifndef SQLITE_OMIT_CONCURRENT
Pgno pgnoRoot; /* Root page of b-tree that this page belongs to */
#endif
/* Only the first 8 bytes (above) are zeroed by pager.c when a new page
** is allocated. All fields that follow must be initialized before use */
u8 leaf; /* True if a leaf page */
@ -456,6 +460,9 @@ struct BtShared {
Btree *pWriter; /* Btree with currently open write transaction */
#endif
u8 *pTmpSpace; /* Temp space sufficient to hold a single cell */
#ifndef SQLITE_OMIT_CONCURRENT
BtreePtrmap *pMap;
#endif
int nPreformatSize; /* Size of last cell written by TransferRow() */
};
@ -673,12 +680,19 @@ struct BtCursor {
** (sqliteMallocRaw), it is not possible to use conditional compilation.
** So, this macro is defined instead.
*/
#ifndef SQLITE_OMIT_AUTOVACUUM
#define ISAUTOVACUUM(pBt) (pBt->autoVacuum)
#else
#ifdef SQLITE_OMIT_AUTOVACUUM
#define ISAUTOVACUUM(pBt) 0
#else
#define ISAUTOVACUUM(pBt) (pBt->autoVacuum)
#endif
#ifdef SQLITE_OMIT_CONCURRENT
# define ISCONCURRENT 0
#else
# define ISCONCURRENT (pBt->pMap!=0)
#endif
#define REQUIRE_PTRMAP (ISAUTOVACUUM(pBt) || ISCONCURRENT)
/*
** This structure is passed around through all the PRAGMA integrity_check

View File

@ -89,8 +89,10 @@ void sqlite3TableLock(
u8 isWriteLock, /* True for a write lock */
const char *zName /* Name of the table to be locked */
){
#ifdef SQLITE_OMIT_CONCURRENT
if( iDb==1 ) return;
if( !sqlite3BtreeSharable(pParse->db->aDb[iDb].pBt) ) return;
#endif
lockTable(pParse, iDb, iTab, isWriteLock, zName);
}
@ -5205,7 +5207,7 @@ void sqlite3BeginTransaction(Parse *pParse, int type){
}
v = sqlite3GetVdbe(pParse);
if( !v ) return;
if( type!=TK_DEFERRED ){
if( type==TK_IMMEDIATE || type==TK_EXCLUSIVE ){
for(i=0; i<db->nDb; i++){
int eTxnType;
Btree *pBt = db->aDb[i].pBt;
@ -5220,7 +5222,7 @@ void sqlite3BeginTransaction(Parse *pParse, int type){
sqlite3VdbeUsesBtree(v, i);
}
}
sqlite3VdbeAddOp0(v, OP_AutoCommit);
sqlite3VdbeAddOp3(v, OP_AutoCommit, 0, 0, (type==TK_CONCURRENT));
}
/*

View File

@ -559,8 +559,9 @@ static void randomFunc(
sqlite3_value **NotUsed2
){
sqlite_int64 r;
sqlite3 *db = sqlite3_context_db_handle(context);
UNUSED_PARAMETER2(NotUsed, NotUsed2);
sqlite3_randomness(sizeof(r), &r);
sqlite3FastRandomness(&db->sPrng, sizeof(r), &r);
if( r<0 ){
/* We need to prevent a random number of 0x8000000000000000
** (or -9223372036854775808) since when you do abs() of that
@ -586,6 +587,7 @@ static void randomBlob(
){
sqlite3_int64 n;
unsigned char *p;
sqlite3 *db = sqlite3_context_db_handle(context);
assert( argc==1 );
UNUSED_PARAMETER(argc);
n = sqlite3_value_int64(argv[0]);
@ -594,7 +596,7 @@ static void randomBlob(
}
p = contextMalloc(context, n);
if( p ){
sqlite3_randomness(n, p);
sqlite3FastRandomness(&db->sPrng, n, p);
sqlite3_result_blob(context, (char*)p, n, sqlite3_free);
}
}

View File

@ -3300,7 +3300,7 @@ static int openDatabase(
db->aDb = db->aDbStatic;
db->lookaside.bDisable = 1;
db->lookaside.sz = 0;
sqlite3FastPrngInit(&db->sPrng);
assert( sizeof(db->aLimit)==sizeof(aHardLimit) );
memcpy(db->aLimit, aHardLimit, sizeof(db->aLimit));
db->aLimit[SQLITE_LIMIT_WORKER_THREADS] = SQLITE_DEFAULT_WORKER_THREADS;
@ -5020,6 +5020,35 @@ void sqlite3_snapshot_free(sqlite3_snapshot *pSnapshot){
}
#endif /* SQLITE_ENABLE_SNAPSHOT */
SQLITE_EXPERIMENTAL int sqlite3_wal_info(
sqlite3 *db, const char *zDb,
unsigned int *pnPrior, unsigned int *pnFrame
){
int rc = SQLITE_OK;
#ifndef SQLITE_OMIT_WAL
Btree *pBt;
int iDb;
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) ){
return SQLITE_MISUSE_BKPT;
}
#endif
sqlite3_mutex_enter(db->mutex);
iDb = sqlite3FindDbName(db, zDb);
if( iDb<0 ){
return SQLITE_ERROR;
}
pBt = db->aDb[iDb].pBt;
rc = sqlite3PagerWalInfo(sqlite3BtreePager(pBt), pnPrior, pnFrame);
sqlite3_mutex_leave(db->mutex);
#endif /* SQLITE_OMIT_WAL */
return rc;
}
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
/*
** Given the name of a compile-time option, return true if that option

View File

@ -1224,6 +1224,10 @@ struct unixInodeInfo {
sem_t *pSem; /* Named POSIX semaphore */
char aSemName[MAX_PATHNAME+2]; /* Name of that semaphore */
#endif
#ifdef SQLITE_SHARED_MAPPING
sqlite3_int64 nSharedMapping; /* Size of mapped region in bytes */
void *pSharedMapping; /* Memory mapped region */
#endif
};
/*
@ -1380,6 +1384,13 @@ static void releaseInodeInfo(unixFile *pFile){
pInode->nRef--;
if( pInode->nRef==0 ){
assert( pInode->pShmNode==0 );
#ifdef SQLITE_SHARED_MAPPING
if( pInode->pSharedMapping ){
osMunmap(pInode->pSharedMapping, pInode->nSharedMapping);
pInode->pSharedMapping = 0;
pInode->nSharedMapping = 0;
}
#endif
sqlite3_mutex_enter(pInode->pLockMutex);
closePendingFds(pFile);
sqlite3_mutex_leave(pInode->pLockMutex);
@ -2243,6 +2254,14 @@ static int nolockUnlock(sqlite3_file *NotUsed, int NotUsed2){
** Close the file.
*/
static int nolockClose(sqlite3_file *id) {
#ifdef SQLITE_SHARED_MAPPING
unixFile *pFd = (unixFile*)id;
if( pFd->pInode ){
unixEnterMutex();
releaseInodeInfo(pFd);
unixLeaveMutex();
}
#endif
return closeUnixFile(id);
}
@ -4062,6 +4081,9 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
*(i64*)pArg = pFile->mmapSizeMax;
if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){
pFile->mmapSizeMax = newLimit;
#ifdef SQLITE_SHARED_MAPPING
if( pFile->pInode==0 )
#endif
if( pFile->mmapSize>0 ){
unixUnmapfile(pFile);
rc = unixMapfile(pFile, -1);
@ -5261,6 +5283,9 @@ static int unixShmUnmap(
*/
static void unixUnmapfile(unixFile *pFd){
assert( pFd->nFetchOut==0 );
#ifdef SQLITE_SHARED_MAPPING
if( pFd->pInode ) return;
#endif
if( pFd->pMapRegion ){
osMunmap(pFd->pMapRegion, pFd->mmapSizeActual);
pFd->pMapRegion = 0;
@ -5392,6 +5417,28 @@ static int unixMapfile(unixFile *pFd, i64 nMap){
nMap = pFd->mmapSizeMax;
}
#ifdef SQLITE_SHARED_MAPPING
if( pFd->pInode ){
unixInodeInfo *pInode = pFd->pInode;
if( pFd->pMapRegion ) return SQLITE_OK;
unixEnterMutex();
if( pInode->pSharedMapping==0 ){
u8 *pNew = osMmap(0, nMap, PROT_READ, MAP_SHARED, pFd->h, 0);
if( pNew==MAP_FAILED ){
unixLogError(SQLITE_OK, "mmap", pFd->zPath);
pFd->mmapSizeMax = 0;
}else{
pInode->pSharedMapping = pNew;
pInode->nSharedMapping = nMap;
}
}
pFd->pMapRegion = pInode->pSharedMapping;
pFd->mmapSizeActual = pFd->mmapSize = pInode->nSharedMapping;
unixLeaveMutex();
return SQLITE_OK;
}
#endif
assert( nMap>0 || (pFd->mmapSize==0 && pFd->pMapRegion==0) );
if( nMap!=pFd->mmapSize ){
unixRemapfile(pFd, nMap);
@ -5834,6 +5881,9 @@ static int fillInUnixFile(
if( pLockingStyle == &posixIoMethods
#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
|| pLockingStyle == &nfsIoMethods
#endif
#ifdef SQLITE_SHARED_MAPPING
|| pLockingStyle == &nolockIoMethods
#endif
){
unixEnterMutex();

View File

@ -658,6 +658,9 @@ struct Pager {
u32 cksumInit; /* Quasi-random value added to every checksum */
u32 nSubRec; /* Number of records written to sub-journal */
Bitvec *pInJournal; /* One bit for each page in the database file */
#ifndef SQLITE_OMIT_CONCURRENT
Bitvec *pAllRead; /* Pages read within current CONCURRENT trans. */
#endif
sqlite3_file *fd; /* File descriptor for database */
sqlite3_file *jfd; /* File descriptor for main journal */
sqlite3_file *sjfd; /* File descriptor for sub-journal */
@ -922,7 +925,9 @@ static int assert_pager_state(Pager *p){
if( !pagerUseWal(pPager) ){
assert( p->eLock>=RESERVED_LOCK );
}
assert( pPager->dbSize==pPager->dbOrigSize );
#ifndef SQLITE_OMIT_CONCURRENT
assert( pPager->dbSize==pPager->dbOrigSize || pPager->pAllRead );
#endif
assert( pPager->dbOrigSize==pPager->dbFileSize );
assert( pPager->dbOrigSize==pPager->dbHintSize );
assert( pPager->setSuper==0 );
@ -1830,6 +1835,53 @@ static int addToSavepointBitvecs(Pager *pPager, Pgno pgno){
return rc;
}
#ifndef SQLITE_OMIT_CONCURRENT
/*
** If they are not already, begin recording all pages read from the pager layer
** by the b-tree layer This is used by concurrent transactions. Return
** SQLITE_OK if successful, or an SQLite error code (SQLITE_NOMEM) if an error
** occurs.
*/
int sqlite3PagerBeginConcurrent(Pager *pPager){
int rc = SQLITE_OK;
if( pPager->pAllRead==0 ){
pPager->pAllRead = sqlite3BitvecCreate(pPager->dbSize);
pPager->dbOrigSize = pPager->dbSize;
if( pPager->pAllRead==0 ){
rc = SQLITE_NOMEM;
}
}
return rc;
}
/* !defined(SQLITE_OMIT_CONCURRENT)
**
** Stop recording all pages read from the pager layer by the b-tree layer
** and discard any current records.
*/
void sqlite3PagerEndConcurrent(Pager *pPager){
sqlite3BitvecDestroy(pPager->pAllRead);
pPager->pAllRead = 0;
}
/* !defined(SQLITE_OMIT_CONCURRENT)
**
** Return true if the database is in wal mode. False otherwise.
*/
int sqlite3PagerIsWal(Pager *pPager){
return pPager->pWal!=0;
}
#endif /* SQLITE_OMIT_CONCURRENT */
/*
** Free the Pager.pInJournal and Pager.pAllRead bitvec objects.
*/
static void pagerFreeBitvecs(Pager *pPager){
sqlite3BitvecDestroy(pPager->pInJournal);
pPager->pInJournal = 0;
sqlite3PagerEndConcurrent(pPager);
}
/*
** This function is a no-op if the pager is in exclusive mode and not
** in the ERROR state. Otherwise, it switches the pager to PAGER_OPEN
@ -1854,8 +1906,7 @@ static void pager_unlock(Pager *pPager){
|| pPager->eState==PAGER_ERROR
);
sqlite3BitvecDestroy(pPager->pInJournal);
pPager->pInJournal = 0;
pagerFreeBitvecs(pPager);
releaseAllSavepoints(pPager);
if( pagerUseWal(pPager) ){
@ -2122,8 +2173,7 @@ static int pager_end_transaction(Pager *pPager, int hasSuper, int bCommit){
}
#endif
sqlite3BitvecDestroy(pPager->pInJournal);
pPager->pInJournal = 0;
pagerFreeBitvecs(pPager);
pPager->nRec = 0;
if( rc==SQLITE_OK ){
if( MEMDB || pagerFlushOnCommit(pPager, bCommit) ){
@ -3153,8 +3203,24 @@ static int pagerRollbackWal(Pager *pPager){
** + Reload page content from the database (if refcount>0).
*/
pPager->dbSize = pPager->dbOrigSize;
rc = sqlite3WalUndo(pPager->pWal, pagerUndoCallback, (void *)pPager);
rc = sqlite3WalUndo(pPager->pWal, pagerUndoCallback, (void *)pPager,
#ifdef SQLITE_OMIT_CONCURRENT
0
#else
pPager->pAllRead!=0
#endif
);
pList = sqlite3PcacheDirtyList(pPager->pPCache);
#ifndef SQLITE_OMIT_CONCURRENT
/* If this is an CONCURRENT transaction, then page 1 must be reread from
** the db file, even if it is not dirty. This is because the b-tree layer
** may have already zeroed the nFree and iTrunk header fields. */
if( rc==SQLITE_OK && (pList==0 || pList->pgno!=1) && pPager->pAllRead ){
rc = pagerUndoCallback((void*)pPager, 1);
}
#endif
while( pList && rc==SQLITE_OK ){
PgHdr *pNext = pList->pDirty;
rc = pagerUndoCallback((void *)pPager, pList->pgno);
@ -3204,6 +3270,8 @@ static int pagerWalFrames(
if( p->pgno<=nTruncate ){
ppNext = &p->pDirty;
nList++;
PAGERTRACE(("TO-WAL %d page %d hash(%08x)\n",
PAGERID(pPager), p->pgno, pager_pagehash(p)));
}
}
assert( pList );
@ -4274,7 +4342,7 @@ static int syncJournal(Pager *pPager, int newHdr){
assert( assert_pager_state(pPager) );
assert( !pagerUseWal(pPager) );
rc = sqlite3PagerExclusiveLock(pPager);
rc = sqlite3PagerExclusiveLock(pPager, 0, 0);
if( rc!=SQLITE_OK ) return rc;
if( !pPager->noSync ){
@ -4625,6 +4693,12 @@ static int pagerStress(void *p, PgHdr *pPg){
pPager->aStat[PAGER_STAT_SPILL]++;
pPg->pDirty = 0;
if( pagerUseWal(pPager) ){
#ifndef SQLITE_OMIT_CONCURRENT
/* If the transaction is a "BEGIN CONCURRENT" transaction, the page
** cannot be flushed to disk. Return early in this case. */
if( pPager->pAllRead ) return SQLITE_OK;
#endif
/* Write a single frame for this page to the log. */
rc = subjournalPageIfRequired(pPg);
if( rc==SQLITE_OK ){
@ -5456,6 +5530,23 @@ static void pagerUnlockIfUnused(Pager *pPager){
}
}
#ifndef SQLITE_OMIT_CONCURRENT
/*
** If this pager is currently in a concurrent transaction (pAllRead!=0),
** then set the bit in the pAllRead vector to indicate that the transaction
** read from page pgno. Return SQLITE_OK if successful, or an SQLite error
** code (i.e. SQLITE_NOMEM) if an error occurs.
*/
int sqlite3PagerUsePage(Pager *pPager, Pgno pgno){
int rc = SQLITE_OK;
if( pPager->pAllRead && pgno<=pPager->dbOrigSize ){
PAGERTRACE(("USING page %d\n", pgno));
rc = sqlite3BitvecSet(pPager->pAllRead, pgno);
}
return rc;
}
#endif
/*
** The page getter methods each try to acquire a reference to a
** page with page number pgno. If the requested reference is
@ -5529,6 +5620,14 @@ static int getPageNormal(
assert( assert_pager_state(pPager) );
assert( pPager->hasHeldSharedLock==1 );
/* If this is an CONCURRENT transaction and the page being read was
** present in the database file when the transaction was opened,
** mark it as read in the pAllRead vector. */
if( (rc = sqlite3PagerUsePage(pPager, pgno))!=SQLITE_OK ){
pPg = 0;
goto pager_acquire_err;
}
if( pgno==0 ) return SQLITE_CORRUPT_BKPT;
pBase = sqlite3PcacheFetch(pPager->pPCache, pgno, 3);
if( pBase==0 ){
@ -5887,11 +5986,14 @@ static int pager_open_journal(Pager *pPager){
** Begin a write-transaction on the specified pager object. If a
** write-transaction has already been opened, this function is a no-op.
**
** If the exFlag argument is false, then acquire at least a RESERVED
** lock on the database file. If exFlag is true, then acquire at least
** If the exFlag argument is 0, then acquire at least a RESERVED
** lock on the database file. If exFlag is >0, then acquire at least
** an EXCLUSIVE lock. If such a lock is already held, no locking
** functions need be called.
**
** If (exFlag<0) and the database is in WAL mode, do not take any locks.
** The transaction will run in CONCURRENT mode instead.
**
** If the subjInMemory argument is non-zero, then any sub-journal opened
** within this transaction will be opened as an in-memory file. This
** has no effect if the sub-journal is already opened (as it may be when
@ -5909,7 +6011,6 @@ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){
if( pPager->eState==PAGER_READER ){
assert( pPager->pInJournal==0 );
if( pagerUseWal(pPager) ){
/* If the pager is configured to use locking_mode=exclusive, and an
** exclusive lock on the database is not already held, obtain it now.
@ -5925,9 +6026,10 @@ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){
/* Grab the write lock on the log file. If successful, upgrade to
** PAGER_RESERVED state. Otherwise, return an error code to the caller.
** The busy-handler is not invoked if another connection already
** holds the write-lock. If possible, the upper layer will call it.
*/
rc = sqlite3WalBeginWriteTransaction(pPager->pWal);
** holds the write-lock. If possible, the upper layer will call it. */
if( exFlag>=0 ){
rc = sqlite3WalBeginWriteTransaction(pPager->pWal);
}
}else{
/* Obtain a RESERVED lock on the database file. If the exFlag parameter
** is true, then immediately upgrade this to an EXCLUSIVE lock. The
@ -5935,7 +6037,7 @@ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){
** lock, but not when obtaining the RESERVED lock.
*/
rc = pagerLockDb(pPager, RESERVED_LOCK);
if( rc==SQLITE_OK && exFlag ){
if( rc==SQLITE_OK && exFlag>0 ){
rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
}
}
@ -6235,7 +6337,7 @@ int sqlite3PagerWrite(PgHdr *pPg){
** to sqlite3PagerWrite(). In other words, return TRUE if it is ok
** to change the content of the page.
*/
#ifndef NDEBUG
#if !defined(SQLITE_OMIT_CONCURRENT) || !defined(NDEBUG)
int sqlite3PagerIswriteable(DbPage *pPg){
return pPg->flags & PGHDR_WRITEABLE;
}
@ -6391,17 +6493,26 @@ int sqlite3PagerSync(Pager *pPager, const char *zSuper){
}
/*
** This function may only be called while a write-transaction is active in
** rollback. If the connection is in WAL mode, this call is a no-op.
** Otherwise, if the connection does not already have an EXCLUSIVE lock on
** the database file, an attempt is made to obtain one.
** This function is called to ensure that all locks required to commit the
** current write-transaction to the database file are held. If the db is
** in rollback mode, this means the EXCLUSIVE lock on the database file.
**
** If the EXCLUSIVE lock is already held or the attempt to obtain it is
** successful, or the connection is in WAL mode, SQLITE_OK is returned.
** Otherwise, either SQLITE_BUSY or an SQLITE_IOERR_XXX error code is
** returned.
** Or, if this is a non-CONCURRENT transaction on a wal-mode database, this
** function is a no-op.
**
** If this is an CONCURRENT transaction on a wal-mode database, this function
** attempts to obtain the WRITER lock on the wal file and also checks to
** see that the transaction can be safely committed (does not commit with
** any other transaction committed since it was opened).
**
** If the required locks are already held or successfully obtained and
** the transaction can be committed, SQLITE_OK is returned. If a required lock
** cannot be obtained, SQLITE_BUSY is returned. Or, if the current transaction
** is CONCURRENT and cannot be committed due to a conflict, SQLITE_BUSY_SNAPSHOT
** is returned. Otherwise, if some other error occurs (IO error, OOM etc.),
** and SQLite error code is returned.
*/
int sqlite3PagerExclusiveLock(Pager *pPager){
int sqlite3PagerExclusiveLock(Pager *pPager, PgHdr *pPage1, Pgno *piConflict){
int rc = pPager->errCode;
assert( assert_pager_state(pPager) );
if( rc==SQLITE_OK ){
@ -6413,10 +6524,73 @@ int sqlite3PagerExclusiveLock(Pager *pPager){
if( 0==pagerUseWal(pPager) ){
rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
}
#ifndef SQLITE_OMIT_CONCURRENT
else{
if( pPager->pAllRead ){
/* This is an CONCURRENT transaction. Attempt to lock the wal database
** here. If SQLITE_BUSY (but not SQLITE_BUSY_SNAPSHOT) is returned,
** invoke the busy-handler and try again for as long as it returns
** non-zero. */
do {
rc = sqlite3WalLockForCommit(
pPager->pWal, pPage1, pPager->pAllRead, piConflict
);
}while( rc==SQLITE_BUSY
&& pPager->xBusyHandler(pPager->pBusyHandlerArg)
);
}
}
#endif /* SQLITE_OMIT_CONCURRENT */
}
return rc;
}
#ifndef SQLITE_OMIT_CONCURRENT
/*
** This function is called as part of committing an CONCURRENT transaction.
** At this point the wal WRITER lock is held, and all pages in the cache
** except for page 1 are compatible with the snapshot at the head of the
** wal file.
**
** This function updates the in-memory data structures and reloads the
** contents of page 1 so that the client is operating on the snapshot
** at the head of the wal file.
**
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
*/
int sqlite3PagerUpgradeSnapshot(Pager *pPager, DbPage *pPage1){
int rc;
assert( pPager->pWal && pPager->pAllRead );
rc = sqlite3WalUpgradeSnapshot(pPager->pWal);
if( rc==SQLITE_OK ){
rc = readDbPage(pPage1);
}
return rc;
}
/* !defined(SQLITE_OMIT_CONCURRENT)
**
** Set the in-memory cache of the database file size to nSz pages.
*/
void sqlite3PagerSetDbsize(Pager *pPager, Pgno nSz){
pPager->dbSize = nSz;
}
/* !defined(SQLITE_OMIT_CONCURRENT)
**
** If this is a WAL mode connection and the WRITER lock is currently held,
** relinquish it.
*/
void sqlite3PagerDropExclusiveLock(Pager *pPager){
if( pagerUseWal(pPager) ){
sqlite3WalEndWriteTransaction(pPager->pWal);
}
}
#endif /* SQLITE_OMIT_CONCURRENT */
/*
** Sync the database file for the pager pPager. zSuper points to the name
** of a super-journal file that should be written into the individual
@ -7784,6 +7958,11 @@ void sqlite3PagerSnapshotUnlock(Pager *pPager){
}
#endif /* SQLITE_ENABLE_SNAPSHOT */
int sqlite3PagerWalInfo(Pager *pPager, u32 *pnPrior, u32 *pnFrame){
return sqlite3WalInfo(pPager->pWal, pnPrior, pnFrame);
}
#endif /* !SQLITE_OMIT_WAL */
#ifdef SQLITE_ENABLE_ZIPVFS

View File

@ -163,7 +163,7 @@ void *sqlite3PagerGetExtra(DbPage *);
void sqlite3PagerPagecount(Pager*, int*);
int sqlite3PagerBegin(Pager*, int exFlag, int);
int sqlite3PagerCommitPhaseOne(Pager*,const char *zSuper, int);
int sqlite3PagerExclusiveLock(Pager*);
int sqlite3PagerExclusiveLock(Pager*, DbPage *pPage1, Pgno*);
int sqlite3PagerSync(Pager *pPager, const char *zSuper);
int sqlite3PagerCommitPhaseTwo(Pager*);
int sqlite3PagerRollback(Pager*);
@ -171,6 +171,7 @@ int sqlite3PagerOpenSavepoint(Pager *pPager, int n);
int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint);
int sqlite3PagerSharedLock(Pager *pPager);
#ifndef SQLITE_OMIT_WAL
int sqlite3PagerCheckpoint(Pager *pPager, sqlite3*, int, int*, int*);
int sqlite3PagerWalSupported(Pager *pPager);
@ -225,10 +226,28 @@ void sqlite3PagerTruncateImage(Pager*,Pgno);
void sqlite3PagerRekey(DbPage*, Pgno, u16);
#ifndef SQLITE_OMIT_CONCURRENT
int sqlite3PagerUsePage(Pager*, Pgno);
void sqlite3PagerEndConcurrent(Pager*);
int sqlite3PagerBeginConcurrent(Pager*);
void sqlite3PagerDropExclusiveLock(Pager*);
int sqlite3PagerUpgradeSnapshot(Pager *pPager, DbPage*);
void sqlite3PagerSetDbsize(Pager *pPager, Pgno);
int sqlite3PagerIsWal(Pager*);
#else
# define sqlite3PagerEndConcurrent(x)
# define sqlite3PagerUsePage(x, y) SQLITE_OK
#endif
#if defined(SQLITE_DEBUG) || !defined(SQLITE_OMIT_CONCURRENT)
int sqlite3PagerIswriteable(DbPage*);
#endif
int sqlite3PagerWalInfo(Pager*, u32 *pnPrior, u32 *pnFrame);
/* Functions to support testing and debugging. */
#if !defined(NDEBUG) || defined(SQLITE_TEST)
Pgno sqlite3PagerPagenumber(DbPage*);
int sqlite3PagerIswriteable(DbPage*);
#endif
#ifdef SQLITE_TEST
int *sqlite3PagerStats(Pager*);

View File

@ -109,6 +109,13 @@
*/
struct TrigEvent { int a; IdList * b; };
/*
** Generate a syntax error
*/
static void parserSyntaxError(Parse *pParse, Token *p){
sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", p);
}
struct FrameBound { int eType; Expr *pExpr; };
/*
@ -168,7 +175,16 @@ trans_opt ::= TRANSACTION nm.
transtype(A) ::= . {A = TK_DEFERRED;}
transtype(A) ::= DEFERRED(X). {A = @X; /*A-overwrites-X*/}
transtype(A) ::= IMMEDIATE(X). {A = @X; /*A-overwrites-X*/}
transtype(A) ::= EXCLUSIVE(X). {A = @X; /*A-overwrites-X*/}
transtype(A) ::= ID(X). {
Token *p = &X;
if( p->n==9 && sqlite3_strnicmp(p->z,"exclusive",9)==0 ){
A = TK_EXCLUSIVE;
}else if( p->n==10 && sqlite3_strnicmp(p->z,"concurrent",10)==0 ){
A = TK_CONCURRENT; /*A-overwrites-X*/
}else{
parserSyntaxError(pParse, p);
}
}
cmd ::= COMMIT|END(X) trans_opt. {sqlite3EndTransaction(pParse,@X);}
cmd ::= ROLLBACK(X) trans_opt. {sqlite3EndTransaction(pParse,@X);}
@ -304,7 +320,6 @@ columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,A,Y);}
// keywords. Any non-standard keyword can also be an identifier.
//
%token_class id ID|INDEXED.
// And "ids" is an identifer-or-string.
//
%token_class ids ID|STRING.
@ -1156,7 +1171,7 @@ expr(A) ::= VARIABLE(X). {
Token t = X; /*A-overwrites-X*/
assert( t.n>=2 );
if( pParse->nested==0 ){
sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t);
parserSyntaxError(pParse, &t);
A = 0;
}else{
A = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
@ -2027,6 +2042,7 @@ filter_clause(A) ::= FILTER LP WHERE expr(X) RP. { A = X; }
UMINUS /* Unary minus */
TRUTH /* IS TRUE or IS FALSE or IS NOT TRUE or IS NOT FALSE */
REGISTER /* Reference to a VDBE register */
CONCURRENT /* BEGIN CONCURRENT */
VECTOR /* Vector */
SELECT_COLUMN /* Choose a single column from a multi-column SELECT */
IF_NULL_ROW /* the if-null-row operator */

View File

@ -441,6 +441,15 @@ static const PragmaName aPragmaName[] = {
/* iArg: */ 0 },
#endif
#endif
#endif
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
#if defined(SQLITE_ENABLE_NOOP_UPDATE)
{/* zName: */ "noop_update",
/* ePragTyp: */ PragTyp_FLAG,
/* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1,
/* ColNames: */ 0, 0,
/* iArg: */ SQLITE_NoopUpdate },
#endif
#endif
{/* zName: */ "optimize",
/* ePragTyp: */ PragTyp_OPTIMIZE,
@ -657,4 +666,4 @@ static const PragmaName aPragmaName[] = {
/* iArg: */ SQLITE_WriteSchema|SQLITE_NoSchemaError },
#endif
};
/* Number of pragmas: 68 on by default, 78 total. */
/* Number of pragmas: 68 on by default, 79 total. */

View File

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

View File

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

View File

@ -10724,6 +10724,31 @@ SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp(
*/
SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb);
/*
** CAPI3REF: Wal related information regarding the most recent COMMIT
** EXPERIMENTAL
**
** This function reports on the state of the wal file (if any) for database
** zDb, which should be "main", "temp", or the name of the attached database.
** Its results - the values written to the output parameters - are only
** defined if the most recent SQL command on the connection was a successful
** COMMIT that wrote data to wal-mode database zDb.
**
** Assuming the above conditions are met, output parameter (*pnFrame) is set
** to the total number of frames in the wal file. Parameter (*pnPrior) is
** set to the number of frames that were present in the wal file before the
** most recent transaction was committed. So that the number of frames written
** by the most recent transaction is (*pnFrame)-(*pnPrior).
**
** If successful, SQLITE_OK is returned. Otherwise, an SQLite error code. It
** is not an error if this function is called at a time when the results
** are undefined.
*/
SQLITE_EXPERIMENTAL int sqlite3_wal_info(
sqlite3 *db, const char *zDb,
unsigned int *pnPrior, unsigned int *pnFrame
);
/*
** CAPI3REF: Serialize a database
**

View File

@ -1306,6 +1306,7 @@ typedef struct DbFixer DbFixer;
typedef struct Schema Schema;
typedef struct Expr Expr;
typedef struct ExprList ExprList;
typedef struct FastPrng FastPrng;
typedef struct FKey FKey;
typedef struct FpDecode FpDecode;
typedef struct FuncDestructor FuncDestructor;
@ -1434,6 +1435,14 @@ typedef int VList;
# define SQLITE_DEFAULT_WAL_SYNCHRONOUS SQLITE_DEFAULT_SYNCHRONOUS
#endif
/*
** State of a simple PRNG used for the per-connection and per-pager
** pseudo-random number generators.
*/
struct FastPrng {
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
@ -1649,6 +1658,7 @@ struct sqlite3 {
u32 dbOptFlags; /* Flags to enable/disable optimizations */
u8 enc; /* Text encoding */
u8 autoCommit; /* The auto-commit flag. */
u8 eConcurrent; /* CONCURRENT_* value */
u8 temp_store; /* 1: file 2: memory 0: default */
u8 mallocFailed; /* True if we have seen a malloc failure */
u8 bBenignMalloc; /* Do not require OOMs if true */
@ -1662,6 +1672,7 @@ struct sqlite3 {
u8 nSqlExec; /* Number of pending OP_SqlExec opcodes */
u8 eOpenState; /* Current condition of the connection */
int nextPagesize; /* Pagesize after VACUUM if >0 */
FastPrng sPrng; /* State of the per-connection PRNG */
i64 nChange; /* Value returned by sqlite3_changes() */
i64 nTotalChange; /* Value returned by sqlite3_total_changes() */
int aLimit[SQLITE_N_LIMIT]; /* Limits */
@ -1769,6 +1780,13 @@ struct sqlite3 {
#endif
};
/*
** Candidate values for sqlite3.eConcurrent
*/
#define CONCURRENT_NONE 0
#define CONCURRENT_OPEN 1
#define CONCURRENT_SCHEMA 2
/*
** A macro to discover the encoding of a database.
*/
@ -1831,6 +1849,9 @@ struct sqlite3 {
#define SQLITE_ReadUncommit HI(0x00004) /* READ UNCOMMITTED in shared-cache */
#define SQLITE_FkNoAction HI(0x00008) /* Treat all FK as NO ACTION */
/* 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 */
@ -5069,6 +5090,8 @@ Vdbe *sqlite3GetVdbe(Parse*);
void sqlite3PrngSaveState(void);
void sqlite3PrngRestoreState(void);
#endif
void sqlite3FastPrngInit(FastPrng*);
void sqlite3FastRandomness(FastPrng*, int N, void *P);
void sqlite3RollbackAll(sqlite3*,int);
void sqlite3CodeVerifySchema(Parse*, int);
void sqlite3CodeVerifyNamedSchema(Parse*, const char *zDb);

View File

@ -8472,6 +8472,41 @@ static int SQLITE_TCLAPI test_dbconfig_maindbname_icecube(
}
}
/*
** Usage: sqlite3_wal_info DB DBNAME
*/
static int SQLITE_TCLAPI test_wal_info(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int rc;
sqlite3 *db;
char *zName;
unsigned int nPrior;
unsigned int nFrame;
if( objc!=3 ){
Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME");
return TCL_ERROR;
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
zName = Tcl_GetString(objv[2]);
rc = sqlite3_wal_info(db, zName, &nPrior, &nFrame);
if( rc!=SQLITE_OK ){
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
return TCL_ERROR;
}else{
Tcl_Obj *pNew = Tcl_NewObj();
Tcl_ListObjAppendElement(interp, pNew, Tcl_NewWideIntObj((i64)nPrior));
Tcl_ListObjAppendElement(interp, pNew, Tcl_NewWideIntObj((i64)nFrame));
Tcl_SetObjResult(interp, pNew);
}
return TCL_OK;
}
/*
** Usage: sqlite3_mmap_warm DB DBNAME
*/
@ -9061,8 +9096,9 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "sqlite3_snapshot_open_blob", test_snapshot_open_blob, 0 },
{ "sqlite3_snapshot_cmp_blob", test_snapshot_cmp_blob, 0 },
#endif
{ "sqlite3_delete_database", test_delete_database, 0 },
{ "atomic_batch_write", test_atomic_batch_write, 0 },
{ "sqlite3_delete_database", test_delete_database, 0 },
{ "sqlite3_wal_info", test_wal_info, 0 },
{ "atomic_batch_write", test_atomic_batch_write, 0 },
{ "sqlite3_mmap_warm", test_mmap_warm, 0 },
{ "sqlite3_config_sorterref", test_config_sorterref, 0 },
{ "sqlite3_autovacuum_pages", test_autovacuum_pages, 0 },

View File

@ -685,6 +685,12 @@ Tcl_SetVar2(interp, "sqlite_options", "mergesort", "1", TCL_GLOBAL_ONLY);
Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "1", TCL_GLOBAL_ONLY);
#endif
#ifndef SQLITE_OMIT_CONCURRENT
Tcl_SetVar2(interp, "sqlite_options", "concurrent", "1", TCL_GLOBAL_ONLY);
#else
Tcl_SetVar2(interp, "sqlite_options", "concurrent", "0", TCL_GLOBAL_ONLY);
#endif
#ifdef SQLITE_OMIT_UTF16
Tcl_SetVar2(interp, "sqlite_options", "utf16", "0", TCL_GLOBAL_ONLY);
#else

View File

@ -465,6 +465,17 @@ void sqlite3Update(
*/
chngRowid = chngPk = 0;
for(i=0; i<pChanges->nExpr; i++){
#if defined(SQLITE_ENABLE_NOOP_UPDATE) && !defined(SQLITE_OMIT_FLAG_PRAGMAS)
if( db->flags & SQLITE_NoopUpdate ){
Token x;
sqlite3ExprDelete(db, pChanges->a[i].pExpr);
x.z = pChanges->a[i].zEName;
x.n = sqlite3Strlen30(x.z);
pChanges->a[i].pExpr =
sqlite3PExpr(pParse, TK_UPLUS, sqlite3ExprAlloc(db, TK_ID, &x, 0), 0);
if( db->mallocFailed ) goto update_cleanup;
}
#endif
u8 hCol = sqlite3StrIHash(pChanges->a[i].zEName);
/* If this is an UPDATE with a FROM clause, do not resolve expressions
** here. The call to sqlite3Select() below will do that. */

View File

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

View File

@ -3848,6 +3848,7 @@ case OP_Savepoint: {
** is committed.
*/
int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint;
assert( db->eConcurrent==0 || db->isTransactionSavepoint==0 );
if( isTransaction && p1==SAVEPOINT_RELEASE ){
if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){
goto vdbe_return;
@ -3934,23 +3935,31 @@ case OP_Savepoint: {
break;
}
/* Opcode: AutoCommit P1 P2 * * *
/* Opcode: AutoCommit P1 P2 P3 * *
**
** Set the database auto-commit flag to P1 (1 or 0). If P2 is true, roll
** back any currently active btree transactions. If there are any active
** VMs (apart from this one), then a ROLLBACK fails. A COMMIT fails if
** there are active writing VMs or active VMs that use shared cache.
**
** If P3 is non-zero, then this instruction is being executed as part of
** a "BEGIN CONCURRENT" command.
**
** This instruction causes the VM to halt.
*/
case OP_AutoCommit: {
int desiredAutoCommit;
int iRollback;
int bConcurrent;
int hrc;
desiredAutoCommit = pOp->p1;
iRollback = pOp->p2;
bConcurrent = pOp->p3;
assert( desiredAutoCommit==1 || desiredAutoCommit==0 );
assert( desiredAutoCommit==1 || iRollback==0 );
assert( desiredAutoCommit==0 || bConcurrent==0 );
assert( db->autoCommit==0 || db->eConcurrent==CONCURRENT_NONE );
assert( db->nVdbeActive>0 ); /* At least this one VM is active */
assert( p->bIsReader );
@ -3959,10 +3968,17 @@ case OP_AutoCommit: {
assert( desiredAutoCommit==1 );
sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
db->autoCommit = 1;
}else if( desiredAutoCommit && db->nVdbeWrite>0 ){
/* If this instruction implements a COMMIT and other VMs are writing
** return an error indicating that the other VMs must complete first.
*/
db->eConcurrent = CONCURRENT_NONE;
}else if( desiredAutoCommit
&& (db->nVdbeWrite>0 || (db->eConcurrent && db->nVdbeActive>1)) ){
/* A transaction may only be committed if there are no other active
** writer VMs. If the transaction is CONCURRENT, then it may only be
** committed if there are no active VMs at all (readers or writers).
**
** If this instruction is a COMMIT and the transaction may not be
** committed due to one of the conditions above, return an error
** indicating that other VMs must complete before the COMMIT can
** be processed. */
sqlite3VdbeError(p, "cannot commit transaction - "
"SQL statements in progress");
rc = SQLITE_BUSY;
@ -3972,12 +3988,16 @@ case OP_AutoCommit: {
}else{
db->autoCommit = (u8)desiredAutoCommit;
}
if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
hrc = sqlite3VdbeHalt(p);
if( (hrc & 0xFF)==SQLITE_BUSY ){
p->pc = (int)(pOp - aOp);
db->autoCommit = (u8)(1-desiredAutoCommit);
p->rc = rc = SQLITE_BUSY;
p->rc = hrc;
rc = SQLITE_BUSY;
goto vdbe_return;
}
assert( bConcurrent==CONCURRENT_NONE || bConcurrent==CONCURRENT_OPEN );
db->eConcurrent = (u8)bConcurrent;
sqlite3CloseSavepoints(db);
if( p->rc==SQLITE_OK ){
rc = SQLITE_DONE;
@ -4190,6 +4210,17 @@ case OP_SetCookie: {
pDb = &db->aDb[pOp->p1];
assert( pDb->pBt!=0 );
assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) );
#ifndef SQLITE_OMIT_CONCURRENT
if( db->eConcurrent
&& (pOp->p2==BTREE_USER_VERSION || pOp->p2==BTREE_APPLICATION_ID)
){
rc = SQLITE_ERROR;
sqlite3VdbeError(p, "cannot modify %s within CONCURRENT transaction",
pOp->p2==BTREE_USER_VERSION ? "user_version" : "application_id"
);
goto abort_due_to_error;
}
#endif
/* See note about index shifting on OP_ReadCookie */
rc = sqlite3BtreeUpdateMeta(pDb->pBt, pOp->p2, pOp->p3);
if( pOp->p2==BTREE_SCHEMA_VERSION ){
@ -4339,6 +4370,11 @@ case OP_OpenWrite:
pX = pDb->pBt;
assert( pX!=0 );
if( pOp->opcode==OP_OpenWrite ){
#ifndef SQLITE_OMIT_CONCURRENT
if( db->eConcurrent==CONCURRENT_OPEN && p2==1 && iDb!=1 ){
db->eConcurrent = CONCURRENT_SCHEMA;
}
#endif
assert( OPFLAG_FORDELETE==BTREE_FORDELETE );
wrFlag = BTREE_WRCSR | (pOp->p5 & OPFLAG_FORDELETE);
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
@ -8109,6 +8145,11 @@ case OP_CursorUnlock: {
*/
case OP_TableLock: {
u8 isWriteLock = (u8)pOp->p3;
#ifndef SQLITE_OMIT_CONCURRENT
if( isWriteLock && db->eConcurrent && pOp->p2==1 && pOp->p1!=1 ){
db->eConcurrent = CONCURRENT_SCHEMA;
}
#endif
if( isWriteLock || 0==(db->flags&SQLITE_ReadUncommit) ){
int p1 = pOp->p1;
assert( p1>=0 && p1<db->nDb );

View File

@ -2954,10 +2954,27 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
assert( i!=1 );
nTrans++;
}
rc = sqlite3PagerExclusiveLock(pPager);
rc = sqlite3BtreeExclusiveLock(pBt);
sqlite3BtreeLeave(pBt);
}
}
#ifndef SQLITE_OMIT_CONCURRENT
if( db->eConcurrent && (rc & 0xFF)==SQLITE_BUSY ){
/* An SQLITE_BUSY or SQLITE_BUSY_SNAPSHOT was encountered while
** attempting to take the WRITER lock on a wal file. Release the
** WRITER locks on all wal files and return early. */
for(i=0; i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
if( sqlite3BtreeTxnState(pBt)==SQLITE_TXN_WRITE ){
sqlite3BtreeEnter(pBt);
sqlite3PagerDropExclusiveLock(sqlite3BtreePager(pBt));
sqlite3BtreeLeave(pBt);
}
}
}
#endif
if( rc!=SQLITE_OK ){
return rc;
}
@ -3359,6 +3376,7 @@ int sqlite3VdbeHalt(Vdbe *p){
sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
sqlite3CloseSavepoints(db);
db->autoCommit = 1;
db->eConcurrent = CONCURRENT_NONE;
p->nChange = 0;
}
}
@ -3397,9 +3415,9 @@ int sqlite3VdbeHalt(Vdbe *p){
** is required. */
rc = vdbeCommit(db, p);
}
if( rc==SQLITE_BUSY && p->readOnly ){
if( (rc & 0xFF)==SQLITE_BUSY && p->readOnly ){
sqlite3VdbeLeave(p);
return SQLITE_BUSY;
return rc;
}else if( rc!=SQLITE_OK ){
sqlite3SystemError(db, rc);
p->rc = rc;
@ -3427,6 +3445,7 @@ int sqlite3VdbeHalt(Vdbe *p){
sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
sqlite3CloseSavepoints(db);
db->autoCommit = 1;
db->eConcurrent = CONCURRENT_NONE;
p->nChange = 0;
}
}
@ -3448,6 +3467,7 @@ int sqlite3VdbeHalt(Vdbe *p){
sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
sqlite3CloseSavepoints(db);
db->autoCommit = 1;
db->eConcurrent = CONCURRENT_NONE;
p->nChange = 0;
}
}

459
src/wal.c
View File

@ -526,8 +526,10 @@ struct Wal {
WalIndexHdr hdr; /* Wal-index header for current transaction */
u32 minFrame; /* Ignore wal frames before this one */
u32 iReCksum; /* On commit, recalculate checksums from here */
u32 nPriorFrame; /* For sqlite3WalInfo() */
const char *zWalName; /* Name of WAL file */
u32 nCkpt; /* Checkpoint sequence counter in the wal-header */
FastPrng sPrng; /* Random number generator */
#ifdef SQLITE_USE_SEH
u32 lockMask; /* Mask of locks held */
void *pFree; /* Pointer to sqlite3_free() if exception thrown */
@ -1065,7 +1067,7 @@ static const char *walLockName(int lockIdx){
/*
** Set or release locks on the WAL. Locks are either shared or exclusive.
** A lock cannot be moved directly between shared and exclusive - it must go
** through the unlocked state first.
** through the concurrent state first.
**
** In locking_mode=EXCLUSIVE, all of these routines become no-ops.
*/
@ -1383,7 +1385,7 @@ static int walIndexRecover(Wal *pWal){
/* Obtain an exclusive lock on all byte in the locking range not already
** locked by the caller. The caller is guaranteed to have locked the
** WAL_WRITE_LOCK byte, and may have also locked the WAL_CKPT_LOCK byte.
** If successful, the same bytes that are locked here are unlocked before
** If successful, the same bytes that are locked here are concurrent before
** this function returns.
*/
assert( pWal->ckptLock==1 || pWal->ckptLock==0 );
@ -1707,6 +1709,7 @@ int sqlite3WalOpen(
pRet->syncHeader = 1;
pRet->padToSectorBoundary = 1;
pRet->exclusiveMode = (bNoShm ? WAL_HEAPMEMORY_MODE: WAL_NORMAL_MODE);
sqlite3FastPrngInit(&pRet->sPrng);
/* Open file handle on the write-ahead log file. */
flags = (SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_WAL);
@ -2336,7 +2339,7 @@ static int walCheckpoint(
rc = SQLITE_BUSY;
}else if( eMode>=SQLITE_CHECKPOINT_RESTART ){
u32 salt1;
sqlite3_randomness(4, &salt1);
sqlite3FastRandomness(&pWal->sPrng, 4, &salt1);
assert( pInfo->nBackfill==pWal->hdr.mxFrame );
rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(1), WAL_NREADER-1);
if( rc==SQLITE_OK ){
@ -2542,6 +2545,49 @@ int sqlite3WalClose(
return rc;
}
/*
** Try to copy the wal-index header from shared-memory into (*pHdr). Return
** zero if successful or non-zero otherwise. If the header is corrupted
** (either because the two copies are inconsistent or because the checksum
** values are incorrect), the read fails and non-zero is returned.
*/
static int walIndexLoadHdr(Wal *pWal, WalIndexHdr *pHdr){
u32 aCksum[2]; /* Checksum on the header content */
WalIndexHdr h2; /* Second copy of the header content */
WalIndexHdr volatile *aHdr; /* Header in shared memory */
/* The first page of the wal-index must be mapped at this point. */
assert( pWal->nWiData>0 && pWal->apWiData[0] );
/* Read the header. This might happen concurrently with a write to the
** same area of shared memory on a different CPU in a SMP,
** meaning it is possible that an inconsistent snapshot is read
** from the file. If this happens, return non-zero.
**
** There are two copies of the header at the beginning of the wal-index.
** When reading, read [0] first then [1]. Writes are in the reverse order.
** Memory barriers are used to prevent the compiler or the hardware from
** reordering the reads and writes.
*/
aHdr = walIndexHdr(pWal);
memcpy(pHdr, (void *)&aHdr[0], sizeof(h2));
walShmBarrier(pWal);
memcpy(&h2, (void *)&aHdr[1], sizeof(h2));
if( memcmp(&h2, pHdr, sizeof(h2))!=0 ){
return 1; /* Dirty read */
}
if( h2.isInit==0 ){
return 1; /* Malformed header - probably all zeros */
}
walChecksumBytes(1, (u8*)&h2, sizeof(h2)-sizeof(h2.aCksum), 0, aCksum);
if( aCksum[0]!=h2.aCksum[0] || aCksum[1]!=h2.aCksum[1] ){
return 1; /* Checksum does not match */
}
return 0;
}
/*
** Try to read the wal-index header. Return 0 on success and 1 if
** there is a problem.
@ -2560,43 +2606,10 @@ int sqlite3WalClose(
** is read successfully and the checksum verified, return zero.
*/
static SQLITE_NO_TSAN int walIndexTryHdr(Wal *pWal, int *pChanged){
u32 aCksum[2]; /* Checksum on the header content */
WalIndexHdr h1, h2; /* Two copies of the header content */
WalIndexHdr volatile *aHdr; /* Header in shared memory */
WalIndexHdr h1; /* Copy of the header content */
/* The first page of the wal-index must be mapped at this point. */
assert( pWal->nWiData>0 && pWal->apWiData[0] );
/* Read the header. This might happen concurrently with a write to the
** same area of shared memory on a different CPU in a SMP,
** meaning it is possible that an inconsistent snapshot is read
** from the file. If this happens, return non-zero.
**
** tag-20200519-1:
** There are two copies of the header at the beginning of the wal-index.
** When reading, read [0] first then [1]. Writes are in the reverse order.
** Memory barriers are used to prevent the compiler or the hardware from
** reordering the reads and writes. TSAN and similar tools can sometimes
** give false-positive warnings about these accesses because the tools do not
** account for the double-read and the memory barrier. The use of mutexes
** here would be problematic as the memory being accessed is potentially
** shared among multiple processes and not all mutex implementations work
** reliably in that environment.
*/
aHdr = walIndexHdr(pWal);
memcpy(&h1, (void *)&aHdr[0], sizeof(h1)); /* Possible TSAN false-positive */
walShmBarrier(pWal);
memcpy(&h2, (void *)&aHdr[1], sizeof(h2));
if( memcmp(&h1, &h2, sizeof(h1))!=0 ){
return 1; /* Dirty read */
}
if( h1.isInit==0 ){
return 1; /* Malformed header - probably all zeros */
}
walChecksumBytes(1, (u8*)&h1, sizeof(h1)-sizeof(h1.aCksum), 0, aCksum);
if( aCksum[0]!=h1.aCksum[0] || aCksum[1]!=h1.aCksum[1] ){
return 1; /* Checksum does not match */
if( walIndexLoadHdr(pWal, &h1) ){
return 1;
}
if( memcmp(&pWal->hdr, &h1, sizeof(WalIndexHdr)) ){
@ -3380,6 +3393,7 @@ static int walBeginReadTransaction(Wal *pWal, int *pChanged){
testcase( rc==SQLITE_PROTOCOL );
testcase( rc==SQLITE_OK );
pWal->nPriorFrame = pWal->hdr.mxFrame;
#ifdef SQLITE_ENABLE_SNAPSHOT
if( rc==SQLITE_OK ){
if( pSnapshot && memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){
@ -3507,8 +3521,7 @@ static int walFindFrame(
return SQLITE_OK;
}
/* Search the hash table or tables for an entry matching page number
** pgno. Each iteration of the following for() loop searches one
/* Each iteration of the following for() loop searches one
** hash table (each hash table indexes up to HASHTABLE_NPAGE frames).
**
** This code might run concurrently to the code in walIndexAppend()
@ -3640,6 +3653,35 @@ Pgno sqlite3WalDbsize(Wal *pWal){
return 0;
}
/*
** Take the WRITER lock on the WAL file. Return SQLITE_OK if successful,
** or an SQLite error code otherwise. This routine does not invoke any
** busy-handler callbacks, that is done at a higher level.
*/
static int walWriteLock(Wal *pWal){
int rc;
/* Cannot start a write transaction without first holding a read lock */
assert( pWal->readLock>=0 );
assert( pWal->writeLock==0 );
assert( pWal->iReCksum==0 );
/* If this is a read-only connection, obtaining a write-lock is not
** possible. In this case return SQLITE_READONLY. Otherwise, attempt
** to grab the WRITER lock. Set Wal.writeLock to true and return
** SQLITE_OK if successful, or leave Wal.writeLock clear and return
** an SQLite error code (possibly SQLITE_BUSY) otherwise. */
if( pWal->readOnly ){
rc = SQLITE_READONLY;
}else{
rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1);
if( rc==SQLITE_OK ){
pWal->writeLock = 1;
}
}
return rc;
}
/*
** This function starts a write transaction on the WAL.
@ -3666,43 +3708,238 @@ int sqlite3WalBeginWriteTransaction(Wal *pWal){
return SQLITE_OK;
}
#endif
/* Cannot start a write transaction without first holding a read
** transaction. */
assert( pWal->readLock>=0 );
assert( pWal->writeLock==0 && pWal->iReCksum==0 );
if( pWal->readOnly ){
return SQLITE_READONLY;
}
/* Only one writer allowed at a time. Get the write lock. Return
** SQLITE_BUSY if unable.
*/
rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1);
if( rc ){
return rc;
}
pWal->writeLock = 1;
/* If another connection has written to the database file since the
** time the read transaction on this connection was started, then
** the write is disallowed.
*/
SEH_TRY {
if( memcmp(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0 ){
rc = SQLITE_BUSY_SNAPSHOT;
rc = walWriteLock(pWal);
if( rc==SQLITE_OK ){
/* If another connection has written to the database file since the
** time the read transaction on this connection was started, then
** the write is disallowed. Release the WRITER lock and return
** SQLITE_BUSY_SNAPSHOT in this case. */
SEH_TRY {
if( memcmp(&pWal->hdr, (void*)walIndexHdr(pWal),sizeof(WalIndexHdr))!=0 ){
rc = SQLITE_BUSY_SNAPSHOT;
}
}
SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; )
if( rc!=SQLITE_OK ){
walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
pWal->writeLock = 0;
}
}
SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; )
if( rc!=SQLITE_OK ){
walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
pWal->writeLock = 0;
}
return rc;
}
/*
** This function is called by a writer that has a read-lock on aReadmark[0]
** (pWal->readLock==0). This function relinquishes that lock and takes a
** lock on a different aReadmark[] slot.
**
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
*/
static int walUpgradeReadlock(Wal *pWal){
int cnt;
int rc;
assert( pWal->writeLock && pWal->readLock==0 );
walUnlockShared(pWal, WAL_READ_LOCK(0));
pWal->readLock = -1;
cnt = 0;
do{
int notUsed;
rc = walTryBeginRead(pWal, &notUsed, 1, &cnt);
}while( rc==WAL_RETRY );
assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */
testcase( (rc&0xff)==SQLITE_IOERR );
testcase( rc==SQLITE_PROTOCOL );
testcase( rc==SQLITE_OK );
return rc;
}
#ifndef SQLITE_OMIT_CONCURRENT
/*
** This function does the work of sqlite3WalLockForCommit(). The difference
** between this function and sqlite3WalLockForCommit() is that the latter
** encloses everything in a SEH_TRY {} block.
*/
static int walLockForCommit(
Wal *pWal,
PgHdr *pPg1,
Bitvec *pAllRead,
Pgno *piConflict
){
int rc = walWriteLock(pWal);
/* If the database has been modified since this transaction was started,
** check if it is still possible to commit. The transaction can be
** committed if:
**
** a) None of the pages in pList have been modified since the
** transaction opened, and
**
** b) The database schema cookie has not been modified since the
** transaction was started.
*/
if( rc==SQLITE_OK ){
WalIndexHdr head;
if( walIndexLoadHdr(pWal, &head) ){
/* This branch is taken if the wal-index header is corrupted. This
** occurs if some other writer has crashed while committing a
** transaction to this database since the current concurrent transaction
** was opened. */
rc = SQLITE_BUSY_SNAPSHOT;
}else if( memcmp(&pWal->hdr, (void*)&head, sizeof(WalIndexHdr))!=0 ){
int iHash;
int iLast = walFramePage(head.mxFrame);
u32 iFirst = pWal->hdr.mxFrame+1; /* First wal frame to check */
if( memcmp(pWal->hdr.aSalt, (u32*)head.aSalt, sizeof(u32)*2) ){
assert( pWal->readLock==0 );
iFirst = 1;
}
if( pPg1==0 ){
/* If pPg1==0, then the current transaction modified the database
** schema. This means it conflicts with all other transactions. */
*piConflict = 1;
rc = SQLITE_BUSY_SNAPSHOT;
}
for(iHash=walFramePage(iFirst); rc==SQLITE_OK && iHash<=iLast; iHash++){
WalHashLoc sLoc;
rc = walHashGet(pWal, iHash, &sLoc);
if( rc==SQLITE_OK ){
u32 i, iMin, iMax;
assert( head.mxFrame>=sLoc.iZero );
iMin = (sLoc.iZero >= iFirst) ? 1 : (iFirst - sLoc.iZero);
iMax = (iHash==0) ? HASHTABLE_NPAGE_ONE : HASHTABLE_NPAGE;
if( iMax>(head.mxFrame-sLoc.iZero) ) iMax = (head.mxFrame-sLoc.iZero);
for(i=iMin; rc==SQLITE_OK && i<=iMax; i++){
PgHdr *pPg;
if( sLoc.aPgno[i-1]==1 ){
/* Check that the schema cookie has not been modified. If
** it has not, the commit can proceed. */
u8 aNew[4];
u8 *aOld = &((u8*)pPg1->pData)[40];
int sz;
i64 iOffset;
sz = pWal->hdr.szPage;
sz = (sz&0xfe00) + ((sz&0x0001)<<16);
iOffset = walFrameOffset(i+sLoc.iZero, sz) + WAL_FRAME_HDRSIZE+40;
rc = sqlite3OsRead(pWal->pWalFd, aNew, sizeof(aNew), iOffset);
if( rc==SQLITE_OK && memcmp(aOld, aNew, sizeof(aNew)) ){
rc = SQLITE_BUSY_SNAPSHOT;
}
}else if( sqlite3BitvecTestNotNull(pAllRead, sLoc.aPgno[i-1]) ){
*piConflict = sLoc.aPgno[i-1];
rc = SQLITE_BUSY_SNAPSHOT;
}else
if( (pPg = sqlite3PagerLookup(pPg1->pPager, sLoc.aPgno[i-1])) ){
/* Page aPgno[i-1], which is present in the pager cache, has been
** modified since the current CONCURRENT transaction was started.
** However it was not read by the current transaction, so is not
** a conflict. There are two possibilities: (a) the page was
** allocated at the of the file by the current transaction or
** (b) was present in the cache at the start of the transaction.
**
** For case (a), do nothing. This page will be moved within the
** database file by the commit code to avoid the conflict. The
** call to PagerUnref() is to release the reference grabbed by
** the sqlite3PagerLookup() above.
**
** In case (b), drop the page from the cache - otherwise
** following the snapshot upgrade the cache would be inconsistent
** with the database as stored on disk. */
if( sqlite3PagerIswriteable(pPg) ){
sqlite3PagerUnref(pPg);
}else{
sqlite3PcacheDrop(pPg);
}
}
}
}
}
}
}
pWal->nPriorFrame = pWal->hdr.mxFrame;
return rc;
}
/*
** This function is only ever called when committing a "BEGIN CONCURRENT"
** transaction. It may be assumed that no frames have been written to
** the wal file. The second parameter is a pointer to the in-memory
** representation of page 1 of the database (which may or may not be
** dirty). The third is a bitvec with a bit set for each page in the
** database file that was read by the current concurrent transaction.
**
** This function performs three tasks:
**
** 1) It obtains the WRITER lock on the wal file,
**
** 2) It checks that there are no conflicts between the current
** transaction and any transactions committed to the wal file since
** it was opened, and
**
** 3) It ejects any non-dirty pages from the page-cache that have been
** written by another client since the CONCURRENT transaction was started
** (so as to avoid ending up with an inconsistent cache after the
** current transaction is committed).
**
** If no error occurs and the caller may proceed with committing the
** transaction, SQLITE_OK is returned. SQLITE_BUSY is returned if the WRITER
** lock cannot be obtained. Or, if the WRITER lock can be obtained but there
** are conflicts with a committed transaction, SQLITE_BUSY_SNAPSHOT. Finally,
** if an error (i.e. an OOM condition or IO error), an SQLite error code
** is returned.
*/
int sqlite3WalLockForCommit(
Wal *pWal,
PgHdr *pPg1,
Bitvec *pAllRead,
Pgno *piConflict
){
int rc = SQLITE_OK;
SEH_TRY {
rc = walLockForCommit(pWal, pPg1, pAllRead, piConflict);
} SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; )
return rc;
}
/* !defined(SQLITE_OMIT_CONCURRENT)
**
** This function is called as part of committing an CONCURRENT transaction.
** It is assumed that sqlite3WalLockForCommit() has already been successfully
** called and so (a) the WRITER lock is held and (b) it is known that the
** wal-index-header stored in shared memory is not corrupt.
**
** Before returning, this function upgrades the client so that it is
** operating on the database snapshot currently at the head of the wal file
** (even if the CONCURRENT transaction ran against an older snapshot).
**
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
*/
int sqlite3WalUpgradeSnapshot(Wal *pWal){
int rc = SQLITE_OK;
assert( pWal->writeLock );
SEH_TRY {
assert( pWal->szPage==pWal->hdr.szPage );
memcpy(&pWal->hdr, (void*)walIndexHdr(pWal), sizeof(WalIndexHdr));
assert( pWal->szPage==pWal->hdr.szPage || pWal->szPage==0 );
pWal->szPage = pWal->hdr.szPage;
/* If this client has its read-lock on slot aReadmark[0] and the entire
** wal has not been checkpointed, switch it to a different slot. Otherwise
** any reads performed between now and committing the transaction will
** read from the old snapshot - not the one just upgraded to. */
if( pWal->readLock==0 && pWal->hdr.mxFrame!=walCkptInfo(pWal)->nBackfill ){
rc = walUpgradeReadlock(pWal);
}
} SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; )
return rc;
}
#endif /* SQLITE_OMIT_CONCURRENT */
/*
** End a write transaction. The commit has already been done. This
** routine merely releases the lock.
@ -3729,20 +3966,32 @@ int sqlite3WalEndWriteTransaction(Wal *pWal){
** Otherwise, if the callback function does not return an error, this
** function returns SQLITE_OK.
*/
int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){
int sqlite3WalUndo(
Wal *pWal,
int (*xUndo)(void *, Pgno),
void *pUndoCtx,
int bConcurrent /* True if this is a CONCURRENT transaction */
){
int rc = SQLITE_OK;
if( ALWAYS(pWal->writeLock) ){
if( pWal->writeLock ){
Pgno iMax = pWal->hdr.mxFrame;
Pgno iFrame;
/* Restore the clients cache of the wal-index header to the state it
** was in before the client began writing to the database.
*/
SEH_TRY {
/* Restore the clients cache of the wal-index header to the state it
** was in before the client began writing to the database.
*/
memcpy(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr));
#ifndef SQLITE_OMIT_CONCURRENT
if( bConcurrent ){
pWal->hdr.aCksum[0]++;
}
#else
UNUSED_PARAMETER(bConcurrent);
#endif
for(iFrame=pWal->hdr.mxFrame+1;
ALWAYS(rc==SQLITE_OK) && iFrame<=iMax;
for(iFrame=pWal->hdr.mxFrame+1;
ALWAYS(rc==SQLITE_OK) && iFrame<=iMax;
iFrame++
){
/* This call cannot fail. Unless the page for which the page number
@ -3760,8 +4009,7 @@ int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){
rc = xUndo(pUndoCtx, walFramePgno(pWal, iFrame));
}
if( iMax!=pWal->hdr.mxFrame ) walCleanupHash(pWal);
}
SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; )
} SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; )
}
return rc;
}
@ -3773,7 +4021,6 @@ int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){
** point in the event of a savepoint rollback (via WalSavepointUndo()).
*/
void sqlite3WalSavepoint(Wal *pWal, u32 *aWalData){
assert( pWal->writeLock );
aWalData[0] = pWal->hdr.mxFrame;
aWalData[1] = pWal->hdr.aFrameCksum[0];
aWalData[2] = pWal->hdr.aFrameCksum[1];
@ -3789,7 +4036,7 @@ void sqlite3WalSavepoint(Wal *pWal, u32 *aWalData){
int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData){
int rc = SQLITE_OK;
assert( pWal->writeLock );
assert( pWal->writeLock || aWalData[0]==pWal->hdr.mxFrame );
assert( aWalData[3]!=pWal->nCkpt || aWalData[0]<=pWal->hdr.mxFrame );
if( aWalData[3]!=pWal->nCkpt ){
@ -3828,14 +4075,13 @@ int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData){
*/
static int walRestartLog(Wal *pWal){
int rc = SQLITE_OK;
int cnt;
if( pWal->readLock==0 ){
volatile WalCkptInfo *pInfo = walCkptInfo(pWal);
assert( pInfo->nBackfill==pWal->hdr.mxFrame );
if( pInfo->nBackfill>0 ){
u32 salt1;
sqlite3_randomness(4, &salt1);
sqlite3FastRandomness(&pWal->sPrng, 4, &salt1);
rc = walLockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
if( rc==SQLITE_OK ){
/* If all readers are using WAL_READ_LOCK(0) (in other words if no
@ -3849,21 +4095,21 @@ static int walRestartLog(Wal *pWal){
** to handle if this transaction is rolled back. */
walRestartHdr(pWal, salt1);
walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
pWal->nPriorFrame = 0;
}else if( rc!=SQLITE_BUSY ){
return rc;
}
}
walUnlockShared(pWal, WAL_READ_LOCK(0));
pWal->readLock = -1;
cnt = 0;
do{
int notUsed;
rc = walTryBeginRead(pWal, &notUsed, 1, &cnt);
}while( rc==WAL_RETRY );
assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */
testcase( (rc&0xff)==SQLITE_IOERR );
testcase( rc==SQLITE_PROTOCOL );
testcase( rc==SQLITE_OK );
/* Regardless of whether or not the wal file was restarted, change the
** read-lock held by this client to a slot other than aReadmark[0].
** Clients with a lock on aReadmark[0] read from the database file
** only - never from the wal file. This means that if a writer holding
** a lock on aReadmark[0] were to commit a transaction but not close the
** read-transaction, subsequent read operations would read directly from
** the database file - ignoring the new pages just appended
** to the wal file. */
rc = walUpgradeReadlock(pWal);
}
return rc;
}
@ -4047,7 +4293,7 @@ static int walFrames(
sqlite3Put4byte(&aWalHdr[4], WAL_MAX_VERSION);
sqlite3Put4byte(&aWalHdr[8], szPage);
sqlite3Put4byte(&aWalHdr[12], pWal->nCkpt);
if( pWal->nCkpt==0 ) sqlite3_randomness(8, pWal->hdr.aSalt);
if( pWal->nCkpt==0 ) sqlite3FastRandomness(&pWal->sPrng, 8, pWal->hdr.aSalt);
memcpy(&aWalHdr[16], pWal->hdr.aSalt, 8);
walChecksumBytes(1, aWalHdr, WAL_HDRSIZE-2*4, 0, aCksum);
sqlite3Put4byte(&aWalHdr[24], aCksum[0]);
@ -4126,6 +4372,7 @@ static int walFrames(
p->flags |= PGHDR_WAL_APPEND;
}
/* Recalculate checksums within the wal file if required. */
if( isCommit && pWal->iReCksum ){
rc = walRewriteChecksums(pWal, iFrame);
@ -4340,9 +4587,9 @@ int sqlite3WalCheckpoint(
if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){
rc = SQLITE_CORRUPT_BKPT;
}else{
rc = walCheckpoint(pWal, db, eMode2, xBusy2, pBusyArg, sync_flags,zBuf);
rc = walCheckpoint(pWal, db, eMode2, xBusy2, pBusyArg, sync_flags, zBuf);
}
/* If no error occurred, set the output variables. */
if( rc==SQLITE_OK || rc==SQLITE_BUSY ){
if( pnLog ) *pnLog = (int)pWal->hdr.mxFrame;
@ -4590,4 +4837,16 @@ sqlite3_file *sqlite3WalFile(Wal *pWal){
return pWal->pWalFd;
}
/*
** Return the values required by sqlite3_wal_info().
*/
int sqlite3WalInfo(Wal *pWal, u32 *pnPrior, u32 *pnFrame){
int rc = SQLITE_OK;
if( pWal ){
*pnFrame = pWal->hdr.mxFrame;
*pnPrior = pWal->nPriorFrame;
}
return rc;
}
#endif /* #ifndef SQLITE_OMIT_WAL */

View File

@ -34,7 +34,7 @@
# define sqlite3WalDbsize(y) 0
# define sqlite3WalBeginWriteTransaction(y) 0
# define sqlite3WalEndWriteTransaction(x) 0
# define sqlite3WalUndo(x,y,z) 0
# define sqlite3WalUndo(w,x,y,z) 0
# define sqlite3WalSavepoint(y,z)
# define sqlite3WalSavepointUndo(y,z) 0
# define sqlite3WalFrames(u,v,w,x,y,z) 0
@ -84,7 +84,7 @@ int sqlite3WalBeginWriteTransaction(Wal *pWal);
int sqlite3WalEndWriteTransaction(Wal *pWal);
/* Undo any frames written (but not committed) to the log */
int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx);
int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx, int);
/* Return an integer that records the current (uncommitted) write
** position in the WAL */
@ -137,6 +137,15 @@ int sqlite3WalSnapshotCheck(Wal *pWal, sqlite3_snapshot *pSnapshot);
void sqlite3WalSnapshotUnlock(Wal *pWal);
#endif
#ifndef SQLITE_OMIT_CONCURRENT
/* Tell the wal layer that we want to commit a concurrent transaction */
int sqlite3WalLockForCommit(Wal *pWal, PgHdr *pPg, Bitvec *pRead, Pgno*);
/* Upgrade the state of the client to take into account changes written
** by other connections */
int sqlite3WalUpgradeSnapshot(Wal *pWal);
#endif /* SQLITE_OMIT_CONCURRENT */
#ifdef SQLITE_ENABLE_ZIPVFS
/* If the WAL file is not empty, return the number of bytes of content
** stored in each frame (i.e. the db page-size when the WAL was created).
@ -156,5 +165,8 @@ 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
test/bc_test1.c Normal file
View File

@ -0,0 +1,556 @@
/*
** 2016-05-07
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
*/
#include <sqlite3.h>
#include <stdlib.h>
#include <stddef.h>
#include "tt3_core.c"
#ifdef USE_OSINST
# include "../src/test_osinst.c"
#else
# define vfslog_time() 0
#endif
typedef struct Config Config;
typedef struct ThreadCtx ThreadCtx;
#define THREAD_TIME_INSERT 0
#define THREAD_TIME_COMMIT 1
#define THREAD_TIME_ROLLBACK 2
#define THREAD_TIME_WRITER 3
#define THREAD_TIME_CKPT 4
struct ThreadCtx {
Config *pConfig;
Sqlite *pDb;
Error *pErr;
sqlite3_int64 aTime[5];
};
struct Config {
int nIPT; /* --inserts-per-transaction */
int nThread; /* --threads */
int nSecond; /* --seconds */
int bMutex; /* --mutex */
int nAutoCkpt; /* --autockpt */
int bRm; /* --rm */
int bClearCache; /* --clear-cache */
int nMmap; /* mmap limit in MB */
char *zFile;
int bOsinst; /* True to use osinst */
ThreadCtx *aCtx; /* Array of size nThread */
pthread_cond_t cond;
pthread_mutex_t mutex;
int nCondWait; /* Number of threads waiting on hCond */
sqlite3_vfs *pVfs;
};
typedef struct VfsWrapperFd VfsWrapperFd;
struct VfsWrapperFd {
sqlite3_file base; /* Base class */
int bWriter; /* True if holding shm WRITER lock */
int iTid;
Config *pConfig;
sqlite3_file *pFd; /* Underlying file descriptor */
};
/* Methods of the wrapper VFS */
static int vfsWrapOpen(sqlite3_vfs*, const char*, sqlite3_file*, int, int*);
static int vfsWrapDelete(sqlite3_vfs*, const char*, int);
static int vfsWrapAccess(sqlite3_vfs*, const char*, int, int*);
static int vfsWrapFullPathname(sqlite3_vfs*, const char *, int, char*);
static void *vfsWrapDlOpen(sqlite3_vfs*, const char*);
static void vfsWrapDlError(sqlite3_vfs*, int, char*);
static void (*vfsWrapDlSym(sqlite3_vfs*,void*, const char*))(void);
static void vfsWrapDlClose(sqlite3_vfs*, void*);
static int vfsWrapRandomness(sqlite3_vfs*, int, char*);
static int vfsWrapSleep(sqlite3_vfs*, int);
static int vfsWrapCurrentTime(sqlite3_vfs*, double*);
static int vfsWrapGetLastError(sqlite3_vfs*, int, char*);
static int vfsWrapCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
static int vfsWrapSetSystemCall(sqlite3_vfs*, const char*, sqlite3_syscall_ptr);
static sqlite3_syscall_ptr vfsWrapGetSystemCall(sqlite3_vfs*, const char*);
static const char *vfsWrapNextSystemCall(sqlite3_vfs*, const char*);
/* Methods of wrapper sqlite3_io_methods object (see vfsWrapOpen()) */
static int vfsWrapClose(sqlite3_file*);
static int vfsWrapRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
static int vfsWrapWrite(sqlite3_file*, const void*, int iAmt, sqlite3_int64);
static int vfsWrapTruncate(sqlite3_file*, sqlite3_int64 size);
static int vfsWrapSync(sqlite3_file*, int flags);
static int vfsWrapFileSize(sqlite3_file*, sqlite3_int64 *pSize);
static int vfsWrapLock(sqlite3_file*, int);
static int vfsWrapUnlock(sqlite3_file*, int);
static int vfsWrapCheckReservedLock(sqlite3_file*, int *pResOut);
static int vfsWrapFileControl(sqlite3_file*, int op, void *pArg);
static int vfsWrapSectorSize(sqlite3_file*);
static int vfsWrapDeviceCharacteristics(sqlite3_file*);
static int vfsWrapShmMap(sqlite3_file*, int iPg, int, int, void volatile**);
static int vfsWrapShmLock(sqlite3_file*, int offset, int n, int flags);
static void vfsWrapShmBarrier(sqlite3_file*);
static int vfsWrapShmUnmap(sqlite3_file*, int deleteFlag);
static int vfsWrapFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **);
static int vfsWrapUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p);
static int vfsWrapOpen(
sqlite3_vfs *pVfs,
const char *zName,
sqlite3_file *pFd,
int flags,
int *fout
){
static sqlite3_io_methods methods = {
3,
vfsWrapClose, vfsWrapRead, vfsWrapWrite,
vfsWrapTruncate, vfsWrapSync, vfsWrapFileSize,
vfsWrapLock, vfsWrapUnlock, vfsWrapCheckReservedLock,
vfsWrapFileControl, vfsWrapSectorSize, vfsWrapDeviceCharacteristics,
vfsWrapShmMap, vfsWrapShmLock, vfsWrapShmBarrier,
vfsWrapShmUnmap, vfsWrapFetch, vfsWrapUnfetch
};
Config *pConfig = (Config*)pVfs->pAppData;
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
int rc;
memset(pWrapper, 0, sizeof(VfsWrapperFd));
if( flags & SQLITE_OPEN_MAIN_DB ){
pWrapper->iTid = (int)sqlite3_uri_int64(zName, "tid", 0);
}
pWrapper->pFd = (sqlite3_file*)&pWrapper[1];
pWrapper->pConfig = pConfig;
rc = pConfig->pVfs->xOpen(pConfig->pVfs, zName, pWrapper->pFd, flags, fout);
if( rc==SQLITE_OK ){
pWrapper->base.pMethods = &methods;
}
return rc;
}
static int vfsWrapDelete(sqlite3_vfs *pVfs, const char *a, int b){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xDelete(pConfig->pVfs, a, b);
}
static int vfsWrapAccess(sqlite3_vfs *pVfs, const char *a, int b, int *c){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xAccess(pConfig->pVfs, a, b, c);
}
static int vfsWrapFullPathname(sqlite3_vfs *pVfs, const char *a, int b, char*c){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xFullPathname(pConfig->pVfs, a, b, c);
}
static void *vfsWrapDlOpen(sqlite3_vfs *pVfs, const char *a){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xDlOpen(pConfig->pVfs, a);
}
static void vfsWrapDlError(sqlite3_vfs *pVfs, int a, char *b){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xDlError(pConfig->pVfs, a, b);
}
static void (*vfsWrapDlSym(sqlite3_vfs *pVfs, void *a, const char *b))(void){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xDlSym(pConfig->pVfs, a, b);
}
static void vfsWrapDlClose(sqlite3_vfs *pVfs, void *a){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xDlClose(pConfig->pVfs, a);
}
static int vfsWrapRandomness(sqlite3_vfs *pVfs, int a, char *b){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xRandomness(pConfig->pVfs, a, b);
}
static int vfsWrapSleep(sqlite3_vfs *pVfs, int a){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xSleep(pConfig->pVfs, a);
}
static int vfsWrapCurrentTime(sqlite3_vfs *pVfs, double *a){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xCurrentTime(pConfig->pVfs, a);
}
static int vfsWrapGetLastError(sqlite3_vfs *pVfs, int a, char *b){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xGetLastError(pConfig->pVfs, a, b);
}
static int vfsWrapCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *a){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xCurrentTimeInt64(pConfig->pVfs, a);
}
static int vfsWrapSetSystemCall(
sqlite3_vfs *pVfs,
const char *a,
sqlite3_syscall_ptr b
){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xSetSystemCall(pConfig->pVfs, a, b);
}
static sqlite3_syscall_ptr vfsWrapGetSystemCall(
sqlite3_vfs *pVfs,
const char *a
){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xGetSystemCall(pConfig->pVfs, a);
}
static const char *vfsWrapNextSystemCall(sqlite3_vfs *pVfs, const char *a){
Config *pConfig = (Config*)pVfs->pAppData;
return pConfig->pVfs->xNextSystemCall(pConfig->pVfs, a);
}
static int vfsWrapClose(sqlite3_file *pFd){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
pWrapper->pFd->pMethods->xClose(pWrapper->pFd);
pWrapper->pFd = 0;
return SQLITE_OK;
}
static int vfsWrapRead(sqlite3_file *pFd, void *a, int b, sqlite3_int64 c){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xRead(pWrapper->pFd, a, b, c);
}
static int vfsWrapWrite(
sqlite3_file *pFd,
const void *a, int b,
sqlite3_int64 c
){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xWrite(pWrapper->pFd, a, b, c);
}
static int vfsWrapTruncate(sqlite3_file *pFd, sqlite3_int64 a){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xTruncate(pWrapper->pFd, a);
}
static int vfsWrapSync(sqlite3_file *pFd, int a){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xSync(pWrapper->pFd, a);
}
static int vfsWrapFileSize(sqlite3_file *pFd, sqlite3_int64 *a){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xFileSize(pWrapper->pFd, a);
}
static int vfsWrapLock(sqlite3_file *pFd, int a){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xLock(pWrapper->pFd, a);
}
static int vfsWrapUnlock(sqlite3_file *pFd, int a){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xUnlock(pWrapper->pFd, a);
}
static int vfsWrapCheckReservedLock(sqlite3_file *pFd, int *a){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xCheckReservedLock(pWrapper->pFd, a);
}
static int vfsWrapFileControl(sqlite3_file *pFd, int a, void *b){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xFileControl(pWrapper->pFd, a, b);
}
static int vfsWrapSectorSize(sqlite3_file *pFd){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xSectorSize(pWrapper->pFd);
}
static int vfsWrapDeviceCharacteristics(sqlite3_file *pFd){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xDeviceCharacteristics(pWrapper->pFd);
}
static int vfsWrapShmMap(
sqlite3_file *pFd,
int a, int b, int c,
void volatile **d
){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xShmMap(pWrapper->pFd, a, b, c, d);
}
static int vfsWrapShmLock(sqlite3_file *pFd, int offset, int n, int flags){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
Config *pConfig = pWrapper->pConfig;
int bMutex = 0;
int rc;
if( (offset==0 && n==1)
&& (flags & SQLITE_SHM_LOCK) && (flags & SQLITE_SHM_EXCLUSIVE)
){
pthread_mutex_lock(&pConfig->mutex);
pWrapper->bWriter = 1;
bMutex = 1;
if( pWrapper->iTid ){
sqlite3_int64 t = vfslog_time();
pConfig->aCtx[pWrapper->iTid-1].aTime[THREAD_TIME_WRITER] -= t;
}
}
rc = pWrapper->pFd->pMethods->xShmLock(pWrapper->pFd, offset, n, flags);
if( (rc!=SQLITE_OK && bMutex)
|| (offset==0 && (flags & SQLITE_SHM_UNLOCK) && pWrapper->bWriter)
){
assert( pWrapper->bWriter );
pthread_mutex_unlock(&pConfig->mutex);
pWrapper->bWriter = 0;
if( pWrapper->iTid ){
sqlite3_int64 t = vfslog_time();
pConfig->aCtx[pWrapper->iTid-1].aTime[THREAD_TIME_WRITER] += t;
}
}
return rc;
}
static void vfsWrapShmBarrier(sqlite3_file *pFd){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xShmBarrier(pWrapper->pFd);
}
static int vfsWrapShmUnmap(sqlite3_file *pFd, int a){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xShmUnmap(pWrapper->pFd, a);
}
static int vfsWrapFetch(sqlite3_file *pFd, sqlite3_int64 a, int b, void **c){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xFetch(pWrapper->pFd, a, b, c);
}
static int vfsWrapUnfetch(sqlite3_file *pFd, sqlite3_int64 a, void *b){
VfsWrapperFd *pWrapper = (VfsWrapperFd*)pFd;
return pWrapper->pFd->pMethods->xUnfetch(pWrapper->pFd, a, b);
}
static void create_vfs(Config *pConfig){
static sqlite3_vfs vfs = {
3, 0, 0, 0, "wrapper", 0,
vfsWrapOpen, vfsWrapDelete, vfsWrapAccess,
vfsWrapFullPathname, vfsWrapDlOpen, vfsWrapDlError,
vfsWrapDlSym, vfsWrapDlClose, vfsWrapRandomness,
vfsWrapSleep, vfsWrapCurrentTime, vfsWrapGetLastError,
vfsWrapCurrentTimeInt64, vfsWrapSetSystemCall, vfsWrapGetSystemCall,
vfsWrapNextSystemCall
};
sqlite3_vfs *pVfs;
pVfs = sqlite3_vfs_find(0);
vfs.mxPathname = pVfs->mxPathname;
vfs.szOsFile = pVfs->szOsFile + sizeof(VfsWrapperFd);
vfs.pAppData = (void*)pConfig;
pConfig->pVfs = pVfs;
sqlite3_vfs_register(&vfs, 1);
}
/*
** Wal hook used by connections in thread_main().
*/
static int thread_wal_hook(
void *pArg, /* Pointer to ThreadCtx object */
sqlite3 *db,
const char *zDb,
int nFrame
){
ThreadCtx *pCtx = (ThreadCtx*)pArg;
Config *pConfig = pCtx->pConfig;
if( pConfig->nAutoCkpt && nFrame>=pConfig->nAutoCkpt ){
pCtx->aTime[THREAD_TIME_CKPT] -= vfslog_time();
pthread_mutex_lock(&pConfig->mutex);
if( pConfig->nCondWait>=0 ){
pConfig->nCondWait++;
if( pConfig->nCondWait==pConfig->nThread ){
execsql(pCtx->pErr, pCtx->pDb, "PRAGMA wal_checkpoint");
pthread_cond_broadcast(&pConfig->cond);
}else{
pthread_cond_wait(&pConfig->cond, &pConfig->mutex);
}
pConfig->nCondWait--;
}
pthread_mutex_unlock(&pConfig->mutex);
pCtx->aTime[THREAD_TIME_CKPT] += vfslog_time();
}
return SQLITE_OK;
}
static char *thread_main(int iTid, void *pArg){
Config *pConfig = (Config*)pArg;
Error err = {0}; /* Error code and message */
Sqlite db = {0}; /* SQLite database connection */
int nAttempt = 0; /* Attempted transactions */
int nCommit = 0; /* Successful transactions */
int j;
ThreadCtx *pCtx = &pConfig->aCtx[iTid-1];
char *zUri = 0;
#ifdef USE_OSINST
char *zOsinstName = 0;
char *zLogName = 0;
if( pConfig->bOsinst ){
zOsinstName = sqlite3_mprintf("osinst%d", iTid);
zLogName = sqlite3_mprintf("bc_test1.log.%d.%d", (int)getpid(), iTid);
zUri = sqlite3_mprintf(
"file:%s?vfs=%s&tid=%d", pConfig->zFile, zOsinstName, iTid
);
sqlite3_vfslog_new(zOsinstName, 0, zLogName);
opendb(&err, &db, zUri, 0);
}else
#endif
{
zUri = sqlite3_mprintf("file:%s?tid=%d", pConfig->zFile, iTid);
opendb(&err, &db, zUri, 0);
}
sqlite3_busy_handler(db.db, 0, 0);
sql_script_printf(&err, &db,
"PRAGMA wal_autocheckpoint = 0;"
"PRAGMA synchronous = 0;"
"PRAGMA mmap_size = %lld;",
(i64)(pConfig->nMmap) * 1024 * 1024
);
pCtx->pConfig = pConfig;
pCtx->pErr = &err;
pCtx->pDb = &db;
sqlite3_wal_hook(db.db, thread_wal_hook, (void*)pCtx);
while( !timetostop(&err) ){
execsql(&err, &db, "BEGIN CONCURRENT");
pCtx->aTime[THREAD_TIME_INSERT] -= vfslog_time();
for(j=0; j<pConfig->nIPT; j++){
execsql(&err, &db,
"INSERT INTO t1 VALUES"
"(randomblob(10), randomblob(20), randomblob(30), randomblob(200))"
);
}
pCtx->aTime[THREAD_TIME_INSERT] += vfslog_time();
pCtx->aTime[THREAD_TIME_COMMIT] -= vfslog_time();
execsql(&err, &db, "COMMIT");
pCtx->aTime[THREAD_TIME_COMMIT] += vfslog_time();
pCtx->aTime[THREAD_TIME_ROLLBACK] -= vfslog_time();
nAttempt++;
if( err.rc==SQLITE_OK ){
nCommit++;
}else{
clear_error(&err, SQLITE_BUSY);
execsql(&err, &db, "ROLLBACK");
}
pCtx->aTime[THREAD_TIME_ROLLBACK] += vfslog_time();
if( pConfig->bClearCache ){
sqlite3_db_release_memory(db.db);
}
}
closedb(&err, &db);
#ifdef USE_OSINST
if( pConfig->bOsinst ){
sqlite3_vfslog_finalize(zOsinstName);
sqlite3_free(zOsinstName);
sqlite3_free(zLogName);
}
#endif
sqlite3_free(zUri);
pthread_mutex_lock(&pConfig->mutex);
pConfig->nCondWait = -1;
pthread_cond_broadcast(&pConfig->cond);
pthread_mutex_unlock(&pConfig->mutex);
return sqlite3_mprintf("commits: %d/%d insert: %dms"
" commit: %dms"
" rollback: %dms"
" writer: %dms"
" checkpoint: %dms",
nCommit, nAttempt,
(int)(pCtx->aTime[THREAD_TIME_INSERT]/1000),
(int)(pCtx->aTime[THREAD_TIME_COMMIT]/1000),
(int)(pCtx->aTime[THREAD_TIME_ROLLBACK]/1000),
(int)(pCtx->aTime[THREAD_TIME_WRITER]/1000),
(int)(pCtx->aTime[THREAD_TIME_CKPT]/1000)
);
}
int main(int argc, const char **argv){
Error err = {0}; /* Error code and message */
Sqlite db = {0}; /* SQLite database connection */
Threadset threads = {0}; /* Test threads */
Config conf = {5, 3, 5};
int i;
CmdlineArg apArg[] = {
{ "-seconds", CMDLINE_INT, offsetof(Config, nSecond) },
{ "-inserts", CMDLINE_INT, offsetof(Config, nIPT) },
{ "-threads", CMDLINE_INT, offsetof(Config, nThread) },
{ "-mutex", CMDLINE_BOOL, offsetof(Config, bMutex) },
{ "-rm", CMDLINE_BOOL, offsetof(Config, bRm) },
{ "-autockpt",CMDLINE_INT, offsetof(Config, nAutoCkpt) },
{ "-mmap", CMDLINE_INT, offsetof(Config, nMmap) },
{ "-clear-cache", CMDLINE_BOOL, offsetof(Config, bClearCache) },
{ "-file", CMDLINE_STRING, offsetof(Config, zFile) },
{ "-osinst", CMDLINE_BOOL, offsetof(Config, bOsinst) },
{ 0, 0, 0 }
};
conf.nAutoCkpt = 1000;
cmdline_process(apArg, argc, argv, (void*)&conf);
if( err.rc==SQLITE_OK ){
char *z = cmdline_construct(apArg, (void*)&conf);
printf("With: %s\n", z);
sqlite3_free(z);
}
if( conf.zFile==0 ){
conf.zFile = "xyz.db";
}
/* Create the special VFS - "wrapper". And the mutex and condition
** variable. */
create_vfs(&conf);
pthread_mutex_init(&conf.mutex, 0);
pthread_cond_init(&conf.cond, 0);
conf.aCtx = sqlite3_malloc(sizeof(ThreadCtx) * conf.nThread);
memset(conf.aCtx, 0, sizeof(ThreadCtx) * conf.nThread);
/* Ensure the schema has been created */
opendb(&err, &db, conf.zFile, conf.bRm);
sql_script(&err, &db,
"PRAGMA journal_mode = wal;"
"CREATE TABLE IF NOT EXISTS t1(a PRIMARY KEY, b, c, d) WITHOUT ROWID;"
"CREATE INDEX IF NOT EXISTS t1b ON t1(b);"
"CREATE INDEX IF NOT EXISTS t1c ON t1(c);"
);
setstoptime(&err, conf.nSecond*1000);
if( conf.nThread==1 ){
char *z = thread_main(1, (void*)&conf);
printf("Thread 0 says: %s\n", (z==0 ? "..." : z));
fflush(stdout);
}else{
for(i=0; i<conf.nThread; i++){
launch_thread(&err, &threads, thread_main, (void*)&conf);
}
join_all_threads(&err, &threads);
}
if( err.rc==SQLITE_OK ){
printf("Database is %dK\n", (int)(filesize(&err, conf.zFile) / 1024));
}
if( err.rc==SQLITE_OK ){
char *zWal = sqlite3_mprintf("%s-wal", conf.zFile);
printf("Wal file is %dK\n", (int)(filesize(&err, zWal) / 1024));
}
closedb(&err, &db);
print_and_free_err(&err);
return 0;
}

127
test/concfault.test Normal file
View File

@ -0,0 +1,127 @@
# 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
test/concurrent.test Normal file
View File

@ -0,0 +1,688 @@
# 2015 July 26
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
set ::testprefix concurrent
ifcapable !concurrent {
finish_test
return
}
do_execsql_test 1.0 {
PRAGMA journal_mode = wal;
} {wal}
do_execsql_test 1.1 {
CREATE TABLE t1(k INTEGER PRIMARY KEY, v);
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(1, 'abcd');
COMMIT;
}
do_execsql_test 1.2 {
SELECT * FROM t1;
} {1 abcd}
do_execsql_test 1.3 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(2, 'efgh');
ROLLBACK;
}
do_execsql_test 1.4 {
SELECT * FROM t1;
} {1 abcd}
#-------------------------------------------------------------------------
# CONCURRENT transactions cannot do cache spills.
#
foreach {tn trans spill} {
1 {BEGIN CONCURRENT} 0
2 {BEGIN} 1
} {
do_test 1.5.$tn {
sqlite3 db2 test.db
set walsz [file size test.db-wal]
execsql { PRAGMA cache_size = 10 } db2
execsql $trans db2
execsql {
WITH cnt(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM cnt WHERE i<50)
INSERT INTO t1(v) SELECT randomblob(900) FROM cnt;
} db2
expr {[file size test.db-wal]==$walsz}
} [expr !$spill]
execsql ROLLBACK db2
db2 close
}
#-------------------------------------------------------------------------
# CONCURRENT transactions man not be committed while there are active
# readers.
do_execsql_test 1.6.setup {
DROP TABLE t1;
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 2);
INSERT INTO t1 VALUES(3, 4);
INSERT INTO t1 VALUES(5, 6);
}
foreach {tn trans commit_ok} {
1 {BEGIN CONCURRENT} 0
2 {BEGIN} 1
} {
do_test 1.6.$tn.1 {
set stmt [sqlite3_prepare db "SELECT * FROM t1" -1 dummy]
sqlite3_step $stmt
} SQLITE_ROW
do_test 1.6.$tn.2 {
execsql $trans
execsql { INSERT INTO t1 VALUES(7, 8) }
} {}
if { $commit_ok } {
do_test 1.6.$tn.3 { catchsql COMMIT } {0 {}}
} else {
do_test 1.6.$tn.4 { catchsql COMMIT } {/1 {cannot commit transaction .*}/}
}
sqlite3_finalize $stmt
catchsql ROLLBACK
}
#-------------------------------------------------------------------------
# CONCURRENT transactions may not modify the db schema.
#
sqlite3 db2 test.db
foreach {tn sql} {
1 { CREATE TABLE xx(a, b) }
2 { DROP TABLE t1 }
3 { CREATE INDEX i1 ON t1(a) }
4 { CREATE VIEW v1 AS SELECT * FROM t1 }
} {
do_catchsql_test 1.7.0.$tn.1 "
BEGIN CONCURRENT;
$sql
" {0 {}}
db2 eval {INSERT INTO t1 DEFAULT VALUES}
do_catchsql_test 1.7.0.$tn.2 {
COMMIT
} {1 {database is locked}}
do_execsql_test 1.7.0.$tn.2 ROLLBACK
do_execsql_test 1.7.0.$tn.3 {
SELECT sql FROM sqlite_master;
SELECT sql FROM sqlite_temp_master;
} {{CREATE TABLE t1(a, b)}}
#do_execsql_test 1.7.0.$tn.3 COMMIT
}
# Except the temp db schema.
foreach {tn sql} {
1 { CREATE TEMP TABLE xx(a, b) }
2 { DROP TABLE xx }
3 { CREATE TEMP TABLE yy(a, b) }
4 { CREATE VIEW temp.v1 AS SELECT * FROM t1 }
5 { CREATE INDEX yyi1 ON yy(a); }
6 { CREATE TABLE temp.zz(a, b) }
} {
do_catchsql_test 1.7.1.$tn.1 "
BEGIN CONCURRENT;
$sql
" {0 {}}
do_execsql_test 1.7.1.$tn.2 COMMIT
}
do_execsql_test 1.7.1.x {
SELECT sql FROM sqlite_master;
SELECT sql FROM sqlite_temp_master;
} {
{CREATE TABLE t1(a, b)}
{CREATE TABLE yy(a, b)}
{CREATE VIEW v1 AS SELECT * FROM t1}
{CREATE INDEX yyi1 ON yy(a)}
{CREATE TABLE zz(a, b)}
}
db2 close
#-------------------------------------------------------------------------
# If an auto-vacuum database is written within an CONCURRENT transaction, it
# is handled in the same way as for a non-CONCURRENT transaction.
#
reset_db
do_execsql_test 1.8.1 {
PRAGMA auto_vacuum = 1;
PRAGMA journal_mode = wal;
CREATE TABLE t1(x, y);
INSERT INTO t1 VALUES('x', 'y');
} {wal}
do_execsql_test 1.8.2 {
BEGIN CONCURRENT;
SELECT * FROM t1;
COMMIT;
} {x y}
do_catchsql_test 1.8.3 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES('a', 'b');
} {0 {}}
do_test 1.8.4 {
sqlite3 db2 test.db
catchsql {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES('c', 'd');
} db2
} {1 {database is locked}}
do_test 1.8.5 {
db eval COMMIT
db2 eval COMMIT
} {}
db close
db2 close
do_multiclient_test tn {
#-----------------------------------------------------------------------
# 1. Start an CONCURRENT transaction using [db1].
#
# 2. Start and then rollback a regular transaction using [db2]. This
# can be done as the ongoing [db1] transaction is CONCURRENT.
#
# 3. The [db1] transaction can now be committed, as [db2] has relinquished
# the write lock.
#
do_test 2.$tn.1.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(k INTEGER PRIMARY KEY, v);
INSERT INTO t1 VALUES(1, 'one');
}
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(2, 'two');
}
code1 { sqlite3_get_autocommit db }
} 0
do_test 2.$tn.1.2 {
sql2 {
BEGIN;
INSERT INTO t1 VALUES(3, 'three');
ROLLBACK;
}
} {}
do_test 2.$tn.1.3 {
sql1 COMMIT
sql2 { SELECT * FROM t1 }
} {1 one 2 two}
#-----------------------------------------------------------------------
# 1. Start an CONCURRENT transaction using [db1].
#
# 2. Commit a transaction using [db2].
#
# 3. Try to commit with [db1]. Check that SQLITE_BUSY_SNAPSHOT is returned,
# and the transaction is not rolled back.
#
do_test 2.$tn.2.1 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(-1, 'hello world');
}
} {}
do_test 2.$tn.2.2 {
sql2 {
INSERT INTO t1 VALUES(3, 'three');
}
} {}
do_test 2.$tn.2.3.1 {
set rc [catch { sql1 COMMIT } msg]
list $rc $msg
} {1 {database is locked}}
do_test 2.$tn.2.3.2 {
code1 { list [sqlite3_extended_errcode db] [sqlite3_get_autocommit db] }
} {SQLITE_BUSY_SNAPSHOT 0}
do_test 2.$tn.2.3.3 {
sql1 {
SELECT * FROM t1;
ROLLBACK;
}
} {-1 {hello world} 1 one 2 two}
#-----------------------------------------------------------------------
# 1. Start an CONCURRENT transaction using [db1].
#
# 2. Open a transaction using [db2].
#
# 3. Try to commit with [db1]. Check that SQLITE_BUSY is returned,
# and the transaction is not rolled back.
#
# 4. Have [db2] roll its transaction back. Then check that [db1] can
# commit.
#
do_test 2.$tn.3.1 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(4, 'four');
}
} {}
do_test 2.$tn.3.2 {
sql2 {
BEGIN;
INSERT INTO t1 VALUES(-1, 'xyz');
}
} {}
do_test 2.$tn.3.3.1 {
set rc [catch { sql1 COMMIT } msg]
list $rc $msg
} {1 {database is locked}}
do_test 2.$tn.3.3.2 {
code1 { list [sqlite3_extended_errcode db] [sqlite3_get_autocommit db] }
} {SQLITE_BUSY 0}
do_test 2.$tn.3.3.3 {
sql1 { SELECT * FROM t1; }
} {1 one 2 two 3 three 4 four}
do_test 2.$tn.3.4 {
sql2 ROLLBACK
sql1 COMMIT
sql1 { SELECT * FROM t1; }
} {1 one 2 two 3 three 4 four}
#-----------------------------------------------------------------------
# 1. Create a second table - t2.
#
# 2. Write to t1 with [db] and t2 with [db2].
#
# 3. See if it worked.
#
do_test 2.$tn.4.1 {
sql1 { CREATE TABLE t2(a, b) }
} {}
do_test 2.$tn.4.2 {
sql2 {
BEGIN CONCURRENT;
INSERT INTO t2 VALUES('i', 'n');
}
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(5, 'five');
COMMIT;
}
sql2 COMMIT
} {}
do_test 2.$tn.4.3.1 {
sql2 {SELECT * FROM t1}
} {1 one 2 two 3 three 4 four 5 five}
do_test 2.$tn.4.3.2 {
sql1 {SELECT * FROM t1}
} {1 one 2 two 3 three 4 four 5 five}
do_test 2.$tn.4.3.3 { sql2 {SELECT * FROM t2} } {i n}
do_test 2.$tn.4.3.4 { sql1 {SELECT * FROM t2} } {i n}
#-----------------------------------------------------------------------
# The "schema cookie" issue.
#
# 1. Begin and CONCURRENT write to "t1" using [db]
#
# 2. Create an index on t1 using [db2].
#
# 3. Attempt to commit the CONCURRENT write. This is an SQLITE_BUSY_SNAPSHOT,
# even though there is no page collision.
#
do_test 2.$tn.5.1 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(6, 'six');
}
} {}
do_test 2.$tn.5.2 {
sql2 { CREATE INDEX i1 ON t1(v); }
} {}
do_test 2.$tn.5.3 {
list [catch { sql1 { COMMIT } } msg] $msg [sqlite3_errcode db]
} {1 {database is locked} SQLITE_BUSY_SNAPSHOT}
do_test 2.$tn.5.4 {
sql2 { PRAGMA integrity_check }
} {ok}
catch { sql1 ROLLBACK }
#-----------------------------------------------------------------------
#
# 1. Begin an CONCURRENT write to "t1" using [db]
#
# 2. Lots of inserts into t2. Enough to grow the db file and modify page 1.
#
# 3. Check that the CONCURRENT transaction can not be committed.
#
do_test 2.$tn.6.1 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(6, 'six');
}
} {}
do_test 2.$tn.6.2 {
sql2 {
WITH src(a,b) AS (
VALUES(1,1) UNION ALL SELECT a+1,b+1 FROM src WHERE a<10000
) INSERT INTO t2 SELECT * FROM src;
}
} {}
do_test 2.$tn.6.3 {
sql1 { SELECT count(*) FROM t2 }
list [catch { sql1 { COMMIT } } msg] $msg [sqlite3_errcode db]
} {1 {database is locked} SQLITE_BUSY_SNAPSHOT}
sql1 ROLLBACK
do_test 2.$tn.6.4 {
sql1 {
SELECT count(*) FROM t1;
SELECT count(*) FROM t2;
}
} {5 10001}
#-----------------------------------------------------------------------
#
# 1. Begin an big CONCURRENT write to "t1" using [db] - large enough to
# grow the db file.
#
# 2. Lots of inserts into t2. Also enough to grow the db file.
#
# 3. Check that the CONCURRENT transaction cannot be committed (due to a clash
# on page 1 - the db size field).
#
do_test 2.$tn.7.1 {
sql1 {
BEGIN CONCURRENT;
WITH src(a,b) AS (
VALUES(10000,10000) UNION ALL SELECT a+1,b+1 FROM src WHERE a<20000
) INSERT INTO t1 SELECT * FROM src;
}
} {}
do_test 2.$tn.7.2 {
sql2 {
WITH src(a,b) AS (
VALUES(1,1) UNION ALL SELECT a+1,b+1 FROM src WHERE a<10000
) INSERT INTO t2 SELECT * FROM src;
}
} {}
do_test 2.$tn.7.3 {
list [catch { sql1 { COMMIT } } msg] $msg [sqlite3_errcode db]
} {0 {} SQLITE_OK}
do_test 2.$tn.7.4 { sql3 { PRAGMA integrity_check } } ok
}
#-------------------------------------------------------------------------
# Concurrent transactions may not modify the user_version or application_id.
#
reset_db
do_execsql_test 3.0 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x, y);
INSERT INTO t1 VALUES('a', 'b');
PRAGMA user_version = 10;
} {wal}
do_execsql_test 3.1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES('c', 'd');
SELECT * FROM t1;
} {a b c d}
do_catchsql_test 3.2 {
PRAGMA user_version = 11;
} {1 {cannot modify user_version within CONCURRENT transaction}}
do_execsql_test 3.3 {
PRAGMA user_version;
SELECT * FROM t1;
} {10 a b c d}
do_catchsql_test 3.4 {
PRAGMA application_id = 11;
} {1 {cannot modify application_id within CONCURRENT transaction}}
do_execsql_test 3.5 {
COMMIT;
PRAGMA user_version;
PRAGMA application_id;
SELECT * FROM t1;
} {10 0 a b c d}
#-------------------------------------------------------------------------
# However, another transaction modifying the user_version or application_id
# should not cause a conflict. And committing a concurrent transaction does not
# clobber the modification - even if the concurrent transaction allocates or
# frees database pages.
#
do_multiclient_test tn {
do_test 4.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE ttt(y UNIQUE, z UNIQUE);
PRAGMA user_version = 14;
BEGIN CONCURRENT;
INSERT INTO ttt VALUES('y', 'z');
}
} {wal}
do_test 4.$tn.2 {
sql2 { PRAGMA user_version = 16 }
sql1 COMMIT
sql1 { PRAGMA user_version }
} {16}
do_test 4.$tn.3 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO ttt VALUES(randomblob(10000), randomblob(4));
PRAGMA user_version;
}
} {16}
do_test 4.$tn.4 {
sql2 { PRAGMA user_version = 1234 }
sql1 {
PRAGMA user_version;
COMMIT;
PRAGMA user_version;
PRAGMA integrity_check;
}
} {16 1234 ok}
do_test 4.$tn.5 {
sql1 {
BEGIN CONCURRENT;
DELETE FROM ttt;
PRAGMA user_version;
}
} {1234}
do_test 4.$tn.4 {
sql2 { PRAGMA user_version = 5678 }
sql1 {
PRAGMA user_version;
COMMIT;
PRAGMA user_version;
PRAGMA integrity_check;
}
} {1234 5678 ok}
}
do_multiclient_test tn {
do_test 5.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE tt(a INTEGER PRIMARY KEY, b);
CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
INSERT INTO tt VALUES(1, randomblob(400));
BEGIN CONCURRENT;
}
} {wal}
do_test 5.$tn.2 {
sql1 { UPDATE t2 SET b=5 WHERE a=3 }
sql2 { INSERT INTO tt VALUES(2, randomblob(6000)) }
} {}
do_test 5.$tn.3 {
sql1 { COMMIT }
} {}
}
do_multiclient_test tn {
do_test 6.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
INSERT INTO t1 VALUES(1, 'one');
INSERT INTO t2 VALUES(2, 'two');
}
} {wal}
do_test 6.$tn.2 {
sql2 {
BEGIN CONCURRENT;
SELECT * FROM t2;
INSERT INTO t1 VALUES(3, 'three');
}
} {2 two}
do_test 6.$tn.3 {
sql1 {
INSERT INTO t2 VALUES(3, 'three');
}
} {}
do_test 6.$tn.2 {
list [catch { sql2 { COMMIT } } msg] $msg
} {1 {database is locked}}
}
do_multiclient_test tn {
do_test 7.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<100)
INSERT INTO t1 SELECT NULL, randomblob(400) FROM s;
CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<50000)
INSERT INTO t2 SELECT NULL, randomblob(400) FROM s;
CREATE TABLE t3(a INTEGER PRIMARY KEY, b);
WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<100)
INSERT INTO t3 SELECT NULL, randomblob(400) FROM s;
CREATE TABLE t4(a INTEGER PRIMARY KEY, b);
}
set {} {}
} {}
do_test 7.$tn.2 {
sql2 {
BEGIN CONCURRENT;
SELECT * FROM t1;
INSERT INTO t4 VALUES(1, 2);
}
set {} {}
} {}
do_test 7.$tn.3 {
sql3 {
BEGIN CONCURRENT;
SELECT * FROM t3;
INSERT INTO t4 VALUES(1, 2);
}
set {} {}
} {}
do_test 7.$tn.4 {
sql1 {
UPDATE t1 SET b=randomblob(400);
UPDATE t2 SET b=randomblob(400);
UPDATE t3 SET b=randomblob(400);
}
} {}
do_test 7.$tn.5 {
csql2 { COMMIT }
} {1 {database is locked}}
do_test 7.$tn.6 {
csql3 { COMMIT }
} {1 {database is locked}}
csql2 ROLLBACK
csql3 ROLLBACK
# The following test works with $tn==1 (sql2 and sql3 use separate
# processes), but is quite slow. So only run it with $tn==2 (all
# connections in the same process).
#
if {$tn==2} {
do_test 7.$tn.7 {
for {set i 1} {$i < 10000} {incr i} {
sql3 {
PRAGMA wal_checkpoint;
BEGIN CONCURRENT;
SELECT * FROM t3;
INSERT INTO t4 VALUES(1, 2);
}
sql1 {
UPDATE t2 SET b = randomblob(400) WHERE rowid <= $i;
UPDATE t3 SET b = randomblob(400) WHERE rowid = 1;
}
if {[csql3 COMMIT]!={1 {database is locked}}} {
error "Failed at i=$i"
}
csql3 ROLLBACK
}
} {}
}
}
finish_test

626
test/concurrent2.test Normal file
View File

@ -0,0 +1,626 @@
# 2015 July 26
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# Miscellaneous tests for transactions started with BEGIN CONCURRENT.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/wal_common.tcl
set ::testprefix concurrent2
ifcapable !concurrent {
finish_test
return
}
do_multiclient_test tn {
do_test 1.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x);
CREATE TABLE t2(y);
}
} {wal}
do_test 1.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}
# Test that an CONCURRENT transaction that allocates/frees no pages does
# not conflict with a transaction that does allocate pages.
do_test 1.$tn.2 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(4);
}
sql2 {
INSERT INTO t2 VALUES(randomblob(1500));
}
sql1 {
COMMIT;
}
} {}
do_test 1.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}
# But that an CONCURRENT transaction does conflict with a transaction
# that modifies the db schema.
do_test 1.$tn.3 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(5);
}
sql2 {
CREATE TABLE t3(z);
}
list [catch { sql1 COMMIT } msg] $msg
} {1 {database is locked}}
do_test 1.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}
# Test that an CONCURRENT transaction that allocates at least one page
# does not conflict with a transaction that allocates no pages.
do_test 1.$tn.4 {
sql1 {
ROLLBACK;
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(randomblob(1500));
}
sql2 {
INSERT INTO t2 VALUES(8);
}
sql1 {
COMMIT;
}
} {}
do_test 1.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}
}
do_multiclient_test tn {
do_test 2.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x UNIQUE);
CREATE TABLE t2(y UNIQUE);
}
} {wal}
do_test 2.$tn.2 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(randomblob(1500));
}
sql2 {
INSERT INTO t2 VALUES(randomblob(1500));
}
sql1 COMMIT
} {}
do_test 2.$tn.3 { sql3 { PRAGMA integrity_check } } {ok}
do_test 2.$tn.4 {
sql1 {
BEGIN CONCURRENT;
DELETE FROM t1;
}
sql2 {
DELETE FROM t2;
}
sql1 COMMIT
} {}
do_test 2.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}
do_test 2.$tn.6 {
sql1 {
INSERT INTO t1 VALUES(randomblob(1500));
INSERT INTO t1 VALUES(randomblob(1500));
INSERT INTO t2 VALUES(randomblob(1500));
DELETE FROM t1 WHERE rowid=1;
}
sql1 {
BEGIN CONCURRENT;
DELETE FROM t1 WHERE rowid=2;
}
sql2 {
DELETE FROM t2;
}
sql1 COMMIT
} {}
do_test 2.$tn.7 { sql3 { PRAGMA integrity_check } } {ok}
}
#-------------------------------------------------------------------------
# When an CONCURRENT transaction is opened on a database, the nFree and
# iTrunk header fields of the cached version of page 1 are both set
# to 0. This allows an CONCURRENT transaction to use its own private
# free-page-list, which is merged with the main database free-list when
# the transaction is committed.
#
# The following tests check that nFree/iTrunk are correctly restored if
# an CONCURRENT transaction is rolled back, and that savepoint rollbacks
# that occur within CONCURRENT transactions do not incorrectly restore
# these fields to their on-disk values.
#
reset_db
do_execsql_test 3.0 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x, y);
INSERT INTO t1 VALUES(randomblob(1500), randomblob(1500));
DELETE FROM t1;
} {wal}
do_execsql_test 3.1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(1, 2);
ROLLBACK;
}
do_execsql_test 3.2 { PRAGMA integrity_check } {ok}
do_execsql_test 3.3 { PRAGMA freelist_count } {2}
do_execsql_test 3.4.1 {
BEGIN CONCURRENT;
PRAGMA freelist_count;
} {2}
do_execsql_test 3.4.2 {
SAVEPOINT xyz;
INSERT INTO t1 VALUES(randomblob(1500), NULL);
PRAGMA freelist_count;
} {0}
do_execsql_test 3.4.3 {
ROLLBACK TO xyz;
} {}
do_execsql_test 3.4.4 { PRAGMA freelist_count } {0}
do_execsql_test 3.4.5 { COMMIT; PRAGMA freelist_count } {2}
do_execsql_test 3.4.6 { PRAGMA integrity_check } {ok}
do_execsql_test 3.5.1 {
BEGIN CONCURRENT;
UPDATE t1 SET x=randomblob(10) WHERE y=555;
PRAGMA freelist_count;
} {0}
do_execsql_test 3.5.2 {
ROLLBACK;
PRAGMA freelist_count;
} {2}
do_execsql_test 3.5.3 { PRAGMA integrity_check } {ok}
#-------------------------------------------------------------------------
# Test that nothing goes wrong if an CONCURRENT transaction allocates a
# page at the end of the file, frees it within the same transaction, and
# then has to move the same page to avoid a conflict on COMMIT.
#
do_multiclient_test tn {
do_test 4.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x);
CREATE TABLE t2(x);
}
} {wal}
do_test 4.$tn.2 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(randomblob(1500));
INSERT INTO t1 VALUES(randomblob(1500));
DELETE FROM t1 WHERE rowid = 1;
}
sql2 {
INSERT INTO t2 VALUES(randomblob(1500));
INSERT INTO t2 VALUES(randomblob(1500));
INSERT INTO t2 VALUES(randomblob(1500));
INSERT INTO t2 VALUES(randomblob(1500));
DELETE FROM t2 WHERE rowid IN (1, 2);
}
sql1 COMMIT
} {}
}
#-------------------------------------------------------------------------
#
do_multiclient_test tn {
do_test 5.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x);
CREATE TABLE t2(x);
INSERT INTO t1 VALUES(randomblob(1500));
PRAGMA page_count;
}
} {wal 4}
do_test 5.$tn.2 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t2 VALUES(randomblob(1500));
PRAGMA page_count;
}
} {5}
do_test 5.$tn.3 {
sql2 {
DELETE FROM t1;
PRAGMA freelist_count;
PRAGMA page_count;
}
} {1 4}
do_test 5.$tn.4 { sql1 COMMIT } {}
do_test 5.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}
}
#-------------------------------------------------------------------------
#
do_multiclient_test tn {
do_test 6.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x);
INSERT INTO t1 VALUES(randomblob(1500));
PRAGMA wal_checkpoint;
}
} {wal 0 5 5}
do_test 6.$tn.2 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(randomblob(1500));
INSERT INTO t1 VALUES(randomblob(1500));
}
} {}
do_test 6.$tn.3 {
sql2 {
BEGIN;
INSERT INTO t1 VALUES(randomblob(1500));
INSERT INTO t1 VALUES(randomblob(1500));
COMMIT;
}
} {}
do_test 6.$tn.4 {
list [catch { sql1 COMMIT } msg] $msg
} {1 {database is locked}}
do_test 6.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}
do_test 6.$tn.5 { sql3 { SELECT count(*) from t1 } } {3}
}
#-------------------------------------------------------------------------
# Test that if a corrupt wal-index-header is encountered when attempting
# to commit a CONCURRENT transaction, the transaction is not committed
# (or rolled back) and that SQLITE_BUSY_SNAPSHOT is returned to the user.
#
catch { db close }
forcedelete test.db
testvfs tvfs
sqlite3 db test.db -vfs tvfs
do_execsql_test 7.1 {
PRAGMA journal_mode = wal;
BEGIN;
CREATE TABLE t1(a, b, PRIMARY KEY(a));
INSERT INTO t1 VALUES(1, 2);
INSERT INTO t1 VALUES(3, 4);
COMMIT;
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(5, 6);
INSERT INTO t1 VALUES(7, 8);
SELECT * FROM t1;
} {wal 1 2 3 4 5 6 7 8}
# Corrupt the wal-index header
incr_tvfs_hdr test.db 11 1
do_catchsql_test 7.2.1 { COMMIT } {1 {database is locked}}
do_test 7.2.2 { sqlite3_extended_errcode db } SQLITE_BUSY_SNAPSHOT
do_execsql_test 7.3.1 {
SELECT * FROM t1;
ROLLBACK;
} {1 2 3 4 5 6 7 8}
do_execsql_test 7.3.2 {
SELECT * FROM t1;
} {1 2 3 4}
#-------------------------------------------------------------------------
# Test that "PRAGMA integrity_check" works within a concurrent
# transaction. Within a concurrent transaction, "PRAGMA integrity_check"
# is unable to detect unused database pages, but can detect other types
# of corruption.
#
reset_db
do_test 8.1 {
execsql {
PRAGMA journal_mode = wal;
CREATE TABLE kv(k INTEGER PRIMARY KEY, v UNIQUE);
INSERT INTO kv VALUES(NULL, randomblob(750));
INSERT INTO kv SELECT NULL, randomblob(750) FROM kv;
INSERT INTO kv SELECT NULL, randomblob(750) FROM kv;
INSERT INTO kv SELECT NULL, randomblob(750) FROM kv;
INSERT INTO kv SELECT NULL, randomblob(750) FROM kv;
INSERT INTO kv SELECT NULL, randomblob(750) FROM kv;
DELETE FROM kv WHERE rowid%2;
}
set v [db one {PRAGMA freelist_count}]
expr $v==33 || $v==34
} {1}
do_execsql_test 8.2 { PRAGMA integrity_check } ok
do_execsql_test 8.3 {
BEGIN CONCURRENT;
PRAGMA integrity_check;
} {ok}
do_execsql_test 8.4 {
INSERT INTO kv VALUES(1100, 1100);
PRAGMA integrity_check;
} {ok}
do_execsql_test 8.5 {
COMMIT;
PRAGMA integrity_check;
} {ok}
#-------------------------------------------------------------------------
# Test that concurrent transactions do not allow foreign-key constraints
# to be bypassed.
#
do_multiclient_test tn {
do_test 9.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE pp(i INTEGER PRIMARY KEY, j);
CREATE TABLE cc(a, b REFERENCES pp);
WITH seq(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM seq WHERE i<100)
INSERT INTO pp SELECT i, randomblob(1000) FROM seq;
PRAGMA foreign_keys = 1;
}
} {wal}
do_test 9.$tn.2.1 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO cc VALUES(42, 42);
}
} {}
do_test 9.$tn.2.2 {
sql2 { DELETE FROM pp WHERE i=42 }
list [catch { sql1 COMMIT } msg] $msg
} {1 {database is locked}}
do_test 9.$tn.2.3 {
sql1 ROLLBACK
} {}
do_test 9.$tn.3.1 {
sql1 {
PRAGMA foreign_keys = 0;
BEGIN CONCURRENT;
INSERT INTO cc VALUES(43, 43);
}
} {}
do_test 9.$tn.3.2 {
sql2 { DELETE FROM pp WHERE i=43 }
list [catch { sql1 COMMIT } msg] $msg
} {0 {}}
do_test 9.$tn.4.1 {
sql1 {
PRAGMA foreign_keys = on;
BEGIN CONCURRENT;
INSERT INTO cc VALUES(44, 44);
}
} {}
do_test 9.$tn.4.2 {
sql2 { DELETE FROM pp WHERE i=1 }
list [catch { sql1 COMMIT } msg] $msg
} {0 {}}
}
#-------------------------------------------------------------------------
# Test that even if a SELECT statement appears before all writes within
# a CONCURRENT transaction, the pages it reads are still considered when
# considering whether or not the transaction may be committed.
#
do_multiclient_test tn {
do_test 10.$tn.1.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(a);
CREATE TABLE t2(b);
CREATE TABLE t3(c);
INSERT INTO t1 VALUES(1), (2), (3);
INSERT INTO t2 VALUES(1), (2), (3);
INSERT INTO t3 VALUES(1), (2), (3);
}
} {wal}
do_test 10.$tn.1.2 {
sql1 {
BEGIN CONCURRENT;
SELECT * FROM t1;
INSERT INTO t2 VALUES(4);
}
} {1 2 3}
do_test 10.$tn.1.3 {
sql2 { INSERT INTO t1 VALUES(4) }
list [catch {sql1 COMMIT} msg] $msg
} {1 {database is locked}}
sql1 ROLLBACK
# In this case, because the "SELECT * FROM t1" is first stepped before
# the "BEGIN CONCURRENT", the pages it reads are not recorded by the
# pager object. And so the transaction can be committed. Technically
# this behaviour (the effect of an ongoing SELECT on a BEGIN CONCURRENT
# transacation) is undefined.
#
do_test 10.$tn.2.1 {
code1 {
set ::stmt [sqlite3_prepare db "SELECT * FROM t1" -1 dummy]
sqlite3_step $::stmt
}
} {SQLITE_ROW}
do_test 10.$tn.2.2 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t2 VALUES(4);
}
code1 {
set res [list]
lappend res [sqlite3_column_int $::stmt 0]
while {[sqlite3_step $::stmt]=="SQLITE_ROW"} {
lappend res [sqlite3_column_int $::stmt 0]
}
sqlite3_finalize $::stmt
set res
}
} {1 2 3 4}
do_test 10.$tn.2.3 {
sql2 { INSERT INTO t1 VALUES(5) }
sql1 COMMIT
} {}
# More tests surrounding long-lived prepared statements and concurrent
# transactions.
do_test 10.$tn.3.1 {
sql1 {
BEGIN CONCURRENT;
SELECT * FROM t1;
COMMIT;
}
sql1 {
BEGIN CONCURRENT;
INSERT INTO t2 VALUES(5);
}
sql2 {
INSERT INTO t1 VALUES(5);
}
sql1 COMMIT
sql3 {
SELECT * FROM t2;
}
} {1 2 3 4 5}
do_test 10.$tn.3.2 {
sql1 {
BEGIN CONCURRENT;
SELECT * FROM t1;
ROLLBACK;
}
sql1 {
BEGIN CONCURRENT;
INSERT INTO t2 VALUES(6);
}
sql2 {
INSERT INTO t1 VALUES(6);
}
sql1 COMMIT
sql3 { SELECT * FROM t2 }
} {1 2 3 4 5 6}
do_test 10.$tn.3.3 {
sql1 { BEGIN CONCURRENT }
code1 {
set ::stmt [sqlite3_prepare db "SELECT * FROM t1" -1 dummy]
sqlite3_step $::stmt
}
sql1 {
INSERT INTO t2 VALUES(7);
SELECT * FROM t3;
ROLLBACK;
BEGIN CONCURRENT;
}
sql2 { INSERT INTO t3 VALUES(5) }
code1 { sqlite3_finalize $::stmt }
sql1 {
INSERT INTO t2 VALUES(8);
COMMIT;
}
} {}
}
do_multiclient_test tn {
do_test 11.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(a);
}
} {wal}
do_test 11.$tn.2 {
code1 { sqlite3_wal_info db main }
} {0 2}
do_test 11.$tn.3 {
sql1 { INSERT INTO t1 VALUES(1) }
code1 { sqlite3_wal_info db main }
} {2 3}
do_test 11.$tn.4 {
sql2 { INSERT INTO t1 VALUES(2) }
code2 { sqlite3_wal_info db2 main }
} {3 4}
do_test 11.$tn.5 {
sql1 { PRAGMA wal_checkpoint }
sql2 { INSERT INTO t1 VALUES(3) }
code2 { sqlite3_wal_info db2 main }
} {0 1}
}
reset_db
do_execsql_test 12.0 {
PRAGMA journal_mode = wal;
CREATE TABLE tx(a INTEGER PRIMARY KEY, b);
} {wal}
do_test 12.1 {
for {set i 0} {$i < 50} {incr i} {
execsql {
BEGIN CONCURRENT;
INSERT INTO tx(b) VALUES( randomblob( 1200 ) );
COMMIT;
}
}
execsql { PRAGMA page_size }
} {1024}
do_execsql_test 12.2 {
DELETE FROM tx;
}
do_test 12.3 {
for {set i 0} {$i < 50} {incr i} {
execsql {
BEGIN CONCURRENT;
INSERT INTO tx(b) VALUES( randomblob( 1200 ) );
COMMIT;
}
}
execsql { PRAGMA page_size }
} {1024}
do_execsql_test 12.4 {
DELETE FROM tx;
}
do_test 12.5 {
execsql { BEGIN CONCURRENT }
for {set i 0} {$i < 5000} {incr i} {
execsql {
INSERT INTO tx(b) VALUES( randomblob( 1200 ) );
}
}
execsql { COMMIT }
execsql { PRAGMA page_size }
} {1024}
finish_test

234
test/concurrent3.test Normal file
View File

@ -0,0 +1,234 @@
# 2015 July 26
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# Tests for transactions started with BEGIN CONCURRENT. The tests in this
# file focus on testing that deferred page allocation works properly.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
set ::testprefix concurrent3
if {$AUTOVACUUM} { finish_test ; return }
ifcapable !concurrent {
finish_test
return
}
db close
sqlite3_shutdown
test_sqlite3_log xLog
proc xLog {error_code msg} {
# puts "$error_code: $msg"
# Enable the previous for debugging
}
reset_db
proc create_schema {} {
db eval {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x, y);
CREATE TABLE t2(x, y);
CREATE TABLE t3(x, y);
CREATE TABLE t4(x, y);
CREATE INDEX i1 ON t1(y, x);
CREATE INDEX i2 ON t2(y, x);
CREATE INDEX i3 ON t3(y, x);
CREATE INDEX i4 ON t4(y, x);
}
}
proc do_sql_op {iTbl iOp} {
set db "db$iTbl"
switch $iOp {
"i" {
set sql "
WITH cnt(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM cnt WHERE i<10)
INSERT INTO t$iTbl SELECT randomblob(800), randomblob(800) FROM cnt;
"
}
"d" {
set sql "
DELETE FROM t$iTbl WHERE rowid IN (
SELECT rowid FROM t$iTbl ORDER BY 1 ASC LIMIT 10
)
"
}
"D" {
set sql "
DELETE FROM t$iTbl WHERE rowid IN (
SELECT rowid FROM t$iTbl o WHERE (
SELECT count(*) FROM t$iTbl i WHERE i.rowid<o.rowid
) % 2
)
"
}
"I" {
set sql "
INSERT INTO t$iTbl SELECT randomblob(800), randomblob(800) FROM t$iTbl;
"
}
default {
error "bad iOp parameter: $iOp"
}
}
$db eval $sql
}
set DBLIST {db1 db2 db3 db4}
create_schema
foreach {tn oplist} {
1 {1i 2i 3i 4i}
2 {1iii 2iii 3iii 4iii}
3 {1d 2d 3d 4d}
. -----------------------
4 {1i}
5 {1d 2i}
. -----------------------
6 {1iii 2iii 3iii 4iii}
7 {1di 2id 3iii 4ddd}
8 {1iii 2iii 3iii 4iii}
9 {1D 2II}
10 {1I 2D 3I 4D}
11 {1III 3dddddd 4III}
} {
if {[string range $oplist 0 0]=="-"} {
reset_db
create_schema
continue
}
foreach db $DBLIST { sqlite3 $db test.db }
do_test 1.$tn {
foreach db $DBLIST { $db eval "BEGIN CONCURRENT" }
foreach op $oplist {
set iTbl [string range $op 0 0]
foreach char [split [string range $op 1 end] {}] {
do_sql_op $iTbl $char
}
}
foreach db $DBLIST { $db eval "COMMIT" }
db eval {PRAGMA integrity_check}
} {ok}
foreach db $DBLIST {
$db close
}
}
#-------------------------------------------------------------------------
#
proc create_schema2 {} {
db eval {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x INTEGER PRIMARY KEY, y);
CREATE INDEX i1 ON t1(y);
}
}
proc randint {nMax} {
db eval {SELECT abs(random() % $nMax)}
}
proc do_sql_op2 {db iOp} {
switch -- $iOp {
i {
# Insert 1 rows.
set r [randint 1000000000]
set ::rows($r) 1
#puts "insert row $r"
$db eval { INSERT OR IGNORE INTO t1 VALUES($r, randomblob(50)); }
}
d {
# Insert 1 row
set keys [array names ::rows]
set r [randint [llength $keys]]
set rowid [lindex $keys $r]
$db eval { DELETE FROM t1 WHERE x=$rowid }
unset ::rows($rowid)
}
}
}
foreach {tn nRepeat oplist} {
- - ----------------------------
1 100 { 1iiiiiiiiii }
2 100 { 1i 2d }
3 100 { 1d 2i }
4 50 { 1d 2i 3d }
5 500 { 1i 2i 3i 4i }
6 500 { 1i 2d 3d 4d }
} {
if {[string range $oplist 0 0]=="-"} {
array unset rows
reset_db
create_schema2
continue
}
foreach db $DBLIST {
sqlite3 $db test.db
set stats($db,0) 0
set stats($db,1) 0
}
array unset used
do_test 2.$tn {
for {set i 0} {$i < $nRepeat} {incr i} {
foreach db $DBLIST { $db eval "BEGIN CONCURRENT" }
foreach op $oplist {
set iDb [string range $op 0 0]
set used(db$iDb) 1
foreach char [split [string range $op 1 end] {}] {
do_sql_op2 "db$iDb" $char
}
}
foreach db $DBLIST {
set rc [catch { $db eval COMMIT } msg]
if {$rc} { $db eval ROLLBACK }
incr stats($db,$rc)
}
set res [db eval {PRAGMA integrity_check}]
if {$res != "ok"} { puts "after $db $rc: $res" ; after 1000000 }
}
} {}
foreach db $DBLIST {
$db close
}
# foreach k [lsort [array names used]] {
# puts "$k: $stats($k,0) committed, $stats($k,1) rolled back"
# }
}
catch { db close }
sqlite3_shutdown
test_sqlite3_log
finish_test

194
test/concurrent4.test Normal file
View File

@ -0,0 +1,194 @@
# 2017 May 26
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# Miscellaneous tests for transactions started with BEGIN CONCURRENT.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/wal_common.tcl
set ::testprefix concurrent4
ifcapable !concurrent {
finish_test
return
}
do_execsql_test 1.0 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x PRIMARY KEY, y UNIQUE);
WITH s(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100)
INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM s;
DELETE FROM t1 WHERE rowid<2;
} {wal}
do_execsql_test 1.1 {
BEGIN CONCURRENT;
INSERT INTO t1(rowid, x, y) VALUES(1000, randomblob(3000), randomblob(3000));
SAVEPOINT abc;
DELETE FROM t1 WHERE rowid = 1000;
}
do_execsql_test 1.2 { ROLLBACK TO abc }
do_execsql_test 1.3 { COMMIT }
do_execsql_test 1.4 { PRAGMA integrity_check } {ok}
do_multiclient_test tn {
do_test 2.$tn.1 {
sql1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(a, b);
WITH s(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100)
INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM s;
CREATE TABLE t2(a, b);
WITH s(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100)
INSERT INTO t2 SELECT randomblob(400), randomblob(400) FROM s;
}
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(randomblob(3000), randomblob(3000));
}
} {}
do_test 2.$tn.2 {
sql2 {
WITH s(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<10)
INSERT INTO t2 SELECT randomblob(400), randomblob(400) FROM s;
}
sql2 {
DELETE FROM t2 WHERE rowid<10;
}
} {}
do_test 2.$tn.3 {
sql1 {
COMMIT;
PRAGMA integrity_check;
}
} {ok}
do_test 2.$tn.4 {
sql2 {
PRAGMA integrity_check;
}
} {ok}
}
reset_db
do_execsql_test 3.1 {
PRAGMA page_size = 1024;
PRAGMA journal_mode = wal;
CREATE TABLE t2(x);
INSERT INTO t2 VALUES(randomblob(5000));
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
INSERT INTO t1 VALUES(25, randomblob(104));
DELETE FROM t2;
} {wal}
do_execsql_test 3.2 {
BEGIN CONCURRENT;
REPLACE INTO t1 VALUES(25, randomblob(1117));
COMMIT;
} {}
#-------------------------------------------------------------------------
# Test the effect of BEGIN CONCURRENT transactions that consist entirely
# of read-only statements.
#
reset_db
do_execsql_test 4.0 {
PRAGMA page_size = 1024;
PRAGMA journal_mode = wal;
CREATE TABLE t4(a, b);
INSERT INTO t4 VALUES(1, 2);
INSERT INTO t4 VALUES(3, 4);
} {wal}
sqlite3 db2 test.db
do_test 4.1.1 {
db eval {
BEGIN CONCURRENT;
INSERT INTO t4 VALUES(5, 6);
}
db2 eval {
BEGIN CONCURRENT;
SELECT * FROM t4;
ROLLBACK;
}
} {1 2 3 4}
do_test 4.1.2 {
db eval { COMMIT }
db2 eval { SELECT * FROM t4 }
} {1 2 3 4 5 6}
do_test 4.2.1 {
db eval {
BEGIN CONCURRENT;
INSERT INTO t4 VALUES(7, 8);
}
db2 eval {
BEGIN CONCURRENT;
SELECT * FROM t4;
COMMIT;
}
} {1 2 3 4 5 6}
do_test 4.2.2 {
db eval { COMMIT }
db2 eval { SELECT * FROM t4 }
} {1 2 3 4 5 6 7 8}
do_test 4.3 {
db2 eval {
BEGIN CONCURRENT;
SELECT * FROM t4;
}
db eval {
BEGIN CONCURRENT;
INSERT INTO t4 VALUES(9, 10);
COMMIT;
}
db2 eval {
SELECT * FROM t4;
COMMIT;
}
} {1 2 3 4 5 6 7 8}
set sz [file size test.db-wal]
do_test 4.4.1 {
db eval {
BEGIN CONCURRENT;
SELECT * FROM t4;
SELECT * FROM sqlite_master;
}
db eval COMMIT
file size test.db-wal
} $sz
do_test 4.4.2 {
db eval {
BEGIN CONCURRENT;
SELECT * FROM t4;
SELECT * FROM sqlite_master;
ROLLBACK;
}
file size test.db-wal
} $sz
finish_test

278
test/concurrent5.test Normal file
View File

@ -0,0 +1,278 @@
# 2017 May 26
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/wal_common.tcl
set ::testprefix concurrent5
ifcapable !concurrent {
finish_test
return
}
db close
sqlite3_shutdown
test_sqlite3_log [list lappend ::log]
set ::log [list]
sqlite3 db test.db
proc do_test_conflict_msg {tn msg} {
set msg "cannot commit CONCURRENT transaction - [string trim $msg]"
uplevel [list do_test $tn {lindex $::log end} $msg]
}
do_execsql_test 1.0 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x, y);
CREATE TABLE t2(c);
WITH s(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100)
INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM s;
} {wal}
sqlite3 db2 test.db
do_test 1.1.1 {
set ::log [list]
db2 eval {
BEGIN CONCURRENT;
SELECT count(*) FROM t1;
INSERT INTO t2 VALUES(10);
}
db eval {
INSERT INTO t1 VALUES(randomblob(200), randomblob(200));
}
catchsql COMMIT db2
} {1 {database is locked}}
do_test_conflict_msg 1.1.2 {
conflict at page 2 (read-only page; part of db table t1; content=0500000063021100...)
}
do_test 1.2.1 {
set ::log [list]
db2 eval {
ROLLBACK;
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(11, 12);
}
db eval {
INSERT INTO t1 VALUES(12, 11);
}
catchsql COMMIT db2
} {1 {database is locked}}
do_test_conflict_msg 1.2.2 {
conflict at page 105 (read/write page; part of db table t1; content=0D00000002026100...)
}
do_test 1.3.1 {
set ::log [list]
db2 eval {
ROLLBACK;
BEGIN CONCURRENT;
INSERT INTO t2 VALUES('x');
}
db eval {
INSERT INTO t2 VALUES('y');
}
catchsql COMMIT db2
} {1 {database is locked}}
do_test_conflict_msg 1.3.2 {
conflict at page 3 (read/write page; part of db table t2; content=0D0000000103FB00...)
}
do_test 1.4.1 {
set ::log [list]
execsql {
ROLLBACK;
CREATE TABLE t3(a INTEGER PRIMARY KEY, b INTEGER);
CREATE INDEX i3 ON t3(b);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<5000
) INSERT INTO t3 SELECT i, i FROM s;
BEGIN CONCURRENT;
INSERT INTO t3 VALUES(0, 5001);
} db2
execsql { INSERT INTO t3 VALUES(NULL, 5002) } db
catchsql COMMIT db2
} {1 {database is locked}}
do_test_conflict_msg 1.3.2 {
conflict at page 211 (read/write page; part of db index t3.i3; content=0A0310006300D800...)
}
db2 close
reset_db
do_execsql_test 1.5.0 {
PRAGMA auto_vacuum = 0;
PRAGMA journal_mode = wal;
CREATE TABLE t1(a, b);
CREATE INDEX i1 ON t1(a, b);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO t1 SELECT i, randomblob(200) FROM s;
} {wal}
do_test 1.5.1 {
set ::log [list]
execsql {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(100000, '');
} db
sqlite3 db2 test.db
execsql { INSERT INTO t1(rowid, a, b) VALUES(-1, 100001, '') } db2
catchsql COMMIT db
} {1 {database is locked}}
do_test_conflict_msg 1.5.2 {
conflict at page 507 (read/write page; part of db index t1.i1; content=0A00000003025000...)
}
#-------------------------------------------------------------------------
reset_db
sqlite3 db2 test.db
set big1 [string repeat ab 10000]
set big2 "[string repeat ab 9999]xy"
do_execsql_test 1.6.0 {
CREATE TABLE x1(x, y);
INSERT INTO x1 VALUES(1, $big1);
PRAGMA journal_mode = wal;
} {wal}
do_execsql_test -db db2 1.6.1.1 {
BEGIN;
UPDATE x1 SET y=$big2;
} {}
do_execsql_test 1.6.1.2 {
BEGIN CONCURRENT;
UPDATE x1 SET y=$big2;
}
do_execsql_test -db db2 1.6.1.3 COMMIT
do_catchsql_test 1.6.1.4 {
COMMIT;
} {1 {database is locked}}
do_test_conflict_msg 1.6.1.5 {
conflict at page 21 (read/write page; part of db table x1; content=0000000061626162...)
}
catchsql ROLLBACK
do_test 1.6.2.1 {
execsql { BEGIN } db2
set fd [db2 incrblob main x1 y 1]
seek $fd 19998
puts -nonewline $fd 00
close $fd
} {}
do_test 1.6.2.2 {
execsql { BEGIN CONCURRENT } db
set fd [db incrblob main x1 y 1]
seek $fd 19998
puts -nonewline $fd 12
close $fd
} {}
do_execsql_test -db db2 1.6.2.3 COMMIT
do_catchsql_test 1.6.2.4 {
COMMIT;
} {1 {database is locked}}
do_test_conflict_msg 1.6.1.5 {
conflict at page 21 (read/write page; part of db table x1; content=0000000061626162...)
}
catchsql ROLLBACK
#--------------------------------------------------------------------------
reset_db
sqlite3 db2 test.db
set big1 [string repeat ab 10000]
set big2 "[string repeat ab 9999]xy"
do_execsql_test 1.7.0 {
CREATE TABLE ww(a);
CREATE TABLE y1(x, y);
INSERT INTO y1 VALUES(1, $big1);
PRAGMA journal_mode = wal;
} {wal}
do_execsql_test -db db2 1.7.1 {
BEGIN;
UPDATE y1 SET y=$big2;
SELECT * FROM ww;
}
do_execsql_test 1.7.2 {
BEGIN CONCURRENT;
INSERT INTO ww SELECT y FROM y1;
}
do_execsql_test -db db2 1.7.3 COMMIT
do_catchsql_test 1.7.4 {
COMMIT;
} {1 {database is locked}}
#-------------------------------------------------------------------------
reset_db
sqlite3 db2 test.db
set big1 "[string repeat ab 10000]"
set big2 "[string repeat ab 9999]xy"
do_execsql_test 2.0 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT);
CREATE TABLE t2(a INTEGER PRIMARY KEY, b TEXT);
INSERT INTO t1 VALUES(100, $big1);
} {wal}
do_execsql_test -db db2 2.1 {
BEGIN CONCURRENT;
INSERT INTO t2 SELECT * FROM t1;
}
do_execsql_test 2.2 {
UPDATE t1 SET b=$big2
}
do_test 2.3 {
list [catch { db2 eval COMMIT } msg] $msg
} {1 {database is locked}}
do_test_conflict_msg 2.4 {
conflict at page 22 (read-only page; part of db table t1; content=0000000061626162...)
}
db close
db2 close
sqlite3_shutdown
test_sqlite3_log
sqlite3_initialize
finish_test

60
test/concurrent6.test Normal file
View File

@ -0,0 +1,60 @@
# 2017 May 26
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/wal_common.tcl
set ::testprefix concurrent6
ifcapable !concurrent {
finish_test
return
}
sqlite3 db2 test.db
do_execsql_test 1.0 {
PRAGMA page_size = 1024;
PRAGMA journal_mode = wal;
CREATE TABLE t1(x);
CREATE TABLE t2(x);
CREATE TABLE t3(x);
CREATE TABLE t4(x);
INSERT INTO t1 VALUES(zeroblob(1500));
} {wal}
do_execsql_test -db db2 1.1 {
BEGIN CONCURRENT;
INSERT INTO t3 VALUES(zeroblob(4000));
DELETE FROM t1;
}
do_execsql_test 1.2 {
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100)
INSERT INTO t2 SELECT zeroblob(1000) FROM s;
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100)
INSERT INTO t4 SELECT zeroblob(1000) FROM s;
DELETE FROM t4;
}
do_execsql_test -db db2 1.3 {
COMMIT;
}
finish_test

52
test/concurrent7.test Normal file
View File

@ -0,0 +1,52 @@
# 2018 Jan 5
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix concurrent7
sqlite3 db2 test.db
do_execsql_test 1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x);
CREATE TABLE t2(x);
} {wal}
do_execsql_test -db db2 2 {
SELECT * FROM t1;
}
do_execsql_test 3 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(randomblob(1500));
INSERT INTO t1 VALUES(randomblob(1500));
DELETE FROM t1 WHERE rowid = 1;
}
do_execsql_test -db db2 4 {
INSERT INTO t2 VALUES(randomblob(1500));
INSERT INTO t2 VALUES(randomblob(1500));
INSERT INTO t2 VALUES(randomblob(1500));
INSERT INTO t2 VALUES(randomblob(1500));
DELETE FROM t2 WHERE rowid IN (1, 2);
}
do_execsql_test 5 {
COMMIT;
PRAGMA integrity_check;
} {ok}
finish_test

109
test/concurrent8.test Normal file
View File

@ -0,0 +1,109 @@
# 2020 July 17
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix concurrent8
source $testdir/lock_common.tcl
ifcapable !concurrent {
finish_test
return
}
do_multiclient_test tn {
do_test 1.$tn.0 {
sql1 {
CREATE TABLE t1(x, y);
PRAGMA journal_mode = wal;
}
} {wal}
do_test 1.$tn.1 {
sql1 {
BEGIN CONCURRENT;
INSERT INTO t1 VALUES(1, 1);
}
} {}
do_test 1.$tn.2 {
sql2 {
CREATE TABLE t2(a, b);
}
} {}
do_test 1.$tn.3 {
list [catch { sql1 { COMMIT } } msg] $msg
} {1 {database is locked}}
do_test 1.$tn.4 {
code1 { db errorcode }
} {517} ;# SQLITE_BUSY_SNAPSHOT
do_test 1.$tn.5 {
sql1 {
ROLLBACK;
BEGIN CONCURRENT;
CREATE TABLE t3(a, b);
COMMIT;
}
} {}
do_test 1.$tn.6 {
set nPg [sql1 {PRAGMA page_count}]
sql1 "BEGIN CONCURRENT"
for {set i 0} {$i<250} {incr i} {
sql1 "CREATE TABLE z$i (a, b, c)"
}
sql1 "COMMIT"
set nPg2 [sql1 {PRAGMA page_count}]
expr $nPg2>$nPg
} {1}
do_test 1.$tn.7 {
sql2 { PRAGMA integrity_check }
} {ok}
do_test 1.$tn.8 {
sql1 {
BEGIN CONCURRENT;
CREATE TABLE t4(a, b);
}
sql2 {
INSERT INTO t1 VALUES(2, 2);
}
list [catch { sql1 COMMIT } msg] $msg
} {1 {database is locked}}
sql1 ROLLBACK
do_test 1.$tn.9 {
sql1 {
BEGIN CONCURRENT;
CREATE TEMP TABLE t5(a, b);
INSERT INTO t2 VALUES('x', 'x');
}
sql2 {
INSERT INTO t1 VALUES(3, 3);
CREATE TEMP TABLE t1(x, y);
}
sql1 COMMIT
} {}
}
finish_test

120
test/concurrent9.test Normal file
View File

@ -0,0 +1,120 @@
# 2023 January 12
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#*************************************************************************
# This file implements regression tests for SQLite library.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix concurrent9
do_execsql_test 1.0 {
CREATE TABLE t1(x);
INSERT INTO t1 VALUES(1), (2);
CREATE TABLE t2(y);
INSERT INTO t2 VALUES('a'), ('b');
PRAGMA journal_mode = wal;
} {wal}
db close
#-------------------------------------------------------------------------
# Fix a problem that may occur if a BEGIN CONCURRENT transaction is
# started when the wal file is completely empty and committed after
# it has been initialized by some other connection.
#
sqlite3 db test.db
sqlite3 db2 test.db
do_execsql_test -db db 1.1 {
BEGIN CONCURRENT;
INSERT INTO t2 VALUES('c');
}
do_execsql_test -db db2 1.2 {
INSERT INTO t1 VALUES(3);
}
do_execsql_test -db db 1.3 {
COMMIT;
}
do_execsql_test -db db2 1.4 {
SELECT * FROM t1;
SELECT * FROM t2;
} {1 2 3 a b c}
db2 close
#-------------------------------------------------------------------------
reset_db
do_execsql_test 2.1 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
PRAGMA journal_mode = wal;
WITH s(i) AS (
SELECT 1 UNION SELECT i+1 FROM s WHERE i<500
)
INSERT INTO t1(b) SELECT hex(randomblob(200)) FROM s;
PRAGMA page_count;
} {wal 255}
sqlite3 db2 test.db
do_execsql_test -db db2 2.2 {
DELETE FROM t1 WHERE a<100;
PRAGMA freelist_count;
} {49}
do_execsql_test 2.3 {
BEGIN CONCURRENT;
WITH s(i) AS (
SELECT 1 UNION SELECT i+1 FROM s WHERE i<100
)
INSERT INTO t1(b) SELECT hex(randomblob(200)) FROM s;
}
sqlite3_db_status db CACHE_MISS 1
do_execsql_test 2.4.1 {
COMMIT;
}
do_test 2.4.2 {
lindex [sqlite3_db_status db CACHE_MISS 0] 1
} {1}
do_execsql_test -db db2 2.5 {
DELETE FROM t1 WHERE a<200;
PRAGMA freelist_count;
} {50}
do_execsql_test 2.6 {
BEGIN CONCURRENT;
WITH s(i) AS (
SELECT 1 UNION SELECT i+1 FROM s WHERE i<100
)
INSERT INTO t1(b) SELECT hex(randomblob(200)) FROM s;
DELETE FROM t1 WHERE rowid BETWEEN 600 AND 680;
}
sqlite3_db_status db CACHE_MISS 1
do_execsql_test 2.7.1 {
COMMIT;
}
do_test 2.7.2 {
lindex [sqlite3_db_status db CACHE_MISS 0] 1
} {1}
do_execsql_test 2.8 {
PRAGMA integrity_check;
} {ok}
finish_test

View File

@ -141,15 +141,6 @@ do_test 2.0 {
| end c-b92b.txt.db
}]} {}
# This test only works with the legacy RC4 PRNG
if 0 {
prng_seed 0 db
do_catchsql_test 2.1 {
SELECT count(*) FROM sqlite_schema;
WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<1000)
INSERT INTO t1(a) SELECT randomblob(null) FROM c;
} {1 {database disk image is malformed}}
}
reset_db
if {![info exists ::G(perm:presql)]} {

View File

@ -4398,6 +4398,7 @@ 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 ;
@ -4429,6 +4430,7 @@ if {$tcl_platform(byteOrder)=="littleEndian"} {
} {0 {}}
}
}
#-------------------------------------------------------------------------
reset_db

36
test/noop_update.test Normal file
View File

@ -0,0 +1,36 @@
# 2020 September 01
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix noop_update
if {[db eval {PRAGMA noop_update}]==""} {
finish_test
return
}
do_execsql_test 1.0 {
CREATE TABLE t1(x, y);
INSERT INTO t1 VALUES('a', 111);
}
do_execsql_test 1.1 {
UPDATE t1 SET y=222 WHERE x='a';
SELECT * FROM t1;
} {a 222}
do_execsql_test 1.2 {
PRAGMA noop_update = 1;
UPDATE t1 SET y=333 WHERE x='a';
SELECT * FROM t1;
} {a 222}
finish_test

View File

@ -1,103 +0,0 @@
# 2014-08-24
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.
# The focus of this script is testing details of the SQL language parser.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
do_catchsql_test parser1-1.1 {
CREATE TABLE t1(
a TEXT PRIMARY KEY,
b TEXT,
FOREIGN KEY(b COLLATE nocase DESC) REFERENCES t1(a COLLATE binary ASC)
);
} {1 {syntax error after column name "b"}}
# Verify that a legacy schema in the sqlite_master file is allowed to have
# COLLATE, ASC, and DESC keywords on the id list of a FK constraint, and that
# those keywords are silently ignored.
#
sqlite3_db_config db DEFENSIVE 0
do_execsql_test parser1-1.2 {
CREATE TABLE t1(
a TEXT PRIMARY KEY,
b TEXT,
FOREIGN KEY(b) REFERENCES t1(a)
);
INSERT INTO t1 VALUES('abc',NULL),('xyz','abc');
PRAGMA writable_schema=on;
UPDATE sqlite_master SET sql='CREATE TABLE t1(
a TEXT PRIMARY KEY,
b TEXT,
FOREIGN KEY(b COLLATE nocase) REFERENCES t1(a)
)' WHERE name='t1';
SELECT name FROM sqlite_master WHERE sql LIKE '%collate%';
} {t1}
sqlite3 db2 test.db
do_test parser1-1.3 {
sqlite3 db2 test.db
db2 eval {SELECT * FROM t1 ORDER BY 1}
} {abc {} xyz abc}
db2 close
do_execsql_test parser1-1.4 {
UPDATE sqlite_master SET sql='CREATE TABLE t1(
a TEXT PRIMARY KEY,
b TEXT,
FOREIGN KEY(b ASC) REFERENCES t1(a)
)' WHERE name='t1';
SELECT name FROM sqlite_master WHERE sql LIKE '%ASC%';
} {t1}
sqlite3 db2 test.db
do_test parser1-1.5 {
sqlite3 db2 test.db
db2 eval {SELECT * FROM t1 ORDER BY 1}
} {abc {} xyz abc}
db2 close
do_catchsql_test parser1-2.1 {
WITH RECURSIVE
c(x COLLATE binary) AS (VALUES(1) UNION SELECT x+1 FROM c WHERE x<5)
SELECT x FROM c;
} {1 {syntax error after column name "x"}}
do_catchsql_test parser1-2.2 {
WITH RECURSIVE
c(x ASC) AS (VALUES(1) UNION SELECT x+1 FROM c WHERE x<5)
SELECT x FROM c;
} {1 {syntax error after column name "x"}}
# Verify that the comma between multiple table constraints is
# optional.
#
# The missing comma is technically a syntax error. But we have to support
# it because there might be legacy databases that omit the commas in their
# sqlite_master tables.
#
do_execsql_test parser1-3.1 {
CREATE TABLE t300(id INTEGER PRIMARY KEY);
CREATE TABLE t301(
id INTEGER PRIMARY KEY,
c1 INTEGER NOT NULL,
c2 INTEGER NOT NULL,
c3 BOOLEAN NOT NULL DEFAULT 0,
FOREIGN KEY(c1) REFERENCES t300(id) ON DELETE CASCADE ON UPDATE RESTRICT
/* no comma */
FOREIGN KEY(c2) REFERENCES t300(id) ON DELETE CASCADE ON UPDATE RESTRICT
/* no comma */
UNIQUE(c1, c2)
);
PRAGMA foreign_key_list(t301);
} {0 0 t300 c2 id RESTRICT CASCADE NONE 1 0 t300 c1 id RESTRICT CASCADE NONE}
finish_test

1035
test/tt3_core.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -35,43 +35,6 @@ proc cond_incr_sync_count {adj} {
}
}
proc set_tvfs_hdr {file args} {
# Set $nHdr to the number of bytes in the wal-index header:
set nHdr 48
set nInt [expr {$nHdr/4}]
if {[llength $args]>2} {
error {wrong # args: should be "set_tvfs_hdr fileName ?val1? ?val2?"}
}
set blob [tvfs shm $file]
if {$::tcl_platform(byteOrder)=="bigEndian"} {set fmt I} {set fmt i}
if {[llength $args]} {
set ia [lindex $args 0]
set ib $ia
if {[llength $args]==2} {
set ib [lindex $args 1]
}
binary scan $blob a[expr $nHdr*2]a* dummy tail
set blob [binary format ${fmt}${nInt}${fmt}${nInt}a* $ia $ib $tail]
tvfs shm $file $blob
}
binary scan $blob ${fmt}${nInt} ints
return $ints
}
proc incr_tvfs_hdr {file idx incrval} {
set ints [set_tvfs_hdr $file]
set v [lindex $ints $idx]
incr v $incrval
lset ints $idx $v
set_tvfs_hdr $file $ints
}
#-------------------------------------------------------------------------
# Test case wal2-1.*:
#

View File

@ -87,3 +87,43 @@ proc wal_fix_walindex_cksum {hdrvar} {
lset hdr 10 $c1
lset hdr 11 $c2
}
# This command assumes that $file is the name of a database file opened
# in wal mode using a [testvfs] VFS. It returns a list of the 12 32-bit
# integers that make up the wal-index-header for the named file.
#
proc set_tvfs_hdr {file args} {
# Set $nHdr to the number of bytes in the wal-index header:
set nHdr 48
set nInt [expr {$nHdr/4}]
if {[llength $args]>2} {
error {wrong # args: should be "set_tvfs_hdr fileName ?val1? ?val2?"}
}
set blob [tvfs shm $file]
if {$::tcl_platform(byteOrder)=="bigEndian"} {set fmt I} {set fmt i}
if {[llength $args]} {
set ia [lindex $args 0]
set ib $ia
if {[llength $args]==2} {
set ib [lindex $args 1]
}
binary scan $blob a[expr $nHdr*2]a* dummy tail
set blob [binary format ${fmt}${nInt}${fmt}${nInt}a* $ia $ib $tail]
tvfs shm $file $blob
}
binary scan $blob ${fmt}${nInt} ints
return $ints
}
proc incr_tvfs_hdr {file idx incrval} {
set ints [set_tvfs_hdr $file]
set v [lindex $ints $idx]
incr v $incrval
lset ints $idx $v
set_tvfs_hdr $file $ints
}

View File

@ -115,6 +115,12 @@ set pragma_def {
IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
IF: defined(SQLITE_DEBUG)
NAME: noop_update
TYPE: FLAG
ARG: SQLITE_NoopUpdate
IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
IF: defined(SQLITE_ENABLE_NOOP_UPDATE)
NAME: ignore_check_constraints
TYPE: FLAG
ARG: SQLITE_IgnoreChecks