Compare commits

..

116 Commits

Author SHA1 Message Date
drh 07e071e500 If SQL is entered as additional command-line arguments to CLI, that counts
the same as getting input from a pipe or file for the purpose of determining
the default format mode.

FossilOrigin-Name: 3d55ec15a9e4dc8af4bf1e2884eaa2c809995fb1529633f73287dc7a54153629
2025-11-17 19:46:04 +00:00
drh 53794ab541 The -textjsonb option is on by default in the standard non-batch mode of
the CLI.  New fast screening test for JSONB.

FossilOrigin-Name: fd09f934d64ec07fd56f1b80ab05dafb28b605d5802ff5758eea17d8ad24e3cc
2025-11-17 18:51:55 +00:00
drh 234d2e51ac CLI comes up in legacy "list" mode if neither stdin or stdout are a tty, or
if "--compat 20251114" or earlier is specified, or if "--batch" is used.
New modes "batch" and "tty" select either legacy "list" mode or the newer
"qbox" mode with limits.
Make CVS output compatible with legacy.  Break out ".import" into a separate
subroutine in anticipation of forthcoming improvements.  General code
cleanup.

FossilOrigin-Name: f6bfcea9a01493af182e9aa0d35df6f81bf9e36220df79139afa287fa43d9aa3
2025-11-17 16:44:13 +00:00
drh 02268693c3 Fix test cases impacted by the use of ~/.sqliterc
FossilOrigin-Name: 8fc05faef91186429c6c710991fd736b1df9a9af946c29d207db2518d6436b38
2025-11-17 00:15:14 +00:00
drh 4d91527aa5 Fix a bug in ".show". Adjust test cases for the new default mode.
FossilOrigin-Name: 2aebd7bfecaaf1f75b52b05a0d3009fc0dc61289ae666d24cb4e3ddfaf251645
2025-11-16 23:06:45 +00:00
drh 1db7790ecb Default .mode is now qbox with limits. New --compat YYYYMMDD command-line
option, and new -DCOMPATIBILITY_DATE=YYYYMMDD compile-time options can
override.  Command ".mode YYYYMMDD" sets the mode to the default for the
date given.

FossilOrigin-Name: 52e022375961ad9460412e40e100219ee59055eb0ab309cbfc2ab19f61929a45
2025-11-16 17:27:21 +00:00
drh e20406040a Fix a long-standing bug in .schema when the argument contains a ".".
FossilOrigin-Name: 404576214fc20161cdcb031d57696d6068506309c8faecd3e4424e5ced28046b
2025-11-16 10:49:51 +00:00
drh 4dc404cef4 Fix a problem with the new command-line parsing in the CLI.
FossilOrigin-Name: 3e02804c387c89afde59eef8e464c6011e083e158873e4f8ab1f17bcc12b7c28
2025-11-16 02:32:06 +00:00
drh f4870a2b51 Improved --safe mode defenses. Fix a test case error.
FossilOrigin-Name: eacc9db80de8baa878ebdc3a054538c8998db9a2e46720c0d8b109d62a0cac29
2025-11-16 01:34:01 +00:00
drh 36a54c001e Improved error detection in the argument to .mode --widths.
FossilOrigin-Name: 4d774ee495e38282b8701988fe514344ee0e81285692c8d1adc1ee7e22960ad9
2025-11-16 01:03:53 +00:00
drh 4e12c0b0e9 Fix formatting problem in .schema and .fullschema
FossilOrigin-Name: 4035fb15ff36d139ed4c839fdebb0278e6cf9a2f00298e1a548a8f8317ba14b2
2025-11-16 00:56:24 +00:00
drh 1d06c9f700 Help text improvements in the CLI.
FossilOrigin-Name: dcaabd97d5064dd70fb386a2eef55f4301bba4b54915ed6a91d736abe7a04ae8
2025-11-15 20:10:32 +00:00
drh 3b2754446a Bug fix to the --lineline option in the CLI. Other minor CLI tweaks.
FossilOrigin-Name: 40f81a9f9f6ea29144174fbd487f83d150a1be8f35e7496bfc1cfc6a556b6312
2025-11-15 15:30:55 +00:00
drh dd3b30ec85 Columnar modes respond to nScreenWidth.
FossilOrigin-Name: 60d26ebb4d91cc885b6b938ef2fc2864ebbf5a18d2e456521ee2f4aa3b22eddf
2025-11-15 15:18:41 +00:00
drh 18c7b4d651 Columnar formats respond to --screenwidth restrictions by removing
cell padding.

FossilOrigin-Name: cbe233ca131118692fd4a84d8fcf0dfca926fa935f66cfb718c235d54084de3d
2025-11-15 12:09:13 +00:00
drh 3338ca5f56 Data structure improvements on columnar layout. Prep work for getting
columnar layouts to respond to nScreenWidth.

FossilOrigin-Name: 777eeb2ed2708faf42559387bd582b9345a794798a0327e4fcd75e37948eac60
2025-11-15 11:28:23 +00:00
drh 7a0d5bdd29 Improve columnar layout in QRF so that it correctly deals with control
characters, and especially tabs.

FossilOrigin-Name: 0650e2b83170b44c1ba944259a96d41e1a14a57004d4f1f80dc5640ae837a81e
2025-11-15 00:23:09 +00:00
drh 850da77de1 Make use of the new sqlite3_str_free() interface in the CLI.
FossilOrigin-Name: 2e07bc29ab1ca66049337f2cfbefcd57bdcd691a381b309fb8a5db6e72e56d03
2025-11-14 21:49:27 +00:00
drh c2b9896e50 Enforce the --charlimit constraint in QRF.
FossilOrigin-Name: c25f8fdedd8d68e3551a445e24e1c60e105e18f9cf8f1badcbb77a58974f3381
2025-11-14 21:40:20 +00:00
drh 9a3561e2d9 Merge the latest trunk enhancements into the qrf branch.
FossilOrigin-Name: 9ee892b4f97c91208a658f09add00a567406e913edb022a2fd53333ea01c2b26
2025-11-14 20:11:15 +00:00
drh c5d6fbdeea Fix harmless display bug in previous check-in. Add the -noinit command-line
option to bypass reading the ~/.sqliterc file.

FossilOrigin-Name: 229650a01dcd9cc4b39526d661106e9c3eb2f90ca7fbed8adc6e6fe85ef3fdaa
2025-11-14 19:04:23 +00:00
drh 8254c1a4fa Add the --tag and --list options to the ".mode" command.
FossilOrigin-Name: 9daf98c2f449688b7463b71dfa926cb4ae96d8c7ee34946df2172bb37f7c5616
2025-11-14 18:41:21 +00:00
drh 02751a7162 Add the --once option to the .mode command. Improvements to help text.
FossilOrigin-Name: 253980122a35f787423aaeedbec12ec94b31768f245fe1c1fcc7e08911855c60
2025-11-14 17:11:06 +00:00
drh a7b9a10707 Fix harmless compiler warnings. Incorporate "USAGE:" comments on dot-command
implementations into the CLI ".help" command output.

FossilOrigin-Name: 046bfab4a01e8a7cc58d1bdf0756c90ba354562d79e5453c08202daf648e76a6
2025-11-14 14:59:43 +00:00
drh d5ee8aa625 Fix various bugs and compiler warnings. All tests now passing on linux, mac,
and windows.  More testing needed, though.

FossilOrigin-Name: 2220cb70c2f1ee30dcdf917a20feacdfcb3789433d0645fea626fd4c5cf0d099
2025-11-14 13:07:45 +00:00
drh e85b670a55 Merge compiler-warning fix from trunk into the qrf branch.
FossilOrigin-Name: 6ffab43ca32230975e79d91080dfa2e80a4c21deef31ab86455581af18a399cd
2025-11-14 11:06:27 +00:00
drh ce4a86e08f Fix a memory error in the .schema command.
FossilOrigin-Name: 3300ed34b5a3598c46cdc4bdf1e9e81818a5029585ae917424f64c11c718bfa8
2025-11-14 10:42:29 +00:00
drh 0412f3297a Suppress a harmless compiler warning.
FossilOrigin-Name: 89cf1de7b6abff567c70d52a415eea6deb52fc13c3008c1266a77b61e3711217
2025-11-14 03:26:48 +00:00
drh c4f198bfc4 Merge all the latest trunk enhancements into the qrf branch.
FossilOrigin-Name: 97b06e58ed128a1d60b89437ddf0ad80df528b8dbfdb6a7637374fc1ab4ddb10
2025-11-14 02:27:58 +00:00
drh c37fcdd5d1 Improvements to --titles handling in the .mode command. Fix shell1 test
cases having to do with quoting of NULL values in "tcl" mode.

FossilOrigin-Name: 0fef770bd654b8ba8011b0bb610128058e75e8444f86c51e7ea59a6f2625a3fc
2025-11-14 02:22:39 +00:00
drh bf891f707b Get --screenwidth auto working on linux and windows.
FossilOrigin-Name: 48a91f2067005f7f186484354be07384dd76bcfff8427c17579f6e32201e3742
2025-11-13 11:52:34 +00:00
drh 2a376849ca Implement the --titles option to the .mode command.
FossilOrigin-Name: cc25643ebc516db0799406797e9961a2af574875ae9639178b722dbc280c96ad
2025-11-12 20:50:08 +00:00
drh b9a471a745 More details on the format of the QRF_STYLE_Json and QRF_STYLE_JOBject.
FossilOrigin-Name: 2b22321df9c2f51e4b0ab9e4da859c58bb3f194ad69c22b9d32b35831740f9dd
2025-11-12 19:03:05 +00:00
drh a9136b45fa Continuing code cleanup. Various minor bug fixes. All legacy tests now
pass.  All planned features for ".mode" are implemented, at least at the
interface level, though some features are still no-ops.

FossilOrigin-Name: e5a81711d0076b447e5bd3206bc04d755a6229b9f4926f42260fcd01ecf3e5a2
2025-11-12 18:54:09 +00:00
drh ed72bb3fa3 Add a new direct CLI testing script in test/modeA.clitest to demonstration
that can be done.  Rig this this to be run by test/shellB.test during normal
testing.

FossilOrigin-Name: 3107ebb9e1602fe5bed644c6f69426bebf307772e581b17a8fa3e50bd522e566
2025-11-12 13:46:11 +00:00
drh 3b847a4040 Rename QRF_STYLE_JsonLine to QRF_STYLE_JObject. Add new convenience modes
to the CLI and generally refactor and improve the ".mode" command.  Some
tests are failing, mostly due to changes in error message output and similar.

FossilOrigin-Name: 4c8109682e3a224fd8e015e73a9c1c7b6d56ad747d7ca7bc387bb92e1c98f731
2025-11-12 12:44:02 +00:00
drh b43e8920d8 Minor refactoring of some field names in the sqlite3_qrf_spec object, to try
to make the names more intuitive, memorable, and succinct.

FossilOrigin-Name: 3450bc7eecb717abedbaeb56dc824e14eb35ed30322fe3dd3b4b1cbd5fd14c60
2025-11-11 18:07:42 +00:00
drh 7f947dbd6c Move the ".output" command into a separate subroutine. Enhance .output so
that it is able to capture output in a sqlite3_str object, then compare that
captured output we patterns to accomplish tests.

FossilOrigin-Name: ddd167044753f5215624fc9ee0e3657836c528f23a6a3e262401fd1b64cdbb21
2025-11-11 14:08:50 +00:00
drh 3a5f3aa858 Factor the code for ".mode" out of do_meta_command() and into its own
subroutine, to show how the new infrastructure makes this easy.  We need
to do the same for all the dot-commands.  At the same time, add the
-textjsonb option to .mode.

FossilOrigin-Name: b48aa054df488747a7db56faf1cd0da42e322edff60650b9187448e58eb12def
2025-11-11 00:44:25 +00:00
drh babac214d6 Bug fixes. All tests are now passing.
FossilOrigin-Name: a0fc99a3bdd12f9ac69511c1aea2bd9a3d5de593d44b4a732cfc8f48e0931c76
2025-11-11 00:21:26 +00:00
drh 71b65c5055 Revamp the internal data structures that the CLI uses for tracking and
managing the display mode.  This is an incremental check-in.  The code
compiles and runs, but not all tests are passing.

FossilOrigin-Name: 8cc581e53c2ab15bd311e082048b7c57b03a754d25c0b4beead08a3362ac1c7a
2025-11-10 23:40:40 +00:00
drh 0c4c0dbc7d Keep explicit NULL values in QRF_STYLE_Json output.
FossilOrigin-Name: cb07f3d441b0b2a3ebdfaa9456891a9c8e33fa07b967532a9edfaf6ff3163cb0
2025-11-10 22:05:42 +00:00
drh dfeb788b0f Merge all the latest trunk improvements into the qrf branch.
FossilOrigin-Name: 1fc2298edc67cbaf1963ed858a36cab15f670c00779ace6b63bdd266b0dbaaf2
2025-11-10 20:11:36 +00:00
drh a04f5335f9 Improved argument parsing and error message infrastructure for
dot-commands in the CLI.

FossilOrigin-Name: 23d5d09db8eae33b250cb8c86b6e6790fc9d5a62ca16df77d8aa881405da66fa
2025-11-10 18:39:33 +00:00
drh 57781916ad The ".www" command is now handled by QRF. So at this point, QRF handles
all query result formatting in the CLI and the legacy formatter has been
removed from the code.

FossilOrigin-Name: 35d4c7151e63c3f105a11dddc853666ae19cfca190204847a42f2b2a5641e95d
2025-11-10 15:56:18 +00:00
drh e029b4a44e Rewrite the ".schema" and ".fullschema" commands in the CLI so as to
eliminate the need for MODE_Pretty and MODE_Semi.

FossilOrigin-Name: 0eb0410f725eed44973cf8712ab2d24c16fb5cbb249b5780f8fe5d41b2193d79
2025-11-10 15:01:57 +00:00
drh 09f16c5ab6 Add the bTextNull field to sqlite3_qrf_spec. Use QRF to implement "tcl"
mode in the CLI.

FossilOrigin-Name: 2ba92320db3c16c3c91e29ea935ae92da546261f25846d242bd2dd27e0b7e032
2025-11-10 12:32:04 +00:00
drh f863c0187c Merge all the latest trunk fixes, and especially the fix for the ".www"
command in the CLI, into the qrf branch.

FossilOrigin-Name: 07a5523cf27fd7c1d8fbc5cde80cb45fadef49317ca59fec9f8e42b2839c3e19
2025-11-10 10:46:55 +00:00
drh e2201e40ce Fixes to autoExplain
FossilOrigin-Name: 38e4171697057437fc9f70f8734c7f557b91da47804ff10b3d2481802377222f
2025-11-10 00:18:36 +00:00
drh 7f28c6d64e Enforce mxRowHeight in columnar layouts. Code clean-up. Minimum column with
is 2 if there is wrapping, in case there are double-wide characters.

FossilOrigin-Name: 0ee8fc00af4c82da0d597c65dd3d8590c71ea78d8f4a29653bbb21668c6c530d
2025-11-09 17:25:23 +00:00
drh c1aac60386 Fixes for harmless static analyzer warnings.
FossilOrigin-Name: 37bd136ae842c8f8110c10b0834c804a8af133ce0caff492f37bd567a56ac6c6
2025-11-09 00:37:29 +00:00
drh 639d8b407b Remove several more hundred lines of dead code from shell.c.in.
FossilOrigin-Name: 9de3e1d3fb0b2127e983aa455cda2f38646891da65eb9ac39bb5b00d8488f52f
2025-11-09 00:01:48 +00:00
drh 3e98ff6b1d Remove about a thousand lines of dead code from shell.c.in.
FossilOrigin-Name: efbcc7decd5347e3460cc3ed62720c8c3932225dfdf92293a990c51895450906
2025-11-08 23:35:57 +00:00
drh edc34bf866 Name changes. QRF_TEXT_Off becomes QRF_TEXT_Plain. mxTotalWidth becomes
nScreenWidth.

FossilOrigin-Name: 871567e377e976e0bb191d466019d1d4abe126f2da1880f1e25cd92b06a666d1
2025-11-08 22:46:29 +00:00
drh bfa8c7d370 Improvements to QRF_STYLE_Line. Make it responsive to mxTotalWidth and
mxRowHeight.  Indent subsequent lines appropriately.  Add corresponding
test cases.

FossilOrigin-Name: 23ddc38cf19845a385c8b33002805c3c864ab9b2e36c3d609794444b06d20c89
2025-11-08 18:31:57 +00:00
drh 44aea46a07 Fix minor issues with alignment and quoting. All legacy tests are now
passing.

FossilOrigin-Name: 712b890531d351cf19d57d8cc6dced5cdd2c394d69fd1ace98298d79ddca269a
2025-11-08 17:08:19 +00:00
drh 96e47e16e7 Miscellaneous fixes to the new alignment configurations.
FossilOrigin-Name: 787fb4c2ac80db6552bc7a740c3c3c63c0c8414cb4e7db80d367b4b5512519d0
2025-11-08 15:25:07 +00:00
drh 22d6a48ba7 Separate column width specification from cell alignment.
FossilOrigin-Name: 5dadfd7f41764ddfed39bb49bbde3a54d8f282bb494c9c2abbba37d0995a7f8e
2025-11-08 14:04:29 +00:00
drh ca111b06f3 Minor changes to fix a few legacy test cases. Snapshot prior to yet another
refactor.

FossilOrigin-Name: b429e7882fd630a490350efa38bfce8686d6727f324a60191337a59d5436828b
2025-11-08 10:48:55 +00:00
drh e8cd0ee61f Make sure columnar outputs always invoke qrfWrite().
FossilOrigin-Name: 21084041196dd6908bb810d8fce80b7f6d5b12ebe474bf62b85128540ad020eb
2025-11-08 03:44:05 +00:00
drh 6eb5f5d9a7 Rewrite the table layout in QRF. Easier to maintain now. Still a few
shell test failures, but much better after this rewrite.

FossilOrigin-Name: 25838d282c5f7a922988245f373edfe8adbe28e929abce83afcda2b08472e685
2025-11-08 03:20:47 +00:00
drh ec27a2e7e5 Fix some problems. But there is a need to do major surgery on the
table layout algorithm, so it seems good to snapshot before proceeding.

FossilOrigin-Name: 9052991a897ad12ac54476d9a370d1ad18937abc3c3fc11efcb678f21cb5ef69
2025-11-07 19:20:49 +00:00
drh d2424338b0 Incremental check-in. QRF is working, but there are many test failures
in the shell test modules.

FossilOrigin-Name: de7fc9afc43d4bd5f5995832cd698633e0e7213c190fa044856c9f82512161f9
2025-11-07 17:48:14 +00:00
drh a6b4039050 Begin the process of integrating QRF into the CLI. Adjust makefiles.
Include the QRF code in shell.c.  But QRF is not yet used by the CLI.

FossilOrigin-Name: 7596fc32b371e5e657e830c2e80e61b0947320a7359833e915a888a438c2e935
2025-11-07 14:31:17 +00:00
drh eedf226700 Update documentation to clarify that mxTotalWidth and mxLength are not
yet implemented.

FossilOrigin-Name: c25806fc5491554f1cd55174c6cf98b509d619e3011b8e26abdf05b24133a6e8
2025-11-07 13:23:29 +00:00
drh 84c03d6c36 Rename "mxWidth" to "mxColWidth". Add "mxTotalWidth" and "mxLength"
settings and hook them up to the TCL interface, but those settings are
not yet implemented.

FossilOrigin-Name: 3f327e8fbc295b43c79118f8f49f8d2e20c2f3ac96e3b7f996720dab858d8199
2025-11-07 13:11:16 +00:00
drh b6dcd0ae00 Fix an error-message test case broken by the previous check-in.
FossilOrigin-Name: fb040354901569915389c8c4c1530dfda8b724be7885adfde654e229d3e98eec
2025-11-07 12:15:25 +00:00
drh 10747754d4 Add the JsonLine output style.
FossilOrigin-Name: 7dfba841cfc515ac28401c4eb90ec24e04fd52cddaeae41be3cfa6967dac0eac
2025-11-07 12:12:02 +00:00
drh 78e831d31f Fix the size of an integer in the TCL interface.
FossilOrigin-Name: 04156c34e4efeebe9b8c2ee2944f9576b685c6aa7e6180c8a5caff4958cde2e8
2025-11-07 00:05:44 +00:00
drh 04586681af Put the style constants in alphabetic order. Improvements to
documentation.

FossilOrigin-Name: d5e891426f8b2a0c3326bf868f2d811c44a1f7e619f42b273c7445ab443d618c
2025-11-06 23:34:30 +00:00
drh 099f7f052b Revamp so that Explain and Eqp styles force the statement into the
the appropriate explain-mode for the rendering and restore it afterwards.
Add the new scanstatus styles: Stats, StatsEst, and StatsVm.

FossilOrigin-Name: 128f1149076ef00c567405ea2112be0ae665b12e81672ae5494d82af11633af5
2025-11-06 19:22:34 +00:00
drh 1f3efba202 Changes to try to improve EXPLAIN capabilities. This is a snapshot
prior to an attempt to make sweeping changes in that area of the
interface.

FossilOrigin-Name: e7367d8089d43a06933f919c33c6edb15ee9bedf52d9a2d9ea989654077d998f
2025-11-06 17:28:34 +00:00
drh cb69edd23d Merge the latest trunk enhancements into the qrf branch.
FossilOrigin-Name: 4e2ada6ad8a56d00aeb73570dc4ee22c484b65b8100a42a7085414d0a4c68afd
2025-11-06 13:25:40 +00:00
drh bac6b5eaef More test cases and a bug fix.
FossilOrigin-Name: 9a9c4580772fa35a68b3e27a40fc9bc615144979dfd27b1054538e29a489c024
2025-11-05 22:48:33 +00:00
drh 2e660d30f5 Further testing and bug fixing.
FossilOrigin-Name: d6b1bac15a692a69f3707fca721b57b3b283edb3850efa34ba42b02c3669fc71
2025-11-05 22:33:47 +00:00
drh 1301bc1f6e The qrf-tester.c program was removed in the previous check-in, but the
makefile rules to build it were not.  This check-in fixes that oversight.

FossilOrigin-Name: d1542696409b67635d0f172042642709452118d62102ebfab2ad3ff2b5732687
2025-11-05 18:35:54 +00:00
drh 33cc175e51 The TCL interface for QRF is now feature complete (at least until I think
of new features to add).  There is still a lot of testing, bug-fixing,
and documentation to be done, however.

FossilOrigin-Name: 633fe4fe584ae14ed6ced1ae137bf96a434a012423d70f0f93876737b0ca075c
2025-11-05 18:27:18 +00:00
drh b230147671 A few simple test cases added.
FossilOrigin-Name: 7200372ed2424d1ac4c88644e0b17a224539f170b9111ced9c88774edbd75261
2025-11-05 16:29:06 +00:00
drh a354a15717 New style setting QRF_STYLE_Auto which chooses box, explain, or EQP depending
on the statement to be rendered.  Improvements to memory management.
Updates to the documentation.

FossilOrigin-Name: 468ba188f034b23398e1f07b915cf7c8b337dcf7f56a13511947c5322ae98722
2025-11-05 15:44:11 +00:00
drh 0296bab9fd The new "format" method in the TCL interface is now partially functional.
FossilOrigin-Name: ace1ebda08740bb248c009cc4a6c99da318a2bba3e43ef20bd0c365c5021705f
2025-11-05 14:07:16 +00:00
drh 9ff8a73ba4 New build product "tclsqlite-ex.c" is the tclsqlite.c file with QRF added.
This is now used whereever tclsqlite.c was used.  Hence QRF is now in the
testfixture and in the tclextension.  The sqlite3 method is "format".  That
method is currently just a non-functional stub.

FossilOrigin-Name: e08d21fe1365176f268f1dcca4048fb5ff043356e5817c8622b4ed9a1a5a58cf
2025-11-05 12:37:42 +00:00
drh 35cc5176e0 Merge the latest trunk enhancements into the qrf branch
FossilOrigin-Name: bed60e9be6d7f33c9f20dbb98bebdfaa7226a949a8e06691aea4d1e7c1af9507
2025-11-04 22:19:21 +00:00
drh 03e6242802 Striving for better names for fields in the spec structure.
FossilOrigin-Name: 9644e2a8db4d97a85aa7660a1536894ab971abca52fb32f946c01de30eb40de3
2025-10-29 13:03:35 +00:00
drh 630b111cd3 Documentation tweaks. Change the name of the eQuote setting to eText for
consistency.

FossilOrigin-Name: 612326750155ff15c9a73fde938aa035490cdd95b364452c1e6b19b521508dfe
2025-10-29 11:19:04 +00:00
drh a73fb2b233 Add support for the bTxtJsonb flag in sqlite3_qrf_spec, which if true causes
JSONB blobs to be displayed as JSON text.

FossilOrigin-Name: 3fcf9ecb039a903cefcf4eeaf43974de0cbd48d899dc189b8f70c3745addc11a
2025-10-28 22:25:24 +00:00
drh ccae8e0c47 Fix a harmless compiler warning.
FossilOrigin-Name: d00c549126ca96b061e537dfabb93b02824e3a9b8fa0c9135a96cf060262206e
2025-10-28 19:37:18 +00:00
drh 74e5918178 Merge the latest trunk enhancements into the qrf branch.
FossilOrigin-Name: fa5661ea8b6b5ca368d719477bd82a954d2f979f4ab85fff2720cf7bb7cc427d
2025-10-28 19:35:26 +00:00
drh c64739e017 Change the documentation name to README.md so that it displays
automatically when browsing the source directory.

FossilOrigin-Name: 0cda773e7665caafa46acbded0cf430d04e08e21d3c1333c1964e7f7fa200290
2025-10-23 23:07:02 +00:00
drh a75737f931 Bring the documentation into sync with the current implementation.
FossilOrigin-Name: b14e05759aed1cc3ea9fbb10d9241e95f8482bb77973f2f7eda8b849a7c3ea5d
2025-10-23 23:03:14 +00:00
drh c873460fd1 Add support for QUOTE and EXPLAIN modes.
FossilOrigin-Name: c4d612a5720eace463ee8b1d7484eddd9701b8f2155479077a1410532e8d3e4e
2025-10-23 20:13:57 +00:00
drh 52d1e3aef3 Add support for EQP mode.
FossilOrigin-Name: 61b2011d8ec2bfe98595ac9ccae5ed0037444757a800706d0ecbc279014fd3fb
2025-10-23 18:57:15 +00:00
drh cc1a3d3d85 Add CSV mode.
FossilOrigin-Name: bf0fec0d4961bd4df3ff874bf8d32b28df48a1739e107ffb0b7bf57affe5f0b6
2025-10-23 17:09:03 +00:00
drh 09efb92900 Implement LINE mode.
FossilOrigin-Name: b4c4ba0ad53c6685410700aa332ebec75c9e32848f881b31bc257f86f7befe2c
2025-10-23 16:57:57 +00:00
drh b3a3a4c006 Add HTML and INSERT modes.
FossilOrigin-Name: 6d695ecdfdc5dca78b430263fc298bb6c8182a2406dc79b70f159abd37463f1e
2025-10-23 16:40:29 +00:00
drh fa56585242 Implement JSON mode.
FossilOrigin-Name: c3b6339162a4feb6de9828d613bf2c5a43f2b0e5dead9cf5c24cf78ac821b305
2025-10-23 16:22:01 +00:00
drh a678784c4c Assigned widths and alignments on columns.
FossilOrigin-Name: f6359f2fa776ef04449660a3452fdf7ee943523bf3290f3c0e15693962bca1db
2025-10-22 21:24:17 +00:00
drh e40b5d9867 Column modes are now working.
FossilOrigin-Name: ed1c8a62ff6985845a2b664f12752f97404b0449e4290d8604a215406590290c
2025-10-22 21:00:52 +00:00
drh f32be465e8 Code in place to do column formats. Compiles, but does not work.
This is an incremental check-in.

FossilOrigin-Name: a05e021354ff5868f75ac01d384c3aaaedb7c483dd8f9533f6ab9fbb66b5f077
2025-10-22 18:09:54 +00:00
drh 3c11df7e92 Merge va_list misuse fix.
FossilOrigin-Name: e33ca68fee5bb6c5195f5a8fef231dcf08433806f5f44b855c2c670d37df6ac7
2025-10-22 12:59:34 +00:00
stephan 21769e1d85 Fix a va_list misuse.
FossilOrigin-Name: 3d044121768548df59a94bbbe4ee360d6464d1416a5cbe7437f4333b06496381
2025-10-22 12:38:39 +00:00
drh 41b5ded8d0 Refactor: Name change to "query result formatter" or "QRF". Simplify names.
put all the code into its own subdirectory of ext/

FossilOrigin-Name: 8646a2ee224819cf459989aa03bd1b9e1a77fc1f8bee8fd45dba778c63467d61
2025-10-22 12:33:59 +00:00
drh 22f8a84fda Simplify the interface to use just a single API.
FossilOrigin-Name: 82dc13ec4887f90995bd984ede9c2670fb7512962ccceb1848a7e9aab7891c00
2025-10-22 12:09:50 +00:00
drh ce3d809d62 Simplify the set of formatting choices.
FossilOrigin-Name: 1f364ea3c08badd555a9ce70bb96cb8f862d6cc6425e9ff41da228fdd2f29361
2025-10-21 20:10:44 +00:00
drh dcd722d204 Add TCL and JSON output formatting. Add the ability to test the xRender.
FossilOrigin-Name: 1a28eae74ed354d556f5ba45c716b03ff36d26b8a6729a5428d7895d8337af78
2025-10-21 19:56:13 +00:00
drh 15a064807f Text quoting formats CSV and HTML.
FossilOrigin-Name: ea69f1f81f867fe058889effdc128e8cc358b61b9121c1932bba8895bd709adb
2025-10-21 19:03:27 +00:00
drh 8e544ca512 Add separate quoting style for BLOB values.
FossilOrigin-Name: dfe537175e7d15507e8532817a06f76dc375c7eb00f5cdc4b4f8d71ec516137e
2025-10-21 15:30:38 +00:00
drh 8c8bd1969c Control-character escaping
FossilOrigin-Name: fbb69764559f4333890860f4f3c010efa9cc994a82743766b948e7635fd079e8
2025-10-21 13:07:51 +00:00
drh 861cb4c0b4 Recognize the various quoting styles and escape modes in the resfmt-test
program.

FossilOrigin-Name: 3ee2a8775fcc557a53c36240ddc039024ff52e21d9ad9021d7cf5bf79f8b9ed4
2025-10-21 11:00:04 +00:00
drh 6e334ef616 More capabilities being added. Incremental check-in.
FossilOrigin-Name: 9adaf791f88875c5afeba7e7aa72efb59df42c6052898f8d7e2f83aede00a044
2025-10-20 19:55:26 +00:00
drh a1f32824e2 Add test program for resfmt.c. Now compiles and runs.
FossilOrigin-Name: 7aaaeea319c7165284028ff8cf1b3448a818c5029de02e7199614bb45aa304c5
2025-10-20 17:19:30 +00:00
drh 5b5b35f6d6 Notes on the result-formatter utility. None of the code works. These
are just ideas I have jotted down as I try to figure out how to do this.

FossilOrigin-Name: c38f1c63247804486a6b368ead4c5122adaedd5c697487fadbed49e1acb155bc
2025-10-20 14:28:57 +00:00
drh a0b13abd34 Merge recent trunk enhancements into the resfmt branch.
FossilOrigin-Name: 20c79a2cc1f9eedea12cbac0a657301614d042e3487ddf37cd4b0316e6f84f21
2025-10-20 10:55:01 +00:00
drh 377c4a8bfe Update the result formatter spec such that column width are stored
in 16-bit signed integers.

