Compare commits

...

142 Commits

Author SHA1 Message Date
larrybr d8d68a5b8e Sync w/trunk, zap surplus space.
FossilOrigin-Name: b8345630a2a322234bda49ee4b996f6ba20e2b080621e229a2ec5e820892a663
2022-11-28 14:11:48 +00:00
dan abbfa7aa17 Ensure the RETURNING clause is honoured when a row of a temp table is updated by an ON CONFLICT clause.
FossilOrigin-Name: a2449bcc2c71d0f4c3289621fbf1cb97f0f407c9f7b5bf18245b7854a07c6cfa
2022-11-28 13:47:27 +00:00
larrybr c1ca183abf Conform CLI .trace arg handling to its help.
FossilOrigin-Name: 31546ea320e8daa020f8a9f491718ffc3dde0e32954018afed88e8494a6aff6c
2022-11-28 02:28:44 +00:00
stephan ca9309cc56 Document an OPFS API change in Chrome v108 which does not break our code but does change several formerly async methods to synchronous. No code changes.
FossilOrigin-Name: e9dd87e28a96eb6560943b9a2cb3cb0ff14698c96fe63865944dcfed73b67bd3
2022-11-27 00:57:56 +00:00
drh 217e77d240 Show which AggInfo.aCol an Expr node represents in the tree trace debugging
logic.  Does not affect production builds.

FossilOrigin-Name: b8076f4dc55810855f668dfd4221bdb555cc78c61df200cf0cb87eb137e22850
2022-11-26 20:48:56 +00:00
drh 743c9ec4a5 Fix an incorrect assertion-fault in the "TREETRACE" debugging logic on
select.c.  The problem does not affect production builds.

FossilOrigin-Name: f11bb42292d94e7b7687bd2134f72afe1353182238cb4837fc0a6d78a021dd7e
2022-11-26 20:12:02 +00:00
stephan 4245667e35 OPFS VFS: add the opfs-unlock-asap=1 URI flag which tells the VFS to release implicit locks ASAP instead of during VFS idle time. This improves concurrency notably in the test app but brings a significant performance penalty in speedtest1 (roughly 4x slowdown). This is not the final word in OPFS concurrency, but gets us a step further.
FossilOrigin-Name: 9542f9ce9e023b489e2d93661f719fb0751c1e28f72fded9d3c2156d5777e7b1
2022-11-26 15:24:58 +00:00
drh 902ea83925 Relax restriction (8) on the push-down optimization so that it only applies
if one or more columns uses a collating sequence other than BINARY.
See [forum:/forumpost/3824ced748baa808|forum post 3824ced748baa808] and
check-in [346a3b12b861ce7b].

FossilOrigin-Name: adbca3448e2099f0d6149a073978f230ed9a92a2f384779879ef89e672231bcf
2022-11-25 17:05:55 +00:00
drh 49d400b091 Fix #ifdefs so that restrictions (8) and (9) of the push-down optimization
are still enforced even if compiled with SQLITE_OMIT_WINDOWFUNC.  This
fixes a bug introduced by check-in [346a3b12b861ce7b].

FossilOrigin-Name: 09e1e42e0ff26f9a71cbd128169f060a66425828d637bf8f781490ca38d99103
2022-11-25 16:32:59 +00:00
drh ae5cfda656 Immprove the query planner such that it is able to make use of indexed
expressions within an aggregate query with GROUP BY.  This implements
enhancement request [99378177930f87bd].

FossilOrigin-Name: b9190d3da70c41717eb188474fd225ee43d0b46646e1b03de5967bd332553870
2022-11-25 16:10:48 +00:00
drh 23f61a4ba8 Add restriction (9) to the push-down optimization: If the subquery is
a compound then all arms of the compound must have the same affinity.
dbsqlfuzz 3a548de406a50e896c1bf7142692d35d339d697f.

FossilOrigin-Name: 1ad41840c5e0fa702ba2c0df77a3ea126bd695b910b5d1271fa3129c38c58f5f
2022-11-25 15:52:00 +00:00
drh bedbb1bfab Remove a redundant assert() statement.
FossilOrigin-Name: e3e1b453dc47884ddc9e51c4302fa2f4d40bca3d69ac7c13d8e2ae1adc81ac56
2022-11-25 13:26:51 +00:00
drh f5d0656f16 Improve the robustness of the updateAccumulator() routine against OOM.
FossilOrigin-Name: d2844f577b69fdc16a0a2568c0958fc3d8aff33e9a0ef80e0f58e92f01097432
2022-11-25 13:15:48 +00:00
drh 7960da0346 Improved comments. Add assert()s to verify that the AggInfo structure
is unchanged after registers have been assigned.

FossilOrigin-Name: 5200b84195ee1ccaa387f7032eae3d463724c48cb53ba0251bbc79e927dd9752
2022-11-25 13:08:20 +00:00
drh 8683c09281 Correctly deal with IF-NULL-ROW operators when dealing with
indexed expressions in aggregates.

FossilOrigin-Name: 939cb47025354e2df047de7654c0b06f791957cfe4e904abe8892207cea90215
2022-11-24 23:35:27 +00:00
larrybr 472de149ae Speed up base85() conversions and sync w/trunk.
FossilOrigin-Name: 17b823500a2ed135c1f40aa7f4d87ba5b2eab35c0abd9e0856041cf0f510cbee
2022-11-24 20:11:34 +00:00
drh e644f7c24b Take care not to try to add comments to a TK_AGG_COLUMN opcode that does
not have an associated Table object because it is a reference to an indexed
expression.

FossilOrigin-Name: 5fc23863e4a6388884ce061cd69546757b4c39d731bbb58a8121b92fd5144f7b
2022-11-24 19:02:40 +00:00
drh 594357f2e1 Change a NEVER() into an assert().
FossilOrigin-Name: a0fd44f4e5a0ec83465203a0009f307ca50223833575895a3ebf8a289515714f
2022-11-24 18:45:56 +00:00
dan eb84c81c53 Fix a test case in fts3expr4.test to account for different locales.
FossilOrigin-Name: a2b6883ac2ef878f525ee847b170beb9f9ab9d1fa67557101be2cdae1e7f7a57
2022-11-24 17:58:55 +00:00
stephan df5d06d03e More work on the OPFS concurrency testing app.
FossilOrigin-Name: c0458caca3508d5d252f9b5198bda4f51a5c1874540f014b17e409f2daab1706
2022-11-24 17:53:09 +00:00
stephan 6b86b33793 JS documentation cleanups. No code changes.
FossilOrigin-Name: 18e89a436daa18a8c972caf06b298da43c97a6ea3e2e5229dccb6920c27bfdb9
2022-11-24 16:03:49 +00:00
dan 647b0dd12d Update multiplex3.test to account for the fact that the multiplexor xDelete method may return an error even if it manages to delete the first chunk of a file.
FossilOrigin-Name: 1a7f3254735054ed8ca32d5ec7c8cde9195a64702638bdc50392007e396fead2
2022-11-24 15:32:00 +00:00
drh c25f5ea6e8 Add NEVER() and ALWAYS() macros on branches that are believed to be
unreachable.

FossilOrigin-Name: 3a901e88c87fc76c7fe42e47a976a5706830f0dbd6027605663e4d55f4f33590
2022-11-24 15:04:23 +00:00
drh b669bb5e2b New test cases. Fix the logic so that it works for GROUP BY aggregates
that do not require sorting.

FossilOrigin-Name: ef6ebe7922f56c1584a005deedc85ca1070b4fe5082ada8bbf8d06df54f1c9ef
2022-11-24 13:19:25 +00:00
larrybr 9b9017def4 Speed up base64 conversions, and add test with more data for the baseNN conversion to grind.
FossilOrigin-Name: 6c84ae4ba83713c751fddff8be41686bbcb525ac8135e1520436c62d0bc23d2c
2022-11-24 02:59:33 +00:00
stephan f46091d73f Add sqlite3.oo1.DB.prototype.checkRc() and tests for both that method and its class-level counterpart.
FossilOrigin-Name: f7eaa6ba2147bfd6dbdc2444d0f919d846aa7f9b68cccab17ef585ffdacf3d60
2022-11-24 02:35:03 +00:00
drh 4b1555a9fc Merge the latest trunk changes into the agg-with-indexed-expr branch to
simplify diffs.

FossilOrigin-Name: 38c3d3f1ed0fd2bb62aa8a7e5a27f2b247123e094e2fdb0a2475d788c3dfbc04
2022-11-24 01:41:44 +00:00
drh 9b80cb5f39 Add explanatory comment to the new optimization. And add a test case.
FossilOrigin-Name: e6c20f61de7d048eee65c8e74a3eb36760ab9747ebd1ab50e49642b777c10306
2022-11-24 01:40:20 +00:00
stephan f1ce4f40c7 Minor JS doc updates.
FossilOrigin-Name: 27efd63ad7fb3bf8d0d07f2c9f48652c8cacc4e697c229c8085120a8e6b50a39
2022-11-23 21:09:51 +00:00
stephan 875db41afc Add optional zSchema argument to sqlite3_js_db_export().
FossilOrigin-Name: 9c23644b1e5bf44bfb431a35fd1674c11ccb99e9eb0989f10175b0cb2a858eaa
2022-11-23 21:03:22 +00:00
stephan 056a71562f OPFS concurrency test: add a URL flag to enable/disable unlock-asap mode.
FossilOrigin-Name: 1c1bf22eadae2a5a7d4358e7cdd22641c2efb9296f42e7376749293b3a58b114
2022-11-23 20:49:08 +00:00
stephan e79cb67c35 Add an experimental OPFS VFS-specific URI flag, opfs-unlock-asap, which tells the VFS to release implicit locks ASAP. This permits higher concurrency but hurts performance considerably. This may or may not be obsoleted by other concurrency-related experimentation.
FossilOrigin-Name: d23c917013485ec2793125221f3936b05c39d6eca941629fb819b6b4aa714520
2022-11-23 19:03:22 +00:00
drh 2e30d95fb6 Aggregates with GROUP BY now make use of expressions on indexes. This code
works and gets the correct answer for the test case in the ticket.  Lots more
testing and documentation is needed, however.

FossilOrigin-Name: 8dcf9f2031c16f296d187fe876d4204c71fc96fec120984ff11b6d8b03d58a5f
2022-11-23 18:51:04 +00:00
drh 590f013a87 This attempt at modifying AggInfo to make use of indexed expressions does not
work.  It gets an incorrect answer for the test case shown in the ticket.

FossilOrigin-Name: 84c06023f4a1606664fdb9811312603b31f7c94a43d0e443ba7dde7fdba029e3
2022-11-23 17:56:00 +00:00
stephan ad1285c5c0 Initial infrastructure for adding a mode to the OPFS VFS which causes implicit locks to be released ASAP, which increases concurrency at the cost of performance.
FossilOrigin-Name: c5b7a9715a13b696ab3ee965aa0a310f59b65f07cecd72faa2e3504bfd8eb632
2022-11-23 16:39:07 +00:00
dan c32e16643d Update Makefile.in to include new target "sqlite3r.c". For generating "sqlite3r.c" and "sqlite3r.h", versions of the amalgamation that include the recover extension. To build the shell tool against these files, add -DSQLITE_HAVE_SQLITE3R.
FossilOrigin-Name: 5f135575b923cb59947667071c6af9ff14063c933cedf37d6c2a0a1b86c85032
2022-11-23 16:08:49 +00:00
stephan 91a81316b9 Remove a bit of over-cleverness which breaks loading of sqlite3.js in some main-thread cases.
FossilOrigin-Name: 220cc4c6399b772b4ece03305a41b437ef0654d586a8a1c3dc5e7606fd36d655
2022-11-23 15:52:52 +00:00
stephan 3eaaec945c OPFS concurrency tester: ensure that the work interval timer does not overlap with the work time.
FossilOrigin-Name: 8f2076553bc486ea6a17934695ecc02217461af2082d891697b62aab89bd1b43
2022-11-23 15:33:49 +00:00
drh c6138e970e Further foundation prep work prior to starting to flesh-out the
optimizeAggregateUseOfIndexedExpr() routine.

FossilOrigin-Name: 23145fe999ff74d787a3999baedd4ffe755c5f1f1275082ed0338ba637ecc56e
2022-11-23 14:13:39 +00:00
larrybr 809be56218 Add more baseNN tests, get oversize error trapping working, and sync w/trunk
FossilOrigin-Name: 03819e9368fd9f78f351147a1dc865743f9634893e43a9d1e3d7cbaf4c966069
2022-11-22 22:46:41 +00:00
drh 58c0652923 Merge enhancements and fixes from trunk.
FossilOrigin-Name: f8932e04d4d18eb9d71edda15aa08af2eb139ff14d77ca147ea6e9b173e0f5e0
2022-11-22 20:58:18 +00:00
drh b23f1572ab Add the stub function: optimizeAggregateUsingIndexedExpr(). The hope is that
we can fill this in with a routine that does useful optimizations.

FossilOrigin-Name: d85bb724fdd6fbad2b88ed7f60e4174e3f65182356f404d04620c5cf6b17f77e
2022-11-22 20:37:41 +00:00
mistachkin c620522175 Avoid naming collision between the sha1 and shathree extensions.
FossilOrigin-Name: 9ec923b5dc24d6082da8d42bc0ee8ab1c418912625c0c56de9627be2c818ef98
2022-11-22 20:04:00 +00:00
drh c7c5b8a508 Include the treetrace bitmap comment accidentally omitted from the previous
check-in.

FossilOrigin-Name: db07471c531766a8eec1d5b41c9fd0283b5e64ee13166dc3391f70a1e1946121
2022-11-22 19:56:54 +00:00
drh cbef156d93 Merge trunk fixes and enhancements into the agg-with-indexed-expr branch.
FossilOrigin-Name: 070634781a5eb41f96b001e48b064e3cd8c82314f576335eb1fcd43792179291
2022-11-22 19:51:16 +00:00
drh 5d7aef16bf Rename the SELECTTRACE macro to TREETRACE, so that is corresponds to the new
CLI command.  Renumber all of the bits in the bitmask used to enable
various kinds of tracing, and add a trace bitmap decoder in sqliteInt.h.
Changes to debugging logic only.  No (intentional) changes to production code.

FossilOrigin-Name: 8036445a36d9d982c1305935e7e37367bdf9e466b923eb6286b52524802e3ccd
2022-11-22 19:49:16 +00:00
larrybr 411929025b Sync w/trunk. Ready for merge to trunk.
FossilOrigin-Name: ff67460e1a3d21c9ca7cbd171fbc5e6cbdb3797de359887f851776b73b732fdf
2022-11-22 19:12:24 +00:00
larrybr bf82c95e08 Add test/basexx1.test to test is_base85(t), base85(x) and base64(x). Sync w/trunk.
FossilOrigin-Name: 4e4334547795f659b4a5dc5fdaf842535643750a5f1ce1af799c526931a473e4
2022-11-22 19:04:09 +00:00
drh c9b7d943ad Fix the error message in the CLI for "no such VFS".
[forum:/info/1217fdd6b3|Forum post 1217fdd6b3].

FossilOrigin-Name: 861af465fda8485dfff143dc45c659b884d826aaec2ebaa941566404d1fe427b
2022-11-22 18:24:07 +00:00
dan 63b74e02a7 Add Makefile.in targets for sqlite3r.c and sqlite3r.h, versions of the amalgamation that include the recover extension.
FossilOrigin-Name: 59a837cfc7f9f96509491c8fc45355d2e8892af25246955e22adec1cbf37327b
2022-11-22 16:12:53 +00:00
drh 3c8e438583 Since the memory registers used by the columns and functions of an
AggInfo object are sequential, it is not neecessary to remember each register
separately.  We can simply remember the first one and do the math when
others are needed.

FossilOrigin-Name: dc5bd34963b761c819c565653156d0befbf65cc2cc5dc4113b0ce952450f0352
2022-11-22 15:43:16 +00:00
larrybr 2a072506ca Fix last function pointer in sqlite3Apis init. Reported at https://sqlite.org/forum/info/eba0faa96d
FossilOrigin-Name: 6d5dc9eed54b7d883aa7dbe2eb5ff17d2a703eabdb7d548a27cccc1fe5addb4a
2022-11-22 15:41:32 +00:00
drh 89e5dfac01 Omit the unnecessary AggInfo.mnReg field.
FossilOrigin-Name: d79c58ef08b917bacc0f24d210d8eb23f659f955c219b4757af42eee8f17099b
2022-11-22 14:31:13 +00:00
drh 42b78237cd Factor out the allocation of registers for aggregates into a separate
subroutine.

FossilOrigin-Name: 4475799d5b41e93eabc32fac502ac2de240642b3b64739216e32e9af92ee191d
2022-11-22 14:10:22 +00:00
drh ff5e4ecced This is the first in what is anticipated to be a long sequence of incremental
changes aimed at improving aggregate query processing, and in particular
helping aggregate queries take better advantage of indexes on expression.
The end goal is to resolve ticket [99378177930f87bd], though it remains to
be seen whether or not I can get there with this approach.

FossilOrigin-Name: cba837eae93f6b842d4e78ef00661a4f09deb99c53f12b3e8f46763749602597
2022-11-22 13:33:58 +00:00
drh c59b7a8053 Convert an ALWAYS() in sqlite3DbSpanDup() into an assert(), for a performance
increase and size reduction.

FossilOrigin-Name: 21e80a29737c367babcc0cf2533eed61b5d0fcf3cc3c33ab88761899e394eaf3
2022-11-22 12:47:32 +00:00
drh da217c958e Split out the debugging code that dumps an AggInfo object into a separate
subroutine called printAggInfo() so that it can be invoked interactively
during debugging.  No changes to production code.

FossilOrigin-Name: dc33cfbedef4b444adeadea17f8183b7c4ce5b87432d0c712f986b34c7374ff1
2022-11-21 17:40:23 +00:00
drh 03af6d7157 Change the name of the Parse.pIdxExpr field to pIdxEpr so that the name is
distinct from all other identifiers in the code and thus less confusing.

FossilOrigin-Name: a2962d0187534f3496282b48efbf38dabcd226bf8972be925c8433c55a8853fe
2022-11-21 16:40:12 +00:00
stephan 9f2be5db04 OPFS contention test: give each worker a distinct recognizable name instead of a random one.
FossilOrigin-Name: 5f564bf7de7ce3ad7bedb5f06b3086ceaec55da768a60d74059fa4fba4328567
2022-11-21 16:00:26 +00:00
drh b53d8fa9f2 Performance optimization and size reduction in insertCell() by omitting
the "pRC" argument and instead returning the result code as an integer.

FossilOrigin-Name: bee94d1bb0daade023cc1e274339daafc249e1978c0765fc45042b5f9060e478
2022-11-21 15:55:57 +00:00
drh e7d5384418 Performance optimization to sqlite3BtreeInsert().
FossilOrigin-Name: b8976ebfe03fbc1e09a38d598a62493a7f19ff7a2a3acd1ec54d0dee190471e9
2022-11-21 14:13:10 +00:00
drh d01dee5e6a Small performance improvement in sqlite3BtreeTransferRow().
FossilOrigin-Name: dab959ea3edf99788bfd76352cd46a3e56876b0e7d7008c6927aa14534853c50
2022-11-21 13:35:00 +00:00
stephan a303392a2c Add new JS tests dir to those pushed to the test server.
FossilOrigin-Name: 07182dca9f2a4ffea1af0684c93e55e105465b2ee9820c70764e3e7bc1c28efc
2022-11-21 06:07:22 +00:00
stephan b38ac0986e More tweaking of OPFS concurrency measures and the related test app.
FossilOrigin-Name: a8d4da1501d411085ec2fd48c4a056c8b1d97ef3c3203c5b403a854ac2864870
2022-11-21 05:18:24 +00:00
stephan 36d5554c9a Resolve missing SQLITE_LOCKED result code which triggered a new (since last checkin) exception in the OPFS VFS. Improve output of the OPFS contention tester app.
FossilOrigin-Name: 2debbbca33bd4170a1dc4dbb5eb3e68523e51d289b06c551e5560ac4e32e433b
2022-11-21 04:12:38 +00:00
stephan 27c4cd183d Add test app for experimenting with multi-worker OPFS concurrency. Tweak OPFS VFS to significantly improve the otherwise "unfortunate" concurrency situation.
FossilOrigin-Name: 96f76e7616f8157a342b9e1c42f7b1feab200d182268871a2b25f67d4ee2564c
2022-11-21 03:50:52 +00:00
larrybr b5aaf0f285 Get clean builds for MS tools. Add build examples for extension glommer (basexx.c)
Make sure extensions build for dynamic load and built into shell.

FossilOrigin-Name: da940d507e9e31a84daedede44a56f743198d7258a9e83892511fe8dd4c78d2b
2022-11-21 02:18:12 +00:00
larrybr 423003dca9 Fix a base64 decode bug. Provide for convenient inclusion of extension(s) built into the CLI, to simplify testing and for its own sake. Improve comments. Cure collision between base64.c and base85.c when both are in the same translation unit.
FossilOrigin-Name: 07543d23a98c2a851393a2674e59d3cf1df37c244fb451cb7436f49c95c1423f
2022-11-21 00:11:09 +00:00
stephan ae276719f0 js dist: account for a file rename in the previous checkin.
FossilOrigin-Name: 469f9011a885e19b99210c5e3e582afa140b8b5f0aa7a720334848df5ab6ae98
2022-11-20 05:47:17 +00:00
stephan 6d97c1a844 Further minor cleanups in the JS build related to vanilla vs ESM.
FossilOrigin-Name: 100a596800eca61477d9880092465d594c22be3707f2a11aaf6eb9e234fc6f2d
2022-11-20 05:36:52 +00:00
stephan 59a9654715 Remove an obsolete reference to WASMFS from ext/wasm/index.html.
FossilOrigin-Name: 51ff681864ec19844f8e7a46aef132e8a8601a1b64e1f5a243a53c6413f2a61a
2022-11-20 04:14:29 +00:00
stephan b2873a3014 Generic cleanups and doc additions in the wasm build files.
FossilOrigin-Name: d10f385e36ee7fe3077d80d8d6e7ce55732d20ef73e2a63533d8d2932ec8bf62
2022-11-20 04:14:00 +00:00
stephan 8e1bd8264d Add 'snapshot' target to create distinctly-named snapshot builds of the wasm deliverables zip file.
FossilOrigin-Name: f7620aa09e4893971e00cdee5c6f1fe15c3bd21f985bec90fbd90fdfc457ac31
2022-11-20 04:13:16 +00:00
stephan 0a94ef8b52 Minor cleanups in the ESM-related preprocessor filtering.
FossilOrigin-Name: 205884a273128bb666b496b659b4fa9f031ebdbbc1aa704fdeb4b7e015740098
2022-11-20 01:15:22 +00:00
drh ecba10730d New assert()s confirm that BTREE_PREFORMAT must be the same value as
OPFLAG_PREFORMAT.

FossilOrigin-Name: f40bf2c642643ae61d331e8d4815f601224fa258ab34344c6756966163a89f4a
2022-11-19 20:10:55 +00:00
drh 5a1d659d79 Performance optimization in sqlite3BtreeInsert().
FossilOrigin-Name: 9776fa31758161970a50995a487b6543ed71e9610460b7324304ef21d9248707
2022-11-19 19:37:26 +00:00
drh 16635fa777 Performance improvement and size reduction in balance_nonroot().
FossilOrigin-Name: 715bc81eb833ad4834d139a04085e0386c54c30d7395207e48972c4dfe5879c1
2022-11-19 18:43:32 +00:00
drh 47de1f9e19 Small performance improvement and size reduction in btree.
FossilOrigin-Name: daa07149c0a0fcb6a6a1ace6020ca68802588ed309f5aaaf99c871088bc46908
2022-11-19 18:17:40 +00:00
stephan 8d0d409876 Get tester1.js working via an ES6 worker module and add that variant to the dist zipfile.
FossilOrigin-Name: 90480586f1b2ad82118e19536b095431b8457f294c0afaa9b4f883f184cc804c
2022-11-19 16:16:40 +00:00
drh 1e62057436 Improved comments on the pageFreeArray() routine of btree.c. No changes to
code.

FossilOrigin-Name: 0c2adc6d3547b07e950ae49f07f688f71a21b3ad5a51f16f0e8d49ab91564582
2022-11-19 14:18:48 +00:00
drh bf9b994f52 Streamline the decodeFlags() routine in btree.c for a small performance
increase.

FossilOrigin-Name: 4cb285210b4a2b14c80962bf2ecb35be310d3444c329c15d86b3073096455704
2022-11-19 13:09:03 +00:00
stephan 27a67968af Add build of sqlite3.mjs (ES6 module), add a test app for it, and include it in the dist build.
FossilOrigin-Name: 2e783670e10b59e67c14b0db7f4803b41790cc7730de221d54fa2d4483cfba33
2022-11-19 05:26:45 +00:00
stephan c768ef7289 Integrate a custom preprocessor to the JS build process to facilitate creation of both vanilla JS and ES6 Module builds from the same source files. There is still some build-level reworking pending to make an ESM build a first-class deliverable.
FossilOrigin-Name: 10c723d96d61d2e552ec1102563d58f1eb11bc3d30e03606fd8e0279c5a9043a
2022-11-19 02:58:03 +00:00
stephan f7d98ac764 More work towards creation of a ES6 JS module.
FossilOrigin-Name: 6b826e700f6849eebfbba38e5948b96be245994e3e03ea30743114d3f5689c42
2022-11-19 02:51:41 +00:00
larrybr fb2e0bfa8b New extensions for base85 and base64 conversion UDFs
FossilOrigin-Name: 5cc1fe1ddc2a33c59d3c006057e474c7c7975c483395ddea530df6968fe15341
2022-11-19 02:39:16 +00:00
larrybr ff4917e9a5 Create new branch named "base_convert"
FossilOrigin-Name: 0cbf55407a3a94b1c9c0ada52fa2995088bac3739876fa8d465dfb4dfcc4a6ea
2022-11-19 02:32:26 +00:00
drh 85728a21c6 Small performance optimization in btree.c.
FossilOrigin-Name: f710cce13577788cf3b95ed7089c3af2854271ff53f0a0b7b0619f315e331eff
2022-11-19 00:22:12 +00:00
drh cf3107c7d4 Databases created using sqlite3_deserialize() should report their filename
as an empty string, not as "x".  Fix for ticket [53043c9793715f08].

FossilOrigin-Name: ff494449efd475878c549728cc22ee9b12d13674068781747fc042a0c1bd09c8
2022-11-19 00:08:35 +00:00
drh 26e0f2e916 Update the version number to 3.41.0 to begin the next development cycle.
FossilOrigin-Name: 5c669f5f399fe89998b9edba6486f2a6fe5fca789ed82e8711349c8736b293d9
2022-11-18 17:57:19 +00:00
stephan 76abcfbdc0 shell.c.in: on non-Windows platforms, check for $XDG_CONFIG_HOME/sqlite3/sqliterc before ~/.sqliterc, per request in [forum:7a16582b1e403c81|forum post 7a16582b1e403c81].
FossilOrigin-Name: 17065d095d26a814acf1e13f5cc18b21fecc58eb8c9da100458029bb139fcd35
2022-11-18 15:22:45 +00:00
stephan ee026c5479 Replace use of cpp with the fit-to-purpose c-pp to avoid cpp's C-centric/JS-unfriendly quirks.
FossilOrigin-Name: 49d70f071e918d5d095c807575bb7ce2b287a123261e789e938521b3b409429a
2022-11-18 02:29:59 +00:00
drh d8e48fffdf Fix corner cases in UTF8 handling in the REGEXP extension.
[forum:/forumpost/3ffe058b04|Forum post 3ffe058b04].

FossilOrigin-Name: abb18f61c5cec0f524acc41453b4c06b61c5af51ff46417588837fc0c3967288
2022-11-17 19:24:39 +00:00
stephan c7c15d1b83 Merge trunk into js-cpp branch.
FossilOrigin-Name: e047b33d1fb7d6a32e967f03f9952249cd2da4d21dc301fe92bd7baa0da5d6a9
2022-11-17 15:21:49 +00:00
drh 3c1572ddb4 Use the log10() and log2() functions from the standard C library to implement
the equivalent SQL functions, in the hope that this will prevent reported
precision problems.
See [forum:/forumpost/cfceb1230bdcfd84|forum post cfceb1230bdcfd84] and the
surrounding thread.

FossilOrigin-Name: 7c572d02e60a83b36543ba4d9d45f61e9fc111b61fee085410c2d87558c732d6
2022-11-17 14:40:33 +00:00
drh 823872c6d6 Split out the documentation for sqlite3_value_encoding() into its own
page and make it clear that this interface is intended for testing and
debugging only.
[forum:/forumpost/c9f445453da950ad|Forum thread c9f445453da950ad].
Comment changes only - no changes to code.

FossilOrigin-Name: 9048a766ff7dfa0cd91ea74092e462f4501cb3f719033ccb55700bf5e4dfd0d3
2022-11-17 13:58:25 +00:00
drh 841c98e14c Raise an error if an attempt is made to compile the CLI with the
SQLITE_OMIT_COMPLETE option, since sqlite3_complete() really is necessary
for the CLI to operate sanely.

FossilOrigin-Name: a119a9e2ade4eac5feb1aa885c15b83e725f87386351de99d3abb49656219d50
2022-11-17 01:24:06 +00:00
stephan a872466c8e Change a self.X JS reference to X to account for a symbol resolution discrepancy between vanilla JS and ES6 modules, as explained in [forum:801d8f77e5115141|forum post 801d8f77e5115141].
FossilOrigin-Name: 0590de4da1103d842b9f9f25bcd2e69223b2ea067ae2f320f58dd3763218b39d
2022-11-16 21:52:29 +00:00
stephan 1d4f1cf426 wasm build: include FTS5 instead of FTS4, per /chat and HN discussions.
FossilOrigin-Name: 85c3f2ac5a887ba809f236c8c9d3837b2fdb578d4beae61bc78ada7ce03b58e2
2022-11-16 21:39:23 +00:00
drh 08efa45113 Fix harmless typo in comment, reported by Debian in Fossil
[https://fossil-scm.org/forum/forumpost/15f7327318].

FossilOrigin-Name: 5689f0d9ad1be532b274508938b25ff0d63027b8cc31f796dfaa2cca71d53642
2022-11-16 19:57:21 +00:00
drh a07df967f5 Avoid returning SQLITE_SCHEMA if the first query run on a connection
is "SELECT nosuchcolumn" or similar.
[forum:/forumpost/c1798f77ef|Forum post c1798f77ef]

FossilOrigin-Name: 3dc4f75d77417df1ef19be8e3191d246fb44ca7f7d1de7b161c5cb0f8aafeded
2022-11-16 19:53:39 +00:00
drh 2f2c5e2061 Version 3.40.0
FossilOrigin-Name: 89c459e766ea7e9165d0beeb124708b955a4950d0f4792f457465d71b158d318
2022-11-16 12:10:08 +00:00
drh a70a01dbcb Previous change to the fake_big_file command in the test harness was not
correct.  This one should fix the problem.

FossilOrigin-Name: 6ee57a2e4e3399481100c40da0229d2d33cbe3290016185c8a60839d14b209f6
2022-11-14 19:42:01 +00:00
drh 1da477d3ab Change the fake_big_file test command so that it fails for files larger than
2MB on Windows, as we have observed that Window10 will sometimes bluescreen
when operating on very large sparse files.

FossilOrigin-Name: d39ffd2abadcc85c46ba17612f9f575005b4818e18bf39362b8ee574837cfad9
2022-11-14 18:57:21 +00:00
dan cfce889cda Avoid running bigsort.test with SQLITE_DEBUG builds. The assert() statements added in [7c96a56] make it too slow.
FossilOrigin-Name: fa68dd8928c76888d535e649794743eeefd3abe0f909792a128955710bfc948a
2022-11-14 17:48:26 +00:00
stephan 8d7b41302f Remove some outdated code comments. No code changes.
FossilOrigin-Name: 80ff592a9d0157bfa8bd1f9959c3aa26cab0ec16fdccf7b58a7523b8912c1a8c
2022-11-13 19:29:51 +00:00
drh 7e475e571f Disable the ability to change the schema_version cookie when
SQLITE_DBCONFIG_DEFENSIVE mode is enabled.  This is a security
enhancement inspired by the question in
[forum:/forumpost/2b9cc3dae1f1e5f6|forum post 2b9cc3dae1f1e5f6].

FossilOrigin-Name: 1d81381e8f5db5d7064cc313b8544ca3cb1ca9e8cd61e71368a2d2e598befc9c
2022-11-12 17:17:01 +00:00
drh a6303704a3 Remove unnecessary tabs in the ChaCha20 implementation.
[forum:/forumpost/0cdce5db8c|Forum post 0cdce5db8c].

FossilOrigin-Name: b7179efbdb2bdc878acec0abfe272821f7e0d7d9e5ef06cd7fd796ef295e54ab
2022-11-10 23:10:11 +00:00
dan 873a84054c Avoid returning SQLITE_SCHEMA if the first query run on a connection is "SELECT nosuchcolumn" or similar. Forum post <https://sqlite.org/forum/forumpost/c1798f77ef>.
FossilOrigin-Name: d31c019fd6849e4c3f8452e75c6cfefd613ade5355e896be368bd16fef28c627
2022-11-10 19:19:52 +00:00
stephan 2c4d62f013 Automatically relinquish implicitly-acquired OPFS file locks during VFS idle time in an attempt to help alleviate cross-tab locking contention like that described in [forum:58a377083cd24a|forum post 58a377083cd24a].
FossilOrigin-Name: 8daf24ff73dd9928057412e0e4c2e2b2e47e1dca66acfb6b07c846e8d97582cc
2022-11-10 13:22:35 +00:00
stephan da2641597a Rework automatically acquired OPFS locks to be released during idle time. This eliminates the performance hit reported in [46304ba057707c].
FossilOrigin-Name: a7fe91afca473fe55c983bc81d214df4ef3699863c7423fa4b6b9cde23d6a3b4
2022-11-10 13:14:30 +00:00
stephan aafa022f5b OPFS: if an op which needs a lock is called when no lock has been obtained, automatically lock it at the start of the op and unlock it at the end of that op. This is an attempt to alleviate the cross-tab contention described in [forum post 58a377083cd24a|forum:58a377083cd24a] but it increases speedtest1 run time by approximately 4x. Perhaps auto-lock can be combined with the older idle-time-based auto-unlock to unlock such locks (but not those from xLock()) to improve this?
FossilOrigin-Name: 46304ba057707c3b072b6e7bb8c4af774f653aa5814099f0866cd87b2b73abeb
2022-11-10 11:35:10 +00:00
dan 838865c0bb Better handle an error in the fts5 integrity-check code. dbsqlfuzz e87c62f9b67ea21aebdc36ab71cab7cc3eda8dc3.
FossilOrigin-Name: ae43e97087a3207a5ca3ffae75fbe7a33c01f4a38ce0d1d7eed8591ae3083617
2022-11-09 11:17:57 +00:00
drh ea038d3d23 Additional defense against corrupt database files in dbdata.c.
FossilOrigin-Name: 2e70d1e5c9b2c9e068be3ccf8a6062edf7bdde2e46d60ba8ce54eda851af6008
2022-11-09 11:02:39 +00:00
drh 92c0881d9d Increase the precision of the nData parameter to the dbdataValue() routine
inside the sqlite_dbdata virtual table, to avoid the possibility of integer
overflow.

FossilOrigin-Name: 295447b44fc7658891352a9b3f792379b52a76ee5dcfd498b7028f5f87b40e9e
2022-11-09 10:17:25 +00:00
dan 50ba1a0213 Fix a problem with URI formatting in calls to sqlite3rbu_vacuum(zDb, NULL) causing problems on win32. First reported here <https://sqlite.org/forum/forumpost/a7ae915a9d>.
FossilOrigin-Name: 77ccc8ea8a901df64feabe08e1210087d060f0bc133242e87a94a71b68b5b5fa
2022-11-08 20:04:05 +00:00
dan afe768193f Do not attempt to run test script dbpagefault.test with SQLITE_OMIT_VIRTUALTABLE builds.
FossilOrigin-Name: 901918c4867557e51160ef9c495e4e007de26cfe07c237addc00a388662957e8
2022-11-08 19:36:26 +00:00
dan 64c40ec2c7 Fix a problem in main.mk preventing the amalgamation-testfixture target from building.
FossilOrigin-Name: ccf00a0e22a2a9815ff7db1c6c6457fce35aaf50843c1b5ff2adda22d6f70314
2022-11-08 15:49:12 +00:00
drh 80fafc25ce Further improvements to the handling of the u32 and u16 typedefs in the
shell.c source file.

FossilOrigin-Name: 55a19677d723147aeb2b4a86bbd01756ddeb2072cba72c3145ad32d335e203b0
2022-11-07 19:40:20 +00:00
drh d9324a5226 Improve the ability of mkshellc.tcl to remove redundant typedefs from the
generated shell.c code.  This is needed to get shell.c to build on older
C compilers.

FossilOrigin-Name: 3645585f37631d60cefab1d55cdb1ee060aae87317b9b158a01329ca8a4d3e1e
2022-11-07 18:36:02 +00:00
dan 40503750fa Add the sqlite3rbu_rename_handler() API. To override the default routine that RBU uses to rename files.
FossilOrigin-Name: ebbb1f88e7b5d6cbe84d400f1a187acedb4c668d0b7e4c63bf1496e57da9b8ad
2022-11-07 18:00:18 +00:00
stephan 49507d2af6 Minor doc updates in speed-check.sh. No code changes.
FossilOrigin-Name: d2ed4116fbf1de3c840f84e05db6f29f7b489518ac07d56f61df153deab6bf6b
2022-11-07 17:21:53 +00:00
drh 54422235e9 Fix an obsolete comment.
FossilOrigin-Name: e377c0a1ef030395293c5f24d7cb8e5b36ce972e9fac31b99c8425075486a46a
2022-11-07 15:23:51 +00:00
drh e38b6e0318 Fix typo in comment.
FossilOrigin-Name: c3b94d7d4697a5c3983253f6266c544d8037617778a09d491bc12eb13f08ac75
2022-11-07 15:01:05 +00:00
stephan 690d4c545d Add sqlite3.wasm.alloc.impl() as a "public back door" into the low-level non-throwing allocator. Correct sqlite3.WasmAllocError constructor to behave like its usages expect it to and add tests for that.
FossilOrigin-Name: cea8bf9a144d842c4755c3130273524926e8c4831d7f21c4e34d4e8c74109c8c
2022-11-07 13:06:20 +00:00
drh da01757870 Fix requirements marks so that they match documentation corrections.
FossilOrigin-Name: 10d6189d23133006b39ea230045a918483721dd48f5558f77e57e23693097d16
2022-11-07 12:21:06 +00:00
stephan c0a18d6a5a Minor wasm doc touchups. No code changes.
FossilOrigin-Name: 2c448368913a844bdb5e69f8fa3bad91a2b6612ba3b7f3f650dd07b81db25a77
2022-11-07 12:12:27 +00:00
drh 1407458c6f Fix the documentation regarding negative length parameters for
sqlite3_result_text() interfaces, to point out that the length parameter
to sqlite3_result_text64() cannot be negative.
[forum:/forumpost/cf1d043b07|Forum post cf1d043b07].
Comment/documentation change only.

FossilOrigin-Name: 8016507651f377b08deb3a13cc559d56ce6e934c3073a8e63d05fd946b8403a4
2022-11-07 11:19:28 +00:00
drh b36f697b50 Make sure that the recoverStrlen() function (used internally by the new
recovery extension) is 64-bit safe.

FossilOrigin-Name: ea30a6bfc463acce26a4d710e7bf56519ad096ce2fe904ee7e199f5c55068034
2022-11-07 11:05:53 +00:00
drh b819202acf The recoverAssertMutexHeld() function is for testing and verification only
and should be a harmless no-op for production builds.
[forum/forumpost/b4f2c7d402|Forum post b4f2c7d402].

FossilOrigin-Name: d75504710c86af0037ac897106aadffe54955463195ee4dfb2cdfc81d396cbb4
2022-11-06 17:19:34 +00:00
stephan 6e8a3341ea shell.c.in: on non-Windows platforms, check for $XDG_CONFIG_HOME/sqlite3/sqliterc before ~/.sqliterc, per request in [forum:7a16582b1e403c81|forum post 7a16582b1e403c81].
FossilOrigin-Name: 49c6e438a83b9ff40ebadd3dfd5f58e6eea053575e15335909f5ee59a6dba82c
2022-11-06 13:12:11 +00:00
dan f53c0a0d4e Update test file dbpagefault.test to account for the restriction on using SQLITE_VTAB_DIRECTONLY virtual tables from within triggers.
FossilOrigin-Name: 2b68fc8aa35cc69e4d2c26aaebbf3f2b1f1c08d15ca9efcbe5be21d45735d3f1
2022-11-05 19:26:45 +00:00
dan a16edfa29b Tweaks to recover module test scripts to work with various permutations.
FossilOrigin-Name: 454c61e818f6941c9a23f6600e37828a3f2b2ad3c6dbc8d6223570aed5d9cd8b
2022-11-04 18:32:45 +00:00
dan 5f7e173abf Another test case fix for auto-vacuum builds.
FossilOrigin-Name: c1c47eff48e0831b5aed987e90d797aee99caf1861fa8318c6c3ecfb108de7e6
2022-11-04 17:07:33 +00:00
dan 80dbd818f2 Fixes to recover module test scripts so that they may be used with SQLITE_DEFAULT_AUTOVACUUM and SQLITE_SECURE_DELETE builds.
FossilOrigin-Name: 0e5597ce5353dea2cdb092b166b57ba1d60f8115eb468349f2b2869803691a2c
2022-11-04 16:39:39 +00:00
dan ffc9b1b046 Fix Tcl tests so that they again build and run with SQLITE_OMIT_VIRTUALTABLE defined.
FossilOrigin-Name: 06a9dbea40c8a0cdfae6b127a1ce1cba0547acdf1115c087e9b790a78c264b52
2022-11-04 15:17:14 +00:00
drh 881f0f4114 Remove an unimportant testcase() macro that is no longer reachable due to the
improvement in corruption detection.

FossilOrigin-Name: cd7e3568a9b0c0f5ef5ca8a29c55cefe2bbad2dbc88a3718a87bb24a8ada3b7f
2022-11-04 12:59:04 +00:00
dan 4868a86513 Add another test case for the OP_Found fix.
FossilOrigin-Name: 6cdd64a445fe16d547d7bf5ea26dad3ab6d9d5f7d1c2b49fdc9d7a2edaa031c6
2022-11-04 11:59:40 +00:00
drh 093677add4 Enhance the ability of the OP_Found and similar opcodes to detect truncated
index records and report SQLITE_CORRUPT.
dbsqlfuzz 2b12f90aeff8e081706c7e9b58834f04869f446c.  Test cases in TH3.

FossilOrigin-Name: 059a09da2c5fd9c7e723c713565fbaf71602079feef0704129cc5cbbd0033936
2022-11-04 11:54:42 +00:00
stephan c75445087f Correct sqlite3-wasm.c's SQLITE_DEFAULT_CACHE_SIZE (it's measured in kb, not bytes).
FossilOrigin-Name: 479ad980dfe509403e184e39a5aa441171e47b3297e05039f85516e72e9f15be
2022-11-04 09:02:21 +00:00
stephan 7d24ff2971 Add experimental esm target to ext/wasm/GNUmakefile to tell emcc to generate sqlite3.mjs (ES6 module) instead of sqlite3.js. Related to discussion at [forum:ed4596cf8496a39b].
FossilOrigin-Name: e55d8eba83012492d85418dc0faedce5896027ecc70295a5ca1826f61a5edbaf
2022-11-03 22:51:20 +00:00
stephan b0ab21dee4 Experimentally use clang's C preprocessor to filter #ifdef's out of the generated sqlite3-api.js, the goal being to see if we can feasibly use cpp to include ES6 module-specific code in the main code base and conditionally filter it out.
FossilOrigin-Name: 718a6d371e61359d73c8f80afdb248e3d9b4d8df4c4e5c122ac884344e31035b
2022-11-03 22:14:47 +00:00
stephan 4df2ab575f Globally replace '' with "" for empty JS strings to please C preprocessor.
FossilOrigin-Name: e92e1f42bef94a1df29f66b4111ebfde93eba3759bc5d5a9c95f714508851346
2022-11-03 21:21:10 +00:00
107 changed files with 5239 additions and 1097 deletions
+10
View File
@@ -446,6 +446,7 @@ TESTSRC += \
$(TOP)/ext/expert/test_expert.c \
$(TOP)/ext/misc/amatch.c \
$(TOP)/ext/misc/appendvfs.c \
$(TOP)/ext/misc/basexx.c \
$(TOP)/ext/misc/carray.c \
$(TOP)/ext/misc/cksumvfs.c \
$(TOP)/ext/misc/closure.c \
@@ -778,6 +779,15 @@ sqlite3.c: .target_source $(TOP)/tool/mksqlite3c.tcl
cp tsrc/sqlite3ext.h .
cp $(TOP)/ext/session/sqlite3session.h .
sqlite3r.h: sqlite3.h
$(TCLSH_CMD) $(TOP)/tool/mksqlite3h.tcl $(TOP) --enable-recover >sqlite3r.h
sqlite3r.c: sqlite3.c sqlite3r.h
cp $(TOP)/ext/recover/sqlite3recover.c tsrc/
cp $(TOP)/ext/recover/sqlite3recover.h tsrc/
cp $(TOP)/ext/recover/dbdata.c tsrc/
$(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl --enable-recover $(AMALGAMATION_LINE_MACROS)
sqlite3ext.h: .target_source
cp tsrc/sqlite3ext.h .
+1 -1
View File
@@ -1 +1 @@
3.40.0
3.41.0
Vendored
+9 -9
View File
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69 for sqlite 3.40.0.
# Generated by GNU Autoconf 2.69 for sqlite 3.41.0.
#
#
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
@@ -726,8 +726,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='sqlite'
PACKAGE_TARNAME='sqlite'
PACKAGE_VERSION='3.40.0'
PACKAGE_STRING='sqlite 3.40.0'
PACKAGE_VERSION='3.41.0'
PACKAGE_STRING='sqlite 3.41.0'
PACKAGE_BUGREPORT=''
PACKAGE_URL=''
@@ -1468,7 +1468,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures sqlite 3.40.0 to adapt to many kinds of systems.
\`configure' configures sqlite 3.41.0 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1533,7 +1533,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of sqlite 3.40.0:";;
short | recursive ) echo "Configuration of sqlite 3.41.0:";;
esac
cat <<\_ACEOF
@@ -1661,7 +1661,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
sqlite configure 3.40.0
sqlite configure 3.41.0
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2080,7 +2080,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by sqlite $as_me 3.40.0, which was
It was created by sqlite $as_me 3.41.0, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -12390,7 +12390,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by sqlite $as_me 3.40.0, which was
This file was extended by sqlite $as_me 3.41.0, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -12456,7 +12456,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
sqlite config.status 3.40.0
sqlite config.status 3.41.0
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
+1
View File
@@ -6278,6 +6278,7 @@ int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCksum){
/* If this is a new term, query for it. Update cksum3 with the results. */
fts5TestTerm(p, &term, z, n, cksum2, &cksum3);
if( p->rc ) break;
if( eDetail==FTS5_DETAIL_NONE ){
if( 0==fts5MultiIterIsEmpty(p, pIter) ){
+269
View File
@@ -0,0 +1,269 @@
/*
** 2022-11-18
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
**
** This is a SQLite extension for converting in either direction
** between a (binary) blob and base64 text. Base64 can transit a
** sane USASCII channel unmolested. It also plays nicely in CSV or
** written as TCL brace-enclosed literals or SQL string literals,
** and can be used unmodified in XML-like documents.
**
** This is an independent implementation of conversions specified in
** RFC 4648, done on the above date by the author (Larry Brasfield)
** who thereby has the right to put this into the public domain.
**
** The conversions meet RFC 4648 requirements, provided that this
** C source specifies that line-feeds are included in the encoded
** data to limit visible line lengths to 72 characters and to
** terminate any encoded blob having non-zero length.
**
** Length limitations are not imposed except that the runtime
** SQLite string or blob length limits are respected. Otherwise,
** any length binary sequence can be represented and recovered.
** Generated base64 sequences, with their line-feeds included,
** can be concatenated; the result converted back to binary will
** be the concatenation of the represented binary sequences.
**
** This SQLite3 extension creates a function, base64(x), which
** either: converts text x containing base64 to a returned blob;
** or converts a blob x to returned text containing base64. An
** error will be thrown for other input argument types.
**
** This code relies on UTF-8 encoding only with respect to the
** meaning of the first 128 (7-bit) codes matching that of USASCII.
** It will fail miserably if somehow made to try to convert EBCDIC.
** Because it is table-driven, it could be enhanced to handle that,
** but the world and SQLite have moved on from that anachronism.
**
** To build the extension:
** Set shell variable SQDIR=<your favorite SQLite checkout directory>
** *Nix: gcc -O2 -shared -I$SQDIR -fPIC -o base64.so base64.c
** OSX: gcc -O2 -dynamiclib -fPIC -I$SQDIR -o base64.dylib base64.c
** Win32: gcc -O2 -shared -I%SQDIR% -o base64.dll base64.c
** Win32: cl /Os -I%SQDIR% base64.c -link -dll -out:base64.dll
*/
#include <assert.h>
#ifndef SQLITE_SHELL_EXTFUNCS /* Guard for #include as built-in extension. */
#include "sqlite3ext.h"
#endif
SQLITE_EXTENSION_INIT1;
#define PC 0x80 /* pad character */
#define WS 0x81 /* whitespace */
#define ND 0x82 /* Not above or digit-value */
#define PAD_CHAR '='
#ifndef UBYTE_TYPEDEF
typedef unsigned char ubyte;
# define UBYTE_TYPEDEF
#endif
static const ubyte b64DigitValues[128] = {
/* HT LF VT FF CR */
ND,ND,ND,ND, ND,ND,ND,ND, ND,WS,WS,WS, WS,WS,ND,ND,
/* US */
ND,ND,ND,ND, ND,ND,ND,ND, ND,ND,ND,ND, ND,ND,ND,ND,
/*sp + / */
WS,ND,ND,ND, ND,ND,ND,ND, ND,ND,ND,62, ND,ND,ND,63,
/* 0 1 5 9 = */
52,53,54,55, 56,57,58,59, 60,61,ND,ND, ND,PC,ND,ND,
/* A O */
ND, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
/* P Z */
15,16,17,18, 19,20,21,22, 23,24,25,ND, ND,ND,ND,ND,
/* a o */
ND,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
/* p z */
41,42,43,44, 45,46,47,48, 49,50,51,ND, ND,ND,ND,ND
};
static const char b64Numerals[64+1]
= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
#define BX_DV_PROTO(c) ((((ubyte)(c))<0x80)? b64DigitValues[c] : 0x80)
#define IS_BX_DIGIT(bdp) (((ubyte)(bdp))<0x80)
#define IS_BX_WS(bdp) ((bdp)==WS)
#define IS_BX_PAD(bdp) ((bdp)==PC)
#define BX_NUMERAL(dv) (b64Numerals[(ubyte)(dv)])
/* Width of base64 lines. Should be an integer multiple of 4. */
#define B64_DARK_MAX 72
/* Encode a byte buffer into base64 text with linefeeds appended to limit
** encoded group lengths to B64_DARK_MAX or to terminate the last group.
*/
static char* toBase64( ubyte *pIn, int nbIn, char *pOut ){
int nCol = 0;
while( nbIn >= 3 ){
/* Do the bit-shuffle, exploiting unsigned input to avoid masking. */
pOut[0] = BX_NUMERAL(pIn[0]>>2);
pOut[1] = BX_NUMERAL(((pIn[0]<<4)|(pIn[1]>>4))&0x3f);
pOut[2] = BX_NUMERAL(((pIn[1]&0xf)<<2)|(pIn[2]>>6));
pOut[3] = BX_NUMERAL(pIn[2]&0x3f);
pOut += 4;
nbIn -= 3;
pIn += 3;
if( (nCol += 4)>=B64_DARK_MAX || nbIn<=0 ){
*pOut++ = '\n';
nCol = 0;
}
}
if( nbIn > 0 ){
signed char nco = nbIn+1;
int nbe;
unsigned long qv = *pIn++;
for( nbe=1; nbe<3; ++nbe ){
qv <<= 8;
if( nbe<nbIn ) qv |= *pIn++;
}
for( nbe=3; nbe>=0; --nbe ){
char ce = (nbe<nco)? BX_NUMERAL((ubyte)(qv & 0x3f)) : PAD_CHAR;
qv >>= 6;
pOut[nbe] = ce;
}
pOut += 4;
*pOut++ = '\n';
}
*pOut = 0;
return pOut;
}
/* Skip over text which is not base64 numeral(s). */
static char * skipNonB64( char *s ){
char c;
while( (c = *s) && !IS_BX_DIGIT(BX_DV_PROTO(c)) ) ++s;
return s;
}
/* Decode base64 text into a byte buffer. */
static ubyte* fromBase64( char *pIn, int ncIn, ubyte *pOut ){
if( ncIn>0 && pIn[ncIn-1]=='\n' ) --ncIn;
while( ncIn>0 && *pIn!=PAD_CHAR ){
static signed char nboi[] = { 0, 0, 1, 2, 3 };
char *pUse = skipNonB64(pIn);
unsigned long qv = 0L;
int nti, nbo, nac;
ncIn -= (pUse - pIn);
pIn = pUse;
nti = (ncIn>4)? 4 : ncIn;
ncIn -= nti;
nbo = nboi[nti];
if( nbo==0 ) break;
for( nac=0; nac<4; ++nac ){
char c = (nac<nti)? *pIn++ : b64Numerals[0];
ubyte bdp = BX_DV_PROTO(c);
switch( bdp ){
case ND:
/* Treat dark non-digits as pad, but they terminate decode too. */
ncIn = 0;
/* fall thru */
case WS:
/* Treat whitespace as pad and terminate this group.*/
nti = nac;
/* fall thru */
case PC:
bdp = 0;
--nbo;
/* fall thru */
default: /* bdp is the digit value. */
qv = qv<<6 | bdp;
break;
}
}
switch( nbo ){
case 3:
pOut[2] = (qv) & 0xff;
case 2:
pOut[1] = (qv>>8) & 0xff;
case 1:
pOut[0] = (qv>>16) & 0xff;
}
pOut += nbo;
}
return pOut;
}
/* This function does the work for the SQLite base64(x) UDF. */
static void base64(sqlite3_context *context, int na, sqlite3_value *av[]){
int nb, nc, nv = sqlite3_value_bytes(av[0]);
int nvMax = sqlite3_limit(sqlite3_context_db_handle(context),
SQLITE_LIMIT_LENGTH, -1);
char *cBuf;
ubyte *bBuf;
assert(na==1);
switch( sqlite3_value_type(av[0]) ){
case SQLITE_BLOB:
nb = nv;
nc = 4*(nv+2/3); /* quads needed */
nc += (nc+(B64_DARK_MAX-1))/B64_DARK_MAX + 1; /* LFs and a 0-terminator */
if( nvMax < nc ){
sqlite3_result_error(context, "blob expanded to base64 too big", -1);
return;
}
cBuf = sqlite3_malloc(nc);
if( !cBuf ) goto memFail;
bBuf = (ubyte*)sqlite3_value_blob(av[0]);
nc = (int)(toBase64(bBuf, nb, cBuf) - cBuf);
sqlite3_result_text(context, cBuf, nc, sqlite3_free);
break;
case SQLITE_TEXT:
nc = nv;
nb = 3*((nv+3)/4); /* may overestimate due to LF and padding */
if( nvMax < nb ){
sqlite3_result_error(context, "blob from base64 may be too big", -1);
return;
}else if( nb<1 ){
nb = 1;
}
bBuf = sqlite3_malloc(nb);
if( !bBuf ) goto memFail;
cBuf = (char *)sqlite3_value_text(av[0]);
nb = (int)(fromBase64(cBuf, nc, bBuf) - bBuf);
sqlite3_result_blob(context, bBuf, nb, sqlite3_free);
break;
default:
sqlite3_result_error(context, "base64 accepts only blob or text", -1);
return;
}
return;
memFail:
sqlite3_result_error(context, "base64 OOM", -1);
}
/*
** Establish linkage to running SQLite library.
*/
#ifndef SQLITE_SHELL_EXTFUNCS
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_base_init
#else
static int sqlite3_base64_init
#endif
(sqlite3 *db, char **pzErr, const sqlite3_api_routines *pApi){
SQLITE_EXTENSION_INIT2(pApi);
(void)pzErr;
return sqlite3_create_function
(db, "base64", 1,
SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS|SQLITE_DIRECTONLY|SQLITE_UTF8,
0, base64, 0, 0);
}
/*
** Define some macros to allow this extension to be built into the shell
** conveniently, in conjunction with use of SQLITE_SHELL_EXTFUNCS. This
** allows shell.c, as distributed, to have this extension built in.
*/
#define BASE64_INIT(db) sqlite3_base64_init(db, 0, 0)
#define BASE64_EXPOSE(db, pzErr) /* Not needed, ..._init() does this. */
+437
View File
@@ -0,0 +1,437 @@
/*
** 2022-11-16
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
**
** This is a utility for converting binary to base85 or vice-versa.
** It can be built as a standalone program or an SQLite3 extension.
**
** Much like base64 representations, base85 can be sent through a
** sane USASCII channel unmolested. It also plays nicely in CSV or
** written as TCL brace-enclosed literals or SQL string literals.
** It is not suited for unmodified use in XML-like documents.
**
** The encoding used resembles Ascii85, but was devised by the author
** (Larry Brasfield) before Mozilla, Adobe, ZMODEM or other Ascii85
** variant sources existed, in the 1984 timeframe on a VAX mainframe.
** Further, this is an independent implementation of a base85 system.
** Hence, the author has rightfully put this into the public domain.
**
** Base85 numerals are taken from the set of 7-bit USASCII codes,
** excluding control characters and Space ! " ' ( ) { | } ~ Del
** in code order representing digit values 0 to 84 (base 10.)
**
** Groups of 4 bytes, interpreted as big-endian 32-bit values,
** are represented as 5-digit base85 numbers with MS to LS digit
** order. Groups of 1-3 bytes are represented with 2-4 digits,
** still big-endian but 8-24 bit values. (Using big-endian yields
** the simplest transition to byte groups smaller than 4 bytes.
** These byte groups can also be considered base-256 numbers.)
** Groups of 0 bytes are represented with 0 digits and vice-versa.
** No pad characters are used; Encoded base85 numeral sequence
** (aka "group") length maps 1-to-1 to the decoded binary length.
**
** Any character not in the base85 numeral set delimits groups.
** When base85 is streamed or stored in containers of indefinite
** size, newline is used to separate it into sub-sequences of no
** more than 80 digits so that fgets() can be used to read it.
**
** Length limitations are not imposed except that the runtime
** SQLite string or blob length limits are respected. Otherwise,
** any length binary sequence can be represented and recovered.
** Base85 sequences can be concatenated by separating them with
** a non-base85 character; the conversion to binary will then
** be the concatenation of the represented binary sequences.
** The standalone program either converts base85 on stdin to create
** a binary file or converts a binary file to base85 on stdout.
** Read or make it blurt its help for invocation details.
**
** The SQLite3 extension creates a function, base85(x), which will
** either convert text base85 to a blob or a blob to text base85
** and return the result (or throw an error for other types.)
** Unless built with OMIT_BASE85_CHECKER defined, it also creates a
** function, is_base85(t), which returns 1 iff the text t contains
** nothing other than base85 numerals and whitespace, or 0 otherwise.
**
** To build the extension:
** Set shell variable SQDIR=<your favorite SQLite checkout directory>
** and variable OPTS to -DOMIT_BASE85_CHECKER if is_base85() unwanted.
** *Nix: gcc -O2 -shared -I$SQDIR $OPTS -fPIC -o base85.so base85.c
** OSX: gcc -O2 -dynamiclib -fPIC -I$SQDIR $OPTS -o base85.dylib base85.c
** Win32: gcc -O2 -shared -I%SQDIR% %OPTS% -o base85.dll base85.c
** Win32: cl /Os -I%SQDIR% %OPTS% base85.c -link -dll -out:base85.dll
**
** To build the standalone program, define PP symbol BASE85_STANDALONE. Eg.
** *Nix or OSX: gcc -O2 -DBASE85_STANDALONE base85.c -o base85
** Win32: gcc -O2 -DBASE85_STANDALONE -o base85.exe base85.c
** Win32: cl /Os /MD -DBASE85_STANDALONE base85.c
*/
#include <stdio.h>
#include <memory.h>
#include <string.h>
#include <assert.h>
#ifndef OMIT_BASE85_CHECKER
# include <ctype.h>
#endif
#ifndef BASE85_STANDALONE
#ifndef SQLITE_SHELL_EXTFUNCS /* Guard for #include as built-in extension. */
# include "sqlite3ext.h"
#endif
SQLITE_EXTENSION_INIT1;
#else
# ifdef _WIN32
# include <io.h>
# include <fcntl.h>
# else
# define setmode(fd,m)
# endif
static char *zHelp =
"Usage: base85 <dirFlag> <binFile>\n"
" <dirFlag> is either -r to read or -w to write <binFile>,\n"
" content to be converted to/from base85 on stdout/stdin.\n"
" <binFile> names a binary file to be rendered or created.\n"
" Or, the name '-' refers to the stdin or stdout stream.\n"
;
static void sayHelp(){
printf("%s", zHelp);
}
#endif
#ifndef UBYTE_TYPEDEF
typedef unsigned char ubyte;
# define UBYTE_TYPEDEF
#endif
/* Classify c according to interval within USASCII set w.r.t. base85
* Values of 1 and 3 are base85 numerals. Values of 0, 2, or 4 are not.
*/
#define B85_CLASS( c ) (((c)>='#')+((c)>'&')+((c)>='*')+((c)>'z'))
/* Provide digitValue to b85Numeral offset as a function of above class. */
static ubyte b85_cOffset[] = { 0, '#', 0, '*'-4, 0 };
#define B85_DNOS( c ) b85_cOffset[B85_CLASS(c)]
/* Say whether c is a base85 numeral. */
#define IS_B85( c ) (B85_CLASS(c) & 1)
#if 0 /* Not used, */
static ubyte base85DigitValue( char c ){
ubyte dv = (ubyte)(c - '#');
if( dv>87 ) return 0xff;
return (dv > 3)? dv-3 : dv;
}
#endif
/* Width of base64 lines. Should be an integer multiple of 5. */
#define B85_DARK_MAX 80
static char * skipNonB85( char *s ){
char c;
while( (c = *s) && !IS_B85(c) ) ++s;
return s;
}
/* Convert small integer, known to be in 0..84 inclusive, to base85 numeral.
* Do not use the macro form with argument expression having a side-effect.*/
#if 0
static char base85Numeral( ubyte b ){
return (b < 4)? (char)(b + '#') : (char)(b - 4 + '*');
}
#else
# define base85Numeral( dn )\
((char)(((dn) < 4)? (char)((dn) + '#') : (char)((dn) - 4 + '*')))
#endif
static char *putcs(char *pc, char *s){
char c;
while( (c = *s++)!=0 ) *pc++ = c;
return pc;
}
/* Encode a byte buffer into base85 text. If pSep!=0, it's a C string
** to be appended to encoded groups to limit their length to B85_DARK_MAX
** or to terminate the last group (to aid concatenation.)
*/
static char* toBase85( ubyte *pIn, int nbIn, char *pOut, char *pSep ){
int nCol = 0;
while( nbIn >= 4 ){
int nco = 5;
unsigned long qbv = (pIn[0]<<24)|(pIn[1]<<16)|(pIn[2]<<8)|pIn[3];
while( nco > 0 ){
unsigned nqv = (unsigned)(qbv/85UL);
unsigned char dv = qbv - 85UL*nqv;
qbv = nqv;
pOut[--nco] = base85Numeral(dv);
}
nbIn -= 4;
pIn += 4;
pOut += 5;
if( pSep && (nCol += 5)>=B85_DARK_MAX ){
pOut = putcs(pOut, pSep);
nCol = 0;
}
}
if( nbIn > 0 ){
int nco = nbIn + 1;
unsigned long qv = *pIn++;
int nbe = 1;
while( nbe++ < nbIn ){
qv = (qv<<8) | *pIn++;
}
nCol += nco;
while( nco > 0 ){
ubyte dv = (ubyte)(qv % 85);
qv /= 85;
pOut[--nco] = base85Numeral(dv);
}
pOut += (nbIn+1);
}
if( pSep && nCol>0 ) pOut = putcs(pOut, pSep);
*pOut = 0;
return pOut;
}
/* Decode base85 text into a byte buffer. */
static ubyte* fromBase85( char *pIn, int ncIn, ubyte *pOut ){
if( ncIn>0 && pIn[ncIn-1]=='\n' ) --ncIn;
while( ncIn>0 ){
static signed char nboi[] = { 0, 0, 1, 2, 3, 4 };
char *pUse = skipNonB85(pIn);
unsigned long qv = 0L;
int nti, nbo;
ncIn -= (pUse - pIn);
pIn = pUse;
nti = (ncIn>5)? 5 : ncIn;
nbo = nboi[nti];
if( nbo==0 ) break;
while( nti>0 ){
char c = *pIn++;
ubyte cdo = B85_DNOS(c);
--ncIn;
if( cdo==0 ) break;
qv = 85 * qv + (c - cdo);
--nti;
}
nbo -= nti; /* Adjust for early (non-digit) end of group. */
switch( nbo ){
case 4:
*pOut++ = (qv >> 24)&0xff;
case 3:
*pOut++ = (qv >> 16)&0xff;
case 2:
*pOut++ = (qv >> 8)&0xff;
case 1:
*pOut++ = qv&0xff;
case 0:
break;
}
}
return pOut;
}
#ifndef OMIT_BASE85_CHECKER
/* Say whether input char sequence is all (base85 and/or whitespace).*/
static int allBase85( char *p, int len ){
char c;
while( len-- > 0 && (c = *p++) != 0 ){
if( !IS_B85(c) && !isspace(c) ) return 0;
}
return 1;
}
#endif
#ifndef BASE85_STANDALONE
# ifndef OMIT_BASE85_CHECKER
/* This function does the work for the SQLite is_base85(t) UDF. */
static void is_base85(sqlite3_context *context, int na, sqlite3_value *av[]){
assert(na==1);
switch( sqlite3_value_type(av[0]) ){
case SQLITE_TEXT:
{
int rv = allBase85( (char *)sqlite3_value_text(av[0]),
sqlite3_value_bytes(av[0]) );
sqlite3_result_int(context, rv);
}
break;
case SQLITE_NULL:
sqlite3_result_null(context);
break;
default:
sqlite3_result_error(context, "is_base85 accepts only text or NULL", -1);
return;
}
}
# endif
/* This function does the work for the SQLite base85(x) UDF. */
static void base85(sqlite3_context *context, int na, sqlite3_value *av[]){
int nb, nc, nv = sqlite3_value_bytes(av[0]);
int nvMax = sqlite3_limit(sqlite3_context_db_handle(context),
SQLITE_LIMIT_LENGTH, -1);
char *cBuf;
ubyte *bBuf;
assert(na==1);
switch( sqlite3_value_type(av[0]) ){
case SQLITE_BLOB:
nb = nv;
/* ulongs tail newlines tailenc+nul*/
nc = 5*(nv/4) + nv%4 + nv/64+1 + 2;
if( nvMax < nc ){
sqlite3_result_error(context, "blob expanded to base85 too big", -1);
return;
}
cBuf = sqlite3_malloc(nc);
if( !cBuf ) goto memFail;
bBuf = (ubyte*)sqlite3_value_blob(av[0]);
nc = (int)(toBase85(bBuf, nb, cBuf, "\n") - cBuf);
sqlite3_result_text(context, cBuf, nc, sqlite3_free);
break;
case SQLITE_TEXT:
nc = nv;
nb = 4*(nv/5) + nv%5; /* may overestimate */
if( nvMax < nb ){
sqlite3_result_error(context, "blob from base85 may be too big", -1);
return;
}else if( nb<1 ){
nb = 1;
}
bBuf = sqlite3_malloc(nb);
if( !bBuf ) goto memFail;
cBuf = (char *)sqlite3_value_text(av[0]);
nb = (int)(fromBase85(cBuf, nc, bBuf) - bBuf);
sqlite3_result_blob(context, bBuf, nb, sqlite3_free);
break;
default:
sqlite3_result_error(context, "base85 accepts only blob or text.", -1);
return;
}
return;
memFail:
sqlite3_result_error(context, "base85 OOM", -1);
}
/*
** Establish linkage to running SQLite library.
*/
#ifndef SQLITE_SHELL_EXTFUNCS
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_base_init
#else
static int sqlite3_base85_init
#endif
(sqlite3 *db, char **pzErr, const sqlite3_api_routines *pApi){
SQLITE_EXTENSION_INIT2(pApi);
(void)pzErr;
# ifndef OMIT_BASE85_CHECKER
{
int rc = sqlite3_create_function
(db, "is_base85", 1,
SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS|SQLITE_UTF8,
0, is_base85, 0, 0);
if( rc!=SQLITE_OK ) return rc;
}
# endif
return sqlite3_create_function
(db, "base85", 1,
SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS|SQLITE_DIRECTONLY|SQLITE_UTF8,
0, base85, 0, 0);
}
/*
** Define some macros to allow this extension to be built into the shell
** conveniently, in conjunction with use of SQLITE_SHELL_EXTFUNCS. This
** allows shell.c, as distributed, to have this extension built in.
*/
# define BASE85_INIT(db) sqlite3_base85_init(db, 0, 0)
# define BASE85_EXPOSE(db, pzErr) /* Not needed, ..._init() does this. */
#else /* standalone program */
int main(int na, char *av[]){
int cin;
int rc = 0;
ubyte bBuf[4*(B85_DARK_MAX/5)];
char cBuf[5*(sizeof(bBuf)/4)+2];
size_t nio;
# ifndef OMIT_BASE85_CHECKER
int b85Clean = 1;
# endif
char rw;
FILE *fb = 0, *foc = 0;
char fmode[3] = "xb";
if( na < 3 || av[1][0]!='-' || (rw = av[1][1])==0 || (rw!='r' && rw!='w') ){
sayHelp();
return 0;
}
fmode[0] = rw;
if( av[2][0]=='-' && av[2][1]==0 ){
switch( rw ){
case 'r':
fb = stdin;
setmode(fileno(stdin), O_BINARY);
break;
case 'w':
fb = stdout;
setmode(fileno(stdout), O_BINARY);
break;
}
}else{
fb = fopen(av[2], fmode);
foc = fb;
}
if( !fb ){
fprintf(stderr, "Cannot open %s for %c\n", av[2], rw);
rc = 1;
}else{
switch( rw ){
case 'r':
while( (nio = fread( bBuf, 1, sizeof(bBuf), fb))>0 ){
toBase85( bBuf, (int)nio, cBuf, 0 );
fprintf(stdout, "%s\n", cBuf);
}
break;
case 'w':
while( 0 != fgets(cBuf, sizeof(cBuf), stdin) ){
int nc = strlen(cBuf);
size_t nbo = fromBase85( cBuf, nc, bBuf ) - bBuf;
if( 1 != fwrite(bBuf, nbo, 1, fb) ) rc = 1;
# ifndef OMIT_BASE85_CHECKER
b85Clean &= allBase85( cBuf, nc );
# endif
}
break;
default:
sayHelp();
rc = 1;
}
if( foc ) fclose(foc);
}
# ifndef OMIT_BASE85_CHECKER
if( !b85Clean ){
fprintf(stderr, "Base85 input had non-base85 dark or control content.\n");
}
# endif
return rc;
}
#endif
+84
View File
@@ -0,0 +1,84 @@
/*
** 2022-11-20
**
** 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 source allows multiple SQLite extensions to be either: combined
** into a single runtime-loadable library; or built into the SQLite shell
** using a preprocessing convention set by src/shell.c.in (and shell.c).
**
** Presently, it combines the base64.c and base85.c extensions. However,
** it can be used as a template for other combinations.
**
** Example usages:
**
** - Build a runtime-loadable extension from SQLite checkout directory:
** *Nix, OSX: gcc -O2 -shared -I. -fPIC -o basexx.so ext/misc/basexx.c
** Win32: cl /Os -I. ext/misc/basexx.c -link -dll -out:basexx.dll
**
** - Incorporate as built-in in sqlite3 shell:
** *Nix, OSX with gcc on a like platform:
** export mop1=-DSQLITE_SHELL_EXTSRC=ext/misc/basexx.c
** export mop2=-DSQLITE_SHELL_EXTFUNCS=BASEXX
** make sqlite3 "OPTS=$mop1 $mop2"
** Win32 with Microsoft toolset on Windows:
** set mop1=-DSQLITE_SHELL_EXTSRC=ext/misc/basexx.c
** set mop2=-DSQLITE_SHELL_EXTFUNCS=BASEXX
** set mops="OPTS=%mop1% %mop2%"
** nmake -f Makefile.msc sqlite3.exe %mops%
*/
#ifndef SQLITE_SHELL_EXTFUNCS /* Guard for #include as built-in extension. */
# include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1;
#endif
static void init_api_ptr(const sqlite3_api_routines *pApi){
SQLITE_EXTENSION_INIT2(pApi);
}
#undef SQLITE_EXTENSION_INIT1
#define SQLITE_EXTENSION_INIT1 /* */
#undef SQLITE_EXTENSION_INIT2
#define SQLITE_EXTENSION_INIT2(v) (void)v
/* These next 2 undef's are only needed because the entry point names
* collide when formulated per the rules stated for loadable extension
* entry point names that will be deduced from the file basenames.
*/
#undef sqlite3_base_init
#define sqlite3_base_init sqlite3_base64_init
#include "base64.c"
#undef sqlite3_base_init
#define sqlite3_base_init sqlite3_base85_init
#include "base85.c"
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_basexx_init(sqlite3 *db, char **pzErr,
const sqlite3_api_routines *pApi){
init_api_ptr(pApi);
int rc1 = BASE64_INIT(db);
int rc2 = BASE85_INIT(db);
int rc = SQLITE_OK;
if( rc1==SQLITE_OK && rc2==SQLITE_OK ){
BASE64_EXPOSE(db, pzErr);
BASE64_EXPOSE(db, pzErr);
return SQLITE_OK;
}else{
return SQLITE_ERROR;
}
}
# define BASEXX_INIT(db) sqlite3_basexx_init(db, 0, 0)
# define BASEXX_EXPOSE(db, pzErr) /* Not needed, ..._init() does this. */
+4 -4
View File
@@ -185,7 +185,7 @@ static unsigned re_next_char(ReInput *p){
c = (c&0x0f)<<12 | ((p->z[p->i]&0x3f)<<6) | (p->z[p->i+1]&0x3f);
p->i += 2;
if( c<=0x7ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd;
}else if( (c&0xf8)==0xf0 && p->i+3<p->mx && (p->z[p->i]&0xc0)==0x80
}else if( (c&0xf8)==0xf0 && p->i+2<p->mx && (p->z[p->i]&0xc0)==0x80
&& (p->z[p->i+1]&0xc0)==0x80 && (p->z[p->i+2]&0xc0)==0x80 ){
c = (c&0x07)<<18 | ((p->z[p->i]&0x3f)<<12) | ((p->z[p->i+1]&0x3f)<<6)
| (p->z[p->i+2]&0x3f);
@@ -712,15 +712,15 @@ static const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){
** one or more matching characters, enter those matching characters into
** zInit[]. The re_match() routine can then search ahead in the input
** string looking for the initial match without having to run the whole
** regex engine over the string. Do not worry able trying to match
** regex engine over the string. Do not worry about trying to match
** unicode characters beyond plane 0 - those are very rare and this is
** just an optimization. */
if( pRe->aOp[0]==RE_OP_ANYSTAR && !noCase ){
for(j=0, i=1; j<(int)sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){
unsigned x = pRe->aArg[i];
if( x<=127 ){
if( x<=0x7f ){
pRe->zInit[j++] = (unsigned char)x;
}else if( x<=0xfff ){
}else if( x<=0x7ff ){
pRe->zInit[j++] = (unsigned char)(0xc0 | (x>>6));
pRe->zInit[j++] = 0x80 | (x&0x3f);
}else if( x<=0xffff ){
+4 -4
View File
@@ -531,7 +531,7 @@ static void sha3Func(
/* Compute a string using sqlite3_vsnprintf() with a maximum length
** of 50 bytes and add it to the hash.
*/
static void hash_step_vformat(
static void sha3_step_vformat(
SHA3Context *p, /* Add content to this context */
const char *zFormat,
...
@@ -627,7 +627,7 @@ static void sha3QueryFunc(
z = sqlite3_sql(pStmt);
if( z ){
n = (int)strlen(z);
hash_step_vformat(&cx,"S%d:",n);
sha3_step_vformat(&cx,"S%d:",n);
SHA3Update(&cx,(unsigned char*)z,n);
}
@@ -671,14 +671,14 @@ static void sha3QueryFunc(
case SQLITE_TEXT: {
int n2 = sqlite3_column_bytes(pStmt, i);
const unsigned char *z2 = sqlite3_column_text(pStmt, i);
hash_step_vformat(&cx,"T%d:",n2);
sha3_step_vformat(&cx,"T%d:",n2);
SHA3Update(&cx, z2, n2);
break;
}
case SQLITE_BLOB: {
int n2 = sqlite3_column_bytes(pStmt, i);
const unsigned char *z2 = sqlite3_column_blob(pStmt, i);
hash_step_vformat(&cx,"B%d:",n2);
sha3_step_vformat(&cx,"B%d:",n2);
SHA3Update(&cx, z2, n2);
break;
}
+54
View File
@@ -0,0 +1,54 @@
# 2022 November 07
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
source [file join [file dirname [info script]] rbu_common.tcl]
set ::testprefix rburename
do_execsql_test 1.0 {
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 2);
INSERT INTO t1 VALUES(3, 4);
INSERT INTO t1 VALUES(5, 6);
}
forcedelete test.db-vacuum
proc my_rename {old new} {
lappend ::my_rename_calls [list [file tail $old] [file tail $new]]
file rename $old $new
}
do_test 1.1 {
sqlite3rbu_vacuum rbu test.db
rbu rename_handler my_rename
while {[rbu step]=="SQLITE_OK"} {}
rbu close
} SQLITE_DONE
do_test 1.2 {
set ::my_rename_calls
} {{test.db-oal test.db-wal}}
proc my_rename {old new} {
error "something went wrong"
}
do_test 1.3 {
sqlite3rbu_vacuum rbu test.db
rbu rename_handler my_rename
while {[rbu step]=="SQLITE_OK"} {}
list [catch { rbu close } msg] $msg
} {1 SQLITE_IOERR}
finish_test
+5 -4
View File
@@ -227,10 +227,11 @@ do_test 6.1 {
rbu close
} {SQLITE_OK}
do_execsql_test 6.2 {
SELECT 1 FROM sqlite_master LIMIT 1;
PRAGMA wal_checkpoint;
} {1 0 4 4}
do_test 6.2 {
execsql { SELECT 1 FROM sqlite_master LIMIT 1 }
execsql { PRAGMA wal_checkpoint }
execsql { SELECT 1 FROM sqlite_master LIMIT 1 }
} {1}
do_test 6.3 {
sqlite3rbu_vacuum rbu test.db test.db2
+53 -27
View File
@@ -393,6 +393,8 @@ struct sqlite3rbu {
int nPagePerSector; /* Pages per sector for pTargetFd */
i64 iOalSz;
i64 nPhaseOneStep;
void *pRenameArg;
int (*xRename)(void*, const char*, const char*);
/* The following state variables are used as part of the incremental
** checkpoint stage (eStage==RBU_STAGE_CKPT). See comments surrounding
@@ -2781,7 +2783,7 @@ static void rbuOpenDatabase(sqlite3rbu *p, sqlite3 *dbMain, int *pbRetry){
sqlite3_file_control(p->dbRbu, "main", SQLITE_FCNTL_RBUCNT, (void*)p);
if( p->zState==0 ){
const char *zFile = sqlite3_db_filename(p->dbRbu, "main");
p->zState = rbuMPrintf(p, "file://%s-vacuum?modeof=%s", zFile, zFile);
p->zState = rbuMPrintf(p, "file:///%s-vacuum?modeof=%s", zFile, zFile);
}
}
@@ -3241,32 +3243,7 @@ static void rbuMoveOalFile(sqlite3rbu *p){
}
if( p->rc==SQLITE_OK ){
#if defined(_WIN32_WCE)
{
LPWSTR zWideOal;
LPWSTR zWideWal;
zWideOal = rbuWinUtf8ToUnicode(zOal);
if( zWideOal ){
zWideWal = rbuWinUtf8ToUnicode(zWal);
if( zWideWal ){
if( MoveFileW(zWideOal, zWideWal) ){
p->rc = SQLITE_OK;
}else{
p->rc = SQLITE_IOERR;
}
sqlite3_free(zWideWal);
}else{
p->rc = SQLITE_IOERR_NOMEM;
}
sqlite3_free(zWideOal);
}else{
p->rc = SQLITE_IOERR_NOMEM;
}
}
#else
p->rc = rename(zOal, zWal) ? SQLITE_IOERR : SQLITE_OK;
#endif
p->rc = p->xRename(p->pRenameArg, zOal, zWal);
}
if( p->rc!=SQLITE_OK
@@ -4005,6 +3982,7 @@ static sqlite3rbu *openRbuHandle(
/* Create the custom VFS. */
memset(p, 0, sizeof(sqlite3rbu));
sqlite3rbu_rename_handler(p, 0, 0);
rbuCreateVfs(p);
/* Open the target, RBU and state databases */
@@ -4396,6 +4374,54 @@ int sqlite3rbu_savestate(sqlite3rbu *p){
return rc;
}
/*
** Default xRename callback for RBU.
*/
static int xDefaultRename(void *pArg, const char *zOld, const char *zNew){
int rc = SQLITE_OK;
#if defined(_WIN32_WCE)
{
LPWSTR zWideOld;
LPWSTR zWideNew;
zWideOld = rbuWinUtf8ToUnicode(zOld);
if( zWideOld ){
zWideNew = rbuWinUtf8ToUnicode(zNew);
if( zWideNew ){
if( MoveFileW(zWideOld, zWideNew) ){
rc = SQLITE_OK;
}else{
rc = SQLITE_IOERR;
}
sqlite3_free(zWideNew);
}else{
rc = SQLITE_IOERR_NOMEM;
}
sqlite3_free(zWideOld);
}else{
rc = SQLITE_IOERR_NOMEM;
}
}
#else
rc = rename(zOld, zNew) ? SQLITE_IOERR : SQLITE_OK;
#endif
return rc;
}
void sqlite3rbu_rename_handler(
sqlite3rbu *pRbu,
void *pArg,
int (*xRename)(void *pArg, const char *zOld, const char *zNew)
){
if( xRename ){
pRbu->xRename = xRename;
pRbu->pRenameArg = pArg;
}else{
pRbu->xRename = xDefaultRename;
pRbu->pRenameArg = 0;
}
}
/**************************************************************************
** Beginning of RBU VFS shim methods. The VFS shim modifies the behaviour
** of a standard VFS in the following ways:
+28
View File
@@ -544,6 +544,34 @@ SQLITE_API void sqlite3rbu_bp_progress(sqlite3rbu *pRbu, int *pnOne, int*pnTwo);
SQLITE_API int sqlite3rbu_state(sqlite3rbu *pRbu);
/*
** As part of applying an RBU update or performing an RBU vacuum operation,
** the system must at one point move the *-oal file to the equivalent *-wal
** path. Normally, it does this by invoking POSIX function rename(2) directly.
** Except on WINCE platforms, where it uses win32 API MoveFileW(). This
** function may be used to register a callback that the RBU module will invoke
** instead of one of these APIs.
**
** If a callback is registered with an RBU handle, it invokes it instead
** of rename(2) when it needs to move a file within the file-system. The
** first argument passed to the xRename() callback is a copy of the second
** argument (pArg) passed to this function. The second is the full path
** to the file to move and the third the full path to which it should be
** moved. The callback function should return SQLITE_OK to indicate
** success. If an error occurs, it should return an SQLite error code.
** In this case the RBU operation will be abandoned and the error returned
** to the RBU user.
**
** Passing a NULL pointer in place of the xRename argument to this function
** restores the default behaviour.
*/
SQLITE_API void sqlite3rbu_rename_handler(
sqlite3rbu *pRbu,
void *pArg,
int (*xRename)(void *pArg, const char *zOld, const char *zNew)
);
/*
** Create an RBU VFS named zName that accesses the underlying file-system
** via existing VFS zParent. Or, if the zParent parameter is passed NULL,
+54 -3
View File
@@ -26,6 +26,14 @@
# endif
#endif
#include <assert.h>
#include <string.h>
typedef struct TestRbu TestRbu;
struct TestRbu {
sqlite3rbu *pRbu;
Tcl_Interp *interp;
Tcl_Obj *xRename;
};
/* From main.c */
extern const char *sqlite3ErrName(int);
@@ -55,6 +63,20 @@ void test_rbu_delta(sqlite3_context *pCtx, int nArg, sqlite3_value **apVal){
Tcl_DecrRefCount(pScript);
}
static int xRenameCallback(void *pArg, const char *zOld, const char *zNew){
int rc = SQLITE_OK;
TestRbu *pTest = (TestRbu*)pArg;
Tcl_Obj *pEval = Tcl_DuplicateObj(pTest->xRename);
Tcl_IncrRefCount(pEval);
Tcl_ListObjAppendElement(pTest->interp, pEval, Tcl_NewStringObj(zOld, -1));
Tcl_ListObjAppendElement(pTest->interp, pEval, Tcl_NewStringObj(zNew, -1));
rc = Tcl_EvalObjEx(pTest->interp, pEval, TCL_GLOBAL_ONLY);
Tcl_DecrRefCount(pEval);
return rc ? SQLITE_IOERR : SQLITE_OK;
}
static int SQLITE_TCLAPI test_sqlite3rbu_cmd(
ClientData clientData,
@@ -63,7 +85,8 @@ static int SQLITE_TCLAPI test_sqlite3rbu_cmd(
Tcl_Obj *CONST objv[]
){
int ret = TCL_OK;
sqlite3rbu *pRbu = (sqlite3rbu*)clientData;
TestRbu *pTest = (TestRbu*)clientData;
sqlite3rbu *pRbu = pTest->pRbu;
struct RbuCmd {
const char *zName;
int nArg;
@@ -82,6 +105,7 @@ static int SQLITE_TCLAPI test_sqlite3rbu_cmd(
{"temp_size_limit", 3, "LIMIT"}, /* 10 */
{"temp_size", 2, ""}, /* 11 */
{"dbRbu_eval", 3, "SQL"}, /* 12 */
{"rename_handler", 3, "SCRIPT"},/* 13 */
{0,0,0}
};
int iCmd;
@@ -127,6 +151,8 @@ static int SQLITE_TCLAPI test_sqlite3rbu_cmd(
}
ret = TCL_ERROR;
}
if( pTest->xRename ) Tcl_DecrRefCount(pTest->xRename);
ckfree(pTest);
break;
}
@@ -214,6 +240,19 @@ static int SQLITE_TCLAPI test_sqlite3rbu_cmd(
break;
}
case 13: /* rename_handler */ {
Tcl_Obj *pScript = objv[2];
assert( !sqlite3_stricmp(aCmd[13].zName, "rename_handler") );
if( Tcl_GetCharLength(pScript)==0 ){
sqlite3rbu_rename_handler(pRbu, 0, 0);
}else{
pTest->xRename = Tcl_DuplicateObj(pScript);
Tcl_IncrRefCount(pTest->xRename);
sqlite3rbu_rename_handler(pRbu, pTest, xRenameCallback);
}
break;
}
default: /* seems unlikely */
assert( !"cannot happen" );
break;
@@ -222,6 +261,18 @@ static int SQLITE_TCLAPI test_sqlite3rbu_cmd(
return ret;
}
static void createRbuWrapper(
Tcl_Interp *interp,
const char *zCmd,
sqlite3rbu *pRbu
){
TestRbu *pTest = (TestRbu*)ckalloc(sizeof(TestRbu));
memset(pTest, 0, sizeof(TestRbu));
pTest->pRbu = pRbu;
pTest->interp = interp;
Tcl_CreateObjCommand(interp, zCmd, test_sqlite3rbu_cmd, (ClientData)pTest, 0);
}
/*
** Tclcmd: sqlite3rbu CMD <target-db> <rbu-db> ?<state-db>?
*/
@@ -247,7 +298,7 @@ static int SQLITE_TCLAPI test_sqlite3rbu(
if( objc==5 ) zStateDb = Tcl_GetString(objv[4]);
pRbu = sqlite3rbu_open(zTarget, zRbu, zStateDb);
Tcl_CreateObjCommand(interp, zCmd, test_sqlite3rbu_cmd, (ClientData)pRbu, 0);
createRbuWrapper(interp, zCmd, pRbu);
Tcl_SetObjResult(interp, objv[1]);
return TCL_OK;
}
@@ -276,7 +327,7 @@ static int SQLITE_TCLAPI test_sqlite3rbu_vacuum(
if( zStateDb && zStateDb[0]=='\0' ) zStateDb = 0;
pRbu = sqlite3rbu_vacuum(zTarget, zStateDb);
Tcl_CreateObjCommand(interp, zCmd, test_sqlite3rbu_cmd, (ClientData)pRbu, 0);
createRbuWrapper(interp, zCmd, pRbu);
Tcl_SetObjResult(interp, objv[1]);
return TCL_OK;
}
+7 -2
View File
@@ -71,6 +71,7 @@
** It contains one entry for each b-tree pointer between a parent and
** child page in the database.
*/
#if !defined(SQLITEINT_H)
#include "sqlite3ext.h"
@@ -82,6 +83,8 @@ SQLITE_EXTENSION_INIT1
#include <string.h>
#include <assert.h>
#ifndef SQLITE_OMIT_VIRTUALTABLE
#define DBDATA_PADDING_BYTES 100
typedef struct DbdataTable DbdataTable;
@@ -425,7 +428,7 @@ static void dbdataValue(
u32 enc,
int eType,
u8 *pData,
int nData
sqlite3_int64 nData
){
if( eType>=0 && dbdataValueBytes(eType)<=nData ){
switch( eType ){
@@ -861,7 +864,7 @@ static int dbdataColumn(
case DBDATA_COLUMN_VALUE: {
if( pCsr->iField<0 ){
sqlite3_result_int64(ctx, pCsr->iIntkey);
}else{
}else if( &pCsr->pRec[pCsr->nRec] >= pCsr->pPtr ){
sqlite3_int64 iType;
dbdataGetVarintU32(pCsr->pHdrPtr, &iType);
dbdataValue(
@@ -935,3 +938,5 @@ int sqlite3_dbdata_init(
SQLITE_EXTENSION_INIT2(pApi);
return sqlite3DbdataRegister(db);
}
#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
+4 -9
View File
@@ -10,16 +10,9 @@
#***********************************************************************
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source [file join [file dirname [info script]] recover_common.tcl]
source $testdir/tester.tcl
set testprefix recover1
proc compare_result {db1 db2 sql} {
set r1 [$db1 eval $sql]
set r2 [$db2 eval $sql]
@@ -271,11 +264,13 @@ do_recover_test 14
#-------------------------------------------------------------------------
reset_db
do_execsql_test 15.1 {
execsql {
PRAGMA journal_mode=OFF;
PRAGMA mmap_size=10;
}
do_execsql_test 15.1 {
CREATE TABLE t1(x);
} {off 10}
} {}
do_recover_test 15
finish_test
+11 -2
View File
@@ -1,5 +1,14 @@
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source $testdir/tester.tcl
if {[info commands sqlite3_recover_init]==""} {
finish_test
return -code return
}
-8
View File
@@ -12,17 +12,9 @@
# Tests for the SQLITE_RECOVER_ROWIDS option.
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source [file join [file dirname [info script]] recover_common.tcl]
source $testdir/tester.tcl
set testprefix recoverclobber
ifcapable !vtab {
finish_test; return
}
proc recover {db output} {
set R [sqlite3_recover_init db main test.db2]
$R run
-5
View File
@@ -10,12 +10,7 @@
#***********************************************************************
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source [file join [file dirname [info script]] recover_common.tcl]
source $testdir/tester.tcl
set testprefix recovercorrupt
database_may_be_corrupt
-5
View File
@@ -10,12 +10,7 @@
#***********************************************************************
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source [file join [file dirname [info script]] recover_common.tcl]
source $testdir/tester.tcl
set testprefix recovercorrupt2
do_execsql_test 1.0 {
-5
View File
@@ -10,12 +10,7 @@
#***********************************************************************
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source [file join [file dirname [info script]] recover_common.tcl]
source $testdir/tester.tcl
set testprefix recoverfault
-5
View File
@@ -10,12 +10,7 @@
#***********************************************************************
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source [file join [file dirname [info script]] recover_common.tcl]
source $testdir/tester.tcl
set testprefix recoverfault2
+16 -20
View File
@@ -11,17 +11,9 @@
#
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source [file join [file dirname [info script]] recover_common.tcl]
source $testdir/tester.tcl
set testprefix recoverold
ifcapable !vtab {
finish_test; return
}
proc compare_result {db1 db2 sql} {
set r1 [$db1 eval $sql]
set r2 [$db2 eval $sql]
@@ -71,7 +63,10 @@ proc do_recover_test {tn {tsql {}} {res {}}} {
sqlite3 db2 test.db2
db2 eval [join $::sqlhook ";"]
db cache flush
if {$tsql==""} {
compare_dbs db db2
uplevel [list do_test $tn.sql [list compare_dbs db db2] {}]
} else {
uplevel [list do_execsql_test -db db2 $tn.sql $tsql $res]
@@ -155,7 +150,6 @@ do_recover_test 2.4.1 {
2 2 3 {} 8 9 7
}
breakpoint
do_execsql_test 2.5 {
CREATE TABLE x1(a, b, c);
WITH s(i) AS (
@@ -173,17 +167,19 @@ do_recover_test 2.5.1 {
2 2 3 {} 8 9 7
}
do_test 2.6 {
forcedelete test.db2
set R [sqlite3_recover_init db main test.db2]
$R config lostandfound lost_and_found
$R config freelistcorrupt 1
$R run
$R finish
sqlite3 db2 test.db2
execsql { SELECT count(*) FROM lost_and_found_1; } db2
} {103}
db2 close
ifcapable !secure_delete {
do_test 2.6 {
forcedelete test.db2
set R [sqlite3_recover_init db main test.db2]
$R config lostandfound lost_and_found
$R config freelistcorrupt 1
$R run
$R finish
sqlite3 db2 test.db2
execsql { SELECT count(*) FROM lost_and_found_1; } db2
} {103}
db2 close
}
#-------------------------------------------------------------------------
breakpoint
+1 -4
View File
@@ -10,11 +10,7 @@
#***********************************************************************
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source [file join [file dirname [info script]] recover_common.tcl]
source $testdir/tester.tcl
db close
sqlite3_test_control_pending_byte 0x1000000
@@ -27,6 +23,7 @@ foreach {pgsz bOverflow} {
} {
reset_db
execsql "PRAGMA page_size = $pgsz"
execsql "PRAGMA auto_vacuum = 0"
do_execsql_test 1.$pgsz.$bOverflow.1 {
CREATE TABLE t1(a, b, c);
CREATE INDEX i1 ON t1(b, a, c);
-8
View File
@@ -12,17 +12,9 @@
# Tests for the SQLITE_RECOVER_ROWIDS option.
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source [file join [file dirname [info script]] recover_common.tcl]
source $testdir/tester.tcl
set testprefix recoverrowid
ifcapable !vtab {
finish_test; return
}
proc recover {db bRowids output} {
forcedelete $output
+1 -8
View File
@@ -12,18 +12,11 @@
# Tests for the SQLITE_RECOVER_SLOWINDEXES option.
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source [file join [file dirname [info script]] recover_common.tcl]
source $testdir/tester.tcl
set testprefix recoverslowidx
ifcapable !vtab {
finish_test; return
}
do_execsql_test 1.0 {
PRAGMA auto_vacuum = 0;
CREATE TABLE t1(a, b);
CREATE INDEX i1 ON t1(a);
INSERT INTO t1 VALUES(1, 1), (2, 2), (3, 3), (4, 4);
-8
View File
@@ -11,17 +11,9 @@
#
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source [file join [file dirname [info script]] recover_common.tcl]
source $testdir/tester.tcl
set testprefix recoversql
ifcapable !vtab {
finish_test; return
}
do_execsql_test 1.0 {
CREATE TABLE "x.1" (x, y);
INSERT INTO "x.1" VALUES(1, 1), (2, 2), (3, 3);
+17 -6
View File
@@ -17,6 +17,8 @@
#include <assert.h>
#include <string.h>
#ifndef SQLITE_OMIT_VIRTUALTABLE
/*
** Declaration for public API function in file dbdata.c. This may be called
** with NULL as the final two arguments to register the sqlite_dbptr and
@@ -280,10 +282,16 @@ static RecoverGlobal recover_g;
*/
#define RECOVER_ROWID_DEFAULT 1
/*
** Mutex handling:
**
** recoverEnterMutex() - Enter the recovery mutex
** recoverLeaveMutex() - Leave the recovery mutex
** recoverAssertMutexHeld() - Assert that the recovery mutex is held
*/
#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0
# define recoverEnterMutex()
# define recoverLeaveMutex()
# define recoverAssertMutexHeld()
#else
static void recoverEnterMutex(void){
sqlite3_mutex_enter(sqlite3_mutex_alloc(RECOVER_MUTEX_ID));
@@ -291,9 +299,13 @@ static void recoverEnterMutex(void){
static void recoverLeaveMutex(void){
sqlite3_mutex_leave(sqlite3_mutex_alloc(RECOVER_MUTEX_ID));
}
#endif
#if SQLITE_THREADSAFE+0>=1 && defined(SQLITE_DEBUG)
static void recoverAssertMutexHeld(void){
assert( sqlite3_mutex_held(sqlite3_mutex_alloc(RECOVER_MUTEX_ID)) );
}
#else
# define recoverAssertMutexHeld()
#endif
@@ -301,11 +313,8 @@ static void recoverAssertMutexHeld(void){
** Like strlen(). But handles NULL pointer arguments.
*/
static int recoverStrlen(const char *zStr){
int nRet = 0;
if( zStr ){
while( zStr[nRet] ) nRet++;
}
return nRet;
if( zStr==0 ) return 0;
return (int)(strlen(zStr)&0x7fffffff);
}
/*
@@ -2844,3 +2853,5 @@ int sqlite3_recover_finish(sqlite3_recover *p){
}
return rc;
}
#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
+5 -2
View File
@@ -18,6 +18,8 @@
#include <tcl.h>
#include <assert.h>
#ifndef SQLITE_OMIT_VIRTUALTABLE
typedef struct TestRecover TestRecover;
struct TestRecover {
sqlite3_recover *p;
@@ -284,9 +286,10 @@ static int test_sqlite3_dbdata_init(
return TCL_OK;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
int TestRecover_Init(Tcl_Interp *interp){
#ifndef SQLITE_OMIT_VIRTUALTABLE
struct Cmd {
const char *zCmd;
Tcl_ObjCmdProc *xProc;
@@ -302,7 +305,7 @@ int TestRecover_Init(Tcl_Interp *interp){
struct Cmd *p = &aCmd[i];
Tcl_CreateObjCommand(interp, p->zCmd, p->xProc, p->pArg, 0);
}
#endif
return TCL_OK;
}
+368 -155
View File
@@ -20,6 +20,14 @@
# above-listed o? target names.
#
# clean = clean up
#
# Required tools beyond those needed for the canonical builds:
#
# - Emscripten SDK: https://emscripten.org/docs/getting_started/downloads.html
# - The bash shell
# - GNU make, GNU sed, GNU awk, GNU grep (all in the $PATH)
# - wasm-strip for release builds: https://github.com/WebAssembly/wabt
# - InfoZip for 'dist' zip file
########################################################################
SHELL := $(shell which bash 2>/dev/null)
MAKEFILE := $(lastword $(MAKEFILE_LIST))
@@ -71,17 +79,19 @@ dir.jacc := jaccwabyt
dir.common := common
dir.fiddle := fiddle
dir.tool := $(dir.top)/tool
CLEAN_FILES += *~ $(dir.jacc)/*~ $(dir.api)/*~ $(dir.common)/*~ $(dir.fiddle)/*~
########################################################################
# dir.dout = output dir for deliverables.
#
# MAINTENANCE REMINDER: the output .js and .wasm files of emcc must be
# in _this_ dir, rather than a subdir, or else parts of the generated
# code get confused and cannot load property. Specifically, when X.js
# loads X.wasm, whether or not X.js uses the correct path for X.wasm
# depends on how it's loaded: an HTML script tag will resolve it
# intuitively, whereas a Worker's call to importScripts() will not.
# That's a fundamental incompatibility with how URL resolution in
# JS happens between those two contexts. See:
# MAINTENANCE REMINDER: the output .js and .wasm files of certain emcc
# buildables must be in _this_ dir, rather than a subdir, or else
# parts of the generated code get confused and cannot load
# property. Specifically, when X.js loads X.wasm, whether or not X.js
# uses the correct path for X.wasm depends on how it's loaded: an HTML
# script tag will resolve it intuitively, whereas a Worker's call to
# importScripts() will not. That's a fundamental incompatibility with
# how URL resolution in JS happens between those two contexts. See:
#
# https://zzz.buzz/2017/03/14/relative-uris-in-web-development/
#
@@ -96,7 +106,7 @@ dir.dout := $(dir.wasm)/jswasm
# dir.tmp = output dir for intermediary build files, as opposed to
# end-user deliverables.
dir.tmp := $(dir.wasm)/bld
#CLEAN_FILES += $(wildcard $(dir.dout)/*) $(wildcard $(dir.tmp)/*)
CLEAN_FILES += $(dir.tmp)/* $(dir.dout)/*
ifeq (,$(wildcard $(dir.dout)))
dir._tmp := $(shell mkdir -p $(dir.dout))
endif
@@ -104,13 +114,12 @@ ifeq (,$(wildcard $(dir.tmp)))
dir._tmp := $(shell mkdir -p $(dir.tmp))
endif
cflags.common := -I. -I.. -I$(dir.top)
CLEAN_FILES += *~ $(dir.jacc)/*~ $(dir.api)/*~ $(dir.common)/*~
emcc.WASM_BIGINT ?= 1
sqlite3.c := $(dir.top)/sqlite3.c
sqlite3.h := $(dir.top)/sqlite3.h
# Most SQLITE_OPT flags are set in sqlite3-wasm.c but we need them
# made explicit here for building speedtest1.c.
SQLITE_OPT = \
-DSQLITE_ENABLE_FTS4 \
-DSQLITE_ENABLE_FTS5 \
-DSQLITE_ENABLE_RTREE \
-DSQLITE_ENABLE_EXPLAIN_COMMENTS \
-DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION \
@@ -130,16 +139,100 @@ SQLITE_OPT = \
'-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' \
-DSQLITE_USE_URI=1 \
-DSQLITE_WASM_ENABLE_C_TESTS
# ^^^ most flags are set in sqlite3-wasm.c but we need them
# made explicit here for building speedtest1.c.
ifneq (,$(filter release,$(MAKECMDGOALS)))
emcc_opt ?= -Oz -flto
$(sqlite3.c) $(sqlite3.h):
$(MAKE) -C $(dir.top) sqlite3.c
.PHONY: clean distclean
clean:
-rm -f $(CLEAN_FILES)
distclean: clean
-rm -f $(DISTCLEAN_FILES)
ifeq (release,$(filter release,$(MAKECMDGOALS)))
ifeq (,$(wasm-strip))
$(error Cannot make release-quality binary because wasm-strip is not available. \
See notes in the warning above)
endif
else
emcc_opt ?= -O0
# ^^^^ build times for -O levels higher than 0 are painful at
# dev-time.
$(info Development build. Use '$(MAKE) release' for a smaller release build.)
endif
# bin.version-info = binary to output various sqlite3 version info for
# embedding in the JS files and in building the distribution zip file.
# It must NOT be in $(dir.tmp) because we need it to survive the
# cleanup process for the dist build to work properly.
bin.version-info := $(dir.wasm)/version-info
$(bin.version-info): $(dir.wasm)/version-info.c $(sqlite3.h) $(MAKEFILE)
$(CC) -O0 -I$(dir.top) -o $@ $<
DISTCLEAN_FILES += $(bin.version-info)
# bin.stripcomments is used for stripping C/C++-style comments from JS
# files. The JS files contain large chunks of documentation which we
# don't need for all builds. That app's -k flag is of particular
# importance here, as it allows us to retain the opening comment
# blocks, which contain the license header and version info.
bin.stripccomments := $(dir.tool)/stripccomments
$(bin.stripccomments): $(bin.stripccomments).c $(MAKEFILE)
$(CC) -o $@ $<
DISTCLEAN_FILES += $(bin.stripccomments)
########################################################################
# C-PP.FILTER: a $(call)able to transform $(1) to $(2) via ./c-pp -f
# $(1) ...
#
# Historical notes:
#
# - We first attempted to use gcc and/or clang to preprocess JS files
# in the same way we would normally do C files, but C-specific quirks
# of each makes that untennable.
#
# - We implemented c-pp.c (the C-Minus Pre-processor) as a custom
# generic/file-format-agnostic preprocessor to enable us to pack
# code for different target builds into the same JS files. Most
# notably, some ES6 module (a.k.a. ESM) features cannot legally be
# referenced at all in non-ESM code, e.g. the "import" and "export"
# keywords. This preprocessing step permits us to swap out sections
# of code where necessary for ESM and non-ESM (a.k.a. vanilla JS)
# require different implementations. The alternative to such
# preprocessing, would be to have separate source files for ES6
# builds, which would have a higher maintenance burden than c-pp.c
# seems likely to.
#
# c-pp.c was written specifically for the sqlite project's JavaScript
# builds but is maintained as a standalone project:
# https://fossil.wanderinghorse.net/r/c-pp
bin.c-pp := ./c-pp
$(bin.c-pp): c-pp.c $(sqlite3.c) $(MAKEFILE)
$(CC) -O0 -o $@ c-pp.c $(sqlite3.c) '-DCMPP_DEFAULT_DELIM="//#"' -I$(dir.top)
define C-PP.FILTER
# Create $2 from $1 using $(bin.c-pp)
# $1 = Input file: c-pp -f $(1).js
# $2 = Output file: c-pp -o $(2).js
# $3 = optional c-pp -D... flags
$(2): $(1) $$(MAKEFILE) $$(bin.c-pp)
$$(bin.c-pp) -f $(1) -o $$@ $(3)
CLEAN_FILES += $(2)
endef
c-pp.D.vanilla ?=
c-pp.D.esm ?= -Dtarget=es6-module
# /end C-PP.FILTER
########################################################################
# cflags.common = C compiler flags for all builds
cflags.common := -I. -I.. -I$(dir.top)
# emcc.WASM_BIGINT = 1 for BigInt (C int64) support, else 0. The API
# disables certain features if BigInt is not enabled and such builds
# _are not tested_ on any regular basis.
emcc.WASM_BIGINT ?= 1
# emcc_opt = optimization-related flags. These are primarily used by
# the various oX targets. build times for -O levels higher than 0 are
# painful at dev-time.
emcc_opt ?= -O0
# When passing emcc_opt from the CLI, += and re-assignment have no
# effect, so emcc_opt+=-g3 doesn't work. So...
emcc_opt_full := $(emcc_opt) -g3
@@ -165,47 +258,26 @@ emcc_opt_full := $(emcc_opt) -g3
# speed for the unit tests is all over the place either way so it's
# difficult to say whether -Os gives any speed benefit over -Oz.
#
# (Much later: -O2 consistently gives the best speeds.)
# Much practice has demonstrated that -O2 consistently gives the best
# runtime speeds, but not by a large enough factor to rule out use of
# -Oz when small deliverable size is a priority.
########################################################################
$(sqlite3.c) $(sqlite3.h):
$(MAKE) -C $(dir.top) sqlite3.c
.PHONY: clean distclean
clean:
-rm -f $(CLEAN_FILES)
distclean: clean
-rm -f $(DISTCLEAN_FILES)
ifeq (release,$(filter release,$(MAKECMDGOALS)))
ifeq (,$(wasm-strip))
$(error Cannot make release-quality binary because wasm-strip is not available. \
See notes in the warning above)
endif
else
$(info Development build. Use '$(MAKE) release' for a smaller release build.)
endif
bin.version-info := $(dir.wasm)/version-info
# ^^^^ NOT in $(dir.tmp) because we need it to survive the cleanup
# process for the dist build to work properly.
$(bin.version-info): $(dir.wasm)/version-info.c $(sqlite3.h) $(MAKEFILE)
$(CC) -O0 -I$(dir.top) -o $@ $<
DISTCLEAN_FILES += $(bin.version-info)
bin.stripccomments := $(dir.tool)/stripccomments
$(bin.stripccomments): $(bin.stripccomments).c $(MAKEFILE)
$(CC) -o $@ $<
DISTCLEAN_FILES += $(bin.stripccomments)
# EXPORTED_FUNCTIONS.* = files for use with Emscripten's
# -sEXPORTED_FUNCTION flag.
EXPORTED_FUNCTIONS.api.in := $(abspath $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-api)
EXPORTED_FUNCTIONS.api := $(dir.tmp)/EXPORTED_FUNCTIONS.api
$(EXPORTED_FUNCTIONS.api): $(EXPORTED_FUNCTIONS.api.in) $(MAKEFILE)
cat $(EXPORTED_FUNCTIONS.api.in) > $@
cp $(EXPORTED_FUNCTIONS.api.in) $@
# sqlite3-license-version.js = generated JS file with the license
# header and version info.
sqlite3-license-version.js := $(dir.tmp)/sqlite3-license-version.js
# sqlite3-license-version-header.js = JS file containing only the
# license header.
sqlite3-license-version-header.js := $(dir.api)/sqlite3-license-version-header.js
# sqlite3-api-build-version.js = generated JS file which populates the
# sqlite3.version object using $(bin.version-info).
sqlite3-api-build-version.js := $(dir.tmp)/sqlite3-api-build-version.js
# sqlite3-api.jses = the list of JS files which make up $(sqlite3-api.js), in
# the order they need to be assembled.
@@ -225,17 +297,22 @@ sqlite3-api.jses += $(dir.api)/sqlite3-api-cleanup.js
SOAP.js := $(dir.api)/sqlite3-opfs-async-proxy.js
sqlite3-worker1.js := $(dir.api)/sqlite3-worker1.js
sqlite3-worker1-promiser.js := $(dir.api)/sqlite3-worker1-promiser.js
define CP_XAPI
# COPY_XAPI = a $(call)able function to copy $1 to $(dir.dout), where
# $1 must be one of the "external" JS API files.
define COPY_XAPI
sqlite3-api.ext.jses += $$(dir.dout)/$$(notdir $(1))
$$(dir.dout)/$$(notdir $(1)): $(1) $$(MAKEFILE)
cp $$< $$@
endef
$(foreach X,$(SOAP.js) $(sqlite3-worker1.js) $(sqlite3-worker1-promiser.js),\
$(eval $(call CP_XAPI,$(X))))
$(eval $(call COPY_XAPI,$(X))))
all: $(sqlite3-api.ext.jses)
sqlite3-api.js := $(dir.tmp)/sqlite3-api.js
$(sqlite3-api.js): $(sqlite3-api.jses) $(MAKEFILE)
# sqlite3-api.js.in = the generated sqlite3-api.js before it gets
# preprocessed. It contains all of $(sqlite3-api.jses) but none of the
# Emscripten-specific headers and footers.
sqlite3-api.js.in := $(dir.tmp)/sqlite3-api.c-pp.js
$(sqlite3-api.js.in): $(sqlite3-api.jses) $(MAKEFILE)
@echo "Making $@..."
@for i in $(sqlite3-api.jses); do \
echo "/* BEGIN FILE: $$i */"; \
@@ -252,32 +329,8 @@ $(sqlite3-api-build-version.js): $(bin.version-info) $(MAKEFILE)
echo ';'; \
echo '});'; \
} > $@
########################################################################
# --post-js and --pre-js are emcc flags we use to append/prepend JS to
# the generated emscripten module file.
pre-js.js := $(dir.api)/pre-js.js
post-js.js := $(dir.tmp)/post-js.js
post-jses := \
$(dir.api)/post-js-header.js \
$(sqlite3-api.js) \
$(dir.api)/post-js-footer.js
$(post-js.js): $(post-jses) $(MAKEFILE)
@echo "Making $@..."
@for i in $(post-jses); do \
echo "/* BEGIN FILE: $$i */"; \
cat $$i; \
echo "/* END FILE: $$i */"; \
done > $@
extern-post-js.js := $(dir.api)/extern-post-js.js
extern-pre-js.js := $(dir.api)/extern-pre-js.js
pre-post-common.flags := \
--post-js=$(post-js.js) \
--extern-post-js=$(extern-post-js.js) \
--extern-pre-js=$(sqlite3-license-version.js)
pre-post-jses.deps := $(post-js.js) \
$(extern-post-js.js) $(extern-pre-js.js) $(sqlite3-license-version.js)
$(sqlite3-license-version.js): $(sqlite3.h) $(sqlite3-license-version-header.js) $(MAKEFILE)
$(sqlite3-license-version.js): $(sqlite3.h) $(sqlite3-license-version-header.js) \
$(MAKEFILE)
@echo "Making $@..."; { \
cat $(sqlite3-license-version-header.js); \
echo '/*'; \
@@ -289,43 +342,109 @@ $(sqlite3-license-version.js): $(sqlite3.h) $(sqlite3-license-version-header.js)
} > $@
########################################################################
# call-make-pre-js creates rules for pre-js-$(1).js. $1 = the base
# name of the JS file on whose behalf this pre-js is for.
# --post-js and --pre-js are emcc flags we use to append/prepend JS to
# the generated emscripten module file. The following rules generate
# various versions of those files for the vanilla and ESM builds.
pre-js.js.in := $(dir.api)/pre-js.js
pre-js.js.esm := $(dir.tmp)/pre-js.esm.js
pre-js.js.vanilla := $(dir.tmp)/pre-js.vanilla.js
$(eval $(call C-PP.FILTER,$(pre-js.js.in),$(pre-js.js.vanilla),$(c-pp.D.vanilla)))
$(eval $(call C-PP.FILTER,$(pre-js.js.in),$(pre-js.js.esm),$(c-pp.D.esm)))
post-js.js.in := $(dir.tmp)/post-js.js
post-js.js.vanilla := $(dir.tmp)/post-js.vanilla.js
post-js.js.esm := $(dir.tmp)/post-js.esm.js
post-jses.js := \
$(dir.api)/post-js-header.js \
$(sqlite3-api.js.in) \
$(dir.api)/post-js-footer.js
$(post-js.js.in): $(post-jses.js) $(MAKEFILE)
@echo "Making $@..."
@for i in $(post-jses.js); do \
echo "/* BEGIN FILE: $$i */"; \
cat $$i; \
echo "/* END FILE: $$i */"; \
done > $@
$(eval $(call C-PP.FILTER,$(post-js.js.in),$(post-js.js.vanilla),$(c-pp.D.vanilla)))
$(eval $(call C-PP.FILTER,$(post-js.js.in),$(post-js.js.esm),$(c-pp.D.esm)))
# extern-post-js* and extern-pre-js* are files for use with
# Emscripten's --extern-pre-js and --extern-post-js flags. These
# rules make different copies for the vanilla and ESM builds.
extern-post-js.js.in := $(dir.api)/extern-post-js.js
extern-post-js.js.vanilla := $(dir.tmp)/extern-post-js.vanilla.js
extern-post-js.js.esm := $(dir.tmp)/extern-post-js.esm.js
$(eval $(call C-PP.FILTER,$(extern-post-js.js.in),$(extern-post-js.js.vanilla),$(c-pp.D.vanilla)))
$(eval $(call C-PP.FILTER,$(extern-post-js.js.in),$(extern-post-js.js.esm),$(c-pp.D.esm)))
extern-pre-js.js := $(dir.api)/extern-pre-js.js
# Emscripten flags for --[extern-][pre|post]-js=... for the
# various builds.
pre-post-common.flags := \
--extern-pre-js=$(sqlite3-license-version.js)
pre-post-common.flags.vanilla := \
$(pre-post-common.flags) \
--post-js=$(post-js.js.vanilla) \
--extern-post-js=$(extern-post-js.js.vanilla)
pre-post-common.flags.esm := \
$(pre-post-common.flags) \
--post-js=$(post-js.js.esm) \
--extern-post-js=$(extern-post-js.js.esm)
# pre-post-jses.deps.* = a list of dependencies for the
# --[extern-][pre/post]-js files.
pre-post-jses.deps.common := $(extern-pre-js.js) $(sqlite3-license-version.js)
pre-post-jses.deps.vanilla := $(pre-post-jses.deps.common) \
$(post-js.js.vanilla) $(extern-post-js.js.vanilla)
pre-post-jses.deps.esm := $(pre-post-jses.deps.common) \
$(post-js.js.esm) $(extern-post-js.js.esm)
########################################################################
# call-make-pre-js is a $(call)able which creates rules for
# pre-js-$(1).js. $1 = the base name of the JS file on whose behalf
# this pre-js is for. $2 is the build mode: one of (vanilla, esm).
# This sets up --[extern-][pre/post]-js flags in
# $(pre-post-$(1).flags.$(2)) and dependencies in
# $(pre-post-$(1).deps.$(2)).
define call-make-pre-js
pre-post-$(1).flags ?=
$$(dir.tmp)/pre-js-$(1).js: $$(pre-js.js) $$(MAKEFILE)
cp $$(pre-js.js) $$@
pre-post-$(1).flags.$(2) ?=
$$(dir.tmp)/pre-js-$(1)-$(2).js: $$(pre-js.js.$(2)) $$(MAKEFILE)
cp $$(pre-js.js.$(2)) $$@
@if [ sqlite3-wasmfs = $(1) ]; then \
echo "delete Module[xNameOfInstantiateWasm] /*for WASMFS build*/;"; \
elif [ sqlite3 != $(1) ]; then \
echo "Module[xNameOfInstantiateWasm].uri = '$(1).wasm';"; \
fi >> $$@
pre-post-$(1).deps := $$(pre-post-jses.deps) $$(dir.tmp)/pre-js-$(1).js
pre-post-$(1).flags += --pre-js=$$(dir.tmp)/pre-js-$(1).js
pre-post-$(1).deps.$(2) := \
$$(pre-post-jses.deps.$(2)) \
$$(dir.tmp)/pre-js-$(1)-$(2).js
pre-post-$(1).flags.$(2) += \
$$(pre-post-common.flags.$(2)) \
--pre-js=$$(dir.tmp)/pre-js-$(1)-$(2).js
endef
#$(error $(call call-make-pre-js,sqlite3-wasmfs))
# /post-js and pre-js
########################################################################
########################################################################
# emcc flags for .c/.o/.wasm/.js.
emcc.flags :=
#emcc.flags += -v # _very_ loud but also informative about what it's doing
# -g3 is needed to keep -O2 and higher from creating broken JS via
# minification.
ifeq (1,$(emcc.verbose))
emcc.flags += -v
# -v is _very_ loud but also informative about what it's doing
endif
########################################################################
# emcc flags for .c/.o.
emcc.cflags :=
emcc.cflags += -std=c99 -fPIC
# -------------^^^^^^^^ we currently need c99 for WASM-specific sqlite3 APIs.
# -------------^^^^^^^^ we need c99 for $(sqlite3-wasm.c).
emcc.cflags += -I. -I$(dir.top)
########################################################################
# emcc flags specific to building the final .js/.wasm file...
# emcc flags specific to building .js/.wasm files...
emcc.jsflags := -fPIC
emcc.jsflags += --minify 0
emcc.jsflags += --no-entry
emcc.jsflags += -sWASM_BIGINT=$(emcc.WASM_BIGINT)
emcc.jsflags += -sMODULARIZE
emcc.jsflags += -sSTRICT_JS
emcc.jsflags += -sDYNAMIC_EXECUTION=0
@@ -333,7 +452,9 @@ emcc.jsflags += -sNO_POLYFILL
emcc.jsflags += -sEXPORTED_FUNCTIONS=@$(EXPORTED_FUNCTIONS.api)
emcc.exportedRuntimeMethods := \
-sEXPORTED_RUNTIME_METHODS=FS,wasmMemory
# FS ==> stdio/POSIX I/O proxies
# FS ==> stdio/POSIX I/O proxies. Currently used explicitly only
# by the fiddle app, and it must never be exposed to client code
# via our APIs.
# wasmMemory ==> required by our code for use with -sIMPORTED_MEMORY
emcc.jsflags += $(emcc.exportedRuntimeMethods)
emcc.jsflags += -sUSE_CLOSURE_COMPILER=0
@@ -367,32 +488,44 @@ emcc.jsflags += -sINITIAL_MEMORY=$(emcc.INITIAL_MEMORY.$(emcc.INITIAL_MEMORY))
emcc.jsflags += $(emcc.environment)
#emcc.jsflags += -sTOTAL_STACK=4194304
########################################################################
# $(sqlite3.js.init-func) is the name Emscripten assigns our exported
# module init/load function. This symbol name is hard-coded in
# $(extern-post-js.js) as well as in numerous docs.
#
# "sqlite3InitModule" is the symbol we document for client use, so
# that's the symbol name which must be exported, whether it comes from
# Emscripten or our own code in extern-post-js.js.
#
# That said... we can change $(sqlite3.js.init-func) as long as the
# name "sqlite3InitModule" is the one which gets exposed via the
# resulting JS files. That can be accomplished via
# extern-post-js.js. However... using a temporary symbol name here
# and then adding sqlite3InitModule() ourselves results in 2 global
# symbols: we cannot "delete" the Emscripten-defined
# $(sqlite3.js.init-func) because it's declared with "var".
sqlite3.js.init-func := sqlite3InitModule
# ^^^^ $(sqlite3.js.init-func) symbol name is hard-coded in
# $(extern-post-js.js) as well as in numerous docs. If changed, it
# needs to be globally modified in *.js and all related documentation.
emcc.jsflags += -sEXPORT_NAME=$(sqlite3.js.init-func)
emcc.jsflags += -sGLOBAL_BASE=4096 # HYPOTHETICALLY keep func table indexes from overlapping w/ heap addr.
#emcc.jsflags += -sSTRICT # fails due to missing __syscall_...()
#emcc.jsflags += -sALLOW_UNIMPLEMENTED_SYSCALLS
#emcc.jsflags += -sFILESYSTEM=0 # only for experimentation. sqlite3 needs the FS API
#emcc.jsflags += -sABORTING_MALLOC
#emcc.jsflags += -sABORTING_MALLOC # only for experimentation
emcc.jsflags += -sALLOW_TABLE_GROWTH
# -sALLOW_TABLE_GROWTH is required for installing new SQL UDFs
# ^^^^ -sALLOW_TABLE_GROWTH is required for installing new SQL UDFs
emcc.jsflags += -Wno-limited-postlink-optimizations
# ^^^^^ it likes to warn when we have "limited optimizations" via the -g3 flag.
#emcc.jsflags += -sSTANDALONE_WASM # causes OOM errors, not sure why
# https://lld.llvm.org/WebAssembly.html
emcc.jsflags += -sERROR_ON_UNDEFINED_SYMBOLS=0
# ^^^^ emcc likes to warn when we have "limited optimizations" via the
# -g3 flag.
# emcc.jsflags += -sSTANDALONE_WASM # causes OOM errors, not sure why.
# Re. undefined symbol handling, see: https://lld.llvm.org/WebAssembly.html
emcc.jsflags += -sERROR_ON_UNDEFINED_SYMBOLS=1
emcc.jsflags += -sLLD_REPORT_UNDEFINED
#emcc.jsflags += --allow-undefined
#emcc.jsflags += --import-undefined
#emcc.jsflags += --unresolved-symbols=import-dynamic --experimental-pic
#emcc.jsflags += --experimental-pic --unresolved-symbols=ingore-all --import-undefined
#emcc.jsflags += --unresolved-symbols=ignore-all
emcc.jsflags += -sWASM_BIGINT=$(emcc.WASM_BIGINT)
########################################################################
# -sMEMORY64=1 fails to load, erroring with:
@@ -404,21 +537,28 @@ emcc.jsflags += -sWASM_BIGINT=$(emcc.WASM_BIGINT)
# new Uint8Array(wasm.heap8u().buffer, ptr, n)
#
# because ptr is now a BigInt, so is invalid for passing to arguments
# which have strict must-be-a-Number requirements.
# which have strict must-be-a-Number requirements. That aspect will
# make any eventual port to 64-bit address space extremely painful, as
# such constructs are found all over the place in the source code.
########################################################################
########################################################################
# -sSINGLE_FILE:
# https://github.com/emscripten-core/emscripten/blob/main/src/settings.js#L1704
# -sSINGLE_FILE=1 would be really nice but we have to build with -g3
# https://github.com/emscripten-core/emscripten/blob/main/src/settings.js
#
# -sSINGLE_FILE=1 would be _really_ nice but we have to build with -g3
# for -O2 and higher to work (else minification breaks the code) and
# cannot wasm-strip the binary before it gets encoded into the JS
# file. The result is that the generated JS file is, because of the -g3
# debugging info, _huge_.
# file. The result is that the generated JS file is, because of the
# -g3 debugging info, _huge_.
########################################################################
sqlite3.js := $(dir.dout)/sqlite3.js
sqlite3.mjs := $(dir.dout)/sqlite3.mjs
# Undocumented Emscripten feature: if the target file extension is
# "mjs", it defaults to ES6 module builds:
# https://github.com/emscripten-core/emscripten/issues/14383
sqlite3.wasm := $(dir.dout)/sqlite3.wasm
sqlite3-wasm.c := $(dir.api)/sqlite3-wasm.c
# sqlite3-wasm.o vs sqlite3-wasm.c: building against the latter
@@ -426,27 +566,71 @@ sqlite3-wasm.c := $(dir.api)/sqlite3-wasm.c
# enough to the target speed requirements that the 500ms makes a
# difference. Thus we build all binaries against sqlite3-wasm.c
# instead of building a shared copy of sqlite3-wasm.o.
$(eval $(call call-make-pre-js,sqlite3))
$(sqlite3.js):
$(sqlite3.js): $(MAKEFILE) $(sqlite3.wasm.obj) \
$(EXPORTED_FUNCTIONS.api) \
$(pre-post-sqlite3.deps)
$(eval $(call call-make-pre-js,sqlite3,vanilla))
$(eval $(call call-make-pre-js,sqlite3,esm))
$(sqlite3.js) $(sqlite3.mjs): $(MAKEFILE) $(sqlite3.wasm.obj) \
$(EXPORTED_FUNCTIONS.api)
$(sqlite3.js): $(pre-post-sqlite3.deps.vanilla)
$(sqlite3.mjs): $(pre-post-sqlite3.deps.esm)
########################################################################
# SQLITE3.xJS.RECIPE = the $(call)able recipe body for $(sqlite3.js)
# and $(sqlite3.mjs). $1 = one of (vanilla, esm).
#
# Reminder for ESM builds: even if we use -sEXPORT_ES6=0, emcc _still_
# adds:
#
# export default $(sqlite3.js.init-func);
#
# when building *.mjs, which is bad because we need to export an
# overwritten version of that function and cannot "export default"
# twice. Because of this, we have to sed $(sqlite3.mjs) to remove the
# _first_ instance (only) of /^export default/.
#
# Upstream RFE:
# https://github.com/emscripten-core/emscripten/issues/18237
########################################################################
define SQLITE3.xJS.RECIPE
@echo "Building $@ ..."
$(emcc.bin) -o $@ $(emcc_opt_full) $(emcc.flags) \
$(emcc.jsflags) $(pre-post-common.flags) $(pre-post-sqlite3.flags) \
$(emcc.jsflags) \
$(pre-post-sqlite3.flags.$(1)) $(emcc.flags.sqlite3.$(1)) \
$(cflags.common) $(SQLITE_OPT) $(sqlite3-wasm.c)
@if [ esm = $(1) ]; then \
echo "Fragile workaround for an Emscripten annoyance. See emcc.flags.sqlite3.esm."; \
sed -i -e '0,/^export default/{/^export default/d}' $@ || exit $$?; \
if ! grep -q '^export default' $@; then \
echo "Cannot find export default." 1>&2; \
exit 1; \
fi; \
fi
chmod -x $(sqlite3.wasm)
$(maybe-wasm-strip) $(sqlite3.wasm)
@ls -la $@ $(sqlite3.wasm)
endef
emcc.flags.sqlite3.vanilla :=
emcc.flags.sqlite3.esm := -sEXPORT_ES6 -sUSE_ES6_IMPORT_META
$(sqlite3.js):
$(call SQLITE3.xJS.RECIPE,vanilla)
$(sqlite3.mjs):
$(call SQLITE3.xJS.RECIPE,esm)
########################################################################
# We have to ensure that we do not build both $(sqlite3.js) and
# $(sqlite3.mjs) in parallel because both result in the creation of
# $(sqlite3.wasm). We have no(?) way to build just the .mjs file
# without also building the .wasm file. i.e. we're building
# $(sqlite3.wasm) twice, but that's apparently unavoidable (and
# harmless, just a waste of build time).
$(sqlite3.wasm): $(sqlite3.js)
CLEAN_FILES += $(sqlite3.js) $(sqlite3.wasm)
all: $(sqlite3.js)
wasm: $(sqlite3.js)
# End main Emscripten-based module build
$(sqlite3.mjs): $(sqlite3.js)
CLEAN_FILES += $(sqlite3.js) $(sqlite3.mjs) $(sqlite3.wasm)
all: $(sqlite3.mjs)
wasm: $(sqlite3.mjs)
# End main $(sqlite3.js) build
########################################################################
########################################################################
# batch-runner.js...
# batch-runner.js is part of one of the test apps which reads in SQL
# dumps generated by $(speedtest1) and executes them.
dir.sql := sql
speedtest1 := ../../speedtest1
speedtest1.c := ../../test/speedtest1.c
@@ -468,30 +652,30 @@ batch: batch-runner.list
all: batch
# end batch-runner.js
########################################################################
# speedtest1.js...
# speedtest1-common.eflags = emcc flags used by multiple builds of speedtest1
# Wasmified speedtest1 is our primary benchmarking tool.
#
# speedtest1.eflags.common = emcc flags used by multiple builds of speedtest1
# speedtest1.eflags = emcc flags used by main build of speedtest1
speedtest1-common.eflags := $(emcc_opt_full)
speedtest1.eflags.common := $(emcc_opt_full)
speedtest1.eflags :=
speedtest1.eflags += -sENVIRONMENT=web
speedtest1.eflags += -sALLOW_MEMORY_GROWTH
speedtest1.eflags += -sINITIAL_MEMORY=$(emcc.INITIAL_MEMORY.$(emcc.INITIAL_MEMORY))
speedtest1-common.eflags += -sINVOKE_RUN=0
speedtest1-common.eflags += --no-entry
#speedtest1-common.eflags += -flto
speedtest1-common.eflags += -sABORTING_MALLOC
speedtest1-common.eflags += -sSTRICT_JS
speedtest1-common.eflags += -sMODULARIZE
speedtest1-common.eflags += -Wno-limited-postlink-optimizations
speedtest1.eflags.common += -sINVOKE_RUN=0
speedtest1.eflags.common += --no-entry
#speedtest1.eflags.common += -flto
speedtest1.eflags.common += -sABORTING_MALLOC
speedtest1.eflags.common += -sSTRICT_JS
speedtest1.eflags.common += -sMODULARIZE
speedtest1.eflags.common += -Wno-limited-postlink-optimizations
EXPORTED_FUNCTIONS.speedtest1 := $(abspath $(dir.tmp)/EXPORTED_FUNCTIONS.speedtest1)
speedtest1-common.eflags += -sEXPORTED_FUNCTIONS=@$(EXPORTED_FUNCTIONS.speedtest1)
speedtest1-common.eflags += $(emcc.exportedRuntimeMethods)
speedtest1-common.eflags += -sALLOW_TABLE_GROWTH
speedtest1-common.eflags += -sDYNAMIC_EXECUTION=0
speedtest1-common.eflags += --minify 0
speedtest1-common.eflags += -sEXPORT_NAME=$(sqlite3.js.init-func)
speedtest1-common.eflags += -sWASM_BIGINT=$(emcc.WASM_BIGINT)
speedtest1-common.eflags += $(pre-post-common.flags)
speedtest1.eflags.common += -sEXPORTED_FUNCTIONS=@$(EXPORTED_FUNCTIONS.speedtest1)
speedtest1.eflags.common += $(emcc.exportedRuntimeMethods)
speedtest1.eflags.common += -sALLOW_TABLE_GROWTH
speedtest1.eflags.common += -sDYNAMIC_EXECUTION=0
speedtest1.eflags.common += --minify 0
speedtest1.eflags.common += -sEXPORT_NAME=$(sqlite3.js.init-func)
speedtest1.eflags.common += -sWASM_BIGINT=$(emcc.WASM_BIGINT)
speedtest1.exit-runtime0 := -sEXIT_RUNTIME=0
speedtest1.exit-runtime1 := -sEXIT_RUNTIME=1
# Re -sEXIT_RUNTIME=1 vs 0: if it's 1 and speedtest1 crashes, we get
@@ -515,17 +699,17 @@ $(EXPORTED_FUNCTIONS.speedtest1): $(EXPORTED_FUNCTIONS.api)
@echo "Making $@ ..."
@{ echo _wasm_main; cat $(EXPORTED_FUNCTIONS.api); } > $@
speedtest1.js := $(dir.dout)/speedtest1.js
speedtest1.wasm := $(subst .js,.wasm,$(speedtest1.js))
speedtest1.wasm := $(dir.dout)/speedtest1.wasm
speedtest1.cflags := $(cflags.common) -DSQLITE_SPEEDTEST1_WASM
speedtest1.cses := $(speedtest1.c) $(sqlite3-wasm.c)
$(eval $(call call-make-pre-js,speedtest1))
$(eval $(call call-make-pre-js,speedtest1,vanilla))
$(speedtest1.js): $(MAKEFILE) $(speedtest1.cses) \
$(pre-post-speedtest1.deps) \
$(pre-post-speedtest1.deps.vanilla) \
$(EXPORTED_FUNCTIONS.speedtest1)
@echo "Building $@ ..."
$(emcc.bin) \
$(speedtest1.eflags) $(speedtest1-common.eflags) $(speedtest1.cflags) \
$(pre-post-speedtest1.flags) \
$(speedtest1.eflags) $(speedtest1.eflags.common) \
$(speedtest1.cflags) $(pre-post-speedtest1.flags.vanilla) \
$(SQLITE_OPT) \
$(speedtest1.exit-runtime0) \
-o $@ $(speedtest1.cses) -lm
@@ -538,6 +722,29 @@ CLEAN_FILES += $(speedtest1.js) $(speedtest1.wasm)
# end speedtest1.js
########################################################################
########################################################################
# tester1 is the main unit and regression test application and needs
# to be able to run in 4 separate modes to cover the primary
# client-side use cases:
#
# 1) Load sqlite3 in the main UI thread of a conventional script.
# 2) Load sqlite3 in a conventional Worker thread.
# 3) Load sqlite3 as an ES6 module (ESM) in the main thread.
# 4) Load sqlite3 as an ESM worker. (Not all browsers support this.)
#
# To that end, we require two separate builds of tester1.js:
#
# tester1.js: cases 1 and 2
# tester1.mjs: cases 3 and 4
#
# To create those, we filter tester1.c-pp.js with $(bin.c-pp)...
$(eval $(call C-PP.FILTER,tester1.c-pp.js,tester1.js))
$(eval $(call C-PP.FILTER,tester1.c-pp.js,tester1.mjs,$(c-pp.D.esm)))
$(eval $(call C-PP.FILTER,tester1.c-pp.html,tester1.html))
$(eval $(call C-PP.FILTER,tester1.c-pp.html,tester1-esm.html,$(c-pp.D.esm)))
tester1: tester1.js tester1.mjs tester1.html tester1-esm.html
all: tester1
########################################################################
# Convenience rules to rebuild with various -Ox levels. Much
# experimentation shows -O2 to be the clear winner in terms of speed.
@@ -547,8 +754,8 @@ CLEAN_FILES += $(speedtest1.js) $(speedtest1.wasm)
.PHONY: o0 o1 o2 o3 os oz
o-xtra := -flto
# ^^^^ -flto can have a considerably performance boost at -O0 but
# doubles the build time and seems to have negligible effect on
# higher optimization levels.
# doubles the build time and seems to have negligible, if any, effect
# on higher optimization levels.
o0: clean
$(MAKE) -e "emcc_opt=-O0"
o1: clean
@@ -566,16 +773,21 @@ oz: clean
########################################################################
# Sub-makes...
# sqlite.org/fiddle application...
include fiddle.make
# Only add wasmfs if wasmfs.enable=1 or we're running (dist)clean
ifneq (,$(filter wasmfs,$(MAKECMDGOALS)))
wasmfs.enable ?= 1
else
wasmfs.enable ?= $(if $(filter %clean,$(MAKECMDGOALS)),1,0)
endif
ifeq (1,$(wasmfs.enable))
# wasmfs build disabled 2022-10-19 per /chat discussion.
# OPFS-over-wasmfs was initially a stopgap measure and a convenient
# point of comparison for the OPFS sqlite3_vfs's performance, but it
# currently doubles our deliverables and build maintenance burden for
# little, if any, benefit.
# little benefit.
#
########################################################################
# Some platforms do not support the WASMFS build. Raspberry Pi OS is one
@@ -595,15 +807,16 @@ endif
########################################################################
########################################################################
# Create deliverables:
ifneq (,$(filter dist,$(MAKECMDGOALS)))
# Create main client downloadable zip file:
ifneq (,$(filter dist snapshot,$(MAKECMDGOALS)))
include dist.make
endif
########################################################################
# Push files to public wasm-testing.sqlite.org server
wasm-testing.include = $(dir.dout) *.js *.html \
batch-runner.list $(dir.sql) $(dir.common) $(dir.fiddle) $(dir.jacc)
wasm-testing.include = *.js *.html batch-runner.list \
./tests \
$(dir.dout) $(dir.sql) $(dir.common) $(dir.fiddle) $(dir.jacc)
wasm-testing.exclude = sql/speedtest1.sql
wasm-testing.dir = /jail/sites/wasm-testing
wasm-testing.dest ?= wasm-testing:$(wasm-testing.dir)
@@ -7,6 +7,7 @@ _sqlite3_bind_null
_sqlite3_bind_parameter_count
_sqlite3_bind_parameter_index
_sqlite3_bind_text
_sqlite3_busy_timeout
_sqlite3_changes
_sqlite3_changes64
_sqlite3_clear_bindings
+8
View File
@@ -131,3 +131,11 @@ into the build-generated `sqlite3.js` along with `sqlite3-api.js`.
`sqlite3InitModule()` function with one which, after the module is
loaded, also initializes the asynchronous parts of the sqlite3
module. For example, the OPFS VFS support.
## Preprocessing of Source Files
Certain files in the build require preprocessing to filter in/out
parts which differ between vanilla JS builds and ES6 Module
(a.k.a. esm) builds. The preprocessor itself is in
[](/file/ext/wasm/c-pp.c) and the associates flags and rules are in
[](/file/ext/wasm/GNUmakefile).
+23 -7
View File
@@ -1,21 +1,32 @@
/* ^^^^ ACHTUNG: blank line at the start is necessary because
Emscripten will not add a newline in some cases and we need
a blank line for a sed-based kludge for the ES6 build. */
/* extern-post-js.js must be appended to the resulting sqlite3.js
file. It gets its name from being used as the value for the
--extern-post-js=... Emscripten flag. Note that this code, unlike
most of the associated JS code, runs outside of the
Emscripten-generated module init scope, in the current
global scope. */
//#if target=es6-module
const toExportForES6 =
//#endif
(function(){
/**
In order to hide the sqlite3InitModule()'s resulting Emscripten
module from downstream clients (and simplify our documentation by
being able to elide those details), we rewrite
sqlite3InitModule() to return the sqlite3 object.
In order to hide the sqlite3InitModule()'s resulting
Emscripten module from downstream clients (and simplify our
documentation by being able to elide those details), we hide that
function and expose a hand-written sqlite3InitModule() to return
the sqlite3 object (most of the time).
Unfortunately, we cannot modify the module-loader/exporter-based
impls which Emscripten installs at some point in the file above
this.
*/
const originalInit = self.sqlite3InitModule;
const originalInit =
/* Maintenance reminder: DO NOT use `self.` here. It's correct
for non-ES6 Module cases but wrong for ES6 modules because those
resolve this symbol differently. */ sqlite3InitModule;
if(!originalInit){
throw new Error("Expecting self.sqlite3InitModule to be defined by the Emscripten build.");
}
@@ -94,10 +105,15 @@
}
/* Replace the various module exports performed by the Emscripten
glue... */
if (typeof exports === 'object' && typeof module === 'object')
if (typeof exports === 'object' && typeof module === 'object'){
module.exports = sqlite3InitModule;
else if (typeof exports === 'object')
}else if (typeof exports === 'object'){
exports["sqlite3InitModule"] = sqlite3InitModule;
}
/* AMD modules get injected in a way we cannot override,
so we can't handle those here. */
return self.sqlite3InitModule /* required for ESM */;
})();
//#if target=es6-module
export default toExportForES6;
//#endif
+6 -1
View File
@@ -29,6 +29,10 @@ sqlite3InitModuleState.debugModule('self.location =',self.location);
4) If none of the above apply, (prefix+path) is returned.
*/
Module['locateFile'] = function(path, prefix) {
//#if target=es6-module
return new URL(path, import.meta.url).href;
//#else
'use strict';
let theFile;
const up = this.urlParams;
if(up.has(path)){
@@ -47,6 +51,7 @@ Module['locateFile'] = function(path, prefix) {
"result =", theFile
);
return theFile;
//#endif /* SQLITE_JS_EMS */
}.bind(sqlite3InitModuleState);
/**
@@ -65,7 +70,7 @@ Module[xNameOfInstantiateWasm] = function callee(imports,onSuccess){
const uri = Module.locateFile(
callee.uri, (
('undefined'===typeof scriptDirectory/*var defined by Emscripten glue*/)
? '' : scriptDirectory)
? "" : scriptDirectory)
);
sqlite3InitModuleState.debugModule(
"instantiateWasm() uri =", uri
+2 -2
View File
@@ -141,7 +141,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
const __dbArgcMismatch = (pDb,f,n)=>{
return sqlite3.util.sqlite3_wasm_db_error(pDb, capi.SQLITE_MISUSE,
f+"() requires "+n+" argument"+
(1===n?'':'s')+".");
(1===n?"":'s')+".");
};
/**
@@ -158,7 +158,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
n = v.byteLength;
v = util.typedArrayToString(v);
}else if(Array.isArray(v)){
v = v.join('');
v = v.join("");
n = -1;
}
return [v, n];
+21 -11
View File
@@ -55,12 +55,13 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
if(sqliteResultCode){
if(dbPtr instanceof DB) dbPtr = dbPtr.pointer;
toss3(
"sqlite result code",sqliteResultCode+":",
"sqlite3 result code",sqliteResultCode+":",
(dbPtr
? capi.sqlite3_errmsg(dbPtr)
: capi.sqlite3_errstr(sqliteResultCode))
);
}
return arguments[0];
};
/**
@@ -200,9 +201,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
*/
dbCtorHelper.normalizeArgs = function(filename=':memory:',flags = 'c',vfs = null){
const arg = {};
if(1===arguments.length && 'object'===typeof arguments[0]){
const x = arguments[0];
Object.keys(x).forEach((k)=>arg[k] = x[k]);
if(1===arguments.length && arguments[0] && 'object'===typeof arguments[0]){
Object.assign(arg, arguments[0]);
if(undefined===arg.flags) arg.flags = 'c';
if(undefined===arg.vfs) arg.vfs = null;
if(undefined===arg.filename) arg.filename = ':memory:';
@@ -462,14 +462,16 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
Expects to be given a DB instance or an `sqlite3*` pointer (may
be null) and an sqlite3 API result code. If the result code is
not falsy, this function throws an SQLite3Error with an error
message from sqlite3_errmsg(), using dbPtr as the db handle, or
sqlite3_errstr() if dbPtr is falsy. Note that if it's passed a
non-error code like SQLITE_ROW or SQLITE_DONE, it will still
throw but the error string might be "Not an error." The various
non-0 non-error codes need to be checked for in
client code where they are expected.
message from sqlite3_errmsg(), using db (or, if db is-a DB,
db.pointer) as the db handle, or sqlite3_errstr() if db is
falsy. Note that if it's passed a non-error code like SQLITE_ROW
or SQLITE_DONE, it will still throw but the error string might be
"Not an error." The various non-0 non-error codes need to be
checked for in client code where they are expected.
If it does not throw, it returns its first argument.
*/
DB.checkRc = checkSqlite3Rc;
DB.checkRc = (db,resultCode)=>checkSqlite3Rc(db,resultCode);
DB.prototype = {
/** Returns true if this db handle is open, else false. */
@@ -1130,6 +1132,14 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
this.exec("ROLLBACK to SAVEPOINT oo1; RELEASE SAVEPOINT oo1");
throw e;
}
},
/**
A convenience form of DB.checkRc(this,resultCode). If it does
not throw, it returns this object.
*/
checkRc: function(resultCode){
return DB.checkRc(this, resultCode);
}
}/*DB.prototype*/;
+53 -9
View File
@@ -92,7 +92,8 @@ const installOpfsVfs = function callee(options){
}
const urlParams = new URL(self.location.href).searchParams;
if(undefined===options.verbose){
options.verbose = urlParams.has('opfs-verbose') ? 3 : 2;
options.verbose = urlParams.has('opfs-verbose')
? (+urlParams.get('opfs-verbose') || 2) : 1;
}
if(undefined===options.sanityChecks){
options.sanityChecks = urlParams.has('opfs-sanity-check');
@@ -101,6 +102,8 @@ const installOpfsVfs = function callee(options){
options.proxyUri = callee.defaultProxyUri;
}
//console.warn("OPFS options =",options,self.location);
if('function' === typeof options.proxyUri){
options.proxyUri = options.proxyUri();
}
@@ -166,7 +169,12 @@ const installOpfsVfs = function callee(options){
opfsVfs.dispose();
return promiseReject_(err);
};
const W = new Worker(options.proxyUri);
const W =
//#if target=es6-module
new Worker(new URL(options.proxyUri, import.meta.url));
//#else
new Worker(options.proxyUri);
//#endif
W._originalOnError = W.onerror /* will be restored later */;
W.onerror = function(err){
// The error object doesn't contain any useful info when the
@@ -335,16 +343,37 @@ const installOpfsVfs = function callee(options){
'SQLITE_LOCK_PENDING',
'SQLITE_LOCK_RESERVED',
'SQLITE_LOCK_SHARED',
'SQLITE_LOCKED',
'SQLITE_MISUSE',
'SQLITE_NOTFOUND',
'SQLITE_OPEN_CREATE',
'SQLITE_OPEN_DELETEONCLOSE',
'SQLITE_OPEN_MAIN_DB',
'SQLITE_OPEN_READONLY'
].forEach((k)=>{
if(undefined === (state.sq3Codes[k] = capi[k])){
toss("Maintenance required: not found:",k);
}
});
state.opfsFlags = Object.assign(Object.create(null),{
/**
Flag for use with xOpen(). "opfs-unlock-asap=1" enables
this. See defaultUnlockAsap, below.
*/
OPFS_UNLOCK_ASAP: 0x01,
/**
If true, any async routine which implicitly acquires a sync
access handle (i.e. an OPFS lock) will release that locks at
the end of the call which acquires it. If false, such
"autolocks" are not released until the VFS is idle for some
brief amount of time.
The benefit of enabling this is much higher concurrency. The
down-side is much-reduced performance (as much as a 4x decrease
in speedtest1).
*/
defaultUnlockAsap: false
});
/**
Runs the given operation (by name) in the async worker
@@ -467,9 +496,11 @@ const installOpfsVfs = function callee(options){
/**
Returns an array of the deserialized state stored by the most
recent serialize() operation (from from this thread or the
counterpart thread), or null if the serialization buffer is empty.
counterpart thread), or null if the serialization buffer is
empty. If passed a truthy argument, the serialization buffer
is cleared after deserialization.
*/
state.s11n.deserialize = function(){
state.s11n.deserialize = function(clear=false){
++metrics.s11n.deserialize.count;
const t = performance.now();
const argc = viewU8[0];
@@ -494,6 +525,7 @@ const installOpfsVfs = function callee(options){
rc.push(v);
}
}
if(clear) viewU8[0] = 0;
//log("deserialize:",argc, rc);
metrics.s11n.deserialize.time += performance.now() - t;
return rc;
@@ -566,7 +598,7 @@ const installOpfsVfs = function callee(options){
const ndx = Math.random() * (f._n * 64) % f._n | 0;
a[i] = f._chars[ndx];
}
return a.join('');
return a.join("");
};
/**
@@ -832,9 +864,15 @@ const installOpfsVfs = function callee(options){
//xSleep is optionally defined below
xOpen: function f(pVfs, zName, pFile, flags, pOutFlags){
mTimeStart('xOpen');
let opfsFlags = 0;
if(0===zName){
zName = randomFilename();
}else if('number'===typeof zName){
if(capi.sqlite3_uri_boolean(zName, "opfs-unlock-asap", 0)){
/* -----------------------^^^^^ MUST pass the untranslated
C-string here. */
opfsFlags |= state.opfsFlags.OPFS_UNLOCK_ASAP;
}
zName = wasm.cstringToJs(zName);
}
const fh = Object.create(null);
@@ -842,7 +880,7 @@ const installOpfsVfs = function callee(options){
fh.filename = zName;
fh.sab = new SharedArrayBuffer(state.fileBufferSize);
fh.flags = flags;
const rc = opRun('xOpen', pFile, zName, flags);
const rc = opRun('xOpen', pFile, zName, flags, opfsFlags);
if(!rc){
/* Recall that sqlite3_vfs::xClose() will be called, even on
error, unless pFile->pMethods is NULL. */
@@ -1133,6 +1171,9 @@ const installOpfsVfs = function callee(options){
//TODO to support fiddle and worker1 db upload:
//opfsUtil.createFile = function(absName, content=undefined){...}
//We have sqlite3.wasm.sqlite3_wasm_vfs_create_file() for this
//purpose but its interface and name are still under
//consideration.
if(sqlite3.oo1){
opfsUtil.OpfsDb = function(...args){
@@ -1146,7 +1187,10 @@ const installOpfsVfs = function callee(options){
[
/* Truncate journal mode is faster than delete or wal for
this vfs, per speedtest1. */
"pragma journal_mode=truncate;"
"pragma journal_mode=truncate;",
/* Set a default busy-timeout handler to help OPFS dbs
deal with multi-tab/multi-worker contention. */
"pragma busy_timeout=2000;",
/*
This vfs benefits hugely from cache on moderate/large
speedtest1 --size 50 and --size 100 workloads. We currently
@@ -1154,8 +1198,8 @@ const installOpfsVfs = function callee(options){
sqlite3.wasm. If that policy changes, the cache can
be set here.
*/
//"pragma cache_size=-8388608;"
].join('')
//"pragma cache_size=-16384;"
].join("")
);
}
+98 -108
View File
@@ -11,69 +11,18 @@
***********************************************************************
This file is intended to be combined at build-time with other
related code, most notably a header and footer which wraps this whole
file into an Emscripten Module.postRun() handler which has a parameter
named "Module" (the Emscripten Module object). The exact requirements,
conventions, and build process are very much under construction and
will be (re)documented once they've stopped fluctuating so much.
related code, most notably a header and footer which wraps this
whole file into an Emscripten Module.postRun() handler which has a
parameter named "Module" (the Emscripten Module object). The sqlite3
JS API has no hard requirements on Emscripten, and does not expose
any Emscripten APIs to clients. It is structured such that its build
can be tweaked to include it in arbitrary WASM environments which
supply the necessary underlying features (e.g. a POSIX file I/O
layer).
Specific goals of this project:
Main project home page: https://sqlite.org
- Except where noted in the non-goals, provide a more-or-less
feature-complete wrapper to the sqlite3 C API, insofar as WASM
feature parity with C allows for. In fact, provide at least 3
APIs...
1) Bind a low-level sqlite3 API which is as close to the native
one as feasible in terms of usage.
2) A higher-level API, more akin to sql.js and node.js-style
implementations. This one speaks directly to the low-level
API. This API must be used from the same thread as the
low-level API.
3) A second higher-level API which speaks to the previous APIs via
worker messages. This one is intended for use in the main
thread, with the lower-level APIs installed in a Worker thread,
and talking to them via Worker messages. Because Workers are
asynchronouns and have only a single message channel, some
acrobatics are needed here to feed async work results back to
the client (as we cannot simply pass around callbacks between
the main and Worker threads).
- Insofar as possible, support client-side storage using JS
filesystem APIs. As of this writing, such things are still very
much under development.
Specific non-goals of this project:
- As WASM is a web-centric technology and UTF-8 is the King of
Encodings in that realm, there are no currently plans to support
the UTF16-related sqlite3 APIs. They would add a complication to
the bindings for no appreciable benefit. Though web-related
implementation details take priority, and the JavaScript
components of the API specifically focus on browser clients, the
lower-level WASM module "should" work in non-web WASM
environments.
- Supporting old or niche-market platforms. WASM is built for a
modern web and requires modern platforms.
- Though scalar User-Defined Functions (UDFs) may be created in
JavaScript, there are currently no plans to add support for
aggregate and window functions.
Attribution:
This project is endebted to the work of sql.js:
https://github.com/sql-js/sql.js
sql.js was an essential stepping stone in this code's development as
it demonstrated how to handle some of the WASM-related voodoo (like
handling pointers-to-pointers and adding JS implementations of
C-bound callback functions). These APIs have a considerably
different shape than sql.js's, however.
Documentation home page: https://sqlite.org/wasm
*/
/**
@@ -83,6 +32,10 @@
for the current environment, and then optionally be removed from
the global object using `delete self.sqlite3ApiBootstrap`.
This function is not intended for client-level use. It is intended
for use in creating bundles configured for specific WASM
environments.
This function expects a configuration object, intended to abstract
away details specific to any given WASM environment, primarily so
that it can be used without any _direct_ dependency on
@@ -90,11 +43,13 @@
config object is only honored the first time this is
called. Subsequent calls ignore the argument and return the same
(configured) object which gets initialized by the first call.
This function will throw if any of the required config options are
missing.
The config object properties include:
- `exports`[^1]: the "exports" object for the current WASM
environment. In an Emscripten build, this should be set to
environment. In an Emscripten-based build, this should be set to
`Module['asm']`.
- `memory`[^1]: optional WebAssembly.Memory object, defaulting to
@@ -104,7 +59,7 @@
WASM-exported memory.
- `bigIntEnabled`: true if BigInt support is enabled. Defaults to
true if self.BigInt64Array is available, else false. Some APIs
true if `self.BigInt64Array` is available, else false. Some APIs
will throw exceptions if called without BigInt support, as BigInt
is required for marshalling C-side int64 into and out of JS.
@@ -116,10 +71,12 @@
the `free(3)`-compatible routine for the WASM
environment. Defaults to `"free"`.
- `wasmfsOpfsDir`[^1]: if the environment supports persistent storage, this
directory names the "mount point" for that directory. It must be prefixed
by `/` and may currently contain only a single directory-name part. Using
the root directory name is not supported by any current persistent backend.
- `wasmfsOpfsDir`[^1]: if the environment supports persistent
storage using OPFS-over-WASMFS , this directory names the "mount
point" for that directory. It must be prefixed by `/` and may
contain only a single directory-name part. Using the root
directory name is not supported by any current persistent
backend. This setting is only used in WASMFS-enabled builds.
[^1] = This property may optionally be a function, in which case this
@@ -180,11 +137,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
const capi = Object.create(null);
/**
Holds state which are specific to the WASM-related
infrastructure and glue code. It is not expected that client
code will normally need these, but they're exposed here in case
it does. These APIs are _not_ to be considered an
official/stable part of the sqlite3 WASM API. They may change
as the developers' experience suggests appropriate changes.
infrastructure and glue code.
Note that a number of members of this object are injected
dynamically after the api object is fully constructed, so
@@ -217,7 +170,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
result of sqlite3.capi.sqlite3_js_rc_str() or (if that returns
falsy) a synthesized string which contains that integer.
- If passed 2 arguments and the 2nd is a object, it bevaves
- If passed 2 arguments and the 2nd is a object, it behaves
like the Error(string,object) constructor except that the first
argument is subject to the is-integer semantics from the
previous point.
@@ -370,14 +323,14 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
};
/**
If v is-a Array, its join('') result is returned. If
If v is-a Array, its join("") result is returned. If
isSQLableTypedArray(v) is true then typedArrayToString(v) is
returned. If it looks like a WASM pointer, wasm.cstringToJs(v) is
returned. Else v is returned as-is.
*/
const flexibleString = function(v){
if(isSQLableTypedArray(v)) return typedArrayToString(v);
else if(Array.isArray(v)) return v.join('');
else if(Array.isArray(v)) return v.join("");
else if(wasm.isPtr(v)) v = wasm.cstringToJs(v);
return v;
};
@@ -388,8 +341,22 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
exceptions.
*/
class WasmAllocError extends Error {
/**
If called with 2 arguments and the 2nd one is an object, it
behaves like the Error constructor, else it concatenates all
arguments together with a single space between each to
construct an error message string. As a special case, if
called with no arguments then it uses a default error
message.
*/
constructor(...args){
super(...args);
if(2===args.length && 'object'===typeof args){
super(...args);
}else if(args.length){
super(args.join(' '));
}else{
super("Allocation failed.");
}
this.name = 'WasmAllocError';
}
};
@@ -661,9 +628,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
/**
The WASM IR (Intermediate Representation) value for
pointer-type values. It MUST refer to a value type of the
size described by this.ptrSizeof _or_ it may be any value
which ends in '*', which Emscripten's glue code internally
translates to i32.
size described by this.ptrSizeof.
*/
ptrIR: config.wasmPtrIR || "i32",
/**
@@ -699,21 +664,33 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
API NOT throw and must instead return SQLITE_NOMEM (or
equivalent, depending on the context).
That said, very few cases in the API can result in
Very few cases in the sqlite3 JS APIs can result in
client-defined functions propagating exceptions via the C-style
API. Most notably, this applies ot User-defined SQL Functions
(UDFs) registered via sqlite3_create_function_v2(). For that
specific case it is recommended that all UDF creation be
funneled through a utility function and that a wrapper function
be added around the UDF which catches any exception and sets
the error state to OOM. (The overall complexity of registering
UDFs essentially requires a helper for doing so!)
API. Most notably, this applies to WASM-bound JS functions
which are created directly by clients and passed on _as WASM
function pointers_ to functions such as
sqlite3_create_function_v2(). Such bindings created
transparently by this API will automatically use wrappers which
catch exceptions and convert them to appropriate error codes.
For cases where non-throwing allocation is required, use
sqlite3.wasm.alloc.impl(), which is direct binding of the
underlying C-level allocator.
Design note: this function is not named "malloc" primarily
because Emscripten uses that name and we wanted to avoid any
confusion early on in this code's development, when it still
had close ties to Emscripten's glue code.
*/
alloc: undefined/*installed later*/,
/**
The API's one single point of access to the WASM-side memory
deallocator. Works like free(3) (and is likely bound to
free()).
Design note: this function is not named "free" for the same
reason that this.alloc() is not called this.malloc().
*/
dealloc: undefined/*installed later*/
@@ -741,7 +718,9 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
wasm.allocFromTypedArray = function(srcTypedArray){
affirmBindableTypedArray(srcTypedArray);
const pRet = wasm.alloc(srcTypedArray.byteLength || 1);
wasm.heapForSize(srcTypedArray.constructor).set(srcTypedArray.byteLength ? srcTypedArray : [0], pRet);
wasm.heapForSize(srcTypedArray.constructor).set(
srcTypedArray.byteLength ? srcTypedArray : [0], pRet
);
return pRet;
};
@@ -752,13 +731,13 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
if(!(f instanceof Function)) toss3("Missing required exports[",key,"] function.");
}
wasm.alloc = function(n){
const m = wasm.exports[keyAlloc](n);
if(!m) throw new WasmAllocError("Failed to allocate "+n+" bytes.");
wasm.alloc = function f(n){
const m = f.impl(n);
if(!m) throw new WasmAllocError("Failed to allocate",n," bytes.");
return m;
};
wasm.dealloc = (m)=>wasm.exports[keyDealloc](m);
wasm.alloc.impl = wasm.exports[keyAlloc];
wasm.dealloc = wasm.exports[keyDealloc];
/**
Reports info about compile-time options using
@@ -858,6 +837,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
the lines of sqlite3_prepare_v3(). The slightly problematic
part is the final argument (text destructor). */
],
["sqlite3_busy_timeout","int", "sqlite3*", "int"],
["sqlite3_close_v2", "int", "sqlite3*"],
["sqlite3_changes", "int", "sqlite3*"],
["sqlite3_clear_bindings","int", "sqlite3_stmt*"],
@@ -1267,17 +1247,24 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
};
/**
Serializes the given `sqlite3*` pointer to a Uint8Array, as per
sqlite3_serialize(). On success it returns a Uint8Array. On
error it throws with a description of the problem.
A convenience wrapper around sqlite3_serialize() which serializes
the given `sqlite3*` pointer to a Uint8Array.
On success it returns a Uint8Array. If the schema is empty, an
empty array is returned.
`schema` is the schema to serialize. It may be a WASM C-string
pointer or a JS string. If it is falsy, it defaults to `"main"`.
On error it throws with a description of the problem.
*/
capi.sqlite3_js_db_export = function(pDb){
capi.sqlite3_js_db_export = function(pDb, schema=0){
if(!pDb) toss3('Invalid sqlite3* argument.');
if(!wasm.bigIntEnabled) toss3('BigInt64 support is not enabled.');
const stack = wasm.pstack.pointer;
const scope = wasm.scopedAllocPush();
let pOut;
try{
const pSize = wasm.pstack.alloc(8/*i64*/ + wasm.ptrSizeof);
const pSize = wasm.scopedAlloc(8/*i64*/ + wasm.ptrSizeof);
const ppOut = pSize + 8;
/**
Maintenance reminder, since this cost a full hour of grief
@@ -1286,8 +1273,11 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
export reads a garbage size because it's not on an 8-byte
memory boundary!
*/
const zSchema = schema
? (wasm.isPtr(schema) ? schema : wasm.scopedAllocCString(''+schema))
: 0;
let rc = wasm.exports.sqlite3_wasm_db_serialize(
pDb, ppOut, pSize, 0
pDb, zSchema, ppOut, pSize, 0
);
if(rc){
toss3("Database serialization failed with code",
@@ -1301,7 +1291,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
return rc;
}finally{
if(pOut) wasm.exports.sqlite3_free(pOut);
wasm.pstack.restore(stack);
wasm.scopedAllocPop(scope);
}
};
@@ -1333,21 +1323,21 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
/**
Internal helper for sqlite3_js_kvvfs_clear() and friends.
Its argument should be one of ('local','session','').
Its argument should be one of ('local','session',"").
*/
const __kvvfsInfo = function(which){
const rc = Object.create(null);
rc.prefix = 'kvvfs-'+which;
rc.stores = [];
if('session'===which || ''===which) rc.stores.push(self.sessionStorage);
if('local'===which || ''===which) rc.stores.push(self.localStorage);
if('session'===which || ""===which) rc.stores.push(self.sessionStorage);
if('local'===which || ""===which) rc.stores.push(self.localStorage);
return rc;
};
/**
Clears all storage used by the kvvfs DB backend, deleting any
DB(s) stored there. Its argument must be either 'session',
'local', or ''. In the first two cases, only sessionStorage
'local', or "". In the first two cases, only sessionStorage
resp. localStorage is cleared. If it's an empty string (the
default) then both are cleared. Only storage keys which match
the pattern used by kvvfs are cleared: any other client-side
@@ -1357,7 +1347,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
Returns the number of entries cleared.
*/
capi.sqlite3_js_kvvfs_clear = function(which=''){
capi.sqlite3_js_kvvfs_clear = function(which=""){
let rc = 0;
const kvinfo = __kvvfsInfo(which);
kvinfo.stores.forEach((s)=>{
@@ -1377,7 +1367,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
This routine guesses the approximate amount of
window.localStorage and/or window.sessionStorage in use by the
kvvfs database backend. Its argument must be one of
('session', 'local', ''). In the first two cases, only
('session', 'local', ""). In the first two cases, only
sessionStorage resp. localStorage is counted. If it's an empty
string (the default) then both are counted. Only storage keys
which match the pattern used by kvvfs are counted. The returned
@@ -1390,7 +1380,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
those limits are unspecified and may include per-entry
overhead invisible to clients.
*/
capi.sqlite3_js_kvvfs_size = function(which=''){
capi.sqlite3_js_kvvfs_size = function(which=""){
let sz = 0;
const kvinfo = __kvvfsInfo(which);
kvinfo.stores.forEach((s)=>{
+2 -2
View File
@@ -429,7 +429,7 @@ sqlite3.initWorker1API = function(){
};
const isSpecialDbFilename = (n)=>{
return ''===n || ':'===n[0];
return ""===n || ':'===n[0];
};
/**
@@ -453,7 +453,7 @@ sqlite3.initWorker1API = function(){
let byteArray, pVfs;
oargs.vfs = args.vfs;
if(isSpecialDbFilename(args.filename)){
oargs.filename = args.filename || '';
oargs.filename = args.filename || "";
}else{
oargs.filename = args.filename;
byteArray = args.byteArray;
+189 -63
View File
@@ -29,6 +29,22 @@
This file represents an implementation detail of a larger piece of
code, and not a public interface. Its details may change at any time
and are not intended to be used by any client-level code.
2022-11-27: Chrome v108 changes some async methods to synchronous, as
documented at:
https://developer.chrome.com/blog/sync-methods-for-accesshandles/
We cannot change to the sync forms at this point without breaking
clients who use Chrome v104-ish or higher. truncate(), getSize(),
flush(), and close() are now (as of v108) synchronous. Calling them
with an "await", as we have to for the async forms, is still legal
with the sync forms but is superfluous. Calling the async forms with
theFunc().then(...) is not compatible with the change to
synchronous, but we do do not use those APIs that way. i.e. we don't
_need_ to change anything for this, but at some point (after Chrome
versions (approximately) 104-107 are extinct) should change our
usage of those methods to remove the "await".
*/
"use strict";
const toss = function(...args){throw new Error(args.join(' '))};
@@ -44,6 +60,7 @@ if(self.window === self){
this API.
*/
const state = Object.create(null);
/**
verbose:
@@ -52,7 +69,7 @@ const state = Object.create(null);
2 = warnings and errors
3 = debug, warnings, and errors
*/
state.verbose = 2;
state.verbose = 1;
const loggers = {
0:console.error.bind(console),
@@ -96,13 +113,27 @@ metrics.dump = ()=>{
};
/**
Map of sqlite3_file pointers (integers) to metadata related to a
given OPFS file handles. The pointers are, in this side of the
interface, opaque file handle IDs provided by the synchronous
part of this constellation. Each value is an object with a structure
demonstrated in the xOpen() impl.
__openFiles is a map of sqlite3_file pointers (integers) to
metadata related to a given OPFS file handles. The pointers are, in
this side of the interface, opaque file handle IDs provided by the
synchronous part of this constellation. Each value is an object
with a structure demonstrated in the xOpen() impl.
*/
const __openFiles = Object.create(null);
/**
__implicitLocks is a Set of sqlite3_file pointers (integers) which were
"auto-locked". i.e. those for which we obtained a sync access
handle without an explicit xLock() call. Such locks will be
released during db connection idle time, whereas a sync access
handle obtained via xLock(), or subsequently xLock()'d after
auto-acquisition, will not be released until xUnlock() is called.
Maintenance reminder: if we relinquish auto-locks at the end of the
operation which acquires them, we pay a massive performance
penalty: speedtest1 benchmarks take up to 4x as long. By delaying
the lock release until idle time, the hit is negligible.
*/
const __implicitLocks = new Set();
/**
Expects an OPFS file path. It gets resolved, such that ".."
@@ -135,6 +166,71 @@ const getDirForFilename = async function f(absFilename, createDirs = false){
return [dh, filename];
};
/**
If the given file-holding object has a sync handle attached to it,
that handle is remove and asynchronously closed. Though it may
sound sensible to continue work as soon as the close() returns
(noting that it's asynchronous), doing so can cause operations
performed soon afterwards, e.g. a call to getSyncHandle() to fail
because they may happen out of order from the close(). OPFS does
not guaranty that the actual order of operations is retained in
such cases. i.e. always "await" on the result of this function.
*/
const closeSyncHandle = async (fh)=>{
if(fh.syncHandle){
log("Closing sync handle for",fh.filenameAbs);
const h = fh.syncHandle;
delete fh.syncHandle;
delete fh.xLock;
__implicitLocks.delete(fh.fid);
return h.close();
}
};
/**
A proxy for closeSyncHandle() which is guaranteed to not throw.
This function is part of a lock/unlock step in functions which
require a sync access handle but may be called without xLock()
having been called first. Such calls need to release that
handle to avoid locking the file for all of time. This is an
_attempt_ at reducing cross-tab contention but it may prove
to be more of a problem than a solution and may need to be
removed.
*/
const closeSyncHandleNoThrow = async (fh)=>{
try{await closeSyncHandle(fh)}
catch(e){
warn("closeSyncHandleNoThrow() ignoring:",e,fh);
}
};
/* Release all auto-locks. */
const releaseImplicitLocks = async ()=>{
if(__implicitLocks.size){
/* Release all auto-locks. */
for(const fid of __implicitLocks){
const fh = __openFiles[fid];
await closeSyncHandleNoThrow(fh);
log("Auto-unlocked",fid,fh.filenameAbs);
}
}
};
/**
An experiment in improving concurrency by freeing up implicit locks
sooner. This is known to impact performance dramatically but it has
also shown to improve concurrency considerably.
If fh.releaseImplicitLocks is truthy and fh is in __implicitLocks,
this routine returns closeSyncHandleNoThrow(), else it is a no-op.
*/
const releaseImplicitLock = async (fh)=>{
if(fh.releaseImplicitLocks && __implicitLocks.has(fh.fid)){
return closeSyncHandleNoThrow(fh);
}
};
/**
An error class specifically for use with getSyncHandle(), the goal
of which is to eventually be able to distinguish unambiguously
@@ -153,7 +249,22 @@ class GetSyncHandleError extends Error {
this.name = 'GetSyncHandleError';
}
};
GetSyncHandleError.convertRc = (e,rc)=>{
if(0){
/* This approach makes the very wild assumption that such a
failure _is_ a locking error. In practice that appears to be
the most common error, by far, but we cannot unambiguously
distinguish that from other errors.
This approach is highly questionable.
*/
return (e instanceof GetSyncHandleError)
? state.sq3Codes.SQLITE_IOERR_LOCK
: rc;
}else{
return rc;
}
}
/**
Returns the sync access handle associated with the given file
handle object (which must be a valid handle object, as created by
@@ -165,11 +276,11 @@ class GetSyncHandleError extends Error {
still fails at that point it will give up and propagate the
exception.
*/
const getSyncHandle = async (fh)=>{
const getSyncHandle = async (fh,opName)=>{
if(!fh.syncHandle){
const t = performance.now();
log("Acquiring sync handle for",fh.filenameAbs);
const maxTries = 4, msBase = 300;
const maxTries = 6, msBase = 300;
let i = 1, ms = msBase;
for(; true; ms = msBase * ++i){
try {
@@ -181,39 +292,26 @@ const getSyncHandle = async (fh)=>{
}catch(e){
if(i === maxTries){
throw new GetSyncHandleError(
e, "Error getting sync handle.",maxTries,
e, "Error getting sync handle for",opName+"().",maxTries,
"attempts failed.",fh.filenameAbs
);
}
warn("Error getting sync handle. Waiting",ms,
"ms and trying again.",fh.filenameAbs,e);
warn("Error getting sync handle for",opName+"(). Waiting",ms,
"ms and trying again.",fh.filenameAbs,e);
//await releaseImplicitLocks();
Atomics.wait(state.sabOPView, state.opIds.retry, 0, ms);
}
}
log("Got sync handle for",fh.filenameAbs,'in',performance.now() - t,'ms');
log("Got",opName+"() sync handle for",fh.filenameAbs,
'in',performance.now() - t,'ms');
if(!fh.xLock){
__implicitLocks.add(fh.fid);
log("Auto-locked for",opName+"()",fh.fid,fh.filenameAbs);
}
}
return fh.syncHandle;
};
/**
If the given file-holding object has a sync handle attached to it,
that handle is remove and asynchronously closed. Though it may
sound sensible to continue work as soon as the close() returns
(noting that it's asynchronous), doing so can cause operations
performed soon afterwards, e.g. a call to getSyncHandle() to fail
because they may happen out of order from the close(). OPFS does
not guaranty that the actual order of operations is retained in
such cases. i.e. always "await" on the result of this function.
*/
const closeSyncHandle = async (fh)=>{
if(fh.syncHandle){
log("Closing sync handle for",fh.filenameAbs);
const h = fh.syncHandle;
delete fh.syncHandle;
return h.close();
}
};
/**
Stores the given value at state.sabOPView[state.opIds.rc] and then
Atomics.notify()'s it.
@@ -342,9 +440,10 @@ const vfsAsyncImpls = {
xClose: async function(fid/*sqlite3_file pointer*/){
const opName = 'xClose';
mTimeStart(opName);
__implicitLocks.delete(fid);
const fh = __openFiles[fid];
let rc = 0;
wTimeStart('xClose');
wTimeStart(opName);
if(fh){
delete __openFiles[fid];
await closeSyncHandle(fh);
@@ -406,13 +505,14 @@ const vfsAsyncImpls = {
wTimeStart('xFileSize');
try{
affirmLocked('xFileSize',fh);
rc = await (await getSyncHandle(fh)).getSize();
state.s11n.serialize(Number(rc));
const sz = await (await getSyncHandle(fh,'xFileSize')).getSize();
state.s11n.serialize(Number(sz));
rc = 0;
}catch(e){
state.s11n.storeException(2,e);
rc = state.sq3Codes.SQLITE_IOERR;
rc = GetSyncHandleError.convertRc(e,state.sq3Codes.SQLITE_IOERR);
}
await releaseImplicitLock(fh);
wTimeEnd();
storeAndNotify('xFileSize', rc);
mTimeEnd();
@@ -422,12 +522,17 @@ const vfsAsyncImpls = {
mTimeStart('xLock');
const fh = __openFiles[fid];
let rc = 0;
const oldLockType = fh.xLock;
fh.xLock = lockType;
if( !fh.syncHandle ){
wTimeStart('xLock');
try { await getSyncHandle(fh) }
catch(e){
try {
await getSyncHandle(fh,'xLock');
__implicitLocks.delete(fid);
}catch(e){
state.s11n.storeException(1,e);
rc = state.sq3Codes.SQLITE_IOERR_LOCK;
rc = GetSyncHandleError.convertRc(e,state.sq3Codes.SQLITE_IOERR_LOCK);
fh.xLock = oldLockType;
}
wTimeEnd();
}
@@ -435,10 +540,10 @@ const vfsAsyncImpls = {
mTimeEnd();
},
xOpen: async function(fid/*sqlite3_file pointer*/, filename,
flags/*SQLITE_OPEN_...*/){
flags/*SQLITE_OPEN_...*/,
opfsFlags/*OPFS_...*/){
const opName = 'xOpen';
mTimeStart(opName);
const deleteOnClose = (state.sq3Codes.SQLITE_OPEN_DELETEONCLOSE & flags);
const create = (state.sq3Codes.SQLITE_OPEN_CREATE & flags);
wTimeStart('xOpen');
try{
@@ -453,14 +558,9 @@ const vfsAsyncImpls = {
return;
}
const hFile = await hDir.getFileHandle(filenamePart, {create});
/**
wa-sqlite, at this point, grabs a SyncAccessHandle and
assigns it to the syncHandle prop of the file state
object, but only for certain cases and it's unclear why it
places that limitation on it.
*/
wTimeEnd();
__openFiles[fid] = Object.assign(Object.create(null),{
const fh = Object.assign(Object.create(null),{
fid: fid,
filenameAbs: filename,
filenamePart: filenamePart,
dirHandle: hDir,
@@ -468,8 +568,26 @@ const vfsAsyncImpls = {
sabView: state.sabFileBufView,
readOnly: create
? false : (state.sq3Codes.SQLITE_OPEN_READONLY & flags),
deleteOnClose: deleteOnClose
deleteOnClose: !!(state.sq3Codes.SQLITE_OPEN_DELETEONCLOSE & flags)
});
fh.releaseImplicitLocks =
(opfsFlags & state.opfsFlags.OPFS_UNLOCK_ASAP)
|| state.opfsFlags.defaultUnlockAsap;
if(0 /* this block is modelled after something wa-sqlite
does but it leads to immediate contention on journal files. */
&& (0===(flags & state.sq3Codes.SQLITE_OPEN_MAIN_DB))){
/* sqlite does not lock these files, so go ahead and grab an OPFS
lock.
https://www.sqlite.org/uri.html
*/
fh.xLock = "xOpen"/* Truthy value to keep entry from getting
flagged as auto-locked. String value so
that we can easily distinguish is later
if needed. */;
await getSyncHandle(fh,'xOpen');
}
__openFiles[fid] = fh;
storeAndNotify(opName, 0);
}catch(e){
wTimeEnd();
@@ -486,7 +604,7 @@ const vfsAsyncImpls = {
try{
affirmLocked('xRead',fh);
wTimeStart('xRead');
nRead = (await getSyncHandle(fh)).read(
nRead = (await getSyncHandle(fh,'xRead')).read(
fh.sabView.subarray(0, n),
{at: Number(offset64)}
);
@@ -499,8 +617,9 @@ const vfsAsyncImpls = {
if(undefined===nRead) wTimeEnd();
error("xRead() failed",e,fh);
state.s11n.storeException(1,e);
rc = state.sq3Codes.SQLITE_IOERR_READ;
rc = GetSyncHandleError.convertRc(e,state.sq3Codes.SQLITE_IOERR_READ);
}
await releaseImplicitLock(fh);
storeAndNotify('xRead',rc);
mTimeEnd();
},
@@ -529,12 +648,13 @@ const vfsAsyncImpls = {
try{
affirmLocked('xTruncate',fh);
affirmNotRO('xTruncate', fh);
await (await getSyncHandle(fh)).truncate(size);
await (await getSyncHandle(fh,'xTruncate')).truncate(size);
}catch(e){
error("xTruncate():",e,fh);
state.s11n.storeException(2,e);
rc = state.sq3Codes.SQLITE_IOERR_TRUNCATE;
rc = GetSyncHandleError.convertRc(e,state.sq3Codes.SQLITE_IOERR_TRUNCATE);
}
await releaseImplicitLock(fh);
wTimeEnd();
storeAndNotify('xTruncate',rc);
mTimeEnd();
@@ -566,15 +686,16 @@ const vfsAsyncImpls = {
affirmLocked('xWrite',fh);
affirmNotRO('xWrite', fh);
rc = (
n === (await getSyncHandle(fh))
n === (await getSyncHandle(fh,'xWrite'))
.write(fh.sabView.subarray(0, n),
{at: Number(offset64)})
) ? 0 : state.sq3Codes.SQLITE_IOERR_WRITE;
}catch(e){
error("xWrite():",e,fh);
state.s11n.storeException(1,e);
rc = state.sq3Codes.SQLITE_IOERR_WRITE;
rc = GetSyncHandleError.convertRc(e,state.sq3Codes.SQLITE_IOERR_WRITE);
}
await releaseImplicitLock(fh);
wTimeEnd();
storeAndNotify('xWrite',rc);
mTimeEnd();
@@ -610,7 +731,7 @@ const initS11n = ()=>{
default: toss("Invalid type ID:",tid);
}
};
state.s11n.deserialize = function(){
state.s11n.deserialize = function(clear=false){
++metrics.s11n.deserialize.count;
const t = performance.now();
const argc = viewU8[0];
@@ -635,6 +756,7 @@ const initS11n = ()=>{
rc.push(v);
}
}
if(clear) viewU8[0] = 0;
//log("deserialize:",argc, rc);
metrics.s11n.deserialize.time += performance.now() - t;
return rc;
@@ -678,7 +800,7 @@ const initS11n = ()=>{
state.s11n.storeException = state.asyncS11nExceptions
? ((priority,e)=>{
if(priority<=state.asyncS11nExceptions){
state.s11n.serialize([e.name,': ',e.message].join(''));
state.s11n.serialize([e.name,': ',e.message].join(""));
}
})
: ()=>{};
@@ -699,23 +821,26 @@ const waitLoop = async function f(){
/**
waitTime is how long (ms) to wait for each Atomics.wait().
We need to wake up periodically to give the thread a chance
to do other things.
to do other things. If this is too high (e.g. 500ms) then
even two workers/tabs can easily run into locking errors.
*/
const waitTime = 1000;
const waitTime = 100;
while(!flagAsyncShutdown){
try {
if('timed-out'===Atomics.wait(
state.sabOPView, state.opIds.whichOp, 0, waitTime
)){
await releaseImplicitLocks();
continue;
}
const opId = Atomics.load(state.sabOPView, state.opIds.whichOp);
Atomics.store(state.sabOPView, state.opIds.whichOp, 0);
const hnd = opHandlers[opId] ?? toss("No waitLoop handler for whichOp #",opId);
const args = state.s11n.deserialize() || [];
state.s11n.serialize(/* clear s11n to keep the caller from
confusing this with an exception string
written by the upcoming operation */);
const args = state.s11n.deserialize(
true /* clear s11n to keep the caller from confusing this with
an exception string written by the upcoming
operation */
) || [];
//warn("waitLoop() whichOp =",opId, hnd, args);
if(hnd.f) await hnd.f(...args);
else error("Missing callback for opId",opId);
@@ -735,7 +860,7 @@ navigator.storage.getDirectory().then(function(d){
const opt = data.args;
state.littleEndian = opt.littleEndian;
state.asyncS11nExceptions = opt.asyncS11nExceptions;
state.verbose = opt.verbose ?? 2;
state.verbose = opt.verbose ?? 1;
state.fileBufferSize = opt.fileBufferSize;
state.sabS11nOffset = opt.sabS11nOffset;
state.sabS11nSize = opt.sabS11nSize;
@@ -746,6 +871,7 @@ navigator.storage.getDirectory().then(function(d){
state.sabS11nView = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize);
state.opIds = opt.opIds;
state.sq3Codes = opt.sq3Codes;
state.opfsFlags = opt.opfsFlags;
Object.keys(vfsAsyncImpls).forEach((k)=>{
if(!Number.isFinite(state.opIds[k])){
toss("Maintenance required: missing state.opIds[",k,"]");
+12 -13
View File
@@ -67,7 +67,7 @@
** larger cache benefits the larger workloads. Speed differences
** between 2x and nearly 3x have been measured with ample page cache.
*/
# define SQLITE_DEFAULT_CACHE_SIZE -16777216
# define SQLITE_DEFAULT_CACHE_SIZE -16384
#endif
#if 0 && !defined(SQLITE_DEFAULT_PAGE_SIZE)
/* TODO: experiment with this. */
@@ -916,25 +916,27 @@ int sqlite3_wasm_db_export_chunked( sqlite3* pDb,
}
/*
** A proxy for sqlite3_serialize() which serializes the "main" schema
** A proxy for sqlite3_serialize() which serializes the schema zSchema
** of pDb, placing the serialized output in pOut and nOut. nOut may be
** NULL. If pDb or pOut are NULL then SQLITE_MISUSE is returned. If
** allocation of the serialized copy fails, SQLITE_NOMEM is returned.
** On success, 0 is returned and `*pOut` will contain a pointer to the
** memory unless mFlags includes SQLITE_SERIALIZE_NOCOPY and the
** database has no contiguous memory representation, in which case
** `*pOut` will be NULL but 0 will be returned.
** NULL. If zSchema is NULL then "main" is assumed. If pDb or pOut are
** NULL then SQLITE_MISUSE is returned. If allocation of the
** serialized copy fails, SQLITE_NOMEM is returned. On success, 0 is
** returned and `*pOut` will contain a pointer to the memory unless
** mFlags includes SQLITE_SERIALIZE_NOCOPY and the database has no
** contiguous memory representation, in which case `*pOut` will be
** NULL but 0 will be returned.
**
** If `*pOut` is not NULL, the caller is responsible for passing it to
** sqlite3_free() to free it.
*/
SQLITE_WASM_KEEP
int sqlite3_wasm_db_serialize( sqlite3 *pDb, unsigned char **pOut,
int sqlite3_wasm_db_serialize( sqlite3 *pDb, const char *zSchema,
unsigned char **pOut,
sqlite3_int64 *nOut, unsigned int mFlags ){
unsigned char * z;
if( !pDb || !pOut ) return SQLITE_MISUSE;
if(nOut) *nOut = 0;
z = sqlite3_serialize(pDb, "main", nOut, mFlags);
z = sqlite3_serialize(pDb, zSchema ? zSchema : "main", nOut, mFlags);
if( z || (SQLITE_SERIALIZE_NOCOPY & mFlags) ){
*pOut = z;
return 0;
@@ -1108,9 +1110,6 @@ int sqlite3_wasm_init_wasmfs(const char *zMountPoint){
/** It's not enough to instantiate the backend. We have to create a
mountpoint in the VFS and attach the backend to it. */
if( pOpfs && 0!=access(zMountPoint, F_OK) ){
/* mkdir() simply hangs when called from fiddle app. Cause is
not yet determined but the hypothesis is an init-order
issue. */
/* Note that this check and is not robust but it will
hypothetically suffice for the transient wasm-based virtual
filesystem we're currently running in. */
+1525
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1101,7 +1101,7 @@ self.WhWasmUtilInstaller = function(target){
property and a forEach() method. A block of memory list.length
entries long is allocated and each pointer-sized block of that
memory is populated with a scopedAllocCString() conversion of the
(''+value) of each element. Returns a pointer to the start of the
(""+value) of each element. Returns a pointer to the start of the
list, suitable for passing as the 2nd argument to a C-style
main() function.
+33 -27
View File
@@ -6,35 +6,38 @@
# 'make dist' rules for creating a distribution archive of the WASM/JS
# pieces, noting that we only build a dist of the built files, not the
# numerous pieces required to build them.
#
# Use 'make snapshot' to create "snapshot" releases. They use a
# distinctly different zip file and top directory name to distinguish
# them from release builds.
#######################################################################
MAKEFILE.dist := $(lastword $(MAKEFILE_LIST))
########################################################################
# Chicken/egg situation: we need $(bin.version-info) to get the version
# info for the archive name, but that binary may not yet be built, and
# won't be built until we expand the dependencies. We have to use a
# temporary name for the archive.
dist-name = sqlite-wasm-TEMP
#ifeq (0,1)
# $(info WARNING *******************************************************************)
# $(info ** Be sure to create the desired build configuration before creating the)
# $(info ** distribution archive. Use one of the following targets to do so:)
# $(info **)
# $(info ** o2: builds with -O2, resulting in the fastest builds)
# $(info ** oz: builds with -Oz, resulting in the smallest builds)
# $(info /WARNING *******************************************************************)
#endif
# Chicken/egg situation: we need $(bin.version-info) to get the
# version info for the archive name, but that binary may not yet be
# built, and won't be built until we expand the dependencies. Thus we
# have to use a temporary name for the archive until we can get
# that binary built.
ifeq (,$(filter snapshot,$(MAKECMDGOALS)))
dist-name-prefix := sqlite-wasm
else
dist-name-prefix := sqlite-wasm-snapshot-$(shell /usr/bin/date +%Y%m%d)
endif
dist-name := $(dist-name-prefix)-TEMP
########################################################################
# dist.build must be the name of a target which triggers the
# build of the files to be packed into the dist archive. The
# intention is that it be one of (o0, o1, o2, o3, os, oz), each of
# which uses like-named -Ox optimization level flags. The o2 target
# provides the best overall runtime speeds. The oz target provides
# slightly slower speeds (roughly 10%) with significantly smaller WASM
# file sizes. Note that -O2 (the o2 target) results in faster binaries
# than both -O3 and -Os (the o3 and os targets) in all tests run to
# date.
# dist.build must be the name of a target which triggers the build of
# the files to be packed into the dist archive. The intention is that
# it be one of (o0, o1, o2, o3, os, oz), each of which uses like-named
# -Ox optimization level flags. The o2 target provides the best
# overall runtime speeds. The oz target provides slightly slower
# speeds (roughly 10%) with significantly smaller WASM file
# sizes. Note that -O2 (the o2 target) results in faster binaries than
# both -O3 and -Os (the o3 and os targets) in all tests run to
# date. Our general policy is that we want the smallest binaries for
# dist zip files, so use the oz build unless there is a compelling
# reason not to.
dist.build ?= oz
dist-dir.top := $(dist-name)
@@ -42,7 +45,8 @@ dist-dir.jswasm := $(dist-dir.top)/$(notdir $(dir.dout))
dist-dir.common := $(dist-dir.top)/common
dist.top.extras := \
demo-123.html demo-123-worker.html demo-123.js \
tester1.html tester1-worker.html tester1.js \
tester1.html tester1-worker.html tester1-esm.html \
tester1.js tester1.mjs \
demo-jsstorage.html demo-jsstorage.js \
demo-worker1.html demo-worker1.js \
demo-worker1-promiser.html demo-worker1-promiser.js
@@ -51,7 +55,7 @@ dist.common.extras := \
$(wildcard $(dir.common)/*.css) \
$(dir.common)/SqliteTestUtil.js
.PHONY: dist
.PHONY: dist snapshot
########################################################################
# dist: create the end-user deliverable archive.
#
@@ -78,10 +82,12 @@ dist: \
@cp -p $(dist.jswasm.extras) $(dist-dir.jswasm)
@$(bin.stripccomments) -k -k < $(sqlite3.js) \
> $(dist-dir.jswasm)/$(notdir $(sqlite3.js))
@$(bin.stripccomments) -k -k < $(sqlite3.mjs) \
> $(dist-dir.jswasm)/$(notdir $(sqlite3.mjs))
@cp -p $(dist.common.extras) $(dist-dir.common)
@set -e; \
vnum=$$($(bin.version-info) --download-version); \
vdir=sqlite-wasm-$$vnum; \
vdir=$(dist-name-prefix)-$$vnum; \
arczip=$$vdir.zip; \
echo "Making $$arczip ..."; \
rm -fr $$arczip $$vdir; \
@@ -91,7 +97,7 @@ dist: \
ls -la $$arczip; \
set +e; \
unzip -lv $$arczip || echo "Missing unzip app? Not fatal."
snapshot: dist
# We need a separate `clean` rule to account for weirdness in
# a sub-make, where we get a copy of the $(dist-name) dir
# copied into the new $(dist-name) dir.
+3 -3
View File
@@ -58,12 +58,12 @@ fiddle.SOAP.js := $(dir.fiddle)/$(notdir $(SOAP.js))
$(fiddle.SOAP.js): $(SOAP.js)
cp $< $@
$(eval $(call call-make-pre-js,fiddle-module))
$(eval $(call call-make-pre-js,fiddle-module,vanilla))
$(fiddle-module.js): $(MAKEFILE) $(MAKEFILE.fiddle) \
$(EXPORTED_FUNCTIONS.fiddle) \
$(fiddle.cses) $(pre-post-fiddle-module.deps) $(fiddle.SOAP.js)
$(fiddle.cses) $(pre-post-fiddle-module.deps.vanilla) $(fiddle.SOAP.js)
$(emcc.bin) -o $@ $(fiddle.emcc-flags) \
$(pre-post-common.flags) $(pre-post-fiddle-module.flags) \
$(pre-post-fiddle-module.flags.vanilla) \
$(fiddle.cses)
$(maybe-wasm-strip) $(fiddle-module.wasm)
gzip < $@ > $@.gz
+10
View File
@@ -56,6 +56,16 @@
utility code.</li>
<li><a href='tester1-worker.html'>tester1-worker</a>: same thing
but running in a Worker.</li>
<li><a href='tester1-esm.html'>tester1-esm</a>: same as
<code>tester1</code> but loads sqlite3 in the main thread via
an ES6 module.
</li>
<li><a href='tester1-worker.html?esm'>tester1-worker?esm</a>:
same as <code>tester1-esm</code> but loads a Worker Module which
then loads the sqlite3 API via an ES6 module. Note that
not all browsers permit loading modules in Worker
threads.
</li>
</ul>
</li>
<li>Higher-level apps and demos...
+14 -5
View File
@@ -32,10 +32,6 @@
and testing is currently done against a dev-channel release
of Chrome (v107 as of 2022-09-26).
</li>
<li>Whether or not WASMFS/OPFS support is enabled on any given
page may depend on build-time options which are <em>off by
default</em>.
</li>
</ul>
</div>
<div>The tests and demos...
@@ -46,7 +42,17 @@
regression tests for the various APIs and surrounding
utility code.</li>
<li><a href='tester1-worker.html'>tester1-worker</a>: same thing
but running in a Worker.</li>
but running in a Worker.</li>
<li><a href='tester1-esm.html'>tester1-esm</a>: same as
<code>tester1</code> but loads sqlite3 in the main thread via
an ES6 module.
</li>
<li><a href='tester1-worker.html?esm'>tester1-worker?esm</a>:
same as <code>tester1-esm</code> but loads a Worker Module which
then loads the sqlite3 API via an ES6 module. Note that
not all browsers permit loading modules in Worker
threads.
</li>
</ul>
</li>
<li>High-level apps and demos...
@@ -98,6 +104,9 @@
synchronous sqlite3_vfs interface and the async OPFS
impl.
</li>
<li><a href='tests/opfs/concurrency/index.html'>OPFS concurrency</a>
tests using multiple workers.
</li>
</ul>
</li>
<!--li><a href='x.html'></a></li-->
+1 -1
View File
@@ -361,7 +361,7 @@ self.Jaccwabyt = function StructBinderFactory(config){
framework's native format or in Emscripten format.
*/
const __memberSignature = function f(obj,memberName,emscriptenFormat=false){
if(!f._) f._ = (x)=>x.replace(/[^vipPsjrd]/g,'').replace(/[pPs]/g,'i');
if(!f._) f._ = (x)=>x.replace(/[^vipPsjrd]/g,"").replace(/[pPs]/g,'i');
const m = __lookupMember(obj.structInfo, memberName, true);
return emscriptenFormat ? f._(m.signature) : m.signature;
};
+1 -1
View File
@@ -189,7 +189,7 @@
<div id='list-compile-options' class='pseudolist wide2'></div>
</div><!-- .initially-hidden -->
<script src="jswasm/sqlite3.js">/* This tag MUST be in side the
<script src="jswasm/sqlite3.js">/* This tag MUST be inside the
fossil-doc block so that this part can work without modification in
the wasm docs repo. */</script>
<script>(async function(){
+5 -2
View File
@@ -169,7 +169,11 @@
return str+a.join('&nbsp;');
};
const W = new Worker("speedtest1-worker.js?sqlite3.dir=jswasm");
const urlParams = new URL(self.location.href).searchParams;
const W = new Worker(
"speedtest1-worker.js?sqlite3.dir=jswasm"+
(urlParams.has('opfs-verbose') ? '&opfs-verbose' : '')
);
const mPost = function(msgType,payload){
W.postMessage({type: msgType, data: payload});
};
@@ -179,7 +183,6 @@
const eLinkMainThread = E('#link-main-thread');
const eLinkWasmfs = E('#link-wasmfs');
const eLinkKvvfs = E('#link-kvvfs');
const urlParams = new URL(self.location.href).searchParams;
const getSelectedFlags = ()=>{
const f = Array.prototype.map.call(eFlags.selectedOptions, (v)=>v.value);
[
+16 -3
View File
@@ -6,7 +6,7 @@
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
<link rel="stylesheet" href="../common/emscripten.css"/>
<link rel="stylesheet" href="../common/testing.css"/>
<title>sqlite3 tester #1 (Worker thread)</title>
<title>sqlite3 tester #1: Worker thread</title>
<style>
body {
font-family: monospace;
@@ -14,7 +14,7 @@
</style>
</head>
<body>
<h1 id='color-target'>sqlite3 WASM/JS tester #1 (Worker thread)</h1>
<h1 id='color-target'>sqlite3 tester #1: Worker thread</h1>
<div>See <a href='tester1.html' target='tester1.html'>tester1.html</a>
for the UI-thread variant.</div>
<div class='input-wrapper'>
@@ -40,7 +40,20 @@
};
cbReverse.addEventListener('change',cbReverseIt,true);
cbReverseIt();
const w = new Worker("tester1.js?sqlite3.dir=jswasm");
const urlParams = new URL(self.location.href).searchParams;
const workerArgs = [];
if(urlParams.has('esm')){
logHtml('warning',"Attempting to run an ES6 Worker Module, "+
"which is not supported by all browsers! "+
"e.g. Firefox (as of 2022-11) cannot do this.");
workerArgs.push("tester1.mjs",{type:"module"});
document.querySelectorAll('title,#color-target').forEach((e)=>{
e.innerText = "sqlite3 tester #1: ES6 Worker Module";
});
}else{
workerArgs.push("tester1.js?sqlite3.dir=jswasm");
}
const w = new Worker(...workerArgs);
w.onmessage = function({data}){
switch(data.type){
case 'log':
@@ -6,7 +6,13 @@
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
<link rel="stylesheet" href="common/emscripten.css"/>
<link rel="stylesheet" href="common/testing.css"/>
<title>sqlite3 tester #1 (UI thread)</title>
<title>sqlite3 tester #1:
//#if target=es6-module
ES6 Module in UI thread
//#else
UI thread
//#endif
</title>
<style>
body {
font-family: monospace;
@@ -14,7 +20,7 @@
</style>
</head>
<body>
<h1 id='color-target'>sqlite3 WASM/JS tester #1 (UI thread)</h1>
<h1 id='color-target'></h1>
<div>See <a href='tester1-worker.html' target='tester1-worker.html'>tester1-worker.html</a>
for the Worker-thread variant.</div>
<div class='input-wrapper'>
@@ -22,7 +28,15 @@
<label for='cb-log-reverse'>Reverse log order?</label>
</div>
<div id='test-output'></div>
<script>(function(){
document.querySelector('h1').innerHTML =
document.querySelector('title').innerHTML;
})();</script>
//#if target=es6-module
<script src="tester1.mjs" type="module"></script>
//#else
<script src="jswasm/sqlite3.js"></script>
<script src="tester1.js"></script>
//#endif
</body>
</html>
@@ -29,8 +29,25 @@
a db in an early test and close it in a later test. Each test gets
passed the sqlite3 namespace object as its only argument.
*/
/*
This file is intended to be processed by c-pp to inject (or not)
code specific to ES6 modules which is illegal in non-module code.
Non-ES6 module build and ES6 module for the main-thread:
./c-pp -f tester1.c-pp.js -o tester1.js
ES6 worker module build:
./c-pp -f tester1.c-pp.js -o tester1-esm.js -Dtarget=es6-module
*/
//#if target=es6-module
import {default as sqlite3InitModule} from './jswasm/sqlite3.mjs';
self.sqlite3InitModule = sqlite3InitModule;
//#else
'use strict';
(function(){
//#endif
(function(self){
/**
Set up our output channel differently depending
on whether we are running in a worker thread or
@@ -342,8 +359,20 @@
throw new sqlite3.WasmAllocError;
}catch(e){
T.assert(e instanceof Error)
.assert(e instanceof sqlite3.WasmAllocError);
.assert(e instanceof sqlite3.WasmAllocError)
.assert("Allocation failed." === e.message);
}
try {
throw new sqlite3.WasmAllocError("test",{
cause: 3
});
}catch(e){
T.assert(3 === e.cause)
.assert("test" === e.message);
}
try {throw new sqlite3.WasmAllocError("test","ing",".")}
catch(e){T.assert("test ing ." === e.message)}
try{ throw new sqlite3.SQLite3Error(capi.SQLITE_SCHEMA) }
catch(e){ T.assert('SQLITE_SCHEMA' === e.message) }
try{ sqlite3.SQLite3Error.toss(capi.SQLITE_CORRUPT,{cause: true}) }
@@ -1134,6 +1163,16 @@
.assert(0 === capi.sqlite3_errmsg(db.pointer).indexOf("Invalid SQL"))
.assert(dbFile === db.dbFilename())
.assert(!db.dbFilename('nope'));
//Sanity check DB.checkRc()...
let ex;
try{db.checkRc(rc)}
catch(e){ex = e}
T.assert(ex instanceof sqlite3.SQLite3Error)
.assert(0===ex.message.indexOf("sqlite3 result code"))
.assert(ex.message.indexOf("Invalid SQL")>0);
T.assert(db === db.checkRc(0))
.assert(db === sqlite3.oo1.DB.checkRc(db,0))
.assert(null === sqlite3.oo1.DB.checkRc(null,0))
})
////////////////////////////////////////////////////////////////////
@@ -1805,7 +1844,8 @@
////////////////////////////////////////////////////////////////////////
log("Loading and initializing sqlite3 WASM module...");
if(!isUIThread()){
if(!self.sqlite3InitModule && !isUIThread()){
/* Vanilla worker, as opposed to an ES6 module worker */
/*
If sqlite3.js is in a directory other than this script, in order
to get sqlite3.js to resolve sqlite3.wasm properly, we have to
@@ -1849,4 +1889,5 @@
}
TestUtil.runTests(sqlite3);
});
})();
})(self);
@@ -0,0 +1,51 @@
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
<link rel="stylesheet" href="../../../common/testing.css"/>
<title>sqlite3 OPFS Worker concurrency tester</title>
<style>
body { display: revert; }
body > * {}
#test-output {
font-family: monospace;
}
</style>
</head>
<body>
<h1></h1>
<p>
OPFS concurrency tester using multiple independent Workers.
Disclaimer: concurrency in OPFS is currently a pain point
and timing/concurrency mitigation in this environment is
highly unpredictable!
</p>
<p>
URL flags: pass a number of workers using
the <code>workers=N</code> URL flag. Set the time between each
workload with <code>interval=N</code> (milliseconds). Set the
number of worker iterations with <code>iterations=N</code>.
Enable OPFS VFS verbosity with <code>verbose=1-3</code> (output
goes to the dev console). Enable/disable "unlock ASAP" mode
(higher concurrency, lower speed)
with <code>unlock-asap=0-1</code>.
</p>
<p>Achtung: if it does not start to do anything within a couple of
seconds, check the dev console: Chrome often fails with "cannot allocate
WasmMemory" at startup. Closing and re-opening the tab usually resolves
it.
</p>
<div class='input-wrapper'>
<input type='checkbox' id='cb-log-reverse'>
<label for='cb-log-reverse'>Reverse log order?</label>
</div>
<div id='test-output'></div>
<script>(function(){
document.querySelector('h1').innerHTML =
document.querySelector('title').innerHTML;
})();</script>
<script src="test.js?sqlite3.dir=../../../jswasm"></script>
</body>
</html>
+133
View File
@@ -0,0 +1,133 @@
(async function(self){
const logCss = (function(){
const mapToString = (v)=>{
switch(typeof v){
case 'number': case 'string': case 'boolean':
case 'undefined': case 'bigint':
return ''+v;
default: break;
}
if(null===v) return 'null';
if(v instanceof Error){
v = {
message: v.message,
stack: v.stack,
errorClass: v.name
};
}
return JSON.stringify(v,undefined,2);
};
const normalizeArgs = (args)=>args.map(mapToString);
const logTarget = document.querySelector('#test-output');
const logCss = function(cssClass,...args){
const ln = document.createElement('div');
if(cssClass){
for(const c of (Array.isArray(cssClass) ? cssClass : [cssClass])){
ln.classList.add(c);
}
}
ln.append(document.createTextNode(normalizeArgs(args).join(' ')));
logTarget.append(ln);
};
const cbReverse = document.querySelector('#cb-log-reverse');
const cbReverseKey = 'tester1:cb-log-reverse';
const cbReverseIt = ()=>{
logTarget.classList[cbReverse.checked ? 'add' : 'remove']('reverse');
localStorage.setItem(cbReverseKey, cbReverse.checked ? 1 : 0);
};
cbReverse.addEventListener('change', cbReverseIt, true);
if(localStorage.getItem(cbReverseKey)){
cbReverse.checked = !!(+localStorage.getItem(cbReverseKey));
}
cbReverseIt();
return logCss;
})();
const stdout = (...args)=>logCss('',...args);
const stderr = (...args)=>logCss('error',...args);
const wait = async (ms)=>{
return new Promise((resolve)=>setTimeout(resolve,ms));
};
const urlArgsJs = new URL(document.currentScript.src).searchParams;
const urlArgsHtml = new URL(self.location.href).searchParams;
const options = Object.create(null);
options.sqlite3Dir = urlArgsJs.get('sqlite3.dir');
options.workerCount = (
urlArgsHtml.has('workers') ? +urlArgsHtml.get('workers') : 3
) || 4;
options.opfsVerbose = (
urlArgsHtml.has('verbose') ? +urlArgsHtml.get('verbose') : 1
) || 1;
options.interval = (
urlArgsHtml.has('interval') ? +urlArgsHtml.get('interval') : 750
) || 1000;
options.iterations = (
urlArgsHtml.has('iterations') ? +urlArgsHtml.get('iterations') : 10
) || 10;
options.unlockAsap = (
urlArgsHtml.has('unlock-asap') ? +urlArgsHtml.get('unlock-asap') : 0
) || 0;
const workers = [];
workers.post = (type,...args)=>{
for(const w of workers) w.postMessage({type, payload:args});
};
workers.counts = {loaded: 0, passed: 0, failed: 0};
const checkFinished = function(){
if(workers.counts.passed + workers.counts.failed !== workers.length){
return;
}
if(workers.counts.failed>0){
logCss('tests-fail',"Finished with",workers.counts.failed,"failure(s).");
}else{
logCss('tests-pass',"All",workers.length,"workers finished.");
}
};
workers.onmessage = function(msg){
msg = msg.data;
const prefix = 'Worker #'+msg.worker+':';
switch(msg.type){
case 'loaded':
stdout(prefix,"loaded");
if(++workers.counts.loaded === workers.length){
stdout("All",workers.length,"workers loaded. Telling them to run...");
workers.post('run');
}
break;
case 'stdout': stdout(prefix,...msg.payload); break;
case 'stderr': stderr(prefix,...msg.payload); break;
case 'error': stderr(prefix,"ERROR:",...msg.payload); break;
case 'finished':
++workers.counts.passed;
logCss('tests-pass',prefix,...msg.payload);
checkFinished();
break;
case 'failed':
++workers.counts.failed;
logCss('tests-fail',prefix,"FAILED:",...msg.payload);
checkFinished();
break;
default: logCss('error',"Unhandled message type:",msg); break;
}
};
stdout("Launching",options.workerCount,"workers. Options:",options);
workers.uri = (
'worker.js?'
+ 'sqlite3.dir='+options.sqlite3Dir
+ '&interval='+options.interval
+ '&iterations='+options.iterations
+ '&opfs-verbose='+options.opfsVerbose
+ '&opfs-unlock-asap='+options.unlockAsap
);
for(let i = 0; i < options.workerCount; ++i){
stdout("Launching worker...");
workers.push(new Worker(
workers.uri+'&workerId='+(i+1)+(i ? '' : '&unlink-db')
));
}
// Have to delay onmessage assignment until after the loop
// to avoid that early workers get an undue head start.
workers.forEach((w)=>w.onmessage = workers.onmessage);
})(self);
+113
View File
@@ -0,0 +1,113 @@
importScripts(
(new URL(self.location.href).searchParams).get('sqlite3.dir') + '/sqlite3.js'
);
self.sqlite3InitModule().then(async function(sqlite3){
const urlArgs = new URL(self.location.href).searchParams;
const options = {
workerName: urlArgs.get('workerId') || Math.round(Math.random()*10000),
unlockAsap: urlArgs.get('opfs-unlock-asap') || 0 /*EXPERIMENTAL*/
};
const wPost = (type,...payload)=>{
postMessage({type, worker: options.workerName, payload});
};
const stdout = (...args)=>wPost('stdout',...args);
const stderr = (...args)=>wPost('stderr',...args);
if(!sqlite3.opfs){
stderr("OPFS support not detected. Aborting.");
return;
}
const wait = async (ms)=>{
return new Promise((resolve)=>setTimeout(resolve,ms));
};
const dbName = 'concurrency-tester.db';
if(urlArgs.has('unlink-db')){
await sqlite3.opfs.unlink(dbName);
stdout("Unlinked",dbName);
}
wPost('loaded');
let db;
const interval = Object.assign(Object.create(null),{
delay: urlArgs.has('interval') ? (+urlArgs.get('interval') || 750) : 750,
handle: undefined,
count: 0
});
const finish = ()=>{
if(db){
if(!db.pointer) return;
db.close();
}
if(interval.error){
wPost('failed',"Ending work after interval #"+interval.count,
"due to error:",interval.error);
}else{
wPost('finished',"Ending work after",interval.count,"intervals.");
}
};
const run = async function(){
db = new sqlite3.opfs.OpfsDb({
filename: 'file:'+dbName+'?opfs-unlock-asap='+options.unlockAsap,
flags: 'c'
});
sqlite3.capi.sqlite3_busy_timeout(db.pointer, 5000);
db.transaction((db)=>{
db.exec([
"create table if not exists t1(w TEXT UNIQUE ON CONFLICT REPLACE,v);",
"create table if not exists t2(w TEXT UNIQUE ON CONFLICT REPLACE,v);"
]);
});
const maxIterations =
urlArgs.has('iterations') ? (+urlArgs.get('iterations') || 10) : 10;
stdout("Starting interval-based db updates with delay of",interval.delay,"ms.");
const doWork = async ()=>{
const tm = new Date().getTime();
++interval.count;
const prefix = "v(#"+interval.count+")";
stdout("Setting",prefix,"=",tm);
try{
db.exec({
sql:"INSERT OR REPLACE INTO t1(w,v) VALUES(?,?)",
bind: [options.workerName, new Date().getTime()]
});
//stdout("Set",prefix);
}catch(e){
interval.error = e;
}
};
if(1){/*use setInterval()*/
setTimeout(async function timer(){
await doWork();
if(interval.error || maxIterations === interval.count){
finish();
}else{
setTimeout(timer, interval.delay);
}
}, interval.delay);
}else{
/*This approach provides no concurrency whatsoever: each worker
is run to completion before any others can work.*/
let i;
for(i = 0; i < maxIterations; ++i){
await doWork();
if(interval.error) break;
await wait(interval.ms);
}
finish();
}
}/*run()*/;
self.onmessage = function({data}){
switch(data.type){
case 'run': run().catch((e)=>{
if(!interval.error) interval.error = e;
finish();
});
break;
default:
stderr("Unhandled message type '"+data.type+"'.");
break;
}
};
});
+7 -4
View File
@@ -67,14 +67,17 @@ sqlite3-wasmfs.jsflags += $(sqlite3-wasmfs.fsflags)
# USE_PTHREADS + ALLOW_MEMORY_GROWTH may run non-wasm code slowly,
# see https://github.com/WebAssembly/design/issues/1271 [-Wpthreads-mem-growth]
sqlite3-wasmfs.jsflags += -sWASM_BIGINT=$(emcc.WASM_BIGINT)
$(eval $(call call-make-pre-js,sqlite3-wasmfs))
sqlite3-wasmfs.jsflags += $(pre-post-common.flags) $(pre-post-sqlite3-wasmfs.flags)
$(eval $(call call-make-pre-js,sqlite3-wasmfs,vanilla))
sqlite3-wasmfs.jsflags += \
$(pre-post-common.flags.vanilla) \
$(pre-post-sqlite3-wasmfs.flags.vanilla)
$(sqlite3-wasmfs.js): $(sqlite3-wasm.c) \
$(EXPORTED_FUNCTIONS.api) $(MAKEFILE) $(MAKEFILE.wasmfs) \
$(pre-post-sqlite3-wasmfs.deps)
$(pre-post-sqlite3-wasmfs.deps.vanilla)
@echo "Building $@ ..."
$(emcc.bin) -o $@ $(emcc_opt_full) $(emcc.flags) \
$(sqlite3-wasmfs.cflags) $(sqlite3-wasmfs.jsflags) \
$(pre-post-sqlite3-wasm.flags.vanilla) \
$(sqlite3-wasm.c)
chmod -x $(sqlite3-wasmfs.wasm)
$(maybe-wasm-strip) $(sqlite3-wasmfs.wasm)
@@ -98,7 +101,7 @@ $(speedtest1-wasmfs.js): $(speedtest1.cses) $(sqlite3-wasmfs.js) \
$(EXPORTED_FUNCTIONS.speedtest1)
@echo "Building $@ ..."
$(emcc.bin) \
$(speedtest1-wasmfs.eflags) $(speedtest1-common.eflags) \
$(speedtest1-wasmfs.eflags) $(speedtest1.eflags.common) \
$(pre-post-speedtest1-wasmfs.flags) \
$(speedtest1.cflags) \
$(sqlite3-wasmfs.cflags) \
+4 -4
View File
@@ -390,7 +390,10 @@ TESTSRC += \
$(TOP)/ext/fts5/fts5_tcl.c \
$(TOP)/ext/fts5/fts5_test_mi.c \
$(TOP)/ext/fts5/fts5_test_tok.c \
$(TOP)/ext/rtree/test_rtreedoc.c
$(TOP)/ext/rtree/test_rtreedoc.c \
$(TOP)/ext/recover/sqlite3recover.c \
$(TOP)/ext/recover/dbdata.c \
$(TOP)/ext/recover/test_recover.c
#TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c
@@ -446,9 +449,6 @@ TESTSRC2 = \
$(TOP)/ext/misc/stmt.c \
$(TOP)/ext/session/sqlite3session.c \
$(TOP)/ext/session/test_session.c \
$(TOP)/ext/recover/sqlite3recover.c \
$(TOP)/ext/recover/dbdata.c \
$(TOP)/ext/recover/test_recover.c \
fts5.c
# Header files used by all library source files.
+111 -101
View File
@@ -1,13 +1,13 @@
C Minor\sbuild\scleanups\sand\sfix\sa\sharmless\srace\scondition\sin\sthe\sOPFS\spart\sof\stester1.js.
D 2022-11-02T14:08:59.340
C Sync\sw/trunk,\szap\ssurplus\sspace.
D 2022-11-28T14:11:48.108
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F Makefile.in 78e4c4916f2c3993a8a454018745ff02094a8029d449d0c22db98f1cf8260420
F Makefile.in c223963d7b0828f26cb62ea3e0f583d26839b7d3ef0d1cca87f35c4b222ff01b
F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241
F Makefile.msc e7a564ceec71f0d9666031d5638cf0d4f88b050b44e8df5d32125137cd259ac0
F README.md 8b8df9ca852aeac4864eb1e400002633ee6db84065bd01b78c33817f97d31f5e
F VERSION 8868ddfa6e1eee218286021a94b3e22d13e550c76c72d878857547ca001de24a
F VERSION 413ec94920a487ae32c9a2a8819544d690662d6f7c7ce025c0d0b8a1e74fa9db
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90
@@ -33,7 +33,7 @@ F autoconf/tea/win/nmakehlp.c b01f822eabbe1ed2b64e70882d97d48402b42d2689a1ea0034
F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63
F config.guess 883205ddf25b46f10c181818bf42c09da9888884af96f79e1719264345053bd6
F config.sub c2d0260f17f3e4bc0b6808fccf1b291cb5e9126c14fc5890efc77b9fd0175559
F configure b93755fe94b3e9b2015f3c2d6c63928e8899b0a7c54e042deb843498094179da x
F configure 9cbf6a15a4b5f2b3551a466420e5f51ce04c40aaed7f90c886402bf0b66ec110 x
F configure.ac 48dc6bfee293eef05910faf085760f2fd79b680aa47b50e8e6a22ca40bb026bb
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
F doc/F2FS.txt c1d4a0ae9711cfe0e1d8b019d154f1c29e0d3abfe820787ba1e9ed7691160fcd
@@ -118,7 +118,7 @@ F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b7292
F ext/fts5/fts5_config.c 501e7d3566bc92766b0e11c0109a7c5a6146bc41144195459af5422f6c2078aa
F ext/fts5/fts5_expr.c 40174a64829d30cc86e8266306ad24980f6911edd5ca0b8c1ce7821ea1341b88
F ext/fts5/fts5_hash.c d4fb70940359f2120ccd1de7ffe64cc3efe65de9e8995b822cd536ff64c96982
F ext/fts5/fts5_index.c 4b1ac44c665667be970df780bd8e734748047bd30a971d0bb7e884af8ac6e62c
F ext/fts5/fts5_index.c a8ee270724ae1f958d0ce9897bcd60a5b760ecbeaa058fc8632805a283f1c20a
F ext/fts5/fts5_main.c 6078ae86d3b813753a4f1201054550aff21a3f660e97b30f200d2b1472874151
F ext/fts5/fts5_storage.c 76c6085239eb44424004c022e9da17a5ecd5aaec859fba90ad47d3b08f4c8082
F ext/fts5/fts5_tcl.c b1445cbe69908c411df8084a10b2485500ac70a9c747cdc8cda175a3da59d8ae
@@ -289,6 +289,9 @@ F ext/misc/README.md d6dd0fe1d8af77040216798a6a2b0c46c73054d2f0ea544fbbcdccf6f23
F ext/misc/amatch.c e3ad5532799cee9a97647f483f67f43b38796b84b5a8c60594fe782a4338f358
F ext/misc/anycollseq.c 5ffdfde9829eeac52219136ad6aa7cd9a4edb3b15f4f2532de52f4a22525eddb
F ext/misc/appendvfs.c 9642c7a194a2a25dca7ad3e36af24a0a46d7702168c4ad7e59c9f9b0e16a3824
F ext/misc/base64.c fb140039d4e15710298e28cafe12d2bdff1b927fc57540cbcd07996f82bf9800 x
F ext/misc/base85.c ace591246855806a70e63eb6705ce8736f4b782da2137bdbb5d39cc337346e0d
F ext/misc/basexx.c b37ea21f00deea1029d2ddc670f5701d9370ae06f7d173c5bed79d0f26a67ec1
F ext/misc/blobio.c a867c4c4617f6ec223a307ebfe0eabb45e0992f74dd47722b96f3e631c0edb2a
F ext/misc/btreeinfo.c d28ce349b40054eaa9473e835837bad7a71deec33ba13e39f963d50933bfa0f9
F ext/misc/carray.c b752f46411e4e47e34dce6f0c88bc8e51bb821ba9e49bfcd882506451c928f69
@@ -316,13 +319,13 @@ F ext/misc/normalize.c bd84355c118e297522aba74de34a4fd286fc775524e0499b14473918d
F ext/misc/percentile.c b9086e223d583bdaf8cb73c98a6539d501a2fc4282654adbfea576453d82e691
F ext/misc/prefixes.c 0f4f8cff5aebc00a7e3ac4021fd59cfe1a8e17c800ceaf592859ecb9cbc38196
F ext/misc/qpvtab.c 09738419e25f603a35c0ac8bd0a04daab794f48d08a9bc07a6085b9057b99009
F ext/misc/regexp.c 5abed0ace2d9340b42b9ab1dbe64db9c276e4e8eba38a903232b6253e05ccdaf
F ext/misc/regexp.c 064838f7b31e90d312cce089cfafbb992034e75d359009c48886ca06c0a794b2
F ext/misc/remember.c add730f0f7e7436cd15ea3fd6a90fd83c3f706ab44169f7f048438b7d6baa69c
F ext/misc/rot13.c 51ac5f51e9d5fd811db58a9c23c628ad5f333c173f1fc53c8491a3603d38556c
F ext/misc/scrub.c 2a44b0d44c69584c0580ad2553f6290a307a49df4668941d2812135bfb96a946
F ext/misc/series.c 8d79354f2c3d46b95ee21272a07cf0bcabb58d1f2b06d9e7b8a31dca1dacb3e5
F ext/misc/sha1.c 4011aef176616872b2a0d5bccf0ecfb1f7ce3fe5c3d107f3a8e949d8e1e3f08d
F ext/misc/shathree.c 7b17615869a495659f1569ada1d8d3d21b4a24614f2746d93cc87ef7c0b6b36d
F ext/misc/shathree.c 9b7af7b7d55b27c5bbd16548b67fe6aa47a819e71bc6a80a456144f86f4e834d
F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
F ext/misc/spellfix.c 94df9bbfa514a563c1484f684a2df3d128a2f7209a84ca3ca100c68a0163e29f
F ext/misc/sqlar.c 0ace5d3c10fe736dc584bf1159a36b8e2e60fab309d310cd8a0eecd9036621b6
@@ -374,33 +377,34 @@ F ext/rbu/rbumisc.test 329986cf5dd51890c4eb906c2f960ebb773a79a64bed90f506b7c4178
F ext/rbu/rbumulti.test 5fb139058f37ddc5a113c5b93238de915b769b7792de41b44c983bc7c18cf5b9
F ext/rbu/rbupartial.test f25df014b8dbe3c5345851fba6e66f79ab237f57dc201b2d5f0dbae658ae5a4c
F ext/rbu/rbuprogress.test 857cf1f8166c83ef977edb9ef4fc42d80f71fbd798652b46ae2f3a7031870f8d
F ext/rbu/rburename.test a9b4aea612352b74c45de1757edd2ecb2079348b1d4cc734572dc29e55b1b376
F ext/rbu/rburesume.test dbdc4ca504e9c76375a69e5f0d91205db967dcc509a5166ca80231f8fda49eb1
F ext/rbu/rbusave.test f4190a1a86fccf84f723af5c93813365ae33feda35845ba107b59683d1cdd926
F ext/rbu/rbusplit.test b37e7b40b38760881dc9c854bd40b4744c6b6cd74990754eca3bda0f407051e8
F ext/rbu/rbutemplimit.test 05ceefa90a2e26a99f40dd48282ed63a00df5e59c1f2bfd479c143e201a1b0ba
F ext/rbu/rbuvacuum.test 55e101e90168c2b31df6c9638fe73dc7f7cc666b6142266d1563697d79f73534
F ext/rbu/rbuvacuum2.test 886add83fd74bcb02e6dd016ae5b585367bd58c5d0694c9d9ca7bdb1d1f578c2
F ext/rbu/rbuvacuum2.test 2643b58f4d8d3573db0f93faae18805a35ab162b4c55ff6b656062ff432ed55b
F ext/rbu/rbuvacuum3.test 8addd82e4b83b4c93fa47428eae4fd0dbf410f8512c186f38e348feb49ba03dc
F ext/rbu/rbuvacuum4.test a78898e438a44803eb2bc897ba3323373c9f277418e2d6d76e90f2f1dbccfd10
F ext/rbu/sqlite3rbu.c 8737cabdfbee84bb25a7851ecef8b1312be332761238da9be6ddb10c62ad4291
F ext/rbu/sqlite3rbu.h 1dc88ab7bd32d0f15890ea08d23476c4198d3da3056985403991f8c9cd389812
F ext/rbu/test_rbu.c 03f6f177096a5f822d68d8e4069ad8907fe572c62ff2d19b141f59742821828a
F ext/recover/dbdata.c 3ae32f9b7f02a141889b9075beb87895a826d0fcf3b702b8251cbb0dd3c91a83
F ext/recover/recover1.test 522e2c3353734dbef9118f08e47209470e50308ba35ce818fed00b8e265aee44
F ext/recover/recover_common.tcl 6679af7dffc858e345053a91c9b0a897595b4a13007aceffafca75304ccb137c
F ext/recover/recoverclobber.test 294dcc894124ab4ca3a7b35766630742a3d25810fceac22220beb64f70a33a60
F ext/recover/recovercorrupt.test 6540aae95e17398dd70b44518367fd56588c44962cb276d2623a0fedba9efe9e
F ext/recover/recovercorrupt2.test a7e0735cefb79de7959ebd1ee6d963f9505305fe7983ac58394eb5f5aa9236c7
F ext/recover/recoverfault.test 3a0a32b9fc216592b97775d69220695b0926980c0f7424b7a59144e47d7cb568
F ext/recover/recoverfault2.test 321036336af23e778a87f148c4cc4407f88fbdab1fd72ddb661669be9020d36b
F ext/recover/recoverold.test 46e9d99b595fac583d4c67f74d7d89c20a435c752ef6eeb3e918b599940c88e0
F ext/recover/recoverpgsz.test 93e970eab05e4e89f8fd6b1bd23f9ec137ea09857e66ba0d4d27a83cd1ba4a89
F ext/recover/recoverrowid.test 1694a1a5526d825f71279f3d02ab02a1ee4c5265de18858bf54cb8ec54487ac8
F ext/recover/recoverslowidx.test f356bb9fba7ffd6fc50e045e419464f0129ac6e24decf6e919584f79c3493727
F ext/recover/recoversql.test f9872ff2114e13ffd8ee31e1de06919f62b9b48bc080191b5bd076d10becb60f
F ext/recover/sqlite3recover.c d2feca815f489f3beed4af94b916e0cba046937b9cc760b0f2baacf1ae515fa2
F ext/rbu/sqlite3rbu.c c4ba7901b2d3e0c7845f30840e3ffb35c6f999d6da0d80f121866491f982794c
F ext/rbu/sqlite3rbu.h 02d981e2d39c151391759e1a400e29c7388730812957ac3db8dad7f6c9f9cfc8
F ext/rbu/test_rbu.c ee6ede75147bc081fe9bc3931e6b206277418d14d3fbceea6fdc6216d9b47055
F ext/recover/dbdata.c 8f1f75d636431de69d7977ec50fc41bfdd0c48c510d5ee7eae0cbd4164e1429a
F ext/recover/recover1.test 02004eb8f9ec2825ba77e24742c18e45162cb21d27e76a3a435b83a759a1131a
F ext/recover/recover_common.tcl a61306c1eb45c0c3fc45652c35b2d4ec19729e340bdf65a272ce4c229cefd85a
F ext/recover/recoverclobber.test 3ba6c0c373c5c63d17e82eced64c05c57ccaf26c1abe1ca7141334022a79f32e
F ext/recover/recovercorrupt.test 64c081ad1200ae77b447da99eb724785d6bf71715f394543dc7689642e92bf49
F ext/recover/recovercorrupt2.test 74bef7dd2d7dd4856f3da21be6e213d27da44827e0f5f0946ca0325b46d163ed
F ext/recover/recoverfault.test 9d9f88eeb222615a25e7514f234c950d46bee20d24cd8db49d8fff8d650dcfe1
F ext/recover/recoverfault2.test 730e7371bcda769554d15460cb23126abba1be8eca9539ccabf63623e7bb7e09
F ext/recover/recoverold.test 68db3d6f85dd2b98e785b6c4da4f5eea4bbe52ccf6674d9a94c7506dc92596aa
F ext/recover/recoverpgsz.test 3658ab8e68475b1bb87d6af88baa04551c84b73280a566a1be847182410ffc58
F ext/recover/recoverrowid.test f948bf4024a5f41b0e21b8af80c60564c5b5d78c05a8d64fc00787715ff9f45f
F ext/recover/recoverslowidx.test 5205a9742dd9490ee99950dabb622307355ef1662dea6a3a21030057bfd81411
F ext/recover/recoversql.test e66d01f95302a223bcd3fd42b5ee58dc2b53d70afa90b0d00e41e4b8eab20486
F ext/recover/sqlite3recover.c 1e4de9cd30daa67e5c1cd0cafb764ddf183087ce037ab47b7c1a7e2921c90d37
F ext/recover/sqlite3recover.h 011c799f02deb70ab685916f6f538e6bb32c4e0025e79bfd0e24ff9c74820959
F ext/recover/test_recover.c 61ec931e47abca6b2210f46239cafd9f3060741605e3d3c45a7c7a53f63dd957
F ext/recover/test_recover.c 1a34e2d04533d919a30ae4d5caeb1643f6684e9ccd7597ca27721d8af81f4ade
F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15
F ext/repair/checkfreelist.c e21f06995ff4efdc1622dcceaea4dcba2caa83ca2f31a1607b98a8509168a996
F ext/repair/checkindex.c 4383e4469c21e5b9ae321d0d63cec53e981af9d7a6564be6374f0eeb93dfc890
@@ -487,35 +491,36 @@ F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb
F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c
F ext/wasm/GNUmakefile 78b64d110e66e18709c11437a74c29bc7855d85a713217db99127c7f304a332f
F ext/wasm/GNUmakefile bc1696a1189f4c571b3d878b8f3a67c1f4b52c222f52d356027a5a0c707337c7
F ext/wasm/README-dist.txt 2d670b426fc7c613b90a7d2f2b05b433088fe65181abead970980f0a4a75ea20
F ext/wasm/README.md ef39861aa21632fdbca0bdd469f78f0096f6449a720f3f39642594af503030e9
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 9120c2f8f51fa85f46dcf4dcb6b12f4a807d428f6089b99cdb08d8ddfcfd88b2
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api b4d68c97d14944b48d55e06aa44f544a6f56a7fa2bcb6f9e030936a5b2a9479a
F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287
F ext/wasm/api/README.md 1350088aee90e959ad9a94fab1bb6bcb5e99d4d27f976db389050f54f2640c78
F ext/wasm/api/extern-post-js.js f3dc4219a2a1f183d98452dcbd55a0c5351b759eccca75480a92473974d8b047
F ext/wasm/api/README.md 29276a845e57004e82efba61fa5866fd05f9137380a1dc26dc4c6d65264cd81c
F ext/wasm/api/extern-post-js.js 59e52f579cd3a332d73dae94c91b9579daafb10dd6ada03803f1afa6bdad7689
F ext/wasm/api/extern-pre-js.js cc61c09c7a24a07dbecb4c352453c3985170cec12b4e7e7e7a4d11d43c5c8f41
F ext/wasm/api/post-js-footer.js cd0a8ec768501d9bd45d325ab0442037fb0e33d1f3b4f08902f15c34720ee4a1
F ext/wasm/api/post-js-header.js d6ab3dfef4a06960d28a7eaa338d4e2a1a5981e9b38718168bbde8fdb2a439b8
F ext/wasm/api/pre-js.js 287e462f969342b032c03900e668099fa1471d852df7a472de5bc349161d9c04
F ext/wasm/api/pre-js.js b88499dc303c21fc3f55f2c364a0f814f587b60a95784303881169f9e91c1d5f
F ext/wasm/api/sqlite3-api-cleanup.js ecdc69dbfccfe26146f04799fcfd4a6f5790d46e7e3b9b6e9b0491f92ed8ae34
F ext/wasm/api/sqlite3-api-glue.js 9cfa26a9818532c80c2555bc98615de3b170d5db0cf4b141cc3aa83c33c8758f
F ext/wasm/api/sqlite3-api-oo1.js e9a83489bbb4838ce0aee46eaaa9350e0e25a5b926b565e4f5ae8e840e4fbaed
F ext/wasm/api/sqlite3-api-opfs.js cdcbb57acc66f4569ac9e18f9d13d5a3657d8aae195725c6324943da56c1005d
F ext/wasm/api/sqlite3-api-prologue.js 1f97261b4d8a60a48f30ee41261e1c4a0c3efff35ae08d7ece243fcf49b3eee3
F ext/wasm/api/sqlite3-api-worker1.js cac2f5c63f950f69b5249c9880d4cd385e914c354c459d4096ed5dbb1248de76
F ext/wasm/api/sqlite3-api-glue.js 056f44b82c126358a0175e08a892d56fadfce177b0d7a0012502a6acf67ea6d5
F ext/wasm/api/sqlite3-api-oo1.js 06ad2079368e16cb9f182c18cd37bdc3932536856dff4f60582d0ca5f6c491a8
F ext/wasm/api/sqlite3-api-opfs.js e98a8bd67dea8c20b0ec17332698b44658a6fbc4be18dd87fab2ce05284a10d7
F ext/wasm/api/sqlite3-api-prologue.js 7fce4c6a138ec3d7c285b7c125cee809e6b668d2cb0d2328a1b790b7037765bd
F ext/wasm/api/sqlite3-api-worker1.js e94ba98e44afccfa482874cd9acb325883ade50ed1f9f9526beb9de1711f182f
F ext/wasm/api/sqlite3-license-version-header.js a661182fc93fc2cf212dfd0b987f8e138a3ac98f850b1112e29b5fbdaecc87c3
F ext/wasm/api/sqlite3-opfs-async-proxy.js 936f57737eb65afc0f4c3494b93f7b02208055226a7b3cb58f551c38b03ab083
F ext/wasm/api/sqlite3-opfs-async-proxy.js 798383f6b46fd5dac122d6e35962d25b10401ddb825b5c66df1d21e6b1d8aacc
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
F ext/wasm/api/sqlite3-wasm.c af472ec27bc7a398a2b94329cb7a77f3411109fc17529a289fa10cc55424ece1
F ext/wasm/api/sqlite3-wasm.c 8b32787a3b6bb2990cbaba2304bd5b75a9652acbc8d29909b3279019b6cbaef5
F ext/wasm/api/sqlite3-worker1-promiser.js 0c7a9826dbf82a5ed4e4f7bf7816e825a52aff253afbf3350431f5773faf0e4b
F ext/wasm/api/sqlite3-worker1.js 1e54ea3d540161bcfb2100368a2fc0cad871a207b8336afee1c445715851ec54
F ext/wasm/batch-runner.html 4deeed44fe41496dc6898d9fb17938ea3291f40f4bfb977e29d0cef96fbbe4c8
F ext/wasm/batch-runner.js 49609e89aaac9989d6c1ad3fae268e4878e1ad7bc5fd3e5c2f44959660780b2e
F ext/wasm/c-pp.c 92285f7bce67ed7b7020b40fde8ed0982c442b63dc33df9dfd4b658d4a6c0779
F ext/wasm/common/SqliteTestUtil.js d8bf97ecb0705a2299765c8fc9e11b1a5ac7f10988bbf375a6558b7ca287067b
F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
F ext/wasm/common/testing.css 35889709547d89a6109ff83b25c11bbc91d8dd43aab8722e428655ca98880a06
F ext/wasm/common/whwasmutil.js 77930367c2a65cf6fd6f99ad3644ede33e4d20466f5e506eb87b8d101a0a7655
F ext/wasm/common/whwasmutil.js 16a592d5c304a2d268ca1c28e08a5b029a2f3cbe10af78dbc3456cfc9e3559d1
F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed
F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508
F ext/wasm/demo-123.js ebae30756585bca655b4ab2553ec9236a87c23ad24fc8652115dcedb06d28df6
@@ -525,21 +530,21 @@ F ext/wasm/demo-worker1-promiser.html 1de7c248c7c2cfd4a5783d2aa154bce62d74c6de98
F ext/wasm/demo-worker1-promiser.js b85a2bb1b918db4f09dfa24419241cb3edad7791389425c2505092e9b715017d
F ext/wasm/demo-worker1.html 2c178c1890a2beb5a5fecb1453e796d067a4b8d3d2a04d65ca2eb1ab2c68ef5d
F ext/wasm/demo-worker1.js a619adffc98b75b66c633b00f747b856449a134a9a0357909287d80a182d70fa
F ext/wasm/dist.make 481289899a07958439d07ee4302ff86235fa0fbb72f17ea05db2be90a94abf90
F ext/wasm/fiddle.make e570ec1bfc7d803507a2e514fe32f673fe001b2114b85c73c3964a462ba8bcfc
F ext/wasm/dist.make 11b98da79385701a568a4728671821fe2524c1d1ecd05ff2e24cb3e33b2c6c4f
F ext/wasm/fiddle.make 2812c44c9bafb5be9c8767963d1b9f374d77af7795fcaa06483c03e7059dea74
F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
F ext/wasm/fiddle/fiddle-worker.js b4a0c8ab6c0983218543ca771c45f6075449f63a1dcf290ae5a681b2cba8800d
F ext/wasm/fiddle/fiddle.js 974b995119ac443685d7d94d3b3c58c6a36540e9eb3fed7069d5653284071715
F ext/wasm/fiddle/index.html 5daf54e8f3d7777cbb1ca4f93affe28858dbfff25841cb4ab81d694efed28ec2
F ext/wasm/index-dist.html cb0da16cba0f21cda2c25724c5869102d48eb0af04446acd3cd0ca031f80ed19
F ext/wasm/index.html ce6a68a75532b47e3c0adb83381a06d15de8c0ac0331fb7bf31d33f8e7c77dc4
F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab264dab6c285e6dbbd215
F ext/wasm/index-dist.html c4337617c4d6d4d0796827cec28ac81d128c6f911dcf888a290a32ad50890408
F ext/wasm/index.html 5be176de5be8ae96889798f803fef4f6a2ef31cee305a0430ca4629f6ae04c27
F ext/wasm/jaccwabyt/jaccwabyt.js 95f573de1826474c9605dda620ee622fcb1673ae74f191eb324c0853aa4dcb66
F ext/wasm/jaccwabyt/jaccwabyt.md 9aa6951b529a8b29f578ec8f0355713c39584c92cf1708f63ba0cf917cb5b68e
F ext/wasm/module-symbols.html eca884ef4380612145ee550213be57478ee2b9cd9a9c2b27530cc23359c99682
F ext/wasm/module-symbols.html b8eebafef8e536624bbe5f7a3da40c07a9062b843dfd3161a0bb72cbb6763dc5
F ext/wasm/scratchpad-wasmfs-main.html 20cf6f1a8f368e70d01e8c17200e3eaa90f1c8e1029186d836d14b83845fbe06
F ext/wasm/scratchpad-wasmfs-main.js 4c140457f4d6da9d646a49addd91edb6e9ad1643c6c48e3258b5bce24725dc18
F ext/wasm/speedtest1-wasmfs.html bc28eb29b69a73864b8d7aae428448f8b7e1de81d8bfb9bba99541322054dbd0
F ext/wasm/speedtest1-worker.html 94246488e10af9daa1ebd0979b1b8d7a22a579e5a983fa2a5ad94591ecf55f2c
F ext/wasm/speedtest1-worker.html d75d9de9347c6f2f3278b73ccb6bceb559080644aef385fe496fa389f58bcd66
F ext/wasm/speedtest1-worker.js 13b57c4a41729678a1194014afec2bd5b94435dcfc8d1039dfa9a533ac819ee1
F ext/wasm/speedtest1.html e4c4e5c1c8ec1ad13c995e346e4216a1df152fd2c5cd17e0793b865b2f3c5000
F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x
@@ -547,15 +552,18 @@ F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d826
F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5
F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555e685bce3da8c3f
F ext/wasm/test-opfs-vfs.js 44363db07b2a20e73b0eb1808de4400ca71b703af718d0fa6d962f15e73bf2ac
F ext/wasm/tester1-worker.html 51bf39e2b87f974ae3d5bc3086e2fb36d258f3698c54f6e21ba4b3b99636fa27
F ext/wasm/tester1.html 624ec41cd9f78a1f2b6d7df70aaa7a6394396b1f2455ecbd6de5775c1275b121
F ext/wasm/tester1.js 3a5558201359ff8a1c3051ab24fcc8bed5a4e99ae5e086e5184cbfc915d08724
F ext/wasm/tester1-worker.html 5ef353348c37cf2e4fd0b23da562d3275523e036260b510734e9a3239ba8c987
F ext/wasm/tester1.c-pp.html 74aa9b31c75f12490653f814b53c3dd39f40cd3f70d6a53a716f4e8587107399
F ext/wasm/tester1.c-pp.js 3b91f192c159088004fba6fe3441edea58421a8b88bccf3dd20978a077648d19
F ext/wasm/tests/opfs/concurrency/index.html e8fec75ea6eddc600c8a382da7ea2579feece2263a2fb4417f2cf3e9d451744c
F ext/wasm/tests/opfs/concurrency/test.js bfc3d7e27b207f0827f12568986b8d516a744529550b449314f5c21c9e9faf4a
F ext/wasm/tests/opfs/concurrency/worker.js 0eff027cbd3a495acb2ac94f57ca9e4d21125ab9fda07d45f3701b0efe82d450
F ext/wasm/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd72273503ae7d5
F ext/wasm/wasmfs.make edfd60691d10fd19ada4c061280fd7fbe4cf5f6bf6b913268e8ebedfccea6ab5
F ext/wasm/wasmfs.make 8fea9b4f3cde06141de1fc4c586ab405bd32c3f401554f4ebb18c797401a678d
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 5ade0bc977aa135e79e3faaea894d5671b26107cc91e70783aa7dc83f22f3ba0
F main.mk 73410c1f180660fd95c8203f35e1d4c1003e033d6bd0dbcb2c41610e4166500d
F main.mk fd90f1bd90bd4070c9af7e9c8396bd0cf4208a0186c8d18fa6e2609dff447d5d
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
@@ -575,9 +583,9 @@ F src/auth.c f4fa91b6a90bbc8e0d0f738aa284551739c9543a367071f55574681e0f24f8cf
F src/backup.c a2891172438e385fdbe97c11c9745676bec54f518d4447090af97189fd8e52d7
F src/bitvec.c 7c849aac407230278445cb069bebc5f89bf2ddd87c5ed9459b070a9175707b3d
F src/btmutex.c 6ffb0a22c19e2f9110be0964d0731d2ef1c67b5f7fabfbaeb7b9dabc4b7740ca
F src/btree.c 74fc5f6a0577df703d6f98d0c51ee0d8d91d22dbc0ba86e42e056517e2b45576
F src/btree.h 74d64b8f28cfa4a894d14d4ed64fa432cd697b98b61708d4351482ae15913e22
F src/btreeInt.h 8ce1332edd89dfd2461d561ac10a0ab5601c8e06200cb5230596c3caaf54482e
F src/btree.c 522df0f1173495e06c5589f0b17a61f53a212017b82be53171133fcfc0e1e90a
F src/btree.h 4fcbb0b041013071dd5e9f53c538d49916c092e6ad8842185985e5270a0792de
F src/btreeInt.h 88ad499c92b489afedbfefc3f067c4d15023ec021afe622db240dc9d2277cfa5
F src/build.c d3e43e950e4e377c1d451a4862556792acdef1faba14a03f899d30d09731c48b
F src/callback.c 4cd7225b26a97f7de5fee5ae10464bed5a78f2adefe19534cc2095b3a8ca484a
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
@@ -586,10 +594,10 @@ F src/date.c 94ce83b4cd848a387680a5f920c9018c16655db778c4d36525af0a0f34679ac5
F src/dbpage.c f1a87f4ebcf22284e0aaf0697862f4ccfc120dcd6db3d8dfa3b049b2580c01d8
F src/dbstat.c 861e08690fcb0f2ee1165eff0060ea8d4f3e2ea10f80dab7d32ad70443a6ff2d
F src/delete.c 86573edae75e3d3e9a8b590d87db8e47222103029df4f3e11fa56044459b514e
F src/expr.c 847f87d9df3ede2b2b0a8db088af0b9c1923b21009f8ea1b9b7b28cb0a383170
F src/expr.c 9e7fadc664b938c18f006be0d4f6669888f9302756ee204420c7eccaed5435a6
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c 722f20779f5342a787922deded3628d8c74b5249cab04098cf17ee2f2aaff002
F src/func.c d25f3c667d59dbac195e65f3539fdbbd8a36c142ce7bcdb45d1baf07446ad13a
F src/func.c 7e86074afc4dc702691a29b7801f6dcc191db092b52e8bbe69dcd2f7be52194d
F src/global.c e06ff8e0acd85aec13563c9ecb44fbbf38232ccf73594998fd880b92d619594b
F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19
F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51
@@ -598,15 +606,15 @@ F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
F src/insert.c 90a32bc7faa755cd5292ade21d2b3c6edba8fd1d70754a364caccabfde2c3bb2
F src/json.c 7749b98c62f691697c7ee536b570c744c0583cab4a89200fdd0fc2aa8cc8cbd6
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
F src/loadext.c 8086232d10e51e183a7f64199815bad1c579896354db69435347665f62f481e9
F src/loadext.c 25663175950c5c4404b9377840b7b4c6fe5c53b415caf43634c62f442c02a9a7
F src/main.c fa53bb2ae09549dab5629271c3cfd681f89059f5192afaaaf5c0d396bb3957fe
F src/malloc.c dfddca1e163496c0a10250cedeafaf56dff47673e0f15888fb0925340a8e3f90
F src/malloc.c 3d4ec162214024ee071d85711b93bec25cd3371280aee3702b63bcf312ca8238
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de
F src/mem2.c c8bfc9446fd0798bddd495eb5d9dbafa7d4b7287d8c22d50a83ac9daa26d8a75
F src/mem3.c 30301196cace2a085cbedee1326a49f4b26deff0af68774ca82c1f7c06fda4f6
F src/mem5.c 5a3dbd8ac8a6501152a4fc1fcae9b0900c2d7eb0589c4ec7456fdde15725a26c
F src/memdb.c c2dc88f97c410eb68a24468344b65526685e18354ddfd15906750c1eaf9dc2dd
F src/memdb.c a42248bc04dc1f80da3511618a78956ed77b65058b73e251bde39061204e9f97
F src/memjournal.c c283c6c95d940eb9dc70f1863eef3ee40382dbd35e5a1108026e7817c206e8a0
F src/msvc.h 3a15918220367a8876be3fa4f2abe423a861491e84b864fb2b7426bf022a28f8
F src/mutex.c 5e3409715552348732e97b9194abe92fdfcd934cfb681df4ba0ab87ac6c18d25
@@ -621,33 +629,33 @@ F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85
F src/os_kv.c 0e59600d25b72034c7666b8b7dcc527f039b5d9c16f24a7eca4c08c66f63c364
F src/os_setup.h 6011ad7af5db4e05155f385eb3a9b4470688de6f65d6166b8956e58a3d872107
F src/os_unix.c 287aa5f5691a2b356780c63e83abaa33549add84227b8313395f04088486d79c
F src/os_win.c 8d129ae3e59e0fa900e20d0ad789e96f2e08177f0b00b53cdda65c40331e0902
F src/os_win.c 295fe45f18bd86f2477f4cd79f3377c6f883ceb941b1f46808665c73747f2345
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c 6176d9752eb580419e8fef4592dc417a6b00ddfd43ee22f818819bf8840ceee8
F src/pager.c d3122cf67f327f1e2df12d06236a3473a8099542071e257067552f42917f172d
F src/pager.h f82e9844166e1585f5786837ddc7709966138ced17f568c16af7ccf946c2baa3
F src/parse.y 8e67d820030d2655b9942ffe61c1e7e6b96cea2f2f72183533299393907d0564
F src/pcache.c f4268f7f73c6a3db12ce22fd25bc68dc42315d19599414ab1207d7cf32f79197
F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586
F src/pcache1.c dee95e3cd2b61e6512dc814c5ab76d5eb36f0bfc9441dbb4260fccc0d12bbddc
F src/pragma.c 41430ca04735cc8e5d003bfd9315eadede3ec326e50805cc81bcf34e46601292
F src/pragma.c 894c2621d35edd4beea9b331cfdb1b42032394420074d2294c8febe548eea8a1
F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7
F src/prepare.c 1b02be0441eda4579471fea097f678effcbb77ef0c39ab3f703c837822bcd674
F src/prepare.c 9ebd3a1b12bbd1951f0d6db850f32cf5d4547a6ab8bb9e958d75dfbe4e60d0a3
F src/printf.c e99ee9741e79ae3873458146f59644276657340385ade4e76a5f5d1c25793764
F src/random.c 546d6feb15ec69c1aafe9bb351a277cbb498fd5410e646add673acb805714960
F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
F src/resolve.c efea4e5fbecfd6d0a9071b0be0d952620991673391b6ffaaf4c277b0bb674633
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
F src/select.c 12cb5162e606290354f9512ff0c30fc6dc4d7a77a92b6c5b581395f9c4edd047
F src/shell.c.in 84bb08d8762920285f08f1c0993f1b3992ac43af5d72445cb8a973fc50c71923
F src/sqlite.h.in bf5846820130b6cf01b002e90427eae29f02db07d9cb9b5ccd0e0aad867eed14
F src/select.c c1eb8f3ee25152327f2e7e87db8cea549e57c104b63638bff4fc584d479c33f0
F src/shell.c.in 9fda74d40b206a707aaa69fc5dc38e2c6a9137a3f4a1dcd7af581d59d92c063c
F src/sqlite.h.in 100fc660c2f19961b8ed8437b9d53d687de2f8eb2b96437ec6da216adcb643ca
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h c4b9fa7a7e2bcdf850cfeb4b8a91d5ec47b7a00033bc996fd2ee96cbf2741f5f
F src/sqliteInt.h 2c24ba38f78e32fe5d7ec136321a6ad827698b33ca98664970a8b7274d69ef7c
F src/sqliteInt.h 5dd5d3d47f40b6a12be4a5fc131673bfe00c00373ed266ff4c4ec05d1991e69f
F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
F src/tclsqlite.c 4e64ba300a5a26e0f1170e09032429faeb65e45e8f3d1a7833e8edb69fc2979e
F src/test1.c 40c9a40975512985762f87b83d0c63e4904833a9fe78cbcca664a37095301b1d
F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5
F src/test1.c 09ad3f2bf5f0efe70eb16e460054954b36be8ed09dfcafa0642b37d25d379f6f
F src/test2.c 827446e259a3b7ab949da1542953edda7b5117982576d3e6f1c24a0dd20a5cef
F src/test3.c 61798bb0d38b915067a8c8e03f5a534b431181f802659a6616f9b4ff7d872644
F src/test4.c 4533b76419e7feb41b40582554663ed3cd77aaa54e135cf76b3205098cd6e664
F src/test5.c 328aae2c010c57a9829d255dc099d6899311672d
@@ -701,18 +709,18 @@ F src/test_window.c cdae419fdcea5bad6dcd9368c685abdad6deb59e9fc8b84b153de513d394
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c 1305797eab3542a0896b552c6e7669c972c1468e11e92b370533c1f37a37082b
F src/treeview.c 07787f67cd297a6d09d04b8d70c06769c60c9c1d9080378f93929c16f8fd3298
F src/trigger.c 4163ada044af89d51caba1cb713a73165347b2ec05fe84a283737c134d61fcd5
F src/treeview.c 29b1dc7e0f84ba090734febe27393d4719682af0cae1b902d5ebf0236ecebea4
F src/trigger.c 5e68b790f022b8dafbfb0eb244786512a95c9575fc198719d2557d73e5795858
F src/update.c 5b0302c47cf31b533d5dff04c497ca1d8b9d89c39727e633fbe7b882fd5ac5aa
F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145
F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
F src/util.c 0be191521ff6d2805995f4910f0b6231b42843678b2efdc1abecaf39929a673f
F src/vacuum.c 84ce7f01f8a7a08748e107a441db83bcec13970190ddcb0c9ff522adbc1c23fd
F src/vdbe.c 0c7cb1b934ad8611e14e7efaf2c3a95df7dd3f7964d63ea07fef42a23df86131
F src/vdbe.c c2d6d0c0c343d8ebffef996c73cbb69e337225f757fea7fe5c0e3ea14662adec
F src/vdbe.h 58675f47dcf3105bab182c3ad3726efd60ffd003e954386904ac9107d0d2b743
F src/vdbeInt.h 17b7461ffcf9ee760d1341731715a419f6b8c763089a7ece25c2e8098d702b3f
F src/vdbeapi.c 1e8713d0b653acb43cd1bdf579c40e005c4844ea90f414f065946a83db3c27fb
F src/vdbeaux.c 6d0a75c1fbc7efea6924f6895ebceca664001464bc7ac56949d3c60aa5e498a0
F src/vdbeaux.c 87684b89877eae0c58c78b340bb5356aa1c8fb1dd650b29410c8b745aeeb20b5
F src/vdbeblob.c 5e61ce31aca17db8fb60395407457a8c1c7fb471dde405e0cd675974611dcfcd
F src/vdbemem.c 6cfed43758d57b6e3b99d9cdedfeccd86e45a07e427b22d8487cbdbebb6c522a
F src/vdbesort.c 43756031ca7430f7aec3ef904824a7883c4ede783e51f280d99b9b65c0796e35
@@ -723,11 +731,11 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c b9df133a705093da8977da5eb202eaadb844839f1c7297c08d33471f5491843d
F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
F src/where.c 1ef5aae7fac877057b9f360f06b26d4275888460d8fb6e92bbb9e70e07afe946
F src/where.c ea0f518df9e00aa44013a1d384090b4b3a499ee11d4daa0a7d99c4eb9f7ab4ba
F src/whereInt.h df0c79388c0b71b4a91f480d02791679fe0345d40410435c541c8893e95a4d3f
F src/wherecode.c 133a94f82858787217d073143617df19e4a6a7d0b771a1519f957608109ad5a5
F src/whereexpr.c 05295b44b54eea76d1ba766f0908928d0e20e990c249344c9521454d3d09c7ae
F src/window.c 928e215840e2f2d9a2746e018c9643ef42c66c4ab6630ef0df7fa388fa145e86
F src/window.c 14836767adb26573b50f528eb37f8b1336f2c430ab38de7cead1e5c546bb4d8c
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627
F test/affinity3.test b5c19d504dec222c0dc66642673d23dce915d35737b68e74d9f237b80493eb53
@@ -808,6 +816,7 @@ F test/backup_ioerr.test 4c3c7147cee85b024ecf6e150e090c32fdbb5135
F test/backup_malloc.test 0c9abdf74c51e7bedb66d504cd684f28d4bd4027
F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f
F test/badutf2.test f310fd3b24a491b6b77bccdf14923b85d6ebcce751068c180d93a6b8ff854399
F test/basexx1.test d8a50f0744b93dca656625597bcd3499ff4b9a4ea2a82432b119b7d46e3e0c08
F test/bc_common.tcl b5e42d80305be95697e6370e015af571e5333a1c
F test/bestindex1.test 856a453dff8c68b4568601eed5a8b5e20b4763af9229f3947c215729ed878db0
F test/bestindex2.test 394ff8fbf34703391247116d6a44e1c50ee7282236ee77909044573cefc37bc0
@@ -821,9 +830,9 @@ F test/bestindex9.test bf2eb8556e8d5c00ef3ee18c521751cd03c1b55454b6e7683b4c6742e
F test/between.test b9a65fb065391980119e8a781a7409d3fcf059d89968279c750e190a9a1d5263
F test/bigfile.test aa74f4e5db51c8e54a1d9de9fa65d01d1eb20b59
F test/bigfile2.test 1b489a3a39ae90c7f027b79110d6b4e1dbc71bfc
F test/bigmmap.test b820c234daa56d24bc3bf006e3ac7aa9d9623c8ac656a38f59063b444a2d65d1
F test/bigmmap.test 6021e205487347c6d7e5a541aa472a4b8efc4e9f4a3799a823b61a8e6616105d
F test/bigrow.test f0aeb7573dcb8caaafea76454be3ade29b7fc747
F test/bigsort.test 8299fa9298f4f1e02fc7d2712e8b77d6cd60e5a2
F test/bigsort.test 997e172009905873c06426145e4b3794c7dfe2d563724cb2fd39d45f319cf3d2
F test/bind.test 1e136709b306f7ed3192d349c2930d89df6ab621654ad6f1a72381d3fe76f483
F test/bind2.test 918bc35135f4141809ead7585909cde57d44db90a7a62aef540127148f91aab7
F test/bindxfer.test efecd12c580c14df5f4ad3b3e83c667744a4f7e0
@@ -901,7 +910,7 @@ F test/corruptH.test 79801d97ec5c2f9f3c87739aa1ec2eb786f96454
F test/corruptI.test a17bbf54fdde78d43cf3cc34b0057719fd4a173a3d824285b67dc5257c064c7b
F test/corruptJ.test 4d5ccc4bf959464229a836d60142831ef76a5aa4
F test/corruptK.test 5b4212fe346699831c5ad559a62c54e11c0611bdde1ea8423a091f9c01aa32af
F test/corruptL.test ecce40d7b9b909a670a42a45d86e30d927735d7e7f09041af438b19529d35532
F test/corruptL.test 9d1a0055c8db19baccd12f22ac36a33ec7d63afb59e82eb30835aea8f89b94df
F test/corruptM.test 7d574320e08c1b36caa3e47262061f186367d593a7e305d35f15289cc2c3e067
F test/corruptN.test 7c099d153a554001b4fb829c799b01f2ea6276cbc32479131e0db0da4efd9cc4
F test/cost.test b11cdbf9f11ffe8ef99c9881bf390e61fe92baf2182bad1dbe6de59a7295c576
@@ -934,7 +943,7 @@ F test/dbfuzz001.test 55e1a3504f8dea84155e09912fe3b1c3ad77e0b1a938ec42ca03b8e51b
F test/dbfuzz2-seed1.db e6225c6f3d7b63f9c5b6867146a5f329d997ab105bee64644dc2b3a2f2aebaee
F test/dbfuzz2.c 4b3c12de4d98b1b2d908ab03d217d4619e47c8b23d5e67f8a6f2b1bdee7cae23
F test/dbpage.test fce29035c7566fd7835ec0f19422cb4b9c6944ce0e1b936ff8452443f92e887d
F test/dbpagefault.test 20fe3a2a295f1c8cb30195b7b58a010530ecbc060e53e146af9e3e4fde4cab15
F test/dbpagefault.test b893e9e43d55edc0cdf3f71ae093802f80f0fb517d839eefeae5647c49a41f36
F test/dbstatus.test 4a4221a883025ffd39696b3d1b3910b928fb097d77e671351acb35f3aed42759
F test/dbstatus2.test f5fe0afed3fa45e57cfa70d1147606c20d2ba23feac78e9a172f2fe8ab5b78ef
F test/decimal.test fcf403fd5585f47342234e153c4a4338cd737b8e0884ac66fc484df47dbcf1a7
@@ -1089,7 +1098,7 @@ F test/fts3e.test 1f6c6ac9cc8b772ca256e6b22aaeed50c9350851
F test/fts3expr.test ebae205a7a89446c32583bcd492dcb817b9f6b31819bb4dde2583bb99c77e526
F test/fts3expr2.test 18da930352e5693eaa163a3eacf96233b7290d1a
F test/fts3expr3.test c4d4a7d6327418428c96e0a3a1137c251b8dfbf8
F test/fts3expr4.test f5b2832549f01b1f7f73389fa21d4b875499bc95bf7c8b36271844888c6a0938
F test/fts3expr4.test 6c7675bbdbffe6ffc95e9e861500b8ac3f739c4d004ffda812f138eeb1b45529
F test/fts3expr5.test a5b9a053becbdb8e973fbf4d6d3abaabeb42d511d1848bd57931f3e0a1cf983e
F test/fts3f.test 8c438d5e1cab526b0021988fb1dc70cf3597b006a33ffd6c955ee89929077fe3
F test/fts3fault.test f4e1342acfe6d216a001490e8cd52afac1f9ffe4a11bbcdcb296129a45c5df45
@@ -1335,7 +1344,7 @@ F test/mmapfault.test d4c9eff9cd8c2dc14bc43e71e042f175b0a26fe3
F test/mmapwarm.test 2272005969cd17a910077bd5082f70bc1fefad9a875afec7fc9af483898ecaf3
F test/multiplex.test d74c034e52805f6de8cc5432cef8c9eb774bb64ec29b83a22effc8ca4dac1f08
F test/multiplex2.test 580ca5817c7edbe4cc68fa150609c9473393003a
F test/multiplex3.test d228f59eac91839a977eac19f21d053f03e4d101
F test/multiplex3.test fac575e0b1b852025575a6a8357701d80933e98b5d2fe6d35ddaa68f92f6a1f7
F test/multiplex4.test e8ae4c4bd70606a5727743241f13b5701990abe4
F test/mutex1.test 177db2e4edb530f2ff21edc52ac79a412dbe63e4c47c3ae9504d3fb4f1ce81fa
F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660
@@ -1385,9 +1394,9 @@ F test/parser1.test 6ccdf5e459a5dc4673d3273dc311a7e9742ca952dd0551a6a6320d27035c
F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
F test/permutations.test 650d89ab5aad0c9fab9325b11deca8662cb5e72f43e005073d35f12ad00eaca2
F test/permutations.test 3e0d6eea70e5087f3240b1a2fe621b0c73445f38a262029f0a1d2d89564026f7
F test/pg_common.tcl 3b27542224db1e713ae387459b5d117c836a5f6e328846922993b6d2b7640d9f
F test/pragma.test cae534c12a033a5c319ccc94f50b32811acdef9f67bf19a82ff42697caccd69f
F test/pragma.test 620622fb0815f1cbea8e26e1d8abad38e0cbcbed8927fd84048fe9fd6239e323
F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
F test/pragma3.test 92a46bbea12322dd94a404f49edcfbfc913a2c98115f0d030a7459bb4712ef31
F test/pragma4.test ca5e4dfc46adfe490f75d73734f70349d95a199e6510973899e502eef2c8b1f8
@@ -1398,7 +1407,7 @@ F test/printf.test 390d0d7fcffc3c4ea3c1bb4cbb267444e32b33b048ae21895f23a291844fe
F test/printf2.test 3f55c1871a5a65507416076f6eb97e738d5210aeda7595a74ee895f2224cce60
F test/progress.test ebab27f670bd0d4eb9d20d49cef96e68141d92fb
F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc
F test/pushdown.test 5e72c51c5e33253ed639ccee1e01ce62d62b6eee5ca893cd82334e4ee7b1d7fc
F test/pushdown.test c69f0970ea17e0afc674b89741f60c172cb6f761d81665fc71015f674f0f66ba
F test/queryonly.test 5f653159e0f552f0552d43259890c1089391dcca
F test/quick.test 1681febc928d686362d50057c642f77a02c62e57
F test/quota-glob.test 32901e9eed6705d68ca3faee2a06b73b57cb3c26
@@ -1409,14 +1418,14 @@ F test/randexpr1.tcl 40dec52119ed3a2b8b2a773bce24b63a3a746459
F test/randexpr1.test eda062a97e60f9c38ae8d806b03b0ddf23d796df
F test/rbu.test 168573d353cd0fd10196b87b0caa322c144ef736
F test/rdonly.test 64e2696c322e3538df0b1ed624e21f9a23ed9ff8
F test/recover.test cd81539661b9a6263d68f7bfd1440f4ac0aeb68f0fa0370db32a9e706da89de7
F test/regexp1.test 83c631617357150f8054ca1d1fed40a552b0d0f8eb7a7f090c3be02cee9f9913
F test/recover.test fd5199f928757cb308661b5fdca1abc19398a798ff7f24b57c3071e9f8e0471e
F test/regexp1.test 8f2a8bc1569666e29a4cee6c1a666cd224eb6d50e2470d1dc1df995170f3e0f1
F test/regexp2.test 55ed41da802b0e284ac7e2fe944be3948f93ff25abbca0361a609acfed1368b5
F test/reindex.test cd9d6021729910ece82267b4f5e1b5ac2911a7566c43b43c176a6a4732e2118d
F test/releasetest_data.tcl 11ba48a21ed1c808147b0e77c6e93d204577f4327ffe6d7c3b34cd3c01eac3a2
F test/releasetest_data.tcl 0db8aee0c348090fd06da47020ab4ed8ec692e0723427b2f3947d4dfb806f3b0
F test/resetdb.test 8062cf10a09d8c048f8de7711e94571c38b38168db0e5877ba7561789e5eeb2b
F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb
F test/returning1.test c43b8370a351f77aec6d71f4a2cde59b849369ed1933261a2c2c69e23e34ff5e
F test/returning1.test 1366e04566cfe1a082d17b1e0f195ec64473c79374b3a5d4ae00c43d885dea31
F test/returningfault.test ae4c4b5e8745813287a359d9ccdb9d5c883c2e68afb18fb0767937d5de5692a4
F test/rollback.test 06680159bc6746d0f26276e339e3ae2f951c64812468308838e0a3362d911eaa
F test/rollback2.test 3f3a4e20401825017df7e7671e9f31b6de5fae5620c2b9b49917f52f8c160a8f
@@ -1492,7 +1501,7 @@ F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
F test/shell1.test e4b4de56f454708e0747b52915135baa2cbfec4965406d6eaf02a4a5c22a9880
F test/shell2.test c536c2aab4852608f8a606262330797abc4d964a4c2c782a7760f54ea1f17a6a
F test/shell3.test 91febeac0412812bf6370abb8ed72700e32bf8f9878849414518f662dfd55e8a
F test/shell4.test 7dc8a515705bc093d8ffe381670e8fa7a969661e8ed177c35c847e3c6dfc35e2
F test/shell4.test 9abd0c12a7e20a4c49e84d5be208d2124fa6c09e728f56f1f4bee0f02853935f
F test/shell5.test c8b6c54f26ec537f8558273d7ed293ca3725ef42e6b12b8f151718628bd1473b
F test/shell6.test 1ceb51b2678c472ba6cf1e5da96679ce8347889fe2c3bf93a0e0fa73f00b00d3
F test/shell7.test 115132f66d0463417f408562cc2cf534f6bbc6d83a6d50f0072a9eb171bae97f
@@ -1617,12 +1626,13 @@ F test/tkt-78e04e52ea.test b731f2ab7d1c2482ac5152097da02ef4805a45147ba9498d3cd9d
F test/tkt-7a31705a7e6.test 9e9c057b6a9497c8f7ba7b16871029414ccf6550e7345d9085d6d71c9a56bb6f
F test/tkt-7bbfb7d442.test e87b59e620700b5a52ecd92f05d56686c1cad9e1aa17456eada55e0bb821b698
F test/tkt-80ba201079.test 105a721e6aad0ae3c5946d7615d1e4d03f6145b8
F test/tkt-80e031a00f.test 9ee36348b761bf7c14261e002b75a4c0d5a04d4c
F test/tkt-80e031a00f.test 7c93af53f43527f50020983a4bcc39b077e77c7362d7af8e04a5fd155fe5e829
F test/tkt-8454a207b9.test aff2e76143cfa443ddce6f7d85968a2e9b57a3deb0b881b730120740555f9e2f
F test/tkt-868145d012.test a5f941107ece6a64410ca4755c6329b7eb57a356
F test/tkt-8c63ff0ec.test 258b7fc8d7e4e1cb5362c7d65c143528b9c4cbed
F test/tkt-91e2e8ba6f.test 08c4f94ae07696b05c9b822da0b4e5337a2f54c5
F test/tkt-94c04eaadb.test f738c57c7f68ab8be1c054415af7774617cb6223
F test/tkt-99378177930f87bd.test 0f932e85fa1d41f30532cb7be9718d82e491e953123b8c4c85cf025f36ffe34b
F test/tkt-9a8b09f8e6.test b2ef151d0984b2ebf237760dbeaa50724e5a0667
F test/tkt-9d68c883.test 16f7cb96781ba579bc2e19bb14b4ad609d9774b6
F test/tkt-9f2eb3abac.test cb6123ac695a08b4454c3792fbe85108f67fabf8
@@ -1872,7 +1882,7 @@ F test/walslow.test c05c68d4dc2700a982f89133ce103a1a84cc285f
F test/walthread.test 14b20fcfa6ae152f5d8e12f5dc8a8a724b7ef189f5d8ef1e2ceab79f2af51747
F test/walvfs.test e1a6ad0f3c78e98b55c3d5f0889cf366cc0d0a1cb2bccb44ac9ec67384adc4a1
F test/wapp.tcl b440cd8cf57953d3a49e7ee81e6a18f18efdaf113b69f7d8482b0710a64566ec
F test/wapptest.tcl e3b6d5afa021c39a0f459ea9fbd1077459c1d81fca98eb40af8404ad3fc2360f x
F test/wapptest.tcl 1bea58a6a8e68a73f542ee4fca28b771b84ed803bd0c9e385087070b3d747b3c x
F test/where.test d13cd7c24e80009d2b54e2f7a8893c457afa49c64f99359c9eb3fe668ba1d9d4
F test/where2.test 03c21a11e7b90e2845fc3c8b4002fc44cc2797fa74c86ee47d70bd7ea4f29ed6
F test/where3.test 5b4ffc0ac2ea0fe92f02b1244b7531522fe4d7bccf6fa8741d54e82c10e67753
@@ -1986,12 +1996,12 @@ F tool/mkopcodec.tcl 33d20791e191df43209b77d37f0ff0904620b28465cca6990cf8d60da61
F tool/mkopcodeh.tcl bcb2bd5affb545fd219ef0304c7978e2a356407ab723f45ec8569235892c1c3f
F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa
F tool/mkpragmatab.tcl bd07bd59d45d0f3448e123d6937e9811195f9908a51e09d774609883055bfd3d
F tool/mkshellc.tcl df5d249617f9cc94d5c48eb0401673eb3f31f383ecbc54e8a13ca3dd97e89450
F tool/mkshellc.tcl 02d0de8349ef830c0fb20d29680320bde2466e2ec422e5bd94c4317a7a7e8cc9
F tool/mksourceid.c 36aa8020014aed0836fd13c51d6dc9219b0df1761d6b5f58ff5b616211b079b9
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
F tool/mksqlite3c-noext.tcl 4f7cfef5152b0c91920355cbfc1d608a4ad242cb819f1aea07f6d0274f584a7f
F tool/mksqlite3c.tcl 4fc26a9bfa0c4505b203d7ca0cf086e75ebcd4d63eb719c82f37e3fecdf23d37
F tool/mksqlite3h.tcl 1f5e4a1dbbbc43c83cc6e74fe32c6c620502240b66c7c0f33a51378e78fc4edf
F tool/mksqlite3c.tcl eb47021591b1ad4a6862e2cb5625f1ac67ec1e0c6db5ba3953c069c635284cf5
F tool/mksqlite3h.tcl d391cff7cad0a372ee1406faee9ccc7dad9cb80a0c95cae0f73d10dd26e06762
F tool/mksqlite3internalh.tcl eb994013e833359137eb53a55acdad0b5ae1049b
F tool/mkvsix.tcl b9e0777a213c23156b6542842c238479e496ebf5
F tool/offsets.c 8ed2b344d33f06e71366a9b93ccedaa38c096cc1dbd4c3c26ad08c6115285845
@@ -2011,7 +2021,7 @@ F tool/showstat4.c 0682ebea7abf4d3657f53c4a243f2e7eab48eab344ed36a94bb75dcd19a5c
F tool/showwal.c 0253c187ae16fdae9cde89e63e1dfcd3bb35e5416d066415f99e2f8cac6ab03d
F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe
F tool/spaceanal.tcl 1b5be34c6223cb1af06da2a10fb77863eb869b1962d055820b0a11cf2336ab45
F tool/speed-check.sh 13f8e07dbfe25f3aecda33fb6068894665af61ca1360a7b654be0ad0c3f3ae0b
F tool/speed-check.sh 9b27e158330a6587e92214b2344cc6fadde514996c0648fc0de7955ef7a79d77
F tool/speedtest.tcl 06c76698485ccf597b9e7dbb1ac70706eb873355
F tool/speedtest16.c ecb6542862151c3e6509bbc00509b234562ae81e
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
@@ -2054,8 +2064,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P f6fa0cffa921ccde8910e7fa4a63c2e4ef8ddb376c8ce99e436b27ac332c4498
R 49cc6fc37416a3ba7fdfd0b20ce15895
U stephan
Z b66cc4ccdf23cf485ede2fda7e429a86
P 17b823500a2ed135c1f40aa7f4d87ba5b2eab35c0abd9e0856041cf0f510cbee a2449bcc2c71d0f4c3289621fbf1cb97f0f407c9f7b5bf18245b7854a07c6cfa
R b25d12f698cc565856f0ef09be44e05e
U larrybr
Z cf8d223b7c2dae4330a88af45b5f7bd0
# Remove this line to create a well-formed Fossil manifest.
+1 -1
View File
@@ -1 +1 @@
70ee6ee014bc4e2c1daa9b4a8909cf76ccecf32de1eb39055f20d3d0b1daa1bd
b8345630a2a322234bda49ee4b996f6ba20e2b080621e229a2ec5e820892a663
+119 -109
View File
@@ -1655,7 +1655,6 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){
** fragmented bytes within the page. */
memcpy(&aData[iAddr], &aData[pc], 2);
aData[hdr+7] += (u8)x;
testcase( pc+x>maxPC );
return &aData[pc];
}else if( x+pc > maxPC ){
/* This slot extends off the end of the usable part of the page */
@@ -1901,62 +1900,67 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
** Only the following combinations are supported. Anything different
** indicates a corrupt database files:
**
** PTF_ZERODATA
** PTF_ZERODATA | PTF_LEAF
** PTF_LEAFDATA | PTF_INTKEY
** PTF_LEAFDATA | PTF_INTKEY | PTF_LEAF
** PTF_ZERODATA (0x02, 2)
** PTF_LEAFDATA | PTF_INTKEY (0x05, 5)
** PTF_ZERODATA | PTF_LEAF (0x0a, 10)
** PTF_LEAFDATA | PTF_INTKEY | PTF_LEAF (0x0d, 13)
*/
static int decodeFlags(MemPage *pPage, int flagByte){
BtShared *pBt; /* A copy of pPage->pBt */
assert( pPage->hdrOffset==(pPage->pgno==1 ? 100 : 0) );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
pPage->leaf = (u8)(flagByte>>3); assert( PTF_LEAF == 1<<3 );
flagByte &= ~PTF_LEAF;
pPage->childPtrSize = 4-4*pPage->leaf;
pBt = pPage->pBt;
if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){
/* EVIDENCE-OF: R-07291-35328 A value of 5 (0x05) means the page is an
** interior table b-tree page. */
assert( (PTF_LEAFDATA|PTF_INTKEY)==5 );
/* EVIDENCE-OF: R-26900-09176 A value of 13 (0x0d) means the page is a
** leaf table b-tree page. */
assert( (PTF_LEAFDATA|PTF_INTKEY|PTF_LEAF)==13 );
pPage->intKey = 1;
if( pPage->leaf ){
pPage->max1bytePayload = pBt->max1bytePayload;
if( flagByte>=(PTF_ZERODATA | PTF_LEAF) ){
pPage->childPtrSize = 0;
pPage->leaf = 1;
if( flagByte==(PTF_LEAFDATA | PTF_INTKEY | PTF_LEAF) ){
pPage->intKeyLeaf = 1;
pPage->xCellSize = cellSizePtrTableLeaf;
pPage->xParseCell = btreeParseCellPtr;
pPage->intKey = 1;
pPage->maxLocal = pBt->maxLeaf;
pPage->minLocal = pBt->minLeaf;
}else if( flagByte==(PTF_ZERODATA | PTF_LEAF) ){
pPage->intKey = 0;
pPage->intKeyLeaf = 0;
pPage->xCellSize = cellSizePtr;
pPage->xParseCell = btreeParseCellPtrIndex;
pPage->maxLocal = pBt->maxLocal;
pPage->minLocal = pBt->minLocal;
}else{
pPage->intKey = 0;
pPage->intKeyLeaf = 0;
pPage->xCellSize = cellSizePtr;
pPage->xParseCell = btreeParseCellPtrIndex;
return SQLITE_CORRUPT_PAGE(pPage);
}
}else{
pPage->childPtrSize = 4;
pPage->leaf = 0;
if( flagByte==(PTF_ZERODATA) ){
pPage->intKey = 0;
pPage->intKeyLeaf = 0;
pPage->xCellSize = cellSizePtr;
pPage->xParseCell = btreeParseCellPtrIndex;
pPage->maxLocal = pBt->maxLocal;
pPage->minLocal = pBt->minLocal;
}else if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){
pPage->intKeyLeaf = 0;
pPage->xCellSize = cellSizePtrNoPayload;
pPage->xParseCell = btreeParseCellPtrNoPayload;
pPage->intKey = 1;
pPage->maxLocal = pBt->maxLeaf;
pPage->minLocal = pBt->minLeaf;
}else{
pPage->intKey = 0;
pPage->intKeyLeaf = 0;
pPage->xCellSize = cellSizePtr;
pPage->xParseCell = btreeParseCellPtrIndex;
return SQLITE_CORRUPT_PAGE(pPage);
}
pPage->maxLocal = pBt->maxLeaf;
pPage->minLocal = pBt->minLeaf;
}else if( flagByte==PTF_ZERODATA ){
/* EVIDENCE-OF: R-43316-37308 A value of 2 (0x02) means the page is an
** interior index b-tree page. */
assert( (PTF_ZERODATA)==2 );
/* EVIDENCE-OF: R-59615-42828 A value of 10 (0x0a) means the page is a
** leaf index b-tree page. */
assert( (PTF_ZERODATA|PTF_LEAF)==10 );
pPage->intKey = 0;
pPage->intKeyLeaf = 0;
pPage->xCellSize = cellSizePtr;
pPage->xParseCell = btreeParseCellPtrIndex;
pPage->maxLocal = pBt->maxLocal;
pPage->minLocal = pBt->minLocal;
}else{
/* EVIDENCE-OF: R-47608-56469 Any other value for the b-tree page type is
** an error. */
pPage->intKey = 0;
pPage->intKeyLeaf = 0;
pPage->xCellSize = cellSizePtr;
pPage->xParseCell = btreeParseCellPtrIndex;
return SQLITE_CORRUPT_PAGE(pPage);
}
pPage->max1bytePayload = pBt->max1bytePayload;
return SQLITE_OK;
}
@@ -5496,9 +5500,25 @@ int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
** on success. Set *pRes to 0 if the cursor actually points to something
** or set *pRes to 1 if the table is empty.
*/
static SQLITE_NOINLINE int btreeLast(BtCursor *pCur, int *pRes){
int rc = moveToRoot(pCur);
if( rc==SQLITE_OK ){
assert( pCur->eState==CURSOR_VALID );
*pRes = 0;
rc = moveToRightmost(pCur);
if( rc==SQLITE_OK ){
pCur->curFlags |= BTCF_AtLast;
}else{
pCur->curFlags &= ~BTCF_AtLast;
}
}else if( rc==SQLITE_EMPTY ){
assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 );
*pRes = 1;
rc = SQLITE_OK;
}
return rc;
}
int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
int rc;
assert( cursorOwnsBtShared(pCur) );
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
@@ -5519,23 +5539,7 @@ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
*pRes = 0;
return SQLITE_OK;
}
rc = moveToRoot(pCur);
if( rc==SQLITE_OK ){
assert( pCur->eState==CURSOR_VALID );
*pRes = 0;
rc = moveToRightmost(pCur);
if( rc==SQLITE_OK ){
pCur->curFlags |= BTCF_AtLast;
}else{
pCur->curFlags &= ~BTCF_AtLast;
}
}else if( rc==SQLITE_EMPTY ){
assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 );
*pRes = 1;
rc = SQLITE_OK;
}
return rc;
return btreeLast(pCur, pRes);
}
/* Move the cursor so that it points to an entry in a table (a.k.a INTKEY)
@@ -6256,8 +6260,8 @@ static int allocateBtreePage(
assert( eMode==BTALLOC_ANY || (nearby>0 && IfNotOmitAV(pBt->autoVacuum)) );
pPage1 = pBt->pPage1;
mxPage = btreePagecount(pBt);
/* EVIDENCE-OF: R-05119-02637 The 4-byte big-endian integer at offset 36
** stores stores the total number of pages on the freelist. */
/* EVIDENCE-OF: R-21003-45125 The 4-byte big-endian integer at offset 36
** stores the total number of pages on the freelist. */
n = get4byte(&pPage1->aData[36]);
testcase( n==mxPage-1 );
if( n>=mxPage ){
@@ -6602,7 +6606,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
/* If the database supports auto-vacuum, write an entry in the pointer-map
** to indicate that the page is free.
*/
if( ISAUTOVACUUM ){
if( ISAUTOVACUUM(pBt) ){
ptrmapPut(pBt, iPage, PTRMAP_FREEPAGE, 0, &rc);
if( rc ) goto freepage_out;
}
@@ -7042,24 +7046,20 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
** in pTemp or the original pCell) and also record its index.
** Allocating a new entry in pPage->aCell[] implies that
** pPage->nOverflow is incremented.
**
** *pRC must be SQLITE_OK when this routine is called.
*/
static void insertCell(
static int insertCell(
MemPage *pPage, /* Page into which we are copying */
int i, /* New cell becomes the i-th cell of the page */
u8 *pCell, /* Content of the new cell */
int sz, /* Bytes of content in pCell */
u8 *pTemp, /* Temp storage space for pCell, if needed */
Pgno iChild, /* If non-zero, replace first 4 bytes with this value */
int *pRC /* Read and write return code from here */
Pgno iChild /* If non-zero, replace first 4 bytes with this value */
){
int idx = 0; /* Where to write new cell content in data[] */
int j; /* Loop counter */
u8 *data; /* The content of the whole page */
u8 *pIns; /* The point in pPage->aCellIdx[] where no cell inserted */
assert( *pRC==SQLITE_OK );
assert( i>=0 && i<=pPage->nCell+pPage->nOverflow );
assert( MX_CELL(pPage->pBt)<=10921 );
assert( pPage->nCell<=MX_CELL(pPage->pBt) || CORRUPT_DB );
@@ -7094,14 +7094,13 @@ static void insertCell(
}else{
int rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc!=SQLITE_OK ){
*pRC = rc;
return;
return rc;
}
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
data = pPage->aData;
assert( &data[pPage->cellOffset]==pPage->aCellIdx );
rc = allocateSpace(pPage, sz, &idx);
if( rc ){ *pRC = rc; return; }
if( rc ){ return rc; }
/* The allocateSpace() routine guarantees the following properties
** if it returns successfully */
assert( idx >= 0 );
@@ -7128,13 +7127,16 @@ static void insertCell(
assert( get2byte(&data[pPage->hdrOffset+3])==pPage->nCell || CORRUPT_DB );
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pPage->pBt->autoVacuum ){
int rc = SQLITE_OK;
/* The cell may contain a pointer to an overflow page. If so, write
** the entry for the overflow page into the pointer map.
*/
ptrmapPutOvflPtr(pPage, pPage, pCell, pRC);
ptrmapPutOvflPtr(pPage, pPage, pCell, &rc);
if( rc ) return rc;
}
#endif
}
return SQLITE_OK;
}
/*
@@ -7235,14 +7237,16 @@ struct CellArray {
** computed.
*/
static void populateCellCache(CellArray *p, int idx, int N){
MemPage *pRef = p->pRef;
u16 *szCell = p->szCell;
assert( idx>=0 && idx+N<=p->nCell );
while( N>0 ){
assert( p->apCell[idx]!=0 );
if( p->szCell[idx]==0 ){
p->szCell[idx] = p->pRef->xCellSize(p->pRef, p->apCell[idx]);
if( szCell[idx]==0 ){
szCell[idx] = pRef->xCellSize(pRef, p->apCell[idx]);
}else{
assert( CORRUPT_DB ||
p->szCell[idx]==p->pRef->xCellSize(p->pRef, p->apCell[idx]) );
szCell[idx]==pRef->xCellSize(pRef, p->apCell[idx]) );
}
idx++;
N--;
@@ -7444,8 +7448,8 @@ static int pageFreeArray(
int nRet = 0;
int i;
int iEnd = iFirst + nCell;
u8 *pFree = 0;
int szFree = 0;
u8 *pFree = 0; /* \__ Parameters for pending call to */
int szFree = 0; /* / freeSpace() */
for(i=iFirst; i<iEnd; i++){
u8 *pCell = pCArray->apCell[i];
@@ -7466,6 +7470,9 @@ static int pageFreeArray(
return 0;
}
}else{
/* The current cell is adjacent to and before the pFree cell.
** Combine the two regions into one to reduce the number of calls
** to freeSpace(). */
pFree = pCell;
szFree += sz;
}
@@ -7673,7 +7680,7 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){
** be marked as dirty. Returning an error code will cause a
** rollback, undoing any changes made to the parent page.
*/
if( ISAUTOVACUUM ){
if( ISAUTOVACUUM(pBt) ){
ptrmapPut(pBt, pgnoNew, PTRMAP_BTREE, pParent->pgno, &rc);
if( szCell>pNew->minLocal ){
ptrmapPutOvflPtr(pNew, pNew, pCell, &rc);
@@ -7701,8 +7708,8 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){
/* Insert the new divider cell into pParent. */
if( rc==SQLITE_OK ){
insertCell(pParent, pParent->nCell, pSpace, (int)(pOut-pSpace),
0, pPage->pgno, &rc);
rc = insertCell(pParent, pParent->nCell, pSpace, (int)(pOut-pSpace),
0, pPage->pgno);
}
/* Set the right-child pointer of pParent to point to the new page. */
@@ -7811,7 +7818,7 @@ static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){
/* If this is an auto-vacuum database, update the pointer-map entries
** for any b-tree or overflow pages that pTo now contains the pointers to.
*/
if( ISAUTOVACUUM ){
if( ISAUTOVACUUM(pBt) ){
*pRC = setChildPtrmaps(pTo);
}
}
@@ -8235,15 +8242,17 @@ static int balance_nonroot(
d = r + 1 - leafData;
(void)cachedCellSize(&b, d);
do{
int szR, szD;
assert( d<nMaxCells );
assert( r<nMaxCells );
(void)cachedCellSize(&b, r);
szR = cachedCellSize(&b, r);
szD = b.szCell[d];
if( szRight!=0
&& (bBulk || szRight+b.szCell[d]+2 > szLeft-(b.szCell[r]+(i==k-1?0:2)))){
&& (bBulk || szRight+szD+2 > szLeft-(szR+(i==k-1?0:2)))){
break;
}
szRight += b.szCell[d] + 2;
szLeft -= b.szCell[r] + 2;
szRight += szD + 2;
szLeft -= szR + 2;
cntNew[i-1] = r;
r--;
d--;
@@ -8297,7 +8306,7 @@ static int balance_nonroot(
cntOld[i] = b.nCell;
/* Set the pointer-map entry for the new sibling page. */
if( ISAUTOVACUUM ){
if( ISAUTOVACUUM(pBt) ){
ptrmapPut(pBt, pNew->pgno, PTRMAP_BTREE, pParent->pgno, &rc);
if( rc!=SQLITE_OK ){
goto balance_cleanup;
@@ -8390,7 +8399,7 @@ static int balance_nonroot(
** updated. This happens below, after the sibling pages have been
** populated, not here.
*/
if( ISAUTOVACUUM ){
if( ISAUTOVACUUM(pBt) ){
MemPage *pOld;
MemPage *pNew = pOld = apNew[0];
int cntOldNext = pNew->nCell + pNew->nOverflow;
@@ -8487,7 +8496,7 @@ static int balance_nonroot(
rc = SQLITE_CORRUPT_BKPT;
goto balance_cleanup;
}
insertCell(pParent, nxDiv+i, pCell, sz, pTemp, pNew->pgno, &rc);
rc = insertCell(pParent, nxDiv+i, pCell, sz, pTemp, pNew->pgno);
if( rc!=SQLITE_OK ) goto balance_cleanup;
assert( sqlite3PagerIswriteable(pParent->pDbPage) );
}
@@ -8583,7 +8592,7 @@ static int balance_nonroot(
);
copyNodeContent(apNew[0], pParent, &rc);
freePage(apNew[0], &rc);
}else if( ISAUTOVACUUM && !leafCorrection ){
}else if( ISAUTOVACUUM(pBt) && !leafCorrection ){
/* Fix the pointer map entries associated with the right-child of each
** sibling page. All other pointer map entries have already been taken
** care of. */
@@ -8604,7 +8613,7 @@ static int balance_nonroot(
}
#if 0
if( ISAUTOVACUUM && rc==SQLITE_OK && apNew[0]->isInit ){
if( ISAUTOVACUUM(pBt) && rc==SQLITE_OK && apNew[0]->isInit ){
/* The ptrmapCheckPages() contains assert() statements that verify that
** all pointer map pages are set correctly. This is helpful while
** debugging. This is usually disabled because a corrupt database may
@@ -8666,7 +8675,7 @@ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){
if( rc==SQLITE_OK ){
rc = allocateBtreePage(pBt,&pChild,&pgnoChild,pRoot->pgno,0);
copyNodeContent(pRoot, pChild, &rc);
if( ISAUTOVACUUM ){
if( ISAUTOVACUUM(pBt) ){
ptrmapPut(pBt, pgnoChild, PTRMAP_BTREE, pRoot->pgno, &rc);
}
}
@@ -8998,7 +9007,6 @@ int sqlite3BtreeInsert(
int idx;
MemPage *pPage;
Btree *p = pCur->pBtree;
BtShared *pBt = p->pBt;
unsigned char *oldCell;
unsigned char *newCell = 0;
@@ -9017,7 +9025,7 @@ int sqlite3BtreeInsert(
** not to clear the cursor here.
*/
if( pCur->curFlags & BTCF_Multiple ){
rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
rc = saveAllCursors(p->pBt, pCur->pgnoRoot, pCur);
if( rc ) return rc;
if( loc && pCur->iPage<0 ){
/* This can only happen if the schema is corrupt such that there is more
@@ -9041,8 +9049,8 @@ int sqlite3BtreeInsert(
assert( cursorOwnsBtShared(pCur) );
assert( (pCur->curFlags & BTCF_WriteFlag)!=0
&& pBt->inTransaction==TRANS_WRITE
&& (pBt->btsFlags & BTS_READ_ONLY)==0 );
&& p->pBt->inTransaction==TRANS_WRITE
&& (p->pBt->btsFlags & BTS_READ_ONLY)==0 );
assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );
/* Assert that the caller has been consistent. If this cursor was opened
@@ -9159,26 +9167,28 @@ int sqlite3BtreeInsert(
pCur->pgnoRoot, pX->nKey, pX->nData, pPage->pgno,
loc==0 ? "overwrite" : "new entry"));
assert( pPage->isInit || CORRUPT_DB );
newCell = pBt->pTmpSpace;
newCell = p->pBt->pTmpSpace;
assert( newCell!=0 );
assert( BTREE_PREFORMAT==OPFLAG_PREFORMAT );
if( flags & BTREE_PREFORMAT ){
rc = SQLITE_OK;
szNew = pBt->nPreformatSize;
szNew = p->pBt->nPreformatSize;
if( szNew<4 ) szNew = 4;
if( ISAUTOVACUUM && szNew>pPage->maxLocal ){
if( ISAUTOVACUUM(p->pBt) && szNew>pPage->maxLocal ){
CellInfo info;
pPage->xParseCell(pPage, newCell, &info);
if( info.nPayload!=info.nLocal ){
Pgno ovfl = get4byte(&newCell[szNew-4]);
ptrmapPut(pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, &rc);
ptrmapPut(p->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, &rc);
if( NEVER(rc) ) goto end_insert;
}
}
}else{
rc = fillInCell(pPage, newCell, pX, &szNew);
if( rc ) goto end_insert;
}
if( rc ) goto end_insert;
assert( szNew==pPage->xCellSize(pPage, newCell) );
assert( szNew <= MX_CELL_SIZE(pBt) );
assert( szNew <= MX_CELL_SIZE(p->pBt) );
idx = pCur->ix;
if( loc==0 ){
CellInfo info;
@@ -9198,7 +9208,7 @@ int sqlite3BtreeInsert(
testcase( pCur->curFlags & BTCF_ValidOvfl );
invalidateOverflowCache(pCur);
if( info.nSize==szNew && info.nLocal==info.nPayload
&& (!ISAUTOVACUUM || szNew<pPage->minLocal)
&& (!ISAUTOVACUUM(p->pBt) || szNew<pPage->minLocal)
){
/* Overwrite the old cell with the new if they are the same size.
** We could also try to do this if the old cell is smaller, then add
@@ -9228,7 +9238,7 @@ int sqlite3BtreeInsert(
}else{
assert( pPage->leaf );
}
insertCell(pPage, idx, newCell, szNew, 0, 0, &rc);
rc = insertCell(pPage, idx, newCell, szNew, 0, 0);
assert( pPage->nOverflow==0 || rc==SQLITE_OK );
assert( rc!=SQLITE_OK || pPage->nCell>0 || pPage->nOverflow>0 );
@@ -9301,7 +9311,6 @@ end_insert:
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
*/
int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 iKey){
int rc = SQLITE_OK;
BtShared *pBt = pDest->pBt;
u8 *aOut = pBt->pTmpSpace; /* Pointer to next output buffer */
const u8 *aIn; /* Pointer to next input buffer */
@@ -9324,7 +9333,9 @@ int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 iKey){
if( nIn==nRem && nIn<pDest->pPage->maxLocal ){
memcpy(aOut, aIn, nIn);
pBt->nPreformatSize = nIn + (aOut - pBt->pTmpSpace);
return SQLITE_OK;
}else{
int rc = SQLITE_OK;
Pager *pSrcPager = pSrc->pBt->pPager;
u8 *pPgnoOut = 0;
Pgno ovflIn = 0;
@@ -9376,7 +9387,7 @@ int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 iKey){
MemPage *pNew = 0;
rc = allocateBtreePage(pBt, &pNew, &pgnoNew, 0, 0);
put4byte(pPgnoOut, pgnoNew);
if( ISAUTOVACUUM && pPageOut ){
if( ISAUTOVACUUM(pBt) && pPageOut ){
ptrmapPut(pBt, pgnoNew, PTRMAP_OVERFLOW2, pPageOut->pgno, &rc);
}
releasePage(pPageOut);
@@ -9392,9 +9403,8 @@ int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 iKey){
releasePage(pPageOut);
sqlite3PagerUnref(pPageIn);
return rc;
}
return rc;
}
/*
@@ -9549,7 +9559,7 @@ int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
assert( pTmp!=0 );
rc = sqlite3PagerWrite(pLeaf->pDbPage);
if( rc==SQLITE_OK ){
insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n, &rc);
rc = insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n);
}
dropCell(pLeaf, pLeaf->nCell-1, nCell, &rc);
if( rc ) return rc;
+1 -1
View File
@@ -183,7 +183,7 @@ int sqlite3BtreeNewDb(Btree *p);
** reduce network bandwidth.
**
** Note that BTREE_HINT_FLAGS with BTREE_BULKLOAD is the only hint used by
** standard SQLite. The other hints are provided for extentions that use
** standard SQLite. The other hints are provided for extensions that use
** the SQLite parser and code generator but substitute their own storage
** engine.
*/
+2 -2
View File
@@ -674,9 +674,9 @@ struct BtCursor {
** So, this macro is defined instead.
*/
#ifndef SQLITE_OMIT_AUTOVACUUM
#define ISAUTOVACUUM (pBt->autoVacuum)
#define ISAUTOVACUUM(pBt) (pBt->autoVacuum)
#else
#define ISAUTOVACUUM 0
#define ISAUTOVACUUM(pBt) 0
#endif
+118 -74
View File
@@ -53,7 +53,7 @@ char sqlite3ExprAffinity(const Expr *pExpr){
}
op = pExpr->op;
if( op==TK_REGISTER ) op = pExpr->op2;
if( op==TK_COLUMN || op==TK_AGG_COLUMN ){
if( op==TK_COLUMN || (op==TK_AGG_COLUMN && pExpr->y.pTab!=0) ){
assert( ExprUseYTab(pExpr) );
assert( pExpr->y.pTab!=0 );
return sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn);
@@ -173,7 +173,9 @@ CollSeq *sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr){
while( p ){
int op = p->op;
if( op==TK_REGISTER ) op = p->op2;
if( op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_TRIGGER ){
if( (op==TK_AGG_COLUMN && p->y.pTab!=0)
|| op==TK_COLUMN || op==TK_TRIGGER
){
int j;
assert( ExprUseYTab(p) );
assert( p->y.pTab!=0 );
@@ -4037,7 +4039,7 @@ static int exprCodeInlineFunction(
}
/*
** Check to see if pExpr is one of the indexed expressions on pParse->pIdxExpr.
** Check to see if pExpr is one of the indexed expressions on pParse->pIdxEpr.
** If it is, then resolve the expression by reading from the index and
** return the register into which the value has been read. If pExpr is
** not an indexed expression, then return negative.
@@ -4049,7 +4051,7 @@ static SQLITE_NOINLINE int sqlite3IndexedExprLookup(
){
IndexedExpr *p;
Vdbe *v;
for(p=pParse->pIdxExpr; p; p=p->pIENext){
for(p=pParse->pIdxEpr; p; p=p->pIENext){
int iDataCur = p->iDataCur;
if( iDataCur<0 ) continue;
if( pParse->iSelfTab ){
@@ -4069,10 +4071,10 @@ static SQLITE_NOINLINE int sqlite3IndexedExprLookup(
sqlite3VdbeAddOp3(v, OP_Column, p->iIdxCur, p->iIdxCol, target);
VdbeComment((v, "%s expr-column %d", p->zIdxName, p->iIdxCol));
sqlite3VdbeGoto(v, 0);
p = pParse->pIdxExpr;
pParse->pIdxExpr = 0;
p = pParse->pIdxEpr;
pParse->pIdxEpr = 0;
sqlite3ExprCode(pParse, pExpr, target);
pParse->pIdxExpr = p;
pParse->pIdxEpr = p;
sqlite3VdbeJumpHere(v, addr+2);
}else{
sqlite3VdbeAddOp3(v, OP_Column, p->iIdxCur, p->iIdxCol, target);
@@ -4111,7 +4113,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
expr_code_doover:
if( pExpr==0 ){
op = TK_NULL;
}else if( pParse->pIdxExpr!=0
}else if( pParse->pIdxEpr!=0
&& !ExprHasProperty(pExpr, EP_Leaf)
&& (r1 = sqlite3IndexedExprLookup(pParse, pExpr, target))>=0
){
@@ -4128,15 +4130,16 @@ expr_code_doover:
assert( pExpr->iAgg>=0 && pExpr->iAgg<pAggInfo->nColumn );
pCol = &pAggInfo->aCol[pExpr->iAgg];
if( !pAggInfo->directMode ){
assert( pCol->iMem>0 );
return pCol->iMem;
return AggInfoColumnReg(pAggInfo, pExpr->iAgg);
}else if( pAggInfo->useSortingIdx ){
Table *pTab = pCol->pTab;
sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdxPTab,
pCol->iSorterColumn, target);
if( pCol->iColumn<0 ){
if( pTab==0 ){
/* No comment added */
}else if( pCol->iColumn<0 ){
VdbeComment((v,"%s.rowid",pTab->zName));
}else if( ALWAYS(pTab!=0) ){
}else{
VdbeComment((v,"%s.%s",
pTab->zName, pTab->aCol[pCol->iColumn].zCnName));
if( pTab->aCol[pCol->iColumn].affinity==SQLITE_AFF_REAL ){
@@ -4144,6 +4147,11 @@ expr_code_doover:
}
}
return target;
}else if( pExpr->y.pTab==0 ){
/* This case happens when the argument to an aggregate function
** is rewritten by aggregateConvertIndexedExprRefToColumn() */
sqlite3VdbeAddOp3(v, OP_Column, pExpr->iTable, pExpr->iColumn, target);
return target;
}
/* Otherwise, fall thru into the TK_COLUMN case */
/* no break */ deliberate_fall_through
@@ -4441,7 +4449,7 @@ expr_code_doover:
assert( !ExprHasProperty(pExpr, EP_IntValue) );
sqlite3ErrorMsg(pParse, "misuse of aggregate: %#T()", pExpr);
}else{
return pInfo->aFunc[pExpr->iAgg].iMem;
return AggInfoFuncReg(pInfo, pExpr->iAgg);
}
break;
}
@@ -4730,7 +4738,7 @@ expr_code_doover:
if( pAggInfo ){
assert( pExpr->iAgg>=0 && pExpr->iAgg<pAggInfo->nColumn );
if( !pAggInfo->directMode ){
inReg = pAggInfo->aCol[pExpr->iAgg].iMem;
inReg = AggInfoColumnReg(pAggInfo, pExpr->iAgg);
break;
}
if( pExpr->pAggInfo->useSortingIdx ){
@@ -6242,6 +6250,72 @@ static int addAggInfoFunc(sqlite3 *db, AggInfo *pInfo){
return i;
}
/*
** Search the AggInfo object for an aCol[] entry that has iTable and iColumn.
** Return the index in aCol[] of the entry that describes that column.
**
** If no prior entry is found, create a new one and return -1. The
** new column will have an idex of pAggInfo->nColumn-1.
*/
static void findOrCreateAggInfoColumn(
Parse *pParse, /* Parsing context */
AggInfo *pAggInfo, /* The AggInfo object to search and/or modify */
Expr *pExpr /* Expr describing the column to find or insert */
){
struct AggInfo_col *pCol;
int k;
assert( pAggInfo->iFirstReg==0 );
pCol = pAggInfo->aCol;
for(k=0; k<pAggInfo->nColumn; k++, pCol++){
if( pCol->iTable==pExpr->iTable
&& pCol->iColumn==pExpr->iColumn
&& pExpr->op!=TK_IF_NULL_ROW
){
goto fix_up_expr;
}
}
k = addAggInfoColumn(pParse->db, pAggInfo);
if( k<0 ){
/* OOM on resize */
assert( pParse->db->mallocFailed );
return;
}
pCol = &pAggInfo->aCol[k];
assert( ExprUseYTab(pExpr) );
pCol->pTab = pExpr->y.pTab;
pCol->iTable = pExpr->iTable;
pCol->iColumn = pExpr->iColumn;
pCol->iSorterColumn = -1;
pCol->pCExpr = pExpr;
if( pAggInfo->pGroupBy && pExpr->op!=TK_IF_NULL_ROW ){
int j, n;
ExprList *pGB = pAggInfo->pGroupBy;
struct ExprList_item *pTerm = pGB->a;
n = pGB->nExpr;
for(j=0; j<n; j++, pTerm++){
Expr *pE = pTerm->pExpr;
if( pE->op==TK_COLUMN
&& pE->iTable==pExpr->iTable
&& pE->iColumn==pExpr->iColumn
){
pCol->iSorterColumn = j;
break;
}
}
}
if( pCol->iSorterColumn<0 ){
pCol->iSorterColumn = pAggInfo->nSortingColumn++;
}
fix_up_expr:
ExprSetVVAProperty(pExpr, EP_NoReduce);
pExpr->pAggInfo = pAggInfo;
if( pExpr->op==TK_COLUMN ){
pExpr->op = TK_AGG_COLUMN;
}
pExpr->iAgg = (i16)k;
}
/*
** This is the xExprCallback for a tree walker. It is used to
** implement sqlite3ExprAnalyzeAggregates(). See sqlite3ExprAnalyzeAggregates
@@ -6255,7 +6329,36 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
AggInfo *pAggInfo = pNC->uNC.pAggInfo;
assert( pNC->ncFlags & NC_UAggInfo );
assert( pAggInfo->iFirstReg==0 );
switch( pExpr->op ){
default: {
IndexedExpr *pIEpr;
Expr tmp;
assert( pParse->iSelfTab==0 );
if( (pNC->ncFlags & NC_InAggFunc)==0 ) break;
if( pParse->pIdxEpr==0 ) break;
for(pIEpr=pParse->pIdxEpr; pIEpr; pIEpr=pIEpr->pIENext){
int iDataCur = pIEpr->iDataCur;
if( iDataCur<0 ) continue;
if( sqlite3ExprCompare(0, pExpr, pIEpr->pExpr, iDataCur)==0 ) break;
}
if( pIEpr==0 ) break;
if( NEVER(!ExprUseYTab(pExpr)) ) break;
/* If we reach this point, it means that expression pExpr can be
** translated into a reference to an index column as described by
** pIEpr.
*/
memset(&tmp, 0, sizeof(tmp));
tmp.op = TK_AGG_COLUMN;
tmp.iTable = pIEpr->iIdxCur;
tmp.iColumn = pIEpr->iIdxCol;
findOrCreateAggInfoColumn(pParse, pAggInfo, &tmp);
pAggInfo->aCol[tmp.iAgg].pCExpr = pExpr;
pExpr->pAggInfo = pAggInfo;
pExpr->iAgg = tmp.iAgg;
return WRC_Prune;
}
case TK_IF_NULL_ROW:
case TK_AGG_COLUMN:
case TK_COLUMN: {
@@ -6267,67 +6370,9 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
if( ALWAYS(pSrcList!=0) ){
SrcItem *pItem = pSrcList->a;
for(i=0; i<pSrcList->nSrc; i++, pItem++){
struct AggInfo_col *pCol;
assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) );
if( pExpr->iTable==pItem->iCursor ){
/* If we reach this point, it means that pExpr refers to a table
** that is in the FROM clause of the aggregate query.
**
** Make an entry for the column in pAggInfo->aCol[] if there
** is not an entry there already.
*/
int k;
pCol = pAggInfo->aCol;
for(k=0; k<pAggInfo->nColumn; k++, pCol++){
if( pCol->iTable==pExpr->iTable
&& pCol->iColumn==pExpr->iColumn
&& pExpr->op!=TK_IF_NULL_ROW
){
break;
}
}
if( (k>=pAggInfo->nColumn)
&& (k = addAggInfoColumn(pParse->db, pAggInfo))>=0
){
pCol = &pAggInfo->aCol[k];
assert( ExprUseYTab(pExpr) );
pCol->pTab = pExpr->y.pTab;
pCol->iTable = pExpr->iTable;
pCol->iColumn = pExpr->iColumn;
pCol->iMem = ++pParse->nMem;
pCol->iSorterColumn = -1;
pCol->pCExpr = pExpr;
if( pAggInfo->pGroupBy && pExpr->op!=TK_IF_NULL_ROW ){
int j, n;
ExprList *pGB = pAggInfo->pGroupBy;
struct ExprList_item *pTerm = pGB->a;
n = pGB->nExpr;
for(j=0; j<n; j++, pTerm++){
Expr *pE = pTerm->pExpr;
if( pE->op==TK_COLUMN
&& pE->iTable==pExpr->iTable
&& pE->iColumn==pExpr->iColumn
){
pCol->iSorterColumn = j;
break;
}
}
}
if( pCol->iSorterColumn<0 ){
pCol->iSorterColumn = pAggInfo->nSortingColumn++;
}
}
/* There is now an entry for pExpr in pAggInfo->aCol[] (either
** because it was there before or because we just created it).
** Convert the pExpr to be a TK_AGG_COLUMN referring to that
** pAggInfo->aCol[] entry.
*/
ExprSetVVAProperty(pExpr, EP_NoReduce);
pExpr->pAggInfo = pAggInfo;
if( pExpr->op==TK_COLUMN ){
pExpr->op = TK_AGG_COLUMN;
}
pExpr->iAgg = (i16)k;
findOrCreateAggInfoColumn(pParse, pAggInfo, pExpr);
break;
} /* endif pExpr->iTable==pItem->iCursor */
} /* end loop over pSrcList */
@@ -6357,7 +6402,6 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
pItem = &pAggInfo->aFunc[i];
pItem->pFExpr = pExpr;
pItem->iMem = ++pParse->nMem;
assert( ExprUseUToken(pExpr) );
pItem->pFunc = sqlite3FindFunction(pParse->db,
pExpr->u.zToken,
+3 -5
View File
@@ -2106,17 +2106,15 @@ static void logFunc(
}
ans = log(x)/b;
}else{
ans = log(x);
switch( SQLITE_PTR_TO_INT(sqlite3_user_data(context)) ){
case 1:
/* Convert from natural logarithm to log base 10 */
ans /= M_LN10;
ans = log10(x);
break;
case 2:
/* Convert from natural logarithm to log base 2 */
ans /= M_LN2;
ans = log2(x);
break;
default:
ans = log(x);
break;
}
}
+1 -1
View File
@@ -510,7 +510,7 @@ static const sqlite3_api_routines sqlite3Apis = {
#endif
sqlite3_db_name,
/* Version 3.40.0 and later */
sqlite3_value_type
sqlite3_value_encoding
};
/* True if x is the directory separator character
+6 -1
View File
@@ -793,9 +793,14 @@ char *sqlite3DbStrNDup(sqlite3 *db, const char *z, u64 n){
*/
char *sqlite3DbSpanDup(sqlite3 *db, const char *zStart, const char *zEnd){
int n;
#ifdef SQLITE_DEBUG
/* Because of the way the parser works, the span is guaranteed to contain
** at least one non-space character */
for(n=0; sqlite3Isspace(zStart[n]); n++){ assert( &zStart[n]<zEnd ); }
#endif
while( sqlite3Isspace(zStart[0]) ) zStart++;
n = (int)(zEnd - zStart);
while( ALWAYS(n>0) && sqlite3Isspace(zStart[n-1]) ) n--;
while( sqlite3Isspace(zStart[n-1]) ) n--;
return sqlite3DbStrNDup(db, zStart, n);
}
+7
View File
@@ -857,6 +857,13 @@ end_deserialize:
return rc;
}
/*
** Return true if the VFS is the memvfs.
*/
int sqlite3IsMemdb(const sqlite3_vfs *pVfs){
return pVfs==&memdb_vfs;
}
/*
** This routine is called when the extension is loaded.
** Register the new VFS.
+3 -2
View File
@@ -4725,9 +4725,10 @@ static int winMakeEndInDirSep(int nBuf, char *zBuf){
}
/*
** If sqlite3_temp_directory is not, take the mutex and return true.
** If sqlite3_temp_directory is defined, take the mutex and return true.
**
** If sqlite3_temp_directory is NULL, omit the mutex and return false.
** If sqlite3_temp_directory is NULL (undefined), omit the mutex and
** return false.
*/
static int winTempDirDefined(void){
sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
+5 -1
View File
@@ -7009,7 +7009,11 @@ int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
*/
const char *sqlite3PagerFilename(const Pager *pPager, int nullIfMemDb){
static const char zFake[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
return (nullIfMemDb && pPager->memDb) ? &zFake[4] : pPager->zFilename;
if( nullIfMemDb && (pPager->memDb || sqlite3IsMemdb(pPager->pVfs)) ){
return &zFake[4];
}else{
return pPager->zFilename;
}
}
/*
+5
View File
@@ -2170,6 +2170,11 @@ void sqlite3Pragma(
aOp[1].p2 = iCookie;
aOp[1].p3 = sqlite3Atoi(zRight);
aOp[1].p5 = 1;
if( iCookie==BTREE_SCHEMA_VERSION && (db->flags & SQLITE_Defensive)!=0 ){
/* Do not allow the use of PRAGMA schema_version=VALUE in defensive
** mode. Change the OP_SetCookie opcode into a no-op. */
aOp[1].opcode = OP_Noop;
}
}else{
/* Read the specified cookie value */
static const VdbeOpList readCookie[] = {
+1 -1
View File
@@ -520,8 +520,8 @@ static void schemaIsValid(Parse *pParse){
sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&cookie);
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
if( cookie!=db->aDb[iDb].pSchema->schema_cookie ){
if( DbHasProperty(db, iDb, DB_SchemaLoaded) ) pParse->rc = SQLITE_SCHEMA;
sqlite3ResetOneSchema(db, iDb);
pParse->rc = SQLITE_SCHEMA;
}
/* Close the transaction, if one was opened. */
+4 -4
View File
@@ -31,10 +31,10 @@ static SQLITE_WSD struct sqlite3PrngType {
/* The RFC-7539 ChaCha20 block function
*/
#define ROTL(a,b) (((a) << (b)) | ((a) >> (32 - (b))))
#define QR(a, b, c, d) ( \
a += b, d ^= a, d = ROTL(d,16), \
c += d, b ^= c, b = ROTL(b,12), \
a += b, d ^= a, d = ROTL(d, 8), \
#define QR(a, b, c, d) ( \
a += b, d ^= a, d = ROTL(d,16), \
c += d, b ^= c, b = ROTL(b,12), \
a += b, d ^= a, d = ROTL(d, 8), \
c += d, b ^= c, b = ROTL(b, 7))
static void chacha_block(u32 *out, const u32 *in){
int i;
+324 -129
View File
@@ -2091,7 +2091,7 @@ void sqlite3GenerateColumnNames(
if( pParse->colNamesSet ) return;
/* Column names are determined by the left-most term of a compound select */
while( pSelect->pPrior ) pSelect = pSelect->pPrior;
SELECTTRACE(1,pParse,pSelect,("generating column names\n"));
TREETRACE(0x80,pParse,pSelect,("generating column names\n"));
pTabList = pSelect->pSrc;
pEList = pSelect->pEList;
assert( v!=0 );
@@ -2877,7 +2877,7 @@ static int multiSelect(
pPrior->iLimit = p->iLimit;
pPrior->iOffset = p->iOffset;
pPrior->pLimit = p->pLimit;
SELECTTRACE(1, pParse, p, ("multiSelect UNION ALL left...\n"));
TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL left...\n"));
rc = sqlite3Select(pParse, pPrior, &dest);
pPrior->pLimit = 0;
if( rc ){
@@ -2895,7 +2895,7 @@ static int multiSelect(
}
}
ExplainQueryPlan((pParse, 1, "UNION ALL"));
SELECTTRACE(1, pParse, p, ("multiSelect UNION ALL right...\n"));
TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL right...\n"));
rc = sqlite3Select(pParse, p, &dest);
testcase( rc!=SQLITE_OK );
pDelete = p->pPrior;
@@ -2948,7 +2948,7 @@ static int multiSelect(
*/
assert( !pPrior->pOrderBy );
sqlite3SelectDestInit(&uniondest, priorOp, unionTab);
SELECTTRACE(1, pParse, p, ("multiSelect EXCEPT/UNION left...\n"));
TREETRACE(0x200, pParse, p, ("multiSelect EXCEPT/UNION left...\n"));
rc = sqlite3Select(pParse, pPrior, &uniondest);
if( rc ){
goto multi_select_end;
@@ -2968,7 +2968,7 @@ static int multiSelect(
uniondest.eDest = op;
ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE",
sqlite3SelectOpName(p->op)));
SELECTTRACE(1, pParse, p, ("multiSelect EXCEPT/UNION right...\n"));
TREETRACE(0x200, pParse, p, ("multiSelect EXCEPT/UNION right...\n"));
rc = sqlite3Select(pParse, p, &uniondest);
testcase( rc!=SQLITE_OK );
assert( p->pOrderBy==0 );
@@ -3029,7 +3029,7 @@ static int multiSelect(
/* Code the SELECTs to our left into temporary table "tab1".
*/
sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1);
SELECTTRACE(1, pParse, p, ("multiSelect INTERSECT left...\n"));
TREETRACE(0x400, pParse, p, ("multiSelect INTERSECT left...\n"));
rc = sqlite3Select(pParse, pPrior, &intersectdest);
if( rc ){
goto multi_select_end;
@@ -3046,7 +3046,7 @@ static int multiSelect(
intersectdest.iSDParm = tab2;
ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE",
sqlite3SelectOpName(p->op)));
SELECTTRACE(1, pParse, p, ("multiSelect INTERSECT right...\n"));
TREETRACE(0x400, pParse, p, ("multiSelect INTERSECT right...\n"));
rc = sqlite3Select(pParse, p, &intersectdest);
testcase( rc!=SQLITE_OK );
pDelete = p->pPrior;
@@ -3693,8 +3693,8 @@ static int multiSelectOrderBy(
*/
sqlite3VdbeResolveLabel(v, labelEnd);
/* Reassemble the compound query so that it will be freed correctly
** by the calling function */
/* Make arrangements to free the 2nd and subsequent arms of the compound
** after the parse has finished */
if( pSplit->pPrior ){
sqlite3ParserAddCleanup(pParse,
(void(*)(sqlite3*,void*))sqlite3SelectDelete, pSplit->pPrior);
@@ -4050,6 +4050,34 @@ static ExprList *findLeftmostExprlist(Select *pSel){
return pSel->pEList;
}
/*
** Return true if any of the result-set columns in the compound query
** have incompatible affinities on one or more arms of the compound.
*/
static int compoundHasDifferentAffinities(Select *p){
int ii;
ExprList *pList;
assert( p!=0 );
assert( p->pEList!=0 );
assert( p->pPrior!=0 );
pList = p->pEList;
for(ii=0; ii<pList->nExpr; ii++){
char aff;
Select *pSub1;
assert( pList->a[ii].pExpr!=0 );
aff = sqlite3ExprAffinity(pList->a[ii].pExpr);
for(pSub1=p->pPrior; pSub1; pSub1=pSub1->pPrior){
assert( pSub1->pEList!=0 );
assert( pSub1->pEList->nExpr>ii );
assert( pSub1->pEList->a[ii].pExpr!=0 );
if( sqlite3ExprAffinity(pSub1->pEList->a[ii].pExpr)!=aff ){
return 1;
}
}
}
return 0;
}
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
/*
** This routine attempts to flatten subqueries as a performance optimization.
@@ -4153,7 +4181,8 @@ static ExprList *findLeftmostExprlist(Select *pSel){
** query or there are no RIGHT or FULL JOINs in any arm
** of the subquery. (This is a duplicate of condition (27b).)
** (17h) The corresponding result set expressions in all arms of the
** compound must have the same affinity.
** compound must have the same affinity. (See restriction (9)
** on the push-down optimization.)
**
** The parent and sub-query may contain WHERE clauses. Subject to
** rules (11), (13) and (14), they may also contain ORDER BY,
@@ -4372,19 +4401,7 @@ static int flattenSubquery(
if( (p->selFlags & SF_Recursive) ) return 0;
/* Restriction (17h) */
for(ii=0; ii<pSub->pEList->nExpr; ii++){
char aff;
assert( pSub->pEList->a[ii].pExpr!=0 );
aff = sqlite3ExprAffinity(pSub->pEList->a[ii].pExpr);
for(pSub1=pSub->pPrior; pSub1; pSub1=pSub1->pPrior){
assert( pSub1->pEList!=0 );
assert( pSub1->pEList->nExpr>ii );
assert( pSub1->pEList->a[ii].pExpr!=0 );
if( sqlite3ExprAffinity(pSub1->pEList->a[ii].pExpr)!=aff ){
return 0;
}
}
}
if( compoundHasDifferentAffinities(pSub) ) return 0;
if( pSrc->nSrc>1 ){
if( pParse->nSelect>500 ) return 0;
@@ -4395,7 +4412,7 @@ static int flattenSubquery(
}
/***** If we reach this point, flattening is permitted. *****/
SELECTTRACE(1,pParse,p,("flatten %u.%p from term %d\n",
TREETRACE(0x4,pParse,p,("flatten %u.%p from term %d\n",
pSub->selId, pSub, iFrom));
/* Authorize the subquery */
@@ -4474,7 +4491,7 @@ static int flattenSubquery(
if( pPrior ) pPrior->pNext = pNew;
pNew->pNext = p;
p->pPrior = pNew;
SELECTTRACE(2,pParse,p,("compound-subquery flattener"
TREETRACE(0x4,pParse,p,("compound-subquery flattener"
" creates %u as peer\n",pNew->selId));
}
assert( pSubitem->pSelect==0 );
@@ -4654,8 +4671,8 @@ static int flattenSubquery(
sqlite3SelectDelete(db, pSub1);
#if TREETRACE_ENABLED
if( sqlite3TreeTrace & 0x100 ){
SELECTTRACE(0x100,pParse,p,("After flattening:\n"));
if( sqlite3TreeTrace & 0x4 ){
TREETRACE(0x4,pParse,p,("After flattening:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
@@ -5029,12 +5046,14 @@ static int pushDownWindowCheck(Parse *pParse, Select *pSubq, Expr *pExpr){
** be materialized. (This restriction is implemented in the calling
** routine.)
**
** (8) The subquery may not be a compound that uses UNION, INTERSECT,
** or EXCEPT. (We could, perhaps, relax this restriction to allow
** this case if none of the comparisons operators between left and
** right arms of the compound use a collation other than BINARY.
** But it is a lot of work to check that case for an obscure and
** minor optimization, so we omit it for now.)
** (8) If the subquery is a compound that uses UNION, INTERSECT,
** or EXCEPT, then all of the result set columns for all arms of
** the compound must use the BINARY collating sequence.
**
** (9) If the subquery is a compound, then all arms of the compound must
** have the same affinity. (This is the same as restriction (17h)
** for query flattening.)
**
**
** Return 0 if no changes are made and non-zero if one or more WHERE clause
** terms are duplicated into the subquery.
@@ -5051,20 +5070,44 @@ static int pushDownWhereTerms(
if( pSubq->selFlags & (SF_Recursive|SF_MultiPart) ) return 0;
if( pSrc->fg.jointype & (JT_LTORJ|JT_RIGHT) ) return 0;
#ifndef SQLITE_OMIT_WINDOWFUNC
if( pSubq->pPrior ){
Select *pSel;
int notUnionAll = 0;
for(pSel=pSubq; pSel; pSel=pSel->pPrior){
u8 op = pSel->op;
assert( op==TK_ALL || op==TK_SELECT
|| op==TK_UNION || op==TK_INTERSECT || op==TK_EXCEPT );
if( op!=TK_ALL && op!=TK_SELECT ) return 0; /* restriction (8) */
if( op!=TK_ALL && op!=TK_SELECT ){
notUnionAll = 1;
}
#ifndef SQLITE_OMIT_WINDOWFUNC
if( pSel->pWin ) return 0; /* restriction (6b) */
#endif
}
if( compoundHasDifferentAffinities(pSubq) ){
return 0; /* restriction (9) */
}
if( notUnionAll ){
/* If any of the compound arms are connected using UNION, INTERSECT,
** or EXCEPT, then we must ensure that none of the columns use a
** non-BINARY collating sequence. */
for(pSel=pSubq; pSel; pSel=pSel->pPrior){
int ii;
const ExprList *pList = pSel->pEList;
assert( pList!=0 );
for(ii=0; ii<pList->nExpr; ii++){
CollSeq *pColl = sqlite3ExprCollSeq(pParse, pList->a[ii].pExpr);
if( !sqlite3IsBinary(pColl) ){
return 0; /* Restriction (8) */
}
}
}
}
}else{
#ifndef SQLITE_OMIT_WINDOWFUNC
if( pSubq->pWin && pSubq->pWin->pPartition==0 ) return 0;
}
#endif
}
#ifdef SQLITE_DEBUG
/* Only the first term of a compound can have a WITH clause. But make
@@ -6068,8 +6111,8 @@ static int selectExpander(Walker *pWalker, Select *p){
}
}
#if TREETRACE_ENABLED
if( sqlite3TreeTrace & 0x100 ){
SELECTTRACE(0x100,pParse,p,("After result-set wildcard expansion:\n"));
if( sqlite3TreeTrace & 0x8 ){
TREETRACE(0x8,pParse,p,("After result-set wildcard expansion:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
@@ -6204,6 +6247,172 @@ void sqlite3SelectPrep(
sqlite3SelectAddTypeInfo(pParse, p);
}
#if TREETRACE_ENABLED
/*
** Display all information about an AggInfo object
*/
static void printAggInfo(AggInfo *pAggInfo){
int ii;
for(ii=0; ii<pAggInfo->nColumn; ii++){
struct AggInfo_col *pCol = &pAggInfo->aCol[ii];
sqlite3DebugPrintf(
"agg-column[%d] pTab=%s iTable=%d iColumn=%d iMem=%d"
" iSorterColumn=%d %s\n",
ii, pCol->pTab ? pCol->pTab->zName : "NULL",
pCol->iTable, pCol->iColumn, pAggInfo->iFirstReg+ii,
pCol->iSorterColumn,
ii>=pAggInfo->nAccumulator ? "" : " Accumulator");
sqlite3TreeViewExpr(0, pAggInfo->aCol[ii].pCExpr, 0);
}
for(ii=0; ii<pAggInfo->nFunc; ii++){
sqlite3DebugPrintf("agg-func[%d]: iMem=%d\n",
ii, AggInfoFuncReg(pAggInfo,ii));
sqlite3TreeViewExpr(0, pAggInfo->aFunc[ii].pFExpr, 0);
}
}
#endif /* TREETRACE_ENABLED */
/*
** Analyze the arguments to aggregate functions. Create new pAggInfo->aCol[]
** entries for columns that are arguments to aggregate functions but which
** are not otherwise used.
**
** The aCol[] entries in AggInfo prior to nAccumulator are columns that
** are referenced outside of aggregate functions. These might be columns
** that are part of the GROUP by clause, for example. Other database engines
** would throw an error if there is a column reference that is not in the
** GROUP BY clause and that is not part of an aggregate function argument.
** But SQLite allows this.
**
** The aCol[] entries beginning with the aCol[nAccumulator] and following
** are column references that are used exclusively as arguments to
** aggregate functions. This routine is responsible for computing
** (or recomputing) those aCol[] entries.
*/
static void analyzeAggFuncArgs(
Parse *pParse,
AggInfo *pAggInfo,
NameContext *pNC
){
int i;
assert( pAggInfo!=0 );
assert( pAggInfo->iFirstReg==0 );
pNC->ncFlags |= NC_InAggFunc;
for(i=0; i<pAggInfo->nFunc; i++){
Expr *pExpr = pAggInfo->aFunc[i].pFExpr;
assert( ExprUseXList(pExpr) );
sqlite3ExprAnalyzeAggList(pNC, pExpr->x.pList);
#ifndef SQLITE_OMIT_WINDOWFUNC
assert( !IsWindowFunc(pExpr) );
if( ExprHasProperty(pExpr, EP_WinFunc) ){
sqlite3ExprAnalyzeAggregates(pNC, pExpr->y.pWin->pFilter);
}
#endif
}
pNC->ncFlags &= ~NC_InAggFunc;
}
/*
** An index on expressions is being used in the inner loop of an
** aggregate query with a GROUP BY clause. This routine attempts
** to adjust the AggInfo object to take advantage of index and to
** perhaps use the index as a covering index.
**
*/
static void optimizeAggregateUseOfIndexedExpr(
Parse *pParse, /* Parsing context */
Select *pSelect, /* The SELECT statement being processed */
AggInfo *pAggInfo, /* The aggregate info */
NameContext *pNC /* Name context used to resolve agg-func args */
){
assert( pAggInfo->iFirstReg==0 );
pAggInfo->nColumn = pAggInfo->nAccumulator;
if( ALWAYS(pAggInfo->nSortingColumn>0) ){
if( pAggInfo->nColumn==0 ){
pAggInfo->nSortingColumn = 0;
}else{
pAggInfo->nSortingColumn =
pAggInfo->aCol[pAggInfo->nColumn-1].iSorterColumn+1;
}
}
analyzeAggFuncArgs(pParse, pAggInfo, pNC);
#if TREETRACE_ENABLED
if( sqlite3TreeTrace & 0x20 ){
IndexedExpr *pIEpr;
TREETRACE(0x20, pParse, pSelect,
("AggInfo (possibly) adjusted for Indexed Exprs\n"));
sqlite3TreeViewSelect(0, pSelect, 0);
for(pIEpr=pParse->pIdxEpr; pIEpr; pIEpr=pIEpr->pIENext){
printf("data-cursor=%d index={%d,%d}\n",
pIEpr->iDataCur, pIEpr->iIdxCur, pIEpr->iIdxCol);
sqlite3TreeViewExpr(0, pIEpr->pExpr, 0);
}
printAggInfo(pAggInfo);
}
#else
(void)pSelect; /* Suppress unused-parameter warnings */
#endif
}
/*
** Walker callback for aggregateConvertIndexedExprRefToColumn().
*/
static int aggregateIdxEprRefToColCallback(Walker *pWalker, Expr *pExpr){
AggInfo *pAggInfo;
struct AggInfo_col *pCol;
if( pExpr->pAggInfo==0 ) return WRC_Continue;
if( pExpr->op==TK_AGG_COLUMN ) return WRC_Continue;
if( pExpr->op==TK_AGG_FUNCTION ) return WRC_Continue;
if( pExpr->op==TK_IF_NULL_ROW ) return WRC_Continue;
pAggInfo = pExpr->pAggInfo;
assert( pExpr->iAgg>=0 && pExpr->iAgg<pAggInfo->nColumn );
pCol = &pAggInfo->aCol[pExpr->iAgg];
pExpr->op = TK_AGG_COLUMN;
pExpr->iTable = pCol->iTable;
pExpr->iColumn = pCol->iColumn;
return WRC_Prune;
}
/*
** Convert every pAggInfo->aFunc[].pExpr such that any node within
** those expressions that has pAppInfo set is changed into a TK_AGG_COLUMN
** opcode.
*/
static void aggregateConvertIndexedExprRefToColumn(AggInfo *pAggInfo){
int i;
Walker w;
memset(&w, 0, sizeof(w));
w.xExprCallback = aggregateIdxEprRefToColCallback;
for(i=0; i<pAggInfo->nFunc; i++){
sqlite3WalkExpr(&w, pAggInfo->aFunc[i].pFExpr);
}
}
/*
** Allocate a block of registers so that there is one register for each
** pAggInfo->aCol[] and pAggInfo->aFunc[] entry in pAggInfo. The first
** register in this block is stored in pAggInfo->iFirstReg.
**
** This routine may only be called once for each AggInfo object. Prior
** to calling this routine:
**
** * The aCol[] and aFunc[] arrays may be modified
** * The AggInfoColumnReg() and AggInfoFuncReg() macros may not be used
**
** After clling this routine:
**
** * The aCol[] and aFunc[] arrays are fixed
** * The AggInfoColumnReg() and AggInfoFuncReg() macros may be used
**
*/
static void assignAggregateRegisters(Parse *pParse, AggInfo *pAggInfo){
assert( pAggInfo!=0 );
assert( pAggInfo->iFirstReg==0 );
pAggInfo->iFirstReg = pParse->nMem + 1;
pParse->nMem += pAggInfo->nColumn + pAggInfo->nFunc;
}
/*
** Reset the aggregate accumulator.
**
@@ -6217,24 +6426,13 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
int i;
struct AggInfo_func *pFunc;
int nReg = pAggInfo->nFunc + pAggInfo->nColumn;
assert( pAggInfo->iFirstReg>0 );
assert( pParse->db->pParse==pParse );
assert( pParse->db->mallocFailed==0 || pParse->nErr!=0 );
if( nReg==0 ) return;
if( pParse->nErr ) return;
#ifdef SQLITE_DEBUG
/* Verify that all AggInfo registers are within the range specified by
** AggInfo.mnReg..AggInfo.mxReg */
assert( nReg==pAggInfo->mxReg-pAggInfo->mnReg+1 );
for(i=0; i<pAggInfo->nColumn; i++){
assert( pAggInfo->aCol[i].iMem>=pAggInfo->mnReg
&& pAggInfo->aCol[i].iMem<=pAggInfo->mxReg );
}
for(i=0; i<pAggInfo->nFunc; i++){
assert( pAggInfo->aFunc[i].iMem>=pAggInfo->mnReg
&& pAggInfo->aFunc[i].iMem<=pAggInfo->mxReg );
}
#endif
sqlite3VdbeAddOp3(v, OP_Null, 0, pAggInfo->mnReg, pAggInfo->mxReg);
sqlite3VdbeAddOp3(v, OP_Null, 0, pAggInfo->iFirstReg,
pAggInfo->iFirstReg+nReg-1);
for(pFunc=pAggInfo->aFunc, i=0; i<pAggInfo->nFunc; i++, pFunc++){
if( pFunc->iDistinct>=0 ){
Expr *pE = pFunc->pFExpr;
@@ -6266,15 +6464,16 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){
ExprList *pList;
assert( ExprUseXList(pF->pFExpr) );
pList = pF->pFExpr->x.pList;
sqlite3VdbeAddOp2(v, OP_AggFinal, pF->iMem, pList ? pList->nExpr : 0);
sqlite3VdbeAddOp2(v, OP_AggFinal, AggInfoFuncReg(pAggInfo,i),
pList ? pList->nExpr : 0);
sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
}
}
/*
** Update the accumulator memory cells for an aggregate based on
** the current cursor position.
** Generate code that will update the accumulator memory cells for an
** aggregate based on the current cursor position.
**
** If regAcc is non-zero and there are no min() or max() aggregates
** in pAggInfo, then only populate the pAggInfo->nAccumulator accumulator
@@ -6294,6 +6493,8 @@ static void updateAccumulator(
struct AggInfo_func *pF;
struct AggInfo_col *pC;
assert( pAggInfo->iFirstReg>0 );
if( pParse->nErr ) return;
pAggInfo->directMode = 1;
for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){
int nArg;
@@ -6354,7 +6555,7 @@ static void updateAccumulator(
if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem;
sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0, (char *)pColl, P4_COLLSEQ);
}
sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, pF->iMem);
sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, (u8)nArg);
sqlite3ReleaseTempRange(pParse, regAgg, nArg);
@@ -6369,7 +6570,7 @@ static void updateAccumulator(
addrHitTest = sqlite3VdbeAddOp1(v, OP_If, regHit); VdbeCoverage(v);
}
for(i=0, pC=pAggInfo->aCol; i<pAggInfo->nAccumulator; i++, pC++){
sqlite3ExprCode(pParse, pC->pCExpr, pC->iMem);
sqlite3ExprCode(pParse, pC->pCExpr, AggInfoColumnReg(pAggInfo,i));
}
pAggInfo->directMode = 0;
@@ -6465,7 +6666,7 @@ static void havingToWhere(Parse *pParse, Select *p){
sqlite3WalkExpr(&sWalker, p->pHaving);
#if TREETRACE_ENABLED
if( sWalker.eCode && (sqlite3TreeTrace & 0x100)!=0 ){
SELECTTRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n"));
TREETRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
@@ -6597,8 +6798,8 @@ static int countOfViewOptimization(Parse *pParse, Select *p){
p->selFlags &= ~SF_Aggregate;
#if TREETRACE_ENABLED
if( sqlite3TreeTrace & 0x400 ){
SELECTTRACE(0x400,pParse,p,("After count-of-view optimization:\n"));
if( sqlite3TreeTrace & 0x200 ){
TREETRACE(0x200,pParse,p,("After count-of-view optimization:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
@@ -6674,8 +6875,8 @@ int sqlite3Select(
assert( db->mallocFailed==0 );
if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1;
#if TREETRACE_ENABLED
SELECTTRACE(1,pParse,p, ("begin processing:\n", pParse->addrExplain));
if( sqlite3TreeTrace & 0x10100 ){
TREETRACE(0x1,pParse,p, ("begin processing:\n", pParse->addrExplain));
if( sqlite3TreeTrace & 0x10000 ){
if( (sqlite3TreeTrace & 0x10001)==0x10000 ){
sqlite3TreeViewLine(0, "In sqlite3Select() at %s:%d",
__FILE__, __LINE__);
@@ -6695,8 +6896,8 @@ int sqlite3Select(
/* All of these destinations are also able to ignore the ORDER BY clause */
if( p->pOrderBy ){
#if TREETRACE_ENABLED
SELECTTRACE(1,pParse,p, ("dropping superfluous ORDER BY:\n"));
if( sqlite3TreeTrace & 0x100 ){
TREETRACE(0x800,pParse,p, ("dropping superfluous ORDER BY:\n"));
if( sqlite3TreeTrace & 0x800 ){
sqlite3TreeViewExprList(0, p->pOrderBy, 0, "ORDERBY");
}
#endif
@@ -6716,8 +6917,8 @@ int sqlite3Select(
assert( db->mallocFailed==0 );
assert( p->pEList!=0 );
#if TREETRACE_ENABLED
if( sqlite3TreeTrace & 0x104 ){
SELECTTRACE(0x104,pParse,p, ("after name resolution:\n"));
if( sqlite3TreeTrace & 0x10 ){
TREETRACE(0x10,pParse,p, ("after name resolution:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
@@ -6758,8 +6959,8 @@ int sqlite3Select(
goto select_end;
}
#if TREETRACE_ENABLED
if( p->pWin && (sqlite3TreeTrace & 0x108)!=0 ){
SELECTTRACE(0x104,pParse,p, ("after window rewrite:\n"));
if( p->pWin && (sqlite3TreeTrace & 0x40)!=0 ){
TREETRACE(0x40,pParse,p, ("after window rewrite:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
@@ -6790,7 +6991,7 @@ int sqlite3Select(
&& sqlite3ExprImpliesNonNullRow(p->pWhere, pItem->iCursor)
&& OptimizationEnabled(db, SQLITE_SimplifyJoin)
){
SELECTTRACE(0x100,pParse,p,
TREETRACE(0x1000,pParse,p,
("LEFT-JOIN simplifies to JOIN on term %d\n",i));
pItem->fg.jointype &= ~(JT_LEFT|JT_OUTER);
assert( pItem->iCursor>=0 );
@@ -6846,7 +7047,7 @@ int sqlite3Select(
&& (p->selFlags & SF_OrderByReqd)==0 /* Condition (3) and (4) */
&& OptimizationEnabled(db, SQLITE_OmitOrderBy)
){
SELECTTRACE(0x100,pParse,p,
TREETRACE(0x800,pParse,p,
("omit superfluous ORDER BY on %r FROM-clause subquery\n",i+1));
sqlite3ParserAddCleanup(pParse,
(void(*)(sqlite3*,void*))sqlite3ExprListDelete,
@@ -6901,8 +7102,8 @@ int sqlite3Select(
if( p->pPrior ){
rc = multiSelect(pParse, p, pDest);
#if TREETRACE_ENABLED
SELECTTRACE(0x1,pParse,p,("end compound-select processing\n"));
if( (sqlite3TreeTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
TREETRACE(0x400,pParse,p,("end compound-select processing\n"));
if( (sqlite3TreeTrace & 0x400)!=0 && ExplainQueryPlanParent(pParse)==0 ){
sqlite3TreeViewSelect(0, p, 0);
}
#endif
@@ -6922,13 +7123,13 @@ int sqlite3Select(
&& propagateConstants(pParse, p)
){
#if TREETRACE_ENABLED
if( sqlite3TreeTrace & 0x100 ){
SELECTTRACE(0x100,pParse,p,("After constant propagation:\n"));
if( sqlite3TreeTrace & 0x2000 ){
TREETRACE(0x2000,pParse,p,("After constant propagation:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
}else{
SELECTTRACE(0x100,pParse,p,("Constant propagation not helpful\n"));
TREETRACE(0x2000,pParse,p,("Constant propagation not helpful\n"));
}
#ifdef SQLITE_COUNTOFVIEW_OPTIMIZATION
@@ -7001,15 +7202,15 @@ int sqlite3Select(
&& pushDownWhereTerms(pParse, pSub, p->pWhere, pItem)
){
#if TREETRACE_ENABLED
if( sqlite3TreeTrace & 0x100 ){
SELECTTRACE(0x100,pParse,p,
if( sqlite3TreeTrace & 0x4000 ){
TREETRACE(0x4000,pParse,p,
("After WHERE-clause push-down into subquery %d:\n", pSub->selId));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
assert( pItem->pSelect && (pItem->pSelect->selFlags & SF_PushDown)!=0 );
}else{
SELECTTRACE(0x100,pParse,p,("Push-down not possible\n"));
TREETRACE(0x4000,pParse,p,("Push-down not possible\n"));
}
zSavedAuthContext = pParse->zAuthContext;
@@ -7124,8 +7325,8 @@ int sqlite3Select(
sDistinct.isTnct = (p->selFlags & SF_Distinct)!=0;
#if TREETRACE_ENABLED
if( sqlite3TreeTrace & 0x400 ){
SELECTTRACE(0x400,pParse,p,("After all FROM-clause analysis:\n"));
if( sqlite3TreeTrace & 0x8000 ){
TREETRACE(0x8000,pParse,p,("After all FROM-clause analysis:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
@@ -7161,8 +7362,8 @@ int sqlite3Select(
sDistinct.isTnct = 2;
#if TREETRACE_ENABLED
if( sqlite3TreeTrace & 0x400 ){
SELECTTRACE(0x400,pParse,p,("Transform DISTINCT into GROUP BY:\n"));
if( sqlite3TreeTrace & 0x20000 ){
TREETRACE(0x20000,pParse,p,("Transform DISTINCT into GROUP BY:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
@@ -7248,7 +7449,7 @@ int sqlite3Select(
/* Begin the database scan. */
SELECTTRACE(1,pParse,p,("WhereBegin\n"));
TREETRACE(0x2,pParse,p,("WhereBegin\n"));
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, sSort.pOrderBy,
p->pEList, p, wctrlFlags, p->nSelectRow);
if( pWInfo==0 ) goto select_end;
@@ -7265,7 +7466,7 @@ int sqlite3Select(
sSort.pOrderBy = 0;
}
}
SELECTTRACE(1,pParse,p,("WhereBegin returns\n"));
TREETRACE(0x2,pParse,p,("WhereBegin returns\n"));
/* If sorting index that was created by a prior OP_OpenEphemeral
** instruction ended up not being needed, then change the OP_OpenEphemeral
@@ -7304,7 +7505,7 @@ int sqlite3Select(
/* End the database scan loop.
*/
SELECTTRACE(1,pParse,p,("WhereEnd\n"));
TREETRACE(0x2,pParse,p,("WhereEnd\n"));
sqlite3WhereEnd(pWInfo);
}
}else{
@@ -7390,7 +7591,6 @@ int sqlite3Select(
sNC.pSrcList = pTabList;
sNC.uNC.pAggInfo = pAggInfo;
VVA_ONLY( sNC.ncFlags = NC_UAggInfo; )
pAggInfo->mnReg = pParse->nMem+1;
pAggInfo->nSortingColumn = pGroupBy ? pGroupBy->nExpr : 0;
pAggInfo->pGroupBy = pGroupBy;
sqlite3ExprAnalyzeAggList(&sNC, pEList);
@@ -7411,45 +7611,17 @@ int sqlite3Select(
}else{
minMaxFlag = WHERE_ORDERBY_NORMAL;
}
for(i=0; i<pAggInfo->nFunc; i++){
Expr *pExpr = pAggInfo->aFunc[i].pFExpr;
assert( ExprUseXList(pExpr) );
sNC.ncFlags |= NC_InAggFunc;
sqlite3ExprAnalyzeAggList(&sNC, pExpr->x.pList);
#ifndef SQLITE_OMIT_WINDOWFUNC
assert( !IsWindowFunc(pExpr) );
if( ExprHasProperty(pExpr, EP_WinFunc) ){
sqlite3ExprAnalyzeAggregates(&sNC, pExpr->y.pWin->pFilter);
}
#endif
sNC.ncFlags &= ~NC_InAggFunc;
}
pAggInfo->mxReg = pParse->nMem;
analyzeAggFuncArgs(pParse, pAggInfo, &sNC);
if( db->mallocFailed ) goto select_end;
#if TREETRACE_ENABLED
if( sqlite3TreeTrace & 0x400 ){
int ii;
SELECTTRACE(0x400,pParse,p,("After aggregate analysis %p:\n", pAggInfo));
if( sqlite3TreeTrace & 0x20 ){
TREETRACE(0x20,pParse,p,("After aggregate analysis %p:\n", pAggInfo));
sqlite3TreeViewSelect(0, p, 0);
if( minMaxFlag ){
sqlite3DebugPrintf("MIN/MAX Optimization (0x%02x) adds:\n", minMaxFlag);
sqlite3TreeViewExprList(0, pMinMaxOrderBy, 0, "ORDERBY");
}
for(ii=0; ii<pAggInfo->nColumn; ii++){
struct AggInfo_col *pCol = &pAggInfo->aCol[ii];
sqlite3DebugPrintf(
"agg-column[%d] pTab=%s iTable=%d iColumn=%d iMem=%d"
" iSorterColumn=%d\n",
ii, pCol->pTab ? pCol->pTab->zName : "NULL",
pCol->iTable, pCol->iColumn, pCol->iMem,
pCol->iSorterColumn);
sqlite3TreeViewExpr(0, pAggInfo->aCol[ii].pCExpr, 0);
}
for(ii=0; ii<pAggInfo->nFunc; ii++){
sqlite3DebugPrintf("agg-func[%d]: iMem=%d\n",
ii, pAggInfo->aFunc[ii].iMem);
sqlite3TreeViewExpr(0, pAggInfo->aFunc[ii].pFExpr, 0);
}
printAggInfo(pAggInfo);
}
#endif
@@ -7518,7 +7690,7 @@ int sqlite3Select(
** in the right order to begin with.
*/
sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset);
SELECTTRACE(1,pParse,p,("WhereBegin\n"));
TREETRACE(0x2,pParse,p,("WhereBegin\n"));
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, pDistinct,
p, (sDistinct.isTnct==2 ? WHERE_DISTINCTBY : WHERE_GROUPBY)
| (orderByGrp ? WHERE_SORTBYGROUP : 0) | distFlag, 0
@@ -7527,8 +7699,12 @@ int sqlite3Select(
sqlite3ExprListDelete(db, pDistinct);
goto select_end;
}
if( pParse->pIdxEpr ){
optimizeAggregateUseOfIndexedExpr(pParse, p, pAggInfo, &sNC);
}
assignAggregateRegisters(pParse, pAggInfo);
eDist = sqlite3WhereIsDistinct(pWInfo);
SELECTTRACE(1,pParse,p,("WhereBegin returns\n"));
TREETRACE(0x2,pParse,p,("WhereBegin returns\n"));
if( sqlite3WhereIsOrdered(pWInfo)==pGroupBy->nExpr ){
/* The optimizer is able to deliver rows in group by order so
** we do not have to sort. The OP_OpenEphemeral table will be
@@ -7577,7 +7753,7 @@ int sqlite3Select(
sqlite3VdbeAddOp2(v, OP_SorterInsert, pAggInfo->sortingIdx, regRecord);
sqlite3ReleaseTempReg(pParse, regRecord);
sqlite3ReleaseTempRange(pParse, regBase, nCol);
SELECTTRACE(1,pParse,p,("WhereEnd\n"));
TREETRACE(0x2,pParse,p,("WhereEnd\n"));
sqlite3WhereEnd(pWInfo);
pAggInfo->sortingIdxPTab = sortPTab = pParse->nTab++;
sortOut = sqlite3GetTempReg(pParse);
@@ -7587,6 +7763,23 @@ int sqlite3Select(
pAggInfo->useSortingIdx = 1;
}
/* If there entries in pAgggInfo->aFunc[] that contain subexpressions
** that are indexed (and that were previously identified and tagged
** in optimizeAggregateUseOfIndexedExpr()) then those subexpressions
** must now be converted into a TK_AGG_COLUMN node so that the value
** is correctly pulled from the index rather than being recomputed. */
if( pParse->pIdxEpr ){
aggregateConvertIndexedExprRefToColumn(pAggInfo);
#if TREETRACE_ENABLED
if( sqlite3TreeTrace & 0x20 ){
TREETRACE(0x20, pParse, p,
("AggInfo function expressions converted to reference index\n"));
sqlite3TreeViewSelect(0, p, 0);
printAggInfo(pAggInfo);
}
#endif
}
/* If the index or temporary table used by the GROUP BY sort
** will naturally deliver rows in the order required by the ORDER BY
** clause, cancel the ephemeral table open coded earlier.
@@ -7655,7 +7848,7 @@ int sqlite3Select(
sqlite3VdbeAddOp2(v, OP_SorterNext, pAggInfo->sortingIdx,addrTopOfLoop);
VdbeCoverage(v);
}else{
SELECTTRACE(1,pParse,p,("WhereEnd\n"));
TREETRACE(0x2,pParse,p,("WhereEnd\n"));
sqlite3WhereEnd(pWInfo);
sqlite3VdbeChangeToNoop(v, addrSortingIdx);
}
@@ -7765,7 +7958,8 @@ int sqlite3Select(
if( pKeyInfo ){
sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO);
}
sqlite3VdbeAddOp2(v, OP_Count, iCsr, pAggInfo->aFunc[0].iMem);
assignAggregateRegisters(pParse, pAggInfo);
sqlite3VdbeAddOp2(v, OP_Count, iCsr, AggInfoFuncReg(pAggInfo,0));
sqlite3VdbeAddOp1(v, OP_Close, iCsr);
explainSimpleCount(pParse, pTab, pBest);
}else{
@@ -7801,6 +7995,7 @@ int sqlite3Select(
pDistinct = pAggInfo->aFunc[0].pFExpr->x.pList;
distFlag = pDistinct ? (WHERE_WANT_DISTINCT|WHERE_AGG_DISTINCT) : 0;
}
assignAggregateRegisters(pParse, pAggInfo);
/* This case runs if the aggregate has no GROUP BY clause. The
** processing is much simpler since there is only a single row
@@ -7817,13 +8012,13 @@ int sqlite3Select(
assert( minMaxFlag==WHERE_ORDERBY_NORMAL || pMinMaxOrderBy!=0 );
assert( pMinMaxOrderBy==0 || pMinMaxOrderBy->nExpr==1 );
SELECTTRACE(1,pParse,p,("WhereBegin\n"));
TREETRACE(0x2,pParse,p,("WhereBegin\n"));
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMaxOrderBy,
pDistinct, p, minMaxFlag|distFlag, 0);
if( pWInfo==0 ){
goto select_end;
}
SELECTTRACE(1,pParse,p,("WhereBegin returns\n"));
TREETRACE(0x2,pParse,p,("WhereBegin returns\n"));
eDist = sqlite3WhereIsDistinct(pWInfo);
updateAccumulator(pParse, regAcc, pAggInfo, eDist);
if( eDist!=WHERE_DISTINCT_NOOP ){
@@ -7837,7 +8032,7 @@ int sqlite3Select(
if( minMaxFlag ){
sqlite3WhereMinMaxOptEarlyOut(v, pWInfo);
}
SELECTTRACE(1,pParse,p,("WhereEnd\n"));
TREETRACE(0x2,pParse,p,("WhereEnd\n"));
sqlite3WhereEnd(pWInfo);
finalizeAggFunctions(pParse, pAggInfo);
}
@@ -7884,7 +8079,7 @@ select_end:
if( pAggInfo && !db->mallocFailed ){
for(i=0; i<pAggInfo->nColumn; i++){
Expr *pExpr = pAggInfo->aCol[i].pCExpr;
assert( pExpr!=0 );
if( pExpr==0 ) continue;
assert( pExpr->pAggInfo==pAggInfo );
assert( pExpr->iAgg==i );
}
@@ -7898,8 +8093,8 @@ select_end:
#endif
#if TREETRACE_ENABLED
SELECTTRACE(0x1,pParse,p,("end processing\n"));
if( (sqlite3TreeTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
TREETRACE(0x1,pParse,p,("end processing\n"));
if( (sqlite3TreeTrace & 0x40000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
sqlite3TreeViewSelect(0, p, 0);
}
#endif
+82 -10
View File
@@ -16,6 +16,8 @@
/* This needs to come before any includes for MSVC compiler */
#define _CRT_SECURE_NO_WARNINGS
#endif
typedef unsigned int u32;
typedef unsigned short int u16;
/*
** Optionally #include a user-defined header, whereby compilation options
@@ -1064,9 +1066,14 @@ INCLUDE ../ext/expert/sqlite3expert.c
#define SQLITE_SHELL_HAVE_RECOVER 0
#endif
#if SQLITE_SHELL_HAVE_RECOVER
INCLUDE ../ext/recover/dbdata.c
INCLUDE ../ext/recover/sqlite3recover.h
# ifndef SQLITE_HAVE_SQLITE3R
INCLUDE ../ext/recover/dbdata.c
INCLUDE ../ext/recover/sqlite3recover.c
# endif
#endif
#ifdef SQLITE_SHELL_EXTSRC
# include SHELL_STRINGIFY(SQLITE_SHELL_EXTSRC)
#endif
#if defined(SQLITE_ENABLE_SESSION)
@@ -5113,6 +5120,7 @@ static void open_db(ShellState *p, int openFlags){
}
exit(1);
}
#ifndef SQLITE_OMIT_LOAD_EXTENSION
sqlite3_enable_load_extension(p->db, 1);
#endif
@@ -5135,6 +5143,34 @@ static void open_db(ShellState *p, int openFlags){
sqlite3_sqlar_init(p->db, 0, 0);
}
#endif
#ifdef SQLITE_SHELL_EXTFUNCS
/* Create a preprocessing mechanism for extensions to make
* their own provisions for being built into the shell.
* This is a short-span macro. See further below for usage.
*/
#define SHELL_SUB_MACRO(base, variant) base ## _ ## variant
#define SHELL_SUBMACRO(base, variant) SHELL_SUB_MACRO(base, variant)
/* Let custom-included extensions get their ..._init() called.
* The WHATEVER_INIT( db, pzErrorMsg, pApi ) macro should cause
* the extension's sqlite3_*_init( db, pzErrorMsg, pApi )
* inititialization routine to be called.
*/
{
int irc = SHELL_SUBMACRO(SQLITE_SHELL_EXTFUNCS, INIT)(p->db);
/* Let custom-included extensions expose their functionality.
* The WHATEVER_EXPOSE( db, pzErrorMsg ) macro should cause
* the SQL functions, virtual tables, collating sequences or
* VFS's implemented by the extension to be registered.
*/
if( irc==SQLITE_OK
|| irc==SQLITE_OK_LOAD_PERMANENTLY ){
SHELL_SUBMACRO(SQLITE_SHELL_EXTFUNCS, EXPOSE)(p->db, 0);
}
#undef SHELL_SUB_MACRO
#undef SHELL_SUBMACRO
}
#endif
sqlite3_create_function(p->db, "shell_add_schema", 3, SQLITE_UTF8, 0,
shellAddSchemaName, 0, 0);
sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, 0,
@@ -5155,6 +5191,7 @@ static void open_db(ShellState *p, int openFlags){
sqlite3_create_function(p->db, "edit", 2, SQLITE_UTF8, 0,
editFunc, 0, 0);
#endif
if( p->openMode==SHELL_OPEN_ZIPFILE ){
char *zSql = sqlite3_mprintf(
"CREATE VIRTUAL TABLE zip USING zipfile(%Q);", zDbFilename);
@@ -10654,7 +10691,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}else{
output_file_close(p->traceOut);
p->traceOut = output_file_open(azArg[1], 0);
p->traceOut = output_file_open(z, 0);
}
}
if( p->traceOut==0 ){
@@ -10957,13 +10994,11 @@ static int line_is_command_terminator(char *zLine){
}
/*
** We need a default sqlite3_complete() implementation to use in case
** the shell is compiled with SQLITE_OMIT_COMPLETE. The default assumes
** any arbitrary text is a complete SQL statement. This is not very
** user-friendly, but it does seem to work.
** The CLI needs a working sqlite3_complete() to work properly. So error
** out of the build if compiling with SQLITE_OMIT_COMPLETE.
*/
#ifdef SQLITE_OMIT_COMPLETE
#define sqlite3_complete(x) 1
# error the CLI application is imcompatable with SQLITE_OMIT_COMPLETE.
#endif
/*
@@ -11251,9 +11286,43 @@ static char *find_home_dir(int clearFlag){
return home_dir;
}
/*
** On non-Windows platforms, look for $XDG_CONFIG_HOME.
** If ${XDG_CONFIG_HOME}/sqlite3/sqliterc is found, return
** the path to it, else return 0. The result is cached for
** subsequent calls.
*/
static const char *find_xdg_config(void){
#if defined(_WIN32) || defined(WIN32) || defined(_WIN32_WCE) \
|| defined(__RTP__) || defined(_WRS_KERNEL)
return 0;
#else
static int alreadyTried = 0;
static char *zConfig = 0;
const char *zXdgHome;
if( alreadyTried!=0 ){
return zConfig;
}
alreadyTried = 1;
zXdgHome = getenv("XDG_CONFIG_HOME");
if( zXdgHome==0 ){
return 0;
}
zConfig = sqlite3_mprintf("%s/sqlite3/sqliterc", zXdgHome);
shell_check_oom(zConfig);
if( access(zConfig,0)!=0 ){
sqlite3_free(zConfig);
zConfig = 0;
}
return zConfig;
#endif
}
/*
** Read input from the file given by sqliterc_override. Or if that
** parameter is NULL, take input from ~/.sqliterc
** parameter is NULL, take input from the first of find_xdg_config()
** or ~/.sqliterc which is found.
**
** Returns the number of errors.
*/
@@ -11267,7 +11336,10 @@ static void process_sqliterc(
FILE *inSaved = p->in;
int savedLineno = p->lineno;
if (sqliterc == NULL) {
if( sqliterc == NULL ){
sqliterc = find_xdg_config();
}
if( sqliterc == NULL ){
home_dir = find_home_dir(0);
if( home_dir==0 ){
raw_printf(stderr, "-- warning: cannot find home directory;"
@@ -11730,7 +11802,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
if( pVfs ){
sqlite3_vfs_register(pVfs, 1);
}else{
utf8_printf(stderr, "no such VFS: \"%s\"\n", argv[i]);
utf8_printf(stderr, "no such VFS: \"%s\"\n", zVfs);
exit(1);
}
}
+26 -13
View File
@@ -2339,6 +2339,7 @@ struct sqlite3_mem_methods {
** <ul>
** <li> The [PRAGMA writable_schema=ON] statement.
** <li> The [PRAGMA journal_mode=OFF] statement.
** <li> The [PRAGMA schema_version=N] statement.
** <li> Writes to the [sqlite_dbpage] virtual table.
** <li> Direct writes to [shadow tables].
** </ul>
@@ -5541,16 +5542,6 @@ SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),
** then the conversion is performed. Otherwise no conversion occurs.
** The [SQLITE_INTEGER | datatype] after conversion is returned.)^
**
** ^(The sqlite3_value_encoding(X) interface returns one of [SQLITE_UTF8],
** [SQLITE_UTF16BE], or [SQLITE_UTF16LE] according to the current encoding
** of the value X, assuming that X has type TEXT.)^ If sqlite3_value_type(X)
** returns something other than SQLITE_TEXT, then the return value from
** sqlite3_value_encoding(X) is meaningless. ^Calls to
** sqlite3_value_text(X), sqlite3_value_text16(X), sqlite3_value_text16be(X),
** sqlite3_value_text16le(X), sqlite3_value_bytes(X), or
** sqlite3_value_bytes16(X) might change the encoding of the value X and
** thus change the return from subsequent calls to sqlite3_value_encoding(X).
**
** ^Within the [xUpdate] method of a [virtual table], the
** sqlite3_value_nochange(X) interface returns true if and only if
** the column corresponding to X is unchanged by the UPDATE operation
@@ -5615,6 +5606,27 @@ int sqlite3_value_type(sqlite3_value*);
int sqlite3_value_numeric_type(sqlite3_value*);
int sqlite3_value_nochange(sqlite3_value*);
int sqlite3_value_frombind(sqlite3_value*);
/*
** CAPI3REF: Report the internal text encoding state of an sqlite3_value object
** METHOD: sqlite3_value
**
** ^(The sqlite3_value_encoding(X) interface returns one of [SQLITE_UTF8],
** [SQLITE_UTF16BE], or [SQLITE_UTF16LE] according to the current text encoding
** of the value X, assuming that X has type TEXT.)^ If sqlite3_value_type(X)
** returns something other than SQLITE_TEXT, then the return value from
** sqlite3_value_encoding(X) is meaningless. ^Calls to
** [sqlite3_value_text(X)], [sqlite3_value_text16(X)], [sqlite3_value_text16be(X)],
** [sqlite3_value_text16le(X)], [sqlite3_value_bytes(X)], or
** [sqlite3_value_bytes16(X)] might change the encoding of the value X and
** thus change the return from subsequent calls to sqlite3_value_encoding(X).
**
** This routine is intended for used by applications that test and validate
** the SQLite implementation. This routine is inquiring about the opaque
** internal state of an [sqlite3_value] object. Ordinary applications should
** not need to know what the internal state of an sqlite3_value object is and
** hence should not need to use this interface.
*/
int sqlite3_value_encoding(sqlite3_value*);
/*
@@ -5874,9 +5886,10 @@ typedef void (*sqlite3_destructor_type)(void*);
** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE].
** ^SQLite takes the text result from the application from
** the 2nd parameter of the sqlite3_result_text* interfaces.
** ^If the 3rd parameter to the sqlite3_result_text* interfaces
** is negative, then SQLite takes result text from the 2nd parameter
** through the first zero character.
** ^If the 3rd parameter to any of the sqlite3_result_text* interfaces
** other than sqlite3_result_text64() is negative, then SQLite computes
** the string length itself by searching the 2nd parameter for the first
** zero character.
** ^If the 3rd parameter to the sqlite3_result_text* interfaces
** is non-negative, then as many bytes (not characters) of the text
** pointed to by the 2nd parameter are taken as the application-defined
+42 -7
View File
@@ -1029,15 +1029,38 @@ extern u32 sqlite3TreeTrace;
&& (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_SELECTTRACE) \
|| defined(SQLITE_ENABLE_TREETRACE))
# define TREETRACE_ENABLED 1
# define SELECTTRACE(K,P,S,X) \
# define TREETRACE(K,P,S,X) \
if(sqlite3TreeTrace&(K)) \
sqlite3DebugPrintf("%u/%d/%p: ",(S)->selId,(P)->addrExplain,(S)),\
sqlite3DebugPrintf X
#else
# define SELECTTRACE(K,P,S,X)
# define TREETRACE(K,P,S,X)
# define TREETRACE_ENABLED 0
#endif
/* TREETRACE flag meanings:
**
** 0x00000001 Beginning and end of SELECT processing
** 0x00000002 WHERE clause processing
** 0x00000004 Query flattener
** 0x00000008 Result-set wildcard expansion
** 0x00000010 Query name resolution
** 0x00000020 Aggregate analysis
** 0x00000040 Window functions
** 0x00000080 Generated column names
** 0x00000100 Move HAVING terms into WHERE
** 0x00000200 Count-of-view optimization
** 0x00000400 Compound SELECT processing
** 0x00000800 Drop superfluous ORDER BY
** 0x00001000 LEFT JOIN simplifies to JOIN
** 0x00002000 Constant propagation
** 0x00004000 Push-down optimization
** 0x00008000 After all FROM-clause analysis
** 0x00010000 Beginning of DELETE/INSERT/UPDATE processing
** 0x00020000 Transform DISTINCT into GROUP BY
** 0x00040000 SELECT tree dump after all code has been generated
*/
/*
** Macros for "wheretrace"
*/
@@ -2716,16 +2739,15 @@ struct AggInfo {
** from source tables rather than from accumulators */
u8 useSortingIdx; /* In direct mode, reference the sorting index rather
** than the source table */
u16 nSortingColumn; /* Number of columns in the sorting index */
int sortingIdx; /* Cursor number of the sorting index */
int sortingIdxPTab; /* Cursor number of pseudo-table */
int nSortingColumn; /* Number of columns in the sorting index */
int mnReg, mxReg; /* Range of registers allocated for aCol and aFunc */
int iFirstReg; /* First register in range for aCol[] and aFunc[] */
ExprList *pGroupBy; /* The group by clause */
struct AggInfo_col { /* For each column used in source tables */
Table *pTab; /* Source table */
Expr *pCExpr; /* The original expression */
int iTable; /* Cursor number of the source table */
int iMem; /* Memory location that acts as accumulator */
i16 iColumn; /* Column number within the source table */
i16 iSorterColumn; /* Column number in the sorting index */
} *aCol;
@@ -2736,7 +2758,6 @@ struct AggInfo {
struct AggInfo_func { /* For each aggregate function */
Expr *pFExpr; /* Expression encoding the function */
FuncDef *pFunc; /* The aggregate function implementation */
int iMem; /* Memory location that acts as accumulator */
int iDistinct; /* Ephemeral table used to enforce DISTINCT */
int iDistAddr; /* Address of OP_OpenEphemeral */
} *aFunc;
@@ -2744,6 +2765,17 @@ struct AggInfo {
u32 selId; /* Select to which this AggInfo belongs */
};
/*
** Macros to compute aCol[] and aFunc[] register numbers.
**
** These macros should not be used prior to the call to
** assignAggregateRegisters() that computes the value of pAggInfo->iFirstReg.
** The assert()s that are part of this macro verify that constraint.
*/
#define AggInfoColumnReg(A,I) (assert((A)->iFirstReg),(A)->iFirstReg+(I))
#define AggInfoFuncReg(A,I) \
(assert((A)->iFirstReg),(A)->iFirstReg+(A)->nColumn+(I))
/*
** The datatype ynVar is a signed integer, either 16-bit or 32-bit.
** Usually it is 16-bits. But if SQLITE_MAX_VARIABLE_NUMBER is greater
@@ -3656,7 +3688,7 @@ struct Parse {
int nLabelAlloc; /* Number of slots in aLabel */
int *aLabel; /* Space to hold the labels */
ExprList *pConstExpr;/* Constant expressions */
IndexedExpr *pIdxExpr;/* List of expressions used by active indexes */
IndexedExpr *pIdxEpr;/* List of expressions used by active indexes */
Token constraintName;/* Name of the constraint currently being parsed */
yDbMask writeMask; /* Start a write transaction on these databases */
yDbMask cookieMask; /* Bitmask of schema verified databases */
@@ -5003,6 +5035,9 @@ const char *sqlite3ErrName(int);
#ifndef SQLITE_OMIT_DESERIALIZE
int sqlite3MemdbInit(void);
int sqlite3IsMemdb(const sqlite3_vfs*);
#else
# define sqlite3IsMemdb(X) 0
#endif
const char *sqlite3ErrStr(int);
+2
View File
@@ -7709,6 +7709,7 @@ static int SQLITE_TCLAPI tclLoadStaticExtensionCmd(
){
extern int sqlite3_amatch_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_appendvfs_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_basexx_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_carray_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_closure_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_csv_init(sqlite3*,char**,const sqlite3_api_routines*);
@@ -7740,6 +7741,7 @@ static int SQLITE_TCLAPI tclLoadStaticExtensionCmd(
} aExtension[] = {
{ "amatch", sqlite3_amatch_init },
{ "appendvfs", sqlite3_appendvfs_init },
{ "basexx", sqlite3_basexx_init },
{ "carray", sqlite3_carray_init },
{ "closure", sqlite3_closure_init },
{ "csv", sqlite3_csv_init },
+8
View File
@@ -521,6 +521,14 @@ static int SQLITE_TCLAPI fake_big_file(
return TCL_ERROR;
}
if( Tcl_GetInt(interp, argv[1], &n) ) return TCL_ERROR;
#if defined(_WIN32)
if( n>2 ){
Tcl_AppendResult(interp, "cannot create ", argv[1],
"MB file because Windows "
"does not support sparse files", (void*)0);
return TCL_ERROR;
}
#endif
pVfs = sqlite3_vfs_find(0);
nFile = (int)strlen(argv[2]);
+4 -1
View File
@@ -487,7 +487,7 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
sqlite3TreeViewPop(&pView);
return;
}
if( pExpr->flags || pExpr->affExpr || pExpr->vvaFlags ){
if( pExpr->flags || pExpr->affExpr || pExpr->vvaFlags || pExpr->pAggInfo ){
StrAccum x;
sqlite3StrAccumInit(&x, 0, zFlgs, sizeof(zFlgs), 0);
sqlite3_str_appendf(&x, " fg.af=%x.%c",
@@ -504,6 +504,9 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
if( ExprHasVVAProperty(pExpr, EP_Immutable) ){
sqlite3_str_appendf(&x, " IMMUTABLE");
}
if( pExpr->pAggInfo!=0 ){
sqlite3_str_appendf(&x, " agg-column[%d]", pExpr->iAgg);
}
sqlite3StrAccumFinish(&x);
}else{
zFlgs[0] = 0;
+1 -1
View File
@@ -61,7 +61,7 @@ Trigger *sqlite3TriggerList(Parse *pParse, Table *pTab){
if( pTrig->pTabSchema==pTab->pSchema
&& pTrig->table
&& 0==sqlite3StrICmp(pTrig->table, pTab->zName)
&& pTrig->pTabSchema!=pTmpSchema
&& (pTrig->pTabSchema!=pTmpSchema || pTrig->bReturning)
){
pTrig->pNext = pList;
pList = pTrig;
+1
View File
@@ -5579,6 +5579,7 @@ case OP_Insert: {
x.nZero = 0;
}
x.pKey = 0;
assert( BTREE_PREFORMAT==OPFLAG_PREFORMAT );
rc = sqlite3BtreeInsert(pC->uc.pCursor, &x,
(pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION|OPFLAG_PREFORMAT)),
seekResult
+7 -2
View File
@@ -4575,7 +4575,7 @@ int sqlite3VdbeRecordCompareWithSkip(
assert( pPKey2->pKeyInfo->aSortFlags!=0 );
assert( pPKey2->pKeyInfo->nKeyField>0 );
assert( idx1<=szHdr1 || CORRUPT_DB );
do{
while( 1 /*exit-by-break*/ ){
u32 serial_type;
/* RHS is an integer */
@@ -4713,8 +4713,13 @@ int sqlite3VdbeRecordCompareWithSkip(
if( i==pPKey2->nField ) break;
pRhs++;
d1 += sqlite3VdbeSerialTypeLen(serial_type);
if( d1>(unsigned)nKey1 ) break;
idx1 += sqlite3VarintLen(serial_type);
}while( idx1<(unsigned)szHdr1 && d1<=(unsigned)nKey1 );
if( idx1>=(unsigned)szHdr1 ){
pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT;
return 0; /* Corrupt index */
}
}
/* No memory allocation is ever used on mem1. Prove this using
** the following assert(). If the assert() fails, it indicates a
+11 -11
View File
@@ -4591,8 +4591,8 @@ static i8 wherePathSatisfiesOrderBy(
if( pOBExpr->iTable!=iCur ) continue;
if( pOBExpr->iColumn!=iColumn ) continue;
}else{
Expr *pIdxExpr = pIndex->aColExpr->a[j].pExpr;
if( sqlite3ExprCompareSkip(pOBExpr, pIdxExpr, iCur) ){
Expr *pIxExpr = pIndex->aColExpr->a[j].pExpr;
if( sqlite3ExprCompareSkip(pOBExpr, pIxExpr, iCur) ){
continue;
}
}
@@ -5461,13 +5461,13 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful(
/*
** This is an sqlite3ParserAddCleanup() callback that is invoked to
** free the Parse->pIdxExpr list when the Parse object is destroyed.
** free the Parse->pIdxEpr list when the Parse object is destroyed.
*/
static void whereIndexedExprCleanup(sqlite3 *db, void *pObject){
Parse *pParse = (Parse*)pObject;
while( pParse->pIdxExpr!=0 ){
IndexedExpr *p = pParse->pIdxExpr;
pParse->pIdxExpr = p->pIENext;
while( pParse->pIdxEpr!=0 ){
IndexedExpr *p = pParse->pIdxEpr;
pParse->pIdxEpr = p->pIENext;
sqlite3ExprDelete(db, p->pExpr);
sqlite3DbFreeNN(db, p);
}
@@ -5479,13 +5479,13 @@ static void whereIndexedExprCleanup(sqlite3 *db, void *pObject){
** number for the index and iDataCur is the cursor number for the corresponding
** table.
**
** This routine adds IndexedExpr entries to the Parse->pIdxExpr field for
** This routine adds IndexedExpr entries to the Parse->pIdxEpr field for
** each of the expressions in the index so that the expression code generator
** will know to replace occurrences of the indexed expression with
** references to the corresponding column of the index.
*/
static SQLITE_NOINLINE void whereAddIndexedExpr(
Parse *pParse, /* Add IndexedExpr entries to pParse->pIdxExpr */
Parse *pParse, /* Add IndexedExpr entries to pParse->pIdxEpr */
Index *pIdx, /* The index-on-expression that contains the expressions */
int iIdxCur, /* Cursor number for pIdx */
SrcItem *pTabItem /* The FROM clause entry for the table */
@@ -5514,7 +5514,7 @@ static SQLITE_NOINLINE void whereAddIndexedExpr(
if( sqlite3ExprIsConstant(pExpr) ) continue;
p = sqlite3DbMallocRaw(pParse->db, sizeof(IndexedExpr));
if( p==0 ) break;
p->pIENext = pParse->pIdxExpr;
p->pIENext = pParse->pIdxEpr;
p->pExpr = sqlite3ExprDup(pParse->db, pExpr, 0);
p->iDataCur = pTabItem->iCursor;
p->iIdxCur = iIdxCur;
@@ -5523,7 +5523,7 @@ static SQLITE_NOINLINE void whereAddIndexedExpr(
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
p->zIdxName = pIdx->zName;
#endif
pParse->pIdxExpr = p;
pParse->pIdxEpr = p;
if( p->pIENext==0 ){
sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pParse);
}
@@ -6474,7 +6474,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
last = pWInfo->iEndWhere;
}
if( pIdx->bHasExpr ){
IndexedExpr *p = pParse->pIdxExpr;
IndexedExpr *p = pParse->pIdxEpr;
while( p ){
if( p->iIdxCur==pLevel->iIdxCur ){
p->iDataCur = -1;
+1 -1
View File
@@ -1069,7 +1069,7 @@ int sqlite3WindowRewrite(Parse *pParse, Select *p){
pSub = sqlite3SelectNew(
pParse, pSublist, pSrc, pWhere, pGroupBy, pHaving, pSort, 0, 0
);
SELECTTRACE(1,pParse,pSub,
TREETRACE(0x40,pParse,pSub,
("New window-function subquery in FROM clause of (%u/%p)\n",
p->selId, p));
p->pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0);
+155
View File
@@ -0,0 +1,155 @@
# 2022 November 22
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix basexx
if {[catch {load_static_extension db basexx} error]} {
puts "Skipping basexx tests, hit load error: $error"
finish_test; return
}
# Empty blobs encode to empty strings.
do_execsql_test 100 {
SELECT base64(x'')||base85(x'');
} {{}}
# Empty strings decode to empty blobs.
do_execsql_test 101 {
SELECT hex(x'01'||base64('')||base85('')||x'02');
} {0102}
# Basic base64 encoding
do_execsql_test 102 {
SELECT base64(x'000102030405');
SELECT base64(x'0001020304');
SELECT base64(x'00010203');
} {{AAECAwQF
} {AAECAwQ=
} {AAECAw==
}}
# Basic base64 decoding with pad chars
do_execsql_test 103 {
SELECT hex(base64('AAECAwQF'));
SELECT hex(base64('AAECAwQ='));
SELECT hex(base64('AAECAw=='));
} {000102030405 0001020304 00010203}
# Basic base64 decoding without pad chars and with whitespace
do_execsql_test 104 {
SELECT hex(base64(' AAECAwQF '));
SELECT hex(base64(' AAECAwQ'));
SELECT hex(base64('AAECAw '));
} {000102030405 0001020304 00010203}
# Basic base85 encoding
do_execsql_test 105 {
SELECT base85(x'000102030405');
SELECT base85(x'0001020304');
SELECT base85(x'00010203');
} {{##/2,#2/
} {##/2,#*
} {##/2,
}}
# Basic base85 decoding with and without whitespace
do_execsql_test 106 {
SELECT hex(base85('##/2,#2/'));
SELECT hex(base85('##/2,#*'));
SELECT hex(base85('##/2,'));
SELECT hex(base85(' ##/2,#2/ '));
SELECT hex(base85(' ##/2,#*'));
SELECT hex(base85('##/2, '));
} {000102030405 0001020304 00010203 000102030405 0001020304 00010203}
# Round-trip some random blobs.
do_execsql_test 107 {
CREATE TEMP TABLE rb( len int, b blob ) STRICT;
INSERT INTO rb(len) VALUES (1),(2),(3),(4),(5),(150),(151),(152),(153),(1054);
UPDATE rb SET b = randomblob(len);
SELECT len, base64(base64(b))=b, base85(base85(b))=b
FROM rb ORDER BY len;
} {1 1 1 2 1 1 3 1 1 4 1 1 5 1 1 150 1 1 151 1 1 152 1 1 153 1 1 1054 1 1}
# Same round-trip but with space or junk prepended and/or appended or not.
do_execsql_test 108 {
CREATE TEMP TABLE junk(j text, rank int);
INSERT INTO junk VALUES ('',0),(' ',1),('~',2);
SELECT len, base64(j.j||base64(b)||j.j)=b, base85(j.j||base85(b)||j.j)=b
FROM rb r, junk j WHERE j.rank=(r.len+r.len/25)%3 ORDER BY len;
} {1 1 1 2 1 1 3 1 1 4 1 1 5 1 1 150 1 1 151 1 1 152 1 1 153 1 1 1054 1 1}
# Exercise the fail-on-too-large result feature.
set inLimit [sqlite3_limit db SQLITE_LIMIT_LENGTH -1]
sqlite3_limit db SQLITE_LIMIT_LENGTH 1300
do_catchsql_test 109 {
SELECT len, base64(b) FROM rb WHERE len>200;
} {1 {blob expanded to base64 too big}}
do_catchsql_test 110 {
SELECT len, base85(b) FROM rb WHERE len>200;
} {1 {blob expanded to base85 too big}}
do_catchsql_test 111 {
SELECT length(base85(b))=1335 FROM rb WHERE len=1054;
} {1 {blob expanded to base85 too big}}
sqlite3_limit db SQLITE_LIMIT_LENGTH $inLimit
# Exercise is_base85(t)
do_execsql_test 112 {
SELECT is_base85(' '||base85(x'123456')||char(10)),
is_base85('#$%&*+,-./0123456789:;<=>?@'
||'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
||'[\]^_`'
||'abcdefghijklmnopqrstuvwxyz'),
is_base85('!'), is_base85('"'), is_base85(''''), is_base85('('),
is_base85(')'), is_base85(char(123)), is_base85('|'), is_base85(char(125)),
is_base85('~'), is_base85(char(127));
} {1 1 0 0 0 0 0 0 0 0 0 0}
do_execsql_test 113 {
SELECT is_base85(NULL) IS NULL;
} {1}
do_catchsql_test 114 {
SELECT is_base85(1);
} {1 {is_base85 accepts only text or NULL}}
do_catchsql_test 115 {
SELECT is_base85(1.1);
} {1 {is_base85 accepts only text or NULL}}
do_catchsql_test 116 {
SELECT is_base85(x'00');
} {1 {is_base85 accepts only text or NULL}}
# Round-trip many bigger random blobs.
do_execsql_test 117 {
CREATE TABLE bs(b blob, num);
INSERT INTO bs SELECT randomblob(4000 + n%3), n
FROM (
WITH RECURSIVE seq(n) AS (
VALUES(1) UNION ALL SELECT n+1
FROM seq WHERE n<100
) SELECT n FROM seq);
SELECT num FROM bs WHERE base64(base64(b))!=b;
SELECT num FROM bs WHERE base85(base85(b))!=b;
} {}
finish_test
+5 -1
View File
@@ -52,7 +52,11 @@ do_execsql_test 1.0 {
}
for {set i 1} {$i < 8} {incr i} {
fake_big_file [expr $i*1024] [get_pwd]/test.db
if {[catch {fake_big_file [expr $i*1024] [get_pwd]/test.db}]} {
puts "Cannot create ${i}MB sparse file"
finish_test
return
}
hexio_write test.db 28 [format %.8x [expr ($i*1024*1024*1024/4096) - 5]]
do_execsql_test 1.$i "
+7
View File
@@ -24,10 +24,17 @@ set testprefix bigsort
# memory. Make sure the host has at least 8GB available before running
# this test.
#
# Update: https://sqlite.org/src/info/7c96a56 adds assert() statements
# that make this test too slow to run with SQLITE_DEBUG builds.
#
if {[catch {exec free | grep Mem:} out] || [lindex $out 1]<8000000} {
finish_test
return
}
ifcapable debug {
finish_test
return
}
do_execsql_test 1.0 {
PRAGMA page_size = 1024;
+19 -6
View File
@@ -1479,13 +1479,26 @@ do_test 19.0 {
do_execsql_test 19.1 {
PRAGMA writable_schema=ON;
}
set err "UNIQUE constraint failed: index 'a'"
ifcapable oversize_cell_check {
set err "database disk image is malformed"
}
do_catchsql_test 19.2 {
UPDATE t1 SET a=1;
} [list 1 $err]
} {1 {database disk image is malformed}}
reset_db
do_execsql_test 19.3 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT, c INTEGER, d TEXT);
CREATE INDEX i1 ON t1((NULL));
INSERT INTO t1 VALUES(1, NULL, 1, 'text value');
PRAGMA writable_schema = on;
UPDATE sqlite_schema SET
sql = 'CREATE INDEX i1 ON t1(b, c, d)',
tbl_name = 't1',
type='index'
WHERE name='i1';
}
db close
sqlite3 db test.db
do_catchsql_test 19.4 {
PRAGMA integrity_check;
} {1 {database disk image is malformed}}
finish_test
+23 -8
View File
@@ -20,6 +20,11 @@ if {[permutation] == "inmemory_journal"} {
return
}
ifcapable !vtab {
finish_test
return
}
set testprefix dbpagefault
faultsim_save_and_close
@@ -57,15 +62,25 @@ do_execsql_test 3.0 {
END;
}
do_faultsim_test 3 -prep {
catch { db close }
sqlite3 db test.db
execsql { PRAGMA trusted_schema = true }
} -body {
execsql { INSERT INTO x1 DEFAULT VALUES; }
} -test {
faultsim_test_result {0 {}}
# This test case no longer works, as it is no longer possible to use
# virtual table sqlite_dbpage from within a trigger.
#
do_execsql_test 3.1 {
PRAGMA trusted_schema = 1;
}
do_catchsql_test 3.2 {
PRAGMA trusted_schema = 1;
INSERT INTO x1 DEFAULT VALUES;
} {1 {unsafe use of virtual table "sqlite_dbpage"}}
#do_faultsim_test 3 -prep {
# catch { db close }
# sqlite3 db test.db
# execsql { PRAGMA trusted_schema = 1 }
#} -body {
# execsql { INSERT INTO x1 DEFAULT VALUES; }
#} -test {
# faultsim_test_result {0 {}}
#}
finish_test
+10 -1
View File
@@ -50,7 +50,16 @@ do_icu_expr_test 1.6 { "(x OR y)" } {PHRASE 3 0 ( x or y )}
# is passed to the tokenizer.
#
do_icu_expr_test 1.7 {a:word} {PHRASE 0 0 word}
do_icu_expr_test 1.8 {d:word} {PHRASE 3 0 d:word}
# do_icu_expr_test 1.8 {d:word} {PHRASE 3 0 d:word}
do_test 1.8 {
set res [
db one {SELECT fts3_exprtest('icu en_US', 'd:word', 'a', 'b', 'c')}
]
expr {
$res=="PHRASE 3 0 d:word" ||
$res=="AND {AND {PHRASE 3 0 d} {PHRASE 3 0 :}} {PHRASE 3 0 word}"
}
} 1
set sqlite_fts3_enable_parentheses 0
+2
View File
@@ -82,6 +82,8 @@ do_faultsim_test 1 -prep {
multiplex_restore_db
sqlite3 db file:test.db?8_3_names=1
sqlite3_multiplex_control db main chunk_size [expr 256*1024]
execsql { PRAGMA journal_mode = truncate }
execsql { PRAGMA synchronous = off }
} -body {
execsql {
UPDATE t1 SET a=randomblob(12), b=randomblob(1500) WHERE (rowid%32)=0
+2
View File
@@ -806,6 +806,8 @@ test_suite "inmemory_journal" -description {
# This test depends on a successful recovery from the pager error
# state. Which is not possible with an in-memory journal
fts5fault1.test
recoverpgsz.test
}]
ifcapable mem3 {
+9 -9
View File
@@ -904,15 +904,15 @@ do_test pragma-8.1.2 {
PRAGMA schema_version;
}
} {schema_version 105}
do_test pragma-8.1.3 {
execsql {
PRAGMA schema_version = 106;
}
} {}
do_test pragma-8.1.4 {
execsql {
PRAGMA schema_version;
}
sqlite3_db_config db DEFENSIVE 1
do_execsql_test pragma-8.1.3 {
PRAGMA schema_version = 106;
PRAGMA schema_version;
} 105
sqlite3_db_config db DEFENSIVE 0
do_execsql_test pragma-8.1.4 {
PRAGMA schema_version = 106;
PRAGMA schema_version;
} 106
# Check that creating a table modifies the schema-version (this is really
+26 -1
View File
@@ -86,6 +86,31 @@ do_test 2.2 {
set L
} {three}
# 2022-11-25 dbsqlfuzz crash-3a548de406a50e896c1bf7142692d35d339d697f
# Disable the push-down optimization for compound subqueries if any
# arm of the compound has an incompatible affinity.
#
reset_db
do_execsql_test 3.1 {
CREATE TABLE t0(c0 INT);
INSERT INTO t0 VALUES(0);
CREATE TABLE t1_a(a INTEGER PRIMARY KEY, b TEXT);
INSERT INTO t1_a VALUES(1,'one'); --,(4,'four');
CREATE TABLE t1_b(c INTEGER PRIMARY KEY, d TEXT);
INSERT INTO t1_b VALUES(2,'two'); --,(5,'five');
CREATE VIEW v0 AS SELECT CAST(t0.c0 AS INTEGER) AS c0 FROM t0;
CREATE VIEW t1 AS SELECT a, b FROM t1_a UNION ALL SELECT c, 0 FROM t1_b;
SELECT t1.a, quote(t1.b), t0.c0 AS cd FROM t0 LEFT JOIN v0 ON v0.c0!=0,t1;
} {
1 'one' 0
2 '0' 0
}
do_execsql_test 3.2 {
SELECT a, quote(b), cd FROM (
SELECT t1.a, t1.b, t0.c0 AS cd FROM t0 LEFT JOIN v0 ON v0.c0!=0,t1
) WHERE a=2 AND b='0' AND cd=0;
} {
2 '0' 0
}
finish_test
+2
View File
@@ -141,6 +141,8 @@ do_recover_test 3.0
#-------------------------------------------------------------------------
reset_db
execsql { PRAGMA secure_delete = 0 }
execsql { PRAGMA auto_vacuum = 0 }
do_execsql_test 4.0 {
CREATE TABLE t1(a, b, c);
CREATE TABLE t2(d, e, f);
+27
View File
@@ -303,6 +303,33 @@ do_execsql_test regexp1-6.7 {SELECT 'xabc' REGEXP '(^abc|def)';} {0}
do_execsql_test regexp1-6.8 {SELECT 'def' REGEXP '(^abc|def)';} {1}
do_execsql_test regexp1-6.9 {SELECT 'xdef' REGEXP '(^abc|def)';} {1}
# 2022-11-17
# https://sqlite.org/forum/forumpost/3ffe058b04
#
do_execsql_test regexp1-7.1 {
SELECT char(0x61,0x7ff,0x62) REGEXP char(0x7ff);
} 1
do_execsql_test regexp1-7.2 {
SELECT char(0x61,0x800,0x62) REGEXP char(0x800);
} 1
do_execsql_test regexp1-7.3 {
SELECT char(0x61,0xabc,0x62) REGEXP char(0xabc);
} 1
do_execsql_test regexp1-7.4 {
SELECT char(0x61,0xfff,0x62) REGEXP char(0xfff);
} 1
do_execsql_test regexp1-7.5 {
SELECT char(0x61,0x1000,0x62) REGEXP char(0x1000);
} 1
do_execsql_test regexp1-7.10 {
SELECT char(0x61,0xffff,0x62) REGEXP char(0xffff);
} 1
do_execsql_test regexp1-7.11 {
SELECT char(0x61,0x10000,0x62) REGEXP char(0x10000);
} 1
do_execsql_test regexp1-7.12 {
SELECT char(0x61,0x10ffff,0x62) REGEXP char(0x10ffff);
} 1
finish_test
+1
View File
@@ -51,6 +51,7 @@ array set ::Configs [strip_comments {
-O2
--disable-amalgamation --disable-shared
--enable-session
-DSQLITE_ENABLE_RBU
}
"Sanitize" {
CC=clang -fsanitize=address,undefined
+24
View File
@@ -376,4 +376,28 @@ do_execsql_test 16.1 {
SELECT * FROM t2;
} {1 2 3 a b c}
foreach {tn temp} {
1 ""
2 TEMP
} {
reset_db
do_execsql_test 17.$tn.0 "
CREATE $temp TABLE foo (
fooid INTEGER PRIMARY KEY,
fooval INTEGER NOT NULL UNIQUE,
refcnt INTEGER NOT NULL DEFAULT 1
);
"
do_execsql_test 17.$tn.1 {
INSERT INTO foo (fooval) VALUES (17), (4711), (17)
ON CONFLICT DO
UPDATE SET refcnt = refcnt+1
RETURNING fooid;
} {
1 2 1
}
}
finish_test
+6
View File
@@ -125,6 +125,12 @@ SELECT * FROM t1;}}
do_test shell4-2.5 {
catchcmd ":memory:" "CREATE TABLE t1(x);\n.trace stdout\nSELECT * FROM t1;"
} {0 {SELECT * FROM t1;}}
do_test shell4-2.6 {
catchcmd ":memory:" {
CREATE TABLE t1(x);
.trace --stmt stdout
SELECT * FROM t1;}
} {0 {SELECT * FROM t1;}}
}
do_test shell4-3.1 {

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