Compare commits

..

73 Commits

Author SHA1 Message Date
dan 5b156b2bb4 Fix a typo in csv01.test introduced by the previous commit.
FossilOrigin-Name: f38ff718278c8ff477d1c6379ced32167063de15f0666c8eac2e4dd30cbd975c
2017-08-10 19:12:04 +00:00
dan 09fc79bd6f Add extra tests to this branch.
FossilOrigin-Name: b0e3b88a792623f7761586d8bfac058ddf939f49944ff0c4376627ff920895db
2017-08-10 19:10:23 +00:00
drh 4dd176eaab Test case for writing to a WITHOUT ROWID virtual table. The TCLVAR virtual
table is modified to add a "fullname" column which is the primary key, and
to accept update operations against the primary key.

FossilOrigin-Name: 6997e00c3221f266f4d9187501d8a9e5bafb85551e88a744cdc8ffe3b75ec2a4
2017-08-10 17:53:11 +00:00
drh e3740f272b Experimental changes that allow a WITHOUT ROWID virtual table to be writable
as long as it has only a single-column PRIMARY KEY.

FossilOrigin-Name: ab9ee4c1e64c09c7130e385a23d043d78bad95dff5509c7adc9b992350a4a537
2017-08-10 15:19:39 +00:00
drh 6fa9375c01 Add a first draft of the "vtablog" extensions that implements a generic
virtual table useful for experimentation using the command-line shell.

FossilOrigin-Name: e49279e65169a939b6058a0960dc1fe09ce4ee2d78992a1969773cbc7ce1043b
2017-08-10 03:27:27 +00:00
drh 7524b613c5 Preserve the error code from xConnect or xCreate methods in virtual table
implementations when they are encountered during parsing.

FossilOrigin-Name: dcdf091388251292ff9939bdff920708320bc64dacfe0fa1878c5ffd11b679c9
2017-08-09 22:55:09 +00:00
drh 73e00cf056 Various bug fixes for the new LSM1 virtual table design.
FossilOrigin-Name: 94434a252f0f2b57f325fd8fb82534f20cc1340ff13076cd88deeb47740ef6a2
2017-08-09 19:27:24 +00:00
drh 2e27d28fec Work toward redesigning the interface to the LSM1 virtual table.
FossilOrigin-Name: 313df946668b943b0a9a9a91fd7bafa7212d05765c7714fa6c0de46aa9062a74
2017-08-09 18:40:35 +00:00
drh ca03bdaf6e The identifier "vsnprintf" appears to be a reserved word in recent versions
of XCode, so avoid using it as a structure field.

FossilOrigin-Name: 795eede331b832a53e886318466dedc12504f7181c1d7295af7935864a04b72c
2017-08-08 21:40:23 +00:00
drh b8313cc998 Fix a faulty signed/unsigned character comparison in the LIKE optimization
logic.

FossilOrigin-Name: f4a4b1497355c1b27d3d0770550fffcc3b2d2d51ab284101f19e8fc4264ee675
2017-08-08 21:30:43 +00:00
drh 4f57352937 Enhance the CSV virtual table extension so that it accepts the last row of
the CSV file even if the last row omits the closing \n, as long as the last
row has a full set of columns.

FossilOrigin-Name: 537e3be2e9503183799afffcd91defc751fea2c779e9b77b77f9485f7de5d170
2017-08-08 20:03:10 +00:00
dan b46dfdb81c Avoid casting a value larger than 2^31 to a (size_t) or (SIZE_T) on systems
where it is a 32-bit type.

FossilOrigin-Name: b26d7a1c7b5d59a2ceabc3716ccea32e26de729eb164a9c0e47f2d8f6ad3df37
2017-08-07 19:12:49 +00:00
mistachkin e35395a45a On Windows, avoid casting a value larger than 2^31 to a (SIZE_T) on systems where it is a 32-bit type.
FossilOrigin-Name: f08d63b413601b22726e8b96ff8eb779857321b9df30db0333f71e50ffb5077d
2017-08-07 19:06:54 +00:00
dan 089df50687 Fix a problem with handling SQLITE_FCNTL_MMAP_SIZE requests with a negative
parameter in os_unix.c.

FossilOrigin-Name: 4249fcf7b0c0233f9b3ba5139702738d5221c5309240e6e91dc139eff59471fe
2017-08-07 18:54:10 +00:00
dan 010a016ac9 Update bigmmap.test to account for builds that use
"-DSQLITE_MAX_MMAP_SIZE=<integer-constant>LL".

FossilOrigin-Name: 7c8b6f1cace42ee1182c252c8e66e5679f523f4248c27d38c075ae21e7c25374
2017-08-07 18:27:19 +00:00
dan 43c1e622cd Avoid casting a value larger than 2^31 to a (size_t) on systems where it
is a 32-bit type.

FossilOrigin-Name: 46c3085dcad6372ac20eff499e17fe11680fdf4adb9186bf8b12221a5047e485
2017-08-07 18:13:28 +00:00
mistachkin 54640de156 Fix an out-of-order test number.
FossilOrigin-Name: 38f30091f9b1e2c393396da7257f3487fa374e1ee6d610577291909768ff9626
2017-08-07 17:28:18 +00:00
dan 969012e534 Add new test file "bigmmap.test". For testing builds with
-DSQLITE_MAX_MMAP_SIZE > 2GB.

FossilOrigin-Name: 17447062799239ee978bedbf7fcc67f4c7d2cad2e82dcf9349a966fc8f67d390
2017-08-07 17:14:30 +00:00
dan 226744d11b Fix a segfault in swarmvtab that could occur if there was an error in the SQL
statement passed to the constructor. Add other test cases.

FossilOrigin-Name: 6ce8b7ca62fcf97875395fc1a989179309e0abb48d4465658ef0d871434ea057
2017-08-05 16:15:33 +00:00
drh eabb67fb61 Microoptimization in pcache1.c makes the code slightly smaller and faster.
FossilOrigin-Name: 422cd9f39403feeacd70133c7a147e23572c75d617ac564344f016ae6611162c
2017-08-05 15:49:03 +00:00
dan 7d90417733 Fix memory leak in swarmvtab.
FossilOrigin-Name: 002a9c7baccf3417c56ec7d16755ea32fdd70a654ffc75f2fc4624fd2b688aed
2017-08-05 15:30:46 +00:00
drh 1451778a88 Add the swarm virtual table to the existing union virtual table module.
FossilOrigin-Name: 0f6f6f03deaba2f4bdd168980647136e8ddff4907863b62b3056b8e4665599df
2017-08-04 20:27:13 +00:00
drh a5aed4b10b Add the optional non-found-callback to the swarm-vtab.
FossilOrigin-Name: a94e2f600bc766fb459418e674b842628ba21e27cf9942c00cd533507d7b35fe
2017-08-04 20:15:08 +00:00
dan d83e082524 Add further test cases for swarmvtab. And minor code changes.
FossilOrigin-Name: 0f82d3b9dd5bd2e34a984c78e4a4a87921cf3e15b01b611133378c0ea9901010
2017-08-04 17:39:13 +00:00
dan 0ff2217035 Add test cases and associated fixes for swarmvtab.
FossilOrigin-Name: 7ae20eac83fc053dc1bbc42501dd41f77445a6b9a33cfa42b899fc7a18c637ab
2017-08-04 16:16:32 +00:00
dan f12228fa23 Add SQLITE_API qualifiers to public API functions in header file sqlite3rbu.h.
FossilOrigin-Name: e11e6eb1d98518ba3f0361efa1c274084c5d6325b52e8470b6873e14e00c4eff
2017-08-04 08:24:52 +00:00
dan f5f0e43a6d Update script tool/mksqlite3c.tcl to avoid duplicating the SQLITE_API
qualifier when copying sqlite3rbu.h into the amalgamation.

FossilOrigin-Name: 61ab112f326a51d2d524a6371d8c93851ca2c6917414a802ac72d7cf5a2413fb
2017-08-04 08:23:33 +00:00
dan a64d1fbc23 Modify swarmvtab to use a separate database connection for each database file.
FossilOrigin-Name: 1f05ad29c3a540408470da7f8111f1319f961539c1a96b1a81abf1423af90f15
2017-08-03 20:13:00 +00:00
dan f48d4ef8bc Add SQLITE_API qualifiers to public API functions in header file sqlite3rbu.h.
FossilOrigin-Name: d8637badf63b90b650e9d6dc970c7e1ce46669ce8272f46f01fac9bd97c1b0fc
2017-08-03 15:50:10 +00:00
dan 0c2e5cfda5 Fix some problems in RBU test cases. Also update RBU source code to better
handle the trivial case where an RBU update is applied to a database zero
pages in size.

FossilOrigin-Name: 7676b39bc120ae23da0c6a14452eb99a58901ee45c3d43b8beae426f9e4dc0c3
2017-08-03 15:43:55 +00:00
drh 5859636fb1 Optimization to the comparison opcodes in the byte-code engine.
FossilOrigin-Name: 654935c7737f1a9e08fde9b220c543e86ff6e05910e2f08973a2f93ab2b3e028
2017-08-03 00:29:23 +00:00
drh a485ad191f In the KeyInfo object, refactor the nField and nXField elements into
nKeyField and nAllField, which are more useful and run a little faster.

FossilOrigin-Name: aea5990eab5e85f92df966aa641db2271c81052010ad2d80982475c4275a1284
2017-08-02 22:43:14 +00:00
dan 63331b1af3 Enhance the code in unionvtab.c to also provide the "swarmvtab" virtual table
module. There are still several problems on this branch.

FossilOrigin-Name: 03d94388d62fd0f1fae377d273bbd5561208adc34bd97f7ce27783b30a369fd7
2017-08-02 19:59:56 +00:00
drh 754ee285fa Avoid redundant calls to sqlite3ApiExit() in sqlite3_step().
FossilOrigin-Name: 527974d4caba8bce7c89a28ea04a573b14c558657c14d9ad3c64bf1e0884caf8
2017-08-02 19:04:37 +00:00
drh 59533aa5e0 Only attempt to invoke WAL callbacks when a transaction has committed.
FossilOrigin-Name: bcc6dacb9114df709ef1bde24264c2193d9e39fc7fab024d5ebfc6056033274c
2017-08-02 18:28:26 +00:00
drh 60172a5a93 Minor optimization on clearCell()
FossilOrigin-Name: f3c39c2986be08683c2af4df610bc12e3c6bc6bec265c94ce01b94a950723524
2017-08-02 18:27:50 +00:00
drh 85ef630fde Optimizations associated with error handling in btree cursors.
FossilOrigin-Name: 49ba54e26731ed371745d4bdd9dd1dfeb73357b6d206c85a4252ec866be971d0
2017-08-02 15:50:09 +00:00
drh 57498ae9cf Remove unused token codes.
FossilOrigin-Name: c45078c062f41f43c71965b2e5a97932ff79123eeb017ba853314987bebc2bca
2017-08-02 12:38:10 +00:00
drh 13aa88d956 Rearrange some of the added token codes for a very slight size decrease and
performance increase.

FossilOrigin-Name: cb3742f51f4b3aa0fd8dc373af771e93ddb4100787115705955f2008aef92745
2017-08-02 11:36:16 +00:00
drh 6a8700b97b Rearrange integer token values in the parser and logic in the
resolveP2Values() routine for a small size reduction and performance increase.

FossilOrigin-Name: 1cad2926ad88b83becab1326bd189d7bac8ba6d470b36ba5d29af5c9fb016014
2017-08-02 11:04:00 +00:00
drh a02860511d Show which opcodes are jumps in the comments when generating
the opcodes.h header file.

FossilOrigin-Name: eef643a369250f1acac4c01a9b3d29068a510e5bf7fa843d565df5e2523e4dd9
2017-08-02 03:21:52 +00:00
drh 59c435a015 Add the "%token" control to the lemon parser. Not currently used by SQLite.
FossilOrigin-Name: a6e4c5ae8f29bc2e7f2088426341254e9281d19db9dc9a14abc376d56dad4c4b
2017-08-02 03:21:11 +00:00
drh 0019881e08 Faster implementation of resolveP2Values().
FossilOrigin-Name: 82e46fe0d497f871e652a579f25e77de7ef05d56484418961a1296f65f19415e
2017-08-02 02:46:43 +00:00
drh 6903bf6d49 Slightly smaller and faster by allocating Parser objects on the stack.
FossilOrigin-Name: 436a89b91901851ce21bf0cb997291b48888c52788b904822083d8dfac32b84b
2017-08-01 20:59:41 +00:00
drh 86b40dfd33 Split the OP_Last opcode into OP_Last and OP_SeekEnd. Use OP_SeekEnd to
position a cursor prior to appending.  Ticket [cb91bf4290c211d].

FossilOrigin-Name: 3e02474c7bbe16891a7cfc8771cf72f64cd2c0692779037982d7d307512a4f23
2017-08-01 19:53:43 +00:00
drh b7673ede37 Take advantage of atomic-write capabilities in the F2FS filesystem when the
database is stored on such a filesystem.  This is a compile-time option
activated using SQLITE_ENABLE_BATCH_ATOMIC_WRITE.

FossilOrigin-Name: 24190b221f73472dafaead6de101b4debc2c91c1ca28d70b45a38df5bb61fb39
2017-08-01 14:16:15 +00:00
drh e9e1074d27 Allow ATTACH and DETACH inside of a transaction.
FossilOrigin-Name: 95e8f31658254dd2df3eeaae337aff0fe2125d170ae966c74f4fc70400e099b1
2017-08-01 00:20:34 +00:00
mistachkin 334d163e41 Recognize 'AMD64' as 'x64' in the TclKit batch tool for MSVC.
FossilOrigin-Name: 0c77935cf9949099d834ec51384c1d4dcdaf7b4422c859c9fce6d3cb3bde2645
2017-07-31 19:39:34 +00:00
mistachkin 5b54259873 Sync up the MSVC autoconf makefile.
FossilOrigin-Name: b3b89f4ff92ee4b76f467d4e70fb42267470aef77af645f1ce71f9b1a675aabb
2017-07-31 19:26:06 +00:00
drh ab2eca0965 Fix a windows-compatibility issue in the recently added schema6.test script.
FossilOrigin-Name: 14e6c302ec3bc0e012b3c01aabee2d32e81389e1a4d834d1c57dd60bfbc97f29
2017-07-31 17:51:49 +00:00
drh 93bbfbe539 Prevent OSSFuzz from using debugging pragmas that are disabled in default builds
and which generate lots of excess output.

FossilOrigin-Name: cdc6494c050d821908087e6fd9b1e44ba92d81f363494a80b54647f19e5675f7
2017-07-31 17:06:34 +00:00
drh c644980c4d Move the generation of output column names earlier, to right after
name resolution and before query transformations such as flattening. 
This prevents the names from getting mangled by query transformations, 
and obviates hacks in the query flattener that attempt to work around 
the name mangling. The resulting code is smaller and faster and gives
more consistent output. Fix to ticket [de3403bf5ae5f72ed].

FossilOrigin-Name: ade7ddf1998190b2b630715774963150d86bed3211b7fd600cbf3068427e1531
2017-07-31 16:42:46 +00:00
drh ea2844f153 Add the schema6.test module for demonstrating schemas that generate identical
and different content.

FossilOrigin-Name: ac1da06a829051d393ccb8bb986e78f5bd35b060687688f6b3661913b13c9a5a
2017-07-30 19:50:42 +00:00
drh df94966c8b Correctly handle an "INTEGER PRIMARY KEY UNIQUE" column in a WITHOUT ROWID
table.  This is a fix for ticket [bc115541132dad136], a problem discovered
by OSSFuzz.

FossilOrigin-Name: 5216bfb73f1a49bdd879d470de139bf46a212474eaf6f38ad2390536d66a2afd
2017-07-30 18:40:52 +00:00
drh 2fc2f92ed6 Disable the writecrash.test module on Windows.
FossilOrigin-Name: 4c0520d4df7473eb4cc764774df7d99bb96cf067ac224755e09f0df47fb2a810
2017-07-28 20:49:02 +00:00
drh 9e61104c68 More precise determination of when an in-memory journal needs to be
spilled to disk.

FossilOrigin-Name: 6a505bdd9347783d3d8105cb8185aa787801bad0c855b623cfd780fb406f0e7b
2017-07-28 18:16:14 +00:00
drh 344f763f72 Add new extended error codes for the atomic write file-controls.
FossilOrigin-Name: 94a2004756ffe027b3495be91366750135c294a9c460edc9a1b4b9422dcc33bd
2017-07-28 13:18:35 +00:00
dan 2912da0283 Add new test file test/atomic.test.
FossilOrigin-Name: f9213e4864d0542be5489e03cf40c47d9127a86bbbbbabd963aa08525d5dc97b
2017-07-28 11:05:33 +00:00
drh a66632d83a Add the SQLITE_ENABLE_BATCH_ATOMIC_WRITE macro to ctime.c
FossilOrigin-Name: 67bad7fb9b2fdb29b63308f22062444084dc28191a542e08076dc2e39caf6f62
2017-07-28 01:53:32 +00:00
dan 77b4f528fa Do not set device-capabilities flags SEQUENTIAL or SAFE_APPEND for f2fs
file-systems.

FossilOrigin-Name: 4477e60cd801dab7a8aec12c5bc5e81e774bedbfed38abbc8eb8b37336141a44
2017-07-27 18:34:00 +00:00
drh cf201488a5 Allow ATTACH and DETACH to occur inside of a transaction.
FossilOrigin-Name: ac1fd6beb6c804af5faf1e06a51177a8316007ff9e718c398bd7a24d2ecc4ed3
2017-07-26 18:26:44 +00:00
dan 33447e7793 Add a test for the outcome of a process crash within an xWrite VFS method
call.

FossilOrigin-Name: eb8718006cb23ba9304da5c30d19863d688495f0eaae3794c5ad870e481866f8
2017-07-22 20:12:31 +00:00
dan 4da30f8888 Add the "atomic-batch-write" permutation to permutations.test. This
permutation fails if not run on a file-system that supports
atomic-batch-writes.

FossilOrigin-Name: 9f1b83fae9c973eee80eefefe7bd3a1eb7bba8af4cd919d7a2ce911900dd9087
2017-07-22 16:58:47 +00:00
drh 2df9478f5f Keep batch-atomic-writes turned on for journal_mode=MEMORY, but turn them
off for synchronous=OFF.  Refuse to compile with both SQLITE_MMAP_READWRITE
and SQLITE_ENABLE_BATCH_ATOMIC_WRITE.  Fix up some comments in the commit
logic.

FossilOrigin-Name: 2e80e19e4faac30947ed56aa3601c45c758cafb27f84780df255fdbcdc9a6999
2017-07-22 16:32:33 +00:00
drh d76dba7eb5 Omit unused batch-atomic-write code if SQLITE_ENABLE_BATCH_ATOMIC_WRITE is
not defined.

FossilOrigin-Name: a89b62c4966cd297326e8712da40e0962d2328d7ba1f201d5abcec911eeef347
2017-07-22 16:00:34 +00:00
dan 9d70954094 Use ioctl(F2FS_IOC_GET_FEATURES) to determine whether or not atomic batch
writes are available.

FossilOrigin-Name: 532bbf1f2b1028db4e581c756533aa660e482d833caaed4eafb299ef9b584f3a
2017-07-21 21:06:24 +00:00
mistachkin 172861be76 Fix typo in comment. No changes to code.
FossilOrigin-Name: 65ec077ba63ab84ab8da91033adcf4a57bb9269ebcdd10f9e6c97c265512a80c
2017-07-21 20:29:06 +00:00
drh d080e3d7ac Additional documentation on the new VFS interface. No changes to code.
FossilOrigin-Name: 83077ec8b5804e6c709fa4f0b50afec9eae6d5b603cc4c3e8a408ed6b2bfaf28
2017-07-21 14:49:58 +00:00
drh 35270d2604 Add the documentation text for the new VFS interfaces used by batch-atomic
writes.

FossilOrigin-Name: 5e944eea5a32cb4d27a2d5ae283b9ce6fe08c1bc584256e8e8c78583c415eb2a
2017-07-20 21:18:49 +00:00
drh 94e32acb56 Transform two #ifdef statements into #if statements.
FossilOrigin-Name: f2c6b5845028ef25dcd17e23e11b93c9f7296c26c7a21718fdae8f4b48c4e1da
2017-07-20 21:01:53 +00:00
dan d67a97705c Split SQLITE_ENABLE_ATOMIC_WRITE into two options - the original and
SQLITE_ENABLE_BATCH_ATOMIC_WRITE.

FossilOrigin-Name: 7eb9bf2c5b42c39901fd571553c1f70aa5a9dac71fdc8e318b3063b928ad58f7
2017-07-20 21:00:03 +00:00
dan efe1697191 Add support for F2FS atomic writes. Untested at this point.
FossilOrigin-Name: 416973ede3bde8567d1f2699728f72352979e054ef988d1c1e1cfe4290f6f8b8
2017-07-20 19:49:14 +00:00
drh 466004d5a4 Add VFS interfaces needed to make use of batch atomic write capabilities
in the underlying filesystem.

