Compare commits

..

128 Commits

Author SHA1 Message Date
dan 5f0c6bc3a5 Fix an error in README-server-edition.html.
FossilOrigin-Name: 754ad35cd26da361e2ed736b0e400497714a0db9b7fd05fd24e7803b6f478263
2018-03-31 18:43:20 +00:00
dan bddcb53614 Update and add further detail to README-server-edition.html.
FossilOrigin-Name: 337a0b67e30f1030fdc59f712e5914f4801b0e9e4ae19a1e82c10b73eb3f4773
2018-03-30 20:42:25 +00:00
dan fd971c8b83 Update this branch with latest trunk changes.
FossilOrigin-Name: df52e89fff54dbb650cd1fb2b7afe0467acea96a0056728ef48e0c3fea40eeb2
2018-03-28 15:41:57 +00:00
drh 840fda482b Minor comment changes.
FossilOrigin-Name: d282f064698782cf7b584138549a6b27befa0b945ae96b52a3ef6f8a13448077
2018-03-28 15:06:39 +00:00
drh 43a9385def Fix a typo in the README file for ICU. No code changes.
FossilOrigin-Name: 79c4383b66fee9d43a75eef30ed0364573fc99e6d3be12267a99773ab8f57a9f
2018-03-27 22:58:45 +00:00
drh 6a9b9527fa The push-down optimization was being too aggressive such that it sometimes
generated incorrect results.  Reinstate the restriction (4) (with
qualifications) that was removed by check-ins
[b5d3dd8cb0b1e4] and [dd568c27b1d765].

FossilOrigin-Name: f08c1731b0b1dddcba190b094a35306a159713d3db939330f73075ff1d72c81e
2018-03-27 15:13:43 +00:00
drh f900327ed8 Provide the ability for the VFS to do a blocking wait on locks if compiled
with SQLITE_ENABLE_SETLK_TIMEOUT.

FossilOrigin-Name: e7dff982be753bc93123db6a0e8fefd56f2a14fd1c9a8699af0b6b75bc88fc15
2018-03-27 13:57:31 +00:00
drh 000705bcce Do not inject OOM errors on SQLITE_FCNTL_LOCK_TIMEOUT calls as an OOM is
not possible in that context.

FossilOrigin-Name: 5474e560eeb0269964b70bcd876bb86b4197c4fb164bc13454fb8204f083062c
2018-03-26 21:05:47 +00:00
drh fd72563d0a Avoid a race condition that might cause a busy_timeout to last longer than
it should.

FossilOrigin-Name: b81960561b47a1b49646f2f8870dd0684dc4ca7c0b9e11076fd713de66b75972
2018-03-26 20:43:05 +00:00
dan b948948542 Fix a typo preventing test script avtrans.test from running in auto-vacuum
mode.

FossilOrigin-Name: c7473bdb1055df06dc6a93a39ba7f56116f6866292784c4938d843531500c368
2018-03-26 17:56:39 +00:00
drh f0119b2e1b Add infrastructure to support for using F_SETLKW with a timeout on system
that support that functionality.  Requires SQLITE_ENABLE_SETLK_TIMEOUT.

FossilOrigin-Name: 2e54a7433ece4eb27e71bda6f2d121d5aa46ddd5a481357d8543d1432aaad689
2018-03-26 17:40:53 +00:00
drh 80262896d6 Refactor some internal object element names used by the busy handler, to
simplify analysis.

FossilOrigin-Name: 6c40c5574f4ae9795a142d01a8f84afd1b72678ea5f6bfca14a8646c4e862605
2018-03-26 16:37:53 +00:00
mistachkin 43ff906d3e Fix a couple issues in the 'session' module tests.
FossilOrigin-Name: ccf734f7d2bf8f99f3c55124b05c1835e4371bc269f27700f40afc26e7c1bc55
2018-03-24 23:16:05 +00:00
mistachkin 2c42c34df5 Fix harmless compiler warning seen with MSVC.
FossilOrigin-Name: e9508ab1f86ac70a3ca5622ce44141bc05186b199bf32fb8739aef36b95aa05c
2018-03-24 20:06:52 +00:00
drh 821b610b63 Add testcase() macros and improve comments in the LEFT JOIN strength reduction
optimization.

FossilOrigin-Name: 5613457714dd74d6da8e387132a0d8e64980ba4a921a9f53773540b02c0ccec6
2018-03-24 18:01:51 +00:00
drh e3eff266bc Yet another fault in the sqlite3ExprImpliesNotNull() routine, causing errors
in the LEFT JOIN strength reduction optimization of check-in
[dd568c27b1d76563].

FossilOrigin-Name: e88cf3d4df64097ebc19aae464b88d0faf9b7d4c30d057042b582d78327e7ad3
2018-03-24 15:47:31 +00:00
dan 07f119e470 Fix a test script problem causing shell1.test to fail with
-DSQLITE_OMIT_VIRTUAL_TABLE builds.

FossilOrigin-Name: 2e06906e0959e1256fc2feb9dc2c3a9d1e9c19c4ba59585fe52df9af174ab443
2018-03-24 15:08:48 +00:00
drh 2c492061ce Bug fix in the LEFT JOIN strength reduction optimization of
check-in [dd568c27b1d76563].  The sqlite3ExprImpliesNotNull() routine was
mistakenly assuming that a CASE expression must always be NULL if contained
any reference to a variable that was NULL.

FossilOrigin-Name: cf171abe954a5f25262161dd69f2e8cecdbf9446c3f6b298201507dbc743567e
2018-03-24 13:24:02 +00:00
drh b873f4e404 Prepend linkage macros to the sqlite3rebaser interfaces.
FossilOrigin-Name: c64e8f377fe4fa321ebd04806cc27b19287e6fc3523b9988c0a70cad9fce1c18
2018-03-24 00:19:18 +00:00
dan cbeee957e5 Fix typos in sqlite3session.h preventing documentation webpages from being
generated. Also mark all new sessions functions (those related to rebasing) as
experimental.

FossilOrigin-Name: de974235a8fefab0d08673c1b1da4e425e1d8bbfc8c35dac30943c05337b2950
2018-03-23 17:36:31 +00:00
dan 3f2cebb6b4 Fix minor problems with passing NULL pointers to memcmp() and memcpy() found by -fsanitize=undefined.
FossilOrigin-Name: 0b06ce6d8e7cff5cd5d467a94522dfef5593d7c26663ce03c2c2b663a2641551
2018-03-23 16:31:34 +00:00
drh 4aafe5957d Fix the -readonly option on the CLI so that it actually works.
FossilOrigin-Name: 0477fb3fd68509547f3bfd588247aac59aa4aa885ed8a21269852eda64e257ad
2018-03-23 16:08:30 +00:00
mistachkin cf847d44ac Add some more tests for the IS TRUE / IS FALSE operators.
FossilOrigin-Name: 9fe5bebefe909288e583226b5dc32e0465f6b2d8eda850d01820e3f1f8091ddd
2018-03-23 14:56:05 +00:00
drh 8ad427f456 Fix typo in a comment used for documentation. No changes to code.
FossilOrigin-Name: 49974d3b30faaad0891a43d5e840f5dd2dc50ad6c4e6c408292de04e9e8a8868
2018-03-23 14:50:51 +00:00
mistachkin 9d107262e4 Enhance .schema in shell to enable matching patterns with literal underscores.
FossilOrigin-Name: 98e3f5247a6012f58642a2fcdc9be062f7afe8f2e154d06007f4935a0fd87aac
2018-03-23 14:24:34 +00:00
drh f53f88aba6 Fix an error in the CLI in the previous ([0249d9aecf69948]) check-in.
Only the pattern to sqlite3_strlike() need to be escaped.

FossilOrigin-Name: 71d5f6e6322dc037a0e9089049e5d154100b2918830826e30f8043f01a287c00
2018-03-23 13:18:15 +00:00
drh cedfecf90a Fix the ANALYZE command so that it will process tables whose names begin
with "sqlite" as long as they do not being with "sqlite_".

FossilOrigin-Name: 0249d9aecf69948d9343feef9291273beef5fc98aa9a8f95b1e35cc2ce8e6cbd
2018-03-23 12:59:10 +00:00
mistachkin ce2052bc39 Fix harmless compiler warnings seen with MSVC.
FossilOrigin-Name: 2f2be1b1811e6b83fc8945da06e57b8b54233ff8a6ebd9d71b22c6bab2e74afb
2018-03-23 00:31:53 +00:00
dan a968e74fdc Add APIs to the sessions module for "rebasing" changesets.
FossilOrigin-Name: 509506c76b7c104961826721013889d6c6b2ed9b563dcd029e0cb5cb5c34693a
2018-03-22 20:35:20 +00:00
dan 8cb83bee0f Remove some unused code from sqlite3rebaser_rebase().
FossilOrigin-Name: 07cc955eab0e993a75be82d58e17ca53c8abbcaf851983d235049599c19e582f
2018-03-22 20:11:14 +00:00
dan f231e18c6b Fix another problem with rebasing updates against multiple remote changes.
FossilOrigin-Name: c8e7b5a061a3f2fbd9072530177b80f917b39ab5d7cd6acb0a221ab33e78a5cd
2018-03-22 19:52:22 +00:00
dan 516c35a72a Fix an RBU problem causing errors when updating tables with default collation
sequences that require quoting (e.g. COLLATE "ICU_root-u-kn-on").

FossilOrigin-Name: eb4f452e354065d610ff57a6a9312ad119b6b0cc467f9dff105f0718bc27ef01
2018-03-22 17:13:44 +00:00
dan fc50a71be1 Fix a test script problem causing rbuvacuum.test to fail when run along with
other tests.

FossilOrigin-Name: 901cb3b6a2c8d0cc33bd34ec1dbeea49c779ae1ac3ed6733dd9826c8e0eb80c8
2018-03-22 17:02:37 +00:00
dan 3fa5463c03 Add tests to ensure that patchsets are handled correctly by the session rebase
APIs.

FossilOrigin-Name: 0e45baaec01947f6cbf47d5d5922a4cabe7d27181c04c0a08259c952fd023947
2018-03-22 14:07:36 +00:00
drh c6c5ab80ee Add the left join strength reduction optimization. Enhance the push-down
optimization so that it works with many LEFT JOINs.

FossilOrigin-Name: dd568c27b1d7656388ea5b4132cc0265aedd7348d265d8e8c7412b00b28a31aa
2018-03-22 12:00:43 +00:00
drh 66651c2b4b Add the --valid-sql option to the optfuzz test program.
FossilOrigin-Name: a8dfeec73b069f2dd7371c2792b36f152945d32120843db5361ff7e04de177bc
2018-03-22 11:28:31 +00:00
dan b880a7b1f0 Remove some unused code from the sessions module.
FossilOrigin-Name: a09518ab63a1ef1b7f56b9f9b16269fd83920469d4c4e179b1f68b35df4e0c4b
2018-03-22 11:15:59 +00:00
drh 00f0375df7 Get the optfuzz program working.
FossilOrigin-Name: 21346bbce9fd161e8a2037834a6e8eb443a901109ddb3a52c1b7a29000ffeac8
2018-03-21 20:21:29 +00:00
dan f3f6a5dec3 Merge latest trunk changes into this branch.
FossilOrigin-Name: d00b71ecf857bc6d903442d91418d5a313c584dcdd8e9c8ff3b5d940dd45bcc5
2018-03-21 20:13:56 +00:00
dan 95ccb6dc8e Fix some documentation and other issues with the code on this branch.
FossilOrigin-Name: a9ec68627a4533ca6aa7cc1b73f864db615a80e86eda9e33660f22d269a1ba1e
2018-03-21 19:46:36 +00:00
drh 30f30133d7 Add the optfuzz.c program for verifying the query planner using a fuzzer.
This is an initial code check-in.

FossilOrigin-Name: 3fb21251b0c9c731513e8fb137867c2710a77d99411c99348d3ac45d3babffd7
2018-03-21 19:25:59 +00:00
dan 24a0c4534a Fix rebasing of UPDATE changes against a set of remote changesets that feature
both OMIT and REPLACE conflict resolution on different fields of the same row.

FossilOrigin-Name: d8bc3fdb6ba165ca8d7cab857ede8e7e6e2fac24ad59580c5e1db1a4942d295c
2018-03-21 17:29:53 +00:00
drh 7fbb101c54 Relax LEFT-JOIN restrictions on the push-down optimization.
FossilOrigin-Name: b5d3dd8cb0b1e42ed0671a12d22af05194ea9522e4f41fd4bb0deff70b8b0757
2018-03-21 01:59:46 +00:00
drh efce69de37 Do a more thorough job of cleaning erasing traces of the strength-reduced
LEFT JOIN.

FossilOrigin-Name: 08833dda3a25965cc509d0244d7cd68bdb2306351ca52862f347e1efe5db4508
2018-03-20 22:52:27 +00:00
drh 2589787c5a If terms of the WHERE clause require that the right table in a LEFT JOIN
not be a null row, then simplify the LEFT JOIN into an ordinary JOIN.

FossilOrigin-Name: 5b7abecc7ab8ccbbb8cb5e0f672e67625c2555ad03442efbf34cb395f5bb71a8
2018-03-20 21:16:15 +00:00
dan bd45374cc8 Add further tests and documentation for the sessions rebase feature.
FossilOrigin-Name: 7475a363ebb272ae23c0796fe7587714a156dc6a3a4a57ed948ed6f69d3c1218
2018-03-20 20:27:03 +00:00
drh f6e6f02308 Fix incorrect testcase labels on two cases in join5.test. No changes to code.
FossilOrigin-Name: 4661ac81c9589b06a07df8b7931fbd0a7f9a4e9ba1448192b70701dc85a29bd2
2018-03-20 19:02:03 +00:00
drh cd0abc24d1 Improvements to the HAVING-to-WHERE optimization. The code uses less space
and less CPU, and there is now ".selecttrace" output.

FossilOrigin-Name: 5ad668d4339397fe66fe085e0527e37a1930917da88d462a8d89a465faa15e28
2018-03-20 18:08:33 +00:00
mistachkin de8113c31e For 'zipfile', detect attempts to cause a duplicate entry via UPDATE. Also, fix handling of 'UPDATE OR REPLACE' statements run on zipfile virtual tables. Win32 portability fixes to the 'fileio' extension. Miscellaneous test fixes.
FossilOrigin-Name: b36caeca91647616ac2ccdb281200c1a8bcd15d1f30dda0c1d89440d54607196
2018-03-20 16:56:12 +00:00
drh 6974cdcec7 Avoid unnecessary write to the sqlite_sequence table when an insert
is done into an autoincrement table with an application-specified rowid
that is less than the maximum.

FossilOrigin-Name: ec7addc87f97bcff3c3694b14a680453b52de3f8c106436f0708a1cc04b90faa
2018-03-20 13:44:10 +00:00
drh f36e4069f3 Add the ability to disable the push-down optimization using the 0x1000 bit
of SQLITE_TESTCTRL_OPTIMIZATIONS.  Also some documentation fixes and an
enhancement to ".eqp full" in the CLI.

FossilOrigin-Name: ae34edb7f4fa7b37a66cdf3a1d851980cb013e9abe72bdeca23e5323051e0646
2018-03-20 13:26:03 +00:00
dan 93c803e960 Fix handling of "UPDATE OR REPLACE" statements run on zipfile virtual tables.
FossilOrigin-Name: 9a5ef341de2bd2fc4657a510f48159f7876f58d340b457b4272a71d389f88da3
2018-03-20 13:26:01 +00:00
drh 9579947c55 Remove a debugging statement accidently left in
check-in [eddc35f3057e59fd]

FossilOrigin-Name: 85a31557ac0d0401c1bf2e23a502dc2ea57d9b1bc23c01de693f5d3fe22d8341
2018-03-20 13:00:33 +00:00
dan d30830e42d Add a test case for the fix on this branch.
FossilOrigin-Name: 7834cf6c28c63f6ca0e29a508c0f6e3ec9c5c48715344c2db99a9866e92ef205
2018-03-20 12:12:06 +00:00
drh 51efe098f4 In the CLI, avoid extra .selecttrace and .wheretrace output when in ".eqp full"
mode.

FossilOrigin-Name: 427bbf318a28e827bf03d43c2912c8ad937dd52f829feaf7fe374093692ed61b
2018-03-20 12:04:38 +00:00
drh 8b3424d4ee Closer reading of the sqlite3_db_config() documentation show that it is
subtly incorrect.  This check-in fixes the problem.  No code changes.

FossilOrigin-Name: 44d90e7f4bc83680e8fbbf1a0423c6f5cc2cc2aed118b7af1c4c3d9e09b05dd7
2018-03-20 11:58:28 +00:00
drh d06b5357a4 Fix a minor formatting issue on the sqlite3_db_config() documentation.
No changes to code.

FossilOrigin-Name: 8a439a6dda390d7486feb837f87a83e16abd283c882e81e637bd1235d2684379
2018-03-20 11:51:36 +00:00
drh 2d277bb5cb Add the ability to disable the push-down optimization using the 0x1000 bit of
SQLITE_TESTCTRL_OPTIMIZATIONS.

FossilOrigin-Name: eddc35f3057e59fdc25a6911abfaa53a10cc43d8030925b65737b2f51f907421
2018-03-20 11:24:30 +00:00
drh cfd74700da Minor improvements to ".selecttrace". No changes to non-debug code.
FossilOrigin-Name: 03e541f606ead1a13e1084db88e168278c82a2ba72b18c89baafeab3141367b4
2018-03-19 22:28:34 +00:00
drh 8c1eba75a3 Enhance the command-line completion extension to return the names of
triggers and views along with the names of tables.

FossilOrigin-Name: 10e32204e1850354ff16fc052145726e903f87185a37f172831dddad1955184e
2018-03-19 19:05:45 +00:00
drh 9d03f63a7c In the compile_options pragma, show the actual value of the
SQLITE_ENABLE_CEROD compile-time option, if it exists.

FossilOrigin-Name: 1ec339fd109b31e1d2b1e73345bd7a00c3d755f2c45c14d050a8203969163d60
2018-03-19 16:09:36 +00:00
drh 926961dc53 Improved ".selecttrace" output formatting. No changes in non-debug code.
FossilOrigin-Name: 30704d2a52d37717d8b34a402c99ea32453e5c1a9840037dba96591f4acc1503
2018-03-19 16:06:11 +00:00
drh 929cce8890 Do not use sqlite3_column_decltype() in the CLI if it is compiled with
SQLITE_OMIT_DECLTYPE.

FossilOrigin-Name: 442e816b5fed80ebeb58c7c0ab9c2ef999bf488519bf5da670e9cec477034540
2018-03-17 16:26:36 +00:00
mistachkin 2cc8f4831e For 'zipfile', detect attempts to cause a duplicate entry via UPDATE.
FossilOrigin-Name: cf78a882799424610209e9ac5e76771db3f5e6704ffa181f9598eb0986d93eb8
2018-03-17 02:13:13 +00:00
mistachkin d42d8ae337 Another Win32 portability fix for the 'zipfile' tests.
FossilOrigin-Name: 9f604418ca21472d84d754f4c3b7f170e5c2bff05e8cee86640e45b43bc53a49
2018-03-17 00:44:29 +00:00
drh 0d060a071e Detect corruption in the form of the sqlite_sequence table pointing to the
wrong type of btree.

FossilOrigin-Name: 525deb7a67fbd64726c89d5cc36a54b090111e31239431efeeb2bda9836828e0
2018-03-16 23:59:36 +00:00
mistachkin 42a0dbfe92 Win32 portability fixes to the 'fileio' extension.
FossilOrigin-Name: 9d2b0f8b84aac862b0572b183e3ba53ea8c0d8742aaa3c3fbe59f6036054fd1a
2018-03-16 23:54:36 +00:00
mistachkin b5a4a7053b Enable more 'zipfile' tests on Win32.
FossilOrigin-Name: 49d2566c8e7d0df37494ee001fc6eae5f47f95e16f6a575f2d8d6777b62e6068
2018-03-16 23:54:26 +00:00
mistachkin 3c6de3266e Make a couple tests less sensitive to timing.
FossilOrigin-Name: bc2af8e0aa28d8d10612ab9464c008a970348b4720bb34d0c546d341e2bc09e0
2018-03-16 23:54:12 +00:00
mistachkin 2d44d556b5 Fix a duplicate test number and cleanup a bit of Makefile whitespace.
FossilOrigin-Name: 56d11c250961790579a7aeee65e83fdb29462ae37f9de93da13fbd890fc0a01d
2018-03-16 23:53:47 +00:00
drh 86c369428f Detect databases whose schema is corrupted using a CREATE TABLE AS statement
and issue an appropriate error message.

FossilOrigin-Name: d75e67654aa9620b9617786553a002f54e8c6dcbbcc58948a06bd98a0916d75a
2018-03-16 20:23:01 +00:00
drh 1e9c47be1e Better error message text when the schema is corrupted by a CREATE TABLE AS
entry.

FossilOrigin-Name: e13993cf833423eec5f94082cee7213b2d97bcf40dddb2683cf5a8ebf50a33e3
2018-03-16 20:15:58 +00:00
mistachkin 8bbddd8049 Fix a parsing issue associated with a corrupt sqlite_master table.
FossilOrigin-Name: 5f779ff6b48ec97efacd49b1715b7e5272694fa839be349c90667812bbffe22b
2018-03-16 19:10:05 +00:00
drh c8abbc11cd Avoid writing the sqlite_sequence table when it has not actually changed.
FossilOrigin-Name: 3e3849a9d1a06673e6c713a42194f5da339fbf6533fa418c38f63d09bc045867
2018-03-16 18:46:30 +00:00
dan f01d3a7ef7 Fix a problem with handling rebasing UPDATE changes for REPLACE conflict
resolution.

FossilOrigin-Name: f7bf71f1d47044e3cbc74018294b8af5ad52c2bb84954e99bbd4e9b8c36fc077
2018-03-16 18:02:47 +00:00
dan 9f2b04533e Fix a problem in test script thread001.test causing a spurious "-1 files were
left open" error when run separately.

FossilOrigin-Name: 1774f1c3baf0bc3d83a53c52702696ff21814fa1de07cc38fd098832e6757b09
2018-03-16 07:48:43 +00:00
dan f1b40e8305 Add simple tests for the sessions module rebase API.
FossilOrigin-Name: cf0d1abb44cf170d747e9c11f49ec03a29f00ab4821c613ca1e05b883a568211
2018-03-15 19:25:40 +00:00
drh b39161647e Fix a typo in a comment used to generate documentation. No code changes.
FossilOrigin-Name: f1784aff4e6ff1c84d3fa0069034290154220fd2fa85c7df342c717ad54f4b91
2018-03-15 17:46:42 +00:00
drh 3571e7bf4c Fix an issue with the fsdir() table-valued function not resetting correctly
after each pass of a join.

FossilOrigin-Name: 7ce4e71c1b7251be9e922b21dfeba1f9aef6a47ce6495ecb7f94fd785ab564f2
2018-03-15 15:09:45 +00:00
dan c0a499eaad Add largely untested APIs for rebasing changesets.
FossilOrigin-Name: 39915b683b3f8d3bf872af1dede96bf2818b488a8638a1d248395023fc4bd0ef
2018-03-14 21:06:58 +00:00
dan a38e6c57bc Add sqlite3_changeset_apply_v2() and apply_v2_strm() to the sessions module.
FossilOrigin-Name: 445bfe977d9f3a891e08ef33237862ed047fe83e134ef3ed8b47ee0f5abd8cd6
2018-03-13 20:31:23 +00:00
dan 332faddd99 Add a Tcl script to run the performance tests described in
README-server-edition.html.

FossilOrigin-Name: 1b3df8ffc551df0e4d8bcb633e1549ba769d8866cfcffea4cb62949ecf5c4c99
2017-08-29 17:52:14 +00:00
dan 7c4805fa5c Ensure that write-locks on pages are dropped at the end of each
write transaction, even if there is still a read transaction open.

FossilOrigin-Name: 2dd36ade9ea948de27b217b83cbf704e071e6ea5dddb8566165a645143b0f846
2017-08-19 15:50:45 +00:00
dan 852841cc14 Add tests to this branch.
FossilOrigin-Name: abb6e076c851c0b10f62c02d0d7e54f24d86c75f01036dddbc0a8a7dfc627a0d
2017-08-18 18:55:22 +00:00
dan 528e4e4bbf Do not search for locks to clear when connecting to a db in multi-process mode
unless it looks like the previous user of the client-id crashed.

FossilOrigin-Name: 66fb9e1cb479f1e764f1606f041bd97fd3bd428093832c000ee36b643377e9e2
2017-08-18 16:04:40 +00:00
dan 335ce3575f Add support for crash recovery in multi-process mode. And add test cases for
the same.

FossilOrigin-Name: a8115f95e80cc90c095fdd0a151da51f4d3ee427defdc34780e977585a68776d
2017-08-17 19:32:02 +00:00
dan 4c454ff90b Update this branch with the latest changes from trunk.
FossilOrigin-Name: 380a7b7a458b212b0463c8e128c289cc70f2fdb9b9a63b91fc7542c120eb9c10
2017-08-16 17:06:42 +00:00
dan 7ba5f0e768 Enhance this branch to support page-level-locking (without MVCC) for
multi-process deployments.

FossilOrigin-Name: 04e0cb571dbed00e269a890a755e252d7e8204d6d2ed5a7cfdb3d78d990a2876
2017-08-16 16:52:14 +00:00
dan e278a9bacd Remove code for PLL support in wal mode from this branch.
FossilOrigin-Name: 8e1b28ed3e83eba0b2113316a40ed1165e0e051220b68863cb70734c95a82c2a
2017-08-14 07:16:18 +00:00
dan b585dbf8db Update this branch with latest trunk changes.
FossilOrigin-Name: bc2498d60fa22b587ad463ec697abe82d1c87abb7fd0d2cb60cc7316cfd7cec7
2017-08-14 06:55:22 +00:00
dan cfcd9d34b9 Update this branch with latest trunk changes.
FossilOrigin-Name: 17bc7ded07fe2f1f47857bba646eb297e53f69fb1bafd68556d60700ce008750
2017-08-07 14:15:18 +00:00
dan 44bbccc05a Fix a bug on this branch involving mmap mode and readonly transactions.
FossilOrigin-Name: 355c594e821bb5ad782faf0e8d512305f4e1d7fd60949887a8270beed67d03e3
2017-08-07 14:06:01 +00:00
dan 0a1c5dc414 Add documentation file ./README-server-edition.html.
FossilOrigin-Name: fbc4f4ad259cfe04b28d698ef025f179b6708ac788939976c401db2caf8f92c1
2017-07-31 19:55:56 +00:00
dan 0ee56139e9 Merge latest trunk changes with this branch.
FossilOrigin-Name: b42c87790e2dfdc4e52c42967257decf4b44ded909403ea4ac47fe41acfd7077
2017-07-29 17:10:37 +00:00
dan af12f8e2c9 Update test program "tserver" to use a native pthreads mutex/condition
variable to efficiently manage wal file checkpoints without the wal file
growing indefinitely.

FossilOrigin-Name: 8299bdb7cbede30c665dda131bdcbd1d260b3ae9bd16d9b414d8c3776b08f1b3
2017-07-29 17:01:06 +00:00
dan aa90aad86b Reduce the number of mallocs required of writers in server mode.
FossilOrigin-Name: 60953997f62208f82b1efb53b8a1b0c6a26370411041457f747917e10d9a0e68
2017-07-28 21:02:13 +00:00
dan b4b69facc0 Update this branch with latest changes from trunk.
FossilOrigin-Name: d0719ad757bdf7cf2d7d7a4f7b0e713c262ffb434b91ddbb12e190e479abd19e
2017-07-24 19:43:58 +00:00
dan a12baadd15 Only open a db in server-mode if there is a directory named "db-journal"
in the file-system and the VFS is an exclusive locking VFS.

FossilOrigin-Name: e77d29f64480179d7aae7d88adab2ba9ff042701e1f4e624a3a7775cb56a51cc
2017-07-24 19:23:20 +00:00
dan 11584e6779 Merge latest trunk changes with this branch.
FossilOrigin-Name: be0df0a65fc781fde6e94c9370b4819c703d3b270c707dd59e587a2a6f36c9f4
2017-07-19 18:54:21 +00:00
dan 6af090cc05 Add simple tests for "PRAGMA freelist_format".
FossilOrigin-Name: 98a36f4cf127ceec423952a3d80d8d22163f128a5e6290cf6144c59029332944
2017-07-14 08:15:16 +00:00
dan e1e878687d Make the freelist format a separate feature from the page-level locking.
Freelist format is now configure using "PRAGMA freelist_format".

FossilOrigin-Name: dcc407972aa213d28f95cbaf4da5ab7d89ddbad2740f41cfb44b2c2ce20a3132
2017-07-13 21:06:51 +00:00
dan e83dba9e4d Add SQL function usleep() to test program tserver.c.
FossilOrigin-Name: 8cbe8f2b2fe0a5a246401718da8db8d487ced232ada73b53bdc9f9608c47d50e
2017-07-11 18:38:26 +00:00
dan 26a8ddbcdb Fix a bug causing readonly mvcc connections to read the wrong cache entry in
some cases.

FossilOrigin-Name: b6157267f9d4ecdffbdf4b1e591b8d6baaaa964c418ec247a24ccb82146f9041
2017-07-11 16:47:26 +00:00
dan 087794b136 Support read-only MVCC transactions in server-mode. Started using "BEGIN
READONLY".

FossilOrigin-Name: 5a043aa8dd0751e644c495a59deea5fe05da905f49c664d978fe477f9240bc37
2017-07-08 20:46:17 +00:00
dan ad9b696471 Merge latest trunk changes with this branch.
FossilOrigin-Name: 216c757f9289af818482253f77b0b2c9b09e8a478caf67d70a4a6da2fabbc642
2017-07-07 16:40:28 +00:00
dan f42e5d8917 Have this branch maintain an in-memory hash-table of old pages for read-only
MVCC clients. There is no way to access it yet.

FossilOrigin-Name: a3a9a2e1899cc963315590ef4666972e9d92986843706a551962ed16661a19b2
2017-07-07 16:12:45 +00:00
dan 411c20ad62 Merge tserver fixes with this branch.
FossilOrigin-Name: 58a0aab8fd5f5dcbec867468b7f4c1b67c3e16bbe3aeea6bd21ae6b04c2b93f1
2017-06-28 20:21:15 +00:00
dan 6b0cfe5343 Fix bugs in test program tserver.c.
FossilOrigin-Name: 093b9108ea3532437683547c82f756df2413013a50a2fddad3d928cf8d74f9eb
2017-06-28 20:12:41 +00:00
dan 6f3283a427 Support clients within a single process only.
FossilOrigin-Name: dfa9a4d53e0aa7f7911b11af57a4d37a1df2d835cf60eb9e76d28b6b39c2ba58
2017-06-27 20:23:07 +00:00
dan bab38cbc9a Ensure that test tool "tserver" finalizes all statements before attempting to
close a database handle.

FossilOrigin-Name: d8568aacf083b63cdcf68d744ddb07a7615d14f169be6c632813a266a1e8a1cf
2017-06-20 19:20:07 +00:00
dan 538ed8e376 Add too/tserver.c - the implementation of a simple multi-threaded server
designed for interactive testing of concurrency between connections used by
different threads of the same process.

FossilOrigin-Name: 05b4fc4340011847c8585bb822d339dd7b8351266a12b26fdc85edce38fc9dd3
2017-06-07 15:55:59 +00:00
dan b4c4601adf Add code to this branch to emit a log message after each cumulative second
that the WRITER lock has been held.

FossilOrigin-Name: a726d98122704e1e702cfa7ae61d680497df6a826d98082161e0823e115d40a5
2017-05-22 08:01:58 +00:00
dan 60dd40a62c Avoid writer starvation by adding a RESERVED state to page locks.
FossilOrigin-Name: 9b7f80246f2b9921483ab23457865e783ee70b93f67bcecc0c16516447a05875
2017-05-15 19:32:58 +00:00
dan 2ac63b9812 Avoid running recovery while there is another read/write client.
FossilOrigin-Name: a38858a24c6edaa05966b7e158e603bcbde8b05c6233e3bb005cfb32bc91ea06
2017-05-13 19:07:10 +00:00
dan bf431367f3 Require exclusive access to the db to wrap the wal file. Have "PRAGMA
wal_checkpoint = restart" block for this.

FossilOrigin-Name: cbf44ed9758d577e1450b53e645b73c9ca1ee29d2354ce6375c234a41a063400
2017-05-12 18:52:27 +00:00
dan 730859568d Fix a problem causing a lock to be held past the end of a transaction. Use a
blocking lock to take the read-lock on page 1 taken by all transactions.

FossilOrigin-Name: 2584df3d42ece69d37f31f3655cd0d4760914bea73d4f4ccb7f2a7aa47f80f49
2017-05-10 16:18:05 +00:00
dan c9bcad12ea Use a blocking call to obtain the wal-mode WRITER lock in some cases.
FossilOrigin-Name: 4464ca1d686b5c457995cc885d4a8704e402ad387aa4cc37de199276b28cc08e
2017-05-10 13:46:29 +00:00
dan e376cac1d1 Fix a problem with wrapping the log file in server mode.
FossilOrigin-Name: 270b7d1eacb57827465946262b31c5c890d7fc5b5618ccd9e220bc2f9546de54
2017-05-09 16:32:19 +00:00
dan b7bcf5c16c Add some support for wal mode to the hack on this branch.
FossilOrigin-Name: b733afc1d0abc09861903ce8e27a8f2462ec871967f5d3dc2847b31bb28b55f3
2017-05-08 20:15:23 +00:00
dan 31349393ba Update this branch with latest trunk changes.
FossilOrigin-Name: ed6bad67f530afa2fba1bce339a9772f281bd108666ca6c23642efcefa2d98e5
2017-05-06 16:04:58 +00:00
dan 8df9974169 Update this branch with latest trunk changes.
FossilOrigin-Name: 83b1e163ef6ac01d252268415f26a2a999670de583491becc091d294075980bc
2017-05-01 20:07:59 +00:00
dan f427296520 Avoid reading the file-size before page 1 is read-locked.
FossilOrigin-Name: f6b025bf632501f51e691a05d309225671fd78bc62c9a43dd916d35ba4cf6d0e
2017-04-29 15:48:32 +00:00
dan 920a129de3 Fix various bugs revealed by running performance tests.
FossilOrigin-Name: a40b42c422a53aa99280eca7ba419a3937699e273efa3377e0fefe52582cbbd4
2017-04-28 17:32:57 +00:00
dan 34d02325be Add a missing releasePage() call to the server-mode free-list management code.
FossilOrigin-Name: a5a085483c66b17727cf4d2a15165621016031ab78990cb5977ccdbd231d13ac
2017-04-28 14:09:55 +00:00
dan aa189cf00f Use a different free-list format for server-mode databases in order to reduce
contention.

FossilOrigin-Name: 778e8a102d8dc7b0fa006c2d90b0a8cd36ebc1e16bd17477f2d536fc0cef4bf3
2017-04-28 10:20:03 +00:00
dan 2dab053e22 Do not write master journal filenames into server-mode journal files. Use
SQLITE_MUTEX_STATIC_APP1 to protect critical sections in server.c.

FossilOrigin-Name: 3144ae40d2eb63dfd5587579a49163ea1add5947d624daa478ada13339495af4
2017-04-27 14:12:43 +00:00
dan 0561247d07 If possible, delete the journal file when a database connection is closed.
FossilOrigin-Name: d5b5326df25b85b1c3926cd693bcde1ca08e6e03b8aea151a82d222fc9c23dd6
2017-04-27 13:05:43 +00:00
dan 1381bbc262 Experimental implementation of pessimistic page-level locking based on
rollback mode.

FossilOrigin-Name: 64ecf7c7e512827e8a5a42f9f3ad92ff57ec868820e3943dbc74d5823f9a889d
2017-04-26 20:45:00 +00:00
80 changed files with 8193 additions and 476 deletions
+1 -1
View File
@@ -2091,7 +2091,7 @@ keywordhash.h: $(TOP)\tool\mkkeywordhash.c mkkeywordhash.exe
# Source files that go into making shell.c
SHELL_SRC = \
$(TOP)\src\shell.c.in \
$(TOP)\ext\misc\appendvfs.c \
$(TOP)\ext\misc\appendvfs.c \
$(TOP)\ext\misc\shathree.c \
$(TOP)\ext\misc\fileio.c \
$(TOP)\ext\misc\completion.c \
+440
View File
@@ -0,0 +1,440 @@
<html>
<center>
<h1> The "server-process-edition" Branch</h1>
</center>
<p>
The "server-process-edition" branch contains two modifications to stock
SQLite that work together to provide concurrent read/write transactions
using pessimistic page-level-locking. The system runs in two modes:
<ul>
<li><p> Single-process mode - where all clients must be within the
same address space, and
<li><p> Multi-process mode - where clients may be distributed between
multiple OS processes.
</ul>
<p> The system is designed to be most efficient when used with
<a href="https://www.sqlite.org/pragma.html#pragma_synchronous">
"PRAGMA synchronous=OFF"</a>, although it does not require this.
<p>
Up to 16 simultaneous read/write transactions controlled by page-level-locking
are possible. Additionally, in single-process mode there may be any number of
read-only transactions started using the "BEGIN READONLY" command. Read-only
transactions do not block read-write transactions, and read-write transactions
do not block read-only transactions. Read-only transactions access a consistent
snapshot of the database - writes committed by other clients after the
transaction has started are never visible to read-only transactions. In
multi-process mode, the "BEGIN READONLY" command is equivalent to a stock
"BEGIN".
<p>
The two features on this branch are:
<ol>
<li><p> An
<a href=#freelist>alternative layout for the database free-page list</a>.
This is intended to reduce contention between writers when allocating
new database pages, either from the free-list or by extending the
database file.
<li><p> The <a href=#servermode>"server-mode" extension</a>, which
provides read/write page-level-locking concurrency and (in
single-process mode) read-only MVCC concurrency mentioned above.
</ol>
<h2 id=freelist> 1.0 Alternative Free-List Format </h2>
<p>
The alternative free-list format is very similar to the current format. It
differs in the following respects:
<ul>
<li><p>The "total number of free pages" field in the db header is not
maintained. It is always set to zero.
<li><p> Instead of pointing to the first free-list trunk page, the free-list
pointer in the db header points to a page known as the "free-list node".
<li><p> The free-list node page contains N pointers to free-lists stored in the
legacy format (i.e. a linked list of trunk pages each containing
pointers to many free leaf pages).
</ul>
<p>
This effectively means that a database has N free-lists instead of just one. To
allocate a free page, a writer only needs to lock one such free-list, and so up
to N transactions that allocate new pages may proceed concurrently.
<p>
Allocating pages from the end of the db file still locks out all other
read/write transactions (because it involves writing to page 1, which every
transaction needs to read). To minimize the frequency with which this occurs,
when a page must be allocated from the end of the database file, the file is
extended by 2048 pages. These are distributed equally between 16 free-lists
(children of the free-list node page). Additionally, the first trunk page in
each free list is never reused. Doing so would require writing to the
free-list node page - effectively an exclusive lock on the entire
page-allocation system.
<p>
The current format used for the free-list can be modified or queried using a
new pragma:
<pre>
PRAGMA [database.]freelist_format;
PRAGMA [database.]freelist_format = 1|2;
</pre>
<p>
At present, the free-list format may only be modified when the free-list is
completely empty. Which, as the implementation ensures that a free-list that
uses the alternative format is never completely emptied, effectively precludes
changing the format from 2 (alternative) to 1 (legacy).
<p>
For databases that use the "alternative" free-list format, the read and write
versions in the database header (byte offsets 18 and 19) are set to 3 for
rollback mode or 4 for wal mode (instead of 1 and 2 respectively).
<h2 id=servermode> 2.0 Page level locking - "Server Mode" </h2>
<p>
A database client automatically enters "server mode" if there exists a
<i>directory</i> named "&lt;database&gt;-journal" in the file system alongside
the database file "&lt;database&gt;" There is currently no provision for
creating this directory, although it could be safely done for a database in
rollback mode using something like:
<pre>
PRAGMA journal_mode = off;
BEGIN EXCLUSIVE;
&lt;create directory&gt;
END;
</pre>
<p> As well as signalling new clients that they should enter server-mode,
creating a directory named "&lt;database&gt;-journal" has the helpful
side-effect of preventing legacy clients from accessing the database file at
all.
<p> If the VFS is one that takes an exclusive lock on the db file (to
guarantee that no other process accesses the db file), then the system
automatically enters single-process mode. Otherwise, multi-process mode.
<p> In both single and multi-process modes, page-level-locking is managed
by allocating a fixed-size array of "locking slots". Each locking slot is
32-bits in size. By default, the array contains 262144 (2^18) slots. Pages are
assigned to locking slots using the formula (pgno % 262144) - so pages 1,
262145, 524289 etc. share a single locking slot.
<p> In single-process mode, the array of locking slots is allocated on
the process heap and access is protected by a mutex. In multi-process mode, it
is created by memory-mapping a file on disk (similar to the *-shm file in
SQLite wal mode) and access is performed using
<a href="https://en.wikipedia.org/wiki/Compare-and-swap">atomic CAS
primitives</a> exclusively.
<p> Each time a read/write transaction is opened, the client assumes a client
id between 0 and 15 for the duration of the transaction. Client ids are unique
at any point in time - concurrently executing transactions must use different
client ids. So there may exist a maximum of 16 concurrent read/write
transactions at any one time.
<p> Read/write transactions in server-mode are similar to regular SQLite
transactions in rollback mode. The most significant differences are that:
<ul>
<li> <p>Instead of using journal file &lt;database&gt;-journal, server-mode
clients use &lt;database&gt;-journal/&lt;client-id&gt;-journal. If
there are multiple concurrent transactions, each uses a separate
journal file.
<li> <p>No database-wide lock is taken. Instead, individual read and write
locks are taken on the pages accessed by the transaction.
</ul>
<p> Each locking slot is 32-bits in size. A locking slot may simultaneously
support a single write-lock, up to 16 read-locks from read/write clients, and
(in single process mode) up 1024 read-locks from "BEGIN READONLY" clients.
Locking slot bits are used as follows:
<ul>
<li> <p> The least-significant 16-bits are used for read-locks taken by
read/write clients. To take a read-lock, bit &lt;client-id&gt; of the
locking slot is set.
<li> <p> The next 5 bytes are used for the write-lock. If no write-lock
is held on the slot, then this 5 byte integer is set to 0. Otherwise,
it is set to (<i>C</i> + 1), where <i>C</i> is the &lt;client-id&gt; of
the client holding the write-lock.
<li> <p> The next 10 bits contain the total number of read-locks held by
"BEGIN READONLY" clients on the locking slot. See the section below
for a description of how these are used.
</ul>
<p> Currently, if a client requests a lock that cannot be granted due to
a conflicting lock, SQLITE_BUSY is returned to the caller and either the
entire transaction or statement transaction must be rolled back. See
<a href=#problems>Problems and Issues</a> below for more details.
<h3> 2.1 Single-Process Mode </h3>
<p> Single process mode is simpler than multi-process mode because it does
not have to deal with runtime client failure - it is assumed that if one
client fails mid-transaction the entire process crashes. As a result the
only time hot-journal rollback is required in single-process mode is as
part of startup. The first client to connect to a database in single-process
mode attempts to open and rollback all 16 potential hot journal files.
<p> But, in order to support non-blocking "BEGIN READONLY" transactions, it is
also in some ways more complicated than multi-process mode. "BEGIN READONLY"
support works as follows:
<ul>
<li> <p>In single-process mode, writers never spill the cache mid-transaction.
Data is only written to the database as part of committing a transaction.
<li> <p>As well as writing the contents of overwritten pages out to the journal
file, a writer in single-process mode also accumulates a list of buffers
containing the original data for each page overwritten by the current
transaction in main-memory.
<li> <p>When a transaction is ready to be committed, a writer obtains a
transaction-id. Transaction-ids are assigned to writers using a
monotonically increasing function. The writer then adds all of its "old
data" buffers to a hash table accessible to all database clients.
Associated with each hash table entry is the newly assigned transaction-id.
It then waits (spin-locks) for all "BEGIN READONLY" read-locks to clear on
all pages that will be written out by the transaction. Following this, it
commits the transaction as normal (writes out the dirty pages and zeroes
the journal file header).
<li> <p>Clients executing "BEGIN READONLY" transactions are not assigned
a &lt;client-id&gt;. Instead, they are assigned a transaction-id that is
either (a) that of the oldest transaction-id belonging to a writer that has
not yet finished committing, or (b) if there are currently no writers
committing then the value that will be assigned to the next committer.
<li> <p>When a "BEGIN READONLY" transaction reads a page, it first checks
the aforementioned hash table for a suitable entry. A suitable entry
is one with the right page-number and a transaction-id greater than or
equal to that of the "BEGIN READONLY" transaction (i.e. one that had not
finished committing when the BEGIN READONLY transaction started). If such
an entry can be found, the client uses the associated data instead of
reading from the db file. Or, if no such entry is found, the client:
<ol>
<li> Increments the number of BEGIN READONLY read-locks on the page.
<li> Reads the contents of the page from the database file.
<li> Decrements the number of BEGIN READONLY read-locks on the page.
</ol>
<p> The mutex used to protect access to the array of locking slots and
the shared hash table is relinquished for step 2 above.
<li> <p>After each transaction is commited in single-process mode, the
client searches the hash table for entries that can be discarded. An
entry can be discarded if it has a transaction-id older than any still
in use (either by BEGIN READONLY transactions or committers).
</ul>
<h3> 2.2 Multi-Process Mode </h3>
<p> Multi-process mode differs from single-process mode in two important ways:
<ul>
<li> <p>Individual clients may fail mid-transaction and the system must recover
from this.
<li> <p>Partly as a consequence of the above, there are no convenient
primitives like mutexes or malloc() with which to build complicated data
structures like the hash-table used in single-process mode. As a result,
there is no support for "BEGIN READONLY" transactions in multi-process
mode.
</ul>
<p> Unlike single-process mode clients, which may be assigned a different
client-id for each transaction, clients in multi-process mode are assigned a
client-id when they connect to the database and do not relinquish it until
they disconnect. As such, a database in multi-process server-mode supports
at most 16 concurrent client connections.
<p> As well as the array of locking slots, the shared-memory mapping used
by clients in multi-process mode contains 16 "client slots". When a client
connects, it takes a posix WRITE lock on the client slot that corresponds
to its client id. This lock is not released until the client disconnects.
Additionally, whenever a client starts a transaction, it sets the value
in its client locking slot to 1, and clears it again after the transaction
is concluded.
<p> This assists with handling client failure mid-transaction in two ways:
<ul>
<li><p> If client A cannot obtain a lock due to a conflicting lock held by
client B, it can check whether or not client B has failed by attempting a
WRITE lock on its client locking slot. If successful, then client B must
have failed and client A may:
<ul>
<li> Roll back client B's journal, and
<li> By iterating through the entire locking slot array, release all
locks held by client B when it failed.
</ul>
<li><p> When a client first connects and locks its client locking slot, it
can check whether or not the previous user of the client locking slot failed
mid-transaction (since if it did, the locking slot value will still be
non-zero). If it did, the new owner of the client locking slot can release
any locks and roll back any hot-journal before proceeding.
</ul>
<h3> 2.3 Required VFS Support </h3>
<p> The server-mode extension requires that the VFS support various special
file-control commands. Currently support is limited to the "unix" VFS.
<dl>
<dt> SQLITE_FCNTL_SERVER_MODE
<dd><p> This is used by SQLite to query the VFS as to whether the
connection should use single-process server-mode, multi-process server-mode,
or continue in legacy mode.
<p>SQLite invokes this file-control as part of the procedure for detecting a
hot journal (after it has established that there is a file-system entry named
&lt;database&gt;-journal and that no other process holds a RESERVED lock).
If the &lt;database&gt;-journal directory is present in the file-system and
the current VFS takes an exclusive lock on the database file (i.e. is
"unix-excl"), then this file-control indicates that the connection should use
single-process server-mode. Or, if the directory exists but the VFS does not
take an exclusive lock on the database file, that the connection should use
multi-proces server-mode. Or, if there is no directory of the required name,
that the connection should use legacy mode.
<dt> SQLITE_FCNTL_FILEID
<dd><p> Return a 128-bit value that uniquely identifies an open file on disk
from the VFS. This is used to ensure that all connections to the same
database from within a process use the same shared state, even if they
connect to the db using different file-system paths.
<dt> SQLITE_FCNTL_SHMOPEN
<dd>
<dt> SQLITE_FCNTL_SHMOPEN2
<dd>
<dt> SQLITE_FCNTL_SHMLOCK
<dd>
<dt> SQLITE_FCNTL_SHMCLOSE
<dd>
</dl>
<h2 id=problems> 3.0 Problems and Issues </h2>
<ul>
<li> <p>Writer starvation might be the biggest issue. How can it be
prevented?
<li> <p>Blocking locks of some sort would likely improve things. The issue
here is deadlock detection.
<li> <p>The limit of 16 concurrent clients in multi-process mode could be
raised to 27 (since the locking-slot bits used for BEGIN READONLY
locks in single-process mode can be reassigned to support more
read/write client read-locks).
</ul>
<h2> 4.0 Performance Test </h2>
<p>
The test uses a single table with the following schema:
<pre>
CREATE TABLE t1(a INTEGER PRIMARY KEY, b BLOB(16), c BLOB(16), d BLOB(400));
CREATE INDEX i1 ON t1(b);
CREATE INDEX i2 ON t1(c);
</pre>
<p>
The database initially contains 5,000,000 rows. Values for column "a" are
between 1 and 5,000,000, inclusive. Other columns are populated with randomly
generated blob values, each 16, 16, and 400 bytes in size, respectively.
<p>
Read/write transactions used by the test take the following form. Each such
transaction modifies approximately 25 pages (5 in the main table and 10 in each
index), not accounting for tree rebalancing operations.
<pre>
BEGIN;
REPLACE INTO t1 VALUES(abs(random() % 5000000), randomblob(16), randomblob(16), randomblob(400));
REPLACE INTO t1 VALUES(abs(random() % 5000000), randomblob(16), randomblob(16), randomblob(400));
REPLACE INTO t1 VALUES(abs(random() % 5000000), randomblob(16), randomblob(16), randomblob(400));
REPLACE INTO t1 VALUES(abs(random() % 5000000), randomblob(16), randomblob(16), randomblob(400));
REPLACE INTO t1 VALUES(abs(random() % 5000000), randomblob(16), randomblob(16), randomblob(400));
COMMIT;
</pre>
<p>
Read-only transactions are as follows:
<pre>
BEGIN READONLY;
SELECT * FROM t1 WHERE a&gt;abs((random()%5000000)) LIMIT 10;
SELECT * FROM t1 WHERE a&gt;abs((random()%5000000)) LIMIT 10;
SELECT * FROM t1 WHERE a&gt;abs((random()%5000000)) LIMIT 10;
SELECT * FROM t1 WHERE a&gt;abs((random()%5000000)) LIMIT 10;
SELECT * FROM t1 WHERE a&gt;abs((random()%5000000)) LIMIT 10;
END;
</pre>
<p>
The performance test features one or more clients executing read/write
transactions as fast as possible, and zero or more clients executing read-only
transactions, also as fast as possible. All tests use the "unix-excl" VFS and
all clients execute in a separate thread within the same process. The database
is located on a tmpfs file-system.
<p>
In the table below "rw:" refers to the number of read-write clients, and "ro:"
the number of read-only clients used by the test. The TPS values in brackets
are the number of read-only transactions per second. All other values are
read-write transactions per second.
<p>
The collision rate (percentage of attempted transactions that failed due to a
page-level locking conflict) in all tests was between 1 and 2%. Failed
transactions are not included in the TPS counts below.
<p>
<table border=1 width=90% align=center>
<!--
320 jm=persist, rw:1 139675, 135797
320 jm=wal, rw:1 120995, 118889
begin-concurrent, rw:1 119438, 117580
begin-concurrent, rw:2 166923, 166904
begin-concurrent, rw:3 180432, 172825
begin-concurrent, rw:2,ro:1 150427(347319),152087(351601)
server-mode, rw:1 126592, 126742
server-mode, rw:2 228317, 227155
server-mode, rw:3 309712, 306218
server-mode, rw:2,ro:1 213303(576032),210994(556005)
-->
<tr><th> Configuration <th>TPS per client <th> TPS total
<tr><td> 3.20.0, journal_mode=persist, rw:1, ro:0 <td>6886 <td> 6886
<tr><td> 3.20.0, journal_mode=wal, rw:1, ro:0 <td>5997 <td> 5997
<tr><td> begin-concurrent, rw:1, ro:0<td>5925<td> 5925
<tr><td> begin-concurrent, rw:2, ro:0<td>4172 <td> 8345
<tr><td> begin-concurrent, rw:3, ro:0<td>2943 <td> 8831
<tr><td> begin-concurrent, rw:2, ro:1<td>3781 <td> 7562 (17473)
<tr><td> server-mode, rw:1, ro:0 <td>6333 <td> 6333
<tr><td> server-mode, rw:2, ro:0<td> 5693 <td> 11386
<tr><td> server-mode, rw:3, ro:0<td>5132 <td> 15398
<tr><td> server-mode, rw:2, ro:1<td>5303 <td> 10607 (28300)
</table>
+2 -2
View File
@@ -39,8 +39,8 @@ SQLite. Documentation follows.
To utilise "general" case mapping, the upper() or lower() scalar
functions are invoked with one argument:
upper('ABC') -> 'abc'
lower('abc') -> 'ABC'
upper('abc') -> 'ABC'
lower('ABC') -> 'abc'
To access ICU "language specific" case mapping, upper() or lower()
should be invoked with two arguments. The second argument is the name
+2 -3
View File
@@ -78,7 +78,7 @@ struct completion_cursor {
#define COMPLETION_INDEXES 5
#define COMPLETION_TRIGGERS 6
#define COMPLETION_DATABASES 7
#define COMPLETION_TABLES 8
#define COMPLETION_TABLES 8 /* Also VIEWs and TRIGGERs */
#define COMPLETION_COLUMNS 9
#define COMPLETION_MODULES 10
#define COMPLETION_EOF 11
@@ -250,8 +250,7 @@ static int completionNext(sqlite3_vtab_cursor *cur){
const char *zDb = (const char*)sqlite3_column_text(pS2, 1);
zSql = sqlite3_mprintf(
"%z%s"
"SELECT name FROM \"%w\".sqlite_master"
" WHERE type='table'",
"SELECT name FROM \"%w\".sqlite_master",
zSql, zSep, zDb
);
if( zSql==0 ) return SQLITE_NOMEM;
+102 -5
View File
@@ -93,6 +93,9 @@ SQLITE_EXTENSION_INIT1
# include <direct.h>
# include "test_windirent.h"
# define dirent DIRENT
# ifndef chmod
# define chmod _chmod
# endif
# ifndef stat
# define stat _stat
# endif
@@ -159,6 +162,97 @@ static void ctxErrorMsg(sqlite3_context *ctx, const char *zFmt, ...){
va_end(ap);
}
#if defined(_WIN32)
/*
** This function is designed to convert a Win32 FILETIME structure into the
** number of seconds since the Unix Epoch (1970-01-01 00:00:00 UTC).
*/
static sqlite3_uint64 fileTimeToUnixTime(
LPFILETIME pFileTime
){
SYSTEMTIME epochSystemTime;
ULARGE_INTEGER epochIntervals;
FILETIME epochFileTime;
ULARGE_INTEGER fileIntervals;
memset(&epochSystemTime, 0, sizeof(SYSTEMTIME));
epochSystemTime.wYear = 1970;
epochSystemTime.wMonth = 1;
epochSystemTime.wDay = 1;
SystemTimeToFileTime(&epochSystemTime, &epochFileTime);
epochIntervals.LowPart = epochFileTime.dwLowDateTime;
epochIntervals.HighPart = epochFileTime.dwHighDateTime;
fileIntervals.LowPart = pFileTime->dwLowDateTime;
fileIntervals.HighPart = pFileTime->dwHighDateTime;
return (fileIntervals.QuadPart - epochIntervals.QuadPart) / 10000000;
}
/*
** This function attempts to normalize the time values found in the stat()
** buffer to UTC. This is necessary on Win32, where the runtime library
** appears to return these values as local times.
*/
static void statTimesToUtc(
const char *zPath,
struct stat *pStatBuf
){
HANDLE hFindFile;
WIN32_FIND_DATAW fd;
LPWSTR zUnicodeName;
extern LPWSTR sqlite3_win32_utf8_to_unicode(const char*);
zUnicodeName = sqlite3_win32_utf8_to_unicode(zPath);
if( zUnicodeName ){
memset(&fd, 0, sizeof(WIN32_FIND_DATA));
hFindFile = FindFirstFileW(zUnicodeName, &fd);
if( hFindFile!=NULL ){
pStatBuf->st_ctime = (time_t)fileTimeToUnixTime(&fd.ftCreationTime);
pStatBuf->st_atime = (time_t)fileTimeToUnixTime(&fd.ftLastAccessTime);
pStatBuf->st_mtime = (time_t)fileTimeToUnixTime(&fd.ftLastWriteTime);
FindClose(hFindFile);
}
sqlite3_free(zUnicodeName);
}
}
#endif
/*
** This function is used in place of stat(). On Windows, special handling
** is required in order for the included time to be returned as UTC. On all
** other systems, this function simply calls stat().
*/
static int fileStat(
const char *zPath,
struct stat *pStatBuf
){
#if defined(_WIN32)
int rc = stat(zPath, pStatBuf);
if( rc==0 ) statTimesToUtc(zPath, pStatBuf);
return rc;
#else
return stat(zPath, pStatBuf);
#endif
}
/*
** This function is used in place of lstat(). On Windows, special handling
** is required in order for the included time to be returned as UTC. On all
** other systems, this function simply calls lstat().
*/
static int fileLinkStat(
const char *zPath,
struct stat *pStatBuf
){
#if defined(_WIN32)
int rc = lstat(zPath, pStatBuf);
if( rc==0 ) statTimesToUtc(zPath, pStatBuf);
return rc;
#else
return lstat(zPath, pStatBuf);
#endif
}
/*
** Argument zFile is the name of a file that will be created and/or written
** by SQL function writefile(). This function ensures that the directory
@@ -190,7 +284,7 @@ static int makeDirectory(
if( i==nCopy ) break;
zCopy[i] = '\0';
rc2 = stat(zCopy, &sStat);
rc2 = fileStat(zCopy, &sStat);
if( rc2!=0 ){
if( mkdir(zCopy, mode & 0777) ) rc = SQLITE_ERROR;
}else{
@@ -232,7 +326,7 @@ static int writeFile(
** to do so using chmod(), it is not an error. */
struct stat sStat;
if( errno!=EEXIST
|| 0!=stat(zFile, &sStat)
|| 0!=fileStat(zFile, &sStat)
|| !S_ISDIR(sStat.st_mode)
|| ((sStat.st_mode&0777)!=(mode&0777) && 0!=chmod(zFile, mode&0777))
){
@@ -279,6 +373,9 @@ static int writeFile(
lastWrite.dwLowDateTime = (DWORD)intervals;
lastWrite.dwHighDateTime = intervals >> 32;
zUnicodeName = sqlite3_win32_utf8_to_unicode(zFile);
if( zUnicodeName==0 ){
return 1;
}
hFile = CreateFileW(
zUnicodeName, FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, NULL
@@ -291,7 +388,7 @@ static int writeFile(
}else{
return 1;
}
#elif defined(AT_FDCWD) && 0 /* utimensat() is not univerally available */
#elif defined(AT_FDCWD) && 0 /* utimensat() is not universally available */
/* Recent unix */
struct timespec times[2];
times[0].tv_nsec = times[1].tv_nsec = 0;
@@ -568,7 +665,7 @@ static int fsdirNext(sqlite3_vtab_cursor *cur){
sqlite3_free(pCur->zPath);
pCur->zPath = sqlite3_mprintf("%s/%s", pLvl->zDir, pEntry->d_name);
if( pCur->zPath==0 ) return SQLITE_NOMEM;
if( lstat(pCur->zPath, &pCur->sStat) ){
if( fileLinkStat(pCur->zPath, &pCur->sStat) ){
fsdirSetErrmsg(pCur, "cannot stat file: %s", pCur->zPath);
return SQLITE_ERROR;
}
@@ -702,7 +799,7 @@ static int fsdirFilter(
if( pCur->zPath==0 ){
return SQLITE_NOMEM;
}
if( lstat(pCur->zPath, &pCur->sStat) ){
if( fileLinkStat(pCur->zPath, &pCur->sStat) ){
fsdirSetErrmsg(pCur, "cannot stat file: %s", pCur->zPath);
return SQLITE_ERROR;
}
+36 -13
View File
@@ -1500,6 +1500,19 @@ static u32 zipfileGetTime(sqlite3_value *pVal){
return (u32)sqlite3_value_int64(pVal);
}
/*
** Unless it is NULL, entry pOld is currently part of the pTab->pFirstEntry
** linked list. Remove it from the list and free the object.
*/
static void zipfileRemoveEntryFromList(ZipfileTab *pTab, ZipfileEntry *pOld){
if( pOld ){
ZipfileEntry **pp;
for(pp=&pTab->pFirstEntry; (*pp)!=pOld; pp=&((*pp)->pNext));
*pp = (*pp)->pNext;
zipfileEntryFree(pOld);
}
}
/*
** xUpdate method.
*/
@@ -1524,6 +1537,8 @@ static int zipfileUpdate(
u8 *pFree = 0; /* Free this */
char *zFree = 0; /* Also free this */
ZipfileEntry *pOld = 0;
ZipfileEntry *pOld2 = 0;
int bUpdate = 0; /* True for an update that modifies "name" */
int bIsDir = 0;
u32 iCrc32 = 0;
@@ -1536,6 +1551,12 @@ static int zipfileUpdate(
if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
const char *zDelete = (const char*)sqlite3_value_text(apVal[0]);
int nDelete = (int)strlen(zDelete);
if( nVal>1 ){
const char *zUpdate = (const char*)sqlite3_value_text(apVal[1]);
if( zUpdate && zipfileComparePath(zUpdate, zDelete, nDelete)!=0 ){
bUpdate = 1;
}
}
for(pOld=pTab->pFirstEntry; 1; pOld=pOld->pNext){
if( zipfileComparePath(pOld->cds.zFile, zDelete, nDelete)==0 ){
break;
@@ -1613,8 +1634,9 @@ static int zipfileUpdate(
}
}
/* Check that we're not inserting a duplicate entry */
if( pOld==0 && rc==SQLITE_OK ){
/* Check that we're not inserting a duplicate entry -OR- updating an
** entry with a path, thereby making it into a duplicate. */
if( (pOld==0 || bUpdate) && rc==SQLITE_OK ){
ZipfileEntry *p;
for(p=pTab->pFirstEntry; p; p=p->pNext){
if( zipfileComparePath(p->cds.zFile, zPath, nPath)==0 ){
@@ -1623,7 +1645,7 @@ static int zipfileUpdate(
goto zipfile_update_done;
}
case SQLITE_REPLACE: {
pOld = p;
pOld2 = p;
break;
}
default: {
@@ -1661,18 +1683,17 @@ static int zipfileUpdate(
}
}
if( rc==SQLITE_OK && pOld ){
ZipfileEntry **pp;
if( rc==SQLITE_OK && (pOld || pOld2) ){
ZipfileCsr *pCsr;
for(pCsr=pTab->pCsrList; pCsr; pCsr=pCsr->pCsrNext){
if( pCsr->pCurrent==pOld ){
pCsr->pCurrent = pOld->pNext;
if( pCsr->pCurrent && (pCsr->pCurrent==pOld || pCsr->pCurrent==pOld2) ){
pCsr->pCurrent = pCsr->pCurrent->pNext;
pCsr->bNoop = 1;
}
}
for(pp=&pTab->pFirstEntry; (*pp)!=pOld; pp=&((*pp)->pNext));
*pp = (*pp)->pNext;
zipfileEntryFree(pOld);
zipfileRemoveEntryFromList(pTab, pOld);
zipfileRemoveEntryFromList(pTab, pOld2);
}
zipfile_update_done:
@@ -2040,9 +2061,11 @@ void zipfileStep(sqlite3_context *pCtx, int nVal, sqlite3_value **apVal){
p->body.n += zipfileSerializeLFH(&e, &p->body.a[p->body.n]);
/* Append the data to the body of the new archive */
if( (rc = zipfileBufferGrow(&p->body, nData)) ) goto zipfile_step_out;
memcpy(&p->body.a[p->body.n], aData, nData);
p->body.n += nData;
if( nData>0 ){
if( (rc = zipfileBufferGrow(&p->body, nData)) ) goto zipfile_step_out;
memcpy(&p->body.a[p->body.n], aData, nData);
p->body.n += nData;
}
/* Append the CDS record to the directory of the new archive */
nByte = ZIPFILE_CDS_FIXED_SZ + e.cds.nFile + 9;
+1
View File
@@ -71,6 +71,7 @@ proc step_rbu {target rbu} {
}
proc do_rbu_vacuum_test {tn step} {
forcedelete state.db
uplevel [list do_test $tn.1 {
if {$step==0} { sqlite3rbu_vacuum rbu test.db state.db }
while 1 {
+63
View File
@@ -0,0 +1,63 @@
# 2018 March 22
#
# 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.
#
#***********************************************************************
#
source [file join [file dirname [info script]] rbu_common.tcl]
set ::testprefix rbucollate
ifcapable !icu_collations {
finish_test
return
}
db close
sqlite3_shutdown
sqlite3_config_uri 1
reset_db
# Create a simple RBU database. That expects to write to a table:
#
# CREATE TABLE t1(a PRIMARY KEY, b, c);
#
proc create_rbu1 {filename} {
forcedelete $filename
sqlite3 rbu1 $filename
rbu1 eval {
CREATE TABLE data_t1(a, b, c, rbu_control);
INSERT INTO data_t1 VALUES('a', 'one', 1, 0);
INSERT INTO data_t1 VALUES('b', 'two', 2, 0);
INSERT INTO data_t1 VALUES('c', 'three', 3, 0);
}
rbu1 close
return $filename
}
do_execsql_test 1.0 {
SELECT icu_load_collation('en_US', 'my-collate');
CREATE TABLE t1(a COLLATE "my-collate" PRIMARY KEY, b, c);
} {{}}
do_test 1.2 {
create_rbu1 testrbu.db
sqlite3rbu rbu test.db testrbu.db
rbu dbMain_eval { SELECT icu_load_collation('en_US', 'my-collate') }
rbu dbRbu_eval { SELECT icu_load_collation('en_US', 'my-collate') }
while 1 {
set rc [rbu step]
if {$rc!="SQLITE_OK"} break
}
rbu close
db eval { SELECT * FROM t1 }
} {a one 1 b two 2 c three 3}
#forcedelete testrbu.db
finish_test
+2 -2
View File
@@ -1806,7 +1806,7 @@ static void rbuCreateImposterTable2(sqlite3rbu *p, RbuObjIter *pIter){
int iCid = sqlite3_column_int(pXInfo, 1);
int bDesc = sqlite3_column_int(pXInfo, 3);
const char *zCollate = (const char*)sqlite3_column_text(pXInfo, 4);
zCols = rbuMPrintf(p, "%z%sc%d %s COLLATE %s", zCols, zComma,
zCols = rbuMPrintf(p, "%z%sc%d %s COLLATE %Q", zCols, zComma,
iCid, pIter->azTblType[iCid], zCollate
);
zPk = rbuMPrintf(p, "%z%sc%d%s", zPk, zComma, iCid, bDesc?" DESC":"");
@@ -1867,7 +1867,7 @@ static void rbuCreateImposterTable(sqlite3rbu *p, RbuObjIter *pIter){
** "PRIMARY KEY" to the imposter table column declaration. */
zPk = "PRIMARY KEY ";
}
zSql = rbuMPrintf(p, "%z%s\"%w\" %s %sCOLLATE %s%s",
zSql = rbuMPrintf(p, "%z%s\"%w\" %s %sCOLLATE %Q%s",
zSql, zComma, zCol, pIter->azTblType[iCol], zPk, zColl,
(pIter->abNotNull[iCol] ? " NOT NULL" : "")
);
+4 -2
View File
@@ -81,6 +81,7 @@ static int SQLITE_TCLAPI test_sqlite3rbu_cmd(
{"close_no_error", 2, ""}, /* 9 */
{"temp_size_limit", 3, "LIMIT"}, /* 10 */
{"temp_size", 2, ""}, /* 11 */
{"dbRbu_eval", 3, "SQL"}, /* 12 */
{0,0,0}
};
int iCmd;
@@ -146,8 +147,9 @@ static int SQLITE_TCLAPI test_sqlite3rbu_cmd(
break;
}
case 4: /* dbMain_eval */ {
sqlite3 *db = sqlite3rbu_db(pRbu, 0);
case 12: /* dbRbu_eval */
case 4: /* dbMain_eval */ {
sqlite3 *db = sqlite3rbu_db(pRbu, (iCmd==12));
int rc = sqlite3_exec(db, Tcl_GetString(objv[2]), 0, 0, 0);
if( rc!=SQLITE_OK ){
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3_errmsg(db), -1));
+2
View File
@@ -11,6 +11,8 @@
# This file implements regression tests for the session module.
#
package require Tcl 8.6
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
+1
View File
@@ -169,3 +169,4 @@ proc changeset_to_list {c} {
sqlite3session_foreach elem $c { lappend list $elem }
lsort $list
}
+178
View File
@@ -20,6 +20,8 @@ source $testdir/tester.tcl
ifcapable !session {finish_test; return}
set testprefix sessionfault2
if 1 {
do_execsql_test 1.0.0 {
CREATE TABLE t1(a PRIMARY KEY, b UNIQUE);
INSERT INTO t1 VALUES(1, 1);
@@ -103,5 +105,181 @@ do_faultsim_test 2 -faults oom-p* -prep {
faultsim_integrity_check
}
#-------------------------------------------------------------------------
# OOM when collecting and apply a changeset that uses sqlite_stat1.
#
reset_db
forcedelete test.db2
sqlite3 db2 test.db2
do_common_sql {
CREATE TABLE t1(a PRIMARY KEY, b UNIQUE, c);
CREATE INDEX i1 ON t1(c);
INSERT INTO t1 VALUES(1, 2, 3);
INSERT INTO t1 VALUES(4, 5, 6);
INSERT INTO t1 VALUES(7, 8, 9);
CREATE TABLE t2(a, b, c);
INSERT INTO t2 VALUES(1, 2, 3);
INSERT INTO t2 VALUES(4, 5, 6);
INSERT INTO t2 VALUES(7, 8, 9);
ANALYZE;
}
faultsim_save_and_close
db2 close
do_faultsim_test 1.1 -faults oom-* -prep {
catch {db2 close}
catch {db close}
faultsim_restore_and_reopen
sqlite3 db2 test.db2
} -body {
do_then_apply_sql {
INSERT INTO sqlite_stat1 VALUES('x', 'y', 45);
UPDATE sqlite_stat1 SET stat = 123 WHERE tbl='t1' AND idx='i1';
UPDATE sqlite_stat1 SET stat = 456 WHERE tbl='t2';
}
} -test {
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
faultsim_integrity_check
if {$testrc==0} { compare_db db db2 }
}
#-------------------------------------------------------------------------
# OOM when collecting and using a rebase changeset.
#
reset_db
do_execsql_test 2.0 {
CREATE TABLE t3(a, b, c, PRIMARY KEY(b, c));
CREATE TABLE t4(x PRIMARY KEY, y, z);
INSERT INTO t3 VALUES(1, 2, 3);
INSERT INTO t3 VALUES(4, 2, 5);
INSERT INTO t3 VALUES(7, 2, 9);
INSERT INTO t4 VALUES('a', 'b', 'c');
INSERT INTO t4 VALUES('d', 'e', 'f');
INSERT INTO t4 VALUES('g', 'h', 'i');
}
faultsim_save_and_close
db2 close
proc xConflict {ret args} { return $ret }
do_test 2.1 {
faultsim_restore_and_reopen
set C1 [changeset_from_sql {
INSERT INTO t3 VALUES(10, 11, 12);
UPDATE t4 SET y='j' WHERE x='g';
DELETE FROM t4 WHERE x='a';
}]
faultsim_restore_and_reopen
set C2 [changeset_from_sql {
INSERT INTO t3 VALUES(1000, 11, 12);
DELETE FROM t4 WHERE x='g';
}]
faultsim_restore_and_reopen
sqlite3changeset_apply db $C1 [list xConflict OMIT]
faultsim_save_and_close
} {}
do_faultsim_test 2.2 -faults oom* -prep {
catch {db2 close}
catch {db close}
faultsim_restore_and_reopen
sqlite3 db2 test.db2
} -body {
set rebase [sqlite3changeset_apply_v2 db $::C2 [list xConflict OMIT]]
set {} {}
} -test {
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
}
do_faultsim_test 2.3 -faults oom* -prep {
catch {db2 close}
catch {db close}
faultsim_restore_and_reopen
sqlite3 db2 test.db2
} -body {
set rebase [sqlite3changeset_apply_v2 db $::C2 [list xConflict REPLACE]]
set {} {}
} -test {
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
}
do_faultsim_test 2.4 -faults oom* -prep {
catch {db2 close}
catch {db close}
faultsim_restore_and_reopen
set ::rebase [sqlite3changeset_apply_v2 db $::C2 [list xConflict REPLACE]]
} -body {
sqlite3rebaser_create R
R configure $::rebase
R rebase $::C1
set {} {}
} -test {
catch { R delete }
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
}
do_faultsim_test 2.5 -faults oom* -prep {
catch {db2 close}
catch {db close}
faultsim_restore_and_reopen
set ::rebase [sqlite3changeset_apply_v2 db $::C2 [list xConflict OMIT]]
} -body {
sqlite3rebaser_create R
R configure $::rebase
R rebase $::C1
set {} {}
} -test {
catch { R delete }
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
}
}
reset_db
do_execsql_test 3.0 {
CREATE TABLE t1(x PRIMARY KEY, y, z);
INSERT INTO t1 VALUES(3, 1, 4);
INSERT INTO t1 VALUES(1, 5, 9);
}
faultsim_save_and_close
proc xConflict {ret args} { return $ret }
do_test 3.1 {
faultsim_restore_and_reopen
execsql { BEGIN; UPDATE t1 SET z=11; }
set C1 [changeset_from_sql {
UPDATE t1 SET z=10 WHERE x=1;
}]
execsql { ROLLBACK }
execsql { BEGIN; UPDATE t1 SET z=11; }
set C2 [changeset_from_sql {
UPDATE t1 SET z=55 WHERE x=1;
}]
execsql { ROLLBACK }
set ::rebase1 [sqlite3changeset_apply_v2 db $::C1 [list xConflict OMIT]]
set ::rebase2 [sqlite3changeset_apply_v2 db $::C2 [list xConflict OMIT]]
set {} {}
execsql { SELECT * FROM t1 }
} {3 1 4 1 5 9}
do_faultsim_test 3.2 -faults oom* -prep {
faultsim_restore_and_reopen
} -body {
sqlite3rebaser_create R
R configure $::rebase1
R configure $::rebase2
set {} {}
} -test {
catch { R delete }
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
}
finish_test
+477
View File
@@ -0,0 +1,477 @@
# 2018 March 14
#
# 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 [file join [file dirname [info script]] session_common.tcl]
source $testdir/tester.tcl
ifcapable !session {finish_test; return}
set testprefix sessionrebase
set ::lConflict [list]
proc xConflict {args} {
set res [lindex $::lConflict 0]
set ::lConflict [lrange $::lConflict 1 end]
return $res
}
#-------------------------------------------------------------------------
# The following test cases - 1.* - test that the rebase blobs output by
# sqlite3_changeset_apply_v2 look correct in some simple cases. The blob
# is itself a changeset, containing records determined as follows:
#
# * For each conflict resolved with REPLACE, the rebase blob contains
# a DELETE record. All fields other than the PK fields are undefined.
#
# * For each conflict resolved with OMIT, the rebase blob contains an
# INSERT record. For an INSERT or UPDATE operation, the indirect flag
# is clear and all updated fields are defined. For a DELETE operation,
# the indirect flag is set and all non-PK fields left undefined.
#
proc do_apply_v2_test {tn sql modsql conflict_handler res} {
execsql BEGIN
sqlite3session S db main
S attach *
execsql $sql
set changeset [S changeset]
S delete
execsql ROLLBACK
execsql BEGIN
execsql $modsql
set ::lConflict $conflict_handler
set blob [sqlite3changeset_apply_v2 db $changeset xConflict]
execsql ROLLBACK
uplevel [list do_test $tn [list changeset_to_list $blob] [list {*}$res]]
}
set ::lConflict [list]
proc xConflict {args} {
set res [lindex $::lConflict 0]
set ::lConflict [lrange $::lConflict 1 end]
return $res
}
# Take a copy of database test.db in file test.db2. Execute $sql1
# against test.db and $sql2 against test.db2. Capture a changeset
# for each. Then send the test.db2 changeset to test.db and apply
# it with the conflict handlers in $conflict_handler. Patch the
# test.db changeset and then execute it against test.db2. Test that
# the two databases come out the same.
#
proc do_rebase_test {tn sql1 sql2 conflict_handler {testsql ""} {testres ""}} {
for {set i 1} {$i <= 2} {incr i} {
forcedelete test.db2 test.db2-journal test.db2-wal
forcecopy test.db test.db2
sqlite3 db2 test.db2
db eval BEGIN
sqlite3session S1 db main
S1 attach *
execsql $sql1 db
set c1 [S1 changeset]
S1 delete
if {$i==1} {
sqlite3session S2 db2 main
S2 attach *
execsql $sql2 db2
set c2 [S2 changeset]
S2 delete
} else {
set c2 [list]
foreach sql [split $sql2 ";"] {
if {[string is space $sql]} continue
sqlite3session S2 db2 main
S2 attach *
execsql $sql db2
lappend c2 [S2 changeset]
S2 delete
}
}
set ::lConflict $conflict_handler
set rebase [list]
if {$i==1} {
lappend rebase [sqlite3changeset_apply_v2 db $c2 xConflict]
} else {
foreach c $c2 {
#puts "apply_v2: [changeset_to_list $c]"
lappend rebase [sqlite3changeset_apply_v2 db $c xConflict]
}
#puts "llength: [llength $rebase]"
}
#if {$tn=="2.1.4"} { puts [changeset_to_list $rebase] ; breakpoint }
#puts [changeset_to_list [lindex $rebase 0]] ; breakpoint
#puts [llength $rebase]
sqlite3rebaser_create R
foreach r $rebase {
#puts [changeset_to_list $r]
R configure $r
}
set c1r [R rebase $c1]
R delete
#if {$tn=="2.1.4"} { puts [changeset_to_list $c1r] }
sqlite3changeset_apply_v2 db2 $c1r xConflictAbort
if {[string range $tn end end]!="*"} {
uplevel [list do_test $tn.$i.1 [list compare_db db db2] {}]
}
db2 close
if {$testsql!=""} {
uplevel [list do_execsql_test $tn.$i.2 $testsql $testres]
}
db eval ROLLBACK
}
}
do_execsql_test 1.0 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
INSERT INTO t1 VALUES(1, 'value A');
}
do_apply_v2_test 1.1.1 {
UPDATE t1 SET b = 'value B' WHERE a=1;
} {
UPDATE t1 SET b = 'value C' WHERE a=1;
} {
OMIT
} {
{INSERT t1 0 X. {} {i 1 t {value B}}}
}
do_apply_v2_test 1.1.2 {
UPDATE t1 SET b = 'value B' WHERE a=1;
} {
UPDATE t1 SET b = 'value C' WHERE a=1;
} {
REPLACE
} {
{INSERT t1 1 X. {} {i 1 t {value B}}}
}
do_apply_v2_test 1.2.1 {
INSERT INTO t1 VALUES(2, 'first');
} {
INSERT INTO t1 VALUES(2, 'second');
} {
OMIT
} {
{INSERT t1 0 X. {} {i 2 t first}}
}
do_apply_v2_test 1.2.2 {
INSERT INTO t1 VALUES(2, 'first');
} {
INSERT INTO t1 VALUES(2, 'second');
} {
REPLACE
} {
{INSERT t1 1 X. {} {i 2 t first}}
}
do_apply_v2_test 1.3.1 {
DELETE FROM t1 WHERE a=1;
} {
UPDATE t1 SET b='value D' WHERE a=1;
} {
OMIT
} {
{DELETE t1 0 X. {i 1 t {value A}} {}}
}
do_apply_v2_test 1.3.2 {
DELETE FROM t1 WHERE a=1;
} {
UPDATE t1 SET b='value D' WHERE a=1;
} {
REPLACE
} {
{DELETE t1 1 X. {i 1 t {value A}} {}}
}
#-------------------------------------------------------------------------
# Test cases 2.* - simple tests of rebasing actual changesets.
#
# 2.1.1 - 1u2u1r
# 2.1.2 - 1u2u2r
# 2.1.3 - 1d2d
# 2.1.4 - 1d2u1r
# 2.1.5 - 1d2u2r !!
# 2.1.6 - 1u2d1r
# 2.1.7 - 1u2d2r
#
# 2.1.8 - 1i2i2r
# 2.1.9 - 1i2i1r
#
proc xConflictAbort {args} {
return "ABORT"
}
reset_db
do_execsql_test 2.1.0 {
CREATE TABLE t1 (a INTEGER PRIMARY KEY, b TEXT);
INSERT INTO t1 VALUES(1, 'one');
INSERT INTO t1 VALUES(2, 'two');
INSERT INTO t1 VALUES(3, 'three');
}
do_rebase_test 2.1.1 {
UPDATE t1 SET b = 'two.1' WHERE a=2
} {
UPDATE t1 SET b = 'two.2' WHERE a=2;
} {
OMIT
} { SELECT * FROM t1 } {1 one 2 two.1 3 three}
do_rebase_test 2.1.2 {
UPDATE t1 SET b = 'two.1' WHERE a=2
} {
UPDATE t1 SET b = 'two.2' WHERE a=2;
} {
REPLACE
} { SELECT * FROM t1 } {1 one 2 two.2 3 three}
do_rebase_test 2.1.3 {
DELETE FROM t1 WHERE a=3
} {
DELETE FROM t1 WHERE a=3;
} {
OMIT
} { SELECT * FROM t1 } {1 one 2 two}
do_rebase_test 2.1.4 {
DELETE FROM t1 WHERE a=1
} {
UPDATE t1 SET b='one.2' WHERE a=1
} {
OMIT
} { SELECT * FROM t1 } {2 two 3 three}
#do_rebase_test 2.1.5 {
# DELETE FROM t1 WHERE a=1;
#} {
# UPDATE t1 SET b='one.2' WHERE a=1
#} {
# REPLACE
#} { SELECT * FROM t1 } {2 two 3 three}
do_rebase_test 2.1.6 {
UPDATE t1 SET b='three.1' WHERE a=3
} {
DELETE FROM t1 WHERE a=3;
} {
OMIT
} { SELECT * FROM t1 } {1 one 2 two 3 three.1}
do_rebase_test 2.1.7 {
UPDATE t1 SET b='three.1' WHERE a=3
} {
DELETE FROM t1 WHERE a=3;
} {
REPLACE
} { SELECT * FROM t1 } {1 one 2 two}
do_rebase_test 2.1.8 {
INSERT INTO t1 VALUES(4, 'four.1')
} {
INSERT INTO t1 VALUES(4, 'four.2');
} {
REPLACE
} { SELECT * FROM t1 } {1 one 2 two 3 three 4 four.2}
do_rebase_test 2.1.9 {
INSERT INTO t1 VALUES(4, 'four.1')
} {
INSERT INTO t1 VALUES(4, 'four.2');
} {
OMIT
} { SELECT * FROM t1 } {1 one 2 two 3 three 4 four.1}
do_execsql_test 2.2.0 {
CREATE TABLE t2(x, y, z PRIMARY KEY);
INSERT INTO t2 VALUES('i', 'a', 'A');
INSERT INTO t2 VALUES('ii', 'b', 'B');
INSERT INTO t2 VALUES('iii', 'c', 'C');
CREATE TABLE t3(a INTEGER PRIMARY KEY, b, c);
INSERT INTO t3 VALUES(-1, 'z', 'Z');
INSERT INTO t3 VALUES(-2, 'y', 'Y');
}
do_rebase_test 2.2.1 {
UPDATE t2 SET x=1 WHERE z='A'
} {
UPDATE t2 SET y='one' WHERE z='A';
} {
} { SELECT * FROM t2 WHERE z='A' } { 1 one A }
do_rebase_test 2.2.2 {
UPDATE t2 SET x=1, y='one' WHERE z='B'
} {
UPDATE t2 SET y='two' WHERE z='B';
} {
REPLACE
} { SELECT * FROM t2 WHERE z='B' } { 1 two B }
do_rebase_test 2.2.3 {
UPDATE t2 SET x=1, y='one' WHERE z='B'
} {
UPDATE t2 SET y='two' WHERE z='B';
} {
OMIT
} { SELECT * FROM t2 WHERE z='B' } { 1 one B }
#-------------------------------------------------------------------------
reset_db
do_execsql_test 3.0 {
CREATE TABLE t3(a, b, c, PRIMARY KEY(b, c));
CREATE TABLE abcdefghijkl(x PRIMARY KEY, y, z);
INSERT INTO t3 VALUES(1, 2, 3);
INSERT INTO t3 VALUES(4, 2, 5);
INSERT INTO t3 VALUES(7, 2, 9);
INSERT INTO abcdefghijkl VALUES('a', 'b', 'c');
INSERT INTO abcdefghijkl VALUES('d', 'e', 'f');
INSERT INTO abcdefghijkl VALUES('g', 'h', 'i');
}
breakpoint
# do_rebase_test 3.6.tn {
# UPDATE abcdefghijkl SET z='X', y='X' WHERE x='d';
# } {
# UPDATE abcdefghijkl SET y=1 WHERE x='d';
# UPDATE abcdefghijkl SET z=1 WHERE x='d';
# } [list REPLACE REPLACE REPLACE]
foreach {tn p} {
1 OMIT 2 REPLACE
} {
do_rebase_test 3.1.$tn {
INSERT INTO t3 VALUES(1, 1, 1);
UPDATE abcdefghijkl SET y=2;
} {
INSERT INTO t3 VALUES(4, 1, 1);
DELETE FROM abcdefghijkl;
} [list $p $p $p $p $p $p $p $p]
do_rebase_test 3.2.$tn {
INSERT INTO abcdefghijkl SELECT * FROM t3;
UPDATE t3 SET b=b+1;
} {
INSERT INTO t3 VALUES(3, 3, 3);
INSERT INTO abcdefghijkl SELECT * FROM t3;
} [list $p $p $p $p $p $p $p $p]
do_rebase_test 3.3.$tn {
INSERT INTO abcdefghijkl VALUES(22, 23, 24);
} {
INSERT INTO abcdefghijkl VALUES(22, 25, 26);
UPDATE abcdefghijkl SET y=400 WHERE x=22;
} [list $p $p $p $p $p $p $p $p]
do_rebase_test 3.4.$tn {
INSERT INTO abcdefghijkl VALUES(22, 23, 24);
} {
INSERT INTO abcdefghijkl VALUES(22, 25, 26);
UPDATE abcdefghijkl SET y=400 WHERE x=22;
} [list REPLACE $p]
do_rebase_test 3.5.$tn* {
UPDATE abcdefghijkl SET y='X' WHERE x='d';
} {
DELETE FROM abcdefghijkl WHERE x='d';
INSERT INTO abcdefghijkl VALUES('d', NULL, NULL);
} [list $p $p $p]
do_rebase_test 3.5.$tn {
UPDATE abcdefghijkl SET y='X' WHERE x='d';
} {
DELETE FROM abcdefghijkl WHERE x='d';
INSERT INTO abcdefghijkl VALUES('d', NULL, NULL);
} [list REPLACE $p $p]
do_rebase_test 3.6.$tn {
UPDATE abcdefghijkl SET z='X', y='X' WHERE x='d';
} {
UPDATE abcdefghijkl SET y=1 WHERE x='d';
UPDATE abcdefghijkl SET z=1 WHERE x='d';
} [list REPLACE $p $p]
}
#-------------------------------------------------------------------------
# Check that apply_v2() does not create a rebase buffer for a patchset.
# And that it is not possible to rebase a patchset.
#
do_execsql_test 4.0 {
CREATE TABLE t5(o PRIMARY KEY, p, q);
INSERT INTO t5 VALUES(1, 2, 3);
INSERT INTO t5 VALUES(4, 5, 6);
}
foreach {tn cmd rebasable} {
1 patchset 0
2 changeset 1
} {
proc xConflict {args} { return "OMIT" }
do_test 4.1.$tn {
execsql {
BEGIN;
DELETE FROM t5 WHERE o=4;
}
sqlite3session S db main
S attach *
execsql {
INSERT INTO t5 VALUES(4, 'five', 'six');
}
set P [S $cmd]
S delete
execsql ROLLBACK;
set ::rebase [sqlite3changeset_apply_v2 db $P xConflict]
expr [llength $::rebase]>0
} $rebasable
}
foreach {tn cmd rebasable} {
1 patchset 0
2 changeset 1
} {
do_test 4.2.$tn {
sqlite3session S db main
S attach *
execsql {
INSERT INTO t5 VALUES(5+$tn, 'five', 'six');
}
set P [S $cmd]
S delete
sqlite3rebaser_create R
R configure $::rebase
expr [catch {R rebase $P}]==0
} $rebasable
catch { R delete }
}
finish_test
+588 -66
View File
@@ -232,8 +232,8 @@ struct SessionTable {
** statement.
**
** For a DELETE change, all fields within the record except those associated
** with PRIMARY KEY columns are set to "undefined". The PRIMARY KEY fields
** contain the values identifying the row to delete.
** with PRIMARY KEY columns are omitted. The PRIMARY KEY fields contain the
** values identifying the row to delete.
**
** For an UPDATE change, all fields except those associated with PRIMARY KEY
** columns and columns that are modified by the UPDATE are set to "undefined".
@@ -516,7 +516,7 @@ static int sessionPreupdateHash(
static int sessionSerialLen(u8 *a){
int e = *a;
int n;
if( e==0 ) return 1;
if( e==0 || e==0xFF ) return 1;
if( e==SQLITE_NULL ) return 1;
if( e==SQLITE_INTEGER || e==SQLITE_FLOAT ) return 9;
return sessionVarintGet(&a[1], &n) + 1 + n;
@@ -596,7 +596,7 @@ static int sessionChangeEqual(
int n1 = sessionSerialLen(a1);
int n2 = sessionSerialLen(a2);
if( pTab->abPK[iCol] && (n1!=n2 || memcmp(a1, a2, n1)) ){
if( n1!=n2 || memcmp(a1, a2, n1) ){
return 0;
}
a1 += n1;
@@ -839,7 +839,7 @@ static int sessionPreupdateEqual(
}else{
z = sqlite3_value_blob(pVal);
}
if( memcmp(a, z, n) ) return 0;
if( n>0 && memcmp(a, z, n) ) return 0;
a += n;
}
}
@@ -2183,6 +2183,7 @@ static int sessionSelectStmt(
"SELECT tbl, ?2, stat FROM %Q.sqlite_stat1 WHERE tbl IS ?1 AND "
"idx IS (CASE WHEN ?2=X'' THEN NULL ELSE ?2 END)", zDb
);
if( zSql==0 ) rc = SQLITE_NOMEM;
}else{
int i;
const char *zSep = "";
@@ -2918,7 +2919,8 @@ static int sessionChangesetReadTblhdr(sqlite3_changeset_iter *p){
static int sessionChangesetNext(
sqlite3_changeset_iter *p, /* Changeset iterator */
u8 **paRec, /* If non-NULL, store record pointer here */
int *pnRec /* If non-NULL, store size of record here */
int *pnRec, /* If non-NULL, store size of record here */
int *pbNew /* If non-NULL, true if new table */
){
int i;
u8 op;
@@ -2953,6 +2955,7 @@ static int sessionChangesetNext(
op = p->in.aData[p->in.iNext++];
while( op=='T' || op=='P' ){
if( pbNew ) *pbNew = 1;
p->bPatchset = (op=='P');
if( sessionChangesetReadTblhdr(p) ) return p->rc;
if( (p->rc = sessionInputBuffer(&p->in, 2)) ) return p->rc;
@@ -3031,7 +3034,7 @@ static int sessionChangesetNext(
** callback by changeset_apply().
*/
int sqlite3changeset_next(sqlite3_changeset_iter *p){
return sessionChangesetNext(p, 0, 0);
return sessionChangesetNext(p, 0, 0, 0);
}
/*
@@ -3410,6 +3413,8 @@ struct SessionApplyCtx {
int bStat1; /* True if table is sqlite_stat1 */
int bDeferConstraints; /* True to defer constraints */
SessionBuffer constraints; /* Deferred constraints are stored here */
SessionBuffer rebase; /* Rebase information (if any) here */
int bRebaseStarted; /* If table header is already in rebase */
};
/*
@@ -3676,7 +3681,6 @@ static int sessionStat1Sql(sqlite3 *db, SessionApplyCtx *p){
"AND (?4 OR stat IS ?3)"
);
}
assert( rc==SQLITE_OK );
return rc;
}
@@ -3791,6 +3795,54 @@ static int sessionSeekToRow(
return rc;
}
/*
** This function is called from within sqlite3changset_apply_v2() when
** a conflict is encountered and resolved using conflict resolution
** mode eType (either SQLITE_CHANGESET_OMIT or SQLITE_CHANGESET_REPLACE)..
** It adds a conflict resolution record to the buffer in
** SessionApplyCtx.rebase, which will eventually be returned to the caller
** of apply_v2() as the "rebase" buffer.
**
** Return SQLITE_OK if successful, or an SQLite error code otherwise.
*/
static int sessionRebaseAdd(
SessionApplyCtx *p, /* Apply context */
int eType, /* Conflict resolution (OMIT or REPLACE) */
sqlite3_changeset_iter *pIter /* Iterator pointing at current change */
){
int rc = SQLITE_OK;
int i;
int eOp = pIter->op;
if( p->bRebaseStarted==0 ){
/* Append a table-header to the rebase buffer */
const char *zTab = pIter->zTab;
sessionAppendByte(&p->rebase, 'T', &rc);
sessionAppendVarint(&p->rebase, p->nCol, &rc);
sessionAppendBlob(&p->rebase, p->abPK, p->nCol, &rc);
sessionAppendBlob(&p->rebase, (u8*)zTab, (int)strlen(zTab)+1, &rc);
p->bRebaseStarted = 1;
}
assert( eType==SQLITE_CHANGESET_REPLACE||eType==SQLITE_CHANGESET_OMIT );
assert( eOp==SQLITE_DELETE || eOp==SQLITE_INSERT || eOp==SQLITE_UPDATE );
sessionAppendByte(&p->rebase,
(eOp==SQLITE_DELETE ? SQLITE_DELETE : SQLITE_INSERT), &rc
);
sessionAppendByte(&p->rebase, (eType==SQLITE_CHANGESET_REPLACE), &rc);
for(i=0; i<p->nCol; i++){
sqlite3_value *pVal = 0;
if( eOp==SQLITE_DELETE || (eOp==SQLITE_UPDATE && p->abPK[i]) ){
sqlite3changeset_old(pIter, i, &pVal);
}else{
sqlite3changeset_new(pIter, i, &pVal);
}
sessionAppendValue(&p->rebase, pVal, &rc);
}
return rc;
}
/*
** Invoke the conflict handler for the change that the changeset iterator
** currently points to.
@@ -3866,7 +3918,7 @@ static int sessionConflictHandler(
u8 *aBlob = &pIter->in.aData[pIter->in.iCurrent];
int nBlob = pIter->in.iNext - pIter->in.iCurrent;
sessionAppendBlob(&p->constraints, aBlob, nBlob, &rc);
res = SQLITE_CHANGESET_OMIT;
return SQLITE_OK;
}else{
/* No other row with the new.* primary key. */
res = xConflict(pCtx, eType+1, pIter);
@@ -3892,6 +3944,9 @@ static int sessionConflictHandler(
rc = SQLITE_MISUSE;
break;
}
if( rc==SQLITE_OK ){
rc = sessionRebaseAdd(p, res, pIter);
}
}
return rc;
@@ -4067,42 +4122,42 @@ static int sessionApplyOneWithRetry(
int rc;
rc = sessionApplyOneOp(pIter, pApply, xConflict, pCtx, &bReplace, &bRetry);
assert( rc==SQLITE_OK || (bRetry==0 && bReplace==0) );
/* If the bRetry flag is set, the change has not been applied due to an
** SQLITE_CHANGESET_DATA problem (i.e. this is an UPDATE or DELETE and
** a row with the correct PK is present in the db, but one or more other
** fields do not contain the expected values) and the conflict handler
** returned SQLITE_CHANGESET_REPLACE. In this case retry the operation,
** but pass NULL as the final argument so that sessionApplyOneOp() ignores
** the SQLITE_CHANGESET_DATA problem. */
if( bRetry ){
assert( pIter->op==SQLITE_UPDATE || pIter->op==SQLITE_DELETE );
rc = sessionApplyOneOp(pIter, pApply, xConflict, pCtx, 0, 0);
}
/* If the bReplace flag is set, the change is an INSERT that has not
** been performed because the database already contains a row with the
** specified primary key and the conflict handler returned
** SQLITE_CHANGESET_REPLACE. In this case remove the conflicting row
** before reattempting the INSERT. */
else if( bReplace ){
assert( pIter->op==SQLITE_INSERT );
rc = sqlite3_exec(db, "SAVEPOINT replace_op", 0, 0, 0);
if( rc==SQLITE_OK ){
rc = sessionBindRow(pIter,
sqlite3changeset_new, pApply->nCol, pApply->abPK, pApply->pDelete);
sqlite3_bind_int(pApply->pDelete, pApply->nCol+1, 1);
}
if( rc==SQLITE_OK ){
sqlite3_step(pApply->pDelete);
rc = sqlite3_reset(pApply->pDelete);
}
if( rc==SQLITE_OK ){
if( rc==SQLITE_OK ){
/* If the bRetry flag is set, the change has not been applied due to an
** SQLITE_CHANGESET_DATA problem (i.e. this is an UPDATE or DELETE and
** a row with the correct PK is present in the db, but one or more other
** fields do not contain the expected values) and the conflict handler
** returned SQLITE_CHANGESET_REPLACE. In this case retry the operation,
** but pass NULL as the final argument so that sessionApplyOneOp() ignores
** the SQLITE_CHANGESET_DATA problem. */
if( bRetry ){
assert( pIter->op==SQLITE_UPDATE || pIter->op==SQLITE_DELETE );
rc = sessionApplyOneOp(pIter, pApply, xConflict, pCtx, 0, 0);
}
if( rc==SQLITE_OK ){
rc = sqlite3_exec(db, "RELEASE replace_op", 0, 0, 0);
/* If the bReplace flag is set, the change is an INSERT that has not
** been performed because the database already contains a row with the
** specified primary key and the conflict handler returned
** SQLITE_CHANGESET_REPLACE. In this case remove the conflicting row
** before reattempting the INSERT. */
else if( bReplace ){
assert( pIter->op==SQLITE_INSERT );
rc = sqlite3_exec(db, "SAVEPOINT replace_op", 0, 0, 0);
if( rc==SQLITE_OK ){
rc = sessionBindRow(pIter,
sqlite3changeset_new, pApply->nCol, pApply->abPK, pApply->pDelete);
sqlite3_bind_int(pApply->pDelete, pApply->nCol+1, 1);
}
if( rc==SQLITE_OK ){
sqlite3_step(pApply->pDelete);
rc = sqlite3_reset(pApply->pDelete);
}
if( rc==SQLITE_OK ){
rc = sessionApplyOneOp(pIter, pApply, xConflict, pCtx, 0, 0);
}
if( rc==SQLITE_OK ){
rc = sqlite3_exec(db, "RELEASE replace_op", 0, 0, 0);
}
}
}
@@ -4178,7 +4233,8 @@ static int sessionChangesetApply(
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
sqlite3_changeset_iter *p /* Handle describing change and conflict */
),
void *pCtx /* First argument passed to xConflict */
void *pCtx, /* First argument passed to xConflict */
void **ppRebase, int *pnRebase /* OUT: Rebase information */
){
int schemaMismatch = 0;
int rc; /* Return code */
@@ -4216,9 +4272,18 @@ static int sessionChangesetApply(
sqlite3_finalize(sApply.pUpdate);
sqlite3_finalize(sApply.pInsert);
sqlite3_finalize(sApply.pSelect);
memset(&sApply, 0, sizeof(sApply));
sApply.db = db;
sApply.pDelete = 0;
sApply.pUpdate = 0;
sApply.pInsert = 0;
sApply.pSelect = 0;
sApply.nCol = 0;
sApply.azCol = 0;
sApply.abPK = 0;
sApply.bStat1 = 0;
sApply.bDeferConstraints = 1;
sApply.bRebaseStarted = 0;
memset(&sApply.constraints, 0, sizeof(SessionBuffer));
/* If an xFilter() callback was specified, invoke it now. If the
** xFilter callback returns zero, skip this table. If it returns
@@ -4328,16 +4393,52 @@ static int sessionChangesetApply(
sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0);
}
if( rc==SQLITE_OK && bPatchset==0 && ppRebase && pnRebase ){
*ppRebase = (void*)sApply.rebase.aBuf;
*pnRebase = sApply.rebase.nBuf;
sApply.rebase.aBuf = 0;
}
sqlite3_finalize(sApply.pInsert);
sqlite3_finalize(sApply.pDelete);
sqlite3_finalize(sApply.pUpdate);
sqlite3_finalize(sApply.pSelect);
sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */
sqlite3_free((char*)sApply.constraints.aBuf);
sqlite3_free((char*)sApply.rebase.aBuf);
sqlite3_mutex_leave(sqlite3_db_mutex(db));
return rc;
}
/*
** Apply the changeset passed via pChangeset/nChangeset to the main
** database attached to handle "db".
*/
int sqlite3changeset_apply_v2(
sqlite3 *db, /* Apply change to "main" db of this handle */
int nChangeset, /* Size of changeset in bytes */
void *pChangeset, /* Changeset blob */
int(*xFilter)(
void *pCtx, /* Copy of sixth arg to _apply() */
const char *zTab /* Table name */
),
int(*xConflict)(
void *pCtx, /* Copy of sixth arg to _apply() */
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
sqlite3_changeset_iter *p /* Handle describing change and conflict */
),
void *pCtx, /* First argument passed to xConflict */
void **ppRebase, int *pnRebase
){
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
int rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset);
if( rc==SQLITE_OK ){
rc = sessionChangesetApply(
db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase
);
}
return rc;
}
/*
** Apply the changeset passed via pChangeset/nChangeset to the main database
** attached to handle "db". Invoke the supplied conflict handler callback
@@ -4358,12 +4459,9 @@ int sqlite3changeset_apply(
),
void *pCtx /* First argument passed to xConflict */
){
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
int rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset);
if( rc==SQLITE_OK ){
rc = sessionChangesetApply(db, pIter, xFilter, xConflict, pCtx);
}
return rc;
return sqlite3changeset_apply_v2(
db, nChangeset, pChangeset, xFilter, xConflict, pCtx, 0, 0
);
}
/*
@@ -4371,6 +4469,31 @@ int sqlite3changeset_apply(
** attached to handle "db". Invoke the supplied conflict handler callback
** to resolve any conflicts encountered while applying the change.
*/
int sqlite3changeset_apply_v2_strm(
sqlite3 *db, /* Apply change to "main" db of this handle */
int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
void *pIn, /* First arg for xInput */
int(*xFilter)(
void *pCtx, /* Copy of sixth arg to _apply() */
const char *zTab /* Table name */
),
int(*xConflict)(
void *pCtx, /* Copy of sixth arg to _apply() */
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
sqlite3_changeset_iter *p /* Handle describing change and conflict */
),
void *pCtx, /* First argument passed to xConflict */
void **ppRebase, int *pnRebase
){
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
int rc = sqlite3changeset_start_strm(&pIter, xInput, pIn);
if( rc==SQLITE_OK ){
rc = sessionChangesetApply(
db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase
);
}
return rc;
}
int sqlite3changeset_apply_strm(
sqlite3 *db, /* Apply change to "main" db of this handle */
int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
@@ -4386,12 +4509,9 @@ int sqlite3changeset_apply_strm(
),
void *pCtx /* First argument passed to xConflict */
){
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
int rc = sqlite3changeset_start_strm(&pIter, xInput, pIn);
if( rc==SQLITE_OK ){
rc = sessionChangesetApply(db, pIter, xFilter, xConflict, pCtx);
}
return rc;
return sqlite3changeset_apply_v2_strm(
db, xInput, pIn, xFilter, xConflict, pCtx, 0, 0
);
}
/*
@@ -4410,6 +4530,7 @@ struct sqlite3_changegroup {
*/
static int sessionChangeMerge(
SessionTable *pTab, /* Table structure */
int bRebase, /* True for a rebase hash-table */
int bPatchset, /* True for patchsets */
SessionChange *pExist, /* Existing change */
int op2, /* Second change operation */
@@ -4419,6 +4540,7 @@ static int sessionChangeMerge(
SessionChange **ppNew /* OUT: Merged change */
){
SessionChange *pNew = 0;
int rc = SQLITE_OK;
if( !pExist ){
pNew = (SessionChange *)sqlite3_malloc(sizeof(SessionChange) + nRec);
@@ -4428,9 +4550,66 @@ static int sessionChangeMerge(
memset(pNew, 0, sizeof(SessionChange));
pNew->op = op2;
pNew->bIndirect = bIndirect;
pNew->nRecord = nRec;
pNew->aRecord = (u8*)&pNew[1];
memcpy(pNew->aRecord, aRec, nRec);
if( bIndirect==0 || bRebase==0 ){
pNew->nRecord = nRec;
memcpy(pNew->aRecord, aRec, nRec);
}else{
int i;
u8 *pIn = aRec;
u8 *pOut = pNew->aRecord;
for(i=0; i<pTab->nCol; i++){
int nIn = sessionSerialLen(pIn);
if( *pIn==0 ){
*pOut++ = 0;
}else if( pTab->abPK[i]==0 ){
*pOut++ = 0xFF;
}else{
memcpy(pOut, pIn, nIn);
pOut += nIn;
}
pIn += nIn;
}
pNew->nRecord = pOut - pNew->aRecord;
}
}else if( bRebase ){
if( pExist->op==SQLITE_DELETE && pExist->bIndirect ){
*ppNew = pExist;
}else{
int nByte = nRec + pExist->nRecord + sizeof(SessionChange);
pNew = (SessionChange*)sqlite3_malloc(nByte);
if( pNew==0 ){
rc = SQLITE_NOMEM;
}else{
int i;
u8 *a1 = pExist->aRecord;
u8 *a2 = aRec;
u8 *pOut;
memset(pNew, 0, nByte);
pNew->bIndirect = bIndirect || pExist->bIndirect;
pNew->op = op2;
pOut = pNew->aRecord = (u8*)&pNew[1];
for(i=0; i<pTab->nCol; i++){
int n1 = sessionSerialLen(a1);
int n2 = sessionSerialLen(a2);
if( *a1==0xFF || (pTab->abPK[i]==0 && bIndirect) ){
*pOut++ = 0xFF;
}else if( *a2==0 ){
memcpy(pOut, a1, n1);
pOut += n1;
}else{
memcpy(pOut, a2, n2);
pOut += n2;
}
a1 += n1;
a2 += n2;
}
pNew->nRecord = pOut - pNew->aRecord;
}
sqlite3_free(pExist);
}
}else{
int op1 = pExist->op;
@@ -4524,7 +4703,7 @@ static int sessionChangeMerge(
}
*ppNew = pNew;
return SQLITE_OK;
return rc;
}
/*
@@ -4533,15 +4712,15 @@ static int sessionChangeMerge(
*/
static int sessionChangesetToHash(
sqlite3_changeset_iter *pIter, /* Iterator to read from */
sqlite3_changegroup *pGrp /* Changegroup object to add changeset to */
sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */
int bRebase /* True if hash table is for rebasing */
){
u8 *aRec;
int nRec;
int rc = SQLITE_OK;
SessionTable *pTab = 0;
while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec) ){
while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec, 0) ){
const char *zNew;
int nCol;
int op;
@@ -4621,7 +4800,7 @@ static int sessionChangesetToHash(
}
}
rc = sessionChangeMerge(pTab,
rc = sessionChangeMerge(pTab, bRebase,
pIter->bPatchset, pExist, op, bIndirect, aRec, nRec, &pChange
);
if( rc ) break;
@@ -4729,7 +4908,7 @@ int sqlite3changegroup_add(sqlite3_changegroup *pGrp, int nData, void *pData){
rc = sqlite3changeset_start(&pIter, nData, pData);
if( rc==SQLITE_OK ){
rc = sessionChangesetToHash(pIter, pGrp);
rc = sessionChangesetToHash(pIter, pGrp, 0);
}
sqlite3changeset_finalize(pIter);
return rc;
@@ -4760,7 +4939,7 @@ int sqlite3changegroup_add_strm(
rc = sqlite3changeset_start_strm(&pIter, xInput, pIn);
if( rc==SQLITE_OK ){
rc = sessionChangesetToHash(pIter, pGrp);
rc = sessionChangesetToHash(pIter, pGrp, 0);
}
sqlite3changeset_finalize(pIter);
return rc;
@@ -4845,4 +5024,347 @@ int sqlite3changeset_concat_strm(
return rc;
}
/*
** Changeset rebaser handle.
*/
struct sqlite3_rebaser {
sqlite3_changegroup grp; /* Hash table */
};
/*
** Buffers a1 and a2 must both contain a sessions module record nCol
** fields in size. This function appends an nCol sessions module
** record to buffer pBuf that is a copy of a1, except that for
** each field that is undefined in a1[], swap in the field from a2[].
*/
static void sessionAppendRecordMerge(
SessionBuffer *pBuf, /* Buffer to append to */
int nCol, /* Number of columns in each record */
u8 *a1, int n1, /* Record 1 */
u8 *a2, int n2, /* Record 2 */
int *pRc /* IN/OUT: error code */
){
sessionBufferGrow(pBuf, n1+n2, pRc);
if( *pRc==SQLITE_OK ){
int i;
u8 *pOut = &pBuf->aBuf[pBuf->nBuf];
for(i=0; i<nCol; i++){
int nn1 = sessionSerialLen(a1);
int nn2 = sessionSerialLen(a2);
if( *a1==0 || *a1==0xFF ){
memcpy(pOut, a2, nn2);
pOut += nn2;
}else{
memcpy(pOut, a1, nn1);
pOut += nn1;
}
a1 += nn1;
a2 += nn2;
}
pBuf->nBuf = pOut-pBuf->aBuf;
assert( pBuf->nBuf<=pBuf->nAlloc );
}
}
/*
** This function is called when rebasing a local UPDATE change against one
** or more remote UPDATE changes. The aRec/nRec buffer contains the current
** old.* and new.* records for the change. The rebase buffer (a single
** record) is in aChange/nChange. The rebased change is appended to buffer
** pBuf.
**
** Rebasing the UPDATE involves:
**
** * Removing any changes to fields for which the corresponding field
** in the rebase buffer is set to "replaced" (type 0xFF). If this
** means the UPDATE change updates no fields, nothing is appended
** to the output buffer.
**
** * For each field modified by the local change for which the
** corresponding field in the rebase buffer is not "undefined" (0x00)
** or "replaced" (0xFF), the old.* value is replaced by the value
** in the rebase buffer.
*/
static void sessionAppendPartialUpdate(
SessionBuffer *pBuf, /* Append record here */
sqlite3_changeset_iter *pIter, /* Iterator pointed at local change */
u8 *aRec, int nRec, /* Local change */
u8 *aChange, int nChange, /* Record to rebase against */
int *pRc /* IN/OUT: Return Code */
){
sessionBufferGrow(pBuf, 2+nRec+nChange, pRc);
if( *pRc==SQLITE_OK ){
int bData = 0;
u8 *pOut = &pBuf->aBuf[pBuf->nBuf];
int i;
u8 *a1 = aRec;
u8 *a2 = aChange;
*pOut++ = SQLITE_UPDATE;
*pOut++ = pIter->bIndirect;
for(i=0; i<pIter->nCol; i++){
int n1 = sessionSerialLen(a1);
int n2 = sessionSerialLen(a2);
if( pIter->abPK[i] || a2[0]==0 ){
if( !pIter->abPK[i] ) bData = 1;
memcpy(pOut, a1, n1);
pOut += n1;
}else if( a2[0]!=0xFF ){
bData = 1;
memcpy(pOut, a2, n2);
pOut += n2;
}else{
*pOut++ = '\0';
}
a1 += n1;
a2 += n2;
}
if( bData ){
a2 = aChange;
for(i=0; i<pIter->nCol; i++){
int n1 = sessionSerialLen(a1);
int n2 = sessionSerialLen(a2);
if( pIter->abPK[i] || a2[0]!=0xFF ){
memcpy(pOut, a1, n1);
pOut += n1;
}else{
*pOut++ = '\0';
}
a1 += n1;
a2 += n2;
}
pBuf->nBuf = (pOut - pBuf->aBuf);
}
}
}
/*
** pIter is configured to iterate through a changeset. This function rebases
** that changeset according to the current configuration of the rebaser
** object passed as the first argument. If no error occurs and argument xOutput
** is not NULL, then the changeset is returned to the caller by invoking
** xOutput zero or more times and SQLITE_OK returned. Or, if xOutput is NULL,
** then (*ppOut) is set to point to a buffer containing the rebased changeset
** before this function returns. In this case (*pnOut) is set to the size of
** the buffer in bytes. It is the responsibility of the caller to eventually
** free the (*ppOut) buffer using sqlite3_free().
**
** If an error occurs, an SQLite error code is returned. If ppOut and
** pnOut are not NULL, then the two output parameters are set to 0 before
** returning.
*/
static int sessionRebase(
sqlite3_rebaser *p, /* Rebaser hash table */
sqlite3_changeset_iter *pIter, /* Input data */
int (*xOutput)(void *pOut, const void *pData, int nData),
void *pOut, /* Context for xOutput callback */
int *pnOut, /* OUT: Number of bytes in output changeset */
void **ppOut /* OUT: Inverse of pChangeset */
){
int rc = SQLITE_OK;
u8 *aRec = 0;
int nRec = 0;
int bNew = 0;
SessionTable *pTab = 0;
SessionBuffer sOut = {0,0,0};
while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec, &bNew) ){
SessionChange *pChange = 0;
int bDone = 0;
if( bNew ){
const char *zTab = pIter->zTab;
for(pTab=p->grp.pList; pTab; pTab=pTab->pNext){
if( 0==sqlite3_stricmp(pTab->zName, zTab) ) break;
}
bNew = 0;
/* A patchset may not be rebased */
if( pIter->bPatchset ){
rc = SQLITE_ERROR;
}
/* Append a table header to the output for this new table */
sessionAppendByte(&sOut, pIter->bPatchset ? 'P' : 'T', &rc);
sessionAppendVarint(&sOut, pIter->nCol, &rc);
sessionAppendBlob(&sOut, pIter->abPK, pIter->nCol, &rc);
sessionAppendBlob(&sOut,(u8*)pIter->zTab,(int)strlen(pIter->zTab)+1,&rc);
}
if( pTab && rc==SQLITE_OK ){
int iHash = sessionChangeHash(pTab, 0, aRec, pTab->nChange);
for(pChange=pTab->apChange[iHash]; pChange; pChange=pChange->pNext){
if( sessionChangeEqual(pTab, 0, aRec, 0, pChange->aRecord) ){
break;
}
}
}
if( pChange ){
assert( pChange->op==SQLITE_DELETE || pChange->op==SQLITE_INSERT );
switch( pIter->op ){
case SQLITE_INSERT:
if( pChange->op==SQLITE_INSERT ){
bDone = 1;
if( pChange->bIndirect==0 ){
sessionAppendByte(&sOut, SQLITE_UPDATE, &rc);
sessionAppendByte(&sOut, pIter->bIndirect, &rc);
sessionAppendBlob(&sOut, pChange->aRecord, pChange->nRecord, &rc);
sessionAppendBlob(&sOut, aRec, nRec, &rc);
}
}
break;
case SQLITE_UPDATE:
bDone = 1;
if( pChange->op==SQLITE_DELETE ){
if( pChange->bIndirect==0 ){
u8 *pCsr = aRec;
sessionSkipRecord(&pCsr, pIter->nCol);
sessionAppendByte(&sOut, SQLITE_INSERT, &rc);
sessionAppendByte(&sOut, pIter->bIndirect, &rc);
sessionAppendRecordMerge(&sOut, pIter->nCol,
pCsr, nRec-(pCsr-aRec),
pChange->aRecord, pChange->nRecord, &rc
);
}
}else{
sessionAppendPartialUpdate(&sOut, pIter,
aRec, nRec, pChange->aRecord, pChange->nRecord, &rc
);
}
break;
default:
assert( pIter->op==SQLITE_DELETE );
bDone = 1;
if( pChange->op==SQLITE_INSERT ){
sessionAppendByte(&sOut, SQLITE_DELETE, &rc);
sessionAppendByte(&sOut, pIter->bIndirect, &rc);
sessionAppendRecordMerge(&sOut, pIter->nCol,
pChange->aRecord, pChange->nRecord, aRec, nRec, &rc
);
}
break;
}
}
if( bDone==0 ){
sessionAppendByte(&sOut, pIter->op, &rc);
sessionAppendByte(&sOut, pIter->bIndirect, &rc);
sessionAppendBlob(&sOut, aRec, nRec, &rc);
}
if( rc==SQLITE_OK && xOutput && sOut.nBuf>SESSIONS_STRM_CHUNK_SIZE ){
rc = xOutput(pOut, sOut.aBuf, sOut.nBuf);
sOut.nBuf = 0;
}
if( rc ) break;
}
if( rc!=SQLITE_OK ){
sqlite3_free(sOut.aBuf);
memset(&sOut, 0, sizeof(sOut));
}
if( rc==SQLITE_OK ){
if( xOutput ){
if( sOut.nBuf>0 ){
rc = xOutput(pOut, sOut.aBuf, sOut.nBuf);
}
}else{
*ppOut = (void*)sOut.aBuf;
*pnOut = sOut.nBuf;
sOut.aBuf = 0;
}
}
sqlite3_free(sOut.aBuf);
return rc;
}
/*
** Create a new rebaser object.
*/
int sqlite3rebaser_create(sqlite3_rebaser **ppNew){
int rc = SQLITE_OK;
sqlite3_rebaser *pNew;
pNew = sqlite3_malloc(sizeof(sqlite3_rebaser));
if( pNew==0 ){
rc = SQLITE_NOMEM;
}else{
memset(pNew, 0, sizeof(sqlite3_rebaser));
}
*ppNew = pNew;
return rc;
}
/*
** Call this one or more times to configure a rebaser.
*/
int sqlite3rebaser_configure(
sqlite3_rebaser *p,
int nRebase, const void *pRebase
){
sqlite3_changeset_iter *pIter = 0; /* Iterator opened on pData/nData */
int rc; /* Return code */
rc = sqlite3changeset_start(&pIter, nRebase, (void*)pRebase);
if( rc==SQLITE_OK ){
rc = sessionChangesetToHash(pIter, &p->grp, 1);
}
sqlite3changeset_finalize(pIter);
return rc;
}
/*
** Rebase a changeset according to current rebaser configuration
*/
int sqlite3rebaser_rebase(
sqlite3_rebaser *p,
int nIn, const void *pIn,
int *pnOut, void **ppOut
){
sqlite3_changeset_iter *pIter = 0; /* Iterator to skip through input */
int rc = sqlite3changeset_start(&pIter, nIn, (void*)pIn);
if( rc==SQLITE_OK ){
rc = sessionRebase(p, pIter, 0, 0, pnOut, ppOut);
sqlite3changeset_finalize(pIter);
}
return rc;
}
/*
** Rebase a changeset according to current rebaser configuration
*/
int sqlite3rebaser_rebase_strm(
sqlite3_rebaser *p,
int (*xInput)(void *pIn, void *pData, int *pnData),
void *pIn,
int (*xOutput)(void *pOut, const void *pData, int nData),
void *pOut
){
sqlite3_changeset_iter *pIter = 0; /* Iterator to skip through input */
int rc = sqlite3changeset_start_strm(&pIter, xInput, pIn);
if( rc==SQLITE_OK ){
rc = sessionRebase(p, pIter, xOutput, pOut, 0, 0);
sqlite3changeset_finalize(pIter);
}
return rc;
}
/*
** Destroy a rebaser object
*/
void sqlite3rebaser_delete(sqlite3_rebaser *p){
if( p ){
sessionDeleteTable(p->grp.pList);
sqlite3_free(p);
}
}
#endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */
+215 -12
View File
@@ -948,19 +948,18 @@ void sqlite3changegroup_delete(sqlite3_changegroup*);
/*
** CAPI3REF: Apply A Changeset To A Database
**
** Apply a changeset to a database. This function attempts to update the
** "main" database attached to handle db with the changes found in the
** changeset passed via the second and third arguments.
** Apply a changeset or patchset to a database. These functions attempt to
** update the "main" database attached to handle db with the changes found in
** the changeset passed via the second and third arguments.
**
** The fourth argument (xFilter) passed to this function is the "filter
** The fourth argument (xFilter) passed to these functions is the "filter
** callback". If it is not NULL, then for each table affected by at least one
** change in the changeset, the filter callback is invoked with
** the table name as the second argument, and a copy of the context pointer
** passed as the sixth argument to this function as the first. If the "filter
** callback" returns zero, then no attempt is made to apply any changes to
** the table. Otherwise, if the return value is non-zero or the xFilter
** argument to this function is NULL, all changes related to the table are
** attempted.
** passed as the sixth argument as the first. If the "filter callback"
** returns zero, then no attempt is made to apply any changes to the table.
** Otherwise, if the return value is non-zero or the xFilter argument to
** is NULL, all changes related to the table are attempted.
**
** For each table that is not excluded by the filter callback, this function
** tests that the target database contains a compatible table. A table is
@@ -1005,7 +1004,7 @@ void sqlite3changegroup_delete(sqlite3_changegroup*);
**
** <dl>
** <dt>DELETE Changes<dd>
** For each DELETE change, this function checks if the target database
** For each DELETE change, the function checks if the target database
** contains a row with the same primary key value (or values) as the
** original row values stored in the changeset. If it does, and the values
** stored in all non-primary key columns also match the values stored in
@@ -1050,7 +1049,7 @@ void sqlite3changegroup_delete(sqlite3_changegroup*);
** [SQLITE_CHANGESET_REPLACE].
**
** <dt>UPDATE Changes<dd>
** For each UPDATE change, this function checks if the target database
** For each UPDATE change, the function checks if the target database
** contains a row with the same primary key value (or values) as the
** original row values stored in the changeset. If it does, and the values
** stored in all modified non-primary key columns also match the values
@@ -1081,11 +1080,21 @@ void sqlite3changegroup_delete(sqlite3_changegroup*);
** This can be used to further customize the applications conflict
** resolution strategy.
**
** All changes made by this function are enclosed in a savepoint transaction.
** All changes made by these functions are enclosed in a savepoint transaction.
** If any other error (aside from a constraint failure when attempting to
** write to the target database) occurs, then the savepoint transaction is
** rolled back, restoring the target database to its original state, and an
** SQLite error code returned.
**
** If the output parameters (ppRebase) and (pnRebase) are non-NULL and
** the input is a changeset (not a patchset), then sqlite3changeset_apply_v2()
** may set (*ppRebase) to point to a "rebase" that may be used with the
** sqlite3_rebaser APIs buffer before returning. In this case (*pnRebase)
** is set to the size of the buffer in bytes. It is the responsibility of the
** caller to eventually free any such buffer using sqlite3_free(). The buffer
** is only allocated and populated if one or more conflicts were encountered
** while applying the patchset. See comments surrounding the sqlite3_rebaser
** APIs for further details.
*/
int sqlite3changeset_apply(
sqlite3 *db, /* Apply change to "main" db of this handle */
@@ -1102,6 +1111,22 @@ int sqlite3changeset_apply(
),
void *pCtx /* First argument passed to xConflict */
);
int sqlite3changeset_apply_v2(
sqlite3 *db, /* Apply change to "main" db of this handle */
int nChangeset, /* Size of changeset in bytes */
void *pChangeset, /* Changeset blob */
int(*xFilter)(
void *pCtx, /* Copy of sixth arg to _apply() */
const char *zTab /* Table name */
),
int(*xConflict)(
void *pCtx, /* Copy of sixth arg to _apply() */
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
sqlite3_changeset_iter *p /* Handle describing change and conflict */
),
void *pCtx, /* First argument passed to xConflict */
void **ppRebase, int *pnRebase
);
/*
** CAPI3REF: Constants Passed To The Conflict Handler
@@ -1199,6 +1224,161 @@ int sqlite3changeset_apply(
#define SQLITE_CHANGESET_REPLACE 1
#define SQLITE_CHANGESET_ABORT 2
/*
** CAPI3REF: Rebasing changesets
** EXPERIMENTAL
**
** Suppose there is a site hosting a database in state S0. And that
** modifications are made that move that database to state S1 and a
** changeset recorded (the "local" changeset). Then, a changeset based
** on S0 is received from another site (the "remote" changeset) and
** applied to the database. The database is then in state
** (S1+"remote"), where the exact state depends on any conflict
** resolution decisions (OMIT or REPLACE) made while applying "remote".
** Rebasing a changeset is to update it to take those conflict
** resolution decisions into account, so that the same conflicts
** do not have to be resolved elsewhere in the network.
**
** For example, if both the local and remote changesets contain an
** INSERT of the same key on "CREATE TABLE t1(a PRIMARY KEY, b)":
**
** local: INSERT INTO t1 VALUES(1, 'v1');
** remote: INSERT INTO t1 VALUES(1, 'v2');
**
** and the conflict resolution is REPLACE, then the INSERT change is
** removed from the local changeset (it was overridden). Or, if the
** conflict resolution was "OMIT", then the local changeset is modified
** to instead contain:
**
** UPDATE t1 SET b = 'v2' WHERE a=1;
**
** Changes within the local changeset are rebased as follows:
**
** <dl>
** <dt>Local INSERT<dd>
** This may only conflict with a remote INSERT. If the conflict
** resolution was OMIT, then add an UPDATE change to the rebased
** changeset. Or, if the conflict resolution was REPLACE, add
** nothing to the rebased changeset.
**
** <dt>Local DELETE<dd>
** This may conflict with a remote UPDATE or DELETE. In both cases the
** only possible resolution is OMIT. If the remote operation was a
** DELETE, then add no change to the rebased changeset. If the remote
** operation was an UPDATE, then the old.* fields of change are updated
** to reflect the new.* values in the UPDATE.
**
** <dt>Local UPDATE<dd>
** This may conflict with a remote UPDATE or DELETE. If it conflicts
** with a DELETE, and the conflict resolution was OMIT, then the update
** is changed into an INSERT. Any undefined values in the new.* record
** from the update change are filled in using the old.* values from
** the conflicting DELETE. Or, if the conflict resolution was REPLACE,
** the UPDATE change is simply omitted from the rebased changeset.
**
** If conflict is with a remote UPDATE and the resolution is OMIT, then
** the old.* values are rebased using the new.* values in the remote
** change. Or, if the resolution is REPLACE, then the change is copied
** into the rebased changeset with updates to columns also updated by
** the conflicting remote UPDATE removed. If this means no columns would
** be updated, the change is omitted.
** </dl>
**
** A local change may be rebased against multiple remote changes
** simultaneously. If a single key is modified by multiple remote
** changesets, they are combined as follows before the local changeset
** is rebased:
**
** <ul>
** <li> If there has been one or more REPLACE resolutions on a
** key, it is rebased according to a REPLACE.
**
** <li> If there have been no REPLACE resolutions on a key, then
** the local changeset is rebased according to the most recent
** of the OMIT resolutions.
** </ul>
**
** Note that conflict resolutions from multiple remote changesets are
** combined on a per-field basis, not per-row. This means that in the
** case of multiple remote UPDATE operations, some fields of a single
** local change may be rebased for REPLACE while others are rebased for
** OMIT.
**
** In order to rebase a local changeset, the remote changeset must first
** be applied to the local database using sqlite3changeset_apply_v2() and
** the buffer of rebase information captured. Then:
**
** <ol>
** <li> An sqlite3_rebaser object is created by calling
** sqlite3rebaser_create().
** <li> The new object is configured with the rebase buffer obtained from
** sqlite3changeset_apply_v2() by calling sqlite3rebaser_configure().
** If the local changeset is to be rebased against multiple remote
** changesets, then sqlite3rebaser_configure() should be called
** multiple times, in the same order that the multiple
** sqlite3changeset_apply_v2() calls were made.
** <li> Each local changeset is rebased by calling sqlite3rebaser_rebase().
** <li> The sqlite3_rebaser object is deleted by calling
** sqlite3rebaser_delete().
** </ol>
*/
typedef struct sqlite3_rebaser sqlite3_rebaser;
/*
** CAPI3REF: Create a changeset rebaser object.
** EXPERIMENTAL
**
** Allocate a new changeset rebaser object. If successful, set (*ppNew) to
** point to the new object and return SQLITE_OK. Otherwise, if an error
** occurs, return an SQLite error code (e.g. SQLITE_NOMEM) and set (*ppNew)
** to NULL.
*/
int sqlite3rebaser_create(sqlite3_rebaser **ppNew);
/*
** CAPI3REF: Configure a changeset rebaser object.
** EXPERIMENTAL
**
** Configure the changeset rebaser object to rebase changesets according
** to the conflict resolutions described by buffer pRebase (size nRebase
** bytes), which must have been obtained from a previous call to
** sqlite3changeset_apply_v2().
*/
int sqlite3rebaser_configure(
sqlite3_rebaser*,
int nRebase, const void *pRebase
);
/*
** CAPI3REF: Rebase a changeset
** EXPERIMENTAL
**
** Argument pIn must point to a buffer containing a changeset nIn bytes
** in size. This function allocates and populates a buffer with a copy
** of the changeset rebased rebased according to the configuration of the
** rebaser object passed as the first argument. If successful, (*ppOut)
** is set to point to the new buffer containing the rebased changset and
** (*pnOut) to its size in bytes and SQLITE_OK returned. It is the
** responsibility of the caller to eventually free the new buffer using
** sqlite3_free(). Otherwise, if an error occurs, (*ppOut) and (*pnOut)
** are set to zero and an SQLite error code returned.
*/
int sqlite3rebaser_rebase(
sqlite3_rebaser*,
int nIn, const void *pIn,
int *pnOut, void **ppOut
);
/*
** CAPI3REF: Delete a changeset rebaser object.
** EXPERIMENTAL
**
** Delete the changeset rebaser object and all associated resources. There
** should be one call to this function for each successful invocation
** of sqlite3rebaser_create().
*/
void sqlite3rebaser_delete(sqlite3_rebaser *p);
/*
** CAPI3REF: Streaming Versions of API functions.
**
@@ -1303,6 +1483,22 @@ int sqlite3changeset_apply_strm(
),
void *pCtx /* First argument passed to xConflict */
);
int sqlite3changeset_apply_v2_strm(
sqlite3 *db, /* Apply change to "main" db of this handle */
int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
void *pIn, /* First arg for xInput */
int(*xFilter)(
void *pCtx, /* Copy of sixth arg to _apply() */
const char *zTab /* Table name */
),
int(*xConflict)(
void *pCtx, /* Copy of sixth arg to _apply() */
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
sqlite3_changeset_iter *p /* Handle describing change and conflict */
),
void *pCtx, /* First argument passed to xConflict */
void **ppRebase, int *pnRebase
);
int sqlite3changeset_concat_strm(
int (*xInputA)(void *pIn, void *pData, int *pnData),
void *pInA,
@@ -1340,6 +1536,13 @@ int sqlite3changegroup_output_strm(sqlite3_changegroup*,
int (*xOutput)(void *pOut, const void *pData, int nData),
void *pOut
);
int sqlite3rebaser_rebase_strm(
sqlite3_rebaser *pRebaser,
int (*xInput)(void *pIn, void *pData, int *pnData),
void *pIn,
int (*xOutput)(void *pOut, const void *pData, int nData),
void *pOut
);
/*
+180 -11
View File
@@ -14,6 +14,10 @@
# endif
#endif
#ifndef SQLITE_AMALGAMATION
typedef unsigned char u8;
#endif
typedef struct TestSession TestSession;
struct TestSession {
sqlite3_session *pSession;
@@ -711,10 +715,8 @@ static int testStreamInput(
}
/*
** sqlite3changeset_apply DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?
*/
static int SQLITE_TCLAPI test_sqlite3changeset_apply(
static int SQLITE_TCLAPI testSqlite3changesetApply(
int bV2,
void * clientData,
Tcl_Interp *interp,
int objc,
@@ -727,6 +729,8 @@ static int SQLITE_TCLAPI test_sqlite3changeset_apply(
int nChangeset; /* Size of buffer aChangeset in bytes */
TestConflictHandler ctx;
TestStreamInput sStr;
void *pRebase = 0;
int nRebase = 0;
memset(&sStr, 0, sizeof(sStr));
sStr.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
@@ -748,24 +752,68 @@ static int SQLITE_TCLAPI test_sqlite3changeset_apply(
ctx.interp = interp;
if( sStr.nStream==0 ){
rc = sqlite3changeset_apply(db, nChangeset, pChangeset,
(objc==5) ? test_filter_handler : 0, test_conflict_handler, (void *)&ctx
);
if( bV2==0 ){
rc = sqlite3changeset_apply(db, nChangeset, pChangeset,
(objc==5)?test_filter_handler:0, test_conflict_handler, (void *)&ctx
);
}else{
rc = sqlite3changeset_apply_v2(db, nChangeset, pChangeset,
(objc==5)?test_filter_handler:0, test_conflict_handler, (void *)&ctx,
&pRebase, &nRebase
);
}
}else{
sStr.aData = (unsigned char*)pChangeset;
sStr.nData = nChangeset;
rc = sqlite3changeset_apply_strm(db, testStreamInput, (void*)&sStr,
(objc==5) ? test_filter_handler : 0, test_conflict_handler, (void *)&ctx
);
if( bV2==0 ){
rc = sqlite3changeset_apply_strm(db, testStreamInput, (void*)&sStr,
(objc==5) ? test_filter_handler : 0,
test_conflict_handler, (void *)&ctx
);
}else{
rc = sqlite3changeset_apply_v2_strm(db, testStreamInput, (void*)&sStr,
(objc==5) ? test_filter_handler : 0,
test_conflict_handler, (void *)&ctx,
&pRebase, &nRebase
);
}
}
if( rc!=SQLITE_OK ){
return test_session_error(interp, rc, 0);
}else{
Tcl_ResetResult(interp);
if( bV2 && pRebase ){
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pRebase, nRebase));
}
}
Tcl_ResetResult(interp);
sqlite3_free(pRebase);
return TCL_OK;
}
/*
** sqlite3changeset_apply DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?
*/
static int SQLITE_TCLAPI test_sqlite3changeset_apply(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
return testSqlite3changesetApply(0, clientData, interp, objc, objv);
}
/*
** sqlite3changeset_apply_v2 DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?
*/
static int SQLITE_TCLAPI test_sqlite3changeset_apply_v2(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
return testSqlite3changesetApply(1, clientData, interp, objc, objv);
}
/*
** sqlite3changeset_apply_replace_all DB CHANGESET
*/
@@ -1019,6 +1067,125 @@ static int SQLITE_TCLAPI test_sqlite3session_foreach(
return TCL_OK;
}
/*
** tclcmd: CMD configure REBASE-BLOB
** tclcmd: CMD rebase CHANGESET
** tclcmd: CMD delete
*/
static int SQLITE_TCLAPI test_rebaser_cmd(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
struct RebaseSubcmd {
const char *zSub;
int nArg;
const char *zMsg;
int iSub;
} aSub[] = {
{ "configure", 1, "REBASE-BLOB" }, /* 0 */
{ "delete", 0, "" }, /* 1 */
{ "rebase", 1, "CHANGESET" }, /* 2 */
{ 0 }
};
sqlite3_rebaser *p = (sqlite3_rebaser*)clientData;
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;
}
assert( iSub==0 || iSub==1 || iSub==2 );
assert( rc==SQLITE_OK );
switch( iSub ){
case 0: { /* configure */
int nRebase = 0;
unsigned char *pRebase = Tcl_GetByteArrayFromObj(objv[2], &nRebase);
rc = sqlite3rebaser_configure(p, nRebase, pRebase);
break;
}
case 1: /* delete */
Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
break;
default: { /* rebase */
TestStreamInput sStr; /* Input stream */
TestSessionsBlob sOut; /* Output blob */
memset(&sStr, 0, sizeof(sStr));
memset(&sOut, 0, sizeof(sOut));
sStr.aData = Tcl_GetByteArrayFromObj(objv[2], &sStr.nData);
sStr.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
if( sStr.nStream ){
rc = sqlite3rebaser_rebase_strm(p,
testStreamInput, (void*)&sStr,
testStreamOutput, (void*)&sOut
);
}else{
rc = sqlite3rebaser_rebase(p, sStr.nData, sStr.aData, &sOut.n, &sOut.p);
}
if( rc==SQLITE_OK ){
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(sOut.p, sOut.n));
}
sqlite3_free(sOut.p);
break;
}
}
if( rc!=SQLITE_OK ){
return test_session_error(interp, rc, 0);
}
return TCL_OK;
}
static void SQLITE_TCLAPI test_rebaser_del(void *clientData){
sqlite3_rebaser *p = (sqlite3_rebaser*)clientData;
sqlite3rebaser_delete(p);
}
/*
** tclcmd: sqlite3rebaser_create NAME
*/
static int SQLITE_TCLAPI test_sqlite3rebaser_create(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int rc;
sqlite3_rebaser *pNew = 0;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "NAME");
return SQLITE_ERROR;
}
rc = sqlite3rebaser_create(&pNew);
if( rc!=SQLITE_OK ){
return test_session_error(interp, rc, 0);
}
Tcl_CreateObjCommand(interp, Tcl_GetString(objv[1]), test_rebaser_cmd,
(ClientData)pNew, test_rebaser_del
);
Tcl_SetObjResult(interp, objv[1]);
return TCL_OK;
}
int TestSession_Init(Tcl_Interp *interp){
struct Cmd {
const char *zCmd;
@@ -1029,9 +1196,11 @@ int TestSession_Init(Tcl_Interp *interp){
{ "sqlite3changeset_invert", test_sqlite3changeset_invert },
{ "sqlite3changeset_concat", test_sqlite3changeset_concat },
{ "sqlite3changeset_apply", test_sqlite3changeset_apply },
{ "sqlite3changeset_apply_v2", test_sqlite3changeset_apply_v2 },
{ "sqlite3changeset_apply_replace_all",
test_sqlite3changeset_apply_replace_all },
{ "sql_exec_changeset", test_sql_exec_changeset },
{ "sqlite3rebaser_create", test_sqlite3rebaser_create },
};
int i;
+3
View File
@@ -71,6 +71,7 @@ LIBOBJ+= vdbe.o parse.o \
pager.o pcache.o pcache1.o pragma.o prepare.o printf.o \
random.o resolve.o rowset.o rtree.o \
select.o sqlite3rbu.o status.o stmt.o \
server.o \
table.o threads.o tokenize.o treeview.o trigger.o \
update.o userauth.o util.o vacuum.o \
vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbesort.o \
@@ -148,6 +149,8 @@ SRC = \
$(TOP)/src/resolve.c \
$(TOP)/src/rowset.c \
$(TOP)/src/select.c \
$(TOP)/src/server.c \
$(TOP)/src/server.h \
$(TOP)/src/status.c \
$(TOP)/src/shell.c.in \
$(TOP)/src/sqlite.h.in \
+84 -70
View File
@@ -1,10 +1,11 @@
C Draft\sfix\sfor\san\sfsdir()\sissue\sreported\svia\sthe\smailing\slist.
D 2018-03-15T05:25:57.098
C Fix\san\serror\sin\sREADME-server-edition.html.
D 2018-03-31T18:43:20.756
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F Makefile.in 7016fc56c6b9bfe5daac4f34be8be38d8c0b5fab79ccbfb764d3b23bf1c6fff3
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
F Makefile.msc 8a6727b41a4626a5552016f4b0088b43fb14a32f53db1f47e83a0fefcb63c49a
F Makefile.msc bdcad21b027a56a73e54a1121cfb9edd0a35c0abfa53aa12c2f996006ff99960
F README-server-edition.html b98409c486d6f02871b20a9e29e1e18cd050a02e03062569ffb051774b4d6861
F README.md 1d5342ebda97420f114283e604e5fe99b0da939d63b76d492eabbaae23488276
F VERSION cdf91ac446255ecf3d8f6d8c3ee40d64123235ae5b3cef29d344e61b45ec3759
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
@@ -217,7 +218,7 @@ F ext/fts5/tool/fts5txt2db.tcl 526a9979c963f1c54fd50976a05a502e533a4c59
F ext/fts5/tool/loadfts5.tcl 95b03429ee6b138645703c6ca192c3ac96eaf093
F ext/fts5/tool/mkfts5c.tcl d1c2a9ab8e0ec690a52316f33dd9b1d379942f45
F ext/fts5/tool/showfts5.tcl d54da0e067306663e2d5d523965ca487698e722c
F ext/icu/README.txt d9fbbad0c2f647c3fdf715fc9fd64af53aedfc43
F ext/icu/README.txt a295e91db742b153e8dce8f7efd31d28ad1eea4df31ef4daa3eedc85be2f5138
F ext/icu/icu.c c2c7592574c08cd1270d909b8fb8797f6ea1f49e931e71dbcc25506b9b224580
F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37
F ext/lsm1/Makefile 98b0a24b45e248283d6bea4b6cb3e58d7b394edd8e96a0ac28c5fa5104813bad
@@ -272,12 +273,12 @@ F ext/misc/appendvfs.c 3777f22ec1057dc4e5fd89f2fbddcc7a29fbeef1ad038c736c54411bb
F ext/misc/btreeinfo.c 78c8c57d325185ccc04b7679e5b020e34a4d9c87453e6b7ac943d0a26cee3256
F ext/misc/carray.c ed96c218ea940b85c9a274c4d9c59fe9491c299147a38a8bba537687bd6c6005
F ext/misc/closure.c 0d2a038df8fbae7f19de42e7c7d71f2e4dc88704
F ext/misc/completion.c 52c3f01523e3e387eb321b4739a89d1fe47cbe6025aa1f2d8d3685e9e365df0f
F ext/misc/completion.c 0d0bd16378415b982e7119baddef52a0d2cc25860c238a9d2832b0cc6a84a16d
F ext/misc/compress.c dd4f8a6d0baccff3c694757db5b430f3bbd821d8686d1fc24df55cf9f035b189
F ext/misc/csv.c 1a009b93650732e22334edc92459c4630b9fa703397cbb3c8ca279921a36ca11
F ext/misc/dbdump.c 22018e00eb50e9ebf9067c92d4e7162dc5006a3efc4e0c19bc3829825a1043b0
F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2
F ext/misc/fileio.c 673d6bde25cab00ad40cc95b89fa99c9f00870c565de3beaab9d6a9658bbc3e0
F ext/misc/fileio.c 48c7751c78fc4cdd29d8c862fd2f3f98bbfefa2a3cf1ca1496df4bf02eb8cded
F ext/misc/fuzzer.c 7c64b8197bb77b7d64eff7cac7848870235d4c25
F ext/misc/ieee754.c f190d0cc5182529acb15babd177781be1ac1718c
F ext/misc/json1.c dbe086615b9546c156bf32b9378fc09383b58bd17513b866cfd24c1e15281984
@@ -304,7 +305,7 @@ F ext/misc/vfsstat.c bf10ef0bc51e1ad6756629e1edb142f7a8db1178
F ext/misc/vtablog.c 31d0d8f4406795679dcd3a67917c213d3a2a5fb3ea5de35f6e773491ed7e13c9
F ext/misc/vtshim.c 1976e6dd68dd0d64508c91a6dfab8e75f8aaf6cd
F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212
F ext/misc/zipfile.c e4e4f8289a8dee7e4f78547dacfb6548bcd83a02b52c0bc9205d8fbfb7f4b8a1
F ext/misc/zipfile.c c4de8f0ad446ce4a49aae11ff7b771cd7af60d7136c0bcfb53da1475b9075e79
F ext/misc/zorder.c b0ff58fa643afa1d846786d51ea8d5c4b6b35aa0254ab5a82617db92f3adda64
F ext/rbu/rbu.c ea7d1b7eb44c123a2a619332e19fe5313500705c4a58aaa1887905c0d83ffc2e
F ext/rbu/rbu1.test 43836fac8c7179a358eaf38a8a1ef3d6e6285842
@@ -322,7 +323,8 @@ F ext/rbu/rbu9.test 0806d1772c9f4981774ff028de6656e4183082af
F ext/rbu/rbuA.test 4e58e46e60d4064248614c43303d71f1b18cc804dd834ce6a913b3861828b28d
F ext/rbu/rbuB.test c25bc325b8072a766e56bb76c001866b405925c2
F ext/rbu/rbuC.test efe47db508a0269b683cb2a1913a425ffd39a831
F ext/rbu/rbu_common.tcl a38e8e2d4a50fd6aaf151633714c1b1d2fae3ead
F ext/rbu/rbu_common.tcl ebb8d81f44dc20e360cff1f34eb2ad0def33128805c5b36afcc44ab338509589
F ext/rbu/rbucollate.test 86d6fc9b8f59a27b7b5a6e20b5e29816d338a0dbdea8c54bfcc549a0d437f3ea
F ext/rbu/rbucrash.test 61470d977a06a0abc2ec35b05d82a1d7d87d10f4ffabad14c1c231edc942ad66
F ext/rbu/rbucrash2.test b2ecbdd7bb72c88bd217c65bd00dafa07f7f2d4d
F ext/rbu/rbudiff.test 3e605cf624d00d04d0fb1316a3acec4fbe3b3ac5
@@ -339,9 +341,9 @@ F ext/rbu/rbusave.test 0f43b6686084f426ddd040b878426452fd2c2f48
F ext/rbu/rbutemplimit.test cd553a9288d515d0b5f87d277e76fd18c4aa740b761e7880fab11ce986ea18d1
F ext/rbu/rbuvacuum.test ff357e9b556ca7ad4673da0ff7f244def919ff858e0f9f350d3e30fdd83a62a8
F ext/rbu/rbuvacuum2.test 2074ab14fe66e1c7e7210c62562650dcd215bbaa
F ext/rbu/sqlite3rbu.c 64bd08c1011456f90564ed167abce3a9c2af421a924b21eb57231e078da04feb
F ext/rbu/sqlite3rbu.c f6e9ca388b5d4680fbf266a4d10a21aec11d6baf48f6d06fd53f6b205fad959f
F ext/rbu/sqlite3rbu.h b42bcd4d8357268c6c39ab2a60b29c091e89328fa8cc49c8fac5ab8d007e79b2
F ext/rbu/test_rbu.c 7073979b9cc80912bb03599ac8d85ab5d3bf03cfacd3463f2dcdd7822997533a
F ext/rbu/test_rbu.c baa23eb28457580673d2175e5f0c29ced0cd320ee819b13ad362398c53b96e90
F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15
F ext/repair/checkfreelist.c 0dbae18c1b552f58d64f8969e4fb1e7f11930c60a8c2a9a8d50b7f15bdfd54bd
F ext/repair/checkindex.c 7d28c01a2e012ac64257d230fc452b2cafb78311a91a343633d01d95220f66f3
@@ -381,7 +383,7 @@ F ext/session/changeset.c 4ccbaa4531944c24584bf6a61ba3a39c62b6267a
F ext/session/session1.test 736d7ff178662f0b717c37f46531b84a5ce0210ccb0c4edf629c55dbcbbc3ea1
F ext/session/session2.test 284de45abae4cc1082bc52012ee81521d5ac58e0
F ext/session/session3.test ce9ce3dfa489473987f899e9f6a0f2db9bde3479
F ext/session/session4.test 3eea8058643e5adbd3293a5c553255c35e774ed90e7cbec09c9b010d176ad396
F ext/session/session4.test 6778997065b44d99c51ff9cece047ff9244a32856b328735ae27ddef68979c40
F ext/session/session5.test 716bc6fafd625ce60dfa62ae128971628c1a1169
F ext/session/session6.test 443789bc2fca12e4f7075cf692c60b8a2bea1a26
F ext/session/session8.test 8e194b3f655d861ca36de5d4de53f702751bab3b
@@ -394,24 +396,25 @@ F ext/session/sessionE.test 0a616c4ad8fd2c05f23217ebb6212ef80b7fef30f5f086a6633a
F ext/session/sessionF.test c2f178d4dfd723a5fd94a730ea2ccb44c669e3ce
F ext/session/sessionG.test 63f9a744341d670775af29e4f19c1ef09a4810798400f28cd76704803a2e56ff
F ext/session/sessionH.test 332b60e4c2e0a680105e11936201cabe378216f307e2747803cea56fa7d9ebae
F ext/session/session_common.tcl 7776eda579773113b30c7abfd4545c445228cb73
F ext/session/session_common.tcl 748141b02042b942e04a7afad9ffb2212a3997de536ed95f6dec7bb5018ede2c
F ext/session/session_speed_test.c edc1f96fd5e0e4b16eb03e2a73041013d59e8723
F ext/session/sessionat.test efe88965e74ff1bc2af9c310b28358c02d420c1fb2705cc7a28f0c1cc142c3ec
F ext/session/sessiondiff.test ad13dd65664bae26744e1f18eb3cbd5588349b7e9118851d8f9364248d67bcec
F ext/session/sessionfault.test da273f2712b6411e85e71465a1733b8501dbf6f7
F ext/session/sessionfault2.test 04aa0bc9aa70ea43d8de82c4f648db4de1e990b0
F ext/session/sessionfault2.test 555a8504de03d59b369ef20209585da5aeb2671dedabc4584e9ffe6269689185
F ext/session/sessionrebase.test 4e1bcfd26fd8ed8ac571746f56cceeb45184f4d65490ea0d405227cfc8a9cba8
F ext/session/sessionstat1.test 41cd97c2e48619a41cdf8ae749e1b25f34719de638689221aa43971be693bf4e
F ext/session/sessionwor.test 2f3744236dc8b170a695b7d8ddc8c743c7e79fdc
F ext/session/sqlite3session.c 9edfaaa74977ddecd7bbd94e8f844d9b0f6eec22d1d547e806361670db814c1e
F ext/session/sqlite3session.h 2e1584b030fbd841cefdce15ba984871978d305f586da2d1972f6e1958fa10b1
F ext/session/test_session.c eb0bd6c1ea791c1d66ee4ef94c16500dad936386
F ext/session/sqlite3session.c 4e21db8d2abb7960ded6f66e745671442e3ae2156a5ff8f7cf07567c507c324e
F ext/session/sqlite3session.h 85fd2dc3df1532b0695beb345e2ff375c2745a4654b405fcbe33afa18baa6cc7
F ext/session/test_session.c f253742ea01b089326f189b5ae15a5b55c1c9e97452e4a195ee759ba51b404d5
F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
F ext/userauth/userauth.c 3410be31283abba70255d71fd24734e017a4497f
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
F main.mk 63668484c95454af7fc04a384da27ac556f27368d6d0c345e405e1677c66768f
F main.mk 3b8fd9c7783cb6790a22d414ce0653bf1073bf4ebb025abbe18e50a946075ff2
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
@@ -424,24 +427,24 @@ F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
F src/alter.c cf7a8af45cb0ace672f47a1b29ab24092a9e8cd8d945a9974e3b5d925f548594
F src/analyze.c 6b42e36a5dcc2703a771f2411bd5e99524bd62c7ecde209bb88dfb04c72f046e
F src/analyze.c 71fbbeb7b25417592f54d869fe90c28b48e4cecb9926ef9b06d90fb0aec48941
F src/attach.c f6f212c43dddba79dfcb723fb9470785f3ff55bde8953cd9d2546f3022070a41
F src/auth.c 6277d63837357549fe14e723490d6dc1a38768d71c795c5eb5c0f8a99f918f73
F src/backup.c faf17e60b43233c214aae6a8179d24503a61e83b
F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33
F src/btmutex.c 0e9ce2d56159b89b9bc8e197e023ee11e39ff8ca
F src/btree.c 18a53540aa35dbdf77f715ea928422a4ed9011dc16ea7b50f803fd1617fcc4f5
F src/btree.h 0866c0a08255142ea0e754aabd211c843cab32045c978a592a43152405ed0c84
F src/btree.c ac5e98b809c0e7e2d1840afa0908f6aa2542cf7bed631aab17199047a35b5588
F src/btree.h 6fb019c0097f90a5c02fffdf8217bc1eb86b8bd5f286a5bdf269e8cfa29ba668
F src/btreeInt.h 620ab4c7235f43572cf3ac2ac8723cbdf68073be4d29da24897c7b77dda5fd96
F src/build.c 672022c06e1a5c2653f80c77a687de11f7e65ce81d20fe2825aadfa13a875c33
F src/build.c 4b085737d385ab2f07e07a8a5ef64d7378dad112ecf36f6150388b80dd2dcdb5
F src/callback.c fe677cb5f5abb02f7a772a62a98c2f516426081df68856e8f2d5f950929b966a
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
F src/ctime.c ff1be3eed7bdd75aaca61ca8dc848f7c9f850ef2fb9cb56f2734e922a098f9c0
F src/ctime.c bd9da3f1ff21b432564a16ef0b154cff03585dc43742842e99c58907c6cb4bef
F src/date.c ebe1dc7c8a347117bb02570f1a931c62dd78f4a2b1b516f4837d45b7d6426957
F src/dbpage.c 8db4c97f630e7d83f884ea75caf1ffd0988c160e9d530194d93721c80821e0f6
F src/dbstat.c 7a4ba8518b6369ef3600c49cf9c918ad979acba610b2aebef1b656d649b96720
F src/delete.c 20c8788451dc737a967c87ea53ad43544d617f5b57d32ccce8bd52a0daf9e89b
F src/expr.c 904f85e5f5fa232488cbb662fcf3306ae203728751468670fb9840a9b1886abc
F src/expr.c 51500461dcd4d0873a938bf188d03076d6712d70ff00a0f0d65621edf4e521c6
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c d617daf66b5515e2b42c1405b2b4984c30ca50fb705ab164271a9bf66c69e331
F src/func.c 94f42cba2cc1c34aeaa441022ba0170ec3fec4bba54db4e0ded085c6dc0fdc51
@@ -450,17 +453,17 @@ F src/hash.c a12580e143f10301ed5166ea4964ae2853d3905a511d4e0c44497245c7ce1f7a
F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4
F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
F src/insert.c 14686083cedc198540b15a79586cdd4be2acf6d5fa97627e355f817ab07e9fee
F src/insert.c b9ff71cc2913d1d57698a1e22bf853261a9a642baf62bdf40ddeb3809adb85b5
F src/legacy.c 134ab3e3fae00a0f67a5187981d6935b24b337bcf0f4b3e5c9fa5763da95bf4e
F src/loadext.c f6e4e416a736369f9e80eba609f0acda97148a8b0453784d670c78d3eed2f302
F src/main.c c1e97e4c6fffff1fb1a6f1c48807386819af78657b4ae4acfd6d6d17353b8277
F src/main.c ffe71c007fc943adfdc638796a80581fb8e2f035bcfa2312b579a2e852d836ec
F src/malloc.c 07295435093ce354c6d9063ac05a2eeae28bd251d2e63c48b3d67c12c76f7e18
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de
F src/mem2.c f1940d9e91948dd6a908fbb9ce3835c36b5d83c3
F src/mem3.c 8768ac94694f31ffaf8b4d0ea5dc08af7010a35a
F src/mem5.c 9bf955937b07f8c32541c8a9991f33ce3173d944
F src/memdb.c 2bcebf99f77270e4b30ee5fad749347a6296db1662b3a196593e5410780aea85
F src/memdb.c e94c478a757c4307fd170fe0a7650ef4cf722c59e5a95a8a7896ffedc1679139
F src/memjournal.c 6f3d36a0a8f72f48f6c3c722f04301ac64f2515435fa42924293e46fc7994661
F src/msvc.h 4942752b6a253116baaa8de75256c51a459a5e81
F src/mutex.c b021263554c8a3995e9d53193b8194b96d1ed28e06c3b532dd7f7d29cf0c7d53
@@ -469,38 +472,40 @@ F src/mutex_noop.c 9d4309c075ba9cc7249e19412d3d62f7f94839c4
F src/mutex_unix.c aaf9ebc3f89df28483c52208497a99a02cc3650011422fc9d4c57e4392f7fe58
F src/mutex_w32.c 7670d770c94bbfe8289bec9d7f1394c5a00a57c37f892aab6b6612d085255235
F src/notify.c 9711a7575036f0d3040ba61bc6e217f13a9888e7
F src/os.c 22d31db3ca5a96a408fbf1ceeaaebcaf64c87024d2ff9fe1cf2ddbec3e75c104
F src/os.c 750d7dca7eff3d76566fc71057e6960316914e3557776e8f50d4314f01090317
F src/os.h 48388821692e87da174ea198bf96b1b2d9d83be5dfc908f673ee21fafbe0d432
F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85
F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586
F src/os_unix.c 928586cbde31890538854f1657463392d63b2d7794f577774573ae53ec283d7b
F src/os_unix.c e853b5922c4b4bc04bd181289bc2b9756f4bb0a3b5861554bc31937abb0e7a7b
F src/os_win.c eb03c6d52f893bcd7fdd4c6006674c13c1b5e49543fec98d605201af2997171c
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c 3e3b659b6b3f40b5a5efef6b8890ced490c397a81d96feb5266fde1f7362e608
F src/pager.h 581698f2177e8bd4008fe4760898ce20b6133d1df22139b9101b5155f900df7a
F src/parse.y 3be4b2b5e33ef7dab3128b765898ee786fc6cd698a7f053f756a68f2f238f0aa
F src/pager.c b99ae56c331ea1129d06c0e6634daa4a7fa544dffe76a4f520febbc9b9afc0a8
F src/pager.h d0fcb55b76087aa8fd605492451edcf03a5aaaa5eac074db8c438ee9185ca832
F src/parse.y a3ab90377e3a58309802ccd866e4df7a9a0eb7d7405ec4c22f05dd352b9c363e
F src/pcache.c 135ef0bc6fb2e3b7178d49ab5c9176254c8a691832c1bceb1156b2fbdd0869bd
F src/pcache.h 072f94d29281cffd99e46c1539849f248c4b56ae7684c1f36626797fee375170
F src/pcache1.c 716975564c15eb6679e97f734cec1bfd6c16ac3d4010f05f1f8e509fc7d19880
F src/pragma.c bea56df3ae0637768c0da4fbbb8f2492f780980d95000034a105ff291bf7ca69
F src/pragma.h bb83728944b42f6d409c77f5838a8edbdb0fe83046c5496ffc9602b40340a324
F src/prepare.c 259f4e7960c47082c9653f3d5f0c294abd68bb9c3aab86de7630700cba1c20fb
F src/pragma.c 5a4145c6cef2710c4fc638768d0a6f48b085394c81c5b4b4baf2068d0ae81995
F src/pragma.h a59d572cbc35d210a610264a20403e209165d581bc087ceb3a75b8dc3c2553f7
F src/prepare.c b086fea6a1952db88beca31fdd621201ee5e4ce3f02905248cc3035a8174aa89
F src/printf.c d3b7844ddeb11fbbdd38dd84d09c9c1ac171d21fb038473c3aa97981201cc660
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
F src/resolve.c 66c73fcb7719b8ff0e841b58338f13604ff3e2b50a723f9b8f383595735262f6
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
F src/select.c f02352ac5cbb6fad3804add825743b924cfb6c742ba2e8666d726828a9985d73
F src/shell.c.in 9b7e7ac1ccdbc045e285e745ff6d21dbec6ce91330f2bdd5db50d5c39f40679e
F src/sqlite.h.in 6283ece8dbc3749d78593415a2373f4e7050afce7d18f87acea3b9a48f6fa576
F src/select.c e51efe5479d1cb4f48defe0b97cdba7391df42a755ba9592b9159510d03cf738
F src/server.c 70421e6acbb2279878606be160b45c7db78933d6ec320317a2e939218496deb9
F src/server.h f46be129ffe407cac9b7018e6d4851b04e685d59b6837c73a1fb69e6aab52e3a
F src/shell.c.in d6a07811aa9f3b10200c15ab8dd4b6b998849a3b0c8b125bfa980329a33c26a6
F src/sqlite.h.in 45150a75c20ad6f9d914cd6e59caf36453206b0f824d514f194b56236f2d63d7
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 83a3c4ce93d650bedfd1aa558cb85a516bd6d094445ee989740827d0d944368d
F src/sqliteInt.h 2ef60a6d9a50e6eef224e9eae71b29a9fb91c301e882d9fbbb77afd50b6cf815
F src/sqliteInt.h 59a8bd112bc0224d2319c8b3b195056d36db1f1e9e993756f934bea663b204ec
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
F src/tclsqlite.c 916a92de77ec5cbe27818ca194d8cf0c58aa7ad5b87527098f6aa5a6068800ce
F src/test1.c 1ab7cbbb6693e08364c1a9241e2aee17f8c4925e4cc52396be77ae6845a05828
F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5
F src/test2.c 824e16d2ff3b57dc3680a5635d049cc889492f95910368ec1ffe2ad44ca45a7f
F src/test3.c b8434949dfb8aff8dfa082c8b592109e77844c2135ed3c492113839b6956255b
F src/test4.c 18ec393bb4d0ad1de729f0b94da7267270f3d8e6
F src/test5.c 328aae2c010c57a9829d255dc099d6899311672d
@@ -514,7 +519,7 @@ F src/test_backup.c bf5da90c9926df0a4b941f2d92825a01bbe090a0
F src/test_bestindex.c 78809f11026f18a93fcfd798d9479cba37e1201c830260bf1edc674b2fa9b857
F src/test_blob.c ae4a0620b478548afb67963095a7417cd06a4ec0a56adb453542203bfdcb31ce
F src/test_btree.c 8b2dc8b8848cf3a4db93f11578f075e82252a274
F src/test_config.c 097c6189803886a1fb26ec37d8bc62b90512cb53ab79a1fb6d35196c1ec42ded
F src/test_config.c 432d0e740c9927a33094d12e7e2723b71071c93d6e53eca5b05d90b4ec0897af
F src/test_delete.c e2fe07646dff6300b48d49b2fee2fe192ed389e834dd635e3b3bac0ce0bf9f8f
F src/test_demovfs.c a0c3bdd45ed044115c2c9f7779e56eafff18741e
F src/test_devsym.c 1960abbb234b97e9b920f07e99503fc04b443f62bbc3c6ff2c2cea2133e3b8a2
@@ -552,17 +557,17 @@ F src/test_windirent.h 90dfbe95442c9762357fe128dc7ae3dc199d006de93eb33ba3972e0a9
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c 5b0c661a85f783d35b9883830736eeb63be4aefc4f6b7d9cd081d48782c041e2
F src/treeview.c e0d62678314abf0e1e6d09ea405f4de3f404a17b4641c2169c304b5edf509320
F src/treeview.c 14d5d1254702ec96876aa52642cb31548612384134970409fae333b25b39d6bb
F src/trigger.c a34539c69433276d37b0da9a89c117726ff2d292c0902895af1f393a983cd3a1
F src/update.c a90a32ffc0100265b0693dbbdbe490756447af181f5ea2c138cce515b08c8795
F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5
F src/util.c d9eb0a6c4aae1b00a7369eadd7ca0bbe946cb4c953b6751aa20d357c2f482157
F src/vacuum.c 762ee9bbf8733d87d8cd06f58d950e881982e416f8c767334a40ffd341b6bff5
F src/vdbe.c 88d8e0797bf49624d056014f34f302a7370cc119915cc0ece8f2b7bf5e7c30fb
F src/vdbe.c 6a454c0f6c43275e2fcc0b2ea85374e034dd21b7d5f8f9fe8994ebf6cefe9b61
F src/vdbe.h 134beb7a12a6213c00eba58febaede33447cc4441bc568a0d9c144b33fc3720a
F src/vdbeInt.h 95f7adfdc5c8f1353321f55a6c5ec00a90877e3b85af5159e393afb41ff54110
F src/vdbeapi.c 29d2baf9c1233131ec467d7bed1b7c8a03c27579048d768c4b04acf427838858
F src/vdbeaux.c 2756ac68ac259c416554100598fc291870063288cd7e1af22847f57b3e130e56
F src/vdbeaux.c 4115729898d68209562d5ed084244d893cc537312d877d51475cccef2f2800b2
F src/vdbeblob.c f5c70f973ea3a9e915d1693278a5f890dc78594300cf4d54e64f2b0917c94191
F src/vdbemem.c 414e28d3a7e2a8bee2bb247de115dcbc68e3cbac284d5862d077002f7a93bce1
F src/vdbesort.c 731a09e5cb9e96b70c394c1b7cf3860fbe84acca7682e178615eb941a3a0ef2f
@@ -589,7 +594,7 @@ F test/alter3.test 4d79934d812eaeacc6f22781a080f8cfe012fdc3
F test/alter4.test b6d7b86860111864f6cddb54af313f5862dda23b
F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc
F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f
F test/analyze.test 3eb35a4af972f98422e5dc0586501b17d103d321
F test/analyze.test b3a9c67d00e1df7588a5b7be9a0292899f94fe8cac1f94a017277474ca2e59df
F test/analyze3.test 8b3ef8ba6d1096b76c40e0925c0fe51e700d2b779cdda40914580de3f9b9d80f
F test/analyze4.test eff2df19b8dd84529966420f29ea52edc6b56213
F test/analyze5.test 765c4e284aa69ca172772aa940946f55629bc8c4
@@ -628,7 +633,7 @@ F test/autoindex4.test 49d3cd791a9baa16fb461d7ea3de80d019a819cf
F test/autoindex5.test 96f084a5e6024ea07cace5888df3223f3ea86990
F test/autovacuum.test 0831cd34e14695d297187f7f6519265e3121c5b0a1720e548e86829e796129e9
F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4
F test/avtrans.test 0252654f4295ddda3b2cce0e894812259e655a85
F test/avtrans.test b7dc25459ecbd86c6fa9c606ee3068f59d81e225118617dcf2bbb6ded2ade89e
F test/backcompat.test 3e64cedda754c778ef6bbe417b6e7a295e662a4d
F test/backup.test dd4a5ff756e3df3931dacb1791db0584d4bad989
F test/backup2.test 1fd1ad8c5b3d2d5b9c0cce4143a4fc610d51ddc6ae16a7a122973d43e6b50bbd
@@ -736,13 +741,13 @@ F test/cse.test 277350a26264495e86b1785f34d2d0c8600e021c
F test/csv01.test 526fc6aefd052badd5a0283f86b4b395c3df76bfe98d96c801f494f5e2c7836c
F test/ctime.test 78749e6c9a5f0010d67985be80788f841e3cd2da18114e2ed6010399a7d807f3
F test/cursorhint.test 7bc346788390475e77a345da2b92270d04d35856
F test/cursorhint2.test 8457e93d97f665f23f97cdbc8477d16e3480331b
F test/cursorhint2.test 0078ae1ded4afcf5eb80d06e3a72b6e1c3f1a646aab26eeb583b0a9ec6f0d56e
F test/date.test 9b73bbeb1b82d9c1f44dec5cf563bf7da58d2373
F test/date2.test 74c234bece1b016e94dd4ef9c8cc7a199a8806c0e2291cab7ba64bace6350b10
F test/dbfuzz.c 73047c920d6210e5912c87cdffd9a1c281d4252e
F test/dbpage.test dbf50a4d361f9e45a979432c727506065113124478a7d2db12074fa655e65d6c
F test/dbstatus.test c15fa97f743dac7ce996814c84b56317e138895ee15ce27f15b608aa6924c90a
F test/dbstatus2.test 35a987036fefbbc696e65b6a3fbd860864cfd54941c52f50c61b55d4778ac66c
F test/dbstatus2.test f5fe0afed3fa45e57cfa70d1147606c20d2ba23feac78e9a172f2fe8ab5b78ef
F test/default.test 0cb49b1c315a0d81c81d775e407f66906a2a604d
F test/delete.test acc38fca8ee4851467705b1c2cfea64cd26667e5
F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa
@@ -771,7 +776,7 @@ F test/e_fts3.test 8cf40550bb088a6aa187c818c00fabe26ef82900a4cd5c66b427ccafe28be
F test/e_insert.test f02f7f17852b2163732c6611d193f84fc67bc641fb4882c77a464076e5eba80e
F test/e_reindex.test 2bebf7b393e519198b7c654407221cf171a439b8
F test/e_resolve.test a61751c368b109db73df0f20fc75fb47e166b1d8
F test/e_select.test 16651bb681e83a1a2875ff4a595ed2b4b4dee375
F test/e_select.test 6fd45fd4a59ec82b6dda7468699dcc0ec1a72538577750b4f90357a62c1d2723
F test/e_select2.test aceb80ab927d46fba5ce7586ebabf23e2bb0604f
F test/e_totalchanges.test b12ee5809d3e63aeb83238dd501a7bca7fd72c10
F test/e_update.test f46c2554d915c9197548681e8d8c33a267e84528
@@ -1002,12 +1007,12 @@ F test/ioerr3.test d3cec5e1a11ad6d27527d0d38573fbff14c71bdd
F test/ioerr4.test f130fe9e71008577b342b8874d52984bd04ede2c
F test/ioerr5.test 2edfa4fb0f896f733071303b42224df8bedd9da4
F test/ioerr6.test a395a6ab144b26a9e3e21059a1ab6a7149cca65b
F test/istrue.test 2d89873d72248b7a0ef7b5457120100cce75a19181bcf4b30e90a3f96a5c5558
F test/join.test 442c462eea85cf065d70a663c626b780a95af6e11585d909bb63b87598afe678
F test/join2.test f1b5ba94ba70a791df2d99e61adcebf62edacad1eca57881c97eb3e2ca718595
F test/istrue.test d6e659764da5ccc03adcdba18fe77d7917ba5e4abd04ef14bd4e4cf43e024b5b
F test/join.test 730e3e8d511289531efca01f8684f98da1e6de51eacf95c5960d0c46e77719e3
F test/join2.test f5ea0fd3b0a441c8e439706339dcd17cec63a896a755c04a30bfd442ecce1190
F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0
F test/join4.test 1a352e4e267114444c29266ce79e941af5885916
F test/join5.test bc98ea4b4e5003f5b1453701ebb8cd7d1c01a550
F test/join5.test c6bd62effc37a152bea735f9ef241b19bb967bd4593dc99b20e2fc55ae707e38
F test/join6.test cfe6503791ceb0cbb509966740286ec423cbf10b
F test/journal1.test c7b768041b7f494471531e17abc2f4f5ebf9e5096984f43ed17c4eb80ba34497
F test/journal2.test 9dac6b4ba0ca79c3b21446bbae993a462c2397c4
@@ -1110,6 +1115,9 @@ F test/numcast.test 5d126f7f581432e86a90d1e35cac625164aec4a1
F test/numindex1.test 20a5450d4b056e48cd5db30e659f13347a099823
F test/offset1.test f06b83657bcf26f9ce805e67450e189e282143b2
F test/openv2.test 0d3040974bf402e19b7df4b783e447289d7ab394
F test/optfuzz-db01.c a0c256905c8ac79f9a5de2f374a3d9f757bef0dca2a238dc7c10cc8a38031834
F test/optfuzz-db01.txt 21f6bdeadc701cf11528276e2a55c70bfcb846ba42df327f979bd9e7b6ce7041
F test/optfuzz.c 50e330304eb1992e15ddd11f3daaad9bcc0d9aaad09cb2bcc77f9515df2e88b1
F test/orderby1.test 4d22a7c75f6a83fc1f188cc7bb5192285fdf2552
F test/orderby2.test bc11009f7cd99d96b1b11e57b199b00633eb5b04
F test/orderby3.test 8619d06a3debdcd80a27c0fdea5c40b468854b99
@@ -1136,7 +1144,7 @@ F test/parser1.test 391b9bf9a229547a129c61ac345ed1a6f5eb1854
F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
F test/permutations.test 8ada8c1dee071e0fc275bc8bc2db7de537d625cad949d2200664b99a0a89eac5
F test/permutations.test 17d9cbfce2e7d0e2007a245cf88c3c48ee9531fd6008034442feeb0f11357132
F test/pragma.test 7c8cfc328a1717a95663cf8edb06c52ddfeaf97bb0aee69ae7457132e8d39e7d
F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
F test/pragma3.test 14c12bc5352b1e100e0b6b44f371053a81ccf8ed
@@ -1214,6 +1222,13 @@ F test/selectE.test a8730ca330fcf40ace158f134f4fe0eb00c7edbf
F test/selectF.test 21c94e6438f76537b72532fa9fd4710cdd455fc3
F test/selectG.test 089f7d3d7e6db91566f00b036cb353107a2cca6220eb1cb264085a836dae8840
F test/server1.test 46803bd3fe8b99b30dbc5ff38ffc756f5c13a118
F test/server2.test 787ba6044b5e9b2c3d60588bc5596054e6a5c3a7dc64bc2fb0e62f6616c142a9
F test/server3.test c3ae4ca7a6e7df870bfcd2450a9815507eaa80b9cdc44ee6c7975d48311505d4
F test/server_common.tcl c491d0f509b94a5cca845d45ca3bb47e464ad3a4bc89641982269112d0f1f3f4
F test/servercrash.test 1cbd2f98cadee2d8d42ed85ad76fbcf48958fedd537c82221838cd9bc6899dae
F test/serverfreelist.test 2e554001145170094a19731a8ce2981d040cf44c947542b35d130e6e31256fca
F test/serverlimit.test 4bc013c0b991956486ddbff6ea3bee78a0d14a3d8091f5ec00e2bd34a7fa9aa7
F test/serverreadonly.test 97040670597948a695b1973537d770417589f1998bcbb3959302aaee3c211250
F test/session.test 78fa2365e93d3663a6e933f86e7afc395adf18be
F test/sessionfuzz-data1.db 1f8d5def831f19b1c74571037f0d53a588ea49a6c4ca2a028fc0c27ef896dbcb
F test/sessionfuzz.c b0fcdcf757451957e17396a3af5171f1fdf9b2babc81da9fa35675df46c4729a
@@ -1229,7 +1244,7 @@ F test/sharedA.test 49d87ec54ab640fbbc3786ee3c01de94aaa482a3a9f834ad3fe92770eb69
F test/sharedB.test 16cc7178e20965d75278f410943109b77b2e645e
F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939
F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
F test/shell1.test 9f8b8da05a79b134e252a5e1d8d411245ad83ac7126c262900b9f42b43108ffd
F test/shell1.test e2f7d375ae80eb16590af23d6c40cd0140b6d1f92642c1b71eb94630a144ee08
F test/shell2.test e242a9912f44f4c23c3d1d802a83e934e84c853b
F test/shell3.test ac8c2b744014c3e9a0e26bfd829ab65f00923dc1a91ffd044863e9423cc91494
F test/shell4.test 89ad573879a745974ff2df20ff97c5d6ffffbd5d
@@ -1306,8 +1321,8 @@ F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30
F test/temptable2.test cd396beb41117a5302fff61767c35fa4270a0d5e
F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637
F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc
F test/tester.tcl 94901a4625d9a2229666dd5c44120ddf7f0fb639470710ef74a4cefc7b039e07
F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5
F test/tester.tcl f6342dac83dbc6ca42bac34a5f65a70df4b18e03e98bcb230efd35e621b671c0
F test/thread001.test b61a29dd87cf669f5f6ac96124a7c97d71b0c80d9012746072055877055cf9ef
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
F test/thread004.test f51dfc3936184aaf73ee85f315224baad272a87f
@@ -1464,7 +1479,7 @@ F test/tokenize.test ce430a7aed48fc98301611429595883fdfcab5d7
F test/tpch01.test 04adbf8d8300fa60a222f28d901abd76e7be6dd4
F test/trace.test a659a9862957f4789e37a92b3bf6d2caf5c86b02cdeefc41e850ae53acf6992a
F test/trace2.test f5cb67ad3bc09e0c58e8cca78dfd0b5639259983
F test/trace3.test b41076f536876b6edf661c0cf8f9216771316efd0fe78905576cc23ce269e7f2
F test/trace3.test 1dff966888773ff1bfea01c080caf15417892b3f998408fe920c4791f7337144
F test/trans.test 6e1b4c6a42dba31bd65f8fa5e61a2708e08ddde6
F test/trans2.test 62bd045bfc7a1c14c5ba83ba64d21ade31583f76
F test/trans3.test 91a100e5412b488e22a655fe423a14c26403ab94
@@ -1611,8 +1626,8 @@ F test/wordcount.c cb589cec469a1d90add05b1f8cee75c7210338d87a5afd65260ed5c0f4bbf
F test/writecrash.test f1da7f7adfe8d7f09ea79b42e5ca6dcc41102f27f8e334ad71539501ddd910cc
F test/zeroblob.test 3857870fe681b8185654414a9bccfde80b62a0fa
F test/zerodamage.test 9c41628db7e8d9e8a0181e59ea5f189df311a9f6ce99cc376dc461f66db6f8dc
F test/zipfile.test a9806a86e5a153d897f320de624a83a9e9774515eb79ad2419496187b1acc7d7
F test/zipfile2.test 67d5f08a202796d4b7a71dfa4b8dcb74aa7a9d1f42c5f17bedff9855c1ba7aa5
F test/zipfile.test 2a923f6ead6a0f9b61d936881f3ee2aeaabe15fc65c196456f58ea9b4b450f9b
F test/zipfile2.test fc2f08d5ec19c18c83289fbed32e378dc5116519972166e57a244da7bf2e5805
F test/zipfilefault.test 44d4d7a7f7cca7521d569d7f71026b241d65a6b1757aa409c1a168827edbbc2c
F tool/GetFile.cs a15e08acb5dd7539b75ba23501581d7c2b462cb5
F tool/GetTclKit.bat 8995df40c4209808b31f24de0b58f90930239a234f7591e3675d45bfbb990c5d
@@ -1640,18 +1655,18 @@ F tool/max-limits.c cbb635fbb37ae4d05f240bfb5b5270bb63c54439
F tool/mkautoconfamal.sh 422fc365358a2e92876ffc62971a0ff28ed472fc8bcf9de0df921c736fdeca5e
F tool/mkccode.tcl 86463e68ce9c15d3041610fedd285ce32a5cf7a58fc88b3202b8b76837650dbe x
F tool/mkctimec.tcl dd183b73ae1c28249669741c250525f0407e579a70482371668fd5f130d9feb3
F tool/mkkeywordhash.c 2e852ac0dfdc5af18886dc1ce7e9676d11714ae3df0a282dc7d90b3a0fe2033c
F tool/mkkeywordhash.c 969c50301da61d73c4c6d0661c1d1abc6c25ba35f1212832d0b12965f90505ab
F tool/mkmsvcmin.tcl 8baf26690b80d861d0ac341b29880eec6ade39e4f11fe690271ded9cb90563a3
F tool/mkopcodec.tcl d1b6362bd3aa80d5520d4d6f3765badf01f6c43c
F tool/mkopcodeh.tcl 4ee2a30ccbd900dc4d5cdb61bdab87cd2166cd2affcc78c9cc0b8d22a65b2eee
F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa
F tool/mkpragmatab.tcl 2144bc8550a6471a029db262a132d2df4b9e0db61b90398bf64f5b7b3f8d92cd
F tool/mkpragmatab.tcl c249dee507fc499d43072b14eb8b0373204dca952e770b9b9924c211bca0169a
F tool/mkshellc.tcl 1f45770aea226ac093a9c72f718efbb88a2a2833409ec2e1c4cecae4202626f5
F tool/mksourceid.c d458f9004c837bee87a6382228ac20d3eae3c49ea3b0a5aace936f8b60748d3b
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
F tool/mksqlite3c-noext.tcl fef88397668ae83166735c41af99d79f56afaabb
F tool/mksqlite3c.tcl a03cee30de81a2e67b93e5c659f24113a003677c557daeb008205c8e6d4345d6
F tool/mksqlite3h.tcl f92f994d9709aeb9e2b6e6f9fc8b069d2f55202c8e23f453edc44390a25982dc
F tool/mksqlite3c.tcl caf7dec7adbdfd0d76893bafa3e09a30401cc649b4c7bfcc4234eadede944469
F tool/mksqlite3h.tcl 080873e3856eceb9d289a08a00c4b30f875ea3feadcbece796bd509b1532792c
F tool/mksqlite3internalh.tcl eb994013e833359137eb53a55acdad0b5ae1049b
F tool/mkvsix.tcl b9e0777a213c23156b6542842c238479e496ebf5
F tool/offsets.c fe4262fdfa378e8f5499a42136d17bf3b98f6091
@@ -1662,6 +1677,7 @@ F tool/replace.tcl 60f91e8dd06ab81f74d213ecbd9c9945f32ac048
F tool/restore_jrnl.tcl 6957a34f8f1f0f8285e07536225ec3b292a9024a
F tool/rollback-test.c 9fc98427d1e23e84429d7e6d07d9094fbdec65a5
F tool/run-speed-test.sh f95d19fd669b68c4c38b6b475242841d47c66076
F tool/se_perf_test.tcl e2a5081f9378f188a431c4a85c5f3f9e99719027df5a6faa405c981b267ad8fd
F tool/showdb.c e6bc9dba233bf1b57ca0a525a2bba762db4e223de84990739db3f09c46151b1e
F tool/showjournal.c 5bad7ae8784a43d2b270d953060423b8bd480818
F tool/showlocks.c 9920bcc64f58378ff1118caead34147201f48c68
@@ -1685,6 +1701,7 @@ F tool/srcck1.c 371de5363b70154012955544f86fdee8f6e5326f
F tool/stack_usage.tcl f8e71b92cdb099a147dad572375595eae55eca43
F tool/symbols-mingw.sh 4dbcea7e74768305384c9fd2ed2b41bbf9f0414d
F tool/symbols.sh c5a617b8c61a0926747a56c65f5671ef8ac0e148
F tool/tserver.c ac67b8fe175850b780c5bd1888c2bb2f0f5e24096f040db7fc1a2af7a4908f2b
F tool/varint.c 5d94cb5003db9dbbcbcc5df08d66f16071aee003
F tool/vdbe-compress.tcl 5926c71f9c12d2ab73ef35c29376e756eb68361c
F tool/vdbe_profile.tcl 246d0da094856d72d2c12efec03250d71639d19f
@@ -1712,10 +1729,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 48a06eb02b42a021b835ff9766535805723259b9701b87fb17fa488b133cb53a
R f348889711b0641611a4541c43f0ef77
T *branch * fsDirFix
T *sym-fsDirFix *
T -sym-trunk *
U mistachkin
Z e35ad5809ec8c5cee9fda8092826113a
P 337a0b67e30f1030fdc59f712e5914f4801b0e9e4ae19a1e82c10b73eb3f4773
R 5045a1c8302fea1a74fa92a9ce0972dd
U dan
Z d3c495daa23a1fc63d551424dd8817b7
+1 -1
View File
@@ -1 +1 @@
48641010c24bf37f13d68c4d19c5c3e9b41404af12a4947705cc53a31db04178
754ad35cd26da361e2ed736b0e400497714a0db9b7fd05fd24e7803b6f478263
+1 -1
View File
@@ -1015,7 +1015,7 @@ static void analyzeOneTable(
/* Do not gather statistics on views or virtual tables */
return;
}
if( sqlite3_strlike("sqlite_%", pTab->zName, 0)==0 ){
if( sqlite3_strlike("sqlite\\_%", pTab->zName, '\\')==0 ){
/* Do not gather statistics on system tables */
return;
}
+388 -30
View File
@@ -266,6 +266,15 @@ static int hasReadConflicts(Btree *pBtree, Pgno iRoot){
}
#endif /* #ifdef SQLITE_DEBUG */
#ifdef SQLITE_SERVER_EDITION
/*
** Return true if the b-tree uses free-list format 2. Or false otherwise.
*/
static int btreeFreelistFormat2(BtShared *pBt){
return (pBt->pPage1->aData[18] > 2);
}
#endif
/*
** Query to see if Btree handle p may obtain a lock of type eLock
** (READ_LOCK or WRITE_LOCK) on the table with root-page iTab. Return
@@ -2231,7 +2240,8 @@ static int btreeInvokeBusyHandler(void *pArg){
BtShared *pBt = (BtShared*)pArg;
assert( pBt->db );
assert( sqlite3_mutex_held(pBt->db->mutex) );
return sqlite3InvokeBusyHandler(&pBt->db->busyHandler);
return sqlite3InvokeBusyHandler(&pBt->db->busyHandler,
sqlite3PagerFile(pBt->pPager));
}
/*
@@ -2409,7 +2419,7 @@ int sqlite3BtreeOpen(
}
pBt->openFlags = (u8)flags;
pBt->db = db;
sqlite3PagerSetBusyhandler(pBt->pPager, btreeInvokeBusyHandler, pBt);
sqlite3PagerSetBusyHandler(pBt->pPager, btreeInvokeBusyHandler, pBt);
p->pBt = pBt;
pBt->pCursor = 0;
@@ -2989,7 +2999,7 @@ static int lockBtree(BtShared *pBt){
assert( sqlite3_mutex_held(pBt->mutex) );
assert( pBt->pPage1==0 );
rc = sqlite3PagerSharedLock(pBt->pPager);
rc = sqlite3PagerSharedLock(pBt->pPager, pBt->db->readonlyTrans);
if( rc!=SQLITE_OK ) return rc;
rc = btreeGetPage(pBt, 1, &pPage1, 0);
if( rc!=SQLITE_OK ) return rc;
@@ -3006,6 +3016,15 @@ static int lockBtree(BtShared *pBt){
u32 pageSize;
u32 usableSize;
u8 *page1 = pPage1->aData;
u8 i18 = page1[18];
u8 i19 = page1[19];
#ifdef SQLITE_SERVER_EDITION
if( i18==i19 && i18>2 ){
i18 -= 2;
i19 -= 2;
}
#endif
rc = SQLITE_NOTADB;
/* EVIDENCE-OF: R-43737-39999 Every valid SQLite database file begins
** with the following 16 bytes (in hex): 53 51 4c 69 74 65 20 66 6f 72 6d
@@ -3015,17 +3034,17 @@ static int lockBtree(BtShared *pBt){
}
#ifdef SQLITE_OMIT_WAL
if( page1[18]>1 ){
if( i18>1 ){
pBt->btsFlags |= BTS_READ_ONLY;
}
if( page1[19]>1 ){
if( i19>1 ){
goto page1_init_failed;
}
#else
if( page1[18]>2 ){
if( i18>2 ){
pBt->btsFlags |= BTS_READ_ONLY;
}
if( page1[19]>2 ){
if( i19>2 ){
goto page1_init_failed;
}
@@ -3037,7 +3056,7 @@ static int lockBtree(BtShared *pBt){
** may not be the latest version - there may be a newer one in the log
** file.
*/
if( page1[19]==2 && (pBt->btsFlags & BTS_NO_WAL)==0 ){
if( i19==2 && (pBt->btsFlags & BTS_NO_WAL)==0 ){
int isOpen = 0;
rc = sqlite3PagerOpenWal(pBt->pPager, &isOpen);
if( rc!=SQLITE_OK ){
@@ -3372,6 +3391,7 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
}
}while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE &&
btreeInvokeBusyHandler(pBt) );
sqlite3PagerResetLockTimeout(pBt->pPager);
if( rc==SQLITE_OK ){
if( p->inTrans==TRANS_NONE ){
@@ -5719,6 +5739,255 @@ int sqlite3BtreePrevious(BtCursor *pCur, int flags){
return SQLITE_OK;
}
#ifdef SQLITE_SERVER_EDITION
#define SERVER_DEFAULT_FREELISTS 16
#define SERVER_DEFAULT_FREELIST_SIZE 128
/*
** Allocate the free-node and the first SERVER_DEFAULT_FREELISTS
** trunk pages.
*/
static int allocateServerFreenode(BtShared *pBt){
int rc;
MemPage *pPage1 = pBt->pPage1;
rc = sqlite3PagerWrite(pPage1->pDbPage);
if( rc==SQLITE_OK ){
Pgno pgnoNode = (++pBt->nPage);
MemPage *pNode = 0;
int i;
put4byte(&pPage1->aData[32], pgnoNode);
rc = btreeGetUnusedPage(pBt, pgnoNode, &pNode, PAGER_GET_NOCONTENT);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pNode->pDbPage);
}
if( rc==SQLITE_OK ){
put4byte(&pNode->aData[0], 0);
put4byte(&pNode->aData[4], SERVER_DEFAULT_FREELISTS);
}
for(i=0; rc==SQLITE_OK && i<SERVER_DEFAULT_FREELISTS; i++){
MemPage *pTrunk = 0;
Pgno pgnoTrunk;
if( ++pBt->nPage==PENDING_BYTE_PAGE(pBt) ) pBt->nPage++;
pgnoTrunk = pBt->nPage;
rc = btreeGetUnusedPage(pBt, pgnoTrunk, &pTrunk, PAGER_GET_NOCONTENT);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pTrunk->pDbPage);
}
if( rc==SQLITE_OK ){
memset(pTrunk->aData, 0, 8);
put4byte(&pNode->aData[8+i*4], pgnoTrunk);
}
releasePage(pTrunk);
}
releasePage(pNode);
}
return rc;
}
/*
** Return a reference to the first trunk page in one of the database free-lists.
** Allocate the database free-lists if required.
*/
static int findServerTrunk(BtShared *pBt, int bAlloc, MemPage **ppTrunk){
MemPage *pPage1 = pBt->pPage1;
MemPage *pNode = 0; /* The node page */
MemPage *pTrunk = 0; /* The returned page */
Pgno iNode; /* Page number of node page */
int rc = SQLITE_OK;
/* If the node page and free-list trunks have not yet been allocated, allocate
** them now. */
pPage1 = pBt->pPage1;
iNode = get4byte(&pPage1->aData[32]);
if( iNode==0 ){
rc = allocateServerFreenode(pBt);
iNode = get4byte(&pPage1->aData[32]);
}
/* Grab the node page */
if( rc==SQLITE_OK ){
rc = btreeGetUnusedPage(pBt, iNode, &pNode, 0);
}
if( rc==SQLITE_OK ){
int nList; /* Number of free-lists in this db */
int i;
/* Try to lock a free-list trunk. If bAlloc is true, it has to be a
** free-list trunk with at least one entry in the free-list. */
nList = (int)get4byte(&pNode->aData[4]);
for(i=0; i<nList; i++){
Pgno iTrunk = get4byte(&pNode->aData[8+i*4]);
if( SQLITE_OK==sqlite3PagerPagelock(pBt->pPager, iTrunk, 1) ){
rc = btreeGetUnusedPage(pBt, iTrunk, &pTrunk, 0);
if( rc==SQLITE_OK && bAlloc ){
if( !get4byte(&pTrunk->aData[0]) && !get4byte(&pTrunk->aData[4]) ){
releasePage(pTrunk);
pTrunk = 0;
}
}
if( rc!=SQLITE_OK || pTrunk ) break;
}
}
/* No free pages in any free-list. Or perhaps we were locked out. In
** either case, try to allocate more from the end of the file now. */
if( i==nList ){
assert( rc==SQLITE_OK && pTrunk==0 );
rc = sqlite3PagerWrite(pPage1->pDbPage);
for(i=0; rc==SQLITE_OK && i<nList; i++){
/* Add some free pages to each free-list. No server-locks are required
** to do this as we have a write-lock on page 1 - guaranteeing
** exclusive access to the db file. */
MemPage *pT = 0;
Pgno iTrunk = get4byte(&pNode->aData[8+i*4]);
rc = btreeGetUnusedPage(pBt, iTrunk, &pT, 0);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pT->pDbPage);
}
if( rc==SQLITE_OK ){
int iPg = get4byte(&pT->aData[4]);
for(/*no-op*/; iPg<SERVER_DEFAULT_FREELIST_SIZE; iPg++){
if( ++pBt->nPage==PENDING_BYTE_PAGE(pBt) ) pBt->nPage++;
put4byte(&pT->aData[8+iPg*4], pBt->nPage);
}
put4byte(&pT->aData[4], iPg);
if( pTrunk==0 ){
pTrunk = pT;
pT = 0;
}
}
releasePage(pT);
}
if( rc==SQLITE_OK ){
MemPage *pLast = 0;
rc = btreeGetUnusedPage(pBt, pBt->nPage, &pLast, 0);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pLast->pDbPage);
releasePage(pLast);
put4byte(28 + (u8*)pPage1->aData, pBt->nPage);
}
}
}
}
releasePage(pNode);
if( rc==SQLITE_OK ){
assert( pTrunk );
rc = sqlite3PagerWrite(pTrunk->pDbPage);
}
if( rc!=SQLITE_OK ){
releasePage(pTrunk);
pTrunk = 0;
}
*ppTrunk = pTrunk;
return rc;
}
static int allocateServerPage(
BtShared *pBt, /* The btree */
MemPage **ppPage, /* Store pointer to the allocated page here */
Pgno *pPgno, /* Store the page number here */
Pgno nearby, /* Search for a page near this one */
u8 eMode /* BTALLOC_EXACT, BTALLOC_LT, or BTALLOC_ANY */
){
int rc; /* Return code */
MemPage *pTrunk = 0; /* The node page */
Pgno pgnoNew = 0;
#ifdef SQLITE_DEBUG
int nRef = sqlite3PagerRefcount(pBt->pPager);
#endif
assert( eMode==BTALLOC_ANY );
assert( sqlite3_mutex_held(pBt->mutex) );
*ppPage = 0;
rc = findServerTrunk(pBt, 1, &pTrunk);
if( rc==SQLITE_OK ){
int nFree; /* Number of free pages on this trunk page */
nFree = (int)get4byte(&pTrunk->aData[4]);
if( nFree==0 ){
pgnoNew = get4byte(&pTrunk->aData[0]);
assert( pgnoNew );
}else{
nFree--;
pgnoNew = get4byte(&pTrunk->aData[8+4*nFree]);
put4byte(&pTrunk->aData[4], (u32)nFree);
releasePage(pTrunk);
pTrunk = 0;
}
}
if( rc==SQLITE_OK ){
MemPage *pNew = 0;
int flags = pTrunk ? 0 : PAGER_GET_NOCONTENT;
rc = btreeGetUnusedPage(pBt, pgnoNew, &pNew, flags);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pNew->pDbPage);
if( rc!=SQLITE_OK ){
releasePage(pNew);
pNew = 0;
}
}
if( rc==SQLITE_OK && pTrunk ){
memcpy(pTrunk->aData, pNew->aData, pBt->usableSize);
}
*ppPage = pNew;
*pPgno = pgnoNew;
}
releasePage(pTrunk);
assert( (rc==SQLITE_OK)==(*ppPage!=0) );
assert( sqlite3PagerRefcount(pBt->pPager)==(nRef+(*ppPage!=0)) );
return rc;
}
static int freeServerPage2(BtShared *pBt, MemPage *pPage, Pgno iPage){
int rc; /* Return code */
MemPage *pTrunk = 0; /* The node page */
#ifdef SQLITE_DEBUG
int nRef = sqlite3PagerRefcount(pBt->pPager);
#endif
assert( sqlite3_mutex_held(pBt->mutex) );
rc = findServerTrunk(pBt, 0, &pTrunk);
if( rc==SQLITE_OK ){
int nFree; /* Number of free pages on this trunk page */
nFree = (int)get4byte(&pTrunk->aData[4]);
if( nFree>=((pBt->usableSize / 4) - 2) ){
if( pPage==0 ){
rc = btreeGetUnusedPage(pBt, iPage, &pPage, 0);
}else{
sqlite3PagerRef(pPage->pDbPage);
}
rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc==SQLITE_OK ){
memcpy(pPage->aData, pTrunk->aData, pBt->usableSize);
put4byte(&pTrunk->aData[0], iPage);
put4byte(&pTrunk->aData[4], 0);
}
releasePage(pPage);
}else{
put4byte(&pTrunk->aData[8+nFree*4], iPage);
put4byte(&pTrunk->aData[4], (u32)nFree+1);
}
releasePage(pTrunk);
}
assert( nRef==sqlite3PagerRefcount(pBt->pPager) );
return rc;
}
#else
# define allocateServerPage(v, w, x, y, z) SQLITE_OK
# define freeServerPage2(x, y, z) SQLITE_OK
#endif /* SQLITE_SERVER_EDITION */
/*
** Allocate a new page from the database file.
**
@@ -5756,6 +6025,10 @@ static int allocateBtreePage(
MemPage *pPrevTrunk = 0;
Pgno mxPage; /* Total size of the database file */
if( btreeFreelistFormat2(pBt) ){
return allocateServerPage(pBt, ppPage, pPgno, nearby, eMode);
}
assert( sqlite3_mutex_held(pBt->mutex) );
assert( eMode==BTALLOC_ANY || (nearby>0 && IfNotOmitAV(pBt->autoVacuum)) );
pPage1 = pBt->pPage1;
@@ -6083,12 +6356,6 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
pPage = btreePageLookup(pBt, iPage);
}
/* Increment the free page count on pPage1 */
rc = sqlite3PagerWrite(pPage1->pDbPage);
if( rc ) goto freepage_out;
nFree = get4byte(&pPage1->aData[36]);
put4byte(&pPage1->aData[36], nFree+1);
if( pBt->btsFlags & BTS_SECURE_DELETE ){
/* If the secure_delete option is enabled, then
** always fully overwrite deleted information with zeros.
@@ -6100,6 +6367,17 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
}
memset(pPage->aData, 0, pPage->pBt->pageSize);
}
if( btreeFreelistFormat2(pBt) ){
rc = freeServerPage2(pBt, pPage, iPage);
goto freepage_out;
}
/* Increment the free page count on pPage1 */
rc = sqlite3PagerWrite(pPage1->pDbPage);
if( rc ) goto freepage_out;
nFree = get4byte(&pPage1->aData[36]);
put4byte(&pPage1->aData[36], nFree+1);
/* If the database supports auto-vacuum, write an entry in the pointer-map
** to indicate that the page is free.
@@ -9578,6 +9856,49 @@ end_of_check:
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
#ifndef SQLITE_OMIT_INTEGRITY_CHECK
#if !defined(SQLITE_OMIT_INTEGRITY_CHECK) && defined(SQLITE_SERVER_EDITION)
static void checkServerList(IntegrityCk *pCheck){
u32 pgnoNode = get4byte(&pCheck->pBt->pPage1->aData[32]);
if( pgnoNode ){
DbPage *pNode = 0;
u8 *aNodeData;
u32 nList; /* Number of free-lists */
int i;
checkRef(pCheck, pgnoNode);
if( sqlite3PagerGet(pCheck->pPager, (Pgno)pgnoNode, &pNode, 0) ){
checkAppendMsg(pCheck, "failed to get node page %d", pgnoNode);
return;
}
aNodeData = sqlite3PagerGetData(pNode);
nList = get4byte(&aNodeData[4]);
for(i=0; i<nList; i++){
u32 pgnoTrunk = get4byte(&aNodeData[8+4*i]);
while( pgnoTrunk ){
DbPage *pTrunk = 0;
checkRef(pCheck, pgnoTrunk);
if( sqlite3PagerGet(pCheck->pPager, (Pgno)pgnoTrunk, &pTrunk, 0) ){
checkAppendMsg(pCheck, "failed to get page %d", pgnoTrunk);
pgnoTrunk = 0;
}else{
u8 *aTrunkData = sqlite3PagerGetData(pTrunk);
int nLeaf = (int)get4byte(&aTrunkData[4]);
int iLeaf;
for(iLeaf=0; iLeaf<nLeaf; iLeaf++){
u32 pgnoLeaf = get4byte(&aTrunkData[8+iLeaf*4]);
checkRef(pCheck, pgnoLeaf);
}
pgnoTrunk = get4byte(&aTrunkData[0]);
sqlite3PagerUnref(pTrunk);
}
}
}
sqlite3PagerUnref(pNode);
}
}
#endif
/*
** This routine does a complete check of the given BTree file. aRoot[] is
** an array of pages numbers were each page number is the root page of
@@ -9643,8 +9964,15 @@ char *sqlite3BtreeIntegrityCheck(
/* Check the integrity of the freelist
*/
sCheck.zPfx = "Main freelist: ";
checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]),
get4byte(&pBt->pPage1->aData[36]));
#ifdef SQLITE_SERVER_EDITION
if( btreeFreelistFormat2(pBt) ){
checkServerList(&sCheck);
}else
#endif
{
checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]),
get4byte(&pBt->pPage1->aData[36]));
}
sCheck.zPfx = 0;
/* Check all the tables.
@@ -9913,6 +10241,37 @@ void sqlite3BtreeIncrblobCursor(BtCursor *pCur){
}
#endif
int btreeSetVersion(Btree *pBtree, int iVersion, int iFreelistFmt){
BtShared *pBt = pBtree->pBt;
int rc = sqlite3BtreeBeginTrans(pBtree, 0);
if( rc==SQLITE_OK ){
u8 iVal;
u8 *aData = pBt->pPage1->aData;
assert( (iVersion==0 && (iFreelistFmt==1 || iFreelistFmt==2))
|| (iFreelistFmt==0 && (iVersion==1 || iVersion==2))
);
if( iVersion==0 ){
iVal = ((aData[18] & 0x01) ? 1 : 2) + (u8)(iFreelistFmt==2 ? 2 : 0);
}else{
iVal = (u8)iVersion + (u8)(aData[18]>2 ? 2 : 0);
}
if( aData[18]!=iVal || aData[19]!=iVal ){
rc = sqlite3BtreeBeginTrans(pBtree, 2);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
if( rc==SQLITE_OK ){
aData[18] = iVal;
aData[19] = iVal;
}
}
}
}
return rc;
}
/*
** Set both the "read version" (single byte at byte offset 18) and
** "write version" (single byte at byte offset 19) fields in the database
@@ -9929,23 +10288,22 @@ int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){
*/
pBt->btsFlags &= ~BTS_NO_WAL;
if( iVersion==1 ) pBt->btsFlags |= BTS_NO_WAL;
rc = btreeSetVersion(pBtree, iVersion, 0);
pBt->btsFlags &= ~BTS_NO_WAL;
return rc;
}
rc = sqlite3BtreeBeginTrans(pBtree, 0);
if( rc==SQLITE_OK ){
u8 *aData = pBt->pPage1->aData;
if( aData[18]!=(u8)iVersion || aData[19]!=(u8)iVersion ){
rc = sqlite3BtreeBeginTrans(pBtree, 2);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
if( rc==SQLITE_OK ){
aData[18] = (u8)iVersion;
aData[19] = (u8)iVersion;
}
}
int sqlite3BtreeFreelistFormat(Btree *p, int eParam, int *peFmt){
int rc = SQLITE_OK;
sqlite3BtreeEnter(p);
if( eParam ){
u8 *aData = p->pBt->pPage1->aData;
if( 0==get4byte(&aData[32]) ){
rc = btreeSetVersion(p, 0, eParam);
}
}
pBt->btsFlags &= ~BTS_NO_WAL;
*peFmt = (btreeFreelistFormat2(p->pBt) ? 2 : 1);
sqlite3BtreeLeave(p);
return rc;
}
+3
View File
@@ -371,5 +371,8 @@ void sqlite3BtreeCursorList(Btree*);
# define sqlite3SchemaMutexHeld(X,Y,Z) 1
#endif
#ifdef SQLITE_SERVER_EDITION
int sqlite3BtreeFreelistFormat(Btree *p, int eParam, int *peFmt);
#endif
#endif /* SQLITE_BTREE_H */
+6 -4
View File
@@ -1870,8 +1870,6 @@ void sqlite3EndTable(
p = pParse->pNewTable;
if( p==0 ) return;
assert( !db->init.busy || !pSelect );
/* If the db->init.busy is 1 it means we are reading the SQL off the
** "sqlite_master" or "sqlite_temp_master" table on the disk.
** So do not write to the disk again. Extract the root page number
@@ -1882,6 +1880,10 @@ void sqlite3EndTable(
** table itself. So mark it read-only.
*/
if( db->init.busy ){
if( pSelect ){
sqlite3ErrorMsg(pParse, "");
return;
}
p->tnum = db->init.newTnum;
if( p->tnum==1 ) p->tabFlags |= TF_Readonly;
}
@@ -3976,13 +3978,13 @@ void sqlite3BeginTransaction(Parse *pParse, int type){
}
v = sqlite3GetVdbe(pParse);
if( !v ) return;
if( type!=TK_DEFERRED ){
if( type!=TK_DEFERRED && type!=TK_READONLY ){
for(i=0; i<db->nDb; i++){
sqlite3VdbeAddOp2(v, OP_Transaction, i, (type==TK_EXCLUSIVE)+1);
sqlite3VdbeUsesBtree(v, i);
}
}
sqlite3VdbeAddOp0(v, OP_AutoCommit);
sqlite3VdbeAddOp3(v, OP_AutoCommit, 0, 0, type);
}
/*
+1 -1
View File
@@ -188,7 +188,7 @@ static const char * const sqlite3azCompileOpt[] = {
"ENABLE_BATCH_ATOMIC_WRITE",
#endif
#if SQLITE_ENABLE_CEROD
"ENABLE_CEROD",
"ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD),
#endif
#if SQLITE_ENABLE_COLUMN_METADATA
"ENABLE_COLUMN_METADATA",
+74
View File
@@ -5001,6 +5001,80 @@ int sqlite3ExprImpliesExpr(Parse *pParse, Expr *pE1, Expr *pE2, int iTab){
return 0;
}
/*
** This is the Expr node callback for sqlite3ExprImpliesNotNullRow().
** If the expression node requires that the table at pWalker->iCur
** have a non-NULL column, then set pWalker->eCode to 1 and abort.
*/
static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
/* This routine is only called for WHERE clause expressions and so it
** cannot have any TK_AGG_COLUMN entries because those are only found
** in HAVING clauses. We can get a TK_AGG_FUNCTION in a WHERE clause,
** but that is an illegal construct and the query will be rejected at
** a later stage of processing, so the TK_AGG_FUNCTION case does not
** need to be considered here. */
assert( pExpr->op!=TK_AGG_COLUMN );
testcase( pExpr->op==TK_AGG_FUNCTION );
if( ExprHasProperty(pExpr, EP_FromJoin) ) return WRC_Prune;
switch( pExpr->op ){
case TK_ISNULL:
case TK_IS:
case TK_OR:
case TK_CASE:
case TK_IN:
case TK_FUNCTION:
testcase( pExpr->op==TK_ISNULL );
testcase( pExpr->op==TK_IS );
testcase( pExpr->op==TK_OR );
testcase( pExpr->op==TK_CASE );
testcase( pExpr->op==TK_IN );
testcase( pExpr->op==TK_FUNCTION );
return WRC_Prune;
case TK_COLUMN:
if( pWalker->u.iCur==pExpr->iTable ){
pWalker->eCode = 1;
return WRC_Abort;
}
return WRC_Prune;
default:
return WRC_Continue;
}
}
/*
** Return true (non-zero) if expression p can only be true if at least
** one column of table iTab is non-null. In other words, return true
** if expression p will always be NULL or false if every column of iTab
** is NULL.
**
** False negatives are acceptable. In other words, it is ok to return
** zero even if expression p will never be true of every column of iTab
** is NULL. A false negative is merely a missed optimization opportunity.
**
** False positives are not allowed, however. A false positive may result
** in an incorrect answer.
**
** Terms of p that are marked with EP_FromJoin (and hence that come from
** the ON or USING clauses of LEFT JOINS) are excluded from the analysis.
**
** This routine is used to check if a LEFT JOIN can be converted into
** an ordinary JOIN. The p argument is the WHERE clause. If the WHERE
** clause requires that some column of the right table of the LEFT JOIN
** be non-NULL, then the LEFT JOIN can be safely converted into an
** ordinary join.
*/
int sqlite3ExprImpliesNonNullRow(Expr *p, int iTab){
Walker w;
w.xExprCallback = impliesNotNullRow;
w.xSelectCallback = 0;
w.xSelectCallback2 = 0;
w.eCode = 0;
w.u.iCur = iTab;
sqlite3WalkExpr(&w, p);
return w.eCode;
}
/*
** An instance of the following structure is used by the tree walker
** to determine if an expression can be evaluated by reference to the
+21 -13
View File
@@ -210,11 +210,12 @@ static int readsTable(Parse *p, int iDb, Table *pTab){
** first use of table pTab. On 2nd and subsequent uses, the original
** AutoincInfo structure is used.
**
** Three memory locations are allocated:
** Four consecutive registers are allocated:
**
** (1) Register to hold the name of the pTab table.
** (2) Register to hold the maximum ROWID of pTab.
** (3) Register to hold the rowid in sqlite_sequence of pTab
** (1) The name of the pTab table.
** (2) The maximum ROWID of pTab.
** (3) The rowid in sqlite_sequence of pTab
** (4) The original value of the max ROWID in pTab, or NULL if none
**
** The 2nd register is the one that is returned. That is all the
** insert routine needs to know about.
@@ -242,7 +243,7 @@ static int autoIncBegin(
pInfo->iDb = iDb;
pToplevel->nMem++; /* Register to hold name of table */
pInfo->regCtr = ++pToplevel->nMem; /* Max rowid register */
pToplevel->nMem++; /* Rowid in sqlite_sequence */
pToplevel->nMem +=2; /* Rowid in sqlite_sequence + orig max val */
}
memId = pInfo->regCtr;
}
@@ -270,15 +271,17 @@ void sqlite3AutoincrementBegin(Parse *pParse){
static const int iLn = VDBE_OFFSET_LINENO(2);
static const VdbeOpList autoInc[] = {
/* 0 */ {OP_Null, 0, 0, 0},
/* 1 */ {OP_Rewind, 0, 9, 0},
/* 1 */ {OP_Rewind, 0, 10, 0},
/* 2 */ {OP_Column, 0, 0, 0},
/* 3 */ {OP_Ne, 0, 7, 0},
/* 3 */ {OP_Ne, 0, 9, 0},
/* 4 */ {OP_Rowid, 0, 0, 0},
/* 5 */ {OP_Column, 0, 1, 0},
/* 6 */ {OP_Goto, 0, 9, 0},
/* 7 */ {OP_Next, 0, 2, 0},
/* 8 */ {OP_Integer, 0, 0, 0},
/* 9 */ {OP_Close, 0, 0, 0}
/* 6 */ {OP_AddImm, 0, 0, 0},
/* 7 */ {OP_Copy, 0, 0, 0},
/* 8 */ {OP_Goto, 0, 11, 0},
/* 9 */ {OP_Next, 0, 2, 0},
/* 10 */ {OP_Integer, 0, 0, 0},
/* 11 */ {OP_Close, 0, 0, 0}
};
VdbeOp *aOp;
pDb = &db->aDb[p->iDb];
@@ -289,14 +292,17 @@ void sqlite3AutoincrementBegin(Parse *pParse){
aOp = sqlite3VdbeAddOpList(v, ArraySize(autoInc), autoInc, iLn);
if( aOp==0 ) break;
aOp[0].p2 = memId;
aOp[0].p3 = memId+1;
aOp[0].p3 = memId+2;
aOp[2].p3 = memId;
aOp[3].p1 = memId-1;
aOp[3].p3 = memId;
aOp[3].p5 = SQLITE_JUMPIFNULL;
aOp[4].p2 = memId+1;
aOp[5].p3 = memId;
aOp[8].p2 = memId;
aOp[6].p1 = memId;
aOp[7].p2 = memId+2;
aOp[7].p1 = memId;
aOp[10].p2 = memId;
}
}
@@ -343,6 +349,8 @@ static SQLITE_NOINLINE void autoIncrementEnd(Parse *pParse){
iRec = sqlite3GetTempReg(pParse);
assert( sqlite3SchemaMutexHeld(db, 0, pDb->pSchema) );
sqlite3VdbeAddOp3(v, OP_Le, memId+2, sqlite3VdbeCurrentAddr(v)+7, memId);
VdbeCoverage(v);
sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenWrite);
aOp = sqlite3VdbeAddOpList(v, ArraySize(autoIncEnd), autoIncEnd, iLn);
if( aOp==0 ) break;
+47 -14
View File
@@ -1476,21 +1476,38 @@ const char *sqlite3ErrStr(int rc){
** again until a timeout value is reached. The timeout value is
** an integer number of milliseconds passed in as the first
** argument.
**
** Return non-zero to retry the lock. Return zero to stop trying
** and cause SQLite to return SQLITE_BUSY.
*/
static int sqliteDefaultBusyCallback(
void *ptr, /* Database connection */
int count /* Number of times table has been busy */
void *ptr, /* Database connection */
int count, /* Number of times table has been busy */
sqlite3_file *pFile /* The file on which the lock occurred */
){
#if SQLITE_OS_WIN || HAVE_USLEEP
/* This case is for systems that have support for sleeping for fractions of
** a second. Examples: All windows systems, unix systems with usleep() */
static const u8 delays[] =
{ 1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 100 };
static const u8 totals[] =
{ 0, 1, 3, 8, 18, 33, 53, 78, 103, 128, 178, 228 };
# define NDELAY ArraySize(delays)
sqlite3 *db = (sqlite3 *)ptr;
int timeout = db->busyTimeout;
int tmout = db->busyTimeout;
int delay, prior;
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
if( sqlite3OsFileControl(pFile,SQLITE_FCNTL_LOCK_TIMEOUT,&tmout)==SQLITE_OK ){
if( count ){
tmout = 0;
sqlite3OsFileControl(pFile, SQLITE_FCNTL_LOCK_TIMEOUT, &tmout);
return 0;
}else{
return 1;
}
}
#endif
assert( count>=0 );
if( count < NDELAY ){
delay = delays[count];
@@ -1499,16 +1516,18 @@ static int sqliteDefaultBusyCallback(
delay = delays[NDELAY-1];
prior = totals[NDELAY-1] + delay*(count-(NDELAY-1));
}
if( prior + delay > timeout ){
delay = timeout - prior;
if( prior + delay > tmout ){
delay = tmout - prior;
if( delay<=0 ) return 0;
}
sqlite3OsSleep(db->pVfs, delay*1000);
return 1;
#else
/* This case for unix systems that lack usleep() support. Sleeping
** must be done in increments of whole seconds */
sqlite3 *db = (sqlite3 *)ptr;
int timeout = ((sqlite3 *)ptr)->busyTimeout;
if( (count+1)*1000 > timeout ){
int tmout = ((sqlite3 *)ptr)->busyTimeout;
if( (count+1)*1000 > tmout ){
return 0;
}
sqlite3OsSleep(db->pVfs, 1000000);
@@ -1519,14 +1538,25 @@ static int sqliteDefaultBusyCallback(
/*
** Invoke the given busy handler.
**
** This routine is called when an operation failed with a lock.
** This routine is called when an operation failed to acquire a
** lock on VFS file pFile.
**
** If this routine returns non-zero, the lock is retried. If it
** returns 0, the operation aborts with an SQLITE_BUSY error.
*/
int sqlite3InvokeBusyHandler(BusyHandler *p){
int sqlite3InvokeBusyHandler(BusyHandler *p, sqlite3_file *pFile){
int rc;
if( NEVER(p==0) || p->xFunc==0 || p->nBusy<0 ) return 0;
rc = p->xFunc(p->pArg, p->nBusy);
if( p->xBusyHandler==0 || p->nBusy<0 ) return 0;
if( p->bExtraFileArg ){
/* Add an extra parameter with the pFile pointer to the end of the
** callback argument list */
int (*xTra)(void*,int,sqlite3_file*);
xTra = (int(*)(void*,int,sqlite3_file*))p->xBusyHandler;
rc = xTra(p->pBusyArg, p->nBusy, pFile);
}else{
/* Legacy style busy handler callback */
rc = p->xBusyHandler(p->pBusyArg, p->nBusy);
}
if( rc==0 ){
p->nBusy = -1;
}else{
@@ -1548,9 +1578,10 @@ int sqlite3_busy_handler(
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
#endif
sqlite3_mutex_enter(db->mutex);
db->busyHandler.xFunc = xBusy;
db->busyHandler.pArg = pArg;
db->busyHandler.xBusyHandler = xBusy;
db->busyHandler.pBusyArg = pArg;
db->busyHandler.nBusy = 0;
db->busyHandler.bExtraFileArg = 0;
db->busyTimeout = 0;
sqlite3_mutex_leave(db->mutex);
return SQLITE_OK;
@@ -1598,8 +1629,10 @@ int sqlite3_busy_timeout(sqlite3 *db, int ms){
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
#endif
if( ms>0 ){
sqlite3_busy_handler(db, sqliteDefaultBusyCallback, (void*)db);
sqlite3_busy_handler(db, (int(*)(void*,int))sqliteDefaultBusyCallback,
(void*)db);
db->busyTimeout = ms;
db->busyHandler.bExtraFileArg = 1;
}else{
sqlite3_busy_handler(db, 0, 0);
}
+1 -1
View File
@@ -10,7 +10,7 @@
**
******************************************************************************
**
** This file implements in-memory VFS. A database is held as a contiguous
** This file implements an in-memory VFS. A database is held as a contiguous
** block of memory.
**
** This file also implements interface sqlite3_serialize() and
+3 -1
View File
@@ -126,7 +126,9 @@ int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut){
*/
int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){
#ifdef SQLITE_TEST
if( op!=SQLITE_FCNTL_COMMIT_PHASETWO ){
if( op!=SQLITE_FCNTL_COMMIT_PHASETWO
&& op!=SQLITE_FCNTL_LOCK_TIMEOUT
){
/* Faults are not injected into COMMIT_PHASETWO because, assuming SQLite
** is using a regular VFS, it is called after the corresponding
** transaction has been committed. Injecting a fault at this point
+293 -3
View File
@@ -229,6 +229,9 @@ struct unixFile {
#if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__)
unsigned fsFlags; /* cached details from statfs() */
#endif
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
unsigned iBusyTimeout; /* Wait this many millisec on locks */
#endif
#if OS_VXWORKS
struct vxworksFileId *pId; /* Unique file ID */
#endif
@@ -1465,6 +1468,43 @@ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){
return rc;
}
/*
** Set a posix-advisory-lock.
**
** There are two versions of this routine. If compiled with
** SQLITE_ENABLE_SETLK_TIMEOUT then the routine has an extra parameter
** which is a pointer to a unixFile. If the unixFile->iBusyTimeout
** value is set, then it is the number of milliseconds to wait before
** failing the lock. The iBusyTimeout value is always reset back to
** zero on each call.
**
** If SQLITE_ENABLE_SETLK_TIMEOUT is not defined, then do a non-blocking
** attempt to set the lock.
*/
#ifndef SQLITE_ENABLE_SETLK_TIMEOUT
# define osSetPosixAdvisoryLock(h,x,t) osFcntl(h,F_SETLK,x)
#else
static int osSetPosixAdvisoryLock(
int h, /* The file descriptor on which to take the lock */
struct flock *pLock, /* The description of the lock */
unixFile *pFile /* Structure holding timeout value */
){
int rc = osFcntl(h,F_SETLK,pLock);
while( rc<0 && pFile->iBusyTimeout>0 ){
/* On systems that support some kind of blocking file lock with a timeout,
** make appropriate changes here to invoke that blocking file lock. On
** generic posix, however, there is no such API. So we simply try the
** lock once every millisecond until either the timeout expires, or until
** the lock is obtained. */
usleep(1000);
rc = osFcntl(h,F_SETLK,pLock);
pFile->iBusyTimeout--;
}
return rc;
}
#endif /* SQLITE_ENABLE_SETLK_TIMEOUT */
/*
** Attempt to set a system-lock on the file pFile. The lock is
** described by pLock.
@@ -1497,7 +1537,7 @@ static int unixFileLock(unixFile *pFile, struct flock *pLock){
lock.l_start = SHARED_FIRST;
lock.l_len = SHARED_SIZE;
lock.l_type = F_WRLCK;
rc = osFcntl(pFile->h, F_SETLK, &lock);
rc = osSetPosixAdvisoryLock(pFile->h, &lock, pFile);
if( rc<0 ) return rc;
pInode->bProcessLock = 1;
pInode->nLock++;
@@ -1505,7 +1545,7 @@ static int unixFileLock(unixFile *pFile, struct flock *pLock){
rc = 0;
}
}else{
rc = osFcntl(pFile->h, F_SETLK, pLock);
rc = osSetPosixAdvisoryLock(pFile->h, pLock, pFile);
}
return rc;
}
@@ -3801,12 +3841,247 @@ static void unixModeBit(unixFile *pFile, unsigned char mask, int *pArg){
/* Forward declaration */
static int unixGetTempname(int nBuf, char *zBuf);
#ifdef SQLITE_SERVER_EDITION
/*
** Structure passed by SQLite through the (void*) argument to various
** fcntl operations.
*/
struct UnixServerArg {
void *h; /* Handle from SHMOPEN */
void *p; /* Mapping */
int i1; /* Integer value 1 */
int i2; /* Integer value 2 */
};
typedef struct UnixServerArg UnixServerArg;
/*
** Structure used as a server-shm handle.
*/
struct UnixServerShm {
void *pMap; /* Pointer to mapping */
int nMap; /* Size of mapping in bytes */
int fd; /* File descriptor open on *-hma file */
};
typedef struct UnixServerShm UnixServerShm;
/*
** Implementation of SQLITE_FCNTL_FILEID
*/
static int unixFcntlServerFileid(unixFile *pFile, void *pArg){
i64 *aId = (i64*)pArg;
aId[0] = (i64)(pFile->pInode->fileId.dev);
aId[1] = (i64)(pFile->pInode->fileId.ino);
return SQLITE_OK;
}
/*
** Implementation of SQLITE_FCNTL_SERVER_MODE
*/
static int unixFcntlServerMode(unixFile *pFile, void *pArg){
int rc = SQLITE_OK;
int eServer = 0;
char *zJrnl = sqlite3_mprintf("%s-journal", pFile->zPath);
if( zJrnl==0 ){
rc = SQLITE_NOMEM;
}else{
struct stat buf; /* Used to hold return values of stat() */
if( osStat(zJrnl, &buf) ){
rc = SQLITE_IOERR_FSTAT;
}else if( buf.st_mode & S_IFDIR ){
eServer = (pFile->ctrlFlags & UNIXFILE_EXCL) ? 1 : 2;
}
}
sqlite3_free(zJrnl);
*((int*)pArg) = eServer;
return rc;
}
/*
** Implementation of SQLITE_FCNTL_SERVER_SHMOPEN.
**
** The (void*) argument passed to this file control should actually be
** a pointer to a UnixServerArg or equivalent structure. Arguments are
** interpreted as follows:
**
** UnixServerArg.h - OUT: New server shm handle.
** UnixServerArg.p - OUT: New server shm mapping.
** UnixServerArg.i1 - Size of requested mapping in bytes.
** UnixServerArg.i2 - OUT: True if journal rollback + SHMOPEN2 are required.
*/
static int unixFcntlServerShmopen(unixFile *pFd, void *pArg){
int rc = SQLITE_OK;
UnixServerArg *pSArg = (UnixServerArg*)pArg;
UnixServerShm *p;
char *zHma;
p = sqlite3_malloc(sizeof(UnixServerShm));
if( p==0 ) return SQLITE_NOMEM;
memset(p, 0, sizeof(UnixServerShm));
p->fd = -1;
zHma = sqlite3_mprintf("%s-journal/hma", pFd->zPath);
if( zHma==0 ){
rc = SQLITE_NOMEM;
}else{
p->fd = osOpen(zHma, O_RDWR|O_CREAT, 0644);
p->nMap = pSArg->i1;
if( p->fd<0 ){
rc = SQLITE_CANTOPEN;
}else{
int res = ftruncate(p->fd, p->nMap);
if( res!=0 ){
rc = SQLITE_IOERR_TRUNCATE;
}else{
p->pMap = osMmap(0, p->nMap, PROT_READ|PROT_WRITE, MAP_SHARED, p->fd,0);
if( p->pMap==0 ){
rc = SQLITE_IOERR_MMAP;
}
}
}
sqlite3_free(zHma);
}
if( rc==SQLITE_OK ){
int res;
struct flock lock;
memset(&lock, 0, sizeof(struct flock));
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = p->nMap;
lock.l_len = 1;
res = osFcntl(p->fd, F_SETLK, &lock);
if( res==0 ){
pSArg->i2 = 1;
memset(p->pMap, 0, p->nMap);
}else{
pSArg->i2 = 0;
lock.l_type = F_RDLCK;
res = osFcntl(p->fd, F_SETLKW, &lock);
if( res!=0 ){
rc = SQLITE_IOERR_LOCK;
}
}
}
if( rc!=SQLITE_OK ){
if( p->pMap ) osMunmap(p->pMap, p->nMap);
if( p->fd>=0 ) close(p->fd);
sqlite3_free(p);
pSArg->h = pSArg->p = 0;
}else{
pSArg->h = (void*)p;
pSArg->p = (void*)(p->pMap);
}
return rc;
}
/*
** Implementation of SQLITE_FCNTL_SERVER_SHMOPEN2.
**
** The (void*) argument passed to this file control should actually be
** a pointer to a UnixServerArg or equivalent structure. Arguments are
** interpreted as follows:
**
** UnixServerArg.h - Server shm handle (from SHMOPEN).
** UnixServerArg.p - unused.
** UnixServerArg.i1 - unused.
** UnixServerArg.i2 - unused.
*/
static int unixFcntlServerShmopen2(unixFile *pFd, void *pArg){
UnixServerArg *pSArg = (UnixServerArg*)pArg;
UnixServerShm *p = (UnixServerShm*)pSArg->h;
int res;
struct flock lock;
memset(&lock, 0, sizeof(struct flock));
lock.l_type = F_RDLCK;
lock.l_whence = SEEK_SET;
lock.l_start = p->nMap;
lock.l_len = 1;
res = osFcntl(p->fd, F_SETLK, &lock);
return res ? SQLITE_IOERR_LOCK : SQLITE_OK;
}
/*
** Implementation of SQLITE_FCNTL_SERVER_SHMCLOSE.
**
** The (void*) argument passed to this file control should actually be
** a pointer to a UnixServerArg or equivalent structure. Arguments are
** interpreted as follows:
**
** UnixServerArg.h - Server shm handle (from SHMOPEN).
** UnixServerArg.p - unused.
** UnixServerArg.i1 - unused.
** UnixServerArg.i2 - unused.
*/
static int unixFcntlServerShmclose(unixFile *pFd, void *pArg){
UnixServerArg *pSArg = (UnixServerArg*)pArg;
UnixServerShm *p = (UnixServerShm*)pSArg->h;
if( p->pMap ) osMunmap(p->pMap, p->nMap);
if( p->fd>=0 ) close(p->fd);
sqlite3_free(p);
return SQLITE_OK;
}
/*
** Implementation of SQLITE_FCNTL_SERVER_SHMLOCK.
**
** The (void*) argument passed to this file control should actually be
** a pointer to a UnixServerArg or equivalent structure. Arguments are
** interpreted as follows:
**
** UnixServerArg.h - Server shm handle (from SHMOPEN).
** UnixServerArg.p - unused.
** UnixServerArg.i1 - slot to lock.
** UnixServerArg.i2 - true to take the lock, false to release it.
*/
static int unixFcntlServerShmlock(unixFile *pFd, void *pArg){
UnixServerArg *pSArg = (UnixServerArg*)pArg;
UnixServerShm *p = (UnixServerShm*)pSArg->h;
int res;
struct flock lock;
memset(&lock, 0, sizeof(struct flock));
lock.l_type = pSArg->i2 ? F_WRLCK : F_UNLCK;
lock.l_whence = SEEK_SET;
lock.l_start = p->nMap + pSArg->i1 + 1;
lock.l_len = 1;
res = osFcntl(p->fd, F_SETLK, &lock);
return (res==0 ? SQLITE_OK : SQLITE_BUSY);
}
#endif
/*
** Information and control of an open file handle.
*/
static int unixFileControl(sqlite3_file *id, int op, void *pArg){
unixFile *pFile = (unixFile*)id;
switch( op ){
#ifdef SQLITE_SERVER_EDITION
case SQLITE_FCNTL_FILEID:
return unixFcntlServerFileid(pFile, pArg);
case SQLITE_FCNTL_SERVER_MODE:
return unixFcntlServerMode(pFile, pArg);
case SQLITE_FCNTL_SERVER_SHMOPEN:
return unixFcntlServerShmopen(pFile, pArg);
case SQLITE_FCNTL_SERVER_SHMOPEN2:
return unixFcntlServerShmopen2(pFile, pArg);
case SQLITE_FCNTL_SERVER_SHMCLOSE:
return unixFcntlServerShmclose(pFile, pArg);
case SQLITE_FCNTL_SERVER_SHMLOCK:
return unixFcntlServerShmlock(pFile, pArg);
#endif
#if defined(__linux__) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
case SQLITE_FCNTL_BEGIN_ATOMIC_WRITE: {
int rc = osIoctl(pFile->h, F2FS_IOC_START_ATOMIC_WRITE);
@@ -3865,6 +4140,12 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
*(int*)pArg = fileHasMoved(pFile);
return SQLITE_OK;
}
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
case SQLITE_FCNTL_LOCK_TIMEOUT: {
pFile->iBusyTimeout = *(int*)pArg;
return SQLITE_OK;
}
#endif
#if SQLITE_MAX_MMAP_SIZE>0
case SQLITE_FCNTL_MMAP_SIZE: {
i64 newLimit = *(i64*)pArg;
@@ -4184,7 +4465,7 @@ static int unixShmSystemLock(
f.l_whence = SEEK_SET;
f.l_start = ofst;
f.l_len = n;
rc = osFcntl(pShmNode->h, F_SETLK, &f);
rc = osSetPosixAdvisoryLock(pShmNode->h, &f, pFile);
rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY;
}
@@ -5734,6 +6015,15 @@ static int findCreateFileMode(
zDb[nDb] = '\0';
rc = getFileMode(zDb, pMode, pUid, pGid);
#ifdef SQLITE_SERVER_EDITION
if( rc==SQLITE_IOERR_FSTAT ){
while( nDb && zDb[nDb]!='/' ) nDb--;
if( nDb>8 && memcmp("-journal/", &zDb[nDb-8], 9)==0 ){
zDb[nDb-8] = '\0';
rc = getFileMode(zDb, pMode, pUid, pGid);
}
}
#endif
}else if( flags & SQLITE_OPEN_DELETEONCLOSE ){
*pMode = 0600;
}else if( flags & SQLITE_OPEN_URI ){
+243 -17
View File
@@ -717,6 +717,10 @@ struct Pager {
Wal *pWal; /* Write-ahead log used by "journal_mode=wal" */
char *zWal; /* File name for write-ahead log */
#endif
#ifdef SQLITE_SERVER_EDITION
Server *pServer;
ServerPage *pServerPage;
#endif
};
/*
@@ -848,6 +852,13 @@ int sqlite3PagerUseWal(Pager *pPager, Pgno pgno){
# define pagerBeginReadTransaction(z) SQLITE_OK
#endif
#ifdef SQLITE_SERVER_EDITION
# define pagerIsServer(x) ((x)->pServer!=0)
# define pagerIsProcessServer(x) sqlite3ServerIsSingleProcess((x)->pServer)
#else
# define pagerIsServer(x) 0
#endif
#ifndef NDEBUG
/*
** Usage:
@@ -1056,6 +1067,9 @@ static void setGetterMethod(Pager *pPager){
pPager->xGet = getPageError;
#if SQLITE_MAX_MMAP_SIZE>0
}else if( USEFETCH(pPager)
#ifdef SQLITE_SERVER_EDITION
&& sqlite3ServerIsReadonly(pPager->pServer)==0
#endif
#ifdef SQLITE_HAS_CODEC
&& pPager->xCodec==0
#endif
@@ -1146,6 +1160,7 @@ static int pagerUnlockDb(Pager *pPager, int eLock){
assert( !pPager->exclusiveMode || pPager->eLock==eLock );
assert( eLock==NO_LOCK || eLock==SHARED_LOCK );
assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 );
assert( eLock!=NO_LOCK || pagerIsServer(pPager)==0 );
if( isOpen(pPager->fd) ){
assert( pPager->eLock>=eLock );
rc = pPager->noLock ? SQLITE_OK : sqlite3OsUnlock(pPager->fd, eLock);
@@ -1807,6 +1822,21 @@ static int addToSavepointBitvecs(Pager *pPager, Pgno pgno){
return rc;
}
#ifdef SQLITE_SERVER_EDITION
/*
** Free the linked list of ServerPage objects headed at Pager.pServerPage.
*/
static void pagerFreeServerPage(Pager *pPager){
ServerPage *pPg;
ServerPage *pNext;
for(pPg=pPager->pServerPage; pPg; pPg=pNext){
pNext = pPg->pNext;
sqlite3_free(pPg);
}
pPager->pServerPage = 0;
}
#endif
/*
** 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
@@ -1835,6 +1865,13 @@ static void pager_unlock(Pager *pPager){
pPager->pInJournal = 0;
releaseAllSavepoints(pPager);
#ifdef SQLITE_SERVER_EDITION
if( pagerIsServer(pPager) ){
pagerFreeServerPage(pPager);
sqlite3ServerEnd(pPager->pServer);
pPager->eState = PAGER_OPEN;
}else
#endif
if( pagerUseWal(pPager) ){
assert( !isOpen(pPager->jfd) );
sqlite3WalEndReadTransaction(pPager->pWal);
@@ -2135,6 +2172,11 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
}
#ifdef SQLITE_SERVER_EDITION
if( pagerIsServer(pPager) ){
rc2 = sqlite3ServerEndWrite(pPager->pServer);
}else
#endif
if( !pPager->exclusiveMode
&& (!pagerUseWal(pPager) || sqlite3WalExclusiveMode(pPager->pWal, 0))
){
@@ -3031,11 +3073,28 @@ static int readDbPage(PgHdr *pPg){
}else
#endif
{
i64 iOffset = (pPg->pgno-1)*(i64)pPager->pageSize;
rc = sqlite3OsRead(pPager->fd, pPg->pData, pPager->pageSize, iOffset);
if( rc==SQLITE_IOERR_SHORT_READ ){
rc = SQLITE_OK;
#ifdef SQLITE_SERVER_EDITION
u8 *pData = 0;
if( pagerIsServer(pPager) ){
sqlite3ServerReadPage(pPager->pServer, pPg->pgno, &pData);
if( pData ){
memcpy(pPg->pData, pData, pPager->pageSize);
}
}
if( pData==0 ){
#endif
i64 iOffset = (pPg->pgno-1)*(i64)pPager->pageSize;
rc = sqlite3OsRead(pPager->fd, pPg->pData, pPager->pageSize, iOffset);
if( rc==SQLITE_IOERR_SHORT_READ ){
rc = SQLITE_OK;
}
#ifdef SQLITE_SERVER_EDITION
if( pagerIsServer(pPager) ){
sqlite3ServerEndReadPage(pPager->pServer, pPg->pgno);
}
}
#endif
}
if( pPg->pgno==1 ){
@@ -3704,7 +3763,7 @@ static int pagerOpentemp(
** retried. If it returns zero, then the SQLITE_BUSY error is
** returned to the caller of the pager API function.
*/
void sqlite3PagerSetBusyhandler(
void sqlite3PagerSetBusyHandler(
Pager *pPager, /* Pager object */
int (*xBusyHandler)(void *), /* Pointer to busy-handler function */
void *pBusyHandlerArg /* Argument to pass to xBusyHandler */
@@ -4180,15 +4239,31 @@ int sqlite3PagerClose(Pager *pPager, sqlite3 *db){
** rollback before accessing the database file.
*/
if( isOpen(pPager->jfd) ){
#if 0
if( pagerIsServer(pPager) ){
assert( pPager->journalMode==PAGER_JOURNALMODE_PERSIST );
pPager->journalMode = PAGER_JOURNALMODE_DELETE;
/* If necessary, change the pager state so that the journal file
** is deleted by the call to pagerUnlockAndRollback() below. */
if( pPager->eState==PAGER_OPEN ) pPager->eState = PAGER_READER;
}
#endif
pager_error(pPager, pagerSyncHotJournal(pPager));
}
pagerUnlockAndRollback(pPager);
}
#ifdef SQLITE_SERVER_EDITION
if( pagerIsServer(pPager) ){
sqlite3ServerDisconnect(pPager->pServer, pPager->fd);
pPager->pServer = 0;
}else{
sqlite3OsClose(pPager->jfd);
}
#endif
sqlite3EndBenignMalloc();
enable_simulated_io_errors();
PAGERTRACE(("CLOSE %d\n", PAGERID(pPager)));
IOTRACE(("CLOSE %p\n", pPager))
sqlite3OsClose(pPager->jfd);
IOTRACE(("CLOSE %p\n", pPager));
sqlite3OsClose(pPager->fd);
sqlite3PageFree(pTmp);
sqlite3PcacheClose(pPager->pPCache);
@@ -4198,7 +4273,7 @@ int sqlite3PagerClose(Pager *pPager, sqlite3 *db){
#endif
assert( !pPager->aSavepoint && !pPager->pInJournal );
assert( !isOpen(pPager->jfd) && !isOpen(pPager->sjfd) );
assert( !isOpen(pPager->sjfd) );
sqlite3_free(pPager);
return SQLITE_OK;
@@ -4407,6 +4482,14 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
assert( pPager->eLock==EXCLUSIVE_LOCK );
assert( isOpen(pPager->fd) || pList->pDirty==0 );
#ifdef SQLITE_SERVER_EDITION
if( pagerIsProcessServer(pPager) ){
rc = sqlite3ServerPreCommit(pPager->pServer, pPager->pServerPage);
pPager->pServerPage = 0;
if( rc!=SQLITE_OK ) return rc;
}
#endif
/* If the file is a temp-file has not yet been opened, open it now. It
** is not possible for rc to be other than SQLITE_OK if this branch
** is taken, as pager_wait_on_lock() is a no-op for temp-files.
@@ -4589,6 +4672,8 @@ static int pagerStress(void *p, PgHdr *pPg){
Pager *pPager = (Pager *)p;
int rc = SQLITE_OK;
if( pagerIsServer(pPager) ) return SQLITE_OK;
assert( pPg->pPager==pPager );
assert( pPg->flags&PGHDR_DIRTY );
@@ -5038,7 +5123,7 @@ act_like_temp_file:
** to determine whether or not a hot-journal file exists, the IO error
** code is returned and the value of *pExists is undefined.
*/
static int hasHotJournal(Pager *pPager, int *pExists){
static int hasHotJournal(Pager *pPager, int *pExists, int *peServer){
sqlite3_vfs * const pVfs = pPager->pVfs;
int rc = SQLITE_OK; /* Return code */
int exists = 1; /* True if a journal file is present */
@@ -5059,6 +5144,13 @@ static int hasHotJournal(Pager *pPager, int *pExists){
if( rc==SQLITE_OK && exists ){
int locked = 0; /* True if some process holds a RESERVED lock */
#ifdef SQLITE_SERVER_EDITION
rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SERVER_MODE, peServer);
if( rc!=SQLITE_NOTFOUND ){
if( rc!=SQLITE_OK || *peServer ) return rc;
}
#endif
/* Race condition here: Another process might have been holding the
** the RESERVED lock and have a journal open at the sqlite3OsAccess()
** call above, but then delete the journal and drop the lock before
@@ -5131,6 +5223,53 @@ static int hasHotJournal(Pager *pPager, int *pExists){
return rc;
}
#ifdef SQLITE_SERVER_EDITION
static int pagerServerConnect(Pager *pPager, int eServer){
int rc = SQLITE_OK;
if( pPager->tempFile==0 ){
pPager->noLock = 1;
pPager->journalMode = PAGER_JOURNALMODE_PERSIST;
rc = sqlite3ServerConnect(pPager, eServer, &pPager->pServer);
}
return rc;
}
int sqlite3PagerRollbackJournal(Pager *pPager, sqlite3_file *pJfd){
int rc; /* Return Code */
sqlite3_file *saved_jfd = pPager->jfd;
u8 saved_eState = pPager->eState;
u8 saved_eLock = pPager->eLock;
i64 saved_journalOff = pPager->journalOff;
i64 saved_journalHdr = pPager->journalHdr;
assert( pPager->journalMode==PAGER_JOURNALMODE_PERSIST );
pPager->eLock = EXCLUSIVE_LOCK;
pPager->eState = PAGER_WRITER_DBMOD;
pPager->jfd = pJfd;
rc = pagerSyncHotJournal(pPager);
if( rc==SQLITE_OK ) rc = pager_playback(pPager, 1);
assert( isOpen(pPager->jfd) );
pPager->jfd = saved_jfd;
pPager->eState = saved_eState;
pPager->eLock = saved_eLock;
pPager->journalOff = saved_journalOff;
pPager->journalHdr = saved_journalHdr;
return rc;
}
void sqlite3PagerServerJournal(
Pager *pPager,
sqlite3_file *jfd,
const char *zJournal
){
pPager->zJournal = (char*)zJournal;
pPager->jfd = jfd;
}
#endif
/*
** This function is called to obtain a shared lock on the database file.
** It is illegal to call sqlite3PagerGet() until after this function
@@ -5158,8 +5297,11 @@ static int hasHotJournal(Pager *pPager, int *pExists){
** occurs while locking the database, checking for a hot-journal file or
** rolling back a journal file, the IO error code is returned.
*/
int sqlite3PagerSharedLock(Pager *pPager){
int sqlite3PagerSharedLock(Pager *pPager, int bReadonly){
int rc = SQLITE_OK; /* Return code */
#ifdef SQLITE_SERVER_EDITION
int eServer = 0;
#endif
/* This routine is only called from b-tree and only when there are no
** outstanding pages. This implies that the pager state should either
@@ -5170,7 +5312,9 @@ int sqlite3PagerSharedLock(Pager *pPager){
assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER );
assert( pPager->errCode==SQLITE_OK );
if( !pagerUseWal(pPager) && pPager->eState==PAGER_OPEN ){
if( !pagerUseWal(pPager)
&& !pagerIsServer(pPager)
&& pPager->eState==PAGER_OPEN ){
int bHotJournal = 1; /* True if there exists a hot journal-file */
assert( !MEMDB );
@@ -5186,7 +5330,8 @@ int sqlite3PagerSharedLock(Pager *pPager){
** database file, then it either needs to be played back or deleted.
*/
if( pPager->eLock<=SHARED_LOCK ){
rc = hasHotJournal(pPager, &bHotJournal);
rc = hasHotJournal(pPager, &bHotJournal, &eServer);
assert( bHotJournal==0 || eServer==0 );
}
if( rc!=SQLITE_OK ){
goto failed;
@@ -5319,6 +5464,7 @@ int sqlite3PagerSharedLock(Pager *pPager){
if( rc!=SQLITE_IOERR_SHORT_READ ){
goto failed;
}
rc = SQLITE_OK;
memset(dbFileVers, 0, sizeof(dbFileVers));
}
@@ -5337,16 +5483,37 @@ int sqlite3PagerSharedLock(Pager *pPager){
}
}
#ifdef SQLITE_SERVER_EDITION
if( eServer ){
rc = pagerServerConnect(pPager, eServer);
}
#endif
/* If there is a WAL file in the file-system, open this database in WAL
** mode. Otherwise, the following function call is a no-op.
*/
rc = pagerOpenWalIfPresent(pPager);
if( rc==SQLITE_OK ){
rc = pagerOpenWalIfPresent(pPager);
}
#ifndef SQLITE_OMIT_WAL
assert( pPager->pWal==0 || rc==SQLITE_OK );
#endif
}
if( pagerUseWal(pPager) ){
#ifdef SQLITE_SERVER_EDITION
if( pagerIsServer(pPager) ){
assert( rc==SQLITE_OK );
assert( sqlite3PagerRefcount(pPager)==0 );
assert( pagerUseWal(pPager)==0 );
pager_reset(pPager);
rc = sqlite3ServerBegin(pPager->pServer, bReadonly);
if( rc==SQLITE_OK ){
rc = sqlite3ServerLock(pPager->pServer, 1, 0, 0);
}
setGetterMethod(pPager);
}
#endif
if( rc==SQLITE_OK && pagerUseWal(pPager) ){
assert( rc==SQLITE_OK );
rc = pagerBeginReadTransaction(pPager);
}
@@ -5635,6 +5802,12 @@ int sqlite3PagerGet(
DbPage **ppPage, /* Write a pointer to the page here */
int flags /* PAGER_GET_XXX flags */
){
#ifdef SQLITE_SERVER_EDITION
if( pagerIsServer(pPager) ){
int rc = sqlite3ServerLock(pPager->pServer, pgno, 0, 0);
if( rc!=SQLITE_OK ) return rc;
}
#endif
return pPager->xGet(pPager, pgno, ppPage, flags);
}
@@ -5693,6 +5866,7 @@ void sqlite3PagerUnrefPageOne(DbPage *pPg){
assert( pPg->pgno==1 );
assert( (pPg->flags & PGHDR_MMAP)==0 ); /* Page1 is never memory mapped */
pPager = pPg->pPager;
sqlite3PagerResetLockTimeout(pPager);
sqlite3PcacheRelease(pPg);
pagerUnlockIfUnused(pPager);
}
@@ -5884,6 +6058,24 @@ static SQLITE_NOINLINE int pagerAddPageToRollbackJournal(PgHdr *pPg){
char *pData2;
i64 iOff = pPager->journalOff;
#ifdef SQLITE_SERVER_EDITION
if( pagerIsProcessServer(pPager) ){
ServerPage *p = sqlite3ServerBuffer(pPager->pServer);
if( p==0 ){
int nByte = sizeof(ServerPage) + pPager->pageSize;
p = (ServerPage*)sqlite3_malloc(nByte);
if( !p ) return SQLITE_NOMEM_BKPT;
}
memset(p, 0, sizeof(ServerPage));
p->aData = (u8*)&p[1];
p->nData = pPager->pageSize;
p->pgno = pPg->pgno;
p->pNext = pPager->pServerPage;
pPager->pServerPage = p;
memcpy(p->aData, pPg->pData, pPager->pageSize);
}
#endif
/* We should never write to the journal file the page that
** contains the database locks. The following assert verifies
** that we do not. */
@@ -5951,6 +6143,13 @@ static int pager_write(PgHdr *pPg){
assert( pPager->readOnly==0 );
CHECK_PAGE(pPg);
#ifdef SQLITE_SERVER_EDITION
if( pagerIsServer(pPager) ){
rc = sqlite3ServerLock(pPager->pServer, pPg->pgno, 1, 0);
if( rc!=SQLITE_OK ) return rc;
}
#endif
/* The journal file needs to be opened. Higher level routines have already
** obtained the necessary locks to begin the write-transaction, but the
** rollback journal might not yet be open. Open it now if this is the case.
@@ -6229,7 +6428,10 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
# define DIRECT_MODE isDirectMode
#endif
if( !pPager->changeCountDone && ALWAYS(pPager->dbSize>0) ){
if( 0==pagerIsServer(pPager)
&& !pPager->changeCountDone
&& ALWAYS(pPager->dbSize>0)
){
PgHdr *pPgHdr; /* Reference to page 1 */
assert( !pPager->tempFile && isOpen(pPager->fd) );
@@ -6388,6 +6590,10 @@ int sqlite3PagerCommitPhaseOne(
** backup in progress needs to be restarted. */
sqlite3BackupRestart(pPager->pBackup);
}else{
/* If this connection is in server mode, ignore any master journal. */
if( pagerIsServer(pPager) ){
zMaster = 0;
}
if( pagerUseWal(pPager) ){
PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache);
PgHdr *pPageOne = 0;
@@ -6970,6 +7176,18 @@ sqlite3_file *sqlite3PagerFile(Pager *pPager){
return pPager->fd;
}
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
/*
** Reset the lock timeout for pager.
*/
void sqlite3PagerResetLockTimeout(Pager *pPager){
if( isOpen(pPager->fd) ){
int x = 0;
sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_LOCK_TIMEOUT, &x);
}
}
#endif
/*
** Return the file handle for the journal file (if it exists).
** This will be either the rollback journal or the WAL file.
@@ -7328,7 +7546,7 @@ int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){
int state = pPager->eState;
assert( state==PAGER_OPEN || state==PAGER_READER );
if( state==PAGER_OPEN ){
rc = sqlite3PagerSharedLock(pPager);
rc = sqlite3PagerSharedLock(pPager, 0);
}
if( pPager->eState==PAGER_READER ){
assert( rc==SQLITE_OK );
@@ -7430,6 +7648,7 @@ int sqlite3PagerCheckpoint(
pPager->walSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace,
pnLog, pnCkpt
);
sqlite3PagerResetLockTimeout(pPager);
}
return rc;
}
@@ -7444,7 +7663,7 @@ int sqlite3PagerWalCallback(Pager *pPager){
*/
int sqlite3PagerWalSupported(Pager *pPager){
const sqlite3_io_methods *pMethods = pPager->fd->pMethods;
if( pPager->noLock ) return 0;
if( pPager->noLock && !pagerIsServer(pPager) ) return 0;
return pPager->exclusiveMode || (pMethods->iVersion>=2 && pMethods->xShmMap);
}
@@ -7652,4 +7871,11 @@ int sqlite3PagerWalFramesize(Pager *pPager){
}
#endif
#ifdef SQLITE_SERVER_EDITION
int sqlite3PagerPagelock(Pager *pPager, Pgno pgno, int bWrite){
if( pagerIsServer(pPager)==0 ) return SQLITE_OK;
return sqlite3ServerLock(pPager->pServer, pgno, bWrite, 0);
}
#endif
#endif /* SQLITE_OMIT_DISKIO */
+13 -2
View File
@@ -126,7 +126,7 @@ int sqlite3PagerClose(Pager *pPager, sqlite3*);
int sqlite3PagerReadFileheader(Pager*, int, unsigned char*);
/* Functions used to configure a Pager object. */
void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *);
void sqlite3PagerSetBusyHandler(Pager*, int(*)(void *), void *);
int sqlite3PagerSetPagesize(Pager*, u32*, int);
#ifdef SQLITE_HAS_CODEC
void sqlite3PagerAlignReserve(Pager*,Pager*);
@@ -171,7 +171,7 @@ int sqlite3PagerCommitPhaseTwo(Pager*);
int sqlite3PagerRollback(Pager*);
int sqlite3PagerOpenSavepoint(Pager *pPager, int n);
int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint);
int sqlite3PagerSharedLock(Pager *pPager);
int sqlite3PagerSharedLock(Pager *pPager, int bReadonly);
#ifndef SQLITE_OMIT_WAL
int sqlite3PagerCheckpoint(Pager *pPager, sqlite3*, int, int*, int*);
@@ -212,6 +212,11 @@ int sqlite3PagerIsMemdb(Pager*);
void sqlite3PagerCacheStat(Pager *, int, int, int *);
void sqlite3PagerClearCache(Pager*);
int sqlite3SectorSize(sqlite3_file *);
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
void sqlite3PagerResetLockTimeout(Pager *pPager);
#else
# define sqlite3PagerResetLockTimeout(X)
#endif
/* Functions used to truncate the database file. */
void sqlite3PagerTruncateImage(Pager*,Pgno);
@@ -237,4 +242,10 @@ void *sqlite3PagerCodec(DbPage *);
# define enable_simulated_io_errors()
#endif
#ifdef SQLITE_SERVER_EDITION
int sqlite3PagerRollbackJournal(Pager*, sqlite3_file*);
int sqlite3PagerPagelock(Pager *pPager, Pgno, int);
void sqlite3PagerServerJournal(Pager*, sqlite3_file*, const char*);
#endif
#endif /* SQLITE_PAGER_H */
+2 -2
View File
@@ -131,6 +131,7 @@ trans_opt ::= TRANSACTION.
trans_opt ::= TRANSACTION nm.
%type transtype {int}
transtype(A) ::= . {A = TK_DEFERRED;}
transtype(A) ::= READONLY(X). {A = @X; /*A-overwrites-X*/}
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*/}
@@ -523,8 +524,7 @@ oneselect(A) ::= SELECT(S) distinct(D) selcollist(W) from(X) where_opt(Y)
if( A!=0 ){
const char *z = s.z+6;
int i;
sqlite3_snprintf(sizeof(A->zSelName), A->zSelName, "#%d",
++pParse->nSelect);
sqlite3_snprintf(sizeof(A->zSelName), A->zSelName,"#%d",++pParse->nSelect);
while( z[0]==' ' ) z++;
if( z[0]=='/' && z[1]=='*' ){
z += 2;
+27
View File
@@ -660,6 +660,33 @@ void sqlite3Pragma(
break;
}
#ifdef SQLITE_SERVER_EDITION
/*
** PRAGMA [schema.]freelist_format
** PRAGMA [schema.]freelist_format = (1|2)
*/
case PragTyp_FREELIST_FORMAT: {
sqlite3VdbeUsesBtree(v, iDb);
static const VdbeOpList freelist[] = {
{ OP_Transaction, 0, 0, 0}, /* 0 */
{ OP_FreelistFmt, 0, 1, 0}, /* 1 */
{ OP_ResultRow, 1, 1, 0} /* 2 */
};
VdbeOp *aOp;
sqlite3VdbeVerifyNoMallocRequired(v, ArraySize(freelist));
aOp = sqlite3VdbeAddOpList(v, ArraySize(freelist), freelist,0);
aOp[0].p1 = iDb;
aOp[1].p1 = iDb;
if( zRight && (zRight[0]=='1' || zRight[0]=='2') && zRight[1]=='\0' ){
aOp[0].p2 = 1; /* Open a write transaction */
aOp[1].p3 = (int)(zRight[0] - '0');
}
break;
}
#endif
/*
** PRAGMA [schema.]journal_size_limit
** PRAGMA [schema.]journal_size_limit=N
+41 -33
View File
@@ -20,38 +20,39 @@
#define PragTyp_ENCODING 12
#define PragTyp_FOREIGN_KEY_CHECK 13
#define PragTyp_FOREIGN_KEY_LIST 14
#define PragTyp_FUNCTION_LIST 15
#define PragTyp_INCREMENTAL_VACUUM 16
#define PragTyp_INDEX_INFO 17
#define PragTyp_INDEX_LIST 18
#define PragTyp_INTEGRITY_CHECK 19
#define PragTyp_JOURNAL_MODE 20
#define PragTyp_JOURNAL_SIZE_LIMIT 21
#define PragTyp_LOCK_PROXY_FILE 22
#define PragTyp_LOCKING_MODE 23
#define PragTyp_PAGE_COUNT 24
#define PragTyp_MMAP_SIZE 25
#define PragTyp_MODULE_LIST 26
#define PragTyp_OPTIMIZE 27
#define PragTyp_PAGE_SIZE 28
#define PragTyp_PRAGMA_LIST 29
#define PragTyp_SECURE_DELETE 30
#define PragTyp_SHRINK_MEMORY 31
#define PragTyp_SOFT_HEAP_LIMIT 32
#define PragTyp_SYNCHRONOUS 33
#define PragTyp_TABLE_INFO 34
#define PragTyp_TEMP_STORE 35
#define PragTyp_TEMP_STORE_DIRECTORY 36
#define PragTyp_THREADS 37
#define PragTyp_WAL_AUTOCHECKPOINT 38
#define PragTyp_WAL_CHECKPOINT 39
#define PragTyp_ACTIVATE_EXTENSIONS 40
#define PragTyp_HEXKEY 41
#define PragTyp_KEY 42
#define PragTyp_REKEY 43
#define PragTyp_LOCK_STATUS 44
#define PragTyp_PARSER_TRACE 45
#define PragTyp_STATS 46
#define PragTyp_FREELIST_FORMAT 15
#define PragTyp_FUNCTION_LIST 16
#define PragTyp_INCREMENTAL_VACUUM 17
#define PragTyp_INDEX_INFO 18
#define PragTyp_INDEX_LIST 19
#define PragTyp_INTEGRITY_CHECK 20
#define PragTyp_JOURNAL_MODE 21
#define PragTyp_JOURNAL_SIZE_LIMIT 22
#define PragTyp_LOCK_PROXY_FILE 23
#define PragTyp_LOCKING_MODE 24
#define PragTyp_PAGE_COUNT 25
#define PragTyp_MMAP_SIZE 26
#define PragTyp_MODULE_LIST 27
#define PragTyp_OPTIMIZE 28
#define PragTyp_PAGE_SIZE 29
#define PragTyp_PRAGMA_LIST 30
#define PragTyp_SECURE_DELETE 31
#define PragTyp_SHRINK_MEMORY 32
#define PragTyp_SOFT_HEAP_LIMIT 33
#define PragTyp_SYNCHRONOUS 34
#define PragTyp_TABLE_INFO 35
#define PragTyp_TEMP_STORE 36
#define PragTyp_TEMP_STORE_DIRECTORY 37
#define PragTyp_THREADS 38
#define PragTyp_WAL_AUTOCHECKPOINT 39
#define PragTyp_WAL_CHECKPOINT 40
#define PragTyp_ACTIVATE_EXTENSIONS 41
#define PragTyp_HEXKEY 42
#define PragTyp_KEY 43
#define PragTyp_REKEY 44
#define PragTyp_LOCK_STATUS 45
#define PragTyp_PARSER_TRACE 46
#define PragTyp_STATS 47
/* Property flags associated with various pragma. */
#define PragFlg_NeedSchema 0x01 /* Force schema load before running */
@@ -300,6 +301,13 @@ static const PragmaName aPragmaName[] = {
/* ColNames: */ 0, 0,
/* iArg: */ BTREE_FREE_PAGE_COUNT },
#endif
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && defined(SQLITE_SERVER_EDITION)
{/* zName: */ "freelist_format",
/* ePragTyp: */ PragTyp_FREELIST_FORMAT,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq,
/* ColNames: */ 0, 0,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
{/* zName: */ "full_column_names",
/* ePragTyp: */ PragTyp_FLAG,
@@ -646,4 +654,4 @@ static const PragmaName aPragmaName[] = {
/* iArg: */ SQLITE_WriteSchema },
#endif
};
/* Number of pragmas: 60 on by default, 77 total. */
/* Number of pragmas: 60 on by default, 78 total. */
+1 -1
View File
@@ -29,7 +29,7 @@ static void corruptSchema(
char *z;
if( zObj==0 ) zObj = "?";
z = sqlite3MPrintf(db, "malformed database schema (%s)", zObj);
if( zExtra ) z = sqlite3MPrintf(db, "%z - %s", z, zExtra);
if( zExtra && zExtra[0] ) z = sqlite3MPrintf(db, "%z - %s", z, zExtra);
sqlite3DbFree(db, *pData->pzErrMsg);
*pData->pzErrMsg = z;
}
+94 -41
View File
@@ -21,8 +21,7 @@
/***/ int sqlite3SelectTrace = 0;
# define SELECTTRACE(K,P,S,X) \
if(sqlite3SelectTrace&(K)) \
sqlite3DebugPrintf("%*s%s.%p: ",(P)->nSelectIndent*2-2,"",\
(S)->zSelName,(S)),\
sqlite3DebugPrintf("%s/%p: ",(S)->zSelName,(S)),\
sqlite3DebugPrintf X
#else
# define SELECTTRACE(K,P,S,X)
@@ -383,6 +382,29 @@ static void setJoinExpr(Expr *p, int iTable){
}
}
/* Undo the work of setJoinExpr(). In the expression tree p, convert every
** term that is marked with EP_FromJoin and iRightJoinTable==iTable into
** an ordinary term that omits the EP_FromJoin mark.
**
** This happens when a LEFT JOIN is simplified into an ordinary JOIN.
*/
static void unsetJoinExpr(Expr *p, int iTable){
while( p ){
if( ExprHasProperty(p, EP_FromJoin)
&& (iTable<0 || p->iRightJoinTable==iTable) ){
ExprClearProperty(p, EP_FromJoin);
}
if( p->op==TK_FUNCTION && p->x.pList ){
int i;
for(i=0; i<p->x.pList->nExpr; i++){
unsetJoinExpr(p->x.pList->a[i].pExpr, iTable);
}
}
unsetJoinExpr(p->pLeft, iTable);
p = p->pRight;
}
}
/*
** This routine processes the join information for a SELECT statement.
** ON and USING clauses are converted into extra terms of the WHERE clause.
@@ -3835,12 +3857,22 @@ static int flattenSubquery(
** (3) The inner query has a LIMIT clause (since the changes to the WHERE
** close would change the meaning of the LIMIT).
**
** (4) The inner query is the right operand of a LEFT JOIN. (The caller
** enforces this restriction since this routine does not have enough
** information to know.)
** (4) The inner query is the right operand of a LEFT JOIN and the
** expression to be pushed down does not come from the ON clause
** on that LEFT JOIN.
**
** (5) The WHERE clause expression originates in the ON or USING clause
** of a LEFT JOIN.
** of a LEFT JOIN where iCursor is not the right-hand table of that
** left join. An example:
**
** SELECT *
** FROM (SELECT 1 AS a1 UNION ALL SELECT 2) AS aa
** JOIN (SELECT 1 AS b2 UNION ALL SELECT 2) AS bb ON (a1=b2)
** LEFT JOIN (SELECT 8 AS c3 UNION ALL SELECT 9) AS cc ON (b2=2);
**
** The correct answer is three rows: (1,1,NULL),(2,2,8),(2,2,9).
** But if the (b2=2) term were to be pushed down into the bb subquery,
** then the (1,1,NULL) row would be suppressed.
**
** Return 0 if no changes are made and non-zero if one or more WHERE clause
** terms are duplicated into the subquery.
@@ -3849,7 +3881,8 @@ static int pushDownWhereTerms(
Parse *pParse, /* Parse context (for malloc() and error reporting) */
Select *pSubq, /* The subquery whose WHERE clause is to be augmented */
Expr *pWhere, /* The WHERE clause of the outer query */
int iCursor /* Cursor number of the subquery */
int iCursor, /* Cursor number of the subquery */
int isLeftJoin /* True if pSubq is the right term of a LEFT JOIN */
){
Expr *pNew;
int nChng = 0;
@@ -3873,15 +3906,25 @@ static int pushDownWhereTerms(
return 0; /* restriction (3) */
}
while( pWhere->op==TK_AND ){
nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, iCursor);
nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight,
iCursor, isLeftJoin);
pWhere = pWhere->pLeft;
}
if( ExprHasProperty(pWhere,EP_FromJoin) ) return 0; /* restriction (5) */
if( isLeftJoin
&& (ExprHasProperty(pWhere,EP_FromJoin)==0
|| pWhere->iRightJoinTable!=iCursor)
){
return 0; /* restriction (4) */
}
if( ExprHasProperty(pWhere,EP_FromJoin) && pWhere->iRightJoinTable!=iCursor ){
return 0; /* restriction (5) */
}
if( sqlite3ExprIsTableConstant(pWhere, iCursor) ){
nChng++;
while( pSubq ){
SubstContext x;
pNew = sqlite3ExprDup(pParse->db, pWhere, 0);
unsetJoinExpr(pNew, -1);
x.pParse = pParse;
x.iTable = iCursor;
x.iNewTable = iCursor;
@@ -4913,14 +4956,6 @@ static void explainSimpleCount(
# define explainSimpleCount(a,b,c)
#endif
/*
** Context object for havingToWhereExprCb().
*/
struct HavingToWhereCtx {
Expr **ppWhere;
ExprList *pGroupBy;
};
/*
** sqlite3WalkExpr() callback used by havingToWhere().
**
@@ -4934,15 +4969,16 @@ struct HavingToWhereCtx {
*/
static int havingToWhereExprCb(Walker *pWalker, Expr *pExpr){
if( pExpr->op!=TK_AND ){
struct HavingToWhereCtx *p = pWalker->u.pHavingCtx;
if( sqlite3ExprIsConstantOrGroupBy(pWalker->pParse, pExpr, p->pGroupBy) ){
Select *pS = pWalker->u.pSelect;
if( sqlite3ExprIsConstantOrGroupBy(pWalker->pParse, pExpr, pS->pGroupBy) ){
sqlite3 *db = pWalker->pParse->db;
Expr *pNew = sqlite3ExprAlloc(db, TK_INTEGER, &sqlite3IntTokens[1], 0);
if( pNew ){
Expr *pWhere = *(p->ppWhere);
Expr *pWhere = pS->pWhere;
SWAP(Expr, *pNew, *pExpr);
pNew = sqlite3ExprAnd(db, pWhere, pNew);
*(p->ppWhere) = pNew;
pS->pWhere = pNew;
pWalker->eCode = 1;
}
}
return WRC_Prune;
@@ -4965,23 +5001,19 @@ static int havingToWhereExprCb(Walker *pWalker, Expr *pExpr){
** entirely of constants and expressions that are also GROUP BY terms that
** use the "BINARY" collation sequence.
*/
static void havingToWhere(
Parse *pParse,
ExprList *pGroupBy,
Expr *pHaving,
Expr **ppWhere
){
struct HavingToWhereCtx sCtx;
static void havingToWhere(Parse *pParse, Select *p){
Walker sWalker;
sCtx.ppWhere = ppWhere;
sCtx.pGroupBy = pGroupBy;
memset(&sWalker, 0, sizeof(sWalker));
sWalker.pParse = pParse;
sWalker.xExprCallback = havingToWhereExprCb;
sWalker.u.pHavingCtx = &sCtx;
sqlite3WalkExpr(&sWalker, pHaving);
sWalker.u.pSelect = p;
sqlite3WalkExpr(&sWalker, p->pHaving);
#if SELECTTRACE_ENABLED
if( sWalker.eCode && (sqlite3SelectTrace & 0x100)!=0 ){
SELECTTRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
}
/*
@@ -5142,7 +5174,6 @@ int sqlite3Select(
if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1;
memset(&sAggInfo, 0, sizeof(sAggInfo));
#if SELECTTRACE_ENABLED
pParse->nSelectIndent++;
SELECTTRACE(1,pParse,p, ("begin processing:\n"));
if( sqlite3SelectTrace & 0x100 ){
sqlite3TreeViewSelect(0, p, 0);
@@ -5188,13 +5219,29 @@ int sqlite3Select(
generateColumnNames(pParse, p);
}
/* Try to flatten subqueries in the FROM clause up into the main query
/* Try to various optimizations (flattening subqueries, and strength
** reduction of join operators) in the FROM clause up into the main query
*/
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
for(i=0; !p->pPrior && i<pTabList->nSrc; i++){
struct SrcList_item *pItem = &pTabList->a[i];
Select *pSub = pItem->pSelect;
Table *pTab = pItem->pTab;
/* Convert LEFT JOIN into JOIN if there are terms of the right table
** of the LEFT JOIN used in the WHERE clause.
*/
if( (pItem->fg.jointype & JT_LEFT)!=0
&& sqlite3ExprImpliesNonNullRow(p->pWhere, pItem->iCursor)
&& OptimizationEnabled(db, SQLITE_SimplifyJoin)
){
SELECTTRACE(0x100,pParse,p,
("LEFT-JOIN simplifies to JOIN on term %d\n",i));
pItem->fg.jointype &= ~(JT_LEFT|JT_OUTER);
unsetJoinExpr(p->pWhere, pItem->iCursor);
}
/* No futher action if this term of the FROM clause is no a subquery */
if( pSub==0 ) continue;
/* Catch mismatch in the declared columns of a view and the number of
@@ -5263,7 +5310,6 @@ int sqlite3Select(
explainSetInteger(pParse->iSelectId, iRestoreSelectId);
#if SELECTTRACE_ENABLED
SELECTTRACE(1,pParse,p,("end compound-select processing\n"));
pParse->nSelectIndent--;
#endif
return rc;
}
@@ -5336,8 +5382,9 @@ int sqlite3Select(
/* Make copies of constant WHERE-clause terms in the outer query down
** inside the subquery. This can help the subquery to run more efficiently.
*/
if( (pItem->fg.jointype & JT_OUTER)==0
&& pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor)
if( OptimizationEnabled(db, SQLITE_PushDown)
&& pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor,
(pItem->fg.jointype & JT_OUTER)!=0)
){
#if SELECTTRACE_ENABLED
if( sqlite3SelectTrace & 0x100 ){
@@ -5345,6 +5392,8 @@ int sqlite3Select(
sqlite3TreeViewSelect(0, p, 0);
}
#endif
}else{
SELECTTRACE(0x100,pParse,p,("Push-down not possible\n"));
}
zSavedAuthContext = pParse->zAuthContext;
@@ -5547,6 +5596,7 @@ int sqlite3Select(
wctrlFlags |= p->selFlags & SF_FixedLimit;
/* Begin the database scan. */
SELECTTRACE(1,pParse,p,("WhereBegin\n"));
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, sSort.pOrderBy,
p->pEList, wctrlFlags, p->nSelectRow);
if( pWInfo==0 ) goto select_end;
@@ -5648,7 +5698,9 @@ int sqlite3Select(
if( pHaving ){
if( pGroupBy ){
assert( pWhere==p->pWhere );
havingToWhere(pParse, pGroupBy, pHaving, &p->pWhere);
assert( pHaving==p->pHaving );
assert( pGroupBy==p->pGroupBy );
havingToWhere(pParse, p);
pWhere = p->pWhere;
}
sqlite3ExprAnalyzeAggregates(&sNC, pHaving);
@@ -5735,6 +5787,7 @@ int sqlite3Select(
** in the right order to begin with.
*/
sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset);
SELECTTRACE(1,pParse,p,("WhereBegin\n"));
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, 0,
WHERE_GROUPBY | (orderByGrp ? WHERE_SORTBYGROUP : 0), 0
);
@@ -5990,6 +6043,7 @@ int sqlite3Select(
assert( minMaxFlag==WHERE_ORDERBY_NORMAL || pMinMaxOrderBy!=0 );
assert( pMinMaxOrderBy==0 || pMinMaxOrderBy->nExpr==1 );
SELECTTRACE(1,pParse,p,("WhereBegin\n"));
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMaxOrderBy,
0, minMaxFlag, 0);
if( pWInfo==0 ){
@@ -6045,7 +6099,6 @@ select_end:
sqlite3DbFree(db, sAggInfo.aFunc);
#if SELECTTRACE_ENABLED
SELECTTRACE(1,pParse,p,("end processing\n"));
pParse->nSelectIndent--;
#endif
return rc;
}
+1026
View File
File diff suppressed because it is too large Load Diff
+56
View File
@@ -0,0 +1,56 @@
/*
** 2017 April 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.
**
*************************************************************************
*/
#ifdef SQLITE_SERVER_EDITION
#ifndef SQLITE_SERVER_H
#define SQLITE_SERVER_H
typedef struct Server Server;
typedef struct ServerPage ServerPage;
struct ServerPage {
Pgno pgno; /* Page number for this record */
int nData; /* Size of aData[] in bytes */
u8 *aData;
ServerPage *pNext;
int iCommitId;
ServerPage *pHashNext;
ServerPage *pHashPrev;
};
int sqlite3ServerConnect(Pager *pPager, int eServer, Server **ppOut);
void sqlite3ServerDisconnect(Server *p, sqlite3_file *dbfd);
int sqlite3ServerBegin(Server *p, int bReadonly);
int sqlite3ServerPreCommit(Server*, ServerPage*);
int sqlite3ServerEnd(Server *p);
int sqlite3ServerEndWrite(Server *p);
int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite, int bBlock);
ServerPage *sqlite3ServerBuffer(Server*);
int sqlite3ServerIsSingleProcess(Server*);
/* For "BEGIN READONLY" clients. */
int sqlite3ServerIsReadonly(Server*);
void sqlite3ServerReadPage(Server*, Pgno, u8**);
void sqlite3ServerEndReadPage(Server*, Pgno);
#endif /* SQLITE_SERVER_H */
#endif /* SQLITE_SERVER_EDITION */
+28 -11
View File
@@ -132,6 +132,9 @@ typedef unsigned char u8;
# ifndef access
# define access(f,m) _access((f),(m))
# endif
# ifndef unlink
# define unlink _unlink
# endif
# undef popen
# define popen _popen
# undef pclose
@@ -2286,18 +2289,20 @@ static int display_stats(
for(i=0; i<nCol; i++){
sqlite3_snprintf(sizeof(z),z,"Column %d %nname:", i, &x);
utf8_printf(out, "%-36s %s\n", z, sqlite3_column_name(pStmt,i));
#ifndef SQLITE_OMIT_DECLTYPE
sqlite3_snprintf(30, z+x, "declared type:");
utf8_printf(out, "%-36s %s\n", z, sqlite3_column_decltype(pStmt, i));
#ifdef SQLITE_ENABLE_COLUMN_METADATA
#endif
#ifdef SQLITE_ENABLE_COLUMN_METADATA
sqlite3_snprintf(30, z+x, "database name:");
utf8_printf(out, "%-36s %s\n", z, sqlite3_column_database_name(pStmt,i));
sqlite3_snprintf(30, z+x, "table name:");
utf8_printf(out, "%-36s %s\n", z, sqlite3_column_table_name(pStmt,i));
sqlite3_snprintf(30, z+x, "origin name:");
utf8_printf(out, "%-36s %s\n", z, sqlite3_column_origin_name(pStmt,i));
#endif
}
#endif
}
}
displayStatLine(pArg, "Memory Used:",
"%lld (max %lld) bytes", SQLITE_STATUS_MEMORY_USED, bReset);
@@ -2877,7 +2882,12 @@ static int shell_exec(
sqlite3_finalize(pExplain);
sqlite3_free(zEQP);
}
sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, triggerEQP, 0);
if( pArg->autoEQP>=AUTOEQP_trigger && triggerEQP==0 ){
sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 0, 0);
/* Reprepare pStmt before reactiving trace modes */
sqlite3_finalize(pStmt);
sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
}
restore_debug_trace_modes();
}
@@ -5329,11 +5339,11 @@ static int arCreateOrUpdateCommand(
rc = arExecSql(pAr, zCreate);
}
for(i=0; i<pAr->nArg && rc==SQLITE_OK; i++){
char *zSql = sqlite3_mprintf(zInsertFmt[pAr->bZip], zTab,
char *zSql2 = sqlite3_mprintf(zInsertFmt[pAr->bZip], zTab,
pAr->bVerbose ? "shell_putsnl(name)" : "name",
pAr->azArg[i], pAr->zDir);
rc = arExecSql(pAr, zSql);
sqlite3_free(zSql);
rc = arExecSql(pAr, zSql2);
sqlite3_free(zSql2);
}
end_ar_transaction:
if( rc!=SQLITE_OK ){
@@ -6611,8 +6621,8 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}
if( zName!=0 ){
int isMaster = sqlite3_strlike(zName, "sqlite_master", 0)==0;
if( isMaster || sqlite3_strlike(zName,"sqlite_temp_master",0)==0 ){
int isMaster = sqlite3_strlike(zName, "sqlite_master", '\\')==0;
if( isMaster || sqlite3_strlike(zName,"sqlite_temp_master", '\\')==0 ){
char *new_argv[2], *new_colv[2];
new_argv[0] = sqlite3_mprintf(
"CREATE TABLE %s (\n"
@@ -6672,13 +6682,18 @@ static int do_meta_command(char *zLine, ShellState *p){
appendText(&sSelect, ") WHERE ", 0);
if( zName ){
char *zQarg = sqlite3_mprintf("%Q", zName);
int bGlob = strchr(zName, '*') != 0 || strchr(zName, '?') != 0 ||
strchr(zName, '[') != 0;
if( strchr(zName, '.') ){
appendText(&sSelect, "lower(printf('%s.%s',sname,tbl_name))", 0);
}else{
appendText(&sSelect, "lower(tbl_name)", 0);
}
appendText(&sSelect, strchr(zName, '*') ? " GLOB " : " LIKE ", 0);
appendText(&sSelect, bGlob ? " GLOB " : " LIKE ", 0);
appendText(&sSelect, zQarg, 0);
if( !bGlob ){
appendText(&sSelect, " ESCAPE '\\' ", 0);
}
appendText(&sSelect, " AND ", 0);
sqlite3_free(zQarg);
}
@@ -7093,7 +7108,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}else{
zLike = z;
bSeparate = 1;
if( sqlite3_strlike("sqlite_%", zLike, 0)==0 ) bSchema = 1;
if( sqlite3_strlike("sqlite\\_%", zLike, '\\')==0 ) bSchema = 1;
}
}
if( bSchema ){
@@ -8399,6 +8414,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
#endif
}else if( strcmp(z,"-append")==0 ){
data.openMode = SHELL_OPEN_APPENDVFS;
}else if( strcmp(z,"-readonly")==0 ){
data.openMode = SHELL_OPEN_READONLY;
}else if( strcmp(z,"-ascii")==0 ){
data.mode = MODE_Ascii;
sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator,
+28 -5
View File
@@ -506,6 +506,7 @@ int sqlite3_exec(
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
#define SQLITE_BUSY_DEADLOCK (SQLITE_BUSY | (3<<8))
#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))
#define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8))
#define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8))
@@ -1064,6 +1065,12 @@ struct sqlite3_io_methods {
** so that all subsequent write operations are independent.
** ^SQLite will never invoke SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE without
** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE].
**
** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]]
** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode causes attempts to obtain
** a file lock using the xLock or xShmLock methods of the VFS to wait
** for up to M milliseconds before failing, where M is the single
** unsigned integer parameter.
** </ul>
*/
#define SQLITE_FCNTL_LOCKSTATE 1
@@ -1098,6 +1105,13 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_BEGIN_ATOMIC_WRITE 31
#define SQLITE_FCNTL_COMMIT_ATOMIC_WRITE 32
#define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33
#define SQLITE_FCNTL_LOCK_TIMEOUT 34
#define SQLITE_FCNTL_FILEID 35
#define SQLITE_FCNTL_SERVER_MODE 36
#define SQLITE_FCNTL_SERVER_SHMOPEN 37
#define SQLITE_FCNTL_SERVER_SHMOPEN2 38
#define SQLITE_FCNTL_SERVER_SHMLOCK 39
#define SQLITE_FCNTL_SERVER_SHMCLOSE 40
/* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
@@ -2054,11 +2068,13 @@ struct sqlite3_mem_methods {
** connections at all to the database. If so, it performs a checkpoint
** operation before closing the connection. This option may be used to
** override this behaviour. The first parameter passed to this operation
** is an integer - non-zero to disable checkpoints-on-close, or zero (the
** default) to enable them. The second parameter is a pointer to an integer
** is an integer - positive to disable checkpoints-on-close, or zero (the
** default) to enable them, and negative to leave the setting unchanged.
** The second parameter is a pointer to an integer
** into which is written 0 or 1 to indicate whether checkpoints-on-close
** have been disabled - 0 if they are not disabled, 1 if they are.
** </dd>
**
** <dt>SQLITE_DBCONFIG_ENABLE_QPSG</dt>
** <dd>^(The SQLITE_DBCONFIG_ENABLE_QPSG option activates or deactivates
** the [query planner stability guarantee] (QPSG). When the QPSG is active,
@@ -2068,13 +2084,20 @@ struct sqlite3_mem_methods {
** slower. But the QPSG has the advantage of more predictable behavior. With
** the QPSG active, SQLite will always use the same query plan in the field as
** was used during testing in the lab.
** The first argument to this setting is an integer which is 0 to disable
** the QPSG, positive to enable QPSG, or negative to leave the setting
** unchanged. The second parameter is a pointer to an integer into which
** is written 0 or 1 to indicate whether the QPSG is disabled or enabled
** following this call.
** </dd>
**
** <dt>SQLITE_DBCONFIG_TRIGGER_EQP</dt>
** <dd> By default, the output of EXPLAIN QUERY PLAN commands does not
** include output for any operations performed by trigger programs. This
** option is used to set or clear (the default) a flag that governs this
** behavior. The first parameter passed to this operation is an integer -
** non-zero to enable output for trigger programs, or zero to disable it.
** positive to enable output for trigger programs, or zero to disable it,
** or negative to leave the setting unchanged.
** The second parameter is a pointer to an integer into which is written
** 0 or 1 to indicate whether output-for-triggers has been disabled - 0 if
** it is not disabled, 1 if it is.
@@ -8802,7 +8825,7 @@ unsigned char *sqlite3_serialize(
** CAPI3REF: Deserialize a database
**
** The sqlite3_deserialize(D,S,P,N,M,F) interface causes the
** [database connection] D to disconnection from database S and then
** [database connection] D to disconnect from database S and then
** reopen S as an in-memory database based on the serialization contained
** in P. The serialized database P is N bytes in size. M is the size of
** the buffer P, which might be larger than N. If M is larger than N, and
@@ -8849,7 +8872,7 @@ int sqlite3_deserialize(
** is resposible for freeing any dynamically allocated memory.
**
** The SQLITE_DESERIALIZE_RESIZEABLE flag means that SQLite is allowed to
** grow the size of the database usign calls to [sqlite3_realloc64()]. This
** grow the size of the database using calls to [sqlite3_realloc64()]. This
** flag should only be used if SQLITE_DESERIALIZE_FREEONCLOSE is also used.
** Without this flag, the deserialized database cannot increase in size beyond
** the number of bytes specified by the M parameter.
+12 -7
View File
@@ -961,9 +961,10 @@ typedef INT16_TYPE LogEst;
*/
typedef struct BusyHandler BusyHandler;
struct BusyHandler {
int (*xFunc)(void *,int); /* The busy callback */
void *pArg; /* First arg to busy callback */
int nBusy; /* Incremented with each busy call */
int (*xBusyHandler)(void *,int); /* The busy callback */
void *pBusyArg; /* First arg to busy callback */
int nBusy; /* Incremented with each busy call */
u8 bExtraFileArg; /* Include sqlite3_file as callback arg */
};
/*
@@ -1119,6 +1120,7 @@ typedef int VList;
#include "pcache.h"
#include "os.h"
#include "mutex.h"
#include "server.h"
/* The SQLITE_EXTRA_DURABLE compile-time option used to set the default
** synchronous setting to EXTRA. It is no longer supported.
@@ -1344,6 +1346,7 @@ struct sqlite3 {
u16 dbOptFlags; /* Flags to enable/disable optimizations */
u8 enc; /* Text encoding */
u8 autoCommit; /* The auto-commit flag. */
u8 readonlyTrans; /* Transaction opened with BEGIN READONLY */
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 */
@@ -1532,6 +1535,8 @@ struct sqlite3 {
#define SQLITE_CursorHints 0x0400 /* Add OP_CursorHint opcodes */
#define SQLITE_Stat34 0x0800 /* Use STAT3 or STAT4 data */
/* TH3 expects the Stat34 ^^^^^^ value to be 0x0800. Don't change it */
#define SQLITE_PushDown 0x1000 /* The push-down optimization */
#define SQLITE_SimplifyJoin 0x2000 /* Convert LEFT JOIN to JOIN */
#define SQLITE_AllOpts 0xffff /* All optimizations */
/*
@@ -2993,7 +2998,6 @@ struct Parse {
int nMaxArg; /* Max args passed to user function by sub-program */
#if SELECTTRACE_ENABLED
int nSelect; /* Number of SELECT statements seen */
int nSelectIndent; /* How far to indent SELECTTRACE() output */
#endif
#ifndef SQLITE_OMIT_SHARED_CACHE
int nTableLock; /* Number of locks in aTableLock */
@@ -3357,9 +3361,9 @@ struct Walker {
struct CCurHint *pCCurHint; /* Used by codeCursorHint() */
int *aiCol; /* array of column indexes */
struct IdxCover *pIdxCover; /* Check for index coverage */
struct IdxExprTrans *pIdxTrans; /* Convert indexed expr to column */
struct IdxExprTrans *pIdxTrans; /* Convert idxed expr to column */
ExprList *pGroupBy; /* GROUP BY clause */
struct HavingToWhereCtx *pHavingCtx; /* HAVING to WHERE clause ctx */
Select *pSelect; /* HAVING to WHERE clause ctx */
} u;
};
@@ -3823,6 +3827,7 @@ int sqlite3ExprCompare(Parse*,Expr*, Expr*, int);
int sqlite3ExprCompareSkip(Expr*, Expr*, int);
int sqlite3ExprListCompare(ExprList*, ExprList*, int);
int sqlite3ExprImpliesExpr(Parse*,Expr*, Expr*, int);
int sqlite3ExprImpliesNonNullRow(Expr*,int);
void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*);
void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*);
int sqlite3ExprCoveredByIndex(Expr*, int iCur, Index *pIdx);
@@ -4101,7 +4106,7 @@ void sqlite3AlterBeginAddColumn(Parse *, SrcList *);
CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*);
char sqlite3AffinityType(const char*, u8*);
void sqlite3Analyze(Parse*, Token*, Token*);
int sqlite3InvokeBusyHandler(BusyHandler*);
int sqlite3InvokeBusyHandler(BusyHandler*, sqlite3_file*);
int sqlite3FindDb(sqlite3*, Token*);
int sqlite3FindDbName(sqlite3 *, const char *);
int sqlite3AnalysisLoad(sqlite3*,int iDB);
+1 -1
View File
@@ -324,7 +324,7 @@ static int SQLITE_TCLAPI page_get(
}
pPager = sqlite3TestTextToPtr(argv[1]);
if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR;
rc = sqlite3PagerSharedLock(pPager);
rc = sqlite3PagerSharedLock(pPager, 0);
if( rc==SQLITE_OK ){
rc = sqlite3PagerGet(pPager, pgno, &pPage, 0);
}
+6
View File
@@ -762,6 +762,12 @@ Tcl_SetVar2(interp, "sqlite_options", "mergesort", "1", TCL_GLOBAL_ONLY);
Tcl_SetVar2(interp, "sqlite_options", "uri_00_error", "0", TCL_GLOBAL_ONLY);
#endif
#ifdef SQLITE_SERVER_EDITION
Tcl_SetVar2(interp, "sqlite_options", "server", "1", TCL_GLOBAL_ONLY);
#else
Tcl_SetVar2(interp, "sqlite_options", "server", "0", TCL_GLOBAL_ONLY);
#endif
#define LINKVAR(x) { \
static const int cv_ ## x = SQLITE_ ## x; \
Tcl_LinkVar(interp, "SQLITE_" #x, (char *)&(cv_ ## x), \
+10
View File
@@ -137,11 +137,21 @@ void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){
sqlite3TreeViewPush(pView, 1);
}
do{
#if SELECTTRACE_ENABLED
sqlite3TreeViewLine(pView,
"SELECT%s%s (%s/%p) selFlags=0x%x nSelectRow=%d",
((p->selFlags & SF_Distinct) ? " DISTINCT" : ""),
((p->selFlags & SF_Aggregate) ? " agg_flag" : ""),
p->zSelName, p, p->selFlags,
(int)p->nSelectRow
);
#else
sqlite3TreeViewLine(pView, "SELECT%s%s (0x%p) selFlags=0x%x nSelectRow=%d",
((p->selFlags & SF_Distinct) ? " DISTINCT" : ""),
((p->selFlags & SF_Aggregate) ? " agg_flag" : ""), p, p->selFlags,
(int)p->nSelectRow
);
#endif
if( cnt++ ) sqlite3TreeViewPop(pView);
if( p->pPrior ){
n = 1000;
+34 -1
View File
@@ -3096,7 +3096,7 @@ 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
@@ -3113,6 +3113,7 @@ case OP_AutoCommit: {
iRollback = pOp->p2;
assert( desiredAutoCommit==1 || desiredAutoCommit==0 );
assert( desiredAutoCommit==1 || iRollback==0 );
assert( desiredAutoCommit==0 || pOp->p3==0 );
assert( db->nVdbeActive>0 ); /* At least this one VM is active */
assert( p->bIsReader );
@@ -3144,6 +3145,7 @@ case OP_AutoCommit: {
sqlite3CloseSavepoints(db);
if( p->rc==SQLITE_OK ){
rc = SQLITE_DONE;
db->readonlyTrans = (pOp->p3==TK_READONLY);
}else{
rc = SQLITE_ERROR;
}
@@ -3351,6 +3353,33 @@ case OP_SetCookie: {
break;
}
#ifdef SQLITE_SERVER_EDITION
/* Opcode: FreelistFmt P1 P2 P3 * *
**
** Parameter P3 must be 0, 1 or 2. If it is not 0, attempt to set the
** freelist format of database P1 to format 1 or format 2. Before
** returning, store the final freelist format (either 1 or 2) of
** database P1 into register P2.
*/
case OP_FreelistFmt: { /* out2 */
Db *pDb;
int iVal;
assert( pOp->p1>=0 && pOp->p1<db->nDb );
assert( DbMaskTest(p->btreeMask, pOp->p1) );
assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) );
pDb = &db->aDb[pOp->p1];
assert( pDb->pBt!=0 );
pOut = out2Prerelease(p, pOp);
rc = sqlite3BtreeFreelistFormat(pDb->pBt, pOp->p3, &iVal);
if( rc ) goto abort_due_to_error;
pOut->u.i = iVal;
break;
}
#endif
/* Opcode: OpenRead P1 P2 P3 P4 P5
** Synopsis: root=P2 iDb=P3
**
@@ -4287,6 +4316,10 @@ case OP_NewRowid: { /* out2 */
pOut = out2Prerelease(p, pOp);
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
if( !pC->isTable ){
rc = SQLITE_CORRUPT_BKPT;
goto abort_due_to_error;
}
assert( pC!=0 );
assert( pC->eCurType==CURTYPE_BTREE );
assert( pC->uc.pCursor!=0 );
+7 -6
View File
@@ -2662,12 +2662,13 @@ int sqlite3VdbeHalt(Vdbe *p){
/* Check for one of the special errors */
mrc = p->rc & 0xff;
isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR
|| mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL;
|| mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL
|| p->rc==SQLITE_BUSY_DEADLOCK;
if( isSpecialError ){
/* If the query was read-only and the error code is SQLITE_INTERRUPT,
** no rollback is necessary. Otherwise, at least a savepoint
** transaction must be rolled back to restore the database to a
** consistent state.
/* If the query was read-only and the error code is SQLITE_INTERRUPT
** or SQLITE_BUSY_SERVER, no rollback is necessary. Otherwise, at
** least a savepoint transaction must be rolled back to restore the
** database to a consistent state.
**
** Even if the statement is read-only, it is important to perform
** a statement or transaction rollback operation. If the error
@@ -2676,7 +2677,7 @@ int sqlite3VdbeHalt(Vdbe *p){
** pagerStress() in pager.c), the rollback is required to restore
** the pager to a consistent state.
*/
if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){
if( !p->readOnly || (mrc!=SQLITE_INTERRUPT && mrc!=SQLITE_BUSY) ){
if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL) && p->usesStmtJournal ){
eStatementOp = SAVEPOINT_ROLLBACK;
}else{
+17 -1
View File
@@ -349,7 +349,7 @@ ifcapable stat4||stat3 {
# This test corrupts the database file so it must be the last test
# in the series.
#
do_test analyze-99.1 {
do_test analyze-5.99 {
execsql {
PRAGMA writable_schema=on;
UPDATE sqlite_master SET sql='nonsense' WHERE name='sqlite_stat1';
@@ -361,4 +361,20 @@ do_test analyze-99.1 {
}
} {1 {malformed database schema (sqlite_stat1)}}
# Verify that tables whose names begin with "sqlite" but not
# "sqlite_" are analyzed.
#
db close
sqlite3 db :memory:
do_execsql_test analyze-6.1 {
CREATE TABLE sqliteDemo(a);
INSERT INTO sqliteDemo(a) VALUES(1),(2),(3),(4),(5);
CREATE TABLE SQLiteDemo2(a INTEGER PRIMARY KEY AUTOINCREMENT);
INSERT INTO SQLiteDemo2 SELECT * FROM sqliteDemo;
CREATE TABLE t1(b);
INSERT INTO t1(b) SELECT a FROM sqliteDemo;
ANALYZE;
SELECT tbl FROM sqlite_stat1 WHERE idx IS NULL ORDER BY tbl;
} {SQLiteDemo2 sqliteDemo t1}
finish_test
+2 -1
View File
@@ -22,7 +22,7 @@ source $testdir/tester.tcl
# Create several tables to work with.
#
do_test avtrans-1.0 {
execsql { PRAGMA auto_vacuum=ON }
execsql { PRAGMA auto_vacuum=full }
wal_set_journal_mode
execsql {
CREATE TABLE one(a int PRIMARY KEY, b text);
@@ -32,6 +32,7 @@ do_test avtrans-1.0 {
SELECT b FROM one ORDER BY a;
}
} {one two three}
do_test avtrans-1.0.1 { execsql { PRAGMA auto_vacuum } } 1
do_test avtrans-1.1 {
execsql {
CREATE TABLE two(a int PRIMARY KEY, b text);
+41 -36
View File
@@ -136,45 +136,50 @@ do_extract_hints_test 2.5 {
x2 {EQ(c0,r[2])}
}
do_extract_hints_test 2.6 {
SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE 0 = (b IS NOT NULL)
} {
x2 {EQ(c0,r[2])}
}
do_extract_hints_test 2.7 {
SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE 0 = (b IS NOT +NULL)
} {
x2 {EQ(c0,r[2])}
}
do_extract_hints_test 2.8 {
SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE b IS NOT +NULL
} {
x2 {EQ(c0,r[2])}
}
do_extract_hints_test 2.9 {
SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE CASE b WHEN 0 THEN 0 ELSE 1 END;
} {
x2 {EQ(c0,r[2])}
}
do_extract_hints_test 2.10 {
SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE x2.b = 32+32
} {
x2 {AND(EQ(c1,ADD(32,32)),EQ(c0,r[2]))}
}
ifcapable !icu {
# This test only works using the built-in LIKE, not the ICU LIKE extension.
do_extract_hints_test 2.11 {
SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE x2.b LIKE 'abc%'
if {0} {
# These tests no longer work due to the LEFT-JOIN strength reduction
# optimization
do_extract_hints_test 2.6 {
SELECT * FROM x1 CROSS JOIN x2 ON (a=x) WHERE 0 = (b IS NOT NULL)
} {
x2 {AND(expr,EQ(c0,r[2]))}
x2 {EQ(c0,r[2])}
}
do_extract_hints_test 2.7 {
SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE 0 = (b IS NOT +NULL)
} {
x2 {EQ(c0,r[2])}
}
do_extract_hints_test 2.8 {
SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE b IS NOT +NULL
} {
x2 {EQ(c0,r[2])}
}
do_extract_hints_test 2.9 {
SELECT * FROM x1 LEFT JOIN x2 ON (a=x)
WHERE CASE b WHEN 0 THEN 0 ELSE 1 END;
} {
x2 {EQ(c0,r[2])}
}
do_extract_hints_test 2.10 {
SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE x2.b = 32+32
} {
x2 {AND(EQ(c1,ADD(32,32)),EQ(c0,r[2]))}
}
ifcapable !icu {
# This test only works using the built-in LIKE, not the ICU LIKE extension.
do_extract_hints_test 2.11 {
SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE x2.b LIKE 'abc%'
} {
x2 {AND(expr,EQ(c0,r[2]))}
}
}
}
do_extract_hints_test 2.12 {
SELECT * FROM x1 LEFT JOIN x2 ON (a=x) WHERE coalesce(x2.b, 1)
} {
+1 -1
View File
@@ -110,6 +110,6 @@ do_execsql_test 3.2 {
PRAGMA cache_size=3;
UPDATE t1 SET b=randomblob(1000);
} {delete}
do_test 3.2 { db_spill db 0 } {0 8 0}
do_test 3.3 { db_spill db 0 } {0 8 0}
finish_test
+1 -1
View File
@@ -748,7 +748,7 @@ do_execsql_test e_select-3.2.1a {
SELECT k FROM x1 LEFT JOIN x2 USING(k)
} {1 2 3 4 5 6}
do_execsql_test e_select-3.2.1b {
SELECT k FROM x1 LEFT JOIN x2 USING(k) WHERE x2.k
SELECT k FROM x1 LEFT JOIN x2 USING(k) WHERE x2.k ORDER BY +k
} {1 3 5}
do_execsql_test e_select-3.2.2 {
SELECT k FROM x1 LEFT JOIN x2 USING(k) WHERE x2.k IS NULL
+20
View File
@@ -123,4 +123,24 @@ do_catchsql_test istrue-524 {
INSERT INTO t2 VALUES(2,true,false,null,false);
} {1 {CHECK constraint failed: t2}}
foreach {tn val} [list 1 NaN 2 -NaN 3 NaN0 4 -NaN0 5 Inf 6 -Inf] {
do_execsql_test istrue-600.$tn.1 {
DROP TABLE IF EXISTS t1;
CREATE TABLE t1(x);
}
do_test istrue-600.$tn.2 {
set ::STMT [sqlite3_prepare db "INSERT INTO t1 VALUES(?)" -1 TAIL]
sqlite3_bind_double $::STMT 1 $val
sqlite3_step $::STMT
sqlite3_reset $::STMT
sqlite3_finalize $::STMT
} {SQLITE_OK}
do_execsql_test istrue-600.$tn.3 {
SELECT x IS TRUE FROM t1;
} [expr {$tn in [list 5 6] ? {1} : {0}}]
do_execsql_test istrue-600.$tn.4 {
SELECT x IS FALSE FROM t1;
} {0}
}
finish_test
+54
View File
@@ -780,4 +780,58 @@ do_execsql_test join-14.20 {
LEFT JOIN t3 AS x3 ON x2.id=x3.c3;
} {456 {} {}}
# 2018-03-24.
# E.Pasma discovered that the LEFT JOIN strength reduction optimization
# was misbehaving. The problem turned out to be that the
# sqlite3ExprImpliesNotNull() routine was saying that CASE expressions
# like
#
# CASE WHEN true THEN true ELSE x=0 END
#
# could never be true if x is NULL. The following test cases verify
# that this error has been resolved.
#
db close
sqlite3 db :memory:
do_execsql_test join-15.100 {
CREATE TABLE t1(a INT, b INT);
INSERT INTO t1 VALUES(1,2),(3,4);
CREATE TABLE t2(x INT, y INT);
SELECT *, 'x'
FROM t1 LEFT JOIN t2
WHERE CASE WHEN FALSE THEN a=x ELSE 1 END;
} {1 2 {} {} x 3 4 {} {} x}
do_execsql_test join-15.105 {
SELECT *, 'x'
FROM t1 LEFT JOIN t2
WHERE a IN (1,3,x,y);
} {1 2 {} {} x 3 4 {} {} x}
do_execsql_test join-15.110 {
DROP TABLE t1;
DROP TABLE t2;
CREATE TABLE t1(a INTEGER PRIMARY KEY, b INTEGER);
INSERT INTO t1(a,b) VALUES(1,0),(11,1),(12,1),(13,1),(121,12);
CREATE INDEX t1b ON t1(b);
CREATE TABLE t2(x INTEGER PRIMARY KEY);
INSERT INTO t2(x) VALUES(0),(1);
SELECT a1, a2, a3, a4, a5
FROM (SELECT a AS a1 FROM t1 WHERE b=0)
JOIN (SELECT x AS x1 FROM t2)
LEFT JOIN (SELECT a AS a2, b AS b2 FROM t1)
ON x1 IS TRUE AND b2=a1
JOIN (SELECT x AS x2 FROM t2)
ON x2<=CASE WHEN x1 THEN CASE WHEN a2 THEN 1 ELSE -1 END ELSE 0 END
LEFT JOIN (SELECT a AS a3, b AS b3 FROM t1)
ON x2 IS TRUE AND b3=a2
JOIN (SELECT x AS x3 FROM t2)
ON x3<=CASE WHEN x2 THEN CASE WHEN a3 THEN 1 ELSE -1 END ELSE 0 END
LEFT JOIN (SELECT a AS a4, b AS b4 FROM t1)
ON x3 IS TRUE AND b4=a3
JOIN (SELECT x AS x4 FROM t2)
ON x4<=CASE WHEN x3 THEN CASE WHEN a4 THEN 1 ELSE -1 END ELSE 0 END
LEFT JOIN (SELECT a AS a5, b AS b5 FROM t1)
ON x4 IS TRUE AND b5=a4
ORDER BY a1, a2, a3, a4, a5;
} {1 {} {} {} {} 1 11 {} {} {} 1 12 {} {} {} 1 12 121 {} {} 1 13 {} {} {}}
finish_test
+15 -1
View File
@@ -86,7 +86,7 @@ do_execsql_test 2.0 {
}
do_catchsql_test 2.1 {
SELECT * FROM aa LEFT JOIN cc ON (a=b) JOIN bb ON (b=c);
SELECT * FROM aa LEFT JOIN cc ON (a=b) JOIN bb ON (b=coalesce(c,1));
} {1 {ON clause references tables to its right}}
do_catchsql_test 2.2 {
SELECT * FROM aa JOIN cc ON (a=b) JOIN bb ON (b=c);
@@ -265,4 +265,18 @@ do_eqp_test 6.1 {
0 0 0 {SCAN TABLE u2}
}
db close
sqlite3 db :memory:
do_execsql_test 7.0 {
CREATE TABLE t1(a,b); INSERT INTO t1 VALUES(1,2),(3,4),(5,6);
CREATE TABLE t2(c,d); INSERT INTO t2 VALUES(2,4),(3,6);
CREATE TABLE t3(x); INSERT INTO t3 VALUES(9);
CREATE VIEW test AS
SELECT *, 'x'
FROM t1 LEFT JOIN (SELECT * FROM t2, t3) ON (c=b AND x=9)
WHERE c IS NULL;
SELECT * FROM test;
} {3 4 {} {} {} x 5 6 {} {} {} x}
finish_test
+2 -2
View File
@@ -164,7 +164,7 @@ do_execsql_test join5-3.3 {
# Ticket https://www.sqlite.org/src/tktview/c2a19d81652f40568c770c43 on
# 2015-08-20. LEFT JOIN and the push-down optimization.
#
do_execsql_test join6-4.1 {
do_execsql_test join5-4.1 {
SELECT *
FROM (
SELECT 'apple' fruit
@@ -178,7 +178,7 @@ do_execsql_test join6-4.1 {
SELECT 1 isyellow
) c ON b.fruit='banana';
} {apple apple {} banana banana 1}
do_execsql_test join6-4.2 {
do_execsql_test join5-4.2 {
SELECT *
FROM (SELECT 'apple' fruit UNION ALL SELECT 'banana')
LEFT JOIN (SELECT 1) ON fruit='banana';
+948
View File
@@ -0,0 +1,948 @@
/* content of file testdb01.db */
unsigned char data001[] = {
83, 81, 76,105,116,101, 32,102,111,114,109, 97,116, 32, 51, 0, 2, 0, 1,
1, 0, 64, 32, 32, 0, 0, 0, 2, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 31, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2, 0, 46, 32,152, 5, 0, 0, 0, 7, 1,221, 0, 0, 0, 0, 35, 1,251,
1,246, 1,241, 1,236, 1,231, 1,226, 1,221, 84, 4, 7, 23, 17, 17, 1,
129, 19,116, 97, 98,108,101,116, 52,116, 52, 5, 67, 82, 69, 65, 84, 69, 32,
84, 65, 66, 76, 69, 32,116, 52, 40, 97, 32, 73, 78, 84, 32, 85, 78, 73, 81,
85, 69, 32, 78, 79, 84, 32, 78, 85, 76, 76, 44, 32, 98, 32, 73, 78, 84, 32,
85, 78, 73, 81, 85, 69, 32, 78, 79, 84, 32, 78, 85, 76, 76, 44, 99, 44,100,
44,101, 41, 35, 6, 6, 23, 55, 17, 1, 0,105,110,100,101,120,115,113,108,
105,116,101, 95, 97,117,116,111,105,110,100,101,120, 95,116, 52, 95, 50,116,
52, 7, 35, 5, 6, 23, 55, 17, 1, 0,105,110,100,101,120,115,113,108,105,
116,101, 95, 97,117,116,111,105,110,100,101,120, 95,116, 52, 95, 49,116, 52,
6, 42, 3, 6, 23, 17, 17, 1, 65,116, 97, 98,108,101,116, 51,116, 51, 4,
67, 82, 69, 65, 84, 69, 32, 84, 65, 66, 76, 69, 32,116, 51, 40, 97, 44, 98,
44, 99, 44,100, 44,101, 41, 95, 2, 7, 23, 17, 17, 1,129, 41,116, 97, 98,
108,101,116, 50,116, 50, 3, 67, 82, 69, 65, 84, 69, 32, 84, 65, 66, 76, 69,
32,116, 50, 40, 97, 32, 73, 78, 84, 44, 32, 98, 32, 73, 78, 84, 44, 32, 99,
32, 73, 78, 84, 44,100, 32, 73, 78, 84, 44,101, 32, 73, 78, 84, 44, 80, 82,
73, 77, 65, 82, 89, 32, 75, 69, 89, 40, 98, 44, 97, 41, 41, 87, 73, 84, 72,
79, 85, 84, 32, 82, 79, 87, 73, 68, 83, 1, 7, 23, 17, 17, 1,129, 17,116,
97, 98,108,101,116, 49,116, 49, 2, 67, 82, 69, 65, 84, 69, 32, 84, 65, 66,
76, 69, 32,116, 49, 40, 97, 32, 73, 78, 84, 69, 71, 69, 82, 32, 80, 82, 73,
77, 65, 0, 0, 0, 34, 32, 0, 0, 0, 33, 29, 0, 0, 0, 32, 26, 0, 0,
0, 31, 23, 0, 0, 0, 30, 19, 0, 0, 0, 11, 14, 0, 0, 0, 9, 7, 5,
0, 0, 0, 1, 1,251, 0, 0, 0, 0, 16, 1,251, 1,195, 1,180, 1,166,
1,151, 1,136, 1,121, 1,105, 1, 91, 1, 76, 1, 61, 1, 46, 1, 29, 1,
14, 0,252, 0,238, 0,224, 0,209, 0,194, 0,177, 0,157, 0,143, 0,128,
0,110, 0, 94, 0, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 14, 28, 6, 0, 1, 1, 1, 23, 17, 67, 31,119,111,114,107,115, 14, 27,
6, 0, 1, 1, 1, 23, 22, 71, 3, 97,110,103,101,108, 16, 26, 6, 0, 1,
1, 1, 27, 40, 98, 17,109,111,114,110,105,110,103, 13, 25, 6, 0, 1, 1,
1, 21, 10, 7, 19,103,111,110,101, 12, 24, 6, 0, 1, 1, 9, 21, 43, 46,
119, 97,121,115, 18, 23, 6, 0, 1, 1, 1, 31, 6, 37, 31,115, 97, 99,114,
105,102,105, 99,101, 15, 22, 6, 0, 1, 1, 1, 25, 45, 71, 28,116,104,111,
117,103,104, 13, 21, 6, 0, 1, 1, 1, 21, 22, 92, 18,115,111,109,101, 13,
20, 6, 0, 9, 1, 1, 23, 2, 45, 97, 98,111,118,101, 12, 19, 6, 0, 1,
1, 8, 21, 4, 58,119, 97,121,115, 12, 18, 6, 0, 1, 1, 1, 19, 44, 19,
43,119, 97,114, 16, 17, 6, 0, 1, 1, 1, 27, 29, 74, 36, 98,101,116,119,
101,101,110, 13, 16, 6, 0, 1, 1, 1, 21, 44, 52, 19,112,111,111,114, 15,
15, 6, 0, 1, 1, 1, 25, 6, 3, 11,116,101,109,112,108,101, 13, 14, 6,
0, 1, 1, 1, 21, 35, 48, 27,100,105,101,100, 13, 13, 6, 0, 1, 1, 1,
21, 4, 21, 39,100,111,116,104, 13, 12, 6, 0, 1, 1, 1, 21, 4, 38, 36,
115,101,110,100, 12, 11, 6, 0, 1, 1, 1, 19, 13, 48, 22,115,105,120, 14,
10, 6, 0, 1, 1, 1, 23, 41, 89, 14,115,101,114,118,101, 13, 9, 6, 0,
8, 1, 1, 23, 16, 50, 98,101,103, 97,116, 13, 8, 6, 0, 1, 1, 1, 21,
42, 49, 34,115,101,110,100, 13, 7, 6, 0, 1, 1, 1, 21, 21, 91, 38,110,
101, 97,114, 12, 6, 6, 0, 1, 1, 1, 19, 2, 37, 11, 99, 97,110, 13, 5,
6, 0, 1, 1, 1, 21, 25, 27, 28,103,111,110,101, 13, 4, 6, 0, 1, 1,
1, 21, 41, 32, 35,110,101, 97,114, 14, 3, 6, 0, 1, 1, 1, 23, 32, 24,
26,115,101,114,118,101, 13, 2, 6, 0, 1, 1, 1, 21, 45, 14, 39,115, 97,
118,101, 13, 1, 6, 0, 1, 1, 1, 21, 40, 68, 0, 0, 0, 15, 28, 2, 0,
0, 0, 1, 1,238, 0, 0, 0, 0, 22, 1,238, 1,197, 1,181, 1,166, 1,
151, 1,137, 1,121, 1,104, 1, 84, 1, 73, 1, 59, 1, 41, 1, 26, 1, 11,
0,253, 0,238, 0,223, 0,207, 0,191, 0,175, 0,159, 0,144, 0,129, 0,
113, 0, 97, 0, 82, 0, 68, 0, 0, 13, 6, 1, 1, 1, 1, 19, 26, 34, 15,
20, 97,114,107, 14, 6, 1, 1, 1, 1, 21, 25, 5, 27, 28,103,111,110,101,
15, 6, 1, 1, 1, 1, 23, 22, 47, 16, 40, 97,110,103,101,114, 15, 6, 1,
1, 1, 1, 23, 22, 27, 71, 3, 97,110,103,101,108, 14, 6, 1, 1, 1, 1,
21, 22, 21, 92, 18,115,111,109,101, 14, 6, 1, 1, 1, 1, 21, 21, 7, 91,
38,110,101, 97,114, 15, 6, 1, 1, 1, 1, 23, 20, 42, 18, 5, 98,101,103,
97,116, 15, 6, 1, 1, 1, 1, 23, 17, 37, 66, 18,100,119,101,108,116, 15,
6, 1, 1, 1, 1, 23, 17, 28, 67, 31,119,111,114,107,115, 15, 6, 1, 1,
1, 8, 25, 16, 32, 7,112,108, 97, 99,101,115, 14, 6, 1, 1, 1, 1, 21,
16, 30, 81, 25,119, 97,108,107, 14, 6, 1, 1, 1, 1, 21, 14, 40, 30, 26,
115,101,110,100, 13, 6, 1, 1, 1, 1, 19, 13, 11, 48, 22,115,105,120, 14,
6, 1, 1, 1, 1, 21, 10, 38, 97, 34,115,104,101,119, 14, 6, 1, 1, 1,
1, 21, 10, 25, 7, 19,103,111,110,101, 17, 6, 1, 1, 1, 1, 27, 9, 50,
92, 29,116,104,101,114,101,105,110, 13, 6, 1, 1, 1, 1, 19, 9, 49, 51,
38,111,105,108, 10, 6, 1, 1, 1, 1, 0, 7, 33, 72, 31, 19, 6, 1, 1,
1, 1, 31, 6, 23, 37, 31,115, 97, 99,114,105,102,105, 99,101, 16, 6, 1,
1, 1, 1, 25, 6, 15, 3, 11,116,101,109,112,108,101, 15, 6, 1, 1, 1,
1, 23, 5, 43, 23, 41, 98,101,103, 97,116, 13, 6, 1, 1, 1, 8, 21, 4,
19, 58,119, 97,121,115, 14, 6, 1, 1, 1, 1, 21, 4, 13, 21, 39,100,111,
116,104, 14, 6, 1, 1, 1, 1, 21, 4, 12, 38, 36,115,101,110,100, 15, 6,
1, 1, 1, 1, 23, 3, 39, 21, 45, 98,101,103, 97,116, 13, 6, 1, 1, 1,
1, 19, 2, 6, 37, 11, 99, 97,110, 14, 6, 9, 1, 1, 1, 23, 20, 2, 45,
97, 98,111,118,101, 14, 6, 8, 1, 1, 1, 23, 36, 52, 17, 99,104, 0, 0,
0, 21, 13, 6, 1, 1, 1, 1, 19, 26, 34, 15, 20, 97,114,107, 13, 0, 0,
0, 35, 0, 92, 0, 1,244, 1,232, 1,216, 1,204, 1,186, 1,171, 1,160,
1,149, 1,138, 1,128, 1,117, 1,106, 1, 92, 1, 76, 1, 65, 1, 49, 1,
32, 1, 21, 1, 10, 0,255, 0,241, 0,225, 0,214, 0,203, 0,192, 0,180,
0,168, 0,156, 0,144, 0,132, 0,124, 0,116, 0,108, 0,100, 0, 92, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 35, 6, 0, 0, 0,
0, 0, 6, 34, 6, 0, 0, 0, 0, 0, 6, 33, 6, 0, 0, 0, 0, 0, 6,
32, 6, 0, 0, 0, 0, 0, 6, 31, 6, 0, 0, 0, 0, 0, 10, 30, 6, 1,
1, 1, 1, 0, 48, 37, 93, 7, 10, 29, 6, 1, 1, 1, 1, 0, 28, 17, 67,
31, 10, 28, 6, 1, 1, 1, 1, 0, 22, 45, 71, 28, 10, 27, 6, 1, 1, 1,
1, 0, 12, 4, 38, 36, 10, 26, 6, 1, 1, 1, 1, 0, 49, 9, 51, 38, 9,
25, 6, 1, 1, 1, 0, 0, 17, 29, 74, 9, 24, 6, 1, 1, 1, 0, 0, 47,
22, 16, 9, 23, 6, 1, 1, 1, 0, 0, 32, 16, 7, 14, 22, 6, 1, 1, 1,
0, 23, 42, 20, 18, 98,101,103, 97,116, 12, 21, 6, 1, 1, 1, 0, 19, 34,
26, 15, 97,114,107, 9, 20, 6, 1, 1, 0, 1, 0, 49, 9, 38, 9, 19, 6,
1, 1, 0, 1, 0, 44, 48, 9, 9, 18, 6, 1, 1, 0, 1, 0, 21, 22, 18,
15, 17, 6, 1, 1, 0, 1, 25, 35, 38, 22, 99,117, 98,105,116,115, 14, 16,
6, 1, 1, 0, 1, 23, 37, 17, 18,100,119,101,108,116, 9, 15, 6, 1, 0,
1, 1, 0, 49, 51, 38, 14, 14, 6, 1, 0, 1, 1, 23, 10, 89, 14,115,101,
114,118,101, 12, 13, 6, 9, 0, 1, 1, 21, 68, 32,100,111,116,104, 9, 12,
6, 1, 0, 1, 1, 0, 47, 16, 40, 9, 11, 6, 1, 0, 1, 1, 0, 25, 7,
19, 8, 10, 6, 0, 1, 1, 8, 0, 16, 7, 9, 9, 6, 0, 1, 1, 1, 0,
16, 81, 25, 9, 8, 6, 0, 1, 1, 1, 0, 7, 72, 31, 9, 7, 6, 0, 1,
1, 1, 0, 6, 37, 31, 13, 6, 6, 0, 1, 1, 1, 21, 21, 91, 38,110,101,
97,114, 16, 5, 6, 1, 1, 1, 1, 25, 15, 6, 3, 11,116,101,109,112,108,
101, 10, 4, 6, 1, 1, 1, 1, 0, 21, 22, 92, 18, 14, 3, 6, 1, 1, 1,
1, 21, 4, 41, 32, 35,110,101, 97,114, 10, 2, 6, 1, 1, 1, 1, 0, 46,
28, 88, 22, 10, 1, 6, 1, 1, 1, 1, 0, 17, 29, 74, 36, 13, 0, 0, 0,
15, 1, 71, 0, 1,243, 1,230, 1,217, 1,204, 1,191, 1,179, 1,167, 1,
155, 1,143, 1,131, 1,119, 1,107, 1, 95, 1, 83, 1, 71, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
10, 15, 6, 1, 1, 1, 1, 0, 48, 37, 93, 7, 10, 14, 6, 1, 1, 1, 1,
0, 22, 45, 71, 28, 10, 13, 6, 1, 1, 1, 1, 0, 12, 4, 38, 36, 10, 12,
6, 1, 1, 1, 0, 1, 32, 16, 7, 79, 10, 11, 6, 1, 1, 1, 0, 1, 42,
20, 18, 19, 10, 10, 6, 1, 1, 1, 0, 1, 34, 26, 15, 13, 10, 9, 6, 1,
1, 0, 1, 1, 49, 9, 38, 97, 10, 8, 6, 1, 1, 0, 1, 1, 44, 48, 9,
90, 10, 7, 6, 1, 1, 0, 1, 1, 35, 38, 22, 33, 10, 6, 6, 1, 1, 0,
1, 1, 37, 17, 18, 18, 11, 5, 6, 1, 1, 1, 1, 1, 15, 6, 3, 11, 43,
11, 4, 6, 1, 1, 1, 1, 1, 21, 22, 92, 18, 62, 11, 3, 6, 1, 1, 1,
1, 1, 4, 41, 32, 35, 36, 11, 2, 6, 1, 1, 1, 1, 1, 46, 28, 88, 22,
77, 11, 1, 6, 1, 1, 1, 1, 1, 17, 29, 74, 36, 61, 10, 0, 0, 0, 15,
1,167, 0, 1,250, 1,244, 1,238, 1,233, 1,227, 1,221, 1,215, 1,209,
1,203, 1,197, 1,191, 1,185, 1,179, 1,173, 1,167, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
5, 3, 1, 1, 49, 9, 5, 3, 1, 1, 48, 15, 5, 3, 1, 1, 46, 2, 5,
3, 1, 1, 44, 8, 5, 3, 1, 1, 42, 11, 5, 3, 1, 1, 37, 6, 5, 3,
1, 1, 35, 7, 5, 3, 1, 1, 34, 10, 5, 3, 1, 1, 32, 12, 5, 3, 1,
1, 22, 14, 5, 3, 1, 1, 21, 4, 4, 3, 1, 9, 17, 5, 3, 1, 1, 15,
5, 5, 3, 1, 1, 12, 13, 5, 3, 1, 1, 4, 3, 10, 0, 0, 0, 15, 1,
167, 0, 1,250, 1,244, 1,238, 1,232, 1,226, 1,220, 1,214, 1,208, 1,
202, 1,197, 1,191, 1,185, 1,179, 1,173, 1,167, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5,
3, 1, 1, 48, 8, 5, 3, 1, 1, 45, 14, 5, 3, 1, 1, 41, 3, 5, 3,
1, 1, 38, 7, 5, 3, 1, 1, 37, 15, 4, 3, 1, 9, 29, 5, 3, 1, 1,
28, 2, 5, 3, 1, 1, 26, 10, 5, 3, 1, 1, 22, 4, 5, 3, 1, 1, 20,
11, 5, 3, 1, 1, 17, 6, 5, 3, 1, 1, 16, 12, 5, 3, 1, 1, 9, 9,
5, 3, 1, 1, 6, 5, 5, 3, 1, 1, 4, 13, 5, 0, 0, 0, 2, 1,246,
0, 0, 0, 0, 27, 1,251, 1,246, 1,168, 1,148, 1,130, 1,107, 1, 86,
1, 65, 1, 44, 1, 27, 1, 14, 0,250, 0,224, 0,205, 0,184, 0,165, 0,
145, 0,123, 0,106, 0, 86, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 17, 23, 6, 0, 23, 1, 1, 21,107,110,111,119,110, 52, 19,112,
111,111,114, 18, 22, 6, 0, 23, 1, 1, 23, 97, 98,111,118,101, 24, 26,115,
101,114,118,101, 15, 21, 6, 0, 19, 1, 1, 21,119, 97,114, 52, 19,112,111,
111,114, 20, 20, 6, 0, 27, 1, 8, 25,110,111,116,104,105,110,103, 7,112,
108, 97, 99,101,115, 18, 19, 6, 0, 23, 1, 1, 23, 98,101,103, 97,116, 90,
27,116,114,117,116,104, 17, 18, 6, 0, 23, 1, 1, 21,100,119,101,108,116,
21, 39,100,111,116,104, 19, 17, 6, 0, 27, 1, 1, 21,109,111,114,110,105,
110,103, 52, 19,112,111,111,114, 17, 16, 6, 0, 21, 1, 1, 23,115,104,101,
119, 90, 27,116,114,117,116,104, 24, 15, 6, 0, 27, 1, 1, 31,116,104,101,
114,101,105,110, 37, 31,115, 97, 99,114,105,102,105, 99,101, 18, 14, 6, 0,
23, 1, 8, 25,115,109,111,116,101, 7,112,108, 97, 99,101,115, 11, 13, 6,
0, 19, 1, 1, 0, 97,114,107, 72, 31, 15, 12, 6, 0, 21, 1, 8, 21,119,
105,110,101, 58,119, 97,121,115, 19, 11, 6, 0, 21, 1, 1, 27,115,111,109,
101, 98, 17,109,111,114,110,105,110,103, 19, 10, 6, 0, 27, 1, 1, 21, 98,
101,116,119,101,101,110, 92, 18,115,111,109,101, 19, 9, 6, 0, 21, 1, 1,
27,115, 97,118,101, 74, 36, 98,101,116,119,101,101,110, 21, 8, 6, 0, 25,
1, 1, 27,116,104,111,117,103,104, 98, 17,109,111,114,110,105,110,103, 16,
7, 6, 0, 21, 1, 1, 21,115,101,110,100, 49, 34,115,101,110,100, 18, 6,
6, 0, 25, 1, 1, 21,119,105,115,100,111,109, 38, 36,115,101,110,100, 16,
5, 6, 0, 23, 1, 9, 21, 97,110,103,101,114, 46,119, 97,121,115, 14, 4,
6, 0, 19, 1, 1, 19, 99, 97,110, 19, 43,119, 97,114, 16, 3, 6, 0, 23,
1, 1, 19,111,102,102,101,114, 48, 22,115,105,120, 16, 2, 6, 0, 23, 1,
8, 21,119,111,114,107,115, 58,119, 97,121,115, 16, 1, 6, 0, 23, 1, 1,
19, 0, 0, 0, 26, 45, 0, 0, 0, 25, 23, 13, 0, 0, 0, 7, 0, 48, 0,
1,171, 1, 74, 1, 30, 0,126, 0,249, 0,212, 0, 48, 0, 81, 0, 0, 84,
4, 7, 23, 17, 17, 1,129, 19,116, 97, 98,108,101,116, 52,116, 52, 5, 67,
82, 69, 76, 7, 7, 23, 17, 17, 1,129, 3,116, 97, 98,108,101,116, 53,116,
53, 8, 67, 82, 69, 65, 84, 69, 32, 84, 65, 66, 76, 69, 32,116, 53, 40, 97,
32, 73, 78, 84, 69, 71, 69, 82, 32, 80, 82, 73, 77, 65, 82, 89, 32, 75, 69,
89, 44, 32, 98, 32, 84, 69, 88, 84, 32, 85, 78, 73, 81, 85, 69, 44, 99, 44,
100, 44,101, 41, 84, 4, 7, 23, 17, 17, 1,129, 19,116, 97, 98,108,101,116,
52,116, 52, 5, 67, 82, 69, 65, 84, 69, 32, 84, 65, 66, 76, 69, 32,116, 52,
40, 97, 32, 73, 78, 84, 32, 85, 78, 73, 81, 85, 69, 32, 78, 79, 84, 32, 78,
85, 76, 76, 44, 32, 98, 32, 73, 78, 84, 32, 85, 78, 73, 81, 85, 69, 32, 78,
79, 84, 32, 78, 85, 76, 76, 44, 99, 44,100, 44,101, 41, 35, 6, 6, 23, 55,
17, 1, 0,105,110,100,101,120,115,113,108,105,116,101, 95, 97,117,116,111,
105,110,100,101,120, 95,116, 52, 95, 50,116, 52, 7, 35, 5, 6, 23, 55, 17,
1, 0,105,110,100,101,120,115,113,108,105,116,101, 95, 97,117,116,111,105,
110,100,101,120, 95,116, 52, 95, 49,116, 52, 6, 42, 3, 6, 23, 17, 17, 1,
65,116, 97, 98,108,101,116, 51,116, 51, 4, 67, 82, 69, 65, 84, 69, 32, 84,
65, 66, 76, 69, 32,116, 51, 40, 97, 44, 98, 44, 99, 44,100, 44,101, 41, 95,
2, 7, 23, 17, 17, 1,129, 41,116, 97, 98,108,101,116, 50,116, 50, 3, 67,
82, 69, 65, 84, 69, 32, 84, 65, 66, 76, 69, 32,116, 50, 40, 97, 32, 73, 78,
84, 44, 32, 98, 32, 73, 78, 84, 44, 32, 99, 32, 73, 78, 84, 44,100, 32, 73,
78, 84, 44,101, 32, 73, 78, 84, 44, 80, 82, 73, 77, 65, 82, 89, 32, 75, 69,
89, 40, 98, 44, 97, 41, 41, 87, 73, 84, 72, 79, 85, 84, 32, 82, 79, 87, 73,
68, 83, 1, 7, 23, 17, 17, 1,129, 17,116, 97, 98,108,101,116, 49,116, 49,
2, 67, 82, 69, 65, 84, 69, 32, 84, 65, 66, 76, 69, 32,116, 49, 40, 97, 32,
73, 78, 84, 69, 71, 69, 82, 32, 80, 82, 73, 77, 65, 82, 89, 32, 75, 69, 89,
44, 32, 98, 32, 73, 78, 84, 44, 32, 99, 32, 73, 78, 84, 44, 32,100, 32, 73,
78, 84, 44, 32,101, 32, 73, 78, 84, 41, 2, 0, 0, 0, 1, 1,243, 0, 0,
0, 0, 29, 1,243, 1,218, 1,209, 1,199, 1,187, 1,179, 1,169, 1,158,
1,145, 1,136, 1,127, 1,117, 1,107, 1, 98, 1, 82, 1, 72, 1, 63, 1,
51, 1, 42, 1, 30, 1, 20, 1, 12, 1, 3, 0,248, 0,239, 0,225, 0,216,
0,207, 0,197, 0,188, 0,180, 0,170, 0,161, 0,152, 0,141, 0,129, 0,
118, 0,106, 0, 97, 0, 0, 0, 0, 0, 0, 0, 8, 3, 21, 1,116,114,101,
101, 49, 11, 3, 27, 1,116,104,121,115,101,108,102, 27, 10, 3, 25, 1,116,
104,111,117,103,104, 8, 11, 3, 27, 1,116,104,101,114,101,105,110, 15, 10,
3, 25, 1,116,101,109,112,108,101, 43, 8, 3, 21, 1,116,101,108,108, 25,
8, 3, 21, 1,115,111,109,101, 11, 9, 3, 23, 1,115,109,111,116,101, 14,
7, 3, 19, 1,115,105,120, 48, 8, 3, 21, 1,115,104,101,119, 16, 9, 3,
23, 1,115,101,114,118,101, 37, 8, 3, 21, 1,115,101,110,100, 7, 8, 3,
21, 1,115, 97,118,101, 9, 13, 3, 31, 1,115, 97, 99,114,105,102,105, 99,
101, 24, 8, 3, 21, 1,112,111,111,114, 40, 10, 3, 25, 1,112,108, 97, 99,
101,115, 28, 8, 3, 21, 1,112, 97,114,116, 30, 7, 3, 19, 1,111,105,108,
46, 9, 3, 23, 1,111,102,102,101,114, 3, 11, 3, 27, 1,110,111,116,104,
105,110,103, 20, 8, 3, 21, 1,110,101, 97,114, 36, 11, 3, 27, 1,109,111,
114,110,105,110,103, 17, 8, 3, 21, 1,108,111,110,103, 35, 9, 3, 23, 1,
107,110,111,119,110, 23, 15, 3, 35, 1,105,110,104, 97, 98,105,116, 97,110,
116,115, 45, 8, 3, 21, 1,103,111,110,101, 32, 9, 3, 23, 1,102,114,117,
105,116, 38, 9, 3, 23, 1,100,119,101,108,116, 18, 8, 3, 21, 1,100,111,
116,104, 39, 8, 3, 21, 1,100,105,101,100, 47, 12, 3, 29, 1,100,101,112,
97,114,116,101,100, 26, 10, 3, 25, 1, 99,117, 98,105,116,115, 33, 9, 3,
23, 1, 99,104,105,108,100, 42, 7, 3, 19, 1, 99, 97,110, 4, 11, 3, 27,
1, 98,101,116,119,101,101,110, 10, 9, 3, 23, 1, 98,101,103, 97,116, 19,
8, 3, 21, 1, 98,101, 97,114, 29, 7, 3, 19, 1, 97,114,107, 13, 9, 3,
23, 1, 97,110,103,101,114, 5, 9, 3, 23, 1, 97,110,103, 0, 0, 0, 28,
8, 3, 21, 1,116,114,101,101, 49, 13, 1,104, 0, 7, 0, 24, 0, 1, 67,
1, 13, 0,225, 0,177, 0,109, 1,171, 0, 24, 0, 0, 83, 14, 7, 21, 19,
19, 8,129, 17,118,105,101,119,118, 50, 48,118, 50, 48, 67, 82, 69, 65, 84,
69, 32, 86, 73, 69, 87, 32,118, 50, 48, 40, 97, 44, 98, 44, 99, 44,100, 44,
101, 41, 32, 65, 83, 32, 83, 69, 76, 69, 67, 84, 32, 97, 44, 98, 44, 99, 44,
100, 44,101, 32, 70, 82, 79, 77, 32,116, 50, 32, 87, 72, 69, 82, 69, 32, 97,
60, 62, 50, 53, 66, 12, 6, 21, 19, 19, 8,113,118,105,101,119,118, 48, 48,
118, 48, 48, 67, 82, 69, 65, 84, 69, 32, 86, 73, 69, 87, 32,118, 48, 48, 40,
97, 44, 98, 44, 99, 44,100, 44,101, 41, 32, 65, 83, 32, 83, 69, 76, 69, 67,
84, 32, 49, 44, 49, 44, 49, 44, 49, 44, 39,111,110,101, 39, 46, 11, 6, 23,
21, 17, 1, 69,105,110,100,101,120,116, 50,101,100,116, 50, 14, 67, 82, 69,
65, 84, 69, 32, 73, 78, 68, 69, 88, 32,116, 50,101,100, 32, 79, 78, 32,116,
50, 40,101, 44,100, 41, 42, 10, 6, 23, 19, 17, 1, 63,105,110,100,101,120,
116, 49,101,116, 49, 13, 67, 82, 69, 65, 84, 69, 32, 73, 78, 68, 69, 88, 32,
116, 49,101, 32, 79, 78, 32,116, 49, 40,101, 41, 52, 9, 6, 23, 21, 17, 1,
81,105,110,100,101,120,116, 51,120, 49,116, 51, 12, 67, 82, 69, 65, 84, 69,
32, 73, 78, 68, 69, 88, 32,116, 51,120, 49, 32, 79, 78, 32,116, 51, 40, 97,
44, 98, 44, 99, 44,100, 44,101, 41, 35, 8, 6, 23, 55, 17, 1, 0,105,110,
100,101,120,115,113,108,105,116,101, 95, 97,117,116,111,105,110,100,101,120,
95,116, 53, 95, 49,116, 53, 10, 0, 0, 0, 67, 17, 17, 1,129, 3,116, 97,
98,108,101,116, 53,116, 53, 8, 67, 82, 69, 65, 84, 69, 32, 84, 65, 66, 76,
69, 32,116, 53, 40, 97, 32, 73, 78, 84, 69, 71, 69, 82, 32, 80, 82, 73, 77,
65, 82, 89, 32, 75, 69, 89, 44, 32, 98, 32, 84, 69, 88, 84, 32, 85, 78, 83,
13, 7, 21, 19, 19, 8,129, 17,118,105,101,119,118, 49, 48,118, 49, 48, 67,
82, 69, 65, 84, 69, 32, 86, 73, 69, 87, 32,118, 49, 48, 40, 97, 44, 98, 44,
99, 44,100, 44,101, 41, 32, 65, 83, 32, 83, 69, 76, 69, 67, 84, 32, 97, 44,
98, 44, 99, 44,100, 44,101, 32, 70, 82, 79, 77, 32,116, 49, 32, 87, 72, 69,
82, 69, 32, 97, 60, 62, 50, 53, 2, 0, 0, 0, 1, 1,240, 0, 0, 0, 0,
24, 1,240, 1,220, 1,211, 1,199, 1,187, 1,176, 1,164, 1,148, 1,133,
1,116, 1, 99, 1, 86, 1, 67, 1, 55, 1, 43, 1, 31, 1, 18, 1, 5, 0,
249, 0,236, 0,224, 0,209, 0,191, 0,174, 0,157, 0,145, 0,132, 0,120,
0,108, 0, 95, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 7, 1, 0,
1, 1, 0, 1, 49, 51, 38, 15, 12, 7, 1, 1, 1, 1, 0, 1, 48, 37, 93,
7, 30, 11, 7, 1, 1, 1, 0, 0, 1, 47, 22, 16, 24, 11, 7, 1, 0, 1,
1, 0, 1, 47, 16, 40, 12, 12, 7, 1, 1, 1, 1, 0, 1, 46, 28, 88, 22,
2, 11, 7, 1, 1, 0, 1, 0, 1, 44, 48, 9, 19, 16, 7, 1, 1, 1, 0,
23, 1, 42, 20, 18, 98,101,103, 97,116, 22, 16, 7, 1, 1, 0, 1, 23, 1,
37, 17, 18,100,119,101,108,116, 16, 17, 7, 1, 1, 0, 1, 25, 1, 35, 38,
22, 99,117, 98,105,116,115, 17, 14, 7, 1, 1, 1, 0, 19, 1, 34, 26, 15,
97,114,107, 21, 11, 7, 1, 1, 1, 0, 0, 1, 32, 16, 7, 23, 12, 7, 1,
1, 1, 1, 0, 1, 28, 17, 67, 31, 29, 11, 7, 1, 0, 1, 1, 0, 1, 25,
7, 19, 11, 12, 7, 1, 1, 1, 1, 0, 1, 22, 45, 71, 28, 28, 12, 7, 1,
1, 1, 1, 0, 1, 21, 22, 92, 18, 4, 11, 7, 1, 1, 0, 1, 0, 1, 21,
22, 18, 18, 11, 7, 1, 1, 1, 1, 0, 9, 17, 29, 74, 36, 11, 7, 1, 1,
1, 0, 0, 1, 17, 29, 74, 25, 18, 7, 1, 1, 1, 1, 25, 1, 15, 6, 3,
11,116,101,109,112,108,101, 5, 12, 7, 1, 1, 1, 1, 0, 1, 12, 4, 38,
36, 27, 16, 7, 1, 0, 1, 1, 23, 1, 10, 89, 14,115,101,114,118,101, 14,
16, 7, 1, 1, 1, 1, 21, 1, 4, 41, 32, 35,110,101, 97,114, 3, 14, 7,
9, 0, 1, 1, 21, 1, 68, 32,100,111,116,104, 13, 15, 7, 0, 1, 1, 1,
21, 1, 21, 91, 38,110,101, 97,114, 6, 11, 7, 0, 1, 1, 1, 0, 1, 16,
81, 25, 9, 10, 7, 0, 1, 1, 8, 0, 1, 16, 7, 10, 11, 7, 0, 1, 1,
1, 0, 1, 7, 72, 31, 8, 11, 7, 0, 1, 1, 1, 0, 1, 6, 37, 31, 7,
8, 7, 0, 0, 0, 0, 0, 1, 35, 8, 7, 0, 0, 0, 0, 0, 1, 34, 8,
7, 0, 0, 0, 0, 0, 1, 33, 8, 7, 0, 0, 0, 23, 11, 7, 1, 0, 1,
1, 0, 1, 49, 51, 38, 15, 2, 0, 0, 0, 1, 1,241, 0, 0, 0, 0, 18,
1,241, 1,221, 1,211, 1,203, 1,193, 1,183, 1,173, 1,163, 1,151, 1,
143, 1,133, 1,122, 1,109, 1,100, 1, 92, 1, 83, 1, 74, 1, 64, 1, 55,
1, 46, 1, 34, 1, 22, 1, 13, 1, 4, 0,252, 0,241, 0,232, 0,218, 0,
209, 0,200, 0,191, 0,182, 0,173, 0,163, 0,153, 0,144, 0,136, 0,127,
0,116, 0,105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 3,
25, 1,116,101,109,112,108,101, 48, 10, 3, 25, 1,116,101,109,112,108,101,
15, 8, 3, 21, 1,115,111,109,101, 21, 7, 3, 19, 1,115,105,120, 11, 8,
3, 21, 1,115,104,101,119, 38, 9, 3, 23, 1,115,101,114,118,101, 10, 9,
3, 23, 1,115,101,114,118,101, 3, 8, 3, 21, 1,115,101,110,100, 40, 8,
3, 21, 1,115,101,110,100, 29, 8, 3, 21, 1,115,101,110,100, 12, 8, 3,
21, 1,115,101,110,100, 8, 8, 3, 21, 1,115, 97,118,101, 2, 13, 3, 31,
1,115, 97, 99,114,105,102,105, 99,101, 23, 8, 3, 21, 1,112,111,111,114,
16, 10, 3, 25, 1,112,108, 97, 99,101,115, 32, 7, 3, 19, 1,111,105,108,
49, 8, 3, 21, 1,110,101, 97,114, 7, 8, 3, 21, 1,110,101, 97,114, 4,
11, 3, 27, 1,109,111,114,110,105,110,103, 41, 11, 3, 27, 1,109,111,114,
110,105,110,103, 26, 8, 3, 21, 1,103,111,110,101, 25, 8, 3, 21, 1,103,
111,110,101, 5, 9, 3, 23, 1,100,119,101,108,116, 37, 8, 3, 21, 1,100,
111,116,104, 44, 8, 3, 21, 1,100,111,116,104, 13, 7, 3, 21, 9,100,111,
116,104, 8, 3, 21, 1,100,105,101,100, 14, 12, 3, 29, 1,100,101,112, 97,
114,116,101,100, 46, 10, 3, 25, 1, 99,117, 98,105,116,115, 35, 9, 3, 23,
1, 99,104,105,108,100, 36, 7, 3, 19, 1, 99, 97,110, 6, 11, 3, 27, 1,
98,101,116,119,101,101,110, 17, 9, 3, 23, 1, 98,101,103, 97,116, 43, 9,
3, 23, 1, 98,101,103, 97,116, 42, 9, 3, 23, 1, 98,101,103, 97,116, 39,
9, 3, 23, 1, 98,101,103, 97,116, 9, 7, 3, 19, 1, 97,114,107, 34, 9,
3, 23, 1, 97,110,103,101,114, 47, 9, 3, 23, 1, 97,110,103,101,108, 27,
9, 3, 23, 1, 97, 98,111,118,101, 45, 0, 0, 0, 17, 10, 3, 25, 1,116,
101,109,112,108,101, 48, 2, 0, 0, 0, 1, 1,239, 0, 0, 0, 0, 20, 1,
239, 1,206, 1,192, 1,180, 1,166, 1,152, 1,138, 1,125, 1,109, 1, 97,
1, 84, 1, 69, 1, 52, 1, 39, 1, 26, 1, 14, 1, 1, 0,243, 0,230, 0,
217, 0,201, 0,185, 0,172, 0,159, 0,147, 0,133, 0,120, 0,102, 0, 89,
0, 76, 0, 0, 0, 0, 12, 5, 21, 1, 1, 1,115,101,110,100, 26, 14, 40,
12, 5, 21, 1, 1, 1,115, 97,118,101, 39, 45, 2, 17, 5, 31, 1, 1, 1,
115, 97, 99,114,105,102,105, 99,101, 31, 6, 23, 12, 5, 21, 1, 1, 1,112,
111,111,114, 19, 44, 16, 13, 5, 25, 8, 1, 1,112,108, 97, 99,101,115, 16,
32, 11, 5, 19, 1, 1, 1,111,105,108, 38, 9, 49, 12, 5, 21, 1, 1, 1,
110,101, 97,114, 38, 21, 7, 12, 5, 21, 1, 1, 1,110,101, 97,114, 35, 41,
4, 15, 5, 27, 1, 1, 1,109,111,114,110,105,110,103, 17, 40, 26, 15, 5,
27, 1, 1, 1,109,111,114,110,105,110,103, 13, 46, 41, 12, 5, 21, 1, 1,
1,103,111,110,101, 28, 25, 5, 12, 5, 21, 1, 1, 1,103,111,110,101, 19,
10, 25, 13, 5, 23, 1, 1, 1,100,119,101,108,116, 18, 17, 37, 12, 5, 21,
1, 1, 1,100,111,116,104, 39, 4, 13, 11, 5, 21, 1, 1, 9,100,111,116,
104, 32, 40, 12, 5, 21, 1, 1, 1,100,111,116,104, 9, 48, 44, 12, 5, 21,
1, 1, 1,100,105,101,100, 27, 35, 14, 16, 5, 29, 1, 1, 1,100,101,112,
97,114,116,101,100, 22, 28, 46, 14, 5, 25, 1, 1, 1, 99,117, 98,105,116,
115, 22, 38, 35, 12, 5, 23, 1, 8, 1, 99,104,105,108,100, 17, 36, 11, 5,
19, 1, 1, 1, 99, 97,110, 11, 2, 6, 15, 5, 27, 1, 1, 1, 98,101,116,
119,101,101,110, 36, 29, 17, 12, 5, 23, 1, 8, 1, 98,101,103, 97,116, 50,
9, 13, 5, 23, 1, 1, 1, 98,101,103, 97,116, 45, 3, 39, 13, 5, 23, 1,
1, 1, 98,101,103, 97,116, 41, 5, 43, 13, 5, 23, 1, 1, 1, 98,101,103,
97,116, 5, 20, 42, 11, 5, 19, 1, 1, 1, 97,114,107, 20, 26, 34, 13, 5,
23, 1, 1, 1, 97,110,103,101,114, 40, 22, 47, 13, 5, 23, 1, 1, 1, 97,
110,103,101,108, 3, 22, 27, 12, 5, 23, 1, 9, 1, 97, 98,111,118,101, 45,
20, 13, 5, 23, 1, 1, 1, 0, 0, 0, 19, 12, 5, 21, 1, 1, 1,115,101,
110,100, 26, 14, 40, 13, 0, 0, 0, 28, 0, 78, 0, 1,241, 1,226, 1,210,
1,195, 1,180, 1,166, 1,151, 1,136, 1,121, 1,105, 1, 91, 1, 76, 1,
61, 1, 46, 1, 29, 1, 14, 0,252, 0,238, 0,224, 0,209, 0,194, 0,177,
0,157, 0,143, 0,128, 0,110, 0, 94, 0, 78, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 14, 28, 6, 0, 1, 1, 1, 23, 17, 67, 31,119,
111,114,107,115, 14, 27, 6, 0, 1, 1, 1, 23, 22, 71, 3, 97,110,103,101,
108, 16, 26, 6, 0, 1, 1, 1, 27, 40, 98, 17,109,111,114,110,105,110,103,
13, 25, 6, 0, 1, 1, 1, 21, 10, 7, 19,103,111,110,101, 12, 24, 6, 0,
1, 1, 9, 21, 43, 46,119, 97,121,115, 18, 23, 6, 0, 1, 1, 1, 31, 6,
37, 31,115, 97, 99,114,105,102,105, 99,101, 15, 22, 6, 0, 1, 1, 1, 25,
45, 71, 28,116,104,111,117,103,104, 13, 21, 6, 0, 1, 1, 1, 21, 22, 92,
18,115,111,109,101, 13, 20, 6, 0, 9, 1, 1, 23, 2, 45, 97, 98,111,118,
101, 12, 19, 6, 0, 1, 1, 8, 21, 4, 58,119, 97,121,115, 12, 18, 6, 0,
1, 1, 1, 19, 44, 19, 43,119, 97,114, 16, 17, 6, 0, 1, 1, 1, 27, 29,
74, 36, 98,101,116,119,101,101,110, 13, 16, 6, 0, 1, 1, 1, 21, 44, 52,
19,112,111,111,114, 15, 15, 6, 0, 1, 1, 1, 25, 6, 3, 11,116,101,109,
112,108,101, 13, 14, 6, 0, 1, 1, 1, 21, 35, 48, 27,100,105,101,100, 13,
13, 6, 0, 1, 1, 1, 21, 4, 21, 39,100,111,116,104, 13, 12, 6, 0, 1,
1, 1, 21, 4, 38, 36,115,101,110,100, 12, 11, 6, 0, 1, 1, 1, 19, 13,
48, 22,115,105,120, 14, 10, 6, 0, 1, 1, 1, 23, 41, 89, 14,115,101,114,
118,101, 13, 9, 6, 0, 8, 1, 1, 23, 16, 50, 98,101,103, 97,116, 13, 8,
6, 0, 1, 1, 1, 21, 42, 49, 34,115,101,110,100, 13, 7, 6, 0, 1, 1,
1, 21, 21, 91, 38,110,101, 97,114, 12, 6, 6, 0, 1, 1, 1, 19, 2, 37,
11, 99, 97,110, 13, 5, 6, 0, 1, 1, 1, 21, 25, 27, 28,103,111,110,101,
13, 4, 6, 0, 1, 1, 1, 21, 41, 32, 35,110,101, 97,114, 14, 3, 6, 0,
1, 1, 1, 23, 32, 24, 26,115,101,114,118,101, 13, 2, 6, 0, 1, 1, 1,
21, 45, 14, 39,115, 97,118,101, 13, 1, 6, 0, 1, 1, 1, 21, 40, 68, 32,
100,111,116,104, 13, 0, 0, 0, 22, 0,166, 0, 1,241, 1,226, 1,210, 1,
194, 1,183, 1,169, 1,152, 1,137, 1,121, 1,106, 1, 90, 1, 75, 1, 57,
1, 41, 1, 25, 1, 10, 0,250, 0,231, 0,215, 0,198, 0,184, 0,166, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,
50, 6, 0, 1, 1, 1, 27, 9, 92, 29,116,104,101,114,101,105,110, 12, 49,
6, 0, 1, 1, 1, 19, 9, 51, 38,111,105,108, 15, 48, 6, 0, 1, 1, 1,
25, 37, 93, 7,116,101,109,112,108,101, 14, 47, 6, 0, 1, 1, 1, 23, 22,
16, 40, 97,110,103,101,114, 17, 46, 6, 0, 1, 1, 1, 29, 28, 88, 22,100,
101,112, 97,114,116,101,100, 14, 45, 6, 0, 1, 1, 1, 23, 47, 54, 12, 97,
98,111,118,101, 13, 44, 6, 0, 1, 1, 1, 21, 48, 15, 9,100,111,116,104,
14, 43, 6, 0, 1, 1, 1, 23, 5, 23, 41, 98,101,103, 97,116, 14, 42, 6,
0, 1, 1, 1, 23, 20, 18, 5, 98,101,103, 97,116, 16, 41, 6, 0, 1, 1,
1, 27, 46, 92, 13,109,111,114,110,105,110,103, 13, 40, 6, 0, 1, 1, 1,
21, 14, 30, 26,115,101,110,100, 14, 39, 6, 0, 1, 1, 1, 23, 3, 21, 45,
98,101,103, 97,116, 13, 38, 6, 0, 1, 1, 1, 21, 10, 97, 34,115,104,101,
119, 14, 37, 6, 0, 1, 1, 1, 23, 17, 66, 18,100,119,101,108,116, 13, 36,
6, 0, 8, 1, 1, 23, 52, 17, 99,104,105,108,100, 15, 35, 6, 0, 1, 1,
1, 25, 38, 34, 22, 99,117, 98,105,116,115, 12, 34, 6, 0, 1, 1, 1, 19,
26, 15, 20, 97,114,107, 9, 33, 6, 0, 1, 1, 1, 0, 7, 72, 31, 14, 32,
6, 0, 1, 1, 8, 25, 16, 7,112,108, 97, 99,101,115, 14, 31, 6, 0, 1,
1, 1, 23, 39, 90, 27,116,114,117,116,104, 13, 30, 6, 0, 1, 1, 1, 21,
16, 81, 25,119, 97,108,107, 13, 29, 6, 0, 1, 1, 1, 21, 34, 62, 27,115,
101,110,100, 10, 0, 0, 0, 41, 0,116, 0, 1,251, 1,241, 1,231, 1,221,
1,211, 1,203, 1,193, 1,183, 1,173, 1,163, 1,151, 1,143, 1,133, 1,
122, 1,109, 1,100, 1, 92, 1, 83, 1, 74, 1, 64, 1, 55, 1, 46, 1, 34,
1, 22, 1, 13, 1, 4, 0,252, 0,241, 0,232, 0,218, 0,209, 0,200, 0,
191, 0,182, 0,173, 0,163, 0,153, 0,144, 0,136, 0,127, 0,116, 0,105,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11,116,101,
109,112,108,101, 48, 10, 3, 25, 1,116,101,109,112,108,101, 15, 8, 3, 21,
1,115,111,109,101, 21, 7, 3, 19, 1,115,105,120, 11, 8, 3, 21, 1,115,
104,101,119, 38, 9, 3, 23, 1,115,101,114,118,101, 10, 9, 3, 23, 1,115,
101,114,118,101, 3, 8, 3, 21, 1,115,101,110,100, 40, 8, 3, 21, 1,115,
101,110,100, 29, 8, 3, 21, 1,115,101,110,100, 12, 8, 3, 21, 1,115,101,
110,100, 8, 8, 3, 21, 1,115, 97,118,101, 2, 13, 3, 31, 1,115, 97, 99,
114,105,102,105, 99,101, 23, 8, 3, 21, 1,112,111,111,114, 16, 10, 3, 25,
1,112,108, 97, 99,101,115, 32, 7, 3, 19, 1,111,105,108, 49, 8, 3, 21,
1,110,101, 97,114, 7, 8, 3, 21, 1,110,101, 97,114, 4, 11, 3, 27, 1,
109,111,114,110,105,110,103, 41, 11, 3, 27, 1,109,111,114,110,105,110,103,
26, 8, 3, 21, 1,103,111,110,101, 25, 8, 3, 21, 1,103,111,110,101, 5,
9, 3, 23, 1,100,119,101,108,116, 37, 8, 3, 21, 1,100,111,116,104, 44,
8, 3, 21, 1,100,111,116,104, 13, 7, 3, 21, 9,100,111,116,104, 8, 3,
21, 1,100,105,101,100, 14, 12, 3, 29, 1,100,101,112, 97,114,116,101,100,
46, 10, 3, 25, 1, 99,117, 98,105,116,115, 35, 9, 3, 23, 1, 99,104,105,
108,100, 36, 7, 3, 19, 1, 99, 97,110, 6, 11, 3, 27, 1, 98,101,116,119,
101,101,110, 17, 9, 3, 23, 1, 98,101,103, 97,116, 43, 9, 3, 23, 1, 98,
101,103, 97,116, 42, 9, 3, 23, 1, 98,101,103, 97,116, 39, 9, 3, 23, 1,
98,101,103, 97,116, 9, 7, 3, 19, 1, 97,114,107, 34, 9, 3, 23, 1, 97,
110,103,101,114, 47, 9, 3, 23, 1, 97,110,103,101,108, 27, 9, 3, 23, 1,
97, 98,111,118,101, 45, 9, 3, 23, 1, 97, 98,111,118,101, 20, 4, 3, 0,
1, 33, 10, 0, 0, 0, 8, 1,178, 0, 1,244, 1,233, 1,223, 1,214, 1,
206, 1,197, 1,188, 1,178, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,
3, 23, 1,119,111,114,107,115, 28, 8, 3, 21, 1,119, 97,121,115, 24, 8,
3, 21, 1,119, 97,121,115, 19, 7, 3, 19, 1,119, 97,114, 18, 8, 3, 21,
1,119, 97,108,107, 30, 9, 3, 23, 1,116,114,117,116,104, 31, 10, 3, 25,
1,116,104,111,117,103,104, 22, 11, 3, 27, 1,116,104,101,114,101,105,110,
50, 10, 0, 0, 0, 31, 0, 89, 0, 1,247, 1,233, 1,220, 1,206, 1,192,
1,180, 1,166, 1,152, 1,138, 1,125, 1,109, 1, 97, 1, 84, 1, 69, 1,
52, 1, 39, 1, 26, 1, 14, 1, 1, 0,243, 0,230, 0,217, 0,201, 0,185,
0,172, 0,159, 0,147, 0,133, 0,120, 0,102, 0, 89, 0, 76, 0, 0, 0,
0, 0, 0, 0, 13, 1, 1,115,101,110,100, 26, 14, 40, 12, 5, 21, 1, 1,
1,115, 97,118,101, 39, 45, 2, 17, 5, 31, 1, 1, 1,115, 97, 99,114,105,
102,105, 99,101, 31, 6, 23, 12, 5, 21, 1, 1, 1,112,111,111,114, 19, 44,
16, 13, 5, 25, 8, 1, 1,112,108, 97, 99,101,115, 16, 32, 11, 5, 19, 1,
1, 1,111,105,108, 38, 9, 49, 12, 5, 21, 1, 1, 1,110,101, 97,114, 38,
21, 7, 12, 5, 21, 1, 1, 1,110,101, 97,114, 35, 41, 4, 15, 5, 27, 1,
1, 1,109,111,114,110,105,110,103, 17, 40, 26, 15, 5, 27, 1, 1, 1,109,
111,114,110,105,110,103, 13, 46, 41, 12, 5, 21, 1, 1, 1,103,111,110,101,
28, 25, 5, 12, 5, 21, 1, 1, 1,103,111,110,101, 19, 10, 25, 13, 5, 23,
1, 1, 1,100,119,101,108,116, 18, 17, 37, 12, 5, 21, 1, 1, 1,100,111,
116,104, 39, 4, 13, 11, 5, 21, 1, 1, 9,100,111,116,104, 32, 40, 12, 5,
21, 1, 1, 1,100,111,116,104, 9, 48, 44, 12, 5, 21, 1, 1, 1,100,105,
101,100, 27, 35, 14, 16, 5, 29, 1, 1, 1,100,101,112, 97,114,116,101,100,
22, 28, 46, 14, 5, 25, 1, 1, 1, 99,117, 98,105,116,115, 22, 38, 35, 12,
5, 23, 1, 8, 1, 99,104,105,108,100, 17, 36, 11, 5, 19, 1, 1, 1, 99,
97,110, 11, 2, 6, 15, 5, 27, 1, 1, 1, 98,101,116,119,101,101,110, 36,
29, 17, 12, 5, 23, 1, 8, 1, 98,101,103, 97,116, 50, 9, 13, 5, 23, 1,
1, 1, 98,101,103, 97,116, 45, 3, 39, 13, 5, 23, 1, 1, 1, 98,101,103,
97,116, 41, 5, 43, 13, 5, 23, 1, 1, 1, 98,101,103, 97,116, 5, 20, 42,
11, 5, 19, 1, 1, 1, 97,114,107, 20, 26, 34, 13, 5, 23, 1, 1, 1, 97,
110,103,101,114, 40, 22, 47, 13, 5, 23, 1, 1, 1, 97,110,103,101,108, 3,
22, 27, 12, 5, 23, 1, 9, 1, 97, 98,111,118,101, 45, 20, 13, 5, 23, 1,
1, 1, 97, 98,111,118,101, 12, 47, 45, 8, 5, 0, 1, 1, 1, 31, 7, 33,
10, 0, 0, 0, 18, 1, 13, 0, 1,243, 1,230, 1,217, 1,203, 1,189, 1,
176, 1,164, 1,151, 1,136, 1,121, 1,105, 1, 90, 1, 76, 1, 63, 1, 51,
1, 39, 1, 27, 1, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 13, 5, 23, 1, 1, 1,119,111,114,107,115, 31, 17, 28, 11, 5,
21, 9, 1, 1,119, 97,121,115, 43, 24, 11, 5, 21, 8, 1, 1,119, 97,121,
115, 4, 19, 11, 5, 19, 1, 1, 1,119, 97,114, 43, 44, 18, 12, 5, 21, 1,
1, 1,119, 97,108,107, 25, 16, 30, 13, 5, 23, 1, 1, 1,116,114,117,116,
104, 27, 39, 31, 14, 5, 25, 1, 1, 1,116,104,111,117,103,104, 28, 45, 22,
15, 5, 27, 1, 1, 1,116,104,101,114,101,105,110, 29, 9, 50, 14, 5, 25,
1, 1, 1,116,101,109,112,108,101, 11, 6, 15, 14, 5, 25, 1, 1, 1,116,
101,109,112,108,101, 7, 37, 48, 12, 5, 21, 1, 1, 1,115,111,109,101, 18,
22, 21, 11, 5, 19, 1, 1, 1,115,105,120, 22, 13, 11, 12, 5, 21, 1, 1,
1,115,104,101,119, 34, 10, 38, 13, 5, 23, 1, 1, 1,115,101,114,118,101,
26, 32, 3, 13, 5, 23, 1, 1, 1,115,101,114,118,101, 14, 41, 10, 12, 5,
21, 1, 1, 1,115,101,110,100, 36, 4, 12, 12, 5, 21, 1, 1, 1,115,101,
110,100, 34, 42, 8, 12, 5, 21, 1, 1, 1,115,101,110,100, 27, 34, 29, 10,
0, 0, 0, 28, 0, 82, 0, 1,241, 1,226, 1,211, 1,197, 1,181, 1,166,
1,151, 1,137, 1,121, 1,104, 1, 84, 1, 73, 1, 59, 1, 41, 1, 26, 1,
11, 0,253, 0,238, 0,223, 0,207, 0,191, 0,175, 0,159, 0,144, 0,129,
0,113, 0, 97, 0, 82, 0, 68, 0, 0, 0, 0, 0, 14, 1, 1, 19, 26, 34,
15, 20, 97,114,107, 14, 6, 1, 1, 1, 1, 21, 25, 5, 27, 28,103,111,110,
101, 15, 6, 1, 1, 1, 1, 23, 22, 47, 16, 40, 97,110,103,101,114, 15, 6,
1, 1, 1, 1, 23, 22, 27, 71, 3, 97,110,103,101,108, 14, 6, 1, 1, 1,
1, 21, 22, 21, 92, 18,115,111,109,101, 14, 6, 1, 1, 1, 1, 21, 21, 7,
91, 38,110,101, 97,114, 15, 6, 1, 1, 1, 1, 23, 20, 42, 18, 5, 98,101,
103, 97,116, 15, 6, 1, 1, 1, 1, 23, 17, 37, 66, 18,100,119,101,108,116,
15, 6, 1, 1, 1, 1, 23, 17, 28, 67, 31,119,111,114,107,115, 15, 6, 1,
1, 1, 8, 25, 16, 32, 7,112,108, 97, 99,101,115, 14, 6, 1, 1, 1, 1,
21, 16, 30, 81, 25,119, 97,108,107, 14, 6, 1, 1, 1, 1, 21, 14, 40, 30,
26,115,101,110,100, 13, 6, 1, 1, 1, 1, 19, 13, 11, 48, 22,115,105,120,
14, 6, 1, 1, 1, 1, 21, 10, 38, 97, 34,115,104,101,119, 14, 6, 1, 1,
1, 1, 21, 10, 25, 7, 19,103,111,110,101, 17, 6, 1, 1, 1, 1, 27, 9,
50, 92, 29,116,104,101,114,101,105,110, 13, 6, 1, 1, 1, 1, 19, 9, 49,
51, 38,111,105,108, 10, 6, 1, 1, 1, 1, 0, 7, 33, 72, 31, 19, 6, 1,
1, 1, 1, 31, 6, 23, 37, 31,115, 97, 99,114,105,102,105, 99,101, 16, 6,
1, 1, 1, 1, 25, 6, 15, 3, 11,116,101,109,112,108,101, 15, 6, 1, 1,
1, 1, 23, 5, 43, 23, 41, 98,101,103, 97,116, 13, 6, 1, 1, 1, 8, 21,
4, 19, 58,119, 97,121,115, 14, 6, 1, 1, 1, 1, 21, 4, 13, 21, 39,100,
111,116,104, 14, 6, 1, 1, 1, 1, 21, 4, 12, 38, 36,115,101,110,100, 15,
6, 1, 1, 1, 1, 23, 3, 39, 21, 45, 98,101,103, 97,116, 13, 6, 1, 1,
1, 1, 19, 2, 6, 37, 11, 99, 97,110, 14, 6, 9, 1, 1, 1, 23, 20, 2,
45, 97, 98,111,118,101, 14, 6, 8, 1, 1, 1, 23, 36, 52, 17, 99,104,105,
108,100, 14, 6, 8, 1, 1, 1, 23, 9, 16, 50, 98,101,103, 97,116, 10, 0,
0, 0, 21, 0,177, 0, 1,237, 1,219, 1,203, 1,188, 1,173, 1,156, 1,
139, 1,123, 1,109, 1, 91, 1, 76, 1, 60, 1, 45, 1, 31, 1, 16, 1, 2,
0,243, 0,226, 0,208, 0,192, 0,177, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 14, 6, 1, 1, 1, 1, 21, 48, 44, 15, 9,100,111,116,104,
15, 6, 1, 1, 1, 1, 23, 47, 45, 54, 12, 97, 98,111,118,101, 17, 6, 1,
1, 1, 1, 27, 46, 41, 92, 13,109,111,114,110,105,110,103, 16, 6, 1, 1,
1, 1, 25, 45, 22, 71, 28,116,104,111,117,103,104, 14, 6, 1, 1, 1, 1,
21, 45, 2, 14, 39,115, 97,118,101, 13, 6, 1, 1, 1, 1, 19, 44, 18, 19,
43,119, 97,114, 14, 6, 1, 1, 1, 1, 21, 44, 16, 52, 19,112,111,111,114,
13, 6, 1, 1, 1, 9, 21, 43, 24, 46,119, 97,121,115, 14, 6, 1, 1, 1,
1, 21, 42, 8, 49, 34,115,101,110,100, 15, 6, 1, 1, 1, 1, 23, 41, 10,
89, 14,115,101,114,118,101, 14, 6, 1, 1, 1, 1, 21, 41, 4, 32, 35,110,
101, 97,114, 17, 6, 1, 1, 1, 1, 27, 40, 26, 98, 17,109,111,114,110,105,
110,103, 13, 6, 1, 9, 1, 1, 21, 40, 68, 32,100,111,116,104, 15, 6, 1,
1, 1, 1, 23, 39, 31, 90, 27,116,114,117,116,104, 16, 6, 1, 1, 1, 1,
25, 38, 35, 34, 22, 99,117, 98,105,116,115, 16, 6, 1, 1, 1, 1, 25, 37,
48, 93, 7,116,101,109,112,108,101, 14, 6, 1, 1, 1, 1, 21, 35, 14, 48,
27,100,105,101,100, 14, 6, 1, 1, 1, 1, 21, 34, 29, 62, 27,115,101,110,
100, 15, 6, 1, 1, 1, 1, 23, 32, 3, 24, 26,115,101,114,118,101, 17, 6,
1, 1, 1, 1, 27, 29, 17, 74, 36, 98,101,116,119,101,101,110, 18, 6, 1,
1, 1, 1, 29, 28, 46, 88, 22,100,101,112, 97,114,116,101,100, 10, 0, 0,
0, 32, 0, 95, 0, 1,247, 1,238, 1,229, 1,220, 1,211, 1,199, 1,187,
1,176, 1,164, 1,148, 1,133, 1,116, 1, 99, 1, 86, 1, 67, 1, 55, 1,
43, 1, 31, 1, 18, 1, 5, 0,249, 0,236, 0,224, 0,209, 0,191, 0,174,
0,157, 0,145, 0,132, 0,120, 0,108, 0, 95, 0, 83, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 12, 1, 1, 0, 1, 49, 51, 38, 15, 12, 7, 1,
1, 1, 1, 0, 1, 48, 37, 93, 7, 30, 11, 7, 1, 1, 1, 0, 0, 1, 47,
22, 16, 24, 11, 7, 1, 0, 1, 1, 0, 1, 47, 16, 40, 12, 12, 7, 1, 1,
1, 1, 0, 1, 46, 28, 88, 22, 2, 11, 7, 1, 1, 0, 1, 0, 1, 44, 48,
9, 19, 16, 7, 1, 1, 1, 0, 23, 1, 42, 20, 18, 98,101,103, 97,116, 22,
16, 7, 1, 1, 0, 1, 23, 1, 37, 17, 18,100,119,101,108,116, 16, 17, 7,
1, 1, 0, 1, 25, 1, 35, 38, 22, 99,117, 98,105,116,115, 17, 14, 7, 1,
1, 1, 0, 19, 1, 34, 26, 15, 97,114,107, 21, 11, 7, 1, 1, 1, 0, 0,
1, 32, 16, 7, 23, 12, 7, 1, 1, 1, 1, 0, 1, 28, 17, 67, 31, 29, 11,
7, 1, 0, 1, 1, 0, 1, 25, 7, 19, 11, 12, 7, 1, 1, 1, 1, 0, 1,
22, 45, 71, 28, 28, 12, 7, 1, 1, 1, 1, 0, 1, 21, 22, 92, 18, 4, 11,
7, 1, 1, 0, 1, 0, 1, 21, 22, 18, 18, 11, 7, 1, 1, 1, 1, 0, 9,
17, 29, 74, 36, 11, 7, 1, 1, 1, 0, 0, 1, 17, 29, 74, 25, 18, 7, 1,
1, 1, 1, 25, 1, 15, 6, 3, 11,116,101,109,112,108,101, 5, 12, 7, 1,
1, 1, 1, 0, 1, 12, 4, 38, 36, 27, 16, 7, 1, 0, 1, 1, 23, 1, 10,
89, 14,115,101,114,118,101, 14, 16, 7, 1, 1, 1, 1, 21, 1, 4, 41, 32,
35,110,101, 97,114, 3, 14, 7, 9, 0, 1, 1, 21, 1, 68, 32,100,111,116,
104, 13, 15, 7, 0, 1, 1, 1, 21, 1, 21, 91, 38,110,101, 97,114, 6, 11,
7, 0, 1, 1, 1, 0, 1, 16, 81, 25, 9, 10, 7, 0, 1, 1, 8, 0, 1,
16, 7, 10, 11, 7, 0, 1, 1, 1, 0, 1, 7, 72, 31, 8, 11, 7, 0, 1,
1, 1, 0, 1, 6, 37, 31, 7, 8, 7, 0, 0, 0, 0, 0, 1, 35, 8, 7,
0, 0, 0, 0, 0, 1, 34, 8, 7, 0, 0, 0, 0, 0, 1, 33, 8, 7, 0,
0, 0, 0, 0, 1, 32, 8, 7, 0, 0, 0, 0, 0, 1, 31, 10, 0, 0, 0,
2, 1,231, 0, 1,244, 1,231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 12, 7, 1, 1, 1, 1, 0, 1, 49, 9, 51,
38, 26, 11, 7, 1, 1, 0, 1, 0, 1, 49, 9, 38, 20, 13, 0, 0, 0, 23,
0, 67, 0, 1,238, 1,220, 1,202, 1,186, 1,168, 1,148, 1,130, 1,107,
1, 86, 1, 65, 1, 44, 1, 27, 1, 14, 0,250, 0,224, 0,205, 0,184, 0,
165, 0,145, 0,123, 0,106, 0, 86, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 17, 23, 6, 0, 23, 1, 1, 21,107,110,111,119,110, 52,
19,112,111,111,114, 18, 22, 6, 0, 23, 1, 1, 23, 97, 98,111,118,101, 24,
26,115,101,114,118,101, 15, 21, 6, 0, 19, 1, 1, 21,119, 97,114, 52, 19,
112,111,111,114, 20, 20, 6, 0, 27, 1, 8, 25,110,111,116,104,105,110,103,
7,112,108, 97, 99,101,115, 18, 19, 6, 0, 23, 1, 1, 23, 98,101,103, 97,
116, 90, 27,116,114,117,116,104, 17, 18, 6, 0, 23, 1, 1, 21,100,119,101,
108,116, 21, 39,100,111,116,104, 19, 17, 6, 0, 27, 1, 1, 21,109,111,114,
110,105,110,103, 52, 19,112,111,111,114, 17, 16, 6, 0, 21, 1, 1, 23,115,
104,101,119, 90, 27,116,114,117,116,104, 24, 15, 6, 0, 27, 1, 1, 31,116,
104,101,114,101,105,110, 37, 31,115, 97, 99,114,105,102,105, 99,101, 18, 14,
6, 0, 23, 1, 8, 25,115,109,111,116,101, 7,112,108, 97, 99,101,115, 11,
13, 6, 0, 19, 1, 1, 0, 97,114,107, 72, 31, 15, 12, 6, 0, 21, 1, 8,
21,119,105,110,101, 58,119, 97,121,115, 19, 11, 6, 0, 21, 1, 1, 27,115,
111,109,101, 98, 17,109,111,114,110,105,110,103, 19, 10, 6, 0, 27, 1, 1,
21, 98,101,116,119,101,101,110, 92, 18,115,111,109,101, 19, 9, 6, 0, 21,
1, 1, 27,115, 97,118,101, 74, 36, 98,101,116,119,101,101,110, 21, 8, 6,
0, 25, 1, 1, 27,116,104,111,117,103,104, 98, 17,109,111,114,110,105,110,
103, 16, 7, 6, 0, 21, 1, 1, 21,115,101,110,100, 49, 34,115,101,110,100,
18, 6, 6, 0, 25, 1, 1, 21,119,105,115,100,111,109, 38, 36,115,101,110,
100, 16, 5, 6, 0, 23, 1, 9, 21, 97,110,103,101,114, 46,119, 97,121,115,
14, 4, 6, 0, 19, 1, 1, 19, 99, 97,110, 19, 43,119, 97,114, 16, 3, 6,
0, 23, 1, 1, 19,111,102,102,101,114, 48, 22,115,105,120, 16, 2, 6, 0,
23, 1, 8, 21,119,111,114,107,115, 58,119, 97,121,115, 16, 1, 6, 0, 23,
1, 1, 19,116,114,117,116,104, 37, 11, 99, 97,110, 13, 0, 0, 0, 22, 0,
64, 0, 1,230, 1,213, 1,191, 1,169, 1,148, 1,130, 1,108, 1, 89, 1,
70, 1, 51, 1, 34, 1, 16, 0,253, 0,233, 0,214, 0,194, 0,174, 0,151,
0,132, 0,109, 0, 90, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 24, 45, 6, 0, 35, 1, 1, 23,105,110,104, 97, 98,105,116, 97,110,116,
115, 23, 41, 98,101,103, 97,116, 17, 44, 6, 0, 23, 1, 1, 21, 97,110,103,
101,108, 48, 27,100,105,101,100, 21, 43, 6, 0, 25, 1, 1, 27,116,101,109,
112,108,101, 74, 36, 98,101,116,119,101,101,110, 17, 42, 6, 0, 23, 1, 1,
21, 99,104,105,108,100, 81, 25,119, 97,108,107, 21, 41, 6, 0, 21, 1, 1,
31,119, 97,121,115, 37, 31,115, 97, 99,114,105,102,105, 99,101, 18, 40, 6,
0, 21, 1, 1, 25,112,111,111,114, 93, 7,116,101,109,112,108,101, 18, 39,
6, 0, 21, 1, 1, 25,100,111,116,104, 3, 11,116,101,109,112,108,101, 17,
38, 6, 0, 23, 1, 1, 21,102,114,117,105,116, 62, 27,115,101,110,100, 18,
37, 6, 0, 23, 1, 1, 23,115,101,114,118,101, 90, 27,116,114,117,116,104,
17, 36, 6, 0, 21, 1, 1, 23,110,101, 97,114, 90, 27,116,114,117,116,104,
16, 35, 6, 0, 21, 1, 1, 21,108,111,110,103, 14, 39,115, 97,118,101, 15,
34, 6, 0, 21, 1, 1, 19,119, 97,108,107, 15, 20, 97,114,107, 17, 33, 6,
0, 25, 1, 9, 21, 99,117, 98,105,116,115, 46,119, 97,121,115, 17, 32, 6,
0, 21, 1, 1, 23,103,111,110,101, 23, 41, 98,101,103, 97,116, 17, 31, 6,
0, 23, 1, 1, 21,119,104,105,108,101, 49, 34,115,101,110,100, 20, 30, 6,
0, 21, 1, 1, 29,112, 97,114,116, 88, 22,100,101,112, 97,114,116,101,100,
16, 29, 6, 0, 21, 1, 1, 21, 98,101, 97,114, 92, 18,115,111,109,101, 19,
28, 6, 0, 25, 1, 1, 23,112,108, 97, 99,101,115, 23, 41, 98,101,103, 97,
116, 20, 27, 6, 0, 27, 1, 1, 23,116,104,121,115,101,108,102, 54, 12, 97,
98,111,118,101, 20, 26, 6, 0, 29, 1, 1, 21,100,101,112, 97,114,116,101,
100, 92, 18,115,111,109,101, 15, 25, 6, 0, 21, 1, 1, 19,116,101,108,108,
19, 43,119, 97,114, 24, 24, 6, 0, 31, 1, 1, 27,115, 97, 99,114,105,102,
105, 99,101, 92, 13,109,111,114,110,105,110,103, 13, 0, 0, 0, 5, 1,162,
0, 1,239, 1,221, 1,203, 1,182, 1,162, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 50, 6, 0, 23, 1, 1,
23,119,114, 97,116,104, 21, 45, 98,101,103, 97,116, 19, 49, 6, 0, 21, 1,
1, 27,116,114,101,101, 98, 17,109,111,114,110,105,110,103, 16, 48, 6, 0,
19, 1, 1, 23,115,105,120, 71, 3, 97,110,103,101,108, 16, 47, 6, 0, 21,
1, 1, 21,100,105,101,100, 7, 19,103,111,110,101, 15, 46, 6, 0, 19, 1,
1, 21,111,105,108, 81, 25,119, 97,108,107, 10, 0, 0, 0, 40, 0,106, 0,
1,246, 1,236, 1,226, 1,218, 1,209, 1,199, 1,187, 1,179, 1,169, 1,
158, 1,145, 1,136, 1,127, 1,117, 1,107, 1, 98, 1, 82, 1, 72, 1, 63,
1, 51, 1, 42, 1, 30, 1, 20, 1, 12, 1, 3, 0,248, 0,239, 0,225, 0,
216, 0,207, 0,197, 0,188, 0,180, 0,170, 0,161, 0,152, 0,141, 0,129,
0,118, 0,106, 0, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,116,114,
101,101, 49, 11, 3, 27, 1,116,104,121,115,101,108,102, 27, 10, 3, 25, 1,
116,104,111,117,103,104, 8, 11, 3, 27, 1,116,104,101,114,101,105,110, 15,
10, 3, 25, 1,116,101,109,112,108,101, 43, 8, 3, 21, 1,116,101,108,108,
25, 8, 3, 21, 1,115,111,109,101, 11, 9, 3, 23, 1,115,109,111,116,101,
14, 7, 3, 19, 1,115,105,120, 48, 8, 3, 21, 1,115,104,101,119, 16, 9,
3, 23, 1,115,101,114,118,101, 37, 8, 3, 21, 1,115,101,110,100, 7, 8,
3, 21, 1,115, 97,118,101, 9, 13, 3, 31, 1,115, 97, 99,114,105,102,105,
99,101, 24, 8, 3, 21, 1,112,111,111,114, 40, 10, 3, 25, 1,112,108, 97,
99,101,115, 28, 8, 3, 21, 1,112, 97,114,116, 30, 7, 3, 19, 1,111,105,
108, 46, 9, 3, 23, 1,111,102,102,101,114, 3, 11, 3, 27, 1,110,111,116,
104,105,110,103, 20, 8, 3, 21, 1,110,101, 97,114, 36, 11, 3, 27, 1,109,
111,114,110,105,110,103, 17, 8, 3, 21, 1,108,111,110,103, 35, 9, 3, 23,
1,107,110,111,119,110, 23, 15, 3, 35, 1,105,110,104, 97, 98,105,116, 97,
110,116,115, 45, 8, 3, 21, 1,103,111,110,101, 32, 9, 3, 23, 1,102,114,
117,105,116, 38, 9, 3, 23, 1,100,119,101,108,116, 18, 8, 3, 21, 1,100,
111,116,104, 39, 8, 3, 21, 1,100,105,101,100, 47, 12, 3, 29, 1,100,101,
112, 97,114,116,101,100, 26, 10, 3, 25, 1, 99,117, 98,105,116,115, 33, 9,
3, 23, 1, 99,104,105,108,100, 42, 7, 3, 19, 1, 99, 97,110, 4, 11, 3,
27, 1, 98,101,116,119,101,101,110, 10, 9, 3, 23, 1, 98,101,103, 97,116,
19, 8, 3, 21, 1, 98,101, 97,114, 29, 7, 3, 19, 1, 97,114,107, 13, 9,
3, 23, 1, 97,110,103,101,114, 5, 9, 3, 23, 1, 97,110,103,101,108, 44,
9, 3, 23, 1, 97, 98,111,118,101, 22, 10, 0, 0, 0, 9, 1,171, 0, 1,
247, 1,238, 1,230, 1,221, 1,211, 1,202, 1,191, 1,181, 1,171, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
9, 3, 23, 1,119,114, 97,116,104, 50, 9, 3, 23, 1,119,111,114,107,115,
2, 10, 3, 25, 1,119,105,115,100,111,109, 6, 8, 3, 21, 1,119,105,110,
101, 12, 9, 3, 23, 1,119,104,105,108,101, 31, 8, 3, 21, 1,119, 97,121,
115, 41, 7, 3, 19, 1,119, 97,114, 21, 8, 3, 21, 1,119, 97,108,107, 34,
8, 3, 23, 9,116,114,117,116,104, 13, 0, 0, 0, 5, 0, 84, 0, 1, 78,
0,249, 0,177, 1,163, 0, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 91, 19,
7, 21, 19, 19, 8,129, 33,118,105,101,119,118, 50, 49,118, 50, 49, 67, 82,
69, 65, 84, 69, 32, 86, 73, 69, 87, 32,118, 50, 49, 40, 97, 44, 98, 44, 99,
44,100, 44,101, 41, 32, 65, 83, 32, 83, 69, 76, 69, 67, 84, 32, 97, 44, 98,
44, 99, 44,100, 44,101, 32, 70, 82, 79, 77, 32,116, 50, 32, 79, 82, 68, 69,
82, 32, 66, 89, 32, 98, 32, 76, 73, 77, 73, 84, 32, 49, 48, 70, 17, 6, 21,
19, 19, 8,121,118,105,101,119,118, 53, 48,118, 53, 48, 67, 82, 69, 65, 84,
69, 32, 86, 73, 69, 87, 32,118, 53, 48, 40, 97, 44, 98, 41, 32, 65, 83, 32,
83, 69, 76, 69, 67, 84, 32, 97, 44, 98, 32, 70, 82, 79, 77, 32,116, 53, 32,
87, 72, 69, 82, 69, 32, 97, 60, 62, 50, 53, 83, 16, 7, 21, 19, 19, 8,129,
17,118,105,101,119,118, 52, 48,118, 52, 48, 67, 82, 69, 65, 84, 69, 32, 86,
73, 69, 87, 32,118, 52, 48, 40, 97, 44, 98, 44, 99, 44,100, 44,101, 41, 32,
65, 83, 32, 83, 69, 76, 69, 67, 84, 32, 97, 44, 98, 44, 99, 44,100, 44,101,
32, 70, 82, 79, 77, 32,116, 52, 32, 87, 72, 69, 82, 69, 32, 97, 60, 62, 50,
53, 83, 15, 7, 21, 19, 19, 8,129, 17,118,105,101,119,118, 51, 48,118, 51,
48, 67, 82, 69, 65, 84, 69, 32, 86, 73, 69, 87, 32,118, 51, 48, 40, 97, 44,
98, 44, 99, 44,100, 44,101, 41, 32, 65, 83, 32, 83, 69, 76, 69, 67, 84, 32,
97, 44, 98, 44, 99, 44,100, 44,101, 32, 70, 82, 79, 77, 32,116, 51, 32, 87,
72, 69, 82, 69, 32, 97, 60, 62, 50, 53, 91, 18, 7, 21, 19, 19, 8,129, 33,
118,105,101,119,118, 49, 49,118, 49, 49, 67, 82, 69, 65, 84, 69, 32, 86, 73,
69, 87, 32,118, 49, 49, 40, 97, 44, 98, 44, 99, 44,100, 44,101, 41, 32, 65,
83, 32, 83, 69, 76, 69, 67, 84, 32, 97, 44, 98, 44, 99, 44,100, 44,101, 32,
70, 82, 79, 77, 32,116, 49, 32, 79, 82, 68, 69, 82, 32, 66, 89, 32, 98, 32,
76, 73, 77, 73, 84, 32, 49, 48, 13, 1,163, 0, 4, 0, 40, 0, 1, 70, 0,
233, 0,152, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,110, 23, 7, 21, 19, 19, 8,129, 71,
118,105,101,119,118, 49, 50,118, 49, 50, 67, 82, 69, 65, 84, 69, 32, 86, 73,
69, 87, 32,118, 49, 50, 40, 97, 44, 98, 44, 99, 44,100, 44,101, 41, 32, 65,
83, 10, 32, 32, 83, 69, 76, 69, 67, 84, 32,115,117,109, 40, 97, 41, 44, 32,
97,118,103, 40, 98, 41, 44, 32, 99,111,117,110,116, 40, 42, 41, 44, 32,109,
105,110, 40,100, 41, 44, 32,101, 32, 70, 82, 79, 77, 32,116, 49, 32, 71, 82,
79, 85, 80, 32, 66, 89, 32, 53, 79, 22, 7, 21, 19, 19, 8,129, 9,118,105,
101,119,118, 53, 49,118, 53, 49, 67, 82, 69, 65, 84, 69, 32, 86, 73, 69, 87,
32,118, 53, 49, 40, 97, 44, 98, 41, 32, 65, 83, 32, 83, 69, 76, 69, 67, 84,
32, 97, 44, 98, 32, 70, 82, 79, 77, 32,116, 53, 32, 79, 82, 68, 69, 82, 32,
66, 89, 32, 98, 32, 76, 73, 77, 73, 84, 32, 49, 48, 91, 21, 7, 21, 19, 19,
8,129, 33,118,105,101,119,118, 52, 49,118, 52, 49, 67, 82, 69, 65, 84, 69,
32, 86, 73, 69, 87, 32,118, 52, 49, 40, 97, 44, 98, 44, 99, 44,100, 44,101,
41, 32, 65, 83, 32, 83, 69, 76, 69, 67, 84, 32, 97, 44, 98, 44, 99, 44,100,
44,101, 32, 70, 82, 79, 77, 32,116, 52, 32, 79, 82, 68, 69, 82, 32, 66, 89,
32, 98, 32, 76, 73, 77, 73, 84, 32, 49, 48, 91, 20, 7, 21, 19, 19, 8,129,
33,118,105,101,119,118, 51, 49,118, 51, 49, 67, 82, 69, 65, 84, 69, 32, 86,
73, 69, 87, 32,118, 51, 49, 40, 97, 44, 98, 44, 99, 44,100, 44,101, 41, 32,
65, 83, 32, 83, 69, 76, 69, 67, 84, 32, 97, 44, 98, 44, 99, 44,100, 44,101,
32, 70, 82, 79, 77, 32,116, 51, 32, 79, 82, 68, 69, 82, 32, 66, 89, 32, 98,
32, 76, 73, 77, 73, 84, 32, 49, 48, 0, 0, 0, 93, 19, 19, 8,129, 33,118,
105,101,119,118, 50, 49,118, 50, 49, 67, 82, 69, 65, 84, 69, 32, 86, 73, 69,
87, 32,118, 50, 49, 40, 97, 44, 98, 44, 99, 44,100, 44,101, 41, 32, 65, 83,
32, 83, 69, 76, 69, 67, 84, 32, 97, 44, 98, 44, 99, 44,100, 44,101, 32, 70,
82, 79, 77, 32,116, 50, 32, 79, 82, 68, 69, 82, 32, 66, 89, 32, 98, 32, 76,
73, 77, 73, 84, 32, 49, 48, 13, 0, 0, 0, 3, 0, 66, 0, 1,107, 0,214,
0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,129, 17, 26,
7, 21, 19, 19, 8,130, 13,118,105,101,119,118, 52, 50,118, 52, 50, 67, 82,
69, 65, 84, 69, 32, 86, 73, 69, 87, 32,118, 52, 50, 40, 97, 44, 98, 44, 99,
44,100, 44,101, 41, 32, 65, 83, 10, 32, 32, 83, 69, 76, 69, 67, 84, 32,115,
117,109, 40, 97, 41, 44, 32, 97,118,103, 40, 98, 41, 44, 32, 99,111,117,110,
116, 40, 42, 41, 44, 32,109,105,110, 40,100, 41, 44, 32,101, 32, 70, 82, 79,
77, 32,116, 52, 32, 71, 82, 79, 85, 80, 32, 66, 89, 32, 53, 10, 32, 32, 32,
32, 72, 65, 86, 73, 78, 71, 32,109,105,110, 40,100, 41, 60, 51, 48, 32, 79,
82, 68, 69, 82, 32, 66, 89, 32, 51, 44, 32, 49,129, 18, 25, 7, 21, 19, 19,
8,130, 15,118,105,101,119,118, 51, 50,118, 51, 50, 67, 82, 69, 65, 84, 69,
32, 86, 73, 69, 87, 32,118, 51, 50, 40, 97, 44, 98, 44, 99, 44,100, 44,101,
41, 32, 65, 83, 10, 32, 32, 83, 69, 76, 69, 67, 84, 32,115,117,109, 40, 97,
41, 44, 32, 97,118,103, 40, 98, 41, 44, 32, 99,111,117,110,116, 40, 42, 41,
44, 32,109,105,110, 40,100, 41, 44, 32,101, 32, 70, 82, 79, 77, 32,116, 51,
32, 71, 82, 79, 85, 80, 32, 66, 89, 32, 53, 10, 32, 32, 32, 32, 72, 65, 86,
73, 78, 71, 32, 99,111,117,110,116, 40, 42, 41, 62, 49, 32, 79, 82, 68, 69,
82, 32, 66, 89, 32, 51, 44, 32, 49,129, 18, 24, 7, 21, 19, 19, 8,130, 15,
118,105,101,119,118, 50, 50,118, 50, 50, 67, 82, 69, 65, 84, 69, 32, 86, 73,
69, 87, 32,118, 50, 50, 40, 97, 44, 98, 44, 99, 44,100, 44,101, 41, 32, 65,
83, 10, 32, 32, 83, 69, 76, 69, 67, 84, 32,115,117,109, 40, 97, 41, 44, 32,
97,118,103, 40, 98, 41, 44, 32, 99,111,117,110,116, 40, 42, 41, 44, 32,109,
105,110, 40,100, 41, 44, 32,101, 32, 70, 82, 79, 77, 32,116, 50, 32, 71, 82,
79, 85, 80, 32, 66, 89, 32, 53, 10, 32, 32, 32, 32, 72, 65, 86, 73, 78, 71,
32, 99,111,117,110,116, 40, 42, 41, 62, 49, 32, 79, 82, 68, 69, 82, 32, 66,
89, 32, 51, 44, 32, 49, 13, 1,108, 0, 3, 0, 83, 0, 0,225, 0, 83, 1,
136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,129, 11, 28, 7, 21, 19,
19, 8,130, 1,118,105,101,119,118, 49, 51,118, 49, 51, 67, 82, 69, 65, 84,
69, 32, 86, 73, 69, 87, 32,118, 49, 51, 40, 97, 44, 98, 44, 99, 44,100, 44,
101, 41, 32, 65, 83, 10, 32, 32, 83, 69, 76, 69, 67, 84, 32, 97, 44, 98, 44,
99, 44,100, 44,101, 32, 70, 82, 79, 77, 32,116, 49, 10, 32, 32, 85, 78, 73,
79, 78, 32, 83, 69, 76, 69, 67, 84, 32, 97, 44, 98, 44, 99, 44,100, 44,101,
32, 70, 82, 79, 77, 32,116, 50, 10, 32, 32, 85, 78, 73, 79, 78, 32, 83, 69,
76, 69, 67, 84, 32, 97, 44, 98, 44, 99, 44,100, 44,101, 32, 70, 82, 79, 77,
32,116, 51,129, 8, 27, 7, 21, 19, 19, 8,129,123,118,105,101,119,118, 53,
50,118, 53, 50, 67, 82, 69, 65, 84, 69, 32, 86, 73, 69, 87, 32,118, 53, 50,
40, 97, 44, 98, 44, 99, 44,100, 44,101, 41, 32, 65, 83, 10, 32, 32, 83, 69,
76, 69, 67, 84, 32, 99,111,117,110,116, 40, 42, 41, 44, 32,109,105,110, 40,
98, 41, 44, 32,115,117, 98,115,116,114, 40, 98, 44, 49, 44, 49, 41, 44, 32,
109,105,110, 40, 97, 41, 44, 32,109, 97,120, 40, 97, 41, 32, 70, 82, 79, 77,
32,116, 53, 10, 32, 32, 32, 71, 82, 79, 85, 80, 32, 66, 89, 32, 51, 32, 79,
82, 68, 69, 82, 32, 66, 89, 32, 49, 0, 0, 0, 28, 21, 19, 19, 8,130, 13,
118,105,101,119,118, 52, 50,118, 52, 50, 67, 82, 69, 65, 84, 69, 32, 86,118,
29, 7, 21, 19, 19, 8,129, 87,118,105,101,119,118, 50, 51,118, 50, 51, 67,
82, 69, 65, 84, 69, 32, 86, 73, 69, 87, 32,118, 50, 51, 40, 97, 44, 98, 44,
99, 44,100, 44,101, 41, 32, 65, 83, 10, 32, 32, 83, 69, 76, 69, 67, 84, 32,
97, 44, 98, 44, 99, 44,100, 44,101, 32, 70, 82, 79, 77, 32,116, 49, 10, 32,
32, 69, 88, 67, 69, 80, 84, 32, 83, 69, 76, 69, 67, 84, 32, 97, 44, 98, 44,
99, 44,100, 44,101, 32, 70, 82, 79, 77, 32,116, 49, 32, 87, 72, 69, 82, 69,
32, 98, 60, 50, 53, 13, 0, 0, 0, 3, 0, 40, 0, 1,134, 1, 12, 0, 40,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,129, 97, 32, 7, 21, 19, 19, 8,131, 45,118,105,
101,119,118, 54, 50,118, 54, 50, 67, 82, 69, 65, 84, 69, 32, 86, 73, 69, 87,
32,118, 54, 50, 40, 97, 44, 98, 44, 99, 44,100, 44,101, 41, 32, 65, 83, 10,
32, 32, 83, 69, 76, 69, 67, 84, 32,116, 49, 46, 97, 44,116, 50, 46, 98, 44,
116, 51, 46, 99, 44,116, 52, 46,100, 44,116, 53, 46, 98, 10, 32, 32, 32, 32,
70, 82, 79, 77, 32,116, 49, 32, 74, 79, 73, 78, 32,116, 50, 32, 79, 78, 32,
40,116, 49, 46, 97, 61,116, 50, 46, 98, 41, 10, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 74, 79, 73, 78, 32,116, 51, 32, 79, 78, 32, 40,116, 49,
46, 97, 61,116, 51, 46, 97, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 74, 79, 73, 78, 32,116, 52, 32, 79, 78, 32, 40,116, 52, 46, 98, 61,
116, 51, 46, 98, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 76,
69, 70, 84, 32, 74, 79, 73, 78, 32,116, 53, 32, 79, 78, 32, 40,116, 53, 46,
97, 61,116, 49, 46, 99, 41,120, 31, 7, 21, 19, 19, 8,129, 91,118,105,101,
119,118, 54, 49,118, 54, 49, 67, 82, 69, 65, 84, 69, 32, 86, 73, 69, 87, 32,
118, 54, 49, 40, 97, 44, 98, 44, 99, 44,100, 44,101, 41, 32, 65, 83, 10, 32,
32, 83, 69, 76, 69, 67, 84, 32,116, 50, 46, 97, 44,116, 51, 46, 98, 44,116,
50, 46, 99, 44,116, 51, 46,100, 44,116, 50, 46,101, 10, 32, 32, 32, 32, 70,
82, 79, 77, 32,116, 50, 32, 76, 69, 70, 84, 32, 74, 79, 73, 78, 32,116, 51,
32, 79, 78, 32, 40,116, 50, 46, 97, 61,116, 51, 46, 97, 41,120, 30, 7, 21,
19, 19, 8,129, 91,118,105,101,119,118, 54, 48,118, 54, 48, 67, 82, 69, 65,
84, 69, 32, 86, 73, 69, 87, 32,118, 54, 48, 40, 97, 44, 98, 44, 99, 44,100,
44,101, 41, 32, 65, 83, 10, 32, 32, 83, 69, 76, 69, 67, 84, 32,116, 49, 46,
97, 44,116, 50, 46, 98, 44,116, 49, 46, 99, 44,116, 50, 46,100, 44,116, 49,
46,101, 10, 32, 32, 32, 32, 70, 82, 79, 77, 32,116, 49, 32, 76, 69, 70, 84,
32, 74, 79, 73, 78, 32,116, 50, 32, 79, 78, 32, 40,116, 49, 46, 97, 61,116,
50, 46, 98, 41, 13, 0, 0, 0, 1, 1, 73, 0, 1, 73, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,129, 52, 33, 7, 21, 19, 19, 8,130,
83,118,105,101,119,118, 55, 48,118, 55, 48, 67, 82, 69, 65, 84, 69, 32, 86,
73, 69, 87, 32,118, 55, 48, 40, 97, 44, 98, 44, 99, 44,100, 44,101, 41, 32,
65, 83, 10, 32, 32, 87, 73, 84, 72, 32, 82, 69, 67, 85, 82, 83, 73, 86, 69,
32, 99, 48, 40,120, 41, 32, 65, 83, 32, 40, 86, 65, 76, 85, 69, 83, 40, 49,
41, 32, 85, 78, 73, 79, 78, 32, 65, 76, 76, 32, 83, 69, 76, 69, 67, 84, 32,
120, 43, 49, 32, 70, 82, 79, 77, 32, 99, 48, 32, 87, 72, 69, 82, 69, 32,120,
60, 57, 41, 10, 32, 32, 83, 69, 76, 69, 67, 84, 32,120, 44, 32, 98, 44, 32,
99, 44, 32,100, 44, 32,101, 32, 70, 82, 79, 77, 32, 99, 48, 32, 74, 79, 73,
78, 32,116, 49, 32, 79, 78, 32, 40,116, 49, 46, 97, 61, 53, 48, 45, 99, 48,
46,120, 41,
};
+142
View File
@@ -0,0 +1,142 @@
-- Run this script through the sqlite3 command-line shell in order to generate
-- a database file containing lots of data for testing purposes.
--
-- This script assumes that the "bin2c" program is available on ones $PATH.
-- The "bin2c" program reads a binary file and outputs C-code that creates
-- an array of bytes holding the content of that file.
--
-- This script is designed to create many tables and views all having
-- 5 columns, "a" through "e", and with a variety of integers, short strings,
-- and NULL values.
--
.open -new testdb01.db
PRAGMA page_size=512;
BEGIN;
CREATE TABLE t1(a INTEGER PRIMARY KEY, b INT, c INT, d INT, e INT);
WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<50)
INSERT INTO t1(a,b,c,d,e) SELECT x,abs(random()%51),
abs(random()%100), abs(random()%51), abs(random()%100) FROM c;
CREATE TABLE t2(a INT, b INT, c INT,d INT,e INT,PRIMARY KEY(b,a))WITHOUT ROWID;
INSERT INTO t2 SELECT * FROM t1;
CREATE TABLE t3(a,b,c,d,e);
INSERT INTO t3 SELECT a,b,c,d,e FROM t1 ORDER BY random() LIMIT 5;
INSERT INTO t3 SELECT null,b,c,d,e FROM t1 ORDER BY random() LIMIT 5;
INSERT INTO t3 SELECT a,null,c,d,e FROM t1 ORDER BY random() LIMIT 5;
INSERT INTO t3 SELECT a,b,null,d,e FROM t1 ORDER BY random() LIMIT 5;
INSERT INTO t3 SELECT a,b,c,null,e FROM t1 ORDER BY random() LIMIT 5;
INSERT INTO t3 SELECT a,b,c,d,null FROM t1 ORDER BY random() LIMIT 5;
INSERT INTO t3 SELECT null,null,null,null,null FROM t1 LIMIT 5;
CREATE INDEX t3x1 ON t3(a,b,c,d,e);
CREATE TABLE t4(a INT UNIQUE NOT NULL, b INT UNIQUE NOT NULL,c,d,e);
INSERT OR IGNORE INTO t4 SELECT a,b,c,d,e FROM t3;
CREATE TABLE t5(a INTEGER PRIMARY KEY, b TEXT UNIQUE,c,d,e);
INSERT INTO t5(b) VALUES
('truth'),
('works'),
('offer'),
('can'),
('anger'),
('wisdom'),
('send'),
('though'),
('save'),
('between'),
('some'),
('wine'),
('ark'),
('smote'),
('therein'),
('shew'),
('morning'),
('dwelt'),
('begat'),
('nothing'),
('war'),
('above'),
('known'),
('sacrifice'),
('tell'),
('departed'),
('thyself'),
('places'),
('bear'),
('part'),
('while'),
('gone'),
('cubits'),
('walk'),
('long'),
('near'),
('serve'),
('fruit'),
('doth'),
('poor'),
('ways'),
('child'),
('temple'),
('angel'),
('inhabitants'),
('oil'),
('died'),
('six'),
('tree'),
('wrath');
UPDATE t1 SET e=(SELECT b FROM t5 WHERE t5.a=(t1.e%51));
UPDATE t5 SET (c,d,e) =
(SELECT c,d,e FROM t1 WHERE t1.a=abs(t5.a+random()/100)%50+1);
UPDATE t2 SET e=(SELECT b FROM t5 WHERE t5.a=(t2.e%51));
UPDATE t3 SET e=(SELECT b FROM t5 WHERE t5.a=t3.e);
CREATE INDEX t1e ON t1(e);
CREATE INDEX t2ed ON t2(e,d);
CREATE VIEW v00(a,b,c,d,e) AS SELECT 1,1,1,1,'one';
CREATE VIEW v10(a,b,c,d,e) AS SELECT a,b,c,d,e FROM t1 WHERE a<>25;
CREATE VIEW v20(a,b,c,d,e) AS SELECT a,b,c,d,e FROM t2 WHERE a<>25;
CREATE VIEW v30(a,b,c,d,e) AS SELECT a,b,c,d,e FROM t3 WHERE a<>25;
CREATE VIEW v40(a,b,c,d,e) AS SELECT a,b,c,d,e FROM t4 WHERE a<>25;
CREATE VIEW v50(a,b) AS SELECT a,b FROM t5 WHERE a<>25;
CREATE VIEW v11(a,b,c,d,e) AS SELECT a,b,c,d,e FROM t1 ORDER BY b LIMIT 10;
CREATE VIEW v21(a,b,c,d,e) AS SELECT a,b,c,d,e FROM t2 ORDER BY b LIMIT 10;
CREATE VIEW v31(a,b,c,d,e) AS SELECT a,b,c,d,e FROM t3 ORDER BY b LIMIT 10;
CREATE VIEW v41(a,b,c,d,e) AS SELECT a,b,c,d,e FROM t4 ORDER BY b LIMIT 10;
CREATE VIEW v51(a,b) AS SELECT a,b FROM t5 ORDER BY b LIMIT 10;
CREATE VIEW v12(a,b,c,d,e) AS
SELECT sum(a), avg(b), count(*), min(d), e FROM t1 GROUP BY 5;
CREATE VIEW v22(a,b,c,d,e) AS
SELECT sum(a), avg(b), count(*), min(d), e FROM t2 GROUP BY 5
HAVING count(*)>1 ORDER BY 3, 1;
CREATE VIEW v32(a,b,c,d,e) AS
SELECT sum(a), avg(b), count(*), min(d), e FROM t3 GROUP BY 5
HAVING count(*)>1 ORDER BY 3, 1;
CREATE VIEW v42(a,b,c,d,e) AS
SELECT sum(a), avg(b), count(*), min(d), e FROM t4 GROUP BY 5
HAVING min(d)<30 ORDER BY 3, 1;
CREATE VIEW v52(a,b,c,d,e) AS
SELECT count(*), min(b), substr(b,1,1), min(a), max(a) FROM t5
GROUP BY 3 ORDER BY 1;
CREATE VIEW v13(a,b,c,d,e) AS
SELECT a,b,c,d,e FROM t1
UNION SELECT a,b,c,d,e FROM t2
UNION SELECT a,b,c,d,e FROM t3;
CREATE VIEW v23(a,b,c,d,e) AS
SELECT a,b,c,d,e FROM t1
EXCEPT SELECT a,b,c,d,e FROM t1 WHERE b<25;
CREATE VIEW v60(a,b,c,d,e) AS
SELECT t1.a,t2.b,t1.c,t2.d,t1.e
FROM t1 LEFT JOIN t2 ON (t1.a=t2.b);
CREATE VIEW v61(a,b,c,d,e) AS
SELECT t2.a,t3.b,t2.c,t3.d,t2.e
FROM t2 LEFT JOIN t3 ON (t2.a=t3.a);
CREATE VIEW v62(a,b,c,d,e) AS
SELECT t1.a,t2.b,t3.c,t4.d,t5.b
FROM t1 JOIN t2 ON (t1.a=t2.b)
JOIN t3 ON (t1.a=t3.a)
JOIN t4 ON (t4.b=t3.b)
LEFT JOIN t5 ON (t5.a=t1.c);
CREATE VIEW v70(a,b,c,d,e) AS
WITH RECURSIVE c0(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c0 WHERE x<9)
SELECT x, b, c, d, e FROM c0 JOIN t1 ON (t1.a=50-c0.x);
COMMIT;
VACUUM;
.shell bin2c testdb01.db
+309
View File
@@ -0,0 +1,309 @@
/*
** 2018-03-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.
**
*************************************************************************
**
** This program attempts to verify the correctness of the SQLite query
** optimizer by fuzzing.
**
** The input is an SQL script, presumably generated by a fuzzer. The
** argument is the name of the input. If no files are named, standard
** input is read.
**
** The SQL script is run twice, once with optimization enabled, and again
** with optimization disabled. If the output is not equivalent, an error
** is printed and the program returns non-zero.
*/
/* Include the SQLite amalgamation, after making appropriate #defines.
*/
#define SQLITE_THREADSAFE 0
#define SQLITE_OMIT_LOAD_EXTENSION 1
#define SQLITE_ENABLE_DESERIALIZE 1
#include "sqlite3.c"
/* Content of the read-only test database */
#include "optfuzz-db01.c"
/*
** Prepare a single SQL statement. Panic if anything goes wrong
*/
static sqlite3_stmt *prepare_sql(sqlite3 *db, const char *zFormat, ...){
char *zSql;
int rc;
sqlite3_stmt *pStmt = 0;
va_list ap;
va_start(ap, zFormat);
zSql = sqlite3_vmprintf(zFormat, ap);
va_end(ap);
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
if( rc ){
printf("Error: %s\nSQL: %s\n",
sqlite3_errmsg(db), zSql);
exit(1);
}
sqlite3_free(zSql);
return pStmt;
}
/*
** Run SQL. Panic if anything goes wrong
*/
static void run_sql(sqlite3 *db, const char *zFormat, ...){
char *zSql;
int rc;
char *zErr = 0;
va_list ap;
va_start(ap, zFormat);
zSql = sqlite3_vmprintf(zFormat, ap);
va_end(ap);
rc = sqlite3_exec(db, zSql, 0, 0, &zErr);
if( rc || zErr ){
printf("Error: %s\nsqlite3_errmsg: %s\nSQL: %s\n",
zErr, sqlite3_errmsg(db), zSql);
exit(1);
}
sqlite3_free(zSql);
}
/*
** Run one or more SQL statements contained in zSql against database dbRun.
** Store the input in database dbOut.
*/
static int optfuzz_exec(
sqlite3 *dbRun, /* The database on which the SQL executes */
const char *zSql, /* The SQL to be executed */
sqlite3 *dbOut, /* Store results in this database */
const char *zOutTab, /* Store results in this table of dbOut */
int *pnStmt, /* Write the number of statements here */
int *pnRow, /* Write the number of rows here */
int bTrace /* Print query results if true */
){
int rc = SQLITE_OK; /* Return code */
const char *zLeftover; /* Tail of unprocessed SQL */
sqlite3_stmt *pStmt = 0; /* The current SQL statement */
sqlite3_stmt *pIns = 0; /* Statement to insert into dbOut */
const char *zCol; /* Single column value */
int nCol; /* Number of output columns */
char zLine[4000]; /* Complete row value */
run_sql(dbOut, "BEGIN");
run_sql(dbOut, "CREATE TABLE IF NOT EXISTS staging(x TEXT)");
run_sql(dbOut, "CREATE TABLE IF NOT EXISTS \"%w\"(x TEXT)", zOutTab);
pIns = prepare_sql(dbOut, "INSERT INTO staging(x) VALUES(?1)");
*pnRow = *pnStmt = 0;
while( rc==SQLITE_OK && zSql && zSql[0] ){
zLeftover = 0;
rc = sqlite3_prepare_v2(dbRun, zSql, -1, &pStmt, &zLeftover);
zSql = zLeftover;
assert( rc==SQLITE_OK || pStmt==0 );
if( rc!=SQLITE_OK ){
printf("Error with [%s]\n%s\n", zSql, sqlite3_errmsg(dbRun));
break;
}
if( !pStmt ) continue;
(*pnStmt)++;
nCol = sqlite3_column_count(pStmt);
run_sql(dbOut, "DELETE FROM staging;");
while( sqlite3_step(pStmt)==SQLITE_ROW ){
int i, j;
for(i=j=0; i<nCol && j<sizeof(zLine)-50; i++){
int eType = sqlite3_column_type(pStmt, i);
if( eType==SQLITE_NULL ){
zCol = "NULL";
}else{
zCol = (const char*)sqlite3_column_text(pStmt, i);
}
if( i ) zLine[j++] = ',';
if( eType==SQLITE_TEXT ){
sqlite3_snprintf(sizeof(zLine)-j, zLine+j, "'%q'", zCol);
}else{
sqlite3_snprintf(sizeof(zLine)-j, zLine+j, "%s", zCol);
}
j += (int)strlen(zLine+j);
}
/* Detect if any row is too large and throw an error, because we will
** want to go back and look more closely at that case */
if( j>=sizeof(zLine)-100 ){
printf("Excessively long output line: %d bytes\n" ,j);
exit(1);
}
if( bTrace ){
printf("%s\n", zLine);
}
(*pnRow)++;
sqlite3_bind_text(pIns, 1, zLine, j, SQLITE_TRANSIENT);
rc = sqlite3_step(pIns);
assert( rc==SQLITE_DONE );
rc = sqlite3_reset(pIns);
}
run_sql(dbOut,
"INSERT INTO \"%w\"(x) VALUES('### %q ###')",
zOutTab, sqlite3_sql(pStmt)
);
run_sql(dbOut,
"INSERT INTO \"%w\"(x) SELECT group_concat(x,char(10))"
" FROM (SELECT x FROM staging ORDER BY x)",
zOutTab
);
run_sql(dbOut, "COMMIT");
sqlite3_finalize(pStmt);
pStmt = 0;
}
sqlite3_finalize(pStmt);
sqlite3_finalize(pIns);
return rc;
}
/*
** Read the content of file zName into memory obtained from sqlite3_malloc64()
** and return a pointer to the buffer. The caller is responsible for freeing
** the memory.
**
** If parameter pnByte is not NULL, (*pnByte) is set to the number of bytes
** read.
**
** For convenience, a nul-terminator byte is always appended to the data read
** from the file before the buffer is returned. This byte is not included in
** the final value of (*pnByte), if applicable.
**
** NULL is returned if any error is encountered. The final value of *pnByte
** is undefined in this case.
*/
static char *readFile(const char *zName, int *pnByte){
FILE *in = fopen(zName, "rb");
long nIn;
size_t nRead;
char *pBuf;
if( in==0 ) return 0;
fseek(in, 0, SEEK_END);
nIn = ftell(in);
rewind(in);
pBuf = sqlite3_malloc64( nIn+1 );
if( pBuf==0 ) return 0;
nRead = fread(pBuf, nIn, 1, in);
fclose(in);
if( nRead!=1 ){
sqlite3_free(pBuf);
return 0;
}
pBuf[nIn] = 0;
if( pnByte ) *pnByte = nIn;
return pBuf;
}
int main(int argc, char **argv){
int nIn = 0; /* Number of input files */
char **azIn = 0; /* Names of input files */
sqlite3 *dbOut = 0; /* Database to hold results */
sqlite3 *dbRun = 0; /* Database used for tests */
int bTrace = 0; /* Show query results */
int bShowValid = 0; /* Just list inputs that are valid SQL */
int nRow, nStmt; /* Number of rows and statements */
int i, rc;
for(i=1; i<argc; i++){
const char *z = argv[i];
if( z[0]=='-' && z[1]=='-' ) z++;
if( strcmp(z,"-help")==0 ){
printf("Usage: %s [OPTIONS] FILENAME ...\n", argv[0]);
printf("Options:\n");
printf(" --help Show his message\n");
printf(" --output-trace Show each line of SQL output\n");
printf(" --valid-sql List FILEs that are valid SQL\n");
return 0;
}
else if( strcmp(z,"-output-trace")==0 ){
bTrace = 1;
}
else if( strcmp(z,"-valid-sql")==0 ){
bShowValid = 1;
}
else if( z[0]=='-' ){
printf("unknown option \"%s\". Use --help for details\n", argv[i]);
return 1;
}
else {
nIn++;
azIn = realloc(azIn, sizeof(azIn[0])*nIn);
if( azIn==0 ){
printf("out of memory\n");
exit(1);
}
azIn[nIn-1] = argv[i];
}
}
sqlite3_open(":memory:", &dbOut);
sqlite3_open(":memory:", &dbRun);
sqlite3_deserialize(dbRun, "main", data001, sizeof(data001),
sizeof(data001), SQLITE_DESERIALIZE_READONLY);
for(i=0; i<nIn; i++){
char *zSql = readFile(azIn[i], 0);
sqlite3_stmt *pCk;
sqlite3_exec(dbRun, "ROLLBACK", 0, 0, 0);
if( bShowValid ){
rc = sqlite3_exec(dbRun, zSql, 0, 0, 0);
if( rc==SQLITE_OK ) printf("%s\n", azIn[i]);
sqlite3_free(zSql);
continue;
}
sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, dbRun, 0);
if( bTrace ) printf("%s: Optimized\n", azIn[i]);
rc = optfuzz_exec(dbRun, zSql, dbOut, "opt", &nStmt, &nRow, bTrace);
if( rc ){
printf("%s: optimized run failed: %s\n",
azIn[i], sqlite3_errmsg(dbRun));
}else{
sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, dbRun, 0xffff);
if( bTrace ) printf("%s: Non-optimized\n", azIn[i]);
rc = optfuzz_exec(dbRun, zSql, dbOut, "noopt", &nStmt, &nRow, bTrace);
if( rc ){
printf("%s: non-optimized run failed: %s\n",
azIn[i], sqlite3_errmsg(dbRun));
exit(1);
}
pCk = prepare_sql(dbOut,
"SELECT (SELECT group_concat(x,char(10)) FROM opt)=="
" (SELECT group_concat(x,char(10)) FROM noopt)");
rc = sqlite3_step(pCk);
if( rc!=SQLITE_ROW ){
printf("%s: comparison failed\n", sqlite3_errmsg(dbOut));
exit(1);
}
if( !sqlite3_column_int(pCk, 0) ){
printf("%s: opt/no-opt outputs differ\n", azIn[i]);
pCk = prepare_sql(dbOut,
"SELECT group_concat(x,char(10)) FROM opt "
"UNION ALL "
"SELECT group_concat(x,char(10)) FROM noopt");
sqlite3_step(pCk);
printf("opt:\n%s\n", sqlite3_column_text(pCk,0));
sqlite3_step(pCk);
printf("noopt:\n%s\n", sqlite3_column_text(pCk,0));
exit(1);
}else{
printf("%s: %d stmts %d rows ok\n", azIn[i], nStmt, nRow);
}
sqlite3_finalize(pCk);
}
sqlite3_free(zSql);
}
sqlite3_close(dbRun);
sqlite3_close(dbOut);
free(azIn);
if( sqlite3_memory_used() ){
printf("Memory leak of %lld bytes\n", sqlite3_memory_used());
exit(1);
}
return 0;
}
+8
View File
@@ -275,6 +275,14 @@ test_suite "fts5" -prefix "" -description {
All FTS5 tests.
} -files [glob -nocomplain $::testdir/../ext/fts5/test/*.test]
test_suite "server" -prefix "" -description {
All server-edition tests.
} -files [
test_set \
select1.test server2.test server3.test serverfreelist.test \
serverreadonly.test servercrash.test serverlimit.test
]
test_suite "fts5-light" -prefix "" -description {
All FTS5 tests.
} -files [
+175
View File
@@ -0,0 +1,175 @@
# 2017 April 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 implements regression tests for SQLite library. The
# focus of this script is testing the server mode of SQLite.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix server2
source $testdir/server_common.tcl
return_if_no_server
db close
foreach {tn vfs} {1 unix-excl 2 unix} {
server_set_vfs $vfs
foreach f [glob -nocomplain test.db*] {
forcedelete $f
}
#-------------------------------------------------------------------------
# Check that the *-journal* files are deleted correctly.
#
server_reset_db
do_execsql_test 1.0 {
CREATE TABLE t1(a, b);
} {}
do_test $tn.1.1 {
lsort [glob -nocomplain test.db-journal/*-journal]
} {test.db-journal/0-journal}
do_test $tn.1.2 {
db close
lsort [glob -nocomplain test.db-journal/*-journal]
} {}
server_sqlite3 db test.db
do_execsql_test $tn.1.3 {
CREATE TABLE t2(a, b);
} {}
server_sqlite3 db2 test.db
do_test $tn.1.4 {
db eval {
BEGIN;
INSERT INTO t1 VALUES(1, 2);
}
db2 eval {
BEGIN;
INSERT INTO t2 VALUES(3, 4);
}
} {}
do_test $tn.1.5 {
db2 eval COMMIT
db eval COMMIT
lsort [glob -nocomplain test.db-journal/*-journal]
} {test.db-journal/0-journal test.db-journal/1-journal}
do_test $tn.1.6 {
db close
lsort [glob -nocomplain test.db-journal/*-journal]
} {test.db-journal/0-journal test.db-journal/1-journal}
do_test $tn.1.7 {
db2 close
lsort [glob -nocomplain test.db-journal/*-journal]
} {}
#-------------------------------------------------------------------------
#
server_reset_db
server_sqlite3 db2 test.db
do_execsql_test $tn.2.0 {
CREATE TABLE t1(a, b);
CREATE TABLE t2(c, d);
}
# Two concurrent transactions committed.
#
do_test $tn.2.1 {
db eval {
BEGIN;
INSERT INTO t1 VALUES(1, 2);
}
db2 eval {
BEGIN;
INSERT INTO t2 VALUES(3, 4);
}
} {}
do_test $tn.2.2 {
lsort [glob -nocomplain test.db-journal/*-journal]
} {test.db-journal/0-journal test.db-journal/1-journal}
do_test $tn.2.3.1 { db eval COMMIT } {}
do_test $tn.2.3.2 { db2 eval COMMIT } {}
do_execsql_test 2.4 {SELECT * FROM t1, t2} {1 2 3 4}
do_test $tn.2.5 {
lsort [glob -nocomplain test.db-journal/*-journal]
} {test.db-journal/0-journal test.db-journal/1-journal}
do_test $tn.2.6 {
execsql {BEGIN}
execsql {INSERT INTO t1 VALUES(5, 6)}
execsql {BEGIN} db2
catchsql {INSERT INTO t1 VALUES(7, 8)} db2
} {1 {database is locked}}
do_test $tn.2.7 {
# Transaction is automatically rolled back in this case.
sqlite3_get_autocommit db2
} {1}
do_test $tn.2.8 {
execsql COMMIT
execsql { SELECT * FROM t1 } db2
} {1 2 5 6}
db2 close
#-------------------------------------------------------------------------
#
server_reset_db
do_execsql_test $tn.3.0 {
CREATE TABLE t1(a, b);
}
do_test $tn.3.1 {
lsort [glob -nocomplain test.db-journal/*-journal]
} {test.db-journal/0-journal}
do_test $tn.3.2 {
db close
lsort [glob -nocomplain test.db-journal/*-journal]
} {}
#-----------------------------------------------------------------------
# Test that write-locks are downgraded when a transaction is ended,
# even if the connection holds an open read statement.
#
do_test $tn.4.1 {
server_sqlite3 db test.db
server_sqlite3 db2 test.db
db eval {
CREATE TABLE t2(a);
INSERT INTO t2 VALUES('one');
INSERT INTO t2 VALUES('two');
INSERT INTO t2 VALUES('three');
CREATE TABLE t3(k INTEGER PRIMARY KEY, val);
}
set res [list]
db eval { SELECT a FROM t2 ORDER BY rowid } {
db eval { REPLACE INTO t3 VALUES(1, $a) }
lappend res [db2 one { SELECT val FROM t3 }]
}
set res
} {one two three}
catch { db close }
catch { db2 close }
}
finish_test
+57
View File
@@ -0,0 +1,57 @@
# 2017 April 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 implements regression tests for SQLite library. The
# focus of this script is testing the server mode of SQLite.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
set testprefix server3
source $testdir/server_common.tcl
return_if_no_server
foreach {tn vfs} {1 unix-excl 2 unix} {
server_set_vfs $vfs
server_reset_db
server_sqlite3 db2 test.db
do_test 1.1 {
db eval { CREATE TABLE t1(a, b) }
db2 eval { CREATE TABLE t2(a, b) }
} {}
do_test 1.2 {
db eval {
INSERT INTO t2 VALUES(1, 2);
BEGIN;
INSERT INTO t1 VALUES(1, 2);
}
} {}
do_test 1.3 {
list [catch { db2 eval { SELECT * FROM t1 } } msg] $msg
} {1 {database is locked}}
do_test 1.4 {
list [catch { db2 eval { SELECT * FROM t1 } } msg] $msg
} {1 {database is locked}}
do_test 1.4 {
db2 eval { SELECT * FROM t2 }
} {1 2}
}
finish_test
+46
View File
@@ -0,0 +1,46 @@
# 2017 July 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.
#
#***********************************************************************
#
#
ifcapable !server {
proc return_if_no_server {} {
finish_test
return -code return
}
return
} else {
proc return_if_no_server {} {}
}
proc server_sqlite3 {cmd file} {
sqlite3 $cmd $file -vfs $::server_vfs
}
proc server_reset_db {} {
catch {db close}
forcedelete test.db test.db-journal test.db-wal
file mkdir test.db-journal
server_sqlite3 db test.db
}
set ::server_vfs unix-excl
proc server_set_vfs {vfs} {
if {$vfs=="single"} {
set ::server_vfs unix-excl
} elseif {$vfs=="multi"} {
set ::server_vfs unix
} else {
set ::server_vfs $vfs
}
}
+87
View File
@@ -0,0 +1,87 @@
# 2017 April 27
#
# 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 servercrash
ifcapable !crashtest {
finish_test
return
}
do_not_use_codec
source $testdir/server_common.tcl
return_if_no_server
db close
server_set_vfs unix
server_reset_db
do_execsql_test 1.0 {
PRAGMA page_size = 4096;
PRAGMA auto_vacuum = OFF;
CREATE TABLE t1(a, b);
CREATE TABLE t2(c, d);
INSERT INTO t1 VALUES(1, 2), (3, 4);
INSERT INTO t2 VALUES(1, 2), (3, 4);
}
for {set i 0} {$i < 10} {incr i} {
do_test 1.$i.1 {
crashsql -delay 1 -file test.db { INSERT INTO t1 VALUES(5, 6) }
} {1 {child process exited abnormally}}
do_execsql_test 1.$i.2 {
SELECT * FROM t1
} {1 2 3 4}
}
for {set i 0} {$i < 10} {incr i} {
do_test 2.$i.1 {
crashsql -delay 1 -file test.db { INSERT INTO t1 VALUES(5, 6) }
} {1 {child process exited abnormally}}
do_test 2.$i.2 {
sqlite3 dbX test.db
execsql { SELECT * FROM t1 } dbX
} {1 2 3 4}
dbX close
}
db close
for {set i 0} {$i < 10} {incr i} {
do_test 3.$i.1 {
crashsql -delay 1 -file test.db { INSERT INTO t1 VALUES(5, 6) }
} {1 {child process exited abnormally}}
sqlite3 db test.db
do_execsql_test 3.$i.2 { SELECT * FROM t1 } {1 2 3 4}
db close
}
sqlite3 db test.db
db eval {SELECT * FROM t1}
for {set i 0} {$i < 10} {incr i} {
do_test 4.$i.1 {
crashsql -delay 1 -file test.db { INSERT INTO t1 VALUES(5, 6) }
} {1 {child process exited abnormally}}
db close
sqlite3 db test.db
do_execsql_test 4.$i.2 { SELECT * FROM t1 } {1 2 3 4}
}
finish_test
+56
View File
@@ -0,0 +1,56 @@
# 2017 July 09
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.
#
# The focus of this script is testing the server mode of SQLite.
# Specifically, that "PRAGMA freelist_format" works.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix server5
do_execsql_test 1.0 {
PRAGMA freelist_format;
} {1}
do_execsql_test 1.1 {
PRAGMA freelist_format = 2;
} {2}
do_execsql_test 1.2 {
PRAGMA freelist_format;
} {2}
do_execsql_test 1.3 {
PRAGMA freelist_format = 1;
} {1}
do_execsql_test 1.4 {
PRAGMA freelist_format;
} {1}
do_execsql_test 1.5 {
CREATE TABLE t1(x);
PRAGMA freelist_format = 2;
} {2}
do_execsql_test 1.6 {
CREATE TABLE t2(y);
}
do_execsql_test 1.6 {
PRAGMA freelist_format = 1;
} {2}
finish_test
+177
View File
@@ -0,0 +1,177 @@
# 2017 April 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 implements regression tests for SQLite library. The
# focus of this script is testing the server mode of SQLite.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix serverlimit
source $testdir/server_common.tcl
source $testdir/lock_common.tcl
return_if_no_server
#-------------------------------------------------------------------------
# Test plan:
#
# 1.* The concurrent connections limit in multi-process mode. With all
# connections in the local process.
#
# 2.* The concurrent connections limit in multi-process mode. Using
# multiple processes.
#
# 3.* The concurrent transactions limit in single-process mode.
#
server_set_vfs multi
server_reset_db
set MLIMIT 16 ;# maximum number of allowed connections
do_test 1.0 {
server_sqlite3 db test.db
db eval {
CREATE TABLE t1(x);
INSERT INTO t1 VALUES('hello'), ('world');
}
db close
for {set i 0} {$i < $MLIMIT} {incr i} {
server_sqlite3 db.$i test.db
db.$i eval { SELECT * FROM t1 }
}
set {} {}
} {}
# Connection [db] cannot connect - all client slots are occupied.
#
do_test 1.1 {
server_sqlite3 db test.db
list [catch { db eval { SELECT * FROM t1 } } msg] $msg
} {1 {database is locked}}
# But, if one connection disconnects, [db] can then connect and
# query the db.
#
do_test 1.2 {
db.0 close
list [catch { db eval { SELECT * FROM t1 } } msg] $msg
} {0 {hello world}}
do_test 1.3 {
for {set i 0} {$i < $MLIMIT} {incr i} {
catch { db.$i close }
}
set {} {}
} {}
#-------------------------------------------------------------------------
# Connections in different processes.
do_multiclient_test tn {
code1 { db close }
code2 { db2 close }
code3 { db3 close }
set N1 [expr $MLIMIT / 2]
set N2 [expr $MLIMIT - $N1]
do_test 2.$tn.0 {
file mkdir test.db-journal
code1 {
sqlite3 db test.db
db eval {
CREATE TABLE t11(a, b);
INSERT INTO t11 VALUES(1, 2), (3, 4);
}
db close
for {set i 0} {$i < $N1} {incr i} {
sqlite3 db.$i test.db
db.$i eval { SELECT * FROM t11 }
}
}
code2 [string map [list %N2% $N2] {
for {set i 0} {$i < %N2%} {incr i} {
sqlite3 db2.$i test.db
db2.$i eval { SELECT * FROM t11 }
}
}]
code2 { db2.0 eval {SELECT * FROM t11} }
} {1 2 3 4}
do_test 2.$tn.1 {
code3 { sqlite3 db3 test.db }
csql3 { SELECT * FROM t11 }
} {1 {database is locked}}
do_test 2.$tn.2 {
code2 { db2.0 close }
csql3 { SELECT * FROM t11 }
} {0 {1 2 3 4}}
do_test 2.$tn.3 {
code1 { sqlite3 db test.db }
csql1 { SELECT * FROM t11 }
} {1 {database is locked}}
do_test 2.$tn.4 {
code2 { db2.1 close }
csql1 { SELECT * FROM t11 }
} {0 {1 2 3 4}}
do_test 2.$tn.X {
code1 { for {set i 0} {$i < 50} {incr i} { catch {db.$i close} } }
code2 { for {set i 0} {$i < 50} {incr i} { catch {db2.$i close} } }
} {}
}
server_set_vfs single
server_reset_db
set TLIMIT 16
do_test 3.0 {
execsql "CREATE TABLE t1 (o PRIMARY KEY) WITHOUT ROWID"
for {set i 0} {$i < $TLIMIT} {incr i} {
execsql "CREATE TABLE x$i (o PRIMARY KEY) WITHOUT ROWID"
}
set "" ""
} {}
do_test 3.1 {
for {set i 0} {$i < $TLIMIT} {incr i} {
sqlite3 db.$i test.db
db.$i eval "
BEGIN;
INSERT INTO x$i VALUES ('one');
"
}
} {}
do_catchsql_test 3.2 {
INSERT INTO t1 VALUES('two');
} {1 {database is locked}}
do_test 3.3 {
db.0 eval COMMIT
execsql { INSERT INTO t1 VALUES('two'); }
} {}
do_catchsql_test 3.4 { SELECT * FROM x1 } {1 {database is locked}}
do_catchsql_test 3.5 { SELECT * FROM x0 } {0 one}
do_test 3.6 {
for {set i 1} {$i < $TLIMIT} {incr i} {
db.$i eval COMMIT
}
} {}
finish_test
+65
View File
@@ -0,0 +1,65 @@
# 2017 July 09
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.
#
# The focus of this script is testing the server mode of SQLite.
# Specifically, that "BEGIN READONLY" starts a read-only MVCC
# transaction.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
set testprefix server4
source $testdir/server_common.tcl
return_if_no_server
server_reset_db
server_sqlite3 db2 test.db
do_execsql_test 1.0 {
CREATE TABLE t1(x);
INSERT INTO t1 VALUES(1);
CREATE TABLE t2(x);
INSERT INTO t2 VALUES(1);
BEGIN;
INSERT INTO t1 VALUES(2);
INSERT INTO t2 VALUES(2);
}
do_execsql_test -db db2 1.1 {
BEGIN READONLY;
SELECT * FROM t1;
} {1}
do_execsql_test 1.2 {
COMMIT;
INSERT INTO t1 VALUES(3);
SELECT * FROM t1;
} {1 2 3}
do_execsql_test 1.2a {
INSERT INTO t2 VALUES(3);
} {}
do_execsql_test -db db2 1.3 {
SELECT * FROM t2;
} {1}
do_execsql_test -db db2 1.4 {
ROLLBACK;
SELECT * FROM t1;
} {1 2 3}
finish_test
+42
View File
@@ -1097,4 +1097,46 @@ do_test shell1-6.1 {
} {}
}
db close
forcedelete test.db test.db-journal test.db-wal
sqlite3 db test.db
# The shell tool ".schema" command uses virtual table "pragma_database_list"
#
ifcapable vtab {
do_test shell1-7.1.1 {
db eval {
CREATE TABLE Z (x TEXT PRIMARY KEY);
CREATE TABLE _ (x TEXT PRIMARY KEY);
CREATE TABLE YY (x TEXT PRIMARY KEY);
CREATE TABLE __ (x TEXT PRIMARY KEY);
CREATE TABLE WWW (x TEXT PRIMARY KEY);
CREATE TABLE ___ (x TEXT PRIMARY KEY);
}
} {}
do_test shell1-7.1.2 {
catchcmd "test.db" ".schema _"
} {0 {CREATE TABLE Z (x TEXT PRIMARY KEY);
CREATE TABLE _ (x TEXT PRIMARY KEY);}}
do_test shell1-7.1.3 {
catchcmd "test.db" ".schema \\\\_"
} {0 {CREATE TABLE _ (x TEXT PRIMARY KEY);}}
do_test shell1-7.1.4 {
catchcmd "test.db" ".schema __"
} {0 {CREATE TABLE YY (x TEXT PRIMARY KEY);
CREATE TABLE __ (x TEXT PRIMARY KEY);}}
do_test shell1-7.1.5 {
catchcmd "test.db" ".schema \\\\_\\\\_"
} {0 {CREATE TABLE __ (x TEXT PRIMARY KEY);}}
do_test shell1-7.1.6 {
catchcmd "test.db" ".schema ___"
} {0 {CREATE TABLE WWW (x TEXT PRIMARY KEY);
CREATE TABLE ___ (x TEXT PRIMARY KEY);}}
do_test shell1-7.1.7 {
catchcmd "test.db" ".schema \\\\_\\\\_\\\\_"
} {0 {CREATE TABLE ___ (x TEXT PRIMARY KEY);}}
}
finish_test
+4
View File
@@ -586,6 +586,10 @@ proc reset_db {} {
forcedelete test.db
forcedelete test.db-journal
forcedelete test.db-wal
for {set i 0} {$i < 16} {incr i} {
forcedelete test.db-journal$i
}
sqlite3 db ./test.db
set ::DB [sqlite3_connection_pointer db]
if {[info exists ::SETUP_SQL]} {
+1
View File
@@ -141,5 +141,6 @@ foreach {tn same_db shared_cache} [list \
}
sqlite3_enable_shared_cache $::enable_shared_cache
catch { db close }
set sqlite_open_file_count 0
finish_test
+2 -2
View File
@@ -129,7 +129,7 @@ do_test trace3-4.3 {
}
set stmt [lindex [lindex $::stmtlist(record) 0] 0]
set ns [lindex [lindex $::stmtlist(record) 0] 1]
list $stmt [expr {$ns >= 0 && $ns <= 1000000}]; # less than 0.001 second
list $stmt [expr {$ns >= 0 && $ns <= 9999999}]; # less than 0.010 seconds
} {/^-?\d+ 1$/}
do_test trace3-4.4 {
set ::stmtlist(record) {}
@@ -139,7 +139,7 @@ do_test trace3-4.4 {
}
set stmt [lindex [lindex $::stmtlist(record) 0] 0]
set ns [lindex [lindex $::stmtlist(record) 0] 1]
list $stmt [expr {$ns >= 0 && $ns <= 1000000}]; # less than 0.001 second
list $stmt [expr {$ns >= 0 && $ns <= 9999999}]; # less than 0.010 seconds
} {/^-?\d+ 1$/}
do_test trace3-5.1 {
+136 -46
View File
@@ -10,6 +10,8 @@
#***********************************************************************
#
package require Tcl 8.6
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix zipfile
@@ -21,6 +23,10 @@ if {[catch {load_static_extension db zipfile} error]} {
puts "Skipping zipfile tests, hit load error: $error"
finish_test; return
}
if {[catch {load_static_extension db fileio} error]} {
puts "Skipping zipfile tests, hit load error: $error"
finish_test; return
}
proc readfile {f} {
set fd [open $f]
@@ -30,16 +36,39 @@ proc readfile {f} {
set data
}
if {$::tcl_platform(platform)=="unix" && [catch {exec unzip}]==0} {
set ::UNZIP 1
load_static_extension db fileio
unset -nocomplain ::UNZIP
if {[catch {exec unzip} msg]==0 && \
[regexp -line {^UnZip \d+\.\d+ .*? Info-ZIP\.} $msg]} {
set ::UNZIP unzip
proc fix_stat_mode {name mode} {
if {$::tcl_platform(platform)=="windows"} {
#
# NOTE: Set or unset the write bits of the file permissions
# based on the read-only attribute because the Win32
# version of UnZip does this.
#
set writebits 0x12; # 0o22
set result $mode
if {[file attributes $name -readonly]} {
set result [expr {$result | $writebits}]
} else {
set result [expr {$result & ~$writebits}]
}
return $result
} else {
return $mode
}
}
proc do_unzip {file} {
forcedelete test_unzip
file mkdir test_unzip
exec unzip -d test_unzip $file
set res [db eval {
SELECT replace(name,'test_unzip/',''),mode,mtime,data
exec $::UNZIP -d test_unzip $file
db func modefix fix_stat_mode
set res [db eval {
SELECT replace(name,'test_unzip/',''),modefix(name,mode),mtime,data
FROM fsdir('test_unzip')
WHERE name!='test_unzip'
ORDER BY name
@@ -108,7 +137,6 @@ proc do_zipfile_blob_test {tn file} {
# the same results as in (1).
#
proc do_unzip_test {tn file} {
if {[info vars ::UNZIP]==""} { return }
db func sss strip_slash
db eval {
@@ -247,65 +275,73 @@ do_execsql_test 1.6.2 {
i.txt 33188 4 zxcvb 0
}
do_execsql_test 1.6.3 {
UPDATE zz SET mode='-rw-r--r-x' WHERE name='h.txt';
SELECT name, mode, mtime, data, method FROM zipfile('test.zip');
} {
f.txt 33188 1000000000 abcde 0
h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8
i.txt 33188 4 zxcvb 0
if {$::tcl_platform(platform)=="unix"} {
set modes -rw-r--r-x
set perms 33189
} else {
set modes -rw-r--r--; # no execute bits on Win32
set perms 33188
}
do_execsql_test 1.6.3 {
UPDATE zz SET mode=$modes WHERE name='h.txt';
SELECT name, mode, mtime, data, method FROM zipfile('test.zip');
} [string map [list %perms% $perms] {
f.txt 33188 1000000000 abcde 0
h.txt %perms% 1000000004 aaaaaaaaaabbbbbbbbbb 8
i.txt 33188 4 zxcvb 0
}]
do_zip_tests 1.6.3a test.zip
do_execsql_test 1.6.4 {
UPDATE zz SET name = 'blue.txt' WHERE name='f.txt';
SELECT name, mode, mtime, data, method FROM zipfile('test.zip');
} {
} [string map [list %perms% $perms] {
blue.txt 33188 1000000000 abcde 0
h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8
h.txt %perms% 1000000004 aaaaaaaaaabbbbbbbbbb 8
i.txt 33188 4 zxcvb 0
}
}]
do_zip_tests 1.6.4a test.zip
do_execsql_test 1.6.5 {
UPDATE zz SET data = 'edcba' WHERE name='blue.txt';
SELECT name, mode, mtime, data, method FROM zipfile('test.zip');
} {
} [string map [list %perms% $perms] {
blue.txt 33188 1000000000 edcba 0
h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8
h.txt %perms% 1000000004 aaaaaaaaaabbbbbbbbbb 8
i.txt 33188 4 zxcvb 0
}
}]
do_execsql_test 1.6.6 {
UPDATE zz SET mode=NULL, data = NULL WHERE name='blue.txt';
SELECT name, mode, mtime, data, method FROM zipfile('test.zip');
} {
} [string map [list %perms% $perms] {
blue.txt/ 16877 1000000000 {} 0
h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8
h.txt %perms% 1000000004 aaaaaaaaaabbbbbbbbbb 8
i.txt 33188 4 zxcvb 0
}
}]
do_catchsql_test 1.6.7 {
UPDATE zz SET data=NULL WHERE name='i.txt'
} {1 {zipfile: mode does not match data}}
do_execsql_test 1.6.8 {
SELECT name, mode, mtime, data, method FROM zipfile('test.zip');
} {
} [string map [list %perms% $perms] {
blue.txt/ 16877 1000000000 {} 0
h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8
h.txt %perms% 1000000004 aaaaaaaaaabbbbbbbbbb 8
i.txt 33188 4 zxcvb 0
}
do_execsql_test 1.6.8 {
UPDATE zz SET data = '' WHERE name='i.txt';
SELECT name,mode,mtime,data,method from zipfile('test.zip');
} {
blue.txt/ 16877 1000000000 {} 0
h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8
i.txt 33188 4 {} 0
}
}]
do_execsql_test 1.6.9 {
UPDATE zz SET data = '' WHERE name='i.txt';
SELECT name,mode,mtime,data,method from zipfile('test.zip');
} [string map [list %perms% $perms] {
blue.txt/ 16877 1000000000 {} 0
h.txt %perms% 1000000004 aaaaaaaaaabbbbbbbbbb 8
i.txt 33188 4 {} 0
}]
do_execsql_test 1.6.10 {
SELECT a.name, a.data
FROM zz AS a, zz AS b
WHERE a.name=+b.name AND +a.mode=b.mode
@@ -315,7 +351,7 @@ do_execsql_test 1.6.9 {
i.txt {}
}
do_execsql_test 1.6.10 {
do_execsql_test 1.6.11 {
SELECT name, data FROM zz WHERE name LIKE '%txt'
} {
h.txt aaaaaaaaaabbbbbbbbbb
@@ -360,13 +396,18 @@ do_execsql_test 2.4 {
}
do_zip_tests 2.4a test.zip
# If on unix, check that the [unzip] utility can unpack our archive.
# Check that the [unzip] utility can unpack our archive.
#
if {$::tcl_platform(platform)=="unix"} {
if {[info exists ::UNZIP]} {
do_test 2.5.1 {
forcedelete dirname
forcedelete dirname2
set rc [catch { exec unzip test.zip > /dev/null } msg]
if {$::tcl_platform(platform)=="unix"} {
set null /dev/null
} else {
set null NUL
}
set rc [catch { exec $::UNZIP test.zip > $null } msg]
list $rc $msg
} {0 {}}
do_test 2.5.2 { file isdir dirname3 } 1
@@ -384,6 +425,7 @@ if {$::tcl_platform(platform)=="unix"} {
reset_db
forcedelete test.zip
load_static_extension db zipfile
load_static_extension db fileio
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE temp.x1 USING zipfile('test.zip');
@@ -453,7 +495,7 @@ do_catchsql_test 4.7 {
SELECT zipfile(name,data) FROM c
} {1 {first argument to zipfile() must be non-NULL}}
do_catchsql_test 4.7 {
do_catchsql_test 4.8 {
WITH c(name,data,method) AS (
SELECT 'a.txt', 'abc', 0
UNION SELECT 'b.txt', 'def', 8
@@ -462,7 +504,7 @@ do_catchsql_test 4.7 {
SELECT zipfile(name,NULL,NULL,data,method) FROM c
} {1 {illegal method value: 16}}
do_catchsql_test 4.8 {
do_catchsql_test 4.9 {
WITH c(name,data) AS (
SELECT 'a.txt', 'abc'
UNION SELECT 'b.txt', 'def'
@@ -485,9 +527,8 @@ do_execsql_test 5.0 {
a.txt 946684800 abc
}
if {[info vars ::UNZIP]!=""} {
if {[info exists ::UNZIP]} {
ifcapable datetime {
load_static_extension db fileio
forcedelete test1.zip test2.zip
do_test 6.0 {
execsql {
@@ -502,7 +543,7 @@ ifcapable datetime {
}
forcedelete test_unzip
file mkdir test_unzip
exec unzip -d test_unzip test1.zip
exec $::UNZIP -d test_unzip test1.zip
db eval {
SELECT name, strftime('%s', mtime, 'unixepoch', 'localtime')
@@ -515,6 +556,14 @@ ifcapable datetime {
test_unzip/c.txt 1111111000
}]
# fsdir() issue reported on the mailing list on 2018-03-14 by Jack Thaw.
do_test 6.0b {
db eval {
SELECT sum(name LIKE '%/a.txt')
FROM (VALUES(1),(2),(3)) CROSS JOIN fsdir('test_unzip')
}
} {3}
do_execsql_test 6.1 {
SELECT name, mtime, data FROM zipfile('test1.zip')
} {
@@ -526,7 +575,7 @@ ifcapable datetime {
do_test 6.2 {
forcedelete test_unzip
file mkdir test_unzip
exec unzip -d test_unzip test2.zip
exec $::UNZIP -d test_unzip test2.zip
db eval {
SELECT name, mtime
@@ -644,6 +693,8 @@ catch {db close}
forcedelete test.zip test.db
sqlite3 db :memory:
load_static_extension db zipfile
load_static_extension db fileio
do_execsql_test 10.0 {
CREATE VIRTUAL TABLE z USING zipfile('test.zip');
} {}
@@ -666,5 +717,44 @@ do_execsql_test 10.6 {
SELECT name, data FROM z;
} {a0 four}
do_execsql_test 11.1 {
DELETE FROM z;
} {}
do_execsql_test 11.2 {
SELECT name, data FROM z;
} {}
do_execsql_test 11.3 {
INSERT INTO z (name,data) VALUES ('b0','one');
SELECT name, data FROM z;
} {b0 one}
do_execsql_test 11.4 {
UPDATE z SET name = 'b1' WHERE name = 'b0';
SELECT name, data FROM z;
} {b1 one}
do_execsql_test 11.5 {
INSERT INTO z (name,data) VALUES ('b0','one');
SELECT name, data FROM z ORDER BY name;
} {b0 one b1 one}
do_catchsql_test 11.6 {
UPDATE z SET name = 'b1' WHERE name = 'b0';
} {1 {duplicate name: "b1"}}
do_execsql_test 11.7 {
UPDATE z SET data = 'two' WHERE name = 'b0';
SELECT name, data FROM z ORDER BY name;
} {b0 two b1 one}
do_catchsql_test 11.8 {
UPDATE z SET name = 'b1';
} {1 {duplicate name: "b1"}}
do_catchsql_test 11.9 {
UPDATE z SET name = 'b2';
} {1 {duplicate name: "b2"}}
do_execsql_test 11.10 {
UPDATE z SET name = name;
SELECT name, data FROM z ORDER BY name;
} {b0 two b2 one}
do_execsql_test 11.11 {
UPDATE z SET name = name || 'suffix';
SELECT name, data FROM z ORDER BY name;
} {b0suffix two b2suffix one}
finish_test
+39 -1
View File
@@ -10,6 +10,8 @@
#***********************************************************************
#
package require Tcl 8.6
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix zipfile2
@@ -50,12 +52,17 @@ do_execsql_test 1.0 {
CREATE VIRTUAL TABLE fff USING zipfile('test''zip');
}
if {$::tcl_platform(platform)=="windows"} {
set res {1 {cannot open file: testdir}}
} else {
set res {1 {error in fread()}}
}
do_test 2.0 {
forcedelete testdir
file mkdir testdir
execsql { CREATE VIRTUAL TABLE hhh USING zipfile('testdir') }
catchsql { SELECT * FROM hhh }
} {1 {error in fread()}}
} $res
set archive {
@@ -203,6 +210,37 @@ do_test 5.0 {
execsql { SELECT name, data IS NULL FROM zipfile($blob2) }
} {notadi/ 1}
#-------------------------------------------------------------------------
# Test that duplicate entries may not be created using UPDATE
# statements.
#
forcedelete test.zip
do_execsql_test 6.0 {
CREATE VIRTUAL TABLE temp.zip USING zipfile('test.zip');
INSERT INTO temp.zip (name,data) VALUES ('test1','test');
INSERT INTO temp.zip (name,data) VALUES ('test2','test');
}
do_catchsql_test 6.1 {
UPDATE temp.zip SET name='test1' WHERE name='test2'
} {1 {duplicate name: "test1"}}
forcedelete test.zip
do_catchsql_test 6.2 {
DROP TABLE zip;
CREATE VIRTUAL TABLE temp.zip USING zipfile('test.zip');
INSERT INTO temp.zip (name,data) VALUES ('test','test');
UPDATE temp.zip set name=name||'new' where name='test';
INSERT INTO temp.zip (name,data) VALUES ('test','test');
UPDATE temp.zip set name=name||'new' where name='test';
} {1 {duplicate name: "testnew"}}
forcedelete test.zip
do_execsql_test 6.3 {
INSERT INTO temp.zip (name,data) VALUES ('test1','test');
INSERT INTO temp.zip (name,data) VALUES ('test2','test');
UPDATE OR REPLACE zip SET name='test2' WHERE name='test1';
SELECT name FROM zip;
} {test2}
finish_test
+1
View File
@@ -239,6 +239,7 @@ static Keyword aKeywordTable[] = {
{ "PRIMARY", "TK_PRIMARY", ALWAYS },
{ "QUERY", "TK_QUERY", EXPLAIN },
{ "RAISE", "TK_RAISE", TRIGGER },
{ "READONLY", "TK_READONLY", ALWAYS },
{ "RECURSIVE", "TK_RECURSIVE", CTE },
{ "REFERENCES", "TK_REFERENCES", FKEY },
{ "REGEXP", "TK_LIKE_KW", ALWAYS },
+4
View File
@@ -382,6 +382,10 @@ set pragma_def {
NAME: optimize
FLAG: Result1 NeedSchema
NAME: freelist_format
FLAG: NeedSchema Result0 SchemaReq
IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) && defined(SQLITE_SERVER_EDITION)
}
# Open the output file
+2
View File
@@ -114,6 +114,7 @@ foreach hdr {
pcache.h
pragma.h
rtree.h
server.h
sqlite3session.h
sqlite3.h
sqlite3ext.h
@@ -328,6 +329,7 @@ foreach file {
rowset.c
pager.c
wal.c
server.c
btmutex.c
btree.c
+5 -1
View File
@@ -72,6 +72,9 @@ set declpattern3 \
set declpattern4 \
{^ *([a-zA-Z][a-zA-Z_0-9 ]+ \**)(sqlite3changegroup_[_a-zA-Z0-9]+)(\(.*)$}
set declpattern5 \
{^ *([a-zA-Z][a-zA-Z_0-9 ]+ \**)(sqlite3rebaser_[_a-zA-Z0-9]+)(\(.*)$}
# Force the output to use unix line endings, even on Windows.
fconfigure stdout -translation lf
@@ -121,7 +124,8 @@ foreach file $filelist {
if {[regexp $declpattern1 $line all rettype funcname rest] || \
[regexp $declpattern2 $line all rettype funcname rest] || \
[regexp $declpattern3 $line all rettype funcname rest] || \
[regexp $declpattern4 $line all rettype funcname rest]} {
[regexp $declpattern4 $line all rettype funcname rest] || \
[regexp $declpattern5 $line all rettype funcname rest]} {
set line SQLITE_API
append line " " [string trim $rettype]
if {[string index $rettype end] ne "*"} {
+303
View File
@@ -0,0 +1,303 @@
#!/usr/bin/tclsh
#
# This script is used to run the performance test cases described in
# README-server-edition.html.
#
package require sqlite3
# Default values for command line switches:
set O(-database) ""
set O(-mode) "server"
set O(-rows) [expr 5000000]
set O(-tserver) "./tserver"
set O(-seconds) 20
set O(-writers) 2
set O(-readers) 1
set O(-verbose) 0
proc error_out {err} {
puts stderr $err
exit -1
}
proc usage {} {
puts stderr "Usage: $::argv0 ?OPTIONS?"
puts stderr ""
puts stderr "Where OPTIONS are:"
puts stderr " -database <database file> (default: test.$mode.db)"
puts stderr " -mode server|begin-concurrent (default: server)"
puts stderr " -rows <number of rows> (default: 5000000)"
puts stderr " -tserver <path to tserver executable> (default: ./tserver)"
puts stderr " -seconds <time to run for in seconds> (default: 20)"
puts stderr " -writers <number of writer clients> (default: 2)"
puts stderr " -readers <number of reader clients> (default: 1)"
puts stderr " -verbose 0|1 (default: 0)"
exit -1
}
for {set i 0} {$i < [llength $argv]} {incr i} {
set opt ""
set arg [lindex $argv $i]
set n [expr [string length $arg]-1]
foreach k [array names ::O] {
if {[string range $k 0 $n]==$arg} {
if {$opt==""} {
set opt $k
} else {
error_out "ambiguous option: $arg ($k or $opt)"
}
}
}
if {$opt==""} { usage }
if {$i==[llength $argv]-1} {
error_out "option requires an argument: $opt"
}
incr i
set val [lindex $argv $i]
switch -- $opt {
-mode {
if {$val != "server" && $val != "begin-concurrent"
&& $val != "wal" && $val != "persist"
} {
set xyz "\"server\", \"begin-concurrent\", \"wal\" or \"persist\""
error_out "Found \"$val\" - expected $xyz"
}
}
}
set O($opt) [lindex $argv $i]
}
if {$O(-database)==""} {
set O(-database) "test.$O(-mode).db"
}
set O(-rows) [expr $O(-rows)]
#--------------------------------------------------------------------------
# Create and populate the required test database, if it is not already
# present in the file-system.
#
proc create_test_database {} {
global O
if {[file exists $O(-database)]} {
sqlite3 db $O(-database)
# Check the schema looks Ok.
set s [db one {
SELECT group_concat(name||pk, '.') FROM pragma_table_info('t1');
}]
if {$s != "a1.b0.c0.d0"} {
error_out "Database $O(-database) exists but is not usable (schema)"
}
# Check that the row count matches.
set n [db one { SELECT count(*) FROM t1 }]
if {$n != $O(-rows)} {
error_out "Database $O(-database) exists but is not usable (row-count)"
}
db close
} else {
catch { file delete -force $O(-database)-journal }
catch { file delete -force $O(-database)-wal }
if {$O(-verbose)} {
puts "Building database $O(-database)..."
}
sqlite3 db $O(-database)
db eval {
CREATE TABLE t1(
a INTEGER PRIMARY KEY,
b BLOB(16),
c BLOB(16),
d BLOB(400)
);
CREATE INDEX i1 ON t1(b);
CREATE INDEX i2 ON t1(c);
WITH s(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<$O(-rows))
INSERT INTO t1
SELECT i-1, randomblob(16), randomblob(16), randomblob(400) FROM s;
}
if {$O(-mode)=="server"} {
db eval "PRAGMA freelist_format = 2"
}
db close
switch -- $O(-mode) {
server {
if {![file exists $O(-database)-journal]} {
file mkdir $O(-database)-journal
}
}
wal {
sqlite3 db $O(-database)
db eval {PRAGMA journal_mode = wal}
db close
}
begin-concurrent {
sqlite3 db $O(-database)
db eval {PRAGMA journal_mode = wal}
db close
}
}
}
}
#-------------------------------------------------------------------------
# Functions to start and stop the tserver process:
#
# tserver_start
# tserver_stop
#
set ::tserver {}
proc tserver_start {} {
global O
set ::tserver [open "|$O(-tserver) -vfs unix-excl $O(-database)"]
fconfigure $::tserver -blocking 0
fileevent $::tserver readable tserver_data
}
proc tserver_data {} {
global O
if {[eof $::tserver]} {
error_out "tserver has exited"
}
set line [gets $::tserver]
if {$line != "" && $O(-verbose)} {
puts "tserver: $line"
}
}
proc tserver_stop {} {
close $::tserver
set fd [socket localhost 9999]
puts $fd ".stop"
close $fd
}
#-------------------------------------------------------------------------
set ::nClient 0
set ::client_output [list]
proc client_data {name fd} {
global O
if {[eof $fd]} {
incr ::nClient -1
close $fd
return
}
set str [gets $fd]
if {[string trim $str]!=""} {
if {[string range $str 0 3]=="### "} {
lappend ::client_output [concat [list name $name] [lrange $str 1 end]]
}
if {$O(-verbose)} {
puts "$name: $str"
}
}
}
proc client_launch {name script} {
global O
set fd [socket localhost 9999]
fconfigure $fd -blocking 0
switch -- $O(-mode) {
persist {
puts $fd "PRAGMA journal_mode = PERSIST;"
}
}
puts $fd "PRAGMA synchronous = OFF;"
puts $fd ".repeat 1"
puts $fd ".run"
puts $fd $script
puts $fd ".seconds $O(-seconds)"
puts $fd ".run"
puts $fd ".quit"
flush $fd
incr ::nClient
fileevent $fd readable [list client_data $name $fd]
}
proc client_wait {} {
while {$::nClient>0} {vwait ::nClient}
}
proc script_writer {} {
global O
set commit "COMMIT;"
set begin "BEGIN;"
if {$O(-mode)=="begin-concurrent" || $O(-mode)=="wal"} {
set commit ".mutex_commit"
set begin "BEGIN CONCURRENT;"
}
if {$O(-mode)=="server"} { set beginarg "READONLY" }
set tail "randomblob(16), randomblob(16), randomblob(400));"
return [subst -nocommands {
$begin
REPLACE INTO t1 VALUES(abs(random() % $O(-rows)), $tail
REPLACE INTO t1 VALUES(abs(random() % $O(-rows)), $tail
REPLACE INTO t1 VALUES(abs(random() % $O(-rows)), $tail
REPLACE INTO t1 VALUES(abs(random() % $O(-rows)), $tail
REPLACE INTO t1 VALUES(abs(random() % $O(-rows)), $tail
$commit
}]
}
proc script_reader {} {
global O
set beginarg ""
if {$O(-mode)=="server"} { set beginarg "READONLY" }
return [subst -nocommands {
BEGIN $beginarg;
SELECT * FROM t1 WHERE a>abs((random()%$O(-rows))) LIMIT 10;
SELECT * FROM t1 WHERE a>abs((random()%$O(-rows))) LIMIT 10;
SELECT * FROM t1 WHERE a>abs((random()%$O(-rows))) LIMIT 10;
SELECT * FROM t1 WHERE a>abs((random()%$O(-rows))) LIMIT 10;
SELECT * FROM t1 WHERE a>abs((random()%$O(-rows))) LIMIT 10;
END;
}]
}
create_test_database
tserver_start
for {set i 0} {$i < $O(-writers)} {incr i} {
client_launch w.$i [script_writer]
}
for {set i 0} {$i < $O(-readers)} {incr i} {
client_launch r.$i [script_reader]
}
client_wait
set name(w) "Writers"
set name(r) "Readers"
foreach r $::client_output {
array set a $r
set type [string range $a(name) 0 0]
incr x($type.ok) $a(ok);
incr x($type.busy) $a(busy);
incr x($type.n) 1
set t($type) 1
}
foreach type [array names t] {
set nTPS [expr $x($type.ok) / $O(-seconds)]
set nC [expr $nTPS / $x($type.n)]
set nTotal [expr $x($type.ok) + $x($type.busy)]
set bp [format %.2f [expr $x($type.busy) * 100.0 / $nTotal]]
puts "$name($type): $nTPS transactions/second ($nC per client) ($bp% busy)"
}
tserver_stop
+582
View File
@@ -0,0 +1,582 @@
/*
** 2017 June 7
**
** 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.
**
*************************************************************************
**
** Simple multi-threaded server used for informal testing of concurrency
** between connections in different threads. Listens for tcp/ip connections
** on port 9999 of the 127.0.0.1 interface only. To build:
**
** gcc -g $(TOP)/tool/tserver.c sqlite3.o -lpthread -o tserver
**
** To run using "x.db" as the db file:
**
** ./tserver x.db
**
** To connect, open a client socket on port 9999 and start sending commands.
** Commands are either SQL - which must be terminated by a semi-colon, or
** dot-commands, which must be terminated by a newline. If an SQL statement
** is seen, it is prepared and added to an internal list.
**
** Dot-commands are:
**
** .list Display all SQL statements in the list.
** .quit Disconnect.
** .run Run all SQL statements in the list.
** .repeats N Configure the number of repeats per ".run".
** .seconds N Configure the number of seconds to ".run" for.
** .mutex_commit Add a "COMMIT" protected by a g.commit_mutex
** to the current SQL.
** .stop Stop the tserver process - exit(0).
**
** Example input:
**
** BEGIN;
** INSERT INTO t1 VALUES(randomblob(10), randomblob(100));
** INSERT INTO t1 VALUES(randomblob(10), randomblob(100));
** INSERT INTO t1 VALUES(randomblob(10), randomblob(100));
** COMMIT;
** .repeats 100000
** .run
**
*/
#define TSERVER_PORTNUMBER 9999
#include <arpa/inet.h>
#include <assert.h>
#include <pthread.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#include "sqlite3.h"
#define TSERVER_DEFAULT_CHECKPOINT_THRESHOLD 3900
/* Global variables */
struct TserverGlobal {
char *zDatabaseName; /* Database used by this server */
char *zVfs;
sqlite3_mutex *commit_mutex;
sqlite3 *db; /* Global db handle */
/* The following use native pthreads instead of a portable interface. This
** is because a condition variable, as well as a mutex, is required. */
pthread_mutex_t ckpt_mutex;
pthread_cond_t ckpt_cond;
int nThreshold; /* Checkpoint when wal is this large */
int bCkptRequired; /* True if wal checkpoint is required */
int nRun; /* Number of clients in ".run" */
int nWait; /* Number of clients waiting on ckpt_cond */
};
static struct TserverGlobal g = {0};
typedef struct ClientSql ClientSql;
struct ClientSql {
sqlite3_stmt *pStmt;
int bMutex;
};
typedef struct ClientCtx ClientCtx;
struct ClientCtx {
sqlite3 *db; /* Database handle for this client */
int fd; /* Client fd */
int nRepeat; /* Number of times to repeat SQL */
int nSecond; /* Number of seconds to run for */
ClientSql *aPrepare; /* Array of prepared statements */
int nPrepare; /* Valid size of apPrepare[] */
int nAlloc; /* Allocated size of apPrepare[] */
};
static int is_eol(int i){
return (i=='\n' || i=='\r');
}
static int is_whitespace(int i){
return (i==' ' || i=='\t' || is_eol(i));
}
/*
** Implementation of SQL scalar function usleep().
*/
static void usleepFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
int nUs;
sqlite3_vfs *pVfs = (sqlite3_vfs*)sqlite3_user_data(context);
assert( argc==1 );
nUs = sqlite3_value_int64(argv[0]);
pVfs->xSleep(pVfs, nUs);
}
static void trim_string(const char **pzStr, int *pnStr){
const char *zStr = *pzStr;
int nStr = *pnStr;
while( nStr>0 && is_whitespace(zStr[0]) ){
zStr++;
nStr--;
}
while( nStr>0 && is_whitespace(zStr[nStr-1]) ){
nStr--;
}
*pzStr = zStr;
*pnStr = nStr;
}
static int send_message(ClientCtx *p, const char *zFmt, ...){
char *zMsg;
va_list ap; /* Vararg list */
va_start(ap, zFmt);
int res = -1;
zMsg = sqlite3_vmprintf(zFmt, ap);
if( zMsg ){
res = write(p->fd, zMsg, strlen(zMsg));
}
sqlite3_free(zMsg);
va_end(ap);
return (res<0);
}
static int handle_some_sql(ClientCtx *p, const char *zSql, int nSql){
const char *zTail = zSql;
int nTail = nSql;
int rc = SQLITE_OK;
while( rc==SQLITE_OK ){
if( p->nPrepare>=p->nAlloc ){
int nByte = (p->nPrepare+32) * sizeof(ClientSql);
ClientSql *aNew = sqlite3_realloc(p->aPrepare, nByte);
if( aNew ){
memset(&aNew[p->nPrepare], 0, sizeof(ClientSql)*32);
p->aPrepare = aNew;
p->nAlloc = p->nPrepare+32;
}else{
rc = SQLITE_NOMEM;
break;
}
}
rc = sqlite3_prepare_v2(
p->db, zTail, nTail, &p->aPrepare[p->nPrepare].pStmt, &zTail
);
if( rc!=SQLITE_OK ){
send_message(p, "error - %s\n", sqlite3_errmsg(p->db));
rc = 1;
break;
}
if( p->aPrepare[p->nPrepare].pStmt==0 ){
break;
}
p->nPrepare++;
nTail = nSql - (zTail-zSql);
rc = send_message(p, "ok (%d SQL statements)\n", p->nPrepare);
}
return rc;
}
static sqlite3_int64 get_timer(void){
struct timeval t;
gettimeofday(&t, 0);
return ((sqlite3_int64)t.tv_usec / 1000) + ((sqlite3_int64)t.tv_sec * 1000);
}
static void clear_sql(ClientCtx *p){
int j;
for(j=0; j<p->nPrepare; j++){
sqlite3_finalize(p->aPrepare[j].pStmt);
}
p->nPrepare = 0;
}
/*
** The sqlite3_wal_hook() callback used by all client database connections.
*/
static int clientWalHook(void *pArg, sqlite3 *db, const char *zDb, int nFrame){
if( nFrame>=g.nThreshold ){
g.bCkptRequired = 1;
}
return SQLITE_OK;
}
static int handle_run_command(ClientCtx *p){
int i, j;
int nBusy = 0;
sqlite3_int64 t0 = get_timer();
sqlite3_int64 t1 = t0;
int nT1 = 0;
int nTBusy1 = 0;
int rc = SQLITE_OK;
pthread_mutex_lock(&g.ckpt_mutex);
g.nRun++;
pthread_mutex_unlock(&g.ckpt_mutex);
for(j=0; (p->nRepeat<=0 || j<p->nRepeat) && rc==SQLITE_OK; j++){
sqlite3_int64 t2;
for(i=0; i<p->nPrepare && rc==SQLITE_OK; i++){
sqlite3_stmt *pStmt = p->aPrepare[i].pStmt;
/* If the bMutex flag is set, grab g.commit_mutex before executing
** the SQL statement (which is always "COMMIT" in this case). */
if( p->aPrepare[i].bMutex ){
sqlite3_mutex_enter(g.commit_mutex);
}
/* Execute the statement */
while( sqlite3_step(pStmt)==SQLITE_ROW );
rc = sqlite3_reset(pStmt);
/* Relinquish the g.commit_mutex mutex if required. */
if( p->aPrepare[i].bMutex ){
sqlite3_mutex_leave(g.commit_mutex);
}
if( (rc & 0xFF)==SQLITE_BUSY ){
if( sqlite3_get_autocommit(p->db)==0 ){
sqlite3_exec(p->db, "ROLLBACK", 0, 0, 0);
}
nBusy++;
rc = SQLITE_OK;
break;
}
else if( rc!=SQLITE_OK ){
send_message(p, "error - %s\n", sqlite3_errmsg(p->db));
}
}
t2 = get_timer();
if( t2>=(t1+1000) ){
int nMs = (t2 - t1);
int nDone = (j+1 - nBusy - nT1);
rc = send_message(
p, "(%d done @ %d per second, %d busy)\n",
nDone, (1000*nDone + nMs/2) / nMs, nBusy - nTBusy1
);
t1 = t2;
nT1 = j+1 - nBusy;
nTBusy1 = nBusy;
if( p->nSecond>0 && (p->nSecond*1000)<=t1-t0 ) break;
}
/* Checkpoint handling. */
pthread_mutex_lock(&g.ckpt_mutex);
if( rc==SQLITE_OK && g.bCkptRequired ){
if( g.nWait==g.nRun-1 ){
/* All other clients are already waiting on the condition variable.
** Run the checkpoint, signal the condition and move on. */
rc = sqlite3_wal_checkpoint(p->db, "main");
g.bCkptRequired = 0;
pthread_cond_broadcast(&g.ckpt_cond);
}else{
assert( g.nWait<g.nRun-1 );
g.nWait++;
pthread_cond_wait(&g.ckpt_cond, &g.ckpt_mutex);
g.nWait--;
}
}
pthread_mutex_unlock(&g.ckpt_mutex);
}
if( rc==SQLITE_OK ){
int nMs = (int)(get_timer() - t0);
send_message(p, "ok (%d/%d SQLITE_BUSY)\n", nBusy, j);
if( p->nRepeat<=0 ){
send_message(p, "### ok %d busy %d ms %d\n", j-nBusy, nBusy, nMs);
}
}
clear_sql(p);
pthread_mutex_lock(&g.ckpt_mutex);
g.nRun--;
pthread_mutex_unlock(&g.ckpt_mutex);
return rc;
}
static int handle_dot_command(ClientCtx *p, const char *zCmd, int nCmd){
int n;
int rc = 0;
const char *z = &zCmd[1];
const char *zArg;
int nArg;
assert( zCmd[0]=='.' );
for(n=0; n<(nCmd-1); n++){
if( is_whitespace(z[n]) ) break;
}
zArg = &z[n];
nArg = nCmd-n;
trim_string(&zArg, &nArg);
if( n>=1 && n<=4 && 0==strncmp(z, "list", n) ){
int i;
for(i=0; rc==0 && i<p->nPrepare; i++){
const char *zSql = sqlite3_sql(p->aPrepare[i].pStmt);
int nSql = strlen(zSql);
trim_string(&zSql, &nSql);
rc = send_message(p, "%d: %.*s\n", i, nSql, zSql);
}
}
else if( n>=1 && n<=4 && 0==strncmp(z, "quit", n) ){
rc = 1;
}
else if( n>=2 && n<=7 && 0==strncmp(z, "repeats", n) ){
if( nArg ){
p->nRepeat = strtol(zArg, 0, 0);
if( p->nRepeat>0 ) p->nSecond = 0;
}
rc = send_message(p, "ok (repeat=%d)\n", p->nRepeat);
}
else if( n>=2 && n<=3 && 0==strncmp(z, "run", n) ){
rc = handle_run_command(p);
}
else if( n>=2 && n<=7 && 0==strncmp(z, "seconds", n) ){
if( nArg ){
p->nSecond = strtol(zArg, 0, 0);
if( p->nSecond>0 ) p->nRepeat = 0;
}
rc = send_message(p, "ok (repeat=%d)\n", p->nRepeat);
}
else if( n>=1 && n<=12 && 0==strncmp(z, "mutex_commit", n) ){
rc = handle_some_sql(p, "COMMIT;", 7);
if( rc==SQLITE_OK ){
p->aPrepare[p->nPrepare-1].bMutex = 1;
}
}
else if( n>=2 && n<=4 && 0==strncmp(z, "stop", n) ){
sqlite3_close(g.db);
exit(0);
}
else{
send_message(p,
"unrecognized dot command: %.*s\n"
"should be \"list\", \"run\", \"repeats\", \"mutex_commit\" "
"or \"seconds\"\n", n, z
);
rc = 1;
}
return rc;
}
static void *handle_client(void *pArg){
char zCmd[32*1024]; /* Read buffer */
int nCmd = 0; /* Valid bytes in zCmd[] */
int res; /* Result of read() call */
int rc = SQLITE_OK;
ClientCtx ctx;
memset(&ctx, 0, sizeof(ClientCtx));
ctx.fd = (int)(intptr_t)pArg;
ctx.nRepeat = 1;
rc = sqlite3_open_v2(g.zDatabaseName, &ctx.db,
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, g.zVfs
);
if( rc!=SQLITE_OK ){
fprintf(stderr, "sqlite3_open(): %s\n", sqlite3_errmsg(ctx.db));
return 0;
}
sqlite3_create_function(
ctx.db, "usleep", 1, SQLITE_UTF8, (void*)sqlite3_vfs_find(0),
usleepFunc, 0, 0
);
/* Register the wal-hook with the new client connection */
sqlite3_wal_hook(ctx.db, clientWalHook, (void*)&ctx);
while( rc==SQLITE_OK ){
int i;
int iStart;
int nConsume;
res = read(ctx.fd, &zCmd[nCmd], sizeof(zCmd)-nCmd-1);
if( res<=0 ) break;
nCmd += res;
if( nCmd>=sizeof(zCmd)-1 ){
fprintf(stderr, "oversized (>32KiB) message\n");
res = 0;
break;
}
zCmd[nCmd] = '\0';
do {
nConsume = 0;
/* Gobble up any whitespace */
iStart = 0;
while( is_whitespace(zCmd[iStart]) ) iStart++;
if( zCmd[iStart]=='.' ){
/* This is a dot-command. Search for end-of-line. */
for(i=iStart; i<nCmd; i++){
if( is_eol(zCmd[i]) ){
rc = handle_dot_command(&ctx, &zCmd[iStart], i-iStart);
nConsume = i+1;
break;
}
}
}else{
int iSemi;
char c = 0;
for(iSemi=iStart; iSemi<nCmd; iSemi++){
if( zCmd[iSemi]==';' ){
c = zCmd[iSemi+1];
zCmd[iSemi+1] = '\0';
break;
}
}
if( iSemi<nCmd ){
if( sqlite3_complete(zCmd) ){
rc = handle_some_sql(&ctx, zCmd, iSemi+1);
nConsume = iSemi+1;
}
if( c ){
zCmd[iSemi+1] = c;
}
}
}
if( nConsume>0 ){
nCmd = nCmd-nConsume;
if( nCmd>0 ){
memmove(zCmd, &zCmd[nConsume], nCmd);
}
}
}while( rc==SQLITE_OK && nConsume>0 );
}
fprintf(stdout, "Client %d disconnects\n", ctx.fd);
fflush(stdout);
close(ctx.fd);
clear_sql(&ctx);
sqlite3_free(ctx.aPrepare);
sqlite3_close(ctx.db);
return 0;
}
static void usage(const char *zExec){
fprintf(stderr, "Usage: %s ?-vfs VFS? DATABASE\n", zExec);
exit(1);
}
int main(int argc, char *argv[]) {
int sfd;
int rc;
int yes = 1;
struct sockaddr_in server;
/* Ignore SIGPIPE. Otherwise the server exits if a client disconnects
** abruptly. */
signal(SIGPIPE, SIG_IGN);
if( argc!=2 && argc!=4 ){
usage(argv[0]);
}
if( argc==4 ){
int n = strlen(argv[1]);
if( n<2 || n>4 || memcmp("-vfs", argv[1], 4) ) usage(argv[0]);
g.zVfs = argv[2];
}
g.zDatabaseName = argv[argc-1];
g.commit_mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
g.nThreshold = TSERVER_DEFAULT_CHECKPOINT_THRESHOLD;
pthread_mutex_init(&g.ckpt_mutex, 0);
pthread_cond_init(&g.ckpt_cond, 0);
rc = sqlite3_open_v2(g.zDatabaseName, &g.db,
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, g.zVfs
);
if( rc!=SQLITE_OK ){
fprintf(stderr, "sqlite3_open(): %s\n", sqlite3_errmsg(g.db));
return 1;
}
rc = sqlite3_exec(g.db, "SELECT * FROM sqlite_master", 0, 0, 0);
if( rc!=SQLITE_OK ){
fprintf(stderr, "sqlite3_exec(): %s\n", sqlite3_errmsg(g.db));
return 1;
}
sfd = socket(AF_INET, SOCK_STREAM, 0);
if( sfd<0 ){
fprintf(stderr, "socket() failed\n");
return 1;
}
rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
if( rc<0 ){
perror("setsockopt");
return 1;
}
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr("127.0.0.1");
server.sin_port = htons(TSERVER_PORTNUMBER);
rc = bind(sfd, (struct sockaddr *)&server, sizeof(struct sockaddr));
if( rc<0 ){
fprintf(stderr, "bind() failed\n");
return 1;
}
rc = listen(sfd, 8);
if( rc<0 ){
fprintf(stderr, "listen() failed\n");
return 1;
}
while( 1 ){
pthread_t tid;
int cfd = accept(sfd, NULL, NULL);
if( cfd<0 ){
perror("accept()");
return 1;
}
fprintf(stdout, "Client %d connects\n", cfd);
fflush(stdout);
rc = pthread_create(&tid, NULL, handle_client, (void*)(intptr_t)cfd);
if( rc!=0 ){
perror("pthread_create()");
return 1;
}
pthread_detach(tid);
}
return 0;
}