Compare commits

..

146 Commits

Author SHA1 Message Date
larrybr dd65746db0 Eliminate a redundant NK_DbAboutToClose notification.
FossilOrigin-Name: eccffaa7ca97828202c26341dca615fb762922c221be12a60b14858cdbe5e91d
2023-09-02 22:07:23 +00:00
larrybr a900467804 Issue NK_DbAboutToClose notifications for any shell DB about to be closed, whether presently visible in ShellExState or not.
FossilOrigin-Name: 8e6e54e48c1739c76db3e7979669cf17f7cee471b1d37836adbf72f42e8b8972
2023-09-02 22:00:48 +00:00
larrybr f5b71c20a9 Sync w/trunk
FossilOrigin-Name: fe9aa2e9c17f33a8f6045e9cf755f8251d261c34f89a59d658b72f7c99a6a12c
2023-06-29 17:58:51 +00:00
larrybr 9b2ef36b2f Sync w/trunk (for CLI -cachetrace)
FossilOrigin-Name: a61f9377014ee582ec469f1066196e07b745295724c6d3ff4baffcaed22ae5a1
2023-06-21 14:48:41 +00:00
larrybr 07bbda75fe Cure tclshext build warning. Sync w/trunk.
FossilOrigin-Name: e9f2119106f687ecf9bc9c5f78c043ce7dd91874d3fb516d7621dbd806d8a174
2023-06-17 23:53:51 +00:00
larrybr d46c5cde2e Sync w/trunk.
FossilOrigin-Name: 746733c1c5905ec8c372eaf0dd720b7efac63e1102f0c63812a2bc3bb4ed6dc3
2023-06-17 01:56:55 +00:00
larrybr 5bfcd01a8d Clear MSVC warnings and fix a typedef issue with MSVC build.
FossilOrigin-Name: b0cb4cfceaa45c31c217e8e0edf4b1431f5e1bd05f85d49528792d69bdbdd6dc
2023-06-16 19:45:55 +00:00
larrybr e491fd9438 More doc tweaks, one teensy enhancement, and a new feature bug fix
FossilOrigin-Name: 1073ef0629921dbb74719045a742d68d2bb4e28fc5fa6463e725ddd1d9e6e651
2023-06-16 02:18:59 +00:00
larrybr fc2e294988 Improve and add docs for extensible shell.
FossilOrigin-Name: 1a57af86da17a3da6bb343001c779d4d9d2ff2c1937ded4d6370d6a951c3cda4
2023-06-13 23:04:34 +00:00
larrybr 524ec63005 Finish .parameter improvements. (no set <cast_op> anymore, not useful enough for its apparent complexity)
FossilOrigin-Name: bbbdb864f087e806f8fb73618dd769cba5450252e33e938dac0630dd0abb8d43
2023-06-12 18:11:11 +00:00
larrybr ccf446a569 More .parameter improvements, a WIP for ".parameter set <cast_op> ..."
FossilOrigin-Name: 2b51dcdad8fdace33daefdf8ad00fc2f3ab069d5832a1ecf2ac95f580556685d
2023-06-09 06:04:32 +00:00
larrybr a1fe51ffb9 Sync w/trunk. Fix spelling nits. Add ".parameter edit ..." improvements.
FossilOrigin-Name: d73f065b939eaf7f468c43efe21d66dd20109d8b0ffc8b7ffa4bd491072a50dc
2023-06-09 01:11:02 +00:00
larrybr dd2a8b9f14 More ExportHandler use. Sync w/trunk. Fix .archive UB.
FossilOrigin-Name: 4fd4240f14a696a26bce637eaa94121f64edff0894f8c1f341c2f426a74c8fe0
2023-06-06 01:07:00 +00:00
larrybr 1a25fa3d5e Get built-in ExportHandler instances to do more of .mode work. Replace atexit() with something that works for (not quite) "main" returns. Sync w/trunk.
FossilOrigin-Name: a588dd9fc6b1d89a1ad884b581086e2c668af17ff4564805704620908b84276e
2023-06-02 20:27:47 +00:00
larrybr 9e1e39de61 CLI much closer to being callable. (fewer exit() calls) ^C interrupt response improved. Sync w/trunk. Some diagnostic code removed. Some refactoring for sake of FIDDLE build. Streamline main(), by moving code to more dedicated routines and otherwise. Make -quiet more useful.
FossilOrigin-Name: bff3a9a834a71871129e516c9a6d05fab92e004934fe0b99bccb9f3f1fd56e33
2023-05-27 00:03:43 +00:00
larrybr e6452d34e9 First serious stab at FIDDLE-build-ready. Fix a flub with absent -A option for some builds.
FossilOrigin-Name: bfc36f0f10c37ae89bae6de96da38197998c8d95ab49397775d43d8a1ae7eeef
2023-05-21 05:06:30 +00:00
larrybr 4ff28b2dc1 CLI code cleanup and shuffle for clarity. Moderate refactoring to exploit commmon patterns. Extend OOM simulation for debug builds. Plug one leak. Fix trivial nit copied from trunk.
FossilOrigin-Name: b64ef7f92e7c369b279dac136983c69ab1e6fedc7f12a7dff65a86506761bce5
2023-05-21 02:48:51 +00:00
larrybr 898ea36c92 CLI resmanage used wherever resources should not be leaked upon abrupt (OOM) termination.
FossilOrigin-Name: 1687d12339f18dfc0412624765ae76d899a89e727e007e3054730533fac2a36c
2023-05-19 16:25:24 +00:00
larrybr 529e842a18 CLI resmanage use to 100% by commands, 84% by LOC. WIP
FossilOrigin-Name: 48f81d8fc650bf85028c729968f5de894f9c7e96b6ea1ec58cab31a39cb78417
2023-05-18 23:42:43 +00:00
larrybr 211ab33bf0 More CLI resmanage use (75% LOC)
FossilOrigin-Name: 3428ce82476e996c0bf06b623c93e6d5ba1a4542e1384350321405f8bd0d0e74
2023-05-17 15:22:17 +00:00
larrybr 51d385df85 Further CLI resmanage use. (69% by LOC, 53% by command count) Some renames for clarity.
FossilOrigin-Name: c010e1a7b2120d44c096b7d4216a8a51601972581f232d537b577617dfa6195a
2023-05-17 03:57:12 +00:00
larrybr 3008965443 Further extend use of CLI resmanage. (40% of dot commands)
FossilOrigin-Name: 0cdab9f17a2ceaa4d4228e535c3d780ed5fd2cb4e36cc50becba99e8ebff362d
2023-05-16 20:37:38 +00:00
larrybr 555fe8a08e Use CLI resmanage to ensure input redirection is unwound on abort. Remove unused resmanage feature. Extend no-leak-on-abort fraction. Fix unlikely (and old) GPF upon OOM.
FossilOrigin-Name: 243085279c6f8c51ad85bdc1e7c07ef6f858067640a7330f77d0bf94ed11b438
2023-05-16 14:45:19 +00:00
larrybr 08cd1e99fd CLI debug build ready for testing whether OOM handling is well-behaved.
FossilOrigin-Name: 3cec1488f4f1a375d9c97e073a4fe2e2099113e03a88a401a26e9331c783da86
2023-05-15 23:43:26 +00:00
larrybr c7d6066072 Fix CLI memory leak upon loading any shell extension.
FossilOrigin-Name: b91cec479d1b43598863d7b15927054cd089f51a385e86a4e511ffef64f6cfad
2023-05-15 21:33:26 +00:00
larrybr f43776e655 Extend CLI resource manager usage. Improve interface to it for clarity and simplicity.
FossilOrigin-Name: fd379f22926d55d52176b34b20e6dda2cd1218adaaed446e4945c38a5efe0fb1
2023-05-13 16:54:12 +00:00
larrybr 0cb00bbe97 CLI closer to doing full cleanup on error exits. Needs testing with OOM simulation.
FossilOrigin-Name: 8751f93fa505a514d8ab7eae4f9093310ee60b90046f4632e80858001781cb31
2023-05-13 07:23:14 +00:00
larrybr 077ac43551 CLI resource management applied to about 30% of shell source. A few minor flaws fixed. Comments begin to describe OOM responses.
FossilOrigin-Name: ce25a07950e10e5f0c33f179f9b7d307a73b23ad859b5a97e5c6d7bc9e68b254
2023-05-12 21:21:37 +00:00
larrybr 51b3f46de9 More CLI resource manager use. Catch more OOMs. Eliminate some opportunities to lose resources under OOM conditions. Conform to coding guidelines.
FossilOrigin-Name: efdcf1093b4a327da36b5854cff32a8244244302a5f979859c1398e8d191fc6b
2023-05-11 21:47:01 +00:00
larrybr 57ab256d3c For CLI: Cure an old leak and one segfault. Get all shell tests to pass (by altering expected error output in some cases.) Ready now for full use of CLI resource manager.
FossilOrigin-Name: 0f55868e2c51775eaa717564f220acf6ddb2094d358d2011e6736f033e77d8dc
2023-05-11 17:04:15 +00:00
larrybr c92093a868 WIP, CLI resource manager worked in, barely used yet. (Leaking one object.)
FossilOrigin-Name: dbd00bcaa081ec73c2cb6eef0be5f82c890c8293aa90876942bc049e71f3c83a
2023-05-11 03:11:20 +00:00
larrybr 26247e4352 Begin use of a resource manager for CLI.
FossilOrigin-Name: 1527d429d66505d4f4bb8635c4d9d7ab926037b136554605d67835ee5d19d2de
2023-05-10 09:51:51 +00:00
larrybr b2ac62e96c Get TCL extension to build and extensible shell tests to pass.
FossilOrigin-Name: 05ecd8a59ec576d4afe7f8785bdcc052c0887b7a5933783a832895b8c40cdeb9
2023-05-10 00:45:17 +00:00
larrybr 90d7c2a16f Passes shell{1-8}.test .
FossilOrigin-Name: 76734a4b6c61ab004703df40e2197e4f6e5c0b3d5937aed7ea41193ebe2721a1
2023-05-10 00:15:05 +00:00
larrybr 6bbd187ce2 sqlite3x builds too. Fails a few shell2.test TCL tests.
FossilOrigin-Name: 2749aea2086f257f98e58d349ef67d88d355b7d02157cc07e89a1e6b62265572
2023-05-09 21:53:59 +00:00
larrybr 317269e6c1 Made to build (again).
FossilOrigin-Name: 3eb194957c2b21cf017d2909725afdc2a277109c300bc420b8f090653ef986a4
2023-05-09 21:20:34 +00:00
larrybr d6a1c2e0a2 WIP, sync with trunk to pickup 22 weeks of shell library enhancements.
FossilOrigin-Name: 00d0d5b10556bcc4ffb8d6083669134a913bc6ae7fcc787af5b1c292923354e8
2023-05-08 21:35:07 +00:00
larrybr 5f5f3b4713 WIP, pre-sync-to-trunk check-in to capture extensive changes to shell source. (WASM and usual shell tweaks)
FossilOrigin-Name: 3db119c8d754979ceb16253f1b79b645a5bc68b399406cacc4c50a2a71e84e2d
2022-12-18 10:27:43 +00:00
larrybr 29cbcde95a Cause CLI to use ExportHandler interface for its query output, and implement built-in subclasses of it, all in preparation for supporting implementations by shell extensions. (a WIP)
FossilOrigin-Name: 9b37e0be1a416a49671bbfb1a7e62c66f07b3a9ef6b4ce6cf72a135b046675c5
2022-05-05 03:49:35 +00:00
larrybr 9eefed6177 Remove FILE* from shell extension interface.
FossilOrigin-Name: 27ff5ce5170ef5902f15ca8fe4133e41b139e0ef5214f8f5a58d12e852a2b782
2022-05-01 14:26:49 +00:00
larrybr b095d4511b Get sqlite3x.exe and extensions tested with MSVC build.
FossilOrigin-Name: d2b16c29fcbd73c2579aefcfb7042b22a2676e84e815c8ba4bf4b5570eca0d97
2022-05-01 04:55:59 +00:00
larrybr 870380511a For Tcl extension, get Tk option into MSVC build.
FossilOrigin-Name: c3494fa75772077d3ef51bc4d9e7cd673050e6d60c78f1543bcaf9f1a2534915
2022-04-29 21:38:46 +00:00
larrybr 6aae48eced Get MSVC build going for sqlite3x.exe and extensions.
FossilOrigin-Name: cd5e57e0fe4ef2a59c1202b66f4e774f6642fa826cfe3840a786104841dde2f6
2022-04-29 18:36:17 +00:00
larrybr a2842bda96 Get CLI conformed to revised doc for it, and take recent fixes.
FossilOrigin-Name: 60e85c7e7130724a426fab11552ed2aa8e9280f53a219c7e15e0ae0efcaded57
2022-04-24 20:00:15 +00:00
larrybr 3d430307ed Get docs caught up with the .shxload change.
FossilOrigin-Name: e21964480f68152d1c1607f75895354241ec3ea7247f1ee3c2a20c454beec919
2022-04-17 21:03:44 +00:00
larrybr 19c6c50377 Move shell extension load functionality into its own .shxload command for usage and code simplicity.
FossilOrigin-Name: e2c8c88b4b4f2c23c42fb1fdb86c0ade2992ce2e5d7f23b06af0e9ea3eb6a4f9
2022-04-17 19:22:42 +00:00
larrybr 4203750e8e For sqlite3x, rudimentary testing in place as make target shellxtest . More to come.
FossilOrigin-Name: b73928befb013622a369bf8f9904b5aeca626a5598884b4030229a33f684960f
2022-04-17 00:48:49 +00:00
larrybr a07d62a561 Get sqlite3x and extensions testing from Makefile setup, and some of wapptest changes done to run same. (a WIP)
FossilOrigin-Name: d238fcf4beb0b121e4754e288df9906cb61d38a827f1fe38cf4aaa784520fc08
2022-04-15 21:21:01 +00:00
larrybr 34ddefc419 Extension interface cleanup, and rename MetaCommand to DotCommand (to better match docs)
FossilOrigin-Name: f78d7b8b89b667daba486fdb67de105bd8524203cc44bb02c3cb94acd85560e9
2022-04-13 03:48:28 +00:00
larrybr b8302b973c Further clarification of Tcl extension intro.
FossilOrigin-Name: 29ab6e90372d216a0cb9d86861c804d356cd2f11ad835443da08651bec0da398
2022-04-12 10:06:21 +00:00
larrybr e821fcdc8a Tcl extension doc, minor clarifications and spelling improvements
FossilOrigin-Name: 6ccdd9b7519bf8945c5187742c8ea9c335cb6a89b87f6b005bf2ba9dc6890b2f
2022-04-11 23:08:36 +00:00
larrybr 10417f2224 Fix CLI mishandling of SQL "execute" tokens, go and /
FossilOrigin-Name: 1a117b0388d61f72d1f5947c9092ab5b6fffda32c438baadaef3187dc52bd7ca
2022-04-11 23:01:18 +00:00
larrybr d754081c21 Fix mis-renditions of help text.
FossilOrigin-Name: 1a7a6b6f48327fffeff1173a8e437c9989f761ce236c99478f088087a386b155
2022-04-11 18:42:15 +00:00
larrybr dbafc78aa8 Add embedded doc, Intro to Tcl Extension
FossilOrigin-Name: 33591833326e98882e0fa6225a7a55a7e915063a36bf24bb3c17c35e693d972e
2022-04-11 08:07:16 +00:00
larrybr 7c7f3d1435 Create parallel C and C++ test/demo shell extensions, with header tweaks to make that work. Fix a bug exposed when multiple shell extensions were loaded.
FossilOrigin-Name: 2596e7c439b6c4c89921079a78ba6174e99f766b30fab8763be20c1b631e49ec
2022-04-10 09:17:29 +00:00
larrybr 866c30285d Sync w/trunk, for .import fix.
FossilOrigin-Name: 861ab023be283782131df5ccd9eff50cf2f06f48f1ea7defd3f08177a3df5889
2022-04-09 19:39:58 +00:00
larrybr 7ace558fe1 Get tclshext made with Tk, optionally, and make unknown work as in tclsh.
FossilOrigin-Name: 43eb311e517b79cde9e17c1a80baed8d13d9d943dd9ee44b31831159df8715fc
2022-04-09 14:57:17 +00:00
larrybr a6966c5034 Fix TCL extension build issue.
FossilOrigin-Name: 0b3bb0e793f8c7fd79042322b7748a75d17a3c3d0752cfb0f2c544af49b1d6d6
2022-04-09 03:12:40 +00:00
larrybr d1e19b0c5c For shell extensions, generate a single-file header. For TCL extension, provide Makefile target, and make Tk opt-in rather than default. (a WIP)
FossilOrigin-Name: 2cd49df502a7cd250c453cbbd6a2651e588d69dd755bbdf5a55b58715e548760
2022-04-09 02:31:50 +00:00
larrybr 4d3f6ff6bf Sync w/trunk, including all CLI updates
FossilOrigin-Name: ad8b48d6326b253e5d62dd262130820e4e6e56896abf17ddc6f1f7802204503a
2022-04-08 04:24:34 +00:00
larrybr a9f3e717d6 For shell, silence some clang warnings, and cure one bona-fide gripe.
FossilOrigin-Name: 52be86685006ac66cce79a7d8026d671030b308823c6ba427b52377591ba7064
2022-04-08 03:40:05 +00:00
larrybr 6b0f9f3304 Revamp and simplify shell help extension interface, and provide for scripted shell extensions to be seen in .help output.
FossilOrigin-Name: aa785473d948b9f05f32c2107fb302374573a2906d867b4599a7063943277b0f
2022-04-08 03:20:39 +00:00
larrybr b4534f173f Get shell.c past clang preprocessor, silently.
FossilOrigin-Name: 588c3512286491e2128103bcef0d6b3bdd0a0d0dcdc6bdc3e3068db552d70ed4
2022-04-06 17:07:42 +00:00
larrybr 2a9d64214f Add sqlite3x target to other makefiles.
FossilOrigin-Name: efc9f08dcb1d64b92dd08e8776abc805d911fbb905758f8af6977f31a6633bb4
2022-04-06 16:47:56 +00:00
larrybr e5ad1c8a62 Add sqlite3x build target. Cure CLI build warning. Tweak CLI test.
FossilOrigin-Name: 45736d46cdca75d0f6eb1a0b9f2702b86851bd1da85f34c0687a3aaadf628975
2022-04-06 16:19:16 +00:00
larrybr 82a3fe26f0 For shell extension writers, reduce boilerplate (mimicing SQLITE_EXTENSION_INIT# macros)
FossilOrigin-Name: 761208132d99ad602b80c3fe2f0780020c54a3a4ca5be3d2b69b2e3c25e83bc1
2022-04-04 17:27:59 +00:00
larrybr 2dc3acf1fc For TCL extension: Adjust provided "gui" command, and document it.
For shell: Separate shell variables from binding parameters, mainly so they live longer, in the shell DB.
Add .vars dot command to reflect this separation, and specialized for shell variables.
Much code shuffling to share code between .parameters and .vars commands.


FossilOrigin-Name: fa492ff57ca9d89ac623734e8ed0411e29ed6a926c2f534f2a91e41fad994b46
2022-04-04 06:33:43 +00:00
larrybr 0e0720f226 Make CLI build after an incomplete rename completed.
FossilOrigin-Name: 4f11e639f02e6ebe5ec9268cf066b83e6992599c049f497ebed704614b870706
2022-03-31 04:05:48 +00:00
larrybr 32c53ef074 For TCL extension: Add Tk and means to run it, optionally. Implement ScriptSupport interface.
For shell: Get scripting support thru an object interface like the others. Rename OutMode* to Export*.
For both: Provide a way to pass arguments to an extension upon load and get them into TCL's argv.


FossilOrigin-Name: d2446e50805cc3a46f9bcafccf017da60b0cf9fcd27695c214d71d1c5141d03f
2022-03-31 03:45:52 +00:00
larrybr d362752e16 For TCL extension:
Cleanup TCL library interaction and interpreter management.
  Group code into more purpose oriented functions.
  Add .unknown dot command, delegating to TCL commands with "dot" names.
  Drop planned .eval new dot command.
For shell:
  Implement undocumented .unknown dot command, doing little.
  Make dispatcher call .unknown's implementation for unknown dot command.
  Fix a command lookup bug exposed by above change to unknown handling.
  Add .eval dot command.
  Add options to .x command aimed at control of errors in command sequences. (a WIP)
  Make dispatcher report ambiguous dot command lookups, even with no extension.


FossilOrigin-Name: 7616a6f4abe20699e5aa66018233247aa94e3e87f3e6ebe1357f776a18115eb3
2022-03-29 21:11:30 +00:00
larrybr c3a9ba08c1 TCL extension polished and more smoothly integrated. A couple of bugs related to takedown fixed. Help for added features. Code cleaned up and commented.
FossilOrigin-Name: 4ee7df118033af368854992763d96f860f8bd218b28b9ad27dd7ad4291cabfd7
2022-03-28 21:42:16 +00:00
larrybr 1a59843b39 In TCL shell extension, wrap shell DB for use from TCL.
FossilOrigin-Name: 41cc84336bbf7bc64492c24e1bf5be0fccbb8a3db57498070b624af8818d0075
2022-03-28 05:57:07 +00:00
larrybr ef1a2bf778 TCL extension made to use shell's line inputter with prompting and line editing/history.
FossilOrigin-Name: fbf0eb0d12932513bba4c6a6ef31d9972d704ab38690c806098504a4cd67786d
2022-03-27 23:33:43 +00:00
larrybr ccf4a2bc69 In TCL shell extension, add TCL command "udb". This gets the TCL environment to near parity with pre-extended utility, except for query result display (TBD soon).
FossilOrigin-Name: c9aa76bf88401d193a536bc6576405aaad06681504996916b492962d890bc9e0
2022-03-27 03:08:02 +00:00
larrybr c351035606 Sync w/trunk
FossilOrigin-Name: 8402e5e78a83af3ba015bb6ab173350c1b73e04ce37ef933f9a847a7cf5e6157
2022-03-25 20:39:09 +00:00
larrybr 09b8308045 CLI preparation for shell's db to be used from TCL
FossilOrigin-Name: 57501e7fbda84a22cd8ab15beff8af4766a3851aa3c6c705484fe1eab808caa9
2022-03-25 18:27:47 +00:00
larrybr b58d987b42 Get shell TCL extension more smoothly integrated.
FossilOrigin-Name: 29fd246fb2605a2731dd6327a354daedd8182225a97234939c79fc66e1e9e83e
2022-03-25 06:46:20 +00:00
larrybr 26b291fe66 TCL shell extension delegates to dot commands
FossilOrigin-Name: 353943108b36cabb3b9c745aa7f4d9a42cd100b7d0444373184b45cc1715837e
2022-03-24 12:49:05 +00:00
larrybr 7c4d15a128 Scripting support roughed in, with a demo extension. More work is needed to make this truly useful. Tests are missing.
FossilOrigin-Name: abf0316b3f58646974ab8e4d3e68896c9fc03bdd338eb7dc7b2f5d4de7365298
2022-03-23 21:03:48 +00:00
larrybr ff98736baf macro-ize extension boiler-plate, improve exit processing
FossilOrigin-Name: 7996d3a359ab90ef9f42f501e0ddb2efa964a906739116836cb1eafe2a96a0ed
2022-03-19 14:27:57 +00:00
larrybr 00c94befae Further migration to centralized error reporting
FossilOrigin-Name: 898088008e2d15f89941db433f743594aea1351f353c12ce3932a39902dfb161
2022-03-18 16:00:07 +00:00
larrybr 0746356f82 For CLI, begin centralizing argument checking/complaining.
FossilOrigin-Name: 3457f87c5db7a28eb6a47223f7853cd066245edfe1882feabbdb8ce9555f872e
2022-03-15 10:07:56 +00:00
larrybr 82a5e9332b CLI extension's registerMetaCommand done, and used by test_shellext.c (demo code)
FossilOrigin-Name: 9be5e0b503f63dbbf1375460ce74a0aad6f202c2e5b2a748bba1d3af82c7d2d8
2022-03-11 22:59:24 +00:00
larrybr 8526f8738a (WIP) Add DB-dispatch for dot commands upon shell extension load.
FossilOrigin-Name: 19f2a747b8f4d84d8695beb9b23a455f1dead24c94aa90466510555a796027d7
2022-03-08 16:44:55 +00:00
larrybr 1cdb1fa7e3 CLI regularization and code cleanup
FossilOrigin-Name: 16af0e4560349f720d4e2fbb4c08211e6a9a573c1fb4eb287d8bc2aefc3c7ce0
2022-03-08 01:02:37 +00:00
larrybr 5a9d2a497e Sync with trunk
FossilOrigin-Name: 1194093a58978b99c7afee08483d0590ce9aff4647a46dc9a338f022647fdb42
2022-03-07 19:00:25 +00:00
larrybr adbd991fa2 (WIP) Shell meta-command objectification done, ready for dbShell to be loaded and used for extension meta-commands
FossilOrigin-Name: a88983ecb7e2905219898e17d11de3598ee801c57b8ba32c51cb2790456a0e5b
2022-03-06 17:53:09 +00:00
larrybr 961f27a668 (WIP) shell extension load and ShellState internal/external partitioning in place, passing shell tests with trivial exceptions
FossilOrigin-Name: a85679d3e9e1edf71ed6308377783cdaa677c51290f644a28da9a69d429e7be9
2022-03-06 03:22:13 +00:00
larrybr 19d72321fb Merge shell-tweaks enhancements (with 3.38)
FossilOrigin-Name: 4c7d94d3f339d5de98ab5488e75b5dfea2c11fadb2cd44a9f6bc4e0769dbd34a
2022-02-25 00:09:07 +00:00
larrybr a6f9a8d97f Regularize CLI .mode processing
FossilOrigin-Name: 6d0557244f69e78474f9222aae95c4c47925a466aa15e5666d0ada237538e262
2022-02-24 04:29:09 +00:00
larrybr ef7f2c8d88 Sync with 3.38
FossilOrigin-Name: b9b27d74b93ada32ece46c9e53652f766de97f5a77fe4c34251996697374e659
2022-02-23 10:10:59 +00:00
larrybr b524e6741a Sync w/3.38, add .parameter ls to CLI
FossilOrigin-Name: 8c9a5fb26ba045edef1269c5f5e8c8d87fa890b88ddb1121be72514a389a845d
2022-02-22 22:28:37 +00:00
larrybr f7394cf9a9 Sync with trunk
FossilOrigin-Name: 2b4a295c58a908288840157c3e08289af8c1d287f72e5f14b7adfd98065bf241
2022-02-15 17:04:37 +00:00
larrybr 4af7ea5b4c CLI extended parsing and .x features mostly tested
FossilOrigin-Name: 9c664984fd61d5858b436952d876bf3560333ee0edd8e1956cfe74cf9649511d
2022-02-06 14:30:16 +00:00
larrybr 4ef0175d06 Sync with trunk. More shell9.test tests to come.
FossilOrigin-Name: 2e72dc4621bcccd6ef4f01d059ff052bc39b8ea122b006d842be6102736460f4
2022-02-06 00:07:18 +00:00
larrybr cf4fb1ac20 CLI cleanup, mode push/pop, parameter list beautification
FossilOrigin-Name: 53d4582a0e9dab594264a5125324e1fab476e5fd806ebc61bd5fc6ffff10e34b
2022-02-05 23:45:07 +00:00
larrybr ab4251820f Fix CLI Windows build, refine its help, re-org ShellState
FossilOrigin-Name: 991a06a4801270fddea7375d141863dc29fa87b737c728af60c81eea8445a0ea
2022-02-05 15:26:39 +00:00
larrybr 12ee7d9acf CLI .param save/load done; many other small refinements
FossilOrigin-Name: 6d415ac2c10da26e97164cb0846683a57e6a7f3717519c5fe889e077321a2cbd
2022-02-04 21:49:47 +00:00
larrybr 7f43d714ad Fix .param edit unable to edit script vars (bad assert)
FossilOrigin-Name: 62d212d0da93102dd3b7dc4368a0c8acf2469a7f8ef8fcf60e57e69fe028600e
2022-02-04 09:39:30 +00:00
larrybr 2540eccd60 Sync to trunk. Shell tests pass.
FossilOrigin-Name: 79275b81dcd0c7d18b83614f42a0878fb30d93408e599574b61510fc8afa8c80
2022-02-03 21:07:01 +00:00
larrybr 2527e8b2f8 Scripting and .parameter enhancements most in; all in working
FossilOrigin-Name: a1581118b07b111edbad8dc930a959ec0b98461b2737619d9976ef4a498b6571
2022-02-03 20:57:47 +00:00
larrybr 23e68222f5 Take CLI's wordwrap from trunk
FossilOrigin-Name: 768c70a926bd3d5338c4d7c07689e61f03973b4bc7d9d2c62c6f83a6c2b85829
2022-02-01 15:08:10 +00:00
larrybr 9ba2dcf540 Sync to trunk
FossilOrigin-Name: f51a17b6271a8dd7c48725e4ec2df1fde0460866c81c7225dc27216ab389591e
2022-01-31 19:52:30 +00:00
larrybr d7596a2a00 Cleanup dot-command handling, make multi-line work, honor exit requests from more contexts
FossilOrigin-Name: 5cf66e89071b619d116a4707b6ff1cd7917edffe5ab504514f37da433a77b61d
2022-01-31 19:23:32 +00:00
larrybr 8d81c85a76 Sync with trunk
FossilOrigin-Name: 59693d3e732bbfd4855d97c55e91ad3fbc9d22ebd2c073c5debe958e8100c93e
2022-01-30 01:35:58 +00:00
larrybr c8a1bd9567 Enhance shell parser for multi-line dot-commands
FossilOrigin-Name: 5ed528e27b84466f165c0af52028242d95cc54dc53d6bb4d7afcbb081e6e11de
2022-01-30 01:21:12 +00:00
larrybr 63255fda5c Take CLI input redirect recursion limit from trunk
FossilOrigin-Name: 5e7020441514d1febdd6d97d6c5be4429f65310e9bcde2eb50c071aa9459a333
2022-01-24 07:11:25 +00:00
larrybr 442f1b06e8 Add CLI .x command, make string redirection work
FossilOrigin-Name: 2f2f1aaed691ba31ba70577012f785aae1f4a53ac7582bf30d26fbbec1eb3f3c
2022-01-24 04:21:20 +00:00
larrybr 10e2866c99 CLI prepared for reading string input
FossilOrigin-Name: 073ded4d185d0133b07b4548dd78f4ad5eb17ce1f5c457d89fee23281420bbff
2022-01-24 00:22:06 +00:00
larrybr 19d77e2791 Factor out DB protection lift and restore in CLI
FossilOrigin-Name: 4462ed896459ee0312f1989d9a813c65150347ebdd15b59b2aa86bbce3abec1c
2022-01-23 20:54:25 +00:00
larrybr 8a6c538ed3 Shell refinements, seeargs, booleans, undocumented commands
FossilOrigin-Name: 8735caf13fc7e30da2fc5dc90921acbb3109f965c63dbf69fa70ef19c02308c0
2022-01-23 04:19:52 +00:00
larrybr 8a30af3e1d Create new branch named "shell-tweaks"
FossilOrigin-Name: 3f13df296b7d623085d2abb1a84fb9117f79f8e653a35956266da83727eeecba
2022-01-23 02:53:25 +00:00
larrybr b6265f854c A WIP checkin, progress toward what .help promises
FossilOrigin-Name: 4688e6dff88527dbff436b811512206d31a9695095776c4e1fdff95da0b0c4d4
2022-01-20 05:20:27 +00:00
larrybr 96b922020c Initial help changes for .script (and enhanced .parameter)
FossilOrigin-Name: a94ab403eb836d3fcb9710d22da5129f58db05d3be145bc77ce1c017761e7894
2022-01-19 21:11:23 +00:00
larrybr 408f4e998e .ar fixed and constituent line numbers off (until they are right)
FossilOrigin-Name: ec363f81ce775bb3313d08a959e1573f694f18eb4699a33a9b98ce241056292e
2022-01-17 17:22:53 +00:00
larrybr c4e13636a2 Merge from trunk, w/adapations. (.ar still broken)
FossilOrigin-Name: e09a6dd7b9de6c53e1dd59967c3455b38551024bb8d5d1606e7e7324b3b65931
2022-01-17 03:53:37 +00:00
larrybr 3dc9adfcd2 Bring .import --schema enhancement in. Refine mkshellc include logic.
FossilOrigin-Name: e38f45cdc5694dc126bc6a41d3657807fc9953487f4ae26a802a00cbbd12aa61
2022-01-17 03:25:00 +00:00
larrybr 1ef4579896 mkshellc.tcl to convert #include to INCLUDE for in-tree includees
FossilOrigin-Name: b1f3b317c08f467fa8d4cbafdf08ef859b8f84ce371d19c13f87f7a1f2313b98
2022-01-08 21:59:49 +00:00
larrybr 6cd6ec811c Bring in trunk shell mods.
FossilOrigin-Name: f628c3afd0a0527b1db7e3e117afc33a6cb929a507d1e32670e5fd5933fc4833
2022-01-02 00:16:09 +00:00
larrybr 8acc81f100 All shell tests except shell8.test pass. (a WIP)
FossilOrigin-Name: 653db501b46a4035ea1cb2f39862d781fa40fa0442a6abc84d32a9ac29e3af20
2021-12-10 18:11:29 +00:00
larrybr 5fc17a2f98 Pickup trunk .mode upgrade + cosmetic shell changes
FossilOrigin-Name: 8dc69c81b9ad1c45f881f7833efdec736c16d6f542490d14d7f3707d5e0ee1ef
2021-12-09 16:45:03 +00:00
larrybr e1cb8f2639 Closer to passing all Tcl tests (with an odd failure)
FossilOrigin-Name: 6956e989083462b6745276c45edcb03ac7ab9c31518e1f505bfae9e8f1cd2b2f
2021-12-09 04:45:31 +00:00
larrybr ab0866c73a For CLI, extensibility refinement and small steps toward embedability
FossilOrigin-Name: ad9970bcb567822522c7ade3eb037cea34f7063ca2c8aa8255f95efc2d3a151a
2021-12-07 23:02:06 +00:00
larrybr fba56bcec4 Merge .testctrl safe mode blocks
FossilOrigin-Name: a42fc4ac0804673fe05a8e73aa8ea8bbdf468b6857884a716518a8e5d8db1b17
2021-12-06 16:22:17 +00:00
larrybr a8a17ccd4b Add design doc for shell extensibility
FossilOrigin-Name: 96b8ffb05497b6c44f491fbb56d5ff6580b4fea112274f9f19faf24f79727460
2021-12-04 18:40:51 +00:00
larrybr fe1b4b0592 Extension interface tweaks in support of planned usage
FossilOrigin-Name: ce2a91438a3403f55cddc6c5e26db292bf6dd10e805f55416063a63986d28740
2021-12-03 19:27:06 +00:00
larrybr dcd8d2d403 Make shell buildable, fix a potential leak-to-be
FossilOrigin-Name: eab1e1af5b51eb267e9bceaf6412d2be12b8f3deb435631e55291211099720ff
2021-12-02 20:30:35 +00:00
larrybr 23c0ede104 Merge for CLI fix.
FossilOrigin-Name: 98cf18611d4c0ddc7ee38ba2565265ad5bc1c9ceec545f9f8f63987015e64117
2021-12-02 18:09:45 +00:00
larrybr 1df96058be Extensible shell builder changes in prep for actual extension load
FossilOrigin-Name: 00c5af1f7e638de0638aeaa26ecf4eefda16c33fd81bad126909b87fce4c3093
2021-12-01 17:46:53 +00:00
larrybr 7f1d54d69f Merge in 3.37 release
FossilOrigin-Name: 15780cb2bb1e992665a0a7a51206d11d8656d64917d0cf8cc4d72e1005531591
2021-11-30 23:00:23 +00:00
larrybr 016cead2a8 Port recent improvements to extensible shell
FossilOrigin-Name: bfa94f3d1fef1ea855e4fce978cf9c7c3c13becf3f7bc0b8795adc6aebe80d0e
2021-11-30 22:57:14 +00:00
larrybr b89bb8111c Commencing dynamic extensibility transition. (a WIP, may not build)
FossilOrigin-Name: 5ea71afe96ebe32641024aa8324b3ddd73da3ba35de204669130f8136cc1ba85
2021-09-23 17:27:17 +00:00
larrybr f655a244ae Complete shell.c maker's migration to TCL v.8.4
FossilOrigin-Name: e4b9b5b14bcb7b4c19ba47a7d5c74764b9122d36be82f52053cafcee144c581e
2021-09-05 18:45:38 +00:00
larrybr 34b1f2b2b8 Get shell.c maker acceptable to TCL v8.4 and minor tweaks
FossilOrigin-Name: c60f4f90c954eee6a2b644c50aca9a4ed7616d89177fd1c6acb997a362d9abff
2021-09-02 16:29:53 +00:00
larrybr fb05c996a3 Meld -safe option into CLI
FossilOrigin-Name: c7c84998dbec5dfd71d62e12cfa741edba4b6439cc384ab46b21199f290feedd
2021-09-02 01:21:14 +00:00
larrybr c3c68e345a Sync to trunk
FossilOrigin-Name: dd356ace4ffa57518c16fa7fd9b6bc7bef0bb0ddcbc9bdf60ab0a17c25f8e5c0
2021-08-09 19:15:03 +00:00
larrybr ad4e9155ee Sync to trunk
FossilOrigin-Name: d449941b81a796fa30382bd00e88dc744a6745dc3d5a0eb8377aa90e4966a391
2021-07-31 19:37:56 +00:00
larrybr 2345b91f73 Manual merge of new .connection shell command
FossilOrigin-Name: 0d41f7f93c273cd2d0b92269ac1705f66daefa4eb7c24ed8f3662ef45a5397f9
2021-07-26 01:35:47 +00:00
larrybr 52a0e0b740 Much self-doc improvement for shell.c generation. Misleading error message fixed.
FossilOrigin-Name: 7738ce1b2b97fa29125ed8391670cda9343b87a476ad01f4421fea30e0d4dfe1
2021-07-11 12:58:51 +00:00
larrybr b0e5e7b34a .tables fixup for legacy behavior.
FossilOrigin-Name: 827ea61d7d509fb6356aeab9e4fdd7619c1eeb4e8860d71ccb79a91737f9dde9
2021-07-10 14:48:46 +00:00
larrybr e5a5f4bcf9 Migration to dispatched meta-commands done
FossilOrigin-Name: ac4267da196020b41f736b72e8ddc97e8e83be5e97e23caabd107a5a6d832921
2021-07-10 03:42:25 +00:00
larrybr d18d0a0699 General cleanup of mkshellc.tcl, + help and options, decruft
FossilOrigin-Name: 8bdd5fbf127e886d4d8dad2775c37d6591e8e24916250774f04dc8cf5951a8a9
2021-07-07 18:43:01 +00:00
larrybr fcc17dd039 General cleanup of mkshellc.tcl, + help and options
FossilOrigin-Name: d1952ff42b26e66d8df45dcf7ba37378d0818b1db3d6df9352db1e30b9ca844e
2021-07-07 18:23:07 +00:00
larrybr 80d2d6e828 More commands dispathed. Dispatch translation parameterized.
FossilOrigin-Name: 372e3241c9e87144ffb19f7e880e9c75ec80260b0db5aed24474bc2a8d7381e4
2021-07-06 02:07:21 +00:00
larrybr efb3302986 Streamline most common command definition
FossilOrigin-Name: dd76b41a72aca94450fb6f45ff56af95d2adb9275eebe9ca67ebd04a52a63c33
2021-07-04 22:38:20 +00:00
larrybr ab5722c7da Create infrastructure for dynamic shell extension.
FossilOrigin-Name: 5e7e0d4ef8665e924f499238b1469a5fc06d24f6cf96864b502e62734d92e7ee
2021-07-03 19:20:48 +00:00
466 changed files with 24791 additions and 52429 deletions
Regular → Executable
+133 -95
View File
@@ -52,12 +52,23 @@ TCC += @TCL_INCLUDE_SPEC@
# The library that programs using TCL must link against.
#
LIBTCL = @TCL_LIB_SPEC@
# The preprocessing options that are needed for images using the TCL library.
INCTCL = @TCL_INCLUDE_SPEC@
# TCCX is the C Compile and options for use in building shell extensions
# that will run on the target platform. It relies on no in-tree includes,
# using only pre-glommed source/header files in the current directory.
TCCX = ${CC} ${CFLAGS} -fPIC -shared -I.
TCCX += @EXTENSION_DEBUG@
# TCXX is the C++ compile for building a shell extension from a C++ source.
TCXX = ${CXX} ${CFLAGS} -fPIC -shared -I.
TCXX += @EXTENSION_DEBUG@
# Compiler options needed for programs that use the readline() library.
#
READLINE_FLAGS = -DHAVE_READLINE=@TARGET_HAVE_READLINE@ @TARGET_READLINE_INC@
READLINE_FLAGS += -DHAVE_EDITLINE=@TARGET_HAVE_EDITLINE@
READLINE_FLAGS += -DHAVE_LINENOISE=@TARGET_HAVE_LINENOISE@
# The library that programs using readline() must link against.
#
@@ -162,6 +173,9 @@ LTCOMPILE = $(LIBTOOL) --mode=compile --tag=CC $(TCC) $(LTCOMPILE_EXTRAS)
LTLINK = $(LIBTOOL) --mode=link $(TCC) $(LTCOMPILE_EXTRAS) @LDFLAGS@ $(LTLINK_EXTRAS)
LTINSTALL = $(LIBTOOL) --mode=install $(INSTALL)
# Shell and extension source builder tool
MKSHELL_TOOL = $(TCLSH_CMD) $(TOP)/tool/mkshellc.tcl
# You should not have to change anything below this line
###############################################################################
@@ -571,6 +585,11 @@ TESTPROGS = \
dbhash$(TEXE) \
sqltclsh$(TEXE)
TESTPROGS_SHX = \
testfixture$(TEXE) \
sqlite3x$(TEXE) \
shell_extensions
# Databases containing fuzzer test cases
#
FUZZDATA = \
@@ -647,6 +666,9 @@ SQLITE3_SHELL_TARGET_ = sqlite3$(TEXE)
SQLITE3_SHELL_TARGET_1 =
SQLITE3_SHELL_TARGET = $(SQLITE3_SHELL_TARGET_@HAVE_WASI_SDK@)
#
SHELL_OPT_NOEXT = $(SHELL_OPT) -DSHELL_OMIT_EXTENSIONS=7
# This is the default Makefile target. The objects listed here
# are what get build when you type just "make" with no arguments.
#
@@ -671,6 +693,11 @@ libtclsqlite3.la: tclsqlite.lo libsqlite3.la
-avoid-version
sqlite3$(TEXE): shell.c sqlite3.c
$(LTLINK) $(READLINE_FLAGS) $(SHELL_OPT_NOEXT) -o $@ \
shell.c sqlite3.c \
$(LIBREADLINE) $(TLIBS) -rpath "$(libdir)"
sqlite3x$(TEXE): shell.c sqlite3.c
$(LTLINK) $(READLINE_FLAGS) $(SHELL_OPT) -o $@ \
shell.c sqlite3.c \
$(LIBREADLINE) $(TLIBS) -rpath "$(libdir)"
@@ -707,9 +734,6 @@ fuzzcheck$(TEXE): $(FUZZCHECK_SRC) sqlite3.c sqlite3.h $(FUZZCHECK_DEP)
fuzzcheck-asan$(TEXE): $(FUZZCHECK_SRC) sqlite3.c sqlite3.h $(FUZZCHECK_DEP)
$(LTLINK) -o $@ -fsanitize=address $(FUZZCHECK_OPT) $(FUZZCHECK_SRC) sqlite3.c $(TLIBS)
fuzzcheck-ubsan$(TEXE): $(FUZZCHECK_SRC) sqlite3.c sqlite3.h $(FUZZCHECK_DEP)
$(LTLINK) -o $@ -fsanitize=undefined $(FUZZCHECK_OPT) $(FUZZCHECK_SRC) sqlite3.c $(TLIBS)
ossshell$(TEXE): $(TOP)/test/ossfuzz.c $(TOP)/test/ossshell.c sqlite3.c sqlite3.h
$(LTLINK) -o $@ $(FUZZCHECK_OPT) $(TOP)/test/ossshell.c \
$(TOP)/test/ossfuzz.c sqlite3.c $(TLIBS)
@@ -769,22 +793,13 @@ mptest: mptester$(TEXE)
$(MPTEST2) --journalmode DELETE
has_tclsh84:
sh $(TOP)/tool/cktclsh.sh 8.4 $(TCLSH_CMD)
touch has_tclsh84
has_tclsh85:
sh $(TOP)/tool/cktclsh.sh 8.5 $(TCLSH_CMD)
touch has_tclsh85
# This target creates a directory named "tsrc" and fills it with
# copies of all of the C source code and header files needed to
# build on the target system. Some of the C source code and header
# files are automatically generated. This target takes care of
# all that automatic generation.
#
.target_source: $(SRC) $(TOP)/tool/vdbe-compress.tcl has_tclsh84 fts5.c
.target_source: $(SRC) $(TOP)/tool/vdbe-compress.tcl fts5.c
rm -rf tsrc
mkdir tsrc
cp -f $(SRC) tsrc
@@ -794,15 +809,15 @@ has_tclsh85:
cp fts5.c fts5.h tsrc
touch .target_source
sqlite3.c: .target_source $(TOP)/tool/mksqlite3c.tcl src-verify has_tclsh84
sqlite3.c: .target_source $(TOP)/tool/mksqlite3c.tcl src-verify
$(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl $(AMALGAMATION_LINE_MACROS)
cp tsrc/sqlite3ext.h .
cp $(TOP)/ext/session/sqlite3session.h .
sqlite3r.h: sqlite3.h has_tclsh84
sqlite3r.h: sqlite3.h
$(TCLSH_CMD) $(TOP)/tool/mksqlite3h.tcl $(TOP) --enable-recover >sqlite3r.h
sqlite3r.c: sqlite3.c sqlite3r.h has_tclsh84
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/
@@ -817,7 +832,7 @@ tclsqlite3.c: sqlite3.c
echo '#endif /* USE_SYSTEM_SQLITE */' >>tclsqlite3.c
cat $(TOP)/src/tclsqlite.c >>tclsqlite3.c
sqlite3-all.c: sqlite3.c $(TOP)/tool/split-sqlite3c.tcl has_tclsh84
sqlite3-all.c: sqlite3.c $(TOP)/tool/split-sqlite3c.tcl
$(TCLSH_CMD) $(TOP)/tool/split-sqlite3c.tcl
# Rule to build the amalgamation
@@ -1105,10 +1120,10 @@ tclsqlite3$(TEXE): tclsqlite-shell.lo libsqlite3.la
# Rules to build opcodes.c and opcodes.h
#
opcodes.c: opcodes.h $(TOP)/tool/mkopcodec.tcl has_tclsh84
opcodes.c: opcodes.h $(TOP)/tool/mkopcodec.tcl
$(TCLSH_CMD) $(TOP)/tool/mkopcodec.tcl opcodes.h >opcodes.c
opcodes.h: parse.h $(TOP)/src/vdbe.c $(TOP)/tool/mkopcodeh.tcl has_tclsh84
opcodes.h: parse.h $(TOP)/src/vdbe.c $(TOP)/tool/mkopcodeh.tcl
cat parse.h $(TOP)/src/vdbe.c | $(TCLSH_CMD) $(TOP)/tool/mkopcodeh.tcl >opcodes.h
# Rules to build parse.c and parse.h - the outputs of lemon.
@@ -1119,10 +1134,10 @@ parse.c: $(TOP)/src/parse.y lemon$(BEXE)
cp $(TOP)/src/parse.y .
./lemon$(BEXE) $(OPT_FEATURE_FLAGS) $(OPTS) -S parse.y
sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest mksourceid$(BEXE) $(TOP)/VERSION has_tclsh84
sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest mksourceid$(BEXE) $(TOP)/VERSION
$(TCLSH_CMD) $(TOP)/tool/mksqlite3h.tcl $(TOP) >sqlite3.h
sqlite3rc.h: $(TOP)/src/sqlite3.rc $(TOP)/VERSION has_tclsh84
sqlite3rc.h: $(TOP)/src/sqlite3.rc $(TOP)/VERSION
echo '#ifndef SQLITE_RESOURCE_VERSION' >$@
echo -n '#define SQLITE_RESOURCE_VERSION ' >>$@
cat $(TOP)/VERSION | $(TCLSH_CMD) $(TOP)/tool/replace.tcl exact . , >>$@
@@ -1132,37 +1147,36 @@ keywordhash.h: $(TOP)/tool/mkkeywordhash.c
$(BCC) -o mkkeywordhash$(BEXE) $(OPT_FEATURE_FLAGS) $(OPTS) $(TOP)/tool/mkkeywordhash.c
./mkkeywordhash$(BEXE) >keywordhash.h
$(TOP)/ext/misc/basexx.c: $(TOP)/ext/misc/base64.c $(TOP)/ext/misc/base85.c
# Source files that go into making shell.c
SHELL_SRC = \
$(TOP)/src/shell.c.in \
$(TOP)/ext/misc/appendvfs.c \
$(TOP)/ext/misc/completion.c \
$(TOP)/ext/misc/decimal.c \
$(TOP)/ext/misc/basexx.c \
$(TOP)/ext/misc/base64.c \
$(TOP)/ext/misc/base85.c \
$(TOP)/ext/misc/fileio.c \
$(TOP)/ext/misc/ieee754.c \
$(TOP)/ext/misc/regexp.c \
$(TOP)/ext/misc/series.c \
$(TOP)/ext/misc/shathree.c \
$(TOP)/ext/misc/sqlar.c \
$(TOP)/ext/misc/uint.c \
$(TOP)/ext/expert/sqlite3expert.c \
$(TOP)/ext/expert/sqlite3expert.h \
$(TOP)/ext/misc/zipfile.c \
$(TOP)/ext/misc/memtrace.c \
$(TOP)/ext/misc/pcachetrace.c \
$(TOP)/ext/recover/dbdata.c \
$(TOP)/ext/recover/sqlite3recover.c \
$(TOP)/ext/recover/sqlite3recover.h \
$(TOP)/src/test_windirent.c
shell.c: $(SHELL_SRC) $(TOP)/tool/mkshellc.tcl has_tclsh84
$(TCLSH_CMD) $(TOP)/tool/mkshellc.tcl >shell.c
$(TOP)/src/shell.c.in \
$(TOP)/ext/expert/sqlite3expert.c \
$(TOP)/ext/expert/sqlite3expert.h \
$(TOP)/ext/misc/appendvfs.c \
$(TOP)/ext/misc/basexx.c \
$(TOP)/ext/misc/completion.c \
$(TOP)/ext/misc/decimal.c \
$(TOP)/ext/misc/fileio.c \
$(TOP)/ext/misc/ieee754.c \
$(TOP)/ext/misc/regexp.c \
$(TOP)/ext/misc/series.c \
$(TOP)/ext/misc/shathree.c \
$(TOP)/ext/misc/sqlar.c \
$(TOP)/ext/misc/uint.c \
$(TOP)/ext/misc/memtrace.c \
$(TOP)/ext/misc/pcachetrace.c \
$(TOP)/src/shext_linkage.h \
$(TOP)/src/obj_interfaces.h \
$(TOP)/src/resmanage.h \
$(TOP)/src/resmanage.c \
$(TOP)/ext/recover/dbdata.c \
$(TOP)/ext/misc/zipfile.c \
$(TOP)/src/test_windirent.c
shell.c: $(SHELL_SRC) $(TOP)/tool/mkshellc.tcl
$(MKSHELL_TOOL) >shell.c
# Rules to build the extension objects.
#
@@ -1246,7 +1260,7 @@ fts5parse.c: $(TOP)/ext/fts5/fts5parse.y lemon$(BEXE)
fts5parse.h: fts5parse.c
fts5.c: $(FTS5_SRC) has_tclsh84
fts5.c: $(FTS5_SRC)
$(TCLSH_CMD) $(TOP)/ext/fts5/tool/mkfts5c.tcl
cp $(TOP)/ext/fts5/fts5.h .
@@ -1256,6 +1270,54 @@ fts5.lo: fts5.c $(HDR) $(EXTHDR)
sqlite3rbu.lo: $(TOP)/ext/rbu/sqlite3rbu.c $(HDR) $(EXTHDR)
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/rbu/sqlite3rbu.c
# Rule to build shx_link.h (for use by shell extensions)
SHX_LINK_SRC = \
$(TOP)/src/shext_linkage.h \
$(TOP)/src/obj_interfaces.h \
sqlite3ext.h \
sqlite3.h
shx_link.h: $(SHX_LINK_SRC)
$(MKSHELL_TOOL) -short-head -header-gen \
$(TOP)/src/shext_linkage.h > $@
# Rules to build shell extensions used for testing extensible shell
shell_extensions: tcl_shell_extension \
test_shellext_cpp$(SHLIB_SUFFIX) test_shellext_c$(SHLIB_SUFFIX)
test_shellext_cpp$(SHLIB_SUFFIX): $(TOP)/src/test_shellext_cpp.cpp \
$(TOP)/shx_link.h
$(TCXX) $(TOP)/src/test_shellext_cpp.cpp -o $@
test_shellext_c$(SHLIB_SUFFIX): $(TOP)/src/test_shellext_c.c \
$(TOP)/shx_link.h
$(TCCX) $(TOP)/src/test_shellext_c.c -o $@
# Rules to build the TCL shell extension (tclshext.{so,dll,dylib})
tcl_shell_extension: tclshext$(SHLIB_SUFFIX)
# This is a work-around for building Tk variant. Ultimately, the configure
# script will find the Tk library with the same version as the Tcl library.
# This has the same effect with the lib naming now used by the Tcl project,
# but, if it will fail, does so when make rather than configure is run.
TCL_LIBS = $(LIBTCL)
ifdef WITH_TK
ifneq ($(WITH_TK),0)
TCLEXT_OPTS += -DSHELL_ENABLE_TK
LIBTK := $(patsubst -ltcl%,-ltk%,$(LIBTCL))
TCL_LIBS += $(LIBTK)
endif
endif
tclshext.c: $(TOP)/ext/misc/tclshext.c.in $(TOP)/src/tclsqlite.c
$(MKSHELL_TOOL) $(TOP)/ext/misc/tclshext.c.in > $@
tclshext$(SHLIB_SUFFIX): tclshext.c shx_link.h
$(TCCX) $(INCTCL) $(TCLEXT_OPTS) tclshext.c \
-o $@ $(TCL_LIBS) $(TCLEXT_LDOPTS)
# Rules to build the 'testfixture' application.
#
@@ -1280,7 +1342,7 @@ TESTFIXTURE_SRC1 = sqlite3.c
TESTFIXTURE_SRC = $(TESTSRC) $(TOP)/src/tclsqlite.c
TESTFIXTURE_SRC += $(TESTFIXTURE_SRC$(USE_AMALGAMATION))
testfixture$(TEXE): has_tclsh85 $(TESTFIXTURE_SRC)
testfixture$(TEXE): $(TESTFIXTURE_SRC)
$(LTLINK) -DSQLITE_NO_SYNC=1 $(TEMP_STORE) $(TESTFIXTURE_FLAGS) \
-o $@ $(TESTFIXTURE_SRC) $(LIBTCL) $(TLIBS)
@@ -1304,17 +1366,11 @@ fulltestonly: $(TESTPROGS) fuzztest
./testfixture$(TEXE) $(TOP)/test/full.test
# Fuzz testing
#
# WARNING: When the "fuzztest" target is run by the testrunner.tcl script,
# it does not actually run this code. Instead, it schedules equivalent
# commands. Therefore, if this target is updated, then code in
# testrunner_data.tcl (search for "trd_fuzztest_data") must also be updated.
#
fuzztest: fuzzcheck$(TEXE) $(FUZZDATA) sessionfuzz$(TEXE)
fuzztest: fuzzcheck$(TEXE) $(FUZZDATA) sessionfuzz$(TEXE) $(TOP)/test/sessionfuzz-data1.db
./fuzzcheck$(TEXE) $(FUZZDATA)
./sessionfuzz$(TEXE) run $(TOP)/test/sessionfuzz-data1.db
valgrindfuzz: fuzzcheck$(TEXT) $(FUZZDATA) sessionfuzz$(TEXE)
valgrindfuzz: fuzzcheck$(TEXT) $(FUZZDATA) sessionfuzz$(TEXE) $(TOP)/test/sessionfuzz-data1.db
valgrind ./fuzzcheck$(TEXE) --cell-size-check --limit-mem 10M $(FUZZDATA)
valgrind ./sessionfuzz$(TEXE) run $(TOP)/test/sessionfuzz-data1.db
@@ -1331,23 +1387,11 @@ testrunner: testfixture$(TEXE)
# Runs both fuzztest and testrunner, consecutively.
#
devtest: srctree-check testfixture$(TEXE) fuzztest testrunner
mdevtest: srctree-check has_tclsh85
$(TCLSH_CMD) $(TOP)/test/testrunner.tcl mdevtest
sdevtest: has_tclsh85
$(TCLSH_CMD) $(TOP)/test/testrunner.tcl sdevtest
# Validate that various generated files in the source tree
# are up-to-date.
#
srctree-check: $(TOP)/tool/srctree-check.tcl
$(TCLSH_CMD) $(TOP)/tool/srctree-check.tcl
devtest: testfixture$(TEXE) fuzztest testrunner
# Testing for a release
#
releasetest: srctree-check testfixture$(TEXE)
releasetest: testfixture$(TEXE)
./testfixture$(TEXE) $(TOP)/test/testrunner.tcl release
# Minimal testing that runs in less than 3 minutes
@@ -1358,7 +1402,7 @@ quicktest: ./testfixture$(TEXE)
# This is the common case. Run many tests that do not take too long,
# including fuzzcheck, sqlite3_analyzer, and sqldiff tests.
#
test: srctree-check fuzztest sourcetest $(TESTPROGS) tcltest
test: fuzztest sourcetest $(TESTPROGS) tcltest
# Run a test using valgrind. This can take a really long time
# because valgrind is so much slower than a native machine.
@@ -1373,16 +1417,23 @@ valgrindtest: $(TESTPROGS) valgrindfuzz
smoketest: $(TESTPROGS) fuzzcheck$(TEXE)
./testfixture$(TEXE) $(TOP)/test/main.test $(TESTOPTS)
shelltest: $(TESTPROGS)
./testfixture$(TEXT) $(TOP)/test/permutations.test shell
TESTRUNS_SHX = \
$(TOP)/test/shell_x/shell9.test \
$(TOP)/test/shell_x/shell10.test
sqlite3_analyzer.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqlite3_analyzer.c.in has_tclsh85
shellxtest: $(TESTPROGS_SHX)
./testfixture$(TEXE) $(TESTRUNS_SHX)
shelltest: $(TESTPROGS)
./testfixture$(TEXE) $(TOP)/test/permutations.test shell
sqlite3_analyzer.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqlite3_analyzer.c.in
$(TCLSH_CMD) $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqlite3_analyzer.c.in >sqlite3_analyzer.c
sqlite3_analyzer$(TEXE): sqlite3_analyzer.c
$(LTLINK) sqlite3_analyzer.c -o $@ $(LIBTCL) $(TLIBS)
sqltclsh.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/sqltclsh.tcl $(TOP)/ext/misc/appendvfs.c $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqltclsh.c.in has_tclsh85
sqltclsh.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/sqltclsh.tcl $(TOP)/ext/misc/appendvfs.c $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqltclsh.c.in
$(TCLSH_CMD) $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqltclsh.c.in >sqltclsh.c
sqltclsh$(TEXE): sqltclsh.c
@@ -1401,7 +1452,7 @@ CHECKER_DEPS =\
$(TOP)/ext/misc/btreeinfo.c \
$(TOP)/ext/repair/sqlite3_checker.c.in
sqlite3_checker.c: $(CHECKER_DEPS) has_tclsh85
sqlite3_checker.c: $(CHECKER_DEPS)
$(TCLSH_CMD) $(TOP)/tool/mkccode.tcl $(TOP)/ext/repair/sqlite3_checker.c.in >$@
sqlite3_checker$(TEXE): sqlite3_checker.c
@@ -1487,11 +1538,6 @@ amalgamation-tarball: sqlite3.c sqlite3rc.h
snapshot-tarball: sqlite3.c sqlite3rc.h
TOP=$(TOP) sh $(TOP)/tool/mkautoconfamal.sh --snapshot
# Build a ZIP archive containing various command-line tools.
#
tool-zip: testfixture sqlite3 sqldiff sqlite3_analyzer $(TOP)/tool/mktoolzip.tcl
./testfixture $(TOP)/tool/mktoolzip.tcl
# The next two rules are used to support the "threadtest" target. Building
# threadtest runs a few thread-safety tests that are implemented in C. This
# target is invoked by the releasetest.tcl script.
@@ -1549,11 +1595,11 @@ clean:
rm -f LogEst$(TEXE) fts3view$(TEXE) rollback-test$(TEXE) showdb$(TEXE)
rm -f showjournal$(TEXE) showstat4$(TEXE) showwal$(TEXE) speedtest1$(TEXE)
rm -f wordcount$(TEXE) changeset$(TEXE)
rm -f version-info$(TEXT)
rm -f sqlite3.dll sqlite3.lib sqlite3.exp sqlite3.def
rm -f sqlite3.c
rm -f sqlite3rc.h
rm -f shell.c sqlite3ext.h
rm -f shx_link.h tclshext.c tclshext$(SHLIB_SUFFIX)
rm -f sqlite3_analyzer$(TEXE) sqlite3_analyzer.c
rm -f sqlite-*-output.vsix
rm -f mptester mptester.exe
@@ -1567,7 +1613,6 @@ clean:
rm -f threadtest5
rm -f src-verify
rm -f custom.rws
rm -f has_tclsh84 has_tclsh85
distclean: clean
rm -f sqlite_cfg.h config.log config.status libtool Makefile sqlite3.pc \
@@ -1612,12 +1657,5 @@ fiddle: sqlite3.c shell.c
@echo 'Updating custom dictionary from tool/custom.txt'
aspell --lang=en create master ./custom.rws < $<
misspell: ./custom.rws has_tclsh84
misspell: ./custom.rws
$(TCLSH_CMD) ./tool/spellsift.tcl ./src/*.c ./src/*.h ./src/*.in
#
# tool/version-info: a utility for emitting sqlite3 version info
# in various forms.
#
version-info$(TEXE): $(TOP)/tool/version-info.c Makefile sqlite3.h
$(LTLINK) $(ST_OPT) -o $@ $(TOP)/tool/version-info.c
+6
View File
@@ -22,6 +22,12 @@ TOP = ../sqlite
BCC = gcc -g -O0
#BCC = /opt/ancic/bin/c89 -0
#### If the target operating system supports the "usleep()" system
# call, then define the HAVE_USLEEP macro for all C modules.
#
#USLEEP =
USLEEP = -DHAVE_USLEEP=1
#### If you want the SQLite library to be safe for use within a
# multi-threaded program, then define the following macro
# appropriately:
Regular → Executable
+94 -62
View File
@@ -52,13 +52,6 @@ MINIMAL_AMALGAMATION = 0
USE_STDCALL = 0
!ENDIF
# Use the USE_SEH=0 option on the nmake command line to omit structured
# exception handling (SEH) support. SEH is on by default.
#
!IFNDEF USE_SEH
USE_SEH = 1
!ENDIF
# Set this non-0 to have the shell executable link against the core dynamic
# link library.
#
@@ -225,12 +218,6 @@ WIN32HEAP = 0
OSTRACE = 0
!ENDIF
# enable address sanitizer using ASAN=1 on the command-line.
#
!IFNDEF ASAN
ASAN = 0
!ENDIF
# Set this to one of the following values to enable various debugging
# features. Each level includes the debugging options from the previous
# levels. Currently, the recognized values for DEBUG are:
@@ -324,6 +311,7 @@ SQLITE3EXE = winsqlite3shell.exe
SQLITE3EXE = sqlite3.exe
!ENDIF
!ENDIF
SQLITE3XEXE = sqlite3x.exe
# This is the argument used to set the program database (PDB) file for the
# SQLite shell executable (EXE).
@@ -374,7 +362,6 @@ SQLITE_TCL_DEP =
!IFNDEF OPT_FEATURE_FLAGS
!IF $(MINIMAL_AMALGAMATION)==0
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS3=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS5=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RTREE=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_GEOPOLY=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_STMTVTAB=1
@@ -403,14 +390,6 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_MATH_FUNCTIONS
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RBU=1
!ENDIF
# Should structured exception handling (SEH) be enabled for WAL mode in
# the core library? It is on by default. Only omit it if the
# USE_SEH=0 option is provided on the nmake command-line.
#
!IF $(USE_SEH)==0
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_OMIT_SEH=1
!ENDIF
# These are the "extended" SQLite compilation options used when compiling for
# the Windows 10 platform.
#
@@ -899,13 +878,6 @@ RCC = $(RCC) -DSQLITE_WIN32_MALLOC_VALIDATE=1
!ENDIF
!ENDIF
# Address sanitizer if ASAN=1
#
!IF $(ASAN)>0
TCC = $(TCC) /fsanitize=address
!ENDIF
# <<mark>>
# The locations of the Tcl header and library files. Also, the library that
# non-stubs enabled programs using Tcl must link against. These variables
@@ -937,6 +909,14 @@ TCLLIBDIR = $(TCLDIR)\lib
LIBTCL = tcl$(TCLVERSION)$(TCLSUFFIX).lib
!ENDIF
# For Tcl shell extension only:
!IFDEF WITH_TK
!IFNDEF LIBTK
LIBTK = tk$(TCLVERSION)$(TCLSUFFIX).lib
!ENDIF
!ENDIF
!IFNDEF LIBTCLSTUB
LIBTCLSTUB = tclstub$(TCLVERSION)$(TCLSUFFIX).lib
!ENDIF
@@ -1594,7 +1574,8 @@ TESTEXT = \
$(TOP)\ext\rtree\test_rtreedoc.c \
$(TOP)\ext\recover\sqlite3recover.c \
$(TOP)\ext\recover\test_recover.c \
$(TOP)\ext\recover\dbdata.c
$(TOP)\ext\recover\dbdata.c \
fts5.c
# If use of zlib is enabled, add the "zipfile.c" source file.
#
@@ -1610,8 +1591,7 @@ TESTSRC2 = \
$(SRC01) \
$(SRC07) \
$(SRC10) \
$(TOP)\ext\async\sqlite3async.c \
fts5.c
$(TOP)\ext\async\sqlite3async.c
# Header files used by all library source files.
#
@@ -1669,6 +1649,11 @@ TESTPROGS = \
dbhash.exe \
sqltclsh.exe
TESTPROGS_SHX = \
testfixture.exe \
$(SQLITE3XEXE) \
shell_extensions
# Databases containing fuzzer test cases
#
FUZZDATA = \
@@ -1690,10 +1675,11 @@ SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_DQS=0
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_FTS4=1
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS=1
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_OFFSET_SQL_FUNC=1
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION=1
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_STMT_SCANSTATUS=1
!ENDIF
#
SHELL_NOEXT_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSHELL_OMIT_EXTENSIONS=7
# <<mark>>
# Extra compiler options for various test tools.
#
@@ -1819,12 +1805,16 @@ sqlite3.def: libsqlite3.lib
# <</block2>>
$(SQLITE3EXE): shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLITE3H)
$(LTLINK) $(SHELL_NOEXT_COMPILE_OPTS) $(READLINE_FLAGS) shell.c $(SHELL_CORE_SRC) \
/link $(SQLITE3EXEPDB) $(LDFLAGS) $(LTLINKOPTS) $(SHELL_LINK_OPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS)
$(SQLITE3XEXE): shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLITE3H)
$(LTLINK) $(SHELL_COMPILE_OPTS) $(READLINE_FLAGS) shell.c $(SHELL_CORE_SRC) \
/link $(SQLITE3EXEPDB) $(LDFLAGS) $(LTLINKOPTS) $(SHELL_LINK_OPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS)
# <<mark>>
sqldiff.exe: $(TOP)\tool\sqldiff.c $(SQLITE3C) $(SQLITE3H) $(LIBRESOBJS)
$(LTLINK) $(NO_WARN) $(TOP)\tool\sqldiff.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) $(LIBRESOBJS)
sqldiff.exe: $(TOP)\tool\sqldiff.c $(SQLITE3C) $(SQLITE3H)
$(LTLINK) $(NO_WARN) $(TOP)\tool\sqldiff.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
dbhash.exe: $(TOP)\tool\dbhash.c $(SQLITE3C) $(SQLITE3H)
$(LTLINK) $(NO_WARN) $(TOP)\tool\dbhash.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
@@ -2260,26 +2250,28 @@ keywordhash.h: $(TOP)\tool\mkkeywordhash.c mkkeywordhash.exe
# Source files that go into making shell.c
SHELL_SRC = \
$(TOP)\src\shell.c.in \
$(TOP)\ext\misc\appendvfs.c \
$(TOP)\ext\misc\completion.c \
$(TOP)\ext\misc\base64.c \
$(TOP)\ext\misc\base85.c \
$(TOP)\ext\misc\decimal.c \
$(TOP)\ext\misc\fileio.c \
$(TOP)\ext\misc\ieee754.c \
$(TOP)\ext\misc\regexp.c \
$(TOP)\ext\misc\series.c \
$(TOP)\ext\misc\shathree.c \
$(TOP)\ext\misc\uint.c \
$(TOP)\ext\expert\sqlite3expert.c \
$(TOP)\ext\expert\sqlite3expert.h \
$(TOP)\ext\misc\memtrace.c \
$(TOP)\ext\misc\pcachetrace.c \
$(TOP)\ext\recover\dbdata.c \
$(TOP)\ext\recover\sqlite3recover.c \
$(TOP)\ext\recover\sqlite3recover.h \
$(TOP)\src\test_windirent.c
$(TOP)\src\shell.c.in \
$(TOP)\ext\misc\appendvfs.c \
$(TOP)\ext\misc\completion.c \
$(TOP)\ext\misc\base64.c \
$(TOP)\ext\misc\base85.c \
$(TOP)\ext\misc\decimal.c \
$(TOP)\ext\misc\fileio.c \
$(TOP)\ext\misc\ieee754.c \
$(TOP)\ext\misc\regexp.c \
$(TOP)\ext\misc\series.c \
$(TOP)\ext\misc\shathree.c \
$(TOP)\ext\misc\uint.c \
$(TOP)\ext\expert\sqlite3expert.c \
$(TOP)\ext\expert\sqlite3expert.h \
$(TOP)\ext\misc\memtrace.c \
$(TOP)\ext\misc\pcachetrace.c \
$(TOP)\ext\recover\dbdata.c \
$(TOP)\ext\recover\sqlite3recover.c \
$(TOP)\ext\recover\sqlite3recover.h \
$(TOP)\src\resmanage.h \
$(TOP)\src\resmanage.c \
$(TOP)\src\test_windirent.c
# If use of zlib is enabled, add the "zipfile.c" source file.
#
@@ -2414,6 +2406,45 @@ fts5.dll: fts5_ext.lo
sqlite3rbu.lo: $(TOP)\ext\rbu\sqlite3rbu.c $(HDR) $(EXTHDR)
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)\ext\rbu\sqlite3rbu.c
# Rule to build shx_link.h (for use by shell extensions)
SHX_LINK_SRC = \
$(TOP)/src/shext_linkage.h \
$(TOP)/src/obj_interfaces.h \
sqlite3ext.h \
sqlite3.h
shx_link.h: $(SHX_LINK_SRC)
$(TCLSH_CMD) $(TOP)\tool\mkshellc.tcl -short-head -header-gen \
$(TOP)/src/shext_linkage.h > $@
# Rules to build shell extensions used for testing extensible shell
shell_extensions: tcl_shell_extension \
test_shellext_cpp.dll test_shellext_c.dll
test_shellext_cpp.dll: $(TOP)/src/test_shellext_cpp.cpp $(TOP)/shx_link.h
$(TCC) $(TOP)/src/test_shellext_cpp.cpp -LD -Fe$@
test_shellext_c.dll: $(TOP)/src/test_shellext_c.c $(TOP)/shx_link.h
$(TCC) $(TOP)/src/test_shellext_c.c -LD -Fe$@
tcl_shell_extension: tclshext.dll
!IFDEF WITH_TK
TCLEXT_OPTS=-DSHELL_ENABLE_TK
TCLEXT_LIBS = $(TCLLIBS) $(LIBTK)
!ELSE
TCLEXT_OPTS=
TCLEXT_LIBS = $(TCLLIBS)
!ENDIF
tclshext.c: $(TOP)/ext/misc/tclshext.c.in $(TOP)/src/tclsqlite.c
$(TCLSH_CMD) $(TOP)\tool\mkshellc.tcl $(TOP)/ext/misc/tclshext.c.in > $@
tclshext.dll: tclshext.c shx_link.h
$(TCC) -I$(TCLINCDIR) $(TCLEXT_OPTS) tclshext.c -Fe$@ -link -DLL \
-LIBPATH:$(TCLLIBDIR) $(TCLEXT_LIBS)
# Rules to build the 'testfixture' application.
#
# If using the amalgamation, use sqlite3.c directly to build the test
@@ -2470,9 +2501,6 @@ extensiontest: testfixture.exe testloadext.dll
@set PATH=$(LIBTCLPATH);$(PATH)
.\testfixture.exe $(TOP)\test\loadext.test $(TESTOPTS)
tool-zip: testfixture.exe sqlite3.exe sqldiff.exe sqlite3_analyzer.exe $(TOP)\tool\mktoolzip.tcl
.\testfixture.exe $(TOP)\tool\mktoolzip.tcl
coretestprogs: $(TESTPROGS)
testprogs: coretestprogs srcck1.exe fuzzcheck.exe sessionfuzz.exe
@@ -2525,9 +2553,6 @@ testrunner: testfixture.exe
#
devtest: testfixture.exe fuzztest testrunner
mdevtest:
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl mdevtest
# Testing for a release
#
releasetest: testfixture.exe fuzztest
@@ -2538,6 +2563,13 @@ smoketest: $(TESTPROGS)
@set PATH=$(LIBTCLPATH);$(PATH)
.\testfixture.exe $(TOP)\test\main.test $(TESTOPTS)
TESTRUNS_SHX = \
$(TOP)/test/shell_x/shell9.test \
$(TOP)/test/shell_x/shell10.test
shellxtest: $(TESTPROGS_SHX)
.\testfixture.exe $(TESTRUNS_SHX)
shelltest: $(TESTPROGS)
.\testfixture.exe $(TOP)\test\permutations.test shell
Regular → Executable
+27 -27
View File
@@ -41,10 +41,11 @@ email to drh at sqlite dot org.
The SQLite source code is in the public domain. See
<https://sqlite.org/copyright.html> for details.
Because SQLite is in the public domain, we do not normally accept pull
requests, because if we did take a pull request, the changes in that
pull request might carry a copyright and the SQLite source code would
then no longer be fully in the public domain.
Because SQLite is in the public domain,
we cannot accept pull requests, because
if we did accept a pull request, the changes in that pull request would
carry a copyright and the SQLite source code would no longer be fully in
the public domain.
## Obtaining The SQLite Source Code
@@ -103,9 +104,9 @@ For example:
mkdir bld ;# Build will occur in a sibling directory
cd bld ;# Change to the build directory
../sqlite/configure ;# Run the configure script
make ;# Builds the "sqlite3" command-line tool
make ;# Run the makefile.
make sqlite3.c ;# Build the "amalgamation" source file
make devtest ;# Run some tests (requires Tcl)
make test ;# Run some tests (requires Tcl)
See the makefile for additional targets.
@@ -115,33 +116,32 @@ script does not work out for you, there is a generic makefile named
can copy and edit to suit your needs. Comments on the generic makefile
show what changes are needed.
## Compiling for Windows Using MSVC
## Using MSVC for Windows systems
On Windows, all applicable build products can be compiled with MSVC.
You will also need a working installation of TCL.
See the [compile-for-windows.md](doc/compile-for-windows.md) document for
additional information about how to install MSVC and TCL and configure your
build environment.
First open the command prompt window associated with the desired compiler
version (e.g. "Developer Command Prompt for VS2013"). Next, use NMAKE
with the provided "Makefile.msc" to build one of the supported targets.
If you want to run tests, you need to let SQLite know the location of your
TCL library, using a command like this:
For example, from the parent directory of the source subtree named "sqlite":
set TCLDIR=c:\Tcl
mkdir bld
cd bld
nmake /f ..\sqlite\Makefile.msc TOP=..\sqlite
nmake /f ..\sqlite\Makefile.msc sqlite3.c TOP=..\sqlite
nmake /f ..\sqlite\Makefile.msc sqlite3.dll TOP=..\sqlite
nmake /f ..\sqlite\Makefile.msc sqlite3.exe TOP=..\sqlite
nmake /f ..\sqlite\Makefile.msc test TOP=..\sqlite
SQLite uses "tclsh.exe" as part of the build process, and so that utility
program will need to be somewhere on your %PATH%. The finished SQLite library
does not contain any TCL code, but it does use TCL to help with the build process
and to run tests.
There are several build options that can be set via the NMAKE command
line. For example, to build for WinRT, simply add "FOR_WINRT=1" argument
to the "sqlite3.dll" command line above. When debugging into the SQLite
code, adding the "DEBUG=1" argument to one of the above command lines is
recommended.
Build using Makefile.msc. Example:
nmake /f Makefile.msc
nmake /f Makefile.msc sqlite3.c
nmake /f Makefile.msc devtest
nmake /f Makefile.msc releasetest
There are many other makefile targets. See comments in Makefile.msc for
details.
SQLite does not require [Tcl](http://www.tcl.tk/) to run, but a Tcl installation
is required by the makefiles (including those for MSVC). SQLite contains
a lot of generated code and Tcl is used to do much of that code generation.
## Source Code Tour
Regular → Executable
+1 -1
View File
@@ -1 +1 @@
3.45.0
3.43.0
+1 -1
View File
@@ -9,7 +9,7 @@ sqlite3_SOURCES = shell.c sqlite3.h
EXTRA_sqlite3_SOURCES = sqlite3.c
sqlite3_LDADD = @EXTRA_SHELL_OBJ@ @READLINE_LIBS@
sqlite3_DEPENDENCIES = @EXTRA_SHELL_OBJ@
sqlite3_CFLAGS = $(AM_CFLAGS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_DQS=0 -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_ENABLE_STMTVTAB -DSQLITE_ENABLE_DBSTAT_VTAB $(SHELL_CFLAGS)
sqlite3_CFLAGS = $(AM_CFLAGS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_ENABLE_STMTVTAB -DSQLITE_ENABLE_DBSTAT_VTAB $(SHELL_CFLAGS)
include_HEADERS = sqlite3.h sqlite3ext.h
-31
View File
@@ -52,13 +52,6 @@ MINIMAL_AMALGAMATION = 0
USE_STDCALL = 0
!ENDIF
# Use the USE_SEH=0 option on the nmake command line to omit structured
# exception handling (SEH) support. SEH is on by default.
#
!IFNDEF USE_SEH
USE_SEH = 1
!ENDIF
# Set this non-0 to have the shell executable link against the core dynamic
# link library.
#
@@ -187,12 +180,6 @@ WIN32HEAP = 0
OSTRACE = 0
!ENDIF
# enable address sanitizer using ASAN=1 on the command-line.
#
!IFNDEF ASAN
ASAN = 0
!ENDIF
# Set this to one of the following values to enable various debugging
# features. Each level includes the debugging options from the previous
# levels. Currently, the recognized values for DEBUG are:
@@ -296,7 +283,6 @@ SQLITE3EXEPDB = /pdb:sqlite3sh.pdb
!IFNDEF OPT_FEATURE_FLAGS
!IF $(MINIMAL_AMALGAMATION)==0
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS3=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS5=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RTREE=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_GEOPOLY=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_STMTVTAB=1
@@ -325,14 +311,6 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_MATH_FUNCTIONS
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RBU=1
!ENDIF
# Should structured exception handling (SEH) be enabled for WAL mode in
# the core library? It is on by default. Only omit it if the
# USE_SEH=0 option is provided on the nmake command-line.
#
!IF $(USE_SEH)==0
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_OMIT_SEH=1
!ENDIF
# These are the "extended" SQLite compilation options used when compiling for
# the Windows 10 platform.
#
@@ -740,13 +718,6 @@ RCC = $(RCC) -DSQLITE_WIN32_MALLOC_VALIDATE=1
!ENDIF
# Address sanitizer if ASAN=1
#
!IF $(ASAN)>0
TCC = $(TCC) /fsanitize=address
!ENDIF
# Compiler options needed for programs that use the readline() library.
#
!IFNDEF READLINE_FLAGS
@@ -988,8 +959,6 @@ SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_DQS=0
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_FTS4=1
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS=1
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_OFFSET_SQL_FUNC=1
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION=1
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_STMT_SCANSTATUS=1
!ENDIF
+1 -1
View File
@@ -19,7 +19,7 @@ dnl to configure the system for the local environment.
# so that we create the export library with the dll.
#-----------------------------------------------------------------------
AC_INIT([sqlite],[3.45.0])
AC_INIT([sqlite],[3.42.0])
#--------------------------------------------------------------------
# Call TEA_INIT as the first TEA_ macro to set up initial vars.
Vendored
+27 -48
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.45.0.
# Generated by GNU Autoconf 2.69 for sqlite 3.43.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.45.0'
PACKAGE_STRING='sqlite 3.45.0'
PACKAGE_VERSION='3.43.0'
PACKAGE_STRING='sqlite 3.43.0'
PACKAGE_BUGREPORT=''
PACKAGE_URL=''
@@ -776,7 +776,7 @@ OPT_FEATURE_FLAGS
HAVE_ZLIB
USE_AMALGAMATION
TARGET_DEBUG
TARGET_HAVE_LINENOISE
EXTENSION_DEBUG
TARGET_HAVE_EDITLINE
TARGET_HAVE_READLINE
TARGET_READLINE_INC
@@ -904,7 +904,6 @@ enable_editline
enable_readline
with_readline_lib
with_readline_inc
with_linenoise
enable_debug
enable_amalgamation
enable_load_extension
@@ -1472,7 +1471,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.45.0 to adapt to many kinds of systems.
\`configure' configures sqlite 3.43.0 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1537,7 +1536,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of sqlite 3.45.0:";;
short | recursive ) echo "Configuration of sqlite 3.43.0:";;
esac
cat <<\_ACEOF
@@ -1589,7 +1588,6 @@ Optional Packages:
(tclConfig.sh)
--with-readline-lib specify readline library
--with-readline-inc specify readline include paths
--with-linenoise=DIR source directory for linenoise library
Some influential environment variables:
CC C compiler command
@@ -1668,7 +1666,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
sqlite configure 3.45.0
sqlite configure 3.43.0
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2087,7 +2085,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.45.0, which was
It was created by sqlite $as_me 3.43.0, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -3945,13 +3943,13 @@ if ${lt_cv_nm_interface+:} false; then :
else
lt_cv_nm_interface="BSD nm"
echo "int some_variable = 0;" > conftest.$ac_ext
(eval echo "\"\$as_me:3948: $ac_compile\"" >&5)
(eval echo "\"\$as_me:3945: $ac_compile\"" >&5)
(eval "$ac_compile" 2>conftest.err)
cat conftest.err >&5
(eval echo "\"\$as_me:3951: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
(eval echo "\"\$as_me:3948: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
(eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
cat conftest.err >&5
(eval echo "\"\$as_me:3954: output\"" >&5)
(eval echo "\"\$as_me:3951: output\"" >&5)
cat conftest.out >&5
if $GREP 'External.*some_variable' conftest.out > /dev/null; then
lt_cv_nm_interface="MS dumpbin"
@@ -5157,7 +5155,7 @@ ia64-*-hpux*)
;;
*-*-irix6*)
# Find out which ABI we are using.
echo '#line 5160 "configure"' > conftest.$ac_ext
echo '#line 5157 "configure"' > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
ac_status=$?
@@ -6682,11 +6680,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:6685: $lt_compile\"" >&5)
(eval echo "\"\$as_me:6682: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
echo "$as_me:6689: \$? = $ac_status" >&5
echo "$as_me:6686: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@@ -7021,11 +7019,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:7024: $lt_compile\"" >&5)
(eval echo "\"\$as_me:7021: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
echo "$as_me:7028: \$? = $ac_status" >&5
echo "$as_me:7025: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@@ -7126,11 +7124,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:7129: $lt_compile\"" >&5)
(eval echo "\"\$as_me:7126: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
echo "$as_me:7133: \$? = $ac_status" >&5
echo "$as_me:7130: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@@ -7181,11 +7179,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:7184: $lt_compile\"" >&5)
(eval echo "\"\$as_me:7181: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
echo "$as_me:7188: \$? = $ac_status" >&5
echo "$as_me:7185: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@@ -9561,7 +9559,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
#line 9564 "configure"
#line 9561 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@@ -9657,7 +9655,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
#line 9660 "configure"
#line 9657 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@@ -11248,27 +11246,6 @@ fi
fi
fi
# Check whether --with-linenoise was given.
if test "${with_linenoise+set}" = set; then :
withval=$with_linenoise; with_linenoise=$withval
else
with_linenoise="no"
fi
if test "x$with_linenoise" != "xno"; then
TARGET_HAVE_READLINE=0
TARGET_HAVE_EDITLINE=0
TARGET_HAVE_LINENOISE=1
TARGET_READLINE_INC="-I${with_linenoise}"
TARGET_READLINE_LIBS="${with_linenoise}/linenoise.c"
echo "using linenoise source code at ${with_linenoise}"
else
TARGET_HAVE_LINENOISE=0
echo "not using linenoise"
fi
@@ -11345,11 +11322,13 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build type" >&5
$as_echo_n "checking build type... " >&6; }
if test "${enable_debug}" = "yes" ; then
TARGET_DEBUG="-DSQLITE_DEBUG=1 -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE -O0 -Wall"
TARGET_DEBUG="-DSQLITE_DEBUG=1 -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE -O0"
EXTENSION_DEBUG="-DSQLITE_DEBUG=1"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: debug" >&5
$as_echo "debug" >&6; }
else
TARGET_DEBUG="-DNDEBUG"
EXTENSION_DEBUG="-DNDEBUG"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: release" >&5
$as_echo "release" >&6; }
fi
@@ -12481,7 +12460,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.45.0, which was
This file was extended by sqlite $as_me 3.43.0, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -12547,7 +12526,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.45.0
sqlite config.status 3.43.0
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
+5 -19
View File
@@ -74,7 +74,7 @@
# you don't need (for example BLT) by erasing or commenting out
# the corresponding code.
#
AC_INIT([sqlite],m4_esyscmd(cat VERSION | tr -d '\n'))
AC_INIT([sqlite],[m4_esyscmd(cat VERSION | tr -d '\n')])
dnl Make sure the local VERSION file matches this configure script
sqlite_version_sanity_check=`cat $srcdir/VERSION | tr -d '\n'`
@@ -598,28 +598,11 @@ if test x"$with_readline" != xno; then
TARGET_HAVE_READLINE=1
fi
fi
AC_ARG_WITH([linenoise],
[AS_HELP_STRING([--with-linenoise=DIR],[source directory for linenoise library])],
[with_linenoise=$withval],
[with_linenoise="no"])
if test "x$with_linenoise" != "xno"; then
TARGET_HAVE_READLINE=0
TARGET_HAVE_EDITLINE=0
TARGET_HAVE_LINENOISE=1
TARGET_READLINE_INC="-I${with_linenoise}"
TARGET_READLINE_LIBS="${with_linenoise}/linenoise.c"
echo "using linenoise source code at ${with_linenoise}"
else
TARGET_HAVE_LINENOISE=0
echo "not using linenoise"
fi
AC_SUBST(TARGET_READLINE_LIBS)
AC_SUBST(TARGET_READLINE_INC)
AC_SUBST(TARGET_HAVE_READLINE)
AC_SUBST(TARGET_HAVE_EDITLINE)
AC_SUBST(TARGET_HAVE_LINENOISE)
##########
# Figure out what C libraries are required to compile programs
@@ -632,13 +615,16 @@ AC_SEARCH_LIBS(fdatasync, [rt])
AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug],[enable debugging & verbose explain]))
AC_MSG_CHECKING([build type])
if test "${enable_debug}" = "yes" ; then
TARGET_DEBUG="-DSQLITE_DEBUG=1 -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE -O0 -Wall"
TARGET_DEBUG="-DSQLITE_DEBUG=1 -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE -O0"
EXTENSION_DEBUG="-DSQLITE_DEBUG=1"
AC_MSG_RESULT([debug])
else
TARGET_DEBUG="-DNDEBUG"
EXTENSION_DEBUG="-DNDEBUG"
AC_MSG_RESULT([release])
fi
AC_SUBST(TARGET_DEBUG)
AC_SUBST(EXTENSION_DEBUG)
#########
# See whether we should use the amalgamation to build
-148
View File
@@ -1,148 +0,0 @@
# Notes On Compiling SQLite On Windows 11
Here are step-by-step instructions on how to build SQLite from
canonical source on a new Windows 11 PC, as of 2023-11-01:
1. Install Microsoft Visual Studio. The free "community edition"
will work fine. Do a standard install for C++ development.
SQLite only needs the
"cl" compiler and the "nmake" build tool.
2. Under the "Start" menu, find "All Apps" then go to "Visual Studio 20XX"
and find "x64 Native Tools Command Prompt for VS 20XX". Pin that
application to your task bar, as you will use it a lot. Bring up
an instance of this command prompt and do all of the subsequent steps
in that "x64 Native Tools" command prompt. (Or use "x86" if you want
a 32-bit build.) The subsequent steps will not work in a vanilla
DOS prompt. Nor will they work in PowerShell.
3. Install TCL development libraries. This note assumes that you will
install the TCL development libraries in the "`c:\Tcl`" directory.
Make adjustments
if you want TCL installed somewhere else. SQLite needs both the
"tclsh.exe" command-line tool as part of the build process, and
the "tcl86.lib" library in order to run tests. You will need
TCL version 8.6 or later.
<ol type="a">
<li>Get the TCL source archive, perhaps from
[https://www.tcl.tk/software/tcltk/download.html](https://www.tcl.tk/software/tcltk/download.html).
<li>Untar or unzip the source archive. CD into the "win/" subfolder
of the source tree.
<li>Run: `nmake /f makefile.vc release`
<li>Run: `nmake /f makefile.vc INSTALLDIR=c:\Tcl install`
<li>CD to `c:\Tcl\lib`. In that subfolder make a copy of the
"`tcl86t.lib`" file to the alternative name "`tcl86.lib`"
(omitting the second 't'). Leave the copy in the same directory
as the original.
<li>CD to `c:\Tcl\bin`. Make a copy of the "`tclsh86t.exe`"
file into "`tclsh.exe`" (without the "86t") in the same directory.
<li>Add `c:\Tcl\bin` to your %PATH%. To do this, go to Settings
and search for "path". Select "edit environment variables for
your account" and modify your default PATH accordingly.
You will need to close and reopen your command prompts after
making this change.
</ol>
4. Download the SQLite source tree and unpack it. CD into the
toplevel directory of the source tree.
5. Set the TCLDIR environment variable to point to your TCL installation.
Like this:
<ul>
<li> `set TCLDIR=c:\Tcl`
</ul>
6. Run the "`Makefile.msc`" makefile with an appropriate target.
Examples:
<ul>
<li> `nmake /f makefile.msc`
<li> `nmake /f makefile.msc sqlite3.c`
<li> `nmake /f makefile.msc devtest`
<li> `nmake /f makefile.msc releasetest`
</ul>
## 32-bit Builds
Doing a 32-bit build is just like doing a 64-bit build with the
following minor changes:
1. Use the "x86 Native Tools Command Prompt" instead of
"x64 Native Tools Command Prompt". "**x86**" instead of "**x64**".
2. Use a different installation directory for TCL.
The recommended directory is `c:\tcl32`. Thus you end up
with two TCL builds:
<ul>
<li> `c:\tcl` &larr; 64-bit (the default)
<li> `c:\tcl32` &larr; 32-bit
</ul>
3. Ensure that `c:\tcl32\bin` comes before `c:\tcl\bin` on
your PATH environment variable. You can achieve this using
a command like:
<ul>
<li> `set PATH=c:\tcl32\bin;%PATH%`
</ul>
## Building a DLL
The command the developers use for building the deliverable DLL on the
[download page](https://sqlite.org/download.html) is as follows:
> ~~~~
nmake /f Makefile.msc sqlite3.dll USE_NATIVE_LIBPATHS=1 "OPTS=-DSQLITE_ENABLE_FTS3=1 -DSQLITE_ENABLE_FTS4=1 -DSQLITE_ENABLE_FTS5=1 -DSQLITE_ENABLE_RTREE=1 -DSQLITE_ENABLE_JSON1=1 -DSQLITE_ENABLE_GEOPOLY=1 -DSQLITE_ENABLE_SESSION=1 -DSQLITE_ENABLE_PREUPDATE_HOOK=1 -DSQLITE_ENABLE_SERIALIZE=1 -DSQLITE_ENABLE_MATH_FUNCTIONS=1"
~~~~
That command generates both the sqlite3.dll and sqlite3.def files. The same
command works for both 32-bit and 64-bit builds.
## Statically Linking The TCL Library
Some utility programs associated with SQLite need to be linked
with TCL in order to function. The [sqlite3_analyzer.exe program](https://sqlite.org/sqlanalyze.html)
is an example. You can build as described above, and then
enter:
> ~~~~
nmake /f Makefile.msc sqlite3_analyzer.exe
~~~~
And you will end up with a working executable. However, that executable
will depend on having the "tcl86.dll" library somewhere on your %PATH%.
Use the following steps to build an executable that has the TCL library
statically linked so that it does not depend on separate DLL:
1. Use the appropriate "Command Prompt" window - either x86 or
x64, depending on whether you want a 32-bit or 64-bit executable.
2. Untar the TCL source tarball into a fresh directory. CD into
the "win/" subfolder.
3. Run: `nmake /f makefile.vc OPTS=nothreads,static shell`
4. CD into the "Release*" subfolder that is created (note the
wildcard - the full name of the directory might vary). There
you will find the "tcl86s.lib" file. Copy this file into the
same directory that you put the "tcl86.lib" on your initial
installation. (In this document, that directory is
"`C:\Tcl32\lib`" for 32-bit builds and
"`C:\Tcl\lib`" for 64-bit builds.)
5. CD into your SQLite source code directory and build the desired
utility program, but add the following extra arguments to the
nmake command line:
<blockquote><pre>
CCOPTS="-DSTATIC_BUILD" LIBTCL="tcl86s.lib netapi32.lib user32.lib"
</pre></blockquote>
<p>So, for example, to build a statically linked version of
sqlite3_analyzer.exe, you might type:
<blockquote><pre>
nmake /f Makefile.msc CCOPTS="-DSTATIC_BUILD" LIBTCL="tcl86s.lib netapi32.lib user32.lib" sqlite3_analyzer.exe
</pre></blockquote>
6. After your executable is built, you can verify that it does not
depend on the TCL DLL by running:
<blockquote><pre>
dumpbin /dependents sqlite3_analyzer.exe
</pre></blockquote>
+73
View File
@@ -0,0 +1,73 @@
# Introduction to the SQLite Extensible Shell #
This article introduces a set of enhancements to the SQLite "CLI" shell
which make it extensible as a shell and provide other useful features.
What "extensible" means as of June 2023 is that additional dot-commands
can be implemented by a dynamically loaded extension and integrated
into the shell's command repertoire and help for same. It also means
that scripting support can be provided by an extension and integrated
into the shell's input collection and dispatching facility.
In the future, "extensible" will also mean that additional forms of query
result output formatting (or other disposition), and additional methods
of data collection into tables (or other disposition), can be provided
by an extension and utilized via the shell's .mode and .import commands.
(This integration with .mode and .import commands is in-work.)
Scripting support for the shell is presently implemented by a
[Tcl Shell Extension](https://sqlite.org/src/file?name=doc/tcl_extension_intro.md&ci=cli_extension).
Some new features are available via these new or altered dot-commands:
**.eval** will interpret its arguments as shell input. This is mainly useful
from within a script to run fixed or formulated shell commands.
**.parameter** has several enhancements: The new "edit" subcommand can be
used to create or modify SQL binding parameters, either by name or from
being referenced in the most recently run SQL. The new "save" and "load"
subcommands will store or retrieve all or named parameters into/from a file.
**.shxload** is used to load shell extensions at runtime.
**.shxopts** either shows or alters extended shell features. Presently,
this allows shell input parsing to be enhanced as described below or to be
reverted to the traditional rule where dot-commands always occupy one line
and neither dot-commands nor #-prefixed comments may have leading whitespace.
**.tables** has new options to show only tables, views, or system tables.
**.vars** is used to create or modify "shell" variables. These are
key/value pairs which are associated with the shell session rather than
a specific database such as .databases would list. These variables may
be the subject of "edit", "set", "save" and "load" subcommands with
effect similar to what they do with the .parameter dot-command.
**.x** is a general purpose, "run these" command. By default, its arguments
are treated as shell variable names, values of which are interpreted as
shell input. Optionally, its arguments are interpreted similarly to the
.eval and .read subcommand. They differ in that interpretation of the
arguments stops upon any error.
Other new shell features are:
By default, when invoked as "sqlite3x", or after ".shxopts +parsing" is run,
the shell effects enhanced command parsing whereby quoted dot-command arguments
may span multiple lines or dot-command input lines may be spliced together
with trailing backslash, and all shell input may have leading whitespace.
When not invoked as "sqlite3x", or after ".shxopts -parsing" is run, any
dot-command arguments and the dot-command itself end at the first newline.
Also, input lines with leading whitespace will not be accepted as dot-commands
or #-prefixed comments.
This might be needed to run legacy shell scripts having some dot-command(s)
with a final argument that has an initial quote but no closing quote or
which happen to end with backslash.
The shell's handling of certain fatal conditions has been changed to make
it more suitable being embedded into other applications. It no longer calls
exit() for OOM conditions or -safe mode violations. This feature has not
yet been fully tested; work remains to be sure that the shell can be called,
then return to its caller, repeatedly without leaking resources or leaving
the console in an odd state. But the infrastructure is in place to make
that all work with only minor (or possibly no) revisions.
+2 -2
View File
@@ -1229,8 +1229,8 @@ first syntax error, of course, if there are no instances of the
<p>Lemon was originally written by Richard Hipp sometime in the late
1980s on a Sun4 Workstation using K&amp;R C.
There was a companion LL(1) parser generator program named "Lime".
The Lime source code has been lost.</p>
There was a companion LL(1) parser generator program named "Lime", the
source code to which as been lost.</p>
<p>The lemon.c source file was originally many separate files that were
compiled together to generate the "lemon" executable. Sometime in the
+608
View File
@@ -0,0 +1,608 @@
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>CLI Shell Extensibility</title>
</head>
<body>
<style>
body{counter-reset: section}
h2{counter-reset: h2counter; text-indent: 10px}
h3{counter-reset: h3counter; text-indent: 25px}
h4{counter-reset: h4counter; text-indent: 40px}
h2:before{
counter-increment: section;
content: counter(section) " ";
}
h3:before{
counter-increment: h2counter;
content: counter(section) "." counter(h2counter) " ";
}
h4:before{
counter-increment: h3counter;
content: counter(section) "." counter(h2counter) "." counter(h3counter) " ";
}
h5:before{
counter-increment: h4counter;
content: counter(section) "." counter(h2counter) "." counter(h3counter) "." counter(h4counter) " ";
}
</style>
<img class="logo" src="https://sqlite.org/images/sqlite370_banner.gif"
alt="SQLite" border="0"/>
<h1>CLI Shell Extensibility</h1>
<hr/>
<h2>Introduction</h2>
<p>The command line shell for SQLite can be customized
to modify or add certain kinds of features, without altering its source.
This document details this extensibility
and how such extension may be accomplished by shell users.
<p>Shell extensibility serves to reduce the tension between
keeping the shell simple with broadly useful features
and allowing the shell to become an ever-growing tool
meeting diverse needs for those not ready to satisfy them
with a custom program using the SQLite libary.
Extensions contributed by the SQLite developers or others
will become (or are) available for use in situations
that may not justify permanently adding the same features
to the core shell published by the SQLite project in binary form.
<h2>Extensible Features</h2>
<p>Only certain categories of features may be
added or modified via extension, namely:
<h3>New or Revised Meta-Commands</h3>
<p>The "dot" commands implemented by the shell,
along with help text for them, may be augmented or overridden.
A meta-command effected with an extension may be used
in the same ways as one available in the core, non-extended shell.
<h3>New or Revised Import Modes (work-in-progress)</h3>
<p>The ways in which data may be imported to a DB table
may be augmented or overriden. Once that is done, the .import
meta-command will either have a new option to specify a new import
handler or an existing .import option can be overridden for this.
<h3>New or Revised Query Result Handling (work-in-progress)</h3>
<p>The display formatting or other disposition of query results
may be augmented or overriden. Once that is done, the .mode
meta-command will either have a new option to specify a new result
handler or an existing .mode option can be overridden for this.
<h3>Generalized Import Modes and Query Result Handling</h3>
<p>New handlers added via extension are not restricted to
importing data from a file or result display formatting.
They may be considered more generally to be either
a data source or a data sink, producing or accepting
data row sets. The origin or destination of
the data is up to the handler, as may be affected
by arguments to the .import or most recent .mode command.
<h3>Scriptability</h3>
<p>An shell extension may provide for integration of a scripting
subsystem with the shell. Such a subsystem may participate in
classification of input, extending the recognized classes from
{ meta-command SQL-statement(s) comment } to add { script }, and
then handle input in the "script" input class. The shell exposes
some its internal data and code to script implementations so that
they may act in a manner providing adaptation or enhancement of
shell features rather than entirely independent new features.
<h2>Extension Methods, Dynamic or Static</h2>
<p>Shell extension may be effected at different times
in different ways according to convenience and need.
<h3>Runtime Extension</h3>
<p>Extension at runtime (when the shell is running)
is effected via the .shxload command.
In this way, a dynamically loaded library (DLL) is loaded with
provision made for its sqlite3_X_init() function to obtain
the shell extension API entry points and thereby
register with the shell core any
new meta-commands, or import or query result handlers
or scripting support that it implements.
<h3>Build-Time Extension</h3>
<p>Extension when the shell is built is effected by specifying
certain option values to either the "make" invocation or
to a utility, (tool/mkshellc.tcl), which assembles and transforms
sources to produce the shell's unitary source file (shell.c).
<h3>Extension Source Code</h3>
<p>With certain coding conventions and methods followed,
the same source code can be used either
to produce most of a runtime shell extension DLL or to
be incorporated into shell.c as a built-in shell extension.
(See the extension code samples for details. (Not fully implemented.))
<h3>Build-Time Diminuation</h3>
<p>Just as new meta-commands can be readily incorporated into
the shell when it is built, many of the core meta-commands
can be readily omitted from the shell build. This is done
with a variation of the option values that may be given
to "make" or to tool/mkshellc.tcl as the shell is built.
(See tool/mkshellc.tcl --help output for details.)
Such omission of meta-commands might be done when building
a customized shell which need not have the various meta-commands
which exist for the purpose of testing the SQLite library.
<h2>Interface</h2>
<p>The following details relate to src/shext_linkage.h, a header
in which declarations appear for objects and functions
that facilitate runtime interaction between the shell core
and shell extensions written in C/C++.
Comments in that header tersely summarize these explanations:
<h3>struct ShellInState and ShellExState</h3>
<p>The Shell{In,Ex}State objects, are maintained by the shell and known
as shell state. The shell state consists of a public portion, which
is available and stable for use in shell/extension interactions,
and a private portion which may not be stable. Shell extension code
used only for build-time extension might use the private part,
(to which it has access because such code is compiled within the same
translation unit as the core shell code),
but such usage generally precludes (or makes hazardous)
use of runtime loadable extensions built from such code.
<h3>ExtensionId typedef and eid Member</h3>
<p>An object of this type serves to uniquely identify an extension
loaded at runtime so that it may be unloaded later. It must be
passed back to the shell (in the ShellExtensionLink eid member)
by the sqlite3_X_init() function if the extension DLL is ever
to be unloaded during that shell session.
<h3>ShellExState typedef</h3>
<p>An object of this type is passed between the shell core and its
import or query result handlers to: (1) convey or keep parameters and
data related to formatting or parsing data rows in an external form;
(2) keep state associated with the progression of an import or result
handling operation from initiation to completion; (3) to facilitate
access to exposed shell state generally useful to such handlers or
meta-commands; or (4) to provide for abnormal shell exits.
<p>The shell core .mode and .import implementations also use the same
instance of this type to affect result output and import operations.
That instance resides in a ShellStateX object kept by the shell so
that extension meta-commands can access it, possibly to change it.
Meta-commands or handlers which alter this instance for their own
purposes (rather than for intended effect) should take care to
restore its prior value as the operation completes.
<h3>extensionDestruct member</h3>
<p>The function addressed by this member will be called prior to
exit or the extension being unloaded (if the pointer is non-zero.)
This is an out parameter from the sqlite3_X_init() function.
It may perform any cleanup or deallocations necessitated by
successful initialization generally (and will never be called
after failed initialization.)
<h3>Notes Regarding Object Interfaces for C vs C++ Writers</h3>
<p>The objects registered with the shell core to provided extension
functionality may be implemented in C or C++ (or anything else
presenting the same ABI.) In the below descriptions of their
interfaces, it should be understood that: C++ implementations
need not explicitly deal with anything like a Whatsit_Vtable
struct and will refer to the object pointer, passed implicitly,
as "this"; and C implementations will need to populate a static
Whatsit_Vtable and refer to the initial object pointer as "pThis".
<p>All shell extension interfaces have a method, destruct(), which
is (or may be) called by the shell core prior to deactivating any
registered meta-command, output result or import handler.
This call will be made in addition to any automatic (or implicit)
takedown that may occur due to atexit() or C++ destructor calls,
so destruct()'s responsibility should be limited to reversing
the per-registered-object effects of sqlite3_X_init().
<p>A registered object is deactivated when either: the extension
is immanently going to be unloaded; the registered object is
being overridden by some like-named object (such that it can no
longer be reached by the core shell); or the shell is about to
return or exit.
<h3>DotCommand typedef</h3>
<p>These objects represent an extension meta-command, including a
dispatch table for the public interface and any accompanying
data (which is opaque to the core shell.) Such objects are created
by extensions and passed to the core shell only by reference.
They are made known to the shell core via registerDotCommand() calls.
<h3>DotCommand_Vtable typedef</h3>
<p>These objects represent the dispatch table of a DotCommand object.
<p>All methods are given the same leading (or lone) argument:<br>
(1) the address of the registered DotCommand object.
<h4>destruct method</h4>
<p>This method is called prior to unloading a runtime extension
for any registered DotCommand object, provided its dispatch
table entry is non-zero.
It should free resources allocated during the sqlite3_X_init() call
associated with creation or preparation of the object.
<h4>name method</h4>
<p>This method returns the name of the meta-command (sans leading '.'.)
The returned pointer must remain valid throughout the lifetime of
the registered DotCommand object.
<h4>help method</h4>
<p>This method returns help text for the meta-command. This text
should be formatted and aligned with the built-in meta-command
help text so that it can be displayed seamlessly.
<p>There is one additional argument:<br>
(2) a const char * directing what help text to return, with
0 indicating primary, single-line help, or
&"" indicating more detailed help beyond the primary level, or
&"(other)" reserved for expansion.
<p>The return for the first 2 forms is either a C string or
null pointer. The C string will not be freed by the core shell, and
must remain valid during the lifetime of the DotCommand object.
<h4>argsCheck method</h4>
<p>This method returns a DotCmdRC value indicating the validity
(or invalidity) of arguments for the meta-command, using the
coding scheme defined for the DotCmdRC type.
<p>There are three additional arguments:<br>
(2) a char** to possibly receive an error message, which the
caller must eventually pass to sqlite3_free().<br>
(3) a count of all provided arguments; and<br>
(4) a char* array of given arguments, led by meta-command's name.
<h4>execute method</h4>
<p>This method performs whatever work the meta-command is supposed
to do when invoked. It has 4 additional arguments:<br>
(2) present ShellExState, passed by reference (an in/out parameter);<br>
(3) an error message pointer, passed by reference, set upon error but
otherwise not modified, to be freed by the shell core;<br>
(4) the number of invocation arguments; and<br>
(5) an array of C strings constituting the invocation arguments;<br>
<p>The return is a DotCmdRC value, indicating success, error, or
other disposition as documented for the DotCmdRC type.
<h3>ExportHandler typedef</h3>
<p>These objects represent an extension query result handler, including
a dispatch table for the public interface and any accompanying
data which is opaque to the core shell. Such objects are created
by extensions and passed to the core shell only by reference.
They are made known to the shell core via registerExporter() calls.
<h3>ExportHandler_Vtable typedef</h3>
<p>These objects represent the dispatch table of an ExportHandler object.
All methods in the dispatch table are given at
least this leading argument:<br>
(1) The ExportHandler address registered via registerExporter();<br>
<h4>destruct method</h4>
<p>This method is called prior to the shell's return or exit or
unloading the runtime extension for a registered ExportHandler object,
provided its dispatch table entry is non-zero.
It should free resources allocated during the sqlite3_X_init() call
associated with creation or preparation of the object.
<h4>name method</h4>
<p>This method returns the name of the ExportHandler, which users
specify to the .mode command (as the mode's name) to designate use
of the registered ExportHandler for subsequent query results.
The returned pointer must remain valid throughout the lifetime of
the registered ExportHandler object.
<h4>help method</h4>
<p>This method returns help text for the ExportHandler.
<p>There is one additional argument:<br>
(2) a const char * directing what help text to return, with
0 indicating primary, single-line help, or
&"" indicating more detailed help beyond the primary level, or
&"(other)" reserved for expansion.
<p>The primary help is included in the .mode command's own
detailed help text, so it should be aligned accordingly.
The detailed help is shown by the .mode command's --help option.
<p>The return is either a C string or null pointer. The C string
will not be freed by the core shell, and must remain valid
during the lifetime of the ExportHandler object.
<h4>Common arguments</h4>
<p>The following methods are given these 2 additional arguments:<br>
(2) A ShellExState object passed by reference; and<br>
(3) An error message pointer, passed by reference, to receive errors.
<h4>openResultsOutStream method</h4>
<p>This method is called when a query output is setup,
(via the .mode command with the handler's name given as a --flag.)
<p>It is given 3 additional arguments:<br>
(4) the number of arguments in the said .mode command;<br>
(5) an array of C strings with the said argument values; and<br>
(6) the name given as a --flag which caused the handler to be used.
<p>When the extension handler is activated via a .mode command,
parsing that command and behaving accordingly is the responsibility
of this method alone. (The command is parsed and acted upon by the
default .mode implementation only when no ExportHandler is used.)
<p>Once this method is called and succeeds, it is guaranteed that the
closeResultsOutStream method will be called.
<p>This method should return SQLITE_OK only upon success.
Any other return will abort remaining calls in the handling sequence.
<h4>prependResultsOut method</h4>
<p>This method is called when a query succeeds for which this handler
will be given the results. This is purely preparatory; zero or
more result rows may follow. It is up to this method to determine
if any results can even be had (by considering the return from
sqlite3_column_count()) and acting accordingly.
<p>It is given 1 additional argument:<br>
(4) the query prepared statement (pointer), not yet stepped.
<p>This method should return SQLITE_OK only upon success.
Any other return will abort the next 2 calls in the handling sequence.
(TBD: Such aborts without error should be supported for DDL and DML.)
<h4>rowResultsOut method</h4>
<p>This method is called when a query's prepared statement is stepped,
for each result row available.
<p>It is given 1 additional argument:<br>
(4) the query prepared statement (pointer), stepped with
a result row available. It is permitted for this method to
perform the remaining stepping, as indicated by the return.
<p>This method should return SQLITE_OK only upon success
without having completed stepping. If this method completes stepping,
(by making its own calls to sqlite3_step() until it returns SQLITE_DONE),
it must return SQLITE_DONE. Any other return will avoid the next call
in the handling sequence.
<h4>appendResultsOut method</h4>
<p>This method is called when result set stepping has been completed.
<p>It is given additional argument:<br>
(4) the query prepared statement (pointer), completely stepped.
<p>This method should return SQLITE_OK upon success, or may return
something else to indicate error with no effect upon succeeding calls.
<h4>closeResultsOutStream method</h4>
<p>This method is called when a new .mode command is invoked specifying
some output mode not using this ExportHandler or when the extension
is about to be unloaded with this ExportHandler selected for output.
It should free resources allocated or held as a result of the
previous openResultsOutStream call.
<h3>ImportHandler typedef</h3>
<p>These objects represent an extension data import handler, including
a dispatch table for the public interface and any accompanying
data which is opaque to the core shell. Such objects are created
by extensions and passed to the core shell only by reference.
They are made known to the shell core via registerImporter() calls.
<h3>ImportHandler_Vtable typedef</h3>
<p>These objects represent the dispatch table of an ImportHandler object.
All methods in the dispatch table are given this 1 leading argument:<br>
(1) The ImportHandler address registered via registerImporter().
<h4>destruct method</h4>
<p>This method is called prior to the shell's return or exit or
unloading the runtime extension for a registered ImportHandler object,
provided its dispatch table entry is non-zero.
It should free resources allocated during the sqlite3_X_init() call
associated with creation or preparation of the object.
<h4>name method</h4>
<p>This method returns the name of the importer, which is to
be passed with leading '--' (or '-') to the .import command
to specify use of the registered importer for that invocation.
The returned pointer must remain valid throughout the lifetime of
the registered ImportHandler object.
<h4>help method</h4>
<p>This method returns help text for the ImportHandler.
<p>There is one additional argument:<br>
(2) a const char * directing what help text to return, with
0 indicating primary, single-line help, or
&"" indicating more detailed help beyond the primary level, or
&"(other)" reserved for expansion.
<p>The primary help is included in the .import command's own
detailed help text, so it should be aligned accordingly.
The detailed help is shown by the .import command's --help option.
<p>The return is either a C string or null pointer. The C string
will not be freed by the core shell, and must remain valid
during the lifetime of the ImportHandler object.
<h4>Common arguments</h4>
<p>The following methods are given these 2 additional arguments:<br>
(2) A ShellExState object passed by reference; and<br>
(3) An error message pointer, passed by reference, to receive errors.
<h4>openDataInStream method</h4>
<p>This method is called when a .import command is invoked with
the '--' (or '-') prefixed name of the ImportHandler as an argument.
<p>These 3 additional arguments are passed:<br>
(4) the number of arguments to said .import command;<br>
(5) an array of C strings with the said argument values; and<br>
(6) the name of the ImportHandler (as its name method would return.)
<p>When the extension handler is activated via a .import command,
responsibility for parsing that command and behaving accordingly is
shared between the shell core .import implementation and this method.
The shell core normally takes the last argument as the name of a table
(to be created if necessary) which will receive the imported data.
(But see return values for exceptions to this treatment.)
The shell core also detects any --flag argument selecting a registered
ImportHandler (giving effect to the last one) and calls this and
following methods to perform the input part of the import operation.
This method may interpret any or all of the arguments as needed
and (supposedly) documented by the associated help(...) method returns.
<p>Once this method is called and succeeds, it is guaranteed that the
closeDataInStream method will be called.
<p>This method should return SQLITE_OK upon success where disposition
of the imported data (into a table named by the last argument) is
to be handled normally, by the shell core.
An alternative success return is SQLITE_DONE, indicating that
disposition of the imported data has been done by the ImportHandler.
In that case, the next 3 methods (*DataInput(...)) will not be called
and this method should have completed the whole import operation.
After a success return, closeDataInStream is guaranteed to be called.
Other returns will abort all remaining calls in the handling sequence.
<h4>prepareDataInput method</h4>
<p>This method prepares for the particular import operation commenced
with a .import invocation. It may take into account the shape of
the input data (and possibly its type) as discovered during the call.
<p>This 1 additional argument is passed:<br>
(4) a to-be-prepared statement (pointer), passed by reference.
This is an out parameter conveying a prepared statement created
by this method specifically for the single import operation.
<p>The prepared statement should, in some manner wholly determined
by the extension handler, incorporate compiled SQL (possibly with
as-yet unbound parameters) which will (or may) produce a result set.
(This may be no more than a query such as "SELECT @1 as one, ...",
or could be a SELECT from a temporary table used for buffering.)
<p>The return should be SQLITE_OK upon success, in which case the
following 2 methods will also be called. Any other return will
abort the {prepare,row,finish}DataInput() call sequence. In that
case, no prepared statement should be returned either.
<h4>rowDataInput method</h4>
<p>This method is called to collect imported data, making it available
through the prepared statement passed as the last argument with as
many steps as are needed to get SQLITE_DONE from sqlite3_step().
It will be called repeatedly until its return indicates no more data.
<p>It is given 1 additional argument:<br>
(4) the prepared statement (pointer) returned by prepareDataInput().<br>
This prepared statement can have values bound to it or be reset as
necessary to return some or more data. However, it must remain the
same sqlite3_statement instance returned by prepareDataInput().
<p>The return should be SQLITE_DONE when no more data is available,
or SQLITE_ROW to indicate that more data is or might be available.
Any other return (including SQLITE_OK) indicates a condition which
will abort the data collection phase of the import operation. Any
of those 3 returns is treated as success by the shell core.
<h4>finishDataInput method</h4>
<p>This method is called to complete the data input phase of the
import operation commenced by prepareDataInput().
<p>It is given 1 additional argument:<br>
(4) the prepared statement (pointer) returned by prepareDataInput().<br>
This prepared statement should be finalized by this method. Other
cleanup or import wrap-up related to the transfer may also be performed.
<h4>closeDataInStream method</h4>
<p>This method is called as a .import command which specified this
ImportHandler completes.
It should free resources allocated or held as a result of the
previous openDataInStream call.
<h3>ScriptSupport typedef</h3>
<p>An object of this type is passed by an extension to registerScripting()
to enable the shell core to gain scripting functionality provided by
the extension.
<p>All methods in the dispatch table have this 1 leading argument:<br>
(1) The object address registered via registerScripting().
<p>The object's v-table contains these methods:
<h4>destruct method</h4>
<p>This method is called prior to the shell's return or exit or unloading
the runtime extension, provided its dispatch table entry is non-zero.
It should free resources allocated during the sqlite3_X_init() call
associated with creation or preparation of the object.
<h4>name</h4>
<p>This method returns the name of the ScriptSupport object.
There is no presently planned scheme for using this to switch among
scripting providers or even to identify one that is loaded.
<h4>help</h4>
<p>This method returns help text for the ExportHandler.
There is no presently implemented use for this method's return.
<p>There is one additional argument:<br>
(2) a const char * directing what help text to return.
<h4>configure</h4>
<p>This method allows load-time configuration of the extension.
It returns a DotCmdRC code indicating status of the extension
load and subsequent configuration.
It has these 4 additional arguments:<br>
(2) ShellExState reference, for general use by the extension;<br>
(3) A char ** for an error message associated with configuration;<br>
(4) Count of arguments passed in next parameter; and<br>
(5) A char * array of arguments passed in the .shxload command tail.
<h4>isScriptLeader</h4>
<p>This method returns TRUE if the provided line text should be
considered part of an input line group to be handled by the extension
(rather than being treated as a dot-command or SQL), otherwise FALSE.
There is one additional argument:<br>
(2) char * zLineLead, the initial line of a shell input group.
<h4>scriptIsComplete</h4>
<p>This method returns TRUE if the provided line group text should be
considered ready to execute, otherwise FALSE.
There are two additional arguments:<br>
(2) char * zScript, the possibly ready-to-exectue text; and<br>
(3) char ** pzWhyNot, for possibly saying why it is not ready.
<h4>resetCompletionScan</h4>
<p>This method is called by the shell, with no additional arguments,
to (possibly) restore the extension's input line scanning state.
It has no return.
<h4>runScript</h4>
<p>This method executes the provided script text, returning a
DotCmdRC value to indicate success, error, or other dispositions.
It has three additional arguments:<br>
(2) char * zScript, the to-be-executed text;<br>
(3) ShellExState *, (same struct as is passed to configure); and<br>
(4) A char ** for an error message associated with the execution.
<h3>ShellExtensionLink typedef</h3>
<p>An object of this type is passed (somewhat indirectly) to the
sqlite3_X_init(...) function which is called when a runtime
extension is loaded via a .load command with a --shell flag.
It is used to establish linkage between the loaded extension
and a shell core API exposed specifically for extensibility.
<p>At present, the extension API is limited to registration of
meta-commands, query result handlers, import handlers, and
scripting support implemented by extensions. This API may be
extended in future versions of the core shell, in a backwards-
compatible manner.
(See the pExtra sentinel, whose offset may increase.)
<h4>Establishing Shell/Extension Linkage</h4>
<p>An extension's sqlite3_X_init() function is called soon after
the extension is dynamically loaded. It may obtain a pointer
to the ShellExtensionLink object by the following means. (Or,
without reference to that object, it can do nothing useful.)
<p>During extension build, a macro provided by shext_linkage.h,
SHDB_TO_SHEXTLINK(link_function_name), is used to define a
function named per its argument. This function accepts a
sqlite3 *, as passed into sqlite3_X_init(db, ...), and reads
that DB to retrieve the address of a ShellExtensionLink object,
which it returns. (Or, it returns NULL if somebody has loaded
the extension erroneously using the .load meta-command.)
<p>The sqlite3_X_init() function may call this macro-written
function to obtain a reference to a ShellExtensionLink object
which will remain valid while the extension is loaded.
<h2>Development Aspects of Extensibility and Its Evolution</h2>
<p>There is critical tension between competing uses of the ShellState
object which is kept and used by the core shell for state which
must persist between meta-command invocations. For unhindered
implementation and feature expansion flexibility, the structure
of the ShellState data should be unconstrained across versions
of the shell. However, unless extension meta-commands and data
transferers are to act entirely independently of built-in meta-commands,
(excepting interaction through the above-described extension methods),
some portion of the ShellState data needs to be shared in a stable
manner between extensions and the core shell code.
<p>With respect to interface stability concerns, it is nearly immaterial
whether the sharing occurs through exposed data structures
or an extension API devised to convey similar data. (Only data layout
and possible change notification are at stake in that choice.)
<p>For simplicity, and because it can work in a way consistent with how
built-in shell features are implemented now, the chosen sharing method
is to simply directly expose a subset of the ShellState data. That
subset will be extremely limited to minimize hinderance of what has
previously been unfettered change to that data structure/meaning.
It will initially be: the present output stream, as affected by
.output and .once commands; the currently open user DB (if any);
the dedicated shell DB; and the data related to formatting
and transfer of data in external forms. This data resides in the
ShellStateX object as a ShellExState struct.
<p>As shell extensibility evolves, additional data items may need to
move into the publicly exposed portion of the ShellStateX object,
either directly (via exposed data members) or by means of additional
extension APIs defined in the ShellExtensionLink object (which has
been defined to accommodate growth of its function pointer list in
a backwards-compatible manner.)
</body>
</html>
+431
View File
@@ -0,0 +1,431 @@
# Introduction to the TCL Extension for the SQLite Extensible Shell #
This article introduces an extension,
written to be hosted by the SQLite Extensible Shell,
which adds [Tcl](https://www.tcl.tk/about/index.html) features to it.
## Motivation ##
An understanding of the motivation for this feature may prove useful
for putting what follows into context.
Over its 2+ decade existence, the sqlite3 CLI shell has evolved,
from its original utility as a test vehicle for the SQLite library,
to a tool used by many people who want to use SQLite databases
without writing a specialized application. While the shell exposes
many of the library features, its original design was best suited
for performing short, fixed sequences of DB operations.
Effecting more complex, algorithmically determined operations
can be difficult,
usually requiring use of a general purpose scripting tool
to drive the sqlite3 shell.
This can work well where the task can be decomposed into
"Prepare a sequence of elementary DB operations,"
followed by
"then feed the sequence into sqlite3 as its command/SQL input."
However, where the task requires multiple such steps,
with sqlite3 either being run multiple times or
being made to engage in two-way communication with a driving process,
getting the combination to work as intended can be tedious and tricky.
Those difficulties are enough to make one wish for a better way
to drive the tool, and to muse: If only there was a "Tool Command Language".
Fortunately, there is; it is known as "Tcl".
The sqlite3 shell is a tool, in need of being flexibly commanded,
and Tcl is more than adequate for that purpose, as will be shown.
Not to be overlooked is that SQLite itself originated as a
library for Tcl, and that its integration with Tcl makes it both
powerful and easy to use in Tcl programs.
In fact, Tcl with SQLite can be a very good substitute for
ad hoc combinations of general purpose scripting tools with the SQLite shell,
and has been
[since year 2000](https://www.sqlite.org/src/timeline?c=2000-05-29+14:26:00).
Beyond the utility of [Tcl with SQLite](https://www.tcl.tk/about/uses.html),
SQLite (the shell) with Tcl has virtues of its own.
These include instrumentation,
query plan display, ready-to-use data import and export features,
among others familiar to sqlite3 shell users.
## Basics, Terminology and Getting Started ##
A variant of the SQLite shell can be built, called "sqlite3x" here,
whose long name is "The SQLite Extensible Shell".
It may be built on Unix-like systems by running "make sqlite3x".
On Windows systems, using MSVC, with suitable environment setup,
it may be built by running "nmake -f Makefile.msc sqlite3x.exe".
The Tcl extension for sqlite3x, called "tclshext" here,
may be built from the top directory of a SQLite project checkout.
(See [Obtaining Code ...](https://sqlite.org/getthecode.html#obtaining_code_directly_from_the_version_control_system).)
Build the extension for Unix-like systems,
after configure is run with a "--enable-tcl" option
(and a "--with-tcl=..." option if necessary),
by invoking make with "tcl_shell_extension" as a target.
On Windows systems, using MSVC, with nmake variables TCLDIR and TCLSUFFIX set
appropriately, it may be built by running
"nmake -f Makefile.msc tcl_shell_extension".
It may be necessary to first install the Tcl development package or library
in order for configure to find the Tcl interpreter
and specify how to locate it as a library to be linked with the extension.
To manually get a Tcl-extended-shell started,
(assuming the above-mentioned images were built and have
been placed where the shell and OS loader can find them [^ loader ]),
either of these inputs is needed:<br>
```
From a running shell:
sqlite3x
.shxload tclshext
or
At shell invocation:
sqlite3x -cmd ".shxload tclshext"
```
[^loader]: A directory path may need to be prepended to the extension's
name for the OS loader to find it unless it is in one of the locations
designated to the loader as a candidate for dynamic libraries. How such
designation is made is beyond the scope of this introduction.
Provided this results in another prompt without any error message(s),
the Tcl-extended shell is ready to go.
For brevity, the shell in this state will be called "sqlite3xt".
## Yet Another Prompt -- Now What? ##
When sqlite3xt is ready for input, it acts very much like the sqlite3 shell.
(^ The sqlite3x shell passes a test suite which is substantially
identical to that used for the traditional shell, differing only
in the content and format of error messages.)
When given sensible inputs (as discussed below in "Parsing"),
that are recognized by and valid for the sqlite3 shell,
the same outputs and effects will occur as would with sqlite3.
This condition,
where SQL and dot commands recognized by sqlite3 may be input
and acted upon as sqlite3 would,
is referred to below as the "shell execution environment".
For sqlite3xt, that is just the beginning.
## Execution Environments and Input Parsing Changes ##
The effect of loading tclshext can be briefly summarized as
the introduction of an alternative "execution environment",
together with a way of entering and leaving it,
explication of which follows this on the primary "execution environment":
### Shell execution environment ###
The "shell execution environment", in effect upon sqlite3xt startup,
has these characteristics pertinent here:
* The leading non-whitespace token of an input line group
is interpreted as a dot command if it begins with a single '.';
* if that token is nothing but a '.', the line is a no-op;
* or, if it begins with anything but a '.', it is collected as SQL,
until terminated with ';', '/' or 'go',
then submitted to the SQL execution (prepare/step) engine.
* or, if it begins with '#', it is ignored (as a comment).
* Whitespace-delimited arguments after the leading token
are parsed according to the section on "Dot-command arguments"
[here](https://sqlite.org/cli.html#rules_for_dot_commands_sql_and_more),
with exceptions as noted next.
The main exception to legacy argument parsing is that open quote
constructs are not auto-closed by the end of an input line.
(This legacy behavior may be reinstated if desired by: entering
".shxopts -parsing" at the prompt; renaming the sqlite3x image
to sqlite3 before executing it; or invoking it with the option
pair '-shxopt' '0'.)
Instead, arguments are collected, potentially across multiple lines,
until an input line ends outside of any open quote construct.
(Input which does not depend on the legacy, auto-close-on-newline
behavior is what the term "sensible inputs" means as used above.)
For example, this would not be sensible input to sqlite3:<br>
```
.print 'Hello.
This was entered as multi-line input.'
```
as it would result in an error, (malformed SQL), while this input:<br>
```
.print &quot;I'm not fond of closing my quotations.
```
is acceptable to sqlite3, but deemed "not sensible" here.
When either is input to sqlite3x, a continuation prompt will be issued
(in interactive mode) and the rest of the multi-line input
will be taken as part of the .print dot-command.
Of course, this is (mostly) review to those familiar with the sqlite3 shell.
### Tcl execution environment ###
The "Tcl execution environment" differs in several important ways
from the familiar execution environment describe above.
How to get into this alternative execution environment is described later.
In this alternative execution environment, these critical differences exist:
* An expanded set of command words is available and readily expanded further.
* The available command words generally do not begin with '.'.
(^In the Tcl execution environment, enter
"info&nbsp;commands" to see the available command words.)
* When in interactive mode, commands whose
initial token is not defined as a Tcl command,
but which can be found as an executable
in a directory named in the PATH environment variable,
will be executed in a sub-process
(unless blocked by Tcl global variable auto_noexec having been set.)
* The command word and arguments are collected,
parsed and expanded according to the usual
[rules for the Tcl language](https://www.tcl.tk/about/language.html).
In particular, input line groups are collected until deemed "complete"
by the Tcl parser. This means no open brace, quote or bracket constructs.
* New [command words can be readily defined]
(https://www.tcl.tk/man/tcl8.6/TclCmd/proc.html),
and [variables can be set](https://www.tcl.tk/man/tcl8.6/TclCmd/set.html),
either of which may affect argument expansion
per the usual Tcl rules.
This environment will be familiar to those who use Tcl.
There are a few differences however. These are:<br>
* A single '.' on an input line which is not inside of an incomplete
Tcl line group is treated as a (momentary) end-of-input by the REPL.
(^ "REPL" is an acronym for "Read, Evaluate, Print Loop".)
* The shell's dot commands, while not visible via \[info commands\],
are discoverable by the "unknown" command and will be executed
if their names (with the '.' prefix) would be found and resolved
unambiguously in the shell execution environment.
Commands whose names begin with '.' which are not found as unique in the
shell execution environment produce an "invalid command name" error.
Except for that treatment, the unknown command in effect
acts like the standard Tcl version.
(That standard version remains available as _original_unkown,
to which handling of non-dot-prefixed commands is delegated.)
* A few non-standard-Tcl commands are available. In particular:
+ Commands udb and shdb act nearly like commands creatable
by the "sqlite3" Tcl package to represent an open database. They
differ in that they do not accept the "close" subcommand, which
reflects the fact that they exist to allow the shell's current
user database and the shell's own database to be accessed just
like ones created in Tcl via "sqlite3&nbsp;someDbName&nbsp;itsFilename".
+ Commands sqlite_shell_REPL, get_tcl_group and now_interactive
permit input to be collected in the same manner (and from the same
sources) as the shell's REPL does. Here, "collected in the same manner"
does not include the execution environment switching or SQL execution
that the shell execution environment implements.
+ Command register_adhoc_command permits a newly (or oldly) defined
Tcl command, likely with a leading '.' in its name, to be associated
by name with some help text in a table kept by the shell
for augmentation of its .help facility.
In this way, the .help command can emit help
text, in summary or long forms, for Tcl commands that might be
executable from the shell execution environment.
(More on this below.)
+ Finally, the command ".." (sans quotes) exists for reasons made
evident below. With no arguments,
it does nothing, quietly and successfully, with the empty result.
* The present implementation does not run the event loop processing
that the standard Tcl REPL uses to support certain functionality
(such as sockets, asynchronous I/O and the Tk GUI subsystem.)
A future enhancement is to support use of event loop and Tk.
## Switching Execution Environments ##
The simplest execution environment switching
is effected with lone dots and dot pairs.
Some examples should make this clear. For now, focus on "." or ".."
entered on a line by itself.
(The # comments are not required. The leading prompts reflect
what would be seen in interactive use; they are not to be typed either.)<br>
```
sqlite> # Now in shell execution environment, but it is time to do Tcl stuff.
sqlite> ..
tcl% # Now in Tcl execution environment, ready to roll a trivial dot command.
tcl% proc .do {what} {
> .eval $what
> }
tcl% register_adhoc_command {.do Does whatever one thing it is told to do
> from the shell execution environment and little else}
tcl% # Time to return to shell execution environment.
tcl% .
sqlite> # See how ad hoc command creation worked.
sqlite> .help do
.do Does whatever one thing it is told to do
from the shell execution environment and little else
sqlite> .do .conn
ACTIVE 0: :memory:
sqlite> .do ".. puts Putting"
Putting
sqlite> # Be sure about being in shell execution environment.
sqlite> .
sqlite> # Oh, the prompt would have sufficed. Now, for some Tcl ...
sqlite> ..
tcl% # Let's see what that .. command does.
tcl% ..
tcl% # Apparently, it either gets to the Tcl environment or stays there.
```
The use of lone . and .. to switch environments is easiest to understand.
However, as explained in the next section,
use of .. not alone on an input line exploits other Tcl functionality.
Another effect of loading tclshext is that a new dot command, .tcl ,
becomes part of the shell's repertoire.
When entered without any argument(s)
from the shell execution environment,
the .tcl command has the same effect as .. by itself.
(^When run from the Tcl execution environment with no arguments,
.tcl acts as a no-op rather than entering a recursive REPL.)
With arguments, it will read file content into the Tcl interpreter
just as Tcl's source command would.
This may be useful for getting the Tcl execution environment
customized via sqlite3x invocation options such as
"'-cmd'&nbsp;'.tcl&nbsp;my_sqlite3xt_init.tcl'"
A side benefit of the .tcl command's existence is that
it appears in .help output, which can remind users how
to use the extension's main feature.
## Momentary Use of Tcl Execution Environment ##
When .. is not the sole non-whitespace content of an input line group
which has been submitted from the shell execution environment,
that causes argument collection and expansion to be performed
according to Tcl rules by the Tcl interpreter,
without entering and staying in the Tcl execution environment.
These two variations exist (where "..." stands for the provided argument set):
```
sqlite> ..dotcmd ...
or
sqlite> .. tclcmd ...
```
The former, where no space separates .. from dotcmd, causes the
dot command known as .dotcmd to be executed (if it can be found.)
The latter, where space separates .. from tclcmd, causes the
Tcl command known as tclcmd to be executed (if it can be found.)
In either case, when the execution terminates (or fails with a
"command does not yet exist" or "invalid command name" error),
the shell execution environment remains in effect afterward.
This temporary use of the Tcl interpreter serves two purposes.
One is to exploit the more powerful capabilities of Tcl for argument processing.
Within the text of arguments as provided,
variables can be accessed, computations can be done,
and Tcl commands can be invoked to yield results (or produce side effects),
all of which can affect what the expanded arguments finally contain.
The other (miniscule) effect is to avoid the need for extra
input lines to switch to the Tcl execution environment and back
when "one-shot" use of Tcl is all that is needed.
In other words:<br>
```
..dotcmd whatever ...
acts as a shorter form of
..
.dotcmd whatever ...
.
```
Whenever '..' leads an input line group
submitted in the shell execution environment,
then most of that input is given to the Tcl interpreter for
processing on a one-shot basis.
## Cross-Execution Environment Interactions ##
Study of the above examples might give rise to these questions:<br>
&nbsp;&nbsp;"How can '..dotcmd ...' work in the Tcl execution environment?"<br>
and<br>
&nbsp;&nbsp;"Why does '.do anything' work in the shell execution environment?"<br>
There are two features at work behind those examples working:
In the Tcl execution environment, (as touched upon above),
when a prospective Tcl command is not found to be defined
in the namespace(s) searched by the interpreter, it is
passed to the Tcl command named "unknown".
In the tclshext-augmented Tcl environment,
that procedure is implemented by C code which treats
a purported command with a leading '.' specially,
by attempting to find it in the shell's repertoire of dot commands.
If found (unambiguously), it is then executed
and the result (such as it is) returned to the caller.
(The sqlite3 shell dot-commands return the empty result on success.)
So, assuming there is a dot command invokable as .dotcmd,
(which there could be if another extension provided it),
it can be found and executed from the Tcl execution environment
with arguments as collected and expanded by the Tcl interpreter.
In the shell execution environment, if any extensions have been
loaded when a prospective dot command is not found in the
existing repertoire of dot commands kept by the shell,
the shell's dispatcher gives the (undocumented) .unknown
dot command a chance to execute it. This has no chance
of succeeding unless some extension has overridden the
built-in .unknown implementation. However, tclshext does
override it, and the replacement attempts to find any
dot command name it is given in the Tcl root namespace.
If it can be found, (such as will be true for the above
example invoking .do from the shell execution environment),
then that command is executed as a Tcl command
with whatever arguments were passed to .unknown .
Because of how this works, there is not often any reason or need to
leave the Tcl execution environment. Dot commands and Tcl commands
can be freely intermixed and executed in that environment. The main
reason one might revert to the shell execution environment would
be to evaluate SQL statements in the usual, sqlite3 way, (by just
typing them followed by ";".) However, SQL can be evaluated without
leaving the Tcl execution environment by use of the recently added
dot command, .eval, which evaluates its arguments in the shell
execution environment. For example:<br>
```
tcl% .mode box
tcl% .eval {
> .print "Let's have a one,"
> select 1 as one;
> } {
> .print "and a two."
> select 2 as two;
> }
Let's have a one,
┌─────┐
│ one │
├─────┤
│ 1 │
└─────┘
and a two.
┌─────┐
│ two │
├─────┤
│ 2 │
└─────┘
tcl%
```
It should be noted, for those new to Tcl, that brace-quoting in Tcl
means "Whatever is between these matching braces is the (single) value."
It can be used for nearly any content, excepting explicit mismatched braces.
## Summary, More to Come ##
The Tcl extension should prove useful to those who like the features
of the sqlite3 shell but find using it programmatically to be challenging.
A future article will cover using Tk, a graphical user interface
toolkit originally created for use with Tcl, with sqlite3xt.
-284
View File
@@ -1,284 +0,0 @@
# The testrunner.tcl Script
# 1. Overview
testrunner.tcl is a Tcl script used to run multiple SQLite tests using
multiple jobs. It supports the following types of tests:
* Tcl test scripts.
* Tests run with [make] commands. Specifically, at time of writing,
[make fuzztest], [make mptest], [make sourcetest] and [make threadtest].
testrunner.tcl pipes the output of all tests and builds run into log file
**testrunner.log**, created in the cwd directory. Searching this file for
"failed" is a good way to find the output of a failed test.
testrunner.tcl also populates SQLite database **testrunner.db**. This database
contains details of all tests run, running and to be run. A useful query
might be:
```
SELECT * FROM script WHERE state='failed'
```
Running the command:
```
./testfixture $(TESTDIR)/testrunner.tcl status
```
in the directory containing the testrunner.db database runs various queries
to produce a succinct report on the state of a running testrunner.tcl script.
Running:
```
watch ./testfixture $(TESTDIR)/testrunner.tcl status
```
in another terminal is a good way to keep an eye on a long running test.
Sometimes testrunner.tcl uses the [testfixture] binary that it is run with
to run tests (see "Binary Tests" below). Sometimes it builds testfixture and
other binaries in specific configurations to test (see "Source Tests").
# 2. Binary Tests
The commands described in this section all run various combinations of the Tcl
test scripts using the [testfixture] binary used to run the testrunner.tcl
script (i.e. they do not invoke the compiler to build new binaries, or the
[make] command to run tests that are not Tcl scripts). The procedure to run
these tests is therefore:
1. Build the "testfixture" (or "testfixture.exe" for windows) binary using
whatever method seems convenient.
2. Test the binary built in step 1 by running testrunner.tcl with it,
perhaps with various options.
The following sub-sections describe the various options that can be
passed to testrunner.tcl to test binary testfixture builds.
## 2.1. Organization of Tcl Tests
Tcl tests are stored in files that match the pattern *\*.test*. They are
found in both the $TOP/test/ directory, and in the various sub-directories
of the $TOP/ext/ directory of the source tree. Not all *\*.test* files
contain Tcl tests - a handful are Tcl scripts designed to invoke other
*\*.test* files.
The **veryquick** set of tests is a subset of all Tcl test scripts in the
source tree. In includes most tests, but excludes some that are very slow.
Almost all fault-injection tests (those that test the response of the library
to OOM or IO errors) are excluded. It is defined in source file
*test/permutations.test*.
The **full** set of tests includes all Tcl test scripts in the source tree.
To run a "full" test is to run all Tcl test scripts that can be found in the
source tree.
File *permutations.test* defines various test "permutations". A permutation
consists of:
* A subset of Tcl test scripts, and
* Runtime configuration to apply before running each test script
(e.g. enabling auto-vacuum, or disable lookaside).
Running **all** tests is to run all tests in the full test set, plus a dozen
or so permutations. The specific permutations that are run as part of "all"
are defined in file *testrunner_data.tcl*.
## 2.2. Commands to Run Tests
To run the "veryquick" test set, use either of the following:
```
./testfixture $TESTDIR/testrunner.tcl
./testfixture $TESTDIR/testrunner.tcl veryquick
```
To run the "full" test suite:
```
./testfixture $TESTDIR/testrunner.tcl full
```
To run the subset of the "full" test suite for which the test file name matches
a specified pattern (e.g. all tests that start with "fts5"), either of:
```
./testfixture $TESTDIR/testrunner.tcl fts5%
./testfixture $TESTDIR/testrunner.tcl 'fts5*'
```
To run "all" tests (full + permutations):
```
./testfixture $TESTDIR/testrunner.tcl all
```
<a name=binary_test_failures></a>
## 2.3. Investigating Binary Test Failures
If a test fails, testrunner.tcl reports name of the Tcl test script and, if
applicable, the name of the permutation, to stdout. This information can also
be retrieved from either *testrunner.log* or *testrunner.db*.
If there is no permutation, the individual test script may be run with:
```
./testfixture $PATH_TO_SCRIPT
```
Or, if the failure occured as part of a permutation:
```
./testfixture $TESTDIR/testrunner.tcl $PERMUTATION $PATH_TO_SCRIPT
```
TODO: An example instead of "$PERMUTATION" and $PATH\_TO\_SCRIPT?
# 3. Source Code Tests
The commands described in this section invoke the C compiler to build
binaries from the source tree, then use those binaries to run Tcl and
other tests. The advantages of this are that:
* it is possible to test multiple build configurations with a single
command, and
* it ensures that tests are always run using binaries created with the
same set of compiler options.
The testrunner.tcl commands described in this section may be run using
either a *testfixture* (or testfixture.exe) build, or with any other Tcl
shell that supports SQLite 3.31.1 or newer via "package require sqlite3".
TODO: ./configure + Makefile.msc build systems.
## Commands to Run SQLite Tests
The **mdevtest** command is equivalent to running the veryquick tests and
the [make fuzztest] target once for each of two --enable-all builds - one
with debugging enabled and one without:
```
tclsh $TESTDIR/testrunner.tcl mdevtest
```
In other words, it is equivalent to running:
```
$TOP/configure --enable-all --enable-debug
make fuzztest
make testfixture
./testfixture $TOP/test/testrunner.tcl veryquick
# Then, after removing files created by the tests above:
$TOP/configure --enable-all OPTS="-O0"
make fuzztest
make testfixture
./testfixture $TOP/test/testrunner.tcl veryquick
```
The **sdevtest** command is identical to the mdevtest command, except that the
second of the two builds is a sanitizer build. Specifically, this means that
OPTS="-fsanitize=address,undefined" is specified instead of OPTS="-O0":
```
tclsh $TESTDIR/testrunner.tcl sdevtest
```
The **release** command runs lots of tests under lots of builds. It runs
different combinations of builds and tests depending on whether it is run
on Linux, Windows or OSX. Refer to *testrunner\_data.tcl* for the details
of the specific tests run.
```
tclsh $TESTDIR/testrunner.tcl release
```
## Running ZipVFS Tests
testrunner.tcl can build a zipvfs-enabled testfixture and use it to run
tests from the Zipvfs project with the following command:
```
tclsh $TESTDIR/testrunner.tcl --zipvfs $PATH_TO_ZIPVFS
```
This can be combined with any of "mdevtest", "sdevtest" or "release" to
test both SQLite and Zipvfs with a single command:
```
tclsh $TESTDIR/testrunner.tcl --zipvfs $PATH_TO_ZIPVFS mdevtest
```
## Investigating Source Code Test Failures
Investigating a test failure that occurs during source code testing is a
two step process:
1. Recreating the build configuration in which the test failed, and
2. Re-running the actual test.
To recreate a build configuration, use the testrunner.tcl **script** command
to create a build script. A build script is a bash script on Linux or OSX, or
a dos \*.bat file on windows. For example:
```
# Create a script that recreates build configuration "Device-One" on
# Linux or OSX:
tclsh $TESTDIR/testrunner.tcl script Device-One > make.sh
# Create a script that recreates build configuration "Have-Not" on Windows:
tclsh $TESTDIR/testrunner.tcl script Have-Not > make.bat
```
The generated bash or \*.bat file script accepts a single argument - a makefile
target to build. This may be used either to run a [make] command test directly,
or else to build a testfixture (or testfixture.exe) binary with which to
run a Tcl test script, as <a href=#binary_test_failures>described above</a>.
# 4. Controlling CPU Core Utilization
When running either binary or source code tests, testrunner.tcl reports the
number of jobs it intends to use to stdout. e.g.
```
$ ./testfixture $TESTDIR/testrunner.tcl
splitting work across 16 jobs
... more output ...
```
By default, testfixture.tcl attempts to set the number of jobs to the number
of real cores on the machine. This can be overridden using the "--jobs" (or -j)
switch:
```
$ ./testfixture $TESTDIR/testrunner.tcl --jobs 8
splitting work across 8 jobs
... more output ...
```
The number of jobs may also be changed while an instance of testrunner.tcl is
running by exucuting the following command from the directory containing the
testrunner.log and testrunner.db files:
```
$ ./testfixture $TESTDIR/testrunner.tcl njob $NEW_NUMBER_OF_JOBS
```
-19
View File
@@ -464,23 +464,4 @@ do_execsql_test 5.3 {
t2 t2_idx_0001295b {100 20 5}
}
if 0 {
do_test expert1-6.0 {
catchcmd :memory: {
.expert
select base64('');
.expert
select name from pragma_collation_list order by name collate uint;
}
} {0 {(no new indexes)
SCAN CONSTANT ROW
(no new indexes)
SCAN pragma_collation_list VIRTUAL TABLE INDEX 0:
USE TEMP B-TREE FOR ORDER BY
}}
}
finish_test
+2 -103
View File
@@ -32,7 +32,7 @@
#endif /* !defined(SQLITE_AMALGAMATION) */
#ifndef SQLITE_OMIT_VIRTUALTABLE
#ifndef SQLITE_OMIT_VIRTUALTABLE
typedef sqlite3_int64 i64;
typedef sqlite3_uint64 u64;
@@ -662,7 +662,6 @@ static int idxRegisterVtab(sqlite3expert *p){
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0, /* xIntegrity */
};
return sqlite3_create_module(p->dbv, "expert", &expertModule, (void*)p);
@@ -1819,88 +1818,6 @@ static int idxPopulateStat1(sqlite3expert *p, char **pzErr){
return rc;
}
/*
** Define and possibly pretend to use a useless collation sequence.
** This pretense allows expert to accept SQL using custom collations.
*/
int dummyCompare(void *up1, int up2, const void *up3, int up4, const void *up5){
(void)up1;
(void)up2;
(void)up3;
(void)up4;
(void)up5;
assert(0); /* VDBE should never be run. */
return 0;
}
/* And a callback to register above upon actual need */
void useDummyCS(void *up1, sqlite3 *db, int etr, const char *zName){
(void)up1;
sqlite3_create_collation_v2(db, zName, etr, 0, dummyCompare, 0);
}
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) \
&& !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS)
/*
** dummy functions for no-op implementation of UDFs during expert's work
*/
void dummyUDF(sqlite3_context *up1, int up2, sqlite3_value **up3){
(void)up1;
(void)up2;
(void)up3;
assert(0); /* VDBE should never be run. */
}
void dummyUDFvalue(sqlite3_context *up1){
(void)up1;
assert(0); /* VDBE should never be run. */
}
/*
** Register UDFs from user database with another.
*/
int registerUDFs(sqlite3 *dbSrc, sqlite3 *dbDst){
sqlite3_stmt *pStmt;
int rc = sqlite3_prepare_v2(dbSrc,
"SELECT name,type,enc,narg,flags "
"FROM pragma_function_list() "
"WHERE builtin==0", -1, &pStmt, 0);
if( rc==SQLITE_OK ){
while( SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){
int nargs = sqlite3_column_int(pStmt,3);
int flags = sqlite3_column_int(pStmt,4);
const char *name = (char*)sqlite3_column_text(pStmt,0);
const char *type = (char*)sqlite3_column_text(pStmt,1);
const char *enc = (char*)sqlite3_column_text(pStmt,2);
if( name==0 || type==0 || enc==0 ){
/* no-op. Only happens on OOM */
}else{
int ienc = SQLITE_UTF8;
int rcf = SQLITE_ERROR;
if( strcmp(enc,"utf16le")==0 ) ienc = SQLITE_UTF16LE;
else if( strcmp(enc,"utf16be")==0 ) ienc = SQLITE_UTF16BE;
ienc |= (flags & (SQLITE_DETERMINISTIC|SQLITE_DIRECTONLY));
if( strcmp(type,"w")==0 ){
rcf = sqlite3_create_window_function(dbDst,name,nargs,ienc,0,
dummyUDF,dummyUDFvalue,0,0,0);
}else if( strcmp(type,"a")==0 ){
rcf = sqlite3_create_function(dbDst,name,nargs,ienc,0,
0,dummyUDF,dummyUDFvalue);
}else if( strcmp(type,"s")==0 ){
rcf = sqlite3_create_function(dbDst,name,nargs,ienc,0,
dummyUDF,0,0);
}
if( rcf!=SQLITE_OK ){
rc = rcf;
break;
}
}
}
sqlite3_finalize(pStmt);
if( rc==SQLITE_DONE ) rc = SQLITE_OK;
}
return rc;
}
#endif
/*
** Allocate a new sqlite3expert object.
*/
@@ -1927,21 +1844,7 @@ sqlite3expert *sqlite3_expert_new(sqlite3 *db, char **pzErrmsg){
sqlite3_db_config(pNew->dbm, SQLITE_DBCONFIG_TRIGGER_EQP, 1, (int*)0);
}
}
/* Allow custom collations to be dealt with through prepare. */
if( rc==SQLITE_OK ) rc = sqlite3_collation_needed(pNew->dbm,0,useDummyCS);
if( rc==SQLITE_OK ) rc = sqlite3_collation_needed(pNew->dbv,0,useDummyCS);
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) \
&& !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS)
/* Register UDFs from database [db] with [dbm] and [dbv]. */
if( rc==SQLITE_OK ){
rc = registerUDFs(pNew->db, pNew->dbm);
}
if( rc==SQLITE_OK ){
rc = registerUDFs(pNew->db, pNew->dbv);
}
#endif
/* Copy the entire schema of database [db] into [dbm]. */
if( rc==SQLITE_OK ){
@@ -2017,10 +1920,6 @@ int sqlite3_expert_sql(
while( rc==SQLITE_OK && zStmt && zStmt[0] ){
sqlite3_stmt *pStmt = 0;
/* Ensure that the provided statement compiles against user's DB. */
rc = idxPrepareStmt(p->db, &pStmt, pzErr, zStmt);
if( rc!=SQLITE_OK ) break;
sqlite3_finalize(pStmt);
rc = sqlite3_prepare_v2(p->dbv, zStmt, -1, &pStmt, &zStmt);
if( rc==SQLITE_OK ){
if( pStmt ){
+17 -81
View File
@@ -640,7 +640,6 @@ static void fts3DeclareVtab(int *pRc, Fts3Table *p){
zLanguageid = (p->zLanguageid ? p->zLanguageid : "__langid");
sqlite3_vtab_config(p->db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
sqlite3_vtab_config(p->db, SQLITE_VTAB_INNOCUOUS);
/* Create a list of user columns for the virtual table */
zCols = sqlite3_mprintf("%Q, ", p->azColumn[0]);
@@ -3890,8 +3889,6 @@ static int fts3RenameMethod(
rc = sqlite3Fts3PendingTermsFlush(p);
}
p->bIgnoreSavepoint = 1;
if( p->zContentTbl==0 ){
fts3DbExec(&rc, db,
"ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';",
@@ -3919,8 +3916,6 @@ static int fts3RenameMethod(
"ALTER TABLE %Q.'%q_segdir' RENAME TO '%q_segdir';",
p->zDb, p->zName, zName
);
p->bIgnoreSavepoint = 0;
return rc;
}
@@ -3931,28 +3926,12 @@ static int fts3RenameMethod(
*/
static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
int rc = SQLITE_OK;
Fts3Table *pTab = (Fts3Table*)pVtab;
assert( pTab->inTransaction );
assert( pTab->mxSavepoint<=iSavepoint );
TESTONLY( pTab->mxSavepoint = iSavepoint );
if( pTab->bIgnoreSavepoint==0 ){
if( fts3HashCount(&pTab->aIndex[0].hPending)>0 ){
char *zSql = sqlite3_mprintf("INSERT INTO %Q.%Q(%Q) VALUES('flush')",
pTab->zDb, pTab->zName, pTab->zName
);
if( zSql ){
pTab->bIgnoreSavepoint = 1;
rc = sqlite3_exec(pTab->db, zSql, 0, 0, 0);
pTab->bIgnoreSavepoint = 0;
sqlite3_free(zSql);
}else{
rc = SQLITE_NOMEM;
}
}
if( rc==SQLITE_OK ){
pTab->iSavepoint = iSavepoint+1;
}
UNUSED_PARAMETER(iSavepoint);
assert( ((Fts3Table *)pVtab)->inTransaction );
assert( ((Fts3Table *)pVtab)->mxSavepoint <= iSavepoint );
TESTONLY( ((Fts3Table *)pVtab)->mxSavepoint = iSavepoint );
if( ((Fts3Table *)pVtab)->bIgnoreSavepoint==0 ){
rc = fts3SyncMethod(pVtab);
}
return rc;
}
@@ -3963,11 +3942,12 @@ static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
** This is a no-op.
*/
static int fts3ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
Fts3Table *pTab = (Fts3Table*)pVtab;
assert( pTab->inTransaction );
assert( pTab->mxSavepoint >= iSavepoint );
TESTONLY( pTab->mxSavepoint = iSavepoint-1 );
pTab->iSavepoint = iSavepoint;
TESTONLY( Fts3Table *p = (Fts3Table*)pVtab );
UNUSED_PARAMETER(iSavepoint);
UNUSED_PARAMETER(pVtab);
assert( p->inTransaction );
assert( p->mxSavepoint >= iSavepoint );
TESTONLY( p->mxSavepoint = iSavepoint-1 );
return SQLITE_OK;
}
@@ -3977,13 +3957,11 @@ static int fts3ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
** Discard the contents of the pending terms table.
*/
static int fts3RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
Fts3Table *pTab = (Fts3Table*)pVtab;
Fts3Table *p = (Fts3Table*)pVtab;
UNUSED_PARAMETER(iSavepoint);
assert( pTab->inTransaction );
TESTONLY( pTab->mxSavepoint = iSavepoint );
if( (iSavepoint+1)<=pTab->iSavepoint ){
sqlite3Fts3PendingTermsClear(pTab);
}
assert( p->inTransaction );
TESTONLY( p->mxSavepoint = iSavepoint );
sqlite3Fts3PendingTermsClear(p);
return SQLITE_OK;
}
@@ -4002,49 +3980,8 @@ static int fts3ShadowName(const char *zName){
return 0;
}
/*
** Implementation of the xIntegrity() method on the FTS3/FTS4 virtual
** table.
*/
static int fts3Integrity(
sqlite3_vtab *pVtab, /* The virtual table to be checked */
const char *zSchema, /* Name of schema in which pVtab lives */
const char *zTabname, /* Name of the pVTab table */
int isQuick, /* True if this is a quick_check */
char **pzErr /* Write error message here */
){
Fts3Table *p = (Fts3Table*)pVtab;
char *zSql;
int rc;
char *zErr = 0;
assert( pzErr!=0 );
assert( *pzErr==0 );
UNUSED_PARAMETER(isQuick);
zSql = sqlite3_mprintf(
"INSERT INTO \"%w\".\"%w\"(\"%w\") VALUES('integrity-check');",
zSchema, zTabname, zTabname);
if( zSql==0 ){
return SQLITE_NOMEM;
}
rc = sqlite3_exec(p->db, zSql, 0, 0, &zErr);
sqlite3_free(zSql);
if( (rc&0xff)==SQLITE_CORRUPT ){
*pzErr = sqlite3_mprintf("malformed inverted index for FTS%d table %s.%s",
p->bFts4 ? 4 : 3, zSchema, zTabname);
}else if( rc!=SQLITE_OK ){
*pzErr = sqlite3_mprintf("unable to validate the inverted index for"
" FTS%d table %s.%s: %s",
p->bFts4 ? 4 : 3, zSchema, zTabname, zErr);
}
sqlite3_free(zErr);
return SQLITE_OK;
}
static const sqlite3_module fts3Module = {
/* iVersion */ 4,
/* iVersion */ 3,
/* xCreate */ fts3CreateMethod,
/* xConnect */ fts3ConnectMethod,
/* xBestIndex */ fts3BestIndexMethod,
@@ -4068,7 +4005,6 @@ static const sqlite3_module fts3Module = {
/* xRelease */ fts3ReleaseMethod,
/* xRollbackTo */ fts3RollbackToMethod,
/* xShadowName */ fts3ShadowName,
/* xIntegrity */ fts3Integrity,
};
/*
-1
View File
@@ -265,7 +265,6 @@ struct Fts3Table {
int nPgsz; /* Page size for host database */
char *zSegmentsTbl; /* Name of %_segments table */
sqlite3_blob *pSegments; /* Blob handle open on %_segments table */
int iSavepoint;
/*
** The following array of hash tables is used to buffer pending index
+1 -2
View File
@@ -545,8 +545,7 @@ int sqlite3Fts3InitAux(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
0 /* xShadowName */
};
int rc; /* Return code */
+1 -2
View File
@@ -362,8 +362,7 @@ int sqlite3Fts3InitTerm(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
0 /* xShadowName */
};
int rc; /* Return code */
+1 -2
View File
@@ -445,8 +445,7 @@ int sqlite3Fts3InitTok(sqlite3 *db, Fts3Hash *pHash, void(*xDestroy)(void*)){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
0 /* xShadowName */
};
int rc; /* Return code */
Regular → Executable
+8 -20
View File
@@ -3325,6 +3325,7 @@ int sqlite3Fts3PendingTermsFlush(Fts3Table *p){
rc = fts3SegmentMerge(p, p->iPrevLangid, i, FTS3_SEGCURSOR_PENDING);
if( rc==SQLITE_DONE ) rc = SQLITE_OK;
}
sqlite3Fts3PendingTermsClear(p);
/* Determine the auto-incr-merge setting if unknown. If enabled,
** estimate the number of leaf blocks of content to be written
@@ -3346,10 +3347,6 @@ int sqlite3Fts3PendingTermsFlush(Fts3Table *p){
rc = sqlite3_reset(pStmt);
}
}
if( rc==SQLITE_OK ){
sqlite3Fts3PendingTermsClear(p);
}
return rc;
}
@@ -3981,8 +3978,6 @@ static int fts3AppendToNode(
blobGrowBuffer(pPrev, nTerm, &rc);
if( rc!=SQLITE_OK ) return rc;
assert( pPrev!=0 );
assert( pPrev->a!=0 );
nPrefix = fts3PrefixCompress(pPrev->a, pPrev->n, zTerm, nTerm);
nSuffix = nTerm - nPrefix;
@@ -4039,13 +4034,9 @@ static int fts3IncrmergeAppend(
nSpace += sqlite3Fts3VarintLen(nDoclist) + nDoclist;
/* If the current block is not empty, and if adding this term/doclist
** to the current block would make it larger than Fts3Table.nNodeSize bytes,
** and if there is still room for another leaf page, write this block out to
** the database. */
if( pLeaf->block.n>0
&& (pLeaf->block.n + nSpace)>p->nNodeSize
&& pLeaf->iBlock < (pWriter->iStart + pWriter->nLeafEst)
){
** to the current block would make it larger than Fts3Table.nNodeSize
** bytes, write this block out to the database. */
if( pLeaf->block.n>0 && (pLeaf->block.n + nSpace)>p->nNodeSize ){
rc = fts3WriteSegment(p, pLeaf->iBlock, pLeaf->block.a, pLeaf->block.n);
pWriter->nWork++;
@@ -4377,7 +4368,7 @@ static int fts3IncrmergeLoad(
rc = sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock,0);
blobGrowBuffer(&pNode->block,
MAX(nBlock, p->nNodeSize)+FTS3_NODE_PADDING, &rc
);
);
if( rc==SQLITE_OK ){
memcpy(pNode->block.a, aBlock, nBlock);
pNode->block.n = nBlock;
@@ -5227,7 +5218,7 @@ static u64 fts3ChecksumIndex(
int rc;
u64 cksum = 0;
if( *pRc ) return 0;
assert( *pRc==SQLITE_OK );
memset(&filter, 0, sizeof(filter));
memset(&csr, 0, sizeof(csr));
@@ -5442,11 +5433,8 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
rc = fts3DoIncrmerge(p, &zVal[6]);
}else if( nVal>10 && 0==sqlite3_strnicmp(zVal, "automerge=", 10) ){
rc = fts3DoAutoincrmerge(p, &zVal[10]);
}else if( nVal==5 && 0==sqlite3_strnicmp(zVal, "flush", 5) ){
rc = sqlite3Fts3PendingTermsFlush(p);
}
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
else{
}else{
int v;
if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){
v = atoi(&zVal[9]);
@@ -5464,8 +5452,8 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
if( v>=4 && v<=FTS3_MERGE_COUNT && (v&1)==0 ) p->nMergeCount = v;
rc = SQLITE_OK;
}
}
#endif
}
return rc;
}
+6 -6
View File
@@ -263,7 +263,7 @@ struct Fts5PhraseIter {
** See xPhraseFirstColumn above.
*/
struct Fts5ExtensionApi {
int iVersion; /* Currently always set to 2 */
int iVersion; /* Currently always set to 3 */
void *(*xUserData)(Fts5Context*);
@@ -492,8 +492,8 @@ struct Fts5ExtensionApi {
** as separate queries of the FTS index are required for each synonym.
**
** When using methods (2) or (3), it is important that the tokenizer only
** provide synonyms when tokenizing document text (method (3)) or query
** text (method (2)), not both. Doing so will not cause any errors, but is
** provide synonyms when tokenizing document text (method (2)) or query
** text (method (3)), not both. Doing so will not cause any errors, but is
** inefficient.
*/
typedef struct Fts5Tokenizer Fts5Tokenizer;
@@ -541,7 +541,7 @@ struct fts5_api {
int (*xCreateTokenizer)(
fts5_api *pApi,
const char *zName,
void *pUserData,
void *pContext,
fts5_tokenizer *pTokenizer,
void (*xDestroy)(void*)
);
@@ -550,7 +550,7 @@ struct fts5_api {
int (*xFindTokenizer)(
fts5_api *pApi,
const char *zName,
void **ppUserData,
void **ppContext,
fts5_tokenizer *pTokenizer
);
@@ -558,7 +558,7 @@ struct fts5_api {
int (*xCreateFunction)(
fts5_api *pApi,
const char *zName,
void *pUserData,
void *pContext,
fts5_extension_function xFunction,
void (*xDestroy)(void*)
);
-15
View File
@@ -154,10 +154,6 @@ typedef struct Fts5Config Fts5Config;
** attempt to merge together. A value of 1 sets the object to use the
** compile time default. Zero disables auto-merge altogether.
**
** bContentlessDelete:
** True if the contentless_delete option was present in the CREATE
** VIRTUAL TABLE statement.
**
** zContent:
**
** zContentRowid:
@@ -192,7 +188,6 @@ struct Fts5Config {
int nPrefix; /* Number of prefix indexes */
int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */
int eContent; /* An FTS5_CONTENT value */
int bContentlessDelete; /* "contentless_delete=" option (dflt==0) */
char *zContent; /* content table */
char *zContentRowid; /* "content_rowid=" option value */
int bColumnsize; /* "columnsize=" option value (dflt==1) */
@@ -214,7 +209,6 @@ struct Fts5Config {
char *zRank; /* Name of rank function */
char *zRankArgs; /* Arguments to rank function */
int bSecureDelete; /* 'secure-delete' */
int nDeleteMerge; /* 'deletemerge' */
/* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */
char **pzErrmsg;
@@ -537,9 +531,6 @@ int sqlite3Fts5IndexReset(Fts5Index *p);
int sqlite3Fts5IndexLoadConfig(Fts5Index *p);
int sqlite3Fts5IndexGetOrigin(Fts5Index *p, i64 *piOrigin);
int sqlite3Fts5IndexContentlessDelete(Fts5Index *p, i64 iOrigin, i64 iRowid);
/*
** End of interface to code in fts5_index.c.
**************************************************************************/
@@ -624,11 +615,6 @@ int sqlite3Fts5HashWrite(
*/
void sqlite3Fts5HashClear(Fts5Hash*);
/*
** Return true if the hash is empty, false otherwise.
*/
int sqlite3Fts5HashIsEmpty(Fts5Hash*);
int sqlite3Fts5HashQuery(
Fts5Hash*, /* Hash table to query */
int nPre,
@@ -650,7 +636,6 @@ void sqlite3Fts5HashScanEntry(Fts5Hash *,
);
/*
** End of interface to code in fts5_hash.c.
**************************************************************************/
+12 -47
View File
@@ -110,19 +110,15 @@ static int fts5CInstIterInit(
*/
typedef struct HighlightContext HighlightContext;
struct HighlightContext {
/* Constant parameters to fts5HighlightCb() */
CInstIter iter; /* Coalesced Instance Iterator */
int iPos; /* Current token offset in zIn[] */
int iRangeStart; /* First token to include */
int iRangeEnd; /* If non-zero, last token to include */
const char *zOpen; /* Opening highlight */
const char *zClose; /* Closing highlight */
const char *zIn; /* Input text */
int nIn; /* Size of input text in bytes */
/* Variables modified by fts5HighlightCb() */
CInstIter iter; /* Coalesced Instance Iterator */
int iPos; /* Current token offset in zIn[] */
int iOff; /* Have copied up to this offset in zIn[] */
int bOpen; /* True if highlight is open */
int iOff; /* Current offset within zIn[] */
char *zOut; /* Output value */
};
@@ -155,8 +151,8 @@ static int fts5HighlightCb(
int tflags, /* Mask of FTS5_TOKEN_* flags */
const char *pToken, /* Buffer containing token */
int nToken, /* Size of token in bytes */
int iStartOff, /* Start byte offset of token */
int iEndOff /* End byte offset of token */
int iStartOff, /* Start offset of token */
int iEndOff /* End offset of token */
){
HighlightContext *p = (HighlightContext*)pContext;
int rc = SQLITE_OK;
@@ -172,55 +168,30 @@ static int fts5HighlightCb(
if( p->iRangeStart && iPos==p->iRangeStart ) p->iOff = iStartOff;
}
/* If the parenthesis is open, and this token is not part of the current
** phrase, and the starting byte offset of this token is past the point
** that has currently been copied into the output buffer, close the
** parenthesis. */
if( p->bOpen
&& (iPos<=p->iter.iStart || p->iter.iStart<0)
&& iStartOff>p->iOff
){
fts5HighlightAppend(&rc, p, p->zClose, -1);
p->bOpen = 0;
}
/* If this is the start of a new phrase, and the highlight is not open:
**
** * copy text from the input up to the start of the phrase, and
** * open the highlight.
*/
if( iPos==p->iter.iStart && p->bOpen==0 ){
if( iPos==p->iter.iStart ){
fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iStartOff - p->iOff);
fts5HighlightAppend(&rc, p, p->zOpen, -1);
p->iOff = iStartOff;
p->bOpen = 1;
}
if( iPos==p->iter.iEnd ){
if( p->bOpen==0 ){
assert( p->iRangeEnd>=0 );
if( p->iRangeEnd>=0 && p->iter.iStart<p->iRangeStart ){
fts5HighlightAppend(&rc, p, p->zOpen, -1);
p->bOpen = 1;
}
fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
fts5HighlightAppend(&rc, p, p->zClose, -1);
p->iOff = iEndOff;
if( rc==SQLITE_OK ){
rc = fts5CInstIterNext(&p->iter);
}
}
if( iPos==p->iRangeEnd ){
if( p->bOpen ){
if( p->iter.iStart>=0 && iPos>=p->iter.iStart ){
fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
p->iOff = iEndOff;
}
fts5HighlightAppend(&rc, p, p->zClose, -1);
p->bOpen = 0;
}
if( p->iRangeEnd>=0 && iPos==p->iRangeEnd ){
fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
p->iOff = iEndOff;
if( iPos>=p->iter.iStart && iPos<p->iter.iEnd ){
fts5HighlightAppend(&rc, p, p->zClose, -1);
}
}
return rc;
@@ -261,9 +232,6 @@ static void fts5HighlightFunction(
if( rc==SQLITE_OK ){
rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb);
}
if( ctx.bOpen ){
fts5HighlightAppend(&rc, &ctx, ctx.zClose, -1);
}
fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff);
if( rc==SQLITE_OK ){
@@ -542,9 +510,6 @@ static void fts5SnippetFunction(
if( rc==SQLITE_OK ){
rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb);
}
if( ctx.bOpen ){
fts5HighlightAppend(&rc, &ctx, ctx.zClose, -1);
}
if( ctx.iRangeEnd>=(nColSize-1) ){
fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff);
}else{
-47
View File
@@ -22,8 +22,6 @@
#define FTS5_DEFAULT_CRISISMERGE 16
#define FTS5_DEFAULT_HASHSIZE (1024*1024)
#define FTS5_DEFAULT_DELETE_AUTOMERGE 10 /* default 10% */
/* Maximum allowed page size */
#define FTS5_MAX_PAGE_SIZE (64*1024)
@@ -354,16 +352,6 @@ static int fts5ConfigParseSpecial(
return rc;
}
if( sqlite3_strnicmp("contentless_delete", zCmd, nCmd)==0 ){
if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){
*pzErr = sqlite3_mprintf("malformed contentless_delete=... directive");
rc = SQLITE_ERROR;
}else{
pConfig->bContentlessDelete = (zArg[0]=='1');
}
return rc;
}
if( sqlite3_strnicmp("content_rowid", zCmd, nCmd)==0 ){
if( pConfig->zContentRowid ){
*pzErr = sqlite3_mprintf("multiple content_rowid=... directives");
@@ -608,28 +596,6 @@ int sqlite3Fts5ConfigParse(
sqlite3_free(zTwo);
}
/* We only allow contentless_delete=1 if the table is indeed contentless. */
if( rc==SQLITE_OK
&& pRet->bContentlessDelete
&& pRet->eContent!=FTS5_CONTENT_NONE
){
*pzErr = sqlite3_mprintf(
"contentless_delete=1 requires a contentless table"
);
rc = SQLITE_ERROR;
}
/* We only allow contentless_delete=1 if columnsize=0 is not present.
**
** This restriction may be removed at some point.
*/
if( rc==SQLITE_OK && pRet->bContentlessDelete && pRet->bColumnsize==0 ){
*pzErr = sqlite3_mprintf(
"contentless_delete=1 is incompatible with columnsize=0"
);
rc = SQLITE_ERROR;
}
/* If a tokenizer= option was successfully parsed, the tokenizer has
** already been allocated. Otherwise, allocate an instance of the default
** tokenizer (unicode61) now. */
@@ -924,18 +890,6 @@ int sqlite3Fts5ConfigSetValue(
}
}
else if( 0==sqlite3_stricmp(zKey, "deletemerge") ){
int nVal = -1;
if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
nVal = sqlite3_value_int(pVal);
}else{
*pbBadkey = 1;
}
if( nVal<0 ) nVal = FTS5_DEFAULT_DELETE_AUTOMERGE;
if( nVal>100 ) nVal = 0;
pConfig->nDeleteMerge = nVal;
}
else if( 0==sqlite3_stricmp(zKey, "rank") ){
const char *zIn = (const char*)sqlite3_value_text(pVal);
char *zRank;
@@ -984,7 +938,6 @@ int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){
pConfig->nUsermerge = FTS5_DEFAULT_USERMERGE;
pConfig->nCrisisMerge = FTS5_DEFAULT_CRISISMERGE;
pConfig->nHashSize = FTS5_DEFAULT_HASHSIZE;
pConfig->nDeleteMerge = FTS5_DEFAULT_DELETE_AUTOMERGE;
zSql = sqlite3Fts5Mprintf(&rc, zSelect, pConfig->zDb, pConfig->zName);
if( zSql ){
Regular → Executable
+3 -5
View File
@@ -2477,7 +2477,7 @@ Fts5ExprNode *sqlite3Fts5ParseImplicitAnd(
return pRet;
}
#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
#ifdef SQLITE_TEST
static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){
sqlite3_int64 nByte = 0;
Fts5ExprTerm *p;
@@ -2583,8 +2583,6 @@ static char *fts5ExprPrintTcl(
if( zRet==0 ) return 0;
}
}else if( pExpr->eType==0 ){
zRet = sqlite3_mprintf("{}");
}else{
char const *zOp = 0;
int i;
@@ -2846,14 +2844,14 @@ static void fts5ExprFold(
sqlite3_result_int(pCtx, sqlite3Fts5UnicodeFold(iCode, bRemoveDiacritics));
}
}
#endif /* if SQLITE_TEST || SQLITE_FTS5_DEBUG */
#endif /* ifdef SQLITE_TEST */
/*
** This is called during initialization to register the fts5_expr() scalar
** UDF with the SQLite handle passed as the only argument.
*/
int sqlite3Fts5ExprInit(Fts5Global *pGlobal, sqlite3 *db){
#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
#ifdef SQLITE_TEST
struct Fts5ExprFunc {
const char *z;
void (*x)(sqlite3_context*,int,sqlite3_value**);
+5 -24
View File
@@ -432,8 +432,10 @@ static Fts5HashEntry *fts5HashEntryMerge(
}
/*
** Link all tokens from hash table iHash into a list in sorted order. The
** tokens are not removed from the hash table.
** Extract all tokens from hash table iHash and link them into a list
** in sorted order. The hash table is cleared before returning. It is
** the responsibility of the caller to free the elements of the returned
** list.
*/
static int fts5HashEntrySort(
Fts5Hash *pHash,
@@ -473,6 +475,7 @@ static int fts5HashEntrySort(
pList = fts5HashEntryMerge(pList, ap[i]);
}
pHash->nEntry = 0;
sqlite3_free(ap);
*ppSorted = pList;
return SQLITE_OK;
@@ -526,28 +529,6 @@ int sqlite3Fts5HashScanInit(
return fts5HashEntrySort(p, pTerm, nTerm, &p->pScan);
}
#ifdef SQLITE_DEBUG
static int fts5HashCount(Fts5Hash *pHash){
int nEntry = 0;
int ii;
for(ii=0; ii<pHash->nSlot; ii++){
Fts5HashEntry *p = 0;
for(p=pHash->aSlot[ii]; p; p=p->pHashNext){
nEntry++;
}
}
return nEntry;
}
#endif
/*
** Return true if the hash table is empty, false otherwise.
*/
int sqlite3Fts5HashIsEmpty(Fts5Hash *pHash){
assert( pHash->nEntry==fts5HashCount(pHash) );
return pHash->nEntry==0;
}
void sqlite3Fts5HashScanNext(Fts5Hash *p){
assert( !sqlite3Fts5HashScanEof(p) );
p->pScan = p->pScan->pScanNext;
Regular → Executable
+318 -1388
View File
File diff suppressed because it is too large Load Diff
+19 -131
View File
@@ -117,8 +117,6 @@ struct Fts5FullTable {
Fts5Storage *pStorage; /* Document store */
Fts5Global *pGlobal; /* Global (connection wide) data */
Fts5Cursor *pSortCsr; /* Sort data from this cursor */
int iSavepoint; /* Successful xSavepoint()+1 */
int bInSavepoint;
#ifdef SQLITE_DEBUG
struct Fts5TransactionState ts;
#endif
@@ -407,13 +405,6 @@ static int fts5InitVtab(
pConfig->pzErrmsg = 0;
}
if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
rc = sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, (int)1);
}
if( rc==SQLITE_OK ){
rc = sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
}
if( rc!=SQLITE_OK ){
fts5FreeVtab(pTab);
pTab = 0;
@@ -1338,9 +1329,6 @@ static int fts5FilterMethod(
pCsr->iFirstRowid = fts5GetRowidLimit(pRowidGe, SMALLEST_INT64);
}
rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
if( rc!=SQLITE_OK ) goto filter_out;
if( pTab->pSortCsr ){
/* If pSortCsr is non-NULL, then this call is being made as part of
** processing for a "... MATCH <expr> ORDER BY rank" query (ePlan is
@@ -1363,7 +1351,6 @@ static int fts5FilterMethod(
pCsr->pExpr = pTab->pSortCsr->pExpr;
rc = fts5CursorFirst(pTab, pCsr, bDesc);
}else if( pCsr->pExpr ){
assert( rc==SQLITE_OK );
rc = fts5CursorParseRank(pConfig, pCsr, pRank);
if( rc==SQLITE_OK ){
if( bOrderByRank ){
@@ -1535,7 +1522,6 @@ static int fts5SpecialInsert(
Fts5Config *pConfig = pTab->p.pConfig;
int rc = SQLITE_OK;
int bError = 0;
int bLoadConfig = 0;
if( 0==sqlite3_stricmp("delete-all", zCmd) ){
if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
@@ -1547,7 +1533,6 @@ static int fts5SpecialInsert(
}else{
rc = sqlite3Fts5StorageDeleteAll(pTab->pStorage);
}
bLoadConfig = 1;
}else if( 0==sqlite3_stricmp("rebuild", zCmd) ){
if( pConfig->eContent==FTS5_CONTENT_NONE ){
fts5SetVtabError(pTab,
@@ -1557,7 +1542,6 @@ static int fts5SpecialInsert(
}else{
rc = sqlite3Fts5StorageRebuild(pTab->pStorage);
}
bLoadConfig = 1;
}else if( 0==sqlite3_stricmp("optimize", zCmd) ){
rc = sqlite3Fts5StorageOptimize(pTab->pStorage);
}else if( 0==sqlite3_stricmp("merge", zCmd) ){
@@ -1570,8 +1554,6 @@ static int fts5SpecialInsert(
}else if( 0==sqlite3_stricmp("prefix-index", zCmd) ){
pConfig->bPrefixIndex = sqlite3_value_int(pVal);
#endif
}else if( 0==sqlite3_stricmp("flush", zCmd) ){
rc = sqlite3Fts5FlushToDisk(&pTab->p);
}else{
rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
if( rc==SQLITE_OK ){
@@ -1585,12 +1567,6 @@ static int fts5SpecialInsert(
}
}
}
if( rc==SQLITE_OK && bLoadConfig ){
pTab->p.pConfig->iCookie--;
rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
}
return rc;
}
@@ -1648,6 +1624,7 @@ static int fts5UpdateMethod(
int eType0; /* value_type() of apVal[0] */
int rc = SQLITE_OK; /* Return code */
int bUpdateOrDelete = 0;
/* A transaction must be open when this is called. */
assert( pTab->ts.eState==1 || pTab->ts.eState==2 );
@@ -1677,14 +1654,7 @@ static int fts5UpdateMethod(
if( pConfig->eContent!=FTS5_CONTENT_NORMAL
&& 0==sqlite3_stricmp("delete", z)
){
if( pConfig->bContentlessDelete ){
fts5SetVtabError(pTab,
"'delete' may not be used with a contentless_delete=1 table"
);
rc = SQLITE_ERROR;
}else{
rc = fts5SpecialDelete(pTab, apVal);
}
rc = fts5SpecialDelete(pTab, apVal);
}else{
rc = fts5SpecialInsert(pTab, z, apVal[2 + pConfig->nCol + 1]);
}
@@ -1701,7 +1671,7 @@ static int fts5UpdateMethod(
** Cases 3 and 4 may violate the rowid constraint.
*/
int eConflict = SQLITE_ABORT;
if( pConfig->eContent==FTS5_CONTENT_NORMAL || pConfig->bContentlessDelete ){
if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
eConflict = sqlite3_vtab_on_conflict(pConfig->db);
}
@@ -1709,12 +1679,8 @@ static int fts5UpdateMethod(
assert( nArg!=1 || eType0==SQLITE_INTEGER );
/* Filter out attempts to run UPDATE or DELETE on contentless tables.
** This is not suported. Except - they are both supported if the CREATE
** VIRTUAL TABLE statement contained "contentless_delete=1". */
if( eType0==SQLITE_INTEGER
&& pConfig->eContent==FTS5_CONTENT_NONE
&& pConfig->bContentlessDelete==0
){
** This is not suported. */
if( eType0==SQLITE_INTEGER && fts5IsContentless(pTab) ){
pTab->p.base.zErrMsg = sqlite3_mprintf(
"cannot %s contentless fts5 table: %s",
(nArg>1 ? "UPDATE" : "DELETE from"), pConfig->zName
@@ -1738,8 +1704,7 @@ static int fts5UpdateMethod(
}
else if( eType0!=SQLITE_INTEGER ){
/* An INSERT statement. If the conflict-mode is REPLACE, first remove
** the current entry (if any). */
/* If this is a REPLACE, first remove the current entry (if any) */
if( eConflict==SQLITE_REPLACE && eType1==SQLITE_INTEGER ){
i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0);
@@ -1802,7 +1767,8 @@ static int fts5SyncMethod(sqlite3_vtab *pVtab){
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
fts5CheckTransactionState(pTab, FTS5_SYNC, 0);
pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg;
rc = sqlite3Fts5FlushToDisk(&pTab->p);
fts5TripCursors(pTab);
rc = sqlite3Fts5StorageSync(pTab->pStorage);
pTab->p.pConfig->pzErrmsg = 0;
return rc;
}
@@ -2569,12 +2535,6 @@ static int fts5ColumnMethod(
sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pStmt, iCol+1));
}
pConfig->pzErrmsg = 0;
}else if( pConfig->bContentlessDelete && sqlite3_vtab_nochange(pCtx) ){
char *zErr = sqlite3_mprintf("cannot UPDATE a subset of "
"columns on fts5 contentless-delete table: %s", pConfig->zName
);
sqlite3_result_error(pCtx, zErr, -1);
sqlite3_free(zErr);
}
return rc;
}
@@ -2613,12 +2573,8 @@ static int fts5RenameMethod(
sqlite3_vtab *pVtab, /* Virtual table handle */
const char *zName /* New name of table */
){
int rc;
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
pTab->bInSavepoint = 1;
rc = sqlite3Fts5StorageRename(pTab->pStorage, zName);
pTab->bInSavepoint = 0;
return rc;
return sqlite3Fts5StorageRename(pTab->pStorage, zName);
}
int sqlite3Fts5FlushToDisk(Fts5Table *pTab){
@@ -2632,29 +2588,9 @@ int sqlite3Fts5FlushToDisk(Fts5Table *pTab){
** Flush the contents of the pending-terms table to disk.
*/
static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
int rc = SQLITE_OK;
char *zSql = 0;
fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint);
if( pTab->bInSavepoint==0 ){
zSql = sqlite3_mprintf("INSERT INTO %Q.%Q(%Q) VALUES('flush')",
pTab->p.pConfig->zDb, pTab->p.pConfig->zName, pTab->p.pConfig->zName
);
if( zSql ){
pTab->bInSavepoint = 1;
rc = sqlite3_exec(pTab->p.pConfig->db, zSql, 0, 0, 0);
pTab->bInSavepoint = 0;
sqlite3_free(zSql);
}else{
rc = SQLITE_NOMEM;
}
if( rc==SQLITE_OK ){
pTab->iSavepoint = iSavepoint+1;
}
}
return rc;
UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_SAVEPOINT, iSavepoint);
return sqlite3Fts5FlushToDisk((Fts5Table*)pVtab);
}
/*
@@ -2663,16 +2599,9 @@ static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
** This is a no-op.
*/
static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
int rc = SQLITE_OK;
fts5CheckTransactionState(pTab, FTS5_RELEASE, iSavepoint);
if( (iSavepoint+1)<pTab->iSavepoint ){
rc = sqlite3Fts5FlushToDisk(&pTab->p);
if( rc==SQLITE_OK ){
pTab->iSavepoint = iSavepoint;
}
}
return rc;
UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_RELEASE, iSavepoint);
return sqlite3Fts5FlushToDisk((Fts5Table*)pVtab);
}
/*
@@ -2682,14 +2611,11 @@ static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
*/
static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
int rc = SQLITE_OK;
UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint);
fts5TripCursors(pTab);
pTab->p.pConfig->pgsz = 0;
if( (iSavepoint+1)<=pTab->iSavepoint ){
rc = sqlite3Fts5StorageRollback(pTab->pStorage);
}
return rc;
return sqlite3Fts5StorageRollback(pTab->pStorage);
}
/*
@@ -2909,46 +2835,9 @@ static int fts5ShadowName(const char *zName){
return 0;
}
/*
** Run an integrity check on the FTS5 data structures. Return a string
** if anything is found amiss. Return a NULL pointer if everything is
** OK.
*/
static int fts5Integrity(
sqlite3_vtab *pVtab, /* the FTS5 virtual table to check */
const char *zSchema, /* Name of schema in which this table lives */
const char *zTabname, /* Name of the table itself */
int isQuick, /* True if this is a quick-check */
char **pzErr /* Write error message here */
){
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
Fts5Config *pConfig = pTab->p.pConfig;
char *zSql;
char *zErr = 0;
int rc;
assert( pzErr!=0 && *pzErr==0 );
UNUSED_PARAM(isQuick);
zSql = sqlite3_mprintf(
"INSERT INTO \"%w\".\"%w\"(\"%w\") VALUES('integrity-check');",
zSchema, zTabname, pConfig->zName);
if( zSql==0 ) return SQLITE_NOMEM;
rc = sqlite3_exec(pConfig->db, zSql, 0, 0, &zErr);
sqlite3_free(zSql);
if( (rc&0xff)==SQLITE_CORRUPT ){
*pzErr = sqlite3_mprintf("malformed inverted index for FTS5 table %s.%s",
zSchema, zTabname);
}else if( rc!=SQLITE_OK ){
*pzErr = sqlite3_mprintf("unable to validate the inverted index for"
" FTS5 table %s.%s: %s",
zSchema, zTabname, zErr);
}
sqlite3_free(zErr);
return SQLITE_OK;
}
static int fts5Init(sqlite3 *db){
static const sqlite3_module fts5Mod = {
/* iVersion */ 4,
/* iVersion */ 3,
/* xCreate */ fts5CreateMethod,
/* xConnect */ fts5ConnectMethod,
/* xBestIndex */ fts5BestIndexMethod,
@@ -2971,8 +2860,7 @@ static int fts5Init(sqlite3 *db){
/* xSavepoint */ fts5SavepointMethod,
/* xRelease */ fts5ReleaseMethod,
/* xRollbackTo */ fts5RollbackToMethod,
/* xShadowName */ fts5ShadowName,
/* xIntegrity */ fts5Integrity
/* xShadowName */ fts5ShadowName
};
int rc;
+13 -75
View File
@@ -77,10 +77,10 @@ static int fts5StorageGetStmt(
"INSERT INTO %Q.'%q_content' VALUES(%s)", /* INSERT_CONTENT */
"REPLACE INTO %Q.'%q_content' VALUES(%s)", /* REPLACE_CONTENT */
"DELETE FROM %Q.'%q_content' WHERE id=?", /* DELETE_CONTENT */
"REPLACE INTO %Q.'%q_docsize' VALUES(?,?%s)", /* REPLACE_DOCSIZE */
"REPLACE INTO %Q.'%q_docsize' VALUES(?,?)", /* REPLACE_DOCSIZE */
"DELETE FROM %Q.'%q_docsize' WHERE id=?", /* DELETE_DOCSIZE */
"SELECT sz%s FROM %Q.'%q_docsize' WHERE id=?", /* LOOKUP_DOCSIZE */
"SELECT sz FROM %Q.'%q_docsize' WHERE id=?", /* LOOKUP_DOCSIZE */
"REPLACE INTO %Q.'%q_config' VALUES(?,?)", /* REPLACE_CONFIG */
"SELECT %s FROM %s AS T", /* SCAN */
@@ -128,19 +128,6 @@ static int fts5StorageGetStmt(
break;
}
case FTS5_STMT_REPLACE_DOCSIZE:
zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName,
(pC->bContentlessDelete ? ",?" : "")
);
break;
case FTS5_STMT_LOOKUP_DOCSIZE:
zSql = sqlite3_mprintf(azStmt[eStmt],
(pC->bContentlessDelete ? ",origin" : ""),
pC->zDb, pC->zName
);
break;
default:
zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName);
break;
@@ -330,11 +317,9 @@ int sqlite3Fts5StorageOpen(
}
if( rc==SQLITE_OK && pConfig->bColumnsize ){
const char *zCols = "id INTEGER PRIMARY KEY, sz BLOB";
if( pConfig->bContentlessDelete ){
zCols = "id INTEGER PRIMARY KEY, sz BLOB, origin INTEGER";
}
rc = sqlite3Fts5CreateTable(pConfig, "docsize", zCols, 0, pzErr);
rc = sqlite3Fts5CreateTable(
pConfig, "docsize", "id INTEGER PRIMARY KEY, sz BLOB", 0, pzErr
);
}
if( rc==SQLITE_OK ){
rc = sqlite3Fts5CreateTable(
@@ -411,7 +396,7 @@ static int fts5StorageDeleteFromIndex(
){
Fts5Config *pConfig = p->pConfig;
sqlite3_stmt *pSeek = 0; /* SELECT to read row iDel from %_data */
int rc = SQLITE_OK; /* Return code */
int rc; /* Return code */
int rc2; /* sqlite3_reset() return code */
int iCol;
Fts5InsertCtx ctx;
@@ -427,6 +412,7 @@ static int fts5StorageDeleteFromIndex(
ctx.pStorage = p;
ctx.iCol = -1;
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel);
for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){
if( pConfig->abUnindexed[iCol-1]==0 ){
const char *zText;
@@ -463,37 +449,6 @@ static int fts5StorageDeleteFromIndex(
return rc;
}
/*
** This function is called to process a DELETE on a contentless_delete=1
** table. It adds the tombstone required to delete the entry with rowid
** iDel. If successful, SQLITE_OK is returned. Or, if an error occurs,
** an SQLite error code.
*/
static int fts5StorageContentlessDelete(Fts5Storage *p, i64 iDel){
i64 iOrigin = 0;
sqlite3_stmt *pLookup = 0;
int rc = SQLITE_OK;
assert( p->pConfig->bContentlessDelete );
assert( p->pConfig->eContent==FTS5_CONTENT_NONE );
/* Look up the origin of the document in the %_docsize table. Store
** this in stack variable iOrigin. */
rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0);
if( rc==SQLITE_OK ){
sqlite3_bind_int64(pLookup, 1, iDel);
if( SQLITE_ROW==sqlite3_step(pLookup) ){
iOrigin = sqlite3_column_int64(pLookup, 1);
}
rc = sqlite3_reset(pLookup);
}
if( rc==SQLITE_OK && iOrigin!=0 ){
rc = sqlite3Fts5IndexContentlessDelete(p->pIndex, iOrigin, iDel);
}
return rc;
}
/*
** Insert a record into the %_docsize table. Specifically, do:
@@ -514,17 +469,10 @@ static int fts5StorageInsertDocsize(
rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_DOCSIZE, &pReplace, 0);
if( rc==SQLITE_OK ){
sqlite3_bind_int64(pReplace, 1, iRowid);
if( p->pConfig->bContentlessDelete ){
i64 iOrigin = 0;
rc = sqlite3Fts5IndexGetOrigin(p->pIndex, &iOrigin);
sqlite3_bind_int64(pReplace, 3, iOrigin);
}
if( rc==SQLITE_OK ){
sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC);
sqlite3_step(pReplace);
rc = sqlite3_reset(pReplace);
sqlite3_bind_null(pReplace, 2);
}
sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC);
sqlite3_step(pReplace);
rc = sqlite3_reset(pReplace);
sqlite3_bind_null(pReplace, 2);
}
}
return rc;
@@ -588,15 +536,7 @@ int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel, sqlite3_value **apVal){
/* Delete the index records */
if( rc==SQLITE_OK ){
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel);
}
if( rc==SQLITE_OK ){
if( p->pConfig->bContentlessDelete ){
rc = fts5StorageContentlessDelete(p, iDel);
}else{
rc = fts5StorageDeleteFromIndex(p, iDel, apVal);
}
rc = fts5StorageDeleteFromIndex(p, iDel, apVal);
}
/* Delete the %_docsize record */
@@ -1184,9 +1124,7 @@ int sqlite3Fts5StorageSync(Fts5Storage *p){
i64 iLastRowid = sqlite3_last_insert_rowid(p->pConfig->db);
if( p->bTotalsValid ){
rc = fts5StorageSaveTotals(p);
if( rc==SQLITE_OK ){
p->bTotalsValid = 0;
}
p->bTotalsValid = 0;
}
if( rc==SQLITE_OK ){
rc = sqlite3Fts5IndexSync(p->pIndex);
+1 -2
View File
@@ -472,8 +472,7 @@ int sqlite3Fts5TestRegisterTok(sqlite3 *db, fts5_api *pApi){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
0 /* xShadowName */
};
int rc; /* Return code */
+26 -69
View File
@@ -229,12 +229,6 @@ static const unsigned char sqlite3Utf8Trans1[] = {
#endif /* ifndef SQLITE_AMALGAMATION */
#define FTS5_SKIP_UTF8(zIn) { \
if( ((unsigned char)(*(zIn++)))>=0xc0 ){ \
while( (((unsigned char)*zIn) & 0xc0)==0x80 ){ zIn++; } \
} \
}
typedef struct Unicode61Tokenizer Unicode61Tokenizer;
struct Unicode61Tokenizer {
unsigned char aTokenChar[128]; /* ASCII range token characters */
@@ -1270,7 +1264,6 @@ static int fts5PorterTokenize(
typedef struct TrigramTokenizer TrigramTokenizer;
struct TrigramTokenizer {
int bFold; /* True to fold to lower-case */
int iFoldParam; /* Parameter to pass to Fts5UnicodeFold() */
};
/*
@@ -1297,7 +1290,6 @@ static int fts5TriCreate(
}else{
int i;
pNew->bFold = 1;
pNew->iFoldParam = 0;
for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
const char *zArg = azArg[i+1];
if( 0==sqlite3_stricmp(azArg[i], "case_sensitive") ){
@@ -1306,21 +1298,10 @@ static int fts5TriCreate(
}else{
pNew->bFold = (zArg[0]=='0');
}
}else if( 0==sqlite3_stricmp(azArg[i], "remove_diacritics") ){
if( (zArg[0]!='0' && zArg[0]!='1' && zArg[0]!='2') || zArg[1] ){
rc = SQLITE_ERROR;
}else{
pNew->iFoldParam = (zArg[0]!='0') ? 2 : 0;
}
}else{
rc = SQLITE_ERROR;
}
}
if( pNew->iFoldParam!=0 && pNew->bFold==0 ){
rc = SQLITE_ERROR;
}
if( rc!=SQLITE_OK ){
fts5TriDelete((Fts5Tokenizer*)pNew);
pNew = 0;
@@ -1343,62 +1324,40 @@ static int fts5TriTokenize(
TrigramTokenizer *p = (TrigramTokenizer*)pTok;
int rc = SQLITE_OK;
char aBuf[32];
char *zOut = aBuf;
int ii;
const unsigned char *zIn = (const unsigned char*)pText;
const unsigned char *zEof = &zIn[nText];
u32 iCode;
int aStart[3]; /* Input offset of each character in aBuf[] */
UNUSED_PARAM(unusedFlags);
/* Populate aBuf[] with the characters for the first trigram. */
for(ii=0; ii<3; ii++){
do {
aStart[ii] = zIn - (const unsigned char*)pText;
READ_UTF8(zIn, zEof, iCode);
if( iCode==0 ) return SQLITE_OK;
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam);
}while( iCode==0 );
WRITE_UTF8(zOut, iCode);
}
/* At the start of each iteration of this loop:
**
** aBuf: Contains 3 characters. The 3 characters of the next trigram.
** zOut: Points to the byte following the last character in aBuf.
** aStart[3]: Contains the byte offset in the input text corresponding
** to the start of each of the three characters in the buffer.
*/
assert( zIn<=zEof );
while( 1 ){
int iNext; /* Start of character following current tri */
const char *z1;
char *zOut = aBuf;
int iStart = zIn - (const unsigned char*)pText;
const unsigned char *zNext;
/* Read characters from the input up until the first non-diacritic */
do {
iNext = zIn - (const unsigned char*)pText;
READ_UTF8(zIn, zEof, iCode);
if( iCode==0 ) break;
zNext = zIn;
if( zIn<zEof ){
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0);
WRITE_UTF8(zOut, iCode);
READ_UTF8(zIn, zEof, iCode);
if( iCode==0 ) break;
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam);
}while( iCode==0 );
/* Pass the current trigram back to fts5 */
rc = xToken(pCtx, 0, aBuf, zOut-aBuf, aStart[0], iNext);
if( iCode==0 || rc!=SQLITE_OK ) break;
/* Remove the first character from buffer aBuf[]. Append the character
** with codepoint iCode. */
z1 = aBuf;
FTS5_SKIP_UTF8(z1);
memmove(aBuf, z1, zOut - z1);
zOut -= (z1 - aBuf);
WRITE_UTF8(zOut, iCode);
/* Update the aStart[] array */
aStart[0] = aStart[1];
aStart[1] = aStart[2];
aStart[2] = iNext;
}else{
break;
}
if( zIn<zEof ){
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0);
WRITE_UTF8(zOut, iCode);
READ_UTF8(zIn, zEof, iCode);
if( iCode==0 ) break;
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0);
WRITE_UTF8(zOut, iCode);
}else{
break;
}
rc = xToken(pCtx, 0, aBuf, zOut-aBuf, iStart, iStart + zOut-aBuf);
if( rc!=SQLITE_OK ) break;
zIn = zNext;
}
return rc;
@@ -1421,9 +1380,7 @@ int sqlite3Fts5TokenizerPattern(
){
if( xCreate==fts5TriCreate ){
TrigramTokenizer *p = (TrigramTokenizer*)pTok;
if( p->iFoldParam==0 ){
return p->bFold ? FTS5_PATTERN_LIKE : FTS5_PATTERN_GLOB;
}
return p->bFold ? FTS5_PATTERN_LIKE : FTS5_PATTERN_GLOB;
}
return FTS5_PATTERN_NONE;
}
+1 -2
View File
@@ -783,8 +783,7 @@ int sqlite3Fts5VocabInit(Fts5Global *pGlobal, sqlite3 *db){
/* xSavepoint */ 0,
/* xRelease */ 0,
/* xRollbackTo */ 0,
/* xShadowName */ 0,
/* xIntegrity */ 0
/* xShadowName */ 0
};
void *p = (void*)pGlobal;
+2 -5
View File
@@ -65,9 +65,7 @@ foreach w {a b c d e f} {
do_execsql_test 2.4 {
INSERT INTO t1(t1) VALUES('integrity-check');
PRAGMA integrity_check;
PRAGMA integrity_check(t1);
} {ok ok}
}
#-------------------------------------------------------------------------
@@ -90,7 +88,6 @@ foreach {i x y} {
} {
do_execsql_test 3.$i.1 { INSERT INTO t1 VALUES($x, $y) }
do_execsql_test 3.$i.2 { INSERT INTO t1(t1) VALUES('integrity-check') }
do_execsql_test 3.$i.3 { PRAGMA integrity_check(t1) } ok
if {[set_test_counter errors]} break
}
@@ -138,7 +135,7 @@ foreach {i x y} {
10 {ddd abcde dddd dd c} {dddd c c d abcde}
} {
do_execsql_test 5.$i.1 { INSERT INTO t1 VALUES($x, $y) }
do_execsql_test 5.$i.2 { PRAGMA integrity_check(t1) } ok
do_execsql_test 5.$i.2 { INSERT INTO t1(t1) VALUES('integrity-check') }
if {[set_test_counter errors]} break
}
Regular → Executable
View File
+1 -27
View File
@@ -307,31 +307,5 @@ do_catchsql_test 10.1.4 {
SELECT group_concat(firstcol(t1), '.') FROM t1 GROUP BY rowid
} {1 {unable to use function firstcol in the requested context}}
#-------------------------------------------------------------------------
# Test that xInstCount() works from within an xPhraseQuery() callback.
#
reset_db
proc xCallback {cmd} {
incr ::hitcount [$cmd xInstCount]
return SQLITE_OK
}
proc fts5_hitcount {cmd} {
set ::hitcount 0
$cmd xQueryPhrase 0 xCallback
return $::hitcount
}
sqlite3_fts5_create_function db fts5_hitcount fts5_hitcount
do_execsql_test 11.1 {
CREATE VIRTUAL TABLE x1 USING fts5(z);
INSERT INTO x1 VALUES('one two three');
INSERT INTO x1 VALUES('one two one three one');
INSERT INTO x1 VALUES('one two three');
}
do_execsql_test 11.2 {
SELECT fts5_hitcount(x1) FROM x1('one') LIMIT 1;
} {5}
finish_test
Regular → Executable
View File
-40
View File
@@ -65,44 +65,4 @@ do_execsql_test 2.1 {
INSERT INTO fts_idx(fts_idx) VALUES('integrity-check');
}
#-------------------------------------------------------------------------
# Tests for OR IGNORE conflict handling.
#
reset_db
foreach_detail_mode $::testprefix {
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE t1 USING fts5(xyz, detail=%DETAIL%);
BEGIN;
INSERT INTO t1(rowid, xyz) VALUES(13, 'thirteen documents');
INSERT INTO t1(rowid, xyz) VALUES(14, 'fourteen documents');
INSERT INTO t1(rowid, xyz) VALUES(15, 'fifteen documents');
COMMIT;
}
set db_cksum [cksum]
foreach {tn sql} {
1 {
INSERT OR IGNORE INTO t1(rowid, xyz) VALUES(14, 'new text');
}
2 {
UPDATE OR IGNORE t1 SET rowid=13 WHERE rowid=15;
}
3 {
INSERT OR IGNORE INTO t1(rowid, xyz)
SELECT 13, 'some text'
UNION ALL
SELECT 14, 'some text'
UNION ALL
SELECT 15, 'some text'
}
} {
do_execsql_test 3.1.$tn.1 $sql
do_test 3.1.$tn.2 { cksum } $db_cksum
}
}
finish_test
+1
View File
@@ -294,3 +294,4 @@ do_catchsql_test 7.2.5 {
} {1 {recursively defined fts5 content table}}
finish_test
-270
View File
@@ -1,270 +0,0 @@
# 2014 Dec 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 file contains tests for the content= and content_rowid= options.
#
source [file join [file dirname [info script]] fts5_common.tcl]
set testprefix fts5contentless
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
ifcapable !fts5 {
finish_test
return
}
# Check that it is not possible to specify "contentless_delete=1" for
# anything other than a contentless table.
#
set res(0) {0 {}}
set res(1) {1 {contentless_delete=1 requires a contentless table}}
foreach {tn sql bError} {
1 "(a, b, contentless_delete=1)" 1
2 "(a, b, contentless_delete=1, content=abc)" 1
3 "(a, b, contentless_delete=1, content=)" 0
4 "(content=, contentless_delete=1, a)" 0
5 "(content='', contentless_delete=1, hello)" 0
} {
execsql { BEGIN }
do_catchsql_test 1.$tn "CREATE VIRTUAL TABLE t1 USING fts5 $sql" $res($bError)
execsql { ROLLBACK }
}
# Check that it is not possible to specify "contentless_delete=1"
# along with columnsize=1.
#
set res(0) {0 {}}
set res(1) {1 {contentless_delete=1 is incompatible with columnsize=0}}
foreach {tn sql bError} {
2 "(a, b, content='', contentless_delete=1, columnsize=0)" 1
} {
execsql { BEGIN }
do_catchsql_test 1.$tn "CREATE VIRTUAL TABLE t1 USING fts5 $sql" $res($bError)
execsql { ROLLBACK }
}
# Check that if contentless_delete=1 is specified, then the "origin"
# column is added to the %_docsize table.
reset_db
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE x1 USING fts5(c, content='');
CREATE VIRTUAL TABLE x2 USING fts5(c, content='', contentless_delete=1);
}
do_execsql_test 3.1 {
SELECT sql FROM sqlite_schema WHERE name IN ('x1_docsize', 'x2_docsize');
} {
{CREATE TABLE 'x1_docsize'(id INTEGER PRIMARY KEY, sz BLOB)}
{CREATE TABLE 'x2_docsize'(id INTEGER PRIMARY KEY, sz BLOB, origin INTEGER)}
}
do_execsql_test 3.2.1 {
SELECT hex(block) FROM x1_data WHERE id=10
} {00000000000000}
do_execsql_test 3.2.2 {
SELECT hex(block) FROM x2_data WHERE id=10
} {00000000FF000001000000}
do_execsql_test 3.3 {
INSERT INTO x2 VALUES('first text');
INSERT INTO x2 VALUES('second text');
}
do_execsql_test 3.4 {
SELECT id, origin FROM x2_docsize
} {1 1 2 2}
do_execsql_test 3.5 {
SELECT level, segment, loc1, loc2 FROM fts5_structure(
(SELECT block FROM x2_data WHERE id=10)
)
} {
0 0 1 1
0 1 2 2
}
do_execsql_test 3.6 {
INSERT INTO x2(x2) VALUES('optimize');
}
do_execsql_test 3.7 {
SELECT level, segment, loc1, loc2 FROM fts5_structure(
(SELECT block FROM x2_data WHERE id=10)
)
} {
1 0 1 2
}
do_execsql_test 3.8 {
DELETE FROM x2 WHERE rowid=2;
}
do_execsql_test 3.9 {
SELECT rowid FROM x2('text')
} {1}
#--------------------------------------------------------------------------
reset_db
proc document {n} {
set vocab [list A B C D E F G H I J K L M N O P Q R S T U V W X Y Z]
set ret [list]
for {set ii 0} {$ii < $n} {incr ii} {
lappend ret [lindex $vocab [expr int(rand()*[llength $vocab])]]
}
set ret
}
set nRow 1000
do_execsql_test 4.0 {
CREATE TABLE t1(x);
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
INSERT INTO ft(ft, rank) VALUES('pgsz', 100);
}
do_test 4.1 {
for {set ii 0} {$ii < $nRow} {incr ii} {
set doc [document 6]
execsql {
INSERT INTO t1 VALUES($doc);
INSERT INTO ft VALUES($doc);
}
}
} {}
foreach v {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z} {
set L1 [execsql {SELECT rowid FROM t1 WHERE x LIKE '%'||$v||'%'}]
set L2 [execsql {SELECT rowid FROM ft($v)}]
do_test 4.2.$v { set L1 } $L2
}
do_test 4.3 {
for {set ii 1} {$ii < $nRow} {incr ii 2} {
execsql {
DELETE FROM ft WHERE rowid=$ii;
DELETE FROM t1 WHERE rowid=$ii;
}
}
} {}
foreach v {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z} {
set L1 [execsql {SELECT rowid FROM t1 WHERE x LIKE '%'||$v||'%'}]
set L2 [execsql {SELECT rowid FROM ft($v)}]
do_test 4.4.$v { set L1 } $L2
}
do_execsql_test 4.5 {
INSERT INTO ft(ft) VALUES('optimize');
} {}
foreach v {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z} {
set L1 [execsql {SELECT rowid FROM t1 WHERE x LIKE '%'||$v||'%'}]
set L2 [execsql {SELECT rowid FROM ft($v)}]
do_test 4.6.$v { set L1 } $L2
}
#execsql_pp { SELECT fts5_decode(id, block) FROM ft_data }
#-------------------------------------------------------------------------
reset_db
do_execsql_test 5.0 {
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
INSERT INTO ft(rowid, x) VALUES(1, 'one two three');
INSERT INTO ft(rowid, x) VALUES(2, 'one two four');
INSERT INTO ft(rowid, x) VALUES(3, 'one two five');
INSERT INTO ft(rowid, x) VALUES(4, 'one two seven');
INSERT INTO ft(rowid, x) VALUES(5, 'one two eight');
}
do_execsql_test 5.1 {
DELETE FROM ft WHERE rowid=2
}
do_execsql_test 5.2 {
SELECT rowid FROM ft
} {1 3 4 5}
do_catchsql_test 5.3 {
UPDATE ft SET x='four six' WHERE rowid=3
} {0 {}}
do_execsql_test 5.4 {
SELECT rowid FROM ft('one');
} {1 4 5}
do_execsql_test 5.5 {
REPLACE INTO ft(rowid, x) VALUES(3, 'four six');
SELECT rowid FROM ft('one');
} {1 4 5}
do_execsql_test 5.6 {
REPLACE INTO ft(rowid, x) VALUES(6, 'one two eleven');
SELECT rowid FROM ft('one');
} {1 4 5 6}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 6.0 {
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
INSERT INTO ft(rowid, x) VALUES(1, 'one two three');
INSERT INTO ft(rowid, x) VALUES(2, 'one two four');
}
do_test 6.1 {
db eval { SELECT rowid FROM ft('one two') } {
if {$rowid==1} {
db eval { INSERT INTO ft(rowid, x) VALUES(3, 'one two four') }
}
}
} {}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 7.0 {
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
}
set lRowid [list -450 0 1 2 42]
do_test 7.1 {
execsql BEGIN
foreach r $lRowid {
execsql { INSERT INTO ft(rowid, x) VALUES($r, 'one one one'); }
}
execsql COMMIT
} {}
do_test 7.2 {
execsql BEGIN
foreach r $lRowid {
execsql { REPLACE INTO ft(rowid, x) VALUES($r, 'two two two'); }
}
execsql COMMIT
} {}
do_execsql_test 7.3 { SELECT rowid FROM ft('one'); } {}
do_execsql_test 7.4 { SELECT rowid FROM ft('two'); } $lRowid
#-------------------------------------------------------------------------
reset_db
do_execsql_test 8.0 {
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
INSERT INTO ft VALUES('hello world');
INSERT INTO ft VALUES('one two three');
}
do_catchsql_test 8.1 {
INSERT INTO ft(ft, rowid, x) VALUES('delete', 1, 'hello world');
} {1 {'delete' may not be used with a contentless_delete=1 table}}
do_execsql_test 8.2 {
BEGIN;
INSERT INTO ft(rowid, x) VALUES(3, 'four four four');
DELETE FROM ft WHERE rowid=3;
COMMIT;
SELECT rowid FROM ft('four');
} {}
finish_test
-207
View File
@@ -1,207 +0,0 @@
# 2023 July 19
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# This file contains tests for the content= and content_rowid= options.
#
source [file join [file dirname [info script]] fts5_common.tcl]
set testprefix fts5contentless2
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
ifcapable !fts5 {
finish_test
return
}
proc vocab {} {
list aaa bbb ccc ddd eee fff ggg hhh iii jjj kkk lll mmm nnn ooo ppp
}
proc document {nToken} {
set doc [list]
set vocab [vocab]
for {set ii 0} {$ii < $nToken} {incr ii} {
lappend doc [lindex $vocab [expr int(rand()*[llength $vocab])]]
}
set doc
}
db func document document
proc contains {doc token} {
expr {[lsearch $doc $token]>=0}
}
db func contains contains
proc do_compare_tables_test {tn} {
uplevel [list do_test $tn {
foreach v [vocab] {
set l1 [execsql { SELECT rowid FROM t1 WHERE contains(doc, $v) }]
set l2 [execsql { SELECT rowid FROM t2($v) }]
if {$l1!=$l2} { error "1: query mismatch ($l1) ($l2)" }
set w "[string range $v 0 1]*"
set l1 [execsql { SELECT rowid FROM t1 WHERE contains(doc, $w) }]
set l2 [execsql { SELECT rowid FROM t2($w) }]
if {$l1!=$l2} { error "2: query mismatch ($l1) ($l2)" }
set w "[string range $v 0 0]*"
set l1 [execsql { SELECT rowid FROM t1 WHERE contains(doc, $w) }]
set l2 [execsql { SELECT rowid FROM t2($w) }]
if {$l1!=$l2} { error "2: query mismatch ($l1) ($l2)" }
set l1 [execsql {
SELECT rowid FROM t1 WHERE contains(doc, $v) ORDER BY rowid DESC
}]
set l2 [execsql { SELECT rowid FROM t2($v) ORDER BY rowid DESC }]
if {$l1!=$l2} { error "1: query mismatch ($l1) ($l2)" }
}
set {} {}
} {}]
}
proc lshuffle {in} {
set L [list]
set ret [list]
foreach elem $in { lappend L [list [expr rand()] $elem] }
foreach pair [lsort -index 0 $L] { lappend ret [lindex $pair 1] }
set ret
}
expr srand(0)
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE t2 USING fts5(
doc, prefix=2, content=, contentless_delete=1
);
CREATE TABLE t1(doc);
CREATE TRIGGER tr1 AFTER DELETE ON t1 BEGIN
DELETE FROM t2 WHERE rowid = old.rowid;
END;
}
set SMALLEST64 -9223372036854775808
set LARGEST64 9223372036854775807
foreach {tn r1 r2} {
1 0 50
2 $SMALLEST64 $SMALLEST64+50
3 $LARGEST64-50 $LARGEST64
4 -50 -1
} {
set r1 [expr $r1]
set r2 [expr $r2]
do_test 1.1.$tn {
execsql BEGIN
for {set ii $r1} {$ii <= $r2} {incr ii} {
execsql { INSERT INTO t1(rowid, doc) VALUES ($ii, document(8)); }
}
execsql COMMIT
} {}
}
do_test 1.2 {
db eval { SELECT rowid, doc FROM t1 } {
execsql { INSERT INTO t2(rowid, doc) VALUES($rowid, $doc) }
}
} {}
foreach {tn rowid} {
1 $SMALLEST64
2 0
3 -5
4 -30
5 $LARGEST64
6 $LARGEST64-1
} {
set rowid [expr $rowid]
do_execsql_test 1.3.$tn.1 {
DELETE FROM t1 WHERE rowid=$rowid
}
do_compare_tables_test 1.3.$tn.2
}
set iTest 1
foreach r [lshuffle [execsql {SELECT rowid FROM t1}]] {
if {($iTest % 50)==0} {
execsql { INSERT INTO t2(t2) VALUES('optimize') }
}
if {($iTest % 5)==0} {
execsql { INSERT INTO t2(t2, rank) VALUES('merge', 5) }
}
do_execsql_test 1.4.$iTest.1($r) {
DELETE FROM t1 WHERE rowid=$r
}
do_compare_tables_test 1.4.$iTest.2
incr iTest
}
do_execsql_test 1.5 {
SELECT * FROM t1
} {}
#-------------------------------------------------------------------------
reset_db
db func document document
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE t2 USING fts5(doc, content=, contentless_delete=1);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO t2(rowid, doc) SELECT i, i || ' ' || i FROM s;
}
do_execsql_test 2.1 {
BEGIN;
DELETE FROM t2 WHERE rowid=32;
DELETE FROM t2 WHERE rowid=64;
DELETE FROM t2 WHERE rowid=96;
DELETE FROM t2 WHERE rowid=128;
DELETE FROM t2 WHERE rowid=160;
DELETE FROM t2 WHERE rowid=192;
COMMIT;
}
do_execsql_test 2.2 {
SELECT * FROM t2('128');
} {}
#-------------------------------------------------------------------------
foreach {tn step} {
1 3
2 7
3 15
} {
set step [expr $step]
reset_db
db func document document
do_execsql_test 3.$tn.0 {
CREATE VIRTUAL TABLE t2 USING fts5(doc, content=, contentless_delete=1);
INSERT INTO t2(t2, rank) VALUES('pgsz', 100);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO t2(rowid, doc) SELECT i, i || ' ' || i FROM s;
}
do_execsql_test 3.$tn.1 {
DELETE FROM t2 WHERE (rowid % $step)==0
}
do_execsql_test 3.$tn.2 {
SELECT * FROM t2( $step * 5 )
} {}
}
finish_test
-195
View File
@@ -1,195 +0,0 @@
# 2023 July 21
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# This file contains tests for the content= and content_rowid= options.
#
source [file join [file dirname [info script]] fts5_common.tcl]
set testprefix fts5contentless3
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
ifcapable !fts5 {
finish_test
return
}
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE ft USING fts5(x, content=, contentless_delete=1);
BEGIN;
INSERT INTO ft VALUES('one one one');
INSERT INTO ft VALUES('two two two');
INSERT INTO ft VALUES('three three three');
INSERT INTO ft VALUES('four four four');
INSERT INTO ft VALUES('five five five');
INSERT INTO ft VALUES('six six six');
INSERT INTO ft VALUES('seven seven seven');
INSERT INTO ft VALUES('eight eight eight');
INSERT INTO ft VALUES('nine nine nine');
COMMIT;
DELETE FROM ft WHERE rowid=3;
}
proc myhex {hex} { binary decode hex $hex }
db func myhex myhex
do_execsql_test 1.1 {
UPDATE ft_data SET block =
myhex('04000000 00000001' ||
'01020304 01020304 01020304 01020304' ||
'01020304 01020304 01020304 01020304'
)
WHERE id = (SELECT max(id) FROM ft_data);
}
do_execsql_test 1.2 {
DELETE FROM ft WHERE rowid=1
}
do_execsql_test 1.3 {
SELECT rowid FROM ft('two');
} {2}
do_execsql_test 1.3 {
UPDATE ft_data SET block =
myhex('08000000 00000001' ||
'0000000001020304 0000000001020304 0000000001020304 0000000001020304' ||
'0000000001020304 0000000001020304 0000000001020304 0000000001020304'
)
WHERE id = (SELECT max(id) FROM ft_data);
}
do_execsql_test 1.4 {
SELECT rowid FROM ft('two');
} {2}
do_execsql_test 1.5 {
DELETE FROM ft WHERE rowid=4
}
do_execsql_test 1.6 {
UPDATE ft_data SET block = myhex('04000000 00000000')
WHERE id = (SELECT max(id) FROM ft_data);
}
do_execsql_test 1.7 {
SELECT rowid FROM ft('two');
} {2}
do_execsql_test 1.8 {
UPDATE ft_data SET block = myhex('04000000 00000000')
WHERE id = (SELECT max(id) FROM ft_data);
}
do_execsql_test 1.9 {
DELETE FROM ft WHERE rowid=8
} {}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE ft USING fts5(x, content=, contentless_delete=1);
INSERT INTO ft VALUES('one one one');
INSERT INTO ft VALUES('two two two');
INSERT INTO ft VALUES('three three three');
INSERT INTO ft VALUES('four four four');
INSERT INTO ft VALUES('five five five');
INSERT INTO ft VALUES('six six six');
INSERT INTO ft VALUES('seven seven seven');
INSERT INTO ft VALUES('eight eight eight');
INSERT INTO ft VALUES('nine nine nine');
}
do_execsql_test 2.1 {
INSERT INTO ft(ft) VALUES('optimize');
}
do_execsql_test 2.2 {
SELECT count(*) FROM ft_data
} {3}
do_execsql_test 2.3 {
DELETE FROM ft WHERE rowid=5
}
do_execsql_test 2.4 {
SELECT count(*) FROM ft_data
} {4}
# Check that an 'optimize' works (rewrites the index) if there is a single
# segment with one or more tombstone hash pages.
do_execsql_test 2.5 {
INSERT INTO ft(ft) VALUES('optimize');
}
do_execsql_test 2.6 {
SELECT count(*) FROM ft_data
} {3}
# Check that an 'optimize' is a no-op if there is a single segment
# and no tombstone hash pages.
do_execsql_test 2.7 {
INSERT INTO ft(ft) VALUES('optimize');
SELECT rowid FROM ft_data;
} [db eval {SELECT rowid FROM ft_data}]
#-------------------------------------------------------------------------
reset_db
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE ft USING fts5(x, content=, contentless_delete=1);
INSERT INTO ft(ft, rank) VALUES('pgsz', 64);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO ft(rowid, x) SELECT i, i||' '||i||' '||i||' '||i FROM s;
INSERT INTO ft(ft) VALUES('optimize');
}
do_execsql_test 3.1 {
SELECT count(*) FROM ft_data
} {200}
do_execsql_test 3.2 {
DELETE FROM ft WHERE (rowid % 50)==0;
SELECT count(*) FROM ft_data;
} {203}
do_execsql_test 3.3 {
INSERT INTO ft(ft, rank) VALUES('merge', 500);
SELECT rowid FROM ft_data;
} [db eval {SELECT rowid FROM ft_data}]
do_execsql_test 3.4 {
INSERT INTO ft(ft, rank) VALUES('merge', -1000);
SELECT count(*) FROM ft_data;
} {197}
do_execsql_test 3.5 {
DELETE FROM ft WHERE (rowid % 50)==1;
SELECT count(*) FROM ft_data;
} {200}
do_execsql_test 3.6 {
SELECT level, segment, npgtombstone FROM fts5_structure(
(SELECT block FROM ft_data WHERE id=10)
)
} {1 0 3}
do_test 3.6 {
while 1 {
set nChange [db total_changes]
execsql { INSERT INTO ft(ft, rank) VALUES('merge', -5) }
if {([db total_changes] - $nChange)<2} break
}
} {}
do_execsql_test 3.7 {
SELECT level, segment, npgtombstone FROM fts5_structure(
(SELECT block FROM ft_data WHERE id=10)
)
} {2 0 0}
finish_test
-247
View File
@@ -1,247 +0,0 @@
# 2023 July 21
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# This file contains tests for the content= and content_rowid= options.
#
source [file join [file dirname [info script]] fts5_common.tcl]
set testprefix fts5contentless4
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
ifcapable !fts5 {
finish_test
return
}
proc document {n} {
set vocab [list A B C D E F G H I J K L M N O P Q R S T U V W X Y Z]
set ret [list]
for {set ii 0} {$ii < $n} {incr ii} {
lappend ret [lindex $vocab [expr int(rand()*[llength $vocab])]]
}
set ret
}
db func document document
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
INSERT INTO ft(ft, rank) VALUES('pgsz', 240);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO ft SELECT document(12) FROM s;
}
do_execsql_test 1.1 {
INSERT INTO ft(ft) VALUES('optimize');
}
do_execsql_test 1.2 {
SELECT level, segment, nentry, nentrytombstone FROM fts5_structure((
SELECT block FROM ft_data WHERE id=10
))
} {0 0 1000 0}
do_execsql_test 1.3 {
DELETE FROM ft WHERE rowid < 50
}
do_execsql_test 1.4 {
SELECT level, segment, nentry, nentrytombstone FROM fts5_structure((
SELECT block FROM ft_data WHERE id=10
))
} {0 0 1000 49}
do_execsql_test 1.5 {
DELETE FROM ft WHERE rowid < 1000
}
do_execsql_test 1.6 {
SELECT level, segment, nentry, nentrytombstone FROM fts5_structure((
SELECT block FROM ft_data WHERE id=10
))
} {1 0 1 0}
#--------------------------------------------------------------------------
reset_db
db func document document
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
}
do_test 2.1 {
for {set ii 0} {$ii < 5000} {incr ii} {
execsql { INSERT INTO ft VALUES( document(12) ) }
}
} {}
do_execsql_test 2.2 {
SELECT sum(nentry) - sum(nentrytombstone) FROM fts5_structure((
SELECT block FROM ft_data WHERE id=10
))
} {5000}
for {set ii 5000} {$ii >= 0} {incr ii -100} {
do_execsql_test 2.3.$ii {
DELETE FROM ft WHERE rowid > $ii
}
do_execsql_test 2.3.$ii.2 {
SELECT
CAST((total(nentry) - total(nentrytombstone)) AS integer)
FROM
fts5_structure( (SELECT block FROM ft_data WHERE id=10) )
} $ii
}
execsql_pp {
SELECT * FROM fts5_structure((
SELECT block FROM ft_data WHERE id=10
))
}
do_test 2.4 {
for {set ii 0} {$ii < 5000} {incr ii} {
execsql { INSERT INTO ft VALUES( document(12) ) }
}
} {}
for {set ii 1} {$ii <= 5000} {incr ii 10} {
do_execsql_test 2.3.$ii {
DELETE FROM ft WHERE rowid = $ii;
INSERT INTO ft VALUES( document(12) );
INSERT INTO ft(ft, rank) VALUES('merge', -10);
}
do_execsql_test 2.3.$ii.2 {
SELECT
CAST((total(nentry) - total(nentrytombstone)) AS integer)
FROM
fts5_structure( (SELECT block FROM ft_data WHERE id=10) )
} 5000
}
#-------------------------------------------------------------------------
reset_db
db func document document
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100
)
INSERT INTO ft SELECT document(12) FROM s;
}
do_catchsql_test 3.1 {
INSERT INTO ft(ft, rank) VALUES('deletemerge', 'text');
} {1 {SQL logic error}}
do_catchsql_test 3.2 {
INSERT INTO ft(ft, rank) VALUES('deletemerge', 50);
} {0 {}}
do_execsql_test 3.3 {
SELECT * FROM ft_config WHERE k='deletemerge'
} {deletemerge 50}
do_catchsql_test 3.4 {
INSERT INTO ft(ft, rank) VALUES('deletemerge', 101);
} {0 {}}
do_execsql_test 3.5 {
SELECT * FROM ft_config WHERE k='deletemerge'
} {deletemerge 101}
do_execsql_test 3.6 {
DELETE FROM ft WHERE rowid<95
}
do_execsql_test 3.7 {
SELECT nentrytombstone, nentry FROM fts5_structure((
SELECT block FROM ft_data WHERE id=10
))
} {94 100}
do_execsql_test 3.8 {
DELETE FROM ft WHERE rowid=95
}
do_execsql_test 3.9 {
SELECT nentrytombstone, nentry FROM fts5_structure((
SELECT block FROM ft_data WHERE id=10
))
} {95 100}
do_execsql_test 3.10 {
DELETE FROM ft;
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100
)
INSERT INTO ft SELECT document(12) FROM s;
INSERT INTO ft(ft, rank) VALUES('deletemerge', 50);
}
do_execsql_test 3.11 {
DELETE FROM ft WHERE rowid<95
}
do_execsql_test 3.12 {
SELECT nentrytombstone, nentry FROM fts5_structure((
SELECT block FROM ft_data WHERE id=10
))
} {0 6}
#-------------------------------------------------------------------------
reset_db
db func document document
do_execsql_test 4.0 {
CREATE VIRTUAL TABLE x1 USING fts5(x, content='', contentless_delete=1);
INSERT INTO x1(x1, rank) VALUES('usermerge', 16);
INSERT INTO x1(x1, rank) VALUES('deletemerge', 40);
INSERT INTO x1 VALUES('one');
INSERT INTO x1 VALUES('two');
INSERT INTO x1 VALUES('three');
INSERT INTO x1 VALUES('four');
INSERT INTO x1 VALUES('five');
INSERT INTO x1 VALUES('six');
INSERT INTO x1 VALUES('seven');
INSERT INTO x1 VALUES('eight');
INSERT INTO x1 VALUES('nine');
INSERT INTO x1 VALUES('ten');
}
do_execsql_test 4.1 {
SELECT level, segment FROM fts5_structure((
SELECT block FROM x1_data WHERE id=10
))
} {
0 0 0 1 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0 9
}
for {set ii 1} {$ii < 4} {incr ii} {
do_execsql_test 4.2.$ii {
DELETE FROM x1 WHERE rowid = $ii;
INSERT INTO x1(x1, rank) VALUES('merge', 5);
SELECT level, segment FROM fts5_structure((
SELECT block FROM x1_data WHERE id=10
))
} {
0 0 0 1 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0 9
}
}
do_execsql_test 4.3 {
DELETE FROM x1 WHERE rowid = $ii;
INSERT INTO x1(x1, rank) VALUES('merge', 5);
SELECT level, segment, nentry FROM fts5_structure((
SELECT block FROM x1_data WHERE id=10
))
} {
1 0 6
}
finish_test
-58
View File
@@ -1,58 +0,0 @@
# 2023 August 7
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# This file contains tests for the content= and content_rowid= options.
#
source [file join [file dirname [info script]] fts5_common.tcl]
set testprefix fts5contentless5
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
ifcapable !fts5 {
finish_test
return
}
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE t1 USING fts5(a, b, c, content='', contentless_delete=1);
INSERT INTO t1 VALUES('A', 'B', 'C');
INSERT INTO t1 VALUES('D', 'E', 'F');
INSERT INTO t1 VALUES('G', 'H', 'I');
}
do_execsql_test 1.01 {
CREATE TABLE t2(x, y);
INSERT INTO t2 VALUES('x', 'y');
}
# explain_i "UPDATE t1 SET a='a' WHERE t1.rowid=1"
breakpoint
explain_i "UPDATE t1 SET a='a' FROM t2 WHERE t1.rowid=1 AND b IS NULL"
#breakpoint
#explain_i "UPDATE t1 SET a='a' WHERE b IS NULL AND rowid=?"
foreach {tn up err} {
1 "UPDATE t1 SET a='a', b='b', c='c' WHERE rowid=1" 0
2 "UPDATE t1 SET a='a', b='b' WHERE rowid=1" 1
3 "UPDATE t1 SET b='b', c='c' WHERE rowid=1" 1
4 "UPDATE t1 SET a='a', c='c' WHERE rowid=1" 1
5 "UPDATE t1 SET a='a', c='c' WHERE t1.rowid=1 AND b IS NULL" 1
6 "UPDATE t1 SET a='a' FROM t2 WHERE t1.rowid=1" 1
7 "UPDATE t1 SET a='a', b='b', c='c' FROM t2 WHERE t1.rowid=1" 0
} {
set res(0) {0 {}}
set res(1) {1 {cannot UPDATE a subset of columns on fts5 contentless-delete table: t1}}
do_catchsql_test 1.$tn $up $res($err)
}
finish_test
-4
View File
@@ -48,10 +48,6 @@ do_test 1.3 {
}
catchsql { INSERT INTO t1(t1) VALUES('integrity-check') }
} {1 {database disk image is malformed}}
do_execsql_test 1.3b {
PRAGMA integrity_check(t1);
} {{malformed inverted index for FTS5 table main.t1}}
do_test 1.4 {
db_restore_and_reopen
-3
View File
@@ -167,9 +167,6 @@ foreach {tn hdr} {
do_test 3.$tn.$tn2.2 {
catchsql { INSERT INTO x3(x3) VALUES('integrity-check') }
} {1 {database disk image is malformed}}
do_execsql_test 3.$tn.$tn2.3 {
PRAGMA integrity_check(x3);
} {{malformed inverted index for FTS5 table main.x3}}
}
execsql ROLLBACK
Regular → Executable
View File
Regular → Executable
View File
-3
View File
@@ -95,9 +95,6 @@ do_execsql_test 3.3 {
SELECT rowid, bm25(e1) FROM e1 WHERE e1 MATCH '"/" OR "just"' ORDER BY rank;
} {1 -1e-06}
do_execsql_test 3.4 "
SELECT fts5_expr_tcl('e AND \" \"');
" {{AND [nearset -- {e}] [{}]}}
finish_test
-111
View File
@@ -1,111 +0,0 @@
# 2023 July 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 file is focused on OOM errors. Particularly those that may occur
# when using contentless_delete=1 databases.
#
source [file join [file dirname [info script]] fts5_common.tcl]
source $testdir/malloc_common.tcl
set testprefix fts5faultF
# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
ifcapable !fts5 {
finish_test
return
}
faultsim_save_and_close
do_faultsim_test 1 -prep {
faultsim_restore_and_reopen
} -body {
execsql {
CREATE VIRTUAL TABLE t1 USING fts5(x, y, content=, contentless_delete=1)
}
} -test {
faultsim_test_result {0 {}} {1 {vtable constructor failed: t1}}
}
reset_db
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE t1 USING fts5(doc, content=, contentless_delete=1);
BEGIN;
INSERT INTO t1(rowid, doc) VALUES(1, 'a b c d');
INSERT INTO t1(rowid, doc) VALUES(2, 'a b c d');
INSERT INTO t1(rowid, doc) VALUES(3, 'a b c d');
INSERT INTO t1(rowid, doc) VALUES(4, 'a b c d');
COMMIT;
DELETE FROM t1 WHERE rowid IN (2, 4);
}
do_faultsim_test 2 -prep {
sqlite3 db test.db
execsql { SELECT rowid FROM t1 }
} -body {
execsql {
SELECT rowid FROM t1('b c');
}
} -test {
faultsim_test_result {0 {1 3}}
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE t1 USING fts5(doc, content=, contentless_delete=1);
BEGIN;
INSERT INTO t1(rowid, doc) VALUES(1, 'a b c d');
INSERT INTO t1(rowid, doc) VALUES(2, 'a b c d');
INSERT INTO t1(rowid, doc) VALUES(3, 'a b c d');
INSERT INTO t1(rowid, doc) VALUES(4, 'a b c d');
COMMIT;
}
faultsim_save_and_close
do_faultsim_test 3 -prep {
faultsim_restore_and_reopen
execsql { SELECT rowid FROM t1 }
} -body {
execsql {
INSERT INTO t1(rowid, doc) VALUES(5, 'a b c d');
}
} -test {
faultsim_test_result {0 {}}
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 4.0 {
CREATE VIRTUAL TABLE t1 USING fts5(doc, content=, contentless_delete=1);
INSERT INTO t1(t1, rank) VALUES('pgsz', 64);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
)
INSERT INTO t1(rowid, doc) SELECT i, 'a b c d' FROM s;
}
do_execsql_test 4.1 { DELETE FROM t1 WHERE rowid <= 25 }
faultsim_save_and_close
do_faultsim_test 4 -faults oom-t* -prep {
faultsim_restore_and_reopen
execsql { SELECT rowid FROM t1 }
} -body {
execsql {
DELETE FROM t1 WHERE rowid < 100
}
} -test {
faultsim_test_result {0 {}}
}
finish_test
-50
View File
@@ -1,50 +0,0 @@
# 2010 June 15
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
source [file join [file dirname [info script]] fts5_common.tcl]
source $testdir/malloc_common.tcl
set testprefix fts5faultG
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
ifcapable !fts5 {
finish_test
return
}
set ::testprefix fts5faultG
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE t1 USING fts5(a);
INSERT INTO t1 VALUES('test renaming the table');
INSERT INTO t1 VALUES(' after it has been written');
INSERT INTO t1 VALUES(' actually other stuff instead');
}
faultsim_save_and_close
do_faultsim_test 1 -faults oom* -prep {
faultsim_restore_and_reopen
execsql {
BEGIN;
DELETE FROM t1 WHERE rowid=2;
}
} -body {
execsql {
DELETE FROM t1;
}
} -test {
catchsql { COMMIT }
faultsim_integrity_check
faultsim_test_result {0 {}}
}
finish_test
-38
View File
@@ -77,9 +77,6 @@ do_catchsql_test 4.2 {
UPDATE aa_docsize SET sz = X'44' WHERE rowid = 3;
INSERT INTO aa(aa) VALUES('integrity-check');
} {1 {database disk image is malformed}}
do_execsql_test 4.2.1 {
PRAGMA integrity_check(aa);
} {{malformed inverted index for FTS5 table main.aa}}
do_catchsql_test 4.3 {
ROLLBACK;
@@ -320,39 +317,4 @@ do_catchsql_test 10.5.3 {
INSERT INTO vt0(vt0) VALUES('integrity-check');
} {0 {}}
reset_db
proc slang {in} {return [string map {th d e eh} $in]}
db function slang -deterministic -innocuous slang
do_execsql_test 11.0 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT, c TEXT AS (slang(b)));
INSERT INTO t1(b) VALUES('the quick fox jumps over the lazy brown dog');
SELECT c FROM t1;
} {{deh quick fox jumps ovehr deh lazy brown dog}}
do_execsql_test 11.1 {
CREATE VIRTUAL TABLE t2 USING fts5(content="t1", c);
INSERT INTO t2(t2) VALUES('rebuild');
SELECT rowid FROM t2 WHERE t2 MATCH 'deh';
} {1}
do_execsql_test 11.2 {
PRAGMA integrity_check(t2);
} {ok}
db close
sqlite3 db test.db
# FIX ME?
#
# FTS5 integrity-check does not care if the content table is unreadable or
# does not exist. It only looks for internal inconsistencies in the
# inverted index.
#
do_execsql_test 11.3 {
PRAGMA integrity_check(t2);
} {ok}
do_execsql_test 11.4 {
DROP TABLE t1;
PRAGMA integrity_check(t2);
} {ok}
finish_test
Regular → Executable
View File
+3 -7
View File
@@ -44,12 +44,12 @@ do_catchsql_test 1.2.2 {
do_catchsql_test 1.3.1 {
SELECT highlight(t1, 4, '<b>', '</b>') FROM t1('*reads');
} {1 {no such cursor: 2}}
} {1 {no such cursor: 1}}
do_catchsql_test 1.3.2 {
SELECT a FROM t1
WHERE rank = (SELECT highlight(t1, 4, '<b>', '</b>') FROM t1('*reads'));
} {1 {no such cursor: 2}}
} {1 {no such cursor: 1}}
db close
sqlite3 db test.db
@@ -424,12 +424,10 @@ do_execsql_test -db db2 15.3 {
SAVEPOINT one;
} {}
do_execsql_test 15.4 END
do_test 15.5 {
do_test 15.4 {
list [catch { db2 eval COMMIT } msg] $msg
} {0 {}}
db2 close
#-------------------------------------------------------------------------
reset_db
forcedelete test.db2
@@ -471,8 +469,6 @@ do_execsql_test -db db2 16.6 {
SELECT * FROM x1
} {abc def}
db2 close
#-------------------------------------------------------------------------
reset_db
do_execsql_test 17.1 {
+20 -1
View File
@@ -9,7 +9,7 @@
#
#***********************************************************************
#
# TESTRUNNER: superslow
# TESTRUNNER: slow
#
source [file join [file dirname [info script]] fts5_common.tcl]
@@ -42,4 +42,23 @@ do_execsql_test 1.2 {
SELECT count(*) FROM t1('mno')
} $nLoop
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE t2 USING fts5(x);
INSERT INTO t2(t2, rank) VALUES('pgsz', 32);
}
do_test 2.1 {
for {set ii 0} {$ii < $nLoop} {incr ii} {
execsql {
INSERT INTO t2 VALUES('abc def ghi');
INSERT INTO t2 VALUES('jkl mno pqr');
INSERT INTO t2(t2, rank) VALUES('merge', -1);
}
}
} {}
do_execsql_test 2.2 {
SELECT count(*) FROM t2('mno')
} $nLoop
finish_test
-45
View File
@@ -1,45 +0,0 @@
# 2023 Aug 27
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# TESTRUNNER: superslow
#
source [file join [file dirname [info script]] fts5_common.tcl]
set testprefix fts5optimize2
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
ifcapable !fts5 {
finish_test
return
}
set nLoop 2500
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE t2 USING fts5(x);
INSERT INTO t2(t2, rank) VALUES('pgsz', 32);
}
do_test 1.1 {
for {set ii 0} {$ii < $nLoop} {incr ii} {
execsql {
INSERT INTO t2 VALUES('abc def ghi');
INSERT INTO t2 VALUES('jkl mno pqr');
INSERT INTO t2(t2, rank) VALUES('merge', -1);
}
}
} {}
do_execsql_test 1.2 {
SELECT count(*) FROM t2('mno')
} $nLoop
finish_test
-30
View File
@@ -52,36 +52,6 @@ do_execsql_test 2.1 {
SELECT * FROM t2('to*');
} {top to tommy}
#-------------------------------------------------------------------------
foreach {tn newrowid} {
1 122
2 123
3 124
} {
reset_db
do_execsql_test 3.$tn.0 {
CREATE VIRTUAL TABLE t12 USING fts5(x);
INSERT INTO t12(rowid, x) VALUES(123, 'wwww');
}
do_execsql_test 3.$tn.1 {
BEGIN;
DELETE FROM t12 WHERE rowid=123;
SELECT * FROM t12('wwww*');
INSERT INTO t12(rowid, x) VALUES($newrowid, 'wwww');
SELECT * FROM t12('wwww*');
END;
} {wwww}
do_execsql_test 3.$tn.2 {
INSERT INTO t12(t12) VALUES('integrity-check');
}
do_execsql_test 3.$tn.3 {
SELECT rowid FROM t12('wwww*');
} $newrowid
}
finish_test
finish_test
-24
View File
@@ -180,28 +180,4 @@ do_execsql_test 6.1 {
{table table table} {the table names.} {rank on an fts5 table}
}
#-------------------------------------------------------------------------
# forum post: https://sqlite.org/forum/forumpost/a2dd636330
#
reset_db
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE t USING fts5 (a, b);
INSERT INTO t (a, b) VALUES ('data1', 'sentence1'), ('data2', 'sentence2');
INSERT INTO t(t, rank) VALUES ('rank', 'bm25(10.0,1.0)');
}
sqlite3 db2 test.db
do_execsql_test -db db2 1.1 {
SELECT *, rank<0.0 FROM t('data*') ORDER BY RANK;
} {data1 sentence1 1 data2 sentence2 1}
do_execsql_test 1.2 {
INSERT INTO t(t, rank) VALUES ('rank', 'bm25(10.0,1.0)');
}
do_execsql_test -db db2 1.3 {
SELECT *, rank<0.0 FROM t('data*') ORDER BY RANK;
} {data1 sentence1 1 data2 sentence2 1}
db2 close
finish_test
+1 -1
View File
@@ -71,7 +71,7 @@ ifcapable fts3 {
do_catchsql_test 3.2 {
DROP TABLE vt1;
} {0 {}}
} {1 {SQL logic error}}
do_execsql_test 3.3 {
SAVEPOINT x;
-70
View File
@@ -273,76 +273,6 @@ do_execsql_test 5.3 {
do_execsql_test 5.4 { SELECT rowid FROM t1('abc'); } 2
do_execsql_test 5.5 { SELECT rowid FROM t1('aa'); } 2
#-------------------------------------------------------------------------
# Tests for the bug fixed by https://sqlite.org/src/info/4b60a1c3
#
reset_db
do_execsql_test 6.0 {
CREATE VIRTUAL TABLE fts USING fts5(content);
INSERT INTO fts(fts, rank) VALUES ('secure-delete', 1);
INSERT INTO fts(rowid, content) VALUES
(3407, 'profile profile profile profile profile profile profile profile pull pulling pulling really');
DELETE FROM fts WHERE rowid IS 3407;
INSERT INTO fts(fts) VALUES ('integrity-check');
}
foreach {tn detail} {
1 full
2 column
3 none
} {
do_execsql_test 6.1.$detail "
DROP TABLE IF EXISTS t1;
CREATE VIRTUAL TABLE t1 USING fts5(x, detail=$detail);
"
do_execsql_test 6.2.$detail {
INSERT INTO t1(t1, rank) VALUES('secure-delete', 1);
}
for {set ii 1} {$ii < 100} {incr ii} {
do_execsql_test 6.3.$detail.$ii.1 {
BEGIN;
INSERT INTO t1(rowid, x) VALUES(10, 'word1');
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<CAST($ii AS integer)
)
INSERT INTO t1(x) SELECT 'word3' FROM s;
COMMIT;
INSERT INTO t1(t1) VALUES('optimize');
}
do_execsql_test 6.3.$detail.$ii.2 {
DELETE FROM t1 WHERE rowid=10;
INSERT INTO t1(t1) VALUES ('integrity-check');
}
do_execsql_test 6.3.$detail.$ii.3 {
DELETE FROM t1;
}
do_execsql_test 6.3.$detail.$ii.4 {
BEGIN;
INSERT INTO t1(rowid, x) VALUES(10, 'tokenA');
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<CAST($ii AS integer)
)
INSERT INTO t1(x) SELECT group_concat('tokenB ') FROM s;
COMMIT;
INSERT INTO t1(t1) VALUES('optimize');
}
do_execsql_test 6.3.$detail.$ii.5 {
DELETE FROM t1 WHERE rowid=10;
INSERT INTO t1(t1) VALUES ('integrity-check');
}
do_execsql_test 6.3.$detail.$ii.6 {
DELETE FROM t1;
}
}
}
finish_test
+1 -87
View File
@@ -18,7 +18,7 @@ db progress 1 progress_handler
set ::PHC 0
proc progress_handler {args} {
incr ::PHC
# if {($::PHC % 100000)==0} breakpoint
if {($::PHC % 100000)==0} breakpoint
return 0
}
@@ -50,92 +50,6 @@ do_test 1.3 {
expr $phc(1)*5 < $phc(2)
} {1}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE t1 USING fts5(x);
INSERT INTO t1(t1, rank) VALUES('secure-delete', $sd)
}
do_execsql_test 2.1 {
BEGIN;
INSERT INTO t1(rowid, x) VALUES(-100000, 'abc def ghi');
INSERT INTO t1(rowid, x) VALUES(-99999, 'abc def ghi');
INSERT INTO t1(rowid, x) VALUES(9223372036854775800, 'abc def ghi');
COMMIT;
}
do_execsql_test 2.2 {
SELECT rowid FROM t1('def')
} {-100000 -99999 9223372036854775800}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE t1 USING fts5(x);
INSERT INTO t1(t1, rank) VALUES('secure-delete', $sd)
}
do_execsql_test 3.1 {
BEGIN;
INSERT INTO t1(rowid, x)
VALUES(51869, 'when whenever where weress what turn'),
(51871, 'to were');
COMMIT;
}
do_execsql_test 3.2 {
DELETE FROM t1 WHERE rowid=51871;
INSERT INTO t1(t1) VALUES('integrity-check');
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 4.0 {
CREATE VIRTUAL TABLE t1 USING fts5(x);
INSERT INTO t1(rowid, x) VALUES(10, 'one two');
}
do_execsql_test 4.1 {
UPDATE t1 SET x = 'one three' WHERE rowid=10;
INSERT INTO t1(t1, rank) VALUES('secure-delete', 1);
}
do_execsql_test 4.2 {
DELETE FROM t1 WHERE rowid=10;
}
do_execsql_test 4.3 {
INSERT INTO t1(t1) VALUES('integrity-check');
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 5.0 {
CREATE VIRTUAL TABLE t1 USING fts5(content);
INSERT INTO t1(t1,rank) VALUES('secure-delete',1);
INSERT INTO t1 VALUES('active'),('boomer'),('atom'),('atomic'),
('alpha channel backup abandon test aback boomer atom alpha active');
DELETE FROM t1 WHERE t1 MATCH 'abandon';
}
do_execsql_test 5.1 {
INSERT INTO t1(t1) VALUES('rebuild');
}
do_execsql_test 5.2 {
DELETE FROM t1 WHERE rowid NOTNULL<5;
}
db close
sqlite3 db test.db
do_execsql_test 5.3 {
PRAGMA integrity_check;
} {ok}
finish_test
-116
View File
@@ -1,116 +0,0 @@
# 2023 Feb 17
#
# 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.
#
#*************************************************************************
#
# TESTRUNNER: slow
#
source [file join [file dirname [info script]] fts5_common.tcl]
ifcapable !fts5 { finish_test ; return }
set ::testprefix fts5secure7
set NVOCAB 500
set NDOC [expr 1000]
set NREP 100
set nDeletePerRep [expr 5]
set VOCAB [list]
proc select_one {list} {
set n [llength $list]
lindex $list [expr {abs(int(rand()*$n))}]
}
proc init_vocab {} {
set L [split "abcdefghijklmnopqrstuvwxyz" {}]
set nL [llength $L]
for {set i 0} {$i < $::NVOCAB} {incr i} {
set n [expr {6 + int(rand()*8)}]
set word ""
for {set j 0} {$j < $n} {incr j} {
append word [select_one $L]
}
lappend ::VOCAB $word
}
}
proc get_word {} {
select_one $::VOCAB
}
proc get_document {nWord} {
set ret [list]
for {set i 0} {$i < $nWord} {incr i} {
lappend ret [get_word]
}
return $ret
}
init_vocab
db func document [list get_document 12]
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE t1 USING fts5(body);
INSERT INTO t1(t1, rank) VALUES('secure-delete', 1);
}
do_execsql_test 1.1 {
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<$NDOC
)
INSERT INTO t1 SELECT document() FROM s;
}
for {set iRep 0} {$iRep < $NREP} {incr iRep} {
set lRowid [db eval {SELECT rowid FROM t1}]
for {set iDel 0} {$iDel < $nDeletePerRep} {incr iDel} {
set idx [select_one $lRowid]
db eval {
DELETE FROM t1 WHERE rowid=$idx
}
}
db eval {
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<$nDeletePerRep
)
INSERT INTO t1 SELECT document() FROM s;
}
do_execsql_test 1.2.$iRep {
INSERT INTO t1(t1) VALUES('integrity-check');
}
}
reset_db
db func document [list get_document 12]
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE t1 USING fts5(body);
INSERT INTO t1(t1, rank) VALUES('secure-delete', 1);
INSERT INTO t1(t1, rank) VALUES('pgsz', 128);
}
do_execsql_test 2.1 {
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<$NDOC
)
INSERT INTO t1 SELECT document() FROM s;
}
for {set ii 0} {$ii < $NDOC} {incr ii} {
set lRowid [db eval {SELECT rowid FROM t1}]
set idx [select_one $lRowid]
db eval { DELETE FROM t1 WHERE rowid=$idx }
do_execsql_test 2.2.$ii {
INSERT INTO t1(t1) VALUES('integrity-check');
}
}
finish_test
-3
View File
@@ -122,9 +122,6 @@ foreach {tn expr} {
4.1 "NEAR(one two, 2)"
4.2 "NEAR(one two three, 2)"
4.3 "NEAR(eight nine, 1) OR NEAR(six seven, 1)"
5.1 "one + two"
5.2 "1 + two"
} {
if {[fts5_expr_ok $expr ss]==0} {
do_test 1.$tok.$tn.OMITTED { list } [list]
-89
View File
@@ -1,89 +0,0 @@
# 2023 Nov 03
#
# 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.
#
#***********************************************************************
#
# Tests focusing on the built-in fts5 tokenizers.
#
source [file join [file dirname [info script]] fts5_common.tcl]
set testprefix fts5tokenizer2
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
ifcapable !fts5 {
finish_test
return
}
sqlite3_fts5_create_tokenizer db tst get_tst_tokenizer
proc get_tst_tokenizer {args} {
return "tst_tokenizer"
}
proc tst_tokenizer {flags txt} {
set token ""
set lTok [list]
foreach c [split $txt {}] {
if {$token==""} {
append token $c
} else {
set t1 [string is upper $token]
set t2 [string is upper $c]
if {$t1!=$t2} {
lappend lTok $token
set token ""
}
append token $c
}
}
if {$token!=""} { lappend lTok $token }
set iOff 0
foreach t $lTok {
set n [string length $t]
sqlite3_fts5_token $t $iOff [expr $iOff+$n]
incr iOff $n
}
}
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE t1 USING fts5(t, tokenize=tst);
}
do_execsql_test 1.1 {
INSERT INTO t1 VALUES('AAdontBBmess');
}
do_execsql_test 1.2 {
SELECT snippet(t1, 0, '>', '<', '...', 4) FROM t1('BB');
} {AAdont>BB<mess}
do_execsql_test 1.3 {
SELECT highlight(t1, 0, '>', '<') FROM t1('BB');
} {AAdont>BB<mess}
do_execsql_test 1.4 {
SELECT highlight(t1, 0, '>', '<') FROM t1('AA');
} {>AA<dontBBmess}
do_execsql_test 1.5 {
SELECT highlight(t1, 0, '>', '<') FROM t1('dont');
} {AA>dont<BBmess}
do_execsql_test 1.6 {
SELECT highlight(t1, 0, '>', '<') FROM t1('mess');
} {AAdontBB>mess<}
do_execsql_test 1.7 {
SELECT highlight(t1, 0, '>', '<') FROM t1('BB mess');
} {AAdont>BBmess<}
finish_test
-38
View File
@@ -215,42 +215,4 @@ do_execsql_test 7.2 {
SELECT rowid FROM f WHERE filename GLOB '*ир*';
} {20}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 8.0 {
CREATE VIRTUAL TABLE t1 USING fts5(y, tokenize=trigram);
INSERT INTO t1 VALUES('abcdefghijklm');
}
foreach {tn match res} {
1 "abc ghi" "(abc)def(ghi)jklm"
2 "def ghi" "abc(defghi)jklm"
3 "efg ghi" "abcd(efghi)jklm"
4 "efghi" "abcd(efghi)jklm"
5 "abcd jklm" "(abcd)efghi(jklm)"
6 "ijkl jklm" "abcdefgh(ijklm)"
7 "ijk ijkl hijk" "abcdefg(hijkl)m"
} {
do_execsql_test 8.1.$tn {
SELECT highlight(t1, 0, '(', ')') FROM t1($match)
} $res
}
do_execsql_test 8.2 {
CREATE VIRTUAL TABLE ft2 USING fts5(a, tokenize="trigram");
INSERT INTO ft2 VALUES('abc x cde');
INSERT INTO ft2 VALUES('abc cde');
INSERT INTO ft2 VALUES('abcde');
}
do_execsql_test 8.3 {
SELECT highlight(ft2, 0, '[', ']') FROM ft2 WHERE ft2 MATCH 'abc AND cde';
} {
{[abc] x [cde]}
{[abc] [cde]}
{[abcde]}
}
finish_test
-109
View File
@@ -1,109 +0,0 @@
# 2023 October 24
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#*************************************************************************
#
# Tests for the fts5 "trigram" tokenizer.
#
source [file join [file dirname [info script]] fts5_common.tcl]
ifcapable !fts5 { finish_test ; return }
set ::testprefix fts5trigram2
do_execsql_test 1.0 "
CREATE VIRTUAL TABLE t1 USING fts5(y, tokenize='trigram remove_diacritics 1');
INSERT INTO t1 VALUES('abc\u0303defghijklm');
INSERT INTO t1 VALUES('a\u0303b\u0303c\u0303defghijklm');
"
do_execsql_test 1.1 {
SELECT highlight(t1, 0, '(', ')') FROM t1('abc');
} [list \
"(abc\u0303)defghijklm" \
"(a\u0303b\u0303c\u0303)defghijklm" \
]
do_execsql_test 1.2 {
SELECT highlight(t1, 0, '(', ')') FROM t1('bcde');
} [list \
"a(bc\u0303de)fghijklm" \
"a\u0303(b\u0303c\u0303de)fghijklm" \
]
do_execsql_test 1.3 {
SELECT highlight(t1, 0, '(', ')') FROM t1('cdef');
} [list \
"ab(c\u0303def)ghijklm" \
"a\u0303b\u0303(c\u0303def)ghijklm" \
]
do_execsql_test 1.4 {
SELECT highlight(t1, 0, '(', ')') FROM t1('def');
} [list \
"abc\u0303(def)ghijklm" \
"a\u0303b\u0303c\u0303(def)ghijklm" \
]
#-------------------------------------------------------------------------
do_catchsql_test 2.0 {
CREATE VIRTUAL TABLE t2 USING fts5(
z, tokenize='trigram case_sensitive 1 remove_diacritics 1'
);
} {1 {error in tokenizer constructor}}
do_execsql_test 2.1 {
CREATE VIRTUAL TABLE t2 USING fts5(
z, tokenize='trigram case_sensitive 0 remove_diacritics 1'
);
}
do_execsql_test 2.2 "
INSERT INTO t2 VALUES('\u00E3bcdef');
INSERT INTO t2 VALUES('b\u00E3cdef');
INSERT INTO t2 VALUES('bc\u00E3def');
INSERT INTO t2 VALUES('bcd\u00E3ef');
"
do_execsql_test 2.3 {
SELECT highlight(t2, 0, '(', ')') FROM t2('abc');
} "(\u00E3bc)def"
do_execsql_test 2.4 {
SELECT highlight(t2, 0, '(', ')') FROM t2('bac');
} "(b\u00E3c)def"
do_execsql_test 2.5 {
SELECT highlight(t2, 0, '(', ')') FROM t2('bca');
} "(bc\u00E3)def"
do_execsql_test 2.6 "
SELECT highlight(t2, 0, '(', ')') FROM t2('\u00E3bc');
" "(\u00E3bc)def"
#-------------------------------------------------------------------------
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE t3 USING fts5(
z, tokenize='trigram remove_diacritics 1'
);
} {}
do_execsql_test 3.1 "
INSERT INTO t3 VALUES ('\u0303abc\u0303');
"
do_execsql_test 3.2 {
SELECT highlight(t3, 0, '(', ')') FROM t3('abc');
} "\u0303(abc\u0303)"
#-------------------------------------------------------------------------
do_execsql_test 4.0 {
CREATE VIRTUAL TABLE t4 USING fts5(z, tokenize=trigram);
} {}
breakpoint
do_execsql_test 4.1 {
INSERT INTO t4 VALUES('ABCD');
} {}
finish_test
-505
View File
@@ -1,505 +0,0 @@
# Quick-and-dirty makefile to bootstrap the sqlite3-jni project. This
# build assumes a Linux-like system.
default: all
JAVA_HOME ?= $(HOME)/jdk/current
# e.g. /usr/lib/jvm/default-javajava-19-openjdk-amd64
JDK_HOME ?= $(JAVA_HOME)
# ^^^ JDK_HOME is not as widely used as JAVA_HOME
bin.jar := $(JDK_HOME)/bin/jar
bin.java := $(JDK_HOME)/bin/java
bin.javac := $(JDK_HOME)/bin/javac
bin.javadoc := $(JDK_HOME)/bin/javadoc
ifeq (,$(wildcard $(JDK_HOME)))
$(error set JDK_HOME to the top-most dir of your JDK installation.)
endif
MAKEFILE := $(lastword $(MAKEFILE_LIST))
$(MAKEFILE):
package.jar := sqlite3-jni.jar
dir.top := ../..
dir.tool := ../../tool
dir.jni := $(patsubst %/,%,$(dir $(MAKEFILE)))
dir.src := $(dir.jni)/src
dir.src.c := $(dir.src)/c
dir.bld := $(dir.jni)/bld
dir.bld.c := $(dir.bld)
dir.src.jni := $(dir.src)/org/sqlite/jni
dir.src.capi := $(dir.src.jni)/capi
dir.src.fts5 := $(dir.src.jni)/fts5
dir.tests := $(dir.src)/tests
mkdir ?= mkdir -p
$(dir.bld.c):
$(mkdir) $@
javac.flags ?= -Xlint:unchecked -Xlint:deprecation
java.flags ?=
javac.flags += -encoding utf8
# -------------^^^^^^^^^^^^^^ required for Windows builds
jnicheck ?= 1
ifeq (1,$(jnicheck))
java.flags += -Xcheck:jni
endif
classpath := $(dir.src)
CLEAN_FILES := $(package.jar)
DISTCLEAN_FILES := $(dir.jni)/*~ $(dir.src.c)/*~ $(dir.src.jni)/*~
sqlite3-jni.h := $(dir.src.c)/sqlite3-jni.h
.NOTPARALLEL: $(sqlite3-jni.h)
CApi.java := $(dir.src.capi)/CApi.java
SQLTester.java := $(dir.src.capi)/SQLTester.java
CApi.class := $(CApi.java:.java=.class)
SQLTester.class := $(SQLTester.java:.java=.class)
########################################################################
# The future of FTS5 customization in this API is as yet unclear.
# The pieces are all in place, and are all thin proxies so not much
# complexity, but some semantic changes were required in porting
# which are largely untested.
#
# Reminder: this flag influences the contents of $(sqlite3-jni.h),
# which is checked in. Please do not check in changes to that file in
# which the fts5 APIs have been stripped unless that feature is
# intended to be stripped for good.
enable.fts5 ?= 1
ifeq (,$(wildcard $(dir.tests)/*))
enable.tester := 0
else
enable.tester := 1
endif
# bin.version-info = binary to output various sqlite3 version info
# building the distribution zip file.
bin.version-info := $(dir.top)/version-info
.NOTPARALLEL: $(bin.version-info)
$(bin.version-info): $(dir.tool)/version-info.c $(sqlite3.h) $(dir.top)/Makefile
$(MAKE) -C $(dir.top) version-info
# Be explicit about which Java files to compile so that we can work on
# in-progress files without requiring them to be in a compilable statae.
JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/annotation/%,\
Experimental.java \
NotNull.java \
Nullable.java \
) $(patsubst %,$(dir.src.capi)/%,\
AbstractCollationCallback.java \
AggregateFunction.java \
AuthorizerCallback.java \
AutoExtensionCallback.java \
BusyHandlerCallback.java \
CollationCallback.java \
CollationNeededCallback.java \
CommitHookCallback.java \
ConfigLogCallback.java \
ConfigSqlLogCallback.java \
NativePointerHolder.java \
OutputPointer.java \
PrepareMultiCallback.java \
PreupdateHookCallback.java \
ProgressHandlerCallback.java \
ResultCode.java \
RollbackHookCallback.java \
ScalarFunction.java \
SQLFunction.java \
CallbackProxy.java \
CApi.java \
TableColumnMetadata.java \
TraceV2Callback.java \
UpdateHookCallback.java \
ValueHolder.java \
WindowFunction.java \
XDestroyCallback.java \
sqlite3.java \
sqlite3_blob.java \
sqlite3_context.java \
sqlite3_stmt.java \
sqlite3_value.java \
) $(patsubst %,$(dir.src.jni)/wrapper1/%,\
AggregateFunction.java \
ScalarFunction.java \
SqlFunction.java \
Sqlite.java \
SqliteException.java \
ValueHolder.java \
WindowFunction.java \
)
JAVA_FILES.unittest := $(patsubst %,$(dir.src.jni)/%,\
capi/Tester1.java \
wrapper1/Tester2.java \
)
ifeq (1,$(enable.fts5))
JAVA_FILES.unittest += $(patsubst %,$(dir.src.fts5)/%,\
TesterFts5.java \
)
JAVA_FILES.main += $(patsubst %,$(dir.src.fts5)/%,\
fts5_api.java \
fts5_extension_function.java \
fts5_tokenizer.java \
Fts5.java \
Fts5Context.java \
Fts5ExtensionApi.java \
Fts5PhraseIter.java \
Fts5Tokenizer.java \
XTokenizeCallback.java \
)
endif
JAVA_FILES.tester := $(SQLTester.java)
JAVA_FILES.package.info := \
$(dir.src.jni)/package-info.java \
$(dir.src.jni)/annotation/package-info.java
CLASS_FILES.main := $(JAVA_FILES.main:.java=.class)
CLASS_FILES.unittest := $(JAVA_FILES.unittest:.java=.class)
CLASS_FILES.tester := $(JAVA_FILES.tester:.java=.class)
JAVA_FILES += $(JAVA_FILES.main) $(JAVA_FILES.unittest)
ifeq (1,$(enable.tester))
JAVA_FILES += $(JAVA_FILES.tester)
endif
CLASS_FILES :=
define CLASSFILE_DEPS
all: $(1).class
$(1).class: $(1).java
CLASS_FILES += $(1).class
endef
$(foreach B,$(basename \
$(JAVA_FILES.main) $(JAVA_FILES.unittest) $(JAVA_FILES.tester)),\
$(eval $(call CLASSFILE_DEPS,$(B))))
$(CLASS_FILES): $(MAKEFILE)
$(bin.javac) $(javac.flags) -h $(dir.bld.c) -cp $(classpath) $(JAVA_FILES)
#.PHONY: classfiles
########################################################################
# Set up sqlite3.c and sqlite3.h...
#
# To build with SEE (https://sqlite.org/see), either put sqlite3-see.c
# in the top of this build tree or pass
# sqlite3.c=PATH_TO_sqlite3-see.c to the build. Note that only
# encryption modules with no 3rd-party dependencies will currently
# work here: AES256-OFB, AES128-OFB, and AES128-CCM. Not
# coincidentally, those 3 modules are included in the sqlite3-see.c
# bundle.
#
# A custom sqlite3.c must not have any spaces in its name.
# $(sqlite3.canonical.c) must point to the sqlite3.c in
# the sqlite3 canonical source tree, as that source file
# is required for certain utility and test code.
sqlite3.canonical.c := $(firstword $(wildcard $(dir.src.c)/sqlite3.c) $(dir.top)/sqlite3.c)
sqlite3.canonical.h := $(firstword $(wildcard $(dir.src.c)/sqlite3.h) $(dir.top)/sqlite3.h)
sqlite3.c := $(sqlite3.canonical.c)
sqlite3.h := $(sqlite3.canonical.h)
#ifeq (,$(shell grep sqlite3_activate_see $(sqlite3.c) 2>/dev/null))
# SQLITE_C_IS_SEE := 0
#else
# SQLITE_C_IS_SEE := 1
# $(info This is an SEE build.)
#endif
.NOTPARALLEL: $(sqlite3.h)
$(sqlite3.h):
$(MAKE) -C $(dir.top) sqlite3.c
$(sqlite3.c): $(sqlite3.h)
opt.threadsafe ?= 1
opt.fatal-oom ?= 1
opt.debug ?= 1
opt.metrics ?= 1
SQLITE_OPT = \
-DSQLITE_THREADSAFE=$(opt.threadsafe) \
-DSQLITE_TEMP_STORE=2 \
-DSQLITE_USE_URI=1 \
-DSQLITE_OMIT_LOAD_EXTENSION \
-DSQLITE_OMIT_DEPRECATED \
-DSQLITE_OMIT_SHARED_CACHE \
-DSQLITE_C=$(sqlite3.c) \
-DSQLITE_JNI_FATAL_OOM=$(opt.fatal-oom) \
-DSQLITE_JNI_ENABLE_METRICS=$(opt.metrics)
opt.extras ?= 1
ifeq (1,$(opt.extras))
SQLITE_OPT += -DSQLITE_ENABLE_RTREE \
-DSQLITE_ENABLE_EXPLAIN_COMMENTS \
-DSQLITE_ENABLE_STMTVTAB \
-DSQLITE_ENABLE_DBPAGE_VTAB \
-DSQLITE_ENABLE_DBSTAT_VTAB \
-DSQLITE_ENABLE_BYTECODE_VTAB \
-DSQLITE_ENABLE_OFFSET_SQL_FUNC \
-DSQLITE_ENABLE_PREUPDATE_HOOK \
-DSQLITE_ENABLE_NORMALIZE \
-DSQLITE_ENABLE_SQLLOG \
-DSQLITE_ENABLE_COLUMN_METADATA
endif
ifeq (1,$(opt.debug))
SQLITE_OPT += -DSQLITE_DEBUG -g -DDEBUG -UNDEBUG
else
SQLITE_OPT += -Os
endif
ifeq (1,$(enable.fts5))
SQLITE_OPT += -DSQLITE_ENABLE_FTS5
endif
sqlite3-jni.c := $(dir.src.c)/sqlite3-jni.c
sqlite3-jni.o := $(dir.bld.c)/sqlite3-jni.o
sqlite3-jni.h := $(dir.src.c)/sqlite3-jni.h
package.dll := $(dir.bld.c)/libsqlite3-jni.so
# All javac-generated .h files must be listed in $(sqlite3-jni.h.in):
sqlite3-jni.h.in :=
# $(java.with.jni) lists all Java files which contain JNI decls:
java.with.jni :=
define ADD_JNI_H
sqlite3-jni.h.in += $$(dir.bld.c)/org_sqlite_jni$(3)_$(2).h
java.with.jni += $(1)/$(2).java
$$(dir.bld.c)/org_sqlite_jni$(3)_$(2).h: $(1)/$(2).java
endef
# Invoke ADD_JNI_H once for each Java file which includes JNI
# declarations:
$(eval $(call ADD_JNI_H,$(dir.src.capi),CApi,_capi))
$(eval $(call ADD_JNI_H,$(dir.src.capi),SQLTester,_capi))
ifeq (1,$(enable.fts5))
$(eval $(call ADD_JNI_H,$(dir.src.fts5),Fts5ExtensionApi,_fts5))
$(eval $(call ADD_JNI_H,$(dir.src.fts5),fts5_api,_fts5))
$(eval $(call ADD_JNI_H,$(dir.src.fts5),fts5_tokenizer,_fts5))
endif
$(sqlite3-jni.h.in): $(dir.bld.c)
#package.dll.cfiles :=
package.dll.cflags = \
-std=c99 \
-fPIC \
-I. \
-I$(dir $(sqlite3.h)) \
-I$(dir.src.c) \
-I$(JDK_HOME)/include \
$(patsubst %,-I%,$(patsubst %.h,,$(wildcard $(JDK_HOME)/include/*))) \
-Wall
# The gross $(patsubst...) above is to include the platform-specific
# subdir which lives under $(JDK_HOME)/include and is a required
# include path for client-level code.
#
# Using (-Wall -Wextra) triggers an untennable number of
# gcc warnings from sqlite3.c for mundane things like
# unused parameters.
########################################################################
ifeq (1,$(enable.tester))
package.dll.cflags += -DSQLITE_JNI_ENABLE_SQLTester
endif
$(sqlite3-jni.h): $(sqlite3-jni.h.in) $(MAKEFILE)
@cat $(sqlite3-jni.h.in) > $@.tmp
@if cmp $@ $@.tmp >/dev/null; then \
rm -f $@.tmp; \
echo "$@ not modified"; \
else \
mv $@.tmp $@; \
echo "Updated $@"; \
fi
@if [ x1 != x$(enable.fts5) ]; then \
echo "*** REMINDER:"; \
echo "*** enable.fts5=0, so please do not check in changes to $@."; \
fi
$(package.dll): $(sqlite3-jni.h) $(sqlite3.c) $(sqlite3.h)
$(package.dll): $(sqlite3-jni.c) $(MAKEFILE)
$(CC) $(package.dll.cflags) $(SQLITE_OPT) \
$(sqlite3-jni.c) -shared -o $@
all: $(package.dll)
.PHONY: test test-one
Tester1.flags ?=
Tester2.flags ?=
test.flags.jvm = -ea -Djava.library.path=$(dir.bld.c) \
$(java.flags) -cp $(classpath)
test.deps := $(CLASS_FILES) $(package.dll)
test-one: $(test.deps)
$(bin.java) $(test.flags.jvm) org.sqlite.jni.capi.Tester1 $(Tester1.flags)
$(bin.java) $(test.flags.jvm) org.sqlite.jni.wrapper1.Tester2 $(Tester2.flags)
test-sqllog: $(test.deps)
@echo "Testing with -sqllog..."
$(bin.java) $(test.flags.jvm) org.sqlite.jni.capi.Tester1 $(Tester1.flags) -sqllog
test-mt: $(test.deps)
@echo "Testing in multi-threaded mode:";
$(bin.java) $(test.flags.jvm) org.sqlite.jni.capi.Tester1 \
-t 7 -r 50 -shuffle $(Tester1.flags)
$(bin.java) $(test.flags.jvm) org.sqlite.jni.wrapper1.Tester2 \
-t 7 -r 50 -shuffle $(Tester2.flags)
test: test-one test-mt
tests: test test-sqllog
tester.scripts := $(sort $(wildcard $(dir.src)/tests/*.test))
tester.flags ?= # --verbose
.PHONY: tester tester-local tester-ext
ifeq (1,$(enable.tester))
tester-local: $(CLASS_FILES.tester) $(package.dll)
$(bin.java) -ea -Djava.library.path=$(dir.bld.c) \
$(java.flags) -cp $(classpath) \
org.sqlite.jni.capi.SQLTester $(tester.flags) $(tester.scripts)
tester: tester-local
else
tester:
@echo "SQLTester support is disabled."
endif
tester.extdir.default := $(dir.tests)/ext
tester.extdir ?= $(tester.extdir.default)
tester.extern-scripts := $(wildcard $(tester.extdir)/*.test)
ifneq (,$(tester.extern-scripts))
tester-ext:
$(bin.java) -ea -Djava.library.path=$(dir.bld.c) \
$(java.flags) -cp $(classpath) \
org.sqlite.jni.capi.SQLTester $(tester.flags) $(tester.extern-scripts)
else
tester-ext:
@echo "******************************************************"; \
echo "*** Include the out-of-tree test suite in the 'tester'"; \
echo "*** target by either symlinking its directory to"; \
echo "*** $(tester.extdir.default) or passing it to make"; \
echo "*** as tester.extdir=/path/to/that/dir."; \
echo "******************************************************";
endif
tester-ext: tester-local
tester: tester-ext
tests: tester
########################################################################
# Build each SQLITE_THREADMODE variant and run all tests against them.
multitest: clean
define MULTIOPT
multitest: multitest-$(1)
multitest-$(1):
$$(MAKE) opt.debug=$$(opt.debug) $(patsubst %,opt.%,$(2)) \
tests clean enable.fts5=1
endef
$(eval $(call MULTIOPT,01,threadsafe=0 oom=1))
$(eval $(call MULTIOPT,00,threadsafe=0 oom=0))
$(eval $(call MULTIOPT,11,threadsafe=1 oom=1))
$(eval $(call MULTIOPT,10,threadsafe=1 oom=0))
$(eval $(call MULTIOPT,21,threadsafe=2 oom=1))
$(eval $(call MULTIOPT,20,threadsafe=2 oom=0))
########################################################################
# jar bundle...
package.jar.in := $(abspath $(dir.src)/jar.in)
CLEAN_FILES += $(package.jar.in)
JAVA_FILES.jar := $(JAVA_FILES.main) $(JAVA_FILES.unittest) $(JAVA_FILES.package.info)
CLASS_FILES.jar := $(filter-out %/package-info.class,$(JAVA_FILES.jar:.java=.class))
$(package.jar.in): $(package.dll) $(MAKEFILE)
ls -1 \
$(dir.src.jni)/*/*.java $(dir.src.jni)/*/*.class \
| sed -e 's,^$(dir.src)/,,' | sort > $@
$(package.jar): $(CLASS_FILES.jar) $(MAKEFILE) $(package.jar.in)
@rm -f $(dir.src)/c/*~ $(dir.src.jni)/*~
cd $(dir.src); $(bin.jar) -cfe ../$@ org.sqlite.jni.capi.Tester1 @$(package.jar.in)
@ls -la $@
@echo "To use this jar you will need the -Djava.library.path=DIR/CONTAINING/libsqlite3-jni.so flag."
@echo "e.g. java -Djava.library.path=bld -jar $@"
jar: $(package.jar)
run-jar: $(package.jar) $(package.dll)
$(bin.java) -Djava.library.path=$(dir.bld) -jar $(package.jar) $(run-jar.flags)
########################################################################
# javadoc...
dir.doc := $(dir.jni)/javadoc
doc.index := $(dir.doc)/index.html
javadoc.exclude := -exclude org.sqlite.jni.fts5
# ^^^^ 2023-09-13: elide the fts5 parts from the public docs for
# the time being, as it's not clear where the Java bindings for
# those bits are going.
# javadoc.exclude += -exclude org.sqlite.jni.capi
# ^^^^ exclude the capi API only for certain builds (TBD)
$(doc.index): $(JAVA_FILES.main) $(MAKEFILE)
@if [ -d $(dir.doc) ]; then rm -fr $(dir.doc)/*; fi
$(bin.javadoc) -cp $(classpath) -d $(dir.doc) -quiet \
-subpackages org.sqlite.jni $(javadoc.exclude)
@echo "javadoc output is in $@"
.PHONY: doc javadoc docserve
.FORCE: doc
doc: $(doc.index)
javadoc: $(doc.index)
# Force rebild of docs
redoc:
@rm -f $(doc.index)
@$(MAKE) doc
docserve: $(doc.index)
cd $(dir.doc) && althttpd -max-age 1 -page index.html
########################################################################
# Clean up...
CLEAN_FILES += $(dir.bld.c)/* \
$(dir.src.jni)/*.class \
$(dir.src.jni)/*/*.class \
$(package.dll) \
hs_err_pid*.log
.PHONY: clean distclean
clean:
-rm -f $(CLEAN_FILES)
distclean: clean
-rm -f $(DISTCLEAN_FILES)
-rm -fr $(dir.bld.c) $(dir.doc)
########################################################################
# disttribution bundle rules...
ifeq (,$(filter snapshot,$(MAKECMDGOALS)))
dist-name-prefix := sqlite-jni
else
dist-name-prefix := sqlite-jni-snapshot-$(shell /usr/bin/date +%Y%m%d)
endif
dist-name := $(dist-name-prefix)-TEMP
dist-dir.top := $(dist-name)
dist-dir.src := $(dist-dir.top)/src
dist.top.extras := \
README.md
.PHONY: dist snapshot
dist: \
$(bin.version-info) $(sqlite3.canonical.c) \
$(package.jar) $(MAKEFILE)
@echo "Making end-user deliverables..."
@echo "****************************************************************************"; \
echo "*** WARNING: be sure to build this with JDK8 (javac 1.8) for compatibility."; \
echo "*** reasons!"; $$($(bin.javac) -version); \
echo "****************************************************************************"
@rm -fr $(dist-dir.top)
@mkdir -p $(dist-dir.src)
@cp -p $(dist.top.extras) $(dist-dir.top)/.
@cp -p jar-dist.make $(dist-dir.top)/Makefile
@cp -p $(dir.src.c)/*.[ch] $(dist-dir.src)/.
@cp -p $(sqlite3.canonical.c) $(sqlite3.canonical.h) $(dist-dir.src)/.
@set -e; \
vnum=$$($(bin.version-info) --download-version); \
vjar=$$($(bin.version-info) --version); \
vdir=$(dist-name-prefix)-$$vnum; \
arczip=$$vdir.zip; \
cp -p $(package.jar) $(dist-dir.top)/sqlite3-jni-$${vjar}.jar; \
echo "Making $$arczip ..."; \
rm -fr $$arczip $$vdir; \
mv $(dist-dir.top) $$vdir; \
zip -qr $$arczip $$vdir; \
rm -fr $$vdir; \
ls -la $$arczip; \
set +e; \
unzip -lv $$arczip || echo "Missing unzip app? Not fatal."
snapshot: dist
.PHONY: dist-clean
clean: dist-clean
dist-clean:
rm -fr $(dist-name) $(wildcard sqlite-jni-*.zip)
-316
View File
@@ -1,316 +0,0 @@
SQLite3 via JNI
========================================================================
This directory houses a Java Native Interface (JNI) binding for the
sqlite3 API. If you are reading this from the distribution ZIP file,
links to resources in the canonical source tree will note work. The
canonical copy of this file can be browsed at:
<https://sqlite.org/src/doc/trunk/ext/jni/README.md>
Technical support is available in the forum:
<https://sqlite.org/forum>
> **FOREWARNING:** this subproject is very much in development and
subject to any number of changes. Please do not rely on any
information about its API until this disclaimer is removed. The JNI
bindings released with version 3.43 are a "tech preview." Once
finalized, strong backward compatibility guarantees will apply.
Project goals/requirements:
- A [1-to-1(-ish) mapping of the C API](#1to1ish) to Java via JNI,
insofar as cross-language semantics allow for. A closely-related
goal is that [the C documentation](https://sqlite.org/c3ref/intro.html)
should be usable as-is, insofar as possible, for the JNI binding.
- Support Java as far back as version 8 (2014).
- Environment-independent. Should work everywhere both Java
and SQLite3 do.
- No 3rd-party dependencies beyond the JDK. That includes no
build-level dependencies for specific IDEs and toolchains. We
welcome the addition of build files for arbitrary environments
insofar as they neither interfere with each other nor become
a maintenance burden for the sqlite developers.
Non-goals:
- Creation of high-level OO wrapper APIs. Clients are free to create
them off of the C-style API.
- Virtual tables are unlikely to be supported due to the amount of
glue code needed to fit them into Java.
- Support for mixed-mode operation, where client code accesses SQLite
both via the Java-side API and the C API via their own native
code. Such cases would be a minefield of potential mis-interactions
between this project's JNI bindings and mixed-mode client code.
Hello World
-----------------------------------------------------------------------
```java
import org.sqlite.jni.*;
import static org.sqlite.jni.CApi.*;
...
final sqlite3 db = sqlite3_open(":memory:");
try {
final int rc = sqlite3_errcode(db);
if( 0 != rc ){
if( null != db ){
System.out.print("Error opening db: "+sqlite3_errmsg(db));
}else{
System.out.print("Error opening db: rc="+rc);
}
... handle error ...
}
// ... else use the db ...
}finally{
// ALWAYS close databases using sqlite3_close() or sqlite3_close_v2()
// when done with them. All of their active statement handles must
// first have been passed to sqlite3_finalize().
sqlite3_close_v2(db);
}
```
Building
========================================================================
The canonical builds assumes a Linux-like environment and requires:
- GNU Make
- A JDK supporting Java 8 or higher
- A modern C compiler. gcc and clang should both work.
Put simply:
```console
$ export JAVA_HOME=/path/to/jdk/root
$ make
$ make test
$ make clean
```
The jar distribution can be created with `make jar`, but note that it
does not contain the binary DLL file. A different DLL is needed for
each target platform.
<a id='1to1ish'></a>
One-to-One(-ish) Mapping to C
========================================================================
This JNI binding aims to provide as close to a 1-to-1 experience with
the C API as cross-language semantics allow. Interface changes are
necessarily made where cross-language semantics do not allow a 1-to-1,
and judiciously made where a 1-to-1 mapping would be unduly cumbersome
to use in Java. In all cases, this binding makes every effort to
provide semantics compatible with the C API documentation even if the
interface to those semantics is slightly different. Any cases which
deviate from those semantics (either removing or adding semantics) are
clearly documented.
Where it makes sense to do so for usability, Java-side overloads are
provided which accept or return data in alternative forms or provide
sensible default argument values. In all such cases they are thin
proxies around the corresponding C APIs and do not introduce new
semantics.
In a few cases, Java-specific capabilities have been added in
new APIs, all of which have "_java" somewhere in their names.
Examples include:
- `sqlite3_result_java_object()`
- `sqlite3_column_java_object()`
- `sqlite3_value_java_object()`
which, as one might surmise, collectively enable the passing of
arbitrary Java objects from user-defined SQL functions through to the
caller.
Golden Rule: Garbage Collection Cannot Free SQLite Resources
------------------------------------------------------------------------
It is important that all databases and prepared statement handles get
cleaned up by client code. A database cannot be closed if it has open
statement handles. `sqlite3_close()` fails if the db cannot be closed
whereas `sqlite3_close_v2()` recognizes that case and marks the db as
a "zombie," pending finalization when the library detects that all
pending statements have been closed. Be aware that Java garbage
collection _cannot_ close a database or finalize a prepared statement.
Those things require explicit API calls.
Classes for which it is sensible support Java's `AutoCloseable`
interface so can be used with try-with-resources constructs.
Golden Rule #2: _Never_ Throw from Callbacks (Unless...)
------------------------------------------------------------------------
All routines in this API, barring explicitly documented exceptions,
retain C-like semantics. For example, they are not permitted to throw
or propagate exceptions and must return error information (if any) via
result codes or `null`. The only cases where the C-style APIs may
throw is through client-side misuse, e.g. passing in a null where it
may cause a `NullPointerException`. The APIs clearly mark function
parameters which should not be null, but does not generally actively
defend itself against such misuse. Some C-style APIs explicitly accept
`null` as a no-op for usability's sake, and some of the JNI APIs
deliberately return an error code, instead of segfaulting, when passed
a `null`.
Client-defined callbacks _must never throw exceptions_ unless _very
explitly documented_ as being throw-safe. Exceptions are generally
reserved for higher-level bindings which are constructed to
specifically deal with them and ensure that they do not leak C-level
resources. In some cases, callback handlers are permitted to throw, in
which cases they get translated to C-level result codes and/or
messages. If a callback which is not permitted to throw throws, its
exception may trigger debug output but will otherwise be suppressed.
The reason some callbacks are permitted to throw and others not is
because all such callbacks act as proxies for C function callback
interfaces and some of those interfaces have no error-reporting
mechanism. Those which are capable of propagating errors back through
the library convert exceptions from callbacks into corresponding
C-level error information. Those which cannot propagate errors
necessarily suppress any exceptions in order to maintain the C-style
semantics of the APIs.
Unwieldy Constructs are Re-mapped
------------------------------------------------------------------------
Some constructs, when modelled 1-to-1 from C to Java, are unduly
clumsy to work with in Java because they try to shoehorn C's way of
doing certain things into Java's wildly different ways. The following
subsections cover those, starting with a verbose explanation and
demonstration of where such changes are "really necessary"...
### Custom Collations
A prime example of where interface changes for Java are necessary for
usability is [registration of a custom
collation](https://sqlite.org/c3ref/create_collation.html):
```c
// C:
int sqlite3_create_collation(sqlite3 * db, const char * name, int eTextRep,
void *pUserData,
int (*xCompare)(void*,int,void const *,int,void const *));
int sqlite3_create_collation_v2(sqlite3 * db, const char * name, int eTextRep,
void *pUserData,
int (*xCompare)(void*,int,void const *,int,void const *),
void (*xDestroy)(void*));
```
The `pUserData` object is optional client-defined state for the
`xCompare()` and/or `xDestroy()` callback functions, both of which are
passed that object as their first argument. That data is passed around
"externally" in C because that's how C models the world. If we were to
bind that part as-is to Java, the result would be awkward to use (^Yes,
we tried this.):
```java
// Java:
int sqlite3_create_collation(sqlite3 db, String name, int eTextRep,
Object pUserData, xCompareType xCompare);
int sqlite3_create_collation_v2(sqlite3 db, String name, int eTextRep,
Object pUserData,
xCompareType xCompare, xDestroyType xDestroy);
```
The awkwardness comes from (A) having two distinctly different objects
for callbacks and (B) having their internal state provided separately,
which is ill-fitting in Java. For the sake of usability, C APIs which
follow that pattern use a slightly different Java interface:
```java
int sqlite3_create_collation(sqlite3 db, String name, int eTextRep,
SomeCallbackType collation);
```
Where the `Collation` class has an abstract `call()` method and
no-op `xDestroy()` method which can be overridden if needed, leading to
a much more Java-esque usage:
```java
int rc = sqlite3_create_collation(db, "mycollation", SQLITE_UTF8, new SomeCallbackType(){
// Required comparison function:
@Override public int call(byte[] lhs, byte[] rhs){ ... }
// Optional finalizer function:
@Override public void xDestroy(){ ... }
// Optional local state:
private String localState1 =
"This is local state. There are many like it, but this one is mine.";
private MyStateType localState2 = new MyStateType();
...
});
```
Noting that:
- It is possible to bind in call-scope-local state via closures, if
desired, as opposed to packing it into the Collation object.
- No capabilities of the C API are lost or unduly obscured via the
above API reshaping, so power users need not make any compromises.
- In the specific example above, `sqlite3_create_collation_v2()`
becomes superfluous because the provided interface effectively
provides both the v1 and v2 interfaces, the difference being that
overriding the `xDestroy()` method effectively gives it v2
semantics.
### User-defined SQL Functions (a.k.a. UDFs)
The [`sqlite3_create_function()`](https://sqlite.org/c3ref/create_function.html)
family of APIs make heavy use of function pointers to provide
client-defined callbacks, necessitating interface changes in the JNI
binding. The Java API has only one core function-registration function:
```java
int sqlite3_create_function(sqlite3 db, String funcName, int nArgs,
int encoding, SQLFunction func);
```
> Design question: does the encoding argument serve any purpose in
Java? That's as-yet undetermined. If not, it will be removed.
`SQLFunction` is not used directly, but is instead instantiated via
one of its three subclasses:
- `ScalarFunction` implements simple scalar functions using but a
single callback.
- `AggregateFunction` implements aggregate functions using two
callbacks.
- `WindowFunction` implements window functions using four
callbacks.
Search [`Tester1.java`](/file/ext/jni/src/org/sqlite/jni/capi/Tester1.java) for
`SQLFunction` for how it's used.
Reminder: see the disclaimer at the top of this document regarding the
in-flux nature of this API.
### And so on...
Various APIs which accept callbacks, e.g. `sqlite3_trace_v2()` and
`sqlite3_update_hook()`, use interfaces similar to those shown above.
Despite the changes in signature, the JNI layer makes every effort to
provide the same semantics as the C API documentation suggests.
-60
View File
@@ -1,60 +0,0 @@
#!/this/is/make
#^^^^ help emacs out
#
# This is a POSIX-make-compatible makefile for building the sqlite3
# JNI library from "dist" zip file. It must be edited to set the
# proper top-level JDK directory and, depending on the platform, add a
# platform-specific -I directory. It should build as-is with any
# 2020s-era version of gcc or clang. It requires JDK version 8 or
# higher and that JAVA_HOME points to the top-most installation
# directory of that JDK. On Ubuntu-style systems the JDK is typically
# installed under /usr/lib/jvm/java-VERSION-PLATFORM.
default: all
JAVA_HOME = /usr/lib/jvm/java-1.8.0-openjdk-amd64
CFLAGS = \
-fPIC \
-Isrc \
-I$(JAVA_HOME)/include \
-I$(JAVA_HOME)/include/linux \
-I$(JAVA_HOME)/include/apple \
-I$(JAVA_HOME)/include/bsd \
-Wall
SQLITE_OPT = \
-DSQLITE_ENABLE_RTREE \
-DSQLITE_ENABLE_EXPLAIN_COMMENTS \
-DSQLITE_ENABLE_STMTVTAB \
-DSQLITE_ENABLE_DBPAGE_VTAB \
-DSQLITE_ENABLE_DBSTAT_VTAB \
-DSQLITE_ENABLE_BYTECODE_VTAB \
-DSQLITE_ENABLE_OFFSET_SQL_FUNC \
-DSQLITE_OMIT_LOAD_EXTENSION \
-DSQLITE_OMIT_DEPRECATED \
-DSQLITE_OMIT_SHARED_CACHE \
-DSQLITE_THREADSAFE=1 \
-DSQLITE_TEMP_STORE=2 \
-DSQLITE_USE_URI=1 \
-DSQLITE_ENABLE_FTS5 \
-DSQLITE_DEBUG
sqlite3-jni.dll = libsqlite3-jni.so
$(sqlite3-jni.dll):
@echo "************************************************************************"; \
echo "*** If this fails to build, be sure to edit this makefile ***"; \
echo "*** to configure it for your system. ***"; \
echo "************************************************************************"
$(CC) $(CFLAGS) $(SQLITE_OPT) \
src/sqlite3-jni.c -shared -o $@
@echo "Now try running it with: make test"
test.flags = -Djava.library.path=. sqlite3-jni-*.jar
test: $(sqlite3-jni.dll)
java -jar $(test.flags)
java -jar $(test.flags) -t 7 -r 10 -shuffle
clean:
-rm -f $(sqlite3-jni.dll)
all: $(sqlite3-jni.dll)
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,30 +0,0 @@
/*
** 2023-09-27
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file houses the Experimental annotation for the sqlite3 C API.
*/
package org.sqlite.jni.annotation;
import java.lang.annotation.*;
/**
This annotation is for flagging methods, constructors, and types
which are expressly experimental and subject to any amount of
change or outright removal. Client code should not rely on such
features.
*/
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target({
ElementType.METHOD,
ElementType.CONSTRUCTOR,
ElementType.TYPE
})
public @interface Experimental{}
@@ -1,71 +0,0 @@
/*
** 2023-09-27
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file houses the NotNull annotation for the sqlite3 C API.
*/
package org.sqlite.jni.annotation;
import java.lang.annotation.*;
/**
This annotation is for flagging parameters which may not legally be
null or point to closed/finalized C-side resources.
<p>In the case of Java types which map directly to C struct types
(e.g. {@link org.sqlite.jni.capi.sqlite3}, {@link
org.sqlite.jni.capi.sqlite3_stmt}, and {@link
org.sqlite.jni.capi.sqlite3_context}), a closed/finalized resource
is also considered to be null for purposes this annotation because
the C-side effect of passing such a handle is the same as if null
is passed.</p>
<p>When used in the context of Java interfaces which are called
from the C APIs, this annotation communicates that the C API will
never pass a null value to the callback for that parameter.</p>
<p>Passing a null, for this annotation's definition of null, for
any parameter marked with this annoation specifically invokes
undefined behavior (see below).</p>
<p>Passing 0 (i.e. C NULL) or a negative value for any long-type
parameter marked with this annoation specifically invokes undefined
behavior (see below). Such values are treated as C pointers in the
JNI layer.</p>
<p><b>Undefined behaviour:</b> the JNI build uses the {@code
SQLITE_ENABLE_API_ARMOR} build flag, meaning that the C code
invoked with invalid NULL pointers and the like will not invoke
undefined behavior in the conventional C sense, but may, for
example, return result codes which are not documented for the
affected APIs or may otherwise behave unpredictably. In no known
cases will such arguments result in C-level code dereferencing a
NULL pointer or accessing out-of-bounds (or otherwise invalid)
memory. In other words, they may cause unexpected behavior but
should never cause an outright crash or security issue.</p>
<p>Note that the C-style API does not throw any exceptions on its
own because it has a no-throw policy in order to retain its C-style
semantics, but it may trigger NullPointerExceptions (or similar) if
passed a null for a parameter flagged with this annotation.</p>
<p>This annotation is informational only. No policy is in place to
programmatically ensure that NotNull is conformed to in client
code.</p>
<p>This annotation is solely for the use by the classes in the
org.sqlite.jni package and subpackages, but is made public so that
javadoc will link to it from the annotated functions. It is not
part of the public API and client-level code must not rely on
it.</p>
*/
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.PARAMETER)
public @interface NotNull{}
@@ -1,33 +0,0 @@
/*
** 2023-09-27
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file houses the Nullable annotation for the sqlite3 C API.
*/
package org.sqlite.jni.annotation;
import java.lang.annotation.*;
/**
This annotation is for flagging parameters which may legally be
null, noting that they may behave differently if passed null but
are prepared to expect null as a value. When used in the context of
callback methods which are called into from the C APIs, this
annotation communicates that the C API may pass a null value to the
callback.
<p>This annotation is solely for the use by the classes in this
package but is made public so that javadoc will link to it from the
annotated functions. It is not part of the public API and
client-level code must not rely on it.
*/
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.PARAMETER)
public @interface Nullable{}
@@ -1,17 +0,0 @@
/*
** 2023-09-27
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
*/
/**
This package houses annotations specific to the JNI bindings of the
SQLite3 C API.
*/
package org.sqlite.jni.annotation;
@@ -1,34 +0,0 @@
/*
** 2023-08-25
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.capi;
import org.sqlite.jni.annotation.NotNull;
/**
An implementation of {@link CollationCallback} which provides a
no-op xDestroy() method.
*/
public abstract class AbstractCollationCallback
implements CollationCallback, XDestroyCallback {
/**
Must compare the given byte arrays and return the result using
{@code memcmp()} semantics.
*/
public abstract int call(@NotNull byte[] lhs, @NotNull byte[] rhs);
/**
Optionally override to be notified when the UDF is finalized by
SQLite. This implementation does nothing.
*/
public void xDestroy(){}
}
@@ -1,138 +0,0 @@
/*
** 2023-08-25
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.capi;
/**
A SQLFunction implementation for aggregate functions. Its T is the
data type of its "accumulator" state, an instance of which is
intended to be be managed using the getAggregateState() and
takeAggregateState() methods.
*/
public abstract class AggregateFunction<T> implements SQLFunction {
/**
As for the xStep() argument of the C API's
sqlite3_create_function(). If this function throws, the
exception is not propagated and a warning might be emitted to a
debugging channel.
*/
public abstract void xStep(sqlite3_context cx, sqlite3_value[] args);
/**
As for the xFinal() argument of the C API's sqlite3_create_function().
If this function throws, it is translated into an sqlite3_result_error().
*/
public abstract void xFinal(sqlite3_context cx);
/**
Optionally override to be notified when the UDF is finalized by
SQLite.
*/
public void xDestroy() {}
/**
PerContextState assists aggregate and window functions in
managing their accumulator state across calls to the UDF's
callbacks.
<p>T must be of a type which can be legally stored as a value in
java.util.HashMap<KeyType,T>.
<p>If a given aggregate or window function is called multiple times
in a single SQL statement, e.g. SELECT MYFUNC(A), MYFUNC(B)...,
then the clients need some way of knowing which call is which so
that they can map their state between their various UDF callbacks
and reset it via xFinal(). This class takes care of such
mappings.
<p>This class works by mapping
sqlite3_context.getAggregateContext() to a single piece of
state, of a client-defined type (the T part of this class), which
persists across a "matching set" of the UDF's callbacks.
<p>This class is a helper providing commonly-needed functionality
- it is not required for use with aggregate or window functions.
Client UDFs are free to perform such mappings using custom
approaches. The provided {@link AggregateFunction} and {@link
WindowFunction} classes use this.
*/
public static final class PerContextState<T> {
private final java.util.Map<Long,ValueHolder<T>> map
= new java.util.HashMap<>();
/**
Should be called from a UDF's xStep(), xValue(), and xInverse()
methods, passing it that method's first argument and an initial
value for the persistent state. If there is currently no
mapping for the given context within the map, one is created
using the given initial value, else the existing one is used
and the 2nd argument is ignored. It returns a ValueHolder<T>
which can be used to modify that state directly without
requiring that the client update the underlying map's entry.
<p>The caller is obligated to eventually call
takeAggregateState() to clear the mapping.
*/
public ValueHolder<T> getAggregateState(sqlite3_context cx, T initialValue){
final Long key = cx.getAggregateContext(true);
ValueHolder<T> rc = null==key ? null : map.get(key);
if( null==rc ){
map.put(key, rc = new ValueHolder<>(initialValue));
}
return rc;
}
/**
Should be called from a UDF's xFinal() method and passed that
method's first argument. This function removes the value
associated with cx.getAggregateContext() from the map and
returns it, returning null if no other UDF method has been
called to set up such a mapping. The latter condition will be
the case if a UDF is used in a statement which has no result
rows.
*/
public T takeAggregateState(sqlite3_context cx){
final ValueHolder<T> h = map.remove(cx.getAggregateContext(false));
return null==h ? null : h.value;
}
}
/** Per-invocation state for the UDF. */
private final PerContextState<T> map = new PerContextState<>();
/**
To be called from the implementation's xStep() method, as well
as the xValue() and xInverse() methods of the {@link WindowFunction}
subclass, to fetch the current per-call UDF state. On the
first call to this method for any given sqlite3_context
argument, the context is set to the given initial value. On all other
calls, the 2nd argument is ignored.
@see SQLFunction.PerContextState#getAggregateState
*/
protected final ValueHolder<T> getAggregateState(sqlite3_context cx, T initialValue){
return map.getAggregateState(cx, initialValue);
}
/**
To be called from the implementation's xFinal() method to fetch
the final state of the UDF and remove its mapping.
see SQLFunction.PerContextState#takeAggregateState
*/
protected final T takeAggregateState(sqlite3_context cx){
return map.takeAggregateState(cx);
}
}
@@ -1,29 +0,0 @@
/*
** 2023-08-25
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.capi;
import org.sqlite.jni.annotation.*;
/**
Callback for use with {@link CApi#sqlite3_set_authorizer}.
*/
public interface AuthorizerCallback extends CallbackProxy {
/**
Must function as described for the C-level
sqlite3_set_authorizer() callback. If it throws, the error is
converted to a db-level error and the exception is suppressed.
*/
int call(int opId, @Nullable String s1, @Nullable String s2,
@Nullable String s3, @Nullable String s4);
}
@@ -1,40 +0,0 @@
/*
** 2023-08-25
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.capi;
/**
Callback for use with the {@link CApi#sqlite3_auto_extension}
family of APIs.
*/
public interface AutoExtensionCallback extends CallbackProxy {
/**
Must function as described for a C-level
sqlite3_auto_extension() callback.
<p>This callback may throw and the exception's error message will
be set as the db's error string.
<p>Tips for implementations:
<p>- Opening a database from an auto-extension handler will lead to
an endless recursion of the auto-handler triggering itself
indirectly for each newly-opened database.
<p>- If this routine is stateful, it may be useful to make the
overridden method synchronized.
<p>- Results are undefined if the given db is closed by an auto-extension.
*/
int call(sqlite3 db);
}
@@ -1,26 +0,0 @@
/*
** 2023-08-25
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.capi;
/**
Callback for use with {@link CApi#sqlite3_busy_handler}.
*/
public interface BusyHandlerCallback extends CallbackProxy {
/**
Must function as documented for the C-level
sqlite3_busy_handler() callback argument, minus the (void*)
argument the C-level function requires.
*/
int call(int n);
}
File diff suppressed because it is too large Load Diff
@@ -1,45 +0,0 @@
/*
** 2023-08-25
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.capi;
/**
This marker interface exists soley for use as a documentation and
class-grouping tool. It should be applied to interfaces or
classes which have a call() method implementing some specific
callback interface on behalf of the C library.
<p>Unless very explicitely documented otherwise, callbacks must
never throw. Any which do throw but should not might trigger debug
output regarding the error, but the exception will not be
propagated. For callback interfaces which support returning error
info to the core, the JNI binding will convert any exceptions to
C-level error information. For callback interfaces which do not
support returning error information, all exceptions will
necessarily be suppressed in order to retain the C-style no-throw
semantics and avoid invoking undefined behavior in the C layer.
<p>Callbacks of this style follow a common naming convention:
<p>1) They use the UpperCamelCase form of the C function they're
proxying for, minus the {@code sqlite3_} prefix, plus a {@code
Callback} suffix. e.g. {@code sqlite3_busy_handler()}'s callback is
named {@code BusyHandlerCallback}. Exceptions are made where that
would potentially be ambiguous, e.g. {@link ConfigSqllogCallback}
instead of {@code ConfigCallback} because the {@code
sqlite3_config()} interface may need to support more callback types
in the future.
<p>2) They all have a {@code call()} method but its signature is
callback-specific.
*/
public interface CallbackProxy {}
@@ -1,35 +0,0 @@
/*
** 2023-08-25
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.capi;
import org.sqlite.jni.annotation.NotNull;
/**
Callback for use with {@link CApi#sqlite3_create_collation}.
@see AbstractCollationCallback
*/
public interface CollationCallback
extends CallbackProxy, XDestroyCallback {
/**
Must compare the given byte arrays and return the result using
{@code memcmp()} semantics.
*/
int call(@NotNull byte[] lhs, @NotNull byte[] rhs);
/**
Called by SQLite when the collation is destroyed. If a collation
requires custom cleanup, override this method.
*/
void xDestroy();
}
@@ -1,29 +0,0 @@
/*
** 2023-08-25
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.capi;
/**
Callback for use with {@link CApi#sqlite3_collation_needed}.
*/
public interface CollationNeededCallback extends CallbackProxy {
/**
Has the same semantics as the C-level sqlite3_create_collation()
callback.
<p>Because the C API has no mechanism for reporting errors
from this callbacks, any exceptions thrown by this callback
are suppressed.
*/
void call(sqlite3 db, int eTextRep, String collationName);
}
@@ -1,26 +0,0 @@
/*
** 2023-08-25
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.capi;
/**
Callback for use with {@link CApi#sqlite3_commit_hook}.
*/
public interface CommitHookCallback extends CallbackProxy {
/**
Works as documented for the C-level sqlite3_commit_hook()
callback. If it throws, the exception is translated into
a db-level error.
*/
int call();
}
@@ -1,25 +0,0 @@
/*
** 2023-08-23
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.capi;
/**
A callback for use with sqlite3_config().
*/
public interface ConfigLogCallback {
/**
Must function as described for a C-level callback for
{@link CApi#sqlite3_config(ConfigLogCallback)}, with the slight signature change.
*/
void call(int errCode, String msg);
}
@@ -1,25 +0,0 @@
/*
** 2023-08-23
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.capi;
/**
A callback for use with sqlite3_config().
*/
public interface ConfigSqlLogCallback {
/**
Must function as described for a C-level callback for
{@link CApi#sqlite3_config(ConfigSqlLogCallback)}, with the slight signature change.
*/
void call(sqlite3 db, String msg, int msgType );
}
@@ -1,46 +0,0 @@
/*
** 2023-07-21
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.capi;
/**
A helper for passing pointers between JNI C code and Java, in
particular for output pointers of high-level object types in the
sqlite3 C API, e.g. (sqlite3**) and (sqlite3_stmt**). This is
intended to be subclassed and the ContextType is intended to be the
class which is doing the subclassing. The intent of the ContextType
is strictly to provide some level of type safety by avoiding that
NativePointerHolder is not inadvertently passed to an incompatible
function signature.
These objects do not own the pointer they refer to. They are
intended simply to communicate that pointer between C and Java.
*/
public class NativePointerHolder<ContextType> {
//! Only set from JNI, where access permissions don't matter.
private volatile long nativePointer = 0;
/**
For use ONLY by package-level APIs which act as proxies for
close/finalize operations. Such ops must call this to zero out
the pointer so that this object is not carrying a stale
pointer. This function returns the prior value of the pointer and
sets it to 0.
*/
final long clearNativePointer() {
final long rv = nativePointer;
nativePointer= 0;
return rv;
}
public final long getNativePointer(){ return nativePointer; }
}
@@ -1,253 +0,0 @@
/*
** 2023-07-21
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.capi;
/**
Helper classes for handling JNI output pointers.
<p>We do not use a generic OutputPointer<T> because working with those
from the native JNI code is unduly quirky due to a lack of
autoboxing at that level.
<p>The usage is similar for all of thes types:
<pre>{@code
OutputPointer.sqlite3 out = new OutputPointer.sqlite3();
assert( null==out.get() );
int rc = sqlite3_open(":memory:", out);
if( 0!=rc ) ... error;
assert( null!=out.get() );
sqlite3 db = out.take();
assert( null==out.get() );
}</pre>
<p>With the minor exception that the primitive types permit direct
access to the object's value via the `value` property, whereas the
JNI-level opaque types do not permit client-level code to set that
property.
<p>Warning: do not share instances of these classes across
threads. Doing so may lead to corrupting sqlite3-internal state.
*/
public final class OutputPointer {
/**
Output pointer for use with routines, such as sqlite3_open(),
which return a database handle via an output pointer. These
pointers can only be set by the JNI layer, not by client-level
code.
*/
public static final class sqlite3 {
private org.sqlite.jni.capi.sqlite3 value;
/** Initializes with a null value. */
public sqlite3(){value = null;}
/** Sets the current value to null. */
public void clear(){value = null;}
/** Returns the current value. */
public final org.sqlite.jni.capi.sqlite3 get(){return value;}
/** Equivalent to calling get() then clear(). */
public final org.sqlite.jni.capi.sqlite3 take(){
final org.sqlite.jni.capi.sqlite3 v = value;
value = null;
return v;
}
}
/**
Output pointer for sqlite3_blob_open(). These
pointers can only be set by the JNI layer, not by client-level
code.
*/
public static final class sqlite3_blob {
private org.sqlite.jni.capi.sqlite3_blob value;
/** Initializes with a null value. */
public sqlite3_blob(){value = null;}
/** Sets the current value to null. */
public void clear(){value = null;}
/** Returns the current value. */
public final org.sqlite.jni.capi.sqlite3_blob get(){return value;}
/** Equivalent to calling get() then clear(). */
public final org.sqlite.jni.capi.sqlite3_blob take(){
final org.sqlite.jni.capi.sqlite3_blob v = value;
value = null;
return v;
}
}
/**
Output pointer for use with routines, such as sqlite3_prepare(),
which return a statement handle via an output pointer. These
pointers can only be set by the JNI layer, not by client-level
code.
*/
public static final class sqlite3_stmt {
private org.sqlite.jni.capi.sqlite3_stmt value;
/** Initializes with a null value. */
public sqlite3_stmt(){value = null;}
/** Sets the current value to null. */
public void clear(){value = null;}
/** Returns the current value. */
public final org.sqlite.jni.capi.sqlite3_stmt get(){return value;}
/** Equivalent to calling get() then clear(). */
public final org.sqlite.jni.capi.sqlite3_stmt take(){
final org.sqlite.jni.capi.sqlite3_stmt v = value;
value = null;
return v;
}
}
/**
Output pointer for use with routines, such as sqlite3_prepupdate_new(),
which return a sqlite3_value handle via an output pointer. These
pointers can only be set by the JNI layer, not by client-level
code.
*/
public static final class sqlite3_value {
private org.sqlite.jni.capi.sqlite3_value value;
/** Initializes with a null value. */
public sqlite3_value(){value = null;}
/** Sets the current value to null. */
public void clear(){value = null;}
/** Returns the current value. */
public final org.sqlite.jni.capi.sqlite3_value get(){return value;}
/** Equivalent to calling get() then clear(). */
public final org.sqlite.jni.capi.sqlite3_value take(){
final org.sqlite.jni.capi.sqlite3_value v = value;
value = null;
return v;
}
}
/**
Output pointer for use with native routines which return booleans
via integer output pointers.
*/
public static final class Bool {
/**
This is public for ease of use. Accessors are provided for
consistency with the higher-level types.
*/
public boolean value;
/** Initializes with the value 0. */
public Bool(){this(false);}
/** Initializes with the value v. */
public Bool(boolean v){value = v;}
/** Returns the current value. */
public final boolean get(){return value;}
/** Sets the current value to v. */
public final void set(boolean v){value = v;}
}
/**
Output pointer for use with native routines which return integers via
output pointers.
*/
public static final class Int32 {
/**
This is public for ease of use. Accessors are provided for
consistency with the higher-level types.
*/
public int value;
/** Initializes with the value 0. */
public Int32(){this(0);}
/** Initializes with the value v. */
public Int32(int v){value = v;}
/** Returns the current value. */
public final int get(){return value;}
/** Sets the current value to v. */
public final void set(int v){value = v;}
}
/**
Output pointer for use with native routines which return 64-bit integers
via output pointers.
*/
public static final class Int64 {
/**
This is public for ease of use. Accessors are provided for
consistency with the higher-level types.
*/
public long value;
/** Initializes with the value 0. */
public Int64(){this(0);}
/** Initializes with the value v. */
public Int64(long v){value = v;}
/** Returns the current value. */
public final long get(){return value;}
/** Sets the current value. */
public final void set(long v){value = v;}
}
/**
Output pointer for use with native routines which return strings via
output pointers.
*/
public static final class String {
/**
This is public for ease of use. Accessors are provided for
consistency with the higher-level types.
*/
public java.lang.String value;
/** Initializes with a null value. */
public String(){this(null);}
/** Initializes with the value v. */
public String(java.lang.String v){value = v;}
/** Returns the current value. */
public final java.lang.String get(){return value;}
/** Sets the current value. */
public final void set(java.lang.String v){value = v;}
}
/**
Output pointer for use with native routines which return byte
arrays via output pointers.
*/
public static final class ByteArray {
/**
This is public for ease of use. Accessors are provided for
consistency with the higher-level types.
*/
public byte[] value;
/** Initializes with the value null. */
public ByteArray(){this(null);}
/** Initializes with the value v. */
public ByteArray(byte[] v){value = v;}
/** Returns the current value. */
public final byte[] get(){return value;}
/** Sets the current value. */
public final void set(byte[] v){value = v;}
}
/**
Output pointer for use with native routines which return
blobs via java.nio.ByteBuffer.
See {@link org.sqlite.jni.capi.CApi#sqlite3_jni_supports_nio}
*/
public static final class ByteBuffer {
/**
This is public for ease of use. Accessors are provided for
consistency with the higher-level types.
*/
public java.nio.ByteBuffer value;
/** Initializes with the value null. */
public ByteBuffer(){this(null);}
/** Initializes with the value v. */
public ByteBuffer(java.nio.ByteBuffer v){value = v;}
/** Returns the current value. */
public final java.nio.ByteBuffer get(){return value;}
/** Sets the current value. */
public final void set(java.nio.ByteBuffer v){value = v;}
}
}
@@ -1,81 +0,0 @@
/*
** 2023-09-13
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.capi;
/**
Callback for use with {@link CApi#sqlite3_prepare_multi}.
*/
public interface PrepareMultiCallback extends CallbackProxy {
/**
Gets passed a sqlite3_stmt which it may handle in arbitrary ways,
transfering ownership of it to this function.
sqlite3_prepare_multi() will _not_ finalize st - it is up
to the call() implementation how st is handled.
Must return 0 on success or an SQLITE_... code on error. If it
throws, sqlite3_prepare_multi() will transform the exception into
a db-level error in order to retain the C-style error semantics
of the API.
See the {@link Finalize} class for a wrapper which finalizes the
statement after calling a proxy PrepareMultiCallback.
*/
int call(sqlite3_stmt st);
/**
A PrepareMultiCallback impl which wraps a separate impl and finalizes
any sqlite3_stmt passed to its callback.
*/
public static final class Finalize implements PrepareMultiCallback {
private final PrepareMultiCallback p;
/**
p is the proxy to call() when this.call() is called.
*/
public Finalize( PrepareMultiCallback p ){
this.p = p;
}
/**
Calls the call() method of the proxied callback and either returns its
result or propagates an exception. Either way, it passes its argument to
sqlite3_finalize() before returning.
*/
@Override public int call(sqlite3_stmt st){
try {
return this.p.call(st);
}finally{
CApi.sqlite3_finalize(st);
}
}
}
/**
A PrepareMultiCallback impl which steps entirely through a result set,
ignoring all non-error results.
*/
public static final class StepAll implements PrepareMultiCallback {
public StepAll(){}
/**
Calls sqlite3_step() on st until it returns something other than
SQLITE_ROW. If the final result is SQLITE_DONE then 0 is returned,
else the result of the final step is returned.
*/
@Override public int call(sqlite3_stmt st){
int rc = CApi.SQLITE_DONE;
while( CApi.SQLITE_ROW == (rc = CApi.sqlite3_step(st)) ){}
return CApi.SQLITE_DONE==rc ? 0 : rc;
}
}
}
@@ -1,27 +0,0 @@
/*
** 2023-08-25
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.capi;
/**
Callback for use with {@link CApi#sqlite3_preupdate_hook}.
*/
public interface PreupdateHookCallback extends CallbackProxy {
/**
Must function as described for the C-level sqlite3_preupdate_hook()
callback. If it throws, the exception is translated to a
db-level error and the exception is suppressed.
*/
void call(sqlite3 db, int op, String dbName, String dbTable,
long iKey1, long iKey2 );
}
@@ -1,27 +0,0 @@
/*
** 2023-08-25
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.capi;
/**
Callback for use with {@link CApi#sqlite3_progress_handler}.
*/
public interface ProgressHandlerCallback extends CallbackProxy {
/**
Works as documented for the C-level sqlite3_progress_handler() callback.
<p>If it throws, the exception message is passed on to the db and
the exception is suppressed.
*/
int call();
}
@@ -1,155 +0,0 @@
/*
** 2023-07-21
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.capi;
/**
This enum contains all of the core and "extended" result codes used
by the sqlite3 library. It is provided not for use with the C-style
API (with which it won't work) but for higher-level code which may
find it useful to map SQLite result codes to human-readable names.
*/
public enum ResultCode {
SQLITE_OK(CApi.SQLITE_OK),
SQLITE_ERROR(CApi.SQLITE_ERROR),
SQLITE_INTERNAL(CApi.SQLITE_INTERNAL),
SQLITE_PERM(CApi.SQLITE_PERM),
SQLITE_ABORT(CApi.SQLITE_ABORT),
SQLITE_BUSY(CApi.SQLITE_BUSY),
SQLITE_LOCKED(CApi.SQLITE_LOCKED),
SQLITE_NOMEM(CApi.SQLITE_NOMEM),
SQLITE_READONLY(CApi.SQLITE_READONLY),
SQLITE_INTERRUPT(CApi.SQLITE_INTERRUPT),
SQLITE_IOERR(CApi.SQLITE_IOERR),
SQLITE_CORRUPT(CApi.SQLITE_CORRUPT),
SQLITE_NOTFOUND(CApi.SQLITE_NOTFOUND),
SQLITE_FULL(CApi.SQLITE_FULL),
SQLITE_CANTOPEN(CApi.SQLITE_CANTOPEN),
SQLITE_PROTOCOL(CApi.SQLITE_PROTOCOL),
SQLITE_EMPTY(CApi.SQLITE_EMPTY),
SQLITE_SCHEMA(CApi.SQLITE_SCHEMA),
SQLITE_TOOBIG(CApi.SQLITE_TOOBIG),
SQLITE_CONSTRAINT(CApi.SQLITE_CONSTRAINT),
SQLITE_MISMATCH(CApi.SQLITE_MISMATCH),
SQLITE_MISUSE(CApi.SQLITE_MISUSE),
SQLITE_NOLFS(CApi.SQLITE_NOLFS),
SQLITE_AUTH(CApi.SQLITE_AUTH),
SQLITE_FORMAT(CApi.SQLITE_FORMAT),
SQLITE_RANGE(CApi.SQLITE_RANGE),
SQLITE_NOTADB(CApi.SQLITE_NOTADB),
SQLITE_NOTICE(CApi.SQLITE_NOTICE),
SQLITE_WARNING(CApi.SQLITE_WARNING),
SQLITE_ROW(CApi.SQLITE_ROW),
SQLITE_DONE(CApi.SQLITE_DONE),
SQLITE_ERROR_MISSING_COLLSEQ(CApi.SQLITE_ERROR_MISSING_COLLSEQ),
SQLITE_ERROR_RETRY(CApi.SQLITE_ERROR_RETRY),
SQLITE_ERROR_SNAPSHOT(CApi.SQLITE_ERROR_SNAPSHOT),
SQLITE_IOERR_READ(CApi.SQLITE_IOERR_READ),
SQLITE_IOERR_SHORT_READ(CApi.SQLITE_IOERR_SHORT_READ),
SQLITE_IOERR_WRITE(CApi.SQLITE_IOERR_WRITE),
SQLITE_IOERR_FSYNC(CApi.SQLITE_IOERR_FSYNC),
SQLITE_IOERR_DIR_FSYNC(CApi.SQLITE_IOERR_DIR_FSYNC),
SQLITE_IOERR_TRUNCATE(CApi.SQLITE_IOERR_TRUNCATE),
SQLITE_IOERR_FSTAT(CApi.SQLITE_IOERR_FSTAT),
SQLITE_IOERR_UNLOCK(CApi.SQLITE_IOERR_UNLOCK),
SQLITE_IOERR_RDLOCK(CApi.SQLITE_IOERR_RDLOCK),
SQLITE_IOERR_DELETE(CApi.SQLITE_IOERR_DELETE),
SQLITE_IOERR_BLOCKED(CApi.SQLITE_IOERR_BLOCKED),
SQLITE_IOERR_NOMEM(CApi.SQLITE_IOERR_NOMEM),
SQLITE_IOERR_ACCESS(CApi.SQLITE_IOERR_ACCESS),
SQLITE_IOERR_CHECKRESERVEDLOCK(CApi.SQLITE_IOERR_CHECKRESERVEDLOCK),
SQLITE_IOERR_LOCK(CApi.SQLITE_IOERR_LOCK),
SQLITE_IOERR_CLOSE(CApi.SQLITE_IOERR_CLOSE),
SQLITE_IOERR_DIR_CLOSE(CApi.SQLITE_IOERR_DIR_CLOSE),
SQLITE_IOERR_SHMOPEN(CApi.SQLITE_IOERR_SHMOPEN),
SQLITE_IOERR_SHMSIZE(CApi.SQLITE_IOERR_SHMSIZE),
SQLITE_IOERR_SHMLOCK(CApi.SQLITE_IOERR_SHMLOCK),
SQLITE_IOERR_SHMMAP(CApi.SQLITE_IOERR_SHMMAP),
SQLITE_IOERR_SEEK(CApi.SQLITE_IOERR_SEEK),
SQLITE_IOERR_DELETE_NOENT(CApi.SQLITE_IOERR_DELETE_NOENT),
SQLITE_IOERR_MMAP(CApi.SQLITE_IOERR_MMAP),
SQLITE_IOERR_GETTEMPPATH(CApi.SQLITE_IOERR_GETTEMPPATH),
SQLITE_IOERR_CONVPATH(CApi.SQLITE_IOERR_CONVPATH),
SQLITE_IOERR_VNODE(CApi.SQLITE_IOERR_VNODE),
SQLITE_IOERR_AUTH(CApi.SQLITE_IOERR_AUTH),
SQLITE_IOERR_BEGIN_ATOMIC(CApi.SQLITE_IOERR_BEGIN_ATOMIC),
SQLITE_IOERR_COMMIT_ATOMIC(CApi.SQLITE_IOERR_COMMIT_ATOMIC),
SQLITE_IOERR_ROLLBACK_ATOMIC(CApi.SQLITE_IOERR_ROLLBACK_ATOMIC),
SQLITE_IOERR_DATA(CApi.SQLITE_IOERR_DATA),
SQLITE_IOERR_CORRUPTFS(CApi.SQLITE_IOERR_CORRUPTFS),
SQLITE_LOCKED_SHAREDCACHE(CApi.SQLITE_LOCKED_SHAREDCACHE),
SQLITE_LOCKED_VTAB(CApi.SQLITE_LOCKED_VTAB),
SQLITE_BUSY_RECOVERY(CApi.SQLITE_BUSY_RECOVERY),
SQLITE_BUSY_SNAPSHOT(CApi.SQLITE_BUSY_SNAPSHOT),
SQLITE_BUSY_TIMEOUT(CApi.SQLITE_BUSY_TIMEOUT),
SQLITE_CANTOPEN_NOTEMPDIR(CApi.SQLITE_CANTOPEN_NOTEMPDIR),
SQLITE_CANTOPEN_ISDIR(CApi.SQLITE_CANTOPEN_ISDIR),
SQLITE_CANTOPEN_FULLPATH(CApi.SQLITE_CANTOPEN_FULLPATH),
SQLITE_CANTOPEN_CONVPATH(CApi.SQLITE_CANTOPEN_CONVPATH),
SQLITE_CANTOPEN_SYMLINK(CApi.SQLITE_CANTOPEN_SYMLINK),
SQLITE_CORRUPT_VTAB(CApi.SQLITE_CORRUPT_VTAB),
SQLITE_CORRUPT_SEQUENCE(CApi.SQLITE_CORRUPT_SEQUENCE),
SQLITE_CORRUPT_INDEX(CApi.SQLITE_CORRUPT_INDEX),
SQLITE_READONLY_RECOVERY(CApi.SQLITE_READONLY_RECOVERY),
SQLITE_READONLY_CANTLOCK(CApi.SQLITE_READONLY_CANTLOCK),
SQLITE_READONLY_ROLLBACK(CApi.SQLITE_READONLY_ROLLBACK),
SQLITE_READONLY_DBMOVED(CApi.SQLITE_READONLY_DBMOVED),
SQLITE_READONLY_CANTINIT(CApi.SQLITE_READONLY_CANTINIT),
SQLITE_READONLY_DIRECTORY(CApi.SQLITE_READONLY_DIRECTORY),
SQLITE_ABORT_ROLLBACK(CApi.SQLITE_ABORT_ROLLBACK),
SQLITE_CONSTRAINT_CHECK(CApi.SQLITE_CONSTRAINT_CHECK),
SQLITE_CONSTRAINT_COMMITHOOK(CApi.SQLITE_CONSTRAINT_COMMITHOOK),
SQLITE_CONSTRAINT_FOREIGNKEY(CApi.SQLITE_CONSTRAINT_FOREIGNKEY),
SQLITE_CONSTRAINT_FUNCTION(CApi.SQLITE_CONSTRAINT_FUNCTION),
SQLITE_CONSTRAINT_NOTNULL(CApi.SQLITE_CONSTRAINT_NOTNULL),
SQLITE_CONSTRAINT_PRIMARYKEY(CApi.SQLITE_CONSTRAINT_PRIMARYKEY),
SQLITE_CONSTRAINT_TRIGGER(CApi.SQLITE_CONSTRAINT_TRIGGER),
SQLITE_CONSTRAINT_UNIQUE(CApi.SQLITE_CONSTRAINT_UNIQUE),
SQLITE_CONSTRAINT_VTAB(CApi.SQLITE_CONSTRAINT_VTAB),
SQLITE_CONSTRAINT_ROWID(CApi.SQLITE_CONSTRAINT_ROWID),
SQLITE_CONSTRAINT_PINNED(CApi.SQLITE_CONSTRAINT_PINNED),
SQLITE_CONSTRAINT_DATATYPE(CApi.SQLITE_CONSTRAINT_DATATYPE),
SQLITE_NOTICE_RECOVER_WAL(CApi.SQLITE_NOTICE_RECOVER_WAL),
SQLITE_NOTICE_RECOVER_ROLLBACK(CApi.SQLITE_NOTICE_RECOVER_ROLLBACK),
SQLITE_WARNING_AUTOINDEX(CApi.SQLITE_WARNING_AUTOINDEX),
SQLITE_AUTH_USER(CApi.SQLITE_AUTH_USER),
SQLITE_OK_LOAD_PERMANENTLY(CApi.SQLITE_OK_LOAD_PERMANENTLY);
public final int value;
ResultCode(int rc){
value = rc;
ResultCodeMap.set(rc, this);
}
/**
Returns the entry from this enum for the given result code, or
null if no match is found.
*/
public static ResultCode getEntryForInt(int rc){
return ResultCodeMap.get(rc);
}
/**
Internal level of indirection required because we cannot initialize
static enum members in an enum before the enum constructor is
invoked.
*/
private static final class ResultCodeMap {
private static final java.util.Map<Integer,ResultCode> i2e
= new java.util.HashMap<>();
private static void set(int rc, ResultCode e){ i2e.put(rc, e); }
private static ResultCode get(int rc){ return i2e.get(rc); }
}
}
@@ -1,26 +0,0 @@
/*
** 2023-08-25
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.capi;
/**
Callback for use with {@link CApi#sqlite3_rollback_hook}.
*/
public interface RollbackHookCallback extends CallbackProxy {
/**
Must function as documented for the C-level sqlite3_rollback_hook()
callback. If it throws, the exception is translated into
a db-level error.
*/
void call();
}
@@ -1,36 +0,0 @@
/*
** 2023-07-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.
**
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.capi;
/**
SQLFunction is used in conjunction with the
sqlite3_create_function() JNI-bound API to give that native code
access to the callback functions needed in order to implement SQL
functions in Java.
<p>
This class is not used by itself, but is a marker base class. The
three UDF types are modelled by the inner classes Scalar,
Aggregate<T>, and Window<T>. Most simply, clients may subclass
those, or create anonymous classes from them, to implement
UDFs. Clients are free to create their own classes for use with
UDFs, so long as they conform to the public interfaces defined by
those three classes. The JNI layer only actively relies on the
SQLFunction base class and the method names and signatures used by
the UDF callback interfaces.
*/
public interface SQLFunction {
}
File diff suppressed because it is too large Load Diff
@@ -1,33 +0,0 @@
/*
** 2023-08-25
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.capi;
/**
A SQLFunction implementation for scalar functions.
*/
public abstract class ScalarFunction implements SQLFunction {
/**
As for the xFunc() argument of the C API's
sqlite3_create_function(). If this function throws, it is
translated into an sqlite3_result_error().
*/
public abstract void xFunc(sqlite3_context cx, sqlite3_value[] args);
/**
Optionally override to be notified when the UDF is finalized by
SQLite. This default implementation does nothing.
*/
public void xDestroy() {}
}

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