FossilOrigin-Name: d5dfbcf077db340690c482c8ebcc4def85fc9127fa6fcde2f71acfd928feaeff
2025-10-09 14:14:14 +00:00
drh 7ebd8a8d85 Add the pRender callback to the query result formatter spec.
FossilOrigin-Name: 642cf4568abaf91e2a6177b874c869661907a2c594ed90c3175847926a82a365
2025-10-08 20:10:01 +00:00
drh f39a16c595 Merge the latest trunk enhancements into the resfmt branch.
FossilOrigin-Name: 550c48f7c3da30743b994c044ecacc20c434c5251dac8959a96550397056d0a2
2025-10-08 19:34:56 +00:00
drh c02f8fdfe2 Initial prototype spec for the SQLite Query Result Formatter.
FossilOrigin-Name: 87b5e41b999877a1d0b4bb049642909c1698dc1b24e4e45631eb13d02818f0ec
2025-10-07 20:11:19 +00:00
49 changed files with 8470 additions and 4111 deletions
+22 -10
View File
@@ -1459,7 +1459,7 @@ SRC01 = \
$(TOP)\src\status.c \
$(TOP)\src\table.c \
$(TOP)\src\threads.c \
$(TOP)\src\tclsqlite.c \
tclsqlite-ex.c \
$(TOP)\src\tokenize.c \
$(TOP)\src\treeview.c \
$(TOP)\src\trigger.c \
@@ -2020,8 +2020,18 @@ sqlite3.c: .target_source sqlite3ext.h sqlite3session.h $(MKSQLITE3C_TOOL) src-v
sqlite3-all.c: sqlite3.c $(TOP)\tool\split-sqlite3c.tcl $(JIM_TCLSH)
$(JIM_TCLSH) $(TOP)\tool\split-sqlite3c.tcl
TCLSQLITEEX = \
$(TOP)\ext\qrf\qrf.h \
$(TOP)\ext\qrf\qrf.c \
$(TOP)\src\tclsqlite.c
tclsqlite-ex.c: $(TCLSQLITEEX) $(TOP)\tool\mkcombo.tcl $(JIM_TCLSH)
$(JIM_TCLSH) $(TOP)\tool\mkcombo.tcl $(TCLSQLITEEX) -o $@
# <</mark>>
tclsqlite-ex.c:
# Rule to build the amalgamation
#
sqlite3.lo: $(SQLITE3C)
@@ -2321,11 +2331,11 @@ whereexpr.lo: $(TOP)\src\whereexpr.c $(HDR)
window.lo: $(TOP)\src\window.c $(HDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\window.c
tclsqlite.lo: $(TOP)\src\tclsqlite.c $(HDR) $(SQLITE_TCL_DEP)
$(LTCOMPILE) $(NO_WARN) -DUSE_TCL_STUBS=1 -DBUILD_sqlite -I$(TCLINCDIR) -c $(TOP)\src\tclsqlite.c
tclsqlite.lo: tclsqlite-ex.c $(HDR) $(SQLITE_TCL_DEP)
$(LTCOMPILE) $(NO_WARN) -DUSE_TCL_STUBS=1 -DBUILD_sqlite -I$(TCLINCDIR) -c tclsqlite-ex.c /OUT:tclsqlite.lo
tclsqlite-shell.lo: $(TOP)\src\tclsqlite.c $(HDR) $(SQLITE_TCL_DEP)
$(LTCOMPILE) $(NO_WARN) -DTCLSH -DBUILD_sqlite -I$(TCLINCDIR) -c $(TOP)\src\tclsqlite.c
tclsqlite-shell.lo: tclsqlite-ex.c $(HDR) $(SQLITE_TCL_DEP)
$(LTCOMPILE) $(NO_WARN) -DTCLSH -DBUILD_sqlite -I$(TCLINCDIR) -c tclsqlite-ex.c
tclsqlite3.exe: tclsqlite-shell.lo $(SQLITE3C) $(SQLITE3H) $(LIBRESOBJS)
$(LTLINK) $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) $(TCLLIBPATHS) $(LTLIBPATHS) /OUT:$@ tclsqlite-shell.lo $(LIBRESOBJS) $(TCLLIBS) $(LTLIBS) $(TLIBS)
@@ -2378,6 +2388,8 @@ keywordhash.h: $(TOP)\tool\mkkeywordhash.c mkkeywordhash.exe
# Source and header files that shell.c depends on
SHELL_DEP = \
$(TOP)\src\shell.c.in \
$(TOP)\ext\qrf\qrf.c \
$(TOP)\ext\qrf\qrf.h \
$(TOP)\ext\expert\sqlite3expert.c \
$(TOP)\ext\expert\sqlite3expert.h \
$(TOP)\ext\intck\sqlite3intck.c \
@@ -2541,9 +2553,9 @@ TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_STRICT_SUBTYPE=1
TESTFIXTURE_SRC0 = $(TESTEXT) $(TESTSRC2)
TESTFIXTURE_SRC1 = $(TESTEXT) $(SQLITE3C)
!IF $(USE_AMALGAMATION)==0
TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC0)
TESTFIXTURE_SRC = $(TESTSRC) tclsqlite-ex.c $(TESTFIXTURE_SRC0)
!ELSE
TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC1)
TESTFIXTURE_SRC = $(TESTSRC) tclsqlite-ex.c $(TESTFIXTURE_SRC1)
!ENDIF
!IF $(USE_STDCALL)!=0 || $(FOR_WIN10)!=0
@@ -2678,14 +2690,14 @@ smoketest: $(TESTPROGS)
shelltest: $(TESTPROGS)
.\testfixture.exe $(TOP)\test\permutations.test shell
sqlite3_analyzer.c: $(SQLITE3C) $(SQLITE3H) $(TOP)\src\tclsqlite.c $(TOP)\tool\spaceanal.tcl $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqlite3_analyzer.c.in $(TOP)\ext\misc\sqlite3_stdio.h $(TOP)\ext\misc\sqlite3_stdio.c $(SQLITE_TCL_DEP)
sqlite3_analyzer.c: $(SQLITE3C) $(SQLITE3H) tclsqlite-ex.c $(TOP)\tool\spaceanal.tcl $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqlite3_analyzer.c.in $(TOP)\ext\misc\sqlite3_stdio.h $(TOP)\ext\misc\sqlite3_stdio.c $(SQLITE_TCL_DEP)
$(TCLSH_CMD) $(TOP)\tool\mkccode.tcl -DINCLUDE_SQLITE3_C $(TOP)\tool\sqlite3_analyzer.c.in > $@
sqlite3_analyzer.exe: sqlite3_analyzer.c $(LIBRESOBJS)
$(LTLINK) $(NO_WARN) -DBUILD_sqlite -I$(TCLINCDIR) sqlite3_analyzer.c \
/link $(LDFLAGS) $(LTLINKOPTS) $(TCLLIBPATHS) $(LTLIBPATHS) $(LIBRESOBJS) $(TCLLIBS) $(LTLIBS) $(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
sqltclsh.c: sqlite3.c tclsqlite-ex.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.exe: sqltclsh.c $(SHELL_CORE_DEP) $(LIBRESOBJS)
@@ -2698,7 +2710,7 @@ sqlite3_expert.exe: $(SQLITE3C) $(TOP)\ext\expert\sqlite3expert.h $(TOP)\ext\exp
CHECKER_DEPS =\
$(TOP)\tool\mkccode.tcl \
sqlite3.c \
$(TOP)\src\tclsqlite.c \
tclsqlite-ex.c \
$(TOP)\ext\repair\sqlite3_checker.tcl \
$(TOP)\ext\repair\checkindex.c \
$(TOP)\ext\repair\checkfreelist.c \
+2 -1
View File
@@ -805,7 +805,8 @@ proc sqlite-handle-common-feature-flags {} {
sqlite-add-feature-flag -DSQLITE_ENABLE_MEMSYS3
}
}
scanstatus -DSQLITE_ENABLE_STMT_SCANSTATUS {}
bytecode-vtab -DSQLITE_ENABLE_BYTECODE_VTAB {}
scanstatus {-DSQLITE_ENABLE_STMT_SCANSTATUS -DSQLITE_ENABLE_BYTECODE_VTAB} {}
column-metadata -DSQLITE_ENABLE_COLUMN_METADATA {}
dbpage -DSQLITE_ENABLE_DBPAGE_VTAB {}
dbstat -DSQLITE_ENABLE_DBSTAT_VTAB {}
+1 -1
View File
@@ -90,7 +90,7 @@ foreach {tn setup} {
proc do_rec_test {tn sql res} {
set res [squish [string trim $res]]
set tst [subst -nocommands {
squish [string trim [exec $::CLI test.db ".expert" {$sql;}]]
squish [string trim [exec $::CLI -noinit test.db ".expert" {$sql;}]]
}]
uplevel [list do_test $tn $tst $res]
}
+51 -43
View File
@@ -133,9 +133,11 @@
** Which sqlite3.c we're using needs to be configurable to enable
** building against a custom copy, e.g. the SEE variant. We have to
** include sqlite3.c, as opposed to sqlite3.h, in order to get access
** to some internal details like SQLITE_MAX_... and friends, and keep
** those consistent with this build. This increases the rebuild time
** considerably, however.
** to some internal details like SQLITE_MAX_... and friends. This
** increases the rebuild time considerably but we need this in order
** to access some internal functionality and keep the to-Java-exported
** values of SQLITE_MAX_... and SQLITE_LIMIT_... in sync with the C
** build.
*/
#ifndef SQLITE_C
# define SQLITE_C sqlite3.c
@@ -333,7 +335,7 @@ struct S3JniNphOp {
const char * const zMember /* Name of member property */;
const char * const zTypeSig /* JNI type signature of zMember */;
/*
** klazz is a global ref to the class represented by zName.
** klazz is a global ref to the class represented by pRef.
**
** According to:
**
@@ -997,20 +999,19 @@ static S3JniEnv * S3JniEnv__get(JNIEnv * const env){
** JNI bindings such as sqlite3_prepare_v2/v3(), and definitely not
** from client code.
**
** Returns err_code _unless_ err_code is 0 and sqlite3_set_errmsg()
** fails with OOM, in which case it may return SQLITE_OOM or fail
** fatally.
**
** This function predates sqlite3_set_errmsg(), which is why it has a
** slightly different interface. Before that function was introduced,
** this code used the SQLite-internal APIs to do this.
** Returns err_code.
*/
static int s3jni_db_error(JNIEnv * env, sqlite3* const db,
int err_code, const char * const zMsg){
static int s3jni_db_error(sqlite3* const db, int err_code,
const char * const zMsg){
if( db!=0 ){
int const rc = sqlite3_set_errmsg(db, err_code, zMsg);
s3jni_oom_fatal(0==rc);
if( rc && !err_code ) err_code=rc;
if( 0==zMsg ){
sqlite3Error(db, err_code);
}else{
const int nMsg = sqlite3Strlen30(zMsg);
sqlite3_mutex_enter(sqlite3_db_mutex(db));
sqlite3ErrorWithMsg(db, err_code, "%.*s", nMsg, zMsg);
sqlite3_mutex_leave(sqlite3_db_mutex(db));
}
}
return err_code;
}
@@ -1234,11 +1235,11 @@ static int s3jni__db_exception(JNIEnv * const env, sqlite3 * const pDb,
char * zMsg;
S3JniExceptionClear;
zMsg = s3jni_exception_error_msg(env, ex);
s3jni_db_error(env, pDb, errCode, zMsg ? zMsg : zDfltMsg);
s3jni_db_error(pDb, errCode, zMsg ? zMsg : zDfltMsg);
sqlite3_free(zMsg);
S3JniUnrefLocal(ex);
}else if( zDfltMsg ){
s3jni_db_error(env, pDb, errCode, zDfltMsg);
s3jni_db_error(pDb, errCode, zDfltMsg);
}
return errCode;
}
@@ -1955,6 +1956,15 @@ static void S3JniUdf_finalizer(void * s){
S3JniUdf_free(s3jni_env(), (S3JniUdf*)s, 1);
}
/*
** Helper for processing args to UDF handlers with signature
** (sqlite3_context*,int,sqlite3_value**).
*/
typedef struct {
jobject jcx /* sqlite3_context */;
jobjectArray jargv /* sqlite3_value[] */;
} udf_jargs;
/*
** Converts the given (cx, argc, argv) into arguments for the given
** UDF, writing the result (Java wrappers for cx and argv) in the
@@ -1996,7 +2006,7 @@ error_oom:
/*
** Requires that jCx and jArgv are sqlite3_context
** resp. array-of-sqlite3_value values initialized by udf_args(). The
** (argc,argv) are (0,NULL) for UDF types with no arguments. This
** latter will be 0-and-NULL for UDF types with no arguments. This
** function zeroes out the nativePointer member of jCx and each entry
** in jArgv. This is a safety-net precaution to avoid undefined
** behavior if a Java-side UDF holds a reference to its context or one
@@ -2089,19 +2099,19 @@ static int udf_xFSI(sqlite3_context* const pCx, int argc,
sqlite3_value** const argv, S3JniUdf * const s,
jmethodID xMethodID, const char * const zFuncType){
S3JniDeclLocal_env;
jobject jcx = 0 /* sqlite3_context */;
jobjectArray jargv = 0 /* sqlite3_value[] */;
int rc = udf_args(env, pCx, argc, argv, &jcx, &jargv);
udf_jargs args = {0,0};
int rc = udf_args(env, pCx, argc, argv, &args.jcx, &args.jargv);
if( 0 == rc ){
(*env)->CallVoidMethod(env, s->jObj, xMethodID, jcx, jargv);
(*env)->CallVoidMethod(env, s->jObj, xMethodID, args.jcx, args.jargv);
S3JniIfThrew{
rc = udf_report_exception(env, 'F'==zFuncType[1]/*xFunc*/, pCx,
s->zFuncName, zFuncType);
}
udf_unargs(env, jcx, argc, jargv);
udf_unargs(env, args.jcx, argc, args.jargv);
}
S3JniUnrefLocal(jcx);
S3JniUnrefLocal(jargv);
S3JniUnrefLocal(args.jcx);
S3JniUnrefLocal(args.jargv);
return rc;
}
@@ -3290,7 +3300,7 @@ static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env,
S3JniDb_mutex_enter;
ps = S3JniDb_from_jlong(jpDb);
if( !ps ){
s3jni_db_error(env, ps->pDb, SQLITE_MISUSE, 0);
s3jni_db_error(ps->pDb, SQLITE_MISUSE, 0);
S3JniDb_mutex_leave;
return 0;
}
@@ -3310,14 +3320,13 @@ static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env,
else sqlite3_rollback_hook(ps->pDb, 0, 0);
}else{
jclass const klazz = (*env)->GetObjectClass(env, jHook);
jmethodID const xCallback =
(*env)->GetMethodID(env, klazz, "call",
isCommit ? "()I" : "()V");
jmethodID const xCallback = (*env)->GetMethodID(env, klazz, "call",
isCommit ? "()I" : "()V");
S3JniUnrefLocal(klazz);
S3JniIfThrew {
S3JniExceptionReport;
S3JniExceptionClear;
s3jni_db_error(env, ps->pDb, SQLITE_ERROR,
s3jni_db_error(ps->pDb, SQLITE_ERROR,
"Cannot not find matching call() method in"
"hook object.");
}else{
@@ -3597,7 +3606,7 @@ S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(),
(*env)->GetMethodID(env, klazz, "call", "([B[B)I");
S3JniUnrefLocal(klazz);
S3JniIfThrew{
rc = s3jni_db_error(env, ps->pDb, SQLITE_ERROR,
rc = s3jni_db_error(ps->pDb, SQLITE_ERROR,
"Could not get call() method from "
"CollationCallback object.");
}else{
@@ -3636,15 +3645,15 @@ S3JniApi(sqlite3_create_function() sqlite3_create_function_v2()
if( !pDb || !jFuncName ){
return SQLITE_MISUSE;
}else if( !encodingTypeIsValid(eTextRep & 0x0f) ){
return s3jni_db_error(env, pDb, SQLITE_FORMAT,
}else if( !encodingTypeIsValid(eTextRep) ){
return s3jni_db_error(pDb, SQLITE_FORMAT,
"Invalid function encoding option.");
}
s = S3JniUdf_alloc(env, jFunctor);
if( !s ) return SQLITE_NOMEM;
if( UDF_UNKNOWN_TYPE==s->type ){
rc = s3jni_db_error(env, pDb, SQLITE_MISUSE,
rc = s3jni_db_error(pDb, SQLITE_MISUSE,
"Cannot unambiguously determine function type.");
S3JniUdf_free(env, s, 1);
goto error_cleanup;
@@ -4003,7 +4012,7 @@ S3JniApi(sqlite3_jni_db_error(), jint, 1jni_1db_1error)(
zStr = jStr
? s3jni_jstring_to_utf8( jStr, 0)
: NULL;
rc = s3jni_db_error(env, ps->pDb, (int)jRc, zStr );
rc = s3jni_db_error( ps->pDb, (int)jRc, zStr );
sqlite3_free(zStr);
}
return rc;
@@ -4312,7 +4321,7 @@ static void s3jni_updatepre_hook_impl(void * pState, sqlite3 *pDb, int opId,
jTable = jDbName ? s3jni_utf8_to_jstring( zTable, -1) : 0;
S3JniIfThrew {
S3JniExceptionClear;
s3jni_db_error(env, ps->pDb, SQLITE_NOMEM, 0);
s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0);
}else{
assert( hook.jObj );
assert( hook.midCallback );
@@ -4414,7 +4423,7 @@ static jobject s3jni_updatepre_hook(JNIEnv * env, int isPre, jlong jpDb, jobject
S3JniUnrefLocal(klazz);
S3JniIfThrew {
S3JniExceptionClear;
s3jni_db_error(env, ps->pDb, SQLITE_ERROR,
s3jni_db_error(ps->pDb, SQLITE_ERROR,
"Cannot not find matching callback on "
"(pre)update hook object.");
}else{
@@ -4523,7 +4532,7 @@ S3JniApi(sqlite3_progress_handler(),void,1progress_1handler)(
S3JniUnrefLocal(klazz);
S3JniIfThrew {
S3JniExceptionClear;
s3jni_db_error(env, ps->pDb, SQLITE_ERROR,
s3jni_db_error(ps->pDb, SQLITE_ERROR,
"Cannot not find matching xCallback() on "
"ProgressHandler object.");
}else{
@@ -4897,9 +4906,8 @@ S3JniApi(sqlite3_set_authorizer(),jint,1set_1authorizer)(
")I");
S3JniUnrefLocal(klazz);
S3JniIfThrew {
rc = s3jni_db_error(env, ps->pDb, SQLITE_ERROR,
"Error setting up Java parts of "
"authorizer hook.");
rc = s3jni_db_error(ps->pDb, SQLITE_ERROR,
"Error setting up Java parts of authorizer hook.");
}else{
rc = sqlite3_set_authorizer(ps->pDb, s3jni_xAuth, ps);
}
@@ -5183,7 +5191,7 @@ S3JniApi(sqlite3_trace_v2(),jint,1trace_1v2)(
S3JniUnrefLocal(klazz);
S3JniIfThrew {
S3JniExceptionClear;
rc = s3jni_db_error(env, ps->pDb, SQLITE_ERROR,
rc = s3jni_db_error(ps->pDb, SQLITE_ERROR,
"Cannot not find matching call() on "
"TracerCallback object.");
}else{
+1 -3
View File
@@ -815,9 +815,7 @@ public class Tester1 implements Runnable {
};
// Register and use the function...
int rc = sqlite3_create_function(db, "myfunc", -1,
SQLITE_UTF8 | SQLITE_INNOCUOUS,
func);
int rc = sqlite3_create_function(db, "myfunc", -1, SQLITE_UTF8, func);
affirm(0 == rc);
affirm(0 == xFuncAccum.value);
final sqlite3_stmt stmt = prepare(db, "SELECT myfunc(1,2,3)");
+19 -1
View File
@@ -258,7 +258,7 @@ int sqlite3_fputs(const char *z, FILE *out){
/*
** Work-alike for fprintf() from the standard C library.
** Work-alikes for fprintf() and vfprintf() from the standard C library.
*/
int sqlite3_fprintf(FILE *out, const char *zFormat, ...){
int rc;
@@ -285,6 +285,24 @@ int sqlite3_fprintf(FILE *out, const char *zFormat, ...){
}
return rc;
}
int sqlite3_vfprintf(FILE *out, const char *zFormat, va_list ap){
int rc;
if( UseWtextForOutput(out) ){
/* When writing to the command-prompt in Windows, it is necessary
** to use _O_WTEXT input mode and write UTF-16 characters.
*/
char *z;
z = sqlite3_vmprintf(zFormat, ap);
sqlite3_fputs(z, out);
rc = (int)strlen(z);
sqlite3_free(z);
}else{
/* Writing to a file or other destination, just write bytes without
** any translation. */
rc = vfprintf(out, zFormat, ap);
}
return rc;
}
/*
** Set the mode for an output stream. mode argument is typically _O_BINARY or
+3
View File
@@ -31,6 +31,7 @@
#ifdef _WIN32
/**** Definitions For Windows ****/
#include <stdio.h>
#include <stdarg.h>
#include <windows.h>
FILE *sqlite3_fopen(const char *zFilename, const char *zMode);
@@ -38,6 +39,7 @@ FILE *sqlite3_popen(const char *zCommand, const char *type);
char *sqlite3_fgets(char *s, int size, FILE *stream);
int sqlite3_fputs(const char *s, FILE *stream);
int sqlite3_fprintf(FILE *stream, const char *format, ...);
int sqlite3_vfprintf(FILE *stream, const char *format, va_list);
void sqlite3_fsetmode(FILE *stream, int mode);
@@ -49,6 +51,7 @@ void sqlite3_fsetmode(FILE *stream, int mode);
#define sqlite3_fgets fgets
#define sqlite3_fputs fputs
#define sqlite3_fprintf fprintf
#define sqlite3_vfprintf vfprintf
#define sqlite3_fsetmode(F,X) /*no-op*/
#endif
+674
View File
@@ -0,0 +1,674 @@
# SQLite Query Result Formatting Subsystem
The "Query Result Formatter" or "QRF" subsystem is a C-language
subroutine that formats the output from an SQLite query for display using
a fix-width font, for example on a terminal window over an SSH connection.
The output format is configurable. The application can request various
table formats, with flexible column widths and alignments, row-oriented
formats, such as CSV and similar, as well as various special purpose formats
like JSON.
For the first 25 years of SQLite's existance, the
[command-line interface](https://sqlite.org/cli.html) (CLI)
formatted query results using a hodge-podge of routines
that had grown slowly by accretion. The QRF was created
in fall of 2025 to refactor and reorganize this code into
a more usable form. The idea behind QRF is to implement all the
query result formatting capabilities of the CLI in a subroutine
that can be incorporated and reused by other applications.
## 1.0 Overview Of Operation
Suppose `pStmt` is a pointer to an SQLite prepared statement
(a pointer to an `sqlite3_stmt` object) that has been reset and
bound and is ready to run. Then to format the output from this
prepared statement, use code similar to the following:
> ~~~
sqlite3_qrf_spec spec; /* Format specification */
char *zErrMsg; /* Text error message (optional) */
char *zResult = 0; /* Formatted output written here */
int rc; /* Result code */
memset(&spec, 0, sizeof(spec)); /* Initialize the spec */
spec.iVersion = 1; /* Version number must be 1 */
spec.pzOutput = &zResult; /* Write results in variable zResult */
/* Optionally fill in other settings in spec here, as needed */
zErrMsg = 0; /* Not required; just being pedantic */
rc = sqlite3_format_query_result(pStmt, &spec, &zErrMsg); /* Format results */
if( rc ){
printf("Error (%d): %s\n", rc, zErrMsg); /* Report an error */
sqlite3_free(zErrMsg); /* Free the error message text */
}else{
printf("%s", zResult); /* Report the results */
sqlite3_free(zResult); /* Free memory used to hold results */
}
~~~
The `sqlite3_qrf_spec` object describes the desired output format
and what to do with the generated output. Most of the work in using
the QRF involves filling out the sqlite3_qrf_spec.
## 2.0 The `sqlite3_qrf_spec` object
The `sqlite3_qrf_spec` structure defines how the results of a query
are to be formatted, and what to do with the formatted text. The
most recent definition of `sqlite3_qrf_spec` is shown below.
Do not be alarmed by the complexity of this structure. You only have
to understand the properties you want to modify. Zero is always a good
default for all of the attributes (except iVersion and pzOutput/xWrite)
and so simply zeroing out the bulk of this structure is a good start.
You can then slowly make adjustments to individual fields to get the
results you desire.
> ~~~
typedef struct sqlite3_qrf_spec sqlite3_qrf_spec;
struct sqlite3_qrf_spec {
unsigned char iVersion; /* Version number of this structure */
unsigned char eStyle; /* Formatting style. "box", "csv", etc... */
unsigned char eEsc; /* How to escape control characters in text */
unsigned char eText; /* Quoting style for text */
unsigned char eTitle; /* Quating style for the text of column names */
unsigned char eBlob; /* Quoting style for BLOBs */
unsigned char bTitles; /* True to show column names */
unsigned char bWordWrap; /* Try to wrap on word boundaries */
unsigned char bTextJsonb; /* Render JSONB blobs as JSON text */
unsigned char bTextNull; /* Apply eText encoding to zNull[] */
unsigned char eDfltAlign; /* Default alignment, no covered by aAlignment */
unsigned char eTitleAlign; /* Alignment for column headers */
short int nWrap; /* Wrap columns wider than this */
short int nScreenWidth; /* Maximum overall table width */
short int nLineLimit; /* Maximum number of lines for any row */
int nCharLimit; /* Maximum number of characters in a cell */
int nWidth; /* Number of entries in aWidth[] */
int nAlign; /* Number of entries in aAlignment[] */
short int *aWidth; /* Column widths */
unsigned char *aAlign; /* Column alignments */
char *zColumnSep; /* Alternative column separator */
char *zRowSep; /* Alternative row separator */
char *zTableName; /* Output table name */
char *zNull; /* Rendering of NULL */
char *(*xRender)(void*,sqlite3_value*); /* Render a value */
int (*xWrite)(void*,const char*,sqlite3_int64); /* Write output */
void *pRenderArg; /* First argument to the xRender callback */
void *pWriteArg; /* First argument to the xWrite callback */
char **pzOutput; /* Storage location for output string */
/* Additional fields may be added in the future */
};
~~~
Again, the only fields that must initialized are:
* `.iVersion`
* One of `.pzOutput` or `.xWrite`.
All other fields can be zeroed. Or they can contain other values to
alter the formatting of the query results.
Further detail on the meanings of each of the fields in the
`sqlite3_qrf_spec` object are described below.
### 2.1 Structure Version Number
The sqlite3_qrf_spec.iVersion field must be 1. Future enhancements to
the QRF might add new fields onto the bottom of the sqlite3_qrf_spec
object. Those new fields will only be accessible if the iVersion is greater
than 1. Thus the iVersion field is used to support upgradability.
### 2.2 Output Deposition (xWrite and pzOutput)
The formatted output can either be sent to a callback function
or accumulated into an output buffer in memory obtained
from sqlite3_malloc(). If the sqlite3_qrf_spec.xWrite column is not NULL,
then that function is invoked (using sqlite3_qrf_spec.xWriteArg as its
first argument) to transmit the formatted output. Or, if
sqlite3_qrf_spec.pzOutput points to a pointer to a character, then that
pointer is made to point to memory obtained from sqlite3_malloc() that
contains the complete text of the formatted output. If spec.pzOutput\[0\]
is initially non-NULL, then it is assumed to point to memory obtained
from sqlite3_malloc(). In that case, the buffer is resized using
sqlite3_realloc() and the new text is appended.
One of either sqlite3_qrf_spec.xWrite and sqlite3_qrf_spec.pzOutput must be
non-NULL and the other must be NULL.
The return value from xWrite is an SQLITE result code. The usual return
should be SQLITE_OK. But if for some reason the write fails, a different
value might be returned.
### 2.3 Output Format
The sqlite3_qrf_spec.eStyle field is an integer code that defines the
specific output format that will be generated. See section 4.0 below
for details on the meaning of the various style options.
Other fields in sqlite3_qrf_spec might be used or might be
ignored, depending on the value of eStyle.
### 2.4 Show Column Names (bTitles)
The sqlite3_qrf_spec.bTitles field can be either QRF_SW_Auto,
QRF_SW_On, or QRF_SW_Off. Those three constants also have shorter
alternative spellings: QRF_Auto, QRF_No, and
QRF_Yes.
> ~~~
#define QRF_SW_Auto 0 /* Let QRF choose the best value */
#define QRF_SW_Off 1 /* This setting is forced off */
#define QRF_SW_On 2 /* This setting is forced on */
#define QRF_Auto 0 /* Alternate spelling for QRF_SW_Auto and others */
#define QRF_No 1 /* Alternate spelling for QRF_SW_Off */
#define QRF_Yes 2 /* Alternate spelling for QRF_SW_On */
~~~
If the value is QRF_Yes, then column names appear in the output.
If the value is QRF_No, column names are omitted. If the
value is QRF_Auto, then an appropriate default is chosen.
### 2.5 Control Character Escapes (eEsc)
The sqlite3_qrf_spec.eEsc determines how ASCII control characters are
formatted when displaying TEXT values in the result. These are the allowed
values:
> ~~~
#define QRF_ESC_Auto 0 /* Choose the ctrl-char escape automatically */
#define QRF_ESC_Off 1 /* Do not escape control characters */
#define QRF_ESC_Ascii 2 /* Unix-style escapes. Ex: U+0007 shows ^G */
#define QRF_ESC_Symbol 3 /* Unicode escapes. Ex: U+0007 shows U+2407 */
~~~
If the value of eEsc is QRF_ESC_Ascii, then the control character
with value X is displayed as ^Y where Y is X+0x40. Hence, a
backspace character (U+0008) is shown as "^H".
If eEsc is QRF_ESC_Symbol, then control characters in the range of U+0001
through U+001f are mapped into U+2401 through U+241f, respectively.
If the value of eEsc is QRF_ESC_Off, then no translation occurs
and control characters that appear in TEXT strings are transmitted
to the formatted output as-is. This can be dangerous in applications,
since an adversary who can control TEXT values might be able to
inject ANSI cursor movement sequences to hide nefarious values.
The QRF_ESC_Auto value for eEsc means that the query result formatter
gets to pick whichever control-character encoding it thinks is best for
the situation. This will usually be QRF_ESC_Ascii.
The TAB (U+0009), LF (U+000a) and CR-LF (U+000d,U+000a) character
sequence are always output literally and are not mapped to alternative
display values, regardless of this setting.
### 2.6 Display of TEXT values (eText, eTitle)
The sqlite3_qrf_spec.eText controls how text values are rendered in the
display. sqlite3_qrf_spec.eTitle controls how column names are rendered.
Both fields can have one of the following values:
> ~~~
#define QRF_TEXT_Auto 0 /* Choose text encoding automatically */
#define QRF_TEXT_Plain 1 /* Literal text */
#define QRF_TEXT_Sql 2 /* Quote as an SQL literal */
#define QRF_TEXT_Csv 3 /* CSV-style quoting */
#define QRF_TEXT_Html 4 /* HTML-style quoting */
#define QRF_TEXT_Tcl 5 /* C/Tcl quoting */
#define QRF_TEXT_Json 6 /* JSON quoting */
~~~
A value of QRF_TEXT_Auto means that the query result formatter will choose
what it thinks will be the best text encoding.
A value of QRF_TEXT_Plain means that text values appear in the output exactly
as they are found in the database file, with no translation.
A value of QRF_TEXT_Sql means that text values are escaped so that they
look like SQL literals. That means the value will be surrounded by
single-quotes (U+0027) and any single-quotes contained within the text
will be doubled.
A value of QRF_TEXT_Csv means that text values are escaped in accordance
with RFC&nbsp;4180, which defines Comma-Separated-Value or CSV files.
Text strings that contain no special values appears as-is. Text strings
that contain special values are contained in double-quotes (U+0022) and
any double-quotes within the value are doubled.
A value of QRF_TEXT_Html means that text values are escaped for use in
HTML. Special characters "&lt;", "&amp;", "&gt;", "&quot;", and "&#39;"
are displayed as "&amp;lt;", "&amp;amp;", "&amp;gt;", "&amp;quot;",
and "&amp;#39;", respectively.
A value of QRF_TEXT_Tcl means that text values are displayed inside of
double-quotes and special characters within the string are escaped using
backslash escape, as in ANSI-C or TCL or Perl or other popular programming
languages.
A value of QRF_TEXT_Json gives similar results as QRF_TEXT_Tcl except that the
rules are adjusted so that the displayed string is strictly conforming
the JSON specification.
### 2.7 How to display BLOB values (eBlob and bTextJsonb)
If the sqlite3_qrf_spec.bTextJsonb flag is QRF_SW_On and if the value to be
displayed is JSONB, then the JSONB is translated into text JSON and the
text is shown according to the sqlite3_qrf_spec.eText setting as
described in the previous section.
If the bTextJsonb flag is QRF_SW_Off (the usual case) or if the BLOB value to
be displayed is not JSONB, then the sqlite3_qrf_spec.eBlob field determines
how the BLOB value is formatted. The following options are available;
> ~~~
#define QRF_BLOB_Auto 0 /* Determine BLOB quoting using eText */
#define QRF_BLOB_Text 1 /* Display content exactly as it is */
#define QRF_BLOB_Sql 2 /* Quote as an SQL literal */
#define QRF_BLOB_Hex 3 /* Hexadecimal representation */
#define QRF_BLOB_Tcl 4 /* "\000" notation */
#define QRF_BLOB_Json 5 /* A JSON string */
~~~
A value of QRF_BLOB_Auto means that display format is selected automatically
by sqlite3_format_query_result() based on eStyle and eText.
A value of QRF_BLOB_Text means that BLOB values are interpreted as UTF8
text and are displayed using formatting results set by eEsc and
eText.
A value of QRF_BLOB_Sql means that BLOB values are shown as SQL BLOB
literals: a prefix "`x'`" following by hexadecimal and ending with a
final "`'`".
A value of QRF_BLOB_Hex means that BLOB values are shown as
hexadecimal text with no delimiters.
A value of QRF_BLOB_Tcl means that BLOB values are shown as a
C/Tcl/Perl string literal where every byte is an octal backslash
escape. So a BLOB of `x'052881f3'` would be displayed as
`"\005\050\201\363"`.
A value of QRF_BLOB_Json is similar to QRF_BLOB_Tcl except that is
uses unicode backslash escapes, since JSON does not understand
the C/Tcl/Perl octal backslash escapes. So the string from the
previous paragraph would be shown as
`"\u0005\u0028\u0081\u00f3"`.
### 2.8 Maximum size of displayed content (nLineLimit, nCharLimit)
If the sqlite3_qrf_spec.nCharLimit setting is non-zero, then the formatter
will display only the first nCharLimit characters of each value.
Only characters that take up space are counted when enforcing this
limit. Zero-width characters and VT100 escape sequences do not count
toward this limit. The count is in characters, not bytes. When
imposing this limit, the formatter adds the three characters "..."
to the end of the value. Those added characters are not counted
as part of the limit. Very small limits still result in truncation,
but might render a few more characters than the limit.
If the sqlite3_qrf_spec.nLineLimit setting is non-zero, then the
formatter will only display the first nLineLimit lines of each value.
It does not matter if the value is split because it contains a newline
character, or if it split by wrapping. This setting merely limits
the number of displayed lines. This setting only works for
**Box**, **Column**, **Line**, **Markdown**, and **Table** styles.
The idea behind both of these settings is to prevent large renderings
when doing a query that (unexpectedly) contains very large text or
blob values: perhaps megabyes of text.
### 2.9 Word Wrapping In Columnar Styles (nWrap, bWordWrap)
When using columnar formatting modes (QRF_STYLE_Box, QRF_STYLE_Column,
QRF_STYLE_Markdown, or QRF_STYLE_Table), the formatter attempts to limit
the width of any individual column to sqlite3_qrf_spec.nWrap characters
if nWrap is non-zero. A zero value for nWrap means "unlimited".
The nWrap limit might be exceeded if the limit is very small.
In order to keep individual columns within requested width limits,
it is sometimes necessary to wrap the content for a single row of
a single column across multiple lines. When this
becomes necessary and if the bWordWrap setting is QRF_Yes, then the
formatter attempts to split the content on whitespace or at a word boundary.
If bWordWrap is QRF_No, then the formatter is free to split content
anywhere, including in the middle of a word.
For narrow columns and wide words, it might sometimes be necessary to split
a column in the middle of a word, even when bWordWrap is QRF_Yes.
### 2.10 Helping The Output To Fit On The Terminal (nScreenWidth)
The sqlite3_qrf_spec.nScreenWidth field can be set the number of
characters that will fit on one line on the viewer output device.
This is typically a number like 80 or 132. The formatter will attempt
to reduce the length of output lines, depending on the style, so
that all output fits on that screen.
A value of zero for nScreenWidth means "unknown" or "no width limit".
When the value is zero, the formatter makes no attempt to keep the
lines of output short.
The nScreenWidth is a hint to the formatter, not a requirement.
The formatter trieds to keep lines below the nScreenWidth limit,
but it does not guarantee that it will.
### 2.11 Individual Column Width (nWidth and aWidth)
The sqlite3_qrf_spec.aWidth field is a pointer to an array of
signed 16-bit integers that control the width of individual columns
in columnar output modes (QRF_STYLE_Box, QRF_STYLE_Column,
QRF_STYLE_Markdown, or QRF_STYLE_Table). The sqlite3_qrf_spec.nWidth
field is the number of integers in the aWidth array.
If aWidth is a NULL pointer or if nWidth is zero, then the array is
assumed to be all zeros. If nWidth is less then the number of
columns in the output, then zero is used for the width
for all columns past then end of the aWidth array.
The aWidth array is deliberately an array of 16-bit signed integers.
Only 16 bits are used because no good comes for having very large
column widths. The range if further restricted as follows:
> ~~~
#define QRF_MAX_WIDTH 10000 /* Maximum column width */
#define QRF_MIN_WIDTH 0 /* Minimum column width */
~~~
A width greater than then QRF_MAX_WIDTH is interpreted as QRF_MAX_WIDTH.
Any aWidth\[\] value of zero means the formatter should use a flexible
width column (limited only by sqlite_qrf_spec.mxWidth) that is just
big enough to hold the largest row.
For historical compatibility, aWidth\[\] can contain negative values,
down to -QRF_MAX_WIDTH. The column width used is the absolute value
of the number in aWidth\[\]. The only difference is that negative
values cause the default horizontal alignment to be QRF_ALIGN_Right.
The sign of the aWidth\[\] values only affects alignment if the
alignment is not otherwise specified by aAlign\[\] or eDfltAlign.
Again, negative values for aWidth\[\] entries are supported for
backwards compatibility only, and are not recommended for new
applications.
### 2.12 Alignment (nAlignment, aAlignment, eDfltAlign, eTitleAlign)
Some cells in a display table might contain a lot of text and thus
be wide, or they might contain newline characters or be wrapped by
width constraints so that they span many rows of text. Other cells
might be narrower and shorter. In columnar formats, the display width
of a cell is the maximum of the widest value in the same column, and the
display height is the height of the tallest value in the same row.
So some cells might be much taller and wider than necessary to hold
their values.
Alignment determines where smaller values are placed within larger cells.
The sqlite3_qrf_spec.aAlign field points to an array of unsigned characters
that specifies alignment (both vertical and horizontal) of individual
columns within the table. The sqlite3_qrf_spec.nAlign fields holds
the number of entries in the aAlign\[\] array.
If sqlite3_qrf_spec.aAlign is a NULL pointer or if sqlite3_qrf_spec.nAlign
is zero, or for columns to the right of what are specified by
sqlite3_qrf_spec.nAlign, the sqlite3_qrf_spec.eDfltAlign value is used
for the alignment. Column names can be (and often are) aligned
differently, as specified by sqlite3_qrf_spec.eTitleAlign.
Each alignment value specifies both vertical and horizontal alignment.
Horizontal alignment can be left, center, right, or no preference.
Vertical alignment can be top, middle, bottom, or no preference.
Thus there are 16 possible alignment values, as follows:
> ~~~
/*
** Horizontal Vertial
** ---------- -------- */
#define QRF_ALIGN_Auto 0 /* auto auto */
#define QRF_ALIGN_Left 1 /* left auto */
#define QRF_ALIGN_Center 2 /* center auto */
#define QRF_ALIGN_Right 3 /* right auto */
#define QRF_ALIGN_Top 4 /* auto top */
#define QRF_ALIGN_NW 5 /* left top */
#define QRF_ALIGN_N 6 /* center top */
#define QRF_ALIGN_NE 7 /* right top */
#define QRF_ALIGN_Middle 8 /* auto middle */
#define QRF_ALIGN_W 9 /* left middle */
#define QRF_ALIGN_C 10 /* center middle */
#define QRF_ALIGN_E 11 /* right middle */
#define QRF_ALIGN_Bottom 12 /* auto bottom */
#define QRF_ALIGN_SW 13 /* left bottom */
#define QRF_ALIGN_S 14 /* center bottom */
#define QRF_ALIGN_SE 15 /* right bottom */
~~~
Notice how alignment values with an unspecified horizontal
or vertical component can be added to another alignment value
for which that component is specified, to get a fully
specified alignment. For eample:
> QRF_ALIGN_Center + QRF_ALIGN_Bottom == QRF_ALIGN_S.
The alignment for column names is always determined by the
eTitleAlign setting. If eTitleAlign is QRF_Auto, then column
names use center-bottom alignment, QRF_ALIGN_W, value 14.
The aAlign\[\] and eDfltAlign settings have no affect on
column names.
For data in the first nAlign columns, the aAlign\[\] array
entry for that column takes precedence. If either the horizontal
or vertical alignment has an "auto" value for that column or if
a column is beyond the first nAlign entries, then eDfltAlign
is used as a backup. If neither aAlign\[\] nor eDfltAlign
specify a horizontal alignment, then values are left-aligned
(QRF_ALIGN_Left). If neither aAlign\[\] nor eDfltAlign
specify a vertical alignment, then values are top-aligned
(QRF_ALIGN_Top).
*As of 2025-11-08, only horizontal alignment is implemented.
The vertical alignment settings are currently ignored and
the vertical alignment is always QRF_ALIGN_Top.*
### 2.13 Row and Column Separator Strings
The sqlite3_qrf_spec.zColumnSep and sqlite3_qrf_spec.zRowSep strings
are alternative column and row separator character sequences. If not
specified (if these pointers are left as NULL) then appropriate defaults
are used. Some output styles have hard-coded column and row separators
and these settings are ignored for those styles.
### 2.14 The Output Table Name
The sqlite3_qrf_spec.zTableName value is the name of the output table
when eStyle is QRF_STYLE_Insert.
### 2.15 The Rendering Of NULL (zNull, eTextNull)
If a value is NULL then show the NULL using the string
found in sqlite3_qrf_spec.zNull. If zNull is itself a NULL pointer
then NULL values are rendered as an empty string.
If the sqlite3_qrf_spec.bTextNull field is QRF_Yes, then the
text encoding specified by eText is applied to the value in
zNull. If bTextNull is QRF_No or QRF_Auto, then the value
in zNull is shown verbatim.
### 2.16 Optional Value Rendering Callback
If the sqlite3_qrf_spec.xRender field is not NULL, then each
sqlite3_value coming out of the query is first passed to the
xRender function, giving that function an opportunity to render
the results itself, using whatever custom format is desired.
If xRender chooses to render, it should write the rendering
into memory obtained from sqlite3_malloc() and return a pointer
to that memory. The xRender function can decline
to render (for example, based on the sqlite3_value_type() or other
characteristics of the value) in which case it can simply return a
NULL pointer and the usual default rendering will be used instead.
The sqlite3_format_query_result() function (which calls xRender)
will take responsibility for freeing the string returned by xRender
after it has finished using it.
The eText, eBlob, and eEsc settings above become no-ops if the xRender
routine returns non-NULL. In other words, the application-supplied
xRender routine is expected to do all of its own quoting and formatting.
## 3.0 The `sqlite3_format_query_result()` Interface
Invoke the `sqlite3_format_query_result(P,S,E)` interface to run
the prepared statement P and format its results according to the
specification found in S. The sqlite3_format_query_result() function
will return an SQLite result code, usually SQLITE_OK, but perhaps
SQLITE_NOMEM or SQLITE_ERROR or similar. If an error occurs and if
the E parameter is not NULL, then error message text might be written
into *E. Any error message text will be stored in memory obtained
from sqlite3_malloc() and it is the responsibility of the caller to
free that memory by a subsequent call to sqlite3_free().
## 4.0 Output Styles
The result formatter supports a variety of output styles. The
output style used is determined by the eStyle setting of the
sqlite3_qrf_spec object. The set of supported output modes
might increase in future versions.
The following output modes are currently defined:
> ~~~
#define QRF_STYLE_Auto 0 /* Choose a style automatically */
#define QRF_STYLE_Box 1 /* Unicode box-drawing characters */
#define QRF_STYLE_Column 2 /* One record per line in neat columns */
#define QRF_STYLE_Count 3 /* Output only a count of the rows of output */
#define QRF_STYLE_Csv 4 /* Comma-separated-value */
#define QRF_STYLE_Eqp 5 /* Format EXPLAIN QUERY PLAN output */
#define QRF_STYLE_Explain 6 /* EXPLAIN output */
#define QRF_STYLE_Html 7 /* Generate an XHTML table */
#define QRF_STYLE_Insert 8 /* Generate SQL "insert" statements */
#define QRF_STYLE_Json 9 /* Output is a list of JSON objects */
#define QRF_STYLE_JObject 10 /* Independent JSON objects for each row */
#define QRF_STYLE_Line 11 /* One column per line. */
#define QRF_STYLE_List 12 /* One record per line with a separator */
#define QRF_STYLE_Markdown 13 /* Markdown formatting */
#define QRF_STYLE_Off 14 /* No query output shown */
#define QRF_STYLE_Quote 15 /* SQL-quoted, comma-separated */
#define QRF_STYLE_Stats 16 /* EQP-like output but with performance stats */
#define QRF_STYLE_StatsEst 17 /* EQP-like output with planner estimates */
#define QRF_STYLE_StatsVm 18 /* EXPLAIN-like output with performance stats */
#define QRF_STYLE_Table 19 /* MySQL-style table formatting */
~~~
In the following subsections, these styles will often be referred
to without the "QRF_STYLE_" prefix.
### 4.1 Default Style (Auto)
The **Auto** style means QRF gets to choose an appropriate output
style. It will usually choose **Box**, but might also pick one of
**Explain** or **Eqp** if the `sqlite3_stmt_explain()` function
returns 1 or 2, respectively.
### 4.2 Columnar Styles (Box, Column, Markdown, Table)
The **Box**, **Column**, **Markdown**, and **Table**
modes are columnar. This means the output is arranged into neat,
uniform-width columns. These styles can use more memory, especially when
the query result has many rows, because they need to load the entire output
into memory first in order to determine how wide to make each column.
The nWidth, aWidth, and mxWidth fields of the `sqlite3_qrf_spec` object
are used by these styles only, and are ignored by all other styles.
The zRowSep and zColumnSep settings are ignored by these styles. The
bTitles setting is honored by these styles; it defaults to QRF_SW_On.
The **Box** style uses Unicode box-drawing character to draw a grid
of columns and rows to show the result. The **Table** is the same,
except that it uses ASCII-art rather than Unicode box-drawing characters
to draw the grid. The **Column** arranges the results in neat columns
but does not draw in column or row separator, except that it does draw
lines horizontal lines using "`-`" characters to separate the column names
from the data below. This is very similar to default output styling in
psql. The **Markdown** renders its result in the
Markdown table format.
### 4.3 Line-oriented Styles
The line-oriented styles output each row of result as it is received from
the prepared statement.
The **List** style is the most familiar line-oriented output format.
The **List** style shows output columns for each row on the
same line, each separated by a single "`|`" character and with lines
terminated by a single newline (\\u000a or \\n). These column
and row separator choices can be overridden using the zColumnSep
and zRowSep fields of the `sqlite3_qrf_spec` structure. The text
formatting is QRF_TEXT_Plain, and BLOB encoding is QRF_BLOB_Text. So
characters appear in the output exactly as they appear in the database.
Except the eEsp mode defaults to `QRF_ESC_On`, so that control
characters are escaped, for safety.
The **Csv** and **Quote** styles are simply variations on **List**
with hard-coded values for some of the sqlite3_qrf_spec settings:
<table border=1 cellpadding=2 cellspacing=0>
<tr><th>&nbsp;<th>Quote<th>Csv
<tr><td>zColumnSep<td>","<td>","
<tr><td>zRowSep<td>"\\n"<td>"\\r\\n"
<tr><td>zNull<td>"NULL"<td>""
<tr><td>eText<td>QRF_TEXT_Sql<td>QRF_TEXT_Csv
<tr><td>eBlob<td>QRF_BLOB_Sql<td>QRF_BLOB_Text
</table>
The **Html** style generates HTML table content, just without
the `<TABLE>..</TABLE>` around the outside.
The **Insert** style generates a series of SQL "INSERT" statements
that will inserts the data that is output into a table whose name is defined
by the zTableName field of `sqlite3_qrf_spec`. If zTableName is NULL,
then a substitute name is used.
The **Json** and **JObject** styles generates JSON text for the query result.
The **Json** style produces a JSON array of structures with one
structure per row. **JObject** outputs independent JSON objects, one per
row, with each structure on a separate line all by itself, and not
part of a larger array. In both cases, the labels on the elements of the
JSON objects are taken from the column names of the SQL query. So if
you have an SQL query that has two or more output columns with the same
name, you will end up with JSON structures that have duplicate elements.
Finally, the **Line** style paints each column of a row on a
separate line with the column name on the left and a "`=`" separating the
column name from its value. A single blank line appears between rows.
### 4.4 EXPLAIN Styles (Eqp, Explain)
The **Eqp** and **Explain** styles format output for
EXPLAIN QUERY PLAN and EXPLAIN statements, respectively. If the input
statement is not already an EXPLAIN QUERY PLAN or EXPLAIN statement is
is temporarily converted for the duration of the rendering, but
is converted back before `sqlite3_format_query_result()` returns.
### 4.5 ScanStatus Styles (Stats, StatsEst, StatsVm)
The **Stats**, **StatsEst**, and **StatsVm** styles are similar to **Eqp**
and **Explain** except that they include profiling information
from prior executions of the input prepared statement.
These modes only work if SQLite has been compiled with
-DSQLITE_ENABLE_STMT_SCANSTATUS and if the SQLITE_DBCONFIG_STMT_SCANSTATUS
is enabled for the database connection. The **StatsVm** style
also requires the bytecode() virtual table which is enabled using
the -DSQLITE_ENABLE_BYTECODE_VTAB compile-time option.
### 4.6 Other Styles (Count, Off)
The **Count** style discards all query results and returns
a count of the number of rows of output at the end. The **Off**
style is completely silent; it generates no output. These corner-case
modes are sometimes useful for debugging.
### 5.0 Source Code Files
The SQLite Query Result Formatter is implemented in three source code files:
* `qrf.c` &rarr; The implementation, written in portable C99
* `qrf.h` &rarr; A header file defining interfaces
* `README.md` &rarr; This documentation, in Markdown
To use the SQLite result formatter, include the "`qrf.h`" header file
and link the application against the "`qrf.c`" source file.
+2496
View File
File diff suppressed because it is too large Load Diff
+184
View File
@@ -0,0 +1,184 @@
/*
** 2025-10-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.
**
*************************************************************************
** Header file for the Result-Format or "resfmt" utility library for SQLite.
** See the resfmt.md documentation for additional information.
*/
#ifndef SQLITE_QRF_H
#define SQLITE_QRF_H
#include <stdlib.h>
#include "sqlite3.h"
/*
** Specification used by clients to define the output format they want
*/
typedef struct sqlite3_qrf_spec sqlite3_qrf_spec;
struct sqlite3_qrf_spec {
unsigned char iVersion; /* Version number of this structure */
unsigned char eStyle; /* Formatting style. "box", "csv", etc... */
unsigned char eEsc; /* How to escape control characters in text */
unsigned char eText; /* Quoting style for text */
unsigned char eTitle; /* Quating style for the text of column names */
unsigned char eBlob; /* Quoting style for BLOBs */
unsigned char bTitles; /* True to show column names */
unsigned char bWordWrap; /* Try to wrap on word boundaries */
unsigned char bTextJsonb; /* Render JSONB blobs as JSON text */
unsigned char bTextNull; /* Apply eText encoding to zNull[] */
unsigned char eDfltAlign; /* Default alignment, no covered by aAlignment */
unsigned char eTitleAlign; /* Alignment for column headers */
short int nWrap; /* Wrap columns wider than this */
short int nScreenWidth; /* Maximum overall table width */
short int nLineLimit; /* Maximum number of lines for any row */
int nCharLimit; /* Maximum number of characters in a cell */
int nWidth; /* Number of entries in aWidth[] */
int nAlign; /* Number of entries in aAlignment[] */
short int *aWidth; /* Column widths */
unsigned char *aAlign; /* Column alignments */
char *zColumnSep; /* Alternative column separator */
char *zRowSep; /* Alternative row separator */
char *zTableName; /* Output table name */
char *zNull; /* Rendering of NULL */
char *(*xRender)(void*,sqlite3_value*); /* Render a value */
int (*xWrite)(void*,const char*,sqlite3_int64); /* Write output */
void *pRenderArg; /* First argument to the xRender callback */
void *pWriteArg; /* First argument to the xWrite callback */
char **pzOutput; /* Storage location for output string */
/* Additional fields may be added in the future */
};
/*
** Interfaces
*/
int sqlite3_format_query_result(
sqlite3_stmt *pStmt, /* SQL statement to run */
const sqlite3_qrf_spec *pSpec, /* Result format specification */
char **pzErr /* OUT: Write error message here */
);
/*
** Range of values for sqlite3_qrf_spec.aWidth[] entries and for
** sqlite3_qrf_spec.mxColWidth and .nScreenWidth
*/
#define QRF_MAX_WIDTH 10000
#define QRF_MIN_WIDTH 0
/*
** Output styles:
*/
#define QRF_STYLE_Auto 0 /* Choose a style automatically */
#define QRF_STYLE_Box 1 /* Unicode box-drawing characters */
#define QRF_STYLE_Column 2 /* One record per line in neat columns */
#define QRF_STYLE_Count 3 /* Output only a count of the rows of output */
#define QRF_STYLE_Csv 4 /* Comma-separated-value */
#define QRF_STYLE_Eqp 5 /* Format EXPLAIN QUERY PLAN output */
#define QRF_STYLE_Explain 6 /* EXPLAIN output */
#define QRF_STYLE_Html 7 /* Generate an XHTML table */
#define QRF_STYLE_Insert 8 /* Generate SQL "insert" statements */
#define QRF_STYLE_Json 9 /* Output is a list of JSON objects */
#define QRF_STYLE_JObject 10 /* Independent JSON objects for each row */
#define QRF_STYLE_Line 11 /* One column per line. */
#define QRF_STYLE_List 12 /* One record per line with a separator */
#define QRF_STYLE_Markdown 13 /* Markdown formatting */
#define QRF_STYLE_Off 14 /* No query output shown */
#define QRF_STYLE_Quote 15 /* SQL-quoted, comma-separated */
#define QRF_STYLE_Stats 16 /* EQP-like output but with performance stats */
#define QRF_STYLE_StatsEst 17 /* EQP-like output with planner estimates */
#define QRF_STYLE_StatsVm 18 /* EXPLAIN-like output with performance stats */
#define QRF_STYLE_Table 19 /* MySQL-style table formatting */
/*
** Quoting styles for text.
** Allowed values for sqlite3_qrf_spec.eText
*/
#define QRF_TEXT_Auto 0 /* Choose text encoding automatically */
#define QRF_TEXT_Plain 1 /* Literal text */
#define QRF_TEXT_Sql 2 /* Quote as an SQL literal */
#define QRF_TEXT_Csv 3 /* CSV-style quoting */
#define QRF_TEXT_Html 4 /* HTML-style quoting */
#define QRF_TEXT_Tcl 5 /* C/Tcl quoting */
#define QRF_TEXT_Json 6 /* JSON quoting */
/*
** Quoting styles for BLOBs
** Allowed values for sqlite3_qrf_spec.eBlob
*/
#define QRF_BLOB_Auto 0 /* Determine BLOB quoting using eText */
#define QRF_BLOB_Text 1 /* Display content exactly as it is */
#define QRF_BLOB_Sql 2 /* Quote as an SQL literal */
#define QRF_BLOB_Hex 3 /* Hexadecimal representation */
#define QRF_BLOB_Tcl 4 /* "\000" notation */
#define QRF_BLOB_Json 5 /* A JSON string */
/*
** Control-character escape modes.
** Allowed values for sqlite3_qrf_spec.eEsc
*/
#define QRF_ESC_Auto 0 /* Choose the ctrl-char escape automatically */
#define QRF_ESC_Off 1 /* Do not escape control characters */
#define QRF_ESC_Ascii 2 /* Unix-style escapes. Ex: U+0007 shows ^G */
#define QRF_ESC_Symbol 3 /* Unicode escapes. Ex: U+0007 shows U+2407 */
/*
** Allowed values for "boolean" fields, such as "bColumnNames", "bWordWrap",
** and "bTextJsonb". There is an extra "auto" variants so these are actually
** tri-state settings, not booleans.
*/
#define QRF_SW_Auto 0 /* Let QRF choose the best value */
#define QRF_SW_Off 1 /* This setting is forced off */
#define QRF_SW_On 2 /* This setting is forced on */
#define QRF_Auto 0 /* Alternate spelling for QRF_*_Auto */
#define QRF_No 1 /* Alternate spelling for QRF_SW_Off */
#define QRF_Yes 2 /* Alternate spelling for QRF_SW_On */
/*
** Possible alignment values alignment settings
**
** Horizontal Vertial
** ---------- -------- */
#define QRF_ALIGN_Auto 0 /* auto auto */
#define QRF_ALIGN_Left 1 /* left auto */
#define QRF_ALIGN_Center 2 /* center auto */
#define QRF_ALIGN_Right 3 /* right auto */
#define QRF_ALIGN_Top 4 /* auto top */
#define QRF_ALIGN_NW 5 /* left top */
#define QRF_ALIGN_N 6 /* center top */
#define QRF_ALIGN_NE 7 /* right top */
#define QRF_ALIGN_Middle 8 /* auto middle */
#define QRF_ALIGN_W 9 /* left middle */
#define QRF_ALIGN_C 10 /* center middle */
#define QRF_ALIGN_E 11 /* right middle */
#define QRF_ALIGN_Bottom 12 /* auto bottom */
#define QRF_ALIGN_SW 13 /* left bottom */
#define QRF_ALIGN_S 14 /* center bottom */
#define QRF_ALIGN_SE 15 /* right bottom */
#define QRF_ALIGN_HMASK 3 /* Horizontal alignment mask */
#define QRF_ALIGN_VMASK 12 /* Vertical alignment mask */
/*
** Auxiliary routines contined within this module that might be useful
** in other contexts, and which are therefore exported.
*/
/*
** Return an estimate of the width, in columns, for the single Unicode
** character c. For normal characters, the answer is always 1. But the
** estimate might be 0 or 2 for zero-width and double-width characters.
**
** Different display devices display unicode using different widths. So
** it is impossible to know that true display width with 100% accuracy.
** Inaccuracies in the width estimates might cause columns to be misaligned.
** Unfortunately, there is nothing we can do about that.
*/
int sqlite3_qrf_wcwidth(int c);
#endif /* !defined(SQLITE_QRF_H) */
+25 -45
View File
@@ -219,8 +219,9 @@ b.cp = $(call b.mkdir@); \
# $4 = optional $(bin.c-pp) flags
define b.c-pp.shcmd
$(call b.mkdir@); \
$(call b.echo,$(1),$(emo.disk)$(emo.lock)[$(3)] $(4)); \
rm -f $(3); $(bin.c-pp) -o $(3) $(4) $(2) || exit; \
$(call b.echo,$(1),$(emo.disk)$(emo.lock) $(bin.c-pp) $(4) $(if $(loud.if),$(2))); \
rm -f $(3); \
$(bin.c-pp) -o $(3) $(4) $(2) || exit; \
chmod -w $(3)
endef
@@ -232,8 +233,7 @@ endef
# Args: as for $(b.c-pp.shcmd).
define b.c-pp.target
$(3): $$(MAKEFILE_LIST) $$(bin.c-pp) $(2)
@$$(call b.mkdir@)
@$$(call b.c-pp.shcmd,$(1),$(2),$(3),$(4) $$(b.c-pp.target.flags))
@$$(call b.c-pp.shcmd,$(1),$(2),$(3),$(4) $(b.c-pp.target.flags))
CLEAN_FILES += $(3)
endef
@@ -260,7 +260,7 @@ endef
#
# To repeat: all node.js builds are 100% untested and unsupported.
#
# Most c-pp.D.X are set via $(bin.mkwb) and X is a build name.
# Most c-pp.D.X are set via $(bin.mkwasmbuilds) and X is a build name.
# Those make rules reference c-pp.D.64bit, so it should be defined in
# advance.
#
@@ -282,20 +282,9 @@ c-pp.D.64bit = -Dbits64
# This is intended to be used in makefile targets which generate an
# Emscripten module and where $@ is the module's .js/.mjs file.
#
# This is inherently fragile and has been broken by Emscripten updates
# before.
#
ifeq (1,1)
define b.strip-js-emcc-bindings
echo "$(1) $(emo.garbage) Stripping export wrappers."; \
sed -i -e '/var _sqlite3.*makeInvalidEarly.*;/d' \
-e '/assert.*sqlite3.*missing.*;/d' \
-e '/_sqlite.*createExportWrapper.*;/d' $@
endef
else
b.strip-js-emcc-bindings = \
echo '$(1) $(emo.bug) (disabled because it breaks emsdk 4.0.16+)'
endif
#
# Set up sqlite3.c and sqlite3.h...
@@ -861,8 +850,9 @@ endef
#
sqlite3-license-version.js = $(dir.tmp)/sqlite3-license-version.js
$(sqlite3-license-version.js): $(bin.version-info) \
$(dir.api)/sqlite3-license-version-header.js $(MAKEFILE)
@$(call b.mkdir@); echo '$(logtag.@) $(emo.disk)'; { \
$(dir.api)/sqlite3-license-version-header.js
@echo '$(logtag.@) $(emo.disk)'; \
$(call b.mkdir@); { \
cat $(dir.api)/sqlite3-license-version-header.js || exit $$?; \
echo '/*'; \
echo '** This code was built from sqlite3 version...'; \
@@ -870,7 +860,7 @@ $(sqlite3-license-version.js): $(bin.version-info) \
awk '/define SQLITE_VERSION/{$$1=""; print "**" $$0}' $(sqlite3.h); \
awk '/define SQLITE_SOURCE_ID/{$$1=""; print "**" $$0}' $(sqlite3.h); \
echo '**'; echo '** Emscripten SDK: $(emcc.version)'; \
echo '**'; echo '** Preprocessed by:'; echo '** @c-pp::ARGV@'; \
echo '**'; \
echo '*/'; \
} > $@
@@ -896,10 +886,10 @@ $(sqlite3-api-build-version.js): $(bin.version-info) $(MAKEFILE)
# sqlite3-api.js, in the order they need to be assembled.
sqlite3-api.jses = $(sqlite3-license-version.js)
sqlite3-api.jses += $(dir.api)/sqlite3-api-prologue.js
sqlite3-api.jses += $(sqlite3-api-build-version.js)
sqlite3-api.jses += $(dir.common)/whwasmutil.js
sqlite3-api.jses += $(dir.jacc)/jaccwabyt.js
sqlite3-api.jses += $(dir.api)/sqlite3-api-glue.c-pp.js
sqlite3-api.jses += $(sqlite3-api-build-version.js)
sqlite3-api.jses += $(dir.api)/sqlite3-api-oo1.c-pp.js
sqlite3-api.jses += $(dir.api)/sqlite3-api-worker1.c-pp.js
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-helper.c-pp.js
@@ -908,13 +898,7 @@ ifeq (0,$(wasm-bare-bones))
endif
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs.c-pp.js
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs-sahpool.c-pp.js
# Parallel builds can fail if $(sqlite3-license-version.js) is not
# created early enough, so make all files in $(sqlite-api.jses) except
# for $(sqlite3-license-version.js) depend on
# $(sqlite3-license-version.js).
deps.jses = $(filter-out $(sqlite3-license-version.js),$(sqlite3-api.jses))
$(deps.jses): $(sqlite3-license-version.js)
sqlite3-api.jses += $(dir.api)/sqlite3-api-cleanup.js
#
# extern-post-js* and extern-pre-js* are files for use with
@@ -953,7 +937,7 @@ $(post-js.in.js): $(MKDIR.bld) $(post-jses.js) $(MAKEFILE)
done > $@
#
# speedtest1 decls needed before the $(bin.mkwb)-generated makefile
# speedtest1 decls needed before the $(bin.mkws)-generated makefile
# is included.
#
bin.speedtest1 = ../../speedtest1
@@ -994,7 +978,7 @@ endif
EXPORTED_FUNCTIONS.fiddle = $(dir.tmp)/EXPORTED_FUNCTIONS.fiddle
$(EXPORTED_FUNCTIONS.fiddle): $(EXPORTED_FUNCTIONS.api.in) $(MAKEFILE_LIST)
@$(call b.mkdir@)
@$(b.mkdir@)
@$(call b.c-pp.shcmd,filter,$(EXPORTED_FUNCTIONS.api.in),\
$@,$(EXPORTED_FUNCTIONS.c-pp.flags) -Dfiddle)
@echo $(logtag.@) $(emo.disk)
@@ -1122,10 +1106,10 @@ sqlite3.ext.js =
define gen-worker1
# $1 = X.ext part of sqlite3-worker1X.ext
# $2 = $(c-pp.D.NAME)
$(call b.c-pp.target,filter,$$(dir.api)/sqlite3-worker1.c-pp.js,\
$$(dir.dout)/sqlite3-worker1$(1),$(2))
sqlite3.ext.js += $$(dir.dout)/sqlite3-worker1$(1)
all: $$(dir.dout)/sqlite3-worker1$(1)
$(call b.c-pp.target,filter,$(dir.api)/sqlite3-worker1.c-pp.js,\
$(dir.dout)/sqlite3-worker1$(1),$(2))
sqlite3.ext.js += $(dir.dout)/sqlite3-worker1$(1)
all: $(dir.dout)/sqlite3-worker1$(1)
endef
$(eval $(call gen-worker1,.js,$(c-pp.D.vanilla)))
@@ -1139,10 +1123,10 @@ $(eval $(call gen-worker1,-bundler-friendly.mjs,$(c-pp.D.bundler)))
define gen-promiser
# $1 = X.ext part of sqlite3-worker1-promiserX.ext
# $2 = $(c-pp.D.NAME)
$(call b.c-pp.target,filter,$$(dir.api)/sqlite3-worker1-promiser.c-pp.js,\
$$(dir.dout)/sqlite3-worker1-promiser$(1),$(2))
sqlite3.ext.js += $$(dir.dout)/sqlite3-worker1-promiser$(1)
all: $$(dir.dout)/sqlite3-worker1-promiser$(1)
$(call b.c-pp.target,filter,$(dir.api)/sqlite3-worker1-promiser.c-pp.js,\
$(dir.dout)/sqlite3-worker1-promiser$(1),$(2))
sqlite3.ext.js += $(dir.dout)/sqlite3-worker1-promiser$(1)
all: $(dir.dout)/sqlite3-worker1-promiser$(1)
endef
$(eval $(call gen-promiser,.js,$(c-pp.D.vanilla)))
@@ -1184,19 +1168,14 @@ $(dir.dout)/sqlite3-opfs-async-proxy.js: $(dir.api)/sqlite3-opfs-async-proxy.js
# we don't otherwise have a great place to attach them such that
# they're always copied when we need them.
#
# The var $(out.$(B).js) comes from $(bin.mkwb) and $(B) is the name
# of a build set up by that tool, e.g. b-vanilla or b-esm64.
# The var $(out.$(B).js) comes from $(bin.mkwasmbuilds).
#
$(foreach B,$(b.names),$(eval $(out.$(B).js): $(sqlite3.ext.js)))
#
# b-all: builds all available js/wasm builds.
#
$(foreach B,$(b.names),$(eval b-all: $(out.$(B).js)))
#$(foreach B,$(b.names),$(eval pre: $(pre-js.$(B).js)))
$(foreach B,$(b.names),$(eval post: $(post-js.$(B).js)))
#
# speedtest1 is our primary benchmarking tool.
#
@@ -1246,7 +1225,8 @@ speedtest1.exit-runtime1 = -sEXIT_RUNTIME=1
# which runs speedtest1 multiple times.
$(EXPORTED_FUNCTIONS.speedtest1): $(EXPORTED_FUNCTIONS.api)
@$(call b.mkdir@); $(call b.echo,@,$(emo.disk)); \
@$(call b.echo,@,$(emo.disk)); \
$(call b.mkdir@); \
{ echo _wasm_main; cat $(EXPORTED_FUNCTIONS.api); } > $@ || exit
speedtest1: b-speedtest1
+36 -39
View File
@@ -60,14 +60,14 @@ maintenance point of view.
At the center of the onion is `sqlite3-api.js`, which gets generated
by concatenating the following files together in their listed order:
- **`sqlite3-api-prologue.js`**
- **`sqlite3-api-prologue.js`**\
Contains the initial bootstrap setup of the sqlite3 API
objects. This is exposed as a function, rather than objects, so that
the next step can pass in a config object which abstracts away parts
of the WASM environment, to facilitate plugging it in to arbitrary
WASM toolchains. The bootstrapping function gets removed from the
global scope in a later stage of the bootstrapping process.
- **`../common/whwasmutil.js`**
- **`../common/whwasmutil.js`**\
A semi-third-party collection of JS/WASM utility code intended to
replace much of the Emscripten glue. The sqlite3 APIs internally use
these APIs instead of their Emscripten counterparts, in order to be
@@ -77,78 +77,79 @@ by concatenating the following files together in their listed order:
toolchains. It is "semi-third-party" in that it was created in order
to support this tree but is standalone and maintained together
with...
- **`../jaccwabyt/jaccwabyt.js`**
- **`../jaccwabyt/jaccwabyt.js`**\
Another semi-third-party API which creates bindings between JS
and C structs, such that changes to the struct state from either JS
or C are visible to the other end of the connection. This is also an
independent spinoff project, conceived for the sqlite3 project but
maintained separately.
- **`sqlite3-api-glue.js`**
- **`sqlite3-api-glue.js`**\
Invokes functionality exposed by the previous two files to flesh out
low-level parts of `sqlite3-api-prologue.js`. Most of these pieces
involve populating the `sqlite3.capi.wasm` object and creating
`sqlite3.capi.sqlite3_...()` bindings. This file also deletes most
global-scope symbols the above files create, effectively moving them
into the scope being used for initializing the API.
- **`<build>/sqlite3-api-build-version.js`**
- **`<build>/sqlite3-api-build-version.js`**\
Gets created by the build process and populates the
`sqlite3.version` object. This part is not critical, but records the
version of the library against which this module was built.
- **`sqlite3-api-oo1.js`**
- **`sqlite3-api-oo1.js`**\
Provides a high-level object-oriented wrapper to the lower-level C
API, colloquially known as OO API #1. Its API is similar to other
high-level sqlite3 JS wrappers and should feel relatively familiar
to anyone familiar with such APIs. It is not a "required component"
and can be elided from builds which do not want it.
- **`sqlite3-api-worker1.js`**
- **`sqlite3-api-worker1.js`**\
A Worker-thread-based API which uses OO API #1 to provide an
interface to a database which can be driven from the main Window
thread via the Worker message-passing interface. Like OO API #1,
this is an optional component, offering one of any number of
potential implementations for such an API.
- **`sqlite3-worker1.js`**
- **`sqlite3-worker1.js`**\
Is not part of the amalgamated sources and is intended to be
loaded by a client Worker thread. It loads the sqlite3 module
and runs the Worker #1 API which is implemented in
`sqlite3-api-worker1.js`.
- **`sqlite3-worker1-promiser.js`**
- **`sqlite3-worker1-promiser.js`**\
Is likewise not part of the amalgamated sources and provides
a Promise-based interface into the Worker #1 API. This is
a far user-friendlier way to interface with databases running
in a Worker thread.
- **`sqlite3-vfs-helper.js`**
- **`sqlite3-vfs-helper.js`**\
Installs the `sqlite3.vfs` namespace, which contain helpers for use
by downstream code which creates `sqlite3_vfs` implementations.
- **`sqlite3-vtab-helper.js`**
- **`sqlite3-vtab-helper.js`**\
Installs the `sqlite3.vtab` namespace, which contain helpers for use
by downstream code which creates `sqlite3_module` implementations.
- **`sqlite3-vfs-opfs.c-pp.js`**
- **`sqlite3-vfs-opfs.c-pp.js`**\
is an sqlite3 VFS implementation which supports the [Origin-Private
FileSystem (OPFS)][OPFS] as a storage layer to provide persistent
storage for database files in a browser. It requires...
- **`sqlite3-opfs-async-proxy.js`**
- **`sqlite3-opfs-async-proxy.js`**\
is the asynchronous backend part of the [OPFS][] proxy. It
speaks directly to the (async) OPFS API and channels those
results back to its synchronous counterpart. This file, because
it must be started in its own Worker, is not part of the
amalgamation.
- **`sqlite3-vfs-opfs-sahpool.c-pp.js`**
- **`sqlite3-vfs-opfs-sahpool.c-pp.js`**\
is another sqlite3 VFS supporting the [OPFS][], but uses a
completely different approach than the above-listed one.
- **`sqlite3-api-cleanup.js`**\
The previous files do not immediately extend the library. Instead
they add callback functions to be called during its
bootstrapping. Some also temporarily create global objects in order
to communicate their state to the files which follow them. This file
cleans up any dangling globals and runs the API bootstrapping
process, which is what finally executes the initialization code
installed by the previous files. As of this writing, this code
ensures that the previous files leave no more than a single global
symbol installed - `sqlite3InitModule()`. When adapting the API for
non-Emscripten toolchains, this "should" be the only file, of those
in this list, where changes are needed. The Emscripten-specific
pieces described below may also require counterparts in any as-yet
hypothetical alternative build.
The previous files do not immediately extend the library. Instead they
install a global function `sqlite3ApiBootstrap()`, which downstream
code must call to configure the library for the current JS/WASM
environment. Each file listed above pushes a callback into the
bootstrapping queue, to be called as part of `sqlite3ApiBootstrap()`.
Some files also temporarily create global objects in order to
communicate their state to the files which follow them. Those
get cleaned up vi `post-js-footer.js`, described below.
Adapting the build for non-Emscripten toolchains essentially requires packaging
the above files, concatated together, into that toolchain's "JS glue"
and, in the final stage of that glue, call `sqlite3ApiBootstrap()` and
return its result to the end user.
**Files with the extension `.c-pp.js`** are intended [to be processed
with `c-pp`](#c-pp), noting that such preprocessing may be applied
@@ -170,27 +171,23 @@ from this file rather than `sqlite3.c`.
The following Emscripten-specific files are injected into the
build-generated `sqlite3.js` along with `sqlite3-api.js`.
- **`extern-pre-js.js`**
- **`extern-pre-js.js`**\
Emscripten-specific header for Emscripten's `--extern-pre-js`
flag. As of this writing, that file is only used for experimentation
purposes and holds no code relevant to the production deliverables.
- **`pre-js.c-pp.js`**
- **`pre-js.c-pp.js`**\
Emscripten-specific header for Emscripten's `--pre-js` flag. This
file overrides certain Emscripten behavior before Emscripten does
most of its work.
- **`post-js-header.js`**
- **`post-js-header.js`**\
Emscripten-specific header for the `--post-js` input. It opens up,
but does not close, a function used for initializing the library.
- **`sqlite3-api.js`** gets sandwiched between these &uarr; two
&darr; files.
- **`post-js-footer.js`**
- (**`sqlite3-api.js`** gets sandwiched between these &uarr; two
&darr; files.)
- **`post-js-footer.js`**\
Emscripten-specific footer for the `--post-js` input. This closes
off the function opened by `post-js-header.js`. This file cleans up
any dangling globals and runs `sqlite3ApiBootstrap()`. As of this
writing, this code ensures that the previous files leave no more
than a single global symbol installed - `sqlite3InitModule()`.
- **`extern-post-js.c-pp.js`**
off the function opened by `post-js-header.js`.
- **`extern-post-js.c-pp.js`**\
Emscripten-specific header for Emscripten's `--extern-post-js`
flag. This file is run in the global scope. It overwrites the
Emscripten-installed `sqlite3InitModule()` function with one which
-69
View File
@@ -1,72 +1,3 @@
/*
2022-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 the tail end of the sqlite3-api.js constellation,
closing the function scope opened by post-js-header.js.
In terms of amalgamation code placement, this file is appended
immediately after the final sqlite3-api-*.js piece. Those files
cooperate to prepare sqlite3ApiBootstrap() and this file calls it.
It is run within a context which gives it access to Emscripten's
Module object, after sqlite3.wasm is loaded but before
sqlite3ApiBootstrap() has been called.
Because this code resides (after building) inside the function
installed by post-js-header.js, it has access to state set up by
pre-js.c-pp.js and friends.
*/
try{
/* We are in the closing block of Module.runSQLite3PostLoadInit(), so
its arguments are visible here. */
/* Config options for sqlite3ApiBootstrap(). */
const bootstrapConfig = Object.assign(
Object.create(null),
/** The WASM-environment-dependent configuration for sqlite3ApiBootstrap() */
{
memory: ('undefined'!==typeof wasmMemory)
? wasmMemory
: EmscriptenModule['wasmMemory'],
exports: ('undefined'!==typeof wasmExports)
? wasmExports /* emscripten >=3.1.44 */
: (Object.prototype.hasOwnProperty.call(EmscriptenModule,'wasmExports')
? EmscriptenModule['wasmExports']
: EmscriptenModule['asm']/* emscripten <=3.1.43 */)
},
globalThis.sqlite3ApiBootstrap.defaultConfig, // default options
globalThis.sqlite3ApiConfig || {} // optional client-provided options
);
sqlite3InitScriptInfo.debugModule("Bootstrapping lib config", bootstrapConfig);
/**
For purposes of the Emscripten build, call sqlite3ApiBootstrap().
Ideally clients should be able to inject their own config here,
but that's not practical in this particular build constellation
because of the order everything happens in. Clients may either
define globalThis.sqlite3ApiConfig or modify
globalThis.sqlite3ApiBootstrap.defaultConfig to tweak the default
configuration used by a no-args call to sqlite3ApiBootstrap(),
but must have first loaded their WASM module in order to be able
to provide the necessary configuration state.
*/
const p = globalThis.sqlite3ApiBootstrap(bootstrapConfig);
delete globalThis.sqlite3ApiBootstrap;
return p /* the eventual result of globalThis.sqlite3InitModule() */;
}catch(e){
console.error("sqlite3ApiBootstrap() error:",e);
throw e;
}
//console.warn("This is the end of the Module.runSQLite3PostLoadInit handler.");
}/*Module.runSQLite3PostLoadInit(...)*/;
//console.warn("This is the end of the setup of the (pending) Module.runSQLite3PostLoadInit");
+6 -4
View File
@@ -3,13 +3,14 @@
post-js.js for use with Emscripten's --post-js flag, so it gets
injected in the earliest stages of sqlite3InitModule().
This function wraps the whole SQLite3 library but does not
bootstrap it.
Running this function will bootstrap the library and return
a Promise to the sqlite3 namespace object.
In the canonical builds, this gets called by extern-post-js.c-pp.js
*/
Module.runSQLite3PostLoadInit = async function(
sqlite3InitScriptInfo,
Module.runSQLite3PostLoadInit = function(
sqlite3InitScriptInfo /* populated by extern-post-js.c-pp.js */,
EmscriptenModule/*the Emscripten-style module object*/,
sqlite3IsUnderTest
){
@@ -34,6 +35,7 @@ Module.runSQLite3PostLoadInit = async function(
- sqlite3-vtab-helper.c-pp.js => Utilities for virtual table impls
- sqlite3-vfs-opfs.c-pp.js => OPFS VFS
- sqlite3-vfs-opfs-sahpool.c-pp.js => OPFS SAHPool VFS
- sqlite3-api-cleanup.js => final bootstrapping phase
- post-js-footer.js => this file's epilogue
And all of that gets sandwiched between extern-pre-js.js and
+6 -8
View File
@@ -35,12 +35,8 @@
const sIMS =
globalThis.sqlite3InitModuleState/*from extern-post-js.c-pp.js*/
|| Object.assign(Object.create(null),{
/* In WASMFS builds this file gets loaded once per thread,
but sqlite3InitModuleState is not getting set for the
worker threads? That those workers seem to function fine
despite that is curious. */
debugModule: function(){
console.warn("globalThis.sqlite3InitModuleState is missing",arguments);
debugModule: ()=>{
console.warn("globalThis.sqlite3InitModuleState is missing");
}
});
delete globalThis.sqlite3InitModuleState;
@@ -93,7 +89,8 @@
//#endif target:es6-module
}.bind(sIMS);
//#if Module.instantiateWasm and not wasmfs
//#if Module.instantiateWasm
//#if not wasmfs
/**
Override Module.instantiateWasm().
@@ -129,6 +126,7 @@
.then(finalThen)
return loadWasm();
}.bind(sIMS);
//#endif Module.instantiateWasm and not wasmfs
//#endif not wasmfs
//#endif Module.instantiateWasm
})(Module);
/* END FILE: api/pre-js.js. */
+75
View File
@@ -0,0 +1,75 @@
/*
2022-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 the tail end of the sqlite3-api.js constellation,
intended to be appended after all other sqlite3-api-*.js files so
that it can finalize any setup and clean up any global symbols
temporarily used for setting up the API's various subsystems.
In Emscripten builds it's run in the context of what amounts to a
Module.postRun handler, though it's no longer actually a postRun
handler because Emscripten 4.0 changed postRun semantics in an
incompatible way.
In terms of amalgamation code placement, this file is appended
immediately after the final sqlite3-api-*.js piece. Those files
cooperate to prepare sqlite3ApiBootstrap() and this file calls it.
It is run within a context which gives it access to Emscripten's
Module object, after sqlite3.wasm is loaded but before
sqlite3ApiBootstrap() has been called.
Because this code resides (after building) inside the function
installed by post-js-header.js, it has access to state set up by
pre-js.c-pp.js and friends.
*/
'use strict';
if( 'undefined' !== typeof EmscriptenModule/*from post-js-header.js*/ ){
try{
/* Config options for sqlite3ApiBootstrap(). */
const bootstrapConfig = Object.assign(
Object.create(null),
globalThis.sqlite3ApiBootstrap.defaultConfig, // default options
globalThis.sqlite3ApiConfig || {}, // optional client-provided options
/** The WASM-environment-dependent configuration for sqlite3ApiBootstrap() */
{
memory: ('undefined'!==typeof wasmMemory)
? wasmMemory
: EmscriptenModule['wasmMemory'],
exports: ('undefined'!==typeof wasmExports)
? wasmExports /* emscripten >=3.1.44 */
: (Object.prototype.hasOwnProperty.call(EmscriptenModule,'wasmExports')
? EmscriptenModule['wasmExports']
: EmscriptenModule['asm']/* emscripten <=3.1.43 */)
}
);
sqlite3InitScriptInfo.debugModule("Bootstrapping lib config", bootstrapConfig);
/**
For purposes of the Emscripten build, call sqlite3ApiBootstrap().
Ideally clients should be able to inject their own config here,
but that's not practical in this particular build constellation
because of the order everything happens in. Clients may either
define globalThis.sqlite3ApiConfig or modify
globalThis.sqlite3ApiBootstrap.defaultConfig to tweak the default
configuration used by a no-args call to sqlite3ApiBootstrap(),
but must have first loaded their WASM module in order to be able
to provide the necessary configuration state.
*/
const p = globalThis.sqlite3ApiBootstrap(bootstrapConfig);
delete globalThis.sqlite3ApiBootstrap;
return p /* the eventual result of globalThis.sqlite3InitModule() */;
}catch(e){
console.error("sqlite3ApiBootstrap() error:",e);
throw e;
}
}/*Emscripten post-load lib init*/
+55 -85
View File
@@ -27,14 +27,13 @@
/**
sqlite3ApiBootstrap() is the only global symbol persistently
exposed by this API. It is intended to be called one time at the
end of the API amalgamation process and passed configuration details
for the current environment.
end of the API amalgamation process, passed configuration details
for the current environment, and then optionally be removed from
the global object using `delete globalThis.sqlite3ApiBootstrap`.
This function is not intended for client-level use. It is intended
for use in creating bundles configured for specific WASM
environments. That said, the "sqlite3-api.js" intermediary build
file aims to be suitable for dropping in to custom builds, and it
exposes only this function.
environments.
This function expects a configuration object, intended to abstract
away details specific to any given WASM environment, primarily so
@@ -94,10 +93,9 @@
can be replaced with (e.g.) empty functions to squelch all such
output.
- `wasmfsOpfsDir`[^1]: Specifies the "mount point" of the
OPFS-backed filesystem in WASMFS-capable builds. This is only
used in WASMFS-capable builds of the library (which the canonical
builds do not include).
- `wasmfsOpfsDir`[^1]: Specifies the "mount point" of the OPFS-backed
filesystem in WASMFS-capable builds.
[^1] = This property may optionally be a function, in which case
this function calls that function to fetch the value,
@@ -127,8 +125,7 @@
Both sqlite3ApiBootstrap.defaultConfig and
globalThis.sqlite3ApiConfig get deleted by sqlite3ApiBootstrap()
because any changes to them made after that point would have no
useful effect. This function also deletes itself from globalThis
when it's called.
useful effect.
This function returns a Promise to the sqlite3 namespace object,
which resolves after the async pieces of the library init are
@@ -180,6 +177,14 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(
}
});
/**
Eliminate any confusion about whether these config objects may
be used after library initialization by eliminating the outward-facing
objects...
*/
delete globalThis.sqlite3ApiConfig;
delete sqlite3ApiBootstrap.defaultConfig;
/**
The main sqlite3 binding API gets installed into this object,
mimicking the C API as closely as we can. The numerous members
@@ -791,8 +796,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(
/**
wasm.X properties which are used for configuring the wasm
environment via whwashutil.js. This object gets fleshed out with
a number of WASM-specific utilities, in sqlite3-api-glue.c-pp.js.
environment via whwashutil.js.
*/
Object.assign(wasm, {
@@ -805,9 +809,8 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(
/**
When Emscripten compiles with `-sIMPORTED_MEMORY`, it
initializes the heap and imports it into wasm, as opposed to
the other way around. In this case, the memory is not available
via this.exports.memory so the client must pass it in via
config.memory.
the other way around. In this case, the memory is not
available via this.exports.memory.
*/
memory: config.memory
|| config.exports['memory']
@@ -953,11 +956,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(
};
wasm.realloc.impl = wasm.exports[keyRealloc];
wasm.dealloc = function f(m){
f.impl(wasm.ptr.coerce(m)/*tag:64bit*/)
/* This coerce() is the reason we have to set wasm.pointerSize before
calling WhWasmUtilInstaller(). If we don't, that code will call
into this very early in its init, before wasm.ptr has been set up,
resulting in a null deref here. */;
f.impl(wasm.ptr.coerce(m)/*tag:64bit*/);
};
wasm.dealloc.impl = wasm.exports[keyDealloc];
}
@@ -1028,18 +1027,18 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(
}/*compileOptionUsed()*/;
/**
sqlite3.wasm.pstack (pseudo-stack) holds a special-case allocator
intended solely for short-lived, small data. In practice, it's
primarily used to allocate output pointers. It must not be used
for any memory which needs to outlive the scope in which it's
obtained from pstack.
sqlite3.wasm.pstack (pseudo-stack) holds a special-case intended
solely for short-lived, small data. In practice, it's primarily
used to allocate output pointers. It mus not be used for any
memory which needs to outlive the scope in which it's obtained
from pstack.
The library guarantees only that a minimum of 2kb are available
in this allocator, and it may provide more (it's a build-time
value). pstack.quota and pstack.remaining can be used to get the
total resp. remaining amount of memory.
It has only a single intended usage pattern:
It has only a single intended usage:
```
const stackPos = pstack.pointer;
@@ -1056,11 +1055,13 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(
```
This allocator is much faster than a general-purpose one but is
limited to usage patterns like the one shown above (which are
pretty common when using sqlite3.capi).
limited to usage patterns like the one shown above.
The memory lives in the WASM heap and can be used with routines
such as wasm.poke() and wasm.heap8u().slice().
It operates from a static range of memory which lives outside of
space managed by Emscripten's stack-management, so does not
collide with Emscripten-provided stack allocation APIs. The
memory lives in the WASM heap and can be used with routines such
as wasm.poke() and wasm.heap8u().slice().
*/
wasm.pstack = Object.assign(Object.create(null),{
/**
@@ -1134,7 +1135,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(
argument: if it's 1, it returns a single pointer value. If it's
more than 1, it returns the same as allocChunks().
When a returned pointer will refer to a 64-bit value, e.g. a
When a returned pointers will refer to a 64-bit value, e.g. a
double or int64, and that value must be written or fetched,
e.g. using wasm.poke() or wasm.peek(), it is
important that the pointer in question be aligned to an 8-byte
@@ -1202,9 +1203,6 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(
}
})/*wasm.pstack properties*/;
/**
Docs: https://sqlite.org/wasm/doc/trunk/api-c-style.md#sqlite3_randomness
*/
capi.sqlite3_randomness = (...args)=>{
if(1===args.length
&& util.isTypedArray(args[0])
@@ -1239,6 +1237,8 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(
wasm.exports.sqlite3_randomness(...args);
};
/** State for sqlite3_wasmfs_opfs_dir(). */
let __wasmfsOpfsDir = undefined;
/**
If the wasm environment has a WASMFS/OPFS-backed persistent
storage directory, its path is returned by this function. If it
@@ -1262,7 +1262,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(
WASMFS capability requires a custom build.
*/
capi.sqlite3_wasmfs_opfs_dir = function(){
if(undefined !== this.dir) return this.dir;
if(undefined !== __wasmfsOpfsDir) return __wasmfsOpfsDir;
// If we have no OPFS, there is no persistent dir
const pdir = config.wasmfsOpfsDir;
if(!pdir
@@ -1270,21 +1270,21 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(
|| !globalThis.FileSystemDirectoryHandle
|| !globalThis.FileSystemFileHandle
|| !wasm.exports.sqlite3__wasm_init_wasmfs){
return this.dir = "";
return __wasmfsOpfsDir = "";
}
try{
if(pdir && 0===wasm.xCallWrapped(
'sqlite3__wasm_init_wasmfs', 'i32', ['string'], pdir
)){
return this.dir = pdir;
return __wasmfsOpfsDir = pdir;
}else{
return this.dir = "";
return __wasmfsOpfsDir = "";
}
}catch(e){
// sqlite3__wasm_init_wasmfs() is not available
return this.dir = "";
return __wasmfsOpfsDir = "";
}
}.bind(Object.create(null));
};
/**
Returns true if sqlite3.capi.sqlite3_wasmfs_opfs_dir() is a
@@ -1340,7 +1340,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(
*/
capi.sqlite3_js_vfs_list = function(){
const rc = [];
let pVfs = capi.sqlite3_vfs_find(wasm.ptr.null);
let pVfs = capi.sqlite3_vfs_find(wasm.ptr.coerce(0));
while(pVfs){
const oVfs = new capi.sqlite3_vfs(pVfs);
rc.push(wasm.cstrToJs(oVfs.$zName));
@@ -2068,8 +2068,8 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(
*/
asyncPostInit: async function ff(){
if(ff.isReady instanceof Promise) return ff.isReady;
let lia = this.initializersAsync;
delete this.initializersAsync;
let lia = sqlite3ApiBootstrap.initializersAsync;
delete sqlite3ApiBootstrap.initializersAsync;
const postInit = async ()=>{
if(!sqlite3.__isUnderTest){
/* Delete references to internal-only APIs which are used by
@@ -2094,7 +2094,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(
let p = Promise.resolve(sqlite3);
while(lia.length) p = p.then(lia.shift());
return ff.isReady = p.catch(catcher);
}.bind(sqlite3ApiBootstrap),
},
/**
scriptInfo ideally gets injected into this object by the
infrastructure which assembles the JS/WASM module. It contains
@@ -2121,37 +2121,16 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(
}
delete sqlite3ApiBootstrap.initializers;
sqlite3ApiBootstrap.sqlite3 = sqlite3;
if( 'undefined'!==typeof sqlite3InitScriptInfo/* from post-js-header.js */ ){
sqlite3InitScriptInfo.debugModule(
"sqlite3ApiBootstrap() complete", sqlite3
);
sqlite3.scriptInfo
/* Used by some async init code. As of 2025-11-15 this is still
in use by the OPFS VFS for locating its worker. In non-Emscripten
builds, this would need to be injected in somewhere to get
that VFS loading. */ = sqlite3InitScriptInfo;
}
sqlite3.__isUnderTest =
('undefined'===typeof sqlite3IsUnderTest /* from post-js-header.js */)
? false : !!sqlite3IsUnderTest;
if( sqlite3.__isUnderTest ){
if( 'undefined'!==typeof EmscriptenModule ){
sqlite3.config.emscripten = EmscriptenModule;
}
/*
The problem with exposing these pieces (in non-testing runs) via
sqlite3.wasm is that it exposes non-SQLite pieces to the
clients, who may come to expect it to remain. _We_ only have
these data because we've overridden Emscripten's wasm file
loader, and if we lose that capability for some reason then
we'll lose access to this metadata.
These data are interesting for exploring how the wasm/JS
pieces connect, e.g. for exploring exactly what Emscripten
imports into WASM from its JS glue, but it's not
SQLite-related.
*/
const iw = sqlite3.scriptInfo?.instantiateWasm;
delete globalThis.sqlite3ApiBootstrap;
delete globalThis.sqlite3ApiConfig;
sqlite3InitScriptInfo.debugModule(
"sqlite3ApiBootstrap() complete", sqlite3
);
sqlite3.scriptInfo /* used by some async init code */ =
sqlite3InitScriptInfo /* from post-js-header.js */;
if( (sqlite3.__isUnderTest = sqlite3IsUnderTest /* from post-js-header.js */) ){
sqlite3.config.emscripten = EmscriptenModule;
const iw = sqlite3InitScriptInfo.instantiateWasm;
if( iw ){
/* Metadata injected by the custom Module.instantiateWasm()
in pre-js.c-pp.js. */
@@ -2160,15 +2139,6 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(
sqlite3.wasm.imports = iw.imports;
}
}
/**
Eliminate any confusion about whether these config objects may
be used after library initialization by eliminating the outward-facing
objects...
*/
delete globalThis.sqlite3ApiConfig;
delete globalThis.sqlite3ApiBootstrap;
delete sqlite3ApiBootstrap.defaultConfig;
return sqlite3.asyncPostInit().then((s)=>{
sqlite3InitScriptInfo.debugModule(
"sqlite3.asyncPostInit() complete", sqlite3
+2 -2
View File
@@ -16,7 +16,7 @@
asynchronous Origin-Private FileSystem (OPFS) APIs using a second
Worker, implemented in sqlite3-opfs-async-proxy.js. This file is
intended to be appended to the main sqlite3 JS deliverable somewhere
after sqlite3-api-oo1.js.
after sqlite3-api-oo1.js and before sqlite3-api-cleanup.js.
*/
'use strict';
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
@@ -1441,7 +1441,7 @@ installOpfsVfs.defaultProxyUri =
globalThis.sqlite3ApiBootstrap.initializersAsync.push(async (sqlite3)=>{
try{
let proxyJs = installOpfsVfs.defaultProxyUri;
if( sqlite3?.scriptInfo?.sqlite3Dir ){
if(sqlite3.scriptInfo.sqlite3Dir){
installOpfsVfs.defaultProxyUri =
sqlite3.scriptInfo.sqlite3Dir + proxyJs;
//sqlite3.config.warn("installOpfsVfs.defaultProxyUri =",installOpfsVfs.defaultProxyUri);
+2 -2
View File
@@ -136,8 +136,8 @@
/*
** If SQLITE_WASM_BARE_BONES is defined, undefine most of the ENABLE
** macros. This will, when using the canonical makefile, also elide
** any C functions from the WASM exports: see
** ./EXPORTED_FUNCTIONS.c-pp.
** any C functions from the WASM exports which are listed in
** ./EXPORT_FUNCTIONS.sqlite3-extras.
*/
#ifdef SQLITE_WASM_BARE_BONES
# undef SQLITE_ENABLE_COLUMN_METADATA
+6 -7
View File
@@ -667,14 +667,12 @@ globalThis.WhWasmUtilInstaller = function(target){
const ft = target.functionTable();
const oldLen = __asPtrType(ft.length);
let ptr;
while( (ptr = cache.freeFuncIndexes.pop()) ){
if(ft.get(ptr)){
/* freeFuncIndexes's entry is stale. Table was modified via a
different API */
while(cache.freeFuncIndexes.length){
ptr = cache.freeFuncIndexes.pop();
if(ft.get(ptr)){ /* Table was modified via a different API */
ptr = null;
continue;
}else{
/* This index is free. We'll re-use it. */
break;
}
}
@@ -765,9 +763,10 @@ globalThis.WhWasmUtilInstaller = function(target){
has no side effects and returns undefined.
*/
target.uninstallFunction = function(ptr){
if(!ptr && __NullPtr!==ptr) return undefined;
if(!ptr && 0!==ptr) return undefined;
const fi = cache.freeFuncIndexes;
const ft = target.functionTable();
cache.freeFuncIndexes.push(ptr);
fi.push(ptr);
const rc = ft.get(ptr);
ft.set(ptr, null);
return rc;
+3 -3
View File
@@ -280,7 +280,7 @@ const BuildDefs oBuildDefs = {
" -DSQLITE_SPEEDTEST1_WASM"
" $(SQLITE_OPT)"
" -USQLITE_WASM_BARE_BONES"
" -USQLITE_C -DSQLITE_C=$(sqlite3.c)"
" -USQLITE_C -DSQLITE_C=$(sqlite3.canonical.c)"
" $(speedtest1.exit-runtime0)"
" $(speedtest1.c.in)"
" -lm",
@@ -308,7 +308,7 @@ const BuildDefs oBuildDefs = {
" -DSQLITE_SPEEDTEST1_WASM"
" $(SQLITE_OPT)"
" -USQLITE_WASM_BARE_BONES"
" -USQLITE_C -DSQLITE_C=$(sqlite3.c)"
" -USQLITE_C -DSQLITE_C=$(sqlite3.canonical.c)"
" $(speedtest1.exit-runtime0)"
" $(speedtest1.c.in)"
" -lm",
@@ -633,7 +633,7 @@ static void mk_pre_post(char const *zBuildName, BuildDef const * pB){
pB->zDotWasm);
}
ps("");
pf("\t@$(call b.mkdir@); $(call b.c-pp.shcmd,"
pf("\t@$(call b.c-pp.shcmd,"
"%s,"
"$(pre-js.in.js),"
"$(pre-js.%s.js),"
+23 -13
View File
@@ -652,7 +652,7 @@ SRC = \
$(TOP)/src/sqliteInt.h \
$(TOP)/src/sqliteLimit.h \
$(TOP)/src/table.c \
$(TOP)/src/tclsqlite.c \
tclsqlite-ex.c \
$(TOP)/src/threads.c \
$(TOP)/src/tokenize.c \
$(TOP)/src/treeview.c \
@@ -1409,15 +1409,15 @@ whereexpr.o: $(TOP)/src/whereexpr.c $(DEPS_OBJ_COMMON)
window.o: $(TOP)/src/window.c $(DEPS_OBJ_COMMON)
$(T.cc.sqlite) -c $(TOP)/src/window.c
tclsqlite.o: $(T.tcl.env.sh) $(TOP)/src/tclsqlite.c $(DEPS_OBJ_COMMON)
tclsqlite.o: $(T.tcl.env.sh) tclsqlite-ex.c $(DEPS_OBJ_COMMON)
$(T.compile.tcl) -DUSE_TCL_STUBS=1 $$TCL_INCLUDE_SPEC \
-c $(TOP)/src/tclsqlite.c
-c tclsqlite-ex.c -o tclsqlite.o
tclsqlite-shell.o: $(T.tcl.env.sh) $(TOP)/src/tclsqlite.c $(DEPS_OBJ_COMMON)
$(T.compile.tcl) -DTCLSH -o $@ -c $(TOP)/src/tclsqlite.c $$TCL_INCLUDE_SPEC
tclsqlite-shell.o: $(T.tcl.env.sh) tclsqlite-ex.c $(DEPS_OBJ_COMMON)
$(T.compile.tcl) -DTCLSH -o $@ -c tclsqlite-ex.c $$TCL_INCLUDE_SPEC
tclsqlite-stubs.o: $(T.tcl.env.sh) $(TOP)/src/tclsqlite.c $(DEPS_OBJ_COMMON)
$(T.compile.tcl) -DUSE_TCL_STUBS=1 -o $@ -c $(TOP)/src/tclsqlite.c $$TCL_INCLUDE_SPEC
tclsqlite-stubs.o: $(T.tcl.env.sh) tclsqlite-ex.c $(DEPS_OBJ_COMMON)
$(T.compile.tcl) -DUSE_TCL_STUBS=1 -o $@ -c tclsqlite-ex.c $$TCL_INCLUDE_SPEC
#
# STATIC_TCLSQLITE3 = 1 to statically link tclsqlite3, else
@@ -1661,11 +1661,19 @@ install-tcl-0 install-tcl-:
install-tcl: install-tcl-$(HAVE_TCL)
install: install-tcl
tclsqlite3.c: sqlite3.c
TCLSQLITEEX = \
$(TOP)/ext/qrf/qrf.h \
$(TOP)/ext/qrf/qrf.c \
$(TOP)/src/tclsqlite.c
tclsqlite-ex.c: $(TCLSQLITEEX) $(TOP)/tool/mkcombo.tcl $(B.tclsh)
$(B.tclsh) $(TOP)/tool/mkcombo.tcl $(TCLSQLITEEX) -o $@
tclsqlite3.c: sqlite3.c tclsqlite-ex.c
echo '#ifndef USE_SYSTEM_SQLITE' >tclsqlite3.c
cat sqlite3.c >>tclsqlite3.c
echo '#endif /* USE_SYSTEM_SQLITE */' >>tclsqlite3.c
cat $(TOP)/src/tclsqlite.c >>tclsqlite3.c
cat tclsqlite-ex.c >>tclsqlite3.c
#
# $(CFLAGS.tclextension) = CFLAGS for the tclextension* targets.
@@ -1782,7 +1790,7 @@ TESTFIXTURE_FLAGS += -DSQLITE_STRICT_SUBTYPE=1
TESTFIXTURE_SRC0 = $(TESTSRC2) $(libsqlite3.LIB)
TESTFIXTURE_SRC1 = sqlite3.c
TESTFIXTURE_SRC = $(TESTSRC) $(TOP)/src/tclsqlite.c
TESTFIXTURE_SRC = $(TESTSRC) tclsqlite-ex.c
TESTFIXTURE_SRC += $(TESTFIXTURE_SRC$(USE_AMALGAMATION))
testfixture$(T.exe): $(T.tcl.env.sh) has_tclsh85 $(TESTFIXTURE_SRC)
@@ -1912,7 +1920,7 @@ shelltest:
#
sqlite3_analyzer.c.flags.0 = -DINCLUDE_SQLITE3_C=1
sqlite3_analyzer.c.flags.1 =
sqlite3_analyzer.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl \
sqlite3_analyzer.c: sqlite3.c tclsqlite-ex.c $(TOP)/tool/spaceanal.tcl \
$(TOP)/tool/mkccode.tcl $(TOP)/tool/sqlite3_analyzer.c.in
$(B.tclsh) $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqlite3_analyzer.c.in \
$(sqlite3_analyzer.c.flags.$(LINK_TOOLS_DYNAMICALLY)) \
@@ -1936,7 +1944,7 @@ sqlite3_analyzer$(T.exe): $(T.tcl.env.sh) sqlite3_analyzer.c \
# can cause the $@ to link to an out-of-tree libsqlite3.so, which may
# or may not fail or otherwise cause confusion.
sqltclsh.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/sqltclsh.tcl \
sqltclsh.c: sqlite3.c tclsqlite-ex.c $(TOP)/tool/sqltclsh.tcl \
$(TOP)/ext/misc/appendvfs.c $(TOP)/tool/mkccode.tcl \
$(TOP)/tool/sqltclsh.c.in
$(B.tclsh) $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqltclsh.c.in >sqltclsh.c
@@ -1957,7 +1965,7 @@ xbin: sqlite3_expert$(T.exe)
CHECKER_DEPS =\
$(TOP)/tool/mkccode.tcl \
sqlite3.c \
$(TOP)/src/tclsqlite.c \
tclsqlite-ex.c \
$(TOP)/ext/repair/sqlite3_checker.tcl \
$(TOP)/ext/repair/checkindex.c \
$(TOP)/ext/repair/checkfreelist.c \
@@ -2333,6 +2341,8 @@ mptest: mptester$(T.exe)
# Source and header files that shell.c depends on
SHELL_DEP = \
$(TOP)/src/shell.c.in \
$(TOP)/ext/qrf/qrf.c \
$(TOP)/ext/qrf/qrf.h \
$(TOP)/ext/expert/sqlite3expert.c \
$(TOP)/ext/expert/sqlite3expert.h \
$(TOP)/ext/intck/sqlite3intck.c \
+51 -44
View File
@@ -1,12 +1,12 @@
C Experimental\sSQLITE_MAX_SUBQUERY\scompile-time\soption.
D 2025-11-18T00:16:40.468
C If\sSQL\sis\sentered\sas\sadditional\scommand-line\sarguments\sto\sCLI,\sthat\scounts\nthe\ssame\sas\sgetting\sinput\sfrom\sa\spipe\sor\sfile\sfor\sthe\spurpose\sof\sdetermining\nthe\sdefault\sformat\smode.
D 2025-11-17T19:46:04.211
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md 6bc480fc673fb4acbc4094e77edb326267dd460162d7723c7f30bee2d3d9e97d
F Makefile.in 3ce07126d7e87c7464301482e161fdae6a51d0a2aa06b200b8f0000ef4d6163b
F Makefile.linux-generic bd3e3cacd369821a6241d4ea1967395c962dfe3057e38cb0a435cee0e8b789d0
F Makefile.msc 523bab2f6569e912a0aaf2d13e0b3f9bcb36d52310b236d950b354068c9de3f3
F Makefile.msc 8c5ed7173ee4cc3c03ba8e21065128f7ca5292c83d68f3bf971786e21fc6e382
F README.md dae499194b75deed76a13a4a83c82493f2530331882d7dfe5754d63287d3f8f7
F VERSION 74672bfd4c7826c0fc6f84762488a707c52e7d2d94af42ccb0edcc6c74311c41
F art/icon-243x273.gif 9750b734f82fdb3dc43127753d5e6fbf3b62c9f4e136c2fbf573b2f57ea87af5
@@ -47,7 +47,7 @@ F autosetup/find_tclconfig.tcl e64886ffe3b982d4df42cd28ed91fe0b5940c2c5785e126c1
F autosetup/jimsh0.c a57c16e65dcffc9c76e496757cb3f7fb47e01ecbd1631a0a5e01751fc856f049
F autosetup/pkg-config.tcl 4e635bf39022ff65e0d5434339dd41503ea48fc53822c9c5bde88b02d3d952ba
F autosetup/proj.tcl 6fc14ef82b19b77a95788ffbcfad7989b4e3cb4ce96a21dcb5cf7312f362fba9
F autosetup/sqlite-config.tcl 1fd7c55394051fbfba2fb9fa9192c027ee7d74612dbf772b06e27feb9f1de5df
F autosetup/sqlite-config.tcl 97dc76d332ae3344d02d15c99c57db0ac063537e9133cbee0520d48ba0eddc08
F autosetup/system.tcl 51d4be76cd9a9074704b584e5c9cbba616202c8468cf9ba8a4f8294a7ab1dba9
F autosetup/teaish/README.txt b40071e6f8506500a2f7f71d5fc69e0bf87b9d7678dd9da1e5b4d0acbf40b1ca
F autosetup/teaish/core.tcl e014dd95900c7f9a34e8e0f460f47e94841059827bce8b4c49668b0c7ae3f1a0
@@ -71,7 +71,7 @@ F doc/wal-lock.md 7db0cd61e2000b545b78ce89b0c2a9a8dd8d64c097839258ac10d7c5c4156e
F ext/README.md 6eb1ac267d917767952ed0ef63f55de003b6a5da433ce1fa389e1a9532e73132
F ext/expert/README.md b321c2762bb93c18ea102d5a5f7753a4b8bac646cb392b3b437f633caf2020c3
F ext/expert/expert.c d548d603a4cc9e61f446cc179c120c6713511c413f82a4a32b1e1e69d3f086a4
F ext/expert/expert1.test 1d2da6606623b57bb47064e02140823ce1daecd4cacbf402c73ad3473d7f000c
F ext/expert/expert1.test d9dfbf7fb527cfd43049e30a6238ef02c94484041fa4461ed41acbc6435425d6
F ext/expert/sqlite3expert.c 546010043fbec93544f762de5161b3d553165859e6bd853c4b85c05f93484260
F ext/expert/sqlite3expert.h ca81efc2679a92373a13a3e76a6138d0310e32be53d6c3bfaedabd158ea8969b
F ext/expert/test_expert.c c395134bd6d4efa594a7d26578a1cb624c4027b79b4b5fcd44736c5ef1f5f725
@@ -290,7 +290,7 @@ F ext/intck/test_intck.c 4f9eaadaedccb9df1d26ba41116a0a8e5b0c5556dc3098c8ff68633
F ext/jni/GNUmakefile 8a94e3a1953b88cf117fb2a5380480feada8b4f5316f02572cab425030a720b4
F ext/jni/README.md 1479c83dbe26125264a060ee6873531795a7082dbc0d43c4067683371331559f
F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa
F ext/jni/src/c/sqlite3-jni.c 6ccc50b0e98b8ef8fd6a8b837223e1c8ebb92bfe6b976128cc757c26257d994a
F ext/jni/src/c/sqlite3-jni.c 3d84a0176af779737ae977ba1c90d2ffe2537b8299c5d9f6552620493f12ac4b
F ext/jni/src/c/sqlite3-jni.h df43024cced914c49485633d0f90168689e70577b3b17b0ecbdaf16e4a417bff
F ext/jni/src/org/sqlite/jni/annotation/Experimental.java 8603498634e41d0f7c70f661f64e05df64376562ea8f126829fd1e0cdd47e82b
F ext/jni/src/org/sqlite/jni/annotation/NotNull.java be6cc3e8e114485822331630097cc0f816377e8503af2fc02f9305ff2b353917
@@ -319,7 +319,7 @@ F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java 0d1e9afc9ff8a2adb94a155b72385
F ext/jni/src/org/sqlite/jni/capi/SQLTester.java 3c0babc067d8560627a9ed1b07979f9d4393464e2282c2fca4832052e982c7bc
F ext/jni/src/org/sqlite/jni/capi/ScalarFunction.java 93b9700fca4c68075ccab12fe0fbbc76c91cafc9f368e835b9bd7cd7732c8615
F ext/jni/src/org/sqlite/jni/capi/TableColumnMetadata.java 9133bb7685901d2edf07801191284975e33b5583ce09dce1c05202ff91e7bb99
F ext/jni/src/org/sqlite/jni/capi/Tester1.java 378d142435d220b20b7ce7343c62a03e853bb8c51e80447ee0f8ac5c37362d9a
F ext/jni/src/org/sqlite/jni/capi/Tester1.java 4c3d16fdf6e979f839b2ecdb14d0a0c04bd3d0e41500fc9e8110b588883b140b
F ext/jni/src/org/sqlite/jni/capi/TraceV2Callback.java 0a25e117a0daae3394a77f24713e36d7b44c67d6e6d30e9e1d56a63442eef723
F ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java c8bdf7848e6599115d601bcc9427ff902cb33129b9be32870ac6808e04b6ae56
F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 2ce069f3e007fdbbe1f4e507a5a407fc9679da31a0aa40985e6317ed4d5ec7b5
@@ -397,8 +397,8 @@ F ext/misc/shathree.c fd22d70620f86a0467acfdd3acd8435d5cb54eb1e2d9ff36ae44e38982
F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
F ext/misc/spellfix.c 693c8fd3293087fa821322967a97e59dfa24051e5d2ca7fa85790a4034db6fa4
F ext/misc/sqlar.c a6175790482328171da47095f87608b48a476d4fac78d8a9ff18b03a2454f634
F ext/misc/sqlite3_stdio.c 0fe5a45bd332b30aef2b68c64edbe69e31e9c42365b0fa79ce95a034bca6fbb0
F ext/misc/sqlite3_stdio.h f05eaf5e0258f0573910324a789a9586fc360a57678c57a6d63cfaa2245b6176
F ext/misc/sqlite3_stdio.c e49c07050bf7bdc87866da7583beda236f2f8c462018a34b61785d99cbddedfd
F ext/misc/sqlite3_stdio.h 27a4ecea47e61bc9574ccdf2806f468afe23af2f95028c9b689bfa08ab1ce99f
F ext/misc/stmt.c b090086cd6bd6281c21271d38d576eeffe662f0e6b67536352ce32bbaa438321
F ext/misc/stmtrand.c 59cffa5d8e158943ff1ce078956d8e208e8c04e67307e8f249dece2436dcb7fc
F ext/misc/templatevtab.c 10f15b165b95423ddef593bc5dcb915ec4eb5e0f1066d585e5435a368b8bc22b
@@ -416,6 +416,9 @@ F ext/misc/wholenumber.c 0fa0c082676b7868bf2fa918e911133f2b349bcdceabd1198bba5f6
F ext/misc/windirent.h 02211ce51f3034c675f2dbf4d228194d51b3ee05734678bad5106fff6292e60c
F ext/misc/zipfile.c 09e6e3a3ff40a99677de3c0bc6569bd5f4709b1844ac3d1c1452a456c5a62f1c
F ext/misc/zorder.c bddff2e1b9661a90c95c2a9a9c7ecd8908afab5763256294dd12d609d4664eee
F ext/qrf/README.md dd565fd1ca0c46ea37dbf4d496e368b9ecade768c92669640bc106e039629016
F ext/qrf/qrf.c ee3964addf075c87fc60e312a472eec35112ba34581edf655fcd5c34e2492602
F ext/qrf/qrf.h b4b3489b3b3683523fd248d15cf5945830643b036943efacdb772a3e00367aa2
F ext/rbu/rbu.c 801450b24eaf14440d8fd20385aacc751d5c9d6123398df41b1b5aa804bf4ce8
F ext/rbu/rbu1.test 25870dd7db7eb5597e2b4d6e29e7a7e095abf332660f67d89959552ce8f8f255
F ext/rbu/rbu10.test 7c22caa32c2ff26983ca8320779a31495a6555737684af7aba3daaf762ef3363
@@ -575,7 +578,7 @@ F ext/session/sessionwor.test 6fd9a2256442cebde5b2284936ae9e0d54bde692d0f5fd009e
F ext/session/sqlite3session.c b3de195ce668cace9b324599bf6255a70290cbfb5451e826e946f3aee6e64c54
F ext/session/sqlite3session.h 7404723606074fcb2afdc6b72c206072cdb2b7d8ba097ca1559174a80bc26f7a
F ext/session/test_session.c 8766b5973a6323934cb51248f621c3dc87ad2a98f023c3cc280d79e7d78d36fb
F ext/wasm/GNUmakefile aef238930e96197fed6fa1b4d9e5443b519dd6c24e0139e124c26bc51270f39a
F ext/wasm/GNUmakefile b669dac12f134b8ca7e0f2fc49784abe3ae0cf3e559b155d2651c0861ff021f3
F ext/wasm/README-dist.txt f01081a850ce38a56706af6b481e3a7878e24e42b314cfcd4b129f0f8427066a
F ext/wasm/README.md 2e87804e12c98f1d194b7a06162a88441d33bb443efcfe00dc6565a780d2f259
F ext/wasm/SQLTester/GNUmakefile e0794f676d55819951bbfae45cc5e8d7818dc460492dc317ce7f0d2eca15caff
@@ -584,30 +587,31 @@ F ext/wasm/SQLTester/SQLTester.run.mjs 57f2adb33f43f2784abbf8026c1bfd049d8013af1
F ext/wasm/SQLTester/index.html 64f3435084c7d6139b08d1f2a713828a73f68de2ae6a3112cbb5980d991ba06f
F ext/wasm/SQLTester/touint8array.c 2d5ece04ec1393a6a60c4bf96385bda5e1a10ad49f3038b96460fc5e5aa7e536
F ext/wasm/api/EXPORTED_FUNCTIONS.c-pp d609c22bc05e25991bdd2eeed4497485a3756446158b2794471426e5e3517056
F ext/wasm/api/README.md aae128e14711dafac29e7b645ff1b6be42305b73f74b1303d8401351f8448631
F ext/wasm/api/README.md df476398c6d80affae807a8208c5a97d1615786e1d6767b5292bfe67edb59b18
F ext/wasm/api/extern-post-js.c-pp.js 4b310c9891886e7c1ea4e2419966ff45dfeda825b897fdaa4119332509f7e3d1
F ext/wasm/api/extern-pre-js.js cc61c09c7a24a07dbecb4c352453c3985170cec12b4e7e7e7a4d11d43c5c8f41
F ext/wasm/api/post-js-footer.js a50c1a2c4d008aede7b2aa1f18891a7ee71437c2f415b8aeb3db237ddce2935b
F ext/wasm/api/post-js-header.js d24bd0d065f3489c8b78ddf3ead6321e5d047187a162cd503c41700e03dd1f06
F ext/wasm/api/pre-js.c-pp.js ad2546290e0c8ce5ca2081bff6e85cc25eeb904a3303921f1184290a7ff1b32f
F ext/wasm/api/post-js-footer.js 5bd7170b5e8ce7b62102702bbcf47ef7b3b49cd56ed40c043fd990aa715b74ee
F ext/wasm/api/post-js-header.js 79d078aec33d93b640a19c574b504d88bb2446432f38e2fbb3bb8e36da436e70
F ext/wasm/api/pre-js.c-pp.js b1a7a09d14df1f36de11e801ba06cb92c0ddeb520e5b2f28836af0ec8146e288
F ext/wasm/api/sqlite3-api-cleanup.js f0a408704e56d6e59d5c0a2f6d84651b71dc2af39a2a4017256213ee29c65cc4
F ext/wasm/api/sqlite3-api-glue.c-pp.js 79a54b54ca6324d28e31e19b56bbaebb7d2cc4b3079066e7e901333fa5047c53
F ext/wasm/api/sqlite3-api-oo1.c-pp.js 31dbfd470c91ffd96d77399b749bab6b69e3ba9074188833f97ac13f087cf07b
F ext/wasm/api/sqlite3-api-prologue.js a7cab3e8f78d5a93d7173657677fef18265f48d4e0b1e1755740f734a69cae6d
F ext/wasm/api/sqlite3-api-prologue.js 68cf99ffc9742462323da3f3b207027472214727a750a952c6c10d53b11a3fa6
F ext/wasm/api/sqlite3-api-worker1.c-pp.js 1041dd645e8e821c082b628cd8d9acf70c667430f9d45167569633ffc7567938
F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89
F ext/wasm/api/sqlite3-opfs-async-proxy.js 9654b565b346dc609b75d15337f20acfa7af7d9d558da1afeb9b6d8eaa404966
F ext/wasm/api/sqlite3-vfs-helper.c-pp.js 3f828cc66758acb40e9c5b4dcfd87fd478a14c8fb7f0630264e6c7fa0e57515d
F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js 26cb41d5a62f46a106b6371eb00fef02de3cdbfaa51338ba087a45f53028e0d0
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js aa330fa0e8ef35cbd92eb0d52e05fbaa07e61540c5cb164e693c82428ce1d763
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 418c33fe284739564daab3c7a7a88882fdd3c99137497900f98eddec1e409af5
F ext/wasm/api/sqlite3-vtab-helper.c-pp.js 9097074724172e31e56ce20ccd7482259cf72a76124213cbc9469d757676da86
F ext/wasm/api/sqlite3-wasm.c 03451f8335f4ba8ea9a79dfc170560b1ee5376de64ff49c1a114274ccf50ab3d
F ext/wasm/api/sqlite3-wasm.c 2d1cbe498f7b0fb64b11451eda481f458df74d6258baea635513e637fcbb8b1a
F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bda1c75bd674a92a0e27cc2f3d46dbbf21e422413f8046814515a0bd7409328a
F ext/wasm/api/sqlite3-worker1.c-pp.js 802d69ead8c38dc1be52c83afbfc77e757da8a91a2e159e7ed3ecda8b8dba2e7
F ext/wasm/c-pp-lite.c 943be1a36774d58385dca32de36fc18d4f432fe79f7aa35e6c85dd6a6b825bd0
F ext/wasm/common/SqliteTestUtil.js 7adaeffef757d8708418dc9190f72df22367b531831775804b31598b44f6aa51
F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15
F ext/wasm/common/testing.css e97549bab24126c24e0daabfe2de9bb478fb0a69fdb2ddd0a73a992c091aad6f
F ext/wasm/common/whwasmutil.js 804409dec98458f732b23e6e86788934ac6dec002107435cc4055355585d1c15
F ext/wasm/common/whwasmutil.js b4fd73fac162406e67f276647ff8bae51881e15eebdf4b725aeb2b10ab1e056a
F ext/wasm/config.make.in c424ae1cc3c89274520ad312509d36c4daa34a3fce5d0c688e5f8f4365e1049a
F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed
F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508
@@ -627,7 +631,7 @@ F ext/wasm/index.html 54e27db740695ab2cb296e02d42c4c66b3f11b65797340d19fa6590f5b
F ext/wasm/jaccwabyt/jaccwabyt.js 1e734c624205cdf621f322972dfb0fc8013d573a5882f57492a6830e5ec23e17
F ext/wasm/jaccwabyt/jaccwabyt.md 167fc0b624c9bc2c477846e336de9403842d81b1a24fc4d3b24317cb9eba734f
F ext/wasm/mkdist.sh 64d53f469c823ed311f6696f69cec9093f745e467334b34f5ceabdf9de3c5b28 x
F ext/wasm/mkwasmbuilds.c ef42e404236dd98cedb6ecea47b6d2474e3c593633ce6d992d316289dfc442b6
F ext/wasm/mkwasmbuilds.c 9b02298593276962a6136575630498d452de5055f4a4737a3c254c1b04e7cc04
F ext/wasm/module-symbols.html e54f42112e0aac2a31f850ab33e7f2630a2ea4f63496f484a12469a2501e07e2
F ext/wasm/scratchpad-wasmfs.html a3d7388f3c4b263676b58b526846e9d02dfcb4014ff29d3a5040935286af5b96
F ext/wasm/scratchpad-wasmfs.mjs 66034b9256b218de59248aad796760a1584c1dd842231505895eff00dbd57c63
@@ -653,7 +657,7 @@ F ext/wasm/tests/opfs/sahpool/index.html be736567fd92d3ecb9754c145755037cbbd2bca
F ext/wasm/tests/opfs/sahpool/sahpool-pausing.js f264925cfc82155de38cecb3d204c36e0f6991460fff0cb7c15079454679a4e2
F ext/wasm/tests/opfs/sahpool/sahpool-worker.js bd25a43fc2ab2d1bafd8f2854ad3943ef673f7c3be03e95ecf1612ff6e8e2a61
F magic.txt 5ade0bc977aa135e79e3faaea894d5671b26107cc91e70783aa7dc83f22f3ba0
F main.mk 29078d11f7545a8fae4347305b164bb4e777b801da16baeb48214014a8d63a06
F main.mk 27a0769b40a813343f19e8fca2e9d55405f0311cb3cc5327f5702640df0182a0
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
F mptest/crash01.test 61e61469e257df0850df4293d7d4d6c2af301421
@@ -673,7 +677,7 @@ F src/btmutex.c 30dada73a819a1ef5b7583786370dce1842e12e1ad941e4d05ac29695528daea
F src/btree.c 8850125300b9780fa54bc45a41af88eb2796b90f2f97942279094beef9b0e971
F src/btree.h e823c46d87f63d904d735a24b76146d19f51f04445ea561f71cc3382fd1307f0
F src/btreeInt.h 9c0f9ea5c9b5f4dcaea18111d43efe95f2ac276cd86d770dce10fd99ccc93886
F src/build.c 62cf7d885a93d87dcdb2a314408b760367cd9d9b54f8d4ea7d71d0cd36f0ab4c
F src/build.c 611e07299d72ff04bbcb9e7109183467e30925d203c3e121ef9bb3cf6876289b
F src/callback.c 3605bbf02bd7ed46c79cd48346db4a32fc51d67624400539c0532f4eead804ad
F src/carray.c ff6081a31878fc34df8fa1052a9cbf17ddc22652544dcb3e2326886ed1053b55
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
@@ -726,20 +730,20 @@ F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5
F src/pcache1.c 131ca0daf4e66b4608d2945ae76d6ed90de3f60539afbd5ef9ec65667a5f2fcd
F src/pragma.c ecec75795c1821520266e4f93fa8840cce48979af532db06f085e36a7813860f
F src/prepare.c 2af0b5c1ec787c8eebd21baa9d79caf4a4dc3a18e76ce2edbf2027d706bca37a
F src/printf.c b1b29b5e58e1530d5daeee5963d3c318d8ab2d7e38437580e28755753e0c1ded
F src/printf.c 0f6c73d1427e397c631bfaf1e0117b12c9c0545e4c2e44987237ec7849c92044
F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
F src/resolve.c 5616fbcf3b833c7c705b24371828215ad0925d0c0073216c4f153348d5753f0a
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
F src/select.c ba9cd07ffa3277883c1986085f6ddc4320f4d35d5f212ab58df79a7ecc1a576a
F src/shell.c.in ceb0a9cc008ac82d8d2e6ef353db14a54bc40dfd60a8cfbb6bc98d071f538761
F src/sqlite.h.in 667dff873941366da98da3200cf757ac05dfb62ea785e6642e33dd586ae5285c
F src/shell.c.in ee5c0326a045f83f27705ef07746c84e532654bdadac3cc3824da6d7dab2ccf5
F src/sqlite.h.in 795ce84cc136b4e74d882cf4fab56d2927c20b9af9fd2fcea27760a6fe50851b
F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479
F src/sqlite3ext.h 5d5330f5f8461f5ce74960436ddcfa53ecd09c2b8b23901e22ae38aec3243998
F src/sqliteInt.h 775c133757bd5c7d0fce10407959ee45993678ae95088e4ebdb22ef24e9d78ff
F src/sqliteInt.h 88f7fc9ce1630d9a5f7e0a8e1f3287cdc63882fba985c18e7eee1b9f457f59aa
F src/sqliteLimit.h fe70bd8983e5d317a264f2ea97473b359faf3ebb0827877a76813f5cf0cdc364
F src/status.c 7565d63a79aa2f326339a24a0461a60096d0bd2bce711fefb50b5c89335f3592
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
F src/tclsqlite.c 3c604c49e6cf4211960a9ddb9505280fd22cde32175f40884c641c0f5a286036
F src/tclsqlite.c 381384fbe3cf342115f9ad01208fa81092e9a2156a4ea4d44de87852b8df3a8a
F src/tclsqlite.h 614b3780a62522bc9f8f2b9fb22689e8009958e7aa77e572d0f3149050af348a
F src/test1.c 5d061afe479c7364842e0170be7220dea13389575fa6030d30b3e20bec4e1f75
F src/test2.c 62f0830958f9075692c29c6de51b495ae8969e1bef85f239ffcd9ba5fb44a5ff
@@ -883,7 +887,7 @@ F test/autoindex5.test 3fb938cbf4e7f3896563ce04e2a24b0bc653fc6245b4bf3268cd7b20f
F test/autovacuum.test 00671369bbf96c6a49989a9425f5b78b94075d6a4b031e5e00000c2c32f365df
F test/autovacuum2.test 76f7eb4fe6a6bf6d33a196a7141dba98886d2fb53a268d7feca285d5da4759d7
F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4
F test/avfs.test 76f59743dc1f5fa533840d1818b420fe1ee45e21c0fd6bbac7942ba677903128
F test/avfs.test 95bb8d04f8edad6dc9e600221d103f7e2cc3da398af84df215a3a819e560c45c
F test/avtrans.test 7a6eae44763293024b137b53ff824d8500d754dbae060a8d940afbacfc1d4a15
F test/backcompat.test f2431465ed668f09fc3f6998e56e893a1506ccea6e8b6f409f085f759f431b48
F test/backup.test 3b08fd4af69f0fa786931103a31f4542b184aba16e239e5f22b18c3c2476697f
@@ -1433,6 +1437,7 @@ F test/mmap4.test 2e2b4e32555b58da15176e6fe750f17c9dcf7f93
F test/mmapcorrupt.test 470fb44fe92e99c1d23701d156f8c17865f5b027063c9119dcfdb842791f4465
F test/mmapfault.test d4c9eff9cd8c2dc14bc43e71e042f175b0a26fe3
F test/mmapwarm.test 2272005969cd17a910077bd5082f70bc1fefad9a875afec7fc9af483898ecaf3
F test/modeA.clitest 73d091c785671f629af0900f0654a28468bbba5685a545c1513049c57f1b8cbc
F test/multiplex.test d74c034e52805f6de8cc5432cef8c9eb774bb64ec29b83a22effc8ca4dac1f08
F test/multiplex2.test 580ca5817c7edbe4cc68fa150609c9473393003a
F test/multiplex3.test fac575e0b1b852025575a6a8357701d80933e98b5d2fe6d35ddaa68f92f6a1f7
@@ -1502,6 +1507,9 @@ F test/printf2.test 3f55c1871a5a65507416076f6eb97e738d5210aeda7595a74ee895f2224c
F test/progress.test ebab27f670bd0d4eb9d20d49cef96e68141d92fb
F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc
F test/pushdown.test 46a626ef1c0ca79b85296ff2e078b9da20a50e9b804b38f441590c3987580ddd
F test/qrf01.test dfdbbd2b5f2808f5ff6598c8632ee002389b690eb5016450c2f847d8e51b9b92
F test/qrf02.test 39b4afdc000bedccdafc0aecf17638df67a67aaa2d2942865ae6abcc48ba0e92
F test/qrf03.test 9d88aeb5cdd53f050b7ab9bd203281f7c9d063c33f22f8808e441b7ac0874ccf
F test/queryonly.test 5f653159e0f552f0552d43259890c1089391dcca
F test/quick.test 1681febc928d686362d50057c642f77a02c62e57
F test/quickcheck.test a4b7e878cd97e46108291c409b0bf8214f29e18fddd68a42bc5c1375ad1fb80a
@@ -1514,7 +1522,7 @@ F test/randexpr1.test eda062a97e60f9c38ae8d806b03b0ddf23d796df
F test/rbu.test 168573d353cd0fd10196b87b0caa322c144ef736
F test/rdonly.test 64e2696c322e3538df0b1ed624e21f9a23ed9ff8
F test/readonly.test 0d307c335b3421898cfe64a783a376138aa003849b6bff61ee2d21e805bc0051
F test/recover.test c76d05f33f0271fba0f0752170e03b0ab5952dc61dcea7ab3ba40df03c4c42de
F test/recover.test 643139b911ac880a1e881d7621f02cfb546b608b8f2494d7d26fd5ed103b1ceb
F test/regexp1.test 8f2a8bc1569666e29a4cee6c1a666cd224eb6d50e2470d1dc1df995170f3e0f1
F test/regexp2.test 64f9726b2ddc71aea06725fcad53231833d038d58b936d49083ace658b370a13
F test/reindex.test cd9d6021729910ece82267b4f5e1b5ac2911a7566c43b43c176a6a4732e2118d
@@ -1595,16 +1603,17 @@ F test/sharedA.test 64bdd21216dda2c6a3bd3475348ccdc108160f34682c97f2f51c19fc0e21
F test/sharedB.test 1a84863d7a2204e0d42f2e1606577c5e92e4473fa37ea0f5bdf829e4bf8ee707
F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939
F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
F test/shell1.test ebe953d64c937ad42a0f33170ac0d2d2568faae26813fc7a95203756446d54aa
F test/shell2.test ab23f01ea2347e4b72bb2399af7ee82aa00f9c059141749f7c4064abca5ad728
F test/shell3.test 603b448e917537cf77be0f265c05c6f63bc677c63a533c8e96aae923b56f4a0e
F test/shell4.test 03593fa7908a55f255916ffeda707cdf55680c777736e3da62b1d78cde0d684d
F test/shell5.test d17e7927ab8b7f720efbdd9b5d05fceb6c3c56c25917901b315400214bf24ef4
F test/shell1.test 8fbb73650b685766cdf4b5e8b5d12dee72664f44195eeb788d0f52fce08e04eb
F test/shell2.test 103140814bdc7508aa41dd3462413cbc4aa84b4261112cb8d501d74275cb7d48
F test/shell3.test 840192774cc4edf7653520c0434a311c7477b9bc324abbc7bd2887915792fa8c
F test/shell4.test e25580a792b7b54560c3a76b6968bd8189261f38979fe28e6bc6312c5db280db
F test/shell5.test 7a249400bb2af59ac8524f357f8cf2844a62b6ac5ff8ecd69b045ceb688700ae
F test/shell6.test e3b883b61d4916b6906678a35f9d19054861123ad91b856461e0a456273bdbb8
F test/shell7.test 43fd8e511c533bab5232e95c7b4be93b243451709e89582600d4b6e67693d5c3
F test/shell8.test 641cf21a99c59404c24e3062923734951c4099a6b6b6520de00cf7a1249ee871
F test/shell9.test 8742a5b390cdcef6369f5aa223e415aa4255a4129ef249b177887dc635a87209
F test/shellA.test 4ecff8b7b2c0122ba8174abfbcc4b0f59e44d80f2a911068f8cd4cfc6661032d
F test/shellA.test 4ece22f204532fb04ab702c5b9c70d8680ff610bff1e82c849c16a7b5cd57d83
F test/shellB.test ca8a5ce5b9a59098732fa140c911162f0306f70239c6c2de6da9b718ca304cad
F test/shmlock.test 9f1f729a7fe2c46c88b156af819ac9b72c0714ac6f7246638a73c5752b5fd13c
F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3
F test/show_speedtest1_rtree.tcl 32e6c5f073d7426148a6936a0408f4b5b169aba5
@@ -1678,7 +1687,7 @@ F test/tabfunc01.test 56eeae736217204bb1d9f9ef38340d48058f809b64249217cf77ff4ba6
F test/table.test e87294bf1c80bfd7792142b84ab32ea5beb4f3f71e535d7fb263a6b2068377bf
F test/tableapi.test e37c33e6be2276e3a96bb54b00eea7f321277115d10e5b30fdb52a112b432750
F test/tableopts.test dba698ba97251017b7c80d738c198d39ab747930
F test/tclsqlite.test 3f697424cfc1cdc9c076ec0cadb0e700f059400a3e3ce134b7d856fc9f880e1c
F test/tclsqlite.test 5d6c73bfe7006c85e2f7fb7db8638b521eb2043d5451aaacdac4851eab895443
F test/tempdb.test 4cdaa23ddd8acb4d79cbb1b68ccdfd09b0537aaba909ca69a876157c2a2cbd08
F test/tempdb2.test 353864e96fd3ae2f70773d0ffbf8b1fe48589b02c2ec05013b540879410c3440
F test/tempfault.test 0c0d349c9a99bf5f374655742577f8712c647900
@@ -1690,7 +1699,7 @@ F test/tester.tcl 463ae33b8bf75ac77451df19bd65e7c415c2e9891227c7c9e657d0a2d8e107
F test/testloadext.c 862b848783eaed9985fbce46c65cd214664376b549fae252b364d5d1ef350a27
F test/testrunner.tcl 86179a8e78997e9257cb8f738c5624cb23897da5297855578ba74715e64f1602 x
F test/testrunner_data.tcl c507a9afa911c03446ed90442ffd4a98aca02882c3d51bd1177c24795674def8
F test/testrunner_estwork.tcl 7927a84327259a32854926f68a75292e33a61e7e052fdbfcb01f18696c99c724
F test/testrunner_estwork.tcl 81e2ae10238f50540f42fbf2d94913052a99bfb494b69e546506323f195dcff9
F test/thread001.test a0985c117eab62c0c65526e9fa5d1360dd1cac5b03bde223902763274ce21899
F test/thread002.test c24c83408e35ba5a952a3638b7ac03ccdf1ce4409289c54a050ac4c5f1de7502
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
@@ -2111,6 +2120,7 @@ F tool/merge-test.tcl de76b62f2de2a92d4c1ca4f976bce0aea6899e0229e250479b229b2a19
F tool/mkamalzip.tcl 8aa5ebe7973c8b8774062d34e15fea9815c4cc2ceea3a9b184695f005910876a
F tool/mkautoconfamal.sh 647dada5e34c466bef62a4408e1c99a7e5e1922805479dd57944f33f9803f2f8
F tool/mkccode.tcl c42a8f8cf78f92e83795d5447460dbce7aaf78a3bbf9082f1507dc71a3665f3c x
F tool/mkcombo.tcl 2a5189b219c4a495e1ff7fc980bd568d3cfb82ae9d50c84e77f7a161e96fc132
F tool/mkctimec.tcl 3fb5cad05922f5da61262cb6bcd5868a34e94a49ca8833ae2d7796e7df075576 x
F tool/mkkeywordhash.c 6b0be901c47f9ad42215fc995eb2f4384ac49213b1fba395102ec3e999acf559
F tool/mkmsvcmin.tcl d76c45efda1cce2d4005bcea7b8a22bb752e3256009f331120fb4fecb14ebb7a
@@ -2118,7 +2128,7 @@ F tool/mkopcodec.tcl 33d20791e191df43209b77d37f0ff0904620b28465cca6990cf8d60da61
F tool/mkopcodeh.tcl 2b4e6967a670ef21bf53a164964c35c6163277d002a4c6f56fa231d68c88d023
F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa
F tool/mkpragmatab.tcl 3801ce32f8c55fe63a3b279f231fb26c2c1a2ea9a09d2dd599239d87a609acec
F tool/mkshellc.tcl bab0a72a68384181a5706712dfdf6815f6526446d4e8aacace2de5e80cda91b2
F tool/mkshellc.tcl 1c9197524174237b580dc1a6d1552541c5f5b78162949b4b8e640a7aef688d40
F tool/mksourceid.c 36aa8020014aed0836fd13c51d6dc9219b0df1761d6b5f58ff5b616211b079b9
F tool/mksqlite3c-noext.tcl 351c55256213154cabb051a3c870ef9f4487de905015141ae50dc7578a901b84
F tool/mksqlite3c.tcl 7a268139158e5deef27a370bc2f8db6ccf100c1ad7ac5e5b23743c0fd354f609
@@ -2149,7 +2159,7 @@ F tool/split-sqlite3c.tcl 4969fd642dad0ea483e4e104163021d92baf98f6a8eac981fe4852
F tool/sqldiff.c 134be7866be19f8beb32043d5aea5657f01aaeae2df8d33d758ff722c78666b9
F tool/sqlite3_analyzer.c.in 14f02cb5ec3c264cd6107d1f1dad77092b1cf440fc196c30b69ae87b56a1a43b
F tool/sqlite3_rsync.c d0e58a1e49fe2192c3ee0b697aed182d502bebfe5b4b406ba6b2baa52a04ecbe
F tool/sqltclsh.c.in 1bcc2e9da58fadf17b0bf6a50e68c1159e602ce057210b655d50bad5aaaef898
F tool/sqltclsh.c.in c103c6fc7d42bce611f9d4596774d60b7ef3d0b291a1f58c9e6184e458b89296
F tool/sqltclsh.tcl 862f4cf1418df5e1315b5db3b5ebe88969e2a784525af5fbf9596592f14ed848
F tool/src-verify.c 6c655d9a8d6b30f3648fc78a79bf3838ed68f8543869d380c43ea9f17b3b8501
F tool/srcck1.c 559e703c6cca1d70398bdba1d7f91036c1a71adf718a1aaa6401a562ccaed154
@@ -2166,11 +2176,8 @@ F tool/version-info.c 33d0390ef484b3b1cb685d59362be891ea162123cea181cb8e6d2cf6dd
F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
P 6621737cc05cbf8ff5f576775a8a3c64f666b56d42939968ebb55d72a835646b
R d682117481a7b1cf594f7dd8ef6eff70
T *branch * max-subquery
T *sym-max-subquery *
T -sym-trunk *
P fd09f934d64ec07fd56f1b80ab05dafb28b605d5802ff5758eea17d8ad24e3cc
R 6cfd84a13cbe695177bf002c2876e553
U drh
Z 62a7b6290233b3d348ba184dec1ce219
Z ac921b72c830caadbe8526f354a297ee
# Remove this line to create a well-formed Fossil manifest.
+2 -2
View File
@@ -1,2 +1,2 @@
branch max-subquery
tag max-subquery
branch qrf
tag qrf
+1 -1
View File
@@ -1 +1 @@
35ec117e5a20f30030a37d2fbd059654bb13d0ec64fcf8719d71d9a4318dc970
3d55ec15a9e4dc8af4bf1e2884eaa2c809995fb1529633f73287dc7a54153629
-5
View File
@@ -5019,11 +5019,6 @@ int sqlite3SrcItemAttachSubquery(
Subquery *p;
assert( pSelect!=0 );
assert( pItem->fg.isSubquery==0 );
#if SQLITE_MAX_SUBQUERY+0 > 0
if( pParse->nSubquery++ > SQLITE_MAX_SUBQUERY ){
sqlite3ErrorMsg(pParse, "Too many subqueries");
}
#endif
if( pItem->fg.fixedSchema ){
pItem->u4.pSchema = 0;
pItem->fg.fixedSchema = 0;
+1 -1
View File
@@ -1230,7 +1230,7 @@ int sqlite3_str_length(sqlite3_str *p){
/* Truncate the text of the string to be no more than N bytes. */
void sqlite3_str_truncate(sqlite3_str *p, int N){
if( p!=0 && N>=0 && (u32)N<p->nChar ){
if( p!=0 && N>=0 && N<p->nChar ){
p->nChar = N;
p->zText[p->nChar] = 0;
}
+2671 -3535
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -8730,7 +8730,7 @@ sqlite3_str *sqlite3_str_new(sqlite3*);
**
** ^The [sqlite3_str_free(X)] interface destroys both the sqlite3_str object
** X and the string content it contains. Calling sqlite3_str_free(X) is
** the equivalent of calling [sqlite3_free](sqlite3_str_finish(X)).
** the equivalent of calling [sqlite3_free](sqlite3_str_finalize(X)).
*/
char *sqlite3_str_finish(sqlite3_str*);
void sqlite3_str_free(sqlite3_str*);
-3
View File
@@ -3942,9 +3942,6 @@ struct Parse {
yDbMask cookieMask; /* Bitmask of schema verified databases */
int nMaxArg; /* Max args to xUpdate and xFilter vtab methods */
int nSelect; /* Number of SELECT stmts. Counter for Select.selId */
#if SQLITE_MAX_SUBQUERY+0 > 0
u32 nSubquery; /* Number of subqueries seen so far */
#endif
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
u32 nProgressSteps; /* xProgress steps taken during sqlite3_prepare() */
#endif
+382 -17
View File
@@ -124,6 +124,15 @@
/* Forward declaration */
typedef struct SqliteDb SqliteDb;
/* Add -DSQLITE_ENABLE_QRF_IN_TCL to add the Query Result Formatter (QRF)
** into the build of the TCL extension, when building using separate
** source files. The QRF is included automatically when building from
** the tclsqlite3.c amalgamation.
*/
#if defined(SQLITE_ENABLE_QRF_IN_TCL)
#include "qrf.h"
#endif
/*
** New SQL functions can be created as TCL scripts. Each such function
** is described by an instance of the following structure.
@@ -2035,6 +2044,349 @@ static void DbHookCmd(
sqlite3_wal_hook(db, (pDb->pWalHook?DbWalHandler:0), pDb);
}
/*
** Implementation of the "db format" command.
**
** Based on provided options, format the results of the SQL statement(s)
** provided into human-readable form using the Query Result Formatter (QRF)
** and return the resuling text.
**
** Syntax: db format OPTIONS SQL
**
** OPTIONS may be:
**
** -style ("auto"|"box"|"column"|...) Output style
** -esc ("auto"|"off"|"ascii"|"symbol") How to deal with ctrl chars
** -text ("auto"|"off"|"sql"|"csv"|...) How to escape TEXT values
** -title ("auto"|"off"|"sql"|...|"off") How to escape column names
** -blob ("auto"|"text"|"sql"|...) How to escape BLOB values
** -wordwrap ("auto"|"off"|"on") Try to wrap at word boundry?
** -textjsonb ("auto"|"off"|"on") Auto-convert JSONB to text?
** -textnull ("auto"|"off"|"on") Use text encoding for -null.
** -defaultalign ("auto"|"left"|...) Default alignment
** -titalalign ("auto"|"left"|"right"|...) Default column name alignment
** -wrap NUMBER Max width of any single column
** -screenwidth NUMBER Width of the display TTY
** -linelimit NUMBER Max lines for any cell
** -charlimit NUMBER Content truncated to this size
** -align LIST-OF-ALIGNMENT Alignment of columns
** -widths LIST-OF-NUMBERS Widths for individual columns
** -columnsep TEXT Column separator text
** -rowsep TEXT Row separator text
** -tablename TEXT Table name for style "insert"
** -null TEXT Text for NULL values
**
** A mapping from TCL "format" command options to sqlite3_qrf_spec fields
** is below. Use this to reference the QRF documentation:
**
** TCL Option spec field
** ---------- ----------
** -style eStyle
** -esc eEsc
** -text eText
** -title eTitle, bTitle
** -blob eBlob
** -wordwrap bWordWrap
** -textjsonb bTextJsonb
** -textnull bTestNull
** -defaultalign eDfltAlign
** -titlealign eTitleAlign
** -wrap nWrap
** -screenwidth nScreenWidth
** -linelimit nLineLimit
** -charlimit nCharLimit
** -align nAlign, aAlign
** -widths nWidth, aWidth
** -columnsep zColumnSep
** -rowsep zRowSep
** -tablename zTableName
** -null zNull
*/
static int dbQrf(SqliteDb *pDb, int objc, Tcl_Obj *const*objv){
#ifndef SQLITE_QRF_H
Tcl_SetResult(pDb->interp, "QRF not available in this build", TCL_VOLATILE);
return TCL_ERROR;
#else
char *zResult = 0; /* Result to be returned */
const char *zSql = 0; /* SQL to run */
int i; /* Loop counter */
int rc; /* Result code */
sqlite3_qrf_spec qrf; /* Formatting spec */
static const char *azAlign[] = {
"auto", "bottom", "c",
"center", "e", "left",
"middle", "n", "ne",
"nw", "right", "s",
"se", "sw", "top",
"w", 0
};
static const unsigned char aAlignMap[] = {
QRF_ALIGN_Auto, QRF_ALIGN_Bottom, QRF_ALIGN_C,
QRF_ALIGN_Center, QRF_ALIGN_E, QRF_ALIGN_Left,
QRF_ALIGN_Middle, QRF_ALIGN_N, QRF_ALIGN_NE,
QRF_ALIGN_NW, QRF_ALIGN_Right, QRF_ALIGN_S,
QRF_ALIGN_SE, QRF_ALIGN_SW, QRF_ALIGN_Top,
QRF_ALIGN_W
};
memset(&qrf, 0, sizeof(qrf));
qrf.iVersion = 1;
qrf.pzOutput = &zResult;
for(i=2; i<objc; i++){
const char *zArg = Tcl_GetString(objv[i]);
const char *azBool[] = { "auto", "yes", "no", "on", "off", 0 };
const unsigned char aBoolMap[] = { 0, 2, 1, 2, 1 };
if( zArg[0]!='-' ){
if( zSql ){
Tcl_AppendResult(pDb->interp, "unknown argument: ", zArg, (char*)0);
rc = TCL_ERROR;
goto format_failed;
}
zSql = zArg;
}else if( i==objc-1 ){
Tcl_AppendResult(pDb->interp, "option has no argument: ", zArg, (char*)0);
rc = TCL_ERROR;
goto format_failed;
}else if( strcmp(zArg,"-style")==0 ){
static const char *azStyles[] = {
"auto", "box", "column",
"count", "csv", "eqp",
"explain", "html", "insert",
"jobject", "json", "line",
"list", "markdown", "quote",
"stats", "stats-est", "stats-vm",
"table", 0
};
static unsigned char aStyleMap[] = {
QRF_STYLE_Auto, QRF_STYLE_Box, QRF_STYLE_Column,
QRF_STYLE_Count, QRF_STYLE_Csv, QRF_STYLE_Eqp,
QRF_STYLE_Explain, QRF_STYLE_Html, QRF_STYLE_Insert,
QRF_STYLE_JObject, QRF_STYLE_Json, QRF_STYLE_Line,
QRF_STYLE_List, QRF_STYLE_Markdown, QRF_STYLE_Quote,
QRF_STYLE_Stats, QRF_STYLE_StatsEst, QRF_STYLE_StatsVm,
QRF_STYLE_Table
};
int style;
rc = Tcl_GetIndexFromObj(pDb->interp, objv[i+1], azStyles,
"format style (-style)", 0, &style);
if( rc ) goto format_failed;
qrf.eStyle = aStyleMap[style];
i++;
}else if( strcmp(zArg,"-esc")==0 ){
static const char *azEsc[] = {
"ascii", "auto", "off", "symbol", 0
};
static unsigned char aEscMap[] = {
QRF_ESC_Ascii, QRF_ESC_Auto, QRF_ESC_Off, QRF_ESC_Symbol
};
int esc;
rc = Tcl_GetIndexFromObj(pDb->interp, objv[i+1], azEsc,
"control character escape (-esc)", 0, &esc);
if( rc ) goto format_failed;
qrf.eEsc = aEscMap[esc];
i++;
}else if( strcmp(zArg,"-text")==0 || strcmp(zArg, "-title")==0 ){
/* NB: --title can be "off" or "on but --text may not be. Thus we put
** the "off" and "on" choices first and start the search on the
** thrid element of the array when processing --text */
static const char *azText[] = { "off", "on",
"auto", "csv", "html",
"json", "plain", "sql",
"tcl", 0
};
static unsigned char aTextMap[] = {
QRF_TEXT_Auto, QRF_TEXT_Csv, QRF_TEXT_Html,
QRF_TEXT_Json, QRF_TEXT_Plain, QRF_TEXT_Sql,
QRF_TEXT_Tcl
};
int txt;
int k = zArg[2]=='e';
rc = Tcl_GetIndexFromObj(pDb->interp, objv[i+1], &azText[k*2], zArg,
0, &txt);
if( rc ) goto format_failed;
if( k ){
qrf.eText = aTextMap[txt];
}else if( txt<=1 ){
qrf.bTitles = txt ? QRF_Yes : QRF_No;
qrf.eTitle = QRF_TEXT_Auto;
}else{
qrf.bTitles = QRF_Yes;
qrf.eTitle = aTextMap[txt-2];
}
i++;
}else if( strcmp(zArg,"-blob")==0 ){
static const char *azBlob[] = {
"auto", "hex", "json",
"tcl", "text", "sql", 0
};
static unsigned char aBlobMap[] = {
QRF_BLOB_Auto, QRF_BLOB_Hex, QRF_BLOB_Json,
QRF_BLOB_Tcl, QRF_BLOB_Text, QRF_BLOB_Sql
};
int blob;
rc = Tcl_GetIndexFromObj(pDb->interp, objv[i+1], azBlob,
"BLOB encoding (-blob)", 0, &blob);
if( rc ) goto format_failed;
qrf.eBlob = aBlobMap[blob];
i++;
}else if( strcmp(zArg,"-wordwrap")==0 ){
int v = 0;
rc = Tcl_GetIndexFromObj(pDb->interp, objv[i+1], azBool,
"-wordwrap", 0, &v);
if( rc ) goto format_failed;
qrf.bWordWrap = aBoolMap[v];
i++;
}else if( strcmp(zArg,"-textjsonb")==0 || strcmp(zArg,"-textnull")==0 ){
int v = 0;
rc = Tcl_GetIndexFromObj(pDb->interp, objv[i+1], azBool,
zArg, 0, &v);
if( rc ) goto format_failed;
if( zArg[5]=='j' ){
qrf.bTextJsonb = aBoolMap[v];
}else{
qrf.bTextNull = aBoolMap[v];
}
i++;
}else if( strcmp(zArg,"-defaultalign")==0 || strcmp(zArg,"-titlealign")==0){
int ax = 0;
rc = Tcl_GetIndexFromObj(pDb->interp, objv[i+1], azAlign,
zArg[1]=='d' ? "default alignment (-defaultalign)" :
"title alignment (-titlealign)",
0, &ax);
if( rc ) goto format_failed;
if( zArg[1]=='d' ){
qrf.eDfltAlign = aAlignMap[ax];
}else{
qrf.eTitleAlign = aAlignMap[ax];
}
i++;
}else if( strcmp(zArg,"-wrap")==0
|| strcmp(zArg,"-screenwidth")==0
|| strcmp(zArg,"-linelimit")==0
){
int v = 0;
rc = Tcl_GetIntFromObj(pDb->interp, objv[i+1], &v);
if( rc ) goto format_failed;
if( v<QRF_MIN_WIDTH ){
v = QRF_MIN_WIDTH;
}else if( v>QRF_MAX_WIDTH ){
v = QRF_MAX_WIDTH;
}
if( zArg[1]=='w' ){
qrf.nWrap = v;
}else if( zArg[1]=='s' ){
qrf.nScreenWidth = v;
}else{
qrf.nLineLimit = v;
}
i++;
}else if( strcmp(zArg,"-charlimit")==0 ){
int v = 0;
rc = Tcl_GetIntFromObj(pDb->interp, objv[i+1], &v);
if( rc ) goto format_failed;
if( v<0 ) v = 0;
qrf.nCharLimit = v;
i++;
}else if( strcmp(zArg,"-align")==0 ){
Tcl_Size n = 0;
int jj;
rc = Tcl_ListObjLength(pDb->interp, objv[i+1], &n);
if( rc ) goto format_failed;
sqlite3_free(qrf.aAlign);
qrf.aAlign = sqlite3_malloc64( (n+1)*sizeof(qrf.aAlign[0]) );
if( qrf.aAlign==0 ){
Tcl_AppendResult(pDb->interp, "out of memory", (char*)0);
rc = TCL_ERROR;
goto format_failed;
}
memset(qrf.aAlign, 0, (n+1)*sizeof(qrf.aAlign[0]));
qrf.nAlign = n;
for(jj=0; jj<n; jj++){
int x;
Tcl_Obj *pTerm;
rc = Tcl_ListObjIndex(pDb->interp, objv[i+1], jj, &pTerm);
if( rc ) goto format_failed;
rc = Tcl_GetIndexFromObj(pDb->interp, pTerm, azAlign,
"column alignment (-align)", 0, &x);
if( rc ) goto format_failed;
qrf.aAlign[jj] = aAlignMap[x];
}
i++;
}else if( strcmp(zArg,"-widths")==0 ){
Tcl_Size n = 0;
int jj;
rc = Tcl_ListObjLength(pDb->interp, objv[i+1], &n);
if( rc ) goto format_failed;
sqlite3_free(qrf.aWidth);
qrf.aWidth = sqlite3_malloc64( (n+1)*sizeof(qrf.aWidth[0]) );
if( qrf.aWidth==0 ){
Tcl_AppendResult(pDb->interp, "out of memory", (char*)0);
rc = TCL_ERROR;
goto format_failed;
}
memset(qrf.aWidth, 0, (n+1)*sizeof(qrf.aWidth[0]));
qrf.nWidth = n;
for(jj=0; jj<n; jj++){
Tcl_Obj *pTerm;
int v;
rc = Tcl_ListObjIndex(pDb->interp, objv[i+1], jj, &pTerm);
if( rc ) goto format_failed;
rc = Tcl_GetIntFromObj(pDb->interp, pTerm, &v);
if( v<(-QRF_MAX_WIDTH) ){
v = -QRF_MAX_WIDTH;
}else if( v>QRF_MAX_WIDTH ){
v = QRF_MAX_WIDTH;
}
qrf.aWidth[jj] = (short int)v;
}
i++;
}else if( strcmp(zArg,"-columnsep")==0 ){
qrf.zColumnSep = Tcl_GetString(objv[i+1]);
i++;
}else if( strcmp(zArg,"-rowsep")==0 ){
qrf.zRowSep = Tcl_GetString(objv[i+1]);
i++;
}else if( strcmp(zArg,"-tablename")==0 ){
qrf.zTableName = Tcl_GetString(objv[i+1]);
i++;
}else if( strcmp(zArg,"-null")==0 ){
qrf.zNull = Tcl_GetString(objv[i+1]);
i++;
}else{
Tcl_AppendResult(pDb->interp, "unknown option: ", zArg, (char*)0);
rc = TCL_ERROR;
goto format_failed;
}
}
while( zSql && zSql[0] ){
SqlPreparedStmt *pStmt = 0; /* Next statement to run */
char *zErr = 0; /* Error message from QRF */
rc = dbPrepareAndBind(pDb, zSql, &zSql, &pStmt);
if( rc ) goto format_failed;
if( pStmt==0 ) continue;
rc = sqlite3_format_query_result(pStmt->pStmt, &qrf, &zErr);
dbReleaseStmt(pDb, pStmt, 0);
if( rc ){
Tcl_SetResult(pDb->interp, zErr, TCL_VOLATILE);
sqlite3_free(zErr);
rc = TCL_ERROR;
goto format_failed;
}
}
Tcl_SetResult(pDb->interp, zResult, TCL_VOLATILE);
rc = TCL_OK;
/* Fall through...*/
format_failed:
sqlite3_free(qrf.aWidth);
sqlite3_free(qrf.aAlign);
sqlite3_free(zResult);
return rc;
#endif
}
/*
** The "sqlite" command below creates a new Tcl command for each
** connection it opens to an SQLite database. This routine is invoked
@@ -2064,15 +2416,15 @@ static int SQLITE_TCLAPI DbObjCmd(
"commit_hook", "complete", "config",
"copy", "deserialize", "enable_load_extension",
"errorcode", "erroroffset", "eval",
"exists", "function", "incrblob",
"interrupt", "last_insert_rowid", "nullvalue",
"onecolumn", "preupdate", "profile",
"progress", "rekey", "restore",
"rollback_hook", "serialize", "status",
"timeout", "total_changes", "trace",
"trace_v2", "transaction", "unlock_notify",
"update_hook", "version", "wal_hook",
0
"exists", "format", "function",
"incrblob", "interrupt", "last_insert_rowid",
"nullvalue", "onecolumn", "preupdate",
"profile", "progress", "rekey",
"restore", "rollback_hook", "serialize",
"status", "timeout", "total_changes",
"trace", "trace_v2", "transaction",
"unlock_notify", "update_hook", "version",
"wal_hook", 0
};
enum DB_enum {
DB_AUTHORIZER, DB_BACKUP, DB_BIND_FALLBACK,
@@ -2081,14 +2433,15 @@ static int SQLITE_TCLAPI DbObjCmd(
DB_COMMIT_HOOK, DB_COMPLETE, DB_CONFIG,
DB_COPY, DB_DESERIALIZE, DB_ENABLE_LOAD_EXTENSION,
DB_ERRORCODE, DB_ERROROFFSET, DB_EVAL,
DB_EXISTS, DB_FUNCTION, DB_INCRBLOB,
DB_INTERRUPT, DB_LAST_INSERT_ROWID, DB_NULLVALUE,
DB_ONECOLUMN, DB_PREUPDATE, DB_PROFILE,
DB_PROGRESS, DB_REKEY, DB_RESTORE,
DB_ROLLBACK_HOOK, DB_SERIALIZE, DB_STATUS,
DB_TIMEOUT, DB_TOTAL_CHANGES, DB_TRACE,
DB_TRACE_V2, DB_TRANSACTION, DB_UNLOCK_NOTIFY,
DB_UPDATE_HOOK, DB_VERSION, DB_WAL_HOOK,
DB_EXISTS, DB_FORMAT, DB_FUNCTION,
DB_INCRBLOB, DB_INTERRUPT, DB_LAST_INSERT_ROWID,
DB_NULLVALUE, DB_ONECOLUMN, DB_PREUPDATE,
DB_PROFILE, DB_PROGRESS, DB_REKEY,
DB_RESTORE, DB_ROLLBACK_HOOK, DB_SERIALIZE,
DB_STATUS, DB_TIMEOUT, DB_TOTAL_CHANGES,
DB_TRACE, DB_TRACE_V2, DB_TRANSACTION,
DB_UNLOCK_NOTIFY, DB_UPDATE_HOOK, DB_VERSION,
DB_WAL_HOOK
};
/* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */
@@ -2978,6 +3331,18 @@ deserialize_error:
break;
}
/*
** $db format [OPTIONS] SQL
**
** Run the SQL statement(s) given as the final argument. Use the
** Query Result Formatter extension of SQLite to format the output as
** text and return that text.
*/
case DB_FORMAT: {
rc = dbQrf(pDb, objc, objv);
break;
}
/*
** $db function NAME [OPTIONS] SCRIPT
**
+2
View File
@@ -332,9 +332,11 @@ do_test 4.3 {
set ofd [open $shdo w]
if {$::cliDoesAr} {
puts $ofd ".ar -u $shdo"
puts $ofd ".mode list"
puts $ofd "select count(*) from sqlar where name = '$shdo';"
} else {
puts $ofd "insert into sqlar values (1);"
puts $ofd ".mode list"
puts $ofd "select count(*) from sqlar;"
}
puts $ofd ".q"
+64
View File
@@ -0,0 +1,64 @@
# 2025-11-12
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# Test cases for the ".mode" command of the CLI.
# To run these tests:
#
# ./sqlite3 <test/modeA.clitest
#
#
CREATE TABLE t1(a,b,c,d,e);
INSERT INTO t1 VALUES(1,2.5,'three',x'4444',NULL);
INSERT INTO t1 SELECT b,c,d,e,a FROM t1;
INSERT INTO t1 SELECT d,e,a,b,c FROM t1;
.mode box
.output memory
SELECT * FROM t1;
.output --verify END
┌─────┬───────┬───────┬───────┬───────┐
│ a │ b │ c │ d │ e │
├─────┼───────┼───────┼───────┼───────┤
│ 1 │ 2.5 │ three │ DD │ │
│ 2.5 │ three │ DD │ │ 1 │
│ DD │ │ 1 │ 2.5 │ three │
│ │ 1 │ 2.5 │ three │ DD │
└─────┴───────┴───────┴───────┴───────┘
END
.output memory
.mode --null xyz
SELECT * FROM t1;
.output --verify END
┌─────┬───────┬───────┬───────┬───────┐
│ a │ b │ c │ d │ e │
├─────┼───────┼───────┼───────┼───────┤
│ 1 │ 2.5 │ three │ DD │ xyz │
│ 2.5 │ three │ DD │ xyz │ 1 │
│ DD │ xyz │ 1 │ 2.5 │ three │
│ xyz │ 1 │ 2.5 │ three │ DD │
└─────┴───────┴───────┴───────┴───────┘
END
.output memory --error-prefix "Error:"
.mode foo
.output --verify END
Error: .mode foo
Error: ^--- unknown mode
Error: Use ".help .mode" for more info
END
.output memory
.mode --null xyzzy -v
.output -glob ' --null "xyzzy"'
.output memory
.mode -null abcde -v
.output -glob ' --null "abcde"'
+893
View File
@@ -0,0 +1,893 @@
# 2025-11-05
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# Test cases for the Query Result Formatter (QRF)
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix qrf01
do_execsql_test 1.0 {
CREATE TABLE t1(a, b, c);
INSERT INTO t1 VALUES(1,2.5,'three'),(x'424c4f42',NULL,'Ἀμήν');
}
do_test 1.10 {
set result "\n[db format {SELECT * FROM t1}]"
} {
┌──────┬─────┬───────┐
│ a │ b │ c │
├──────┼─────┼───────┤
│ 1 │ 2.5 │ three │
│ BLOB │ │ Ἀμήν │
└──────┴─────┴───────┘
}
do_test 1.11a {
set result "\n[db format -title off {SELECT * FROM t1}]"
} {
┌──────┬─────┬───────┐
│ 1 │ 2.5 │ three │
│ BLOB │ │ Ἀμήν │
└──────┴─────┴───────┘
}
do_test 1.11b {
set result "\n[db format -text sql {SELECT * FROM t1}]"
} {
┌─────────────┬─────┬─────────┐
│ a │ b │ c │
├─────────────┼─────┼─────────┤
│ 1 │ 2.5 │ 'three' │
│ x'424c4f42' │ │ 'Ἀμήν' │
└─────────────┴─────┴─────────┘
}
do_test 1.12 {
set result "\n[db format -text csv {SELECT * FROM t1}]"
} {
┌────────────────────┬─────┬────────┐
│ a │ b │ c │
├────────────────────┼─────┼────────┤
│ 1 │ 2.5 │ three │
│ "\102\114\117\102" │ │ "Ἀμήν" │
└────────────────────┴─────┴────────┘
}
do_test 1.13 {
set result "\n[db format -text csv -blob hex {SELECT * FROM t1}]"
} {
┌──────────┬─────┬────────┐
│ a │ b │ c │
├──────────┼─────┼────────┤
│ 1 │ 2.5 │ three │
│ 424c4f42 │ │ "Ἀμήν" │
└──────────┴─────┴────────┘
}
do_test 1.14 {
catch {db format -text unk -blob hex {SELECT * FROM t1}} res
set res
} {bad -text "unk": must be auto, csv, html, json, plain, sql, or tcl}
do_test 1.15 {
catch {db format -text sql -blob unk {SELECT * FROM t1}} res
set res
} {bad BLOB encoding (-blob) "unk": must be auto, hex, json, tcl, text, or sql}
do_test 1.16 {
catch {db format -text sql -style unk {SELECT * FROM t1}} res
set res
} {bad format style (-style) "unk": must be auto, box, column, count, csv, eqp, explain, html, insert, jobject, json, line, list, markdown, quote, stats, stats-est, stats-vm, or table}
do_test 1.20 {
set result "\n[db format -style box {SELECT * FROM t1}]"
} {
┌──────┬─────┬───────┐
│ a │ b │ c │
├──────┼─────┼───────┤
│ 1 │ 2.5 │ three │
│ BLOB │ │ Ἀμήν │
└──────┴─────┴───────┘
}
do_test 1.30 {
set result "\n[db format -style table {SELECT * FROM t1}]"
} {
+------+-----+-------+
| a | b | c |
+------+-----+-------+
| 1 | 2.5 | three |
| BLOB | | Ἀμήν |
+------+-----+-------+
}
do_test 1.31 {
set result "\n[db format -style table -title off {SELECT * FROM t1}]"
} {
+------+-----+-------+
| 1 | 2.5 | three |
| BLOB | | Ἀμήν |
+------+-----+-------+
}
do_test 1.40 {
set result "\n[db format -style column {SELECT * FROM t1}]"
} {
a b c
---- --- -----
1 2.5 three
BLOB Ἀμήν
}
do_test 1.41 {
set result "\n[db format -style column -title off {SELECT * FROM t1}]"
} {
1 2.5 three
BLOB Ἀμήν
}
do_test 1.50 {
db format -style count {SELECT * FROM t1}
} 2
do_test 1.60a {
db format -style list -columnsep , -rowsep \r\n -text csv -blob tcl {SELECT * FROM t1}
} "1,2.5,three\r\n\"\\102\\114\\117\\102\",,\"Ἀμήν\"\r\n"
do_test 1.60b {
db format -style csv -columnsep xyz -rowsep pqr -text sql -blob sql {SELECT * FROM t1}
} "1,2.5,three\r\nBLOB,,\"Ἀμήν\"\r\n"
do_test 1.61a {
db format -style list -columnsep , -rowsep \r\n -text csv -title auto -blob tcl {SELECT * FROM t1}
} "a,b,c\r\n1,2.5,three\r\n\"\\102\\114\\117\\102\",,\"Ἀμήν\"\r\n"
do_test 1.61b {
db format -style csv -title auto -blob tcl {SELECT * FROM t1}
} "a,b,c\r\n1,2.5,three\r\nBLOB,,\"Ἀμήν\"\r\n"
do_test 1.62a {
db format -style list -columnsep , -rowsep \r\n -text csv -title csv -blob tcl {SELECT a AS 'a x y', b, c FROM t1}
} "\"a x y\",b,c\r\n1,2.5,three\r\n\"\\102\\114\\117\\102\",,\"Ἀμήν\"\r\n"
do_test 1.62b {
db format -style csv -title csv -blob tcl {SELECT a AS 'a x y', b, c FROM t1}
} "\"a x y\",b,c\r\n1,2.5,three\r\nBLOB,,\"Ἀμήν\"\r\n"
do_test 1.70 {
set result "\n[db format -style html {SELECT * FROM t1}]"
} {
<TR>
<TD>1
<TD>2.5
<TD>three
</TR>
<TR>
<TD>BLOB
<TD>null
<TD>Ἀμήν
</TR>
}
do_test 1.71 {
set result "\n[db format -style html -title auto {SELECT * FROM t1}]"
} {
<TR>
<TH>a
<TH>b
<TH>c
</TR>
<TR>
<TD>1
<TD>2.5
<TD>three
</TR>
<TR>
<TD>BLOB
<TD>null
<TD>Ἀμήν
</TR>
}
do_test 1.80 {
set result "\n[db format -style insert {SELECT * FROM t1}]"
} {
INSERT INTO tab VALUES(1,2.5,'three');
INSERT INTO tab VALUES(x'424c4f42',NULL,'Ἀμήν');
}
do_test 1.81 {
set result "\n[db format -style insert -tablename t1 {SELECT * FROM t1}]"
} {
INSERT INTO t1 VALUES(1,2.5,'three');
INSERT INTO t1 VALUES(x'424c4f42',NULL,'Ἀμήν');
}
do_test 1.82 {
set result "\n[db format -style insert -tablename t1 -title auto \
{SELECT * FROM t1}]"
} {
INSERT INTO t1(a,b,c) VALUES(1,2.5,'three');
INSERT INTO t1(a,b,c) VALUES(x'424c4f42',NULL,'Ἀμήν');
}
do_test 1.83 {
set result "\n[db format -style insert -tablename drop -title on \
{SELECT a AS "a-b", b, c AS "123" FROM t1}]"
} {
INSERT INTO "drop"("a-b",b,"123") VALUES(1,2.5,'three');
INSERT INTO "drop"("a-b",b,"123") VALUES(x'424c4f42',NULL,'Ἀμήν');
}
do_test 1.90 {
set result "\n[db format -style json {SELECT * FROM t1}]"
} {
[{"a":1,"b":2.5,"c":"three"},
{"a":"\u0042\u004c\u004f\u0042","b":null,"c":"Ἀμήν"}]
}
do_test 1.91 {
set result "\n[db format -style jobject {SELECT * FROM t1}]"
} {
{"a":1,"b":2.5,"c":"three"}
{"a":"\u0042\u004c\u004f\u0042","b":null,"c":"Ἀμήν"}
}
do_test 1.92 {
set result "\n[db format -style jobject {SELECT *, unistr('abc\u000a123\u000d\u000axyz') AS xyz FROM t1}]"
} {
{"a":1,"b":2.5,"c":"three","xyz":"abc\n123\r\nxyz"}
{"a":"\u0042\u004c\u004f\u0042","b":null,"c":"Ἀμήν","xyz":"abc\n123\r\nxyz"}
}
do_test 1.100 {
set result "\n[db format -style line {SELECT * FROM t1}]"
} {
a = 1
b = 2.5
c = three
a = BLOB
b =
c = Ἀμήν
}
do_test 1.101 {
set result "\n[db format -style line -null (NULL) {SELECT * FROM t1}]"
} {
a = 1
b = 2.5
c = three
a = BLOB
b = (NULL)
c = Ἀμήν
}
do_test 1.102 {
set result "\n[db format -style line -null (NULL) \
-text sql {SELECT * FROM t1}]"
} {
a = 1
b = 2.5
c = 'three'
a = x'424c4f42'
b = (NULL)
c = 'Ἀμήν'
}
do_test 1.110 {
set result "\n[db format -style list {SELECT * FROM t1}]"
} {
1|2.5|three
BLOB||Ἀμήν
}
do_test 1.111 {
set result "\n[db format -style list -title on {SELECT * FROM t1}]"
} {
a|b|c
1|2.5|three
BLOB||Ἀμήν
}
do_test 1.112 {
set result "\n[db format -style list -title on -text sql -null NULL \
-title plain {SELECT * FROM t1}]"
} {
a|b|c
1|2.5|'three'
x'424c4f42'|NULL|'Ἀμήν'
}
do_test 1.118 {
set rc [catch {db format -style list -title unk {SELECT * FROM t1}} res]
lappend rc $res
} {1 {bad -title "unk": must be off, on, auto, csv, html, json, plain, sql, or tcl}}
do_test 1.120 {
set result "\n[db format -style markdown {SELECT * FROM t1}]"
} {
| a | b | c |
|------|-----|-------|
| 1 | 2.5 | three |
| BLOB | | Ἀμήν |
}
do_test 1.121 {
set result "\n[db format -style markdown -title off {SELECT * FROM t1}]"
} {
| 1 | 2.5 | three |
| BLOB | | Ἀμήν |
}
do_test 1.130 {
set result "\n[db format -style quote {SELECT * FROM t1}]"
} {
1,2.5,'three'
x'424c4f42',NULL,'Ἀμήν'
}
do_test 1.131 {
set result "\n[db format -style quote -title on {SELECT * FROM t1}]"
} {
'a','b','c'
1,2.5,'three'
x'424c4f42',NULL,'Ἀμήν'
}
do_execsql_test 2.0 {
DELETE FROM t1;
INSERT INTO t1 VALUES(1,2,'The quick fox jumps over the lazy brown dog.');
}
do_test 2.1 {
set result "\n[db format -widths {5 -5 19} -wordwrap on \
{SELECT * FROM t1}]"
} {
┌───────┬───────┬─────────────────────┐
│ a │ b │ c │
├───────┼───────┼─────────────────────┤
│ 1 │ 2 │ The quick fox jumps │
│ │ │ over the lazy brown │
│ │ │ dog. │
└───────┴───────┴─────────────────────┘
}
do_test 2.2 {
set result "\n[db format -widths {5 -5 19} -wordwrap off \
{SELECT * FROM t1}]"
} {
┌───────┬───────┬─────────────────────┐
│ a │ b │ c │
├───────┼───────┼─────────────────────┤
│ 1 │ 2 │ The quick fox jumps │
│ │ │ over the lazy brown │
│ │ │ dog. │
└───────┴───────┴─────────────────────┘
}
do_test 2.3 {
set result "\n[db format -widths {5 -5 18} -wordwrap on \
{SELECT * FROM t1}]"
} {
┌───────┬───────┬────────────────────┐
│ a │ b │ c │
├───────┼───────┼────────────────────┤
│ 1 │ 2 │ The quick fox │
│ │ │ jumps over the │
│ │ │ lazy brown dog. │
└───────┴───────┴────────────────────┘
}
do_test 2.4 {
set result "\n[db format -widths {5 -5 -18} -wordwrap on \
{SELECT * FROM t1}]"
} {
┌───────┬───────┬────────────────────┐
│ a │ b │ c │
├───────┼───────┼────────────────────┤
│ 1 │ 2 │ The quick fox │
│ │ │ jumps over the │
│ │ │ lazy brown dog. │
└───────┴───────┴────────────────────┘
}
do_test 2.5 {
set result "\n[db format -widths {5 -5 19} -wordwrap off \
{SELECT * FROM t1}]"
} {
┌───────┬───────┬─────────────────────┐
│ a │ b │ c │
├───────┼───────┼─────────────────────┤
│ 1 │ 2 │ The quick fox jumps │
│ │ │ over the lazy brown │
│ │ │ dog. │
└───────┴───────┴─────────────────────┘
}
do_test 2.6 {
set result "\n[db format -widths {5 -5 18} -wordwrap off \
{SELECT * FROM t1}]"
} {
┌───────┬───────┬────────────────────┐
│ a │ b │ c │
├───────┼───────┼────────────────────┤
│ 1 │ 2 │ The quick fox jump │
│ │ │ s over the lazy br │
│ │ │ own dog. │
└───────┴───────┴────────────────────┘
}
do_test 2.7 {
set result "\n[db format -widths {5 5 18} -wordwrap yes \
-align {left center right} -titlealign right \
{SELECT * FROM t1}]"
} {
┌───────┬───────┬────────────────────┐
│ a │ b │ c │
├───────┼───────┼────────────────────┤
│ 1 │ 2 │ The quick fox │
│ │ │ jumps over the │
│ │ │ lazy brown dog. │
└───────┴───────┴────────────────────┘
}
do_test 2.8 {
set result "\n[db format -widths {5 8 11} -wordwrap yes \
-align {auto auto center} -titlealign left \
-defaultalign right \
{SELECT * FROM t1}]"
} {
┌───────┬──────────┬─────────────┐
│ a │ b │ c │
├───────┼──────────┼─────────────┤
│ 1 │ 2 │ The quick │
│ │ │ fox jumps │
│ │ │ over the │
│ │ │ lazy brown │
│ │ │ dog. │
└───────┴──────────┴─────────────┘
}
do_test 2.9 {
catch {db format -align {auto xyz 123} {SELECT * FROM t1}} res
set res
} {bad column alignment (-align) "xyz": must be auto, bottom, c, center, e, left, middle, n, ne, nw, right, s, se, sw, top, or w}
do_test 2.10 {
catch {db format -defaultalign xyz {SELECT * FROM t1}} res
set res
} {bad default alignment (-defaultalign) "xyz": must be auto, bottom, c, center, e, left, middle, n, ne, nw, right, s, se, sw, top, or w}
do_test 2.11 {
catch {db format -titlealign xyz {SELECT * FROM t1}} res
set res
} {bad title alignment (-titlealign) "xyz": must be auto, bottom, c, center, e, left, middle, n, ne, nw, right, s, se, sw, top, or w}
do_execsql_test 2.30 {
UPDATE t1 SET c='Η γρήγορη αλεπού πηδάει πάνω από το τεμπέλικο καφέ σκυλί';
SELECT hex(c) FROM t1;
} {CE9720CEB3CF81CEAECEB3CEBFCF81CEB720CEB1CEBBCEB5CF80CEBFCF8D20CF80CEB7CEB4CEACCEB5CEB920CF80CEACCEBDCF8920CEB1CF80CF8C20CF84CEBF20CF84CEB5CEBCCF80CEADCEBBCEB9CEBACEBF20CEBACEB1CF86CEAD20CF83CEBACF85CEBBCEAF}
do_test 2.31 {
set result "\n[db format -widths {5 -5 18} -wordwrap on \
{SELECT * FROM t1}]"
} {
┌───────┬───────┬────────────────────┐
│ a │ b │ c │
├───────┼───────┼────────────────────┤
│ 1 │ 2 │ Η γρήγορη αλεπού │
│ │ │ πηδάει πάνω από το │
│ │ │ τεμπέλικο καφέ │
│ │ │ σκυλί │
└───────┴───────┴────────────────────┘
}
do_test 2.32 {
set result "\n[db format -widths {5 5 18} -align {left center center} -wordwrap on \
{SELECT * FROM t1}]"
} {
┌───────┬───────┬────────────────────┐
│ a │ b │ c │
├───────┼───────┼────────────────────┤
│ 1 │ 2 │ Η γρήγορη αλεπού │
│ │ │ πηδάει πάνω από το │
│ │ │ τεμπέλικο καφέ │
│ │ │ σκυλί │
└───────┴───────┴────────────────────┘
}
do_execsql_test 3.0 {
DELETE FROM t1;
INSERT INTO t1 VALUES(1,2,unistr('abc\u001b[1;31m123\u001b[0mxyz'));
}
do_test 3.1 {
set result "\n[db format {SELECT * FROM t1}]"
} {
┌───┬───┬────────────────────────┐
│ a │ b │ c │
├───┼───┼────────────────────────┤
│ 1 │ 2 │ abc^[[1;31m123^[[0mxyz │
└───┴───┴────────────────────────┘
}
do_test 3.2 {
set result "\n[db format -esc off {SELECT * FROM t1}]"
string map [list \033 X] $result
} {
┌───┬───┬───────────┐
│ a │ b │ c │
├───┼───┼───────────┤
│ 1 │ 2 │ abcX[1;31m123X[0mxyz │
└───┴───┴───────────┘
}
do_test 3.3 {
set result "\n[db format -esc symbol {SELECT * FROM t1}]"
} {
┌───┬───┬──────────────────────┐
│ a │ b │ c │
├───┼───┼──────────────────────┤
│ 1 │ 2 │ abc␛[1;31m123␛[0mxyz │
└───┴───┴──────────────────────┘
}
do_test 3.4 {
set result "\n[db format -esc ascii {SELECT * FROM t1}]"
} {
┌───┬───┬────────────────────────┐
│ a │ b │ c │
├───┼───┼────────────────────────┤
│ 1 │ 2 │ abc^[[1;31m123^[[0mxyz │
└───┴───┴────────────────────────┘
}
do_test 3.5 {
catch {db format -esc unk {SELECT * FROM t1}} res
set res
} {bad control character escape (-esc) "unk": must be ascii, auto, off, or symbol}
do_execsql_test 4.0 {
DELETE FROM t1;
INSERT INTO t1 VALUES(json('{a:5,b:6}'), jsonb('{c:1,d:2}'), 99);
}
do_test 4.1 {
set result "\n[db format -text sql {SELECT * FROM t1}]"
} {
┌─────────────────┬───────────────────────┬────┐
│ a │ b │ c │
├─────────────────┼───────────────────────┼────┤
│ '{"a":5,"b":6}' │ x'8c1763133117641332' │ 99 │
└─────────────────┴───────────────────────┴────┘
}
do_test 4.2 {
set result "\n[db format -text sql -textjsonb on {SELECT * FROM t1}]"
} {
┌─────────────────┬────────────────────────┬────┐
│ a │ b │ c │
├─────────────────┼────────────────────────┼────┤
│ '{"a":5,"b":6}' │ jsonb('{"c":1,"d":2}') │ 99 │
└─────────────────┴────────────────────────┴────┘
}
do_test 4.3 {
set result "\n[db format -text plain -textjsonb on -wrap 11 \
{SELECT a AS json, b AS jsonb, c AS num FROM t1}]"
} {
┌─────────────┬─────────────┬─────┐
│ json │ jsonb │ num │
├─────────────┼─────────────┼─────┤
│ {"a":5,"b": │ {"c":1,"d": │ 99 │
│ 6} │ 2} │ │
└─────────────┴─────────────┴─────┘
}
do_execsql_test 5.0 {
DROP TABLE t1;
CREATE TABLE t1(name, mtime, value);
INSERT INTO t1 VALUES
('entry-one',1708791504,zeroblob(300)),
(unistr('one\u000atwo\u000athree'),1333206973,NULL),
('sample-jsonb',1333101221,jsonb('{
"alpha":53.11688723,
"beta":"qrfWidthPrint(p, p->pOut, -p->u.sLine.mxColWth);",
"zeta":[15,null,1333206973,"fd8ffe000104a46494600010101"]}'));
}
do_test 5.1 {
set sql {SELECT name, mtime, datetime(mtime,'unixepoch') AS time,
value FROM t1 ORDER BY mtime}
set result "\n[db format -style line -screenwidth 60 -blob sql \
-text sql -wordwrap off -linelimit 77 $sql]"
} {
name = 'sample-jsonb'
mtime = 1333101221
time = '2012-03-30 09:53:41'
value = x'cc7c57616c706861b535332e31313638383732334762657461
c73071726657696474685072696e7428702c20702d3e704f7574
2c202d702d3e752e734c696e652e6d78436f6c577468293b477a
657461cb2c23313500a331333333323036393733c71b66643866
6665303030313034613436343934363030303130313031'
name = unistr('one\u000atwo\u000athree')
mtime = 1333206973
time = '2012-03-31 15:16:13'
value =
name = 'entry-one'
mtime = 1708791504
time = '2024-02-24 16:18:24'
value = x'00000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000
000000000000000000000000000000'
}
do_test 5.2 {
set sql {SELECT name, mtime, datetime(mtime,'unixepoch') AS time,
value FROM t1 ORDER BY mtime}
set result "\n[db format -style line -screenwidth 60 -blob sql \
-text plain -esc off -textjsonb yes \
-wordwrap yes -linelimit 3 $sql]"
} {
name = sample-jsonb
mtime = 1333101221
time = 2012-03-30 09:53:41
value = {"alpha":53.11688723,"beta":"qrfWidthPrint(p,
p->pOut, -p->u.sLine.mxColWth);","zeta":[15,null,
1333206973,"fd8ffe000104a46494600010101"]}
name = one
two
three
mtime = 1333206973
time = 2012-03-31 15:16:13
value =
name = entry-one
mtime = 1708791504
time = 2024-02-24 16:18:24
value = x'00000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000
...
}
do_test 5.3a {
set result "\n[db format -style box -widths {0 10 10 14}\
-align {left right right center} \
-blob sql \
-text plain -esc off -textjsonb no \
-wordwrap yes -linelimit 2 $sql]"
} {
┌──────────────┬────────────┬────────────┬────────────────┐
│ name │ mtime │ time │ value │
├──────────────┼────────────┼────────────┼────────────────┤
│ sample-jsonb │ 1333101221 │ 2012-03-30 │ x'cc7c57616c70 │
│ │ │ 09:53:41 │ 6861b535332e31 │
│ │ │ │ ... │
├──────────────┼────────────┼────────────┼────────────────┤
│ one │ 1333206973 │ 2012-03-31 │ │
│ two │ │ 15:16:13 │ │
│ ... │ │ │ │
├──────────────┼────────────┼────────────┼────────────────┤
│ entry-one │ 1708791504 │ 2024-02-24 │ x'000000000000 │
│ │ │ 16:18:24 │ 00000000000000 │
│ │ │ │ ... │
└──────────────┴────────────┴────────────┴────────────────┘
}
do_test 5.3b {
set result "\n[db format -style table -widths {0 10 10 14}\
-align {center right right right} \
-blob sql \
-text plain -esc off -textjsonb no \
-wordwrap yes -linelimit 2 $sql]"
} {
+--------------+------------+------------+----------------+
| name | mtime | time | value |
+--------------+------------+------------+----------------+
| sample-jsonb | 1333101221 | 2012-03-30 | x'cc7c57616c70 |
| | | 09:53:41 | 6861b535332e31 |
| | | | ... |
+--------------+------------+------------+----------------+
| one | 1333206973 | 2012-03-31 | |
| two | | 15:16:13 | |
| ... | | | |
+--------------+------------+------------+----------------+
| entry-one | 1708791504 | 2024-02-24 | x'000000000000 |
| | | 16:18:24 | 00000000000000 |
| | | | ... |
+--------------+------------+------------+----------------+
}
do_test 5.3c {
set result "\n[db format -style column -widths {0 10 10 14}\
-align {center right right right} \
-blob sql \
-text plain -esc off -textjsonb no \
-wordwrap yes -linelimit 2 $sql]"
} {
name mtime time value
------------ ---------- ---------- --------------
sample-jsonb 1333101221 2012-03-30 x'cc7c57616c70
09:53:41 6861b535332e31
...
one 1333206973 2012-03-31
two 15:16:13
...
entry-one 1708791504 2024-02-24 x'000000000000
16:18:24 00000000000000
...
}
do_test 5.4 {
db eval {
CREATE TABLE t2(a,b,c,d,e);
WITH v(x) AS (SELECT 'abcdefghijklmnopqrstuvwxyz')
INSERT INTO t2 SELECT x,x,x,x,x FROM v;
}
set sql {SELECT char(0x61,0xa,0x62,0xa,0x63,0xa,0x64) a,
mtime b, mtime c, mtime d, mtime e FROM t1}
set result "\n[db format -style box -widths {1 2 3 4 5}\
-linelimit 3 -wordwrap off {SELECT *, 'x' AS x FROM t2}]"
} {
┌────┬────┬─────┬──────┬───────┬───┐
│ a │ b │ c │ d │ e │ x │
├────┼────┼─────┼──────┼───────┼───┤
│ ab │ ab │ abc │ abcd │ abcde │ x │
│ cd │ cd │ def │ efgh │ fghij │ │
│ ef │ ef │ ghi │ ijkl │ klmno │ │
│ .. │ .. │ ... │ ... │ ... │ │
└────┴────┴─────┴──────┴───────┴───┘
}
do_execsql_test 6.0 {
DELETE FROM t2;
INSERT INTO t2 VALUES
(1, 2.5, 'three', x'342028666f757229', null);
}
do_test 6.1a {
set result "\n[db format -style list -null NULL \
-text tcl -columnsep , \
{SELECT * FROM t2}]"
} {
1,2.5,"three","\064\040\050\146\157\165\162\051",NULL
}
do_test 6.1b {
set result "\n[db format -style list -null NULL \
-text tcl -columnsep , -textnull off \
{SELECT * FROM t2}]"
} {
1,2.5,"three","\064\040\050\146\157\165\162\051",NULL
}
do_test 6.1c {
set result "\n[db format -style list -null NULL \
-text tcl -columnsep , -textnull auto \
{SELECT * FROM t2}]"
} {
1,2.5,"three","\064\040\050\146\157\165\162\051",NULL
}
do_test 6.2 {
set result "\n[db format -style list -null NULL \
-text tcl -columnsep , -textnull yes \
{SELECT * FROM t2}]"
} {
1,2.5,"three","\064\040\050\146\157\165\162\051","NULL"
}
do_test 6.3 {
catch {db format -textnull xyz {SELECT * FROM t2}} res
set res
} {bad -textnull "xyz": must be auto, yes, no, on, or off}
do_execsql_test 7.0 {
CREATE TABLE t7(a,b);
INSERT INTO t7 VALUES('abcdefghijklmnop',
'abcぁdefかghiのjklはmnop');
}
do_test 7.1 {
set result "\n[db format -style list -charlimit 13 \
{SELECT * FROM t7}]"
} {
abcdefghijklm...|abcぁdefかghi...
}
do_test 7.2 {
set result "\n[db format -style list -charlimit 14 \
{SELECT * FROM t7}]"
} {
abcdefghijklmn...|abcぁdefかghi...
}
do_test 7.3 {
set result "\n[db format -style list -charlimit 15 \
{SELECT * FROM t7}]"
} {
abcdefghijklmno...|abcぁdefかghiの...
}
do_test 7.4 {
set result "\n[db format -style list -charlimit 16 \
{SELECT * FROM t7}]"
} {
abcdefghijklmnop|abcぁdefかghiのj...
}
do_test 8.0 {
set result "\n[db format -style table {
WITH c(n) AS (VALUES(1) UNION ALL SELECT n+1 FROM c WHERE n<10)
SELECT 'aaa' AS 'a', format('%.*c',n,'b')||char(9)||'xx' AS x FROM c}]"
} {
+-----+--------------------+
| a | x |
+-----+--------------------+
| aaa | b xx |
| aaa | bb xx |
| aaa | bbb xx |
| aaa | bbbb xx |
| aaa | bbbbb xx |
| aaa | bbbbbb xx |
| aaa | bbbbbbb xx |
| aaa | bbbbbbbb xx |
| aaa | bbbbbbbbb xx |
| aaa | bbbbbbbbbb xx |
+-----+--------------------+
}
do_test 8.1 {
set result "\n[db format -style table {
WITH c(n) AS (VALUES(1) UNION ALL SELECT n+1 FROM c WHERE n<10)
SELECT 'aaaa' AS 'a', format('%.*c',n,'b')||char(9)||'xx' AS x FROM c}]"
} {
+------+--------------------+
| a | x |
+------+--------------------+
| aaaa | b xx |
| aaaa | bb xx |
| aaaa | bbb xx |
| aaaa | bbbb xx |
| aaaa | bbbbb xx |
| aaaa | bbbbbb xx |
| aaaa | bbbbbbb xx |
| aaaa | bbbbbbbb xx |
| aaaa | bbbbbbbbb xx |
| aaaa | bbbbbbbbbb xx |
+------+--------------------+
}
do_test 8.3 {
set result "\n[db format -style table -esc off {
WITH c(n) AS (VALUES(1) UNION ALL SELECT n+1 FROM c WHERE n<15)
SELECT 'aaa' AS 'a',n, 'xx'||char(n)||'yy' AS xy FROM c
WHERE n NOT IN (8,10,13,14)}]"
} {
+-----+----+------------+
| a | n | xy |
+-----+----+------------+
| aaa | 1 | xx␁yy |
| aaa | 2 | xx␂yy |
| aaa | 3 | xx␃yy |
| aaa | 4 | xx␄yy |
| aaa | 5 | xx␅yy |
| aaa | 6 | xx␆yy |
| aaa | 7 | xx␇yy |
| aaa | 9 | xx yy |
| aaa | 11 | xx␋yy |
| aaa | 12 | xx␌yy |
| aaa | 15 | xx␏yy |
+-----+----+------------+
}
do_test 8.4 {
set result "\n[db format -style table -esc off {
WITH c(n) AS (VALUES(1) UNION ALL SELECT n+1 FROM c WHERE n<15)
SELECT 'aaa' AS 'a',n, 'xx'||char(n)||'yy'||char(9)||'zz' AS xyz FROM c
WHERE n NOT IN (8,10,13,14)}]"
} {
+-----+----+--------------------+
| a | n | xyz |
+-----+----+--------------------+
| aaa | 1 | xx␁yy zz |
| aaa | 2 | xx␂yy zz |
| aaa | 3 | xx␃yy zz |
| aaa | 4 | xx␄yy zz |
| aaa | 5 | xx␅yy zz |
| aaa | 6 | xx␆yy zz |
| aaa | 7 | xx␇yy zz |
| aaa | 9 | xx yy zz |
| aaa | 11 | xx␋yy zz |
| aaa | 12 | xx␌yy zz |
| aaa | 15 | xx␏yy zz |
+-----+----+--------------------+
}
do_test 9.1 {
db eval {
CREATE TABLE t9(x);
INSERT INTO t9 VALUES
(x'4331323334'),
(x'c30431323334'),
(x'd3000431323334'),
(x'e30000000431323334'),
(x'f3000000000000000431323334');
}
db format -style list -text plain -rowsep , -textjsonb on \
{SELECT * FROM t9}
} {1234,1234,1234,1234,1234,}
do_test 9.2 {
db format -style list -text sql -rowsep , -textjsonb on \
{SELECT * FROM t9}
} {jsonb('1234'),jsonb('1234'),jsonb('1234'),jsonb('1234'),jsonb('1234'),}
db close
finish_test
+47
View File
@@ -0,0 +1,47 @@
# 2025-11-05
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# Test cases for the Query Result Formatter (QRF)
#
# These tests are for EXPLAIN and EXPLAIN QUERY PLAN formatting, the
# output of which can change when enhancments are made to the query
# planner. So expect to have to modify the expected results of these
# test cases in the future.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix qrf02
do_execsql_test 1.0 {
CREATE TABLE t1(a);
INSERT INTO t1 VALUES(1);
}
set result [db format {EXPLAIN SELECT * FROM t1}]
do_test 1.10 {
set result
} {/*addr opcode p1 p2 p3 p4 p5 comment
---- ------------- ---- ---- ---- ------------- -- -------------
0 Init */}
regsub -all {\d+} $result {N} result2
do_test 1.11 {
set result2
} "/.*\nN Rewind .*\nN Column .*/"
do_test 1.20 {
set result "\n[db format {EXPLAIN QUERY PLAN SELECT * FROM t1}]"
} {
QUERY PLAN
`--SCAN t1
}
finish_test
+176
View File
@@ -0,0 +1,176 @@
# 2025-11-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.
#
#***********************************************************************
#
# Test cases for the Query Result Formatter (QRF)
#
# Format narrowing due to nScreenWidth
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix qrf03
do_execsql_test 1.0 {
CREATE TABLE mlink(
mid INTEGER,
fid INTEGER,
pmid INTEGER,
pid INTEGER,
fnid INTEGER REFERENCES filename,
pfnid INTEGER,
mperm INTEGER,
isaux BOOLEAN DEFAULT 0
);
INSERT INTO mlink VALUES(28775,28774,28773,28706,1,0,0,0);
INSERT INTO mlink VALUES(28773,28706,28770,28685,1,0,0,0);
INSERT INTO mlink VALUES(28770,28736,28769,28695,2,0,0,0);
INSERT INTO mlink VALUES(28770,28697,28769,28698,3,0,0,0);
INSERT INTO mlink VALUES(28767,28768,28759,28746,4,0,0,0);
CREATE TABLE event(
type TEXT,
mtime DATETIME,
objid INTEGER PRIMARY KEY,
tagid INTEGER,
uid INTEGER REFERENCES user,
bgcolor TEXT,
euser TEXT,
user TEXT,
ecomment TEXT,
comment TEXT,
brief TEXT,
omtime DATETIME
);
INSERT INTO event VALUES('ci',2460994.978048461023,126223,NULL,NULL,NULL,NULL,'drh',NULL,unistr('Data structure improvements on columnar layout. Prep work for getting\u000acolumnar layouts to respond to nScreenWidth.'),NULL,2460994.978048461023);
INSERT INTO event VALUES('ci',2460994.836955601816,126218,NULL,NULL,NULL,NULL,'stephan',NULL,'API doc typo fix.',NULL,2460994.836955601816);
INSERT INTO event VALUES('ci',2460994.88823369192,126212,NULL,NULL,NULL,NULL,'stephan',NULL,'Move sqlite3-api-cleanup.js into post-js-footer.js to remove the final direct Emscripten dependency from the intermediary build product sqlite3-api.js (the whole library, waiting to be bootstrapped). This is partly in response to [forum:4b7d45433731d2e0|forum post 4b7d45433731d2e0], which demonstrates a potential use case for a standalone sqlite3-api.js. This is a build/doc change, not a functional one.',NULL,2460994.88823369192);
INSERT INTO event VALUES('ci',2460994.516081551089,126211,NULL,NULL,NULL,NULL,'drh',NULL,unistr('Improve columnar layout in QRF so that it correctly deals with control\u000acharacters, and especially tabs.'),NULL,2460994.516081551089);
INSERT INTO event VALUES('ci',2460994.409343171865,126208,NULL,NULL,NULL,NULL,'drh',NULL,'Make use of the new sqlite3_str_free() interface in the CLI.',NULL,2460994.409343171865);
}
do_test 1.10 {
set x "\n[db format -style box -screenwidth 68 \
{SELECT * FROM mlink ORDER BY rowid}]"
} {
┌───────┬───────┬───────┬───────┬──────┬───────┬───────┬───────┐
│ mid │ fid │ pmid │ pid │ fnid │ pfnid │ mperm │ isaux │
├───────┼───────┼───────┼───────┼──────┼───────┼───────┼───────┤
│ 28775 │ 28774 │ 28773 │ 28706 │ 1 │ 0 │ 0 │ 0 │
│ 28773 │ 28706 │ 28770 │ 28685 │ 1 │ 0 │ 0 │ 0 │
│ 28770 │ 28736 │ 28769 │ 28695 │ 2 │ 0 │ 0 │ 0 │
│ 28770 │ 28697 │ 28769 │ 28698 │ 3 │ 0 │ 0 │ 0 │
│ 28767 │ 28768 │ 28759 │ 28746 │ 4 │ 0 │ 0 │ 0 │
└───────┴───────┴───────┴───────┴──────┴───────┴───────┴───────┘
}
do_test 1.11 {
set x "\n[db format -style box -screenwidth 52 \
{SELECT * FROM mlink ORDER BY rowid}]"
} {
┌─────┬─────┬─────┬─────┬────┬─────┬─────┬─────┐
│ mid │ fid │pmid │ pid │fnid│pfnid│mperm│isaux│
├─────┼─────┼─────┼─────┼────┼─────┼─────┼─────┤
│28775│28774│28773│28706│1 │0 │0 │0 │
│28773│28706│28770│28685│1 │0 │0 │0 │
│28770│28736│28769│28695│2 │0 │0 │0 │
│28770│28697│28769│28698│3 │0 │0 │0 │
│28767│28768│28759│28746│4 │0 │0 │0 │
└─────┴─────┴─────┴─────┴────┴─────┴─────┴─────┘
}
do_test 1.20 {
set x "\n[db format -style table -screenwidth 68 \
{SELECT * FROM mlink ORDER BY rowid}]"
} {
+-------+-------+-------+-------+------+-------+-------+-------+
| mid | fid | pmid | pid | fnid | pfnid | mperm | isaux |
+-------+-------+-------+-------+------+-------+-------+-------+
| 28775 | 28774 | 28773 | 28706 | 1 | 0 | 0 | 0 |
| 28773 | 28706 | 28770 | 28685 | 1 | 0 | 0 | 0 |
| 28770 | 28736 | 28769 | 28695 | 2 | 0 | 0 | 0 |
| 28770 | 28697 | 28769 | 28698 | 3 | 0 | 0 | 0 |
| 28767 | 28768 | 28759 | 28746 | 4 | 0 | 0 | 0 |
+-------+-------+-------+-------+------+-------+-------+-------+
}
do_test 1.21 {
set x "\n[db format -style table -screenwidth 52 \
{SELECT * FROM mlink ORDER BY rowid}]"
} {
+-----+-----+-----+-----+----+-----+-----+-----+
| mid | fid |pmid | pid |fnid|pfnid|mperm|isaux|
+-----+-----+-----+-----+----+-----+-----+-----+
|28775|28774|28773|28706|1 |0 |0 |0 |
|28773|28706|28770|28685|1 |0 |0 |0 |
|28770|28736|28769|28695|2 |0 |0 |0 |
|28770|28697|28769|28698|3 |0 |0 |0 |
|28767|28768|28759|28746|4 |0 |0 |0 |
+-----+-----+-----+-----+----+-----+-----+-----+
}
do_test 1.30 {
set x "\n[db format -style markdown -screenwidth 68 \
{SELECT * FROM mlink ORDER BY rowid}]"
} {
| mid | fid | pmid | pid | fnid | pfnid | mperm | isaux |
|-------|-------|-------|-------|------|-------|-------|-------|
| 28775 | 28774 | 28773 | 28706 | 1 | 0 | 0 | 0 |
| 28773 | 28706 | 28770 | 28685 | 1 | 0 | 0 | 0 |
| 28770 | 28736 | 28769 | 28695 | 2 | 0 | 0 | 0 |
| 28770 | 28697 | 28769 | 28698 | 3 | 0 | 0 | 0 |
| 28767 | 28768 | 28759 | 28746 | 4 | 0 | 0 | 0 |
}
do_test 1.31 {
set x "\n[db format -style markdown -screenwidth 52 \
{SELECT * FROM mlink ORDER BY rowid}]"
} {
| mid | fid |pmid | pid |fnid|pfnid|mperm|isaux|
|-----|-----|-----|-----|----|-----|-----|-----|
|28775|28774|28773|28706|1 |0 |0 |0 |
|28773|28706|28770|28685|1 |0 |0 |0 |
|28770|28736|28769|28695|2 |0 |0 |0 |
|28770|28697|28769|28698|3 |0 |0 |0 |
|28767|28768|28759|28746|4 |0 |0 |0 |
}
do_test 1.40 {
set x "\n[db format -style column -screenwidth 68 \
{SELECT * FROM mlink ORDER BY rowid}]"
} {
mid fid pmid pid fnid pfnid mperm isaux
----- ----- ----- ----- ---- ----- ----- -----
28775 28774 28773 28706 1 0 0 0
28773 28706 28770 28685 1 0 0 0
28770 28736 28769 28695 2 0 0 0
28770 28697 28769 28698 3 0 0 0
28767 28768 28759 28746 4 0 0 0
}
do_test 1.41 {
set x "\n[db format -style column -screenwidth 52 \
{SELECT * FROM mlink ORDER BY rowid}]"
} {
mid fid pmid pid fnid pfnid mperm isaux
----- ----- ----- ----- ---- ----- ----- -----
28775 28774 28773 28706 1 0 0 0
28773 28706 28770 28685 1 0 0 0
28770 28736 28769 28695 2 0 0 0
28770 28697 28769 28698 3 0 0 0
28767 28768 28759 28746 4 0 0 0
}
finish_test
+1 -1
View File
@@ -42,7 +42,7 @@ proc compare_dbs {db1 db2} {
proc recover_with_opts {opts} {
set cmd ".recover $opts"
set fd [open [list |$::CLI test.db $cmd]]
set fd [open [list |$::CLI -noinit test.db $cmd]]
fconfigure $fd -translation binary
set sql [read $fd]
close $fd
+89 -72
View File
@@ -48,7 +48,7 @@ do_test shell1-1.1.1b {
} {1 1}
# error on extra options
do_test shell1-1.1.2 {
catchcmd "test.db \"select+3\" \"select+4\"" ""
catchcmd "-compat 20250101 test.db \"select+3\" \"select+4\"" ""
} {0 {3
4}}
# error on extra options
@@ -182,7 +182,7 @@ do_test shell1-1.17.1 {
catchcmd {-- --db "CREATE TABLE T(c1);"}
} {0 {}}
do_test shell1-1.17.2 {
catchcmd {-- --db "SELECT name from sqlite_schema;"}
catchcmd {-compat 20250101 -- --db "SELECT name from sqlite_schema;"}
} {0 T}
forcedelete ./--db
@@ -216,10 +216,14 @@ do_test shell1-2.2.4 {
} {0 {}}
do_test shell1-2.2.5 {
catchcmd "test.db" ".mode \"insert FOO"
} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}}
} {1 {line 1: .mode "insert FOO
line 1: ^--- unknown mode
line 1: Use ".help .mode" for more info}}
do_test shell1-2.2.6 {
catchcmd "test.db" ".mode \'insert FOO"
} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}}
} {1 {line 1: .mode 'insert FOO
line 1: ^--- unknown mode
line 1: Use ".help .mode" for more info}}
# check multiple tokens, and quoted tokens
do_test shell1-2.3.1 {
@@ -247,7 +251,9 @@ do_test shell1-2.3.7 {
# check quoted args are unquoted
do_test shell1-2.4.1 {
catchcmd "test.db" ".mode FOO"
} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}}
} {1 {line 1: .mode FOO
line 1: ^--- unknown mode
line 1: Use ".help .mode" for more info}}
do_test shell1-2.4.2 {
catchcmd "test.db" ".mode csv"
} {0 {}}
@@ -364,7 +370,6 @@ do_test shell1-3.7.4 {
catchcmd "test.db" ".explain OFF BAD"
} {0 {}}
# .header(s) ON|OFF Turn display of headers on or off
do_test shell1-3.9.1 {
catchcmd "test.db" ".header"
@@ -400,7 +405,7 @@ do_test shell1-3.10.1 {
# look for a few of the possible help commands
list [regexp {.help} $res] \
[regexp {.quit} $res] \
[regexp {.show} $res]
[regexp {.mode} $res]
} {1 1 1}
do_test shell1-3.10.2 {
# we allow .help to take extra args (it is help after all)
@@ -408,7 +413,7 @@ do_test shell1-3.10.2 {
# look for a few of the possible help commands
list [regexp {.help} $res] \
[regexp {.quit} $res] \
[regexp {.show} $res]
[regexp {.mode} $res]
} {1 1 1}
# .import FILE TABLE Import data from FILE into TABLE
@@ -421,7 +426,8 @@ do_test shell1-3.11.2 {
do_test shell1-3.11.3 {
# too many arguments
catchcmd "test.db" ".import FOO BAR BAD"
} {/1 .ERROR: extra argument: "BAD".*./}
} {1 {line 1: .import FOO BAR BAD
line 1: ^--- unknown argument}}
# .indexes ?TABLE? Show names of all indexes
# If TABLE specified, only show indexes for tables
@@ -451,11 +457,13 @@ do_test shell1-3.12.3 {
# tabs Tab-separated values
# tcl TCL list elements
do_test shell1-3.13.1 {
catchcmd "test.db" ".mode"
} {0 {current output mode: list --escape ascii}}
catchcmd "test.db" ".mode batch\n.mode"
} {0 {current output mode: list}}
do_test shell1-3.13.2 {
catchcmd "test.db" ".mode FOO"
} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}}
} {1 {line 1: .mode FOO
line 1: ^--- unknown mode
line 1: Use ".help .mode" for more info}}
do_test shell1-3.13.3 {
catchcmd "test.db" ".mode csv"
} {0 {}}
@@ -503,7 +511,7 @@ do_test shell1-3.15.1 {
.print x"
} {0 x}
do_test shell1-3.15.2 {
catchcmd "test.db" ".output FOO
catchcmd "test.db" ".mode batch\n.output FOO
.print x
.output
SELECT readfile('FOO');"
@@ -512,17 +520,8 @@ SELECT readfile('FOO');"
do_test shell1-3.15.3 {
# too many arguments
catchcmd "test.db" ".output FOO BAD"
} {1 {ERROR: extra parameter: "BAD". Usage:
.output ?FILE? Send output to FILE or stdout if FILE is omitted
If FILE begins with '|' then open it as a pipe.
If FILE is 'off' then output is disabled.
Options:
--bom Prefix output with a UTF8 byte-order mark
-e Send output to the system text editor
--plain Use text/plain for -w option
-w Send output to a web browser
-x Send output as CSV to a spreadsheet
child process exited abnormally}}
} {1 {line 1: .output FOO BAD
line 1: ^--- surplus argument}}
# .output stdout Send output to the screen
do_test shell1-3.16.1 {
@@ -531,17 +530,8 @@ do_test shell1-3.16.1 {
do_test shell1-3.16.2 {
# too many arguments
catchcmd "test.db" ".output stdout BAD"
} {1 {ERROR: extra parameter: "BAD". Usage:
.output ?FILE? Send output to FILE or stdout if FILE is omitted
If FILE begins with '|' then open it as a pipe.
If FILE is 'off' then output is disabled.
Options:
--bom Prefix output with a UTF8 byte-order mark
-e Send output to the system text editor
--plain Use text/plain for -w option
-w Send output to a web browser
-x Send output as CSV to a spreadsheet
child process exited abnormally}}
} {1 {line 1: .output stdout BAD
line 1: ^--- surplus argument}}
# .prompt MAIN CONTINUE Replace the standard prompts
do_test shell1-3.17.1 {
@@ -643,7 +633,7 @@ do_test shell1-3.22.4 {
# .show Show the current values for various settings
do_test shell1-3.23.1 {
set res [catchcmd "test.db" ".show"]
set res [catchcmd "test.db" ".mode batch\n.show"]
list [regexp {echo:} $res] \
[regexp {explain:} $res] \
[regexp {headers:} $res] \
@@ -679,7 +669,7 @@ do_test shell1-3.23b.4 {
# Adverse interaction between .stats and .eqp
#
do_test shell1-3.23b.5 {
catchcmd "test.db" [string map {"\n " "\n"} {
catchcmd "test.db" [string map {"\n " "\n"} {.mode batch
CREATE TEMP TABLE t1(x);
INSERT INTO t1 VALUES(1),(2);
.stats on
@@ -762,9 +752,9 @@ do_test shell1-3.27.4 {
catchcmd "test.db" ".timer OFF BAD"
} {1 {Usage: .timer on|off}}
do_test shell1-3-28.1 {
do_test shell1-3.28.1 {
catchcmd test.db \
".log stdout\nSELECT coalesce(sqlite_log(123,'hello'),'456');"
".mode batch\n.log stdout\nSELECT coalesce(sqlite_log(123,'hello'),'456');"
} "0 {(123) hello\n456}"
do_test shell1-3-29.1 {
@@ -803,14 +793,14 @@ INSERT INTO t1 VALUES('');
INSERT INTO t1 VALUES(1);
INSERT INTO t1 VALUES(2.25);
INSERT INTO t1 VALUES('hello');
INSERT INTO t1 VALUES(X'807f');
INSERT INTO t1 VALUES(x'807f');
CREATE TABLE t3(x,y);
INSERT INTO t3 VALUES(1,NULL);
INSERT INTO t3 VALUES(2,'');
INSERT INTO t3 VALUES(3,1);
INSERT INTO t3 VALUES(4,2.25);
INSERT INTO t3 VALUES(5,'hello');
INSERT INTO t3 VALUES(6,X'807f');
INSERT INTO t3 VALUES(6,x'807f');
COMMIT;}}
@@ -828,14 +818,14 @@ INSERT INTO t1(rowid,x) VALUES(2,'');
INSERT INTO t1(rowid,x) VALUES(3,1);
INSERT INTO t1(rowid,x) VALUES(4,2.25);
INSERT INTO t1(rowid,x) VALUES(5,'hello');
INSERT INTO t1(rowid,x) VALUES(6,X'807f');
INSERT INTO t1(rowid,x) VALUES(6,x'807f');
CREATE TABLE t3(x,y);
INSERT INTO t3(rowid,x,y) VALUES(1,1,NULL);
INSERT INTO t3(rowid,x,y) VALUES(2,2,'');
INSERT INTO t3(rowid,x,y) VALUES(3,3,1);
INSERT INTO t3(rowid,x,y) VALUES(4,4,2.25);
INSERT INTO t3(rowid,x,y) VALUES(5,5,'hello');
INSERT INTO t3(rowid,x,y) VALUES(6,6,X'807f');
INSERT INTO t3(rowid,x,y) VALUES(6,6,x'807f');
COMMIT;}}
# If the table contains an INTEGER PRIMARY KEY, do not record a separate
@@ -854,12 +844,12 @@ do_test shell1-4.1.2 {
} {0 {PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE t1(x INTEGER PRIMARY KEY, y);
INSERT INTO t1 VALUES(1,NULL);
INSERT INTO t1 VALUES(2,'');
INSERT INTO t1 VALUES(3,1);
INSERT INTO t1 VALUES(4,2.25);
INSERT INTO t1 VALUES(5,'hello');
INSERT INTO t1 VALUES(6,X'807f');
INSERT INTO t1(x,y) VALUES(1,NULL);
INSERT INTO t1(x,y) VALUES(2,'');
INSERT INTO t1(x,y) VALUES(3,1);
INSERT INTO t1(x,y) VALUES(4,2.25);
INSERT INTO t1(x,y) VALUES(5,'hello');
INSERT INTO t1(x,y) VALUES(6,x'807f');
COMMIT;}}
# Verify that the table named [table] is correctly quoted and that
@@ -883,7 +873,7 @@ INSERT INTO "table"(rowid,x,y) VALUES(2,12,'');
INSERT INTO "table"(rowid,x,y) VALUES(3,23,1);
INSERT INTO "table"(rowid,x,y) VALUES(4,34,2.25);
INSERT INTO "table"(rowid,x,y) VALUES(5,45,'hello');
INSERT INTO "table"(rowid,x,y) VALUES(6,56,X'807f');
INSERT INTO "table"(rowid,x,y) VALUES(6,56,x'807f');
COMMIT;}}
# Do not record rowids for a WITHOUT ROWID table. Also check correct quoting
@@ -902,12 +892,12 @@ do_test shell1-4.1.4 {
} {0 {PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE [ta<>ble](x INTEGER PRIMARY KEY, y) WITHOUT ROWID;
INSERT INTO "ta<>ble" VALUES(1,NULL);
INSERT INTO "ta<>ble" VALUES(12,'');
INSERT INTO "ta<>ble" VALUES(23,1);
INSERT INTO "ta<>ble" VALUES(34,2.25);
INSERT INTO "ta<>ble" VALUES(45,'hello');
INSERT INTO "ta<>ble" VALUES(56,X'807f');
INSERT INTO "ta<>ble"(x,y) VALUES(1,NULL);
INSERT INTO "ta<>ble"(x,y) VALUES(12,'');
INSERT INTO "ta<>ble"(x,y) VALUES(23,1);
INSERT INTO "ta<>ble"(x,y) VALUES(34,2.25);
INSERT INTO "ta<>ble"(x,y) VALUES(45,'hello');
INSERT INTO "ta<>ble"(x,y) VALUES(56,x'807f');
COMMIT;}}
# Do not record rowids if the rowid is inaccessible
@@ -924,9 +914,9 @@ do_test shell1-4.1.5 {
} {0 {PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE t1(_ROWID_,rowid,oid);
INSERT INTO t1 VALUES(1,NULL,'alpha');
INSERT INTO t1 VALUES(12,'',99);
INSERT INTO t1 VALUES(23,1,X'b0b1b2');
INSERT INTO t1(_ROWID_,rowid,oid) VALUES(1,NULL,'alpha');
INSERT INTO t1(_ROWID_,rowid,oid) VALUES(12,'',99);
INSERT INTO t1(_ROWID_,rowid,oid) VALUES(23,1,x'b0b1b2');
COMMIT;}}
} else {
@@ -1020,7 +1010,7 @@ INSERT INTO t1 VALUES('');
INSERT INTO t1 VALUES(1);
INSERT INTO t1 VALUES(2.25);
INSERT INTO t1 VALUES('hello');
INSERT INTO t1 VALUES(X'807f');}}
INSERT INTO t1 VALUES(x'807f');}}
# Test the output of ".mode insert" with headers
#
@@ -1031,7 +1021,7 @@ INSERT INTO t1(x) VALUES('');
INSERT INTO t1(x) VALUES(1);
INSERT INTO t1(x) VALUES(2.25);
INSERT INTO t1(x) VALUES('hello');
INSERT INTO t1(x) VALUES(X'807f');}}
INSERT INTO t1(x) VALUES(x'807f');}}
# Test the output of ".mode insert"
#
@@ -1042,7 +1032,7 @@ INSERT INTO t3 VALUES(2,'');
INSERT INTO t3 VALUES(3,1);
INSERT INTO t3 VALUES(4,2.25);
INSERT INTO t3 VALUES(5,'hello');
INSERT INTO t3 VALUES(6,X'807f');}}
INSERT INTO t3 VALUES(6,x'807f');}}
# Test the output of ".mode insert" with headers
#
@@ -1053,7 +1043,7 @@ INSERT INTO t3(x,y) VALUES(2,'');
INSERT INTO t3(x,y) VALUES(3,1);
INSERT INTO t3(x,y) VALUES(4,2.25);
INSERT INTO t3(x,y) VALUES(5,'hello');
INSERT INTO t3(x,y) VALUES(6,X'807f');}}
INSERT INTO t3(x,y) VALUES(6,x'807f');}}
# Test the output of ".mode tcl"
#
@@ -1069,8 +1059,8 @@ do_test shell1-4.3 {
catchcmd test.db ".mode tcl\nselect * from t1;"
} {0 {""
""
"1"
"2.25"
1
2.25
"hello"
"\200\177"}}
@@ -1083,15 +1073,15 @@ do_test shell1-4.4 {
}
catchcmd test.db ".mode tcl\nselect * from t2;"
} {0 {"" ""
"1" "2.25"
1 2.25
"hello" "\200\177"}}
# Test the output of ".mode tcl" with ".nullvalue"
#
do_test shell1-4.5 {
catchcmd test.db ".mode tcl\n.nullvalue NULL\nselect * from t2;"
} {0 {"NULL" ""
"1" "2.25"
} {0 {NULL ""
1 2.25
"hello" "\200\177"}}
# Test the output of ".mode tcl" with Tcl reserved characters
@@ -1115,7 +1105,7 @@ do_test shell1-4.6 {
#
do_test shell1-4.7 {
catchcmd test.db ".mode quote\nselect x'0123456789ABCDEF';"
} {0 X'0123456789abcdef'}
} {0 x'0123456789abcdef'}
# Test using arbitrary byte data with the shell via standard input/output.
#
@@ -1274,7 +1264,7 @@ do_test shell1-7.1.7 {
# information.
#
do_test shell1-8.1 {
catchcmd ":memory:" {
catchcmd ":memory:" {.mode batch
-- The pow2 table will hold all the necessary powers of two.
CREATE TABLE pow2(x INTEGER PRIMARY KEY, v TEXT);
WITH RECURSIVE c(x,v) AS (
@@ -1326,6 +1316,7 @@ do_test_with_ansi_output shell1-8.5 {
create table t(a text, b int);
insert into t values ('too long for one line', 1), ('shorter', NULL);
.header on
.mode box --wordwrap off
.width 10 10
.nullvalue NADA
select * from t;}
@@ -1344,12 +1335,12 @@ select * from t;}
#
do_test shell1-9.1 {
catchcmd :memory: {
.mode csv
.mode csv --rowsep "\n"
/*
x */ select 1,2; --x
-- .nada
;
.mode csv
.mode csv --rowsep "\n"
--x
select 2,1; select 3,4;
}
@@ -1387,4 +1378,30 @@ select base85(zeroblob(2000000000));
}
} {/1.*too big.*/}
#----------------------------------------------------------------------------
# As of 2025-11-17, the default mode is:
#
# qbox --screenwidth auto --linelimit 5 --charlimit 300 --textjsonb on
#
do_test shell1-12.1 {
catchcmd :memory: {.mode tty
.print
SELECT jsonb(1234) AS x;}
} {0 {
┌───────────────┐
│ x │
├───────────────┤
│ jsonb('1234') │
└───────────────┘}}
do_test shell1-12.2 {
catchcmd :memory: {.mode box --textjsonb on
.print
SELECT jsonb(1234) AS x;}
} {0 {
┌──────┐
│ x │
├──────┤
│ 1234 │
└──────┘}}
finish_test
+79 -9
View File
@@ -44,7 +44,7 @@ do_test shell2-1.1.1 {
# Shell silently ignores extra parameters.
# Ticket [f5cb008a65].
do_test shell2-1.2.1 {
catchcmdex {:memory: "select+3" "select+4"}
catchcmdex {:memory: -list "select+3" "select+4"}
} {0 {3
4
}}
@@ -64,7 +64,7 @@ do_test shell2-1.3 {
UPDATE OR REPLACE t5 SET a = 4 WHERE a = 1;
}
} {1 {Runtime error near line 9: too many levels of trigger recursion}}
} {1 {Error near line 9: too many levels of trigger recursion}}
@@ -75,7 +75,8 @@ do_test shell2-1.3 {
# NB. whitespace is important
do_test shell2-1.4.1 {
forcedelete foo.db
catchcmd "foo.db" {CREATE TABLE foo(a);
catchcmd "foo.db" {.mode batch
CREATE TABLE foo(a);
INSERT INTO foo(a) VALUES(1);
SELECT * FROM foo;}
} {0 1}
@@ -84,7 +85,7 @@ SELECT * FROM foo;}
# NB. whitespace is important
do_test shell2-1.4.2 {
forcedelete foo.db
catchcmd "-echo foo.db" {CREATE TABLE foo(a);
catchcmd "-compat 20250101 -echo foo.db" {CREATE TABLE foo(a);
INSERT INTO foo(a) VALUES(1);
SELECT * FROM foo;}
} {0 {CREATE TABLE foo(a);
@@ -96,7 +97,9 @@ SELECT * FROM foo;
# NB. whitespace is important
do_test shell2-1.4.3 {
forcedelete foo.db
catchcmd "foo.db" {.echo ON
catchcmd "foo.db" {
.mode batch
.echo ON
CREATE TABLE foo(a);
INSERT INTO foo(a) VALUES(1);
SELECT * FROM foo;}
@@ -110,7 +113,9 @@ SELECT * FROM foo;
# NB. whitespace is important
do_test shell2-1.4.4 {
forcedelete foo.db
catchcmd "foo.db" {.echo ON
catchcmd "foo.db" {
.mode batch
.echo ON
CREATE TABLE foo(a);
.echo OFF
INSERT INTO foo(a) VALUES(1);
@@ -124,7 +129,9 @@ SELECT * FROM foo;}
# NB. whitespace is important
do_test shell2-1.4.5 {
forcedelete foo.db
catchcmdex "foo.db" {.echo ON
catchcmdex "foo.db" {
.mode batch
.echo ON
CREATE TABLE foo1(a);
INSERT INTO foo1(a) VALUES(1);
CREATE TABLE foo2(b);
@@ -153,7 +160,9 @@ SELECT * FROM foo1; SELECT * FROM foo2;
# NB. whitespace is important
do_test shell2-1.4.6 {
forcedelete foo.db
catchcmdex "foo.db" {.echo ON
catchcmdex "foo.db" {
.mode batch
.echo ON
.headers ON
CREATE TABLE foo1(a);
INSERT INTO foo1(a) VALUES(1);
@@ -208,6 +217,7 @@ do_test shell2-1.4.9 {
do_test shell2-1.4.9 {
forcedelete clone.db
set res [catchcmd :memory: [string trim {
.mode batch
CREATE TABLE t(id INTEGER PRIMARY KEY AUTOINCREMENT);
INSERT INTO t VALUES (1),(2);
.clone clone.db
@@ -222,6 +232,7 @@ ifcapable vtab {
# See overflow report at https://sqlite.org/forum/forumpost/5d34ce5280
do_test shell2-1.4.10 {
set res [catchcmd :memory: [string trim {
.mode batch
SELECT * FROM generate_series(9223372036854775807,9223372036854775807,1);
SELECT * FROM generate_series(9223372036854775807,9223372036854775807,-1);
SELECT avg(value),min(value),max(value) FROM generate_series(
@@ -248,6 +259,65 @@ do_test shell2-1.4.10 {
0
1
2}}
do_test shell2-1.4.10b {
set res [catchcmd :memory: [string trim {
.mode tty
.print
SELECT * FROM generate_series(9223372036854775807,9223372036854775807,1);
SELECT * FROM generate_series(9223372036854775807,9223372036854775807,-1);
SELECT avg(value),min(value),max(value) FROM generate_series(
-9223372036854775808,9223372036854775807,1085102592571150095);
SELECT * FROM generate_series(-9223372036854775808,9223372036854775807,
9223372036854775807);
SELECT value FROM generate_series(-4611686018427387904,
4611686018427387904, 4611686018427387904) ORDER BY value DESC;
SELECT * FROM generate_series(0,-2,-1);
SELECT * FROM generate_series(0,-2);
SELECT * FROM generate_series(0,2) LIMIT 3;}]]
} {0 {
┌─────────────────────┐
│ value │
├─────────────────────┤
│ 9223372036854775807 │
└─────────────────────┘
┌─────────────────────┐
│ value │
├─────────────────────┤
│ 9223372036854775807 │
└─────────────────────┘
┌────────────┬──────────────────────┬─────────────────────┐
│ avg(value) │ min(value) │ max(value) │
├────────────┼──────────────────────┼─────────────────────┤
│ -0.5 │ -9223372036854775808 │ 9223372036854775807 │
└────────────┴──────────────────────┴─────────────────────┘
┌──────────────────────┐
│ value │
├──────────────────────┤
│ -9223372036854775808 │
│ -1 │
│ 9223372036854775806 │
└──────────────────────┘
┌──────────────────────┐
│ value │
├──────────────────────┤
│ 4611686018427387904 │
│ 0 │
│ -4611686018427387904 │
└──────────────────────┘
┌───────┐
│ value │
├───────┤
│ 0 │
│ -1 │
│ -2 │
└───────┘
┌───────┐
│ value │
├───────┤
│ 0 │
│ 1 │
│ 2 │
└───────┘}}
} ;# ifcapable vtab
ifcapable vtab {
@@ -270,7 +340,7 @@ do_test shell2-1.4.11 {
# Bug from forum post 7cbe081746dd3803
# Keywords as column names were producing an error message.
do_test shell2-1.4.12 {
set res [catchcmd :memory: [string trim {
set res [catchcmd :memory: [string trim {.mode batch
CREATE TABLE "group"("order" text);
INSERT INTO "group" VALUES ('ABC');
.sha3sum}]]
+1 -1
View File
@@ -109,7 +109,7 @@ do_test shell3-2.7 {
# Run combinations of odd identifiers, comments, semicolon placement
do_test shell3-3.1 {
forcedelete foo.db
set rc [ catchcmd "foo.db" {CREATE TABLE t1("
set rc [ catchcmd "foo.db -compat 20250101" {CREATE TABLE t1("
a--.
" --x
); CREATE TABLE t2("a[""b""]");
+4 -4
View File
@@ -136,15 +136,15 @@ SELECT * FROM t1;}
do_test shell4-3.1 {
set fd [open t1.txt wb]
puts $fd "SELECT 'squirrel';"
puts $fd ".mode list\nSELECT 'squirrel';"
close $fd
exec $::CLI_ONLY :memory: --interactive ".read t1.txt"
exec $::CLI_ONLY --noinit :memory: --interactive ".read t1.txt"
} {squirrel}
do_test_with_ansi_output shell4-3.2 {
set fd [open t1.txt wb]
puts $fd "SELECT 'pound: \302\243';"
puts $fd ".mode list\nSELECT 'pound: \302\243';"
close $fd
exec $::CLI_ONLY :memory: --interactive ".read t1.txt"
exec $::CLI_ONLY --noinit :memory: --interactive ".read t1.txt"
} {pound: £}
do_test shell4-4.1 {
+29 -21
View File
@@ -40,7 +40,8 @@ do_test shell5-1.1.2 {
do_test shell5-1.1.3 {
# too many arguments
catchcmd "test.db" ".import FOO BAR BAD"
} {/1 .ERROR: extra argument.*/}
} {1 {line 1: .import FOO BAR BAD
line 1: ^--- unknown argument}}
# .separator STRING Change separator used by output mode and .import
do_test shell5-1.2.1 {
@@ -59,13 +60,13 @@ do_test shell5-1.2.4 {
# column separator should default to "|"
do_test shell5-1.3.1.1 {
set res [catchcmd "test.db" ".show"]
set res [catchcmd "test.db" ".mode list\n.show"]
list [regexp {colseparator: \"\|\"} $res]
} {1}
# row separator should default to "\n"
do_test shell5-1.3.1.2 {
set res [catchcmd "test.db" ".show"]
set res [catchcmd "test.db" ".mode list\n.show"]
list [regexp {rowseparator: \"\\n\"} $res]
} {1}
@@ -97,7 +98,9 @@ do_test shell5-1.4.2 {
forcedelete shell5.csv
set in [open shell5.csv w]
close $in
set res [catchcmd ":memory:" {ATTACH 'test.db' AS test;
set res [catchcmd ":memory:" {
.mode list
ATTACH 'test.db' AS test;
.import -schema test shell5.csv t1
SELECT COUNT(*) FROM test.t1;}]
} {0 0}
@@ -116,7 +119,7 @@ do_test shell5-1.4.4 {
set in [open shell5.csv w]
puts $in "1|2|3"
close $in
set res [catchcmd ":memory:" {ATTACH 'test.db' AS test;
set res [catchcmd ":memory: --list" {ATTACH 'test.db' AS test;
.import --schema test shell5.csv t1}]
} {1 {shell5.csv:1: expected 2 columns but found 3 - extras ignored}}
@@ -125,7 +128,7 @@ do_test shell5-1.4.5 {
set in [open shell5.csv w]
puts $in "1|2"
close $in
set res [catchcmd "test.db" {DELETE FROM t1;
set res [catchcmd "test.db -list" {DELETE FROM t1;
.import shell5.csv t1
SELECT COUNT(*) FROM t1;}]
} {0 1}
@@ -138,7 +141,9 @@ do_test shell5-1.4.6 {
puts $in "2|3"
puts $in "3|4"
close $in
set res [catchcmd ":memory:" {ATTACH 'test.db' AS test;
set res [catchcmd ":memory:" {
.mode list
ATTACH 'test.db' AS test;
.import -schema test shell5.csv t1
SELECT COUNT(*) FROM test.t1;}]
} {0 3}
@@ -148,7 +153,9 @@ do_test shell5-1.4.7 {
set in [open shell5.csv w]
puts $in "4,5"
close $in
set res [catchcmd ":memory:" {ATTACH 'test.db' AS test;
set res [catchcmd ":memory:" {
.mode list
ATTACH 'test.db' AS test;
.separator ,
.import --schema test shell5.csv t1
SELECT COUNT(*) FROM test.t1;}]
@@ -159,12 +166,13 @@ do_test shell5-1.4.8.1 {
set in [open shell5.csv w]
puts $in "5|Now is the time for all good men to come to the aid of their country."
close $in
set res [catchcmd "test.db" {.import shell5.csv t1
set res [catchcmd "test.db" {.mode list
.import shell5.csv t1
SELECT COUNT(*) FROM t1;}]
} {0 5}
do_test shell5-1.4.8.2 {
catchcmd "test.db" {SELECT b FROM t1 WHERE a='5';}
catchcmd "test.db -list" {SELECT b FROM t1 WHERE a='5';}
} {0 {Now is the time for all good men to come to the aid of their country.}}
# import file with 1 row, 2 columns, quoted text data
@@ -174,12 +182,12 @@ do_test shell5-1.4.9.1 {
set in [open shell5.csv w]
puts $in "6|'Now is the time for all good men to come to the aid of their country.'"
close $in
set res [catchcmd "test.db" {.import shell5.csv t1
set res [catchcmd "test.db -list" {.import shell5.csv t1
SELECT COUNT(*) FROM t1;}]
} {0 6}
do_test shell5-1.4.9.2 {
catchcmd "test.db" {SELECT b FROM t1 WHERE a='6';}
catchcmd "test.db -list" {SELECT b FROM t1 WHERE a='6';}
} {0 {'Now is the time for all good men to come to the aid of their country.'}}
# import file with 1 row, 2 columns, quoted text data
@@ -187,12 +195,12 @@ do_test shell5-1.4.10.1 {
set in [open shell5.csv w]
puts $in "7|\"Now is the time for all good men to come to the aid of their country.\""
close $in
set res [catchcmd "test.db" {.import shell5.csv t1
set res [catchcmd "test.db -list" {.import shell5.csv t1
SELECT COUNT(*) FROM t1;}]
} {0 7}
do_test shell5-1.4.10.2 {
catchcmd "test.db" {SELECT b FROM t1 WHERE a='7';}
catchcmd "test.db -list" {SELECT b FROM t1 WHERE a='7';}
} {0 {Now is the time for all good men to come to the aid of their country.}}
# import file with 2 rows, 2 columns and an initial BOM
@@ -203,7 +211,7 @@ do_test shell5-1.4.11 {
puts $in "2|3"
puts $in "4|5"
close $in
set res [catchcmd "test.db" {CREATE TABLE t2(x INT, y INT);
set res [catchcmd "test.db -list" {CREATE TABLE t2(x INT, y INT);
.import shell5.csv t2
.mode quote
.header on
@@ -218,7 +226,7 @@ do_test shell5-1.4.12 {
puts $in "\xef\xbb\xbf\"two\"|3"
puts $in "4|5"
close $in
set res [catchcmd "test.db" {DELETE FROM t2;
set res [catchcmd "test.db -list" {DELETE FROM t2;
.import shell5.csv t2
.mode quote
.header on
@@ -232,7 +240,7 @@ do_test shell5-1.5.1 {
set in [open shell5.csv w]
puts $in "8|$str"
close $in
set res [catchcmd "test.db" {.import shell5.csv t1
set res [catchcmd "test.db -list" {.import shell5.csv t1
SELECT length(b) FROM t1 WHERE a='8';}]
} {0 999}
@@ -252,7 +260,7 @@ do_test shell5-1.6.1 {
set in [open shell5.csv w]
puts $in $data
close $in
set res [catchcmd "test.db" {DROP TABLE IF EXISTS t2;
set res [catchcmd "test.db -list" {DROP TABLE IF EXISTS t2;
.import shell5.csv t2
SELECT COUNT(*) FROM t2;}]
} {0 1}
@@ -576,8 +584,8 @@ do_test_with_ansi_output shell5-6.2 {
catchcmd test.db {.import -csv shell5.csv t1
.mode line
SELECT * FROM t1;}
} {0 { 1 = あい
2 = うえお}}
} {0 {1 = あい
2 = うえお}}
# 2024-03-11 https://sqlite.org/forum/forumpost/ca014d7358
# Import into a table that contains computed columns.
@@ -588,7 +596,7 @@ do_test shell5-7.1 {
puts $out {aaa|bbb}
close $out
forcedelete test.db
catchcmd :memory: {CREATE TABLE t1(a TEXT, b TEXT, c AS (a||b));
catchcmd ":memory: -list" {CREATE TABLE t1(a TEXT, b TEXT, c AS (a||b));
.import shell5.csv t1
SELECT * FROM t1;}
} {0 aaa|bbb|aaabbb}
+27 -31
View File
@@ -36,7 +36,7 @@ do_execsql_test shellA-1.0 {
# and that our calls to the CLI are working.
#
do_test_with_ansi_output shellA-1.2 {
exec {*}$CLI test.db {.mode box --escape symbol} {SELECT * FROM t1;}
exec {*}$CLI -noinit test.db {.mode box -quote off --escape symbol} {SELECT * FROM t1;}
} {
┌───┬──────────────────────────┐
│ a │ x │
@@ -63,33 +63,33 @@ do_test_with_ansi_output shellA-1.2 {
# ".mode list"
#
do_test shellA-1.3 {
exec {*}$CLI test.db {SELECT x FROM t1 WHERE a=2;}
exec {*}$CLI -noinit -list test.db {SELECT x FROM t1 WHERE a=2;}
} {
^[[31mVT-100 codes^[[0m
}
do_test_with_ansi_output shellA-1.4 {
exec {*}$CLI test.db --escape symbol {SELECT x FROM t1 WHERE a=2;}
exec {*}$CLI -noinit -list test.db --escape symbol {SELECT x FROM t1 WHERE a=2;}
} {
␛[31mVT-100 codes␛[0m
}
do_test shellA-1.5 {
exec {*}$CLI test.db --escape ascii {SELECT x FROM t1 WHERE a=2;}
exec {*}$CLI -noinit -list test.db --escape ascii {SELECT x FROM t1 WHERE a=2;}
} {
^[[31mVT-100 codes^[[0m
}
do_test_with_ansi_output shellA-1.6 {
exec {*}$CLI test.db {.mode list --escape symbol} {SELECT x FROM t1 WHERE a=2;}
exec {*}$CLI -noinit test.db {.mode list --escape symbol} {SELECT x FROM t1 WHERE a=2;}
} {
␛[31mVT-100 codes␛[0m
}
do_test shellA-1.7 {
exec {*}$CLI test.db {.mode list --escape ascii} {SELECT x FROM t1 WHERE a=2;}
exec {*}$CLI -noinit test.db {.mode list --escape ascii} {SELECT x FROM t1 WHERE a=2;}
} {
^[[31mVT-100 codes^[[0m
}
do_test shellA-1.8 {
file delete -force out.txt
exec {*}$CLI test.db {.mode list --escape off} {SELECT x FROM t1 WHERE a=7;} \
exec {*}$CLI -noinit test.db {.mode list --escape off} {SELECT x FROM t1 WHERE a=7;} \
>out.txt
set fd [open out.txt rb]
set res [read $fd]
@@ -98,44 +98,45 @@ do_test shellA-1.8 {
} "carriage\rreturn"
do_test shellA-1.9 {
set rc [catch {
exec {*}$CLI test.db {.mode test --escape xyz}
exec {*}$CLI -noinit test.db {.mode test --escape xyz}
} msg]
lappend rc $msg
} {1 {unknown control character escape mode "xyz" - choices: ascii symbol off}}
} {1 {argv[3]: .mode test --escape xyz
argv[3]: ^--- unknown mode
argv[3]: Use ".help .mode" for more info}}
do_test shellA-1.10 {
set rc [catch {
exec {*}$CLI --escape abc test.db .q
exec {*}$CLI --noinit --escape abc test.db .q
} msg]
lappend rc $msg
} {1 {unknown control character escape mode "abc" - choices: ascii symbol off}}
} {1 {unknown control character escape mode "abc" - choices: auto off ascii symbol}}
# ".mode quote"
#
do_test shellA-2.1 {
exec {*}$CLI test.db --quote {SELECT a, x FROM t1 WHERE a IN (1,2,6,7,8)}
exec {*}$CLI -noinit test.db --quote {SELECT a, x FROM t1 WHERE a IN (1,2,6,7,8)}
} {
1,'line with '' single quote'
2,unistr('\u001b[31mVT-100 codes\u001b[0m')
6,'new
line'
6,unistr('new\u000aline')
7,unistr('carriage\u000dreturn')
8,'last line'
}
do_test shellA-2.2 {
exec {*}$CLI test.db --quote {.mode}
} {current output mode: quote --escape ascii}
exec {*}$CLI -noinit test.db --quote {.mode -v}
} {/*current output mode: quote* --escape auto*/}
do_test shellA-2.3 {
exec {*}$CLI test.db --quote --escape SYMBOL {.mode}
exec {*}$CLI -noinit -compat 20250101 test.db --quote --escape SYMBOL {.mode}
} {current output mode: quote --escape symbol}
do_test shellA-2.4 {
exec {*}$CLI test.db --quote --escape OFF {.mode}
exec {*}$CLI -noinit -compat 20250101 test.db --quote --escape OFF {.mode}
} {current output mode: quote --escape off}
# ".mode line"
#
do_test_with_ansi_output shellA-3.1 {
exec {*}$CLI test.db --line --escape symbol \
exec {*}$CLI -noinit test.db --line --escape symbol \
{SELECT a, x FROM t1 WHERE a IN (1,2,6,7,8)}
} {
a = 1
@@ -155,7 +156,7 @@ line
x = last line
}
do_test shellA-3.2 {
exec {*}$CLI test.db --line --escape ascii \
exec {*}$CLI -noinit test.db --line --escape ascii \
{SELECT a, x FROM t1 WHERE a IN (1,2,6,7,8)}
} {
a = 1
@@ -178,7 +179,7 @@ line
# ".mode box"
#
do_test_with_ansi_output shellA-4.1 {
exec {*}$CLI test.db --box --escape ascii \
exec {*}$CLI -noinit test.db --box --escape ascii \
{SELECT a, x FROM t1 WHERE a IN (1,2,6,7,8)}
} {
┌───┬──────────────────────────┐
@@ -197,20 +198,15 @@ do_test_with_ansi_output shellA-4.1 {
└───┴──────────────────────────┘
}
do_test_with_ansi_output shellA-4.2 {
exec {*}$CLI test.db {.mode qbox} {SELECT a, x FROM t1 WHERE a IN (1,2,6,7,8)}
exec {*}$CLI -noinit test.db {.mode qbox} {SELECT a, x FROM t1 WHERE a IN (1,2,6,7,8)}
} {
┌───┬───────────────────────────────────────────┐
│ a │ x │
├───┼───────────────────────────────────────────┤
│ 1 │ 'line with '' single quote' │
├───┼───────────────────────────────────────────┤
│ 2 │ unistr('\u001b[31mVT-100 codes\u001b[0m') │
├───┼───────────────────────────────────────────┤
│ 6 │ 'new │
│ │ line' │
├───┼───────────────────────────────────────────┤
│ 6 │ unistr('new\u000aline') │
│ 7 │ unistr('carriage\u000dreturn') │
├───┼───────────────────────────────────────────┤
│ 8 │ 'last line' │
└───┴───────────────────────────────────────────┘
}
@@ -218,7 +214,7 @@ do_test_with_ansi_output shellA-4.2 {
# ".mode insert"
#
do_test shellA-5.1 {
exec {*}$CLI test.db {.mode insert t1 --escape ascii} \
exec {*}$CLI -noinit test.db {.mode insert t1 --escape ascii} \
{SELECT a, x FROM t1 WHERE a IN (1,2,6,7,8)}
} {
INSERT INTO t1 VALUES(1,'line with '' single quote');
@@ -228,7 +224,7 @@ INSERT INTO t1 VALUES(7,unistr('carriage\u000dreturn'));
INSERT INTO t1 VALUES(8,'last line');
}
do_test shellA-5.2 {
exec {*}$CLI test.db {.mode insert t1 --escape symbol} \
exec {*}$CLI -noinit test.db {.mode insert t1 --escape symbol} \
{SELECT a, x FROM t1 WHERE a IN (1,2,6,7,8)}
} {
INSERT INTO t1 VALUES(1,'line with '' single quote');
@@ -239,7 +235,7 @@ INSERT INTO t1 VALUES(8,'last line');
}
do_test shellA-5.3 {
file delete -force out.txt
exec {*}$CLI test.db {.mode insert t1 --escape off} \
exec {*}$CLI -noinit test.db {.mode insert t1 --escape off} \
{SELECT a, x FROM t1 WHERE a IN (1,2,6,7,8)} >out.txt
set fd [open out.txt rb]
set res [read $fd]
+44
View File
@@ -0,0 +1,44 @@
# 2025-11-12
#
# 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: shell
#
# Test cases for the command-line shell using the new ".output memory"
# feature.
#
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set CLI [test_cli_invocation]
# Run an instance of the CLI on the file $name.
# Capture the number of test cases and the number of
# errors and increment the counts.
#
proc do_clitest {name} {
set mapping [list <NAME> $::testdir/$name <CLI> $::CLI]
set script [string map $mapping {
catch {exec <CLI> :memory: ".read <NAME>" 2>@stdout} res
set ntest 0
set nerr 999
regexp {.*(\d+) tests? run with (\d+) errors?} $res all ntest nerr
set_test_counter count [expr {[set_test_counter count]+$ntest-1}]
set_test_counter errors [expr {[set_test_counter errors]+$nerr}]
if {$nerr==0} {set res "error count: 0"}
set res
}]
# puts $script
do_test shellB-$name $script {error count: 0}
}
do_clitest modeA.clitest
finish_test
+1 -1
View File
@@ -42,7 +42,7 @@ do_test tcl-1.1.1 {
do_test tcl-1.2 {
set v [catch {db bogus} msg]
lappend v $msg
} {1 {bad option "bogus": must be authorizer, backup, bind_fallback, busy, cache, changes, close, collate, collation_needed, commit_hook, complete, config, copy, deserialize, enable_load_extension, errorcode, erroroffset, eval, exists, function, incrblob, interrupt, last_insert_rowid, nullvalue, onecolumn, preupdate, profile, progress, rekey, restore, rollback_hook, serialize, status, timeout, total_changes, trace, trace_v2, transaction, unlock_notify, update_hook, version, or wal_hook}}
} {1 {bad option "bogus": must be authorizer, backup, bind_fallback, busy, cache, changes, close, collate, collation_needed, commit_hook, complete, config, copy, deserialize, enable_load_extension, errorcode, erroroffset, eval, exists, format, function, incrblob, interrupt, last_insert_rowid, nullvalue, onecolumn, preupdate, profile, progress, rekey, restore, rollback_hook, serialize, status, timeout, total_changes, trace, trace_v2, transaction, unlock_notify, update_hook, version, or wal_hook}}
do_test tcl-1.2.1 {
set v [catch {db cache bogus} msg]
lappend v $msg
+1
View File
@@ -364,6 +364,7 @@ set estwork(shell6.test) 3
set estwork(shell8.test) 104
set estwork(shell9.test) 3
set estwork(shellA.test) 2
set estwork(shellB.test) 2
set estwork(shmlock.test) 27
set estwork(sidedelete.test) 10
set estwork(skipscan1.test) 7
+106
View File
@@ -0,0 +1,106 @@
#!/usr/bin/tclsh
#
# Use this script to combine multiple source code files into a single
# file. Example:
#
# tclsh mkcombo.tcl file1.c file2.c file3.c -o file123.c
#
set help {Usage: tclsh mkcombo.tcl [OPTIONS] [FILELIST]
where OPTIONS is zero or more of the following with these effects:
--linemacros=? => Emit #line directives into output or not. (? = 1 or 0)
--o FILE => write to alternative output file named FILE
--help => See this.
}
set linemacros 0
set fname {}
set src [list]
for {set i 0} {$i<[llength $argv]} {incr i} {
set x [lindex $argv $i]
if {[regexp {^-?-linemacros(?:=([01]))?$} $x ma ulm]} {
if {$ulm == ""} {set ulm 1}
set linemacros $ulm
} elseif {[regexp {^-o$} $x]} {
incr i
if {$i==[llength $argv]} {
error "No argument following $x"
}
set fname [lindex $argv $i]
} elseif {[regexp {^-?-((help)|\?)$} $x]} {
puts $help
exit 0
} elseif {[regexp {^-?-} $x]} {
error "unknown command-line option: $x"
} else {
lappend src $x
}
}
# Open the output file and write a header comment at the beginning
# of the file.
#
if {![info exists fname]} {
set fname sqlite3.c
if {$enable_recover} { set fname sqlite3r.c }
}
set out [open $fname wb]
# Return a string consisting of N "*" characters.
#
proc star N {
set r {}
for {set i 0} {$i<$N} {incr i} {append r *}
return $r
}
# Force the output to use unix line endings, even on Windows.
fconfigure $out -translation binary
puts $out "/[star 78]"
puts $out {** The following is an amalgamation of these source code files:}
puts $out {**}
foreach s $src {
regsub {^.*/(src|ext)/} $s {\1/} s2
puts $out "** $s2"
}
puts $out {**}
puts $out "[star 78]/"
# Insert a comment into the code
#
proc section_comment {text} {
global out s78
set n [string length $text]
set nstar [expr {60 - $n}]
puts $out "/************** $text [star $nstar]/"
}
# Read the source file named $filename and write it into the
# sqlite3.c output file. The only transformation is the trimming
# of EOL whitespace.
#
proc copy_file_verbatim {filename} {
global out
set in [open $filename rb]
set tail [file tail $filename]
section_comment "Begin file $tail"
while {![eof $in]} {
set line [string trimright [gets $in]]
puts $out $line
}
section_comment "End of $tail"
}
set taillist ""
foreach file $src {
copy_file_verbatim $file
append taillist ", [file tail $file]"
}
set taillist "End of the amalgamation of [string range $taillist 2 end]"
set n [string length $taillist]
set ns [expr {(75-$n)/2}]
if {$ns<3} {set ns 3}
puts $out "/[star $ns] $taillist [star $ns]/"
close $out
+104 -25
View File
@@ -13,28 +13,90 @@ set topdir [file dir [file dir [file normal $argv0]]]
set out stdout
fconfigure stdout -translation binary
if {[lindex $argv 0]!=""} {
set out [open [lindex $argv 0] wb]
set output_file [lindex $argv 0]
file delete -force $output_file
set out [open $output_file wb]
} else {
set output_file {}
}
puts $out {/* DO NOT EDIT!
** This file is automatically generated by the script in the canonical
** SQLite source tree at tool/mkshellc.tcl. That script combines source
** code from various constituent source files of SQLite into this single
** "shell.c" file used to implement the SQLite command-line shell.
**
** Most of the code found below comes from the "src/shell.c.in" file in
** the canonical SQLite source tree. That main file contains "INCLUDE"
** lines that specify other files in the canonical source tree that are
** inserted to getnerate this complete program source file.
**
** The code from multiple files is combined into this single "shell.c"
** source file to help make the command-line program easier to compile.
**
** To modify this program, get a copy of the canonical SQLite source tree,
** edit the src/shell.c.in" and/or some of the other files that are included
** by "src/shell.c.in", then rerun the tool/mkshellc.tcl script.
*/}
############################## FIRST PASS ################################
# Read through the shell.c.in source file to gather information. Do not
# yet generate any code
#
set in [open $topdir/src/shell.c.in]
fconfigure $in -translation binary
set allSource(src/shell.c.in) 1
set inUsage 0
set dotcmd {}
while {1} {
set lx [gets $in]
if {[eof $in]} break;
if {[regexp {^INCLUDE } $lx]} {
set cfile [lindex $lx 1]
if {[string match ../* $cfile]} {
set xfile [string range $cfile 3 end]
} else {
set xfile "src/$cfile"
}
set allSource($xfile) 1
} elseif {[regexp {^\*\* USAGE:\s+([^\s]+)} $lx all dotcmd]} {
set inUsage 1
set details [string trim [string range $lx 2 end]]
} elseif {$inUsage} {
if {![regexp {^\*\*} $lx] || [regexp { DOT-COMMAND: } $lx]} {
set inUsage 0
set Usage($dotcmd) [string trim $details]
} else {
append details \n
append details [string range [string trimright $lx] 3 end]
}
}
}
# Generate dot-command usage text based on the data accumulated in
# the Usage() array.
#
proc generate_usage {out} {
global Usage
puts $out "/**************************************************************"
puts $out "** \"Usage\" help text automatically generated from comments */"
puts $out "static const struct \173"
puts $out " const char *zCmd; /* Name of the dot-command */"
puts $out " const char *zUsage; /* Documentation */"
puts $out "\175 aUsage\[\] = \173"
foreach dotcmd [array names Usage] {
puts $out " \173 \"$dotcmd\","
foreach line [split $Usage($dotcmd) \n] {
set x [string map [list \\ \\\\ \" \\\"] $line]
puts $out "\"$x\\n\""
}
puts $out " \175,"
}
puts $out "\175;"
}
# generate_usage stderr
###### SECOND PASS #######
# Make a second pass through shell.c.in to generate the the final
# output, based on data gathered during the first pass.
#
puts $out {/*
** This is the amalgamated source code to the "sqlite3" or "sqlite3.exe"
** command-line shell (CLI) for SQLite. This file is automatically
** generated by the tool/mkshellc.tcl script from the following sources:
**}
foreach fn [lsort [array names allSource]] {
puts $out "** $fn"
}
puts $out {**
** To modify this program, get a copy of the canonical SQLite source tree,
** edit the src/shell.c.in file and/or some of the other files that are
** listed above, then rerun the rerun "make shell.c".
*/}
seek $in 0 start
puts $out "/************************* Begin src/shell.c.in ******************/"
proc omit_redundant_typedefs {line} {
global typedef_seen
if {[regexp {^typedef .* ([a-zA-Z0-9_]+);} $line all typename]} {
@@ -53,9 +115,14 @@ while {1} {
incr iLine
if {[regexp {^INCLUDE } $lx]} {
set cfile [lindex $lx 1]
puts $out "/************************* Begin $cfile ******************/"
# puts $out "#line 1 \"$cfile\""
set in2 [open $topdir/src/$cfile]
if {[string match ../* $cfile]} {
set xfile [string range $cfile 3 end]
} else {
set xfile "src/$cfile"
}
puts $out "/************************* Begin $xfile ******************/"
# puts $out "#line 1 \"$xfile\""
set in2 [open $topdir/$xfile]
fconfigure $in2 -translation binary
while {![eof $in2]} {
set lx [omit_redundant_typedefs [gets $in2]]
@@ -69,11 +136,23 @@ while {1} {
puts $out $lx
}
close $in2
puts $out "/************************* End $cfile ********************/"
puts $out "/************************* End $xfile ********************/"
# puts $out "#line [expr $iLine+1] \"shell.c.in\""
continue
} elseif {[regexp {^INSERT-USAGE-TEXT-HERE} $lx]} {
generate_usage $out
} else {
puts $out $lx
}
puts $out $lx
}
puts $out "/************************* End src/shell.c.in ******************/"
close $in
close $out
# Try to disable write permissions on the generate file, to prevent
# accidentally editing the generated file rather than source files.
#
if {$output_file ne ""} {
catch {file attributes $output_file -readonly 1}
catch {file attributes $output_file -permissions -w}
catch {exec chmod -w $output_file}
}
+1 -1
View File
@@ -33,7 +33,7 @@ INCLUDE $ROOT/ext/misc/appendvfs.c
INCLUDE $ROOT/ext/misc/zipfile.c
INCLUDE $ROOT/ext/misc/sqlar.c
#endif
INCLUDE $ROOT/src/tclsqlite.c
INCLUDE tclsqlite-ex.c
const char *sqlite3_tclapp_init_proc(Tcl_Interp *interp){
(void)interp;