Compare commits

..

174 Commits

Author SHA1 Message Date
drh 843418ae70 In the CLI on the ".mode" command, replace the recently added --no-limits option with
"--limits L,C".  And add --sw as an alias for --screenwidth.

FossilOrigin-Name: caefe4c746599b22aec446f3aeb296e53edaf8c54f8ae803381e8c21d5e29ab4
2025-11-24 12:27:18 +00:00
dan 0ec8c56567 Remove the the SQLITE_SYNC_DATAONLY flag from the core on the grounds that it is not currently used by either built-in VFS and is therefore untested.
FossilOrigin-Name: ff2065b533769cbf5278d13d09ffa93a7a0f841b1b59b6be0e5f27e3d1c341d0
2025-11-24 11:22:56 +00:00
stephan 9101b90159 Add the long-missing sqlite3_next_stmt() binding to JS/WASM.
FossilOrigin-Name: de27d2d3f2353309227b4d5f77eff76c108c094b61ac59f5a769229a084f2567
2025-11-23 22:18:26 +00:00
drh f63bc3a922 New test case for bSplitColumn in QRF.
FossilOrigin-Name: 6d01f9f49eef5f7d4bddadc458691b6ca36e277bae1d4b43b60a128a44e3faca
2025-11-23 10:55:38 +00:00
drh 58a4e08e3d In QRF in bSplitColumn mode, increase the spacing between columns to be as
wide as five, for improved legibility, if screen width allows.

FossilOrigin-Name: 673648cc812e1549c2b3e7749306e7395d0b7e396a7d5ac432948eb3a6c3713f
2025-11-23 00:45:47 +00:00
drh d0056d4234 Use the new "split" mode to simplify and improve the implementation of
the ".tables" and ".indexes" commands in the CLI.  Fix a bug in QRF
that this new implementation found.

FossilOrigin-Name: b73df612b621699cd58de6ef327fefdeb8f52eb620f0d2df00d38ff6608a8dd5
2025-11-23 00:10:06 +00:00
drh c4190338c5 Add "split" mode in the CLI. Fix CLI commands given on the command line
so that they honor --once in prior commands.

FossilOrigin-Name: 2530f04d1bb02b800fda83544f5b5dcd42f52b3e56ad625cd03b40e42f24ac6b
2025-11-22 23:34:53 +00:00
drh eeb38d3665 Add the --no-limits option to the .mode command in the CLI.
FossilOrigin-Name: 3cee168e8801481f6d909fb5d20a773b9c6ca0b8daedfc301c9d762a17f9d839
2025-11-22 21:47:48 +00:00
drh 946857cfee In QRF, change the name of the bWrapSnglCol flag to bSplitColumn.
Similar changes to the TCL interface.

FossilOrigin-Name: 76512b3b80fdcaae22340cde0b69857e2a4d7e86a63c6759465bd8a0bcbf82bc
2025-11-22 21:34:33 +00:00
stephan 2fcc13f8c9 Cherrypick [0b53be562f1e] into trunk.
FossilOrigin-Name: e86e5723d778721b10c1c908545c56233d4d5549a7ca3b7cf8ca649c70450add
2025-11-22 19:29:23 +00:00
drh 494811810c Ignore semicolons at the end of dot-commands, if the compatibility date is
20251115 or later.
[forum:/forumpost/72d084ed552|Forum thread 2025-10-09T21:12:32z]

FossilOrigin-Name: 7a9e753e977ff61ceee50c4e0e5f9b1fc179e91acd9ac88378c888b3e45ebe3a
2025-11-22 16:43:52 +00:00
stephan c7d155aa5d Cherrypick [e0b33b51229a] for the latest wasmutil/jaccwabyt - all tests are happy. Fix a make deps bug which can break parallel builds involving fiddle.
FossilOrigin-Name: 0be777bd6cc6c6b4f52d948cdba7e9c30ad062a35c659512b3f3f89f53aaba25
2025-11-22 15:49:45 +00:00
drh f4197be567 Implementation of bWrapSnglCol in QRF.
FossilOrigin-Name: 4bbd3f7ead50c0babd7843f58972e2e1762a84866cc4ed88a2a74375b13da11c
2025-11-22 00:38:17 +00:00
drh 081a877211 Add the bWrapSnglCol flag to the QRF spec, though it is not yet documented
and does not yet work.  Fix column output so that it omits trailing space.

FossilOrigin-Name: a858027fc92727c680b7e984303df7f9e0a2d90c860c547176f290b113a69390
2025-11-21 20:10:12 +00:00
drh a5e454d11c Need a double-underscore prefix on the cplusplus macro
FossilOrigin-Name: 04394387e626cd99ff98df978c4b7f2d32f65760e0c26e53f1ef4f59e4e91a4f
2025-11-21 19:47:13 +00:00
drh 725e6836d0 Fix the qrf.h header so that it is usable from C++
FossilOrigin-Name: 1105b710934cf4a243c64d37dd147bc9378c0f7aa55a99891b7ac6e8f8f17f88
2025-11-21 19:01:26 +00:00
drh 5b19de37b6 Fix typo in the implementation of the ".schema" command of the CLI for the
case when there are ATTACH-ed databases.

FossilOrigin-Name: 8a230e4da230a7b103749b069a99a58e4c220873c2f9576abdc928c3538d70b3
2025-11-21 17:30:44 +00:00
stephan 794a9b48ac Add new file missing from [3c4061428544].
FossilOrigin-Name: 41f94eca01d8317364aa60ddd8e5fe3cd21a215040ef271a157d450a914139d0
2025-11-21 15:03:38 +00:00
drh 873cd21ec3 Enhance ALTER TABLE to support adding and removing NOT NULL and CHECK
constraints.

FossilOrigin-Name: 895498e4431e02cff65a5d96db22f0b0cb9c96aedf1e3cdcdeb3c34c6fec432b
2025-11-21 14:15:56 +00:00
drh ac16642cc4 Fix the QRF so that it omits the closing text from styles "json" and "jobject"
when the result set it empty.

FossilOrigin-Name: e1ba175124be83da4db6d5d4e583b9b3b7dcb82e983c6f185d590469dcdc3139
2025-11-21 11:00:29 +00:00
stephan 40ce52a4e5 Move the JS pieces of kvvfs into their own file to facilitate pending feature experimentation.
FossilOrigin-Name: 3c40614285449df259a3444e36f888cfb5e782ea58287914f97f496ea61e9e9f
2025-11-21 10:49:32 +00:00
drh 1073985be7 Test cases for the fix in the prior check-in.
FossilOrigin-Name: 4d41bee75eda51251121c8e3903f47941116e5182238a03f41a593c47efb6fcf
2025-11-20 22:46:27 +00:00
drh 980580efec Preserve auto-explain mode across mode changes, as has always been the
case before.

FossilOrigin-Name: 8896185ae0b0af8918aa8ce449f18759e6ae60358afbac1019397bae307b15d5
2025-11-20 21:08:46 +00:00
drh a68046e79d Fix hwtime.h (broken by [305bb022368bac9f]) so that unamalgamated windows
builds work again.

FossilOrigin-Name: 1245319257d03a00eddbc8ff4b43b5813f8a567a12a9957f923bef58c85c0c20
2025-11-20 12:41:26 +00:00
stephan 6d62ed4f87 Remove an extraneous loop level from the JS bootstrapping bits.
FossilOrigin-Name: 86817b4e9f011d296fb420be95269fecb54fb48a220bce84020e3af2d2c164a4
2025-11-20 12:14:03 +00:00
stephan df3fca8da4 Enable the SQLITE_EXPERIMENTAL_PRAGMA_20251114 in the WASM builds for the sake of VFS experimentors. The links in the comments describe what that does.
FossilOrigin-Name: aaa55a3ebf8e725443030be5f31ac7fc0766c1f3c771ce69852f31e7e6f165b9
2025-11-20 12:12:55 +00:00
drh 3df99c4861 Use sqlite3MPrintf() instead of sqlite3_mprintf() internally in alter.c,
for improved OOM reporting.

FossilOrigin-Name: 57555d75daa2ee8345f8329749841a322b3e57679e5c4899ef749c8bc814812d
2025-11-20 11:06:58 +00:00
drh d7e7ab7b5f Additional checks for OOM conditions.
FossilOrigin-Name: 8fafd4988bf1fa1aa799c2834dd31bebdd52e7ae8e68c0197ccb497eb9b1e5f6
2025-11-20 02:03:57 +00:00
drh 6cb88552bc Mark an unreachable branch with NEVER().
FossilOrigin-Name: df2259a32fd65986eafceb2c551e6e1661927f1f5b18dbb97c59d9627279eea0
2025-11-20 00:56:28 +00:00
drh 325a361d5a Call sqlite3OomFault() rather than setting db->mallocFailed directly.
FossilOrigin-Name: 4fd5dfa215f14b20ddfc55904acbcc9bc9fd2123bb06fff72ef76efa17304566
2025-11-19 23:11:56 +00:00
drh c23a1b9bea Fix three harmless declaration-after-statement warnings in the new CLI code.
FossilOrigin-Name: b65b29656c835db22557733f4dcfe906bf21a7a3c36266cbbc7e3277f029b901
2025-11-19 19:37:28 +00:00
drh 57b61bf78f Improved comments in code. Omit unneeded wrapper functions.
FossilOrigin-Name: 3485c7c81bee8e98ea6b403438bcdb2b6fa058afeafea150ce1c859345e1529b
2025-11-19 15:25:18 +00:00
dan 3a4c9daa5c Strip unterminated "--" comments from the end of constraints added using ALTER TABLE ADD CONSTRAINT or ADD NOT NULL.
FossilOrigin-Name: c73ab47b0250c01f1ffa6e506793769744ebeb8ca7edbb78de9a08db2037d6de
2025-11-19 11:31:59 +00:00
drh b58ccaea8a Clarification to the header comment on dropConstraintFunc(). No changes
to code.

FossilOrigin-Name: 4bd53fbb20d78de7bbff1154e7ee1321b93b0ede435e150ad4ee992a4108ad5a
2025-11-19 11:28:48 +00:00
drh 7423111fe1 Trivial simplification to the ALTER TABLE ADD CONSTRAINT code.
FossilOrigin-Name: 383965b3ba701d3627e0bbd09ad04d0ce00d4721497d82e241dc7cffa745b1d9
2025-11-19 10:26:49 +00:00
drh ec047587e3 Merge the latest trunk changes into the alter-table-constraints branch.
FossilOrigin-Name: 31d7aeeaef20ac42c3c395dbaf7a8225ac718256a4cbeb7c198d1c7361766b1b
2025-11-18 19:36:45 +00:00
drh f1928b3e54 Do not attempt to make the generated file "shell.c" read-only. This turns
out to be annoying in general, and especially annoying on macs.

FossilOrigin-Name: 6ab18d036a3e412ee01806a40f69aca2b77283bd5336df27597cc19d32e6e6f2
2025-11-18 19:11:21 +00:00
drh f639befec8 New subcomponent, the Query Result Formatter (QRF), that formats query
results for display to humans on a fixed-width font terminal.  Rework the
CLI to make use of the QRF.  Renovate the .mode command of the CLI.  Also
incorporate the QRF into the TCL interface as the "format" method.

FossilOrigin-Name: 7e460ffa5aae884807db9e7c8214d6d822d5d38ea406fe3b3eac04ac16f158fa
2025-11-18 17:49:48 +00:00
drh 4b43acc718 Add support for SQLITE_LIMIT_PARSER_DEPTH to limit the size of the stack
used by the parser.  This can help prevent deeply nested parse trees that
then cause problems on machines with smaller CPU stacks.  Modify the
%realloc and %free directives of Lemon and add the new %stack_size_limit
directive in support of this capability.

FossilOrigin-Name: 52ba0c731d004409353a55ce8ca5a514ce486a077a2be82db5b8fea7619848d5
2025-11-18 17:27:46 +00:00
drh 9e91a1fe89 Modify the "%realloc" and "%free" commands in Lemon so that the functions
they specify take an extra parameter at the end, the %extra_context pointer.
This allows the implementation to distinguish between OOM errors and
failures to increase the stack size because of the stack size limit.

FossilOrigin-Name: 9862c945d9a8531f9bef123aee9ed1fd3f64542250a57beb3a150227bc3c1a12
2025-11-18 15:40:02 +00:00
drh 9432655038 Lower the default stack size for the parse to 50.
FossilOrigin-Name: 41fe19ab054acda912bc32dd6f9c6412416ab1af6cf55515e96c89fb55b46424
2025-11-18 15:20:22 +00:00
drh fef2233090 Add the SQLITE_LIMIT_PARSER_DEPTH value for sqlite3_limit(). This isn't
something that many applications will need, but it is useful for testing.