FossilOrigin-Name: 929bc46b8be0111a7b6e3d6fe129f0cad869c6a551bd47569c122742597b9f1c
2017-07-19 11:20:32 +00:00
82 changed files with 3752 additions and 985 deletions
+1 -1
View File
@@ -1 +1 @@
3.20.1
3.21.0
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.69 for sqlite 3.20.1.
# Generated by GNU Autoconf 2.69 for sqlite 3.21.0.
#
#
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
@@ -726,8 +726,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='sqlite'
PACKAGE_TARNAME='sqlite'
PACKAGE_VERSION='3.20.1'
PACKAGE_STRING='sqlite 3.20.1'
PACKAGE_VERSION='3.21.0'
PACKAGE_STRING='sqlite 3.21.0'
PACKAGE_BUGREPORT=''
PACKAGE_URL=''
@@ -1463,7 +1463,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.20.1 to adapt to many kinds of systems.
\`configure' configures sqlite 3.21.0 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1528,7 +1528,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of sqlite 3.20.1:";;
short | recursive ) echo "Configuration of sqlite 3.21.0:";;
esac
cat <<\_ACEOF
@@ -1652,7 +1652,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
sqlite configure 3.20.1
sqlite configure 3.21.0
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2071,7 +2071,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.20.1, which was
It was created by sqlite $as_me 3.21.0, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -12151,7 +12151,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=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.20.1, which was
This file was extended by sqlite $as_me 3.21.0, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -12217,7 +12217,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
sqlite config.status 3.20.1
sqlite config.status 3.21.0
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
+493 -246
View File
@@ -12,6 +12,42 @@
**
** This file implements a simple virtual table wrapper around the LSM
** storage engine from SQLite4.
**
** USAGE
**
** CREATE VIRTUAL TABLE demo USING lsm1(filename,key,keytype,value1,...);
**
** The keytype must be one of: UINT, TEXT, BLOB. All keys must be of that
** one type. "UINT" means unsigned integer. The values may be any
** SQLite datatype.
**
** INTERNALS
**
** The key encoding for BLOB and TEXT is just a copy of the blob or text.
** UTF-8 is used for text. The key encoding for UINT is the variable-length
** integer format at https://sqlite.org/src4/doc/trunk/www/varint.wiki.
**
** The values are encoded as a single blob (since that is what lsm stores as
** its content). There is a "type integer" followed by "content" for each
** value, alternating back and forth. The content might be empty.
**
** TYPE1 CONTENT1 TYPE2 CONTENT2 TYPE3 CONTENT3 ....
**
** Each "type integer" is encoded as a variable-length integer in the
** format of the link above. Let the type integer be T. The actual
** datatype is an integer 0-5 equal to T%6. Values 1 through 5 correspond
** to SQLITE_INTEGER through SQLITE_NULL. The size of the content in bytes
** is T/6. Type value 0 means that the value is an integer whose actual
** values is T/6 and there is no content. The type-value-0 integer format
** only works for integers in the range of 0 through 40.
**
** There is no content for NULL or type-0 integers. For BLOB and TEXT
** values, the content is the blob data or the UTF-8 text data. For
** non-negative integers X, the content is a variable-length integer X*2.
** For negative integers Y, the content is varaible-length integer (1-Y)*2+1.
** For FLOAT values, the content is the IEEE754 floating point value in
** native byte-order. This means that FLOAT values will be corrupted when
** database file is moved between big-endian and little-endian machines.
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
@@ -22,14 +58,19 @@ SQLITE_EXTENSION_INIT1
/* Forward declaration of subclasses of virtual table objects */
typedef struct lsm1_vtab lsm1_vtab;
typedef struct lsm1_cursor lsm1_cursor;
typedef struct lsm1_vblob lsm1_vblob;
/* Primitive types */
typedef unsigned char u8;
typedef unsigned int u32;
typedef sqlite3_uint64 u64;
/* An open connection to an LSM table */
struct lsm1_vtab {
sqlite3_vtab base; /* Base class - must be first */
lsm_db *pDb; /* Open connection to the LSM table */
u8 keyType; /* SQLITE_BLOB, _TEXT, or _INTEGER */
u32 nVal; /* Number of value columns */
};
@@ -43,8 +84,82 @@ struct lsm1_cursor {
u8 isDesc; /* 0: scan forward. 1: scan reverse */
u8 atEof; /* True if the scan is complete */
u8 bUnique; /* True if no more than one row of output */
u8 *zData; /* Content of the current row */
u32 nData; /* Number of bytes in the current row */
u8 *aeType; /* Types for all column values */
u32 *aiOfst; /* Offsets to the various fields */
u32 *aiLen; /* Length of each field */
u8 *pKey2; /* Loop termination key, or NULL */
u32 nKey2; /* Length of the loop termination key */
};
/* An extensible buffer object.
**
** Content can be appended. Space to hold new content is automatically
** allocated.
*/
struct lsm1_vblob {
u8 *a; /* Space to hold content, from sqlite3_malloc64() */
u64 n; /* Bytes of space used */
u64 nAlloc; /* Bytes of space allocated */
u8 errNoMem; /* True if a memory allocation error has been seen */
};
#if defined(__GNUC__)
# define LSM1_NOINLINE __attribute__((noinline))
#elif defined(_MSC_VER) && _MSC_VER>=1310
# define LSM1_NOINLINE __declspec(noinline)
#else
# define LSM1_NOINLINE
#endif
/* Increase the available space in the vblob object so that it can hold
** at least N more bytes. Return the number of errors.
*/
static int lsm1VblobEnlarge(lsm1_vblob *p, u32 N){
if( p->n+N>p->nAlloc ){
if( p->errNoMem ) return 1;
p->nAlloc += N + (p->nAlloc ? p->nAlloc : N);
p->a = sqlite3_realloc64(p->a, p->nAlloc);
if( p->a==0 ){
p->n = 0;
p->nAlloc = 0;
p->errNoMem = 1;
return 1;
}
p->nAlloc = sqlite3_msize(p->a);
}
return 0;
}
/* Append N bytes to a vblob after first enlarging it */
static LSM1_NOINLINE void lsm1VblobEnlargeAndAppend(
lsm1_vblob *p,
const u8 *pData,
u32 N
){
if( p->n+N>p->nAlloc && lsm1VblobEnlarge(p, N) ) return;
memcpy(p->a+p->n, pData, N);
p->n += N;
}
/* Append N bytes to a vblob */
static void lsm1VblobAppend(lsm1_vblob *p, const u8 *pData, u32 N){
sqlite3_int64 n = p->n;
if( n+N>p->nAlloc ){
lsm1VblobEnlargeAndAppend(p, pData, N);
}else{
p->n += N;
memcpy(p->a+n, pData, N);
}
}
/* append text to a vblob */
static void lsm1VblobAppendText(lsm1_vblob *p, const char *z){
lsm1VblobAppend(p, (u8*)z, (u32)strlen(z));
}
/* Dequote the string */
static void lsm1Dequote(char *z){
int j;
@@ -76,9 +191,28 @@ static int lsm1Connect(
lsm1_vtab *pNew;
int rc;
char *zFilename;
u8 keyType = 0;
int i;
lsm1_vblob sql;
static const char *azTypes[] = { "UINT", "TEXT", "BLOB" };
static const u8 aeTypes[] = { SQLITE_INTEGER, SQLITE_TEXT, SQLITE_BLOB };
static const char *azArgName[] = {"filename", "key", "key type", "value1" };
if( argc!=4 || argv[3]==0 || argv[3][0]==0 ){
*pzErr = sqlite3_mprintf("filename argument missing");
for(i=0; i<sizeof(azArgName)/sizeof(azArgName[0]); i++){
if( argc<i+4 || argv[i+3]==0 || argv[i+3][0]==0 ){
*pzErr = sqlite3_mprintf("%s (%r) argument missing",
azArgName[i], i+1);
return SQLITE_ERROR;
}
}
for(i=0; i<sizeof(azTypes)/sizeof(azTypes[0]); i++){
if( sqlite3_stricmp(azTypes[i],argv[5])==0 ){
keyType = aeTypes[i];
break;
}
}
if( keyType==0 ){
*pzErr = sqlite3_mprintf("key type should be INT, TEXT, or BLOB");
return SQLITE_ERROR;
}
*ppVtab = sqlite3_malloc( sizeof(*pNew) );
@@ -87,6 +221,7 @@ static int lsm1Connect(
return SQLITE_NOMEM;
}
memset(pNew, 0, sizeof(*pNew));
pNew->keyType = keyType;
rc = lsm_new(0, &pNew->pDb);
if( rc ){
*pzErr = sqlite3_mprintf("lsm_new failed with error code %d", rc);
@@ -103,22 +238,26 @@ static int lsm1Connect(
goto connect_failed;
}
/* Column numbers */
#define LSM1_COLUMN_KEY 0
#define LSM1_COLUMN_BLOBKEY 1
#define LSM1_COLUMN_VALUE 2
#define LSM1_COLUMN_BLOBVALUE 3
#define LSM1_COLUMN_COMMAND 4
memset(&sql, 0, sizeof(sql));
lsm1VblobAppendText(&sql, "CREATE TABLE x(");
lsm1VblobAppendText(&sql, argv[4]);
lsm1VblobAppendText(&sql, " ");
lsm1VblobAppendText(&sql, argv[5]);
for(i=6; i<argc; i++){
lsm1VblobAppendText(&sql, ", ");
lsm1VblobAppendText(&sql, argv[i]);
pNew->nVal++;
}
lsm1VblobAppendText(&sql,
", lsm1_command HIDDEN, lsm1_key HIDDEN, lsm1_value HIDDEN)");
lsm1VblobAppend(&sql, (u8*)"", 1);
if( sql.errNoMem ){
rc = SQLITE_NOMEM;
goto connect_failed;
}
rc = sqlite3_declare_vtab(db, (const char*)sql.a);
sqlite3_free(sql.a);
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x("
" key," /* The primary key. Any non-NULL */
" blobkey," /* Pure BLOB primary key */
" value," /* The value associated with key. Any non-NULL */
" blobvalue," /* Pure BLOB value */
" command hidden" /* Insert here for control operations */
");"
);
connect_failed:
if( rc!=SQLITE_OK ){
if( pNew ){
@@ -147,9 +286,13 @@ static int lsm1Open(sqlite3_vtab *pVtab, sqlite3_vtab_cursor **ppCursor){
lsm1_vtab *p = (lsm1_vtab*)pVtab;
lsm1_cursor *pCur;
int rc;
pCur = sqlite3_malloc( sizeof(*pCur) );
pCur = sqlite3_malloc64( sizeof(*pCur)
+ p->nVal*(sizeof(pCur->aiOfst)+sizeof(pCur->aiLen)+1) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
pCur->aiOfst = (u32*)&pCur[1];
pCur->aiLen = &pCur->aiOfst[p->nVal];
pCur->aeType = (u8*)&pCur->aiLen[p->nVal];
*ppCursor = &pCur->base;
rc = lsm_csr_open(p->pDb, &pCur->pLsmCur);
if( rc==LSM_OK ){
@@ -167,6 +310,7 @@ static int lsm1Open(sqlite3_vtab *pVtab, sqlite3_vtab_cursor **ppCursor){
*/
static int lsm1Close(sqlite3_vtab_cursor *cur){
lsm1_cursor *pCur = (lsm1_cursor*)cur;
sqlite3_free(pCur->pKey2);
lsm_csr_close(pCur->pLsmCur);
sqlite3_free(pCur);
return SQLITE_OK;
@@ -190,6 +334,21 @@ static int lsm1Next(sqlite3_vtab_cursor *cur){
if( rc==LSM_OK && lsm_csr_valid(pCur->pLsmCur)==0 ){
pCur->atEof = 1;
}
if( pCur->pKey2 && pCur->atEof==0 ){
const u8 *pVal;
u32 nVal;
assert( pCur->isDesc==0 );
rc = lsm_csr_key(pCur->pLsmCur, (const void**)&pVal, (int*)&nVal);
if( rc==LSM_OK ){
u32 len = pCur->nKey2;
int c;
if( len>nVal ) len = nVal;
c = memcmp(pVal, pCur->pKey2, len);
if( c==0 ) c = nVal - pCur->nKey2;
if( c>0 ) pCur->atEof = 1;
}
}
pCur->zData = 0;
}
return rc==LSM_OK ? SQLITE_OK : SQLITE_ERROR;
}
@@ -295,6 +454,14 @@ static int lsm1PutVarint64(unsigned char *z, sqlite3_uint64 x){
return 9;
}
/* Append non-negative integer x as a variable-length integer.
*/
static void lsm1VblobAppendVarint(lsm1_vblob *p, sqlite3_uint64 x){
sqlite3_int64 n = p->n;
if( n+9>p->nAlloc && lsm1VblobEnlarge(p, 9) ) return;
p->n += lsm1PutVarint64(p->a+p->n, x);
}
/*
** Decode the varint in the first n bytes z[]. Write the integer value
** into *pResult and return the number of bytes in the varint.
@@ -349,69 +516,74 @@ static int lsm1GetVarint64(
return 9;
}
/*
** Generate a key encoding for pValue such that all keys compare in
** lexicographical order. Return an SQLite error code or SQLITE_OK.
/* Encoded a signed integer as a varint. Numbers close to zero uses fewer
** bytes than numbers far away from zero. However, the result is not in
** lexicographical order.
**
** The key encoding is *pnKey bytes in length written into *ppKey.
** Space to hold the key is taken from pSpace if sufficient, or else
** from sqlite3_malloc(). The caller is responsible for freeing malloced
** space.
** Encoding: Non-negative integer X is encoding as an unsigned
** varint X*2. Negative integer Y is encoding as an unsigned
** varint (1-Y)*2 + 1.
*/
static int lsm1EncodeKey(
sqlite3_value *pValue, /* Value to be encoded */
unsigned char **ppKey, /* Write the encoding here */
int *pnKey, /* Write the size of the encoding here */
unsigned char *pSpace, /* Use this space if it is large enough */
int nSpace /* Size of pSpace[] */
){
int eType = sqlite3_value_type(pValue);
*ppKey = 0;
*pnKey = 0;
assert( nSpace>=32 );
switch( eType ){
default: {
return SQLITE_ERROR; /* We cannot handle NULL keys */
}
case SQLITE_BLOB:
case SQLITE_TEXT: {
int nVal = sqlite3_value_bytes(pValue);
const void *pVal;
if( eType==SQLITE_BLOB ){
eType = LSM1_TYPE_BLOB;
pVal = sqlite3_value_blob(pValue);
}else{
eType = LSM1_TYPE_TEXT;
pVal = (const void*)sqlite3_value_text(pValue);
if( pVal==0 ) return SQLITE_NOMEM;
}
if( nVal+1>nSpace ){
pSpace = sqlite3_malloc( nVal+1 );
if( pSpace==0 ) return SQLITE_NOMEM;
}
pSpace[0] = (unsigned char)eType;
memcpy(&pSpace[1], pVal, nVal);
*ppKey = pSpace;
*pnKey = nVal+1;
break;
}
case SQLITE_INTEGER: {
sqlite3_int64 iVal = sqlite3_value_int64(pValue);
sqlite3_uint64 uVal;
if( iVal<0 ){
if( iVal==0xffffffffffffffffLL ) return SQLITE_ERROR;
uVal = *(sqlite3_uint64*)&iVal;
eType = LSM1_TYPE_NEGATIVE;
}else{
uVal = iVal;
eType = LSM1_TYPE_POSITIVE;
}
pSpace[0] = (unsigned char)eType;
*ppKey = pSpace;
*pnKey = 1 + lsm1PutVarint64(&pSpace[1], uVal);
}
static int lsm1PutSignedVarint64(u8 *z, sqlite3_int64 v){
sqlite3_uint64 u;
if( v>=0 ){
u = (sqlite3_uint64)v;
return lsm1PutVarint64(z, u*2);
}else{
u = (sqlite3_uint64)(-1-v);
return lsm1PutVarint64(z, u*2+1);
}
return SQLITE_OK;
}
/* Decoded a signed varint. */
static int lsm1GetSignedVarint64(
const unsigned char *z,
int n,
sqlite3_int64 *pResult
){
sqlite3_uint64 u = 0;
n = lsm1GetVarint64(z, n, &u);
if( u&1 ){
*pResult = -1 - (sqlite3_int64)(u>>1);
}else{
*pResult = (sqlite3_int64)(u>>1);
}
return n;
}
/*
** Read the value part of the key-value pair and decode it into columns.
*/
static int lsm1DecodeValues(lsm1_cursor *pCur){
lsm1_vtab *pTab = (lsm1_vtab*)(pCur->base.pVtab);
int i, n;
int rc;
u8 eType;
sqlite3_uint64 v;
if( pCur->zData ) return 1;
rc = lsm_csr_value(pCur->pLsmCur, (const void**)&pCur->zData,
(int*)&pCur->nData);
if( rc ) return 0;
for(i=n=0; i<pTab->nVal; i++){
v = 0;
n += lsm1GetVarint64(pCur->zData+n, pCur->nData-n, &v);
pCur->aeType[i] = eType = (u8)(v%6);
if( eType==0 ){
pCur->aiOfst[i] = (u32)(v/6);
pCur->aiLen[i] = 0;
}else{
pCur->aiOfst[i] = n;
n += (pCur->aiLen[i] = (u32)(v/6));
}
if( n>pCur->nData ) break;
}
if( i<pTab->nVal ){
pCur->zData = 0;
return 0;
}
return 1;
}
/*
@@ -424,83 +596,74 @@ static int lsm1Column(
int i /* Which column to return */
){
lsm1_cursor *pCur = (lsm1_cursor*)cur;
switch( i ){
case LSM1_COLUMN_BLOBKEY: {
lsm1_vtab *pTab = (lsm1_vtab*)(cur->pVtab);
if( i==0 ){
/* The key column */
const void *pVal;
int nVal;
if( lsm_csr_key(pCur->pLsmCur, &pVal, &nVal)==LSM_OK ){
if( pTab->keyType==SQLITE_BLOB ){
sqlite3_result_blob(ctx, pVal, nVal, SQLITE_TRANSIENT);
}else if( pTab->keyType==SQLITE_TEXT ){
sqlite3_result_text(ctx,(const char*)pVal, nVal, SQLITE_TRANSIENT);
}else{
const unsigned char *z = (const unsigned char*)pVal;
sqlite3_uint64 v1;
lsm1GetVarint64(z, nVal, &v1);
sqlite3_result_int64(ctx, (sqlite3_int64)v1);
}
}
}else if( i>pTab->nVal ){
if( i==pTab->nVal+2 ){ /* lsm1_key */
const void *pVal;
int nVal;
if( lsm_csr_key(pCur->pLsmCur, &pVal, &nVal)==LSM_OK ){
sqlite3_result_blob(ctx, pVal, nVal, SQLITE_TRANSIENT);
}
break;
}
case LSM1_COLUMN_KEY: {
const unsigned char *pVal;
int nVal;
if( lsm_csr_key(pCur->pLsmCur, (const void**)&pVal, &nVal)==LSM_OK
&& nVal>=1
){
if( pVal[0]==LSM1_TYPE_BLOB ){
sqlite3_result_blob(ctx, (const void*)&pVal[1],nVal-1,
SQLITE_TRANSIENT);
}else if( pVal[0]==LSM1_TYPE_TEXT ){
sqlite3_result_text(ctx, (const char*)&pVal[1],nVal-1,
SQLITE_TRANSIENT);
}else if( nVal>=2 && nVal<=10 &&
(pVal[0]==LSM1_TYPE_POSITIVE || pVal[0]==LSM1_TYPE_NEGATIVE)
){
sqlite3_int64 iVal;
lsm1GetVarint64(pVal+1, nVal-1, (sqlite3_uint64*)&iVal);
sqlite3_result_int64(ctx, iVal);
}
}
break;
}
case LSM1_COLUMN_BLOBVALUE: {
}else if( i==pTab->nVal+3 ){ /* lsm1_value */
const void *pVal;
int nVal;
if( lsm_csr_value(pCur->pLsmCur, (const void**)&pVal, &nVal)==LSM_OK ){
if( lsm_csr_value(pCur->pLsmCur, &pVal, &nVal)==LSM_OK ){
sqlite3_result_blob(ctx, pVal, nVal, SQLITE_TRANSIENT);
}
break;
}
case LSM1_COLUMN_VALUE: {
const unsigned char *aVal;
int nVal;
if( lsm_csr_value(pCur->pLsmCur, (const void**)&aVal, &nVal)==LSM_OK
&& nVal>=1
){
switch( aVal[0] ){
case SQLITE_FLOAT:
case SQLITE_INTEGER: {
sqlite3_uint64 x = 0;
int j;
for(j=1; j<nVal; j++){
x = (x<<8) | aVal[j];
}
if( aVal[0]==SQLITE_INTEGER ){
sqlite3_result_int64(ctx, *(sqlite3_int64*)&x);
}else{
double r;
assert( sizeof(r)==sizeof(x) );
memcpy(&r, &x, sizeof(r));
sqlite3_result_double(ctx, r);
}
break;
}
case SQLITE_TEXT: {
sqlite3_result_text(ctx, (char*)&aVal[1], nVal-1, SQLITE_TRANSIENT);
break;
}
case SQLITE_BLOB: {
sqlite3_result_blob(ctx, &aVal[1], nVal-1, SQLITE_TRANSIENT);
break;
}
}
}else if( lsm1DecodeValues(pCur) ){
/* The i-th value column (where leftmost is 1) */
const u8 *zData;
u32 nData;
i--;
zData = pCur->zData + pCur->aiOfst[i];
nData = pCur->aiLen[i];
switch( pCur->aeType[i] ){
case 0: { /* in-line integer */
sqlite3_result_int(ctx, pCur->aiOfst[i]);
break;
}
case SQLITE_INTEGER: {
sqlite3_int64 v;
lsm1GetSignedVarint64(zData, nData, &v);
sqlite3_result_int64(ctx, v);
break;
}
case SQLITE_FLOAT: {
double v;
if( nData==sizeof(v) ){
memcpy(&v, zData, sizeof(v));
sqlite3_result_double(ctx, v);
}
break;
}
case SQLITE_TEXT: {
sqlite3_result_text(ctx, (const char*)zData, nData, SQLITE_TRANSIENT);
break;
}
case SQLITE_BLOB: {
sqlite3_result_blob(ctx, zData, nData, SQLITE_TRANSIENT);
break;
}
default: {
/* A NULL. Do nothing */
}
break;
}
default: {
break;
}
}
return SQLITE_OK;
@@ -514,21 +677,85 @@ static int lsm1Filter(
int argc, sqlite3_value **argv
){
lsm1_cursor *pCur = (lsm1_cursor *)pVtabCursor;
lsm1_vtab *pTab = (lsm1_vtab*)(pCur->base.pVtab);
int rc = LSM_OK;
int seekType = -1;
const void *pVal = 0;
int nVal;
u8 keyType = pTab->keyType;
u8 aKey1[16];
pCur->atEof = 1;
if( idxNum==1 ){
assert( argc==1 );
pCur->isDesc = 0;
pCur->bUnique = 1;
if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){
const void *pVal = sqlite3_value_blob(argv[0]);
int nVal = sqlite3_value_bytes(argv[0]);
rc = lsm_csr_seek(pCur->pLsmCur, pVal, nVal, LSM_SEEK_EQ);
sqlite3_free(pCur->pKey2);
pCur->pKey2 = 0;
if( idxNum<99 ){
if( keyType==SQLITE_BLOB ){
pVal = sqlite3_value_blob(argv[0]);
nVal = sqlite3_value_bytes(argv[0]);
}else if( keyType==SQLITE_TEXT ){
pVal = sqlite3_value_text(argv[0]);
nVal = sqlite3_value_bytes(argv[0]);
}else{
sqlite3_int64 v = sqlite3_value_int64(argv[0]);
if( v<0 ) v = 0;
nVal = lsm1PutVarint64(aKey1, v);
pVal = aKey1;
}
}
switch( idxNum ){
case 0: { /* key==argv[0] */
assert( argc==1 );
seekType = LSM_SEEK_EQ;
pCur->isDesc = 0;
pCur->bUnique = 1;
break;
}
case 1: { /* key>=argv[0] AND key<=argv[1] */
u8 aKey[12];
seekType = LSM_SEEK_GE;
pCur->isDesc = 0;
pCur->bUnique = 0;
if( keyType==SQLITE_INTEGER ){
sqlite3_int64 v = sqlite3_value_int64(argv[1]);
if( v<0 ) v = 0;
pCur->nKey2 = lsm1PutVarint64(aKey, (sqlite3_uint64)v);
pCur->pKey2 = sqlite3_malloc( pCur->nKey2 );
if( pCur->pKey2==0 ) return SQLITE_NOMEM;
memcpy(pCur->pKey2, aKey, pCur->nKey2);
}else{
pCur->nKey2 = sqlite3_value_bytes(argv[1]);
pCur->pKey2 = sqlite3_malloc( pCur->nKey2 );
if( pCur->pKey2==0 ) return SQLITE_NOMEM;
if( keyType==SQLITE_BLOB ){
memcpy(pCur->pKey2, sqlite3_value_blob(argv[1]), pCur->nKey2);
}else{
memcpy(pCur->pKey2, sqlite3_value_text(argv[1]), pCur->nKey2);
}
}
break;
}
case 2: { /* key>=argv[0] */
seekType = LSM_SEEK_GE;
pCur->isDesc = 0;
pCur->bUnique = 0;
break;
}
case 3: { /* key<=argv[0] */
seekType = LSM_SEEK_LE;
pCur->isDesc = 1;
pCur->bUnique = 0;
break;
}
default: { /* full table scan */
pCur->isDesc = 0;
pCur->bUnique = 0;
break;
}
}
if( pVal ){
rc = lsm_csr_seek(pCur->pLsmCur, pVal, nVal, seekType);
}else{
rc = lsm_csr_first(pCur->pLsmCur);
pCur->isDesc = 0;
pCur->bUnique = 0;
}
if( rc==LSM_OK && lsm_csr_valid(pCur->pLsmCur)!=0 ){
pCur->atEof = 0;
@@ -540,59 +767,87 @@ static int lsm1Filter(
** Only comparisons against the key are allowed. The idxNum defines
** which comparisons are available:
**
** 0 Full table scan only
** bit 1 key==?1 single argument for ?1
** bit 2 key>?1
** bit 3 key>=?1
** bit 4 key<?N (N==1 if bits 2,3 clear, or 2 if bits2,3 set)
** bit 5 key<=?N (N==1 if bits 2,3 clear, or 2 if bits2,3 set)
** bit 6 Use blobkey instead of key
**
** To put it another way:
**
** 0 Full table scan.
** 1 key==?1
** 2 key>?1
** 4 key>=?1
** 8 key<?1
** 10 key>?1 AND key<?2
** 12 key>=?1 AND key<?2
** 16 key<=?1
** 18 key>?1 AND key<=?2
** 20 key>=?1 AND key<=?2
** 33..52 Use blobkey in place of key...
** 0 key==?1
** 1 key>=?1 AND key<=?2
** 2 key>?1 or key>=?1
** 3 key<?1 or key<=?1
** 99 Full table scan only
*/
static int lsm1BestIndex(
sqlite3_vtab *tab,
sqlite3_index_info *pIdxInfo
){
int i; /* Loop over constraints */
int idxNum = 0; /* The query plan bitmask */
int idxNum = 99; /* The query plan */
int nArg = 0; /* Number of arguments to xFilter */
int eqIdx = -1; /* Index of the key== constraint, or -1 if none */
int argIdx = -1; /* Index of the key== constraint, or -1 if none */
int iIdx2 = -1; /* The index of the second key */
int omit1 = 0;
int omit2 = 0;
const struct sqlite3_index_constraint *pConstraint;
pConstraint = pIdxInfo->aConstraint;
for(i=0; i<pIdxInfo->nConstraint && idxNum<16; i++, pConstraint++){
if( pConstraint->usable==0 ) continue;
if( pConstraint->iColumn!=LSM1_COLUMN_KEY ) continue;
if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
if( pConstraint->iColumn!=0 ) continue;
switch( pConstraint->op ){
case SQLITE_INDEX_CONSTRAINT_EQ: {
eqIdx = i;
idxNum = 1;
if( idxNum>0 ){
argIdx = i;
iIdx2 = -1;
idxNum = 0;
omit1 = 1;
}
break;
}
case SQLITE_INDEX_CONSTRAINT_GE:
case SQLITE_INDEX_CONSTRAINT_GT: {
if( idxNum==99 ){
argIdx = i;
idxNum = 2;
omit1 = pConstraint->op==SQLITE_INDEX_CONSTRAINT_GE;
}else if( idxNum==3 ){
iIdx2 = idxNum;
omit2 = omit1;
argIdx = i;
idxNum = 1;
omit1 = pConstraint->op==SQLITE_INDEX_CONSTRAINT_GE;
}
break;
}
case SQLITE_INDEX_CONSTRAINT_LE:
case SQLITE_INDEX_CONSTRAINT_LT: {
if( idxNum==99 ){
argIdx = i;
idxNum = 3;
omit1 = pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE;
}else if( idxNum==2 ){
iIdx2 = i;
idxNum = 1;
omit1 = pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE;
}
break;
}
}
}
if( eqIdx>=0 ){
pIdxInfo->aConstraintUsage[eqIdx].argvIndex = ++nArg;
pIdxInfo->aConstraintUsage[eqIdx].omit = 1;
if( argIdx>=0 ){
pIdxInfo->aConstraintUsage[argIdx].argvIndex = ++nArg;
pIdxInfo->aConstraintUsage[argIdx].omit = omit1;
}
if( idxNum==1 ){
if( iIdx2>=0 ){
pIdxInfo->aConstraintUsage[iIdx2].argvIndex = ++nArg;
pIdxInfo->aConstraintUsage[iIdx2].omit = omit2;
}
if( idxNum==0 ){
pIdxInfo->estimatedCost = (double)1;
pIdxInfo->estimatedRows = 1;
pIdxInfo->orderByConsumed = 1;
}else if( idxNum==1 ){
pIdxInfo->estimatedCost = (double)100;
pIdxInfo->estimatedRows = 100;
}else if( idxNum<99 ){
pIdxInfo->estimatedCost = (double)5000;
pIdxInfo->estimatedRows = 5000;
}else{
/* Full table scan */
pIdxInfo->estimatedCost = (double)2147483647;
@@ -615,16 +870,13 @@ int lsm1Update(
sqlite_int64 *pRowid
){
lsm1_vtab *p = (lsm1_vtab*)pVTab;
const void *pKey;
void *pFree = 0;
int nKey;
int eType;
int i;
int rc = LSM_OK;
sqlite3_value *pValue;
const unsigned char *pVal;
unsigned char *pData;
int nVal;
unsigned char pSpace[100];
unsigned char *pKey;
unsigned char aKey[16];
unsigned char pSpace[16];
lsm1_vblob val;
if( argc==1 ){
pVTab->zErrMsg = sqlite3_mprintf("cannot DELETE");
@@ -635,78 +887,73 @@ int lsm1Update(
return SQLITE_ERROR;
}
/* "INSERT INTO tab(command) VALUES('....')" is used to implement
/* "INSERT INTO tab(lsm1_command) VALUES('....')" is used to implement
** special commands.
*/
if( sqlite3_value_type(argv[2+LSM1_COLUMN_COMMAND])!=SQLITE_NULL ){
if( sqlite3_value_type(argv[3+p->nVal])!=SQLITE_NULL ){
return SQLITE_OK;
}
if( sqlite3_value_type(argv[2+LSM1_COLUMN_BLOBKEY])==SQLITE_BLOB ){
/* Use the blob key exactly as supplied */
pKey = sqlite3_value_blob(argv[2+LSM1_COLUMN_BLOBKEY]);
nKey = sqlite3_value_bytes(argv[2+LSM1_COLUMN_BLOBKEY]);
if( p->keyType==SQLITE_BLOB ){
pKey = (u8*)sqlite3_value_blob(argv[2]);
nKey = sqlite3_value_bytes(argv[2]);
}else if( p->keyType==SQLITE_TEXT ){
pKey = (u8*)sqlite3_value_text(argv[2]);
nKey = sqlite3_value_bytes(argv[2]);
}else{
/* Use a key encoding that sorts in lexicographical order */
rc = lsm1EncodeKey(argv[2+LSM1_COLUMN_KEY],
(unsigned char**)&pKey,&nKey,
pSpace,sizeof(pSpace));
if( rc ) return rc;
if( pKey!=(const void*)pSpace ) pFree = (void*)pKey;
sqlite3_int64 v = sqlite3_value_int64(argv[2]);
if( v>=0 ){
nKey = lsm1PutVarint64(aKey, (sqlite3_uint64)v);
pKey = aKey;
}else{
pVTab->zErrMsg = sqlite3_mprintf("key must be non-negative");
return SQLITE_ERROR;
}
}
if( sqlite3_value_type(argv[2+LSM1_COLUMN_BLOBVALUE])==SQLITE_BLOB ){
pVal = sqlite3_value_blob(argv[2+LSM1_COLUMN_BLOBVALUE]);
nVal = sqlite3_value_bytes(argv[2+LSM1_COLUMN_BLOBVALUE]);
rc = lsm_insert(p->pDb, pKey, nKey, pVal, nVal);
}else{
pValue = argv[2+LSM1_COLUMN_VALUE];
eType = sqlite3_value_type(pValue);
memset(&val, 0, sizeof(val));
for(i=0; i<p->nVal; i++){
sqlite3_value *pArg = argv[3+i];
u8 eType = sqlite3_value_type(pArg);
switch( eType ){
case SQLITE_NULL: {
rc = lsm_delete(p->pDb, pKey, nKey);
lsm1VblobAppendVarint(&val, SQLITE_NULL);
break;
}
case SQLITE_BLOB:
case SQLITE_TEXT: {
if( eType==SQLITE_TEXT ){
pVal = sqlite3_value_text(pValue);
case SQLITE_INTEGER: {
sqlite3_int64 v = sqlite3_value_int64(pArg);
if( v>=0 && v<=240/6 ){
lsm1VblobAppendVarint(&val, v*6);
}else{
pVal = (unsigned char*)sqlite3_value_blob(pValue);
}
nVal = sqlite3_value_bytes(pValue);
pData = sqlite3_malloc( nVal+1 );
if( pData==0 ){
rc = SQLITE_NOMEM;
}else{
pData[0] = (unsigned char)eType;
memcpy(&pData[1], pVal, nVal);
rc = lsm_insert(p->pDb, pKey, nKey, pData, nVal+1);
sqlite3_free(pData);
int n = lsm1PutSignedVarint64(pSpace, v);
lsm1VblobAppendVarint(&val, SQLITE_INTEGER + n*6);
lsm1VblobAppend(&val, pSpace, n);
}
break;
}
case SQLITE_INTEGER:
case SQLITE_FLOAT: {
sqlite3_uint64 x;
unsigned char aVal[9];
int i;
if( eType==SQLITE_INTEGER ){
*(sqlite3_int64*)&x = sqlite3_value_int64(pValue);
}else{
double r = sqlite3_value_double(pValue);
assert( sizeof(r)==sizeof(x) );
memcpy(&x, &r, sizeof(r));
}
for(i=8; x>0 && i>=1; i--){
aVal[i] = x & 0xff;
x >>= 8;
}
aVal[i] = (unsigned char)eType;
rc = lsm_insert(p->pDb, pKey, nKey, &aVal[i], 9-i);
double r = sqlite3_value_double(pArg);
lsm1VblobAppendVarint(&val, SQLITE_FLOAT + 8*6);
lsm1VblobAppend(&val, (u8*)&r, sizeof(r));
break;
}
case SQLITE_BLOB: {
int n = sqlite3_value_bytes(pArg);
lsm1VblobAppendVarint(&val, n*6 + SQLITE_BLOB);
lsm1VblobAppend(&val, sqlite3_value_blob(pArg), n);
break;
}
case SQLITE_TEXT: {
int n = sqlite3_value_bytes(pArg);
lsm1VblobAppendVarint(&val, n*6 + SQLITE_TEXT);
lsm1VblobAppend(&val, sqlite3_value_text(pArg), n);
break;
}
}
}
sqlite3_free(pFree);
if( val.errNoMem ){
return SQLITE_NOMEM;
}
rc = lsm_insert(p->pDb, pKey, nKey, val.a, val.n);
sqlite3_free(val.a);
return rc==LSM_OK ? SQLITE_OK : SQLITE_ERROR;
}
+11 -12
View File
@@ -20,26 +20,27 @@ load_lsm1_vtab db
forcedelete testlsm.db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE x1 USING lsm1(testlsm.db);
CREATE VIRTUAL TABLE x1 USING lsm1(testlsm.db,a,UINT,b,c,d);
PRAGMA table_info(x1);
} {
0 key {} 0 {} 0
1 blobkey {} 0 {} 0
2 value {} 0 {} 0
3 blobvalue {} 0 {} 0
0 a UINT 0 {} 0
1 b {} 0 {} 0
2 c {} 0 {} 0
3 d {} 0 {} 0
}
do_execsql_test 1.1 {
INSERT INTO x1(blobkey, blobvalue) VALUES(x'abcd', x'1234');
SELECT quote(blobkey), quote(blobvalue) FROM x1;
} {X'ABCD' X'1234'}
INSERT INTO x1(a,b,c,d) VALUES(15, 11, 22, 33),(8,'banjo',x'333231',NULL),
(12,NULL,3.25,-559281390);
SELECT a, quote(b), quote(c), quote(d) FROM x1;
} {8 'banjo' X'333231' NULL 12 NULL 3.25 -559281390 15 11 22 33}
do_catchsql_test 1.2 {
UPDATE x1 SET blobvalue = x'7890' WHERE blobkey = x'abcd';
UPDATE x1 SET d = d+1.0 WHERE a=15;
} {1 {cannot UPDATE}}
do_catchsql_test 1.3 {
DELETE FROM x1 WHERE blobkey = x'abcd'
DELETE FROM x1 WHERE a=15;
} {1 {cannot DELETE}}
do_test 1.4 {
@@ -52,5 +53,3 @@ do_test 1.5 {
} {testlsm.db}
finish_test
+7 -7
View File
@@ -680,16 +680,16 @@ static int csvtabNext(sqlite3_vtab_cursor *cur){
i++;
}
}while( pCur->rdr.cTerm==',' );
while( i<pTab->nCol ){
sqlite3_free(pCur->azVal[i]);
pCur->azVal[i] = 0;
pCur->aLen[i] = 0;
i++;
}
if( z==0 || pCur->rdr.cTerm==EOF ){
if( z==0 || (pCur->rdr.cTerm==EOF && i<pTab->nCol) ){
pCur->iRowid = -1;
}else{
pCur->iRowid++;
while( i<pTab->nCol ){
sqlite3_free(pCur->azVal[i]);
pCur->azVal[i] = 0;
pCur->aLen[i] = 0;
i++;
}
}
return SQLITE_OK;
}
+415 -97
View File
@@ -10,8 +10,8 @@
**
*************************************************************************
**
** This file contains the implementation of the "unionvtab" virtual
** table. This module provides read-only access to multiple tables,
** This file contains the implementation of the "unionvtab" and "swarmvtab"
** virtual tables. These modules provide read-only access to multiple tables,
** possibly in multiple database files, via a single database object.
** The source tables must have the following characteristics:
**
@@ -25,26 +25,48 @@
**
** * Each table must contain a distinct range of rowid values.
**
** A "unionvtab" virtual table is created as follows:
** The difference between the two virtual table modules is that for
** "unionvtab", all source tables must be located in the main database or
** in databases ATTACHed to the main database by the user. For "swarmvtab",
** the tables may be located in any database file on disk. The "swarmvtab"
** implementation takes care of opening and closing database files
** automatically.
**
** CREATE VIRTUAL TABLE <name> USING unionvtab(<sql statement>);
** UNIONVTAB
**
** The implementation evalutes <sql statement> whenever a unionvtab virtual
** table is created or opened. It should return one row for each source
** database table. The four columns required of each row are:
** A "unionvtab" virtual table is created as follows:
**
** 1. The name of the database containing the table ("main" or "temp" or
** the name of an attached database). Or NULL to indicate that all
** databases should be searched for the table in the usual fashion.
** CREATE VIRTUAL TABLE <name> USING unionvtab(<sql-statement>);
**
** 2. The name of the database table.
** The implementation evalutes <sql statement> whenever a unionvtab virtual
** table is created or opened. It should return one row for each source
** database table. The four columns required of each row are:
**
** 3. The smallest rowid in the range of rowids that may be stored in the
** database table (an integer).
** 1. The name of the database containing the table ("main" or "temp" or
** the name of an attached database). Or NULL to indicate that all
** databases should be searched for the table in the usual fashion.
**
** 4. The largest rowid in the range of rowids that may be stored in the
** database table (an integer).
** 2. The name of the database table.
**
** 3. The smallest rowid in the range of rowids that may be stored in the
** database table (an integer).
**
** 4. The largest rowid in the range of rowids that may be stored in the
** database table (an integer).
**
** SWARMVTAB
**
** A "swarmvtab" virtual table is created similarly to a unionvtab table:
**
** CREATE VIRTUAL TABLE <name>
** USING swarmvtab(<sql-statement>, <callback>);
**
** The difference is that for a swarmvtab table, the first column returned
** by the <sql statement> must return a path or URI that can be used to open
** the database file containing the source table. The <callback> option
** is optional. If included, it is the name of an application-defined
** SQL function that is invoked with the URI of the file, if the file
** does not already exist on disk.
*/
#include "sqlite3ext.h"
@@ -65,6 +87,30 @@ SQLITE_EXTENSION_INIT1
# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
#endif
/*
** The following is also copied from sqliteInt.h. To facilitate coverage
** testing.
*/
#ifndef ALWAYS
# if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST)
# define ALWAYS(X) (1)
# define NEVER(X) (0)
# elif !defined(NDEBUG)
# define ALWAYS(X) ((X)?1:(assert(0),0))
# define NEVER(X) ((X)?(assert(0),1):0)
# else
# define ALWAYS(X) (X)
# define NEVER(X) (X)
# endif
#endif
/*
** The swarmvtab module attempts to keep the number of open database files
** at or below this limit. This may not be possible if there are too many
** simultaneous queries.
*/
#define SWARMVTAB_MAX_OPEN 9
typedef struct UnionCsr UnionCsr;
typedef struct UnionTab UnionTab;
typedef struct UnionSrc UnionSrc;
@@ -79,6 +125,12 @@ struct UnionSrc {
char *zTab; /* Source table name */
sqlite3_int64 iMin; /* Minimum rowid */
sqlite3_int64 iMax; /* Maximum rowid */
/* Fields used by swarmvtab only */
char *zFile; /* Database file containing table zTab */
int nUser; /* Current number of users */
sqlite3 *db; /* Database handle */
UnionSrc *pNextClosable; /* Next in list of closable sources */
};
/*
@@ -87,9 +139,17 @@ struct UnionSrc {
struct UnionTab {
sqlite3_vtab base; /* Base class - must be first */
sqlite3 *db; /* Database handle */
int bSwarm; /* 1 for "swarmvtab", 0 for "unionvtab" */
int iPK; /* INTEGER PRIMARY KEY column, or -1 */
int nSrc; /* Number of elements in the aSrc[] array */
UnionSrc *aSrc; /* Array of source tables, sorted by rowid */
/* Used by swarmvtab only */
char *zSourceStr; /* Expected unionSourceToStr() value */
char *zNotFoundCallback; /* UDF to invoke if file not found on open */
UnionSrc *pClosable; /* First in list of closable sources */
int nOpen; /* Current number of open sources */
int nMaxOpen; /* Maximum number of open sources */
};
/*
@@ -98,8 +158,20 @@ struct UnionTab {
struct UnionCsr {
sqlite3_vtab_cursor base; /* Base class - must be first */
sqlite3_stmt *pStmt; /* SQL statement to run */
/* Used by swarmvtab only */
sqlite3_int64 iMaxRowid; /* Last rowid to visit */
int iTab; /* Index of table read by pStmt */
};
/*
** Given UnionTab table pTab and UnionSrc object pSrc, return the database
** handle that should be used to access the table identified by pSrc. This
** is the main db handle for "unionvtab" tables, or the source-specific
** handle for "swarmvtab".
*/
#define unionGetDb(pTab, pSrc) ((pTab)->bSwarm ? (pSrc)->db : (pTab)->db)
/*
** If *pRc is other than SQLITE_OK when this function is called, it
** always returns NULL. Otherwise, it attempts to allocate and return
@@ -160,7 +232,7 @@ static void unionDequote(char *z){
int iIn = 1;
int iOut = 0;
if( q=='[' ) q = ']';
while( z[iIn] ){
while( ALWAYS(z[iIn]) ){
if( z[iIn]==q ){
if( z[iIn+1]!=q ){
/* Character iIn was the close quote. */
@@ -202,6 +274,7 @@ static sqlite3_stmt *unionPrepare(
char **pzErr /* OUT: Error message */
){
sqlite3_stmt *pRet = 0;
assert( pzErr );
if( *pRc==SQLITE_OK ){
int rc = sqlite3_prepare_v2(db, zSql, -1, &pRet, 0);
if( rc!=SQLITE_OK ){
@@ -250,6 +323,7 @@ static sqlite3_stmt *unionPreparePrintf(
** In this case, *pzErr may be set to point to an error message
** buffer allocated by sqlite3_malloc().
*/
#if 0
static void unionReset(int *pRc, sqlite3_stmt *pStmt, char **pzErr){
int rc = sqlite3_reset(pStmt);
if( *pRc==SQLITE_OK ){
@@ -259,15 +333,39 @@ static void unionReset(int *pRc, sqlite3_stmt *pStmt, char **pzErr){
}
}
}
#endif
/*
** Call sqlite3_finalize() on SQL statement pStmt. If *pRc is set to
** SQLITE_OK when this function is called, then it is set to the
** value returned by sqlite3_finalize() before this function exits.
*/
static void unionFinalize(int *pRc, sqlite3_stmt *pStmt){
static void unionFinalize(int *pRc, sqlite3_stmt *pStmt, char **pzErr){
sqlite3 *db = sqlite3_db_handle(pStmt);
int rc = sqlite3_finalize(pStmt);
if( *pRc==SQLITE_OK ) *pRc = rc;
if( *pRc==SQLITE_OK ){
*pRc = rc;
if( rc ){
*pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
}
}
}
/*
** This function is a no-op for unionvtab. For swarmvtab, it attempts to
** close open database files until at most nMax are open. An SQLite error
** code is returned if an error occurs, or SQLITE_OK otherwise.
*/
static void unionCloseSources(UnionTab *pTab, int nMax){
while( pTab->pClosable && pTab->nOpen>nMax ){
UnionSrc **pp;
for(pp=&pTab->pClosable; (*pp)->pNextClosable; pp=&(*pp)->pNextClosable);
assert( (*pp)->db );
sqlite3_close((*pp)->db);
(*pp)->db = 0;
*pp = 0;
pTab->nOpen--;
}
}
/*
@@ -278,15 +376,52 @@ static int unionDisconnect(sqlite3_vtab *pVtab){
UnionTab *pTab = (UnionTab*)pVtab;
int i;
for(i=0; i<pTab->nSrc; i++){
sqlite3_free(pTab->aSrc[i].zDb);
sqlite3_free(pTab->aSrc[i].zTab);
UnionSrc *pSrc = &pTab->aSrc[i];
sqlite3_free(pSrc->zDb);
sqlite3_free(pSrc->zTab);
sqlite3_free(pSrc->zFile);
sqlite3_close(pSrc->db);
}
sqlite3_free(pTab->zSourceStr);
sqlite3_free(pTab->zNotFoundCallback);
sqlite3_free(pTab->aSrc);
sqlite3_free(pTab);
}
return SQLITE_OK;
}
/*
** Check that the table identified by pSrc is a rowid table. If not,
** return SQLITE_ERROR and set (*pzErr) to point to an English language
** error message. If the table is a rowid table and no error occurs,
** return SQLITE_OK and leave (*pzErr) unmodified.
*/
static int unionIsIntkeyTable(
sqlite3 *db, /* Database handle */
UnionSrc *pSrc, /* Source table to test */
char **pzErr /* OUT: Error message */
){
int bPk = 0;
const char *zType = 0;
int rc;
sqlite3_table_column_metadata(
db, pSrc->zDb, pSrc->zTab, "_rowid_", &zType, 0, 0, &bPk, 0
);
rc = sqlite3_errcode(db);
if( rc==SQLITE_ERROR
|| (rc==SQLITE_OK && (!bPk || sqlite3_stricmp("integer", zType)))
){
rc = SQLITE_ERROR;
*pzErr = sqlite3_mprintf("no such rowid table: %s%s%s",
(pSrc->zDb ? pSrc->zDb : ""),
(pSrc->zDb ? "." : ""),
pSrc->zTab
);
}
return rc;
}
/*
** This function is a no-op if *pRc is other than SQLITE_OK when it is
** called. In this case it returns NULL.
@@ -306,41 +441,27 @@ static int unionDisconnect(sqlite3_vtab *pVtab){
*/
static char *unionSourceToStr(
int *pRc, /* IN/OUT: Error code */
sqlite3 *db, /* Database handle */
UnionTab *pTab, /* Virtual table object */
UnionSrc *pSrc, /* Source table to test */
sqlite3_stmt *pStmt,
char **pzErr /* OUT: Error message */
){
char *zRet = 0;
if( *pRc==SQLITE_OK ){
int bPk = 0;
const char *zType = 0;
int rc;
sqlite3_table_column_metadata(
db, pSrc->zDb, pSrc->zTab, "_rowid_", &zType, 0, 0, &bPk, 0
sqlite3 *db = unionGetDb(pTab, pSrc);
int rc = unionIsIntkeyTable(db, pSrc, pzErr);
sqlite3_stmt *pStmt = unionPrepare(&rc, db,
"SELECT group_concat(quote(name) || '.' || quote(type)) "
"FROM pragma_table_info(?, ?)", pzErr
);
rc = sqlite3_errcode(db);
if( rc==SQLITE_ERROR
|| (rc==SQLITE_OK && (!bPk || sqlite3_stricmp("integer", zType)))
){
rc = SQLITE_ERROR;
*pzErr = sqlite3_mprintf("no such rowid table: %s%s%s",
(pSrc->zDb ? pSrc->zDb : ""),
(pSrc->zDb ? "." : ""),
pSrc->zTab
);
}
if( rc==SQLITE_OK ){
sqlite3_bind_text(pStmt, 1, pSrc->zTab, -1, SQLITE_STATIC);
sqlite3_bind_text(pStmt, 2, pSrc->zDb, -1, SQLITE_STATIC);
if( SQLITE_ROW==sqlite3_step(pStmt) ){
zRet = unionStrdup(&rc, (const char*)sqlite3_column_text(pStmt, 0));
const char *z = (const char*)sqlite3_column_text(pStmt, 0);
zRet = unionStrdup(&rc, z);
}
unionReset(&rc, pStmt, pzErr);
unionFinalize(&rc, pStmt, pzErr);
}
*pRc = rc;
}
@@ -356,34 +477,152 @@ static char *unionSourceToStr(
** other error occurs, SQLITE_OK is returned.
*/
static int unionSourceCheck(UnionTab *pTab, char **pzErr){
const char *zSql =
"SELECT group_concat(quote(name) || '.' || quote(type)) "
"FROM pragma_table_info(?, ?)";
int rc = SQLITE_OK;
char *z0 = 0;
int i;
if( pTab->nSrc==0 ){
*pzErr = sqlite3_mprintf("no source tables configured");
rc = SQLITE_ERROR;
}else{
sqlite3_stmt *pStmt = 0;
char *z0 = 0;
int i;
assert( *pzErr==0 );
z0 = unionSourceToStr(&rc, pTab, &pTab->aSrc[0], pzErr);
for(i=1; i<pTab->nSrc; i++){
char *z = unionSourceToStr(&rc, pTab, &pTab->aSrc[i], pzErr);
if( rc==SQLITE_OK && sqlite3_stricmp(z, z0) ){
*pzErr = sqlite3_mprintf("source table schema mismatch");
rc = SQLITE_ERROR;
}
sqlite3_free(z);
}
sqlite3_free(z0);
pStmt = unionPrepare(&rc, pTab->db, zSql, pzErr);
return rc;
}
/*
** Try to open the swarmvtab database. If initially unable, invoke the
** not-found callback UDF and then try again.
*/
static int unionOpenDatabaseInner(UnionTab *pTab, UnionSrc *pSrc, char **pzErr){
int rc = SQLITE_OK;
static const int openFlags =
SQLITE_OPEN_READONLY | SQLITE_OPEN_URI;
rc = sqlite3_open_v2(pSrc->zFile, &pSrc->db, openFlags, 0);
if( rc==SQLITE_OK ) return rc;
if( pTab->zNotFoundCallback ){
char *zSql = sqlite3_mprintf("SELECT \"%w\"(%Q);",
pTab->zNotFoundCallback, pSrc->zFile);
sqlite3_close(pSrc->db);
pSrc->db = 0;
if( zSql==0 ){
*pzErr = sqlite3_mprintf("out of memory");
return SQLITE_NOMEM;
}
rc = sqlite3_exec(pTab->db, zSql, 0, 0, pzErr);
sqlite3_free(zSql);
if( rc ) return rc;
rc = sqlite3_open_v2(pSrc->zFile, &pSrc->db, openFlags, 0);
}
if( rc!=SQLITE_OK ){
*pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(pSrc->db));
}
return rc;
}
/*
** This function may only be called for swarmvtab tables. The results of
** calling it on a unionvtab table are undefined.
**
** For a swarmvtab table, this function ensures that source database iSrc
** is open. If the database is opened successfully and the schema is as
** expected, or if it is already open when this function is called, SQLITE_OK
** is returned.
**
** Alternatively If an error occurs while opening the databases, or if the
** database schema is unsuitable, an SQLite error code is returned and (*pzErr)
** may be set to point to an English language error message. In this case it is
** the responsibility of the caller to eventually free the error message buffer
** using sqlite3_free().
*/
static int unionOpenDatabase(UnionTab *pTab, int iSrc, char **pzErr){
int rc = SQLITE_OK;
UnionSrc *pSrc = &pTab->aSrc[iSrc];
assert( pTab->bSwarm && iSrc<pTab->nSrc );
if( pSrc->db==0 ){
unionCloseSources(pTab, pTab->nMaxOpen-1);
rc = unionOpenDatabaseInner(pTab, pSrc, pzErr);
if( rc==SQLITE_OK ){
z0 = unionSourceToStr(&rc, pTab->db, &pTab->aSrc[0], pStmt, pzErr);
}
for(i=1; i<pTab->nSrc; i++){
char *z = unionSourceToStr(&rc, pTab->db, &pTab->aSrc[i], pStmt, pzErr);
if( rc==SQLITE_OK && sqlite3_stricmp(z, z0) ){
*pzErr = sqlite3_mprintf("source table schema mismatch");
rc = SQLITE_ERROR;
char *z = unionSourceToStr(&rc, pTab, pSrc, pzErr);
if( rc==SQLITE_OK ){
if( pTab->zSourceStr==0 ){
pTab->zSourceStr = z;
}else{
if( sqlite3_stricmp(z, pTab->zSourceStr) ){
*pzErr = sqlite3_mprintf("source table schema mismatch");
rc = SQLITE_ERROR;
}
sqlite3_free(z);
}
}
sqlite3_free(z);
}
unionFinalize(&rc, pStmt);
sqlite3_free(z0);
if( rc==SQLITE_OK ){
pSrc->pNextClosable = pTab->pClosable;
pTab->pClosable = pSrc;
pTab->nOpen++;
}else{
sqlite3_close(pSrc->db);
pSrc->db = 0;
}
}
return rc;
}
/*
** This function is a no-op for unionvtab tables. For swarmvtab, increment
** the reference count for source table iTab. If the reference count was
** zero before it was incremented, also remove the source from the closable
** list.
*/
static void unionIncrRefcount(UnionTab *pTab, int iTab){
if( pTab->bSwarm ){
UnionSrc *pSrc = &pTab->aSrc[iTab];
assert( pSrc->nUser>=0 && pSrc->db );
if( pSrc->nUser==0 ){
UnionSrc **pp;
for(pp=&pTab->pClosable; *pp!=pSrc; pp=&(*pp)->pNextClosable);
*pp = pSrc->pNextClosable;
pSrc->pNextClosable = 0;
}
pSrc->nUser++;
}
}
/*
** Finalize the SQL statement pCsr->pStmt and return the result.
**
** If this is a swarmvtab table (not unionvtab) and pCsr->pStmt was not
** NULL when this function was called, also decrement the reference
** count on the associated source table. If this means the source tables
** refcount is now zero, add it to the closable list.
*/
static int unionFinalizeCsrStmt(UnionCsr *pCsr){
int rc = SQLITE_OK;
if( pCsr->pStmt ){
UnionTab *pTab = (UnionTab*)pCsr->base.pVtab;
UnionSrc *pSrc = &pTab->aSrc[pCsr->iTab];
rc = sqlite3_finalize(pCsr->pStmt);
pCsr->pStmt = 0;
if( pTab->bSwarm ){
pSrc->nUser--;
assert( pSrc->nUser>=0 );
if( pSrc->nUser==0 ){
pSrc->pNextClosable = pTab->pClosable;
pTab->pClosable = pSrc;
}
unionCloseSources(pTab, pTab->nMaxOpen);
}
}
return rc;
}
@@ -393,10 +632,11 @@ static int unionSourceCheck(UnionTab *pTab, char **pzErr){
**
** The argv[] array contains the following:
**
** argv[0] -> module name ("unionvtab")
** argv[0] -> module name ("unionvtab" or "swarmvtab")
** argv[1] -> database name
** argv[2] -> table name
** argv[3] -> SQL statement
** argv[4] -> not-found callback UDF name
*/
static int unionConnect(
sqlite3 *db,
@@ -407,14 +647,15 @@ static int unionConnect(
){
UnionTab *pTab = 0;
int rc = SQLITE_OK;
int bSwarm = (pAux==0 ? 0 : 1);
const char *zVtab = (bSwarm ? "swarmvtab" : "unionvtab");
(void)pAux; /* Suppress harmless 'unused parameter' warning */
if( sqlite3_stricmp("temp", argv[1]) ){
/* unionvtab tables may only be created in the temp schema */
*pzErr = sqlite3_mprintf("unionvtab tables must be created in TEMP schema");
*pzErr = sqlite3_mprintf("%s tables must be created in TEMP schema", zVtab);
rc = SQLITE_ERROR;
}else if( argc!=4 ){
*pzErr = sqlite3_mprintf("wrong number of arguments for unionvtab");
}else if( argc!=4 && argc!=5 ){
*pzErr = sqlite3_mprintf("wrong number of arguments for %s", zVtab);
rc = SQLITE_ERROR;
}else{
int nAlloc = 0; /* Allocated size of pTab->aSrc[] */
@@ -464,30 +705,60 @@ static int unionConnect(
rc = SQLITE_ERROR;
}
pSrc = &pTab->aSrc[pTab->nSrc++];
pSrc->zDb = unionStrdup(&rc, zDb);
pSrc->zTab = unionStrdup(&rc, zTab);
pSrc->iMin = iMin;
pSrc->iMax = iMax;
if( rc==SQLITE_OK ){
pSrc = &pTab->aSrc[pTab->nSrc++];
pSrc->zTab = unionStrdup(&rc, zTab);
pSrc->iMin = iMin;
pSrc->iMax = iMax;
if( bSwarm ){
pSrc->zFile = unionStrdup(&rc, zDb);
}else{
pSrc->zDb = unionStrdup(&rc, zDb);
}
}
}
unionFinalize(&rc, pStmt);
unionFinalize(&rc, pStmt, pzErr);
pStmt = 0;
/* Verify that all source tables exist and have compatible schemas. */
/* Capture the not-found callback UDF name */
if( rc==SQLITE_OK && argc>=5 ){
pTab->zNotFoundCallback = unionStrdup(&rc, argv[4]);
unionDequote(pTab->zNotFoundCallback);
}
/* It is an error if the SELECT statement returned zero rows. If only
** because there is no way to determine the schema of the virtual
** table in this case. */
if( rc==SQLITE_OK && pTab->nSrc==0 ){
*pzErr = sqlite3_mprintf("no source tables configured");
rc = SQLITE_ERROR;
}
/* For unionvtab, verify that all source tables exist and have
** compatible schemas. For swarmvtab, attach the first database and
** check that the first table is a rowid table only. */
if( rc==SQLITE_OK ){
pTab->db = db;
rc = unionSourceCheck(pTab, pzErr);
pTab->bSwarm = bSwarm;
pTab->nMaxOpen = SWARMVTAB_MAX_OPEN;
if( bSwarm ){
rc = unionOpenDatabase(pTab, 0, pzErr);
}else{
rc = unionSourceCheck(pTab, pzErr);
}
}
/* Compose a CREATE TABLE statement and pass it to declare_vtab() */
if( rc==SQLITE_OK ){
pStmt = unionPreparePrintf(&rc, pzErr, db, "SELECT "
UnionSrc *pSrc = &pTab->aSrc[0];
sqlite3 *tdb = unionGetDb(pTab, pSrc);
pStmt = unionPreparePrintf(&rc, pzErr, tdb, "SELECT "
"'CREATE TABLE xyz('"
" || group_concat(quote(name) || ' ' || type, ', ')"
" || ')',"
"max((cid+1) * (type='INTEGER' COLLATE nocase AND pk=1))-1 "
"FROM pragma_table_info(%Q, ?)",
pTab->aSrc[0].zTab, pTab->aSrc[0].zDb
pSrc->zTab, pSrc->zDb
);
}
if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
@@ -496,7 +767,7 @@ static int unionConnect(
pTab->iPK = sqlite3_column_int(pStmt, 1);
}
unionFinalize(&rc, pStmt);
unionFinalize(&rc, pStmt, pzErr);
}
if( rc!=SQLITE_OK ){
@@ -508,7 +779,6 @@ static int unionConnect(
return rc;
}
/*
** xOpen
*/
@@ -526,25 +796,56 @@ static int unionOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
*/
static int unionClose(sqlite3_vtab_cursor *cur){
UnionCsr *pCsr = (UnionCsr*)cur;
sqlite3_finalize(pCsr->pStmt);
unionFinalizeCsrStmt(pCsr);
sqlite3_free(pCsr);
return SQLITE_OK;
}
/*
** This function does the work of the xNext() method. Except that, if it
** returns SQLITE_ROW, it should be called again within the same xNext()
** method call. See unionNext() for details.
*/
static int doUnionNext(UnionCsr *pCsr){
int rc = SQLITE_OK;
assert( pCsr->pStmt );
if( sqlite3_step(pCsr->pStmt)!=SQLITE_ROW ){
UnionTab *pTab = (UnionTab*)pCsr->base.pVtab;
rc = unionFinalizeCsrStmt(pCsr);
if( rc==SQLITE_OK && pTab->bSwarm ){
pCsr->iTab++;
if( pCsr->iTab<pTab->nSrc ){
UnionSrc *pSrc = &pTab->aSrc[pCsr->iTab];
if( pCsr->iMaxRowid>=pSrc->iMin ){
/* It is necessary to scan the next table. */
rc = unionOpenDatabase(pTab, pCsr->iTab, &pTab->base.zErrMsg);
pCsr->pStmt = unionPreparePrintf(&rc, &pTab->base.zErrMsg, pSrc->db,
"SELECT rowid, * FROM %Q %s %lld",
pSrc->zTab,
(pSrc->iMax>pCsr->iMaxRowid ? "WHERE _rowid_ <=" : "-- "),
pCsr->iMaxRowid
);
if( rc==SQLITE_OK ){
assert( pCsr->pStmt );
unionIncrRefcount(pTab, pCsr->iTab);
rc = SQLITE_ROW;
}
}
}
}
}
return rc;
}
/*
** xNext
*/
static int unionNext(sqlite3_vtab_cursor *cur){
UnionCsr *pCsr = (UnionCsr*)cur;
int rc;
assert( pCsr->pStmt );
if( sqlite3_step(pCsr->pStmt)!=SQLITE_ROW ){
rc = sqlite3_finalize(pCsr->pStmt);
pCsr->pStmt = 0;
}else{
rc = SQLITE_OK;
}
do {
rc = doUnionNext((UnionCsr*)cur);
}while( rc==SQLITE_ROW );
return rc;
}
@@ -637,8 +938,7 @@ static int unionFilter(
}
}
sqlite3_finalize(pCsr->pStmt);
pCsr->pStmt = 0;
unionFinalizeCsrStmt(pCsr);
if( bZero ){
return SQLITE_OK;
}
@@ -674,12 +974,25 @@ static int unionFilter(
zSql = sqlite3_mprintf("%z %s rowid<=%lld", zSql, zWhere, iMax);
}
}
if( pTab->bSwarm ){
pCsr->iTab = i;
pCsr->iMaxRowid = iMax;
rc = unionOpenDatabase(pTab, i, &pTab->base.zErrMsg);
break;
}
}
if( zSql==0 ) return rc;
pCsr->pStmt = unionPrepare(&rc, pTab->db, zSql, &pTab->base.zErrMsg);
sqlite3_free(zSql);
if( zSql==0 ){
return rc;
}else{
sqlite3 *db = unionGetDb(pTab, &pTab->aSrc[pCsr->iTab]);
pCsr->pStmt = unionPrepare(&rc, db, zSql, &pTab->base.zErrMsg);
if( pCsr->pStmt ){
unionIncrRefcount(pTab, pCsr->iTab);
}
sqlite3_free(zSql);
}
if( rc!=SQLITE_OK ) return rc;
return unionNext(pVtabCursor);
}
@@ -791,8 +1104,13 @@ static int createUnionVtab(sqlite3 *db){
0, /* xRelease */
0 /* xRollbackTo */
};
int rc;
return sqlite3_create_module(db, "unionvtab", &unionModule, 0);
rc = sqlite3_create_module(db, "unionvtab", &unionModule, 0);
if( rc==SQLITE_OK ){
rc = sqlite3_create_module(db, "swarmvtab", &unionModule, (void*)db);
}
return rc;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
+509
View File
@@ -0,0 +1,509 @@
/*
** 2017-08-10
**
** 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 a virtual table that prints diagnostic information
** on stdout when its key interfaces are called. This is intended for
** interactive analysis and debugging of virtual table interfaces.
**
** Usage example:
**
** .load ./vtablog
** CREATE VIRTUAL TABLE temp.log USING vtablog(
** schema='CREATE TABLE x(a,b,c)',
** rows=25
** );
** SELECT * FROM log;
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>
/* vtablog_vtab is a subclass of sqlite3_vtab which will
** serve as the underlying representation of a vtablog virtual table
*/
typedef struct vtablog_vtab vtablog_vtab;
struct vtablog_vtab {
sqlite3_vtab base; /* Base class - must be first */
int nRow; /* Number of rows in the table */
int iInst; /* Instance number for this vtablog table */
int nCursor; /* Number of cursors created */
};
/* vtablog_cursor is a subclass of sqlite3_vtab_cursor which will
** serve as the underlying representation of a cursor that scans
** over rows of the result
*/
typedef struct vtablog_cursor vtablog_cursor;
struct vtablog_cursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
int iCursor; /* Cursor number */
sqlite3_int64 iRowid; /* The rowid */
};
/* Skip leading whitespace. Return a pointer to the first non-whitespace
** character, or to the zero terminator if the string has only whitespace */
static const char *vtablog_skip_whitespace(const char *z){
while( isspace((unsigned char)z[0]) ) z++;
return z;
}
/* Remove trailing whitespace from the end of string z[] */
static void vtablog_trim_whitespace(char *z){
size_t n = strlen(z);
while( n>0 && isspace((unsigned char)z[n]) ) n--;
z[n] = 0;
}
/* Dequote the string */
static void vtablog_dequote(char *z){
int j;
char cQuote = z[0];
size_t i, n;
if( cQuote!='\'' && cQuote!='"' ) return;
n = strlen(z);
if( n<2 || z[n-1]!=z[0] ) return;
for(i=1, j=0; i<n-1; i++){
if( z[i]==cQuote && z[i+1]==cQuote ) i++;
z[j++] = z[i];
}
z[j] = 0;
}
/* Check to see if the string is of the form: "TAG = VALUE" with optional
** whitespace before and around tokens. If it is, return a pointer to the
** first character of VALUE. If it is not, return NULL.
*/
static const char *vtablog_parameter(const char *zTag, int nTag, const char *z){
z = vtablog_skip_whitespace(z);
if( strncmp(zTag, z, nTag)!=0 ) return 0;
z = vtablog_skip_whitespace(z+nTag);
if( z[0]!='=' ) return 0;
return vtablog_skip_whitespace(z+1);
}
/* Decode a parameter that requires a dequoted string.
**
** Return non-zero on an error.
*/
static int vtablog_string_parameter(
char **pzErr, /* Leave the error message here, if there is one */
const char *zParam, /* Parameter we are checking for */
const char *zArg, /* Raw text of the virtual table argment */
char **pzVal /* Write the dequoted string value here */
){
const char *zValue;
zValue = vtablog_parameter(zParam,(int)strlen(zParam),zArg);
if( zValue==0 ) return 0;
if( *pzVal ){
*pzErr = sqlite3_mprintf("more than one '%s' parameter", zParam);
return 1;
}
*pzVal = sqlite3_mprintf("%s", zValue);
if( *pzVal==0 ){
*pzErr = sqlite3_mprintf("out of memory");
return 1;
}
vtablog_trim_whitespace(*pzVal);
vtablog_dequote(*pzVal);
return 0;
}
#if 0 /* not used - yet */
/* Return 0 if the argument is false and 1 if it is true. Return -1 if
** we cannot really tell.
*/
static int vtablog_boolean(const char *z){
if( sqlite3_stricmp("yes",z)==0
|| sqlite3_stricmp("on",z)==0
|| sqlite3_stricmp("true",z)==0
|| (z[0]=='1' && z[1]==0)
){
return 1;
}
if( sqlite3_stricmp("no",z)==0
|| sqlite3_stricmp("off",z)==0
|| sqlite3_stricmp("false",z)==0
|| (z[0]=='0' && z[1]==0)
){
return 0;
}
return -1;
}
#endif
/*
** The vtablogConnect() method is invoked to create a new
** vtablog_vtab that describes the vtablog virtual table.
**
** Think of this routine as the constructor for vtablog_vtab objects.
**
** All this routine needs to do is:
**
** (1) Allocate the vtablog_vtab object and initialize all fields.
**
** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
** result set of queries against vtablog will look like.
*/
static int vtablogConnectCreate(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr,
int isCreate
){
static int nInst = 0;
vtablog_vtab *pNew;
int i;
int rc;
int iInst = ++nInst;
char *zSchema = 0;
char *zNRow = 0;
printf("vtablog%s(tab=%d):\n", isCreate ? "Create" : "Connect", iInst);
printf(" argc=%d\n", argc);
for(i=0; i<argc; i++){
printf(" argv[%d] = ", i);
if( argv[i] ){
printf("[%s]\n", argv[i]);
}else{
printf("NULL\n");
}
}
for(i=3; i<argc; i++){
const char *z = argv[i];
if( vtablog_string_parameter(pzErr, "schema", z, &zSchema) ){
return SQLITE_ERROR;
}
if( vtablog_string_parameter(pzErr, "rows", z, &zNRow) ){
return SQLITE_ERROR;
}
}
if( zSchema==0 ){
*pzErr = sqlite3_mprintf("no schema defined");
return SQLITE_ERROR;
}
rc = sqlite3_declare_vtab(db, zSchema);
if( rc==SQLITE_OK ){
pNew = sqlite3_malloc( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
pNew->nRow = 10;
if( zNRow ) pNew->nRow = atoi(zNRow);
pNew->iInst = iInst;
}
return rc;
}
static int vtablogCreate(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
return vtablogConnectCreate(db,pAux,argc,argv,ppVtab,pzErr,1);
}
static int vtablogConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
return vtablogConnectCreate(db,pAux,argc,argv,ppVtab,pzErr,0);
}
/*
** This method is the destructor for vtablog_cursor objects.
*/
static int vtablogDisconnect(sqlite3_vtab *pVtab){
vtablog_vtab *pTab = (vtablog_vtab*)pVtab;
printf("vtablogDisconnect(%d)\n", pTab->iInst);
sqlite3_free(pVtab);
return SQLITE_OK;
}
/*
** This method is the destructor for vtablog_cursor objects.
*/
static int vtablogDestroy(sqlite3_vtab *pVtab){
vtablog_vtab *pTab = (vtablog_vtab*)pVtab;
printf("vtablogDestroy(%d)\n", pTab->iInst);
sqlite3_free(pVtab);
return SQLITE_OK;
}
/*
** Constructor for a new vtablog_cursor object.
*/
static int vtablogOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
vtablog_vtab *pTab = (vtablog_vtab*)p;
vtablog_cursor *pCur;
printf("vtablogOpen(tab=%d, cursor=%d)\n", pTab->iInst, ++pTab->nCursor);
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
pCur->iCursor = pTab->nCursor;
*ppCursor = &pCur->base;
return SQLITE_OK;
}
/*
** Destructor for a vtablog_cursor.
*/
static int vtablogClose(sqlite3_vtab_cursor *cur){
vtablog_cursor *pCur = (vtablog_cursor*)cur;
vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
printf("vtablogClose(tab=%d, cursor=%d)\n", pTab->iInst, pCur->iCursor);
sqlite3_free(cur);
return SQLITE_OK;
}
/*
** Advance a vtablog_cursor to its next row of output.
*/
static int vtablogNext(sqlite3_vtab_cursor *cur){
vtablog_cursor *pCur = (vtablog_cursor*)cur;
vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
printf("vtablogNext(tab=%d, cursor=%d) rowid %d -> %d\n",
pTab->iInst, pCur->iCursor, (int)pCur->iRowid, (int)pCur->iRowid+1);
pCur->iRowid++;
return SQLITE_OK;
}
/*
** Return values of columns for the row at which the vtablog_cursor
** is currently pointing.
*/
static int vtablogColumn(
sqlite3_vtab_cursor *cur, /* The cursor */
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
int i /* Which column to return */
){
vtablog_cursor *pCur = (vtablog_cursor*)cur;
vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
char zVal[50];
if( i<26 ){
sqlite3_snprintf(sizeof(zVal),zVal,"%c%d",
"abcdefghijklmnopqrstuvwyz"[i], pCur->iRowid);
}else{
sqlite3_snprintf(sizeof(zVal),zVal,"{%d}%d", i, pCur->iRowid);
}
printf("vtablogColumn(tab=%d, cursor=%d, i=%d): [%s]\n",
pTab->iInst, pCur->iCursor, i, zVal);
sqlite3_result_text(ctx, zVal, -1, SQLITE_TRANSIENT);
return SQLITE_OK;
}
/*
** Return the rowid for the current row. In this implementation, the
** rowid is the same as the output value.
*/
static int vtablogRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
vtablog_cursor *pCur = (vtablog_cursor*)cur;
vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
printf("vtablogRowid(tab=%d, cursor=%d): %d\n",
pTab->iInst, pCur->iCursor, (int)pCur->iRowid);
*pRowid = pCur->iRowid;
return SQLITE_OK;
}
/*
** Return TRUE if the cursor has been moved off of the last
** row of output.
*/
static int vtablogEof(sqlite3_vtab_cursor *cur){
vtablog_cursor *pCur = (vtablog_cursor*)cur;
vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
int rc = pCur->iRowid >= pTab->nRow;
printf("vtablogEof(tab=%d, cursor=%d): %d\n",
pTab->iInst, pCur->iCursor, rc);
return rc;
}
/*
** Output an sqlite3_value object's value as an SQL literal.
*/
static void vtablogQuote(sqlite3_value *p){
char z[50];
switch( sqlite3_value_type(p) ){
case SQLITE_NULL: {
printf("NULL");
break;
}
case SQLITE_INTEGER: {
sqlite3_snprintf(50,z,"%lld", sqlite3_value_int64(p));
printf("%s", z);
break;
}
case SQLITE_FLOAT: {
sqlite3_snprintf(50,z,"%!.20g", sqlite3_value_double(p));
printf("%s", z);
break;
}
case SQLITE_BLOB: {
int n = sqlite3_value_bytes(p);
const unsigned char *z = (const unsigned char*)sqlite3_value_blob(p);
int i;
printf("x'");
for(i=0; i<n; i++) printf("%02x", z[i]);
printf("'");
break;
}
case SQLITE_TEXT: {
const char *z = (const char*)sqlite3_value_text(p);
int i;
char c;
for(i=0; (c = z[i])!=0 && c!='\''; i++){}
if( c==0 ){
printf("'%s'",z);
}else{
printf("'");
while( *z ){
for(i=0; (c = z[i])!=0 && c!='\''; i++){}
if( c=='\'' ) i++;
if( i ){
printf("%.*s", i, z);
z += i;
}
if( c=='\'' ){
printf("'");
continue;
}
if( c==0 ){
break;
}
z++;
}
printf("'");
}
break;
}
}
}
/*
** This method is called to "rewind" the vtablog_cursor object back
** to the first row of output. This method is always called at least
** once prior to any call to vtablogColumn() or vtablogRowid() or
** vtablogEof().
*/
static int vtablogFilter(
sqlite3_vtab_cursor *cur,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
vtablog_cursor *pCur = (vtablog_cursor *)cur;
vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
printf("vtablogFilter(tab=%d, cursor=%d):\n", pTab->iInst, pCur->iCursor);
pCur->iRowid = 0;
return SQLITE_OK;
}
/*
** SQLite will invoke this method one or more times while planning a query
** that uses the vtablog virtual table. This routine needs to create
** a query plan for each invocation and compute an estimated cost for that
** plan.
*/
static int vtablogBestIndex(
sqlite3_vtab *tab,
sqlite3_index_info *pIdxInfo
){
vtablog_vtab *pTab = (vtablog_vtab*)tab;
printf("vtablogBestIndex(tab=%d):\n", pTab->iInst);
pIdxInfo->estimatedCost = (double)500;
pIdxInfo->estimatedRows = 500;
return SQLITE_OK;
}
/*
** SQLite invokes this method to INSERT, UPDATE, or DELETE content from
** the table.
**
** This implementation does not actually make any changes to the table
** content. It merely logs the fact that the method was invoked
*/
static int vtablogUpdate(
sqlite3_vtab *tab,
int argc,
sqlite3_value **argv,
sqlite_int64 *pRowid
){
vtablog_vtab *pTab = (vtablog_vtab*)tab;
int i;
printf("vtablogUpdate(tab=%d):\n", pTab->iInst);
printf(" argc=%d\n", argc);
for(i=0; i<argc; i++){
printf(" argv[%d]=", i);
vtablogQuote(argv[i]);
printf("\n");
}
return SQLITE_OK;
}
/*
** This following structure defines all the methods for the
** vtablog virtual table.
*/
static sqlite3_module vtablogModule = {
0, /* iVersion */
vtablogCreate, /* xCreate */
vtablogConnect, /* xConnect */
vtablogBestIndex, /* xBestIndex */
vtablogDisconnect, /* xDisconnect */
vtablogDestroy, /* xDestroy */
vtablogOpen, /* xOpen - open a cursor */
vtablogClose, /* xClose - close a cursor */
vtablogFilter, /* xFilter - configure scan constraints */
vtablogNext, /* xNext - advance a cursor */
vtablogEof, /* xEof - check for end of scan */
vtablogColumn, /* xColumn - read data */
vtablogRowid, /* xRowid - read data */
vtablogUpdate, /* xUpdate */
0, /* xBegin */
0, /* xSync */
0, /* xCommit */
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
};
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_vtablog_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc;
SQLITE_EXTENSION_INIT2(pApi);
rc = sqlite3_create_module(db, "vtablog", &vtablogModule, 0);
return rc;
}
+1 -1
View File
@@ -114,7 +114,7 @@ ifcapable fts3 {
INSERT INTO data_xt VALUES('a', 'b', 1, 0);
}
} msg] $msg
} {1 {SQLITE_ERROR - SQL logic error or missing database}}
} {1 {SQLITE_ERROR - SQL logic error}}
}
#--------------------------------------------------------------------
+1 -1
View File
@@ -125,7 +125,7 @@ foreach {tn2 setup sql expect} {
{1 SQLITE_IOERR_WRITE}
{1 SQLITE_IOERR_READ}
{1 SQLITE_IOERR_FSYNC}
{1 {SQLITE_ERROR - SQL logic error or missing database}}
{1 {SQLITE_ERROR - SQL logic error}}
{1 {SQLITE_ERROR - unable to open database: rbu.db}}
{1 {SQLITE_IOERR - unable to open database: rbu.db}}
}
+1 -1
View File
@@ -31,7 +31,7 @@ foreach {fault errlist} {
{1 SQLITE_IOERR_READ}
{1 {SQLITE_IOERR - unable to open database: test.db2}}
{1 {SQLITE_ERROR - unable to open database: test.db2}}
{1 {SQLITE_ERROR - SQL logic error or missing database}}
{1 {SQLITE_ERROR - SQL logic error}}
}
cantopen* {
+2 -2
View File
@@ -119,14 +119,14 @@ do_test 3.2 {
CREATE TABLE data_ft(x, rbu_rowid, rbu_control);
INSERT INTO data_ft VALUES(NULL, 2, 1);
} } msg] $msg]
} {1 {SQLITE_ERROR - SQL logic error or missing database]}}
} {1 {SQLITE_ERROR - SQL logic error]}}
do_test 3.3 {
list [catch { apply_rbu_update test.db {
CREATE TABLE data_ft(x, rbu_rowid, rbu_control);
INSERT INTO data_ft VALUES('7 8 9', 1, 'x');
} } msg] $msg]
} {1 {SQLITE_ERROR - SQL logic error or missing database]}}
} {1 {SQLITE_ERROR - SQL logic error]}}
+3 -1
View File
@@ -4610,7 +4610,9 @@ static int rbuVfsAccess(
if( *pResOut ){
rc = SQLITE_CANTOPEN;
}else{
*pResOut = 1;
sqlite3_int64 sz = 0;
rc = rbuVfsFileSize(&pDb->base, &sz);
*pResOut = (sz>0);
}
}
}
+11 -11
View File
@@ -308,7 +308,7 @@ typedef struct sqlite3rbu sqlite3rbu;
** not work out of the box with zipvfs. Refer to the comment describing
** the zipvfs_create_vfs() API below for details on using RBU with zipvfs.
*/
sqlite3rbu *sqlite3rbu_open(
SQLITE_API sqlite3rbu *sqlite3rbu_open(
const char *zTarget,
const char *zRbu,
const char *zState
@@ -347,7 +347,7 @@ sqlite3rbu *sqlite3rbu_open(
** a description of the complications associated with using RBU with
** zipvfs databases.
*/
sqlite3rbu *sqlite3rbu_vacuum(
SQLITE_API sqlite3rbu *sqlite3rbu_vacuum(
const char *zTarget,
const char *zState
);
@@ -383,7 +383,7 @@ sqlite3rbu *sqlite3rbu_vacuum(
** Database handles returned by this function remain valid until the next
** call to any sqlite3rbu_xxx() function other than sqlite3rbu_db().
*/
sqlite3 *sqlite3rbu_db(sqlite3rbu*, int bRbu);
SQLITE_API sqlite3 *sqlite3rbu_db(sqlite3rbu*, int bRbu);
/*
** Do some work towards applying the RBU update to the target db.
@@ -397,7 +397,7 @@ sqlite3 *sqlite3rbu_db(sqlite3rbu*, int bRbu);
** SQLITE_OK, all subsequent calls on the same RBU handle are no-ops
** that immediately return the same value.
*/
int sqlite3rbu_step(sqlite3rbu *pRbu);
SQLITE_API int sqlite3rbu_step(sqlite3rbu *pRbu);
/*
** Force RBU to save its state to disk.
@@ -409,7 +409,7 @@ int sqlite3rbu_step(sqlite3rbu *pRbu);
**
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
*/
int sqlite3rbu_savestate(sqlite3rbu *pRbu);
SQLITE_API int sqlite3rbu_savestate(sqlite3rbu *pRbu);
/*
** Close an RBU handle.
@@ -429,14 +429,14 @@ int sqlite3rbu_savestate(sqlite3rbu *pRbu);
** update has been partially applied, or SQLITE_DONE if it has been
** completely applied.
*/
int sqlite3rbu_close(sqlite3rbu *pRbu, char **pzErrmsg);
SQLITE_API int sqlite3rbu_close(sqlite3rbu *pRbu, char **pzErrmsg);
/*
** Return the total number of key-value operations (inserts, deletes or
** updates) that have been performed on the target database since the
** current RBU update was started.
*/
sqlite3_int64 sqlite3rbu_progress(sqlite3rbu *pRbu);
SQLITE_API sqlite3_int64 sqlite3rbu_progress(sqlite3rbu *pRbu);
/*
** Obtain permyriadage (permyriadage is to 10000 as percentage is to 100)
@@ -478,7 +478,7 @@ sqlite3_int64 sqlite3rbu_progress(sqlite3rbu *pRbu);
** table exists but is not correctly populated, the value of the *pnOne
** output variable during stage 1 is undefined.
*/
void sqlite3rbu_bp_progress(sqlite3rbu *pRbu, int *pnOne, int *pnTwo);
SQLITE_API void sqlite3rbu_bp_progress(sqlite3rbu *pRbu, int *pnOne, int*pnTwo);
/*
** Obtain an indication as to the current stage of an RBU update or vacuum.
@@ -516,7 +516,7 @@ void sqlite3rbu_bp_progress(sqlite3rbu *pRbu, int *pnOne, int *pnTwo);
#define SQLITE_RBU_STATE_DONE 4
#define SQLITE_RBU_STATE_ERROR 5
int sqlite3rbu_state(sqlite3rbu *pRbu);
SQLITE_API int sqlite3rbu_state(sqlite3rbu *pRbu);
/*
** Create an RBU VFS named zName that accesses the underlying file-system
@@ -560,7 +560,7 @@ int sqlite3rbu_state(sqlite3rbu *pRbu);
** file-system via "rbu" all the time, even if it only uses RBU functionality
** occasionally.
*/
int sqlite3rbu_create_vfs(const char *zName, const char *zParent);
SQLITE_API int sqlite3rbu_create_vfs(const char *zName, const char *zParent);
/*
** Deregister and destroy an RBU vfs created by an earlier call to
@@ -570,7 +570,7 @@ int sqlite3rbu_create_vfs(const char *zName, const char *zParent);
** before all database handles that use it have been closed, the results
** are undefined.
*/
void sqlite3rbu_destroy_vfs(const char *zName);
SQLITE_API void sqlite3rbu_destroy_vfs(const char *zName);
#ifdef __cplusplus
} /* end of the 'extern "C"' block */
+4
View File
@@ -228,6 +228,10 @@ do_execsql_test rtreeA-7.100 {
do_catchsql_test rtreeA-7.110 {
SELECT * FROM t1 WHERE x1>0 AND x1<100 AND x2>0 AND x2<100;
} {1 {undersize RTree blobs in "t1_node"}}
do_test rtreeA-7.120 {
sqlite3_extended_errcode db
} {SQLITE_CORRUPT}
finish_test
+86 -80
View File
@@ -1,10 +1,10 @@
C Version\s3.20.1
D 2017-08-24T16:21:36.477
C Fix\sa\stypo\sin\scsv01.test\sintroduced\sby\sthe\sprevious\scommit.
D 2017-08-10T19:12:04.643
F Makefile.in d9873c9925917cca9990ee24be17eb9613a668012c85a343aef7e5536ae266e8
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
F Makefile.msc 02b469e9dcd5b7ee63fc1fb05babc174260ee4cfa4e0ef2e48c3c6801567a016
F README.md f5c87359573c4d255425e588a56554b50fdcc2afba4e017a2e02a43701456afd
F VERSION 2fd9e47664e2bdb2b12a5fcced76c6768d35cd2730c4d8d5948638d6e1697176
F VERSION f81232df28e2d3ff049feefad5fbd5489cc33697f6bd2ecf61af7f0dde3b83d0
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90
@@ -30,7 +30,7 @@ F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63
F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977
F config.h.in 6376abec766e9a0785178b1823b5a587e9f1ccbc
F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55
F configure 2158ca3525085497feff8aa56386ee451bb0ebe49ef5d5fde3ac00e255fbc808 x
F configure 2500b432572804093c9a2516e2b16dbcec5d797ea1cd654915cfecd1d7a39ee3 x
F configure.ac 13f45f02e6c51dd0e347315b5401c3f047712b7f79b7f35619115c23755afcff
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
F doc/lemon.html 1f8b8d4c9f5cfe40e679fee279cc9eb2da8e6eb74ad406028538d7864cc4b6cb
@@ -249,10 +249,10 @@ F ext/lsm1/lsm_str.c 65e361b488c87b10bf3e5c0070b14ffc602cf84f094880bece77bbf6678
F ext/lsm1/lsm_tree.c 682679d7ef2b8b6f2fe77aeb532c8d29695bca671c220b0abac77069de5fb9fb
F ext/lsm1/lsm_unix.c 57361bcf5b1a1a028f5d66571ee490e9064d2cfb145a2cc9e5ddade467bb551b
F ext/lsm1/lsm_varint.c 43f954af668a66c7928b81597c14d6ad4be9fedbc276bbd80f52fa28a02fdb62
F ext/lsm1/lsm_vtab.c d5af32abe32601b3f2618b9488225db9ca06af803ddee1aaaf1653e08e9d112b
F ext/lsm1/lsm_vtab.c 976ded75b78be59fc358cf5c06641cda01a4bd0b52374419ab36bf37f646d5df
F ext/lsm1/lsm_win32.c 0a4acbd7e8d136dd3a5753f0a9e7a9802263a9d96cef3278cf120bcaa724db7c
F ext/lsm1/test/lsm1_common.tcl 5ed4bab07c93be2e4f300ebe46007ecf4b3e20bc5fbe1dedaf04a8774a6d8d82
F ext/lsm1/test/lsm1_simple.test 3bb38951450cd1f12a6c294949334d6fbb109a3da38c48eaf0877a37c43a0fab
F ext/lsm1/test/lsm1_simple.test 0ca15907fbfe09b6606f93149416786709e0cd55a69ecf53f605fadefa64228c
F ext/misc/README.md 8e008c8d2b02e09096b31dfba033253ac27c6c06a18aa5826e299fa7601d90b2
F ext/misc/amatch.c 6db4607cb17c54b853a2d7c7c36046d004853f65b9b733e6f019d543d5dfae87
F ext/misc/anycollseq.c 5ffdfde9829eeac52219136ad6aa7cd9a4edb3b15f4f2532de52f4a22525eddb
@@ -260,7 +260,7 @@ F ext/misc/carray.c ed96c218ea940b85c9a274c4d9c59fe9491c299147a38a8bba537687bd6c
F ext/misc/closure.c 0d2a038df8fbae7f19de42e7c7d71f2e4dc88704
F ext/misc/completion.c 52c3f01523e3e387eb321b4739a89d1fe47cbe6025aa1f2d8d3685e9e365df0f
F ext/misc/compress.c 122faa92d25033d6c3f07c39231de074ab3d2e83
F ext/misc/csv.c d91c0388445b08f6e373dd0e8fc024d4551b1fcaf64e876a1c3f4fac8a63adc2
F ext/misc/csv.c b10ea114cf29f446e384064ae61e01373b431f6f172896f41ca0ba3507709003
F ext/misc/dbdump.c 3509fa6b8932d04e932d6b6b827b6a82ca362781b8e8f3c77336f416793e215e
F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2
F ext/misc/fileio.c b1aa06c0f1dac277695d4529e5e976c65ab5678dcbb53a0304deaa8adc44b332
@@ -281,14 +281,15 @@ F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
F ext/misc/spellfix.c a4723b6aff748a417b5091b68a46443265c40f0d
F ext/misc/stmt.c 6f16443abb3551e3f5813bb13ba19a30e7032830015b0f92fe0c0453045c0a11
F ext/misc/totype.c 4a167594e791abeed95e0a8db028822b5e8fe512
F ext/misc/unionvtab.c 56fd163d2b6d2f4df0078be482fc9a874658ce51cce33f180c08834193449c78
F ext/misc/unionvtab.c 1e0ebc5078e1a916db191bcd88f87e94ea7ba4aa563ee30ff706261cb4b39461
F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95
F ext/misc/vfsstat.c bf10ef0bc51e1ad6756629e1edb142f7a8db1178
F ext/misc/vtablog.c 31d0d8f4406795679dcd3a67917c213d3a2a5fb3ea5de35f6e773491ed7e13c9
F ext/misc/vtshim.c 1976e6dd68dd0d64508c91a6dfab8e75f8aaf6cd
F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212
F ext/rbu/rbu.c ea7d1b7eb44c123a2a619332e19fe5313500705c4a58aaa1887905c0d83ffc2e
F ext/rbu/rbu1.test 43836fac8c7179a358eaf38a8a1ef3d6e6285842
F ext/rbu/rbu10.test 046b0980041d30700464a800bbf6733ed2df515d
F ext/rbu/rbu10.test 1846519a438697f45e9dcb246908af81b551c29e1078d0304fae83f1fed7e9ee
F ext/rbu/rbu11.test 9bc68c2d3dbeb1720153626e3bd0466dcc017702
F ext/rbu/rbu12.test bde22ed0004dd5d1888c72a84ae407e574aeae16
F ext/rbu/rbu13.test 462ff799c4afedc3ef8a47ff818c0ffbf14ae4f2
@@ -307,18 +308,18 @@ F ext/rbu/rbucrash.test 61470d977a06a0abc2ec35b05d82a1d7d87d10f4ffabad14c1c231ed
F ext/rbu/rbucrash2.test b2ecbdd7bb72c88bd217c65bd00dafa07f7f2d4d
F ext/rbu/rbudiff.test 3e605cf624d00d04d0fb1316a3acec4fbe3b3ac5
F ext/rbu/rbudor.test 99b05cc0df613e962c2c8085cfb05686a09cf315
F ext/rbu/rbufault.test cc0be8d5d392d98b0c2d6a51be377ea989250a89
F ext/rbu/rbufault.test 2654aef20f8ee7de37c9c1997a44f2773dc7bf24887adea39fb19314ef32cb90
F ext/rbu/rbufault2.test 9a7f19edd6ea35c4c9f807d8a3db0a03a5670c06
F ext/rbu/rbufault3.test 54a399888ac4af44c68f9f58afbed23149428bca
F ext/rbu/rbufault3.test 0913c1aeaee266d9c36c33179341a5a504aad7d423d1979cfec43c8346a29899
F ext/rbu/rbufault4.test 34e70701cbec51571ffbd9fbf9d4e0f2ec495ca7
F ext/rbu/rbufts.test 828cd689da825f0a7b7c53ffc1f6f7fdb6fa5bda
F ext/rbu/rbufts.test a2bbd202c9321fba15fb4a62a90add7d70e07bd8404e1e598135adbfff8a0508
F ext/rbu/rbuprogress.test 1849d4e0e50616edf5ce75ce7db86622e656b5cf
F ext/rbu/rburesume.test 8acb77f4a422ff55acfcfc9cc15a5cb210b1de83
F ext/rbu/rbusave.test 0f43b6686084f426ddd040b878426452fd2c2f48
F ext/rbu/rbuvacuum.test ff357e9b556ca7ad4673da0ff7f244def919ff858e0f9f350d3e30fdd83a62a8
F ext/rbu/rbuvacuum2.test 2074ab14fe66e1c7e7210c62562650dcd215bbaa
F ext/rbu/sqlite3rbu.c d1438580a451eebda3bfd42ef69b677512f00125285e0e4e789b6131a45f6dd8
F ext/rbu/sqlite3rbu.h fc25e1fcd99b5c6d32b1b5b1c73122632e873ac89bd0be9bf646db362b7ce02c
F ext/rbu/sqlite3rbu.c 920941a6ff7dbbea0970717c43662878fda5c37e43752de329f0fdd76680ab75
F ext/rbu/sqlite3rbu.h 82c102e5ae41025e3b245d3d5944315f82811da85e2cd363a75caa97cbd0cd3e
F ext/rbu/test_rbu.c ec18cfc69a104309df23c359e3c80306c9a6bdd1d2c53c8b70ae158e9832dcd6
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
F ext/rtree/rtree.c 4f1804b80ae06ddf7ff69192aacdceee283646dc6a328acb951f116147445212
@@ -332,7 +333,7 @@ F ext/rtree/rtree6.test 773a90db2dce6a8353dd0d5b64bca69b29761196
F ext/rtree/rtree7.test 1fa710b9e6bf997a0c1a537b81be7bb6fded1971
F ext/rtree/rtree8.test 076d9d5b783b61b7a23a5ab45fc899551dfffd821974f36ee599ff29f4de7a61
F ext/rtree/rtree9.test 8bfa84dfaba1c897468a2448c28db0a00ad12d464225b5993c7814e907f3776f
F ext/rtree/rtreeA.test e25d76c1701f8591e7a0b6de8224d5dbc1418c562654c7240e6f33f37b1e36f7
F ext/rtree/rtreeA.test abb1e2434defc8cdc5e3195a18ead3681cae04565c06069251d1998796e77d55
F ext/rtree/rtreeB.test c85f9ce78766c4e68b8b89fbf2979ee9cfa82b4e
F ext/rtree/rtreeC.test c0a9c67f2efa98b6fae12acb8a28348d231a481d
F ext/rtree/rtreeD.test fe46aa7f012e137bd58294409b16c0d43976c3bb92c8f710481e577c4a1100dc
@@ -388,43 +389,43 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
F src/alter.c 850ede4e607f12fa25ea4f3cb6ece2b2e29d1aa50e3f786ce49d615788849552
F src/alter.c cf7a8af45cb0ace672f47a1b29ab24092a9e8cd8d945a9974e3b5d925f548594
F src/analyze.c 0d0ccf7520a201d8747ea2f02c92c26e26f801bc161f714f27b9f7630dde0421
F src/attach.c 3bd555e28382603e80d430dfebb2270f86e1e375b4c4be3e1ab1aec3a0c44943
F src/attach.c 07b706e336fd3cedbd855e1f8266d10e82fecae07daf86717b5760cd7784c584
F src/auth.c 79f96c6f33bf0e5da8d1c282cee5ebb1852bb8a6ccca3e485d7c459b035d9c3c
F src/backup.c faf17e60b43233c214aae6a8179d24503a61e83b
F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33
F src/btmutex.c 0e9ce2d56159b89b9bc8e197e023ee11e39ff8ca
F src/btree.c f55ea8f456d103328d61076be40fa39acbfea05eaa4eccfed275532a63c867c4
F src/btree.c 1a17ba1a765d80c3ca39ce33ff55f92e1f51eb84bbbdab5377f11d36b1515fa1
F src/btree.h 3edc5329bc59534d2d15b4f069a9f54b779a7e51289e98fa481ae3c0e526a5ca
F src/btreeInt.h 97700795edf8a43245720414798b7b29d8e465aef46bf301ffacd431910c0da1
F src/build.c c3837ad57cea1351cd824a646836d2bb678f264ae160d691b632d495c8be2f5b
F src/callback.c 8e14b60d1ed1c87c02cb5f121ecda99224f2aea6524a77ee6f72c9b5c7110f84
F src/build.c 33b0f6055bd990ed052b96e71368acefcd98daa21ccf21f91aa90e8b769c2219
F src/callback.c 930648a084a3adc741c6471adfbdc50ba47ba3542421cb80a26f259f467de65e
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
F src/ctime.c 928954802b1397d9fb1378c7eb702c94b4735bbab1d5793e21b6a77734f56a1b
F src/ctime.c ff1be3eed7bdd75aaca61ca8dc848f7c9f850ef2fb9cb56f2734e922a098f9c0
F src/date.c 48f743d88bbe88f848532d333cca84f26e52a4f217e86f86be7fc1b919c33d74
F src/dbstat.c 7a4ba8518b6369ef3600c49cf9c918ad979acba610b2aebef1b656d649b96720
F src/delete.c 939bd15e6b54b82b951e1c0ffc2ff2b4ab579196780a1f6d394e47bd6f799b6c
F src/delete.c 21a5f1812fdb599e9f7afb9f650bdabab60a3afd51d7e94e539c982f647b0023
F src/expr.c fdb2fc465cabbf372fecad1fc2b291758bec74150b4db0fb945332e09df28a0e
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c 5ff2c895fe087756d8085dc1a9bc229b5670e2a65c3929dd87c71e43649af333
F src/func.c e2854b19386b93ad6b498a3f3b7d6baa98ec14cfe84530fb12fce4414263d871
F src/func.c ed8888ae80b39f5a5d403954e4a05e0a38303523dff8143161439c142d31dec1
F src/global.c 8a6ab6b4d91effb96ffa81b39f0d70c862abca157f8aaa194600a4a8b7923344
F src/hash.c a12580e143f10301ed5166ea4964ae2853d3905a511d4e0c44497245c7ce1f7a
F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4
F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
F src/insert.c 487d2c1cd8d549f8d9492377ecc4d2f507b1bd5185e96c54389f6b607dce854c
F src/insert.c d2d1bf12d2b5382450620d7cede84c7ffe57e6a89fa9a908f1aba68df2731cd9
F src/legacy.c 134ab3e3fae00a0f67a5187981d6935b24b337bcf0f4b3e5c9fa5763da95bf4e
F src/loadext.c 20865b183bb8a3723d59cf1efffc3c50217eb452c1021d077b908c94da26b0b2
F src/main.c 3a9da9e3974d8a32ef6ca15b75503d540af22d284beb75bc7f0d93254ca3f8f7
F src/main.c 42f6a2660c7a1d643cc7e863d2dcd630c6aa1e8343f5478b0592120ab84c97ba
F src/malloc.c e20bb2b48abec52d3faf01cce12e8b4f95973755fafec98d45162dfdab111978
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de
F src/mem2.c f1940d9e91948dd6a908fbb9ce3835c36b5d83c3
F src/mem3.c 8768ac94694f31ffaf8b4d0ea5dc08af7010a35a
F src/mem5.c 9bf955937b07f8c32541c8a9991f33ce3173d944
F src/memjournal.c 95752936c11dc6995672d1dd783cd633eea0cc95
F src/memjournal.c 6f3d36a0a8f72f48f6c3c722f04301ac64f2515435fa42924293e46fc7994661
F src/msvc.h 4942752b6a253116baaa8de75256c51a459a5e81
F src/mutex.c 8e45800ee78e0cd1f1f3fe8e398853307f4a085c
F src/mutex.h 779d588e3b7756ec3ecf7d78cde1d84aba414f85
@@ -436,39 +437,39 @@ F src/os.c add02933b1dce7a39a005b00a2f5364b763e9a24
F src/os.h 8e976e59eb4ca1c0fca6d35ee803e38951cb0343
F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85
F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586
F src/os_unix.c 30e2c43e4955db990e5b5a81e901f8aa74cc8820
F src/os_win.c 2a6c73eef01c51a048cc4ddccd57f981afbec18a
F src/os_unix.c a361273749229755f92c8f0e3e4855054ad39bbc5c65773e8db5d0b79afa632c
F src/os_win.c 964165b66cde03abc72fe948198b01be608436894732eadb94c8720d2467f223
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c 14f6982c470c05b8e85575c69e9c1712010602e20400f8670d8699e21283e0e4
F src/pager.c 1e63b0299cf123cf38c48413ec03190f56c1e7d0ccc6573c467d8ac240b898e9
F src/pager.h f2a99646c5533ffe11afa43e9e0bea74054e4efa
F src/parse.y e384cb73f99e1b074085c974b37f4d830e885359e4b60837e30f7d67c16ba65b
F src/parse.y 52ef3cecd0934e9da4a45b585883a03243ad615d338ad94f44501a05891dcdfa
F src/pcache.c 62835bed959e2914edd26afadfecce29ece0e870
F src/pcache.h 521bb9610d38ef17a3cc9b5ddafd4546c2ea67fa3d0e464823d73c2a28d50e11
F src/pcache1.c 1195a21fe28e223e024f900b2011e80df53793f0356a24caace4188b098540dc
F src/pcache1.c 0b793738b5dddaf0a645784835c6b5557b1ecfaee339af9c26810c6ecdb273aa
F src/pragma.c cd6aeda3587be6c5c08f9b2d45eae6068666a03c9d077c8c43cdb85fb0aa70f2
F src/pragma.h bb83728944b42f6d409c77f5838a8edbdb0fe83046c5496ffc9602b40340a324
F src/prepare.c dd250f904739b1dc449c131ac527c35e3424d94082dd111321bd83f80c6bb0fe
F src/prepare.c 3cbb99757d7295997674972f9dd2331c5c544368854ca08954c9beb1e9b6145a
F src/printf.c 8757834f1b54dae512fb25eb1acc8e94a0d15dd2290b58f2563f65973265adb2
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
F src/resolve.c 4324a94573b1e29286f8121e4881db59eaedc014afeb274c8d3e07ed282e0e20
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
F src/select.c 31b35ddf55f1021f7148a01306984b057c11ebb6e3463d94677225e0a1e301a3
F src/select.c 3fd19c98c5223d411b883502d1ac928ddb762a1ea8f031d910210316545fc67c
F src/shell.c bd6a37cbe8bf64ef6a6a74fdc50f067d3148149b4ce2b4d03154663e66ded55f
F src/shell.c.in b5725acacba95ccefa57b6d068f710e29ba8239c3aa704628a1902a1f729c175
F src/sqlite.h.in a7e29d5977916cded93ff6cf0d56ada657e13e91adbc2039e5cf02e41122a45f
F src/sqlite.h.in 72f1775c7a134f9e358eedafe1ebc703c28b0d705d976464ddbf6a9219448952
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 0f9f72b86a3792314f5db7a1dfbc2c82376bcd8d0919ceb80637bca126ec3c68
F src/sqliteInt.h bd6be75bc43d38ada272ef0b3472bc44ef0cc15536bea22c349ca1a2812a19ce
F src/sqlite3ext.h a1fd3aa82f967da436164e0728a7d6841651fd0c6e27b9044e0eb9f6c8462e47
F src/sqliteInt.h 07e4d3c8021aea80e3bbafab4dd52833cfcfa4f000210af0d15c7fdaed2f09fc
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
F src/tclsqlite.c 2c29b0b76e91edfd1b43bf135c32c8674710089197327682b6b7e6af88062c3d
F src/test1.c cfb78b728b37ae3a2b14fe1b3a6c766e0da41370eda112594e698c94011b622e
F src/tclsqlite.c 487951d81f9704800fd9f0ffdaa2f935a83ccb6be3575c2c4ef83e4789b4c828
F src/test1.c 8513b17ca4a7a9ba28748535d178b6e472ec7394ae0eea53907f2d3bcdbab2df
F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5
F src/test3.c b8434949dfb8aff8dfa082c8b592109e77844c2135ed3c492113839b6956255b
F src/test4.c 18ec393bb4d0ad1de729f0b94da7267270f3d8e6
F src/test5.c 328aae2c010c57a9829d255dc099d6899311672d
F src/test6.c 004ad42f121f693b8cbe060d1a330678abc61620
F src/test6.c e8d839fbc552ce044bec8234561a2d5b8819b48e29548ad0ba400471697946a8
F src/test7.c 5612e9aecf934d6df7bba6ce861fdf5ba5456010
F src/test8.c 4f4904721167b32f7a4fa8c7b32a07a673d6cc86
F src/test9.c 12e5ba554d2d1cbe0158f6ab3f7ffcd7a86ee4e5
@@ -481,7 +482,7 @@ F src/test_btree.c 8b2dc8b8848cf3a4db93f11578f075e82252a274
F src/test_config.c abf6fc1fe9d041b699578c42e3db81f8831c4f5b804f1927958102ee8f2b773e
F src/test_delete.c e2fe07646dff6300b48d49b2fee2fe192ed389e834dd635e3b3bac0ce0bf9f8f
F src/test_demovfs.c a0c3bdd45ed044115c2c9f7779e56eafff18741e
F src/test_devsym.c 4e58dec2602d8e139ca08659f62a62450587cb58
F src/test_devsym.c 1960abbb234b97e9b920f07e99503fc04b443f62bbc3c6ff2c2cea2133e3b8a2
F src/test_fs.c 35a2f7dd8a915900873386331386d9ba1ae1b5026d74fd20c2807bc76221f291
F src/test_func.c a4fdab3363b436c1b12660e9362ce3f3782b7b5e
F src/test_hexio.c 1d4469ca61ab202a1fcec6543f584d2407205e8d
@@ -505,7 +506,7 @@ F src/test_server.c a2615049954cbb9cfb4a62e18e2f0616e4dc38fe
F src/test_sqllog.c 11e6ce7575f489155c604ac4b439f2ac1d3d5aef
F src/test_superlock.c 4839644b9201da822f181c5bc406c0b2385f672e
F src/test_syscall.c 1073306ba2e9bfc886771871a13d3de281ed3939
F src/test_tclvar.c df9fe1213c2634687a9ca0b0bec0d2119d359ae3
F src/test_tclvar.c 33ff42149494a39c5fbb0df3d25d6fafb2f668888e41c0688d07273dcb268dfc
F src/test_thread.c 911d15fb14e19c0c542bdc8aabf981c2f10a4858
F src/test_vfs.c f0186261a24de2671d080bcd8050732f0cb64f6e
F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698
@@ -515,29 +516,29 @@ F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c 1003d6d90c6783206c711f0a9397656fa5b055209f4d092caa43bb3bf5215db5
F src/treeview.c 2ee4a5dada213d5ab08a742af5c876cee6f1aaae65f10a61923f3fb63846afef
F src/trigger.c d1cae560bfacc8bfb3a072d73658245c1714c0389097da69b4cb23877a082d7e
F src/update.c c443935c652af9365e033f756550b5032d02e1b06eb2cb890ed7511ae0c051dc
F src/trigger.c 48e0f7ed6749ce4d50a695e09e20ce9cf84ecabf2691852c965a51e0b620eccc
F src/update.c 5404be9e840717323a69209190cdbc9d0d34adaedaaf1d1a1069babf2c4171c0
F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5
F src/util.c fc081ec6f63448dcd80d3dfad35baecfa104823254a815b081a4d9fe76e1db23
F src/vacuum.c 874c0f2f15ab2908748297d587d22d485ea96d55aaec91d4775dddb2e24d2ecf
F src/vdbe.c 1e541ec7ff409bbabcc6b4f154957296fff5827c16c2ab0056348acae75685bf
F src/vacuum.c 90839322fd5f00df9617eb21b68beda9b6e2a2937576b0d65985e4aeb1c53739
F src/vdbe.c 821b3edde2d17ec60da0617db1018a88f38634c359d22f3c8f7be10336c82636
F src/vdbe.h d50cadf12bcf9fb99117ef392ce1ea283aa429270481426b6e8b0280c101fd97
F src/vdbeInt.h ff2b7db0968d20e6184aee256d2e535d565f5a172e3588a78adb166a41fc4911
F src/vdbeapi.c d362429ebb02df609a71a24abb295406b47252a3caa231d0bd0d715033b5d362
F src/vdbeaux.c 3fe68bad02b33b09e08bdc0ad90d6b92b3d571f7864c3d047abca1bde050751c
F src/vdbeblob.c 359891617358deefc85bef7bcf787fa6b77facb9
F src/vdbemem.c 9ca2854976f35db40341977e688a08204c96427505f5b90215dc7970f6ea42c4
F src/vdbesort.c f512c68d0bf7e0105316a5594c4329358c8ee9cae3b25138df041d97516c0372
F src/vdbeapi.c 05d6b14ab73952db0d73f6452d6960216997bd966a710266b2fe051f25326abc
F src/vdbeaux.c 1bef372f59f9e1dba5ead70cc5c24bf978bab0b9fdc2f69692afaa3a2d4dd8f3
F src/vdbeblob.c db3cf91060f6f4b2f1358a4200e844697990752177784c7c95da00b7ac9f1c7b
F src/vdbemem.c b7fac20534c79b7554dab2e8a180c585a8bc1b9c85149d1b2d9746cf314d06ed
F src/vdbesort.c fea2bea25f5e9ccd91e0760d7359f0365f9fba1aaeac7216c71cad78765f58e3
F src/vdbetrace.c 41963d5376f0349842b5fc4aaaaacd7d9cdc0834
F src/vtab.c 35b9bdc2b41de32a417141d12097bcc4e29a77ed7cdb8f836d1d2305d946b61b
F src/vtab.c f1d5c23132fb0247af3e86146404112283ddedb6c518de0d4edc91cfb36970ef
F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c 40c543f0a2195d1b0dc88ef12142bea690009344
F src/wal.h 06b2a0b599cc0f53ea97f497cf8c6b758c999f71
F src/walker.c a7ca64ce08a83a20d32186fbe06bca9234e348cfcf07959ee322fdc3e8a6173a
F src/where.c cbe8ddffbcec7ce86f7a800fe8fd10aee412c76c87e0dd3732a1682e68d74cd9
F src/whereInt.h 93bb90b77d39901eda31b44d8e90da1351193ccfe96876f89b58a93a33b84c3d
F src/wherecode.c c0c4c31573486cd14bb2cbfc63e41eda591609e5190416261999f211bf97abc1
F src/whereexpr.c bf983d2d33e325cd63a36c40b8de289fd3d7b4d9f2db9052fb8f59f7161a34a0
F src/wherecode.c e7be3b7f4c11908500cdf02b299d190d3742659533f58e0f4047962fdb5a48da
F src/whereexpr.c 1e55d79174522fe3b9b7ab224ebedc03da6ec5e6d204d740fa73e71280f54574
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d
@@ -572,8 +573,9 @@ F test/async3.test d73a062002376d7edc1fe3edff493edbec1fc2f7
F test/async4.test 1787e3952128aa10238bf39945126de7ca23685a
F test/async5.test 383ab533fdb9f7ad228cc99ee66e1acb34cc0dc0
F test/atof1.test ff0b0156fd705b67c506e1f2bfe9e26102bea9bd
F test/atomic.test 065a453dde33c77ff586d91ccaa6ed419829d492dbb1a5694b8a09f3f9d7d061
F test/attach.test f4b8918ba2f3e88e6883b8452340545f10a1388af808343c37fc5c577be8281c
F test/attach2.test 0ec5defa340363de6cd50fd595046465e9aaba2d
F test/attach2.test 567047a7607aae8ebb3794642ebb168abe66b4af366fcd0cf7f616a1495cd43f
F test/attach3.test c59d92791070c59272e00183b7353eeb94915976
F test/attach4.test 53bf502f17647c6d6c5add46dda6bac8b6f4665c
F test/attachmalloc.test 3a4bfca9545bfe906a8d2e622de10fbac5b711b0
@@ -607,6 +609,7 @@ F test/bestindex4.test 4cb5ff7dbaebadb87d366f51969271778423b455
F test/between.test 34d375fb5ce1ae283ffe82b6b233e9f38e84fc6c
F test/bigfile.test aa74f4e5db51c8e54a1d9de9fa65d01d1eb20b59
F test/bigfile2.test 1b489a3a39ae90c7f027b79110d6b4e1dbc71bfc
F test/bigmmap.test ed6058a7794be26865c94d5bb62e12cdc4f7f01562b3b04f13eb3cdc52783921
F test/bigrow.test f0aeb7573dcb8caaafea76454be3ade29b7fc747
F test/bigsort.test 8299fa9298f4f1e02fc7d2712e8b77d6cd60e5a2
F test/bind.test 1e136709b306f7ed3192d349c2930d89df6ab621654ad6f1a72381d3fe76f483
@@ -692,7 +695,7 @@ F test/crashM.test d95f59046fa749b0d0822edf18a717788c8f318d
F test/crashtest1.c 09c1c7d728ccf4feb9e481671e29dda5669bbcc2
F test/createtab.test b5de160630b209c4b8925bdcbbaf48cc90b67fe8
F test/cse.test 277350a26264495e86b1785f34d2d0c8600e021c
F test/csv01.test e0ba3caaa57e4c667a0b45977689fb8082f14348
F test/csv01.test 526fc6aefd052badd5a0283f86b4b395c3df76bfe98d96c801f494f5e2c7836c
F test/ctime.test 78749e6c9a5f0010d67985be80788f841e3cd2da18114e2ed6010399a7d807f3
F test/cursorhint.test 7bc346788390475e77a345da2b92270d04d35856
F test/cursorhint2.test 8457e93d97f665f23f97cdbc8477d16e3480331b
@@ -754,7 +757,7 @@ F test/exists.test 79a75323c78f02bbe9c251ea502a092f9ef63dac
F test/expr.test 66a2c9ac34f74f036faa4092f5402c7d3162fc93
F test/extension01.test 00d13cec817f331a687a243e0e5a2d87b0e358c9
F test/extraquick.test cb254400bd42bfb777ff675356aabf3287978f79
F test/fallocate.test 3e979af17dfa7e5e9dda5eba1a696c04fa9d47f7
F test/fallocate.test 87b5e43c872b7e69cd80b7b8813eb102b571a75d45dda24e38b65537bcc85733
F test/filectrl.test 6e871c2d35dead1d9a88e176e8d2ca094fec6bb3
F test/filefmt.test f393e80c4b8d493b7a7f8f3809a8425bbf4292af1f5140f01cb1427798a2bbd4
F test/fkey1.test ba64806ff9a04eecab2679caad377ae99a5e94e4
@@ -978,7 +981,7 @@ F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff
F test/kvtest.c d2b8cfc91047ebf6cac4f3a04f19c3a864e4ecfd683bbb65c395df450b8dc79c
F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63
F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200
F test/like.test 3d26ae14d7042a0e96f7b0b9e9ad2c8ca6ed122772439c6b1c691fe167e15a37
F test/like.test 67d7431c9b664254febce9e90fd2f47c7c75c8b38444e2a50ef9ec2776b84ca8
F test/like2.test 3b2ee13149ba4a8a60b59756f4e5d345573852da
F test/like3.test 3608a2042b6f922f900fbfd5d3ce4e7eca57f7c4
F test/limit.test 0c99a27a87b14c646a9d583c7c89fd06c352663e
@@ -1029,7 +1032,7 @@ F test/minmax.test 6751e87b409fe11b02e70a306d846fa544e25a41
F test/minmax2.test b44bae787fc7b227597b01b0ca5575c7cb54d3bc
F test/minmax3.test cc1e8b010136db0d01a6f2a29ba5a9f321034354
F test/minmax4.test 936941484ebdceb8adec7c86b6cd9b6e5e897c1f
F test/misc1.test 6430dabfb4b4fa480633590118964201f94d3ccc
F test/misc1.test 51ec3f56a2a7965de226964cff856695e743186826561536647f1e8b7d1d0eb3
F test/misc2.test 00d7de54eda90e237fc9a38b9e5ccc769ebf6d4d
F test/misc3.test cf3dda47d5dda3e53fc5804a100d3c82be736c9d
F test/misc4.test 0d8be3466adf123a7791a66ba2bc8e8d229e87f3
@@ -1071,7 +1074,7 @@ F test/orderby7.test 3d1383d52ade5b9eb3a173b3147fdd296f0202da
F test/orderby8.test 23ef1a5d72bd3adcc2f65561c654295d1b8047bd
F test/orderby9.test 87fb9548debcc2cd141c5299002dd94672fa76a3
F test/oserror.test b32dc34f2363ef18532e3a0a7358e3e7e321974f
F test/ossfuzz.c f5abed3177f719df3c3109901fcdd26b9fb7f581c8da50fc26f3a81ddfb2c2ae
F test/ossfuzz.c 7f5cc87a0280a5854c1bfa7d5c4d07d34731f08ec34dc9c916aa35ed292b1468
F test/ossshell.c 296ab63067841bd1b1e97b46a0b2af48ee7f69d50d1a723008bee12dd7122622
F test/ovfl.test 199c482696defceacee8c8e0e0ef36da62726b2f
F test/pager1.test 8149b2a8986fee667ab6a8171ab310be19e77ae215bebad0e90c857b0df1935c
@@ -1087,7 +1090,7 @@ F test/parser1.test 391b9bf9a229547a129c61ac345ed1a6f5eb1854
F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
F test/permutations.test 5e2e5439642898e0947ced066ad09b82bd817ddfb83dc71291b4c957efc84b62
F test/permutations.test 3b94f8fd431d39fac4952eb5dc38e1bb2b4518e1ac967d66f5abc815c104aeb6
F test/pragma.test f274259d6393b6681eb433beb8dd39a26ec06a4431052a4880b43b84912a3f58
F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
F test/pragma3.test 14c12bc5352b1e100e0b6b44f371053a81ccf8ed
@@ -1113,7 +1116,7 @@ F test/regexp2.test 40e894223b3d6672655481493f1be12012f2b33c
F test/reindex.test 44edd3966b474468b823d481eafef0c305022254
F test/releasetest.tcl 7bb585433ce7fb2a2c255ae4b5e24f1bc27fe177ec1120f886cc4852f48f5ee9 x
F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb
F test/rollback.test 458fe73eb3ffdfdf9f6ba3e9b7350a6220414dea
F test/rollback.test f580934279800d480a19176c6b44909df31ce7ad45267ea475a541daa522f3d3
F test/rollback2.test 8435d6ff0f13f51d2a4181c232e706005fa90fc5
F test/rollbackfault.test 0e646aeab8840c399cfbfa43daab46fd609cf04a
F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc
@@ -1131,7 +1134,7 @@ F test/rowvalue9.test d8dd2c6ecac432dadaa79e41dc2434f007be1b6b
F test/rowvaluefault.test 7cd9ccc6c2fbdd881672984087aad0491bb75504
F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798
F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09
F test/savepoint.test c671fdbd34cd3bfe1518a777526ada595180cf8d
F test/savepoint.test 1f8a6b1aea9a0d05837adc463d4bf47bd9d0f1c842f1c2a9caccd639baf34bf9
F test/savepoint2.test 9b8543940572a2f01a18298c3135ad0c9f4f67d7
F test/savepoint4.test c8f8159ade6d2acd9128be61e1230f1c1edc6cc0
F test/savepoint5.test 0735db177e0ebbaedc39812c8d065075d563c4fd
@@ -1144,6 +1147,7 @@ F test/schema2.test 906408621ea881fdb496d878b1822572a34e32c5
F test/schema3.test 1bc1008e1f8cb5654b248c55f27249366eb7ed38
F test/schema4.test 3b26c9fa916abb6dadf894137adcf41b7796f7b9
F test/schema5.test 29699b4421f183c8f0e88bd28ce7d75d13ea653e
F test/schema6.test 5b21bbdd405bc93b3e6af5e6ece64d230e35f65cc4035e5c2b89fc8a090d7270
F test/securedel.test 5f997cb6bd38727b81e0985f53ec386c99db6441b2b9e6357240649d29017239
F test/securedel2.test 2d54c28e46eb1fd6902089958b20b1b056c6f1c5
F test/select1.test be62204d2bd9a5a8a149e9974cfddce893d8f686
@@ -1229,10 +1233,13 @@ F test/subselect.test 0966aa8e720224dbd6a5e769a3ec2a723e332303
F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a
F test/subtype1.test 7fe09496352f97053af1437150751be2d0a0cae8
F test/superlock.test ec94f0556b6488d97f71c79f9061ae08d9ab8f12
F test/swarmvtab.test 05c4ca7b6ab0cc6f4c335a510347f99d741fa71366004699cf7dfa3cff4e2d17
F test/swarmvtab2.test 038ef9bcad6fd2fb9e395196080cf23e223ddb1219015049a61540c161bc577d
F test/swarmvtabfault.test 73563eefe3073c6fb3bb14475fb4ef5d4f2e3a67a02947ee0ca08980ea3dd7fe
F test/symlink.test c9ebe7330d228249e447038276bfc8a7b22f4849
F test/sync.test 2f84bdbc2b2df1fcb0220575b4b9f8cea94b7529
F test/sync2.test 6be8ed007fa063b147773c1982b5bdba97a32badc536bdc6077eff5cf8710ece
F test/syscall.test 7a60601770172a8014a4d222d5f3d95a5d2b5c47fbb0374e2698e89c99e37256
F test/syscall.test a39d9a36f852ae6e4800f861bc2f2e83f68bbc2112d9399931ecfadeabd2d69d
F test/sysfault.test c9f2b0d8d677558f74de750c75e12a5454719d04
F test/tabfunc01.test c47171c36b3d411df2bd49719dcaa5d034f8d277477fd41d253940723b969a51
F test/table.test b708f3e5fa2542fa51dfab21fc07b36ea445cb2f
@@ -1246,7 +1253,7 @@ F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30
F test/temptable2.test cd396beb41117a5302fff61767c35fa4270a0d5e
F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637
F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc
F test/tester.tcl 581f0185434daf7026ccede4c07e8d1479186ec5
F test/tester.tcl eb7ec55fe074a909423c1de701f7c545417b8aa96787b8c3e7a79203f2cebec8
F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
@@ -1434,7 +1441,7 @@ F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff
F test/types2.test 1aeb81976841a91eef292723649b5c4fe3bc3cac
F test/types3.test 99e009491a54f4dc02c06bdbc0c5eea56ae3e25a
F test/unionvtab.test 595fb601de00188ca5e7c6dbe266c17a0faf234cf434ce85eccec1a929ef9baf
F test/unionvtabfault.test 18f4f878e550ae3c5efa195b1420c78607c0f9810f7a71a49ab6379dfe501f32
F test/unionvtabfault.test 26b6854d5aef9005cd630513025690bff1b7378ae9c97b81e2a3cbe84eee0f58
F test/unique.test 93f8b2ef5ea51b9495f8d6493429b1fd0f465264
F test/unique2.test 3674e9f2a3f1fbbfd4772ac74b7a97090d0f77d2
F test/unixexcl.test d936ba2b06794018e136418addd59a2354eeae97
@@ -1455,7 +1462,7 @@ F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102
F test/veryquick.test 57ab846bacf7b90cf4e9a672721ea5c5b669b661
F test/view.test 765802c7a66d37fabd5ac8e2f2dbe572b43eb9ab
F test/vtab1.test 8f91b9538d1404c3932293a588c4344218a0c94792d4289bb55e41020e7b3fff
F test/vtab2.test f8cd1bb9aba7143eba97812d9617880a36d247ad
F test/vtab2.test 14d4ab26cee13ba6cf5c5601b158e4f57552d3b055cdd9406cf7f711e9c84082
F test/vtab3.test b45f47d20f225ccc9c28dc915d92740c2dee311e
F test/vtab4.test 8e73ed268f3d596bc3590f45fc948fb40f28e9c3
F test/vtab5.test 889f444970393c73f1e077e2bdc5d845e157a391
@@ -1467,10 +1474,11 @@ F test/vtabA.test 1317f06a03597eee29f40a49b6c21e1aaba4285f
F test/vtabB.test 04df5dc531b9f44d9ca65b9c1b79f12b5922a796
F test/vtabC.test 4528f459a13136f982e75614d120aef165f17292
F test/vtabD.test 05b3f1d77117271671089e48719524b676842e96
F test/vtabE.test d5024aa42754962f6bb0afd261681686488e7afe
F test/vtabE.test 2a143fe75a11275781d1fd1988d86b66a3f69cb98f4add62e3da8fd0f637b45f
F test/vtabF.test 1918844c7c902f6a16c8dacf1ec8f84886d6e78b
F test/vtabH.test 26d54e8b5407f797638b787a55f9c88323850a58dd142de02d06b9a1159bd283
F test/vtabH.test 3cf9aa1c1c4381b3b3ac33f933376f06fbb99d2294a83c79b7562d3ed87be450
F test/vtabI.test 751b07636700dbdea328e4265b6077ccd6811a3f
F test/vtabJ.test d7b73675708cf63cfcb9d443bb451fc01a028347275b7311e51f9fdf3ca6757f
F test/vtab_alter.test 736e66fb5ec7b4fee58229aa3ada2f27ec58bc58c00edae4836890c3784c6783
F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8
F test/vtab_shared.test 5253bff2355a9a3f014c15337da7e177ab0ef8ad
@@ -1513,7 +1521,7 @@ F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b
F test/where7.test f520bcec2c3d12dc4615623b06b2aec7c2d67e94
F test/where8.test 98eedca0d375fb400b8377269c4b4686582dfb45
F test/where9.test 729c3ba9b47e8f9f1aab96bae7dad2a524f1d1a2
F test/whereA.test 4d253178d135ec46d1671e440cd8f2b916aa6e6b
F test/whereA.test 6c6a420ca7d313242f9b1bd471dc80e4d0f8323700ba9c78df0bb843d4daa3b4
F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5
F test/whereC.test cae295158703cb3fc23bf1a108a9ab730efff0f6
F test/whereD.test 711d4df58d6d4fb9b3f5ce040b818564198be002
@@ -1542,12 +1550,13 @@ F test/without_rowid4.test 4e08bcbaee0399f35d58b5581881e7a6243d458a
F test/without_rowid5.test 89b1c587bd92a0590e440da33e7666bf4891572a
F test/without_rowid6.test 1f99644e6508447fb050f73697350c7ceca3392e
F test/wordcount.c 06efb84b7c48a4973c2c24ea06c93d00bce24389
F test/writecrash.test f1da7f7adfe8d7f09ea79b42e5ca6dcc41102f27f8e334ad71539501ddd910cc
F test/zeroblob.test 3857870fe681b8185654414a9bccfde80b62a0fa
F test/zerodamage.test e59a56443d6298ecf7435f618f0b27654f0c849e
F tool/GetFile.cs a15e08acb5dd7539b75ba23501581d7c2b462cb5
F tool/GetTclKit.bat 8995df40c4209808b31f24de0b58f90930239a234f7591e3675d45bfbb990c5d
F tool/Replace.cs 02c67258801c2fb5f63231e0ac0f220b4b36ba91
F tool/addopcodes.tcl edbd53806bf20e25af2373ad0c091be4385081c1aa1813b916bf093f94ed8380
F tool/addopcodes.tcl 7181c041d495e3f26acc36d15c86923ed722285f9015f017f41a3efdb9a0dab4
F tool/build-all-msvc.bat c12328d06c45fec8baada5949e3d5af54bf8c887 x
F tool/build-shell.sh 950f47c6174f1eea171319438b93ba67ff5bf367
F tool/cg_anno.tcl f95b0006c52cf7f0496b506343415b6ee3cdcdd3 x
@@ -1561,7 +1570,7 @@ F tool/genfkey.README cf68fddd4643bbe3ff8e31b8b6d8b0a1b85e20f4
F tool/genfkey.test 4196a8928b78f51d54ef58e99e99401ab2f0a7e5
F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
F tool/kvtest-speed.sh 4761a9c4b3530907562314d7757995787f7aef8f
F tool/lemon.c 5a04dff28578a67415cea5bf981b893c50cebfdd4388fb21254d1892525edfd8
F tool/lemon.c e6056373044d55296d21f81467dba7632bbb81dc49af072b3f0e76338771497e
F tool/lempar.c 10579a61dc2290182725e7abdefe311dd8b521a8f7f0aabbfc571e9012a09eaf
F tool/libvers.c caafc3b689638a1d88d44bc5f526c2278760d9b9
F tool/loadfts.c c3c64e4d5e90e8ba41159232c2189dba4be7b862
@@ -1572,13 +1581,13 @@ F tool/mkctimec.tcl dd183b73ae1c28249669741c250525f0407e579a70482371668fd5f130d9
F tool/mkkeywordhash.c 2e852ac0dfdc5af18886dc1ce7e9676d11714ae3df0a282dc7d90b3a0fe2033c
F tool/mkmsvcmin.tcl cbd93f1cfa3a0a9ae56fc958510aa3fc3ac65e29cb111716199e3d0e66eefaa4
F tool/mkopcodec.tcl d1b6362bd3aa80d5520d4d6f3765badf01f6c43c
F tool/mkopcodeh.tcl bb04ab6e5e2000c91e0c69a597e7e36e002320d123e2e1944cb2819181b72ee9
F tool/mkopcodeh.tcl 4ee2a30ccbd900dc4d5cdb61bdab87cd2166cd2affcc78c9cc0b8d22a65b2eee
F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e
F tool/mkpragmatab.tcl 2144bc8550a6471a029db262a132d2df4b9e0db61b90398bf64f5b7b3f8d92cd
F tool/mkshellc.tcl 69c38ecd7b74b2b0799a35ce20e1e3998e504d8c99c100ca4b98ae9d8f6279bc
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
F tool/mksqlite3c-noext.tcl fef88397668ae83166735c41af99d79f56afaabb
F tool/mksqlite3c.tcl f6214285bec900d28441366ca31af327aade18bbc424b0480497966ec05bc43c
F tool/mksqlite3c.tcl a4b36eaa002ed00a0ab2c93d999a14f1acae98ff09a85382e5abc05a91edb82b
F tool/mksqlite3h.tcl 51bd5e7e840a920388a5966c9f2ccc618f434c57bd68c1bab4085b2553e1e237
F tool/mksqlite3internalh.tcl eb994013e833359137eb53a55acdad0b5ae1049b
F tool/mkvsix.tcl b9e0777a213c23156b6542842c238479e496ebf5
@@ -1637,10 +1646,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 70338c7cb128855bb134dddebf04fbcf4c0c36b3127a53b9e24cd9f7fd235905
R f28d3a18ae99c0abfb9dbb754ba936b5
T +bgcolor * #d0c0ff
T +sym-release *
T +sym-version-3.20.1 *
U drh
Z 060abdbd0746413c79f6ad9e0e76e1d3
P b0e3b88a792623f7761586d8bfac058ddf939f49944ff0c4376627ff920895db
R f6522b1ac79c489b723c146b9fd586f4
U dan
Z 105e790a02c4f620d8e963aaaf4e7ac3
+1 -1
View File
@@ -1 +1 @@
8d3a7ea6c5690d6b7c3767558f4f01b511c55463e3f9e64506801fe9b74dce34
f38ff718278c8ff477d1c6379ced32167063de15f0666c8eac2e4dd30cbd975c
+7 -7
View File
@@ -403,9 +403,9 @@ void sqlite3AlterRenameTable(
char *zWhere = 0; /* Where clause to locate temp triggers */
#endif
VTable *pVTab = 0; /* Non-zero if this is a v-tab with an xRename() */
int savedDbFlags; /* Saved value of db->flags */
u32 savedDbFlags; /* Saved value of db->mDbFlags */
savedDbFlags = db->flags;
savedDbFlags = db->mDbFlags;
if( NEVER(db->mallocFailed) ) goto exit_rename_table;
assert( pSrc->nSrc==1 );
assert( sqlite3BtreeHoldsAllMutexes(pParse->db) );
@@ -414,7 +414,7 @@ void sqlite3AlterRenameTable(
if( !pTab ) goto exit_rename_table;
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
zDb = db->aDb[iDb].zDbSName;
db->flags |= SQLITE_PreferBuiltin;
db->mDbFlags |= DBFLAG_PreferBuiltin;
/* Get a NULL terminated version of the new table name. */
zName = sqlite3NameFromToken(db, pName);
@@ -579,7 +579,7 @@ void sqlite3AlterRenameTable(
exit_rename_table:
sqlite3SrcListDelete(db, pSrc);
sqlite3DbFree(db, zName);
db->flags = savedDbFlags;
db->mDbFlags = savedDbFlags;
}
/*
@@ -680,11 +680,11 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
zCol = sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n);
if( zCol ){
char *zEnd = &zCol[pColDef->n-1];
int savedDbFlags = db->flags;
u32 savedDbFlags = db->mDbFlags;
while( zEnd>zCol && (*zEnd==';' || sqlite3Isspace(*zEnd)) ){
*zEnd-- = '\0';
}
db->flags |= SQLITE_PreferBuiltin;
db->mDbFlags |= DBFLAG_PreferBuiltin;
sqlite3NestedParse(pParse,
"UPDATE \"%w\".%s SET "
"sql = substr(sql,1,%d) || ', ' || %Q || substr(sql,%d) "
@@ -693,7 +693,7 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
zTab
);
sqlite3DbFree(db, zCol);
db->flags = savedDbFlags;
db->mDbFlags = savedDbFlags;
}
/* Make sure the schema version is at least 3. But do not upgrade
-9
View File
@@ -93,10 +93,6 @@ static void attachFunc(
);
goto attach_error;
}
if( !db->autoCommit ){
zErrDyn = sqlite3MPrintf(db, "cannot ATTACH database within transaction");
goto attach_error;
}
for(i=0; i<db->nDb; i++){
char *z = db->aDb[i].zDbSName;
assert( z && zName );
@@ -288,11 +284,6 @@ static void detachFunc(
sqlite3_snprintf(sizeof(zErr),zErr, "cannot detach database %s", zName);
goto detach_error;
}
if( !db->autoCommit ){
sqlite3_snprintf(sizeof(zErr), zErr,
"cannot DETACH database within transaction");
goto detach_error;
}
if( sqlite3BtreeIsInReadTrans(pDb->pBt) || sqlite3BtreeIsInBackup(pDb->pBt) ){
sqlite3_snprintf(sizeof(zErr),zErr, "database %s is locked", zName);
goto detach_error;
+16 -17
View File
@@ -731,7 +731,7 @@ static int SQLITE_NOINLINE saveCursorsOnList(
return rc;
}
}else{
testcase( p->iPage>0 );
testcase( p->iPage>=0 );
btreeReleaseAllCursorPages(p);
}
}
@@ -3981,7 +3981,6 @@ int sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode, int writeOnly){
if( pBtree ){
sqlite3BtreeEnter(pBtree);
for(p=pBtree->pBt->pCursor; p; p=p->pNext){
int i;
if( writeOnly && (p->curFlags & BTCF_WriteFlag)==0 ){
if( p->eState==CURSOR_VALID || p->eState==CURSOR_SKIPNEXT ){
rc = saveCursorPosition(p);
@@ -3995,10 +3994,7 @@ int sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode, int writeOnly){
p->eState = CURSOR_FAULT;
p->skipNext = errCode;
}
for(i=0; i<=p->iPage; i++){
releasePage(p->apPage[i]);
p->apPage[i] = 0;
}
btreeReleaseAllCursorPages(p);
}
sqlite3BtreeLeave(pBtree);
}
@@ -4315,7 +4311,7 @@ int sqlite3BtreeCloseCursor(BtCursor *pCur){
}while( ALWAYS(pPrev) );
}
for(i=0; i<=pCur->iPage; i++){
releasePage(pCur->apPage[i]);
releasePageNotNull(pCur->apPage[i]);
}
unlockBtreeIfUnused(pBt);
sqlite3_free(pCur->aOverflow);
@@ -4938,13 +4934,7 @@ static int moveToRoot(BtCursor *pCur){
assert( CURSOR_INVALID < CURSOR_REQUIRESEEK );
assert( CURSOR_VALID < CURSOR_REQUIRESEEK );
assert( CURSOR_FAULT > CURSOR_REQUIRESEEK );
if( pCur->eState>=CURSOR_REQUIRESEEK ){
if( pCur->eState==CURSOR_FAULT ){
assert( pCur->skipNext!=SQLITE_OK );
return pCur->skipNext;
}
sqlite3BtreeClearCursor(pCur);
}
assert( pCur->eState < CURSOR_REQUIRESEEK || pCur->iPage<0 );
if( pCur->iPage>=0 ){
if( pCur->iPage ){
@@ -4959,6 +4949,13 @@ static int moveToRoot(BtCursor *pCur){
return SQLITE_OK;
}else{
assert( pCur->iPage==(-1) );
if( pCur->eState>=CURSOR_REQUIRESEEK ){
if( pCur->eState==CURSOR_FAULT ){
assert( pCur->skipNext!=SQLITE_OK );
return pCur->skipNext;
}
sqlite3BtreeClearCursor(pCur);
}
rc = getAndInitPage(pCur->pBtree->pBt, pCur->pgnoRoot, &pCur->apPage[0],
0, pCur->curPagerFlags);
if( rc!=SQLITE_OK ){
@@ -6120,7 +6117,7 @@ static int clearCell(
unsigned char *pCell, /* First byte of the Cell */
CellInfo *pInfo /* Size information about the cell */
){
BtShared *pBt = pPage->pBt;
BtShared *pBt;
Pgno ovflPgno;
int rc;
int nOvfl;
@@ -6136,6 +6133,7 @@ static int clearCell(
return SQLITE_CORRUPT_PGNO(pPage->pgno);
}
ovflPgno = get4byte(pCell + pInfo->nSize - 4);
pBt = pPage->pBt;
assert( pBt->usableSize > 4 );
ovflPageSize = pBt->usableSize - 4;
nOvfl = (pInfo->nPayload - pInfo->nLocal + ovflPageSize - 1)/ovflPageSize;
@@ -8254,7 +8252,7 @@ int sqlite3BtreeInsert(
pCur->apPage[pCur->iPage]->nOverflow = 0;
pCur->eState = CURSOR_INVALID;
if( (flags & BTREE_SAVEPOSITION) && rc==SQLITE_OK ){
rc = moveToRoot(pCur);
btreeReleaseAllCursorPages(pCur);
if( pCur->pKeyInfo ){
assert( pCur->pKey==0 );
pCur->pKey = sqlite3Malloc( pX->nKey );
@@ -8268,7 +8266,7 @@ int sqlite3BtreeInsert(
pCur->nKey = pX->nKey;
}
}
assert( pCur->apPage[pCur->iPage]->nOverflow==0 );
assert( pCur->iPage<0 || pCur->apPage[pCur->iPage]->nOverflow==0 );
end_insert:
return rc;
@@ -8438,6 +8436,7 @@ int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
}else{
rc = moveToRoot(pCur);
if( bPreserve ){
btreeReleaseAllCursorPages(pCur);
pCur->eState = CURSOR_REQUIRESEEK;
}
}
+10 -9
View File
@@ -479,7 +479,7 @@ void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){
}
freeIndex(db, pIndex);
}
db->flags |= SQLITE_InternChanges;
db->mDbFlags |= DBFLAG_SchemaChange;
}
/*
@@ -551,7 +551,7 @@ void sqlite3ResetAllSchemasOfConnection(sqlite3 *db){
sqlite3SchemaClear(pDb->pSchema);
}
}
db->flags &= ~SQLITE_InternChanges;
db->mDbFlags &= ~DBFLAG_SchemaChange;
sqlite3VtabUnlockList(db);
sqlite3BtreeLeaveAll(db);
sqlite3CollapseDatabaseArray(db);
@@ -561,7 +561,7 @@ void sqlite3ResetAllSchemasOfConnection(sqlite3 *db){
** This routine is called when a commit occurs.
*/
void sqlite3CommitInternalChanges(sqlite3 *db){
db->flags &= ~SQLITE_InternChanges;
db->mDbFlags &= ~DBFLAG_SchemaChange;
}
/*
@@ -665,7 +665,7 @@ void sqlite3UnlinkAndDeleteTable(sqlite3 *db, int iDb, const char *zTabName){
pDb = &db->aDb[iDb];
p = sqlite3HashInsert(&pDb->pSchema->tblHash, zTabName, 0);
sqlite3DeleteTable(db, p);
db->flags |= SQLITE_InternChanges;
db->mDbFlags |= DBFLAG_SchemaChange;
}
/*
@@ -778,7 +778,8 @@ int sqlite3TwoPartName(
return -1;
}
}else{
assert( db->init.iDb==0 || db->init.busy || (db->flags & SQLITE_Vacuum)!=0);
assert( db->init.iDb==0 || db->init.busy
|| (db->mDbFlags & DBFLAG_Vacuum)!=0);
iDb = db->init.iDb;
*pUnqual = pName1;
}
@@ -1680,7 +1681,7 @@ static int hasColumn(const i16 *aiCol, int nCol, int x){
** schema to the rootpage from the main table.
** (5) Add all table columns to the PRIMARY KEY Index object
** so that the PRIMARY KEY is a covering index. The surplus
** columns are part of KeyInfo.nXField and are not used for
** columns are part of KeyInfo.nAllField and are not used for
** sorting or lookup or uniqueness checks.
** (6) Replace the rowid tail on all automatically generated UNIQUE
** indices with the PRIMARY KEY columns.
@@ -2055,7 +2056,7 @@ void sqlite3EndTable(
return;
}
pParse->pNewTable = 0;
db->flags |= SQLITE_InternChanges;
db->mDbFlags |= DBFLAG_SchemaChange;
#ifndef SQLITE_OMIT_ALTERTABLE
if( !p->pSelect ){
@@ -2835,7 +2836,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);
sqlite3VdbeAddOp1(v, OP_SeekEnd, iIdx);
sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdx, regRecord);
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
sqlite3ReleaseTempReg(pParse, regRecord);
@@ -3324,7 +3325,7 @@ void sqlite3CreateIndex(
sqlite3OomFault(db);
goto exit_create_index;
}
db->flags |= SQLITE_InternChanges;
db->mDbFlags |= DBFLAG_SchemaChange;
if( pTblName!=0 ){
pIndex->tnum = db->init.newTnum;
}
+2 -2
View File
@@ -374,7 +374,7 @@ FuncDef *sqlite3FindFunction(
/* If no match is found, search the built-in functions.
**
** If the SQLITE_PreferBuiltin flag is set, then search the built-in
** If the DBFLAG_PreferBuiltin flag is set, then search the built-in
** functions even if a prior app-defined function was found. And give
** priority to built-in functions.
**
@@ -384,7 +384,7 @@ FuncDef *sqlite3FindFunction(
** new function. But the FuncDefs for built-in functions are read-only.
** So we must not search for built-ins when creating a new function.
*/
if( !createFlag && (pBest==0 || (db->flags & SQLITE_PreferBuiltin)!=0) ){
if( !createFlag && (pBest==0 || (db->mDbFlags & DBFLAG_PreferBuiltin)!=0) ){
bestScore = 0;
h = (sqlite3UpperToLower[(u8)zName[0]] + nName) % SQLITE_FUNC_HASH_SZ;
p = functionSearch(h, zName);
+3
View File
@@ -184,6 +184,9 @@ static const char * const sqlite3azCompileOpt[] = {
#if SQLITE_ENABLE_ATOMIC_WRITE
"ENABLE_ATOMIC_WRITE",
#endif
#if SQLITE_ENABLE_BATCH_ATOMIC_WRITE
"ENABLE_BATCH_ATOMIC_WRITE",
#endif
#if SQLITE_ENABLE_CEROD
"ENABLE_CEROD",
#endif
+5 -1
View File
@@ -502,7 +502,11 @@ void sqlite3DeleteFrom(
}
}else if( pPk ){
addrLoop = sqlite3VdbeAddOp1(v, OP_Rewind, iEphCur); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_RowData, iEphCur, iKey);
if( IsVirtual(pTab) ){
sqlite3VdbeAddOp3(v, OP_Column, iEphCur, 0, iKey);
}else{
sqlite3VdbeAddOp2(v, OP_RowData, iEphCur, iKey);
}
assert( nKey==0 ); /* OP_Found will use a composite key */
}else{
addrLoop = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, 0, iKey);
+22 -8
View File
@@ -1706,9 +1706,14 @@ void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){
/*
** pExpr points to an expression which implements a function. If
** it is appropriate to apply the LIKE optimization to that function
** then set aWc[0] through aWc[2] to the wildcard characters and
** return TRUE. If the function is not a LIKE-style function then
** return FALSE.
** then set aWc[0] through aWc[2] to the wildcard characters and the
** escape character and then return TRUE. If the function is not a
** LIKE-style function then return FALSE.
**
** The expression "a LIKE b ESCAPE c" is only considered a valid LIKE
** operator if c is a string literal that is exactly one byte in length.
** That one byte is stored in aWc[3]. aWc[3] is set to zero if there is
** no ESCAPE clause.
**
** *pIsNocase is set to true if uppercase and lowercase are equivalent for
** the function (default for LIKE). If the function makes the distinction
@@ -1717,17 +1722,26 @@ void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){
*/
int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){
FuncDef *pDef;
if( pExpr->op!=TK_FUNCTION
|| !pExpr->x.pList
|| pExpr->x.pList->nExpr!=2
){
int nExpr;
if( pExpr->op!=TK_FUNCTION || !pExpr->x.pList ){
return 0;
}
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
pDef = sqlite3FindFunction(db, pExpr->u.zToken, 2, SQLITE_UTF8, 0);
nExpr = pExpr->x.pList->nExpr;
pDef = sqlite3FindFunction(db, pExpr->u.zToken, nExpr, SQLITE_UTF8, 0);
if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_FUNC_LIKE)==0 ){
return 0;
}
if( nExpr<3 ){
aWc[3] = 0;
}else{
Expr *pEscape = pExpr->x.pList->a[2].pExpr;
char *zEscape;
if( pEscape->op!=TK_STRING ) return 0;
zEscape = pEscape->u.zToken;
if( zEscape[0]==0 || zEscape[1]!=0 ) return 0;
aWc[3] = zEscape[0];
}
/* The memcpy() statement assumes that the wildcard characters are
** the first three statements in the compareInfo structure. The
+10 -10
View File
@@ -226,7 +226,7 @@ static int autoIncBegin(
){
int memId = 0; /* Register holding maximum rowid */
if( (pTab->tabFlags & TF_Autoincrement)!=0
&& (pParse->db->flags & SQLITE_Vacuum)==0
&& (pParse->db->mDbFlags & DBFLAG_Vacuum)==0
){
Parse *pToplevel = sqlite3ParseToplevel(pParse);
AutoincInfo *pInfo;
@@ -2059,7 +2059,7 @@ static int xferOptimization(
Column *pDestCol = &pDest->aCol[i];
Column *pSrcCol = &pSrc->aCol[i];
#ifdef SQLITE_ENABLE_HIDDEN_COLUMNS
if( (db->flags & SQLITE_Vacuum)==0
if( (db->mDbFlags & DBFLAG_Vacuum)==0
&& (pDestCol->colFlags | pSrcCol->colFlags) & COLFLAG_HIDDEN
){
return 0; /* Neither table may have __hidden__ columns */
@@ -2135,15 +2135,15 @@ static int xferOptimization(
regRowid = sqlite3GetTempReg(pParse);
sqlite3OpenTable(pParse, iDest, iDbDest, pDest, OP_OpenWrite);
assert( HasRowid(pDest) || destHasUniqueIdx );
if( (db->flags & SQLITE_Vacuum)==0 && (
if( (db->mDbFlags & DBFLAG_Vacuum)==0 && (
(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
** DBFLAG_Vacuum flag is set, this block generates code to make
** that determination. If DBFLAG_Vacuum is set, then the destination
** table is always empty.
**
** Conditions under which the destination must be empty:
@@ -2179,8 +2179,8 @@ static int xferOptimization(
assert( (pDest->tabFlags & TF_Autoincrement)==0 );
}
sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
if( db->flags & SQLITE_Vacuum ){
sqlite3VdbeAddOp3(v, OP_Last, iDest, 0, -1);
if( db->mDbFlags & DBFLAG_Vacuum ){
sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest);
insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID|
OPFLAG_APPEND|OPFLAG_USESEEKRESULT;
}else{
@@ -2211,13 +2211,13 @@ static int xferOptimization(
VdbeComment((v, "%s", pDestIdx->zName));
addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v);
sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
if( db->flags & SQLITE_Vacuum ){
if( db->mDbFlags & DBFLAG_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
** of every OP_IdxInsert opcode, an OP_SeekEnd is added before the
** OP_IdxInsert to seek to the point within the b-tree where each key
** should be inserted. This is faster.
**
@@ -2232,7 +2232,7 @@ static int xferOptimization(
}
if( i==pSrcIdx->nColumn ){
idxInsFlags = OPFLAG_USESEEKRESULT;
sqlite3VdbeAddOp3(v, OP_Last, iDest, 0, -1);
sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest);
}
}
if( !HasRowid(pSrc) && pDestIdx->idxType==2 ){
+3 -3
View File
@@ -824,7 +824,7 @@ int sqlite3_db_config(sqlite3 *db, int op, ...){
if( aFlagOp[i].op==op ){
int onoff = va_arg(ap, int);
int *pRes = va_arg(ap, int*);
int oldFlags = db->flags;
u32 oldFlags = db->flags;
if( onoff>0 ){
db->flags |= aFlagOp[i].mask;
}else if( onoff==0 ){
@@ -1259,7 +1259,7 @@ void sqlite3RollbackAll(sqlite3 *db, int tripCode){
** the database rollback and schema reset, which can cause false
** corruption reports in some cases. */
sqlite3BtreeEnterAll(db);
schemaChange = (db->flags & SQLITE_InternChanges)!=0 && db->init.busy==0;
schemaChange = (db->mDbFlags & DBFLAG_SchemaChange)!=0 && db->init.busy==0;
for(i=0; i<db->nDb; i++){
Btree *p = db->aDb[i].pBt;
@@ -1273,7 +1273,7 @@ void sqlite3RollbackAll(sqlite3 *db, int tripCode){
sqlite3VtabRollback(db);
sqlite3EndBenignMalloc();
if( (db->flags&SQLITE_InternChanges)!=0 && db->init.busy==0 ){
if( (db->mDbFlags&DBFLAG_SchemaChange)!=0 && db->init.busy==0 ){
sqlite3ExpirePreparedStatements(db);
sqlite3ResetAllSchemasOfConnection(db);
}
+24 -8
View File
@@ -96,7 +96,8 @@ static int memjrnlRead(
int iChunkOffset;
FileChunk *pChunk;
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
|| defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
if( (iAmt+iOfst)>p->endpoint.iOffset ){
return SQLITE_IOERR_SHORT_READ;
}
@@ -215,7 +216,8 @@ static int memjrnlWrite(
** atomic-write optimization. In this case the first 28 bytes of the
** journal file may be written as part of committing the transaction. */
assert( iOfst==p->endpoint.iOffset || iOfst==0 );
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
|| defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
if( iOfst==0 && p->pFirst ){
assert( p->nChunkSize>iAmt );
memcpy((u8*)p->pFirst->zChunk, zBuf, iAmt);
@@ -384,17 +386,31 @@ void sqlite3MemJournalOpen(sqlite3_file *pJfd){
sqlite3JournalOpen(0, 0, pJfd, 0, -1);
}
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
|| defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
/*
** If the argument p points to a MemJournal structure that is not an
** in-memory-only journal file (i.e. is one that was opened with a +ve
** nSpill parameter), and the underlying file has not yet been created,
** create it now.
** nSpill parameter or as SQLITE_OPEN_MAIN_JOURNAL), and the underlying
** file has not yet been created, create it now.
*/
int sqlite3JournalCreate(sqlite3_file *p){
int sqlite3JournalCreate(sqlite3_file *pJfd){
int rc = SQLITE_OK;
if( p->pMethods==&MemJournalMethods && ((MemJournal*)p)->nSpill>0 ){
rc = memjrnlCreateFile((MemJournal*)p);
MemJournal *p = (MemJournal*)pJfd;
if( p->pMethod==&MemJournalMethods && (
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
p->nSpill>0
#else
/* While this appears to not be possible without ATOMIC_WRITE, the
** paths are complex, so it seems prudent to leave the test in as
** a NEVER(), in case our analysis is subtly flawed. */
NEVER(p->nSpill>0)
#endif
#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
|| (p->flags & SQLITE_OPEN_MAIN_JOURNAL)
#endif
)){
rc = memjrnlCreateFile(p);
}
return rc;
}
+92 -34
View File
@@ -90,6 +90,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
@@ -220,10 +221,8 @@ struct unixFile {
sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */
void *pMapRegion; /* Memory mapped region */
#endif
#ifdef __QNXNTO__
int sectorSize; /* Device sector size */
int deviceCharacteristics; /* Precomputed device characteristics */
#endif
#if SQLITE_ENABLE_LOCKING_STYLE
int openFlags; /* The flags specified at open() */
#endif
@@ -328,6 +327,20 @@ static pid_t randomnessPid = 0;
# define lseek lseek64
#endif
#ifdef __linux__
/*
** Linux-specific IOCTL magic numbers used for controlling F2FS
*/
#define F2FS_IOCTL_MAGIC 0xf5
#define F2FS_IOC_START_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 1)
#define F2FS_IOC_COMMIT_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 2)
#define F2FS_IOC_START_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 3)
#define F2FS_IOC_ABORT_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 5)
#define F2FS_IOC_GET_FEATURES _IOR(F2FS_IOCTL_MAGIC, 12, u32)
#define F2FS_FEATURE_ATOMIC_WRITE 0x0004
#endif /* __linux__ */
/*
** Different Unix systems declare open() in different ways. Same use
** open(const char*,int,mode_t). Others use open(const char*,int,...).
@@ -500,6 +513,9 @@ static struct unix_syscall {
#endif
#define osLstat ((int(*)(const char*,struct stat*))aSyscall[27].pCurrent)
{ "ioctl", (sqlite3_syscall_ptr)ioctl, 0 },
#define osIoctl ((int(*)(int,int,...))aSyscall[28].pCurrent)
}; /* End of the overrideable system calls */
@@ -3777,6 +3793,21 @@ static int unixGetTempname(int nBuf, char *zBuf);
static int unixFileControl(sqlite3_file *id, int op, void *pArg){
unixFile *pFile = (unixFile*)id;
switch( op ){
#if defined(__linux__) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
case SQLITE_FCNTL_BEGIN_ATOMIC_WRITE: {
int rc = osIoctl(pFile->h, F2FS_IOC_START_ATOMIC_WRITE);
return rc ? SQLITE_IOERR_BEGIN_ATOMIC : SQLITE_OK;
}
case SQLITE_FCNTL_COMMIT_ATOMIC_WRITE: {
int rc = osIoctl(pFile->h, F2FS_IOC_COMMIT_ATOMIC_WRITE);
return rc ? SQLITE_IOERR_COMMIT_ATOMIC : SQLITE_OK;
}
case SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE: {
int rc = osIoctl(pFile->h, F2FS_IOC_ABORT_VOLATILE_WRITE);
return rc ? SQLITE_IOERR_ROLLBACK_ATOMIC : SQLITE_OK;
}
#endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */
case SQLITE_FCNTL_LOCKSTATE: {
*(int*)pArg = pFile->eFileLock;
return SQLITE_OK;
@@ -3827,6 +3858,14 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
if( newLimit>sqlite3GlobalConfig.mxMmap ){
newLimit = sqlite3GlobalConfig.mxMmap;
}
/* The value of newLimit may be eventually cast to (size_t) and passed
** to mmap(). Restrict its value to 2GB if (size_t) is not at least a
** 64-bit type. */
if( newLimit>0 && sizeof(size_t)<8 ){
newLimit = (newLimit & 0x7FFFFFFF);
}
*(i64*)pArg = pFile->mmapSizeMax;
if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){
pFile->mmapSizeMax = newLimit;
@@ -3860,30 +3899,41 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
}
/*
** Return the sector size in bytes of the underlying block device for
** the specified file. This is almost always 512 bytes, but may be
** larger for some devices.
** If pFd->sectorSize is non-zero when this function is called, it is a
** no-op. Otherwise, the values of pFd->sectorSize and
** pFd->deviceCharacteristics are set according to the file-system
** characteristics.
**
** SQLite code assumes this function cannot fail. It also assumes that
** if two files are created in the same file-system directory (i.e.
** a database and its journal file) that the sector size will be the
** same for both.
** There are two versions of this function. One for QNX and one for all
** other systems.
*/
#ifndef __QNXNTO__
static int unixSectorSize(sqlite3_file *NotUsed){
UNUSED_PARAMETER(NotUsed);
return SQLITE_DEFAULT_SECTOR_SIZE;
}
#endif
#ifndef __QNXNTO__
static void setDeviceCharacteristics(unixFile *pFd){
assert( pFd->deviceCharacteristics==0 || pFd->sectorSize!=0 );
if( pFd->sectorSize==0 ){
#if defined(__linux__) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
int res;
u32 f = 0;
/*
** The following version of unixSectorSize() is optimized for QNX.
*/
#ifdef __QNXNTO__
/* Check for support for F2FS atomic batch writes. */
res = osIoctl(pFd->h, F2FS_IOC_GET_FEATURES, &f);
if( res==0 && (f & F2FS_FEATURE_ATOMIC_WRITE) ){
pFd->deviceCharacteristics = SQLITE_IOCAP_BATCH_ATOMIC;
}
#endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */
/* Set the POWERSAFE_OVERWRITE flag if requested. */
if( pFd->ctrlFlags & UNIXFILE_PSOW ){
pFd->deviceCharacteristics |= SQLITE_IOCAP_POWERSAFE_OVERWRITE;
}
pFd->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE;
}
}
#else
#include <sys/dcmd_blk.h>
#include <sys/statvfs.h>
static int unixSectorSize(sqlite3_file *id){
unixFile *pFile = (unixFile*)id;
static void setDeviceCharacteristics(unixFile *pFile){
if( pFile->sectorSize == 0 ){
struct statvfs fsInfo;
@@ -3952,9 +4002,24 @@ static int unixSectorSize(sqlite3_file *id){
pFile->deviceCharacteristics = 0;
pFile->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE;
}
return pFile->sectorSize;
}
#endif /* __QNXNTO__ */
#endif
/*
** Return the sector size in bytes of the underlying block device for
** the specified file. This is almost always 512 bytes, but may be
** larger for some devices.
**
** SQLite code assumes this function cannot fail. It also assumes that
** if two files are created in the same file-system directory (i.e.
** a database and its journal file) that the sector size will be the
** same for both.
*/
static int unixSectorSize(sqlite3_file *id){
unixFile *pFd = (unixFile*)id;
setDeviceCharacteristics(pFd);
return pFd->sectorSize;
}
/*
** Return the device characteristics for the file.
@@ -3970,16 +4035,9 @@ static int unixSectorSize(sqlite3_file *id){
** available to turn it off and URI query parameter available to turn it off.
*/
static int unixDeviceCharacteristics(sqlite3_file *id){
unixFile *p = (unixFile*)id;
int rc = 0;
#ifdef __QNXNTO__
if( p->sectorSize==0 ) unixSectorSize(id);
rc = p->deviceCharacteristics;
#endif
if( p->ctrlFlags & UNIXFILE_PSOW ){
rc |= SQLITE_IOCAP_POWERSAFE_OVERWRITE;
}
return rc;
unixFile *pFd = (unixFile*)id;
setDeviceCharacteristics(pFd);
return pFd->deviceCharacteristics;
}
#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
@@ -7598,7 +7656,7 @@ int sqlite3_os_init(void){
/* Double-check that the aSyscall[] array has been constructed
** correctly. See ticket [bb3a86e890c8e96ab] */
assert( ArraySize(aSyscall)==28 );
assert( ArraySize(aSyscall)==29 );
/* Register all VFSes defined in the aVfs[] array */
for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){
+8
View File
@@ -3559,6 +3559,14 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
if( newLimit>sqlite3GlobalConfig.mxMmap ){
newLimit = sqlite3GlobalConfig.mxMmap;
}
/* The value of newLimit may be eventually cast to (SIZE_T) and passed
** to MapViewOfFile(). Restrict its value to 2GB if (SIZE_T) is not at
** least a 64-bit type. */
if( newLimit>0 && sizeof(SIZE_T)<8 ){
newLimit = (newLimit & 0x7FFFFFFF);
}
*(i64*)pArg = pFile->mmapSizeMax;
if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){
pFile->mmapSizeMax = newLimit;
+106 -46
View File
@@ -947,6 +947,7 @@ static int assert_pager_state(Pager *p){
assert( isOpen(p->jfd)
|| p->journalMode==PAGER_JOURNALMODE_OFF
|| p->journalMode==PAGER_JOURNALMODE_WAL
|| (sqlite3OsDeviceCharacteristics(p->fd)&SQLITE_IOCAP_BATCH_ATOMIC)
);
assert( pPager->dbOrigSize<=pPager->dbHintSize );
break;
@@ -958,6 +959,7 @@ static int assert_pager_state(Pager *p){
assert( isOpen(p->jfd)
|| p->journalMode==PAGER_JOURNALMODE_OFF
|| p->journalMode==PAGER_JOURNALMODE_WAL
|| (sqlite3OsDeviceCharacteristics(p->fd)&SQLITE_IOCAP_BATCH_ATOMIC)
);
break;
@@ -1168,34 +1170,45 @@ static int pagerLockDb(Pager *pPager, int eLock){
}
/*
** This function determines whether or not the atomic-write optimization
** can be used with this pager. The optimization can be used if:
** This function determines whether or not the atomic-write or
** atomic-batch-write optimizations can be used with this pager. The
** atomic-write optimization can be used if:
**
** (a) the value returned by OsDeviceCharacteristics() indicates that
** a database page may be written atomically, and
** (b) the value returned by OsSectorSize() is less than or equal
** to the page size.
**
** The optimization is also always enabled for temporary files. It is
** an error to call this function if pPager is opened on an in-memory
** database.
** If it can be used, then the value returned is the size of the journal
** file when it contains rollback data for exactly one page.
**
** If the optimization cannot be used, 0 is returned. If it can be used,
** then the value returned is the size of the journal file when it
** contains rollback data for exactly one page.
** The atomic-batch-write optimization can be used if OsDeviceCharacteristics()
** returns a value with the SQLITE_IOCAP_BATCH_ATOMIC bit set. -1 is
** returned in this case.
**
** If neither optimization can be used, 0 is returned.
*/
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
static int jrnlBufferSize(Pager *pPager){
assert( !MEMDB );
if( !pPager->tempFile ){
int dc; /* Device characteristics */
int nSector; /* Sector size */
int szPage; /* Page size */
assert( isOpen(pPager->fd) );
dc = sqlite3OsDeviceCharacteristics(pPager->fd);
nSector = pPager->sectorSize;
szPage = pPager->pageSize;
#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
|| defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
int dc; /* Device characteristics */
assert( isOpen(pPager->fd) );
dc = sqlite3OsDeviceCharacteristics(pPager->fd);
#endif
#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
if( dc&SQLITE_IOCAP_BATCH_ATOMIC ){
return -1;
}
#endif
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
{
int nSector = pPager->sectorSize;
int szPage = pPager->pageSize;
assert(SQLITE_IOCAP_ATOMIC512==(512>>8));
assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8));
@@ -1205,11 +1218,11 @@ static int jrnlBufferSize(Pager *pPager){
}
return JOURNAL_HDR_SZ(pPager) + JOURNAL_PG_SZ(pPager);
}
#else
# define jrnlBufferSize(x) 0
#endif
return 0;
}
/*
** If SQLITE_CHECK_PAGES is defined then we do some sanity checking
** on the cache using a hash function. This is used for testing
@@ -2012,7 +2025,9 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
}
releaseAllSavepoints(pPager);
assert( isOpen(pPager->jfd) || pPager->pInJournal==0 );
assert( isOpen(pPager->jfd) || pPager->pInJournal==0
|| (sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_BATCH_ATOMIC)
);
if( isOpen(pPager->jfd) ){
assert( !pagerUseWal(pPager) );
@@ -4567,6 +4582,13 @@ static int pagerStress(void *p, PgHdr *pPg){
rc = pagerWalFrames(pPager, pPg, 0, 0);
}
}else{
#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
if( pPager->tempFile==0 ){
rc = sqlite3JournalCreate(pPager->jfd);
if( rc!=SQLITE_OK ) return pager_error(pPager, rc);
}
#endif
/* Sync the journal file if required. */
if( pPg->flags&PGHDR_NEED_SYNC
@@ -6347,6 +6369,21 @@ int sqlite3PagerCommitPhaseOne(
sqlite3PcacheCleanAll(pPager->pPCache);
}
}else{
/* The bBatch boolean is true if the batch-atomic-write commit method
** should be used. No rollback journal is created if batch-atomic-write
** is enabled.
*/
sqlite3_file *fd = pPager->fd;
#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
const int bBatch = zMaster==0 /* An SQLITE_IOCAP_BATCH_ATOMIC commit */
&& (sqlite3OsDeviceCharacteristics(fd) & SQLITE_IOCAP_BATCH_ATOMIC)
&& !pPager->noSync
&& sqlite3JournalIsInMemory(pPager->jfd);
#else
# define bBatch 0
#endif
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
/* The following block updates the change-counter. Exactly how it
** does this depends on whether or not the atomic-update optimization
** was enabled at compile time, and if this transaction meets the
@@ -6370,33 +6407,40 @@ int sqlite3PagerCommitPhaseOne(
** in 'direct' mode. In this case the journal file will never be
** created for this transaction.
*/
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
PgHdr *pPg;
assert( isOpen(pPager->jfd)
|| pPager->journalMode==PAGER_JOURNALMODE_OFF
|| pPager->journalMode==PAGER_JOURNALMODE_WAL
);
if( !zMaster && isOpen(pPager->jfd)
&& pPager->journalOff==jrnlBufferSize(pPager)
&& pPager->dbSize>=pPager->dbOrigSize
&& (0==(pPg = sqlite3PcacheDirtyList(pPager->pPCache)) || 0==pPg->pDirty)
){
/* Update the db file change counter via the direct-write method. The
** following call will modify the in-memory representation of page 1
** to include the updated change counter and then write page 1
** directly to the database file. Because of the atomic-write
** property of the host file-system, this is safe.
*/
rc = pager_incr_changecounter(pPager, 1);
}else{
rc = sqlite3JournalCreate(pPager->jfd);
if( rc==SQLITE_OK ){
rc = pager_incr_changecounter(pPager, 0);
if( bBatch==0 ){
PgHdr *pPg;
assert( isOpen(pPager->jfd)
|| pPager->journalMode==PAGER_JOURNALMODE_OFF
|| pPager->journalMode==PAGER_JOURNALMODE_WAL
);
if( !zMaster && isOpen(pPager->jfd)
&& pPager->journalOff==jrnlBufferSize(pPager)
&& pPager->dbSize>=pPager->dbOrigSize
&& (!(pPg = sqlite3PcacheDirtyList(pPager->pPCache)) || 0==pPg->pDirty)
){
/* Update the db file change counter via the direct-write method. The
** following call will modify the in-memory representation of page 1
** to include the updated change counter and then write page 1
** directly to the database file. Because of the atomic-write
** property of the host file-system, this is safe.
*/
rc = pager_incr_changecounter(pPager, 1);
}else{
rc = sqlite3JournalCreate(pPager->jfd);
if( rc==SQLITE_OK ){
rc = pager_incr_changecounter(pPager, 0);
}
}
}
#else
#else
#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
if( zMaster ){
rc = sqlite3JournalCreate(pPager->jfd);
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
}
#endif
rc = pager_incr_changecounter(pPager, 0);
#endif
#endif
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
/* Write the master journal name into the journal file. If a master
@@ -6419,8 +6463,24 @@ int sqlite3PagerCommitPhaseOne(
*/
rc = syncJournal(pPager, 0);
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
if( bBatch ){
/* The pager is now in DBMOD state. But regardless of what happens
** next, attempting to play the journal back into the database would
** be unsafe. Close it now to make sure that does not happen. */
sqlite3OsClose(pPager->jfd);
rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_BEGIN_ATOMIC_WRITE, 0);
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
}
rc = pager_write_pagelist(pPager,sqlite3PcacheDirtyList(pPager->pPCache));
if( bBatch ){
if( rc==SQLITE_OK ){
rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_COMMIT_ATOMIC_WRITE, 0);
}else{
sqlite3OsFileControl(fd, SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE, 0);
}
}
if( rc!=SQLITE_OK ){
assert( rc!=SQLITE_IOERR_BLOCKED );
goto commit_phase_one_exit;
+13
View File
@@ -192,6 +192,19 @@ columnlist ::= columnlist COMMA columnname carglist.
columnlist ::= columnname carglist.
columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,&A,&Y);}
// Declare some tokens early in order to influence their values, to
// improve performance and reduce the executable size. The goal here is
// to get the "jump" operations in ISNULL through ESCAPE to have numeric
// values that are early enough so that all jump operations are clustered
// at the beginning, but also so that the comparison tokens NE through GE
// are as large as possible so that they are near to FUNCTION, which is a
// token synthesized by addopcodes.tcl.
//
%token ABORT ACTION AFTER ANALYZE ASC ATTACH BEFORE BEGIN BY CASCADE CAST.
%token CONFLICT DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL.
%token OR AND NOT IS MATCH LIKE_KW BETWEEN IN ISNULL NOTNULL NE EQ.
%token GT LE LT GE ESCAPE.
// The following directive causes tokens ABORT, AFTER, ASC, etc. to
// fallback to ID if they will not parse as their original value.
// This obviates the need for the "id" nonterminal.
+17 -18
View File
@@ -96,7 +96,6 @@ typedef struct PGroup PGroup;
struct PgHdr1 {
sqlite3_pcache_page page; /* Base class. Must be first. pBuf & pExtra */
unsigned int iKey; /* Key value (page number) */
u8 isPinned; /* Page in use, not on the LRU list */
u8 isBulkLocal; /* This page from bulk local storage */
u8 isAnchor; /* This is the PGroup.lru element */
PgHdr1 *pNext; /* Next in hash table chain */
@@ -105,6 +104,12 @@ struct PgHdr1 {
PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */
};
/*
** A page is pinned if it is no on the LRU list
*/
#define PAGE_IS_PINNED(p) ((p)->pLruNext==0)
#define PAGE_IS_UNPINNED(p) ((p)->pLruNext!=0)
/* Each page cache (or PCache) belongs to a PGroup. A PGroup is a set
** of one or more PCaches that are able to recycle each other's unpinned
** pages when they are under memory pressure. A PGroup is an instance of
@@ -557,22 +562,18 @@ static void pcache1ResizeHash(PCache1 *p){
** The PGroup mutex must be held when this function is called.
*/
static PgHdr1 *pcache1PinPage(PgHdr1 *pPage){
PCache1 *pCache;
assert( pPage!=0 );
assert( pPage->isPinned==0 );
pCache = pPage->pCache;
assert( PAGE_IS_UNPINNED(pPage) );
assert( pPage->pLruNext );
assert( pPage->pLruPrev );
assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
assert( sqlite3_mutex_held(pPage->pCache->pGroup->mutex) );
pPage->pLruPrev->pLruNext = pPage->pLruNext;
pPage->pLruNext->pLruPrev = pPage->pLruPrev;
pPage->pLruNext = 0;
pPage->pLruPrev = 0;
pPage->isPinned = 1;
assert( pPage->isAnchor==0 );
assert( pCache->pGroup->lru.isAnchor==1 );
pCache->nRecyclable--;
assert( pPage->pCache->pGroup->lru.isAnchor==1 );
pPage->pCache->nRecyclable--;
return pPage;
}
@@ -610,7 +611,7 @@ static void pcache1EnforceMaxPage(PCache1 *pCache){
&& (p=pGroup->lru.pLruPrev)->isAnchor==0
){
assert( p->pCache->pGroup==pGroup );
assert( p->isPinned==0 );
assert( PAGE_IS_UNPINNED(p) );
pcache1PinPage(p);
pcache1RemoveFromHash(p, 1);
}
@@ -659,7 +660,7 @@ static void pcache1TruncateUnsafe(
if( pPage->iKey>=iLimit ){
pCache->nPage--;
*pp = pPage->pNext;
if( !pPage->isPinned ) pcache1PinPage(pPage);
if( PAGE_IS_UNPINNED(pPage) ) pcache1PinPage(pPage);
pcache1FreePage(pPage);
}else{
pp = &pPage->pNext;
@@ -878,7 +879,7 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
){
PCache1 *pOther;
pPage = pGroup->lru.pLruPrev;
assert( pPage->isPinned==0 );
assert( PAGE_IS_UNPINNED(pPage) );
pcache1RemoveFromHash(pPage, 0);
pcache1PinPage(pPage);
pOther = pPage->pCache;
@@ -905,7 +906,6 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
pPage->pCache = pCache;
pPage->pLruPrev = 0;
pPage->pLruNext = 0;
pPage->isPinned = 1;
*(void **)pPage->page.pExtra = 0;
pCache->apHash[h] = pPage;
if( iKey>pCache->iMaxKey ){
@@ -991,7 +991,7 @@ static PgHdr1 *pcache1FetchNoMutex(
** Otherwise (page not in hash and createFlag!=0) continue with
** subsequent steps to try to create the page. */
if( pPage ){
if( !pPage->isPinned ){
if( PAGE_IS_UNPINNED(pPage) ){
return pcache1PinPage(pPage);
}else{
return pPage;
@@ -1066,7 +1066,7 @@ static void pcache1Unpin(
** part of the PGroup LRU list.
*/
assert( pPage->pLruPrev==0 && pPage->pLruNext==0 );
assert( pPage->isPinned==1 );
assert( PAGE_IS_PINNED(pPage) );
if( reuseUnlikely || pGroup->nCurrentPage>pGroup->nMaxPage ){
pcache1RemoveFromHash(pPage, 1);
@@ -1077,7 +1077,6 @@ static void pcache1Unpin(
(pPage->pLruNext = *ppFirst)->pLruPrev = pPage;
*ppFirst = pPage;
pCache->nRecyclable++;
pPage->isPinned = 0;
}
pcache1LeaveMutex(pCache->pGroup);
@@ -1221,7 +1220,7 @@ int sqlite3PcacheReleaseMemory(int nReq){
#ifdef SQLITE_PCACHE_SEPARATE_HEADER
nFree += sqlite3MemSize(p);
#endif
assert( p->isPinned==0 );
assert( PAGE_IS_UNPINNED(p) );
pcache1PinPage(p);
pcache1RemoveFromHash(p, 1);
}
@@ -1245,7 +1244,7 @@ void sqlite3PcacheStats(
PgHdr1 *p;
int nRecyclable = 0;
for(p=pcache1.grp.lru.pLruNext; p && !p->isAnchor; p=p->pLruNext){
assert( p->isPinned==0 );
assert( PAGE_IS_UNPINNED(p) );
nRecyclable++;
}
*pnCurrent = pcache1.grp.nCurrentPage;
+9 -11
View File
@@ -85,7 +85,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
rc = db->errCode;
assert( (rc&0xFF)==(rcp&0xFF) );
db->init.iDb = saved_iDb;
assert( saved_iDb==0 || (db->flags & SQLITE_Vacuum)!=0 );
assert( saved_iDb==0 || (db->mDbFlags & DBFLAG_Vacuum)!=0 );
if( SQLITE_OK!=rc ){
if( db->init.orphanTrigger ){
assert( iDb==1 );
@@ -354,7 +354,7 @@ error_out:
*/
int sqlite3Init(sqlite3 *db, char **pzErrMsg){
int i, rc;
int commit_internal = !(db->flags&SQLITE_InternChanges);
int commit_internal = !(db->mDbFlags&DBFLAG_SchemaChange);
assert( sqlite3_mutex_held(db->mutex) );
assert( sqlite3BtreeHoldsMutex(db->aDb[0].pBt) );
@@ -494,16 +494,14 @@ int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){
** Free all memory allocations in the pParse object
*/
void sqlite3ParserReset(Parse *pParse){
if( pParse ){
sqlite3 *db = pParse->db;
sqlite3DbFree(db, pParse->aLabel);
sqlite3ExprListDelete(db, pParse->pConstExpr);
if( db ){
assert( db->lookaside.bDisable >= pParse->disableLookaside );
db->lookaside.bDisable -= pParse->disableLookaside;
}
pParse->disableLookaside = 0;
sqlite3 *db = pParse->db;
sqlite3DbFree(db, pParse->aLabel);
sqlite3ExprListDelete(db, pParse->pConstExpr);
if( db ){
assert( db->lookaside.bDisable >= pParse->disableLookaside );
db->lookaside.bDisable -= pParse->disableLookaside;
}
pParse->disableLookaside = 0;
}
/*
+5 -5
View File
@@ -562,11 +562,11 @@ static void pushOntoSorter(
if( pParse->db->mallocFailed ) return;
pOp->p2 = nKey + nData;
pKI = pOp->p4.pKeyInfo;
memset(pKI->aSortOrder, 0, pKI->nField); /* Makes OP_Jump below testable */
memset(pKI->aSortOrder, 0, pKI->nKeyField); /* Makes OP_Jump testable */
sqlite3VdbeChangeP4(v, -1, (char*)pKI, P4_KEYINFO);
testcase( pKI->nXField>2 );
testcase( pKI->nAllField > pKI->nKeyField+2 );
pOp->p4.pKeyInfo = keyInfoFromExprList(pParse, pSort->pOrderBy, nOBSat,
pKI->nXField-1);
pKI->nAllField-pKI->nKeyField-1);
addrJmp = sqlite3VdbeCurrentAddr(v);
sqlite3VdbeAddOp3(v, OP_Jump, addrJmp+1, 0, addrJmp+1); VdbeCoverage(v);
pSort->labelBkOut = sqlite3VdbeMakeLabel(v);
@@ -1035,8 +1035,8 @@ KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
KeyInfo *p = sqlite3DbMallocRawNN(db, sizeof(KeyInfo) + nExtra);
if( p ){
p->aSortOrder = (u8*)&p->aColl[N+X];
p->nField = (u16)N;
p->nXField = (u16)X;
p->nKeyField = (u16)N;
p->nAllField = (u16)(N+X);
p->enc = ENC(db);
p->db = db;
p->nRef = 1;
+48 -1
View File
@@ -494,6 +494,9 @@ int sqlite3_exec(
#define SQLITE_IOERR_CONVPATH (SQLITE_IOERR | (26<<8))
#define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8))
#define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8))
#define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8))
#define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
#define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
@@ -580,6 +583,11 @@ int sqlite3_exec(
** SQLITE_IOCAP_IMMUTABLE flag indicates that the file is on
** read-only media and cannot be changed even by processes with
** elevated privileges.
**
** The SQLITE_IOCAP_BATCH_ATOMIC property means that the underlying
** filesystem supports doing multiple write operations atomically when those
** write operations are bracketed by [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] and
** [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE].
*/
#define SQLITE_IOCAP_ATOMIC 0x00000001
#define SQLITE_IOCAP_ATOMIC512 0x00000002
@@ -595,6 +603,7 @@ int sqlite3_exec(
#define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800
#define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000
#define SQLITE_IOCAP_IMMUTABLE 0x00002000
#define SQLITE_IOCAP_BATCH_ATOMIC 0x00004000
/*
** CAPI3REF: File Locking Levels
@@ -729,6 +738,7 @@ struct sqlite3_file {
** <li> [SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN]
** <li> [SQLITE_IOCAP_POWERSAFE_OVERWRITE]
** <li> [SQLITE_IOCAP_IMMUTABLE]
** <li> [SQLITE_IOCAP_BATCH_ATOMIC]
** </ul>
**
** The SQLITE_IOCAP_ATOMIC property means that all writes of
@@ -1012,6 +1022,40 @@ struct sqlite3_io_methods {
** The [SQLITE_FCNTL_RBU] opcode is implemented by the special VFS used by
** the RBU extension only. All other VFS should return SQLITE_NOTFOUND for
** this opcode.
**
** <li>[[SQLITE_FCNTL_BEGIN_ATOMIC_WRITE]]
** If the [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] opcode returns SQLITE_OK, then
** the file descriptor is placed in "batch write mode", which
** means all subsequent write operations will be deferred and done
** atomically at the next [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]. Systems
** that do not support batch atomic writes will return SQLITE_NOTFOUND.
** ^Following a successful SQLITE_FCNTL_BEGIN_ATOMIC_WRITE and prior to
** the closing [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE] or
** [SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE], SQLite will make
** no VFS interface calls on the same [sqlite3_file] file descriptor
** except for calls to the xWrite method and the xFileControl method
** with [SQLITE_FCNTL_SIZE_HINT].
**
** <li>[[SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]]
** The [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE] opcode causes all write
** operations since the previous successful call to
** [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] to be performed atomically.
** This file control returns [SQLITE_OK] if and only if the writes were
** all performed successfully and have been committed to persistent storage.
** ^Regardless of whether or not it is successful, this file control takes
** the file descriptor out of batch write mode so that all subsequent
** write operations are independent.
** ^SQLite will never invoke SQLITE_FCNTL_COMMIT_ATOMIC_WRITE without
** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE].
**
** <li>[[SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE]]
** The [SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE] opcode causes all write
** operations since the previous successful call to
** [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] to be rolled back.
** ^This file control takes the file descriptor out of batch write mode
** so that all subsequent write operations are independent.
** ^SQLite will never invoke SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE without
** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE].
** </ul>
*/
#define SQLITE_FCNTL_LOCKSTATE 1
@@ -1043,6 +1087,9 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_JOURNAL_POINTER 28
#define SQLITE_FCNTL_WIN32_GET_HANDLE 29
#define SQLITE_FCNTL_PDB 30
#define SQLITE_FCNTL_BEGIN_ATOMIC_WRITE 31
#define SQLITE_FCNTL_COMMIT_ATOMIC_WRITE 32
#define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33
/* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
@@ -4199,7 +4246,7 @@ const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
** other than [SQLITE_ROW] before any subsequent invocation of
** sqlite3_step(). Failure to reset the prepared statement using
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]),
** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1],
** sqlite3_step() began
** calling [sqlite3_reset()] automatically in this circumstance rather
** than returning [SQLITE_MISUSE]. This is not considered a compatibility
+3 -3
View File
@@ -246,7 +246,7 @@ struct sqlite3_api_routines {
int (*uri_boolean)(const char*,const char*,int);
sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
const char *(*uri_parameter)(const char*,const char*);
char *(*vsnprintf)(int,char*,const char*,va_list);
char *(*xvsnprintf)(int,char*,const char*,va_list);
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
/* Version 3.8.7 and later */
int (*auto_extension)(void(*)(void));
@@ -442,7 +442,7 @@ typedef int (*sqlite3_loadext_entry)(
#define sqlite3_value_text16le sqlite3_api->value_text16le
#define sqlite3_value_type sqlite3_api->value_type
#define sqlite3_vmprintf sqlite3_api->vmprintf
#define sqlite3_vsnprintf sqlite3_api->vsnprintf
#define sqlite3_vsnprintf sqlite3_api->xvsnprintf
#define sqlite3_overload_function sqlite3_api->overload_function
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
@@ -518,7 +518,7 @@ typedef int (*sqlite3_loadext_entry)(
#define sqlite3_uri_boolean sqlite3_api->uri_boolean
#define sqlite3_uri_int64 sqlite3_api->uri_int64
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
#define sqlite3_uri_vsnprintf sqlite3_api->vsnprintf
#define sqlite3_uri_vsnprintf sqlite3_api->xvsnprintf
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
/* Version 3.8.7 and later */
#define sqlite3_auto_extension sqlite3_api->auto_extension
+30 -18
View File
@@ -630,6 +630,15 @@
# define SQLITE_DEFAULT_PCACHE_INITSZ 20
#endif
/*
** The compile-time options SQLITE_MMAP_READWRITE and
** SQLITE_ENABLE_BATCH_ATOMIC_WRITE are not compatible with one another.
** You must choose one or the other (or neither) but not both.
*/
#if defined(SQLITE_MMAP_READWRITE) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
#error Cannot use both SQLITE_MMAP_READWRITE and SQLITE_ENABLE_BATCH_ATOMIC_WRITE
#endif
/*
** GCC does not define the offsetof() macro so we'll have to do it
** ourselves.
@@ -1316,7 +1325,8 @@ struct sqlite3 {
sqlite3_mutex *mutex; /* Connection mutex */
Db *aDb; /* All backends */
int nDb; /* Number of backends currently in use */
int flags; /* Miscellaneous flags. See below */
u32 mDbFlags; /* flags recording internal state */
u32 flags; /* flags settable by pragmas. See below */
i64 lastRowid; /* ROWID of most recent insert (see above) */
i64 szMmap; /* Default mmap_size setting */
unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */
@@ -1470,18 +1480,13 @@ struct sqlite3 {
#define SQLITE_ForeignKeys 0x00004000 /* Enforce foreign key constraints */
#define SQLITE_AutoIndex 0x00008000 /* Enable automatic indexes */
#define SQLITE_LoadExtension 0x00010000 /* Enable load_extension */
#define SQLITE_EnableTrigger 0x00020000 /* True to enable triggers */
#define SQLITE_DeferFKs 0x00040000 /* Defer all FK constraints */
#define SQLITE_QueryOnly 0x00080000 /* Disable database changes */
#define SQLITE_CellSizeCk 0x00100000 /* Check btree cell sizes on load */
#define SQLITE_Fts3Tokenizer 0x00200000 /* Enable fts3_tokenizer(2) */
#define SQLITE_EnableQPSG 0x00400000 /* Query Planner Stability Guarantee */
/* The next four values are not used by PRAGMAs or by sqlite3_dbconfig() and
** could be factored out into a separate bit vector of the sqlite3 object. */
#define SQLITE_InternChanges 0x00800000 /* Uncommitted Hash table changes */
#define SQLITE_LoadExtFunc 0x01000000 /* Enable load_extension() SQL func */
#define SQLITE_PreferBuiltin 0x02000000 /* Preference to built-in funcs */
#define SQLITE_Vacuum 0x04000000 /* Currently in a VACUUM */
#define SQLITE_LoadExtFunc 0x00020000 /* Enable load_extension() SQL func */
#define SQLITE_EnableTrigger 0x00040000 /* True to enable triggers */
#define SQLITE_DeferFKs 0x00080000 /* Defer all FK constraints */
#define SQLITE_QueryOnly 0x00100000 /* Disable database changes */
#define SQLITE_CellSizeCk 0x00200000 /* Check btree cell sizes on load */
#define SQLITE_Fts3Tokenizer 0x00400000 /* Enable fts3_tokenizer(2) */
#define SQLITE_EnableQPSG 0x00800000 /* Query Planner Stability Guarantee */
/* Flags used only if debugging */
#ifdef SQLITE_DEBUG
#define SQLITE_SqlTrace 0x08000000 /* Debug print SQL as it executes */
@@ -1491,6 +1496,12 @@ struct sqlite3 {
#define SQLITE_VdbeEQP 0x80000000 /* Debug EXPLAIN QUERY PLAN */
#endif
/*
** Allowed values for sqlite3.mDbFlags
*/
#define DBFLAG_SchemaChange 0x0001 /* Uncommitted Hash table changes */
#define DBFLAG_PreferBuiltin 0x0002 /* Preference to built-in funcs */
#define DBFLAG_Vacuum 0x0004 /* Currently in a VACUUM */
/*
** Bits of the sqlite3.dbOptFlags field that are used by the
@@ -2041,8 +2052,8 @@ struct FKey {
struct KeyInfo {
u32 nRef; /* Number of references to this KeyInfo object */
u8 enc; /* Text encoding - one of the SQLITE_UTF* values */
u16 nField; /* Number of key columns in the index */
u16 nXField; /* Number of columns beyond the key columns */
u16 nKeyField; /* Number of key columns in the index */
u16 nAllField; /* Total columns, including key plus others */
sqlite3 *db; /* The database connection */
u8 *aSortOrder; /* Sort order for each column. */
CollSeq *aColl[1]; /* Collating sequence for each term of the key */
@@ -2089,8 +2100,8 @@ struct UnpackedRecord {
u16 nField; /* Number of entries in apMem[] */
i8 default_rc; /* Comparison result if keys are equal */
u8 errCode; /* Error detected by xRecordCompare (CORRUPT or NOMEM) */
i8 r1; /* Value to return if (lhs > rhs) */
i8 r2; /* Value to return if (rhs < lhs) */
i8 r1; /* Value to return if (lhs < rhs) */
i8 r2; /* Value to return if (lhs > rhs) */
u8 eqSeen; /* True if an equality comparison has been seen */
};
@@ -4285,7 +4296,8 @@ int sqlite3FindInIndex(Parse *, Expr *, u32, int*, int*);
int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int);
int sqlite3JournalSize(sqlite3_vfs *);
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
|| defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
int sqlite3JournalCreate(sqlite3_file *);
#endif
+15 -4
View File
@@ -3882,28 +3882,39 @@ static int SQLITE_TCLAPI md5file_cmd(
const char **argv
){
FILE *in;
int ofst;
int amt;
MD5Context ctx;
void (*converter)(unsigned char*, char*);
unsigned char digest[16];
char zBuf[10240];
if( argc!=2 ){
if( argc!=2 && argc!=4 ){
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
" FILENAME\"", (char*)0);
" FILENAME [OFFSET AMT]\"", (char*)0);
return TCL_ERROR;
}
if( argc==4 ){
ofst = atoi(argv[2]);
amt = atoi(argv[3]);
}else{
ofst = 0;
amt = 2147483647;
}
in = fopen(argv[1],"rb");
if( in==0 ){
Tcl_AppendResult(interp,"unable to open file \"", argv[1],
"\" for reading", (char*)0);
return TCL_ERROR;
}
fseek(in, ofst, SEEK_SET);
MD5Init(&ctx);
for(;;){
while( amt>0 ){
int n;
n = (int)fread(zBuf, 1, sizeof(zBuf), in);
n = (int)fread(zBuf, 1, sizeof(zBuf)<=amt ? sizeof(zBuf) : amt, in);
if( n<=0 ) break;
MD5Update(&ctx, (unsigned char*)zBuf, (unsigned)n);
amt -= n;
}
fclose(in);
MD5Final(digest, &ctx);
+41
View File
@@ -2553,6 +2553,46 @@ static int SQLITE_TCLAPI test_delete_database(
return TCL_OK;
}
/*
** Usage: atomic_batch_write PATH
*/
static int SQLITE_TCLAPI test_atomic_batch_write(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
char *zFile = 0; /* Path to file to test */
sqlite3 *db = 0; /* Database handle */
sqlite3_file *pFd = 0; /* SQLite fd open on zFile */
int bRes = 0; /* Integer result of this command */
int dc = 0; /* Device-characteristics mask */
int rc; /* sqlite3_open() return code */
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "PATH");
return TCL_ERROR;
}
zFile = Tcl_GetString(objv[1]);
rc = sqlite3_open(zFile, &db);
if( rc!=SQLITE_OK ){
Tcl_AppendResult(interp, sqlite3_errmsg(db), 0);
sqlite3_close(db);
return TCL_ERROR;
}
rc = sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, (void*)&pFd);
dc = pFd->pMethods->xDeviceCharacteristics(pFd);
if( dc & SQLITE_IOCAP_BATCH_ATOMIC ){
bRes = 1;
}
Tcl_SetObjResult(interp, Tcl_NewIntObj(bRes));
sqlite3_close(db);
return TCL_OK;
}
/*
** Usage: sqlite3_next_stmt DB STMT
**
@@ -7645,6 +7685,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "sqlite3_snapshot_cmp_blob", test_snapshot_cmp_blob, 0 },
#endif
{ "sqlite3_delete_database", test_delete_database, 0 },
{ "atomic_batch_write", test_atomic_batch_write, 0 },
};
static int bitmask_size = sizeof(Bitmask)*8;
static int longdouble_size = sizeof(LONGDOUBLE_TYPE);
+25
View File
@@ -736,6 +736,7 @@ static int processDevSymArgs(
{ "sequential", SQLITE_IOCAP_SEQUENTIAL },
{ "safe_append", SQLITE_IOCAP_SAFE_APPEND },
{ "powersafe_overwrite", SQLITE_IOCAP_POWERSAFE_OVERWRITE },
{ "batch-atomic", SQLITE_IOCAP_BATCH_ATOMIC },
{ 0, 0 }
};
@@ -976,7 +977,30 @@ static int SQLITE_TCLAPI devSymObjCmd(
devsym_register(iDc, iSectorSize);
return TCL_OK;
}
/*
** tclcmd: sqlite3_crash_on_write N
*/
static int SQLITE_TCLAPI writeCrashObjCmd(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
void devsym_crash_on_write(int);
int nWrite = 0;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "NWRITE");
return TCL_ERROR;
}
if( Tcl_GetIntFromObj(interp, objv[1], &nWrite) ){
return TCL_ERROR;
}
devsym_crash_on_write(nWrite);
return TCL_OK;
}
/*
@@ -1068,6 +1092,7 @@ int Sqlitetest6_Init(Tcl_Interp *interp){
Tcl_CreateObjCommand(interp, "sqlite3_crashparams", crashParamsObjCmd, 0, 0);
Tcl_CreateObjCommand(interp, "sqlite3_crash_now", crashNowCmd, 0, 0);
Tcl_CreateObjCommand(interp, "sqlite3_simulate_device", devSymObjCmd, 0, 0);
Tcl_CreateObjCommand(interp, "sqlite3_crash_on_write", writeCrashObjCmd,0,0);
Tcl_CreateObjCommand(interp, "unregister_devsim", dsUnregisterObjCmd, 0, 0);
Tcl_CreateObjCommand(interp, "register_jt_vfs", jtObjCmd, 0, 0);
Tcl_CreateObjCommand(interp, "unregister_jt_vfs", jtUnregisterObjCmd, 0, 0);
+168 -50
View File
@@ -28,6 +28,7 @@
** Name used to identify this VFS.
*/
#define DEVSYM_VFS_NAME "devsym"
#define WRITECRASH_NAME "writecrash"
typedef struct devsym_file devsym_file;
struct devsym_file {
@@ -72,61 +73,13 @@ static int devsymRandomness(sqlite3_vfs*, int nByte, char *zOut);
static int devsymSleep(sqlite3_vfs*, int microseconds);
static int devsymCurrentTime(sqlite3_vfs*, double*);
static sqlite3_vfs devsym_vfs = {
2, /* iVersion */
sizeof(devsym_file), /* szOsFile */
DEVSYM_MAX_PATHNAME, /* mxPathname */
0, /* pNext */
DEVSYM_VFS_NAME, /* zName */
0, /* pAppData */
devsymOpen, /* xOpen */
devsymDelete, /* xDelete */
devsymAccess, /* xAccess */
devsymFullPathname, /* xFullPathname */
#ifndef SQLITE_OMIT_LOAD_EXTENSION
devsymDlOpen, /* xDlOpen */
devsymDlError, /* xDlError */
devsymDlSym, /* xDlSym */
devsymDlClose, /* xDlClose */
#else
0, /* xDlOpen */
0, /* xDlError */
0, /* xDlSym */
0, /* xDlClose */
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
devsymRandomness, /* xRandomness */
devsymSleep, /* xSleep */
devsymCurrentTime, /* xCurrentTime */
0, /* xGetLastError */
0 /* xCurrentTimeInt64 */
};
static sqlite3_io_methods devsym_io_methods = {
2, /* iVersion */
devsymClose, /* xClose */
devsymRead, /* xRead */
devsymWrite, /* xWrite */
devsymTruncate, /* xTruncate */
devsymSync, /* xSync */
devsymFileSize, /* xFileSize */
devsymLock, /* xLock */
devsymUnlock, /* xUnlock */
devsymCheckReservedLock, /* xCheckReservedLock */
devsymFileControl, /* xFileControl */
devsymSectorSize, /* xSectorSize */
devsymDeviceCharacteristics, /* xDeviceCharacteristics */
devsymShmMap, /* xShmMap */
devsymShmLock, /* xShmLock */
devsymShmBarrier, /* xShmBarrier */
devsymShmUnmap /* xShmUnmap */
};
struct DevsymGlobal {
sqlite3_vfs *pVfs;
int iDeviceChar;
int iSectorSize;
int nWriteCrash;
};
struct DevsymGlobal g = {0, 0, 512};
struct DevsymGlobal g = {0, 0, 512, 0};
/*
** Close an devsym-file.
@@ -271,6 +224,26 @@ static int devsymOpen(
int flags,
int *pOutFlags
){
static sqlite3_io_methods devsym_io_methods = {
2, /* iVersion */
devsymClose, /* xClose */
devsymRead, /* xRead */
devsymWrite, /* xWrite */
devsymTruncate, /* xTruncate */
devsymSync, /* xSync */
devsymFileSize, /* xFileSize */
devsymLock, /* xLock */
devsymUnlock, /* xUnlock */
devsymCheckReservedLock, /* xCheckReservedLock */
devsymFileControl, /* xFileControl */
devsymSectorSize, /* xSectorSize */
devsymDeviceCharacteristics, /* xDeviceCharacteristics */
devsymShmMap, /* xShmMap */
devsymShmLock, /* xShmLock */
devsymShmBarrier, /* xShmBarrier */
devsymShmUnmap /* xShmUnmap */
};
int rc;
devsym_file *p = (devsym_file *)pFile;
p->pReal = (sqlite3_file *)&p[1];
@@ -372,6 +345,137 @@ static int devsymCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
return g.pVfs->xCurrentTime(g.pVfs, pTimeOut);
}
/*
** Return the sector-size in bytes for an writecrash-file.
*/
static int writecrashSectorSize(sqlite3_file *pFile){
devsym_file *p = (devsym_file *)pFile;
return sqlite3OsSectorSize(p->pReal);
}
/*
** Return the device characteristic flags supported by an writecrash-file.
*/
static int writecrashDeviceCharacteristics(sqlite3_file *pFile){
devsym_file *p = (devsym_file *)pFile;
return sqlite3OsDeviceCharacteristics(p->pReal);
}
/*
** Write data to an writecrash-file.
*/
static int writecrashWrite(
sqlite3_file *pFile,
const void *zBuf,
int iAmt,
sqlite_int64 iOfst
){
devsym_file *p = (devsym_file *)pFile;
if( g.nWriteCrash>0 ){
g.nWriteCrash--;
if( g.nWriteCrash==0 ) abort();
}
return sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst);
}
/*
** Open an writecrash file handle.
*/
static int writecrashOpen(
sqlite3_vfs *pVfs,
const char *zName,
sqlite3_file *pFile,
int flags,
int *pOutFlags
){
static sqlite3_io_methods writecrash_io_methods = {
2, /* iVersion */
devsymClose, /* xClose */
devsymRead, /* xRead */
writecrashWrite, /* xWrite */
devsymTruncate, /* xTruncate */
devsymSync, /* xSync */
devsymFileSize, /* xFileSize */
devsymLock, /* xLock */
devsymUnlock, /* xUnlock */
devsymCheckReservedLock, /* xCheckReservedLock */
devsymFileControl, /* xFileControl */
writecrashSectorSize, /* xSectorSize */
writecrashDeviceCharacteristics, /* xDeviceCharacteristics */
devsymShmMap, /* xShmMap */
devsymShmLock, /* xShmLock */
devsymShmBarrier, /* xShmBarrier */
devsymShmUnmap /* xShmUnmap */
};
int rc;
devsym_file *p = (devsym_file *)pFile;
p->pReal = (sqlite3_file *)&p[1];
rc = sqlite3OsOpen(g.pVfs, zName, p->pReal, flags, pOutFlags);
if( p->pReal->pMethods ){
pFile->pMethods = &writecrash_io_methods;
}
return rc;
}
static sqlite3_vfs devsym_vfs = {
2, /* iVersion */
sizeof(devsym_file), /* szOsFile */
DEVSYM_MAX_PATHNAME, /* mxPathname */
0, /* pNext */
DEVSYM_VFS_NAME, /* zName */
0, /* pAppData */
devsymOpen, /* xOpen */
devsymDelete, /* xDelete */
devsymAccess, /* xAccess */
devsymFullPathname, /* xFullPathname */
#ifndef SQLITE_OMIT_LOAD_EXTENSION
devsymDlOpen, /* xDlOpen */
devsymDlError, /* xDlError */
devsymDlSym, /* xDlSym */
devsymDlClose, /* xDlClose */
#else
0, /* xDlOpen */
0, /* xDlError */
0, /* xDlSym */
0, /* xDlClose */
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
devsymRandomness, /* xRandomness */
devsymSleep, /* xSleep */
devsymCurrentTime, /* xCurrentTime */
0, /* xGetLastError */
0 /* xCurrentTimeInt64 */
};
static sqlite3_vfs writecrash_vfs = {
2, /* iVersion */
sizeof(devsym_file), /* szOsFile */
DEVSYM_MAX_PATHNAME, /* mxPathname */
0, /* pNext */
WRITECRASH_NAME, /* zName */
0, /* pAppData */
writecrashOpen, /* xOpen */
devsymDelete, /* xDelete */
devsymAccess, /* xAccess */
devsymFullPathname, /* xFullPathname */
#ifndef SQLITE_OMIT_LOAD_EXTENSION
devsymDlOpen, /* xDlOpen */
devsymDlError, /* xDlError */
devsymDlSym, /* xDlSym */
devsymDlClose, /* xDlClose */
#else
0, /* xDlOpen */
0, /* xDlError */
0, /* xDlSym */
0, /* xDlClose */
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
devsymRandomness, /* xRandomness */
devsymSleep, /* xSleep */
devsymCurrentTime, /* xCurrentTime */
0, /* xGetLastError */
0 /* xCurrentTimeInt64 */
};
/*
** This procedure registers the devsym vfs with SQLite. If the argument is
@@ -379,10 +483,13 @@ static int devsymCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
** available function in this file.
*/
void devsym_register(int iDeviceChar, int iSectorSize){
if( g.pVfs==0 ){
g.pVfs = sqlite3_vfs_find(0);
devsym_vfs.szOsFile += g.pVfs->szOsFile;
writecrash_vfs.szOsFile += g.pVfs->szOsFile;
sqlite3_vfs_register(&devsym_vfs, 0);
sqlite3_vfs_register(&writecrash_vfs, 0);
}
if( iDeviceChar>=0 ){
g.iDeviceChar = iDeviceChar;
@@ -403,4 +510,15 @@ void devsym_unregister(){
g.iSectorSize = 0;
}
void devsym_crash_on_write(int nWrite){
if( g.pVfs==0 ){
g.pVfs = sqlite3_vfs_find(0);
devsym_vfs.szOsFile += g.pVfs->szOsFile;
writecrash_vfs.szOsFile += g.pVfs->szOsFile;
sqlite3_vfs_register(&devsym_vfs, 0);
sqlite3_vfs_register(&writecrash_vfs, 0);
}
g.nWriteCrash = nWrite;
}
#endif
+88 -2
View File
@@ -15,6 +15,25 @@
**
** The emphasis of this file is a virtual table that provides
** access to TCL variables.
**
** The TCLVAR eponymous virtual table has a schema like this:
**
** CREATE TABLE tclvar(
** name TEXT, -- base name of the variable: "x" in "$x(y)"
** arrayname TEXT, -- array index name: "y" in "$x(y)"
** value TEXT, -- the value of the variable
** fullname TEXT, -- the full name of the variable
** PRIMARY KEY(fullname)
** ) WITHOUT ROWID;
**
** DELETE, INSERT, and UPDATE operations use the "fullname" field to
** determine the variable to be modified. Changing "value" to NULL
** deletes the variable.
**
** For SELECT operations, the "name" and "arrayname" fields will always
** match the "fullname" field. For DELETE, INSERT, and UPDATE, the
** "name" and "arrayname" fields are ignored and the variable is modified
** according to "fullname" and "value" only.
*/
#include "sqliteInt.h"
#if defined(INCLUDE_SQLITE_TCL_H)
@@ -67,7 +86,12 @@ static int tclvarConnect(
){
tclvar_vtab *pVtab;
static const char zSchema[] =
"CREATE TABLE whatever(name TEXT, arrayname TEXT, value TEXT)";
"CREATE TABLE x("
" name TEXT," /* Base name */
" arrayname TEXT," /* Array index */
" value TEXT," /* Value */
" fullname TEXT PRIMARY KEY" /* base(index) name */
") WITHOUT ROWID";
pVtab = sqlite3MallocZero( sizeof(*pVtab) );
if( pVtab==0 ) return SQLITE_NOMEM;
*ppVtab = &pVtab->base;
@@ -251,6 +275,16 @@ static int tclvarColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
sqlite3_result_text(ctx, Tcl_GetString(pVal), -1, SQLITE_TRANSIENT);
break;
}
case 3: {
char *z3;
if( p2 ){
z3 = sqlite3_mprintf("%s(%s)", z1, z2);
sqlite3_result_text(ctx, z3, -1, sqlite3_free);
}else{
sqlite3_result_text(ctx, z1, -1, SQLITE_TRANSIENT);
}
break;
}
}
return SQLITE_OK;
}
@@ -376,6 +410,58 @@ static int tclvarBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
return SQLITE_OK;
}
/*
** Invoked for any UPDATE, INSERT, or DELETE against a tclvar table
*/
static int tclvarUpdate(
sqlite3_vtab *tab,
int argc,
sqlite3_value **argv,
sqlite_int64 *pRowid
){
tclvar_vtab *pTab = (tclvar_vtab*)tab;
if( argc==1 ){
/* A DELETE operation. The variable to be deleted is stored in argv[0] */
const char *zVar = (const char*)sqlite3_value_text(argv[0]);
Tcl_UnsetVar(pTab->interp, zVar, TCL_GLOBAL_ONLY);
return SQLITE_OK;
}
if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
/* An INSERT operation */
const char *zValue = (const char*)sqlite3_value_text(argv[4]);
const char *zName;
if( sqlite3_value_type(argv[5])!=SQLITE_TEXT ){
tab->zErrMsg = sqlite3_mprintf("the 'fullname' column must be TEXT");
return SQLITE_ERROR;
}
zName = (const char*)sqlite3_value_text(argv[5]);
if( zValue ){
Tcl_SetVar(pTab->interp, zName, zValue, TCL_GLOBAL_ONLY);
}else{
Tcl_UnsetVar(pTab->interp, zName, TCL_GLOBAL_ONLY);
}
return SQLITE_OK;
}
if( sqlite3_value_type(argv[0])==SQLITE_TEXT
&& sqlite3_value_type(argv[1])==SQLITE_TEXT
){
/* An UPDATE operation */
const char *zOldName = (const char*)sqlite3_value_text(argv[0]);
const char *zNewName = (const char*)sqlite3_value_text(argv[1]);
const char *zValue = (const char*)sqlite3_value_text(argv[4]);
if( strcmp(zOldName, zNewName)!=0 || zValue==0 ){
Tcl_UnsetVar(pTab->interp, zOldName, TCL_GLOBAL_ONLY);
}
if( zValue!=0 ){
Tcl_SetVar(pTab->interp, zNewName, zValue, TCL_GLOBAL_ONLY);
}
return SQLITE_OK;
}
tab->zErrMsg = sqlite3_mprintf("prohibited TCL variable change");
return SQLITE_ERROR;
}
/*
** A virtual table module that provides read-only access to a
** Tcl global variable namespace.
@@ -394,7 +480,7 @@ static sqlite3_module tclvarModule = {
tclvarEof, /* xEof - check for end of scan */
tclvarColumn, /* xColumn - read data */
tclvarRowid, /* xRowid - read data */
0, /* xUpdate */
tclvarUpdate, /* xUpdate */
0, /* xBegin */
0, /* xSync */
0, /* xCommit */
+1 -1
View File
@@ -585,7 +585,7 @@ void sqlite3UnlinkAndDeleteTrigger(sqlite3 *db, int iDb, const char *zName){
*pp = (*pp)->pNext;
}
sqlite3DeleteTrigger(db, pTrigger);
db->flags |= SQLITE_InternChanges;
db->mDbFlags |= DBFLAG_SchemaChange;
}
}
+17 -6
View File
@@ -803,12 +803,6 @@ static void updateVirtualTable(
if( pWInfo==0 ) return;
/* Populate the argument registers. */
sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg);
if( pRowid ){
sqlite3ExprCode(pParse, pRowid, regArg+1);
}else{
sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg+1);
}
for(i=0; i<pTab->nCol; i++){
if( aXRef[i]>=0 ){
sqlite3ExprCode(pParse, pChanges->a[aXRef[i]].pExpr, regArg+2+i);
@@ -816,6 +810,23 @@ static void updateVirtualTable(
sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, i, regArg+2+i);
}
}
if( HasRowid(pTab) ){
sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg);
if( pRowid ){
sqlite3ExprCode(pParse, pRowid, regArg+1);
}else{
sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg+1);
}
}else{
Index *pPk; /* PRIMARY KEY index */
i16 iPk; /* PRIMARY KEY column */
pPk = sqlite3PrimaryKeyIndex(pTab);
assert( pPk!=0 );
assert( pPk->nKeyCol==1 );
iPk = pPk->aiColumn[0];
sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, iPk, regArg);
sqlite3VdbeAddOp2(v, OP_SCopy, regArg+2+iPk, regArg+1);
}
bOnePass = sqlite3WhereOkOnePass(pWInfo, aDummy);
+8 -5
View File
@@ -130,7 +130,8 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
int rc = SQLITE_OK; /* Return code from service routines */
Btree *pMain; /* The database being vacuumed */
Btree *pTemp; /* The temporary database we vacuum into */
int saved_flags; /* Saved value of the db->flags */
u16 saved_mDbFlags; /* Saved value of db->mDbFlags */
u32 saved_flags; /* Saved value of db->flags */
int saved_nChange; /* Saved value of db->nChange */
int saved_nTotalChange; /* Saved value of db->nTotalChange */
u8 saved_mTrace; /* Saved trace settings */
@@ -153,11 +154,12 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
** restored before returning. Then set the writable-schema flag, and
** disable CHECK and foreign key constraints. */
saved_flags = db->flags;
saved_mDbFlags = db->mDbFlags;
saved_nChange = db->nChange;
saved_nTotalChange = db->nTotalChange;
saved_mTrace = db->mTrace;
db->flags |= (SQLITE_WriteSchema | SQLITE_IgnoreChecks
| SQLITE_PreferBuiltin | SQLITE_Vacuum);
db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks;
db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum;
db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder | SQLITE_CountRows);
db->mTrace = 0;
@@ -268,8 +270,8 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
"WHERE type='table'AND coalesce(rootpage,1)>0",
zDbMain
);
assert( (db->flags & SQLITE_Vacuum)!=0 );
db->flags &= ~SQLITE_Vacuum;
assert( (db->mDbFlags & DBFLAG_Vacuum)!=0 );
db->mDbFlags &= ~DBFLAG_Vacuum;
if( rc!=SQLITE_OK ) goto end_of_vacuum;
/* Copy the triggers, views, and virtual tables from the main database
@@ -337,6 +339,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
end_of_vacuum:
/* Restore the original value of db->flags */
db->init.iDb = 0;
db->mDbFlags = saved_mDbFlags;
db->flags = saved_flags;
db->nChange = saved_nChange;
db->nTotalChange = saved_nTotalChange;
+49 -34
View File
@@ -1955,13 +1955,23 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl);
}
compare_op:
switch( pOp->opcode ){
case OP_Eq: res2 = res==0; break;
case OP_Ne: res2 = res; break;
case OP_Lt: res2 = res<0; break;
case OP_Le: res2 = res<=0; break;
case OP_Gt: res2 = res>0; break;
default: res2 = res>=0; break;
/* At this point, res is negative, zero, or positive if reg[P1] is
** less than, equal to, or greater than reg[P3], respectively. Compute
** the answer to this operator in res2, depending on what the comparison
** operator actually is. The next block of code depends on the fact
** that the 6 comparison operators are consecutive integers in this
** order: NE, EQ, GT, LE, LT, GE */
assert( OP_Eq==OP_Ne+1 ); assert( OP_Gt==OP_Ne+2 ); assert( OP_Le==OP_Ne+3 );
assert( OP_Lt==OP_Ne+4 ); assert( OP_Ge==OP_Ne+5 );
if( res<0 ){ /* ne, eq, gt, le, lt, ge */
static const unsigned char aLTb[] = { 1, 0, 0, 1, 1, 0 };
res2 = aLTb[pOp->opcode - OP_Ne];
}else if( res==0 ){
static const unsigned char aEQb[] = { 0, 1, 0, 1, 0, 1 };
res2 = aEQb[pOp->opcode - OP_Ne];
}else{
static const unsigned char aGTb[] = { 1, 0, 1, 0, 0, 1 };
res2 = aGTb[pOp->opcode - OP_Ne];
}
/* Undo any changes made by applyAffinity() to the input registers. */
@@ -1973,7 +1983,6 @@ compare_op:
if( pOp->p5 & SQLITE_STOREP2 ){
pOut = &aMem[pOp->p2];
iCompare = res;
res2 = res2!=0; /* For this path res2 must be exactly 0 or 1 */
if( (pOp->p5 & SQLITE_KEEPNULL)!=0 ){
/* The KEEPNULL flag prevents OP_Eq from overwriting a NULL with 1
** and prevents OP_Ne from overwriting NULL with 0. This flag
@@ -2104,7 +2113,7 @@ case OP_Compare: {
assert( memIsValid(&aMem[p2+idx]) );
REGISTER_TRACE(p1+idx, &aMem[p1+idx]);
REGISTER_TRACE(p2+idx, &aMem[p2+idx]);
assert( i<pKeyInfo->nField );
assert( i<pKeyInfo->nKeyField );
pColl = pKeyInfo->aColl[i];
bRev = pKeyInfo->aSortOrder[i];
iCompare = sqlite3MemCompare(&aMem[p1+idx], &aMem[p2+idx], pColl);
@@ -2950,7 +2959,7 @@ case OP_Savepoint: {
int isSchemaChange;
iSavepoint = db->nSavepoint - iSavepoint - 1;
if( p1==SAVEPOINT_ROLLBACK ){
isSchemaChange = (db->flags & SQLITE_InternChanges)!=0;
isSchemaChange = (db->mDbFlags & DBFLAG_SchemaChange)!=0;
for(ii=0; ii<db->nDb; ii++){
rc = sqlite3BtreeTripAllCursors(db->aDb[ii].pBt,
SQLITE_ABORT_ROLLBACK,
@@ -2969,7 +2978,7 @@ case OP_Savepoint: {
if( isSchemaChange ){
sqlite3ExpirePreparedStatements(db);
sqlite3ResetAllSchemasOfConnection(db);
db->flags = (db->flags | SQLITE_InternChanges);
db->mDbFlags |= DBFLAG_SchemaChange;
}
}
@@ -3249,7 +3258,7 @@ case OP_SetCookie: {
if( pOp->p2==BTREE_SCHEMA_VERSION ){
/* When the schema cookie changes, record the new cookie internally */
pDb->pSchema->schema_cookie = pOp->p3;
db->flags |= SQLITE_InternChanges;
db->mDbFlags |= DBFLAG_SchemaChange;
}else if( pOp->p2==BTREE_FILE_FORMAT ){
/* Record changes in the file format */
pDb->pSchema->file_format = pOp->p3;
@@ -3398,7 +3407,7 @@ case OP_OpenWrite:
pKeyInfo = pOp->p4.pKeyInfo;
assert( pKeyInfo->enc==ENC(db) );
assert( pKeyInfo->db==db );
nField = pKeyInfo->nField+pKeyInfo->nXField;
nField = pKeyInfo->nAllField;
}else if( pOp->p4type==P4_INT32 ){
nField = pOp->p4.i;
}
@@ -4776,7 +4785,17 @@ case OP_NullRow: {
break;
}
/* Opcode: Last P1 P2 P3 * *
/* Opcode: SeekEnd P1 * * * *
**
** Position cursor P1 at the end of the btree for the purpose of
** appending a new entry onto the btree.
**
** It is assumed that the cursor is used only for appending and so
** if the cursor is valid, then the cursor must already be pointing
** at the end of the btree and so no changes are made to
** the cursor.
*/
/* Opcode: Last P1 P2 * * *
**
** The next use of the Rowid or Column or Prev instruction for P1
** will refer to the last entry in the database table or index.
@@ -4787,14 +4806,8 @@ case OP_NullRow: {
** This opcode leaves the cursor configured to move in reverse order,
** from the end toward the beginning. In other words, the cursor is
** configured to use Prev, not Next.
**
** If P3 is -1, then the cursor is positioned at the end of the btree
** for the purpose of appending a new entry onto the btree. In that
** case P2 must be 0. It is assumed that the cursor is used only for
** appending and so if the cursor is valid, then the cursor must already
** be pointing at the end of the btree and so no changes are made to
** the cursor.
*/
case OP_SeekEnd:
case OP_Last: { /* jump */
VdbeCursor *pC;
BtCursor *pCrsr;
@@ -4807,22 +4820,24 @@ case OP_Last: { /* jump */
pCrsr = pC->uc.pCursor;
res = 0;
assert( pCrsr!=0 );
pC->seekResult = pOp->p3;
#ifdef SQLITE_DEBUG
pC->seekOp = OP_Last;
pC->seekOp = pOp->opcode;
#endif
if( pOp->p3==0 || !sqlite3BtreeCursorIsValidNN(pCrsr) ){
rc = sqlite3BtreeLast(pCrsr, &res);
pC->nullRow = (u8)res;
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
if( rc ) goto abort_due_to_error;
if( pOp->p2>0 ){
VdbeBranchTaken(res!=0,2);
if( res ) goto jump_to_p2;
}
}else{
if( pOp->opcode==OP_SeekEnd ){
assert( pOp->p2==0 );
pC->seekResult = -1;
if( sqlite3BtreeCursorIsValidNN(pCrsr) ){
break;
}
}
rc = sqlite3BtreeLast(pCrsr, &res);
pC->nullRow = (u8)res;
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
if( rc ) goto abort_due_to_error;
if( pOp->p2>0 ){
VdbeBranchTaken(res!=0,2);
if( res ) goto jump_to_p2;
}
break;
}
+26 -29
View File
@@ -398,8 +398,7 @@ void sqlite3_result_pointer(
){
Mem *pOut = pCtx->pOut;
assert( sqlite3_mutex_held(pOut->db->mutex) );
sqlite3VdbeMemRelease(pOut);
pOut->flags = MEM_Null;
sqlite3VdbeMemSetNull(pOut);
sqlite3VdbeMemSetPointer(pOut, pPtr, zPType, xDestructor);
}
void sqlite3_result_subtype(sqlite3_context *pCtx, unsigned int eSubtype){
@@ -524,7 +523,7 @@ static int doWalCallbacks(sqlite3 *db){
sqlite3BtreeEnter(pBt);
nEntry = sqlite3PagerWalCallback(sqlite3BtreePager(pBt));
sqlite3BtreeLeave(pBt);
if( db->xWalCallback && nEntry>0 && rc==SQLITE_OK ){
if( nEntry>0 && db->xWalCallback && rc==SQLITE_OK ){
rc = db->xWalCallback(db->pWalArg, db, db->aDb[i].zDbSName, nEntry);
}
}
@@ -634,7 +633,7 @@ static int sqlite3Step(Vdbe *p){
if( rc!=SQLITE_ROW ) checkProfileCallback(db, p);
#endif
if( rc==SQLITE_DONE ){
if( rc==SQLITE_DONE && db->autoCommit ){
assert( p->rc==SQLITE_OK );
p->rc = doWalCallbacks(db);
if( p->rc!=SQLITE_OK ){
@@ -678,7 +677,6 @@ end_of_step:
*/
int sqlite3_step(sqlite3_stmt *pStmt){
int rc = SQLITE_OK; /* Result from sqlite3Step() */
int rc2 = SQLITE_OK; /* Result from sqlite3Reprepare() */
Vdbe *v = (Vdbe*)pStmt; /* the prepared statement */
int cnt = 0; /* Counter to prevent infinite loop of reprepares */
sqlite3 *db; /* The database connection */
@@ -692,32 +690,31 @@ int sqlite3_step(sqlite3_stmt *pStmt){
while( (rc = sqlite3Step(v))==SQLITE_SCHEMA
&& cnt++ < SQLITE_MAX_SCHEMA_RETRY ){
int savedPc = v->pc;
rc2 = rc = sqlite3Reprepare(v);
if( rc!=SQLITE_OK) break;
rc = sqlite3Reprepare(v);
if( rc!=SQLITE_OK ){
/* This case occurs after failing to recompile an sql statement.
** The error message from the SQL compiler has already been loaded
** into the database handle. This block copies the error message
** from the database handle into the statement and sets the statement
** program counter to 0 to ensure that when the statement is
** finalized or reset the parser error message is available via
** sqlite3_errmsg() and sqlite3_errcode().
*/
const char *zErr = (const char *)sqlite3_value_text(db->pErr);
sqlite3DbFree(db, v->zErrMsg);
if( !db->mallocFailed ){
v->zErrMsg = sqlite3DbStrDup(db, zErr);
v->rc = rc = sqlite3ApiExit(db, rc);
} else {
v->zErrMsg = 0;
v->rc = rc = SQLITE_NOMEM_BKPT;
}
break;
}
sqlite3_reset(pStmt);
if( savedPc>=0 ) v->doingRerun = 1;
assert( v->expired==0 );
}
if( rc2!=SQLITE_OK ){
/* This case occurs after failing to recompile an sql statement.
** The error message from the SQL compiler has already been loaded
** into the database handle. This block copies the error message
** from the database handle into the statement and sets the statement
** program counter to 0 to ensure that when the statement is
** finalized or reset the parser error message is available via
** sqlite3_errmsg() and sqlite3_errcode().
*/
const char *zErr = (const char *)sqlite3_value_text(db->pErr);
sqlite3DbFree(db, v->zErrMsg);
if( !db->mallocFailed ){
v->zErrMsg = sqlite3DbStrDup(db, zErr);
v->rc = rc2;
} else {
v->zErrMsg = 0;
v->rc = rc = SQLITE_NOMEM_BKPT;
}
}
rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(db->mutex);
return rc;
}
@@ -1723,7 +1720,7 @@ static UnpackedRecord *vdbeUnpackRecord(
pRet = sqlite3VdbeAllocUnpackedRecord(pKeyInfo);
if( pRet ){
memset(pRet->aMem, 0, sizeof(Mem)*(pKeyInfo->nField+1));
memset(pRet->aMem, 0, sizeof(Mem)*(pKeyInfo->nKeyField+1));
sqlite3VdbeRecordUnpack(pKeyInfo, nKey, pKey, pRet);
}
return pRet;
@@ -1796,7 +1793,7 @@ int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
*/
int sqlite3_preupdate_count(sqlite3 *db){
PreUpdate *p = db->pPreUpdate;
return (p ? p->keyinfo.nField : 0);
return (p ? p->keyinfo.nKeyField : 0);
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
+51 -32
View File
@@ -597,6 +597,27 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
p->bIsReader = 1;
break;
}
case OP_Next:
case OP_NextIfOpen:
case OP_SorterNext: {
pOp->p4.xAdvance = sqlite3BtreeNext;
pOp->p4type = P4_ADVANCE;
/* The code generator never codes any of these opcodes as a jump
** to a label. They are always coded as a jump backwards to a
** known address */
assert( pOp->p2>=0 );
break;
}
case OP_Prev:
case OP_PrevIfOpen: {
pOp->p4.xAdvance = sqlite3BtreePrevious;
pOp->p4type = P4_ADVANCE;
/* The code generator never codes any of these opcodes as a jump
** to a label. They are always coded as a jump backwards to a
** known address */
assert( pOp->p2>=0 );
break;
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
case OP_VUpdate: {
if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2;
@@ -608,27 +629,25 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
assert( pOp[-1].opcode==OP_Integer );
n = pOp[-1].p1;
if( n>nMaxArgs ) nMaxArgs = n;
break;
/* Fall through into the default case */
}
#endif
case OP_Next:
case OP_NextIfOpen:
case OP_SorterNext: {
pOp->p4.xAdvance = sqlite3BtreeNext;
pOp->p4type = P4_ADVANCE;
break;
}
case OP_Prev:
case OP_PrevIfOpen: {
pOp->p4.xAdvance = sqlite3BtreePrevious;
pOp->p4type = P4_ADVANCE;
default: {
if( pOp->p2<0 ){
/* The mkopcodeh.tcl script has so arranged things that the only
** non-jump opcodes less than SQLITE_MX_JUMP_CODE are guaranteed to
** have non-negative values for P2. */
assert( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 );
assert( ADDR(pOp->p2)<pParse->nLabel );
pOp->p2 = aLabel[ADDR(pOp->p2)];
}
break;
}
}
if( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 && pOp->p2<0 ){
assert( ADDR(pOp->p2)<pParse->nLabel );
pOp->p2 = aLabel[ADDR(pOp->p2)];
}
/* The mkopcodeh.tcl script has so arranged things that the only
** non-jump opcodes less than SQLITE_MX_JUMP_CODE are guaranteed to
** have non-negative values for P2. */
assert( (sqlite3OpcodeProperty[pOp->opcode]&OPFLG_JUMP)==0 || pOp->p2>=0);
}
if( pOp==p->aOp ) break;
pOp--;
@@ -1301,8 +1320,8 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
int j;
KeyInfo *pKeyInfo = pOp->p4.pKeyInfo;
assert( pKeyInfo->aSortOrder!=0 );
sqlite3XPrintf(&x, "k(%d", pKeyInfo->nField);
for(j=0; j<pKeyInfo->nField; j++){
sqlite3XPrintf(&x, "k(%d", pKeyInfo->nKeyField);
for(j=0; j<pKeyInfo->nKeyField; j++){
CollSeq *pColl = pKeyInfo->aColl[j];
const char *zColl = pColl ? pColl->zName : "";
if( strcmp(zColl, "BINARY")==0 ) zColl = "B";
@@ -3528,13 +3547,13 @@ UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(
){
UnpackedRecord *p; /* Unpacked record to return */
int nByte; /* Number of bytes required for *p */
nByte = ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nField+1);
nByte = ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nKeyField+1);
p = (UnpackedRecord *)sqlite3DbMallocRaw(pKeyInfo->db, nByte);
if( !p ) return 0;
p->aMem = (Mem*)&((char*)p)[ROUND8(sizeof(UnpackedRecord))];
assert( pKeyInfo->aSortOrder!=0 );
p->pKeyInfo = pKeyInfo;
p->nField = pKeyInfo->nField + 1;
p->nField = pKeyInfo->nKeyField + 1;
return p;
}
@@ -3574,7 +3593,7 @@ void sqlite3VdbeRecordUnpack(
pMem++;
if( (++u)>=p->nField ) break;
}
assert( u<=pKeyInfo->nField + 1 );
assert( u<=pKeyInfo->nKeyField + 1 );
p->nField = u;
}
@@ -3623,9 +3642,9 @@ static int vdbeRecordCompareDebug(
idx1 = getVarint32(aKey1, szHdr1);
if( szHdr1>98307 ) return SQLITE_CORRUPT;
d1 = szHdr1;
assert( pKeyInfo->nField+pKeyInfo->nXField>=pPKey2->nField || CORRUPT_DB );
assert( pKeyInfo->nAllField>=pPKey2->nField || CORRUPT_DB );
assert( pKeyInfo->aSortOrder!=0 );
assert( pKeyInfo->nField>0 );
assert( pKeyInfo->nKeyField>0 );
assert( idx1<=szHdr1 || CORRUPT_DB );
do{
u32 serial_type1;
@@ -3687,12 +3706,12 @@ debugCompareEnd:
/*
** Count the number of fields (a.k.a. columns) in the record given by
** pKey,nKey. The verify that this count is less than or equal to the
** limit given by pKeyInfo->nField + pKeyInfo->nXField.
** limit given by pKeyInfo->nAllField.
**
** If this constraint is not satisfied, it means that the high-speed
** vdbeRecordCompareInt() and vdbeRecordCompareString() routines will
** not work correctly. If this assert() ever fires, it probably means
** that the KeyInfo.nField or KeyInfo.nXField values were computed
** that the KeyInfo.nKeyField or KeyInfo.nAllField values were computed
** incorrectly.
*/
static void vdbeAssertFieldCountWithinLimits(
@@ -3713,7 +3732,7 @@ static void vdbeAssertFieldCountWithinLimits(
idx += getVarint32(aKey+idx, notUsed);
nField++;
}
assert( nField <= pKeyInfo->nField+pKeyInfo->nXField );
assert( nField <= pKeyInfo->nAllField );
}
#else
# define vdbeAssertFieldCountWithinLimits(A,B,C)
@@ -4018,10 +4037,10 @@ int sqlite3VdbeRecordCompareWithSkip(
}
VVA_ONLY( mem1.szMalloc = 0; ) /* Only needed by assert() statements */
assert( pPKey2->pKeyInfo->nField+pPKey2->pKeyInfo->nXField>=pPKey2->nField
assert( pPKey2->pKeyInfo->nAllField>=pPKey2->nField
|| CORRUPT_DB );
assert( pPKey2->pKeyInfo->aSortOrder!=0 );
assert( pPKey2->pKeyInfo->nField>0 );
assert( pPKey2->pKeyInfo->nKeyField>0 );
assert( idx1<=szHdr1 || CORRUPT_DB );
do{
u32 serial_type;
@@ -4354,7 +4373,7 @@ RecordCompare sqlite3VdbeFindCompare(UnpackedRecord *p){
** The easiest way to enforce this limit is to consider only records with
** 13 fields or less. If the first field is an integer, the maximum legal
** header size is (12*5 + 1 + 1) bytes. */
if( (p->pKeyInfo->nField + p->pKeyInfo->nXField)<=13 ){
if( p->pKeyInfo->nAllField<=13 ){
int flags = p->aMem[0].flags;
if( p->pKeyInfo->aSortOrder[0] ){
p->r1 = 1;
@@ -4689,7 +4708,7 @@ void sqlite3VdbePreUpdateHook(
preupdate.iNewReg = iReg;
preupdate.keyinfo.db = db;
preupdate.keyinfo.enc = ENC(db);
preupdate.keyinfo.nField = pTab->nCol;
preupdate.keyinfo.nKeyField = pTab->nCol;
preupdate.keyinfo.aSortOrder = (u8*)&fakeSortOrder;
preupdate.iKey1 = iKey1;
preupdate.iKey2 = iKey2;
@@ -4699,8 +4718,8 @@ void sqlite3VdbePreUpdateHook(
db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2);
db->pPreUpdate = 0;
sqlite3DbFree(db, preupdate.aRecord);
vdbeFreeUnpacked(db, preupdate.keyinfo.nField+1, preupdate.pUnpacked);
vdbeFreeUnpacked(db, preupdate.keyinfo.nField+1, preupdate.pNewUnpacked);
vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pUnpacked);
vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pNewUnpacked);
if( preupdate.aNew ){
int i;
for(i=0; i<pCsr->nField; i++){
+17 -21
View File
@@ -129,8 +129,8 @@ int sqlite3_blob_open(
int rc = SQLITE_OK;
char *zErr = 0;
Table *pTab;
Parse *pParse = 0;
Incrblob *pBlob = 0;
Parse sParse;
#ifdef SQLITE_ENABLE_API_ARMOR
if( ppBlob==0 ){
@@ -148,37 +148,34 @@ int sqlite3_blob_open(
sqlite3_mutex_enter(db->mutex);
pBlob = (Incrblob *)sqlite3DbMallocZero(db, sizeof(Incrblob));
if( !pBlob ) goto blob_open_out;
pParse = sqlite3StackAllocRaw(db, sizeof(*pParse));
if( !pParse ) goto blob_open_out;
do {
memset(pParse, 0, sizeof(Parse));
pParse->db = db;
memset(&sParse, 0, sizeof(Parse));
if( !pBlob ) goto blob_open_out;
sParse.db = db;
sqlite3DbFree(db, zErr);
zErr = 0;
sqlite3BtreeEnterAll(db);
pTab = sqlite3LocateTable(pParse, 0, zTable, zDb);
pTab = sqlite3LocateTable(&sParse, 0, zTable, zDb);
if( pTab && IsVirtual(pTab) ){
pTab = 0;
sqlite3ErrorMsg(pParse, "cannot open virtual table: %s", zTable);
sqlite3ErrorMsg(&sParse, "cannot open virtual table: %s", zTable);
}
if( pTab && !HasRowid(pTab) ){
pTab = 0;
sqlite3ErrorMsg(pParse, "cannot open table without rowid: %s", zTable);
sqlite3ErrorMsg(&sParse, "cannot open table without rowid: %s", zTable);
}
#ifndef SQLITE_OMIT_VIEW
if( pTab && pTab->pSelect ){
pTab = 0;
sqlite3ErrorMsg(pParse, "cannot open view: %s", zTable);
sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable);
}
#endif
if( !pTab ){
if( pParse->zErrMsg ){
if( sParse.zErrMsg ){
sqlite3DbFree(db, zErr);
zErr = pParse->zErrMsg;
pParse->zErrMsg = 0;
zErr = sParse.zErrMsg;
sParse.zErrMsg = 0;
}
rc = SQLITE_ERROR;
sqlite3BtreeLeaveAll(db);
@@ -242,7 +239,7 @@ int sqlite3_blob_open(
}
}
pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(pParse);
pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(&sParse);
assert( pBlob->pStmt || db->mallocFailed );
if( pBlob->pStmt ){
@@ -315,10 +312,10 @@ int sqlite3_blob_open(
aOp[1].p4.i = pTab->nCol+1;
aOp[3].p2 = pTab->nCol;
pParse->nVar = 0;
pParse->nMem = 1;
pParse->nTab = 1;
sqlite3VdbeMakeReady(v, pParse);
sParse.nVar = 0;
sParse.nMem = 1;
sParse.nTab = 1;
sqlite3VdbeMakeReady(v, &sParse);
}
}
@@ -340,8 +337,7 @@ blob_open_out:
}
sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr);
sqlite3DbFree(db, zErr);
sqlite3ParserReset(pParse);
sqlite3StackFree(db, pParse);
sqlite3ParserReset(&sParse);
rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(db->mutex);
return rc;
+2 -2
View File
@@ -1163,7 +1163,7 @@ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){
if( pRec ){
pRec->pKeyInfo = sqlite3KeyInfoOfIndex(p->pParse, pIdx);
if( pRec->pKeyInfo ){
assert( pRec->pKeyInfo->nField+pRec->pKeyInfo->nXField==nCol );
assert( pRec->pKeyInfo->nAllField==nCol );
assert( pRec->pKeyInfo->enc==ENC(db) );
pRec->aMem = (Mem *)((u8*)pRec + ROUND8(sizeof(UnpackedRecord)));
for(i=0; i<nCol; i++){
@@ -1699,7 +1699,7 @@ int sqlite3Stat4Column(
void sqlite3Stat4ProbeFree(UnpackedRecord *pRec){
if( pRec ){
int i;
int nCol = pRec->pKeyInfo->nField+pRec->pKeyInfo->nXField;
int nCol = pRec->pKeyInfo->nAllField;
Mem *aMem = pRec->aMem;
sqlite3 *db = aMem[0].db;
for(i=0; i<nCol; i++){
+7 -8
View File
@@ -823,7 +823,7 @@ static int vdbeSorterCompareText(
}
if( res==0 ){
if( pTask->pSorter->pKeyInfo->nField>1 ){
if( pTask->pSorter->pKeyInfo->nKeyField>1 ){
res = vdbeSorterCompareTail(
pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2
);
@@ -892,7 +892,7 @@ static int vdbeSorterCompareInt(
}
if( res==0 ){
if( pTask->pSorter->pKeyInfo->nField>1 ){
if( pTask->pSorter->pKeyInfo->nKeyField>1 ){
res = vdbeSorterCompareTail(
pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2
);
@@ -907,7 +907,7 @@ static int vdbeSorterCompareInt(
/*
** Initialize the temporary index cursor just opened as a sorter cursor.
**
** Usually, the sorter module uses the value of (pCsr->pKeyInfo->nField)
** Usually, the sorter module uses the value of (pCsr->pKeyInfo->nKeyField)
** to determine the number of fields that should be compared from the
** records being sorted. However, if the value passed as argument nField
** is non-zero and the sorter is able to guarantee a stable sort, nField
@@ -960,7 +960,7 @@ int sqlite3VdbeSorterInit(
assert( pCsr->pKeyInfo && pCsr->pBtx==0 );
assert( pCsr->eCurType==CURTYPE_SORTER );
szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nField-1)*sizeof(CollSeq*);
szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nKeyField-1)*sizeof(CollSeq*);
sz = sizeof(VdbeSorter) + nWorker * sizeof(SortSubtask);
pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo);
@@ -972,8 +972,7 @@ int sqlite3VdbeSorterInit(
memcpy(pKeyInfo, pCsr->pKeyInfo, szKeyInfo);
pKeyInfo->db = 0;
if( nField && nWorker==0 ){
pKeyInfo->nXField += (pKeyInfo->nField - nField);
pKeyInfo->nField = nField;
pKeyInfo->nKeyField = nField;
}
pSorter->pgsz = pgsz = sqlite3BtreeGetPageSize(db->aDb[0].pBt);
pSorter->nTask = nWorker + 1;
@@ -1013,7 +1012,7 @@ int sqlite3VdbeSorterInit(
}
}
if( (pKeyInfo->nField+pKeyInfo->nXField)<13
if( pKeyInfo->nAllField<13
&& (pKeyInfo->aColl[0]==0 || pKeyInfo->aColl[0]==db->pDfltColl)
){
pSorter->typeMask = SORTER_TYPE_INTEGER | SORTER_TYPE_TEXT;
@@ -1328,7 +1327,7 @@ static int vdbeSortAllocUnpacked(SortSubtask *pTask){
if( pTask->pUnpacked==0 ){
pTask->pUnpacked = sqlite3VdbeAllocUnpackedRecord(pTask->pSorter->pKeyInfo);
if( pTask->pUnpacked==0 ) return SQLITE_NOMEM_BKPT;
pTask->pUnpacked->nField = pTask->pSorter->pKeyInfo->nField;
pTask->pUnpacked->nField = pTask->pSorter->pKeyInfo->nKeyField;
pTask->pUnpacked->errCode = 0;
}
return SQLITE_OK;
+48 -47
View File
@@ -644,6 +644,7 @@ int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){
rc = vtabCallConstructor(db, pTab, pMod, pMod->pModule->xConnect, &zErr);
if( rc!=SQLITE_OK ){
sqlite3ErrorMsg(pParse, "%s", zErr);
pParse->rc = rc;
}
sqlite3DbFree(db, zErr);
}
@@ -733,10 +734,10 @@ int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, char **pzErr){
*/
int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
VtabCtx *pCtx;
Parse *pParse;
int rc = SQLITE_OK;
Table *pTab;
char *zErr = 0;
Parse sParse;
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) || zCreateTable==0 ){
@@ -753,55 +754,55 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
pTab = pCtx->pTab;
assert( IsVirtual(pTab) );
pParse = sqlite3StackAllocZero(db, sizeof(*pParse));
if( pParse==0 ){
rc = SQLITE_NOMEM_BKPT;
}else{
pParse->declareVtab = 1;
pParse->db = db;
pParse->nQueryLoop = 1;
if( SQLITE_OK==sqlite3RunParser(pParse, zCreateTable, &zErr)
&& pParse->pNewTable
&& !db->mallocFailed
&& !pParse->pNewTable->pSelect
&& !IsVirtual(pParse->pNewTable)
){
if( !pTab->aCol ){
Table *pNew = pParse->pNewTable;
Index *pIdx;
pTab->aCol = pNew->aCol;
pTab->nCol = pNew->nCol;
pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid);
pNew->nCol = 0;
pNew->aCol = 0;
assert( pTab->pIndex==0 );
if( !HasRowid(pNew) && pCtx->pVTable->pMod->pModule->xUpdate!=0 ){
rc = SQLITE_ERROR;
}
pIdx = pNew->pIndex;
if( pIdx ){
assert( pIdx->pNext==0 );
pTab->pIndex = pIdx;
pNew->pIndex = 0;
pIdx->pTable = pTab;
}
memset(&sParse, 0, sizeof(sParse));
sParse.declareVtab = 1;
sParse.db = db;
sParse.nQueryLoop = 1;
if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable, &zErr)
&& sParse.pNewTable
&& !db->mallocFailed
&& !sParse.pNewTable->pSelect
&& !IsVirtual(sParse.pNewTable)
){
if( !pTab->aCol ){
Table *pNew = sParse.pNewTable;
Index *pIdx;
pTab->aCol = pNew->aCol;
pTab->nCol = pNew->nCol;
pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid);
pNew->nCol = 0;
pNew->aCol = 0;
assert( pTab->pIndex==0 );
assert( HasRowid(pNew) || sqlite3PrimaryKeyIndex(pNew)!=0 );
if( !HasRowid(pNew)
&& pCtx->pVTable->pMod->pModule->xUpdate!=0
&& sqlite3PrimaryKeyIndex(pNew)->nKeyCol!=1
){
/* WITHOUT ROWID virtual tables must either be read-only (xUpdate==0)
** or else must have a single-column PRIMARY KEY */
rc = SQLITE_ERROR;
}
pIdx = pNew->pIndex;
if( pIdx ){
assert( pIdx->pNext==0 );
pTab->pIndex = pIdx;
pNew->pIndex = 0;
pIdx->pTable = pTab;
}
pCtx->bDeclared = 1;
}else{
sqlite3ErrorWithMsg(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr);
sqlite3DbFree(db, zErr);
rc = SQLITE_ERROR;
}
pParse->declareVtab = 0;
if( pParse->pVdbe ){
sqlite3VdbeFinalize(pParse->pVdbe);
}
sqlite3DeleteTable(db, pParse->pNewTable);
sqlite3ParserReset(pParse);
sqlite3StackFree(db, pParse);
pCtx->bDeclared = 1;
}else{
sqlite3ErrorWithMsg(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr);
sqlite3DbFree(db, zErr);
rc = SQLITE_ERROR;
}
sParse.declareVtab = 0;
if( sParse.pVdbe ){
sqlite3VdbeFinalize(sParse.pVdbe);
}
sqlite3DeleteTable(db, sParse.pNewTable);
sqlite3ParserReset(&sParse);
assert( (rc&0xff)==rc );
rc = sqlite3ApiExit(db, rc);
+1 -1
View File
@@ -794,7 +794,7 @@ static int codeCursorHintIsOrFunction(Walker *pWalker, Expr *pExpr){
pWalker->eCode = 1;
}else if( pExpr->op==TK_FUNCTION ){
int d1;
char d2[3];
char d2[4];
if( 0==sqlite3IsLikeFunction(pWalker->pParse->db, pExpr, &d1, d2) ){
pWalker->eCode = 1;
}
+34 -6
View File
@@ -194,12 +194,12 @@ static int isLikeOrGlob(
int *pisComplete, /* True if the only wildcard is % in the last character */
int *pnoCase /* True if uppercase is equivalent to lowercase */
){
const char *z = 0; /* String on RHS of LIKE operator */
const u8 *z = 0; /* String on RHS of LIKE operator */
Expr *pRight, *pLeft; /* Right and left size of LIKE operator */
ExprList *pList; /* List of operands to the LIKE operator */
int c; /* One character in z[] */
int cnt; /* Number of non-wildcard prefix characters */
char wc[3]; /* Wildcard characters */
char wc[4]; /* Wildcard characters */
sqlite3 *db = pParse->db; /* Database connection */
sqlite3_value *pVal = 0;
int op; /* Opcode of pRight */
@@ -221,12 +221,12 @@ static int isLikeOrGlob(
int iCol = pRight->iColumn;
pVal = sqlite3VdbeGetBoundValue(pReprepare, iCol, SQLITE_AFF_BLOB);
if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){
z = (char *)sqlite3_value_text(pVal);
z = sqlite3_value_text(pVal);
}
sqlite3VdbeSetVarmask(pParse->pVdbe, iCol);
assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER );
}else if( op==TK_STRING ){
z = pRight->u.zToken;
z = (u8*)pRight->u.zToken;
}
if( z ){
@@ -246,16 +246,44 @@ static int isLikeOrGlob(
return 0;
}
}
/* Count the number of prefix characters prior to the first wildcard */
cnt = 0;
while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){
cnt++;
if( c==wc[3] && z[cnt]!=0 ){
if( z[cnt++]>0xc0 ) while( (z[cnt]&0xc0)==0x80 ){ cnt++; }
}
}
/* The optimization is possible only if (1) the pattern does not begin
** with a wildcard and if (2) the non-wildcard prefix does not end with
** an (illegal 0xff) character. The second condition is necessary so
** that we can increment the prefix key to find an upper bound for the
** range search.
*/
if( cnt!=0 && 255!=(u8)z[cnt-1] ){
Expr *pPrefix;
/* A "complete" match if the pattern ends with "*" or "%" */
*pisComplete = c==wc[0] && z[cnt+1]==0;
pPrefix = sqlite3Expr(db, TK_STRING, z);
if( pPrefix ) pPrefix->u.zToken[cnt] = 0;
/* Get the pattern prefix. Remove all escapes from the prefix. */
pPrefix = sqlite3Expr(db, TK_STRING, (char*)z);
if( pPrefix ){
int iFrom, iTo;
char *zNew = pPrefix->u.zToken;
zNew[cnt] = 0;
for(iFrom=iTo=0; iFrom<cnt; iFrom++){
if( zNew[iFrom]==wc[3] ) iFrom++;
zNew[iTo++] = zNew[iFrom];
}
zNew[iTo] = 0;
}
*ppPrefix = pPrefix;
/* If the RHS pattern is a bound parameter, make arrangements to
** reprepare the statement when that parameter is rebound */
if( op==TK_VARIABLE ){
Vdbe *v = pParse->pVdbe;
sqlite3VdbeSetVarmask(v, pRight->iColumn);
+41
View File
@@ -0,0 +1,41 @@
# 2015-11-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. The
# focus of this file is testing the WITH clause.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix atomic
db close
if {[atomic_batch_write test.db]==0} {
puts "No f2fs atomic-batch-write support. Skipping tests..."
finish_test
return
}
reset_db
do_execsql_test 1.0 {
CREATE TABLE t1(x, y);
BEGIN;
INSERT INTO t1 VALUES(1, 2);
}
do_test 1.1 { file exists test.db-journal } {0}
do_execsql_test 1.2 {
COMMIT;
}
finish_test
+3 -8
View File
@@ -374,8 +374,9 @@ do_test attach2-6.1 {
do_test attach2-6.2 {
catchsql {
ATTACH 'test3.db' as aux2;
DETACH aux2;
}
} {1 {cannot ATTACH database within transaction}}
} {0 {}}
# EVIDENCE-OF: R-59740-55581 This statement will fail if SQLite is in
# the middle of a transaction.
@@ -384,13 +385,7 @@ do_test attach2-6.3 {
catchsql {
DETACH aux;
}
} {1 {cannot DETACH database within transaction}}
do_test attach2-6.4 {
execsql {
COMMIT;
DETACH aux;
}
} {}
} {0 {}}
db close
+104
View File
@@ -0,0 +1,104 @@
# 2017 August 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. The
# focus of this script testing the ability of SQLite to use mmap
# to access files larger than 4GiB.
#
if {[file exists skip-big-file]} return
if {$tcl_platform(os)=="Darwin"} return
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix bigmmap
ifcapable !mmap {
finish_test
return
}
set mmap_limit 0
db eval {
SELECT compile_options AS x FROM pragma_compile_options
WHERE x LIKE 'max_mmap_size=%'
} {
regexp {MAX_MMAP_SIZE=([0-9]*)} $x -> mmap_limit
}
if {$mmap_limit < [expr 8 * 1<<30]} {
puts "Skipping bigmmap.test - requires SQLITE_MAX_MMAP_SIZE >= 8G"
finish_test
return
}
#-------------------------------------------------------------------------
# Create the database file roughly 8GiB in size. Most pages are unused,
# except that there is a table and index clustered around each 1GiB
# boundary.
#
do_execsql_test 1.0 {
PRAGMA page_size = 4096;
CREATE TABLE t0(a INTEGER PRIMARY KEY, b, c, UNIQUE(b, c));
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 100 )
INSERT INTO t0 SELECT i, 't0', randomblob(800) FROM s;
}
for {set i 1} {$i < 8} {incr i} {
fake_big_file [expr $i*1024] [get_pwd]/test.db
hexio_write test.db 28 [format %.8x [expr ($i*1024*1024*1024/4096) - 5]]
do_execsql_test 1.$i "
CREATE TABLE t$i (a INTEGER PRIMARY KEY, b, c, UNIQUE(b, c));
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 100 )
INSERT INTO t$i SELECT i, 't$i', randomblob(800) FROM s;
"
}
#-------------------------------------------------------------------------
# Check that data can be retrieved from the db with a variety of
# configured mmap size limits.
#
for {set i 0} {$i < 9} {incr i} {
# Configure a memory mapping $i GB in size.
#
set val [expr $i*1024*1024*1024]
execsql "PRAGMA main.mmap_size = $val"
do_execsql_test 2.$i.0 {
PRAGMA main.mmap_size
} $val
for {set t 0} {$t < 8} {incr t} {
do_execsql_test 2.$i.$t.1 "
SELECT count(*) FROM t$t;
SELECT count(b || c) FROM t$t GROUP BY b;
" {100 100}
do_execsql_test 2.$i.$t.2 "
SELECT * FROM t$t AS o WHERE
NOT EXISTS( SELECT * FROM t$t AS i WHERE a=o.a AND +b=o.b AND +c=o.c )
ORDER BY b, c;
" {}
do_eqp_test 2.$i.$t.3 "
SELECT * FROM t$t AS o WHERE
NOT EXISTS( SELECT * FROM t$t AS i WHERE a=o.a AND +b=o.b AND +c=o.c )
ORDER BY b, c;
" "
0 0 0 {SCAN TABLE t$t AS o USING COVERING INDEX sqlite_autoindex_t${t}_1}
0 0 0 {EXECUTE CORRELATED SCALAR SUBQUERY 1}
1 0 0 {SEARCH TABLE t$t AS i USING INTEGER PRIMARY KEY (rowid=?)}
"
}
}
finish_test
+35 -3
View File
@@ -93,6 +93,7 @@ do_catchsql_test 3.2 {
SELECT rowid, a FROM t3;
} {1 {no such column: rowid}}
# Multi-column WITHOUT ROWID virtual tables may not be writable.
do_catchsql_test 4.0 {
DROP TABLE t3;
CREATE VIRTUAL TABLE temp.t4 USING csv_wr(
@@ -100,13 +101,44 @@ do_catchsql_test 4.0 {
'1,2,3,4
5,6,7,8
9,10,11,12
13,14,15,16
',
13,14,15,16',
columns=4,
schema=
'CREATE TABLE t3(a PRIMARY KEY,b TEXT,c TEXT,d TEXT) WITHOUT ROWID',
'CREATE TABLE t3(a,b,c,d,PRIMARY KEY(a,b)) WITHOUT ROWID',
testflags=1
);
} {1 {vtable constructor failed: t4}}
# WITHOUT ROWID tables with a single-column PRIMARY KEY may be writable.
do_catchsql_test 4.1 {
DROP TABLE IF EXISTS t4;
CREATE VIRTUAL TABLE temp.t4 USING csv_wr(
data=
'1,2,3,4
5,6,7,8
9,10,11,12
13,14,15,16',
columns=4,
schema=
'CREATE TABLE t3(a,b,c,d,PRIMARY KEY(b)) WITHOUT ROWID',
testflags=1
);
} {0 {}}
do_catchsql_test 4.2 {
DROP TABLE IF EXISTS t5;
CREATE VIRTUAL TABLE temp.t5 USING csv_wr(
data=
'1,2,3,4
5,6,7,8
9,10,11,12
13,14,15,16',
columns=4,
schema=
'CREATE TABLE t3(a,b,c,d) WITHOUT ROWID',
testflags=1
);
} {1 {vtable constructor failed: t5}}
finish_test
+3 -1
View File
@@ -59,7 +59,9 @@ do_test fallocate-1.6 {
#
do_test fallocate-1.7 {
execsql { BEGIN; INSERT INTO t1 VALUES(1, 2); }
if {[permutation] != "inmemory_journal"} {
if {[permutation] != "inmemory_journal"
&& [permutation] != "atomic-batch-write"
} {
hexio_get_int [hexio_read test.db-journal 16 4]
} else {
set {} 1024
+49 -1
View File
@@ -207,7 +207,7 @@ do_test like-3.3.100 {
SELECT x FROM t1 WHERE x LIKE 'abc%' ORDER BY 1;
}
} {abc abcd nosort {} i1}
do_test like-3.3.101 {
do_test like-3.3.100.cnt {
set sqlite_like_count
} 0
@@ -1048,4 +1048,52 @@ ifcapable !icu {
} {1}
}
# As of 2017-07-27 (3.21.0) the LIKE optimization works with ESCAPE as
# long as the ESCAPE is a single-byte literal.
#
db close
sqlite3 db :memory:
do_execsql_test like-15.100 {
CREATE TABLE t15(x TEXT COLLATE nocase, y, PRIMARY KEY(x));
INSERT INTO t15(x,y) VALUES
('abcde',1), ('ab%de',2), ('a_cde',3),
('uvwxy',11),('uvwx%',12),('uvwx_',13),
('_bcde',21),('%bcde',22),
('abcd_',31),('abcd%',32),
('ab%xy',41);
SELECT y FROM t15 WHERE x LIKE 'ab/%d%' ESCAPE '/';
} {2}
do_execsql_test like-15.101 {
EXPLAIN QUERY PLAN
SELECT y FROM t15 WHERE x LIKE 'ab/%d%' ESCAPE '/';
} {/SEARCH/}
do_execsql_test like-15.102 {
EXPLAIN QUERY PLAN
SELECT y FROM t15 WHERE x LIKE 'ab/%d%' ESCAPE '//';
} {/SCAN/}
do_execsql_test like-15.103 {
EXPLAIN QUERY PLAN
SELECT y FROM t15 WHERE x LIKE 'ab/%d%' ESCAPE '';
} {/SCAN/}
do_execsql_test like-15.110 {
SELECT y FROM t15 WHERE x LIKE 'abcdx%%' ESCAPE 'x';
} {32}
do_execsql_test like-15.111 {
SELECT y FROM t15 WHERE x LIKE 'abx%%' ESCAPE 'x' ORDER BY +y
} {2 41}
do_execsql_test like-15.112 {
EXPLAIN QUERY PLAN
SELECT y FROM t15 WHERE x LIKE 'abx%%' ESCAPE 'x' ORDER BY +y
} {/SEARCH/}
do_execsql_test like-15.120 {
SELECT y FROM t15 WHERE x LIKE '/%bc%' ESCAPE '/';
} {22}
do_execsql_test like-15.121 {
EXPLAIN QUERY PLAN
SELECT y FROM t15 WHERE x LIKE '/%bc%' ESCAPE '/';
} {/SEARCH/}
finish_test
+22 -20
View File
@@ -479,26 +479,28 @@ ifcapable curdir {
# Make sure a database connection still works after changing the
# working directory.
#
do_test misc1-14.1 {
file mkdir tempdir
cd tempdir
execsql {BEGIN}
file exists ./test.db-journal
} {0}
do_test misc1-14.2a {
execsql {UPDATE t1 SET a=a||'x' WHERE 0}
file exists ../test.db-journal
} {0}
do_test misc1-14.2b {
execsql {UPDATE t1 SET a=a||'y' WHERE 1}
file exists ../test.db-journal
} {1}
do_test misc1-14.3 {
cd ..
forcedelete tempdir
execsql {COMMIT}
file exists ./test.db-journal
} {0}
if {[atomic_batch_write test.db]==0} {
do_test misc1-14.1 {
file mkdir tempdir
cd tempdir
execsql {BEGIN}
file exists ./test.db-journal
} {0}
do_test misc1-14.2a {
execsql {UPDATE t1 SET a=a||'x' WHERE 0}
file exists ../test.db-journal
} {0}
do_test misc1-14.2b {
execsql {UPDATE t1 SET a=a||'y' WHERE 1}
file exists ../test.db-journal
} {1}
do_test misc1-14.3 {
cd ..
forcedelete tempdir
execsql {COMMIT}
file exists ./test.db-journal
} {0}
}
}
# A failed create table should not leave the table in the internal
+25
View File
@@ -70,6 +70,28 @@ static int progress_handler(void *pClientData) {
}
#endif
/*
** Disallow debugging pragmas such as "PRAGMA vdbe_debug" and
** "PRAGMA parser_trace" since they can dramatically increase the
** amount of output without actually testing anything useful.
*/
static int block_debug_pragmas(
void *Notused,
int eCode,
const char *zArg1,
const char *zArg2,
const char *zArg3,
const char *zArg4
){
if( eCode==SQLITE_PRAGMA
&& (sqlite3_strnicmp("vdbe_", zArg1, 5)==0
|| sqlite3_stricmp("parser_trace", zArg1)==0)
){
return SQLITE_DENY;
}
return SQLITE_OK;
}
/*
** Callback for sqlite3_exec().
*/
@@ -128,6 +150,9 @@ int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
sqlite3_db_config(cx.db, SQLITE_DBCONFIG_ENABLE_FKEY, uSelector&1, &rc);
uSelector >>= 1;
/* Do not allow debugging pragma statements that might cause excess output */
sqlite3_set_authorizer(cx.db, block_debug_pragmas, 0);
/* Remaining bits of the selector determine a limit on the number of
** output rows */
execCnt = uSelector + 1;
+24
View File
@@ -389,6 +389,30 @@ test_suite "vfslog" -prefix "" -description {
wal* mmap*
]
test_suite "atomic-batch-write" -prefix "" -description {
Like veryquick.test, but must be run on a file-system that supports
atomic-batch-writes. Tests that depend on the journal file being present
are omitted.
} -files [
test_set $allquicktests -exclude *malloc* *ioerr* *fault* *bigfile* *_err* \
*fts5corrupt* *fts5big* *fts5aj* \
crash8.test delete_db.test \
exclusive.test journal3.test \
journal1.test \
jrnlmode.test jrnlmode2.test \
lock4.test pager1.test \
pager3.test sharedA.test \
symlink.test stmt.test \
sync.test sync2.test \
tempdb.test tkt3457.test \
vacuum5.test wal2.test \
walmode.test zerodamage.test
] -initialize {
if {[atomic_batch_write test.db]==0} {
error "File system does NOT support atomic-batch-write"
}
}
lappend ::testsuitelist xxx
#-------------------------------------------------------------------------
# Define the coverage related test suites:
+1
View File
@@ -82,6 +82,7 @@ do_test rollback-1.9 {
if {$tcl_platform(platform) == "unix"
&& [permutation] ne "onefile"
&& [permutation] ne "inmemory_journal"
&& [permutation] ne "atomic-batch-write"
} {
do_test rollback-2.1 {
execsql {
+7 -2
View File
@@ -616,12 +616,16 @@ ifcapable auth {
# First make sure it is not possible to attach or detach a database while
# a savepoint is open (it is not possible if any transaction is open).
#
# UPDATE 2017-07-26: It is not possible to ATTACH and DETACH within a
# a transaction.
#
do_test savepoint-10.1.1 {
catchsql {
SAVEPOINT one;
ATTACH 'test2.db' AS aux;
DETACH aux;
}
} {1 {cannot ATTACH database within transaction}}
} {0 {}}
do_test savepoint-10.1.2 {
execsql {
RELEASE one;
@@ -630,8 +634,9 @@ do_test savepoint-10.1.2 {
catchsql {
SAVEPOINT one;
DETACH aux;
ATTACH 'test2.db' AS aux;
}
} {1 {cannot DETACH database within transaction}}
} {0 {}}
do_test savepoint-10.1.3 {
execsql {
RELEASE one;
+163
View File
@@ -0,0 +1,163 @@
# 2017-07-30
#
# 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 tests to show that certain CREATE TABLE statements
# generate identical database files. For example, changes in identifier
# names, white-space, and formatting of the CREATE TABLE statement should
# produce identical table content.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix schema6
# Command: check_same_database_content TESTNAME SQL1 SQL2 SQL3 ...
#
# This command creates fresh databases using SQL1 and subsequent arguments
# and checks to make sure the content of all database files is byte-for-byte
# identical. Page 1 of the database files is allowed to be different, since
# page 1 contains the sqlite_master table which is expected to vary.
#
proc check_same_database_content {basename args} {
set i 0
set hash {}
foreach sql $args {
catch {db close}
forcedelete test.db
sqlite3 db test.db
db eval $sql
set pgsz [db one {PRAGMA page_size}]
db close
set sz [file size test.db]
set thishash [md5file test.db $pgsz [expr {$sz-$pgsz}]]
if {$i==0} {
set hash $thishash
} else {
do_test $basename-$i "set x $thishash" $hash
}
incr i
}
}
# Command: check_different_database_content TESTNAME SQL1 SQL2 SQL3 ...
#
# This command creates fresh databases using SQL1 and subsequent arguments
# and checks to make sure the content of all database files is different
# in ways other than on page 1.
#
proc check_different_database_content {basename args} {
set i 0
set hashes {}
foreach sql $args {
forcedelete test.db
sqlite3 db test.db
db eval $sql
set pgsz [db one {PRAGMA page_size}]
db close
set sz [file size test.db]
set thishash [md5file test.db $pgsz [expr {$sz-$pgsz}]]
set j [lsearch $hashes $thishash]
if {$j>=0} {
do_test $basename-$i "set x {$i is the same as $j}" "All are different"
} else {
do_test $basename-$i "set x {All are different}" "All are different"
}
lappend hashes $thishash
incr i
}
}
check_same_database_content 100 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE);
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
} {
CREATE TABLE t1(xyz INTEGER, abc, PRIMARY KEY(xyz), UNIQUE(abc));
INSERT INTO t1(xyz,abc) VALUES(123,'Four score and seven years ago...');
} {
CREATE TABLE t1(xyz INTEGER, abc, UNIQUE(abc), PRIMARY KEY(xyz));
INSERT INTO t1(xyz,abc) VALUES(123,'Four score and seven years ago...');
} {
CREATE TABLE t1(a INTEGER PRIMARY KEY ASC, b UNIQUE);
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
} {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
CREATE UNIQUE INDEX t1b ON t1(b);
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
} {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
CREATE UNIQUE INDEX t1b ON t1(b);
}
check_same_database_content 110 {
CREATE TABLE t1(a INTEGER PRIMARY KEY UNIQUE, b UNIQUE);
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
} {
CREATE TABLE t1(a INTEGER UNIQUE PRIMARY KEY, b UNIQUE);
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
} {
CREATE TABLE t1(a INTEGER UNIQUE PRIMARY KEY, b UNIQUE, UNIQUE(a));
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
} {
CREATE TABLE t1(a INTEGER UNIQUE PRIMARY KEY, b);
CREATE UNIQUE INDEX t1b ON t1(b);
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
} {
CREATE TABLE t1(a INTEGER UNIQUE PRIMARY KEY, b);
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
CREATE UNIQUE INDEX t1b ON t1(b);
}
check_same_database_content 120 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE) WITHOUT ROWID;
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
} {
CREATE TABLE t1(xyz INTEGER, abc, PRIMARY KEY(xyz), UNIQUE(abc))WITHOUT ROWID;
INSERT INTO t1(xyz,abc) VALUES(123,'Four score and seven years ago...');
} {
CREATE TABLE t1(xyz INTEGER, abc, UNIQUE(abc), PRIMARY KEY(xyz))WITHOUT ROWID;
INSERT INTO t1(xyz,abc) VALUES(123,'Four score and seven years ago...');
} {
CREATE TABLE t1(a INTEGER PRIMARY KEY ASC, b UNIQUE) WITHOUT ROWID;
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
} {
CREATE TABLE t1(a INTEGER PRIMARY KEY UNIQUE, b UNIQUE) WITHOUT ROWID;
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
} {
CREATE TABLE t1(a INTEGER UNIQUE PRIMARY KEY, b UNIQUE) WITHOUT ROWID;
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
} {
CREATE TABLE t1(a INTEGER UNIQUE PRIMARY KEY, b UNIQUE, UNIQUE(a))
WITHOUT ROWID;
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
} {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b) WITHOUT ROWID;
CREATE UNIQUE INDEX t1b ON t1(b);
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
} {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b) WITHOUT ROWID;
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
CREATE UNIQUE INDEX t1b ON t1(b);
}
check_different_database_content 130 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE);
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
} {
CREATE TABLE t1(a INTEGER PRIMARY KEY UNIQUE, b UNIQUE);
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
} {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE) WITHOUT ROWID;
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
}
finish_test
+246
View File
@@ -0,0 +1,246 @@
# 2017-07-15
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this file is the "swarmvtab" extension
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix swarmvtab
ifcapable !vtab {
finish_test
return
}
load_static_extension db unionvtab
set nFile $sqlite_open_file_count
do_execsql_test 1.0 {
CREATE TABLE t0(a INTEGER PRIMARY KEY, b TEXT);
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<400)
INSERT INTO t0 SELECT i, hex(randomblob(50)) FROM s;
CREATE TABLE dir(f, t, imin, imax);
}
do_test 1.1 {
for {set i 0} {$i < 40} {incr i} {
set iMin [expr $i*10 + 1]
set iMax [expr $iMin+9]
forcedelete "test.db$i"
execsql [subst {
ATTACH 'test.db$i' AS aux;
CREATE TABLE aux.t$i (a INTEGER PRIMARY KEY, b TEXT);
INSERT INTO aux.t$i SELECT * FROM t0 WHERE a BETWEEN $iMin AND $iMax;
DETACH aux;
INSERT INTO dir VALUES('test.db$i', 't$i', $iMin, $iMax);
}]
}
execsql {
CREATE VIRTUAL TABLE temp.s1 USING swarmvtab('SELECT * FROM dir');
}
} {}
do_execsql_test 1.2 {
DROP TABLE s1;
} {}
do_execsql_test 1.3 {
CREATE VIRTUAL TABLE temp.s1 USING swarmvtab('SELECT * FROM dir');
SELECT count(*) FROM s1 WHERE rowid<50;
} {49}
proc do_compare_test {tn where} {
set sql [subst {
SELECT (SELECT group_concat(a || ',' || b, ',') FROM t0 WHERE $where)
IS
(SELECT group_concat(a || ',' || b, ',') FROM s1 WHERE $where)
}]
uplevel [list do_execsql_test $tn $sql 1]
}
do_compare_test 1.4.1 "rowid = 700"
do_compare_test 1.4.2 "rowid = -1"
do_compare_test 1.4.3 "rowid = 0"
do_compare_test 1.4.4 "rowid = 55"
do_compare_test 1.4.5 "rowid BETWEEN 20 AND 100"
do_compare_test 1.4.6 "rowid > 350"
do_compare_test 1.4.7 "rowid >= 350"
do_compare_test 1.4.8 "rowid >= 200"
do_compare_test 1.4.9 "1"
# Multiple simultaneous cursors.
#
do_execsql_test 1.5.1.(5-seconds-or-so) {
SELECT count(*) FROM s1 a, s1 b WHERE b.rowid<=200;
} {80000}
do_execsql_test 1.5.2 {
SELECT count(*) FROM s1 a, s1 b, s1 c
WHERE a.rowid=b.rowid AND b.rowid=c.rowid;
} {400}
# Empty source tables.
#
do_test 1.6.0 {
for {set i 0} {$i < 20} {incr i} {
sqlite3 db2 test.db$i
db2 eval " DELETE FROM t$i "
db2 close
}
db eval { DELETE FROM t0 WHERE rowid<=200 }
} {}
do_compare_test 1.6.1 "rowid = 700"
do_compare_test 1.6.2 "rowid = -1"
do_compare_test 1.6.3 "rowid = 0"
do_compare_test 1.6.4 "rowid = 55"
do_compare_test 1.6.5 "rowid BETWEEN 20 AND 100"
do_compare_test 1.6.6 "rowid > 350"
do_compare_test 1.6.7 "rowid >= 350"
do_compare_test 1.6.8 "rowid >= 200"
do_compare_test 1.6.9 "1"
do_compare_test 1.6.10 "rowid >= 5"
do_test 1.x {
set sqlite_open_file_count
} [expr $nFile+9]
do_test 1.y { db close } {}
# Delete all the database files created above.
#
for {set i 0} {$i < 40} {incr i} { forcedelete "test.db$i" }
#-------------------------------------------------------------------------
# Test some error conditions:
#
# 2.1: Database file does not exist.
# 2.2: Table does not exist.
# 2.3: Table schema does not match.
# 2.4: Syntax error in SELECT statement.
#
reset_db
load_static_extension db unionvtab
do_test 2.0.1 {
db eval {
CREATE TABLE t0(a INTEGER PRIMARY KEY, b TEXT);
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<400)
INSERT INTO t0 SELECT i, hex(randomblob(50)) FROM s;
CREATE TABLE dir(f, t, imin, imax);
}
for {set i 0} {$i < 40} {incr i} {
set iMin [expr $i*10 + 1]
set iMax [expr $iMin+9]
forcedelete "test.db$i"
db eval [subst {
ATTACH 'test.db$i' AS aux;
CREATE TABLE aux.t$i (a INTEGER PRIMARY KEY, b TEXT);
INSERT INTO aux.t$i SELECT * FROM t0 WHERE a BETWEEN $iMin AND $iMax;
DETACH aux;
INSERT INTO dir VALUES('test.db$i', 't$i', $iMin, $iMax);
}]
}
execsql {
CREATE VIRTUAL TABLE temp.s1 USING swarmvtab('SELECT * FROM dir');
}
} {}
do_test 2.0.2 {
forcedelete test.db5
sqlite3 db2 test.db15
db2 eval { DROP TABLE t15 }
db2 close
sqlite3 db2 test.db25
db2 eval {
DROP TABLE t25;
CREATE TABLE t25(x, y, z PRIMARY KEY);
}
db2 close
} {}
do_catchsql_test 2.1 {
SELECT * FROM s1 WHERE rowid BETWEEN 1 AND 100;
} {1 {unable to open database file}}
do_catchsql_test 2.2 {
SELECT * FROM s1 WHERE rowid BETWEEN 101 AND 200;
} {1 {no such rowid table: t15}}
do_catchsql_test 2.3 {
SELECT * FROM s1 WHERE rowid BETWEEN 201 AND 300;
} {1 {source table schema mismatch}}
do_catchsql_test 2.4 {
CREATE VIRTUAL TABLE temp.x1 USING swarmvtab('SELECT * FROMdir');
} {1 {sql error: near "FROMdir": syntax error}}
do_catchsql_test 2.5 {
CREATE VIRTUAL TABLE temp.x1 USING swarmvtab('SELECT * FROMdir', 'fetchdb');
} {1 {sql error: near "FROMdir": syntax error}}
for {set i 0} {$i < 40} {incr i} {
forcedelete "test.db$i"
}
#-------------------------------------------------------------------------
# Test the outcome of the fetch function throwing an exception.
#
proc fetch_db {file} {
error "fetch_db error!"
}
db func fetch_db fetch_db
do_catchsql_test 3.1 {
CREATE VIRTUAL TABLE temp.xyz USING swarmvtab(
'VALUES
("test.db1", "t1", 1, 10),
("test.db2", "t1", 11, 20)
', 'fetch_db_no_such_function'
);
} {1 {no such function: fetch_db_no_such_function}}
do_catchsql_test 3.2 {
CREATE VIRTUAL TABLE temp.xyz USING swarmvtab(
'VALUES
("test.db1", "t1", 1, 10),
("test.db2", "t1", 11, 20)
', 'fetch_db'
);
} {1 {fetch_db error!}}
do_execsql_test 3.3.1 {
ATTACH 'test.db1' AS aux;
CREATE TABLE aux.t1(a INTEGER PRIMARY KEY, b);
INSERT INTO aux.t1 VALUES(1, NULL);
INSERT INTO aux.t1 VALUES(2, NULL);
INSERT INTO aux.t1 VALUES(9, NULL);
DETACH aux;
CREATE VIRTUAL TABLE temp.xyz USING swarmvtab(
'VALUES
("test.db1", "t1", 1, 10),
("test.db2", "t1", 11, 20)
', 'fetch_db'
);
} {}
do_catchsql_test 3.3.2 { SELECT * FROM xyz } {1 {fetch_db error!}}
finish_test
+74
View File
@@ -0,0 +1,74 @@
# 2017-07-15
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this file is the "swarmvtab" extension
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix swarmvtab
ifcapable !vtab {
finish_test
return
}
db close
foreach name [glob -nocomplain test*.db] {
forcedelete $name
}
sqlite3 db test.db
load_static_extension db unionvtab
proc create_database {filename} {
sqlite3 dbx $filename
set num [regsub -all {[^0-9]+} $filename {}]
set num [string trimleft $num 0]
set start [expr {$num*1000}]
set end [expr {$start+999}]
dbx eval {
CREATE TABLE t2(a INTEGER PRIMARY KEY,b);
WITH RECURSIVE c(x) AS (
VALUES($start) UNION ALL SELECT x+1 FROM c WHERE x<$end
)
INSERT INTO t2(a,b) SELECT x, printf('**%05d**',x) FROM c;
}
dbx close
}
db func create_database create_database
do_execsql_test 100 {
CREATE TABLE t1(filename, tablename, istart, iend);
WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<99)
INSERT INTO t1 SELECT printf('test%03d.db',x),'t2',x*1000,x*1000+999 FROM c;
CREATE VIRTUAL TABLE temp.v1 USING swarmvtab(
'SELECT * FROM t1', 'create_database'
);
} {}
do_execsql_test 110 {
SELECT b FROM v1 WHERE a=3875;
} {**03875**}
do_test 120 {
lsort [glob -nocomplain test?*.db]
} {test001.db test003.db}
do_execsql_test 130 {
SELECT b FROM v1 WHERE a BETWEEN 3999 AND 4000 ORDER BY a;
} {**03999** **04000**}
do_test 140 {
lsort [glob -nocomplain test?*.db]
} {test001.db test003.db test004.db}
do_execsql_test 150 {
SELECT b FROM v1 WHERE a>=99998;
} {**99998** **99999**}
do_test 160 {
lsort -dictionary [glob -nocomplain test?*.db]
} {test001.db test003.db test004.db test099.db}
finish_test
+61
View File
@@ -0,0 +1,61 @@
# 2017-07-15
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this file is error handling in the swarmvtab extension.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix swarmvtabfault
ifcapable !vtab {
finish_test
return
}
proc fetch_db {file} {
forcedelete $file
sqlite3 dbX $file
dbX eval { CREATE TABLE t1(a INTEGER PRIMARY KEY, b) }
dbX close
}
forcedelete test.db1
do_execsql_test 1.0 {
ATTACH 'test.db1' AS aux;
CREATE TABLE aux.t1(a INTEGER PRIMARY KEY, b);
INSERT INTO aux.t1 VALUES(1, NULL);
INSERT INTO aux.t1 VALUES(2, NULL);
INSERT INTO aux.t1 VALUES(9, NULL);
DETACH aux;
} {}
faultsim_save_and_close
do_faultsim_test 1.1 -faults oom* -prep {
faultsim_restore_and_reopen
db func fetch_db fetch_db
load_static_extension db unionvtab
db eval {
CREATE VIRTUAL TABLE temp.xyz USING swarmvtab(
'VALUES
("test.db1", "t1", 1, 10),
("test.db2", "t1", 11, 20)
', 'fetch_db'
);
}
} -body {
execsql { SELECT a FROM xyz }
} -test {
faultsim_test_result {0 {1 2 9}} {1 {sql error: out of memory}}
}
finish_test
+1 -1
View File
@@ -61,7 +61,7 @@ foreach s {
fcntl read pread write pwrite fchmod fallocate
pread64 pwrite64 unlink openDirectory mkdir rmdir
statvfs fchown geteuid umask mmap munmap mremap
getpagesize readlink lstat
getpagesize readlink lstat ioctl
} {
if {[test_syscall exists $s]} {lappend syscall_list $s}
}
+48
View File
@@ -1608,6 +1608,54 @@ proc crashsql {args} {
lappend r $msg
}
# crash_on_write ?-devchar DEVCHAR? CRASHDELAY SQL
#
proc crash_on_write {args} {
set nArg [llength $args]
if {$nArg<2 || $nArg%2} {
error "bad args: $args"
}
set zSql [lindex $args end]
set nDelay [lindex $args end-1]
set devchar {}
for {set ii 0} {$ii < $nArg-2} {incr ii 2} {
set opt [lindex $args $ii]
switch -- [lindex $args $ii] {
-devchar {
set devchar [lindex $args [expr $ii+1]]
}
default { error "unrecognized option: $opt" }
}
}
set f [open crash.tcl w]
puts $f "sqlite3_crash_on_write $nDelay"
puts $f "sqlite3_test_control_pending_byte $::sqlite_pending_byte"
puts $f "sqlite3 db test.db -vfs writecrash"
puts $f "db eval {$zSql}"
puts $f "set {} {}"
close $f
set r [catch {
exec [info nameofexec] crash.tcl >@stdout
} msg]
# Windows/ActiveState TCL returns a slightly different
# error message. We map that to the expected message
# so that we don't have to change all of the test
# cases.
if {$::tcl_platform(platform)=="windows"} {
if {$msg=="child killed: unknown signal"} {
set msg "child process exited abnormally"
}
}
lappend r $msg
}
proc run_ioerr_prep {} {
set ::sqlite_io_error_pending 0
catch {db close}
+12
View File
@@ -67,6 +67,18 @@ do_faultsim_test 1.2 -faults oom* -prep {
faultsim_test_result {0 {1 one 2 two 3 three 10 ten 11 eleven 12 twelve 20 twenty 21 twenty-one 22 twenty-two}}
}
#-------------------------------------------------------------------------
# Error while registering the two vtab modules.
do_faultsim_test 2.0 -faults * -prep {
catch { db close }
sqlite3 db :memory:
} -body {
load_static_extension db unionvtab
} -test {
faultsim_test_result {0 {}} {1 {initialization of unionvtab failed: }}
}
finish_test
+2 -2
View File
@@ -60,7 +60,7 @@ do_test vtab2-2.1 {
set ::abc 123
execsql {
CREATE VIRTUAL TABLE vars USING tclvar;
SELECT * FROM vars WHERE name='abc';
SELECT name, arrayname, value FROM vars WHERE name='abc';
}
} [list abc "" 123]
do_test vtab2-2.2 {
@@ -68,7 +68,7 @@ do_test vtab2-2.2 {
set A(2) 4
set A(3) 9
execsql {
SELECT * FROM vars WHERE name='A';
SELECT name, arrayname, value FROM vars WHERE name='A';
}
} [list A 1 1 A 2 4 A 3 9]
unset -nocomplain result
+3 -1
View File
@@ -39,7 +39,9 @@ do_test vtabE-1 {
CREATE VIRTUAL TABLE t1 USING tclvar;
CREATE VIRTUAL TABLE t2 USING tclvar;
CREATE TABLE t3(a INTEGER PRIMARY KEY, b);
SELECT t1.*, t2.*, abs(t3.b + abs(t2.value + abs(t1.value)))
SELECT t1.name, t1.arrayname, t1.value,
t2.name, t2.arrayname, t2.value,
abs(t3.b + abs(t2.value + abs(t1.value)))
FROM t1 LEFT JOIN t2 ON t2.name = t1.arrayname
LEFT JOIN t3 ON t3.a=t2.value
WHERE t1.name = 'vtabE'
+1 -1
View File
@@ -55,7 +55,7 @@ register_tclvar_module db
set ::xyz 10
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE vars USING tclvar;
SELECT * FROM vars WHERE name = 'xyz';
SELECT name, arrayname, value FROM vars WHERE name = 'xyz';
} {xyz {} 10}
set x1 aback
+126
View File
@@ -0,0 +1,126 @@
# 2017-08-10
#
# 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 tests of writing to WITHOUT ROWID virtual tables
# using the tclvar eponymous virtual table.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix vtabJ
ifcapable !vtab {
finish_test
return
}
register_tclvar_module db
unset -nocomplain vtabJ
do_test 100 {
set vtabJ(1) this
set vtabJ(two) is
set vtabJ(3) {a test}
db eval {
SELECT fullname, value FROM tclvar WHERE name='vtabJ' ORDER BY fullname;
}
} {vtabJ(1) this vtabJ(3) {a test} vtabJ(two) is}
do_execsql_test 110 {
INSERT INTO tclvar(fullname, value)
VALUES('vtabJ(4)',4),('vtabJ(five)',555);
SELECT fullname, value FROM tclvar WHERE name='vtabJ' ORDER BY fullname;
} {vtabJ(1) this vtabJ(3) {a test} vtabJ(4) 4 vtabJ(five) 555 vtabJ(two) is}
do_test 111 {
set res {}
foreach vname [lsort [array names vtabJ]] {
lappend res vtabJ($vname) $vtabJ($vname)
}
set res
} {vtabJ(1) this vtabJ(3) {a test} vtabJ(4) 4 vtabJ(five) 555 vtabJ(two) is}
do_test 120 {
db eval {
INSERT INTO tclvar(fullname, value) VALUES('vtabJ(4)',444);
}
set vtabJ(4)
} {444}
do_test 130 {
db eval {
INSERT INTO tclvar(fullname, value) VALUES('vtabJ(4)',NULL);
}
info exists vtabJ(4)
} {0}
do_test 140 {
db eval {
UPDATE tclvar SET value=55 WHERE fullname='vtabJ(five)';
}
set vtabJ(five)
} {55}
do_test 150 {
db eval {
UPDATE tclvar SET fullname='vtabJ(5)' WHERE fullname='vtabJ(five)';
}
set vtabJ(5)
} {55}
do_test 151 {
info exists vtabJ(five)
} {0}
do_test 152 {
set res {}
foreach vname [lsort [array names vtabJ]] {
lappend res vtabJ($vname) $vtabJ($vname)
}
set res
} {vtabJ(1) this vtabJ(3) {a test} vtabJ(5) 55 vtabJ(two) is}
do_execsql_test 160 {
SELECT fullname FROM tclvar WHERE arrayname='two'
} {vtabJ(two)}
do_execsql_test 161 {
DELETE FROM tclvar WHERE arrayname='two';
SELECT fullname, value FROM tclvar WHERE name='vtabJ' ORDER BY fullname;
} {vtabJ(1) this vtabJ(3) {a test} vtabJ(5) 55}
do_test 162 {
set res {}
foreach vname [lsort [array names vtabJ]] {
lappend res vtabJ($vname) $vtabJ($vname)
}
set res
} {vtabJ(1) this vtabJ(3) {a test} vtabJ(5) 55}
# Try to trick the module into updating the same variable twice for a
# single UPDATE statement.
#
do_execsql_test 171 {
INSERT INTO tclvar(fullname, value) VALUES('xx', 'a');
SELECT name, value FROM tclvar where name = 'xx';
} {xx a}
do_execsql_test 172 {
UPDATE tclvar SET value = value || 't'
WHERE name = 'xx' OR name = 'x'||'x';
SELECT name, value FROM tclvar where name = 'xx';
} {xx at}
do_execsql_test 173 {
UPDATE tclvar SET value = value || 't'
WHERE name = 'xx' OR name BETWEEN 'xx' AND 'xx';
SELECT name, value FROM tclvar where name = 'xx';
} {xx att}
do_execsql_test 181 {
DELETE FROM tclvar WHERE name BETWEEN 'xx' AND 'xx' OR name='xx';
SELECT name, value FROM tclvar where name = 'xx';
} {}
finish_test
+12
View File
@@ -158,5 +158,17 @@ do_test whereA-4.6 {
}
} {2 1 1}
# Ticket https://sqlite.org/src/tktview/cb91bf4290c211 2017-08-01
# Assertion fault following PRAGMA reverse_unordered_selects=ON.
#
do_execsql_test whereA-5.1 {
PRAGMA reverse_unordered_selects=on;
DROP TABLE IF EXISTS t1;
CREATE TABLE t1(a,b);
INSERT INTO t1 VALUES(1,2);
CREATE INDEX t1b ON t1(b);
SELECT a FROM t1 WHERE b=-99 OR b>1;
} {1}
finish_test
+68
View File
@@ -0,0 +1,68 @@
# 2009 January 8
#
# 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.
#
#***********************************************************************
#
# Test the outcome of a writer crashing within a call to the VFS
# xWrite function.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix writecrash
do_not_use_codec
if {$tcl_platform(platform)=="windows"} {
finish_test
return
}
do_execsql_test 1.0 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b BLOB UNIQUE);
WITH s(i) AS (
VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<100
)
INSERT INTO t1 SELECT NULL, randomblob(900) FROM s;
} {}
set bGo 1
for {set tn 1} {$bGo} {incr tn} {
db close
sqlite3 db test.db
do_test 1.$tn.1 {
set res [crash_on_write $tn {
UPDATE t1 SET b = randomblob(899) WHERE (a%3)==0
}]
set bGo 0
if {[string match {1 {child killed:*}} $res]} {
set res {0 {}}
set bGo 1
}
set res
} {0 {}}
#db close
#sqlite3 db test.db
do_execsql_test 1.$tn.2 { PRAGMA integrity_check } {ok}
db close
sqlite3 db test.db
do_execsql_test 1.$tn.3 { PRAGMA integrity_check } {ok}
}
finish_test
+2 -7
View File
@@ -22,14 +22,7 @@ close $in
# ILLEGAL *must* be the last two token codes and they must be in that order.
#
set extras {
TO_TEXT
TO_BLOB
TO_NUMERIC
TO_INT
TO_REAL
ISNOT
END_OF_FILE
UNCLOSED_STRING
FUNCTION
COLUMN
AGG_FUNCTION
@@ -42,6 +35,8 @@ set extras {
IF_NULL_ROW
ASTERISK
SPAN
END_OF_FILE
UNCLOSED_STRING
SPACE
ILLEGAL
}
+24 -1
View File
@@ -2155,7 +2155,8 @@ enum e_state {
WAITING_FOR_FALLBACK_ID,
WAITING_FOR_WILDCARD_ID,
WAITING_FOR_CLASS_ID,
WAITING_FOR_CLASS_TOKEN
WAITING_FOR_CLASS_TOKEN,
WAITING_FOR_TOKEN_NAME
};
struct pstate {
char *filename; /* Name of the input file */
@@ -2470,6 +2471,8 @@ to follow the previous rule.");
}else if( strcmp(x,"fallback")==0 ){
psp->fallback = 0;
psp->state = WAITING_FOR_FALLBACK_ID;
}else if( strcmp(x,"token")==0 ){
psp->state = WAITING_FOR_TOKEN_NAME;
}else if( strcmp(x,"wildcard")==0 ){
psp->state = WAITING_FOR_WILDCARD_ID;
}else if( strcmp(x,"token_class")==0 ){
@@ -2624,6 +2627,26 @@ to follow the previous rule.");
}
}
break;
case WAITING_FOR_TOKEN_NAME:
/* Tokens do not have to be declared before use. But they can be
** in order to control their assigned integer number. The number for
** each token is assigned when it is first seen. So by including
**
** %token ONE TWO THREE
**
** early in the grammar file, that assigns small consecutive values
** to each of the tokens ONE TWO and THREE.
*/
if( x[0]=='.' ){
psp->state = WAITING_FOR_DECL_OR_RULE;
}else if( !ISUPPER(x[0]) ){
ErrorMsg(psp->filename, psp->tokenlineno,
"%%token argument \"%s\" should be a token", x);
psp->errorcnt++;
}else{
(void)Symbol_new(x);
}
break;
case WAITING_FOR_WILDCARD_ID:
if( x[0]=='.' ){
psp->state = WAITING_FOR_DECL_OR_RULE;
+7 -9
View File
@@ -202,19 +202,17 @@ for {set i 0} {$i<=$max} {incr i} {
set name $def($i)
puts -nonewline [format {#define %-16s %3d} $name $i]
set com {}
if {$jump($name)} {
lappend com "jump"
}
if {[info exists sameas($i)]} {
set com "same as $sameas($i)"
lappend com "same as $sameas($i)"
}
if {[info exists synopsis($name)]} {
set x $synopsis($name)
if {$com==""} {
set com "synopsis: $x"
} else {
append com ", synopsis: $x"
}
lappend com "synopsis: $synopsis($name)"
}
if {$com!=""} {
puts -nonewline [format " /* %-42s */" $com]
if {[llength $com]} {
puts -nonewline [format " /* %-42s */" [join $com {, }]]
}
puts ""
}
+2
View File
@@ -224,6 +224,8 @@ proc copy_file {filename} {
if {![info exists varonly_hdr($tail)]
&& [regexp $declpattern $line all rettype funcname rest]} {
regsub {^SQLITE_API } $line {} line
regsub {^SQLITE_API } $rettype {} rettype
# Add the SQLITE_PRIVATE or SQLITE_API keyword before functions.
# so that linkage can be modified at compile-time.
if {[regexp {^sqlite3[a-z]*_} $funcname]} {