Compare commits

..

156 Commits

Author SHA1 Message Date
drh 449b34571e Enable the EXISTS-to-JOIN optimization if the outer query has no
FROM clause.

FossilOrigin-Name: 1b9b124f9a35ebd1ac4ea70ef1ee08a4c82c11da690d4164f6b785a6fd9730d9
2025-07-08 17:28:09 +00:00
drh 8c8443a38a Small performance optimization in the resolver.
FossilOrigin-Name: 840646df0696706bd4dd7a04dfc8b16ad80c24cbcbc57a22c9e65a93cc17af2b
2025-07-07 19:03:50 +00:00
drh 44a03671c4 Merge the latest trunk enhancements and fixes into the empty-table-optimizations branch.
FossilOrigin-Name: f15cdf07573c05276a13885d74bae21a93544766344f19ef939b7a69edd1073b
2025-07-07 18:35:51 +00:00
drh 0a5508aeb4 Rework the fix to the problem described by
[forum:/forumpost/b9647a113b465950|forum post b9647a113b] so that it
provides a more complete fix that covers cases that were not resolved by
the original fix, and so that it does not cause performance regressions.

FossilOrigin-Name: 28db0d152d90fb5e62d03ea5caceabe8901be98522aef3dc2b54564fbc35355d
2025-07-07 16:19:44 +00:00
drh 4fe1ac8fe1 Simplifications to the row-value IN operator logic. Do not let the query
planner accept a WhereLoop for a row-value IN operator that uses the same
index column more than once.

FossilOrigin-Name: d2adf61f21a3ce901a2b08199ad0de191e88ef16e097c5f7a75c95ad958eff12
2025-07-07 15:40:53 +00:00
stephan 24d290e7b0 Work around the Emscripten 4.10 regression described in [https://github.com/emscripten-core/emscripten/issues/24656 | Emscripten ticket #24656]. Problem reported off-list by BrickViking.
FossilOrigin-Name: c385475b250f3364507a95c5832137098a9bb7c7fc12ab3bb116e1fad7bb7645
2025-07-07 12:11:26 +00:00
dan 8504d37b99 Avoid invoking the preupdate hook from within sqlite3_blob_write() if the cursor is already invalid.
FossilOrigin-Name: 9f335b9a4e9e761a0c6afd6dc69665a24506141bde88530bf59fcbdf957ae881
2025-07-07 11:37:55 +00:00
drh 4aacd1ef8e Back out the fix at [ba7d5bad32ad6aac] because it does not always work and because
it causes a performance regression.  Add new test cases for row-value lookups of
indexes that contain redundant columns, three of which are currently failing.  This
branch is seeking an improved solution to the redundant index column problem for
row-value lookups.

FossilOrigin-Name: ad8ddcefab5cc526b1cd77731e00939c672e61ca83350d28961b67635d20da03
2025-07-07 10:54:00 +00:00
drh 0840460689 Fix parser error introduced by [325e547a2195571e]. See
[forum:/forumpost/095dbfc06e5b1f7e|forum post 095dbfc06e5].

FossilOrigin-Name: 4eefab44941fc6e17742fa49c8734e7f00a2177d82bc572e596228add53aad39
2025-07-07 02:18:27 +00:00
drh c701d17366 Improvements to the EXPLAIN QUERY PLAN output for EXISTS-to-JOIN.
FossilOrigin-Name: 6b1ecbaa2ee405be040901dceac45d027d35c313622748ba4dbbd404e297a7fa
2025-07-06 01:19:09 +00:00
drh 8bc112e3e4 Enhance the EXISTS-to-JOIN optimization so that it works on EXISTS subqueries
that do not have a WHERE clause, and so that it works on nested EXISTS
subqueries.

FossilOrigin-Name: c1d5295724f9cf7f49e0786d28016eff2d268a2b670f934d24c76787626089db
2025-07-05 23:33:21 +00:00
stephan 27408ab9c9 Add 'reconfigure' target to Makefile.in to re-run the configure script with the same flags it was generated with.
FossilOrigin-Name: c60907e77b32824aaaf024d299cdaf161b5f64fc927ffe5d5455eeb5754e6b01
2025-07-04 18:32:18 +00:00
stephan d9eae6b75a Propagate the -ldl and -lpthread flags, if needed, into sqlite3.pc, per request in [forum:44a58c807353162f | forum post 44a58c807353162f].
FossilOrigin-Name: 64f5f14322349b47451b8cac03bf8cd6f1ae45a8822e7f1ddee3d0b265047501
2025-07-04 17:20:37 +00:00
drh 45bc9b9f97 Merge fixes from trunk into the empty-table-optimizations branch
FossilOrigin-Name: 6f98b16d210a9f5b6ca4b4599e3dab3263eddbae7c70ddbcabf988f4a1014e8b
2025-07-04 14:24:18 +00:00
drh e33ea17d24 Fix harmless compiler warnings.
FossilOrigin-Name: 13af4acebe09b047756c22b800136cffaba532e7fcaa448a4edf4fedb94e9bbc
2025-07-04 12:25:24 +00:00
drh a12e92d2f7 Remove an ALWAYS() added by [960a8e6fc91f4] that turns out to be false
in some cases of malformed SQL.

FossilOrigin-Name: cc8171461bf35f584888a24b844c8b79ad30155b19b61161e9f3abc276e8aae5
2025-07-04 11:48:11 +00:00
drh 478dfc7e74 Do not allow the EXISTS-to-JOIN optimization if the EXISTS clause is based
on a view, since that view might expand into a join.

FossilOrigin-Name: 872c41feddafcc21a02f1229ce017ceea9f8e309a4dd5b6e323477d67f975947
2025-07-04 11:06:34 +00:00
drh 1b62720fa8 Improve the early-termination optimization so that it works in queries
which use the LIKE optimization in the outer loop.

FossilOrigin-Name: b4e4d148243cfcb09aa0aaca30e83812b42e2780073e55c0e8c6e3da16243dfc
2025-07-04 10:26:55 +00:00
drh 1e3a862b79 Merge the latest trunk fixes and enhancements into the empty-table-optimizations branch
FossilOrigin-Name: d4f47e04f5880e99a53089e2dd5cde64a7ea44f059d9906b5d11324896546714
2025-07-03 20:51:08 +00:00
dan bd05edd98f Fix a few cases where LIMIT clauses that were part of scalar sub-queries on virtual tables were not being passed to xBestIndex methods correctly.
FossilOrigin-Name: 960a8e6fc91f47add3a089dc6cef013109deadf809994c5149ad3bdfb3884de0
2025-07-03 16:05:41 +00:00
drh a24a397b8b Improvements to sqlite3_vtab_rhs_value() logging in the
ext/misc/vtablog.c extension.

FossilOrigin-Name: 25131ee84f53dab1191e02c19cefd256aed2828c7edea325fcc0ba3b0a668583
2025-07-03 15:50:18 +00:00
dan 0e6e05d4d5 Make the value of an explicit LIMIT clause on a scalar sub-query available to xBestIndex for simple "LIMIT 0" and "LIMIT 1" queries.
FossilOrigin-Name: 33b6a63caafccc61b3193714911cd8b5dd9b7f1798054b8c7845b23688d531f2
2025-07-03 15:32:27 +00:00
dan c525e6e817 Make handling of LIMIT clauses in correlated sub-queries on virtual tables more efficient.
FossilOrigin-Name: 7214cb2a5b35190a06a1040cd4c54f325d347f8d8e36a07fd76c0a421e266522
2025-07-03 14:28:47 +00:00
drh 1ff6f19d8b Enhancements to the xBestIndex output from the ext/misc/vtablog.c
extension.

FossilOrigin-Name: 8b31acc0b18f38eb4af6efebd1ea25cd65a146651101579aee20afd9ec6dc2de
2025-07-03 14:10:03 +00:00
drh bfb4993364 Fix an uninitialized variable added yesterday by [d27d34fb746280e7].
This problem was discovered overnight by 
[https://github.com/google/oss-fuzz|OSSFuzz].

FossilOrigin-Name: 6db4703f1178fc808f3a75c355fb6638fb12c88f6e1ce7f579e200ced8089114
2025-07-03 11:52:17 +00:00
drh debc8f7bb7 Minor tweaks to the exists-to-join optimization.
FossilOrigin-Name: 9cb600ad576c68647ed943a0773019312c5f01c9c1ca9ff0bf1214b03a531b48
2025-07-03 00:17:27 +00:00
drh aa54d7a0ca Merge in the exists-to-join optimization that has been modified
to relax the requirement of having an indexed join constraint.

FossilOrigin-Name: 1c1aef2b7feae29066d0330699ab634ef41f5b60cdcd479a60cb1a5409553138
2025-07-02 20:46:02 +00:00
drh eb27359e5e Fix VDBE coverage
FossilOrigin-Name: ff593a16d61cc5c588d1737deb822abb90b1759475a4cabfcf608978b1191487
2025-07-02 17:43:59 +00:00
drh 33f3273705 Early exit if one of the inner loops of a 3-way or larger join is an
empty table.

FossilOrigin-Name: eaad6ac707a5960d9518d4049b7b1759e7512727ce87be3c402408144bda0a97
2025-07-02 14:53:48 +00:00
drh 216676664d If the LHS for an EXCEPT or INTERSECT operator is empty, skip over the
computation of the RHS.

FossilOrigin-Name: 13f096ae8a850a05d4a8684561066f11693ee66289e6568c44ef32822cca06f6
2025-07-02 13:19:24 +00:00
drh 6245e5a46b Improve the bytecode for joins such that it exits earlier if it determines
that no output is possible.

FossilOrigin-Name: 2d2b61cba44a756a3a41ef5c95bbb0c0b7111f4b679c578fec9bd0b214cca367
2025-07-02 11:47:54 +00:00
drh caf0473652 Ensure that Expr.op2 values for TK_AGG_FUNCTION nodes are adjusted when
query flattening.

FossilOrigin-Name: d27d34fb746280e7e81335db4e195914b15403ef0da7b2955550553dd78fbe9a
2025-07-02 02:03:43 +00:00
drh 2427ce16d9 Improved comments on bytecode used to implement aggregate queries, to aid
in debugging.

FossilOrigin-Name: a1a8b85cdba64a17dcdcd4e6b42b872957cec2dc05b0ac34dfcd82f59344034b
2025-07-01 23:17:36 +00:00
drh 46bfcc1a21 Improved byte-code comments for the short-circuit optimization of
[0083d5169a46104a], to aid in debugging.

FossilOrigin-Name: 113f9d10e347eeaa893ff8b47a461009e0b12589374b93cf3e0bddd19c702dea
2025-07-01 20:32:45 +00:00
drh 5e71497404 Cache and reuse virtual table cursors in the bytecode engine.
FossilOrigin-Name: 2d187d4232d750cb1840f1d89c8aed65962cb0883c1d7f91c554b451e475c514
2025-07-01 17:36:55 +00:00
dan 526399b0fd Avoid an assert() failure in fts5 that may occur when processing corrupt records.
FossilOrigin-Name: 8afd6ca85724a69970181042d6aac53742ea2b76ded5966b2541c3afe1121fb9
2025-07-01 16:21:47 +00:00
drh e1910ed1d2 Merge trunk fixes into the empty-table-optimizations branch.
FossilOrigin-Name: 63306e447efb3ac17e789a331ed3bb65459eb8b79d66e9c185ba3bd852f34ce3
2025-07-01 15:13:37 +00:00
drh d82c6a2cf7 When attempting to optimize "expr AND false" to "false" and
"expr IN ()" to "false", take care not to delete aggregate functions
in the "expr" as doing so can change the meaning of the query.
See [forum:/forumpost/f4878de3e7dd4764|forum thread f4878de3e7].

FossilOrigin-Name: 77397bd67d918db57d5ac545d6d963194806fdabcdaa8f822b6b09e4cfe8b715
2025-07-01 12:43:13 +00:00
drh 3d21dcc924 More aggressive optimization of addrHalt for RIGHT JOIN.
FossilOrigin-Name: 5e51d1c0dfcafef9e71c99de3f626dee157c935724b84c6e0c630299a880446f
2025-06-30 21:07:08 +00:00
drh ba56f7020d Compute WhereLevel.addrBrk and .addrHalt early so that those labels can be
used to abort loops early.  Use this to improve performance on two more
of the cases described by [forum:/forumpost/52651713ac|forum post 52651713ac].

FossilOrigin-Name: 6fc0b9ac23be6840542982de4bb282ebca1db8b5ab3baefdde95a997c1506e81
2025-06-30 20:19:19 +00:00
drh 99f1aa03fb Strive to skip the evaluation of scalar subqueries that are part of a
larger expression if the result from the scalar subquery does not change the
result of the overall expression.

FossilOrigin-Name: 0083d5169a46104a25355bdd9d5a2f4027b049191ebda571dd228477ec217296
2025-06-30 16:41:40 +00:00
drh 280559b446 For all binary operators, try to avoid computing subquery operands if the
other operand is NULL.

FossilOrigin-Name: d86eb16283c4b573c506d4faa422d5d9aeb6abc279d8e6a8e2104737162d417f
2025-06-30 12:14:47 +00:00
drh e2e81e6983 Slightly smaller and faster version of the previous check-in.
FossilOrigin-Name: f6e6fd02f4ad49c390a2d3c9626d57f9b2fff1f67eb361b30074cc1f5121810e
2025-06-30 11:04:55 +00:00
stephan b8c81790b2 Minor API doc typo fixes from brickviking.
FossilOrigin-Name: b48d95191662e09659b5b55ae65cd462c9e1700c4f92dd9d40b59548f0797c02
2025-06-30 11:00:59 +00:00
drh e24f20a4f5 Factor out the code that tries to avoid evaluating subquery operands if the
other operand is NULL into a subroutine, so that it can be more easily reused
by other parts of the code generator.

FossilOrigin-Name: 3c6c71bcea16443b82234414137dfa1b59e2ee8fe5097906c738fc1228fec4e6
2025-06-30 10:30:47 +00:00
stephan f0991c416b Minor API doc typo fixes from brickviking.
FossilOrigin-Name: c7cf9dcb69ce558ba6b81b2787f92ed7383e37f08b199faeb14f7adb4e494532
2025-06-29 07:32:20 +00:00
drh 311d73efc2 Improve the bytecode generated for comparisons so that if one operand is
a subquery and the other operand evaluates to NULL, the subquery operand
is not even computed.  This fixes 5 of the 12 slow queries described
in [forum:/forumpost/52651713ac|forum post 52651713ac].

FossilOrigin-Name: f147bc04776ac0056412f69dfc518016c0d5b4e9d964664e3d88f595fb29dbe0
2025-06-28 17:59:15 +00:00
drh c52e9d97d4 Raise an error right away if the number of aggregate terms in a query
exceeds the maximum number of columns.

FossilOrigin-Name: 5508b56fd24016c13981ec280ecdd833007c9d8dd595edb295b984c2b487b5c8
2025-06-27 19:02:21 +00:00
stephan fbf1c0526e API doc typo fixes and closing DD element tags from brickviking.
FossilOrigin-Name: c9ddd15b0197e6e5c5a74581d94cf986523514ffdb28c66ba18de9a22aec97e9
2025-06-27 12:46:50 +00:00
drh e77e589a35 Work around an apparent GCC UBSAN bug. See
[forum:/forumpost/1d7c25d4a2d6f5e2|forum thread 1d7c25d4a2d] for details.

FossilOrigin-Name: 23056532e51abcff486e38a3319545e6663b4a855abe1bc443e2cd7e0473748c
2025-06-26 18:57:20 +00:00
stephan 981022b7ff Minor API doc fixes sent off-list from brickviking.
FossilOrigin-Name: cb4d05633a0c9cdf146f3108e1b4b10754cd79d72a425d8cc9cd21836037a01b
2025-06-25 20:42:40 +00:00
drh a67c71224f Similar fix to the previous check-in, but this time for
sqlite3_preupdate_new().

FossilOrigin-Name: 9aaff764d508cbd9823f23cdf0c510255721bc5c09be481ebc60bd8902ceb8c5
2025-06-24 18:27:59 +00:00
drh 66cd200ede Range check the column index on the sqlite3_preupdate_old() interface
and return SQLITE_MISUSE if too large.
[forum:/forumpost/b617e497287235d0|Forum post b617e49728].

FossilOrigin-Name: 6a5701e6c7be25cba93e55438f950966e1dacb32eb2b23a8acc8ac53da6f0a85
2025-06-24 15:58:32 +00:00
stephan 513fff88c4 API doc typo fixes and one rephrasing improvement from brickviking.
FossilOrigin-Name: 85e8060c4701a601d4bc8a639acd268ee4f2736193820d55ecd9b8abf864132f
2025-06-24 07:38:52 +00:00
dan fe182f74b2 Improve some of the error messages emitted by fts5 when it encounters corruption.
FossilOrigin-Name: 48044a6b57c0a16cb75139c103ad88ca4ab64d74f70a3dee0d8b817fbfbec3c6
2025-06-23 19:38:22 +00:00
drh b1560be826 Fix an SQL typo introduced by the previous check-in.
FossilOrigin-Name: 72a81d247bc74a21d077d311969ac585d0f26bffbd210ec60f03c38bf138790e
2025-06-23 19:00:30 +00:00
drh 28789029ee Escape the "_" character in LIKE patterns in the CLI.
[forum:/forumpost/6a89702f5d|Forum post 6a89702f5d].

FossilOrigin-Name: cf61cd359e666c66b6bba4407a653c799f7f07e1f5ee6b837ad467029c461a6a
2025-06-23 16:51:33 +00:00
drh f453e8d84e Fix a harmless compiler warning injected by [c978aed3b6f82b3d].
FossilOrigin-Name: f46500247b3a2f8b4a2f0f032cc4066af9cb3918e38a09c59db0a1da5747a25c
2025-06-23 13:42:59 +00:00
drh 4c1c400d64 Improved "statement aborts at ..." log-file messages that identify the
trigger that caused the abort.

FossilOrigin-Name: 7691264fe37300c880d612c91cf093327841fbe85fb7ec67e777e911d2cfc9ee
2025-06-23 13:28:13 +00:00
stephan 87c807c6dd Add the --disable-rpath configure script flag to address [forum:13cac3b56516f849 | forum post 13cac3b56516f849].
FossilOrigin-Name: a59d9bb25e518f5d79f654615b92f6c50cfb704b5abee0f820912644b89366c5
2025-06-22 22:48:11 +00:00
stephan b5aa9593ac API doc typo fixes from brickviking.
FossilOrigin-Name: 18e07f8f53693d0dace3c4025a572c1a10582290a338aa7f0913c83842a69b05
2025-06-22 12:04:47 +00:00
stephan 5806a922ba Minor JS test cleanups prompted by a linter.
FossilOrigin-Name: 764235a3d15fda55af85ebe3b5885fa27336cc0e979f1be2f6770b66a84bbe87
2025-06-21 16:33:05 +00:00
stephan 427d2cd57d Remove some duplicated JS tests.
FossilOrigin-Name: d1cb9ed79d806f667c22f5ba9861451d4d0765aa3072e085831045cf3a39f8b4
2025-06-21 16:18:23 +00:00
stephan db37e6097c Reduce the maximum --size flag for JS kvvfs speedtest1 from 4 to 2, as --size 3 and 4 are overflowing the kvvfs storage limits.
FossilOrigin-Name: b77b3adc828ce543e880e230701edd5e0ccb681e4c3c724ea8ad5ecbd69c73a6
2025-06-21 16:02:20 +00:00
stephan 6fcb7f22a1 Give oo1.Stmt.get() similar treatment to [8c187140a60b]. This is an internal change only - the API is unaffected.
FossilOrigin-Name: f5a7abc0a447273de40dacc463d267d26d4b62be56ee15baf05825791c2a7a6e
2025-06-21 15:58:20 +00:00
stephan 2f7f948fa7 Extend the SEE-via-kvvfs tests to include all three key types.
FossilOrigin-Name: fc001aa5ee978795c2ff670bea64db0b69b6bde98653769d9cede8825a28e698
2025-06-21 15:38:59 +00:00
stephan 0cdde5b44f Rework how JS's oo1.DB.exec() flags its Stmt objects to make certain Stmt APIs illegal (i.e. throwing) if called while that Stmt is being step()ped by DB.exec() (which can happen via client-provided per-result-row callbacks). This is an internal change only - the API is unaffected. Remove some unrelated dead code.
FossilOrigin-Name: 8c187140a60b62dc3b6066b8615766d52b7a29a5de992cbb6d312dbb225a980b
2025-06-21 14:38:53 +00:00
stephan a0c6de56ba Numerous small doc typo from BrickViking.
FossilOrigin-Name: 3aab4415de615c178205936372c625bb571bad1016c3d2e7bb2a45932356f3d6
2025-06-21 06:07:13 +00:00
drh b68d63158a Enhance sqlite3BtreeSetPageSize() so that it detects early if no changes
are needed and exists with SQLITE_OK.

FossilOrigin-Name: c978aed3b6f82b3d9d2e0ca0283c611351bca5e5496e86a9d5d0731ebcd27c84
2025-06-20 13:12:55 +00:00
drh 59de42763f Fix an out-of-order local variable declaration in ext/misc/fileio.c.
FossilOrigin-Name: a88bb75288a06492a04ab1278e8a2101a74f4ba712d328b4c73e86ac01cb946d
2025-06-19 20:19:12 +00:00
drh 37794b4ad0 Improve the accuracy of sqlite3BtreeRowCountEst().
FossilOrigin-Name: e553fb538ec1527457e2927edf9127dccf268a8a3b5918d4fb0d066ad9676094
2025-06-19 20:00:37 +00:00
drh 397b82cf0f Generalize the indexCellCompare() so that works on any index page, not just
the current page that a cursor is pointing to.

FossilOrigin-Name: b305a7f5db183d8e0e5d62ca3c9c6260ad94bb954f7342bd3caabcd8308a21f5
2025-06-19 19:33:06 +00:00
stephan 92d1bec584 A slight doc rephrasing for clarity, suggested in the forum.
FossilOrigin-Name: 0d6084a398edb013fcfb613eff8d75e2f86cbb5e3c8049328ede89e75db93cb3
2025-06-19 12:35:30 +00:00
stephan a5c484b432 Doc typo fixes from [forum:0bce273669 | forum post 0bce273669].
FossilOrigin-Name: 8f97e3f00d24b26fe87a9d3396ad855277a1a369f6f89e9c77dc9af4d203bdf1
2025-06-19 10:46:39 +00:00
stephan 64159096b8 Expose the column metadata APIs to WASM (which does not require a non-default sqlite3.c build, contrary to my prior mistaken claims). This adds only 424 bytes to sqlite3.wasm and 660 bytes to sqlite3.js.
FossilOrigin-Name: 3cf983816ccd167d93cb39c7a16b6db4b4f7ef2a64f4181d3bc4530cd0bd3b67
2025-06-19 10:25:50 +00:00
drh ab9c91ae82 Extend the pedantic enforcement of type to VIRTUAL columns.
FossilOrigin-Name: b734c74e55acb26eb61b60937bef870f4b55b2e2e7560a22362f5f31ba2fcd03
2025-06-18 19:04:28 +00:00
drh bcf25e7129 Enforce judgmental typing on STORED generated columns for STRICT
tables.  [forum:/forumpost/6caf195248a849e4|Forum post 6caf195248].

FossilOrigin-Name: 5e9279bff0482806f86657ae05ca3e916708d138bc3c3ceb3fbf454818649d44
2025-06-18 16:17:00 +00:00
stephan 2be0a700ff Export the column-metadata APIs to WASM. Doing so requires a non-default build of sqlite3.c, so this is a proof-of-concept branch saved just in case SQLITE_ENABLE_COLUMN_METADATA ever becomes the default for sqlite3.c.
FossilOrigin-Name: 59db3f639d1073678805dea26a4686eddfb238f055aa90bd429fc7fd33241502
2025-06-18 15:37:04 +00:00
stephan 3a26f72e20 Add the --enable-column-metadata flag to the configure script (off by default). It's only available in the canonical build, not the autoconf build, because it changes how sqlite3.c gets generated.
FossilOrigin-Name: 986b601db11a041d280eb61004723604bf4b6fd573b9832c97e3a9da9ea16e9a
2025-06-18 15:22:50 +00:00
stephan d961d49971 Eliminate configure/build discrepancies in how 'make tclextension-...' works vs other makefile-side handling of the extension, conforming to the former.
FossilOrigin-Name: 7ca545fd3b460fcf92e117baef42655eb6e78ebb5bdc6302f8f1b5b8446c0a06
2025-06-18 14:17:18 +00:00
drh 9a9140ba59 Change the definition of SQLITE_DYNAMIC to a function that has exactly
the same type as sqlite3_destructor_type, in an effort to work around
possible legacy compiler bugs.

FossilOrigin-Name: 83553edf78c35d171a0ddf1a51306e963d715e56d95b8159b561b729cfb27843
2025-06-18 14:14:46 +00:00
stephan 12bd8ea09a Add the --tclConfig.sh FILE flag to buildtclext.tcl to eliminate the discrepancy between the configure-time-detected tclConfig.sh and the one auto-detected by the tclextension family of makefile targets. Add the tclextension-all target to run all of the various tclextension-... targets in their natural order to facilitate testing changes like this one. Update the --help text to note that --with-tclsh should only be used if there's a specific need, and that --with-tcl is generall preferred. This is in response to [https://github.com/termux/termux-packages/issues/23268 | Termux ticket 23268].
FossilOrigin-Name: 928976abcb11f9d5abe1f7631002592c4af7bf83ecadba5b0763866d039c2270
2025-06-18 11:11:30 +00:00
stephan 1e9baaf743 tea: remove the vsatisfies 8.6- enforcement from the extension because at least one platform with tcl 8.6 is inexplicably failing it. [forum:fde857fb8101a4be | Forum post fde857fb8101a4be]
FossilOrigin-Name: 7331e32a76a8a41cc1bd1c84069219c9929e0e0f941c1d9e308cbbb7959373c5
2025-06-17 20:50:51 +00:00
drh df8aa3745a Avoid writing frames with no checksums into the wal file if a
savepoint is rolled back after dirty pages have already been
spilled into the wal file.

FossilOrigin-Name: 5973f9b9aa828ec9274b02a124b95f452c58235eaafffbdda1c32b4ae2d5616d
2025-06-17 19:10:33 +00:00
drh 110055cd6c Fix a corner-case for [9441fff52cc4e19c].
FossilOrigin-Name: 336a59eb3afd80ce048de472368df6dfc32934ee783859d37663ed8f5cf169a5
2025-06-17 18:57:07 +00:00
dan 79a746b1f2 Update walcksum.test so that it works with SQLITE_DEFAULT_AUTOVACUUM=1 builds.
FossilOrigin-Name: 0da16f6c625bff9f2ca5624afa6dce6b5a157f44c1d56b25b92946c13217ab02
2025-06-17 18:17:09 +00:00
stephan 9ebf9d358b Extend [b5c6cb13cff5] to use a wildcard on darwin* because some OSes report a version number in the suffix. [forum:0c4bbc2962dfb06e | Forum post 0c4bbc2962dfb06e].
FossilOrigin-Name: d960bd6f9256410ebc98125b0567380457e39bc8fe768fcd9ea05c6de1994df6
2025-06-17 17:15:12 +00:00
stephan e68aa0e44b Minor proj.tcl portability fixes and cleanups suggested in [forum:7b218c3c9f|forum post 7b218c3c9f]. Tested on Linux, Msys, Cygwin, and Haiku.
FossilOrigin-Name: b5c6cb13cff53f4ba1e0e6a0d61389252bd004d9099727e7a9304217e3f81d80
2025-06-17 14:56:30 +00:00
stephan 6e1c2f0483 proj.tcl portability fixes and cleanups suggested in [forum:7b218c3c9f|forum post 7b218c3c9f]. Branching so that it can be tested on msys/cygwin/haiku before committing to it.
FossilOrigin-Name: 89552e9137b7ebba9600443720e936e8dc4f7ab021d2ac3f0bb01b74071e130c
2025-06-17 14:37:31 +00:00
dan 1ea6a53762 Avoid writing frames with no checksums into the wal file if a savepoint is rolled back after dirty pages have already been spilled into the wal file. Possible fix for [forum:/forumpost/b490f726db | forum post b490f726db].
FossilOrigin-Name: 3a3269fe8e925a522d5df9f011220a842fb15a9f56e785884e32d2e15abda5bb
2025-06-17 11:36:39 +00:00
drh 86d9c2d9f3 Fix an issue going back to version 3.39.0 with transitive IS constraints
in queries that make use of RIGHT JOIN.  Problem reported by
[forum:/forumpost/68f29a2005|forum post 68f29a2005].

FossilOrigin-Name: 9441fff52cc4e19c44df1a77ffe474f409d519b270c7166ce17f99e6ea48fc1e
2025-06-16 17:36:11 +00:00
drh d3a13f7d25 Improved debugging output for the transitive constraint optimization.
FossilOrigin-Name: 94b53c20e9bc8687c44272419aa7a93076eebdeae9a4f50b95b96a49993f9c0d
2025-06-16 16:07:14 +00:00
drh 6ed5aa4b9b Make the show-%p-az-zero hack of the previous check-in configurable at
run-time using the 0x100000 bit of either .treetrace or .wheretrace.
As before, this is all a no-op except for debugging builds.

FossilOrigin-Name: a29627d7e7f8344d9a099cc133bda85250b02dc5ee5f358ba59691e0816b5b2d
2025-06-16 15:34:26 +00:00
drh 67f708298f Add an "#if 0" that can be changed to "1" to cause all %p output to render
as 0, thus making comparisons of .treetrace and .wheretrace output easier.

FossilOrigin-Name: 65e2704c0bc329f3dd248548a2d4edb7d3ace8ceaf0b82b8618b9b1b764884cd
2025-06-16 13:51:09 +00:00
dan 792d1d1b6d Have sqlite3_setlk_timeout() take the database handle mutex. This fixes an assert() failure that could occur if sqlite3_setlk_timeout() were called on a threadsafe handle.
FossilOrigin-Name: a95d126e1330e1b83f42b51f97c4c216622cf38062f3b5d72ccb76313187e850
2025-06-12 07:35:38 +00:00
dan 6a5e74cbf0 Fix a problem with UPDATEs on fts5 tables that contain blob values.
FossilOrigin-Name: badf3014bd1620fd3d4b8013f641fd820b249649fb93cc75b7b8df9dfd6f32a6
2025-06-11 15:03:53 +00:00
drh 23e59b34e9 Fix the concat_ws() SQL function so that it includes empty strings in the
concatenation.  [forum:/forumpost/52503ac21d|Forum post 52503ac21d].

FossilOrigin-Name: 80a78987da484d435a8242c05c48d546d430920df713b24a9d9d9fff7ba1113d
2025-06-11 00:01:42 +00:00
drh a09a4fbac9 Improved selection of the divisor when subdividing nested Bitvec objects.
This fixes a potential stack overflow that can occur when the database size
is within 60 pages of the maximum allowed by the file format.

FossilOrigin-Name: f7ab764ed9df6d7a4a96cb0933d291f00174f33fed3d9951785078fe225adcb7
2025-06-10 19:52:21 +00:00
drh 6a23ff5a07 Minor corrections to the new Bitvec testing logic.
FossilOrigin-Name: 77b79ca1277419b91589aff2c601d4abdd7107d48019a58f7f7c85d96c2a538e
2025-06-10 18:26:09 +00:00
drh 5706b316c3 Enhancements to sqlite3BitvecBuiltinTest() that allow testing code to
create very large Bitvec objects that do not use the linear array cross-check.

FossilOrigin-Name: c5680672cae23f65637eebf66f3bb983a2864be03ea70378832034f3c89ef728
2025-06-10 17:22:53 +00:00
drh 90ba0d4995 Improved diagnostics for Bitvec: Add the sqlite3ShowBitvec() routine that
can be called from a debugger (only available with SQLITE_DEBUG).  Add new
output opcodes for sqlite3BitvecBuiltinTest().

FossilOrigin-Name: dea1e37fa67ada6efc1533b449d9eb22338d9e58eec8f89b48c38319c212c8f4
2025-06-10 16:02:29 +00:00
drh f091f423ad Adjustments to ext/misc/fileio.c in an attempt to get it to build using mingw.
FossilOrigin-Name: 96b14a3f1193de8f30e9fa704f87558dab8027a218868d32e47688cd5df497b7
2025-06-09 22:38:34 +00:00
drh 4c323ba4be Remove unnecessary whitespace and otherwise improve comments in the
wherecode.c module.  No coding changes.

FossilOrigin-Name: 2eb4e9bf0f2df50324a62fb272a92bbd931b8b4e4b35bac0c05b676c97b61339
2025-06-06 23:10:18 +00:00
drh 368c549044 Update the "msort" function in Lemon so that it works with lists of any
length, and also so that the sort is stable.  This patch was motivated by
[forum:/forumpost/63750d717c9ed961|forum post 63750d717c] but was
independently developed, then tested by temporarily setting LISTSIZE to 2.

FossilOrigin-Name: aba5c3135edf7de2798ad808fa0ff176fdba3f4a9b101f1f4210b441cba8e75d
2025-06-06 23:02:03 +00:00
drh b1929b7117 Remove the clunky test_windirent.h and test_windirent.c files from src/
and replace them with a much cleaner and more compact ext/misc/windirent.h.

FossilOrigin-Name: acc978df52ec41ffdb5c27764f30d53efa1f25a314b7d98983dc0d211a36b570
2025-06-05 20:12:41 +00:00
drh 6facd9566d Enhance the FSDIR virtual table with a new "level" column. The query planner
knows how to optimize to avoid search deeper than the maximum requested level.

FossilOrigin-Name: 1ddc0f9e79c33957961bc1443ccb74d756a02cbd20850052079782e76aef2706
2025-06-05 18:28:54 +00:00
stephan 26370358b6 tea build: add an info-exists check after a 'scan' call, as scan does not create its target vars on error. Problem reported at [forum:fde857fb8101a4be | forum post fde857fb8101a4be] and triggers when the 'vsatisfies' test for the host's Tcl version fails (so the build would fail anyway, but will fail more informatively with this fix).
FossilOrigin-Name: 4f21874d5d20aef2e2d67a59e4fa03d98aa6514b16e4d956acfc817142cfbdb6
2025-06-04 18:34:20 +00:00
stephan a29b138692 Apply the duplicate 'export default' workaround to the (untested and unsupported) node-specific build rules in mkwasmbuilds.c to resolve a problem reported off-list by Thomas Steiner.
FossilOrigin-Name: 2f8a1b79533879e4975b405c46fea496ba8bffbef065e7dd0ad29fd4aa8f8f92
2025-06-03 18:10:59 +00:00
drh 1d151e6d25 Enhance sqlite3_rsync so that it works even if the replica database is
initially malformed.

FossilOrigin-Name: ea1754f7d8a770477a1b19b606b27724fdc0b733e51fef32c1ef834f972c3cc5
2025-06-03 10:49:51 +00:00
drh df724c893a Fix an off-by-one error in sqlite3_rsync, reported in
[forum:/forumpost/b6d78f60fc|forum post b6d78f60fc].

FossilOrigin-Name: 27d9e8e79b921c4a86916556f3bf56b94684a7e3d40166c0fdaaf750d4011de0
2025-06-03 10:28:47 +00:00
drh 140748f949 Fix harmless compiler warning introduced by the setlk-snapshot-fix merge.
FossilOrigin-Name: c1f20f89d9eb3e1dd1018c0e0efe5d3043a592f078e235ad04b960334c7186c2
2025-06-03 10:18:54 +00:00
drh 2bd9f69d40 Fix JSONB edit so that when it is trying to reduce the size of an element
it understands 0xf0 (8-byte) sizes.

FossilOrigin-Name: 5b3de6e8ab6b228bf6c0e0c31b50ca29d8b7852b460eaaca6c6ecf5a3c083cab
2025-06-02 23:34:42 +00:00
drh 333b07b18d Increase the version number to 3.51.0
FossilOrigin-Name: ed69d4432712603bea2161e0ca0c99e59480fb000e1cbf1aa1401d924544e351
2025-06-02 18:58:42 +00:00
dan 9269b212bf Fixes to ensure SQLITE_ENABLE_SETLK_TIMEOUT builds use a blocking lock and do not call xSleep() when (a) opening a snapshot transaction, and (b) when blocked by another process running recovery.
FossilOrigin-Name: 7f9c0cdd0630a41db359b188b226a1ad6a3bae1663c27169acfe25edc7fb171b
2025-06-02 18:48:36 +00:00
dan 69ce758efa Fix os_win.c so that SQLITE_ENABLE_SETLK_TIMEOUT=2 builds work on windows.
FossilOrigin-Name: 8efb95e0d4670b9c5dbd8cf34512334f47951a8dff8fdadc8645f75076acd91f
2025-06-02 18:37:32 +00:00
drh 342ef63e63 Improve the accuracy of affinity and collating sequence analysis for
NATURAL JOINs to the left of RIGHT JOINs where source tables are views
or subqueries.  Initial problem report in
[forum:/forumpost/829306db47|forum post 829306db47].

FossilOrigin-Name: f184d1d236e47962658a4639d9533f67a525b74cfe0f06c93e9b85fdcd02a15f
2025-06-02 18:34:17 +00:00
dan cfee69fbe4 Updates to new test cases to run with SQLITE_ENABLE_SETLK_TIMEOUT=2 builds.
FossilOrigin-Name: c6d3e3542fdf6a3428d3a2ec20c699c169ed53341dc782d81f7fe5944f769748
2025-06-02 18:09:46 +00:00
dan eb9a145711 Merge trunk changes into this branch.
FossilOrigin-Name: 7d27451804e525190f8e67da75aaeb3bf2de677021f7566c6681398e60a88c72
2025-06-02 17:44:10 +00:00
stephan d84bbac8be Slight modernization of the TEA README.txt.
FossilOrigin-Name: bf7be67e3fb7b75fc281997f7bf9aa69eaf4da6bdf2fefe359b12d25ec95f512
2025-06-02 15:17:59 +00:00
stephan a3fcd7c79e TEA: remove the stale man page and references to the MSC makefiles from the README.txt. Based on discussion at [forum:87e6660191a472c5 | forum post 87e6660191a472c5]. A couple of weeks ago we internally discussed pulling in the MSC makefiles from the 3.49 tree but they are stale and possibly unused, so opted against it for the time being.
FossilOrigin-Name: c9888a409565e6c8891a3dafa56962df364fb729a61df2701dc0bb282e36fb75
2025-06-02 15:10:41 +00:00
drh 8658a8df59 Remove an unnecessary parameter from sqlite3VdbeRecordUnpack(). Improved
comments and assert()s on KeyInfo.

FossilOrigin-Name: 387f4c4d98b8fb83f6ae406e4143dabda7766e8752b7f6ca104655e51330c978
2025-06-02 13:54:33 +00:00
drh 7590bfd7fc Fix stale comments related to KeyInfo. Add new assert()s associated with
memory management of KeyInfo.

FossilOrigin-Name: abd805bc76f14ede7359b029908179b7ca57e929c5918acae1403ef73ae0bd47
2025-06-02 09:49:07 +00:00
drh d4c224b833 Fix an off-by-one error in the size computation of a vdbe-sorter.
[forum:/forumpost/c1cc8b057a|Forum post c1cc8b057a].
Problem introduced by checkin [d4307a0d43f42e96].

FossilOrigin-Name: 8b7a7fcf62e5c2742c243808fa482472954f2b4aae0bc7ae513bc07065c93737
2025-06-01 21:38:35 +00:00
drh f18bf8997b Fix VACUUM so that it works even when ATTACH_WRITE is disabled.
FossilOrigin-Name: 42494f85acb303919d3f1f2202f8b95fbd657652da4b8dc00451c10ea6c496e0
2025-06-01 16:10:25 +00:00
drh c0190101d1 Enhance "box" and "column" mode formatting in the CLI to better deal with
double-wide characters.

FossilOrigin-Name: b0de22ed0abf2ea5d269f191c884d7b2be167a2ed27018c25aaa0ea238cd621a
2025-05-31 20:51:42 +00:00
dan 977b0f8813 Update a few test scripts so that they run on windows.
FossilOrigin-Name: 14a18f4e3a3f35e636262a253364ab1e8054ed38c0c7fd482633522cbcf5ffc4
2025-05-31 19:55:07 +00:00
drh 0d3e5ca28e New makefile target "xdevtest" works like "releasetest" except that it
omits the "verify-source" dependency so that it can be run with uncommitted
changes in the source tree.

FossilOrigin-Name: 1afb1ac3e9f0a122f1374799c09b60a0dd5443434d4567d94385096ada91bf12
2025-05-31 18:26:37 +00:00
dan 51e3f855b8 Fix an affinity problem caused by a USING or NATURAL JOIN on the LHS of a FULL JOIN. [forum:/forumpost/5028c785b6|Forum post 5028c785b6].
FossilOrigin-Name: 8d393ca07fe09f48d77adb517e2e4baaa58a9251422de62a0504999205d3ea1d
2025-05-31 18:16:21 +00:00
drh 5248693924 Relax query flattener constraint (3b) and thereby allow flattening the RHS of
a LEFT JOIN even if the RHS contains a virtual table.  This was previously
disallowed by [9dbae1df75219e2a] as a performance optimization.  It
turns out that the constraint causes performance issues, and we do not have
a record of any performance issue that it solves.

FossilOrigin-Name: 1ddaa92057e550ea281d45d9860eafe69399224725548a93dd91c47a34e52152
2025-05-31 16:17:14 +00:00
dan 420233e375 Fix assert() statements in os_unix.c and os_win.c. Allow walsetlk_recover.test to run in non-SQLITE_ENABLE_SNAPSHOT builds.
FossilOrigin-Name: 9f521ecda2b8d2f383cc84e308b3a4adfcf1bd6339eb834c8fa76c8704c861b7
2025-05-31 15:10:41 +00:00
stephan 1ff4233f90 Move a mis-located makefile comment block.
FossilOrigin-Name: 7d884386bec11f47c2c18002dd8c573c9b5cb6f7cdf2307a96cccead05b6abf2
2025-05-31 11:08:06 +00:00
stephan 10206572b6 tcl extension: UDFs may now 'break' to return an SQL NULL. Add the (eval -asdict) flag to use a dict, instead of an array, for the eval row data.
FossilOrigin-Name: 413a626b5c7902c1810142536c36e4ea8ee7c616ea82dfe1114199f9319091f7
2025-05-31 11:02:06 +00:00
stephan b504aab848 Add some missing UNUSED_PARAMETER() annotations to squelch downstream build warnings when using -Wextra -pedantic.
FossilOrigin-Name: a98a2f49355ec39c56e571c70d377675b1bd99a6d43cf9217b0eb1e081895d8e
2025-05-31 09:44:00 +00:00
drh d930d7ef14 Follow-up to the previous: The same optimization suppression needs to
happen if the left-hand side is coming from a LEFT JOIN.

FossilOrigin-Name: cf5b37b3a39013d8ca9de92da2289346caf52b56daff59e19b140cc586a3421f
2025-05-30 22:58:09 +00:00
dan ded1959120 If blocking locks are enabled, avoid using the busy handler when blocked by another process running recovery.
FossilOrigin-Name: a35236757ab57c4c9b34e47c5dbc10d8f1220f8152955f5303cf9c3902ee169b
2025-05-30 20:52:18 +00:00
drh dd16539e71 When synthesizing an ON constraint from a USING or NATURAL, if the left-hand
side is coming from a RIGHT JOIN, be sure to set the EP_CanBeNull flag so that
the optimizer knows to check for NULL even if the column has a NOT NULL
constraint.  Fix for the problem reported by
[forum:/forumpost/4fc70203b61c7e12|forum post 4fc70203b61]

FossilOrigin-Name: 60adc78a22956429d34ccc4e2c193c5994c11c3b3cff7901d47fad7d92dba935
2025-05-30 19:55:46 +00:00
dan 88ed38ca6d Avoid invoking the busy-handler if a blocking lock times out while attempting to open a snapshot transaction.
FossilOrigin-Name: fe11f85fd3283bb7002f43bd8b796e9e7b67f6d1a92eb676be06b46d1458fc1d
2025-05-30 18:23:03 +00:00
stephan f42ceb9075 Configure-related fixes and additions, most notably integration of self-tests for proj.tcl's APIs. Teaish make-install fixes based on the discussion in [forum:87e6660191a472c5 | forum thread 87e6660191a472c5].
FossilOrigin-Name: 2e486f8fd011d28fdd7e59ff34f7f04374019932eb160a8c4de56a5ce01e5782
2025-05-30 16:08:31 +00:00
stephan 7ac10ec8b5 Random typo fixes in JNI docs.
FossilOrigin-Name: f63608a3847469b130e029cc569fe6f03a9053352ec43c10d69849cbab4f61c5
2025-05-30 15:46:52 +00:00
drh 8ae57fab64 Use a more robust backup definition for offsetof().
FossilOrigin-Name: 22441955e03df07903b98832a60c05c53721cd67c667f6c83d5e97fcc62735ee
2025-05-30 15:43:04 +00:00
dan b65326f51a Add "include <stddef.h>" to fts5 and rtree to ensure that they use the system version of the offsetof() macro when it is available, as the built-in version triggers ubsan errors with clang.
FossilOrigin-Name: 838deb7f3423df84061a043928ed34e1d74e2e7d57ef1a9519bb32fea82e4352
2025-05-30 11:14:11 +00:00
stephan 0148820fd7 Squelch an interesting but harmless struct initialization warning emitted after an emsdk update. Fix JS breakage introduced by changes in Emscripten 4.0.7: manually export the HEAPxyz symbols which used to be exposed by default.
FossilOrigin-Name: 10d0897cc9a5998fe1344cfbb242a78b59012e29aa3b1993895dfac26721b053
2025-05-30 10:18:09 +00:00
stephan d2b230a488 Fix the missing -lm link flag for the sqlite3 shell when building the autoconf bundle with --disable-static-shell, as reported in [forum:5adf1c932a | forum post 5adf1c932a].
FossilOrigin-Name: f6318c4a6b5a133657741c066ef2c76a71880dbc98cfff1fdd632637daf002dc
2025-05-29 20:29:13 +00:00
drh f429845a60 Extend the fix for ticket [623eff57e76d45f6] so that it covers RIGHT JOIN
in addition to LEFT JOIN.  Problem reported by 
[forum:/forumpost/7dee41d32506c4ae|forum post 2025-05-29T15:10:14Z].

FossilOrigin-Name: 29b1e1b97619d03a97ef562a5707929e241d019179b4ff1d0bc2a8c008441431
2025-05-29 18:44:41 +00:00
drh b163f66de0 Fix a goofy hash function in Lemon. No changes to SQLite itself.
FossilOrigin-Name: d6cbabe23d3919d5bde6a83421cdae92125caec09d5c39a648d0305878c6a1dc
2025-05-29 17:46:34 +00:00
drh a3ee3860a2 Merge all the latest trunk enhancements into the exists-to-join branch.
FossilOrigin-Name: 9084a4c8726a2c7ba1c381886e29c7b86121d531282be0d63d5988d84f6f448d
2024-07-31 23:46:10 +00:00
drh c96e47c80e Merge the latest trunk enhancements into the exists-to-join branch.
FossilOrigin-Name: fc643f8a12e9b7448136b281f798e18dfebe0a3df5115d930b965c8a33933e2d
2024-06-27 14:54:15 +00:00
drh 620a00ee32 Do not attempt the exists-to-join optimization if the FROM clause is full.
FossilOrigin-Name: 8e3a1d2850337a902ab36b1d6a0dad4ae35030b71d1e15547f6e7487c1f86d18
2024-06-20 12:07:25 +00:00
dan 62b954d545 Add tests for the change on this branch.
FossilOrigin-Name: ae19ff9ba819439fd107e745b6e8e503e5b68bfdb9da58b74035413704ad3caf
2024-06-14 18:06:25 +00:00
dan 0d92e66dfc Do not attempt the exists-to-join optimization for expressions from the ON clause of joins.
FossilOrigin-Name: 4666433cbd9af21c2e0440b10bcb39878624a39485e2bb514553b276acb8a401
2024-06-12 17:01:00 +00:00
drh ddc62664bf Provide the ability to disable the exists-to-join optimization using
SQLITE_TESTCTRL_OPTIMIZATIONS.  Provide tree-trace output for the optimization.

FossilOrigin-Name: 33a3f327855b427ae6ba0057218d043a1417bc9d780728f47f23acdd836e1686
2024-06-11 18:22:48 +00:00
drh b4f41b801a Merge trunk enhancements into the exists-to-join branch.
FossilOrigin-Name: 5f25a9518a675efbd0525cc2f5595ee7bc7122be7cfecdf6b20c909185dea370
2024-06-11 17:37:36 +00:00
dan df77e56cde Add missing calls to sqlite3exprSkipCollateAndLikely() to the enchancement on this branch.
FossilOrigin-Name: 078537d057d638389e3ab3bc04bcac53f342c7bf1d8d75222296ef42d09e9ee7
2024-06-10 19:45:33 +00:00
dan dc9a262319 Fix a crash that could occur when the SELECT in an EXISTS(SELECT ...) used an unknown collation sequence.
FossilOrigin-Name: f3009a6d7facd1ead520d588c5ad089db76d8641cd3dae076f2405492d7defcd
2024-06-10 19:31:18 +00:00
dan b111f1cb2a Add extra tests for the changes on this branch.
FossilOrigin-Name: a6365c778f2a6d6fa78e8520553373898f382ce73bf6496533e26291648ef5d1
2024-06-08 18:13:12 +00:00
dan 5525ac1049 Experimental optimization to rewrite a SELECT with an EXISTS(...) expression in the WHERE clause as a join.
FossilOrigin-Name: 972a33db0b0e924b78d5309d222d8ea298bd59c72da14ea2d14e8e2caaad1e0a
2024-06-07 21:00:42 +00:00
87 changed files with 3718 additions and 1446 deletions
+10 -1
View File
@@ -213,6 +213,13 @@ TCL_CONFIG_SH = @TCL_CONFIG_SH@
#TCL_STUB_LIB_SPEC = @TCL_STUB_LIB_SPEC@
#TCL_EXEC_PREFIX = @TCL_EXEC_PREFIX@
#TCL_VERSION = @TCL_VERSION@
TCL_MAJOR_VERSION = @TCL_MAJOR_VERSION@
# ^^^ main.mk optionally uses this for determining the Tcl extension's
# DLL name.
TCL_EXT_DLL_BASENAME = @TCL_EXT_DLL_BASENAME@
# ^^^ base name of the Tcl extension DLL. It varies by platform and
# Tcl version.
#
# $(TCLLIBDIR) = where to install the tcl plugin. If this is empty, it
# is calculated at make-time by the targets which need it but we
@@ -257,7 +264,9 @@ AS_AUTO_DEF = $(TOP)/auto.def
# invoked with to produce this makefile.
#
AS_AUTORECONFIG = @SQLITE_AUTORECONFIG@
.PHONY: reconfigure
reconfigure:
$(AS_AUTORECONFIG)
USE_AMALGAMATION ?= @USE_AMALGAMATION@
LINK_TOOLS_DYNAMICALLY ?= @LINK_TOOLS_DYNAMICALLY@
AMALGAMATION_GEN_FLAGS ?= --linemacros=@AMALGAMATION_LINE_MACROS@
+11 -6
View File
@@ -1623,7 +1623,6 @@ TESTSRC = \
$(TOP)\src\test_thread.c \
$(TOP)\src\test_vdbecov.c \
$(TOP)\src\test_vfs.c \
$(TOP)\src\test_windirent.c \
$(TOP)\src\test_window.c \
$(TOP)\src\test_wsd.c \
$(TOP)\ext\fts3\fts3_term.c \
@@ -2395,12 +2394,11 @@ SHELL_DEP = \
$(TOP)\ext\misc\sqlite3_stdio.h \
$(TOP)\ext\misc\uint.c \
$(TOP)\ext\misc\vfstrace.c \
$(TOP)\ext\misc\windirent.h \
$(TOP)\ext\misc\zipfile.c \
$(TOP)\ext\recover\dbdata.c \
$(TOP)\ext\recover\sqlite3recover.c \
$(TOP)\ext\recover\sqlite3recover.h \
$(TOP)\src\test_windirent.c \
$(TOP)\src\test_windirent.h
$(TOP)\ext\recover\sqlite3recover.h
# If use of zlib is enabled, add the "zipfile.c" source file.
#
@@ -2585,7 +2583,7 @@ sqlite_tcl.h:
testfixture.exe: $(TESTFIXTURE_SRC) $(TESTFIXTURE_DEP) $(SQLITE3H) $(LIBRESOBJS) $(HDR) $(SQLITE_TCL_DEP)
$(LTLINK) -DSQLITE_NO_SYNC=1 $(TESTFIXTURE_FLAGS) \
-DBUILD_sqlite -I$(TCLINCDIR) \
-DBUILD_sqlite -I$(TCLINCDIR) -I$(TOP)\ext\misc \
$(TESTFIXTURE_SRC) \
/link $(LDFLAGS) $(LTLINKOPTS) $(TCLLIBPATHS) $(LTLIBPATHS) $(LIBRESOBJS) $(TCLLIBS) $(LTLIBS) $(TLIBS)
@@ -2674,7 +2672,14 @@ srctree-check: $(TOP)\tool\srctree-check.tcl
# Testing for a release
#
releasetest:
releasetest: verify-source
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl release
# xdevtest is like releasetest, except that it skips the
# dependency on verify-source so that xdevtest can be run from
# a modified source tree.
#
xdevtest:
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl release
+1 -1
View File
@@ -1 +1 @@
3.50.4
3.51.0
+51 -28
View File
@@ -146,7 +146,7 @@ LDFLAGS.shlib = @SH_LDFLAGS@
# sources passed to [teaish-src-add], but may also be appended to by
# teaish.make.
#
tx.src =@TEAISH_EXT_SRC@
tx.src = @TEAISH_EXT_SRC@
#
# tx.CFLAGS is typically set by teaish.make, whereas TEAISH_CFLAGS
@@ -167,6 +167,11 @@ tx.LDFLAGS =
#
tx.dist.files = @TEAISH_DIST_FILES@
#
# The base name for a distribution tar/zip file.
#
tx.dist.basename = $(tx.name.dist)-$(tx.version)
# List of deps which may trigger an auto-reconfigure.
#
teaish__autogen.deps = \
@@ -199,9 +204,14 @@ $(teaish.makefile): $(teaish__auto.def) $(teaish.makefile.in) \
@AUTODEPS@
@if TEAISH_TESTER_TCL_IN
@TEAISH_TESTER_TCL_IN@:
@TEAISH_TESTER_TCL@: @TEAISH_TESTER_TCL_IN@
config.log: @TEAISH_TESTER_TCL@
@TEAISH_TESTER_TCL_IN@: $(teaish__autogen.deps)
config.log: @TEAISH_TESTER_TCL_IN@
@TEAISH_TESTER_TCL@: @TEAISH_TESTER_TCL_IN@
@endif
@if TEAISH_TEST_TCL_IN
@TEAISH_TEST_TCL_IN@: $(teaish__autogen.deps)
config.log: @TEAISH_TEST_TCL_IN@
@TEAISH_TEST_TCL@: @TEAISH_TEST_TCL_IN@
@endif
#
@@ -217,7 +227,7 @@ CC.tcl = \
#
CC.dll = \
$(CC.tcl) $(tx.src) $(LDFLAGS.shlib) \
$(LDFLAGS.configure) $(LDFLAGS) $(tx.LDFLAGS) $(TCL_STUB_LIB_SPEC)
$(tx.LDFLAGS) $(LDFLAGS.configure) $(LDFLAGS) $(TCL_STUB_LIB_SPEC)
@if TEAISH_ENABLE_DLL
#
@@ -248,16 +258,25 @@ test-extension: # this name is reserved for use by teaish.make[.in]
test-prepre: $(tx.dll)
@endif
@if TEAISH_TESTER_TCL
test-core.args = @TEAISH_TESTER_TCL@
teaish.tester.tcl = @TEAISH_TESTER_TCL@
test-core.args = $(teaish.tester.tcl)
@if TEAISH_ENABLE_DLL
test-core.args += '$(tx.dll)' '$(tx.loadPrefix)'
@else
test-core.args += '' ''
@endif
test-core.args += @TEAISH_TESTUTIL_TCL@
# Clients may pass additional args via test.args=...
# and ::argv will be rewritten before the test script loads, to
# remove $(test-core.args)
test.args ?=
test-core: test-pre
$(TCLSH) $(test-core.args)
test-prepre: @TEAISH_TESTER_TCL@
$(TCLSH) $(test-core.args) $(test.args)
test-gdb: $(teaish.tester.tcl)
gdb --args $(TCLSH) $(test-core.args) $(test.args)
test-vg.flags ?= --leak-check=full -v --show-reachable=yes --track-origins=yes
test-vg: $(teaish.tester.tcl)
valgrind $(test-vg.flags) $(TCLSH) $(test-core.args) $(test.args)
@else # !TEAISH_TESTER_TCL
test-prepre:
@endif # TEAISH_TESTER_TCL
@@ -288,7 +307,7 @@ distclean-core: distclean-pre
@endif
@endif
@if TEAISH_TESTER_TCL_IN
rm -f @TEAISH_TESTER_TCL@
rm -f $(teaish.tester.tcl)
@endif
@if TEAISH_PKGINDEX_TCL_IN
rm -f @TEAISH_PKGINDEX_TCL@
@@ -355,10 +374,15 @@ install-core: install-pre
@endif
install-test: install-core
@echo "Post-install test of [package require $(tx.name.pkg) $(tx.version)]..."; \
set xtra=""; \
if [ x != "x$(DESTDIR)" ]; then \
xtra='set ::auto_path [linsert $$::auto_path 0 [file normalize $(DESTDIR)$(TCLLIBDIR)/..]];'; \
fi; \
if echo \
'set c 0; ' \
'set c 0; ' $$xtra \
'@TEAISH_POSTINST_PREREQUIRE@' \
'if {[catch {package require $(tx.name.pkg) $(tx.version)}]} {incr c};' \
'if {[catch {package require $(tx.name.pkg) $(tx.version)} xc]} {incr c};' \
'if {$$c && "" ne $$xc} {puts $$xc; puts "auto_path=$$::auto_path"};' \
'exit $$c' \
| $(TCLSH) ; then \
echo "passed"; \
@@ -406,7 +430,7 @@ config.log: $(teaish.makefile.in)
# recognized when running in --teaish-install mode, causing
# the sub-configure to fail.
dist.flags = --with-tclsh=$(TCLSH)
dist.reconfig = $(teaish.dir)/configure $(dist.flags)
dist.reconfig = $(teaish.dir)/configure $(tx.dist.reconfig-flags) $(dist.flags)
# Temp dir for dist.zip. Must be different than dist.tgz or else
# parallel builds may hose the dist.
@@ -414,24 +438,23 @@ teaish__dist.tmp.zip = teaish__dist_zip
#
# Make a distribution zip file...
#
dist.basename = $(tx.name.dist)-$(tx.version)
dist.zip = $(dist.basename).zip
dist.zip = $(tx.dist.basename).zip
.PHONY: dist.zip dist.zip-core dist.zip-post
#dist.zip-pre:
# We apparently can't add a pre-hook here, else "make dist" rebuilds
# the archive each time it's run.
$(dist.zip): $(tx.dist.files)
@rm -fr $(teaish__dist.tmp.zip)
@mkdir -p $(teaish__dist.tmp.zip)/$(dist.basename)
@mkdir -p $(teaish__dist.tmp.zip)/$(tx.dist.basename)
@tar cf $(teaish__dist.tmp.zip)/tmp.tar $(tx.dist.files)
@tar xf $(teaish__dist.tmp.zip)/tmp.tar -C $(teaish__dist.tmp.zip)/$(dist.basename)
@tar xf $(teaish__dist.tmp.zip)/tmp.tar -C $(teaish__dist.tmp.zip)/$(tx.dist.basename)
@if TEAISH_DIST_FULL
@$(dist.reconfig) \
--teaish-install=$(teaish__dist.tmp.zip)/$(dist.basename) \
--t-e-d=$(teaish__dist.tmp.zip)/$(dist.basename) >/dev/null
--teaish-install=$(teaish__dist.tmp.zip)/$(tx.dist.basename) \
--t-e-d=$(teaish__dist.tmp.zip)/$(tx.dist.basename) >/dev/null
@endif
@rm -f $(dist.basename)/tmp.tar $(dist.zip)
@cd $(teaish__dist.tmp.zip) && zip -q -r ../$(dist.zip) $(dist.basename)
@rm -f $(tx.dist.basename)/tmp.tar $(dist.zip)
@cd $(teaish__dist.tmp.zip) && zip -q -r ../$(dist.zip) $(tx.dist.basename)
@rm -fr $(teaish__dist.tmp.zip)
@ls -la $(dist.zip)
dist.zip-core: $(dist.zip)
@@ -447,23 +470,23 @@ undist: undist-zip
# Make a distribution tarball...
#
teaish__dist.tmp.tgz = teaish__dist_tgz
dist.tgz = $(dist.basename).tar.gz
dist.tgz = $(tx.dist.basename).tar.gz
.PHONY: dist.tgz dist.tgz-core dist.tgz-post
# dist.tgz-pre:
# see notes in dist.zip
$(dist.tgz): $(tx.dist.files)
@rm -fr $(teaish__dist.tmp.tgz)
@mkdir -p $(teaish__dist.tmp.tgz)/$(dist.basename)
@mkdir -p $(teaish__dist.tmp.tgz)/$(tx.dist.basename)
@tar cf $(teaish__dist.tmp.tgz)/tmp.tar $(tx.dist.files)
@tar xf $(teaish__dist.tmp.tgz)/tmp.tar -C $(teaish__dist.tmp.tgz)/$(dist.basename)
@tar xf $(teaish__dist.tmp.tgz)/tmp.tar -C $(teaish__dist.tmp.tgz)/$(tx.dist.basename)
@if TEAISH_DIST_FULL
@rm -f $(teaish__dist.tmp.tgz)/$(dist.basename)/pkgIndex.tcl.in; # kludge
@rm -f $(teaish__dist.tmp.tgz)/$(tx.dist.basename)/pkgIndex.tcl.in; # kludge
@$(dist.reconfig) \
--teaish-install=$(teaish__dist.tmp.tgz)/$(dist.basename) \
--t-e-d=$(teaish__dist.tmp.zip)/$(dist.basename) >/dev/null
--teaish-install=$(teaish__dist.tmp.tgz)/$(tx.dist.basename) \
--t-e-d=$(teaish__dist.tmp.zip)/$(tx.dist.basename) >/dev/null
@endif
@rm -f $(dist.basename)/tmp.tar $(dist.tgz)
@cd $(teaish__dist.tmp.tgz) && tar czf ../$(dist.tgz) $(dist.basename)
@rm -f $(tx.dist.basename)/tmp.tar $(dist.tgz)
@cd $(teaish__dist.tmp.tgz) && tar czf ../$(dist.tgz) $(tx.dist.basename)
@rm -fr $(teaish__dist.tmp.tgz)
@ls -la $(dist.tgz)
dist.tgz-core: $(dist.tgz)
+4 -14
View File
@@ -83,22 +83,12 @@ script and then run make. For example:
$ cd sqlite-*-tea
$ ./configure --with-tcl=/path/to/tcl/install/root
$ make
$ make test
$ make install
WINDOWS BUILD
=============
The recommended method to build extensions under windows is to use the
Msys + Mingw build process. This provides a Unix-style build while
generating native Windows binaries. Using the Msys + Mingw build tools
means that you can use the same configure script as per the Unix build
to create a Makefile. See the tcl/win/README file for the URL of
the Msys + Mingw download.
If you have VC++ then you may wish to use the files in the win
subdirectory and build the extension using just VC++. These files have
been designed to be as generic as possible but will require some
additional maintenance by the project developer to synchronise with
the TEA configure.in and Makefile.in files. Instructions for using the
VC++ makefile are written in the first part of the Makefile.vc
file.
On Windows this build is known to work on Cygwin and some Msys2
environments. We do not currently support Microsoft makefiles for
native Windows builds.
+2 -1
View File
@@ -21,7 +21,8 @@ if {[llength [lindex $::argv 0]] > 0} {
# ----^^^^^^^ needed on Haiku when argv 0 is just a filename, else
# load cannot find the file.
}
source -encoding utf-8 [lindex $::argv 2]; # teaish/tester.tcl
set ::argv [lassign $argv - -]
source -encoding utf-8 [lindex $::argv 0]; # teaish/tester.tcl
@if TEAISH_PKGINIT_TCL
apply {{file} {
set dir [file dirname $::argv0]
-15
View File
@@ -1,15 +0,0 @@
.TH sqlite3 n 4.1 "Tcl-Extensions"
.HS sqlite3 tcl
.BS
.SH NAME
sqlite3 \- an interface to the SQLite3 database engine
.SH SYNOPSIS
\fBsqlite3\fI command_name ?filename?\fR
.br
.SH DESCRIPTION
SQLite3 is a self-contains, zero-configuration, transactional SQL database
engine. This extension provides an easy to use interface for accessing
SQLite database files from Tcl.
.PP
For full documentation see \fIhttps://sqlite.org/\fR and
in particular \fIhttps://sqlite.org/tclsqlite.html\fR.
+7 -3
View File
@@ -64,12 +64,18 @@ apply {{dir} {
-name.pkg sqlite3
-version $version
-name.dist $distname
-vsatisfies 8.6-
-libDir sqlite$version
-pragmas $pragmas
-src generic/tclsqlite3.c
}
# We should also have:
# -vsatisfies 8.6-
# But at least one platform is failing this vsatisfies check
# for no apparent reason:
# https://sqlite.org/forum/forumpost/fde857fb8101a4be
}} [teaish-get -dir]
#
# Must return either an empty string or a list in the form accepted by
# autosetup's [options] function.
@@ -119,8 +125,6 @@ proc teaish-options {} {
proc teaish-configure {} {
use teaish/feature
teaish-src-add -dist -dir generic/tclsqlite3.c
if {[proj-opt-was-provided override-sqlite-version]} {
teaish-pkginfo-set -version [opt-val override-sqlite-version]
proj-warn "overriding sqlite version number:" [teaish-pkginfo-get -version]
+387 -123
View File
@@ -60,10 +60,11 @@
# $proj__Config is an internal-use-only array for storing whatever generic
# internal stuff we need stored.
#
array set ::proj__Config {
self-tests 1
}
array set ::proj__Config [subst {
self-tests [get-env proj.self-tests 0]
verbose-assert [get-env proj.assert-verbose 0]
isatty [isatty? stdout]
}]
#
# List of dot-in files to filter in the final stages of
@@ -75,7 +76,6 @@ array set ::proj__Config {
# See: proj-dot-ins-append and proj-dot-ins-process
#
set ::proj__Config(dot-in-files) [list]
set ::proj__Config(isatty) [isatty? stdout]
#
# @proj-warn msg
@@ -85,28 +85,29 @@ set ::proj__Config(isatty) [isatty? stdout]
#
proc proj-warn {args} {
show-notices
puts stderr [join [list "WARNING: \[[proj-scope 1]\]: " {*}$args] " "]
puts stderr [join [list "WARNING:" \[ [proj-scope 1] \]: {*}$args] " "]
}
#
# Internal impl of [proj-fatal] and [proj-error]. It must be called
# using tailcall.
proc proj__faterr {failMode argv} {
#
proc proj__faterr {failMode args} {
show-notices
set lvl 1
while {"-up" eq [lindex $argv 0]} {
set argv [lassign $argv -]
while {"-up" eq [lindex $args 0]} {
set args [lassign $args -]
incr lvl
}
if {$failMode} {
puts stderr [join [list "FATAL: \[[proj-scope $lvl]]: " {*}$argv]]
puts stderr [join [list "FATAL:" \[ [proj-scope $lvl] \]: {*}$args]]
exit 1
} else {
error [join [list "\[[proj-scope $lvl]]:" {*}$argv]]
error [join [list in \[ [proj-scope $lvl] \]: {*}$args]]
}
}
#
# @proj-fatal ?-up...? msg...
#
@@ -118,7 +119,7 @@ proc proj__faterr {failMode argv} {
# additional level.
#
proc proj-fatal {args} {
tailcall proj__faterr 1 $args
tailcall proj__faterr 1 {*}$args
}
#
@@ -127,10 +128,9 @@ proc proj-fatal {args} {
# Works like proj-fatal but uses [error] intead of [exit].
#
proc proj-error {args} {
tailcall proj__faterr 0 $args
tailcall proj__faterr 0 {*}$args
}
set ::proj__Config(verbose-assert) [get-env proj-assert-verbose 0]
#
# @proj-assert script ?message?
#
@@ -147,7 +147,7 @@ proc proj-assert {script {msg ""}} {
if {"" eq $msg} {
set msg $script
}
proj-fatal "Assertion failed in \[[proj-scope 1]\]: $msg"
tailcall proj__faterr 1 "Assertion failed:" $msg
}
}
@@ -885,7 +885,9 @@ proc proj-looks-like-windows {{key host}} {
#
proc proj-looks-like-mac {{key host}} {
switch -glob -- [get-define $key] {
*apple* {
*-*-darwin* {
# https://sqlite.org/forum/forumpost/7b218c3c9f207646
# There's at least one Linux out there which matches *apple*.
return 1
}
default {
@@ -927,17 +929,13 @@ proc proj-exe-extension {} {
#
proc proj-dll-extension {} {
set inner {{key} {
switch -glob -- [get-define $key] {
*apple* {
return ".dylib"
}
*-*-ming* - *-*-cygwin - *-*-msys {
return ".dll"
}
default {
return ".so"
}
if {[proj-looks-like-mac $key]} {
return ".dylib"
}
if {[proj-looks-like-windows $key]} {
return ".dll"
}
return ".so"
}}
define BUILD_DLLEXT [apply $inner build]
define TARGET_DLLEXT [apply $inner host]
@@ -1665,7 +1663,7 @@ proc proj-dot-ins-append {fileIn args} {
proj-fatal "Too many arguments: $fileIn $args"
}
}
#puts "******* [proj-scope]: adding $fileIn"
#puts "******* [proj-scope]: adding [llength $fileIn]-length item: $fileIn"
lappend ::proj__Config(dot-in-files) $fileIn
}
@@ -1703,17 +1701,18 @@ proc proj-dot-ins-list {} {
# makes proj-dot-ins-append available for re-use.
#
proc proj-dot-ins-process {args} {
proj-parse-simple-flags args flags {
proj-parse-flags args flags {
-touch "" {return "-touch"}
-clear 0 {expr 1}
-validate 0 {expr 1}
}
#puts "args=$args"; parray flags
if {[llength $args] > 0} {
error "Invalid argument to [proj-scope]: $args"
}
foreach f $::proj__Config(dot-in-files) {
proj-assert {3==[llength $f]} \
"Expecting proj-dot-ins-list to be stored in 3-entry lists"
"Expecting proj-dot-ins-list to be stored in 3-entry lists. Got: $f"
lassign $f fIn fOut fScript
#puts "DOING $fIn ==> $fOut"
proj-make-from-dot-in {*}$flags(-touch) $fIn $fOut
@@ -1753,7 +1752,7 @@ proc proj-validate-no-unresolved-ats {args} {
set isMake [string match {*[Mm]ake*} $f]
foreach line [proj-file-content-list $f] {
if {!$isMake || ![string match "#*" [string trimleft $line]]} {
if {[regexp {(@[A-Za-z0-9_]+@)} $line match]} {
if {[regexp {(@[A-Za-z0-9_\.]+@)} $line match]} {
error "Unresolved reference to $match at line $lnno of $f"
}
}
@@ -1893,7 +1892,7 @@ proc proj-define-amend {args} {
#
proc proj-define-to-cflag {args} {
set rv {}
proj-parse-simple-flags args flags {
proj-parse-flags args flags {
-list 0 {expr 1}
-quote 0 {expr 1}
-zero-undef 0 {expr 1}
@@ -2001,7 +2000,7 @@ proc proj-cache-key {arg {addLevel 0}} {
# See proj-cache-key for -key's and -level's semantics, noting that
# this function adds one to -level for purposes of that call.
proc proj-cache-set {args} {
proj-parse-simple-flags args flags {
proj-parse-flags args flags {
-key => 0
-level => 0
}
@@ -2037,7 +2036,7 @@ proc proj-cache-remove {{key 0} {addLevel 0}} {
# See proj-cache-key for $key's and $addLevel's semantics, noting that
# this function adds one to $addLevel for purposes of that call.
proc proj-cache-check {args} {
proj-parse-simple-flags args flags {
proj-parse-flags args flags {
-key => 0
-level => 0
}
@@ -2070,147 +2069,316 @@ proc proj-coalesce {args} {
}
#
# @proj-parse-simple-flags ...
# @proj-parse-flags argvListName targetArrayName {prototype}
#
# A helper to parse flags from proc argument lists.
#
# Expects a list of arguments to parse, an array name to store any
# -flag values to, and a prototype object which declares the flags.
# The first argument is the name of a var holding the args to
# parse. It will be overwritten, possibly with a smaller list.
#
# The prototype must be a list in one of the following forms:
# The second argument is the name of an array variable to create in
# the caller's scope.
#
# -flag defaultValue {script}
# The third argument, $prototype, is a description of how to handle
# the flags. Each entry in that list must be in one of the
# following forms:
#
# -flag => defaultValue
# -----^--^ (with spaces there!)
# -flag defaultValue ?-literal|-call|-apply?
# script|number|incr|proc-name|{apply $aLambda}
#
# Repeated for each flag.
# -flag* ...as above...
#
# The first form represents a basic flag with no associated
# following argument. The second form extracts its value
# from the following argument in $argvName.
# -flag => defaultValue ?-call proc-name-and-args|-apply lambdaExpr?
#
# The first argument to this function is the name of a var holding the
# args to parse. It will be overwritten, possibly with a smaller list.
# -flag* => ...as above...
#
# The second argument the name of an array variable to create in the
# caller's scope. (Pneumonic: => points to the next argument.)
# :PRAGMA
#
# For the first form of flag, $script is run in the caller's scope if
# $argv contains -flag, and the result of that script is the new value
# for $tgtArrayName(-flag). This function intercepts [return $val]
# from $script. Any empty script will result in the flag having ""
# assigned to it.
# The first two forms represents a basic flag with no associated
# following argument. The third and fourth forms, called arg-consuming
# flags, extract the value from the following argument in $argvName
# (pneumonic: => points to the next argument.). The :PRAGMA form
# offers a way to configure certain aspects of this call.
#
# The args list is only inspected until the first argument which is
# not described by $prototype. i.e. the first "non-flag" (not counting
# values consumed for flags defined like --flag=>default).
# If $argv contains any given flag from $prototype, its default value
# is overridden depending on several factors:
#
# - If the -literal flag is used, or the flag's script is a number,
# value is used verbatim.
#
# - Else if the -call flag is used, the argument must be a proc name
# and any leading arguments, e.g. {apply $myLambda}. The proc is passed
# the (flag, value) as arguments (non-consuming flags will get
# passed the flag's current/starting value and consuming flags will
# get the next argument). Its result becomes the result of the
# flag.
#
# - Else if -apply X is used, it's effectively shorthand for -call
# {apply X}. Its argument may either be a $lambaRef or a {{f v}
# {body}} construct.
#
# - Else if $script is one of the following values, it is treated as
# the result of...
#
# - incr: increments the current value of the flag.
#
# - Else $script is eval'd to get its result value. That result
# becomes the new flag value for $tgtArrayName(-flag). This
# function intercepts [return $val] from eval'ing $script. Any
# empty script will result in the flag having "" assigned to it.
#
# Unless the -flag has a trailing asterisk, e.g. -flag*, this function
# assumes that each flag is unique, and using a flag more than once
# causes an error to be triggered. the -flag* forms works similarly
# except that may appear in $argv any number of times:
#
# - For non-arg-consuming flags, each invocation of -flag causes the
# result of $script to overwrite the previous value. e.g. so
# {-flag* {x} {incr foo}} has a default value of x, but passing in
# -flag twice would change it to the result of incrementing foo
# twice. This form can be used to implement, e.g., increasing
# verbosity levels by passing -verbose multiple times.
#
# - For arg-consuming flags, the given flag starts with value X, but
# if the flag is provided in $argv, the default is cleared, then
# each instance of -flag causes its value to be appended to the
# result, so {-flag* => {a b c}} defaults to {a b c}, but passing
# in -flag y -flag z would change it to {y z}, not {a b c y z}..
#
# By default, the args list is only inspected until the first argument
# which is not described by $prototype. i.e. the first "non-flag" (not
# counting values consumed for flags defined like -flag => default).
# The :all-flags pragma (see below) can modify this behavior.
#
# If a "--" flag is encountered, no more arguments are inspected as
# flags. If "--" is the first non-flag argument, the "--" flag is
# removed from the results but all remaining arguments are passed
# through. If "--" appears after the first non-flag, it is retained.
# flags unless the :all-flags pragma (see below) is in effect. The
# first instance of "--" is removed from the target result list but
# all remaining instances of "--" are are passed through.
#
# This function assumes that each flag is unique, and using a flag
# more than once behaves in a last-one-wins fashion.
# Any argvName entries not described in $prototype are considered to
# be "non-flags" for purposes of this function, even if they
# ostensibly look like flags.
#
# Any argvName entries not described in $prototype are not treated as
# flags.
#
# Returns the number of flags it processed in $argvName.
# Returns the number of flags it processed in $argvName, not counting
# "--".
#
# Example:
#
# set args [list -foo -bar {blah} 8 9 10 -theEnd]
# proj-parse-simple-flags args flags {
# -foo 0 {expr 1}
# -bar => 0
# -no-baz 2 {return 0}
# }
## set args [list -foo -bar {blah} -z 8 9 10 -theEnd]
## proj-parse-flags args flags {
## -foo 0 {expr 1}
## -bar => 0
## -no-baz 1 {return 0}
## -z 0 2
## }
#
# After that $flags would contain {-foo 1 -bar {blah} -no-baz 2}
# After that $flags would contain {-foo 1 -bar {blah} -no-baz 1 -z 2}
# and $args would be {8 9 10 -theEnd}.
#
# Potential TODOs: consider using lappend instead of set so that any
# given flag can be used more than once. Or add a syntax to indicate
# that multiples are allowed. Also consider searching the whole
# argv list, rather than stopping at the first non-flag
# Pragmas:
#
proc proj-parse-simple-flags {argvName tgtArrayName prototype} {
# Passing :PRAGMAS to this function may modify how it works. The
# following pragmas are supported (note the leading ":"):
#
# :all-flags indicates that the whole input list should be scanned,
# not stopping at the first non-flag or "--".
#
proc proj-parse-flags {argvName tgtArrayName prototype} {
upvar $argvName argv
upvar $tgtArrayName tgt
array set dflt {}
array set scripts {}
array set consuming {}
upvar $tgtArrayName outFlags
array set flags {}; # staging area
array set blob {}; # holds markers for various per-key state and options
set incrSkip 1; # 1 if we stop at the first non-flag, else 0
# Parse $prototype for flag definitions...
set n [llength $prototype]
# Figure out what our flags are...
set checkProtoFlag {
#puts "**** checkProtoFlag #$i of $n k=$k fv=$fv"
switch -exact -- $fv {
-literal {
proj-assert {![info exists blob(${k}.consumes)]}
set blob(${k}.script) [list expr [lindex $prototype [incr i]]]
}
-apply {
set fv [lindex $prototype [incr i]]
if {2 == [llength $fv]} {
# Treat this as a lambda literal
set fv [list $fv]
}
lappend blob(${k}.call) "apply $fv"
}
-call {
# arg is either a proc name or {apply $aLambda}
set fv [lindex $prototype [incr i]]
lappend blob(${k}.call) $fv
}
default {
proj-assert {![info exists blob(${k}.consumes)]}
set blob(${k}.script) $fv
}
}
if {$i >= $n} {
proj-error -up "[proj-scope]: Missing argument for $k flag"
}
}
for {set i 0} {$i < $n} {incr i} {
set k [lindex $prototype $i]
#puts "**** #$i of $n k=$k"
proj-assert {[string match -* $k]} \
"Invalid flag value: $k"
set v ""
set s ""
switch -exact -- [lindex $prototype [expr {$i + 1}]] {
=> {
incr i 2
if {$i >= $n} {
proj-error "Missing argument for $k => flag"
}
set consuming($k) 1
set v [lindex $prototype $i]
}
default {
set v [lindex $prototype [incr i]]
set s [lindex $prototype [incr i]]
set scripts($k) $s
# Check for :PRAGMA...
switch -exact -- $k {
:all-flags {
set incrSkip 0
continue
}
}
#puts "**** #$i of $n k=$k v=$v s=$s"
set dflt($k) $v
proj-assert {[string match -* $k]} \
"Invalid argument: $k"
if {[string match {*\*} $k]} {
# Re-map -foo* to -foo and flag -foo as a repeatable flag
set k [string map {* ""} $k]
incr blob(${k}.multi)
}
if {[info exists flags($k)]} {
proj-error -up "[proj-scope]: Duplicated prototype for flag $k"
}
switch -exact -- [lindex $prototype [expr {$i + 1}]] {
=> {
# -flag => DFLT ?-subflag arg?
incr i 2
if {$i >= $n} {
proj-error -up "[proj-scope]: Missing argument for $k => flag"
}
incr blob(${k}.consumes)
set vi [lindex $prototype $i]
if {$vi in {-apply -call}} {
proj-error -up "[proj-scope]: Missing default value for $k flag"
} else {
set fv [lindex $prototype [expr {$i + 1}]]
if {$fv in {-apply -call}} {
incr i
eval $checkProtoFlag
}
}
}
default {
# -flag VALUE ?flag? SCRIPT
set vi [lindex $prototype [incr i]]
set fv [lindex $prototype [incr i]]
eval $checkProtoFlag
}
}
#puts "**** #$i of $n k=$k vi=$vi"
set flags($k) $vi
}
# Now look for those flags in the source list
array set tgt [array get dflt]
unset dflt
#puts "-- flags"; parray flags
#puts "-- blob"; parray blob
set rc 0
set rv {}
set rv {}; # staging area for the target argv value
set skipMode 0
set n [llength $argv]
# Now look for those flags in $argv...
for {set i 0} {$i < $n} {incr i} {
set arg [lindex $argv $i]
#puts "-- [proj-scope] arg=$arg"
if {$skipMode} {
lappend rv $arg
} elseif {"--" eq $arg} {
incr skipMode
} elseif {[info exists tgt($arg)]} {
if {[info exists consuming($arg)]} {
if {$i + 1 >= $n} {
proj-assert 0 {Cannot happen - bounds already checked}
# "--" is the conventional way to end processing of args
if {[incr blob(--)] > 1} {
# Elide only the first one
lappend rv $arg
}
incr skipMode $incrSkip
} elseif {[info exists flags($arg)]} {
# A known flag...
set isMulti [info exists blob(${arg}.multi)]
incr blob(${arg}.seen)
if {1 < $blob(${arg}.seen) && !$isMulti} {
proj-error -up [proj-scope] "$arg flag was used multiple times"
}
set vMode 0; # 0=as-is, 1=eval, 2=call
set isConsuming [info exists blob(${arg}.consumes)]
if {$isConsuming} {
incr i
if {$i >= $n} {
proj-error -up [proj-scope] "is missing argument for $arg flag"
}
set tgt($arg) [lindex $argv [incr i]]
} elseif {"" eq $scripts($arg)} {
set tgt($arg) ""
set vv [lindex $argv $i]
} elseif {[info exists blob(${arg}.script)]} {
set vMode 1
set vv $blob(${arg}.script)
} else {
#puts "**** running scripts($arg) $scripts($arg)"
set code [catch {uplevel 1 $scripts($arg)} xrc xopt]
#puts "**** tgt($arg)=$scripts($arg) code=$code rc=$rc"
if {$code in {0 2}} {
set tgt($arg) $xrc
} else {
return {*}$xopt $xrc
set vv $flags($arg)
}
if {[info exists blob(${arg}.call)]} {
set vMode 2
set vv [concat {*}$blob(${arg}.call) $arg $vv]
} elseif {$isConsuming} {
proj-assert {!$vMode}
# fall through
} elseif {"" eq $vv || [string is double -strict $vv]} {
set vMode 0
} elseif {$vv in {incr}} {
set vMode 0
switch -exact $vv {
incr {
set xx $flags($k); incr xx; set vv $xx; unset xx
}
default {
proj-error "Unhandled \$vv value $vv"
}
}
} else {
set vv [list eval $vv]
set vMode 1
}
if {$vMode} {
set code [catch [list uplevel 1 $vv] vv xopt]
if {$code ni {0 2}} {
return {*}$xopt $vv
}
}
if {$isConsuming && $isMulti} {
if {1 == $blob(${arg}.seen)} {
# On the first hit, overwrite the default with a new list.
set flags($arg) [list $vv]
} else {
# On subsequent hits, append to the list.
lappend flags($arg) $vv
}
} else {
set flags($arg) $vv
}
incr rc
} else {
incr skipMode
# Non-flag
incr skipMode $incrSkip
lappend rv $arg
}
}
set argv $rv
array set outFlags [array get flags]
#puts "-- rv=$rv argv=$argv flags="; parray flags
return $rc
}; # proj-parse-flags
#
# Older (deprecated) name of proj-parse-flags.
#
proc proj-parse-simple-flags {args} {
tailcall proj-parse-flags {*}$args
}
if {$::proj__Config(self-tests)} {
set __ova $::proj__Config(verbose-assert);
set ::proj__Config(verbose-assert) 1
puts "Running [info script] self-tests..."
# proj-cache...
apply {{} {
#proj-warn "Test code for proj-cache"
proj-assert {![proj-cache-check -key here check]}
@@ -2233,4 +2401,100 @@ if {$::proj__Config(self-tests)} {
proj-assert {"" eq [proj-cache-remove]}
proj-assert {"" eq $check}
}}
}
# proj-parse-flags ...
apply {{} {
set foo 3
set argv {-a "hi - world" -b -b -b -- -a {bye bye} -- -d -D c -a "" --}
proj-parse-flags argv flags {
:all-flags
-a* => "gets overwritten"
-b* 7 {incr foo}
-d 1 0
-D 0 1
}
#puts "-- argv = $argv"; parray flags;
proj-assert {"-- c --" eq $argv}
proj-assert {$flags(-a) eq "{hi - world} {bye bye} {}"}
proj-assert {$foo == 6}
proj-assert {$flags(-b) eq $foo}
proj-assert {$flags(-d) == 0}
proj-assert {$flags(-D) == 1}
set foo 0
foreach x $flags(-a) {
proj-assert {$x in {{hi - world} {bye bye} {}}}
incr foo
}
proj-assert {3 == $foo}
set argv {-a {hi world} -b -maybe -- -a {bye bye} -- -b c --}
set foo 0
proj-parse-flags argv flags {
-a => "aaa"
-b 0 {incr foo}
-maybe no -literal yes
}
#parray flags; puts "--- argv = $argv"
proj-assert {"-a {bye bye} -- -b c --" eq $argv}
proj-assert {$flags(-a) eq "hi world"}
proj-assert {1 == $flags(-b)}
proj-assert {"yes" eq $flags(-maybe)}
set argv {-f -g -a aaa -M -M -M -L -H -A AAA a b c}
set foo 0
set myLambda {{flag val} {
proj-assert {$flag in {-f -g -M}}
#puts "myLambda flag=$flag val=$val"
incr val
}}
proc myNonLambda {flag val} {
proj-assert {$flag in {-A -a}}
#puts "myNonLambda flag=$flag val=$val"
concat $val $val
}
proj-parse-flags argv flags {
-f 0 -call {apply $myLambda}
-g 2 -apply $myLambda
-h 3 -apply $myLambda
-H 30 33
-a => aAAAa -apply {{f v} {
set v
}}
-A => AaaaA -call myNonLambda
-B => 17 -call myNonLambda
-M* 0 -apply $myLambda
-L "" -literal $myLambda
}
rename myNonLambda ""
#puts "--- argv = $argv"; parray flags
proj-assert {$flags(-f) == 1}
proj-assert {$flags(-g) == 3}
proj-assert {$flags(-h) == 3}
proj-assert {$flags(-H) == 33}
proj-assert {$flags(-a) == {aaa}}
proj-assert {$flags(-A) eq "AAA AAA"}
proj-assert {$flags(-B) == 17}
proj-assert {$flags(-M) == 3}
proj-assert {$flags(-L) eq $myLambda}
set argv {-touch -validate}
proj-parse-flags argv flags {
-touch "" {return "-touch"}
-validate 0 1
}
#puts "----- argv = $argv"; parray flags
proj-assert {$flags(-touch) eq "-touch"}
proj-assert {$flags(-validate) == 1}
proj-assert {$argv eq {}}
set argv {-i -i -i}
proj-parse-flags argv flags {
-i* 0 incr
}
proj-assert {3 == $flags(-i)}
}}
set ::proj__Config(verbose-assert) $__ova
unset __ova
puts "Done running [info script] self-tests."
}; # proj- API self-tests
+40 -19
View File
@@ -217,6 +217,11 @@ proc sqlite-configure {buildMode configScript} {
=> {This legacy flag has no effect on the library but may influence
the generated sqlite_cfg.h by adding #define HAVE_LFS}
}
{canonical} {
column-metadata => {Enable the column metadata APIs}
# ^^^ Affects how sqlite3.c is generated, so is not available in
# the autoconf build.
}
}
# Options for TCL support
@@ -227,8 +232,6 @@ proc sqlite-configure {buildMode configScript} {
This tree requires TCL for code generation but can use the in-tree
copy of autosetup/jimsh0.c for that. The SQLite TCL extension and the
test code require a canonical tclsh.}
}
{canonical} {
with-tcl:DIR
=> {Directory containing tclConfig.sh or a directory one level up from
that, from which we can derive a directory containing tclConfig.sh.
@@ -236,11 +239,10 @@ proc sqlite-configure {buildMode configScript} {
the --prefix flag.}
with-tclsh:PATH
=> {Full pathname of tclsh to use. It is used for (A) trying to find
tclConfig.sh and (B) all TCL-based code generation. Warning: if
its containing dir has multiple tclsh versions, it may select the
tclConfig.sh and (B) all TCL-based code generation. Use --with-tcl
unless you have a specific need for this flag. Warning: if its
containing dir has multiple tclsh versions, it may select the
wrong tclConfig.sh!}
}
{canonical} {
static-tclsqlite3=0
=> {Statically-link tclsqlite3. This only works if TCL support is
enabled and all requisite libraries are available in
@@ -334,8 +336,8 @@ proc sqlite-configure {buildMode configScript} {
=> {Link the sqlite3 shell app against the DLL instead of embedding sqlite3.c}
}
{canonical autoconf} {
# A potential TODO without a current use case:
#rpath=1 => {Disable use of the rpath linker flag}
rpath=1 => {Disable use of the rpath linker flag}
# soname: https://sqlite.org/src/forumpost/5a3b44f510df8ded
soname:=legacy
=> {SONAME for libsqlite3.so. "none", or not using this flag, sets no
@@ -772,7 +774,8 @@ proc sqlite-handle-common-feature-flags {} {
sqlite-add-feature-flag -DSQLITE_ENABLE_MEMSYS3
}
}
scanstatus -DSQLITE_ENABLE_STMT_SCANSTATUS {}
scanstatus -DSQLITE_ENABLE_STMT_SCANSTATUS {}
column-metadata -DSQLITE_ENABLE_COLUMN_METADATA {}
}] {
if {$boolFlag ni $::autosetup(options)} {
# Skip flags which are in the canonical build but not
@@ -1969,13 +1972,14 @@ proc sqlite-check-tcl {} {
# TCLLIBDIR from here, which will cause the canonical makefile to
# use this one rather than to re-calculate it at make-time.
set tcllibdir [get-env TCLLIBDIR ""]
set sq3Ver [get-define PACKAGE_VERSION]
if {"" eq $tcllibdir} {
# Attempt to extract TCLLIBDIR from TCL's $auto_path
if {"" ne $with_tclsh &&
[catch {exec echo "puts stdout \$auto_path" | "$with_tclsh"} result] == 0} {
foreach i $result {
if {[file isdir $i]} {
set tcllibdir $i/sqlite3
set tcllibdir $i/sqlite${sq3Ver}
break
}
}
@@ -2111,15 +2115,31 @@ proc sqlite-determine-codegen-tcl {} {
# sqlite-determine-codegen-tcl.
proc sqlite-handle-tcl {} {
sqlite-check-tcl
if {"canonical" eq $::sqliteConfig(build-mode)} {
msg-result "TCL for code generation: [sqlite-determine-codegen-tcl]"
if {"canonical" ne $::sqliteConfig(build-mode)} return
msg-result "TCL for code generation: [sqlite-determine-codegen-tcl]"
# Determine the base name of the Tcl extension's DLL
#
if {[get-define HAVE_TCL]} {
if {[string match *-cygwin [get-define host]]} {
set libname cyg
} else {
set libname lib
}
if {[get-define TCL_MAJOR_VERSION] > 8} {
append libname tcl9
}
append libname sqlite
} else {
set libname ""
}
define TCL_EXT_DLL_BASENAME $libname
# The extension is added in the makefile
}
########################################################################
# Handle the --enable/disable-rpath flag.
proc sqlite-handle-rpath {} {
proj-check-rpath
# autosetup/cc-shared.tcl sets the rpath flag definition in
# [get-define SH_LINKRPATH], but it does so on a per-platform basis
# rather than as a compiler check. Though we should do a proper
@@ -2128,12 +2148,13 @@ proc sqlite-handle-rpath {} {
# for which sqlite-env-is-unix-on-windows returns a non-empty
# string.
# if {[proj-opt-truthy rpath]} {
# proj-check-rpath
# } else {
# msg-result "Disabling use of rpath."
# define LDFLAGS_RPATH ""
# }
# https://sqlite.org/forum/forumpost/13cac3b56516f849
if {[proj-opt-truthy rpath]} {
proj-check-rpath
} else {
msg-result "Disabling use of rpath."
define LDFLAGS_RPATH ""
}
}
########################################################################
+77 -56
View File
@@ -92,6 +92,7 @@ array set teaish__Config [proj-strip-hash-comments {
-tm.tcl.in TEAISH_TM_TCL_IN
-options {}
-pragmas {}
-src {}
}
#
@@ -331,29 +332,33 @@ proc teaish-configure-core {} {
-url - -v ""
-tm.tcl - -v ""
-tm.tcl.in - -v ""
-src - -v ""
} {
#proj-assert 0 {Just testing}
set isPIFlag [expr {"-" ne $pflag}]
if {$isPIFlag} {
if {[info exists ::teaish__PkgInfo($pflag)]} {
# Was already set - skip it.
continue;
}
proj-assert {{-} eq $key}
proj-assert {{-} eq $key};# "Unexpected pflag=$pflag key=$key type=$type val=$val"
set key $f2d($pflag)
}
proj-assert {"" ne $key}
set got [get-define $key "<nope>"]
if {"<nope>" ne $got} {
# Was already set - skip it.
continue
if {"" ne $key} {
if {"<nope>" ne [get-define $key "<nope>"]} {
# Was already set - skip it.
continue
}
}
switch -exact -- $type {
-v {}
-e { set val [eval $val] }
default { proj-error "Invalid type flag: $type" }
}
#puts "***** defining default $pflag $key {$val} isPIFlag=$isPIFlag got=$got"
define $key $val
#puts "***** defining default $pflag $key {$val} isPIFlag=$isPIFlag"
if {$key ne ""} {
define $key $val
}
if {$isPIFlag} {
set ::teaish__PkgInfo($pflag) $val
}
@@ -541,10 +546,10 @@ proc teaish__configure_phase1 {} {
define TEAISH_VSATISFIES_CODE [join $code "\n"]
}}; # vsatisfies
if {[proj-looks-like-windows] || [proj-looks-like-mac]} {
if {[proj-looks-like-windows]} {
# Without this, linking of an extension will not work on Cygwin or
# Msys2.
msg-result "Using USE_TCL_STUBS for this environment"
msg-result "Using USE_TCL_STUBS for Unix(ish)-on-Windows environment"
teaish-cflags-add -DUSE_TCL_STUBS=1
}
@@ -585,7 +590,8 @@ proc teaish__configure_phase1 {} {
#
if {0x0f & $::teaish__Config(pkginit-policy)} {
file delete -force -- [get-define TEAISH_PKGINIT_TCL]
proj-dot-ins-append [get-define TEAISH_PKGINIT_TCL_IN]
proj-dot-ins-append [get-define TEAISH_PKGINIT_TCL_IN] \
[get-define TEAISH_PKGINIT_TCL]
}
if {0x0f & $::teaish__Config(tm-policy)} {
file delete -force -- [get-define TEAISH_TM_TCL]
@@ -595,17 +601,20 @@ proc teaish__configure_phase1 {} {
apply {{} {
# Queue up any remaining dot-in files
set dotIns [list]
foreach d {
TEAISH_TESTER_TCL_IN
TEAISH_TEST_TCL_IN
TEAISH_MAKEFILE_IN
foreach {dIn => dOut} {
TEAISH_TESTER_TCL_IN => TEAISH_TESTER_TCL
TEAISH_TEST_TCL_IN => TEAISH_TEST_TCL
TEAISH_MAKEFILE_IN => TEAISH_MAKEFILE
} {
lappend dotIns [get-define $d ""]
lappend dotIns [get-define $dIn ""] [get-define $dOut ""]
}
lappend dotIns $::autosetup(srcdir)/Makefile.in; # must be after TEAISH_MAKEFILE_IN
foreach f $dotIns {
if {"" ne $f} {
proj-dot-ins-append $f
lappend dotIns $::autosetup(srcdir)/Makefile.in Makefile; # must be after TEAISH_MAKEFILE_IN.
# Much later: probably because of timestamps for deps purposes :-?
#puts "dotIns=$dotIns"
foreach {i o} $dotIns {
if {"" ne $i && "" ne $o} {
#puts " pre-dot-ins-append: \[$i\] -> \[$o\]"
proj-dot-ins-append $i $o
}
}
}}
@@ -640,10 +649,10 @@ proc teaish__configure_phase1 {} {
#
# NO [define]s after this point!
#
proj-dot-ins-process -validate
proj-if-opt-truthy teaish-dump-defines {
proj-file-write config.defines.txt $tdefs
}
proj-dot-ins-process -validate
}; # teaish__configure_phase1
@@ -1068,7 +1077,7 @@ If you are attempting an out-of-tree build, use
]]} {
if {[string match *.in $extM]} {
define TEAISH_MAKEFILE_IN $extM
define TEAISH_MAKEFILE [file rootname [file tail $extM]]
define TEAISH_MAKEFILE _[file rootname [file tail $extM]]
} else {
define TEAISH_MAKEFILE_IN ""
define TEAISH_MAKEFILE $extM
@@ -1136,8 +1145,8 @@ If you are attempting an out-of-tree build, use
set flist [list $dirExt/teaish.test.tcl.in $dirExt/teaish.test.tcl]
if {[proj-first-file-found ttt $flist]} {
if {[string match *.in $ttt]} {
# Generate teaish.test.tcl from $ttt
set xt [file rootname [file tail $ttt]]
# Generate _teaish.test.tcl from $ttt
set xt _[file rootname [file tail $ttt]]
file delete -force -- $xt; # ensure no stale copy is used
define TEAISH_TEST_TCL $xt
define TEAISH_TEST_TCL_IN $ttt
@@ -1304,7 +1313,6 @@ proc teaish-ldflags-prepend {args} {
# object files (which are typically in the build tree)).
#
proc teaish-src-add {args} {
set i 0
proj-parse-simple-flags args flags {
-dist 0 {expr 1}
-dir 0 {expr 1}
@@ -1389,7 +1397,7 @@ proc teaish__cleanup_rule {{tgt clean}} {
return ${tgt}-_${x}_
}
# @teaish-make-obj objfile srcfile ?...args?
# @teaish-make-obj ?flags? ?...args?
#
# Uses teaish-make-add to inject makefile rules for $objfile from
# $srcfile, which is assumed to be C code which uses libtcl. Unless
@@ -1403,43 +1411,45 @@ proc teaish__cleanup_rule {{tgt clean}} {
# Any arguments after the 2nd may be flags described below or, if no
# -recipe is provided, flags for the compiler call.
#
# -obj obj-filename.o
#
# -src src-filename.c
#
# -recipe {...}
# Uses the trimmed value of {...} as the recipe, prefixing it with
# a single hard-tab character.
#
# -deps {...}
# List of extra files to list as dependencies of $o. Good luck
# escaping non-trivial cases properly.
# List of extra files to list as dependencies of $o.
#
# -clean
# Generate cleanup rules as well.
proc teaish-make-obj {o src args} {
set consume 0
set clean 0
set flag ""
array set flags {}
set xargs {}
foreach arg $args {
if {$consume} {
set consume 0
set flags($flag) $arg
continue
}
switch -exact -- $arg {
-clean {incr clean}
-recipe -
-deps {
set flag $arg
incr consume
}
default {
lappend xargs $arg
}
proc teaish-make-obj {args} {
proj-parse-simple-flags args flags {
-clean 0 {expr 1}
-recipe => {}
-deps => {}
-obj => {}
-src => {}
}
#parray flags
if {"" eq $flags(-obj)} {
set args [lassign $args flags(-obj)]
if {"" eq $flags(-obj)} {
proj-error "Missing -obj flag."
}
}
foreach f {-deps -src} {
set flags($f) [string trim [string map {\n " "} $flags($f)]]
}
foreach f {-deps -src} {
set flags($f) [string trim $flags($f)]
}
#parray flags
#puts "-- args=$args"
teaish-make-add \
"# [proj-scope 1] -> [proj-scope] $o $src" -nl \
"$o: $src $::teaish__Config(teaish.tcl)"
"# [proj-scope 1] -> [proj-scope] $flags(-obj) $flags(-src)" -nl \
"$flags(-obj): $flags(-src) $::teaish__Config(teaish.tcl)"
if {[info exists flags(-deps)]} {
teaish-make-add " " [join $flags(-deps)]
}
@@ -1447,12 +1457,12 @@ proc teaish-make-obj {o src args} {
if {[info exists flags(-recipe)]} {
teaish-make-add [string trim $flags(-recipe)] -nl
} else {
teaish-make-add [join [list \$(CC.tcl) -c $src {*}$xargs]] -nl
teaish-make-add [join [list \$(CC.tcl) -c $flags(-src) {*}$args]] -nl
}
if {$clean} {
if {$flags(-clean)} {
set rule [teaish__cleanup_rule]
teaish-make-add \
"clean: $rule\n$rule:\n\trm -f \"$o\"\n"
"clean: $rule\n$rule:\n\trm -f \"$flags(-obj)\"\n"
}
}
@@ -2080,6 +2090,17 @@ proc teaish-pkginfo-set {args} {
set v $x
}
-src {
set d $::teaish__Config(extension-dir)
foreach f $v {
lappend ::teaish__Config(dist-files) $f
lappend ::teaish__Config(extension-src) $d/$f
lappend ::teaish__PkgInfo(-src) $f
# ^^^ so that default-value initialization in
# teaish-configure-core recognizes that it's been set.
}
}
-tm.tcl -
-tm.tcl.in {
if {0x30 & $::teaish__Config(pkgindex-policy)} {
@@ -2517,7 +2538,7 @@ proc teaish__install {{dDest ""}} {
] {
teaish__verbose 1 msg-result "Copying files to $destDir..."
file mkdir $destDir
foreach f [glob -directory $srcDir *] {
foreach f [glob -nocomplain -directory $srcDir *] {
if {[string match {*~} $f] || [string match "#*#" [file tail $f]]} {
# Editor-generated backups and emacs lock files
continue
+68 -3
View File
@@ -99,7 +99,7 @@ proc test__affert {failMode args} {
lassign $args script msg
}
incr ::test__Counters($what)
if {![uplevel 1 [concat expr [list $script]]]} {
if {![uplevel 1 expr [list $script]]} {
if {"" eq $msg} {
set msg $script
}
@@ -136,6 +136,40 @@ proc assert {args} {
tailcall test__affert 1 {*}$args
}
#
# @assert-matches ?-e? pattern ?-e? rhs ?msg?
#
# Equivalent to assert {[string match $pattern $rhs]} except that
# if either of those are prefixed with an -e flag, they are eval'd
# and their results are used.
#
proc assert-matches {args} {
set evalLhs 0
set evalRhs 0
if {"-e" eq [lindex $args 0]} {
incr evalLhs
set args [lassign $args -]
}
set args [lassign $args pattern]
if {"-e" eq [lindex $args 0]} {
incr evalRhs
set args [lassign $args -]
}
set args [lassign $args rhs msg]
if {$evalLhs} {
set pattern [uplevel 1 $pattern]
}
if {$evalRhs} {
set rhs [uplevel 1 $rhs]
}
#puts "***pattern=$pattern\n***rhs=$rhs"
tailcall test__affert 1 \
[join [list \[ string match [list $pattern] [list $rhs] \]]] $msg
# why does this not work? [list \[ string match [list $pattern] [list $rhs] \]] $msg
# "\[string match [list $pattern] [list $rhs]\]"
}
#
# @test-assert testId script ?msg?
#
@@ -157,7 +191,7 @@ proc test-expect {testId script result} {
puts "test $testId"
set x [string trim [uplevel 1 $script]]
set result [string trim $result]
tailcall test__affert 0 [list $x eq $result] \
tailcall test__affert 0 [list "{$x}" eq "{$result}"] \
"\nEXPECTED: <<$result>>\nGOT: <<$x>>"
}
@@ -169,7 +203,7 @@ proc test-expect {testId script result} {
#
proc test-catch {cmd args} {
if {[catch {
$cmd {*}$args
uplevel 1 $cmd {*}$args
} rc xopts]} {
puts "[test-current-scope] ignoring failure of: $cmd [lindex $args 0]: $rc"
return 1
@@ -177,6 +211,37 @@ proc test-catch {cmd args} {
return 0
}
#
# @test-catch-matching pattern (script|cmd args...)
#
# Works like test-catch, but it expects its argument(s) to to throw an
# error matching the given string (checked with [string match]). If
# they do not throw, or the error does not match $pattern, this
# function throws, else it returns 1.
#
# If there is no second argument, the $cmd is assumed to be a script,
# and will be eval'd in the caller's scope.
#
# TODO: add -glob and -regex flags to control matching flavor.
#
proc test-catch-matching {pattern cmd args} {
if {[catch {
#puts "**** catch-matching cmd=$cmd args=$args"
if {0 == [llength $args]} {
uplevel 1 $cmd {*}$args
} else {
$cmd {*}$args
}
} rc xopts]} {
if {[string match $pattern $rc]} {
return 1
} else {
error "[test-current-scope] exception does not match {$pattern}: {$rc}"
}
}
error "[test-current-scope] expecting to see an error matching {$pattern}"
}
if {![array exists ::teaish__BuildFlags]} {
array set ::teaish__BuildFlags {}
}
+1 -1
View File
@@ -213,7 +213,7 @@ typedef sqlite3_int64 i64; /* 8-byte signed integer */
** Macros needed to provide flexible arrays in a portable way
*/
#ifndef offsetof
# define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
# define offsetof(ST,M) ((size_t)((char*)&((ST*)0)->M - (char*)0))
#endif
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
# define FLEXARRAY
+1 -1
View File
@@ -80,7 +80,7 @@ typedef sqlite3_uint64 u64;
** Macros needed to provide flexible arrays in a portable way
*/
#ifndef offsetof
# define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
# define offsetof(ST,M) ((size_t)((char*)&((ST*)0)->M - (char*)0))
#endif
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
# define FLEXARRAY
+97 -51
View File
@@ -554,6 +554,36 @@ struct Fts5SegIter {
u8 bDel; /* True if the delete flag is set */
};
static int fts5IndexCorruptRowid(Fts5Index *pIdx, i64 iRowid){
pIdx->rc = FTS5_CORRUPT;
sqlite3Fts5ConfigErrmsg(pIdx->pConfig,
"fts5: corruption found reading blob %lld from table \"%s\"",
iRowid, pIdx->pConfig->zName
);
return SQLITE_CORRUPT_VTAB;
}
#define FTS5_CORRUPT_ROWID(pIdx, iRowid) fts5IndexCorruptRowid(pIdx, iRowid)
static int fts5IndexCorruptIter(Fts5Index *pIdx, Fts5SegIter *pIter){
pIdx->rc = FTS5_CORRUPT;
sqlite3Fts5ConfigErrmsg(pIdx->pConfig,
"fts5: corruption on page %d, segment %d, table \"%s\"",
pIter->iLeafPgno, pIter->pSeg->iSegid, pIdx->pConfig->zName
);
return SQLITE_CORRUPT_VTAB;
}
#define FTS5_CORRUPT_ITER(pIdx, pIter) fts5IndexCorruptIter(pIdx, pIter)
static int fts5IndexCorruptIdx(Fts5Index *pIdx){
pIdx->rc = FTS5_CORRUPT;
sqlite3Fts5ConfigErrmsg(pIdx->pConfig,
"fts5: corruption in table \"%s\"", pIdx->pConfig->zName
);
return SQLITE_CORRUPT_VTAB;
}
#define FTS5_CORRUPT_IDX(pIdx) fts5IndexCorruptIdx(pIdx)
/*
** Array of tombstone pages. Reference counted.
*/
@@ -843,7 +873,7 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){
** All the reasons those functions might return SQLITE_ERROR - missing
** table, missing row, non-blob/text in block column - indicate
** backing store corruption. */
if( rc==SQLITE_ERROR ) rc = FTS5_CORRUPT;
if( rc==SQLITE_ERROR ) rc = FTS5_CORRUPT_ROWID(p, iRowid);
if( rc==SQLITE_OK ){
u8 *aOut = 0; /* Read blob data into this buffer */
@@ -893,7 +923,7 @@ static Fts5Data *fts5LeafRead(Fts5Index *p, i64 iRowid){
Fts5Data *pRet = fts5DataRead(p, iRowid);
if( pRet ){
if( pRet->nn<4 || pRet->szLeaf>pRet->nn ){
p->rc = FTS5_CORRUPT;
FTS5_CORRUPT_ROWID(p, iRowid);
fts5DataRelease(pRet);
pRet = 0;
}
@@ -1252,8 +1282,14 @@ static Fts5Structure *fts5StructureReadUncached(Fts5Index *p){
/* TODO: Do we need this if the leaf-index is appended? Probably... */
memset(&pData->p[pData->nn], 0, FTS5_DATA_PADDING);
p->rc = fts5StructureDecode(pData->p, pData->nn, &iCookie, &pRet);
if( p->rc==SQLITE_OK && (pConfig->pgsz==0 || pConfig->iCookie!=iCookie) ){
p->rc = sqlite3Fts5ConfigLoad(pConfig, iCookie);
if( p->rc==SQLITE_OK ){
if( (pConfig->pgsz==0 || pConfig->iCookie!=iCookie) ){
p->rc = sqlite3Fts5ConfigLoad(pConfig, iCookie);
}
}else if( p->rc==SQLITE_CORRUPT_VTAB ){
sqlite3Fts5ConfigErrmsg(p->pConfig,
"fts5: corrupt structure record for table \"%s\"", p->pConfig->zName
);
}
fts5DataRelease(pData);
if( p->rc!=SQLITE_OK ){
@@ -1876,7 +1912,7 @@ static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){
while( iOff>=pIter->pLeaf->szLeaf ){
fts5SegIterNextPage(p, pIter);
if( pIter->pLeaf==0 ){
if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT;
if( p->rc==SQLITE_OK ) FTS5_CORRUPT_ITER(p, pIter);
return;
}
iOff = 4;
@@ -1908,7 +1944,7 @@ static void fts5SegIterLoadTerm(Fts5Index *p, Fts5SegIter *pIter, int nKeep){
iOff += fts5GetVarint32(&a[iOff], nNew);
if( iOff+nNew>pIter->pLeaf->szLeaf || nKeep>pIter->term.n || nNew==0 ){
p->rc = FTS5_CORRUPT;
FTS5_CORRUPT_ITER(p, pIter);
return;
}
pIter->term.n = nKeep;
@@ -1950,9 +1986,9 @@ static void fts5SegIterSetNext(Fts5Index *p, Fts5SegIter *pIter){
** leave an error in the Fts5Index object.
*/
static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){
const i64 nTomb = (i64)pIter->pSeg->nPgTombstone;
const int nTomb = pIter->pSeg->nPgTombstone;
if( nTomb>0 ){
i64 nByte = SZ_FTS5TOMBSTONEARRAY(nTomb+1);
int nByte = SZ_FTS5TOMBSTONEARRAY(nTomb+1);
Fts5TombstoneArray *pNew;
pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte);
if( pNew ){
@@ -2103,7 +2139,7 @@ static void fts5SegIterReverseNewPage(Fts5Index *p, Fts5SegIter *pIter){
iRowidOff = fts5LeafFirstRowidOff(pNew);
if( iRowidOff ){
if( iRowidOff>=pNew->szLeaf ){
p->rc = FTS5_CORRUPT;
FTS5_CORRUPT_ITER(p, pIter);
}else{
pIter->pLeaf = pNew;
pIter->iLeafOffset = iRowidOff;
@@ -2337,7 +2373,7 @@ static void fts5SegIterNext(
}
assert_nc( iOff<pLeaf->szLeaf );
if( iOff>pLeaf->szLeaf ){
p->rc = FTS5_CORRUPT;
FTS5_CORRUPT_ITER(p, pIter);
return;
}
}
@@ -2445,18 +2481,20 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){
fts5DataRelease(pIter->pLeaf);
pIter->pLeaf = pLast;
pIter->iLeafPgno = pgnoLast;
iOff = fts5LeafFirstRowidOff(pLast);
if( iOff>pLast->szLeaf ){
p->rc = FTS5_CORRUPT;
return;
}
iOff += fts5GetVarint(&pLast->p[iOff], (u64*)&pIter->iRowid);
pIter->iLeafOffset = iOff;
if( p->rc==SQLITE_OK ){
iOff = fts5LeafFirstRowidOff(pLast);
if( iOff>pLast->szLeaf ){
FTS5_CORRUPT_ITER(p, pIter);
return;
}
iOff += fts5GetVarint(&pLast->p[iOff], (u64*)&pIter->iRowid);
pIter->iLeafOffset = iOff;
if( fts5LeafIsTermless(pLast) ){
pIter->iEndofDoclist = pLast->nn+1;
}else{
pIter->iEndofDoclist = fts5LeafFirstTermOff(pLast);
if( fts5LeafIsTermless(pLast) ){
pIter->iEndofDoclist = pLast->nn+1;
}else{
pIter->iEndofDoclist = fts5LeafFirstTermOff(pLast);
}
}
}
@@ -2526,7 +2564,7 @@ static void fts5LeafSeek(
iPgidx += fts5GetVarint32(&a[iPgidx], iTermOff);
iOff = iTermOff;
if( iOff>n ){
p->rc = FTS5_CORRUPT;
FTS5_CORRUPT_ITER(p, pIter);
return;
}
@@ -2569,7 +2607,7 @@ static void fts5LeafSeek(
iOff = iTermOff;
if( iOff>=n ){
p->rc = FTS5_CORRUPT;
FTS5_CORRUPT_ITER(p, pIter);
return;
}
@@ -2591,7 +2629,7 @@ static void fts5LeafSeek(
iPgidx = (u32)pIter->pLeaf->szLeaf;
iPgidx += fts5GetVarint32(&pIter->pLeaf->p[iPgidx], iOff);
if( iOff<4 || (i64)iOff>=pIter->pLeaf->szLeaf ){
p->rc = FTS5_CORRUPT;
FTS5_CORRUPT_ITER(p, pIter);
return;
}else{
nKeep = 0;
@@ -2606,7 +2644,7 @@ static void fts5LeafSeek(
search_success:
if( (i64)iOff+nNew>n || nNew<1 ){
p->rc = FTS5_CORRUPT;
FTS5_CORRUPT_ITER(p, pIter);
return;
}
pIter->iLeafOffset = iOff + nNew;
@@ -3071,7 +3109,7 @@ static void fts5SegIterGotoPage(
assert( iLeafPgno>pIter->iLeafPgno );
if( iLeafPgno>pIter->pSeg->pgnoLast ){
p->rc = FTS5_CORRUPT;
FTS5_CORRUPT_IDX(p);
}else{
fts5DataRelease(pIter->pNextLeaf);
pIter->pNextLeaf = 0;
@@ -3086,7 +3124,7 @@ static void fts5SegIterGotoPage(
u8 *a = pIter->pLeaf->p;
int n = pIter->pLeaf->szLeaf;
if( iOff<4 || iOff>=n ){
p->rc = FTS5_CORRUPT;
FTS5_CORRUPT_IDX(p);
}else{
iOff += fts5GetVarint(&a[iOff], (u64*)&pIter->iRowid);
pIter->iLeafOffset = iOff;
@@ -3565,7 +3603,7 @@ static void fts5ChunkIterate(
if( nRem<=0 ){
break;
}else if( pSeg->pSeg==0 ){
p->rc = FTS5_CORRUPT;
FTS5_CORRUPT_IDX(p);
return;
}else{
pgno++;
@@ -4668,7 +4706,7 @@ static void fts5TrimSegments(Fts5Index *p, Fts5Iter *pIter){
** a single page has been assigned to more than one segment. In
** this case a prior iteration of this loop may have corrupted the
** segment currently being trimmed. */
p->rc = FTS5_CORRUPT;
FTS5_CORRUPT_ROWID(p, iLeafRowid);
}else{
fts5BufferZero(&buf);
fts5BufferGrow(&p->rc, &buf, pData->nn);
@@ -5135,7 +5173,7 @@ static void fts5SecureDeleteOverflow(
}else if( bDetailNone ){
break;
}else if( iNext>=pLeaf->szLeaf || pLeaf->nn<pLeaf->szLeaf || iNext<4 ){
p->rc = FTS5_CORRUPT;
FTS5_CORRUPT_ROWID(p, iRowid);
break;
}else{
int nShift = iNext - 4;
@@ -5155,7 +5193,7 @@ static void fts5SecureDeleteOverflow(
i1 += fts5GetVarint32(&aPg[i1], iFirst);
if( iFirst<iNext ){
p->rc = FTS5_CORRUPT;
FTS5_CORRUPT_ROWID(p, iRowid);
break;
}
aIdx = sqlite3Fts5MallocZero(&p->rc, (pLeaf->nn-pLeaf->szLeaf)+2);
@@ -5378,14 +5416,14 @@ static void fts5DoSecureDelete(
nSuffix = (nPrefix2 + nSuffix2) - nPrefix;
if( (iKeyOff+nSuffix)>iPgIdx || (iNextOff+nSuffix2)>iPgIdx ){
p->rc = FTS5_CORRUPT;
FTS5_CORRUPT_IDX(p);
}else{
if( iKey!=1 ){
iOff += sqlite3Fts5PutVarint(&aPg[iOff], nPrefix);
}
iOff += sqlite3Fts5PutVarint(&aPg[iOff], nSuffix);
if( nPrefix2>pSeg->term.n ){
p->rc = FTS5_CORRUPT;
FTS5_CORRUPT_IDX(p);
}else if( nPrefix2>nPrefix ){
memcpy(&aPg[iOff], &pSeg->term.p[nPrefix], nPrefix2-nPrefix);
iOff += (nPrefix2-nPrefix);
@@ -5809,7 +5847,7 @@ static Fts5Structure *fts5IndexOptimizeStruct(
}
nByte += (((i64)pStruct->nLevel)+1) * sizeof(Fts5StructureLevel);
assert( nByte==SZ_FTS5STRUCTURE(pStruct->nLevel+2) );
assert( nByte==(i64)SZ_FTS5STRUCTURE(pStruct->nLevel+2) );
pNew = (Fts5Structure*)sqlite3Fts5MallocZero(&p->rc, nByte);
if( pNew ){
@@ -6178,7 +6216,7 @@ static void fts5MergePrefixLists(
}
if( pHead==0 || pHead->pNext==0 ){
p->rc = FTS5_CORRUPT;
FTS5_CORRUPT_IDX(p);
break;
}
@@ -6215,7 +6253,7 @@ static void fts5MergePrefixLists(
assert_nc( tmp.n+nTail<=nTmp );
assert( tmp.n+nTail<=nTmp+nMerge*10 );
if( tmp.n+nTail>nTmp-FTS5_DATA_ZERO_PADDING ){
if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT;
if( p->rc==SQLITE_OK ) FTS5_CORRUPT_IDX(p);
break;
}
fts5BufferSafeAppendVarint(&out, (tmp.n+nTail) * 2);
@@ -8344,14 +8382,17 @@ static void fts5IndexIntegrityCheckEmpty(
for(i=iFirst; p->rc==SQLITE_OK && i<=iLast; i++){
Fts5Data *pLeaf = fts5DataRead(p, FTS5_SEGMENT_ROWID(pSeg->iSegid, i));
if( pLeaf ){
if( !fts5LeafIsTermless(pLeaf) ) p->rc = FTS5_CORRUPT;
if( i>=iNoRowid && 0!=fts5LeafFirstRowidOff(pLeaf) ) p->rc = FTS5_CORRUPT;
if( !fts5LeafIsTermless(pLeaf)
|| (i>=iNoRowid && 0!=fts5LeafFirstRowidOff(pLeaf))
){
FTS5_CORRUPT_ROWID(p, FTS5_SEGMENT_ROWID(pSeg->iSegid, i));
}
}
fts5DataRelease(pLeaf);
}
}
static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){
static void fts5IntegrityCheckPgidx(Fts5Index *p, i64 iRowid, Fts5Data *pLeaf){
i64 iTermOff = 0;
int ii;
@@ -8369,12 +8410,12 @@ static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){
iOff = iTermOff;
if( iOff>=pLeaf->szLeaf ){
p->rc = FTS5_CORRUPT;
FTS5_CORRUPT_ROWID(p, iRowid);
}else if( iTermOff==nIncr ){
int nByte;
iOff += fts5GetVarint32(&pLeaf->p[iOff], nByte);
if( (iOff+nByte)>pLeaf->szLeaf ){
p->rc = FTS5_CORRUPT;
FTS5_CORRUPT_ROWID(p, iRowid);
}else{
fts5BufferSet(&p->rc, &buf1, nByte, &pLeaf->p[iOff]);
}
@@ -8383,7 +8424,7 @@ static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){
iOff += fts5GetVarint32(&pLeaf->p[iOff], nKeep);
iOff += fts5GetVarint32(&pLeaf->p[iOff], nByte);
if( nKeep>buf1.n || (iOff+nByte)>pLeaf->szLeaf ){
p->rc = FTS5_CORRUPT;
FTS5_CORRUPT_ROWID(p, iRowid);
}else{
buf1.n = nKeep;
fts5BufferAppendBlob(&p->rc, &buf1, nByte, &pLeaf->p[iOff]);
@@ -8391,7 +8432,7 @@ static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){
if( p->rc==SQLITE_OK ){
res = fts5BufferCompare(&buf1, &buf2);
if( res<=0 ) p->rc = FTS5_CORRUPT;
if( res<=0 ) FTS5_CORRUPT_ROWID(p, iRowid);
}
}
fts5BufferSet(&p->rc, &buf2, buf1.n, buf1.p);
@@ -8452,7 +8493,7 @@ static void fts5IndexIntegrityCheckSegment(
** entry even if all the terms are removed from it by secure-delete
** operations. */
}else{
p->rc = FTS5_CORRUPT;
FTS5_CORRUPT_ROWID(p, iRow);
}
}else{
@@ -8464,15 +8505,15 @@ static void fts5IndexIntegrityCheckSegment(
iOff = fts5LeafFirstTermOff(pLeaf);
iRowidOff = fts5LeafFirstRowidOff(pLeaf);
if( iRowidOff>=iOff || iOff>=pLeaf->szLeaf ){
p->rc = FTS5_CORRUPT;
FTS5_CORRUPT_ROWID(p, iRow);
}else{
iOff += fts5GetVarint32(&pLeaf->p[iOff], nTerm);
res = fts5Memcmp(&pLeaf->p[iOff], zIdxTerm, MIN(nTerm, nIdxTerm));
if( res==0 ) res = nTerm - nIdxTerm;
if( res<0 ) p->rc = FTS5_CORRUPT;
if( res<0 ) FTS5_CORRUPT_ROWID(p, iRow);
}
fts5IntegrityCheckPgidx(p, pLeaf);
fts5IntegrityCheckPgidx(p, iRow, pLeaf);
}
fts5DataRelease(pLeaf);
if( p->rc ) break;
@@ -8502,7 +8543,7 @@ static void fts5IndexIntegrityCheckSegment(
iKey = FTS5_SEGMENT_ROWID(iSegid, iPg);
pLeaf = fts5DataRead(p, iKey);
if( pLeaf ){
if( fts5LeafFirstRowidOff(pLeaf)!=0 ) p->rc = FTS5_CORRUPT;
if( fts5LeafFirstRowidOff(pLeaf)!=0 ) FTS5_CORRUPT_ROWID(p, iKey);
fts5DataRelease(pLeaf);
}
}
@@ -8517,12 +8558,12 @@ static void fts5IndexIntegrityCheckSegment(
int iRowidOff = fts5LeafFirstRowidOff(pLeaf);
ASSERT_SZLEAF_OK(pLeaf);
if( iRowidOff>=pLeaf->szLeaf ){
p->rc = FTS5_CORRUPT;
FTS5_CORRUPT_ROWID(p, iKey);
}else if( bSecureDelete==0 || iRowidOff>0 ){
i64 iDlRowid = fts5DlidxIterRowid(pDlidx);
fts5GetVarint(&pLeaf->p[iRowidOff], (u64*)&iRowid);
if( iRowid<iDlRowid || (bSecureDelete==0 && iRowid!=iDlRowid) ){
p->rc = FTS5_CORRUPT;
FTS5_CORRUPT_ROWID(p, iKey);
}
}
fts5DataRelease(pLeaf);
@@ -8637,7 +8678,12 @@ int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCksum){
fts5TestTerm(p, &term, 0, 0, cksum2, &cksum3);
fts5MultiIterFree(pIter);
if( p->rc==SQLITE_OK && bUseCksum && cksum!=cksum2 ) p->rc = FTS5_CORRUPT;
if( p->rc==SQLITE_OK && bUseCksum && cksum!=cksum2 ){
p->rc = FTS5_CORRUPT;
sqlite3Fts5ConfigErrmsg(p->pConfig,
"fts5: checksum mismatch for table \"%s\"", p->pConfig->zName
);
}
fts5StructureRelease(pStruct);
#ifdef SQLITE_DEBUG
+2 -1
View File
@@ -3701,8 +3701,9 @@ static int fts5IntegrityMethod(
" FTS5 table %s.%s: %s",
zSchema, zTabname, sqlite3_errstr(rc));
}
}else if( (rc&0xff)==SQLITE_CORRUPT ){
rc = SQLITE_OK;
}
sqlite3Fts5IndexCloseReader(pTab->p.pIndex);
pTab->p.pConfig->pzErrmsg = 0;
+1 -1
View File
@@ -428,7 +428,7 @@ do_execsql_test 15.1 {
}
do_catchsql_test 15.2 {
INSERT INTO t1(t1) VALUES('integrity-check');
} {1 {database disk image is malformed}}
} {1 {fts5: checksum mismatch for table "t1"}}
#-------------------------------------------------------------------------
#
+3 -4
View File
@@ -47,11 +47,10 @@ do_test 1.3 {
DELETE FROM t1_data WHERE rowid = fts5_rowid('segment', $segid, 4);
}
catchsql { INSERT INTO t1(t1) VALUES('integrity-check') }
} {1 {database disk image is malformed}}
} {1 {fts5: corruption found reading blob 137438953476 from table "t1"}}
do_execsql_test 1.3b {
PRAGMA integrity_check(t1);
} {{malformed inverted index for FTS5 table main.t1}}
} {{fts5: corruption found reading blob 137438953476 from table "t1"}}
do_test 1.4 {
db_restore_and_reopen
@@ -61,7 +60,7 @@ do_test 1.4 {
rowid = fts5_rowid('segment', $segid, 4);
}
catchsql { INSERT INTO t1(t1) VALUES('integrity-check') }
} {1 {database disk image is malformed}}
} {1 {fts5: corruption found reading blob 137438953476 from table "t1"}}
db_restore_and_reopen
#db eval {SELECT rowid, fts5_decode(rowid, block) aS r FROM t1_data} {puts $r}
+6 -6
View File
@@ -109,12 +109,12 @@ for {set i [expr $nbyte-2]} {$i>=0} {incr i -1} {
do_catchsql_test 2.$i.2 {
INSERT INTO t1(t1) VALUES('integrity-check');
} {1 {database disk image is malformed}}
} {/1.*fts5: corruption.*/}
do_test 2.$i.3 {
set res [catchsql {SELECT rowid FROM t1 WHERE t1 MATCH 'x*'}]
expr {
$res=="1 {database disk image is malformed}"
[string match {*fts5: corruption*} $res]
|| $res=="0 {$all}"
}
} 1
@@ -160,17 +160,17 @@ foreach {tn hdr} {
close $fd
set res [catchsql {SELECT rowid FROM x3 WHERE x3 MATCH 'x AND a'}]
if {$res == "1 {database disk image is malformed}"} {incr nCorrupt}
if {[string match {*fts5: corruption*} $res]} {incr nCorrupt}
set {} 1
} {1}
if {($tn2 % 10)==0 && $existing != $hdr} {
do_test 3.$tn.$tn2.2 {
catchsql { INSERT INTO x3(x3) VALUES('integrity-check') }
} {1 {database disk image is malformed}}
} {/.*fts5: corruption.*/}
do_execsql_test 3.$tn.$tn2.3 {
PRAGMA integrity_check(x3);
} {{malformed inverted index for FTS5 table main.x3}}
} {/.*fts5: corruption.*/}
}
execsql ROLLBACK
@@ -209,7 +209,7 @@ foreach {tn nCut} {
set res [catchsql {
SELECT rowid FROM x4 WHERE x4 MATCH 'a' ORDER BY 1 DESC
}]
if {$res == "1 {database disk image is malformed}"} {incr nCorrupt}
if {[string match {*fts5: corruption*} $res]} {incr nCorrupt}
set {} 1
} {1}
File diff suppressed because one or more lines are too long
+4 -4
View File
@@ -237,7 +237,7 @@ do_test 1.0 {
do_catchsql_test 1.1 {
SELECT * FROM t1('R*') WHERE (a,b)<=(current_date,0) ORDER BY rowid DESC;
} {1 {database disk image is malformed}}
} {/.*fts5: corrupt.*/}
#-------------------------------------------------------------------------
#
@@ -450,7 +450,7 @@ do_test 2.0 {
do_catchsql_test 2.1 {
SELECT * FROM t1('R*R*R*R*') WHERE (a,b)<=(current_date,0) ORDER BY rowid DESC;
} {1 {database disk image is malformed}}
} {/.*fts5: corrupt.*/}
#-------------------------------------------------------------------------
reset_db
@@ -569,7 +569,7 @@ do_test 3.0 {
do_catchsql_test 3.1 {
UPDATE t1 SET b=quote(zeroblob(200)) WHERE a MATCH 'thra*T';
} {1 {database disk image is malformed}}
} {/.*fts5: corrupt.*/}
#-------------------------------------------------------------------------
reset_db
@@ -878,7 +878,7 @@ do_execsql_test 5.1 {
}
do_catchsql_test 5.4 {
UPDATE t1 SET content=randomblob(500);
} {1 {database disk image is malformed}}
} {/.*fts5: corrupt.*/}
#-------------------------------------------------------------------------
reset_db
+1 -1
View File
@@ -123,6 +123,6 @@ do_execsql_test 2.2 {
do_catchsql_test 2.3 {
DELETE FROM t1 WHERE rowid = 1
} {1 {database disk image is malformed}}
} {/.*fts5: corrupt.*/}
finish_test
+1 -54
View File
@@ -32,7 +32,7 @@ sqlite3 db test.db
do_catchsql_test 1.2 {
SELECT * FROM t1
} {1 {database disk image is malformed}}
} {1 {fts5: corrupt structure record for table "t1"}}
do_catchsql_test 1.3 {
DROP TABLE t1
} {0 {}}
@@ -90,58 +90,5 @@ do_execsql_test 3.7 {
SELECT * FROM sqlite_schema
}
#-------------------------------------------------------------------------
reset_db
proc hex_to_blob {hex} {
binary encode hex $hex
}
db func hex_to_blob hex_to_blob
do_execsql_test 4.0 {
CREATE VIRTUAL TABLE x1 USING fts5(x, content='', contentless_delete=1);
BEGIN;
INSERT INTO x1(rowid, x) VALUES(1, 'a b c d e f g h');
INSERT INTO x1(rowid, x) VALUES(2, 'a b c d e f g h');
COMMIT;
DELETE FROM x1 WHERE rowid=1;
}
do_execsql_test 4.1 {
SELECT hex(block) FROM x1_data WHERE id=10
} {
00000000FF00000101010200010101010101010102
}
do_execsql_test 4.2.1 {
UPDATE x1_data SET block=
X'00000000FF00000101010200010101010101819C9B95A8000102'
WHERE id=10;
}
do_catchsql_test 4.2.2 {
SELECT * FROM x1('c d e');
} {1 {out of memory}}
do_execsql_test 4.3.1 {
UPDATE x1_data SET block=
X'00000000FF000001010102000101010101019282AFF9A0000102'
WHERE id=10;
}
do_catchsql_test 4.3.2 {
SELECT * FROM x1('c d e');
} {1 {out of memory}}
do_execsql_test 4.4.1 {
UPDATE x1_data SET block=
X'00000000FF000001010102000101010101018181808080130102'
WHERE id=10;
}
do_catchsql_test 4.3.2 {
SELECT * FROM x1('c d e');
} {1 {out of memory}}
finish_test
+1 -1
View File
@@ -46,7 +46,7 @@ do_execsql_test 1.5 {
do_catchsql_test 1.6 {
INSERT INTO f1(f1) VALUES('integrity-check');
} {1 {database disk image is malformed}}
} {/.*fts5: corrupt.*/}
do_execsql_test 1.7 {
INSERT INTO f1(f1) VALUES('rebuild');
@@ -120,7 +120,7 @@ public abstract class AggregateFunction<T> implements SQLFunction {
argument, the context is set to the given initial value. On all other
calls, the 2nd argument is ignored.
@see SQLFunction.PerContextState#getAggregateState
@see AggregateFunction.PerContextState#getAggregateState
*/
protected final ValueHolder<T> getAggregateState(sqlite3_context cx, T initialValue){
return map.getAggregateState(cx, initialValue);
@@ -130,7 +130,7 @@ public abstract class AggregateFunction<T> implements SQLFunction {
To be called from the implementation's xFinal() method to fetch
the final state of the UDF and remove its mapping.
see SQLFunction.PerContextState#takeAggregateState
see AggregateFunction.PerContextState#takeAggregateState
*/
protected final T takeAggregateState(sqlite3_context cx){
return map.takeAggregateState(cx);
+92 -40
View File
@@ -67,6 +67,7 @@
** data: For a regular file, a blob containing the file data. For a
** symlink, a text value containing the text of the link. For a
** directory, NULL.
** level: Directory hierarchy level. Topmost is 1.
**
** If a non-NULL value is specified for the optional $dir parameter and
** $path is a relative path, then $path is interpreted relative to $dir.
@@ -94,11 +95,8 @@ SQLITE_EXTENSION_INIT1
# include <sys/time.h>
# define STRUCT_STAT struct stat
#else
# include "windows.h"
# include <io.h>
# include "windirent.h"
# include <direct.h>
# include "test_windirent.h"
# define dirent DIRENT
# define STRUCT_STAT struct _stat
# define chmod(path,mode) fileio_chmod(path,mode)
# define mkdir(path,mode) fileio_mkdir(path)
@@ -117,14 +115,16 @@ SQLITE_EXTENSION_INIT1
/*
** Structure of the fsdir() table-valued function
*/
/* 0 1 2 3 4 5 */
#define FSDIR_SCHEMA "(name,mode,mtime,data,path HIDDEN,dir HIDDEN)"
/* 0 1 2 3 4 5 6 */
#define FSDIR_SCHEMA "(name,mode,mtime,data,level,path HIDDEN,dir HIDDEN)"
#define FSDIR_COLUMN_NAME 0 /* Name of the file */
#define FSDIR_COLUMN_MODE 1 /* Access mode */
#define FSDIR_COLUMN_MTIME 2 /* Last modification time */
#define FSDIR_COLUMN_DATA 3 /* File content */
#define FSDIR_COLUMN_PATH 4 /* Path to top of search */
#define FSDIR_COLUMN_DIR 5 /* Path is relative to this directory */
#define FSDIR_COLUMN_LEVEL 4 /* Level. Topmost is 1 */
#define FSDIR_COLUMN_PATH 5 /* Path to top of search */
#define FSDIR_COLUMN_DIR 6 /* Path is relative to this directory */
/*
** UTF8 chmod() function for Windows
@@ -621,6 +621,7 @@ struct fsdir_cursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
int nLvl; /* Number of entries in aLvl[] array */
int mxLvl; /* Maximum level */
int iLvl; /* Index of current entry */
FsdirLevel *aLvl; /* Hierarchy of directories being traversed */
@@ -739,7 +740,7 @@ static int fsdirNext(sqlite3_vtab_cursor *cur){
mode_t m = pCur->sStat.st_mode;
pCur->iRowid++;
if( S_ISDIR(m) ){
if( S_ISDIR(m) && pCur->iLvl+3<pCur->mxLvl ){
/* Descend into this directory */
int iNew = pCur->iLvl + 1;
FsdirLevel *pLvl;
@@ -847,7 +848,11 @@ static int fsdirColumn(
}else{
readFileContents(ctx, pCur->zPath);
}
break;
}
case FSDIR_COLUMN_LEVEL:
sqlite3_result_int(ctx, pCur->iLvl+2);
break;
case FSDIR_COLUMN_PATH:
default: {
/* The FSDIR_COLUMN_PATH and FSDIR_COLUMN_DIR are input parameters.
@@ -881,8 +886,11 @@ static int fsdirEof(sqlite3_vtab_cursor *cur){
/*
** xFilter callback.
**
** idxNum==1 PATH parameter only
** idxNum==2 Both PATH and DIR supplied
** idxNum bit Meaning
** 0x01 PATH=N
** 0x02 DIR=N
** 0x04 LEVEL<N
** 0x08 LEVEL<=N
*/
static int fsdirFilter(
sqlite3_vtab_cursor *cur,
@@ -891,6 +899,7 @@ static int fsdirFilter(
){
const char *zDir = 0;
fsdir_cursor *pCur = (fsdir_cursor*)cur;
int i;
(void)idxStr;
fsdirResetCursor(pCur);
@@ -899,14 +908,24 @@ static int fsdirFilter(
return SQLITE_ERROR;
}
assert( argc==idxNum && (argc==1 || argc==2) );
assert( (idxNum & 0x01)!=0 && argc>0 );
zDir = (const char*)sqlite3_value_text(argv[0]);
if( zDir==0 ){
fsdirSetErrmsg(pCur, "table function fsdir requires a non-NULL argument");
return SQLITE_ERROR;
}
if( argc==2 ){
pCur->zBase = (const char*)sqlite3_value_text(argv[1]);
i = 1;
if( (idxNum & 0x02)!=0 ){
assert( argc>i );
pCur->zBase = (const char*)sqlite3_value_text(argv[i++]);
}
if( (idxNum & 0x0c)!=0 ){
assert( argc>i );
pCur->mxLvl = sqlite3_value_int(argv[i++]);
if( idxNum & 0x08 ) pCur->mxLvl++;
if( pCur->mxLvl<=0 ) pCur->mxLvl = 1000000000;
}else{
pCur->mxLvl = 1000000000;
}
if( pCur->zBase ){
pCur->nBase = (int)strlen(pCur->zBase)+1;
@@ -935,10 +954,11 @@ static int fsdirFilter(
** In this implementation idxNum is used to represent the
** query plan. idxStr is unused.
**
** The query plan is represented by values of idxNum:
** The query plan is represented by bits in idxNum:
**
** (1) The path value is supplied by argv[0]
** (2) Path is in argv[0] and dir is in argv[1]
** 0x01 The path value is supplied by argv[0]
** 0x02 dir is in argv[1]
** 0x04 maxdepth is in argv[1] or [2]
*/
static int fsdirBestIndex(
sqlite3_vtab *tab,
@@ -947,6 +967,9 @@ static int fsdirBestIndex(
int i; /* Loop over constraints */
int idxPath = -1; /* Index in pIdxInfo->aConstraint of PATH= */
int idxDir = -1; /* Index in pIdxInfo->aConstraint of DIR= */
int idxLevel = -1; /* Index in pIdxInfo->aConstraint of LEVEL< or <= */
int idxLevelEQ = 0; /* 0x08 for LEVEL<= or LEVEL=. 0x04 for LEVEL< */
int omitLevel = 0; /* omit the LEVEL constraint */
int seenPath = 0; /* True if an unusable PATH= constraint is seen */
int seenDir = 0; /* True if an unusable DIR= constraint is seen */
const struct sqlite3_index_constraint *pConstraint;
@@ -954,25 +977,48 @@ static int fsdirBestIndex(
(void)tab;
pConstraint = pIdxInfo->aConstraint;
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
switch( pConstraint->iColumn ){
case FSDIR_COLUMN_PATH: {
if( pConstraint->usable ){
idxPath = i;
seenPath = 0;
}else if( idxPath<0 ){
seenPath = 1;
if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
switch( pConstraint->iColumn ){
case FSDIR_COLUMN_PATH: {
if( pConstraint->usable ){
idxPath = i;
seenPath = 0;
}else if( idxPath<0 ){
seenPath = 1;
}
break;
}
case FSDIR_COLUMN_DIR: {
if( pConstraint->usable ){
idxDir = i;
seenDir = 0;
}else if( idxDir<0 ){
seenDir = 1;
}
break;
}
case FSDIR_COLUMN_LEVEL: {
if( pConstraint->usable && idxLevel<0 ){
idxLevel = i;
idxLevelEQ = 0x08;
omitLevel = 0;
}
break;
}
break;
}
case FSDIR_COLUMN_DIR: {
if( pConstraint->usable ){
idxDir = i;
seenDir = 0;
}else if( idxDir<0 ){
seenDir = 1;
}
break;
}else
if( pConstraint->iColumn==FSDIR_COLUMN_LEVEL
&& pConstraint->usable
&& idxLevel<0
){
if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE ){
idxLevel = i;
idxLevelEQ = 0x08;
omitLevel = 1;
}else if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ){
idxLevel = i;
idxLevelEQ = 0x04;
omitLevel = 1;
}
}
}
@@ -989,14 +1035,20 @@ static int fsdirBestIndex(
}else{
pIdxInfo->aConstraintUsage[idxPath].omit = 1;
pIdxInfo->aConstraintUsage[idxPath].argvIndex = 1;
pIdxInfo->idxNum = 0x01;
pIdxInfo->estimatedCost = 1.0e9;
i = 2;
if( idxDir>=0 ){
pIdxInfo->aConstraintUsage[idxDir].omit = 1;
pIdxInfo->aConstraintUsage[idxDir].argvIndex = 2;
pIdxInfo->idxNum = 2;
pIdxInfo->estimatedCost = 10.0;
}else{
pIdxInfo->idxNum = 1;
pIdxInfo->estimatedCost = 100.0;
pIdxInfo->aConstraintUsage[idxDir].argvIndex = i++;
pIdxInfo->idxNum |= 0x02;
pIdxInfo->estimatedCost /= 1.0e4;
}
if( idxLevel>=0 ){
pIdxInfo->aConstraintUsage[idxLevel].omit = omitLevel;
pIdxInfo->aConstraintUsage[idxLevel].argvIndex = i++;
pIdxInfo->idxNum |= idxLevelEQ;
pIdxInfo->estimatedCost /= 1.0e4;
}
}
+45 -3
View File
@@ -435,6 +435,39 @@ static int vtablogFilter(
return SQLITE_OK;
}
/*
** Return an sqlite3_index_info operator name in static space.
** The name is possibly overwritten on subsequent calls.
*/
static char *vtablogOpName(unsigned char op){
static char zUnknown[30];
char *zOut;
switch( op ){
case SQLITE_INDEX_CONSTRAINT_EQ: zOut = "EQ"; break;
case SQLITE_INDEX_CONSTRAINT_GT: zOut = "GT"; break;
case SQLITE_INDEX_CONSTRAINT_LE: zOut = "LE"; break;
case SQLITE_INDEX_CONSTRAINT_LT: zOut = "LT"; break;
case SQLITE_INDEX_CONSTRAINT_GE: zOut = "GE"; break;
case SQLITE_INDEX_CONSTRAINT_MATCH: zOut = "MATCH"; break;
case SQLITE_INDEX_CONSTRAINT_LIKE: zOut = "LIKE"; break;
case SQLITE_INDEX_CONSTRAINT_GLOB: zOut = "GLOB"; break;
case SQLITE_INDEX_CONSTRAINT_REGEXP: zOut = "REGEXP"; break;
case SQLITE_INDEX_CONSTRAINT_NE: zOut = "NE"; break;
case SQLITE_INDEX_CONSTRAINT_ISNOT: zOut = "ISNOT"; break;
case SQLITE_INDEX_CONSTRAINT_ISNOTNULL: zOut = "ISNOTNULL"; break;
case SQLITE_INDEX_CONSTRAINT_ISNULL: zOut = "ISNULL"; break;
case SQLITE_INDEX_CONSTRAINT_IS: zOut = "IS"; break;
case SQLITE_INDEX_CONSTRAINT_LIMIT: zOut = "LIMIT"; break;
case SQLITE_INDEX_CONSTRAINT_OFFSET: zOut = "OFFSET"; break;
case SQLITE_INDEX_CONSTRAINT_FUNCTION: zOut = "FUNCTION"; break;
default:
sqlite3_snprintf(sizeof(zUnknown),zUnknown,"%d",op);
zOut = zUnknown;
break;
}
return zOut;
}
/*
** SQLite will invoke this method one or more times while planning a query
** that uses the vtablog virtual table. This routine needs to create
@@ -451,14 +484,23 @@ static int vtablogBestIndex(
printf(" colUsed: 0x%016llx\n", p->colUsed);
printf(" nConstraint: %d\n", p->nConstraint);
for(i=0; i<p->nConstraint; i++){
sqlite3_value *pVal = 0;
int rc = sqlite3_vtab_rhs_value(p, i, &pVal);
printf(
" constraint[%d]: col=%d termid=%d op=%d usabled=%d collseq=%s\n",
" constraint[%d]: col=%d termid=%d op=%s usabled=%d coll=%s rhs=",
i,
p->aConstraint[i].iColumn,
p->aConstraint[i].iTermOffset,
p->aConstraint[i].op,
vtablogOpName(p->aConstraint[i].op),
p->aConstraint[i].usable,
sqlite3_vtab_collation(p,i));
sqlite3_vtab_collation(p,i)
);
if( rc==SQLITE_OK ){
vtablogQuote(pVal);
printf("\n");
}else{
printf("N/A\n");
}
}
printf(" nOrderBy: %d\n", p->nOrderBy);
for(i=0; i<p->nOrderBy; i++){
+163
View File
@@ -0,0 +1,163 @@
/*
** 2025-06-05
**
** 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.
**
*************************************************************************
**
** An implementation of opendir(), readdir(), and closedir() for Windows,
** based on the FindFirstFile(), FindNextFile(), and FindClose() APIs
** of Win32.
**
** #include this file inside any C-code module that needs to use
** opendir()/readdir()/closedir(). This file is a no-op on non-Windows
** machines. On Windows, static functions are defined that implement
** those standard interfaces.
*/
#if defined(_WIN32) && defined(_MSC_VER) && !defined(SQLITE_WINDIRENT_H)
#define SQLITE_WINDIRENT_H
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <io.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#ifndef FILENAME_MAX
# define FILENAME_MAX (260)
#endif
#ifndef S_ISREG
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#endif
#ifndef S_ISDIR
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
#ifndef S_ISLNK
#define S_ISLNK(m) (0)
#endif
typedef unsigned short mode_t;
/* The dirent object for Windows is abbreviated. The only field really
** usable by applications is d_name[].
*/
struct dirent {
int d_ino; /* Inode number (synthesized) */
unsigned d_attributes; /* File attributes */
char d_name[FILENAME_MAX]; /* Null-terminated filename */
};
/* The internals of DIR are opaque according to standards. So it
** does not matter what we put here. */
typedef struct DIR DIR;
struct DIR {
intptr_t d_handle; /* Handle for findfirst()/findnext() */
struct dirent cur; /* Current entry */
};
/* Ignore hidden and system files */
#define WindowsFileToIgnore(a) \
((((a).attrib)&_A_HIDDEN) || (((a).attrib)&_A_SYSTEM))
/*
** Close a previously opened directory
*/
static int closedir(DIR *pDir){
int rc = 0;
if( pDir==0 ){
return EINVAL;
}
if( pDir->d_handle!=0 && pDir->d_handle!=(-1) ){
rc = _findclose(pDir->d_handle);
}
sqlite3_free(pDir);
return rc;
}
/*
** Open a new directory. The directory name should be UTF-8 encoded.
** appropriate translations happen automatically.
*/
static DIR *opendir(const char *zDirName){
DIR *pDir;
wchar_t *b1;
sqlite3_int64 sz;
struct _wfinddata_t data;
pDir = sqlite3_malloc64( sizeof(DIR) );
if( pDir==0 ) return 0;
memset(pDir, 0, sizeof(DIR));
memset(&data, 0, sizeof(data));
sz = strlen(zDirName);
b1 = sqlite3_malloc64( (sz+3)*sizeof(b1[0]) );
if( b1==0 ){
closedir(pDir);
return NULL;
}
sz = MultiByteToWideChar(CP_UTF8, 0, zDirName, sz, b1, sz);
b1[sz++] = '\\';
b1[sz++] = '*';
b1[sz] = 0;
if( sz+1>sizeof(data.name)/sizeof(data.name[0]) ){
closedir(pDir);
sqlite3_free(b1);
return NULL;
}
memcpy(data.name, b1, (sz+1)*sizeof(b1[0]));
sqlite3_free(b1);
pDir->d_handle = _wfindfirst(data.name, &data);
if( pDir->d_handle<0 ){
closedir(pDir);
return NULL;
}
while( WindowsFileToIgnore(data) ){
memset(&data, 0, sizeof(data));
if( _wfindnext(pDir->d_handle, &data)==-1 ){
closedir(pDir);
return NULL;
}
}
pDir->cur.d_ino = 0;
pDir->cur.d_attributes = data.attrib;
WideCharToMultiByte(CP_UTF8, 0, data.name, -1,
pDir->cur.d_name, FILENAME_MAX, 0, 0);
return pDir;
}
/*
** Read the next entry from a directory.
**
** The returned struct-dirent object is managed by DIR. It is only
** valid until the next readdir() or closedir() call. Only the
** d_name[] field is meaningful. The d_name[] value has been
** translated into UTF8.
*/
static struct dirent *readdir(DIR *pDir){
struct _wfinddata_t data;
if( pDir==0 ) return 0;
if( (pDir->cur.d_ino++)==0 ){
return &pDir->cur;
}
do{
memset(&data, 0, sizeof(data));
if( _wfindnext(pDir->d_handle, &data)==-1 ){
return NULL;
}
}while( WindowsFileToIgnore(data) );
pDir->cur.d_attributes = data.attrib;
WideCharToMultiByte(CP_UTF8, 0, data.name, -1,
pDir->cur.d_name, FILENAME_MAX, 0, 0);
return &pDir->cur;
}
#endif /* defined(_WIN32) && defined(_MSC_VER) */
+1 -1
View File
@@ -97,7 +97,7 @@ typedef unsigned int u32;
# define NEVER(X) (X)
#endif
#ifndef offsetof
#define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
# define offsetof(ST,M) ((size_t)((char*)&((ST*)0)->M - (char*)0))
#endif
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
# define FLEXARRAY
+7 -8
View File
@@ -260,7 +260,8 @@ SQLITE_OPT.full-featured := \
-DSQLITE_ENABLE_RTREE \
-DSQLITE_ENABLE_SESSION \
-DSQLITE_ENABLE_STMTVTAB \
-DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
-DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION \
-DSQLITE_ENABLE_COLUMN_METADATA
ifeq (0,$(wasm-bare-bones))
# The so-called canonical build is full-featured:
@@ -598,8 +599,6 @@ emcc.flags += -v
endif
# wasmMemory ==> required by our code for use with -sIMPORTED_MEMORY
# Emscripten 4.0.7 (2025-04-15) stops exporting HEAP* by default
########################################################################
# emcc flags for .c/.o.
emcc.cflags :=
@@ -619,6 +618,8 @@ emcc.jsflags += -sNO_POLYFILL
emcc.jsflags += -sEXPORTED_FUNCTIONS=@$(EXPORTED_FUNCTIONS.api)
emcc.exportedRuntimeMethods := \
-sEXPORTED_RUNTIME_METHODS=wasmMemory,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAP64,HEAPU64
# wasmMemory ==> required by our code for use with -sIMPORTED_MEMORY
# Emscripten 4.0.7 (2025-04-15) stops exporting HEAP* by default
emcc.jsflags += $(emcc.exportedRuntimeMethods)
emcc.jsflags += -sUSE_CLOSURE_COMPILER=0
emcc.jsflags += -sIMPORTED_MEMORY
@@ -1193,11 +1194,9 @@ push-testing:
########################################################################
# If we find a copy of the sqlite.org/wasm docs checked out, copy
# certain files over to it, noting that some need automatable edits...
wasm.docs.home ?= $(firstword $(wildcard ../../../wasm $(HOME)/f/s/wasm))
wasm.docs.found = $(if \
$(wildcard $(wasm.docs.home)/api-index.md),$(wildcard $(wasm.docs.home)),)
# ^^^ don't split between the args there, else it can introduce an extra
# space.
wasm.docs.home ?= ../../../wasm
wasm.docs.found = $(if $(wildcard $(wasm.docs.home)/api-index.md),\
$(wildcard $(wasm.docs.home)),)
.PHONY: update-docs
ifeq (,$(wasm.docs.found))
update-docs:
+17 -14
View File
@@ -1,12 +1,27 @@
_sqlite3_column_database_name
_sqlite3_column_origin_name
_sqlite3_column_table_name
_sqlite3_create_module
_sqlite3_create_module_v2
_sqlite3_create_window_function
_sqlite3_progress_handler
_sqlite3_set_authorizer
_sqlite3_declare_vtab
_sqlite3_drop_modules
_sqlite3_preupdate_blobwrite
_sqlite3_preupdate_count
_sqlite3_preupdate_depth
_sqlite3_preupdate_hook
_sqlite3_preupdate_new
_sqlite3_preupdate_old
_sqlite3_progress_handler
_sqlite3_set_authorizer
_sqlite3_vtab_collation
_sqlite3_vtab_distinct
_sqlite3_vtab_in
_sqlite3_vtab_in_first
_sqlite3_vtab_in_next
_sqlite3_vtab_nochange
_sqlite3_vtab_on_conflict
_sqlite3_vtab_rhs_value
_sqlite3changegroup_add
_sqlite3changegroup_add_strm
_sqlite3changegroup_delete
@@ -49,15 +64,3 @@ _sqlite3session_object_config
_sqlite3session_patchset
_sqlite3session_patchset_strm
_sqlite3session_table_filter
_sqlite3_create_module
_sqlite3_create_module_v2
_sqlite3_declare_vtab
_sqlite3_drop_modules
_sqlite3_vtab_collation
_sqlite3_vtab_distinct
_sqlite3_vtab_in
_sqlite3_vtab_in_first
_sqlite3_vtab_in_next
_sqlite3_vtab_nochange
_sqlite3_vtab_on_conflict
_sqlite3_vtab_rhs_value
+8 -1
View File
@@ -20,7 +20,6 @@
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
'use strict';
const toss = (...args)=>{throw new Error(args.join(' '))};
const toss3 = sqlite3.SQLite3Error.toss;
const capi = sqlite3.capi, wasm = sqlite3.wasm, util = sqlite3.util;
globalThis.WhWasmUtilInstaller(wasm);
delete globalThis.WhWasmUtilInstaller;
@@ -368,6 +367,14 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
);
}/* sqlite3_set_authorizer() */
if( !!wasm.exports.sqlite3_column_origin_name ){
wasm.bindingSignatures.push(
["sqlite3_column_database_name","string", "sqlite3_stmt*", "int"],
["sqlite3_column_origin_name","string", "sqlite3_stmt*", "int"],
["sqlite3_column_table_name","string", "sqlite3_stmt*", "int"]
);
}
if(false && wasm.compileOptionUsed('SQLITE_ENABLE_NORMALIZE')){
/* ^^^ "the problem" is that this is an optional feature and the
build-time function-export list does not currently take
+48 -23
View File
@@ -16,7 +16,6 @@
and it installs its deliverable as globalThis.sqlite3.oo1.
*/
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
const toss = (...args)=>{throw new Error(args.join(' '))};
const toss3 = (...args)=>{throw new sqlite3.SQLite3Error(...args)};
const capi = sqlite3.capi, wasm = sqlite3.wasm, util = sqlite3.util;
@@ -1061,18 +1060,18 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
const cbArgCache = Object.create(null)
/* 2nd arg for arg.cbArg, used by (at least) row-to-object
converter */;
for(; stmt.step(); stmt._lockedByExec = false){
for( ; stmt.step(); __execLock.delete(stmt) ){
if(0===gotColNames++){
stmt.getColumnNames(cbArgCache.columnNames = (opt.columnNames || []));
}
stmt._lockedByExec = true;
__execLock.add(stmt);
const row = arg.cbArg(stmt,cbArgCache);
if(resultRows) resultRows.push(row);
if(callback && false === callback.call(opt, row, stmt)){
break;
}
}
stmt._lockedByExec = false;
__execLock.delete(stmt);
}
if(0===gotColNames){
/* opt.columnNames was provided but we visited no result rows */
@@ -1094,7 +1093,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
}*/finally{
wasm.scopedAllocPop(stack);
if(stmt){
delete stmt._lockedByExec;
__execLock.delete(stmt);
stmt.finalize();
}
}
@@ -1388,7 +1387,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
/**
Starts a transaction, calls the given callback, and then either
rolls back or commits the savepoint, depending on whether the
rolls back or commits the transaction, depending on whether the
callback throws. The callback is passed this db object as its
only argument. On success, returns the result of the
callback. Throws on error.
@@ -1511,7 +1510,30 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
};
/**
If stmt._lockedByExec is truthy, this throws an exception
Each Stmt object which is "locked" by DB.exec() gets an entry
here to note that "lock".
The reason this is in place is because exec({callback:...})'s
callback gets access to the Stmt objects created internally by
exec() but it must not use certain Stmt APIs.
*/
const __execLock = new Set();
/**
This is a Stmt.get() counterpart of __execLock. Each time
Stmt.step() returns true, the statement is added to this set,
indicating that Stmt.get() is legal. Stmt APIs which invalidate
that status remove the Stmt object from this set, which will
cause Stmt.get() to throw with a descriptive error message
instead of a more generic "API misuse" if we were to allow that
call to reach the C API.
*/
const __stmtMayGet = new Set();
/**
Stmt APIs which are prohibited on locked objects must call
affirmNotLockedByExec() before doing any work.
If __execLock.has(stmt) is truthy, this throws an exception
complaining that the 2nd argument (an operation name,
e.g. "bind()") is not legal while the statement is "locked".
Locking happens before an exec()-like callback is passed a
@@ -1519,7 +1541,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
finalize the statement. If it does not throw, it returns stmt.
*/
const affirmNotLockedByExec = function(stmt,currentOpName){
if(stmt._lockedByExec){
if(__execLock.has(stmt)){
toss3("Operation is illegal when statement is locked:",currentOpName);
}
return stmt;
@@ -1604,7 +1626,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
toss3("Unsupported bind() argument type: "+(typeof val));
}
if(rc) DB.checkRc(stmt.db.pointer, rc);
stmt._mayGet = false;
return stmt;
};
@@ -1627,9 +1648,9 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
const rc = capi.sqlite3_finalize(this.pointer);
delete __stmtMap.get(this.db)[this.pointer];
__ptrMap.delete(this);
delete this._mayGet;
__execLock.delete(this);
__stmtMayGet.delete(this);
delete this.parameterCount;
delete this._lockedByExec;
delete this.db;
return rc;
}
@@ -1643,7 +1664,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
clearBindings: function(){
affirmNotLockedByExec(affirmStmtOpen(this), 'clearBindings()')
capi.sqlite3_clear_bindings(this.pointer);
this._mayGet = false;
__stmtMayGet.delete(this);
return this;
},
/**
@@ -1669,7 +1690,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
affirmNotLockedByExec(this,'reset()');
if(alsoClearBinds) this.clearBindings();
const rc = capi.sqlite3_reset(affirmStmtOpen(this).pointer);
this._mayGet = false;
__stmtMayGet.delete(this);
checkSqlite3Rc(this.db, rc);
return this;
},
@@ -1756,7 +1777,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
}else if(!this.parameterCount){
toss3("This statement has no bindable parameters.");
}
this._mayGet = false;
__stmtMayGet.delete(this);
if(null===arg){
/* bind NULL */
return bindOne(this, ndx, BindTypes.null, arg);
@@ -1821,14 +1842,18 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
affirmNotLockedByExec(this, 'step()');
const rc = capi.sqlite3_step(affirmStmtOpen(this).pointer);
switch(rc){
case capi.SQLITE_DONE: return this._mayGet = false;
case capi.SQLITE_ROW: return this._mayGet = true;
default:
this._mayGet = false;
sqlite3.config.warn("sqlite3_step() rc=",rc,
capi.sqlite3_js_rc_str(rc),
"SQL =", capi.sqlite3_sql(this.pointer));
DB.checkRc(this.db.pointer, rc);
case capi.SQLITE_DONE:
__stmtMayGet.delete(this);
return false;
case capi.SQLITE_ROW:
__stmtMayGet.add(this);
return true;
default:
__stmtMayGet.delete(this);
sqlite3.config.warn("sqlite3_step() rc=",rc,
capi.sqlite3_js_rc_str(rc),
"SQL =", capi.sqlite3_sql(this.pointer));
DB.checkRc(this.db.pointer, rc);
}
},
/**
@@ -1913,7 +1938,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
getJSON() can be used for that.
*/
get: function(ndx,asType){
if(!affirmStmtOpen(this)._mayGet){
if(!__stmtMayGet.has(affirmStmtOpen(this))){
toss3("Stmt.step() has not (recently) returned true.");
}
if(Array.isArray(ndx)){
+4 -1
View File
@@ -135,9 +135,12 @@
/*
** If SQLITE_WASM_BARE_BONES is defined, undefine most of the ENABLE
** macros.
** macros. This will, when using the canonical makefile, also elide
** any C functions from the WASM exports which are listed in
** ./EXPORT_FUNCTIONS.sqlite3-extras.
*/
#ifdef SQLITE_WASM_BARE_BONES
# undef SQLITE_ENABLE_COLUMN_METADATA
# undef SQLITE_ENABLE_DBPAGE_VTAB
# undef SQLITE_ENABLE_DBSTAT_VTAB
# undef SQLITE_ENABLE_EXPLAIN_COMMENTS
+1 -1
View File
@@ -279,7 +279,7 @@
opt.innerHTML = lbl;
opt.value = f;
if(preselectedFlags.indexOf(f) >= 0) opt.selected = true;
});
});
const cbReverseLog = E('#cb-reverse-log-order');
const lblReverseLog = E('#lbl-reverse-log-order');
if(cbReverseLog.checked){
+1 -1
View File
@@ -118,7 +118,7 @@
argv.push("--vfs", vfs);
log2('',"Using VFS:",vfs);
if('kvvfs' === vfs){
forceSize = 4 /* 5 uses approx. 4.96mb */;
forceSize = 2 /* 5 uses approx. 4.96mb */;
dbFile = 'session';
log2('warning',"kvvfs VFS: forcing --size",forceSize,
"and filename '"+dbFile+"'.");
+124 -58
View File
@@ -1263,7 +1263,6 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
capi.sqlite3_stmt_status(
st, capi.SQLITE_STMTSTATUS_RUN, 0
) === 0)
.assert(!st._mayGet)
.assert('a' === st.getColumnName(0))
.mustThrowMatching(()=>st.columnCount=2,
/columnCount property is read-only/)
@@ -1287,9 +1286,9 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
.assert(1===st.get(0,capi.SQLITE_BLOB).length)
.assert(st.getBlob(0) instanceof Uint8Array)
.assert('3'.charCodeAt(0) === st.getBlob(0)[0])
.assert(st._mayGet)
.assert(false===st.step())
.assert(!st._mayGet)
.mustThrowMatching(()=>st.get(0),
"Stmt.step() has not (recently) returned true.")
.assert(
capi.sqlite3_stmt_status(
st, capi.SQLITE_STMTSTATUS_RUN, 0
@@ -1297,11 +1296,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
T.assert(this.progressHandlerCount>0
|| wasm.compileOptionUsed('OMIT_PROGRESS_CALLBACK'),
"Expecting progress callback.").
assert(0===capi.sqlite3_strglob("*.txt", "foo.txt")).
assert(0!==capi.sqlite3_strglob("*.txt", "foo.xtx")).
assert(0===capi.sqlite3_strlike("%.txt", "foo.txt", 0)).
assert(0!==capi.sqlite3_strlike("%.txt", "foo.xtx", 0));
"Expecting progress callback.");
}finally{
rc = st.finalize();
}
@@ -1350,7 +1345,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
.assert(pVfsDb > 0)
.assert(pVfsMem !== pVfsDflt
/* memdb lives on top of the default vfs */)
.assert(pVfsDb === pVfsDflt || pVfsdb === pVfsMem)
.assert(pVfsDb === pVfsDflt || pVfsDb === pVfsMem)
;
/*const vMem = new capi.sqlite3_vfs(pVfsMem),
vDflt = new capi.sqlite3_vfs(pVfsDflt),
@@ -2193,7 +2188,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
"back into JS because of the lack of 64-bit integer support.");
}
}finally{
const x = w.scopedAlloc(1), y = w.scopedAlloc(1), z = w.scopedAlloc(1);
//const x = w.scopedAlloc(1), y = w.scopedAlloc(1), z = w.scopedAlloc(1);
//log("x=",x,"y=",y,"z=",z); // just looking at the alignment
w.scopedAllocPop(stack);
}
@@ -2673,50 +2668,70 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|| "Only available in main thread."),
test: function(sqlite3){
this.kvvfsUnlink();
let db;
const encOpt1 = 1
? {textkey: 'foo'}
: {key: 'foo'};
const encOpt2 = encOpt1.textkey
? encOpt1
: {hexkey: new Uint8Array([0x66,0x6f,0x6f]/*==>"foo"*/)}
try{
db = new this.JDb({
filename: this.kvvfsDbFile,
...encOpt1
});
db.exec([
"create table t(a,b);",
"insert into t(a,b) values(1,2),(3,4)"
]);
db.close();
let err;
try{
db = new this.JDb({
filename: this.kvvfsDbFile,
flags: 'ct'
let initDb = true;
const tryKey = function(keyKey, key, expectCount){
let db;
//console.debug('tryKey()',arguments);
const ctoropt = {
filename: this.kvvfsDbFile
//vfs: 'kvvfs'
//,flags: 'ct'
};
try {
if (initDb) {
initDb = false;
db = new this.JDb({
...ctoropt,
[keyKey]: key
});
db.exec([
"drop table if exists t;",
"create table t(a);"
]);
db.close();
// Ensure that it's actually encrypted...
let err;
try {
db = new this.JDb(ctoropt);
T.assert(db, 'db opened') /* opening is fine, but... */;
db.exec("select 1 from sqlite_schema");
console.warn("(should not be reached) sessionStorage =", sessionStorage);
} catch (e) {
err = e;
} finally {
db.close()
}
T.assert(err, "Expecting an exception")
.assert(sqlite3.capi.SQLITE_NOTADB == err.resultCode,
"Expecting NOTADB");
}/*initDb*/
//console.debug('tryKey()',arguments);
db = new sqlite3.oo1.DB({
...ctoropt,
vfs: 'kvvfs',
[keyKey]: key
});
T.assert(db) /* opening is fine, but... */;
db.exec("select 1 from sqlite_schema");
console.warn("sessionStorage =",sessionStorage);
}catch(e){
err = e;
}finally{
db.close();
db.exec("insert into t(a) values (1),(2)");
T.assert(expectCount === db.selectValue('select sum(a) from t'));
} finally {
if (db) db.close();
}
T.assert(err,"Expecting an exception")
.assert(sqlite3.capi.SQLITE_NOTADB==err.resultCode,
"Expecting NOTADB");
db = new sqlite3.oo1.DB({
filename: this.kvvfsDbFile,
vfs: 'kvvfs',
...encOpt2
});
T.assert( 4===db.selectValue('select sum(a) from t') );
}finally{
if( db ) db.close();
this.kvvfsUnlink();
}
}.bind(this);
const hexFoo = new Uint8Array([0x66,0x6f,0x6f]/*=="foo"*/);
tryKey('textkey', 'foo', 3);
T.assert( !initDb );
tryKey('textkey', 'foo', 6);
this.kvvfsUnlink();
initDb = true;
tryKey('key', 'foo', 3);
T.assert( !initDb );
tryKey('key', hexFoo, 6);
this.kvvfsUnlink();
initDb = true;
tryKey('hexkey', hexFoo, 3);
T.assert( !initDb );
tryKey('hexkey', hexFoo, 6);
this.kvvfsUnlink();
}
})/*kvvfs with SEE*/
//#endif enable-see
@@ -2836,6 +2851,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
},
9
);
T.assert( 0==rc );
db.transaction((d)=>{
d.exec([
"create table t(a);",
@@ -2849,8 +2865,10 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
.assert(2 === countHook[capi.SQLITE_UPDATE])
.assert(1 === countHook[capi.SQLITE_DELETE]);
//wasm.xWrap.FuncPtrAdapter.debugFuncInstall = true;
db.close();
T.assert( !!capi.sqlite3_preupdate_hook(db, 0, 0) );
//wasm.xWrap.FuncPtrAdapter.debugFuncInstall = false;
T.assert( !capi.sqlite3_preupdate_hook(db, 0, 0) );
db.close();
}
})/*pre-update hooks*/
;/*end hook API tests*/
@@ -3051,7 +3069,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
T.assert(6 === db.selectValue('select count(*) from p')).
assert( this.opfsImportSize == exp.byteLength );
db.close();
const unlink = this.opfsUnlink =
this.opfsUnlink =
(fn=filename)=>sqlite3.util.sqlite3__wasm_vfs_unlink("opfs",fn);
this.opfsUnlink(filename);
T.assert(!(await sqlite3.opfs.entryExists(filename)));
@@ -3302,7 +3320,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
.assert(true === await u3.removeVfs())
.assert(false === await P3b.removeVfs());
}
}/*OPFS SAH Pool sanity checks*/)
}/*OPFS SAH Pool sanity checks*/);
////////////////////////////////////////////////////////////////////////
T.g('Misc. APIs')
@@ -3323,6 +3341,44 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
db.close();
})
/**
Ensure that certain Stmt members throw when called
via DB.exec().
*/
.t('locked-by-exec() APIs', function(sqlite3){
const db = new sqlite3.oo1.DB();
db.exec("create table t(a);insert into t(a) values(1);");
let checkCount = 0;
const checkOp = function(op){
++checkCount;
T.mustThrowMatching(() => {
db.exec({
sql: "select ?1",
bind: op,
callback: (row, stmt) => {
switch (row[0]) {
case 'bind': stmt.bind(1); break;
case 'finalize':
case 'clearBindings':
case 'reset':
case 'step': stmt[op](); break;
}
}
});
}, /^Operation is illegal when statement is locked.*/)
};
try{
checkOp('bind');
checkOp('finalize');
checkOp('clearBindings');
checkOp('reset');
checkOp('step');
T.assert(5===checkCount);
}finally{
db.close();
}
})
////////////////////////////////////////////////////////////////////
.t("Misc. stmt_...", function(sqlite3){
const db = new sqlite3.oo1.DB();
@@ -3353,6 +3409,16 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
T.assert( 1===n )
.assert( 0===capi.sqlite3_stmt_busy(stmt) )
.assert( !stmt.isBusy() );
if( wasm.exports.sqlite3_column_origin_name ){
log("Column metadata APIs enabled");
T.assert( "t" === capi.sqlite3_column_table_name(stmt, 0))
.assert("a" === capi.sqlite3_column_origin_name(stmt, 0))
.assert("main" === capi.sqlite3_column_database_name(stmt, 0))
}else{
log("Column metadata APIs not enabled");
} // column metadata APIs
stmt.finalize();
db.close();
})
@@ -3364,7 +3430,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
capi.sqlite3_interrupt(db);
T.assert( 0!==capi.sqlite3_is_interrupted(db) );
db.close();
})
});
////////////////////////////////////////////////////////////////////////
T.g('Bug Reports')
@@ -3384,10 +3450,10 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
sql: "SELECT * FROM f order by path",
rowMode: 'array'
});
const dump = function(lbl){
/*const dump = function(lbl){
let rc = fetchEm();
log((lbl ? (lbl+' results') : ''),rc);
};
};*/
//dump('Full fts table');
let rc = fetchEm();
T.assert(3===rc.length);
+44 -14
View File
@@ -772,7 +772,6 @@ TESTSRC = \
$(TOP)/src/test_thread.c \
$(TOP)/src/test_vdbecov.c \
$(TOP)/src/test_vfs.c \
$(TOP)/src/test_windirent.c \
$(TOP)/src/test_window.c \
$(TOP)/src/test_wsd.c \
$(TOP)/ext/fts3/fts3_term.c \
@@ -1048,13 +1047,17 @@ $(T.tcl.env.sh): $(TCLSH_CMD) $(TCL_CONFIG_SH) $(MAKEFILE_LIST)
@if [ x = "x$(TCL_CONFIG_SH)" ]; then \
echo 'TCL_CONFIG_SH must be set to point to a "tclConfig.sh"' 1>&2; exit 1; \
fi; \
if [ x != "x$(TCLLIBDIR)" ]; then echo TCLLIBDIR="$(TCLLIBDIR)"; else \
if [ x != "x$(TCLLIBDIR)" ]; then \
echo "# generated by main.mk"; \
echo TCLLIBDIR="$(TCLLIBDIR)"; \
else \
ld= ; \
for d in `echo "puts stdout \\$$auto_path" | $(TCLSH_CMD)`; do \
if [ -d "$$d" ]; then ld=$$d; break; fi; \
done; \
if [ x = "x$$ld" ]; then echo "Cannot determine TCLLIBDIR" 1>&2; exit 1; fi; \
echo "TCLLIBDIR=$$ld/sqlite3"; \
echo "# generated by main.mk"; \
echo "TCLLIBDIR=$$ld/sqlite3$(PACKAGE_VERSION)"; \
fi > $@; \
echo ". \"$(TCL_CONFIG_SH)\" || exit \$$?" >> $@; \
echo "Created $@"
@@ -1623,15 +1626,29 @@ install-headers: sqlite3.h $(install-dir.include)
$(INSTALL.noexec) sqlite3.h "$(TOP)/src/sqlite3ext.h" "$(install-dir.include)"
install: install-headers
#
# If TCL_EXT_DLL_BASENAME is not set then guess the Tcl extension's
# DLL name depending on the Tcl version. This does not account for
# Cygwin's naming - the canonical build will usually set it, but
# static makefiles importing this one will need to account for that on
# their own. They can do that by setting libtclsqlite3.basename-[89]
# to appropriate names (cygsqlite resp. cygtcl9sqlite).
#
TCL_MAJOR_VERSION ?= 0
libtclsqlite3.basename-8 ?= libsqlite
libtclsqlite3.basename-9 ?= libtcl9sqlite
TCL_EXT_DLL_BASENAME ?= $(libtclsqlite3.basename-$(TCL_MAJOR_VERSION))
libtclsqlite3.DLL ?= $(TCL_EXT_DLL_BASENAME)$(PACKAGE_VERSION)$(T.dll)
#
# libtclsqlite3...
#
pkgIndex.tcl:
echo 'package ifneeded sqlite3 $(PACKAGE_VERSION) [list load [file join $$dir libtclsqlite3[info sharedlibextension]] Sqlite3]' > $@
pkgIndex.tcl: $(TOP)/main.mk
echo 'package ifneeded sqlite3 $(PACKAGE_VERSION) [list load [file join $$dir $(libtclsqlite3.DLL)] Sqlite3]' > $@
pkgIndex.tcl-1: pkgIndex.tcl
pkgIndex.tcl-0 pkgIndex.tcl-:
tcl: pkgIndex.tcl-$(HAVE_TCL)
libtclsqlite3.DLL = libtclsqlite3$(T.dll)
$(libtclsqlite3.DLL): $(T.tcl.env.sh) tclsqlite.o $(LIBOBJ)
$(T.tcl.env.source); \
$(T.link.shared) -o $@ tclsqlite.o \
@@ -1672,34 +1689,47 @@ CFLAGS.tclextension = $(CFLAGS.intree_includes) $(CFLAGS.env) $(OPT_FEATURE_FLAG
# by --with-tclsh=
#
tclextension: tclsqlite3.c
$(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --build-only --cc "$(T.cc)" $(CFLAGS.tclextension)
$(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --build-only \
--tclConfig.sh $(TCL_CONFIG_SH) --cc "$(T.cc)" $(CFLAGS.tclextension)
#
# Install the SQLite TCL extension in a way that is appropriate for $TCLSH_CMD
# to find it.
#
tclextension-install: tclsqlite3.c
$(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --destdir "$(DESTDIR)" --cc "$(T.cc)" $(CFLAGS.tclextension)
$(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --destdir "$(DESTDIR)" \
--tclConfig.sh $(TCL_CONFIG_SH) --cc "$(T.cc)" $(CFLAGS.tclextension)
#
# Uninstall the SQLite TCL extension that is used by $TCLSH_CMD.
#
tclextension-uninstall:
$(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --uninstall
$(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --uninstall \
--tclConfig.sh $(TCL_CONFIG_SH)
#
# List all installed the SQLite TCL extensions that is are accessible
# by $TCLSH_CMD, including prior versions.
#
tclextension-list:
@ $(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --info
@ $(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --info \
--tclConfig.sh $(TCL_CONFIG_SH)
# Verify that the SQLite TCL extension that is loaded by default
# in $(TCLSH_CMD) is the same as the version of SQLite for the
# current source tree
#
tclextension-verify: sqlite3.h
@ $(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --version-check
@ $(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --version-check \
--tclConfig.sh $(TCL_CONFIG_SH)
# Run all of the tclextension targets in order, ending with uninstall.
tclextension-all:
$(MAKE) tclextension
$(MAKE) tclextension-install
$(MAKE) tclextension-list
$(MAKE) tclextension-verify
$(MAKE) tclextension-uninstall
#
# FTS5 things
@@ -2330,12 +2360,12 @@ SHELL_DEP = \
$(TOP)/ext/misc/sqlar.c \
$(TOP)/ext/misc/uint.c \
$(TOP)/ext/misc/vfstrace.c \
$(TOP)/ext/misc/windirent.h \
$(TOP)/ext/misc/zipfile.c \
$(TOP)/ext/recover/dbdata.c \
$(TOP)/ext/recover/sqlite3recover.c \
$(TOP)/ext/recover/sqlite3recover.h \
$(TOP)/src/test_windirent.c \
$(TOP)/src/test_windirent.h
$(TOP)/ext/recover/sqlite3recover.h
shell.c: $(SHELL_DEP) $(TOP)/tool/mkshellc.tcl $(B.tclsh)
$(B.tclsh) $(TOP)/tool/mkshellc.tcl shell.c
+87 -87
View File
@@ -1,14 +1,14 @@
C Version\s3.50.4
D 2025-07-30T19:33:53.432
C Enable\sthe\sEXISTS-to-JOIN\soptimization\sif\sthe\souter\squery\shas\sno\nFROM\sclause.
D 2025-07-08T17:28:09.030
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md e108e1e69ae8e8a59e93c455654b8ac9356a11720d3345df2a4743e9590fb20d
F Makefile.in c3e414df4dc8dfb12f1f6baf129fcb6d18cd0ebd3c9109370fb3fceeeef9a37a
F Makefile.in a6c14b6906f5322920dd5d1ff9a84808337911068f2a6e777ec7088a87cbc3a8
F Makefile.linux-generic bd3e3cacd369821a6241d4ea1967395c962dfe3057e38cb0a435cee0e8b789d0
F Makefile.msc 0206f28988bb6634c7e8aff05bf6cfa65d6dfe1d2b6bd95160dd99290a83dfc7
F Makefile.msc ec2011bbdfc917d6a1c7c173dabb29c14ead0dd8e2e0b67278a00ae4ba576a77
F README.md e28077cfbef795e99c9c75ed95aa7257a1166709b562076441a8506ac421b7c1
F VERSION 33191da58a15d51e312248db3124d33e1dbc1797d7f8fe14d9df5ff07430b503
F VERSION 16eddb43056a79c1977427ab7a05f3457c373fa159dcdced8754eb89ce7e06b8
F art/icon-243x273.gif 9750b734f82fdb3dc43127753d5e6fbf3b62c9f4e136c2fbf573b2f57ea87af5
F art/icon-80x90.gif 65509ce3e5f86a9cd64fe7fca2d23954199f31fe44c1e09e208c80fb83d87031
F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
@@ -22,15 +22,14 @@ F autoconf/Makefile.msc f15ad424ca2820df8e39d9157965710af0a64d87773706706a12ea4f
F autoconf/README.first f1d3876e9a7852c22f275a6f06814e64934cecbc0b5b9617d64849094c1fd136
F autoconf/README.txt b749816b8452b3af994dc6d607394bef3df1736d7e09359f1087de8439a52807
F autoconf/auto.def 3d994f3a9cc9b712dbce92a5708570ddcf3b988141b6eb738f2ed16127a9f0ac
F autoconf/tea/Makefile.in 14c6a79ce87e10d8a35398f2d0e04e1d83a88eb52ee16ebf0eeaccf005ff84b3
F autoconf/tea/README.txt 6301adfea2e75eabcc550a1ed4812faaa4024c9584ac89a7f018e39f9bc9defb
F autoconf/tea/_teaish.tester.tcl.in ed5445512e91c12afbbb99771efb68a23be4a046d52d61213fb5b6f010118129
F autoconf/tea/Makefile.in bf6b43eafcd18766d81a8f0085cfc9cb051d8abae9031a8e7c3f5f1246e8f166
F autoconf/tea/README.txt 23475876343498ef2b514cc7510e8f1559a17e8e03fbc7a41c1c8a3b89e7b7e3
F autoconf/tea/_teaish.tester.tcl.in 8253b44be88e2e3f21de95a65d3a90c2be8e70b7bdd08a5b80e337ba7402f8f1
F autoconf/tea/auto.def ce95b9450e2fa4ba5dc857e208fe10f4e6f2d737796ac3278aee6079db417529
F autoconf/tea/configure d0b12b984edca6030d1976375b80157ac78b5b90a5b4f0dcee39357f63f4a80b x
F autoconf/tea/doc/sqlite3.n 9a97f4f717ceab73004ea412af7960625c1cb24b5c25e4ae4c8b5d8fa4300f4e
F autoconf/tea/license.terms 13bd403c9610fd2b76ece0ab50c4c5eda933d523
F autoconf/tea/pkgIndex.tcl.in e07da6b94561f4aa382bab65b1ccceb04701b97bf59d007c1d1f20a222b22d07
F autoconf/tea/teaish.tcl 81571a9f9ae5c70735595b05586cb2de9d2aea7e32aad10417c4982f2e2f01c8
F autoconf/tea/teaish.tcl 81feb417e718ed75cdd7e2fdf6771f3da80dae97377a90c4d5b62b3754abbf1d
F autoconf/tea/teaish.test.tcl cfe94e1fb79dd078f650295be59843d470125e0cc3a17a1414c1fb8d77f4aea6
F autosetup/LICENSE 41a26aebdd2cd185d1e2b210f71b7ce234496979f6b35aef2cbf6b80cbed4ce4
F autosetup/README.autosetup a78ff8c4a3d2636a4268736672a74bf14a82f42687fcf0631a70c516075c031e
@@ -47,13 +46,13 @@ F autosetup/cc.tcl c0fcc50ca91deff8741e449ddad05bcd08268bc31177e613a6343bbd1fd3e
F autosetup/find_tclconfig.tcl e64886ffe3b982d4df42cd28ed91fe0b5940c2c5785e126c1821baf61bc86a7e
F autosetup/jimsh0.c 563b966c137a4ce3c9333e5196723b7ac0919140a9d7989eb440463cd855c367
F autosetup/pkg-config.tcl 4e635bf39022ff65e0d5434339dd41503ea48fc53822c9c5bde88b02d3d952ba
F autosetup/proj.tcl c4a77735b57f3c016a185bff048212a197b77723f9bea6cfafe396e4b542c666
F autosetup/sqlite-config.tcl ccda82e43e377b832aae72a1678b1dc17dcaff36ed0ebbd8f0cfc88612ae8de3
F autosetup/proj.tcl 6aac1eb3059fc511c8e1659f33b96eb2a216a371ed9b28be6661374061be9b15
F autosetup/sqlite-config.tcl 3177dedd7bd49465fa06677fd743c4966eee5702a9ddf4914c2c1af5e5972a52
F autosetup/system.tcl 51d4be76cd9a9074704b584e5c9cbba616202c8468cf9ba8a4f8294a7ab1dba9
F autosetup/teaish/README.txt b40071e6f8506500a2f7f71d5fc69e0bf87b9d7678dd9da1e5b4d0acbf40b1ca
F autosetup/teaish/core.tcl 42b4f76ebcacafded7c13a225ce55454bd1fcf8a11912729128e1076b12f6433
F autosetup/teaish/core.tcl aee092fc71986d1272b835ea7492bb55ffc213a289502e4f14da80cf67b7e3c3
F autosetup/teaish/feature.tcl 18194fb79a24d30e5bbdeab40999616f39278b53a27525349ded033af2fd73be
F autosetup/teaish/tester.tcl 091745984473faea6985254b9986c6dfd0cce06f68bc515ba4afc1e6b3742fa8
F autosetup/teaish/tester.tcl 1799514c2652db49561b3386c5242b94534d1663f2cfac861a955e071895fdd0
F configure 9a00b21dfd13757bbfb8d89b30660a89ec1f8f3a79402b8f9f9b6fc475c3303a x
F contrib/sqlitecon.tcl eb4c6578e08dd353263958da0dc620f8400b869a50d06e271ab0be85a51a08d3
F doc/F2FS.txt c1d4a0ae9711cfe0e1d8b019d154f1c29e0d3abfe820787ba1e9ed7691160fcd
@@ -82,7 +81,7 @@ F ext/fts3/README.tokenizers b92bdeb8b46503f0dd301d364efc5ef59ef9fa8e2758b8e742f
F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
F ext/fts3/fts3.c 4f02858ab845a97bedf06e6cc1fba49a81fe5e00a26df68d0ad0f00a5814fa70
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
F ext/fts3/fts3Int.h 53f00a0d037c81174af1089ad7776563dcbccb5ed4697405256d3d4260d9fccc
F ext/fts3/fts3Int.h d0f70d602be987501cf6eff86a06a0b5d0389929c6abe24972d59594e6f4cb03
F ext/fts3/fts3_aux.c 7eab82a9cf0830f6551ba3abfdbe73ed39e322a4d3940ee82fbf723674ecd9f3
F ext/fts3/fts3_expr.c 5c13796638d8192c388777166075cdc8bc4b6712024cd5b72c31acdbefce5984
F ext/fts3/fts3_hash.c d9dba473741445789330c7513d4f65737c92df23c3212784312931641814672a
@@ -108,14 +107,14 @@ F ext/fts3/unicode/mkunicode.tcl cbf5f7b5c8ce8014bad731f246f2e520eece908465de477
F ext/fts3/unicode/parseunicode.tcl a981bd6466d12dd17967515801c3ff23f74a281be1a03cf1e6f52a6959fc77eb
F ext/fts5/extract_api_docs.tcl 009cf59c77afa86d137b0cca3e3b1a5efbe2264faa2df233f9a7aa8563926d15
F ext/fts5/fts5.h ff5d3cc88b29e41612bfb29eb723e29e38973de62ca75ba3e8f94ccb67f5b5f2
F ext/fts5/fts5Int.h 4c7380ce83e8f6b5b3216ebe2b33093a20fa72e832a88d14023f827b7d8b9933
F ext/fts5/fts5Int.h cc1e6ed28e1fbde71e4427ba6d15b0c73a4f8f119adeb4f7bcc97e3336fbbeac
F ext/fts5/fts5_aux.c da4a7a9a11ec15c6df0699d908915a209bcde48f0b04101461316b59f71abffb
F ext/fts5/fts5_buffer.c f1e6d0324d7c55329d340673befc26681a372a4d36086caa8d1ec7d7c53066c7
F ext/fts5/fts5_config.c e7d8dd062b44a66cd77e5a0f74f23a2354cd1f3f8575afb967b2773c3384f7f8
F ext/fts5/fts5_expr.c be9e5f7f11d87e7bd3680832c93c13050fe351994b5052b0215c2ef40312c23a
F ext/fts5/fts5_hash.c a6266cedd801ab7964fa9e74ebcdda6d30ec6a96107fa24148ec6b7b5b80f6e0
F ext/fts5/fts5_index.c 5896ca188574f728426a8f03752dfbf4fad48d1a4b7450b29d8d3acae1739761
F ext/fts5/fts5_main.c 57933c18efe1058d8871199875c7a59744dabc3904f3aefbf9ff4a4e11fc79e2
F ext/fts5/fts5_index.c ed562b75c87efaa80c40e55f6c1102904728e35baa293c371f96b1c8e151399d
F ext/fts5/fts5_main.c e558225168845dc708abeb2ad10415696e5a3249bcba1810ba3c7ef80764962e
F ext/fts5/fts5_storage.c 19bc7c4cbe1e6a2dd9849ef7d84b5ca1fcbf194cefc3e386b901e00e08bf05c2
F ext/fts5/fts5_tcl.c 7fb5a3d3404099075aaa2457307cb459bbc257c0de3dbd52b1e80a5b503e0329
F ext/fts5/fts5_test_mi.c 4308d5658cb1f5eee5998dcbaac7d5bdf7a2ef43c8192ca6e0c843f856ccee26
@@ -127,7 +126,7 @@ F ext/fts5/fts5_vocab.c ff0441c4ea165081e8152dec6d29056faa0cdc281a9f218a00e3d7aa
F ext/fts5/fts5parse.y eb526940f892ade5693f22ffd6c4f2702543a9059942772526eac1fde256bb05
F ext/fts5/mkportersteps.tcl 5acf962d2e0074f701620bb5308155fa1e4a63ba
F ext/fts5/test/fts5_common.tcl c5aa7cf7148b6dcffb5b61520ae18212baf169936af734ab265143f59db328fe
F ext/fts5/test/fts5aa.test 015c81b84d53bfcedd77d624202c8b02e9f0cbbb4b51688e3a9c9f90bccbb4ac
F ext/fts5/test/fts5aa.test cf4ff6180873bbc131666ba846ddd90148fcb61c20aad089711d3511cce24300
F ext/fts5/test/fts5ab.test 4bdb619fee409e11417e8827e320b857e42e926a01a0408fc9f143ec870a6ced
F ext/fts5/test/fts5ac.test 4a73626de86f3d17c95738034880c4f0de8d54741fb943d819b528373657e59b
F ext/fts5/test/fts5ad.test 058e616612964e61d19f70295f0e6eaedceb4b29b1fbf4f859615ef7e779dc22
@@ -161,14 +160,14 @@ F ext/fts5/test/fts5contentless2.test 70ffe6c611d8f278240da56734df8a77948f04e273
F ext/fts5/test/fts5contentless3.test 75eaae5ad6b284ee447788943974d323228f27cc35a1681da997135cff95bc6a
F ext/fts5/test/fts5contentless4.test ec34dc69ef474ca9997dae6d91e072906e0e9a5a4b05ea89964c863833b6eff8
F ext/fts5/test/fts5contentless5.test 38cd0392c730dc7090c550321ce3c24ba4c392bc97308b51a4180e9959dca7b5
F ext/fts5/test/fts5corrupt.test 6485f721b88ba355ca5d701e7ee87a4efa3ea578d8e6adb26f51ef956c8328bd
F ext/fts5/test/fts5corrupt2.test 335911e3f68b9625d850325f9e29a128db3f4276a8c9d4e32134580da8f924c4
F ext/fts5/test/fts5corrupt3.test 3420ad30bf9e9bbdbd43b3224c582431744899530a65b11b60ddacdf14200e19
F ext/fts5/test/fts5corrupt.test 237fce1c3261bb3a5bec333b0f0dbf5b105ec32627ef14cccbda3cfe13833193
F ext/fts5/test/fts5corrupt2.test 4a03a158c2cb617c9f76d26b35c1ef2534124bc0bbddcea38dfd5b170ebea27b
F ext/fts5/test/fts5corrupt3.test 03a6118a8fe5a7c217c28c92b6b25ba04643640a4ac0a1d2b8d10de8191dc5f4
F ext/fts5/test/fts5corrupt4.test dc08d19f5b8943e95a7778a7d8da592042504faf18dd93f68f7d7a0d7d7dd733
F ext/fts5/test/fts5corrupt5.test bcf0801b0c991eadae3cb8e978e82b4bf01412cb4df41874a90d5aa279c7cc96
F ext/fts5/test/fts5corrupt5.test 73985d4fe6d8f0d5d5c7bcf79ae7c6522c376cd6ad710a0ff2f26e0c2e222abe
F ext/fts5/test/fts5corrupt6.test 2d72db743db7b5d9c9a6d0cfef24d799ed1aa5e8192b66c40e871a37ed9eed06
F ext/fts5/test/fts5corrupt7.test 4e830875c33b9ea3c4cf1ba71e692b63893cbb4faae8c69b1071889dc26e211c
F ext/fts5/test/fts5corrupt8.test ffaaad3524c79621042eaf176d9206f68c5f07611c4d24943e60a3b6ea0026e8
F ext/fts5/test/fts5corrupt7.test 814aab492d7a09abb5bfdd81cc66fc206d7f3868f9a3bae91876e02efc466fb3
F ext/fts5/test/fts5corrupt8.test 7618b102b9b3a5a3494271f4975ab5837e2fb3f61f5adfcdeeb31772c859e6df
F ext/fts5/test/fts5delete.test 2a5008f8b1174ef41d1974e606928c20e4f9da77d9f8347aed818994d89cced4
F ext/fts5/test/fts5detail.test 54015e9c43ec4ba542cfb93268abdf280e0300f350efd08ee411284b03595cc4
F ext/fts5/test/fts5determin.test 1b77879b2ae818b5b71c859e534ee334dac088b7cf3ff3bf76a2c82b1c788d11
@@ -230,7 +229,7 @@ F ext/fts5/test/fts5prefix.test c0b7842f1a2d830c0b146cd438a95ea4c5a25635719ed0d9
F ext/fts5/test/fts5prefix2.test a5bb43b8a2687efafa7ac4e5ccff6812015cf8cf18e3086bb0eb3126f30fbbf6
F ext/fts5/test/fts5query.test 0320a7a4b58a6e3e50ec8910b301649da90ace675001f9e0bf6392750ad4591d
F ext/fts5/test/fts5rank.test 47c1e8e5d84754ff18e012fdd629776088b5a15de41bdd24957581cf084d8a00
F ext/fts5/test/fts5rebuild.test 83e72d77636378833233fadc7cb7517a2fa446ea7d1f94dd526ba3e7e104b9f5
F ext/fts5/test/fts5rebuild.test dc09779fbbe151ab68206a0931c10a611912a7a12c7a85d71c5e48453f2375a5
F ext/fts5/test/fts5restart.test 9af2084b8e065130037b95f05f3f220bb7973903a7701e2c5fb916dff7cf80c5
F ext/fts5/test/fts5rowid.test 8632829fec04996832a4cfb4f0bd89721ba65b7e398c1731741bdb63f070e1a3
F ext/fts5/test/fts5savepoint.test 1447758d7900afe903cef08b4524c5331fb60c1126ae6fba7f4d8704268013c5
@@ -296,7 +295,7 @@ F ext/jni/src/org/sqlite/jni/annotation/NotNull.java be6cc3e8e114485822331630097
F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 56e3dee1f3f703a545dfdeddc1c3d64d1581172b1ad01ffcae95c18547fafd90
F ext/jni/src/org/sqlite/jni/annotation/package-info.java 977b374aed9d5853cbf3438ba3b0940abfa2ea4574f702a2448ee143b98ac3ca
F ext/jni/src/org/sqlite/jni/capi/AbstractCollationCallback.java 1afa90d3f236f79cc7fcd2497e111992644f7596fbc8e8bcf7f1908ae00acd6c
F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java 0b72cdff61533b564d65b63418129656daa9a9f30e7e7be982bd5ab394b1dbd0
F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java 0e28a0df51368c7127e505f1e9acd92a7e66e035bcd6288463aa691cb300c9af
F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java c045a5b47e02bb5f1af91973814a905f12048c428a3504fbc5266d1c1be3de5a
F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759
F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca
@@ -418,7 +417,7 @@ F ext/misc/dbdump.c b8592f6f2da292c62991a13864a60d6c573c47a9cc58362131b9e6a64f82
F ext/misc/decimal.c 228d47e9ef4de60daf5851da19e3ac9ac1eda9e94432816914469501db6a1129
F ext/misc/eval.c 04bc9aada78c888394204b4ed996ab834b99726fb59603b0ee3ed6e049755dc1
F ext/misc/explain.c 606100185fb90d6a1eade1ed0414d53503c86820d8956a06e3b0a56291894f2b
F ext/misc/fileio.c 69b70008290598030592aec969bcd2cba04466a7b85ad3e906f87981690aab3b
F ext/misc/fileio.c 88cb2e5744296de6638af02ef6349fd468c2eb5e5f41ba405f88d9b4ad500f8e
F ext/misc/fossildelta.c 0aeb099e9627eea693cf21ae47826ecd1e0319b93143bed23090838b2ef0c162
F ext/misc/fuzzer.c 6b231352815304ba60d8e9ec2ee73d4918e74d9b76bda8940ba2b64e8777515e
F ext/misc/ieee754.c c9dd9d77c8e8e18e0a5706f8ffcccf4ccb6562073709f7453d4d73f5122f4362
@@ -457,9 +456,10 @@ F ext/misc/uuid.c 5bb2264c1b64d163efa46509544fd7500cb8769cb7c16dd52052da8d961505
F ext/misc/vfslog.c 3932ab932eeb2601dbc4447cb14d445aaa9fbe43b863ef5f014401c3420afd20
F ext/misc/vfsstat.c 0b23c0a69a2b63dc0ef0af44f9c1fc977300c480a1f7a9814500369d8211f56e
F ext/misc/vfstrace.c 0e4b8b17ac0675ea90f6d168d8214687e06ca3efbc0060aad4814994d82b41fb
F ext/misc/vtablog.c a197addbbd1e267a5476274b74953e1b6f050e28516f0a5fe7d6382753165ee6
F ext/misc/vtablog.c 62d54fb0f4ff9df72ffa44f4a226451a964e1d96ac550f2b8906feebdad00b58
F ext/misc/vtshim.c e5bce24ab8c532f4fdc600148718fe1802cb6ed57417f1c1032d8961f72b0e8f
F ext/misc/wholenumber.c 0fa0c082676b7868bf2fa918e911133f2b349bcdceabd1198bba5f65b4fc0668
F ext/misc/windirent.h 02211ce51f3034c675f2dbf4d228194d51b3ee05734678bad5106fff6292e60c
F ext/misc/zipfile.c b62147ac4985eaac4e368d529b1f4f43ad6bc9ac13d6805d907fff3afdac64d3
F ext/misc/zorder.c b0ff58fa643afa1d846786d51ea8d5c4b6b35aa0254ab5a82617db92f3adda64
F ext/rbu/rbu.c 801450b24eaf14440d8fd20385aacc751d5c9d6123398df41b1b5aa804bf4ce8
@@ -539,7 +539,7 @@ F ext/repair/test/checkindex01.test b530f141413b587c9eb78ff734de6bb79bc3515c3350
F ext/repair/test/test.tcl 686d76d888dffd021f64260abf29a55c57b2cedfa7fc69150b42b1d6119aac3c
F ext/rtree/README 734aa36238bcd2dee91db5dba107d5fcbdb02396612811377a8ad50f1272b1c1
F ext/rtree/geopoly.c f0573d5109fdc658a180db0db6eec86ab2a1cf5ce58ec66cbf3356167ea757eb
F ext/rtree/rtree.c 811edc5c2f3e13dcee825a8ec6f2ebe29b34bdb0186184d75d461621173638fa
F ext/rtree/rtree.c a1f04a2013bc8f982307760615d1cee591355ac4723c74c3761c128d5c6954ab
F ext/rtree/rtree.h 4a690463901cb5e6127cf05eb8e642f127012fd5003830dbc974eca5802d9412
F ext/rtree/rtree1.test e0608db762b2aadca0ecb6f97396cf66244490adc3ba88f2a292b27be3e1da3e
F ext/rtree/rtree2.test 9d9deddbb16fd0c30c36e6b4fdc3ee3132d765567f0f9432ee71e1303d32603d
@@ -621,7 +621,7 @@ F ext/session/sqlite3session.c 6b0877fe1ab832aa4b85eaca72606dfd1630a1363a1be7af1
F ext/session/sqlite3session.h 9bb1a6687b467764b35178dc29bbd2c57ab8cd3acdc8a62f088c34ad17e4fe2b
F ext/session/test_session.c 2ddff73ea368d827028c32851b291416e1008845832feb27b751d15e57e13cc3
F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c
F ext/wasm/GNUmakefile a9acd702e7423cd47b96b78d0ef546bb7612fa7731e1b400261def96bd90385e
F ext/wasm/GNUmakefile a05bb1766f97c88cb42fbfe9e349c799c691f8ae0dc959e9d9469f0bcee89350
F ext/wasm/README-dist.txt f01081a850ce38a56706af6b481e3a7878e24e42b314cfcd4b129f0f8427066a
F ext/wasm/README.md b89605f65661cf35bf034ff6d43e448cc169b8017fc105d498e33b81218b482c
F ext/wasm/SQLTester/GNUmakefile e0794f676d55819951bbfae45cc5e8d7818dc460492dc317ce7f0d2eca15caff
@@ -630,7 +630,7 @@ F ext/wasm/SQLTester/SQLTester.run.mjs 57f2adb33f43f2784abbf8026c1bfd049d8013af1
F ext/wasm/SQLTester/index.html 64f3435084c7d6139b08d1f2a713828a73f68de2ae6a3112cbb5980d991ba06f
F ext/wasm/SQLTester/touint8array.c 2d5ece04ec1393a6a60c4bf96385bda5e1a10ad49f3038b96460fc5e5aa7e536
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-core 2bcbbfe3b95c043ed6037e2708a2ee078d212dd1612c364f93588d8dc97300fe
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-extras fe40d6d758646e38f8b15f709044951e10884214f5453d35502100179c388c13
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-extras cb4fa8842c875b6ee99381523792975c5ebb7371bd27fbd1bd863a43c7f3505a
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-see fb29e62082a658f0d81102488414d422c393c4b20cc2f685b216bc566237957b
F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287
F ext/wasm/api/README.md c64ec8e84449c069e0217706d9d7d31b3bd53627228b2ba0c3cddbdc2350ca66
@@ -640,8 +640,8 @@ F ext/wasm/api/post-js-footer.js 365405929f41ca0e6d389ed8a8da3f3c93e11d3ef43a90a
F ext/wasm/api/post-js-header.js 53740d824e5d9027eb1e6fd59e216abbd2136740ce260ea5f0699ff2acb0a701
F ext/wasm/api/pre-js.c-pp.js a614a2c82b12c4d96d8e3ba77330329efc53c4d56a8a7e60ade900f341866cfb
F ext/wasm/api/sqlite3-api-cleanup.js 3ac1786e461ada63033143be8c3b00b26b939540661f3e839515bb92f2e35359
F ext/wasm/api/sqlite3-api-glue.c-pp.js bd8ae059bb3ea489666b2f167025833e55dc5941be99c129a56fc25e541d37ce
F ext/wasm/api/sqlite3-api-oo1.c-pp.js f3a8e2004c6625d17946c11f2fb32008be78bc5207bf746fc77d59848813225f
F ext/wasm/api/sqlite3-api-glue.c-pp.js 0b76510f3650053bac67ca8947cb6ab9d050ad2218118a2e7796dd37be832ffa
F ext/wasm/api/sqlite3-api-oo1.c-pp.js c68d6da0088c2527156fca9163a721abe08e7bd077b15404fd8d292f4612adc1
F ext/wasm/api/sqlite3-api-prologue.js 8708570165f5b4bce9a78ccd91bc9ddf8735970ac1c4d659e36c9a7d9a644bb4
F ext/wasm/api/sqlite3-api-worker1.c-pp.js f646a65257973b8c4481f8a6a216370b85644f23e64b126e7ae113570587c0ab
F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89
@@ -650,7 +650,7 @@ F ext/wasm/api/sqlite3-vfs-helper.c-pp.js 3f828cc66758acb40e9c5b4dcfd87fd478a14c
F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js 0f68a64e508598910e7c01214ae27d603dfc8baec6a184506fafac603a901931
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 4ab0704ee198de7d1059eccedc7703c931510b588d10af0ee36ea5b3ebbac284
F ext/wasm/api/sqlite3-vtab-helper.c-pp.js e809739d71e8b35dfe1b55d24d91f02d04239e6aef7ca1ea92a15a29e704f616
F ext/wasm/api/sqlite3-wasm.c 7ea3d4a286a2241f6fcc65c9ff10fc04ee5590f80f40763a57001dd5e93aa4c4
F ext/wasm/api/sqlite3-wasm.c d2672a83b99a89db6bab31eccf9f93fca15a01838d8b0224967db6d4c4bff560
F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bc65debfe43b81fc39fb25c40ad0cc1946bd82580fbf644351107b544d6177ee
F ext/wasm/api/sqlite3-worker1.c-pp.js 5e8706c2c4af2a57fbcdc02f4e7ef79869971bc21bb8ede777687786ce1c92d5
F ext/wasm/batch-runner-sahpool.html e9a38fdeb36a13eac7b50241dfe7ae066fe3f51f5c0b0151e7baee5fce0d07a7
@@ -688,9 +688,9 @@ F ext/wasm/scratchpad-wasmfs.html a3d7388f3c4b263676b58b526846e9d02dfcb4014ff29d
F ext/wasm/scratchpad-wasmfs.mjs 66034b9256b218de59248aad796760a1584c1dd842231505895eff00dbd57c63
F ext/wasm/speedtest1-wasmfs.html 0e9d335a9b5b5fafe6e1bc8dc0f0ca7e22e6eb916682a2d7c36218bb7d67379d
F ext/wasm/speedtest1-wasmfs.mjs c77c7231338ed5c0e1ce16aa29106df8e5b5cf11a48319c49433490a8d3ded30
F ext/wasm/speedtest1-worker.html 864b65ed78ce24847a348c180e7f267621a02ca027068a1863ec1c90187c1852
F ext/wasm/speedtest1-worker.html d24d1e06caf3dcd83430c8c3d87761ff7555fd06eaeaf2fc02ce49cf45f0d032
F ext/wasm/speedtest1-worker.js 95e549e13a4d35863a9a7fc66122b5f546c0130d3be7b06dfcc556eb66d24bde
F ext/wasm/speedtest1.html ff048b4a623aa192e83e143e48f1ce2a899846dd42c023fdedc8772b6e3f07da
F ext/wasm/speedtest1.html 217bf15d43065634a27a06dbd5c9a320f6da9932dbcdfc963d482bc2f99dbea9
F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x
F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0
F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5
@@ -698,7 +698,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555
F ext/wasm/test-opfs-vfs.js 1618670e466f424aa289859fe0ec8ded223e42e9e69b5c851f809baaaca1a00c
F ext/wasm/tester1-worker.html ebc4b820a128963afce328ecf63ab200bd923309eb939f4110510ab449e9814c
F ext/wasm/tester1.c-pp.html 1c1bc78b858af2019e663b1a31e76657b73dc24bede28ca92fbe917c3a972af2
F ext/wasm/tester1.c-pp.js 419717b16e12703487a7ccf3ea4e63d693bdfbf7657e55a7e6c559bbccf027d3
F ext/wasm/tester1.c-pp.js 766a2ba51a2619d41a49be7c6a1ad014c1d23fc97b67496e4f103038203eb17d
F ext/wasm/tests/opfs/concurrency/index.html 657578a6e9ce1e9b8be951549ed93a6a471f4520a99e5b545928668f4285fb5e
F ext/wasm/tests/opfs/concurrency/test.js d08889a5bb6e61937d0b8cbb78c9efbefbf65ad09f510589c779b7cc6a803a88
F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2
@@ -709,7 +709,7 @@ F ext/wasm/tests/opfs/sahpool/sahpool-pausing.js f264925cfc82155de38cecb3d204c36
F ext/wasm/tests/opfs/sahpool/sahpool-worker.js bd25a43fc2ab2d1bafd8f2854ad3943ef673f7c3be03e95ecf1612ff6e8e2a61
F ext/wasm/wasmfs.make 68999f5bd8c489239592d59a420f8c627c99169bbd6fa16a404751f757b9f702
F magic.txt 5ade0bc977aa135e79e3faaea894d5671b26107cc91e70783aa7dc83f22f3ba0
F main.mk 34290a772ec671de1fa5defd4fa4074aad24b1ea7eaabebba071e30564c6498c
F main.mk 3ced12d068b22957f4e82a0b94a2c1d83b37dcf39c087263691267f7bfb39a84
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
F mptest/crash01.test 61e61469e257df0850df4293d7d4d6c2af301421
@@ -724,19 +724,19 @@ F src/analyze.c 03bcfc083fc0cccaa9ded93604e1d4244ea245c17285d463ef6a60425fcb247d
F src/attach.c 9af61b63b10ee702b1594ecd24fb8cea0839cfdb6addee52fba26fa879f5db9d
F src/auth.c 54ab9c6c5803b47c0d45b76ce27eff22a03b4b1f767c5945a3a4eb13aa4c78dc
F src/backup.c 5c97e8023aab1ce14a42387eb3ae00ba5a0644569e3476f38661fa6f824c3523
F src/bitvec.c 203368f515a36e5537f1efc2a7f47903e9e44a6626e4fd0554f8feed981bba88
F src/bitvec.c e242d4496774dfc88fa278177dd23b607dce369ccafb3f61b41638eea2c9b399
F src/btmutex.c 30dada73a819a1ef5b7583786370dce1842e12e1ad941e4d05ac29695528daea
F src/btree.c da98489a981c347cc3a3982ea2810bbb583511a73cc34762547f30dbb4cda7f0
F src/btree.h 18e5e7b2124c23426a283523e5f31a4bff029131b795bb82391f9d2f3136fc50
F src/btree.c cb5b8ceb9baa02a63a2f83dec09c4153e1cfbdf9c2adef5c62c26d2160eeb067
F src/btree.h e823c46d87f63d904d735a24b76146d19f51f04445ea561f71cc3382fd1307f0
F src/btreeInt.h 9c0f9ea5c9b5f4dcaea18111d43efe95f2ac276cd86d770dce10fd99ccc93886
F src/build.c 67159d31bfc565e5f23a7be8159325bae599bddd19fc584ac2511b947cf341d3
F src/build.c cc4f287348790bbb7219f7e8dee13b1c345c3377fcdd98eca866e7457ecd07e7
F src/callback.c acae8c8dddda41ee85cfdf19b926eefe830f371069f8aadca3aa39adf5b1c859
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
F src/date.c 9db4d604e699a73e10b8e85a44db074a1f04c0591a77e2abfd77703f50dce1e9
F src/dbpage.c fcb1aafe00872a8aff9a7aa0ef7ff1b01e5817ec7bbd521f8f3e1e674ac8d609
F src/dbpage.c b3e218f8ed74fcbb7fa805df8ca669a3718d397617b3d8a8aac3307dc315c4d6
F src/dbstat.c 73362c0df0f40ad5523a6f5501224959d0976757b511299bf892313e79d14f5c
F src/delete.c 03a77ba20e54f0f42ebd8eddf15411ed6bdb06a2c472ac4b6b336521bf7cea42
F src/expr.c 72e7cddcb2ea06de48f40ee301b81608d86a17b73781e42901ce585f53074589
F src/expr.c 12d8a79f6a0eb9bfae2d468a6d5bd7b9a0ff00988b3f5326ec9bbaad61bbf385
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c 928ed2517e8732113d2b9821aa37af639688d752f4ea9ac6e0e393d713eeb76f
F src/func.c de47a8295503aa130baae5e6d9868ecf4f7c4dbffa65d83ad1f70bdbac0ee2d6
@@ -745,7 +745,7 @@ F src/hash.c 73934a7f7ab1cb110614a9388cb516893b0cf5b7b69e4fd1a0780ac4ce166be7
F src/hash.h 46b92795a95bfefb210f52f0c316e9d7cdbcdd7e7fcfb0d8be796d3a5767cddf
F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
F src/insert.c d05934dfab2c5c0c480fc6fd2038f11215661de08ea6ff38d2563216bd555c1b
F src/insert.c dfd311b0ac2d4f6359e62013db67799757f4d2cc56cca5c10f4888acfbbfa3fd
F src/json.c cb87977b1ee25ee7d27505d65a9261b687395bf895342c8ba566b7c01aee2047
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
F src/loadext.c d7edd8e671237539d795d30daaf888908a2c82e99bade4c78f3be021e8b7d655
@@ -781,22 +781,22 @@ F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5
F src/pcache1.c 131ca0daf4e66b4608d2945ae76d6ed90de3f60539afbd5ef9ec65667a5f2fcd
F src/pragma.c 30b535d0a66348df844ee36f890617b4cf45e9a22dcbc47ec3ca92909c50aaf1
F src/prepare.c 1832be043fce7d489959aae6f994c452d023914714c4d5457beaed51c0f3d126
F src/printf.c 3b91c334f528359145f4dde0dedd945bbb21044d0825ea064934d7222d61662c
F src/printf.c 71b6d3a0093bf23f473e25480ca0024e8962681506c75f4ffd3d343a3f0ab113
F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
F src/resolve.c d40fe18d7c2fd0339f5846ffcf7d6809866e380acdf14c76fb2af87e9fe13f64
F src/resolve.c d3ee7ed308d46f4ee6d3bb6316d8d6f87158f93a7fd616732138cc953cf364f0
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
F src/select.c 5c129b669317a1d57283055482b9c1e105199a7e47d69526491ca165d3376999
F src/shell.c.in ba53a52dafb167ac6320703da741386c34fbcabe8c078a188bb9f89808e3ef8f
F src/sqlite.h.in 9ae373d11e1b11ac9c81c508523ae37f1619e739858280078ee9fb4e1e62d3ed
F src/select.c 244f2fba5f73c7ea937333bd54280e83e218a0b652fc4540cbd72d33b0f7b4d8
F src/shell.c.in 73c0eeb7c265d59b99219d5aa055f412f07842088d8036b6d259927d85dd1bbf
F src/sqlite.h.in 5c54f2461a1ea529bab8499148a2b238e2d4bb571d59e8ea5322d0c190abb693
F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479
F src/sqlite3ext.h 0bfd049bb2088cc44c2ad54f2079d1c6e43091a4e1ce8868779b75f6c1484f1e
F src/sqliteInt.h 0c24996d4c02eca6fd971ecbe5a565aaaf5f024265a53ce62d77ee366fcc7c6a
F src/sqliteInt.h 9c99d7565a839ad342cdda504c4b7921bb1a24c07227b8f50b7b131245a20693
F src/sqliteLimit.h 6d817c28a8f19af95e6f4921933b7fbbca48a962bce0eb0ec81e8bb3ef38e68b
F src/status.c 0e72e4f6be6ccfde2488eb63210297e75f569f3ce9920f6c3d77590ec6ce5ffd
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
F src/tclsqlite.c d0e63ffe7944dd223bf62066d9f982cbee1978811c7fbfd889f4ba9c5baed3d1
F src/tclsqlite.c 3c604c49e6cf4211960a9ddb9505280fd22cde32175f40884c641c0f5a286036
F src/tclsqlite.h 65e2c761446e1c9fa0342b7d2612a703483643c8b6a316d12a65b745a4727395
F src/test1.c 9b54135e5f1352f06b1d23d7c183f124c1f33de6ea8997cd801f0f215c43591d
F src/test1.c 13cc07851f989141b29f7ca3c6c90f6d18f90081ab423c66716c8cb29d277d1f
F src/test2.c 62f0830958f9075692c29c6de51b495ae8969e1bef85f239ffcd9ba5fb44a5ff
F src/test3.c 432646f581d8af1bb495e58fc98234380250954f5d5535e507fc785eccc3987a
F src/test4.c 0ac87fc13cdb334ab3a71823f99b6c32a6bebe5d603cd6a71d84c823d43a25a0
@@ -813,7 +813,7 @@ F src/test_config.c 7f412406592794636d6226268e26d413850a9f799bc5f3c01afc2820b165
F src/test_delete.c d0e8f6dc55cfc98a7c27c057fb88d512260564bf0b611482656c68b8f7f401ed
F src/test_demovfs.c 3efa2adf4f21e10d95521721687d5ca047aea91fa62dd8cc22ac9e5a9c942383
F src/test_devsym.c 649434ed34d0b03fbd5a6b42df80f0f9a7e53f94dd1710aad5dd8831e91c4e86
F src/test_fs.c 6711fd4c6c05914b613cfc99918a24978452f999ce03fc8f89c9794c03b20a5b
F src/test_fs.c a946408c81231feff4b6a2c18068f20aefe254dc82288687128af8b7520d32cb
F src/test_func.c 858d4dddb7acf88222ebcba7cffb585f6dde83e4a15b838c0d05ccdf8d5219b9
F src/test_hexio.c a90baa0a8ab5e7cfe2216a61c9a31cfd1f8378353a3d23e25fa94c09aa755bb0
F src/test_init.c 1649e02448f536e53172f6b1ff873254fe9a0c6c8a4502a2d25c0cc7b11945ea
@@ -841,8 +841,6 @@ F src/test_tclvar.c ae873248a0188459b1c16ca7cc431265dacce524399e8b46725c2b3b7e04
F src/test_thread.c 3edb4a5b5aeb1a6e9a275dccc848ac95acab7f496b3e9230f6d2d04953a2b862
F src/test_vdbecov.c 5c426d9cd2b351f5f9ceb30cabf8c64a63bfcad644c507e0bd9ce2f6ae1a3bf3
F src/test_vfs.c b4135c1308516adf0dfd494e6d6c33114e03732be899eace0502919b674586b5
F src/test_windirent.c cbee2b9a118b56b5a26b99c895adcbc861587bc66d24b88d1ad6e4c1d09dad7b
F src/test_windirent.h f8245d8002aa0d4322192d35b0f8bbfc757479e90d60fd0beb386d3913f72cdd
F src/test_window.c 6d80e11fba89a1796525e6f0048ff0c7789aa2c6b0b11c80827dc1437bd8ea72
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
@@ -853,15 +851,15 @@ F src/update.c 3e5e7ff66fa19ebe4d1b113d480639a24cc1175adbefabbd1a948a07f28e37cf
F src/upsert.c 215328c3f91623c520ec8672c44323553f12caeb4f01b1090ebdca99fdf7b4f1
F src/utf.c 7267c3fb9e2467020507601af3354c2446c61f444387e094c779dccd5ca62165
F src/util.c 36fb1150062957280777655976f3f9a75db236cb8207a0770ceae8d5ec17fcd3
F src/vacuum.c d580ceb395c1ae3d59da41cbfea60683ff7dd2b94ddf4d0f5657620159e2eeb7
F src/vdbe.c 0feab5781141acca67bd5de84172fff902304274ec5cfe58609f005b8d160050
F src/vdbe.h 31eddcffc1d14c76c2a20fe4e137e1ee43d44f370896fae14a067052801a3625
F src/vdbeInt.h 5446f60e89b2aa7cdf3ab0ec4e7b01b8732cd9d52d9092a0b8b1bf700768f784
F src/vdbeapi.c 28fab30ed0acc981aecfdcaab0a421503609078e29850eb28494816682baf0a7
F src/vdbeaux.c 948c379976885a073b54cc7d8ffda087dc1a1095d1f5bb8df218796f8c933ac3
F src/vdbeblob.c b1b4032cac46b41e44b957c4d00aee9851f862dfd85ecb68116ba49884b03dfd
F src/vacuum.c 1bacdd0a81d2b5dc1c508fbf0d938c89fa78dd8d5b46ec92686d44030d4f4789
F src/vdbe.c e505b8b879a330e8dafbe3ed9582eae2fc671b44a64748d1b58c07e4e0f527da
F src/vdbe.h 93761ed7c6b8bc19524912fd9b9b587d41bf4f1d0ade650a00dadc10518d8958
F src/vdbeInt.h 0bc581a9763be385e3af715e8c0a503ba8422c2b7074922faf4bb0d6ae31b15e
F src/vdbeapi.c f9a4881a9674fec3fa13da35044a1484d3c4b95f9ec891cc8ffb02ef2b7a41df
F src/vdbeaux.c fd2c6b19a8892c31a2adc719f156f313560f9cc490cdbd04ff08fdae5d7aedb7
F src/vdbeblob.c b3f0640db9642fbdc88bd6ebcc83d6009514cafc98f062f675f2c8d505d82692
F src/vdbemem.c e67d9c6484d868c879d20c70d00bf4a9058082f1d4058607ca15d50eb3aebc21
F src/vdbesort.c 706acdc581944cf6381f75c0ccf40f2debf71cdd51c5056592f3b74a1a0c3624
F src/vdbesort.c cb6f472e83ca12c46aa7de0ac0a9d11458b357986f2617a1c90dfb19a542ecbe
F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf823
F src/vdbevtab.c fc46b9cbd759dc013f0b3724549cc0d71379183c667df3a5988f7e2f1bd485f3
F src/vtab.c 828221bdbeaaa6d62126ee6d07fd4ec0d09dcaea846f87ad01944d8b7e548859
@@ -869,10 +867,10 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c 20be6f0a25a80b7897cf2a5369bfd37ef198e6f0b6cdef16d83eee856056b159
F src/wal.h ba252daaa94f889f4b2c17c027e823d9be47ce39da1d3799886bbd51f0490452
F src/walker.c d5006d6b005e4ea7302ad390957a8d41ed83faa177e412f89bc5600a7462a014
F src/where.c 09fea7e821556e8f8851b68edc39d14becb6ee64bea88e34b05ceeaadcdfaecd
F src/whereInt.h ecdbfb5551cf394f04ec7f0bc7ad963146d80eee3071405ac29aa84950128b8e
F src/wherecode.c 66684a11713667f532a69214ac552acee7e0825e2f1b71abaebe05511d190fb4
F src/whereexpr.c 1c60db88b6e8472f4864b98014d1b2d9d16bdd42d5d181ec515acbcdeddc18db
F src/where.c 6a9266dd1a559d48d8c7ca670a3e80143c7913153f7d1ceb0a4eca1087318951
F src/whereInt.h 8d94cb116c9e06205c3d5ac87af065fc044f8cf08bfdccd94b6ea1c1308e65da
F src/wherecode.c 2a2d2993fd98c46f525f71b3bfd330fde73d8613aa0ff3e20402dd1fc63470af
F src/whereexpr.c d007dc41364de5902181739632380afd671e14f0c5cc9978e64a2c6df8f28c6c
F src/window.c d01227141f622f24fbe36ca105fbe6ef023f9fd98f1ccd65da95f88886565db5
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test 4d7a34d328e58ca2a2d78fd76c27614a41ca7ddf4312ded9c68c04f430b3b47d
@@ -964,7 +962,7 @@ F test/bestindex8.test b63a4f171a2c83d481bb14c431a8b72e85d27b2ffdaa0435a95d58ca9
F test/bestindex9.test 1a4b93db117fd8abe74ae9be982f86aa72f01e60cd4ac541e6ede39673a451a0
F test/bestindexA.test e1b5def6b190797cacf008e6815ffb78fb30261999030d60a728d572eef44c7f
F test/bestindexB.test 328b97b69cd1a20928d5997f9ecb04d2e00f1d18e19ab27f9e9adb44d7bc51ce
F test/bestindexC.test 2df6ada16d8f00d9bb6a9664d9c323560aeed0e0ebc7a32b99d85d70037fd250
F test/bestindexC.test 95b4a527b1a5d07951d731604a6d4cf7e5a806b39cea0e7819d4c9667e11c3fc
F test/bestindexD.test 6a8f6f84990bcf17dfa59652a1f935beddb7afd96f8302830fbc86b0a13df3c3
F test/between.test b9a65fb065391980119e8a781a7409d3fcf059d89968279c750e190a9a1d5263
F test/bigfile.test aa74f4e5db51c8e54a1d9de9fa65d01d1eb20b59
@@ -1084,6 +1082,7 @@ F test/dbfuzz.c fc566102f72c8af84ae8077b4faf7f056c571e6fa7a32e98b66e42b7505f47b6
F test/dbfuzz001.test 6c9a4622029d69dc38926f115864b055cb2f39badd25ec22cbfb130c8ba8e9c3
F test/dbfuzz2-seed1.db e6225c6f3d7b63f9c5b6867146a5f329d997ab105bee64644dc2b3a2f2aebaee
F test/dbfuzz2.c 4b3c12de4d98b1b2d908ab03d217d4619e47c8b23d5e67f8a6f2b1bdee7cae23
F test/dblwidth-a.sql eb4141518610e52f931a55a984310075e98dc31eee5a28ae806b1e35377be85a
F test/dbpage.test 63fab1eb026bada121107e53436fa749bbf83281dc9dea17af422f7a5c0f289f
F test/dbpagefault.test ea39de2ca86041a9c6df1135645180a76d0a8da93ac159e2fafe38e39636530b
F test/dbstatus.test 4a4221a883025ffd39696b3d1b3910b928fb097d77e671351acb35f3aed42759
@@ -1132,7 +1131,7 @@ F test/enc.test b5503a87b31cea8a5084c6e447383f9ca08933bd2f29d97b6b6201081b2343eb
F test/enc2.test 872afe58db772e7dfa1ad8e0759f8cc820e9efc8172d460fae83023101c2e435
F test/enc3.test 55ef64416d72975c66167310a51dc9fc544ba3ae4858b8d5ab22f4cb6500b087
F test/enc4.test c8f1ce3618508fd0909945beb8b8831feef2c020
F test/eqp.test 82f221e8cd588434d7f3bba9a0f4c78cbe7a541615a41632e12f50608bfb4a99
F test/eqp.test 746db9fe11629a0d00328e1721cc2a2e4726d574b677ab14de35fd914f54cc82
F test/eqp2.test 6e8996148de88f0e7670491e92e712a2920a369b4406f21a27c3c9b6a46b68dd
F test/errmsg.test eae9f091eb39ce7e20305de45d8e5d115b68fa856fba4ea6757b6ca3705ff7f9
F test/errofst1.test 6da78363739ba8991f498396ab331b5d64e7ab5c4172c12b5884683ef523ac53
@@ -1141,6 +1140,9 @@ F test/exclusive.test 7ff63be7503990921838d5c9f77f6e33e68e48ed1a9d48cd28745bf650
F test/exclusive2.test cd70b1d9c6fffd336f9795b711dcc5d9ceba133ad3f7001da3fda63615bdc91e
F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7
F test/exists.test 79a75323c78f02bbe9c251ea502a092f9ef63dac
F test/existsexpr.test 40ddd9500109579dd949cd15bbb4e3a88f79f905d1f31905b9493651f60aacf6
F test/existsexpr2.test dc23e76389eff3d29f6488ff733012a3560cd67ec8cfaecbecd52cced5d5af11
F test/existsfault.test ff41c11f3052c1bbd4f8dd557802310026253d67d7c4e3a180c16d2f0862973e
F test/expr.test 4ada8eb822c45ef27a36851a258004d43c1e95e7c82585a1217e732084e4482c
F test/expr2.test c27327ae9c017a7ff6280123f67aff496f912da74d78c888926d68b46ec75fd8
F test/exprfault.test da33606d799718e2f8e34efd0e5858884a1ad87f608774c552a7f5517cc27181
@@ -1301,7 +1303,7 @@ F test/genesis.tcl 1e2e2e8e5cc4058549a154ff1892fe5c9de19f98
F test/having.test a89236dd8d55aa50c4805f82ac9daf64d477a44d712d8209c118978d0ca21ec9
F test/hexlit.test 4a6a5f46e3c65c4bf1fa06f5dd5a9507a5627751
F test/hidden.test 23c1393a79e846d68fd902d72c85d5e5dcf98711
F test/hook.test 3481a68009fe143e3363fca922f6fc7a1e1f3776c51e42777f1a01b26ad2a9c8
F test/hook.test 2d89bf9480646feb8093be3a58ea502d6521906779ed960de31dd9c4502c0541
F test/hook2.test b9ff3b8c6519fb67f33192f1afe86e7782ee4ac8
F test/icu.test 8da7d52cd9722c82f33b0466ed915460cb03c23a38f18a9a2d3ff97da9a4a8c0
F test/ieee754.test b0945d12be7d255f3dfa18e2511b17ca37e0edd2b803231c52d05b86c04ab26e
@@ -1316,7 +1318,7 @@ F test/in7.test d9efdee00b074a60c6343993b2eda78bc369ab080dad864513c73f8aca89d566
F test/incrblob.test c9b96afc292aeff43d6687bcb09b0280aa599822
F test/incrblob2.test a494c9e848560039a23974b9119cfc2cf3ad3bd15cc2694ee6367ae537ef8f1f
F test/incrblob3.test 67621a04b3084113bf38ce03797d70eca012d9d8f948193b8f655df577b0da6f
F test/incrblob4.test 21a52a6843a56cdcce968c6a86b72a7066d0e6ba
F test/incrblob4.test 10f4537febe1774c02f8d490d393322b4a50898d4027868b67055e9c3adc22db
F test/incrblob_err.test 89372a28f1d98254f03fed705f9efcd34ef61a674df16d2dbb4726944a2de5e9
F test/incrblobfault.test de274b1e329169c2c3438f9528994807ea8201ebf38ae9f157d34bf3ec0cc549
F test/incrcorrupt.test 6c567fbf870aa9e91866fe52ce6f200cd548939a
@@ -1388,7 +1390,7 @@ F test/json/json-generator.tcl dc0dd0f393800c98658fc4c47eaa6af29d4e17527380cd286
F test/json/json-q1.txt 65f9d1cdcc4cffa9823fb73ed936aae5658700cd001fde448f68bfb91c807307
F test/json/json-speed-check.sh 7d5898808ce7542762318306ae6075a30f5e7ee115c4a409f487e123afe91d88 x
F test/json/jsonb-q1.txt 1e180fe6491efab307e318b22879e3a736ac9a96539bbde7911a13ee5b33abc7
F test/json101.test 8237a484c256965eab1678fd950a32ac56325bb7d0dadbd095a46b0ddd95d62b
F test/json101.test cf53254f0f0c1399a01b21fc58fee0e63a12a556be91b9ee9faccdb8b82c083c
F test/json102.test 9b2e5ada10845ff84853b3feaae2ce51ce7145ae458f74c6a6cecc6ef6ee3ae1
F test/json103.test 355746a6b66aa438f214b4fae454b13068fad2444b5f693e0d538ad1c059b264
F test/json104.test 1b844a70cddcfa2e4cd81a5db0657b2e61e7f00868310f24f56a9ba0114348c1
@@ -1495,7 +1497,7 @@ F test/notify1.test 669b2b743618efdc18ca4b02f45423d5d2304abf
F test/notify2.test 2ecabaa1305083856b7c39cf32816b612740c161
F test/notify3.test 796c7b7157f55c93b4e672b724e9c923a6fc6aa72ac419379a623e2350472e22
F test/notnull.test a37b663d5bb728d66fc182016613fb8e4a0a4bbf3d75b8876a7527f7d4ed3f18
F test/notnull2.test 2ac7b4e04917148c7a1a9ed36df20150175ce942f07f5714375b29acbaca7106
F test/notnull2.test 5b7dd6e82c409b2d011ad6acf19ae4bf0816a9c69ccf600b529d7405d7c49874
F test/notnullfault.test fc4bb7845582a2b3db376001ef49118393b1b11abe0d24adb03db057ee2b73d5
F test/null.test b7ff206a1c60fe01aa2abd33ef9ea83c93727d993ca8a613de86e925c9f2bc6f
F test/nulls1.test 7a5e4346ee4285034100b4cd20e6784f16a9d6c927e44ecdf10034086bbee9c9
@@ -1702,7 +1704,7 @@ F test/statfault.test 064f43379e4992b5221b7d9ac887c313b3191f85cce605d78e416fc404
F test/stmt.test 54ed2cc0764bf3e48a058331813c3dbd19fc1d0827c3d8369914a5d8f564ec75
F test/stmtrand.test 340e2ea4841c5cdc02d36e33739769c5d907ab529b12bb535407def0e413ca17
F test/stmtvtab1.test 6873dfb24f8e79cbb5b799b95c2e4349060eb7a3b811982749a84b359468e2d5
F test/strict1.test 4d2b492152b984fd7e8196d23eb88e2ccb0ef9e46ca2f96c2ce7147ceef9d168
F test/strict1.test a7f9091603fe71cdc62baab0766684cba12a97ec69bfbb70be965532669cd77a
F test/strict2.test b22c7a98b5000aef937f1990776497f0e979b1a23bc4f63e2d53b00e59b20070
F test/subjournal.test 8d4e2572c0ee9a15549f0d8e40863161295107e52f07a3e8012a2e1fdd093c49
F test/subquery.test 23087f9b1c15ab9cc5231d04946bdebc51db527c95eb9d7434a2222127e17a84
@@ -1725,7 +1727,7 @@ F test/tabfunc01.test 8a484fe8b19fc24844f72ca1ceb7c9ae8c9a6bca000a5c6ccab5d89f5c
F test/table.test e87294bf1c80bfd7792142b84ab32ea5beb4f3f71e535d7fb263a6b2068377bf
F test/tableapi.test e37c33e6be2276e3a96bb54b00eea7f321277115d10e5b30fdb52a112b432750
F test/tableopts.test dba698ba97251017b7c80d738c198d39ab747930
F test/tclsqlite.test ad0bbd92edabe64cc91d990a0748142fe5ab962d74ac71fa3bfa94d50d2f4c87
F test/tclsqlite.test 3f697424cfc1cdc9c076ec0cadb0e700f059400a3e3ce134b7d856fc9f880e1c
F test/tempdb.test 4cdaa23ddd8acb4d79cbb1b68ccdfd09b0537aaba909ca69a876157c2a2cbd08
F test/tempdb2.test 353864e96fd3ae2f70773d0ffbf8b1fe48589b02c2ec05013b540879410c3440
F test/tempfault.test 0c0d349c9a99bf5f374655742577f8712c647900
@@ -1959,7 +1961,7 @@ F test/uri.test 1250724af9beeed2d6c3716f5b990c483200c54f408d3c0ec9543a3c7961f8fc
F test/uri2.test 9d3ba7a53ee167572d53a298ee4a5d38ec4a8fb7
F test/utf16align.test 9fde0bb5d3a821594aa68c6829ab9c5453a084384137ebb9f6153e2d678039da
F test/vacuum-into.test 5a489714feecfdabfc7b293be4111564a173dee92c0d6818dd0207f3ade65783
F test/vacuum.test ce91c39f7f91a4273bf620efad21086b5aa6ef1d
F test/vacuum.test f3b2257a4fcd659513c866a5d9e5f70999cc58fd5d74e979290385fa350b79ee
F test/vacuum2.test 9fd45ce6ce29f5614c249e03938d3567c06a9e772d4f155949f8eafe2d8af520
F test/vacuum3.test d9d9a04ee58c485b94694fd4f68cffaba49c32234fdefe1ac1a622c5e17d4ce3
F test/vacuum4.test 7ea76b769fffeb41f925303b04cbcf5a5bbeabe55e4c60ae754ff24eeeb7c010
@@ -2125,7 +2127,7 @@ F tool/GetTclKit.bat d84033c6a93dfe735d247f48ba00292a1cc284dcf69963e5e672444e045
F tool/Replace.cs 02c67258801c2fb5f63231e0ac0f220b4b36ba91
F tool/build-all-msvc.bat 1960a7a3e5d8176c4329e31476f6e3dfa9543675355fa9020a569f4452628458 x
F tool/build-shell.sh 369c4b171cc877ad974fef691e4da782b4c1e99fe8f4361316c735f64d49280f
F tool/buildtclext.tcl 85d1bcd5410d0a4c739e2d013106f03674d082cc4d573bcc57a68546b6db67cd
F tool/buildtclext.tcl d09b753d7858314104eeaf5f4def85d35784470279809e47a633f142226f2b3f
F tool/cg_anno.tcl c1f875f5a4c9caca3d59937b16aff716f8b1883935f1b4c9ae23124705bc8099 x
F tool/checkSpacing.c 810e51703529a204fc4e1eb060e9ab663e3c06d2
F tool/cktclsh.sh 6075eef9c6b9ba4b38fef2ca2a66d25f2311bd3c610498d18a9b01f861629cca
@@ -2145,7 +2147,7 @@ F tool/genfkey.README e550911fa984c8255ebed2ef97824125d83806eb5232582700de949edf
F tool/genfkey.test b6afd7b825d797a1e1274f519ab5695373552ecad5cd373530c63533638a5a4f
F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
F tool/index_usage.c f62a0c701b2c7ff2f3e21d206f093c123f222dbf07136a10ffd1ca15a5c706c5
F tool/lemon.c 00535f27e61ae8200ba8402c1753483de39eef2f59b60da8b7951c458a32e017
F tool/lemon.c 8f6c122e5727cb0e5f302b8efc91489b1947a8d98206d7a1b1cfc0ed685b6e7c
F tool/lempar.c bdffd3b233a4e4e78056c9c01fadd2bb3fe902435abde3bce3d769fdf0d5cca2
F tool/libvers.c caafc3b689638a1d88d44bc5f526c2278760d9b9
F tool/loadfts.c c3c64e4d5e90e8ba41159232c2189dba4be7b862
@@ -2162,7 +2164,7 @@ F tool/mkopcodec.tcl 33d20791e191df43209b77d37f0ff0904620b28465cca6990cf8d60da61
F tool/mkopcodeh.tcl 2b4e6967a670ef21bf53a164964c35c6163277d002a4c6f56fa231d68c88d023
F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa
F tool/mkpragmatab.tcl 3801ce32f8c55fe63a3b279f231fb26c2c1a2ea9a09d2dd599239d87a609acec
F tool/mkshellc.tcl 9ce74de0fa904a2c56a96f8d8b5261246bacb0eaa8d7e184f9e18ff94145ebbc
F tool/mkshellc.tcl bab0a72a68384181a5706712dfdf6815f6526446d4e8aacace2de5e80cda91b2
F tool/mksourceid.c 36aa8020014aed0836fd13c51d6dc9219b0df1761d6b5f58ff5b616211b079b9
F tool/mksqlite3c-noext.tcl 351c55256213154cabb051a3c870ef9f4487de905015141ae50dc7578a901b84
F tool/mksqlite3c.tcl f11b63445c4840509248bd4aa151a81aea25d5415fef71943c8d436eba4f3b3c
@@ -2191,7 +2193,7 @@ F tool/spellsift.tcl 52b4b04dc4333c7ab024f09d9d66ed6b6f7c6eb00b38497a09f338fa55d
F tool/split-sqlite3c.tcl 4969fd642dad0ea483e4e104163021d92baf98f6a8eac981fe48525f9b873430
F tool/sqldiff.c 134be7866be19f8beb32043d5aea5657f01aaeae2df8d33d758ff722c78666b9
F tool/sqlite3_analyzer.c.in 14f02cb5ec3c264cd6107d1f1dad77092b1cf440fc196c30b69ae87b56a1a43b
F tool/sqlite3_rsync.c 4e152221a51ed2974f08100d86500c2cda549472e48e09699a8011bfb956d00c
F tool/sqlite3_rsync.c c1e1a737ebb458a4ccbbaa21e5eaa878448fef1aaa71b879b9a685785e28987f
F tool/sqltclsh.c.in 1bcc2e9da58fadf17b0bf6a50e68c1159e602ce057210b655d50bad5aaaef898
F tool/sqltclsh.tcl 862f4cf1418df5e1315b5db3b5ebe88969e2a784525af5fbf9596592f14ed848
F tool/src-verify.c 6c655d9a8d6b30f3648fc78a79bf3838ed68f8543869d380c43ea9f17b3b8501
@@ -2209,10 +2211,8 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350
F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
F tool/warnings.sh 1ad0169b022b280bcaaf94a7fa231591be96b514230ab5c98fbf15cd7df842dd
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
P f39a0865d336d4a964627d4819ce7ba8dafd43a0e8e2a4431ea431ef73dd96c3
R 5c3da0295a5c868b2c70cbb25c7978d3
T +sym-release *
T +sym-version-3.50.4 *
P 840646df0696706bd4dd7a04dfc8b16ad80c24cbcbc57a22c9e65a93cc17af2b
R e35575e41e9bd119ef19709aeaa4c684
U drh
Z e0e73bda1cca0dba2213fe2c15c764d6
Z 4da98ddcb106ab62869cc3825500f67f
# Remove this line to create a well-formed Fossil manifest.
+1 -1
View File
@@ -1 +1 @@
4d8adfb30e03f9cf27f800a2c1ba3c48fb4ca1b08b0f5ed59a4d5ecbf45e20a3
1b9b124f9a35ebd1ac4ea70ef1ee08a4c82c11da690d4164f6b785a6fd9730d9
+96 -14
View File
@@ -107,6 +107,7 @@ struct Bitvec {
} u;
};
/*
** Create a new bitmap object able to handle bits between 0 and iSize,
** inclusive. Return a pointer to the new object. Return NULL if
@@ -295,6 +296,52 @@ u32 sqlite3BitvecSize(Bitvec *p){
return p->iSize;
}
#ifdef SQLITE_DEBUG
/*
** Show the content of a Bitvec option and its children. Indent
** everything by n spaces. Add x to each bitvec value.
**
** From a debugger such as gdb, one can type:
**
** call sqlite3ShowBitvec(p)
**
** For some Bitvec p and see a recursive view of the Bitvec's content.
*/
static void showBitvec(Bitvec *p, int n, unsigned x){
int i;
if( p==0 ){
printf("NULL\n");
return;
}
printf("Bitvec 0x%p iSize=%u", p, p->iSize);
if( p->iSize<=BITVEC_NBIT ){
printf(" bitmap\n");
printf("%*s bits:", n, "");
for(i=1; i<=BITVEC_NBIT; i++){
if( sqlite3BitvecTest(p,i) ) printf(" %u", x+(unsigned)i);
}
printf("\n");
}else if( p->iDivisor==0 ){
printf(" hash with %u entries\n", p->nSet);
printf("%*s bits:", n, "");
for(i=0; i<BITVEC_NINT; i++){
if( p->u.aHash[i] ) printf(" %u", x+(unsigned)p->u.aHash[i]);
}
printf("\n");
}else{
printf(" sub-bitvec with iDivisor=%u\n", p->iDivisor);
for(i=0; i<BITVEC_NPTR; i++){
if( p->u.apSub[i]==0 ) continue;
printf("%*s apSub[%d]=", n, "", i);
showBitvec(p->u.apSub[i], n+4, i*p->iDivisor);
}
}
}
void sqlite3ShowBitvec(Bitvec *p){
showBitvec(p, 0, 0);
}
#endif
#ifndef SQLITE_UNTESTABLE
/*
** Let V[] be an array of unsigned characters sufficient to hold
@@ -306,6 +353,7 @@ u32 sqlite3BitvecSize(Bitvec *p){
#define CLEARBIT(V,I) V[I>>3] &= ~(BITVEC_TELEM)(1<<(I&7))
#define TESTBIT(V,I) (V[I>>3]&(1<<(I&7)))!=0
/*
** This routine runs an extensive test of the Bitvec code.
**
@@ -314,7 +362,7 @@ u32 sqlite3BitvecSize(Bitvec *p){
** by 0, 1, or 3 operands, depending on the opcode. Another
** opcode follows immediately after the last operand.
**
** There are 6 opcodes numbered from 0 through 5. 0 is the
** There are opcodes numbered starting with 0. 0 is the
** "halt" opcode and causes the test to end.
**
** 0 Halt and return the number of errors
@@ -323,18 +371,25 @@ u32 sqlite3BitvecSize(Bitvec *p){
** 3 N Set N randomly chosen bits
** 4 N Clear N randomly chosen bits
** 5 N S X Set N bits from S increment X in array only, not in bitvec
** 6 Invoice sqlite3ShowBitvec() on the Bitvec object so far
** 7 X Show compile-time parameters and the hash of X
**
** The opcodes 1 through 4 perform set and clear operations are performed
** on both a Bitvec object and on a linear array of bits obtained from malloc.
** Opcode 5 works on the linear array only, not on the Bitvec.
** Opcode 5 is used to deliberately induce a fault in order to
** confirm that error detection works.
** confirm that error detection works. Opcodes 6 and greater are
** state output opcodes. Opcodes 6 and greater are no-ops unless
** SQLite has been compiled with SQLITE_DEBUG.
**
** At the conclusion of the test the linear array is compared
** against the Bitvec object. If there are any differences,
** an error is returned. If they are the same, zero is returned.
**
** If a memory allocation error occurs, return -1.
**
** sz is the size of the Bitvec. Or if sz is negative, make the size
** 2*(unsigned)(-sz) and disabled the linear vector check.
*/
int sqlite3BitvecBuiltinTest(int sz, int *aOp){
Bitvec *pBitvec = 0;
@@ -345,10 +400,15 @@ int sqlite3BitvecBuiltinTest(int sz, int *aOp){
/* Allocate the Bitvec to be tested and a linear array of
** bits to act as the reference */
pBitvec = sqlite3BitvecCreate( sz );
pV = sqlite3MallocZero( (7+(i64)sz)/8 + 1 );
if( sz<=0 ){
pBitvec = sqlite3BitvecCreate( 2*(unsigned)(-sz) );
pV = 0;
}else{
pBitvec = sqlite3BitvecCreate( sz );
pV = sqlite3MallocZero( (7+(i64)sz)/8 + 1 );
}
pTmpSpace = sqlite3_malloc64(BITVEC_SZ);
if( pBitvec==0 || pV==0 || pTmpSpace==0 ) goto bitvec_end;
if( pBitvec==0 || pTmpSpace==0 || (pV==0 && sz>0) ) goto bitvec_end;
/* NULL pBitvec tests */
sqlite3BitvecSet(0, 1);
@@ -357,6 +417,24 @@ int sqlite3BitvecBuiltinTest(int sz, int *aOp){
/* Run the program */
pc = i = 0;
while( (op = aOp[pc])!=0 ){
if( op>=6 ){
#ifdef SQLITE_DEBUG
if( op==6 ){
sqlite3ShowBitvec(pBitvec);
}else if( op==7 ){
printf("BITVEC_SZ = %d (%d by sizeof)\n",
BITVEC_SZ, (int)sizeof(Bitvec));
printf("BITVEC_USIZE = %d\n", (int)BITVEC_USIZE);
printf("BITVEC_NELEM = %d\n", (int)BITVEC_NELEM);
printf("BITVEC_NBIT = %d\n", (int)BITVEC_NBIT);
printf("BITVEC_NINT = %d\n", (int)BITVEC_NINT);
printf("BITVEC_MXHASH = %d\n", (int)BITVEC_MXHASH);
printf("BITVEC_NPTR = %d\n", (int)BITVEC_NPTR);
}
#endif
pc++;
continue;
}
switch( op ){
case 1:
case 2:
@@ -378,12 +456,12 @@ int sqlite3BitvecBuiltinTest(int sz, int *aOp){
pc += nx;
i = (i & 0x7fffffff)%sz;
if( (op & 1)!=0 ){
SETBIT(pV, (i+1));
if( pV ) SETBIT(pV, (i+1));
if( op!=5 ){
if( sqlite3BitvecSet(pBitvec, i+1) ) goto bitvec_end;
}
}else{
CLEARBIT(pV, (i+1));
if( pV ) CLEARBIT(pV, (i+1));
sqlite3BitvecClear(pBitvec, i+1, pTmpSpace);
}
}
@@ -393,14 +471,18 @@ int sqlite3BitvecBuiltinTest(int sz, int *aOp){
** match (rc==0). Change rc to non-zero if a discrepancy
** is found.
*/
rc = sqlite3BitvecTest(0,0) + sqlite3BitvecTest(pBitvec, sz+1)
+ sqlite3BitvecTest(pBitvec, 0)
+ (sqlite3BitvecSize(pBitvec) - sz);
for(i=1; i<=sz; i++){
if( (TESTBIT(pV,i))!=sqlite3BitvecTest(pBitvec,i) ){
rc = i;
break;
if( pV ){
rc = sqlite3BitvecTest(0,0) + sqlite3BitvecTest(pBitvec, sz+1)
+ sqlite3BitvecTest(pBitvec, 0)
+ (sqlite3BitvecSize(pBitvec) - sz);
for(i=1; i<=sz; i++){
if( (TESTBIT(pV,i))!=sqlite3BitvecTest(pBitvec,i) ){
rc = i;
break;
}
}
}else{
rc = 0;
}
/* Free allocated structure */
+45 -11
View File
@@ -872,7 +872,7 @@ static int btreeMoveto(
assert( nKey==(i64)(int)nKey );
pIdxKey = sqlite3VdbeAllocUnpackedRecord(pKeyInfo);
if( pIdxKey==0 ) return SQLITE_NOMEM_BKPT;
sqlite3VdbeRecordUnpack(pKeyInfo, (int)nKey, pKey, pIdxKey);
sqlite3VdbeRecordUnpack((int)nKey, pKey, pIdxKey);
if( pIdxKey->nField==0 || pIdxKey->nField>pKeyInfo->nAllField ){
rc = SQLITE_CORRUPT_BKPT;
}else{
@@ -1929,10 +1929,10 @@ static int freeSpace(MemPage *pPage, int iStart, int iSize){
assert( pPage->pBt!=0 );
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
assert( CORRUPT_DB || iStart>=pPage->hdrOffset+6+pPage->childPtrSize );
assert( CORRUPT_DB || iEnd <= pPage->pBt->usableSize );
assert( CORRUPT_DB || iEnd <= (int)pPage->pBt->usableSize );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
assert( iSize>=4 ); /* Minimum cell size is 4 */
assert( CORRUPT_DB || iStart<=pPage->pBt->usableSize-4 );
assert( CORRUPT_DB || iStart<=(int)pPage->pBt->usableSize-4 );
/* The list of freeblocks must be in ascending order. Find the
** spot on the list where iStart should be inserted.
@@ -2856,6 +2856,7 @@ static int removeFromSharingList(BtShared *pBt){
sqlite3_mutex_leave(pMainMtx);
return removed;
#else
UNUSED_PARAMETER( pBt );
return 1;
#endif
}
@@ -3073,6 +3074,10 @@ int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){
sqlite3BtreeEnter(p);
pBt->nReserveWanted = (u8)nReserve;
x = pBt->pageSize - pBt->usableSize;
if( x==nReserve && (pageSize==0 || (u32)pageSize==pBt->pageSize) ){
sqlite3BtreeLeave(p);
return SQLITE_OK;
}
if( nReserve<x ) nReserve = x;
if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){
sqlite3BtreeLeave(p);
@@ -5662,6 +5667,30 @@ int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
return rc;
}
/* Set *pRes to 1 (true) if the BTree pointed to by cursor pCur contains zero
** rows of content. Set *pRes to 0 (false) if the table contains content.
** Return SQLITE_OK on success or some error code (ex: SQLITE_NOMEM) if
** something goes wrong.
*/
int sqlite3BtreeIsEmpty(BtCursor *pCur, int *pRes){
int rc;
assert( cursorOwnsBtShared(pCur) );
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
if( pCur->eState==CURSOR_VALID ){
*pRes = 0;
return SQLITE_OK;
}
rc = moveToRoot(pCur);
if( rc==SQLITE_EMPTY ){
*pRes = 1;
rc = SQLITE_OK;
}else{
*pRes = 0;
}
return rc;
}
#ifdef SQLITE_DEBUG
/* The cursors is CURSOR_VALID and has BTCF_AtLast set. Verify that
** this flags are true for a consistent database.
@@ -5881,8 +5910,8 @@ moveto_table_finish:
}
/*
** Compare the "idx"-th cell on the page the cursor pCur is currently
** pointing to to pIdxKey using xRecordCompare. Return negative or
** Compare the "idx"-th cell on the page pPage against the key
** pointing to by pIdxKey using xRecordCompare. Return negative or
** zero if the cell is less than or equal pIdxKey. Return positive
** if unknown.
**
@@ -5897,12 +5926,11 @@ moveto_table_finish:
** a positive value as that will cause the optimization to be skipped.
*/
static int indexCellCompare(
BtCursor *pCur,
MemPage *pPage,
int idx,
UnpackedRecord *pIdxKey,
RecordCompare xRecordCompare
){
MemPage *pPage = pCur->pPage;
int c;
int nCell; /* Size of the pCell cell in bytes */
u8 *pCell = findCellPastPtr(pPage, idx);
@@ -6011,14 +6039,14 @@ int sqlite3BtreeIndexMoveto(
){
int c;
if( pCur->ix==pCur->pPage->nCell-1
&& (c = indexCellCompare(pCur, pCur->ix, pIdxKey, xRecordCompare))<=0
&& (c = indexCellCompare(pCur->pPage,pCur->ix,pIdxKey,xRecordCompare))<=0
&& pIdxKey->errCode==SQLITE_OK
){
*pRes = c;
return SQLITE_OK; /* Cursor already pointing at the correct spot */
}
if( pCur->iPage>0
&& indexCellCompare(pCur, 0, pIdxKey, xRecordCompare)<=0
&& indexCellCompare(pCur->pPage, 0, pIdxKey, xRecordCompare)<=0
&& pIdxKey->errCode==SQLITE_OK
){
pCur->curFlags &= ~(BTCF_ValidOvfl|BTCF_AtLast);
@@ -6235,7 +6263,7 @@ i64 sqlite3BtreeRowCountEst(BtCursor *pCur){
n = pCur->pPage->nCell;
for(i=0; i<pCur->iPage; i++){
n *= pCur->apPage[i]->nCell;
n *= pCur->apPage[i]->nCell+1;
}
return n;
}
@@ -8692,7 +8720,12 @@ static int balance_nonroot(
** of the right-most new sibling page is set to the value that was
** originally in the same field of the right-most old sibling page. */
if( (pageFlags & PTF_LEAF)==0 && nOld!=nNew ){
MemPage *pOld = (nNew>nOld ? apNew : apOld)[nOld-1];
MemPage *pOld;
if( nNew>nOld ){
pOld = apNew[nOld-1];
}else{
pOld = apOld[nOld-1];
}
memcpy(&apNew[nNew-1]->aData[8], &pOld->aData[8], 4);
}
@@ -11324,6 +11357,7 @@ void *sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){
*/
int sqlite3BtreeSchemaLocked(Btree *p){
int rc;
UNUSED_PARAMETER(p); /* only used in DEBUG builds */
assert( sqlite3_mutex_held(p->db->mutex) );
sqlite3BtreeEnter(p);
rc = querySharedCacheTableLock(p, SCHEMA_ROOT, READ_LOCK);
+1
View File
@@ -317,6 +317,7 @@ struct BtreePayload {
int sqlite3BtreeInsert(BtCursor*, const BtreePayload *pPayload,
int flags, int seekResult);
int sqlite3BtreeFirst(BtCursor*, int *pRes);
int sqlite3BtreeIsEmpty(BtCursor *pCur, int *pRes);
int sqlite3BtreeLast(BtCursor*, int *pRes);
int sqlite3BtreeNext(BtCursor*, int flags);
int sqlite3BtreeEof(BtCursor*);
+10 -4
View File
@@ -5137,16 +5137,22 @@ void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pIndexedBy){
** are deleted by this function.
*/
SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, SrcList *p2){
assert( p1 && p1->nSrc==1 );
assert( p1 );
assert( p2 || pParse->nErr );
assert( p2==0 || p2->nSrc>=1 );
testcase( p1->nSrc==0 );
if( p2 ){
SrcList *pNew = sqlite3SrcListEnlarge(pParse, p1, p2->nSrc, 1);
int nOld = p1->nSrc;
SrcList *pNew = sqlite3SrcListEnlarge(pParse, p1, p2->nSrc, nOld);
if( pNew==0 ){
sqlite3SrcListDelete(pParse->db, p2);
}else{
p1 = pNew;
memcpy(&p1->a[1], p2->a, p2->nSrc*sizeof(SrcItem));
memcpy(&p1->a[nOld], p2->a, p2->nSrc*sizeof(SrcItem));
assert( nOld==1 || (p2->a[0].fg.jointype & JT_LTORJ)==0 );
assert( p1->nSrc>=1 );
p1->a[0].fg.jointype |= (JT_LTORJ & p2->a[0].fg.jointype);
sqlite3DbFree(pParse->db, p2);
p1->a[0].fg.jointype |= (JT_LTORJ & p1->a[1].fg.jointype);
}
}
return p1;
+7 -6
View File
@@ -227,7 +227,7 @@ static int dbpageEof(sqlite3_vtab_cursor *pCursor){
** idxStr is not used
*/
static int dbpageFilter(
sqlite3_vtab_cursor *pCursor,
sqlite3_vtab_cursor *pCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
@@ -237,10 +237,11 @@ static int dbpageFilter(
sqlite3 *db = pTab->db;
Btree *pBt;
(void)idxStr;
UNUSED_PARAMETER(idxStr);
UNUSED_PARAMETER(argc);
/* Default setting is no rows of result */
pCsr->pgno = 1;
pCsr->pgno = 1;
pCsr->mxPgno = 0;
if( idxNum & 2 ){
@@ -275,8 +276,8 @@ static int dbpageFilter(
}
static int dbpageColumn(
sqlite3_vtab_cursor *pCursor,
sqlite3_context *ctx,
sqlite3_vtab_cursor *pCursor,
sqlite3_context *ctx,
int i
){
DbpageCursor *pCsr = (DbpageCursor *)pCursor;
+203 -66
View File
@@ -2373,6 +2373,86 @@ Expr *sqlite3ExprSimplifiedAndOr(Expr *pExpr){
return pExpr;
}
/*
** Return true if it might be advantageous to compute the right operand
** of expression pExpr first, before the left operand.
**
** Normally the left operand is computed before the right operand. But if
** the left operand contains a subquery and the right does not, then it
** might be more efficient to compute the right operand first.
*/
static int exprEvalRhsFirst(Expr *pExpr){
if( ExprHasProperty(pExpr->pLeft, EP_Subquery)
&& !ExprHasProperty(pExpr->pRight, EP_Subquery)
){
return 1;
}else{
return 0;
}
}
/*
** Compute the two operands of a binary operator.
**
** If either operand contains a subquery, then the code strives to
** compute the operand containing the subquery second. If the other
** operand evalutes to NULL, then a jump is made. The address of the
** IsNull operand that does this jump is returned. The caller can use
** this to optimize the computation so as to avoid doing the potentially
** expensive subquery.
**
** If no optimization opportunities exist, return 0.
*/
static int exprComputeOperands(
Parse *pParse, /* Parsing context */
Expr *pExpr, /* The comparison expression */
int *pR1, /* OUT: Register holding the left operand */
int *pR2, /* OUT: Register holding the right operand */
int *pFree1, /* OUT: Temp register to free if not zero */
int *pFree2 /* OUT: Another temp register to free if not zero */
){
int addrIsNull;
int r1, r2;
Vdbe *v = pParse->pVdbe;
assert( v!=0 );
/*
** If the left operand contains a (possibly expensive) subquery and the
** right operand does not and the right operation might be NULL,
** then compute the right operand first and do an IsNull jump if the
** right operand evalutes to NULL.
*/
if( exprEvalRhsFirst(pExpr) && sqlite3ExprCanBeNull(pExpr->pRight) ){
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, pFree2);
addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, r2);
VdbeComment((v, "skip left operand"));
VdbeCoverage(v);
}else{
r2 = 0; /* Silence a false-positive uninit-var warning in MSVC */
addrIsNull = 0;
}
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, pFree1);
if( addrIsNull==0 ){
/*
** If the right operand contains a subquery and the left operand does not
** and the left operand might be NULL, then check the left operand do
** an IsNull check on the left operand before computing the right
** operand.
*/
if( ExprHasProperty(pExpr->pRight, EP_Subquery)
&& sqlite3ExprCanBeNull(pExpr->pLeft)
){
addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, r1);
VdbeComment((v, "skip right operand"));
VdbeCoverage(v);
}
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, pFree2);
}
*pR1 = r1;
*pR2 = r2;
return addrIsNull;
}
/*
** pExpr is a TK_FUNCTION node. Try to determine whether or not the
** function is a constant function. A function is constant if all of
@@ -3817,17 +3897,23 @@ int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
VdbeComment((v, "Init EXISTS result"));
}
if( pSel->pLimit ){
/* The subquery already has a limit. If the pre-existing limit is X
** then make the new limit X<>0 so that the new limit is either 1 or 0 */
sqlite3 *db = pParse->db;
pLimit = sqlite3Expr(db, TK_INTEGER, "0");
if( pLimit ){
pLimit->affExpr = SQLITE_AFF_NUMERIC;
pLimit = sqlite3PExpr(pParse, TK_NE,
sqlite3ExprDup(db, pSel->pLimit->pLeft, 0), pLimit);
/* The subquery already has a limit. If the pre-existing limit X is
** not already integer value 1 or 0, then make the new limit X<>0 so that
** the new limit is either 1 or 0 */
Expr *pLeft = pSel->pLimit->pLeft;
if( ExprHasProperty(pLeft, EP_IntValue)==0
|| (pLeft->u.iValue!=1 && pLeft->u.iValue!=0)
){
sqlite3 *db = pParse->db;
pLimit = sqlite3Expr(db, TK_INTEGER, "0");
if( pLimit ){
pLimit->affExpr = SQLITE_AFF_NUMERIC;
pLimit = sqlite3PExpr(pParse, TK_NE,
sqlite3ExprDup(db, pLeft, 0), pLimit);
}
sqlite3ExprDeferredDelete(pParse, pLeft);
pSel->pLimit->pLeft = pLimit;
}
sqlite3ExprDeferredDelete(pParse, pSel->pLimit->pLeft);
pSel->pLimit->pLeft = pLimit;
}else{
/* If there is no pre-existing limit add a limit of 1 */
pLimit = sqlite3Expr(pParse->db, TK_INTEGER, "1");
@@ -4269,7 +4355,12 @@ void sqlite3ExprCodeGeneratedColumn(
iAddr = 0;
}
sqlite3ExprCodeCopy(pParse, sqlite3ColumnExpr(pTab,pCol), regOut);
if( pCol->affinity>=SQLITE_AFF_TEXT ){
if( (pCol->colFlags & COLFLAG_VIRTUAL)!=0
&& (pTab->tabFlags & TF_Strict)!=0
){
int p3 = 2+(int)(pCol - pTab->aCol);
sqlite3VdbeAddOp4(v, OP_TypeCheck, regOut, 1, p3, (char*)pTab, P4_TABLE);
}else if( pCol->affinity>=SQLITE_AFF_TEXT ){
sqlite3VdbeAddOp4(v, OP_Affinity, regOut, 1, 0, &pCol->affinity, 1);
}
if( iAddr ) sqlite3VdbeJumpHere(v, iAddr);
@@ -4898,12 +4989,6 @@ expr_code_doover:
sqlite3VdbeLoadString(v, target, pExpr->u.zToken);
return target;
}
case TK_NULLS: {
/* Set a range of registers to NULL. pExpr->y.nReg registers starting
** with target */
sqlite3VdbeAddOp3(v, OP_Null, 0, target, target + pExpr->y.nReg - 1);
return target;
}
default: {
/* Make NULL the default case so that if a bug causes an illegal
** Expr node to be passed into this function, it will be handled
@@ -4962,11 +5047,17 @@ expr_code_doover:
case TK_NE:
case TK_EQ: {
Expr *pLeft = pExpr->pLeft;
int addrIsNull = 0;
if( sqlite3ExprIsVector(pLeft) ){
codeVectorCompare(pParse, pExpr, target, op, p5);
}else{
r1 = sqlite3ExprCodeTemp(pParse, pLeft, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
if( ExprHasProperty(pExpr, EP_Subquery) && p5!=SQLITE_NULLEQ ){
addrIsNull = exprComputeOperands(pParse, pExpr,
&r1, &r2, &regFree1, &regFree2);
}else{
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
}
sqlite3VdbeAddOp2(v, OP_Integer, 1, inReg);
codeCompare(pParse, pLeft, pExpr->pRight, op, r1, r2,
sqlite3VdbeCurrentAddr(v)+2, p5,
@@ -4981,9 +5072,15 @@ expr_code_doover:
sqlite3VdbeAddOp2(v, OP_Integer, 0, inReg);
}else{
sqlite3VdbeAddOp3(v, OP_ZeroOrNull, r1, inReg, r2);
if( addrIsNull ){
sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+2);
sqlite3VdbeJumpHere(v, addrIsNull);
sqlite3VdbeAddOp2(v, OP_Null, 0, inReg);
}
}
testcase( regFree1==0 );
testcase( regFree2==0 );
}
break;
}
@@ -4999,6 +5096,7 @@ expr_code_doover:
case TK_LSHIFT:
case TK_RSHIFT:
case TK_CONCAT: {
int addrIsNull;
assert( TK_AND==OP_And ); testcase( op==TK_AND );
assert( TK_OR==OP_Or ); testcase( op==TK_OR );
assert( TK_PLUS==OP_Add ); testcase( op==TK_PLUS );
@@ -5010,11 +5108,23 @@ expr_code_doover:
assert( TK_LSHIFT==OP_ShiftLeft ); testcase( op==TK_LSHIFT );
assert( TK_RSHIFT==OP_ShiftRight ); testcase( op==TK_RSHIFT );
assert( TK_CONCAT==OP_Concat ); testcase( op==TK_CONCAT );
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
if( ExprHasProperty(pExpr, EP_Subquery) ){
addrIsNull = exprComputeOperands(pParse, pExpr,
&r1, &r2, &regFree1, &regFree2);
}else{
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
addrIsNull = 0;
}
sqlite3VdbeAddOp3(v, op, r2, r1, target);
testcase( regFree1==0 );
testcase( regFree2==0 );
if( addrIsNull ){
sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+2);
sqlite3VdbeJumpHere(v, addrIsNull);
sqlite3VdbeAddOp2(v, OP_Null, 0, target);
VdbeComment((v, "short-circut value"));
}
break;
}
case TK_UMINUS: {
@@ -5588,25 +5698,6 @@ int sqlite3ExprCodeRunJustOnce(
return regDest;
}
/*
** Make arrangements to invoke OP_Null on a range of registers
** during initialization.
*/
SQLITE_NOINLINE void sqlite3ExprNullRegisterRange(
Parse *pParse, /* Parsing context */
int iReg, /* First register to set to NULL */
int nReg /* Number of sequential registers to NULL out */
){
u8 okConstFactor = pParse->okConstFactor;
Expr t;
memset(&t, 0, sizeof(t));
t.op = TK_NULLS;
t.y.nReg = nReg;
pParse->okConstFactor = 1;
sqlite3ExprCodeRunJustOnce(pParse, &t, iReg);
pParse->okConstFactor = okConstFactor;
}
/*
** Generate code to evaluate an expression and store the results
** into a register. Return the register number where the results
@@ -5882,17 +5973,27 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
Expr *pAlt = sqlite3ExprSimplifiedAndOr(pExpr);
if( pAlt!=pExpr ){
sqlite3ExprIfTrue(pParse, pAlt, dest, jumpIfNull);
}else if( op==TK_AND ){
int d2 = sqlite3VdbeMakeLabel(pParse);
testcase( jumpIfNull==0 );
sqlite3ExprIfFalse(pParse, pExpr->pLeft, d2,
jumpIfNull^SQLITE_JUMPIFNULL);
sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
sqlite3VdbeResolveLabel(v, d2);
}else{
testcase( jumpIfNull==0 );
sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull);
sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
Expr *pFirst, *pSecond;
if( exprEvalRhsFirst(pExpr) ){
pFirst = pExpr->pRight;
pSecond = pExpr->pLeft;
}else{
pFirst = pExpr->pLeft;
pSecond = pExpr->pRight;
}
if( op==TK_AND ){
int d2 = sqlite3VdbeMakeLabel(pParse);
testcase( jumpIfNull==0 );
sqlite3ExprIfFalse(pParse, pFirst, d2,
jumpIfNull^SQLITE_JUMPIFNULL);
sqlite3ExprIfTrue(pParse, pSecond, dest, jumpIfNull);
sqlite3VdbeResolveLabel(v, d2);
}else{
testcase( jumpIfNull==0 );
sqlite3ExprIfTrue(pParse, pFirst, dest, jumpIfNull);
sqlite3ExprIfTrue(pParse, pSecond, dest, jumpIfNull);
}
}
break;
}
@@ -5931,10 +6032,16 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
case TK_GE:
case TK_NE:
case TK_EQ: {
int addrIsNull;
if( sqlite3ExprIsVector(pExpr->pLeft) ) goto default_expr;
testcase( jumpIfNull==0 );
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
if( ExprHasProperty(pExpr, EP_Subquery) && jumpIfNull!=SQLITE_NULLEQ ){
addrIsNull = exprComputeOperands(pParse, pExpr,
&r1, &r2, &regFree1, &regFree2);
}else{
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
addrIsNull = 0;
}
codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
r1, r2, dest, jumpIfNull, ExprHasProperty(pExpr,EP_Commuted));
assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt);
@@ -5949,6 +6056,13 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
VdbeCoverageIf(v, op==OP_Ne && jumpIfNull!=SQLITE_NULLEQ);
testcase( regFree1==0 );
testcase( regFree2==0 );
if( addrIsNull ){
if( jumpIfNull ){
sqlite3VdbeChangeP2(v, addrIsNull, dest);
}else{
sqlite3VdbeJumpHere(v, addrIsNull);
}
}
break;
}
case TK_ISNULL:
@@ -6056,17 +6170,27 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
Expr *pAlt = sqlite3ExprSimplifiedAndOr(pExpr);
if( pAlt!=pExpr ){
sqlite3ExprIfFalse(pParse, pAlt, dest, jumpIfNull);
}else if( pExpr->op==TK_AND ){
testcase( jumpIfNull==0 );
sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull);
sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
}else{
int d2 = sqlite3VdbeMakeLabel(pParse);
testcase( jumpIfNull==0 );
sqlite3ExprIfTrue(pParse, pExpr->pLeft, d2,
jumpIfNull^SQLITE_JUMPIFNULL);
sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
sqlite3VdbeResolveLabel(v, d2);
Expr *pFirst, *pSecond;
if( exprEvalRhsFirst(pExpr) ){
pFirst = pExpr->pRight;
pSecond = pExpr->pLeft;
}else{
pFirst = pExpr->pLeft;
pSecond = pExpr->pRight;
}
if( pExpr->op==TK_AND ){
testcase( jumpIfNull==0 );
sqlite3ExprIfFalse(pParse, pFirst, dest, jumpIfNull);
sqlite3ExprIfFalse(pParse, pSecond, dest, jumpIfNull);
}else{
int d2 = sqlite3VdbeMakeLabel(pParse);
testcase( jumpIfNull==0 );
sqlite3ExprIfTrue(pParse, pFirst, d2,
jumpIfNull^SQLITE_JUMPIFNULL);
sqlite3ExprIfFalse(pParse, pSecond, dest, jumpIfNull);
sqlite3VdbeResolveLabel(v, d2);
}
}
break;
}
@@ -6108,10 +6232,16 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
case TK_GE:
case TK_NE:
case TK_EQ: {
int addrIsNull;
if( sqlite3ExprIsVector(pExpr->pLeft) ) goto default_expr;
testcase( jumpIfNull==0 );
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
if( ExprHasProperty(pExpr, EP_Subquery) && jumpIfNull!=SQLITE_NULLEQ ){
addrIsNull = exprComputeOperands(pParse, pExpr,
&r1, &r2, &regFree1, &regFree2);
}else{
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
addrIsNull = 0;
}
codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
r1, r2, dest, jumpIfNull,ExprHasProperty(pExpr,EP_Commuted));
assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt);
@@ -6126,6 +6256,13 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
VdbeCoverageIf(v, op==OP_Ne && jumpIfNull==SQLITE_NULLEQ);
testcase( regFree1==0 );
testcase( regFree2==0 );
if( addrIsNull ){
if( jumpIfNull ){
sqlite3VdbeChangeP2(v, addrIsNull, dest);
}else{
sqlite3VdbeJumpHere(v, addrIsNull);
}
}
break;
}
case TK_ISNULL:
+4 -1
View File
@@ -185,12 +185,15 @@ void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){
** by one slot and insert a new OP_TypeCheck where the current
** OP_MakeRecord is found */
VdbeOp *pPrev;
int p3;
sqlite3VdbeAppendP4(v, pTab, P4_TABLE);
pPrev = sqlite3VdbeGetLastOp(v);
assert( pPrev!=0 );
assert( pPrev->opcode==OP_MakeRecord || sqlite3VdbeDb(v)->mallocFailed );
pPrev->opcode = OP_TypeCheck;
sqlite3VdbeAddOp3(v, OP_MakeRecord, pPrev->p1, pPrev->p2, pPrev->p3);
p3 = pPrev->p3;
pPrev->p3 = 0;
sqlite3VdbeAddOp3(v, OP_MakeRecord, pPrev->p1, pPrev->p2, p3);
}else{
/* Insert an isolated OP_Typecheck */
sqlite3VdbeAddOp2(v, OP_TypeCheck, iReg, pTab->nNVCol);
+8
View File
@@ -416,6 +416,14 @@ void sqlite3_str_vappendf(
}
prefix = 0;
}
#if WHERETRACE_ENABLED
if( xtype==etPOINTER && sqlite3WhereTrace & 0x100000 ) longvalue = 0;
#endif
#if TREETRACE_ENABLED
if( xtype==etPOINTER && sqlite3TreeTrace & 0x100000 ) longvalue = 0;
#endif
if( longvalue==0 ) flag_alternateform = 0;
if( flag_zeropad && precision<width-(prefix!=0) ){
precision = width-(prefix!=0);
+4 -1
View File
@@ -1358,11 +1358,13 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
return WRC_Prune;
}
#ifndef SQLITE_OMIT_SUBQUERY
case TK_EXISTS:
case TK_SELECT:
case TK_EXISTS: testcase( pExpr->op==TK_EXISTS );
#endif
case TK_IN: {
testcase( pExpr->op==TK_IN );
testcase( pExpr->op==TK_EXISTS );
testcase( pExpr->op==TK_SELECT );
if( ExprUseXSelect(pExpr) ){
int nRef = pNC->nRef;
testcase( pNC->ncFlags & NC_IsCheck );
@@ -1370,6 +1372,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
testcase( pNC->ncFlags & NC_IdxExpr );
testcase( pNC->ncFlags & NC_GenCol );
assert( pExpr->x.pSelect );
if( pExpr->op==TK_EXISTS ) pParse->bHasExists = 1;
if( pNC->ncFlags & NC_SelfRef ){
notValidImpl(pParse, pNC, "subqueries", pExpr, pExpr);
}else{
+114 -12
View File
@@ -384,7 +384,7 @@ static int tableAndColumnIndex(
int iEnd, /* Last member of pSrc->a[] to check */
const char *zCol, /* Name of the column we are looking for */
int *piTab, /* Write index of pSrc->a[] here */
int *piCol, /* Write index of pSrc->a[*piTab].pTab->aCol[] here */
int *piCol, /* Write index of pSrc->a[*piTab].pSTab->aCol[] here */
int bIgnoreHidden /* Ignore hidden columns */
){
int i; /* For looping over tables in pSrc */
@@ -2113,6 +2113,10 @@ static void generateColumnTypes(
#endif
sqlite3VdbeSetColName(v, i, COLNAME_DECLTYPE, zType, SQLITE_TRANSIENT);
}
#else
UNUSED_PARAMETER(pParse);
UNUSED_PARAMETER(pTabList);
UNUSED_PARAMETER(pEList);
#endif /* !defined(SQLITE_OMIT_DECLTYPE) */
}
@@ -3032,7 +3036,9 @@ static int multiSelect(
int priorOp; /* The SRT_ operation to apply to prior selects */
Expr *pLimit; /* Saved values of p->nLimit */
int addr;
int emptyBypass = 0; /* IfEmpty opcode to bypass RHS */
SelectDest uniondest;
testcase( p->op==TK_EXCEPT );
testcase( p->op==TK_UNION );
@@ -3071,6 +3077,8 @@ static int multiSelect(
*/
if( p->op==TK_EXCEPT ){
op = SRT_Except;
emptyBypass = sqlite3VdbeAddOp1(v, OP_IfEmpty, unionTab);
VdbeCoverage(v);
}else{
assert( p->op==TK_UNION );
op = SRT_Union;
@@ -3091,6 +3099,7 @@ static int multiSelect(
if( p->op==TK_UNION ){
p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
}
if( emptyBypass ) sqlite3VdbeJumpHere(v, emptyBypass);
sqlite3ExprDelete(db, p->pLimit);
p->pLimit = pLimit;
p->iLimit = 0;
@@ -3124,6 +3133,7 @@ static int multiSelect(
int addr;
SelectDest intersectdest;
int r1;
int emptyBypass;
/* INTERSECT is different from the others since it requires
** two temporary tables. Hence it has its own case. Begin
@@ -3147,6 +3157,7 @@ static int multiSelect(
if( rc ){
goto multi_select_end;
}
emptyBypass = sqlite3VdbeAddOp1(v, OP_IfEmpty, tab1); VdbeCoverage(v);
/* Code the current SELECT into temporary table "tab2"
*/
@@ -3178,7 +3189,7 @@ static int multiSelect(
iBreak = sqlite3VdbeMakeLabel(pParse);
iCont = sqlite3VdbeMakeLabel(pParse);
computeLimitRegisters(pParse, p, iBreak);
sqlite3VdbeAddOp2(v, OP_Rewind, tab1, iBreak); VdbeCoverage(v);
sqlite3VdbeAddOp1(v, OP_Rewind, tab1);
r1 = sqlite3GetTempReg(pParse);
iStart = sqlite3VdbeAddOp2(v, OP_RowData, tab1, r1);
sqlite3VdbeAddOp4Int(v, OP_NotFound, tab2, iCont, r1, 0);
@@ -3190,6 +3201,7 @@ static int multiSelect(
sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart); VdbeCoverage(v);
sqlite3VdbeResolveLabel(v, iBreak);
sqlite3VdbeAddOp2(v, OP_Close, tab2, 0);
sqlite3VdbeJumpHere(v, emptyBypass);
sqlite3VdbeAddOp2(v, OP_Close, tab1, 0);
break;
}
@@ -3868,6 +3880,7 @@ typedef struct SubstContext {
int iTable; /* Replace references to this table */
int iNewTable; /* New table number */
int isOuterJoin; /* Add TK_IF_NULL_ROW opcodes on each replacement */
int nSelDepth; /* Depth of sub-query recursion. Top==1 */
ExprList *pEList; /* Replacement expressions */
ExprList *pCList; /* Collation sequences for replacement expr */
} SubstContext;
@@ -3975,6 +3988,9 @@ static Expr *substExpr(
if( pExpr->op==TK_IF_NULL_ROW && pExpr->iTable==pSubst->iTable ){
pExpr->iTable = pSubst->iNewTable;
}
if( pExpr->op==TK_AGG_FUNCTION && pExpr->op2>=pSubst->nSelDepth ){
pExpr->op2--;
}
pExpr->pLeft = substExpr(pSubst, pExpr->pLeft);
pExpr->pRight = substExpr(pSubst, pExpr->pRight);
if( ExprUseXSelect(pExpr) ){
@@ -4012,6 +4028,7 @@ static void substSelect(
SrcItem *pItem;
int i;
if( !p ) return;
pSubst->nSelDepth++;
do{
substExprList(pSubst, p->pEList);
substExprList(pSubst, p->pGroupBy);
@@ -4029,6 +4046,7 @@ static void substSelect(
}
}
}while( doPrior && (p = p->pPrior)!=0 );
pSubst->nSelDepth--;
}
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
@@ -4640,7 +4658,7 @@ static int flattenSubquery(
** complete, since there may still exist Expr.pTab entries that
** refer to the subquery even after flattening. Ticket #3346.
**
** pSubitem->pTab is always non-NULL by test restrictions and tests above.
** pSubitem->pSTab is always non-NULL by test restrictions and tests above.
*/
if( ALWAYS(pSubitem->pSTab!=0) ){
Table *pTabToDel = pSubitem->pSTab;
@@ -4770,6 +4788,7 @@ static int flattenSubquery(
x.iTable = iParent;
x.iNewTable = iNewParent;
x.isOuterJoin = isOuterJoin;
x.nSelDepth = 0;
x.pEList = pSub->pEList;
x.pCList = findLeftmostExprlist(pSub);
substSelect(&x, pParent, 0);
@@ -5355,6 +5374,7 @@ static int pushDownWhereTerms(
x.iTable = pSrc->iCursor;
x.iNewTable = pSrc->iCursor;
x.isOuterJoin = 0;
x.nSelDepth = 0;
x.pEList = pSubq->pEList;
x.pCList = findLeftmostExprlist(pSubq);
pNew = substExpr(&x, pNew);
@@ -5752,7 +5772,7 @@ With *sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){
** CTE expression, through routine checks to see if the reference is
** a recursive reference to the CTE.
**
** If pFrom matches a CTE according to either of these two above, pFrom->pTab
** If pFrom matches a CTE according to either of these two above, pFrom->pSTab
** and other fields are populated accordingly.
**
** Return 0 if no match is found.
@@ -7379,6 +7399,82 @@ static int fromClauseTermCanBeCoroutine(
return 1;
}
/*
** Argument pWhere is the WHERE clause belonging to SELECT statement p. This
** function attempts to transform expressions of the form:
**
** EXISTS (SELECT ...)
**
** into joins. For example, given
**
** CREATE TABLE sailors(sid INTEGER PRIMARY KEY, name TEXT);
** CREATE TABLE reserves(sid INT, day DATE, PRIMARY KEY(sid, day));
**
** SELECT name FROM sailors AS S WHERE EXISTS (
** SELECT * FROM reserves AS R WHERE S.sid = R.sid AND R.day = '2022-10-25'
** );
**
** the SELECT statement may be transformed as follows:
**
** SELECT name FROM sailors AS S, reserves AS R
** WHERE S.sid = R.sid AND R.day = '2022-10-25';
**
** **Approximately**. Really, we have to ensure that the FROM-clause term
** that was formerly inside the EXISTS is only executed once. This is handled
** by setting the SrcItem.fg.fromExists flag, which then causes code in
** the where.c file to exit the corresponding loop after the first successful
** match (if any).
*/
static SQLITE_NOINLINE void existsToJoin(
Parse *pParse, /* Parsing context */
Select *p, /* The SELECT statement being optimized */
Expr *pWhere /* part of the WHERE clause currently being examined */
){
if( pWhere
&& !ExprHasProperty(pWhere, EP_OuterON|EP_InnerON)
&& p->pSrc->nSrc<BMS
&& pParse->db->mallocFailed==0
){
if( pWhere->op==TK_AND ){
Expr *pRight = pWhere->pRight;
existsToJoin(pParse, p, pWhere->pLeft);
existsToJoin(pParse, p, pRight);
}
else if( pWhere->op==TK_EXISTS ){
Select *pSub = pWhere->x.pSelect;
Expr *pSubWhere = pSub->pWhere;
if( pSub->pSrc->nSrc==1
&& (pSub->selFlags & SF_Aggregate)==0
&& !pSub->pSrc->a[0].fg.isSubquery
){
memset(pWhere, 0, sizeof(*pWhere));
pWhere->op = TK_INTEGER;
pWhere->u.iValue = 1;
ExprSetProperty(pWhere, EP_IntValue);
assert( p->pWhere!=0 );
pSub->pSrc->a[0].fg.fromExists = 1;
pSub->pSrc->a[0].fg.jointype |= JT_CROSS;
p->pSrc = sqlite3SrcListAppendList(pParse, p->pSrc, pSub->pSrc);
if( pSubWhere ){
p->pWhere = sqlite3PExpr(pParse, TK_AND, p->pWhere, pSubWhere);
pSub->pWhere = 0;
}
pSub->pSrc = 0;
sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pSub);
#if TREETRACE_ENABLED
if( sqlite3TreeTrace & 0x100000 ){
TREETRACE(0x100000,pParse,p,
("After EXISTS-to-JOIN optimization:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
existsToJoin(pParse, p, pSubWhere);
}
}
}
}
/*
** Generate byte-code for the SELECT statement given in the p argument.
**
@@ -7747,6 +7843,13 @@ int sqlite3Select(
}
#endif
/* If there may be an "EXISTS (SELECT ...)" in the WHERE clause, attempt
** to change it into a join. */
if( pParse->bHasExists && OptimizationEnabled(db,SQLITE_ExistsToJoin) ){
existsToJoin(pParse, p, p->pWhere);
pTabList = p->pSrc;
}
/* Do the WHERE-clause constant propagation optimization if this is
** a join. No need to spend time on this operation for non-join queries
** as the equivalent optimization will be handled by query planner in
@@ -8363,7 +8466,6 @@ int sqlite3Select(
sqlite3VdbeAddOp2(v, OP_Integer, 0, iAbortFlag);
VdbeComment((v, "clear abort flag"));
sqlite3VdbeAddOp3(v, OP_Null, 0, iAMem, iAMem+pGroupBy->nExpr-1);
sqlite3ExprNullRegisterRange(pParse, iAMem, pGroupBy->nExpr);
/* Begin a loop that will extract all source rows in GROUP BY order.
** This might involve two separate loops with an OP_Sort in between, or
@@ -8534,12 +8636,12 @@ int sqlite3Select(
** for the next GROUP BY batch.
*/
sqlite3VdbeAddOp2(v, OP_Gosub, regOutputRow, addrOutputRow);
VdbeComment((v, "output one row"));
VdbeComment((v, "output one row of %d", p->selId));
sqlite3ExprCodeMove(pParse, iBMem, iAMem, pGroupBy->nExpr);
sqlite3VdbeAddOp2(v, OP_IfPos, iAbortFlag, addrEnd); VdbeCoverage(v);
VdbeComment((v, "check abort flag"));
sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset);
VdbeComment((v, "reset accumulator"));
VdbeComment((v, "reset accumulator %d", p->selId));
/* Update the aggregate accumulators based on the content of
** the current row
@@ -8547,7 +8649,7 @@ int sqlite3Select(
sqlite3VdbeJumpHere(v, addr1);
updateAccumulator(pParse, iUseFlag, pAggInfo, eDist);
sqlite3VdbeAddOp2(v, OP_Integer, 1, iUseFlag);
VdbeComment((v, "indicate data in accumulator"));
VdbeComment((v, "indicate data in accumulator %d", p->selId));
/* End of the loop
*/
@@ -8564,7 +8666,7 @@ int sqlite3Select(
/* Output the final row of result
*/
sqlite3VdbeAddOp2(v, OP_Gosub, regOutputRow, addrOutputRow);
VdbeComment((v, "output final row"));
VdbeComment((v, "output final row of %d", p->selId));
/* Jump over the subroutines
*/
@@ -8585,7 +8687,7 @@ int sqlite3Select(
addrOutputRow = sqlite3VdbeCurrentAddr(v);
sqlite3VdbeAddOp2(v, OP_IfPos, iUseFlag, addrOutputRow+2);
VdbeCoverage(v);
VdbeComment((v, "Groupby result generator entry point"));
VdbeComment((v, "Groupby result generator entry point %d", p->selId));
sqlite3VdbeAddOp1(v, OP_Return, regOutputRow);
finalizeAggFunctions(pParse, pAggInfo);
sqlite3ExprIfFalse(pParse, pHaving, addrOutputRow+1, SQLITE_JUMPIFNULL);
@@ -8593,14 +8695,14 @@ int sqlite3Select(
&sDistinct, pDest,
addrOutputRow+1, addrSetAbort);
sqlite3VdbeAddOp1(v, OP_Return, regOutputRow);
VdbeComment((v, "end groupby result generator"));
VdbeComment((v, "end groupby result generator %d", p->selId));
/* Generate a subroutine that will reset the group-by accumulator
*/
sqlite3VdbeResolveLabel(v, addrReset);
resetAccumulator(pParse, pAggInfo);
sqlite3VdbeAddOp2(v, OP_Integer, 0, iUseFlag);
VdbeComment((v, "indicate accumulator empty"));
VdbeComment((v, "indicate accumulator %d empty", p->selId));
sqlite3VdbeAddOp1(v, OP_Return, regReset);
if( distFlag!=0 && eDist!=WHERE_DISTINCT_NOOP ){
+63 -13
View File
@@ -887,12 +887,21 @@ static int strlen30(const char *z){
/*
** Return the length of a string in characters. Multibyte UTF8 characters
** count as a single character.
** count as a single character for single-width characters, or as two
** characters for double-width characters.
*/
static int strlenChar(const char *z){
int n = 0;
while( *z ){
if( (0xc0&*(z++))!=0x80 ) n++;
if( (0x80&z[0])==0 ){
n++;
z++;
}else{
int u = 0;
int len = decodeUtf8((const u8*)z, &u);
z += len;
n += cli_wcwidth(u);
}
}
return n;
}
@@ -1325,11 +1334,7 @@ static void shellAddSchemaName(
#define SQLITE_EXTENSION_INIT1
#define SQLITE_EXTENSION_INIT2(X) (void)(X)
#if defined(_WIN32) && defined(_MSC_VER)
INCLUDE test_windirent.h
INCLUDE test_windirent.c
#define dirent DIRENT
#endif
INCLUDE ../ext/misc/windirent.h
INCLUDE ../ext/misc/memtrace.c
INCLUDE ../ext/misc/pcachetrace.c
INCLUDE ../ext/misc/shathree.c
@@ -9323,7 +9328,8 @@ static int do_meta_command(char *zLine, ShellState *p){
" (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x"
" FROM sqlite_schema UNION ALL"
" SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_schema) "
"WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%' "
"WHERE type!='meta' AND sql NOTNULL"
" AND name NOT LIKE 'sqlite__%' ESCAPE '_' "
"ORDER BY x",
callback, &data, 0
);
@@ -10799,7 +10805,7 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_free(zQarg);
}
if( bNoSystemTabs ){
appendText(&sSelect, "name NOT LIKE 'sqlite_%%' AND ", 0);
appendText(&sSelect, "name NOT LIKE 'sqlite__%%' ESCAPE '_' AND ", 0);
}
appendText(&sSelect, "sql IS NOT NULL"
" ORDER BY snum, rowid", 0);
@@ -11230,7 +11236,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}else{
zSql = "SELECT lower(name) as tname FROM sqlite_schema"
" WHERE type='table' AND coalesce(rootpage,0)>1"
" AND name NOT LIKE 'sqlite_%'"
" AND name NOT LIKE 'sqlite__%' ESCAPE '_'"
" ORDER BY 1 collate nocase";
}
sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
@@ -11295,7 +11301,7 @@ static int do_meta_command(char *zLine, ShellState *p){
char *zRevText = /* Query for reversible to-blob-to-text check */
"SELECT lower(name) as tname FROM sqlite_schema\n"
"WHERE type='table' AND coalesce(rootpage,0)>1\n"
"AND name NOT LIKE 'sqlite_%%'%s\n"
"AND name NOT LIKE 'sqlite__%%' ESCAPE '_'%s\n"
"ORDER BY 1 collate nocase";
zRevText = sqlite3_mprintf(zRevText, zLike? " AND name LIKE $tspec" : "");
zRevText = sqlite3_mprintf(
@@ -11491,7 +11497,7 @@ static int do_meta_command(char *zLine, ShellState *p){
appendText(&s, ".sqlite_schema ", 0);
if( c=='t' ){
appendText(&s," WHERE type IN ('table','view')"
" AND name NOT LIKE 'sqlite_%'"
" AND name NOT LIKE 'sqlite__%' ESCAPE '_'"
" AND name LIKE ?1", 0);
}else{
appendText(&s," WHERE type='index'"
@@ -11585,7 +11591,7 @@ static int do_meta_command(char *zLine, ShellState *p){
{"always", SQLITE_TESTCTRL_ALWAYS, 1, "BOOLEAN" },
{"assert", SQLITE_TESTCTRL_ASSERT, 1, "BOOLEAN" },
/*{"benign_malloc_hooks",SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS,1, "" },*/
/*{"bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, 1, "" },*/
{"bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, 1, "SIZE INT-ARRAY"},
{"byteorder", SQLITE_TESTCTRL_BYTEORDER, 0, "" },
{"extra_schema_checks",SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS,0,"BOOLEAN" },
{"fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, 1,"args..." },
@@ -11704,6 +11710,7 @@ static int do_meta_command(char *zLine, ShellState *p){
{ 0x08000000, 1, "OnePass" },
{ 0x10000000, 1, "OrderBySubq" },
{ 0x20000000, 1, "StarQuery" },
{ 0x40000000, 1, "ExistsToJoin" },
{ 0xffffffff, 0, "All" },
};
unsigned int curOpt;
@@ -11923,6 +11930,49 @@ static int do_meta_command(char *zLine, ShellState *p){
}
sqlite3_test_control(testctrl, &rc2);
break;
case SQLITE_TESTCTRL_BITVEC_TEST: {
/* Examples:
** .testctrl bitvec_test 100 6,1 -- Show BITVEC constants
** .testctrl bitvec_test 1000 1,12,7,3 -- Simple test
** ---- --------
** size of Bitvec -----^ ^--- aOp array. 0 added at end.
**
** See comments on sqlite3BitvecBuiltinTest() for more information
** about the aOp[] array.
*/
int iSize;
const char *zTestArg;
int nOp;
int ii, jj, x;
int *aOp;
if( nArg!=4 ){
sqlite3_fprintf(stderr,
"ERROR - should be: \".testctrl bitvec_test SIZE INT-ARRAY\"\n"
);
rc = 1;
goto meta_command_exit;
}
isOk = 3;
iSize = (int)integerValue(azArg[2]);
zTestArg = azArg[3];
nOp = (int)strlen(zTestArg)+1;
aOp = malloc( sizeof(int)*(nOp+1) );
shell_check_oom(aOp);
memset(aOp, 0, sizeof(int)*(nOp+1) );
for(ii = jj = x = 0; zTestArg[ii]!=0; ii++){
if( IsDigit(zTestArg[ii]) ){
x = x*10 + zTestArg[ii] - '0';
}else{
aOp[jj++] = x;
x = 0;
}
}
aOp[jj] = x;
x = sqlite3_test_control(testctrl, iSize, aOp);
sqlite3_fprintf(p->out, "result: %d\n", x);
free(aOp);
break;
}
case SQLITE_TESTCTRL_FAULT_INSTALL: {
int kk;
int bShowHelp = nArg<=2;
+50 -50
View File
@@ -168,9 +168,9 @@ extern "C" {
** assert( strcmp(sqlite3_libversion(),SQLITE_VERSION)==0 );
** </pre></blockquote>)^
**
** ^The sqlite3_version[] string constant contains the text of [SQLITE_VERSION]
** macro. ^The sqlite3_libversion() function returns a pointer to the
** to the sqlite3_version[] string constant. The sqlite3_libversion()
** ^The sqlite3_version[] string constant contains the text of the
** [SQLITE_VERSION] macro. ^The sqlite3_libversion() function returns a
** pointer to the sqlite3_version[] string constant. The sqlite3_libversion()
** function is provided for use in DLLs since DLL users usually do not have
** direct access to string constants within the DLL. ^The
** sqlite3_libversion_number() function returns an integer equal to
@@ -370,7 +370,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
** without having to use a lot of C code.
**
** ^The sqlite3_exec() interface runs zero or more UTF-8 encoded,
** semicolon-separate SQL statements passed into its 2nd argument,
** semicolon-separated SQL statements passed into its 2nd argument,
** in the context of the [database connection] passed in as its 1st
** argument. ^If the callback function of the 3rd argument to
** sqlite3_exec() is not NULL, then it is invoked for each result row
@@ -403,7 +403,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
** result row is NULL then the corresponding string pointer for the
** sqlite3_exec() callback is a NULL pointer. ^The 4th argument to the
** sqlite3_exec() callback is an array of pointers to strings where each
** entry represents the name of corresponding result column as obtained
** entry represents the name of a corresponding result column as obtained
** from [sqlite3_column_name()].
**
** ^If the 2nd parameter to sqlite3_exec() is a NULL pointer, a pointer
@@ -589,7 +589,7 @@ int sqlite3_exec(
** Note in particular that passing the SQLITE_OPEN_EXCLUSIVE flag into
** [sqlite3_open_v2()] does *not* cause the underlying database file
** to be opened using O_EXCL. Passing SQLITE_OPEN_EXCLUSIVE into
** [sqlite3_open_v2()] has historically be a no-op and might become an
** [sqlite3_open_v2()] has historically been a no-op and might become an
** error in future versions of SQLite.
*/
#define SQLITE_OPEN_READONLY 0x00000001 /* Ok for sqlite3_open_v2() */
@@ -683,7 +683,7 @@ int sqlite3_exec(
** SQLite uses one of these integer values as the second
** argument to calls it makes to the xLock() and xUnlock() methods
** of an [sqlite3_io_methods] object. These values are ordered from
** lest restrictive to most restrictive.
** least restrictive to most restrictive.
**
** The argument to xLock() is always SHARED or higher. The argument to
** xUnlock is either SHARED or NONE.
@@ -999,7 +999,7 @@ struct sqlite3_io_methods {
**
** <li>[[SQLITE_FCNTL_VFSNAME]]
** ^The [SQLITE_FCNTL_VFSNAME] opcode can be used to obtain the names of
** all [VFSes] in the VFS stack. The names are of all VFS shims and the
** all [VFSes] in the VFS stack. The names of all VFS shims and the
** final bottom-level VFS are written into memory obtained from
** [sqlite3_malloc()] and the result is stored in the char* variable
** that the fourth parameter of [sqlite3_file_control()] points to.
@@ -1013,7 +1013,7 @@ struct sqlite3_io_methods {
** ^The [SQLITE_FCNTL_VFS_POINTER] opcode finds a pointer to the top-level
** [VFSes] currently in use. ^(The argument X in
** sqlite3_file_control(db,SQLITE_FCNTL_VFS_POINTER,X) must be
** of type "[sqlite3_vfs] **". This opcodes will set *X
** of type "[sqlite3_vfs] **". This opcode will set *X
** to a pointer to the top-level VFS.)^
** ^When there are multiple VFS shims in the stack, this opcode finds the
** upper-most shim only.
@@ -1203,7 +1203,7 @@ struct sqlite3_io_methods {
** <li>[[SQLITE_FCNTL_EXTERNAL_READER]]
** The EXPERIMENTAL [SQLITE_FCNTL_EXTERNAL_READER] opcode is used to detect
** whether or not there is a database client in another process with a wal-mode
** transaction open on the database or not. It is only available on unix.The
** transaction open on the database or not. It is only available on unix. The
** (void*) argument passed with this file-control should be a pointer to a
** value of type (int). The integer value is set to 1 if the database is a wal
** mode database and there exists at least one client in another process that
@@ -1628,7 +1628,7 @@ struct sqlite3_vfs {
** SQLite interfaces so that an application usually does not need to
** invoke sqlite3_initialize() directly. For example, [sqlite3_open()]
** calls sqlite3_initialize() so the SQLite library will be automatically
** initialized when [sqlite3_open()] is called if it has not be initialized
** initialized when [sqlite3_open()] is called if it has not been initialized
** already. ^However, if SQLite is compiled with the [SQLITE_OMIT_AUTOINIT]
** compile-time option, then the automatic calls to sqlite3_initialize()
** are omitted and the application must call sqlite3_initialize() directly
@@ -1885,21 +1885,21 @@ struct sqlite3_mem_methods {
** The [sqlite3_mem_methods]
** structure is filled with the currently defined memory allocation routines.)^
** This option can be used to overload the default memory allocation
** routines with a wrapper that simulations memory allocation failure or
** routines with a wrapper that simulates memory allocation failure or
** tracks memory usage, for example. </dd>
**
** [[SQLITE_CONFIG_SMALL_MALLOC]] <dt>SQLITE_CONFIG_SMALL_MALLOC</dt>
** <dd> ^The SQLITE_CONFIG_SMALL_MALLOC option takes single argument of
** <dd> ^The SQLITE_CONFIG_SMALL_MALLOC option takes a single argument of
** type int, interpreted as a boolean, which if true provides a hint to
** SQLite that it should avoid large memory allocations if possible.
** SQLite will run faster if it is free to make large memory allocations,
** but some application might prefer to run slower in exchange for
** but some applications might prefer to run slower in exchange for
** guarantees about memory fragmentation that are possible if large
** allocations are avoided. This hint is normally off.
** </dd>
**
** [[SQLITE_CONFIG_MEMSTATUS]] <dt>SQLITE_CONFIG_MEMSTATUS</dt>
** <dd> ^The SQLITE_CONFIG_MEMSTATUS option takes single argument of type int,
** <dd> ^The SQLITE_CONFIG_MEMSTATUS option takes a single argument of type int,
** interpreted as a boolean, which enables or disables the collection of
** memory allocation statistics. ^(When memory allocation statistics are
** disabled, the following SQLite interfaces become non-operational:
@@ -1944,7 +1944,7 @@ struct sqlite3_mem_methods {
** ^If pMem is NULL and N is non-zero, then each database connection
** does an initial bulk allocation for page cache memory
** from [sqlite3_malloc()] sufficient for N cache lines if N is positive or
** of -1024*N bytes if N is negative, . ^If additional
** of -1024*N bytes if N is negative. ^If additional
** page cache memory is needed beyond what is provided by the initial
** allocation, then SQLite goes to [sqlite3_malloc()] separately for each
** additional cache line. </dd>
@@ -1973,7 +1973,7 @@ struct sqlite3_mem_methods {
** <dd> ^(The SQLITE_CONFIG_MUTEX option takes a single argument which is a
** pointer to an instance of the [sqlite3_mutex_methods] structure.
** The argument specifies alternative low-level mutex routines to be used
** in place the mutex routines built into SQLite.)^ ^SQLite makes a copy of
** in place of the mutex routines built into SQLite.)^ ^SQLite makes a copy of
** the content of the [sqlite3_mutex_methods] structure before the call to
** [sqlite3_config()] returns. ^If SQLite is compiled with
** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then
@@ -2015,7 +2015,7 @@ struct sqlite3_mem_methods {
**
** [[SQLITE_CONFIG_GETPCACHE2]] <dt>SQLITE_CONFIG_GETPCACHE2</dt>
** <dd> ^(The SQLITE_CONFIG_GETPCACHE2 option takes a single argument which
** is a pointer to an [sqlite3_pcache_methods2] object. SQLite copies of
** is a pointer to an [sqlite3_pcache_methods2] object. SQLite copies off
** the current page cache implementation into that object.)^ </dd>
**
** [[SQLITE_CONFIG_LOG]] <dt>SQLITE_CONFIG_LOG</dt>
@@ -2032,7 +2032,7 @@ struct sqlite3_mem_methods {
** the logger function is a copy of the first parameter to the corresponding
** [sqlite3_log()] call and is intended to be a [result code] or an
** [extended result code]. ^The third parameter passed to the logger is
** log message after formatting via [sqlite3_snprintf()].
** a log message after formatting via [sqlite3_snprintf()].
** The SQLite logging interface is not reentrant; the logger function
** supplied by the application must not invoke any SQLite interface.
** In a multi-threaded application, the application-defined logger
@@ -2223,7 +2223,7 @@ struct sqlite3_mem_methods {
** These constants are the available integer configuration options that
** can be passed as the second parameter to the [sqlite3_db_config()] interface.
**
** The [sqlite3_db_config()] interface is a var-args functions. It takes a
** The [sqlite3_db_config()] interface is a var-args function. It takes a
** variable number of parameters, though always at least two. The number of
** parameters passed into sqlite3_db_config() depends on which of these
** constants is given as the second parameter. This documentation page
@@ -2357,8 +2357,8 @@ struct sqlite3_mem_methods {
** When the first argument to this interface is 1, then only the C-API is
** enabled and the SQL function remains disabled. If the first argument to
** this interface is 0, then both the C-API and the SQL function are disabled.
** If the first argument is -1, then no changes are made to state of either the
** C-API or the SQL function.
** If the first argument is -1, then no changes are made to the state of either
** the C-API or the SQL function.
** The second parameter is a pointer to an integer into which
** is written 0 or 1 to indicate whether [sqlite3_load_extension()] interface
** is disabled or enabled following this call. The second parameter may
@@ -2476,7 +2476,7 @@ struct sqlite3_mem_methods {
** [[SQLITE_DBCONFIG_LEGACY_ALTER_TABLE]]
** <dt>SQLITE_DBCONFIG_LEGACY_ALTER_TABLE</dt>
** <dd>The SQLITE_DBCONFIG_LEGACY_ALTER_TABLE option activates or deactivates
** the legacy behavior of the [ALTER TABLE RENAME] command such it
** the legacy behavior of the [ALTER TABLE RENAME] command such that it
** behaves as it did prior to [version 3.24.0] (2018-06-04). See the
** "Compatibility Notice" on the [ALTER TABLE RENAME documentation] for
** additional information. This feature can also be turned on and off
@@ -2525,7 +2525,7 @@ struct sqlite3_mem_methods {
** <dt>SQLITE_DBCONFIG_LEGACY_FILE_FORMAT</dt>
** <dd>The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates
** the legacy file format flag. When activated, this flag causes all newly
** created database file to have a schema format version number (the 4-byte
** created database files to have a schema format version number (the 4-byte
** integer found at offset 44 into the database header) of 1. This in turn
** means that the resulting database file will be readable and writable by
** any SQLite version back to 3.0.0 ([dateof:3.0.0]). Without this setting,
@@ -2552,7 +2552,7 @@ struct sqlite3_mem_methods {
** the database handle both when the SQL statement is prepared and when it
** is stepped. The flag is set (collection of statistics is enabled)
** by default. <p>This option takes two arguments: an integer and a pointer to
** an integer.. The first argument is 1, 0, or -1 to enable, disable, or
** an integer. The first argument is 1, 0, or -1 to enable, disable, or
** leave unchanged the statement scanstatus option. If the second argument
** is not NULL, then the value of the statement scanstatus setting after
** processing the first argument is written into the integer that the second
@@ -2595,8 +2595,8 @@ struct sqlite3_mem_methods {
** <dd>The SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE option enables or disables the
** ability of the [ATTACH DATABASE] SQL command to open a database for writing.
** This capability is enabled by default. Applications can disable or
** reenable this capability using the current DBCONFIG option. If the
** the this capability is disabled, the [ATTACH] command will still work,
** reenable this capability using the current DBCONFIG option. If
** this capability is disabled, the [ATTACH] command will still work,
** but the database will be opened read-only. If this option is disabled,
** then the ability to create a new database using [ATTACH] is also disabled,
** regardless of the value of the [SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE]
@@ -2630,7 +2630,7 @@ struct sqlite3_mem_methods {
**
** <p>Most of the SQLITE_DBCONFIG options take two arguments, so that the
** overall call to [sqlite3_db_config()] has a total of four parameters.
** The first argument (the third parameter to sqlite3_db_config()) is a integer.
** The first argument (the third parameter to sqlite3_db_config()) is an integer.
** The second argument is a pointer to an integer. If the first argument is 1,
** then the option becomes enabled. If the first integer argument is 0, then the
** option is disabled. If the first argument is -1, then the option setting
@@ -2920,7 +2920,7 @@ int sqlite3_is_interrupted(sqlite3*);
** ^These routines return 0 if the statement is incomplete. ^If a
** memory allocation fails, then SQLITE_NOMEM is returned.
**
** ^These routines do not parse the SQL statements thus
** ^These routines do not parse the SQL statements and thus
** will not detect syntactically incorrect SQL.
**
** ^(If SQLite has not been initialized using [sqlite3_initialize()] prior
@@ -3037,7 +3037,7 @@ int sqlite3_busy_timeout(sqlite3*, int ms);
** indefinitely if possible. The results of passing any other negative value
** are undefined.
**
** Internally, each SQLite database handle store two timeout values - the
** Internally, each SQLite database handle stores two timeout values - the
** busy-timeout (used for rollback mode databases, or if the VFS does not
** support blocking locks) and the setlk-timeout (used for blocking locks
** on wal-mode databases). The sqlite3_busy_timeout() method sets both
@@ -3067,7 +3067,7 @@ int sqlite3_setlk_timeout(sqlite3*, int ms, int flags);
** This is a legacy interface that is preserved for backwards compatibility.
** Use of this interface is not recommended.
**
** Definition: A <b>result table</b> is memory data structure created by the
** Definition: A <b>result table</b> is a memory data structure created by the
** [sqlite3_get_table()] interface. A result table records the
** complete query results from one or more queries.
**
@@ -3210,7 +3210,7 @@ char *sqlite3_vsnprintf(int,char*,const char*, va_list);
** ^Calling sqlite3_free() with a pointer previously returned
** by sqlite3_malloc() or sqlite3_realloc() releases that memory so
** that it might be reused. ^The sqlite3_free() routine is
** a no-op if is called with a NULL pointer. Passing a NULL pointer
** a no-op if it is called with a NULL pointer. Passing a NULL pointer
** to sqlite3_free() is harmless. After being freed, memory
** should neither be read nor written. Even reading previously freed
** memory might result in a segmentation fault or other severe error.
@@ -3228,13 +3228,13 @@ char *sqlite3_vsnprintf(int,char*,const char*, va_list);
** sqlite3_free(X).
** ^sqlite3_realloc(X,N) returns a pointer to a memory allocation
** of at least N bytes in size or NULL if insufficient memory is available.
** ^If M is the size of the prior allocation, then min(N,M) bytes
** of the prior allocation are copied into the beginning of buffer returned
** ^If M is the size of the prior allocation, then min(N,M) bytes of the
** prior allocation are copied into the beginning of the buffer returned
** by sqlite3_realloc(X,N) and the prior allocation is freed.
** ^If sqlite3_realloc(X,N) returns NULL and N is positive, then the
** prior allocation is not freed.
**
** ^The sqlite3_realloc64(X,N) interfaces works the same as
** ^The sqlite3_realloc64(X,N) interface works the same as
** sqlite3_realloc(X,N) except that N is a 64-bit unsigned integer instead
** of a 32-bit signed integer.
**
@@ -3284,7 +3284,7 @@ sqlite3_uint64 sqlite3_msize(void*);
** was last reset. ^The values returned by [sqlite3_memory_used()] and
** [sqlite3_memory_highwater()] include any overhead
** added by SQLite in its implementation of [sqlite3_malloc()],
** but not overhead added by the any underlying system library
** but not overhead added by any underlying system library
** routines that [sqlite3_malloc()] may call.
**
** ^The memory high-water mark is reset to the current value of
@@ -3736,7 +3736,7 @@ void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** there is no harm in trying.)
**
** ^(<dt>[SQLITE_OPEN_SHAREDCACHE]</dt>
** <dd>The database is opened [shared cache] enabled, overriding
** <dd>The database is opened with [shared cache] enabled, overriding
** the default shared cache setting provided by
** [sqlite3_enable_shared_cache()].)^
** The [use of shared cache mode is discouraged] and hence shared cache
@@ -3744,7 +3744,7 @@ void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** this option is a no-op.
**
** ^(<dt>[SQLITE_OPEN_PRIVATECACHE]</dt>
** <dd>The database is opened [shared cache] disabled, overriding
** <dd>The database is opened with [shared cache] disabled, overriding
** the default shared cache setting provided by
** [sqlite3_enable_shared_cache()].)^
**
@@ -4162,7 +4162,7 @@ void sqlite3_free_filename(sqlite3_filename);
** subsequent calls to other SQLite interface functions.)^
**
** ^The sqlite3_errstr(E) interface returns the English-language text
** that describes the [result code] E, as UTF-8, or NULL if E is not an
** that describes the [result code] E, as UTF-8, or NULL if E is not a
** result code for which a text error message is available.
** ^(Memory to hold the error message string is managed internally
** and must not be freed by the application)^.
@@ -4170,7 +4170,7 @@ void sqlite3_free_filename(sqlite3_filename);
** ^If the most recent error references a specific token in the input
** SQL, the sqlite3_error_offset() interface returns the byte offset
** of the start of that token. ^The byte offset returned by
** sqlite3_error_offset() assumes that the input SQL is UTF8.
** sqlite3_error_offset() assumes that the input SQL is UTF-8.
** ^If the most recent error does not reference a specific token in the input
** SQL, then the sqlite3_error_offset() function returns -1.
**
@@ -4269,8 +4269,8 @@ int sqlite3_limit(sqlite3*, int id, int newVal);
**
** These constants define various performance limits
** that can be lowered at run-time using [sqlite3_limit()].
** The synopsis of the meanings of the various limits is shown below.
** Additional information is available at [limits | Limits in SQLite].
** A concise description of these limits follows, and additional information
** is available at [limits | Limits in SQLite].
**
** <dl>
** [[SQLITE_LIMIT_LENGTH]] ^(<dt>SQLITE_LIMIT_LENGTH</dt>
@@ -4335,7 +4335,7 @@ int sqlite3_limit(sqlite3*, int id, int newVal);
/*
** CAPI3REF: Prepare Flags
**
** These constants define various flags that can be passed into
** These constants define various flags that can be passed into the
** "prepFlags" parameter of the [sqlite3_prepare_v3()] and
** [sqlite3_prepare16_v3()] interfaces.
**
@@ -4422,7 +4422,7 @@ int sqlite3_limit(sqlite3*, int id, int newVal);
** there is a small performance advantage to passing an nByte parameter that
** is the number of bytes in the input string <i>including</i>
** the nul-terminator.
** Note that nByte measure the length of the input in bytes, not
** Note that nByte measures the length of the input in bytes, not
** characters, even for the UTF-16 interfaces.
**
** ^If pzTail is not NULL then *pzTail is made to point to the first byte
@@ -4556,7 +4556,7 @@ int sqlite3_prepare16_v3(
**
** ^The sqlite3_expanded_sql() interface returns NULL if insufficient memory
** is available to hold the result, or if the result would exceed the
** the maximum string length determined by the [SQLITE_LIMIT_LENGTH].
** maximum string length determined by the [SQLITE_LIMIT_LENGTH].
**
** ^The [SQLITE_TRACE_SIZE_LIMIT] compile-time option limits the size of
** bound parameter expansions. ^The [SQLITE_OMIT_TRACE] compile-time
@@ -4744,7 +4744,7 @@ typedef struct sqlite3_value sqlite3_value;
**
** The context in which an SQL function executes is stored in an
** sqlite3_context object. ^A pointer to an sqlite3_context object
** is always first parameter to [application-defined SQL functions].
** is always the first parameter to [application-defined SQL functions].
** The application-defined SQL function implementation will pass this
** pointer through into calls to [sqlite3_result_int | sqlite3_result()],
** [sqlite3_aggregate_context()], [sqlite3_user_data()],
@@ -5481,7 +5481,7 @@ int sqlite3_column_type(sqlite3_stmt*, int iCol);
**
** ^The sqlite3_finalize() function is called to delete a [prepared statement].
** ^If the most recent evaluation of the statement encountered no errors
** or if the statement is never been evaluated, then sqlite3_finalize() returns
** or if the statement has never been evaluated, then sqlite3_finalize() returns
** SQLITE_OK. ^If the most recent evaluation of statement S failed, then
** sqlite3_finalize(S) returns the appropriate [error code] or
** [extended error code].
@@ -5713,7 +5713,7 @@ int sqlite3_create_window_function(
/*
** CAPI3REF: Text Encodings
**
** These constant define integer codes that represent the various
** These constants define integer codes that represent the various
** text encodings supported by SQLite.
*/
#define SQLITE_UTF8 1 /* IMP: R-37514-35566 */
@@ -5805,7 +5805,7 @@ int sqlite3_create_window_function(
** result.
** Every function that invokes [sqlite3_result_subtype()] should have this
** property. If it does not, then the call to [sqlite3_result_subtype()]
** might become a no-op if the function is used as term in an
** might become a no-op if the function is used as a term in an
** [expression index]. On the other hand, SQL functions that never invoke
** [sqlite3_result_subtype()] should avoid setting this property, as the
** purpose of this property is to disable certain optimizations that are
@@ -5932,7 +5932,7 @@ SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),
** sqlite3_value_nochange(X) interface returns true if and only if
** the column corresponding to X is unchanged by the UPDATE operation
** that the xUpdate method call was invoked to implement and if
** and the prior [xColumn] method call that was invoked to extracted
** the prior [xColumn] method call that was invoked to extract
** the value for that column returned without setting a result (probably
** because it queried [sqlite3_vtab_nochange()] and found that the column
** was unchanging). ^Within an [xUpdate] method, any value for which
+33 -14
View File
@@ -765,7 +765,7 @@
** ourselves.
*/
#ifndef offsetof
#define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
# define offsetof(ST,M) ((size_t)((char*)&((ST*)0)->M - (char*)0))
#endif
/*
@@ -1153,6 +1153,8 @@ extern u32 sqlite3TreeTrace;
** 0x00020000 Transform DISTINCT into GROUP BY
** 0x00040000 SELECT tree dump after all code has been generated
** 0x00080000 NOT NULL strength reduction
** 0x00100000 Pointers are all shown as zero
** 0x00200000 EXISTS-to-JOIN optimization
*/
/*
@@ -1197,6 +1199,7 @@ extern u32 sqlite3WhereTrace;
** 0x00020000 Show WHERE terms returned from whereScanNext()
** 0x00040000 Solver overview messages
** 0x00080000 Star-query heuristic
** 0x00100000 Pointers are all shown as zero
*/
@@ -1269,7 +1272,7 @@ struct BusyHandler {
** pointer will work here as long as it is distinct from SQLITE_STATIC
** and SQLITE_TRANSIENT.
*/
#define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3OomClear)
#define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3RowSetClear)
/*
** When SQLITE_OMIT_WSD is defined, it means that the target platform does
@@ -1924,6 +1927,7 @@ struct sqlite3 {
#define SQLITE_OnePass 0x08000000 /* Single-pass DELETE and UPDATE */
#define SQLITE_OrderBySubq 0x10000000 /* ORDER BY in subquery helps outer */
#define SQLITE_StarQuery 0x20000000 /* Heurists for star queries */
#define SQLITE_ExistsToJoin 0x40000000 /* The EXISTS-to-JOIN optimization */
#define SQLITE_AllOpts 0xffffffff /* All optimizations */
/*
@@ -2645,9 +2649,15 @@ struct FKey {
** argument to sqlite3VdbeKeyCompare and is used to control the
** comparison of the two index keys.
**
** Note that aSortOrder[] and aColl[] have nField+1 slots. There
** are nField slots for the columns of an index then one extra slot
** for the rowid at the end.
** The aSortOrder[] and aColl[] arrays have nAllField slots each. There
** are nKeyField slots for the columns of an index then extra slots
** for the rowid or key at the end. The aSortOrder array is located after
** the aColl[] array.
**
** If SQLITE_ENABLE_PREUPDATE_HOOK is defined, then aSortFlags might be NULL
** to indicate that this object is for use by a preupdate hook. When aSortFlags
** is NULL, then nAllField is uninitialized and no space is allocated for
** aColl[], so those fields may not be used.
*/
struct KeyInfo {
u32 nRef; /* Number of references to this KeyInfo object */
@@ -2659,9 +2669,18 @@ struct KeyInfo {
CollSeq *aColl[FLEXARRAY]; /* Collating sequence for each term of the key */
};
/* The size (in bytes) of a KeyInfo object with up to N fields */
/* The size (in bytes) of a KeyInfo object with up to N fields. This includes
** the main body of the KeyInfo object and the aColl[] array of N elements,
** but does not count the memory used to hold aSortFlags[]. */
#define SZ_KEYINFO(N) (offsetof(KeyInfo,aColl) + (N)*sizeof(CollSeq*))
/* The size of a bare KeyInfo with no aColl[] entries */
#if FLEXARRAY+1 > 1
# define SZ_KEYINFO_0 offsetof(KeyInfo,aColl)
#else
# define SZ_KEYINFO_0 sizeof(KeyInfo)
#endif
/*
** Allowed bit values for entries in the KeyInfo.aSortFlags[] array.
*/
@@ -2680,9 +2699,8 @@ struct KeyInfo {
**
** An instance of this object serves as a "key" for doing a search on
** an index b+tree. The goal of the search is to find the entry that
** is closed to the key described by this object. This object might hold
** just a prefix of the key. The number of fields is given by
** pKeyInfo->nField.
** is closest to the key described by this object. This object might hold
** just a prefix of the key. The number of fields is given by nField.
**
** The r1 and r2 fields are the values to return if this key is less than
** or greater than a key in the btree, respectively. These are normally
@@ -2692,7 +2710,7 @@ struct KeyInfo {
** The key comparison functions actually return default_rc when they find
** an equals comparison. default_rc can be -1, 0, or +1. If there are
** multiple entries in the b-tree with the same key (when only looking
** at the first pKeyInfo->nFields,) then default_rc can be set to -1 to
** at the first nField elements) then default_rc can be set to -1 to
** cause the search to find the last match, or +1 to cause the search to
** find the first match.
**
@@ -2704,8 +2722,8 @@ struct KeyInfo {
** b-tree.
*/
struct UnpackedRecord {
KeyInfo *pKeyInfo; /* Collation and sort-order information */
Mem *aMem; /* Values */
KeyInfo *pKeyInfo; /* Comparison info for the index that is unpacked */
Mem *aMem; /* Values for columns of the index */
union {
char *z; /* Cache of aMem[0].z for vdbeRecordCompareString() */
i64 i; /* Cache of aMem[0].u.i for vdbeRecordCompareInt() */
@@ -3064,7 +3082,6 @@ struct Expr {
Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL
** for a column of an index on an expression */
Window *pWin; /* EP_WinFunc: Window/Filter defn for a function */
int nReg; /* TK_NULLS: Number of registers to NULL out */
struct { /* TK_IN, TK_SELECT, and TK_EXISTS */
int iAddr; /* Subroutine entry address */
int regReturn; /* Register used to hold return address */
@@ -3354,6 +3371,7 @@ struct SrcItem {
unsigned rowidUsed :1; /* The ROWID of this table is referenced */
unsigned fixedSchema :1; /* Uses u4.pSchema, not u4.zDatabase */
unsigned hadSchema :1; /* Had u4.zDatabase before u4.pSchema */
unsigned fromExists :1; /* Comes from WHERE EXISTS(...) */
} fg;
int iCursor; /* The VDBE cursor number used to access this table */
Bitmask colUsed; /* Bit N set if column N used. Details above for N>62 */
@@ -3884,6 +3902,7 @@ struct Parse {
u8 disableLookaside; /* Number of times lookaside has been disabled */
u8 prepFlags; /* SQLITE_PREPARE_* flags */
u8 withinRJSubrtn; /* Nesting level for RIGHT JOIN body subroutines */
u8 bHasExists; /* Has a correlated "EXISTS (SELECT ....)" expression */
u8 mSubrtnSig; /* mini Bloom filter on available SubrtnSig.selId */
u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */
u8 bReturning; /* Coding a RETURNING trigger */
@@ -4880,6 +4899,7 @@ char *sqlite3VMPrintf(sqlite3*,const char*, va_list);
void sqlite3ShowWindow(const Window*);
void sqlite3ShowWinFunc(const Window*);
#endif
void sqlite3ShowBitvec(Bitvec*);
#endif
void sqlite3SetString(char **, sqlite3*, const char*);
@@ -5099,7 +5119,6 @@ void sqlite3ExprCodeGeneratedColumn(Parse*, Table*, Column*, int);
void sqlite3ExprCodeCopy(Parse*, Expr*, int);
void sqlite3ExprCodeFactorable(Parse*, Expr*, int);
int sqlite3ExprCodeRunJustOnce(Parse*, Expr*, int);
void sqlite3ExprNullRegisterRange(Parse*, int, int);
int sqlite3ExprCodeTemp(Parse*, Expr*, int*);
int sqlite3ExprCodeTarget(Parse*, Expr*, int);
int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int, u8);
+97 -41
View File
@@ -49,6 +49,10 @@
# define CONST const
#elif !defined(Tcl_Size)
typedef int Tcl_Size;
# ifndef Tcl_BounceRefCount
# define Tcl_BounceRefCount(X) Tcl_IncrRefCount(X); Tcl_DecrRefCount(X)
/* https://www.tcl-lang.org/man/tcl9.0/TclLib/Object.html */
# endif
#endif
/**** End copy of tclsqlite.h ****/
@@ -1084,7 +1088,9 @@ static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){
Tcl_DecrRefCount(pCmd);
}
if( rc && rc!=TCL_RETURN ){
if( TCL_BREAK==rc ){
sqlite3_result_null(context);
}else if( rc && rc!=TCL_RETURN ){
sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1);
}else{
Tcl_Obj *pVar = Tcl_GetObjResult(p->interp);
@@ -1102,7 +1108,7 @@ static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){
}else if( (c=='b' && pVar->bytes==0 && strcmp(zType,"boolean")==0 )
|| (c=='b' && pVar->bytes==0 && strcmp(zType,"booleanString")==0 )
|| (c=='w' && strcmp(zType,"wideInt")==0)
|| (c=='i' && strcmp(zType,"int")==0)
|| (c=='i' && strcmp(zType,"int")==0)
){
eType = SQLITE_INTEGER;
}else if( c=='d' && strcmp(zType,"double")==0 ){
@@ -1616,11 +1622,12 @@ struct DbEvalContext {
SqlPreparedStmt *pPreStmt; /* Current statement */
int nCol; /* Number of columns returned by pStmt */
int evalFlags; /* Flags used */
Tcl_Obj *pArray; /* Name of array variable */
Tcl_Obj *pVarName; /* Name of target array/dict variable */
Tcl_Obj **apColName; /* Array of column names */
};
#define SQLITE_EVAL_WITHOUTNULLS 0x00001 /* Unset array(*) for NULL */
#define SQLITE_EVAL_ASDICT 0x00002 /* Use dict instead of array */
/*
** Release any cache of column names currently held as part of
@@ -1641,20 +1648,20 @@ static void dbReleaseColumnNames(DbEvalContext *p){
/*
** Initialize a DbEvalContext structure.
**
** If pArray is not NULL, then it contains the name of a Tcl array
** If pVarName is not NULL, then it contains the name of a Tcl array
** variable. The "*" member of this array is set to a list containing
** the names of the columns returned by the statement as part of each
** call to dbEvalStep(), in order from left to right. e.g. if the names
** of the returned columns are a, b and c, it does the equivalent of the
** tcl command:
**
** set ${pArray}(*) {a b c}
** set ${pVarName}(*) {a b c}
*/
static void dbEvalInit(
DbEvalContext *p, /* Pointer to structure to initialize */
SqliteDb *pDb, /* Database handle */
Tcl_Obj *pSql, /* Object containing SQL script */
Tcl_Obj *pArray, /* Name of Tcl array to set (*) element of */
Tcl_Obj *pVarName, /* Name of Tcl array to set (*) element of */
int evalFlags /* Flags controlling evaluation */
){
memset(p, 0, sizeof(DbEvalContext));
@@ -1662,9 +1669,9 @@ static void dbEvalInit(
p->zSql = Tcl_GetString(pSql);
p->pSql = pSql;
Tcl_IncrRefCount(pSql);
if( pArray ){
p->pArray = pArray;
Tcl_IncrRefCount(pArray);
if( pVarName ){
p->pVarName = pVarName;
Tcl_IncrRefCount(pVarName);
}
p->evalFlags = evalFlags;
addDatabaseRef(p->pDb);
@@ -1687,7 +1694,7 @@ static void dbEvalRowInfo(
Tcl_Obj **apColName = 0; /* Array of column names */
p->nCol = nCol = sqlite3_column_count(pStmt);
if( nCol>0 && (papColName || p->pArray) ){
if( nCol>0 && (papColName || p->pVarName) ){
apColName = (Tcl_Obj**)Tcl_Alloc( sizeof(Tcl_Obj*)*nCol );
for(i=0; i<nCol; i++){
apColName[i] = Tcl_NewStringObj(sqlite3_column_name(pStmt,i), -1);
@@ -1696,20 +1703,35 @@ static void dbEvalRowInfo(
p->apColName = apColName;
}
/* If results are being stored in an array variable, then create
** the array(*) entry for that array
/* If results are being stored in a variable then create the
** array(*) or dict(*) entry for that variable.
*/
if( p->pArray ){
if( p->pVarName ){
Tcl_Interp *interp = p->pDb->interp;
Tcl_Obj *pColList = Tcl_NewObj();
Tcl_Obj *pStar = Tcl_NewStringObj("*", -1);
Tcl_IncrRefCount(pColList);
Tcl_IncrRefCount(pStar);
for(i=0; i<nCol; i++){
Tcl_ListObjAppendElement(interp, pColList, apColName[i]);
}
Tcl_IncrRefCount(pStar);
Tcl_ObjSetVar2(interp, p->pArray, pStar, pColList, 0);
if( 0==(SQLITE_EVAL_ASDICT & p->evalFlags) ){
Tcl_ObjSetVar2(interp, p->pVarName, pStar, pColList, 0);
}else{
Tcl_Obj * pDict = Tcl_ObjGetVar2(interp, p->pVarName, NULL, 0);
if( !pDict ){
pDict = Tcl_NewDictObj();
}else if( Tcl_IsShared(pDict) ){
pDict = Tcl_DuplicateObj(pDict);
}
if( Tcl_DictObjPut(interp, pDict, pStar, pColList)==TCL_OK ){
Tcl_ObjSetVar2(interp, p->pVarName, NULL, pDict, 0);
}
Tcl_BounceRefCount(pDict);
}
Tcl_DecrRefCount(pStar);
Tcl_DecrRefCount(pColList);
}
}
@@ -1751,7 +1773,7 @@ static int dbEvalStep(DbEvalContext *p){
if( rcs==SQLITE_ROW ){
return TCL_OK;
}
if( p->pArray ){
if( p->pVarName ){
dbEvalRowInfo(p, 0, 0);
}
rcs = sqlite3_reset(pStmt);
@@ -1802,9 +1824,9 @@ static void dbEvalFinalize(DbEvalContext *p){
dbReleaseStmt(p->pDb, p->pPreStmt, 0);
p->pPreStmt = 0;
}
if( p->pArray ){
Tcl_DecrRefCount(p->pArray);
p->pArray = 0;
if( p->pVarName ){
Tcl_DecrRefCount(p->pVarName);
p->pVarName = 0;
}
Tcl_DecrRefCount(p->pSql);
dbReleaseColumnNames(p);
@@ -1879,7 +1901,7 @@ static int DbUseNre(void){
/*
** This function is part of the implementation of the command:
**
** $db eval SQL ?ARRAYNAME? SCRIPT
** $db eval SQL ?TGT-NAME? SCRIPT
*/
static int SQLITE_TCLAPI DbEvalNextCmd(
ClientData data[], /* data[0] is the (DbEvalContext*) */
@@ -1893,8 +1915,8 @@ static int SQLITE_TCLAPI DbEvalNextCmd(
** is a pointer to a Tcl_Obj containing the script to run for each row
** returned by the queries encapsulated in data[0]. */
DbEvalContext *p = (DbEvalContext *)data[0];
Tcl_Obj *pScript = (Tcl_Obj *)data[1];
Tcl_Obj *pArray = p->pArray;
Tcl_Obj * const pScript = (Tcl_Obj *)data[1];
Tcl_Obj * const pVarName = p->pVarName;
while( (rc==TCL_OK || rc==TCL_CONTINUE) && TCL_OK==(rc = dbEvalStep(p)) ){
int i;
@@ -1902,15 +1924,46 @@ static int SQLITE_TCLAPI DbEvalNextCmd(
Tcl_Obj **apColName;
dbEvalRowInfo(p, &nCol, &apColName);
for(i=0; i<nCol; i++){
if( pArray==0 ){
if( pVarName==0 ){
Tcl_ObjSetVar2(interp, apColName[i], 0, dbEvalColumnValue(p,i), 0);
}else if( (p->evalFlags & SQLITE_EVAL_WITHOUTNULLS)!=0
&& sqlite3_column_type(p->pPreStmt->pStmt, i)==SQLITE_NULL
&& sqlite3_column_type(p->pPreStmt->pStmt, i)==SQLITE_NULL
){
Tcl_UnsetVar2(interp, Tcl_GetString(pArray),
Tcl_GetString(apColName[i]), 0);
/* Remove NULL-containing column from the target container... */
if( 0==(SQLITE_EVAL_ASDICT & p->evalFlags) ){
/* Target is an array */
Tcl_UnsetVar2(interp, Tcl_GetString(pVarName),
Tcl_GetString(apColName[i]), 0);
}else{
/* Target is a dict */
Tcl_Obj *pDict = Tcl_ObjGetVar2(interp, pVarName, NULL, 0);
if( pDict ){
if( Tcl_IsShared(pDict) ){
pDict = Tcl_DuplicateObj(pDict);
}
if( Tcl_DictObjRemove(interp, pDict, apColName[i])==TCL_OK ){
Tcl_ObjSetVar2(interp, pVarName, NULL, pDict, 0);
}
Tcl_BounceRefCount(pDict);
}
}
}else if( 0==(SQLITE_EVAL_ASDICT & p->evalFlags) ){
/* Target is an array: set target(colName) = colValue */
Tcl_ObjSetVar2(interp, pVarName, apColName[i],
dbEvalColumnValue(p,i), 0);
}else{
Tcl_ObjSetVar2(interp, pArray, apColName[i], dbEvalColumnValue(p,i), 0);
/* Target is a dict: set target(colName) = colValue */
Tcl_Obj *pDict = Tcl_ObjGetVar2(interp, pVarName, NULL, 0);
if( !pDict ){
pDict = Tcl_NewDictObj();
}else if( Tcl_IsShared(pDict) ){
pDict = Tcl_DuplicateObj(pDict);
}
if( Tcl_DictObjPut(interp, pDict, apColName[i],
dbEvalColumnValue(p,i))==TCL_OK ){
Tcl_ObjSetVar2(interp, pVarName, NULL, pDict, 0);
}
Tcl_BounceRefCount(pDict);
}
}
@@ -2019,7 +2072,7 @@ static int SQLITE_TCLAPI DbObjCmd(
"timeout", "total_changes", "trace",
"trace_v2", "transaction", "unlock_notify",
"update_hook", "version", "wal_hook",
0
0
};
enum DB_enum {
DB_AUTHORIZER, DB_BACKUP, DB_BIND_FALLBACK,
@@ -2853,13 +2906,15 @@ deserialize_error:
}
/*
** $db eval ?options? $sql ?array? ?{ ...code... }?
** $db eval ?options? $sql ?varName? ?{ ...code... }?
**
** The SQL statement in $sql is evaluated. For each row, the values are
** placed in elements of the array named "array" and ...code... is executed.
** If "array" and "code" are omitted, then no callback is every invoked.
** If "array" is an empty string, then the values are placed in variables
** that have the same name as the fields extracted by the query.
** The SQL statement in $sql is evaluated. For each row, the values
** are placed in elements of the array or dict named $varName and
** ...code... is executed. If $varName and $code are omitted, then
** no callback is ever invoked. If $varName is an empty string,
** then the values are placed in variables that have the same name
** as the fields extracted by the query, and those variables are
** accessible during the eval of $code.
*/
case DB_EVAL: {
int evalFlags = 0;
@@ -2867,8 +2922,9 @@ deserialize_error:
while( objc>3 && (zOpt = Tcl_GetString(objv[2]))!=0 && zOpt[0]=='-' ){
if( strcmp(zOpt, "-withoutnulls")==0 ){
evalFlags |= SQLITE_EVAL_WITHOUTNULLS;
}
else{
}else if( strcmp(zOpt, "-asdict")==0 ){
evalFlags |= SQLITE_EVAL_ASDICT;
}else{
Tcl_AppendResult(interp, "unknown option: \"", zOpt, "\"", (void*)0);
return TCL_ERROR;
}
@@ -2876,8 +2932,8 @@ deserialize_error:
objv++;
}
if( objc<3 || objc>5 ){
Tcl_WrongNumArgs(interp, 2, objv,
"?OPTIONS? SQL ?ARRAY-NAME? ?SCRIPT?");
Tcl_WrongNumArgs(interp, 2, objv,
"?OPTIONS? SQL ?VAR-NAME? ?SCRIPT?");
return TCL_ERROR;
}
@@ -2903,17 +2959,17 @@ deserialize_error:
}else{
ClientData cd2[2];
DbEvalContext *p;
Tcl_Obj *pArray = 0;
Tcl_Obj *pVarName = 0;
Tcl_Obj *pScript;
if( objc>=5 && *(char *)Tcl_GetString(objv[3]) ){
pArray = objv[3];
pVarName = objv[3];
}
pScript = objv[objc-1];
Tcl_IncrRefCount(pScript);
p = (DbEvalContext *)Tcl_Alloc(sizeof(DbEvalContext));
dbEvalInit(p, pDb, objv[2], pArray, evalFlags);
dbEvalInit(p, pDb, objv[2], pVarName, evalFlags);
cd2[0] = (void *)p;
cd2[1] = (void *)pScript;
+5
View File
@@ -8458,7 +8458,12 @@ static int SQLITE_TCLAPI test_sqlite3_db_config(
{ "DQS_DML", SQLITE_DBCONFIG_DQS_DML },
{ "DQS_DDL", SQLITE_DBCONFIG_DQS_DDL },
{ "LEGACY_FILE_FORMAT", SQLITE_DBCONFIG_LEGACY_FILE_FORMAT },
{ "TRUSTED_SCHEMA", SQLITE_DBCONFIG_TRUSTED_SCHEMA },
{ "STMT_SCANSTATUS", SQLITE_DBCONFIG_STMT_SCANSTATUS },
{ "REVERSE_SCANORDER", SQLITE_DBCONFIG_REVERSE_SCANORDER },
{ "ATTACH_CREATE", SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE },
{ "ATTACH_WRITE", SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE },
{ "COMMENTS", SQLITE_DBCONFIG_ENABLE_COMMENTS },
};
int i;
int v = 0;
+4 -8
View File
@@ -72,12 +72,8 @@
#if !defined(_WIN32) || defined(__MSVCRT__)
# include <unistd.h>
# include <dirent.h>
# ifndef DIRENT
# define DIRENT dirent
# endif
#else
# include <io.h>
# include "test_windirent.h"
# include "windirent.h"
# ifndef S_ISREG
# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
# endif
@@ -121,7 +117,7 @@ struct FsdirCsr {
char *zDir; /* Buffer containing directory scanned */
DIR *pDir; /* Open directory */
sqlite3_int64 iRowid;
struct DIRENT *pEntry;
struct dirent *pEntry;
};
/*
@@ -483,9 +479,9 @@ static int fstreeFilter(
char aWild[2] = { '\0', '\0' };
#ifdef _WIN32
const char *zDrive = windirent_getenv("fstreeDrive");
const char *zDrive = getenv("fstreeDrive");
if( zDrive==0 ){
zDrive = windirent_getenv("SystemDrive");
zDrive = getenv("SystemDrive");
}
zRoot = sqlite3_mprintf("%s%c", zDrive, '/');
nRoot = sqlite3Strlen30(zRoot);
-162
View File
@@ -1,162 +0,0 @@
/*
** 2015 November 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 contains code to implement most of the opendir() family of
** POSIX functions on Win32 using the MSVCRT.
*/
#if defined(_WIN32) && defined(_MSC_VER)
#include "test_windirent.h"
/*
** Implementation of the POSIX getenv() function using the Win32 API.
** This function is not thread-safe.
*/
const char *windirent_getenv(
const char *name
){
static char value[32768]; /* Maximum length, per MSDN */
DWORD dwSize = sizeof(value) / sizeof(char); /* Size in chars */
DWORD dwRet; /* Value returned by GetEnvironmentVariableA() */
memset(value, 0, sizeof(value));
dwRet = GetEnvironmentVariableA(name, value, dwSize);
if( dwRet==0 || dwRet>dwSize ){
/*
** The function call to GetEnvironmentVariableA() failed -OR-
** the buffer is not large enough. Either way, return NULL.
*/
return 0;
}else{
/*
** The function call to GetEnvironmentVariableA() succeeded
** -AND- the buffer contains the entire value.
*/
return value;
}
}
/*
** Implementation of the POSIX opendir() function using the MSVCRT.
*/
LPDIR opendir(
const char *dirname /* Directory name, UTF8 encoding */
){
struct _wfinddata_t data;
LPDIR dirp = (LPDIR)sqlite3_malloc(sizeof(DIR));
SIZE_T namesize = sizeof(data.name) / sizeof(data.name[0]);
wchar_t *b1;
sqlite3_int64 sz;
if( dirp==NULL ) return NULL;
memset(dirp, 0, sizeof(DIR));
/* TODO: Remove this if Unix-style root paths are not used. */
if( sqlite3_stricmp(dirname, "/")==0 ){
dirname = windirent_getenv("SystemDrive");
}
memset(&data, 0, sizeof(data));
sz = strlen(dirname);
b1 = sqlite3_malloc64( (sz+3)*sizeof(b1[0]) );
if( b1==0 ){
closedir(dirp);
return NULL;
}
sz = MultiByteToWideChar(CP_UTF8, 0, dirname, sz, b1, sz);
b1[sz++] = '\\';
b1[sz++] = '*';
b1[sz] = 0;
if( sz+1>(sqlite3_int64)namesize ){
closedir(dirp);
sqlite3_free(b1);
return NULL;
}
memcpy(data.name, b1, (sz+1)*sizeof(b1[0]));
sqlite3_free(b1);
dirp->d_handle = _wfindfirst(data.name, &data);
if( dirp->d_handle==BAD_INTPTR_T ){
closedir(dirp);
return NULL;
}
/* TODO: Remove this block to allow hidden and/or system files. */
if( is_filtered(data) ){
next:
memset(&data, 0, sizeof(data));
if( _wfindnext(dirp->d_handle, &data)==-1 ){
closedir(dirp);
return NULL;
}
/* TODO: Remove this block to allow hidden and/or system files. */
if( is_filtered(data) ) goto next;
}
dirp->d_first.d_attributes = data.attrib;
WideCharToMultiByte(CP_UTF8, 0, data.name, -1,
dirp->d_first.d_name, DIRENT_NAME_MAX, 0, 0);
return dirp;
}
/*
** Implementation of the POSIX readdir() function using the MSVCRT.
*/
LPDIRENT readdir(
LPDIR dirp
){
struct _wfinddata_t data;
if( dirp==NULL ) return NULL;
if( dirp->d_first.d_ino==0 ){
dirp->d_first.d_ino++;
dirp->d_next.d_ino++;
return &dirp->d_first;
}
next:
memset(&data, 0, sizeof(data));
if( _wfindnext(dirp->d_handle, &data)==-1 ) return NULL;
/* TODO: Remove this block to allow hidden and/or system files. */
if( is_filtered(data) ) goto next;
dirp->d_next.d_ino++;
dirp->d_next.d_attributes = data.attrib;
WideCharToMultiByte(CP_UTF8, 0, data.name, -1,
dirp->d_next.d_name, DIRENT_NAME_MAX, 0, 0);
return &dirp->d_next;
}
/*
** Implementation of the POSIX closedir() function using the MSVCRT.
*/
INT closedir(
LPDIR dirp
){
INT result = 0;
if( dirp==NULL ) return EINVAL;
if( dirp->d_handle!=NULL_INTPTR_T && dirp->d_handle!=BAD_INTPTR_T ){
result = _findclose(dirp->d_handle);
}
sqlite3_free(dirp);
return result;
}
#endif /* defined(WIN32) && defined(_MSC_VER) */
-158
View File
@@ -1,158 +0,0 @@
/*
** 2015 November 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 contains declarations for most of the opendir() family of
** POSIX functions on Win32 using the MSVCRT.
*/
#if defined(_WIN32) && defined(_MSC_VER) && !defined(SQLITE_WINDIRENT_H)
#define SQLITE_WINDIRENT_H
/*
** We need several data types from the Windows SDK header.
*/
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include "windows.h"
/*
** We need several support functions from the SQLite core.
*/
#include "sqlite3.h"
/*
** We need several things from the ANSI and MSVCRT headers.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <io.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
/*
** We may need several defines that should have been in "sys/stat.h".
*/
#ifndef S_ISREG
#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
#endif
#ifndef S_ISDIR
#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
#endif
#ifndef S_ISLNK
#define S_ISLNK(mode) (0)
#endif
/*
** We may need to provide the "mode_t" type.
*/
#ifndef MODE_T_DEFINED
#define MODE_T_DEFINED
typedef unsigned short mode_t;
#endif
/*
** We may need to provide the "ino_t" type.
*/
#ifndef INO_T_DEFINED
#define INO_T_DEFINED
typedef unsigned short ino_t;
#endif
/*
** We need to define "NAME_MAX" if it was not present in "limits.h".
*/
#ifndef NAME_MAX
# ifdef FILENAME_MAX
# define NAME_MAX (FILENAME_MAX)
# else
# define NAME_MAX (260)
# endif
# define DIRENT_NAME_MAX (NAME_MAX)
#endif
/*
** We need to define "NULL_INTPTR_T" and "BAD_INTPTR_T".
*/
#ifndef NULL_INTPTR_T
# define NULL_INTPTR_T ((intptr_t)(0))
#endif
#ifndef BAD_INTPTR_T
# define BAD_INTPTR_T ((intptr_t)(-1))
#endif
/*
** We need to provide the necessary structures and related types.
*/
#ifndef DIRENT_DEFINED
#define DIRENT_DEFINED
typedef struct DIRENT DIRENT;
typedef DIRENT *LPDIRENT;
struct DIRENT {
ino_t d_ino; /* Sequence number, do not use. */
unsigned d_attributes; /* Win32 file attributes. */
char d_name[NAME_MAX + 1]; /* Name within the directory. */
};
#endif
#ifndef DIR_DEFINED
#define DIR_DEFINED
typedef struct DIR DIR;
typedef DIR *LPDIR;
struct DIR {
intptr_t d_handle; /* Value returned by "_findfirst". */
DIRENT d_first; /* DIRENT constructed based on "_findfirst". */
DIRENT d_next; /* DIRENT constructed based on "_findnext". */
};
#endif
/*
** Provide a macro, for use by the implementation, to determine if a
** particular directory entry should be skipped over when searching for
** the next directory entry that should be returned by the readdir().
*/
#ifndef is_filtered
# define is_filtered(a) ((((a).attrib)&_A_HIDDEN) || (((a).attrib)&_A_SYSTEM))
#endif
/*
** Provide the function prototype for the POSIX compatible getenv()
** function. This function is not thread-safe.
*/
extern const char *windirent_getenv(const char *name);
/*
** Finally, we can provide the function prototypes for the opendir(),
** readdir(), and closedir() POSIX functions.
*/
extern LPDIR opendir(const char *dirname);
extern LPDIRENT readdir(LPDIR dirp);
extern INT closedir(LPDIR dirp);
#endif /* defined(WIN32) && defined(_MSC_VER) */
+2 -1
View File
@@ -195,7 +195,8 @@ SQLITE_NOINLINE int sqlite3RunVacuum(
saved_nChange = db->nChange;
saved_nTotalChange = db->nTotalChange;
saved_mTrace = db->mTrace;
db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_Comments;
db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_Comments
| SQLITE_AttachCreate | SQLITE_AttachWrite;
db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum;
db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder
| SQLITE_Defensive | SQLITE_CountRows);
+95 -12
View File
@@ -792,6 +792,36 @@ static SQLITE_NOINLINE int vdbeColumnFromOverflow(
return rc;
}
/*
** Send a "statement aborts" message to the error log.
*/
static SQLITE_NOINLINE void sqlite3VdbeLogAbort(
Vdbe *p, /* The statement that is running at the time of failure */
int rc, /* Error code */
Op *pOp, /* Opcode that filed */
Op *aOp /* All opcodes */
){
const char *zSql = p->zSql; /* Original SQL text */
const char *zPrefix = ""; /* Prefix added to SQL text */
int pc; /* Opcode address */
char zXtra[100]; /* Buffer space to store zPrefix */
if( p->pFrame ){
assert( aOp[0].opcode==OP_Init );
if( aOp[0].p4.z!=0 ){
assert( aOp[0].p4.z[0]=='-'
&& aOp[0].p4.z[1]=='-'
&& aOp[0].p4.z[2]==' ' );
sqlite3_snprintf(sizeof(zXtra), zXtra,"/* %s */ ",aOp[0].p4.z+3);
zPrefix = zXtra;
}else{
zPrefix = "/* unknown trigger */ ";
}
}
pc = (int)(pOp - aOp);
sqlite3_log(rc, "statement aborts at %d: %s; [%s%s]",
pc, p->zErrMsg, zPrefix, zSql);
}
/*
** Return the symbolic name for the data type of a pMem
@@ -1317,8 +1347,7 @@ case OP_Halt: {
}else{
sqlite3VdbeError(p, "%s", pOp->p4.z);
}
pcx = (int)(pOp - aOp);
sqlite3_log(pOp->p1, "abort at %d: %s; [%s]", pcx, p->zErrMsg, p->zSql);
sqlite3VdbeLogAbort(p, pOp->p1, pOp, aOp);
}
rc = sqlite3VdbeHalt(p);
assert( rc==SQLITE_BUSY || rc==SQLITE_OK || rc==SQLITE_ERROR );
@@ -2476,6 +2505,7 @@ case OP_Compare: {
pKeyInfo = pOp->p4.pKeyInfo;
assert( n>0 );
assert( pKeyInfo!=0 );
assert( pKeyInfo->aSortFlags!=0 );
p1 = pOp->p1;
p2 = pOp->p2;
#ifdef SQLITE_DEBUG
@@ -3238,6 +3268,15 @@ op_column_corrupt:
** Take the affinities from the Table object in P4. If any value
** cannot be coerced into the correct type, then raise an error.
**
** If P3==0, then omit checking of VIRTUAL columns.
**
** If P3==1, then omit checking of all generated column, both VIRTUAL
** and STORED.
**
** If P3>=2, then only check column number P3-2 in the table (which will
** be a VIRTUAL column) against the value in reg[P1]. In this case,
** P2 will be 1.
**
** This opcode is similar to OP_Affinity except that this opcode
** forces the register type to the Table column type. This is used
** to implement "strict affinity".
@@ -3251,8 +3290,8 @@ op_column_corrupt:
**
** <ul>
** <li> P2 should be the number of non-virtual columns in the
** table of P4.
** <li> Table P4 should be a STRICT table.
** table of P4 unless P3>1, in which case P2 will be 1.
** <li> Table P4 is a STRICT table.
** </ul>
**
** If any precondition is false, an assertion fault occurs.
@@ -3261,16 +3300,28 @@ case OP_TypeCheck: {
Table *pTab;
Column *aCol;
int i;
int nCol;
assert( pOp->p4type==P4_TABLE );
pTab = pOp->p4.pTab;
assert( pTab->tabFlags & TF_Strict );
assert( pTab->nNVCol==pOp->p2 );
assert( pOp->p3>=0 && pOp->p3<pTab->nCol+2 );
aCol = pTab->aCol;
pIn1 = &aMem[pOp->p1];
for(i=0; i<pTab->nCol; i++){
if( aCol[i].colFlags & COLFLAG_GENERATED ){
if( aCol[i].colFlags & COLFLAG_VIRTUAL ) continue;
if( pOp->p3<2 ){
assert( pTab->nNVCol==pOp->p2 );
i = 0;
nCol = pTab->nCol;
}else{
i = pOp->p3-2;
nCol = i+1;
assert( i<pTab->nCol );
assert( aCol[i].colFlags & COLFLAG_VIRTUAL );
assert( pOp->p2==1 );
}
for(; i<nCol; i++){
if( (aCol[i].colFlags & COLFLAG_GENERATED)!=0 && pOp->p3<2 ){
if( (aCol[i].colFlags & COLFLAG_VIRTUAL)!=0 ) continue;
if( pOp->p3 ){ pIn1++; continue; }
}
assert( pIn1 < &aMem[pOp->p1+pOp->p2] );
@@ -5349,7 +5400,7 @@ case OP_Found: { /* jump, in3, ncycle */
if( rc ) goto no_mem;
pIdxKey = sqlite3VdbeAllocUnpackedRecord(pC->pKeyInfo);
if( pIdxKey==0 ) goto no_mem;
sqlite3VdbeRecordUnpack(pC->pKeyInfo, r.aMem->n, r.aMem->z, pIdxKey);
sqlite3VdbeRecordUnpack(r.aMem->n, r.aMem->z, pIdxKey);
pIdxKey->default_rc = 0;
rc = sqlite3BtreeIndexMoveto(pC->uc.pCursor, pIdxKey, &pC->seekResult);
sqlite3DbFreeNN(db, pIdxKey);
@@ -6347,6 +6398,32 @@ case OP_Rewind: { /* jump0, ncycle */
break;
}
/* Opcode: IfEmpty P1 P2 * * *
** Synopsis: if( empty(P1) ) goto P2
**
** Check to see if the b-tree table that cursor P1 references is empty
** and jump to P2 if it is.
*/
case OP_IfEmpty: { /* jump */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
assert( pOp->p2>=0 && pOp->p2<p->nOp );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
assert( pC->eCurType==CURTYPE_BTREE );
pCrsr = pC->uc.pCursor;
assert( pCrsr );
rc = sqlite3BtreeIsEmpty(pCrsr, &res);
if( rc ) goto abort_due_to_error;
VdbeBranchTaken(res!=0,2);
if( res ) goto jump_to_p2;
break;
}
/* Opcode: Next P1 P2 P3 * P5
**
** Advance cursor P1 so that it points to the next key/data pair in its
@@ -8218,7 +8295,14 @@ case OP_VOpen: { /* ncycle */
const sqlite3_module *pModule;
assert( p->bIsReader );
pCur = 0;
pCur = p->apCsr[pOp->p1];
if( pCur!=0
&& ALWAYS( pCur->eCurType==CURTYPE_VTAB )
&& ALWAYS( pCur->uc.pVCur->pVtab==pOp->p4.pVtab->pVtab )
){
/* This opcode is a no-op if the cursor is already open */
break;
}
pVCur = 0;
pVtab = pOp->p4.pVtab->pVtab;
if( pVtab==0 || NEVER(pVtab->pModule==0) ){
@@ -9160,8 +9244,7 @@ abort_due_to_error:
p->rc = rc;
sqlite3SystemError(db, rc);
testcase( sqlite3GlobalConfig.xLog!=0 );
sqlite3_log(rc, "statement aborts at %d: %s; [%s]",
(int)(pOp - aOp), p->zErrMsg, p->zSql);
sqlite3VdbeLogAbort(p, rc, pOp, aOp);
if( p->eVdbeState==VDBE_RUN_STATE ) sqlite3VdbeHalt(p);
if( rc==SQLITE_IOERR_NOMEM ) sqlite3OomFault(db);
if( rc==SQLITE_CORRUPT && db->autoCommit==0 ){
+1 -1
View File
@@ -301,7 +301,7 @@ void sqlite3VdbeSetVarmask(Vdbe*, int);
int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*);
int sqlite3BlobCompare(const Mem*, const Mem*);
void sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,UnpackedRecord*);
void sqlite3VdbeRecordUnpack(int,const void*,UnpackedRecord*);
int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*);
int sqlite3VdbeRecordCompareWithSkip(int, const void *, UnpackedRecord *, int);
UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo*);
+1 -1
View File
@@ -557,7 +557,7 @@ struct PreUpdate {
Table *pTab; /* Schema object being updated */
Index *pPk; /* PK index if pTab is WITHOUT ROWID */
sqlite3_value **apDflt; /* Array of default values, if required */
u8 keyinfoSpace[SZ_KEYINFO(0)]; /* Space to hold pKeyinfo[0] content */
u8 keyinfoSpace[SZ_KEYINFO_0]; /* Space to hold pKeyinfo[0] content */
};
/*
+6 -1
View File
@@ -2163,7 +2163,7 @@ static UnpackedRecord *vdbeUnpackRecord(
pRet = sqlite3VdbeAllocUnpackedRecord(pKeyInfo);
if( pRet ){
memset(pRet->aMem, 0, sizeof(Mem)*(pKeyInfo->nKeyField+1));
sqlite3VdbeRecordUnpack(pKeyInfo, nKey, pKey, pRet);
sqlite3VdbeRecordUnpack(nKey, pKey, pRet);
}
return pRet;
}
@@ -2192,6 +2192,9 @@ int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
}
if( p->pPk ){
iStore = sqlite3TableColumnToIndex(p->pPk, iIdx);
}else if( iIdx >= p->pTab->nCol ){
rc = SQLITE_MISUSE_BKPT;
goto preupdate_old_out;
}else{
iStore = sqlite3TableColumnToStorage(p->pTab, iIdx);
}
@@ -2347,6 +2350,8 @@ int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
}
if( p->pPk && p->op!=SQLITE_UPDATE ){
iStore = sqlite3TableColumnToIndex(p->pPk, iIdx);
}else if( iIdx >= p->pTab->nCol ){
return SQLITE_MISUSE_BKPT;
}else{
iStore = sqlite3TableColumnToStorage(p->pTab, iIdx);
}
+10 -16
View File
@@ -4202,30 +4202,22 @@ void sqlite3VdbeSerialGet(
return;
}
/*
** This routine is used to allocate sufficient space for an UnpackedRecord
** structure large enough to be used with sqlite3VdbeRecordUnpack() if
** the first argument is a pointer to KeyInfo structure pKeyInfo.
** Allocate sufficient space for an UnpackedRecord structure large enough
** to hold a decoded index record for pKeyInfo.
**
** The space is either allocated using sqlite3DbMallocRaw() or from within
** the unaligned buffer passed via the second and third arguments (presumably
** stack space). If the former, then *ppFree is set to a pointer that should
** be eventually freed by the caller using sqlite3DbFree(). Or, if the
** allocation comes from the pSpace/szSpace buffer, *ppFree is set to NULL
** before returning.
**
** If an OOM error occurs, NULL is returned.
** The space is allocated using sqlite3DbMallocRaw(). If an OOM error
** occurs, NULL is returned.
*/
UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(
KeyInfo *pKeyInfo /* Description of the record */
){
UnpackedRecord *p; /* Unpacked record to return */
int nByte; /* Number of bytes required for *p */
u64 nByte; /* Number of bytes required for *p */
assert( sizeof(UnpackedRecord) + sizeof(Mem)*65536 < 0x7fffffff );
nByte = ROUND8P(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nKeyField+1);
p = (UnpackedRecord *)sqlite3DbMallocRaw(pKeyInfo->db, nByte);
if( !p ) return 0;
p->aMem = (Mem*)&((char*)p)[ROUND8P(sizeof(UnpackedRecord))];
assert( pKeyInfo->aSortFlags!=0 );
p->pKeyInfo = pKeyInfo;
p->nField = pKeyInfo->nKeyField + 1;
return p;
@@ -4237,7 +4229,6 @@ UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(
** contents of the decoded record.
*/
void sqlite3VdbeRecordUnpack(
KeyInfo *pKeyInfo, /* Information about the record format */
int nKey, /* Size of the binary record */
const void *pKey, /* The binary record */
UnpackedRecord *p /* Populate this structure before returning. */
@@ -4248,6 +4239,7 @@ void sqlite3VdbeRecordUnpack(
u16 u; /* Unsigned loop counter */
u32 szHdr;
Mem *pMem = p->aMem;
KeyInfo *pKeyInfo = p->pKeyInfo;
p->default_rc = 0;
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
@@ -4275,6 +4267,8 @@ void sqlite3VdbeRecordUnpack(
** warnings from MSAN. */
sqlite3VdbeMemSetNull(pMem-1);
}
testcase( u == pKeyInfo->nKeyField + 1 );
testcase( u < pKeyInfo->nKeyField + 1 );
assert( u<=pKeyInfo->nKeyField + 1 );
p->nField = u;
}
@@ -5134,6 +5128,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. */
assert( p->pKeyInfo->aSortFlags!=0 );
if( p->pKeyInfo->nAllField<=13 ){
int flags = p->aMem[0].flags;
if( p->pKeyInfo->aSortFlags[0] ){
@@ -5492,7 +5487,6 @@ void sqlite3VdbePreUpdateHook(
i64 iKey2;
PreUpdate preupdate;
const char *zTbl = pTab->zName;
static const u8 fakeSortOrder = 0;
#ifdef SQLITE_DEBUG
int nRealCol;
if( pTab->tabFlags & TF_WithoutRowid ){
@@ -5531,7 +5525,7 @@ void sqlite3VdbePreUpdateHook(
preupdate.pKeyinfo->db = db;
preupdate.pKeyinfo->enc = ENC(db);
preupdate.pKeyinfo->nKeyField = pTab->nCol;
preupdate.pKeyinfo->aSortFlags = (u8*)&fakeSortOrder;
preupdate.pKeyinfo->aSortFlags = 0; /* Indicate .aColl, .nAllField uninit */
preupdate.iKey1 = iKey1;
preupdate.iKey2 = iKey2;
preupdate.pTab = pTab;
+24 -9
View File
@@ -385,7 +385,7 @@ static int blobReadWrite(
int iOffset,
int (*xCall)(BtCursor*, u32, u32, void*)
){
int rc;
int rc = SQLITE_OK;
Incrblob *p = (Incrblob *)pBlob;
Vdbe *v;
sqlite3 *db;
@@ -425,17 +425,32 @@ static int blobReadWrite(
** using the incremental-blob API, this works. For the sessions module
** anyhow.
*/
sqlite3_int64 iKey;
iKey = sqlite3BtreeIntegerKey(p->pCsr);
assert( v->apCsr[0]!=0 );
assert( v->apCsr[0]->eCurType==CURTYPE_BTREE );
sqlite3VdbePreUpdateHook(
v, v->apCsr[0], SQLITE_DELETE, p->zDb, p->pTab, iKey, -1, p->iCol
);
if( sqlite3BtreeCursorIsValidNN(p->pCsr)==0 ){
/* If the cursor is not currently valid, try to reseek it. This
** always either fails or finds the correct row - the cursor will
** have been marked permanently CURSOR_INVALID if the open row has
** been deleted. */
int bDiff = 0;
rc = sqlite3BtreeCursorRestore(p->pCsr, &bDiff);
assert( bDiff==0 || sqlite3BtreeCursorIsValidNN(p->pCsr)==0 );
}
if( sqlite3BtreeCursorIsValidNN(p->pCsr) ){
sqlite3_int64 iKey;
iKey = sqlite3BtreeIntegerKey(p->pCsr);
assert( v->apCsr[0]!=0 );
assert( v->apCsr[0]->eCurType==CURTYPE_BTREE );
sqlite3VdbePreUpdateHook(
v, v->apCsr[0], SQLITE_DELETE, p->zDb, p->pTab, iKey, -1, p->iCol
);
}
}
if( rc==SQLITE_OK ){
rc = xCall(p->pCsr, iOffset+p->iOffset, n, z);
}
#else
rc = xCall(p->pCsr, iOffset+p->iOffset, n, z);
#endif
rc = xCall(p->pCsr, iOffset+p->iOffset, n, z);
sqlite3BtreeLeaveCursor(p->pCsr);
if( rc==SQLITE_ABORT ){
sqlite3VdbeFinalize(v);
+12 -4
View File
@@ -766,7 +766,7 @@ static int vdbeSorterCompareTail(
){
UnpackedRecord *r2 = pTask->pUnpacked;
if( *pbKey2Cached==0 ){
sqlite3VdbeRecordUnpack(pTask->pSorter->pKeyInfo, nKey2, pKey2, r2);
sqlite3VdbeRecordUnpack(nKey2, pKey2, r2);
*pbKey2Cached = 1;
}
return sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, r2, 1);
@@ -793,7 +793,7 @@ static int vdbeSorterCompare(
){
UnpackedRecord *r2 = pTask->pUnpacked;
if( !*pbKey2Cached ){
sqlite3VdbeRecordUnpack(pTask->pSorter->pKeyInfo, nKey2, pKey2, r2);
sqlite3VdbeRecordUnpack(nKey2, pKey2, r2);
*pbKey2Cached = 1;
}
return sqlite3VdbeRecordCompare(nKey1, pKey1, r2);
@@ -833,6 +833,7 @@ static int vdbeSorterCompareText(
);
}
}else{
assert( pTask->pSorter->pKeyInfo->aSortFlags!=0 );
assert( !(pTask->pSorter->pKeyInfo->aSortFlags[0]&KEYINFO_ORDER_BIGNULL) );
if( pTask->pSorter->pKeyInfo->aSortFlags[0] ){
res = res * -1;
@@ -896,6 +897,7 @@ static int vdbeSorterCompareInt(
}
}
assert( pTask->pSorter->pKeyInfo->aSortFlags!=0 );
if( res==0 ){
if( pTask->pSorter->pKeyInfo->nKeyField>1 ){
res = vdbeSorterCompareTail(
@@ -969,7 +971,8 @@ int sqlite3VdbeSorterInit(
assert( pCsr->eCurType==CURTYPE_SORTER );
assert( sizeof(KeyInfo) + UMXV(pCsr->pKeyInfo->nKeyField)*sizeof(CollSeq*)
< 0x7fffffff );
szKeyInfo = SZ_KEYINFO(pCsr->pKeyInfo->nKeyField);
assert( pCsr->pKeyInfo->nKeyField<=pCsr->pKeyInfo->nAllField );
szKeyInfo = SZ_KEYINFO(pCsr->pKeyInfo->nAllField);
sz = SZ_VDBESORTER(nWorker+1);
pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo);
@@ -983,7 +986,12 @@ int sqlite3VdbeSorterInit(
pKeyInfo->db = 0;
if( nField && nWorker==0 ){
pKeyInfo->nKeyField = nField;
assert( nField<=pCsr->pKeyInfo->nAllField );
}
/* It is OK that pKeyInfo reuses the aSortFlags field from pCsr->pKeyInfo,
** since the pCsr->pKeyInfo->aSortFlags[] array is invariant and lives
** longer that pSorter. */
assert( pKeyInfo->aSortFlags==pCsr->pKeyInfo->aSortFlags );
sqlite3BtreeEnter(pBt);
pSorter->pgsz = pgsz = sqlite3BtreeGetPageSize(pBt);
sqlite3BtreeLeave(pBt);
@@ -2763,7 +2771,7 @@ int sqlite3VdbeSorterCompare(
assert( r2->nField==nKeyCol );
pKey = vdbeSorterRowkey(pSorter, &nKey);
sqlite3VdbeRecordUnpack(pKeyInfo, nKey, pKey, r2);
sqlite3VdbeRecordUnpack(nKey, pKey, r2);
for(i=0; i<nKeyCol; i++){
if( r2->aMem[i].flags & MEM_Null ){
*pRes = -1;
+33 -7
View File
@@ -426,11 +426,11 @@ static WhereTerm *whereScanNext(WhereScan *pScan){
pScan->pWC = pWC;
pScan->k = k+1;
#ifdef WHERETRACE_ENABLED
if( sqlite3WhereTrace & 0x20000 ){
if( (sqlite3WhereTrace & 0x20000)!=0 && pScan->nEquiv>1 ){
int ii;
sqlite3DebugPrintf("SCAN-TERM %p: nEquiv=%d",
pTerm, pScan->nEquiv);
for(ii=0; ii<pScan->nEquiv; ii++){
sqlite3DebugPrintf("EQUIVALENT TO {%d:%d} (due to TERM-%d):",
pScan->aiCur[0], pScan->aiColumn[0], pTerm->iTerm);
for(ii=1; ii<pScan->nEquiv; ii++){
sqlite3DebugPrintf(" {%d:%d}",
pScan->aiCur[ii], pScan->aiColumn[ii]);
}
@@ -1201,7 +1201,9 @@ static SQLITE_NOINLINE void constructAutomaticIndex(
VdbeCoverage(v);
VdbeComment((v, "next row of %s", pSrc->pSTab->zName));
}else{
addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); VdbeCoverage(v);
assert( pLevel->addrHalt );
addrTop = sqlite3VdbeAddOp2(v, OP_Rewind,pLevel->iTabCur,pLevel->addrHalt);
VdbeCoverage(v);
}
if( pPartial ){
iContinue = sqlite3VdbeMakeLabel(pParse);
@@ -1229,11 +1231,14 @@ static SQLITE_NOINLINE void constructAutomaticIndex(
pSrc->u4.pSubq->regResult, pLevel->iIdxCur);
sqlite3VdbeGoto(v, addrTop);
pSrc->fg.viaCoroutine = 0;
sqlite3VdbeJumpHere(v, addrTop);
}else{
sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v);
sqlite3VdbeChangeP5(v, SQLITE_STMTSTATUS_AUTOINDEX);
if( (pSrc->fg.jointype & JT_LEFT)!=0 ){
sqlite3VdbeJumpHere(v, addrTop);
}
}
sqlite3VdbeJumpHere(v, addrTop);
sqlite3ReleaseTempReg(pParse, regRecord);
/* Jump here when skipping the initialization */
@@ -2385,6 +2390,7 @@ void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm){
}else{
sqlite3_snprintf(sizeof(zLeft),zLeft,"left=%d", pTerm->leftCursor);
}
iTerm = pTerm->iTerm = MAX(iTerm,pTerm->iTerm);
sqlite3DebugPrintf(
"TERM-%-3d %p %s %-12s op=%03x wtFlags=%04x",
iTerm, pTerm, zType, zLeft, pTerm->eOperator, pTerm->wtFlags);
@@ -3526,6 +3532,7 @@ static int whereLoopAddBtreeIndex(
&& pProbe->hasStat1!=0
&& OptimizationEnabled(db, SQLITE_SkipScan)
&& pProbe->aiRowLogEst[saved_nEq+1]>=42 /* TUNING: Minimum for skip-scan */
&& pSrc->fg.fromExists==0
&& (rc = whereLoopResize(db, pNew, pNew->nLTerm+1))==SQLITE_OK
){
LogEst nIter;
@@ -4030,7 +4037,6 @@ static int whereLoopAddBtree(
pNew->u.btree.nEq = 0;
pNew->u.btree.nBtm = 0;
pNew->u.btree.nTop = 0;
pNew->u.btree.nDistinctCol = 0;
pNew->nSkip = 0;
pNew->nLTerm = 0;
pNew->iSortIdx = 0;
@@ -5099,6 +5105,8 @@ static i8 wherePathSatisfiesOrderBy(
obSat = obDone;
}
break;
}else if( wctrlFlags & WHERE_DISTINCTBY ){
pLoop->u.btree.nDistinctCol = 0;
}
iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor;
@@ -7082,6 +7090,14 @@ WhereInfo *sqlite3WhereBegin(
pTab = pTabItem->pSTab;
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
pLoop = pLevel->pWLoop;
pLevel->addrBrk = sqlite3VdbeMakeLabel(pParse);
if( ii==0 || (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){
pLevel->addrHalt = pLevel->addrBrk;
}else if( pWInfo->a[ii-1].pRJ ){
pLevel->addrHalt = pWInfo->a[ii-1].addrBrk;
}else{
pLevel->addrHalt = pWInfo->a[ii-1].addrHalt;
}
if( (pTab->tabFlags & TF_Ephemeral)!=0 || IsView(pTab) ){
/* Do nothing */
}else
@@ -7133,6 +7149,13 @@ WhereInfo *sqlite3WhereBegin(
sqlite3VdbeAddOp4Dup8(v, OP_ColumnsUsed, pTabItem->iCursor, 0, 0,
(const u8*)&pTabItem->colUsed, P4_INT64);
#endif
if( ii>=2
&& (pTabItem[0].fg.jointype & (JT_LTORJ|JT_LEFT))==0
&& pLevel->addrHalt==pWInfo->a[0].addrHalt
){
sqlite3VdbeAddOp2(v, OP_IfEmpty, pTabItem->iCursor, pWInfo->iBreak);
VdbeCoverage(v);
}
}else{
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
}
@@ -7389,6 +7412,9 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
sqlite3VdbeAddOp2(v, OP_Goto, 1, pLevel->p2);
}
#endif /* SQLITE_DISABLE_SKIPAHEAD_DISTINCT */
if( pTabList->a[pLevel->iFrom].fg.fromExists ){
sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+2);
}
/* The common case: Advance to the next row */
if( pLevel->addrCont ) sqlite3VdbeResolveLabel(v, pLevel->addrCont);
sqlite3VdbeAddOp3(v, pLevel->op, pLevel->p1, pLevel->p2, pLevel->p3);
+4
View File
@@ -75,6 +75,7 @@ struct WhereLevel {
int iTabCur; /* The VDBE cursor used to access the table */
int iIdxCur; /* The VDBE cursor used to access pIdx */
int addrBrk; /* Jump here to break out of the loop */
int addrHalt; /* Abort the query due to empty table or similar */
int addrNxt; /* Jump here to start the next IN combination */
int addrSkip; /* Jump here for next iteration of skip-scan */
int addrCont; /* Jump here to continue with the next loop cycle */
@@ -280,6 +281,9 @@ struct WhereTerm {
u8 eMatchOp; /* Op for vtab MATCH/LIKE/GLOB/REGEXP terms */
int iParent; /* Disable pWC->a[iParent] when this term disabled */
int leftCursor; /* Cursor number of X in "X <op> <expr>" */
#ifdef SQLITE_DEBUG
int iTerm; /* Which WhereTerm is this, for debug purposes */
#endif
union {
struct {
int leftColumn; /* Column number of X in "X <op> <expr>" */
+47 -53
View File
@@ -110,7 +110,7 @@ static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop){
}
/*
** This function sets the P4 value of an existing OP_Explain opcode to
** This function sets the P4 value of an existing OP_Explain opcode to
** text describing the loop in pLevel. If the OP_Explain opcode already has
** a P4 value, it is freed before it is overwritten.
*/
@@ -126,7 +126,6 @@ void sqlite3WhereAddExplainText(
#endif
{
VdbeOp *pOp = sqlite3VdbeGetOp(pParse->pVdbe, addr);
SrcItem *pItem = &pTabList->a[pLevel->iFrom];
sqlite3 *db = pParse->db; /* Database handle */
int isSearch; /* True for a SEARCH. False for SCAN. */
@@ -149,7 +148,10 @@ void sqlite3WhereAddExplainText(
sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
str.printfFlags = SQLITE_PRINTF_INTERNAL;
sqlite3_str_appendf(&str, "%s %S", isSearch ? "SEARCH" : "SCAN", pItem);
sqlite3_str_appendf(&str, "%s %S%s",
isSearch ? "SEARCH" : "SCAN",
pItem,
pItem->fg.fromExists ? " EXISTS" : "");
if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){
const char *zFmt = 0;
Index *pIdx;
@@ -941,7 +943,7 @@ static int codeAllEqualityTerms(
testcase( pIdx->aiColumn[j]==XN_EXPR );
VdbeComment((v, "%s", explainIndexColumnName(pIdx, j)));
}
}
}
/* Evaluate the equality constraints
*/
@@ -1282,7 +1284,7 @@ static void codeDeferredSeek(
assert( iIdxCur>0 );
assert( pIdx->aiColumn[pIdx->nColumn-1]==-1 );
pWInfo->bDeferredSeek = 1;
sqlite3VdbeAddOp3(v, OP_DeferredSeek, iIdxCur, 0, iCur);
if( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))
@@ -1393,6 +1395,7 @@ static SQLITE_NOINLINE void filterPullDown(
int addrNxt, /* Jump here to bypass inner loops */
Bitmask notReady /* Loops that are not ready */
){
int saved_addrBrk;
while( ++iLevel < pWInfo->nLevel ){
WhereLevel *pLevel = &pWInfo->a[iLevel];
WhereLoop *pLoop = pLevel->pWLoop;
@@ -1401,7 +1404,7 @@ static SQLITE_NOINLINE void filterPullDown(
/* ,--- Because sqlite3ConstructBloomFilter() has will not have set
** vvvvv--' pLevel->regFilter if this were true. */
if( NEVER(pLoop->prereq & notReady) ) continue;
assert( pLevel->addrBrk==0 );
saved_addrBrk = pLevel->addrBrk;
pLevel->addrBrk = addrNxt;
if( pLoop->wsFlags & WHERE_IPK ){
WhereTerm *pTerm = pLoop->aLTerm[0];
@@ -1431,19 +1434,19 @@ static SQLITE_NOINLINE void filterPullDown(
VdbeCoverage(pParse->pVdbe);
}
pLevel->regFilter = 0;
pLevel->addrBrk = 0;
pLevel->addrBrk = saved_addrBrk;
}
}
/*
** Loop pLoop is a WHERE_INDEXED level that uses at least one IN(...)
** operator. Return true if level pLoop is guaranteed to visit only one
** Loop pLoop is a WHERE_INDEXED level that uses at least one IN(...)
** operator. Return true if level pLoop is guaranteed to visit only one
** row for each key generated for the index.
*/
static int whereLoopIsOneRow(WhereLoop *pLoop){
if( pLoop->u.btree.pIndex->onError
&& pLoop->nSkip==0
&& pLoop->u.btree.nEq==pLoop->u.btree.pIndex->nKeyCol
if( pLoop->u.btree.pIndex->onError
&& pLoop->nSkip==0
&& pLoop->u.btree.nEq==pLoop->u.btree.pIndex->nKeyCol
){
int ii;
for(ii=0; ii<pLoop->u.btree.nEq; ii++){
@@ -1478,7 +1481,6 @@ Bitmask sqlite3WhereCodeOneLoopStart(
sqlite3 *db; /* Database connection */
SrcItem *pTabItem; /* FROM clause term being coded */
int addrBrk; /* Jump here to break out of the loop */
int addrHalt; /* addrBrk for the outermost loop */
int addrCont; /* Jump here to continue with next cycle */
int iRowidReg = 0; /* Rowid is stored in this register, if not zero */
int iReleaseReg = 0; /* Temp register to free before returning */
@@ -1522,7 +1524,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
** there are no IN operators in the constraints, the "addrNxt" label
** is the same as "addrBrk".
*/
addrBrk = pLevel->addrBrk = pLevel->addrNxt = sqlite3VdbeMakeLabel(pParse);
addrBrk = pLevel->addrNxt = pLevel->addrBrk;
addrCont = pLevel->addrCont = sqlite3VdbeMakeLabel(pParse);
/* If this is the right table of a LEFT OUTER JOIN, allocate and
@@ -1538,14 +1540,6 @@ Bitmask sqlite3WhereCodeOneLoopStart(
VdbeComment((v, "init LEFT JOIN match flag"));
}
/* Compute a safe address to jump to if we discover that the table for
** this loop is empty and can never contribute content. */
for(j=iLevel; j>0; j--){
if( pWInfo->a[j].iLeftJoin ) break;
if( pWInfo->a[j].pRJ ) break;
}
addrHalt = pWInfo->a[j].addrBrk;
/* Special case of a FROM clause subquery implemented as a co-routine */
if( pTabItem->fg.viaCoroutine ){
int regYield;
@@ -1784,7 +1778,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
VdbeCoverageIf(v, pX->op==TK_GE);
sqlite3ReleaseTempReg(pParse, rTemp);
}else{
sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iCur, addrHalt);
sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iCur, pLevel->addrHalt);
VdbeCoverageIf(v, bRev==0);
VdbeCoverageIf(v, bRev!=0);
}
@@ -1824,37 +1818,37 @@ Bitmask sqlite3WhereCodeOneLoopStart(
sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC | SQLITE_JUMPIFNULL);
}
}else if( pLoop->wsFlags & WHERE_INDEXED ){
/* Case 4: A scan using an index.
/* Case 4: Search using an index.
**
** The WHERE clause may contain zero or more equality
** terms ("==" or "IN" operators) that refer to the N
** left-most columns of the index. It may also contain
** inequality constraints (>, <, >= or <=) on the indexed
** column that immediately follows the N equalities. Only
** the right-most column can be an inequality - the rest must
** use the "==" and "IN" operators. For example, if the
** index is on (x,y,z), then the following clauses are all
** optimized:
** The WHERE clause may contain zero or more equality
** terms ("==" or "IN" or "IS" operators) that refer to the N
** left-most columns of the index. It may also contain
** inequality constraints (>, <, >= or <=) on the indexed
** column that immediately follows the N equalities. Only
** the right-most column can be an inequality - the rest must
** use the "==", "IN", or "IS" operators. For example, if the
** index is on (x,y,z), then the following clauses are all
** optimized:
**
** x=5
** x=5 AND y=10
** x=5 AND y<10
** x=5 AND y>5 AND y<10
** x=5 AND y=5 AND z<=10
** x=5
** x=5 AND y=10
** x=5 AND y<10
** x=5 AND y>5 AND y<10
** x=5 AND y=5 AND z<=10
**
** The z<10 term of the following cannot be used, only
** the x=5 term:
** The z<10 term of the following cannot be used, only
** the x=5 term:
**
** x=5 AND z<10
** x=5 AND z<10
**
** N may be zero if there are inequality constraints.
** If there are no inequality constraints, then N is at
** least one.
** N may be zero if there are inequality constraints.
** If there are no inequality constraints, then N is at
** least one.
**
** This case is also used when there are no WHERE clause
** constraints but an index is selected anyway, in order
** to force the output order to conform to an ORDER BY.
*/
** This case is also used when there are no WHERE clause
** constraints but an index is selected anyway, in order
** to force the output order to conform to an ORDER BY.
*/
static const u8 aStartOp[] = {
0,
0,
@@ -2008,7 +2002,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
}
if( zStartAff ){
updateRangeAffinityStr(pRight, nBtm, &zStartAff[nEq]);
}
}
nConstraint += nBtm;
testcase( pRangeStart->wtFlags & TERM_VIRTUAL );
if( sqlite3ExprIsVector(pRight)==0 ){
@@ -2210,7 +2204,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
** a LEFT JOIN: */
assert( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))==0 );
}
/* Record the instruction used to terminate the loop. */
if( (pLoop->wsFlags & WHERE_ONEROW)
|| (pLevel->u.in.nIn && regBignull==0 && whereLoopIsOneRow(pLoop))
@@ -2579,7 +2573,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
codeCursorHint(pTabItem, pWInfo, pLevel, 0);
pLevel->op = aStep[bRev];
pLevel->p1 = iCur;
pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrHalt);
pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev],iCur,pLevel->addrHalt);
VdbeCoverageIf(v, bRev==0);
VdbeCoverageIf(v, bRev!=0);
pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP;
@@ -2599,7 +2593,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
**
** iLoop==1: Code only expressions that are entirely covered by pIdx.
** iLoop==2: Code remaining expressions that do not contain correlated
** sub-queries.
** sub-queries.
** iLoop==3: Code all remaining expressions.
**
** An effort is made to skip unnecessary iterations of the loop.
@@ -2870,7 +2864,7 @@ SQLITE_NOINLINE void sqlite3WhereRightJoinLoop(
pSubq = pRight->u4.pSubq;
assert( pSubq->pSelect!=0 && pSubq->pSelect->pEList!=0 );
sqlite3VdbeAddOp3(
v, OP_Null, 0, pSubq->regResult,
v, OP_Null, 0, pSubq->regResult,
pSubq->regResult + pSubq->pSelect->pEList->nExpr-1
);
}
+20 -3
View File
@@ -1124,6 +1124,9 @@ static void exprAnalyze(
}
assert( pWC->nTerm > idxTerm );
pTerm = &pWC->a[idxTerm];
#ifdef SQLITE_DEBUG
pTerm->iTerm = idxTerm;
#endif
pMaskSet = &pWInfo->sMaskSet;
pExpr = pTerm->pExpr;
assert( pExpr!=0 ); /* Because malloc() has not failed */
@@ -1649,7 +1652,7 @@ static void whereAddLimitExpr(
**
** 1. The SELECT statement has a LIMIT clause, and
** 2. The SELECT statement is not an aggregate or DISTINCT query, and
** 3. The SELECT statement has exactly one object in its from clause, and
** 3. The SELECT statement has exactly one object in its FROM clause, and
** that object is a virtual table, and
** 4. There are no terms in the WHERE clause that will not be passed
** to the virtual table xBestIndex method.
@@ -1686,8 +1689,22 @@ void SQLITE_NOINLINE sqlite3WhereAddLimit(WhereClause *pWC, Select *p){
** (leftCursor==iCsr) test below. */
continue;
}
if( pWC->a[ii].leftCursor!=iCsr ) return;
if( pWC->a[ii].prereqRight!=0 ) return;
if( pWC->a[ii].leftCursor==iCsr && pWC->a[ii].prereqRight==0 ) continue;
/* If this term has a parent with exactly one child, and the parent will
** be passed through to xBestIndex, then this term can be ignored. */
if( pWC->a[ii].iParent>=0 ){
WhereTerm *pParent = &pWC->a[ pWC->a[ii].iParent ];
if( pParent->leftCursor==iCsr
&& pParent->prereqRight==0
&& pParent->nChild==1
){
continue;
}
}
/* This term will not be passed through. Do not add a LIMIT clause. */
return;
}
/* Check condition (5). Return early if it is not met. */
+74
View File
@@ -349,4 +349,78 @@ do_execsql_test 5.9 {
three six seven
}
#--------------------------------------------------------------------------
reset_db
register_tcl_module db
proc quote {str} {
return "'[string map {' ''} $str]'"
}
proc vtab_command {lVal method args} {
switch -- $method {
xConnect {
return "CREATE TABLE t1(a, b, c, d)"
}
xBestIndex {
set hdl [lindex $args 0]
set clist [$hdl constraints]
set idx 0
set idxnum 0
foreach c $clist {
array set a $c
if {$a(usable)==0} continue
if {$a(op)=="limit"} {
set idxnum [$hdl rhs_value $idx 555]
}
incr idx
}
return "cost 1000 rows 1000 idxnum $idxnum"
}
xFilter {
foreach {idxnum idxstr lArg} $args {}
return [list sql "SELECT 0, $idxnum, $idxnum, $idxnum, $idxnum"]
}
}
return {}
}
do_execsql_test 6.0 {
CREATE TABLE t1(x, y);
INSERT INTO t1 VALUES(2, 2);
CREATE VIRTUAL TABLE x1 USING tcl(vtab_command t1);
}
do_execsql_test 6.1 { SELECT * FROM x1 LIMIT 50 } {50 50 50 50}
do_execsql_test 6.2 { SELECT * FROM x1 WHERE b=c LIMIT 5 } {0 0 0 0}
do_execsql_test 6.3 {
SELECT (SELECT a FROM x1 WHERE t1.x=t1.y LIMIT 10) FROM t1
} {0}
do_execsql_test 6.4 {
SELECT (SELECT a FROM x1 WHERE x1.a=1) FROM t1
} {1}
do_execsql_test 6.5 {
SELECT (SELECT a FROM x1 WHERE x1.a=1 LIMIT 1) FROM t1
} {1}
do_execsql_test 6.6 {
SELECT (SELECT a FROM x1 WHERE x1.a=555 LIMIT 2) FROM t1
} {555}
finish_test
+20
View File
@@ -0,0 +1,20 @@
/*
** Run this script using "sqlite3" to confirm that the command-line
** shell properly handles the output of double-width characters.
**
** https://sqlite.org/forum/forumpost/008ac80276
*/
.mode box
CREATE TABLE data(word TEXT, description TEXT);
INSERT INTO data VALUES('〈οὐκέτι〉','Greek without dblwidth <...>');
.print .mode box
SELECT * FROM data;
.mode table
.print .mode table
SELECT * FROM data;
.mode qbox
.print .mode qbox
SELECT * FROM data;
.mode column
.print .mode column
SELECT * FROM data;
+1 -2
View File
@@ -338,8 +338,7 @@ det 3.3.3 {
} {
QUERY PLAN
|--SCAN t1
`--CORRELATED SCALAR SUBQUERY xxxxxx
`--SCAN t2
`--SCAN t2 EXISTS
}
#-------------------------------------------------------------------------
+426
View File
@@ -0,0 +1,426 @@
# 2024 May 25
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
set testprefix existsexpr
do_execsql_test 1.0 {
CREATE TABLE x1(a, b, PRIMARY KEY(a)) WITHOUT ROWID;
INSERT INTO x1 VALUES(1, 2), (3, 4), (5, 6);
CREATE INDEX x1b ON x1(b);
CREATE TABLE x2(x, y);
INSERT INTO x2 VALUES(1, 2), (3, 4), (5, 6);
}
do_execsql_test 1.1 {
SELECT 1 WHERE EXISTS (SELECT 1 FROM x1 WHERE a=5)
} {1}
do_execsql_test 1.2 {
SELECT * FROM x2 WHERE EXISTS (SELECT 1 FROM x1 WHERE a=x)
} {1 2 3 4 5 6}
# With "a=x", the UNIQUE index means the EXIST can be transformed to a join.
# So no "SUBQUERY". With "b=x", the index is not UNIQUE and so there is a
# "SUBQUERY".
do_execsql_test 1.3.1 {
EXPLAIN QUERY PLAN
SELECT * FROM x2 WHERE EXISTS (SELECT 1 FROM x1 WHERE a=x)
} {~/SUBQUERY/}
do_execsql_test 1.3.2 {
EXPLAIN QUERY PLAN
SELECT * FROM x2 WHERE EXISTS (SELECT 1 FROM x1 WHERE b=x)
} {~/SUBQUERY/}
do_execsql_test 1.4.1 {
EXPLAIN QUERY PLAN
SELECT * FROM x2 WHERE x=1 AND EXISTS (SELECT 1 FROM x1 WHERE a=x)
} {~/SUBQUERY/}
do_execsql_test 1.4.2 {
EXPLAIN QUERY PLAN
SELECT * FROM x2 WHERE EXISTS (SELECT 1 FROM x1 WHERE a=x) AND y=2
} {~/SUBQUERY/}
do_execsql_test 1.5 {
SELECT count(*) FROM x2 WHERE EXISTS (SELECT 1 FROM x1 WHERE a=x)
} {3}
#-------------------------------------------------------------------------
do_execsql_test 2.0 {
CREATE TABLE t1(a, b);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
) INSERT INTO t1 SELECT i, i FROM s;
CREATE TABLE t2(c, d);
WITH s(i) AS (
SELECT 10 UNION ALL SELECT i+10 FROM s WHERE i<1000
) INSERT INTO t2 SELECT i, i FROM s;
}
do_execsql_test 2.1 {
SELECT count(*) FROM t1;
SELECT count(*) FROM t2;
} {1000 100}
do_execsql_test 2.2 {
SELECT count(*) FROM t1, t2 WHERE a=c;
} {100}
do_execsql_test 2.3 {
SELECT count(*) FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c=a)
} {100}
do_eqp_test 2.4 {
SELECT count(*) FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c=a)
} {SCAN t1}
do_execsql_test 2.4.0 {
CREATE UNIQUE INDEX t2c ON t2(c);
CREATE UNIQUE INDEX t1a ON t1(a);
}
do_eqp_test 2.4.1 {
SELECT count(*) FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c=a);
} {SCAN t1*t2 EXISTS}
do_execsql_test 2.4.2 {
ANALYZE;
}
do_eqp_test 2.4.3 {
SELECT count(*) FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c=a);
} {SCAN t1*t2 EXISTS}
do_execsql_test 2.4.4 {
SELECT count(*) FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c=a);
} {100}
do_execsql_test 2.5.1 {
EXPLAIN QUERY PLAN
SELECT count(*) FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.rowid=a);
} {~/SUBQUERY/}
#-------------------------------------------------------------------------
proc do_subquery_test {tn bSub sql res} {
set r1(0) ~/SUBQUERY/
set r1(1) /SUBQUERY/
do_execsql_test $tn.1 "explain query plan $sql" $r1($bSub)
do_execsql_test $tn.2 $sql $res
}
do_execsql_test 3.0 {
CREATE TABLE y1(a, b, c);
CREATE TABLE y2(x, y, z);
CREATE UNIQUE INDEX y2zy ON y2(z, y);
INSERT INTO y1 VALUES(1, 1, 1);
INSERT INTO y1 VALUES(2, 2, 2);
INSERT INTO y1 VALUES(3, 3, 3);
INSERT INTO y1 VALUES(4, 4, 4);
INSERT INTO y2 VALUES(1, 1, 1);
INSERT INTO y2 VALUES(3, 3, 3);
}
do_subquery_test 3.1 0 {
SELECT * FROM y1 WHERE EXISTS (
SELECT 1 FROM y2 WHERE z=a AND y=b AND x=z
)
} {
1 1 1 3 3 3
}
do_subquery_test 3.2 0 {
SELECT * FROM y1 WHERE EXISTS (
SELECT 1 FROM y2 WHERE z=max(a,b) AND y=min(b,a) AND x=z
)
} {
1 1 1 3 3 3
}
do_subquery_test 3.3 0 {
SELECT * FROM y1 WHERE EXISTS (
SELECT 1 FROM y2 WHERE z=max(a,b) AND y=min(b,a) AND c!=3
)
} {
1 1 1
}
do_subquery_test 3.4 0 {
SELECT * FROM y1 WHERE EXISTS (
SELECT 1 FROM y2 WHERE z=max(a,b) AND b=3
)
} {
3 3 3
}
do_subquery_test 3.5 0 {
SELECT * FROM y1 WHERE EXISTS (
SELECT 1 FROM y2 WHERE z=a-1 AND y=a-1
)
} {
2 2 2
4 4 4
}
do_subquery_test 3.6 0 {
SELECT * FROM y1 WHERE EXISTS (
SELECT 1 FROM y2 WHERE z=a-1 AND y+1=a
)
} {
2 2 2
4 4 4
}
do_subquery_test 3.7 1 {
SELECT * FROM y1 WHERE EXISTS (
SELECT count(*) FROM y2 WHERE z=a-1 AND y=a-1
)
} {
1 1 1
2 2 2
3 3 3
4 4 4
}
do_subquery_test 3.8 0 {
SELECT * FROM y1 WHERE EXISTS ( SELECT a+1 FROM y2 )
} {
1 1 1
2 2 2
3 3 3
4 4 4
}
do_subquery_test 3.9 1 {
SELECT * FROM y1 WHERE EXISTS (
SELECT 1 FROM y2 one, y2 two WHERE one.z=a-1 AND one.y=a-1
)
} {
2 2 2
4 4 4
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 4.0 {
CREATE TABLE tx1(a TEXT COLLATE nocase, b TEXT);
CREATE UNIQUE INDEX tx1ab ON tx1(a, b);
INSERT INTO tx1 VALUES('a', 'a');
INSERT INTO tx1 VALUES('B', 'b');
INSERT INTO tx1 VALUES('c', 'c');
INSERT INTO tx1 VALUES('D', 'd');
INSERT INTO tx1 VALUES('e', 'e');
CREATE TABLE tx2(x, y);
INSERT INTO tx2 VALUES('A', 'a');
INSERT INTO tx2 VALUES('b', 'b');
INSERT INTO tx2 VALUES('C', 'c');
INSERT INTO tx2 VALUES('D', 'd');
}
do_subquery_test 4.1 0 {
SELECT * FROM tx2 WHERE EXISTS (
SELECT 1 FROM tx1 WHERE a=x AND b=y
)
} {
A a
b b
C c
D d
}
do_subquery_test 4.1.1 0 {
SELECT * FROM tx2 WHERE EXISTS (
SELECT 1 FROM tx1 WHERE (a COLLATE nocase)=x AND b=y
)
} {
A a b b C c D d
}
do_subquery_test 4.1.2 0 {
SELECT * FROM tx2 WHERE EXISTS (
SELECT 1 FROM tx1 WHERE a=x AND (b COLLATE binary)=y
)
} {
A a b b C c D d
}
do_subquery_test 4.1.1 0 {
SELECT * FROM tx2 WHERE EXISTS (
SELECT 1 FROM tx1 WHERE x=(a COLLATE nocase) AND b=y
)
} {
A a b b C c D d
}
do_subquery_test 4.1.2 0 {
SELECT * FROM tx2 WHERE EXISTS (
SELECT 1 FROM tx1 WHERE a=x AND y=(b COLLATE binary)
)
} {
A a b b C c D d
}
do_subquery_test 4.2 0 {
SELECT * FROM tx2 WHERE EXISTS (
SELECT 1 FROM tx1 WHERE a=x AND b=y COLLATE nocase
)
} {
A a
b b
C c
D d
}
do_execsql_test 4.3 {
DROP INDEX tx1ab;
CREATE UNIQUE INDEX tx1ab ON tx1(a COLLATE binary, b);
}
do_subquery_test 4.4 0 {
SELECT * FROM tx2 WHERE EXISTS (
SELECT 1 FROM tx1 WHERE a=x AND b=y
)
} {
A a
b b
C c
D d
}
do_subquery_test 4.4 0 {
SELECT * FROM tx2 WHERE EXISTS (
SELECT 1 FROM tx1 WHERE a=x COLLATE binary AND b=y
)
} {
D d
}
do_subquery_test 4.4 1 {
SELECT EXISTS ( SELECT x FROM tx1 ) FROM tx2
} {
1 1 1 1
}
do_subquery_test 4.4 1 {
SELECT (SELECT EXISTS ( SELECT x FROM tx1 ) WHERE 1) FROM tx2
} {
1 1 1 1
}
#-------------------------------------------------------------------------
proc cols {s f} {
set lCols [list]
for {set i $s} {$i<=$f} {incr i} {
lappend lCols [format "c%02d" $i]
}
join $lCols ", "
}
proc vals {n val} {
set lVal [list]
for {set i 0} {$i<$n} {incr i} {
lappend lVal $val
}
join $lVal ", "
}
proc exprs {s f} {
set lExpr [list]
for {set i $s} {$i<=$f} {incr i} {
lappend lExpr [format "c%02d = o" $i]
}
join $lExpr " AND "
}
do_execsql_test 5.0 "
CREATE TABLE a1( [cols 0 99] );
"
do_execsql_test 5.1 "
-- 63 column index
CREATE UNIQUE INDEX a1idx1 ON a1( [cols 0 62] );
"
do_execsql_test 5.2 "
-- 64 column index
CREATE UNIQUE INDEX a1idx2 ON a1( [cols 10 73] );
"
do_execsql_test 5.2 "
-- 65 column index
CREATE UNIQUE INDEX a1idx3 ON a1( [cols 20 84] );
"
do_test 5.3 {
foreach v {1 2 3 4 5 6} {
execsql "INSERT INTO a1 VALUES( [vals 100 $v] )"
}
} {}
do_execsql_test 5.4 {
CREATE TABLE a2(o);
INSERT INTO a2 VALUES(2), (5);
}
do_subquery_test 5.5 0 "
SELECT o FROM a2 WHERE EXISTS (
SELECT 1 FROM a1 WHERE [exprs 0 62]
)
" {
2 5
}
do_subquery_test 5.6 0 "
SELECT o FROM a2 WHERE EXISTS (
SELECT 1 FROM a1 WHERE [exprs 10 73]
)
" {
2 5
}
do_subquery_test 5.7 0 "
SELECT o FROM a2 WHERE EXISTS (
SELECT 1 FROM a1 WHERE [exprs 20 84]
)
" {
2 5
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 6.0 {
CREATE TABLE t1(a, b UNIQUE, c UNIQUE);
CREATE TABLE t2(a INfEGER PRIMARY KEY, b);
CREATE UNIQUE INDEX t2b ON t2(b);
}
do_catchsql_test 6.1 {
SELECT a FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c COLLATE f = a)
} {1 {no such collation sequence: f}}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 7.0 {
CREATE TABLE t1(x);
CREATE TABLE t2(y UNIQUE);
INSERT INTO t1 VALUES(1), (2);
INSERT INTO t2 VALUES(1), (3);
SELECT * FROM t1 one LEFT JOIN t1 two ON (one.x=two.x AND EXISTS (
SELECT 1 FROM t2 WHERE y=one.x
));
} {
1 1
2 {}
}
finish_test
+96
View File
@@ -0,0 +1,96 @@
# 2024 June 14
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
set testprefix existsexpr2
do_execsql_test 1.0 {
CREATE TABLE x1(a, b, PRIMARY KEY(a)) WITHOUT ROWID;
INSERT INTO x1 VALUES(1, 2), (3, 4), (5, 6);
CREATE INDEX x1b ON x1(b);
CREATE TABLE x2(x, y);
INSERT INTO x2 VALUES(1, 2), (3, 4), (5, 6);
}
do_execsql_test 1.1 {
SELECT * FROM x1 WHERE EXISTS (SELECT 1 FROM x2 WHERE a!=123)
} {1 2 3 4 5 6}
do_execsql_test 1.2 {
CREATE TABLE x3(u, v);
CREATE INDEX x3u ON x3(u);
INSERT INTO x3 VALUES
(1, 1), (1, 2), (1, 3),
(2, 1), (2, 2), (2, 3);
}
do_execsql_test 1.3 {
SELECT * FROM x1 WHERE EXISTS (
SELECT 1 FROM x3 WHERE u IN (1, 2, 3, 4) AND v=b
);
} {
1 2
}
#-------------------------------------------------------------------------
#
reset_db
do_execsql_test 2.0 {
CREATE TABLE t1(a, b, c);
CREATE INDEX t1ab ON t1(a,b);
INSERT INTO t1 VALUES
('abc', 1, 1),
('abc', 2, 2),
('abc', 2, 3),
('def', 1, 1),
('def', 2, 2),
('def', 2, 3);
CREATE TABLE t2(x, y);
INSERT INTO t2 VALUES(1, 1), (2, 2), (3, 3);
ANALYZE;
DELETE FROM sqlite_stat1;
INSERT INTO sqlite_stat1 VALUES('t1','t1ab','10000 5000 2');
ANALYZE sqlite_master;
}
do_execsql_test 2.1 {
SELECT a,b,c FROM t1 WHERE b=2 ORDER BY a
} {
abc 2 2
abc 2 3
def 2 2
def 2 3
}
do_execsql_test 2.2 {
SELECT x, y FROM t2 WHERE EXISTS (
SELECT 1 FROM t1 WHERE b=x
)
} {
1 1
2 2
}
finish_test
+49
View File
@@ -0,0 +1,49 @@
# 2024 May 25
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/malloc_common.tcl
set testprefix existsfault
db close
sqlite3_shutdown
sqlite3_config_lookaside 0 0
sqlite3_initialize
autoinstall_test_functions
sqlite3 db test.db
do_execsql_test 1.0 {
CREATE TABLE x1(a, b);
INSERT INTO x1 VALUES(1, 2), (3, 4), (5, 6);
CREATE UNIQUE INDEX x1a ON x1(a);
CREATE INDEX x1b ON x1(b);
CREATE TABLE x2(x, y);
INSERT INTO x2 VALUES(1, 2), (3, 4), (5, 6);
}
do_faultsim_test 1 -faults oom* -prep {
sqlite3 db test.db
execsql { SELECT * FROM sqlite_schema }
} -body {
execsql {
SELECT count(*) FROM x2 WHERE EXISTS (SELECT 1 FROM x1 WHERE a=x) AND y!=11
}
} -test {
faultsim_test_result {0 3}
}
finish_test
+10
View File
@@ -488,11 +488,21 @@ proc preupdate_hook {args} {
set type [lindex $args 0]
eval lappend ::preupdate $args
if {$type != "INSERT"} {
set x [catch {db preupdate old [db preupdate count]}]
if {!$x} {
lappend "ERROR: sqlite3_preupdate_old() accepted an out-of-bounds\
column index"
}
for {set i 0} {$i < [db preupdate count]} {incr i} {
lappend ::preupdate [db preupdate old $i]
}
}
if {$type != "DELETE"} {
set x [catch {db preupdate new [db preupdate count]}]
if {!$x} {
lappend "ERROR: sqlite3_preupdate_old() accepted an out-of-bounds\
column index"
}
for {set i 0} {$i < [db preupdate count]} {incr i} {
set rc [catch { db preupdate new $i } v]
lappend ::preupdate $v
+91
View File
@@ -106,4 +106,95 @@ do_test 4.4 {
} {SQLITE_LOCKED}
close $blob
#-------------------------------------------------------------------------
reset_db
do_execsql_test 5.1 {
CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
INSERT INTO t2 VALUES(1000, 'abcdefghijklmnopqrstuvwxyz');
INSERT INTO t2 VALUES(2000, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
INSERT INTO t2 VALUES(3000, 'abcdefghijklmnopqrstuvwxyz');
}
do_test 5.2.1 {
execsql BEGIN
set blob [db incrblob t2 b 2000]
seek $blob 0
puts -nonewline $blob "hello "
flush $blob
execsql ROLLBACK
} {}
do_test 5.2.2 {
puts -nonewline $blob "world"
list [catch { flush $blob } msg] $msg
} "1 {error flushing \"$blob\": I/O error}"
catch { close $blob }
set preupdate_count 0
proc preupdate {args} { incr ::preupdate_count ; return {} }
db preupdate hook preupdate
set preupdate_count 0
do_test 5.3.1 {
execsql BEGIN
set blob [db incrblob t2 b 1000]
seek $blob 0
puts -nonewline $blob "hello "
flush $blob
execsql ROLLBACK
} {}
do_test 5.3.2 {
puts -nonewline $blob "world"
list [catch { flush $blob } msg] $msg
} "1 {error flushing \"$blob\": I/O error}"
catch { close $blob }
do_test 5.3.3 {
set ::preupdate_count
} {1}
set preupdate_count 0
do_test 5.4.1 {
execsql BEGIN
set blob [db incrblob t2 b 1000]
seek $blob 0
puts -nonewline $blob "hello "
flush $blob
execsql { DELETE FROM t2 WHERE a=3000; }
} {}
do_test 5.4.2 {
puts -nonewline $blob "world"
list [catch { flush $blob } msg] $msg
} "0 {}"
catch { close $blob }
catchsql { ROLLBACK }
do_test 5.3.3 {
set ::preupdate_count
} {3}
set preupdate_count 0
do_test 5.4.3 {
execsql BEGIN
set blob [db incrblob t2 b 2000]
seek $blob 0
puts -nonewline $blob "hello "
flush $blob
execsql { UPDATE t2 SET b='abcdefghijklmnopqrstuvwxyz' WHERE a=2000 }
} {}
do_test 5.4.4 {
puts -nonewline $blob "world"
list [catch { flush $blob } msg] $msg
} "1 {error flushing \"$blob\": I/O error}"
catch { close $blob }
catchsql { ROLLBACK }
do_test 5.3.3 {
set ::preupdate_count
} {2}
finish_test
+4 -4
View File
@@ -892,15 +892,15 @@ do_execsql_test json101-13.100 {
INSERT INTO t2(id,json) VALUES(4,'{"value":4}');
INSERT INTO t2(id,json) VALUES(5,'{"value":5}');
INSERT INTO t2(id,json) VALUES(6,'{"value":6}');
SELECT * FROM t1 CROSS JOIN t2
SELECT *, 'NL' FROM t1 CROSS JOIN t2
WHERE EXISTS(SELECT 1 FROM json_each(t1.json,'$.items') AS Z
WHERE Z.value==t2.id);
} {1 {{"items":[3,5]}} 3 {{"value":3}} 1 {{"items":[3,5]}} 5 {{"value":5}}}
} {1 {{"items":[3,5]}} 3 {{"value":3}} NL 1 {{"items":[3,5]}} 5 {{"value":5}} NL}
do_execsql_test json101-13.110 {
SELECT * FROM t2 CROSS JOIN t1
SELECT *, 'NL' FROM t2 CROSS JOIN t1
WHERE EXISTS(SELECT 1 FROM json_each(t1.json,'$.items') AS Z
WHERE Z.value==t2.id);
} {3 {{"value":3}} 1 {{"items":[3,5]}} 5 {{"value":5}} 1 {{"items":[3,5]}}}
} {3 {{"value":3}} 1 {{"items":[3,5]}} NL 5 {{"value":5}} 1 {{"items":[3,5]}} NL}
# 2018-05-16
# Incorrect fullkey output from json_each()
+1 -1
View File
@@ -66,7 +66,7 @@ do_vmstep_test 1.5.2 {
SELECT count(*) FROM t2 WHERE EXISTS(
SELECT 1 FROM t1 WHERE t1.a=450 AND t2.c IS NULL
)
} +8000 {0}
} 4000 {0}
#-------------------------------------------------------------------------
reset_db
+97
View File
@@ -162,4 +162,101 @@ do_execsql_test strict1-8.2 {
SELECT *, '|' FROM t1;
} {/5.0 5.0 4.6116\d*e\+18 4.6116\d+e\+18 |/}
# 2025-06-18 https://sqlite.org/forum/forumpost/6caf195248a849e4
#
# Enforce STRICT table type constraints on STORED generated columns
#
do_execsql_test strict1-9.1 {
CREATE TABLE strict (
k INTEGER PRIMARY KEY,
c1 REAL AS(if(k=11,1.5, k=12,2, k=13,'x', k=14,x'34', 0.0)) STORED,
c2 INT AS(if(k=21,1.5, k=22,2, k=23,'x', k=24,x'34', 0)) STORED,
c3 TEXT AS(if(k=31,1.5, k=32,2, k=33,'x', k=34,x'34', 'x')) STORED,
c4 BLOB AS(if(k=41,1.5, k=42,2, k=43,'x', k=44,x'34', x'00')) STORED,
c5 ANY AS(if(k=51,1.5, k=52,2, k=53,'x', k=54,x'34', 0)) STORED
) STRICT;
INSERT INTO strict(k) VALUES(11);
INSERT INTO strict(k) VALUES(12);
INSERT INTO strict(k) VALUES(22);
INSERT INTO strict(k) VALUES(31);
INSERT INTO strict(k) VALUES(32);
INSERT INTO strict(k) VALUES(33);
INSERT INTO strict(k) VALUES(44);
PRAGMA integrity_check;
} {ok}
do_catchsql_test strict1-9.2.13 {
INSERT INTO strict(k) VALUES(13);
} {1 {cannot store TEXT value in REAL column strict.c1}}
do_catchsql_test strict1-9.2.14 {
INSERT INTO strict(k) VALUES(14);
} {1 {cannot store BLOB value in REAL column strict.c1}}
do_catchsql_test strict1-9.2.21 {
INSERT INTO strict(k) VALUES(21);
} {1 {cannot store REAL value in INT column strict.c2}}
do_catchsql_test strict1-9.2.23 {
INSERT INTO strict(k) VALUES(23);
} {1 {cannot store TEXT value in INT column strict.c2}}
do_catchsql_test strict1-9.2.24 {
INSERT INTO strict(k) VALUES(24);
} {1 {cannot store BLOB value in INT column strict.c2}}
do_catchsql_test strict1-9.2.34 {
INSERT INTO strict(k) VALUES(34);
} {1 {cannot store BLOB value in TEXT column strict.c3}}
do_catchsql_test strict1-9.2.41 {
INSERT INTO strict(k) VALUES(41);
} {1 {cannot store REAL value in BLOB column strict.c4}}
do_catchsql_test strict1-9.2.42 {
INSERT INTO strict(k) VALUES(42);
} {1 {cannot store INT value in BLOB column strict.c4}}
do_catchsql_test strict1-9.2.43 {
INSERT INTO strict(k) VALUES(43);
} {1 {cannot store TEXT value in BLOB column strict.c4}}
do_execsql_test strict1-9.3 {
DROP TABLE strict;
CREATE TABLE strict (
k INTEGER PRIMARY KEY,
c1 REAL AS(if(k=11,1.5, k=12,2, k=13,'x', k=14,x'34', 0.0)) VIRTUAL,
c2 INT AS(if(k=21,1.5, k=22,2, k=23,'x', k=24,x'34', 0)) VIRTUAL,
c3 TEXT AS(if(k=31,1.5, k=32,2, k=33,'x', k=34,x'34', 'x')) VIRTUAL,
c4 BLOB AS(if(k=41,1.5, k=42,2, k=43,'x', k=44,x'34', x'00')) VIRTUAL,
c5 ANY AS(if(k=51,1.5, k=52,2, k=53,'x', k=54,x'34', 0)) VIRTUAL
) STRICT;
INSERT INTO strict(k) VALUES(11);
INSERT INTO strict(k) VALUES(12);
INSERT INTO strict(k) VALUES(22);
INSERT INTO strict(k) VALUES(31);
INSERT INTO strict(k) VALUES(32);
INSERT INTO strict(k) VALUES(33);
INSERT INTO strict(k) VALUES(44);
PRAGMA integrity_check;
} {ok}
do_catchsql_test strict1-9.4.13 {
INSERT INTO strict(k) VALUES(13);
} {1 {cannot store TEXT value in REAL column strict.c1}}
do_catchsql_test strict1-9.4.14 {
INSERT INTO strict(k) VALUES(14);
} {1 {cannot store BLOB value in REAL column strict.c1}}
do_catchsql_test strict1-9.4.21 {
INSERT INTO strict(k) VALUES(21);
} {1 {cannot store REAL value in INT column strict.c2}}
do_catchsql_test strict1-9.4.23 {
INSERT INTO strict(k) VALUES(23);
} {1 {cannot store TEXT value in INT column strict.c2}}
do_catchsql_test strict1-9.4.24 {
INSERT INTO strict(k) VALUES(24);
} {1 {cannot store BLOB value in INT column strict.c2}}
do_catchsql_test strict1-9.4.34 {
INSERT INTO strict(k) VALUES(34);
} {1 {cannot store BLOB value in TEXT column strict.c3}}
do_catchsql_test strict1-9.4.41 {
INSERT INTO strict(k) VALUES(41);
} {1 {cannot store REAL value in BLOB column strict.c4}}
do_catchsql_test strict1-9.4.42 {
INSERT INTO strict(k) VALUES(42);
} {1 {cannot store INT value in BLOB column strict.c4}}
do_catchsql_test strict1-9.4.43 {
INSERT INTO strict(k) VALUES(43);
} {1 {cannot store TEXT value in BLOB column strict.c4}}
finish_test
+108 -45
View File
@@ -9,7 +9,7 @@
#
#***********************************************************************
# This file implements regression tests for TCL interface to the
# SQLite library.
# SQLite library.
#
# Actually, all tests are based on the TCL interface, so the main
# interface is pretty well tested. This file contains some addition
@@ -121,7 +121,7 @@ ifcapable {complete} {
do_test tcl-1.14 {
set v [catch {db eval} msg]
lappend v $msg
} {1 {wrong # args: should be "db eval ?OPTIONS? SQL ?ARRAY-NAME? ?SCRIPT?"}}
} {1 {wrong # args: should be "db eval ?OPTIONS? SQL ?VAR-NAME? ?SCRIPT?"}}
do_test tcl-1.15 {
set v [catch {db function} msg]
lappend v $msg
@@ -359,6 +359,19 @@ do_test tcl-9.3 {
execsql {SELECT typeof(ret_int())}
} {integer}
proc breakAsNullUdf args {
if {"1" eq [lindex $args 0]} {return -code break}
}
do_test tcl-9.4 {
db function banu breakAsNullUdf
execsql {SELECT typeof(banu()), typeof(banu(1))}
} {text null}
do_test tcl-9.5 {
db nullvalue banunull
db eval {SELECT banu(), banu(1)}
} {{} banunull}
# Recursive calls to the same user-defined function
#
ifcapable tclvar {
@@ -465,7 +478,7 @@ do_test tcl-10.13 {
db eval {SELECT * FROM t4}
} {1 2 5 6 7}
# Now test that [db transaction] commands may be nested with
# Now test that [db transaction] commands may be nested with
# the expected results.
#
do_test tcl-10.14 {
@@ -475,7 +488,7 @@ do_test tcl-10.14 {
INSERT INTO t4 VALUES('one');
}
catch {
catch {
db transaction {
db eval { INSERT INTO t4 VALUES('two') }
db transaction {
@@ -674,11 +687,11 @@ do_test tcl-15.5 {
} {0}
# 2017-06-26: The --withoutnulls flag to "db eval".
# 2017-06-26: The -withoutnulls flag to "db eval".
#
# In the "db eval --withoutnulls SQL ARRAY" form, NULL results cause the
# corresponding array entry to be unset. The default behavior (without
# the -withoutnulls flags) is for the corresponding array value to get
# In the "db eval -withoutnulls SQL TARGET" form, NULL results cause the
# corresponding target entry to be unset. The default behavior (without
# the -withoutnulls flags) is for the corresponding target value to get
# the [db nullvalue] string.
#
catch {db close}
@@ -720,64 +733,64 @@ reset_db
proc add {a b} { return [expr $a + $b] }
proc ret {a} { return $a }
db function add_i -returntype integer add
db function add_i -returntype integer add
db function add_r -ret real add
db function add_t -return text add
db function add_b -returntype blob add
db function add_a -returntype any add
db function add_t -return text add
db function add_b -returntype blob add
db function add_a -returntype any add
db function ret_i -returntype int ret
db function ret_i -returntype int ret
db function ret_r -returntype real ret
db function ret_t -returntype text ret
db function ret_b -returntype blob ret
db function ret_a -r any ret
db function ret_t -returntype text ret
db function ret_b -returntype blob ret
db function ret_a -r any ret
do_execsql_test 17.0 {
SELECT quote( add_i(2, 3) );
SELECT quote( add_r(2, 3) );
SELECT quote( add_t(2, 3) );
SELECT quote( add_b(2, 3) );
SELECT quote( add_a(2, 3) );
SELECT quote( add_r(2, 3) );
SELECT quote( add_t(2, 3) );
SELECT quote( add_b(2, 3) );
SELECT quote( add_a(2, 3) );
} {5 5.0 '5' X'35' 5}
do_execsql_test 17.1 {
SELECT quote( add_i(2.2, 3.3) );
SELECT quote( add_r(2.2, 3.3) );
SELECT quote( add_t(2.2, 3.3) );
SELECT quote( add_b(2.2, 3.3) );
SELECT quote( add_a(2.2, 3.3) );
SELECT quote( add_r(2.2, 3.3) );
SELECT quote( add_t(2.2, 3.3) );
SELECT quote( add_b(2.2, 3.3) );
SELECT quote( add_a(2.2, 3.3) );
} {5.5 5.5 '5.5' X'352E35' 5.5}
do_execsql_test 17.2 {
SELECT quote( ret_i(2.5) );
SELECT quote( ret_r(2.5) );
SELECT quote( ret_t(2.5) );
SELECT quote( ret_b(2.5) );
SELECT quote( ret_a(2.5) );
SELECT quote( ret_r(2.5) );
SELECT quote( ret_t(2.5) );
SELECT quote( ret_b(2.5) );
SELECT quote( ret_a(2.5) );
} {2.5 2.5 '2.5' X'322E35' 2.5}
do_execsql_test 17.3 {
SELECT quote( ret_i('2.5') );
SELECT quote( ret_r('2.5') );
SELECT quote( ret_t('2.5') );
SELECT quote( ret_b('2.5') );
SELECT quote( ret_a('2.5') );
SELECT quote( ret_r('2.5') );
SELECT quote( ret_t('2.5') );
SELECT quote( ret_b('2.5') );
SELECT quote( ret_a('2.5') );
} {2.5 2.5 '2.5' X'322E35' '2.5'}
do_execsql_test 17.4 {
SELECT quote( ret_i('abc') );
SELECT quote( ret_r('abc') );
SELECT quote( ret_t('abc') );
SELECT quote( ret_b('abc') );
SELECT quote( ret_a('abc') );
SELECT quote( ret_r('abc') );
SELECT quote( ret_t('abc') );
SELECT quote( ret_b('abc') );
SELECT quote( ret_a('abc') );
} {'abc' 'abc' 'abc' X'616263' 'abc'}
do_execsql_test 17.5 {
SELECT quote( ret_i(X'616263') );
SELECT quote( ret_r(X'616263') );
SELECT quote( ret_t(X'616263') );
SELECT quote( ret_b(X'616263') );
SELECT quote( ret_a(X'616263') );
SELECT quote( ret_r(X'616263') );
SELECT quote( ret_t(X'616263') );
SELECT quote( ret_b(X'616263') );
SELECT quote( ret_a(X'616263') );
} {'abc' 'abc' 'abc' X'616263' X'616263'}
do_test 17.6.1 {
@@ -848,21 +861,70 @@ do_catchsql_test 19.911 {
} {1 {invalid command name "bind_fallback_does_not_exist"}}
db bind_fallback {}
#-------------------------------------------------------------------------
# 2025-05-05: the -asdict eval flag
#
do_test 20.0 {
execsql {CREATE TABLE tad(a,b)}
execsql {INSERT INTO tad(a,b) VALUES('aa','bb'),('AA','BB')}
db eval -asdict {
SELECT a, b FROM tad WHERE 0
} D {}
set D
} {* {a b}}
do_test 20.1 {
unset D
set i 0
set res {}
set colNames {}
db eval -asdict {
SELECT a, b FROM tad ORDER BY a
} D {
dict set D i [incr i]
lappend res $i [dict get $D a] [dict get $D b]
if {1 == $i} {
set colNames [dict get $D *]
}
}
lappend res $colNames
unset D
set res
} {1 AA BB 2 aa bb {a b}}
do_test 20.2 {
set res {}
db eval -asdict -withoutnulls {
SELECT n, a, b FROM (
SELECT 1 as n, 'aa' as a, NULL as b
UNION ALL
SELECT 2 as n, NULL as a, 'bb' as b
)
ORDER BY n
} D {
dict unset D *
lappend res [dict values $D]
}
unset D
execsql {DROP TABLE tad}
set res
} {{1 aa} {2 bb}}
#-------------------------------------------------------------------------
do_test 21.0 {
db transaction {
db close
}
} {}
do_test 20.1 {
do_test 21.1 {
sqlite3 db test.db
set rc [catch {
db eval {SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3} { db close }
} msg]
list $rc $msg
} {1 {invalid command name "db"}}
proc closedb {} {
db close
@@ -874,7 +936,7 @@ sqlite3 db test.db
db func closedb closedb
db func func1 func1
do_test 20.2 {
do_test 21.2 {
set rc [catch {
db eval {
SELECT closedb(),func1() UNION ALL SELECT 20,30 UNION ALL SELECT 30,40
@@ -884,9 +946,10 @@ do_test 20.2 {
} {0 {10 1 20 30 30 40}}
sqlite3 db :memory:
do_test 21.1 {
do_test 22.1 {
catch {db eval {SELECT 1 2 3;}} msg
db erroroffset
} {9}
finish_test
+21
View File
@@ -401,4 +401,25 @@ do_test vacuum-10.1 {
} {}
do_test vacuum-10.2 { execsql VACUUM } {}
# Verify that VACUUM still works if ATTACH is disabled.
#
do_execsql_test vacuum-11.1 {
PRAGMA page_size=1024;
VACUUM;
PRAGMA page_size;
} {1024}
sqlite3_db_config db ATTACH_CREATE 0
do_execsql_test vacuum-11.2 {
PRAGMA page_size=2048;
VACUUM;
PRAGMA page_size;
} {2048}
sqlite3_db_config db ATTACH_CREATE 1
sqlite3_db_config db ATTACH_WRITE 0
do_execsql_test vacuum-11.3 {
PRAGMA page_size=4096;
VACUUM;
PRAGMA page_size;
} {4096}
finish_test
+43 -31
View File
@@ -17,6 +17,7 @@ Options:
--uninstall Uninstall the extension
--version-check Check extension version against this source tree
--destdir DIR Installation root (used by "make install DESTDIR=...")
--tclConfig.sh FILE Use this tclConfig.sh instead of looking for one
Other options are retained and passed through into the compiler.}
@@ -29,6 +30,7 @@ set versioncheck 0
set CC {}
set OPTS {}
set DESTDIR ""; # --destdir "$(DESTDIR)"
set tclConfigSh ""; # --tclConfig.sh FILE
for {set ii 0} {$ii<[llength $argv]} {incr ii} {
set a0 [lindex $argv $ii]
if {$a0=="--install-only"} {
@@ -56,6 +58,9 @@ for {set ii 0} {$ii<[llength $argv]} {incr ii} {
} elseif {$a0=="--destdir" && $ii+1<[llength $argv]} {
incr ii
set DESTDIR [lindex $argv $ii]
} elseif {$a0=="--tclConfig.sh" && $ii+1<[llength $argv]} {
incr ii
set tclConfigSh [lindex $argv $ii]
} elseif {[string match -* $a0]} {
append OPTS " $a0"
} else {
@@ -88,39 +93,46 @@ if {$tcl_platform(platform) eq "windows"} {
}
set OUT tclsqlite3.dll
} else {
# Figure out the location of the tclConfig.sh file used by the
# tclsh that is executing this script.
#
if {[catch {
set LIBDIR [tcl::pkgconfig get libdir,install]
}]} {
puts stderr "$argv0: tclsh does not support tcl::pkgconfig."
exit 1
}
if {![file exists $LIBDIR]} {
puts stderr "$argv0: cannot find the tclConfig.sh file."
puts stderr "$argv0: tclsh reported library directory \"$LIBDIR\"\
does not exist."
exit 1
}
if {![file exists $LIBDIR/tclConfig.sh]
|| [file size $LIBDIR/tclConfig.sh]<5000} {
set n1 $LIBDIR/tcl$::tcl_version
if {[file exists $n1/tclConfig.sh]
&& [file size $n1/tclConfig.sh]>5000} {
set LIBDIR $n1
} else {
puts stderr "$argv0: cannot find tclConfig.sh in either $LIBDIR or $n1"
exit 1
}
}
# Read the tclConfig.sh file into the $tclConfig variable
#
#puts "using $LIBDIR/tclConfig.sh"
set fd [open $LIBDIR/tclConfig.sh rb]
set tclConfig [read $fd]
close $fd
if {"" eq $tclConfigSh} {
# Figure out the location of the tclConfig.sh file used by the
# tclsh that is executing this script.
#
if {[catch {
set LIBDIR [tcl::pkgconfig get libdir,install]
}]} {
puts stderr "$argv0: tclsh does not support tcl::pkgconfig."
exit 1
}
if {![file exists $LIBDIR]} {
puts stderr "$argv0: cannot find the tclConfig.sh file."
puts stderr "$argv0: tclsh reported library directory \"$LIBDIR\"\
does not exist."
exit 1
}
if {![file exists $LIBDIR/tclConfig.sh]
|| [file size $LIBDIR/tclConfig.sh]<5000} {
set n1 $LIBDIR/tcl$::tcl_version
if {[file exists $n1/tclConfig.sh]
&& [file size $n1/tclConfig.sh]>5000} {
set LIBDIR $n1
} else {
puts stderr "$argv0: cannot find tclConfig.sh in either $LIBDIR or $n1"
exit 1
}
}
#puts "using $LIBDIR/tclConfig.sh"
set fd [open $LIBDIR/tclConfig.sh rb]
set tclConfig [read $fd]
close $fd
} else {
# User-provided tclConfig.sh
#
set fd [open $tclConfigSh rb]
set tclConfig [read $fd]
close $fd
}
# Extract parameter we will need from the tclConfig.sh file
#
+3 -3
View File
@@ -2023,10 +2023,10 @@ static char *msort(
list = NEXT(list);
NEXT(ep) = 0;
for(i=0; i<LISTSIZE-1 && set[i]!=0; i++){
ep = merge(ep,set[i],cmp,offset);
ep = merge(set[i],ep,cmp,offset);
set[i] = 0;
}
set[i] = ep;
set[i] = merge(set[i],ep,cmp,offset);
}
ep = 0;
for(i=0; i<LISTSIZE; i++) if( set[i] ) ep = merge(set[i],ep,cmp,offset);
@@ -5925,7 +5925,7 @@ struct state **State_arrayof(void)
PRIVATE unsigned confighash(struct config *a)
{
unsigned h=0;
h = h*571 + a->rp->index*37 + a->dot;
h = a->rp->index*37 + a->dot;
return h;
}
+1 -1
View File
@@ -62,7 +62,7 @@ while {1} {
if {[regexp {^# *include "sqlite} $lx]} {
set lx "/* $lx */"
}
if {[regexp {^# *include "test_windirent.h"} $lx]} {
if {[regexp {^# *include "windirent.h"} $lx]} {
set lx "/* $lx */"
}
set lx [string map [list __declspec(dllexport) {}] $lx]
+1
View File
@@ -1797,6 +1797,7 @@ static void replicaSide(SQLiteRsync *p){
closeDb(p);
break;
}
sqlite3_db_config(p->db, SQLITE_DBCONFIG_WRITABLE_SCHEMA, 1, 0);
runSql(p, "ATTACH %Q AS 'replica'", p->zReplica);
if( p->wrongEncoding ){
p->wrongEncoding = 0;