FossilOrigin-Name: 8f0b07f36159225c476f756f8f9b35c75783bc8bed43b578f4d1055fa800ecc9
2025-11-18 14:48:33 +00:00
drh d26ac414c8 Yet another attempt at controlling the parser stack size.
FossilOrigin-Name: cb19986dc6bc483df21e082e54a14cb6d7540b1734259e6d326d676908ac0172
2025-11-18 13:03:08 +00:00
drh ef4abc0a63 Fix incorrect "#line" generation in Lemon.
FossilOrigin-Name: 5c0214df2c0a7470ac2edca0c483a3edd3c39ef0739688ab9a06e23882200360
2025-11-18 10:38:41 +00:00
stephan 847d2ed685 Add api.oo1=0 flag to ext/wasm/GNUmakefile to strip out the sqlite3.oo1 pieces from the build. Part of the ongoing response to [forum:4b7d45433731d2e0|forum post 4b7d45433731d2e0].
FossilOrigin-Name: ea48567ac54e4949a8b68977a58a5de7946e074ae8737133071d02f40ac97f34
2025-11-17 23:55:41 +00:00
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
dan 0a416b9fd9 Merge trunk with this branch.
FossilOrigin-Name: d7d8d08f849b2caeef4f18244c4b594e4f66506ef00be47ab2dd4b53d3ab98e8
2025-11-11 17:06:24 +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
dan 39d132db5b Merge latest trunk changes with this branch.
FossilOrigin-Name: c0571f0010f3a6311fa99cf1b37a8e5ee39a9b8e27313cde5ea4bd1701bf2f93
2025-11-07 19:23:06 +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
drh 064a1aa308 Fix harmless compiler warnings.
FossilOrigin-Name: 963a4c406799fc5352157552bdbbc6a0cc7edf68040e893e9d736ab7224d5a4b
2025-10-02 16:14:25 +00:00
drh 9abd7edaa7 Merge recent trunk fixes and enhancements into the alter-table-constraints branch.
FossilOrigin-Name: 26f8a700785edf64e2540b37c748e9f201870865ba70374f8681cdfdd048e68f
2025-10-02 15:30:26 +00:00
dan d802904c8a Fix some problems with DROP CONSTRAINT and DROP NOT NULL on generated columns.
FossilOrigin-Name: 66c47e18e6e1b69df4c2ceb63ff20423bbc1d5819934d77b140cf321fa14eac5
2025-10-01 17:27:23 +00:00
dan c592b7e01d Fix handling of FK constraints that immediately follow named table constraints in DROP CONSTRAINT.
FossilOrigin-Name: 1c6f09f9f95ba76efa4f11f1c05d405d799b43f3570b23791472c3e165971bbc
2025-09-30 19:20:53 +00:00
dan 1f6d80c1e2 Fix some DROP CONSTRAINT and DROP NOT NULL cases involving SQL comments or multiple table constraints without separating commas.
FossilOrigin-Name: b0b02e58ac60e4371981938c904ccf831b2367e3fd7d22e0ec2b4782e4c6805b
2025-09-30 17:13:12 +00:00
dan d7330b571d Add further tests for the new ALTER TABLE commands on this branch.
FossilOrigin-Name: 17a6f4e69e54a5dc233e33501f16f0f69f77fff071142cefc309b5407f57f721
2025-09-29 16:49:55 +00:00
dan fc66c6380f Add test cases for ALTER TABLE and attached databases.
FossilOrigin-Name: 8f2e066e9a0a34fe7a41b153c0fb54b621128b2195141cc454a44180c969b4a9
2025-09-27 19:17:08 +00:00
dan eceff127ea Add OOM injection tests for new commands on this branch.
FossilOrigin-Name: 34b47c9b1be8a6ead917c6e9a85f8a295b14640cde177d8f1384b15f8bc692bb
2025-09-27 18:25:27 +00:00
dan d47220c190 Add tests for ALTER TABLE authorization callbacks and other things.
FossilOrigin-Name: 55cad8d15040bc01300bfe5efcc7225dd9695c2972c0f9b4bbd311ed57188e1f
2025-09-27 18:08:19 +00:00
dan 32c49bd6e6 Fix authorization callbacks for the new commands on this branch.
FossilOrigin-Name: 0165e2afa8b640c9d7a31414f06144e089844dc13691f06f363a50d14fec62bc
2025-09-27 16:13:56 +00:00
dan ba1ee803ee Add experimental implementations of ALTER TABLE commands to add and drop CHECK and NOT NULL constraints.
FossilOrigin-Name: d939b25d76fe70a3255cfe38097d4489323028cd05e5512a98dce06b48eee445
2025-09-27 14:59:21 +00:00
70 changed files with 12449 additions and 4740 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 \
+3
View File
@@ -1020,6 +1020,7 @@ SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_OFFSET_SQL_FUNC=1
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_PERCENTILE=1
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION=1
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_STMT_SCANSTATUS=1
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_BYTECODE_VTAB=1
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_STRICT_SUBTYPE=1
!ENDIF
@@ -1068,6 +1069,8 @@ $(SQLITE3EXE): shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLIT
/link $(SQLITE3EXEPDB) $(LDFLAGS) $(LTLINKOPTS) $(SHELL_LINK_OPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS)
tclsqlite-ex.c:
# Rule to build the amalgamation
#
sqlite3.lo: $(SQLITE3C)
+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 {}
+22 -8
View File
@@ -696,6 +696,7 @@ other than that, the order of directives in Lemon is arbitrary.</p>
<li><tt><a href='#pright'>%right</a></tt>
<li><tt><a href='#reallc'>%realloc</a></tt>
<li><tt><a href='#stack_overflow'>%stack_overflow</a></tt>
<li><tt><a href='#reallc'>%stack_size_limit</a></tt>
<li><tt><a href='#stack_size'>%stack_size</a></tt>
<li><tt><a href='#start_symbol'>%start_symbol</a></tt>
<li><tt><a href='#syntax_error'>%syntax_error</a></tt>
@@ -1203,20 +1204,33 @@ the wildcard token and some other token, the other token is always used.
The wildcard token is only matched if there are no alternatives.</p>
<a id='reallc'></a>
<h4>4.4.26 The <tt>%realloc</tt> and <tt>%free</tt> directives</h4>
<h4>4.4.26 The <tt>%realloc</tt>, <tt>%free</tt>, and
<tt>%stack_size_limit</tt> directives</h4>
<p>The <tt>%realloc</tt> and <tt>%free</tt> directives defines function
that allocate and free heap memory. The signatures of these functions
should be the same as the realloc() and free() functions from the standard
C library.
that allocate and free heap memory. The signatures and semantics of
these functions are similar to the realloc() and free() functions from
the standard C library, except that these functions take an extra
parameter at the end that is determined by %extra_context. If
%extra_context is not defined, then the extra argument is 0. The
extra parameter provides the capability to do better error reporting
in the event of a memory allocation error, and/or to use an alternative
private application heap.
<p>If both of these functions are defined
then these functions are used to allocate and free
memory for supplemental parser stack space, if the initial
parse stack space is exceeded. The initial parser stack size
<p>If both of these functions are defined then they are used to
allocate and free memory for supplemental parser stack space, if
the initial parse stack space is exceeded. The initial parser stack size
is specified by either <tt>%stack_size</tt> or the
-DYYSTACKDEPTH compile-time flag.
<p>The <tt>%stack_size_limit</tt> directive defines a function that returns
the maximum allowed parser stack size. If this diretive does not exist,
no size limit is enforced. This function takes a single argument which
is the %extra_context value or "0" if %extra_context is not defined.
The function should return an integer that is the maximum
number of parser stack entries. If more stack space
than this is needed, the %stack_overflow code is invoked.
<a id='errors'></a>
<h2>5.0 Error Processing</h2>
+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]
}
+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.
+2664
View File
File diff suppressed because it is too large Load Diff
+189
View File
@@ -0,0 +1,189 @@
/*
** 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
#ifdef __cplusplus
extern "C" {
#endif
#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 */
unsigned char bSplitColumn; /* Wrap single-column output into many columns */
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);
#ifdef __cplusplus
}
#endif
#endif /* !defined(SQLITE_QRF_H) */
+7 -1
View File
@@ -576,6 +576,10 @@ b.c-pp.target.flags ?=
ifeq (1,$(SQLITE_C_IS_SEE))
b.c-pp.target.flags += -Denable-see
endif
api.oo1 ?= 1
ifeq (0,$(api.oo1))
b.c-pp.target.flags += -Domit-oo1
endif
#
# cflags.common = C compiler flags for all builds
@@ -906,6 +910,7 @@ sqlite3-api.jses += $(dir.api)/sqlite3-vfs-helper.c-pp.js
ifeq (0,$(wasm-bare-bones))
sqlite3-api.jses += $(dir.api)/sqlite3-vtab-helper.c-pp.js
endif
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-kvvfs.c-pp.js
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs.c-pp.js
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs-sahpool.c-pp.js
@@ -993,7 +998,8 @@ endif
########################################################################
EXPORTED_FUNCTIONS.fiddle = $(dir.tmp)/EXPORTED_FUNCTIONS.fiddle
$(EXPORTED_FUNCTIONS.fiddle): $(EXPORTED_FUNCTIONS.api.in) $(MAKEFILE_LIST)
$(EXPORTED_FUNCTIONS.fiddle): $(EXPORTED_FUNCTIONS.api.in) \
$(MAKEFILE_LIST) $(bin.c-pp)
@$(call b.mkdir@)
@$(call b.c-pp.shcmd,filter,$(EXPORTED_FUNCTIONS.api.in),\
$@,$(EXPORTED_FUNCTIONS.c-pp.flags) -Dfiddle)
+1
View File
@@ -75,6 +75,7 @@ _sqlite3_limit
_sqlite3_malloc
_sqlite3_malloc64
_sqlite3_msize
_sqlite3_next_stmt
_sqlite3_open
_sqlite3_open_v2
_sqlite3_overload_function
+3 -106
View File
@@ -200,6 +200,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
["sqlite3_libversion_number", "int"],
["sqlite3_limit", "int", ["sqlite3*", "int", "int"]],
["sqlite3_malloc", "*","int"],
["sqlite3_next_stmt", "sqlite3_stmt*", ["sqlite3*","sqlite3_stmt*"]],
["sqlite3_open", "int", "string", "*"],
["sqlite3_open_v2", "int", "string", "*", "int", "string"],
/* sqlite3_prepare_v2() and sqlite3_prepare_v3() are handled
@@ -975,10 +976,8 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
"entry SQLITE_WASM_DEALLOC (=="+capi.SQLITE_WASM_DEALLOC+").");
}
const __rcMap = Object.create(null);
for(const t of ['resultCodes']){
for(const e of Object.entries(wasm.ctype[t])){
__rcMap[e[1]] = e[0];
}
for(const e of Object.entries(wasm.ctype['resultCodes'])){
__rcMap[e[1]] = e[0];
}
/**
For the given integer, returns the SQLITE_xxx result code as a
@@ -990,8 +989,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
const notThese = Object.assign(Object.create(null),{
// For each struct to NOT register, map its name to true:
WasmTestStruct: true,
/* We unregister the kvvfs VFS from Worker threads below. */
sqlite3_kvvfs_methods: !util.isUIThread(),
/* sqlite3_index_info and friends require int64: */
sqlite3_index_info: !wasm.bigIntEnabled,
sqlite3_index_constraint: !wasm.bigIntEnabled,
@@ -1785,106 +1782,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
};
}/* auto-extension */
const pKvvfs = capi.sqlite3_vfs_find("kvvfs");
if( pKvvfs ){/* kvvfs-specific glue */
if(util.isUIThread()){
const kvvfsMethods = new capi.sqlite3_kvvfs_methods(
wasm.exports.sqlite3__wasm_kvvfs_methods()
);
delete capi.sqlite3_kvvfs_methods;
const kvvfsMakeKey = wasm.exports.sqlite3__wasm_kvvfsMakeKeyOnPstack,
pstack = wasm.pstack;
const kvvfsStorage = (zClass)=>
((115/*=='s'*/===wasm.peek(zClass))
? sessionStorage : localStorage);
/**
Implementations for members of the object referred to by
sqlite3__wasm_kvvfs_methods(). We swap out the native
implementations with these, which use localStorage or
sessionStorage for their backing store.
*/
const kvvfsImpls = {
xRead: (zClass, zKey, zBuf, nBuf)=>{
const stack = pstack.pointer,
astack = wasm.scopedAllocPush();
try {
const zXKey = kvvfsMakeKey(zClass,zKey);
if(!zXKey) return -3/*OOM*/;
const jKey = wasm.cstrToJs(zXKey);
const jV = kvvfsStorage(zClass).getItem(jKey);
if(!jV) return -1;
const nV = jV.length /* We are relying 100% on v being
ASCII so that jV.length is equal
to the C-string's byte length. */;
if(nBuf<=0) return nV;
else if(1===nBuf){
wasm.poke(zBuf, 0);
return nV;
}
const zV = wasm.scopedAllocCString(jV);
if(nBuf > nV + 1) nBuf = nV + 1;
wasm.heap8u().copyWithin(
Number(zBuf), Number(zV), wasm.ptr.addn(zV, nBuf,- 1)
);
wasm.poke(wasm.ptr.add(zBuf, nBuf, -1), 0);
return nBuf - 1;
}catch(e){
sqlite3.config.error("kvstorageRead()",e);
return -2;
}finally{
pstack.restore(stack);
wasm.scopedAllocPop(astack);
}
},
xWrite: (zClass, zKey, zData)=>{
const stack = pstack.pointer;
try {
const zXKey = kvvfsMakeKey(zClass,zKey);
if(!zXKey) return 1/*OOM*/;
const jKey = wasm.cstrToJs(zXKey);
kvvfsStorage(zClass).setItem(jKey, wasm.cstrToJs(zData));
return 0;
}catch(e){
sqlite3.config.error("kvstorageWrite()",e);
return capi.SQLITE_IOERR;
}finally{
pstack.restore(stack);
}
},
xDelete: (zClass, zKey)=>{
const stack = pstack.pointer;
try {
const zXKey = kvvfsMakeKey(zClass,zKey);
if(!zXKey) return 1/*OOM*/;
kvvfsStorage(zClass).removeItem(wasm.cstrToJs(zXKey));
return 0;
}catch(e){
sqlite3.config.error("kvstorageDelete()",e);
return capi.SQLITE_IOERR;
}finally{
pstack.restore(stack);
}
}
}/*kvvfsImpls*/;
for(const k of Object.keys(kvvfsImpls)){
kvvfsMethods[kvvfsMethods.memberKey(k)] =
wasm.installFunction(
kvvfsMethods.memberSignature(k),
kvvfsImpls[k]
);
}
}else{
/* Worker thread: unregister kvvfs to avoid it being used
for anything other than local/sessionStorage. It "can"
be used that way but it's not really intended to be. */
capi.sqlite3_vfs_unregister(pKvvfs);
delete capi.sqlite3_kvvfs_methods;
}
}/*pKvvfs*/
/* Warn if client-level code makes use of FuncPtrAdapter. */
wasm.xWrap.FuncPtrAdapter.warnOnUse = true;
+1 -50
View File
@@ -239,7 +239,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
*/
ctor._name2vfs = Object.create(null);
const isWorkerThread = ('function'===typeof importScripts/*===running in worker thread*/)
? (n)=>toss3("The VFS for",n,"is only available in the main window thread.")
? (n)=>toss3("VFS",n,"is only available in the main window thread.")
: false;
ctor._name2vfs[':localStorage:'] = {
vfs: 'kvvfs', filename: isWorkerThread || (()=>'local')
@@ -2295,55 +2295,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
Stmt
}/*oo1 object*/;
if(util.isUIThread()){
/**
Functionally equivalent to DB(storageName,'c','kvvfs') except
that it throws if the given storage name is not one of 'local'
or 'session'.
As of version 3.46, the argument may optionally be an options
object in the form:
{
filename: 'session'|'local',
... etc. (all options supported by the DB ctor)
}
noting that the 'vfs' option supported by main DB
constructor is ignored here: the vfs is always 'kvvfs'.
*/
sqlite3.oo1.JsStorageDb = function(storageName='session'){
const opt = dbCtorHelper.normalizeArgs(...arguments);
storageName = opt.filename;
if('session'!==storageName && 'local'!==storageName){
toss3("JsStorageDb db name must be one of 'session' or 'local'.");
}
opt.vfs = 'kvvfs';
dbCtorHelper.call(this, opt);
};
const jdb = sqlite3.oo1.JsStorageDb;
jdb.prototype = Object.create(DB.prototype);
/** Equivalent to sqlite3_js_kvvfs_clear(). */
jdb.clearStorage = capi.sqlite3_js_kvvfs_clear;
/**
Clears this database instance's storage or throws if this
instance has been closed. Returns the number of
database blocks which were cleaned up.
*/
jdb.prototype.clearStorage = function(){
return jdb.clearStorage(affirmDbOpen(this).filename);
};
/** Equivalent to sqlite3_js_kvvfs_size(). */
jdb.storageSize = capi.sqlite3_js_kvvfs_size;
/**
Returns the _approximate_ number of bytes this database takes
up in its storage or throws if this instance has been closed.
*/
jdb.prototype.storageSize = function(){
return jdb.storageSize(affirmDbOpen(this).filename);
};
}/*main-window-only bits*/
});
//#else
/* Built with the omit-oo1 flag. */
-80
View File
@@ -1604,86 +1604,6 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(
return x===v ? undefined : x;
}
if( util.isUIThread() ){
/* Features specific to the main window thread... */
/**
Internal helper for sqlite3_js_kvvfs_clear() and friends.
Its argument should be one of ('local','session',"").
*/
const __kvvfsInfo = function(which){
const rc = Object.create(null);
rc.prefix = 'kvvfs-'+which;
rc.stores = [];
if('session'===which || ""===which) rc.stores.push(globalThis.sessionStorage);
if('local'===which || ""===which) rc.stores.push(globalThis.localStorage);
return rc;
};
/**
Clears all storage used by the kvvfs DB backend, deleting any
DB(s) stored there. Its argument must be either 'session',
'local', or "". In the first two cases, only sessionStorage
resp. localStorage is cleared. If it's an empty string (the
default) then both are cleared. Only storage keys which match
the pattern used by kvvfs are cleared: any other client-side
data are retained.
This function is only available in the main window thread.
Returns the number of entries cleared.
*/
capi.sqlite3_js_kvvfs_clear = function(which=""){
let rc = 0;
const kvinfo = __kvvfsInfo(which);
kvinfo.stores.forEach((s)=>{
const toRm = [] /* keys to remove */;
let i;
for( i = 0; i < s.length; ++i ){
const k = s.key(i);
if(k.startsWith(kvinfo.prefix)) toRm.push(k);
}
toRm.forEach((kk)=>s.removeItem(kk));
rc += toRm.length;
});
return rc;
};
/**
This routine guesses the approximate amount of
window.localStorage and/or window.sessionStorage in use by the
kvvfs database backend. Its argument must be one of
('session', 'local', ""). In the first two cases, only
sessionStorage resp. localStorage is counted. If it's an empty
string (the default) then both are counted. Only storage keys
which match the pattern used by kvvfs are counted. The returned
value is the "length" value of every matching key and value,
noting that JavaScript stores each character in 2 bytes.
Note that the returned size is not authoritative from the
perspective of how much data can fit into localStorage and
sessionStorage, as the precise algorithms for determining
those limits are unspecified and may include per-entry
overhead invisible to clients.
*/
capi.sqlite3_js_kvvfs_size = function(which=""){
let sz = 0;
const kvinfo = __kvvfsInfo(which);
kvinfo.stores.forEach((s)=>{
let i;
for(i = 0; i < s.length; ++i){
const k = s.key(i);
if(k.startsWith(kvinfo.prefix)){
sz += k.length;
sz += s.getItem(k).length;
}
}
});
return sz * 2 /* because JS uses 2-byte char encoding */;
};
}/* main-window-only bits */
/**
Wraps all known variants of the C-side variadic
sqlite3_db_config().
+333
View File
@@ -0,0 +1,333 @@
//#if not omit-kvvfs
/*
2025-11-21
The author disclaims copyright to this source code. In place of a
legal notice, here is a blessing:
* May you do good and not evil.
* May you find forgiveness for yourself and forgive others.
* May you share freely, never taking more than you give.
***********************************************************************
This file houses the "kvvfs" pieces of the JS API.
Main project home page: https://sqlite.org
Documentation home page: https://sqlite.org/wasm
*/
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
'use strict';
/* We unregister the kvvfs VFS from Worker threads later on. */
const util = sqlite3.util,
capi = sqlite3.capi,
wasm = sqlite3.wasm,
sqlite3_kvvfs_methods = capi.sqlite3_kvvfs_methods,
pKvvfs = sqlite3.capi.sqlite3_vfs_find("kvvfs");
delete capi.sqlite3_kvvfs_methods /* this is JS plumbing, not part
of the public API */;
if( !pKvvfs ) return /* built without kvvfs */;
if( !util.isUIThread() ){
/* One test currently relies on this VFS not being visible
in Workers. If we add generic object storage, we can
retain this VFS in Workers. */
capi.sqlite3_vfs_unregister(pKvvfs);
return;
}
/**
Internal helper for sqlite3_js_kvvfs_clear() and friends.
Its argument should be one of ('local','session',"").
*/
const __kvfsWhich = function(which){
const rc = Object.create(null);
rc.prefix = 'kvvfs-'+which;
rc.stores = [];
if('session'===which || ""===which) rc.stores.push(globalThis.sessionStorage);
if('local'===which || ""===which) rc.stores.push(globalThis.localStorage);
return rc;
};
/**
Clears all storage used by the kvvfs DB backend, deleting any
DB(s) stored there. Its argument must be either 'session',
'local', or "". In the first two cases, only sessionStorage
resp. localStorage is cleared. If it's an empty string (the
default) then both are cleared. Only storage keys which match
the pattern used by kvvfs are cleared: any other client-side
data are retained.
This function is only available in the main window thread.
Returns the number of entries cleared.
*/
capi.sqlite3_js_kvvfs_clear = function(which=""){
let rc = 0;
const kvWhich = __kvfsWhich(which);
kvWhich.stores.forEach((s)=>{
const toRm = [] /* keys to remove */;
let i;
for( i = 0; i < s.length; ++i ){
const k = s.key(i);
if(k.startsWith(kvWhich.prefix)) toRm.push(k);
}
toRm.forEach((kk)=>s.removeItem(kk));
rc += toRm.length;
});
return rc;
};
/**
This routine guesses the approximate amount of
window.localStorage and/or window.sessionStorage in use by the
kvvfs database backend. Its argument must be one of ('session',
'local', ""). In the first two cases, only sessionStorage
resp. localStorage is counted. If it's an empty string (the
default) then both are counted. Only storage keys which match
the pattern used by kvvfs are counted. The returned value is
twice the "length" value of every matching key and value,
noting that JavaScript stores each character in 2 bytes.
The returned size is not authoritative from the perspective of
how much data can fit into localStorage and sessionStorage, as
the precise algorithms for determining those limits are
unspecified and may include per-entry overhead invisible to
clients.
*/
capi.sqlite3_js_kvvfs_size = function(which=""){
let sz = 0;
const kvWhich = __kvfsWhich(which);
kvWhich.stores.forEach((s)=>{
let i;
for(i = 0; i < s.length; ++i){
const k = s.key(i);
if(k.startsWith(kvWhich.prefix)){
sz += k.length;
sz += s.getItem(k).length;
}
}
});
return sz * 2 /* because JS uses 2-byte char encoding */;
};
const kvvfsMakeKey = wasm.exports.sqlite3__wasm_kvvfsMakeKeyOnPstack;
const pstack = wasm.pstack;
const kvvfsStorage = (zClass)=>((115/*=='s'*/===wasm.peek(zClass))
? sessionStorage : localStorage);
{ /* Override native sqlite3_kvvfs_methods */
const kvvfsMethods = new sqlite3_kvvfs_methods(
/* Wraps the static sqlite3_api_methods singleton */
wasm.exports.sqlite3__wasm_kvvfs_methods()
);
try{
/**
Implementations for members of the object referred to by
sqlite3__wasm_kvvfs_methods(). We swap out the native
implementations with these, which use JS Storage for their
backing store.
The interface docs for these methods are in
src/os_kv.c:kvstorageRead(), kvstorageWrite(), and
kvstorageDelete().
*/
for(const e of Object.entries({
xRead: (zClass, zKey, zBuf, nBuf)=>{
const stack = pstack.pointer,
astack = wasm.scopedAllocPush();
try {
const zXKey = kvvfsMakeKey(zClass,zKey);
if(!zXKey) return -3/*OOM*/;
const jKey = wasm.cstrToJs(zXKey);
const jV = kvvfsStorage(zClass).getItem(jKey);
if(!jV) return -1;
const nV = jV.length /* We are relying 100% on v being
ASCII so that jV.length is equal
to the C-string's byte length. */;
if(nBuf<=0) return nV;
else if(1===nBuf){
wasm.poke(zBuf, 0);
return nV;
}
const zV = wasm.scopedAllocCString(jV);
if(nBuf > nV + 1) nBuf = nV + 1;
wasm.heap8u().copyWithin(
Number(zBuf), Number(zV), wasm.ptr.addn(zV, nBuf,- 1)
);
wasm.poke(wasm.ptr.add(zBuf, nBuf, -1), 0);
return nBuf - 1;
}catch(e){
sqlite3.config.error("kvstorageRead()",e);
return -2;
}finally{
pstack.restore(stack);
wasm.scopedAllocPop(astack);
}
},
xWrite: (zClass, zKey, zData)=>{
const stack = pstack.pointer;
try {
const zXKey = kvvfsMakeKey(zClass,zKey);
if(!zXKey) return 1/*OOM*/;
const jKey = wasm.cstrToJs(zXKey);
kvvfsStorage(zClass).setItem(jKey, wasm.cstrToJs(zData));
return 0;
}catch(e){
sqlite3.config.error("kvstorageWrite()",e);
return capi.SQLITE_IOERR;
}finally{
pstack.restore(stack);
}
},
xDelete: (zClass, zKey)=>{
const stack = pstack.pointer;
try {
const zXKey = kvvfsMakeKey(zClass,zKey);
if(!zXKey) return 1/*OOM*/;
kvvfsStorage(zClass).removeItem(wasm.cstrToJs(zXKey));
return 0;
}catch(e){
sqlite3.config.error("kvstorageDelete()",e);
return capi.SQLITE_IOERR;
}finally{
pstack.restore(stack);
}
}
})){
kvvfsMethods[kvvfsMethods.memberKey(e[0])] =
wasm.installFunction(kvvfsMethods.memberSignature(e[0]), e[1]);
}
}finally{
kvvfsMethods.dispose();
}
}/* Override native sqlite3_api_methods */
/**
After initial refactoring to support the use of arbitrary Storage
objects (the interface from which localStorage and sessionStorage
dervie), we will apparently need to override some of the
associated sqlite3_vfs and sqlite3_io_methods members.
We can call back into the native impls when needed, but we need
to override certain operations here to bypass its strict
db-naming rules (which, funnily enough, are in place because
they're relevant (only) for this browser-side
implementation). Apropos: the port to generic objects would also
make non-persistent kvvfs available in Worker threads and
non-browser builds.
*/
const eventualTodo = 1 || {
vfsMethods:{
/**
*/
xOpen: function(pProtoVfs,zName,pProtoFile,flags,pOutFlags){},
xDelete: function(pVfs, zName, iSyncFlag){},
xAccess:function(pProtoVfs, zPath, flags, pResOut){},
xFullPathname: function(pVfs, zPath, nOut, zOut){},
xDlOpen: function(pVfs, zFilename){},
xSleep: function(pVfs,usec){},
xCurrentTime: function(pVfs,pOutDbl){},
xCurrentTimeInt64: function(pVfs,pOutI64){},
xRandomness: function(pVfs, nOut, pOut){
const heap = wasm.heap8u();
let i = 0;
const npOut = Number(pOut);
for(; i < nOut; ++i) heap[npOut + i] = (Math.random()*255000) & 0xFF;
return i;
}
},
/**
kvvfs has separate impls for some of the I/O methods,
depending on whether it's a db or journal file.
*/
ioMethods:{
db:{
xClose: function(pFile){},
xRead: function(pFile,pTgt,n,iOff64){},
xWrite: function(pFile,pSrc,n,iOff64){},
xTruncate: function(pFile,i64){},
xSync: function(pFile,flags){},
xFileControl: function(pFile, opId, pArg){},
xFileSize: function(pFile,pi64Out){},
xLock: function(pFile,iLock){},
xUnlock: function(pFile,iLock){},
xCheckReservedLock: function(pFile,piOut){},
xFileControl: function(pFile,iOp,pArg){},
xSectorSize: function(pFile){},
xDeviceCharacteristics: function(pFile){}
},
jrnl:{
xClose: todoIOMethodsDb.xClose,
xRead: function(pFile,pTgt,n,iOff64){},
xWrite: function(pFile,pSrc,n,iOff64){},
xTruncate: function(pFile,i64){},
xSync: function(pFile,flags){},
xFileControl: function(pFile, opId, pArg){},
xFileSize: function(pFile,pi64Out){},
xLock: todoIOMethodsDb.xLock,
xUnlock: todoIOMethodsDb.xUnlock,
xCheckReservedLock: todoIOMethodsDb.xCheckReservedLock,
xFileControl: function(pFile,iOp,pArg){},
xSectorSize: todoIOMethodsDb.xSectorSize,
xDeviceCharacteristics: todoIOMethodsDb.xDeviceCharacteristics
}
}
}/*eventualTodo*/;
if(sqlite3?.oo1?.DB){
/**
Functionally equivalent to DB(storageName,'c','kvvfs') except
that it throws if the given storage name is not one of 'local'
or 'session'.
As of version 3.46, the argument may optionally be an options
object in the form:
{
filename: 'session'|'local',
... etc. (all options supported by the DB ctor)
}
noting that the 'vfs' option supported by main DB
constructor is ignored here: the vfs is always 'kvvfs'.
*/
sqlite3.oo1.JsStorageDb = function(
storageName = sqlite3.oo1.JsStorageDb.defaultStorageName
){
const opt = sqlite3.oo1.DB.dbCtorHelper.normalizeArgs(...arguments);
storageName = opt.filename;
if('session'!==storageName && 'local'!==storageName){
toss3("JsStorageDb db name must be one of 'session' or 'local'.");
}
opt.vfs = 'kvvfs';
sqlite3.oo1.DB.dbCtorHelper.call(this, opt);
};
sqlite3.oo1.JsStorageDb.defaultStorageName = 'session';
const jdb = sqlite3.oo1.JsStorageDb;
jdb.prototype = Object.create(sqlite3.oo1.DB.prototype);
/** Equivalent to sqlite3_js_kvvfs_clear(). */
jdb.clearStorage = capi.sqlite3_js_kvvfs_clear;
/**
Clears this database instance's storage or throws if this
instance has been closed. Returns the number of
database blocks which were cleaned up.
*/
jdb.prototype.clearStorage = function(){
return jdb.clearStorage(this.affirmDbOpen().filename);
};
/** Equivalent to sqlite3_js_kvvfs_size(). */
jdb.storageSize = capi.sqlite3_js_kvvfs_size;
/**
Returns the _approximate_ number of bytes this database takes
up in its storage or throws if this instance has been closed.
*/
jdb.prototype.storageSize = function(){
return jdb.storageSize(this.affirmDbOpen().filename);
};
}/*sqlite3.oo1.JsStorageDb*/
})/*globalThis.sqlite3ApiBootstrap.initializers*/;
//#endif not omit-kvvfs
+2 -2
View File
@@ -589,7 +589,7 @@ const installOpfsVfs = function callee(options){
/**
Returns an array of the deserialized state stored by the most
recent serialize() operation (from from this thread or the
recent serialize() operation (from this thread or the
counterpart thread), or null if the serialization buffer is
empty. If passed a truthy argument, the serialization buffer
is cleared after deserialization.
@@ -924,7 +924,7 @@ const installOpfsVfs = function callee(options){
fh.filename = zName;
fh.sab = new SharedArrayBuffer(state.fileBufferSize);
fh.flags = flags;
fh.readOnly = !(sqlite3.SQLITE_OPEN_CREATE & flags)
fh.readOnly = !(capi.SQLITE_OPEN_CREATE & flags)
&& !!(flags & capi.SQLITE_OPEN_READONLY);
const rc = opRun('xOpen', pFile, zName, flags, opfsFlags);
if(!rc){
+12
View File
@@ -93,6 +93,18 @@
#undef SQLITE_ENABLE_API_ARMOR
#define SQLITE_ENABLE_API_ARMOR 1
/**********************************************************************/
/* SQLITE_EXPERIMENTAL_PRAGMA_20251114 */
/*
** See:
** https://sqlite.org/src/info/e2b3f1a9480a9be3
** https://github.com/rhashimoto/wa-sqlite/discussions/301
**
** It is enabled here for the sake of VFS experimentors.
*/
#undef SQLITE_EXPERIMENTAL_PRAGMA_20251114
#define SQLITE_EXPERIMENTAL_PRAGMA_20251114
/**********************************************************************/
/* SQLITE_O... */
#undef SQLITE_OMIT_DEPRECATED
+29 -13
View File
@@ -16,27 +16,35 @@
More specifically:
https://fossil.wanderinghorse.net/r/jaccwabyt/file/common/whwasmutil.js
https://fossil.wanderinghorse.net/r/jaccwabyt/dir/wasmutil
and SQLite:
https://sqlite.org
This file is kept in sync between both of those trees.
This build was generated using:
./c-pp -o js/whwasmutil.js -@policy=error wasmutil/whwasmutil.c-pp.js
by libcmpp 2.x 2fc4afc31f6505c27b9c34988973a2bd9b157d559247cdd26868ae75632c3a5e @ 2025-11-16 23:03:27.352 UTC
*/
/**
The primary goal of this function is to replace, where possible,
Emscripten-generated glue code with equivalent utility code which
can be used in arbitrary WASM environments built with toolchains
other than Emscripten. To that end, it populates the given object
with various WASM-specific APIs. These APIs work with both 32- and
64-bit WASM builds.
The primary goal of this function is to provide JS/WASM utility
code similar to some of that provided by Emscripten-generated
builds, the difference being that this one can be used in arbitrary
WASM environments built with toolchains other than Emscripten. To
that end, it populates the given object with various WASM-specific
APIs. These APIs work with both 32- and 64-bit WASM builds.
Forewarning: this API explicitly targets only browser environments.
If a given non-browser environment has the capabilities needed for
a given feature (e.g. TextEncoder), great, but it does not go out
of its way to account for them and does not provide compatibility
crutches for them.
crutches for them. That said: no specific incompatibilities with,
e.g., node.js are known (whereas it is known that some folks
use this with node.js).
Intended usage:
@@ -217,7 +225,9 @@
newly-created (or config-provided) target. The current approach
seemed better at the time.
*/
globalThis.WhWasmUtilInstaller = function(target){
'use strict';
globalThis.WhWasmUtilInstaller =
function WhWasmUtilInstaller(target){
'use strict';
if(undefined===target.bigIntEnabled){
target.bigIntEnabled = !!globalThis['BigInt64Array'];
@@ -766,6 +776,7 @@ globalThis.WhWasmUtilInstaller = function(target){
*/
target.uninstallFunction = function(ptr){
if(!ptr && __NullPtr!==ptr) return undefined;
const ft = target.functionTable();
cache.freeFuncIndexes.push(ptr);
const rc = ft.get(ptr);
@@ -2461,7 +2472,8 @@ globalThis.WhWasmUtilInstaller = function(target){
- If `wasmUtilTarget.alloc` is not set and
`instance.exports.malloc` is, it installs
`wasmUtilTarget.alloc()` and `wasmUtilTarget.dealloc()`
wrappers for the exports `malloc` and `free` functions.
wrappers for the exports' `malloc` and `free` functions
if exports.malloc exists.
It returns a function which, when called, initiates loading of the
module and returns a Promise. When that Promise resolves, it calls
@@ -2484,7 +2496,9 @@ globalThis.WhWasmUtilInstaller = function(target){
Error handling is up to the caller, who may attach a `catch()` call
to the promise.
*/
globalThis.WhWasmUtilInstaller.yawl = function(config){
globalThis.WhWasmUtilInstaller
.yawl = function yawl(config){
'use strict';
const wfetch = ()=>fetch(config.uri, {credentials: 'same-origin'});
const wui = this;
const finalThen = function(arg){
@@ -2509,7 +2523,7 @@ globalThis.WhWasmUtilInstaller.yawl = function(config){
tgt.alloc = function(n){
return exports.malloc(n) || toss("Allocation of",n,"bytes failed.");
};
tgt.dealloc = function(m){exports.free(m)};
tgt.dealloc = function(m){m && exports.free(m)};
}
wui(tgt);
}
@@ -2528,4 +2542,6 @@ globalThis.WhWasmUtilInstaller.yawl = function(config){
.then(finalThen)
;
return loadWasm;
}.bind(globalThis.WhWasmUtilInstaller)/*yawl()*/;
}.bind(
globalThis.WhWasmUtilInstaller
)/*yawl()*/;
+346 -154
View File
@@ -16,9 +16,17 @@
Project homes:
- https://fossil.wanderinghorse.net/r/jaccwabyt
- https://sqlite.org/src/dir/ext/wasm/jaccwabyt
This build was generated using:
./c-pp -o js/jaccwabyt.js -@policy=error jaccwabyt/jaccwabyt.c-pp.js
by libcmpp 2.x 2fc4afc31f6505c27b9c34988973a2bd9b157d559247cdd26868ae75632c3a5e @ 2025-11-16 23:03:27.352 UTC
*/
'use strict';
globalThis.Jaccwabyt = function StructBinderFactory(config){
globalThis.Jaccwabyt =
function StructBinderFactory(config){
'use strict';
/* ^^^^ it is recommended that clients move that object into wherever
they'd like to have it and delete the globalThis-held copy. This
API does not require the global reference - it is simply installed
@@ -30,19 +38,32 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
all args with a space between each. */
const toss = (...args)=>{throw new Error(args.join(' '))};
if(!(config.heap instanceof WebAssembly.Memory)
&& !(config.heap instanceof Function)){
toss("config.heap must be WebAssembly.Memory instance or a function which returns one.");
{
let h = config.heap;
if( h instanceof WebAssembly.Memory ){
h = function(){return new Uint8Array(this.buffer)}.bind(h);
}else if( !(h instanceof Function) ){
//console.warn("The bothersome StructBinderFactory config:",config);
toss("config.heap must be WebAssembly.Memory instance or",
"a function which returns one.");
}
config.heap = h;
}
['alloc','dealloc'].forEach(function(k){
(config[k] instanceof Function) ||
toss("Config option '"+k+"' must be a function.");
});
const __heap = config.heap;
const SBF = StructBinderFactory;
const heap = __heap ? __heap : ()=>new Uint8Array(__heap.buffer),
const heap = config.heap,
alloc = config.alloc,
dealloc = config.dealloc,
realloc = (config.realloc || function(){
toss("This StructBinderFactory was configured without realloc()");
/* We can't know the original memory's size from here unless
we internally proxy alloc()/dealloc() to track all
pointers (not going to happen), so we can't fall back to
doing alloc()/copy/dealloc(). */
}),
log = config.log || console.debug.bind(console),
memberPrefix = (config.memberPrefix || ""),
memberSuffix = (config.memberSuffix || ""),
@@ -51,19 +72,21 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
bigIntEnabled = config.bigIntEnabled ?? !!BigInt64Array;
//console.warn("config",config);
let ptr = alloc(1);
const ptrIR = config.pointerIR
|| config.ptrIR/*deprecated*/
|| ('bigint'===typeof ptr ? 'i64' : 'i32');
/* Undocumented (on purpose) config options: */
const ptrSize = config.ptrSize/*deprecated*/
|| ('i32'===ptrIR ? 4 : 8);
dealloc(ptr);
ptr = undefined;
let ptr;
const ptrSize = config.pointerSize
|| config.ptrSize/*deprecated*/
|| ('bigint'===typeof (ptr = alloc(1)) ? 8 : 4);
const ptrIR = config.pointerIR/*deprecated*/
|| config.ptrIR/*deprecated*/
|| (4===ptrSize ? 'i32' : 'i64');
if( ptr ){
dealloc(ptr);
ptr = undefined;
}
//console.warn("ptrIR =",ptrIR,"ptrSize =",ptrSize);
if(ptrIR!=='i32' && ptrIR!=='i64') toss("Invalid pointer representation:",ptrIR);
if(ptrSize!==4 && ptrSize!==8) toss("Invalid pointer size:",ptrSize);
if(ptrIR!=='i32' && ptrIR!=='i64') toss("Invalid pointer representation:",ptrIR);
/** Either BigInt or, if !bigIntEnabled, a function which
throws complaining that BigInt is not enabled. */
@@ -91,6 +114,10 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
return rc;
};
const __ptrAddSelf = function(...args){
return __ptrAdd(this.pointer,...args);
};
if(!SBF.debugFlags){
SBF.__makeDebugFlags = function(deriveFrom=null){
/* This is disgustingly overengineered. :/ */
@@ -120,12 +147,12 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
SBF.debugFlags = SBF.__makeDebugFlags();
}/*static init*/
const isLittleEndian = (function() {
const isLittleEndian = true || (function() {
const buffer = new ArrayBuffer(2);
new DataView(buffer).setInt16(0, 256, true /* littleEndian */);
// Int16Array uses the platform's endianness.
return new Int16Array(buffer)[0] === 256;
})();
})() /* WASM is, by definition, Little Endian */;
/**
Some terms used in the internal docs:
@@ -146,8 +173,9 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
const isAutoPtrSig = (s)=>'P'===s /*EXPERIMENTAL*/;
/** Returns p if SIG s is a function SIG, else returns s[0]. */
const sigLetter = (s)=>s ? (isFuncSig(s) ? 'p' : s[0]) : undefined;
/** Returns the WASM IR form of the Emscripten-conventional letter
at SIG s[0]. Throws for an unknown SIG. */
/** Returns the WASM IR form of the letter at SIG s[0]. Throws for
an unknown SIG. */
const sigIR = function(s){
switch(sigLetter(s)){
case 'c': case 'C': return 'i8';
@@ -159,8 +187,9 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
}
toss("Unhandled signature IR:",s);
};
/** Returns the WASM sizeof of the Emscripten-conventional letter
at SIG s[0]. Throws for an unknown SIG. */
/** Returns the WASM sizeof of the letter at SIG s[0]. Throws for an
unknown SIG. */
const sigSize = function(s){
switch(sigLetter(s)){
case 'c': case 'C': return 1;
@@ -175,6 +204,7 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
const affirmBigIntArray = BigInt64Array
? ()=>true : ()=>toss('BigInt64Array is not available.');
/** Returns the name of a DataView getter method corresponding
to the given SIG. */
const sigDVGetter = function(s){
@@ -195,6 +225,7 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
}
toss("Unhandled DataView getter for signature:",s);
};
/** Returns the name of a DataView setter method corresponding
to the given SIG. */
const sigDVSetter = function(s){
@@ -246,26 +277,44 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
/**
In order to completely hide StructBinder-bound struct pointers
from JS code, we store them in a scope-local WeakMap which maps
the struct-bound objects to their WASM pointers. The pointers are
accessible via boundObject.pointer, which is gated behind a
property interceptor, but are not exposed anywhere else in the
object.
the struct-bound objects to an object with their metadata:
This approach means we cannot proxy arrays, or any type which
might be realloced, as that pointer could change out from under
us. That's not an issue for nested structs, but it might be for a
struct they're embedded in. In the case of nested structs we
"could" record their top-most parent object and their offset into
that object, instead of storing the pointer itself. We could that
by allowing a function instead of a pointer in this map, that
function returning the (lazily-calculated) address. Hmm.
{
.p = the native pointer,
.o = self (for an eventual reverse-mapping),
.xb = extra bytes allocated for p,
.zod = zeroOnDispose,
.ownsPointer = true if this object owns p
}
The .p data are accessible via obj.pointer, which is gated behind
a property interceptor, but are not exposed anywhere else in the
public API.
*/
const __instancePointerMap = new WeakMap();
const getInstanceHandle = function f(obj, create=true){
let ii = f.map.get(obj);
if( !ii && create ){
f.map.set(obj, (ii=f.create(obj)));
}
return ii;
};
getInstanceHandle.map = new WeakMap;
getInstanceHandle.create = (forObj)=>{
return Object.assign(Object.create(null),{
o: forObj,
p: undefined/*native ptr*/,
ownsPointer: false,
zod: false/*zeroOnDispose*/,
xb: 0/*extraBytes*/
});
};
const getInstancePtr = (obj)=>__instancePointerMap.get(obj);
/** Property name for the pointer-is-external marker. */
const xPtrPropName = Symbol('(pointer-is-external)');
/**
Remove the getInstanceHandle() mapping for obj.
*/
const rmInstanceHandle = (obj)=>getInstanceHandle.map.delete(obj)
/* If/when we have a reverse map of ptr-to-objects, we need to
clean that here. */;
const __isPtr32 = (ptr)=>('number'===typeof ptr && (ptr===(ptr|0)) && ptr>=0);
const __isPtr64 = (ptr)=>(
@@ -278,85 +327,127 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
*/
const __isPtr = (4===ptrSize) ? __isPtr32 : __isPtr64;
/** Frees the obj.pointer memory and clears the pointer
property. */
const __isNonNullPtr = (v)=>__isPtr(v) && (v>0);
/** Frees the obj.pointer memory (a.k.a. m), handles obj.ondispose,
and unmaps obj from its native resources. */
const __freeStruct = function(ctor, obj, m){
if(!m) m = getInstancePtr(obj);
if(m) {
__instancePointerMap.delete(obj);
if(Array.isArray(obj.ondispose)){
let x;
while((x = obj.ondispose.shift())){
try{
if(x instanceof Function) x.call(obj);
else if(x instanceof StructType) x.dispose();
else if(__isPtr(x)) dealloc(x);
// else ignore. Strings are permitted to annotate entries
// to assist in debugging.
}catch(e){
console.warn("ondispose() for",ctor.structName,'@',
m,'threw. NOT propagating it.',e);
}
}
}else if(obj.ondispose instanceof Function){
try{obj.ondispose()}
catch(e){
/*do not rethrow: destructors must not throw*/
const ii = getInstanceHandle(obj, false);
if( !ii ) return;
rmInstanceHandle(obj);
if( !m && !(m = ii.p) ){
console.warn("Cannot(?) happen: __freeStruct() found no instanceInfo");
return;
}
if(Array.isArray(obj.ondispose)){
let x;
while((x = obj.ondispose.pop())){
try{
if(x instanceof Function) x.call(obj);
else if(x instanceof StructType) x.dispose();
else if(__isPtr(x)) dealloc(x);
// else ignore. Strings are permitted to annotate entries
// to assist in debugging.
}catch(e){
console.warn("ondispose() for",ctor.structName,'@',
m,'threw. NOT propagating it.',e);
}
}
delete obj.ondispose;
if(ctor.debugFlags.__flags.dealloc){
log("debug.dealloc:",(obj[xPtrPropName]?"EXTERNAL":""),
ctor.structName,"instance:",
ctor.structInfo.sizeof,"bytes @"+m);
}
if(!obj[xPtrPropName]){
if( ctor.structInfo.zeroOnFree ){
heap().fill(0, Number(m), Number(m)+ctor.structInfo.sizeof);
}
dealloc(m);
}else if(obj.ondispose instanceof Function){
try{obj.ondispose()}
catch(e){
/*do not rethrow: destructors must not throw*/
console.warn("ondispose() for",ctor.structName,'@',
m,'threw. NOT propagating it.',e);
}
}
delete obj.ondispose;
if(ctor.debugFlags.__flags.dealloc){
log("debug.dealloc:",(ii.ownsPointer?"":"EXTERNAL"),
ctor.structName,"instance:",
ctor.structInfo.sizeof,"bytes @"+m);
}
if(ii.ownsPointer){
if( ii.zod || ctor.structInfo.zeroOnDispose ){
heap().fill(0, Number(m),
Number(m) + ctor.structInfo.sizeof + ii.xb);
}
dealloc(m);
}
};
/** Returns a skeleton for a read-only, non-iterable property
* descriptor. */
const rop0 = ()=>{return {configurable: false, writable: false,
iterable: false}};
/** Returns a skeleton for a read-only property accessor wrapping
value v. */
const rop = (v)=>{return {configurable: false, writable: false,
iterable: false, value: v}};
const rop = (v)=>{return {...rop0(), value: v}};
/** Allocates obj's memory buffer based on the size defined in
ctor.structInfo.sizeof. */
const __allocStruct = function(ctor, obj, m){
let fill = !m;
if(m) Object.defineProperty(obj, xPtrPropName, rop(m));
else{
m = alloc(ctor.structInfo.sizeof);
if(!m) toss("Allocation of",ctor.structName,"structure failed.");
const __allocStruct = function f(ctor, obj, xm){
let opt;
const checkPtr = (ptr)=>{
__isNonNullPtr(ptr) ||
toss("Invalid pointer value",arguments[0],"for",ctor.structName,"constructor.");
};
if( arguments.length>=3 ){
if( xm && ('object'===typeof xm) ){
opt = xm;
xm = opt?.wrap;
}else{
checkPtr(xm);
opt = {wrap: xm};
}
}else{
opt = {}
}
const fill = !xm /* true if we need to zero the memory */;
let nAlloc = 0;
let ownsPointer = false;
if(xm){
/* Externally-allocated memory. */
checkPtr(xm);
ownsPointer = !!opt?.takeOwnership;
}else{
const nX = opt?.extraBytes ?? 0;
if( nX<0 || (nX!==(nX|0)) ){
toss("Invalid extraBytes value:",opt?.extraBytes);
}
nAlloc = ctor.structInfo.sizeof + nX;
xm = alloc(nAlloc)
|| toss("Allocation of",ctor.structName,"structure failed.");
ownsPointer = true;
}
try {
if(ctor.debugFlags.__flags.alloc){
if( opt?.debugFlags ){
/* specifically undocumented */
obj.debugFlags(opt.debugFlags);
}
if(ctor./*prototype.???*/debugFlags.__flags.alloc){
log("debug.alloc:",(fill?"":"EXTERNAL"),
ctor.structName,"instance:",
ctor.structInfo.sizeof,"bytes @"+m);
ctor.structInfo.sizeof,"bytes @"+xm);
}
if(fill){
heap().fill(0, Number(m), Number(m) + ctor.structInfo.sizeof);
heap().fill(0, Number(xm), Number(xm) + nAlloc);
}
const ii = getInstanceHandle(obj);
ii.p = xm;
ii.ownsPointer = ownsPointer;
ii.xb = nAlloc ? (nAlloc-ctor.structInfo.sizeof) : 0;
ii.zod = !!opt?.zeroOnDispose;
if( opt?.ondispose && opt.ondispose!==xm ){
obj.addOnDispose( opt.ondispose );
}
__instancePointerMap.set(obj, m);
}catch(e){
__freeStruct(ctor, obj, m);
__freeStruct(ctor, obj, xm);
throw e;
}
};
/** Gets installed as the memoryDump() method of all structs. */
const __memoryDump = function(){
const p = this.pointer;
return p
? new Uint8Array(heap().slice(Number(p), Number(p) + this.structInfo.sizeof))
: null;
};
/** True if sig looks like an emscripten/jaccwabyt
type signature, else false. */
@@ -390,25 +481,33 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
// StructBinder::adaptGet()
const __adaptGet = function(key, ...args){
/*if( looksLikeASig(key) ){
toss("Getter adaptor's name (",key,") collides with a data type signature.");
}*/
return __adaptor(this, 0, key, ...args);
};
const __affirmNotASig = function(ctx,key){
looksLikeASig(key) &&
toss(ctx,"(",key,") collides with a data type signature.");
};
// StructBinder::adaptSet()
const __adaptSet = function(key, ...args){
if( looksLikeASig(key) ){
toss("Setter adaptor's name (",key,") collides with a data type signature.");
}
__affirmNotASig('Setter adaptor',key);
return __adaptor(this, 1, key, ...args);
};
// StructBinder::adaptStruct()
const __adaptStruct = function(key, ...args){
__affirmNotASig('Struct adaptor',key);
return __adaptor(this, 2, key, ...args);
};
/**
An internal counterpart of __adaptStruct(). If key is-a string,
uses __adaptor(who) to fetch the struct-adaptor entry for key,
else key is assumed to be a struct description object. If it
resolves to an object, that's returned, else an exception is
thrown.
*/
const __adaptStruct2 = function(who,key){
const si = ('string'===typeof key)
? __adaptor(who, 2, key) : key;
@@ -455,15 +554,6 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
return emscriptenFormat ? f._(m.signature) : m.signature;
};
const __ptrPropDescriptor = {
configurable: false, enumerable: false,
get: function(){return getInstancePtr(this)},
set: ()=>toss("Cannot assign the 'pointer' property of a struct.")
// Reminder: leaving `set` undefined makes assignments
// to the property _silently_ do nothing. Current unit tests
// rely on it throwing, though.
};
/** Impl of X.memberKeys() for StructType and struct ctors. */
const __structMemberKeys = rop(function(){
const a = [];
@@ -597,13 +687,13 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
Prototype for all StructFactory instances (the constructors
returned from StructBinder).
*/
const StructType = function ctor(structName, structInfo){
if(arguments[2]!==rop){
const StructType = function StructType(structName, structInfo){
if(arguments[2]!==rop/*internal sentinel value*/){
toss("Do not call the StructType constructor",
"from client-level code.");
}
Object.defineProperties(this,{
//isA: rop((v)=>v instanceof ctor),
//isA: rop((v)=>v instanceof StructType),
structName: rop(structName),
structInfo: rop(structInfo)
});
@@ -629,8 +719,31 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
memberSignature: rop(function(memberName, emscriptenFormat=false){
return __memberSignature(this, memberName, emscriptenFormat);
}),
memoryDump: rop(__memoryDump),
pointer: __ptrPropDescriptor,
memoryDump: rop(function(){
const p = this.pointer;
return p
? new Uint8Array(heap().slice(Number(p), Number(p) + this.structInfo.sizeof))
: null;
}),
extraBytes: {
configurable: false, enumerable: false,
get: function(){return getInstanceHandle(this, false)?.xb ?? 0;}
},
zeroOnDispose: {
configurable: false, enumerable: false,
get: function(){
return getInstanceHandle(this, false)?.zod
?? !!this.structInfo.zeroOnDispose;
}
},
pointer: {
configurable: false, enumerable: false,
get: function(){return getInstanceHandle(this, false)?.p},
set: ()=>toss("Cannot assign the 'pointer' property of a struct.")
// Reminder: leaving `set` undefined makes assignments
// to the property _silently_ do nothing. Current unit tests
// rely on it throwing, though.
},
setMemberCString: rop(function(memberName, str){
return __setMemberCString(this, memberName, str);
})
@@ -649,8 +762,12 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
Object.defineProperties(StructType, {
allocCString: rop(__allocCString),
isA: rop((v)=>v instanceof StructType),
hasExternalPointer: rop((v)=>(v instanceof StructType) && !!v[xPtrPropName]),
hasExternalPointer: rop((v)=>{
const ii = getInstanceHandle(v, false);
return !!(ii?.p && !ii?.ownsPointer);
}),
memberKey: __memberKeyProp
//ptrAdd = rop(__ptrAdd) no b/c one might think that it adds based on this.pointer.
});
/**
@@ -693,7 +810,7 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
do any permanent harm, as the wrapper object will be recreated
when accessing f.bar, pointing to the same memory in f.
The si.zeroOnFree flag has no effect on embedded structs because
The si.zeroOnDispose flag has no effect on embedded structs because
they wrap "external" memory, so do not own it, and are thus never
freed, as such.
*/
@@ -725,7 +842,7 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
enumerable: false,
set: __propThrowOnSet(ctor/*not si.constructor*/.structName, key),
get: function(){
const dbg = si.constructor.prototype.debugFlags.__flags;
const dbg = this.debugFlags.__flags;
const p = this.pointer;
const k = p+'.'+key;
let s = __innerStructs.get(k);
@@ -746,6 +863,8 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
}/*makeMemberStructWrapper()*/;
/**
This is where most of the magic happens.
Pass this a StructBinderImpl-generated constructor, a member
property name, and the struct member description object. It will
define property accessors for proto[memberKey] which read
@@ -758,16 +877,16 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
return makeMemberStructWrapper.call(this, ctor, name, si);
}
if(!f._){
if(!f.cache){
/* Cache all available getters/setters/set-wrappers for
direct reuse in each accessor function. */
f._ = {getters: {}, setters: {}, sw:{}};
f.cache = {getters: {}, setters: {}, sw:{}};
const a = ['i','c','C','p','P','s','f','d','v()'];
if(bigIntEnabled) a.push('j');
a.forEach(function(v){
f._.getters[v] = sigDVGetter(v) /* DataView[MethodName] values for GETTERS */;
f._.setters[v] = sigDVSetter(v) /* DataView[MethodName] values for SETTERS */;
f._.sw[v] = sigDVSetWrapper(v) /* BigInt or Number ctor to wrap around values
f.cache.getters[v] = sigDVGetter(v) /* DataView[MethodName] values for GETTERS */;
f.cache.setters[v] = sigDVSetter(v) /* DataView[MethodName] values for SETTERS */;
f.cache.sw[v] = sigDVSetWrapper(v) /* BigInt or Number ctor to wrap around values
for conversion */;
});
f.sigCheck = function(obj, name, key,sig){
@@ -784,8 +903,8 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
si.key = key;
si.name = name;
const sigGlyph = sigLetter(si.signature);
const xPropName = sPropName(ctor.prototype.structName,key);
const dbg = ctor.prototype.debugFlags.__flags;
const xPropName = sPropName(ctor.structName,key);
const dbg = ctor.debugFlags.__flags;
/*
TODO?: set prototype of si to an object which can set/fetch
its preferred representation, e.g. conversion to string or mapped
@@ -797,14 +916,20 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
prop.configurable = false;
prop.enumerable = false;
prop.get = function(){
/**
This getter proxy reads its value from the appropriate pointer
address in the heap. It knows where and how much to read based on
this.pointer, si.offset, and si.sizeof.
*/
if(dbg.getter){
log("debug.getter:",f._.getters[sigGlyph],"for", sigIR(sigGlyph),
log("debug.getter:",f.cache.getters[sigGlyph],"for", sigIR(sigGlyph),
xPropName,'@', this.pointer,'+',si.offset,'sz',si.sizeof);
}
let rc = (
new DataView(heap().buffer, Number(this.pointer) + si.offset, si.sizeof)
)[f._.getters[sigGlyph]](0, isLittleEndian);
if(getterProxy) rc = getterProxy.apply(this,[rc,key]);
)[f.cache.getters[sigGlyph]](0, isLittleEndian);
if(getterProxy) rc = getterProxy.apply(this,[key,rc]);
if(dbg.getter) log("debug.getter:",xPropName,"result =",rc);
return rc;
};
@@ -813,28 +938,33 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
}else{
const setterProxy = memberSetterProxy(si);
prop.set = function(v){
/**
The converse of prop.get(), this encodes v into the appropriate
spot in the WASM heap.
*/
if(dbg.setter){
log("debug.setter:",f._.setters[sigGlyph],"for", sigIR(sigGlyph),
log("debug.setter:",f.cache.setters[sigGlyph],"for", sigIR(sigGlyph),
xPropName,'@', this.pointer,'+',si.offset,'sz',si.sizeof, v);
}
if(!this.pointer){
toss("Cannot set native property on a disposed struct instance.");
toss("Cannot set native property on a disposed",
this.structName,"instance.");
}
if( setterProxy ) v = setterProxy.apply(this,[v]);
if( setterProxy ) v = setterProxy.apply(this,[key,v]);
if( null===v || undefined===v ) v = __NullPtr;
else while( isPtrSig(si.signature) && !__isPtr(v) ){
else if( isPtrSig(si.signature) && !__isPtr(v) ){
if(isAutoPtrSig(si.signature) && (v instanceof StructType)){
// It's a struct instance: let's store its pointer value!
// It's a struct instance: store its pointer value
v = v.pointer || __NullPtr;
if(dbg.setter) log("debug.setter:",xPropName,"resolved to",v);
break;
}else{
toss("Invalid value for pointer-type",xPropName+'.');
}
toss("Invalid value for pointer-type",xPropName+'.');
}
(
new DataView(heap().buffer, Number(this.pointer) + si.offset,
si.sizeof)
)[f._.setters[sigGlyph]](0, f._.sw[sigGlyph](v), isLittleEndian);
)[f.cache.setters[sigGlyph]](0, f.cache.sw[sigGlyph](v), isLittleEndian);
};
}
Object.defineProperty(ctor.prototype, key, prop);
@@ -863,25 +993,59 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
StructCtor is the eventual return value of this function. We
need to populate this early on so that we can do some trickery
in feeding it through recursion.
Uses:
// heap-allocated:
const x = new StructCtor;
// externally-managed memory:
const y = new StructCtor( aPtrToACompatibleCStruct );
or, more recently:
const z = new StructCtor({
extraBytes: [int=0] extra bytes to allocate after the struct
wrap: [aPtrToACompatibleCStruct=undefined]. If provided, this
instance waps, but does not (by default) own the memory, else
a new instance is allocated from the WASM heap.
ownsPointer: true if this object takes over ownership of
wrap.
zeroOnDispose: [bool=StructCtor.structInfo.zeroOnDispose]
autoCalcSizeOffset: [bool=false] Automatically calculate
sizeof an offset. This is fine for pure-JS structs (which
probably aren't useful beyond testing of Jaccwabyt) but it's
dangerous to use with actual WASM objects because we cannot
be guaranteed to have the same memory layout as an ostensibly
matching C struct. This applies recursively to all children
of the struct description.
// TODO? Per-instance overrides of the struct-level flags?
get: (k,v)=>v,
set: (k,v)=>v,
adaptGet: string,
adaptSet: string
// That wouldn't fit really well right now, apparently.
});
*/
const StructCtor = function StructCtor(externalMemory){
externalMemory = __asPtrType(externalMemory);
//console.warn("externalMemory",externalMemory,arguments[0]);
const StructCtor = function StructCtor(arg){
//console.warn("opt",opt,arguments[0]);
if(!(this instanceof StructCtor)){
toss("The",structName,"constructor may only be called via 'new'.");
}else if(arguments.length){
if( !__isPtr(externalMemory) ){
toss("Invalid pointer value",arguments[0],"for",structName,"constructor.");
}
__allocStruct(StructCtor, this, externalMemory);
}else{
__allocStruct(StructCtor, this);
}
__allocStruct(StructCtor, this, ...arguments);
};
const self = this;
/**
"Convert" struct description x to a struct description, if
needed.
needed. This expands adaptStruct() mappings and transforms
{memberName:signatureString} signature syntax to object form.
*/
const ads = (x)=>{
//console.warn("looksLikeASig(",x,") =",looksLikeASig(x));
@@ -902,6 +1066,7 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
structName ??= opt.structName;
if( !structName ) toss("One of 'name' or 'structName' are required.");
if( si.adapt ){
/* Install adaptGet(), adaptSet(), and adaptStruct() proxies. */
Object.keys(si.adapt.struct||{}).forEach((k)=>{
__adaptStruct.call(StructBinderImpl, k, si.adapt.struct[k]);
});
@@ -924,21 +1089,32 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
memberKeys: __structMemberKeys,
//methodInfoForKey: rop(function(mKey){/*???*/}),
structInfo: rop(si),
structName: rop(structName)
structName: rop(structName),
ptrAdd: rop(__ptrAdd)
});
StructCtor.prototype = new StructType(structName, si, rop);
Object.defineProperties(StructCtor.prototype,{
debugFlags: debugFlags,
constructor: rop(StructCtor)
/*if we assign StructCtor.prototype and don't do
this then StructCtor!==instance.constructor*/
this then StructCtor!==instance.constructor*/,
ptrAdd: rop(__ptrAddSelf)
});
let lastMember = false;
let offset = 0;
const autoCalc = !!si.autoCalcSizeOffset;
//console.warn(structName,"si =",si);
si.offset ??= 0;
if( !autoCalc ){
if( !si.sizeof ){
toss(structName,"description is missing its sizeof property.");
}
/*if( undefined===si.offset ){
toss(structName,"description is missing its offset property.");
}*/
si.offset ??= 0;
}else{
si.offset ??= 0;
}
Object.keys(si.members || {}).forEach((k)=>{
// Sanity checks of sizeof/offset info...
let m = ads(si.members[k]);
@@ -951,11 +1127,23 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
}
}
if( undefined===m.offset ){
m.offset = offset;
if( autoCalc ) m.offset = offset;
else{
toss(sPropName(structName,k),"is missing its offset.",
JSON.stringify(m));
}
/* A missing offset on the initial child is okay (it's always
zero), but we don't know for sure that the members are
their natural order, so we don't know, at this point, which
one is "first". */
}
si.members[k] = m;
si.members[k] = m /* in case ads() resolved it to something else */;
if(!lastMember || lastMember.offset < m.offset) lastMember = m;
makeMemberWrapper.call(self, StructCtor, k, m)
const oldAutoCalc = !!m.autoCalc;
if( autoCalc ) m.autoCalcSizeOffset = true;
makeMemberWrapper.call(self, StructCtor, k, m);
if( oldAutoCalc ) m.autoCalcSizeOffset = true;
else delete m.autoCalcSizeOffset;
offset += m.sizeof;
//console.warn("offset",sPropName(structName,k),offset);
});
@@ -979,12 +1167,15 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
toss(structName,"offset is not aligned. offset="+si.offset);
}
}
if( si.sizeof < offset ){
console.warn("Suspect struct description:",si,"offset =",offset);
toss("Mismatch in the calculated vs. the provided sizeof/offset info.",
"Expected sizeof",offset,"but got",si.sizeof,"for",si);
/* It is legal for the native struct to be larger, so long as
we're pointing to all the right offsets for the members
exposed here. */
}
delete si.autoCalcSizeOffset;
return StructCtor;
}/*StructBinderImpl*/;
@@ -999,6 +1190,7 @@ globalThis.Jaccwabyt = function StructBinderFactory(config){
StructBinder.adaptGet = __adaptGet;
StructBinder.adaptSet = __adaptSet;
StructBinder.adaptStruct = __adaptStruct;
StructBinder.ptrAdd = __ptrAdd;
if(!StructBinder.debugFlags){
StructBinder.debugFlags = SBF.__makeDebugFlags(SBF.debugFlags);
}
File diff suppressed because it is too large Load Diff
+3 -2
View File
@@ -1027,7 +1027,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
let ptr = k1.pointer;
k1.dispose();
T.assert(undefined === k1.pointer).
mustThrowMatching(()=>{k1.$pP=1}, /disposed struct instance/);
mustThrowMatching(()=>{k1.$pP=1}, /disposed/);
}finally{
k1.dispose();
k2.dispose();
@@ -2907,11 +2907,12 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
]);
T.assert(3 === db.selectValue('select count(*) from kvvfs'));
db.close();
db = undefined;
db = new JDb(filename);
db.exec('insert into kvvfs(a) values(4),(5),(6)');
T.assert(6 === db.selectValue('select count(*) from kvvfs'));
}finally{
db.close();
if( db ) db.close();
}
}
}/*kvvfs sanity checks*/)
+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 \
+72 -62
View File
@@ -1,12 +1,12 @@
C Experimental\sSQLITE_MAX_SUBQUERY\scompile-time\soption.
D 2025-11-18T00:16:40.468
C In\sthe\sCLI\son\sthe\s".mode"\scommand,\sreplace\sthe\srecently\sadded\s--no-limits\soption\swith\n"--limits\sL,C".\s\sAnd\sadd\s--sw\sas\san\salias\sfor\s--screenwidth.
D 2025-11-24T12:27:18.181
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
@@ -18,7 +18,7 @@ F art/sqlite370.svg 40b7e2fe8aac3add5d56dd86ab8d427a4eca5bcb3fe4f8946cb3794e1821
F auto.def 44a0d1bf09d78355fc88251ccbf8e64e6341fd89c11de68a01c3645e53a2bade
F autoconf/Makefile.fallback 22fe523eb36dfce31e0f6349f782eb084e86a5620b2b0b4f84a2d6133f53f5ac
F autoconf/Makefile.in 306f500fce8cae9e1af9459f1ac7fe89ce46999594ef3fdc62dc9d7d651fb091
F autoconf/Makefile.msc 803aa9e23b6a764751f6a91d0673620eefa46729fb80faf51391120e1d752966
F autoconf/Makefile.msc 7952ed1fbb824f9421688e6b46d09fc4d5a13decec791a6264be4f65e64dfc08
F autoconf/README.first f1d3876e9a7852c22f275a6f06814e64934cecbc0b5b9617d64849094c1fd136
F autoconf/README.txt b749816b8452b3af994dc6d607394bef3df1736d7e09359f1087de8439a52807
F autoconf/auto.def 3d994f3a9cc9b712dbce92a5708570ddcf3b988141b6eb738f2ed16127a9f0ac
@@ -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
@@ -60,7 +60,7 @@ F doc/compile-for-unix.md c9dce1ddd4bf0d25efccc5c63eb047e78c01ce06a6ff29c73e0a8a
F doc/compile-for-windows.md f9e74d74da88f384edd5809f825035e071608f00f7f39c0e448df7b3982f979c
F doc/json-enhancements.md e356fc834781f1f1aa22ee300027a270b2c960122468499bf347bb123ce1ea4f
F doc/jsonb.md acd77fc3a709f51242655ad7803510c886aa8304202fa9cf2abc5f5c4e9d7ae5
F doc/lemon.html 89ea833a6f71773ab1a9063fbb7fb9b32147bc0b1057b53ecab94a3b30c0aef5
F doc/lemon.html 2085fda0a90a94fe92159a79dccc5c30d5a2313524887a31659cd66162f17233
F doc/pager-invariants.txt 83aa3a4724b2d7970cc3f3461f0295c46d4fc19a835a5781cbb35cb52feb0577
F doc/tcl-extension-testing.md b88861804fc1eaf83249f8e206334189b61e150c360e1b80d0dcf91af82354f5
F doc/testrunner.md 5ee928637e03f136a25fef852c5ed975932e31927bd9b05a574424ae18c31019
@@ -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
@@ -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 7f8498231be49e9fa6290f75c611ccb8658e2d50f0e0dc4207923e59bc63c6f9
F ext/qrf/qrf.h bccc9d1c1b6a05fa0da56332bba29d85782318926f022c849114a63da916393f
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 58b851cb13559027499f25050a8c4a103b4c674d97f2ce4e75f2acc8cb112de7
F ext/wasm/README-dist.txt f01081a850ce38a56706af6b481e3a7878e24e42b314cfcd4b129f0f8427066a
F ext/wasm/README.md 2e87804e12c98f1d194b7a06162a88441d33bb443efcfe00dc6565a780d2f259
F ext/wasm/SQLTester/GNUmakefile e0794f676d55819951bbfae45cc5e8d7818dc460492dc317ce7f0d2eca15caff
@@ -583,31 +586,32 @@ F ext/wasm/SQLTester/SQLTester.mjs 6b3c52ed36a5573ca4883176f326332a8d4c0cecf5efd
F ext/wasm/SQLTester/SQLTester.run.mjs 57f2adb33f43f2784abbf8026c1bfd049d8013af1998e7dcb8b50c89ffc332e0
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/EXPORTED_FUNCTIONS.c-pp 7ba933e8f1290cc65459dd371c0c9a031d96bdf14d7a2244fa761d9775117b90
F ext/wasm/api/README.md aae128e14711dafac29e7b645ff1b6be42305b73f74b1303d8401351f8448631
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/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-glue.c-pp.js 9b33e3ee467791dec4fd1b444b12a8545dfbb6c8b28ac651c7bdc7661a3b5a5c
F ext/wasm/api/sqlite3-api-oo1.c-pp.js 8ce38bd4b22aa2b0311c7a8e87e748e06213766fe2141de7574672d103ece255
F ext/wasm/api/sqlite3-api-prologue.js 7004b569624765c5132984bfecee2305bef928a6adf44e0202dacc9cbc5c8e2a
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-kvvfs.c-pp.js f44a4396b2305b27dc81c00a1629fe4f615520f7fb11ed4e606255e365956ba2
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 88ce2078267a2d1af57525a32d896295f4a8db7664de0e17e82dc9ff006ed8d3
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 7778527bdbb241b8e742a5fc50e470e8b8d0529c41fab9ac127c2c63f3c90d47
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 831f07a0d9bb61713164871370811432e96d0f813806a4d2c783d3c77c2373a0
F ext/wasm/config.make.in c424ae1cc3c89274520ad312509d36c4daa34a3fce5d0c688e5f8f4365e1049a
F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed
F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508
@@ -624,8 +628,8 @@ F ext/wasm/fiddle/fiddle.js 84fd75967e0af8b69d3dd849818342227d0f81d13db92e0dcbc6
F ext/wasm/fiddle/index.html a27b8127ef9ecf19612da93b2a6a73bdb3777b5c56b5450bb7200a94bc108ff9
F ext/wasm/index-dist.html db23748044e286773f2768eec287669501703b5d5f72755e8db73607dc54d290
F ext/wasm/index.html 54e27db740695ab2cb296e02d42c4c66b3f11b65797340d19fa6590f5b287da1
F ext/wasm/jaccwabyt/jaccwabyt.js 1e734c624205cdf621f322972dfb0fc8013d573a5882f57492a6830e5ec23e17
F ext/wasm/jaccwabyt/jaccwabyt.md 167fc0b624c9bc2c477846e336de9403842d81b1a24fc4d3b24317cb9eba734f
F ext/wasm/jaccwabyt/jaccwabyt.js 4e2b797dc170851c9c530c3567679f4aa509eec0fab73b466d945b00b356574b
F ext/wasm/jaccwabyt/jaccwabyt.md 6aa90fa1a973d0ad10d077088bea163b241d8470c75eafdef87620a1de1dea41
F ext/wasm/mkdist.sh 64d53f469c823ed311f6696f69cec9093f745e467334b34f5ceabdf9de3c5b28 x
F ext/wasm/mkwasmbuilds.c ef42e404236dd98cedb6ecea47b6d2474e3c593633ce6d992d316289dfc442b6
F ext/wasm/module-symbols.html e54f42112e0aac2a31f850ab33e7f2630a2ea4f63496f484a12469a2501e07e2
@@ -643,7 +647,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555
F ext/wasm/test-opfs-vfs.js 1618670e466f424aa289859fe0ec8ded223e42e9e69b5c851f809baaaca1a00c
F ext/wasm/tester1-worker.c-pp.html 0e432ec2c0d99cd470484337066e8d27e7aee4641d97115338f7d962bf7b081a
F ext/wasm/tester1.c-pp.html 52d88fe2c6f21a046030a36410b4839b632f4424028197a45a3d5669ea724ddb
F ext/wasm/tester1.c-pp.js 2c255093205a0dac9dae7475030665c2c9d6dccc857de68ee7daf49aa82e6de8
F ext/wasm/tester1.c-pp.js 015b4133cc3a5fb41d6236a6b39d23d996cc2d61a4877acde31a1f69574d4ce3
F ext/wasm/tests/opfs/concurrency/index.html 657578a6e9ce1e9b8be951549ed93a6a471f4520a99e5b545928668f4285fb5e
F ext/wasm/tests/opfs/concurrency/test.js d08889a5bb6e61937d0b8cbb78c9efbefbf65ad09f510589c779b7cc6a803a88
F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2
@@ -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
@@ -663,7 +667,7 @@ F mptest/multiwrite01.test dab5c5f8f9534971efce679152c5146da265222d
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
F sqlite3.1 1b9c24374a85dfc7eb8fa7c4266ee0db4f9609cceecfc5481cd8307e5af04366
F sqlite3.pc.in e6dee284fba59ef500092fdc1843df3be8433323a3733c91da96690a50a5b398
F src/alter.c fc7bbbeb9e89c7124bf5772ce474b333b7bdc18d6e080763211a40fde69fb1da
F src/alter.c f31437552c733957f19351cdfae8fad8e8f0c7d11041e5b7966aae57206ad91f
F src/analyze.c 03bcfc083fc0cccaa9ded93604e1d4244ea245c17285d463ef6a60425fcb247d
F src/attach.c 9af61b63b10ee702b1594ecd24fb8cea0839cfdb6addee52fba26fa879f5db9d
F src/auth.c 54ab9c6c5803b47c0d45b76ce27eff22a03b4b1f767c5945a3a4eb13aa4c78dc
@@ -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
@@ -688,13 +692,13 @@ F src/func.c 0b802107498048d3dcac0b757720bcb8506507ce02159e213ab8161458eb293b
F src/global.c a19e4b1ca1335f560e9560e590fc13081e21f670643367f99cb9e8f9dc7d615b
F src/hash.c 03c8c0f4be9e8bcb6de65aa26d34a61d48a9430747084a69f9469fbb00ea52ca
F src/hash.h 46b92795a95bfefb210f52f0c316e9d7cdbcdd7e7fcfb0d8be796d3a5767cddf
F src/hwtime.h d4d1b98c1afdc56986a5c0eba882786ef017e2180a320da25b4231b8e7e463e8
F src/hwtime.h 21c2cf1f736e7b97502c3674d0c386db3f06870d6f10d0cf8174e2a4b8cb726e
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
F src/insert.c dfd311b0ac2d4f6359e62013db67799757f4d2cc56cca5c10f4888acfbbfa3fd
F src/json.c fb031340edee159c07ad37dbe668ffe945ed86f525b0eb3822e4a67cbc498a72
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
F src/loadext.c 4747f72084dc80520a2d23e5bb0bc0760672208a81a2b115ce0b657acd65edf4
F src/main.c eaf5215917b7e8b34ddea013524287f6d5021ba0fc70202e7602e225ef0d16bf
F src/main.c c99e86fd7dc6c8c2c0f67395562e3300bca2780b845db9bdc2bdac6acf13c6aa
F src/malloc.c 410e570b30c26cc36e3372577df50f7a96ee3eed5b2b161c6b6b48773c650c5e
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c 3bb59158c38e05f6270e761a9f435bf19827a264c13d1631c58b84bdc96d73b2
@@ -715,33 +719,33 @@ F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63
F src/os_common.h 6c0eb8dd40ef3e12fe585a13e709710267a258e2c8dd1c40b1948a1d14582e06
F src/os_kv.c fb7ba8d6204197357f1eb7e1c7450d09c10043bf7e99aba602f4aa46b8fb11a3
F src/os_setup.h 8efc64eda6a6c2f221387eefc2e7e45fd5a3d5c8337a7a83519ba4fbd2957ae2
F src/os_unix.c 7945ede1e85b2d1b910e1b4af9ba342e964b1e30e79f4176480a60736445cb36
F src/os_unix.c bf55d195730393a9faf31dcec65257710c61f61a728342151af06a8267ffb35d
F src/os_win.c a89b501fc195085c7d6c9eec7f5bd782625e94bb2a96b000f4d009703df1083f
F src/os_win.h 4c247cdb6d407c75186c94a1e84d5a22cbae4adcec93fcae8d2bc1f956fd1f19
F src/pager.c a81461de271ac4886ad75b7ca2cca8157a48635820c4646cd2714acdc2c17e5f
F src/pager.c 52bae5ea11f25cbdc425be7b0a8c3310b3ec2a1381e73b5d5b6af20c26094108
F src/pager.h 6137149346e6c8a3ddc1eeb40aee46381e9bc8b0fcc6dda8a1efde993c2275b8
F src/parse.y 619c3e92a54686c5e47923688c4b9bf7ec534a4690db5677acc28b299c403250
F src/parse.y 424d195ea70f4656a3f6440e0b79ca8f712ae4da9431033a19ec8c9816469287
F src/pcache.c 588cc3c5ccaaadde689ed35ce5c5c891a1f7b1f4d1f56f6cf0143b74d8ee6484
F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5
F src/pcache1.c 131ca0daf4e66b4608d2945ae76d6ed90de3f60539afbd5ef9ec65667a5f2fcd
F src/pragma.c ecec75795c1821520266e4f93fa8840cce48979af532db06f085e36a7813860f
F src/prepare.c 2af0b5c1ec787c8eebd21baa9d79caf4a4dc3a18e76ce2edbf2027d706bca37a
F src/prepare.c f6a6e28a281bd1d1da12f47d370a81af46159b40f73bf7fa0b276b664f9c8b7d
F src/printf.c b1b29b5e58e1530d5daeee5963d3c318d8ab2d7e38437580e28755753e0c1ded
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 bc2185e0e988f93643dbaac3c8f190b3687475cd7ac802c035d20347b8e27fbb
F src/sqlite.h.in b5b2450724c9aa2d7ae2861c881385bc21ba940c79122c893f5ae9bb36ad10dd
F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479
F src/sqlite3ext.h 5d5330f5f8461f5ce74960436ddcfa53ecd09c2b8b23901e22ae38aec3243998
F src/sqliteInt.h 775c133757bd5c7d0fce10407959ee45993678ae95088e4ebdb22ef24e9d78ff
F src/sqliteLimit.h fe70bd8983e5d317a264f2ea97473b359faf3ebb0827877a76813f5cf0cdc364
F src/sqliteInt.h a89c3a9296928dffcb4c287df176a739f9cf620c7c9d33aec59e8efb9b39cbbd
F src/sqliteLimit.h 0a5516b4ec192a205c541e05f67009028a9451dc6678aae4cf8e68596903c246
F src/status.c 7565d63a79aa2f326339a24a0461a60096d0bd2bce711fefb50b5c89335f3592
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
F src/tclsqlite.c 3c604c49e6cf4211960a9ddb9505280fd22cde32175f40884c641c0f5a286036
F src/tclsqlite.c f5aca78b32b2ba823a6dc7dbb9b2272c93e0f71df2f93104d5a61a6348f3b115
F src/tclsqlite.h 614b3780a62522bc9f8f2b9fb22689e8009958e7aa77e572d0f3149050af348a
F src/test1.c 5d061afe479c7364842e0170be7220dea13389575fa6030d30b3e20bec4e1f75
F src/test1.c 0e71fbcb484a271564e98e0158192c28c24f5521594218c3ba48bcb4cf634f91
F src/test2.c 62f0830958f9075692c29c6de51b495ae8969e1bef85f239ffcd9ba5fb44a5ff
F src/test3.c 432646f581d8af1bb495e58fc98234380250954f5d5535e507fc785eccc3987a
F src/test4.c 0ac87fc13cdb334ab3a71823f99b6c32a6bebe5d603cd6a71d84c823d43a25a0
@@ -771,7 +775,7 @@ F src/test_md5.c 811a45330c9391933360f998156a8907ee29909c828ab83ac05d329942cbea8
F src/test_multiplex.c 82f0aa8eee629b6949782cfab8782ed35a9b56dc80d12877af52147f304d22b8
F src/test_multiplex.h f0ff5b6f4462bfd46dac165d6375b9530d08089b7bcbe75e88e0926110db5363
F src/test_mutex.c dacae6790956c0d4e705aaed2090227792e291b0496cccd688e9994c1e21f740
F src/test_onefile.c f31e52e891c5fef6709b9fcef54ce660648a34172423a9cbdf4cbce3ba0049f4
F src/test_onefile.c f52ae709489837863521e6e0e1a8965b6716d45f784445b98641b6ce0fc2137b
F src/test_osinst.c 269039d9c0820a02ee928014c30860d57ee757ecda54df42e463d0ca1377b835
F src/test_pcache.c 496da3f7e2ca66aefbc36bbf22138b1eff43ba0dff175c228b760fa020a37bd0
F src/test_quota.c 180e87437250bed7e17e4e61c106730939e39fec9be73d28961f27f579a92078
@@ -785,7 +789,7 @@ F src/test_tclsh.c c01706ac60bd3176754d3ccd37da74c6ad97c2e14489f8ed71b497c1c0ac0
F src/test_tclvar.c ae873248a0188459b1c16ca7cc431265dacce524399e8b46725c2b3b7e048424
F src/test_thread.c 3edb4a5b5aeb1a6e9a275dccc848ac95acab7f496b3e9230f6d2d04953a2b862
F src/test_vdbecov.c 5c426d9cd2b351f5f9ceb30cabf8c64a63bfcad644c507e0bd9ce2f6ae1a3bf3
F src/test_vfs.c b4135c1308516adf0dfd494e6d6c33114e03732be899eace0502919b674586b5
F src/test_vfs.c 7b06f2f82584a9e723e62cb6894d60d21dc4cd9bcd02d6de976e5434e5d60a30
F src/test_window.c 6d80e11fba89a1796525e6f0048ff0c7789aa2c6b0b11c80827dc1437bd8ea72
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
@@ -831,12 +835,14 @@ F test/alter2.test 7e3d26ab409df52df887b366a63902c3429b935c41cb962fd58ffc25784f2
F test/alter3.test dcdd5f850f30656a45a0f05e41abfb52b74bbf6ccba165d0f7adf6b0116e4fd6
F test/alter4.test 37cafe164067a6590a0ee4cec780bddbbaa33dc50b11542dcfbe0e65626494fd
F test/alterauth.test 63442ba61ceb0c1eeb63aac1f4f5cebfa509d352276059d27106ae256bafc959
F test/alterauth2.test 48967abae0494d9a300d1c92473d99fcb66edfcc23579c89322f033f49410adc
F test/altercol.test b43fb5725332f4cf8cff0280605202c1672e808281accea60a066d2ccc5129e5
F test/alterauth2.test 4b74fa8f184f4736497317feb587b65759eb87d87acfe3a8ef433d4d18bb002b
F test/altercol.test 3661c432aacb42bc2198dd4611bbb9c3b09fc73251b59edda046109103b8ac00
F test/altercons.test ea18def4a0f26b9066da56095c9c480df705df4d02e4ae151708fae76f7e3884
F test/altercons2.test 3c1f58312817df43aeada3b1827fdc3ce3fc50c6f49a95ef62cf4cbbae8583a0
F test/altercorrupt.test 2e1d705342cf9d7de884518ddbb053fd52d7e60d2b8869b7b63b2fda68435c12
F test/alterdropcol.test a653a3945f964d26845ec0cd0a8e74189f46de3119a984c5bc45457da392612e
F test/alterdropcol2.test 527fce683b200d620f560f666c44ae33e22728e990a10a48a543280dfd4b4d41
F test/alterfault.test 289067108947bedca27534edd4ff251bcd298cf84402d7b24eaa3749305418c6
F test/alterfault.test 9baff95be91ad77e955701454fa9e4bdd8c94f7fe6e814939962d3d5efb83220
F test/alterlegacy.test f38c6d06cda39e1f7b955bbce57f2e3ef5b7cb566d3d1234502093e228c15811
F test/altermalloc.test 167a47de41b5c638f5f5c6efb59784002b196fff70f98d9b4ed3cd74a3fb80c9
F test/altermalloc2.test 17fb3724c4b004c469c27dc4ef181608aa644555fbd3f3236767584f73747c81
@@ -883,7 +889,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
@@ -1420,7 +1426,7 @@ F test/misc1.test e3e36262aff1bd9b8b9bf1eeb3af04adb3fc1e23f0a92dbff708bba9e939ac
F test/misc2.test a1a3573cc02662becd967766021d6f16c54684d56df5f227481c7ef0d9df0bd0
F test/misc3.test 651b88bca19b8ff6a7b6af73dae00c3fd5b3ea5bee0c0d1d91abd4c4b4748718
F test/misc4.test 10cd6addb2fa9093df4751a1b92b50440175dd5468a6ec84d0386e78f087db0e
F test/misc5.test 02fcaf4d42405be02ec975e946270a50b0282dac98c78303ade0d1392839d2b8
F test/misc5.test 0a5d7604e197f10ee471280bfcaaf8229f9d8e2eebfef2c8853222cbc1ea9cd5
F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91
F test/misc7.test d595599972ec0b436985f0f02f243b68500ffc977b9b3194ec66c0866cfddcab
F test/misc8.test 08d2380bc435486b12161521f225043ac2be26f02471c2c1ea4cac0b1548edbd
@@ -1433,6 +1439,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 43098858465029ebe9229009fbd59528adf89f265fb82af87fc5c58f7901b2bc
F test/multiplex.test d74c034e52805f6de8cc5432cef8c9eb774bb64ec29b83a22effc8ca4dac1f08
F test/multiplex2.test 580ca5817c7edbe4cc68fa150609c9473393003a
F test/multiplex3.test fac575e0b1b852025575a6a8357701d80933e98b5d2fe6d35ddaa68f92f6a1f7
@@ -1502,6 +1509,10 @@ F test/printf2.test 3f55c1871a5a65507416076f6eb97e738d5210aeda7595a74ee895f2224c
F test/progress.test ebab27f670bd0d4eb9d20d49cef96e68141d92fb
F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc
F test/pushdown.test 46a626ef1c0ca79b85296ff2e078b9da20a50e9b804b38f441590c3987580ddd
F test/qrf01.test a6ecc051613ea4dde796e4ff20fdb2c59a2138faf926d90a2a92c1e291776617
F test/qrf02.test 39b4afdc000bedccdafc0aecf17638df67a67aaa2d2942865ae6abcc48ba0e92
F test/qrf03.test 9de53aea459f5a127283db03cbb6011500757685646d21aa3c29c44c6ef23e86
F test/qrf04.test 0894692c998d2401dcc33449c02051b503ecce0c94217be54fb007c82d2d1379
F test/queryonly.test 5f653159e0f552f0552d43259890c1089391dcca
F test/quick.test 1681febc928d686362d50057c642f77a02c62e57
F test/quickcheck.test a4b7e878cd97e46108291c409b0bf8214f29e18fddd68a42bc5c1375ad1fb80a
@@ -1514,7 +1525,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 +1606,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 9438957ab1c0d4c4622b98a11648999a86b6184c8317a87d3c5cb760d2f7bca9
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 4b8983a40703b5550f691592b6c5bc64886c26755eeb7a474af849d1e39b51f2
F test/shellB.test de879b1ea7c25daf1a06b2c882b45a5d002e6580c81c57169ce47084cc6afb6b
F test/shmlock.test 9f1f729a7fe2c46c88b156af819ac9b72c0714ac6f7246638a73c5752b5fd13c
F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3
F test/show_speedtest1_rtree.tcl 32e6c5f073d7426148a6936a0408f4b5b169aba5
@@ -1678,7 +1690,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 +1702,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
@@ -1913,7 +1925,7 @@ F test/upsertfault.test f21ca47740841fdb4d61acfa7b17646d773e67724fe8c185b71c018d
F test/uri.test 1250724af9beeed2d6c3716f5b990c483200c54f408d3c0ec9543a3c7961f8fc
F test/uri2.test 9d3ba7a53ee167572d53a298ee4a5d38ec4a8fb7
F test/utf16align.test 9fde0bb5d3a821594aa68c6829ab9c5453a084384137ebb9f6153e2d678039da
F test/vacuum-into.test 5a489714feecfdabfc7b293be4111564a173dee92c0d6818dd0207f3ade65783
F test/vacuum-into.test ebd223213fde4c985818b9d6dc33fe29a7cdd4272738d82e3e14aa2e686d228b
F test/vacuum.test f3b2257a4fcd659513c866a5d9e5f70999cc58fd5d74e979290385fa350b79ee
F test/vacuum2.test 9fd45ce6ce29f5614c249e03938d3567c06a9e772d4f155949f8eafe2d8af520
F test/vacuum3.test d9d9a04ee58c485b94694fd4f68cffaba49c32234fdefe1ac1a622c5e17d4ce3
@@ -2101,8 +2113,8 @@ F tool/genfkey.README e550911fa984c8255ebed2ef97824125d83806eb5232582700de949edf
F tool/genfkey.test b6afd7b825d797a1e1274f519ab5695373552ecad5cd373530c63533638a5a4f
F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
F tool/index_usage.c f62a0c701b2c7ff2f3e21d206f093c123f222dbf07136a10ffd1ca15a5c706c5
F tool/lemon.c 8f6c122e5727cb0e5f302b8efc91489b1947a8d98206d7a1b1cfc0ed685b6e7c
F tool/lempar.c bdffd3b233a4e4e78056c9c01fadd2bb3fe902435abde3bce3d769fdf0d5cca2
F tool/lemon.c 4150f2020d453cfa46b6fa45542e59b923ad7eab063fb4ca20777995622cab0b
F tool/lempar.c b57e1780bf8098dd4a9a5bba537f994276ea825a420f6165153e5894dc2dfb07
F tool/libvers.c caafc3b689638a1d88d44bc5f526c2278760d9b9
F tool/loadfts.c 63412f9790e5e8538fbde0b4f6db154aaaf80f7a10a01e3c94d14b773a8dd5a6
F tool/logest.c c34e5944318415de513d29a6098df247a9618c96d83c38d4abd88641fe46e669
@@ -2111,6 +2123,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 +2131,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 a9a4d421a35b7117bb9d50e8e533c59a67f8a03957e2dd1282b92d27fb87e74c
F tool/mksourceid.c 36aa8020014aed0836fd13c51d6dc9219b0df1761d6b5f58ff5b616211b079b9
F tool/mksqlite3c-noext.tcl 351c55256213154cabb051a3c870ef9f4487de905015141ae50dc7578a901b84
F tool/mksqlite3c.tcl 7a268139158e5deef27a370bc2f8db6ccf100c1ad7ac5e5b23743c0fd354f609
@@ -2149,7 +2162,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 +2179,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 ff2065b533769cbf5278d13d09ffa93a7a0f841b1b59b6be0e5f27e3d1c341d0
R 628a0162ba8f0d8ab2de4d5bcae00f5f
U drh
Z 62a7b6290233b3d348ba184dec1ce219
Z 990cca7d1ccfde0bfbdaf6f519eba45c
# 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 remove-sync-dataonly
tag remove-sync-dataonly
+1 -1
View File
@@ -1 +1 @@
35ec117e5a20f30030a37d2fbd059654bb13d0ec64fcf8719d71d9a4318dc970
caefe4c746599b22aec446f3aeb296e53edaf8c54f8ae803381e8c21d5e29ab4
+720 -4
View File
@@ -491,7 +491,7 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
/* Look up the table being altered. */
assert( pParse->pNewTable==0 );
assert( sqlite3BtreeHoldsAllMutexes(db) );
if( db->mallocFailed ) goto exit_begin_add_column;
if( NEVER(db->mallocFailed) ) goto exit_begin_add_column;
pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]);
if( !pTab ) goto exit_begin_add_column;
@@ -563,7 +563,7 @@ exit_begin_add_column:
** Or, if pTab is not a view or virtual table, zero is returned.
*/
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE)
static int isRealTable(Parse *pParse, Table *pTab, int bDrop){
static int isRealTable(Parse *pParse, Table *pTab, int iOp){
const char *zType = 0;
#ifndef SQLITE_OMIT_VIEW
if( IsView(pTab) ){
@@ -576,9 +576,12 @@ static int isRealTable(Parse *pParse, Table *pTab, int bDrop){
}
#endif
if( zType ){
const char *azMsg[] = {
"rename columns of", "drop column from", "edit constraints of"
};
assert( iOp>=0 && iOp<ArraySize(azMsg) );
sqlite3ErrorMsg(pParse, "cannot %s %s \"%s\"",
(bDrop ? "drop column from" : "rename columns of"),
zType, pTab->zName
azMsg[iOp], zType, pTab->zName
);
return 1;
}
@@ -1049,6 +1052,25 @@ static RenameToken *renameColumnTokenNext(RenameCtx *pCtx){
return pBest;
}
/*
** Set the error message of the context passed as the first argument to
** the result of formatting zFmt using printf() style formatting.
*/
static void errorMPrintf(sqlite3_context *pCtx, const char *zFmt, ...){
sqlite3 *db = sqlite3_context_db_handle(pCtx);
char *zErr = 0;
va_list ap;
va_start(ap, zFmt);
zErr = sqlite3VMPrintf(db, zFmt, ap);
va_end(ap);
if( zErr ){
sqlite3_result_error(pCtx, zErr, -1);
sqlite3DbFree(db, zErr);
}else{
sqlite3_result_error_nomem(pCtx);
}
}
/*
** An error occurred while parsing or otherwise processing a database
** object (either pParse->pNewTable, pNewIndex or pNewTrigger) as part of an
@@ -2313,6 +2335,696 @@ exit_drop_column:
sqlite3SrcListDelete(db, pSrc);
}
/*
** Return the number of bytes of leading whitespace/comments in string z[].
*/
static int getWhitespace(const u8 *z){
int nRet = 0;
while( 1 ){
int t = 0;
int n = sqlite3GetToken(&z[nRet], &t);
if( t!=TK_SPACE && t!=TK_COMMENT ) break;
nRet += n;
}
return nRet;
}
/*
** Return the number of bytes until the end of the next non-whitespace and
** non-comment token. For the purpose of this function, a "(" token includes
** all of the bytes through and including the matching ")", or until the
** first illegal token, whichever comes first.
**
** Write the token type into *piToken.
**
** The value returned is the number of bytes in the token itself plus
** the number of bytes of leading whitespace and comments skipped plus
** all bytes through the next matching ")" if the token is TK_LP.
**
** Example: (Note: '.' used in place of '*' in the example z[] text)
**
** ,--------- *piToken := TK_RP
** v
** z[] = " /.comment./ --comment\n (two three four) five"
** | |
** |<-------------------------------------->|
** |
** `--- return value
*/
static int getConstraintToken(const u8 *z, int *piToken){
int iOff = 0;
int t = 0;
do {
iOff += sqlite3GetToken(&z[iOff], &t);
}while( t==TK_SPACE || t==TK_COMMENT );
*piToken = t;
if( t==TK_LP ){
int nNest = 1;
while( nNest>0 ){
iOff += sqlite3GetToken(&z[iOff], &t);
if( t==TK_LP ){
nNest++;
}else if( t==TK_RP ){
t = TK_LP;
nNest--;
}else if( t==TK_ILLEGAL ){
break;
}
}
}
*piToken = t;
return iOff;
}
/*
** Argument z points into the body of a constraint - specifically the
** second token of the constraint definition. For a named constraint,
** z points to the first token past the CONSTRAINT keyword. For an
** unnamed NOT NULL constraint, z points to the first byte past the NOT
** keyword.
**
** Return the number of bytes until the end of the constraint.
*/
static int getConstraint(const u8 *z){
int iOff = 0;
int t = 0;
/* Now, the current constraint proceeds until the next occurence of one
** of the following tokens:
**
** CONSTRAINT, PRIMARY, NOT, UNIQUE, CHECK, DEFAULT,
** COLLATE, REFERENCES, FOREIGN, GENERATED, AS, RP, or COMMA
**
** Also exit the loop if ILLEGAL turns up.
*/
while( 1 ){
int n = getConstraintToken(&z[iOff], &t);
if( t==TK_CONSTRAINT || t==TK_PRIMARY || t==TK_NOT || t==TK_UNIQUE
|| t==TK_CHECK || t==TK_DEFAULT || t==TK_COLLATE || t==TK_REFERENCES
|| t==TK_FOREIGN || t==TK_RP || t==TK_COMMA || t==TK_ILLEGAL
|| t==TK_AS || t==TK_GENERATED
){
break;
}
iOff += n;
}
return iOff;
}
/*
** Compare two constraint names.
**
** Summary: *pRes := zQuote != zCmp
**
** Details:
** Compare the (possibly quoted) constraint name zQuote[0..nQuote-1]
** against zCmp[]. Write zero into *pRes if they are the same and
** non-zero if they differ. Normally return SQLITE_OK, except if there
** is an OOM, set the OOM error condition on ctx and return SQLITE_NOMEM.
*/
static int quotedCompare(
sqlite3_context *ctx, /* Function context on which to report errors */
const u8 *zQuote, /* Possibly quoted text. Not zero-terminated. */
int nQuote, /* Length of zQuote in bytes */
const u8 *zCmp, /* Zero-terminated, unquoted name to compare against */
int *pRes /* OUT: Set to 0 if equal, non-zero if unequal */
){
char *zCopy = 0; /* De-quoted, zero-terminated copy of zQuote[] */
zCopy = sqlite3MallocZero(nQuote+1);
if( zCopy==0 ){
sqlite3_result_error_nomem(ctx);
return SQLITE_NOMEM_BKPT;
}
memcpy(zCopy, zQuote, nQuote);
sqlite3Dequote(zCopy);
*pRes = sqlite3_stricmp((const char*)zCopy, (const char*)zCmp);
sqlite3_free(zCopy);
return SQLITE_OK;
}
/*
** zSql[] is a CREATE TABLE statement, supposedly. Find the offset
** into zSql[] of the first character past the first "(" and write
** that offset into *piOff and return SQLITE_OK. Or, if not found,
** set the SQLITE_CORRUPT error code and return SQLITE_ERROR.
*/
static int skipCreateTable(sqlite3_context *ctx, const u8 *zSql, int *piOff){
int iOff = 0;
if( zSql==0 ) return SQLITE_ERROR;
/* Jump past the "CREATE TABLE" bit. */
while( 1 ){
int t = 0;
iOff += sqlite3GetToken(&zSql[iOff], &t);
if( t==TK_LP ) break;
if( t==TK_ILLEGAL ){
sqlite3_result_error_code(ctx, SQLITE_CORRUPT_BKPT);
return SQLITE_ERROR;
}
}
*piOff = iOff;
return SQLITE_OK;
}
/*
** Internal SQL function sqlite3_drop_constraint(): Given an input
** CREATE TABLE statement, return a revised CREATE TABLE statement
** with a constraint removed. Two forms, depending on the datatype
** of argv[2]:
**
** sqlite_drop_constraint(SQL, INT) -- Omit NOT NULL from the INT-th column
** sqlite_drop_constraint(SQL, TEXT) -- OMIT constraint with name TEXT
**
** In the first case, the left-most column is 0.
*/
static void dropConstraintFunc(
sqlite3_context *ctx,
int NotUsed,
sqlite3_value **argv
){
const u8 *zSql = sqlite3_value_text(argv[0]);
const u8 *zCons = 0;
int iNotNull = -1;
int ii;
int iOff = 0;
int iStart = 0;
int iEnd = 0;
char *zNew = 0;
int t = 0;
sqlite3 *db;
UNUSED_PARAMETER(NotUsed);
if( zSql==0 ) return;
/* Jump past the "CREATE TABLE" bit. */
if( skipCreateTable(ctx, zSql, &iOff) ) return;
if( sqlite3_value_type(argv[1])==SQLITE_INTEGER ){
iNotNull = sqlite3_value_int(argv[1]);
}else{
zCons = sqlite3_value_text(argv[1]);
}
/* Search for the named constraint within column definitions. */
for(ii=0; iEnd==0; ii++){
/* Now parse the column or table constraint definition. Search
** for the token CONSTRAINT if this is a DROP CONSTRAINT command, or
** NOT in the right column if this is a DROP NOT NULL. */
while( 1 ){
iStart = iOff;
iOff += getConstraintToken(&zSql[iOff], &t);
if( t==TK_CONSTRAINT && (zCons || iNotNull==ii) ){
/* Check if this is the constraint we are searching for. */
int nTok = 0;
int cmp = 1;
/* Skip past any whitespace. */
iOff += getWhitespace(&zSql[iOff]);
/* Compare the next token - which may be quoted - with the name of
** the constraint being dropped. */
nTok = getConstraintToken(&zSql[iOff], &t);
if( zCons ){
if( quotedCompare(ctx, &zSql[iOff], nTok, zCons, &cmp) ) return;
}
iOff += nTok;
/* The next token is usually the first token of the constraint
** definition. This is enough to tell the type of the constraint -
** TK_NOT means it is a NOT NULL, TK_CHECK a CHECK constraint etc.
**
** There is also the chance that the next token is TK_CONSTRAINT
** (or TK_DEFAULT or TK_COLLATE), for example if a table has been
** created as follows:
**
** CREATE TABLE t1(cols, CONSTRAINT one CONSTRAINT two NOT NULL);
**
** In this case, allow the "CONSTRAINT one" bit to be dropped by
** this command if that is what is requested, or to advance to
** the next iteration of the loop with &zSql[iOff] still pointing
** to the CONSTRAINT keyword. */
nTok = getConstraintToken(&zSql[iOff], &t);
if( t==TK_CONSTRAINT || t==TK_DEFAULT || t==TK_COLLATE
|| t==TK_COMMA || t==TK_RP || t==TK_GENERATED || t==TK_AS
){
t = TK_CHECK;
}else{
iOff += nTok;
iOff += getConstraint(&zSql[iOff]);
}
if( cmp==0 || (iNotNull>=0 && t==TK_NOT) ){
if( t!=TK_NOT && t!=TK_CHECK ){
errorMPrintf(ctx, "constraint may not be dropped: %s", zCons);
return;
}
iEnd = iOff;
break;
}
}else if( t==TK_NOT && iNotNull==ii ){
iEnd = iOff + getConstraint(&zSql[iOff]);
break;
}else if( t==TK_RP || t==TK_ILLEGAL ){
iEnd = -1;
break;
}else if( t==TK_COMMA ){
break;
}
}
}
/* If the constraint has not been found it is an error. */
if( iEnd<=0 ){
if( zCons ){
errorMPrintf(ctx, "no such constraint: %s", zCons);
}else{
/* SQLite follows postgres in that a DROP NOT NULL on a column that is
** not NOT NULL is not an error. So just return the original SQL here. */
sqlite3_result_text(ctx, (const char*)zSql, -1, SQLITE_TRANSIENT);
}
}else{
/* Figure out if an extra space should be inserted after the constraint
** is removed. And if an additional comma preceding the constraint
** should be removed. */
const char *zSpace = " ";
iEnd += getWhitespace(&zSql[iEnd]);
sqlite3GetToken(&zSql[iEnd], &t);
if( t==TK_RP || t==TK_COMMA ){
zSpace = "";
if( zSql[iStart-1]==',' ) iStart--;
}
db = sqlite3_context_db_handle(ctx);
zNew = sqlite3MPrintf(db, "%.*s%s%s", iStart, zSql, zSpace, &zSql[iEnd]);
sqlite3_result_text(ctx, zNew, -1, SQLITE_DYNAMIC);
}
}
/*
** Internal SQL function:
**
** sqlite_add_constraint(SQL, CONSTRAINT-TEXT, ICOL)
**
** SQL is a CREATE TABLE statement. Return a modified version of
** SQL that adds CONSTRAINT-TEXT at the end of the ICOL-th column
** definition. (The left-most column defintion is 0.)
*/
static void addConstraintFunc(
sqlite3_context *ctx,
int NotUsed,
sqlite3_value **argv
){
const u8 *zSql = sqlite3_value_text(argv[0]);
const char *zCons = (const char*)sqlite3_value_text(argv[1]);
int iCol = sqlite3_value_int(argv[2]);
int iOff = 0;
int ii;
char *zNew = 0;
int t = 0;
sqlite3 *db;
UNUSED_PARAMETER(NotUsed);
if( skipCreateTable(ctx, zSql, &iOff) ) return;
for(ii=0; ii<=iCol || (iCol<0 && t!=TK_RP); ii++){
iOff += getConstraintToken(&zSql[iOff], &t);
while( 1 ){
int nTok = getConstraintToken(&zSql[iOff], &t);
if( t==TK_COMMA || t==TK_RP ) break;
if( t==TK_ILLEGAL ){
sqlite3_result_error_code(ctx, SQLITE_CORRUPT_BKPT);
return;
}
iOff += nTok;
}
}
iOff += getWhitespace(&zSql[iOff]);
db = sqlite3_context_db_handle(ctx);
if( iCol<0 ){
zNew = sqlite3MPrintf(db, "%.*s, %s%s", iOff, zSql, zCons, &zSql[iOff]);
}else{
zNew = sqlite3MPrintf(db, "%.*s %s%s", iOff, zSql, zCons, &zSql[iOff]);
}
sqlite3_result_text(ctx, zNew, -1, SQLITE_DYNAMIC);
}
/*
** Find a column named pCol in table pTab. If successful, set output
** parameter *piCol to the index of the column in the table and return
** SQLITE_OK. Otherwise, set *piCol to -1 and return an SQLite error
** code.
*/
static int alterFindCol(Parse *pParse, Table *pTab, Token *pCol, int *piCol){
sqlite3 *db = pParse->db;
char *zName = sqlite3NameFromToken(db, pCol);
int rc = SQLITE_NOMEM;
int iCol = -1;
if( zName ){
iCol = sqlite3ColumnIndex(pTab, zName);
if( iCol<0 ){
sqlite3ErrorMsg(pParse, "no such column: %s", zName);
rc = SQLITE_ERROR;
}else{
rc = SQLITE_OK;
}
}
#ifndef SQLITE_OMIT_AUTHORIZATION
if( rc==SQLITE_OK ){
const char *zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName;
const char *zCol = pTab->aCol[iCol].zCnName;
if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, zCol) ){
pTab = 0;
}
}
#endif
sqlite3DbFree(db, zName);
*piCol = iCol;
return rc;
}
/*
** Find the table named by the first entry in source list pSrc. If successful,
** return a pointer to the Table structure and set output variable (*pzDb)
** to point to the name of the database containin the table (i.e. "main",
** "temp" or the name of an attached database).
**
** If the table cannot be located, return NULL. The value of the two output
** parameters is undefined in this case.
*/
static Table *alterFindTable(
Parse *pParse, /* Parsing context */
SrcList *pSrc, /* Name of the table to look for */
int *piDb, /* OUT: write the iDb here */
const char **pzDb, /* OUT: write name of schema here */
int bAuth /* Do ALTER TABLE authorization checks if true */
){
sqlite3 *db = pParse->db;
Table *pTab = 0;
assert( sqlite3BtreeHoldsAllMutexes(db) );
pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]);
if( pTab ){
int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
*pzDb = db->aDb[iDb].zDbSName;
*piDb = iDb;
if( SQLITE_OK!=isRealTable(pParse, pTab, 2)
|| SQLITE_OK!=isAlterableTable(pParse, pTab)
){
pTab = 0;
}
}
#ifndef SQLITE_OMIT_AUTHORIZATION
if( pTab && bAuth ){
if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, *pzDb, pTab->zName, 0) ){
pTab = 0;
}
}
#endif
sqlite3SrcListDelete(db, pSrc);
return pTab;
}
/*
** Generate bytecode for one of:
**
** (1) ALTER TABLE pSrc DROP CONSTRAINT pCons
** (2) ALTER TABLE pSrc ALTER pCol DROP NOT NULL
**
** One of pCons and pCol must be NULL and the other non-null.
*/
void sqlite3AlterDropConstraint(
Parse *pParse, /* Parsing context */
SrcList *pSrc, /* The table being altered */
Token *pCons, /* Name of the constraint to drop */
Token *pCol /* Name of the column from which to remove the NOT NULL */
){
sqlite3 *db = pParse->db;
Table *pTab = 0;
int iDb = 0;
const char *zDb = 0;
char *zArg = 0;
assert( (pCol==0)!=(pCons==0) );
assert( pSrc->nSrc==1 );
pTab = alterFindTable(pParse, pSrc, &iDb, &zDb, pCons!=0);
if( !pTab ) return;
if( pCons ){
zArg = sqlite3MPrintf(db, "%.*Q", pCons->n, pCons->z);
}else{
int iCol;
if( alterFindCol(pParse, pTab, pCol, &iCol) ) return;
zArg = sqlite3MPrintf(db, "%d", iCol);
}
/* Edit the SQL for the named table. */
sqlite3NestedParse(pParse,
"UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET "
"sql = sqlite_drop_constraint(sql, %s) "
"WHERE type='table' AND tbl_name=%Q COLLATE nocase"
, zDb, zArg, pTab->zName
);
sqlite3DbFree(db, zArg);
/* Finally, reload the database schema. */
renameReloadSchema(pParse, iDb, INITFLAG_AlterDropCons);
}
/*
** The implementation of SQL function sqlite_fail(MSG). This takes a single
** argument, and returns it as an error message with the error code set to
** SQLITE_CONSTRAINT.
*/
static void failConstraintFunc(
sqlite3_context *ctx,
int NotUsed,
sqlite3_value **argv
){
const char *zText = (const char*)sqlite3_value_text(argv[0]);
int err = sqlite3_value_int(argv[1]);
(void)NotUsed;
sqlite3_result_error(ctx, zText, -1);
sqlite3_result_error_code(ctx, err);
}
/*
** Buffer pCons, which is nCons bytes in size, contains the text of a
** NOT NULL or CHECK constraint that will be inserted into a CREATE TABLE
** statement. If successful, this function returns the size of the buffer in
** bytes not including any trailing whitespace or "--" style comments. Or,
** if an OOM occurs, it returns 0 and sets db->mallocFailed to true.
**
** C-style comments at the end are preserved. "--" style comments are
** removed because the comment terminator might be \000, and we are about
** to insert the pCons[] text into the middle of a larger string, and that
** will have the effect of removing the comment terminator and messing up
** the syntax.
*/
static int alterRtrimConstraint(
sqlite3 *db, /* used to record OOM error */
const char *pCons, /* Buffer containing constraint */
int nCons /* Size of pCons in bytes */
){
u8 *zTmp = (u8*)sqlite3MPrintf(db, "%.*s", nCons, pCons);
int iOff = 0;
int iEnd = 0;
if( zTmp==0 ) return 0;
while( 1 ){
int t = 0;
int nToken = sqlite3GetToken(&zTmp[iOff], &t);
if( t==TK_ILLEGAL ) break;
if( t!=TK_SPACE && (t!=TK_COMMENT || zTmp[iOff]!='-') ){
iEnd = iOff+nToken;
}
iOff += nToken;
}
sqlite3DbFree(db, zTmp);
return iEnd;
}
/*
** Prepare a statement of the form:
**
** ALTER TABLE pSrc ALTER pCol SET NOT NULL
*/
void sqlite3AlterSetNotNull(
Parse *pParse, /* Parsing context */
SrcList *pSrc, /* Name of the table being altered */
Token *pCol, /* Name of the column to add a NOT NULL constraint to */
Token *pFirst /* The NOT token of the NOT NULL constraint text */
){
Table *pTab = 0;
int iCol = 0;
int iDb = 0;
const char *zDb = 0;
const char *pCons = 0;
int nCons = 0;
/* Look up the table being altered. */
assert( pSrc->nSrc==1 );
pTab = alterFindTable(pParse, pSrc, &iDb, &zDb, 0);
if( !pTab ) return;
/* Find the column being altered. */
if( alterFindCol(pParse, pTab, pCol, &iCol) ){
return;
}
/* Find the length in bytes of the constraint definition */
pCons = pFirst->z;
nCons = alterRtrimConstraint(pParse->db, pCons, pParse->sLastToken.z - pCons);
/* Search for a constraint violation. Throw an exception if one is found. */
sqlite3NestedParse(pParse,
"SELECT sqlite_fail('constraint failed', %d) "
"FROM %Q.%Q AS x WHERE x.%.*s IS NULL",
SQLITE_CONSTRAINT, zDb, pTab->zName, (int)pCol->n, pCol->z
);
/* Edit the SQL for the named table. */
sqlite3NestedParse(pParse,
"UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET "
"sql = sqlite_add_constraint(sqlite_drop_constraint(sql, %d), %.*Q, %d) "
"WHERE type='table' AND tbl_name=%Q COLLATE nocase"
, zDb, iCol, nCons, pCons, iCol, pTab->zName
);
/* Finally, reload the database schema. */
renameReloadSchema(pParse, iDb, INITFLAG_AlterDropCons);
}
/*
** Implementation of internal SQL function:
**
** sqlite_find_constraint(SQL, CONSTRAINT-NAME)
**
** This function returns true if the SQL passed as the first argument is a
** CREATE TABLE that contains a constraint with the name CONSTRAINT-NAME,
** or false otherwise.
*/
static void findConstraintFunc(
sqlite3_context *ctx,
int NotUsed,
sqlite3_value **argv
){
const u8 *zSql = 0;
const u8 *zCons = 0;
int iOff = 0;
int t = 0;
(void)NotUsed;
zSql = sqlite3_value_text(argv[0]);
zCons = sqlite3_value_text(argv[1]);
if( zSql==0 || zCons==0 ) return;
while( t!=TK_LP && t!=TK_ILLEGAL ){
iOff += sqlite3GetToken(&zSql[iOff], &t);
}
while( 1 ){
iOff += getConstraintToken(&zSql[iOff], &t);
if( t==TK_CONSTRAINT ){
int nTok = 0;
int cmp = 0;
iOff += getWhitespace(&zSql[iOff]);
nTok = getConstraintToken(&zSql[iOff], &t);
if( quotedCompare(ctx, &zSql[iOff], nTok, zCons, &cmp) ) return;
if( cmp==0 ){
sqlite3_result_int(ctx, 1);
return;
}
}else if( t==TK_ILLEGAL ){
break;
}
}
sqlite3_result_int(ctx, 0);
}
/*
** Generate bytecode to implement:
**
** ALTER TABLE pSrc ADD [CONSTRAINT pName] CHECK(pExpr)
**
** Any "ON CONFLICT" text that occurs after the "CHECK(...)", up
** until pParse->sLastToken, is included as part of the new constraint.
*/
void sqlite3AlterAddConstraint(
Parse *pParse, /* Parse context */
SrcList *pSrc, /* Table to add constraint to */
Token *pFirst, /* First token of new constraint */
Token *pName, /* Name of new constraint. NULL if name omitted. */
const char *pExpr, /* Text of CHECK expression */
int nExpr /* Size of pExpr in bytes */
){
Table *pTab = 0; /* Table identified by pSrc */
int iDb = 0; /* Which schema does pTab live in */
const char *zDb = 0; /* Name of the schema in which pTab lives */
const char *pCons = 0; /* Text of the constraint */
int nCons; /* Bytes of text to use from pCons[] */
/* Look up the table being altered. */
assert( pSrc->nSrc==1 );
pTab = alterFindTable(pParse, pSrc, &iDb, &zDb, 1);
if( !pTab ) return;
/* If this new constraint has a name, check that it is not a duplicate of
** an existing constraint. It is an error if it is. */
if( pName ){
char *zName = sqlite3NameFromToken(pParse->db, pName);
sqlite3NestedParse(pParse,
"SELECT sqlite_fail('constraint %q already exists', %d) "
"FROM \"%w\"." LEGACY_SCHEMA_TABLE " "
"WHERE type='table' AND tbl_name=%Q COLLATE nocase "
"AND sqlite_find_constraint(sql, %Q)",
zName, SQLITE_ERROR, zDb, pTab->zName, zName
);
sqlite3DbFree(pParse->db, zName);
}
/* Search for a constraint violation. Throw an exception if one is found. */
sqlite3NestedParse(pParse,
"SELECT sqlite_fail('constraint failed', %d) "
"FROM %Q.%Q WHERE (%.*s) IS NOT TRUE",
SQLITE_CONSTRAINT, zDb, pTab->zName, nExpr, pExpr
);
/* Edit the SQL for the named table. */
pCons = pFirst->z;
nCons = alterRtrimConstraint(pParse->db, pCons, pParse->sLastToken.z - pCons);
sqlite3NestedParse(pParse,
"UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET "
"sql = sqlite_add_constraint(sql, %.*Q, -1) "
"WHERE type='table' AND tbl_name=%Q COLLATE nocase"
, zDb, nCons, pCons, pTab->zName
);
/* Finally, reload the database schema. */
renameReloadSchema(pParse, iDb, INITFLAG_AlterDropCons);
}
/*
** Register built-in functions used to help implement ALTER TABLE
*/
@@ -2323,6 +3035,10 @@ void sqlite3AlterFunctions(void){
INTERNAL_FUNCTION(sqlite_rename_test, 7, renameTableTest),
INTERNAL_FUNCTION(sqlite_drop_column, 3, dropColumnFunc),
INTERNAL_FUNCTION(sqlite_rename_quotefix,2, renameQuotefixFunc),
INTERNAL_FUNCTION(sqlite_drop_constraint,2, dropConstraintFunc),
INTERNAL_FUNCTION(sqlite_fail, 2, failConstraintFunc),
INTERNAL_FUNCTION(sqlite_add_constraint, 3, addConstraintFunc),
INTERNAL_FUNCTION(sqlite_find_constraint,2, findConstraintFunc),
};
sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs));
}
-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
View File
@@ -18,6 +18,7 @@
#if defined(_MSC_VER) && defined(_WIN32)
#include "windows.h"
#include <profileapi.h>
__inline sqlite3_uint64 sqlite3Hwtime(void){
+3 -1
View File
@@ -2930,6 +2930,7 @@ static const int aHardLimit[] = {
SQLITE_MAX_VARIABLE_NUMBER, /* IMP: R-38091-32352 */
SQLITE_MAX_TRIGGER_DEPTH,
SQLITE_MAX_WORKER_THREADS,
SQLITE_MAX_PARSER_DEPTH,
};
/*
@@ -2999,6 +3000,7 @@ int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){
assert( aHardLimit[SQLITE_LIMIT_SQL_LENGTH]==SQLITE_MAX_SQL_LENGTH );
assert( aHardLimit[SQLITE_LIMIT_COLUMN]==SQLITE_MAX_COLUMN );
assert( aHardLimit[SQLITE_LIMIT_EXPR_DEPTH]==SQLITE_MAX_EXPR_DEPTH );
assert( aHardLimit[SQLITE_LIMIT_PARSER_DEPTH]==SQLITE_MAX_PARSER_DEPTH );
assert( aHardLimit[SQLITE_LIMIT_COMPOUND_SELECT]==SQLITE_MAX_COMPOUND_SELECT);
assert( aHardLimit[SQLITE_LIMIT_VDBE_OP]==SQLITE_MAX_VDBE_OP );
assert( aHardLimit[SQLITE_LIMIT_FUNCTION_ARG]==SQLITE_MAX_FUNCTION_ARG );
@@ -3008,7 +3010,7 @@ int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){
assert( aHardLimit[SQLITE_LIMIT_VARIABLE_NUMBER]==SQLITE_MAX_VARIABLE_NUMBER);
assert( aHardLimit[SQLITE_LIMIT_TRIGGER_DEPTH]==SQLITE_MAX_TRIGGER_DEPTH );
assert( aHardLimit[SQLITE_LIMIT_WORKER_THREADS]==SQLITE_MAX_WORKER_THREADS );
assert( SQLITE_LIMIT_WORKER_THREADS==(SQLITE_N_LIMIT-1) );
assert( SQLITE_LIMIT_PARSER_DEPTH==(SQLITE_N_LIMIT-1) );
if( limitId<0 || limitId>=SQLITE_N_LIMIT ){
+15 -25
View File
@@ -3756,20 +3756,18 @@ int sqlite3_fullsync_count = 0;
** enabled, however, since with SQLITE_NO_SYNC enabled, an OS crash
** or power failure will likely corrupt the database file.
**
** SQLite sets the dataOnly flag if the size of the file is unchanged.
** The idea behind dataOnly is that it should only write the file content
** to disk, not the inode. We only set dataOnly if the file size is
** unchanged since the file size is part of the inode. However,
** Ted Ts'o tells us that fdatasync() will also write the inode if the
** file size has changed. The only real difference between fdatasync()
** and fsync(), Ted tells us, is that fdatasync() will not flush the
** inode if the mtime or owner or other inode attributes have changed.
** We only care about the file size, not the other file attributes, so
** as far as SQLite is concerned, an fdatasync() is always adequate.
** So, we always use fdatasync() if it is available, regardless of
** the value of the dataOnly flag.
** At one point we attempted to pass a flag to this function if the
** file-size had not changed to tell it to use fdatasync() instead of
** fsync() in this case. However, Ted Ts'o tells us that fdatasync() will
** also write the inode if the file size has changed. The only real
** difference between fdatasync() and fsync(), Ted tells us, is that
** fdatasync() will not flush the inode if the mtime or owner or other
** inode attributes have changed. We only care about the file size, not
** the other file attributes, so as far as SQLite is concerned, an
** fdatasync() is always adequate. So, we always use fdatasync() if it is
** available.
*/
static int full_fsync(int fd, int fullSync, int dataOnly){
static int full_fsync(int fd, int fullSync){
int rc;
/* The following "ifdef/elif/else/" block has the same structure as
@@ -3779,12 +3777,9 @@ static int full_fsync(int fd, int fullSync, int dataOnly){
#ifdef SQLITE_NO_SYNC
UNUSED_PARAMETER(fd);
UNUSED_PARAMETER(fullSync);
UNUSED_PARAMETER(dataOnly);
#elif HAVE_FULLFSYNC
UNUSED_PARAMETER(dataOnly);
#else
UNUSED_PARAMETER(fullSync);
UNUSED_PARAMETER(dataOnly);
#endif
/* Record the number of times that we do a normal fsync() and
@@ -3890,10 +3885,6 @@ static int openDirectory(const char *zFilename, int *pFd){
/*
** Make sure all writes to a particular file are committed to disk.
**
** If dataOnly==0 then both the file itself and its metadata (file
** size, access time, etc) are synced. If dataOnly!=0 then only the
** file data is synced.
**
** Under Unix, also make sure that the directory entry for the file
** has been created by fsync-ing the directory that contains the file.
** If we do not do this and we encounter a power failure, the directory
@@ -3906,7 +3897,6 @@ static int unixSync(sqlite3_file *id, int flags){
int rc;
unixFile *pFile = (unixFile*)id;
int isDataOnly = (flags&SQLITE_SYNC_DATAONLY);
int isFullsync = (flags&0x0F)==SQLITE_SYNC_FULL;
/* Check that one of SQLITE_SYNC_NORMAL or FULL was passed */
@@ -3921,7 +3911,7 @@ static int unixSync(sqlite3_file *id, int flags){
assert( pFile );
OSTRACE(("SYNC %-3d\n", pFile->h));
rc = full_fsync(pFile->h, isFullsync, isDataOnly);
rc = full_fsync(pFile->h, isFullsync);
SimulateIOError( rc=1 );
if( rc ){
storeLastErrno(pFile, errno);
@@ -3938,7 +3928,7 @@ static int unixSync(sqlite3_file *id, int flags){
HAVE_FULLFSYNC, isFullsync));
rc = osOpenDirectory(pFile->zPath, &dirfd);
if( rc==SQLITE_OK ){
full_fsync(dirfd, 0, 0);
full_fsync(dirfd, 0);
robust_close(pFile, dirfd, __LINE__);
}else{
assert( rc==SQLITE_CANTOPEN );
@@ -6854,7 +6844,7 @@ static int unixDelete(
int fd;
rc = osOpenDirectory(zPath, &fd);
if( rc==SQLITE_OK ){
if( full_fsync(fd,0,0) ){
if( full_fsync(fd, 0) ){
rc = unixLogError(SQLITE_IOERR_DIR_FSYNC, "fsync", zPath);
}
robust_close(0, fd, __LINE__);
@@ -7910,7 +7900,7 @@ static int proxyTakeConch(unixFile *pFile){
writeSize = PROXY_PATHINDEX + strlen(&writeBuffer[PROXY_PATHINDEX]);
robust_ftruncate(conchFile->h, writeSize);
rc = unixWrite((sqlite3_file *)conchFile, writeBuffer, writeSize, 0);
full_fsync(conchFile->h,0,0);
full_fsync(conchFile->h, 0);
/* If we created a new conch file (not just updated the contents of a
** valid conch file), try to match the permissions of the database
*/
+2 -4
View File
@@ -1400,7 +1400,7 @@ static int zeroJournalHdr(Pager *pPager, int doTruncate){
rc = sqlite3OsWrite(pPager->jfd, zeroHdr, sizeof(zeroHdr), 0);
}
if( rc==SQLITE_OK && !pPager->noSync ){
rc = sqlite3OsSync(pPager->jfd, SQLITE_SYNC_DATAONLY|pPager->syncFlags);
rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags);
}
/* At this point the transaction is committed but the write lock
@@ -4367,9 +4367,7 @@ static int syncJournal(Pager *pPager, int newHdr){
if( 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){
PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager)));
IOTRACE(("JSYNC %p\n", pPager))
rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags|
(pPager->syncFlags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0)
);
rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags);
if( rc!=SQLITE_OK ) return rc;
}
+50 -13
View File
@@ -21,9 +21,11 @@
*/
}
// Function used to enlarge the parser stack, if needed
%realloc parserStackRealloc
%free sqlite3_free
// Setup for the parser stack
%stack_size 50 // Initial stack size
%stack_size_limit parserStackSizeLimit // Function returning max stack size
%realloc parserStackRealloc // realloc() for the stack
%free parserStackFree // free() for the stack
// All token codes are small integers with #defines that begin with "TK_"
%token_prefix TK_
@@ -49,7 +51,7 @@
}
}
%stack_overflow {
sqlite3OomFault(pParse->db);
if( pParse->nErr==0 ) sqlite3ErrorMsg(pParse, "Recursion limit");
}
// The name of the generated procedure that implements the parser
@@ -583,8 +585,23 @@ cmd ::= select(X). {
** sqlite3_realloc() that includes a call to sqlite3FaultSim() to facilitate
** testing.
*/
static void *parserStackRealloc(void *pOld, sqlite3_uint64 newSize){
return sqlite3FaultSim(700) ? 0 : sqlite3_realloc(pOld, newSize);
static void *parserStackRealloc(
void *pOld, /* Prior allocation */
sqlite3_uint64 newSize, /* Requested new alloation size */
Parse *pParse /* Parsing context */
){
void *p = sqlite3FaultSim(700) ? 0 : sqlite3_realloc(pOld, newSize);
if( p==0 ) sqlite3OomFault(pParse->db);
return p;
}
static void parserStackFree(void *pOld, Parse *pParse){
(void)pParse;
sqlite3_free(pOld);
}
/* Return an integer that is the maximum allowed stack size */
static int parserStackSizeLimit(Parse *pParse){
return pParse->db->aLimit[SQLITE_LIMIT_PARSER_DEPTH];
}
}
@@ -1831,22 +1848,42 @@ cmd ::= ANALYZE nm(X) dbnm(Y). {sqlite3Analyze(pParse, &X, &Y);}
cmd ::= ALTER TABLE fullname(X) RENAME TO nm(Z). {
sqlite3AlterRenameTable(pParse,X,&Z);
}
cmd ::= ALTER TABLE add_column_fullname
ADD kwcolumn_opt columnname(Y) carglist. {
// The ALTER TABLE ADD COLUMN command. This is broken into two sections so
// that sqlite3AlterBeginAddColumn() is called before parsing the various
// constraints and so on (carglist) attached to the new column definition.
cmd ::= alter_add(Y) carglist. {
Y.n = (int)(pParse->sLastToken.z-Y.z) + pParse->sLastToken.n;
sqlite3AlterFinishAddColumn(pParse, &Y);
}
alter_add(A) ::= ALTER TABLE fullname(X) ADD kwcolumn_opt nm(Y) typetoken(Z). {
disableLookaside(pParse);
sqlite3AlterBeginAddColumn(pParse, X);
sqlite3AddColumn(pParse, Y, Z);
A = Y;
}
cmd ::= ALTER TABLE fullname(X) DROP kwcolumn_opt nm(Y). {
sqlite3AlterDropColumn(pParse, X, &Y);
}
add_column_fullname ::= fullname(X). {
disableLookaside(pParse);
sqlite3AlterBeginAddColumn(pParse, X);
}
cmd ::= ALTER TABLE fullname(X) RENAME kwcolumn_opt nm(Y) TO nm(Z). {
sqlite3AlterRenameColumn(pParse, X, &Y, &Z);
}
cmd ::= ALTER TABLE fullname(X) DROP CONSTRAINT nm(Y). {
sqlite3AlterDropConstraint(pParse, X, &Y, 0);
}
cmd ::= ALTER TABLE fullname(X) ALTER kwcolumn_opt nm(Y) DROP NOT NULL. {
sqlite3AlterDropConstraint(pParse, X, 0, &Y);
}
cmd ::= ALTER TABLE fullname(X) ALTER kwcolumn_opt nm(Y) SET NOT(Z) NULL onconf. {
sqlite3AlterSetNotNull(pParse, X, &Y, &Z);
}
cmd ::= ALTER TABLE fullname(X) ADD CONSTRAINT(Y) nm(Z) CHECK LP(A) expr RP(B) onconf. {
sqlite3AlterAddConstraint(pParse, X, &Y, &Z, A.z+1, (B.z-A.z-1));
}
cmd ::= ALTER TABLE fullname(X) ADD CHECK(Y) LP(A) expr RP(B) onconf. {
sqlite3AlterAddConstraint(pParse, X, &Y, 0, A.z+1, (B.z-A.z-1));
}
kwcolumn_opt ::= .
kwcolumn_opt ::= COLUMNKW.
+2 -1
View File
@@ -33,7 +33,8 @@ static void corruptSchema(
static const char *azAlterType[] = {
"rename",
"drop column",
"add column"
"add column",
"drop constraint"
};
*pData->pzErrMsg = sqlite3MPrintf(db,
"error in %s %s after %s: %s", azObj[0], azObj[1],
+2764 -3606
View File
File diff suppressed because it is too large Load Diff
+6 -3
View File
@@ -766,9 +766,7 @@ struct sqlite3_file {
**
** The flags argument to xSync may be one of [SQLITE_SYNC_NORMAL] or
** [SQLITE_SYNC_FULL]. The first choice is the normal fsync().
** The second choice is a Mac OS X style fullsync. The [SQLITE_SYNC_DATAONLY]
** flag may be ORed in to indicate that only the data of the file
** and not its inode needs to be synced.
** The second choice is a Mac OS X style fullsync.
**
** The integer values to xLock() and xUnlock() are one of
** <ul>
@@ -4339,6 +4337,10 @@ int sqlite3_limit(sqlite3*, int id, int newVal);
** [[SQLITE_LIMIT_EXPR_DEPTH]] ^(<dt>SQLITE_LIMIT_EXPR_DEPTH</dt>
** <dd>The maximum depth of the parse tree on any expression.</dd>)^
**
** [[SQLITE_LIMIT_PARSER_DEPTH]] ^(<dt>SQLITE_LIMIT_PARSER_DEPTH</dt>
** <dd>The maximum depth of the LALR(1) parser stack used to analyze
** input SQL statements.</dd>)^
**
** [[SQLITE_LIMIT_COMPOUND_SELECT]] ^(<dt>SQLITE_LIMIT_COMPOUND_SELECT</dt>
** <dd>The maximum number of terms in a compound SELECT statement.</dd>)^
**
@@ -4383,6 +4385,7 @@ int sqlite3_limit(sqlite3*, int id, int newVal);
#define SQLITE_LIMIT_VARIABLE_NUMBER 9
#define SQLITE_LIMIT_TRIGGER_DEPTH 10
#define SQLITE_LIMIT_WORKER_THREADS 11
#define SQLITE_LIMIT_PARSER_DEPTH 12
/*
** CAPI3REF: Prepare Flags
+6 -5
View File
@@ -1537,7 +1537,7 @@ struct Schema {
** The number of different kinds of things that can be limited
** using the sqlite3_limit() interface.
*/
#define SQLITE_N_LIMIT (SQLITE_LIMIT_WORKER_THREADS+1)
#define SQLITE_N_LIMIT (SQLITE_LIMIT_PARSER_DEPTH+1)
/*
** Lookaside malloc is a set of fixed-size buffers that can be used
@@ -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
@@ -4271,10 +4268,11 @@ typedef struct {
/*
** Allowed values for mInitFlags
*/
#define INITFLAG_AlterMask 0x0003 /* Types of ALTER */
#define INITFLAG_AlterMask 0x0007 /* Types of ALTER */
#define INITFLAG_AlterRename 0x0001 /* Reparse after a RENAME */
#define INITFLAG_AlterDrop 0x0002 /* Reparse after a DROP COLUMN */
#define INITFLAG_AlterAdd 0x0003 /* Reparse after an ADD COLUMN */
#define INITFLAG_AlterDropCons 0x0004 /* Reparse after an ADD COLUMN */
/* Tuning parameters are set using SQLITE_TESTCTRL_TUNE and are controlled
** on debug-builds of the CLI using ".testctrl tune ID VALUE". Tuning
@@ -5455,6 +5453,9 @@ void sqlite3Reindex(Parse*, Token*, Token*);
void sqlite3AlterFunctions(void);
void sqlite3AlterRenameTable(Parse*, SrcList*, Token*);
void sqlite3AlterRenameColumn(Parse*, SrcList*, Token*, Token*);
void sqlite3AlterDropConstraint(Parse*,SrcList*,Token*,Token*);
void sqlite3AlterAddConstraint(Parse*,SrcList*,Token*,Token*,const char*,int);
void sqlite3AlterSetNotNull(Parse*, SrcList*, Token*, Token*);
i64 sqlite3GetToken(const unsigned char *, int *);
void sqlite3NestedParse(Parse*, const char*, ...);
void sqlite3ExpirePreparedStatements(sqlite3*, int);
+22 -4
View File
@@ -66,15 +66,33 @@
#endif
/*
** The maximum depth of an expression tree. This is limited to
** some extent by SQLITE_MAX_SQL_LENGTH. But sometime you might
** want to place more severe limits on the complexity of an
** expression. A value of 0 means that there is no limit.
** The maximum depth of an expression tree. The expression tree depth
** is also limited indirectly by SQLITE_MAX_SQL_LENGTH and by
** SQLITE_MAX_PARSER_DEPTH. Reducing the maximum complexity of
** expressions can help prevent excess memory usage by hostile SQL.
**
** A value of 0 for this compile-time option causes all expression
** depth limiting code to be omitted.
*/
#ifndef SQLITE_MAX_EXPR_DEPTH
# define SQLITE_MAX_EXPR_DEPTH 1000
#endif
/*
** The maximum depth of the LALR(1) stack used in the parser that
** interprets SQL inputs. The parser stack depth can also be limited
** indirectly by SQLITE_MAX_SQL_LENGTH. Limiting the parser stack
** depth can help prevent excess memory usage and excess CPU stack
** usage when processing hostile SQL.
**
** Prior to version 3.45.0 (2024-01-15), the parser stack was
** hard-coded to 100 entries, and that worked fine for almost all
** applications. So the upper bound on this limit need not be large.
*/
#ifndef SQLITE_MAX_PARSER_DEPTH
# define SQLITE_MAX_PARSER_DEPTH 2500
#endif
/*
** The maximum number of terms in a compound SELECT statement.
** The code generator for compound SELECT statements does one
+389 -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,356 @@ 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.
** -splitcolumn ("auto"|"off"|"on") Enable split-column mode
** -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
** -splitcolumn bSplitColumn
** -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
|| strcmp(zArg,"-splitcolumn")==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 if( zArg[5]=='n' ){
qrf.bTextNull = aBoolMap[v];
}else{
qrf.bSplitColumn = 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 +2423,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 +2440,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 +3338,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 -1
View File
@@ -7370,6 +7370,7 @@ static int SQLITE_TCLAPI test_limit(
{ "SQLITE_LIMIT_SQL_LENGTH", SQLITE_LIMIT_SQL_LENGTH },
{ "SQLITE_LIMIT_COLUMN", SQLITE_LIMIT_COLUMN },
{ "SQLITE_LIMIT_EXPR_DEPTH", SQLITE_LIMIT_EXPR_DEPTH },
{ "SQLITE_LIMIT_PARSER_DEPTH", SQLITE_LIMIT_PARSER_DEPTH },
{ "SQLITE_LIMIT_COMPOUND_SELECT", SQLITE_LIMIT_COMPOUND_SELECT },
{ "SQLITE_LIMIT_VDBE_OP", SQLITE_LIMIT_VDBE_OP },
{ "SQLITE_LIMIT_FUNCTION_ARG", SQLITE_LIMIT_FUNCTION_ARG },
@@ -7381,7 +7382,7 @@ static int SQLITE_TCLAPI test_limit(
/* Out of range test cases */
{ "SQLITE_LIMIT_TOOSMALL", -1, },
{ "SQLITE_LIMIT_TOOBIG", SQLITE_LIMIT_WORKER_THREADS+1 },
{ "SQLITE_LIMIT_TOOBIG", SQLITE_LIMIT_PARSER_DEPTH+1 },
};
int i, id = 0;
int val;
+1 -1
View File
@@ -514,7 +514,7 @@ static int fsSync(sqlite3_file *pFile, int flags){
rc = pRealFile->pMethods->xWrite(pRealFile, zSize, 4, 0);
}
if( rc==SQLITE_OK ){
rc = pRealFile->pMethods->xSync(pRealFile, flags&(~SQLITE_SYNC_DATAONLY));
rc = pRealFile->pMethods->xSync(pRealFile, flags);
}
return rc;
-6
View File
@@ -436,12 +436,6 @@ static int tvfsSync(sqlite3_file *pFile, int flags){
case SQLITE_SYNC_FULL:
zFlags = "full";
break;
case SQLITE_SYNC_NORMAL|SQLITE_SYNC_DATAONLY:
zFlags = "normal|dataonly";
break;
case SQLITE_SYNC_FULL|SQLITE_SYNC_DATAONLY:
zFlags = "full|dataonly";
break;
default:
assert(0);
}
+51
View File
@@ -116,4 +116,55 @@ do_auth_test 1.3 {
{SQLITE_UPDATE sqlite_temp_master sql temp {}}
}
do_auth_test 1.4 {
ALTER TABLE t2 ALTER COLUMN b SET NOT NULL;
} {
{SQLITE_ALTER_TABLE main t2 b {}}
{SQLITE_FUNCTION {} sqlite_add_constraint {} {}}
{SQLITE_FUNCTION {} sqlite_drop_constraint {} {}}
{SQLITE_FUNCTION {} sqlite_fail {} {}}
{SQLITE_READ sqlite_master sql main {}}
{SQLITE_READ sqlite_master tbl_name main {}}
{SQLITE_READ sqlite_master type main {}}
{SQLITE_READ t2 b main {}}
{SQLITE_SELECT {} {} {} {}}
{SQLITE_UPDATE sqlite_master sql main {}}
}
do_auth_test 1.5 {
ALTER TABLE t2 ALTER COLUMN 'b' DROP NOT NULL;
} {
{SQLITE_ALTER_TABLE main t2 b {}}
{SQLITE_FUNCTION {} sqlite_drop_constraint {} {}}
{SQLITE_READ sqlite_master sql main {}}
{SQLITE_READ sqlite_master tbl_name main {}}
{SQLITE_READ sqlite_master type main {}}
{SQLITE_UPDATE sqlite_master sql main {}}
}
do_auth_test 1.6 {
ALTER TABLE t2 ADD CONSTRAINT abc CHECK (aaa>b)
} {
{SQLITE_ALTER_TABLE main t2 {} {}}
{SQLITE_FUNCTION {} sqlite_add_constraint {} {}}
{SQLITE_FUNCTION {} sqlite_fail {} {}}
{SQLITE_FUNCTION {} sqlite_find_constraint {} {}}
{SQLITE_READ sqlite_master sql main {}}
{SQLITE_READ sqlite_master tbl_name main {}}
{SQLITE_READ sqlite_master type main {}}
{SQLITE_READ t2 aaa main {}}
{SQLITE_READ t2 b main {}}
{SQLITE_SELECT {} {} {} {}}
{SQLITE_UPDATE sqlite_master sql main {}}
}
do_auth_test 1.7 {
ALTER TABLE t2 DROP CONSTRAINT abc;
} {
{SQLITE_ALTER_TABLE main t2 {} {}}
{SQLITE_FUNCTION {} sqlite_drop_constraint {} {}}
{SQLITE_READ sqlite_master sql main {}}
{SQLITE_READ sqlite_master tbl_name main {}}
{SQLITE_READ sqlite_master type main {}}
{SQLITE_UPDATE sqlite_master sql main {}}
}
finish_test
+13
View File
@@ -946,4 +946,17 @@ do_execsql_test 23.20 {
ROLLBACK;
} {t4new}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 24.0 {
CREATE TABLE t1(a PRIMARY KEY, b);
CREATE TABLE t2(a, b, c);
INSERT INTO t2 VALUES(1, 1, 1);
}
do_catchsql_test 24.1 {
PRAGMA foreign_keys = 1;
ALTER TABLE t2 ADD COLUMN d REFERENCES t1 DEFAULT 123;
} {1 {Cannot add a REFERENCES column with non-NULL default value}}
finish_test
+442
View File
@@ -0,0 +1,442 @@
# 2025 September 18
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#*************************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix altercons
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
ifcapable !altertable {
finish_test
return
}
foreach {tn before after} {
1 { CREATE TABLE t1(a, b CONSTRAINT abc CHECK(t1.a != t1.b)) }
{ CREATE TABLE t1(a, b) }
2 { CREATE TABLE t1(a, b CONSTRAINT abc CHECK(t1.a != t1.b) NOT NULL) }
{ CREATE TABLE t1(a, b NOT NULL) }
3 { CREATE TABLE t1(a, b CONSTRAINT abc CHECK(t1.a != t1.b)NOT NULL) }
{ CREATE TABLE t1(a, b NOT NULL) }
3 { CREATE TABLE t1(a, b NOT NULL CONSTRAINT abc CHECK(t1.a != t1.b)); }
{ CREATE TABLE t1(a, b NOT NULL) }
4 { CREATE TABLE t1(a, b, CONSTRAINT abc CHECK(t1.a != t1.b)) }
{ CREATE TABLE t1(a, b) }
5 { CREATE TABLE t1(a, b, CONSTRAINT abc CHECK(t1.a != t1.b), PRIMARY KEY(a))}
{ CREATE TABLE t1(a, b, PRIMARY KEY(a)) }
6 { CREATE TABLE t1(a, b,CONSTRAINT abc CHECK(t1.a != t1.b),PRIMARY KEY(a))}
{ CREATE TABLE t1(a, b,PRIMARY KEY(a)) }
7 { CREATE TABLE t1(a, b CONSTRAINT abc CHECK(t1.a != t1.b) CONSTRAINT def UNIQUE) }
{ CREATE TABLE t1(a, b CONSTRAINT def UNIQUE) }
8 { CREATE TABLE t1(a, b CONSTRAINT abc CHECK(t1.a != t1.b) CHECK (123)) }
{ CREATE TABLE t1(a, b CHECK (123)) }
9 { CREATE TABLE t1(a, b CONSTRAINT abc CHECK(t1.a != t1.b) DEFAULT NULL) }
{ CREATE TABLE t1(a, b DEFAULT NULL) }
10 { CREATE TABLE t1(a, b CONSTRAINT abc CHECK(t1.a != t1.b) COLLATE nocase) }
{ CREATE TABLE t1(a, b COLLATE nocase) }
11 { CREATE TABLE t1(a, b CONSTRAINT abc CHECK(t1.a != t1.b) REFERENCES t2) }
{ CREATE TABLE t1(a, b REFERENCES t2) }
12 { CREATE TABLE t1(a, b, c, CONSTRAINT one CONSTRAINT abc CHECK(a!=b) CONSTRAINT three) }
{ CREATE TABLE t1(a, b, c, CONSTRAINT one CONSTRAINT three) }
13 { CREATE TABLE t1(a, b, c, CONSTRAINT abc CONSTRAINT one CHECK(a!=b) CONSTRAINT three) }
{ CREATE TABLE t1(a, b, c, CONSTRAINT one CHECK(a!=b) CONSTRAINT three) }
14 { CREATE TABLE t1(a, b, c, CONSTRAINT abc) }
{ CREATE TABLE t1(a, b, c) }
15 { CREATE TABLE t1(a, b, c,
CONSTRAINT abc, CHECK( a!=b )) }
{ CREATE TABLE t1(a, b, c, CHECK( a!=b )) }
16 { CREATE TABLE t1(a, b, c, CONSTRAINT abc /* hello */ CHECK( a!=b )) }
{ CREATE TABLE t1(a, b, c) }
17 { CREATE TABLE t1(a, b, c, /* world */ CONSTRAINT abc CHECK( a!=b )) }
{ CREATE TABLE t1(a, b, c) }
18 { CREATE TABLE t1(a, b, c -- comment
CONSTRAINT abc NOT NULL
) }
{ CREATE TABLE t1(a, b, c) }
19 { CREATE TABLE t1(a, b, c, -- comment
CONSTRAINT abc CHECK (a>b) CONSTRAINT two
) }
{ CREATE TABLE t1(a, b, c, CONSTRAINT two
) }
20 { CREATE TABLE t1(a, b, c, CONSTRAINT one CONSTRAINT abc CHECK (a>b)CONSTRAINT two) }
{ CREATE TABLE t1(a, b, c, CONSTRAINT one CONSTRAINT two) }
21 { CREATE TABLE t1(a, b, c CONSTRAINT abc AS (b+1)) }
{ CREATE TABLE t1(a, b, c AS (b+1)) }
22 { CREATE TABLE t1(a, b, c CONSTRAINT abc GENERATED ALWAYS AS (b+1) STORED) }
{ CREATE TABLE t1(a, b, c GENERATED ALWAYS AS (b+1) STORED) }
} {
reset_db
do_execsql_test 1.$tn.0 $before
do_execsql_test 1.$tn.1 {
ALTER TABLE t1 DROP CONSTRAINT abc;
} {}
do_execsql_test 1.$tn.2 {
SELECT sql FROM sqlite_schema WHERE name='t1'
} [list [string trim $after]]
}
#-------------------------------------------------------------------------
do_execsql_test 2.0 {
CREATE TABLE t2(x, y CONSTRAINT ccc UNIQUE);
}
do_catchsql_test 2.1 {
ALTER TABLE t2 DROP CONSTRAINT ccc
} {1 {constraint may not be dropped: ccc}}
do_catchsql_test 2.2 {
ALTER TABLE t2 DROP CONSTRAINT ddd
} {1 {no such constraint: ddd}}
#-------------------------------------------------------------------------
reset_db
foreach {tn col before after} {
1 a { CREATE TABLE t1(a NOT NULL, b) }
{ CREATE TABLE t1(a, b) }
2 a { CREATE TABLE t1(a NOT NULL ON CONFLICT FAIL, b) }
{ CREATE TABLE t1(a, b) }
3 a { CREATE TABLE t1(a NOT NULL ON CONFLICT FAIL UNIQUE, b) }
{ CREATE TABLE t1(a UNIQUE, b) }
4 b { CREATE TABLE t1(a NOT NULL ON CONFLICT FAIL UNIQUE, b) }
{ CREATE TABLE t1(a NOT NULL ON CONFLICT FAIL UNIQUE, b) }
5 a { CREATE TABLE t1(a CHECK(a<b) NOT NULL, b) }
{ CREATE TABLE t1(a CHECK(a<b), b) }
6 a { CREATE TABLE t1(a CHECK(a<b) CONSTRAINT nn NOT NULL, b) }
{ CREATE TABLE t1(a CHECK(a<b), b) }
7 b { CREATE TABLE t1(a, b NOT NULL PRIMARY KEY) }
{ CREATE TABLE t1(a, b PRIMARY KEY) }
8 b { CREATE TABLE t1(a, b CHECK ((b+a) IS NOT NULL) NOT NULL PRIMARY KEY) }
{ CREATE TABLE t1(a, b CHECK ((b+a) IS NOT NULL) PRIMARY KEY) }
9 b { CREATE TABLE t1(a, b CONSTRAINT nn CHECK (b IS NOT NULL) NOT NULL) }
{ CREATE TABLE t1(a, b CONSTRAINT nn CHECK (b IS NOT NULL)) }
10 b { CREATE TABLE t1(a, b NOT NULL AS (a+1)) }
{ CREATE TABLE t1(a, b AS (a+1)) }
11 b { CREATE TABLE t1(a, b NOT NULL GENERATED ALWAYS AS (a+1)) }
{ CREATE TABLE t1(a, b GENERATED ALWAYS AS (a+1)) }
} {
reset_db
do_execsql_test 3.$tn.0 $before
do_execsql_test 3.$tn.1 "
ALTER TABLE t1 ALTER COLUMN $col DROP NOT NULL
"
do_execsql_test 3.$tn.2 {
SELECT sql FROM sqlite_schema WHERE name='t1'
} [list [string trim $after]]
}
#-------------------------------------------------------------------------
#
reset_db
do_execsql_test 4.0 {
CREATE TABLE t2(x, y CONSTRAINT ccc UNIQUE);
}
do_execsql_test 4.1 {
ALTER TABLE t2 ALTER x DROP NOT NULL;
ALTER TABLE t2 ALTER x DROP NOT NULL;
ALTER TABLE t2 ALTER x DROP NOT NULL;
} {}
#-------------------------------------------------------------------------
#
reset_db
do_execsql_test 5.1 {
CREATE TABLE t3(a INTEGER PRIMARY KEY, b);
INSERT INTO t3 VALUES(1000, NULL);
}
do_catchsql_test 5.2.1 {
ALTER TABLE t3 ALTER b SET NOT NULL
} {1 {constraint failed}}
do_test 5.2.2 {
sqlite3_errcode db
} {SQLITE_CONSTRAINT}
foreach {tn before alter after} {
1 { CREATE TABLE t1(a, b) }
{ ALTER TABLE t1 ALTER a SET NOT NULL }
{ CREATE TABLE t1(a NOT NULL, b) }
2 { CREATE TABLE t1(a, b) }
{ ALTER TABLE t1 ALTER a SET NOT NULL ON CONFLICT FAIL }
{ CREATE TABLE t1(a NOT NULL ON CONFLICT FAIL, b) }
3 { CREATE TABLE t1(a, b) }
{ ALTER TABLE t1 ALTER a SET NOT NULL ON CONFLICT fail; }
{ CREATE TABLE t1(a NOT NULL ON CONFLICT fail, b) }
4 { CREATE TABLE t1(a, b) }
{ ALTER TABLE t1 ALTER b SET NOT NULL ON CONFLICT IGNORE ; }
{ CREATE TABLE t1(a, b NOT NULL ON CONFLICT IGNORE) }
5 { CREATE TABLE t1(a, 'a b c' VARCHAR(10), UNIQUE(a)) }
{ ALTER TABLE t1 ALTER 'a b c' SET NOT NULL }
{ CREATE TABLE t1(a, 'a b c' VARCHAR(10) NOT NULL, UNIQUE(a)) }
} {
reset_db
do_execsql_test 5.3.$tn.1 $before
do_execsql_test 5.3.$tn.2 $alter
do_execsql_test 5.3.$tn.3 {
SELECT sql FROM sqlite_schema WHERE name='t1';
} [list [string trim $after]]
}
do_execsql_test 5.4.1 {
CREATE TABLE x1(a, b, c);
}
do_catchsql_test 5.4.2 {
ALTER TABLE x1 ALTER d SET NOT NULL;
} {1 {no such column: d}}
do_catchsql_test 5.4.3 {
ALTER TABLE x2 ALTER c SET NOT NULL;
} {1 {no such table: x2}}
do_catchsql_test 5.4.4 {
ALTER TABLE temp.x1 ALTER c SET NOT NULL;
} {1 {no such table: temp.x1}}
#-------------------------------------------------------------------------
#
reset_db
do_execsql_test 6.1 {
CREATE TABLE t1(a, b, c);
INSERT INTO t1 VALUES(1, 2, 3);
INSERT INTO t1 VALUES(4, 5, 6);
}
do_catchsql_test 6.2.1 {
ALTER TABLE t1 ADD CONSTRAINT nn CHECK (c!=6);
} {1 {constraint failed}}
do_execsql_test 6.2.2 {
DELETE FROM t1 WHERE c=6;
ALTER TABLE t1 ADD CONSTRAINT nn CHECK (c!=6);
} {}
do_catchsql_test 6.2.3 {
INSERT INTO t1 VALUES(4, 5, 6);
} {1 {CHECK constraint failed: nn}}
foreach {tn before alter after} {
1 { CREATE TABLE t1(a, b) }
{ ALTER TABLE t1 ADD CONSTRAINT nn CHECK (a>=0) }
{ CREATE TABLE t1(a, b, CONSTRAINT nn CHECK (a>=0)) }
2 { CREATE TABLE t1(a, b ) }
{ ALTER TABLE t1 ADD CONSTRAINT nn CHECK (a>=0) }
{ CREATE TABLE t1(a, b , CONSTRAINT nn CHECK (a>=0)) }
3 { CREATE TABLE t1(a, b ) }
{ ALTER TABLE t1 ADD CHECK (a>=0) }
{ CREATE TABLE t1(a, b , CHECK (a>=0)) }
} {
reset_db
do_execsql_test 6.3.$tn.1 $before
do_execsql_test 6.3.$tn.2 $alter
do_execsql_test 6.3.$tn.3 {
SELECT sql FROM sqlite_schema WHERE type='table';
} [list [string trim $after]]
}
do_execsql_test 6.4.1 {
CREATE TABLE b1(a, b, CONSTRAINT abc CHECK (a!=2));
}
do_catchsql_test 6.4.2 {
ALTER TABLE b1 ADD CONSTRAINT abc CHECK (a!=3);
} {1 {constraint abc already exists}}
do_execsql_test 6.4.1 {
SELECT sql FROM sqlite_schema WHERE tbl_name='b1'
} {{CREATE TABLE b1(a, b, CONSTRAINT abc CHECK (a!=2))}}
do_execsql_test 6.5 {
CREATE TABLE abc(x,y);
}
do_catchsql_test 6.6 {
ALTER TABLE abc ADD CHECK (z>=0);
} {1 {no such column: z}}
#-------------------------------------------------------------------------
# Try attaching a NOT NULL to a generated column.
#
reset_db
do_execsql_test 7.0 {
CREATE TABLE x1(a, b AS (a+1));
INSERT INTO x1 VALUES(1), (2), (3), (NULL);
}
do_catchsql_test 7.1 {
ALTER TABLE x1 ALTER b SET NOT NULL;
} {1 {constraint failed}}
do_catchsql_test 7.2 {
DELETE FROM x1 WHERE b IS NULL;
ALTER TABLE x1 ALTER b SET NOT NULL;
} {0 {}}
do_execsql_test 7.3 {
SELECT b FROM x1
} {2 3 4}
do_catchsql_test 7.4 {
ALTER TABLE x1 ALTER rowid SET NOT NULL;
} {1 {no such column: rowid}}
do_execsql_test 7.5 {
CREATE VIEW v1 AS SELECT a, b FROM x1;
}
do_catchsql_test 7.6 {
ALTER TABLE v1 RENAME a TO c;
} {1 {cannot rename columns of view "v1"}}
do_catchsql_test 7.7 {
ALTER TABLE v1 ALTER a SET NOT NULL;
} {1 {cannot edit constraints of view "v1"}}
do_catchsql_test 7.8 {
ALTER TABLE sqlite_schema ALTER sql SET NOT NULL;
} {1 {table sqlite_master may not be altered}}
do_catchsql_test 7.9 {
ALTER TABLE v1 ALTER a DROP NOT NULL
} {1 {cannot edit constraints of view "v1"}}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 8.0 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b NOT NULL, c CHECK (c!=555), d);
INSERT INTO t1 VALUES(1, 1, 1, 1);
INSERT INTO t1 VALUES(2, 2, 2, 2);
INSERT INTO t1 VALUES(3, 3, 3, 3);
}
do_execsql_test 8.1.1 {
ALTER TABLE t1 ALTER a SET NOT NULL;
ALTER TABLE t1 ALTER b SET NOT NULL;
ALTER TABLE t1 ALTER c SET NOT NULL;
ALTER TABLE t1 ALTER d SET NOT NULL;
}
do_execsql_test 8.1.2 {
SELECT sql FROM sqlite_schema WHERE tbl_name = 't1'
} {{CREATE TABLE t1(a INTEGER PRIMARY KEY NOT NULL, b NOT NULL, c CHECK (c!=555) NOT NULL, d NOT NULL)}}
do_execsql_test 8.1.3 {
SELECT * FROM t1 WHERE a=2;
} {2 2 2 2}
do_execsql_test 8.2.1 {
ALTER TABLE t1 ALTER a DROP NOT NULL;
ALTER TABLE t1 ALTER b DROP NOT NULL;
ALTER TABLE t1 ALTER c DROP NOT NULL;
ALTER TABLE t1 ALTER d DROP NOT NULL;
}
do_execsql_test 8.2.2 {
SELECT sql FROM sqlite_schema WHERE tbl_name = 't1'
} {{CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c CHECK (c!=555), d)}}
do_execsql_test 8.2.3 {
SELECT * FROM t1 WHERE a=3;
} {3 3 3 3}
#-------------------------------------------------------------------------
reset_db
forcedelete test.db2
do_execsql_test 9.0 {
CREATE TABLE t1(x, y, z);
ATTACH 'test.db2' AS aux;
CREATE TABLE aux.t1(x, y, z);
INSERT INTO aux.t1 VALUES(1, 1, 1);
INSERT INTO aux.t1 VALUES(2, 2, 2);
INSERT INTO aux.t1 VALUES(3, 3, NULL);
CREATE TABLE aux.t2(x, y, z);
}
do_catchsql_test 9.1.1 {
ALTER TABLE aux.t1 ALTER COLUMN z SET NOT NULL
} {1 {constraint failed}}
do_execsql_test 9.1.2 {
UPDATE aux.t1 SET z=x;
ALTER TABLE aux.t1 ALTER COLUMN z SET NOT NULL;
SELECT sql FROM aux.sqlite_schema WHERE name='t1';
} {{CREATE TABLE t1(x, y, z NOT NULL)}}
do_execsql_test 9.1.3 {
ALTER TABLE aux.t1 ALTER z DROP NOT NULL;
SELECT sql FROM aux.sqlite_schema WHERE name='t1';
} {{CREATE TABLE t1(x, y, z)}}
do_execsql_test 9.1.4 {
ALTER TABLE t2 ALTER x SET NOT NULL;
SELECT sql FROM aux.sqlite_schema WHERE name='t2';
} {{CREATE TABLE t2(x NOT NULL, y, z)}}
do_execsql_test 9.1.5 {
ALTER TABLE t2 ALTER x DROP NOT NULL;
SELECT sql FROM aux.sqlite_schema WHERE name='t2';
} {{CREATE TABLE t2(x, y, z)}}
do_catchsql_test 9.2.1 {
ALTER TABLE aux.t1 ADD CONSTRAINT bill CHECK (y!=2);
} {1 {constraint failed}}
do_execsql_test 9.2.2 {
UPDATE aux.t1 SET y=4 WHERE y=2;
ALTER TABLE aux.t1 ADD CONSTRAINT bill CHECK (y!=2);
SELECT sql FROM aux.sqlite_schema WHERE name='t1';
} {{CREATE TABLE t1(x, y, z, CONSTRAINT bill CHECK (y!=2))}}
do_execsql_test 9.2.3 {
ALTER TABLE aux.t1 DROP CONSTRAINT bill;
SELECT sql FROM aux.sqlite_schema WHERE name='t1';
} {{CREATE TABLE t1(x, y, z)}}
do_execsql_test 9.2.4 {
ALTER TABLE t2 ADD CONSTRAINT william CHECK (z!='');
SELECT sql FROM aux.sqlite_schema WHERE name='t2';
} {{CREATE TABLE t2(x, y, z, CONSTRAINT william CHECK (z!=''))}}
do_execsql_test 9.2.5 {
ALTER TABLE t2 DROP CONSTRAINT william;
SELECT sql FROM aux.sqlite_schema WHERE name='t2';
} {{CREATE TABLE t2(x, y, z)}}
finish_test
+247
View File
@@ -0,0 +1,247 @@
# 2025 September 18
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#*************************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix altercons2
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
ifcapable !altertable {
finish_test
return
}
foreach {tn newsql alter res final} {
1 "CREATE TABLE t1(a, b"
"ALTER TABLE t1 ALTER c SET NOT NULL"
{1 {database disk image is malformed}}
"CREATE TABLE t1(a, b"
2 "CREATE TABLE t1(a, b, "
"ALTER TABLE t1 ALTER c DROP NOT NULL"
{0 {}}
"CREATE TABLE t1(a, b, "
3 "CREATE TABLE t1(a, b, CHECK( ..."
"ALTER TABLE t1 ALTER c DROP NOT NULL"
{0 {}}
"CREATE TABLE t1(a, b, CHECK( ..."
4 "CREATE TABLE t1(a, b, c NOT NULL"
"ALTER TABLE t1 ALTER c DROP NOT NULL"
{0 {}}
"CREATE TABLE t1(a, b, c "
5 "CREATE TABLE"
"ALTER TABLE t1 ALTER c DROP NOT NULL"
{1 {database disk image is malformed}}
"CREATE TABLE"
6 "CREATE TABLE"
"ALTER TABLE t1 ADD CONSTRAINT nn CHECK (a!=0)"
{1 {database disk image is malformed}}
"CREATE TABLE"
} {
reset_db
do_execsql_test 1.$tn.0 {
CREATE TABLE t1(a, b, c NOT NULL, CONSTRAINT xyz CHECK( a!=0 ));
}
do_execsql_test 1.$tn.1 {
PRAGMA writable_schema = 1;
UPDATE sqlite_schema SET sql = $::newsql
}
do_catchsql_test 1.$tn.2 $alter $res
do_execsql_test 1.$tn.3 {
SELECT sql FROM sqlite_schema WHERE name='t1'
} [list $final]
}
#-------------------------------------------------------------------------
reset_db
proc xAuth {t args} {
if {$t=="SQLITE_ALTER_TABLE"} {
return "SQLITE_DENY"
}
return "SQLITE_OK"
}
sqlite3 db test.db
db auth xAuth
do_execsql_test 2.0 {
CREATE TABLE x1(a PRIMARY KEY, b CHECK(a!=b) NOT NULL, c);
}
do_catchsql_test 2.1.1 {
ALTER TABLE x1 ADD CONSTRAINT ccc CHECK (a!='a')
} {1 {not authorized}}
do_execsql_test 2.1.2 {
SELECT sql FROM sqlite_schema WHERE name='x1'
} {{CREATE TABLE x1(a PRIMARY KEY, b CHECK(a!=b) NOT NULL, c)}}
do_catchsql_test 2.2.1 {
ALTER TABLE x1 ALTER c SET NOT NULL
} {1 {not authorized}}
do_execsql_test 2.2.2 {
SELECT sql FROM sqlite_schema WHERE name='x1'
} {{CREATE TABLE x1(a PRIMARY KEY, b CHECK(a!=b) NOT NULL, c)}}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 3.0 {
CREATE TABLE t1(x);
}
do_execsql_test 3.1 {
ALTER TABLE t1 ALTER x SET NOT NULL;
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 4.0 {
CREATE TABLE abc(a, b, c, CONSTRAINT one CONSTRAINT two CHECK (b!=c));
}
do_execsql_test 4.1 {
ALTER TABLE abc DROP CONSTRAINT one
}
do_execsql_test 4.2 {
SELECT sql FROM sqlite_schema
} {
{CREATE TABLE abc(a, b, c, CONSTRAINT two CHECK (b!=c))}
}
#-------------------------------------------------------------------------
reset_db
# The columns must come before the table constraints in a CREATE TABLE
# statement. This is useful, as it means the DROP CONSTRAINT code does
# not have to handle the constraint immediately following the '(' at
# the start of the column-list.
do_catchsql_test 5.0 {
CREATE TABLE abc(a, b, c, CONSTRAINT two CHECK (b!=c), d)
} {1 {near "d": syntax error}}
do_catchsql_test 5.1 {
CREATE TABLE def(CONSTRAINT abc CHECK( b!=c ), a, b, c);
} {1 {near "CONSTRAINT": syntax error}}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 6.0 {
CREATE TABLE abc(a, b CONSTRAINT two COLLATE nocase CHECK (a!=b), c CONSTRAINT one DEFAULT 'abc');
}
do_execsql_test 6.1 {
ALTER TABLE abc DROP CONSTRAINT one;
ALTER TABLE abc DROP CONSTRAINT two;
}
do_execsql_test 6.2 {
SELECT sql FROM sqlite_schema
} {
{CREATE TABLE abc(a, b COLLATE nocase CHECK (a!=b), c DEFAULT 'abc')}
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 7.0 {
CREATE TABLE abc(a, b, c, CONSTRAINT one CHECK (a>b) FOREIGN KEY(a) REFERENCES abc);
}
do_execsql_test 7.1 {
ALTER TABLE abc DROP CONSTRAINT one
}
do_execsql_test 7.2 {
SELECT sql FROM sqlite_schema
} {
{CREATE TABLE abc(a, b, c, FOREIGN KEY(a) REFERENCES abc)}
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 8.0 {
CREATE TABLE abc(a, b, c, CONSTRAINT one FOREIGN KEY(a) REFERENCES abc);
}
do_catchsql_test 8.1 {
ALTER TABLE abc DROP CONSTRAINT one
} {1 {constraint may not be dropped: one}}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 9.0 {
CREATE TABLE abc(a, b NOT NULL AS (a+1))
}
do_execsql_test 9.1 {
ALTER TABLE abc ALTER b DROP NOT NULL;
SELECT sql FROM sqlite_schema;
} {{CREATE TABLE abc(a, b AS (a+1))}}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 10.0 {
CREATE TABLE abc(a, b GENERATED ALWAYS AS (a+1));
INSERT INTO abc VALUES(1), (2);
SELECT * FROM abc;
} {1 2 2 3}
do_execsql_test 10.1 {
ALTER TABLE abc ALTER b SET NOT NULL;
}
do_catchsql_test 10.2 {
INSERT INTO abc VALUES(NULL);
} {1 {NOT NULL constraint failed: abc.b}}
do_execsql_test 10.3 {
INSERT INTO abc VALUES(3);
ALTER TABLE abc ALTER COLUMN b DROP NOT NULL;
}
do_execsql_test 10.4 {
INSERT INTO abc VALUES(NULL);
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 11.0 {
CREATE TABLE t1(a, b, c);
}
do_execsql_test 11.1.1 {
ALTER TABLE t1 ADD CONSTRAINT c1 CHECK(a=b) --comment
;
}
do_execsql_test 11.1.2 {ALTER TABLE t1 ADD CONSTRAINT c2 CHECK(a=b) --comment}
do_execsql_test 11.1.3 {
SELECT sql FROM sqlite_schema;
} {
{CREATE TABLE t1(a, b, c, CONSTRAINT c1 CHECK(a=b), CONSTRAINT c2 CHECK(a=b))}
}
do_execsql_test 11.2.1 {
CREATE TABLE t2(a, b);
}
do_execsql_test 11.2.2 {ALTER TABLE t2 ALTER b SET NOT NULL --new cons}
do_execsql_test 11.2.3 {
SELECT sql FROM sqlite_schema WHERE name='t2';
} {
{CREATE TABLE t2(a, b NOT NULL)}
}
do_execsql_test 11.2.3 {
ALTER TABLE t2 ALTER b DROP NOT NULL;
SELECT sql FROM sqlite_schema WHERE name='t2';
} {
{CREATE TABLE t2(a, b)}
}
do_execsql_test 11.2.2 {ALTER TABLE t2 ALTER b SET NOT NULL --new cons
;
}
finish_test
+34
View File
@@ -36,6 +36,40 @@ do_faultsim_test 1.1 -faults oom* -prep {
faultsim_test_result {0 {}}
}
reset_db
do_execsql_test 2.0 {
CREATE TABLE x1(d, e CONSTRAINT abc NOT NULL, f);
}
faultsim_save_and_close
foreach {tn sql} {
1 { ALTER TABLE x1 ADD CHECK (d!=1) }
2 { ALTER TABLE x1 ADD CONSTRAINT xyz CHECK (f>d+e); }
3 { ALTER TABLE x1 DROP CONSTRAINT abc }
4 { ALTER TABLE x1 ALTER f SET NOT NULL }
5 { ALTER TABLE x1 ALTER e DROP NOT NULL }
} {
do_faultsim_test 2.$tn -faults oom* -prep {
faultsim_restore_and_reopen
} -body {
execsql $::sql
} -test {
faultsim_test_result {0 {}}
}
}
# Test an OOM when returning an error.
#
do_faultsim_test 2.e -faults oom* -prep {
faultsim_restore_and_reopen
} -body {
execsql {
ALTER TABLE x1 DROP CONSTRAINT nosuchconstraint
}
} -test {
faultsim_test_result \
{1 {no such constraint: nosuchconstraint}} \
{1 {SQL logic error}}
}
finish_test
+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"
+1 -1
View File
@@ -595,7 +595,7 @@ do_test misc5-7.1.2 {
}
append sql "0$tail); SELECT * FROM t1;"
catchsql $sql
} {0 900}
} {1 {Recursion limit}}
# Parser stack overflow is silently ignored when it occurs while parsing the
+83
View File
@@ -0,0 +1,83 @@
# 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"'
# Test cases for the ".explain off" command
.mode box -reset
.output memory
EXPLAIN SELECT * FROM t1;
.output --notglob *────* --keep
.output --notglob "* id │ parent │ notused │ detail *" --keep
.output --glob "* Init *"
.output memory
EXPLAIN QUERY PLAN SELECT * FROM t1;
.output --glob "*`--SCAN *"
.explain off
.output memory
EXPLAIN SELECT * FROM t1;
.output --glob *────*
.output memory
EXPLAIN QUERY PLAN SELECT * FROM t1;
.output --glob "* id │ parent │ notused │ detail *"
.explain auto
+994
View File
@@ -0,0 +1,994 @@
# 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'),}
do_test 9.3 {
db format -style json {SELECT * FROM t9 WHERE rowid<0}
} {}
do_test 9.4 {
db format -style jobject {SELECT * FROM t9 WHERE rowid<0}
} {}
do_test 10.1 {
db eval {
DROP TABLE IF EXISTS t1;
CREATE TABLE t1(x);
INSERT INTO t1(x) VALUES
('alice'),
('bob'),
('cinderella-cinderella'),
('daniel'),
('emma'),
('fred'),
('gertrude'),
('harold'),
('ingrid'),
('jake'),
('lisa'),
('mike'),
('nina'),
('octavian'),
('paula'),
('quintus'),
('rita'),
('sam'),
('tammy'),
('ulysses'),
('violet'),
('william'),
('xanthippe'),
('yates'),
('zoe');
}
set result "\n[db format -style column -title off -screenwidth 41 -splitcolumn on \
{SELECT x FROM t1}]"
} {
alice octavian
bob paula
cinderella-cinderella quintus
daniel rita
emma sam
fred tammy
gertrude ulysses
harold violet
ingrid william
jake xanthippe
lisa yates
mike zoe
nina
}
do_test 10.2 {
set result "\n[db format -style column -title off -screenwidth 42 -splitcolumn on \
{SELECT x FROM t1}]"
} {
alice jake tammy
bob lisa ulysses
cinderella-cinderella mike violet
daniel nina william
emma octavian xanthippe
fred paula yates
gertrude quintus zoe
harold rita
ingrid sam
}
do_test 10.3 {
set result "\n[db format -style column -title off -screenwidth 51 -splitcolumn on \
{SELECT x FROM t1}]"
} {
alice harold paula william
bob ingrid quintus xanthippe
cinderella-cinderella jake rita yates
daniel lisa sam zoe
emma mike tammy
fred nina ulysses
gertrude octavian violet
}
do_test 10.4 {
set result "\n[db format -style column -title off -screenwidth 61 -splitcolumn on \
{SELECT x FROM t1}]"
} {
alice fred lisa quintus violet
bob gertrude mike rita william
cinderella-cinderella harold nina sam xanthippe
daniel ingrid octavian tammy yates
emma jake paula ulysses zoe
}
do_test 10.5 {
set result "\n[db format -style column -title off -screenwidth 74 -splitcolumn on \
{SELECT x FROM t1}]"
} {
alice emma ingrid nina rita violet zoe
bob fred jake octavian sam william
cinderella-cinderella gertrude lisa paula tammy xanthippe
daniel harold mike quintus ulysses yates
}
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
+750
View File
@@ -0,0 +1,750 @@
# 2025-11-23
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# Test cases for the Query Result Formatter (QRF), and especially
# the bSplitColumn feature.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix qrf01
# The expected output from test 1.1. The "do_test" procedure normally
# ignores differences in whitespace, but whitespace is important for
# this test, so we have to do the comparison ourselves.
#
set expected {
<---- 22 ---->
alice
bob
cinderella-cinderella
daniel
emma
fred
gertrude
harold
ingrid
jake
lisa
mike
nina
octavian
paula
quintus
rita
sam
tammy
ulysses
violet
william
xanthippe
yates
zoe
<---- 23 ---->
alice
bob
cinderella-cinderella
daniel
emma
fred
gertrude
harold
ingrid
jake
lisa
mike
nina
octavian
paula
quintus
rita
sam
tammy
ulysses
violet
william
xanthippe
yates
zoe
<---- 24 ---->
alice
bob
cinderella-cinderella
daniel
emma
fred
gertrude
harold
ingrid
jake
lisa
mike
nina
octavian
paula
quintus
rita
sam
tammy
ulysses
violet
william
xanthippe
yates
zoe
<---- 25 ---->
alice
bob
cinderella-cinderella
daniel
emma
fred
gertrude
harold
ingrid
jake
lisa
mike
nina
octavian
paula
quintus
rita
sam
tammy
ulysses
violet
william
xanthippe
yates
zoe
<---- 26 ---->
alice
bob
cinderella-cinderella
daniel
emma
fred
gertrude
harold
ingrid
jake
lisa
mike
nina
octavian
paula
quintus
rita
sam
tammy
ulysses
violet
william
xanthippe
yates
zoe
<---- 27 ---->
alice
bob
cinderella-cinderella
daniel
emma
fred
gertrude
harold
ingrid
jake
lisa
mike
nina
octavian
paula
quintus
rita
sam
tammy
ulysses
violet
william
xanthippe
yates
zoe
<---- 28 ---->
alice
bob
cinderella-cinderella
daniel
emma
fred
gertrude
harold
ingrid
jake
lisa
mike
nina
octavian
paula
quintus
rita
sam
tammy
ulysses
violet
william
xanthippe
yates
zoe
<---- 29 ---->
alice
bob
cinderella-cinderella
daniel
emma
fred
gertrude
harold
ingrid
jake
lisa
mike
nina
octavian
paula
quintus
rita
sam
tammy
ulysses
violet
william
xanthippe
yates
zoe
<---- 30 ---->
alice
bob
cinderella-cinderella
daniel
emma
fred
gertrude
harold
ingrid
jake
lisa
mike
nina
octavian
paula
quintus
rita
sam
tammy
ulysses
violet
william
xanthippe
yates
zoe
<---- 31 ---->
alice
bob
cinderella-cinderella
daniel
emma
fred
gertrude
harold
ingrid
jake
lisa
mike
nina
octavian
paula
quintus
rita
sam
tammy
ulysses
violet
william
xanthippe
yates
zoe
<---- 32 ---->
alice octavian
bob paula
cinderella-cinderella quintus
daniel rita
emma sam
fred tammy
gertrude ulysses
harold violet
ingrid william
jake xanthippe
lisa yates
mike zoe
nina
<---- 33 ---->
alice octavian
bob paula
cinderella-cinderella quintus
daniel rita
emma sam
fred tammy
gertrude ulysses
harold violet
ingrid william
jake xanthippe
lisa yates
mike zoe
nina
<---- 34 ---->
alice octavian
bob paula
cinderella-cinderella quintus
daniel rita
emma sam
fred tammy
gertrude ulysses
harold violet
ingrid william
jake xanthippe
lisa yates
mike zoe
nina
<---- 35 ---->
alice octavian
bob paula
cinderella-cinderella quintus
daniel rita
emma sam
fred tammy
gertrude ulysses
harold violet
ingrid william
jake xanthippe
lisa yates
mike zoe
nina
<---- 36 ---->
alice octavian
bob paula
cinderella-cinderella quintus
daniel rita
emma sam
fred tammy
gertrude ulysses
harold violet
ingrid william
jake xanthippe
lisa yates
mike zoe
nina
<---- 37 ---->
alice octavian
bob paula
cinderella-cinderella quintus
daniel rita
emma sam
fred tammy
gertrude ulysses
harold violet
ingrid william
jake xanthippe
lisa yates
mike zoe
nina
<---- 38 ---->
alice octavian
bob paula
cinderella-cinderella quintus
daniel rita
emma sam
fred tammy
gertrude ulysses
harold violet
ingrid william
jake xanthippe
lisa yates
mike zoe
nina
<---- 39 ---->
alice octavian
bob paula
cinderella-cinderella quintus
daniel rita
emma sam
fred tammy
gertrude ulysses
harold violet
ingrid william
jake xanthippe
lisa yates
mike zoe
nina
<---- 40 ---->
alice octavian
bob paula
cinderella-cinderella quintus
daniel rita
emma sam
fred tammy
gertrude ulysses
harold violet
ingrid william
jake xanthippe
lisa yates
mike zoe
nina
<---- 41 ---->
alice octavian
bob paula
cinderella-cinderella quintus
daniel rita
emma sam
fred tammy
gertrude ulysses
harold violet
ingrid william
jake xanthippe
lisa yates
mike zoe
nina
<---- 42 ---->
alice jake tammy
bob lisa ulysses
cinderella-cinderella mike violet
daniel nina william
emma octavian xanthippe
fred paula yates
gertrude quintus zoe
harold rita
ingrid sam
<---- 43 ---->
alice jake tammy
bob lisa ulysses
cinderella-cinderella mike violet
daniel nina william
emma octavian xanthippe
fred paula yates
gertrude quintus zoe
harold rita
ingrid sam
<---- 44 ---->
alice jake tammy
bob lisa ulysses
cinderella-cinderella mike violet
daniel nina william
emma octavian xanthippe
fred paula yates
gertrude quintus zoe
harold rita
ingrid sam
<---- 45 ---->
alice jake tammy
bob lisa ulysses
cinderella-cinderella mike violet
daniel nina william
emma octavian xanthippe
fred paula yates
gertrude quintus zoe
harold rita
ingrid sam
<---- 46 ---->
alice jake tammy
bob lisa ulysses
cinderella-cinderella mike violet
daniel nina william
emma octavian xanthippe
fred paula yates
gertrude quintus zoe
harold rita
ingrid sam
<---- 47 ---->
alice jake tammy
bob lisa ulysses
cinderella-cinderella mike violet
daniel nina william
emma octavian xanthippe
fred paula yates
gertrude quintus zoe
harold rita
ingrid sam
<---- 48 ---->
alice jake tammy
bob lisa ulysses
cinderella-cinderella mike violet
daniel nina william
emma octavian xanthippe
fred paula yates
gertrude quintus zoe
harold rita
ingrid sam
<---- 49 ---->
alice jake tammy
bob lisa ulysses
cinderella-cinderella mike violet
daniel nina william
emma octavian xanthippe
fred paula yates
gertrude quintus zoe
harold rita
ingrid sam
<---- 50 ---->
alice jake tammy
bob lisa ulysses
cinderella-cinderella mike violet
daniel nina william
emma octavian xanthippe
fred paula yates
gertrude quintus zoe
harold rita
ingrid sam
<---- 51 ---->
alice harold paula william
bob ingrid quintus xanthippe
cinderella-cinderella jake rita yates
daniel lisa sam zoe
emma mike tammy
fred nina ulysses
gertrude octavian violet
<---- 52 ---->
alice harold paula william
bob ingrid quintus xanthippe
cinderella-cinderella jake rita yates
daniel lisa sam zoe
emma mike tammy
fred nina ulysses
gertrude octavian violet
<---- 53 ---->
alice harold paula william
bob ingrid quintus xanthippe
cinderella-cinderella jake rita yates
daniel lisa sam zoe
emma mike tammy
fred nina ulysses
gertrude octavian violet
<---- 54 ---->
alice harold paula william
bob ingrid quintus xanthippe
cinderella-cinderella jake rita yates
daniel lisa sam zoe
emma mike tammy
fred nina ulysses
gertrude octavian violet
<---- 55 ---->
alice harold paula william
bob ingrid quintus xanthippe
cinderella-cinderella jake rita yates
daniel lisa sam zoe
emma mike tammy
fred nina ulysses
gertrude octavian violet
<---- 56 ---->
alice harold paula william
bob ingrid quintus xanthippe
cinderella-cinderella jake rita yates
daniel lisa sam zoe
emma mike tammy
fred nina ulysses
gertrude octavian violet
<---- 57 ---->
alice harold paula william
bob ingrid quintus xanthippe
cinderella-cinderella jake rita yates
daniel lisa sam zoe
emma mike tammy
fred nina ulysses
gertrude octavian violet
<---- 58 ---->
alice harold paula william
bob ingrid quintus xanthippe
cinderella-cinderella jake rita yates
daniel lisa sam zoe
emma mike tammy
fred nina ulysses
gertrude octavian violet
<---- 59 ---->
alice harold paula william
bob ingrid quintus xanthippe
cinderella-cinderella jake rita yates
daniel lisa sam zoe
emma mike tammy
fred nina ulysses
gertrude octavian violet
<---- 60 ---->
alice harold paula william
bob ingrid quintus xanthippe
cinderella-cinderella jake rita yates
daniel lisa sam zoe
emma mike tammy
fred nina ulysses
gertrude octavian violet
<---- 61 ---->
alice fred lisa quintus violet
bob gertrude mike rita william
cinderella-cinderella harold nina sam xanthippe
daniel ingrid octavian tammy yates
emma jake paula ulysses zoe
<---- 62 ---->
alice fred lisa quintus violet
bob gertrude mike rita william
cinderella-cinderella harold nina sam xanthippe
daniel ingrid octavian tammy yates
emma jake paula ulysses zoe
<---- 63 ---->
alice fred lisa quintus violet
bob gertrude mike rita william
cinderella-cinderella harold nina sam xanthippe
daniel ingrid octavian tammy yates
emma jake paula ulysses zoe
<---- 64 ---->
alice fred lisa quintus violet
bob gertrude mike rita william
cinderella-cinderella harold nina sam xanthippe
daniel ingrid octavian tammy yates
emma jake paula ulysses zoe
<---- 65 ---->
alice fred lisa quintus violet
bob gertrude mike rita william
cinderella-cinderella harold nina sam xanthippe
daniel ingrid octavian tammy yates
emma jake paula ulysses zoe
<---- 66 ---->
alice fred lisa quintus violet
bob gertrude mike rita william
cinderella-cinderella harold nina sam xanthippe
daniel ingrid octavian tammy yates
emma jake paula ulysses zoe
<---- 67 ---->
alice fred lisa quintus violet
bob gertrude mike rita william
cinderella-cinderella harold nina sam xanthippe
daniel ingrid octavian tammy yates
emma jake paula ulysses zoe
<---- 68 ---->
alice fred lisa quintus violet
bob gertrude mike rita william
cinderella-cinderella harold nina sam xanthippe
daniel ingrid octavian tammy yates
emma jake paula ulysses zoe
<---- 69 ---->
alice fred lisa quintus violet
bob gertrude mike rita william
cinderella-cinderella harold nina sam xanthippe
daniel ingrid octavian tammy yates
emma jake paula ulysses zoe
<---- 70 ---->
alice fred lisa quintus violet
bob gertrude mike rita william
cinderella-cinderella harold nina sam xanthippe
daniel ingrid octavian tammy yates
emma jake paula ulysses zoe
<---- 71 ---->
alice fred lisa quintus violet
bob gertrude mike rita william
cinderella-cinderella harold nina sam xanthippe
daniel ingrid octavian tammy yates
emma jake paula ulysses zoe
<---- 72 ---->
alice fred lisa quintus violet
bob gertrude mike rita william
cinderella-cinderella harold nina sam xanthippe
daniel ingrid octavian tammy yates
emma jake paula ulysses zoe
<---- 73 ---->
alice fred lisa quintus violet
bob gertrude mike rita william
cinderella-cinderella harold nina sam xanthippe
daniel ingrid octavian tammy yates
emma jake paula ulysses zoe
<---- 74 ---->
alice emma ingrid nina rita violet zoe
bob fred jake octavian sam william
cinderella-cinderella gertrude lisa paula tammy xanthippe
daniel harold mike quintus ulysses yates
<---- 75 ---->
alice emma ingrid nina rita violet zoe
bob fred jake octavian sam william
cinderella-cinderella gertrude lisa paula tammy xanthippe
daniel harold mike quintus ulysses yates
<---- 76 ---->
alice emma ingrid nina rita violet zoe
bob fred jake octavian sam william
cinderella-cinderella gertrude lisa paula tammy xanthippe
daniel harold mike quintus ulysses yates
<---- 77 ---->
alice emma ingrid nina rita violet zoe
bob fred jake octavian sam william
cinderella-cinderella gertrude lisa paula tammy xanthippe
daniel harold mike quintus ulysses yates
<---- 78 ---->
alice emma ingrid nina rita violet zoe
bob fred jake octavian sam william
cinderella-cinderella gertrude lisa paula tammy xanthippe
daniel harold mike quintus ulysses yates
<---- 79 ---->
alice emma ingrid nina rita violet zoe
bob fred jake octavian sam william
cinderella-cinderella gertrude lisa paula tammy xanthippe
daniel harold mike quintus ulysses yates
<---- 80 ---->
alice emma ingrid nina rita violet zoe
bob fred jake octavian sam william
cinderella-cinderella gertrude lisa paula tammy xanthippe
daniel harold mike quintus ulysses yates
}
do_test 1.0 {
db eval {
CREATE TABLE t1(x);
INSERT INTO t1(x) VALUES
('alice'),
('bob'),
('cinderella-cinderella'),
('daniel'),
('emma'),
('fred'),
('gertrude'),
('harold'),
('ingrid'),
('jake'),
('lisa'),
('mike'),
('nina'),
('octavian'),
('paula'),
('quintus'),
('rita'),
('sam'),
('tammy'),
('ulysses'),
('violet'),
('william'),
('xanthippe'),
('yates'),
('zoe');
}
set res \n
for {set i 22} {$i<=80} {incr i} {
set sp [expr {$i-13}]
append res [format "<----%*s%3d%*s---->\n" \
[expr {$sp/2}] {} $i [expr {$sp-$sp/2}] {}]
append res [db format -style column -title off \
-screenwidth $i -splitcolumn on \
{SELECT x FROM t1 ORDER BY x ASC}]
}
expr {$res eq $::expected}
} {1}
+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
+90 -73
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
@@ -741,7 +731,7 @@ do_test shell1-3.26.5 {
do_test shell1-3.26.6 {
catchcmd "test.db" ".mode column\n.header off\n.width -10 10\nSELECT 'abcdefg', 123456;"
# this should be treated the same as a '1' width for col 1 and 2
} {0 { abcdefg 123456 }}
} {0 { abcdefg 123456}}
# .timer ON|OFF Turn the CPU timer measurement on or off
@@ -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}
+106 -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]
@@ -254,4 +250,83 @@ INSERT INTO t1 VALUES(7,'carriage\rreturn');
INSERT INTO t1 VALUES(8,'last line');
"
# ".mode split"
#
do_test shellA-6.1 {
db eval {
CREATE TABLE t2(x);
INSERT INTO t2(x) VALUES
('one'), ('two'), ('three'), ('four'), ('five'),
('six'), ('seven'), ('eight'), ('nine'), ('ten'),
('eleven'), ('twelve'), ('thirteen'), ('fourteen');
}
exec {*}$CLI -noinit test.db \
{.print} \
{.mode split -screenwidth 30} \
{SELECT x FROM t2}
} {
one five nine thirteen
two six ten fourteen
three seven eleven
four eight twelve}
# 3456789 123456789 123456789
do_test shellA-6.2 {
exec {*}$CLI -noinit test.db \
{.print} \
{.mode split -screenwidth 30} \
{SELECT x FROM t2} \
{.mode column -titles off} \
{SELECT x FROM t2}
} {
one five nine thirteen
two six ten fourteen
three seven eleven
four eight twelve
one
two
three
four
five
six
seven
eight
nine
ten
eleven
twelve
thirteen
fourteen}
do_test shellA-6.3 {
exec {*}$CLI -noinit test.db \
{.print} \
{.mode table} \
{.mode --once split -screenwidth 30} \
{SELECT x FROM t2} \
{SELECT x FROM t2}
} {
one five nine thirteen
two six ten fourteen
three seven eleven
four eight twelve
+----------+
| x |
+----------+
| one |
| two |
| three |
| four |
| five |
| six |
| seven |
| eight |
| nine |
| ten |
| eleven |
| twelve |
| thirteen |
| fourteen |
+----------+}
finish_test
+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
+1 -1
View File
@@ -198,7 +198,7 @@ foreach {tn pragma res} {
750 {
PRAGMA fullfsync = 1;
PRAGMA synchronous = full;
} {full|dataonly 1 full 2}
} {full 3}
} {
forcedelete test.db2
+41 -21
View File
@@ -494,6 +494,7 @@ struct lemon {
char *filename; /* Name of the input file */
char *outname; /* Name of the current output file */
char *tokenprefix; /* A prefix added to token names in the .h file */
char *stackSizeLimit; /* Function to return the stack size limit */
char *reallocFunc; /* Function to use to allocate stack space */
char *freeFunc; /* Function to use to free stack space */
int nconflict; /* Number of parsing conflicts */
@@ -2638,6 +2639,9 @@ static void parseonetoken(struct pstate *psp)
}else if( strcmp(x,"default_type")==0 ){
psp->declargslot = &(psp->gp->vartype);
psp->insertLineMacro = 0;
}else if( strcmp(x,"stack_size_limit")==0 ){
psp->declargslot = &(psp->gp->stackSizeLimit);
psp->insertLineMacro = 0;
}else if( strcmp(x,"realloc")==0 ){
psp->declargslot = &(psp->gp->reallocFunc);
psp->insertLineMacro = 0;
@@ -3715,7 +3719,7 @@ PRIVATE int compute_action(struct lemon *lemp, struct action *ap)
return act;
}
#define LINESIZE 1000
#define LINESIZE 10000
/* The next cluster of routines are for reading the template file
** and writing the results to the generated parser */
/* The first function transfers data from "in" to "out" until
@@ -3750,12 +3754,9 @@ PRIVATE void tplt_xfer(char *name, FILE *in, FILE *out, int *lineno)
/* Skip forward past the header of the template file to the first "%%"
*/
PRIVATE void tplt_skip_header(FILE *in, int *lineno)
{
PRIVATE void tplt_skip_header(FILE *in){
char line[LINESIZE];
while( fgets(line,LINESIZE,in) && (line[0]!='%' || line[1]!='%') ){
(*lineno)++;
}
while( fgets(line,LINESIZE,in) && (line[0]!='%' || line[1]!='%') ){}
}
/* The next function finds the template file and opens it, returning
@@ -3825,12 +3826,14 @@ PRIVATE void tplt_linedir(FILE *out, int lineno, char *filename)
filename++;
}
fprintf(out,"\"\n");
fflush(out);
}
/* Print a string to the file and keep the linenumber up to date */
PRIVATE void tplt_print(FILE *out, struct lemon *lemp, char *str, int *lineno)
{
if( str==0 ) return;
fflush(out);
while( *str ){
putc(*str,out);
if( *str=='\n' ) (*lineno)++;
@@ -3843,6 +3846,7 @@ PRIVATE void tplt_print(FILE *out, struct lemon *lemp, char *str, int *lineno)
if (!lemp->nolinenosflag) {
(*lineno)++; tplt_linedir(out,*lineno,lemp->outname);
}
fflush(out);
return;
}
@@ -4407,6 +4411,13 @@ static void writeRuleText(FILE *out, struct rule *rp){
}
}
/*
** Return true if the string is not NULL and not empty.
*/
static int notnull(const char *z){
return z && z[0];
}
/* Generate C source code for the parser */
void ReportTable(
@@ -4414,7 +4425,7 @@ void ReportTable(
int mhflag, /* Output in makeheaders format if true */
int sqlFlag /* Generate the *.sql file too */
){
FILE *out, *in, *sql;
FILE *out, *in;
int lineno;
struct state *stp;
struct action *ap;
@@ -4439,15 +4450,8 @@ void ReportTable(
in = tplt_open(lemp);
if( in==0 ) return;
out = file_open(lemp,".c","wb");
if( out==0 ){
fclose(in);
return;
}
if( sqlFlag==0 ){
sql = 0;
}else{
sql = file_open(lemp, ".sql", "wb");
if( sqlFlag ){
FILE *sql = file_open(lemp, ".sql", "wb");
if( sql==0 ){
fclose(in);
fclose(out);
@@ -4515,6 +4519,12 @@ void ReportTable(
}
}
fprintf(sql, "COMMIT;\n");
fclose(sql);
}
out = file_open(lemp,".c","wb");
if( out==0 ){
fclose(in);
return;
}
lineno = 1;
@@ -4543,7 +4553,7 @@ void ReportTable(
}
}
if( lemp->include[0]=='/' ){
tplt_skip_header(in,&lineno);
tplt_skip_header(in);
}else{
tplt_xfer(lemp->name,in,out,&lineno);
}
@@ -4563,7 +4573,7 @@ void ReportTable(
if( mhflag ){
fprintf(out,"#if INTERFACE\n"); lineno++;
}else{
fprintf(out,"#ifndef %s%s\n", prefix, lemp->symbols[1]->name);
fprintf(out,"#ifndef %s%s\n", prefix, lemp->symbols[1]->name); lineno++;
}
for(i=1; i<lemp->nterminal; i++){
fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i);
@@ -4612,25 +4622,33 @@ void ReportTable(
fprintf(out,"#define %sARG_FETCH\n",name); lineno++;
fprintf(out,"#define %sARG_STORE\n",name); lineno++;
}
fprintf(out, "#undef YYREALLOC\n"); lineno++;
if( lemp->reallocFunc ){
fprintf(out,"#define YYREALLOC %s\n", lemp->reallocFunc); lineno++;
}else{
fprintf(out,"#define YYREALLOC realloc\n"); lineno++;
}
fprintf(out, "#undef YYFREE\n"); lineno++;
if( lemp->freeFunc ){
fprintf(out,"#define YYFREE %s\n", lemp->freeFunc); lineno++;
}else{
fprintf(out,"#define YYFREE free\n"); lineno++;
}
fprintf(out, "#undef YYDYNSTACK\n"); lineno++;
if( lemp->reallocFunc && lemp->freeFunc ){
fprintf(out,"#define YYDYNSTACK 1\n"); lineno++;
}else{
fprintf(out,"#define YYDYNSTACK 0\n"); lineno++;
}
if( lemp->ctx && lemp->ctx[0] ){
fprintf(out, "#undef YYSIZELIMIT\n"); lineno++;
if( notnull(lemp->ctx) ){
i = lemonStrlen(lemp->ctx);
while( i>=1 && ISSPACE(lemp->ctx[i-1]) ) i--;
while( i>=1 && (ISALNUM(lemp->ctx[i-1]) || lemp->ctx[i-1]=='_') ) i--;
if( notnull(lemp->stackSizeLimit) ){
fprintf(out,"#define YYSIZELIMIT %s\n", lemp->stackSizeLimit); lineno++;
}
fprintf(out,"#define %sCTX(P) ((P)->%s)\n",name,&lemp->ctx[i]); lineno++;
fprintf(out,"#define %sCTX_SDECL %s;\n",name,lemp->ctx); lineno++;
fprintf(out,"#define %sCTX_PDECL ,%s\n",name,lemp->ctx); lineno++;
fprintf(out,"#define %sCTX_PARAM ,%s\n",name,&lemp->ctx[i]); lineno++;
@@ -4639,6 +4657,7 @@ void ReportTable(
fprintf(out,"#define %sCTX_STORE yypParser->%s=%s;\n",
name,&lemp->ctx[i],&lemp->ctx[i]); lineno++;
}else{
fprintf(out,"#define %sCTX(P) 0\n",name); lineno++;
fprintf(out,"#define %sCTX_SDECL\n",name); lineno++;
fprintf(out,"#define %sCTX_PDECL\n",name); lineno++;
fprintf(out,"#define %sCTX_PARAM\n",name); lineno++;
@@ -4648,10 +4667,13 @@ void ReportTable(
if( mhflag ){
fprintf(out,"#endif\n"); lineno++;
}
fprintf(out, "#undef YYERRORSYMBOL\n"); lineno++;
fprintf(out, "#undef YYERRSYMDT\n"); lineno++;
if( lemp->errsym && lemp->errsym->useCnt ){
fprintf(out,"#define YYERRORSYMBOL %d\n",lemp->errsym->index); lineno++;
fprintf(out,"#define YYERRSYMDT yy%d\n",lemp->errsym->dtnum); lineno++;
}
fprintf(out,"#undef YYFALLBACK\n"); lineno++;
if( lemp->has_fallback ){
fprintf(out,"#define YYFALLBACK 1\n"); lineno++;
}
@@ -5003,7 +5025,6 @@ void ReportTable(
sp2->destLineno = -1; /* Avoid emitting this destructor again */
}
}
emit_destructor_code(out,lemp->symbols[i],lemp,&lineno);
fprintf(out," break;\n"); lineno++;
}
@@ -5103,7 +5124,6 @@ void ReportTable(
acttab_free(pActtab);
fclose(in);
fclose(out);
if( sql ) fclose(sql);
return;
}
+14 -3
View File
@@ -299,15 +299,24 @@ static int yyGrowStack(yyParser *p){
int newSize;
int idx;
yyStackEntry *pNew;
#ifdef YYSIZELIMIT
int nLimit = YYSIZELIMIT(ParseCTX(p));
#endif
newSize = oldSize*2 + 100;
#ifdef YYSIZELIMIT
if( newSize>nLimit ){
newSize = nLimit;
if( newSize<=oldSize ) return 1;
}
#endif
idx = (int)(p->yytos - p->yystack);
if( p->yystack==p->yystk0 ){
pNew = YYREALLOC(0, newSize*sizeof(pNew[0]));
pNew = YYREALLOC(0, newSize*sizeof(pNew[0]), ParseCTX(p));
if( pNew==0 ) return 1;
memcpy(pNew, p->yystack, oldSize*sizeof(pNew[0]));
}else{
pNew = YYREALLOC(p->yystack, newSize*sizeof(pNew[0]));
pNew = YYREALLOC(p->yystack, newSize*sizeof(pNew[0]), ParseCTX(p));
if( pNew==0 ) return 1;
}
p->yystack = pNew;
@@ -459,7 +468,9 @@ void ParseFinalize(void *p){
}
#if YYGROWABLESTACK
if( pParser->yystack!=pParser->yystk0 ) YYFREE(pParser->yystack);
if( pParser->yystack!=pParser->yystk0 ){
YYFREE(pParser->yystack, ParseCTX(pParser));
}
#endif
}
+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
+95 -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,14 @@ 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
+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;