Merge latest trunk changes into this branch.

FossilOrigin-Name: 230fd5533e07ec36f1938f34153c9ef97a7acd6604d023b56dfc5269ca6173c2
This commit is contained in:
dan committed 2025-12-01 17:22:35 +00:00
commit 928276043b
61 files changed
+5379 -1344

No files matched your search

+11
View File
@@ -2664,6 +2664,17 @@ devtest: srctree-check sourcetest
mdevtest:
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl mdevtest
# Show all errors from the most reason testrunner invocation
#
errors:
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl errors
# Show the status of the current testrunner invocation,
# updated every couple of seconds
#
status:
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl status -d 2
# Validate that various generated files in the source tree
# are up-to-date.
#
+47 -48
View File
@@ -1,8 +1,9 @@
<h1 align="center">SQLite Source Repository</h1>
This repository contains the complete source code for the
[SQLite database engine](https://sqlite.org/), including
many tests. Additional tests and most documentation
[SQLite database engine](https://sqlite.org/) going back
to 2000-05-29. The tree includes many tests and some
documentation, though additional tests and most documentation
are managed separately.
See the [on-line documentation](https://sqlite.org/) for more information
@@ -99,16 +100,16 @@ script found at the root of the source tree. Then run "make".
For example:
apt install gcc make tcl-dev ;# Make sure you have all the necessary build tools
apt install gcc make tcl-dev ;# Install the necessary build tools
tar xzf sqlite.tar.gz ;# Unpack the source tree into "sqlite"
mkdir bld ;# Build will occur in a sibling directory
mkdir bld ;# Build happens in a sibling directory
cd bld ;# Change to the build directory
../sqlite/configure ;# Run the configure script
make sqlite3 ;# Builds the "sqlite3" command-line tool
make sqlite3.c ;# Build the "amalgamation" source file
make sqldiff ;# Builds the "sqldiff" command-line tool
# Makefile targets below this point require tcl-dev
make tclextension-install ;# Build and install the SQLite TCL extension
make sqlite3 ;# The "sqlite3" command-line tool
make sqlite3.c ;# The "amalgamation" source file
make sqldiff ;# The "sqldiff" command-line tool
#### Targets below require tcl-dev ####
make tclextension-install ;# Install the SQLite TCL extension
make devtest ;# Run development tests
make releasetest ;# Run full release tests
make sqlite3_analyzer ;# Builds the "sqlite3_analyzer" tool
@@ -116,14 +117,15 @@ For example:
See the makefile for additional targets. For debugging builds, the
core developers typically run "configure" with options like this:
../sqlite/configure --enable-all --enable-debug CFLAGS='-O0 -g'
../sqlite/configure --all --debug CFLAGS='-O0 -g'
For release builds, the core developers usually do:
../sqlite/configure --enable-all
../sqlite/configure --all
Almost all makefile targets require a "tclsh" TCL interpreter version 8.6 or
later. The "tclextension-install" target and the test targets that follow
Core deliverables (sqlite3.c, sqlite3) can be built without a TCL, but
many makefile targets require a "tclsh" TCL interpreter version 8.6
or later. The "tclextension-install" target and the test targets that follow
all require TCL development libraries too. ("apt install tcl-dev"). It is
helpful, but is not required, to install the SQLite TCL extension (the
"tclextension-install" target) prior to running tests. The "releasetest"
@@ -133,20 +135,20 @@ On "make" command-lines, one can add "OPTIONS=..." to specify additional
compile-time options over and above those set by ./configure. For example,
to compile with the SQLITE_OMIT_DEPRECATED compile-time option, one could say:
./configure --enable-all
./configure --all
make OPTIONS=-DSQLITE_OMIT_DEPRECATED sqlite3
The configure script uses autoconf 2.61 and libtool. If the configure
script does not work out for you, there is a generic makefile named
"Makefile.linux-gcc" in the top directory of the source tree that you
can copy and edit to suit your needs. Comments on the generic makefile
show what changes are needed.
The configure script uses [autosetup](https://msteveb.github.io/autosetup/).
If the configure script does not work out for you, there is a generic
makefile named "Makefile.linux-gcc" in the top directory of the source tree
that you can copy and edit to suit your needs. Comments on the generic
makefile show what changes are needed.
## Compiling for Windows Using MSVC
On Windows, everything can be compiled with MSVC.
You will also need a working installation of TCL if you want to run tests.
TCL is not required if you just want to build SQLite itself.
You will also need a working installation of TCL if you want to run tests,
though TCL is not required if you just want to build SQLite itself.
See the [compile-for-windows.md](doc/compile-for-windows.md) document for
additional information about how to install MSVC and TCL and configure your
build environment.
@@ -156,24 +158,25 @@ TCL library, using a command like this:
set TCLDIR=c:\Tcl
SQLite uses "tclsh.exe" as part of the build process, and so that
program will need to be somewhere on your %PATH%. SQLite itself
does not contain any TCL code, but it does use TCL to run tests.
You may need to install TCL development
libraries in order to successfully complete some makefile targets.
It is helpful, but is not required, to install the SQLite TCL extension
(the "tclextension-install" target) prior to running tests.
SQLite itself does not contain any TCL code, but it does use TCL to run
tests. You may need to install TCL development libraries in order to
successfully complete some makefile targets. It is helpful, but is not
required, to install the SQLite TCL extension (the "tclextension-install"
target) prior to running tests.
Build using Makefile.msc. Example:
The source tree contains a "make.bat" file that allows the same "make"
commands of Unix to work on Windows. In the following, you can substitute
"nmake /f Makefile.msc" in place of "make", if you prefer to avoid this BAT
file:
nmake /f Makefile.msc sqlite3.exe
nmake /f Makefile.msc sqlite3.c
nmake /f Makefile.msc sqldiff.exe
# Makefile targets below this point require TCL development libraries
nmake /f Makefile.msc tclextension-install
nmake /f Makefile.msc devtest
nmake /f Makefile.msc releasetest
nmake /f Makefile.msc sqlite3_analyzer.exe
make sqlite3.exe
make sqlite3.c
make sqldiff.exe
#### Targets below require TCL development libraries ####
make tclextension-install
make devtest
make releasetest
make sqlite3_analyzer.exe
There are many other makefile targets. See comments in Makefile.msc for
details.
@@ -181,7 +184,7 @@ details.
As with the unix Makefile, the OPTIONS=... argument can be passed on the nmake
command-line to enable new compile-time options. For example:
nmake /f Makefile.msc OPTIONS=-DSQLITE_OMIT_DEPRECATED sqlite3.exe
make OPTIONS=-DSQLITE_OMIT_DEPRECATED sqlite3.exe
## Source Tree Map
@@ -194,8 +197,7 @@ command-line to enable new compile-time options. For example:
* **test/** - This directory and its subdirectories contains code used
for testing. Files that end in "`.test`" are TCL scripts that run
tests using an augmented TCL interpreter named "testfixture". Use
a command like "`make testfixture`" (unix) or
"`nmake /f Makefile.msc testfixture.exe`" (windows) to build that
a command like "`make testfixture`" to build that
augmented TCL interpreter, then run individual tests using commands like
"`testfixture test/main.test`". This test/ subdirectory also contains
additional C code modules and scripts for other kinds of testing.
@@ -377,10 +379,11 @@ implementation. It will not be the easiest library in the world to hack.
(and some other test programs too) is built and run when you type
"make test".
* **VERSION**, **manifest**, and **manifest.uuid** - These files define
the current SQLite version number. The "VERSION" file is human generated,
but the "manifest" and "manifest.uuid" files are automatically generated
by the [Fossil version control system](https://fossil-scm.org/).
* **VERSION**, **manifest**, **manifest.tags**, and **manifest.uuid** -
These files define the current SQLite version number. The "VERSION" file
is human generated, but the "manifest", "manifest.tags", and
"manifest.uuid" files are automatically generated by the
[Fossil version control system](https://fossil-scm.org/).
There are many other source files. Each has a succinct header comment that
describes its purpose and role within the larger system.
@@ -406,10 +409,6 @@ makefile:
> make verify-source
Or on windows:
> nmake /f Makefile.msc verify-source
Using the makefile to verify source integrity is good for detecting
accidental changes to the source tree, but malicious changes could be
hidden by also modifying the makefiles.
+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)
+10
View File
@@ -375,6 +375,11 @@ configure process, and check it in.
Patching Autosetup for Project-local Changes
------------------------------------------------------------------------
The autosetup files require the following patches after updating
from their upstream sources:
### `--debug` flag
Autosetup reserves the flag name **`--debug`** for its own purposes,
and its own special handling of `--enable-...` flags makes `--debug`
an alias for `--enable-debug`. As this project has a long history of
@@ -388,6 +393,11 @@ If autosetup is upgraded and this patch is _not_ applied the invoking
`./configure` will fail loudly because of the declaration of the
`debug` flag in `auto.def` - duplicated flags are not permitted.
### Fail on `malloc()` error
See [check-in 72c8a5b94cdf5d](/info/72c8a5b94cdf5d).
<a name="branch-customization"></a>
Branch-specific Customization
========================================================================
+6 -4
View File
@@ -7409,11 +7409,13 @@ void *JimDefaultAllocator(void *ptr, size_t size)
free(ptr);
return NULL;
}
else if (ptr) {
return realloc(ptr, size);
}
else {
return malloc(size);
void *p = realloc(ptr, size);
if( p==0 ){
fprintf(stderr,"Out of memory\n");
exit(1);
}
return p;
}
}
+8 -5
View File
@@ -98,9 +98,9 @@ typedef struct SegmentWriter SegmentWriter;
** incrementally. See function fts3PendingListAppend() for details.
*/
struct PendingList {
int nData;
sqlite3_int64 nData;
char *aData;
int nSpace;
sqlite3_int64 nSpace;
sqlite3_int64 iLastDocid;
sqlite3_int64 iLastCol;
sqlite3_int64 iLastPos;
@@ -760,7 +760,8 @@ static int fts3PendingTermsAddOne(
pList = (PendingList *)fts3HashFind(pHash, zToken, nToken);
if( pList ){
p->nPendingData -= (pList->nData + nToken + sizeof(Fts3HashElem));
assert( pList->nData+nToken+sizeof(Fts3HashElem) <= (i64)p->nPendingData );
p->nPendingData -= (int)(pList->nData + nToken + sizeof(Fts3HashElem));
}
if( fts3PendingListAppend(&pList, p->iPrevDocid, iCol, iPos, &rc) ){
if( pList==fts3HashInsert(pHash, zToken, nToken, pList) ){
@@ -773,7 +774,9 @@ static int fts3PendingTermsAddOne(
}
}
if( rc==SQLITE_OK ){
p->nPendingData += (pList->nData + nToken + sizeof(Fts3HashElem));
assert( (i64)p->nPendingData + pList->nData + nToken
+ sizeof(Fts3HashElem) <= 0x3fffffff );
p->nPendingData += (int)(pList->nData + nToken + sizeof(Fts3HashElem));
}
return rc;
}
@@ -5457,7 +5460,7 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
v = atoi(&zVal[9]);
if( v>=24 && v<=p->nPgsz-35 ) p->nNodeSize = v;
rc = SQLITE_OK;
}else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){
}else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 11) ){
v = atoi(&zVal[11]);
if( v>=64 && v<=FTS3_MAX_PENDING_DATA ) p->nMaxPendingData = v;
rc = SQLITE_OK;
+1
View File
@@ -2074,6 +2074,7 @@ static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){
while( 1 ){
u64 iDelta = 0;
if( i>=n ) break;
if( eDetail==FTS5_DETAIL_NONE ){
/* todo */
if( i<n && a[i]==0 ){
+11
View File
@@ -396,7 +396,12 @@ static int fts5VocabOpenMethod(
return rc;
}
/*
** Restore cursor pCsr to the state it was in immediately after being
** created by the xOpen() method.
*/
static void fts5VocabResetCursor(Fts5VocabCursor *pCsr){
int nCol = pCsr->pFts5->pConfig->nCol;
pCsr->rowid = 0;
sqlite3Fts5IterClose(pCsr->pIter);
sqlite3Fts5StructureRelease(pCsr->pStruct);
@@ -406,6 +411,12 @@ static void fts5VocabResetCursor(Fts5VocabCursor *pCsr){
pCsr->nLeTerm = -1;
pCsr->zLeTerm = 0;
pCsr->bEof = 0;
pCsr->iCol = 0;
pCsr->iInstPos = 0;
pCsr->iInstOff = 0;
pCsr->colUsed = 0;
memset(pCsr->aCnt, 0, sizeof(i64)*nCol);
memset(pCsr->aDoc, 0, sizeof(i64)*nCol);
}
/*
+362
View File
@@ -16124,5 +16124,367 @@ do_catchsql_test 83.1 {
SELECT * FROM t1('R*R*R*R*R*R*R*R*') WHERE (a,b)<=(current_date,0 BETWEEN 'a'<>11 AND '') ORDER BY rowid DESC;
} {/.*fts5: corruption found/}
#-------------------------------------------------------------------------
reset_db
do_test 84.0 {
sqlite3 db {}
db deserialize [decode_hexdb {
.open --hexdb
| size 53248 pagesize 4096 filename c1a.txt.db
| page 1 offset 0
| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
| 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 0d .....@ ........
| 32: 00 00 00 02 00 00 00 01 00 00 00 09 00 00 00 04 ................
| 48: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 ................
| 96: 00 00 00 00 0d 0f c7 00 07 0d 92 00 0f 8d 0f 36 ...............6
| 112: 0e cb 0e 6b 0e 0e 0d b6 0d 92 0d 92 00 00 00 00 ...k............
| 3472: 00 00 22 08 06 17 11 11 01 31 74 61 62 6c 65 74 .........1tablet
| 3488: 32 74 32 0d 43 52 45 41 54 45 20 54 41 42 4c 45 2t2.CREATE TABLE
| 3504: 20 74 32 28 78 29 56 07 06 17 1f 1f 01 7d 74 61 t2(x)V.......ta
| 3520: 62 6c 65 74 31 5f 63 6f 6e 66 69 67 74 31 5f 63 blet1_configt1_c
| 3536: 6f 6e 66 69 67 07 43 52 45 41 54 45 20 54 41 42 onfig.CREATE TAB
| 3552: 4c 45 20 27 74 31 5f 63 6f 6e 66 69 67 27 28 6b LE 't1_config'(k
| 3568: 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 76 29 PRIMARY KEY, v)
| 3584: 20 57 49 54 48 4f 55 54 20 52 4f 57 49 44 5b 06 WITHOUT ROWID[.
| 3600: 07 17 21 21 01 81 01 74 61 62 6c 65 74 31 5f 64 ..!!...tablet1_d
| 3616: 6f 63 73 69 7a 65 74 31 5f 64 6f 63 73 69 7a 65 ocsizet1_docsize
| 3632: 06 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 74 .CREATE TABLE 't
| 3648: 31 5f 64 6f 63 73 69 7a 65 27 28 69 64 20 49 4e 1_docsize'(id IN
| 3664: 54 45 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 TEGER PRIMARY KE
| 3680: 59 2c 20 73 7a 20 42 4c 4f 42 29 5e 05 07 17 21 Y, sz BLOB)^...!
| 3696: 21 01 81 07 74 61 62 6c 65 74 31 5f 63 6f 6e 74 !...tablet1_cont
| 3712: 65 6e 74 74 31 5f 63 6f 6e 74 65 6e 74 05 43 52 entt1_content.CR
| 3728: 45 41 54 45 20 54 41 42 4c 45 20 27 74 31 5f 63 EATE TABLE 't1_c
| 3744: 6f 6e 74 65 6e 74 27 28 69 64 20 49 4e 54 45 47 ontent'(id INTEG
| 3760: 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 ER PRIMARY KEY,
| 3776: 63 30 2c 20 63 31 2c 20 63 32 29 69 04 07 17 19 c0, c1, c2)i....
| 3792: 19 01 81 2d 74 61 62 6c 65 74 31 5f 69 64 78 74 ...-tablet1_idxt
| 3808: 31 5f 69 64 78 04 43 52 45 41 54 45 20 54 41 42 1_idx.CREATE TAB
| 3824: 4c 45 20 27 74 31 5f 69 64 78 27 28 73 65 67 69 LE 't1_idx'(segi
| 3840: 64 2c 20 74 65 72 6d 2c 20 70 67 6e 6f 2c 20 50 d, term, pgno, P
| 3856: 52 49 4d 41 52 59 20 4b 45 59 28 73 65 67 69 64 RIMARY KEY(segid
| 3872: 2c 20 74 65 72 6d 29 29 20 57 49 54 48 4f 55 54 , term)) WITHOUT
| 3888: 20 52 4f 57 49 44 55 03 07 17 1b 1b 01 81 01 74 ROWIDU........t
| 3904: 61 62 6c 65 74 31 5f 64 61 74 61 74 31 5f 64 61 ablet1_datat1_da
| 3920: 74 61 03 43 52 45 41 54 45 20 54 41 42 4c 45 20 ta.CREATE TABLE
| 3936: 27 74 31 5f 64 61 74 61 27 28 69 64 20 49 4e 54 't1_data'(id INT
| 3952: 45 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 EGER PRIMARY KEY
| 3968: 2c 20 62 6c 6f 63 6b 20 42 4c 4f 42 29 38 02 06 , block BLOB)8..
| 3984: 17 11 11 08 5f 74 61 62 6c 65 74 31 74 31 43 52 ...._tablet1t1CR
| 4000: 45 41 54 45 20 56 49 52 54 55 41 4c 20 54 41 42 EATE VIRTUAL TAB
| 4016: 4c 45 20 74 31 20 55 53 49 4e 47 20 66 74 73 35 LE t1 USING fts5
| 4032: 28 61 2c 62 2c 63 29 00 00 00 39 00 00 00 00 00 (a,b,c)...9.....
| page 3 offset 8192
| 0: 05 00 00 00 02 0f f1 00 00 00 00 0c 0f fb 0f f1 ................
| 4064: 00 00 0b 01 03 00 1c 81 3a 84 5e 81 3a 81 3a 0a ........:.^.:.:.
| 4080: 0a 00 00 00 0b 84 80 80 80 80 01 00 00 00 0a 0a ................
| page 4 offset 12288
| 0: 0a 00 00 00 01 0f fa 00 0f fa 00 00 00 00 00 00 ................
| 4080: 00 00 00 00 00 00 00 00 00 00 05 04 09 0c 01 02 ................
| page 7 offset 24576
| 0: 0a 00 00 00 01 0f f4 00 0f f4 00 00 00 00 00 00 ................
| 4080: 00 00 00 00 0b 03 1b 01 76 65 72 73 69 6f 6e 04 ........version.
| page 10 offset 36864
| 0: 0d 00 00 00 02 0f e2 00 0f e2 0f ef 00 00 00 00 ................
| 4064: 00 00 0b 01 03 00 1c 81 3a 84 5e 81 3a 81 3a 0f ........:.^.:.:.
| 4080: 0a 03 00 24 00 00 00 00 01 01 02 00 01 01 01 09 ...$............
| page 11 offset 40960
| 0: 0d 00 00 00 01 00 22 00 00 22 00 00 00 00 00 00 ................
| 32: 00 00 9f 56 84 80 80 80 80 01 04 00 bf 30 00 00 ...V.........0..
| 48: 0f 58 02 30 30 19 02 05 01 02 05 01 02 05 16 02 .X.00...........
| 64: 05 01 02 05 01 02 05 61 02 05 01 02 05 01 02 05 .......a........
| 80: 13 02 05 01 02 05 01 02 05 0d 02 03 01 02 03 01 ................
| 96: 02 03 02 09 78 66 66 66 66 66 66 66 65 81 17 02 ....xfffffffe...
| 112: 05 01 02 05 01 02 05 01 01 31 04 02 04 01 02 04 .........1......
| 128: 01 02 04 01 02 05 01 02 05 01 02 05 0d 02 06 01 ................
| 144: 02 06 01 02 06 02 01 30 79 02 04 01 02 04 01 02 .......0y.......
| 160: 04 03 02 30 30 2b 02 05 01 02 05 01 02 05 58 02 ...00+........X.
| 176: 05 01 02 05 01 02 05 01 02 05 01 02 05 01 02 05 ................
| 192: 16 02 05 01 02 05 01 02 05 05 06 30 30 30 30 30 ...........00000
| 208: 30 81 0b 02 04 01 02 04 01 02 04 10 02 05 01 02 0...............
| 224: 05 01 02 05 03 02 32 34 76 02 05 01 02 05 01 02 ......24v.......
| 240: 05 02 01 38 07 02 04 01 02 04 01 02 04 01 01 32 ...8...........2
| 256: 28 02 04 01 02 04 01 02 04 04 02 05 01 02 05 01 (...............
| 272: 02 05 02 01 30 1f 02 05 01 02 05 01 02 05 03 02 ....0...........
| 288: 30 30 10 02 05 01 02 05 01 02 05 6a 02 04 01 02 00.........j....
| 304: 04 01 02 04 02 08 35 30 30 30 30 30 30 30 81 26 ......50000000.&
| 320: 02 05 01 02 05 01 02 05 01 01 33 07 02 06 01 02 ..........3.....
| 336: 06 01 02 06 81 2c 02 04 01 02 04 01 02 04 02 04 .....,..........
| 352: 32 37 36 36 81 23 02 05 01 02 05 01 02 05 01 01 2766.#..........
| 368: 34 13 02 05 01 02 05 01 02 05 02 03 30 39 36 1c 4...........096.
| 384: 02 05 01 02 05 01 02 05 07 02 05 01 02 05 01 02 ................
| 400: 05 01 03 35 30 30 7f 02 05 01 02 05 01 02 05 04 ...500..........
| 416: 02 30 30 81 0e 02 06 01 02 06 01 02 06 06 03 30 .00............0
| 432: 30 30 81 11 02 04 01 02 04 01 02 04 01 05 36 35 00............65
| 448: 35 33 36 81 1a 02 05 01 02 05 01 02 05 01 04 38 536............8
| 464: 31 39 32 81 02 02 06 01 02 06 01 02 06 01 05 61 192............a
| 480: 6c 6c 6f 77 01 02 02 01 02 02 01 02 02 02 02 72 llow...........r
| 496: 67 81 08 02 04 01 02 04 01 02 04 02 05 74 6f 6d g............tom
| 512: 69 63 04 02 02 01 02 02 01 02 02 03 06 74 61 63 ic...........tac
| 528: 68 65 64 79 02 03 01 02 03 01 02 03 02 0d 75 74 hedy..........ut
| 544: 6f 63 68 65 63 6b 70 6f 69 6e 74 2b 02 04 01 02 ocheckpoint+....
| 560: 04 01 02 04 05 06 76 61 63 75 75 6d 0d 02 03 01 ......vacuum....
| 576: 02 03 01 02 03 01 06 62 69 6e 61 72 79 03 06 01 .......binary...
| 592: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
| 608: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
| 624: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
| 640: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
| 656: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
| 672: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
| 688: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
| 704: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
| 720: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
| 736: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
| 752: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
| 768: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
| 784: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
| 800: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
| 816: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
| 832: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
| 848: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
| 864: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
| 880: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
| 896: 01 02 02 02 07 79 74 65 63 6f 64 65 37 02 03 01 .....ytecode7...
| 912: 02 03 01 02 03 01 05 63 61 63 68 65 10 02 03 01 .......cache....
| 928: 02 03 01 01 03 02 04 6c 61 6e 67 07 02 03 01 02 .......lang.....
| 944: 03 01 02 03 02 05 6f 6c 75 6d 6e 7c 02 03 01 02 ......olumn|....
| 960: 03 01 02 03 03 06 6d 6d 65 6e 74 73 43 02 04 01 ......mmentsC...
| 976: 02 04 01 02 04 04 05 70 69 6c 65 72 07 02 02 01 .......piler....
| 992: 02 02 01 02 02 05 04 6f 75 6e 64 7f 02 03 01 02 .......ound.....
| 1008: 03 01 02 03 03 03 75 6e 74 81 17 02 04 01 02 04 ......unt.......
| 1024: 01 02 04 02 05 75 72 73 6f 72 3a 02 03 01 02 03 .....ursor:.....
| 1040: 01 02 03 01 06 64 62 70 61 67 65 3d 02 03 01 02 .....dbpage=....
| 1056: 03 01 02 03 03 04 73 74 61 74 40 02 03 01 02 03 ......stat@.....
| 1072: 01 02 03 02 04 65 62 75 67 0a 02 02 01 02 02 01 .....ebug.......
| 1088: 02 02 03 05 66 61 75 6c 74 0d 02 02 01 02 02 01 ....fault.......
| 1104: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
| 1120: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
| 1136: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
| 1152: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
| 1168: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
| 1184: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
| 1200: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 4f 02 ..............O.
| 1216: 03 01 02 03 01 02 03 03 03 70 74 68 81 05 02 04 .........pth....
| 1232: 01 02 04 01 02 04 19 02 04 01 02 04 01 02 04 02 ................
| 1248: 05 69 72 65 63 74 34 02 02 01 02 02 01 02 02 01 .irect4.........
| 1264: 06 65 6e 61 62 6c 65 37 02 02 01 02 02 01 02 02 .enable7........
| 1280: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
| 1296: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
| 1312: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
| 1328: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
| 1344: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
| 1360: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
| 1376: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
| 1392: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
| 1408: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
| 1424: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
| 1440: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
| 1456: 02 01 02 02 02 06 78 70 6c 61 69 6e 43 02 03 01 ......xplainC...
| 1472: 02 03 01 02 03 04 01 72 81 05 02 03 01 02 03 01 .......r........
| 1488: 02 03 03 07 74 65 6e 73 69 6f 6e 81 2f 02 04 01 ....tension./...
| 1504: 02 04 01 02 04 01 04 66 69 6c 65 13 02 03 01 02 .......file.....
| 1520: 03 01 02 03 02 05 6f 72 6d 61 74 13 02 04 01 02 ......ormat.....
| 1536: 04 01 02 04 02 03 74 73 33 46 02 03 01 02 03 01 ......ts3F......
| 1552: 02 03 01 02 03 01 02 03 01 02 03 04 01 34 4c 02 .............4L.
| 1568: 03 01 02 03 01 02 03 04 01 35 4f 02 03 01 02 03 .........5O.....
| 1584: 01 02 03 02 03 75 6e 63 5e 02 05 01 02 05 01 02 .....unc^.......
| 1600: 05 05 04 74 69 6f 6e 73 02 05 01 02 05 01 02 05 ...tions........
| 1616: 13 02 03 01 02 03 01 02 03 09 01 73 55 02 04 01 ...........sU...
| 1632: 02 04 01 02 04 01 07 67 65 6f 70 6f 6c 79 52 02 .......geopolyR.
| 1648: 03 01 02 03 01 02 03 01 05 68 69 6e 74 73 3a 02 .........hints:.
| 1664: 04 01 02 04 01 02 04 02 03 6f 6f 6b 61 02 04 01 .........ooka...
| 1680: 02 04 01 02 04 01 02 69 6e 01 02 04 01 02 04 01 .......in.......
| 1696: 02 04 03 04 69 74 73 7a 1f 02 04 01 02 04 01 02 ....itsz........
| 1712: 04 03 08 74 72 69 6e 73 69 63 73 04 02 03 01 02 ...trinsics.....
| 1728: 03 01 02 03 01 07 6a 6f 75 72 6e 61 6c 16 02 03 ......journal...
| 1744: 01 02 03 01 02 03 01 06 6c 65 6e 67 74 68 81 0b ........length..
| 1760: 02 03 01 02 03 01 02 03 01 02 05 01 02 05 01 02 ................
| 1776: 05 0d 02 04 01 02 04 01 02 04 02 03 69 6b 65 81 ............ike.
| 1792: 0e 02 03 01 02 03 01 02 03 03 03 6d 69 74 16 02 ...........mit..
| 1808: 05 01 02 05 01 02 05 5e 02 04 01 02 04 01 02 04 .......^........
| 1824: 02 03 6f 61 64 81 2f 02 03 01 02 03 01 02 03 01 ..oad./.........
| 1840: 06 6d 61 6c 6c 6f 63 76 02 02 01 02 02 01 02 02 .mallocv........
| 1856: 3a 02 03 01 02 03 01 02 03 03 02 74 68 55 02 03 :..........thU..
| 1872: 01 02 03 01 02 03 03 01 78 79 02 02 01 02 02 01 ........xy......
| 1888: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
| 1904: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
| 1920: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
| 1936: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
| 1952: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
| 1968: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
| 1984: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
| 2000: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
| 2016: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
| 2032: 02 02 02 05 65 6d 6f 72 79 81 11 02 03 01 02 03 ....emory.......
| 2048: 01 02 03 04 04 73 79 73 35 58 02 03 01 02 03 01 .....sys5X......
| 2064: 02 03 02 03 6d 61 70 19 02 03 01 02 03 01 02 03 ....map.........
| 2080: 79 02 03 01 02 03 01 02 03 02 04 75 74 65 78 81 y..........utex.
| 2096: 2c 02 02 01 02 02 01 02 02 01 06 6e 6f 63 61 73 ,..........nocas
| 2112: 65 02 06 01 02 02 03 06 01 02 02 03 06 01 02 02 e...............
| 2128: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
| 2144: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
| 2160: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
| 2176: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
| 2192: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
| 2208: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
| 2224: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
| 2240: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
| 2256: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
| 2272: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
| 2288: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
| 2304: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
| 2320: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
| 2336: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
| 2352: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
| 2368: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
| 2384: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
| 2400: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
| 2416: 02 02 03 06 01 02 02 03 07 72 6d 61 6c 69 7a 65 .........rmalize
| 2432: 5b 02 03 01 02 03 01 02 03 02 05 75 6d 62 65 72 [..........umber
| 2448: 81 23 02 04 01 02 04 01 02 04 01 06 6f 66 66 73 .#..........offs
| 2464: 65 74 5e 02 03 01 02 03 01 02 03 02 03 6d 69 74 et^..........mit
| 2480: 81 2c 02 03 01 02 03 01 02 03 01 02 02 01 02 02 .,..............
| 2496: 01 02 02 02 01 70 81 26 02 04 01 02 04 01 02 04 .....p.&........
| 2512: 02 07 76 65 72 66 6c 6f 77 34 02 03 01 02 03 01 ..verflow4......
| 2528: 02 03 01 04 70 61 67 65 1c 02 03 01 02 03 01 02 ....page........
| 2544: 03 64 02 04 01 02 04 01 02 04 13 02 03 01 02 03 .d..............
| 2560: 01 02 03 01 02 03 01 02 03 01 02 03 03 09 72 65 ..............re
| 2576: 6e 74 68 65 73 69 73 49 02 04 01 02 04 01 02 04 nthesisI........
| 2592: 03 05 74 74 65 72 6e 81 0e 02 04 01 02 04 01 02 ..ttern.........
| 2608: 04 02 05 63 61 63 68 65 1f 02 03 01 02 03 01 02 ...cache........
| 2624: 03 02 08 72 65 75 70 64 61 74 65 61 02 03 01 02 ...reupdatea....
| 2640: 03 01 02 03 01 04 72 65 61 64 34 02 04 01 02 04 ......read4.....
| 2656: 01 02 04 03 07 63 75 72 73 69 76 65 22 02 03 01 .....cursive....
| 2672: 02 03 01 02 03 02 04 6f 77 69 64 01 02 03 01 02 .......owid.....
| 2688: 03 01 02 03 02 04 74 72 65 65 64 02 03 01 02 03 ......treed.....
| 2704: 01 02 03 04 02 69 6d 01 06 01 02 02 03 06 01 02 .....im.........
| 2720: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
| 2736: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
| 2752: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
| 2768: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
| 2784: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
| 2800: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
| 2816: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
| 2832: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
| 2848: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
| 2864: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
| 2880: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
| 2896: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
| 2912: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
| 2928: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
| 2944: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
| 2960: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
| 2976: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
| 2992: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
| 3008: 01 02 02 03 06 01 02 02 03 06 01 02 02 01 0a 73 ...............s
| 3024: 63 61 6e 73 74 61 74 75 73 70 02 04 01 02 04 01 canstatusp......
| 3040: 02 04 02 05 65 63 74 6f 72 25 02 03 01 02 03 01 ....ector%......
| 3056: 02 03 03 04 6c 65 63 74 7f 02 04 01 02 04 01 02 ....lect........
| 3072: 04 03 05 73 73 69 6f 6e 67 02 03 01 02 03 01 02 ...ssiong.......
| 3088: 03 02 03 69 7a 65 10 02 04 01 02 04 01 02 04 04 ...ize..........
| 3104: 02 04 01 02 04 01 02 04 01 02 04 01 02 04 01 02 ................
| 3120: 04 01 02 04 01 02 04 01 02 04 07 02 04 01 02 04 ................
| 3136: 01 02 04 5b 02 05 01 02 05 01 02 05 10 02 04 01 ...[............
| 3152: 02 04 01 02 04 04 02 04 01 02 04 01 02 04 02 03 ................
| 3168: 6f 66 74 76 02 03 01 02 03 01 02 03 02 02 71 6c oftv..........ql
| 3184: 5e 02 04 01 02 04 01 02 04 13 02 04 01 02 04 01 ^...............
| 3200: 02 04 28 02 03 01 02 03 01 02 03 02 04 74 61 74 ..(..........tat
| 3216: 34 6a 02 03 01 02 03 01 02 03 03 02 6d 74 70 02 4j..........mtp.
| 3232: 03 01 02 03 01 02 03 05 04 76 74 61 62 6d 02 03 .........vtabm..
| 3248: 01 02 03 01 02 03 03 03 6f 72 65 81 35 02 03 01 ........ore.5...
| 3264: 02 03 01 02 03 02 0a 79 6e 63 68 72 6f 6e 6f 75 .......ynchronou
| 3280: 73 28 02 03 01 02 03 01 02 03 04 02 04 01 02 04 s(..............
| 3296: 01 02 04 03 04 73 74 65 6d 81 32 02 02 01 02 02 .....stem.2.....
| 3312: 01 02 02 01 04 74 65 6d 70 81 35 02 02 01 02 02 .....temp.5.....
| 3328: 01 02 02 02 06 68 72 65 61 64 73 31 02 04 01 02 .....hreads1....
| 3344: 04 01 02 04 76 02 04 01 02 04 01 02 04 08 03 61 ....v..........a
| 3360: 66 65 81 38 02 02 01 02 02 01 02 02 02 06 72 69 fe.8..........ri
| 3376: 67 67 65 72 81 20 02 03 01 02 03 01 02 03 08 01 gger. ..........
| 3392: 73 22 02 04 01 02 04 01 02 04 01 07 75 6e 6b 6e s...........unkn
| 3408: 6f 77 6e 73 02 03 01 02 03 01 02 03 01 08 76 61 owns..........va
| 3424: 72 69 61 62 6c 65 81 23 02 03 01 02 03 01 02 03 riable.#........
| 3440: 02 03 64 62 65 81 26 02 03 01 02 03 01 02 03 02 ..dbe.&.........
| 3456: 03 69 65 77 01 02 05 01 02 05 01 02 05 02 03 74 .iew...........t
| 3472: 61 62 37 02 04 01 02 04 01 02 04 04 02 04 01 02 ab7.............
| 3488: 04 01 02 04 01 02 04 01 02 04 01 02 04 01 03 77 ...............w
| 3504: 61 6c 2b 02 03 01 02 03 01 02 03 01 02 03 01 02 al+.............
| 3520: 03 01 02 03 02 05 6f 72 6b 65 72 31 02 03 01 02 ......orker1....
| 3536: 03 01 02 03 76 02 03 01 02 03 01 02 03 01 01 78 ....v..........x
| 3552: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
| 3568: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
| 3584: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
| 3600: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
| 3616: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
| 3632: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
| 3648: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
| 3664: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
| 3680: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
| 3696: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
| 3712: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
| 3728: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
| 3744: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
| 3760: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
| 3776: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
| 3792: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
| 3808: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
| 3824: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
| 3840: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
| 3856: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
| 3872: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
| 3888: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
| 3904: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
| 3920: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
| 3936: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
| 3952: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
| 3968: 06 01 01 02 01 06 04 30 15 1e 0c 28 1b 0d 0c 15 .......0...(....
| 3984: 0c 16 14 16 10 0c 17 0e 0e 0f 11 10 10 0e 10 11 ................
| 4000: 18 11 82 3e 12 10 0f 10 11 10 0f 0f 10 11 0f 0f ...>............
| 4016: 81 05 18 10 81 45 11 0d 13 0f 10 17 0c 0c 0e 18 .....E..........
| 4032: 0c 12 10 0e 0d 0f 13 12 24 0f 17 0f 1a 0d 81 1c ........$.......
| 4048: 11 0f 17 10 82 3e 12 11 11 18 0d 12 2a 14 11 10 .....>......*...
| 4064: 13 0f 12 0f 0f 82 3a 15 10 0f 10 4d 0e 1f 0f 0d ......:....M....
| 4080: 0f 0f 1e 10 10 1a 0f 12 0c 12 14 0f 0e 20 17 19 ............. ..
| page 12 offset 45056
| 0: 0d 00 00 00 01 0d f4 00 0d f4 00 00 00 00 00 00 ................
| 3568: 00 00 00 00 84 04 84 80 80 80 80 02 04 00 88 0c ................
| 3584: 00 07 02 00 01 01 02 56 06 01 01 02 01 06 01 01 .......V........
| 3600: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
| 3616: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
| 3632: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
| 3648: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
| 3664: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
| 3680: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
| 3696: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
| 3712: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
| 3728: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
| 3744: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
| 3760: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
| 3776: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
| 3792: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
| 3808: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
| 3824: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
| 3840: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
| 3856: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
| 3872: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
| 3888: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
| 3904: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
| 3920: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
| 3936: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
| 3952: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
| 3968: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
| 3984: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
| 4000: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
| 4016: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
| 4032: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
| 4048: 52 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 R...............
| 4064: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
| 4080: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
| end c1a.txt.db
}]} {}
do_catchsql_test 84.1 {
SELECT * FROM t1('R*R*x') ORDER BY rowid DESC;
} {1 {fts5: corruption found reading blob 137438953475 from table "t1"}}
sqlite3_fts5_may_be_corrupt 0
finish_test
+22
View File
@@ -305,6 +305,28 @@ do_catchsql_test 6.2 {
sqlite3_fts5_may_be_corrupt 0
#-------------------------------------------------------------------------
reset_db
do_execsql_test 7.0 {
CREATE VIRTUAL TABLE t1 USING fts5(a, b);
CREATE VIRTUAL TABLE v1 USING fts5vocab(t1, col);
INSERT INTO t1 VALUES('xx', 'xx');
CREATE TABLE x1(t);
INSERT INTO x1 VALUES('xx');
INSERT INTO x1 VALUES('xx');
SELECT term, col FROM v1;
} {
xx a xx b
}
do_execsql_test 7.1 {
SELECT * FROM x1 WHERE 'a'=(SELECT col FROM v1 WHERE term=t)
} {xx xx}
finish_test
+10 -5
View File
@@ -230,13 +230,16 @@ static void hash_finish(
*****************************************************************************/
/*
** Implementation of the sha1(X) function.
** Two SQL functions: sha1(X) and sha1b(X).
**
** Return a lower-case hexadecimal rendering of the SHA1 hash of the
** argument X. If X is a BLOB, it is hashed as is. For all other
** sha1(X) returns a lower-case hexadecimal rendering of the SHA1 hash
** of the argument X. If X is a BLOB, it is hashed as is. For all other
** types of input, X is converted into a UTF-8 string and the string
** is hash without the trailing 0x00 terminator. The hash of a NULL
** is hashed without the trailing 0x00 terminator. The hash of a NULL
** value is NULL.
**
** sha1b(X) is the same except that it returns a 20-byte BLOB containing
** the binary hash instead of a hexadecimal string.
*/
static void sha1Func(
sqlite3_context *context,
@@ -257,11 +260,13 @@ static void sha1Func(
hash_step(&cx, sqlite3_value_text(argv[0]), nByte);
}
if( sqlite3_user_data(context)!=0 ){
/* sha1b() - binary result */
hash_finish(&cx, zOut, 1);
sqlite3_result_blob(context, zOut, 20, SQLITE_TRANSIENT);
}else{
/* sha1() - hexadecimal text result */
hash_finish(&cx, zOut, 0);
sqlite3_result_blob(context, zOut, 40, SQLITE_TRANSIENT);
sqlite3_result_text(context, zOut, 40, SQLITE_TRANSIENT);
}
}
+89 -29
View File
@@ -19,10 +19,10 @@ 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:
Suppose variable `sqlite3_stmt *pStmt` is a pointer to an SQLite
prepared statement 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 */
@@ -41,21 +41,60 @@ if( rc ){
sqlite3_free(zErrMsg); /* Free the error message text */
}else{
printf("%s", zResult); /* Report the results */
sqlite3_free(zResult); /* Free memory used to hold 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
and where to send the generated output. Most of the work in using
the QRF involves filling out the sqlite3_qrf_spec.
### 1.1 Using QRF with SQL text
If you start with SQL text instead of an sqlite3_stmt pointer, and
especially if the SQL text might comprise two or more statements, then
the SQL text needs to be converted into sqlite3_stmt objects separately.
If the original SQL text is in a variable `const char *zSql` and the
database connection is in variable `sqlite3 *db`, then code
similar to the following should work:
> ~~~
sqlite3_qrf_spec spec; /* Format specification */
char *zErrMsg; /* Text error message (optional) */
char *zResult = 0; /* Formatted output written here */
sqlite3_stmt *pStmt; /* Next prepared statement */
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 */
while( zSql && zSql[0] ){
pStmt = 0; /* Not required; just being pedantic */
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zSql);
if( rc!=SQLITE_OK ){
printf("Error: %s\n", sqlite3_errmsg(db));
}else{
rc = sqlite3_format_query_result(pStmt, &spec, &zErrMsg); /* Get 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 */
zResult = 0;
}
}
sqlite3_finalize(pStmt);
}
~~~
## 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.
The most recent definition of `sqlite3_qrf_spec` is shown below.
Do not be alarmed by the complexity of this structure. You only have
Do not be alarmed by the complexity of this structure. You only need
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.
@@ -74,9 +113,10 @@ struct sqlite3_qrf_spec {
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 */
unsigned char bBorder; /* Show outer border in Box and Table styles */
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 */
@@ -112,7 +152,7 @@ Further detail on the meanings of each of the fields in the
### 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
the QRF might add new fields to 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.
@@ -126,7 +166,7 @@ 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
is initially non-NULL, then it is assumed to already point to memory obtained
from sqlite3_malloc(). In that case, the buffer is resized using
sqlite3_realloc() and the new text is appended.
@@ -140,8 +180,8 @@ 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.
specific output format that will be generated. See [section 4.0](#style)
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.
@@ -265,6 +305,7 @@ how the BLOB value is formatted. The following options are available;
#define QRF_BLOB_Hex 3 /* Hexadecimal representation */
#define QRF_BLOB_Tcl 4 /* "\000" notation */
#define QRF_BLOB_Json 5 /* A JSON string */
#define QRF_BLOB_Size 6 /* Display the blob size only */
~~~
A value of QRF_BLOB_Auto means that display format is selected automatically
@@ -292,6 +333,10 @@ the C/Tcl/Perl octal backslash escapes. So the string from the
previous paragraph would be shown as
`"\u0005\u0028\u0081\u00f3"`.
A value of QRF_BLOB_Size does not show any BLOB content at all.
Instead, it substitutes a text string that says how many bytes
the BLOB contains.
### 2.8 Maximum size of displayed content (nLineLimit, nCharLimit)
If the sqlite3_qrf_spec.nCharLimit setting is non-zero, then the formatter
@@ -350,6 +395,10 @@ 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.
The nScreenWidth field currently only makes a difference in
columnar styles (**Box**, **Column**, **Markdown**, and **Table**)
and in the **Line** style.
### 2.11 Individual Column Width (nWidth and aWidth)
The sqlite3_qrf_spec.aWidth field is a pointer to an array of
@@ -457,8 +506,9 @@ 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 horizontal alignment, then values are right-aligned
(QRF_ALIGN_Right) if they are numeric and left-aligned
(QRF_ALIGN_Left) otherwise. If neither aAlign\[\] nor eDfltAlign
specify a vertical alignment, then values are top-aligned
(QRF_ALIGN_Top).
@@ -479,17 +529,12 @@ and these settings are ignored for those styles.
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)
### 2.15 The Rendering Of NULL (zNull)
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
@@ -523,12 +568,13 @@ 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().
<a id="style"></a>
## 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.
output style (sometimes called "output mode") is determined by
the eStyle field of the sqlite3_qrf_spec object. The set of
supported output modes might increase in future versions.
The following output modes are currently defined:
> ~~~
@@ -584,8 +630,22 @@ 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.
psql. The **Markdown** renders its result in the Markdown table format.
The **Box** and **Table** styles normally have a border that surrounds
the entire result. However, if sqlite3_qrf_spec.bBorder is QRF_No, then
that border is omitted, saving a little space both horizontally and
vertically.
#### 4.2.1 Split Column Mode
If the bSplitColumn field is QRF_Yes, and eStyle is QRF_STYLE_Column,
and bTitles is QRF_No, and nScreenWidth is greater than zero, and if
the query only returns a single column, then a special rendering known
as "Split Column Mode" will be used. In split column mode, instead
of showing all results in one tall column, the content wraps vertically
so that it appears on the screen as multiple columns, as many as will
fit in the available screen width.
### 4.3 Line-oriented Styles
@@ -668,7 +728,7 @@ 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
* `README.md` &rarr; This documentation
To use the SQLite result formatter, include the "`qrf.h`" header file
and link the application against the "`qrf.c`" source file.
+315 -72
View File
@@ -428,7 +428,7 @@ static const struct {
{1, 0x00f7f}, {0, 0x00f80}, {1, 0x00f85}, {0, 0x00f86}, {1, 0x00f88},
{0, 0x00f90}, {1, 0x00f98}, {0, 0x00f99}, {1, 0x00fbd}, {0, 0x00fc6},
{1, 0x00fc7}, {0, 0x0102d}, {1, 0x01031}, {0, 0x01032}, {1, 0x01033},
{0, 0x01036}, {1, 0x01038}, {0, 0x01039}, {1, 0x0103a}, {0, 0x01058},
{0, 0x01036}, {1, 0x0103b}, {0, 0x01058},
{1, 0x0105a}, {2, 0x01100}, {0, 0x01160}, {1, 0x01200}, {0, 0x0135f},
{1, 0x01360}, {0, 0x01712}, {1, 0x01715}, {0, 0x01732}, {1, 0x01735},
{0, 0x01752}, {1, 0x01754}, {0, 0x01772}, {1, 0x01774}, {0, 0x017b4},
@@ -471,7 +471,7 @@ int sqlite3_qrf_wcwidth(int c){
int iFirst, iLast;
/* Fast path for common characters */
if( c<=0x300 ) return 1;
if( c<0x300 ) return 1;
/* The general case */
iFirst = 0;
@@ -577,11 +577,14 @@ static int qrfDisplayLength(const char *zIn){
** it will need to be split.
*/
static int qrfDisplayWidth(const char *zIn, sqlite3_int64 nByte, int *pnNL){
const unsigned char *z = (const unsigned char*)zIn;
const unsigned char *zEnd = &z[nByte];
const unsigned char *z;
const unsigned char *zEnd;
int mx = 0;
int n = 0;
int nNL = 0;
if( zIn==0 ) zIn = "";
z = (const unsigned char*)zIn;
zEnd = &z[nByte];
while( z<zEnd ){
if( z[0]<' ' ){
int k;
@@ -983,6 +986,11 @@ static void qrfRenderValue(Qrf *p, sqlite3_str *pOut, int iCol){
}
break;
}
case QRF_BLOB_Size: {
int nBlob = sqlite3_column_bytes(p->pStmt,iCol);
sqlite3_str_appendf(pOut, "(%d-byte blob)", nBlob);
break;
}
default: {
const char *zTxt = (const char*)sqlite3_column_text(p->pStmt,iCol);
qrfEncodeText(p, pOut, zTxt);
@@ -991,11 +999,7 @@ static void qrfRenderValue(Qrf *p, sqlite3_str *pOut, int iCol){
break;
}
case SQLITE_NULL: {
if( p->spec.bTextNull==QRF_Yes ){
qrfEncodeText(p, pOut, p->spec.zNull);
}else{
sqlite3_str_appendall(pOut, p->spec.zNull);
}
sqlite3_str_appendall(pOut, p->spec.zNull);
break;
}
case SQLITE_TEXT: {
@@ -1042,6 +1046,17 @@ static void qrfRenderValue(Qrf *p, sqlite3_str *pOut, int iCol){
#endif
}
/* Trim spaces of the end if pOut
*/
static void qrfRTrim(sqlite3_str *pOut){
#if SQLITE_VERSION_NUMBER>=3052000
int nByte = sqlite3_str_length(pOut);
const char *zOut = sqlite3_str_value(pOut);
while( nByte>0 && zOut[nByte-1]==' ' ){ nByte--; }
sqlite3_str_truncate(pOut, nByte);
#endif
}
/*
** Store string zUtf to pOut as w characters. If w is negative,
** then right-justify the text. W is the width in display characters, not
@@ -1119,15 +1134,15 @@ static void qrfWrapLine(
const unsigned char *z = (const unsigned char*)zIn;
unsigned char c = 0;
if( zIn[0]==0 ){
if( z[0]==0 ){
*pnThis = 0;
*pnWide = 0;
*piNext = 0;
return;
}
n = 0;
for(i=0; n<w; i++){
c = zIn[i];
for(i=0; n<=w; i++){
c = z[i];
if( c>=0xc0 ){
int u;
int len = sqlite3_qrf_decode_utf8(&z[i], &u);
@@ -1138,11 +1153,12 @@ static void qrfWrapLine(
continue;
}
if( c>=' ' ){
if( n==w ) break;
n++;
continue;
}
if( c==0 || c=='\n' ) break;
if( c=='\r' && zIn[i+1]=='\n' ){ c = zIn[++i]; break; }
if( c=='\r' && z[i+1]=='\n' ){ c = z[++i]; break; }
if( c=='\t' ){
int wcw = 8 - (n&7);
if( n+wcw>w ) break;
@@ -1151,6 +1167,8 @@ static void qrfWrapLine(
}
if( c==0x1b && (k = qrfIsVt100(&z[i]))>0 ){
i += k-1;
}else if( n==w ){
break;
}else{
n++;
}
@@ -1250,36 +1268,6 @@ static void qrfAppendWithTabs(
sqlite3_str_append(pOut, (const char*)z, i);
}
/*
** Output horizontally justified text into pOut. The text is the
** first nVal bytes of zVal. Include nWS bytes of whitespace, either
** split between both sides, or on the left, or on the right, depending
** on eAlign.
*/
static void qrfPrintAligned(
sqlite3_str *pOut, /* Append text here */
const char *zVal, /* Text to append */
int nVal, /* Use only the first nVal bytes of zVal[] */
int nWS, /* Whitespace for horizonal alignment */
unsigned char eAlign /* Alignment type */
){
eAlign &= QRF_ALIGN_HMASK;
if( eAlign==QRF_ALIGN_Center ){
/* Center the text */
sqlite3_str_appendchar(pOut, nWS/2, ' ');
qrfAppendWithTabs(pOut, zVal, nVal);
sqlite3_str_appendchar(pOut, nWS - nWS/2, ' ');
}else if( eAlign==QRF_ALIGN_Right){
/* Right justify the text */
sqlite3_str_appendchar(pOut, nWS, ' ');
qrfAppendWithTabs(pOut, zVal, nVal);
}else{
/* Left justify the next */
qrfAppendWithTabs(pOut, zVal, nVal);
sqlite3_str_appendchar(pOut, nWS, ' ');
}
}
/*
** GCC does not define the offsetof() macro so we'll have to do it
** ourselves.
@@ -1303,15 +1291,47 @@ struct qrfColData {
sqlite3_int64 n; /* Number of cells. nCol*nRow */
char **az; /* Content of all cells */
int *aiWth; /* Width of each cell */
unsigned char *abNum; /* True for each numeric cell */
struct qrfPerCol { /* Per-column data */
char *z; /* Cache of text for current row */
int w; /* Computed width of this column */
int mxW; /* Maximum natural (unwrapped) width */
unsigned char e; /* Alignment */
unsigned char fx; /* Width is fixed */
unsigned char bNum; /* True if is numeric */
} *a; /* One per column */
};
/*
** Output horizontally justified text into pOut. The text is the
** first nVal bytes of zVal. Include nWS bytes of whitespace, either
** split between both sides, or on the left, or on the right, depending
** on eAlign.
*/
static void qrfPrintAligned(
sqlite3_str *pOut, /* Append text here */
struct qrfPerCol *pCol, /* Information about the text to print */
int nVal, /* Use only the first nVal bytes of zVal[] */
int nWS /* Whitespace for horizonal alignment */
){
unsigned char eAlign = pCol->e & QRF_ALIGN_HMASK;
if( eAlign==QRF_Auto && pCol->bNum ) eAlign = QRF_ALIGN_Right;
if( eAlign==QRF_ALIGN_Center ){
/* Center the text */
sqlite3_str_appendchar(pOut, nWS/2, ' ');
qrfAppendWithTabs(pOut, pCol->z, nVal);
sqlite3_str_appendchar(pOut, nWS - nWS/2, ' ');
}else if( eAlign==QRF_ALIGN_Right ){
/* Right justify the text */
sqlite3_str_appendchar(pOut, nWS, ' ');
qrfAppendWithTabs(pOut, pCol->z, nVal);
}else{
/* Left justify the text */
qrfAppendWithTabs(pOut, pCol->z, nVal);
sqlite3_str_appendchar(pOut, nWS, ' ');
}
}
/*
** Free all the memory allocates in the qrfColData object
*/
@@ -1320,6 +1340,7 @@ static void qrfColDataFree(qrfColData *p){
for(i=0; i<p->n; i++) sqlite3_free(p->az[i]);
sqlite3_free(p->az);
sqlite3_free(p->aiWth);
sqlite3_free(p->abNum);
sqlite3_free(p->a);
memset(p, 0, sizeof(*p));
}
@@ -1331,6 +1352,7 @@ static void qrfColDataFree(qrfColData *p){
static int qrfColDataEnlarge(qrfColData *p){
char **azData;
int *aiWth;
unsigned char *abNum;
p->nAlloc = 2*p->nAlloc + 10*p->nCol;
azData = sqlite3_realloc64(p->az, p->nAlloc*sizeof(char*));
if( azData==0 ){
@@ -1346,6 +1368,13 @@ static int qrfColDataEnlarge(qrfColData *p){
return 1;
}
p->aiWth = aiWth;
abNum = sqlite3_realloc64(p->abNum, p->nAlloc);
if( abNum==0 ){
qrfOom(p->p);
qrfColDataFree(p);
return 1;
}
p->abNum = abNum;
return 0;
}
@@ -1355,13 +1384,18 @@ static int qrfColDataEnlarge(qrfColData *p){
static void qrfRowSeparator(sqlite3_str *pOut, qrfColData *p, char cSep){
int i;
if( p->nCol>0 ){
sqlite3_str_append(pOut, &cSep, 1);
int useBorder = p->p->spec.bBorder!=QRF_No;
if( useBorder ){
sqlite3_str_append(pOut, &cSep, 1);
}
sqlite3_str_appendchar(pOut, p->a[0].w+p->nMargin, '-');
for(i=1; i<p->nCol; i++){
sqlite3_str_append(pOut, &cSep, 1);
sqlite3_str_appendchar(pOut, p->a[i].w+p->nMargin, '-');
}
sqlite3_str_append(pOut, &cSep, 1);
if( useBorder ){
sqlite3_str_append(pOut, &cSep, 1);
}
}
sqlite3_str_append(pOut, "\n", 1);
}
@@ -1419,13 +1453,18 @@ static void qrfBoxSeparator(
){
int i;
if( p->nCol>0 ){
sqlite3_str_appendall(pOut, zSep1);
int useBorder = p->p->spec.bBorder!=QRF_No;
if( useBorder ){
sqlite3_str_appendall(pOut, zSep1);
}
qrfBoxLine(pOut, p->a[0].w+p->nMargin);
for(i=1; i<p->nCol; i++){
sqlite3_str_appendall(pOut, zSep2);
qrfBoxLine(pOut, p->a[i].w+p->nMargin);
}
sqlite3_str_appendall(pOut, zSep3);
if( useBorder ){
sqlite3_str_appendall(pOut, zSep3);
}
}
sqlite3_str_append(pOut, "\n", 1);
}
@@ -1452,6 +1491,144 @@ static void qrfLoadAlignment(qrfColData *pData, Qrf *p){
}
}
/*
** If the single column in pData->a[] with pData->n entries can be
** laid out as nCol columns with a 2-space gap between each such
** that all columns fit within nSW, then return a pointer to an array
** of integers which is the width of each column from left to right.
**
** If the layout is not possible, return a NULL pointer.
**
** Space to hold the returned array is from sqlite_malloc64().
*/
static int *qrfValidLayout(
qrfColData *pData, /* Collected query results */
Qrf *p, /* On which to report an OOM */
int nCol, /* Attempt this many columns */
int nSW /* Screen width */
){
int i; /* Loop counter */
int nr; /* Number of rows */
int w = 0; /* Width of the current column */
int t; /* Total width of all columns */
int *aw; /* Array of individual column widths */
aw = sqlite3_malloc64( sizeof(int)*nCol );
if( aw==0 ){
qrfOom(p);
return 0;
}
nr = (pData->n + nCol - 1)/nCol;
for(i=0; i<pData->n; i++){
if( (i%nr)==0 ){
if( i>0 ) aw[i/nr-1] = w;
w = pData->aiWth[i];
}else if( pData->aiWth[i]>w ){
w = pData->aiWth[i];
}
}
aw[nCol-1] = w;
for(t=i=0; i<nCol; i++) t += aw[i];
t += 2*(nCol-1);
if( t>nSW ){
sqlite3_free(aw);
return 0;
}
return aw;
}
/*
** The output is single-column and the bSplitColumn flag is set.
** Check to see if the single-column output can be split into multiple
** columns that appear side-by-side. Adjust pData appropriately.
*/
static void qrfSplitColumn(qrfColData *pData, Qrf *p){
int nCol = 1;
int *aw = 0;
char **az = 0;
int *aiWth = 0;
unsigned char *abNum = 0;
int nColNext = 2;
int w;
struct qrfPerCol *a = 0;
sqlite3_int64 nRow = 1;
sqlite3_int64 i;
while( 1/*exit-by-break*/ ){
int *awNew = qrfValidLayout(pData, p, nColNext, p->spec.nScreenWidth);
if( awNew==0 ) break;
sqlite3_free(aw);
aw = awNew;
nCol = nColNext;
nRow = (pData->n + nCol - 1)/nCol;
if( nRow==1 ) break;
nColNext++;
while( (pData->n + nColNext - 1)/nColNext == nRow ) nColNext++;
}
if( nCol==1 ){
sqlite3_free(aw);
return; /* Cannot do better than 1 column */
}
az = sqlite3_malloc64( nRow*nCol*sizeof(char*) );
if( az==0 ){
qrfOom(p);
return;
}
aiWth = sqlite3_malloc64( nRow*nCol*sizeof(int) );
if( aiWth==0 ){
sqlite3_free(az);
qrfOom(p);
return;
}
a = sqlite3_malloc64( nCol*sizeof(struct qrfPerCol) );
if( a==0 ){
sqlite3_free(az);
sqlite3_free(aiWth);
qrfOom(p);
return;
}
abNum = sqlite3_malloc64( nRow*nCol );
if( abNum==0 ){
sqlite3_free(az);
sqlite3_free(aiWth);
sqlite3_free(a);
qrfOom(p);
return;
}
for(i=0; i<pData->n; i++){
sqlite3_int64 j = (i%nRow)*nCol + (i/nRow);
az[j] = pData->az[i];
abNum[j]= pData->abNum[i];
pData->az[i] = 0;
aiWth[j] = pData->aiWth[i];
}
while( i<nRow*nCol ){
sqlite3_int64 j = (i%nRow)*nCol + (i/nRow);
az[j] = sqlite3_mprintf("");
if( az[j]==0 ) qrfOom(p);
aiWth[j] = 0;
abNum[j] = 0;
i++;
}
for(i=0; i<nCol; i++){
a[i].fx = a[i].mxW = a[i].w = aw[i];
a[i].e = pData->a[0].e;
}
sqlite3_free(pData->az);
sqlite3_free(pData->aiWth);
sqlite3_free(pData->a);
sqlite3_free(pData->abNum);
sqlite3_free(aw);
pData->az = az;
pData->aiWth = aiWth;
pData->a = a;
pData->abNum = abNum;
pData->nCol = nCol;
pData->n = pData->nAlloc = nRow*nCol;
for(i=w=0; i<nCol; i++) w += a[i].w;
pData->nMargin = (p->spec.nScreenWidth - w)/(nCol - 1);
if( pData->nMargin>5 ) pData->nMargin = 5;
}
/*
** Adjust the layout for the screen width restriction
*/
@@ -1468,6 +1645,7 @@ static void qrfRestrictScreenWidth(qrfColData *pData, Qrf *p){
sepW = pData->nCol*2 - 2;
}else{
sepW = pData->nCol*3 + 1;
if( p->spec.bBorder==QRF_No ) sepW -= 2;
}
nCol = pData->nCol;
for(i=sumW=0; i<nCol; i++) sumW += pData->a[i].w;
@@ -1479,6 +1657,7 @@ static void qrfRestrictScreenWidth(qrfColData *pData, Qrf *p){
sepW = pData->nCol - 1;
}else{
sepW = pData->nCol + 1;
if( p->spec.bBorder==QRF_No ) sepW -= 2;
}
targetW = p->spec.nScreenWidth - sepW;
@@ -1541,6 +1720,7 @@ static void qrfColumnar(Qrf *p){
int bWW; /* True to do word-wrap */
sqlite3_str *pStr; /* Temporary rendering */
qrfColData data; /* Columnar layout data */
int bRTrim; /* Trim trailing space */
rc = sqlite3_step(p->pStmt);
if( rc!=SQLITE_ROW || nColumn==0 ){
@@ -1550,6 +1730,7 @@ static void qrfColumnar(Qrf *p){
/* Initialize the data container */
memset(&data, 0, sizeof(data));
data.nCol = p->nCol;
data.p = p;
data.a = sqlite3_malloc64( nColumn*sizeof(struct qrfPerCol) );
if( data.a==0 ){
qrfOom(p);
@@ -1563,6 +1744,7 @@ static void qrfColumnar(Qrf *p){
if( p->spec.bTitles==QRF_Yes ){
unsigned char saved_eText = p->spec.eText;
p->spec.eText = p->spec.eTitle;
memset(data.abNum, 0, nColumn);
for(i=0; i<nColumn; i++){
const char *z = (const char*)sqlite3_column_name(p->pStmt,i);
int nNL = 0;
@@ -1587,10 +1769,12 @@ static void qrfColumnar(Qrf *p){
char *z;
int nNL = 0;
int n, w;
int eType = sqlite3_column_type(p->pStmt,i);
pStr = sqlite3_str_new(p->db);
qrfRenderValue(p, pStr, i);
n = sqlite3_str_length(pStr);
z = data.az[data.n] = sqlite3_str_finish(pStr);
data.abNum[data.n] = eType==SQLITE_INTEGER || eType==SQLITE_FLOAT;
data.aiWth[data.n] = w = qrfDisplayWidth(z, n, &nNL);
data.n++;
if( w>data.a[i].mxW ) data.a[i].mxW = w;
@@ -1650,8 +1834,22 @@ static void qrfColumnar(Qrf *p){
data.a[i].w = w;
}
/* Adjust the column widths due to screen width restrictions */
qrfRestrictScreenWidth(&data, p);
if( nColumn==1
&& data.n>1
&& p->spec.bSplitColumn==QRF_Yes
&& p->spec.eStyle==QRF_STYLE_Column
&& p->spec.bTitles==QRF_No
&& p->spec.nScreenWidth>data.a[0].w+3
){
/* Attempt to convert single-column tables into multi-column by
** verticle wrapping, if the screen is wide enough and if the
** bSplitColumn flag is set. */
qrfSplitColumn(&data, p);
nColumn = data.nCol;
}else{
/* Adjust the column widths due to screen width restrictions */
qrfRestrictScreenWidth(&data, p);
}
/* Draw the line across the top of the table. Also initialize
** the row boundary and column separator texts. */
@@ -1666,7 +1864,12 @@ static void qrfColumnar(Qrf *p){
colSep = BOX_13;
rowSep = BOX_13 "\n";
}
qrfBoxSeparator(p->pOut, &data, BOX_23, BOX_234, BOX_34);
if( p->spec.bBorder==QRF_No){
rowStart += 3;
rowSep = "\n";
}else{
qrfBoxSeparator(p->pOut, &data, BOX_23, BOX_234, BOX_34);
}
break;
case QRF_STYLE_Table:
if( data.nMargin ){
@@ -1678,13 +1881,26 @@ static void qrfColumnar(Qrf *p){
colSep = "|";
rowSep = "|\n";
}
qrfRowSeparator(p->pOut, &data, '+');
if( p->spec.bBorder==QRF_No ){
rowStart += 1;
rowSep = "\n";
}else{
qrfRowSeparator(p->pOut, &data, '+');
}
break;
case QRF_STYLE_Column:
case QRF_STYLE_Column: {
static const char zSpace[] = " ";
rowStart = "";
colSep = data.nMargin ? " " : " ";
if( data.nMargin<2 ){
colSep = " ";
}else if( data.nMargin<=5 ){
colSep = &zSpace[5-data.nMargin];
}else{
colSep = zSpace;
}
rowSep = "\n";
break;
}
default: /*case QRF_STYLE_Markdown:*/
if( data.nMargin ){
rowStart = "| ";
@@ -1702,6 +1918,15 @@ static void qrfColumnar(Qrf *p){
szColSep = (int)strlen(colSep);
bWW = (p->spec.bWordWrap==QRF_Yes && data.bMultiRow);
if( p->spec.eStyle==QRF_STYLE_Column
|| (p->spec.bBorder==QRF_No
&& (p->spec.eStyle==QRF_STYLE_Box || p->spec.eStyle==QRF_STYLE_Table)
)
){
bRTrim = 1;
}else{
bRTrim = 0;
}
for(i=0; i<data.n; i+=nColumn){
int bMore;
int nRow = 0;
@@ -1710,7 +1935,11 @@ static void qrfColumnar(Qrf *p){
** (if there is a title line) or a row in the body of the table.
** The column number will be j. The row number is i/nColumn.
*/
for(j=0; j<nColumn; j++){ data.a[j].z = data.az[i+j]; }
for(j=0; j<nColumn; j++){
data.a[j].z = data.az[i+j];
if( data.a[j].z==0 ) data.a[j].z = "";
data.a[j].bNum = data.abNum[i+j];
}
do{
sqlite3_str_append(p->pOut, rowStart, szRowStart);
bMore = 0;
@@ -1721,12 +1950,15 @@ static void qrfColumnar(Qrf *p){
int nWS;
qrfWrapLine(data.a[j].z, data.a[j].w, bWW, &nThis, &nWide, &iNext);
nWS = data.a[j].w - nWide;
qrfPrintAligned(p->pOut, data.a[j].z, nThis, nWS, data.a[j].e);
qrfPrintAligned(p->pOut, &data.a[j], nThis, nWS);
data.a[j].z += iNext;
if( data.a[j].z[0]!=0 ) bMore = 1;
if( data.a[j].z[0]!=0 ){
bMore = 1;
}
if( j<nColumn-1 ){
sqlite3_str_append(p->pOut, colSep, szColSep);
}else{
if( bRTrim ) qrfRTrim(p->pOut);
sqlite3_str_append(p->pOut, rowSep, szRowSep);
}
}
@@ -1740,11 +1972,13 @@ static void qrfColumnar(Qrf *p){
}else{
int nE = 3;
if( nE>data.a[j].w ) nE = data.a[j].w;
qrfPrintAligned(p->pOut, "...", nE, data.a[j].w-nE, data.a[j].e);
data.a[j].z = "...";
qrfPrintAligned(p->pOut, &data.a[j], nE, data.a[j].w-nE);
}
if( j<nColumn-1 ){
sqlite3_str_append(p->pOut, colSep, szColSep);
}else{
if( bRTrim ) qrfRTrim(p->pOut);
sqlite3_str_append(p->pOut, rowSep, szRowSep);
}
}
@@ -1785,10 +2019,12 @@ static void qrfColumnar(Qrf *p){
if( j<nColumn-1 ){
sqlite3_str_append(p->pOut, colSep, szColSep);
}else{
qrfRTrim(p->pOut);
sqlite3_str_append(p->pOut, rowSep, szRowSep);
}
}
}else if( data.bMultiRow ){
qrfRTrim(p->pOut);
sqlite3_str_append(p->pOut, "\n", 1);
}
break;
@@ -1798,13 +2034,15 @@ static void qrfColumnar(Qrf *p){
}
/* Draw the line across the bottom of the table */
switch( p->spec.eStyle ){
case QRF_STYLE_Box:
qrfBoxSeparator(p->pOut, &data, BOX_12, BOX_124, BOX_14);
break;
case QRF_STYLE_Table:
qrfRowSeparator(p->pOut, &data, '+');
break;
if( p->spec.bBorder!=QRF_No ){
switch( p->spec.eStyle ){
case QRF_STYLE_Box:
qrfBoxSeparator(p->pOut, &data, BOX_12, BOX_124, BOX_14);
break;
case QRF_STYLE_Table:
qrfRowSeparator(p->pOut, &data, '+');
break;
}
}
qrfWrite(p);
@@ -2254,6 +2492,11 @@ static void qrfInitialize(
if( p->mxWidth<=0 ) p->mxWidth = QRF_MAX_WIDTH;
p->mxHeight = p->spec.nLineLimit;
if( p->mxHeight<=0 ) p->mxHeight = 2147483647;
if( p->spec.eStyle>QRF_STYLE_Table ) p->spec.eStyle = QRF_Auto;
if( p->spec.eEsc>QRF_ESC_Symbol ) p->spec.eEsc = QRF_Auto;
if( p->spec.eText>QRF_TEXT_Json ) p->spec.eText = QRF_Auto;
if( p->spec.eTitle>QRF_TEXT_Json ) p->spec.eTitle = QRF_Auto;
if( p->spec.eBlob>QRF_BLOB_Size ) p->spec.eBlob = QRF_Auto;
qrf_reinit:
switch( p->spec.eStyle ){
case QRF_Auto: {
@@ -2272,7 +2515,6 @@ qrf_reinit:
case QRF_STYLE_JObject:
case QRF_STYLE_Json: {
p->spec.eText = QRF_TEXT_Json;
p->spec.eBlob = QRF_BLOB_Json;
p->spec.zNull = "null";
break;
}
@@ -2283,7 +2525,6 @@ qrf_reinit:
}
case QRF_STYLE_Insert: {
p->spec.eText = QRF_TEXT_Sql;
p->spec.eBlob = QRF_BLOB_Sql;
p->spec.zNull = "NULL";
if( p->spec.zTableName==0 || p->spec.zTableName[0]==0 ){
p->spec.zTableName = "tab";
@@ -2293,7 +2534,6 @@ qrf_reinit:
case QRF_STYLE_Csv: {
p->spec.eStyle = QRF_STYLE_List;
p->spec.eText = QRF_TEXT_Csv;
p->spec.eBlob = QRF_BLOB_Text;
p->spec.zColumnSep = ",";
p->spec.zRowSep = "\r\n";
p->spec.zNull = "";
@@ -2301,7 +2541,6 @@ qrf_reinit:
}
case QRF_STYLE_Quote: {
p->spec.eText = QRF_TEXT_Sql;
p->spec.eBlob = QRF_BLOB_Sql;
p->spec.zNull = "NULL";
p->spec.zColumnSep = ",";
p->spec.zRowSep = "\n";
@@ -2386,13 +2625,17 @@ static void qrfFinalize(Qrf *p){
break;
}
case QRF_STYLE_Json: {
sqlite3_str_append(p->pOut, "}]\n", 3);
qrfWrite(p);
if( p->nRow>0 ){
sqlite3_str_append(p->pOut, "}]\n", 3);
qrfWrite(p);
}
break;
}
case QRF_STYLE_JObject: {
sqlite3_str_append(p->pOut, "}\n", 2);
qrfWrite(p);
if( p->nRow>0 ){
sqlite3_str_append(p->pOut, "}\n", 2);
qrfWrite(p);
}
break;
}
case QRF_STYLE_Line: {
+9 -3
View File
@@ -14,6 +14,9 @@
*/
#ifndef SQLITE_QRF_H
#define SQLITE_QRF_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
#include "sqlite3.h"
@@ -31,9 +34,10 @@ struct sqlite3_qrf_spec {
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 */
unsigned char bBorder; /* Show outer border in Box and Table styles */
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 */
@@ -116,6 +120,7 @@ int sqlite3_format_query_result(
#define QRF_BLOB_Hex 3 /* Hexadecimal representation */
#define QRF_BLOB_Tcl 4 /* "\000" notation */
#define QRF_BLOB_Json 5 /* A JSON string */
#define QRF_BLOB_Size 6 /* Display the blob size only */
/*
** Control-character escape modes.
@@ -179,6 +184,7 @@ int sqlite3_format_query_result(
int sqlite3_qrf_wcwidth(int c);
#ifdef __cplusplus
}
#endif
#endif /* !defined(SQLITE_QRF_H) */
+71 -13
View File
@@ -874,7 +874,6 @@ $(sqlite3-license-version.js): $(bin.version-info) \
awk '/define SQLITE_VERSION/{$$1=""; print "**" $$0}' $(sqlite3.h); \
awk '/define SQLITE_SOURCE_ID/{$$1=""; print "**" $$0}' $(sqlite3.h); \
echo '**'; echo '** Emscripten SDK: $(emcc.version)'; \
echo '**'; echo '** Preprocessed by:'; echo '** @c-pp::ARGV@'; \
echo '*/'; \
} > $@
@@ -910,6 +909,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
@@ -996,13 +996,21 @@ endif
# /shell.c
########################################################################
#
# Fiddle-related decls we need before .wasmbuilds is included
#
fiddle.c.in = $(dir.top)/shell.c $(sqlite3-wasm.c)
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),\
@$(call b.c-pp.shcmd,fiddle,$(EXPORTED_FUNCTIONS.api.in),\
$@,$(EXPORTED_FUNCTIONS.c-pp.flags) -Dfiddle)
@echo $(logtag.@) $(emo.disk)
emcc.flags.fiddle = \
$(emcc.cflags) $(emcc_opt_full) \
--minify 0 \
@@ -1027,21 +1035,23 @@ emcc.flags.fiddle = \
-USQLITE_WASM_BARE_BONES \
-DSQLITE_SHELL_FIDDLE
clean: clean-fiddle
clean-fiddle:
rm -f $(dir.fiddle)/fiddle-module.js \
$(dir.fiddle)/*.wasm \
$(dir.fiddle)/sqlite3-opfs-*.js \
$(dir.fiddle)/*.gz \
$(EXPORTED_FUNCTIONS.fiddle)
rm -fr $(dir.fiddle-debug)
emcc.flags.fiddle.debug = $(emcc.flags.fiddle) \
-DSQLITE_DEBUG \
-DSQLITE_ENABLE_SELECTTRACE \
-DSQLITE_ENABLE_WHERETRACE
fiddle.c.in = $(dir.top)/shell.c $(sqlite3-wasm.c)
clean: clean-fiddle
clean-fiddle:
rm -f $(dir.fiddle)/fiddle-module.js \
$(dir.fiddle)/*.wasm \
$(dir.fiddle)/sqlite3-opfs-*.js \
$(dir.fiddle)/*.gz \
$(dir.fiddle)/index.html \
$(EXPORTED_FUNCTIONS.fiddle)
rm -fr $(dir.fiddle-debug)
distclean: distclean-fiddle
distclean-fiddle:
rm -fr $(dir.fiddle)/jqterm
#
# WASMFS build - unsupported and untested. We used WASMFS
@@ -1370,6 +1380,54 @@ all: tester1
# end tester1
#
#
# jquery.terminal support for fiddle:
#
# If a clone of https://github.com/jcubic/jquery.terminal
# is found in $(JQTERM), defaulting to $(HOME)/src/jquery.terminal
# then add jquery.terminal support to fiddle.
#
# To build that package, from its checkout dir:
#
# npm install
# make
#
c-pp.D.fiddle ?=
JQTERM ?= $(HOME)/src/jquery.terminal
dir.jqtermExt = $(firstword $(wildcard $(JQTERM)))
#$(info dir.jqtermExt=$(dir.jqtermExt))
ifeq (0,$(MAKING_CLEAN))
ifeq (,$(wildcard $(dir.jqtermExt)/js/jquery.terminal.min.js))
$(info $(emo.magic) To add jquery.terminal support to fiddle, set JQTERM=/path/to/its/built/checkout)
else
$(info $(emo.magic) jquery.terminal found in $(dir.jqtermExt) - adding it to fiddle. Make sure it is built!)
dir.jqterm = $(dir.fiddle)/jqterm
$(dir.fiddle)/jqterm/jquery.terminal.bundle.min.js:
@$(call b.mkdir@)
cat $(dir.jqtermExt)/js/jquery-1*.min.js \
$(dir.jqtermExt)/js/jquery.terminal.min.js > $@
$(dir.fiddle)/jqterm/jquery.terminal.min.css: $(dir.jqtermExt)/css/jquery.terminal.min.css
@$(call b.mkdir@)
@$(call b.cp,fiddle,$<,$(dir $@))
$(dir.fiddle)/index.html: $(dir.fiddle)/jqterm/jquery.terminal.bundle.min.js \
$(dir.fiddle)/jqterm/jquery.terminal.min.css
c-pp.D.fiddle += -Djqterm
endif
endif
# ^^^ JQTERM/MAKING_CLEAN
#
# Generate fiddle/index.html. Must come after JQTERM is handled.
#
$(dir.fiddle)/index.html: $(dir.fiddle)/index.c-pp.html
$(eval $(call b.c-pp.target,fiddle,\
$(dir.fiddle)/index.c-pp.html,$(dir.fiddle)/index.html,$(c-pp.D.fiddle)))
$(out.fiddle.wasm): $(dir.fiddle)/index.html
#
# Convenience rules to rebuild with various -Ox levels. Much
# experimentation shows -O2 to be the clear winner in terms of speed.
+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 -3
View File
@@ -62,7 +62,7 @@ by concatenating the following files together in their listed order:
- **`sqlite3-api-prologue.js`**
Contains the initial bootstrap setup of the sqlite3 API
objects. This is exposed as a function, rather than objects, so that
objects. This is exposed as a bootstrapping function so that
the next step can pass in a config object which abstracts away parts
of the WASM environment, to facilitate plugging it in to arbitrary
WASM toolchains. The bootstrapping function gets removed from the
@@ -116,10 +116,10 @@ by concatenating the following files together in their listed order:
a Promise-based interface into the Worker #1 API. This is
a far user-friendlier way to interface with databases running
in a Worker thread.
- **`sqlite3-vfs-helper.js`**
- **`sqlite3-vfs-helper.c-pp.js`**
Installs the `sqlite3.vfs` namespace, which contain helpers for use
by downstream code which creates `sqlite3_vfs` implementations.
- **`sqlite3-vtab-helper.js`**
- **`sqlite3-vtab-helper.c-pp.js`**
Installs the `sqlite3.vtab` namespace, which contain helpers for use
by downstream code which creates `sqlite3_module` implementations.
- **`sqlite3-vfs-opfs.c-pp.js`**
+1 -1
View File
@@ -18,7 +18,7 @@
This file was preprocessed using:
//#@policy error
@c-pp::ARGV@
@c-pp::argv@
//#@policy off
*/
//#if unsupported-build
+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;
+16 -88
View File
@@ -88,7 +88,8 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
wasm.installFunction('i(ippp)', function(t,c,p,x){
if(capi.SQLITE_TRACE_STMT===t){
// x == SQL, p == sqlite3_stmt*
console.log("SQL TRACE #"+(++this.counter)+' via sqlite3@'+c+':',
console.log("SQL TRACE #"+(++this.counter),
'via sqlite3@'+c+'['+capi.sqlite3_db_filename(c,null)+']',
wasm.cstrToJs(x));
}
}.bind({counter: 0}));
@@ -222,32 +223,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
It also accepts those as the first 3 arguments.
*/
const dbCtorHelper = function ctor(...args){
if(!ctor._name2vfs){
/**
Map special filenames which we handle here (instead of in C)
to some helpful metadata...
As of 2022-09-20, the C API supports the names :localStorage:
and :sessionStorage: for kvvfs. However, C code cannot
determine (without embedded JS code, e.g. via Emscripten's
EM_JS()) whether the kvvfs is legal in the current browser
context (namely the main UI thread). In order to help client
code fail early on, instead of it being delayed until they
try to read or write a kvvfs-backed db, we'll check for those
names here and throw if they're not legal in the current
context.
*/
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.")
: false;
ctor._name2vfs[':localStorage:'] = {
vfs: 'kvvfs', filename: isWorkerThread || (()=>'local')
};
ctor._name2vfs[':sessionStorage:'] = {
vfs: 'kvvfs', filename: isWorkerThread || (()=>'session')
};
}
const opt = ctor.normalizeArgs(...args);
//sqlite3.config.debug("DB ctor",opt);
let pDb;
@@ -269,12 +244,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
sqlite3.config.error("Invalid DB ctor args",opt,arguments);
toss3("Invalid arguments for DB constructor:", arguments, "opts:", opt);
}
let fnJs = wasm.isPtr(fn) ? wasm.cstrToJs(fn) : fn;
const vfsCheck = ctor._name2vfs[fnJs];
if(vfsCheck){
vfsName = vfsCheck.vfs;
fn = fnJs = vfsCheck.filename(fnJs);
}
let oflags = 0;
if( flagsStr.indexOf('c')>=0 ){
oflags |= capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE;
@@ -299,7 +268,15 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
}finally{
wasm.pstack.restore(stack);
}
this.filename = fnJs;
this.filename =
/* A poor design choice we have to keep: this.filename may be
in the form "file:....?....". It really should have been
sqlite3_db_filename(pDb) but that discrepancy went too long
unnoticed to be able to change without risk of
breakage. DB.dbFilename() can be used to fetch _just_ the
name part.
*/ wasm.isPtr(fn) ? wasm.cstrToJs(fn) : fn;
}
__ptrMap.set(this, pDb);
__stmtMap.set(this, Object.create(null));
@@ -390,12 +367,12 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
The given db filename must be resolvable using whatever
filesystem layer (virtual or otherwise) is set up for the default
sqlite3 VFS.
sqlite3 VFS or a VFS which can resolve it must be specified.
Note that the special sqlite3 db names ":memory:" and ""
(temporary db) have their normal special meanings here and need
not resolve to real filenames, but "" uses an on-storage
temporary database and requires that the VFS support that.
The special sqlite3 db names ":memory:" and "" (temporary db)
have their normal special meanings here and need not resolve to
real filenames, but "" uses an on-storage temporary database and
requires that the VFS support that.
The second argument specifies the open/create mode for the
database. It must be string containing a sequence of letters (in
@@ -2295,55 +2272,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. */
+5 -83
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().
@@ -2170,9 +2090,11 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(
delete globalThis.sqlite3ApiBootstrap;
delete sqlite3ApiBootstrap.defaultConfig;
return sqlite3.asyncPostInit().then((s)=>{
sqlite3InitScriptInfo.debugModule(
"sqlite3.asyncPostInit() complete", sqlite3
);
if( 'undefined'!==typeof sqlite3InitScriptInfo/* from post-js-header.js */ ){
sqlite3InitScriptInfo.debugModule(
"sqlite3.asyncPostInit() complete", s
);
}
delete s.asyncPostInit;
delete s.scriptInfo;
delete s.emscripten;
+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
@@ -358,7 +358,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
try{
const [cMsg, n] = wasm.scopedAllocCString(e.message, true);
wasm.cstrncpy(pOut, cMsg, nOut);
if(n > nOut) wasm.poke8(pOut + nOut - 1, 0);
if(n > nOut) wasm.poke8(wasm.ptr.add(pOut,nOut,-1), 0);
}catch(e){
return capi.SQLITE_NOMEM;
}finally{
+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
+1 -1
View File
@@ -2603,7 +2603,7 @@ static int arg_is_flag( char const *zFlag, char const *zArg,
static void define_argv(int argc, char const * const * argv){
sqlite3_str * const s = sqlite3_str_new(g.db);
sqlite3_str_append(s, "c-pp::ARGV=", 11);
sqlite3_str_append(s, "c-pp::argv=", 11);
for( int i = 0; i < argc; ++i ){
if( i ) sqlite3_str_appendchar(s, 1, ' ');
sqlite3_str_appendf(s, "%s", argv[i]);
+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()*/;
@@ -5,20 +5,29 @@
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>SQLite3 Fiddle</title>
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
//#if jqterm
<!--
To add a terminal-style view using jquery.terminal[^1],
uncomment the following two HTML lines and ensure that these
files are on the web server.
jquery-bundle.min.js is a concatenation of jquery.min.js from
jquery.terminal.bundle.min.js is a concatenation of jquery.min.js from
[^2] and jquery.terminal.min.js from [^1].
jquery.terminal.min.css is from [^1].
jquery.terminal.min.css is from [^1]. Alterntely, jquery-VERSION.min.js
can be found in jquery.terminal's source tree.
Fiddle automatically enables support for this if it's installed.
In the canonical build process, if the JQTERM env var is set
to a dir containing a clone of [^1] then this block gets
enabled and a copy of [^1] is integrated automatically.
[^1]: https://github.com/jcubic/jquery.terminal
[^2]: https://jquery.com
-->
<!--script src="jqterm/jqterm-bundle.min.js"></script>
<link rel="stylesheet" href="jqterm/jquery.terminal.min.css"-->
<script src="jqterm/jquery.terminal.bundle.min.js"></script>
<link rel="stylesheet" href="jqterm/jquery.terminal.min.css">
//#endif
<style>
/* The following styles are for app-level use. */
:root {
+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*/)
+6
View File
@@ -1863,6 +1863,12 @@ mdevtest: srctree-check has_tclsh85
sdevtest: has_tclsh85
$(TCLSH_CMD) $(TOP)/test/testrunner.tcl sdevtest $(TSTRNNR_OPTS)
errors:
$(TCLSH_CMD) $(TOP)/test/testrunner.tcl errors
status:
$(TCLSH_CMD) $(TOP)/test/testrunner.tcl status -d 2
# Like releasetest, except it omits srctree-check and verify-source so
# that it can be used on a modified source tree.
#
+2
View File
@@ -0,0 +1,2 @@
@echo off
nmake /f Makefile.msc %*
+64 -59
View File
@@ -1,13 +1,13 @@
C Fix\sa\scrash\sthat\scould\soccur\sfollowing\sa\sparse\serror\son\sthis\sbranch.
D 2025-12-01T17:22:15.541
C Merge\slatest\strunk\schanges\sinto\sthis\sbranch.
D 2025-12-01T17:22:35.232
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 8c5ed7173ee4cc3c03ba8e21065128f7ca5292c83d68f3bf971786e21fc6e382
F README.md dae499194b75deed76a13a4a83c82493f2530331882d7dfe5754d63287d3f8f7
F Makefile.msc 295bc62fdd8663a6581cc43252403fac3cb784002164ff53fac8d15fc72fdfaa
F README.md 3fa51fc7ababc32edd175ae8b2986c86d5ea120c1cb1e57c7f7849492d1405ec
F VERSION 74672bfd4c7826c0fc6f84762488a707c52e7d2d94af42ccb0edcc6c74311c41
F art/icon-243x273.gif 9750b734f82fdb3dc43127753d5e6fbf3b62c9f4e136c2fbf573b2f57ea87af5
F art/icon-80x90.gif 65509ce3e5f86a9cd64fe7fca2d23954199f31fe44c1e09e208c80fb83d87031
@@ -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
@@ -33,7 +33,7 @@ F autoconf/tea/teaish.tcl 81feb417e718ed75cdd7e2fdf6771f3da80dae97377a90c4d5b62b
F autoconf/tea/teaish.test.tcl cfe94e1fb79dd078f650295be59843d470125e0cc3a17a1414c1fb8d77f4aea6
F autosetup/LICENSE 41a26aebdd2cd185d1e2b210f71b7ce234496979f6b35aef2cbf6b80cbed4ce4
F autosetup/README.autosetup a78ff8c4a3d2636a4268736672a74bf14a82f42687fcf0631a70c516075c031e
F autosetup/README.md ce0f95980a687bb861bd830b76bc4b48513567be5cf5ee7004f4f3439ffe3841
F autosetup/README.md e8503618f6cb31b692383ca662f6eb9ef4345463f5e3c1fc84a5d6863c7058be
F autosetup/autosetup b16e44924c197783df67366762dda985b45d49ebc4af15f4054e3ee0e3b65169 x
F autosetup/autosetup-config.guess dfa101c5e8220e864d5e9c72a85e87110df60260d36cb951ad0a85d6d9eaa463 x
F autosetup/autosetup-config.sub a38fb074d0dece01cf919e9fb534a26011608aa8fa606490864295328526cd73 x
@@ -44,7 +44,7 @@ F autosetup/cc-lib.tcl 493c5935b5dd3bf9bd4eca89b07c8b1b1a9356d61783035144e21795f
F autosetup/cc-shared.tcl 163eda58c14cd662fd8a504bd2ad8a716ef4db7015dc1de0095d5de8dd601a4b
F autosetup/cc.tcl c0fcc50ca91deff8741e449ddad05bcd08268bc31177e613a6343bbd1fd3e45f
F autosetup/find_tclconfig.tcl e64886ffe3b982d4df42cd28ed91fe0b5940c2c5785e126c1821baf61bc86a7e
F autosetup/jimsh0.c a57c16e65dcffc9c76e496757cb3f7fb47e01ecbd1631a0a5e01751fc856f049
F autosetup/jimsh0.c 916bbdf8023fbda9937afae57d81a853d8c2ea00f2320aa27becbc33574f963d
F autosetup/pkg-config.tcl 4e635bf39022ff65e0d5434339dd41503ea48fc53822c9c5bde88b02d3d952ba
F autosetup/proj.tcl 6fc14ef82b19b77a95788ffbcfad7989b4e3cb4ce96a21dcb5cf7312f362fba9
F autosetup/sqlite-config.tcl 97dc76d332ae3344d02d15c99c57db0ac063537e9133cbee0520d48ba0eddc08
@@ -97,7 +97,7 @@ F ext/fts3/fts3_tokenizer.h 64c6ef6c5272c51ebe60fc607a896e84288fcbc3
F ext/fts3/fts3_tokenizer1.c c1de4ae28356ad98ccb8b2e3388a7fdcce7607b5523738c9afb6275dab765154
F ext/fts3/fts3_unicode.c de426ff05c1c2e7bce161cf6b706638419c3a1d9c2667de9cb9dc0458c18e226
F ext/fts3/fts3_unicode2.c 416eb7e1e81142703520d284b768ca2751d40e31fa912cae24ba74860532bf0f
F ext/fts3/fts3_write.c 2bee1c5828f6401adffd07ca64260aeb79d64138958273a56de8fa5e8759a0c1
F ext/fts3/fts3_write.c 66abcd0a35c722ad13ea73d4c31fc7773b9a01b35d434ad945cbf17b4f2563bd
F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9
F ext/fts3/tool/fts3cov.sh c331d006359456cf6f8f953e37f2b9c7d568f3863f00bb5f7eb87fea4ac01b73
F ext/fts3/tool/fts3view.c 413c346399159df81f86c4928b7c4a455caab73bfbc8cd68f950f632e5751674
@@ -113,7 +113,7 @@ F ext/fts5/fts5_buffer.c f1e6d0324d7c55329d340673befc26681a372a4d36086caa8d1ec7d
F ext/fts5/fts5_config.c e7d8dd062b44a66cd77e5a0f74f23a2354cd1f3f8575afb967b2773c3384f7f8
F ext/fts5/fts5_expr.c b8c32da1127bafaf10d6b4768b0dcb92285798524bed2d87a8686f99a8e8d259
F ext/fts5/fts5_hash.c a6266cedd801ab7964fa9e74ebcdda6d30ec6a96107fa24148ec6b7b5b80f6e0
F ext/fts5/fts5_index.c 8dbda33a9830764167d7697f1c9980c8a6ee74f5decb28206b963222583b8cdd
F ext/fts5/fts5_index.c 5e82963a6691ae519df1e018cc7971272a3da1fd47daca5029af0e9ce24f1a8b
F ext/fts5/fts5_main.c 42025174a556257287071e90516d3ab8115daf1dd525a301883544469a260014
F ext/fts5/fts5_storage.c 19bc7c4cbe1e6a2dd9849ef7d84b5ca1fcbf194cefc3e386b901e00e08bf05c2
F ext/fts5/fts5_tcl.c 7fb5a3d3404099075aaa2457307cb459bbc257c0de3dbd52b1e80a5b503e0329
@@ -122,7 +122,7 @@ F ext/fts5/fts5_test_tok.c 3cb0a9b508b30d17ef025ccddd26ae3dc8ddffbe76c057616e59a
F ext/fts5/fts5_tokenize.c 49aea8cc400a690a6c4f83c4cedc67f4f8830c6789c4ee343404f62bcaebca7b
F ext/fts5/fts5_unicode2.c 536a6dae41d16edadd6a6b58c56e2ebbb133f0dfe757562a2edbcdc9b8362e50
F ext/fts5/fts5_varint.c e64d2113f6e1bfee0032972cffc1207b77af63319746951bf1d09885d1dadf80
F ext/fts5/fts5_vocab.c ff0441c4ea165081e8152dec6d29056faa0cdc281a9f218a00e3d7aacc1958bc
F ext/fts5/fts5_vocab.c 23e263ad94ac357cfffd19bd7e001c3f15c4420fb10fa35b5993142127e780e6
F ext/fts5/fts5parse.y eb526940f892ade5693f22ffd6c4f2702543a9059942772526eac1fde256bb05
F ext/fts5/mkportersteps.tcl 5acf962d2e0074f701620bb5308155fa1e4a63ba
F ext/fts5/test/fts5_common.tcl c5aa7cf7148b6dcffb5b61520ae18212baf169936af734ab265143f59db328fe
@@ -162,7 +162,7 @@ F ext/fts5/test/fts5contentless4.test ec34dc69ef474ca9997dae6d91e072906e0e9a5a4b
F ext/fts5/test/fts5contentless5.test 38cd0392c730dc7090c550321ce3c24ba4c392bc97308b51a4180e9959dca7b5
F ext/fts5/test/fts5corrupt.test 237fce1c3261bb3a5bec333b0f0dbf5b105ec32627ef14cccbda3cfe13833193
F ext/fts5/test/fts5corrupt2.test 4a03a158c2cb617c9f76d26b35c1ef2534124bc0bbddcea38dfd5b170ebea27b
F ext/fts5/test/fts5corrupt3.test 43d6a836892d79ab738ab89b3b6f4ae46c07ee966193e4b357bbb14e7f81d5da
F ext/fts5/test/fts5corrupt3.test e489b51b6c4ded2a808e0f78bdbe126f6d369de41a59ac2717878e37fc3db0e8
F ext/fts5/test/fts5corrupt4.test dc08d19f5b8943e95a7778a7d8da592042504faf18dd93f68f7d7a0d7d7dd733
F ext/fts5/test/fts5corrupt5.test 73985d4fe6d8f0d5d5c7bcf79ae7c6522c376cd6ad710a0ff2f26e0c2e222abe
F ext/fts5/test/fts5corrupt6.test 2d72db743db7b5d9c9a6d0cfef24d799ed1aa5e8192b66c40e871a37ed9eed06
@@ -269,7 +269,7 @@ F ext/fts5/test/fts5update.test b8affd796e45c94a4d19ad5c26606ea06065a0f162a9562d
F ext/fts5/test/fts5update2.test c5baa76799ac605ebb8e5e21035db2014b396cef25c903eb96ba39b1d6f9f046
F ext/fts5/test/fts5version.test 44ab35566267b7618c090443de2d9ad84f633df5d20bf72e9bad199ae5fced84
F ext/fts5/test/fts5vocab.test 2a2bdb60d0998fa3124d541b6d30b019504918dc43a6584645b63a24be72f992
F ext/fts5/test/fts5vocab2.test bbba149c254375d00055930c1a501c9a51e80b0d20bf7b98f3e9fa3b03786373
F ext/fts5/test/fts5vocab2.test 4265137a3747b27deb1e2e2bde5654120c6de72bfed3238e67806d85af60fc4c
F ext/fts5/tool/fts5speed.tcl b0056f91a55b2d1a3684ec05729de92b042e2f85
F ext/fts5/tool/fts5txt2db.tcl c0d43c8590656f8240e622b00957b3a0facc49482411a9fdc2870b45c0c82f9f
F ext/fts5/tool/loadfts5.tcl 95b03429ee6b138645703c6ca192c3ac96eaf093
@@ -392,7 +392,7 @@ F ext/misc/remember.c add730f0f7e7436cd15ea3fd6a90fd83c3f706ab44169f7f048438b7d6
F ext/misc/rot13.c 51ac5f51e9d5fd811db58a9c23c628ad5f333c173f1fc53c8491a3603d38556c
F ext/misc/scrub.c 2a44b0d44c69584c0580ad2553f6290a307a49df4668941d2812135bfb96a946
F ext/misc/series.c 22c6d8f00cc1b5089b1b37392e9097e9df9a5db53be86daf9a7669d95bb179f4
F ext/misc/sha1.c cb5002148c2661b5946f34561701e9105e9d339b713ec8ac057fd888b196dcb9
F ext/misc/sha1.c 3030b5926b34bb09459200e100fae34e48c04077cf175381a7560f72bbf3d9cf
F ext/misc/shathree.c fd22d70620f86a0467acfdd3acd8435d5cb54eb1e2d9ff36ae44e389826993df
F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
F ext/misc/spellfix.c 693c8fd3293087fa821322967a97e59dfa24051e5d2ca7fa85790a4034db6fa4
@@ -416,9 +416,9 @@ F ext/misc/wholenumber.c 0fa0c082676b7868bf2fa918e911133f2b349bcdceabd1198bba5f6
F ext/misc/windirent.h 02211ce51f3034c675f2dbf4d228194d51b3ee05734678bad5106fff6292e60c
F ext/misc/zipfile.c 09e6e3a3ff40a99677de3c0bc6569bd5f4709b1844ac3d1c1452a456c5a62f1c
F ext/misc/zorder.c bddff2e1b9661a90c95c2a9a9c7ecd8908afab5763256294dd12d609d4664eee
F ext/qrf/README.md dd565fd1ca0c46ea37dbf4d496e368b9ecade768c92669640bc106e039629016
F ext/qrf/qrf.c ee3964addf075c87fc60e312a472eec35112ba34581edf655fcd5c34e2492602
F ext/qrf/qrf.h b4b3489b3b3683523fd248d15cf5945830643b036943efacdb772a3e00367aa2
F ext/qrf/README.md 86fc5c3c5e3eddbe54fc1235cbdc52b8c2c0732791d224345c3014cd45c4c0e7
F ext/qrf/qrf.c 425d02cffcc5b5fe9ff5817794bf751b1fdd6912f570c354272429ce1262b866
F ext/qrf/qrf.h 322d48537a5aa39c206c2ec0764a7938ea7662a8c25be1c4e9d742789609ba1e
F ext/rbu/rbu.c 801450b24eaf14440d8fd20385aacc751d5c9d6123398df41b1b5aa804bf4ce8
F ext/rbu/rbu1.test 25870dd7db7eb5597e2b4d6e29e7a7e095abf332660f67d89959552ce8f8f255
F ext/rbu/rbu10.test 7c22caa32c2ff26983ca8320779a31495a6555737684af7aba3daaf762ef3363
@@ -578,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 01f5e1bc688911b0c0accb0b152dccb19818cb301a90ba9772e3b329a7f625b0
F ext/wasm/GNUmakefile 2fe52720144ab481755c6df45f46dcf6c540f8622a5387ed280f1d0b26a6d28f
F ext/wasm/README-dist.txt f01081a850ce38a56706af6b481e3a7878e24e42b314cfcd4b129f0f8427066a
F ext/wasm/README.md 2e87804e12c98f1d194b7a06162a88441d33bb443efcfe00dc6565a780d2f259
F ext/wasm/SQLTester/GNUmakefile e0794f676d55819951bbfae45cc5e8d7818dc460492dc317ce7f0d2eca15caff
@@ -586,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/README.md aae128e14711dafac29e7b645ff1b6be42305b73f74b1303d8401351f8448631
F ext/wasm/api/EXPORTED_FUNCTIONS.c-pp 7ba933e8f1290cc65459dd371c0c9a031d96bdf14d7a2244fa761d9775117b90
F ext/wasm/api/README.md a905d5c6bfc3e2df875bd391d6d6b7b48d41b43bdee02ad115b47244781a7e81
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/pre-js.c-pp.js d73088472f91856a27837c0588ac355516cea441ff67d2990effc2ca0627bf21
F ext/wasm/api/sqlite3-api-glue.c-pp.js 9b33e3ee467791dec4fd1b444b12a8545dfbb6c8b28ac651c7bdc7661a3b5a5c
F ext/wasm/api/sqlite3-api-oo1.c-pp.js 7a6c85b260082d72a5c355caf37876763d8770a29d9e0587751e584c31bf2777
F ext/wasm/api/sqlite3-api-prologue.js fd836265adefbe4ed8a978326e0273bafcfb7d42f754b69a5d131a9031428c9f
F ext/wasm/api/sqlite3-api-worker1.c-pp.js 1041dd645e8e821c082b628cd8d9acf70c667430f9d45167569633ffc7567938
F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89
F ext/wasm/api/sqlite3-opfs-async-proxy.js 9654b565b346dc609b75d15337f20acfa7af7d9d558da1afeb9b6d8eaa404966
F ext/wasm/api/sqlite3-vfs-helper.c-pp.js 3f828cc66758acb40e9c5b4dcfd87fd478a14c8fb7f0630264e6c7fa0e57515d
F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js 26cb41d5a62f46a106b6371eb00fef02de3cdbfaa51338ba087a45f53028e0d0
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js aa330fa0e8ef35cbd92eb0d52e05fbaa07e61540c5cb164e693c82428ce1d763
F ext/wasm/api/sqlite3-vfs-kvvfs.c-pp.js f44a4396b2305b27dc81c00a1629fe4f615520f7fb11ed4e606255e365956ba2
F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js a2eea6442556867b589e04107796c6e1d04a472219529eeb45b7cd221d7d048b
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/c-pp-lite.c f38254fba42561728c2e4764a7ba8d68700091e7c2f4418112868c0daba16783
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,11 +625,11 @@ F ext/wasm/demo-worker1.js 08720227e98fa5b44761cf6e219269cee3e9dd0421d8d91459535
F ext/wasm/example_extra_init.c 2347cd69d19d839ef4e5e77b7855103a7fe3ef2af86f2e8c95839afd8b05862f
F ext/wasm/fiddle/fiddle-worker.js 7798af02e672e088ff192716f80626c8895e19301a65b8af6d5d12b2d13d2451
F ext/wasm/fiddle/fiddle.js 84fd75967e0af8b69d3dd849818342227d0f81d13db92e0dcbc63649b31a4893
F ext/wasm/fiddle/index.html a27b8127ef9ecf19612da93b2a6a73bdb3777b5c56b5450bb7200a94bc108ff9
F ext/wasm/fiddle/index.c-pp.html 72c7e5517217960b3809648429ea396a7cbad0ffb2c92f6a2f5703abecb27317 w ext/wasm/fiddle/index.html
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
@@ -646,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
@@ -656,7 +657,8 @@ 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 27a0769b40a813343f19e8fca2e9d55405f0311cb3cc5327f5702640df0182a0
F main.mk 822f9eda3e689748341597f4327a071c2b0ce41cc3ed477d72f2560b956eb5c0
F make.bat a136fd0b1c93e89854a86d5f4edcf0386d211e5d5ec2434480f6eea436c7420c
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
F mptest/crash01.test 61e61469e257df0850df4293d7d4d6c2af301421
@@ -666,7 +668,7 @@ F mptest/multiwrite01.test dab5c5f8f9534971efce679152c5146da265222d
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
F sqlite3.1 1b9c24374a85dfc7eb8fa7c4266ee0db4f9609cceecfc5481cd8307e5af04366
F sqlite3.pc.in e6dee284fba59ef500092fdc1843df3be8433323a3733c91da96690a50a5b398
F src/alter.c a02edd673c242698f42eecf150a0c6eef11d1cc8ebf555d5c0e9f2847c0e9e9d
F src/alter.c 06a27ab1cc064f5c6647e7fadfb12c8d7cafd35de1751f12419e1f2b28037540
F src/analyze.c 03bcfc083fc0cccaa9ded93604e1d4244ea245c17285d463ef6a60425fcb247d
F src/attach.c 7cf07d4fa42b9fc8662237c60c40b730326c30aa90ae5fffc0b18b2d726ebf61
F src/auth.c 54ab9c6c5803b47c0d45b76ce27eff22a03b4b1f767c5945a3a4eb13aa4c78dc
@@ -676,7 +678,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 611e07299d72ff04bbcb9e7109183467e30925d203c3e121ef9bb3cf6876289b
F src/build.c 4e1afafc56504ed6253e1b115c1502de4243c2287a0c799f4967fcd2d7716ad9
F src/callback.c 3605bbf02bd7ed46c79cd48346db4a32fc51d67624400539c0532f4eead804ad
F src/carray.c ff6081a31878fc34df8fa1052a9cbf17ddc22652544dcb3e2326886ed1053b55
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
@@ -691,7 +693,7 @@ 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
@@ -723,26 +725,26 @@ F src/os_win.c a89b501fc195085c7d6c9eec7f5bd782625e94bb2a96b000f4d009703df1083f
F src/os_win.h 4c247cdb6d407c75186c94a1e84d5a22cbae4adcec93fcae8d2bc1f956fd1f19
F src/pager.c a81461de271ac4886ad75b7ca2cca8157a48635820c4646cd2714acdc2c17e5f
F src/pager.h 6137149346e6c8a3ddc1eeb40aee46381e9bc8b0fcc6dda8a1efde993c2275b8
F src/parse.y 19ad77af6cd14c3b58d2979415c30de94172aa73c4cb1fd33f38407cfb2c311d
F src/parse.y e31046636c50ba4a1cf4d12c7a6112a31b11975b1ade84a45f2109a30e8f358d
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 4fdceb103ba8761bf3d68108c3a6e6499be913a2490a7c384ee79ee547a9517c
F src/sqlite.h.in f1363321ca55cc2feaa289e9fe6dfb08102a28c54edf005564711a2348b06eef
F src/select.c 6a509cddd815d64f6141e539fff633a518a393772a44dffb4490f7fc3f0d83a9
F src/shell.c.in 8ec8bf61a3322c5e29c286d4aecbc82e25157df9244cb1a1a1c4e2c49de8b64d
F src/sqlite.h.in 706cacea5308b0244fb6cec92e08310fb427a125375c64137cc1f878ae4cf5c0
F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479
F src/sqlite3ext.h 5d5330f5f8461f5ce74960436ddcfa53ecd09c2b8b23901e22ae38aec3243998
F src/sqliteInt.h 993fed08967a58d906093bae969e4831e051afcf36f15c90e16f14fa7d772eaf
F src/sqliteInt.h f8b6a6bdb92aaeeedd768d27650a39f543c4b7cc66534c564e2ec249ed80e210
F src/sqliteLimit.h 0a5516b4ec192a205c541e05f67009028a9451dc6678aae4cf8e68596903c246
F src/status.c 7565d63a79aa2f326339a24a0461a60096d0bd2bce711fefb50b5c89335f3592
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
F src/tclsqlite.c 381384fbe3cf342115f9ad01208fa81092e9a2156a4ea4d44de87852b8df3a8a
F src/tclsqlite.c ec2ad3fbf5390c3d4307bc9d1f2a37a9c5d726b5309769dc770ab37f6812152d
F src/tclsqlite.h 614b3780a62522bc9f8f2b9fb22689e8009958e7aa77e572d0f3149050af348a
F src/test1.c 0e71fbcb484a271564e98e0158192c28c24f5521594218c3ba48bcb4cf634f91
F src/test2.c 62f0830958f9075692c29c6de51b495ae8969e1bef85f239ffcd9ba5fb44a5ff
@@ -792,7 +794,7 @@ F src/test_vfs.c b4135c1308516adf0dfd494e6d6c33114e03732be899eace0502919b674586b
F src/test_window.c 6d80e11fba89a1796525e6f0048ff0c7789aa2c6b0b11c80827dc1437bd8ea72
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c cb3294cf23c11106b50d9af6998a6c1bf389b52e15b17698c9fab97bbaa9b37f
F src/tokenize.c ca1382f02a3990d1a770acd5c55731dee10cd644465b2855eca33e4d2ea299ed
F src/treeview.c 3ce7ac9835d2d70cc1c868b01b747ae8a062322e155701e58e3d62ca79aada7a
F src/trigger.c 4983328a32feff8774503da634688aad96c7febf1e838ce9df92c3672f1fa64f
F src/update.c 3e5e7ff66fa19ebe4d1b113d480639a24cc1175adbefabbd1a948a07f28e37cf
@@ -834,12 +836,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 6bf0a5ff9eaa1ba1f654e5263c1ea6f2d2e1216c8cd046476fed571abd579cdf
F test/alterfault.test 2bb3103954ea60f2e2777b1ae12e79ec3e1fd278f2b1398ad316c68835a62898
F test/alterlegacy.test f38c6d06cda39e1f7b955bbce57f2e3ef5b7cb566d3d1234502093e228c15811
F test/altermalloc.test 167a47de41b5c638f5f5c6efb59784002b196fff70f98d9b4ed3cd74a3fb80c9
F test/altermalloc2.test 17fb3724c4b004c469c27dc4ef181608aa644555fbd3f3236767584f73747c81
@@ -1209,7 +1213,7 @@ F test/fts4merge.test 57d093660a5093ae6e9fbd2d17592a88b45bbd66db2703c4b640b28828
F test/fts4merge2.test 5faa558d1b672f82b847d2a337465fa745e46891
F test/fts4merge3.test 8d9ccb4a3d41c4c617a149d6c4b13ad02de797d0
F test/fts4merge4.test 66fce89934cd9508cbdc67de486558c34912ffb2e8ffe5c9a1bbb9b8a4408ba7
F test/fts4merge5.test 69932d85cda8a1c4dcfb742865900ed8fbda51724b8cf9a45bbe226dfd06c596
F test/fts4merge5.test 987af90c930e8555f74ab994f597431caec7f8defc52de7718655c32da07af9e
F test/fts4min.test 1c11e4bde16674a0c795953509cbc3731a7d9cbd1ddc7f35467bf39d632d749f
F test/fts4noti.test d5d933705b1b1516b67a5e3f8e514ecb19c6522fb3357bb744776d48427c2292
F test/fts4onepass.test d69ddc4ee3415e40b0c5d1d0408488a87614d4f63ba9c44f3e52db541d6b7cc7
@@ -1436,7 +1440,7 @@ F test/mmap4.test 2e2b4e32555b58da15176e6fe750f17c9dcf7f93
F test/mmapcorrupt.test 470fb44fe92e99c1d23701d156f8c17865f5b027063c9119dcfdb842791f4465
F test/mmapfault.test d4c9eff9cd8c2dc14bc43e71e042f175b0a26fe3
F test/mmapwarm.test 2272005969cd17a910077bd5082f70bc1fefad9a875afec7fc9af483898ecaf3
F test/modeA.clitest 73d091c785671f629af0900f0654a28468bbba5685a545c1513049c57f1b8cbc
F test/modeA.clitest e0c63c28e2651dd322e21de683fec7d247028b80f7a753663244ba0087215dc8
F test/multiplex.test d74c034e52805f6de8cc5432cef8c9eb774bb64ec29b83a22effc8ca4dac1f08
F test/multiplex2.test 580ca5817c7edbe4cc68fa150609c9473393003a
F test/multiplex3.test fac575e0b1b852025575a6a8357701d80933e98b5d2fe6d35ddaa68f92f6a1f7
@@ -1506,9 +1510,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 dfdbbd2b5f2808f5ff6598c8632ee002389b690eb5016450c2f847d8e51b9b92
F test/qrf01.test 48c86e90f606739e67b73937babb662ef0edf1ede06954489c588815fa17b560
F test/qrf02.test 39b4afdc000bedccdafc0aecf17638df67a67aaa2d2942865ae6abcc48ba0e92
F test/qrf03.test 9d88aeb5cdd53f050b7ab9bd203281f7c9d063c33f22f8808e441b7ac0874ccf
F test/qrf03.test ad7fa339fb46b26b08625f3a77a4827bdee9f0dad402d8ddc1fcb8f05aa64a54
F test/qrf04.test 0894692c998d2401dcc33449c02051b503ecce0c94217be54fb007c82d2d1379
F test/queryonly.test 5f653159e0f552f0552d43259890c1089391dcca
F test/quick.test 1681febc928d686362d50057c642f77a02c62e57
F test/quickcheck.test a4b7e878cd97e46108291c409b0bf8214f29e18fddd68a42bc5c1375ad1fb80a
@@ -1602,7 +1607,7 @@ 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 8fbb73650b685766cdf4b5e8b5d12dee72664f44195eeb788d0f52fce08e04eb
F test/shell1.test f377091779c4099619e78e1f55a0a12513eaac55d5073a14ca6545c6742c746c
F test/shell2.test 103140814bdc7508aa41dd3462413cbc4aa84b4261112cb8d501d74275cb7d48
F test/shell3.test 840192774cc4edf7653520c0434a311c7477b9bc324abbc7bd2887915792fa8c
F test/shell4.test e25580a792b7b54560c3a76b6968bd8189261f38979fe28e6bc6312c5db280db
@@ -1611,8 +1616,8 @@ F test/shell6.test e3b883b61d4916b6906678a35f9d19054861123ad91b856461e0a456273bd
F test/shell7.test 43fd8e511c533bab5232e95c7b4be93b243451709e89582600d4b6e67693d5c3
F test/shell8.test 641cf21a99c59404c24e3062923734951c4099a6b6b6520de00cf7a1249ee871
F test/shell9.test 8742a5b390cdcef6369f5aa223e415aa4255a4129ef249b177887dc635a87209
F test/shellA.test 4ece22f204532fb04ab702c5b9c70d8680ff610bff1e82c849c16a7b5cd57d83
F test/shellB.test ca8a5ce5b9a59098732fa140c911162f0306f70239c6c2de6da9b718ca304cad
F test/shellA.test 4f57da76ed1f6d4a4eba0b2a27cdef4c756f1da2c813dc7034079e4df493110f
F test/shellB.test de879b1ea7c25daf1a06b2c882b45a5d002e6580c81c57169ce47084cc6afb6b
F test/shmlock.test 9f1f729a7fe2c46c88b156af819ac9b72c0714ac6f7246638a73c5752b5fd13c
F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3
F test/show_speedtest1_rtree.tcl 32e6c5f073d7426148a6936a0408f4b5b169aba5
@@ -1697,7 +1702,7 @@ F test/temptrigfault.tes fc5918e64f3867156fefe7cfca9d8e1f495134a5229b2b511b0dc11
F test/temptrigger.test a00f258ed8d21a0e8fd4f322f15e8cfb5cef2e43655670e07a753e3fb4769d61
F test/tester.tcl 463ae33b8bf75ac77451df19bd65e7c415c2e9891227c7c9e657d0a2d8e1074a
F test/testloadext.c 862b848783eaed9985fbce46c65cd214664376b549fae252b364d5d1ef350a27
F test/testrunner.tcl 86179a8e78997e9257cb8f738c5624cb23897da5297855578ba74715e64f1602 x
F test/testrunner.tcl 6cbda5d6ff9b26b11993f1e1ddd57d9521cec7c31676ad323a40a6821ce43ad6 x
F test/testrunner_data.tcl c507a9afa911c03446ed90442ffd4a98aca02882c3d51bd1177c24795674def8
F test/testrunner_estwork.tcl 81e2ae10238f50540f42fbf2d94913052a99bfb494b69e546506323f195dcff9
F test/thread001.test a0985c117eab62c0c65526e9fa5d1360dd1cac5b03bde223902763274ce21899
@@ -2122,7 +2127,7 @@ F tool/mkautoconfamal.sh 647dada5e34c466bef62a4408e1c99a7e5e1922805479dd57944f33
F tool/mkccode.tcl c42a8f8cf78f92e83795d5447460dbce7aaf78a3bbf9082f1507dc71a3665f3c x
F tool/mkcombo.tcl 2a5189b219c4a495e1ff7fc980bd568d3cfb82ae9d50c84e77f7a161e96fc132
F tool/mkctimec.tcl 3fb5cad05922f5da61262cb6bcd5868a34e94a49ca8833ae2d7796e7df075576 x
F tool/mkkeywordhash.c 6b0be901c47f9ad42215fc995eb2f4384ac49213b1fba395102ec3e999acf559
F tool/mkkeywordhash.c 82d5af1d0e677900739fba59155cddac172d8c712c2d91ab73d6e6bcb30060f0
F tool/mkmsvcmin.tcl d76c45efda1cce2d4005bcea7b8a22bb752e3256009f331120fb4fecb14ebb7a
F tool/mkopcodec.tcl 33d20791e191df43209b77d37f0ff0904620b28465cca6990cf8d60da61a07ef
F tool/mkopcodeh.tcl 2b4e6967a670ef21bf53a164964c35c6163277d002a4c6f56fa231d68c88d023
@@ -2176,8 +2181,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 c4cc80eb281f7c8da3d62fec03b2258ce8d0063fc9cfff61f2cbcdd01e13c391
R 2c80bedbc8d39f5ed20d2f9111a22a22
P 951b4533509fae971e252be3ecd3623ac2bdce16220e0ce372e600f728f80f69 65202440874a7fea5aba262e8e78b97c833147d47837a99f301eca968f9a78b1
R 83ef1a079a33c33622b152a79e70f1ed
U dan
Z 4cc55d003d3e663865fa89fedb536d6f
Z 628c58958f6e69e505f16b7101c16084
# Remove this line to create a well-formed Fossil manifest.
+1 -1
View File
@@ -1 +1 @@
951b4533509fae971e252be3ecd3623ac2bdce16220e0ce372e600f728f80f69
230fd5533e07ec36f1938f34153c9ef97a7acd6604d023b56dfc5269ca6173c2
+725 -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
@@ -2309,6 +2331,701 @@ 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 */
int t, /* Token type */
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[] */
if( t==TK_ILLEGAL ){
*pRes = 1;
return SQLITE_OK;
}
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, t, &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, t, &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
*/
@@ -2319,6 +3036,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));
}
+2 -2
View File
@@ -2059,8 +2059,8 @@ void sqlite3ChangeCookie(Parse *pParse, int iDb){
** The estimate is conservative. It might be larger that what is
** really needed.
*/
static int identLength(const char *z){
int n;
static i64 identLength(const char *z){
i64 n;
for(n=0; *z; n++, z++){
if( *z=='"' ){ n++; }
}
+1
View File
@@ -18,6 +18,7 @@
#if defined(_MSC_VER) && defined(_WIN32)
#include "windows.h"
#include <profileapi.h>
__inline sqlite3_uint64 sqlite3Hwtime(void){
+27 -7
View File
@@ -1826,22 +1826,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],
+2 -2
View File
@@ -2430,8 +2430,8 @@ void sqlite3SubqueryColumnTypes(
}
}
if( zType ){
const i64 k = sqlite3Strlen30(zType);
n = sqlite3Strlen30(pCol->zCnName);
const i64 k = strlen(zType);
n = strlen(pCol->zCnName);
pCol->zCnName = sqlite3DbReallocOrFree(db, pCol->zCnName, n+k+2);
pCol->colFlags &= ~(COLFLAG_HASTYPE|COLFLAG_HASCOLL);
if( pCol->zCnName ){
+280 -183
View File
@@ -1428,14 +1428,16 @@ static const char *qrfQuoteNames[] =
#define MODE_List 12 /* One record per line with a separator */
#define MODE_Markdown 13 /* Markdown formatting */
#define MODE_Off 14 /* No query output shown */
#define MODE_QBox 15 /* BOX with SQL-quoted content */
#define MODE_Quote 16 /* Quote values as for SQL */
#define MODE_Table 17 /* MySQL-style table formatting */
#define MODE_Tabs 18 /* Tab-separated values */
#define MODE_Tcl 19 /* Space-separated list of TCL strings */
#define MODE_Www 20 /* Full web-page output */
#define MODE_Psql 15 /* Similar to psql */
#define MODE_QBox 16 /* BOX with SQL-quoted content */
#define MODE_Quote 17 /* Quote values as for SQL */
#define MODE_Split 18 /* Split-column mode */
#define MODE_Table 19 /* MySQL-style table formatting */
#define MODE_Tabs 20 /* Tab-separated values */
#define MODE_Tcl 21 /* Space-separated list of TCL strings */
#define MODE_Www 22 /* Full web-page output */
#define MODE_BUILTIN 20 /* Maximum built-in mode */
#define MODE_BUILTIN 22 /* Maximum built-in mode */
#define MODE_BATCH 50 /* Default mode for batch processing */
#define MODE_TTY 51 /* Default mode for interactive processing */
#define MODE_USER 75 /* First user-defined mode */
@@ -1456,6 +1458,7 @@ struct ModeInfo {
unsigned char bHdr; /* Show headers by default. 0: n/a, 1: no 2: yes */
unsigned char eStyle; /* Underlying QRF style */
unsigned char eCx; /* 0: other, 1: line, 2: columnar */
unsigned char mFlg; /* Flags. 1=border-off 2=split-column */
};
/* String constants used by built-in modes */
@@ -1466,28 +1469,30 @@ static const char *aModeStr[] =
/* 9 10 11 12 */
static const ModeInfo aModeInfo[] = {
/* zName eCSep eRSep eNull eText eHdr eBlob bHdr eStyle eCx */
{ "ascii", 7, 6, 9, 1, 1, 1, 1, 12, 0 },
{ "box", 0, 0, 9, 1, 1, 1, 2, 1, 2 },
{ "c", 4, 1, 10, 5, 5, 4, 1, 12, 0 },
{ "column", 0, 0, 9, 1, 1, 1, 2, 2, 2 },
{ "count", 0, 0, 0, 0, 0, 0, 0, 3, 0 },
{ "csv", 4, 5, 9, 3, 3, 3, 1, 12, 0 },
{ "html", 0, 0, 9, 4, 4, 1, 2, 7, 0 },
{ "insert", 0, 0, 10, 2, 2, 2, 1, 8, 0 },
{ "jatom", 4, 1, 11, 6, 6, 5, 1, 12, 0 },
{ "jobject", 0, 1, 11, 6, 6, 5, 0, 10, 0 },
{ "json", 0, 0, 11, 6, 6, 0, 0, 9, 0 },
{ "line", 0, 1, 9, 1, 1, 0, 0, 11, 1 },
{ "list", 2, 1, 9, 1, 1, 1, 1, 12, 0 },
{ "markdown", 0, 0, 9, 1, 1, 1, 2, 13, 2 },
{ "off", 0, 0, 0, 0, 0, 0, 0, 14, 0 },
{ "qbox", 0, 0, 9, 2, 1, 2, 2, 1, 2 },
{ "quote", 4, 1, 10, 2, 2, 2, 1, 12, 0 },
{ "table", 0, 0, 9, 1, 1, 1, 2, 19, 2 },
{ "tabs", 8, 1, 9, 3, 3, 1, 1, 12, 0 },
{ "tcl", 3, 1, 12, 5, 5, 4, 1, 12, 0 },
{ "www", 0, 0, 9, 4, 4, 1, 2, 7, 0 }
/* zName eCSep eRSep eNull eText eHdr eBlob bHdr eStyle eCx mFlg */
{ "ascii", 7, 6, 9, 1, 1, 0, 1, 12, 0, 0 },
{ "box", 0, 0, 9, 1, 1, 0, 2, 1, 2, 0 },
{ "c", 4, 1, 10, 5, 5, 4, 1, 12, 0, 0 },
{ "column", 0, 0, 9, 1, 1, 0, 2, 2, 2, 0 },
{ "count", 0, 0, 0, 0, 0, 0, 0, 3, 0, 0 },
{ "csv", 4, 5, 9, 3, 3, 0, 1, 12, 0, 0 },
{ "html", 0, 0, 9, 4, 4, 0, 2, 7, 0, 0 },
{ "insert", 0, 0, 10, 2, 2, 0, 1, 8, 0, 0 },
{ "jatom", 4, 1, 11, 6, 6, 0, 1, 12, 0, 0 },
{ "jobject", 0, 1, 11, 6, 6, 0, 0, 10, 0, 0 },
{ "json", 0, 0, 11, 6, 6, 0, 0, 9, 0, 0 },
{ "line", 0, 1, 9, 1, 1, 0, 0, 11, 1, 0 },
{ "list", 2, 1, 9, 1, 1, 0, 1, 12, 0, 0 },
{ "markdown", 0, 0, 9, 1, 1, 0, 2, 13, 2, 0 },
{ "off", 0, 0, 0, 0, 0, 0, 0, 14, 0, 0 },
{ "psql", 0, 0, 9, 1, 1, 0, 2, 19, 2, 1 },
{ "qbox", 0, 0, 10, 2, 1, 0, 2, 1, 2, 0 },
{ "quote", 4, 1, 10, 2, 2, 0, 1, 12, 0, 0 },
{ "split", 0, 0, 9, 1, 1, 0, 1, 2, 2, 2 },
{ "table", 0, 0, 9, 1, 1, 0, 2, 19, 2, 0 },
{ "tabs", 8, 1, 9, 3, 3, 0, 1, 12, 0, 0 },
{ "tcl", 3, 1, 12, 5, 5, 4, 1, 12, 0, 0 },
{ "www", 0, 0, 9, 4, 4, 0, 2, 7, 0, 0 }
}; /* | / / | / / | | \
** | / / | / / | | \_ 2: columnar
** Index into aModeStr[] | / / | | 1: line
@@ -1497,12 +1502,13 @@ static const ModeInfo aModeInfo[] = {
** v-------------------' | hdrs? | The QRF style
** 0: n/a blob | v-----'
** 1: plain v_---------' 0: n/a
** 2: sql 0: n/a 1: no
** 2: sql 0: auto 1: no
** 3: csv 1: as-text 2: yes
** 4: html 2: sql
** 5: c 3: hex
** 6: json 4: c
** 5: json
** 6: size
******************************************************************/
/*
** These are the column/row/line separators used by the various
@@ -1529,6 +1535,7 @@ static const ModeInfo aModeInfo[] = {
** contains.
*/
static void modeFree(Mode *p){
u8 autoExplain = p->autoExplain;
free(p->spec.aWidth);
free(p->spec.aAlign);
free(p->spec.zColumnSep);
@@ -1537,6 +1544,7 @@ static void modeFree(Mode *p){
free(p->spec.zNull);
memset(p, 0, sizeof(*p));
p->spec.iVersion = 1;
p->autoExplain = autoExplain;
}
/*
@@ -1600,8 +1608,8 @@ static void modeSetStr(char **az, const char *zNew){
static void modeChange(ShellState *p, unsigned char eMode){
const ModeInfo *pI;
if( eMode<ArraySize(aModeInfo) ){
pI = &aModeInfo[eMode];
Mode *pM = &p->mode;
pI = &aModeInfo[eMode];
pM->eMode = eMode;
if( pI->eCSep ) modeSetStr(&pM->spec.zColumnSep, aModeStr[pI->eCSep]);
if( pI->eRSep ) modeSetStr(&pM->spec.zRowSep, aModeStr[pI->eRSep]);
@@ -1610,6 +1618,17 @@ static void modeChange(ShellState *p, unsigned char eMode){
pM->spec.eBlob = pI->eBlob;
pM->spec.bTitles = pI->bHdr;
pM->spec.eTitle = pI->eHdr;
if( pI->mFlg & 0x01 ){
pM->spec.bBorder = QRF_No;
}else{
pM->spec.bBorder = QRF_Auto;
}
if( pI->mFlg & 0x02 ){
pM->spec.bSplitColumn = QRF_Yes;
pM->bAutoScreenWidth = 1;
}else{
pM->spec.bSplitColumn = QRF_No;
}
}else if( eMode>=MODE_USER && eMode-MODE_USER<p->nSavedModes ){
modeFree(&p->mode);
modeDup(&p->mode, &p->aSavedModes[eMode-MODE_USER].mode);
@@ -2101,34 +2120,37 @@ static void interrupt_handler(int NotUsed){
/* Try to determine the screen width. Use the default if unable.
*/
int shellScreenWidth(void){
if( stdout_tty_width>0 ) return stdout_tty_width;
if( stdout_tty_width>0 ){
return stdout_tty_width;
}else{
#if defined(TIOCGSIZE)
struct ttysize ts;
if( ioctl(STDIN_FILENO, TIOCGSIZE, &ts)>=0
|| ioctl(STDOUT_FILENO, TIOCGSIZE, &ts)>=0
|| ioctl(STDERR_FILENO, TIOCGSIZE, &ts)>=0
){
return ts.ts_cols;
}
struct ttysize ts;
if( ioctl(STDIN_FILENO, TIOCGSIZE, &ts)>=0
|| ioctl(STDOUT_FILENO, TIOCGSIZE, &ts)>=0
|| ioctl(STDERR_FILENO, TIOCGSIZE, &ts)>=0
){
return ts.ts_cols;
}
#elif defined(TIOCGWINSZ)
struct winsize ws;
if( ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)>=0
|| ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws)>=0
|| ioctl(STDERR_FILENO, TIOCGWINSZ, &ws)>=0
){
return ws.ws_col;
}
struct winsize ws;
if( ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)>=0
|| ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws)>=0
|| ioctl(STDERR_FILENO, TIOCGWINSZ, &ws)>=0
){
return ws.ws_col;
}
#elif defined(_WIN32)
CONSOLE_SCREEN_BUFFER_INFO csbi;
if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)
|| GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), &csbi)
|| GetConsoleScreenBufferInfo(GetStdHandle(STD_INPUT_HANDLE), &csbi)
){
return csbi.srWindow.Right - csbi.srWindow.Left + 1;
}
CONSOLE_SCREEN_BUFFER_INFO csbi;
if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)
|| GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), &csbi)
|| GetConsoleScreenBufferInfo(GetStdHandle(STD_INPUT_HANDLE), &csbi)
){
return csbi.srWindow.Right - csbi.srWindow.Left + 1;
}
#endif
#define DEFAULT_SCREEN_WIDTH 80
return DEFAULT_SCREEN_WIDTH;
return DEFAULT_SCREEN_WIDTH;
}
}
#if (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE)
@@ -3181,6 +3203,13 @@ static int shell_exec(
if( pArg->mode.bAutoScreenWidth ){
spec.nScreenWidth = shellScreenWidth();
}
if( spec.eBlob==QRF_BLOB_Auto ){
switch( spec.eText ){
case QRF_TEXT_Sql: spec.eBlob = QRF_BLOB_Sql; break;
case QRF_TEXT_Json: spec.eBlob = QRF_BLOB_Json; break;
default: spec.eBlob = QRF_BLOB_Text; break;
}
}
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
if( pArg->expert.pExpert ){
@@ -3548,6 +3577,8 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){
savedMode = p->mode;
p->mode.spec.zTableName = (char*)zTable;
p->mode.eMode = MODE_Insert;
p->mode.spec.eText = QRF_TEXT_Sql;
p->mode.spec.eBlob = QRF_BLOB_Sql;
p->mode.spec.bTitles = QRF_No;
rc = shell_exec(p, sSelect.zTxt, 0);
if( (rc&0xff)==SQLITE_CORRUPT ){
@@ -6865,7 +6896,9 @@ static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){
pState->db, "main", recoverSqlCb, (void*)pState
);
sqlite3_recover_config(p, 789, (void*)zRecoveryDb); /* Debug use only */
if( !pState->bSafeMode ){
sqlite3_recover_config(p, 789, (void*)zRecoveryDb); /* Debug use only */
}
sqlite3_recover_config(p, SQLITE_RECOVER_LOST_AND_FOUND, (void*)zLAF);
sqlite3_recover_config(p, SQLITE_RECOVER_ROWIDS, (void*)&bRowids);
sqlite3_recover_config(p, SQLITE_RECOVER_FREELIST_CORRUPT,(void*)&bFreelist);
@@ -7640,9 +7673,9 @@ static int modeTitleDsply(ShellState *p, int bAll){
**
** USAGE: .mode [MODE] [OPTIONS]
**
** Change the output mode to MODE and/or apply OPTIONS to the
** output mode. If no arguments, show the current output mode
** and relevant options.
** Change the output mode to MODE and/or apply OPTIONS to the output mode.
** Arguments are processed from left to right. If no arguments, show the
** current output mode and relevant options.
**
** Options:
** --align STRING Set the alignment of text in columnar modes
@@ -7650,17 +7683,22 @@ static int modeTitleDsply(ShellState *p, int bAll){
** meaning "left", "centered", and "right", with
** one letter per column starting from the left.
** Unspecified alignment defaults to 'L'.
** --blob-quote ARG ARG can be "auto", "text", "sql", "hex", "tcl",
** "json", or "size". Default is "auto".
** --border on|off Show outer border on "box" and "table" modes.
** --charlimit N Set the maximum number of output characters to
** show for any single SQL value to N. Longer values
** truncated. Zero means "no limit".
** --colsep STRING Use STRING as the column separator
** --escape ESC Enable/disable escaping of control characters
** in output. ESC can be "off", "ascii", or
** "symbol".
** found in the output. ESC can be "off", "ascii",
** or "symbol".
** --linelimit N Set the maximum number of output lines to show for
** any single SQL value to N. Longer values are
** truncated. Zero means "no limit". Only works
** in "line" mode and in columnar modes.
** --limits L,C Shorthand for "--linelimit L --charlimit C".
** Or "off" to mean "0,0". Or "on" for "5,300".
** --list List available modes
** --null STRING Render SQL NULL values as the given string
** --once Setting changes to the right are reverted after
@@ -7668,10 +7706,10 @@ static int modeTitleDsply(ShellState *p, int bAll){
** --quote ARG Enable/disable quoting of text. ARG can be
** "off", "on", "sql", "csv", "html", "tcl",
** or "json". "off" means show the text as-is.
** "on and "sql" are synonyms.
** "on" is an alias for "sql".
** --reset Changes all mode settings back to their default.
** --rowsep STRING Use STRING as the row separator
** --screenwidth N Declare the screen width of the output device
** --sw|--screenwidth N Declare the screen width of the output device
** to be N characters. An attempt may be made to
** wrap output text to fit within this limit. Zero
** means "no limit". Or N can be "auto" to set the
@@ -7679,7 +7717,7 @@ static int modeTitleDsply(ShellState *p, int bAll){
** --tablename NAME Set the name of the table for "insert" mode.
** --tag NAME Save mode to the left as NAME.
** --textjsonb BOOLEAN If enabled, JSONB text is displayed as text JSON.
** --title ARG Whether or not to show column headers, and if so
** --title ARG Whether or not to show column headers, and if so
** how to encode them. ARG can be "off", "on",
** "sql", "csv", "html", "tcl", or "json".
** -v|--verbose Verbose output
@@ -7753,6 +7791,29 @@ static int dotCmdMode(ShellState *p){
"Should contain only characters L, C, and R.");
return 1;
}
}else if( pickStr(z,0,"-blob","-blob-quote","")>=0 ){
if( (++i)>=nArg ){
dotCmdError(p, i-1, "missing argument", 0);
return 1;
}
k = pickStr(azArg[i], 0,
"auto", "text", "sql", "hex", "tcl", "json", "size", "");
/* 0 1 2 3 4 5 6
** Must match QRF_BLOB_xxxx values. See also tag-20251124a */
if( k>=0 ){
p->mode.spec.eBlob = k & 0xff;
}
chng = 1;
}else if( optionMatch(z,"border") ){
if( (++i)>=nArg ){
dotCmdError(p, i-1, "missing argument", 0);
return 1;
}
k = pickStr(azArg[i], 0, "auto", "off", "on", "");
if( k>=0 ){
p->mode.spec.bBorder = k & 0x3;
}
chng = 1;
}else if( 0<=(k=pickStr(z,0,"-charlimit","-linelimit","")) ){
int w; /* 0 1 */
if( i+1>=nArg ){
@@ -7783,11 +7844,10 @@ static int dotCmdMode(ShellState *p){
}else if( optionMatch(z,"escape") ){
/* See similar code at tag-20250224-1 */
char *zErr = 0;
if( i+1>=nArg ){
dotCmdError(p, i, "missing argument", 0);
if( (++i)>=nArg ){
dotCmdError(p, i-1, "missing argument", 0);
return 1;
}
i++; /* 0 1 2 <-- One less than QRF_ESC_ */
} /* 0 1 2 <-- One less than QRF_ESC_ */
k = pickStr(azArg[i],&zErr,"off","ascii","symbol","");
if( k<0 ){
dotCmdError(p, i, "unknown escape type", "%s", zErr);
@@ -7796,6 +7856,30 @@ static int dotCmdMode(ShellState *p){
}
p->mode.spec.eEsc = k+1;
chng = 1;
}else if( optionMatch(z,"limits") ){
if( (++i)>=nArg ){
dotCmdError(p, i-1, "missing argument", 0);
return 1;
}
k = pickStr(azArg[i],0,"on","off","");
if( k==0 ){
p->mode.spec.nLineLimit = 5;
p->mode.spec.nCharLimit = 300;
}else if( k==1 ){
p->mode.spec.nLineLimit = 0;
p->mode.spec.nCharLimit = 0;
}else{
int L, C;
int nNum = sscanf(azArg[i], "%d,%d", &L, &C);
if( nNum!=2 || L<0 || C<0 ){
dotCmdError(p, i, "bad argument", "Should be \"L,C\" where L and C"
" are unsigned integers");
return 1;
}
p->mode.spec.nLineLimit = L;
p->mode.spec.nCharLimit = C;
}
chng = 1;
}else if( optionMatch(z,"list") ){
int ii;
cli_puts("available modes:", p->out);
@@ -7807,6 +7891,17 @@ static int dotCmdMode(ShellState *p){
cli_printf(p->out, " %s", p->aSavedModes[ii].zTag);
}
cli_puts(" batch tty\n", p->out);
chng = 1; /* Not really a change, but we still want to suppress the
** "current mode" output */
}else if( optionMatch(z,"once") ){
p->nPopMode = 0;
modePush(p);
p->nPopMode = 1;
}else if( optionMatch(z,"noquote") ){
/* (undocumented legacy) --noquote always turns quoting off */
p->mode.spec.eText = QRF_TEXT_Plain;
p->mode.spec.eBlob = QRF_BLOB_Auto;
chng = 1;
}else if( optionMatch(z,"quote") ){
if( i+1<nArg
&& azArg[i+1][0]!='-'
@@ -7833,64 +7928,50 @@ static int dotCmdMode(ShellState *p){
}
switch( k ){
case 1: /* on */
modeSetStr(&p->mode.spec.zNull, "NULL");
/* Fall through */
case 2: /* sql */
p->mode.spec.eText = QRF_TEXT_Sql;
p->mode.spec.eBlob = QRF_BLOB_Sql;
break;
case 3: /* csv */
p->mode.spec.eText = QRF_TEXT_Csv;
p->mode.spec.eBlob = QRF_BLOB_Text;
break;
case 4: /* html */
p->mode.spec.eText = QRF_TEXT_Html;
p->mode.spec.eBlob = QRF_BLOB_Text;
break;
case 5: /* tcl */
p->mode.spec.eText = QRF_TEXT_Tcl;
p->mode.spec.eBlob = QRF_BLOB_Text;
break;
case 6: /* json */
p->mode.spec.eText = QRF_TEXT_Json;
p->mode.spec.eBlob = QRF_BLOB_Json;
break;
default: /* off */
default: /* off */
p->mode.spec.eText = QRF_TEXT_Plain;
p->mode.spec.eBlob = QRF_BLOB_Text;
break;
}
chng = 1;
}else if( optionMatch(z,"once") ){
p->nPopMode = 0;
modePush(p);
p->nPopMode = 1;
}else if( optionMatch(z,"noquote") ){
/* (undocumented legacy) --noquote always turns quoting off */
p->mode.spec.eText = QRF_TEXT_Plain;
p->mode.spec.eBlob = QRF_BLOB_Text;
chng = 1;
}else if( optionMatch(z,"reset") ){
int saved_eMode = p->mode.eMode;
modeFree(&p->mode);
modeChange(p, saved_eMode);
}else if( optionMatch(z,"screenwidth") ){
if( i+1>=nArg ){
dotCmdError(p, i, "missing argument", 0);
}else if( optionMatch(z,"screenwidth") || optionMatch(z,"sw") ){
if( (++i)>=nArg ){
dotCmdError(p, i-1, "missing argument", 0);
return 1;
}
k = pickStr(azArg[i+1],0,"off","auto","");
k = pickStr(azArg[i],0,"off","auto","");
if( k==0 ){
p->mode.bAutoScreenWidth = 0;
p->mode.spec.nScreenWidth = 0;
}else if( k==1 ){
p->mode.bAutoScreenWidth = 1;
}else{
i64 w = integerValue(azArg[i+1]);
i64 w = integerValue(azArg[i]);
p->mode.bAutoScreenWidth = 0;
if( w<0 ) w = 0;
if( w>QRF_MAX_WIDTH ) w = QRF_MAX_WIDTH;
p->mode.spec.nScreenWidth = w;
}
i++;
chng = 1;
}else if( optionMatch(z,"tag") ){
size_t nByte;
@@ -7954,12 +8035,12 @@ static int dotCmdMode(ShellState *p){
** specify, so strlen(zW) bytes should be plenty of space to hold the
** result. */
aWidth = malloc( strlen(zW) );
while( isspace(zW[0]) ) zW++;
while( IsSpace(zW[0]) ) zW++;
while( zW[0] ){
int w = 0;
int nDigit = 0;
k = zW[0]=='-' && isdigit(zW[1]);
while( isdigit(zW[k]) ){
k = zW[0]=='-' && IsDigit(zW[1]);
while( IsDigit(zW[k]) ){
w = w*10 + zW[k] - '0';
if( w>QRF_MAX_WIDTH ){
dotCmdError(p,i+1,"width too big",
@@ -7980,7 +8061,7 @@ static int dotCmdMode(ShellState *p){
aWidth[nWidth++] = w;
zW += k;
if( zW[0]==',' ) zW++;
while( isspace(zW[0]) ) zW++;
while( IsSpace(zW[0]) ) zW++;
}
free(p->mode.spec.aWidth);
p->mode.spec.aWidth = aWidth;
@@ -8035,7 +8116,21 @@ static int dotCmdMode(ShellState *p){
}
sqlite3_str_append(pDesc, "\"", 1);
}
if( bAll || p->mode.spec.nCharLimit>0 ){
if( bAll
|| (p->mode.spec.bBorder==QRF_No) != ((pI->mFlg&1)!=0)
){
sqlite3_str_appendf(pDesc," --border %s",
p->mode.spec.bBorder==QRF_No ? "off" : "on");
}
if( bAll || p->mode.spec.eBlob!=QRF_BLOB_Auto ){
const char *azBQuote[] =
{ "auto", "text", "sql", "hex", "tcl", "json", "size" };
/* 0 1 2 3 4 5 6
** Must match QRF_BLOB_xxxx values. See all instances of tag-20251124a */
u8 e = p->mode.spec.eBlob;
sqlite3_str_appendf(pDesc, " --blob-quote %s", azBQuote[e]);
}
if( !bAll && p->mode.spec.nLineLimit==0 && p->mode.spec.nCharLimit>0 ){
sqlite3_str_appendf(pDesc, " --charlimit %d",p->mode.spec.nCharLimit);
}
zSetting = aModeStr[pI->eCSep];
@@ -8046,7 +8141,15 @@ static int dotCmdMode(ShellState *p){
if( bAll || p->mode.spec.eEsc!=QRF_Auto ){
sqlite3_str_appendf(pDesc, " --escape %s",qrfEscNames[p->mode.spec.eEsc]);
}
if( bAll || (p->mode.spec.nLineLimit>0 && pI->eCx>0) ){
if( bAll || (p->mode.spec.nLineLimit>0 && p->mode.spec.nCharLimit>0) ){
if( p->mode.spec.nLineLimit==0 && p->mode.spec.nCharLimit==0 ){
sqlite3_str_appendf(pDesc, " --limits off");
}else{
sqlite3_str_appendf(pDesc, " --limits %d,%d",
p->mode.spec.nLineLimit, p->mode.spec.nCharLimit);
}
}else
if( p->mode.spec.nCharLimit==0 && p->mode.spec.nLineLimit>0 && pI->eCx>0 ){
sqlite3_str_appendf(pDesc, " --linelimit %d",p->mode.spec.nLineLimit);
}
zSetting = aModeStr[pI->eNull];
@@ -8419,9 +8522,10 @@ dotCmdOutput_error:
sqlite3_free(zFile);
return 1;
}
/*
** Parse input line zLine up into individual arguments. Retain the
** parse in the p->dot substructure.
** Enlarge the space allocated in p->dot so that it can hold more
** than nArg parsed command-line arguments.
*/
static void parseDotRealloc(ShellState *p, int nArg){
p->dot.nAlloc = nArg+22;
@@ -8432,15 +8536,29 @@ static void parseDotRealloc(ShellState *p, int nArg){
p->dot.abQuot = realloc(p->dot.abQuot,p->dot.nAlloc);
shell_check_oom(p->dot.abQuot);
}
/*
** Parse input line zLine up into individual arguments. Retain the
** parse in the p->dot substructure.
*/
static void parseDotCmdArgs(const char *zLine, ShellState *p){
char *z;
int h = 1;
int nArg = 0;
size_t szLine;
p->dot.zOrig = zLine;
free(p->dot.zCopy);
z = p->dot.zCopy = strdup(zLine);
shell_check_oom(z);
szLine = strlen(z);
while( szLine>0 && IsSpace(z[szLine-1]) ) szLine--;
if( szLine>0 && z[szLine-1]==';' && p->iCompat>=20251115 ){
szLine--;
while( szLine>0 && IsSpace(z[szLine-1]) ) szLine--;
}
z[szLine] = 0;
parseDotRealloc(p, 2);
while( z[h] ){
while( IsSpace(z[h]) ){ h++; }
@@ -9227,8 +9345,8 @@ static int do_meta_command(const char *zLine, ShellState *p){
rc = shell_exec(&data,zSql,0);
sqlite3_free(zSql);
if( rc==SQLITE_OK ){
memset(hasStat, 0, sizeof(hasStat));
sqlite3_stmt *pStmt;
memset(hasStat, 0, sizeof(hasStat));
rc = sqlite3_prepare_v2(p->db,
"SELECT substr(name,12,1) FROM sqlite_schema"
" WHERE name GLOB 'sqlite_stat[134]'",
@@ -9569,6 +9687,8 @@ static int do_meta_command(const char *zLine, ShellState *p){
int openMode = SHELL_OPEN_UNSPEC;
int openFlags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
if( p->bSafeMode ) openFlags = SQLITE_OPEN_READONLY;
/* Check for command-line arguments */
for(iName=1; iName<nArg; iName++){
const char *z = azArg[iName];
@@ -9576,10 +9696,10 @@ static int do_meta_command(const char *zLine, ShellState *p){
if( optionMatch(z,"new") ){
newFlag = 1;
#ifdef SQLITE_HAVE_ZLIB
}else if( optionMatch(z, "zip") ){
}else if( optionMatch(z, "zip") && !p->bSafeMode ){
openMode = SHELL_OPEN_ZIPFILE;
#endif
}else if( optionMatch(z, "append") ){
}else if( optionMatch(z, "append") && !p->bSafeMode ){
openMode = SHELL_OPEN_APPENDVFS;
}else if( optionMatch(z, "readonly") ){
openFlags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
@@ -10059,7 +10179,7 @@ static int do_meta_command(const char *zLine, ShellState *p){
bIndent);
}else{
sqlite3_str_appendf(pSql,
"SELECT shell_format_schema(shell_add_schema(sql,%Q,name),%d))",
"SELECT shell_format_schema(shell_add_schema(sql,%Q,name),%d)",
zDb, bIndent);
}
sqlite3_str_appendf(pSql,
@@ -10749,11 +10869,9 @@ static int do_meta_command(const char *zLine, ShellState *p){
|| cli_strncmp(azArg[0], "indexes", n)==0) )
){
sqlite3_stmt *pStmt;
char **azResult;
int nRow, nAlloc;
int ii;
ShellText s;
initText(&s);
sqlite3_str *pSql;
const char *zPattern = nArg>1 ? azArg[1] : 0;
open_db(p, 0);
rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0);
if( rc ){
@@ -10770,86 +10888,46 @@ static int do_meta_command(const char *zLine, ShellState *p){
sqlite3_finalize(pStmt);
goto meta_command_exit;
}
for(ii=0; sqlite3_step(pStmt)==SQLITE_ROW; ii++){
pSql = sqlite3_str_new(p->db);
while( sqlite3_step(pStmt)==SQLITE_ROW ){
const char *zDbName = (const char*)sqlite3_column_text(pStmt, 1);
if( zDbName==0 ) continue;
if( s.zTxt && s.zTxt[0] ) appendText(&s, " UNION ALL ", 0);
if( sqlite3_stricmp(zDbName, "main")==0 ){
appendText(&s, "SELECT name FROM ", 0);
}else{
appendText(&s, "SELECT ", 0);
appendText(&s, zDbName, '\'');
appendText(&s, "||'.'||name FROM ", 0);
if( sqlite3_str_length(pSql) ){
sqlite3_str_appendall(pSql, " UNION ALL ");
}
appendText(&s, zDbName, '"');
appendText(&s, ".sqlite_schema ", 0);
if( c=='t' ){
appendText(&s," WHERE type IN ('table','view')"
" AND name NOT LIKE 'sqlite__%' ESCAPE '_'"
" AND name LIKE ?1", 0);
if( sqlite3_stricmp(zDbName, "main")==0 ){
sqlite3_str_appendall(pSql, "SELECT name FROM ");
}else{
appendText(&s," WHERE type='index'"
" AND tbl_name LIKE ?1", 0);
sqlite3_str_appendf(pSql, "SELECT %Q||'.'||name FROM ", zDbName);
}
sqlite3_str_appendf(pSql, "\"%w\".sqlite_schema", zDbName);
if( c=='t' ){
sqlite3_str_appendf(pSql,
" WHERE type IN ('table','view')"
" AND name NOT LIKE 'sqlite__%%' ESCAPE '_'"
);
if( zPattern ){
sqlite3_str_appendf(pSql," AND name LIKE %Q", zPattern);
}
}else{
sqlite3_str_appendf(pSql, " WHERE type='index'");
if( zPattern ){
sqlite3_str_appendf(pSql," AND tbl_name LIKE %Q", zPattern);
}
}
}
rc = sqlite3_finalize(pStmt);
if( rc==SQLITE_OK ){
appendText(&s, " ORDER BY 1", 0);
rc = sqlite3_prepare_v2(p->db, s.zTxt, -1, &pStmt, 0);
sqlite3_str_appendall(pSql, " ORDER BY 1");
}
freeText(&s);
/* Run the SQL statement in "split" mode. */
modePush(p);
modeChange(p, MODE_Split);
shell_exec(p, sqlite3_str_value(pSql), 0);
sqlite3_str_free(pSql);
modePop(p);
if( rc ) return shellDatabaseError(p->db);
/* Run the SQL statement prepared by the above block. Store the results
** as an array of nul-terminated strings in azResult[]. */
nRow = nAlloc = 0;
azResult = 0;
if( nArg>1 ){
sqlite3_bind_text(pStmt, 1, azArg[1], -1, SQLITE_TRANSIENT);
}else{
sqlite3_bind_text(pStmt, 1, "%", -1, SQLITE_STATIC);
}
while( sqlite3_step(pStmt)==SQLITE_ROW ){
if( nRow>=nAlloc ){
char **azNew;
sqlite3_int64 n2 = 2*(sqlite3_int64)nAlloc + 10;
azNew = sqlite3_realloc64(azResult, sizeof(azResult[0])*n2);
shell_check_oom(azNew);
nAlloc = (int)n2;
azResult = azNew;
}
azResult[nRow] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
shell_check_oom(azResult[nRow]);
nRow++;
}
if( sqlite3_finalize(pStmt)!=SQLITE_OK ){
rc = shellDatabaseError(p->db);
}
/* Pretty-print the contents of array azResult[] to the output */
if( rc==0 && nRow>0 ){
int len, maxlen = 0;
int i, j;
int nPrintCol, nPrintRow;
for(i=0; i<nRow; i++){
len = strlen30(azResult[i]);
if( len>maxlen ) maxlen = len;
}
nPrintCol = shellScreenWidth()/(maxlen+2);
if( nPrintCol<1 ) nPrintCol = 1;
nPrintRow = (nRow + nPrintCol - 1)/nPrintCol;
for(i=0; i<nPrintRow; i++){
for(j=i; j<nRow; j+=nPrintRow){
char *zSp = j<nPrintRow ? "" : " ";
cli_printf(p->out,
"%s%-*s", zSp, maxlen, azResult[j] ? azResult[j]:"");
}
cli_puts("\n", p->out);
}
}
for(ii=0; ii<nRow; ii++) sqlite3_free(azResult[ii]);
sqlite3_free(azResult);
}else
#ifndef SQLITE_SHELL_FIDDLE
@@ -12704,7 +12782,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
#else
cli_printf(stderr,
"%s: Error: no database filename specified\n", Argv0);
return 1;
rc = 1;
goto shell_main_exit;
#endif
}
data.out = stdout;
@@ -12758,6 +12837,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
modeChange(&data, MODE_Markdown);
}else if( cli_strcmp(z,"-table")==0 ){
modeChange(&data, MODE_Table);
}else if( cli_strcmp(z,"-psql")==0 ){
modeChange(&data, MODE_Psql);
}else if( cli_strcmp(z,"-box")==0 ){
modeChange(&data, MODE_Box);
}else if( cli_strcmp(z,"-csv")==0 ){
@@ -12844,7 +12925,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
}else if( cli_strcmp(z,"-version")==0 ){
cli_printf(stdout, "%s %s (%d-bit)\n",
sqlite3_libversion(), sqlite3_sourceid(), 8*(int)sizeof(char*));
return 0;
rc = 0;
goto shell_main_exit;
}else if( cli_strcmp(z,"-interactive")==0 ){
/* Need to check for interactive override here to so that it can
** affect console setup (for Windows only) and testing thereof.
@@ -12901,25 +12983,29 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
z = cmdline_option_value(argc,argv,++i);
if( z[0]=='.' ){
rc = do_meta_command(z, &data);
if( rc && bail_on_error ) return rc==2 ? 0 : rc;
if( rc && bail_on_error ){
if( rc==2 ) rc = 0;
goto shell_main_exit;
}
}else{
open_db(&data, 0);
rc = shell_exec(&data, z, &zErrMsg);
if( zErrMsg!=0 ){
shellEmitError(zErrMsg);
sqlite3_free(zErrMsg);
if( bail_on_error ) return rc!=0 ? rc : 1;
if( !rc ) rc = 1;
}else if( rc!=0 ){
cli_printf(stderr,"Error: unable to process SQL \"%s\"\n", z);
if( bail_on_error ) return rc;
}
if( bail_on_error ) goto shell_main_exit;
}
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
}else if( cli_strncmp(z, "-A", 2)==0 ){
if( nCmd>0 ){
cli_printf(stderr,"Error: cannot mix regular SQL or dot-commands"
" with \"%s\"\n", z);
return 1;
rc = 1;
goto shell_main_exit;
}
open_db(&data, OPEN_DB_ZIPFILE);
if( z[2] ){
@@ -12939,7 +13025,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
}else{
cli_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z);
eputz("Use -help for a list of options.\n");
return 1;
rc = 1;
goto shell_main_exit;
}
}
@@ -12977,6 +13064,16 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
if( rc==0 ) rc = 1;
goto shell_main_exit;
}
if( data.nPopMode ){
modePop(&data);
data.nPopMode = 0;
}
}
if( data.nPopOutput ){
output_reset(&data);
data.nPopOutput = 0;
}else{
clearTempFile(&data);
}
}
}else{
+3 -2
View File
@@ -1490,7 +1490,7 @@ typedef const char *sqlite3_filename;
** greater and the function pointer is not NULL) and will fall back
** to xCurrentTime() if xCurrentTimeInt64() is unavailable.
**
** ^The xSetSystemCall(), xGetSystemCall(), and xNestSystemCall() interfaces
** ^The xSetSystemCall(), xGetSystemCall(), and xNextSystemCall() interfaces
** are not used by the SQLite core. These optional interfaces are provided
** by some VFSes to facilitate testing of the VFS code. By overriding
** system calls with functions under its control, a test program can
@@ -4174,6 +4174,7 @@ void sqlite3_free_filename(sqlite3_filename);
** <li> sqlite3_errmsg()
** <li> sqlite3_errmsg16()
** <li> sqlite3_error_offset()
** <li> sqlite3_db_handle()
** </ul>
**
** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language
@@ -8730,7 +8731,7 @@ sqlite3_str *sqlite3_str_new(sqlite3*);
** pass the returned value to [sqlite3_free()] to avoid a memory leak.
** ^The [sqlite3_str_finish(X)] interface may return a NULL pointer if any
** errors were encountered during construction of the string. ^The
** [sqlite3_str_finish(X)] interface will also return a NULL pointer if the
** [sqlite3_str_finish(X)] interface might also return a NULL pointer if the
** string in [sqlite3_str] object X is zero bytes long.
**
** ^The [sqlite3_str_free(X)] interface destroys both the sqlite3_str object
+5 -1
View File
@@ -4267,10 +4267,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
@@ -5449,6 +5450,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);
+16 -7
View File
@@ -2062,9 +2062,10 @@ static void DbHookCmd(
** -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
** -border ("auto"|"off"|"on") Border for box and table styles
** -wrap NUMBER Max width of any single column
** -screenwidth NUMBER Width of the display TTY
** -linelimit NUMBER Max lines for any cell
@@ -2088,9 +2089,10 @@ static void DbHookCmd(
** -blob eBlob
** -wordwrap bWordWrap
** -textjsonb bTextJsonb
** -textnull bTestNull
** -splitcolumn bSplitColumn
** -defaultalign eDfltAlign
** -titlealign eTitleAlign
** -border bBorder
** -wrap nWrap
** -screenwidth nScreenWidth
** -linelimit nLineLimit
@@ -2217,11 +2219,13 @@ static int dbQrf(SqliteDb *pDb, int objc, Tcl_Obj *const*objv){
}else if( strcmp(zArg,"-blob")==0 ){
static const char *azBlob[] = {
"auto", "hex", "json",
"tcl", "text", "sql", 0
"tcl", "text", "sql",
"size", 0
};
static unsigned char aBlobMap[] = {
QRF_BLOB_Auto, QRF_BLOB_Hex, QRF_BLOB_Json,
QRF_BLOB_Tcl, QRF_BLOB_Text, QRF_BLOB_Sql
QRF_BLOB_Tcl, QRF_BLOB_Text, QRF_BLOB_Sql,
QRF_BLOB_Size
};
int blob;
rc = Tcl_GetIndexFromObj(pDb->interp, objv[i+1], azBlob,
@@ -2236,15 +2240,20 @@ static int dbQrf(SqliteDb *pDb, int objc, Tcl_Obj *const*objv){
if( rc ) goto format_failed;
qrf.bWordWrap = aBoolMap[v];
i++;
}else if( strcmp(zArg,"-textjsonb")==0 || strcmp(zArg,"-textnull")==0 ){
}else if( strcmp(zArg,"-textjsonb")==0
|| strcmp(zArg,"-splitcolumn")==0
|| strcmp(zArg,"-border")==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' ){
if( zArg[1]=='t' ){
qrf.bTextJsonb = aBoolMap[v];
}else if( zArg[1]=='b' ){
qrf.bBorder = aBoolMap[v];
}else{
qrf.bTextNull = aBoolMap[v];
qrf.bSplitColumn = aBoolMap[v];
}
i++;
}else if( strcmp(zArg,"-defaultalign")==0 || strcmp(zArg,"-titlealign")==0){
+1 -1
View File
@@ -604,7 +604,7 @@ int sqlite3RunParser(Parse *pParse, const char *zSql){
int tokenType; /* type of the next token */
int lastTokenParsed = -1; /* type of the previous token */
sqlite3 *db = pParse->db; /* The database connection */
int mxSqlLen; /* Max length of an SQL string */
i64 mxSqlLen; /* Max length of an SQL string */
Parse *pParentParse = 0; /* Outer parse context, if any */
#ifdef sqlite3Parser_ENGINEALWAYSONSTACK
yyParser sEngine; /* Space to hold the Lemon-generated Parser object */
+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
@@ -39,6 +39,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
+3
View File
@@ -53,6 +53,9 @@ for {set tn 1} {1} {incr tn} {
}
}
do_catchsql_test 1.5 {
INSERT INTO x1(x1) VALUES('maxpendinAB64');
} {1 {SQL logic error}}
finish_test
+27 -8
View File
@@ -27,10 +27,10 @@ SELECT * FROM t1;
┌─────┬───────┬───────┬───────┬───────┐
│ 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 │
│ 1 │ 2.5 │ three │ DD │ │
│ 2.5 │ three │ DD │ │ 1 │
│ DD │ │ 1 │ 2.5 │ three │
│ │ 1 │ 2.5 │ three │ DD │
└─────┴───────┴───────┴───────┴───────┘
END
@@ -41,10 +41,10 @@ SELECT * FROM t1;
┌─────┬───────┬───────┬───────┬───────┐
│ 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 │
│ 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
@@ -62,3 +62,22 @@ END
.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
+179 -36
View File
@@ -49,6 +49,14 @@ do_test 1.11b {
│ x'424c4f42' │ │ 'Ἀμήν' │
└─────────────┴─────┴─────────┘
}
do_test 1.11c {
set result "\n[db format -text sql -border off {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}]"
} {
@@ -76,7 +84,7 @@ do_test 1.14 {
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}
} {bad BLOB encoding (-blob) "unk": must be auto, hex, json, tcl, text, sql, or size}
do_test 1.16 {
catch {db format -text sql -style unk {SELECT * FROM t1}} res
set res
@@ -112,20 +120,58 @@ do_test 1.31 {
| BLOB | | Ἀμήν |
+------+-----+-------+
}
do_test 1.32 {
set result "\n[db format -style table -border off {SELECT * FROM t1}]"
} {
a | b | c
------+-----+-------
1 | 2.5 | three
BLOB | | Ἀμήν
}
do_test 1.33 {
set result "\n[db format -style table -border off \
-screenwidth 15 \
{SELECT * FROM t1}]"
} {
a | b | c
----+---+-----
1|2.5|three
BLOB| |Ἀμήν
}
do_test 1.34 {
set result "\n[db format -style box -border off \
-screenwidth 30 \
{SELECT * FROM t1}]"
} {
a │ b │ c
──────┼─────┼───────
1 │ 2.5 │ three
BLOB │ │ Ἀμήν
}
do_test 1.35 {
set result "\n[db format -style box -border off \
-screenwidth 15 \
{SELECT * FROM t1}]"
} {
a │ b │ c
────┼───┼─────
1│2.5│three
BLOB│ │Ἀμήν
}
do_test 1.40 {
set result "\n[db format -style column {SELECT * FROM t1}]"
} {
a b c
a b c
---- --- -----
1 2.5 three
BLOB Ἀμήν
BLOB Ἀμήν
}
do_test 1.41 {
set result "\n[db format -style column -title off {SELECT * FROM t1}]"
} {
1 2.5 three
BLOB Ἀμήν
BLOB Ἀμήν
}
do_test 1.50 {
@@ -137,19 +183,19 @@ do_test 1.60a {
} "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"
} "1,2.5,three\r\nx'424c4f42',,\"Ἀμήν\"\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"
} "a,b,c\r\n1,2.5,three\r\n\"\\102\\114\\117\\102\",,\"Ἀμήν\"\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"
} "\"a x y\",b,c\r\n1,2.5,three\r\n\"\\102\\114\\117\\102\",,\"Ἀμήν\"\r\n"
do_test 1.70 {
set result "\n[db format -style html {SELECT * FROM t1}]"
@@ -683,15 +729,15 @@ do_test 5.3c {
-text plain -esc off -textjsonb no \
-wordwrap yes -linelimit 2 $sql]"
} {
name mtime time value
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
...
one 1333206973 2012-03-31
two 15:16:13
...
entry-one 1708791504 2024-02-24 x'000000000000
16:18:24 00000000000000
@@ -730,31 +776,6 @@ do_test 6.1a {
} {
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);
@@ -888,6 +909,128 @@ 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
}
do_test 11.1 {
set result "\n[db format -style table -blob size {SELECT randomblob(1234)}]"
} {
+------------------+
| randomblob(1234) |
+------------------+
| (1234-byte blob) |
+------------------+
}
do_test 12.1 {
set result "\n[db format -style box -text html \
{SELECT 'abc','','xyz'}]"
} {
┌───────┬────┬───────┐
│ 'abc' │ '' │ 'xyz' │
├───────┼────┼───────┤
│ abc │ │ xyz │
└───────┴────┴───────┘
}
db close
finish_test
+40 -40
View File
@@ -63,11 +63,11 @@ do_test 1.10 {
┌───────┬───────┬───────┬───────┬──────┬───────┬───────┬───────┐
│ 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 │
│ 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 {
@@ -78,11 +78,11 @@ do_test 1.11 {
┌─────┬─────┬─────┬─────┬────┬─────┬─────┬─────┐
│ 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 │
│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│
└─────┴─────┴─────┴─────┴────┴─────┴─────┴─────┘
}
@@ -94,11 +94,11 @@ do_test 1.20 {
+-------+-------+-------+-------+------+-------+-------+-------+
| 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 |
| 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 {
@@ -109,11 +109,11 @@ do_test 1.21 {
+-----+-----+-----+-----+----+-----+-----+-----+
| 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 |
|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|
+-----+-----+-----+-----+----+-----+-----+-----+
}
@@ -124,11 +124,11 @@ do_test 1.30 {
} {
| 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 |
| 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 \
@@ -137,11 +137,11 @@ do_test 1.31 {
} {
| 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 |
|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 {
@@ -151,11 +151,11 @@ do_test 1.40 {
} {
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
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 \
@@ -164,11 +164,11 @@ do_test 1.41 {
} {
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
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
}
+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}
+8 -2
View File
@@ -731,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
@@ -931,7 +931,7 @@ do_test shell1-4.1.6 {
(4,2.25), (5,'hello'), (6,x'807f');
}
catchcmd test2.db {.dump --preserve-rowids}
} {1 {The --preserve-rowids option is not compatible with SQLITE_OMIT_VIRTUALTABLE}}
} {/.* --preserve-rowids option is not compatible with SQLITE_OMIT_VIRTUALTABLE/}
}
@@ -1311,6 +1311,12 @@ do_test shell1-8.4 {
+------------------+-----+
| 6683623321994527 | -47 |
+------------------+-----+}}
do_test shell1-8.4b {
catchcmd ":memory: --psql" \
{SELECT ieee754_mantissa(47.49) AS M, ieee754_exponent(47.49) AS E;}
} {0 { M | E
------------------+-----
6683623321994527 | -47}}
do_test_with_ansi_output shell1-8.5 {
catchcmd ":memory: --box" {
create table t(a text, b int);
+109
View File
@@ -197,6 +197,24 @@ do_test_with_ansi_output shellA-4.1 {
│ 8 │ last line │
└───┴──────────────────────────┘
}
do_test_with_ansi_output shellA-4.1b {
exec {*}$CLI -noinit test.db --box --escape ascii \
{.mode -border off} \
{SELECT a, x FROM t1 WHERE a IN (1,2,6,7,8)}
} {
a │ x
───┼──────────────────────────
1 │ line with ' single quote
───┼──────────────────────────
2 │ ^[[31mVT-100 codes^[[0m
───┼──────────────────────────
6 │ new
│ line
───┼──────────────────────────
7 │ carriage^Mreturn
───┼──────────────────────────
8 │ last line
}
do_test_with_ansi_output shellA-4.2 {
exec {*}$CLI -noinit test.db {.mode qbox} {SELECT a, x FROM t1 WHERE a IN (1,2,6,7,8)}
} {
@@ -210,6 +228,18 @@ do_test_with_ansi_output shellA-4.2 {
│ 8 │ 'last line' │
└───┴───────────────────────────────────────────┘
}
do_test_with_ansi_output shellA-4.2b {
exec {*}$CLI -noinit test.db {.mode qbox -border off} \
{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 │ unistr('new\u000aline')
7 │ unistr('carriage\u000dreturn')
8 │ 'last line'
}
# ".mode insert"
#
@@ -250,4 +280,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
+1 -1
View File
@@ -29,7 +29,7 @@ proc do_clitest {name} {
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
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"}
+14 -4
View File
@@ -212,7 +212,8 @@ proc default_njob {} {
if {$nCore<=2} {
set nHelper 1
} else {
set nHelper [expr int($nCore*0.5)]
set nHelper [expr int($nCore*0.8)]
if {$nHelper>20} {set nHelper 20}
}
return $nHelper
}
@@ -706,9 +707,18 @@ if {[llength $argv]>=1
}
}
if {![file readable $TRG(dbname)]} {
puts "Database missing: $TRG(dbname)"
exit
set once 1
while {![file readable $TRG(dbname)]} {
if {$delay==0} {
puts "Database missing: $TRG(dbname)"
exit
}
if {$once} {
set once 0
puts "Waiting for testing to start...."
flush stdout
}
after [expr {$delay*1000}]
}
sqlite3 mydb $TRG(dbname)
mydb timeout 2000
+2 -2
View File
@@ -668,8 +668,8 @@ int main(int argc, char **argv){
printf("/* Check to see if z[0..n-1] is a keyword. If it is, write the\n");
printf("** parser symbol code for that keyword into *pType. Always\n");
printf("** return the integer n (the length of the token). */\n");
printf("static int keywordCode(const char *z, int n, int *pType){\n");
printf(" int i, j;\n");
printf("static i64 keywordCode(const char *z, i64 n, int *pType){\n");
printf(" i64 i, j;\n");
printf(" const char *zKW;\n");
printf(" assert( n>=2 );\n");
printf(" i = ((charMap(z[0])*%d) %c", HASH_C0, HASH_CC);