Compare commits

..

299 Commits

Author SHA1 Message Date
dan cf8e91442d Add experimental API sqlite3session_diff().
FossilOrigin-Name: c4340b2ea2115ad97dcac036f9034e132ab789e5
2015-04-08 16:01:12 +00:00
drh 159e6b304c Merge printf() width and precision overflow fixes from trunk.
FossilOrigin-Name: aeca95ac77f6f320a916f7e3c5a7a588ef4a20c8
2015-04-07 23:10:44 +00:00
drh af76530fb0 Merge obscure problem fixes from trunk.
FossilOrigin-Name: 271c110bcf5bf2ea7e113dd01dec876a08e3c047
2015-04-06 12:08:24 +00:00
drh edb3e433ba Merge recent enhancements from trunk.
FossilOrigin-Name: aea439bdc6d4d3a8568423171abf381843188826
2015-04-01 16:39:06 +00:00
drh 2a07f5ecec Merge all recent trunk enhancements and fixes into the sessions branch.
FossilOrigin-Name: df94e61f93da390cb75c48975c50e9d62096ea0b
2015-03-31 00:10:21 +00:00
drh edea911945 Merge all recent trunk enhancements into the sessions branch.
FossilOrigin-Name: 54aaa6f29ad1bf84148c2403eed77e8311f68d66
2015-03-24 19:02:13 +00:00
drh 099006de44 Merge all recent trunk enhancements into the sessions branch.
FossilOrigin-Name: 1d1c57c3566d86cd44f146688b620131b3b57e52
2015-03-17 18:54:10 +00:00
drh c11a172d5f Merge recent trunk enhancements into the sessions branch.
FossilOrigin-Name: 68c8937e83b770d5ec6b1855c1bde81057c11b5f
2015-03-09 13:07:45 +00:00
drh 2f82071478 Merge the latest trunk fixes into the sessions branch.
FossilOrigin-Name: 131a2d3116436ca6304777c9c5d46b7c4051d949
2015-02-25 14:02:53 +00:00
drh 878acb32ef Merge all recent trunk changes into the sessions branch.
FossilOrigin-Name: f36bb5fa5c5e3430808fe35d58f45559a216d341
2015-02-21 15:13:50 +00:00
drh 1ee4a2dd88 Merge all the latest enhancements from trunk.
FossilOrigin-Name: ae7eef117f28a5dae7a05805f2d31ac532a9fcc5
2015-02-06 15:40:32 +00:00
drh 9c2e9f7afa Merge all 3.8.8 changes into the sessions branch.
FossilOrigin-Name: bebd131a3554ea95921e6edbc8aa36674ef25fe3
2015-01-16 12:44:52 +00:00
drh 7a20f698df Merge trunk 3.8.8 beta changes into the sessions branch
FossilOrigin-Name: 0ba124540b5b2a9ceda0f4f4a46e6be54edad813
2015-01-14 17:16:23 +00:00
drh f7af7a17b9 Merge recent enhancements from trunk, including test scripts enhancements and
the removal of limits on the number of terms in a VALUES clause.

FossilOrigin-Name: 5a2dec55bfadc328b430a5231a037b37831a5530
2015-01-08 20:06:06 +00:00
drh c8ae34dc6a Merge the fix to PRAGMA data_version and testing improvements from trunk.
FossilOrigin-Name: 86e39123c1bca457672bc63eff00a823745077e5
2014-12-31 14:27:29 +00:00
drh b5a4387097 Merge the PRAGMA data_version redefinition and other fixes from trunk.
FossilOrigin-Name: 315243e49d30abd0eb31e3795ecf492aee179e4b
2014-12-22 18:48:48 +00:00
drh e465ac29cd Merge the PRAGMA data_version command and the enhancements to FK query planning
from trunk into the sessions branch.

FossilOrigin-Name: d4f82af0a4a9db222e3405cff05c41157e7bd5d9
2014-12-20 14:58:32 +00:00
drh 032f461900 Merge threading fixes from trunk into the sessions branch.
FossilOrigin-Name: 9817a2864eebe2dc90ce505fe0faa8b069ff48ff
2014-12-16 01:05:38 +00:00
drh 52fa3a89f6 Merge the KeyInfo cache mutex fix from trunk.
FossilOrigin-Name: f0940c73bd13cb49a3d229ce2517736324392cfb
2014-12-09 14:54:26 +00:00
drh dd2921ec1d Merge the fix for the shared-cache KeyInfo cache bug from trunk.
FossilOrigin-Name: 5a73da6a3083926a3dafd28c0604122296dc9184
2014-12-05 20:05:10 +00:00
drh c0d33d20f7 Import from trunk the fix the "test" target on the autoconf and MSVC makefiles
and some minor compiler warning fixes.

FossilOrigin-Name: c9f20eb2fbba0868df85ebb98877f9bb969be8a4
2014-12-05 00:41:17 +00:00
drh 34794c3d0c Incorporate the SQLITE_CHECKPOINT_TRUNCATE enhancement and a couple of
obscure bug fixes from trunk.

FossilOrigin-Name: 34ffa3b3c002b0e87d8382ad76e87dbaec0b2bc6
2014-12-04 23:35:08 +00:00
drh 6e09b16852 Merge all recent fixes and enhancements from trunk into sessions.
FossilOrigin-Name: 2617d93713d9f4cf907ab2e7baef6a0f74f7198e
2014-12-02 16:31:01 +00:00
drh 04e8a58669 Merge recent trunk enhancements, including the read-after-ROLLBACK change
and the addition of sqlite3_stmt_scanstatus() support, as well as various
minor bug fixes.

FossilOrigin-Name: f09055f3c4348264c7336f90646375f0d98b061e
2014-11-18 21:20:57 +00:00
drh ca3e10ea37 Merge recent trunk enhancements, and in particular the improvements to
the b-tree balancing logic, into the sessions branch.

FossilOrigin-Name: 28b044a51215a3f64dafb2cf3b6cb7d2029580ef
2014-10-31 14:53:32 +00:00
drh bc2866ca7a Merge all version 3.8.7 updates from trunk.
FossilOrigin-Name: f4de9e07be3819db222317725e62ea997cd22a20
2014-10-17 11:53:22 +00:00
dan abae0c4f1d Fix a problem causing sqlite3changeset_concat() to fail to detect attempts to concatenate patchsets which changesets.
FossilOrigin-Name: 236704a9d1551a50a55bd6e0b6473191a7a7dd21
2014-10-15 20:02:21 +00:00
dan f11b256b0b Merge latest trunk changes with this branch.
FossilOrigin-Name: 1b2824f1d11ac336779372e322aecfb36fb2a31d
2014-10-15 19:37:13 +00:00
drh 92fe38ece5 Merge recent trunk micro-optimizations and the DESC index GROUP BY ORDER BY
bug fix into the sessions branch.

FossilOrigin-Name: 83d4114f2aa404e670ced33511183baacd813a01
2014-10-14 13:41:32 +00:00
drh 78aad7cd1d Merge all recent trunk changes.
FossilOrigin-Name: abfef254721ca29b653f24bfe80e88adaa92dc6c
2014-10-10 12:56:35 +00:00
drh 595db25b28 Merge the latest enhancements from trunk.
FossilOrigin-Name: 2695772c984c215649a16e1e3e18a8048a6a60dd
2014-10-01 01:52:42 +00:00
drh f1a08ad88d Change the names of the stream interface APIs to be of the form
"_strm" instead of "_str".  In other words, added an "m" to the end, to
try to make it clear that we are talking about a "stream" and not a "string.

FossilOrigin-Name: 1f44bfdc237ee6304f4aa56e5c5d1c0d74fcc944
2014-09-27 20:45:48 +00:00
drh 6382b6dc71 Merge recent trunk changes (performance enhancements) into the sessions
branch.

FossilOrigin-Name: 497367cb57345dd37793e5f369b34d12be56172e
2014-09-27 19:51:50 +00:00
dan f05ac11214 Fix a segfault in the sessions module that could follow an OOM.
FossilOrigin-Name: 09985fa6b60a0bf38e23bbccd4d8e1d1cbf66124
2014-09-27 18:18:32 +00:00
dan e8fa8c9649 Fix a segfault in the streaming API functions triggered by a very long table name.
FossilOrigin-Name: d2642543eed54da1ac0f757d43dd4d72482eb752
2014-09-27 16:33:09 +00:00
dan 16228167c2 Improve sessions module documentation and comments. Fix some other code issues.
FossilOrigin-Name: bfc8bd80f8b225cebc66478448510ce84223ae7d
2014-09-27 12:26:18 +00:00
dan fa29ecc4ed Fix a problem with concatenating patchsets containing DELETE and INSERT operations on the same row.
FossilOrigin-Name: 4d8537eafb40e3687abc057ba26a1f7014f2c2d9
2014-09-26 10:52:21 +00:00
dan cbf6d2d2aa Add streaming version of sqlite3changeset_concat().
FossilOrigin-Name: 88eb6656bdb047a104837a2e15e7fe18c0a7a159
2014-09-25 20:43:28 +00:00
dan fa122adac1 Add streaming version of sqlite3changeset_invert() to sessions module.
FossilOrigin-Name: 8ded6a46794c7bff1c8b790c662ba7e92f576380
2014-09-25 14:54:20 +00:00
dan 4757c65866 Add streaming version of sqlite3changeset_apply(). Tests and fixes for the same and sqlite3changeset_start_str().
FossilOrigin-Name: b917fc146876f764442de08d5ec36e5b4cf5ab52
2014-09-24 17:13:20 +00:00
dan ef7a630496 Begin adding 'streaming' APIs to sessions module. This is a work in progress.
FossilOrigin-Name: 3c7d3d950bbf5f5ed3696ebc61c77ca48bafe2b5
2014-09-23 20:39:55 +00:00
drh 6b011d83b4 Merge all recent trunk changes into the sessions branch.
FossilOrigin-Name: 6406b77f2c447751a2fbb16f01c61cdcfd6af59e
2014-09-21 22:49:20 +00:00
drh a4a8a3c22e Merge support for large files on Android from trunk.
FossilOrigin-Name: c2885c6bb24cc55178467e57e77bf71df58b3b13
2014-09-08 15:04:24 +00:00
drh bf9ed6f9af Merge the latest trunk changes, including the multi-threaded sorter, into
the sessions branch.

FossilOrigin-Name: d4cce2c71e64ab7b6a65a81b88b69445ed859351
2014-09-02 15:49:47 +00:00
drh e1ed0b0e17 Merge recent performance enhancements and the CAST operator enhancements
into the sessions branch.

FossilOrigin-Name: 08ae974ac80fabe53f515bbbd93ccf55de8ee671
2014-08-26 02:15:07 +00:00
drh 2571e4eddd Merge all recent trunk changes, especially the fix for
ticket [369d57fb8e5ccdff06f1], but also the skip-scan improvement and
performance improvements in the b-tree code.

FossilOrigin-Name: 0b9e2c3269695713b538561d999c68097db70f0c
2014-08-21 16:09:36 +00:00
drh 3a432b0b47 Disable the hook-7.5.2 tests when using sessions, since that are not correct
in that case.

FossilOrigin-Name: 6d5b9332e8d8bb572ac98b0f4e47e59ad12aac26
2014-08-19 00:33:22 +00:00
drh 2967e0cc9c Improved response to error conditions in the ".session" shell command and
in the "changeset" command-line program.

FossilOrigin-Name: b69f7dd1f35846c3bb9f4f160d50c4f03796f887
2014-08-19 00:26:17 +00:00
drh 9f674f2600 Fix the autoconf and MSVC makefiles so that they construct the changeset
command-line utility upon request.  Delete that utility program when
"make clean" is run.

FossilOrigin-Name: 4dc15fe066f079ff31c89a5e0242dd5b3c851d29
2014-08-18 20:23:29 +00:00
drh f5ab08c779 Fix a harmless compiler warning in the sessionAppendDelete() function.
FossilOrigin-Name: 1324d08d0c639378cf1b03bd6410db81947747ac
2014-08-18 20:14:30 +00:00
drh 5f77b89ecd Add the "changeset" command-line tool for analyzing and manipulating
changesets in files on disk.  Add the ".session" command to the command-line
tool.

FossilOrigin-Name: 31addb627fdbaeb908e0611ad82f6db7537428ea
2014-08-18 20:08:25 +00:00
drh 03168cacd5 A reasonably complete implementation of the "changeset" command-line tool
and the ".sessions" command in the command-line shell.

FossilOrigin-Name: 7b12f1f9c012f33d376242920583807b014b3287
2014-08-18 20:01:31 +00:00
drh 3a67b0453e Add the "changeset" command-line utility for getting an ASCII dump of
change sets.

FossilOrigin-Name: 55bb3544a6b474c04853270067a35ca4b0079f52
2014-08-18 17:56:31 +00:00
dan 082c96dffa Add miscellaneous test cases to improve coverage of sessions module.
FossilOrigin-Name: 0fac6cfffe628ea02c78ebad065307309ec9eaa1
2014-08-18 16:03:46 +00:00
drh e62296100f Begin adding commands to the command-line interface for interacting with
the sessions extension.  This is the first check-in of a work-in-progress.

FossilOrigin-Name: c2fcf0b9f4bdc48dfc6530bda4f531b94a833207
2014-08-18 15:08:26 +00:00
drh 8b7a2e3a1e Merge the latest trunk changes, and in particular the refactoring of the
object names in the command-line shell.

FossilOrigin-Name: 419d286a2fc465f6e0f9662909d0cc52a18eefa4
2014-08-18 13:48:41 +00:00
dan 2e934cd40c Add tests for sessions module.
FossilOrigin-Name: 82fdb1975f5b29a751089a8582713372999ae56e
2014-08-18 08:42:36 +00:00
dan a71d2371d0 Fix some missing and out-of-date comments in the sessions module.
FossilOrigin-Name: 05c1d9149b27c68e2654ed28491722927d2176f5
2014-08-16 19:01:00 +00:00
dan 64277f4a70 Fixes for the sqlite3changeset_concat() API regarding patchsets.
FossilOrigin-Name: dccb34859508eac8146ae5b19c447673d04be3b0
2014-08-16 16:47:55 +00:00
dan 73b3c05590 Begin adding the sqlite3session_patchset() API to the sessions extension. This is an interim commit.
FossilOrigin-Name: 60a4565a8c44762a002cd02979317df5ca47e899
2014-08-15 20:15:49 +00:00
drh f3a3dfe9f1 Update the sessions branch for version 3.8.6.
FossilOrigin-Name: 2acbeac1fd9b9feb26e1c24d4ae50ce79f17a3f8
2014-08-15 15:10:46 +00:00
drh d0738004ca Merge the trunk changes for 3.8.6 beta3 into the sessions branch.
FossilOrigin-Name: d49455d9a972fc2224d9beb97165a998ca56e838
2014-08-13 14:43:32 +00:00
drh 73472e38ce Merge the second InterlockedCompareExchange() fix from trunk.
FossilOrigin-Name: 69018967f6e38521dd9b0fa9ce05c9b947635335
2014-08-06 12:00:08 +00:00
drh 0894993e88 Fix an obsolete comment in the func.c source file. No changes to code.
FossilOrigin-Name: 7a145c9409e70b92946534eb44c2e205f7309897
2014-08-06 11:49:31 +00:00
drh 6a3d6085ad Merge in the fix for the InterlockedCompareExchange() build issue.
FossilOrigin-Name: 091aa5f1047f0ec984a69ab1f356c1960813b77b
2014-08-06 03:16:56 +00:00
drh 58c4cbe152 Merge the latest 3.8.6 beta changes from trunk.
FossilOrigin-Name: 68a6d5e2f43702c78057ae2f2a7345c981d24e17
2014-08-06 01:25:47 +00:00
drh 39a7bfd361 Merge the fix for the CREATE UNIQUE INDEX problem into the sessions branch.
FossilOrigin-Name: 43401ee624587ffa166c3cda7e5265ad0d81da5c
2014-07-30 14:29:54 +00:00
drh aae73527ad Merge recent trunk changes, and especially the fix for the R-Tree problem
described in ticket [d2889096e7bdeac6].

FossilOrigin-Name: 8f1beeade0ddf802900c9d203a4fd678d1190394
2014-07-29 12:40:45 +00:00
drh 35c3d8cc75 Merge recent trunk changes into the sessions branch.
FossilOrigin-Name: a9db017eabdefafcda87c497e8bafa07002ac0fe
2014-07-24 16:23:51 +00:00
drh f257b4c613 Merge the latest trunk enhancements into the sessions branch.
FossilOrigin-Name: a5d94eaba6aa12ff16d2a0af2fc752bcdb461877
2014-06-30 20:02:55 +00:00
drh 83f951ee95 Merge the 3.8.5 release candidate changes into the sessions branch.
FossilOrigin-Name: 09e75d82d02f900e58eceaaa010a42fc7044c89f
2014-06-03 20:09:48 +00:00
drh cbabb20390 Merge trunk changes into the sessions branch.
FossilOrigin-Name: 9f18e78f039ffa89a8806810ad385bdc51bc88ed
2014-05-29 20:29:13 +00:00
drh aff0fbb6a5 Merge recent trunk changes into the sessions branch.
FossilOrigin-Name: a769c7e03e6d4163986d49652687de34d15906a0
2014-05-26 20:00:00 +00:00
drh d963982049 Merge recent trunk changes into the sessions branch.
FossilOrigin-Name: 2d33afe0c43cb99caa521c48b721c3b0971e967c
2014-05-20 00:20:23 +00:00
drh 2e525f18e2 Merge all recent trunk enhancements and fixes into the sessions branch.
FossilOrigin-Name: e158812c34b01ce516d26636a489509bf61d1c27
2014-04-28 18:02:21 +00:00
drh ae28b301e1 Add the sqlite3_rtree_query_callback() API to the RTree virtual table.
FossilOrigin-Name: 3dca2809352c6c6d56db74447a814f77011c6220
2014-04-28 17:51:12 +00:00
drh c686929914 Enhance the sqlite3_rtree_query_info object to report on the number of
elements in the priority queue at each level.

FossilOrigin-Name: f7dad408dd46a1e3612b6142a3afb1d0d4fcda00
2014-04-25 16:29:03 +00:00
drh 98cf9d74f8 Fix the generation of sqlite3_rtree_query_info.iRowid and add test cases
to verify that it is fixed.

FossilOrigin-Name: eba95ead49f8f8ce45d400186562ff0066537c5c
2014-04-21 18:13:37 +00:00
drh 74188329ce Be sure to initialize the sqlite3_rtree_query_info.iRowid field for the
leaves of the R-Tree when doing a query callback search.

FossilOrigin-Name: 4394693882c04c19ebe87ef7547c57e679554397
2014-04-21 15:53:45 +00:00
drh a6ca7f2c1b Fix an off-by-one error in setting the "iLevel" field of the
sqlite3_rtree_query_info structure passed into the RTree query callback.

FossilOrigin-Name: d708f159abfb3b87e2844463088d4fb7f8da9c97
2014-04-21 15:21:19 +00:00
drh ec7c03a08b Further improvements to the RTREE_DECODE_COORD() method, to take advantage
of known processor byte orders when available.  This makes the code 3% faster,
according to valgrind.  Also add test cases to make sure the on-disk
representation is correct.

FossilOrigin-Name: 6f3e94f4b1b403cd7bfc5e8e0ffbd61b5174d3a4
2014-04-18 01:37:08 +00:00
drh e49b2718e0 Merge the latest changes from sessions.
FossilOrigin-Name: d9eef5b03c7c4bb69c11eda41152ee81aed1cac7
2014-04-18 01:14:46 +00:00
drh a1a483d54e Merge recent trunk changes into sessions.
FossilOrigin-Name: 95e77efe076ab421bd246119c47dba5dacf9d087
2014-04-18 01:10:05 +00:00
drh a5db90fc8e Performance optimization on byte-swapping in R-Tree.
FossilOrigin-Name: 444084fd620fc3f45cfb87b83f532d76bd2744e7
2014-04-17 23:23:29 +00:00
drh 1821cbacf1 More test cases with very long priority queues.
FossilOrigin-Name: 71692aa97c78676f0ba80eaeec0ad9ac225f4427
2014-04-17 15:34:58 +00:00
drh b640595f97 Test cases and bug fixes for the sqlite3_rtree_query_callback()
mechanism.

FossilOrigin-Name: 1ccaaed6b516ec2ce953c1b31025a82ba76d00e7
2014-04-17 14:52:20 +00:00
drh 78b486ea12 Refactor the constraint checking logic in RTree. The new-style constraint
callbacks created by sqlite3_rtree_query_callback() are now hooked up from
end to end, though still untested.

FossilOrigin-Name: 32a13870175a1dd1d33af3572dde09ff607a04b6
2014-04-17 13:15:33 +00:00
drh 18b5142b29 Performance optimization on nodeGetCell() in R-Tree.
FossilOrigin-Name: 5d20ff9ec837ad35bc44d6c25d13764b350e81dd
2014-04-16 21:02:28 +00:00
drh 22596b6b3c Convert the RTree module query mechanism over to using a priority queue for
walking the RTree.

FossilOrigin-Name: f26936f71a16ab25590540f7feb273514dfb69ff
2014-04-16 17:23:23 +00:00
drh 933a5ff2d4 TCL tests now all pass.
FossilOrigin-Name: f864baccd3fe0ee939ac1ec20069792f649cddc0
2014-04-16 17:15:14 +00:00
drh ab908cfcb4 Fix a bug in rowid=? query handling. More problems remain.
FossilOrigin-Name: 5b0e6ba4a5050cf81cf41b977b28d714163569e0
2014-04-16 14:45:11 +00:00
drh f247982e37 Bug fixes to the priority-queue implementation for R-Trees. Improved tracing
capability.  Some queries work now, but still many problems.

FossilOrigin-Name: a439ddd629c6bb5ea2e7e274673fee4f5c207acf
2014-04-16 13:00:08 +00:00
drh 96ce1b36aa Initial attempt at getting R-Tree queries to work using a priority queue.
This check-in compiles, but R-Trees do not work well.  And there are
debugging printf()s left in the code.  This is an incremental check-in.

FossilOrigin-Name: 53688a25c23c394278a357829793889970aa4157
2014-04-15 21:06:14 +00:00
drh de8c279b28 Fix comments on the rtreenode() and rtreedepth() test function in the R-Tree
module.

FossilOrigin-Name: ade5b986e8baab9df7bdaf7ccfaee2d6ba55fa3c
2014-04-14 14:43:09 +00:00
drh b6c3aeac90 Remove over 300 lines of unused code, code that implemented the older
Guttman insertion algorithms that are no longer used.

FossilOrigin-Name: 3ba5f295c709faebf5505e61f6dc5266b811b086
2014-04-14 12:18:17 +00:00
drh 8ae04b53c9 Continuing work on the new custom query mechanism for r-tree.
FossilOrigin-Name: ca7357e66ca60f59477b1db000c2cdaeb8082ae1
2014-04-13 16:10:09 +00:00
drh 0ccb6cdc87 Continuing clean-up of the R-Tree module in preparation for cutting in the
new generalized query mechanism.

FossilOrigin-Name: 66c858f20586424e15d0bfa3d7b56643bde66226
2014-04-12 17:44:00 +00:00
drh c9cb207acf Add the new interfaces to rtree, though they do not yet work. Add the
"show_speedtest1_rtree.tcl" script for showing the test data used for the
R-Tree tests of speedtest1.  Change speedtest1 to generate better R-Tree
test data.

FossilOrigin-Name: 0b70275972c7a6a533566c1e50bffbf3ac531e95
2014-04-11 23:14:48 +00:00
drh eb7c4f2aea Add the --verify option to speedtest1. Add verification test cases to
the "rtree" testset and a case that uses a custom geometry callback.

FossilOrigin-Name: 9d485c4207a81f32334857d4a608c5c511dd2b83
2014-04-11 17:41:08 +00:00
drh dcc5c7b2fc Update comments in the R-Tree module in preparation for some big changes.
Add an "rtree" performance test to speedtest1.

FossilOrigin-Name: 20a73ec0b2f56609a4504052e198e32803d30207
2014-04-11 16:14:54 +00:00
drh 652cc4b6dc Merge all recent changes from trunk,
including the fix for the OP_SCopy-vs-OP_Copy problem.

FossilOrigin-Name: 9515c8344a6743bbb0c6a6e49fb79fb3139090df
2014-04-03 16:35:33 +00:00
drh 24ed9d0445 Merge in fixes (including the corrupt-database crash fix) and
performance enhancements from trunk.

FossilOrigin-Name: fc8ca1a87e7127bd28f32fd809aec3e24355fbfa
2014-03-26 19:43:30 +00:00
drh 092378860d Bump the version number to 3.8.4.1., to conform with trunk.
FossilOrigin-Name: 42c9d8fc5fe4f0250bfa1f977c7cc63e5741408f
2014-03-12 10:03:13 +00:00
drh 181aed3c25 Version 3.8.4 for sessions (plus two minor fixes).
FossilOrigin-Name: 917c410808756e1697bd628f4c9c57dd13d08ce7
2014-03-11 01:48:06 +00:00
drh 4e2f459987 Merge the fix for the journal_mode=PERSIST error recovery delay bug.
FossilOrigin-Name: bb6a75f4bbcaaabc3ad47992c7c87868c70e8daf
2014-03-07 14:36:50 +00:00
drh 87778c7f96 Fix a harmless compiler warning in the sessions test harness.
FossilOrigin-Name: d389e20ab07a76d0c4be3801265977fa977a4394
2014-03-06 15:01:11 +00:00
drh b7bc8c9849 Enhance the test harness for the sessions interface so that it does not
use SQLite operations that can encounter an OOM error in places where it
is unable to report an OOM error back up to the test script.

FossilOrigin-Name: bc0e661033b78f27866932244e6204985ae39000
2014-03-06 14:53:25 +00:00
drh f2eded23f5 Merge the latest 3.8.4 tweaks from trunk.
FossilOrigin-Name: 1ed463d91866182fbc884fb88770717ee5bf33ce
2014-03-06 13:48:31 +00:00
drh 3bde1354ff Merge compiler-warning fixes from trunk.
FossilOrigin-Name: a1f2b0428518ec18af74a0e01deb4e40cd95a78f
2014-03-05 23:12:55 +00:00
drh 0503cf491f Merge in various obscure bug fixes and the removal of Mem.memType from trunk.
FossilOrigin-Name: 0828975d580d309a1c66da4b6caeb65b21a01199
2014-03-05 14:49:51 +00:00
drh 7826a9b83f Merge the performance enhancements of trunk (and some obscure bug fixes)
into the sessions branch.

FossilOrigin-Name: 7f51ad97f0b24c57453d58faf25eee68861faa23
2014-03-04 14:34:14 +00:00
drh 15d4211400 Sync with trunk. Bring in the command-line shell updates and the new 3.8.4
version number.

FossilOrigin-Name: 2cd35ff6512e1973077f7ffc2c902476eabbe672
2014-02-11 16:31:14 +00:00
drh 5671ef69ec Sync the latest trunk changes, and in particular the STAT4 IS NOT NULL fix.
FossilOrigin-Name: b006792695d23980e1923b21915d5c1138ecf29d
2014-02-11 04:30:29 +00:00
drh 4b46eb108d Sync with version 3.8.3.
FossilOrigin-Name: a704b65b9476d60c88b5bc82f2743faa2bce5ac2
2014-02-03 13:58:42 +00:00
dan 43a6c6b2d4 Merge latest fixes from the trunk.
FossilOrigin-Name: 6b6dcd4cc75317628072abac7c58b41361cc72b4
2014-01-29 14:21:31 +00:00
drh 19913f4419 Bring in the latest updates from trunk.
FossilOrigin-Name: 7b5f3773867ed0e4ed17bd473ba972d500035318
2014-01-28 18:06:17 +00:00
drh c187698614 Bring in all the latest trunk changes, including the Common Table
Expressions implementation.

FossilOrigin-Name: 9b43e559195680e558264c4c00d34dc9cf9d9146
2014-01-24 14:05:18 +00:00
drh d9e3c267b4 Merge the latest trunk changes into the sessions branch.
FossilOrigin-Name: cfd110bf5db2c1993a5e2ca718648bd9c17ee22c
2013-12-24 12:09:42 +00:00
drh c8d985e09c Merge in all recent preformance enhancements from trunk.
FossilOrigin-Name: 32477642d79615fb85680bdac812ad9655cf6902
2013-12-14 18:24:46 +00:00
drh 65106c77bb Update to the 3.8.2 release.
FossilOrigin-Name: e579661a7950fe9f8eb10012946100c874ba54b0
2013-12-06 15:49:45 +00:00
drh 27964431ea Merge in trunk changes.
FossilOrigin-Name: a2914d6b17358de05eb39f764c0c13d2d4cf8f3b
2013-12-03 20:51:33 +00:00
drh 52208a6332 Merge all recent 3.8.2 beta changes from trunk.
FossilOrigin-Name: 67c34ccfa9596a923695dc4994ec87b331f8326c
2013-12-02 21:58:19 +00:00
drh 5b8d1ab454 Merge all recent trunk changes.
FossilOrigin-Name: 3a2a1bd47875e114d8e6f31c1768908f401d2861
2013-11-27 21:53:51 +00:00
drh 1b2ee4fe1a Merge in performance enhancements from trunk.
FossilOrigin-Name: fc9ae839569eb28eb734c52d95676c59b2e27494
2013-11-26 18:00:29 +00:00
drh 212c6be141 Merge the skip-scan optimization into the sessions branch.
FossilOrigin-Name: 7596d1bf8040f7cefc7b22c5e609acc5d66820bf
2013-11-14 19:18:39 +00:00
drh cf1ef10b62 Import recent bug fixes from trunk.
FossilOrigin-Name: 20eeee4cd34a9bffe6bf65962126ddf8ca04eb3e
2013-11-12 15:39:01 +00:00
drh fba5765b57 Fix two test cases (due to changes in the formatting of constraint errors)
so that all tests now pass.

FossilOrigin-Name: 129e2b69178147d04ee27fce66c17b39f8654da3
2013-11-11 23:02:10 +00:00
drh cbf1b8ef7d Merge in the WITHOUT ROWID changes. A few tests are failing now. They will
be fixed in a follow-on check-in.

FossilOrigin-Name: 5addd1234ded59ce60fb633b76ac87d483377edd
2013-11-11 22:55:26 +00:00
drh d63d4b7bd7 Merge all trunk changes since 3.8.1 into the sessions branch.
FossilOrigin-Name: aa72ea8a004de7fe744fc13baef19d05aea607d5
2013-10-28 22:47:24 +00:00
drh d82d4750a8 Merge the latest trunk changes.
FossilOrigin-Name: 5806546822b717d712dc7cd9de88a86f5bf2f715
2013-10-16 14:32:47 +00:00
drh 9327c70f68 Merge the latest trunk changes into the sessions branch. This merge
should fix the build for WinRT.

FossilOrigin-Name: e111e4edf9d7b2d4d6d6673a7576801839e9537d
2013-10-15 14:10:44 +00:00
drh a63b852992 Synchronize with the trunk.
FossilOrigin-Name: 136445ba020c9475d3f5a7843d7d0add98477138
2013-10-10 20:13:18 +00:00
drh 59255f9a98 Merge in all the latest trunk changes, including the win32-longpath VFS and
the fix for the segfault in the omit-left-join optimization.

FossilOrigin-Name: cdd3838b7876639cf99904b2054884247fbff529
2013-09-03 14:49:07 +00:00
drh 5f9742e0ff Merge from trunk: (1) Recent bug fixes (2) STAT4 support (3) win32-longpath
support.

FossilOrigin-Name: e7ebc8f74fe91dee26f952fdf49e427b45448667
2013-08-29 15:08:38 +00:00
dan 9f0656d8c5 Merge recent trunk changes.
FossilOrigin-Name: 6cc54de88bba00e07fac9de36caac216d94bf070
2013-08-23 17:54:46 +00:00
dan 7531a5a378 Add the sqlite3session_table_filter API to the sessions extension.
FossilOrigin-Name: b7e4dd889d37c8f57c2d3c7900e802f644aac3ea
2013-08-23 17:43:32 +00:00
drh 32683532f0 Merge in minor bug fixes and performance tweaks from trunk leading up to
the version 3.8.0 release.

FossilOrigin-Name: 831492dca8bcfb1a1f83a8bb15de9cc94f29f07e
2013-08-22 15:07:08 +00:00
drh 90bf3d7422 Merge performance enhancements and compiler warning fixes from trunk.
FossilOrigin-Name: 3e4033285deb417bd72c008917729dbf3bf4e90d
2013-08-20 13:02:16 +00:00
drh db78b96111 Merge in all the latest updates and enhancements from trunk.
FossilOrigin-Name: 67587a3348b84424b2ab1f2df1ea923a5168e2e4
2013-08-19 12:49:06 +00:00
drh 700a7fdf83 Merge in the latest changes from trunk.
FossilOrigin-Name: 69d5bed017bda3e184857febcc8b6f6bed6ad228
2013-08-06 14:52:27 +00:00
drh 7fb30bd0dd Merge in the latest trunk changes, including partial indexes, the MAX_PATH
fix in os_win.c, and the sqlite3_cancel_auto_extension() API.

FossilOrigin-Name: 7e1acb390770d1bd189fac7a3a7f96106f96e3a4
2013-08-02 20:44:48 +00:00
drh 6f855952d0 Merge recent trunk changes (such as the query_only PRAGMA, the
defer_foreign_keys PRAGMA, and the SQLITE_DBSTATUS_DEFERRED_FKS parameter
to sqlite3_db_status()) into the sessions branch.

FossilOrigin-Name: 8dfc0b78c38e519b64a796243ff7c0aff688ff36
2013-07-11 15:31:57 +00:00
drh 07001c45a1 Remove the undocumented sqlite3_foreign_key_check() API and replace it with
sqlite3_db_status(db, SQLITE_DBSTATUS_DEFERRED_FKS, ...).  Add test cases
for the new functionality.

FossilOrigin-Name: d39e65fe702a2e11477f2d996f77404f45763368
2013-07-11 13:49:59 +00:00
drh 963c74df59 Rename the SQLITE_DeferForeignKeys macro to shorter SQLITE_DeferFKs. Move
the "defer_foreign_keys" pragma into the flagPragma() routine.

FossilOrigin-Name: 3a2ab74c85a40e0e7ef2d6aef6ec23837cb788e6
2013-07-11 12:19:12 +00:00
drh 888e16e7c5 Pull in all the latest changes from trunk.
FossilOrigin-Name: af3ca4c6e557e6bc92584586b5a97d9be41b0b82
2013-07-09 13:05:49 +00:00
dan bded90b487 Fix a bug preventing some FK constraint checking from being deferred until the end of changeset application.
FossilOrigin-Name: 1452defb8cfcc489230314dd1e0425feba46c49d
2013-07-04 15:22:53 +00:00
dan cb3e4b797e Experimental change to the handling of foreign key constraint violations when applying a changeset: all foreign keys, immediate and deferred, are deferred until the end of the transaction (or sub-transaction) opened by the sqlite3changeset_apply(). A single call to the conflict-handler (if any) is made if any FK constraint violations are still present in the database at this point. The conflict-handler may choose to rollback the changeset, or to apply it, constraint violations and all.
FossilOrigin-Name: 1d44e5d3c2b1dc958442f9114a960b256e002ed3
2013-07-03 19:53:05 +00:00
dan 35e2858e98 Fixes for the sessions module so that it works with sqlite3_extended_error_codes() set.
FossilOrigin-Name: c2972b6aed23f6c76a289534de9ea4732a48f40e
2013-07-02 20:23:40 +00:00
drh fd64cedc4b Bring the sessions branch up-to-date with all the latest trunk changes.
FossilOrigin-Name: 086a127236ee99d67513490fb7b5549e8b752c44
2013-06-26 13:31:50 +00:00
drh 85b303df54 Update the sessions branch to the official 3.7.17 release code.
FossilOrigin-Name: fc88f7311ba888015634b89b5909890208f543f5
2013-05-20 13:01:19 +00:00
drh 466af9baae Update the sessions branch to the latest 3.7.17 beta.
FossilOrigin-Name: af8057ef5dcaa72dc0d2ab5d1553b06757449fa1
2013-05-17 10:58:08 +00:00
drh ad1818d9ed Update the sessions branch with the latest trunk changes.
FossilOrigin-Name: d09355050a74344c1cb6d303af9f601cd41e2368
2013-05-15 18:45:14 +00:00
drh 644abec3a4 Merge trunk changes into the sessions branch.
FossilOrigin-Name: 512f8a1ef8c4cf50723cfc4a78b7370dc37358d9
2013-05-09 23:40:58 +00:00
drh 2464dee6bc Merge all recent trunk changes into the sessions branch.
FossilOrigin-Name: ae6c4a0906ad8caabd8c605bb39c5fb979ab39a2
2013-05-08 18:50:04 +00:00
drh ec7ecb8c9a Merge all recent trunk changes into the sessions branch.
FossilOrigin-Name: 3879ab1b532828fcc12a50a95b6730faebcb69e9
2013-05-03 18:29:22 +00:00
drh 16fb176814 Merge the latest trunk changes into the sessions branch.
FossilOrigin-Name: 6994826c0784280f2e9728dfa4185848846d03df
2013-04-22 23:59:06 +00:00
drh 8bc8bfcb05 Fix the xCheckReservedLock() method on the windows VFS so that it cannot return a false positive when two or more processes use it at the same time on the same file. Ticket [7ff3120e4fa54abb55]. Update to version 3.7.16.2.
FossilOrigin-Name: 67b3c0efa7d5e0cb7cc0fc7606ab3f26ea5419fd
2013-04-12 13:53:50 +00:00
drh 226fb534dd Version 3.7.16.2
FossilOrigin-Name: cbea02d93865ce0e06789db95fd9168ebac970c7
2013-04-12 11:52:43 +00:00
drh 7f6e147a51 Increase the version number to 3.7.16.2 and cherrypick the
fix for ticket [7ff3120e4fa54abb55].  This check-in is a release
candidate.

FossilOrigin-Name: 86f26f9152988e47d7e1cfcca9eedf5e6265a225
2013-04-11 19:04:20 +00:00
mistachkin 73c4041b43 Correct minor typo in two regular expressions.
FossilOrigin-Name: f1eed92b7b0ef4ee22a11d8bb4f9a572b56ce019
2013-04-11 00:45:28 +00:00
drh eeeee7f55d Designate the beginning and the end of the sqlite3session.h file in the
constructed sqlite3.h file.

FossilOrigin-Name: 53f5be74327162a2bd25a91eaf7c91b126294727
2013-04-10 15:01:36 +00:00
drh 8dd661aad1 Merge the changes for the 3.7.16.1 release candidate into the sessions branch.
FossilOrigin-Name: ee35a89712b8ea9f4e70d61a29150348896b519f
2013-03-28 01:19:26 +00:00
drh 1ffe7c7fab Also export the sqlite3changeset interfaces in the amalgamated version of
the sessions extension.

FossilOrigin-Name: 5b399f2bc43612c52ea2289001d852d7ba6f9750
2013-03-19 17:10:10 +00:00
drh e191e2c6d0 Include the sqlite3session.h header file in with sqlite3.h. Make sure
the session APIs are all public.

FossilOrigin-Name: ec490336bc6629d3910667ece1bbc4685a199546
2013-03-19 15:23:18 +00:00
drh c03edc0b62 Adjust the MSVC makefile so that it correctly handles the sessions
extension being included in the amalgamation.  Import the test case
changes that appeared in 3.7.16 final.

FossilOrigin-Name: 9bac09a99aef5f18386037b409869d760c520d53
2013-03-18 16:24:33 +00:00
mistachkin 38b4daa122 Fix typos in MSVC makefile.
FossilOrigin-Name: 2d15c2685750b930c5b7e2f06cb056b24a037967
2013-03-13 19:02:39 +00:00
drh 8ce2b0956a Fix main.mk so that it builds the amalgamation correctly.
FossilOrigin-Name: 0b84e277245f10f928c010ea5958923681c6d582
2013-03-13 12:20:15 +00:00
drh 498dcae0f0 Fix a compiler warning in sqlite3VdbePreUpdateHook().
Add sqlite3session.c to the amalgamation.
Fix the Makefile.in to work with sessions.

FossilOrigin-Name: e54b0225f226a163ec874df7d4b738efba12b3a7
2013-03-13 11:42:00 +00:00
dan bfb5a031e5 Merge latest trunk changes into this branch.
FossilOrigin-Name: d6cd3c780c6bc718d37e0f0b884e3e9a423d57be
2013-03-13 06:34:51 +00:00
dan 1bc8b87e46 Merge recent changes to trunk into sessions branch.
FossilOrigin-Name: 62adb0e0d70e619becb68ffd2625d979bcc777cd
2013-03-12 11:38:59 +00:00
drh b05567ed4b Merge all the latest trunk changes into the sessions branch.
FossilOrigin-Name: d69d21903b883067c66dbcdefe6b369a6fe328b0
2013-03-06 02:12:18 +00:00
drh dab942860c Enhancements to the query planner to make use of indices for ORDER BY even
when IN constraints are in the WHERE clause.  Add extended error codes for
all SQLITE_CONSTRAINT errors.

FossilOrigin-Name: 7e14dc734d7d941da5c3aa6612e26d93b5b929bd
2013-02-13 13:42:50 +00:00
drh 3dbfbcf43e Enhancements to the query planner to exploit transitive relationships in the
WHERE clause, and other minor changes to bring the sessions branch into
alignment with the trunk.

FossilOrigin-Name: 82d3d1ae824e1fbc7958657be79231590ec17ace
2013-01-25 02:10:06 +00:00
drh e6dcfb85df Merge latest trunk changes into the sessions branch, especially the
ORDER BY bug fix of 3.7.15.2.

FossilOrigin-Name: 34af6fac679aeb18ab8349f74e95f3cb6e722ea4
2013-01-09 14:49:37 +00:00
drh 89ded43b36 Merge recent trunk changes into the sessions branch.
FossilOrigin-Name: 7e068e39b3b31364271664e0afb1cd95a235c26f
2013-01-03 22:22:55 +00:00
drh bd36911f4d Pull the latest trunk changes into the sessions branch, and in particular
the collating-sequence refactorization.

FossilOrigin-Name: 4f6d69ae94671df92b976525f75404c01270fef9
2012-12-08 23:37:22 +00:00
drh 26cd614dad Update the sessions branch to include the SQLLOG enhancement, the
SQLITE_IOERR_DELETE_NOENT fix, and a fix for the number-of-documents
bug in FTS4.

FossilOrigin-Name: ba8d08b67021a32fda069c18b7eb93523e6f0d1f
2012-11-27 21:56:28 +00:00
drh afe5b83dcd Merge the latest changes from trunk: chiefly the outer/inner loop query
optimizer scoring enhancement and the INSTR() function.

FossilOrigin-Name: 2993ca20207f8dac02f58d01e31d68c84328356a
2012-11-10 01:27:59 +00:00
drh 3dc2b9032f Pull all the latest trunk enhancements into the sessions branch.
FossilOrigin-Name: fce667f2d93a4ba65ccf1e748469576a3cd7ffcc
2012-10-30 21:03:48 +00:00
drh 036a71a97c Merge all the latest core changes into the sessions branch.
FossilOrigin-Name: 76767d651fe733614b1719aef9ad5ed4568234e4
2012-10-15 14:25:16 +00:00
drh d1102b1ec7 Merge the latest trunk changes, especially the ORDER BY optimizer enhancements
but also other fixes, onto the sessions branch.

FossilOrigin-Name: f1fbb8c5bfa84e84e0b8e2872d83b06a0c0d5acc
2012-10-09 14:58:15 +00:00
drh c4645dacfb Merge the latest trunk changes (especially "PRAGMA busy_timeout" and the
ORDER BY query planner optimizations) into the sessions branch.

FossilOrigin-Name: 6ca8eae1f89d19ee23cbc3a869d85b57d29b4a7d
2012-09-28 13:05:48 +00:00
drh 40eaa08620 Update the sessionfault-9.1 and -9.2 tests to account for the change in
version 3.7.11 in which a pending statement no longer blocks ROLLBACK but
instead causes the next call on that statement to return SQLITE_ABORT.

FossilOrigin-Name: fae9eb197fcef726fd2c7c701afe6805fc008cf9
2012-09-28 12:55:56 +00:00
dan 7417cefec6 Update with latest trunk changes.
FossilOrigin-Name: 87995dc9409482f0a7a367bfc51d78ac0f63b8c3
2012-08-31 14:23:16 +00:00
drh 310874ca00 Merge the latest trunk changes into the sessions branch.
FossilOrigin-Name: aa62d6881b5aae64e15dbae70ce8e77bcc2a9f0b
2012-08-25 01:21:36 +00:00
drh 16de7f4d32 Merge all the latest trunk changes into the sessions branch.
FossilOrigin-Name: fc07a4795e027108674d1d41eb4350df629ddc8b
2012-08-15 16:21:55 +00:00
dan 618fea4830 Merge latest trunk changes with sessions branch.
FossilOrigin-Name: 47f8f0711d63ef4166db9115db44b166959df14c
2012-06-09 19:15:43 +00:00
drh 8973a04e3b Merge all trunk changes, including the WinRT enhancements, into the
sessions branch.

FossilOrigin-Name: c62140398344fd1a68a3e6dfe8b4fab280731ebb
2012-06-07 23:58:36 +00:00
drh c706edac44 Merge the 64-to-32-bit RTree rounding fixes from trunk into the sessions branch.
FossilOrigin-Name: 7eff45c28058fd1cb06dfd7f253cf2e4866e8c7c
2012-05-29 00:48:55 +00:00
drh 14bcd7d25e Merge the unicode61 tokenizer and the shared-cache-memory database changes
into the sessions branch.

FossilOrigin-Name: df817e70afc3f41e680d8f84dfa5772d5b3ae4d9
2012-05-28 18:22:41 +00:00
drh e56467fc9f Version 3.7.12.1
FossilOrigin-Name: d07b7b67d1b3bf65cfe8d96d45a7f1d387bea7ce
2012-05-22 13:01:39 +00:00
drh 7b9370a445 Version 3.7.12
FossilOrigin-Name: be71d2f6678c5dd8a165a67ef6d3f64678227260
2012-05-14 01:58:36 +00:00
dan 3581020e53 Update sessions branch with trunk changes.
FossilOrigin-Name: 3f4848a871a4229ff6951eefaf7673de46d2c43f
2012-05-12 11:50:41 +00:00
drh caee673b0d Merge in the windows AV-defense enhancements for open() and the table
constraint parser fixes for legacy schemas, all from trunk.

FossilOrigin-Name: 323570b8bd52c7e1b0c8c7a0e4f57f6fdebead11
2012-05-10 12:17:18 +00:00
drh 0d7706f41a Update the sessions branch with all the latest changes from trunk.
FossilOrigin-Name: 0f347fbfc7d8821f56f1ae0d1a9472a375631e65
2012-05-04 23:31:14 +00:00
drh efa50bdac1 Merge the latest trunk changes into the sessions branch.
FossilOrigin-Name: 2b7a91e61794ce9ffe210bad584454e52c79924e
2012-04-19 20:00:31 +00:00
drh bd91913dc0 Import all the latest trunk changes into the sessions branch.
FossilOrigin-Name: 87a0eab5d98fff93aa2147c04c4af27be42fb365
2012-04-18 01:41:37 +00:00
drh 108606837e Merge all recent trunk changes into the sessions branch.
FossilOrigin-Name: fb9b9987de965e194fef56bca563ee65c49f0950
2012-03-30 17:30:33 +00:00
dan 6d972c1b98 Merge the latest trunk changes into the sessions branch.
FossilOrigin-Name: 2277c70b6f14e2f8043112a5c49a8bc18b7504f3
2012-03-19 11:17:17 +00:00
drh ddf01aeb16 Merge in the latest trunk changes.
FossilOrigin-Name: ff86875ca35e04cea6c3d5e1b5117a4f227a6b15
2012-03-05 16:26:00 +00:00
drh fdf1795618 Pull all the latest trunk changes into the sessions branch.
FossilOrigin-Name: 361fb66a799f4f253e61ca94d999accde2c75b2c
2012-02-10 17:54:52 +00:00
mistachkin adb96a62e5 Support building with SQLITE_ENABLE_SESSION under MSVC.
FossilOrigin-Name: 2845654d425164de143e82b9fdb255d81a01af56
2012-01-16 12:33:36 +00:00
dan af49af7f9e Update sessions branch with latest changes from trunk.
FossilOrigin-Name: 01c84fd391a0ca1f5245c7eff0644d0cc6cff86b
2012-01-14 13:50:12 +00:00
drh 54cced185c Merge all of the latest trunk changes into the sessions branch.
FossilOrigin-Name: a9bcb432f58b96f079a73c456efd4851c582221e
2012-01-05 13:02:36 +00:00
drh f004e1d69e Merge in all changes found in the version 3.7.9 release candidate.
FossilOrigin-Name: 23580718e1c15ddb89682d0e7566da4d7276bfe9
2011-10-31 14:34:31 +00:00
drh efb1ca3738 Merge the latest trunk enhancements into the sessions branch.
FossilOrigin-Name: 8baef58170ff851d0c4387a6888f59b487b4f33c
2011-10-21 17:08:23 +00:00
drh 29c21c77af Merge all the latest trunk changes into the sessions branch - especially
the SQLITE_ENABLE_STAT3 enhancements.

FossilOrigin-Name: 403431cac6b039b0693915c5422f08dc60dae230
2011-10-11 12:58:38 +00:00
drh 946e610a84 Merge in all trunk changes through the version 3.7.8 release.
FossilOrigin-Name: 98619a23fd11a5eb319687adfdacc91ff2db896c
2011-09-19 20:28:07 +00:00
drh 063d96fc18 Merge the latest trunk changes into the sessions branch.
FossilOrigin-Name: 5efb02949dbeabccfe1d848d275529f03f3dfc84
2011-09-16 19:40:24 +00:00
dan 93bca69578 Merge latest changes from the trunk into the sessions branch.
FossilOrigin-Name: c00e45ede7cbf71a3a6d1ccad0b9275010ca8493
2011-09-14 19:41:44 +00:00
drh 7cca2e6259 Merge the latest trunk changes into the sessions branch.
FossilOrigin-Name: eb036d6f81e15bac013316bf5b1b2ba3e0bd4605
2011-08-26 19:20:47 +00:00
drh 0e9a916c36 Merge the latest trunk changes into the sessions branch.
FossilOrigin-Name: c5709036087b6b4fb6391ab7e4b4b870aac87a31
2011-08-03 21:46:58 +00:00
drh 7f197bb7b0 Avoid segfaulting on an incremental blob write if SQLITE_ENABLE_PREUPDATE_HOOK
set but no pre-update hooks are defined.

FossilOrigin-Name: 62c446340475b52b843c0345eda32ebe86fa9159
2011-08-03 21:32:11 +00:00
dan 964cbd469b Fix errors in a comment in sqlite3session.c. No changes to code.
FossilOrigin-Name: d5f4c4c0bf0d488db11c6ab0d26ca3554a57d0ff
2011-07-26 17:53:53 +00:00
dan 1e556c3fe5 Fix a test case in sessionfault.test. No changes to code.
FossilOrigin-Name: dc6ecacd7724089a0387e69a0fd5cdbfdd2023f8
2011-07-26 15:57:56 +00:00
dan cfec7eee20 Fix a problem causing sqlite3changeset_invert() to effectively drop UPDATE changes.
FossilOrigin-Name: bb3e65d9724dcecdc54b4c9fb0448f95d14495ff
2011-07-26 15:50:36 +00:00
drh 8a3bb3fddd Merge the latest trunk changes into the sessions branch.
FossilOrigin-Name: 110cfd6920cf3011aeaf7e586f8db867bfc69fbb
2011-07-22 12:49:27 +00:00
dan ff53032641 Fix the sqlite3session_isempty() method so that it returns, as documented, non-zero when no changes have been recorded by the session object.
FossilOrigin-Name: d04e0fd82a15aee963e35830caf8159b4b6ccd87
2011-07-18 15:22:56 +00:00
dan a93166e030 Fix error in a comment in sqlite3session.h. No changes to code.
FossilOrigin-Name: 70c84e50209722a61d66fd737e42d49275745b62
2011-07-16 18:35:53 +00:00
dan b69ec3482a Note in the documentation that when iterating through a changeset, all changes to a single table are grouped together. Also add the sqlite3session_isempty() function.
FossilOrigin-Name: 364f3b820a26f9b15cf74a0222ed5e302becc54f
2011-07-16 18:05:07 +00:00
dan 4f52804206 Add a few casts required by 64-bit VS2010 to the sessions code.
FossilOrigin-Name: 5ac4a06111b5fad5f58c20ef5d2b65aeb23e105a
2011-07-15 19:11:32 +00:00
dan 40368988da Add the xFilter callback to the sqlite3changeset_apply() function. This callback allows the application to accept or reject changes on a per-table basis when applying a changeset.
FossilOrigin-Name: 282474c42f24f0e66c69b576b72ef8ce764d49e2
2011-07-13 15:21:02 +00:00
dan e437ca5ec0 Modifications so that the sessions extension works with blob handles.
FossilOrigin-Name: 82ac16c4f873d3bd7c22f36ba7b974b4903a2d50
2011-07-11 19:45:38 +00:00
drh 88ab69f614 Pull the latest version 3.7.7 release-candidate changes into the
sessions branch.

FossilOrigin-Name: 840bf9c2d92192ee3cc2aa7c0e9bdb805a066fd4
2011-06-23 17:40:15 +00:00
drh 893ab7ccba Pull the latest trunk changes into the sessions branch.
FossilOrigin-Name: 5d95b42946b5cf0346164aebe0a8c4f37527bc31
2011-06-23 02:11:11 +00:00
dan 53ae686bfb Update sessions branch with latest trunk changes.
FossilOrigin-Name: 48d5cab3ac08b25ec94d6ecfd25779320558110f
2011-06-22 12:01:08 +00:00
drh 34ffcfff02 Merge the latest trunk changes into the sessions branch.
FossilOrigin-Name: f3d148e0d10e16cd744ec88a2961fd617368c5ed
2011-06-21 01:29:16 +00:00
dan 467eb01caa (no comment)
FossilOrigin-Name: 699b884383f735fb9630d87d25dab61c97dc4485
2011-06-20 11:17:11 +00:00
drh 8863f35f5c Merge the latest trunk changes into the sessions branch.
FossilOrigin-Name: 4c5e276c902e0b93cfc05bf2e1db966ecdac0ed0
2011-06-20 10:44:10 +00:00
drh bf0ed47d82 Merge the latest trunk changes into the sessions branch.
FossilOrigin-Name: 832886b1e6edb916d9824924c7d88202f4eb1969
2011-05-30 13:39:20 +00:00
drh 4ec233f12a Merge all the latest trunk changes into the sessions branch, especially
the disappearing WAL transaction fix.

FossilOrigin-Name: 5b1b536cf828850d0e8ac2ab08e8696082715877
2011-05-19 02:48:46 +00:00
drh 0576a30dad Merge the latest trunk changes into the sessions branch.
FossilOrigin-Name: 6883580e6c8973010a42d1d2c5bde04c6b2f4eb7
2011-05-05 15:46:16 +00:00
dan 23328bedf0 Merge trunk changes into sessions branch.
FossilOrigin-Name: b91b4c31fe311b292044c9c747feba294ffce25c
2011-04-18 17:30:56 +00:00
dan 77fc1d5bb4 Fix further missing comments and other minor issues in the session module code.
FossilOrigin-Name: 99f0f35092b0b78b7016b21c242da263ab64b77b
2011-04-18 15:47:08 +00:00
dan 798693b2b1 Fix some missing comments and other issues with session module code.
FossilOrigin-Name: 20d7c280235201e519f296372f269e7cecda24da
2011-04-18 12:05:03 +00:00
dan 80fe2d9310 Further coverage tests for the session module.
FossilOrigin-Name: 69a01c708bf044eacf21a8951fe9e7d9fb4332c5
2011-04-18 07:36:27 +00:00
dan 6734007d75 Improve test coverage of session module.
FossilOrigin-Name: f46d4b641d613c39a80b12106e6a6ac0efc8be83
2011-04-16 19:23:10 +00:00
dan b08a1efae5 Improve coverage of session module.
FossilOrigin-Name: 3dfd1d63bddfa9bd9018eb00bee1d496379630b5
2011-04-15 19:18:57 +00:00
dan 1756ae10e1 Add tests and fixes for OOM handling in sqlite3changeset_concat().
FossilOrigin-Name: df0b2d21dcd81679e55b24866f97568019932983
2011-04-15 16:03:32 +00:00
dan f29123b572 Have sqlite3changeset_concat() return SQLITE_SCHEMA if an attempt is made to concatenate changesets based on incompatible database schemas.
FossilOrigin-Name: 343b64517d244b75097e38342dc273eb5a52915b
2011-04-15 15:04:31 +00:00
dan 29e03e977c Add documentation for sqlite3changeset_concat() to sqlite3session.h.
FossilOrigin-Name: ada9efa53a6ea55f89d237cfd530f1d180343e19
2011-04-15 12:04:50 +00:00
dan 6cda207f5f Add further tests for the sqlite3changeset_concat() function. Also fixes.
FossilOrigin-Name: 1fc3f15d88c160b45642b46d1d54c591af058ba2
2011-04-14 18:01:41 +00:00
dan 5d607a6e06 Start adding the sqlite3changeset_concat() function to the session module.
FossilOrigin-Name: 8927b2260b8d84f53776cb29e1d2fa41b6b0de0e
2011-04-14 11:16:21 +00:00
drh 9e3fbc0157 Merge the latest trunk changes into the sessions branch.
FossilOrigin-Name: 83705e90a54bad462a5b7fbca70cc129998f871c
2011-04-09 18:07:51 +00:00
drh 36828bd9bb Add a missing "extern C" terminator to the end of sqlite3session.h.
FossilOrigin-Name: 29090b695a95feaba1b74f9894997083a060263a
2011-04-06 23:40:28 +00:00
drh cfdbde214b Fix VC++ compiler warnings.
FossilOrigin-Name: 7b7c8d366c51e89aefc6efc9dcffe3f62c7e1d25
2011-04-06 23:39:28 +00:00
drh 22e9c155ae Merge in the latest changes from the trunk.
FossilOrigin-Name: 435b57dc2be7b071270a6cddece297758b8153aa
2011-04-06 22:33:50 +00:00
drh d7162fa64e Merge the latest trunk changes into the sessions branch.
FossilOrigin-Name: 45f20261720dcd73eb887f7e3df100723000418b
2011-04-05 22:13:49 +00:00
drh c94f31236e Pull the latest trunk changes (and hence the schema-parse-refactor changes)
into the sessions branch.

FossilOrigin-Name: 03ca83422f0259ae5525aeecc14171811252a67d
2011-04-05 13:27:30 +00:00
drh b92fce1eae Merge the latest changes of trunk into the session branch.
FossilOrigin-Name: 95d53c44320b9639f2623aa9cc88d0d3e1a3be8f
2011-04-04 13:19:36 +00:00
dan 3d06237770 Merge latest trunk changes.
FossilOrigin-Name: d184cf00574b52b93f1f089025d6a0addca57558
2011-04-01 15:43:40 +00:00
dan 5d8a298495 If the sessions module is being built as part of the amalgamation, do not try to include sqliteInt.h and vdbeInt.h.
FossilOrigin-Name: f87bfe6e12093e5958b658a49d3766fb29562cbb
2011-04-01 15:30:39 +00:00
drh 9b1c62d416 Add the SQLITE_ENABLE_PREUPDATE_HOOK compile-time option.
FossilOrigin-Name: 6634521461e6acff7cc778590e62d57831f9230d
2011-03-30 21:04:43 +00:00
drh 67c495ac20 Disable the truncate optimization if there is a preupdate hook.
FossilOrigin-Name: d051694e024c590e4687cae1368f900799b7442d
2011-03-30 17:25:35 +00:00
drh 930e1b6a5d Add documentation to the sqlite3_preupdate_hook() interface and its relatives.
FossilOrigin-Name: 8180f2881fa970429e28d4f3258a56cfaabbcabd
2011-03-30 17:07:47 +00:00
drh 5847b5f9c3 Merge in all the latest changes from trunk.
FossilOrigin-Name: b11d941e92897663da46160185e6e305d4e28fe6
2011-03-30 02:03:12 +00:00
dan e5754eec26 Improve coverage of session module a bit more.
FossilOrigin-Name: 4255a9f609c4fd43582a0874143eabe211199726
2011-03-25 19:06:09 +00:00
dan f51e5f6c99 Improve coverage of session module code.
FossilOrigin-Name: 666123c8d07be87d477e67b1cebef2b0fba5b4bc
2011-03-25 10:52:01 +00:00
dan a9605b9125 Fix handling of schema changes mid-session.
FossilOrigin-Name: 76d2d2ad3b2a5171393b7894f35f463ff284e53b
2011-03-24 16:53:57 +00:00
dan ca62ad57f0 Fix handling of schema mismatches in sqlite3session.c so that it matches the docs in sqlite3session.h.
FossilOrigin-Name: 506a0d7a710e1ff2f367821e73f5080fcf63fbc5
2011-03-24 16:04:54 +00:00
dan 244593c846 Store primary key definitions for modified tables in changesets. Add the sqlite3changeset_pk() API to extract this data from a changeset iterator.
FossilOrigin-Name: 54298ee5ed183d1f1c49524f25e8ae1407f3d4b5
2011-03-24 11:22:59 +00:00
drh 9a48bf528d Merge in all the latest changes from the trunk, and especially the interface
changes to the SystemCall methods of the VFS.

FossilOrigin-Name: 9c3a6e479988e96086bef00c79dbce508a14da0d
2011-03-23 22:48:46 +00:00
dan b4480e942f Add the "indirect flag" to the changeset blob format. Also the sqlite3session_indirect() API.
FossilOrigin-Name: 1feaf2d35fd9ec777319717ae2c2929d66fe7baa
2011-03-23 16:03:11 +00:00
dan 1e7a2d4315 Add API function sqlite3_preupdate_depth(), for determining the depth of the trigger stack from within a pre-update callback.
FossilOrigin-Name: bdea70895c2c686a4dd3f4bf0a475fd1501d9551
2011-03-22 18:45:29 +00:00
dan 245b49b203 Fix a crash that can follow an OOM when "all tables" are registered with a session module.
FossilOrigin-Name: 183c236e991faaabdc768e52e926c52cf4a7abc9
2011-03-22 16:54:12 +00:00
dan ff4d0f41bb If a NULL pointer is passed to sqlite3session_attach() in place of a table name, attach all database tables to the session object.
FossilOrigin-Name: e9037e4e4ccaa5c633759c4d041b60b631b92c6c
2011-03-22 15:21:03 +00:00
dan 7aa469cdd3 Add OOM tests and related fixes for the session module.
FossilOrigin-Name: 06048a68b351e3eb15a890cb54db8a1d4b345fbc
2011-03-22 12:08:00 +00:00
shaneh 157546f4df Fix a couple typos for consistency in sessions documentation.
FossilOrigin-Name: 510198f171b9f77a4ad49c06c978c5fbb3a5b7f9
2011-03-22 02:03:23 +00:00
dan db04571c79 Remove some unreachable code in sqlite3session.c. Add test cases.
FossilOrigin-Name: 39cdfa5324ae91bfbbac733b1e3e2d33ca883340
2011-03-21 19:41:29 +00:00
drh 8cc9c1aa15 Merge in the sqlite3_db_config() enhancements for enabling and disabling
FKs and triggers from trunk.

FossilOrigin-Name: 2b3c8b9d9aa909f64a29ed8167de24c328c50d85
2011-03-21 17:17:49 +00:00
dan 12ca0b5695 Add start of fault-injection tests for session module. Fix some bugs related to the same.
FossilOrigin-Name: 32e95164d1192b87b1ab019549fd2394642cd3fe
2011-03-21 16:17:42 +00:00
dan 27453faef8 Clarify handling of NULL values in PK columns in sqlite3session.h. Add tests and fixes for the same.
FossilOrigin-Name: aed4273054cbd150c86b36ea951d17c981633ba0
2011-03-21 11:55:06 +00:00
dan 4e895da185 Fix session module problems with real (floating point) values.
FossilOrigin-Name: a192d04f4e3a9e4960a4d96d1d3ee8635bc1034d
2011-03-21 11:03:24 +00:00
dan 77e6500473 Fix some typos in sqlite3session.h.
FossilOrigin-Name: 0853e530cc8d96f025f5160540e8ab3243dea11b
2011-03-21 07:23:09 +00:00
dan a2df3d9f95 Documentation format updates to sqlite3session.h.
FossilOrigin-Name: f227f60210fba3930f3050aebb1facee8bac9c0b
2011-03-20 11:20:41 +00:00
dan d7fb7d24c6 Fix a problem involving session objects and attached databases.
FossilOrigin-Name: ad91d30073a8faa7eb064dd2e1cc4d2297d7b3f8
2011-03-19 19:19:26 +00:00
dan 1f34f8cc71 Fix a bug in changeset generation code.
FossilOrigin-Name: 825df75ba453c853953e17ec29653e11c46f92bb
2011-03-19 18:46:15 +00:00
dan 4565faa990 Move session1.test from test/ to ext/session/.
FossilOrigin-Name: c4436a936ab302aec3b7f41a4552f69ad5815744
2011-03-19 17:07:57 +00:00
dan 4131639c63 Fix a problem with resizing a hash table in sqlite3session.c.
FossilOrigin-Name: 6e5907e14d3316d56313243c4f8ce8f14d0858fc
2011-03-19 16:26:11 +00:00
dan 7cf7df7d35 Fix some bugs in sqlite3changeset_apply().
FossilOrigin-Name: 7250318dda542c5bbf28852c1f1d0f3c52ae8f96
2011-03-19 15:37:02 +00:00
dan 319eeb7b33 Fix a problem with INTEGER PRIMARY KEY columns and the pre-update hook.
FossilOrigin-Name: 24d4d5dd007197a141555bcca6f2ac9ab47cde80
2011-03-19 08:38:50 +00:00
drh 516ae18005 Merge the fix to ticket [f7b4edece25c99485] into the sessions branch.
FossilOrigin-Name: 1b736ac2934f2361dee5062e9033cbf26b41fc3a
2011-03-19 02:37:38 +00:00
dan 4c22025225 Hold the database mutex for the duration of an sqlite3changeset_apply() call. Also for the duration of all sqlite3session_xxx() calls.
FossilOrigin-Name: c615c38c3283e21c33550c093099a793761123a7
2011-03-18 18:03:13 +00:00
drh 304637c026 Fixes for compiler warnings. Minor code cleanup.
FossilOrigin-Name: 9604d13001e0a195718c7f03fc0e73e352226caa
2011-03-18 16:47:27 +00:00
dan 2635a3be20 Further improvements to documentation in sqlite3session.h.
FossilOrigin-Name: 07019bb9e8d8f2445d1e0342f74ab520e9804cb5
2011-03-18 16:13:53 +00:00
drh ea022cffdd Fix a memory allocation issues in the preupdate hook so that the hook.test
script runs clean in valgrind.

FossilOrigin-Name: bd94f4c8b1c4590c437030f0122015f1a37e2582
2011-03-18 15:13:31 +00:00
dan c21111daff Update comments in sqlite3session.h. More to come.
FossilOrigin-Name: e73e9082f3b14088752717193a10dd7657deb8af
2011-03-18 13:05:15 +00:00
drh 0639c34ecd Merge all the latest trunk enhancements into the sessions branch.
FossilOrigin-Name: 94fd5bb6da5ef4d850c2ed4ad38afabc5569dae6
2011-03-18 12:35:36 +00:00
dan e8d5648e16 Change to the session module to use user-defined primary keys instead of rowids when collecting changes.
FossilOrigin-Name: 6614cfcb9c41da71ddec3c44a3de0d4d849e1cdd
2011-03-17 19:20:27 +00:00
dan 37db03bf73 Add the sqlite3_preupdate_new() API, for retrieving the new.* values from within a pre-update callback.
FossilOrigin-Name: 526545c49f64d9063d1b888cfc14ece62fa3c13c
2011-03-16 19:59:18 +00:00
dan 6566ebe1b6 Remove the sqlite3_transaction_hook() API.
FossilOrigin-Name: b0015a1cfe63c924ee5f250aa08460522882009b
2011-03-16 09:49:14 +00:00
dan 296c76589f Fix some bugs and other code issues in the session module.
FossilOrigin-Name: f2930840e4af3d7d9cb199d316502932fcbbb867
2011-03-15 16:37:27 +00:00
dan 0c698471d1 Fix handling of return values from the conflict handler. Document the conflict handler arguments and return codes in sqlite3session.h.
FossilOrigin-Name: cbbb274e500237dbf7155a51d4f9c17583d704ea
2011-03-14 19:49:23 +00:00
dan 37f133ec31 Fix some issues with UPDATE changes in the session module.
FossilOrigin-Name: 57862efe718fdc93401998f9058511292a0e1a50
2011-03-12 17:22:46 +00:00
dan d5f0767c9c Add the sqlite3changeset_apply() function. Does not yet handle all cases.
FossilOrigin-Name: 2b19be7bf753c7dd12e1c3b384981a3ea1bc8145
2011-03-11 19:05:52 +00:00
dan 91ddd5595b Add a function to the session extension invert a changeset.
FossilOrigin-Name: 75d5dff725dbb66d67d56ad042926f1daae56dbe
2011-03-09 11:17:05 +00:00
dan 4fccf43aae Add start of sessions feature.
FossilOrigin-Name: 269a81a37d7dbdcdec3c2322074916af0fbac91c
2011-03-08 19:22:50 +00:00
dan 21e8d0126d Add the experimental sqlite3_transaction_hook() API.
FossilOrigin-Name: 093d8cd8e2f3a6af5d40cf810e396f4919eb5cef
2011-03-03 20:05:59 +00:00
dan 46c47d4677 Add the experimental sqlite3_preupdate_hook() API.
FossilOrigin-Name: 6145d7b89f83500318713779c60f79a7ab2098ba
2011-03-01 18:42:07 +00:00
101 changed files with 12893 additions and 2538 deletions
+21 -8
View File
@@ -30,7 +30,8 @@ BCC = @BUILD_CC@ @BUILD_CFLAGS@
#
CC = @CC@
CFLAGS = @CPPFLAGS@ @CFLAGS@
TCC = $(CC) $(CFLAGS) -I. -I${TOP}/src -I${TOP}/ext/rtree -I${TOP}/ext/fts3
TCC = ${CC} ${CFLAGS} -I. -I${TOP}/src -I${TOP}/ext/rtree -I${TOP}/ext/icu
TCC += -I${TOP}/ext/fts3 -I${TOP}/ext/async -I${TOP}/ext/session
# Define this for the autoconf-based build, so that the code knows it can
# include the generated config.h
@@ -80,6 +81,7 @@ TEMP_STORE = -DSQLITE_TEMP_STORE=@TEMP_STORE@
# The same set of OMIT and ENABLE flags should be passed to the
# LEMON parser generator and the mkkeywordhash tool as well.
OPT_FEATURE_FLAGS = @OPT_FEATURE_FLAGS@
OPT_FEATURE_FLAGS += -DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_PREUPDATE_HOOK
TCC += $(OPT_FEATURE_FLAGS)
@@ -180,7 +182,8 @@ LIBOBJS0 = alter.lo analyze.lo attach.lo auth.lo \
mutex.lo mutex_noop.lo mutex_unix.lo mutex_w32.lo \
notify.lo opcodes.lo os.lo os_unix.lo os_win.lo \
pager.lo parse.lo pcache.lo pcache1.lo pragma.lo prepare.lo printf.lo \
random.lo resolve.lo rowset.lo rtree.lo select.lo status.lo \
random.lo resolve.lo rowset.lo rtree.lo \
sqlite3session.lo select.lo status.lo \
table.lo threads.lo tokenize.lo trigger.lo \
update.lo util.lo vacuum.lo \
vdbe.lo vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo vdbesort.lo \
@@ -338,6 +341,10 @@ SRC += \
SRC += \
$(TOP)/ext/rtree/rtree.h \
$(TOP)/ext/rtree/rtree.c
SRC += \
$(TOP)/ext/session/sqlite3session.c \
$(TOP)/ext/session/sqlite3session.h
# Generated source code files
@@ -395,7 +402,8 @@ TESTSRC = \
$(TOP)/src/test_vfs.c \
$(TOP)/src/test_wsd.c \
$(TOP)/ext/fts3/fts3_term.c \
$(TOP)/ext/fts3/fts3_test.c
$(TOP)/ext/fts3/fts3_test.c \
$(TOP)/ext/session/test_session.c
# Statically linked extensions
#
@@ -456,7 +464,8 @@ TESTSRC2 = \
$(TOP)/ext/fts3/fts3_term.c \
$(TOP)/ext/fts3/fts3_tokenizer.c \
$(TOP)/ext/fts3/fts3_write.c \
$(TOP)/ext/async/sqlite3async.c
$(TOP)/ext/async/sqlite3async.c \
$(TOP)/ext/session/sqlite3session.c
# Header files used by all library source files.
#
@@ -536,9 +545,6 @@ sqlite3$(TEXE): $(TOP)/src/shell.c libsqlite3.la sqlite3.h
-o $@ $(TOP)/src/shell.c libsqlite3.la \
$(LIBREADLINE) $(TLIBS) -rpath "$(libdir)"
sqldiff$(EXE): $(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h
$(LTLINK) -o $@ $(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS)
mptester$(EXE): sqlite3.c $(TOP)/mptest/mptest.c
$(LTLINK) -o $@ -I. $(TOP)/mptest/mptest.c sqlite3.c \
$(TLIBS) -rpath "$(libdir)"
@@ -575,6 +581,7 @@ mptest: mptester$(EXE)
sqlite3.c: .target_source $(TOP)/tool/mksqlite3c.tcl
$(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl
cp tsrc/shell.c tsrc/sqlite3ext.h .
cp $(TOP)/ext/session/sqlite3session.h .
tclsqlite3.c: sqlite3.c
echo '#ifndef USE_SYSTEM_SQLITE' >tclsqlite3.c
@@ -926,6 +933,9 @@ fts3_write.lo: $(TOP)/ext/fts3/fts3_write.c $(HDR) $(EXTHDR)
rtree.lo: $(TOP)/ext/rtree/rtree.c $(HDR) $(EXTHDR)
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/rtree/rtree.c
sqlite3session.lo: $(TOP)/ext/session/sqlite3session.c $(HDR) $(EXTHDR)
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/session/sqlite3session.c
# Rules to build the 'testfixture' application.
#
@@ -1001,6 +1011,9 @@ showjournal$(TEXE): $(TOP)/tool/showjournal.c sqlite3.lo
showwal$(TEXE): $(TOP)/tool/showwal.c sqlite3.lo
$(LTLINK) -o $@ $(TOP)/tool/showwal.c sqlite3.lo $(TLIBS)
changeset$(TEXE): $(TOP)/ext/session/changeset.c sqlite3.lo
$(LTLINK) -o $@ $(TOP)/ext/session/changeset.c sqlite3.lo $(TLIBS)
rollback-test$(TEXE): $(TOP)/tool/rollback-test.c sqlite3.lo
$(LTLINK) -o $@ $(TOP)/tool/rollback-test.c sqlite3.lo $(TLIBS)
@@ -1082,7 +1095,7 @@ clean:
rm -f testfixture$(TEXE) test.db
rm -f LogEst$(TEXE) fts3view$(TEXE) rollback-test$(TEXE) showdb$(TEXE)
rm -f showjournal$(TEXE) showstat4$(TEXE) showwal$(TEXE) speedtest1$(TEXE)
rm -f wordcount$(TEXE)
rm -f wordcount$(TEXE) changeset$(TEXE)
rm -f sqlite3.dll sqlite3.lib sqlite3.exp sqlite3.def
rm -f sqlite3.c
rm -f sqlite3rc.h
+42 -17
View File
@@ -42,8 +42,8 @@ DYNAMIC_SHELL = 0
#
!IFNDEF NO_WARN
!IF $(USE_FULLWARN)!=0
NO_WARN = -wd4054 -wd4055 -wd4100 -wd4127 -wd4130 -wd4152 -wd4189 -wd4206
NO_WARN = $(NO_WARN) -wd4210 -wd4232 -wd4244 -wd4305 -wd4306 -wd4702 -wd4706
NO_WARN = -wd4054 -wd4055 -wd4100 -wd4127 -wd4152 -wd4189 -wd4206 -wd4210
NO_WARN = $(NO_WARN) -wd4232 -wd4244 -wd4305 -wd4306 -wd4702 -wd4706
!ENDIF
!ENDIF
@@ -446,6 +446,8 @@ TCC = $(TCC) -I$(TOP)\ext\fts3
RCC = $(RCC) -I$(TOP)\ext\fts3
TCC = $(TCC) -I$(TOP)\ext\rtree
RCC = $(RCC) -I$(TOP)\ext\rtree
TCC = $(TCC) -I$(TOP)\ext\session
RCC = $(RCC) -I$(TOP)\ext\session
!ENDIF
# The mksqlite3c.tcl script accepts some options on the command
@@ -453,13 +455,11 @@ RCC = $(RCC) -I$(TOP)\ext\rtree
# options are necessary in order to allow debugging symbols to
# work correctly with Visual Studio when using the amalgamation.
#
!IFNDEF MKSQLITE3C_ARGS
!IF $(DEBUG)>1
MKSQLITE3C_ARGS = --linemacros
!ELSE
MKSQLITE3C_ARGS =
!ENDIF
!ENDIF
# Define -DNDEBUG to compile without debugging (i.e., for production usage)
# Omitting the define will cause extra debugging code to be inserted and
@@ -610,6 +610,14 @@ RCC = $(RCC) -DSQLITE_TEMP_STORE=1
# The same set of OMIT and ENABLE flags should be passed to the
# LEMON parser generator and the mkkeywordhash tool as well.
# BEGIN standard options
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS3=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RTREE=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_SESSION=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_PREUPDATE_HOOK=1
# END standard options
# These are the required SQLite compilation options used when compiling for
# the Windows platform.
#
@@ -797,7 +805,8 @@ LIBOBJS0 = vdbe.lo parse.lo alter.lo analyze.lo attach.lo auth.lo \
mutex.lo mutex_noop.lo mutex_unix.lo mutex_w32.lo \
notify.lo opcodes.lo os.lo os_unix.lo os_win.lo \
pager.lo pcache.lo pcache1.lo pragma.lo prepare.lo printf.lo \
random.lo resolve.lo rowset.lo rtree.lo select.lo status.lo \
random.lo resolve.lo rowset.lo rtree.lo \
sqlite3session.lo select.lo status.lo \
table.lo threads.lo tokenize.lo trigger.lo \
update.lo util.lo vacuum.lo \
vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo vdbesort.lo \
@@ -963,7 +972,9 @@ SRC4 = \
$(TOP)\ext\icu\sqliteicu.h \
$(TOP)\ext\icu\icu.c \
$(TOP)\ext\rtree\rtree.h \
$(TOP)\ext\rtree\rtree.c
$(TOP)\ext\rtree\rtree.c \
$(TOP)\ext\session\sqlite3session.h \
$(TOP)\ext\session\sqlite3session.c
# Generated source code files
@@ -1024,7 +1035,8 @@ TESTSRC = \
$(TOP)\src\test_vfs.c \
$(TOP)\src\test_wsd.c \
$(TOP)\ext\fts3\fts3_term.c \
$(TOP)\ext\fts3\fts3_test.c
$(TOP)\ext\fts3\fts3_test.c \
$(TOP)\ext\session\test_session.c
# Statically linked extensions
#
@@ -1044,6 +1056,7 @@ TESTEXT = \
# Source code to the library files needed by the test fixture
# (non-amalgamation)
#
TESTSRC2 = \
$(TOP)\src\attach.c \
@@ -1089,7 +1102,14 @@ TESTSRC2 = \
$(TOP)\ext\fts3\fts3_unicode.c \
$(TOP)\ext\fts3\fts3_unicode2.c \
$(TOP)\ext\fts3\fts3_write.c \
$(TOP)\ext\async\sqlite3async.c
$(TOP)\ext\async\sqlite3async.c \
$(TOP)\ext\session\sqlite3session.c
# Source code to the library files needed by the test fixture
# (amalgamation)
#
TESTSRC3 =
# Header files used by all library source files.
#
@@ -1140,6 +1160,8 @@ EXTHDR = $(EXTHDR) \
$(TOP)\ext\icu\sqliteicu.h
EXTHDR = $(EXTHDR) \
$(TOP)\ext\rtree\sqlite3rtree.h
EXTHDR = $(EXTHDR) \
$(TOP)\ext\session\sqlite3session.h
# This is the default Makefile target. The objects listed here
# are what get build when you type just "make" with no arguments.
@@ -1156,15 +1178,12 @@ sqlite3.exe: $(TOP)\src\shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) sqlite3.h
$(LTLINK) $(SHELL_COMPILE_OPTS) $(READLINE_FLAGS) $(TOP)\src\shell.c \
/link /pdb:sqlite3sh.pdb $(LTLINKOPTS) $(SHELL_LINK_OPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS)
sqldiff.exe: $(TOP)\tool\sqldiff.c sqlite3.c sqlite3.h
$(LTLINK) $(TOP)\tool\sqldiff.c sqlite3.c
mptester.exe: $(TOP)\mptest\mptest.c $(SHELL_CORE_DEP) $(LIBRESOBJS) sqlite3.h
$(LTLINK) $(SHELL_COMPILE_OPTS) $(TOP)\mptest\mptest.c \
/link $(LTLINKOPTS) $(LTLIBPATHS) $(SHELL_LINK_OPTS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS)
MPTEST1 = mptester mptest.db $(TOP)\mptest\crash01.test --repeat 20
MPTEST2 = mptester mptest.db $(TOP)\mptest\multiwrite01.test --repeat 20
MPTEST1 = mptester mptest.db $(TOP)/mptest/crash01.test --repeat 20
MPTEST2 = mptester mptest.db $(TOP)/mptest/multiwrite01.test --repeat 20
mptest: mptester.exe
del /Q mptest.db 2>NUL
@@ -1200,6 +1219,7 @@ sqlite3.c: .target_source $(TOP)\tool\mksqlite3c.tcl
$(TCLSH_CMD) $(TOP)\tool\mksqlite3c.tcl $(MKSQLITE3C_ARGS)
copy tsrc\shell.c .
copy tsrc\sqlite3ext.h .
copy $(TOP)\ext\session\sqlite3session.h .
sqlite3-all.c: sqlite3.c $(TOP)\tool\split-sqlite3c.tcl
$(TCLSH_CMD) $(TOP)\tool\split-sqlite3c.tcl
@@ -1569,6 +1589,9 @@ fts3_write.lo: $(TOP)\ext\fts3\fts3_write.c $(HDR) $(EXTHDR)
rtree.lo: $(TOP)\ext\rtree\rtree.c $(HDR) $(EXTHDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) $(NO_WARN) -DSQLITE_CORE -c $(TOP)\ext\rtree\rtree.c
sqlite3session.lo: $(TOP)\ext\session\sqlite3sesion.c $(HDR) $(EXTHDR)
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)\ext\session\sqlite3session.c
# Rules to build the 'testfixture' application.
#
@@ -1582,7 +1605,7 @@ TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERVER=1 -DSQLITE_PRIVATE=""
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_CORE $(NO_WARN)
TESTFIXTURE_SRC0 = $(TESTEXT) $(TESTSRC2) $(SHELL_CORE_DEP)
TESTFIXTURE_SRC1 = $(TESTEXT) $(SQLITE3C)
TESTFIXTURE_SRC1 = $(TESTEXT) $(TESTSRC3) $(SQLITE3C)
!IF $(USE_AMALGAMATION)==0
TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC0)
!ELSE
@@ -1613,9 +1636,6 @@ queryplantest: testfixture.exe sqlite3.exe
test: testfixture.exe sqlite3.exe
.\testfixture.exe $(TOP)\test\veryquick.test
smoketest: testfixture.exe
.\testfixture.exe $(TOP)\test\main.test
sqlite3_analyzer.c: $(SQLITE3C) $(TOP)\src\test_stat.c $(TOP)\src\tclsqlite.c $(TOP)\tool\spaceanal.tcl
copy $(SQLITE3C) + $(TOP)\src\test_stat.c + $(TOP)\src\tclsqlite.c $@
echo static const char *tclsh_main_loop(void){ >> $@
@@ -1649,6 +1669,10 @@ showwal.exe: $(TOP)\tool\showwal.c $(SQLITE3C)
$(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
$(TOP)\tool\showwal.c $(SQLITE3C)
changeset.exe: $(TOP)\ext\session\changeset.c $(SQLITE3C)
$(LTLINK) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
$(TOP)\ext\session\changeset.c $(SQLITE3C)
fts3view.exe: $(TOP)\ext\fts3\tool\fts3view.c $(SQLITE3C)
$(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
$(TOP)\ext\fts3\tool\fts3view.c $(SQLITE3C)
@@ -1686,6 +1710,7 @@ clean:
del /Q testloadext.dll 2>NUL
del /Q testfixture.exe test.db 2>NUL
del /Q LogEst.exe fts3view.exe rollback-test.exe showdb.exe 2>NUL
del /Q changeset.exe 2>NUL
del /Q showjournal.exe showstat4.exe showwal.exe speedtest1.exe 2>NUL
del /Q mptester.exe wordcount.exe 2>NUL
del /Q sqlite3.exe sqlite3.dll sqlite3.def 2>NUL
+1 -1
View File
@@ -1 +1 @@
3.8.10
3.8.9
Vendored
+9 -9
View File
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.62 for sqlite 3.8.10.
# Generated by GNU Autoconf 2.62 for sqlite 3.8.9.
#
# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
# 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
@@ -743,8 +743,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
# Identity of this package.
PACKAGE_NAME='sqlite'
PACKAGE_TARNAME='sqlite'
PACKAGE_VERSION='3.8.10'
PACKAGE_STRING='sqlite 3.8.10'
PACKAGE_VERSION='3.8.9'
PACKAGE_STRING='sqlite 3.8.9'
PACKAGE_BUGREPORT=''
# Factoring default headers for most tests.
@@ -1481,7 +1481,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures sqlite 3.8.10 to adapt to many kinds of systems.
\`configure' configures sqlite 3.8.9 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1546,7 +1546,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of sqlite 3.8.10:";;
short | recursive ) echo "Configuration of sqlite 3.8.9:";;
esac
cat <<\_ACEOF
@@ -1660,7 +1660,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
sqlite configure 3.8.10
sqlite configure 3.8.9
generated by GNU Autoconf 2.62
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@@ -1674,7 +1674,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by sqlite $as_me 3.8.10, which was
It was created by sqlite $as_me 3.8.9, which was
generated by GNU Autoconf 2.62. Invocation command line was
$ $0 $@
@@ -13953,7 +13953,7 @@ exec 6>&1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by sqlite $as_me 3.8.10, which was
This file was extended by sqlite $as_me 3.8.9, which was
generated by GNU Autoconf 2.62. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -14006,7 +14006,7 @@ Report bugs to <bug-autoconf@gnu.org>."
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_version="\\
sqlite config.status 3.8.10
sqlite config.status 3.8.9
configured by $0, generated by GNU Autoconf 2.62,
with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
+3 -16
View File
@@ -313,13 +313,6 @@ static int fts3EvalStart(Fts3Cursor *pCsr);
static int fts3TermSegReaderCursor(
Fts3Cursor *, const char *, int, int, Fts3MultiSegReader **);
#ifndef SQLITE_AMALGAMATION
# if defined(SQLITE_DEBUG)
int sqlite3Fts3Always(int b) { assert( b ); return b; }
int sqlite3Fts3Never(int b) { assert( !b ); return b; }
# endif
#endif
/*
** Write a 64-bit variable-length integer to memory starting at p[0].
** The length of data written will be between 1 and FTS3_VARINT_MAX bytes.
@@ -429,7 +422,7 @@ void sqlite3Fts3Dequote(char *z){
/* If the first byte was a '[', then the close-quote character is a ']' */
if( quote=='[' ) quote = ']';
while( z[iIn] ){
while( ALWAYS(z[iIn]) ){
if( z[iIn]==quote ){
if( z[iIn+1]!=quote ) break;
z[iOut++] = quote;
@@ -1026,8 +1019,7 @@ static int fts3ContentColumns(
const char *zTbl, /* Name of content table */
const char ***pazCol, /* OUT: Malloc'd array of column names */
int *pnCol, /* OUT: Size of array *pazCol */
int *pnStr, /* OUT: Bytes of string content */
char **pzErr /* OUT: error message */
int *pnStr /* OUT: Bytes of string content */
){
int rc = SQLITE_OK; /* Return code */
char *zSql; /* "SELECT *" statement on zTbl */
@@ -1038,9 +1030,6 @@ static int fts3ContentColumns(
rc = SQLITE_NOMEM;
}else{
rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
if( rc!=SQLITE_OK ){
*pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
}
}
sqlite3_free(zSql);
@@ -1292,7 +1281,7 @@ static int fts3InitVtab(
if( nCol==0 ){
sqlite3_free((void*)aCol);
aCol = 0;
rc = fts3ContentColumns(db, argv[1], zContent,&aCol,&nCol,&nString,pzErr);
rc = fts3ContentColumns(db, argv[1], zContent, &aCol, &nCol, &nString);
/* If a languageid= option was specified, remove the language id
** column from the aCol[] array. */
@@ -3534,8 +3523,6 @@ static void fts3SnippetFunc(
}
if( !zEllipsis || !zEnd || !zStart ){
sqlite3_result_error_nomem(pContext);
}else if( nToken==0 ){
sqlite3_result_text(pContext, "", -1, SQLITE_STATIC);
}else if( SQLITE_OK==fts3CursorSeek(pContext, pCsr) ){
sqlite3Fts3Snippet(pContext, pCsr, zStart, zEnd, zEllipsis, iCol, nToken);
}
-5
View File
@@ -134,11 +134,6 @@ SQLITE_EXTENSION_INIT3
#ifdef SQLITE_COVERAGE_TEST
# define ALWAYS(x) (1)
# define NEVER(X) (0)
#elif defined(SQLITE_DEBUG)
# define ALWAYS(x) sqlite3Fts3Always((x)!=0)
# define NEVER(x) sqlite3Fts3Never((x)!=0)
int sqlite3Fts3Always(int b);
int sqlite3Fts3Never(int b);
#else
# define ALWAYS(x) (x)
# define NEVER(x) (x)
+416
View File
@@ -0,0 +1,416 @@
/*
** 2014-08-18
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains code to implement the "changeset" command line
** utility for displaying and transforming changesets generated by
** the Sessions extension.
*/
#include "sqlite3.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
/*
** Show a usage message on stderr then quit.
*/
static void usage(const char *argv0){
fprintf(stderr, "Usage: %s FILENAME COMMAND ...\n", argv0);
fprintf(stderr,
"COMMANDs:\n"
" apply DB Apply the changeset to database file DB\n"
" concat FILE2 OUT Concatenate FILENAME and FILE2 into OUT\n"
" dump Show the complete content of the changeset\n"
" invert OUT Write an inverted changeset into file OUT\n"
" sql Give a pseudo-SQL rendering of the changeset\n"
);
exit(1);
}
/*
** Read the content of a disk file into an in-memory buffer
*/
static void readFile(const char *zFilename, int *pSz, void **ppBuf){
FILE *f;
int sz;
void *pBuf;
f = fopen(zFilename, "rb");
if( f==0 ){
fprintf(stderr, "cannot open \"%s\" for reading\n", zFilename);
exit(1);
}
fseek(f, 0, SEEK_END);
sz = (int)ftell(f);
rewind(f);
pBuf = sqlite3_malloc( sz ? sz : 1 );
if( pBuf==0 ){
fprintf(stderr, "cannot allocate %d to hold content of \"%s\"\n",
sz, zFilename);
exit(1);
}
if( sz>0 ){
if( fread(pBuf, sz, 1, f)!=1 ){
fprintf(stderr, "cannot read all %d bytes of \"%s\"\n", sz, zFilename);
exit(1);
}
fclose(f);
}
*pSz = sz;
*ppBuf = pBuf;
}
/* Array for converting from half-bytes (nybbles) into ASCII hex
** digits. */
static const char hexdigits[] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
/*
** Render an sqlite3_value as an SQL string.
*/
static void renderValue(sqlite3_value *pVal){
switch( sqlite3_value_type(pVal) ){
case SQLITE_FLOAT: {
double r1;
char zBuf[50];
r1 = sqlite3_value_double(pVal);
sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.15g", r1);
printf("%s", zBuf);
break;
}
case SQLITE_INTEGER: {
printf("%lld", sqlite3_value_int64(pVal));
break;
}
case SQLITE_BLOB: {
char const *zBlob = sqlite3_value_blob(pVal);
int nBlob = sqlite3_value_bytes(pVal);
int i;
printf("x'");
for(i=0; i<nBlob; i++){
putchar(hexdigits[(zBlob[i]>>4)&0x0F]);
putchar(hexdigits[(zBlob[i])&0x0F]);
}
putchar('\'');
break;
}
case SQLITE_TEXT: {
const unsigned char *zArg = sqlite3_value_text(pVal);
putchar('\'');
while( zArg[0] ){
putchar(zArg[0]);
if( zArg[0]=='\'' ) putchar(zArg[0]);
zArg++;
}
putchar('\'');
break;
}
default: {
assert( sqlite3_value_type(pVal)==SQLITE_NULL );
printf("NULL");
break;
}
}
}
/*
** Number of conflicts seen
*/
static int nConflict = 0;
/*
** The conflict callback
*/
static int conflictCallback(
void *pCtx,
int eConflict,
sqlite3_changeset_iter *pIter
){
int op, bIndirect, nCol, i;
const char *zTab;
unsigned char *abPK;
const char *zType = "";
const char *zOp = "";
const char *zSep = " ";
nConflict++;
sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
sqlite3changeset_pk(pIter, &abPK, 0);
switch( eConflict ){
case SQLITE_CHANGESET_DATA: zType = "DATA"; break;
case SQLITE_CHANGESET_NOTFOUND: zType = "NOTFOUND"; break;
case SQLITE_CHANGESET_CONFLICT: zType = "PRIMARY KEY"; break;
case SQLITE_CHANGESET_FOREIGN_KEY: zType = "FOREIGN KEY"; break;
case SQLITE_CHANGESET_CONSTRAINT: zType = "CONSTRAINT"; break;
}
switch( op ){
case SQLITE_UPDATE: zOp = "UPDATE of"; break;
case SQLITE_INSERT: zOp = "INSERT into"; break;
case SQLITE_DELETE: zOp = "DELETE from"; break;
}
printf("%s conflict on %s table %s with primary key", zType, zOp, zTab);
for(i=0; i<nCol; i++){
sqlite3_value *pVal;
if( abPK[i]==0 ) continue;
printf("%s", zSep);
if( op==SQLITE_INSERT ){
sqlite3changeset_new(pIter, i, &pVal);
}else{
sqlite3changeset_old(pIter, i, &pVal);
}
renderValue(pVal);
zSep = ",";
}
printf("\n");
return SQLITE_CHANGESET_OMIT;
}
int main(int argc, char **argv){
int sz, rc;
void *pBuf = 0;
if( argc<3 ) usage(argv[0]);
readFile(argv[1], &sz, &pBuf);
/* changeset FILENAME apply DB
** Apply the changeset in FILENAME to the database file DB
*/
if( strcmp(argv[2],"apply")==0 ){
sqlite3 *db;
if( argc!=4 ) usage(argv[0]);
rc = sqlite3_open(argv[3], &db);
if( rc!=SQLITE_OK ){
fprintf(stderr, "unable to open database file \"%s\": %s\n",
argv[3], sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
sqlite3_exec(db, "BEGIN", 0, 0, 0);
nConflict = 0;
rc = sqlite3changeset_apply(db, sz, pBuf, 0, conflictCallback, 0);
if( rc ){
fprintf(stderr, "sqlite3changeset_apply() returned %d\n", rc);
}
if( nConflict ){
fprintf(stderr, "%d conflicts - no changes applied\n", nConflict);
sqlite3_exec(db, "ROLLBACK", 0, 0, 0);
}else if( rc ){
fprintf(stderr, "sqlite3changeset_apply() returns %d "
"- no changes applied\n", rc);
sqlite3_exec(db, "ROLLBACK", 0, 0, 0);
}else{
sqlite3_exec(db, "COMMIT", 0, 0, 0);
}
sqlite3_close(db);
}else
/* changeset FILENAME concat FILE2 OUT
** Add changeset FILE2 onto the end of the changeset in FILENAME
** and write the result into OUT.
*/
if( strcmp(argv[2],"concat")==0 ){
int szB;
void *pB;
int szOut;
void *pOutBuf;
FILE *out;
const char *zOut = argv[4];
if( argc!=5 ) usage(argv[0]);
out = fopen(zOut, "wb");
if( out==0 ){
fprintf(stderr, "cannot open \"%s\" for writing\n", zOut);
exit(1);
}
readFile(argv[3], &szB, &pB);
rc = sqlite3changeset_concat(sz, pBuf, szB, pB, &szOut, &pOutBuf);
if( rc!=SQLITE_OK ){
fprintf(stderr, "sqlite3changeset_concat() returns %d\n", rc);
}else if( szOut>0 && fwrite(pOutBuf, szOut, 1, out)!=1 ){
fprintf(stderr, "unable to write all %d bytes of output to \"%s\"\n",
szOut, zOut);
}
fclose(out);
sqlite3_free(pOutBuf);
sqlite3_free(pB);
}else
/* changeset FILENAME dump
** Show the complete content of the changeset in FILENAME
*/
if( strcmp(argv[2],"dump")==0 ){
int cnt = 0;
int i;
sqlite3_changeset_iter *pIter;
rc = sqlite3changeset_start(&pIter, sz, pBuf);
if( rc!=SQLITE_OK ){
fprintf(stderr, "sqlite3changeset_start() returns %d\n", rc);
exit(1);
}
while( sqlite3changeset_next(pIter)==SQLITE_ROW ){
int op, bIndirect, nCol;
const char *zTab;
unsigned char *abPK;
sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
cnt++;
printf("%d: %s table=[%s] indirect=%d nColumn=%d\n",
cnt, op==SQLITE_INSERT ? "INSERT" :
op==SQLITE_UPDATE ? "UPDATE" : "DELETE",
zTab, bIndirect, nCol);
sqlite3changeset_pk(pIter, &abPK, 0);
for(i=0; i<nCol; i++){
sqlite3_value *pVal;
pVal = 0;
sqlite3changeset_old(pIter, i, &pVal);
if( pVal ){
printf(" old[%d]%s = ", i, abPK[i] ? "pk" : " ");
renderValue(pVal);
printf("\n");
}
pVal = 0;
sqlite3changeset_new(pIter, i, &pVal);
if( pVal ){
printf(" new[%d]%s = ", i, abPK[i] ? "pk" : " ");
renderValue(pVal);
printf("\n");
}
}
}
sqlite3changeset_finalize(pIter);
}else
/* changeset FILENAME invert OUT
** Invert the changes in FILENAME and writes the result on OUT
*/
if( strcmp(argv[2],"invert")==0 ){
FILE *out;
int szOut = 0;
void *pOutBuf = 0;
const char *zOut = argv[3];
if( argc!=4 ) usage(argv[0]);
out = fopen(zOut, "wb");
if( out==0 ){
fprintf(stderr, "cannot open \"%s\" for writing\n", zOut);
exit(1);
}
rc = sqlite3changeset_invert(sz, pBuf, &szOut, &pOutBuf);
if( rc!=SQLITE_OK ){
fprintf(stderr, "sqlite3changeset_invert() returns %d\n", rc);
}else if( szOut>0 && fwrite(pOutBuf, szOut, 1, out)!=1 ){
fprintf(stderr, "unable to write all %d bytes of output to \"%s\"\n",
szOut, zOut);
}
fclose(out);
sqlite3_free(pOutBuf);
}else
/* changeset FILE sql
** Show the content of the changeset as pseudo-SQL
*/
if( strcmp(argv[2],"sql")==0 ){
int cnt = 0;
char *zPrevTab = 0;
char *zSQLTabName = 0;
sqlite3_changeset_iter *pIter = 0;
rc = sqlite3changeset_start(&pIter, sz, pBuf);
if( rc!=SQLITE_OK ){
fprintf(stderr, "sqlite3changeset_start() returns %d\n", rc);
exit(1);
}
printf("BEGIN;\n");
while( sqlite3changeset_next(pIter)==SQLITE_ROW ){
int op, bIndirect, nCol;
const char *zTab;
sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
cnt++;
if( zPrevTab==0 || strcmp(zPrevTab,zTab)!=0 ){
sqlite3_free(zPrevTab);
sqlite3_free(zSQLTabName);
zPrevTab = sqlite3_mprintf("%s", zTab);
if( !isalnum(zTab[0]) || sqlite3_strglob("*[^a-zA-Z0-9]*",zTab)==0 ){
zSQLTabName = sqlite3_mprintf("\"%w\"", zTab);
}else{
zSQLTabName = sqlite3_mprintf("%s", zTab);
}
printf("/****** Changes for table %s ***************/\n", zSQLTabName);
}
switch( op ){
case SQLITE_DELETE: {
unsigned char *abPK;
int i;
const char *zSep = " ";
sqlite3changeset_pk(pIter, &abPK, 0);
printf("/* %d */ DELETE FROM %s WHERE", cnt, zSQLTabName);
for(i=0; i<nCol; i++){
sqlite3_value *pVal;
if( abPK[i]==0 ) continue;
printf("%sc%d=", zSep, i+1);
zSep = " AND ";
sqlite3changeset_old(pIter, i, &pVal);
renderValue(pVal);
}
printf(";\n");
break;
}
case SQLITE_UPDATE: {
unsigned char *abPK;
int i;
const char *zSep = " ";
sqlite3changeset_pk(pIter, &abPK, 0);
printf("/* %d */ UPDATE %s SET", cnt, zSQLTabName);
for(i=0; i<nCol; i++){
sqlite3_value *pVal = 0;
sqlite3changeset_new(pIter, i, &pVal);
if( pVal ){
printf("%sc%d=", zSep, i+1);
zSep = ", ";
renderValue(pVal);
}
}
printf(" WHERE");
zSep = " ";
for(i=0; i<nCol; i++){
sqlite3_value *pVal;
if( abPK[i]==0 ) continue;
printf("%sc%d=", zSep, i+1);
zSep = " AND ";
sqlite3changeset_old(pIter, i, &pVal);
renderValue(pVal);
}
printf(";\n");
break;
}
case SQLITE_INSERT: {
int i;
printf("/* %d */ INSERT INTO %s VALUES", cnt, zSQLTabName);
for(i=0; i<nCol; i++){
sqlite3_value *pVal;
printf("%c", i==0 ? '(' : ',');
sqlite3changeset_new(pIter, i, &pVal);
renderValue(pVal);
}
printf(");\n");
break;
}
}
}
printf("COMMIT;\n");
sqlite3changeset_finalize(pIter);
sqlite3_free(zPrevTab);
sqlite3_free(zSQLTabName);
}else
/* If nothing else matches, show the usage comment */
usage(argv[0]);
sqlite3_free(pBuf);
return 0;
}
+553
View File
@@ -0,0 +1,553 @@
# 2011 March 07
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# 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 session1
proc do_changeset_test {tn session res} {
set r [list]
foreach x $res {lappend r $x}
uplevel do_test $tn [list [subst -nocommands {
set x [list]
sqlite3session_foreach c [$session changeset] { lappend x [set c] }
set x
}]] [list $r]
}
proc do_changeset_invert_test {tn session res} {
set r [list]
foreach x $res {lappend r $x}
uplevel do_test $tn [list [subst -nocommands {
set x [list]
set changeset [sqlite3changeset_invert [$session changeset]]
sqlite3session_foreach c [set changeset] { lappend x [set c] }
set x
}]] [list $r]
}
do_execsql_test 1.0 {
CREATE TABLE t1(x PRIMARY KEY, y);
INSERT INTO t1 VALUES('abc', 'def');
}
#-------------------------------------------------------------------------
# Test creating, attaching tables to and deleting session objects.
#
do_test 1.1 { sqlite3session S db main } {S}
do_test 1.2 { S delete } {}
do_test 1.3 { sqlite3session S db main } {S}
do_test 1.4 { S attach t1 } {}
do_test 1.5 { S delete } {}
do_test 1.6 { sqlite3session S db main } {S}
do_test 1.7 { S attach t1 ; S attach t2 ; S attach t3 } {}
do_test 1.8 { S attach t1 ; S attach t2 ; S attach t3 } {}
do_test 1.9 { S delete } {}
do_test 1.10 {
sqlite3session S db main
S attach t1
execsql { INSERT INTO t1 VALUES('ghi', 'jkl') }
} {}
do_test 1.11 { S delete } {}
do_test 1.12 {
sqlite3session S db main
S attach t1
execsql { INSERT INTO t1 VALUES('mno', 'pqr') }
execsql { UPDATE t1 SET x = 111 WHERE rowid = 1 }
execsql { DELETE FROM t1 WHERE rowid = 2 }
} {}
do_test 1.13 {
S changeset
S delete
} {}
#-------------------------------------------------------------------------
# Simple changeset tests. Also test the sqlite3changeset_invert()
# function.
#
do_test 2.1.1 {
execsql { DELETE FROM t1 }
sqlite3session S db main
S attach t1
execsql { INSERT INTO t1 VALUES(1, 'Sukhothai') }
execsql { INSERT INTO t1 VALUES(2, 'Ayutthaya') }
execsql { INSERT INTO t1 VALUES(3, 'Thonburi') }
} {}
do_changeset_test 2.1.2 S {
{INSERT t1 0 X. {} {i 1 t Sukhothai}}
{INSERT t1 0 X. {} {i 2 t Ayutthaya}}
{INSERT t1 0 X. {} {i 3 t Thonburi}}
}
do_changeset_invert_test 2.1.3 S {
{DELETE t1 0 X. {i 1 t Sukhothai} {}}
{DELETE t1 0 X. {i 2 t Ayutthaya} {}}
{DELETE t1 0 X. {i 3 t Thonburi} {}}
}
do_test 2.1.4 { S delete } {}
do_test 2.2.1 {
sqlite3session S db main
S attach t1
execsql { DELETE FROM t1 WHERE 1 }
} {}
do_changeset_test 2.2.2 S {
{DELETE t1 0 X. {i 1 t Sukhothai} {}}
{DELETE t1 0 X. {i 2 t Ayutthaya} {}}
{DELETE t1 0 X. {i 3 t Thonburi} {}}
}
do_changeset_invert_test 2.2.3 S {
{INSERT t1 0 X. {} {i 1 t Sukhothai}}
{INSERT t1 0 X. {} {i 2 t Ayutthaya}}
{INSERT t1 0 X. {} {i 3 t Thonburi}}
}
do_test 2.2.4 { S delete } {}
do_test 2.3.1 {
execsql { DELETE FROM t1 }
sqlite3session S db main
execsql { INSERT INTO t1 VALUES(1, 'Sukhothai') }
execsql { INSERT INTO t1 VALUES(2, 'Ayutthaya') }
execsql { INSERT INTO t1 VALUES(3, 'Thonburi') }
S attach t1
execsql {
UPDATE t1 SET x = 10 WHERE x = 1;
UPDATE t1 SET y = 'Surin' WHERE x = 2;
UPDATE t1 SET x = 20, y = 'Thapae' WHERE x = 3;
}
} {}
do_changeset_test 2.3.2 S {
{INSERT t1 0 X. {} {i 10 t Sukhothai}}
{DELETE t1 0 X. {i 1 t Sukhothai} {}}
{UPDATE t1 0 X. {i 2 t Ayutthaya} {{} {} t Surin}}
{DELETE t1 0 X. {i 3 t Thonburi} {}}
{INSERT t1 0 X. {} {i 20 t Thapae}}
}
do_changeset_invert_test 2.3.3 S {
{DELETE t1 0 X. {i 10 t Sukhothai} {}}
{INSERT t1 0 X. {} {i 1 t Sukhothai}}
{UPDATE t1 0 X. {i 2 t Surin} {{} {} t Ayutthaya}}
{INSERT t1 0 X. {} {i 3 t Thonburi}}
{DELETE t1 0 X. {i 20 t Thapae} {}}
}
do_test 2.3.4 { S delete } {}
do_test 2.4.1 {
sqlite3session S db main
S attach t1
execsql { INSERT INTO t1 VALUES(100, 'Bangkok') }
execsql { DELETE FROM t1 WHERE x = 100 }
} {}
do_changeset_test 2.4.2 S {}
do_changeset_invert_test 2.4.3 S {}
do_test 2.4.4 { S delete } {}
#-------------------------------------------------------------------------
# Test the application of simple changesets. These tests also test that
# the conflict callback is invoked correctly. For these tests, the
# conflict callback always returns OMIT.
#
db close
forcedelete test.db test.db2
sqlite3 db test.db
sqlite3 db2 test.db2
proc xConflict {args} {
lappend ::xConflict $args
return ""
}
proc bgerror {args} { set ::background_error $args }
proc do_conflict_test {tn args} {
set O(-tables) [list]
set O(-sql) [list]
set O(-conflicts) [list]
array set V $args
foreach key [array names V] {
if {![info exists O($key)]} {error "no such option: $key"}
}
array set O $args
sqlite3session S db main
foreach t $O(-tables) { S attach $t }
execsql $O(-sql)
set ::xConflict [list]
sqlite3changeset_apply db2 [S changeset] xConflict
set conflicts [list]
foreach c $O(-conflicts) {
lappend conflicts $c
}
after 1 {set go 1}
vwait go
uplevel do_test $tn [list { set ::xConflict }] [list $conflicts]
S delete
}
proc do_db2_test {testname sql {result {}}} {
uplevel do_test $testname [list "execsql {$sql} db2"] [list [list {*}$result]]
}
# Test INSERT changesets.
#
do_test 3.1.0 {
execsql { CREATE TABLE t1(a PRIMARY KEY, b NOT NULL) } db2
execsql {
CREATE TABLE t1(a PRIMARY KEY, b);
INSERT INTO t1 VALUES(1, 'one');
INSERT INTO t1 VALUES(2, 'two');
} db
} {}
do_db2_test 3.1.1 "INSERT INTO t1 VALUES(6, 'VI')"
do_conflict_test 3.1.2 -tables t1 -sql {
INSERT INTO t1 VALUES(3, 'three');
INSERT INTO t1 VALUES(4, 'four');
INSERT INTO t1 VALUES(5, 'five');
INSERT INTO t1 VALUES(6, 'six');
INSERT INTO t1 VALUES(7, 'seven');
INSERT INTO t1 VALUES(8, NULL);
} -conflicts {
{INSERT t1 CONSTRAINT {i 8 n {}}}
{INSERT t1 CONFLICT {i 6 t six} {i 6 t VI}}
}
do_db2_test 3.1.3 "SELECT * FROM t1" {
6 VI 3 three 4 four 5 five 7 seven
}
do_execsql_test 3.1.4 "SELECT * FROM t1" {
1 one 2 two 3 three 4 four 5 five 6 six 7 seven 8 {}
}
# Test DELETE changesets.
#
do_execsql_test 3.2.1 {
PRAGMA foreign_keys = on;
CREATE TABLE t2(a PRIMARY KEY, b);
CREATE TABLE t3(c, d REFERENCES t2);
INSERT INTO t2 VALUES(1, 'one');
INSERT INTO t2 VALUES(2, 'two');
INSERT INTO t2 VALUES(3, 'three');
INSERT INTO t2 VALUES(4, 'four');
}
do_db2_test 3.2.2 {
PRAGMA foreign_keys = on;
CREATE TABLE t2(a PRIMARY KEY, b);
CREATE TABLE t3(c, d REFERENCES t2);
INSERT INTO t2 VALUES(1, 'one');
INSERT INTO t2 VALUES(2, 'two');
INSERT INTO t2 VALUES(4, 'five');
INSERT INTO t3 VALUES('i', 1);
}
do_conflict_test 3.2.3 -tables t2 -sql {
DELETE FROM t2 WHERE a = 1;
DELETE FROM t2 WHERE a = 2;
DELETE FROM t2 WHERE a = 3;
DELETE FROM t2 WHERE a = 4;
} -conflicts {
{DELETE t2 NOTFOUND {i 3 t three}}
{DELETE t2 DATA {i 4 t four} {i 4 t five}}
{FOREIGN_KEY 1}
}
do_execsql_test 3.2.4 "SELECT * FROM t2" {}
do_db2_test 3.2.5 "SELECT * FROM t2" {4 five}
# Test UPDATE changesets.
#
do_execsql_test 3.3.1 {
CREATE TABLE t4(a, b, c, PRIMARY KEY(b, c));
INSERT INTO t4 VALUES(1, 2, 3);
INSERT INTO t4 VALUES(4, 5, 6);
INSERT INTO t4 VALUES(7, 8, 9);
INSERT INTO t4 VALUES(10, 11, 12);
}
do_db2_test 3.3.2 {
CREATE TABLE t4(a NOT NULL, b, c, PRIMARY KEY(b, c));
INSERT INTO t4 VALUES(0, 2, 3);
INSERT INTO t4 VALUES(4, 5, 7);
INSERT INTO t4 VALUES(7, 8, 9);
INSERT INTO t4 VALUES(10, 11, 12);
}
do_conflict_test 3.3.3 -tables t4 -sql {
UPDATE t4 SET a = -1 WHERE b = 2;
UPDATE t4 SET a = -1 WHERE b = 5;
UPDATE t4 SET a = NULL WHERE c = 9;
UPDATE t4 SET a = 'x' WHERE b = 11;
} -conflicts {
{UPDATE t4 CONSTRAINT {i 7 i 8 i 9} {n {} {} {} {} {}}}
{UPDATE t4 DATA {i 1 i 2 i 3} {i -1 {} {} {} {}} {i 0 i 2 i 3}}
{UPDATE t4 NOTFOUND {i 4 i 5 i 6} {i -1 {} {} {} {}}}
}
do_db2_test 3.3.4 { SELECT * FROM t4 } {0 2 3 4 5 7 7 8 9 x 11 12}
do_execsql_test 3.3.5 { SELECT * FROM t4 } {-1 2 3 -1 5 6 {} 8 9 x 11 12}
#-------------------------------------------------------------------------
# This next block of tests verifies that values returned by the conflict
# handler are intepreted correctly.
#
proc test_reset {} {
db close
db2 close
forcedelete test.db test.db2
sqlite3 db test.db
sqlite3 db2 test.db2
}
proc xConflict {args} {
lappend ::xConflict $args
return $::conflict_return
}
foreach {tn conflict_return after} {
1 OMIT {1 2 value1 4 5 7 10 x x}
2 REPLACE {1 2 value1 4 5 value2 10 8 9}
} {
test_reset
do_test 4.$tn.1 {
foreach db {db db2} {
execsql {
CREATE TABLE t1(a, b, c, PRIMARY KEY(a));
INSERT INTO t1 VALUES(1, 2, 3);
INSERT INTO t1 VALUES(4, 5, 6);
INSERT INTO t1 VALUES(7, 8, 9);
} $db
}
execsql {
REPLACE INTO t1 VALUES(4, 5, 7);
REPLACE INTO t1 VALUES(10, 'x', 'x');
} db2
} {}
do_conflict_test 4.$tn.2 -tables t1 -sql {
UPDATE t1 SET c = 'value1' WHERE a = 1; -- no conflict
UPDATE t1 SET c = 'value2' WHERE a = 4; -- DATA conflict
UPDATE t1 SET a = 10 WHERE a = 7; -- CONFLICT conflict
} -conflicts {
{INSERT t1 CONFLICT {i 10 i 8 i 9} {i 10 t x t x}}
{UPDATE t1 DATA {i 4 {} {} i 6} {{} {} {} {} t value2} {i 4 i 5 i 7}}
}
do_db2_test 4.$tn.3 "SELECT * FROM t1 ORDER BY a" $after
}
foreach {tn conflict_return} {
1 OMIT
2 REPLACE
} {
test_reset
do_test 5.$tn.1 {
# Create an identical schema in both databases.
set schema {
CREATE TABLE "'foolish name'"(x, y, z, PRIMARY KEY(x, y));
}
execsql $schema db
execsql $schema db2
# Add some rows to [db2]. These rows will cause conflicts later
# on when the changeset from [db] is applied to it.
execsql {
INSERT INTO "'foolish name'" VALUES('one', 'one', 'ii');
INSERT INTO "'foolish name'" VALUES('one', 'two', 'i');
INSERT INTO "'foolish name'" VALUES('two', 'two', 'ii');
} db2
} {}
do_conflict_test 5.$tn.2 -tables {{'foolish name'}} -sql {
INSERT INTO "'foolish name'" VALUES('one', 'two', 2);
} -conflicts {
{INSERT {'foolish name'} CONFLICT {t one t two i 2} {t one t two t i}}
}
set res(REPLACE) {one one ii one two 2 two two ii}
set res(OMIT) {one one ii one two i two two ii}
do_db2_test 5.$tn.3 {
SELECT * FROM "'foolish name'" ORDER BY x, y
} $res($conflict_return)
do_test 5.$tn.1 {
set schema {
CREATE TABLE d1("z""z" PRIMARY KEY, y);
INSERT INTO d1 VALUES(1, 'one');
INSERT INTO d1 VALUES(2, 'two');
}
execsql $schema db
execsql $schema db2
execsql {
UPDATE d1 SET y = 'TWO' WHERE "z""z" = 2;
} db2
} {}
do_conflict_test 5.$tn.2 -tables d1 -sql {
DELETE FROM d1 WHERE "z""z" = 2;
} -conflicts {
{DELETE d1 DATA {i 2 t two} {i 2 t TWO}}
}
set res(REPLACE) {1 one}
set res(OMIT) {1 one 2 TWO}
do_db2_test 5.$tn.3 "SELECT * FROM d1" $res($conflict_return)
}
#-------------------------------------------------------------------------
# Test that two tables can be monitored by a single session object.
#
test_reset
set schema {
CREATE TABLE t1(a COLLATE nocase PRIMARY KEY, b);
CREATE TABLE t2(a, b PRIMARY KEY);
}
do_test 6.0 {
execsql $schema db
execsql $schema db2
execsql {
INSERT INTO t1 VALUES('a', 'b');
INSERT INTO t2 VALUES('a', 'b');
} db2
} {}
set conflict_return ""
do_conflict_test 6.1 -tables {t1 t2} -sql {
INSERT INTO t1 VALUES('1', '2');
INSERT INTO t1 VALUES('A', 'B');
INSERT INTO t2 VALUES('A', 'B');
} -conflicts {
{INSERT t1 CONFLICT {t A t B} {t a t b}}
}
do_db2_test 6.2 "SELECT * FROM t1" {a b 1 2}
do_db2_test 6.3 "SELECT * FROM t2" {a b A B}
#-------------------------------------------------------------------------
# Test that session objects are not confused by changes to table in
# other databases.
#
catch { db2 close }
drop_all_tables
forcedelete test.db2
do_iterator_test 7.1 * {
ATTACH 'test.db2' AS aux;
CREATE TABLE main.t1(x PRIMARY KEY, y);
CREATE TABLE aux.t1(x PRIMARY KEY, y);
INSERT INTO main.t1 VALUES('one', 1);
INSERT INTO main.t1 VALUES('two', 2);
INSERT INTO aux.t1 VALUES('three', 3);
INSERT INTO aux.t1 VALUES('four', 4);
} {
{INSERT t1 0 X. {} {t two i 2}}
{INSERT t1 0 X. {} {t one i 1}}
}
#-------------------------------------------------------------------------
# Test the sqlite3session_isempty() function.
#
do_test 8.1 {
execsql {
CREATE TABLE t5(x PRIMARY KEY, y);
CREATE TABLE t6(x PRIMARY KEY, y);
INSERT INTO t5 VALUES('a', 'b');
INSERT INTO t6 VALUES('a', 'b');
}
sqlite3session S db main
S attach *
S isempty
} {1}
do_test 8.2 {
execsql { DELETE FROM t5 }
S isempty
} {0}
do_test 8.3 {
S delete
sqlite3session S db main
S attach t5
execsql { DELETE FROM t5 }
S isempty
} {1}
do_test 8.4 { S delete } {}
do_test 8.5 {
sqlite3session S db main
S attach t5
S attach t6
execsql { INSERT INTO t5 VALUES(1, 2) }
S isempty
} {0}
do_test 8.6 {
S delete
sqlite3session S db main
S attach t5
S attach t6
execsql { INSERT INTO t6 VALUES(1, 2) }
S isempty
} {0}
do_test 8.7 { S delete } {}
#-------------------------------------------------------------------------
#
do_execsql_test 9.1 {
CREATE TABLE t7(a, b, c, d, e PRIMARY KEY, f, g);
INSERT INTO t7 VALUES(1, 1, 1, 1, 1, 1, 1);
}
do_test 9.2 {
sqlite3session S db main
S attach *
execsql { UPDATE t7 SET b=2, d=2 }
} {}
do_changeset_test 9.2 S {{UPDATE t7 0 ....X.. {{} {} i 1 {} {} i 1 i 1 {} {} {} {}} {{} {} i 2 {} {} i 2 {} {} {} {} {} {}}}}
S delete
catch { db2 close }
#-------------------------------------------------------------------------
# Test a really long table name.
#
reset_db
set tblname [string repeat tblname123 100]
do_test 10.1.1 {
execsql "
CREATE TABLE $tblname (a PRIMARY KEY, b);
INSERT INTO $tblname VALUES('xyz', 'def');
"
sqlite3session S db main
S attach $tblname
execsql "
INSERT INTO $tblname VALUES('uvw', 'abc');
DELETE FROM $tblname WHERE a = 'xyz';
"
} {}
breakpoint
do_changeset_test 10.1.2 S "
{INSERT $tblname 0 X. {} {t uvw t abc}}
{DELETE $tblname 0 X. {t xyz t def} {}}
"
do_test 10.1.4 { S delete } {}
finish_test
+591
View File
@@ -0,0 +1,591 @@
# 2011 Mar 16
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# The focus of this file is testing the session module.
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source [file join [file dirname [info script]] session_common.tcl]
source $testdir/tester.tcl
ifcapable !session {finish_test; return}
set testprefix session2
proc test_reset {} {
catch { db close }
catch { db2 close }
forcedelete test.db test.db2
sqlite3 db test.db
sqlite3 db2 test.db2
}
##########################################################################
# End of proc definitions. Start of tests.
##########################################################################
test_reset
do_execsql_test 1.0 {
CREATE TABLE t1(a PRIMARY KEY, b);
INSERT INTO t1 VALUES('i', 'one');
}
do_iterator_test 1.1 t1 {
DELETE FROM t1 WHERE a = 'i';
INSERT INTO t1 VALUES('ii', 'two');
} {
{DELETE t1 0 X. {t i t one} {}}
{INSERT t1 0 X. {} {t ii t two}}
}
do_iterator_test 1.2 t1 {
INSERT INTO t1 VALUES(1.5, 99.9)
} {
{INSERT t1 0 X. {} {f 1.5 f 99.9}}
}
do_iterator_test 1.3 t1 {
UPDATE t1 SET b = 100.1 WHERE a = 1.5;
UPDATE t1 SET b = 99.9 WHERE a = 1.5;
} { }
do_iterator_test 1.4 t1 {
UPDATE t1 SET b = 100.1 WHERE a = 1.5;
} {
{UPDATE t1 0 X. {f 1.5 f 99.9} {{} {} f 100.1}}
}
# Execute each of the following blocks of SQL on database [db1]. Collect
# changes using a session object. Apply the resulting changeset to
# database [db2]. Then check that the contents of the two databases are
# identical.
#
set set_of_tests {
1 { INSERT INTO %T1% VALUES(1, 2) }
2 {
INSERT INTO %T2% VALUES(1, NULL);
INSERT INTO %T2% VALUES(2, NULL);
INSERT INTO %T2% VALUES(3, NULL);
DELETE FROM %T2% WHERE a = 2;
INSERT INTO %T2% VALUES(4, NULL);
UPDATE %T2% SET b=0 WHERE b=1;
}
3 { INSERT INTO %T3% SELECT *, NULL FROM %T2% }
4 {
INSERT INTO %T3% SELECT a||a, b||b, NULL FROM %T3%;
DELETE FROM %T3% WHERE rowid%2;
}
5 { UPDATE %T3% SET c = a||b }
6 { UPDATE %T1% SET a = 32 }
7 {
INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%;
INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%;
INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%;
INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%;
INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%;
INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%;
INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%;
INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%;
INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%;
INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%;
INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%;
DELETE FROM %T1% WHERE (rowid%3)==0;
}
8 {
BEGIN;
INSERT INTO %T1% SELECT randomblob(32), randomblob(32) FROM %T1%;
ROLLBACK;
}
9 {
BEGIN;
UPDATE %T1% SET b = 'xxx';
ROLLBACK;
}
10 {
BEGIN;
DELETE FROM %T1% WHERE 1;
ROLLBACK;
}
11 {
INSERT INTO %T1% VALUES(randomblob(21000), randomblob(0));
INSERT INTO %T1% VALUES(1.5, 1.5);
INSERT INTO %T1% VALUES(4.56, -99.999999999999999999999);
}
12 {
INSERT INTO %T2% VALUES(NULL, NULL);
}
13 {
DELETE FROM %T1% WHERE 1;
-- Insert many rows with real primary keys. Enough to force the session
-- objects hash table to resize.
INSERT INTO %T1% VALUES(0.1, 0.1);
INSERT INTO %T1% SELECT a+0.1, b+0.1 FROM %T1%;
INSERT INTO %T1% SELECT a+0.2, b+0.2 FROM %T1%;
INSERT INTO %T1% SELECT a+0.4, b+0.4 FROM %T1%;
INSERT INTO %T1% SELECT a+0.8, b+0.8 FROM %T1%;
INSERT INTO %T1% SELECT a+1.6, b+1.6 FROM %T1%;
INSERT INTO %T1% SELECT a+3.2, b+3.2 FROM %T1%;
INSERT INTO %T1% SELECT a+6.4, b+6.4 FROM %T1%;
INSERT INTO %T1% SELECT a+12.8, b+12.8 FROM %T1%;
INSERT INTO %T1% SELECT a+25.6, b+25.6 FROM %T1%;
INSERT INTO %T1% SELECT a+51.2, b+51.2 FROM %T1%;
INSERT INTO %T1% SELECT a+102.4, b+102.4 FROM %T1%;
INSERT INTO %T1% SELECT a+204.8, b+204.8 FROM %T1%;
}
14 {
DELETE FROM %T1% WHERE 1;
}
15 {
INSERT INTO %T1% VALUES(1, 1);
INSERT INTO %T1% SELECT a+2, b+2 FROM %T1%;
INSERT INTO %T1% SELECT a+4, b+4 FROM %T1%;
INSERT INTO %T1% SELECT a+8, b+8 FROM %T1%;
INSERT INTO %T1% SELECT a+256, b+256 FROM %T1%;
}
16 {
INSERT INTO %T4% VALUES('abc', 'def');
INSERT INTO %T4% VALUES('def', 'abc');
}
17 { UPDATE %T4% SET b = 1 }
18 { DELETE FROM %T4% WHERE 1 }
19 {
INSERT INTO t1 VALUES('', '');
INSERT INTO t1 VALUES(X'', X'');
}
20 {
DELETE FROM t1;
INSERT INTO t1 VALUES('', NULL);
}
}
test_reset
do_common_sql {
CREATE TABLE t1(a PRIMARY KEY, b);
CREATE TABLE t2(a, b INTEGER PRIMARY KEY);
CREATE TABLE t3(a, b, c, PRIMARY KEY(a, b));
CREATE TABLE t4(a, b, PRIMARY KEY(b, a));
}
foreach {tn sql} [string map {%T1% t1 %T2% t2 %T3% t3 %T4% t4} $set_of_tests] {
do_then_apply_sql $sql
do_test 2.$tn { compare_db db db2 } {}
}
# The following block of tests is similar to the last, except that the
# session object is recording changes made to an attached database. The
# main database contains a table of the same name as the table being
# modified within the attached db.
#
test_reset
forcedelete test.db3
sqlite3 db3 test.db3
do_test 3.0 {
execsql {
ATTACH 'test.db3' AS 'aux';
CREATE TABLE t1(a, b PRIMARY KEY);
CREATE TABLE t2(x, y, z);
CREATE TABLE t3(a);
CREATE TABLE aux.t1(a PRIMARY KEY, b);
CREATE TABLE aux.t2(a, b INTEGER PRIMARY KEY);
CREATE TABLE aux.t3(a, b, c, PRIMARY KEY(a, b));
CREATE TABLE aux.t4(a, b, PRIMARY KEY(b, a));
}
execsql {
CREATE TABLE t1(a PRIMARY KEY, b);
CREATE TABLE t2(a, b INTEGER PRIMARY KEY);
CREATE TABLE t3(a, b, c, PRIMARY KEY(a, b));
CREATE TABLE t4(a, b, PRIMARY KEY(b, a));
} db2
} {}
proc xTrace {args} { puts $args }
foreach {tn sql} [
string map {%T1% aux.t1 %T2% aux.t2 %T3% aux.t3 %T4% aux.t4} $set_of_tests
] {
do_then_apply_sql $sql aux
do_test 3.$tn { compare_db db2 db3 } {}
}
catch {db3 close}
#-------------------------------------------------------------------------
# The following tests verify that NULL values in primary key columns are
# handled correctly by the session module.
#
test_reset
do_execsql_test 4.0 {
CREATE TABLE t1(a PRIMARY KEY);
CREATE TABLE t2(a, b, c, PRIMARY KEY(c, b));
CREATE TABLE t3(a, b INTEGER PRIMARY KEY);
}
foreach {tn sql changeset} {
1 {
INSERT INTO t1 VALUES(123);
INSERT INTO t1 VALUES(NULL);
INSERT INTO t1 VALUES(456);
} {
{INSERT t1 0 X {} {i 456}}
{INSERT t1 0 X {} {i 123}}
}
2 {
UPDATE t1 SET a = NULL;
} {
{DELETE t1 0 X {i 456} {}}
{DELETE t1 0 X {i 123} {}}
}
3 { DELETE FROM t1 } { }
4 {
INSERT INTO t3 VALUES(NULL, NULL)
} {
{INSERT t3 0 .X {} {n {} i 1}}
}
5 { INSERT INTO t2 VALUES(1, 2, NULL) } { }
6 { INSERT INTO t2 VALUES(1, NULL, 3) } { }
7 { INSERT INTO t2 VALUES(1, NULL, NULL) } { }
8 { INSERT INTO t2 VALUES(1, 2, 3) } { {INSERT t2 0 .XX {} {i 1 i 2 i 3}} }
9 { DELETE FROM t2 WHERE 1 } { {DELETE t2 0 .XX {i 1 i 2 i 3} {}} }
} {
do_iterator_test 4.$tn {t1 t2 t3} $sql $changeset
}
#-------------------------------------------------------------------------
# Test that if NULL is passed to sqlite3session_attach(), all database
# tables are attached to the session object.
#
test_reset
do_execsql_test 5.0 {
CREATE TABLE t1(a PRIMARY KEY);
CREATE TABLE t2(x, y PRIMARY KEY);
}
foreach {tn sql changeset} {
1 { INSERT INTO t1 VALUES(35) } { {INSERT t1 0 X {} {i 35}} }
2 { INSERT INTO t2 VALUES(36, 37) } { {INSERT t2 0 .X {} {i 36 i 37}} }
3 {
DELETE FROM t1 WHERE 1;
UPDATE t2 SET x = 34;
} {
{UPDATE t2 0 .X {i 36 i 37} {i 34 {} {}}}
{DELETE t1 0 X {i 35} {}}
}
} {
do_iterator_test 5.$tn * $sql $changeset
}
#-------------------------------------------------------------------------
# The next block of tests verify that the "indirect" flag is set
# correctly within changesets. The indirect flag is set for a change
# if either of the following are true:
#
# * The sqlite3session_indirect() API has been used to set the session
# indirect flag to true, or
# * The change was made by a trigger.
#
# If the same row is updated more than once during a session, then the
# change is considered indirect only if all changes meet the criteria
# above.
#
test_reset
db function indirect [list S indirect]
do_execsql_test 6.0 {
CREATE TABLE t1(a PRIMARY KEY, b, c);
CREATE TABLE t2(x PRIMARY KEY, y);
CREATE TRIGGER AFTER INSERT ON t2 WHEN new.x%2 BEGIN
INSERT INTO t2 VALUES(new.x+1, NULL);
END;
}
do_iterator_test 6.1.1 * {
INSERT INTO t1 VALUES(1, 'one', 'i');
SELECT indirect(1);
INSERT INTO t1 VALUES(2, 'two', 'ii');
SELECT indirect(0);
INSERT INTO t1 VALUES(3, 'three', 'iii');
} {
{INSERT t1 0 X.. {} {i 1 t one t i}}
{INSERT t1 1 X.. {} {i 2 t two t ii}}
{INSERT t1 0 X.. {} {i 3 t three t iii}}
}
do_iterator_test 6.1.2 * {
SELECT indirect(1);
UPDATE t1 SET c = 'I' WHERE a = 1;
SELECT indirect(0);
} {
{UPDATE t1 1 X.. {i 1 {} {} t i} {{} {} {} {} t I}}
}
do_iterator_test 6.1.3 * {
SELECT indirect(1);
UPDATE t1 SET c = '.' WHERE a = 1;
SELECT indirect(0);
UPDATE t1 SET c = 'o' WHERE a = 1;
} {
{UPDATE t1 0 X.. {i 1 {} {} t I} {{} {} {} {} t o}}
}
do_iterator_test 6.1.4 * {
SELECT indirect(0);
UPDATE t1 SET c = 'x' WHERE a = 1;
SELECT indirect(1);
UPDATE t1 SET c = 'i' WHERE a = 1;
} {
{UPDATE t1 0 X.. {i 1 {} {} t o} {{} {} {} {} t i}}
}
do_iterator_test 6.1.4 * {
SELECT indirect(1);
UPDATE t1 SET c = 'y' WHERE a = 1;
SELECT indirect(1);
UPDATE t1 SET c = 'I' WHERE a = 1;
} {
{UPDATE t1 1 X.. {i 1 {} {} t i} {{} {} {} {} t I}}
}
do_iterator_test 6.1.5 * {
INSERT INTO t2 VALUES(1, 'x');
} {
{INSERT t2 0 X. {} {i 1 t x}}
{INSERT t2 1 X. {} {i 2 n {}}}
}
do_iterator_test 6.1.6 * {
SELECT indirect(1);
INSERT INTO t2 VALUES(3, 'x');
SELECT indirect(0);
UPDATE t2 SET y = 'y' WHERE x>2;
} {
{INSERT t2 0 X. {} {i 3 t y}}
{INSERT t2 0 X. {} {i 4 t y}}
}
do_iterator_test 6.1.7 * {
SELECT indirect(1);
DELETE FROM t2 WHERE x = 4;
SELECT indirect(0);
INSERT INTO t2 VALUES(4, 'new');
} {
{UPDATE t2 0 X. {i 4 t y} {{} {} t new}}
}
do_iterator_test 6.1.8 * {
CREATE TABLE t3(a, b PRIMARY KEY);
CREATE TABLE t4(a, b PRIMARY KEY);
CREATE TRIGGER t4t AFTER UPDATE ON t4 BEGIN
UPDATE t3 SET a = new.a WHERE b = new.b;
END;
SELECT indirect(1);
INSERT INTO t3 VALUES('one', 1);
INSERT INTO t4 VALUES('one', 1);
SELECT indirect(0);
UPDATE t4 SET a = 'two' WHERE b = 1;
} {
{INSERT t4 0 .X {} {t two i 1}}
{INSERT t3 1 .X {} {t two i 1}}
}
sqlite3session S db main
do_execsql_test 6.2.1 {
SELECT indirect(0);
SELECT indirect(-1);
SELECT indirect(45);
SELECT indirect(-100);
} {0 0 1 1}
S delete
#-------------------------------------------------------------------------
# Test that if a conflict-handler that has been passed either NOTFOUND or
# CONSTRAINT returns REPLACE - the sqlite3changeset_apply() call returns
# MISUSE and rolls back any changes made so far.
#
# 7.1.*: NOTFOUND conflict-callback.
# 7.2.*: CONSTRAINT conflict-callback.
#
proc xConflict {args} {return REPLACE}
test_reset
do_execsql_test 7.1.1 {
CREATE TABLE t1(a PRIMARY KEY, b);
INSERT INTO t1 VALUES(1, 'one');
INSERT INTO t1 VALUES(2, 'two');
}
do_test 7.1.2 {
execsql {
CREATE TABLE t1(a PRIMARY KEY, b NOT NULL);
INSERT INTO t1 VALUES(1, 'one');
} db2
} {}
do_test 7.1.3 {
set changeset [changeset_from_sql {
UPDATE t1 SET b = 'five' WHERE a = 1;
UPDATE t1 SET b = 'six' WHERE a = 2;
}]
set x [list]
sqlite3session_foreach c $changeset { lappend x $c }
set x
} [list \
{UPDATE t1 0 X. {i 1 t one} {{} {} t five}} \
{UPDATE t1 0 X. {i 2 t two} {{} {} t six}} \
]
do_test 7.1.4 {
list [catch {sqlite3changeset_apply db2 $changeset xConflict} msg] $msg
} {1 SQLITE_MISUSE}
do_test 7.1.5 { execsql { SELECT * FROM t1 } db2 } {1 one}
do_test 7.2.1 {
set changeset [changeset_from_sql { UPDATE t1 SET b = NULL WHERE a = 1 }]
set x [list]
sqlite3session_foreach c $changeset { lappend x $c }
set x
} [list \
{UPDATE t1 0 X. {i 1 t five} {{} {} n {}}} \
]
do_test 7.2.2 {
list [catch {sqlite3changeset_apply db2 $changeset xConflict} msg] $msg
} {1 SQLITE_MISUSE}
do_test 7.2.3 { execsql { SELECT * FROM t1 } db2 } {1 one}
#-------------------------------------------------------------------------
# Test that if a conflict-handler returns ABORT, application of the
# changeset is rolled back and the sqlite3changeset_apply() method returns
# SQLITE_ABORT.
#
# Also test that the same thing happens if a conflict handler returns an
# unrecognized integer value. Except, in this case SQLITE_MISUSE is returned
# instead of SQLITE_ABORT.
#
foreach {tn conflict_return apply_return} {
1 ABORT SQLITE_ABORT
2 567 SQLITE_MISUSE
} {
test_reset
proc xConflict {args} [list return $conflict_return]
do_test 8.$tn.0 {
do_common_sql {
CREATE TABLE t1(x, y, PRIMARY KEY(x, y));
INSERT INTO t1 VALUES('x', 'y');
}
execsql { INSERT INTO t1 VALUES('w', 'w') }
set changeset [changeset_from_sql { DELETE FROM t1 WHERE 1 }]
set x [list]
sqlite3session_foreach c $changeset { lappend x $c }
set x
} [list \
{DELETE t1 0 XX {t w t w} {}} \
{DELETE t1 0 XX {t x t y} {}} \
]
do_test 8.$tn.1 {
list [catch {sqlite3changeset_apply db2 $changeset xConflict} msg] $msg
} [list 1 $apply_return]
do_test 8.$tn.2 {
execsql {SELECT * FROM t1} db2
} {x y}
}
#-------------------------------------------------------------------------
# Try to cause an infinite loop as follows:
#
# 1. Have a changeset insert a row that causes a CONFLICT callback,
# 2. Have the conflict handler return REPLACE,
# 3. After the session module deletes the conflicting row, have a trigger
# re-insert it.
# 4. Goto step 1...
#
# This doesn't work, as the second invocation of the conflict handler is a
# CONSTRAINT, not a CONFLICT. There is at most one CONFLICT callback for
# each change in the changeset.
#
test_reset
proc xConflict {type args} {
if {$type == "CONFLICT"} { return REPLACE }
return OMIT
}
do_test 9.1 {
execsql {
CREATE TABLE t1(a PRIMARY KEY, b);
}
execsql {
CREATE TABLE t1(a PRIMARY KEY, b);
INSERT INTO t1 VALUES('x', 2);
CREATE TRIGGER tr1 AFTER DELETE ON t1 BEGIN
INSERT INTO t1 VALUES(old.a, old.b);
END;
} db2
} {}
do_test 9.2 {
set changeset [changeset_from_sql { INSERT INTO t1 VALUES('x', 1) }]
sqlite3changeset_apply db2 $changeset xConflict
} {}
do_test 9.3 {
execsql { SELECT * FROM t1 } db2
} {x 2}
#-------------------------------------------------------------------------
#
test_reset
db function enable [list S enable]
do_common_sql {
CREATE TABLE t1(a PRIMARY KEY, b);
INSERT INTO t1 VALUES('x', 'X');
}
do_iterator_test 10.1 t1 {
INSERT INTO t1 VALUES('y', 'Y');
SELECT enable(0);
INSERT INTO t1 VALUES('z', 'Z');
SELECT enable(1);
} {
{INSERT t1 0 X. {} {t y t Y}}
}
sqlite3session S db main
do_execsql_test 10.2 {
SELECT enable(0);
SELECT enable(-1);
SELECT enable(1);
SELECT enable(-1);
} {0 0 1 1}
S delete
finish_test
+211
View File
@@ -0,0 +1,211 @@
# 2011 March 24
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for the session module. More
# specifically, it focuses on testing the session modules response to
# database schema modifications and mismatches.
#
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 session3
#-------------------------------------------------------------------------
# These tests - session3-1.* - verify that the session module behaves
# correctly when confronted with a schema mismatch when applying a
# changeset (in function sqlite3changeset_apply()).
#
# session3-1.1.*: Table does not exist in target db.
# session3-1.2.*: Table has wrong number of columns in target db.
# session3-1.3.*: Table has wrong PK columns in target db.
#
db close
sqlite3_shutdown
test_sqlite3_log log
sqlite3 db test.db
proc log {code msg} { lappend ::log $code $msg }
forcedelete test.db2
sqlite3 db2 test.db2
do_execsql_test 1.0 {
CREATE TABLE t1(a PRIMARY KEY, b);
}
do_test 1.1 {
set ::log {}
do_then_apply_sql {
INSERT INTO t1 VALUES(1, 2);
INSERT INTO t1 VALUES(3, 4);
}
set ::log
} {SQLITE_SCHEMA {sqlite3changeset_apply(): no such table: t1}}
do_test 1.2.0 {
execsql { CREATE TABLE t1(a PRIMARY KEY, b, c) } db2
} {}
do_test 1.2.1 {
set ::log {}
do_then_apply_sql {
INSERT INTO t1 VALUES(5, 6);
INSERT INTO t1 VALUES(7, 8);
}
set ::log
} {SQLITE_SCHEMA {sqlite3changeset_apply(): table t1 has 3 columns, expected 2}}
do_test 1.3.0 {
execsql {
DROP TABLE t1;
CREATE TABLE t1(a, b PRIMARY KEY);
} db2
} {}
do_test 1.3.1 {
set ::log {}
do_then_apply_sql {
INSERT INTO t1 VALUES(9, 10);
INSERT INTO t1 VALUES(11, 12);
}
set ::log
} {SQLITE_SCHEMA {sqlite3changeset_apply(): primary key mismatch for table t1}}
#-------------------------------------------------------------------------
# These tests - session3-2.* - verify that the session module behaves
# correctly when the schema of an attached table is modified during the
# session.
#
# session3-2.1.*: Table is dropped midway through the session.
# session3-2.2.*: Table is dropped and recreated with a different # cols.
# session3-2.3.*: Table is dropped and recreated with a different PK.
#
# In all of these scenarios, the call to sqlite3session_changeset() will
# return SQLITE_SCHEMA. Also:
#
# session3-2.4.*: Table is dropped and recreated with an identical schema.
# In this case sqlite3session_changeset() returns SQLITE_OK.
#
do_test 2.1 {
execsql { CREATE TABLE t2(a, b PRIMARY KEY) }
sqlite3session S db main
S attach t2
execsql {
INSERT INTO t2 VALUES(1, 2);
DROP TABLE t2;
}
list [catch { S changeset } msg] $msg
} {1 SQLITE_SCHEMA}
do_test 2.2.1 {
S delete
sqlite3session S db main
execsql { CREATE TABLE t2(a, b PRIMARY KEY, c) }
S attach t2
execsql {
INSERT INTO t2 VALUES(1, 2, 3);
DROP TABLE t2;
CREATE TABLE t2(a, b PRIMARY KEY);
}
list [catch { S changeset } msg] $msg
} {1 SQLITE_SCHEMA}
do_test 2.2.2 {
S delete
sqlite3session S db main
execsql {
DROP TABLE t2;
CREATE TABLE t2(a, b PRIMARY KEY, c);
}
S attach t2
execsql {
INSERT INTO t2 VALUES(1, 2, 3);
DROP TABLE t2;
CREATE TABLE t2(a, b PRIMARY KEY, c, d);
}
list [catch { S changeset } msg] $msg
} {1 SQLITE_SCHEMA}
do_test 2.2.3 {
S delete
sqlite3session S db main
execsql {
DROP TABLE t2;
CREATE TABLE t2(a, b PRIMARY KEY, c);
}
S attach t2
execsql {
INSERT INTO t2 VALUES(1, 2, 3);
DROP TABLE t2;
CREATE TABLE t2(a, b PRIMARY KEY);
INSERT INTO t2 VALUES(4, 5);
}
list [catch { S changeset } msg] $msg
} {1 SQLITE_SCHEMA}
do_test 2.2.4 {
S delete
sqlite3session S db main
execsql {
DROP TABLE t2;
CREATE TABLE t2(a, b PRIMARY KEY, c);
}
S attach t2
execsql {
INSERT INTO t2 VALUES(1, 2, 3);
DROP TABLE t2;
CREATE TABLE t2(a, b PRIMARY KEY, c, d);
INSERT INTO t2 VALUES(4, 5, 6, 7);
}
list [catch { S changeset } msg] $msg
} {1 SQLITE_SCHEMA}
do_test 2.3 {
S delete
sqlite3session S db main
execsql {
DROP TABLE t2;
CREATE TABLE t2(a, b PRIMARY KEY);
}
S attach t2
execsql {
INSERT INTO t2 VALUES(1, 2);
DROP TABLE t2;
CREATE TABLE t2(a PRIMARY KEY, b);
}
list [catch { S changeset } msg] $msg
} {1 SQLITE_SCHEMA}
do_test 2.4 {
S delete
sqlite3session S db main
execsql {
DROP TABLE t2;
CREATE TABLE t2(a, b PRIMARY KEY);
}
S attach t2
execsql {
INSERT INTO t2 VALUES(1, 2);
DROP TABLE t2;
CREATE TABLE t2(a, b PRIMARY KEY);
}
list [catch { S changeset } msg] $msg
} {0 {}}
S delete
catch { db close }
catch { db2 close }
sqlite3_shutdown
test_sqlite3_log
sqlite3_initialize
finish_test
+68
View File
@@ -0,0 +1,68 @@
# 2011 March 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 the session module.
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source [file join [file dirname [info script]] session_common.tcl]
source $testdir/tester.tcl
ifcapable !session {finish_test; return}
set testprefix session4
do_test 1.0 {
execsql {
CREATE TABLE x(a, b, c, d, e, PRIMARY KEY(c, e));
INSERT INTO x VALUES(65.21, X'28B0', 16.35, NULL, 'doers');
INSERT INTO x VALUES(NULL, 78.49, 2, X'60', -66);
INSERT INTO x VALUES('cathedral', NULL, 35, NULL, X'B220937E80A2D8');
INSERT INTO x VALUES(NULL, 'masking', -91.37, NULL, X'596D');
INSERT INTO x VALUES(19, 'domains', 'espouse', -94, 'throw');
}
sqlite3session S db main
set changeset [changeset_from_sql {
DELETE FROM x WHERE e = -66;
UPDATE x SET a = 'parameterizable', b = 31.8 WHERE c = 35;
INSERT INTO x VALUES(-75.61, -17, 16.85, NULL, X'D73DB02678');
}]
set {} {}
} {}
# This currently causes crashes. sqlite3changeset_invert() does not handle
# corrupt changesets well.
if 0 {
do_test 1.1 {
for {set i 0} {$i < [string length $changeset]} {incr i} {
set before [string range $changeset 0 [expr $i-1]]
set after [string range $changeset [expr $i+1] end]
for {set j 10} {$j < 260} {incr j} {
set x [binary format "a*ca*" $before $j $after]
catch { sqlite3changeset_invert $x }
}
}
} {}
}
do_test 1.2 {
set x [binary format "ca*" 0 [string range $changeset 1 end]]
list [catch { sqlite3changeset_invert $x } msg] $msg
} {1 SQLITE_CORRUPT}
do_test 1.3 {
set x [binary format "ca*" 0 [string range $changeset 1 end]]
list [catch { sqlite3changeset_apply db $x xConflict } msg] $msg
} {1 SQLITE_CORRUPT}
finish_test
+408
View File
@@ -0,0 +1,408 @@
# 2011 April 13
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for the session module.
# Specifically, for the sqlite3changeset_concat() command.
#
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 session5
# Organization of tests:
#
# session5-1.*: Simple tests to check the concat() function produces
# correct results.
#
# session5-2.*: More complicated tests.
#
# session5-3.*: Schema mismatch errors.
#
# session5-4.*: Test the concat cases that indicate that the database
# was modified in between recording of the two changesets
# being concatenated (i.e. two changesets that INSERT rows
# with the same PK values).
#
proc do_concat_test {tn args} {
set subtest 0
foreach sql $args {
incr subtest
sqlite3session S db main ; S attach *
execsql $sql
set c [S changeset]
if {[info commands s_prev] != ""} {
set c_concat [sqlite3changeset_concat $c_prev $c]
set c_two [s_prev changeset]
s_prev delete
set h_concat [changeset_to_list $c_concat]
set h_two [changeset_to_list $c_two]
do_test $tn.$subtest [list set {} $h_concat] $h_two
}
set c_prev $c
rename S s_prev
}
catch { s_prev delete }
}
#-------------------------------------------------------------------------
# Test cases session5-1.* - simple tests.
#
do_execsql_test 1.0 {
CREATE TABLE t1(a PRIMARY KEY, b);
}
do_concat_test 1.1.1 {
INSERT INTO t1 VALUES(1, 'one');
} {
INSERT INTO t1 VALUES(2, 'two');
}
do_concat_test 1.1.2 {
UPDATE t1 SET b = 'five' WHERE a = 1;
} {
UPDATE t1 SET b = 'six' WHERE a = 2;
}
do_concat_test 1.1.3 {
DELETE FROM t1 WHERE a = 1;
} {
DELETE FROM t1 WHERE a = 2;
}
# 1.2.1: INSERT + DELETE -> (none)
# 1.2.2: INSERT + UPDATE -> INSERT
#
# 1.2.3: DELETE + INSERT (matching data) -> (none)
# 1.2.4: DELETE + INSERT (non-matching data) -> UPDATE
#
# 1.2.5: UPDATE + UPDATE (matching data) -> (none)
# 1.2.6: UPDATE + UPDATE (non-matching data) -> UPDATE
# 1.2.7: UPDATE + DELETE -> DELETE
#
do_concat_test 1.2.1 {
INSERT INTO t1 VALUES('x', 'y');
} {
DELETE FROM t1 WHERE a = 'x';
}
do_concat_test 1.2.2 {
INSERT INTO t1 VALUES(5.0, 'five');
} {
UPDATE t1 SET b = 'six' WHERE a = 5.0;
}
do_execsql_test 1.2.3.1 "INSERT INTO t1 VALUES('I', 'one')"
do_concat_test 1.2.3.2 {
DELETE FROM t1 WHERE a = 'I';
} {
INSERT INTO t1 VALUES('I', 'one');
}
do_concat_test 1.2.4 {
DELETE FROM t1 WHERE a = 'I';
} {
INSERT INTO t1 VALUES('I', 'two');
}
do_concat_test 1.2.5 {
UPDATE t1 SET b = 'five' WHERE a = 'I';
} {
UPDATE t1 SET b = 'two' WHERE a = 'I';
}
do_concat_test 1.2.6 {
UPDATE t1 SET b = 'six' WHERE a = 'I';
} {
UPDATE t1 SET b = 'seven' WHERE a = 'I';
}
do_concat_test 1.2.7 {
UPDATE t1 SET b = 'eight' WHERE a = 'I';
} {
DELETE FROM t1 WHERE a = 'I';
}
#-------------------------------------------------------------------------
# Test cases session5-2.* - more complex tests.
#
db function indirect indirect
proc indirect {{x -1}} {
S indirect $x
s_prev indirect $x
}
do_concat_test 2.1 {
CREATE TABLE abc(a, b, c PRIMARY KEY);
INSERT INTO abc VALUES(NULL, NULL, 1);
INSERT INTO abc VALUES('abcdefghijkl', NULL, 2);
} {
DELETE FROM abc WHERE c = 1;
UPDATE abc SET c = 1 WHERE c = 2;
} {
INSERT INTO abc VALUES('abcdefghijkl', NULL, 2);
INSERT INTO abc VALUES(1.0, 2.0, 3);
} {
UPDATE abc SET a = a-1;
} {
CREATE TABLE def(d, e, f, PRIMARY KEY(e, f));
INSERT INTO def VALUES('x', randomblob(11000), 67);
INSERT INTO def SELECT d, e, f+1 FROM def;
INSERT INTO def SELECT d, e, f+2 FROM def;
INSERT INTO def SELECT d, e, f+4 FROM def;
} {
DELETE FROM def WHERE rowid>4;
} {
INSERT INTO def SELECT d, e, f+4 FROM def;
} {
INSERT INTO abc VALUES(22, 44, -1);
} {
UPDATE abc SET c=-2 WHERE c=-1;
UPDATE abc SET c=-3 WHERE c=-2;
} {
UPDATE abc SET c=-4 WHERE c=-3;
} {
UPDATE abc SET a=a+1 WHERE c=-3;
UPDATE abc SET a=a+1 WHERE c=-3;
} {
UPDATE abc SET a=a+1 WHERE c=-3;
UPDATE abc SET a=a+1 WHERE c=-3;
} {
INSERT INTO abc VALUES('one', 'two', 'three');
} {
SELECT indirect(1);
UPDATE abc SET a='one point five' WHERE c = 'three';
} {
SELECT indirect(0);
UPDATE abc SET a='one point six' WHERE c = 'three';
} {
CREATE TABLE x1(a, b, PRIMARY KEY(a));
SELECT indirect(1);
INSERT INTO x1 VALUES(1, 2);
} {
SELECT indirect(1);
UPDATE x1 SET b = 3 WHERE a = 1;
}
catch {db close}
forcedelete test.db
sqlite3 db test.db
do_concat_test 2.2 {
CREATE TABLE t1(a, b, PRIMARY KEY(b));
CREATE TABLE t2(a PRIMARY KEY, b);
INSERT INTO t1 VALUES('string', 1);
INSERT INTO t1 VALUES(4, 2);
INSERT INTO t1 VALUES(X'FFAAFFAAFFAA', 3);
} {
INSERT INTO t2 VALUES('one', 'two');
INSERT INTO t2 VALUES(1, NULL);
UPDATE t1 SET a = 5 WHERE a = 2;
} {
DELETE FROM t2 WHERE a = 1;
UPDATE t1 SET a = 4 WHERE a = 2;
INSERT INTO t2 VALUES('x', 'y');
}
do_test 2.3.0 {
catch {db close}
forcedelete test.db
sqlite3 db test.db
set sql1 ""
set sql2 ""
for {set i 1} {$i < 120} {incr i} {
append sql1 "INSERT INTO x1 VALUES($i*4, $i);"
}
for {set i 1} {$i < 120} {incr i} {
append sql2 "DELETE FROM x1 WHERE a = $i*4;"
}
set {} {}
} {}
do_concat_test 2.3 {
CREATE TABLE x1(a PRIMARY KEY, b)
} $sql1 $sql2 $sql1 $sql2
do_concat_test 2.4 {
CREATE TABLE x2(a PRIMARY KEY, b);
CREATE TABLE x3(a PRIMARY KEY, b);
INSERT INTO x2 VALUES('a', 'b');
INSERT INTO x2 VALUES('x', 'y');
INSERT INTO x3 VALUES('a', 'b');
} {
INSERT INTO x2 VALUES('c', 'd');
INSERT INTO x3 VALUES('e', 'f');
INSERT INTO x3 VALUES('x', 'y');
}
do_concat_test 2.5 {
UPDATE x3 SET b = 'Y' WHERE a = 'x'
} {
DELETE FROM x3 WHERE a = 'x'
} {
DELETE FROM x2 WHERE a = 'a'
} {
INSERT INTO x2 VALUES('a', 'B');
}
for {set k 1} {$k <=10} {incr k} {
do_test 2.6.$k.1 {
drop_all_tables
set sql1 ""
set sql2 ""
for {set i 1} {$i < 120} {incr i} {
append sql1 "INSERT INTO x1 VALUES(randomblob(20+(random()%10)), $i);"
}
for {set i 1} {$i < 120} {incr i} {
append sql2 "DELETE FROM x1 WHERE rowid = $i;"
}
set {} {}
} {}
do_concat_test 2.6.$k {
CREATE TABLE x1(a PRIMARY KEY, b)
} $sql1 $sql2 $sql1 $sql2
}
for {set k 1} {$k <=10} {incr k} {
do_test 2.7.$k.1 {
drop_all_tables
set sql1 ""
set sql2 ""
for {set i 1} {$i < 120} {incr i} {
append sql1 {
INSERT INTO x1 VALUES(
CASE WHEN random()%2 THEN random() ELSE randomblob(20+random()%10) END,
CASE WHEN random()%2 THEN random() ELSE randomblob(20+random()%10) END
);
}
}
for {set i 1} {$i < 120} {incr i} {
append sql2 "DELETE FROM x1 WHERE rowid = $i;"
}
set {} {}
} {}
do_concat_test 2.7.$k {
CREATE TABLE x1(a PRIMARY KEY, b)
} $sql1 $sql2 $sql1 $sql2
}
#-------------------------------------------------------------------------
# Test that schema incompatibilities are detected correctly.
#
# session5-3.1: Incompatible number of columns.
# session5-3.2: Incompatible PK definition.
#
do_test 3.1 {
db close
forcedelete test.db
sqlite3 db test.db
execsql { CREATE TABLE t1(a PRIMARY KEY, b) }
set c1 [changeset_from_sql { INSERT INTO t1 VALUES(1, 2) }]
execsql {
DROP TABLE t1;
CREATE TABLE t1(a PRIMARY KEY, b, c);
}
set c2 [changeset_from_sql { INSERT INTO t1 VALUES(2, 3, 4) }]
list [catch { sqlite3changeset_concat $c1 $c2 } msg] $msg
} {1 SQLITE_SCHEMA}
do_test 3.2 {
db close
forcedelete test.db
sqlite3 db test.db
execsql { CREATE TABLE t1(a PRIMARY KEY, b) }
set c1 [changeset_from_sql { INSERT INTO t1 VALUES(1, 2) }]
execsql {
DROP TABLE t1;
CREATE TABLE t1(a, b PRIMARY KEY);
}
set c2 [changeset_from_sql { INSERT INTO t1 VALUES(2, 3) }]
list [catch { sqlite3changeset_concat $c1 $c2 } msg] $msg
} {1 SQLITE_SCHEMA}
#-------------------------------------------------------------------------
# Test that concat() handles these properly:
#
# session5-4.1: INSERT + INSERT
# session5-4.2: UPDATE + INSERT
# session5-4.3: DELETE + UPDATE
# session5-4.4: DELETE + DELETE
#
proc do_concat_test2 {tn sql1 sqlX sql2 expected} {
sqlite3session S db main ; S attach *
execsql $sql1
set ::c1 [S changeset]
S delete
execsql $sqlX
sqlite3session S db main ; S attach *
execsql $sql2
set ::c2 [S changeset]
S delete
uplevel do_test $tn [list {
changeset_to_list [sqlite3changeset_concat $::c1 $::c2]
}] [list [normalize_list $expected]]
}
drop_all_tables db
do_concat_test2 4.1 {
CREATE TABLE t1(a PRIMARY KEY, b);
INSERT INTO t1 VALUES('key', 'value');
} {
DELETE FROM t1 WHERE a = 'key';
} {
INSERT INTO t1 VALUES('key', 'xxx');
} {
{INSERT t1 0 X. {} {t key t value}}
}
do_concat_test2 4.2 {
UPDATE t1 SET b = 'yyy';
} {
DELETE FROM t1 WHERE a = 'key';
} {
INSERT INTO t1 VALUES('key', 'value');
} {
{UPDATE t1 0 X. {t key t xxx} {{} {} t yyy}}
}
do_concat_test2 4.3 {
DELETE FROM t1 WHERE a = 'key';
} {
INSERT INTO t1 VALUES('key', 'www');
} {
UPDATE t1 SET b = 'valueX' WHERE a = 'key';
} {
{DELETE t1 0 X. {t key t value} {}}
}
do_concat_test2 4.4 {
DELETE FROM t1 WHERE a = 'key';
} {
INSERT INTO t1 VALUES('key', 'ttt');
} {
DELETE FROM t1 WHERE a = 'key';
} {
{DELETE t1 0 X. {t key t valueX} {}}
}
finish_test
+90
View File
@@ -0,0 +1,90 @@
# 2011 July 11
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite sessions extension.
# Specifically, it tests that sessions work when the database is modified
# using incremental blob handles.
#
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 session6
proc do_then_apply_tcl {tcl {dbname main}} {
proc xConflict args { return "OMIT" }
set rc [catch {
sqlite3session S db $dbname
db eval "SELECT name FROM $dbname.sqlite_master WHERE type = 'table'" {
S attach $name
}
eval $tcl
sqlite3changeset_apply db2 [S changeset] xConflict
} msg]
catch { S delete }
if {$rc} {error $msg}
}
test_sqlite3_log x
proc x {args} {puts $args}
forcedelete test.db2
sqlite3 db2 test.db2
do_common_sql {
CREATE TABLE t1(a PRIMARY KEY, b);
CREATE TABLE t2(c PRIMARY KEY, d);
}
# Test a blob update.
#
do_test 1.1 {
do_then_apply_tcl {
db eval { INSERT INTO t1 VALUES(1, 'helloworld') }
db eval { INSERT INTO t2 VALUES(2, 'onetwothree') }
}
compare_db db db2
} {}
do_test 1.2 {
do_then_apply_tcl {
set fd [db incrblob t1 b 1]
puts -nonewline $fd 1234567890
close $fd
}
compare_db db db2
} {}
# Test an attached database.
#
do_test 2.1 {
forcedelete test.db3
file copy test.db2 test.db3
execsql { ATTACH 'test.db3' AS aux; }
do_then_apply_tcl {
set fd [db incrblob aux t2 d 1]
puts -nonewline $fd fourfivesix
close $fd
} aux
sqlite3 db3 test.db3
compare_db db2 db3
} {}
db3 close
db2 close
finish_test
+91
View File
@@ -0,0 +1,91 @@
# 2011 July 13
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.
#
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 session8
proc noop {args} {}
# Like [dbcksum] in tester.tcl. Except this version is not sensitive
# to changes in the value of implicit IPK columns.
#
proc udbcksum {db dbname} {
if {$dbname=="temp"} {
set master sqlite_temp_master
} else {
set master $dbname.sqlite_master
}
set alltab [$db eval "SELECT name FROM $master WHERE type='table'"]
set txt [$db eval "SELECT * FROM $master"]\n
foreach tab $alltab {
append txt [lsort [$db eval "SELECT * FROM $dbname.$tab"]]\n
}
return [md5 $txt]
}
proc do_then_undo {tn sql} {
set ck1 [udbcksum db main]
sqlite3session S db main
S attach *
db eval $sql
set ck2 [udbcksum db main]
set invert [sqlite3changeset_invert [S changeset]]
S delete
sqlite3changeset_apply db $invert noop
set ck3 [udbcksum db main]
set a [expr {$ck1==$ck2}]
set b [expr {$ck1==$ck3}]
uplevel [list do_test $tn.1 "set {} $a" 0]
uplevel [list do_test $tn.2 "set {} $b" 1]
}
do_execsql_test 1.1 {
CREATE TABLE t1(a PRIMARY KEY, b);
INSERT INTO t1 VALUES(1, 2);
INSERT INTO t1 VALUES("abc", "xyz");
}
do_then_undo 1.2 { INSERT INTO t1 VALUES(3, 4); }
do_then_undo 1.3 { DELETE FROM t1 WHERE b=2; }
do_then_undo 1.4 { UPDATE t1 SET b = 3 WHERE a = 1; }
do_execsql_test 2.1 {
CREATE TABLE t2(a, b PRIMARY KEY);
INSERT INTO t2 VALUES(1, 2);
INSERT INTO t2 VALUES('abc', 'xyz');
}
do_then_undo 1.2 { INSERT INTO t2 VALUES(3, 4); }
do_then_undo 1.3 { DELETE FROM t2 WHERE b=2; }
do_then_undo 1.4 { UPDATE t1 SET a = '123' WHERE b = 'xyz'; }
do_execsql_test 3.1 {
CREATE TABLE t3(a, b, c, d, e, PRIMARY KEY(c, e));
INSERT INTO t3 VALUES('x', 45, 0.0, 'abcdef', 12);
INSERT INTO t3 VALUES(45, 0.0, 'abcdef', 12, 'x');
INSERT INTO t3 VALUES(0.0, 'abcdef', 12, 'x', 45);
}
do_then_undo 3.2 { UPDATE t3 SET b=b||b WHERE e!='x' }
do_then_undo 3.3 { UPDATE t3 SET a = 46 }
finish_test
+287
View File
@@ -0,0 +1,287 @@
# 2013 July 04
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# This file tests that the sessions module handles foreign key constraint
# violations when applying changesets as required.
#
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 session9
#--------------------------------------------------------------------
# Basic tests.
#
proc populate_db {} {
drop_all_tables
execsql {
PRAGMA foreign_keys = 1;
CREATE TABLE p1(a PRIMARY KEY, b);
CREATE TABLE c1(a PRIMARY KEY, b REFERENCES p1);
CREATE TABLE c2(a PRIMARY KEY,
b REFERENCES p1 DEFERRABLE INITIALLY DEFERRED
);
INSERT INTO p1 VALUES(1, 'one');
INSERT INTO p1 VALUES(2, 'two');
INSERT INTO p1 VALUES(3, 'three');
INSERT INTO p1 VALUES(4, 'four');
}
}
proc capture_changeset {sql} {
sqlite3session S db main
foreach t [db eval {SELECT name FROM sqlite_master WHERE type='table'}] {
S attach $t
}
execsql $sql
set ret [S changeset]
S delete
return $ret
}
do_test 1.1 {
populate_db
set cc [capture_changeset {
INSERT INTO c1 VALUES('ii', 2);
INSERT INTO c2 VALUES('iii', 3);
}]
set {} {}
} {}
proc xConflict {args} {
lappend ::xConflict {*}$args
return $::conflictret
}
foreach {tn delrow trans conflictargs conflictret} {
1 2 0 {FOREIGN_KEY 1} OMIT
2 3 0 {FOREIGN_KEY 1} OMIT
3 2 1 {FOREIGN_KEY 1} OMIT
4 3 1 {FOREIGN_KEY 1} OMIT
5 2 0 {FOREIGN_KEY 1} ABORT
6 3 0 {FOREIGN_KEY 1} ABORT
7 2 1 {FOREIGN_KEY 1} ABORT
8 3 1 {FOREIGN_KEY 1} ABORT
} {
set A(OMIT) {0 {}}
set A(ABORT) {1 SQLITE_CONSTRAINT}
do_test 1.2.$tn.1 {
populate_db
execsql { DELETE FROM p1 WHERE a=($delrow+0) }
if {$trans} { execsql BEGIN }
set ::xConflict [list]
list [catch {sqlite3changeset_apply db $::cc xConflict} msg] $msg
} $A($conflictret)
do_test 1.2.$tn.2 { set ::xConflict } $conflictargs
set A(OMIT) {1 1}
set A(ABORT) {0 0}
do_test 1.2.$tn.3 {
execsql { SELECT count(*) FROM c1 UNION ALL SELECT count(*) FROM c2 }
} $A($conflictret)
do_test 1.2.$tn.4 { expr ![sqlite3_get_autocommit db] } $trans
do_test 1.2.$tn.5 {
if { $trans } { execsql COMMIT }
} {}
}
#--------------------------------------------------------------------
# Test that closing a transaction clears the defer_foreign_keys flag.
#
foreach {tn open noclose close} {
1 BEGIN {} COMMIT
2 BEGIN {} ROLLBACK
3 {SAVEPOINT one} {} {RELEASE one}
4 {SAVEPOINT one} {ROLLBACK TO one} {RELEASE one}
} {
execsql $open
do_execsql_test 2.$tn.1 { PRAGMA defer_foreign_keys } {0}
do_execsql_test 2.$tn.2 {
PRAGMA defer_foreign_keys = 1;
PRAGMA defer_foreign_keys;
} {1}
execsql $noclose
do_execsql_test 2.$tn.3 { PRAGMA defer_foreign_keys } {1}
execsql $close
do_execsql_test 2.$tn.4 { PRAGMA defer_foreign_keys } {0}
}
#--------------------------------------------------------------------
# Test that a cyclic relationship can be inserted and deleted.
#
# This situation does not come up in practice, but testing it serves to
# show that it does not matter which order parent and child keys
# are processed in internally when applying a changeset.
#
drop_all_tables
do_execsql_test 3.1 {
CREATE TABLE t1(a PRIMARY KEY, b);
CREATE TABLE t2(x PRIMARY KEY, y);
}
# Create changesets as follows:
#
# $cc1 - Insert a row into t1.
# $cc2 - Insert a row into t2.
# $cc - Combination of $cc1 and $cc2.
#
# $ccdel1 - Delete the row from t1.
# $ccdel2 - Delete the row from t2.
# $ccdel - Combination of $cc1 and $cc2.
#
do_test 3.2 {
set cc1 [capture_changeset {
INSERT INTO t1 VALUES('one', 'value one');
}]
set ccdel1 [capture_changeset { DELETE FROM t1; }]
set cc2 [capture_changeset {
INSERT INTO t2 VALUES('value one', 'one');
}]
set ccdel2 [capture_changeset { DELETE FROM t2; }]
set cc [capture_changeset {
INSERT INTO t1 VALUES('one', 'value one');
INSERT INTO t2 VALUES('value one', 'one');
}]
set ccdel [capture_changeset {
DELETE FROM t1;
DELETE FROM t2;
}]
set {} {}
} {}
# Now modify the database schema to create a cyclic foreign key dependency
# between tables t1 and t2. This means that although changesets $cc and
# $ccdel can be applied, none of the others may without violating the
# foreign key constraints.
#
do_test 3.3 {
drop_all_tables
execsql {
CREATE TABLE t1(a PRIMARY KEY, b REFERENCES t2);
CREATE TABLE t2(x PRIMARY KEY, y REFERENCES t1);
}
proc conflict_handler {args} { return "ABORT" }
sqlite3changeset_apply db $cc conflict_handler
execsql {
SELECT * FROM t1;
SELECT * FROM t2;
}
} {one {value one} {value one} one}
do_test 3.3.1 {
list [catch {sqlite3changeset_apply db $::ccdel1 conflict_handler} msg] $msg
} {1 SQLITE_CONSTRAINT}
do_test 3.3.2 {
list [catch {sqlite3changeset_apply db $::ccdel2 conflict_handler} msg] $msg
} {1 SQLITE_CONSTRAINT}
do_test 3.3.4.1 {
list [catch {sqlite3changeset_apply db $::ccdel conflict_handler} msg] $msg
} {0 {}}
do_execsql_test 3.3.4.2 {
SELECT * FROM t1;
SELECT * FROM t2;
} {}
do_test 3.5.1 {
list [catch {sqlite3changeset_apply db $::cc1 conflict_handler} msg] $msg
} {1 SQLITE_CONSTRAINT}
do_test 3.5.2 {
list [catch {sqlite3changeset_apply db $::cc2 conflict_handler} msg] $msg
} {1 SQLITE_CONSTRAINT}
#--------------------------------------------------------------------
# Test that if a change that affects FK processing is not applied
# due to a separate constraint, SQLite does not get confused and
# increment FK counters anyway.
#
drop_all_tables
do_execsql_test 4.1 {
CREATE TABLE p1(x PRIMARY KEY, y);
CREATE TABLE c1(a PRIMARY KEY, b REFERENCES p1);
INSERT INTO p1 VALUES(1,1);
}
do_execsql_test 4.2.1 {
BEGIN;
PRAGMA defer_foreign_keys = 1;
INSERT INTO c1 VALUES('x', 'x');
}
do_catchsql_test 4.2.2 { COMMIT } {1 {FOREIGN KEY constraint failed}}
do_catchsql_test 4.2.3 { ROLLBACK } {0 {}}
do_execsql_test 4.3.1 {
BEGIN;
PRAGMA defer_foreign_keys = 1;
INSERT INTO c1 VALUES(1, 1);
}
do_catchsql_test 4.3.2 {
INSERT INTO c1 VALUES(1, 'x')
} {1 {UNIQUE constraint failed: c1.a}}
do_catchsql_test 4.3.3 { COMMIT } {0 {}}
do_catchsql_test 4.3.4 { BEGIN ; COMMIT } {0 {}}
#--------------------------------------------------------------------
# Test that if a DELETE change cannot be applied due to an
# SQLITE_CONSTRAINT error thrown by a trigger program, things do not
# go awry.
drop_all_tables
reset_db
do_execsql_test 5.1 {
CREATE TABLE x1(x PRIMARY KEY, y);
CREATE TABLE x2(x PRIMARY KEY, y);
INSERT INTO x2 VALUES(1, 1);
INSERT INTO x1 VALUES(1, 1);
}
set ::cc [changeset_from_sql { DELETE FROM x1; }]
do_execsql_test 5.2 {
INSERT INTO x1 VALUES(1, 1);
CREATE TRIGGER tr1 AFTER DELETE ON x1 BEGIN
INSERT INTO x2 VALUES(old.x, old.y);
END;
} {}
proc conflict_handler {args} { return "ABORT" }
do_test 5.3 {
list [catch {sqlite3changeset_apply db $::cc conflict_handler} msg] $msg
} {1 SQLITE_ABORT}
do_execsql_test 5.4 {
SELECT * FROM X1;
} {1 1}
finish_test
+106
View File
@@ -0,0 +1,106 @@
# 2013 July 04
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# This file tests that filter callbacks work as required.
#
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 sessionA
forcedelete test.db2
sqlite3 db2 test.db2
foreach {tn db} {1 db 2 db2} {
do_test 1.$tn.1 {
execsql {
CREATE TABLE t1(a PRIMARY KEY, b);
CREATE TABLE t2(a PRIMARY KEY, b);
CREATE TABLE t3(a PRIMARY KEY, b);
} $db
} {}
}
proc tbl_filter {zTbl} {
return $::table_filter($zTbl)
}
do_test 2.1 {
set ::table_filter(t1) 1
set ::table_filter(t2) 0
set ::table_filter(t3) 1
sqlite3session S db main
S table_filter tbl_filter
execsql {
INSERT INTO t1 VALUES('a', 'b');
INSERT INTO t2 VALUES('c', 'd');
INSERT INTO t3 VALUES('e', 'f');
}
set changeset [S changeset]
S delete
sqlite3changeset_apply db2 $changeset xConflict
execsql {
SELECT * FROM t1;
SELECT * FROM t2;
SELECT * FROM t3;
} db2
} {a b e f}
#-------------------------------------------------------------------------
# Test that filter callbacks passed to sqlite3changeset_apply() are
# invoked correctly.
#
reset_db
do_execsql_test 3.1 {
CREATE TABLE t1(a PRIMARY KEY, b);
CREATE TABLE t2(x PRIMARY KEY, y);
}
do_test 3.2 {
execsql BEGIN
set ::cs [changeset_from_sql {
INSERT INTO t1 VALUES(1, 2);
INSERT INTO t2 VALUES('x', 'y');
}]
execsql ROLLBACK
set {} {}
} {}
proc filter {x y} {
return [string equal $x $y]
}
do_test 3.3 {
sqlite3changeset_apply db $::cs {} [list filter t1]
execsql {
SELECT * FROM t1;
SELECT * FROM t2;
}
} {1 2}
do_test 3.4 {
execsql { DELETE FROM t1 }
sqlite3changeset_apply db $::cs {} [list filter t2]
execsql {
SELECT * FROM t1;
SELECT * FROM t2;
}
} {x y}
finish_test
+518
View File
@@ -0,0 +1,518 @@
# 2014 August 16
#
# 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 sessions SQLite extension.
# Specifically, this file contains tests for "patchset" changes.
#
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 sessionB
#
# 1.*: Test that the blobs returned by the session_patchset() API are
# as expected. Also the sqlite3_changeset_iter functions.
#
# 2.*: Test that patchset blobs are handled by sqlite3changeset_apply().
#
# 3.*: Test that sqlite3changeset_invert() works with patchset blobs.
# Correct behaviour is to return SQLITE_CORRUPT.
proc do_patchset_test {tn session res} {
set r [list]
foreach x $res {lappend r $x}
uplevel do_test $tn [list [subst -nocommands {
set x [list]
sqlite3session_foreach c [$session patchset] { lappend x [set c] }
set x
}]] [list $r]
}
proc do_sql2patchset_test {tn sql res} {
sqlite3session S db main
S attach *
execsql $sql
uplevel [list do_patchset_test $tn S $res]
S delete
}
#-------------------------------------------------------------------------
# Run simple tests of the _patchset() API.
#
do_execsql_test 1.0 {
CREATE TABLE t1(a, b, c, d, PRIMARY KEY(d, a));
INSERT INTO t1 VALUES(1, 2, 3, 4);
INSERT INTO t1 VALUES(5, 6, 7, 8);
INSERT INTO t1 VALUES(9, 10, 11, 12);
}
do_test 1.1 {
sqlite3session S db main
S attach t1
execsql {
INSERT INTO t1 VALUES('w', 'x', 'y', 'z');
DELETE FROM t1 WHERE d=4;
UPDATE t1 SET c = 14 WHERE a=5;
}
} {}
do_patchset_test 1.2 S {
{UPDATE t1 0 X..X {i 5 {} {} {} {} i 8} {{} {} {} {} i 14 {} {}}}
{INSERT t1 0 X..X {} {t w t x t y t z}}
{DELETE t1 0 X..X {i 1 {} {} {} {} i 4} {}}
}
do_test 1.3 {
S delete
} {}
do_sql2patchset_test 1.4 {
DELETE FROM t1;
} {
{DELETE t1 0 X..X {i 5 {} {} {} {} i 8} {}}
{DELETE t1 0 X..X {t w {} {} {} {} t z} {}}
{DELETE t1 0 X..X {i 9 {} {} {} {} i 12} {}}
}
do_sql2patchset_test 1.5 {
INSERT INTO t1 VALUES(X'61626364', NULL, NULL, 4.2);
INSERT INTO t1 VALUES(4.2, NULL, NULL, X'61626364');
} {
{INSERT t1 0 X..X {} {f 4.2 n {} n {} b abcd}}
{INSERT t1 0 X..X {} {b abcd n {} n {} f 4.2}}
}
do_sql2patchset_test 1.6 {
UPDATE t1 SET b=45 WHERE typeof(a)=='blob';
UPDATE t1 SET c='zzzz' WHERE typeof(a)!='blob';
} {
{UPDATE t1 0 X..X {f 4.2 {} {} {} {} b abcd} {{} {} {} {} t zzzz {} {}}}
{UPDATE t1 0 X..X {b abcd {} {} {} {} f 4.2} {{} {} i 45 {} {} {} {}}}
}
do_sql2patchset_test 1.7 {
UPDATE t1 SET b='xyz' WHERE typeof(a)=='blob';
UPDATE t1 SET c='xyz' WHERE typeof(a)!='blob';
UPDATE t1 SET b=45 WHERE typeof(a)=='blob';
UPDATE t1 SET c='zzzz' WHERE typeof(a)!='blob';
} {
}
do_sql2patchset_test 1.8 {
DELETE FROM t1;
} {
{DELETE t1 0 X..X {f 4.2 {} {} {} {} b abcd} {}}
{DELETE t1 0 X..X {b abcd {} {} {} {} f 4.2} {}}
}
#-------------------------------------------------------------------------
# Run simple tests of _apply() with patchset objects.
#
reset_db
proc noop {args} { error $args }
proc exec_rollback_replay {sql} {
sqlite3session S db main
S attach *
execsql BEGIN
execsql $sql
set patchset [S patchset]
S delete
execsql ROLLBACK
sqlite3changeset_apply db $patchset noop
}
do_execsql_test 2.0 {
CREATE TABLE t2(a, b, c, d, PRIMARY KEY(b,c));
CREATE TABLE t3(w, x, y, z, PRIMARY KEY(w));
}
do_test 2.1 {
exec_rollback_replay {
INSERT INTO t2 VALUES(1, 2, 3, 4);
INSERT INTO t2 VALUES('w', 'x', 'y', 'z');
}
execsql { SELECT * FROM t2 }
} {1 2 3 4 w x y z}
do_test 2.2 {
exec_rollback_replay {
DELETE FROM t2 WHERE a=1;
UPDATE t2 SET d = 'a';
}
execsql { SELECT * FROM t2 }
} {w x y a}
#-------------------------------------------------------------------------
# sqlite3changeset_invert()
#
reset_db
do_execsql_test 3.1 { CREATE TABLE t1(x PRIMARY KEY, y) }
do_test 3.2 {
sqlite3session S db main
S attach *
execsql { INSERT INTO t1 VALUES(1, 2) }
set patchset [S patchset]
S delete
list [catch { sqlite3changeset_invert $patchset } msg] [set msg]
} {1 SQLITE_CORRUPT}
#-------------------------------------------------------------------------
# sqlite3changeset_concat()
#
reset_db
proc do_patchconcat_test {tn args} {
set bRevert 0
if {[lindex $args 0] == "-revert"} {
set bRevert 1
set args [lrange $args 1 end]
}
set nSql [expr [llength $args]-1]
set res [lindex $args $nSql]
set patchlist [list]
execsql BEGIN
if {$bRevert} { execsql { SAVEPOINT x } }
foreach sql [lrange $args 0 end-1] {
sqlite3session S db main
S attach *
execsql $sql
lappend patchlist [S patchset]
S delete
if {$bRevert} { execsql { ROLLBACK TO x } }
}
execsql ROLLBACK
set patch [lindex $patchlist 0]
foreach p [lrange $patchlist 1 end] {
set patch [sqlite3changeset_concat $patch $p]
}
set x [list]
sqlite3session_foreach c $patch { lappend x $c }
uplevel [list do_test $tn [list set {} $x] [list {*}$res]]
}
do_execsql_test 4.1.1 {
CREATE TABLE t1(x PRIMARY KEY, y, z);
}
do_patchconcat_test 4.1.2 {
INSERT INTO t1 VALUES(1, 2, 3);
} {
INSERT INTO t1 VALUES(4, 5, 6);
} {
{INSERT t1 0 X.. {} {i 1 i 2 i 3}}
{INSERT t1 0 X.. {} {i 4 i 5 i 6}}
}
do_execsql_test 4.2.1 {
INSERT INTO t1 VALUES(1, 2, 3);
INSERT INTO t1 VALUES(4, 5, 6);
}
do_patchconcat_test 4.2.2 {
UPDATE t1 SET z = 'abc' WHERE x=1
} {
UPDATE t1 SET z = 'def' WHERE x=4
} {
{UPDATE t1 0 X.. {i 1 {} {} {} {}} {{} {} {} {} t abc}}
{UPDATE t1 0 X.. {i 4 {} {} {} {}} {{} {} {} {} t def}}
}
do_patchconcat_test 4.2.3 {
DELETE FROM t1 WHERE x=1;
} {
DELETE FROM t1 WHERE x=4;
} {
{DELETE t1 0 X.. {i 1 {} {} {} {}} {}}
{DELETE t1 0 X.. {i 4 {} {} {} {}} {}}
}
do_execsql_test 4.3.1 {
CREATE TABLE t2(a, b, c, d, PRIMARY KEY(c, b));
INSERT INTO t2 VALUES('.', 1, 1, '.');
INSERT INTO t2 VALUES('.', 1, 2, '.');
INSERT INTO t2 VALUES('.', 2, 1, '.');
INSERT INTO t2 VALUES('.', 2, 2, '.');
}
# INSERT + INSERT
do_patchconcat_test 4.3.2 -revert {
INSERT INTO t2 VALUES('a', 'a', 'a', 'a');
} {
INSERT INTO t2 VALUES('b', 'a', 'a', 'b');
} {
{INSERT t2 0 .XX. {} {t a t a t a t a}}
}
# INSERT + DELETE
do_patchconcat_test 4.3.3 {
INSERT INTO t2 VALUES('a', 'a', 'a', 'a');
} {
DELETE FROM t2 WHERE c = 'a';
} {
}
# INSERT + UPDATE
do_patchconcat_test 4.3.4 {
INSERT INTO t2 VALUES('a', 'a', 'a', 'a');
} {
UPDATE t2 SET d = 'b' WHERE c='a';
} {
{INSERT t2 0 .XX. {} {t a t a t a t b}}
}
# UPDATE + UPDATE
do_patchconcat_test 4.3.5 {
UPDATE t2 SET a = 'a' WHERE c=1 AND b=2;
} {
UPDATE t2 SET d = 'd' WHERE c=1 AND b=2;
} {
{UPDATE t2 0 .XX. {{} {} i 2 i 1 {} {}} {t a {} {} {} {} t d}}
}
# UPDATE + DELETE
do_patchconcat_test 4.3.6 {
UPDATE t2 SET a = 'a' WHERE c=1 AND b=2;
} {
DELETE FROM t2 WHERE c=1 AND b=2;
} {
{DELETE t2 0 .XX. {{} {} i 2 i 1 {} {}} {}}
}
# DELETE + INSERT
do_patchconcat_test 4.3.7 {
DELETE FROM t2 WHERE b=1;
} {
INSERT INTO t2 VALUES('x', 1, 2, '.');
} {
{DELETE t2 0 .XX. {{} {} i 1 i 1 {} {}} {}}
{UPDATE t2 0 .XX. {{} {} i 1 i 2 {} {}} {t x {} {} {} {} t .}}
}
# DELETE + UPDATE
do_patchconcat_test 4.3.8 -revert {
DELETE FROM t2 WHERE b=1 AND c=2;
} {
UPDATE t2 SET a=5 WHERE b=1 AND c=2;
} {
{DELETE t2 0 .XX. {{} {} i 1 i 2 {} {}} {}}
}
# DELETE + UPDATE
do_patchconcat_test 4.3.9 -revert {
DELETE FROM t2 WHERE b=1 AND c=2;
} {
DELETE FROM t2 WHERE b=1;
} {
{DELETE t2 0 .XX. {{} {} i 1 i 1 {} {}} {}}
{DELETE t2 0 .XX. {{} {} i 1 i 2 {} {}} {}}
}
#-------------------------------------------------------------------------
# More rigorous testing of the _patchset(), _apply and _concat() APIs.
#
# The inputs to each test are a populate database and a list of DML
# statements. This test determines that the final database is the same
# if:
#
# 1) the statements are executed directly on the database.
#
# 2) a single patchset is collected while executing the statements and
# then applied to a copy of the original database file.
#
# 3) individual patchsets are collected for statement while executing
# them and concatenated together before being applied to a copy of
# the original database. The concatenation is done in a couple of
# different ways - linear, pairwise etc.
#
# All tests, as it happens, are run with both changesets and patchsets.
# But the focus is on patchset capabilities.
#
# Return a checksum of the contents of the database file. Implicit IPK
# columns are not included in the checksum - just modifying rowids does
# not change the database checksum.
#
proc databasecksum {db} {
set alltab [$db eval {SELECT name FROM sqlite_master WHERE type='table'}]
foreach tab $alltab {
$db eval "SELECT * FROM $tab LIMIT 1" res { }
set slist [list]
foreach col [lsort $res(*)] {
lappend slist "quote($col)"
}
set sql "SELECT [join $slist ,] FROM $tab"
append txt "[lsort [$db eval $sql]]\n"
}
return [md5 $txt]
}
proc do_patchset_test {tn tstcmd lSql} {
if {$tstcmd != "patchset" && $tstcmd != "changeset"} {
error "have $tstcmd: must be patchset or changeset"
}
foreach fname {test.db2 test.db3 test.db4 test.db5} {
forcedelete $fname
forcecopy test.db $fname
}
# Execute the SQL statements on [db]. Collect a patchset for each
# individual statement, as well as a single patchset for the entire
# operation.
sqlite3session S db main
S attach *
foreach sql $lSql {
sqlite3session T db main
T attach *
db eval $sql
lappend lPatch [T $tstcmd]
T delete
}
set patchset [S $tstcmd]
S delete
# Calculate a checksum for the final database.
set cksum [databasecksum db]
# 1. Apply the single large patchset to test.db2
sqlite3 db2 test.db2
sqlite3changeset_apply db2 $patchset noop
uplevel [list do_test $tn.1 { databasecksum db2 } $cksum ]
db2 close
# 2. Apply each of the single-statement patchsets to test.db3
sqlite3 db2 test.db3
foreach p $lPatch {
sqlite3changeset_apply db2 $p noop
}
uplevel [list do_test $tn.2 { databasecksum db2 } $cksum ]
db2 close
# 3. Concatenate all single-statement patchsets into a single large
# patchset, then apply it to test.db4.
#
sqlite3 db2 test.db4
set big ""
foreach p $lPatch {
set big [sqlite3changeset_concat $big $p]
}
sqlite3changeset_apply db2 $big noop
uplevel [list do_test $tn.3 { databasecksum db2 } $cksum ]
db2 close
# 4. Concatenate all single-statement patchsets pairwise into a single
# large patchset, then apply it to test.db5. Pairwise concatenation:
#
# a b c d e f g h i j k
# -> {a b} {c d} {e f} {g h} {i j} k
# -> {a b c d} {e f g h} {i j k}
# -> {a b c d e f g h} {i j k}
# -> {a b c d e f g h i j k}
# -> APPLY!
#
sqlite3 db2 test.db5
set L $lPatch
while {[llength $L] > 1} {
set O [list]
for {set i 0} {$i < [llength $L]} {incr i 2} {
if {$i==[llength $L]-1} {
lappend O [lindex $L $i]
} else {
set i1 [expr $i+1]
lappend O [sqlite3changeset_concat [lindex $L $i] [lindex $L $i1]]
}
}
set L $O
}
sqlite3changeset_apply db2 [lindex $L 0] noop
uplevel [list do_test $tn.4 { databasecksum db2 } $cksum ]
db2 close
}
proc do_patchset_changeset_test {tn initsql args} {
foreach tstcmd {patchset changeset} {
reset_db
execsql $initsql
set x 0
foreach sql $args {
incr x
set lSql [split $sql ";"]
uplevel [list do_patchset_test $tn.$tstcmd.$x $tstcmd $lSql]
}
}
}
do_patchset_changeset_test 5.1 {
CREATE TABLE t1(a PRIMARY KEY, b, c);
INSERT INTO t1 VALUES(1, 2, 3);
} {
INSERT INTO t1 VALUES(4, 5, 6);
DELETE FROM t1 WHERE a=1;
} {
INSERT INTO t1 VALUES(7, 8, 9);
UPDATE t1 SET c = 5;
INSERT INTO t1 VALUES(10, 11, 12);
UPDATE t1 SET c = 6;
INSERT INTO t1 VALUES(13, 14, 15);
} {
UPDATE t1 SET c=c+1;
DELETE FROM t1 WHERE (a%2);
}
do_patchset_changeset_test 5.2 {
CREATE TABLE t1(a PRIMARY KEY, b, c);
CREATE TABLE t2(a, b, c, d, PRIMARY KEY(c, b));
} {
INSERT INTO t1 VALUES(x'00', 0, 'zero');
INSERT INTO t1 VALUES(x'01', 1, 'one');
INSERT INTO t1 VALUES(x'02', 4, 'four');
INSERT INTO t1 VALUES(x'03', 9, 'nine');
INSERT INTO t1 VALUES(x'04', 16, 'sixteen');
INSERT INTO t1 VALUES(x'05', 25, 'twenty-five');
} {
UPDATE t1 SET a = b WHERE b<=4;
INSERT INTO t2 SELECT NULL, * FROM t1;
DELETE FROM t1 WHERE b=25;
} {
DELETE FROM t2;
INSERT INTO t2 SELECT NULL, * FROM t1;
DELETE FROM t1;
INSERT INTO t1 SELECT b, c, d FROM t2;
UPDATE t1 SET b = b+1;
UPDATE t1 SET b = b+1;
UPDATE t1 SET b = b+1;
}
set initsql { CREATE TABLE t1(a, b, c, PRIMARY KEY(c, b)); }
for {set i 0} {$i < 1000} {incr i} {
append insert "INSERT INTO t1 VALUES($i, $i, $i);"
append delete "DELETE FROM t1 WHERE b=$i;"
}
do_patchset_changeset_test 5.3 \
$initsql $insert $delete \
$insert $delete \
"$insert $delete" \
$delete
finish_test
+190
View File
@@ -0,0 +1,190 @@
# 2014 August 16
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
if {![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 sessionC
#-------------------------------------------------------------------------
# Test the outcome of a DELETE operation made as part of applying a
# changeset failing with SQLITE_CONSTRAINT. This may happen if an
# ON DELETE RESTRICT foreign key action is triggered, or if a trigger
# program raises a constraint somehow.
#
do_execsql_test 1.0 {
PRAGMA foreign_keys = 1;
CREATE TABLE p(a PRIMARY KEY, b, c);
CREATE TABLE c(d PRIMARY KEY, e REFERENCES p ON DELETE RESTRICT);
INSERT INTO p VALUES('one', 1, 1);
INSERT INTO p VALUES('two', 2, 2);
INSERT INTO p VALUES('three', 3, 3);
INSERT INTO c VALUES(1, 'one');
INSERT INTO c VALUES(3, 'three');
}
do_test 1.1 {
execsql BEGIN
set C [changeset_from_sql {
INSERT INTO c VALUES(4, 'one');
DELETE FROM p WHERE a='two';
}]
execsql ROLLBACK
execsql {
INSERT INTO c VALUES(2, 'two');
}
} {}
do_test 1.2.1 {
proc xConflict {args} { return "ABORT" }
catch { sqlite3changeset_apply db $C xConflict } msg
set msg
} {SQLITE_ABORT}
do_execsql_test 1.2.2 { SELECT * FROM c } {1 one 3 three 2 two}
do_test 1.3.1 {
proc xConflict {args} { return "OMIT" }
catch { sqlite3changeset_apply db $C xConflict } msg
set msg
} {}
do_execsql_test 1.3.2 { SELECT * FROM c } {1 one 3 three 2 two 4 one}
do_execsql_test 1.3.3 {
SELECT * FROM p;
} {one 1 1 two 2 2 three 3 3}
#-------------------------------------------------------------------------
# Test that concatenating a changeset with a patchset does not work.
# Any attempt to do so returns SQLITE_ERROR.
#
reset_db
do_execsql_test 2.0 {
CREATE TABLE x1(t, v PRIMARY KEY);
INSERT INTO x1 VALUES(12, 55);
INSERT INTO x1 VALUES(55, 14);
}
do_test 2.1 {
execsql BEGIN
sqlite3session S1 db main
S1 attach *
execsql {
UPDATE x1 SET t=13 WHERE v=55;
INSERT INTO x1 VALUES(99, 123);
}
set patchset [S1 patchset]
S1 delete
sqlite3session S1 db main
S1 attach *
execsql {
UPDATE x1 SET t=56 WHERE v=14;
INSERT INTO x1 VALUES(22, 998);
}
set changeset [S1 changeset]
S1 delete
execsql ROLLBACK
} {}
do_test 2.2 {
set rc [catch { sqlite3changeset_concat $patchset $changeset } msg]
list $rc $msg
} {1 SQLITE_ERROR}
do_test 2.3 {
set rc [catch { sqlite3changeset_concat $changeset $patchset } msg]
list $rc $msg
} {1 SQLITE_ERROR}
do_test 2.4 {
set rc [catch { sqlite3changeset_concat {} $patchset } msg]
list $rc $msg
} [list 0 $patchset]
do_test 2.5 {
set rc [catch { sqlite3changeset_concat $patchset {} } msg]
list $rc $msg
} [list 0 $patchset]
do_test 2.6 {
set rc [catch { sqlite3changeset_concat {} $changeset } msg]
list $rc $msg
} [list 0 $changeset]
do_test 2.7 {
set rc [catch { sqlite3changeset_concat $changeset {} } msg]
list $rc $msg
} [list 0 $changeset]
do_test 2.8 {
set rc [catch { sqlite3changeset_concat {} {} } msg]
list $rc $msg
} [list 0 {}]
#-------------------------------------------------------------------------
# Test that the xFilter argument to sqlite3changeset_apply() works.
#
reset_db
do_execsql_test 3.0 {
CREATE TABLE t1(a PRIMARY KEY, b);
CREATE TABLE t2(a PRIMARY KEY, b);
CREATE TABLE t3(a PRIMARY KEY, b);
}
do_test 3.1 {
execsql BEGIN
set changeset [changeset_from_sql {
INSERT INTO t1 VALUES(1, 1);
INSERT INTO t2 VALUES(2, 2);
INSERT INTO t3 VALUES(3, 3);
}]
execsql ROLLBACK
} {}
do_test 3.2 {
proc xFilter {zName} {
if {$zName == "t1"} { return 1 }
return 0
}
sqlite3changeset_apply db $changeset noop xFilter
execsql {
SELECT * FROM t1;
SELECT * FROM t2;
SELECT * FROM t3;
}
} {1 1}
do_test 3.3 {
proc xFilter {zName} {
if {$zName == "t3"} { return 1 }
return 0
}
sqlite3changeset_apply db $changeset noop xFilter
execsql {
SELECT * FROM t1;
SELECT * FROM t2;
SELECT * FROM t3;
}
} {1 1 3 3}
finish_test
+150
View File
@@ -0,0 +1,150 @@
# 2014 August 16
#
# 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 focuses on the sqlite3session_diff() function.
#
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 sessionD
proc scksum {db dbname} {
if {$dbname=="temp"} {
set master sqlite_temp_master
} else {
set master $dbname.sqlite_master
}
set alltab [$db eval "SELECT name FROM $master WHERE type='table'"]
set txt [$db eval "SELECT * FROM $master ORDER BY type,name,sql"]
foreach tab $alltab {
set cols [list]
db eval "PRAGMA $dbname.table_info = $tab" x {
lappend cols "quote($x(name))"
}
set cols [join $cols ,]
append txt [db eval "SELECT $cols FROM $tab ORDER BY $cols"]
}
return [md5 $txt]
}
proc do_diff_test {tn setup} {
reset_db
forcedelete test.db2
execsql { ATTACH 'test.db2' AS aux }
execsql $setup
sqlite3session S db main
foreach tbl [db eval {SELECT name FROM sqlite_master WHERE type='table'}] {
S attach $tbl
S diff aux $tbl
}
set C [S changeset]
S delete
sqlite3 db2 test.db2
sqlite3changeset_apply db2 $C ""
uplevel do_test $tn.1 [list {execsql { PRAGMA integrity_check } db2}] ok
db2 close
set cksum [scksum db main]
uplevel do_test $tn.2 [list {scksum db aux}] [list $cksum]
}
forcedelete test.db2
do_execsql_test 1.0 {
CREATE TABLE t2(a PRIMARY KEY, b);
INSERT INTO t2 VALUES(1, 'one');
INSERT INTO t2 VALUES(2, 'two');
ATTACH 'test.db2' AS aux;
CREATE TABLE aux.t2(a PRIMARY KEY, b);
}
do_test 1.1 {
sqlite3session S db main
S attach t2
S diff aux t2
set C [S changeset]
S delete
} {}
do_test 1.2 {
sqlite3 db2 test.db2
sqlite3changeset_apply db2 $C ""
db2 close
db eval { SELECT * FROM aux.t2 }
} {1 one 2 two}
do_diff_test 2.1 {
CREATE TABLE aux.t1(x, y, PRIMARY KEY(y));
CREATE TABLE t1(x, y, PRIMARY KEY(y));
INSERT INTO t1 VALUES(1, 2);
INSERT INTO t1 VALUES(NULL, 'xyz');
INSERT INTO t1 VALUES(4.5, 5.5);
}
do_diff_test 2.2 {
CREATE TABLE aux.t1(x, y, PRIMARY KEY(y));
CREATE TABLE t1(x, y, PRIMARY KEY(y));
INSERT INTO aux.t1 VALUES(1, 2);
INSERT INTO aux.t1 VALUES(NULL, 'xyz');
INSERT INTO aux.t1 VALUES(4.5, 5.5);
}
do_diff_test 2.3 {
CREATE TABLE aux.t1(a PRIMARY KEY, b TEXT);
CREATE TABLE t1(a PRIMARY KEY, b TEXT);
INSERT INTO aux.t1 VALUES(1, 'one');
INSERT INTO aux.t1 VALUES(2, 'two');
INSERT INTO aux.t1 VALUES(3, 'three');
INSERT INTO t1 VALUES(1, 'I');
INSERT INTO t1 VALUES(2, 'two');
INSERT INTO t1 VALUES(3, 'III');
}
do_diff_test 2.4 {
CREATE TABLE aux.t1(a, b, c, d, PRIMARY KEY(c, b, a));
CREATE TABLE t1(a, b, c, d, PRIMARY KEY(c, b, a));
INSERT INTO t1 VALUES('hvkzyipambwdqlvwv','',-458331.50,X'DA51ED5E84');
INSERT INTO t1 VALUES(X'C5C6B5DD','jjxrath',40917,830244);
INSERT INTO t1 VALUES(-204877.54,X'1704C253D5F3AFA8',155120.88,NULL);
INSERT INTO t1
VALUES('ckmqmzoeuvxisxqy',X'EB5A5D3A1DD22FD1','tidhjcbvbppdt',-642987.37);
INSERT INTO t1 VALUES(-851726,-161992,-469943,-159541);
INSERT INTO t1 VALUES(X'4A6A667F858938',185083,X'7A',NULL);
INSERT INTO aux.t1 VALUES(415075.74,'auawczkb',X'',X'57B4FAAF2595');
INSERT INTO aux.t1 VALUES(727637,711560,-181340,'hphuo');
INSERT INTO aux.t1
VALUES(-921322.81,662959,'lvlgwdgxaurr','ajjrzrbhqflsutnymgc');
INSERT INTO aux.t1 VALUES(-146061,-377892,X'4E','gepvpvvuhszpxabbb');
INSERT INTO aux.t1 VALUES(-851726,-161992,-469943,-159541);
INSERT INTO aux.t1 VALUES(X'4A6A667F858938',185083,X'7A',NULL);
INSERT INTO aux.t1 VALUES(-204877.54,X'1704C253D5F3AFA8',155120.88, 4);
INSERT INTO aux.t1
VALUES('ckmqmzoeuvxisxqy',X'EB5A5D3A1DD22FD1','tidgtsplhjcbvbppdt',-642987.3);
}
finish_test
+135
View File
@@ -0,0 +1,135 @@
proc do_conflict_test {tn args} {
proc xConflict {args} {
lappend ::xConflict $args
return ""
}
proc bgerror {args} { set ::background_error $args }
set O(-tables) [list]
set O(-sql) [list]
set O(-conflicts) [list]
array set V $args
foreach key [array names V] {
if {![info exists O($key)]} {error "no such option: $key"}
}
array set O $args
sqlite3session S db main
foreach t $O(-tables) { S attach $t }
execsql $O(-sql)
set ::xConflict [list]
sqlite3changeset_apply db2 [S changeset] xConflict
set conflicts [list]
foreach c $O(-conflicts) {
lappend conflicts $c
}
after 1 {set go 1}
vwait go
uplevel do_test $tn [list { set ::xConflict }] [list $conflicts]
S delete
}
proc do_common_sql {sql} {
execsql $sql db
execsql $sql db2
}
proc changeset_from_sql {sql {dbname main}} {
set rc [catch {
sqlite3session S db $dbname
db eval "SELECT name FROM $dbname.sqlite_master WHERE type = 'table'" {
S attach $name
}
db eval $sql
S changeset
} changeset]
catch { S delete }
if {$rc} {
error $changeset
}
return $changeset
}
proc do_then_apply_sql {sql {dbname main}} {
proc xConflict args { return "OMIT" }
set rc [catch {
sqlite3session S db $dbname
db eval "SELECT name FROM $dbname.sqlite_master WHERE type = 'table'" {
S attach $name
}
db eval $sql
sqlite3changeset_apply db2 [S changeset] xConflict
} msg]
catch { S delete }
if {$rc} {error $msg}
}
proc do_iterator_test {tn tbl_list sql res} {
sqlite3session S db main
if {[llength $tbl_list]==0} { S attach * }
foreach t $tbl_list {S attach $t}
execsql $sql
set r [list]
foreach v $res { lappend r $v }
set x [list]
sqlite3session_foreach c [S changeset] { lappend x $c }
uplevel do_test $tn [list [list set {} $x]] [list $r]
S delete
}
# Compare the contents of all tables in [db1] and [db2]. Throw an error if
# they are not identical, or return an empty string if they are.
#
proc compare_db {db1 db2} {
set sql {SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name}
set lot1 [$db1 eval $sql]
set lot2 [$db2 eval $sql]
if {$lot1 != $lot2} {
puts $lot1
puts $lot2
error "databases contain different tables"
}
foreach tbl $lot1 {
set col1 [list]
set col2 [list]
$db1 eval "PRAGMA table_info = $tbl" { lappend col1 $name }
$db2 eval "PRAGMA table_info = $tbl" { lappend col2 $name }
if {$col1 != $col2} { error "table $tbl schema mismatch" }
set sql "SELECT * FROM $tbl ORDER BY [join $col1 ,]"
set data1 [$db1 eval $sql]
set data2 [$db2 eval $sql]
if {$data1 != $data2} {
puts "$data1"
puts "$data2"
error "table $tbl data mismatch"
}
}
return ""
}
proc changeset_to_list {c} {
set list [list]
sqlite3session_foreach elem $c { lappend list $elem }
lsort $list
}
+587
View File
@@ -0,0 +1,587 @@
# 2011 Mar 21
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# The focus of this file is testing the session module.
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source [file join [file dirname [info script]] session_common.tcl]
source $testdir/tester.tcl
set testprefix sessionfault
forcedelete test.db2
sqlite3 db2 test.db2
do_common_sql {
CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b));
INSERT INTO t1 VALUES(1, 2, 3);
INSERT INTO t1 VALUES(4, 5, 6);
}
faultsim_save_and_close
db2 close
#-------------------------------------------------------------------------
# Test OOM error handling when collecting and applying a simple changeset.
#
# Test 1.1 attaches tables individually by name to the session object.
# Whereas test 1.2 passes NULL to sqlite3session_attach() to attach all
# tables.
#
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 t1 VALUES('a string value', 8, 9);
UPDATE t1 SET c = 10 WHERE a = 1;
DELETE FROM t1 WHERE a = 4;
}
} -test {
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
faultsim_integrity_check
if {$testrc==0} { compare_db db db2 }
}
do_faultsim_test 1.2 -faults oom-* -prep {
catch {db2 close}
catch {db close}
faultsim_restore_and_reopen
} -body {
sqlite3session S db main
S attach *
execsql {
INSERT INTO t1 VALUES('a string value', 8, 9);
UPDATE t1 SET c = 10 WHERE a = 1;
DELETE FROM t1 WHERE a = 4;
}
set ::changeset [S changeset]
set {} {}
} -test {
catch { S delete }
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
faultsim_integrity_check
if {$testrc==0} {
proc xConflict {args} { return "OMIT" }
sqlite3 db2 test.db2
sqlite3changeset_apply db2 $::changeset xConflict
compare_db db db2
}
}
#-------------------------------------------------------------------------
# The following block of tests - 2.* - are designed to check
# the handling of faults in the sqlite3changeset_apply() function.
#
catch {db close}
catch {db2 close}
forcedelete test.db2 test.db
sqlite3 db2 test.db2
sqlite3 db test.db
do_common_sql {
CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b));
INSERT INTO t1 VALUES('apple', 'orange', 'pear');
CREATE TABLE t2(x PRIMARY KEY, y);
}
db2 close
faultsim_save_and_close
foreach {tn conflict_policy sql sql2} {
1 OMIT { INSERT INTO t1 VALUES('one text', 'two text', X'00ff00') } {}
2 OMIT { DELETE FROM t1 WHERE a = 'apple' } {}
3 OMIT { UPDATE t1 SET c = 'banana' WHERE b = 'orange' } {}
4 REPLACE { INSERT INTO t2 VALUES('keyvalue', 'value 1') } {
INSERT INTO t2 VALUES('keyvalue', 'value 2');
}
} {
proc xConflict args [list return $conflict_policy]
do_faultsim_test 2.$tn -faults oom-transient -prep {
catch {db2 close}
catch {db close}
faultsim_restore_and_reopen
set ::changeset [changeset_from_sql $::sql]
sqlite3 db2 test.db2
sqlite3_db_config_lookaside db2 0 0 0
execsql $::sql2 db2
} -body {
sqlite3changeset_apply db2 $::changeset xConflict
} -test {
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
faultsim_integrity_check
if {$testrc==0} { compare_db db db2 }
}
}
#-------------------------------------------------------------------------
# This test case is designed so that a malloc() failure occurs while
# resizing the session object hash-table from 256 to 512 buckets. This
# is not an error, just a sub-optimal condition.
#
do_faultsim_test 3 -faults oom-* -prep {
catch {db2 close}
catch {db close}
faultsim_restore_and_reopen
sqlite3 db2 test.db2
sqlite3session S db main
S attach t1
execsql { BEGIN }
for {set i 0} {$i < 125} {incr i} {
execsql {INSERT INTO t1 VALUES(10+$i, 10+$i, 10+$i)}
}
} -body {
for {set i 125} {$i < 133} {incr i} {
execsql {INSERT INTO t1 VALUES(10+$i, 10+$i, 1-+$i)}
}
S changeset
set {} {}
} -test {
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
if {$testrc==0} {
sqlite3changeset_apply db2 [S changeset] xConflict
compare_db db db2
}
catch { S delete }
faultsim_integrity_check
}
catch { db close }
catch { db2 close }
forcedelete test.db2 test.db
sqlite3 db2 test.db2
sqlite3 db test.db
proc xConflict {op tbl type args} {
if { $type=="CONFLICT" || $type=="DATA" } {
return "REPLACE"
}
return "OMIT"
}
do_test 4.0 {
execsql {
PRAGMA encoding = 'utf16';
CREATE TABLE t1(a PRIMARY KEY, b);
INSERT INTO t1 VALUES(5, 32);
}
execsql {
PRAGMA encoding = 'utf16';
CREATE TABLE t1(a PRIMARY KEY, b NOT NULL);
INSERT INTO t1 VALUES(1, 2);
INSERT INTO t1 VALUES(2, 4);
INSERT INTO t1 VALUES(4, 16);
} db2
} {}
faultsim_save_and_close
db2 close
do_faultsim_test 4 -faults oom-* -prep {
catch {db2 close}
catch {db close}
faultsim_restore_and_reopen
sqlite3 db2 test.db2
sqlite3session S db main
S attach t1
execsql {
INSERT INTO t1 VALUES(1, 45);
INSERT INTO t1 VALUES(2, 55);
INSERT INTO t1 VALUES(3, 55);
UPDATE t1 SET a = 4 WHERE a = 5;
}
} -body {
sqlite3changeset_apply db2 [S changeset] xConflict
} -test {
catch { S delete }
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
if {$testrc==0} { compare_db db db2 }
}
#-------------------------------------------------------------------------
# This block of tests verifies that OOM faults in the
# sqlite3changeset_invert() function are handled correctly.
#
catch {db close}
catch {db2 close}
forcedelete test.db
sqlite3 db test.db
execsql {
CREATE TABLE t1(a, b, PRIMARY KEY(b));
CREATE TABLE t2(a PRIMARY KEY, b);
INSERT INTO t1 VALUES('string', 1);
INSERT INTO t1 VALUES(4, 2);
INSERT INTO t1 VALUES(X'FFAAFFAAFFAA', 3);
}
set changeset [changeset_from_sql {
INSERT INTO t1 VALUES('xxx', 'yyy');
DELETE FROM t1 WHERE a = 'string';
UPDATE t1 SET a = 20 WHERE b = 2;
}]
db close
do_faultsim_test 5.1 -faults oom* -body {
set ::inverse [sqlite3changeset_invert $::changeset]
set {} {}
} -test {
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
if {$testrc==0} {
set x [list]
sqlite3session_foreach c $::inverse { lappend x $c }
foreach c {
{DELETE t1 0 .X {t xxx t yyy} {}}
{INSERT t1 0 .X {} {t string i 1}}
{UPDATE t1 0 .X {i 20 i 2} {i 4 {} {}}}
} { lappend y $c }
if {$x != $y} { error "changeset no good" }
}
}
catch {db close}
catch {db2 close}
forcedelete test.db
sqlite3 db test.db
execsql {
CREATE TABLE t2(a PRIMARY KEY, b);
INSERT INTO t2 VALUES(1, 'abc');
INSERT INTO t2 VALUES(2, 'def');
}
set changeset [changeset_from_sql {
UPDATE t2 SET b = (b || b || b || b);
UPDATE t2 SET b = (b || b || b || b);
UPDATE t2 SET b = (b || b || b || b);
UPDATE t2 SET b = (b || b || b || b);
}]
db close
set abc [string repeat abc 256]
set def [string repeat def 256]
do_faultsim_test 5.2 -faults oom-tra* -body {
set ::inverse [sqlite3changeset_invert $::changeset]
set {} {}
} -test {
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
if {$testrc==0} {
set x [list]
sqlite3session_foreach c $::inverse { lappend x $c }
foreach c "
{UPDATE t2 0 X. {i 1 t $::abc} {{} {} t abc}}
{UPDATE t2 0 X. {i 2 t $::def} {{} {} t def}}
" { lappend y $c }
if {$x != $y} { error "changeset no good" }
}
}
catch {db close}
catch {db2 close}
forcedelete test.db
sqlite3 db test.db
set abc [string repeat abc 256]
set def [string repeat def 256]
execsql "
CREATE TABLE t2(a PRIMARY KEY, b);
INSERT INTO t2 VALUES(1, '$abc');
"
set changeset [changeset_from_sql "
INSERT INTO t2 VALUES(2, '$def');
DELETE FROM t2 WHERE a = 1;
"]
db close
do_faultsim_test 5.3 -faults oom-tra* -body {
set ::inverse [sqlite3changeset_invert $::changeset]
set {} {}
} -test {
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
if {$testrc==0} {
set x [list]
sqlite3session_foreach c $::inverse { lappend x $c }
foreach c "
{INSERT t2 0 X. {} {i 1 t $::abc}}
{DELETE t2 0 X. {i 2 t $::def} {}}
" { lappend y $c }
if {$x != $y} { error "changeset no good" }
}
}
#-------------------------------------------------------------------------
# Test that OOM errors in sqlite3changeset_concat() are handled correctly.
#
catch {db close}
forcedelete test.db
sqlite3 db test.db
do_execsql_test 5.prep1 {
CREATE TABLE t1(a, b, PRIMARY KEY(b));
CREATE TABLE t2(a PRIMARY KEY, b);
INSERT INTO t1 VALUES('string', 1);
INSERT INTO t1 VALUES(4, 2);
INSERT INTO t1 VALUES(X'FFAAFFAAFFAA', 3);
}
do_test 6.prep2 {
sqlite3session M db main
M attach *
set ::c2 [changeset_from_sql {
INSERT INTO t2 VALUES(randomblob(1000), randomblob(1000));
INSERT INTO t2 VALUES('one', 'two');
INSERT INTO t2 VALUES(1, NULL);
UPDATE t1 SET a = 5 WHERE a = 2;
}]
set ::c1 [changeset_from_sql {
DELETE FROM t2 WHERE a = 1;
UPDATE t1 SET a = 4 WHERE a = 2;
INSERT INTO t2 VALUES('x', 'y');
}]
set ::total [changeset_to_list [M changeset]]
M delete
} {}
do_faultsim_test 6 -faults oom-* -body {
set ::result [sqlite3changeset_concat $::c1 $::c2]
set {} {}
} -test {
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
if {$testrc==0} {
set v [changeset_to_list $::result]
if {$v != $::total} { error "result no good" }
}
}
faultsim_delete_and_reopen
do_execsql_test 5.prep1 {
CREATE TABLE t1(a, b, PRIMARY KEY(a));
}
faultsim_save_and_close
set res [list]
for {set ::i 0} {$::i < 480} {incr ::i 4} {
lappend res "INSERT t1 0 X. {} {i $::i i $::i}"
}
set res [lsort $res]
do_faultsim_test 7 -faults oom-transient -prep {
faultsim_restore_and_reopen
sqlite3session S db main
S attach *
} -body {
for {set ::i 0} {$::i < 480} {incr ::i 4} {
execsql {INSERT INTO t1 VALUES($::i, $::i)}
}
} -test {
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
if {$testrc==0} {
set cres [list [catch {changeset_to_list [S changeset]} msg] $msg]
S delete
if {$cres != "1 SQLITE_NOMEM" && $cres != "0 {$::res}"} {
error "Expected {0 $::res} Got {$cres}"
}
} else {
S changeset
S delete
}
}
faultsim_delete_and_reopen
do_test 8.prep {
sqlite3session S db main
S attach *
execsql {
CREATE TABLE t1(a, b, PRIMARY KEY(a));
INSERT INTO t1 VALUES(1, 2);
INSERT INTO t1 VALUES(3, 4);
INSERT INTO t1 VALUES(5, 6);
}
set ::changeset [S changeset]
S delete
} {}
set expected [normalize_list {
{INSERT t1 0 X. {} {i 1 i 2}}
{INSERT t1 0 X. {} {i 3 i 4}}
{INSERT t1 0 X. {} {i 5 i 6}}
}]
do_faultsim_test 8.1 -faults oom* -body {
set ::res [list]
sqlite3session_foreach -next v $::changeset { lappend ::res $v }
normalize_list $::res
} -test {
faultsim_test_result [list 0 $::expected] {1 SQLITE_NOMEM}
}
do_faultsim_test 8.2 -faults oom* -body {
set ::res [list]
sqlite3session_foreach v $::changeset { lappend ::res $v }
normalize_list $::res
} -test {
faultsim_test_result [list 0 $::expected] {1 SQLITE_NOMEM}
}
faultsim_delete_and_reopen
do_test 9.1.prep {
execsql {
PRAGMA encoding = 'utf16';
CREATE TABLE t1(a PRIMARY KEY, b);
}
} {}
faultsim_save_and_close
set answers [list {0 {}} {1 SQLITE_NOMEM} \
{1 {callback requested query abort}} \
{1 {abort due to ROLLBACK}}]
do_faultsim_test 9.1 -faults oom-transient -prep {
catch { unset ::c }
faultsim_restore_and_reopen
sqlite3session S db main
S attach *
} -body {
execsql {
INSERT INTO t1 VALUES('abcdefghijklmnopqrstuv', 'ABCDEFGHIJKLMNOPQRSTUV');
}
set ::c [S changeset]
set {} {}
} -test {
S delete
eval faultsim_test_result $::answers
if {[info exists ::c]} {
set expected [normalize_list {
{INSERT t1 0 X. {} {t abcdefghijklmnopqrstuv t ABCDEFGHIJKLMNOPQRSTUV}}
}]
if { [changeset_to_list $::c] != $expected } {
error "changeset mismatch"
}
}
}
faultsim_delete_and_reopen
do_test 9.2.prep {
execsql {
PRAGMA encoding = 'utf16';
CREATE TABLE t1(a PRIMARY KEY, b);
INSERT INTO t1 VALUES('abcdefghij', 'ABCDEFGHIJKLMNOPQRSTUV');
}
} {}
faultsim_save_and_close
set answers [list {0 {}} {1 SQLITE_NOMEM} \
{1 {callback requested query abort}} \
{1 {abort due to ROLLBACK}}]
do_faultsim_test 9.2 -faults oom-transient -prep {
catch { unset ::c }
faultsim_restore_and_reopen
sqlite3session S db main
S attach *
} -body {
execsql {
UPDATE t1 SET b = 'xyz';
}
set ::c [S changeset]
set {} {}
} -test {
S delete
eval faultsim_test_result $::answers
if {[info exists ::c]} {
set expected [normalize_list {
{UPDATE t1 0 X. {t abcdefghij t ABCDEFGHIJKLMNOPQRSTUV} {{} {} t xyz}}
}]
if { [changeset_to_list $::c] != $expected } {
error "changeset mismatch"
}
}
}
#-------------------------------------------------------------------------
# Test that if a conflict-handler encounters an OOM in
# sqlite3_value_text() but goes on to return SQLITE_CHANGESET_REPLACE
# anyway, the OOM is picked up by the sessions module.
set bigstr [string repeat abcdefghij 100]
faultsim_delete_and_reopen
do_test 10.prep.1 {
execsql {
CREATE TABLE t1(a PRIMARY KEY, b);
INSERT INTO t1 VALUES($bigstr, $bigstr);
}
sqlite3session S db main
S attach *
execsql { UPDATE t1 SET b = b||'x' }
set C [S changeset]
S delete
execsql { UPDATE t1 SET b = b||'xyz' }
} {}
faultsim_save_and_close
faultsim_restore_and_reopen
do_test 10.prep.2 {
proc xConflict {args} { return "ABORT" }
list [catch { sqlite3changeset_apply db $C xConflict } msg] $msg
} {1 SQLITE_ABORT}
do_execsql_test 10.prep.3 { SELECT b=$bigstr||'x' FROM t1 } 0
do_test 10.prep.4 {
proc xConflict {args} { return "REPLACE" }
list [catch { sqlite3changeset_apply db $C xConflict } msg] $msg
} {0 {}}
do_execsql_test 10.prep.5 { SELECT b=$bigstr||'x' FROM t1 } 1
db close
do_faultsim_test 10 -faults oom-tra* -prep {
faultsim_restore_and_reopen
} -body {
sqlite3changeset_apply_replace_all db $::C
} -test {
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
if {$testrc==0} {
if {[db one {SELECT b=$bigstr||'x' FROM t1}]==0} {
error "data does not look right"
}
}
}
#-------------------------------------------------------------------------
# Test an OOM with an sqlite3changeset_apply() filter callback.
#
reset_db
do_test 11.prep {
execsql {
CREATE TABLE t1(a PRIMARY KEY, b);
CREATE TABLE t2(x PRIMARY KEY, y);
BEGIN;
}
set ::cs [changeset_from_sql {
INSERT INTO t1 VALUES(1, 2);
INSERT INTO t2 VALUES('x', 'y');
}]
execsql ROLLBACK
set {} {}
} {}
proc filter {x} { return [string equal t1 $x] }
faultsim_save_and_close
do_faultsim_test 11 -faults oom-tra* -prep {
faultsim_restore_and_reopen
} -body {
sqlite3changeset_apply db $::cs {} filter
} -test {
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
if {$testrc==0} {
if {[db eval {SELECT * FROM t1 UNION ALL SELECT * FROM t2}] != "1 2"} {
error "data does not look right"
}
}
}
finish_test
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+936
View File
@@ -0,0 +1,936 @@
#if defined(SQLITE_TEST) && defined(SQLITE_ENABLE_SESSION) \
&& defined(SQLITE_ENABLE_PREUPDATE_HOOK)
#include "sqlite3session.h"
#include <assert.h>
#include <string.h>
#include <tcl.h>
typedef struct TestSession TestSession;
struct TestSession {
sqlite3_session *pSession;
Tcl_Interp *interp;
Tcl_Obj *pFilterScript;
};
typedef struct TestStreamInput TestStreamInput;
struct TestStreamInput {
int nStream; /* Maximum chunk size */
unsigned char *aData; /* Pointer to buffer containing data */
int nData; /* Size of buffer aData in bytes */
int iData; /* Bytes of data already read by sessions */
};
#define SESSION_STREAM_TCL_VAR "sqlite3session_streams"
/*
** Attempt to find the global variable zVar within interpreter interp
** and extract an integer value from it. Return this value.
**
** If the named variable cannot be found, or if it cannot be interpreted
** as a integer, return 0.
*/
static int test_tcl_integer(Tcl_Interp *interp, const char *zVar){
Tcl_Obj *pObj;
int iVal = 0;
pObj = Tcl_ObjGetVar2(interp, Tcl_NewStringObj(zVar, -1), 0, TCL_GLOBAL_ONLY);
if( pObj ) Tcl_GetIntFromObj(0, pObj, &iVal);
return iVal;
}
static int test_session_error(Tcl_Interp *interp, int rc){
extern const char *sqlite3ErrName(int);
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
return TCL_ERROR;
}
static int test_table_filter(void *pCtx, const char *zTbl){
TestSession *p = (TestSession*)pCtx;
Tcl_Obj *pEval;
int rc;
int bRes = 0;
pEval = Tcl_DuplicateObj(p->pFilterScript);
Tcl_IncrRefCount(pEval);
rc = Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj(zTbl, -1));
if( rc==TCL_OK ){
rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL);
}
if( rc==TCL_OK ){
rc = Tcl_GetBooleanFromObj(p->interp, Tcl_GetObjResult(p->interp), &bRes);
}
if( rc!=TCL_OK ){
/* printf("error: %s\n", Tcl_GetStringResult(p->interp)); */
Tcl_BackgroundError(p->interp);
}
Tcl_DecrRefCount(pEval);
return bRes;
}
struct TestSessionsBlob {
void *p;
int n;
};
typedef struct TestSessionsBlob TestSessionsBlob;
static int testStreamOutput(
void *pCtx,
const void *pData,
int nData
){
TestSessionsBlob *pBlob = (TestSessionsBlob*)pCtx;
char *pNew;
assert( nData>0 );
pNew = (char*)sqlite3_realloc(pBlob->p, pBlob->n + nData);
if( pNew==0 ){
return SQLITE_NOMEM;
}
pBlob->p = (void*)pNew;
memcpy(&pNew[pBlob->n], pData, nData);
pBlob->n += nData;
return SQLITE_OK;
}
/*
** Tclcmd: $session attach TABLE
** $session changeset
** $session delete
** $session enable BOOL
** $session indirect INTEGER
** $session patchset
** $session table_filter SCRIPT
*/
static int test_session_cmd(
void *clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
TestSession *p = (TestSession*)clientData;
sqlite3_session *pSession = p->pSession;
struct SessionSubcmd {
const char *zSub;
int nArg;
const char *zMsg;
int iSub;
} aSub[] = {
{ "attach", 1, "TABLE", }, /* 0 */
{ "changeset", 0, "", }, /* 1 */
{ "delete", 0, "", }, /* 2 */
{ "enable", 1, "BOOL", }, /* 3 */
{ "indirect", 1, "BOOL", }, /* 4 */
{ "isempty", 0, "", }, /* 5 */
{ "table_filter", 1, "SCRIPT", }, /* 6 */
{ "patchset", 0, "", }, /* 7 */
{ "diff", 2, "FROMDB TBL", }, /* 8 */
{ 0 }
};
int iSub;
int rc;
if( objc<2 ){
Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
return TCL_ERROR;
}
rc = Tcl_GetIndexFromObjStruct(interp,
objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub
);
if( rc!=TCL_OK ) return rc;
if( objc!=2+aSub[iSub].nArg ){
Tcl_WrongNumArgs(interp, 2, objv, aSub[iSub].zMsg);
return TCL_ERROR;
}
switch( iSub ){
case 0: { /* attach */
char *zArg = Tcl_GetString(objv[2]);
if( zArg[0]=='*' && zArg[1]=='\0' ) zArg = 0;
rc = sqlite3session_attach(pSession, zArg);
if( rc!=SQLITE_OK ){
return test_session_error(interp, rc);
}
break;
}
case 7: /* patchset */
case 1: { /* changeset */
TestSessionsBlob o = {0, 0};
if( test_tcl_integer(interp, SESSION_STREAM_TCL_VAR) ){
void *pCtx = (void*)&o;
if( iSub==7 ){
rc = sqlite3session_patchset_strm(pSession, testStreamOutput, pCtx);
}else{
rc = sqlite3session_changeset_strm(pSession, testStreamOutput, pCtx);
}
}else{
if( iSub==7 ){
rc = sqlite3session_patchset(pSession, &o.n, &o.p);
}else{
rc = sqlite3session_changeset(pSession, &o.n, &o.p);
}
}
if( rc==SQLITE_OK ){
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(o.p, o.n));
}
sqlite3_free(o.p);
if( rc!=SQLITE_OK ){
return test_session_error(interp, rc);
}
break;
}
case 2: /* delete */
Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
break;
case 3: { /* enable */
int val;
if( Tcl_GetIntFromObj(interp, objv[2], &val) ) return TCL_ERROR;
val = sqlite3session_enable(pSession, val);
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(val));
break;
}
case 4: { /* indirect */
int val;
if( Tcl_GetIntFromObj(interp, objv[2], &val) ) return TCL_ERROR;
val = sqlite3session_indirect(pSession, val);
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(val));
break;
}
case 5: { /* isempty */
int val;
val = sqlite3session_isempty(pSession);
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(val));
break;
}
case 6: { /* table_filter */
if( p->pFilterScript ) Tcl_DecrRefCount(p->pFilterScript);
p->interp = interp;
p->pFilterScript = Tcl_DuplicateObj(objv[2]);
Tcl_IncrRefCount(p->pFilterScript);
sqlite3session_table_filter(pSession, test_table_filter, clientData);
break;
}
case 8: { /* diff */
char *zErr = 0;
rc = sqlite3session_diff(pSession,
Tcl_GetString(objv[2]),
Tcl_GetString(objv[3]),
&zErr
);
assert( rc!=SQLITE_OK || zErr==0 );
if( zErr ){
Tcl_AppendResult(interp, zErr, 0);
return TCL_ERROR;
}
if( rc ){
return test_session_error(interp, rc);
}
break;
}
}
return TCL_OK;
}
static void test_session_del(void *clientData){
TestSession *p = (TestSession*)clientData;
if( p->pFilterScript ) Tcl_DecrRefCount(p->pFilterScript);
sqlite3session_delete(p->pSession);
ckfree((char*)p);
}
/*
** Tclcmd: sqlite3session CMD DB-HANDLE DB-NAME
*/
static int test_sqlite3session(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
sqlite3 *db;
Tcl_CmdInfo info;
int rc; /* sqlite3session_create() return code */
TestSession *p; /* New wrapper object */
if( objc!=4 ){
Tcl_WrongNumArgs(interp, 1, objv, "CMD DB-HANDLE DB-NAME");
return TCL_ERROR;
}
if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[2]), &info) ){
Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(objv[2]), 0);
return TCL_ERROR;
}
db = *(sqlite3 **)info.objClientData;
p = (TestSession*)ckalloc(sizeof(TestSession));
memset(p, 0, sizeof(TestSession));
rc = sqlite3session_create(db, Tcl_GetString(objv[3]), &p->pSession);
if( rc!=SQLITE_OK ){
ckfree((char*)p);
return test_session_error(interp, rc);
}
Tcl_CreateObjCommand(
interp, Tcl_GetString(objv[1]), test_session_cmd, (ClientData)p,
test_session_del
);
Tcl_SetObjResult(interp, objv[1]);
return TCL_OK;
}
static void test_append_value(Tcl_Obj *pList, sqlite3_value *pVal){
if( pVal==0 ){
Tcl_ListObjAppendElement(0, pList, Tcl_NewObj());
Tcl_ListObjAppendElement(0, pList, Tcl_NewObj());
}else{
Tcl_Obj *pObj;
switch( sqlite3_value_type(pVal) ){
case SQLITE_NULL:
Tcl_ListObjAppendElement(0, pList, Tcl_NewStringObj("n", 1));
pObj = Tcl_NewObj();
break;
case SQLITE_INTEGER:
Tcl_ListObjAppendElement(0, pList, Tcl_NewStringObj("i", 1));
pObj = Tcl_NewWideIntObj(sqlite3_value_int64(pVal));
break;
case SQLITE_FLOAT:
Tcl_ListObjAppendElement(0, pList, Tcl_NewStringObj("f", 1));
pObj = Tcl_NewDoubleObj(sqlite3_value_double(pVal));
break;
case SQLITE_TEXT: {
const char *z = (char*)sqlite3_value_blob(pVal);
int n = sqlite3_value_bytes(pVal);
Tcl_ListObjAppendElement(0, pList, Tcl_NewStringObj("t", 1));
pObj = Tcl_NewStringObj(z, n);
break;
}
case SQLITE_BLOB:
Tcl_ListObjAppendElement(0, pList, Tcl_NewStringObj("b", 1));
pObj = Tcl_NewByteArrayObj(
sqlite3_value_blob(pVal),
sqlite3_value_bytes(pVal)
);
break;
}
Tcl_ListObjAppendElement(0, pList, pObj);
}
}
typedef struct TestConflictHandler TestConflictHandler;
struct TestConflictHandler {
Tcl_Interp *interp;
Tcl_Obj *pConflictScript;
Tcl_Obj *pFilterScript;
};
static int test_obj_eq_string(Tcl_Obj *p, const char *z){
int n;
int nObj;
char *zObj;
n = (int)strlen(z);
zObj = Tcl_GetStringFromObj(p, &nObj);
return (nObj==n && (n==0 || 0==memcmp(zObj, z, n)));
}
static int test_filter_handler(
void *pCtx, /* Pointer to TestConflictHandler structure */
const char *zTab /* Table name */
){
TestConflictHandler *p = (TestConflictHandler *)pCtx;
int res = 1;
Tcl_Obj *pEval;
Tcl_Interp *interp = p->interp;
pEval = Tcl_DuplicateObj(p->pFilterScript);
Tcl_IncrRefCount(pEval);
if( TCL_OK!=Tcl_ListObjAppendElement(0, pEval, Tcl_NewStringObj(zTab, -1))
|| TCL_OK!=Tcl_EvalObjEx(interp, pEval, TCL_EVAL_GLOBAL)
|| TCL_OK!=Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &res)
){
Tcl_BackgroundError(interp);
}
Tcl_DecrRefCount(pEval);
return res;
}
static int test_conflict_handler(
void *pCtx, /* Pointer to TestConflictHandler structure */
int eConf, /* DATA, MISSING, CONFLICT, CONSTRAINT */
sqlite3_changeset_iter *pIter /* Handle describing change and conflict */
){
TestConflictHandler *p = (TestConflictHandler *)pCtx;
Tcl_Obj *pEval;
Tcl_Interp *interp = p->interp;
int ret = 0; /* Return value */
int op; /* SQLITE_UPDATE, DELETE or INSERT */
const char *zTab; /* Name of table conflict is on */
int nCol; /* Number of columns in table zTab */
pEval = Tcl_DuplicateObj(p->pConflictScript);
Tcl_IncrRefCount(pEval);
sqlite3changeset_op(pIter, &zTab, &nCol, &op, 0);
if( eConf==SQLITE_CHANGESET_FOREIGN_KEY ){
int nFk;
sqlite3changeset_fk_conflicts(pIter, &nFk);
Tcl_ListObjAppendElement(0, pEval, Tcl_NewStringObj("FOREIGN_KEY", -1));
Tcl_ListObjAppendElement(0, pEval, Tcl_NewIntObj(nFk));
}else{
/* Append the operation type. */
Tcl_ListObjAppendElement(0, pEval, Tcl_NewStringObj(
op==SQLITE_INSERT ? "INSERT" :
op==SQLITE_UPDATE ? "UPDATE" :
"DELETE", -1
));
/* Append the table name. */
Tcl_ListObjAppendElement(0, pEval, Tcl_NewStringObj(zTab, -1));
/* Append the conflict type. */
switch( eConf ){
case SQLITE_CHANGESET_DATA:
Tcl_ListObjAppendElement(interp, pEval,Tcl_NewStringObj("DATA",-1));
break;
case SQLITE_CHANGESET_NOTFOUND:
Tcl_ListObjAppendElement(interp, pEval,Tcl_NewStringObj("NOTFOUND",-1));
break;
case SQLITE_CHANGESET_CONFLICT:
Tcl_ListObjAppendElement(interp, pEval,Tcl_NewStringObj("CONFLICT",-1));
break;
case SQLITE_CHANGESET_CONSTRAINT:
Tcl_ListObjAppendElement(interp, pEval,Tcl_NewStringObj("CONSTRAINT",-1));
break;
}
/* If this is not an INSERT, append the old row */
if( op!=SQLITE_INSERT ){
int i;
Tcl_Obj *pOld = Tcl_NewObj();
for(i=0; i<nCol; i++){
sqlite3_value *pVal;
sqlite3changeset_old(pIter, i, &pVal);
test_append_value(pOld, pVal);
}
Tcl_ListObjAppendElement(0, pEval, pOld);
}
/* If this is not a DELETE, append the new row */
if( op!=SQLITE_DELETE ){
int i;
Tcl_Obj *pNew = Tcl_NewObj();
for(i=0; i<nCol; i++){
sqlite3_value *pVal;
sqlite3changeset_new(pIter, i, &pVal);
test_append_value(pNew, pVal);
}
Tcl_ListObjAppendElement(0, pEval, pNew);
}
/* If this is a CHANGESET_DATA or CHANGESET_CONFLICT conflict, append
** the conflicting row. */
if( eConf==SQLITE_CHANGESET_DATA || eConf==SQLITE_CHANGESET_CONFLICT ){
int i;
Tcl_Obj *pConflict = Tcl_NewObj();
for(i=0; i<nCol; i++){
int rc;
sqlite3_value *pVal;
rc = sqlite3changeset_conflict(pIter, i, &pVal);
assert( rc==SQLITE_OK );
test_append_value(pConflict, pVal);
}
Tcl_ListObjAppendElement(0, pEval, pConflict);
}
/***********************************************************************
** This block is purely for testing some error conditions.
*/
if( eConf==SQLITE_CHANGESET_CONSTRAINT
|| eConf==SQLITE_CHANGESET_NOTFOUND
){
sqlite3_value *pVal;
int rc = sqlite3changeset_conflict(pIter, 0, &pVal);
assert( rc==SQLITE_MISUSE );
}else{
sqlite3_value *pVal;
int rc = sqlite3changeset_conflict(pIter, -1, &pVal);
assert( rc==SQLITE_RANGE );
rc = sqlite3changeset_conflict(pIter, nCol, &pVal);
assert( rc==SQLITE_RANGE );
}
if( op==SQLITE_DELETE ){
sqlite3_value *pVal;
int rc = sqlite3changeset_new(pIter, 0, &pVal);
assert( rc==SQLITE_MISUSE );
}else{
sqlite3_value *pVal;
int rc = sqlite3changeset_new(pIter, -1, &pVal);
assert( rc==SQLITE_RANGE );
rc = sqlite3changeset_new(pIter, nCol, &pVal);
assert( rc==SQLITE_RANGE );
}
if( op==SQLITE_INSERT ){
sqlite3_value *pVal;
int rc = sqlite3changeset_old(pIter, 0, &pVal);
assert( rc==SQLITE_MISUSE );
}else{
sqlite3_value *pVal;
int rc = sqlite3changeset_old(pIter, -1, &pVal);
assert( rc==SQLITE_RANGE );
rc = sqlite3changeset_old(pIter, nCol, &pVal);
assert( rc==SQLITE_RANGE );
}
if( eConf!=SQLITE_CHANGESET_FOREIGN_KEY ){
/* eConf!=FOREIGN_KEY is always true at this point. The condition is
** just there to make it clearer what is being tested. */
int nDummy;
int rc = sqlite3changeset_fk_conflicts(pIter, &nDummy);
assert( rc==SQLITE_MISUSE );
}
/* End of testing block
***********************************************************************/
}
if( TCL_OK!=Tcl_EvalObjEx(interp, pEval, TCL_EVAL_GLOBAL) ){
Tcl_BackgroundError(interp);
}else{
Tcl_Obj *pRes = Tcl_GetObjResult(interp);
if( test_obj_eq_string(pRes, "OMIT") || test_obj_eq_string(pRes, "") ){
ret = SQLITE_CHANGESET_OMIT;
}else if( test_obj_eq_string(pRes, "REPLACE") ){
ret = SQLITE_CHANGESET_REPLACE;
}else if( test_obj_eq_string(pRes, "ABORT") ){
ret = SQLITE_CHANGESET_ABORT;
}else{
Tcl_GetIntFromObj(0, pRes, &ret);
}
}
Tcl_DecrRefCount(pEval);
return ret;
}
/*
** The conflict handler used by sqlite3changeset_apply_replace_all().
** This conflict handler calls sqlite3_value_text16() on all available
** sqlite3_value objects and then returns CHANGESET_REPLACE, or
** CHANGESET_OMIT if REPLACE is not applicable. This is used to test the
** effect of a malloc failure within an sqlite3_value_xxx() function
** invoked by a conflict-handler callback.
*/
static int replace_handler(
void *pCtx, /* Pointer to TestConflictHandler structure */
int eConf, /* DATA, MISSING, CONFLICT, CONSTRAINT */
sqlite3_changeset_iter *pIter /* Handle describing change and conflict */
){
int op; /* SQLITE_UPDATE, DELETE or INSERT */
const char *zTab; /* Name of table conflict is on */
int nCol; /* Number of columns in table zTab */
int i;
int x = 0;
sqlite3changeset_op(pIter, &zTab, &nCol, &op, 0);
if( op!=SQLITE_INSERT ){
for(i=0; i<nCol; i++){
sqlite3_value *pVal;
sqlite3changeset_old(pIter, i, &pVal);
sqlite3_value_text16(pVal);
x++;
}
}
if( op!=SQLITE_DELETE ){
for(i=0; i<nCol; i++){
sqlite3_value *pVal;
sqlite3changeset_new(pIter, i, &pVal);
sqlite3_value_text16(pVal);
x++;
}
}
if( eConf==SQLITE_CHANGESET_DATA ){
return SQLITE_CHANGESET_REPLACE;
}
return SQLITE_CHANGESET_OMIT;
}
static int testStreamInput(
void *pCtx, /* Context pointer */
void *pData, /* Buffer to populate */
int *pnData /* IN/OUT: Bytes requested/supplied */
){
TestStreamInput *p = (TestStreamInput*)pCtx;
int nReq = *pnData; /* Bytes of data requested */
int nRem = p->nData - p->iData; /* Bytes of data available */
int nRet = p->nStream; /* Bytes actually returned */
/* Allocate and free some space. There is no point to this, other than
** that it allows the regular OOM fault-injection tests to cause an error
** in this function. */
void *pAlloc = sqlite3_malloc(10);
if( pAlloc==0 ) return SQLITE_NOMEM;
sqlite3_free(pAlloc);
if( nRet>nReq ) nRet = nReq;
if( nRet>nRem ) nRet = nRem;
assert( nRet>=0 );
if( nRet>0 ){
memcpy(pData, &p->aData[p->iData], nRet);
p->iData += nRet;
}
*pnData = nRet;
return SQLITE_OK;
}
/*
** sqlite3changeset_apply DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?
*/
static int test_sqlite3changeset_apply(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
sqlite3 *db; /* Database handle */
Tcl_CmdInfo info; /* Database Tcl command (objv[1]) info */
int rc; /* Return code from changeset_invert() */
void *pChangeset; /* Buffer containing changeset */
int nChangeset; /* Size of buffer aChangeset in bytes */
TestConflictHandler ctx;
TestStreamInput sStr;
memset(&sStr, 0, sizeof(sStr));
sStr.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
if( objc!=4 && objc!=5 ){
Tcl_WrongNumArgs(interp, 1, objv,
"DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?"
);
return TCL_ERROR;
}
if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &info) ){
Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(objv[2]), 0);
return TCL_ERROR;
}
db = *(sqlite3 **)info.objClientData;
pChangeset = (void *)Tcl_GetByteArrayFromObj(objv[2], &nChangeset);
ctx.pConflictScript = objv[3];
ctx.pFilterScript = objc==5 ? objv[4] : 0;
ctx.interp = interp;
if( sStr.nStream==0 ){
rc = sqlite3changeset_apply(db, nChangeset, pChangeset,
(objc==5) ? test_filter_handler : 0, test_conflict_handler, (void *)&ctx
);
}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( rc!=SQLITE_OK ){
return test_session_error(interp, rc);
}
Tcl_ResetResult(interp);
return TCL_OK;
}
/*
** sqlite3changeset_apply_replace_all DB CHANGESET
*/
static int test_sqlite3changeset_apply_replace_all(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
sqlite3 *db; /* Database handle */
Tcl_CmdInfo info; /* Database Tcl command (objv[1]) info */
int rc; /* Return code from changeset_invert() */
void *pChangeset; /* Buffer containing changeset */
int nChangeset; /* Size of buffer aChangeset in bytes */
if( objc!=3 ){
Tcl_WrongNumArgs(interp, 1, objv, "DB CHANGESET");
return TCL_ERROR;
}
if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &info) ){
Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(objv[2]), 0);
return TCL_ERROR;
}
db = *(sqlite3 **)info.objClientData;
pChangeset = (void *)Tcl_GetByteArrayFromObj(objv[2], &nChangeset);
rc = sqlite3changeset_apply(db, nChangeset, pChangeset, 0, replace_handler,0);
if( rc!=SQLITE_OK ){
return test_session_error(interp, rc);
}
Tcl_ResetResult(interp);
return TCL_OK;
}
/*
** sqlite3changeset_invert CHANGESET
*/
static int test_sqlite3changeset_invert(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int rc; /* Return code from changeset_invert() */
TestStreamInput sIn; /* Input stream */
TestSessionsBlob sOut; /* Output blob */
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "CHANGESET");
return TCL_ERROR;
}
memset(&sIn, 0, sizeof(sIn));
memset(&sOut, 0, sizeof(sOut));
sIn.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
sIn.aData = Tcl_GetByteArrayFromObj(objv[1], &sIn.nData);
if( sIn.nStream ){
rc = sqlite3changeset_invert_strm(
testStreamInput, (void*)&sIn, testStreamOutput, (void*)&sOut
);
}else{
rc = sqlite3changeset_invert(sIn.nData, sIn.aData, &sOut.n, &sOut.p);
}
if( rc!=SQLITE_OK ){
rc = test_session_error(interp, rc);
}else{
Tcl_SetObjResult(interp,Tcl_NewByteArrayObj((unsigned char*)sOut.p,sOut.n));
}
sqlite3_free(sOut.p);
return rc;
}
/*
** sqlite3changeset_concat LEFT RIGHT
*/
static int test_sqlite3changeset_concat(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int rc; /* Return code from changeset_invert() */
TestStreamInput sLeft; /* Input stream */
TestStreamInput sRight; /* Input stream */
TestSessionsBlob sOut = {0,0}; /* Output blob */
if( objc!=3 ){
Tcl_WrongNumArgs(interp, 1, objv, "LEFT RIGHT");
return TCL_ERROR;
}
memset(&sLeft, 0, sizeof(sLeft));
memset(&sRight, 0, sizeof(sRight));
sLeft.aData = Tcl_GetByteArrayFromObj(objv[1], &sLeft.nData);
sRight.aData = Tcl_GetByteArrayFromObj(objv[2], &sRight.nData);
sLeft.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
sRight.nStream = sLeft.nStream;
if( sLeft.nStream>0 ){
rc = sqlite3changeset_concat_strm(
testStreamInput, (void*)&sLeft,
testStreamInput, (void*)&sRight,
testStreamOutput, (void*)&sOut
);
}else{
rc = sqlite3changeset_concat(
sLeft.nData, sLeft.aData, sRight.nData, sRight.aData, &sOut.n, &sOut.p
);
}
if( rc!=SQLITE_OK ){
rc = test_session_error(interp, rc);
}else{
Tcl_SetObjResult(interp,Tcl_NewByteArrayObj((unsigned char*)sOut.p,sOut.n));
}
sqlite3_free(sOut.p);
return rc;
}
/*
** sqlite3session_foreach VARNAME CHANGESET SCRIPT
*/
static int test_sqlite3session_foreach(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
void *pChangeset;
int nChangeset;
sqlite3_changeset_iter *pIter;
int rc;
Tcl_Obj *pVarname;
Tcl_Obj *pCS;
Tcl_Obj *pScript;
int isCheckNext = 0;
TestStreamInput sStr;
memset(&sStr, 0, sizeof(sStr));
if( objc>1 ){
char *zOpt = Tcl_GetString(objv[1]);
isCheckNext = (strcmp(zOpt, "-next")==0);
}
if( objc!=4+isCheckNext ){
Tcl_WrongNumArgs(interp, 1, objv, "?-next? VARNAME CHANGESET SCRIPT");
return TCL_ERROR;
}
pVarname = objv[1+isCheckNext];
pCS = objv[2+isCheckNext];
pScript = objv[3+isCheckNext];
pChangeset = (void *)Tcl_GetByteArrayFromObj(pCS, &nChangeset);
sStr.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
if( sStr.nStream==0 ){
rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset);
}else{
sStr.aData = (unsigned char*)pChangeset;
sStr.nData = nChangeset;
rc = sqlite3changeset_start_strm(&pIter, testStreamInput, (void*)&sStr);
}
if( rc!=SQLITE_OK ){
return test_session_error(interp, rc);
}
while( SQLITE_ROW==sqlite3changeset_next(pIter) ){
int nCol; /* Number of columns in table */
int nCol2; /* Number of columns in table */
int op; /* SQLITE_INSERT, UPDATE or DELETE */
const char *zTab; /* Name of table change applies to */
Tcl_Obj *pVar; /* Tcl value to set $VARNAME to */
Tcl_Obj *pOld; /* Vector of old.* values */
Tcl_Obj *pNew; /* Vector of new.* values */
int bIndirect;
char *zPK;
unsigned char *abPK;
int i;
/* Test that _fk_conflicts() returns SQLITE_MISUSE if called on this
** iterator. */
int nDummy;
if( SQLITE_MISUSE!=sqlite3changeset_fk_conflicts(pIter, &nDummy) ){
sqlite3changeset_finalize(pIter);
return TCL_ERROR;
}
sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
pVar = Tcl_NewObj();
Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(
op==SQLITE_INSERT ? "INSERT" :
op==SQLITE_UPDATE ? "UPDATE" :
"DELETE", -1
));
Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zTab, -1));
Tcl_ListObjAppendElement(0, pVar, Tcl_NewBooleanObj(bIndirect));
zPK = ckalloc(nCol+1);
memset(zPK, 0, nCol+1);
sqlite3changeset_pk(pIter, &abPK, &nCol2);
assert( nCol==nCol2 );
for(i=0; i<nCol; i++){
zPK[i] = (abPK[i] ? 'X' : '.');
}
Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zPK, -1));
ckfree(zPK);
pOld = Tcl_NewObj();
if( op!=SQLITE_INSERT ){
int i;
for(i=0; i<nCol; i++){
sqlite3_value *pVal;
sqlite3changeset_old(pIter, i, &pVal);
test_append_value(pOld, pVal);
}
}
pNew = Tcl_NewObj();
if( op!=SQLITE_DELETE ){
int i;
for(i=0; i<nCol; i++){
sqlite3_value *pVal;
sqlite3changeset_new(pIter, i, &pVal);
test_append_value(pNew, pVal);
}
}
Tcl_ListObjAppendElement(0, pVar, pOld);
Tcl_ListObjAppendElement(0, pVar, pNew);
Tcl_ObjSetVar2(interp, pVarname, 0, pVar, 0);
rc = Tcl_EvalObjEx(interp, pScript, 0);
if( rc!=TCL_OK && rc!=TCL_CONTINUE ){
sqlite3changeset_finalize(pIter);
return rc==TCL_BREAK ? TCL_OK : rc;
}
}
if( isCheckNext ){
int rc2 = sqlite3changeset_next(pIter);
rc = sqlite3changeset_finalize(pIter);
assert( (rc2==SQLITE_DONE && rc==SQLITE_OK) || rc2==rc );
}else{
rc = sqlite3changeset_finalize(pIter);
}
if( rc!=SQLITE_OK ){
return test_session_error(interp, rc);
}
return TCL_OK;
}
int TestSession_Init(Tcl_Interp *interp){
Tcl_CreateObjCommand(interp, "sqlite3session", test_sqlite3session, 0, 0);
Tcl_CreateObjCommand(
interp, "sqlite3session_foreach", test_sqlite3session_foreach, 0, 0
);
Tcl_CreateObjCommand(
interp, "sqlite3changeset_invert", test_sqlite3changeset_invert, 0, 0
);
Tcl_CreateObjCommand(
interp, "sqlite3changeset_concat", test_sqlite3changeset_concat, 0, 0
);
Tcl_CreateObjCommand(
interp, "sqlite3changeset_apply", test_sqlite3changeset_apply, 0, 0
);
Tcl_CreateObjCommand(
interp, "sqlite3changeset_apply_replace_all",
test_sqlite3changeset_apply_replace_all, 0, 0
);
return TCL_OK;
}
#endif /* SQLITE_TEST && SQLITE_SESSION && SQLITE_PREUPDATE_HOOK */
+23 -7
View File
@@ -47,6 +47,7 @@
TCCX = $(TCC) $(OPTS) -I. -I$(TOP)/src -I$(TOP)
TCCX += -I$(TOP)/ext/rtree -I$(TOP)/ext/icu -I$(TOP)/ext/fts3
TCCX += -I$(TOP)/ext/async -I$(TOP)/ext/userauth
TCCX += -I$(TOP)/ext/session
# Object files for the SQLite library.
#
@@ -71,6 +72,8 @@ LIBOBJ+= vdbe.o parse.o \
vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbesort.o \
vdbetrace.o wal.o walker.o where.o utf.o vtab.o
LIBOBJ += sqlite3session.o
# All of the source code files.
@@ -217,6 +220,9 @@ SRC += \
$(TOP)/ext/rtree/sqlite3rtree.h \
$(TOP)/ext/rtree/rtree.h \
$(TOP)/ext/rtree/rtree.c
SRC += \
$(TOP)/ext/session/sqlite3session.c \
$(TOP)/ext/session/sqlite3session.h
SRC += \
$(TOP)/ext/userauth/userauth.c \
$(TOP)/ext/userauth/sqlite3userauth.h
@@ -338,7 +344,9 @@ TESTSRC2 = \
$(TOP)/ext/fts3/fts3_expr.c \
$(TOP)/ext/fts3/fts3_tokenizer.c \
$(TOP)/ext/fts3/fts3_write.c \
$(TOP)/ext/async/sqlite3async.c
$(TOP)/ext/async/sqlite3async.c \
$(TOP)/ext/session/sqlite3session.c \
$(TOP)/ext/session/test_session.c
# Header files used by all library source files.
#
@@ -404,10 +412,6 @@ sqlite3$(EXE): $(TOP)/src/shell.c libsqlite3.a sqlite3.h
$(TOP)/src/shell.c \
libsqlite3.a $(LIBREADLINE) $(TLIBS) $(THREADLIB)
sqldiff$(EXE): $(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h
$(TCCX) -o sqldiff$(EXE) -DSQLITE_THREADSAFE=0 \
$(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS) $(THREADLIB)
mptester$(EXE): sqlite3.c $(TOP)/mptest/mptest.c
$(TCCX) -o $@ -I. $(TOP)/mptest/mptest.c sqlite3.c \
$(TLIBS) $(THREADLIB)
@@ -446,6 +450,7 @@ target_source: $(SRC) $(TOP)/tool/vdbe-compress.tcl
sqlite3.c: target_source $(TOP)/tool/mksqlite3c.tcl
tclsh $(TOP)/tool/mksqlite3c.tcl
cp tsrc/shell.c tsrc/sqlite3ext.h .
cp $(TOP)/ext/session/sqlite3session.h .
echo '#ifndef USE_SYSTEM_SQLITE' >tclsqlite3.c
cat sqlite3.c >>tclsqlite3.c
echo '#endif /* USE_SYSTEM_SQLITE */' >>tclsqlite3.c
@@ -590,6 +595,9 @@ rtree.o: $(TOP)/ext/rtree/rtree.c $(HDR) $(EXTHDR)
userauth.o: $(TOP)/ext/userauth/userauth.c $(HDR) $(EXTHDR)
$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/userauth/userauth.c
sqlite3session.o: $(TOP)/ext/session/sqlite3session.c $(HDR) $(EXTHDR)
$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/session/sqlite3session.c
# Rules for building test programs and for running tests
#
@@ -597,7 +605,8 @@ tclsqlite3: $(TOP)/src/tclsqlite.c libsqlite3.a
$(TCCX) $(TCL_FLAGS) -DTCLSH=1 -o tclsqlite3 \
$(TOP)/src/tclsqlite.c libsqlite3.a $(LIBTCL) $(THREADLIB)
sqlite3_analyzer.c: sqlite3.c $(TOP)/src/test_stat.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl
sqlite3_analyzer.c: sqlite3.c $(TOP)/src/test_stat.c $(TOP)/src/tclsqlite.c \
$(TOP)/tool/spaceanal.tcl
echo "#define TCLSH 2" > $@
cat sqlite3.c $(TOP)/src/test_stat.c $(TOP)/src/tclsqlite.c >> $@
echo "static const char *tclsh_main_loop(void){" >> $@
@@ -618,9 +627,11 @@ testfixture$(EXE): $(TESTSRC2) libsqlite3.a $(TESTSRC) $(TOP)/src/tclsqlite.c
$(TESTSRC) $(TESTSRC2) $(TOP)/src/tclsqlite.c \
-o testfixture$(EXE) $(LIBTCL) libsqlite3.a $(THREADLIB)
amalgamation-testfixture$(EXE): sqlite3.c $(TESTSRC) $(TOP)/src/tclsqlite.c
amalgamation-testfixture$(EXE): sqlite3.c $(TESTSRC) $(TOP)/src/tclsqlite.c \
$(TOP)/ext/session/test_session.c
$(TCCX) $(TCL_FLAGS) -DTCLSH=1 $(TESTFIXTURE_FLAGS) \
$(TESTSRC) $(TOP)/src/tclsqlite.c sqlite3.c \
$(TOP)/ext/session/test_session.c \
-o testfixture$(EXE) $(LIBTCL) $(THREADLIB)
fts3-testfixture$(EXE): sqlite3.c fts3amal.c $(TESTSRC) $(TOP)/src/tclsqlite.c
@@ -684,6 +695,10 @@ showwal$(EXE): $(TOP)/tool/showwal.c sqlite3.o
$(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o showwal$(EXE) \
$(TOP)/tool/showwal.c sqlite3.o $(THREADLIB)
changeset$(EXE): $(TOP)/ext/session/changeset.c sqlite3.o
$(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o changeset$(EXE) \
$(TOP)/ext/session/changeset.c sqlite3.o $(THREADLIB)
fts3view$(EXE): $(TOP)/ext/fts3/tool/fts3view.c sqlite3.o
$(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o fts3view$(EXE) \
$(TOP)/ext/fts3/tool/fts3view.c sqlite3.o $(THREADLIB)
@@ -742,6 +757,7 @@ clean:
rm -f showjournal showjournal.exe
rm -f showstat4 showstat4.exe
rm -f showwal showwal.exe
rm -f changeset changeset.exe
rm -f speedtest1 speedtest1.exe
rm -f wordcount wordcount.exe
rm -f sqlite3.c sqlite3-*.c fts?amal.c tclsqlite3.c
+107 -86
View File
@@ -1,12 +1,12 @@
C Merge\supdates\sfrom\strunk.
D 2015-04-16T04:20:10.533
C Add\sexperimental\sAPI\ssqlite3session_diff().
D 2015-04-08T16:01:12.482
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 5f78b1ab81b64e7c57a75d170832443e66c0880a
F Makefile.in 3083cf0c2bc6618e532b9478ce735bb512322985
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
F Makefile.msc 11cb5361e34b80e9c5b417ed6a6deee099c5b5f3
F Makefile.msc ffb3a33f21fcc6f7d4b0cc76a981f62f17c62b24
F Makefile.vxworks e1b65dea203f054e71653415bd8f96dcaed47858
F README.md d58e3bebc0a4145e0f2a87994015fdb575a8e866
F VERSION 2e244662b71e6e68a5c29b014ebc5b7564f4cc5a
F VERSION 319eb1ced4b4d17a67730f2b7b85f15c1346cb60
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
F addopcodes.awk 9eb448a552d5c0185cf62c463f9c173cedae3811
F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
@@ -38,7 +38,7 @@ F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63
F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977
F config.h.in 42b71ad3fe21c9e88fa59e8458ca1a6bc72eb0c0
F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55
F configure 2ea5f5b58dd106da449ab598ab6e515339d7fa2a x
F configure 8b18c2378805a1d8aaca85d293671f450dd3c723 x
F configure.ac 0b775d383c536bbaafc1e46dd3cbb81a7ea11aeb
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
F doc/lemon.html 334dbf6621b8fb8790297ec1abf3cfa4621709d1
@@ -78,9 +78,9 @@ F ext/fts3/README.content fdc666a70d5257a64fee209f97cf89e0e6e32b51
F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a
F ext/fts3/README.tokenizers e0a8b81383ea60d0334d274fadf305ea14a8c314
F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
F ext/fts3/fts3.c 4bd75289875b63c04f943d6ed7c31737da99cd74
F ext/fts3/fts3.c 23bd9d37a777342f5c22a648e9b4b005dde9e58f
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
F ext/fts3/fts3Int.h 3626655d6ba903a3919bb44e1c38e5f0f9d6be82
F ext/fts3/fts3Int.h 394858c12a17740f7a1f6bd372c4606d4425a8d1
F ext/fts3/fts3_aux.c 5c211e17a64885faeb16b9ba7772f9d5445c2365
F ext/fts3/fts3_expr.c 40123785eaa3ebd4c45c9b23407cc44ac0c49905
F ext/fts3/fts3_hash.c 29b986e43f4e9dd40110eafa377dc0d63c422c60
@@ -146,21 +146,39 @@ F ext/rtree/rtree_util.tcl 06aab2ed5b826545bf215fff90ecb9255a8647ea
F ext/rtree/sqlite3rtree.h 83349d519fe5f518b3ea025d18dd1fe51b1684bd
F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de
F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
F ext/session/changeset.c 4ccbaa4531944c24584bf6a61ba3a39c62b6267a
F ext/session/session1.test 4653867f32a98ce4bbb4a181aac6debe51ca4dfb
F ext/session/session2.test a95a2d270b32638c1acba7cb9c81856712d469ac
F ext/session/session3.test a7a9ce59b8d1e49e2cc23d81421ac485be0eea01
F ext/session/session4.test a6ed685da7a5293c5d6f99855bcf41dbc352ca84
F ext/session/session5.test 716bc6fafd625ce60dfa62ae128971628c1a1169
F ext/session/session6.test 443789bc2fca12e4f7075cf692c60b8a2bea1a26
F ext/session/session8.test 8e194b3f655d861ca36de5d4de53f702751bab3b
F ext/session/session9.test 5409d90d8141881d08285ed1c2c0d8d10fb92069
F ext/session/sessionA.test 1feeab0b8e03527f08f2f1defb442da25480138f
F ext/session/sessionB.test 06961b7c3641151f5d23088250ecad132501113c
F ext/session/sessionC.test 3982f8577b0744c5ce3aaef7cfeb5bd903f17fe4
F ext/session/sessionD.test 95903bd6a23dadbda7e192f730dd7dedd2d369ef
F ext/session/session_common.tcl 9de0451b6a47218fc16b9ed8876b6238a0a3d88d
F ext/session/sessionfault.test bef044d0952c0d62c31c8d2400be72c8684545cc
F ext/session/sqlite3session.c 3fcf2efe81c90602db76b1da33538895034eef66
F ext/session/sqlite3session.h d9ebd8d4c5791aafdf18165575c7f2223c09279b
F ext/session/test_session.c 037fc25340a918eb2195972fed439d7adf7b5db9
F ext/userauth/sqlite3userauth.h 19cb6f0e31316d0ee4afdfb7a85ef9da3333a220
F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
F main.mk ddffac494a82d42772df9fe30d3a78acf4f7cb41
F main.mk 3876195fb0d4a873f643a3ee8ae4dc29b7bbb96b
F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea
F mkopcodeh.awk d5e22023b5238985bb54a72d33e0ac71fe4f8a32
F mkopcodeh.awk c6b3fa301db6ef7ac916b14c60868aeaec1337b5
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
F mptest/crash01.test 61e61469e257df0850df4293d7d4d6c2af301421
F mptest/crash02.subtest f4ef05adcd15d60e5d2bd654204f2c008b519df8
F mptest/mptest.c fca59f0a922e03f95ed17c44b1515ed37a841c81
F mptest/mptest.c dae6de83eddac3ef97fc4111632f6066760f939a
F mptest/multiwrite01.test dab5c5f8f9534971efce679152c5146da265222d
F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
@@ -168,34 +186,34 @@ F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
F src/alter.c d23d6b6991f66b383934f137fd4384d93fb98c81
F src/analyze.c 91540f835163d5369ccbae78e2e6c74d0dd53c1d
F src/attach.c 3c1053a4cf1c3ca05c8c1d74a94cb688d763cef2
F src/attach.c 880f9b8641a829c563e52dd13c452ce457ae4dd8
F src/auth.c b56c78ebe40a2110fd361379f7e8162d23f92240
F src/backup.c ff743689c4d6c5cb55ad42ed9d174b2b3e71f1e3
F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb
F src/btmutex.c 45a968cc85afed9b5e6cf55bf1f42f8d18107f79
F src/btree.c c6e32d84442f79d5b96965265d65b3baa231dffc
F src/btree.c 2caf598165f3608fde8abac2b243826616ce54b7
F src/btree.h 969adc948e89e449220ff0ff724c94bb2a52e9f1
F src/btreeInt.h 973a22a6fd61350b454ad614832b1f0a5e25a1e4
F src/build.c 01b969b20a44a3d9620e597d9af8242348123540
F src/build.c 0419bba592c22f6d00e6d57a2ca7136720d02c1a
F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0
F src/complete.c a5cf5b4b56390cfb7b8636e8f7ddef90258dd575
F src/complete.c 198a0066ba60ab06fc00fba1998d870a4d575463
F src/ctime.c 98f89724adc891a1a4c655bee04e33e716e05887
F src/date.c e4d50b3283696836ec1036b695ead9a19e37a5ac
F src/delete.c 37964e6c1d73ff49cbea9ff690c9605fb15f600e
F src/expr.c 8800584340a9b4f4c0ca55c360de751c6da0b11a
F src/delete.c 5075d88557eb4e2a7fdb2b61a96142830d8589b8
F src/expr.c d09dac67d53c78880ba31d56e8ba2be3a6490553
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
F src/fkey.c 3343d551a8d810782257244fb33f2ce191493c39
F src/fkey.c e0444b61bed271a76840cbe6182df93a9baa3f12
F src/func.c 1414c24c873c48796ad45942257a179a423ba42f
F src/global.c 4f77cadbc5427d00139ba43d0f3979804cbb700e
F src/hash.c 4263fbc955f26c2e8cdc0cf214bc42435aa4e4f5
F src/hash.h c8f3c31722cf3277d03713909761e152a5b81094
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
F src/insert.c 305dd3f9539d0affa4bf1c14cc7dffb34867e040
F src/insert.c 4f6df86bbed2d7b59e4601730407876825dd7b71
F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
F src/legacy.c ba1863ea58c4c840335a84ec276fc2b25e22bc4e
F src/lempar.c 7274c97d24bb46631e504332ccd3bd1b37841770
F src/loadext.c 86bd4e2fccd520b748cba52492ab60c4a770f660
F src/main.c 40e333960d53f7d50ee8ce09d40431c87ea653f2
F src/main.c 4eecdeb7b3f6be5aa336252f71208344b5edd196
F src/malloc.c 6a370b83d54e4bbf6f94021221c2a311cff26a18
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c abe6ee469b6c5a35c7f22bfeb9c9bac664a1c987
@@ -203,7 +221,7 @@ F src/mem2.c f1940d9e91948dd6a908fbb9ce3835c36b5d83c3
F src/mem3.c 61c9d47b792908c532ca3a62b999cf21795c6534
F src/mem5.c 61eeb90134f9a5be6c2e68d8daae7628b25953fb
F src/memjournal.c 3eb2c0b51adbd869cb6a44780323f05fa904dc85
F src/msvc.h d9ba56c6851227ab44b3f228a35f3f5772296495
F src/msvc.h e78002098966e39b2fd9915bd70b7bc3ec8398b7
F src/mutex.c 19bf9acba69ca2f367c3761080f8a9f0cf4670a8
F src/mutex.h 779d588e3b7756ec3ecf7d78cde1d84aba414f85
F src/mutex_noop.c 529bab0743c3321c940f32c3464de494fd38cfa9
@@ -214,7 +232,7 @@ F src/os.c 8fd25588eeba74068d41102d26810e216999b6c8
F src/os.h 3e57a24e2794a94d3cf2342c6d9a884888cd96bf
F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04
F src/os_setup.h c9d4553b5aaa6f73391448b265b89bed0b890faa
F src/os_unix.c 5ed7e2e453c2980909a6b2c80dc55764b50819a8
F src/os_unix.c 25b80a3d167da44226a2084dc9e89a6cb1f02e2e
F src/os_win.c 03d27be3a20048ef52a648d5f0a15f5edda9f2a3
F src/os_win.h eb7a47aa17b26b77eb97e4823f20a00b8bda12ca
F src/pager.c 4120a49ecd37697e28f5ed807f470b9c0b88410c
@@ -223,23 +241,23 @@ F src/parse.y 1299c66e7b1707322ccd8af43a359b8fb0d46d72
F src/pcache.c 10539fb959849ad6efff80050541cab3d25089d4
F src/pcache.h b44658c9c932d203510279439d891a2a83e12ba8
F src/pcache1.c 69d137620a305f814398bd29a0c998038c0695e9
F src/pragma.c 3965ae4e82bed39fb97ce04c5fe18c9bc3ee6a88
F src/pragma.c ac4f3f856b4234e85f55b0f069698a4766011100
F src/pragma.h 09c89bca58e9a44de2116cc8272b8d454657129f
F src/prepare.c 1fffbdcd6f8a0173a8f70d71f22528f4c0e1e3d3
F src/printf.c 08fa675c200aac29e561c6153f91f909ed17612f
F src/prepare.c 173a5a499138451b2561614ecb87d78f9f4644b9
F src/printf.c 8ae1fa9d30c1200a9268a390ba9e9cea9197b27a
F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
F src/resolve.c 66cfe49a9c3b449ef13b89a8c47036a4ed167eab
F src/resolve.c 41aa91af56d960e9414ce1d7c17cfb68e0d1c6cb
F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e
F src/select.c 93260bc9e7e0e6dfe1b7cb8815b0ed4cad8be9e3
F src/shell.c 04706b14b737229cfba359cc1f46dd03b53a59be
F src/sqlite.h.in ca27603a36fcacdaac5a19d8ee35aaff8ce8516f
F src/select.c c28c52e353287434fac8473e56ee4be848d12c9d
F src/shell.c c4d839ad62f4986891601a21ce629a760f226682
F src/sqlite.h.in 64287a2b3432550264a743addbf4162a692fdd1c
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
F src/sqlite3ext.h 17d487c3c91b0b8c584a32fbeb393f6f795eea7d
F src/sqliteInt.h 90b7bfd89d7307cd0750663da419ba4bb81e7379
F src/sqliteInt.h 19c0a189e57a68ae1d971346cb9ac9383fe29692
F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46
F src/status.c f266ad8a2892d659b74f0f50cb6a88b6e7c12179
F src/table.c e7a09215315a978057fb42c640f890160dbcc45e
F src/tclsqlite.c fa72a7c5278662357c105ba7925c1d0972506ff9
F src/tclsqlite.c 7ed2644b48685cba250eb71c7c298fedab6e221a
F src/test1.c 90fbedce75330d48d99eadb7d5f4223e86969585
F src/test2.c 577961fe48961b2f2e5c8b56ee50c3f459d3359d
F src/test3.c 64d2afdd68feac1bb5e2ffb8226c8c639f798622
@@ -254,7 +272,7 @@ F src/test_autoext.c dea8a01a7153b9adc97bd26161e4226329546e12
F src/test_backup.c 2e6e6a081870150f20c526a2e9d0d29cda47d803
F src/test_blob.c 1f2e3e25255b731c4fcf15ee7990d06347cb6c09
F src/test_btree.c 2e9978eca99a9a4bfa8cae949efb00886860a64f
F src/test_config.c c2d3ff6c129d50183900c7eff14158ff7e9b3f03
F src/test_config.c 5140cf3a0b766d177dfb9f8f2a3b00224ec3f5c8
F src/test_demovfs.c 0de72c2c89551629f58486fde5734b7d90758852
F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
F src/test_fs.c ced436e3d4b8e4681328409b8081051ce614e28f
@@ -287,27 +305,27 @@ F src/test_vfs.c b7e6831e6fcf04c5090accff30640ec5c9630739
F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 6bbcc9fe50c917864d48287b4792d46d6e873481
F src/tokenize.c a8234a67577308935cdf13e618cd66556f5f45d1
F src/trigger.c 69a91bed7c94e46223e37ffccfeeb35e34b999ac
F src/update.c 3c4ecc282accf12d39edb8d524cf089645e55a13
F src/tokenize.c a8d270b06e5f709930f7b67cf70a847969cb5bf3
F src/trigger.c 25571661fdeae8c7f975ff40ffec205520a3f92f
F src/update.c d207deb7a031f698104bee879de0632b611e72dd
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
F src/util.c 98a7627ca48ad3265b6940915a1d08355eb3fc7e
F src/vacuum.c 2ddd5cad2a7b9cef7f9e431b8c7771634c6b1701
F src/vdbe.c 3f3afd12d4794cb51fe13dc948b1172a5eb71a94
F src/vdbe.h 7e538ecf47dccb307ea2d087c3ddc2dd8d70e79d
F src/vdbeInt.h 9cbaa84f53ddd2d09a0cf61a94337a3a035d08a0
F src/vdbeapi.c 583d56b129dd27f12bed518270de9ebe521e6a75
F src/vdbeaux.c a20504ae52392459fa08402fda3f195f19d7c79d
F src/vdbeblob.c 4f2e8e075d238392df98c5e03a64342465b03f90
F src/vdbemem.c c0dc81285b7571b0a31c40f17846fe2397ec1cd9
F src/vdbesort.c 2e7f683464fd5db3be4beaa1ff2d39e24fcb64b8
F src/vacuum.c 9460b9de7b2d4e34b0d374894aa6c8a0632be8ec
F src/vdbe.c 503f4f9396e1109bc9ff0fa6ee01c58e12bc782b
F src/vdbe.h b434bb75fbec973d18d49225a59833ae39ee2afc
F src/vdbeInt.h 96e4303a96c6f983e36e1fe32657b2c547f5c8f1
F src/vdbeapi.c d95f2bb43d01a91d93231cde181811b38182202e
F src/vdbeaux.c 3f36b184dbb5ca87b30c7c616d7cef6eb9e74ed6
F src/vdbeblob.c ab33f9b57cfce7dddb23853090186da614be4846
F src/vdbemem.c 149e585645c3f3ef063f2b7251646388cfe3d47d
F src/vdbesort.c 919717d7599fa31d343ec28bffd0f9e91a4ff5f6
F src/vdbetrace.c 7e4222955e07dd707a2f360c0eb73452be1cb010
F src/vtab.c 9ca557215e8591ceb66e0b7c0a579c6df1e54b2d
F src/vtab.c 62d49237bd8f3be4863815a39387b0f9897fa5e1
F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb
F src/wal.c 753995db83247f20361a8e8a874990b21a75abd9
F src/wal.c 878c8e1a51cb2ec45c395d26b7d5cd9e1a098e4a
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804
F src/where.c 8ba8ff31dc9bf1b69fe771d35d8764d5a1efd310
F src/where.c 85d832efa5ef57de542db7f430b72fecd3af8b38
F src/whereInt.h cbe4aa57326998d89e7698ca65bb7c28541d483c
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
@@ -320,7 +338,7 @@ F test/alter3.test 49c9d9fba2b8fcdce2dedeca97bbf1f369cc548d
F test/alter4.test c461150723ac957f3b2214aa0b11552cd72023ec
F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc
F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f
F test/analyze.test 3eb35a4af972f98422e5dc0586501b17d103d321
F test/analyze.test 1772936d66471c65221e437b6d1999c3a03166c4
F test/analyze3.test 75b9e42ea1e4edc919250450dc5762186965d4e6
F test/analyze4.test eff2df19b8dd84529966420f29ea52edc6b56213
F test/analyze5.test 765c4e284aa69ca172772aa940946f55629bc8c4
@@ -349,7 +367,7 @@ F test/auth.test 855233ef26eb3601b6886567ea4e326c72959360
F test/auth2.test 264c6af53cad9aba5218c68bbe18036e39007bfa
F test/auth3.test 5cfa94ed90c6617c42b7ba4b133fd79678b251c7
F test/autoinc.test c58912526998a39e11f66b533e23cfabea7f25b7
F test/autoindex1.test 14b63a9f1e405fe6d5bfc8c8d00249c2ebaf13ea
F test/autoindex1.test 7008c9f604141fdabe31b7bb95b5ff31b518251f
F test/autoindex2.test af7e595c6864cc6ef5fc38d5db579a3e34940cb8
F test/autoindex3.test a3be0d1a53a7d2edff208a5e442312957047e972
F test/autoindex4.test 49d3cd791a9baa16fb461d7ea3de80d019a819cf
@@ -399,9 +417,9 @@ F test/check.test 5831ddb6f2c687782eaf2e1a07b6e17f24c4f763
F test/close.test 340bd24cc58b16c6bc01967402755027c37eb815
F test/closure01.test b1703ba40639cfc9b295cf478d70739415eec6a4
F test/coalesce.test cee0dccb9fbd2d494b77234bccf9dc6c6786eb91
F test/collate1.test 08c18e7512a5a32c97938854263fa15362eeb846
F test/collate1.test 7fcfe78f9613dc4a7e247d6bd27749955f108741
F test/collate2.test 9aaa410a00734e48bcb27f3872617d6f69b2a621
F test/collate3.test 89defc49983ddfbf0a0555aca8c0521a676f56a5
F test/collate3.test 79558a286362cb9ed603c6fa543f1cda7f563f0f
F test/collate4.test f04d5168685f2eef637ecfa2d4ddf8ec0d600177
F test/collate5.test 65d928034d30d2d263a80f6359f7549ee1598ec6
F test/collate6.test 8be65a182abaac8011a622131486dafb8076e907
@@ -505,11 +523,11 @@ F test/fallocate.test 3e979af17dfa7e5e9dda5eba1a696c04fa9d47f7
F test/filectrl.test 14fa712e42c4cb791e09dfd58a6a03efb47ef13a
F test/filefmt.test cb34663f126cbc2d358af552dcaf5c72769b0146
F test/fkey1.test e1d1fa84cde579185ea01358436839703e415a5b
F test/fkey2.test 223c624e7eccee21e89c98d4d127ac88d774b940
F test/fkey2.test 1db212cda86b0d3ce72714001f7b6381c321341c
F test/fkey3.test 76d475c80b84ee7a5d062e56ccb6ea68882e2b49
F test/fkey4.test 86446017011273aad8f9a99c1a65019e7bd9ca9d
F test/fkey5.test 56bcb5a6e8b725b17febc267fb041a6695e86853
F test/fkey6.test abb59f866c1b44926fd02d1fdd217d831fe04f48
F test/fkey6.test 6697550baa38505c9952eff130ab26a2d156c0cc
F test/fkey7.test 72e915890ee4a005daaf3002cb208e8fe973ac13
F test/fkey8.test 8f08203458321e6c19a263829de4cfc936274ab0
F test/fkey_malloc.test 594a7ea1fbab553c036c70813cd8bd9407d63749
@@ -599,14 +617,14 @@ F test/fts3prefix2.test e1f0a822ca661dced7f12ce392e14eaf65609dce
F test/fts3query.test c838b18f2b859e15fd31c64be3d79ef1556803ca
F test/fts3rnd.test 1320d8826a845e38a96e769562bf83d7a92a15d0
F test/fts3shared.test 57e26a801f21027b7530da77db54286a6fe4997e
F test/fts3snippet.test 63dbd687d5bf5191f1b8e6a0977aa9c1e28a7004
F test/fts3snippet.test 03c2f3be7d3b7c8bb105ed237f204833392bd57f
F test/fts3sort.test ed34c716a11cc2009a35210e84ad5f9c102362ca
F test/fts3tok1.test 178c050199af8c05299b1ad572514ce1c54b7827
F test/fts3tok1.test c551043de056b0b1582a54e878991f57bad074bc
F test/fts3tok_err.test 52273cd193b9036282f7bacb43da78c6be87418d
F test/fts3varint.test 752c08ed5d32c5d7dc211b056f4ed68a76b7e36e
F test/fts4aa.test 10aac8e9d62c7357590acfabe3fad01e9a9ce1cb
F test/fts4check.test 74d77f6cdb768ac49df5afda575cef14ae3d239a
F test/fts4content.test abb0c77bc3da3df64fec72e00844d2257a90025d
F test/fts4content.test 2e7252557d6d24afa101d9ba1de710d6140e6d06
F test/fts4docid.test e33c383cfbdff0284685604d256f347a18fdbf01
F test/fts4growth.test df10fde9f47cf5c71861e63fd8efcd573c4f7e53
F test/fts4growth2.test 2f063be1902a73cd087355837c52fed42ac11a5d
@@ -633,11 +651,11 @@ F test/fuzz_malloc.test 328f70aaca63adf29b4c6f06505ed0cf57ca7c26
F test/fuzzer1.test d4c52aaf3ef923da293a2653cfab33d02f718a36
F test/fuzzerfault.test 8792cd77fd5bce765b05d0c8e01b9edcf8af8536
F test/genesis.tcl 1e2e2e8e5cc4058549a154ff1892fe5c9de19f98
F test/hexlit.test 1d312fa816dfd3650a3bb488093bc09a0c927f67
F test/hook.test 162d7cef7a2d2b04839fe14402934e6a1b79442f
F test/hexlit.test f9ecde8145bfc2341573473256c74ae37a200497
F test/hook.test aa41c095d26822b8a51aa4c82904a14347961be6
F test/icu.test 70df4faca133254c042d02ae342c0a141f2663f4
F test/imposter1.test c3f1db2d3db2c24611a6596a3fc0ffc14f1466c8
F test/in.test b52fa96bcf6cebc5c8829c822315d0f87af9c6c2
F test/in.test 047c4671328e9032ab95666a67021adbbd36e98e
F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75
F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0
F test/in4.test d2b38cba404bc4320f4fe1b595b3d163f212c068
@@ -655,7 +673,7 @@ F test/incrvacuum3.test 75256fb1377e7c39ef2de62bfc42bbff67be295a
F test/incrvacuum_ioerr.test 6ae2f783424e47a0033304808fe27789cf93e635
F test/index.test 4d990005a67a36984e4f1a5f1bdccea8d08da4ee
F test/index2.test ee83c6b5e3173a3d7137140d945d9a5d4fdfb9d6
F test/index3.test b6ec456cf3b81d9a32123fe7e449bde434db338b
F test/index3.test 55a90cff99834305e8141df7afaef39674b57062
F test/index4.test ab92e736d5946840236cd61ac3191f91a7856bf6
F test/index5.test 25b0b451aceed4ac5f7d49f856f6de7257470b3e
F test/index6.test 3ae54e53c53f2adcacda269237d8e52bdb05a481
@@ -743,7 +761,7 @@ F test/minmax.test 42fbad0e81afaa6e0de41c960329f2b2c3526efd
F test/minmax2.test b44bae787fc7b227597b01b0ca5575c7cb54d3bc
F test/minmax3.test cc1e8b010136db0d01a6f2a29ba5a9f321034354
F test/minmax4.test 936941484ebdceb8adec7c86b6cd9b6e5e897c1f
F test/misc1.test 9abcae9a0b8785d6fa92925dbb19c309ae9ea077
F test/misc1.test f3f59b3941c84a10860c06c07e86ad367a74ec92
F test/misc2.test 00d7de54eda90e237fc9a38b9e5ccc769ebf6d4d
F test/misc3.test cf3dda47d5dda3e53fc5804a100d3c82be736c9d
F test/misc4.test 9c078510fbfff05a9869a0b6d8b86a623ad2c4f6
@@ -768,10 +786,10 @@ F test/notify1.test 669b2b743618efdc18ca4b02f45423d5d2304abf
F test/notify2.test 2ecabaa1305083856b7c39cf32816b612740c161
F test/notify3.test 10ff25cde502e72a92053a2f215d64bece4ef934
F test/notnull.test f8fcf58669ddba79274daa2770d61dfad8274f62
F test/null.test 0dcce4f04284ec66108c503327ad6d224c0752b3
F test/null.test a8b09b8ed87852742343b33441a9240022108993
F test/numcast.test 5d126f7f581432e86a90d1e35cac625164aec4a1
F test/openv2.test 0d3040974bf402e19b7df4b783e447289d7ab394
F test/orderby1.test 870e150450437d3980badbde3d0166b81d9e33f6
F test/orderby1.test eb246e377612b21a418fbea57047ba8ea88aaa6b
F test/orderby2.test bc11009f7cd99d96b1b11e57b199b00633eb5b04
F test/orderby3.test 8619d06a3debdcd80a27c0fdea5c40b468854b99
F test/orderby4.test 4d39bfbaaa3ae64d026ca2ff166353d2edca4ba4
@@ -793,12 +811,12 @@ F test/pagesize.test 1dd51367e752e742f58e861e65ed7390603827a0
F test/pcache.test b09104b03160aca0d968d99e8cd2c5b1921a993d
F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
F test/permutations.test f9cc1dd987986c9d4949211c7a4ed55ec9aecba1
F test/pragma.test e6605ce89c66db930aef561e43a22281a09ffc66
F test/permutations.test e5e9f708879336e10095ded8d61966007f4a2c5c
F test/pragma.test ad99d05e411c7687302124be56f3b362204be041
F test/pragma2.test f624a496a95ee878e81e59961eade66d5c00c028
F test/pragma3.test 6f849ccffeee7e496d2f2b5e74152306c0b8757c
F test/printf.test b3ff34e73d59124140eaf89f7672e21bc2ca5fcc
F test/printf2.test 0b61566dd1c0f0b802f59dffa228c5dc5aa6b054
F test/printf2.test b4acd4bf8734243257f01ddefa17c4fb090acc8a
F test/progress.test a282973d1d17f08071bc58a77d6b80f2a81c354d
F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc
F test/queryonly.test 5f653159e0f552f0552d43259890c1089391dcca
@@ -812,7 +830,7 @@ F test/randexpr1.test eda062a97e60f9c38ae8d806b03b0ddf23d796df
F test/rdonly.test 64e2696c322e3538df0b1ed624e21f9a23ed9ff8
F test/regexp1.test 497ea812f264d12b6198d6e50a76be4a1973a9d8
F test/reindex.test 44edd3966b474468b823d481eafef0c305022254
F test/releasetest.tcl a37cd82092c8be438255d65804b5951d6e3ecdae
F test/releasetest.tcl 13f401c10dd4fe1a2fb811ae6ed27fd7d1300d3c
F test/resolver01.test 33abf37ff8335e6bf98f2b45a0af3e06996ccd9a
F test/rollback.test 458fe73eb3ffdfdf9f6ba3e9b7350a6220414dea
F test/rollback2.test fc14cf6d1a2b250d2735ef16124b971bce152f14
@@ -839,7 +857,7 @@ F test/securedel2.test 2d54c28e46eb1fd6902089958b20b1b056c6f1c5
F test/select1.test fc2a61f226a649393664ad54bc5376631801517c
F test/select2.test 352480e0e9c66eda9c3044e412abdf5be0215b56
F test/select3.test 2ce595f8fb8e2ac10071d3b4e424cadd4634a054
F test/select4.test 16fa1cafb942f42294ec85cbb78954c2f2d15a44
F test/select4.test e20e8ce47b558de80616102ef273704cf0d48a3b
F test/select5.test e758b8ef94f69b111df4cb819008856655dcd535
F test/select6.test 39eac4a5c03650b2b473c532882273283ee8b7a0
F test/select7.test 7fd2ef598cfabb6b9ff6ac13973b91d0527df49d
@@ -849,10 +867,11 @@ F test/selectA.test e452bdb975f488ea46d091382a9185b5853ed2c7
F test/selectB.test 954e4e49cf1f896d61794e440669e03a27ceea25
F test/selectC.test 871fb55d884d3de5943c4057ebd22c2459e71977
F test/selectD.test b0f02a04ef7737decb24e08be2c39b9664b43394
F test/selectE.test a8730ca330fcf40ace158f134f4fe0eb00c7edbf
F test/selectE.test fc02a1eb04c8eb537091482644b7d778ae8759b7
F test/selectF.test 21c94e6438f76537b72532fa9fd4710cdd455fc3
F test/selectG.test e8600e379589e85e9fefd2fe4d44a4cdd63f6982
F test/server1.test 46803bd3fe8b99b30dbc5ff38ffc756f5c13a118
F test/session.test 78fa2365e93d3663a6e933f86e7afc395adf18be
F test/shared.test 1da9dbad400cee0d93f252ccf76e1ae007a63746
F test/shared2.test 03eb4a8d372e290107d34b6ce1809919a698e879
F test/shared3.test fcd65cb11d189eff5f5c85cc4fad246fb0933108
@@ -865,7 +884,7 @@ F test/sharedA.test 0cdf1a76dfa00e6beee66af5b534b1e8df2720f5
F test/sharedB.test 16cc7178e20965d75278f410943109b77b2e645e
F test/shared_err.test 2f2aee20db294b9924e81f6ccbe60f19e21e8506
F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
F test/shell1.test ce5e744870387164703bf2dee2cc9753e4a71513
F test/shell1.test ca88b14a8fc8b1f3543a24e519d019585ac9c903
F test/shell2.test 12b8bf901b0e3a8ac58cf5c0c63a0a388d4d1862
F test/shell3.test 5e8545ec72c4413a0e8d4c6be56496e3c257ca29
F test/shell4.test ddf0a99044e2245a87fc17423e3aaa1445b3243b
@@ -911,11 +930,11 @@ F test/sysfault.test fa776e60bf46bdd3ae69f0b73e46ee3977a58ae6
F test/table.test 06271d61eb13871490d38168433c1ef3dd82bb2a
F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126
F test/tableopts.test dba698ba97251017b7c80d738c198d39ab747930
F test/tclsqlite.test 7fb866443c7deceed22b63948ccd6f76b52ad054
F test/tclsqlite.test 7179b4e0bf236ddf0bfa6bfaefa76fbe0a23c28a
F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c
F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30
F test/temptrigger.test 8ec228b0db5d7ebc4ee9b458fc28cb9e7873f5e1
F test/tester.tcl 51211254f2ee2340d3e4fa0a83bd5381b9e1a227
F test/tester.tcl 655afed0715958ec50fd575549e6c4e57311ff18
F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
@@ -1082,7 +1101,7 @@ F test/trigger3.test aa640bb2bbb03edd5ff69c055117ea088f121945
F test/trigger4.test 74700b76ebf3947b2f7a92405141eb2cf2a5d359
F test/trigger5.test 619391a3e9fc194081d22cefd830d811e7badf83
F test/trigger6.test 0e411654f122552da6590f0b4e6f781048a4a9b9
F test/trigger7.test 200dd51e728c9cdc20c72d99d9e9d45c667248f8
F test/trigger7.test b39e6dee1debe0ff9c2ef66326668f149f07c9c4
F test/trigger8.test 30cb0530bd7c4728055420e3f739aa00412eafa4
F test/trigger9.test 2226ec795a33b0460ab5cf8891e9054cc7edef41
F test/triggerA.test fe5597f47ee21bacb4936dc827994ed94161e332
@@ -1107,7 +1126,7 @@ F test/uri.test 23662b7b61958b0f0e47082de7d06341ccf85d5b
F test/userauth01.test e740a2697a7b40d7c5003a7d7edaee16acd349a9
F test/utf16align.test 54cd35a27c005a9b6e7815d887718780b6a462ae
F test/vacuum.test ce91c39f7f91a4273bf620efad21086b5aa6ef1d
F test/vacuum2.test aa048abee196c16c9ba308465494009057b79f9b
F test/vacuum2.test af432e6e3bfc0ea20a80cb86a03c7d9876d38324
F test/vacuum3.test 77ecdd54592b45a0bcb133339f99f1ae0ae94d0d
F test/vacuum4.test d3f8ecff345f166911568f397d2432c16d2867d9
F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102
@@ -1171,7 +1190,7 @@ F test/where9.test 729c3ba9b47e8f9f1aab96bae7dad2a524f1d1a2
F test/whereA.test 4d253178d135ec46d1671e440cd8f2b916aa6e6b
F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5
F test/whereC.test d6f4ecd4fa2d9429681a5b22a25d2bda8e86ab8a
F test/whereD.test 9eba1f9b18e5b19a0b0bcaae5e8c037260195f2b
F test/whereD.test fd9120e262f9da3c45940f52aefeef4d15b904e5
F test/whereE.test b3a055eef928c992b0a33198a7b8dc10eea5ad2f
F test/whereF.test 5b2ba0dbe8074aa13e416b37c753991f0a2492d7
F test/whereG.test 69f5ec4b15760a8c860f80e2d55525669390aab3
@@ -1184,7 +1203,7 @@ F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c
F test/win32heap.test ea19770974795cff26e11575e12d422dbd16893c
F test/win32lock.test fbf107c91d8f5512be5a5b87c4c42ab9fdd54972
F test/win32longpath.test 169c75a3b2e43481f4a62122510210c67b08f26d
F test/with1.test a86bf7f9288ba759a25ee57221d3bffaca36032a
F test/with1.test 9df5cd8a62148b3d9ef8597aea563e3863018bcd
F test/with2.test ee227a663586aa09771cafd4fa269c5217eaf775
F test/withM.test e97f2a8c506ab3ea9eab94e6f6072f6cc924c991
F test/without_rowid1.test 7862e605753c8d25329f665fa09072e842183151
@@ -1194,7 +1213,7 @@ F test/without_rowid4.test 4e08bcbaee0399f35d58b5581881e7a6243d458a
F test/without_rowid5.test 61256715b686359df48ca1742db50cc7e3e7b862
F test/without_rowid6.test db0dbf03c49030aa3c1ba5f618620334bd2baf5f
F test/wordcount.c 9915e06cb33d8ca8109b8700791afe80d305afda
F test/zeroblob.test fb3c0e4ab172d386954deda24c03f500e121d80d
F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688
F test/zerodamage.test cf6748bad89553cc1632be51a6f54e487e4039ac
F tool/build-all-msvc.bat 72e05bc8deca39a547884485c086b915f50a91ed x
F tool/build-shell.sh 950f47c6174f1eea171319438b93ba67ff5bf367
@@ -1215,8 +1234,8 @@ F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e
F tool/mkpragmatab.tcl 94f196c9961e0ca3513e29f57125a3197808be2d
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
F tool/mksqlite3c-noext.tcl 69bae8ce4aa52d2ff82d4a8a856bf283ec035b2e
F tool/mksqlite3c.tcl 52a3352f7aa15f1db851e45ac3a5e2173d6fe93c
F tool/mksqlite3h.tcl 44730d586c9031638cdd2eb443b801c0d2dbd9f8
F tool/mksqlite3c.tcl ebbf1198e19d84b4c34cca077d8f727a06ff8a11
F tool/mksqlite3h.tcl 96d92fcac21c6037d9db20c7cb2e06b534b550ac
F tool/mksqlite3internalh.tcl eb994013e833359137eb53a55acdad0b5ae1049b
F tool/mkvsix.tcl 52a4c613707ac34ae9c226e5ccc69cb948556105
F tool/offsets.c fe4262fdfa378e8f5499a42136d17bf3b98f6091
@@ -1225,7 +1244,7 @@ F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c
F tool/pagesig.c ff0ca355fd3c2398e933da5e22439bbff89b803b
F tool/restore_jrnl.tcl 6957a34f8f1f0f8285e07536225ec3b292a9024a
F tool/rollback-test.c 9fc98427d1e23e84429d7e6d07d9094fbdec65a5
F tool/showdb.c 3b5d335d537e4dc44d0c86967023819453c87dc6
F tool/showdb.c 63cdef19e7fbca0c164b096ef8aef3bb9e9dd222
F tool/showjournal.c 053eb1cc774710c6890b7dd6293300cc297b16a5
F tool/showlocks.c 9920bcc64f58378ff1118caead34147201f48c68
F tool/showstat4.c 9515faa8ec176599d4a8288293ba8ec61f7b728a
@@ -1239,10 +1258,9 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c
F tool/sqldiff.c 5c16cf3a1f566873abbdecac0d13a6691437564f
F tool/stack_usage.tcl f8e71b92cdb099a147dad572375595eae55eca43
F tool/symbols-mingw.sh 4dbcea7e74768305384c9fd2ed2b41bbf9f0414d
F tool/symbols.sh fec58532668296d7c7dc48be9c87f75ccdb5814f
F tool/symbols.sh c5a617b8c61a0926747a56c65f5671ef8ac0e148
F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06
F tool/varint.c 5d94cb5003db9dbbcbcc5df08d66f16071aee003
F tool/vdbe-compress.tcl 5926c71f9c12d2ab73ef35c29376e756eb68361c
@@ -1250,7 +1268,10 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
P c458db41bbb6bd13d47531685e25a09f512c7f5f d3c00d61581c8ba6dce5618391432d3af8d324d4
R 2c042385d701fe541a26946cba425218
U mistachkin
Z e84ee44408bcf2cec5bcea635dbd30f2
P aeca95ac77f6f320a916f7e3c5a7a588ef4a20c8
R 5c3cbffcff665bc16c7e835a4d990399
T *branch * sessions-diff
T *sym-sessions-diff *
T -sym-sessions *
U dan
Z b513e16008d3799dcac5a3d5f415d7d8
+1 -1
View File
@@ -1 +1 @@
22827542a5c7aeac4385ba647f45bd500e787fe6
c4340b2ea2115ad97dcac036f9034e132ab789e5
+16 -11
View File
@@ -72,6 +72,7 @@
sub("\r","",name)
op[name] = -1 # op[x] holds the numeric value for OP symbol x
jump[name] = 0
out2_prerelease[name] = 0
in1[name] = 0
in2[name] = 0
in3[name] = 0
@@ -91,6 +92,8 @@
sub(",","",x)
if(x=="jump"){
jump[name] = 1
}else if(x=="out2-prerelease"){
out2_prerelease[name] = 1
}else if(x=="in1"){
in1[name] = 1
}else if(x=="in2"){
@@ -191,12 +194,13 @@ END {
name = def[i]
a0 = a1 = a2 = a3 = a4 = a5 = a6 = a7 = 0
if( jump[name] ) a0 = 1;
if( in1[name] ) a2 = 2;
if( in2[name] ) a3 = 4;
if( in3[name] ) a4 = 8;
if( out2[name] ) a5 = 16;
if( out3[name] ) a6 = 32;
bv[i] = a0+a1+a2+a3+a4+a5+a6;
if( out2_prerelease[name] ) a1 = 2;
if( in1[name] ) a2 = 4;
if( in2[name] ) a3 = 8;
if( in3[name] ) a4 = 16;
if( out2[name] ) a5 = 32;
if( out3[name] ) a6 = 64;
bv[i] = a0+a1+a2+a3+a4+a5+a6+a7;
}
print "\n"
print "/* Properties such as \"out2\" or \"jump\" that are specified in"
@@ -204,11 +208,12 @@ END {
print "** are encoded into bitvectors as follows:"
print "*/"
print "#define OPFLG_JUMP 0x0001 /* jump: P2 holds jmp target */"
print "#define OPFLG_IN1 0x0002 /* in1: P1 is an input */"
print "#define OPFLG_IN2 0x0004 /* in2: P2 is an input */"
print "#define OPFLG_IN3 0x0008 /* in3: P3 is an input */"
print "#define OPFLG_OUT2 0x0010 /* out2: P2 is an output */"
print "#define OPFLG_OUT3 0x0020 /* out3: P3 is an output */"
print "#define OPFLG_OUT2_PRERELEASE 0x0002 /* out2-prerelease: */"
print "#define OPFLG_IN1 0x0004 /* in1: P1 is an input */"
print "#define OPFLG_IN2 0x0008 /* in2: P2 is an input */"
print "#define OPFLG_IN3 0x0010 /* in3: P3 is an input */"
print "#define OPFLG_OUT2 0x0020 /* out2: P2 is an output */"
print "#define OPFLG_OUT3 0x0040 /* out3: P3 is an output */"
print "#define OPFLG_INITIALIZER {\\"
for(i=0; i<=max; i++){
if( i%8==0 ) printf("/* %3d */",i)
+5 -12
View File
@@ -53,13 +53,6 @@
# define GETPID getpid
#endif
/* The directory separator character(s) */
#if defined(_WIN32)
# define isDirSep(c) (((c) == '/') || ((c) == '\\'))
#else
# define isDirSep(c) ((c) == '/')
#endif
/* Mark a parameter as unused to suppress compiler warnings */
#define UNUSED_PARAMETER(x) (void)x
@@ -831,7 +824,7 @@ static void waitForClient(int iClient, int iTimeout, char *zErrPrefix){
*/
static char *filenameTail(char *z){
int i, j;
for(i=j=0; z[i]; i++) if( isDirSep(z[i]) ) j = i+1;
for(i=j=0; z[i]; i++) if( z[i]=='/' ) j = i+1;
return z+j;
}
@@ -1028,9 +1021,9 @@ static void runScript(
char *zNewFile, *zNewScript;
char *zToDel = 0;
zNewFile = azArg[0];
if( !isDirSep(zNewFile[0]) ){
if( zNewFile[0]!='/' ){
int k;
for(k=(int)strlen(zFilename)-1; k>=0 && !isDirSep(zFilename[k]); k--){}
for(k=(int)strlen(zFilename)-1; k>=0 && zFilename[k]!='/'; k--){}
if( k>0 ){
zNewFile = zToDel = sqlite3_mprintf("%.*s/%s", k,zFilename,zNewFile);
}
@@ -1238,7 +1231,7 @@ static void usage(const char *argv0){
int i;
const char *zTail = argv0;
for(i=0; argv0[i]; i++){
if( isDirSep(argv0[i]) ) zTail = argv0+i+1;
if( argv0[i]=='/' ) zTail = argv0+i+1;
}
fprintf(stderr,"Usage: %s DATABASE ?OPTIONS? ?SCRIPT?\n", zTail);
exit(1);
@@ -1345,7 +1338,6 @@ int SQLITE_CDECL main(int argc, char **argv){
#endif
runSql("PRAGMA journal_mode=%Q;", zJMode);
}
if( !g.bSync ) trySql("PRAGMA synchronous=OFF");
sqlite3_enable_load_extension(g.db, 1);
sqlite3_busy_handler(g.db, busyHandler, 0);
sqlite3_create_function(g.db, "vfsname", 0, SQLITE_UTF8, 0,
@@ -1354,6 +1346,7 @@ int SQLITE_CDECL main(int argc, char **argv){
evalFunc, 0, 0);
g.iTimeout = DEFAULT_TIMEOUT;
if( g.bSqlTrace ) sqlite3_trace(g.db, sqlTraceCallback, 0);
if( !g.bSync ) trySql("PRAGMA synchronous=OFF");
if( iClient>0 ){
if( n>0 ) unrecognizedArguments(argv[0], n, argv+2);
if( g.iTrace ) logMessage("start-client");
+1 -1
View File
@@ -298,7 +298,7 @@ static void detachFunc(
sqlite3BtreeClose(pDb->pBt);
pDb->pBt = 0;
pDb->pSchema = 0;
sqlite3CollapseDatabaseArray(db);
sqlite3ResetAllSchemasOfConnection(db);
return;
detach_error:
+2 -6
View File
@@ -4451,18 +4451,13 @@ static const void *fetchPayload(
BtCursor *pCur, /* Cursor pointing to entry to read from */
u32 *pAmt /* Write the number of available bytes here */
){
u32 amt;
assert( pCur!=0 && pCur->iPage>=0 && pCur->apPage[pCur->iPage]);
assert( pCur->eState==CURSOR_VALID );
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
assert( cursorHoldsMutex(pCur) );
assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell );
assert( pCur->info.nSize>0 );
assert( pCur->info.pPayload>pCur->apPage[pCur->iPage]->aData || CORRUPT_DB );
assert( pCur->info.pPayload<pCur->apPage[pCur->iPage]->aDataEnd ||CORRUPT_DB);
amt = (int)(pCur->apPage[pCur->iPage]->aDataEnd - pCur->info.pPayload);
if( pCur->info.nLocal<amt ) amt = pCur->info.nLocal;
*pAmt = amt;
*pAmt = pCur->info.nLocal;
return (void*)pCur->info.pPayload;
}
@@ -6740,6 +6735,7 @@ static int balance_nonroot(
}else if( iParentIdx==i ){
nxDiv = i-2+bBulk;
}else{
assert( bBulk==0 );
nxDiv = iParentIdx-1;
}
i = 2-bBulk;
+3 -3
View File
@@ -226,7 +226,7 @@ void sqlite3FinishCoding(Parse *pParse){
/* Get the VDBE program ready for execution
*/
if( v && pParse->nErr==0 && !db->mallocFailed ){
if( v && ALWAYS(pParse->nErr==0) && !db->mallocFailed ){
assert( pParse->iCacheLevel==0 ); /* Disables and re-enables match */
/* A minimum of one cursor is required if autoincrement is used
* See ticket [a696379c1f08866] */
@@ -2763,8 +2763,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
addr2 = sqlite3VdbeCurrentAddr(v);
}
sqlite3VdbeAddOp3(v, OP_SorterData, iSorter, regRecord, iIdx);
sqlite3VdbeAddOp3(v, OP_Last, iIdx, 0, -1);
sqlite3VdbeAddOp3(v, OP_IdxInsert, iIdx, regRecord, 0);
sqlite3VdbeAddOp3(v, OP_IdxInsert, iIdx, regRecord, 1);
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
sqlite3ReleaseTempReg(pParse, regRecord);
sqlite3VdbeAddOp2(v, OP_SorterNext, iSorter, addr2); VdbeCoverage(v);
@@ -3777,6 +3776,7 @@ void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pIndexedBy){
void sqlite3SrcListShiftJoinType(SrcList *p){
if( p ){
int i;
assert( p->a || p->nSrc==0 );
for(i=p->nSrc-1; i>0; i--){
p->a[i].jointype = p->a[i-1].jointype;
}
+1 -1
View File
@@ -269,7 +269,7 @@ int sqlite3_complete(const char *zSql){
int sqlite3_complete16(const void *zSql){
sqlite3_value *pVal;
char const *zSql8;
int rc;
int rc = SQLITE_NOMEM;
#ifndef SQLITE_OMIT_AUTOINIT
rc = sqlite3_initialize();
+18 -6
View File
@@ -358,8 +358,15 @@ void sqlite3DeleteFrom(
/* Special case: A DELETE without a WHERE clause deletes everything.
** It is easier just to erase the whole table. Prior to version 3.6.5,
** this optimization caused the row change count (the value returned by
** API function sqlite3_count_changes) to be set incorrectly. */
if( rcauth==SQLITE_OK && pWhere==0 && !pTrigger && !IsVirtual(pTab)
** API function sqlite3_count_changes) to be set incorrectly.
*/
if( rcauth==SQLITE_OK
&& pWhere==0
&& !pTrigger
&& !IsVirtual(pTab)
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
&& db->xPreUpdateCallback==0
#endif
&& 0==sqlite3FkRequired(pParse, pTab, 0, 0)
){
assert( !isView );
@@ -671,13 +678,18 @@ void sqlite3GenerateRowDelete(
/* Delete the index and table entries. Skip this step if pTab is really
** a view (in which case the only effect of the DELETE statement is to
** fire the INSTEAD OF triggers). */
** fire the INSTEAD OF triggers).
**
** If variable 'count' is non-zero, then this OP_Delete instruction should
** invoke the update-hook. The pre-update-hook, on the other hand should
** be invoked unless table pTab is a system table. The difference is that
** the update-hook is not invoked for rows removed by REPLACE, but the
** pre-update-hook is.
*/
if( pTab->pSelect==0 ){
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, 0);
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, (count?OPFLAG_NCHANGE:0));
if( count ){
sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT);
}
sqlite3VdbeChangeP4(v, -1, (char*)pTab, P4_TABLE);
}
/* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to
+1 -2
View File
@@ -1691,7 +1691,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, u32 inFlags, int *prRhsHasNull){
** ephemeral table.
*/
p = (ExprHasProperty(pX, EP_xIsSelect) ? pX->x.pSelect : 0);
if( pParse->nErr==0 && isCandidateForInOpt(p) ){
if( ALWAYS(pParse->nErr==0) && isCandidateForInOpt(p) ){
sqlite3 *db = pParse->db; /* Database connection */
Table *pTab; /* Table <table>. */
Expr *pExpr; /* Expression <column> */
@@ -2016,7 +2016,6 @@ int sqlite3CodeSubselect(
pSel->pLimit = sqlite3PExpr(pParse, TK_INTEGER, 0, 0,
&sqlite3IntTokens[1]);
pSel->iLimit = 0;
pSel->selFlags &= ~SF_AllValues;
if( sqlite3Select(pParse, pSel, &dest) ){
return 0;
}
+1 -2
View File
@@ -1184,8 +1184,7 @@ static Trigger *fkActionTrigger(
iFromCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom;
assert( iFromCol>=0 );
assert( pIdx!=0 || (pTab->iPKey>=0 && pTab->iPKey<pTab->nCol) );
tToCol.z = pTab->aCol[pIdx ? pIdx->aiColumn[i] : pTab->iPKey].zName;
tToCol.z = pIdx ? pTab->aCol[pIdx->aiColumn[i]].zName : "oid";
tFromCol.z = pFKey->pFrom->aCol[iFromCol].zName;
tToCol.n = sqlite3Strlen30(tToCol.z);
+25 -46
View File
@@ -1332,9 +1332,21 @@ void sqlite3GenerateConstraintChecks(
sqlite3MultiWrite(pParse);
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
regNewData, 1, 0, OE_Replace, 1);
}else if( pTab->pIndex ){
sqlite3MultiWrite(pParse);
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, 0);
}else{
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
if( HasRowid(pTab) ){
/* This OP_Delete opcode fires the pre-update-hook only. It does
** not modify the b-tree. It is more efficient to let the coming
** OP_Insert replace the existing entry than it is to delete the
** existing entry and then insert a new one. */
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, OPFLAG_ISNOOP);
sqlite3VdbeChangeP4(v, -1, (char *)pTab, P4_TABLE);
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
if( pTab->pIndex ){
sqlite3MultiWrite(pParse);
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, 0);
}
}
seenReplace = 1;
break;
@@ -1590,7 +1602,7 @@ void sqlite3CompleteInsertion(
}
sqlite3VdbeAddOp3(v, OP_Insert, iDataCur, regRec, regNewData);
if( !pParse->nested ){
sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT);
sqlite3VdbeChangeP4(v, -1, (char *)pTab, P4_TABLE);
}
sqlite3VdbeChangeP5(v, pik_flags);
}
@@ -1765,7 +1777,6 @@ static int xferOptimization(
int onError, /* How to handle constraint errors */
int iDbDest /* The database of pDest */
){
sqlite3 *db = pParse->db;
ExprList *pEList; /* The result set of the SELECT */
Table *pSrc; /* The table in the FROM clause of SELECT */
Index *pSrcIdx, *pDestIdx; /* Source and destination indices */
@@ -1913,11 +1924,11 @@ static int xferOptimization(
** the extra complication to make this rule less restrictive is probably
** not worth the effort. Ticket [6284df89debdfa61db8073e062908af0c9b6118e]
*/
if( (db->flags & SQLITE_ForeignKeys)!=0 && pDest->pFKey!=0 ){
if( (pParse->db->flags & SQLITE_ForeignKeys)!=0 && pDest->pFKey!=0 ){
return 0;
}
#endif
if( (db->flags & SQLITE_CountRows)!=0 ){
if( (pParse->db->flags & SQLITE_CountRows)!=0 ){
return 0; /* xfer opt does not play well with PRAGMA count_changes */
}
@@ -1928,7 +1939,7 @@ static int xferOptimization(
#ifdef SQLITE_TEST
sqlite3_xferopt_count++;
#endif
iDbSrc = sqlite3SchemaToIndex(db, pSrc->pSchema);
iDbSrc = sqlite3SchemaToIndex(pParse->db, pSrc->pSchema);
v = sqlite3GetVdbe(pParse);
sqlite3CodeVerifySchema(pParse, iDbSrc);
iSrc = pParse->nTab++;
@@ -1938,18 +1949,14 @@ static int xferOptimization(
regRowid = sqlite3GetTempReg(pParse);
sqlite3OpenTable(pParse, iDest, iDbDest, pDest, OP_OpenWrite);
assert( HasRowid(pDest) || destHasUniqueIdx );
if( (db->flags & SQLITE_Vacuum)==0 && (
(pDest->iPKey<0 && pDest->pIndex!=0) /* (1) */
if( (pDest->iPKey<0 && pDest->pIndex!=0) /* (1) */
|| destHasUniqueIdx /* (2) */
|| (onError!=OE_Abort && onError!=OE_Rollback) /* (3) */
)){
){
/* In some circumstances, we are able to run the xfer optimization
** only if the destination table is initially empty. Unless the
** SQLITE_Vacuum flag is set, this block generates code to make
** that determination. If SQLITE_Vacuum is set, then the destination
** table is always empty.
**
** Conditions under which the destination must be empty:
** only if the destination table is initially empty. This code makes
** that determination. Conditions under which the destination must
** be empty:
**
** (1) There is no INTEGER PRIMARY KEY but there are indices.
** (If the destination is not initially empty, the rowid fields
@@ -1983,7 +1990,7 @@ static int xferOptimization(
sqlite3VdbeAddOp2(v, OP_RowData, iSrc, regData);
sqlite3VdbeAddOp3(v, OP_Insert, iDest, regData, regRowid);
sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND);
sqlite3VdbeChangeP4(v, -1, pDest->zName, 0);
sqlite3VdbeChangeP4(v, -1, (char*)pDest, P4_TABLE);
sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);
sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);
@@ -1992,7 +1999,6 @@ static int xferOptimization(
sqlite3TableLock(pParse, iDbSrc, pSrc->tnum, 0, pSrc->zName);
}
for(pDestIdx=pDest->pIndex; pDestIdx; pDestIdx=pDestIdx->pNext){
u8 useSeekResult = 0;
for(pSrcIdx=pSrc->pIndex; ALWAYS(pSrcIdx); pSrcIdx=pSrcIdx->pNext){
if( xferCompatibleIndex(pDestIdx, pSrcIdx) ) break;
}
@@ -2006,34 +2012,7 @@ static int xferOptimization(
VdbeComment((v, "%s", pDestIdx->zName));
addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_RowKey, iSrc, regData);
if( db->flags & SQLITE_Vacuum ){
/* This INSERT command is part of a VACUUM operation, which guarantees
** that the destination table is empty. If all indexed columns use
** collation sequence BINARY, then it can also be assumed that the
** index will be populated by inserting keys in strictly sorted
** order. In this case, instead of seeking within the b-tree as part
** of every OP_IdxInsert opcode, an OP_Last is added before the
** OP_IdxInsert to seek to the point within the b-tree where each key
** should be inserted. This is faster.
**
** If any of the indexed columns use a collation sequence other than
** BINARY, this optimization is disabled. This is because the user
** might change the definition of a collation sequence and then run
** a VACUUM command. In that case keys may not be written in strictly
** sorted order. */
int i;
for(i=0; i<pSrcIdx->nColumn; i++){
char *zColl = pSrcIdx->azColl[i];
assert( zColl!=0 );
if( sqlite3_stricmp("BINARY", zColl) ) break;
}
if( i==pSrcIdx->nColumn ){
useSeekResult = OPFLAG_USESEEKRESULT;
sqlite3VdbeAddOp3(v, OP_Last, iDest, 0, -1);
}
}
sqlite3VdbeAddOp3(v, OP_IdxInsert, iDest, regData, 1);
sqlite3VdbeChangeP5(v, useSeekResult);
sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1); VdbeCoverage(v);
sqlite3VdbeJumpHere(v, addr1);
sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);
+21
View File
@@ -1856,6 +1856,27 @@ void *sqlite3_rollback_hook(
return pRet;
}
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
/*
** Register a callback to be invoked each time a row is updated,
** inserted or deleted using this database connection.
*/
void *sqlite3_preupdate_hook(
sqlite3 *db, /* Attach the hook to this database */
void(*xCallback)( /* Callback function */
void*,sqlite3*,int,char const*,char const*,sqlite3_int64,sqlite3_int64),
void *pArg /* First callback argument */
){
void *pRet;
sqlite3_mutex_enter(db->mutex);
pRet = db->pPreUpdateArg;
db->xPreUpdateCallback = xCallback;
db->pPreUpdateArg = pArg;
sqlite3_mutex_leave(db->mutex);
return pRet;
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
#ifndef SQLITE_OMIT_WAL
/*
** The sqlite3_wal_hook() callback registered by sqlite3_wal_autocheckpoint().
-1
View File
@@ -20,7 +20,6 @@
#pragma warning(disable : 4055)
#pragma warning(disable : 4100)
#pragma warning(disable : 4127)
#pragma warning(disable : 4130)
#pragma warning(disable : 4152)
#pragma warning(disable : 4189)
#pragma warning(disable : 4206)
+2 -14
View File
@@ -91,17 +91,6 @@
# include <sys/param.h>
#endif /* SQLITE_ENABLE_LOCKING_STYLE */
#if defined(__APPLE__) && ((__MAC_OS_X_VERSION_MIN_REQUIRED > 1050) || \
(__IPHONE_OS_VERSION_MIN_REQUIRED > 2000))
# if (!defined(TARGET_OS_EMBEDDED) || (TARGET_OS_EMBEDDED==0)) \
&& (!defined(TARGET_IPHONE_SIMULATOR) || (TARGET_IPHONE_SIMULATOR==0))
# define HAVE_GETHOSTUUID 1
# else
# warning "gethostuuid() is disabled."
# endif
#endif
#if OS_VXWORKS
# include <sys/ioctl.h>
# include <semaphore.h>
@@ -6613,10 +6602,8 @@ int sqlite3_hostid_num = 0;
#define PROXY_HOSTIDLEN 16 /* conch file host id length */
#ifdef HAVE_GETHOSTUUID
/* Not always defined in the headers as it ought to be */
extern int gethostuuid(uuid_t id, const struct timespec *wait);
#endif
/* get the host ID via gethostuuid(), pHostID must point to PROXY_HOSTIDLEN
** bytes of writable memory.
@@ -6624,7 +6611,8 @@ extern int gethostuuid(uuid_t id, const struct timespec *wait);
static int proxyGetHostID(unsigned char *pHostID, int *pError){
assert(PROXY_HOSTIDLEN == sizeof(uuid_t));
memset(pHostID, 0, PROXY_HOSTIDLEN);
#ifdef HAVE_GETHOSTUUID
# if defined(__APPLE__) && ((__MAC_OS_X_VERSION_MIN_REQUIRED > 1050) || \
(__IPHONE_OS_VERSION_MIN_REQUIRED > 2000))
{
struct timespec timeout = {1, 0}; /* 1 sec timeout */
if( gethostuuid(pHostID, &timeout) ){
+1 -1
View File
@@ -1041,7 +1041,7 @@ void sqlite3Pragma(
}else if( pPk==0 ){
k = 1;
}else{
for(k=1; k<=pTab->nCol && pPk->aiColumn[k-1]!=i; k++){}
for(k=1; ALWAYS(k<=pTab->nCol) && pPk->aiColumn[k-1]!=i; k++){}
}
sqlite3VdbeAddOp2(v, OP_Integer, k, 6);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6);
+3 -3
View File
@@ -67,7 +67,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */
if( argv[1]==0 ){
corruptSchema(pData, argv[0], 0);
}else if( sqlite3_strnicmp(argv[2],"create ",7)==0 ){
}else if( argv[2] && argv[2][0] ){
/* Call the parser to process a CREATE TABLE, INDEX or VIEW.
** But because db->init.busy is set to 1, no VDBE code is generated
** or executed. All the parser does is build the internal data
@@ -98,8 +98,8 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
}
}
sqlite3_finalize(pStmt);
}else if( argv[0]==0 || (argv[2]!=0 && argv[2][0]!=0) ){
corruptSchema(pData, argv[0], 0);
}else if( argv[0]==0 ){
corruptSchema(pData, 0, 0);
}else{
/* If the SQL column is blank it means this is an index that
** was created to be the PRIMARY KEY or to fulfill a UNIQUE
+1 -1
View File
@@ -826,7 +826,7 @@ static void SQLITE_NOINLINE enlargeAndAppend(StrAccum *p, const char *z, int N){
** size of the memory allocation for StrAccum if necessary.
*/
void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
assert( z!=0 || N==0 );
assert( z!=0 );
assert( p->zText!=0 || p->nChar==0 || p->accError );
assert( N>=0 );
assert( p->accError==0 || p->nAlloc==0 );
+4 -7
View File
@@ -460,7 +460,6 @@ static int lookupName(
if( cnt==0 && zTab==0 && ExprHasProperty(pExpr,EP_DblQuoted) ){
pExpr->op = TK_STRING;
pExpr->pTab = 0;
pExpr->iTable = -1;
return WRC_Prune;
}
@@ -994,11 +993,9 @@ static int resolveCompoundOrderBy(
if( pItem->pExpr==pE ){
pItem->pExpr = pNew;
}else{
Expr *pParent = pItem->pExpr;
assert( pParent->op==TK_COLLATE );
while( pParent->pLeft->op==TK_COLLATE ) pParent = pParent->pLeft;
assert( pParent->pLeft==pE );
pParent->pLeft = pNew;
assert( pItem->pExpr->op==TK_COLLATE );
assert( pItem->pExpr->pLeft==pE );
pItem->pExpr->pLeft = pNew;
}
sqlite3ExprDelete(db, pE);
pItem->u.x.iOrderByCol = (u16)iCol;
@@ -1198,7 +1195,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
** after the names have been resolved. */
if( p->selFlags & SF_Converted ){
Select *pSub = p->pSrc->a[0].pSelect;
assert( p->pSrc->nSrc==1 && p->pOrderBy );
assert( p->pSrc->nSrc==1 && isCompound==0 && p->pOrderBy );
assert( pSub->pPrior && pSub->pOrderBy==0 );
pSub->pOrderBy = p->pOrderBy;
p->pOrderBy = 0;
+1 -2
View File
@@ -3071,7 +3071,7 @@ static int multiSelectOrderBy(
/*** TBD: Insert subroutine calls to close cursors on incomplete
**** subqueries ****/
explainComposite(pParse, p->op, iSub1, iSub2, 0);
return pParse->nErr!=0;
return SQLITE_OK;
}
#endif
@@ -3883,7 +3883,6 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){
pNew->pOrderBy = 0;
p->pPrior = 0;
p->pNext = 0;
p->pWith = 0;
p->selFlags &= ~SF_Compound;
assert( (p->selFlags & SF_Converted)==0 );
p->selFlags |= SF_Converted;
+288 -47
View File
@@ -504,6 +504,19 @@ static char *one_input_line(FILE *in, char *zPrior, int isContinuation){
return zResult;
}
#if defined(SQLITE_ENABLE_SESSION)
/*
** State information for a single open session
*/
typedef struct OpenSession OpenSession;
struct OpenSession {
char *zName; /* Symbolic name for this session */
int nFilter; /* Number of xFilter rejection GLOB patterns */
char **azFilter; /* Array of xFilter rejection GLOB patterns */
sqlite3_session *p; /* The open session */
};
#endif
/*
** Shell output mode information from before ".explain on",
** saved so that it can be restored by ".explain off"
@@ -553,6 +566,10 @@ struct ShellState {
int *aiIndent; /* Array of indents used in MODE_Explain */
int nIndent; /* Size of array aiIndent[] */
int iIndent; /* Index of current op in aiIndent[] */
#if defined(SQLITE_ENABLE_SESSION)
int nSession; /* Number of active sessions */
OpenSession aSession[4]; /* Array of sessions. [0] is in focus. */
#endif
};
/*
@@ -989,16 +1006,7 @@ static int shell_callback(
case MODE_Insert: {
p->cnt++;
if( azArg==0 ) break;
fprintf(p->out,"INSERT INTO %s",p->zDestTable);
if( p->showHeader ){
fprintf(p->out,"(");
for(i=0; i<nArg; i++){
char *zSep = i>0 ? ",": "";
fprintf(p->out, "%s%s", zSep, azCol[i]);
}
fprintf(p->out,")");
}
fprintf(p->out," VALUES(");
fprintf(p->out,"INSERT INTO %s VALUES(",p->zDestTable);
for(i=0; i<nArg; i++){
char *zSep = i>0 ? ",": "";
if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){
@@ -1780,7 +1788,6 @@ static int run_schema_dump_query(
static char zHelp[] =
".backup ?DB? FILE Backup DB (default \"main\") to FILE\n"
".bail on|off Stop after hitting an error. Default OFF\n"
".binary on|off Turn binary output on or off. Default OFF\n"
".clone NEWDB Clone data into NEWDB from the existing database\n"
".databases List names and files of attached databases\n"
".dbinfo ?DB? Show status information about the database\n"
@@ -1832,6 +1839,9 @@ static char zHelp[] =
" LIKE pattern TABLE.\n"
".separator COL ?ROW? Change the column separator and optionally the row\n"
" separator for both the output mode and .import\n"
#if defined(SQLITE_ENABLE_SESSION)
".session CMD ... Create or control sessions\n"
#endif
".shell CMD ARGS... Run CMD ARGS... in a system shell\n"
".show Show the current values for various settings\n"
".stats on|off Turn stats on or off\n"
@@ -1847,6 +1857,30 @@ static char zHelp[] =
" Negative values right-justify\n"
;
#if defined(SQLITE_ENABLE_SESSION)
/*
** Print help information for the ".sessions" command
*/
void session_help(ShellState *p){
fprintf(p->out,
".session ?NAME? SUBCOMMAND ?ARGS...?\n"
"If ?NAME? is omitted, the first defined session is used.\n"
"Subcommands:\n"
" attach TABLE Attach TABLE\n"
" changeset FILE Write a changeset into FILE\n"
" close Close one session\n"
" enable ?BOOLEAN? Set or query the enable bit\n"
" filter GLOB... Reject tables matching GLOBs\n"
" indirect ?BOOLEAN? Mark or query the indirect status\n"
" isempty Query whether the session is empty\n"
" list List currently open session names\n"
" open DB NAME Open a new session on DB\n"
" patchset FILE Write a patchset into FILE\n"
);
}
#endif
/* Forward reference */
static int process_input(ShellState *p, FILE *in);
/*
@@ -1910,6 +1944,51 @@ static void writefileFunc(
sqlite3_result_int64(context, rc);
}
#if defined(SQLITE_ENABLE_SESSION)
/*
** Close a single OpenSession object and release all of its associated
** resources.
*/
static void session_close(OpenSession *pSession){
int i;
sqlite3session_delete(pSession->p);
sqlite3_free(pSession->zName);
for(i=0; i<pSession->nFilter; i++){
sqlite3_free(pSession->azFilter[i]);
}
sqlite3_free(pSession->azFilter);
memset(pSession, 0, sizeof(OpenSession));
}
#endif
/*
** Close all OpenSession objects and release all assocaited resources.
*/
static void session_close_all(ShellState *p){
#if defined(SQLITE_ENABLE_SESSION)
int i;
for(i=0; i<p->nSession; i++){
session_close(&p->aSession[i]);
}
p->nSession = 0;
#endif
}
/*
** Implementation of the xFilter function for an open session. Omit
** any tables named by ".session filter" but let all other table through.
*/
#if defined(SQLITE_ENABLE_SESSION)
static int session_filter(void *pCtx, const char *zTab){
OpenSession *pSession = (OpenSession*)pCtx;
int i;
for(i=0; i<pSession->nFilter; i++){
if( sqlite3_strglob(pSession->azFilter[i], zTab)==0 ) return 0;
}
return 1;
}
#endif
/*
** Make sure the database is open. If it is not, then open it. If
** the database fails to open, print an error message and exit.
@@ -1942,44 +2021,26 @@ static void open_db(ShellState *p, int keepAlive){
/*
** Do C-language style dequoting.
**
** \a -> alarm
** \b -> backspace
** \t -> tab
** \n -> newline
** \v -> vertical tab
** \f -> form feed
** \r -> carriage return
** \s -> space
** \" -> "
** \' -> '
** \\ -> backslash
** \NNN -> ascii character NNN in octal
** \\ -> backslash
*/
static void resolve_backslashes(char *z){
int i, j;
char c;
while( *z && *z!='\\' ) z++;
for(i=j=0; (c = z[i])!=0; i++, j++){
if( c=='\\' && z[i+1]!=0 ){
if( c=='\\' ){
c = z[++i];
if( c=='a' ){
c = '\a';
}else if( c=='b' ){
c = '\b';
if( c=='n' ){
c = '\n';
}else if( c=='t' ){
c = '\t';
}else if( c=='n' ){
c = '\n';
}else if( c=='v' ){
c = '\v';
}else if( c=='f' ){
c = '\f';
}else if( c=='r' ){
c = '\r';
}else if( c=='"' ){
c = '"';
}else if( c=='\'' ){
c = '\'';
}else if( c=='\\' ){
c = '\\';
}else if( c>='0' && c<='7' ){
@@ -2713,19 +2774,6 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}else
if( c=='b' && n>=3 && strncmp(azArg[0], "binary", n)==0 ){
if( nArg==2 ){
if( booleanValue(azArg[1]) ){
setBinaryMode(p->out);
}else{
setTextMode(p->out);
}
}else{
fprintf(stderr, "Usage: .binary on|off\n");
rc = 1;
}
}else
/* The undocumented ".breakpoint" command causes a call to the no-op
** routine named test_breakpoint().
*/
@@ -3294,6 +3342,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}
open_db(p, 1);
if( p->db!=0 ){
session_close_all(p);
sqlite3_close(savedDb);
sqlite3_free(p->zFreeOnClose);
p->zFreeOnClose = zNewFilename;
@@ -3534,7 +3583,6 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}else
#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE)
if( c=='s' && n==11 && strncmp(azArg[0], "selecttrace", n)==0 ){
extern int sqlite3SelectTrace;
@@ -3542,6 +3590,198 @@ static int do_meta_command(char *zLine, ShellState *p){
}else
#endif
#if defined(SQLITE_ENABLE_SESSION)
if( c=='s' && strncmp(azArg[0],"session",n)==0 && n>=3 ){
OpenSession *pSession = &p->aSession[0];
char **azCmd = &azArg[1];
int iSes = 0;
int nCmd = nArg - 1;
int i;
if( nArg<=1 ) goto session_syntax_error;
open_db(p, 0);
if( nArg>=3 ){
for(iSes=0; iSes<p->nSession; iSes++){
if( strcmp(p->aSession[iSes].zName, azArg[1])==0 ) break;
}
if( iSes<p->nSession ){
pSession = &p->aSession[iSes];
azCmd++;
nCmd--;
}else{
pSession = &p->aSession[0];
iSes = 0;
}
}
/* .session attach TABLE
** Invoke the sqlite3session_attach() interface to attach a particular
** table so that it is never filtered.
*/
if( strcmp(azCmd[0],"attach")==0 ){
if( nCmd!=2 ) goto session_syntax_error;
if( pSession->p==0 ){
session_not_open:
fprintf(stderr, "ERROR: No sessions are open\n");
}else{
rc = sqlite3session_attach(pSession->p, azCmd[1]);
if( rc ){
fprintf(stderr, "ERROR: sqlite3session_attach() returns %d\n", rc);
rc = 0;
}
}
}else
/* .session changeset FILE
** .session patchset FILE
** Write a changeset or patchset into a file. The file is overwritten.
*/
if( strcmp(azCmd[0],"changeset")==0 || strcmp(azCmd[0],"patchset")==0 ){
FILE *out = 0;
if( nCmd!=2 ) goto session_syntax_error;
if( pSession->p==0 ) goto session_not_open;
out = fopen(azCmd[1], "wb");
if( out==0 ){
fprintf(stderr, "ERROR: cannot open \"%s\" for writing\n", azCmd[1]);
}else{
int szChng;
void *pChng;
if( azCmd[0][0]=='c' ){
rc = sqlite3session_changeset(pSession->p, &szChng, &pChng);
}else{
rc = sqlite3session_patchset(pSession->p, &szChng, &pChng);
}
if( rc ){
printf("Error: error code %d\n", rc);
rc = 0;
}
if( pChng
&& fwrite(pChng, szChng, 1, out)!=1 ){
fprintf(stderr, "ERROR: Failed to write entire %d-byte output\n",
szChng);
}
sqlite3_free(pChng);
fclose(out);
}
}else
/* .session close
** Close the identified session
*/
if( strcmp(azCmd[0], "close")==0 ){
if( nCmd!=1 ) goto session_syntax_error;
if( p->nSession ){
session_close(pSession);
p->aSession[iSes] = p->aSession[--p->nSession];
}
}else
/* .session enable ?BOOLEAN?
** Query or set the enable flag
*/
if( strcmp(azCmd[0], "enable")==0 ){
int ii;
if( nCmd>2 ) goto session_syntax_error;
ii = nCmd==1 ? -1 : booleanValue(azCmd[1]);
if( p->nSession ){
ii = sqlite3session_enable(pSession->p, ii);
fprintf(p->out, "session %s enable flag = %d\n", pSession->zName, ii);
}
}else
/* .session filter GLOB ....
** Set a list of GLOB patterns of table names to be excluded.
*/
if( strcmp(azCmd[0], "filter")==0 ){
int ii, nByte;
if( nCmd<2 ) goto session_syntax_error;
if( p->nSession ){
for(ii=0; ii<pSession->nFilter; ii++){
sqlite3_free(pSession->azFilter[ii]);
}
sqlite3_free(pSession->azFilter);
nByte = sizeof(pSession->azFilter[0])*(nCmd-1);
pSession->azFilter = sqlite3_malloc( nByte );
if( pSession->azFilter==0 ){
fprintf(stderr, "Error: out or memory\n");
exit(1);
}
for(ii=1; ii<nCmd; ii++){
pSession->azFilter[ii-1] = sqlite3_mprintf("%s", azCmd[ii]);
}
pSession->nFilter = ii-1;
}
}else
/* .session indirect ?BOOLEAN?
** Query or set the indirect flag
*/
if( strcmp(azCmd[0], "indirect")==0 ){
int ii;
if( nCmd>2 ) goto session_syntax_error;
ii = nCmd==1 ? -1 : booleanValue(azCmd[1]);
if( p->nSession ){
ii = sqlite3session_indirect(pSession->p, ii);
fprintf(p->out, "session %s indirect flag = %d\n", pSession->zName,ii);
}
}else
/* .session isempty
** Determine if the session is empty
*/
if( strcmp(azCmd[0], "isempty")==0 ){
int ii;
if( nCmd!=1 ) goto session_syntax_error;
if( p->nSession ){
ii = sqlite3session_isempty(pSession->p);
fprintf(p->out, "session %s isempty flag = %d\n", pSession->zName, ii);
}
}else
/* .session list
** List all currently open sessions
*/
if( strcmp(azCmd[0],"list")==0 ){
for(i=0; i<p->nSession; i++){
fprintf(p->out, "%d %s\n", i, p->aSession[i].zName);
}
}else
/* .session open DB NAME
** Open a new session called NAME on the attached database DB.
** DB is normally "main".
*/
if( strcmp(azCmd[0],"open")==0 ){
char *zName;
if( nCmd!=3 ) goto session_syntax_error;
zName = azCmd[2];
if( zName[0]==0 ) goto session_syntax_error;
for(i=0; i<p->nSession; i++){
if( strcmp(p->aSession[i].zName,zName)==0 ){
fprintf(stderr, "Session \"%s\" already exists\n", zName);
goto meta_command_exit;
}
}
if( p->nSession>=ArraySize(p->aSession) ){
fprintf(stderr, "Maximum of %d sessions\n", ArraySize(p->aSession));
goto meta_command_exit;
}
pSession = &p->aSession[p->nSession];
rc = sqlite3session_create(p->db, azCmd[1], &pSession->p);
if( rc ){
fprintf(stderr, "Cannot open session: error code=%d\n", rc);
rc = 0;
goto meta_command_exit;
}
pSession->nFilter = 0;
sqlite3session_table_filter(pSession->p, session_filter, pSession);
p->nSession++;
pSession->zName = sqlite3_mprintf("%s", zName);
}else
/* If no command name matches, show a syntax error */
session_syntax_error:
session_help(p);
}else
#endif
#ifdef SQLITE_DEBUG
/* Undocumented commands for internal testing. Subject to change
@@ -4737,6 +4977,7 @@ int SQLITE_CDECL main(int argc, char **argv){
}
set_table_name(&data, 0);
if( data.db ){
session_close_all(&data);
sqlite3_close(data.db);
}
sqlite3_free(data.zFreeOnClose);
+110 -91
View File
@@ -270,7 +270,6 @@ typedef sqlite_uint64 sqlite3_uint64;
/*
** CAPI3REF: Closing A Database Connection
** DESTRUCTOR: sqlite3
**
** ^The sqlite3_close() and sqlite3_close_v2() routines are destructors
** for the [sqlite3] object.
@@ -322,7 +321,6 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
/*
** CAPI3REF: One-Step Query Execution Interface
** METHOD: sqlite3
**
** The sqlite3_exec() interface is a convenience wrapper around
** [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()],
@@ -1380,7 +1378,6 @@ int sqlite3_config(int, ...);
/*
** CAPI3REF: Configure database connections
** METHOD: sqlite3
**
** The sqlite3_db_config() interface is used to make configuration
** changes to a [database connection]. The interface is similar to
@@ -1878,7 +1875,6 @@ struct sqlite3_mem_methods {
/*
** CAPI3REF: Enable Or Disable Extended Result Codes
** METHOD: sqlite3
**
** ^The sqlite3_extended_result_codes() routine enables or disables the
** [extended result codes] feature of SQLite. ^The extended result
@@ -1888,7 +1884,6 @@ int sqlite3_extended_result_codes(sqlite3*, int onoff);
/*
** CAPI3REF: Last Insert Rowid
** METHOD: sqlite3
**
** ^Each entry in most SQLite tables (except for [WITHOUT ROWID] tables)
** has a unique 64-bit signed
@@ -1940,7 +1935,6 @@ sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*);
/*
** CAPI3REF: Count The Number Of Rows Modified
** METHOD: sqlite3
**
** ^This function returns the number of rows modified, inserted or
** deleted by the most recently completed INSERT, UPDATE or DELETE
@@ -1993,7 +1987,6 @@ int sqlite3_changes(sqlite3*);
/*
** CAPI3REF: Total Number Of Rows Modified
** METHOD: sqlite3
**
** ^This function returns the total number of rows inserted, modified or
** deleted by all [INSERT], [UPDATE] or [DELETE] statements completed
@@ -2017,7 +2010,6 @@ int sqlite3_total_changes(sqlite3*);
/*
** CAPI3REF: Interrupt A Long-Running Query
** METHOD: sqlite3
**
** ^This function causes any pending database operation to abort and
** return at its earliest opportunity. This routine is typically
@@ -2094,7 +2086,6 @@ int sqlite3_complete16(const void *sql);
/*
** CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors
** KEYWORDS: {busy-handler callback} {busy handler}
** METHOD: sqlite3
**
** ^The sqlite3_busy_handler(D,X,P) routine sets a callback function X
** that might be invoked with argument P whenever
@@ -2154,7 +2145,6 @@ int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*);
/*
** CAPI3REF: Set A Busy Timeout
** METHOD: sqlite3
**
** ^This routine sets a [sqlite3_busy_handler | busy handler] that sleeps
** for a specified amount of time when a table is locked. ^The handler
@@ -2177,7 +2167,6 @@ int sqlite3_busy_timeout(sqlite3*, int ms);
/*
** CAPI3REF: Convenience Routines For Running Queries
** METHOD: sqlite3
**
** This is a legacy interface that is preserved for backwards compatibility.
** Use of this interface is not recommended.
@@ -2513,7 +2502,6 @@ void sqlite3_randomness(int N, void *P);
/*
** CAPI3REF: Compile-Time Authorization Callbacks
** METHOD: sqlite3
**
** ^This routine registers an authorizer callback with a particular
** [database connection], supplied in the first argument.
@@ -2670,7 +2658,6 @@ int sqlite3_set_authorizer(
/*
** CAPI3REF: Tracing And Profiling Functions
** METHOD: sqlite3
**
** These routines register callback functions that can be used for
** tracing and profiling the execution of SQL statements.
@@ -2703,7 +2690,6 @@ SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*,
/*
** CAPI3REF: Query Progress Callbacks
** METHOD: sqlite3
**
** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback
** function X to be invoked periodically during long running calls to
@@ -2737,7 +2723,6 @@ void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
/*
** CAPI3REF: Opening A New Database Connection
** CONSTRUCTOR: sqlite3
**
** ^These routines open an SQLite database file as specified by the
** filename argument. ^The filename argument is interpreted as UTF-8 for
@@ -3023,7 +3008,6 @@ sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64);
/*
** CAPI3REF: Error Codes And Messages
** METHOD: sqlite3
**
** ^If the most recent sqlite3_* API call associated with
** [database connection] D failed, then the sqlite3_errcode(D) interface
@@ -3069,34 +3053,33 @@ const void *sqlite3_errmsg16(sqlite3*);
const char *sqlite3_errstr(int);
/*
** CAPI3REF: Prepared Statement Object
** CAPI3REF: SQL Statement Object
** KEYWORDS: {prepared statement} {prepared statements}
**
** An instance of this object represents a single SQL statement that
** has been compiled into binary form and is ready to be evaluated.
** An instance of this object represents a single SQL statement.
** This object is variously known as a "prepared statement" or a
** "compiled SQL statement" or simply as a "statement".
**
** Think of each SQL statement as a separate computer program. The
** original SQL text is source code. A prepared statement object
** is the compiled object code. All SQL must be converted into a
** prepared statement before it can be run.
**
** The life-cycle of a prepared statement object usually goes like this:
** The life of a statement object goes something like this:
**
** <ol>
** <li> Create the prepared statement object using [sqlite3_prepare_v2()].
** <li> Bind values to [parameters] using the sqlite3_bind_*()
** <li> Create the object using [sqlite3_prepare_v2()] or a related
** function.
** <li> Bind values to [host parameters] using the sqlite3_bind_*()
** interfaces.
** <li> Run the SQL by calling [sqlite3_step()] one or more times.
** <li> Reset the prepared statement using [sqlite3_reset()] then go back
** <li> Reset the statement using [sqlite3_reset()] then go back
** to step 2. Do this zero or more times.
** <li> Destroy the object using [sqlite3_finalize()].
** </ol>
**
** Refer to documentation on individual methods above for additional
** information.
*/
typedef struct sqlite3_stmt sqlite3_stmt;
/*
** CAPI3REF: Run-time Limits
** METHOD: sqlite3
**
** ^(This interface allows the size of various constructs to be limited
** on a connection by connection basis. The first parameter is the
@@ -3208,8 +3191,6 @@ int sqlite3_limit(sqlite3*, int id, int newVal);
/*
** CAPI3REF: Compiling An SQL Statement
** KEYWORDS: {SQL statement compiler}
** METHOD: sqlite3
** CONSTRUCTOR: sqlite3_stmt
**
** To execute an SQL query, it must first be compiled into a byte-code
** program using one of these routines.
@@ -3317,7 +3298,6 @@ int sqlite3_prepare16_v2(
/*
** CAPI3REF: Retrieving Statement SQL
** METHOD: sqlite3_stmt
**
** ^This interface can be used to retrieve a saved copy of the original
** SQL text used to create a [prepared statement] if that statement was
@@ -3327,7 +3307,6 @@ const char *sqlite3_sql(sqlite3_stmt *pStmt);
/*
** CAPI3REF: Determine If An SQL Statement Writes The Database
** METHOD: sqlite3_stmt
**
** ^The sqlite3_stmt_readonly(X) interface returns true (non-zero) if
** and only if the [prepared statement] X makes no direct changes to
@@ -3359,7 +3338,6 @@ int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);
/*
** CAPI3REF: Determine If A Prepared Statement Has Been Reset
** METHOD: sqlite3_stmt
**
** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the
** [prepared statement] S has been stepped at least once using
@@ -3434,7 +3412,6 @@ typedef struct sqlite3_context sqlite3_context;
** CAPI3REF: Binding Values To Prepared Statements
** KEYWORDS: {host parameter} {host parameters} {host parameter name}
** KEYWORDS: {SQL parameter} {SQL parameters} {parameter binding}
** METHOD: sqlite3_stmt
**
** ^(In the SQL statement text input to [sqlite3_prepare_v2()] and its variants,
** literals may be replaced by a [parameter] that matches one of following
@@ -3553,7 +3530,6 @@ int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);
/*
** CAPI3REF: Number Of SQL Parameters
** METHOD: sqlite3_stmt
**
** ^This routine can be used to find the number of [SQL parameters]
** in a [prepared statement]. SQL parameters are tokens of the
@@ -3574,7 +3550,6 @@ int sqlite3_bind_parameter_count(sqlite3_stmt*);
/*
** CAPI3REF: Name Of A Host Parameter
** METHOD: sqlite3_stmt
**
** ^The sqlite3_bind_parameter_name(P,N) interface returns
** the name of the N-th [SQL parameter] in the [prepared statement] P.
@@ -3602,7 +3577,6 @@ const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int);
/*
** CAPI3REF: Index Of A Parameter With A Given Name
** METHOD: sqlite3_stmt
**
** ^Return the index of an SQL parameter given its name. ^The
** index value returned is suitable for use as the second
@@ -3619,7 +3593,6 @@ int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName);
/*
** CAPI3REF: Reset All Bindings On A Prepared Statement
** METHOD: sqlite3_stmt
**
** ^Contrary to the intuition of many, [sqlite3_reset()] does not reset
** the [sqlite3_bind_blob | bindings] on a [prepared statement].
@@ -3629,7 +3602,6 @@ int sqlite3_clear_bindings(sqlite3_stmt*);
/*
** CAPI3REF: Number Of Columns In A Result Set
** METHOD: sqlite3_stmt
**
** ^Return the number of columns in the result set returned by the
** [prepared statement]. ^This routine returns 0 if pStmt is an SQL
@@ -3641,7 +3613,6 @@ int sqlite3_column_count(sqlite3_stmt *pStmt);
/*
** CAPI3REF: Column Names In A Result Set
** METHOD: sqlite3_stmt
**
** ^These routines return the name assigned to a particular column
** in the result set of a [SELECT] statement. ^The sqlite3_column_name()
@@ -3671,7 +3642,6 @@ const void *sqlite3_column_name16(sqlite3_stmt*, int N);
/*
** CAPI3REF: Source Of Data In A Query Result
** METHOD: sqlite3_stmt
**
** ^These routines provide a means to determine the database, table, and
** table column that is the origin of a particular result column in
@@ -3724,7 +3694,6 @@ const void *sqlite3_column_origin_name16(sqlite3_stmt*,int);
/*
** CAPI3REF: Declared Datatype Of A Query Result
** METHOD: sqlite3_stmt
**
** ^(The first parameter is a [prepared statement].
** If this statement is a [SELECT] statement and the Nth column of the
@@ -3757,7 +3726,6 @@ const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
/*
** CAPI3REF: Evaluate An SQL Statement
** METHOD: sqlite3_stmt
**
** After a [prepared statement] has been prepared using either
** [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] or one of the legacy
@@ -3837,7 +3805,6 @@ int sqlite3_step(sqlite3_stmt*);
/*
** CAPI3REF: Number of columns in a result set
** METHOD: sqlite3_stmt
**
** ^The sqlite3_data_count(P) interface returns the number of columns in the
** current row of the result set of [prepared statement] P.
@@ -3891,7 +3858,6 @@ int sqlite3_data_count(sqlite3_stmt *pStmt);
/*
** CAPI3REF: Result Values From A Query
** KEYWORDS: {column access functions}
** METHOD: sqlite3_stmt
**
** These routines form the "result set" interface.
**
@@ -4064,7 +4030,6 @@ sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);
/*
** CAPI3REF: Destroy A Prepared Statement Object
** DESTRUCTOR: sqlite3_stmt
**
** ^The sqlite3_finalize() function is called to delete a [prepared statement].
** ^If the most recent evaluation of the statement encountered no errors
@@ -4092,7 +4057,6 @@ int sqlite3_finalize(sqlite3_stmt *pStmt);
/*
** CAPI3REF: Reset A Prepared Statement Object
** METHOD: sqlite3_stmt
**
** The sqlite3_reset() function is called to reset a [prepared statement]
** object back to its initial state, ready to be re-executed.
@@ -4122,7 +4086,6 @@ int sqlite3_reset(sqlite3_stmt *pStmt);
** KEYWORDS: {function creation routines}
** KEYWORDS: {application-defined SQL function}
** KEYWORDS: {application-defined SQL functions}
** METHOD: sqlite3
**
** ^These functions (collectively known as "function creation routines")
** are used to add SQL functions or aggregates or to redefine the behavior
@@ -4292,7 +4255,6 @@ SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),
/*
** CAPI3REF: Obtaining SQL Function Parameter Values
** METHOD: sqlite3_value
**
** The C-language implementation of SQL functions and aggregates uses
** this set of interface routines to access the parameter values on
@@ -4351,7 +4313,6 @@ int sqlite3_value_numeric_type(sqlite3_value*);
/*
** CAPI3REF: Obtain Aggregate Function Context
** METHOD: sqlite3_context
**
** Implementations of aggregate SQL functions use this
** routine to allocate memory for storing their state.
@@ -4396,7 +4357,6 @@ void *sqlite3_aggregate_context(sqlite3_context*, int nBytes);
/*
** CAPI3REF: User Data For Functions
** METHOD: sqlite3_context
**
** ^The sqlite3_user_data() interface returns a copy of
** the pointer that was the pUserData parameter (the 5th parameter)
@@ -4411,7 +4371,6 @@ void *sqlite3_user_data(sqlite3_context*);
/*
** CAPI3REF: Database Connection For Functions
** METHOD: sqlite3_context
**
** ^The sqlite3_context_db_handle() interface returns a copy of
** the pointer to the [database connection] (the 1st parameter)
@@ -4423,7 +4382,6 @@ sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
/*
** CAPI3REF: Function Auxiliary Data
** METHOD: sqlite3_context
**
** These functions may be used by (non-aggregate) SQL functions to
** associate metadata with argument values. If the same value is passed to
@@ -4496,7 +4454,6 @@ typedef void (*sqlite3_destructor_type)(void*);
/*
** CAPI3REF: Setting The Result Of An SQL Function
** METHOD: sqlite3_context
**
** These routines are used by the xFunc or xFinal callbacks that
** implement SQL functions and aggregates. See
@@ -4632,7 +4589,6 @@ void sqlite3_result_zeroblob(sqlite3_context*, int n);
/*
** CAPI3REF: Define New Collating Sequences
** METHOD: sqlite3
**
** ^These functions add, remove, or modify a [collation] associated
** with the [database connection] specified as the first argument.
@@ -4735,7 +4691,6 @@ int sqlite3_create_collation16(
/*
** CAPI3REF: Collation Needed Callbacks
** METHOD: sqlite3
**
** ^To avoid having to register all collation sequences before a database
** can be used, a single callback function may be registered with the
@@ -4943,7 +4898,6 @@ SQLITE_EXTERN char *sqlite3_data_directory;
/*
** CAPI3REF: Test For Auto-Commit Mode
** KEYWORDS: {autocommit mode}
** METHOD: sqlite3
**
** ^The sqlite3_get_autocommit() interface returns non-zero or
** zero if the given database connection is or is not in autocommit mode,
@@ -4966,7 +4920,6 @@ int sqlite3_get_autocommit(sqlite3*);
/*
** CAPI3REF: Find The Database Handle Of A Prepared Statement
** METHOD: sqlite3_stmt
**
** ^The sqlite3_db_handle interface returns the [database connection] handle
** to which a [prepared statement] belongs. ^The [database connection]
@@ -4979,7 +4932,6 @@ sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
/*
** CAPI3REF: Return The Filename For A Database Connection
** METHOD: sqlite3
**
** ^The sqlite3_db_filename(D,N) interface returns a pointer to a filename
** associated with database N of connection D. ^The main database file
@@ -4996,7 +4948,6 @@ const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName);
/*
** CAPI3REF: Determine if a database is read-only
** METHOD: sqlite3
**
** ^The sqlite3_db_readonly(D,N) interface returns 1 if the database N
** of connection D is read-only, 0 if it is read/write, or -1 if N is not
@@ -5006,7 +4957,6 @@ int sqlite3_db_readonly(sqlite3 *db, const char *zDbName);
/*
** CAPI3REF: Find the next prepared statement
** METHOD: sqlite3
**
** ^This interface returns a pointer to the next [prepared statement] after
** pStmt associated with the [database connection] pDb. ^If pStmt is NULL
@@ -5022,7 +4972,6 @@ sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt);
/*
** CAPI3REF: Commit And Rollback Notification Callbacks
** METHOD: sqlite3
**
** ^The sqlite3_commit_hook() interface registers a callback
** function to be invoked whenever a transaction is [COMMIT | committed].
@@ -5072,7 +5021,6 @@ void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
/*
** CAPI3REF: Data Change Notification Callbacks
** METHOD: sqlite3
**
** ^The sqlite3_update_hook() interface registers a callback function
** with the [database connection] identified by the first argument
@@ -5116,8 +5064,8 @@ void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
** on the same [database connection] D, or NULL for
** the first call on D.
**
** See also the [sqlite3_commit_hook()] and [sqlite3_rollback_hook()]
** interfaces.
** See also the [sqlite3_commit_hook()], [sqlite3_rollback_hook()],
** and [sqlite3_preupdate_hook()] interfaces.
*/
void *sqlite3_update_hook(
sqlite3*,
@@ -5179,7 +5127,6 @@ int sqlite3_release_memory(int);
/*
** CAPI3REF: Free Memory Used By A Database Connection
** METHOD: sqlite3
**
** ^The sqlite3_db_release_memory(D) interface attempts to free as much heap
** memory as possible from database connection D. Unlike the
@@ -5257,7 +5204,6 @@ SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N);
/*
** CAPI3REF: Extract Metadata About A Column Of A Table
** METHOD: sqlite3
**
** ^(The sqlite3_table_column_metadata(X,D,T,C,....) routine returns
** information about column C of table T in database D
@@ -5336,7 +5282,6 @@ int sqlite3_table_column_metadata(
/*
** CAPI3REF: Load An Extension
** METHOD: sqlite3
**
** ^This interface loads an SQLite extension library from the named file.
**
@@ -5378,7 +5323,6 @@ int sqlite3_load_extension(
/*
** CAPI3REF: Enable Or Disable Extension Loading
** METHOD: sqlite3
**
** ^So as not to open security holes in older applications that are
** unprepared to deal with [extension loading], and as a means of disabling
@@ -5628,7 +5572,6 @@ struct sqlite3_index_info {
/*
** CAPI3REF: Register A Virtual Table Implementation
** METHOD: sqlite3
**
** ^These routines are used to register a new [virtual table module] name.
** ^Module names must be registered before
@@ -5725,7 +5668,6 @@ int sqlite3_declare_vtab(sqlite3*, const char *zSQL);
/*
** CAPI3REF: Overload A Function For A Virtual Table
** METHOD: sqlite3
**
** ^(Virtual tables can provide alternative implementations of functions
** using the [xFindFunction] method of the [virtual table module].
@@ -5768,8 +5710,6 @@ typedef struct sqlite3_blob sqlite3_blob;
/*
** CAPI3REF: Open A BLOB For Incremental I/O
** METHOD: sqlite3
** CONSTRUCTOR: sqlite3_blob
**
** ^(This interfaces opens a [BLOB handle | handle] to the BLOB located
** in row iRow, column zColumn, table zTable in database zDb;
@@ -5851,7 +5791,6 @@ int sqlite3_blob_open(
/*
** CAPI3REF: Move a BLOB Handle to a New Row
** METHOD: sqlite3_blob
**
** ^This function is used to move an existing blob handle so that it points
** to a different row of the same database table. ^The new row is identified
@@ -5876,7 +5815,6 @@ SQLITE_EXPERIMENTAL int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64);
/*
** CAPI3REF: Close A BLOB Handle
** DESTRUCTOR: sqlite3_blob
**
** ^This function closes an open [BLOB handle]. ^(The BLOB handle is closed
** unconditionally. Even if this routine returns an error code, the
@@ -5899,7 +5837,6 @@ int sqlite3_blob_close(sqlite3_blob *);
/*
** CAPI3REF: Return The Size Of An Open BLOB
** METHOD: sqlite3_blob
**
** ^Returns the size in bytes of the BLOB accessible via the
** successfully opened [BLOB handle] in its only argument. ^The
@@ -5915,7 +5852,6 @@ int sqlite3_blob_bytes(sqlite3_blob *);
/*
** CAPI3REF: Read Data From A BLOB Incrementally
** METHOD: sqlite3_blob
**
** ^(This function is used to read data from an open [BLOB handle] into a
** caller-supplied buffer. N bytes of data are copied into buffer Z
@@ -5944,7 +5880,6 @@ int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset);
/*
** CAPI3REF: Write Data Into A BLOB Incrementally
** METHOD: sqlite3_blob
**
** ^(This function is used to write data into an open [BLOB handle] from a
** caller-supplied buffer. N bytes of data are copied from the buffer Z
@@ -6272,7 +6207,6 @@ int sqlite3_mutex_notheld(sqlite3_mutex*);
/*
** CAPI3REF: Retrieve the mutex for a database connection
** METHOD: sqlite3
**
** ^This interface returns a pointer the [sqlite3_mutex] object that
** serializes access to the [database connection] given in the argument
@@ -6284,7 +6218,6 @@ sqlite3_mutex *sqlite3_db_mutex(sqlite3*);
/*
** CAPI3REF: Low-Level Control Of Database Files
** METHOD: sqlite3
**
** ^The [sqlite3_file_control()] interface makes a direct call to the
** xFileControl method for the [sqlite3_io_methods] object associated
@@ -6501,7 +6434,6 @@ int sqlite3_status64(
/*
** CAPI3REF: Database Connection Status
** METHOD: sqlite3
**
** ^This interface is used to retrieve runtime status information
** about a single [database connection]. ^The first argument is the
@@ -6603,7 +6535,7 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
** database file in rollback mode databases. Any pages written as part of
** transaction rollback or database recovery operations are not included.
** If an IO or other error occurs while writing a page to disk, the effect
** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined.)^ ^The
** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined). ^The
** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0.
** </dd>
**
@@ -6630,7 +6562,6 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
/*
** CAPI3REF: Prepared Statement Status
** METHOD: sqlite3_stmt
**
** ^(Each prepared statement maintains various
** [SQLITE_STMTSTATUS counters] that measure the number
@@ -7134,7 +7065,6 @@ int sqlite3_backup_pagecount(sqlite3_backup *p);
/*
** CAPI3REF: Unlock Notification
** METHOD: sqlite3
**
** ^When running in shared-cache mode, a database operation may fail with
** an [SQLITE_LOCKED] error if the required locks on the shared-cache or
@@ -7305,7 +7235,6 @@ void sqlite3_log(int iErrCode, const char *zFormat, ...);
/*
** CAPI3REF: Write-Ahead Log Commit Hook
** METHOD: sqlite3
**
** ^The [sqlite3_wal_hook()] function is used to register a callback that
** is invoked each time data is committed to a database in wal mode.
@@ -7345,7 +7274,6 @@ void *sqlite3_wal_hook(
/*
** CAPI3REF: Configure an auto-checkpoint
** METHOD: sqlite3
**
** ^The [sqlite3_wal_autocheckpoint(D,N)] is a wrapper around
** [sqlite3_wal_hook()] that causes any database on [database connection] D
@@ -7376,7 +7304,6 @@ int sqlite3_wal_autocheckpoint(sqlite3 *db, int N);
/*
** CAPI3REF: Checkpoint a database
** METHOD: sqlite3
**
** ^(The sqlite3_wal_checkpoint(D,X) is equivalent to
** [sqlite3_wal_checkpoint_v2](D,X,[SQLITE_CHECKPOINT_PASSIVE],0,0).)^
@@ -7398,7 +7325,6 @@ int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb);
/*
** CAPI3REF: Checkpoint a database
** METHOD: sqlite3
**
** ^(The sqlite3_wal_checkpoint_v2(D,X,M,L,C) interface runs a checkpoint
** operation on database X of [database connection] D in mode M. Status
@@ -7653,7 +7579,6 @@ int sqlite3_vtab_on_conflict(sqlite3 *);
/*
** CAPI3REF: Prepared Statement Scan Status
** METHOD: sqlite3_stmt
**
** This interface returns information about the predicted and measured
** performance for pStmt. Advanced applications can use this
@@ -7691,7 +7616,6 @@ SQLITE_EXPERIMENTAL int sqlite3_stmt_scanstatus(
/*
** CAPI3REF: Zero Scan-Status Counters
** METHOD: sqlite3_stmt
**
** ^Zero all [sqlite3_stmt_scanstatus()] related event counters.
**
@@ -7701,6 +7625,101 @@ SQLITE_EXPERIMENTAL int sqlite3_stmt_scanstatus(
SQLITE_EXPERIMENTAL void sqlite3_stmt_scanstatus_reset(sqlite3_stmt*);
/*
** CAPI3REF: The pre-update hook.
** EXPERIMENTAL
**
** ^These interfaces are only available if SQLite is compiled using the
** [SQLITE_ENABLE_UPDATE_HOOK] compile-time option.
**
** ^The [sqlite3_preupdate_hook()] interface registers a callback function
** that is invoked prior to each [INSERT], [UPDATE], and [DELETE] operation.
** ^At most one preupdate hook may be registered at a time on a single
** [database connection]; each call to [sqlite3_preupdate_hook()] overrides
** the previous setting.
** ^The preupdate hook is disabled by invoking [sqlite3_preupdate_hook()]
** with a NULL pointer as the second parameter.
** ^The third parameter to [sqlite3_preupdate_hook()] is passed through as
** the first parameter to callbacks.
**
** ^The preupdate hook only fires for changes to real tables; the preupdate
** hook is not invoked for changes to virtual tables.
**
** ^The second parameter to the preupdate callback is a pointer to
** the [database connection] that registered the preupdate hook.
** ^The third parameter to the preupdate callback is one of the constants
** [SQLITE_INSERT], [SQLITE_DELETE], or [SQLITE_UPDATE] to indentify the
** kind of update operation that is about to occur.
** ^(The fourth parameter to the preupdate callback is the name of the
** database within the database connection that is being modified. This
** will be "main" for the main database or "temp" for TEMP tables or
** the name given after the AS keyword in the [ATTACH] statement for attached
** databases.)^
** ^The fifth parameter to the preupdate callback is the name of the
** table that is being modified.
** ^The sixth parameter to the preupdate callback is the initial [rowid] of the
** row being changes for SQLITE_UPDATE and SQLITE_DELETE changes and is
** undefined for SQLITE_INSERT changes.
** ^The seventh parameter to the preupdate callback is the final [rowid] of
** the row being changed for SQLITE_UPDATE and SQLITE_INSERT changes and is
** undefined for SQLITE_DELETE changes.
**
** The [sqlite3_preupdate_old()], [sqlite3_preupdate_new()],
** [sqlite3_preupdate_count()], and [sqlite3_preupdate_depth()] interfaces
** provide additional information about a preupdate event. These routines
** may only be called from within a preupdate callback. Invoking any of
** these routines from outside of a preupdate callback or with a
** [database connection] pointer that is different from the one supplied
** to the preupdate callback results in undefined and probably undesirable
** behavior.
**
** ^The [sqlite3_preupdate_count(D)] interface returns the number of columns
** in the row that is being inserted, updated, or deleted.
**
** ^The [sqlite3_preupdate_old(D,N,P)] interface writes into P a pointer to
** a [protected sqlite3_value] that contains the value of the Nth column of
** the table row before it is updated. The N parameter must be between 0
** and one less than the number of columns or the behavior will be
** undefined. This must only be used within SQLITE_UPDATE and SQLITE_DELETE
** preupdate callbacks; if it is used by an SQLITE_INSERT callback then the
** behavior is undefined. The [sqlite3_value] that P points to
** will be destroyed when the preupdate callback returns.
**
** ^The [sqlite3_preupdate_new(D,N,P)] interface writes into P a pointer to
** a [protected sqlite3_value] that contains the value of the Nth column of
** the table row after it is updated. The N parameter must be between 0
** and one less than the number of columns or the behavior will be
** undefined. This must only be used within SQLITE_INSERT and SQLITE_UPDATE
** preupdate callbacks; if it is used by an SQLITE_DELETE callback then the
** behavior is undefined. The [sqlite3_value] that P points to
** will be destroyed when the preupdate callback returns.
**
** ^The [sqlite3_preupdate_depth(D)] interface returns 0 if the preupdate
** callback was invoked as a result of a direct insert, update, or delete
** operation; or 1 for inserts, updates, or deletes invoked by top-level
** triggers; or 2 for changes resulting from triggers called by top-level
** triggers; and so forth.
**
** See also: [sqlite3_update_hook()]
*/
SQLITE_EXPERIMENTAL void *sqlite3_preupdate_hook(
sqlite3 *db,
void(*xPreUpdate)(
void *pCtx, /* Copy of third arg to preupdate_hook() */
sqlite3 *db, /* Database handle */
int op, /* SQLITE_UPDATE, DELETE or INSERT */
char const *zDb, /* Database name */
char const *zName, /* Table name */
sqlite3_int64 iKey1, /* Rowid of row about to be deleted/updated */
sqlite3_int64 iKey2 /* New rowid value (for a rowid UPDATE) */
),
void*
);
SQLITE_EXPERIMENTAL int sqlite3_preupdate_old(sqlite3 *, int, sqlite3_value **);
SQLITE_EXPERIMENTAL int sqlite3_preupdate_count(sqlite3 *);
SQLITE_EXPERIMENTAL int sqlite3_preupdate_depth(sqlite3 *);
SQLITE_EXPERIMENTAL int sqlite3_preupdate_new(sqlite3 *, int, sqlite3_value **);
/*
** Undo the hack that converts floating point types to integer for
** builds on processors without floating point support.
+12 -1
View File
@@ -875,6 +875,7 @@ typedef struct LookasideSlot LookasideSlot;
typedef struct Module Module;
typedef struct NameContext NameContext;
typedef struct Parse Parse;
typedef struct PreUpdate PreUpdate;
typedef struct PrintfArguments PrintfArguments;
typedef struct RowSet RowSet;
typedef struct Savepoint Savepoint;
@@ -1125,6 +1126,13 @@ struct sqlite3 {
void (*xRollbackCallback)(void*); /* Invoked at every commit. */
void *pUpdateArg;
void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64);
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
void *pPreUpdateArg; /* First argument to xPreUpdateCallback */
void (*xPreUpdateCallback)( /* Registered using sqlite3_preupdate_hook() */
void*,sqlite3*,int,char const*,char const*,sqlite3_int64,sqlite3_int64
);
PreUpdate *pPreUpdate; /* Context for active pre-update callback */
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
#ifndef SQLITE_OMIT_WAL
int (*xWalCallback)(void *, sqlite3 *, const char *, int);
void *pWalArg;
@@ -1226,7 +1234,6 @@ struct sqlite3 {
#define SQLITE_DeferFKs 0x01000000 /* Defer all FK constraints */
#define SQLITE_QueryOnly 0x02000000 /* Disable database changes */
#define SQLITE_VdbeEQP 0x04000000 /* Debug EXPLAIN QUERY PLAN */
#define SQLITE_Vacuum 0x08000000 /* Currently in a VACUUM */
/*
@@ -2699,6 +2706,9 @@ struct AuthContext {
/*
** Bitfield flags for P5 value in various opcodes.
**
** Note that the values for ISNOOP and LENGTHARG are the same. But as
** those bits are never used on the same opcode, the overlap is harmless.
*/
#define OPFLAG_NCHANGE 0x01 /* Set to update db->nChange */
#define OPFLAG_EPHEM 0x01 /* OP_Column: Ephemeral output is ok */
@@ -2706,6 +2716,7 @@ struct AuthContext {
#define OPFLAG_ISUPDATE 0x04 /* This OP_Insert is an sql UPDATE */
#define OPFLAG_APPEND 0x08 /* This is likely to be an append */
#define OPFLAG_USESEEKRESULT 0x10 /* Try to avoid a seek in BtreeInsert() */
#define OPFLAG_ISNOOP 0x40 /* OP_Delete does pre-update-hook only */
#define OPFLAG_LENGTHARG 0x40 /* OP_Column only used for length() */
#define OPFLAG_TYPEOFARG 0x80 /* OP_Column only used for typeof() */
#define OPFLAG_BULKCSR 0x01 /* OP_Open** used to open bulk cursor */
+192 -40
View File
@@ -140,6 +140,7 @@ struct SqliteDb {
char *zNull; /* Text to substitute for an SQL NULL value */
SqlFunc *pFunc; /* List of SQL functions */
Tcl_Obj *pUpdateHook; /* Update hook script (if any) */
Tcl_Obj *pPreUpdateHook; /* Pre-update hook script (if any) */
Tcl_Obj *pRollbackHook; /* Rollback hook script (if any) */
Tcl_Obj *pWalHook; /* WAL hook script (if any) */
Tcl_Obj *pUnlockNotify; /* Unlock notify script (if any) */
@@ -518,6 +519,9 @@ static void DbDeleteCmd(void *db){
if( pDb->pUpdateHook ){
Tcl_DecrRefCount(pDb->pUpdateHook);
}
if( pDb->pPreUpdateHook ){
Tcl_DecrRefCount(pDb->pPreUpdateHook);
}
if( pDb->pRollbackHook ){
Tcl_DecrRefCount(pDb->pRollbackHook);
}
@@ -685,6 +689,42 @@ static void DbUnlockNotify(void **apArg, int nArg){
}
#endif
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
/*
** Pre-update hook callback.
*/
static void DbPreUpdateHandler(
void *p,
sqlite3 *db,
int op,
const char *zDb,
const char *zTbl,
sqlite_int64 iKey1,
sqlite_int64 iKey2
){
SqliteDb *pDb = (SqliteDb *)p;
Tcl_Obj *pCmd;
static const char *azStr[] = {"DELETE", "INSERT", "UPDATE"};
assert( (SQLITE_DELETE-1)/9 == 0 );
assert( (SQLITE_INSERT-1)/9 == 1 );
assert( (SQLITE_UPDATE-1)/9 == 2 );
assert( pDb->pPreUpdateHook );
assert( db==pDb->db );
assert( op==SQLITE_INSERT || op==SQLITE_UPDATE || op==SQLITE_DELETE );
pCmd = Tcl_DuplicateObj(pDb->pPreUpdateHook);
Tcl_IncrRefCount(pCmd);
Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(azStr[(op-1)/9], -1));
Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(zDb, -1));
Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(zTbl, -1));
Tcl_ListObjAppendElement(0, pCmd, Tcl_NewWideIntObj(iKey1));
Tcl_ListObjAppendElement(0, pCmd, Tcl_NewWideIntObj(iKey2));
Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT);
Tcl_DecrRefCount(pCmd);
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
static void DbUpdateHandler(
void *p,
int op,
@@ -694,14 +734,18 @@ static void DbUpdateHandler(
){
SqliteDb *pDb = (SqliteDb *)p;
Tcl_Obj *pCmd;
static const char *azStr[] = {"DELETE", "INSERT", "UPDATE"};
assert( (SQLITE_DELETE-1)/9 == 0 );
assert( (SQLITE_INSERT-1)/9 == 1 );
assert( (SQLITE_UPDATE-1)/9 == 2 );
assert( pDb->pUpdateHook );
assert( op==SQLITE_INSERT || op==SQLITE_UPDATE || op==SQLITE_DELETE );
pCmd = Tcl_DuplicateObj(pDb->pUpdateHook);
Tcl_IncrRefCount(pCmd);
Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(
( (op==SQLITE_INSERT)?"INSERT":(op==SQLITE_UPDATE)?"UPDATE":"DELETE"), -1));
Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(azStr[(op-1)/9], -1));
Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(zDb, -1));
Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(zTbl, -1));
Tcl_ListObjAppendElement(0, pCmd, Tcl_NewWideIntObj(rowid));
@@ -1613,6 +1657,46 @@ static int DbEvalNextCmd(
return rc;
}
/*
** This function is used by the implementations of the following database
** handle sub-commands:
**
** $db update_hook ?SCRIPT?
** $db wal_hook ?SCRIPT?
** $db commit_hook ?SCRIPT?
** $db preupdate hook ?SCRIPT?
*/
static void DbHookCmd(
Tcl_Interp *interp, /* Tcl interpreter */
SqliteDb *pDb, /* Database handle */
Tcl_Obj *pArg, /* SCRIPT argument (or NULL) */
Tcl_Obj **ppHook /* Pointer to member of SqliteDb */
){
sqlite3 *db = pDb->db;
if( *ppHook ){
Tcl_SetObjResult(interp, *ppHook);
if( pArg ){
Tcl_DecrRefCount(*ppHook);
*ppHook = 0;
}
}
if( pArg ){
assert( !(*ppHook) );
if( Tcl_GetCharLength(pArg)>0 ){
*ppHook = pArg;
Tcl_IncrRefCount(*ppHook);
}
}
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
sqlite3_preupdate_hook(db, (pDb->pPreUpdateHook?DbPreUpdateHandler:0), pDb);
#endif
sqlite3_update_hook(db, (pDb->pUpdateHook?DbUpdateHandler:0), pDb);
sqlite3_rollback_hook(db, (pDb->pRollbackHook?DbRollbackHandler:0), pDb);
sqlite3_wal_hook(db, (pDb->pWalHook?DbWalHandler:0), pDb);
}
/*
** The "sqlite" command below creates a new Tcl command for each
** connection it opens to an SQLite database. This routine is invoked
@@ -1638,11 +1722,12 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
"errorcode", "eval", "exists",
"function", "incrblob", "interrupt",
"last_insert_rowid", "nullvalue", "onecolumn",
"profile", "progress", "rekey",
"restore", "rollback_hook", "status",
"timeout", "total_changes", "trace",
"transaction", "unlock_notify", "update_hook",
"version", "wal_hook", 0
"preupdate", "profile", "progress",
"rekey", "restore", "rollback_hook",
"status", "timeout", "total_changes",
"trace", "transaction", "unlock_notify",
"update_hook", "version", "wal_hook",
0
};
enum DB_enum {
DB_AUTHORIZER, DB_BACKUP, DB_BUSY,
@@ -1652,11 +1737,11 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
DB_ERRORCODE, DB_EVAL, DB_EXISTS,
DB_FUNCTION, DB_INCRBLOB, DB_INTERRUPT,
DB_LAST_INSERT_ROWID, DB_NULLVALUE, DB_ONECOLUMN,
DB_PROFILE, DB_PROGRESS, DB_REKEY,
DB_RESTORE, DB_ROLLBACK_HOOK, DB_STATUS,
DB_TIMEOUT, DB_TOTAL_CHANGES, DB_TRACE,
DB_TRANSACTION, DB_UNLOCK_NOTIFY, DB_UPDATE_HOOK,
DB_VERSION, DB_WAL_HOOK
DB_PREUPDATE, DB_PROFILE, DB_PROGRESS,
DB_REKEY, DB_RESTORE, DB_ROLLBACK_HOOK,
DB_STATUS, DB_TIMEOUT, DB_TOTAL_CHANGES,
DB_TRACE, DB_TRANSACTION, DB_UNLOCK_NOTIFY,
DB_UPDATE_HOOK, DB_VERSION, DB_WAL_HOOK,
};
/* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */
@@ -2852,6 +2937,90 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
break;
}
/*
** $db preupdate_hook count
** $db preupdate_hook hook ?SCRIPT?
** $db preupdate_hook new INDEX
** $db preupdate_hook old INDEX
*/
case DB_PREUPDATE: {
#ifndef SQLITE_ENABLE_PREUPDATE_HOOK
Tcl_AppendResult(interp, "preupdate_hook was omitted at compile-time");
rc = TCL_ERROR;
#else
static const char *azSub[] = {"count", "depth", "hook", "new", "old", 0};
enum DbPreupdateSubCmd {
PRE_COUNT, PRE_DEPTH, PRE_HOOK, PRE_NEW, PRE_OLD
};
int iSub;
if( objc<3 ){
Tcl_WrongNumArgs(interp, 2, objv, "SUB-COMMAND ?ARGS?");
}
if( Tcl_GetIndexFromObj(interp, objv[2], azSub, "sub-command", 0, &iSub) ){
return TCL_ERROR;
}
switch( (enum DbPreupdateSubCmd)iSub ){
case PRE_COUNT: {
int nCol = sqlite3_preupdate_count(pDb->db);
Tcl_SetObjResult(interp, Tcl_NewIntObj(nCol));
break;
}
case PRE_HOOK: {
if( objc>4 ){
Tcl_WrongNumArgs(interp, 2, objv, "hook ?SCRIPT?");
return TCL_ERROR;
}
DbHookCmd(interp, pDb, (objc==4 ? objv[3] : 0), &pDb->pPreUpdateHook);
break;
}
case PRE_DEPTH: {
Tcl_Obj *pRet;
if( objc!=3 ){
Tcl_WrongNumArgs(interp, 3, objv, "");
return TCL_ERROR;
}
pRet = Tcl_NewIntObj(sqlite3_preupdate_depth(pDb->db));
Tcl_SetObjResult(interp, pRet);
break;
}
case PRE_NEW:
case PRE_OLD: {
int iIdx;
sqlite3_value *pValue;
if( objc!=4 ){
Tcl_WrongNumArgs(interp, 3, objv, "INDEX");
return TCL_ERROR;
}
if( Tcl_GetIntFromObj(interp, objv[3], &iIdx) ){
return TCL_ERROR;
}
if( iSub==PRE_OLD ){
rc = sqlite3_preupdate_old(pDb->db, iIdx, &pValue);
}else{
assert( iSub==PRE_NEW );
rc = sqlite3_preupdate_new(pDb->db, iIdx, &pValue);
}
if( rc==SQLITE_OK ){
Tcl_Obj *pObj;
pObj = Tcl_NewStringObj((char*)sqlite3_value_text(pValue), -1);
Tcl_SetObjResult(interp, pObj);
}else{
Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), 0);
return TCL_ERROR;
}
}
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
break;
}
/*
** $db wal_hook ?script?
** $db update_hook ?script?
@@ -2860,42 +3029,19 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
case DB_WAL_HOOK:
case DB_UPDATE_HOOK:
case DB_ROLLBACK_HOOK: {
/* set ppHook to point at pUpdateHook or pRollbackHook, depending on
** whether [$db update_hook] or [$db rollback_hook] was invoked.
*/
Tcl_Obj **ppHook;
if( choice==DB_UPDATE_HOOK ){
ppHook = &pDb->pUpdateHook;
}else if( choice==DB_WAL_HOOK ){
ppHook = &pDb->pWalHook;
}else{
ppHook = &pDb->pRollbackHook;
}
if( objc!=2 && objc!=3 ){
if( choice==DB_WAL_HOOK ) ppHook = &pDb->pWalHook;
if( choice==DB_UPDATE_HOOK ) ppHook = &pDb->pUpdateHook;
if( choice==DB_ROLLBACK_HOOK ) ppHook = &pDb->pRollbackHook;
if( objc>3 ){
Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?");
return TCL_ERROR;
}
if( *ppHook ){
Tcl_SetObjResult(interp, *ppHook);
if( objc==3 ){
Tcl_DecrRefCount(*ppHook);
*ppHook = 0;
}
}
if( objc==3 ){
assert( !(*ppHook) );
if( Tcl_GetCharLength(objv[2])>0 ){
*ppHook = objv[2];
Tcl_IncrRefCount(*ppHook);
}
}
sqlite3_update_hook(pDb->db, (pDb->pUpdateHook?DbUpdateHandler:0), pDb);
sqlite3_rollback_hook(pDb->db,(pDb->pRollbackHook?DbRollbackHandler:0),pDb);
sqlite3_wal_hook(pDb->db,(pDb->pWalHook?DbWalHandler:0),pDb);
DbHookCmd(interp, pDb, (objc==3 ? objv[2] : 0), ppHook);
break;
}
@@ -3771,6 +3917,9 @@ static void init_all(Tcl_Interp *interp){
extern int Sqlitemultiplex_Init(Tcl_Interp*);
extern int SqliteSuperlock_Init(Tcl_Interp*);
extern int SqlitetestSyscall_Init(Tcl_Interp*);
#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
extern int TestSession_Init(Tcl_Interp*);
#endif
#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
extern int Sqlitetestfts3_Init(Tcl_Interp *interp);
@@ -3814,6 +3963,9 @@ static void init_all(Tcl_Interp *interp){
Sqlitemultiplex_Init(interp);
SqliteSuperlock_Init(interp);
SqlitetestSyscall_Init(interp);
#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
TestSession_Init(interp);
#endif
#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
Sqlitetestfts3_Init(interp);
+12
View File
@@ -131,6 +131,12 @@ static void set_options(Tcl_Interp *interp){
Tcl_SetVar2(interp, "sqlite_options", "mem5", "0", TCL_GLOBAL_ONLY);
#endif
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
Tcl_SetVar2(interp, "sqlite_options", "preupdate", "1", TCL_GLOBAL_ONLY);
#else
Tcl_SetVar2(interp, "sqlite_options", "preupdate", "0", TCL_GLOBAL_ONLY);
#endif
#ifdef SQLITE_MUTEX_OMIT
Tcl_SetVar2(interp, "sqlite_options", "mutex", "0", TCL_GLOBAL_ONLY);
#else
@@ -479,6 +485,12 @@ Tcl_SetVar2(interp, "sqlite_options", "mergesort", "1", TCL_GLOBAL_ONLY);
Tcl_SetVar2(interp, "sqlite_options", "schema_version", "1", TCL_GLOBAL_ONLY);
#endif
#ifdef SQLITE_ENABLE_SESSION
Tcl_SetVar2(interp, "sqlite_options", "session", "1", TCL_GLOBAL_ONLY);
#else
Tcl_SetVar2(interp, "sqlite_options", "session", "0", TCL_GLOBAL_ONLY);
#endif
#ifdef SQLITE_ENABLE_STAT4
Tcl_SetVar2(interp, "sqlite_options", "stat4", "1", TCL_GLOBAL_ONLY);
#else
+4 -3
View File
@@ -430,8 +430,10 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
break;
}
case TK_ILLEGAL: {
sqlite3ErrorMsg(pParse, "unrecognized token: \"%T\"",
sqlite3DbFree(db, *pzErrMsg);
*pzErrMsg = sqlite3MPrintf(db, "unrecognized token: \"%T\"",
&pParse->sLastToken);
nErr++;
goto abort_parse;
}
case TK_SEMI: {
@@ -449,8 +451,7 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
}
}
abort_parse:
assert( nErr==0 );
if( zSql[i]==0 && pParse->rc==SQLITE_OK ){
if( zSql[i]==0 && nErr==0 && pParse->rc==SQLITE_OK ){
if( lastTokenParsed!=TK_SEMI ){
sqlite3Parser(pEngine, TK_SEMI, pParse->sLastToken, pParse);
pParse->zTail = &zSql[i];
+1
View File
@@ -680,6 +680,7 @@ static SrcList *targetSrcList(
pSrc = sqlite3SrcListAppend(pParse->db, 0, &pStep->target, 0);
if( pSrc ){
assert( pSrc->nSrc>0 );
assert( pSrc->a!=0 );
iDb = sqlite3SchemaToIndex(pParse->db, pStep->pTrig->pSchema);
if( iDb==0 || iDb>=2 ){
sqlite3 *db = pParse->db;
+17 -4
View File
@@ -584,10 +584,23 @@ void sqlite3Update(
VdbeCoverageNeverTaken(v);
}
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, aRegIdx);
/* If changing the record number, delete the old record. */
if( hasFK || chngKey || pPk!=0 ){
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, 0);
/* If changing the rowid value, or if there are foreign key constraints
** to process, delete the old record. Otherwise, add a noop OP_Delete
** to invoke the pre-update hook.
**
** That (regNew==regnewRowid+1) is true is also important for the
** pre-update hook. If the caller invokes preupdate_new(), the returned
** value is copied from memory cell (regNewRowid+1+iCol), where iCol
** is the column index supplied by the user.
*/
assert( regNew==regNewRowid+1 );
sqlite3VdbeAddOp3(v, OP_Delete, iDataCur,
OPFLAG_ISUPDATE | ((hasFK || chngKey || pPk!=0) ? 0 : OPFLAG_ISNOOP),
regNewRowid
);
if( !pParse->nested ){
sqlite3VdbeChangeP4(v, -1, (char*)pTab, P4_TABLE);
}
if( bReplace || chngKey ){
sqlite3VdbeJumpHere(v, j1);
-4
View File
@@ -250,8 +250,6 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
** an "INSERT INTO vacuum_db.xxx SELECT * FROM main.xxx;" to copy
** the contents to the temporary database.
*/
assert( (db->flags & SQLITE_Vacuum)==0 );
db->flags |= SQLITE_Vacuum;
rc = execExecSql(db, pzErrMsg,
"SELECT 'INSERT INTO vacuum_db.' || quote(name) "
"|| ' SELECT * FROM main.' || quote(name) || ';'"
@@ -259,8 +257,6 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
"WHERE type = 'table' AND name!='sqlite_sequence' "
" AND coalesce(rootpage,1)>0"
);
assert( (db->flags & SQLITE_Vacuum)!=0 );
db->flags &= ~SQLITE_Vacuum;
if( rc!=SQLITE_OK ) goto end_of_vacuum;
/* Copy over the sequence table
+247 -190
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -59,6 +59,7 @@ struct VdbeOp {
KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */
int *ai; /* Used when p4type is P4_INTARRAY */
SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */
Table *pTab; /* Used when p4type is P4_TABLE */
int (*xAdvance)(BtCursor *, int *);
} p4;
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
@@ -119,6 +120,7 @@ typedef struct VdbeOpList VdbeOpList;
#define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */
#define P4_SUBPROGRAM (-18) /* P4 is a pointer to a SubProgram structure */
#define P4_ADVANCE (-19) /* P4 is a pointer to BtreeNext() or BtreePrev() */
#define P4_TABLE (-20) /* P4 is a pointer to a Table structure */
/* Error message codes for OP_Halt */
#define P5_ConstraintNotNull 1
@@ -213,7 +215,6 @@ int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*);
void sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,UnpackedRecord*);
int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*);
int sqlite3VdbeRecordCompareWithSkip(int, const void *, UnpackedRecord *, int);
UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo *, char *, int, char **);
typedef int (*RecordCompare)(int,const void*,UnpackedRecord*);
+21
View File
@@ -388,6 +388,25 @@ struct Vdbe {
#define VDBE_MAGIC_HALT 0x519c2973 /* VDBE has completed execution */
#define VDBE_MAGIC_DEAD 0xb606c3c8 /* The VDBE has been deallocated */
/*
** Structure used to store the context required by the
** sqlite3_preupdate_*() API functions.
*/
struct PreUpdate {
Vdbe *v;
VdbeCursor *pCsr; /* Cursor to read old values from */
int op; /* One of SQLITE_INSERT, UPDATE, DELETE */
u8 *aRecord; /* old.* database record */
KeyInfo keyinfo;
UnpackedRecord *pUnpacked; /* Unpacked version of aRecord[] */
UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */
int iNewReg; /* Register for new.* values */
i64 iKey1; /* First key value passed to hook */
i64 iKey2; /* Second key value passed to hook */
int iPKey; /* If not negative index of IPK column */
Mem *aNew; /* Array of new.* values */
};
/*
** Function prototypes
*/
@@ -447,6 +466,8 @@ int sqlite3VdbeMemClearAndResize(Mem *pMem, int n);
int sqlite3VdbeCloseStatement(Vdbe *, int);
void sqlite3VdbeFrameDelete(VdbeFrame*);
int sqlite3VdbeFrameRestore(VdbeFrame *);
void sqlite3VdbePreUpdateHook(
Vdbe *, VdbeCursor *, int, const char*, Table *, i64, int);
int sqlite3VdbeTransferError(Vdbe *p);
int sqlite3VdbeSorterInit(sqlite3 *, int, VdbeCursor *);
+181
View File
@@ -1508,6 +1508,187 @@ int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){
return (int)v;
}
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
/*
** Allocate and populate an UnpackedRecord structure based on the serialized
** record in nKey/pKey. Return a pointer to the new UnpackedRecord structure
** if successful, or a NULL pointer if an OOM error is encountered.
*/
static UnpackedRecord *vdbeUnpackRecord(
KeyInfo *pKeyInfo,
int nKey,
const void *pKey
){
char *dummy; /* Dummy argument for AllocUnpackedRecord() */
UnpackedRecord *pRet; /* Return value */
pRet = sqlite3VdbeAllocUnpackedRecord(pKeyInfo, 0, 0, &dummy);
if( pRet ){
memset(pRet->aMem, 0, sizeof(Mem)*(pKeyInfo->nField+1));
sqlite3VdbeRecordUnpack(pKeyInfo, nKey, pKey, pRet);
}
return pRet;
}
/*
** This function is called from within a pre-update callback to retrieve
** a field of the row currently being updated or deleted.
*/
int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
PreUpdate *p = db->pPreUpdate;
int rc = SQLITE_OK;
/* Test that this call is being made from within an SQLITE_DELETE or
** SQLITE_UPDATE pre-update callback, and that iIdx is within range. */
if( !p || p->op==SQLITE_INSERT ){
rc = SQLITE_MISUSE_BKPT;
goto preupdate_old_out;
}
if( iIdx>=p->pCsr->nField || iIdx<0 ){
rc = SQLITE_RANGE;
goto preupdate_old_out;
}
/* If the old.* record has not yet been loaded into memory, do so now. */
if( p->pUnpacked==0 ){
u32 nRec;
u8 *aRec;
rc = sqlite3BtreeDataSize(p->pCsr->pCursor, &nRec);
if( rc!=SQLITE_OK ) goto preupdate_old_out;
aRec = sqlite3DbMallocRaw(db, nRec);
if( !aRec ) goto preupdate_old_out;
rc = sqlite3BtreeData(p->pCsr->pCursor, 0, nRec, aRec);
if( rc==SQLITE_OK ){
p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec);
if( !p->pUnpacked ) rc = SQLITE_NOMEM;
}
if( rc!=SQLITE_OK ){
sqlite3DbFree(db, aRec);
goto preupdate_old_out;
}
p->aRecord = aRec;
}
if( iIdx>=p->pUnpacked->nField ){
*ppValue = (sqlite3_value *)columnNullValue();
}else{
*ppValue = &p->pUnpacked->aMem[iIdx];
if( iIdx==p->iPKey ){
sqlite3VdbeMemSetInt64(*ppValue, p->iKey1);
}
}
preupdate_old_out:
sqlite3Error(db, rc);
return sqlite3ApiExit(db, rc);
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
/*
** This function is called from within a pre-update callback to retrieve
** the number of columns in the row being updated, deleted or inserted.
*/
int sqlite3_preupdate_count(sqlite3 *db){
PreUpdate *p = db->pPreUpdate;
return (p ? p->keyinfo.nField : 0);
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
/*
** This function is designed to be called from within a pre-update callback
** only. It returns zero if the change that caused the callback was made
** immediately by a user SQL statement. Or, if the change was made by a
** trigger program, it returns the number of trigger programs currently
** on the stack (1 for a top-level trigger, 2 for a trigger fired by a
** top-level trigger etc.).
**
** For the purposes of the previous paragraph, a foreign key CASCADE, SET NULL
** or SET DEFAULT action is considered a trigger.
*/
int sqlite3_preupdate_depth(sqlite3 *db){
PreUpdate *p = db->pPreUpdate;
return (p ? p->v->nFrame : 0);
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
/*
** This function is called from within a pre-update callback to retrieve
** a field of the row currently being updated or inserted.
*/
int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
PreUpdate *p = db->pPreUpdate;
int rc = SQLITE_OK;
Mem *pMem;
if( !p || p->op==SQLITE_DELETE ){
rc = SQLITE_MISUSE_BKPT;
goto preupdate_new_out;
}
if( iIdx>=p->pCsr->nField || iIdx<0 ){
rc = SQLITE_RANGE;
goto preupdate_new_out;
}
if( p->op==SQLITE_INSERT ){
/* For an INSERT, memory cell p->iNewReg contains the serialized record
** that is being inserted. Deserialize it. */
UnpackedRecord *pUnpack = p->pNewUnpacked;
if( !pUnpack ){
Mem *pData = &p->v->aMem[p->iNewReg];
rc = sqlite3VdbeMemExpandBlob(pData);
if( rc!=SQLITE_OK ) goto preupdate_new_out;
pUnpack = vdbeUnpackRecord(&p->keyinfo, pData->n, pData->z);
if( !pUnpack ){
rc = SQLITE_NOMEM;
goto preupdate_new_out;
}
p->pNewUnpacked = pUnpack;
}
if( iIdx>=pUnpack->nField ){
pMem = (sqlite3_value *)columnNullValue();
}else{
pMem = &pUnpack->aMem[iIdx];
if( iIdx==p->iPKey ){
sqlite3VdbeMemSetInt64(pMem, p->iKey2);
}
}
}else{
/* For an UPDATE, memory cell (p->iNewReg+1+iIdx) contains the required
** value. Make a copy of the cell contents and return a pointer to it.
** It is not safe to return a pointer to the memory cell itself as the
** caller may modify the value text encoding.
*/
assert( p->op==SQLITE_UPDATE );
if( !p->aNew ){
p->aNew = (Mem *)sqlite3DbMallocZero(db, sizeof(Mem) * p->pCsr->nField);
if( !p->aNew ){
rc = SQLITE_NOMEM;
goto preupdate_new_out;
}
}
assert( iIdx>=0 && iIdx<p->pCsr->nField );
pMem = &p->aNew[iIdx];
if( pMem->flags==0 ){
if( iIdx==p->iPKey ){
sqlite3VdbeMemSetInt64(pMem, p->iKey2);
}else{
rc = sqlite3VdbeMemCopy(pMem, &p->v->aMem[p->iNewReg+1+iIdx]);
if( rc!=SQLITE_OK ) goto preupdate_new_out;
}
}
}
*ppValue = pMem;
preupdate_new_out:
sqlite3Error(db, rc);
return sqlite3ApiExit(db, rc);
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
/*
** Return status data for a single loop within query pStmt.
+88 -4
View File
@@ -66,6 +66,7 @@ const char *sqlite3_sql(sqlite3_stmt *pStmt){
void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){
Vdbe tmp, *pTmp;
char *zTmp;
assert( pA->db==pB->db );
tmp = *pA;
*pA = *pB;
*pB = tmp;
@@ -3237,6 +3238,7 @@ void sqlite3VdbeRecordUnpack(
pMem->db = pKeyInfo->db;
/* pMem->flags = 0; // sqlite3VdbeSerialGet() will set this for us */
pMem->szMalloc = 0;
pMem->z = 0;
d += sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem);
pMem++;
if( (++u)>=p->nField ) break;
@@ -3585,7 +3587,7 @@ static i64 vdbeRecordDecodeInt(u32 serial_type, const u8 *aKey){
** pPKey2->errCode is set to SQLITE_NOMEM and, if it is not NULL, the
** malloc-failed flag set on database handle (pPKey2->pKeyInfo->db).
*/
int sqlite3VdbeRecordCompareWithSkip(
static int vdbeRecordCompareWithSkip(
int nKey1, const void *pKey1, /* Left key */
UnpackedRecord *pPKey2, /* Right key */
int bSkip /* If true, skip the first field */
@@ -3771,7 +3773,7 @@ int sqlite3VdbeRecordCompare(
int nKey1, const void *pKey1, /* Left key */
UnpackedRecord *pPKey2 /* Right key */
){
return sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 0);
return vdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 0);
}
@@ -3859,7 +3861,7 @@ static int vdbeRecordCompareInt(
}else if( pPKey2->nField>1 ){
/* The first fields of the two keys are equal. Compare the trailing
** fields. */
res = sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1);
res = vdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1);
}else{
/* The first fields of the two keys are equal and there are no trailing
** fields. Return pPKey2->default_rc in this case. */
@@ -3907,7 +3909,7 @@ static int vdbeRecordCompareString(
res = nStr - pPKey2->aMem[0].n;
if( res==0 ){
if( pPKey2->nField>1 ){
res = sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1);
res = vdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1);
}else{
res = pPKey2->default_rc;
}
@@ -4183,3 +4185,85 @@ void sqlite3VtabImportErrmsg(Vdbe *p, sqlite3_vtab *pVtab){
pVtab->zErrMsg = 0;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
/*
** If the second argument is not NULL, release any allocations associated
** with the memory cells in the p->aMem[] array. Also free the UnpackedRecord
** structure itself, using sqlite3DbFree().
**
** This function is used to free UnpackedRecord structures allocated by
** the vdbeUnpackRecord() function found in vdbeapi.c.
*/
static void vdbeFreeUnpacked(sqlite3 *db, UnpackedRecord *p){
if( p ){
int i;
for(i=0; i<p->nField; i++){
Mem *pMem = &p->aMem[i];
if( pMem->zMalloc ) sqlite3VdbeMemRelease(pMem);
}
sqlite3DbFree(db, p);
}
}
/*
** Invoke the pre-update hook. If this is an UPDATE or DELETE pre-update call,
** then cursor passed as the second argument should point to the row about
** to be update or deleted. If the application calls sqlite3_preupdate_old(),
** the required value will be read from the row the cursor points to.
*/
void sqlite3VdbePreUpdateHook(
Vdbe *v, /* Vdbe pre-update hook is invoked by */
VdbeCursor *pCsr, /* Cursor to grab old.* values from */
int op, /* SQLITE_INSERT, UPDATE or DELETE */
const char *zDb, /* Database name */
Table *pTab, /* Modified table */
i64 iKey1, /* Initial key value */
int iReg /* Register for new.* record */
){
sqlite3 *db = v->db;
i64 iKey2;
PreUpdate preupdate;
const char *zTbl = pTab->zName;
static const u8 fakeSortOrder = 0;
assert( db->pPreUpdate==0 );
memset(&preupdate, 0, sizeof(PreUpdate));
if( op==SQLITE_UPDATE ){
iKey2 = v->aMem[iReg].u.i;
}else{
iKey2 = iKey1;
}
assert( pCsr->nField==pTab->nCol
|| (pCsr->nField==pTab->nCol+1 && op==SQLITE_DELETE && iReg==-1)
);
preupdate.v = v;
preupdate.pCsr = pCsr;
preupdate.op = op;
preupdate.iNewReg = iReg;
preupdate.keyinfo.db = db;
preupdate.keyinfo.enc = ENC(db);
preupdate.keyinfo.nField = pTab->nCol;
preupdate.keyinfo.aSortOrder = (u8*)&fakeSortOrder;
preupdate.iKey1 = iKey1;
preupdate.iKey2 = iKey2;
preupdate.iPKey = pTab->iPKey;
db->pPreUpdate = &preupdate;
db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2);
db->pPreUpdate = 0;
sqlite3DbFree(db, preupdate.aRecord);
vdbeFreeUnpacked(db, preupdate.pUnpacked);
vdbeFreeUnpacked(db, preupdate.pNewUnpacked);
if( preupdate.aNew ){
int i;
for(i=0; i<pCsr->nField; i++){
sqlite3VdbeMemRelease(&preupdate.aNew[i]);
}
sqlite3DbFree(db, preupdate.aNew);
}
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
+28
View File
@@ -30,6 +30,8 @@ struct Incrblob {
BtCursor *pCsr; /* Cursor pointing at blob row */
sqlite3_stmt *pStmt; /* Statement holding cursor open */
sqlite3 *db; /* The associated database */
char *zDb; /* Database name */
Table *pTab; /* Table object */
};
@@ -205,6 +207,8 @@ int sqlite3_blob_open(
sqlite3BtreeLeaveAll(db);
goto blob_open_out;
}
pBlob->pTab = pTab;
pBlob->zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zName;
/* Now search pTab for the exact column. */
for(iCol=0; iCol<pTab->nCol; iCol++) {
@@ -392,6 +396,30 @@ static int blobReadWrite(
*/
assert( db == v->db );
sqlite3BtreeEnterCursor(p->pCsr);
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
if( xCall==sqlite3BtreePutData && db->xPreUpdateCallback ){
/* If a pre-update hook is registered and this is a write cursor,
** invoke it here.
**
** TODO: The preupdate-hook is passed SQLITE_DELETE, even though this
** operation should really be an SQLITE_UPDATE. This is probably
** incorrect, but is convenient because at this point the new.* values
** are not easily obtainable. And for the sessions module, an
** SQLITE_UPDATE where the PK columns do not change is handled in the
** same way as an SQLITE_DELETE (the SQLITE_DELETE code is actually
** slightly more efficient). Since you cannot write to a PK column
** using the incremental-blob API, this works. For the sessions module
** anyhow.
*/
sqlite3_int64 iKey;
sqlite3BtreeKeySize(p->pCsr, &iKey);
sqlite3VdbePreUpdateHook(
v, v->apCsr[0], SQLITE_DELETE, p->zDb, p->pTab, iKey, -1
);
}
#endif
rc = xCall(p->pCsr, iOffset+p->iOffset, n, z);
sqlite3BtreeLeaveCursor(p->pCsr);
if( rc==SQLITE_ABORT ){
-1
View File
@@ -799,7 +799,6 @@ void sqlite3VdbeMemShallowCopy(Mem *pTo, const Mem *pFrom, int srcType){
int sqlite3VdbeMemCopy(Mem *pTo, const Mem *pFrom){
int rc = SQLITE_OK;
assert( pTo->db==pFrom->db );
assert( (pFrom->flags & MEM_RowSet)==0 );
if( VdbeMemDynamic(pTo) ) vdbeMemClearExternAndSetNull(pTo);
memcpy(pTo, pFrom, MEMCELLSIZE);
+29 -203
View File
@@ -291,7 +291,6 @@ struct MergeEngine {
** after the thread has finished are not dire. So we don't worry about
** memory barriers and such here.
*/
typedef int (*SorterCompare)(SortSubtask*,int*,const void*,int,const void*,int);
struct SortSubtask {
SQLiteThread *pThread; /* Background thread, if any */
int bDone; /* Set if thread is finished but not joined */
@@ -299,12 +298,10 @@ struct SortSubtask {
UnpackedRecord *pUnpacked; /* Space to unpack a record */
SorterList list; /* List for thread to write to a PMA */
int nPMA; /* Number of PMAs currently in file */
SorterCompare xCompare; /* Compare function to use */
SorterFile file; /* Temp file for level-0 PMAs */
SorterFile file2; /* Space for other PMAs */
};
/*
** Main sorter structure. A single instance of this is allocated for each
** sorter cursor created by the VDBE.
@@ -331,13 +328,9 @@ struct VdbeSorter {
u8 bUseThreads; /* True to use background threads */
u8 iPrev; /* Previous thread used to flush PMA */
u8 nTask; /* Size of aTask[] array */
u8 typeMask;
SortSubtask aTask[1]; /* One or more subtasks */
};
#define SORTER_TYPE_INTEGER 0x01
#define SORTER_TYPE_TEXT 0x02
/*
** An instance of the following object is used to read records out of a
** PMA, in sorted order. The next key to be read is cached in nKey/aKey.
@@ -749,162 +742,32 @@ static int vdbePmaReaderInit(
return rc;
}
/*
** A version of vdbeSorterCompare() that assumes that it has already been
** determined that the first field of key1 is equal to the first field of
** key2.
*/
static int vdbeSorterCompareTail(
SortSubtask *pTask, /* Subtask context (for pKeyInfo) */
int *pbKey2Cached, /* True if pTask->pUnpacked is pKey2 */
const void *pKey1, int nKey1, /* Left side of comparison */
const void *pKey2, int nKey2 /* Right side of comparison */
){
UnpackedRecord *r2 = pTask->pUnpacked;
if( *pbKey2Cached==0 ){
sqlite3VdbeRecordUnpack(pTask->pSorter->pKeyInfo, nKey2, pKey2, r2);
*pbKey2Cached = 1;
}
return sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, r2, 1);
}
/*
** Compare key1 (buffer pKey1, size nKey1 bytes) with key2 (buffer pKey2,
** size nKey2 bytes). Use (pTask->pKeyInfo) for the collation sequences
** used by the comparison. Return the result of the comparison.
**
** If IN/OUT parameter *pbKey2Cached is true when this function is called,
** it is assumed that (pTask->pUnpacked) contains the unpacked version
** of key2. If it is false, (pTask->pUnpacked) is populated with the unpacked
** version of key2 and *pbKey2Cached set to true before returning.
** Before returning, object (pTask->pUnpacked) is populated with the
** unpacked version of key2. Or, if pKey2 is passed a NULL pointer, then it
** is assumed that the (pTask->pUnpacked) structure already contains the
** unpacked key to use as key2.
**
** If an OOM error is encountered, (pTask->pUnpacked->error_rc) is set
** to SQLITE_NOMEM.
*/
static int vdbeSorterCompare(
SortSubtask *pTask, /* Subtask context (for pKeyInfo) */
int *pbKey2Cached, /* True if pTask->pUnpacked is pKey2 */
const void *pKey1, int nKey1, /* Left side of comparison */
const void *pKey2, int nKey2 /* Right side of comparison */
){
UnpackedRecord *r2 = pTask->pUnpacked;
if( !*pbKey2Cached ){
if( pKey2 ){
sqlite3VdbeRecordUnpack(pTask->pSorter->pKeyInfo, nKey2, pKey2, r2);
*pbKey2Cached = 1;
}
return sqlite3VdbeRecordCompare(nKey1, pKey1, r2);
}
/*
** A specially optimized version of vdbeSorterCompare() that assumes that
** the first field of each key is a TEXT value and that the collation
** sequence to compare them with is BINARY.
*/
static int vdbeSorterCompareText(
SortSubtask *pTask, /* Subtask context (for pKeyInfo) */
int *pbKey2Cached, /* True if pTask->pUnpacked is pKey2 */
const void *pKey1, int nKey1, /* Left side of comparison */
const void *pKey2, int nKey2 /* Right side of comparison */
){
const u8 * const p1 = (const u8 * const)pKey1;
const u8 * const p2 = (const u8 * const)pKey2;
const u8 * const v1 = &p1[ p1[0] ]; /* Pointer to value 1 */
const u8 * const v2 = &p2[ p2[0] ]; /* Pointer to value 2 */
int n1;
int n2;
int res;
getVarint32(&p1[1], n1); n1 = (n1 - 13) / 2;
getVarint32(&p2[1], n2); n2 = (n2 - 13) / 2;
res = memcmp(v1, v2, MIN(n1, n2));
if( res==0 ){
res = n1 - n2;
}
if( res==0 ){
if( pTask->pSorter->pKeyInfo->nField>1 ){
res = vdbeSorterCompareTail(
pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2
);
}
}else{
if( pTask->pSorter->pKeyInfo->aSortOrder[0] ){
res = res * -1;
}
}
return res;
}
/*
** A specially optimized version of vdbeSorterCompare() that assumes that
** the first field of each key is an INTEGER value.
*/
static int vdbeSorterCompareInt(
SortSubtask *pTask, /* Subtask context (for pKeyInfo) */
int *pbKey2Cached, /* True if pTask->pUnpacked is pKey2 */
const void *pKey1, int nKey1, /* Left side of comparison */
const void *pKey2, int nKey2 /* Right side of comparison */
){
const u8 * const p1 = (const u8 * const)pKey1;
const u8 * const p2 = (const u8 * const)pKey2;
const int s1 = p1[1]; /* Left hand serial type */
const int s2 = p2[1]; /* Right hand serial type */
const u8 * const v1 = &p1[ p1[0] ]; /* Pointer to value 1 */
const u8 * const v2 = &p2[ p2[0] ]; /* Pointer to value 2 */
int res; /* Return value */
assert( (s1>0 && s1<7) || s1==8 || s1==9 );
assert( (s2>0 && s2<7) || s2==8 || s2==9 );
if( s1>7 && s2>7 ){
res = s1 - s2;
}else{
if( s1==s2 ){
if( (*v1 ^ *v2) & 0x80 ){
/* The two values have different signs */
res = (*v1 & 0x80) ? -1 : +1;
}else{
/* The two values have the same sign. Compare using memcmp(). */
static const u8 aLen[] = {0, 1, 2, 3, 4, 6, 8 };
int i;
res = 0;
for(i=0; i<aLen[s1]; i++){
if( (res = v1[i] - v2[i]) ) break;
}
}
}else{
if( s2>7 ){
res = +1;
}else if( s1>7 ){
res = -1;
}else{
res = s1 - s2;
}
assert( res!=0 );
if( res>0 ){
if( *v1 & 0x80 ) res = -1;
}else{
if( *v2 & 0x80 ) res = +1;
}
}
}
if( res==0 ){
if( pTask->pSorter->pKeyInfo->nField>1 ){
res = vdbeSorterCompareTail(
pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2
);
}
}else if( pTask->pSorter->pKeyInfo->aSortOrder[0] ){
res = res * -1;
}
return res;
}
/*
** Initialize the temporary index cursor just opened as a sorter cursor.
**
@@ -972,13 +835,9 @@ int sqlite3VdbeSorterInit(
pSorter->pKeyInfo = pKeyInfo = (KeyInfo*)((u8*)pSorter + sz);
memcpy(pKeyInfo, pCsr->pKeyInfo, szKeyInfo);
pKeyInfo->db = 0;
if( nField && nWorker==0 ){
pKeyInfo->nXField += (pKeyInfo->nField - nField);
pKeyInfo->nField = nField;
}
if( nField && nWorker==0 ) pKeyInfo->nField = nField;
pSorter->pgsz = pgsz = sqlite3BtreeGetPageSize(db->aDb[0].pBt);
pSorter->nTask = nWorker + 1;
pSorter->iPrev = nWorker-1;
pSorter->bUseThreads = (pSorter->nTask>1);
pSorter->db = db;
for(i=0; i<pSorter->nTask; i++){
@@ -1004,12 +863,6 @@ int sqlite3VdbeSorterInit(
if( !pSorter->list.aMemory ) rc = SQLITE_NOMEM;
}
}
if( (pKeyInfo->nField+pKeyInfo->nXField)<13
&& (pKeyInfo->aColl[0]==0 || pKeyInfo->aColl[0]==db->pDfltColl)
){
pSorter->typeMask = SORTER_TYPE_INTEGER | SORTER_TYPE_TEXT;
}
}
return rc;
@@ -1034,24 +887,30 @@ static void vdbeSorterRecordFree(sqlite3 *db, SorterRecord *pRecord){
*/
static void vdbeSortSubtaskCleanup(sqlite3 *db, SortSubtask *pTask){
sqlite3DbFree(db, pTask->pUnpacked);
pTask->pUnpacked = 0;
#if SQLITE_MAX_WORKER_THREADS>0
/* pTask->list.aMemory can only be non-zero if it was handed memory
** from the main thread. That only occurs SQLITE_MAX_WORKER_THREADS>0 */
if( pTask->list.aMemory ){
sqlite3_free(pTask->list.aMemory);
pTask->list.aMemory = 0;
}else
#endif
{
assert( pTask->list.aMemory==0 );
vdbeSorterRecordFree(0, pTask->list.pList);
}
pTask->list.pList = 0;
if( pTask->file.pFd ){
sqlite3OsCloseFree(pTask->file.pFd);
pTask->file.pFd = 0;
pTask->file.iEof = 0;
}
if( pTask->file2.pFd ){
sqlite3OsCloseFree(pTask->file2.pFd);
pTask->file2.pFd = 0;
pTask->file2.iEof = 0;
}
memset(pTask, 0, sizeof(SortSubtask));
}
#ifdef SQLITE_DEBUG_SORTER_THREADS
@@ -1231,7 +1090,6 @@ void sqlite3VdbeSorterReset(sqlite3 *db, VdbeSorter *pSorter){
for(i=0; i<pSorter->nTask; i++){
SortSubtask *pTask = &pSorter->aTask[i];
vdbeSortSubtaskCleanup(db, pTask);
pTask->pSorter = pSorter;
}
if( pSorter->list.aMemory==0 ){
vdbeSorterRecordFree(0, pSorter->list.pList);
@@ -1341,42 +1199,28 @@ static void vdbeSorterMerge(
){
SorterRecord *pFinal = 0;
SorterRecord **pp = &pFinal;
int bCached = 0;
void *pVal2 = p2 ? SRVAL(p2) : 0;
while( p1 && p2 ){
int res;
res = pTask->xCompare(
pTask, &bCached, SRVAL(p1), p1->nVal, SRVAL(p2), p2->nVal
);
res = vdbeSorterCompare(pTask, SRVAL(p1), p1->nVal, pVal2, p2->nVal);
if( res<=0 ){
*pp = p1;
pp = &p1->u.pNext;
p1 = p1->u.pNext;
pVal2 = 0;
}else{
*pp = p2;
pp = &p2->u.pNext;
pp = &p2->u.pNext;
p2 = p2->u.pNext;
bCached = 0;
if( p2==0 ) break;
pVal2 = SRVAL(p2);
}
}
*pp = p1 ? p1 : p2;
*ppOut = pFinal;
}
/*
** Return the SorterCompare function to compare values collected by the
** sorter object passed as the only argument.
*/
static SorterCompare vdbeSorterGetCompare(VdbeSorter *p){
if( p->typeMask==SORTER_TYPE_INTEGER ){
return vdbeSorterCompareInt;
}else if( p->typeMask==SORTER_TYPE_TEXT ){
return vdbeSorterCompareText;
}
return vdbeSorterCompare;
}
/*
** Sort the linked list of records headed at pTask->pList. Return
** SQLITE_OK if successful, or an SQLite error code (i.e. SQLITE_NOMEM) if
@@ -1391,14 +1235,12 @@ static int vdbeSorterSort(SortSubtask *pTask, SorterList *pList){
rc = vdbeSortAllocUnpacked(pTask);
if( rc!=SQLITE_OK ) return rc;
p = pList->pList;
pTask->xCompare = vdbeSorterGetCompare(pTask->pSorter);
aSlot = (SorterRecord **)sqlite3MallocZero(64 * sizeof(SorterRecord *));
if( !aSlot ){
return SQLITE_NOMEM;
}
p = pList->pList;
while( p ){
SorterRecord *pNext;
if( pList->aMemory ){
@@ -1612,12 +1454,13 @@ static int vdbeMergeEngineStep(
int i; /* Index of aTree[] to recalculate */
PmaReader *pReadr1; /* First PmaReader to compare */
PmaReader *pReadr2; /* Second PmaReader to compare */
int bCached = 0;
u8 *pKey2; /* To pReadr2->aKey, or 0 if record cached */
/* Find the first two PmaReaders to compare. The one that was just
** advanced (iPrev) and the one next to it in the array. */
pReadr1 = &pMerger->aReadr[(iPrev & 0xFFFE)];
pReadr2 = &pMerger->aReadr[(iPrev | 0x0001)];
pKey2 = pReadr2->aKey;
for(i=(pMerger->nTree+iPrev)/2; i>0; i=i/2){
/* Compare pReadr1 and pReadr2. Store the result in variable iRes. */
@@ -1627,8 +1470,8 @@ static int vdbeMergeEngineStep(
}else if( pReadr2->pFd==0 ){
iRes = -1;
}else{
iRes = pTask->xCompare(pTask, &bCached,
pReadr1->aKey, pReadr1->nKey, pReadr2->aKey, pReadr2->nKey
iRes = vdbeSorterCompare(pTask,
pReadr1->aKey, pReadr1->nKey, pKey2, pReadr2->nKey
);
}
@@ -1650,9 +1493,9 @@ static int vdbeMergeEngineStep(
if( iRes<0 || (iRes==0 && pReadr1<pReadr2) ){
pMerger->aTree[i] = (int)(pReadr1 - pMerger->aReadr);
pReadr2 = &pMerger->aReadr[ pMerger->aTree[i ^ 0x0001] ];
bCached = 0;
pKey2 = pReadr2->aKey;
}else{
if( pReadr1->pFd ) bCached = 0;
if( pReadr1->pFd ) pKey2 = 0;
pMerger->aTree[i] = (int)(pReadr2 - pMerger->aReadr);
pReadr1 = &pMerger->aReadr[ pMerger->aTree[i ^ 0x0001] ];
}
@@ -1759,16 +1602,6 @@ int sqlite3VdbeSorterWrite(
int bFlush; /* True to flush contents of memory to PMA */
int nReq; /* Bytes of memory required */
int nPMA; /* Bytes of PMA space required */
int t; /* serial type of first record field */
getVarint32((const u8*)&pVal->z[1], t);
if( t>0 && t<10 && t!=7 ){
pSorter->typeMask &= SORTER_TYPE_INTEGER;
}else if( t>10 && (t & 0x01) ){
pSorter->typeMask &= SORTER_TYPE_TEXT;
}else{
pSorter->typeMask = 0;
}
assert( pSorter );
@@ -2034,12 +1867,10 @@ static void vdbeMergeEngineCompare(
}else if( p2->pFd==0 ){
iRes = i1;
}else{
SortSubtask *pTask = pMerger->pTask;
int bCached = 0;
int res;
assert( pTask->pUnpacked!=0 ); /* from vdbeSortSubtaskMain() */
res = pTask->xCompare(
pTask, &bCached, p1->aKey, p1->nKey, p2->aKey, p2->nKey
assert( pMerger->pTask->pUnpacked!=0 ); /* from vdbeSortSubtaskMain() */
res = vdbeSorterCompare(
pMerger->pTask, p1->aKey, p1->nKey, p2->aKey, p2->nKey
);
if( res<=0 ){
iRes = i1;
@@ -2457,11 +2288,6 @@ static int vdbeSorterSetupMerge(VdbeSorter *pSorter){
MergeEngine *pMain = 0;
#if SQLITE_MAX_WORKER_THREADS
sqlite3 *db = pTask0->pSorter->db;
int i;
SorterCompare xCompare = vdbeSorterGetCompare(pSorter);
for(i=0; i<pSorter->nTask; i++){
pSorter->aTask[i].xCompare = xCompare;
}
#endif
rc = vdbeSorterMergeTreeBuild(pSorter, &pMain);
+8 -26
View File
@@ -24,8 +24,6 @@
struct VtabCtx {
VTable *pVTable; /* The virtual table being constructed */
Table *pTab; /* The Table object to which the virtual table belongs */
VtabCtx *pPrior; /* Parent context (if any) */
int bDeclared; /* True after sqlite3_declare_vtab() is called */
};
/*
@@ -489,27 +487,15 @@ static int vtabCallConstructor(
int (*xConstruct)(sqlite3*,void*,int,const char*const*,sqlite3_vtab**,char**),
char **pzErr
){
VtabCtx sCtx;
VtabCtx sCtx, *pPriorCtx;
VTable *pVTable;
int rc;
const char *const*azArg = (const char *const*)pTab->azModuleArg;
int nArg = pTab->nModuleArg;
char *zErr = 0;
char *zModuleName;
char *zModuleName = sqlite3MPrintf(db, "%s", pTab->zName);
int iDb;
VtabCtx *pCtx;
/* Check that the virtual-table is not already being initialized */
for(pCtx=db->pVtabCtx; pCtx; pCtx=pCtx->pPrior){
if( pCtx->pTab==pTab ){
*pzErr = sqlite3MPrintf(db,
"vtable constructor called recursively: %s", pTab->zName
);
return SQLITE_LOCKED;
}
}
zModuleName = sqlite3MPrintf(db, "%s", pTab->zName);
if( !zModuleName ){
return SQLITE_NOMEM;
}
@@ -530,13 +516,11 @@ static int vtabCallConstructor(
assert( xConstruct );
sCtx.pTab = pTab;
sCtx.pVTable = pVTable;
sCtx.pPrior = db->pVtabCtx;
sCtx.bDeclared = 0;
pPriorCtx = db->pVtabCtx;
db->pVtabCtx = &sCtx;
rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr);
db->pVtabCtx = sCtx.pPrior;
db->pVtabCtx = pPriorCtx;
if( rc==SQLITE_NOMEM ) db->mallocFailed = 1;
assert( sCtx.pTab==pTab );
if( SQLITE_OK!=rc ){
if( zErr==0 ){
@@ -552,7 +536,7 @@ static int vtabCallConstructor(
memset(pVTable->pVtab, 0, sizeof(pVTable->pVtab[0]));
pVTable->pVtab->pModule = pMod->pModule;
pVTable->nRef = 1;
if( sCtx.bDeclared==0 ){
if( sCtx.pTab ){
const char *zFormat = "vtable constructor did not declare schema: %s";
*pzErr = sqlite3MPrintf(db, zFormat, pTab->zName);
sqlite3VtabUnlock(pVTable);
@@ -722,8 +706,8 @@ int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, char **pzErr){
** virtual table module.
*/
int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
VtabCtx *pCtx;
Parse *pParse;
int rc = SQLITE_OK;
Table *pTab;
char *zErr = 0;
@@ -734,13 +718,11 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
}
#endif
sqlite3_mutex_enter(db->mutex);
pCtx = db->pVtabCtx;
if( !pCtx || pCtx->bDeclared ){
if( !db->pVtabCtx || !(pTab = db->pVtabCtx->pTab) ){
sqlite3Error(db, SQLITE_MISUSE);
sqlite3_mutex_leave(db->mutex);
return SQLITE_MISUSE_BKPT;
}
pTab = pCtx->pTab;
assert( (pTab->tabFlags & TF_Virtual)!=0 );
pParse = sqlite3StackAllocZero(db, sizeof(*pParse));
@@ -763,7 +745,7 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
pParse->pNewTable->nCol = 0;
pParse->pNewTable->aCol = 0;
}
pCtx->bDeclared = 1;
db->pVtabCtx->pTab = 0;
}else{
sqlite3ErrorWithMsg(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr);
sqlite3DbFree(db, zErr);
-8
View File
@@ -1730,14 +1730,6 @@ static int walCheckpoint(
mxSafeFrame = pWal->hdr.mxFrame;
mxPage = pWal->hdr.nPage;
for(i=1; i<WAL_NREADER; i++){
/* Thread-sanitizer reports that the following is an unsafe read,
** as some other thread may be in the process of updating the value
** of the aReadMark[] slot. The assumption here is that if that is
** happening, the other client may only be increasing the value,
** not decreasing it. So assuming either that either the "old" or
** "new" version of the value is read, and not some arbitrary value
** that would never be written by a real client, things are still
** safe. */
u32 y = pInfo->aReadMark[i];
if( mxSafeFrame>y ){
assert( y<=pWal->hdr.mxFrame );
+4 -4
View File
@@ -1532,7 +1532,7 @@ static int findIndexCol(
&& p->iTable==iBase
){
CollSeq *pColl = sqlite3ExprCollSeq(pParse, pList->a[i].pExpr);
if( pColl && 0==sqlite3StrICmp(pColl->zName, zColl) ){
if( ALWAYS(pColl) && 0==sqlite3StrICmp(pColl->zName, zColl) ){
return i;
}
}
@@ -1806,7 +1806,7 @@ static void constructAutomaticIndex(
idxCols |= cMask;
pIdx->aiColumn[n] = pTerm->u.leftColumn;
pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight);
pIdx->azColl[n] = pColl ? pColl->zName : "BINARY";
pIdx->azColl[n] = ALWAYS(pColl) ? pColl->zName : "BINARY";
n++;
}
}
@@ -4781,7 +4781,7 @@ static int whereLoopAddBtreeIndex(
}else if( eOp & (WO_EQ) ){
pNew->wsFlags |= WHERE_COLUMN_EQ;
if( iCol<0 || (nInMul==0 && pNew->u.btree.nEq==pProbe->nKeyCol-1) ){
if( iCol>=0 && pProbe->uniqNotNull==0 ){
if( iCol>=0 && !IsUniqueIndex(pProbe) ){
pNew->wsFlags |= WHERE_UNQ_WANTED;
}else{
pNew->wsFlags |= WHERE_ONEROW;
@@ -6241,7 +6241,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
pWInfo->revMask = pFrom->revLoop;
}
if( (pWInfo->wctrlFlags & WHERE_SORTBYGROUP)
&& pWInfo->nOBSat==pWInfo->pOrderBy->nExpr && nLoop>0
&& pWInfo->nOBSat==pWInfo->pOrderBy->nExpr
){
Bitmask revMask = 0;
int nOrder = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pOrderBy,
+1 -1
View File
@@ -359,6 +359,6 @@ do_test analyze-99.1 {
catchsql {
ANALYZE
}
} {1 {malformed database schema (sqlite_stat1)}}
} {1 {malformed database schema (sqlite_stat1) - near "nonsense": syntax error}}
finish_test
-7
View File
@@ -512,12 +512,5 @@ do_execsql_test autoindex1-901 {
WHERE t1.x IN (1,2,3);
} {/USING AUTOMATIC COVERING INDEX/}
# 2015-04-15: A NULL CollSeq pointer in automatic index creation.
#
do_execsql_test autoindex1-920 {
CREATE TABLE t920(x);
INSERT INTO t920 VALUES(3),(4),(5);
SELECT * FROM t920,(SELECT 0 FROM t920),(VALUES(9)) WHERE 5 IN (x);
} {5 0 9 5 0 9 5 0 9}
finish_test
+2 -16
View File
@@ -385,20 +385,6 @@ do_execsql_test 6.8 {
SELECT x, y FROM c1 ORDER BY y COLLATE """""""";
} {2 abb 1 ABC 4 WXY 3 wxz}
# 2015-04-15: Nested COLLATE operators
#
do_execsql_test 7.0 {
SELECT 'abc' UNION ALL SELECT 'DEF'
ORDER BY 1 COLLATE nocase COLLATE nocase COLLATE nocase COLLATE nocase;
} {abc DEF}
do_execsql_test 7.1 {
SELECT 'abc' UNION ALL SELECT 'DEF'
ORDER BY 1 COLLATE nocase COLLATE nocase COLLATE nocase COLLATE binary;
} {DEF abc}
do_execsql_test 7.2 {
SELECT 'abc' UNION ALL SELECT 'DEF'
ORDER BY 1 COLLATE binary COLLATE binary COLLATE binary COLLATE nocase;
} {abc DEF}
finish_test
+1 -6
View File
@@ -32,7 +32,7 @@ source $testdir/tester.tcl
#
do_test collate3-1.0 {
execsql {
CREATE TABLE collate3t1(c1 UNIQUE);
CREATE TABLE collate3t1(c1);
}
} {}
do_test collate3-1.1 {
@@ -40,11 +40,6 @@ do_test collate3-1.1 {
SELECT * FROM collate3t1 ORDER BY 1 collate garbage;
}
} {1 {no such collation sequence: garbage}}
do_test collate3-1.1.2 {
catchsql {
SELECT DISTINCT c1 COLLATE garbage FROM collate3t1;
}
} {1 {no such collation sequence: garbage}}
do_test collate3-1.2 {
catchsql {
CREATE TABLE collate3t2(c1 collate garbage);
+2 -2
View File
@@ -746,10 +746,10 @@ do_test fkey2-10.2.2 {
drop_all_tables
do_test fkey2-11.1.1 {
execsql {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, rowid, _rowid_, oid);
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
CREATE TABLE t2(c, d, FOREIGN KEY(c) REFERENCES t1(a) ON UPDATE CASCADE);
INSERT INTO t1 VALUES(10, 100, 'abc', 'def', 'ghi');
INSERT INTO t1 VALUES(10, 100);
INSERT INTO t2 VALUES(10, 100);
UPDATE t1 SET a = 15;
SELECT * FROM t2;
+1 -1
View File
@@ -1,4 +1,4 @@
# 2013-07-11
# 2012 December 17
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
-19
View File
@@ -538,25 +538,6 @@ do_execsql_test 3.4 {
SELECT snippet(t3) FROM t3 WHERE t3 MATCH 'one OR two OR three';
} {{[<b>one</b> <b>two</b> <b>three</b>]}}
#-------------------------------------------------------------------------
# Request a snippet 0 tokens in size. This is always an empty string.
do_execsql_test 4.1 {
CREATE VIRTUAL TABLE t4 USING fts4;
INSERT INTO t4 VALUES('a b c d');
SELECT snippet(t4, '[', ']', '...', 0, 0) FROM t4 WHERE t4 MATCH 'b';
} {{}}
do_test 4.2 {
set x35 [string trim [string repeat "x " 35]]
execsql "INSERT INTO t4 VALUES('$x35 E $x35 F $x35 G $x35');"
llength [db one {
SELECT snippet(t4, '', '', '', 0, 64) FROM t4 WHERE t4 MATCH 'E'
}]
} {64}
set sqlite_fts3_enable_parentheses 0
finish_test
-10
View File
@@ -110,15 +110,5 @@ do_catchsql_test 2.1 {
SELECT * FROM t4;
} {1 {SQL logic error or missing database}}
do_catchsql_test 2.2 {
CREATE VIRTUAL TABLE t USING fts4(tokenize=simple"");
} {0 {}}
ifcapable fts3_unicode {
do_catchsql_test 2.3 {
CREATE VIRTUAL TABLE u USING fts4(tokenize=unicode61"");
} {1 {unknown tokenizer}}
}
finish_test
+2 -16
View File
@@ -48,9 +48,6 @@ ifcapable !fts3 {
#
# 9.* - Test using content=xxx where xxx is a virtual table.
#
# 11.* - Test that circular references (e.g. "t1(content=t1)") are
# detected.
#
do_execsql_test 1.1.1 {
CREATE TABLE t1(a, b, c);
@@ -409,7 +406,7 @@ do_execsql_test 5.1.7 {
#
do_catchsql_test 6.1.1 {
CREATE VIRTUAL TABLE ft7 USING fts4(content=t7);
} {1 {no such table: main.t7}}
} {1 {vtable constructor failed: ft7}}
do_execsql_test 6.2.1 {
CREATE TABLE t7(one, two);
@@ -436,7 +433,7 @@ do_execsql_test 6.2.3 {
}
do_catchsql_test 6.2.4 {
SELECT * FROM ft7;
} {1 {no such table: main.t7}}
} {1 {vtable constructor failed: ft7}}
do_execsql_test 6.2.5 {
CREATE TABLE t7(x, y);
INSERT INTO t7 VALUES('A B', 'B A');
@@ -625,15 +622,4 @@ do_execsql_test 10.7 {
{...c d [e] f g...}
}
#-------------------------------------------------------------------------
# Test cases 11.*
#
reset_db
do_catchsql_test 11.1 {
CREATE VIRTUAL TABLE x1 USING fts4(content=x1);
} {1 {vtable constructor called recursively: x1}}
finish_test
-5
View File
@@ -109,11 +109,6 @@ do_execsql_test hexlit-301 {
do_catchsql_test hexlist-400 {
SELECT 0x10000000000000000;
} {1 {hex literal too big: 0x10000000000000000}}
do_catchsql_test hexlist-410 {
DROP TABLE IF EXISTS t1;
CREATE TABLE t1(x);
INSERT INTO t1 VALUES(1+0x10000000000000000);
} {1 {hex literal too big: 0x10000000000000000}}
finish_test
+413
View File
@@ -21,6 +21,7 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix hook
do_test hook-1.2 {
db commit_hook
@@ -196,10 +197,13 @@ do_test hook-4.1.2 {
# EVIDENCE-OF: R-33257-44249 The update hook is not invoked when WITHOUT
# ROWID tables are modified.
#
breakpoint
do_test hook-4.1.2w {
set ::update_hook {}
execsql {
INSERT INTO t1w VALUES(4, 'four');
PRAGMA vdbe_debug=on;
PRAGMA vdbe_addoptrace=on;
DELETE FROM t1w WHERE b = 'two';
UPDATE t1w SET b = '' WHERE a = 1 OR a = 3;
DELETE FROM t1w WHERE 1; -- Avoid the truncate optimization (for now)
@@ -438,4 +442,413 @@ do_test hook-6.2 {
} {COMMIT ROLLBACK}
unset ::hooks
#----------------------------------------------------------------------------
# The following tests - hook-7.* - test the pre-update hook.
#
ifcapable !preupdate {
finish_test
return
}
#
# 7.1.1 - INSERT statement.
# 7.1.2 - INSERT INTO ... SELECT statement.
# 7.1.3 - REPLACE INTO ... (rowid conflict)
# 7.1.4 - REPLACE INTO ... (other index conflicts)
# 7.1.5 - REPLACE INTO ... (both rowid and other index conflicts)
#
# 7.2.1 - DELETE statement.
# 7.2.2 - DELETE statement that uses the truncate optimization.
#
# 7.3.1 - UPDATE statement.
# 7.3.2 - UPDATE statement that modifies the rowid.
# 7.3.3 - UPDATE OR REPLACE ... (rowid conflict).
# 7.3.4 - UPDATE OR REPLACE ... (other index conflicts)
# 7.3.4 - UPDATE OR REPLACE ... (both rowid and other index conflicts)
#
# 7.4.1 - Test that the pre-update-hook is invoked only once if a row being
# deleted is removed by a BEFORE trigger.
#
# 7.4.2 - Test that the pre-update-hook is invoked if a BEFORE trigger
# removes a row being updated. In this case the update hook should
# be invoked with SQLITE_INSERT as the opcode when inserting the
# new version of the row.
#
# TODO: Short records (those created before a column is added to a table
# using ALTER TABLE)
#
proc do_preupdate_test {tn sql x} {
set X [list]
foreach elem $x {lappend X $elem}
uplevel do_test $tn [list "
set ::preupdate \[list\]
execsql { $sql }
set ::preupdate
"] [list $X]
}
proc preupdate_hook {args} {
set type [lindex $args 0]
eval lappend ::preupdate $args
if {$type != "INSERT"} {
for {set i 0} {$i < [db preupdate count]} {incr i} {
lappend ::preupdate [db preupdate old $i]
}
}
if {$type != "DELETE"} {
for {set i 0} {$i < [db preupdate count]} {incr i} {
set rc [catch { db preupdate new $i } v]
lappend ::preupdate $v
}
}
}
db close
forcedelete test.db
sqlite3 db test.db
db preupdate hook preupdate_hook
# Set up a schema to use for tests 7.1.* to 7.3.*.
do_execsql_test 7.0 {
CREATE TABLE t1(a, b);
CREATE TABLE t2(x, y);
CREATE TABLE t3(i, j, UNIQUE(i));
INSERT INTO t2 VALUES('a', 'b');
INSERT INTO t2 VALUES('c', 'd');
INSERT INTO t3 VALUES(4, 16);
INSERT INTO t3 VALUES(5, 25);
INSERT INTO t3 VALUES(6, 36);
}
do_preupdate_test 7.1.1 {
INSERT INTO t1 VALUES('x', 'y')
} {INSERT main t1 1 1 x y}
# 7.1.2.1 does not use the xfer optimization. 7.1.2.2 does.
do_preupdate_test 7.1.2.1 {
INSERT INTO t1 SELECT y, x FROM t2;
} {INSERT main t1 2 2 b a INSERT main t1 3 3 d c}
do_preupdate_test 7.1.2.2 {
INSERT INTO t1 SELECT * FROM t2;
} {INSERT main t1 4 4 a b INSERT main t1 5 5 c d}
do_preupdate_test 7.1.3 {
REPLACE INTO t1(rowid, a, b) VALUES(1, 1, 1);
} {
DELETE main t1 1 1 x y
INSERT main t1 1 1 1 1
}
do_preupdate_test 7.1.4 {
REPLACE INTO t3 VALUES(4, NULL);
} {
DELETE main t3 1 1 4 16
INSERT main t3 4 4 4 {}
}
do_preupdate_test 7.1.5 {
REPLACE INTO t3(rowid, i, j) VALUES(2, 6, NULL);
} {
DELETE main t3 2 2 5 25
DELETE main t3 3 3 6 36
INSERT main t3 2 2 6 {}
}
do_execsql_test 7.2.0 { SELECT rowid FROM t1 } {1 2 3 4 5}
do_preupdate_test 7.2.1 {
DELETE FROM t1 WHERE rowid = 3
} {
DELETE main t1 3 3 d c
}
do_preupdate_test 7.2.2 {
DELETE FROM t1
} {
DELETE main t1 1 1 1 1
DELETE main t1 2 2 b a
DELETE main t1 4 4 a b
DELETE main t1 5 5 c d
}
do_execsql_test 7.3.0 {
DELETE FROM t1;
DELETE FROM t2;
DELETE FROM t3;
INSERT INTO t2 VALUES('a', 'b');
INSERT INTO t2 VALUES('c', 'd');
INSERT INTO t3 VALUES(4, 16);
INSERT INTO t3 VALUES(5, 25);
INSERT INTO t3 VALUES(6, 36);
}
do_preupdate_test 7.3.1 {
UPDATE t2 SET y = y||y;
} {
UPDATE main t2 1 1 a b a bb
UPDATE main t2 2 2 c d c dd
}
do_preupdate_test 7.3.2 {
UPDATE t2 SET rowid = rowid-1;
} {
UPDATE main t2 1 0 a bb a bb
UPDATE main t2 2 1 c dd c dd
}
do_preupdate_test 7.3.3 {
UPDATE OR REPLACE t2 SET rowid = 1 WHERE x = 'a'
} {
DELETE main t2 1 1 c dd
UPDATE main t2 0 1 a bb a bb
}
do_preupdate_test 7.3.4.1 {
UPDATE OR REPLACE t3 SET i = 5 WHERE i = 6
} {
DELETE main t3 2 2 5 25
UPDATE main t3 3 3 6 36 5 36
}
do_execsql_test 7.3.4.2 {
INSERT INTO t3 VALUES(10, 100);
SELECT rowid, * FROM t3;
} {1 4 16 3 5 36 4 10 100}
do_preupdate_test 7.3.5 {
UPDATE OR REPLACE t3 SET rowid = 1, i = 5 WHERE j = 100;
} {
DELETE main t3 1 1 4 16
DELETE main t3 3 3 5 36
UPDATE main t3 4 1 10 100 5 100
}
do_execsql_test 7.4.1.0 {
CREATE TABLE t4(a, b);
INSERT INTO t4 VALUES('a', 1);
INSERT INTO t4 VALUES('b', 2);
INSERT INTO t4 VALUES('c', 3);
CREATE TRIGGER t4t BEFORE DELETE ON t4 BEGIN
DELETE FROM t4 WHERE b = 1;
END;
}
do_preupdate_test 7.4.1.1 {
DELETE FROM t4 WHERE b = 3
} {
DELETE main t4 1 1 a 1
DELETE main t4 3 3 c 3
}
do_execsql_test 7.4.1.2 {
INSERT INTO t4(rowid, a, b) VALUES(1, 'a', 1);
INSERT INTO t4(rowid, a, b) VALUES(3, 'c', 3);
}
do_preupdate_test 7.4.1.3 {
DELETE FROM t4 WHERE b = 1
} {
DELETE main t4 1 1 a 1
}
do_execsql_test 7.4.2.0 {
CREATE TABLE t5(a, b);
INSERT INTO t5 VALUES('a', 1);
INSERT INTO t5 VALUES('b', 2);
INSERT INTO t5 VALUES('c', 3);
CREATE TRIGGER t5t BEFORE UPDATE ON t5 BEGIN
DELETE FROM t5 WHERE b = 1;
END;
}
do_preupdate_test 7.4.2.1 {
UPDATE t5 SET b = 4 WHERE a = 'c'
} {
DELETE main t5 1 1 a 1
UPDATE main t5 3 3 c 3 c 4
}
do_execsql_test 7.4.2.2 {
INSERT INTO t5(rowid, a, b) VALUES(1, 'a', 1);
}
do_preupdate_test 7.4.2.3 {
UPDATE t5 SET b = 5 WHERE a = 'a'
} {
DELETE main t5 1 1 a 1
}
do_execsql_test 7.5.1.0 {
CREATE TABLE t7(a, b);
INSERT INTO t7 VALUES('one', 'two');
INSERT INTO t7 VALUES('three', 'four');
ALTER TABLE t7 ADD COLUMN c DEFAULT NULL;
}
do_preupdate_test 7.5.1.1 {
DELETE FROM t7 WHERE a = 'one'
} {
DELETE main t7 1 1 one two {}
}
do_preupdate_test 7.5.1.2 {
UPDATE t7 SET b = 'five'
} {
UPDATE main t7 2 2 three four {} three five {}
}
do_execsql_test 7.5.2.0 {
CREATE TABLE t8(a, b);
INSERT INTO t8 VALUES('one', 'two');
INSERT INTO t8 VALUES('three', 'four');
ALTER TABLE t8 ADD COLUMN c DEFAULT 'xxx';
}
ifcapable !session {
# At time of writing, these two are broken. They demonstrate that the
# sqlite3_preupdate_old() method does not handle the case where ALTER TABLE
# has been used to add a column with a default value other than NULL.
#
do_preupdate_test 7.5.2.1 {
DELETE FROM t8 WHERE a = 'one'
} {
DELETE main t8 1 1 one two xxx
}
do_preupdate_test 7.5.2.2 {
UPDATE t8 SET b = 'five'
} {
UPDATE main t8 2 2 three four xxx three five xxx
}
}
# This block of tests verifies that IPK values are correctly reported
# by the sqlite3_preupdate_old() and sqlite3_preupdate_new() functions.
#
do_execsql_test 7.6.1 { CREATE TABLE t9(a, b INTEGER PRIMARY KEY, c) }
do_preupdate_test 7.6.2 {
INSERT INTO t9 VALUES(1, 2, 3);
UPDATE t9 SET b = b+1, c = c+1;
DELETE FROM t9 WHERE a = 1;
} {
INSERT main t9 2 2 1 2 3
UPDATE main t9 2 3 1 2 3 1 3 4
DELETE main t9 3 3 1 3 4
}
#--------------------------------------------------------------------------
# Test that the sqlite3_preupdate_depth() API seems to work.
#
proc preupdate_hook {args} {
set type [lindex $args 0]
eval lappend ::preupdate $args
eval lappend ::preupdate [db preupdate depth]
if {$type != "INSERT"} {
for {set i 0} {$i < [db preupdate count]} {incr i} {
lappend ::preupdate [db preupdate old $i]
}
}
if {$type != "DELETE"} {
for {set i 0} {$i < [db preupdate count]} {incr i} {
set rc [catch { db preupdate new $i } v]
lappend ::preupdate $v
}
}
}
db close
forcedelete test.db
sqlite3 db test.db
db preupdate hook preupdate_hook
do_execsql_test 7.6.1 {
CREATE TABLE t1(x PRIMARY KEY);
CREATE TABLE t2(x PRIMARY KEY);
CREATE TABLE t3(x PRIMARY KEY);
CREATE TABLE t4(x PRIMARY KEY);
CREATE TRIGGER a AFTER INSERT ON t1 BEGIN INSERT INTO t2 VALUES(new.x); END;
CREATE TRIGGER b AFTER INSERT ON t2 BEGIN INSERT INTO t3 VALUES(new.x); END;
CREATE TRIGGER c AFTER INSERT ON t3 BEGIN INSERT INTO t4 VALUES(new.x); END;
CREATE TRIGGER d AFTER UPDATE ON t1 BEGIN UPDATE t2 SET x = new.x; END;
CREATE TRIGGER e AFTER UPDATE ON t2 BEGIN UPDATE t3 SET x = new.x; END;
CREATE TRIGGER f AFTER UPDATE ON t3 BEGIN UPDATE t4 SET x = new.x; END;
CREATE TRIGGER g AFTER DELETE ON t1 BEGIN DELETE FROM t2 WHERE 1; END;
CREATE TRIGGER h AFTER DELETE ON t2 BEGIN DELETE FROM t3 WHERE 1; END;
CREATE TRIGGER i AFTER DELETE ON t3 BEGIN DELETE FROM t4 WHERE 1; END;
}
do_preupdate_test 7.6.2 {
INSERT INTO t1 VALUES('xyz');
} {
INSERT main t1 1 1 0 xyz
INSERT main t2 1 1 1 xyz
INSERT main t3 1 1 2 xyz
INSERT main t4 1 1 3 xyz
}
do_preupdate_test 7.6.3 {
UPDATE t1 SET x = 'abc';
} {
UPDATE main t1 1 1 0 xyz abc
UPDATE main t2 1 1 1 xyz abc
UPDATE main t3 1 1 2 xyz abc
UPDATE main t4 1 1 3 xyz abc
}
do_preupdate_test 7.6.4 {
DELETE FROM t1 WHERE 1;
} {
DELETE main t1 1 1 0 abc
DELETE main t2 1 1 1 abc
DELETE main t3 1 1 2 abc
DELETE main t4 1 1 3 abc
}
do_execsql_test 7.6.5 {
DROP TRIGGER a; DROP TRIGGER b; DROP TRIGGER c;
DROP TRIGGER d; DROP TRIGGER e; DROP TRIGGER f;
DROP TRIGGER g; DROP TRIGGER h; DROP TRIGGER i;
CREATE TRIGGER a BEFORE INSERT ON t1 BEGIN INSERT INTO t2 VALUES(new.x); END;
CREATE TRIGGER b BEFORE INSERT ON t2 BEGIN INSERT INTO t3 VALUES(new.x); END;
CREATE TRIGGER c BEFORE INSERT ON t3 BEGIN INSERT INTO t4 VALUES(new.x); END;
CREATE TRIGGER d BEFORE UPDATE ON t1 BEGIN UPDATE t2 SET x = new.x; END;
CREATE TRIGGER e BEFORE UPDATE ON t2 BEGIN UPDATE t3 SET x = new.x; END;
CREATE TRIGGER f BEFORE UPDATE ON t3 BEGIN UPDATE t4 SET x = new.x; END;
CREATE TRIGGER g BEFORE DELETE ON t1 BEGIN DELETE FROM t2 WHERE 1; END;
CREATE TRIGGER h BEFORE DELETE ON t2 BEGIN DELETE FROM t3 WHERE 1; END;
CREATE TRIGGER i BEFORE DELETE ON t3 BEGIN DELETE FROM t4 WHERE 1; END;
}
do_preupdate_test 7.6.6 {
INSERT INTO t1 VALUES('xyz');
} {
INSERT main t4 1 1 3 xyz
INSERT main t3 1 1 2 xyz
INSERT main t2 1 1 1 xyz
INSERT main t1 1 1 0 xyz
}
do_preupdate_test 7.6.3 {
UPDATE t1 SET x = 'abc';
} {
UPDATE main t4 1 1 3 xyz abc
UPDATE main t3 1 1 2 xyz abc
UPDATE main t2 1 1 1 xyz abc
UPDATE main t1 1 1 0 xyz abc
}
do_preupdate_test 7.6.4 {
DELETE FROM t1 WHERE 1;
} {
DELETE main t4 1 1 3 abc
DELETE main t3 1 1 2 abc
DELETE main t2 1 1 1 abc
DELETE main t1 1 1 0 abc
}
finish_test
-6
View File
@@ -615,12 +615,6 @@ do_test in-13.14 {
}
} {}
do_test in-13.15 {
catchsql {
SELECT 0 WHERE (SELECT 0,0) OR (0 IN (1,2));
}
} {1 {only a single result allowed for a SELECT that is part of an expression}}
do_test in-13.X {
db nullvalue ""
+1 -1
View File
@@ -51,6 +51,6 @@ do_test index3-99.1 {
db close
catch { sqlite3 db test.db }
catchsql { DROP INDEX i1 }
} {1 {malformed database schema (t1)}}
} {1 {malformed database schema (t1) - near "nonsense": syntax error}}
finish_test
-5
View File
@@ -639,9 +639,4 @@ do_catchsql_test misc1-21.2 {
VALUES(0,0x0MATCH#0;
} {1 {near ";": syntax error}}
# 2015-04-15
do_execsql_test misc1-22.1 {
SELECT ""+3 FROM (SELECT ""+5);
} {3}
finish_test
-17
View File
@@ -278,23 +278,6 @@ do_test null-8.15 {
}
} {1}
do_execsql_test null-9.1 {
CREATE TABLE t5(a, b, c);
CREATE UNIQUE INDEX t5ab ON t5(a, b);
INSERT INTO t5 VALUES(1, NULL, 'one');
INSERT INTO t5 VALUES(1, NULL, 'i');
INSERT INTO t5 VALUES(NULL, 'x', 'two');
INSERT INTO t5 VALUES(NULL, 'x', 'ii');
}
do_execsql_test null-9.2 {
SELECT * FROM t5 WHERE a = 1 AND b IS NULL;
} {1 {} one 1 {} i}
do_execsql_test null-9.3 {
SELECT * FROM t5 WHERE a IS NULL AND b = 'x';
} {{} x two {} x ii}
finish_test
-32
View File
@@ -463,9 +463,6 @@ do_execsql_test 5.1 {
do_execsql_test 5.2 {
SELECT 5 UNION ALL SELECT 3 ORDER BY 1
} {3 5}
do_execsql_test 5.3 {
SELECT 986 AS x GROUP BY X ORDER BY X
} {986}
# The following test (originally derived from a single test within fuzz.test)
# verifies that a PseudoTable cursor is not closed prematurely in a deeply
@@ -498,34 +495,5 @@ do_execsql_test 7.0 {
SELECT * FROM t7 WHERE a=?1 ORDER BY rowid;
} {~/ORDER BY/}
#-------------------------------------------------------------------------
# Test a partial sort large enough to cause the sorter to spill data
# to disk.
#
reset_db
do_execsql_test 8.0 {
PRAGMA cache_size = 5;
CREATE TABLE t1(a, b);
CREATE INDEX i1 ON t1(a);
}
do_eqp_test 8.1 {
SELECT * FROM t1 ORDER BY a, b;
} {
0 0 0 {SCAN TABLE t1 USING INDEX i1}
0 0 0 {USE TEMP B-TREE FOR RIGHT PART OF ORDER BY}
}
do_execsql_test 8.2 {
WITH cnt(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM cnt WHERE i<10000
)
INSERT INTO t1 SELECT i%2, randomblob(500) FROM cnt;
}
do_test 8.3 {
db eval { SELECT * FROM t1 ORDER BY a, b } { incr res $a }
set res
} 5000
finish_test
+24 -1
View File
@@ -89,6 +89,9 @@ foreach f [glob $testdir/*.test] { lappend alltests [file tail $f] }
foreach f [glob -nocomplain $testdir/../ext/rtree/*.test] {
lappend alltests $f
}
foreach f [glob -nocomplain $testdir/../ext/session/*.test] {
lappend alltests $f
}
if {$::tcl_platform(platform)!="unix"} {
set alltests [test_set $alltests -exclude crash.test crash2.test]
@@ -96,7 +99,7 @@ if {$::tcl_platform(platform)!="unix"} {
set alltests [test_set $alltests -exclude {
all.test async.test quick.test veryquick.test
memleak.test permutations.test soak.test fts3.test
mallocAll.test rtree.test full.test
mallocAll.test rtree.test full.test session.test
}]
set allquicktests [test_set $alltests -exclude {
@@ -939,6 +942,26 @@ test_suite "rtree" -description {
All R-tree related tests. Provides coverage of source file rtree.c.
} -files [glob -nocomplain $::testdir/../ext/rtree/*.test]
test_suite "session" -description {
All session module related tests.
} -files [glob -nocomplain $::testdir/../ext/session/*.test]
test_suite "session_eec" -description {
All session module related tests with sqlite3_extended_result_codes() set.
} -files [
glob -nocomplain $::testdir/../ext/session/*.test
] -dbconfig {
sqlite3_extended_result_codes $::dbhandle 1
}
test_suite "session_strm" -description {
All session module related tests using the streaming APIs.
} -files [
glob -nocomplain $::testdir/../ext/session/*.test
] -dbconfig {
set ::sqlite3session_streams 1
}
test_suite "no_optimization" -description {
Run test scripts with optimizations disabled using the
sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS) interface.
-10
View File
@@ -752,16 +752,6 @@ do_test pragma-6.7 {
{3 four REAL 0 X'abcdef' 0} \
{4 five {} 0 CURRENT_TIME 0} \
]
do_test pragma-6.8 {
execsql {
CREATE TABLE t68(a,b,c,PRIMARY KEY(a,b,a,c));
PRAGMA table_info(t68);
}
} [concat \
{0 a {} 0 {} 1} \
{1 b {} 0 {} 2} \
{2 c {} 0 {} 4} \
]
} ;# ifcapable schema_pragmas
# Miscellaneous tests
#
-3
View File
@@ -58,9 +58,6 @@ do_execsql_test printf2-1.10 {
do_execsql_test printf2-1.11 {
SELECT printf('%lld%n',314159.2653,'hi');
} {314159}
do_execsql_test printf2-1.12 {
SELECT printf('%n',0);
} {{}}
# EVIDENCE-OF: R-17002-27534 The %z format is interchangeable with %s.
#
+9 -66
View File
@@ -14,7 +14,6 @@ optional) are:
--config CONFIGNAME (Run only CONFIGNAME)
--quick (Run "veryquick.test" only)
--veryquick (Run "make smoketest" only)
--msvc (Use MSVC as the compiler)
--buildonly (Just build testfixture - do not run)
--dryrun (Print what would have happened)
--info (Show diagnostic info)
@@ -23,9 +22,8 @@ The default value for --srcdir is the parent of the directory holding
this script.
The script determines the default value for --platform using the
$tcl_platform(os) and $tcl_platform(machine) variables. Supported
platforms are "Linux-x86", "Linux-x86_64", "Darwin-i386",
"Darwin-x86_64", "Windows NT-intel", and "Windows NT-amd64".
$tcl_platform(os) and $tcl_platform(machine) variables. Supported
platforms are "Linux-x86", "Linux-x86_64" and "Darwin-i386".
Every test begins with a fresh run of the configure script at the top
of the SQLite source tree.
@@ -240,10 +238,6 @@ array set ::Platforms [strip_comments {
"Default" "mptest fulltestonly"
"Have-Not" test
}
"Windows NT-amd64" {
"Default" "mptest fulltestonly"
"Have-Not" test
}
# The Failure-Detection platform runs various tests that deliberately
# fail. This is used as a test of this script to verify that this script
@@ -302,13 +296,6 @@ proc count_tests_and_errors {logfile rcVar errmsgVar} {
set errmsg $msg
}
}
if {[regexp {fatal error +(.*)} $line all msg]} {
incr ::NERRCASE
if {$rc==0} {
set rc 1
set errmsg $msg
}
}
if {[regexp {ERROR SUMMARY: (\d+) errors.*} $line all cnt] && $cnt>0} {
incr ::NERRCASE
if {$rc==0} {
@@ -327,13 +314,7 @@ proc count_tests_and_errors {logfile rcVar errmsgVar} {
}
}
close $fd
if {$::BUILDONLY} {
if {$rc==0} {
set errmsg "Build complete"
} else {
set errmsg "Build failed"
}
} elseif {!$seen} {
if {!$seen} {
set rc 1
set errmsg "Test did not complete"
if {[file readable core]} {
@@ -348,7 +329,7 @@ proc run_test_suite {name testtarget config} {
# CFLAGS. The makefile will pass OPTS to both gcc and lemon, but
# CFLAGS is only passed to gcc.
#
set cflags [expr {$::MSVC ? "-Zi" : "-g"}]
set cflags "-g"
set opts ""
set title ${name}($testtarget)
set configOpts ""
@@ -368,14 +349,7 @@ proc run_test_suite {name testtarget config} {
set cflags [join $cflags " "]
set opts [join $opts " "]
append opts " -DSQLITE_NO_SYNC=1"
# Some configurations already set HAVE_USLEEP; in that case, skip it.
#
if {![regexp { -DHAVE_USLEEP$} $opts]
&& ![regexp { -DHAVE_USLEEP[ =]+} $opts]} {
append opts " -DHAVE_USLEEP=1"
}
append opts " -DSQLITE_NO_SYNC=1 -DHAVE_USLEEP"
# Set the sub-directory to use.
#
@@ -416,10 +390,10 @@ proc run_test_suite {name testtarget config} {
if {$rc} {
puts " FAIL $tm"
incr ::NERR
if {$errmsg!=""} {puts " $errmsg"}
} else {
puts " Ok $tm"
}
if {$errmsg!=""} {puts " $errmsg"}
}
}
@@ -427,7 +401,6 @@ proc run_test_suite {name testtarget config} {
# the current platform, which may be Windows (via MinGW, etc).
#
proc configureCommand {opts} {
if {$::MSVC} return [list]; # This is not needed for MSVC.
set result [list trace_cmd exec]
if {$::tcl_platform(platform)=="windows"} {
lappend result sh
@@ -441,14 +414,7 @@ proc configureCommand {opts} {
# specified targets, compiler flags, and options.
#
proc makeCommand { targets cflags opts } {
set result [list trace_cmd exec]
if {$::MSVC} {
set nmakeDir [file nativename $::SRCDIR]
set nmakeFile [file join $nmakeDir Makefile.msc]
lappend result nmake /f $nmakeFile TOP=$nmakeDir clean
} else {
lappend result make clean
}
set result [list trace_cmd exec make clean]
foreach target $targets {
lappend result $target
}
@@ -477,7 +443,6 @@ proc trace_cmd {args} {
proc process_options {argv} {
set ::SRCDIR [file normalize [file dirname [file dirname $::argv0]]]
set ::QUICK 0
set ::MSVC 0
set ::BUILDONLY 0
set ::DRYRUN 0
set ::EXEC exec
@@ -511,10 +476,6 @@ proc process_options {argv} {
set config [lindex $argv $i]
}
-msvc {
set ::MSVC 1
}
-buildonly {
set ::BUILDONLY 1
}
@@ -533,7 +494,6 @@ proc process_options {argv} {
puts " --platform [list $platform]"
puts " --config [list $config]"
if {$::QUICK} {puts " --quick"}
if {$::MSVC} {puts " --msvc"}
if {$::BUILDONLY} {puts " --buildonly"}
if {$::DRYRUN} {puts " --dryrun"}
if {$::TRACE} {puts " --trace"}
@@ -547,15 +507,7 @@ proc process_options {argv} {
}
exit
}
-g {
if {$::MSVC} {
lappend ::EXTRACONFIG -Zi
} else {
lappend ::EXTRACONFIG [lindex $argv $i]
}
}
-g -
-D* -
-O* -
-enable-* -
@@ -595,7 +547,6 @@ proc process_options {argv} {
puts -nonewline "Flags:"
if {$::DRYRUN} {puts -nonewline " --dryrun"}
if {$::BUILDONLY} {puts -nonewline " --buildonly"}
if {$::MSVC} {puts -nonewline " --msvc"}
switch -- $::QUICK {
1 {puts -nonewline " --quick"}
2 {puts -nonewline " --veryquick"}
@@ -619,20 +570,12 @@ proc main {argv} {
set ::SQLITE_VERSION {}
set STARTTIME [clock seconds]
foreach {zConfig target} $::CONFIGLIST {
if {$::MSVC && ($zConfig eq "Sanitize" || "checksymbols" in $target
|| "valgrindtest" in $target)} {
puts "Skipping $zConfig / $target for MSVC..."
continue
}
if {$target ne "checksymbols"} {
switch -- $::QUICK {
1 {set target test}
2 {set target smoketest}
}
if {$::BUILDONLY} {
set target testfixture
if {$::MSVC} {append target .exe}
}
if {$::BUILDONLY} {set target testfixture}
}
set config_options [concat $::Configs($zConfig) $::EXTRACONFIG]
-12
View File
@@ -799,11 +799,6 @@ do_test select4-11.15 {
SELECT x FROM t2
}
} {1 {SELECTs to the left and right of UNION do not have the same number of result columns}}
do_test select4-11.16 {
catchsql {
INSERT INTO t2(rowid) VALUES(2) UNION SELECT 3,4 UNION SELECT 5,6 ORDER BY 1;
}
} {1 {SELECTs to the left and right of UNION do not have the same number of result columns}}
do_test select4-12.1 {
sqlite3 db2 :memory:
@@ -868,12 +863,5 @@ do_execsql_test select4-14.8 {
do_execsql_test select4-14.9 {
SELECT * FROM t14 UNION ALL VALUES(3,2,1),(2,3,1),(1,2,3),(2,1,3);
} {1 2 3 4 5 6 3 2 1 2 3 1 1 2 3 2 1 3}
do_execsql_test select4-14.10 {
SELECT (VALUES(1),(2),(3),(4))
} {1}
do_execsql_test select4-14.11 {
SELECT (SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4)
} {1}
finish_test
-5
View File
@@ -92,9 +92,4 @@ do_test selectE-2.2 {
}
} {}
do_catchsql_test selectE-3.1 {
SELECT 1 EXCEPT SELECT 2 ORDER BY 1 COLLATE nocase EXCEPT SELECT 3;
} {1 {ORDER BY clause should come after EXCEPT not before}}
finish_test
+22
View File
@@ -0,0 +1,22 @@
# 2008 June 23
#
# 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 runs all rtree related tests.
#
set testdir [file dirname $argv0]
source $testdir/permutations.test
ifcapable session {
# First run tests with sqlite3_extended_error_codes() set, then
# again with it clear.
run_test_suite session_eec
run_test_suite session
run_test_suite session_strm
}
finish_test
+1 -101
View File
@@ -738,9 +738,6 @@ do_test shell1-4.1 {
PRAGMA encoding=UTF16;
CREATE TABLE t1(x);
INSERT INTO t1 VALUES(null), (''), (1), (2.25), ('hello'), (x'807f');
CREATE TABLE t3(x,y);
INSERT INTO t3 VALUES(1,null), (2,''), (3,1),
(4,2.25), (5,'hello'), (6,x'807f');
}
catchcmd test.db {.dump}
} {0 {PRAGMA foreign_keys=OFF;
@@ -752,18 +749,11 @@ INSERT INTO "t1" VALUES(1);
INSERT INTO "t1" VALUES(2.25);
INSERT INTO "t1" VALUES('hello');
INSERT INTO "t1" VALUES(X'807F');
CREATE TABLE t3(x,y);
INSERT INTO "t3" VALUES(1,NULL);
INSERT INTO "t3" VALUES(2,'');
INSERT INTO "t3" VALUES(3,1);
INSERT INTO "t3" VALUES(4,2.25);
INSERT INTO "t3" VALUES(5,'hello');
INSERT INTO "t3" VALUES(6,X'807F');
COMMIT;}}
# Test the output of ".mode insert"
#
do_test shell1-4.2.1 {
do_test shell1-4.2 {
catchcmd test.db ".mode insert t1\nselect * from t1;"
} {0 {INSERT INTO t1 VALUES(NULL);
INSERT INTO t1 VALUES('');
@@ -772,39 +762,6 @@ INSERT INTO t1 VALUES(2.25);
INSERT INTO t1 VALUES('hello');
INSERT INTO t1 VALUES(X'807f');}}
# Test the output of ".mode insert" with headers
#
do_test shell1-4.2.2 {
catchcmd test.db ".mode insert t1\n.headers on\nselect * from t1;"
} {0 {INSERT INTO t1(x) VALUES(NULL);
INSERT INTO t1(x) VALUES('');
INSERT INTO t1(x) VALUES(1);
INSERT INTO t1(x) VALUES(2.25);
INSERT INTO t1(x) VALUES('hello');
INSERT INTO t1(x) VALUES(X'807f');}}
# Test the output of ".mode insert"
#
do_test shell1-4.2.3 {
catchcmd test.db ".mode insert t3\nselect * from t3;"
} {0 {INSERT INTO t3 VALUES(1,NULL);
INSERT INTO t3 VALUES(2,'');
INSERT INTO t3 VALUES(3,1);
INSERT INTO t3 VALUES(4,2.25);
INSERT INTO t3 VALUES(5,'hello');
INSERT INTO t3 VALUES(6,X'807f');}}
# Test the output of ".mode insert" with headers
#
do_test shell1-4.2.4 {
catchcmd test.db ".mode insert t3\n.headers on\nselect * from t3;"
} {0 {INSERT INTO t3(x,y) VALUES(1,NULL);
INSERT INTO t3(x,y) VALUES(2,'');
INSERT INTO t3(x,y) VALUES(3,1);
INSERT INTO t3(x,y) VALUES(4,2.25);
INSERT INTO t3(x,y) VALUES(5,'hello');
INSERT INTO t3(x,y) VALUES(6,X'807f');}}
# Test the output of ".mode tcl"
#
do_test shell1-4.3 {
@@ -861,61 +818,4 @@ do_test shell1-4.6 {
";"
"$"} 7}
# Test using arbitrary byte data with the shell via standard input/output.
#
do_test shell1-5.0 {
#
# NOTE: Skip NUL byte because it appears to be incompatible with command
# shell argument parsing.
#
for {set i 1} {$i < 256} {incr i} {
#
# NOTE: Due to how the Tcl [exec] command works (i.e. where it treats
# command channels opened for it as textual ones), the carriage
# return character (and on Windows, the end-of-file character)
# cannot be used here.
#
if {$i==0x0D || ($tcl_platform(platform)=="windows" && $i==0x1A)} {
continue
}
set hex [format %02X $i]
set char [subst \\x$hex]; set oldChar $char
set escapes [list]
if {$tcl_platform(platform)=="windows"} {
#
# NOTE: On Windows, we need to escape all the whitespace characters,
# the alarm (\a) character, and those with special meaning to
# the SQLite shell itself.
#
set escapes [list \
\a \\a \b \\b \t \\t \n \\n \v \\v \f \\f \r \\r \
" " "\" \"" \" \\\" ' \"'\" \\ \\\\]
} else {
#
# NOTE: On Unix, we need to escape most of the whitespace characters
# and those with special meaning to the SQLite shell itself.
# The alarm (\a), backspace (\b), and carriage-return (\r)
# characters do not appear to require escaping on Unix. For
# the alarm and backspace characters, this is probably due to
# differences in the command shell. For the carriage-return,
# it is probably due to differences in how Tcl handles command
# channel end-of-line translations.
#
set escapes [list \
\t \\t \n \\n \v \\v \f \\f \
" " "\" \"" \" \\\" ' \"'\" \\ \\\\]
}
set char [string map $escapes $char]
set x [catchcmdex test.db ".print $char\n"]
set code [lindex $x 0]
set res [lindex $x 1]
if {$code ne "0"} {
error "failed with error: $res"
}
if {$res ne "$oldChar\n"} {
error "failed with byte $hex mismatch"
}
}
} {}
finish_test
+1 -1
View File
@@ -35,7 +35,7 @@ do_test tcl-1.1 {
do_test tcl-1.2 {
set v [catch {db bogus} msg]
lappend v $msg
} {1 {bad option "bogus": must be authorizer, backup, busy, cache, changes, close, collate, collation_needed, commit_hook, complete, copy, enable_load_extension, errorcode, eval, exists, function, incrblob, interrupt, last_insert_rowid, nullvalue, onecolumn, profile, progress, rekey, restore, rollback_hook, status, timeout, total_changes, trace, transaction, unlock_notify, update_hook, version, or wal_hook}}
} {1 {bad option "bogus": must be authorizer, backup, busy, cache, changes, close, collate, collation_needed, commit_hook, complete, copy, enable_load_extension, errorcode, eval, exists, function, incrblob, interrupt, last_insert_rowid, nullvalue, onecolumn, preupdate, profile, progress, rekey, restore, rollback_hook, status, timeout, total_changes, trace, transaction, unlock_notify, update_hook, version, or wal_hook}}
do_test tcl-1.2.1 {
set v [catch {db cache bogus} msg]
lappend v $msg
+6 -33
View File
@@ -666,15 +666,6 @@ proc do_test {name cmd expected} {
flush stdout
}
proc dumpbytes {s} {
set r ""
for {set i 0} {$i < [string length $s]} {incr i} {
if {$i > 0} {append r " "}
append r [format %02X [scan [string index $s $i] %c]]
}
return $r
}
proc catchcmd {db {cmd ""}} {
global CLI
set out [open cmds.txt w]
@@ -685,30 +676,6 @@ proc catchcmd {db {cmd ""}} {
list $rc $msg
}
proc catchcmdex {db {cmd ""}} {
global CLI
set out [open cmds.txt w]
fconfigure $out -encoding binary -translation binary
puts -nonewline $out $cmd
close $out
set line "exec -keepnewline -- $CLI $db < cmds.txt"
set chans [list stdin stdout stderr]
foreach chan $chans {
catch {
set modes($chan) [fconfigure $chan]
fconfigure $chan -encoding binary -translation binary -buffering none
}
}
set rc [catch { eval $line } msg]
foreach chan $chans {
catch {
eval fconfigure [list $chan] $modes($chan)
}
}
# puts [dumpbytes $msg]
list $rc $msg
}
proc filepath_normalize {p} {
# test cases should be written to assume "unix"-like file paths
if {$::tcl_platform(platform)!="unix"} {
@@ -743,6 +710,12 @@ proc fix_testname {varname} {
}
}
proc normalize_list {L} {
set L2 [list]
foreach l $L {lappend L2 $l}
set L2
}
proc do_execsql_test {testname sql {result {}}} {
fix_testname testname
uplevel do_test [list $testname] [list "execsql {$sql}"] [list [list {*}$result]]
+1 -1
View File
@@ -114,6 +114,6 @@ do_test trigger7-99.1 {
db close
catch { sqlite3 db test.db }
catchsql { DROP TRIGGER t2r5 }
} {1 {malformed database schema (t2r12)}}
} {1 {malformed database schema (t2r12) - near "nonsense": syntax error}}
finish_test
-20
View File
@@ -15,7 +15,6 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix vacuum2
# Do not use a codec for tests in this file, as the database file is
# manipulated directly using tcl scripts (using the [hexio_write] command).
@@ -228,24 +227,5 @@ do_test vacuum2-5.4 {
lappend res2 $res
} {1 2 3 4 5 6 7 8 9 10 {1 {cannot VACUUM - SQL statements in progress}}}
#-------------------------------------------------------------------------
# Check that if the definition of a collation sequence is changed and
# VACUUM run, records are store in the (new) correct order following the
# VACUUM. Even if the modified collation is attached to a PK of a WITHOUT
# ROWID table.
proc cmp {lhs rhs} { string compare $lhs $rhs }
db collate cmp cmp
do_execsql_test 6.0 {
CREATE TABLE t6(x PRIMARY KEY COLLATE cmp, y) WITHOUT ROWID;
CREATE INDEX t6y ON t6(y);
INSERT INTO t6 VALUES('i', 'one');
INSERT INTO t6 VALUES('ii', 'one');
INSERT INTO t6 VALUES('iii', 'one');
}
integrity_check 6.1
proc cmp {lhs rhs} { string compare $rhs $lhs }
do_execsql_test 6.2 VACUUM
integrity_check 6.3
finish_test
+7 -7
View File
@@ -129,11 +129,11 @@ do_execsql_test 3.0 {
do_searchcount_test 3.1 {
SELECT a, b FROM t3 WHERE (a=1 AND b='one') OR (a=2 AND b='two')
} {1 one 2 two search 4}
} {1 one 2 two search 2}
do_searchcount_test 3.2 {
SELECT a, c FROM t3 WHERE (a=1 AND b='one') OR (a=2 AND b='two')
} {1 i 2 ii search 6}
} {1 i 2 ii search 4}
do_searchcount_test 3.4.1 {
SELECT y FROM t4 WHERE x='a'
@@ -142,24 +142,24 @@ do_searchcount_test 3.4.2 {
SELECT a, b FROM t3 WHERE
(a=1 AND b=(SELECT y FROM t4 WHERE x='a'))
OR (a=2 AND b='two')
} {1 one 2 two search 6}
} {1 one 2 two search 4}
do_searchcount_test 3.4.3 {
SELECT a, b FROM t3 WHERE
(a=2 AND b='two')
OR (a=1 AND b=(SELECT y FROM t4 WHERE x='a'))
} {2 two 1 one search 6}
} {2 two 1 one search 4}
do_searchcount_test 3.4.4 {
SELECT a, b FROM t3 WHERE
(a=2 AND b=(SELECT y FROM t4 WHERE x='b'))
OR (a=1 AND b=(SELECT y FROM t4 WHERE x='a'))
} {2 two 1 one search 8}
} {2 two 1 one search 6}
do_searchcount_test 3.5.1 {
SELECT a, b FROM t3 WHERE (a=1 AND b='one') OR rowid=4
} {1 one 2 two search 3}
} {1 one 2 two search 2}
do_searchcount_test 3.5.2 {
SELECT a, c FROM t3 WHERE (a=1 AND b='one') OR rowid=4
} {1 i 2 ii search 3}
} {1 i 2 ii search 2}
# Ticket [d02e1406a58ea02d] (2012-10-04)
# LEFT JOIN with an OR in the ON clause causes segfault
-6
View File
@@ -843,10 +843,4 @@ do_catchsql_test 13.3 {
SELECT i FROM c;
} {1 {table c has 1 values for 2 columns}}
# 2015-04-12
#
do_execsql_test 14.1 {
WITH x AS (SELECT * FROM t) SELECT 0 EXCEPT SELECT 0 ORDER BY 1 COLLATE binary;
} {}
finish_test
-9
View File
@@ -255,14 +255,5 @@ do_test zeroblob-9.8 {
db eval {SELECT zeroblob(2) IN (zeroblob(2))}
} {1}
# Oversized zeroblob records
#
do_test zeroblob-10.1 {
db eval {
CREATE TABLE t10(a,b,c);
}
catchsql {INSERT INTO t10 VALUES(zeroblob(1e9),zeroblob(1e9),zeroblob(1e9))}
} {1 {string or blob too big}}
finish_test
+5 -1
View File
@@ -109,6 +109,7 @@ foreach hdr {
pcache.h
pragma.h
rtree.h
sqlite3session.h
sqlite3ext.h
sqlite3.h
sqliteicu.h
@@ -123,6 +124,7 @@ foreach hdr {
set available_hdr($hdr) 1
}
set available_hdr(sqliteInt.h) 0
set available_hdr(sqlite3session.h) 0
# These headers should be copied into the amalgamation without modifying any
# of their function declarations or definitions.
@@ -212,7 +214,7 @@ proc copy_file {filename} {
regsub {^SQLITE_API } $line {} line
# Add the SQLITE_PRIVATE or SQLITE_API keyword before functions.
# so that linkage can be modified at compile-time.
if {[regexp {^sqlite3_} $funcname]} {
if {[regexp {^sqlite3[a-z]*_} $funcname]} {
set line SQLITE_API
append line " " [string trim $rettype]
if {[string index $rettype end] ne "*"} {
@@ -366,6 +368,8 @@ foreach file {
rtree.c
icu.c
fts3_icu.c
sqlite3session.c
} {
copy_file tsrc/$file
}
+7
View File
@@ -71,6 +71,7 @@ fconfigure stdout -translation lf
set filelist [subst {
$TOP/src/sqlite.h.in
$TOP/ext/rtree/sqlite3rtree.h
$TOP/ext/session/sqlite3session.h
}]
# These are the functions that accept a variable number of arguments. They
@@ -90,6 +91,9 @@ set cdecllist {
#
foreach file $filelist {
set in [open $file]
if {![regexp {sqlite\.h\.in} $file]} {
puts "/******** Begin file [file tail $file] *********/"
}
while {![eof $in]} {
set line [gets $in]
@@ -123,4 +127,7 @@ foreach file $filelist {
puts $line
}
close $in
if {![regexp {sqlite\.h\.in} $file]} {
puts "/******** End of [file tail $file] *********/"
}
}
+2 -4
View File
@@ -64,13 +64,11 @@ static void out_of_memory(void){
*/
static unsigned char *getContent(int ofst, int nByte){
unsigned char *aData;
int got;
aData = malloc(nByte+32);
if( aData==0 ) out_of_memory();
memset(aData, 0, nByte+32);
lseek(db, ofst, SEEK_SET);
got = read(db, aData, nByte);
if( got>0 && got<nByte ) memset(aData+got, 0, nByte-got);
if( read(db, aData, nByte)<nByte ) memset(aData, 0, nByte);
return aData;
}
@@ -983,7 +981,7 @@ int main(int argc, char **argv){
if( pagesize==0 ) pagesize = 1024;
printf("Pagesize: %d\n", pagesize);
fstat(db, &sbuf);
mxPage = (sbuf.st_size+pagesize-1)/pagesize;
mxPage = sbuf.st_size/pagesize;
printf("Available pages: 1..%d\n", mxPage);
if( argc==2 ){
int i;
-1217
View File
File diff suppressed because it is too large Load Diff

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